AI 엔지니어링

프런트엔드 AX 설계기 6편 - done은 직관으로 정하는 상태가 아니다

Kir93 2026. 7. 17. 17:51
728x90
반응형

에이전트에게 일곱 번째 task를 맡긴 참이었다. 구현 도중 에이전트가 두 번째 task —이미 done으로 닫아둔— 의 설계가 틀렸다는 걸 스스로 드러냈다. 나는 잠깐 멈췄다. 상태 판에는 분명 done이라 적혀 있는데, 그 done은 더 이상 사실이 아니었다. 팀에 AI 보조 개발을 들이면 이 장면을 반드시 만난다. 사람이 혼자 짤 때보다 에이전트는 더 자주, 더 빨리 앞선 결정을 무너뜨린다(빈도는 내 운영 관찰이지 벤치마크는 아니다 — 추정으로 읽어달라). 그런데 내가 쓰던 상태 도구에는 그 상황을 적을 칸이 없었다.

이 글은 그 뒤로 done이라는 상태를 다시 설계한 작업기다. 두 군데가 나를 물었다. done에서 나오는 쪽과, 애초에 done으로 들어가는 쪽. 두 번 다 내 직관이 틀렸고, 두 번 다 작은 측정이 그걸 바로잡았다. (2026년 중반 기준 워크플로이고 도구 동작은 자주 바뀌니, 규칙보다 개념을 가져가시길.)

상태는 한 방향으로만 흐르지 않았다

내 spec/task 상태는 여느 이슈 트래커처럼 한 방향이었다. specification → planned → in-progress → done, 한번 done이면 끝. 이 모델은 "완료된 일은 완료된 채로 남는다"는 가정 위에 서 있다. 사람만 일할 땐 그럭저럭 버텼는데, 에이전트가 들어오자 그 가정이 자꾸 깨졌다.

 

그래서 방향을 하나 더 텄다. done → in-progress → planned거꾸로 가는 경로를 상태 머신의 정식 시민으로 넣은 것이다. 무효화된 작업을 억지로 done에 남겨두거나 전혀 새 task로 갈아 끼우는 대신, 상태 자체가 "아직 할 일이 남았다"를 정직하게 말하게 만드는 게 목표였다.

되돌릴 땐 가장 앞선 규칙 하나만 적용했다

거꾸로 갈 때 spec의 상태를 어디까지 내릴지가 관건이었다. 규칙을 여러 갈래로 흩뿌리면 상태가 모호해진다. 그래서 위에서부터 가장 먼저 들어맞는 규칙 하나만 적용하도록 못 박았다.

 

어떤 task가 pending으로 되돌아갔는데(rework) 다른 task 중 in-progress가 하나라도 있으면 spec은 in-progress로 내린다. in-progress인 게 하나도 없으면 planned까지 내린다. 무효화된 게 원래부터 pending이던 task 뿐이라면 spec은 움직이지 않는다. 진행도가 높은 쪽부터 평가해 내려오니 결과가 늘 하나로 떨어졌고, 역전의 바닥은 planned였다 — specification까지는 내려가지 않는다.

처음엔 무효화된 task를 그냥 done인 채로 두고 "나중에 고치자"는 메모만 붙였다. 그게 대시보드를 거짓말쟁이로 만들었다. 다음 실행에서 에이전트는 그 done을 믿고 잘못된 전제로 코드를 짰다. 그래서 규칙을 이렇게 바꿨다. 되돌아온 task는 상태를 되돌리고, rework_required로 표식 하고, 딸린 작업을 depends_on으로 추적한다. 아래는 그렇게 정착한 task 문서의 머리말이다. 그대로 복사해 쓸 수 있다.

# task 문서 frontmatter — 무효화되어 되돌아온 상태
---
task: 2
title: 토큰 갱신 처리
status: pending          # done에서 되돌림
rework_required: true    # 되돌아온 작업이라는 표식 — 항상 pending과 짝
depends_on: [1]          # 이 task가 흔들리면 함께 봐야 할 하위 작업
---
# 상위 spec frontmatter — 규칙에 따라 함께 역전
---
feature: auth-refresh
status: in-progress      # 다른 task가 in-progress라 여기까지만 내림
---

되돌림은 한 층에서 멈추지 않았다

재미있는 일은 한 계층 위에서 벌어졌다. 여러 spec을 묶는 큰 작업 단위를 initiative라 부르자(예: 플랫폼 재작성). 모든 슬라이스가 done이 되면 initiative도 done으로 닫히고 파일이 아카이브 폴더로 넘어간다. 그런데 그 안의 spec 하나가 되돌려져 "전부 완료" 선이 깨지면 어떻게 될까.

 

토큰 로직 한 줄을 되돌린 작은 rework가 세 계층을 거슬러 올라갔다. task가 done에서 pending으로, spec이 done에서 in-progress로, 그리고 아카이브 됐던 initiative가 다시 active로 되살아났다. 파일은 아카이브 폴더에서 원래 자리로 옮겨지고 문서 안의 상대 경로 링크까지 다시 계산됐다. 상태 머신이 한 층에서만 양방향인 게 아니라 되돌림이 위로 전파된다는 것 — 이게 이 설계에서 내가 가장 마음에 든 부분이다.

