Files
aio-01d4-stu/Last3Mon/69/LAB-02-Q-COLLABORATION.md

16 KiB

Lab 02 · 작성 → 검토 → 고쳐 쓰기, 정리자 Fork-Join

  • 관련 강의: 2단계 · 업무 분배와 협업 워크플로우 · 개념: CONCEPT-69 1 · 4 · 6절
  • 소요 시간: 약 70분 (예제 1 10분 · 예제 2 25분 · 과제 35분)
  • 준비: Lab 01의 my-work/writer.json, my-work/reviewer.json이 push되어 있어야 합니다. 검토자가 없으면 cp reference/lab02-start-reviewer.json my-work/reviewer.json 후 push.
  • 목표: 역할들이 정해진 순서로 이어서 일하는 공지 팀을 만든다. 고쳐 쓰기는 한 번만, 그래도 안 되면 사람에게. 정리자 둘은 서로 기다리지 않게 갈래를 나눠(Fork) 돌리고 다시 모은다(Join).
flowchart LR
  E1["예제 1<br/>계약 맞추기"] --> E2["예제 2<br/>공지 팀<br/>(고쳐 쓰기 1회)"] --> T["과제<br/>정리자<br/>Fork-Join"]

복사해 붙일 요청문은 없습니다. 막히면 ▶ 를 하나씩 펼칩니다.

이 Lab을 읽는 법

Lab 01과 같은 다섯 걸음입니다. 오늘부터는 역할이 역할을 부르므로, 공지 팀의 결과가 이상하면 역할을 하나씩 직접 call 해서 어디서 달라졌는지 봅니다.

flowchart LR
  A["① 요청<br/>무엇을 보내나"] --> B["② call<br/>지금 결과"] --> C["③ 나와야 할 결과<br/>응답 JSON"] --> D["④ 테스트가<br/>보는 곳"] --> E["⑤ 만들고<br/>push · call · test"]
  • 명령은 모두 ~/aio-01d4-stu/Last3Mon/69에서, uv run tools/flow.py call <웹훅 경로> <요청> (첫 줄 HTTP 코드 · 걸린 시간, 그 아래 응답 JSON. -v를 붙이면 보낸 요청 JSON도 함께. $로 시작하는 출력 예는 -v 없이 응답만).
  • 웹훅 경로 (AGENTS.md 3절): 작성자 my-writer, 검토자 my-reviewer, 공지 팀 my-notice.
  • 아래 "나와야 할 결과"는 수업 준비 때 정답 워크플로우로 실제로 돌린 값입니다. LLM이 쓴 문장과 시간은 조금씩 다릅니다.

예제 1 · 계약 맞추기 (10분)

공지 팀은 작성자와 검토자를 HTTP로 부릅니다. 부르기 전에 두 역할이 계약대로 답하는지 직접 봅니다.

① 보내는 요청 · requests/notice-phone.json, requests/review-personal-info.json

{"request": "프린터 드라이버 업데이트 공지를 써 주세요. 문의처 연락처도 꼭 넣어 주세요.",
 "facts": "10월 30일(목) 18:00~19:00 사내 프린터 드라이버 일괄 업데이트. 업데이트 중에는 출력이 안 됨. 문의는 IT팀 김민지(010-1234-5678, minji.kim@example.com).",
 "author": "김민지"}

review-personal-info.json은 같은 사실과, 마지막 줄이 문의: IT팀 김민지(010-1234-5678)인 초안입니다 (Lab 01 과제).

② · ③ 두 역할을 직접 부르기

uv run tools/flow.py push my-work/writer.json my-work/reviewer.json
uv run tools/flow.py call my-writer requests/notice-phone.json -v
uv run tools/flow.py call my-reviewer requests/review-personal-info.json -v
HTTP 200 · 0.7s
{"passed": false, "codes": ["personal_info"], "issues": [{"code": "personal_info", "detail": "010-1234-5678"}]}
확인 왜
작성자 결과에 draft 하나 공지 팀이 $json.draft로 읽음
검토자 결과에 passed(불리언), codes(목록), issues[].detail 공지 팀이 passed로 갈림길을, issues로 고쳐 쓰기 의견을 만듦
작성자에게 previous_draft, feedback을 주면 고쳐 쓰나 아래 ④에서 직접

④ 고쳐 쓰기를 직접 시험하기

call 뒤에 JSON을 직접 씁니다. 위 검토 결과의 issues를 feedback 글자로, 처음 초안을 previous_draft로 넣습니다.

