문제는 모델이 아니라 환경이다
내가 함수 하나로 30분을 고민하는 동안, AI는 같은 함수를 15초 만에, 내가 놓친 엣지 케이스까지 챙겨서 짜낸다. reduce 한 줄로 우아하게 접어버리는 걸 보면 인정할 수밖에 없다.
“얘가 나보다 코드를 잘 짠다.”
그런데 진짜 문제는 여기가 아니었다. AI가 코드를 잘 짜는 게 아니라, 그 잘 짜는 능력을 내가 원하는 방향으로 붙들어 두지 못하는 게 문제였다.
같은 AI인데 결과가 매번 다르다. 어떤 날은 내 컨벤션에 딱 맞는 코드를 뽑고, 어떤 날은 page.tsx에 비즈니스 로직을 잔뜩 쑤셔넣는다. 변수명을 s, d, v로 짓는다. 어제 지적한 실수를 오늘 또 반복한다.
결론은 하나다. 문제는 모델의 실력이 아니라 내가 만든 환경(harness)이다.
AI 시대에 개발자는 무슨 일을 하는가
코드를 ‘치는’ 일은 이미 상당 부분 넘어갔다. 타이핑 속도로 승부 보던 시대가 아니다. 하지만 코드를 치는 일이 넘어갔다고 해서 판단하는 일, 기준을 세우는 일, 일관성을 강제하는 일까지 넘어간 건 아니다.
AI는 똑똑하지만 표류한다.
- 맥락이 조금만 흐려지면 자기 마음대로 shape을 지어낸다.
- 지난주에 합의한 규칙을 이번 주에 기억하지 못한다.
- “대충 이렇게 하면 되겠지”의 ‘대충’이 매번 다르다.
그래서 AI 시대 프론트엔드 개발자의 일은 이렇게 옮겨간다.
직접 코드를 짜는 사람에서, 코드가 짜여지는 조건을 설계하는 사람으로.
요즘 내가 실제로 시간을 쓰는 곳이다.
하네스 엔지니어링이라는 관점
‘하네스(harness)‘는 원래 코드를 특정 조건에서 실행·검증하기 위해 감싸는 골격(테스트 하네스)을 가리킨다. 나는 이 단어를 조금 넓혀 쓴다.
하네스 = AI(그리고 나 자신)가 그 안에서만 움직이도록 만드는 구속·검증·피드백의 총체.
AI의 생성 능력을 억누르는 게 아니라, 그 능력이 일관된 방향으로만 발휘되도록 제약과 검증을 거는 일이다. 이 관점으로 옮겨오면 일의 성격이 바뀐다.
| 예전의 나 | 지금의 나 |
|---|---|
| ”이 코드를 어떻게 짜지?" | "이 코드가 잘못 짜여질 수 없게 하려면 어떤 제약이 필요하지?” |
| 버그를 발견하면 고친다 | 버그를 발견하면 같은 버그가 다시 못 나오게 하는 규칙을 판다 |
| 리뷰에서 지적한다 | 지적할 필요조차 없도록 커밋 단계에서 막는다 |
| 문서를 쓴다 | AI가 코드를 짜기 전에 반드시 읽고 지나가는 관문으로 문서를 배치한다 |
핵심은 프롬프트를 잘 쓰는 게 아니라 환경을 잘 짜는 것이다. 좋은 프롬프트는 그 한 번의 대화를 좋게 만들지만, 좋은 하네스는 앞으로의 모든 대화를 좋게 만든다.
내가 실제로 설정한 하네스
어떤 가격비교 커머스 대시보드 프로젝트에서 구축한 하네스를 기록해 둔다. 크게 네 개의 층이다.
1층. 계층형 문서 — “코드를 짜기 전에 반드시 통과하는 관문”
AI에게 “우리 컨벤션 지켜서 짜줘”라고 말하는 건 효과가 없다. 컨벤션이 어디에, 어떤 순서로 놓여 있느냐가 전부다. 문서를 이렇게 계층화했다.
README (진입점) ── 작업 흐름 요약 + 문서 맵. "지금 뭘 보고 시작해야 하는가"
├─ ARCHITECTURE ── 스택·라우트·폴더 구조 + 거버넌스(훅 명세·자가점검 의무)
├─ CONVENTIONS ── 코딩 규칙 §1~20 (TDD·네이밍·스타일·Mock 전략…) "코드 작성 전 필독"
├─ PLANS ── 진행 단계와 무엇을 어떤 순서로 만들지
├─ RETROSPECTIVE ── 결함 패턴(A) / 머지 전 체크리스트(B) / 변경 이력(C)
└─ QUALITY_SCORE ── 도메인별 품질 점수표 + 채점 기준
포인트는 README에 작업 흐름을 못박아 둔 것이다.
- 컨벤션 + 회고(결함 패턴) 확인
- 타입 먼저 → 비즈니스 로직 → mock → api → 화면
- TDD (RED→GREEN→REFACTOR)
- 완료 후 머지 전 체크리스트 실행 + 품질 점수 재평가
- 커밋 규칙 준수
AI든 사람이든 이 흐름을 밟도록 진입점을 설계했다. 문서는 ‘읽어두면 좋은 것’이 아니라 작업 경로 위에 놓인 관문이어야 한다.
2층. Git Hook — “말로 부탁하지 말고 커밋에서 막아라”
문서는 결국 ‘읽어주길 바라는’ 장치다. 안 읽으면 그만이다. 그래서 진짜 강제력은 훅에 넣었다.
pre-commit에서 다음을 자동 검사한다.
- TDD 강제 — 새로 추가된
hooks/·api/·utils/파일에 동명의.test파일이 없으면 커밋 차단 - 필수 룰 즉시 차단 —
React.FC사용, 도메인 문자열 하드코딩, 인라인<svg>, 토큰을localStorage에 저장 → staged 내용을 grep해서 걸리면 차단 - 폴더 격리 — 지정된 작업 폴더 밖의 파일을 건드리면 승인 요구
- Prettier 검사 / string 리터럴 비교 경고(enum 쓰라고)
그리고 commit-msg 훅. 도메인 코드를 바꿨으면 커밋 메시지에 이런 트레일러를 강제한다.
Quality-Score: overview=98/B (95→98 JSDoc 추가)
Quality-Score: performance=100/B (unchanged)
(unchanged)라도 반드시 써야 한다. “점수표 날짜만 슬쩍 갱신하고 실제 재평가는 건너뛰는” 꼼수를 막기 위한 장치다. 날짜 컬럼은 ‘갱신했는지’만 보지만, 이 트레일러는 ‘재평가했는지’를 본다. 성가시게 만드는 게 목적이다. 성가심이 곧 검증이다.
핵심 원칙: 부탁은 무시당하지만 훅은 무시당하지 않는다.
3층. 결함 패턴 누적 — “한 번 당한 실수는 두 번 당하지 않는 시스템”
이 하네스에서 제일 아끼는 부분이다. 회고 문서의 A 섹션에 결함 패턴을 누적한다. 실제로 쌓인 것들:
- Mock shape ≠ 실제 인터페이스: mock 데이터로 화면을 다 만든 뒤 실제 API를 붙이려니, 화면이 mock의 임의 shape에 맞춰져 있어 전부 뜯어고쳐야 했다. → 예방책: 타입 먼저 → api 함수가 타입대로 mock 반환 → 화면은 api/훅으로만 소비.
page.tsx에 비-렌더링 코드 혼입: 뷰 파일에 검증 로직·상태·매핑 상수가 줄줄이 들어갔다. 버그만 고치고 구조 위반은 방치했다가 두 번 연속 지적당했다. → 예방책: page.tsx엔 JSX + 표시 변환만. 로직은 hook, 상수는 constants로 선제 분리.- 한 글자 변수명으로 가독성 파괴: 훅 반환 객체를
s로 받아s.phone,s.codeSent… 선언부까지 거슬러 올라가야 의미를 안다. → 예방책: 역할이 드러나는 이름(s→setup), 단일 문자 식별자 금지.
문서 맨 아래 규칙:
한 번 발생한 실수는 반드시 여기에 패턴화한다. “다음엔 조심”이 아니라 체크 가능한 예방책으로 적는다.
“다음엔 조심할게요”는 AI도 사람도 못 지킨다. 그래서 모든 피드백을 체크 가능한 규칙으로 번역한다. 짜증 한마디 → 결함 패턴 ID → 머지 전 체크리스트 항목 → 가능하면 훅의 grep 규칙. 피드백이 시스템으로 굳는 파이프라인이다.
4층. 품질 점수제 — “주관적인 ‘잘 짰다’를 숫자로 고정”
도메인마다 품질 점수(S/A/B/C/D)를 매기고, 그 도메인 코드를 바꿀 때마다 해당 행을 재평가한다. 채점 항목은 컨벤션과 1:1로 연동된다 — 컨벤션 위반은 그대로 감점(예: 불필요한 absolute -5점, 기존 컴포넌트 재사용 안 함 -10점).
이유는 하나다. “코드 좋네요”는 측정할 수 없고, 측정할 수 없으면 관리할 수 없다. 점수는 AI에게도 명확한 목적 함수가 된다.
이 설정의 장점
1. 재현 가능성. 대화가 바뀌어도, 세션이 끊겨도, 사람이 바뀌어도 결과물의 하한선이 유지된다. 좋은 프롬프트는 휘발되지만 하네스는 남는다.
2. 실수가 자산이 된다. 결함 패턴 누적 구조 덕분에 겪은 삽질이 그대로 방어막이 된다. 프로젝트가 오래될수록 하네스가 두꺼워지고 AI가 실수할 여지가 줄어든다.
3. 리뷰 비용의 이동. 사람이 눈으로 잡던 걸 커밋 훅이 잡는다. “React.FC 쓰지 마세요” 같은 지적을 다시 할 필요가 없다. 그 에너지를 진짜 판단이 필요한 곳에 쓴다.
4. 강제력의 계층화. 문서(권고) → 체크리스트(자가 검증) → 훅(차단)으로 강제력에 층을 뒀다. 사소한 건 경고로, 치명적인 건 차단으로 다룬다.
이 설정의 단점 · 부족한 점
이 하네스는 완성품이 아니라 계속 손대는 물건이다.
1. 초기 구축·유지 비용이 크다. 훅을 짜고, 문서를 계층화하고, 결함 패턴을 관리하는 것 자체가 상당한 일이다. 작은 프로젝트나 단발성 작업에는 과잉이다. 가치가 유지 비용을 넘어서는 시점이 있고, 그 전에 도입하면 오히려 짐이다.
2. 하네스가 사고를 가둘 수 있다. 규칙이 촘촘해질수록 “이 규칙이 지금도 옳은가”를 되묻기 어려워진다. 한때 옳았던 제약이 리팩터링이나 스택 변경 앞에서 발목을 잡는다. 하네스도 리팩터링 대상이다.
3. 강제력에 구멍이 있다. --no-verify 한 방이면 pre-commit은 우회된다. 훅은 본인 .git/config에만 걸리므로 다른 개발자 환경엔 강제되지 않는다. 진짜 강제하려면 CI 파이프라인 레벨로 올려야 하는데 아직 거기까진 못 갔다.
4. 점수제의 주관성. 채점 기준을 명문화해도 경계에서는 주관이 개입한다. 채점자(나 혹은 AI)에 따라 흔들린다. 자동 측정 지표(커버리지, 복잡도 등)와 더 엮어야 한다.
5. 결함 패턴의 무한 증식. 실수를 계속 패턴화하다 보면 회고 문서가 비대해진다. 언젠가 “읽기엔 너무 긴 관문”이 될 위험이 있다. 패턴을 폐기·병합하는 규칙까지 필요하다.
6. 결국 사람의 판단이 병목이다. 하네스는 “하지 말아야 할 것”은 잘 막지만 “무엇을 만들지”는 못 정해준다. 방향 설정, 트레이드오프, 제품 판단은 온전히 사람 몫이다. 하네스가 좋아질수록 이 병목이 더 선명해진다.
결론
AI가 나보다 코드를 잘 짠다는 사실은 더 이상 불안하지 않다. 오히려 내 일의 좌표를 또렷하게 만들어줬다.
내 일은 코드를 치는 것에서, 코드가 어떤 조건에서만 짜여지도록 제약을 설계하는 것으로 옮겨갔다. 문서를 관문으로 배치하고, 규칙을 커밋에서 강제하고, 실수를 시스템으로 굳히고, 품질을 숫자로 고정하는 일. 이게 하네스 엔지니어링이다.
코드 생성은 AI가 한다. 그 생성이 놓일 조건을 설계하는 일은 개별 코드보다 오래 남는다.