한 가지 함정도 여기서 배웠다. spec과 실제 코드가 어긋났는지 검증하는 단계는 반드시 done으로 닫기 전에 돌려야 한다. 일단 아카이브로 넘어간 spec에서는 그 검증이 스스로 건너뛰기 때문에, 사후에 돌리면 아무것도 못 잡는다. 완료 정리도 자동으로 하지 않고 사용자에게 물어보게 뒀다 — 배포나 후속 작업이 남아 있으면 거절할 수 있어야 하니까.

그런데 done은 애초에 누가 내주나

여기까지가 done에서 나오는 쪽 이야기다. 그러다 반대편이 눈에 들어왔다. 애초에 작업이 done으로 들어가는 문은 누가 지키나. 에이전트가 짠 코드를 내가 다 읽을 수는 없으니, 나는 AI가 AI의 코드를 리뷰해 통과·실패를 가르는 게이트를 뒀다. 이른바 LLM-as-judge(모델이 심판을 보는 방식)다.

 

첫 실수는 코드를 짠 모델에게 그대로 채점을 시킨 것이었다. 자기 출력에는 후하다. self-preference bias라 부르는 이 편향 때문에 게이트는 늘 초록불만 켰다. 그래서 코드를 짠 쪽(writer)과 리뷰하는 쪽(evaluator)을 아예 다른 호출로 분리했다. 초기 분리 실험에선 결함을 잡아내는 recall이 대략 두 배가 됐다(초기 측정치라 방향성으로 본다). 중요한 건 점수를 손보는 게 아니라 "누가 평가하느냐"를 바꾸는 구조였다.

관점을 늘릴수록 좋다는 건 착각이었다

리뷰를 한 번에 죽 보는 대신, 서로 다른 관점(lens)으로 나눠 보고 합치기로 했다. 여기서 "관점은 많을수록 안전하다"는 직관이 자연스럽게 올라온다. 재보니 아니었다.

 

관점을 1개에서 2개로 늘릴 때 recall이 확 올랐고, 2개에서 무릎이 꺾여 3개부터는 거의 평평했다. 정직하게 덧붙이면, 처음 방향성 측정에선 더 큰 폭이 나왔지만 표본이 얇았다(thin-n). 조작된 정답 세트로 통제해 다시 재니 recall은 81%에서 88%로, +7pp였다(pooled 기준, 변별이 필요한 fixture에선 +10pp). 직관만 틀리는 게 아니라 통제 없는 측정도 과장한다는 걸 이때 배웠다 — 그래서 헤드라인 숫자는 통제값으로 잡는다.

비용 걱정도 빗나갔다. "관점 2개면 단가도 2배, 게이트가 4배 비싸지는 것 아니냐"라고 지레 겁먹었는데, 실측은 동률이었다. 관점 패널이 한 번의 호출 안에서 돌기 때문에 모델 호출 수 자체가 그대로였다. 비용을 "관점 수"가 아니라 "호출 수"로 세는 순간 사라지는 걱정이었고, 이것도 재보기 전엔 안 보였다.

verdict가 흔들리자 그다음이 무너졌다

마지막으로 게이트의 출력 자체를 손봤다. 관점 패널로 프레이밍을 바꾸자, 사소한 diff를 통과(PASS)가 아니라 "평가 불능(N/A)"으로 밀어내는 회귀가 생겼다. 게이트가 조용히 무력화된 것이다. 다행히 미리 만들어둔 골든 세트가 이 회귀를 잡아냈고, "발견된 문제가 없으면 PASS, N/A는 정말 평가가 불가능할 때만"으로 verdict의 의미를 못 박아 고쳤다. 정밀도를 조이는 손잡이도 발견 개수의 상한이 아니라 확신 임계(confidence-floor)여야 한다는 걸 이 과정에서 정리했다. 게이트가 내보내는 신호가 흔들리면, 그 신호를 읽는 다음 단계 — 바로 done으로 들어가는 그 문 — 가 통째로 오염되기 때문이다.

정리하며

done은 종착역이 아니라 양방향 상태다. 무효화되면 task에서 spec으로, initiative까지 거꾸로 전파된다. 그리고 done을 내주는 리뷰 게이트의 손잡이 — 누가 평가하는가, 관점을 몇 개 보는가, 비용을 무엇으로 세는가, verdict가 무슨 뜻인가 — 는 감이 아니라 측정으로 잡아야 한다. 두 경계 모두 순진한 직관은 틀렸고, 작은 측정 한 번이 그걸 바로잡았다.

당장 할 일은 가볍다. 지금 쓰는 task 템플릿에 rework_required 한 줄과 역방향 규칙 세 줄을 넣고, 리뷰 게이트가 있다면 관점을 하나 더 켜기 전에 작은 골든 세트로 recall이 정말 오르는지 딱 한 번만 재보라.

결국 done도 verdict도, 사람의 의도와 에이전트 사이에 놓인 인터페이스다. 그 인터페이스를 상태로 모델링하고 정합하게 유지하는 일은 화면을 다루든 에이전트를 다루든 프런트엔드가 늘 해온 그 craft다. 표면이 하나 늘었을 뿐이다.

 

2026.06.26 - [AI 엔지니어링] - 프런트엔드 AX 설계기 5편 - 테스트를 지워 초록불을 만드는 AI

반응형