심볼릭 링크와 정크션..
파일 하나를 두 곳에서 동시에 보는 마법
— 정크션과 심볼릭 링크
복사하지 않고도 “같은 파일”을 여러 경로에서 접근하게 만드는 파일시스템의 오래된 트릭
로컬 개발 환경을 손보다가 재미있는 문제에 부딪혔다. 코드는 깔끔하게 Git 저장소 폴더 하나에 모아두고 싶은데, 그 코드를 실행해서 눈으로 확인하려면 워드프레스 프로그램(아파치·DB·PHP가 다 갖춰진)이 요구하는 정해진 폴더 위치에 파일이 있어야 했다. 결국 “파일을 복사하지 않고도, 서로 다른 두 경로에서 같은 파일을 보게 만드는 방법”이 필요했는데 — 그 답이 바로 정크션(Junction)과 심볼릭 링크(Symbolic Link)였다.
한 줄 정의: 정크션과 심볼릭 링크는 “이 경로에 오면 사실 저 경로에 있는 실제 파일을 보여줘라”라고 운영체제(파일시스템)에 등록해두는 표지판이다. 파일을 복제하는 게 아니라, 실제 파일은 딱 한 곳에만 존재한 채로 여러 경로에서 접근 가능하게 만든다.
🚏 흔히 헷갈리는 “바로가기”와는 뭐가 다른가
윈도우를 오래 써본 사람이라면 바탕화면의 “바로가기(.lnk)” 아이콘에 익숙할 것이다. 정크션·심볼릭 링크도 얼핏 비슷해 보이지만, 작동하는 층(layer)이 완전히 다르다.
🖱️ 바로가기 (Shortcut)
탐색기라는 응용 프로그램 수준에서만 인식되는 껍데기 파일(.lnk)이다. 더블클릭하면 탐색기가 “아, 이건 저 경로를 열라는 뜻이구나”하고 해석해줄 뿐, 프로그램(예: 워드프레스 서버)이 그 경로를 직접 파일처럼 읽으려 하면 인식하지 못한다.
🔗 정크션 / 심볼릭 링크
운영체제의 파일시스템 자체에 등록되는 진짜 연결이다. 어떤 프로그램이든 — 워드프레스 서버든, 코드 에디터든 — 이 경로를 열면 실제 파일을 여는 것과 완전히 동일하게 동작한다. 링크를 거친다는 사실조차 눈치채지 못한다.
비유하자면 바로가기는 “저 건물로 가려면 이 방향으로 가세요”라고 적힌 안내판이고, 정크션·심볼릭 링크는 우체국의 주소 이전 등록에 가깝다. 이 주소로 온 우편물은 사람이 별도로 신경 쓰지 않아도 자동으로 실제 거주지로 배달된다 — 시스템 차원에서 완전히 처리되는 것이다.
🧭 왜 굳이 이런 게 필요한가 — 실제로 부딪힌 문제
워드프레스처럼 아파치·PHP·데이터베이스가 통째로 갖춰져야 돌아가는 프로그램은, 코드 파일 하나만 뚝 떼어서 실행할 수 없다. 반면 코드를 Git으로 버전 관리하려면, DB나 캐시 파일 같은 무거운 데이터와 섞이지 않은 깔끔한 코드 전용 폴더가 필요하다. 이 두 가지 요구가 충돌하는 지점에서 정크션이 해결사로 등장한다.
Git 저장소는 코드 전용 폴더에 그대로 둔다
예: C:\site\myproject — 여기엔 테마 코드만 깔끔하게 존재한다.
로컬 서버 프로그램이 요구하는 위치는 따로 있다
Local이나 Laragon 같은 도구는 사이트를 만들 때 자기만의 고정된 내부 경로(예: ~/Local Sites/mysite/app/public/wp-content/themes/)를 강제로 사용한다. 이 경로는 임의로 바꿀 수 없다.
두 경로를 정크션으로 연결
서버가 요구하는 그 위치에 정크션을 하나 만들어, 실제로는 Git 저장소 폴더를 가리키게 한다.
결과 — 파일을 복사할 필요가 완전히 사라진다
Git 저장소에서 코드를 수정하면, 그 즉시 로컬 서버가 보는 화면에도 반영된다. 파일은 하나뿐이고, 두 경로는 그 하나를 바라보는 창문일 뿐이니까.
🔬 손에 잡히도록 — 그림과 실험으로 확인하기
여기까지가 개념이라면, 이제 “복사했을 때”와 “링크했을 때”가 실제로 어떻게 다르게 움직이는지 그림과 눈에 보이는 예시로 확인해보자.
① 복사 vs 링크 — 수정했을 때 무슨 일이 벌어지나
📁 복사 — 서로 다른 파일 2개
🔗 링크 — 진짜 파일은 1개뿐
② 지금까지 다뤘던 실제 사례를 그림으로
두 경로는 이름만 다를 뿐, 디스크에는 파일이 왼쪽 자리 딱 하나만 존재한다
③ 실험 — 링크 전후를 명령어로 직접 비교
같은 파일을 고쳤을 때, 정크션이 없을 때(=단순 복사본)와 있을 때(=링크) 결과가 어떻게 갈리는지 터미널로 재현하면 다음과 같다.
❌ 정크션 없이 — 그냥 복사만 해둔 경우
✅ 정크션 연결 후
비유를 하나 더 보태면 — 같은 사람이 “본명”과 “별명” 두 개로 불리는 것과 같다. 별명으로 불러도 대답하는 건 결국 같은 한 사람이듯, 어느 경로(이름)로 접근하든 열리는 건 디스크 위의 같은 한 파일이다.
🛠️ 직접 만들어보기
윈도우에서는 “정크션”, 맥·리눅스에서는 “심볼릭 링크”라는 이름으로 부르지만 개념은 동일하다. 서버 관리를 해본 사람이라면 ln -s 명령을 한 번쯤 마주쳤을 텐데, 바로 이것과 같은 원리다.
방법 ① PowerShell — New-Item
방법 ② 명령 프롬프트 — mklink
정크션(/J)은 관리자 권한 없이도 만들 수 있고 폴더 전용이다. 반면 같은 mklink에서 /D 옵션(심볼릭 링크)을 쓰면 관리자 권한이 필요하다 — 윈도우 10 이상에서는 “개발자 모드”를 켜두면 이 제약이 풀린다.
연결 확인하는 법
결과에 LinkType: Junction과 실제 대상 경로가 표시되면 정상적으로 연결된 것이다.
ln 명령으로 심볼릭 링크 생성
유닉스 계열(맥·리눅스)에는 애초에 “정크션”이라는 별도 개념이 따로 없다 — 심볼릭 링크 하나로 파일과 폴더를 전부 처리한다. 윈도우의 정크션은 이 심볼릭 링크 개념 중 “폴더 전용, 관리자 권한 불필요” 버전만 별도로 떼어낸 것에 가깝다.
연결 확인하는 법
링크 삭제 시 주의
🌐 Windows vs Mac/Linux — 개념 비교표
| 구분 | 바로가기(Shortcut) | 정크션(Junction) | 심볼릭 링크(Symbolic Link) |
|---|---|---|---|
| 작동 계층 | 응용 프로그램(탐색기) 수준 | 파일시스템(NTFS) 수준 | 파일시스템 수준 |
| 연결 대상 | 파일 · 폴더 모두 | 폴더만 | 파일 · 폴더 모두 |
| 관리자 권한 | 불필요 | 불필요 | 필요 (개발자 모드 시 예외) |
| 지원 운영체제 | Windows 전용 | Windows(NTFS) 전용 | Windows / Mac / Linux 공통 |
| 프로그램의 인식 | 대부분 인식 못함 | 실제 폴더처럼 인식 | 실제 파일 · 폴더처럼 인식 |
🚀 실무에서 심볼릭 링크가 자주 쓰이는 곳
지금까지 다룬 “로컬 개발 환경 연결”은 사실 심볼릭 링크·정크션이 쓰이는 여러 상황 중 하나일 뿐이다. 유닉스 계열 파일시스템은 애초에 “파일”과 “그 파일을 부르는 이름(경로)”을 분리해서 다루는 걸 기본 철학으로 삼고 있어서, 서버 운영·개발 곳곳에서 계속 마주치게 된다.
1. 무중단 배포(Zero-downtime deploy) — current 링크
새 버전을 별도 폴더에 통째로 새로 올려두고, 서버는 항상 current라는 고정된 이름만 바라보게 해둔다. 배포 순간엔 이 링크가 가리키는 대상만 바꿔치기한다.
2. 런타임 버전 전환 — PHP·Node 등
/usr/bin/php → /etc/alternatives/php → php8.2 처럼, 여러 버전을 서버에 깔아두고 링크만 바꿔서 “현재 쓰는 버전”을 스위칭한다. nvm의 버전 전환도 같은 원리다.
3. 웹서버 설정 활성화 — nginx의 sites-enabled
설정 파일 실체는 sites-available에 두고, sites-enabled에는 그걸 가리키는 링크만 걸거나 지워서 사이트를 켜고 끈다.
4. 컨테이너 로그를 표준출력으로 연결
/var/log/nginx/access.log → /dev/stdout — Docker 환경에서 로그 파일 대신 컨테이너 표준출력으로 연결해, docker logs로 바로 확인하게 만드는 트릭.
5. 설정 파일(dotfiles) 동기화
~/.bashrc → ~/dotfiles/bashrc 처럼, 여러 설정 파일을 Git 저장소 하나에 모아두고 홈 디렉터리엔 링크만 심어둔다. 새 컴퓨터 세팅 시 저장소만 클론하면 끝.
6. 패키지 캐시 공유 — node_modules
여러 프로젝트가 같은 라이브러리를 쓸 때, 실제 파일은 전역 캐시에 한 번만 두고 각 프로젝트엔 링크만 걸어 디스크 용량을 절약한다 (pnpm, Yarn PnP 등).
💡 1번을 좀 더 깊게 — 왜 “순간적으로” 전환되는가
무중단 배포가 신뢰받는 이유는 링크를 바꾸는 연산 자체가 원자적(atomic)이기 때문이다. “바꾸는 중간 상태”라는 게 존재하지 않고, 어떤 요청이 들어와도 버전1 완결본이거나 버전2 완결본 둘 중 하나만 보게 된다 — 파일이 절반씩 뒤섞인 상태를 볼 방법이 없다.
PHP-FPM opcache 주의. PHP-FPM은 파일 경로 기준으로 컴파일된 코드를 캐싱해두는데, 링크가 가리키는 대상이 바뀌어도 opcache가 예전 코드를 그대로 물고 있는 경우가 있다. 그래서 배포 스크립트에는 링크 전환 직후 php-fpm 재시작(또는 opcache reset) 단계를 함께 넣는 게 안전하다.
⚠️ 실무에서 주의할 점
삭제 명령에 특히 주의. 정크션·심볼릭 링크가 있는 폴더를 지울 때 잘못된 방식(예: 끝에 슬래시를 붙이거나 재귀 삭제 옵션을 잘못 조합)으로 지우면, 링크만 지워지는 게 아니라 실제 원본 폴더 내용까지 통째로 삭제될 수 있다. 링크를 만들기 전 원본은 반드시 별도로 백업해두는 습관이 안전하다.
백업·압축 프로그램과의 충돌. 일부 백업 도구나 압축 프로그램은 링크를 “실제 파일”로 착각해 원본 내용을 통째로 복사하려 든다. 이 경우 용량이 예상보다 커지거나 중복 백업이 생길 수 있어, 백업 대상에서 링크 폴더를 제외하는 설정이 필요할 때가 있다.
Git과의 궁합은 좋다. Git 저장소 자체가 정크션 대상(원본)이라면, Git 입장에서는 자신이 링크를 통해 접근되고 있다는 사실을 신경 쓸 필요가 없다 — 원본 경로에서 평소처럼 commit·push 하면 그만이다.
정리하면, 정크션과 심볼릭 링크는 “파일은 한 곳에, 접근 경로는 여러 곳에”라는 아주 단순한 아이디어다. 서버 운영에서 흔히 쓰이는 ln -s도, 로컬 개발 환경에서 쓰는 mklink /J도, 무중단 배포의 current 링크도 결국 같은 철학을 각자의 상황에 맞게 구현한 것뿐이다.
처음엔 로컬 개발 환경 문제 하나를 풀려고 알게 된 개념이었는데, 파고들다 보니 배포 자동화·버전 전환·설정 관리처럼 완전히 다른 문제들이 전부 같은 도구 하나로 풀리는 걸 보고 나니 생각이 좀 바뀌었다. 유닉스 파일시스템이 애초에 “파일”과 “그 파일을 부르는 이름”을 분리해서 설계된 덕분에 가능한 일인데, 이 감각 한 번 잡히고 나면 여기저기서 “아, 이것도 링크로 풀 수 있겠는데” 싶은 지점이 계속 눈에 밟히기 시작한다. 몰랐을 땐 그냥 지나쳤을 자리들인데, 알고 나니 활용할 구석이 의외로 많았다.