얼쑤는 녹음을 접수한 뒤 분석을 별도 작업으로 실행합니다. 이 기능을 붙이던 때에는 악보를 채점용 음표로 바꾸는 구현, 녹음 읽기 주소를 만드는 구현, 분석 서버 주소가 서로 다른 작업에서 들어왔습니다. 일부만 합쳐졌을 때도 앱은 켜져야 했습니다. 세 가지가 모두 준비되면 분석을 열고, 동시에 처리할 작업과 대기열은 이 기능의 규모에 맞춰 제한하기로 했습니다.
앱은 켜 두고 조건이 갖춰지면 분석 열기
설정은 Spring의 ObjectProvider로 악보 변환기와 녹음 읽기 주소 제공자를 찾고, 분석 서버 주소가 비었는지도 봅니다. 세 가지가 모두 있으면 HTTP 클라이언트와 분석기를 전용 실행 경로에 연결합니다. 하나라도 빠지면 분석이 꺼진 객체를 반환합니다. 이 상태에서는 업로드가 끝나도 누락 항목을 기록하고 작업을 넘기지 않습니다. 분석을 명시적으로 다시 요청하는 경로에서는 제출 거절을 알려, 호출자가 이미 열었던 시도를 정리할 수 있게 했습니다.
설정 테스트는 실제 앱 전체를 띄우지 않고 작은 빈 팩터리에 필요한 구현만 넣었습니다. 주소만 있는 경우, 주소와 변환기만 있는 경우, 주소와 읽기 주소 제공자만 있는 경우, 두 구현은 있지만 분석 서버 주소가 빈 경우는 모두 준비 상태가 꺼졌습니다. 세 항목을 모두 넣은 한 경우만 켜졌습니다. 별도 단위 검사에서는 꺼진 상태의 업로드가 분석을 0회 시작하고, 명시적인 작업 제출은 거절되는지 확인했습니다. 여기서 준비 상태는 실행에 필요한 구성의 연결 여부를 뜻합니다. 분석 서버에 실제로 접속해 채점하는 검증은 별개입니다.
전용 실행기가 앱의 기본 실행기를 바꾸지 않게
당시 프로젝트의 Spring Boot 4.1.1 기본 설정에서는 사용자 정의 Executor 빈이 있으면 프레임워크가 마련하는 기본 작업 실행기의 자동 구성이 물러날 수 있었습니다. 녹음 분석에 맞춰 둔 워커 수와 대기열을 공용 설정에도 적용하면, 관계없는 비동기 작업까지 영향을 받습니다. 실행기를 빈으로 등록하는 대신 분석 설정에서만 ThreadPoolTaskExecutor를 만들고 분석 작업을 제출하는 객체에 넘겼습니다. 업로드가 끝난 뒤의 첫 분석과 재시도는 그 객체를 통해 같은 풀에 들어갑니다. Spring Boot 작업 실행기 자동 구성
실행기를 가진 객체에서 종료까지 맡기기
전용 풀을 설정 코드에서 직접 만들면 그 수명도 정해야 합니다. Spring이 관리하는 제출 객체가 AutoCloseable을 구현하고, close()에서 풀의 destroy()를 호출하도록 했습니다. 앱 종료 때 실행 중인 분석을 끝까지 기다리는 옵션은 끄고, PENDING에 남은 시도는 이후 상태 조회가 마감 시각을 확인해 TIMEOUT으로 바꾸는 기존 규칙을 사용했습니다. 앱 종료가 분석 완료를 기다리지 않기 때문에 진행 중이던 분석을 나중에 시간 초과로 안내해야 할 수 있습니다. Spring Bean의 종료 콜백
녹음 저장소도 같은 소유 규칙을 따릅니다. 저장소 객체가 닫힐 때 자신이 사용하는 S3 클라이언트와 서명기를 함께 닫습니다.
검사에서 확인한 범위
실행기를 즉시 실행하는 대역에서 첫 분석과 재시도가 각각 지정한 세션을 넘기는지 확인했습니다. 제출을 거절하는 대역에서는 업로드 완료 리스너가 예외를 처리하고, 명시적인 재시도에는 거절이 전달되는지를 나눠 검사했습니다. 설정 검사에서 만든 객체는 검사가 끝날 때 close()로 닫았습니다. 설정과 업로드 리스너의 기존 검사 16개를 격리 환경에서 다시 실행해 모두 통과했습니다. 이 검사의 범위는 연결 조건과 제출 경계입니다. 별도의 최소 Spring 컨텍스트에는 기본 작업 실행기 자동 구성과 실제 분석 설정을 함께 올렸습니다. 기본 작업과 분석 작업은 서로 다른 스레드에서 실행됐습니다. 컨텍스트를 닫자 대기 중이던 분석 작업이 중단되고 전용 스레드가 종료됐습니다. 닫은 뒤에 제출한 새 분석은 거절됐습니다. 이 검사는 실행기의 기동과 종료를 확인하기 위해 DB와 분석 서버를 대역으로 두었습니다.

댓글
댓글과 반응은 GitHub 계정으로 로그인해 남길 수 있어요.