Conductor와 Codex App 사이에서 로컬 포트 추적하기
요즘은 터미널에서 개발을 하기보단, codex-app, conductor를 애용하고 있다.
특히 사내에서는 claude-code를 지원해주기 때문에, conductor를 주로 사용한다.
Worktree
의외로, 생각보다 많은 사람들이 conductor를 사용하지 않는 것 같다.
이 글을 쓰고 난 뒤, 내가 Conductor를 어떻게 사용하고 있는지 글을 써봐도 좋을 것 같다.
최근 단톡방에서 괜찮은 에디터, 또는 터미널을 추천해달라고 했을 때 Conductor라는 단어가 당연히 나올 것이라 생각했지만 주제가 바뀌기 전까지 해당 단어는 노출되지 않았다.
Conductor는 기본적으로 작업을 할 때 워크트리(worktree)를 생성하고, 세션을 열어서 작업을 시작한다. 즉, 하나의 Target 브랜치를 잡고, 그 브랜치에서부터 트리를 생성한 뒤 Target 브랜치로 머지시키면서 생산성을 높인다.

나는 주로 Jira SubTask 기준으로 워크트리를 생성한다.
한번 작업할 때는 주로 워크트리 2~4개 정도 생성해서 작업하는 편이다.
여기서 문제가 시작된다. 바로, local port 관리가 용이하지 않다.
예를 들어, 하나의 워크트리에서 User, Admin만 열어도 8080, 4200으로 포트를 점유하게 된다.
2~3개 정도만 워크트리를 넘나들며 작업하다 보면, 8081, 8082, 8083으로 포트가 열리게 되고, Storybook까지 더하면 관리가 안된다.
심지어 요즘은 agent-browser도 자주 사용하는 편인데, 세션에서 AI가 headless로 브라우저를 열게 되면 또 추가적인 port를 점유한다. 정신차려보면 여기저기 무분별하게 port를 열어놓았고, 맥의 팬이 CPU 열을 낮추기 위해 돌기 시작한다.
결국 claude-code나 codex를 열고 다음과 같이 입력한다.
현재 8080부터 8090까지, 4200~4210까지 열려있는 포트 모두 kill시켜줘..
로컬 포트를 읽는 방법
보통 같았으면, 문제의 상황을 가장 잘 해결해주는 프로덕트를 찾았을 것 같다. 하지만 최근 AI의 도입으로 복잡하지 않은 프로덕트는 얼마든지 만들 수 있을 것이라 판단했다.
오히려, 이 문제를 가장 잘 해결해주는 프로덕트를 발견했다면 '그냥.. 이거 써야겠다..' 혼자말을 중얼거리며 김이 새버렸을 것이다.
그래서 찾아보지도 않았다. 하지만 기술적으로 가능한지만 검증했다.
확인해본 결과 기술적으로는 충분히 가능했다. 리눅스 명령어를 하나도 모르지만 codex에게 로컬 port를 점유하는 리스트를 어떻게 확인할 수 있냐고 물어봤는데, 다음과 같은 답변을 들었다.
/usr/sbin/lsof -nP -iTCP -sTCP:LISTEN
lsof는 list open files의 줄임말이라고 설명해주었다. 어떤 프로세스가 어떤 포트를 열고 있는지를 확인할 수 있는 것이다.
다만, lsof만 입력하면 방대한 정보가 출력된다. 그래서 하나씩 내가 원하는 정보를 출력하기 위해 필터를 적용한다.
옵션 적용 단계:
/usr/sbin/lsof프로세스가 열어둔 파일, 디렉토리, 소켓이 모두 섞여 나온다.
디렉토리
loginwind 432 geuni cwd DIR ... /TCP 대기
ControlCe 816 geuni 10u IPv4 ... TCP *:afs3-fileserver (LISTEN)TCP 연결
Telegram 986 geuni 30u IPv4 ... TCP ...:https (ESTABLISHED)UDP
rapportd 676 geuni 24u IPv6 ... UDP *:xserveraid모두 적용한 뒤, 결과를 한 줄로 설명하면 다음과 같다.
지금 이 로컬에서 TCP 포트를 열고 연결을 기다리고 있는 프로세스 목록을 숫자 기반으로 보여줘.
Port 주인 찾기
이제 로컬에서 어떤 포트가 열려 있는지는 확인할 수 있었다. 하지만 그것만으로는 이 포트가 Conductor가 만든 워크트리에서 실행된 서버인지, Codex가 만든 워크트리에서 실행된 서버인지 알 수 없다.
한 가지 공통된 조건을 발견할 수 있었는데, Conductor나 Codex가 생성하는 워크트리는 다음과 같은 디렉토리 하위에 생성된다.
conductor는 가장 마지막에 도시명이 붙는다. seoul, busan 심지어, yeosu도 봤다.
// conductor
~/conductor/workspaces/<project>/<city-name>
// codex
~/.codex/worktrees/<hash>/<project>
이 조건을 이용해서 포트를 열고 있는 프로세스가 위치한 곳을 찾을 수 있다.
매칭 순서:
LISTEN 중인 TCP Port 목록을 얻는다.
먼저 OS 전체에서 연결을 기다리고 있는 TCP 포트 목록을 가져온다.
/usr/sbin/lsof -nP -iTCP -sTCP:LISTENTCP 대기
node 30916 geuni 17u IPv6 ... TCP *:3000 (LISTEN)meaning
3000번 포트를 열고 기다리는 node 프로세스가 있다.마무리
이후에는 5초마다 polling을 돌면서 열린 포트와 runtime row를 다시 맞추도록 했다.
불편함이 있다면 최적화를 하거나, 성능을 개선하겠지만 지금까지는 큰 불편함을 느끼지 못했다.
애초에 만들 때부터 나만 사용할 것이라 생각하고 만들었다.
내 체형에 딱 맞는 맞춤 옷을 만든 것이다. 그래서 더 빠르고 간편하게 만들 수 있었던 것 같다.
추가로 사용하다가 필요한 기능은 즉각적으로 생성해서 만들었다.
- 감지된 워크트리에서 유저, 어드민, 스토리북 등 스크립트를 바로 실행할 수 있는 버튼 추가
- 워크트리를 삭제할 수 있도록 버튼 추가 및 잘못 삭제했을 경우 10초 이내 되돌릴 수 있도록 기능 추가
- Port Bar가 감지한 실행 중인 runtime과 관련 포트를 한 번에 정리하는 버튼 반영
그 외 작은 수정들도 있었다.
conductor, codex가 분리 없이 워크트리로 생성되니 구분이 되지 않아서 각 세션을 나누거나, 메모리 사용량이 어느정도인지 보고 싶어서, port-bar에 추가하거나, 오랫동안 작업한 워크트리라면 특정 title을 붙여서 구분하고 싶어서 추가해두었다.
사용하다보니 안쓰는 것들도 있어서 제거할 예정이다.
결국 Port Bar는 거창한 개발 도구라기보다, 내가 매일 반복하던 질문을 메뉴바에 올려둔 도구에 가깝다.
지금 어떤 워크트리에서 어떤 서버가 떠 있는지, 그 포트를 누가 잡고 있는지, 필요하면 어디까지 정리해도 되는지 확인하는 작은 화면이다.