신청 데이터와 계약
170분 안팎
학습 목표
필드·식별자·허용값·저장 실패 결과를 정의합니다.
개념
같은 신청을 떠올리게 만드는 필드 약속
화면 담당자는 status를 ‘접수 완료’라고 이해하고 운영자는 ‘승인 완료’라고 이해하면 같은 데이터를 쓰면서 다른 결과를 만듭니다. 기획자가 계약을 남기는 이유는 필드 이름을 길게 나열하기 위해서가 아니라 누가 어떤 조건에서 무엇을 받는지 합의하기 위해서입니다. 앞에서 관찰한 R01의 applicationId와 PENDING을 출발점으로 신청 입력, 생성 결과, 조회 결과와 실패 안내를 표로 정합니다. 이 계약은 제안이며 기존 서비스의 구현을 확인했다는 문서가 아닙니다.
입력과 서버 생성 값을 분리합니다
신청 입력의 eventId는 문자열이며 이번 모의 사례에서 EV01만 허용합니다. 신청 ID인 applicationId는 서버가 생성해 A001 같은 문자열로 반환합니다. 학습자가 A001을 보냈다고 새 신청 ID를 직접 지정하는 규칙은 아닙니다. status도 서버가 초기값 PENDING을 정합니다. 사용자가 입력 본문에 APPROVED를 추가했다고 승인 완료로 받아들이는 설계는 업무 책임을 바꾸므로 허용 필드와 서버 책임을 명확히 적습니다. 모의 코드는 모든 추가 필드를 검증하는 완성 구현이 아니므로 이 부분은 후속 구현 확인 사항입니다.
각 필드 표에는 이름, 타입, 입력·출력 방향, 필수 여부, 생성 책임, 허용값, 빠졌을 때 동작을 씁니다. ‘문자열’만 적으면 빈 값이 가능한지 알 수 없습니다. eventId가 누락되거나 빈 문자열이면 이번 모형은 422와 INVALID_EVENT로 거부하며 새 행은 없습니다. 숫자 1과 문자열 “1”을 같게 받을지, 앞뒤 공백을 제거할지 같은 정책은 구현 전에 질문할 수 있습니다. 지금 모형은 EV01과 정확히 같은 값만 받아들이므로 임의의 정규화가 있다고 쓰지 않습니다.
상태의 의미와 전환 책임을 정합니다
PENDING은 신청이 접수되어 검토를 기다리는 상태, APPROVED는 검토 후 승인, REJECTED는 검토 후 반려로 정의합니다. 신청 API는 PENDING으로 시작하고 조회 API는 현재 저장된 상태를 반환한다는 제안입니다. 운영자 검토 F02가 상태를 바꾸는 책임을 가집니다. 실습에서 PENDING만 생성·조회했다고 나머지 두 상태 전환도 테스트됐다고 쓰지 않습니다. 승인 전환 주체와 허용 순서, 반려 사유의 수집 범위는 다음 명세와 접근 정책에서 검토할 항목으로 남깁니다.
빈 상태와 없는 자원도 구별합니다. ‘아직 신청하지 않았습니다’라는 목록의 빈 상태와 잘못된 단일 신청 번호를 조회해 NOT_FOUND가 오는 경우는 다른 화면입니다. 이번 단일 조회에서는 존재하지 않는 A999에 404를 반환합니다. 목록 API와 목록의 빈 배열은 만들지 않았으므로 단일 조회 계약에 목록 동작을 섞지 않습니다. 화면 초안에는 해당 번호와 경로를 확인할 안내를 넣고 개인정보나 내부 저장 경로를 오류 본문에 넣지 않도록 검토합니다.
식별자의 수명과 용도를 맞춥니다
applicationId는 생성된 신청 하나를 계속 가리키고 requestId는 요청 시도 하나를 가리킵니다. 재전송 키는 신청을 보내려는 같은 의도를 구별합니다. R01과 R05는 서로 다른 요청인데 K01을 공유하고 A001을 반환합니다. 키 없이 두 번 보내면 같은 의도였는지 서버가 알 수 없으므로 이 제안은 제출에 키를 요구하고 없으면 400과 MISSING_KEY를 반환합니다. 실제 제품은 사용자·행사 범위, 키 길이와 보관 기간을 정해야 하며 이번 메모리 모형에는 실행 한 번의 범위만 있습니다.
동일 키·동일 본문은 기존 신청 ID와 200을 돌려주고 행을 추가하지 않습니다. 동일 키·다른 본문은 기존 신청을 덮어쓰지 않고 409와 KEY_CONFLICT로 거부합니다. 이는 HTTP가 자동으로 보장하는 기능이 아니라 우리 API가 정한 정책입니다. 서버의 키 기록과 신청 저장이 함께 확정되는지, 동시에 요청이 들어올 때 한 신청만 만드는지는 개발자가 원자성과 제약을 검토해야 합니다. 순차 모형의 성공을 동시성 보장의 근거로 쓰지 않습니다.
실패 계약은 저장 여부와 다음 행동을 포함합니다
입력 오류 계약에는 어떤 필드가 문제인지, 수정할 값은 무엇인지, 저장 행이 늘어나는지 적습니다. 저장 실패 계약에는 성공 문구를 표시하지 않는다는 기준과 확인 경로를 씁니다. R04는 저장 전 실패를 주입한 사례여서 행 변화 0을 알 수 있습니다. 실제 타임아웃은 서버가 저장한 뒤 응답만 잃었을 수도 있으므로 ‘시간 초과면 저장 안 됨’으로 정하지 않습니다. 신청 내역 확인 또는 같은 재전송 키를 유지한 재시도 정책을 담당자와 합의해야 합니다.
사용자 문구는 책임을 떠넘기는 표현보다 관찰된 상황과 가능한 행동을 안내합니다. INVALID_EVENT는 ‘행사를 선택하고 다시 제출해 주세요’, PENDING은 ‘신청이 접수되어 검토 대기 중입니다’, NOT_FOUND는 ‘신청 번호와 확인 경로를 확인해 주세요’처럼 구분합니다. STORE_UNAVAILABLE에서는 내부 예외 전문을 내보내지 않습니다. 지원팀이 조사할 요청 ID를 남기되 사용자에게 비밀 토큰이나 다른 사람의 신청 정보를 복사해 보내도록 요구하지 않습니다.
미션 문서에 관찰과 제안을 합칩니다
data-flow.json의 flows에는 submit과 lookup 두 흐름을 넣습니다. sourceFlowId는 각각 F01과 F03으로 앞 업무와 연결하고 edges는 screen→api, api→db, db→api, api→screen 네 객체입니다. 각 객체에 from, to, data를 씁니다. contracts에는 두 operation의 method, path, input, output, fields, duplicate를 넣습니다. fields의 required는 문자열 “true”가 아니라 JSON 불리언 true입니다. 미션 README에 필드와 사례 형식이 있으므로 그 표와 본인 문서를 비교합니다.
cases에는 normal, lookup, input-error, store-failure, duplicate, not-found 여섯 사례를 씁니다. 각각 operation, requestId, httpStatus, result, rowDelta, message, nextCheck, basis를 채웁니다. basis는 mock-replay이며 rowDelta는 요청 전후 증가량입니다. R05의 rows가 1이어도 rowDelta는 0입니다. evidenceIds는 E003·E006, counterEvidenceIds는 E005를 보존하고 problemId는 Q01로 둡니다. 사용자 조사 근거를 코드 실행 증거로 바꾸지 않고 새 사례의 검증 방식을 따로 남깁니다.
검사 통과와 합의 완료를 구별합니다
미션 명령은 필드 존재, 이전 문서 보존, 경계 연결, 여섯 사례의 상태와 행 변화를 검사합니다. FAIL contracts.submit.input은 입력 필드가 빠졌다는 뜻이고 FAIL flows.lookup은 조회 경계가 없거나 이어지지 않았다는 뜻입니다. 오류 메시지를 읽고 해당 객체만 고칩니다. JSON 줄·열 오류가 나면 구조를 먼저 바로잡고 의미를 검토합니다. 검사기를 고치거나 기존 근거 문서를 줄여서 통과시키면 조사에서 계약까지의 추적 경로를 잃습니다.
동료 검토에서는 저장 성공과 승인 성공이 구별되는지, 신청 ID로 다시 결과를 확인할 수 있는지, 같은 의도의 중복이 새 신청이 되지 않는지 읽습니다. 개발자에게는 추가 필드 검증, 실제 저장 확정, 인증 범위, 키 보관과 동시 처리라는 미확인 항목을 전달합니다. 자동 검사 통과는 현재 문서의 구조 일치이며 서비스 출시 승인이나 효과 입증이 아닙니다. 결과 경로 문제를 해결하려는 계약이 다음 화면 명세에서 검증 가능하도록 남으면 이번 모듈 미션이 완성됩니다.
따라하기
필드 표를 작성합니다
미션 README를 열고 eventId, applicationId, status의 타입·책임·필수 여부·허용값을 표로 씁니다. 생성 입력과 조회 입력을 다른 열에 놓고 빈 행사 값은 INVALID_EVENT라고 적습니다.
기존 신청과 중복 의도를 구별합니다
R01·R05의 requestId, key, applicationId를 나란히 적습니다. 동일 키·동일 본문은 기존 ID·200·행 증가 0, 다른 본문은 KEY_CONFLICT·409라는 제안을 contracts.duplicate에 문장으로 작성합니다.
여섯 사례와 안내를 연결합니다
cases의 여섯 id에 요청 ID, HTTP 상태, 업무 상태 또는 오류 코드, rowDelta와 사용자 문구를 채웁니다. 정상 접수에는 승인 대기와 결과 확인 경로를 포함하고 타임아웃 저장 여부는 미확인이라는 한계를 씁니다.
구조 검사 후 합의를 검토합니다
미션 루트에서 다음 명령을 실행합니다. 아래는 solution을 실행한 결과입니다. 자신의 문서가 통과한 뒤에도 동료가 필드 의미와 안내를 읽고 실제 구현 미확인 항목을 검토합니다.
python3 check.py --submission data-flow.json실행 결과
검사 실패: 0 PASS 이전 근거·경계·계약·6사례; 내용 타당성은 동료 검토
확인 문제
실습
신청·조회 필드 표와 중복 정책, 정상·조회·입력 오류·저장 실패·중복 요청·없는 번호 여섯 사례를 data-flow.json으로 제출합니다. 미션 starter의 이전 근거는 보존합니다. 각 사례에는 사용자 안내와 다음 확인을 포함하고 rowDelta는 요청 전후 증가량으로 씁니다. 구조 검사 후 동료가 접수와 승인 구분, 타임아웃 불확실성, 관찰과 제안의 구분을 검토합니다. 미션 solution은 작성 예시이며 실제 인증·동시성·DB 영속성을 확인한 결과가 아닙니다.
더 읽기
면접 질문
- 화면에서 저장 버튼을 눌렀을 때의 데이터 흐름을 설명해 주시면 됩니다.