배경 및 목표
플랜 다운그레이드 처리를 담당하는 워커서비스가 NestJS로 운영되고 있었습니다. 해당 서비스는 트랜잭션 경계가 없이 쿼리가 순차적으로 실행되어, 중간 실패 시 데이터 불일치가 발생했고 메시지도 유실되는 구조였습니다.
- Executor들은 트랜잭션 경계 없이 순차 실행 → 3번째에서 실패할 경우 1, 2번은 롤백 불가
- 처리 실패 시 재처리는 메시지 재발행 → 전체 다운그레이드 로직 재실행
- 0건의 테스트 및 유지보수 어려움
목표
- 중간 실패 시 데이터 불일치·메시지 유실이 없는 안정적인 다운그레이드 워커로 전환한다.
- 실패한 스텝만 재처리 가능하고, 테스트로 보호되는 유지보수 가능한 구조를 확보한다.
해결 방법과 해결 후보군
1. 상속을 이용한 도메인별 서비스 분리 + 실패 격리
각 Executor들을 도메인 단위로 재분류하여 Downgrade 인터페이스를 상속한 서비스 클래스들(트랜잭션 경계)로 분리하였습니다. 서비스 단위로 트랜잭션이 보장되도록 실행하여 하나가 실패해도 나머지는 계속 수행합니다.
- 새 도메인 추가 혹은 로직 변경 시 변경 범위 제한
- 실패한 스텝(failSteps)만 재처리하도록 Admin Retry API(DowngradeService 리스트 재활용)
2. AI 협업 개발 파이프라인
이번 신규 서비스 포팅 작업은 클로드를 이용하여 개발 파이프라인을 구상하고, AI와 협업을 하며 실무에 적용해 본 프로젝트입니다.
우선 어떤 파이프라인으로 문제를 해결해나갈지 정의합니다. 각 파이프라인의 스텝이 정하고, 스텝에 맞는 에이전트 역할을 정의하여 결과물을 산출합니다.
| Phase | Claude | 사람 |
|---|---|---|
| 1. 이관 분석 | 21개 Executor SQL/API/데이터 흐름 1:1 매핑, 분석 보고서 | 분석 결과 검증, 비즈니스 맥락 보정 |
| 2. 설계 | 21개 Executor →11개 Service 책임 재분류 초안 | 아키텍처 결정, Feature Flag 전략 수립 |
| 3. 티켓 분할 | 11개 티켓 생성 (AS-IS↔TO-BE 쿼리 테스트 케이스 매핑, Given/When/Then TC) | 티켓 순서/우선순위 판단 |
| 4. TDD | RED: TC → Kotest 변환 / GREEN: 구현 생성 / REFACTOR: 하네스 자동 검증 | AS-IS 동작 일치 여부 검증 |
| 5. 배포 | 전환 체크리스트, 롤백 시나리오 | Flag 전환 시점, 모니터링 지표 판단 |
- 이관 분석을 통해 누락될 수 있는 요소들을 놓치지 않고, 100% 커버
- 작성된 티켓 기반의 TDD 테스트 커버리지(80%+)
- 병렬 작업(티켓별 구현)으로 인한 일정 단축 (주어진 기한 20md → 10md)
결과
| 지표 | 기존 (Node.js) | 개선 (Spring) |
|---|---|---|
| 트랜잭션 관리 | 없음 (fire-and-forget) | 서비스별 독립 트랜잭션 + 실패 격리 |
| 실패 복구 | 전체 재실행 | 실패 step만 선택적 재시도 (Admin API) |
| SQL Injection | 52건 (Raw SQL 파라미터 직접 삽입) | 0건 (QueryDSL 파라미터 바인딩) |
| 테스트 | 0건 | 31개 파일, 커버리지 80%+ |
| 서비스 구조 | 21개 Executor (중복 포함) | 11개 Service (책임 분리, 48% 감소) |
| 기술 스택 | Node.js (NestJS) | Kotlin 단일화 |
| 일정 | - | 20md → 10md |
| 전환 방식 | - | Feature Flag 무중단 전환 (중단 0건) |
모니터링
- 다운그레이드 처리 성공/실패 건수와
failedSteps기록을 관측하고, 재시도 3회 초과 건은 알림으로 escalation한다. - Feature Flag 전환 전후 처리 성공률·에러율을 비교해 무중단 전환을 검증한다.