
계획, 구현, 검증과 리뷰를 거쳐 확인한 기준을 다음 작업에 남겨요.
에이전트가 성공했다고 보고한 E2E를 열어 봤어요. 배너 등록 드로어에서 '업로드 중...' 문구가 사라지길 기다리고 있었는데, 저장소에는 그 문구를 보여주는 코드가 없었어요. 이미 없는 문구가 안 보인다는 이유로 통과한 것인지, 업로드를 정말 기다린 것인지 알 수 없었어요.
Claude 설정을 처음 저장소에 넣었을 때는 결과물을 어떻게 받아 볼지까지 정하지 않았어요. 폼을 어디에 두고 Query Options를 어떻게 만드는지는 문서에 있었지만, 세부적인 선택은 여전히 제가 결과를 읽으며 고쳤어요. 모델이 제 기준을 몰랐던 부분도 많았어요. push에 리뷰어를 붙이고, 구현 전에 plan을 보여주고 기다리는 skill을 관리자 모노레포부터 복지몰까지 들인 이유예요.
한 스프린트에서는 코드를 읽지 않고 화면 동작을 확인하는 데 집중했다가 핫픽스가 많았어요. 그 뒤로는 기능과 리팩터링의 코드와 테스트를 다시 읽어요. 티켓과 plan을 본 뒤 테스트와 구현을 맡기고, 변경분 검사 다음에는 제가 diff와 화면을 봐요. CI를 거쳐 QA 더블 체크까지 요청하는 흐름이에요.

plan 승인과 diff 리뷰, 화면 확인은 제가 판단하고, push 게이트와 CI는 자동으로 검사해요
티켓에 없는 동작은 구현 전에 맞춰요
티켓을 받으면 먼저 코드로 해결할 문제인지, 기존 방식보다 나은 방법이 있는지 생각해요. 요구사항 논의에 참여한 티켓도 있지만, plan을 읽다가 모호한 동작이나 백엔드 변경이 필요한 부분을 발견하기도 해요. 그런 부분은 담당자와 맞춘 뒤 구현하게 해요.
복지몰은 디자이너 시안에 맞춰 작업하고, 다른 제품은 주로 와이어프레임을 보고 만들어요. 시안이 있으면 Figma MCP로 가져와 대조하지만 와이어프레임만으로 정할 수 없는 동작도 있어요. 글로벌 파일 업로드에서는 인증 코드 입력을 언제까지 유지할지, 업로드 실패 뒤 무엇을 보여줄지를 따로 정해야 했어요.
에이전트는 관련 코드와 가까운 구현을 찾고 plan을 써요. 기존 패턴을 따라도 되는 부분과 요구사항 확인이 먼저인 부분, 완성된 화면과 데이터를 제가 봐야 하는 부분을 구분해요. 미리 정해 둔 버그 수정이나 스키마 불일치 대응은 재현과 검증부터 시작하고요.
방향이 틀렸다면 코드가 생기기 전에 바꿔요
완성된 diff에서 방향이 다르다는 걸 발견하면 구현을 버려야 해요. 그래서 티켓 브랜치를 만드는 skill이 plan을 낸 뒤에는 auto mode에서도 제 입력을 기다리게 했어요.
템플릿에는 티켓 요약과 영향 범위, 작업 순서, 검토할 내용을 적어요. 저는 파일 목록보다 티켓을 어떤 동작으로 해석했는지를 먼저 봐요. 상태를 어디에 두고 어떤 결과가 나오면 끝나는지 읽으면서, 빠진 경우나 잘못 이해한 API가 있으면 다시 조사하게 해요. 수정된 plan을 보고 시작하면 아직 버릴 제품 코드는 없어요.
같은 수정을 프롬프트마다 반복하지 않게 했어요
처음에는 주의사항을 프롬프트 끝에 계속 붙였어요. 길어지면서 폼, Query, FSD처럼 작업에 따라 읽을 skill로 나눴어요. 에러 처리는 관리자 서비스의 정책 문서에, 디자인 시스템은 복지몰 skill에 뒀어요. 공통 기준은 AGENTS.md에 두고 필요한 설명을 찾아가게 했어요.
에이전트가 잘못 만든 코드를 고칠 때는 다음에도 같은 일이 생길지 봐요. import 방향이나 슬라이스 경계, raw query key처럼 코드로 판정할 수 있으면 lint로 옮겼어요. 기존 위반을 고친 뒤 적용 범위를 넓혔고, 두 저장소의 규칙 심각도까지 똑같이 맞추지는 않았어요.
컴포넌트가 화면 배치를 조합하는지, 도메인 데이터를 표현하는 UI인지는 사용처를 읽어야 해요. 이런 판단은 문서와 리뷰에 남겼어요.

