연주 평가를 조회하는 요청은 평가가 있는지, 다른 회원의 세션인지, 아직 분석 중인지에 따라 답이 달라집니다. 분석을 다시 요청할 때도 아직 작업을 시작할 수 없는 경우가 여러 가지입니다. 두 기능에서 예상되는 업무 결과를 이름 있는 값으로 표현하고, HTTP 상태와 응답 본문은 컨트롤러에서 정했습니다.

평가 조회의 판단 경계

컨트롤러는 인증을 마친 토큰에서 회원 ID를 읽어 세션 ID와 함께 넘깁니다. 유스케이스는 조회 계약을 통해 소유한 세션을 찾고, 완료된 세션에서만 평가와 음표 결과를 읽습니다. 세션과 음표 결과는 같은 읽기 전용 트랜잭션에서 조회합니다. 그 계약을 구현한 MyBatis 어댑터는 조회 행을 업무 코드가 읽을 값으로 바꿉니다.

유스케이스가 돌려주는 결과는 NotFound, NotReady, Found입니다. 컨트롤러는 세션을 찾지 못하면 404, 분석이 끝나지 않았으면 409, 평가가 있으면 200으로 응답합니다. 인증 정보가 없는 경우의 401은 보안 필터가 담당합니다. 실제 HTTP와 MySQL을 연결한 테스트에서 이 네 상태를 확인했고, 성공 응답에서는 음표 순서와 점수도 대조했습니다.

재시도 실패를 409와 503으로 구분

분석 재시도에는 규칙상 다시 시작할 수 없는 경우와, 다시 시작할 수 있지만 다른 요청이 계속 먼저 갱신하는 경우가 있습니다. 전자는 NotRetryable, 후자는 Busy입니다. 재시도 유스케이스는 이 둘을 구분해 반환하고, 컨트롤러는 각각 409와 503으로 바꿉니다. sealed interface의 결과를 when 식으로 모두 다루면 결과 종류를 추가할 때 응답 매핑이 빠진 지점을 컴파일 단계에서 발견할 수 있습니다.

업무 결과HTTP 응답호출자가 다음에 확인할 것
NotRetryable409 STATE_CONFLICT같은 요청을 반복하기 전에 세션 상태를 확인합니다. 무음이나 너무 짧은 녹음이라면 새로 녹음할 수 있습니다.
Busy503 SERVICE_UNAVAILABLE저장 경쟁이 끝날 시간을 두고, 잠시 뒤 같은 세션의 분석 재시도를 다시 요청할 수 있습니다.

단위 테스트에서는 다른 요청이 매번 먼저 시도 번호를 바꾸는 저장소 대역을 만들었습니다. 새 분석을 시작하려는 갱신이 3회 실패한 뒤 결과는 Busy였고 실행기에 제출된 작업은 0건이었습니다. 이 검사는 업무 분기와 제출 여부를 확인했습니다.

컨트롤러의 기존 요청 검사에서는 인증을 마친 사용자를 대역으로 넣고, 갱신 요청을 계속 거절하는 저장소로 재시도를 호출합니다. 이 검사는 503과 SERVICE_UNAVAILABLE을 함께 단언합니다. 무음·너무 짧은 녹음 등 재시도 규칙을 만족하지 않는 세션은 별도 검사에서 409와 STATE_CONFLICT를 단언합니다. 재시도 업무 로직과 컨트롤러의 기존 검사 23개를 다시 실행해 모두 통과했습니다. 컨트롤러 검사는 Spring MVC 요청을 흉내 내 컨트롤러까지 연결합니다. 보안 필터의 실제 인증, 운영 저장 경쟁의 발생 빈도, 모바일 앱이 다시 요청하는 동작은 이 검사 범위 밖입니다.