블로그는 완성된 화면을 자랑한다. 그 화면에 이르기까지 무엇이 끊겼는지는 쓰지 않는다 — 글쓴이는 이미 뚫었거나, 애초에 안 겪었으니까. 이 장은 반대를 한다. 내가 실제로 겪은 오류들을, 증상과 원인과 처방으로 하나씩 해부한다. 검색해도 잘 나오지 않는 것들이다.
§ 00 · 세우기 전에 — 폴더부터 옮겨라
이 장 전체보다 앞서는 권고를 먼저 적는다. 뒤에 나올 여덟 개의 덫은 전부 유효하고, 나도 전부 실제로 밟았다. 다만 당신이 아직 원격을 세우지 않았고 프로젝트가 초기 단계라면, 세우기 전에 할 일이 하나 있다 — 코드가 든 작업 폴더를 데스크탑 밖으로 옮겨라. 강력히 권한다.
이유는 단순하다. 맥은 데스크탑·문서 같은 폴더를 따로 잠가 두고 관리한다. 뒤의 일곱 번째와 여덟 번째 덫은, 하필 코드가 그 잠긴 폴더 안에 있어서 생기는 일이다. 폴더가 그 밖에 있으면 두 덫은 애초에 생기지 않는다. 나는 순서를 거꾸로 밟았다 — 다 세우고 한참 쓰다가, 두 덫을 다 밟고 나서야 옮겼다. 옮기고 나니 막혀 있던 것이 그 자리에서 풀렸다.
다만 옮기는 일이 폴더를 끌어다 놓는 것으로 끝나지는 않는다. 규칙 하나만 붙들면 된다 — 깨지는 것은 옛 주소를 글자로 박아 둔 것뿐이다. 어딘가에 옛 주소가 글자로 남아 있으면 그것만 깨지고, 주소를 거치지 않고 이어져 있던 것들은 멀쩡하다. 그러니 아래 넷을 하나씩 찾아 고치고, 하나씩 다시 재 봐야 한다. 저절로 따라오는 것은 하나도 없다. 이 이사가 초기 단계일수록 싼 이유가 그것이다 — 박아 둔 주소가 아직 몇 개 없기 때문이다.
| 무엇을 | 안 맞추면 | 다시 맞추고, 무엇으로 확인하나 |
|---|---|---|
| 설정·자동검사 파일에 박힌 옛 주소 | 자동으로 돌던 검사와 도구들이 아무 소리 없이 안 돈다. 오류조차 안 뜨니 한참 지나서야 안다 — 넷 중 가장 위험하다. | 옛 주소 글자를 전부 찾아 새 주소로 바꾼다. 확인 = 옛 주소로 검색했을 때 결과가 0이 될 때까지. |
| git이 기억하는 작업 폴더 자리 | git이 옛 자리를 찾으러 갔다가 실패한다. 곁가지를 여러 개 두고 쓰고 있다면 전부 그렇다. | 등록된 자리를 고치는 명령을 한 번 돌린다. 확인 = 곁가지 목록을 뽑아 전부 새 주소인지 눈으로 전수. |
| 빌드 캐시 | 지난번 구운 결과물 안에 옛 주소가 굳어 있어, 빌드나 서버가 엉뚱한 자리를 가리킨다. | 고치는 게 아니라 버리는 것이다 — 캐시를 지우고 다시 굽는다. 확인 = 다시 구운 것 안에 옛 주소가 0. |
| 문서·메모에 적어 둔 주소 | 사람이 읽는 안내가 죽은 주소를 가리킨다. 기계는 멀쩡한데 사람이 헤맨다. | 검색해서 고친다. 확인 = 검색 결과 0. 다만 지난 기록처럼 그때 그대로 두는 게 맞는 문서는 남긴다. |
§ 01 · 성공담엔 이 순간이 없다
원격을 처음 세우던 며칠, 나는 세 가지에 막혔다. 한글이 깨졌고, 접속이 거부당했고, 멀쩡하던 프로그램이 열리지 않았다. 셋 다 내 코딩 실력과 무관했고, 셋 다 검색으로 바로 나오지 않았다.
이유가 있다. 블로그가 쓰는 것은 해피패스다 — 처음부터 끝까지 순조롭게 흘러가는 길. 초보가 실제로 넘어지는 곳은 그 길에서 벗어난 자리, 즉 실패 지점인데, 성공담에는 그 자리가 없다. 글쓴이가 이미 뚫었으니 기억에 남지 않았거나, 운 좋게 안 겪었거나.
그래서 이 장은 실패만 쓴다. 아래는 내가 실제로 밟은 덫들이다. 각각을 증상·원인·처방으로 적었다. 당신이 같은 자리에서 멈췄을 때, 검색 대신 이 표를 먼저 보라.
§ 02 · 여덟 개의 덫
아래 여덟은 원격개발에서 밟기 쉬운 덫이다. 앞의 여섯은 원격을 처음 세울 때 부딪치는 것들 — 대부분 도구 자체의 버릇이거나, 두 도구가 서로 부딪치는 자리다. 마지막 둘, 일곱과 여덟은 결이 다르다. 세우다 만나는 게 아니라, 다 세워 잘 돌던 원격이 어느 날 아침 갑자기 끊긴다 — 내가 아무것도 건드리지 않았는데. 원인을 알면 처방은 대개 한 줄이다 — 다만 마지막 하나는 아니다.
| № | 덫 (증상) | 원인과 처방 |
|---|---|---|
| 1 | 한글이 깨져 입력이 안 된다 (CJK) | 먼저 CJK. 한국어·중국어·일본어(Chinese·Japanese·Korean)는 글자 하나가 여러 바이트로 이루어지고, 자모가 조합돼 한 글자가 되며, 화면에서 두 칸을 차지한다. 알파벳 한 글자 한 바이트씩을 염두에 두고 만들어진 명령줄 도구에게, CJK는 오래도록 가장 까다로운 문자로 남아 있다 — 아무리 똑똑한 팀이 만든 도구라도 완벽한 한글 입력을 보장하지는 못한다. 그래서 깨짐은 두 곳에서 온다. ① 저편 기계: 원격으로 붙은 창(SSH)은 내 태블릿의 언어 설정을 저편으로 넘겨주지 않아, 기계가 “한 바이트 글자만 쓴다”는 기본값으로 떨어진다. → 처방: 저편 기계에서 접속할 때마다 먼저 자동 실행되는 시작 설정 파일(이를 “프로필”이라 부른다 — 내가 붙을 때마다 맨 앞에 읽히는 나만의 준비 스크립트), 거기에 “나는 UTF-8을 쓴다”는 한 줄을 박는다. ② 내 손안의 창: 저편을 고쳤는데도 한글이 씹힌다면, 범인은 태블릿의 터미널 앱이 한글 조합(입력기)을 제대로 흘려보내지 못하는 것이다. 흔하고, 최상급 앱에서도 난다. → 처방: 그 앱 설정에서 CJK·입력 관련 옵션을 찾아 켠다(터미너스에는 그 옵션이 있다). 두 곳을 다 손봐야 한글이 온전해진다. |
| 2 | 단축키가 서로 먹겠다고 싸운다 | 원인: 화면을 붙드는 그물(티먹스)과, 그 안에서 도는 AI 도구가 같은 단축키를 쓴다. 처방: 둘 중 하나의 단축키를 다른 것으로 옮겨, 충돌을 없앤다. 티먹스 설정 한 줄이면 된다. |
| 3 | 프로그램이 열리다 오류를 낸다 | 원인: 앞서 띄운 같은 프로그램이 죽지 않고 유령처럼 남아, 새로 여는 것을 막는다. 처방: 그 유령 프로세스를 찾아 정리하거나, 그물을 통째로 한 번 재시작하면 풀린다. 재설치는 필요 없다. |
| 4 | 로그인 주소가 “유효하지 않다”고 뜬다 | 원인: 일부 창(터미너스 등)은 로그인용 주소와 일회용 코드를 한 줄에 붙여 보여줘, 그걸 통째로 복사하면 주소가 깨진다. 처방: 주소와 코드를 손으로 나눠, 주소만 브라우저에 넣고 코드는 따로 입력한다. |
| 5 | 자리를 옮기면 접속이 실패한다 | 원인: 네트워크가 바뀌면 사설망(테일스케일)이 새 경로를 다시 뚫는 데 몇 초가 걸리고, 그 사이 접속을 시도하면 실패한다. 처방: 실패는 고장이 아니다. 몇 초 뒤 다시 시도하면 붙는다. 테일스케일 앱을 먼저 깨우면 더 빠르다. |
| 6 | 접속이 끊겨 작업이 날아갈까 두렵다 | 원인: 창에 직접 매달린 작업은 창이 닫히면 죽는다. 처방: 애초에 작업을 그물(티먹스)에 매달았다면, 접속이 끊겨도 작업은 기계 안에서 계속 돈다. 다시 붙어 “재접속”만 하면 하던 자리가 그대로다. |
| 7 | 어제까지 열리던 폴더가 갑자기 접근을 거부당한다 | 이 하나는 결이 다르다 — 세우다 만나는 게 아니라, 다 세워 잘 돌던 원격이 어느 날 아침 끊긴다. 증상: 기계에는 분명히 붙는데(로그인은 됨), 정작 코드가 든 데스크탑의 작업 폴더에 손을 뻗으면 “작업이 허용되지 않음(Operation not permitted)”이 뜬다. 원인: 애플이 심야에 조용히 밀어넣는 보안 업데이트다. 맥은 새벽에 스스로 시스템을 갱신하는데(이 기계도 실제로 새벽 세 시에 업데이트가 깔린 기록이 남아 있다), 그때 원격 접속을 받아 주는 관문 프로그램 — 정확히는 그 관문을 띄우는 부품, 이름이 sshd-keygen-wrapper다 — 이 갖고 있던 “전체 디스크 접근” 허가가 리셋되어 떨어져 나간다. 맥은 데스크탑·문서 같은 민감한 폴더를 따로 잠가 두고(이 잠금이 바로 “전체 디스크 접근” 장치다), 원격에서 그 폴더에 닿으려면 이 관문에 명시적 허가가 있어야 한다. 처방: 맥 본체 화면에서 [시스템 설정 → 개인정보 보호 및 보안 → 전체 디스크 접근]으로 들어가, 목록에서 sshd-keygen-wrapper를 찾아 스위치를 다시 켠다. 목록에 아예 없으면 [+] 단추로 추가하되, 파일 선택창이 뜨면 [이동 → 폴더로 이동](단축키 Command+Shift+G)으로 /usr/libexec 폴더에 들어가 그 안의 sshd-keygen-wrapper를 고른다. 켠 뒤 원격 접속을 한 번 끊었다 다시 붙으면 폴더가 열린다. 이 덫이 가장 악질인 이유: 아무것도 안 바꿨는데 어제 되던 게 오늘 안 되니, 검색으로도 원인에 닿기 어렵다. 원격을 아는 자만이 이 아침에 무너지지 않는다. 그리고 이 덫을 애초에 안 밟는 길이 하나 있다 — 이 장 맨 앞 §00의 권고, 코드 폴더를 잠긴 폴더 밖으로 내보내는 것이다. |
| 8 | 권한을 분명히 켰는데도 계속 거부당한다 | 앞의 7번 처방대로 전체 디스크 접근을 켰다. 화면에서 스위치가 켜진 것도 눈으로 봤다. 그런데 여전히 “작업이 허용되지 않음”이 뜬다. 여기서 대부분 무너진다 — 켠 것을 두 눈으로 확인했으니 더 해볼 게 없어 보이기 때문이다. 원인은 셋이고, 셋 다 화면에는 보이지 않는다. ① 켠 권한은 이미 돌고 있는 프로그램에 반영되지 않는다. 프로그램은 켜질 때 받은 판정을 그대로 들고 계속 간다. 새 판정을 받으려면 그 프로그램을 뿌리부터 다시 띄워야 한다. ② 목록에서 켜야 할 항목이, 사람이 알아보는 이름이 아니다. 권한 목록에는 비슷한 이름이 여럿 나란히 뜨는데, 정작 켜야 하는 것은 실제로 실행되는 파일의 이름이다. 이 기계에서는 사람이 아는 이름 쪽(claude·Claude)이 켜져 있었고, 진짜 실행 파일(claude.exe)이 꺼져 있었다. 그것을 켜기 전까지는 몇 번을 재시작해도 소용없었다. ③ “뿌리”가 도구마다 다른 데 있다. 그물(티먹스) 안에서 도는 것들의 뿌리는 그물의 주인이고, 그물 밖에서 혼자 도는 것들의 뿌리는 그 프로그램 자신이다. 발단은 밤사이에 있었다 — AI 도구가 새벽에 스스로 갱신하며 자기를 세 번 다시 띄웠고, 그 바람에 옛 뿌리를 잃은 갈래들이 생겼다. 맥은 폴더 접근을 허가할 때 “이 요청이 어느 프로그램 혈통에서 왔나”를 따지는데, 그 혈통의 조상이 이미 죽어 있으면 판정 자체가 불가능해져 무조건 거부한다. 처방은 두 층이다. 확실한 길(명령 한 줄도 필요 없다): 기계를 껐다 켠다. 모든 뿌리가 새로 태어나므로 세 원인이 한꺼번에 사라진다. 정밀한 길: 먼저 권한 목록에서 진짜 실행 파일 이름을 찾아 켠다. 그다음 그 프로그램을 뿌리부터 다시 띄운다 — 그물 안에서 돌던 것이면 tmux kill-server로 그물 주인을 갈고 세션을 새로 만들고(주의: 열려 있던 세션이 전부 닫히니 하던 작업부터 확인한다), 그물 밖에서 혼자 돌던 프로그램이면 그 프로그램을 완전히 종료했다 다시 실행한다. 이 덫이 가장 악질인 이유는 따로 있다. 증상이 하나라고 원인도 하나인 것은 아니다. 나는 그물 주인을 갈아 내 쪽이 살아나는 것을 보고 “다 나았다”고 판단했는데, 그물 밖에서 돌던 다른 갈래는 그로부터 이십 분을 더 막혀 있었다. 한 갈래가 살아난 것은 기계가 나은 것이 아니다. 이 덫도 7번과 뿌리가 같다 — §00의 권고대로 코드 폴더가 잠긴 폴더 밖에 있었다면, 둘 다 생기지 않았다. |
§ 03 · 왜 이것이 승부를 가르는가
위 여덟은 사소해 보인다. 한글이 깨지고, 단축키가 부딪치고, 프로그램이 안 열리고, 어제 되던 폴더가 오늘 막히고, 권한을 켰는데도 안 열리는 것 — 화려한 문제가 아니다. 그러나 초보가 원격개발을 포기하는 자리는 정확히 여기다. 코드가 어려워서가 아니라, 도달이 막혀서.
코드는 AI 도구가 대신 짜 줄 수 있다. 그러나 “접속이 안 됩니다”, “한글이 깨집니다”, “프로그램이 안 열립니다” 앞에서 사람들은 멈추고, 검색하고, 안 나오면 접는다. 일은 시작도 못 하고 도달에서 무너지는 것이다.
그래서 이 지루한 표 하나가, 화려한 코드 열 줄보다 오래 당신의 일을 살린다. 도달하는 법을 아는 자가 오래 일한다.
글 읽기: sass.studio.