애자일 제대로 알기 6편(실전): 유저스토리에서 릴리스까지 — 딜리버리 플로우 한 바퀴

애자일 제대로 알기 6편(실전): 유저스토리에서 릴리스까지 — 딜리버리 플로우 한 바퀴


서론

이전 글까지 다섯 편으로 “애자일이 무엇이고, 왜 그렇게 하며, 왜 망가지는가”를 한 바퀴 돌았다. 전부 개념이었다.

그런데 막상 월요일 아침 팀에 앉으면 질문은 더 구체적이다. “스토리는 어떻게 적지?”, “인수조건이 QA로 어떻게 넘어가지?”, “MVP는 어디까지지?”, “이벤트 스토밍은 해야 하나?” 개념을 알아도, 그게 유저스토리에서 릴리스까지 어떤 순서로 굴러가는지는 따로 그려 봐야 잡힌다.

6편은 그 실전 흐름을 한 바퀴 걸어 본다. 특정 비즈니스가 아니라 어느 팀에나 적용되는 일반 딜리버리 플로우다. 그리고 각 단계에서 1–5편의 개념이 어디서 작동하는지 매핑한다. 시리즈가 아직 정면으로 안 다룬 유저스토리·인수조건(Given/When/Then)·MoSCoW·MVP·이벤트 스토밍도 이 흐름 안에서 자연스럽게 소화한다.

대상 독자는 개념은 봤는데 “그래서 우리 팀은 내일 뭘 먼저 하지”가 막막한 사람이다.


TL;DR

  • 모든 흐름의 출발점은 유저스토리다 — “~로서, ~하고 싶다, ~하기 위해” 한 줄. 명세가 아니라 대화의 약속이며, 좋은 유저스토리가 갖춰야 할 조건과 인수조건이 따라붙는다.
  • 인수조건은 Given/When/Then으로 적고, 그대로 QA로 넘어간다 — 인수조건이 곧 완료 정의이자 인수 테스트의 씨앗이다(인수 테스트 주도 개발/행위 주도 개발). “QA에서 유저스토리로 검증”이 이 뜻이다.
  • 우선순위는 필수·권장·선택·제외로 나누는 분류법, 크기는 스토리 포인트, 범위는 최소 기능 제품으로 자른다 — 다 만들고 한 번에 내는 게 아니라, 가장 가치 있는 최소한을 먼저 릴리스한다.
  • 스펙(화면·데이터·API)을 먼저 확정해 병렬로 짓되, 병렬은 단절이 아니다 — API 계약·인수조건·리빙 정책 문서가 백엔드·프론트를 묶는다. ‘스펙 던지고 각자 잠수’는 폭포수다.
  • 정책·데이터 모델은 한 번에 못 박지 않는다 — 스토리·인수조건 단계에서 씨앗을 심고 개발·QA 내내 리빙 문서로 갱신한다. 확정하되 동결하지 않는다.
  • 이 전체 플로우는 1–5편의 실전 합본이다 — 유저스토리→인수조건→우선순위→스펙→개발→QA→릴리스가 결국 5편의 검사-적응 학습 루프를 한 바퀴 도는 일이다.

1. 유저스토리 — 모든 것의 출발점

1.1 As a / I want / so that

유저스토리(user story)는 기능을 사용자 관점에서 한 줄로 적은 것이다. 정해진 틀이 있다.

~로서(As a <역할>),
~하고 싶다(I want <목표>),
~하기 위해(so that <이유/가치>).

예를 들면 이렇다.

사용자로서, 비밀번호를 재설정하고 싶다, 계정에 다시 접근하기 위해.

핵심은 유저스토리가 명세서가 아니라는 점이다. 스토리는 “이 대화를 나중에 꼭 하자”는 약속(placeholder)에 가깝다. 그래서 짧게 적고, 상세는 인수조건과 대화로 채운다. 1편 1가치(개인과 상호작용 > 공정과 도구)가 여기서 작동한다 — 두꺼운 명세 대신 대화를 남긴다.

1.2 INVEST — 좋은 스토리의 조건

