pile·
백엔드·쏘카쏘카·

팀 레거시 개선 (2) 쏘카존 관리 시스템 - 차량재배치 리팩터링

오래된 차량재배치 시스템은 하나의 CarRelocationProcess 클래스에 Kinesis SDK, Spring ApplicationRunner, 비즈니스 로직이 혼재되어 있었고 테스트 코드도 없었다. 쏘카 자산개발팀이 Kinesis → Spring Kafka 전환을 계기로 전사 표준 스택 통일과 책임 분리 리팩터링을 동시에 진행한 과정을 코드 before/after와 함께 설명한다.

핵심 포인트
  • 문제 1: 거대 단일 클래스에 AWS Kinesis SDK, Spring 생명주기, 비즈니스 로직 혼재 → 수정 범위 불명확, 사이드이펙트 우려
  • 문제 2: 조직에서 Maxwell + Kinesis를 쓰는 곳이 거의 없어 장애 시 참고 문헌 부재, 수동 작업 빈번
  • 해결: Kinesis → Kafka 전환 + 패키지를 app(비즈니스)·infra/kafka(기술)로 명확히 분리
  • 전략 패턴 도입: CarRelocationActionServiceSelector가 상태(CANCEL/DRIVING)에 따라 실행 클래스 반환 → CarRelocationService 변경 없이 상태 추가 가능
  • 효과: 클래스 단위 테스트 작성 용이, 기술 교체 시 비즈니스 로직 불변, 오류 발생 책임 클래스 즉시 파악
상세 정리
  • 기존 코드: CarRelocationProcess 하나에 Kinesis 초기화·샤드 처리·ApplicationRunner·DisposableBean·비즈니스 로직 전부 포함
  • 목표: 비즈니스 로직만 재사용하고 나머지는 분리, 테스트 가능 구조로 변경
  • 패키지 구조: carRelocation/app(message, converter, service, ui) + carRelocation/infra/kafka(message, converter)
  • KafkaListener: Kafka 토픽 메시지 수신 → Kotlin 객체 변환 → 다음 계층 전달
  • KafkaReservationController: 파라미터 유효성 검증 + 비즈니스 DTO 변환
  • CarRelocationService: ReservationDto 상태에 따라 어떤 서비스를 실행할지 결정
  • 테스트 원칙: 각 클래스 책임에 맞는 의미 있는 테스트, 기술 검증용 학습 테스트 별도 분리
  • KafkaReservationControllerTest: 성공·스킵(카프카 오류)·스킵(서비스 스킵) 케이스 커버
  • CarRelocationServiceTest: validate 성공/실패, 취소·운행중 상태별 처리 케이스 커버
  • 전략 패턴 적용 이유: CarRelocationService가 CancelService·DrivingService 직접 참조 → 상태 추가 시 Service도 변경 필요
  • CarRelocationActionServiceSelector: CANCEL → CancelService, DRIVING → DrivingService, 기타 → null 반환
  • 최종 구조: 처리 상태 추가 시 Selector·새 Service 추가만으로 기존 Service 미수정
왜 읽나레거시 거대 클래스를 기술 교체 기회에 책임 분리·전략 패턴으로 리팩터링하는 과정을 Kotlin + Spring Kafka 실제 코드와 테스트 코드로 볼 수 있다.
쏘카
쏘카 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 백엔드·github-engGitHub Engineering·

    조기 종료를 없애야 벡터화된다 — 메모리 속도 소스 코드 케이스 폴딩

    GitHub의 코드 검색 엔진 Blackbird는 480TB 이상의 소스 코드를 인덱싱하기 전 모든 바이트에 case folding을 적용한다. 이 글은 Rust로 구현한 case folding을 메모리 대역폭 한계(45+ GiB/s)까지 끌어올린 두 가지 반직관적 최적화를 상세히 다룬다. 핵심은 루프 조기 종료(break) 제거로 LLVM 벡터화를 유도하고, UTF-8을 디코딩하지 않고 바이트 공간 산술만으로 fold를 수행하는 것이다.

    #rust#unicode#simd+2