Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기

Confluence 공간 2개 · Jira 프로젝트 7개를 명세서에서 실제 Atlassian 사이트까지 옮긴 과정. AI가 만든 명세와 실제 API 사이의 간극을 어디서, 어떻게 발견하고 흡수했는가가 이 챕터의 주제입니다.

모듈
Module 2 · Chapter 1
난이도
실무
학습 시간
70분
선행 학습
Module 1 (R-C-T-C-F 프롬프팅 기초)
PM/PO AI 업무 자동화 강의1 / 21
§1

학습 목표 (Learning Objectives)

이 챕터를 마치면 수강자는 다음을 할 수 있습니다.

---

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기2 / 21
§2

실무 배경 — 어떤 문제에서 출발했나

  Product 1: 'PM/PO AI 기획 웹'  (KEY: IDEA_WEB)
  Product 3: 'PM/PO AI 강의 웹'  (KEY: LECTURE_WEB)
  Product 2: TOOL_A / TOOL_B / TOOL_C / TOOL_D
  Confluence 공간: PMPO_WIKI, PMPO_TOOLS
MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기3 / 21
§2

실무 배경 — 어떤 문제에서 출발했나 (계속)

💡 이 챕터의 핵심 교훈: AI가 쓴 기획서는 *읽기에* 완벽할 수 있습니다.

그것이 *실행 가능한지*는 완전히 다른 문제이고, 실행해 보기 전에는 알 수 없습니다.

---

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기4 / 21
§3

3.1 프롬프팅 프레임워크 적용 — R-C-T-C-F

실제로 사용한 프롬프트의 구조입니다.

요소실제 입력값
RoleFull-Stack Software Engineer & AI Automation Architect
ContextIT PM/PO 업무 자동화 4-Product 시스템. Confluence·Jira를 단일 원본으로 관리. 계정 비밀번호는 제공하지 않고 API 토큰만 사용
TaskConfluence 공간/페이지 트리 세팅 가이드 + Jira 프로젝트·백로그 세팅 자동화 스크립트/명세서 작성
Constraints멱등 실행, DRY-RUN 기본값, 비밀값 커밋 금지, 명세와 API 규칙이 충돌하면 먼저 알리고 정규화한 값으로 진행
FormatMarkdown 가이드 + Python CLI + 스펙 JSON

Constraints 한 줄이 결과를 갈랐습니다.

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기5 / 21
§3

3.1 프롬프팅 프레임워크 적용 — R-C-T-C-F (계속)

"명세와 API 규칙이 충돌하면 먼저 알려주고 정규화한 값으로 진행한다"

이 문장이 없었다면 AI는 두 가지 중 하나를 했을 겁니다 — ①명세대로 IDEA_WEB을 써서 실행 시점에 400 에러로 죽거나, ②말없이 IDEAWEB으로 바꿔서 나중에 명세서와 실제 시스템이 어긋난 걸 아무도 모르거나. 둘 다 나쁩니다.

📌 실습 포인트: 프롬프트에 "충돌하면 먼저 알려라"를 넣는 것과 넣지 않는 것의

차이를 직접 비교해 보세요. 이것이 Module 1에서 배운 Constraints의 실전 효과입니다.

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기6 / 21
§3