좋은 스토리인지 점검하는 체크리스트가 INVEST다.

글자한 줄
Independent독립적다른 스토리와 순서·의존 없이 다룰 수 있다
Negotiable협상 가능고정 명세가 아니라 대화로 조정 가능하다
Valuable가치 있는사용자·비즈니스에 가치를 준다
Estimable추정 가능크기를 가늠할 수 있다(4편 스토리 포인트)
Small작은한 스프린트(또는 더 짧게) 안에 끝낼 수 있다
Testable테스트 가능완료를 객관적으로 검증할 수 있다(→ 인수조건)

특히 두 글자가 곧장 다음 이야기로 이어진다. Independent·Small이 깨지면 스토리를 쪼개야 한다는 신호이고(1.3절), Testable은 “끝났다”를 판별할 인수조건으로 이어진다(2절).

1.3 스토리 쪼개기 — 복합 스토리 나누기

INVEST의 I(독립)·S(작게)가 깨진 스토리, 즉 한 스프린트에 안 들어오거나 두 가지 가치가 섞인 스토리는 쪼갠다(story splitting). “이 유저스토리에 사실 두 개가 섞여 있는데?” 싶은 순간이 바로 그 신호다.

먼저 큰 스토리를 두 종류로 구분한다(Mike Cohn).

구분정의쪼개는 법
복합 스토리(compound story)사실은 여러 작은 스토리가 하나로 뭉쳐 있는 스토리안에 든 스토리들로 분해
복잡한 스토리(complex story)쪼갤 하위 스토리가 없고 그 자체로 본질적으로 큰(불확실한) 스토리스파이크로 불확실성부터 제거한 뒤 분할

질문에서 흔히 나오는 “한 스토리에 두 스토리가 섞여 있다”가 바로 복합 스토리다(둘 다 충분히 크면 통칭 에픽(epic)이라 부른다).

실무 신호는 단순하다. 스토리 문장이나 인수조건에 “그리고(and)“가 들어가면 십중팔구 복합 스토리다. “사용자는 검색을 하고 그리고 결과를 즐겨찾기에 저장할 수 있다”는 검색 / 즐겨찾기 두 스토리다.

쪼갤 때 막막하면 SPIDR 패턴을 쓴다(Richard Lawrence). 다섯 가지 축 중 맞는 걸 고른다.

기준
Spike불확실하면 조사부터”결제사 연동 가능 여부 조사”
Path워크플로 경로별해피패스 먼저, 예외 흐름은 나중
InterfaceUI·입력 방식별웹 먼저, 모바일·API는 나중
Data데이터 종류·범위별국내 카드 먼저, 해외 카드 나중
Rules비즈니스 규칙별기본 할인 먼저, 쿠폰 중복 규칙 나중

한 가지 함정이 있다. 쪼갠 조각도 각각 INVEST를 만족해야 한다 — 특히 Valuable. 그래서 “프론트엔드 / 백엔드 / DB”처럼 기술 레이어로 자르면 안 된다. 그렇게 자른 조각은 혼자서는 사용자에게 아무 가치를 못 주기 때문이다. 화면부터 데이터까지 관통하는 가치 단위(수직 슬라이스)로 잘라, 조각 하나만 릴리스해도 사용자가 뭔가를 할 수 있게 한다.

참고: 쪼개기는 우선순위(3절)와 짝을 이룬다. 복합 스토리를 조각내야 비로소 “이 조각은 Must, 저 조각은 Won’t”처럼 MoSCoW로 가를 수 있다. 큰 덩어리째로는 우선순위를 매길 수 없다.


2. 스토리에서 인수조건으로 — Given/When/Then

스토리 한 줄로는 “끝났다”를 판단할 수 없다. 그래서 인수조건(acceptance criteria)을 붙인다. 인수조건은 “이걸 다 만족하면 이 스토리는 받아들여진다”는 조건 목록이다.

인수조건은 흔히 Given/When/Then 형식으로 적는다. BDD(Behavior-Driven Development, 행위 주도 개발)에서 온 형식으로, 상황·행위·결과를 분리한다.