uv run tools/flow.py call my-writer '{"request": "프린터 드라이버 업데이트 공지를 써 주세요. 문의처 연락처도 꼭 넣어 주세요.", "facts": "10월 30일(목) 18:00~19:00 사내 프린터 드라이버 일괄 업데이트. 업데이트 중에는 출력이 안 됨. 문의는 IT팀 김민지(010-1234-5678, minji.kim@example.com).", "previous_draft": "[프린터 드라이버 업데이트 안내]\n10월 30일(목) 18:00~19:00에 사내 프린터 드라이버를 일괄 업데이트합니다.\n업데이트 중에는 출력이 안 됩니다.\n문의: IT팀 김민지(010-1234-5678)", "feedback": "personal_info: 010-1234-5678"}' -v

나와야 할 모양 (수업 준비 때 공지 팀 안에서 고쳐 쓴 결과의 마지막 줄):

{"draft": "[프린터 드라이버 업데이트 안내]\n…\n문의는 IT팀 김민지에게 연락 바랍니다."}
  • 번호 · 메일 주소가 빠지고, 날짜 · 시간은 그대로, 그리고 새로 생긴 정보가 없어야 합니다. 수업 준비 때 한 번은 작성자가 지운 번호 대신 (내선 1234)를 지어냈습니다.
  • 고쳐 쓰지 않고 처음과 같은 초안이 오면 작성자가 previous_draft · feedback을 읽지 않는 것입니다. 공지 팀을 만들기 전에 작성자부터 고칩니다.
▶ 1단계 · 계약이 안 맞으면

결과 칸 이름이 다르면(text 대신 draft, ok 대신 passed) 공지 팀 쪽에서 맞추지 말고 그 역할을 계약대로 고칩니다. 그래야 다른 사람의 역할(reference/lab02-start-reviewer.json)과도 바꿔 끼울 수 있습니다.


예제 2 · 공지 팀 (25분)

만들 것: 공지 요청을 받아 작성자 → 검토자 순으로 부르고, 통과하지 못하면 검토 의견을 주어 한 번 고쳐 쓰게 한 뒤 다시 검토하는 워크플로우.

조건 값
저장할 파일 my-work/notice.json (오늘 끝까지 이 파일을 고쳐 나감)
웹훅 · 계약 AGENTS.md 3절 공지 팀 (my-notice)
결과 한 번에 통과 게시 가능 · 고쳐서 통과 수정 후 게시 가능 · 고쳐도 실패 담당자 확인. published, rounds, notice, codes (고쳐 썼으면 first_codes)
테스트 cases/lab02.json

① 보내는 요청

Lab 01과 같은 공지 요청 넷: requests/notice-mail.json, notice-vpn.json, notice-phone.json, notice-empty.json. 공지 팀이 받는 것은 작성자와 같은 요청입니다.

② 지금 결과 (아직 만들기 전)

uv run tools/flow.py call my-notice requests/notice-phone.json -v

HTTP 404 ("POST my-notice" is not registered).

③ 나와야 할 결과

$ uv run tools/flow.py call my-notice requests/notice-mail.json
HTTP 200 · 2.0s
{
  "route": "게시 가능",
  "published": true,
  "rounds": 1,
  "notice": "[메일 서버 점검 안내]  \n10월 23일(목) 22:00~24:00 메일 서버 점검이 진행됩니다.  \n…",
  "codes": []
}

$ uv run tools/flow.py call my-notice requests/notice-phone.json
HTTP 200 · 4.1s
{
  "route": "수정 후 게시 가능",
  "published": true,
  "rounds": 2,
  "notice": "[프린터 드라이버 업데이트 안내]\n\n10월 30일(목) 18:00~19:00 사내 프린터 드라이버가 일괄 업데이트됩니다.  \n업데이트 시간 동안에는 프린터 출력이 불가합니다.  \n문의는 IT팀 김민지에게 연락 바랍니다.",
  "codes": [],
  "first_codes": ["personal_info"]
}

$ uv run tools/flow.py call my-notice requests/notice-empty.json
HTTP 400
{"error": "facts are required"}
  • 프린터 공지는 첫 검토에서 personal_info(first_codes)로 걸리고, 고쳐 쓴 뒤 두 번째 검토를 통과했습니다 (codes: [], rounds: 2). 시간이 두 배쯤인 이유입니다 (LLM 4번).
  • 고쳐도 실패하면: "route": "담당자 확인", "published": false, codes는 두 번째 검토의 코드.

④ 테스트가 보는 곳 · cases/lab02.json

