[{"data":1,"prerenderedAt":878},["ShallowReactive",2],{"post-document:\u002Fdevops\u002Fagile\u002F2026\u002F08\u002F26\u002F과제\u002F":3},{"id":4,"title":5,"body":6,"categories":863,"date":866,"description":79,"extension":867,"image":868,"key_concepts":868,"last_modified_at":868,"legacyPath":869,"meta":870,"navigation":872,"part":868,"path":873,"published":872,"robots":868,"seo":874,"series":868,"stem":875,"strengths":868,"summary":868,"tags":876,"tradeoffs":868,"__hash__":877},"posts\u002Fposts\u002FDevOps\u002FAgile\u002F과제.md","실습에서는?",{"type":7,"value":8,"toc":831},"minimark",[9,13,24,43,48,72,76,80,86,90,95,102,105,111,114,118,135,195,199,203,209,250,255,258,262,267,270,273,276,280,291,295,308,312,315,367,373,393,396,410,457,480,486,490,496,499,503,509,512,517,520,524,530,536,539,543,546,550,555,559,563,567,595,598,602,608,612,670,673,677,683,686,690,728,731,803,807,827],[10,11,5],"h1",{"id":12},"실습에서는",[14,15,21],"pre",{"className":16,"code":18,"language":19,"meta":20},[17],"language-text","문제 정의\n    ↓\n서비스 기획\n    ↓\nSprint로 우선순위 결정\n    ↓\nAPI를 연결해 실제 동작 증명\n","text","",[22,23,18],"code",{"__ignoreMap":20},[25,26,27,31,34,37,40],"ol",{},[28,29,30],"li",{},"누구의 문제를 해결하는가?",[28,32,33],{},"AI가 그 문제를 어떻게 해결하는가?",[28,35,36],{},"여러 기능 중 무엇을 먼저 만들 것인가?",[28,38,39],{},"그 기능을 어떤 Microservice\u002FAPI가 담당하는가?",[28,41,42],{},"진짜 호출해서 동작하는가?",[44,45,47],"h2",{"id":46},"하면-안-되는-것","하면 안 되는 것",[49,50,51,54,57,60,63,66,69],"ul",{},[28,52,53],{},"Eureka 내부 코드 분석",[28,55,56],{},"OAuth2 내부 구현 분석",[28,58,59],{},"JWT 서명 알고리즘 분석",[28,61,62],{},"JPA Mapping 전체 분석",[28,64,65],{},"Kafka Producer\u002FConsumer 코드 전체 분석",[28,67,68],{},"Spring 코드 한 줄씩 읽기",[28,70,71],{},"화려한 프론트엔드 디자인",[44,73,75],{"id":74},"진정으로-해야-하는-것","진정으로 해야 하는 것",[77,78,79],"p",{},"MSA는 정답이 아니다.",[81,82,83],"blockquote",{},[77,84,85],{},"신청 → 이벤트 발생 → 결제 → 상태 변경이라는 결과 흐름",[44,87,89],{"id":88},"step-1-우리-팀-서비스의-고객을-하나-정하기","STEP 1. 우리 팀 서비스의 고객을 하나 정하기",[91,92,94],"h3",{"id":93},"pain-point-정의","Pain Point 정의",[77,96,97,101],{},[98,99,100],"strong",{},"이해관계자:"," 취업 교육을 제공하는 기업",[77,103,104],{},"기업은 교육생마다 어떤 강의를 추천해야 하는지 판단하기 어렵다.",[14,106,109],{"className":107,"code":108,"language":19,"meta":20},[17],"교육생의 기존 수강 기록\n교육생의 관심 직무\n교육생의 학습 수준\n    ↓\nAI 분석\n    ↓\n다음 강의 추천\n",[22,110,108],{"__ignoreMap":20},[77,112,113],{},"문서에서도 발표의 첫 번째 항목을 이해관계자의 Pain Point, 두 번째를 이를 해결하는 AI Solution",[44,115,117],{"id":116},"step-2-기존-교육-플랫폼을-우리-도메인으로-치환","STEP 2. 기존 교육 플랫폼을 우리 도메인으로 치환",[49,119,120,123,126,129,132],{},[28,121,122],{},"강사",[28,124,125],{},"과목",[28,127,128],{},"수강신청",[28,130,131],{},"결제",[28,133,134],{},"추천",[136,137,138,151],"table",{},[139,140,141],"thead",{},[142,143,144,148],"tr",{},[145,146,147],"th",{},"기존 Template",[145,149,150],{},"우리 서비스",[152,153,154,163,171,179,187],"tbody",{},[142,155,156,160],{},[157,158,159],"td",{},"user",[157,161,162],{},"구직자",[142,164,165,168],{},[157,166,167],{},"course",[157,169,170],{},"채용공고",[142,172,173,176],{},[157,174,175],{},"enrollment",[157,177,178],{},"지원",[142,180,181,184],{},[157,182,183],{},"payment",[157,185,186],{},"기업의 지원 승인",[142,188,189,192],{},[157,190,191],{},"recommend",[157,193,194],{},"AI 채용공고 추천",[44,196,198],{"id":197},"step-3-sprint-1을-딱-하나의-사용자-흐름으로-잡기","STEP 3. Sprint 1을 딱 하나의 사용자 흐름으로 잡기",[91,200,202],{"id":201},"walking-skeleton","Walking Skeleton",[14,204,207],{"className":205,"code":206,"language":19,"meta":20},[17],"사용자 로그인\n    ↓\n상품 조회\n    ↓\n상품 선택\n    ↓\n신청\n",[22,208,206],{"__ignoreMap":20},[136,210,211,222],{},[139,212,213],{},[142,214,215,218],{},[145,216,217],{},"기능",[145,219,221],{"align":220},"right","비중",[152,223,224,231,237,244],{},[142,225,226,228],{},[157,227,159],{},[157,229,230],{"align":220},"30%",[142,232,233,235],{},[157,234,167],{},[157,236,230],{"align":220},[142,238,239,241],{},[157,240,183],{},[157,242,243],{"align":220},"20%",[142,245,246,248],{},[157,247,191],{},[157,249,243],{"align":220},[81,251,252],{},[77,253,254],{},"로그인 → 상품 조회 → 신청이 100% 동작",[77,256,257],{},"Sprint 1에서는 회원, 과목, 수강신청으로 최소 기능 MVP를 완성하고 Sprint 2에서 결제와 이벤트 연동을 확장",[44,259,261],{"id":260},"왜-sprint를-이렇게-나누는가","왜 Sprint를 이렇게 나누는가?",[81,263,264],{},[77,265,266],{},"이 기능이 없으면 사용자에게 가치가 전달되지 않는가?",[77,268,269],{},"YES라면 Sprint 1.",[77,271,272],{},"없어도 일단 서비스가 돌아가면 Sprint 2.",[77,274,275],{},"자료에서도 정확히 이 기준을 사용. 회원가입, 상품, 신청 같은 필수 기능은 Sprint 1에 두고, 결제 자동화와 이벤트 연동 같은 확장 기능은 Sprint 2로 미룬다",[91,277,279],{"id":278},"sprint-1","Sprint 1",[49,281,282,285,288],{},[28,283,284],{},"로그인",[28,286,287],{},"상품 조회",[28,289,290],{},"신청",[91,292,294],{"id":293},"sprint-2","Sprint 2",[49,296,297,299,302,305],{},[28,298,131],{},[28,300,301],{},"Kafka 이벤트",[28,303,304],{},"상태 자동 변경",[28,306,307],{},"AI 추천",[44,309,311],{"id":310},"step-4-그다음-swagger를-본다","STEP 4. 그다음 Swagger를 본다",[77,313,314],{},"우리 Flow에서 어떤 API가 필요한가?",[136,316,317,330],{},[139,318,319],{},[142,320,321,324,327],{},[145,322,323],{},"Method",[145,325,326],{},"Path",[145,328,329],{},"역할",[152,331,332,344,356],{},[142,333,334,337,342],{},[157,335,336],{},"POST",[157,338,339],{},[22,340,341],{},"\u002Fusers\u002Flogin",[157,343,284],{},[142,345,346,349,354],{},[157,347,348],{},"GET",[157,350,351],{},[22,352,353],{},"\u002Fcourses",[157,355,287],{},[142,357,358,360,365],{},[157,359,336],{},[157,361,362],{},[22,363,364],{},"\u002Fenrollments",[157,366,290],{},[14,368,371],{"className":369,"code":370,"language":19,"meta":20},[17],"Try it out\n    ↓\nRequest 작성\n    ↓\nExecute\n    ↓\nResponse 확인\n",[22,372,370],{"__ignoreMap":20},[49,374,375,378,381,384,387,390],{},[28,376,377],{},"아이디어 확정 및 Domain Mapping",[28,379,380],{},"필요한 API만 리스트업",[28,382,383],{},"Swagger Try it out",[28,385,386],{},"Frontend 연결",[28,388,389],{},"Token 연결",[28,391,392],{},"Sprint 2에서 결제 후 상태 변화 확인",[77,394,395],{},"API에서 네가 이해할 수준",[14,397,401],{"className":398,"code":399,"language":400,"meta":20,"style":20},"language-http shiki shiki-themes github-light github-dark","POST \u002Fapi\u002Fusers\u002Flogin\n","http",[22,402,403],{"__ignoreMap":20},[404,405,408],"span",{"class":406,"line":407},"line",1,[404,409,399],{},[14,411,415],{"className":412,"code":413,"language":414,"meta":20,"style":20},"language-json shiki shiki-themes github-light github-dark","{\n  \"email\": \"test@test.com\",\n  \"password\": \"1234\"\n}\n","json",[22,416,417,423,440,451],{"__ignoreMap":20},[404,418,419],{"class":406,"line":407},[404,420,422],{"class":421},"sVt8B","{\n",[404,424,426,430,433,437],{"class":406,"line":425},2,[404,427,429],{"class":428},"sj4cs","  \"email\"",[404,431,432],{"class":421},": ",[404,434,436],{"class":435},"sZZnC","\"test@test.com\"",[404,438,439],{"class":421},",\n",[404,441,443,446,448],{"class":406,"line":442},3,[404,444,445],{"class":428},"  \"password\"",[404,447,432],{"class":421},[404,449,450],{"class":435},"\"1234\"\n",[404,452,454],{"class":406,"line":453},4,[404,455,456],{"class":421},"}\n",[14,458,460],{"className":412,"code":459,"language":414,"meta":20,"style":20},"{\n  \"token\": \"xxxxx\"\n}\n",[22,461,462,466,476],{"__ignoreMap":20},[404,463,464],{"class":406,"line":407},[404,465,422],{"class":421},[404,467,468,471,473],{"class":406,"line":425},[404,469,470],{"class":428},"  \"token\"",[404,472,432],{"class":421},[404,474,475],{"class":435},"\"xxxxx\"\n",[404,477,478],{"class":406,"line":442},[404,479,456],{"class":421},[14,481,484],{"className":482,"code":483,"language":19,"meta":20},[17],"로그인\n    ↓\nToken 획득\n    ↓\nsessionStorage 저장\n    ↓\n다른 API 호출\n    ↓\nAuthorization: Bearer Token\n",[22,485,483],{"__ignoreMap":20},[44,487,489],{"id":488},"step-5-frontend에서-실제-api를-연결","STEP 5. Frontend에서 실제 API를 연결",[14,491,494],{"className":492,"code":493,"language":19,"meta":20},[17],"[로그인 버튼]\n    ↓ fetch\nPOST \u002Fapi\u002Fusers\u002Flogin\n    ↓\nToken\n    ↓\nsessionStorage\n    ↓\n강의 조회 페이지\n    ↓\nGET \u002Fapi\u002Fcourses\n",[22,495,493],{"__ignoreMap":20},[77,497,498],{},"자료에서도 Swagger에서 Method, Path, Request Body, Response를 확인하고 그대로 fetch로 옮기라고 안내",[91,500,502],{"id":501},"sprint-1에서-나와야-하는-결과","Sprint 1에서 나와야 하는 결과",[14,504,507],{"className":505,"code":506,"language":19,"meta":20},[17],"로그인\n    ↓\n데이터 조회\n    ↓\n사용자가 항목 선택\n    ↓\n신청\n    ↓\nDB 상태 변경\n",[22,508,506],{"__ignoreMap":20},[77,510,511],{},"즉 발표하면서 다음 내용을 보여줄 수 있어야 합니다.",[81,513,514],{},[77,515,516],{},"여기 버튼 누르면 이렇게 됩니다.",[77,518,519],{},"코드 설명 20분보다 이 데모 30초가 훨씬 중요",[91,521,523],{"id":522},"sprint-2에서는-무엇을-추가하는가","Sprint 2에서는 무엇을 추가하는가?",[14,525,528],{"className":526,"code":527,"language":19,"meta":20},[17],"사용자가 신청\n    ↓\nPENDING\n    ↓\n결제\n    ↓\npayment.completed\n    ↓\nKafka\n    ↓\nenrollment-service\n    ↓\nAPPROVED\n",[22,529,527],{"__ignoreMap":20},[14,531,534],{"className":532,"code":533,"language":19,"meta":20},[17],"결제 전: PENDING\n    ↓\n결제 수행\n    ↓\n결제 후: COMPLETED\n",[22,535,533],{"__ignoreMap":20},[77,537,538],{},"상태가 실제로 바뀌었는가?",[44,540,542],{"id":541},"최종-결과물-6개","최종 결과물 6개",[77,544,545],{},"자료에 발표 산출물이 아주 명확하게 적혀 있습니다.",[91,547,549],{"id":548},"_1-pain-point","1. Pain Point",[49,551,552],{},[28,553,554],{},"누가 무슨 문제를 겪고 있는가?",[91,556,558],{"id":557},"_2-ai-solution","2. AI Solution",[49,560,561],{},[28,562,33],{},[91,564,566],{"id":565},"_3-sprint-구분","3. Sprint 구분",[136,568,569,579],{},[139,570,571],{},[142,572,573,576],{},[145,574,575],{},"구분",[145,577,578],{},"범위",[152,580,581,588],{},[142,582,583,585],{},[157,584,279],{},[157,586,587],{},"핵심 MVP",[142,589,590,592],{},[157,591,294],{},[157,593,594],{},"확장 기능",[77,596,597],{},"그리고 왜 그렇게 나눴는가?",[91,599,601],{"id":600},"_4-architecture","4. Architecture",[14,603,606],{"className":604,"code":605,"language":19,"meta":20},[17],"Client\n    ↓\nGateway\n    ↓\nA Service\n    ↓\nB Service\n    ↓\nKafka\n    ↓\nC Service\n",[22,607,605],{"__ignoreMap":20},[91,609,611],{"id":610},"_5-api-명세","5. API 명세",[136,613,614,625],{},[139,615,616],{},[142,617,618,620,623],{},[145,619,323],{},[145,621,622],{},"URL",[145,624,329],{},[152,626,627,638,649,659],{},[142,628,629,631,636],{},[157,630,336],{},[157,632,633],{},[22,634,635],{},"\u002Flogin",[157,637,284],{},[142,639,640,642,646],{},[157,641,348],{},[157,643,644],{},[22,645,353],{},[157,647,648],{},"조회",[142,650,651,653,657],{},[157,652,336],{},[157,654,655],{},[22,656,364],{},[157,658,290],{},[142,660,661,663,668],{},[157,662,336],{},[157,664,665],{},[22,666,667],{},"\u002Fpayments",[157,669,131],{},[77,671,672],{},"Request와 Response까지 정리합니다.",[91,674,676],{"id":675},"_6-동작-screenshot-demo","6. 동작 Screenshot \u002F Demo",[14,678,681],{"className":679,"code":680,"language":19,"meta":20},[17],"신청 전\n    ↓\n신청\n    ↓\n결제\n    ↓\n상태 변화\n    ↓\n추천 결과\n",[22,682,680],{"__ignoreMap":20},[77,684,685],{},"교수님 문서도 발표 결과물을 정확히 이 여섯 단계로 요구하며, 왜 → 무엇을 → Sprint → Architecture\u002FAPI → 실제 결과 순서로 논리가 이어져야 한다고 설명합니다.",[44,687,689],{"id":688},"그래서-내일-실제로-해야-할-순서","그래서 내일 실제로 해야 할 순서",[25,691,692,695,698,701,704,707,710,713,716,719,722,725],{},[28,693,694],{},"팀 Pain Point 확정",[28,696,697],{},"AI가 제공할 가치 한 문장 정의",[28,699,700],{},"Domain Mapping 작성",[28,702,703],{},"사용자 End-to-End Scenario 작성",[28,705,706],{},"Sprint 1, Sprint 2 분리",[28,708,709],{},"필요 API 3~5개 선정",[28,711,712],{},"Swagger Try it out 성공",[28,714,715],{},"Frontend와 API 연결",[28,717,718],{},"Sprint 1 End-to-End Demo",[28,720,721],{},"Payment, Kafka, Recommend 확장",[28,723,724],{},"통합 테스트",[28,726,727],{},"Screenshot, Architecture, API 명세 정리",[44,729,730],{"id":730},"서브노트",[136,732,733,742],{},[139,734,735],{},[142,736,737,739],{},[145,738,575],{},[145,740,741],{},"내용",[152,743,744,752,760,771,779,787,795],{},[142,745,746,749],{},[157,747,748],{},"목표",[157,750,751],{},"강의 신청 Flow를 구현한다.",[142,753,754,757],{},[157,755,756],{},"시도",[157,758,759],{},"Swagger에서 API 호출",[142,761,762,765],{},[157,763,764],{},"문제",[157,766,767,770],{},[22,768,769],{},"401 Unauthorized"," 발생",[142,772,773,776],{},[157,774,775],{},"원인",[157,777,778],{},"Authorization Header에 Token이 없었음",[142,780,781,784],{},[157,782,783],{},"해결",[157,785,786],{},"Bearer Token 추가",[142,788,789,792],{},[157,790,791],{},"결과",[157,793,794],{},"신청 성공",[142,796,797,800],{},[157,798,799],{},"이해",[157,801,802],{},"Gateway를 통해 요청하고, Token을 이용해 인증된 API를 호출한다.",[91,804,806],{"id":805},"필요-api","필요 API",[14,808,810],{"className":398,"code":809,"language":400,"meta":20,"style":20},"POST \u002Flogin\nGET \u002Fcourses\nPOST \u002Fenrollments\n",[22,811,812,817,822],{"__ignoreMap":20},[404,813,814],{"class":406,"line":407},[404,815,816],{},"POST \u002Flogin\n",[404,818,819],{"class":406,"line":425},[404,820,821],{},"GET \u002Fcourses\n",[404,823,824],{"class":406,"line":442},[404,825,826],{},"POST \u002Fenrollments\n",[828,829,830],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":20,"searchDepth":425,"depth":425,"links":832},[833,834,835,838,839,842,846,847,851,859,860],{"id":46,"depth":425,"text":47},{"id":74,"depth":425,"text":75},{"id":88,"depth":425,"text":89,"children":836},[837],{"id":93,"depth":442,"text":94},{"id":116,"depth":425,"text":117},{"id":197,"depth":425,"text":198,"children":840},[841],{"id":201,"depth":442,"text":202},{"id":260,"depth":425,"text":261,"children":843},[844,845],{"id":278,"depth":442,"text":279},{"id":293,"depth":442,"text":294},{"id":310,"depth":425,"text":311},{"id":488,"depth":425,"text":489,"children":848},[849,850],{"id":501,"depth":442,"text":502},{"id":522,"depth":442,"text":523},{"id":541,"depth":425,"text":542,"children":852},[853,854,855,856,857,858],{"id":548,"depth":442,"text":549},{"id":557,"depth":442,"text":558},{"id":565,"depth":442,"text":566},{"id":600,"depth":442,"text":601},{"id":610,"depth":442,"text":611},{"id":675,"depth":442,"text":676},{"id":688,"depth":425,"text":689},{"id":730,"depth":425,"text":730,"children":861},[862],{"id":805,"depth":442,"text":806},[864,865],"DevOps","Agile","2026-08-26 14:08:02 +0900","md",null,"\u002Fdevops\u002Fagile\u002F2026\u002F08\u002F26\u002F과제\u002F",{"layout":871},"post",true,"\u002Fposts\u002Fdevops\u002Fagile",{"title":5,"description":79},"posts\u002FDevOps\u002FAgile\u002F과제",[],"fHkX3L0Ppe8DT_id_WhdwZ8Qq8oUTEddMKAqC7Z14hI",1788744789419]