Given 가입된 이메일이 있고,
When 비밀번호 재설정을 요청하면,
Then 재설정 링크가 담긴 메일이 발송된다.

Given 만료된 재설정 링크로,
When 접속하면,
Then 만료 안내와 재요청 버튼을 보여준다.

이 형식이 강력한 이유는 그대로 테스트가 되기 때문이다. Given/When/Then 한 줄 한 줄이 인수 테스트의 단계로 옮겨진다.

flowchart LR
    S["유저스토리<br/>As a / I want / so that"] --> A["인수조건<br/>Given / When / Then"]
    A --> T["인수 테스트<br/>(ATDD/BDD 자동화)"]
    T -->|"통과해야 Done"| D["완료(DoD)"]

여기서 두 가지가 시리즈와 맞물린다. 첫째, 인수조건은 2편의 완료 기준(DoD)의 구체적 형태다 — “이 조건을 통과해야 증분으로 인정”이다. 둘째, 인수조건을 먼저 쓰고 그걸 통과시키는 개발 방식이 ATDD(Acceptance Test-Driven Development, 인수 테스트 주도 개발)로, 4편 TDD의 인수 레벨 버전이다. “QA에서 유저스토리로 검증한다”는 말은 결국 인수조건을 테스트로 돌려 스토리 단위로 확인한다는 뜻이다.

참고: 미결 정책이 인수조건을 막을 때가 있다(“만료 시간은 몇 분?”). 이때 정책을 결정해 리빙 정책 문서에 적고, 그 결정을 인수조건에 반영한다. 정책 문서는 한 번 쓰고 끝나는 게 아니라 진행 중 계속 갱신된다(5절).


3. 우선순위와 릴리스 순서 — MoSCoW · 스토리 포인트 · MVP

스토리가 쌓이면 “무엇을 먼저, 무엇을 이번엔 안 함”을 정해야 한다. 가장 흔한 도구가 MoSCoW다.

등급의미
Must반드시없으면 릴리스가 무의미한 핵심
Should하면 좋음중요하지만 빠져도 릴리스는 가능
Could가능하면여유 있으면 포함, 가장 먼저 버려짐
Won’t이번엔 안 함의도적으로 범위에서 제외(다음으로)

핵심은 마지막 Won’t이다. “이번엔 안 한다”를 명시적으로 적는 게 MoSCoW의 진짜 가치다 — 1편 10원칙(단순성, 해야 할 일을 최소화)을 우선순위 차원에서 실행하는 것이다.

크기는 4편에서 본 스토리 포인트로 매기고(상대 추정·플래닝 포커), 이 둘을 합쳐 MVP(Minimum Viable Product, 최소 기능 제품)의 범위를 자른다. MVP는 “가장 작지만, 사용자에게 가치를 주고 배움을 얻을 수 있는 최소한”이다. 다 만들고 한 번에 내는 게 아니라(그건 폭포수다, 1편), Must부터 묶어 먼저 릴리스하고 나머지를 이어 낸다.

핵심: MVP는 “대충 만든 반쪽”이 아니다. 범위는 좁아도 그 범위 안에서는 인수조건을 만족하는 쓸 수 있는 증분(2편)이어야 한다. 좁게, 그러나 제대로.


4. 화면·데이터·API로 구체화

스토리와 인수조건이 정해지면 만들 수 있게 구체화한다. 순서가 중요하다.

4.1 로우파이 와이어프레임

먼저 로우파이(low-fidelity) 와이어프레임으로 핵심 화면을 거칠게 그린다. 색·폰트가 아니라 “어떤 화면에 무엇이 있고 어디로 가는가”만 본다. 거칠게 그리는 이유는 1편 가치 그대로다 — 정교한 시안에 시간을 쏟기 전에 빨리 보여 주고 피드백을 받는 게 싸다(변경 비용 곡선).

4.2 스펙 우선 — 데이터·API 명세