3.2 아키텍처 — 문서가 아니라 JSON이 단일 원본

              ┌──────────────────────────────────┐
              │  config/specs/*.json             │  ← 단일 원본
              │  confluence_spaces.json          │     (구조를 바꾸려면 여기만)
              │  jira_projects.json              │
              └───────────────┬──────────────────┘
                              │ 읽음
        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                     ▼
  setup validate        setup confluence       setup jira
  KEY 규칙 사전 검증     공간→페이지 트리        프로젝트→Epic→Story
  (실행 전 차단)         (멱등 생성)             (+ 리모트 링크 바인딩)
        │                     │                     │
        └─────────────────────┼─────────────────────┘
                              ▼
                        setup verify
                    실제 생성물 ↔ 스펙 대조 (읽기 전용)

  docs/4-product-system/*.md  ← JSON을 '설명'만 함. 여기를 고쳐도 아무것도 안 바뀜
MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기7 / 21
§3

3.2 아키텍처 — 문서가 아니라 JSON이 단일 원본 (계속)

왜 이 구조인가: 문서와 코드가 각자 구조를 알고 있으면 반드시 어긋납니다. JSON 하나만 진실로 두고, 문서는 그것을 설명하고, 코드는 그것을 실행합니다. 실제로 이 프로젝트에서 구조가 3번 바뀌었는데(KEY 명명 규칙이 두 번, 공간 키가 한 번) 매번 JSON 한 파일만 고치고 스크립트를 재실행해 끝났습니다.

---

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기8 / 21
§4

구현 과정 (Step by Step)

단계작업소요산출물
1명세서·대시보드 원문 읽고 실제 Atlassian 상태 조회10분현황 파악
2KEY 규칙 위반 발견 및 정규화 설계15분매핑 표
3스펙 JSON 2종 작성 (공간·페이지 / 프로젝트·백로그)40분config/specs/
4멱등 클라이언트 + 부트스트랩 구현90분common/, setup_ops/
5표준 템플릿 4종 + 세팅 가이드 3종 작성60분docs/4-product-system/
6테스트 작성 및 DRY-RUN 검증40분pytest 86건
MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기9 / 21
§4

4.1 KEY 규칙 위반 — 가장 중요한 15분

Atlassian의 실제 규칙:

대상규칙명세서 값결과
Jira 프로젝트 KEY^[A-Z][A-Z0-9]{1,9}$IDEA_WEB❌ 언더스코어 불가
Confluence 공간 KEY^[A-Za-z0-9]+$PMPO_WIKI❌ 언더스코어 불가

정규화하면서 추적성을 잃지 않는 것이 설계의 핵심이었습니다.

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기10 / 21
§4

4.1 KEY 규칙 위반 — 가장 중요한 15분 (계속)

명세 표기실제 KEY보존 방법
IDEA_WEBIDEAWEBJSON requestedKey + Jira 라벨 spec:IDEA_WEB
LECTURE_WEBLECTWEB〃
TOOL_A~TOOL_DTOOLA~TOOLD〃
PMPO_WIKIPMPOWIKIJSON requestedKey
PMPO_TOOLSPMPOTOOLS〃
MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기11 / 21
§4

4.1 KEY 규칙 위반 — 가장 중요한 15분 (계속)

def normalize_key(raw: str) -> str:
    """'DATA_PIPE' → 'DATAPIPE'. 규칙 위반이면 ValueError."""
    key = re.sub(r"[^A-Za-z0-9]", "", raw).upper()[:10]
    if not KEY_PATTERN.match(key):
        raise ValueError(
            f"Jira 프로젝트 KEY 로 쓸 수 없습니다: {raw!r} → {key!r}. "
            "규칙: 대문자로 시작, 대문자·숫자만, 2~10자."
        )
    return key

그리고 실행 전에 차단하는 명령을 따로 만들었습니다. 400 에러를 20분 뒤에 만나는 것보다 0초에 표로 보는 게 낫습니다.

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기12 / 21
§4

4.1 KEY 규칙 위반 — 가장 중요한 15분 (계속)

$ python main.py setup validate

                          KEY 검증
┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ 종류            ┃ 명세 표기   ┃ 실제 생성 KEY ┃ 결과     ┃
┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ Confluence 공간 │ PMPO_WIKI   │ PMPOWIKI      │ 정규화됨 │
│ Jira 프로젝트   │ IDEA_WEB    │ IDEAWEB       │ 정규화됨 │
│ Jira 프로젝트   │ TOOL_A      │ TOOLA         │ 정규화됨 │
│ Jira 프로젝트   │ PORTFOLIO   │ PORTFOLIO     │ OK       │
└─────────────────┴─────────────┴───────────────┴──────────┘
MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기13 / 21
§4

4.2 멱등성 — 재실행이 안전해야 손이 가벼워진다

부트스트랩은 몇 번을 돌려도 같은 결과여야 합니다. 그래야 "일단 돌려보자"가 가능합니다.

def ensure_project(self, *, key, name, description="", ...):
    """프로젝트가 없으면 생성하고, 있으면 그대로 반환합니다."""
    key = normalize_key(key)
    if existing := self.find_project(key):
        logger.info("Jira 프로젝트 이미 존재: %s (%s)", key, existing.get("name"))
        return existing
    ...

이슈는 project + summary 완전일치로 조회한 뒤 없을 때만 만듭니다. Confluence 페이지는 제목 기준 upsert이되 기존 본문은 덮어쓰지 않습니다 — 사람이 채운 위키 내용을 스크립트 재실행이 날리는 사고를 막기 위한 기본값입니다.

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기14 / 21
§4

4.3 DRY-RUN이 기본값 — 쓰기는 막고 읽기는 통과

write = method.upper() in {"POST", "PUT", "DELETE"} and not read_only
if write and self.dry_run:
    logger.info("[DRY-RUN] %s %s %s", method, url, _preview(json))
    return dry_run_result if dry_run_result is not None else {}

읽기는 dry-run에서도 실제로 보냅니다. 그래야 "이미 있는 것 / 새로 만들 것"을 정확히 구분해 보여줄 수 있습니다. 읽기까지 막으면 DRY-RUN 출력이 거짓말을 하게 됩니다.

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기15 / 21
§4

4.4 자동화할 수 없는 것을 정직하게 남기기

전부 자동화하려다 실패하는 것보다, 경계를 명확히 긋고 수동 절차를 체크리스트로 주는 것이 낫습니다. API 지원 범위를 조사해 아래를 문서에 명시했습니다.

항목왜 수동인가대응
보드 컬럼 개명팀 관리형 프로젝트 상태를 다루는 공개 API 없음UI 1회 작업 + 체크리스트
Confluence 공간 템플릿 등록REST 지원 제한적99. 표준 템플릿 보관함 페이지로 대체
Google 서비스 계정·OAuth콘솔 동의 필요절차 문서화
Apps Script 설치계정 권한 동의 필요setupTriggers 원클릭 함수 제공

그리고 자동화 실패가 전체를 죽이지 않게 했습니다. 상태 전이가 불가능하면 경고만 남기고 이슈 생성은 계속합니다.

---

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기16 / 21
§5

결과 & 검증 (Result & Validation)

지표BeforeAfter측정 근거
초기 세팅 소요반나절(수기)약 3분(CLI) + 수동 2건실행 로그
구조 변경 반영전 항목 수기 재작업JSON 1파일 수정 + 재실행실제 3회 변경 수행
잘못된 KEY로 인한 실패발견 시점 = 실행 중실행 전 setup validate에서 차단exit 1
명세 ↔ 시스템 추적정규화 시 단절requestedKey + spec: 라벨로 유지JQL 조회 가능

실제 생성물 (2026-08-08 실측)

Confluence  PMPOWIKI   9 페이지 (1단계 5 + Module 1~4)
            PMPOTOOLS 13 페이지 (Tool A~D 4 + 각 하위 2 + 템플릿 1)
Jira        프로젝트 7개 + Epic 20여 개 + Story/Task 40여 건
MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기17 / 21
§5

배운 것 / 다시 한다면 바꿀 것

💡 위 두 개의 ❌ 는 우연이 아닙니다. 둘 다 "실행해 보지 않고 문서에 쓴 것" 입니다.

Chapter 2가 정확히 이 지점에서 출발합니다.

---

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기18 / 21
§6

산출물 링크 (Deliverables)

구분URL
교안 PDF/슬라이드/downloads/module2-ch1-slides.pdf
슬라이드 (HTML)/downloads/module2-ch1-slides.html
실습용 데이터 세트pm-po-automation-tools/config/specs/*.json
시뮬레이터 실행(해당 없음)
GitHub 저장소https://github.com/ursulax/action
세팅 가이드docs/4-product-system/01-confluence-space-guide.md
Jira 명세서docs/4-product-system/02-jira-project-spec.md
실제 ConfluencePM/PO 업무 위키 (비공개)
관련 PRhttps://github.com/ursulax/action/pull/20

---

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기19 / 21
§7

실습 과제 (Action Item)

  1. 본인 업무의 반복 세팅 하나를 골라 스펙 JSON으로 표현하세요. (예: 프로젝트 킥오프 시 만드는 문서 5종, 매주 만드는 리포트 구조)
  1. R-C-T-C-F 프롬프트를 작성하되, Constraints에 반드시 다음을 넣으세요. `` 명세와 실제 API/시스템 규칙이 충돌하면 먼저 알려주고, 정규화한 값으로 진행하되 원 표기를 추적 가능하게 보존하라. ``
  1. validate 성격의 사전 검증 단계를 먼저 만드세요. 본 작업보다 먼저.

제출물: 스펙 JSON 1개 + 검증 함수 1개 + 정규화 매핑 표 자가 점검 기준:

---

MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기20 / 21
§8

Q&A / 자주 막히는 지점

증상원인해결
Jira 프로젝트 생성 시 400KEY에 언더스코어/소문자/11자 이상normalize_key() 통과 후 사용. setup validate로 사전 차단
Confluence 공간 생성 시 거부공간 KEY에 특수문자영숫자만 사용. 표시명에는 슬래시 등 가능
MCP 연동으로 자동화가 안 됨세션 스코프에 공간 생성·Jira 권한 없음API 토큰 기반 REST 사용. 스코프를 먼저 확인할 것
재실행하니 티켓이 중복 생성됨존재 확인 없이 create만 호출ensure_* 패턴 — 조회 후 없을 때만 생성
사람이 채운 위키가 날아감upsert가 항상 덮어씀기본값을 본문 보존으로. 덮어쓰기는 명시적 옵션으로 분리
DRY-RUN 출력이 실제와 다름읽기 호출까지 막아서 상태를 모름쓰기만 막고 읽기는 통과시킬 것
MOD2 Ch1 · Module 2 · Chapter 1 — 명세서를 실행 가능한 시스템으로 옮기기21 / 21