Devin.KR
로그인

리눅스 ln 명령어 사용법 - 심볼릭 링크와 하드 링크 차이, 배포 링크 교체

개발자 조회 2

이 명령어를 언제 쓰는가

ln은 링크를 만든다. 실무에서 가장 많이 보는 형태는 배포 디렉토리를 가리키는 심볼릭 링크다. /opt/app/current가 실제 릴리스 디렉토리를 가리키게 해 두면, 배포는 "링크를 새 릴리스로 다시 거는 것"이 되고 롤백은 "링크를 이전 릴리스로 되돌리는 것"이 된다. 파일을 옮기지 않으므로 순식간에 끝나고 이전 버전도 그대로 남는다.

그다음이 설정 파일 배치다. /etc/nginx/sites-enabled/sites-available/의 설정을 링크로 걸어 활성/비활성을 관리하는 방식이 대표적이다. 하드 링크는 상대적으로 드물게 쓰지만, 같은 파일시스템 안에서 용량을 더 쓰지 않는 사본이 필요할 때(스냅샷식 백업) 유용하다.

기본 형식

ln -s 원본경로 링크이름     # 심볼릭 링크
ln    원본경로 링크이름     # 하드 링크

순서는 cp와 같다. 먼저 쓰는 쪽이 가리킬 대상, 나중이 새로 만들 이름이다. 링크 이름을 생략하면 현재 디렉토리에 원본과 같은 이름으로 만든다.

심볼릭 링크와 하드 링크의 차이

구분심볼릭 링크하드 링크
실체경로 문자열을 담은 별도 파일같은 아이노드를 가리키는 또 하나의 이름
파일시스템 경계넘을 수 있다넘을 수 없다
디렉토리 대상가능불가(일반 사용자)
원본 삭제 시깨진 링크가 된다데이터는 남는다(링크 수만 감소)
확인ls -l-> 표시ls -li의 아이노드 번호가 같다

자주 쓰는 옵션

옵션의미예시
-s심볼릭 링크 생성(사실상 항상 붙인다)ln -s /opt/app/releases/v2 current
-f같은 이름이 있으면 지우고 다시 만든다ln -sf /etc/localtime.new /etc/localtime
-n대상이 디렉토리 링크면 따라 들어가지 않는다(-f와 짝)ln -sfn /opt/app/releases/v2 /opt/app/current
-r상대 경로 링크로 만든다(GNU 전용)ln -sr releases/v2 current
-v만든 링크를 출력ln -svfn a b

실전 예제

1) 배포 링크 교체와 롤백 — 가장 전형적인 무중단 배포 패턴이다.

ln -sfn /opt/app/releases/20260825T0910 /opt/app/current
systemctl reload myapp

# 롤백: 링크만 되돌린다
ln -sfn /opt/app/releases/20260820T1740 /opt/app/current
systemctl reload myapp

2) 지금 어느 릴리스를 서비스 중인지 확인

ls -l /opt/app/current
# lrwxrwxrwx 1 deploy deploy 31 Aug 25 09:11 /opt/app/current -> /opt/app/releases/20260825T0910
readlink -f /opt/app/current

3) nginx 설정 활성화 — Debian·Ubuntu 계열의 관례다.

ln -s /etc/nginx/sites-available/devin.kr /etc/nginx/sites-enabled/devin.kr
nginx -t && systemctl reload nginx

4) 깨진 링크 찾기 — 릴리스 디렉토리를 정리한 뒤 확인한다.

find /opt/app -xtype l -print

-xtype l은 GNU find 전용이다. macOS·BSD에서는 find /opt/app -type l ! -exec test -e {} \; -print처럼 존재 여부를 따로 검사한다.

5) 용량을 더 쓰지 않는 백업 사본 — 같은 파일시스템 안에서 하드 링크로 스냅샷을 만든다.

cp -al /data/current /data/snap-20260825
ls -li /data/current/big.dat /data/snap-20260825/big.dat   # 아이노드가 같다

함정과 주의점

상대 경로 링크는 "현재 디렉토리"가 아니라 "링크가 놓인 디렉토리" 기준이다. 이것이 깨진 링크의 가장 흔한 원인이다.

cd /opt/app
ln -s releases/v2 /opt/deploy/current   # 깨진다
# /opt/deploy/releases/v2 를 찾게 되기 때문이다

확실하지 않으면 절대 경로를 쓰거나, GNU라면 ln -sr로 올바른 상대 경로를 계산하게 한다. ln은 대상이 없어도 링크를 만들어 주므로 만든 직후 ls -l이나 readlink -e로 확인하는 습관이 필요하다.

-f만 쓰고 -n을 빼면 링크가 디렉토리 안으로 들어간다. 기존 current가 디렉토리를 가리키는 링크일 때 ln -sf 새경로 /opt/app/current를 치면, 링크를 갈아끼우는 대신 /opt/app/current/새경로이름이라는 링크가 생긴다. 배포가 "성공했는데 반영이 안 되는" 상태가 여기서 나온다. 디렉토리 링크를 교체할 때는 반드시 ln -sfn을 쓴다.

ln -sfn은 원자적이지 않다. 내부적으로 기존 링크를 지우고 새로 만들기 때문에, 아주 짧게 링크가 없는 순간이 생긴다. 트래픽이 많은 서비스에서 이 틈이 문제라면 임시 링크를 만든 뒤 mv -T로 교체한다(GNU coreutils).

ln -sfn /opt/app/releases/v2 /opt/app/current.tmp
mv -Tf /opt/app/current.tmp /opt/app/current

하드 링크는 파일시스템을 못 넘고, 지워도 용량이 안 준다. 다른 마운트에 걸면 Invalid cross-device link 에러가 나고, 하드 링크가 여러 개인 파일은 마지막 이름까지 지워야 블록이 회수된다. 용량 정리 중이라면 ls -l의 링크 수(두 번째 열)를 확인한다.

링크를 다룰 때 뒤 슬래시를 붙이지 마라. current/처럼 쓰면 도구에 따라 링크 자체가 아니라 대상 디렉토리를 다루게 되어, 링크를 지우려다 내용물을 건드릴 수 있다. 링크는 항상 슬래시 없이 current로 지정한다.

함께 보면 좋은 명령어

  • readlink — 링크가 실제로 무엇을 가리키는지 확인한다. -f는 끝까지, -e는 존재할 때만 출력한다.
  • ls -li — 아이노드 번호와 링크 수를 보여준다. 링크 수가 2 이상이면 하드 링크가 있다는 뜻이고, 어느 파일과 묶였는지까지 찾으려면 find /경로 -samefile 파일을 쓴다.
  • find -xtype l — 깨진 심볼릭 링크를 찾아낸다. 릴리스 정리 후 점검용.