화면이 잡히면 엔티티·필드를 확정하고 API 명세를 먼저 합의한다. 데이터 모델은 이 단계에서 갑자기 튀어나오는 게 아니다 — 유저스토리와 도메인 개념이 “어떤 엔티티가 있나”를 이미 암시하고, 도메인이 복잡하면 4.3절의 이벤트 스토밍이 그 모델의 입력이 된다. 이 단계는 그렇게 모인 이해를 시작할 만큼 확정하는 지점이다.

API 계약을 먼저 못 박으면 웹·앱·서버가 병렬로 작업할 수 있다 — 3편 흐름(flow) 관점에서 의존성을 줄여 병목을 없앤다. 단 여기서 ‘병렬’을 ‘각자 잠수’로 오해하면 안 된다. 그 구분은 5절에서 따로 다룬다.

데이터는 처음부터 마이그레이션이 쉬운 형태로 적재한다. 스키마가 바뀔 걸 전제로(불확실성 인정, 1편) 되돌리기 쉬운 구조를 택한다 — 데이터 모델은 ‘확정’하되 ‘동결’하지 않는다.

4.3 이벤트 스토밍 — 쓸지 말지 판단

이벤트 스토밍(event storming)은 도메인에서 일어나는 “사건(event)“을 포스트잇으로 쭉 펼쳐 도메인을 함께 탐색하는 워크숍이다(알베르토 브란돌리니). 복잡한 도메인을 팀이 같은 그림으로 이해하는 데 강력하다.

단, 모든 프로젝트에 필요하진 않다. 도메인이 단순하면 오버헤드가 된다. 효용이 있을 때만 꺼내는 도구로 보면 된다 — “해야 하니까”가 아니라 “이 도메인이 충분히 복잡해 같이 그려 볼 가치가 있나”로 판단한다.


5. 정책과 협업 — 언제 정하고, 어떻게 병렬로 가나

앞 단계들을 따라오면 자연스러운 질문이 둘 생긴다. “정책은 언제 정하지?” 그리고 “스펙을 넘기면 백엔드·프론트엔드는 각자 알아서 가는 건가?” 실전에서 가장 자주 어긋나는 두 지점이다.

5.1 정책은 한 번에 정해지지 않는다

미결 정책(만료 시간·중복 처리·권한 규칙 같은 것)은 보통 스토리·인수조건을 잡는 단계에서 처음 드러난다 — 인수조건을 쓰다 “이 경우엔 어떻게?”가 막히는 순간이다. 그걸 결정해 리빙 정책 문서에 적고 인수조건에 반영하는 게 출발이다.

하지만 정책은 거기서 끝나지 않는다. 개발하다, QA하다 새 구멍이 계속 드러난다 — 처음엔 안 보이던 엣지 케이스가 코드를 짜야 비로소 보이기 때문이다(1편의 불확실성 인정). 그래서 정책 문서는 한 번 쓰고 동결하는 명세가 아니라, 진행 내내 결정이 쌓이는 살아있는 문서다. 새 정책이 정해지면 즉시 문서에 반영하고, 영향받는 인수조건·코드·테스트를 함께 고친다.

데이터 모델도 같은 결이다(4.2절). 유저스토리·도메인 개념에서 윤곽이 잡히고 명세 단계에서 확정하지만, 마이그레이션이 쉬운 형태로 두어 확정하되 동결하지 않는다.

핵심: 정책·데이터 모델을 “초반에 다 못 박아야 한다”고 여기면 그게 폭포수다. 스토리·인수조건 단계에서 씨앗을 심고, 릴리스까지 계속 키우는 것으로 본다.

5.2 스펙 이후, 병렬은 단절이 아니다

API 명세를 합의하면 백엔드·프론트엔드(·앱)는 병렬로 달린다. 여기서 흔한 오해가 “스펙 넘겼으니 이제 각자 알아서”다. 그건 병렬이 아니라 단절이고, 합치는 순간 통합 지옥(4편)이 터진다.

병렬과 단절을 가르는 건 세 가지 공유 기준이다.