같은 지적은 판단의 성격에 맞는 자리에 남겨 다음 구현부터 맞게 나오게 해요
Compound Engineering을 참고하면서 이 흐름을 보강했어요. 처음부터 갖춘 체계가 있었던 것은 아니에요. 반복해 고친 부분을 설명에 더했고, 모델 성능도 좋아지면서 제 코드 스타일에 가까운 결과가 나왔어요.
화면과 서버에서 어긋나는 부분을 찾았어요
시안을 구현하면 같은 viewport에서 화면을 띄우고 스크린샷을 찍게 해요. 간격과 글꼴, 아이콘, 반응형 배치뿐 아니라 열렸을 때와 오류가 났을 때도 대조해요. 남은 차이를 고친 뒤 다시 보고, 기능은 제가 직접 조작해요.
테스트의 Mock이 모든 요청을 성공으로 처리해서 실제 서버의 400 응답을 숨긴 일도 있었어요. 이후에는 서버 제약을 어긴 요청을 Mock도 거절하게 바꿨어요. dev API에서 다른 제약이 나오면 폼과 Mock, 테스트를 함께 고치고 그 서버를 쓰는 테스트로 다시 확인해요.
테스트가 기다리는 대상을 읽어요
함수와 스키마는 node, 폼과 컴포넌트는 jsdom이나 Browser Mode, 화면을 오가는 과정은 Playwright로 확인해요. 관리자 서비스의 새 테스트는 dev API를 기본으로 쓰고, 직접 만들기 어려운 실패 상태만 MSW로 재현해 .mock 파일로 구분해요.
도구를 골라도 테스트 내용이 틀리면 통과 결과를 믿을 수 없어요. 글머리의 업로드 테스트는 화면 문구 대신 실제 업로드 항목의 상태를 보도록 고쳤어요.
// before: 저장소에 없는 화면 문구가 사라지길 기다린다
await expect(drawer.getByText('업로드 중...')).toBeHidden();
// after: 진행 중인 업로드 항목이 0개인지 본다
await expect(drawer.locator('.ant-upload-list-item-uploading')).toHaveCount(0);이 대기도 진행 중인 항목이 없다는 것만 뜻해요. 업로드가 시작하지 않았거나 실패해도 통과할 수 있어서, 올린 파일과 저장 결과를 함께 확인해야 해요. QueryClient 설정도 앱과 같아야 했어요. 초기 조회 실패가 Error Boundary에 도달하지 않던 테스트를 고치고, 첫 조회 실패와 기존 데이터가 있는 재조회 실패를 나눠 봤어요.
새 기능의 기대 동작은 BDD 형식의 *.spec, 운영과 QA에서 발견한 오류는 *.test에 적어요. 테스트를 읽으면 입력과 행동, 기대한 결과가 보여야 해요. 리팩터링은 기존 동작을 먼저 테스트로 잡고 바꾸며, 새 테스트 패턴은 제품 코드를 일부러 깨뜨려 실패하는지 확인하도록 AGENTS.md에 남겼어요. plan에 적힌 성공 조건을 실제로 검사하는지도 같이 읽어요.

이름으로 명세와 버그 재현을 나누고, 목 없이 확인할 수 있는 자리부터 골라요
프로그램 검수는 병원 관리자와 Back-Office를 모두 지나가요. 병원 관리자가 등록하고 검수를 요청하면 Back-Office에서 검수중으로 바꾼 뒤 승인하거나 반려해요. 그래서 두 앱의 dev 서버를 3000과 3002에서 띄우고 dev API를 호출하는 별도 Playwright 설정을 뒀어요. localhost에서 쿠키 도메인이 겹치므로 병원 관리자 로그인은 다른 브라우저 context로 나눴어요.
반려를 확인할 때도 '반려'라는 글자만 찾으면 안 됐어요. 드로어에 버튼으로 이미 있는 문구라 상태가 안 바뀌어도 찾을 수 있어요. 테스트가 입력한 반려 사유가 상세에 다시 나타나는지로 바꿨어요.

반려는 화면에 원래 있는 글자가 아니라 테스트가 입력한 사유로 확인해요
따로 맡길 티켓과 함께 바꿀 코드를 나눠요
독립적인 티켓은 Orca의 별도 worktree와 세션에서 진행해요. 공용 API와 구조가 함께 바뀌는 리팩터링은 한 세션에서 이어 가요. 어느 쪽이든 완성본을 받으면 제가 diff와 테스트를 읽고, 의도와 다른 부분은 이유를 설명해 다시 맡겨요.
에이전트의 리뷰도 그대로 적용하지는 않아요. plan에서 남겨 두기로 한 동작과 실제 지적의 재현 조건을 코드에서 확인해요. 설계를 함께 결정한 동료의 리뷰와는 다른 입력으로 다루고 있어요.

