리눅스 ln 명령어 사용법 - 심볼릭 링크와 하드 링크 차이, 배포 링크 교체
이 명령어를 언제 쓰는가
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 — 깨진 심볼릭 링크를 찾아낸다. 릴리스 정리 후 점검용.