단절 (폭포수·가짜 애자일)병렬 (애자일)
스펙을 통째로 못 박고 “던지고” 각자 잠수시작할 만큼만 합의하고 병렬 착수
합칠 때 처음 만나 통합 지옥API 계약 + 인수조건으로 계속 정렬
정책 바뀐 걸 한참 뒤에 알게 됨리빙 정책 문서가 양쪽에 동시 반영
스펙은 동결, 어기면 잘못현실이 어긋나면 스펙을 재협상

세 가지 공유 기준을 정리하면 이렇다.

  • API 계약 — 서로 안 기다리게 하는 좌표다(3편 흐름의 의존성 감소).
  • 인수조건(Given/When/Then) — 백엔드·프론트가 같은 “완료의 정의”를 향하고, QA도 같은 기준으로 검증한다(2절).
  • 리빙 정책 문서 + 데일리 — 바뀐 결정을 양쪽에 즉시 동기화한다.

핵심은 5편 스케일링 교훈과 같다. 조율을 더 얹는 게 아니라 ‘조율할 필요’를 계약·인수조건·정책 문서로 줄여, 각 팀이 독립적으로 빠르게 가게 하는 것이다. 그래서 겉보기엔 ‘각자 알아서’처럼 보여도, 실은 공유 기준이 조율을 대신한다. 스펙을 던지고 다들 잠수하면 그건 병렬이 아니라 폭포수이자 가짜 애자일이다.


6. 딜리버리 한 바퀴 — 그리고 1–5편 매핑

이제 전체를 한 흐름으로 잇는다.

flowchart TD
    US["유저스토리 합의<br/>(스코프)"] --> AC["인수조건 확정<br/>Given/When/Then"]
    AC --> PR["우선순위·릴리스 순서<br/>MoSCoW · MVP"]
    PR --> WF["화면 정의<br/>로우파이 와이어프레임"]
    WF --> API["데이터·API 명세<br/>(스펙 우선)"]
    API --> DEV["개발"]
    DEV --> QA["인수조건 기반 QA<br/>Given/When/Then → 테스트"]
    QA --> REL["릴리스<br/>(MVP 우선)"]
    POL["정책 문서(리빙)"] -.->|"결정 즉시 반영"| AC
    POL -.-> DEV
    REL -.->|"배운 것 → 다음 스토리"| US

이 그림에서 두 개의 점선이 중요하다. 리빙 정책 문서는 인수조건과 개발 양쪽에 계속 결정을 흘려보낸다(진행 중 정책은 즉시 반영). 그리고 릴리스에서 배운 것이 다음 유저스토리로 돌아간다 — 이게 5편에서 닫은 검사-적응 학습 루프의 실전 모습이다.

각 단계가 시리즈의 어느 개념인지 매핑하면 이렇다.

단계하는 일연결되는 편
유저스토리 합의스토리·스코프·인수조건 확정1편 가치(고객 협력)·2편 백로그
인수조건·정책Given/When/Then, 정책 문서로 승격2편 DoD·투명성
우선순위MoSCoW·스토리 포인트·MVP4편 추정·1편 단순성
와이어프레임로우파이 화면1편 빨리 보여주고 피드백
데이터·API스펙 우선·병렬화3편 흐름(의존성 감소)·4편 프랙티스
QA인수조건 → 인수 테스트4편 TDD/ATDD
개발·릴리스MVP 우선·리빙 정책3편 흐름·5편 학습 루프

매핑이 말해 주는 건 분명하다. 실전 플로우는 새로운 무언가가 아니라, 1–5편 개념이 순서대로 작동하는 모습이다. 유저스토리는 “왜·누구를 위해”(가치)를 담고, 인수조건은 “끝의 정의”(DoD)를 주고, MoSCoW·MVP는 “무엇을 먼저”(우선순위)를, 스펙·QA는 “어떻게 안전하게”(프랙티스)를, 릴리스는 “배워서 다시”(학습 루프)를 돈다.


정리

