프로젝트를 여러 개 동시에 맡으면 제일 먼저 무너지는 게 정합성이다.

백엔드가 응답 필드 이름을 하나 바꿨는데, 그걸 아무도 모른다.

배포하고 나서 화면이 비어 있는 걸 보고서야 안다.

디자인도 마찬가지다. 시안에서 색이 바뀌었는데 코드에는 하드코딩된 헥사값이 그대로 남아 있다.

이걸 사람이 추적하는 게 맞나 싶었다.

그래서 규칙을 하나 세웠다.

기획서를 보고 손으로 옮기지 않는다. 단일 진실원천에서 코드를 파생시킨다.

계약(contract)이 먼저다

이 방식의 전제는 "무엇이 진실인가"를 먼저 정하는 것이다.

  • API의 진실 = OpenAPI 스펙. 백엔드가 준다. 여기서 타입·쿼리훅·목(mock)을 만든다.
  • 디자인의 진실 = 디자인 토큰. 색상·타이포는 토큰을 참조하고 하드코딩하지 않는다.

이렇게 두면 무슨 일이 생기냐면, 백엔드가 스펙을 바꿨을 때 TypeScript가 깨진 자리만 정확히 짚어준다.

"API 바뀌었나?"를 사람이 물어보고 다니지 않아도 된다.

컴파일러가 대신 물어봐준다.

기획서가 엑셀이나 표로 오면 필드 정의를 타입과 폼으로 옮기고, 구두나 메신저로 오면 먼저 짧은 스펙으로 정리해 확인을 받은 뒤 구현한다.

말로 받은 요구사항을 바로 코드로 옮기면, 나중에 "그렇게 말한 적 없다"에서 이길 방법이 없다.

규칙을 문서가 아니라 컨텍스트로 만들기

원칙을 정하는 건 쉽다. 지키는 게 어렵다.

특히 프로젝트마다 스택도 컨벤션도 다르면, 어제 쓴 패턴이 오늘 프로젝트에서는 틀린 패턴이 된다.

그래서 규칙을 작업 환경 자체에 심었다.

work/
├── CLAUDE.md                   # 워크스페이스 공통 규칙
├── rex-parking-enforcement/
│   └── CLAUDE.md              # 이 프로젝트의 스택·컨벤션
└── couple-finance/
    └── CLAUDE.md              # 이 프로젝트의 스택·컨벤션

3층 구조다.

  • 루트는 공통 규칙 — 커밋 스타일, 브랜치 정책, 계약 중심 워크플로
  • 프로젝트별 파일은 그 프로젝트의 컨벤션 — 상태관리 패턴, queryKey 규칙, 디자인 토큰 네이밍, 파일 명명
  • 충돌하면 프로젝트 규칙이 이긴다

핵심은 이걸 읽으라고 시키지 않아도 자동으로 적용된다는 점이다.

그 프로젝트의 파일을 건드리는 순간 그 프로젝트의 규칙이 로드된다.

예전에는 매번 "우리는 이렇게 해요"를 설명해야 했다.

지금은 설명하지 않는다. 환경이 이미 알고 있다.

기능 개발을 파이프라인으로

기능 하나 만드는 과정을 고정된 순서로 만들었다.

요청 → [0] 대상 프로젝트·기능 식별
     → [1] 계약 확정 (OpenAPI / Figma / 표)
            └ 추측 금지. 모르면 TODO + 목(mock)으로 표시
     → [2] 계획 제시 (화면·타입·훅·상태·i18n)
     → [3] 구현 (기존 패턴 준수)
     → [4] 품질 게이트: type-check → lint
     → [5] 요약 + 백엔드·기획 확인 필요 항목

여기서 제일 중요한 건 **[1]단계의 "추측 금지"**다.

응답 필드 이름을 모를 때 그럴듯하게 지어내면, 그 코드는 컴파일도 되고 리뷰도 통과한다.

그리고 연동하는 날 터진다.

