diff --git a/Lecture/78/00-DAY78-OVERVIEW.md b/Lecture/78/00-DAY78-OVERVIEW.md index 3cf4e9a..30edf72 100644 --- a/Lecture/78/00-DAY78-OVERVIEW.md +++ b/Lecture/78/00-DAY78-OVERVIEW.md @@ -110,7 +110,7 @@ flowchart LR | 품질 차원 (업계에서 쓰는 말) | 뜻 | 오늘 B-1에서 | | --- | --- | --- | | 완전성 (completeness) | 빠진 칸이 없나 | 빈 제목 · 빈 답 · 빈 키워드 | -| 유효성 (validity) | 꼴 · 길이 · 값의 범위 | id 두 자리 · 300자 넘는 답 | +| 유효성 (validity) | 형식 · 길이 · 값의 범위 | id 두 자리 · 300자 넘는 답 | | 고유성 (uniqueness) | 겹치지 않나 | 같은 배치에 FAQ-102가 둘 | | 일관성 (consistency) | 이미 있는 데이터와 맞나 | 이미 있는 FAQ-003 · 같은 제목 | | 정확성 (accuracy) | 내용이 맞나 | 토너 제목에 메일함 답 (LLM) | @@ -153,7 +153,7 @@ flowchart LR | 판정 | 누가 | 까닭 | | --- | --- | --- | -| 빈 값 · 길이 · 꼴 · 중복 · 전화번호 꼴 | **Code** | 정해진 답, 늘 같게, 0.1초 | +| 빈 값 · 길이 · 형식 · 중복 · 전화번호 형식 | **Code** | 정해진 답, 늘 같게, 0.1초 | | 이미 있는 데이터 | Code + 데이터 API | 목록을 읽어 비교 | | 답이 질문에 맞나 | **LLM** (Lab 02) | 뜻은 규칙으로 못 잼 | @@ -210,7 +210,7 @@ flowchart LR | 정한 것 | 오늘 | | --- | --- | | 무엇이 나쁜가 | 차원 여섯 · 코드 열둘 (AGENTS.md 3절) | -| 경계의 말 | 넘으면 · 공백 · 꼴 · 정확히 | +| 경계의 말 | 넘으면 · 공백 · 형식 · 정확히 | | 막을 것 · 알릴 것 | 오류 vs 경고 | | 누구에게 무엇을 | 기계에는 코드, 사람에게는 말 | @@ -327,7 +327,7 @@ flowchart LR "joined": {"type": "date", "required": true}, "phone": {"type": "phone", "required": false}, …}} ``` -- 직원 CSV 9줄 → **4줄 통과** (메일 꼴 · `마케팅팀` · `2026-13-01` · 이름 빈칸 · 메일 중복). 같은 워크플로우로 FAQ B-3 → 5/6. +- 직원 CSV 9줄 → **4줄 통과** (메일 형식 · `마케팅팀` · `2026-13-01` · 이름 빈칸 · 메일 중복). 같은 워크플로우로 FAQ B-3 → 5/6. - 업계 이름: JSON Schema · 테이블 정의 · Great Expectations의 "기대값". --- @@ -338,7 +338,7 @@ flowchart LR | 규칙 | 스키마로 | 따로 | | --- | --- | --- | -| 필수 · 길이 · 꼴 · 목록 · 날짜 · 중복 | ✔ | | +| 필수 · 길이 · 형식 · 목록 · 날짜 · 중복 | ✔ | | | 답 안의 개인정보 · 바깥 링크 | | Code (Lab 01) | | 이미 있는 데이터 | | Code + 데이터 API | | 답이 질문에 맞나 | | LLM | @@ -370,7 +370,7 @@ flowchart LR | 접수 건 9줄 | FAQ 검사 | 스키마 검사 (고치지 않음) | | --- | --- | --- | | 통과 | 0줄 (칸이 달라 모두 나쁨) | **2줄** | -| 이유 | `id_format` · `answer_missing` 9 | `T-12` · `urgent` · **2월 30일** · 3자 제목 · 연락처 꼴 · 중복 · 요청자 빈칸 | +| 이유 | `id_format` · `answer_missing` 9 | `T-12` · `urgent` · **2월 30일** · 3자 제목 · 연락처 형식 · 중복 · 요청자 빈칸 | - 워크플로우를 한 줄도 고치지 않았습니다 — `ticket.json` 한 장. 그래서 **스키마 변경 = 규칙 변경**, 리뷰가 필요 (74일차 관문). diff --git a/Lecture/78/01-STAGE1-QUALITY-RULES.md b/Lecture/78/01-STAGE1-QUALITY-RULES.md index 779a1b5..4e63769 100644 --- a/Lecture/78/01-STAGE1-QUALITY-RULES.md +++ b/Lecture/78/01-STAGE1-QUALITY-RULES.md @@ -1,7 +1,7 @@ # 1단계 · 품질 규칙 - 교시: 1교시 강의 (09:00~09:50) · 2교시 [Lab 01](../../Last3Mon/78/LAB-01-Q-QUALITY-RULES.md) 실습 5개 (10:00~10:50) -- 이 단계가 끝나면: 들어오는 데이터의 "나쁨"을 품질 차원으로 나눠 말하고, 그것을 **말로 정한 규칙과 오류 코드**로 바꾸고, 규칙을 Code로 확인하는 검사를 만들고, 경계(딱 기준선 · 공백 · 꼴 · 비슷한 주소)에서 재 보고, 이미 있는 데이터와 맞춰 보고, 보낸 사람에게 고칠 것을 돌려줄 수 있다. +- 이 단계가 끝나면: 들어오는 데이터의 "나쁨"을 품질 차원으로 나눠 말하고, 그것을 **말로 정한 규칙과 오류 코드**로 바꾸고, 규칙을 Code로 확인하는 검사를 만들고, 경계(딱 기준선 · 공백 · 형식 · 비슷한 주소)에서 재 보고, 이미 있는 데이터와 맞춰 보고, 보낸 사람에게 고칠 것을 돌려줄 수 있다. - 숫자: 모두 강사 노트북 실측입니다 (65일차 n8n 2.42.6, 78일차 판 데이터 API, FAQ 배치 B-1 13줄 · B-2 10줄 · 경계 배치 10줄, 11/01 실습을 5개로 늘린 뒤 정답 워크플로우를 학생 순서대로 1회). 규칙 판정은 누가 돌려도 같습니다. 출력 전부는 수업 뒤 공개되는 `Last3Mon/78/answers/LAB-TEST-RESULT.md`에 있습니다. - 실습 가리키기: 이 문서에서 "Lab 01 예제 1"은 2교시 [Lab 01 문서](../../Last3Mon/78/LAB-01-Q-QUALITY-RULES.md)의 그 절입니다. 파일 경로(`cases/` · `requests/` · `my-work/` · `reference/`)는 실습 폴더 `~/aio-01d4-stu/Last3Mon/78` 기준입니다. @@ -40,18 +40,30 @@ flowchart LR > **생활 속 예 · 식당 주방의 아침 검수.** 아침마다 납품 트럭이 오면 주방장이 상자를 엽니다. 무른 채소, 라벨 없는 고기, 주문하지 않은 물건, 유통기한이 지난 우유를 골라냅니다. "상자가 비어 있지 않으면 받는다"로는 식당이 망합니다. 워크플로우가 믿고 쓰는 데이터(오늘은 FAQ)가 이 식재료이고, 오늘은 검수를 자동으로 하는 날입니다. -**데이터 품질(data quality)은 "이 데이터를 믿고 써도 되나"입니다.** 77일차까지는 실행 기록을 **믿고** 셌습니다. 그런데 워크플로우가 읽는 데이터(FAQ · 직원 목록 · 접수 건)가 틀리면, 워크플로우는 틀린 답을 **자신 있게** 냅니다 — 답을 못 하는 것보다 나쁩니다. 업계에서는 "나쁨"을 몇 가지 **차원**으로 나눠 말합니다. +**데이터 품질(data quality)은 "이 데이터를 믿고 써도 되나"입니다.** 워크플로우 · 보고서 · 챗봇은 모두 어딘가의 데이터를 읽고 일합니다. 읽는 데이터가 틀리면 프로그램은 멈추지 않고 틀린 결과를 **자신 있게** 냅니다. 답을 못 하는 것보다 틀린 답이 더 나쁜 까닭입니다. 그래서 데이터를 쓰기 전에 "좋은가"를 먼저 묻습니다. -| 차원 | 묻는 것 | 예 | +"좋다 · 나쁘다"는 막연하므로 업계에서는 나쁨을 몇 가지 **차원(dimension)**으로 나눠 말합니다. 차원으로 나누면 무엇을 검사해야 할지 빠뜨리지 않고, 문제가 생겼을 때 어떤 종류의 문제인지 이름을 붙일 수 있습니다. + +| 차원 | 묻는 것 | 흔한 예 | | --- | --- | --- | -| 완전성 (completeness) | 꼭 있어야 할 칸이 있나 | 빈 제목 · 빈 답 | -| 유효성 (validity) | 꼴 · 길이 · 값의 범위가 맞나 | id 꼴, 300자 넘는 답, 없는 날짜 | -| 고유성 (uniqueness) | 겹치지 않나 | 같은 id가 둘 | -| 일관성 (consistency) | 다른 데이터와 맞나 | 이미 있는 id · 같은 제목 | -| 정확성 (accuracy) | 내용이 사실 · 질문과 맞나 | 토너 제목에 메일함 답 | -| 적시성 (timeliness) | 지금도 맞나 | 지난 정책의 답 (오늘은 다루지 않음) | +| 완전성 (completeness) | 꼭 있어야 할 값이 있나 | 이름이 빈 회원, 주소가 빠진 주문 | +| 유효성 (validity) | 형식 · 길이 · 범위가 맞나 | `@`가 없는 메일, 2월 30일, 음수 나이 | +| 고유성 (uniqueness) | 겹치지 않나 | 같은 회원 번호가 두 명 | +| 일관성 (consistency) | 다른 데이터와 맞나 | 없는 부서 코드, 이미 있는 상품 번호 | +| 정확성 (accuracy) | 내용이 사실과 맞나 | 질문과 상관없는 답, 틀린 가격 | +| 적시성 (timeliness) | 지금도 맞나 | 바뀌기 전 규정 · 지난 가격 | -여기에 FAQ처럼 **사람에게 그대로 보여 주는** 데이터는 안전(개인정보 · 위험한 링크)도 품질입니다. 업계 도구로는 Great Expectations · Soda · dbt tests · Deequ 같은 "데이터 검사" 도구가 있고, 데이터 파이프라인(ETL)의 한 단계로 검사를 둡니다. 오늘은 n8n 워크플로우로 같은 일을 합니다. +사람에게 그대로 보여 주는 데이터(고객 안내문 · FAQ · 공지)에는 **안전**(개인정보 · 위험한 링크)도 품질에 들어갑니다. 업계에서는 Great Expectations · Soda · dbt tests · Deequ 같은 데이터 검사 도구를 쓰고, 데이터를 옮기고 쌓는 과정의 한 단계로 검사를 둡니다. + +**ETL과 데이터 파이프라인.** 회사의 데이터는 한곳에서 생기지 않습니다. 주문은 쇼핑몰에서, 회원은 회원 시스템에서, 문의는 헬프데스크에서 생깁니다. 이것을 한곳(데이터 웨어하우스 · 검색 색인 · FAQ 저장소)에 모아 쓰려면 세 단계를 거칩니다. + +| 단계 | 영어 | 하는 일 | 예 | +| --- | --- | --- | --- | +| 꺼내기 | **E**xtract | 원래 있던 곳에서 데이터를 읽어 옴 | 콘텐츠 담당자가 보낸 FAQ 파일을 읽음 | +| 바꾸기 | **T**ransform | 쓸 모양으로 다듬고, **검사하고**, 필요한 칸을 더함 | 앞뒤 공백 정리, 규칙 검사, 나쁜 줄 걸러 내기 | +| 싣기 | **L**oad | 쓰는 곳에 넣음 | 검사를 통과한 FAQ만 저장소에 넣음 | + +세 글자를 따서 **ETL**이라고 부르고, 이렇게 데이터가 정해진 단계를 차례로 흘러가게 만든 것을 **데이터 파이프라인**(data pipeline)이라고 부릅니다. 품질 검사는 대개 바꾸기(T) 단계, 즉 **싣기 전에** 둡니다. 한번 실린 나쁜 데이터는 이미 누군가 읽고 쓰기 시작하기 때문입니다. 오늘 만드는 n8n 워크플로우도 작은 데이터 파이프라인입니다: 배치를 받고(E) → 검사하고(T) → 통과한 것만 넣습니다(L). ### 오늘 실습에서 @@ -74,17 +86,17 @@ B-1 13줄을 AGENTS.md 3절 규칙표를 들고 눈으로 읽으면 (Lab 01 예 > **생활 속 예 · 벽에 붙은 검수 기준표.** 주방장마다 보는 눈이 다르면 같은 상자가 어떤 날은 받히고 어떤 날은 반품됩니다. 그래서 식당은 "라벨 없는 고기는 반품, 냉장품은 5도 이하"처럼 기준표를 벽에 붙입니다. 사람이 바뀌어도 판정이 같아지는 것은 기준표 덕분입니다. -**검사 = 규칙 + 판정.** "나쁜 FAQ를 걸러 줘"라는 말만으로는 판정이 하나로 정해지지 않습니다. 77일차의 "지표는 정의가 먼저"와 같습니다. 규칙은 적어도 이것을 정해야 합니다. +**검사 = 규칙 + 판정.** "나쁜 데이터를 걸러 줘"라는 말만으로는 판정이 하나로 정해지지 않습니다. 사람마다 · 프로그램마다 "나쁨"을 다르게 읽기 때문입니다. 77일차에 "지표는 정의가 먼저"였던 것처럼, 검사는 **규칙이 먼저**입니다. 규칙은 적어도 다음을 말로 정해 둬야 합니다. | 정해야 할 것 | 예 | 정하지 않으면 | | --- | --- | --- | -| 무엇을 보나 | 제목 · 답 · 키워드 · id | 한두 칸만 봄 | -| 꼴 · 범위 | `FAQ-` + 숫자 3자리, 답 10~300자 | 사람마다 다름 | -| 경계 | 40자 **넘으면**, 공백은 빈 값 | 딱 기준선의 줄이 날마다 다르게 | -| 판정의 단위 | 줄마다 · 배치마다 | "B-1은 나쁘다"로 끝남 | -| 오류와 경고 | 막을 것 · 알릴 것 (6절) | 모든 것을 막거나 모두 통과 | +| 무엇을 보나 | 어느 칸을 검사하나 | 한두 칸만 보고 나머지는 지나감 | +| 형식 · 범위 | "번호는 영문 3자 + 숫자 3자", "설명은 10~300자" | 사람마다 다른 기준 | +| 경계 | "40자를 **넘으면**", "공백만 있으면 빈 값" | 딱 기준선의 값이 날마다 다르게 판정됨 (5절) | +| 판정의 단위 | 줄마다인가, 묶음 전체인가 | "이 파일은 나쁘다"로 끝나 아무도 못 고침 | +| 막을 것 · 알릴 것 | 오류와 경고 (6절) | 모든 것을 막거나 모든 것을 통과 | -규칙이 요청문에 없으면 에이전트도 자기 방식으로 판정합니다 — 오늘 팀의 기준 검사처럼요. +규칙이 말로 정해져 있지 않으면, 검사를 만드는 사람(또는 CLI 에이전트)이 **자기 방식으로** 판정합니다. 그 판정이 계산상 틀린 것은 아니어도, 팀이 원하는 판정과 다를 수 있습니다. ### 오늘 실습에서 @@ -104,13 +116,19 @@ B-1 13줄을 AGENTS.md 3절 규칙표를 들고 눈으로 읽으면 (Lab 01 예 > **생활 속 예 · 반품 사유 스티커.** 주방장은 반품 상자에 "온도" · "라벨"처럼 짧은 스티커를 붙입니다. 월말에 스티커를 세면 "이번 달 반품의 절반이 온도"가 보입니다. 납품업체에는 스티커를 풀어 쓴 사유서를 보냅니다. 짧은 표시와 긴 설명을 나눠 두는 것입니다. -**줄마다 이유를 코드로 붙입니다.** "B-1은 나쁘다"로는 아무도 고칠 수 없습니다. "FAQ-109는 `pii`"라고 해야 그 줄을 고칩니다. 코드는 짧고 바뀌지 않는 영어(`title_missing` · `pii`)로 정해 두고, 사람이 읽는 말은 따로 붙입니다 (7절). 코드가 같아야 배치마다 몇 번 나왔는지 세고(`codes`), 품질 보고서에서 "가장 흔한 문제"를 고를 수 있습니다 (3단계). +**오류 코드(error code)는 "무엇이 왜 잘못됐는지"를 짧은 이름 하나로 정해 둔 것입니다.** 검사 결과가 "나쁘다" 한 마디뿐이면 아무도 고칠 수 없습니다. "몇째 줄의 어느 칸이 어떤 규칙에 걸렸는지"가 있어야 고칩니다. 그 "어떤 규칙"을 날마다 다른 문장으로 쓰지 않고, 규칙마다 **이름 하나**를 정해 붙이는 것이 오류 코드입니다. -| 코드의 성질 | 까닭 | +우리가 이미 아는 예가 많습니다. 웹의 HTTP 상태 코드(404 = 없음, 500 = 서버 오류), 신용카드 결제 거절 코드, 자동차 계기판의 고장 코드가 모두 같은 생각입니다. 사람이 읽기에는 짧지만, 기계는 이 이름으로 **세고 · 비교하고 · 갈래를 나눕니다.** + +| 좋은 오류 코드의 성질 | 까닭 | | --- | --- | -| 짧고 정해진 이름 | 기계가 셈 · 비교 (`codes.pii`) | -| 한 줄에 여럿 | 제목도 비고 키워드도 비면 둘 다 | -| 판정과 따로 | `ok`는 `errors`가 비었을 때만 — 규칙을 더해도 판정 방식은 그대로 | +| 짧고 바뀌지 않는 이름 (보통 영어) | 프로그램이 이름으로 셈 · 비교. 문장은 고쳐 쓸 때마다 다른 값이 되어 셀 수 없음 | +| 규칙 하나에 이름 하나 | "가장 흔한 문제는 무엇인가"를 이름별로 셀 수 있음 | +| 한 줄에 여럿이 붙을 수 있음 | 이름도 비고 메일도 틀리면 둘 다 알려야 한 번에 고침 | +| 사람이 읽는 말과 나눔 | 코드는 기계용, 설명 문장은 사람용 (7절). 문장을 바꿔도 통계는 그대로 | +| 판정과 나눔 | "통과 = 붙은 코드가 하나도 없음"으로 정하면 규칙을 더해도 판정 방법은 그대로 | + +이름을 정할 때는 "무엇이(칸) · 어떻게(비어 있음 · 너무 김 · 형식이 잘못됨)"가 보이게 짓는 것이 흔합니다. 예를 들어 "제목이 비어 있음", "메일 형식이 잘못됨", "개인정보가 들어 있음" 같은 규칙마다 영어 이름을 하나씩 붙입니다. 이름이 정해지면 검사 결과를 다른 프로그램(가져오기 · 보고서 · 알림)이 그대로 이어 쓸 수 있습니다. ### 오늘 실습에서 @@ -122,14 +140,18 @@ B-1을 내 검사로 보면 `codes`에 여덟 가지가 1건씩입니다: `title > **생활 속 예 · 온도는 온도계로.** 냉장고 온도를 주방장의 손등으로 재지 않습니다. 온도계는 누가 들어도 같은 숫자를 줍니다. 대신 "이 소스, 맛이 맞나?"는 온도계로 잴 수 없어 사람이 맛봅니다. -**정해진 답이 있는 판정은 Code로.** 빈 값 · 길이 · 꼴(정규식) · 중복 · 전화번호 꼴은 같은 줄에 늘 같은 판정이 나와야 합니다. Code는 그렇게 하고, 빠르고(13줄 0.1초), 돈이 들지 않습니다. LLM에게 "이 FAQ 괜찮아?"를 물으면 그럴듯하지만, 날마다 판정이 다를 수 있고 줄마다 비용 · 시간이 듭니다 (75일차). LLM은 규칙으로 알 수 없는 것(답이 질문에 맞나)에만 씁니다 — 2단계에서. +**정답이 정해진 판정은 Code(프로그램)로 합니다.** 빈 값인가, 몇 글자인가, 정해진 형식(정규식)에 맞나, 같은 값이 두 번 나오나 — 이런 판정은 같은 데이터에 **언제나 같은** 답이 나와야 합니다. 프로그램은 그렇게 하고, 빠르고, 돈이 들지 않으며, 왜 그렇게 판정했는지 보여 줄 수 있습니다. -| 판정 | 누가 | -| --- | --- | -| 꼴 · 길이 · 빈 값 · 중복 · 전화 · 링크 | Code | -| 이미 있는 데이터와 겹침 | Code + 데이터 API (6절) | -| 뜻 (답이 질문에 맞나) | LLM (2단계) | -| 고치기 | 사람 | +같은 일을 LLM에게 "이 데이터 괜찮아?"라고 물으면 그럴듯한 답이 오지만, 같은 데이터에 날마다 다른 판정이 나올 수 있고 줄마다 시간과 비용이 듭니다 (75일차). LLM은 **규칙으로 쓸 수 없는 판정**(이 답이 질문에 맞는 내용인가)에만 씁니다 — 2단계. + +| 판정 | 누가 | 까닭 | +| --- | --- | --- | +| 빈 값 · 길이 · 형식 · 중복 · 전화번호 · 링크 | Code | 정답이 정해져 있음 | +| 이미 있는 데이터와 겹치나 | Code + 저장소 조회 (6절) | 저장소에 물어보면 정답이 나옴 | +| 뜻 (내용이 맞나) | LLM (2단계) | 정해진 규칙으로 쓰기 어려움 | +| 고치기 | 사람 | 무엇이 맞는지는 보낸 사람이 앎 | + +하나 더: 검사를 시작하기 **전에** 들어온 데이터의 모양부터 봅니다. 목록이 와야 할 자리에 글자가 오면 "보낸 쪽이 잘못 보냈다(400)"고 바로 돌려보내야 합니다. 그대로 검사 코드에 넣으면 코드가 터져 "서버가 고장 났다(500)"로 보입니다. ### 오늘 실습에서 @@ -141,14 +163,18 @@ B-1을 내 검사로 보면 `codes`에 여덟 가지가 1건씩입니다: `title > **생활 속 예 · 냉장고 온도 딱 5도.** 기준표에 "냉장품 5도 이하"라고 적혀 있습니다. 4도와 8도는 누구나 판정합니다. 하지만 딱 5도는? "이하"라는 한 글자가 정합니다. 온도계 없이 "차갑네"로 판정하면 5.1도도 받습니다. -**경계(edge)는 기준선 바로 위 · 아래 · 위에 있는 값입니다.** 검사의 실수는 대부분 경계에서 나옵니다. 보통 줄(빈 제목, 아주 긴 답)만 시험하면 "잘 된다"고 믿게 됩니다. 규칙에는 경계의 **말**이 들어가야 합니다. +**경계(edge, boundary)는 규칙의 기준선 바로 위 · 아래 · 딱 그 위치의 값입니다.** 검사의 실수는 대부분 경계에서 나옵니다. 아주 나쁜 데이터(텅 빈 값, 엄청 긴 글)만 시험하면 검사가 "잘 된다"고 믿게 되지만, 실제로 헷갈리는 것은 기준선 근처입니다. 소프트웨어 테스트에서는 이것을 **경계값 분석(boundary value analysis)**이라고 부르고, 기준선의 바로 앞 · 위 · 뒤 값을 일부러 시험합니다. + +경계를 정확히 판정하려면 규칙에 경계의 **말**이 들어가야 합니다. | 경계 | 정해야 할 말 | 말이 없으면 | | --- | --- | --- | -| 딱 기준 길이 | "넘으면" / "이상이면" | 40자 제목이 막히거나 41자가 들어감 | -| 공백만 있는 값 | "공백은 빈 값" | `" "`가 "있음"으로 통과 | -| 꼴의 변형 | "하이픈 · 점 · 공백이 없어도" | `01012345678`이 그대로 나감 | -| 비슷한 주소 | "호스트가 **정확히**" | `intra.example.com.evil.io`가 사내 주소로 (피싱이 쓰는 수법, 73일차) | +| 딱 기준 길이 | "넘으면"인가 "이상이면"인가 | 딱 40자인 값이 어떤 날은 막히고 어떤 날은 통과 | +| 공백만 있는 값 | "앞뒤 공백을 뺀 뒤 판정" | 띄어쓰기만 넣은 칸이 "값이 있음"으로 통과 | +| 형식의 변형 | "하이픈 · 점 · 띄어쓰기가 없어도 같은 것" | `010-1234-5678`은 잡고 `01012345678`은 놓침 | +| 비슷하게 생긴 값 | "**정확히** 이 주소" | `intra.example.com.evil.io`처럼 앞부분만 같은 피싱 주소가 사내 주소로 통과 (73일차) | + +경계를 시험 데이터로 만들어 두면, 규칙을 고칠 때마다 다시 돌려 경계가 그대로인지 확인할 수 있습니다. ### 오늘 실습에서 @@ -171,15 +197,17 @@ B-1을 내 검사로 보면 `codes`에 여덟 가지가 1건씩입니다: `title > **생활 속 예 · 냉장고를 먼저 연다.** 납품 상자만 보면 멀쩡한 우유입니다. 그런데 냉장고에 어제 받은 우유가 그대로라면 같은 칸에 넣으면 안 됩니다. 반면 "같은 회사 제품"인 것은 흔하고 괜찮습니다 — 알아만 두면 됩니다. -**들어오는 데이터만이 아니라 이미 있는 데이터와의 관계도 봅니다.** 데이터베이스에서는 참조 무결성(referential integrity) · 유일 제약(unique constraint)이라고 부릅니다. 같은 id로 들어오면 기존 데이터를 덮어쓰거나 두 벌이 되고, 같은 제목이 둘이면 검색이 어느 답을 줄지 모릅니다. +**검사는 들어오는 데이터만이 아니라, 이미 있는 데이터와의 관계도 봅니다.** 들어온 줄 하나만 보면 멀쩡해도, 저장소에 같은 번호가 이미 있으면 덮어쓰거나 두 벌이 됩니다. 같은 제목이 둘이면 검색이 어느 쪽을 보여 줄지 모릅니다. 데이터베이스에서는 이것을 **유일 제약**(unique constraint, 같은 값은 하나만)과 **참조 무결성**(referential integrity, 가리키는 대상이 실제로 있어야 함)이라고 부릅니다. -그리고 **막을 것과 알릴 것**을 나눕니다. 모든 것을 오류로 막으면 아무것도 들어가지 못하고, 모두 통과시키면 검사가 의미 없습니다. +그리고 **막을 것과 알릴 것을 나눕니다.** 모든 문제를 막으면 아무것도 들어가지 못하고, 모두 통과시키면 검사가 의미 없습니다. -| | 오류 (`errors`) | 경고 (`warnings`) | +| | 오류 (error) | 경고 (warning) | | --- | --- | --- | -| 뜻 | 들어가면 안 됨 | 사람이 보고 판단 | -| 판정 | `ok: false` | `ok`에 영향 없음 | -| 예 | 이미 있는 id · 같은 제목 | 이미 쓰는 키워드 | +| 뜻 | 들어가면 안 됨 | 들어가도 되지만 사람이 알아야 함 | +| 판정 | 그 줄은 통과하지 못함 | 통과 여부에 영향 없음 | +| 흔한 예 | 이미 있는 번호, 같은 제목 | 다른 글과 같은 키워드, 평소보다 긴 글 | + +무엇을 오류로, 무엇을 경고로 둘지도 규칙의 일부라 팀이 정해 적어 둡니다. ### 오늘 실습에서 @@ -191,11 +219,18 @@ B-1을 내 검사로 보면 `codes`에 여덟 가지가 1건씩입니다: `title > **생활 속 예 · 반품 사유서.** "안 받아요"만 말하면 납품업체는 무엇이 잘못됐는지 모릅니다. "3번 상자 · 라벨 없음, 7번 상자 · 냉장 온도 9도"라고 적어 주면 다음 납품이 나아집니다. -**검사의 결과는 기계와 사람이 둘 다 읽습니다.** 다음 워크플로우(가져오기)는 `rows` · `codes`를 읽고, 보낸 사람(콘텐츠 담당자)은 사람의 말을 읽습니다. 고칠 것이 코드마다 정해져 있으면 표 하나로 충분합니다 — LLM으로 만들면 같은 코드에 날마다 다른 말이 나오고 돈이 듭니다. +**검사 결과는 기계와 사람이 함께 읽습니다.** 다음 프로그램(가져오기 · 보고서)은 줄마다의 판정과 오류 코드를 읽고, 데이터를 보낸 사람은 "무엇을 어떻게 고치면 되는지"를 읽습니다. 오류 코드마다 고칠 말이 정해져 있으면 표 하나로 충분합니다 ("제목이 비어 있음 → 제목을 써 주세요"). 이것을 LLM으로 만들면 같은 문제에 날마다 다른 말이 나오고 비용이 듭니다. -업계에서는 보내는 쪽과 받는 쪽이 데이터의 모양 · 규칙을 미리 약속하는 것을 **데이터 계약**(data contract)이라고 부릅니다. 약속을 어긴 데이터는 받지 않고 **이유와 함께** 돌려보냅니다. 오늘의 AGENTS.md 3절이 작은 데이터 계약이고, 고침 목록이 돌려보내는 이유입니다. +업계에서는 보내는 쪽과 받는 쪽이 데이터의 모양과 규칙을 **미리 약속**하는 것을 **데이터 계약**(data contract)이라고 부릅니다. 약속을 어긴 데이터는 받지 않고, **이유와 함께** 돌려보냅니다. 돌려보낸 이유가 분명해야 보낸 사람이 배우고 다음 데이터가 나아집니다. -그리고 워크플로우는 **고쳐 주지 않습니다.** 빈 제목을 짐작해 채우면 틀린 FAQ가 "검사를 통과한 FAQ"로 들어가고, 아무도 그 사실을 모릅니다. +| 고침 목록에 들어갈 것 | 까닭 | +| --- | --- | +| 몇째 줄인지 (사람이 세는 번호, 1부터) | 보낸 사람이 자기 파일에서 바로 찾음 | +| 무엇이 잘못됐는지 (코드) | 기계가 셈 · 분류 | +| 어떻게 고치는지 (사람의 말) | 보낸 사람이 바로 고침 | +| 한 줄 요약 ("몇 줄 중 몇 줄 통과") | 한눈에 상황 파악 | + +그리고 워크플로우는 데이터를 **고쳐 주지 않습니다.** 빈 제목을 짐작해 채우면 틀린 내용이 "검사를 통과한 데이터"로 들어가고 아무도 그 사실을 모릅니다. 공백 정리처럼 뜻이 바뀌지 않는 **다듬기**와 내용을 바꾸는 **고치기**는 다릅니다. ### 오늘 실습에서 @@ -221,7 +256,7 @@ B-1을 내 검사로 보면 `codes`에 여덟 가지가 1건씩입니다: `title | "나쁜 FAQ 걸러 줘"만 씀 | 에이전트마다 다른 규칙 | 규칙 표를 요청문에 | | 공백을 값으로 봄 | `" "` 제목이 통과 | 앞뒤 공백을 뺀 뒤 판정 | | `>`와 `>=`를 섞음 | 딱 40자 제목이 막힘 | "넘으면"을 말로 | -| 전화번호를 `010-` 꼴만 찾음 | `01012345678`이 나감 | 구분자가 없어도 되는 꼴 | +| 전화번호를 `010-` 형식만 찾음 | `01012345678`이 나감 | 구분자가 없어도 되는 형식 | | 주소를 "앞이 같나"로 봄 | `evil.io`가 통과 | 호스트가 정확히 · 끝나는지까지 | | Code 안에서 본문 모양을 봄 | 500 | Code 앞에 IF → 400 | | 겹치는 키워드도 오류로 | 넣을 수 있는 FAQ가 없음 | 오류와 경고를 나눔 | diff --git a/Lecture/78/02-STAGE2-QUALITY-GATE.md b/Lecture/78/02-STAGE2-QUALITY-GATE.md index e4a64e4..459f116 100644 --- a/Lecture/78/02-STAGE2-QUALITY-GATE.md +++ b/Lecture/78/02-STAGE2-QUALITY-GATE.md @@ -38,15 +38,15 @@ flowchart LR > **생활 속 예 · 검수와 반품 결정.** 주방장이 "이 상자는 일곱 개가 상했네"라고 보는 것(검수)과, "그러니 트럭째 돌려보내자"고 정하는 것(결정)은 다른 일입니다. 같은 검수 결과로도 식당마다 결정이 다를 수 있습니다. -**검사(validation)는 판정을, 게이트(gate)는 그 판정으로 할 일을 정합니다.** 검사는 Lab 01에서 만들었습니다. 게이트는 그 결과를 읽어 넣을지 · 막을지 · 격리할지를 정합니다. 둘을 나누면 검사를 고칠 때 게이트를, 게이트 기준을 바꿀 때 검사를 건드리지 않습니다. 업계에서는 데이터 파이프라인의 **품질 관문**(quality gate) · 74일차 배포 관문(release gate)과 같은 모양입니다. +**검사(validation)는 판정을, 게이트(gate)는 그 판정으로 할 일을 정합니다.** 검사는 "이 줄은 무엇이 틀렸다"를 말하고, 게이트는 그 결과를 읽어 "넣을까 · 막을까 · 따로 둘까"를 정합니다. 둘을 나누면 검사 규칙을 고칠 때 게이트를, 게이트 기준을 바꿀 때 검사를 건드리지 않아도 됩니다. 데이터 파이프라인의 **품질 관문**(quality gate), 74일차 배포 관문(release gate)과 같은 모양입니다. -**규칙은 한 벌.** 게이트 안에 규칙을 다시 쓰면, 규칙을 고칠 때 두 곳을 고쳐야 하고 언젠가 어긋납니다. 77일차 요약이 보고서를 **불렀듯이**, 가져오기는 검사 웹훅을 부릅니다. +**규칙은 한 벌만 둡니다.** 게이트 안에 검사 규칙을 다시 쓰면, 규칙을 고칠 때 두 곳을 고쳐야 하고 언젠가 둘이 어긋납니다. 그래서 게이트는 규칙을 직접 갖지 않고 **검사를 불러** 그 결과만 씁니다 (77일차에 요약이 보고서를 불렀던 것과 같음). ```mermaid flowchart LR - W(["배치"]) --> C["Run check
POST my-faq-check"] --> G{"invalid / total
≤ 기준?"} - G -->|아니오| R(["422 · 아무것도 넣지 않음"]) - G -->|예| S["Split rows"] --> D["Store row
ok → /faq/add · 아니면 → /quarantine"] --> M(["imported · quarantined · score"]) + W(["들어온 묶음"]) --> C["검사 부르기
(줄마다 판정)"] --> G{"나쁜 줄 비율
≤ 기준?"} + G -->|아니오| R(["묶음 전체 거절
아무것도 넣지 않음"]) + G -->|예| S["줄마다"] --> D["좋은 줄 → 저장
나쁜 줄 → 격리함"] --> M(["넣은 수 · 격리한 수 · 점수"]) ``` ### 오늘 실습에서 @@ -67,16 +67,18 @@ flowchart LR > **생활 속 예 · 트럭째 반품과 보류 칸.** 상자 열에 일곱이 상했으면 하나씩 골라내느니 트럭째 돌려보냅니다 (다시 받는 게 빠름). 대부분 괜찮으면 좋은 것만 냉장고에 넣고, 이상한 것은 **보류 칸**에 사유 쪽지를 붙여 둡니다. 버리지 않습니다 — 업체가 바꿔 오면 확인해야 하니까요. -**게이트는 두 겹입니다.** +**게이트는 보통 두 겹입니다.** | 관문 | 기준 | 넘지 못하면 | | --- | --- | --- | -| 배치 게이트 | 나쁜 줄 비율 ≤ 기준 (오늘 기본 0.3, 같으면 통과) | 배치 전체 거절 — 422, 아무것도 넣지 않음 | -| 줄 게이트 | 그 줄의 `ok` | 그 줄만 격리함으로 | +| 묶음(배치) 게이트 | 나쁜 줄의 비율이 기준 이하인가 | 묶음 전체를 거절, 하나도 넣지 않음 | +| 줄 게이트 | 그 줄이 검사를 통과했나 | 그 줄만 격리함으로 | -**격리(quarantine)는 버리지 않는 것입니다.** 넣지 않은 줄을 원래 내용(`row`)과 이유(`errors`)와 함께 보관합니다. 버리면 무엇이 왜 빠졌는지 모르고, 고친 뒤 다시 넣을 수도 없습니다. 76일차의 DLQ(dead-letter queue, 실패한 요청을 두는 곳)와 같은 생각이고, 데이터 파이프라인에서는 quarantine table · error table이라고 부릅니다. +묶음 게이트가 있는 까닭: 나쁜 줄이 너무 많은 묶음은 보내는 쪽에서 무언가 크게 잘못된 것입니다 (파일을 잘못 고름, 열 순서가 바뀜). 좋은 줄 몇 개를 골라 넣는 것보다 통째로 돌려보내 고쳐 오게 하는 편이 빠르고 안전합니다. -**응답 코드.** 400은 "모양이 틀렸다"(읽을 수 없음), 422는 "읽었지만 받을 수 없다"(내용이 나쁨)입니다. 부른 쪽이 다음에 할 일이 다르기 때문에 나눕니다. +**격리(quarantine)는 버리지 않는 것입니다.** 넣지 않은 줄을 원래 내용과 이유와 함께 따로 보관합니다. 버리면 무엇이 왜 빠졌는지 모르고, 고친 뒤 다시 넣을 수도 없습니다. 76일차의 DLQ(dead-letter queue, 처리하지 못한 요청을 두는 곳)와 같은 생각이고, 데이터 파이프라인에서는 quarantine table · error table이라고 부릅니다. + +**응답 코드도 나눕니다.** 400은 "모양이 틀려 읽을 수 없다", 422는 "읽었지만 내용이 나빠 받을 수 없다"입니다. 부른 쪽이 다음에 할 일(보내는 방법을 고칠지, 데이터를 고칠지)이 다르기 때문입니다. ### 오늘 실습에서 @@ -95,15 +97,15 @@ flowchart LR > **생활 속 예 · 한 숟가락 맛보기.** 무게 · 온도 · 유통기한은 기준표로 잽니다. "주문한 맛이 맞나"는 주방장이 맛봅니다. 그런데 이미 상한 것까지 맛보지는 않습니다 — 기준표에서 떨어진 것은 맛볼 필요가 없습니다. -**규칙으로 알 수 없는 것만 LLM에게.** "답이 제목의 질문에 답하나?"는 정해진 답이 없는 판정이라 규칙(Code)으로 쓰기 어렵습니다. LLM은 이런 뜻의 판정을 잘합니다. 다만 세 가지를 지킵니다. +**규칙으로 알 수 없는 것만 LLM에게 묻습니다.** "이 답이 제목의 질문에 맞는 내용인가"처럼 뜻을 봐야 하는 판정은 정해진 규칙으로 쓰기 어렵습니다. LLM은 이런 판정을 잘합니다. 업계에서는 LLM에게 채점 · 판정을 맡기는 것을 **LLM-as-a-judge**라고 부르고, 콘텐츠 검수 · RAG 답 평가에 씁니다. 다만 세 가지를 지킵니다. | 원칙 | 까닭 | | --- | --- | -| **규칙을 통과한 줄에만** | 규칙에 걸린 줄은 이미 격리 — LLM에 물어도 결과가 같고 돈 · 시간만 듦 (75일차) | -| **JSON으로만 답하게** | `{"match": true/false, "reason"}` — Code가 읽음 | -| **읽을 수 없으면 넣지 않음** (fail-closed) | LLM이 멈추거나 이상한 말을 한 날 모든 줄이 검사 없이 들어가지 않게 (73일차) | +| **규칙을 통과한 줄에만** 묻는다 | 규칙에 걸린 줄은 이미 막힘 — LLM에 물어도 결과는 같고 돈 · 시간만 듦 (75일차) | +| **정해진 모양(JSON)으로만** 답하게 한다 | "맞음/틀림"과 이유를 프로그램이 읽을 수 있어야 다음 단계가 씀 | +| **답을 읽을 수 없으면 넣지 않는다** (fail-closed) | LLM이 멈추거나 엉뚱한 말을 한 날, 모든 줄이 검사 없이 들어가지 않게 (73일차) | -업계에서는 "LLM-as-a-judge"라고 부르고, RAG 품질 평가 · 콘텐츠 검수에 씁니다. 판정의 근거(`reason`)를 함께 남겨 사람이 다시 볼 수 있게 합니다. +판정과 함께 **이유**를 남겨 두면, 사람이 나중에 "왜 막혔지?"를 다시 볼 수 있습니다. LLM의 판정도 틀릴 수 있으므로 이유는 사람의 검토를 돕는 근거입니다. ### 오늘 실습에서 @@ -122,14 +124,16 @@ flowchart LR > **생활 속 예 · 반품 기준을 풀면, 조이면.** "열에 셋까지 골라 받는다"를 "열에 여덟까지"로 풀면 트럭을 돌려보내는 일은 줄지만 보류 칸이 넘치고 주방장이 하루 종일 골라냅니다. "하나도 안 된다"로 조이면 보류 칸은 비지만 거의 모든 트럭이 돌아가 재료가 모자랍니다. -**기준 하나가 두 실수를 맞바꿉니다.** 통계에서는 1종 오류(괜찮은 것을 막음)와 2종 오류(나쁜 것을 받음)라고 부릅니다. 기준은 **맞고 틀리는 값이 아니라 고르는 값**이고, 팀이 정해 적어 둡니다. 77일차 SLO 기준 · 이상 시간대 문턱과 같습니다. +**기준 하나가 두 가지 실수를 맞바꿉니다.** 기준을 느슨하게 하면 나쁜 데이터를 받는 실수가 늘고, 엄하게 하면 괜찮은 데이터를 돌려보내는 실수가 늘어납니다. 통계에서는 1종 오류(괜찮은 것을 막음)와 2종 오류(나쁜 것을 받음)라고 부릅니다. 둘을 모두 0으로 만드는 기준은 없습니다. -| 기준 | 잘못 받는 일 | 잘못 돌려보내는 일 | 누구의 일이 느나 | +그래서 기준은 **맞고 틀리는 값이 아니라 고르는 값**입니다. 무엇을 더 감당할 수 있는지 보고 팀이 정해 적어 둡니다 (77일차 SLO 기준 · 이상 시간대 문턱과 같음). + +| 기준 | 잘못 받는 일 | 잘못 돌려보내는 일 | 일이 느는 사람 | | --- | --- | --- | --- | -| 느슨하게 | 늘어남 | 줄어듦 | 격리함을 보는 사람 | -| 엄하게 | 줄어듦 | 늘어남 | 다시 보내는 사람, FAQ가 늦게 열림 | +| 느슨하게 | 늘어남 | 줄어듦 | 격리함을 살펴보는 사람 | +| 엄하게 | 줄어듦 | 늘어남 | 다시 보내야 하는 사람, 데이터가 늦게 반영됨 | -기준을 바꿀 때마다 워크플로우를 고치지 않으려면 기준을 **입력**으로 받습니다 (`max_invalid_ratio`, 없으면 0.3). +기준을 바꿀 때마다 프로그램을 고치지 않도록, 기준은 코드에 박지 않고 **입력으로 받고 기본값을 둡니다.** 그리고 경계의 말("같으면 통과")까지 함께 정합니다. ### 오늘 실습에서 @@ -151,9 +155,9 @@ flowchart LR > **생활 속 예 · 검수 일지와 사장님 호출.** 주방장은 상자마다 일지에 "몇 개 받음 · 몇 개 반품 · 이유"를 적습니다. 일지가 쌓여야 "요즘 그 업체가 나빠졌다"를 말할 수 있습니다. 트럭째 반품한 날은 사장님께 바로 전화합니다. -**품질은 한 번 재고 끝나지 않습니다 — 기록이 쌓여야 관리됩니다.** 배치마다 같은 모양으로 한 줄(배치 · 데이터셋 · 줄 수 · 통과 · 격리 · 거절 · 점수 · 코드)을 남기면 3단계에서 추세 · 가장 나쁜 배치 · 가장 흔한 문제를 셉니다. 거절한 배치도 **반드시** 기록합니다 — 빼면 가장 나쁜 배치가 보고서에서 사라집니다. +**품질은 한 번 재고 끝나지 않습니다 — 기록이 쌓여야 관리됩니다.** 묶음마다 **같은 모양으로** 한 줄(언제 · 어떤 데이터 · 몇 줄 · 통과 · 격리 · 거절 · 점수 · 오류 코드별 수)을 남기면, 나중에 "나아지고 있나 · 가장 나빴던 때는 · 가장 흔한 문제는"을 셀 수 있습니다 (3단계). 거절한 묶음도 **반드시** 기록합니다 — 빼면 가장 나쁜 묶음이 보고서에서 사라집니다. -**알림은 사람이 할 일이 있을 때만** (76일차). 거절(보낸 사람에게 알려야 함) · 낮은 점수(격리함을 봐야 함)일 때 보냅니다. 그리고 알림을 보내도 **부른 쪽이 받는 결과는 그대로**입니다 — 알림은 곁가지. +**알림은 사람이 할 일이 있을 때만** 보냅니다 (76일차 알림 피로). 묶음을 거절했을 때(보낸 사람에게 알려야 함), 점수가 기준보다 낮을 때(누군가 격리함을 봐야 함)처럼. 그리고 알림을 보내든 말든 **부른 쪽이 받는 결과는 같아야** 합니다 — 기록과 알림은 본래 일의 곁가지입니다. ### 오늘 실습에서 @@ -170,9 +174,11 @@ flowchart LR > **생활 속 예 · 맛보기 횟수와 맛보는 사람.** 사장님이 묻습니다: "오늘 맛보기를 몇 번 했지? 상한 것까지 맛본 건 아니지?" 그리고 "어제 맛본 사람과 오늘 맛본 사람의 판정이 같나?" -**LLM 비용은 호출 수로 말합니다.** 시간은 매번 다르지만(같은 9번이 7.6~9.4초) 호출 수는 정해집니다. 호출당 비용을 알면(75일차) 배치의 비용이 바로 나옵니다. +**LLM 비용은 호출 수로 말합니다.** 걸린 시간은 실행할 때마다 달라지지만, 몇 번 불렀는지는 정해집니다. 호출 한 번의 비용을 알면(75일차) 묶음 하나의 비용이 바로 나옵니다. 그래서 결과에 "LLM을 몇 번 불렀나"를 함께 남기면 비용을 숫자로 말할 수 있습니다. -**"늘 같은가"는 재 봐야 압니다.** temperature 0이어도 LLM 판정이 늘 같다는 보증은 없습니다. 같은 입력을 **같은 처음 상태**에서 여러 번 돌려 판정이 같은지 봅니다 (72일차 반복 시험). 그리고 같은 배치를 처음 상태로 되돌리지 않고 다시 보내면 **데이터가 바뀐 것**이지 판정이 흔들린 것이 아닙니다 — 둘을 구분해야 합니다. +**"늘 같은가"는 재 봐야 압니다.** temperature를 0으로 두어도 LLM의 판정이 언제나 같다는 보증은 없습니다 (74일차 흔들림). 같은 입력을 **같은 처음 상태**에서 여러 번 돌려 판정이 같은지 봅니다 (72일차 반복 시험). + +여기서 흔한 함정이 하나 있습니다. 처음 상태로 되돌리지 않고 같은 데이터를 다시 보내면, 두 번째에는 "이미 있는 데이터"가 되어 다른 판정이 나옵니다. 이것은 **데이터가 바뀐 것**이지 LLM이 흔들린 것이 아닙니다. 반복 시험에서는 둘을 구분해야 합니다. ### 오늘 실습에서 diff --git a/Lecture/78/03-STAGE3-QUALITY-MANAGEMENT.md b/Lecture/78/03-STAGE3-QUALITY-MANAGEMENT.md index 419ba65..d8161e6 100644 --- a/Lecture/78/03-STAGE3-QUALITY-MANAGEMENT.md +++ b/Lecture/78/03-STAGE3-QUALITY-MANAGEMENT.md @@ -38,16 +38,16 @@ flowchart LR > **생활 속 예 · 품목마다 다른 검수 카드.** 채소는 무른 곳, 고기는 온도와 라벨, 생선은 눈 색을 봅니다. 품목마다 주방장을 따로 두지 않고 품목마다 **검수 카드**를 만들어 두면, 한 사람이 카드를 바꿔 들고 무엇이든 검수합니다. 새 품목이 오면 카드를 한 장 더 만듭니다. -**스키마(schema)는 데이터의 모양을 적은 데이터입니다.** 어떤 칸이 있고, 칸마다 타입은 무엇이고, 꼭 있어야 하는지, 범위는 어떤지. 검사 코드는 칸 이름을 모르고 **타입마다** 판정합니다: "이 칸은 `email`이니 메일 꼴인지", "이 칸은 `enum`이니 목록 안에 있는지". 그러면 데이터가 늘어도 검사 코드는 그대로이고 스키마만 늘어납니다. +**스키마(schema)는 데이터의 모양을 적은 데이터입니다.** 어떤 칸이 있고, 칸마다 타입(글자 · 숫자 · 날짜 · 메일 · 목록 중 하나)은 무엇이고, 꼭 있어야 하는지, 길이나 범위는 어떤지를 적습니다. 검사 프로그램은 칸 이름을 미리 알 필요가 없고 **타입마다** 판정합니다: "이 칸의 타입은 메일이니 메일 형식인지", "이 칸은 정해진 목록이니 목록 안의 값인지". 그러면 데이터 종류가 늘어도 검사 프로그램은 그대로이고 스키마만 늘어납니다. | 업계에서 | 같은 생각 | | --- | --- | | JSON Schema · OpenAPI | API 요청 · 응답의 모양 | | 데이터베이스 테이블 정의 | `NOT NULL` · `CHECK` · `UNIQUE` | | Great Expectations · Soda · dbt tests | 데이터셋마다 "기대값" 목록 | -| pydantic · zod | 코드 안의 모델 (스키마를 코드로 씀) | +| pydantic · zod | 프로그램 안의 데이터 모델 (스키마를 코드로 씀) | -**CSV.** 회사 데이터는 CSV로 많이 옵니다. 첫 줄이 열 이름이고, 값은 모두 글자입니다. 그래서 날짜 · 메일도 "글자의 꼴"로 판정하고, 줄 번호는 사람이 세는 번호(열 이름 줄을 빼고 1부터)로 돌려줍니다. +**CSV.** 회사 데이터는 표 파일(CSV)로 많이 옵니다. 첫 줄이 열 이름이고, 값은 모두 **글자**입니다. 그래서 날짜 · 메일도 "글자의 형식"으로 판정하고, 오류를 알려 줄 때 줄 번호는 사람이 세는 번호(열 이름 줄을 빼고 1부터)로 돌려줍니다. ### 오늘 실습에서 @@ -69,14 +69,14 @@ Lab 01의 FAQ 검사에 인사팀의 직원 CSV를 보내면 400 (`items`가 없 > **생활 속 예 · 검수 카드에 없는 칸.** 카드에는 무게 · 온도 · 라벨 칸이 있습니다. 하지만 "이 업체와 이미 계약한 품목인가"는 장부를 봐야 알고, "맛이 맞나"는 맛봐야 압니다. 카드가 할 일과 다른 사람이 할 일이 나뉩니다. -**스키마는 모양을 넓게, Code · LLM은 그 데이터만의 규칙을 깊게.** 스키마로 거의 모든 데이터의 기본 모양(필수 · 길이 · 꼴 · 목록 · 날짜 · 중복)을 봅니다. 그러나 데이터마다 다른 규칙 — 답 안의 개인정보, 이미 있는 데이터와의 관계, 뜻 — 은 스키마의 타입으로 적기 어렵습니다. 둘 중 하나를 고르는 것이 아니라 층을 나눕니다. +**스키마는 모양을 넓게, 프로그램 · LLM은 그 데이터만의 규칙을 깊게 봅니다.** 스키마로는 거의 모든 데이터의 기본 모양(필수 · 길이 · 형식 · 목록 · 날짜 · 중복)을 볼 수 있습니다. 그러나 데이터마다 다른 규칙 — 글 안에 개인정보가 섞였나, 이미 있는 데이터와 겹치나, 내용이 맞나 — 은 스키마의 타입으로 적기 어렵습니다. 그래서 둘 중 하나를 고르는 것이 아니라 **층을 나눕니다.** -| 층 | 무엇 | 오늘 | +| 층 | 무엇을 보나 | 어디에 둠 | | --- | --- | --- | -| 스키마 | 모양 | `my-check` (모든 데이터셋) | -| 데이터별 Code | 내용 규칙 · 이미 있는 데이터 | `my-faq-check` (FAQ만) | -| LLM | 뜻 | `Judge` (FAQ 가져오기) | -| 사람 | 고치기 · 기준 정하기 | 콘텐츠 담당자 · 팀 | +| 스키마 검사 | 모양 (필수 · 길이 · 형식 · 목록 · 중복) | 모든 데이터셋이 함께 쓰는 검사 하나 | +| 데이터별 규칙 (Code) | 그 데이터만의 내용 규칙, 이미 있는 데이터와의 관계 | 그 데이터의 검사 | +| LLM | 뜻 (내용이 맞나) | 규칙을 통과한 줄에만 | +| 사람 | 고치기, 기준 정하기 | 보낸 사람 · 팀 | ### 오늘 실습에서 @@ -88,11 +88,13 @@ FAQ를 `my-check`(`schema: faq`)로만 보면 B-3에서 Lab 01의 검사와 판 > **생활 속 예 · 보류 칸의 쪽지 떼기.** 업체가 라벨을 붙여 다시 가져온 고기를 냉장고에 넣었습니다. 그런데 보류 칸의 옛 상자에 "라벨 없음" 쪽지가 그대로라면, 다음 날 아침 주방장은 "아직 해결 안 됐네" 하고 또 전화합니다. -**격리함은 "사람이 볼 일"의 목록입니다.** 해결된 줄이 남아 있으면 목록을 믿을 수 없습니다. 그래서 고쳐서 다시 넣은 줄은 격리함에서 뺍니다. 다만 **넣은 줄만** 뺍니다 — 고쳤는데 또 틀린 줄은 다시 사람에게 가야 합니다. 이 고리(격리 → 사람이 고침 → 다시 넣음 → 격리함 비우기)가 품질 관리의 한 바퀴이고, 76일차 DLQ의 replay와 같은 모양입니다. +**격리함은 "사람이 볼 일"의 목록입니다.** 이미 해결된 줄이 그대로 남아 있으면 목록을 믿을 수 없고, 사람은 같은 일을 또 봅니다. 그래서 고쳐서 다시 넣은 줄은 격리함에서 뺍니다. 다만 **실제로 들어간 줄만** 뺍니다 — 고쳤는데 또 틀린 줄은 다시 사람에게 가야 합니다. + +이 고리(격리 → 사람이 고침 → 다시 넣음 → 격리함에서 빼기)가 품질 관리의 한 바퀴입니다. 76일차 DLQ의 재처리(replay)와 같은 모양이고, 고리가 끊기면(빼지 않음 · 다시 넣지 않음) 격리함은 쌓이기만 합니다. ```mermaid flowchart LR - Q[("격리함")] -.->|사람이 고침| F(["고친 배치"]) --> I["가져오기
검사 · LLM · 넣기"] --> X["넣은 id만 뺌"] --> Q + Q[("격리함")] -.->|사람이 고침| F(["고친 묶음"]) --> I["가져오기
검사 · LLM · 넣기"] --> X["들어간 줄만 뺌"] --> Q I --> L["품질 기록"] ``` @@ -112,7 +114,9 @@ flowchart LR > **생활 속 예 · 생크림 카드 한 장.** 식당이 디저트를 시작해 생크림이 들어옵니다. 주방장을 새로 뽑지 않고 "생크림 카드"를 한 장 더 만들면 내일부터 같은 사람이 검수합니다. 단, 카드에 "냉장 3도 이하"를 잘못 "30도"로 쓰면 그날부터 상한 크림이 들어옵니다. -**규칙을 데이터로 두면, 새 데이터셋은 설정 한 장입니다** (configuration over code). 워크플로우를 고쳐 push하지 않고 스키마 파일만 더합니다. 그만큼 **스키마 변경 = 규칙 변경**입니다. 스키마 파일도 저장소에 두고 리뷰 · 테스트를 거쳐 바꿉니다 (74일차 배포 관문과 같은 생각). 업계에서는 스키마 레지스트리(schema registry)로 버전을 관리합니다. +**규칙을 데이터로 두면, 새 데이터셋은 설정 한 장입니다** (configuration over code). 새 종류의 데이터가 생겨도 프로그램을 고쳐 다시 배포하지 않고, 스키마 파일 한 장만 더합니다. 70일차의 설정 표와 같은 생각입니다. + +그만큼 **스키마를 바꾸는 것은 규칙을 바꾸는 것**입니다. 한 글자 실수("3"을 "30"으로)가 그날부터 나쁜 데이터를 통과시킵니다. 그래서 스키마 파일도 프로그램처럼 저장소에 두고, 리뷰와 테스트를 거쳐 바꿉니다 (74일차 배포 관문과 같은 생각). 업계에서는 스키마 레지스트리(schema registry)로 스키마의 버전을 관리하고, 바뀐 스키마가 예전 데이터와 맞는지 확인합니다. ### 오늘 실습에서 @@ -123,7 +127,7 @@ flowchart LR | 통과 | **0줄** — 칸이 달라 모두 `id_format` · `answer_missing` | **2줄** | | 이유 | 9줄 모두 같은 세 코드 + 빈 id 중복 8 | `T-12` · `urgent` · **2026-02-30** · 3자 제목 · `010-12-3456` · 중복 · 요청자 빈칸 — 줄마다 다름 | -- 선택 칸 `contact`가 없는 9째 줄은 통과, 있는데 꼴이 틀린 6째 줄은 `bad_format:contact`. "없어도 된다"와 "아무거나 돼도 된다"는 다릅니다. +- 선택 칸 `contact`가 없는 9째 줄은 통과, 있는데 형식이 잘못된 6째 줄은 `bad_format:contact`. "없어도 된다"와 "아무거나 돼도 된다"는 다릅니다. ## 5. 품질 보고서: 품질도 정의가 먼저 (6교시 Lab 03 과제 1) @@ -131,16 +135,16 @@ flowchart LR > **생활 속 예 · 월말 검수 일지 정리.** 사장님이 묻습니다: "이번 달 납품은 어땠어? 제일 나빴던 날은? 반품 이유는 뭐가 제일 많았어? 나아지고 있어?" 일지를 넘기며 말로 답하면 사람마다 답이 다릅니다. -**품질 지표도 77일차 지표처럼 정의가 먼저입니다.** 업계에서는 데이터 품질 점수 · 데이터 품질 대시보드라고 부르고, 데이터 관측(data observability) 도구(Monte Carlo · Bigeye 등)가 같은 일을 합니다. 정해야 할 것은 이렇습니다. +**품질 지표도 77일차 지표처럼 정의가 먼저입니다.** "품질이 좋아졌다"를 말하려면, 무엇을 어떻게 세는지가 먼저 정해져야 합니다. 업계에서는 데이터 품질 점수 · 데이터 품질 대시보드라고 부르고, 데이터 관측(data observability) 도구(Monte Carlo · Bigeye 등)가 같은 일을 합니다. 정해야 할 것은 이렇습니다. -| 정해야 할 것 | 오늘의 정의 (AGENTS.md 3절) | 정하지 않으면 | +| 정해야 할 것 | 질문 | 정하지 않으면 | | --- | --- | --- | -| 거절을 평균에 넣나 | 넣음 (보내온 배치의 품질) | 0.6583과 0.8778 중 무엇인지 모름 | -| 같으면 무엇을 | 먼저 나온 배치 · 코드 글자순 | 실행마다 순서가 바뀜 | -| 추세 | 마지막 − 첫, ±0.05 | "좋아진 것 같다" | -| 0건 | 평균 0, `null` 아님 | 다음 계산이 깨짐 | +| 거절한 묶음을 평균에 넣나 | 받은 데이터의 품질인가, 보내온 데이터의 품질인가 | 같은 기록에서 서로 다른 평균이 나옴 | +| 같은 값이면 무엇을 먼저 | "가장 나쁜 묶음"이 둘이면? | 실행할 때마다 순서가 바뀜 | +| 추세 | 무엇과 무엇을 비교하나, 얼마나 바뀌어야 "좋아짐"인가 | "좋아진 것 같다" | +| 기록이 0건이면 | 평균은 0인가, "모름"인가 | 다음 계산이 깨짐 | -숫자는 Code가 세고, 그 숫자를 **무엇과 무엇을 비교했는지와 함께** 말하는 것은 사람입니다. +숫자는 프로그램이 세고, 그 숫자를 **무엇과 무엇을 비교했는지와 함께** 말하는 것은 사람의 일입니다. ### 오늘 실습에서 @@ -161,9 +165,11 @@ flowchart LR > **생활 속 예 · 업체별 성적표.** 한 달 평균만 보면 "괜찮다"인데 업체별로 나누면 채소 가게는 늘 좋고 생선 가게만 절반이 반품입니다. 사장님이 전화할 곳은 생선 가게입니다. -**같은 기록 모양을 쓰면 다른 워크플로우의 품질도 한 보고서에서 봅니다.** 검사(스키마 검사)도 가져오기와 같은 모양으로 기록을 남기면, 보고서는 고치지 않고도 직원 · 접수 건 데이터셋을 셉니다. 그리고 전체 평균은 나눠 봐야 할 것을 가립니다 — 77일차에 두 주 오류율이 월요일 09시를 가렸듯이. +**같은 기록 모양을 쓰면 여러 데이터의 품질을 한 보고서에서 봅니다.** 검사하는 곳이 여럿이어도(가져오기 · 스키마 검사 · 다른 팀의 검사) 같은 모양으로 기록을 남기면, 보고서는 고치지 않고도 데이터셋마다 나눠 셀 수 있습니다. -**추세는 같은 데이터셋 안에서.** 서로 다른 데이터셋(직원 → FAQ)을 이어 "좋아졌다"고 말하면 뜻이 없습니다. +그리고 **전체 평균은 나눠 봐야 할 것을 가립니다.** 평균이 괜찮아도 데이터셋 하나만 유난히 나쁠 수 있습니다 — 77일차에 두 주 오류율이 월요일 아침 한 시간을 가렸던 것과 같습니다. 통계에서는 이것을 집계가 가리는 문제(심하면 심슨의 역설)라고 부릅니다. 그래서 "어디를 먼저 손볼까"는 나눠서 봅니다. + +**추세는 같은 데이터셋 안에서만** 말합니다. 서로 다른 데이터셋을 이어 놓고(직원 목록 다음에 FAQ) "좋아졌다"고 하면 뜻이 없습니다. ### 오늘 실습에서 @@ -200,7 +206,7 @@ flowchart LR | 실수 | 결과 | 바로잡기 | | --- | --- | --- | | 필드 이름을 검사 코드에 씀 | 새 데이터셋마다 워크플로우 수정 | 스키마의 `fields`를 돌며 타입마다 | -| 날짜를 꼴(`YYYY-MM-DD`)만 봄 | 13월 · 2월 30일이 통과 | 실제 있는 날인지 | +| 날짜를 형식(`YYYY-MM-DD`)만 봄 | 13월 · 2월 30일이 통과 | 실제 있는 날인지 | | 빈 선택 칸에 형식 검사 | 멀쩡한 줄이 `bad_format` | 비었으면 `required`만 | | 스키마만으로 FAQ를 검사 | 메일 주소 FAQ가 통과 | 내용 규칙은 Code로 따로 | | 고친 줄을 넣고 격리함을 그대로 | 해결된 일이 "할 일"로 남음 | 넣은 id만 뺌 | @@ -212,7 +218,7 @@ flowchart LR
1. 스키마 검사를 만들었으니 Lab 01의 FAQ 검사는 지워도 되나요? -아닙니다. 스키마는 모양(필수 · 길이 · 꼴 · 중복)을 보고, FAQ 검사는 FAQ만의 내용 규칙(답 안의 개인정보 · 바깥 링크)과 이미 있는 FAQ와의 관계를 봅니다. B-2를 스키마로만 보면 메일 주소 FAQ-209가 통과합니다. 모양은 스키마로 넓게, 내용은 Code로 깊게. +아닙니다. 스키마는 모양(필수 · 길이 · 형식 · 중복)을 보고, FAQ 검사는 FAQ만의 내용 규칙(답 안의 개인정보 · 바깥 링크)과 이미 있는 FAQ와의 관계를 봅니다. B-2를 스키마로만 보면 메일 주소 FAQ-209가 통과합니다. 모양은 스키마로 넓게, 내용은 Code로 깊게.
2. 고친 배치를 다시 넣었는데 격리함에서 빼지 않으면 무엇이 문제인가요? 넣기는 됐잖아요. diff --git a/Lecture/78/04-STAGE4-WRAPUP.md b/Lecture/78/04-STAGE4-WRAPUP.md index 8adfb57..6212148 100644 --- a/Lecture/78/04-STAGE4-WRAPUP.md +++ b/Lecture/78/04-STAGE4-WRAPUP.md @@ -117,7 +117,7 @@ flowchart LR 오늘의 AGENTS.md 3절이 데이터 계약서였습니다. 최종 프로젝트에서도 데이터마다 이 틀로 한 장을 씁니다. -| 칸 | 타입 · 꼴 | 필수 | 규칙 (코드) | 위반이면 | +| 칸 | 타입 · 형식 | 필수 | 규칙 (코드) | 위반이면 | | --- | --- | --- | --- | --- | | `id` | `pattern ^FAQ-\d{3}$` | ✔ | `bad_format:id`, 중복 `duplicate_in_batch`, 이미 있음 `exists_id` | 격리 | | `title` | `string` ≤ 40 | ✔ | `missing:title` · `too_long:title`, 같은 제목 `same_title` | 격리 | @@ -132,7 +132,7 @@ flowchart LR | 하기 쉬운 일 | 왜 위험한가 | 대신 | | --- | --- | --- | | 규칙 없이 "나쁜 것 걸러 줘" | 판정이 사람 · 에이전트마다 다름 | 규칙 표 · 코드 | -| 경계의 말을 안 정함 | 딱 기준선의 줄이 날마다 다르게 | 넘으면 · 공백 · 꼴 · 정확히 | +| 경계의 말을 안 정함 | 딱 기준선의 줄이 날마다 다르게 | 넘으면 · 공백 · 형식 · 정확히 | | 나쁜 데이터를 고쳐서 넣음 | 틀린 것이 "통과"로, 원인이 숨음 | 막고 알리고, 사람이 고침 | | 나쁜 줄을 버림 | 무엇이 빠졌는지 모름 | 격리 + 이유 | | 모든 것을 LLM에게 판정 | 늘 같지 않고 비쌈 | 규칙은 Code, 뜻만 LLM, 통과한 줄에만 | @@ -178,7 +178,7 @@ flowchart LR - [ ] 데이터마다 계약서 한 장 (6절 틀): 칸 · 타입 · 필수 · 규칙 코드 · 위반이면 - [ ] 검사 웹훅 하나 (규칙은 한 곳), 줄마다 코드, 목록이 아니면 400 -- [ ] 경계 테스트 (딱 기준선 · 공백 · 꼴의 변형 · 비슷한 값) +- [ ] 경계 테스트 (딱 기준선 · 공백 · 형식의 변형 · 비슷한 값) - [ ] 가져오기 게이트: 배치 거절(기준은 입력으로) · 줄 격리 + 이유, LLM은 통과한 줄에만 · fail-closed - [ ] 고침 목록을 보낸 사람에게, 고쳐 돌아오면 격리함 비우기 - [ ] 스키마로 모양을 넓게, 데이터만의 규칙은 Code로 diff --git a/Lecture/78/05-VIBE-CODING.md b/Lecture/78/05-VIBE-CODING.md index 09ab6fd..a77869e 100644 --- a/Lecture/78/05-VIBE-CODING.md +++ b/Lecture/78/05-VIBE-CODING.md @@ -56,7 +56,7 @@ flowchart LR | 고침 목록 | Lab 01 과제 2 | `--fix` (R9) | | 품질 기록 | Lab 03 과제 2 | `--log` (R10) | -- 풀이 거리: 강사 본은 직원 HR-1 · 접수 건 TK-1 · FAQ B-3에서 `rows` · `codes` · `valid`가 `my-check`와 **하나도 다르지 않았습니다.** Python `re`의 `\w`는 한글도 맞아 메일 판정이 JavaScript와 다를 수 있었고(`re.ASCII`), 날짜를 꼴만 보면 2월 30일이 통과합니다. "같은 규칙을 두 곳에 쓰면 반드시 어긋난다 — 그래서 n8n 안에서는 검사를 다시 부르고, 밖에서는 테스트로 같은 판정을 확인한다." +- 풀이 거리: 강사 본은 직원 HR-1 · 접수 건 TK-1 · FAQ B-3에서 `rows` · `codes` · `valid`가 `my-check`와 **하나도 다르지 않았습니다.** Python `re`의 `\w`는 한글도 맞아 메일 판정이 JavaScript와 다를 수 있었고(`re.ASCII`), 날짜를 형식만 보면 2월 30일이 통과합니다. "같은 규칙을 두 곳에 쓰면 반드시 어긋난다 — 그래서 n8n 안에서는 검사를 다시 부르고, 밖에서는 테스트로 같은 판정을 확인한다." - 같은 FAQ B-1을 `tcheck --schema faq --content answer`로 보면 Lab 01 예제 2의 8줄과 같은 줄이 나쁩니다 — 코드 이름만 다릅니다 (`title_missing` ↔ `missing:title`). ## 4. 강사 바이브 코딩 (시간이 남으면 · 수업 뒤, 15분)