작은 변경을 AI에게 요청하기
65분 안팎
학습 목표
허용 파일과 명세 사례를 지정해 생성 API 한 개를 구현하고 AI 사용 기록을 남깁니다.
개념
요청서는 구현 지시와 검토 기준을 함께 담습니다
AI에게 'API를 만들어 주세요'라고 쓰면 생성·조회·완료와 저장 방식까지 한 번에 바뀔 수 있습니다. 이 레슨에서는 생성 경계 한 개만 요청합니다. 요청자는 HTTP-CONTRACT.md의 정상 사례와 거절 사례를 먼저 고르고 변경 가능한 파일을 구체적으로 적습니다. AI는 코드 초안을 만들 수 있지만 무엇을 성공으로 볼지와 기존 계약을 바꿀지는 요청자가 판단합니다. 받은 코드를 직접 읽고 실행해 채택한 이유를 남기는 것이 목표입니다.
요청에 제공하는 출발점은 m03의 TaskService.add입니다. 이 메서드가 title을 검증하고 번호를 부여하며 Task를 반환합니다. 새 컨트롤러에서 같은 정리 규칙을 다시 만들면 두 구현이 서로 달라질 수 있습니다. '서비스를 호출하라'는 문장에 실제 패키지 lab, 메서드 add(Object), 반환 Task까지 붙입니다. 없는 API를 추측해서 사용하지 않도록 기존 파일을 참고 대상으로 지정하고 수정 대상과 구분합니다.
구체적인 입력이 모호한 말을 줄입니다
정상 사례는 POST /tasks에 application/json과 {"title":" 복습 "}을 보내는 것입니다. 기대값은 201, id=1, title=복습, done=false입니다. 실패 사례는 공백 제목으로 400과 EMPTY_TITLE을 받는 것입니다. 실패 뒤 목록이 비어 있고 다음 정상 생성이 번호 1인지도 봅니다. '입력을 잘 검증하라' 대신 이런 사례를 전달하면 AI의 답변과 실제 결과를 같은 기준으로 비교할 수 있습니다.
숫자 title=12와 title 누락은 INVALID_TITLE_TYPE입니다. JSON에서 숫자가 들어왔는데 DTO가 String으로 받으면 경계에서 문자열로 바뀌어 서비스까지 정상 문자열이 갈 수 있습니다. 제공한 CreateTask record는 title을 Object로 받습니다. 서비스가 문자열 여부를 직접 판정하게 하기 위한 선택이며 다른 모든 DTO에 Object를 쓰라는 규칙은 아닙니다. 이 경계에서 필요한 형식 보존의 이유를 요청서에 적습니다.
허용 경로를 정하면 제안을 검토할 수 있습니다
수정 가능한 업무 파일은 TaskController.java와 TaskApplication.java입니다. review.md에는 요청과 응답 및 판단 근거를 추가합니다. pom.xml의 웹 의존성은 실습 준비물에서 이미 승인해 넣었습니다. AI가 다른 JSON 라이브러리나 보안 설정을 추가하려고 하면 현재 생성 계약에 필요한지 질문하고 별도 제안으로 남깁니다. 제목 규칙, 저장소와 테스트 기대값은 이번 요청에서 보호 대상으로 삼습니다.
읽을 수 있는 파일과 바꿀 수 있는 파일은 다릅니다. TaskService와 TitlePolicy는 구현을 이해하기 위해 읽어야 하지만 새 컨트롤러를 편하게 만들려고 수정할 대상은 아닙니다. 직접 의존성의 클래스가 존재하는지 확인할 때는 pom.xml과 사용 import를 대조합니다. ResponseEntity는 Spring Web의 타입이며 RequestBody와 PostMapping은 MVC 경계를 표시합니다. 코드가 컴파일되는지도 확인하되 컴파일 성공을 업무 계약 통과와 합치지 않습니다.
고정 응답도 검토의 재료가 됩니다
실습 starter에는 생성 상태가 200인 컨트롤러가 들어 있습니다. 이는 AI 계정 없이 평가할 수 있는 고정 초안입니다. 정상 생성의 JSON 필드는 맞아도 HTTP 상태가 계약과 다르므로 create 검사가 실패합니다. 조회와 제목 거절 검사 일부는 통과합니다. 학습자는 전체 파일을 답안으로 덮기 전에 어떤 줄이 실패 원인인지 찾고 ResponseEntity.status에 들어간 값을 수정합니다. 수정 한 줄이 어떤 약속을 회복하는지 적습니다.
실제 AI를 사용한다면 동일한 요청에 받은 원문을 별도 구획으로 보관합니다. 응답을 요약할 때 검증하지 않은 '테스트 통과' 문장을 본인 증거로 옮기지 않습니다. 실제 명령을 실행하고 종료 코드와 보고서 수치를 기록합니다. 프롬프트, 응답, 채택 코드, 본인 실행은 서로 다른 자료입니다. fixture를 사용하면 '제공된 고정 응답 사용'이라고 기록하며 비용이나 도구 버전 같은 가변 정보는 성공 기준에 넣지 않습니다.
생성과 오류 응답을 작은 경계로 구현합니다
TaskController에는 RestController와 RequestMapping("/tasks")를 붙입니다. PostMapping 메서드는 RequestBody CreateTask를 받아 service.add(request.title())를 호출합니다. 성공 Task를 ResponseEntity.status(201).body에 담습니다. 서비스 반환값을 다시 만들어 id나 done을 임의로 덮지 않습니다. 정리된 제목도 service.add가 돌려준 값을 사용합니다. 여기서 업무 판단의 소유자는 기존 서비스이고 컨트롤러는 HTTP 표현을 담당합니다.
서비스가 IllegalArgumentException을 발생시키면 해당 컨트롤러의 ExceptionHandler가 오류 메시지를 error 필드로 담고 400을 돌려줍니다. 이 실습의 서비스는 지정된 오류 코드를 사용하므로 그 코드를 그대로 연결합니다. 임의의 런타임 예외를 모두 400으로 바꾸는 catch를 넣으면 프로그램 결함도 사용자 입력 오류처럼 숨길 수 있습니다. 제공된 오류 경계의 범위를 읽고 일반적인 운영 오류 정책으로 확대 해석하지 않습니다.
JSON 문법을 읽지 못하는 경우는 제목 정책까지 도달하지 않습니다. HttpMessageNotReadableException 처리기는 INVALID_JSON을 보냅니다. 정상적인 JSON에서 title이 누락된 경우와 중괄호가 깨진 경우는 서로 다릅니다. 전자는 DTO의 null 제목이 서비스로 들어가 INVALID_TITLE_TYPE이 되고 후자는 변환 과정에서 종료됩니다. 사용자는 실패 응답의 status와 error를 함께 확인하며 400이라는 숫자만으로 원인을 추측하지 않습니다.
검증 결과를 받은 응답과 대조합니다
먼저 ./mvnw test를 실행해 starter의 실패를 확인합니다. HttpContractTest.create에서 expected 201과 actual 200을 읽고 변경 후보를 한 줄로 설명합니다. TaskController의 상태 생성만 고친 뒤 같은 테스트를 확인하고 전체 테스트를 실행합니다. 숫자 제목과 길이 경계 등 이미 통과하던 검사도 남아 있어야 합니다. 실패를 없애려고 isCreated를 isOk로 바꾸는 것은 구현의 결함을 테스트가 따라가게 하는 수정입니다.
전체 보고서에는 앞 모듈의 27개 서비스·빌드 검사도 함께 있습니다. HTTP 경계를 추가해도 기존 업무 계약을 보호하기 위한 검사입니다. 오류 코드가 맞는지, 실패 뒤 상태가 같은지, 40 코드 포인트가 허용되는지 확인합니다. TraceTest는 응답을 관찰 파일로 쓰지만 그 자체의 출력 기록만으로 올바름을 보장하지 않습니다. 기대값을 비교하는 HttpContractTest의 결과와 함께 읽어 관찰과 판정을 구별합니다.
없는 API와 환경 오류를 업무 결함과 나눕니다
cannot find symbol에 TaskService.create가 나오면 실제 메서드 이름 add를 확인합니다. AI가 이름을 그럴듯하게 만들었어도 프로젝트에 존재하지 않을 수 있습니다. package org.springframework.web.bind.annotation does not exist이면 승인된 웹 의존성과 캐시 준비를 확인합니다. 이미 존재하는 메서드를 새로 추가하거나 테스트를 제거해 컴파일을 맞추기 전에 실패 단계가 의존성 해석인지 Java 컴파일인지 테스트 실행인지 구분합니다.
expected 201 but was 200은 테스트가 MVC 요청을 수행했다는 뜻입니다. content type not supported와 415가 보이면 application/json을 보냈는지 확인합니다. 404라면 경로와 메서드 매핑을 읽습니다. 이 실습에는 실제 소켓 요청이 없으므로 포트를 바꿔 해결할 문제가 아닙니다. 같은 출력이라도 발생 조건을 기록하고 요청서의 사례와 맞춰 원인 후보를 좁힙니다. '안 된다'는 말보다 요청·응답·테스트 이름이 다음 수정에 유용합니다.
실습이 끝나면 review.md에 원래 요청, 받은 초안, 상태 코드 변경의 채택 근거, 거절한 제안과 실제 검증을 남깁니다. 사람이 결정한 계약과 AI가 제안한 구현을 구별하고 완료 API는 아직 추가하지 않았다고 씁니다. 자동 테스트는 기록 문장의 정직성이나 의미까지 판정하지 않습니다. 문서와 코드의 일치 여부는 직접 읽어야 합니다. 위임할 일과 사람이 잡을 판단의 일반 원리는 더 읽기에서 확장합니다.
따라하기
starter의 실패와 요청서 작성
starter에서 검사를 실행합니다. create의 expected 201과 actual 200을 기록한 뒤 허용 파일과 정상·실패 사례를 review.md에 씁니다. 예시는 고정 fixture이며 실제 AI를 쓰면 응답 원문을 별도 구획에 붙입니다.
./mvnw test모범 경계의 범위 확인
solution에서 보호 파일 검사를 실행합니다. SCOPE OK는 변경 경로가 기준과 일치함을 의미하며 제목 계약의 정확성은 별도 테스트로 확인합니다.
python3 check_scope.py실행 결과
SCOPE OK
성공 상태를 복구해 계약 검사
starter의 TaskController.create에서 status(200)을 status(201)로 바꿉니다. service.add 원형 위임과 테스트를 유지합니다. 해당 검사를 확인한 후 전체 검사를 실행하고 본인 보고서 수치를 기록합니다.
./mvnw -Dtest=HttpContractTest#create test
./mvnw test응답 관찰 파일 읽기
solution에서 TraceTest가 남긴 응답을 읽습니다. POST의 201과 정리된 title, BLANK의 400을 요청서와 대조합니다. 조회는 다음 기능의 결과이므로 생성 채택 근거와 구분합니다.
cat target/http-trace.txt실행 결과
POST 201 {"id":1,"title":"복습","done":false}
GET 200 [{"id":1,"title":"복습","done":false}]
BLANK 400 {"error":"EMPTY_TITLE"}
확인 문제
실습
TaskController.create의 성공 상태 결함을 고칩니다. 정상 생성 201, 빈 제목 400·EMPTY_TITLE, 숫자·누락 400·INVALID_TITLE_TYPE, 길이 경계·상태 보존 검사를 유지합니다. review.md에 허용 경로를 지정한 요청, 실제 응답 또는 고정 fixture 사용, 채택 줄과 본인 실행 증거를 씁니다. 기존 서비스·테스트·pom.xml은 수정하지 않습니다.
실행 명령
./mvnw test
기대 결과
JUnit 검사 38개가 실패·오류·건너뜀 없이 통과하고 종료 코드 0입니다. 범위 검사 출력은 SCOPE OK입니다.
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- AI에게 구현을 요청하기 전에 준비할 내용을 설명해 주시면 됩니다.