앱 공장 10화 – 제 설계를 검증 AI 셋이 만장일치로 부쉈습니다 — 자동화 loop를 직접 짠 이유

앱 공장 10화 영상 화면 — 직접 쓴 하류 루프 전부가 297줄이고 외부 의존성이 0임을 보여주는 지표

작성자

· 카테고리:

,

이미 잘 만들어진 반복 실행 엔진 위에 파이프라인을 얹으려 했습니다. 그런데 설계안을 검증 에이전트 셋에게 따로 돌렸더니 셋 다 같은 지적을 했습니다. 그 위에는 얹히지 않는다는 것이었습니다.

앱 공장 시리즈 10화 영상입니다. 현재 영상은 예약 공개 전이라 재생되지 않을 수 있습니다.

1. 반복 실행 엔진부터 골랐습니다

자동화 파이프라인을 설계하면서 제일 먼저 정한 것이 반복 실행 엔진이었습니다. 이미 잘 만들어져 있는 것이 있으니 그 위에 얹기만 하면 된다고 생각했습니다.

그 설계안을 검증 에이전트 셋에게 따로 돌렸습니다. 셋이 만장일치로 같은 결론을 냈습니다. 그 엔진 위에는 이 파이프라인이 얹히지 않는다는 것이었습니다.

먼저 표현을 정확히 하겠습니다. 버린 것은 엔진이 아닙니다. 그 엔진은 제가 만든 것도 아니고 자기 목적에는 잘 맞게 돌아갑니다. 제가 만들어 뒀다가 버린 것은 그 위에 파이프라인을 얹겠다는 설계입니다. 설계 문서에는 범용 반복 엔진으로 오분류했다고 적어 뒀습니다. 오분류한 쪽은 저였습니다.

2. 셋이 지적한 것은 같은 세 가지였습니다

첫째, 데이터 계약입니다. 그 엔진은 정해진 파일 이름과 정해진 스키마만 읽습니다. 우리 상태는 roadmap.json에 들어 있는데 그것은 쳐다보지 않습니다. 게다가 자기가 찾는 파일이 없으면 빈 껍데기를 새로 만들어 버립니다. 우리 로드맵을 들고 들어가도 엔진은 그것을 무시하고 자기 파일을 짓습니다.

둘째, 검증 기계입니다. 그 엔진이 통과 판정에 쓰는 것은 테스트와 빌드와 린트입니다. 코드에는 잘 듣지만 기획이나 브랜딩이나 화면 설계 단계에서는 빌드할 것이 아예 없습니다. 신호가 0입니다. 그리고 리뷰어 승인이 마일스톤마다 도는 것이 아니라 전부 끝난 뒤에 한 번 돕니다. 저는 마일스톤 하나마다 별도 검증자에게 물어야 했으니 승인이 붙는 시점 자체가 어긋나 있었습니다.

셋째가 제일 컸습니다. 그 엔진은 작업이 끝나면 취소 명령으로 종료하는데 그 명령이 상태 파일을 정리해 버립니다. 진행 상태를 세션 안에 두는 구조이기 때문입니다. 그런데 앱 하나를 만드는 데는 며칠이 걸립니다. 로드맵이 세션과 함께 사라지면 다음 날 이어서 할 수가 없습니다. 하루를 넘기는 작업에서 상태가 세션에 묶여 있다는 것은 그 자체로 결격이었습니다.

세 가지를 나란히 놓고 보니 공통점이 있었습니다. 그 엔진이 못 만들어진 것이 아닙니다. 자기 목적에는 정확히 맞게 만들어져 있고 지금도 잘 씁니다. 문제는 제가 그것을 아무 작업이나 태울 수 있는 범용 반복 엔진이라고 읽었다는 데 있었습니다. 실제로는 데이터 계약과 종료 조건이 이미 정해져 있는 하나의 작업 절차였습니다. 범용처럼 보이는 것과 범용인 것은 다릅니다.

3. 루프가 필요한 자리를 먼저 갈랐습니다