모르면 모른다고 표시하고 목으로 남겨두는 게, 아는 척하는 것보다 항상 싸다.

[5]단계에서 "백엔드에 확인 필요"를 명시적으로 뽑아내는 것도 같은 이유다.

기능이 끝났다고 끝난 게 아니라, 남은 불확실성이 무엇인지가 함께 나와야 한다.

자동화는 조용히 도는 게 좋다

파일을 편집할 때마다 그 파일이 속한 프로젝트의 포맷터가 자동으로 돈다.

파일 편집 발생
   → 훅 실행
   → 확장자 검사 (ts/tsx/js/jsx만)
   → 파일에서 가장 가까운 package.json 을 찾아 프로젝트 루트 역추적
   → 그 프로젝트의 로컬 prettier --write + eslint --fix
   → 항상 성공으로 종료 (작업을 막지 않음)

멀티 프로젝트라서 "이 파일이 어느 프로젝트 것인지"를 경로로 자동 판별하는 게 핵심이었다.

가장 가까운 package.json을 위로 올라가며 찾으면 된다.

그리고 마지막 줄이 중요하다 — 항상 성공으로 끝낸다.

포맷터가 실패했다고 작업 전체를 막으면, 사람은 결국 자동화를 꺼버린다.

자동화는 도와주기만 해야지, 길을 막으면 안 된다.

품질은 3중 그물로

한 겹으로 막으려 하지 않았다.

시점무엇이무엇을
편집 직후포맷 훅스타일 (prettier + eslint --fix)
기능 완료파이프라인 4단계타입·API 정합 (type-check)
커밋 직전husky pre-commit최종 확인 (lint-staged)

그물이 촘촘해서 좋은 게 아니라, 각 그물이 다른 걸 잡는다는 게 좋다.

스타일은 편집 직후에, 정합성은 기능 완료 시점에, 최종 확인은 커밋에.

같은 검사를 세 번 하는 게 아니라 서로 다른 실패를 서로 다른 시점에 잡는다.

AI는 바닥을 낮추는 게 아니라 천장을 높인다

사내 세미나 준비를 하면서 이 얘기를 정리한 적이 있는데, 여기서도 같은 생각이다.

"AI로 개발한다"는 말이 종종 **"대충 빨리 만든다"**로 읽힌다.

내 경험은 반대였다.

예전에는 시간이 없어서 생략하던 것들이 있었다. 테스트, 문서, 디자인 시스템, CI 설정.

지금은 그걸 기본으로 켜고도 빠르다.

정석을 생략해서 빨라진 게 아니라, 정석을 빠르게 할 수 있게 된 것이다.

그리고 사람이 하는 일이 바뀌었다.

타이핑이 아니라 설계·검증·품질 게이트를 한다.

계약을 정하고, 경계를 긋고, 결과가 맞는지 확인하는 일.

이게 더 어렵고 더 재밌다.

맹신하지는 않는다

솔직히 적어두면, 이 방식에도 한계가 있다.

  • 계약이 없으면 아무것도 안 된다. OpenAPI를 안 주는 백엔드가 있으면 이 파이프라인은 절반만 돈다.
  • 생성된 코드는 검증을 사람이 해야 한다. 컴파일이 된다고 맞는 게 아니다.
  • 보안·라이선스 같은 건 자동화가 판단할 수 없다. 가드레일은 사람이 친다.

그래서 나는 이걸 "자동으로 개발해주는 시스템"이라고 생각하지 않는다.

사람이 판단해야 할 지점을 명확하게 만들어주는 시스템에 가깝다.

무엇을 확인해야 하는지 알려주고, 나머지 반복은 대신 해준다.


정리하면 이렇다.

기획서를 손으로 옮기던 시절에는 정합성이 사람의 기억력에 달려 있었다.

계약에서 파생시키기 시작한 뒤로는 정합성이 타입 시스템에 달려 있다.

사람은 잊어버리고, 타입 시스템은 잊어버리지 않는다.

그 차이 하나가 생각보다 컸다.