6편의 핵심을 한 줄씩 정리하면 다음과 같다.

  • 유저스토리가 출발점이다 — 명세가 아니라 대화의 약속(As a/I want/so that), INVEST로 점검하고 인수조건을 붙인다.
  • 인수조건(Given/When/Then)이 DoD이자 QA의 씨앗 — 그대로 인수 테스트가 되고(ATDD/BDD), “QA에서 유저스토리로 검증”이 이 뜻이다.
  • MoSCoW·스토리 포인트·MVP로 범위를 자른다 — 특히 “이번엔 안 함(Won’t)“을 명시하는 게 단순성의 실행이다.
  • 스펙을 먼저 확정해 병렬로 만들되, 병렬은 단절이 아니다 — API 계약·인수조건·리빙 정책 문서가 백엔드·프론트를 묶는다. 던지고 잠수하면 그건 폭포수다.
  • 정책·데이터 모델은 한 번에 못 박지 않는다 — 스토리·인수조건 단계에서 씨앗을 심고 릴리스까지 리빙 문서로 갱신한다(확정하되 동결 않음).
  • 이 전체가 결국 검사-적응 학습 루프 한 바퀴 — 실전 플로우는 1–5편 개념이 순서대로 작동하는 모습일 뿐이다.

6편은 1편의 가치에서 5편의 스케일링까지를 유저스토리에서 릴리스까지 한 흐름으로 묶어 봤다. 먼저 당신의 팀에서 이 루프가 지금 돌고 있는지 들여다보고, 안 돌면 한 단계라도 되살리는 게 첫걸음이다.

다만 한 가지가 남는다 — 그 출발점인 유저스토리는 대체 어디서 오는가? 다음 7편(디스커버리)은 6편의 바로 앞 구간을 다룬다. 문제 발견에서 리서치·가설·기회를 거쳐 유저스토리가 만들어지기까지의 사전 작업이다. 6편의 딜리버리와 합치면 듀얼 트랙 애자일이라는 한 그림이 되고, 그것으로 시리즈를 닫는다.


부록

A. 용어 정리

용어한 줄 정의
유저스토리(user story)기능을 사용자 관점에서 “~로서, ~하고 싶다, ~하기 위해”로 적은 한 줄. 명세가 아니라 대화의 약속
INVEST좋은 스토리의 조건 — Independent·Negotiable·Valuable·Estimable·Small·Testable
복합 스토리(compound story)여러 작은 스토리가 하나로 뭉친 스토리 — 안에 든 스토리들로 분해해 쪼갠다
복잡한 스토리(complex story)하위 스토리 없이 그 자체로 크고 불확실한 스토리 — 스파이크로 불확실성부터 제거
에픽(epic)한 스프린트에 안 들어올 만큼 큰 스토리의 통칭(복합·복잡 모두 포함)
스토리 쪼개기(story splitting)큰 스토리를 INVEST를 만족하는 작은 가치 단위로 나누는 작업
SPIDR스토리 쪼개기 패턴 — Spike·Path·Interface·Data·Rules
인수조건(acceptance criteria)스토리가 받아들여지기 위해 만족해야 할 조건 목록
Given/When/Then상황·행위·결과로 인수조건을 적는 형식(BDD에서 유래)
BDD(Behavior-Driven Development)행위(시나리오) 중심으로 개발·검증하는 방식
ATDD(Acceptance Test-Driven Development)인수 테스트를 먼저 쓰고 통과시키는 개발 방식(TDD의 인수 레벨)
MoSCoW우선순위 분류 — Must·Should·Could·Won’t(이번엔 안 함)
MVP(Minimum Viable Product)가장 작지만 가치를 주고 배움을 얻을 수 있는 최소 기능 제품
로우파이 와이어프레임색·폰트 없이 화면 구성·흐름만 거칠게 그린 화면 정의
이벤트 스토밍(event storming)도메인 사건을 포스트잇으로 펼쳐 함께 탐색하는 워크숍(알베르토 브란돌리니)
리빙 정책 문서진행 중 결정된 정책·인수조건이 계속 누적·갱신되는 살아있는 문서

B. 외부 참조

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.