그렇다고 반복 루프 자체가 필요 없었던 것은 아닙니다. 다시 설계하면서 파이프라인을 두 층으로 갈랐더니 루프가 필요한 자리와 필요 없는 자리가 분명해졌습니다.

앞쪽을 상류라고 부릅니다. 아이디어 검증, 기획, 브랜딩, 화면 설계, 앱 골격 생성, 첫 구현까지입니다. 순서가 정해져 있고 끝이 있는 유한한 흐름입니다. 되돌아올 일이 없으니 수렴할 때까지 반복한다는 개념 자체가 성립하지 않습니다. 여기에 루프 엔진을 얹으면 얻는 것은 없고 문맥만 먹습니다. 그래서 상류는 순서대로 부르고 단계 사이마다 검증 게이트만 붙입니다.

뒤쪽이 하류입니다. 로드맵에 남은 마일스톤을 하나씩 소진하는 구간입니다. 미완 마일스톤이 남아 있는 한 계속 돌아야 하고 몇 번 돌아야 끝나는지 미리 알 수가 없습니다. 실패하면 같은 자리를 다시 밟습니다. 반복 루프의 가치는 정확히 여기서만 나옵니다.

처음 설계가 틀렸던 것은 이 구분 없이 파이프라인 전체를 하나의 루프에 태우려 했기 때문입니다. 상류는 방향이 있고 끝이 정해진 흐름이고 하류는 조건이 만족될 때까지 도는 수렴 반복입니다. 제어 흐름이 아예 다른데 한 엔진에 밀어 넣으려 했으니 어느 쪽에도 맞지 않는 물건이 나왔습니다. 층을 가른 것이 이번 설계에서 가장 큰 결정입니다.

4. 직접 쓴 루프는 297줄입니다

하류에만 루프를 두고 그것을 직접 소유하기로 했습니다. 결과물은 파이썬 파일 하나, 297줄이고 외부 의존성이 없습니다. 남의 엔진을 어댑터로 개조하는 길과 우리 상태 파일을 직접 도는 최소 루프를 짜는 길 사이에서 뒤쪽을 골랐습니다.

규모로 보면 허무할 수도 있는데 이 크기가 나온 것은 우연이 아닙니다. 루프가 해야 할 일을 계속 덜어냈기 때문입니다.

불변식이 하나 있습니다. 한 회전은 마일스톤 하나만 처리한다. 회전 한 번은 파일을 읽고, 처리하고, 파일을 갱신하고 끝납니다. 메모리에 아무것도 들고 있지 않습니다. 그래서 회전이 끝난 뒤에 세션이 죽어도 잃는 것이 없습니다. 다음 회전이 파일을 다시 읽으면 그만입니다.

상태는 앱 저장소 안에 둡니다. 앱 폴더마다 .factory 디렉터리를 만들고 네 개를 넣습니다. 바뀌지 않는 메타, 마일스톤 목록과 통과 여부가 든 로드맵, 판정 기록을 쌓는 감사 로그, 지금 어느 단계인지 적은 상태 파일. 루프가 보는 것은 이것이 전부입니다.

처음 설계는 이것을 세션 폴더에 뒀는데 그것이 앞서 말한 세 번째 함정이었습니다. 저장소로 옮겨 커밋하니 어느 시점 로드맵이었는지가 이력에 남고, 다른 기계에서 받아도 이어 돌릴 수 있게 됐습니다.

5. 루프를 멈춘 것은 코드가 아니라 질문이었습니다

여기까지가 설계입니다. 실제로 돌려 보니 진짜 문제는 다른 데 있었습니다. 루프가 멈추는 이유가 코드가 아니었습니다. 에이전트가 물어보기 때문이었습니다.

마일스톤 하나를 끝내고 나면 다음 것을 할지, 이어서 진행할지, 이 정도면 될지를 묻고 사람을 부릅니다. 예의 바른 행동인데 자율 루프에서는 그것이 곧 정지입니다. 밤새 돌아야 할 것이 첫 마일스톤 앞에서 서 있게 됩니다.

