Episode 01 - Spec & Planning
이 글에서 다루는 것
이런 경험, 한 번쯤 있으실 거예요. "결제 페이지 만들어줘" 한 줄을 던졌더니 에이전트가 묻지도 않고 코드를 좌르륵 뽑아냅니다.
그런데 돌려보면 누락된 기능이 발견되고, 결제가 엉성하게 된다거나, 예상하지 않게 동작하는 경우죠. 사실 중간에 물은만도 한데 어떤 Agent는 상황에 따라서 스스로 맥락을 파악하고 임의 결정사항을 근거로 기능을 쭉 뽑아냅니다. 아마 완성 기준역시 본인의 기준에 따라서 완성할것이고요.
또한 Agents는 개발을 시작하면 괜히 멈추지도 않습니다. 임무완수 & 고객만족을 최우선으로 생각하는것 같군요.
요즘은 그래도 "이건 어떻게 할까요?" 하고 되묻는 경우가 있지만 언제 나에게 질문할지는 사실 모릅니다. 어쩔땐 그냥 눈치껏 알아서 해주길 바라기도 하죠.
아무튼 모호한 요구사항에도 불구하고 가장 그럴듯해 보이는 쪽으로 빈칸을 알아서 채워서 그냥 진행해 버립니다. 문제는 그 "그럴듯한"이 사실 학습 데이터의 평균이라는 거예요. 여러분들의 프로젝트에 최선인 상황이 아니고요. 그래서 애매하게 요구사항을 비워두면, 그 자리는 평균적이며 무난한 결과로 채워집니다.
사실 이부분이 AI 개발에서 큰 실패로 이어지는 경우가 있죠. 이건 엉뚱한 결과지 사실 버그도 아니에요. 그래서 수백 줄을 다 만든 다음에야 접근이 틀렸다는 걸 알게 되기도 하죠. 토큰만 낭비한 셈이군요.
다행히 막는 방법은 심플할 수 있어요. 결과를 하나하나 고치느라 매달리는 대신, 시작하기 전에 결정을 미리 정해두는 거예요.
손볼 지점을 출력이 아니라 입력으로 옮기는 거죠.
핵심 메시지
일반적으로 코드를 시작하기 전에 정해둘 결정은 크게 네 가지로 분류가 됩니다.
-
무엇을 만들지 (요구사항)
-
어떻게 갈지 (접근)
-
얼마 단위로 자를지 (작업)
-
그걸 어떻게 전달할지 (예시)
오늘 볼 패턴들은 전부, 이 네 자리를 평균으로 채워지기 전에 내 결정으로 먼저 채워두는 도구예요.
코드보다 먼저 끝낼 결정 네 가지
1. Requirement Grilling - 꼼꼼한 스펙작성
대부분은 결과가 맘에 들지 않거나 어긋나면 프롬프트를 더 꼼꼼하게 다듬으려 합니다.
"이렇게 말고 저렇게, 이 경우엔 이렇게…"
그런데 어긋난 결과의 대부분은 출력이 아니라 입력 문제인 경우가 많아요.
애초에 무엇을 만들지가 안 정해진 채로 시작했기 때문이죠. 우리는 이걸 명세 부족(underspecification)이라고 해요.
왜 에이전트는 멋대로 가정하지 ?
LLM은 애매한 요청에도 쉽게 멈추지 않죠. 가장 그럴듯한 쪽으로 누락된 요구사항 부분을 마음대로 채우고 진행합니다. 그런데 그 "가장 그럴듯한"은 모델의 학습 데이터의 평균이지 우리 프로젝트에 최적화된 것은 아니죠. "결제 페이지"의 평균은 카드 결제 + 정상 흐름(해피 패스)이거든요. 정작 필요한 계좌이체·부분 환불·멱등 처리(같은 요청이 두 번 와도 한 번만 처리)는 평균에 속한 데이터도 아니고 누락될 확률이 있죠.
정리하면, LLM은 애매한 요청에도 멈추지 않고 그저 가장 그럴듯한 평균 쪽으로 빈칸을 채우고 진행합니다. 예를들어서 중복 요청에 대한 처리를 하는 부분등의 평균가 거리가 있는 예외케이스에 대한 처리는 그냥 빼먹는 경우가 많습니다.
이 문제를 해결하는것은 단순히 설명을 막연히 길게 한다고 해결되지 않을수 있죠. 진짜 위험한 건 빠뜨린 줄도 모르는 경우죠.
설명하지 말고, 질문하게 시키기
Requirement Grilling패턴은 접근방식을 반대로 합니다. 내가 설명하는 대신 에이전트가 나를 인터뷰하게 하는 거죠.
"결제 기능을 만들 거야. 코드는 아직 쓰지 마.
시작 전에 결정해야 할 걸 하나씩 물어봐 줘 — 각 질문엔 네 추천도 같이.
중요한 것부터, 빠진 게 없을 때까지."
작업 전에 분석한 것도 별로 없고 특별한 요구을 준것도 아닙니다. 그저 이 기능에 대한 꼼꼼한 질문만을 요청했죠.
그러면 에이전트가 질문을 하나씩 던지기 시작해요 — 답하면 그걸 보고 다음 걸 묻고, 빠진 게 없을 때까지 캐물으면서요. 한 세션에 보통 16~50개쯤 나오는데, 각 질문에 추천 답도 같이 줘서 대부분은 "응" 하고 넘어가면 돼요. 그런데 그중엔 우리가 물어봐 주기 전엔 떠올리지도 못한 것들이 꽤 있죠.
1. 결제 수단 — 카드만? 계좌이체·간편결제도?
2. 같은 요청이 두 번 오면? (멱등성 키로 막을까)
3. PG사 webhook이 중복·늦게 도착하면?
4. 부분 환불 허용? 0원·음수 금액은 어떻게 막나?
5. 검증은 프론트/백 어디서? (프론트만이면 그냥 넘어갈 수 있음)
"PG사 webhook이 중복·늦게? 그러네, 저것도 고려했어야 했네."
그래서 Grilling은 내가 아는 걸 정리하는 도구가 아니라, 모르는 줄도 몰랐던 걸 드러내는 패턴인 셈이죠.
Requirement Grilling 흐름 — 모호한 요청에서 명세까지
답이 곧 명세
질문에 하나씩 답하다 보면, 신기하게도 머릿속에만 흐릿하게 떠다니던 결정들이 전부 글로 정리되는 셈이에요. 그리고 이 Q&A 기록을 spec.md 같은 파일에 적어두면 거기서 끝이 아니죠. 이후 세션들이 그 파일을 읽고 같은 전제 위에서 일하게 되거든요. 한 번 힘들게 끄집어낸 결정을 계속 재사용하는 거예요.
그리고 이미 명세가 뚜렷하다면 이 방법이 매우 유용하지는 않을겁니다. 그럼에도 놓칠 수 있는 부분을 발견하고 더 꼼꼼한 대응을 할 수 있기 때문에 전 어느상황에서도 유용하다고 생각합니다.
또한 질문 수십 개를 다 답하다보며 지칠 수 있는데요. 그렇다면 "중요한 것 10개만 묻고, 나머지는 적당한 기본값으로 진행하되 뭘 가정했는지 표시해줘" 라고 할 수도 있을겁니다. 물론 답변 결과를 보면서 우리가 스스로 판단해야겠죠.
2. Plan 먼저
코드 리뷰는 보통 코드가 다 나온 뒤에 diff를 펼쳐 놓고 하잖아요. 사람끼리 일할 땐 그게 맞죠. 그런데 에이전트랑 일할 땐, 그게 너무 늦은 리뷰예요.
첫 추측이 곧 구현
에이전트는 세션마다 콜드스타트(매번 빈 상태로 시작)예요. 우리 코드베이스를 아무것도 모른 채 시작하는 거죠. 그 상태로 바로 손을 대면, 처음 떠오른 그럴듯한 방향(네, 여기서도 그 '평균'이죠)으로 일단 들어가서 코드를 쭉쭉 쌓아 올립니다. 문제는 그 속도예요. 5분이면 200줄이 뚝딱 생기고, 그제서야 보고 "어, 접근 자체가 틀렸네" 하게 되거든요. 이미 한참 늦은 거죠.
원인은 사실 단순해요 — 단계가 안 나뉜 거죠. 탐색과 구현이 한 덩어리로 섞이면, 첫 추측이 그대로 구현이 되어버립니다.
그래서 작업의 순서를 또렷하게 끊어보는 겁니다.
리뷰를 먼저 하기
완성된 코드를 검증하고 리뷰하는것은 비싼 비용인것이고, 앞단에서 미리 검토하는것은 상대적으로 저렴한 비용이라고 할 수 있죠. 그러니 앞단에서의 사전검토가 중요하죠.
그래서 작업을 탐색 → 계획 → 구현 → 커밋(Explore-Plan-Code-Commit)으로 나눠보는 것입니다. 여기서 핵심은 대충 시켜놓고 바로 코드부터 짜지 않는 것이죠.
시작은 탐색인데요. 이는 말그대로 Agent가 충분한 맥락을 판단하도록 하는것이죠. 본격적인 작업을 하기 전에 충분한 맥락을 정리하고 이를 통해서 환경을 이해시키는것이 먼저입니다. 우리의 실제 일도 비슷하죠. 배경상황을 공유해야 공감대가 생기고 이 작업의 이유를 아는것과 비슷합니다.
계획은 보통 plan mode 를 활용해서 개발전에 어떻게 할지를 설계하는 단계죠. 상황마다 어울리는 최적의 상황을 계획해야할텐데요. 앞서 나온 Requirement Grilling 패턴이 좋은 실제 방법일겁니다.
그리고 계획이 충분한지 가늠하는 좋은 기준이 하나 있어요. "이 변경을 한 문장으로 설명 못 하면, 계획이 덜 된 것." 한 문장으로 못 줄인다는 건, 본인도 접근이 아직 흐릿하거나 범위가 안 좁혀졌다는 신호거든요.
계획 모드 잠금 → 사람 승인 → 구현 모드
습관 말고 도구로 강제하기
"계획부터 보자"는 습관은… 솔직히 잘 안 지켜지죠. 급하면 "에이, 이번만" 하고 건너뛰게 되니까요. 그래서 아예 도구로 강제해버립니다
대부분 Agent에 있는 기능으로 Plan-Mode Gate예요. 편집을 막은 읽기 전용으로 탐색·계획만 하게 두고, 사람이 "ok" 하기 전엔 쓰기가 잠겨 있죠. 이 프로세스를 습관적으로 강제화 하는것을 전 추천합니다. 쏟아지는 코드리뷰를 하기 귀찮고 부담스럽다면 이 단계를 꼭 거쳐서 하는것이 나을겁니다.
흐름은 이렇습니다..
계획 모드에서 에이전트가 접근을 글로 쭉 내놓으면, 사람이 읽어보고 "X 말고 Y로 가자" -> ok -> 그제서야 구현 모드로 쓰기가 열립니다.
주의
- 뻔한 한 줄 수정에까지 계획 게이트를 걸면 순수 오버헤드예요. 위험·되돌리기 어려움에 맞춰서 세기를 조절합니다.
- 단, 되돌릴 수 없는 작업(배포·DB DROP·실제 과금)만큼은 예외 없이 항상 수동 승인으로 둡니다. 여기선 그 오버헤드가 그만한 값을 하죠.
3. Vertical Slice - 의미있는 단위로 개발
요구사항과 계획도 어느정도 정했다면 이제 Agent에게 한 번에 얼마큼 어떤 작업을 요청할까요?
컨텍스트는 생각보다 좁음
에이전트의 작업 공간(컨텍스트, 한 번에 들고 보는 정보의 양)은 생각보다 좁아요. 큰 작업을 통째로 던져버리면 봐야 할 게 너무 많아져서 중요한 걸 놓치죠. (컨텍스트 엔지니어링)
어디까지 했는지도 잃어버리고, 결과물이 너무 많아서 사람이 검토하기도 어렵고요 — 그러다 검토 안 된 코드가 그대로 머지되기도 해요.
그래서 큰 작업은 잘라야 하는데, 문제는 어떻게 자르느냐겠죠.
가로로 자르면 혼자 못 돈다
보통은 층으로 나누잖아요 — 프론트엔드 / 백엔드 / DB. 익숙하니까요. 그런데 이렇게 자른 가로 조각은 혼자 동작하질 못해요. 백엔드만 만들어선 보여줄 화면이 없고, 합치기 전엔 맞는지 틀린지도 모르죠. 조각이 끝나도 그것만으로는 동작하지 않거든요. 인간계(?)에서는 전문화된 업무로 나눠지다보니 수평적으로 나눠진(fe,be따로) 작업으로 많이들하죠.
(개인적으로 조직적인 이유로 AI개발시대에 이렇게 직무가 나눠진 것은 이해는 가지만, 효율적인 방법은 아니라고 생각이 됩니다)
그래서 대신 세로(기능) 로 자르는거죠 — 결제 폼 -> 요청 처리 -> 결과 화면처럼, 한 기능이 세 층을 쭉 다 거치게요(Vertical Slice).
한 조각만으로 처음부터 끝까지(end-to-end) 동작하는 단위인 거죠.
가로 층 분할 vs 세로 기능 분할
세로 조각은 그 자체로 동작한다
세로로 자르면 두 가지 장점이 생깁니다.
첫째, 조각 하나가 끝날 때마다 바로 동작하는 결과가 나와요. 진짜 결제 한 건이 동작하는 데모 같은 거죠. 얼마나 됐는지가 눈에 딱 보이고요.
둘째, 이게 에이전트한텐 특히 중요한데 — 동작하는 조각은 통과/실패 신호를 주거든요. 에이전트가 스스로 "이 조각 맞게 됐나?"를 확인하고 다음으로 넘어갈 수 있어요. 가로 조각엔 그 신호가 없죠. (이렇게 스스로 확인할 신호가 있어야 에이전트가 사람 도움 없이도 다음으로 나아가요 — 뒤 편들에서 계속 나올 얘기예요.)
덤으로, 합칠 때 터지는 문제도 일찍 드러나요. "프론트랑 백엔드가 안 맞네" 같은 게 마지막에 몰려서가 아니라 첫 조각에서 바로 드러나니까, 싸게 잡히는 거죠.
너무 잘게 자르지는 않기
- 반대로 너무 잘게 쪼개면 조각 사이를 맞추고 오가는 비용이 이득보다 커집니다.
- 강하게 얽혀 떼어낼 수 없는 변경은 깔끔하게 안 잘려요 — 억지로 자르지 않습니다.
4. Reference-Pattern
이 패턴은 지침을 주는 단계에서의 프롬프트 엔지니어링과 관련이 있습니다.
평균을 부르는 "깔끔하게 만들어줘~"
"깔끔하게 만들어줘", "안전하게 짜줘." 이런 형용사는 애매하잖아요. 그리고 애매한 요구사항은 앞서 말했던 그 평균값으로 채워져요.
"깔끔한 코드"의 평균은 인터넷 어딘가의 일반적인 코드지, 내 코드베이스의 컨벤션은 아니죠.
그래서 흔히들 형용사를 더 정확하게 고르려고 해요 — "모던하게", "프로덕션급으로" 하는 식으로요. 근데 그건 헛수고예요. 더 정확한 말을 찾을 게 아니라, 형용사 자체를 버려야 하거든요.
가리키면 컨벤션이 통째로 따라온다
Reference-Pattern은 그 추상적인 형용사를, 가리킬 수 있는 것으로 바꿔주는 패턴이에요.
"이 버튼을 Button.tsx의 PrimaryButton처럼 만들어줘."
이렇게 한 번 가리키면 컨벤션·구조·스타일·에러 처리·이름 짓기가 통째로 따라와요.
말로 다 적으면 한 문단이 될 걸, "이거처럼" 한마디가 다 해주는 거죠. 더 중요한 건, 말로는 정리하기도 어려운 규칙까지 같이 온다는 거예요 — 들여쓰기, import 순서, 에러 처리 방식 같은 거요. 형용사로는 절대 전달이 안 되는 것들이죠.
형용사 vs 예시 가리키기
함정은 있습니다.
대신 분명한 함정이 하나 있어요. 에이전트는 "이거처럼"을 판단 없이 그대로 따라요. 그래서 나쁜 예를 가리키면, 나쁜 패턴도 그대로 복제돼버리죠. (Agent는 너무 말을 잘 들어서 그것도 참 문제죠 ^^) 레거시 파일을 가리키면 레거시가 마구 생기는 거예요. 그러니 가리키기 전에, 그 대상이 정말로 좋은 예가 맞는지 한 번 봐주셔야 해요. 가리키는 대상의 품질이 곧 결과의 품질이거든요.
가리킬 게 없으면
가리킬 만한 좋은 예가 코드베이스에 아직 없다면, 같은 원리의 다른 방법을 쓰면 돼요 — 입출력 쌍을 보여주는 Few-shot, 시그니처·테스트를 먼저 써서 따라야 할 약속으로 삼기, 역할을 주는 Role/Persona 같은 거요. 전부 추상적인 형용사를 구체적으로 짚을 것으로 바꾼다는, 결국 같은 얘기예요.
함께 보면 좋은 패턴
Spec & Planning 관점에서 네 가지를 알아봤는데요.
그외에도 비슷하면서 다양한 방식들이 많습니다. 자세하게는 못다르지만 각각 뭘 하는지, 위 네가지 대표패턴 중 무엇과 짝인지만 짚고 갈게요.
Spec-Driven Development : spec을 기준 문서로 두고, 코드는 거기에 맞추기
Grilling으로 좋은 spec을 한 번 만들었다 쳐요. 그런데 작업하다 보면 코드만 슬금슬금 바뀌고 spec은 금세 옛날 얘기가 되죠. Spec-Driven은 순서를 뒤집어요. spec.md를 계속 기준 문서(= 진짜 원본)로 두고, 바꿀 게 생기면 코드부터 손대는 게 아니라 spec을 고친 다음, 에이전트한테 코드를 거기에 맞추게 합니다. 그러면 "지금 기준이 뭐냐"가 늘 한 군데에 있고, spec과 코드가 따로 놀지 않죠.
1번(Requirement Grilling)과 이어지는 짝. Grilling이 좋은 spec을 뽑아내는 한 번의 작업이라면, Spec-Driven은 그 spec을 계속 원본으로 굴리는 방식이에요. 만드는 법 vs 쓰는 법이라 안 겹치죠 — 보통 Grilling으로 만들고 → Spec-Driven으로 운영합니다.
Discrete-Prompt : 한 프롬프트에 한 단계씩만 시키기
2번에서 단계를 끊었다면(탐색 → 계획 → 구현 → 커밋), Discrete-Prompt는 그 끊은 단계를 한 번에 하나씩만 프롬프트로 주는 거예요. "탐색해서 요약만 해줘"로 한 번 끊고, 결과를 본 다음에야 "그럼 이 계획으로 구현해줘"로 다음을 줍니다. 한 메시지에 여러 단계를 욱여넣지 않으니, 매 단계마다 사람이 끼어들 자리가 생기죠.
2번(Plan 먼저)의 더 잘게 쪼갠 버전. 2번이 단계를 나누는 큰 틀이라면, Discrete-Prompt는 그 단계를 실제로 전달하는 단위예요 — 한 메시지 = 한 단계.
Confidence-Gated : 확신에 따라 자동 통과와 사람 승인을 가르기
모든 단계에 똑같이 사람 승인을 걸면 금세 지치죠. Confidence-Gated는 단계마다 *확신도(confidence)*로 통과방식을 조금 다르게 하는것인데요.
확신이 높고 되돌리기 쉬운 일은 자동으로 통과시키고, 확신이 낮거나 되돌리기 어려운 일(배포,삭제,과금등)은 사람 승인으로 막습니다.
같은 게이트라도 어디에 얼마나 엄격하게 할지를 위험에 맞춰 조절하는 것이죠.
2번(Plan-Mode Gate)의 세기 조절판. 2번 게이트가 "막느냐 여느냐"의 on/off라면, Confidence-Gated는 어디엔 빡빡하게, 어디엔 느슨하게를 정합니다. 덕분에 안전한 일까지 일일이 승인하느라 지치는 걸 줄이죠.
이런 판단을 결정하는 것도 Agent와 상의할지 있으나 어쨋든 개발자가 Domain에 지식과 여러가지 상황을 잘 파악해야 가능할겁니다. 이런점이 개발자, 엔지니어의 역할로 점점 중요해진 이유기도 하죠.
Acceptance-First — 완료 기준을 먼저 적고 시작하기
3번에서 작업을 세로 조각으로 잘랐다면, Acceptance-First는 그 조각마다 *"이게 되면 끝"*을 먼저 적어두는 거예요 — "카드번호가 틀리면 인라인 에러가 뜬다" 같은 통과/실패 기준을요. 그러면 에이전트가 다 만든 뒤 그 기준으로 스스로 맞는지 확인하고 넘어갑니다. (혹시 TDD나 BDD 가 떠오르시나요?)
3번(Vertical Slice)을 완성하는 짝. 3번이 조각을 어떻게 자르냐라면, Acceptance-First는 자른 조각에 합격선을 박는 일이에요. 조각에 기준이 있어야 3번에서 말한 "통과/실패 신호"가 비로소 생기거든요.
이 편의 나머지 패턴
이외에도 Spec & Planning 에피소드로 묶이는 패턴은 많은 것들이 있더군요.
이름과 한 줄 설명으로 훑고 갈게요
| 패턴 | 한 줄 |
|---|---|
| Idea-Honing | 막연한 아이디어를 한 번에 한 질문씩 인터뷰해 spec으로 키우기 |
| Voice Memo → PRD | 말로 요구사항을 쭉 늘어놓으면 정리된 요구 문서로 받아 적기 |
| Delta-Spec | 매번 전체가 아니라, 바뀐 부분만 명세로 주기 |
| Dependency-Ordered | 의존성 순서대로 작업을 나눠, 앞 결과 위에 다음을 쌓기 |
| Phased Gate | 큰 작업을 단계로 나눠, 단계마다 사람이 통과를 승인 |
| Timeout-Auto-Approve | 일정 시간 응답이 없으면 안전한 작업은 자동으로 진행 |
| Few-shot | 입력 → 출력 예시 몇 개로 원하는 형태를 보여주기 |
| Documentation-Driven | 문서(스펙·가이드)를 지시문 삼아 거기서 구현을 끌어내기 |
| Inline-Comment | 코드 안 주석으로 "여기서 이렇게" 그 자리에 지시하기 |
| Role/Persona | 역할을 지정해(예: 보안 검토자) 관점을 고정하기 |
| Prompt Anti-Patterns | 흔한 프롬프트 실수 모음 — 하라가 아니라 피하라 쪽 |
마무리 - 이것 만큼은 기억하기
여러분들도 좀 느꼈나요? 핵심은 시키기 전에, 결정을 먼저 끝내기! 라는 점으로 볼 수 있어요.
이번 편에서 핵심으로 나온 4가지는, 사실 따로가 아니에요. 결국 다 같은 이야기거든요
| 결정 | 패턴 | 한 줄 |
|---|---|---|
| 무엇을 | Requirement Grilling | 짓기 전에 다 물어보게 시키기 |
| 어떻게 | Plan 먼저 · 사람 검토 | 틀린 접근을 0줄에서 잡기 |
| 얼마 단위로 | Vertical Slice | 혼자 동작하는 세로 조각으로 자르기 |
| 어떻게 전달 | Reference-Pattern | 형용사 말고 "이 파일처럼" 가리키기 |
다음 편 — 컨텍스트 엔지니어링. 오늘 어렵게 끄집어낸 결정도, 에이전트가 잊거나 헷갈리면 소용없어요.
한 번 내린 결정을 에이전트가 계속 기억하게 만드는 법을 다룹니다.
참고 영상