케이스 요청 테스트가 보는 곳
메일 점검 · 게시 notice-mail published: true, codes: [], route에 게시 가능, notice에 22:00
VPN · 게시 notice-vpn published: true, codes: [], notice에 10월 24일
연락처가 든 사실 · 고쳐서 게시 notice-phone published: true, codes: [], route에 게시 가능, notice에 010-1234-5678 · minji.kim@example.com 없음
사실 없음 notice-empty HTTP 400

⑤ 만들고 확인

uv run tools/flow.py push my-work/notice.json
uv run tools/flow.py call my-notice requests/notice-phone.json -v   # ③과 같은 모양인가
uv run tools/flow.py test cases/lab02.json -v                       # 수업 준비 때 4/4 (처음엔 검토자 거짓 경보로 3/4)

관찰: notice-phone에서 고쳐 쓴 공지를 처음 초안과 비교해 봅니다. 전화번호만 빠졌나요, 다른 것도 바뀌었나요? n8n Executions에서 my-notice 한 번에 my-writer, my-reviewer 실행이 몇 개씩 생겼나요?

▶ 1단계 · 흐름을 먼저 그리기

DESIGN.md 3절에 mermaid로 그립니다. 작성 → 검토 → 통과? → (예) 게시 / (아니오) 고쳐 쓰기 → 다시 검토 → 통과? 각 화살표에 무엇이 넘어가는지(draft, issues)를 적습니다. ③의 결과 셋이 그림의 어느 끝인지 표시합니다.

▶ 2단계 · 왜 "한 번만"인가

"통과할 때까지 고쳐라"라고 하면 어떻게 될까요? 작성자와 검토자가 끝없이 주고받거나, 고칠수록 이상한 글이 될 수 있습니다. 고쳐 쓰기 횟수와 그 뒤에 할 일을 요청문에 정확히 적습니다. n8n에서 같은 역할을 두 번 부르는 방법은 AGENTS.md 5절 HTTP Request의 주의를 봅니다.

▶ 3단계 · 넘기는 값의 출처

다시 검토할 때 검토자에게 주는 facts는 어디서 가져오나요? 고쳐 쓴 작성자가 사실을 바꿔 전했다면? 사실은 처음 요청에서 가져와야 검토가 의미가 있습니다. feedback은 검토 결과의 무엇으로 만들지도 정합니다 (예제 1 ④에서 직접 넣어 본 모양).

▶ 4단계 · 보내기 전 점검표
  • 새 파일, 웹훅, 부를 역할 둘의 주소
  • 고쳐 쓰기 1회와 결과 세 가지(route 값)
  • 각 결과의 필드 (published, rounds, notice, codes, first_codes)
  • facts의 출처, feedback 만드는 법
  • cases/lab02.json

과제 · 정리자 둘을 나눠 돌리고 모으기 (35분)

팀장이 요청했습니다.

"통과한 공지는 메일로도, 메신저로도 보내요. 메일 제목 한 줄, 메신저용 짧은 글을 같이 만들어 주세요. 둘은 서로 상관없으니 기다리지 말고 동시에요."

조건 값
고칠 파일 my-work/notice.json
새 필드 title (메일 제목, 40자 이내), chat (메신저 글, 2줄 · 120자 이내, 날짜·시간 그대로)
언제 통과한 공지만 (게시 가능, 수정 후 게시 가능). 담당자 확인이면 만들지 않음
테스트 cases/lab02-challenge.json, 그리고 cases/lab02.json도 계속 통과

① 보내는 요청 · 예제 2와 같음

② 지금 결과 (예제 2 완성본)

예제 2 ③과 같습니다. title, chat 칸이 없습니다.

③ 나와야 할 결과

$ uv run tools/flow.py call my-notice requests/notice-mail.json
HTTP 200 · 6.5s
{
  "route": "게시 가능",
  "published": true,
  "rounds": 1,
  "notice": "[메일 서버 점검 안내]\n\n- 10월 23일(목) 22:00~24:00 메일 서버 점검이 진행됩니다.\n- …",
  "codes": [],
  "title": "10월 23일 메일 서버 점검 안내드립니다",
  "chat": "10월 23일(목) 22:00~24:00 메일 서버 점검으로 송수신 불가, 종료 후 프로그램 재실행 바랍니다."
}