그래서 스킬 문서에 다음 마일스톤을 할지 절대 묻지 말라고 못 박았습니다. 묻는 대신 다음 마일스톤을 달라는 명령을 호출하라고 적었습니다. 문장으로 부탁하지 않고 규칙으로 적은 이유는, 부탁은 매번 다르게 해석되지만 호출은 한 가지로만 실행되기 때문입니다. 판단할 자리를 아예 없앤 것입니다.

같은 처리를 코드에도 넣었습니다. 남은 마일스톤이 없으면 루프가 끝났다고 답하는데, 답만 주는 것이 아니라 다음에 무엇을 하라는 지시를 같이 실어 보냅니다. 종점이 아니라는 문장까지 출력에 박아 뒀습니다. 이것은 사람이 읽으라고 쓴 문장이 아니라 에이전트가 읽으라고 쓴 문장입니다. 판단할 여지를 주면 임의로 끝났다고 결론짓기 때문입니다.

통과 판정도 같은 원리로 막아 뒀습니다. 마일스톤을 통과시키려면 게이트 결과를 파일로 먼저 적어야 하고, 하나라도 비면 통과가 거부되며 종료 코드 5가 납니다. 검증자 칸에 본인이라고 적어도 거부됩니다. 이 부분은 앱 공장 9화에서 실제 통과를 막은 게이트로 자세히 다뤘습니다.

6. 44 대 8

이 루프가 실제로 얼마나 돌았는지 세어 봤습니다. 자율 파이프라인으로 만든 앱이 8개이고 로드맵에 등록된 마일스톤이 전부 52개입니다. 그중 44개가 통과 표시로 넘어가 있습니다. 회전 한 번이 마일스톤 하나이니 최소한 44번은 돌았다는 뜻입니다. 앱 8개 중 5개는 로드맵을 끝까지 소진했습니다.

남은 8개도 말씀드리는 것이 맞겠습니다. 그중 5개가 한 앱에 몰려 있는데, 맨 앞의 것이 출시 데이터로 다음 투자를 결정하는 마일스톤입니다. 데이터가 쌓여야 판정이 되니 루프가 못 도는 것이 아니라 기다리는 것입니다. 나머지 두 앱에도 각각 1개와 2개가 남아 있습니다. 전부 소진이 아니라 44 대 8이라고 말하는 것이 정확합니다.

7. 엔진을 고르기 전에 확인할 세 가지

제가 겪은 것을 세 줄로 줄이면 이렇습니다. 반복 엔진을 가져다 쓰기 전에 이것만 확인하면 됩니다.

  1. 그 엔진이 내 상태 파일을 그대로 읽는가. 파일 이름과 스키마가 고정돼 있지 않은가.
  2. 통과 판정 기준을 내 것으로 갈아 끼울 수 있는가.
  3. 그 엔진이 끝날 때 내 상태를 지우지 않는가.

셋 중 하나라도 아니오라면 그것은 범용 엔진이 아니라 남의 작업 절차입니다.

8. 정리

순서 이야기를 하나 덧붙이겠습니다. 처음 계획은 상류 스킬을 먼저 세 개 만들고 마지막에 엔진을 배선하는 것이었습니다. 그렇게 갔으면 얹히지 않는다는 것을 맨 끝에서 발견했을 것이고 재작업이 컸을 것입니다. 다시 짤 때는 순서를 뒤집어 가장 불확실한 것을 첫 번째에 놓고 작은 실험으로 되는지부터 확인했습니다.

범용 엔진처럼 보이던 것이 실은 하나의 작업 절차였고, 제가 버린 것은 그 위에 얹겠다는 설계였습니다. 상류와 하류를 가르고 하류만 297줄로 직접 짰습니다. 상태를 파일에만 두니 재개가 공짜가 됐고, 묻지 못하게 막으니 루프가 멈추지 않았습니다.

이제 루프가 돕니다. 그런데 이 루프가 만든 것을 바깥으로 내보내는 순간은 되돌릴 수가 없습니다. 다음 글에서는 사람이 지키는 유일한 게이트를 이야기하겠습니다.


코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다