작업을 검증 단위로 나누기
65분 안팎
학습 목표
생성·조회·완료 API를 의존성 순서와 완료 검사로 나눠 요청서를 작성합니다.
개념
작다는 기준을 관찰 가능한 결과로 정합니다
개인 학습 할 일 앱에 웹 화면을 붙이려면 생성·조회·완료 API가 필요합니다. 세 기능을 한꺼번에 요청하면 상태 변경 오류와 응답 변환 오류가 섞여 어느 층이 잘못되었는지 찾기 어렵습니다. 교육 담당자는 신입에게 먼저 요청을 나누는 표를 쓰게 합니다. 작은 작업은 파일 수나 줄 수보다 입력 하나를 넣고 기대 결과를 비교할 수 있는 크기로 판단합니다. 이번 목표는 코딩을 시작하기 전에 작업 순서와 각 단계의 종료 증거를 정하는 것입니다.
이번 모듈의 실습은 JDK 17, Python 3와 제공된 Maven Wrapper를 사용합니다. starter와 solution 압축은 서로 다른 폴더에 풉니다. 명령은 각 실습의 pom.xml이 있는 폴더에서 실행하며 학습자 기본 명령은 ./mvnw test입니다. 최초 의존성 준비 뒤 캐시가 있으면 ./mvnw -o -q test로 검사할 수 있습니다. Permission denied이면 chmod +x mvnw를 실행합니다. 캐시 부족 메시지는 과제의 assertion 실패와 구별하며 서버와 DB는 필요하지 않습니다.
따라하기의 관찰 명령은 solution에서 실행하고 수정은 starter에서 수행합니다. 실제 AI 도구를 쓰면 요청과 받은 응답을 review.md에 남깁니다. 계정이 없으면 제공된 코드와 candidate.patch를 고정 응답 fixture로 사용해 같은 검토를 합니다. fixture 사용 사실을 적고 AI 호출을 했다고 꾸미지 않습니다. Maven의 실제 결과와 사람이 검토한 범위도 나눠 기록합니다. 이 안내는 뒤 레슨에서도 같은 방식으로 적용합니다.
앞 단계의 계약을 변경 전 기준으로 잡습니다
m03의 TaskService는 add, list, complete를 이미 제공합니다. add는 앞뒤 공백을 제거한 1~40 코드 포인트의 문자열을 저장하고 id와 done=false를 반환합니다. list는 번호 순서로 새 목록을 반환합니다. 실패한 생성은 목록과 다음 번호를 바꾸지 않습니다. 이 규칙은 이번 작업에서 다시 설계할 대상이 아니라 보호할 출발점입니다. Python 참조 모델과 Java 테스트가 다루는 계약을 읽고 HTTP 변환이 무엇을 새로 결정해야 하는지 구분합니다.
새로 결정할 내용은 HTTP 경로, 요청 본문의 모양, 성공 상태와 오류 상태입니다. POST /tasks는 title을 담은 JSON을 받고 201과 Task 객체를 돌려줍니다. GET /tasks는 200과 배열을 돌려줍니다. 서비스의 apply가 반환하던 ok 포장은 HTTP에 넣지 않습니다. 따라서 내부 모델을 보존한다는 말과 외부 표현을 똑같이 복사한다는 말은 다릅니다. HTTP-CONTRACT.md에 경계의 새 약속을 별도 항목으로 적습니다.
의존 순서와 검증 순서를 함께 씁니다
첫 작업은 이미 승인한 서비스에 생성 요청을 연결하는 것입니다. 입력 DTO가 title을 받아 service.add로 넘기고 201을 만드는 경로까지 하나의 결과로 묶습니다. 빈 제목이 서비스에서 EMPTY_TITLE을 내면 400 오류 JSON으로 연결하는 처리도 함께 확인합니다. 성공 경로만 추가하고 오류를 나중으로 미루면 사용 가능한 생성 경계를 완료했다고 말할 수 없습니다. 정상 생성과 거절 생성은 같은 기능의 서로 다른 조건입니다.
두 번째 작업은 목록 조회 경로입니다. 새 서비스의 GET 결과가 빈 배열인지 확인하고 생성 뒤에는 저장된 항목 전체가 순서대로 보이는지 비교합니다. 생성이 준비되어야 조회 코드를 작성할 수 있는 것은 아닙니다. 빈 조회는 독립적으로 확인할 수 있습니다. 다만 이번 교육 순서는 생성이 통과한 다음 그 생성 결과를 조회로 관찰하도록 정합니다. 기술적 의존성과 학습·검증의 순서를 같은 것으로 단정하지 않습니다.
세 번째 작업인 완료 API는 다음 모듈로 넘깁니다. 현재 서비스에 complete 메서드가 있다고 이번 요청에 경로까지 추가할 이유는 없습니다. 다음 단계에서는 완료의 상태 전이와 재완료 조건을 직접 만든 테스트로 다룹니다. 작업 표에는 완료를 삭제하지 않고 보류 사유와 선행 조건을 적습니다. 빠르게 모든 기능을 넣는 제안이 나오더라도 이번 단계의 검증 범위로 판단합니다. 남은 일을 숨기지 않으면서 요청 범위를 작게 유지합니다.
작업 카드에는 목표와 종료 관문이 필요합니다
생성 카드의 목표를 '컨트롤러 작성'이라고만 쓰면 클래스 파일이 생긴 순간 종료할 수 있습니다. 대신 공백을 붙인 제목을 보내 정리된 제목, 번호 1, 미완료와 201을 받는다고 씁니다. 빈 제목은 400이며 그 뒤 정상 생성이 번호 1이라고 덧붙입니다. 변경 파일은 TaskController.java, TaskApplication.java와 검토 기록으로 한정합니다. 기존 정책·저장소·테스트·의존성의 임의 수정은 별도 승인이 필요한 제안으로 분리합니다.
조회 카드에는 빈 목록과 두 항목 생성 뒤 전체 배열의 기대값을 넣습니다. 개수만 확인하면 제목 누락과 done 오염을 놓칩니다. 목록 코드가 단순해 보여도 생성과 다른 서비스 객체를 쓰면 항상 빈 배열이 됩니다. 두 경로가 동일한 저장 상태를 관찰한다는 검사를 종료 관문으로 삼습니다. 테스트가 존재한다는 사실보다 테스트가 어떤 잘못된 결과를 거절하는지 문장으로 설명할 수 있어야 합니다.
제외 범위에는 파일 저장, 인증, DB, 포트 변경과 운영 배포를 적습니다. 프로젝트 전체 목표에 포함될 수 있는 개선과 현재 카드의 요구는 구별합니다. 유용한 개선을 발견했으면 보류 목록에 이유를 쓰고 이번 결과에 섞지 않습니다. '가능하면 정리도' 같은 표현은 범위를 넓히기 쉬우므로 삭제합니다. 카드가 길어져서 목표가 두 개가 되면 새 카드로 나누되 오류 처리처럼 같은 입력 계약에 필요한 부분은 함께 둡니다.
증거 없이 다음 요청으로 넘어가지 않습니다
생성 검사를 실행한 뒤 종료 코드와 테스트 보고서를 남깁니다. 보고서는 Tests, Failures, Errors, Skipped를 함께 읽습니다. 컴파일 오류로 테스트가 실행되지 않은 경우와 실행된 비교식이 실패한 경우는 다른 상태입니다. AI가 '완료'라고 적었거나 Maven이 파일을 생성했다는 사실은 성공 응답 계약을 대신하지 않습니다. 기준 입력과 실제 응답의 차이를 적고 생성 카드가 확인된 뒤 조회 카드를 전달합니다.
검증한 작업과 대기 작업을 구분해 인계 기록을 남깁니다. 예를 들면 '생성은 MockMvc에서 확인, 조회는 아직 미확인, 완료는 다음 모듈'처럼 됩니다. 한 카드만 통과한 시점에 앱 완성이라고 쓰지 않습니다. 앞 단계 결과와 이번 변경을 구분하고 각 증거가 어디까지 확인했는지 적습니다. 미확인 범위를 적는 것은 실패 선언이 아니라 다음 작업자가 사용할 수 있는 정확한 상태 정보입니다.
흔한 실패를 카드 수준에서 수정합니다
생성·조회·완료를 모두 한 카드로 묶었다면 오류가 생겼을 때 가장 최근에 추가한 경로를 특정하기 어렵습니다. 기능별 입력과 출력이 따로 비교되도록 나눕니다. 반대로 DTO 한 줄을 별도 카드로 쪼개고 실행 경로는 나중에 붙이면 첫 카드만으로는 결과를 검사하기 어렵습니다. 테스트로 관찰되는 경계까지 묶고 구현 내부 조각은 그 카드의 하위 작업으로 적습니다. 줄 수에 맞추는 분할보다 결과에 맞추는 분할이 검토에 유리합니다.
Cannot resolve dependency처럼 의존성을 받지 못한 상황은 범위를 늘려 다른 프레임워크로 바꿀 이유가 아닙니다. 사용한 JDK와 준비된 캐시를 확인하고 환경 문제로 기록합니다. expected 201 but was 200이면 생성 경로는 실행되었지만 HTTP 약속을 어겼다는 뜻입니다. 상태 코드를 바꾸는 작은 수정부터 확인합니다. 완료 기준이 구체적이면 오류 메시지를 읽고 무엇을 고쳐야 하는지 더 빨리 판단할 수 있습니다.
이 레슨의 제출물은 세 장의 작업 카드입니다. 각 카드에 입력·성공 출력·거절 조건·허용 파일·선행 조건·검증 명령·다음 단계 조건을 적습니다. 생성과 조회는 이번 모듈, 완료는 다음 모듈로 표시합니다. 생성 실패 뒤 상태가 보존되는 이유와 조회를 확인할 방법을 설명하면 요청 분할을 실제로 수행한 것입니다. 작은 단계와 체크포인트의 일반적인 설명은 더 읽기로 보내고 여기서는 HTTP 연결 계획의 판단에 집중합니다.
따라하기
카드를 의존 순서로 출력
생성·조회·완료의 카드와 검증 순서를 실행해 봅니다. 완료는 다음 단계로 표시합니다. 이 출력은 계획이며 API 실행 결과가 아닙니다.
cards = [("생성", "201·400과 상태 보존", "이번"), ("조회", "빈 배열·생성 후 전체 배열", "이번"), ("완료", "완료·재완료 상태 전이", "다음")]
for name, gate, stage in cards:
print(f"{name} | {gate} | {stage}")실행 결과
생성 | 201·400과 상태 보존 | 이번 조회 | 빈 배열·생성 후 전체 배열 | 이번 완료 | 완료·재완료 상태 전이 | 다음
선행 관문을 계산
생성이 확인되어도 조회가 미확인이면 다음 생성·조회 통합 작업이 끝난 것은 아닙니다. 상태 목록을 직접 바꿔 다음 관문을 비교합니다.
verified = {"생성": True, "조회": False}
print("조회 요청 가능=" + str(verified["생성"]))
print("이번 모듈 완료=" + str(all(verified.values())))실행 결과
조회 요청 가능=True 이번 모듈 완료=False
요청 카드에 빠진 항목 찾기
예시 카드에는 실패 조건이 빠져 있습니다. 출력에 나온 항목을 공백 제목 오류와 상태 보존 기준으로 채웁니다. 학습자 제출물의 내용 타당성은 사람이 검토합니다.
required = ["목표", "입력", "성공", "실패", "허용 파일", "검증"]
card = {"목표": "생성", "입력": "title", "성공": "201", "허용 파일": "TaskController.java", "검증": "HttpContractTest"}
print("누락=" + ", ".join(k for k in required if k not in card))실행 결과
누락=실패
확인 문제
실습
생성·조회·완료 세 장의 작업 카드를 제출합니다. 각 카드에 목표, 입력, 성공·거절 결과, 허용 파일, 선행 조건, 검증 명령, 종료 관문을 씁니다. 생성은 201·400과 실패 뒤 상태·번호 보존, 조회는 빈 배열·두 생성 뒤 전체 배열을 넣습니다. 완료는 다음 모듈로 보류합니다. 파일 수로만 나누지 않은 이유, 빈 조회의 독립 검증 가능성, 기술적 의존성과 확인 순서 차이를 설명합니다. 제출물의 의미는 사람이 검토합니다.
더 읽기
면접 질문
- AI에게 구현을 요청하기 전에 준비할 내용을 설명해 주시면 됩니다.