본문으로 건너뛰기
뒤로

MSA에서 무엇을 함께 성공시켜야 할까

초안아직 소유자가 검토하고 실습해 확인하지 않은 글입니다. 학습 기록이 아니라, 확인 전의 글입니다.

읽기 약 5분 · 실습 시간 별도

Temporal을 알아보기 전에 풀고 싶은 문제부터 적어 보자. 여러 서비스가 각각 정상적으로 요청을 처리해도 사용자에게는 실패한 업무가 남을 수 있다. 문서를 변환하는 서비스는 결과를 만들었지만 저장 서비스가 응답하지 않거나, 처리 허용량을 예약한 뒤 변환 프로세스가 종료되는 상황이다. 개별 API의 성공 여부만으로 전체 업무의 성공을 판단하기 어렵다.

이 시리즈에서는 처리 허용량 예약 → 문서 변환 → 결과 저장 → 예약 확정이라는 가상 업무를 사용한다. 허용량은 동시에 처리하거나 사용할 수 있는 자원을 임시로 확보하기 위한 기록이다. 예약은 최종 사용과 다르며, 작업이 실패하면 해제할 수 있어야 한다. 실제 제품의 정책을 가져오지 않고 이 작은 예제를 단계적으로 확장한다. 이 글은 학습 초안이며 직접 실행한 실험 결과는 후속 검토에서 보강한다.

API 네 개보다 먼저 정할 것

첫 번째 질문은 네 단계를 반드시 하나의 순간에 성공시켜야 하는지다. 사용자가 잠시 처리 중 상태를 보는 것은 허용하면서 최종적으로 완료나 실패를 알리면 되는 업무가 있다. 반대로 일부 변경이 다른 요청에 노출되는 것 자체가 허용되지 않는 조건도 있다. 두 경우는 같은 해법을 요구하지 않는다. 단지 서비스가 네 개라는 사실로 분산 트랜잭션이나 Saga를 선택할 수는 없다.

예제에서는 사용자가 처리 중 상태를 보는 것을 허용한다고 정한다. 대신 완료로 표시한 문서는 실제 결과를 읽을 수 있어야 하고, 확정하지 않은 예약이 실패 뒤 무기한 남지 않아야 한다. 같은 요청이 중복 전달돼도 사용량이 두 번 확정되어서는 안 된다. 이 문장들은 특정 라이브러리의 기능 목록이 아니라 애플리케이션이 지켜야 할 조건이다. 이후 설계가 맞는지는 이 조건으로 판단한다.

중단 위치이미 남을 수 있는 것이후에 결정할 일
예약 직후예약 기록변환을 다시 시작할지 예약을 해제할지
변환 직후변환 결과 파일저장을 재시도할지 임시 파일을 지울지
결과 저장 직후조회 가능한 결과예약을 확정할지 결과 공개를 보류할지
예약 확정 직후결과와 사용 기록완료 응답을 다시 제공할 방법

이 표에서 중요한 점은 실패했다고 해서 앞선 결과가 사라지지 않는다는 것이다. 이미 커밋한 다른 DB의 변경, 업로드한 파일, 외부에 보낸 알림은 호출자의 예외 처리만으로 되돌아가지 않는다. 예외가 발생한 위치와 외부 효과가 발생한 위치는 다를 수 있다. 응답을 받지 못했다는 사실도 상대가 실행하지 않았다는 뜻은 아니다.

업무 상태와 기술적인 실행 상태를 나눈다

HTTP 응답이 타임아웃됐다는 것은 기술적인 관찰이다. 예약이 존재하는지, 결과가 공개됐는지, 사용량이 확정됐는지는 업무 상태다. 둘을 같은 값으로 저장하면 타임아웃 = 실패 = 예약 없음 같은 잘못된 결론을 내리기 쉽다. API 호출 결과를 모르는 상태를 다룰 수 있어야 한다.

예를 들어 예약 API가 DB 변경을 마친 뒤 응답을 보내기 전에 연결이 끊길 수 있다. 호출자는 실패를 받았지만 예약은 남는다. 이때 새 예약을 만드는 방식으로 재시도하면 중복 예약이 생길 수 있다. 같은 업무 식별자로 예약 상태를 확인하거나 동일 요청을 반복해도 같은 예약을 반환하는 계약이 필요하다. 이런 요구는 Temporal을 도입하더라도 사라지지 않는다.

또한 모든 실패에서 이전 상태로 돌아가야 하는 것은 아니다. 결과 저장이 일시적으로 불가능하면 저장을 재시도하는 편이 맞을 수 있다. 문서 형식 자체를 지원하지 않는다면 같은 변환을 계속 반복할 이유가 없다. 예약을 해제하고 실패를 알리는 쪽이 업무 조건에 맞는다. 어떤 실패는 앞으로 진행하고 어떤 실패는 앞선 효과를 보상할지 구분하는 것이 Saga를 이해하는 출발점이다. AWS의 Saga 설명도 이 두 복구 방향을 구별한다.

첫 실험은 정상 경로보다 실패 표를 확인한다

처음에는 네 단계를 한 Java 프로그램에서 순서대로 호출해도 충분하다. 복잡한 배포를 준비하기 전에 예약 기록과 문서 상태를 간단한 DB에 남긴다. 변환 함수가 의도적으로 예외를 던지게 하고 프로그램을 종료한 뒤 기록을 다시 읽는다. 예약이 남아 있다면 그것은 실험이 실패했다는 뜻이 아니라 아직 정리 책임을 구현하지 않았다는 관찰이다.

  1. 업무 식별자 하나로 예약을 만들고 문서 상태를 PROCESSING으로 기록한다.
  2. 변환 단계에서 오류를 발생시킨다.
  3. 프로세스를 다시 실행하기 전에 예약과 문서 상태를 조회한다.
  4. 사용자가 볼 상태와 운영자가 처리할 일을 표에 추가한다.

예상 결과는 변환 실패만으로 예약이 자동 삭제되지 않는 것이다. 실제 결과가 다르면 DB 트랜잭션 범위와 정리 코드가 이미 어디에 있는지 확인한다. 여러 서비스를 아직 만들지 않았더라도 어떤 변경을 함께 묶고 싶은지, 어떤 변경은 나중에 보상해야 하는지 드러난다.

중앙에서 순서를 관리하고 싶다는 판단은 여기서 나올 수 있다. 하지만 그 판단과 Temporal 선택은 별개다. 먼저 서비스 경계를 조정해 하나의 로컬 트랜잭션으로 문제를 줄일 수 있는지 살펴봐야 한다. 그것이 어렵다면 부분 성공을 어떻게 수습할지, 중간 상태를 어디까지 허용할지 정한다. 도구는 그 다음 결정이다.

이해 확인

예약 API의 응답을 받지 못했을 때 예약 실패라고 바로 기록하면 어떤 상태를 놓칠 수 있을까? 또 이 예제에서 완료 상태를 표시하기 전에 반드시 확인해야 할 외부 결과는 무엇일까? 답을 기술 용어 없이 업무 상태로 설명하면 다음 글의 서비스 경계 검토로 넘어갈 준비가 된 것이다.

참고 자료



댓글