혼자서 열두 개 작업 굴리기
구현을 AI에 맡기면 병목은 컨텍스트 스위칭으로 옮겨간다 — worktree로 작업 공간을 가르고, 옵시디언에 히스토리를 쌓고, 대시보드로 전체를 본 기록
구현을 AI에 맡기면 병목은 컨텍스트 스위칭으로 옮겨간다 — worktree로 작업 공간을 가르고, 옵시디언에 히스토리를 쌓고, 대시보드로 전체를 본 기록
구현을 AI에 맡기기 시작하면 곧 좀 이상한 지점에 도달한다. 한 작업이 도는 동안 내가 할 일이 없어진다는 것이다. 그래서 다음 작업을 잡고, 그것도 돌기 시작하면 또 하나를 잡는다.
평소에는 4~5개, 많을 때는 터미널을 열두 개 띄워놓고 동시에 돌린 적도 있다. 사람 혼자서는 컨텍스트 스위칭 비용 때문에 불가능한 숫자인데, 구현이 각자의 자리에서 알아서 돌고 나는 상태만 관리하니까 가능했다.
대신 이 규모가 되면 세 가지가 없으면 무너진다. 작업 공간 분리, 히스토리 기록, 그리고 전체를 조망하는 화면이다. 구현 속도가 병목이 아니게 되는 대신 내가 상황을 파악하는 속도가 병목이 되기 때문이다.
같은 저장소에서 브랜치를 오가며 여러 작업을 하면 반드시 사고가 난다. A 작업이 구현 중인데 B 작업 QA를 하려고 체크아웃하는 순간 A의 작업 상태가 꼬인다. 그래서 작업마다 worktree를 분리했다.
# worktree 디렉토리는 티켓 번호로, 브랜치에는 업무 요약까지
git worktree add ../proj-PROJ-1234 \
-b feature/PROJ-1234__mypage-email-consent
worktree 디렉토리는 티켓 번호만으로 만들고, 업무 요약은 브랜치 이름에만 붙인다 — feature/{티켓번호}__{업무 요약} 형식. 요약을 디렉토리가 아니라 브랜치에 두는 이유가 있다. worktree 하나에 여러 작업이 브랜치로 들어올 수 있기 때문이다. 디렉토리는 재사용되는 공간이고 작업의 정체성은 브랜치에 있으니, 디렉토리 이름에 특정 업무 요약을 박아두면 나중에 이름과 내용이 어긋난다.
브랜치에 요약을 붙이는 효과는 확실하다. 티켓 번호로만 브랜치를 만들면 작업이 서너 개 쌓였을 때 어느 브랜치가 무슨 작업인지 번호를 다시 조회해봐야 하는데, 어느 페이지의 어떤 작업인지 드러나는 요약이 있으면 브랜치 목록에서도, 대시보드에서도, 깃랩 MR에서도 바로 구분된다. 이 요약도 /work가 티켓 내용에서 뽑아서 붙인다.
/work가 이렇게 worktree와 브랜치를 만들고, 클로드 코드 세션도 그 worktree 안에서 돈다. 각 작업이 독립된 디렉토리를 가지니까 구현이 도는 작업, 테스트 서버에 올라간 작업, QA 중인 작업이 서로를 전혀 건드리지 않는다. 병렬 처리는 사실 worktree 없이는 성립하지 않았다.
작업이 여러 개 돌면 “이 티켓 어디까지 갔더라”를 머리로 기억할 수 없다. 그래서 옵시디언에 작업 단위로 노트를 남기게 했다. 각 스킬이 단계를 끝낼 때마다 해당 작업 노트에 기록을 추가한다.
연동이라고 하기엔 아직 대단한 수준은 아니다. 나중에 확장할 것을 염두에 두고 일단 기록부터 쌓아두는 단계에 가깝다. 그런데 이 단순한 기록이 이미 제값을 한다. 이전 작업에서 궁금한 게 생기면 클로드 코드에 그냥 물어본다. 그러면 옵시디언 노트들을 뒤져서 당시 히스토리를 찾아온다. 깃랩 커밋에는 “무엇을 바꿨는지”만 남고 “왜 그렇게 결정했는지”, “중간에 무슨 논의가 있었는지”는 누락되기 마련인데, 그 빠진 맥락이 노트에 있다. 커밋 내용에 없는 이전 히스토리를 문서로 복원할 수 있는 것이다.
다음날 작업을 이어갈 때는 보통 어제 쓰던 세션을 그대로 다시 연다. 세션이 살아 있으면 맥락도 같이 남아 있어서 따로 할 일이 없다.
문제는 세션이 끊겼을 때다. 컨텍스트가 날아갔거나, 한참 묵혀뒀다 다시 잡는 작업이면 “이거 어디까지 갔더라”부터 다시 세워야 한다. 이때 깃랩을 뒤지게 하면 커밋 목록과 MR diff가 통째로 컨텍스트에 들어오면서 토큰이 꽤 나간다. 대신 티켓 히스토리 노트를 먼저 읽게 하면 정리된 문서 한 장으로 상황 파악이 끝나고, 바로 이어서 진행할 수 있다. 세션이 들고 있던 맥락을 노트가 대신 들고 있다가 새 세션에 건네는 셈이다. 요약된 기록은 사람에게만 좋은 게 아니라 AI에게도 싼 컨텍스트다. QA 증적 캡처를 스크립트 한 번으로 줄인 것과 원리가 같다. 에이전트에게 원본을 뒤지게 할 것인가, 정리된 결과를 건네줄 것인가의 차이다.
히스토리는 작업이 끝난 뒤에 몰아 쓰는 게 아니라 진행하면서 자동으로 쌓여야 한다는 걸 여기서 배웠다.
마지막 조각은 대시보드다. 작업이 4~5개씩 병렬로 돌면 각각이 어느 단계인지 파악하는 것 자체가 일이 된다. 그래서 지라, 컨플루언스, 깃랩을 연동한 개인 대시보드를 직접 만들었다. 로컬에서 도는 웹앱인데, 컴퓨터를 켜면 자동으로 실행되게 해뒀다. 브라우저 시작 페이지가 아니라 아예 업무 시작 페이지인 셈이다.
한 작업당 카드 하나로, 이런 정보가 보인다.
아침에 대시보드를 열면 오늘 뭘 해야 하는지가 바로 보인다. 구현이 끝나고 QA를 기다리는 작업부터 잡고, 막혀 있는 작업은 왜 막혔는지 히스토리를 확인한다. 예전의 나는 이 정보를 얻으려고 터미널 탭을 뒤지고 티켓 시스템을 새로고침했다. 지금은 상태 파악에 쓰는 시간이 거의 없다.
열두 개가 동시에 돈다고 하면 따라오는 질문이 있다. 내가 안 읽은 코드가 그대로 머지되는 거 아니냐는 것이다.
먼저 짚을 게 있다. diff는 작업이 끝난 뒤에만 보는 게 아니다. 진행 중에도 무엇이 어떻게 바뀌었는지 정확히 확인해야 할 때마다 계속 연다. 코드를 읽는 창구가 diff인 셈이다.
그 위에 게이트가 두 겹 있다. 작업이 끝나면 변경된 파일을 전부 놓고 셀프 리뷰를 한다. AI가 짰다는 이유로 안 읽고 넘기지 않는다. 그다음 셀프 리뷰를 통과한 작업만 릴리즈 브랜치로 MR을 올리고, 거기서 팀 코드리뷰를 받은 뒤에 최종 병합한다.
그러니까 병렬로 도는 건 구현까지다. 병합은 병렬이 아니다. 열두 개가 동시에 돌아도 릴리즈로 들어가는 문은 하나고, 그 문에서 사람 눈을 두 번 거친다.
충돌도 worktree가 없애주지는 않는다. worktree가 막아주는 건 체크아웃 충돌뿐이고, 여러 작업이 같은 파일을 건드렸다면 머지 시점에 충돌은 그대로 난다. 해소 자체는 평범하게 한다. 대신 해소한 뒤에 테스트 서버에서 실제 동작을 다시 확인한다. 충돌 해소는 diff가 그럴듯해 보이고 빌드가 통과해도 의미가 깨져 있을 수 있어서, 코드가 맞아 보이는 것으로 끝내지 않는다.
정리하면 스킬이 각 단계를 자동화하고, worktree가 병렬 실행을 가능하게 하고, 옵시디언이 과정을 기록하고, 대시보드가 전체를 보여주는 구조다. 넷 중 하나만 빠져도 병렬 워크플로우는 어딘가에서 새기 시작한다.
여기서 말한 스킬과 /work가 무엇인지는 클로드 코드 스킬로 티켓부터 배포까지 자동화한 이야기에 정리해뒀다.