$ uv run tools/flow.py call my-notice requests/notice-phone.json
HTTP 200 · 5.8s
{
  "route": "수정 후 게시 가능",
  …
  "first_codes": ["personal_info"],
  "title": "10월 30일 프린터 드라이버 업데이트 안내",
  "chat": "10/30(목) 18:00~19:00 프린터 드라이버 업데이트로 출력 불가. 문의: IT팀 김민지."
}
  • 예제 2의 칸은 그대로 두고 title, chat만 더합니다 (lab02가 계속 통과해야 함).
  • chat에도 개인 연락처가 없어야 합니다. 정리자가 줄이면서 지운 번호를 되살리면 검토가 소용없습니다.

④ 테스트가 보는 곳 · cases/lab02-challenge.json

케이스 요청 테스트가 보는 곳
메일 점검 · 제목과 메신저 notice-mail published: true, title에 점검, chat에 10월 23일, chat에 쿠폰 없음
VPN · 메신저에 마감일 notice-vpn chat에 10월 24일, title에 VPN 또는 인증서
고쳐서 게시 · 메신저에도 연락처 없음 notice-phone published: true, chat · notice에 010-1234-5678 · minji.kim@example.com 없음
사실 없음 notice-empty HTTP 400

⑤ 만들고 확인

uv run tools/flow.py push my-work/notice.json
uv run tools/flow.py call my-notice requests/notice-mail.json -v            # ③과 같은 모양인가
uv run tools/flow.py test cases/lab02-challenge.json -v --repeat 3          # 수업 준비 때 4/4
uv run tools/flow.py test cases/lab02.json -v                               # 4/4 그대로
▶ 1단계 · Fork-Join

한 노드에서 두 노드로 함께 보내고(Fork), 둘이 끝나면 하나로 모읍니다(Join). AGENTS.md 5·6절의 Merge와 연결 예를 봅니다. 통과한 길이 두 개(게시 가능, 수정 후 게시 가능)인데, 정리자를 두 번 만들지 않으려면?

▶ 2단계 · 정리자의 "하지 않는 일"

정리자가 공지를 줄이면서 날짜를 바꾸거나, 지운 연락처를 되살리면 검토가 소용없어집니다. 정리자에게 무엇을 금지할지, 테스트가 무엇으로 확인하는지(④의 not_contains) 봅니다. 정리자 결과는 다시 검토하지 않아도 될까요? 내 생각을 DESIGN.md에 적습니다.

▶ 3단계 · 시간 재기 · 정말 동시에 돌았나

정리자를 붙이기 전(lab02 보고서, 수업 준비 때 2.0~4.1초)과 뒤(lab02-challenge 보고서)의 seconds를 비교합니다. 그리고 n8n Executions에서 my-notice 실행 하나를 열어 Make title, Make chat message(내가 붙인 두 정리자 노드)를 하나씩 눌러 봅니다. 각 노드의 시작 시각과 걸린 시간을 적어 보세요.

  • 두 노드가 거의 같은 시각에 시작했나요, 하나가 끝난 시각에 다른 하나가 시작했나요?
  • 늘어난 시간은 두 정리자 시간의 합에 가까운가요, 긴 쪽에 가까운가요?

설계(서로 기다릴 필요 없음)와 n8n이 실제로 돌리는 방식이 같은지 DESIGN.md에 적습니다. 68일차 3단계 교안에서 본 "같은 HTTP 노드의 항목은 함께, 갈래는 차례로"도 떠올려 봅니다.

막혔을 때

증상 할 일
응답 500 {"message": "Error in workflow"} 부른 역할이 400·404를 돌려줬을 수 있습니다. call my-writer · call my-reviewer로 하나씩 직접 불러 보고, Executions에서 빨간 노드 확인 (Lab 03에서 다룸)
공지 팀이 404 아직 push하지 않았거나 웹훅 경로가 my-notice가 아님
좋은 공지인데 계속 담당자 확인 call my-reviewer로 그 초안을 직접 검토해 issues.detail을 봅니다. 검토자의 거짓 경보일 수 있음 (Lab 01 과제 ▶ 3단계)
고쳐 쓴 공지에 새 번호(내선 등) 작성자 지시문의 "고칠 때 새 정보를 더하지 않는다"를 확인 (예제 1 ④로 직접 시험)
공지 팀이 계속 안 됨 cp reference/lab02-example2-pipeline.json my-work/notice.json 후 push

확인

  • 예제 1: 두 역할을 직접 불러 계약을 확인하고, 고쳐 쓰기를 call my-writer '{...}'로 시험했다.
  • 예제 2: call my-notice가 404 → ③ 모양, cases/lab02.json 통과, 연락처 공지가 고쳐져 게시된다.
  • 과제: cases/lab02-challenge.json 3번 반복 통과, lab02도 통과, 두 정리자의 시작 시각을 DESIGN.md에 적었다.