작업 간 의존성으로 세션을 나누고, 완성본은 직접 읽고 검토해요.
세션을 늘리자 검사들이 겹쳤어요
별도 worktree는 브랜치와 의존성이 다른 작업에 영향을 주지 않게 해줬지만, 각 세션이 전체 lint와 테스트를 동시에 시작하는 문제는 막지 못했어요. Turbo가 모든 패키지를 콜드로 돌리며 CPU를 점유했고, 검사가 겹친 실행은 exit 143으로 끝나기도 했어요. 종료 원인을 그 코드만으로 단정할 수는 없었지만 검사를 마치지 못해 push도 판단할 수 없었어요.
처음 관리자 push 게이트는 전 패키지 pnpm run lint였어요. 이후에는 변경 파일의 ESLint와 해당 패키지의 타입 검사, vitest --changed로 관련 테스트를 먼저 보게 했어요. 타입 검사는 소비하는 쪽까지 영향을 보고, Knip은 저장소 참조 그래프를 봐요. 관리자 CI는 lint와 단위 테스트를 돌리고 dev API 테스트와 E2E는 따로 확인해요. 복지몰 CI는 lint와 타입 검사, Knip을 돌려요.
검사는 작업한 worktree에서 해요. 캐시를 쓰려고 메인 worktree에 잠깐 합치지는 않아요. 실패하면 원인에 따라 plan이나 구현, 테스트로 돌아가고, 완료 보고도 plan을 따랐다는 말보다 처음의 요구사항과 대조해요. plan 자체가 잘못될 수도 있으니까요.
push 전에 멈춰야 할 이유를 구분해요
별도 리뷰어가 origin/develop...HEAD를 읽고 Critical, Warning, Nit과 확신도를 붙여요. 처음에는 두 저장소 모두 리뷰를 먼저 돌렸어요. Critical이 있으면 오래 걸리는 검사를 시작하지 않으려는 순서였어요. 복지몰은 그대로 두었고, 관리자는 대기 시간을 줄이려고 리뷰와 검사를 같이 시작해 둘 다 끝난 뒤 push하도록 바꿨어요.
관리자에서 한 화면의 마크업과 문구, 스타일만 바꿨다고 판단한 작업은 리뷰어와 테스트를 빼고 ESLint와 타입 검사, Knip을 돌려요. 분기나 데이터 흐름을 바꾸면 작은 수정이어도 리뷰와 검사를 모두 거쳐요.
Critical은 고치고 다시 검사해요. 해결할 수 없거나 제 판단이 필요한 항목이 남으면 멈춰요. Warning과 Nit은 보고하지만 그것만으로 막지는 않아요. 반면 lint나 타입 검사, 테스트 실패는 다음 단계로 넘어가지 못하게 했어요.

Critical은 수정 후 다시 검증하고, 해결하지 못한 항목은 제 확인을 받아요.
리뷰어가 반복하는 오탐은 예외로 설명했어요. Back-Office와 병원 관리자의 src/app/api route가 비슷해서 공통 핸들러 팩토리를 권하는 일이 반복됐어요. 각 앱의 백엔드와 응답 타입에 묶인 코드라 분리한 채로 두고, 그 추출을 다시 권하지 않도록 적었어요.
배포할 때는 합쳐진 diff를 다시 봐요
PR을 각각 읽을 때는 괜찮아도 한데 모으면 같은 역할의 추상화가 겹치거나 이름과 FSD 경계가 어긋날 수 있어요. 배포 PR을 준비할 때 합쳐진 diff를 읽는 이유예요.
배포 뒤에도 불편한 패턴을 만나면 저장소에서 같은 코드를 찾아 고쳤어요. 복지몰의 entities를 없애고 page 간 직접 참조를 막는 규칙도 이렇게 시작해 develop에 반영했어요. 관리자와 복지몰의 구조를 전부 같게 만든 작업은 아니었어요. FSD 레이어를 다시 나눈 과정은 따로 적었어요.
검사에서 뺐던 상담 채팅은 다시 lint와 테스트에 넣었고, 분리할 이유가 있던 API route에는 리뷰 예외를 남겼어요. 같은 지적이 돌아올 때마다 규칙을 더 세게 만들기보다, 실제 코드에 필요한 검사인지도 다시 보게 됐어요.
참고
- Compound Engineering: every.to 원문
- 관련 글: 테스트 코드를 도입하면서 세운 기준
- 관련 글: FSD 레이어를 저장소에 맞게 다시 나눈 과정


