data-migration

2 개의 포스트

LINE 앱의 다자간 대화 기능 통합 (새 탭에서 열림)

LINE은 서로 다른 용도로 운영되던 '여러 명과의 대화'와 '그룹' 기능을 '그룹 대화'라는 단일 모델로 통합하여 사용자 경험을 개선하고 시스템 리소스를 효율화했습니다. 기존의 이원화된 구조에서 발생하던 기능 제한과 중복 대화방 생성 문제를 해결하기 위해 통합 API 설계 및 점진적인 데이터 마이그레이션을 수행했습니다. 이를 통해 사용자는 생성 방식에 관계없이 모든 기능을 동일하게 사용할 수 있게 되었으며, 중복 방 생성 비율을 획기적으로 낮추는 기술적 성과를 거두었습니다. ### 이원화된 대화 모델의 한계 * **여러 명과의 대화(Room):** 별도의 승인 없이 즉시 대화가 가능하지만, 일시적 목적으로 설계되어 앨범이나 노트 같은 그룹 전용 기능을 사용할 수 없었습니다. * **그룹(Group):** 초대 승인 절차가 필요한 대신 장기적인 소통에 적합한 다양한 편의 기능을 제공했으나, 초기 진입 장벽이 존재했습니다. * **사용자 혼란 및 리소스 낭비:** 사용자들이 두 모델의 차이를 이해하지 못해 기능이 제한된 방을 잘못 만들거나, 동일한 구성원의 대화방을 중복으로 생성하여 서버와 클라이언트의 리소스가 불필요하게 소모되었습니다. ### 그룹 대화로의 기술적 마이그레이션 * **점진적 API 전환:** 새로운 그룹 대화 API를 설계한 후, '이중 읽기(Dual Read)' 방식을 도입하여 이전 API와의 호환성을 유지하며 단계적으로 전환을 진행했습니다. * **데이터 배치 처리:** 기존의 모든 그룹 데이터를 배치 처리를 통해 신규 모델로 이관하였으며, 안정성이 확인된 후 이중 읽기를 중단하고 그룹 대화 시스템으로 단일화했습니다. * **통합 모델 확립:** 그룹 모델의 아키텍처를 기반으로 여러 명과의 대화 모델을 흡수하여, 향후 추가될 모든 신규 기능이 모든 대화방에 동일하게 적용되도록 구조를 개선했습니다. ### 사용자 경험 최적화 및 운영 성과 * **초대 메커니즘 단일화:** 대화방 생성 UI를 통합하여 '즉시 참여'와 '수락 후 참여' 여부를 사용자가 상황에 맞게 직접 선택할 수 있도록 개선했습니다. * **중복 생성 방지 힌트:** 동일한 구성원으로 새로운 방을 만들려 할 때 기존 대화방을 안내하는 '힌트' 기능을 제공하여 불필요한 대화 목록 생성을 방지했습니다. * **정량적 성과:** 프로젝트 결과, 동일 구성원으로 중복 생성되는 대화방 비율이 기존 15%에서 0.78%로 급감하며 데이터 관리 효율성이 크게 향상되었습니다. 대규모 서비스에서 유사한 기능을 통합할 때는 사용자에게 갑작스러운 변화를 강요하기보다, 점진적인 API 전환과 기능적 일원화를 통해 자연스러운 이동을 유도하는 것이 중요합니다. 이번 통합 사례는 시스템의 복잡성을 줄이면서도 데이터 일관성과 사용자 편의성을 동시에 확보할 수 있는 구체적인 마이그레이션 전략을 보여줍니다.

레거시 결제 원장을 확장 가능한 시스템으로 (새 탭에서 열림)

토스페이먼츠는 20년 된 레거시 결제 원장의 구조적 한계와 도메인 간 강한 결합을 해결하기 위해 MySQL 기반의 신규 원장 시스템을 구축했습니다. 데이터 불변성을 보장하는 INSERT-only 원칙과 이벤트 기반 아키텍처를 도입하여 복합 결제 지원 등 비즈니스 확장성을 확보했습니다. 이 과정에서 발생한 데이터 불일치와 타임아웃 문제를 해결하며 시스템의 자가 회복 능력을 강화하고 안정적인 운영 환경을 마련했습니다. ### 레거시 원장 시스템의 한계와 과제 - **데이터 구조의 불일치:** 결제수단별로 테이블 구조가 다르고, 동일한 성격의 데이터가 서로 다른 테이블에 저장되어 유지보수와 온보딩에 큰 비용이 발생했습니다. - **도메인 간 강한 결합:** 결제, 정산, 회계 등 여러 서비스가 하나의 원장 테이블과 컬럼을 공유하여, 작은 기능 수정 시에도 전사적인 영향도 분석이 필요했습니다. - **구조적 확장성 부족:** 결제와 결제수단이 1:1 관계로 묶여 있어, 더치페이나 복합 결제(카드+포인트)와 같은 현대적인 결제 시나리오를 지원할 수 없었습니다. ### 신규 원장 설계의 3가지 전략 - **데이터 불변성과 일관성:** 모든 승인 내역을 공통 테이블(`approve`)에 저장하고, 수정 대신 INSERT-only 방식을 채택하여 데이터의 정합성을 높이고 데드락을 방지했습니다. - **이벤트 기반의 도메인 분리:** 각 도메인이 직접 DB를 조회하는 대신 Kafka 이벤트를 구독하여 데이터를 처리하게 함으로써 도메인 간 의존성을 제거했습니다. - **결제와 승인 개념의 분리:** '결제'는 주문의 상태를, '승인'은 실제 결제수단의 실행을 의미하도록 분리하여 하나의 결제에 여러 승인 수단이 연결될 수 있는 유연한 구조를 만들었습니다. ### 무중단 마이그레이션 및 정합성 검증 - **비동기 점진적 적재:** 실서비스 장애를 방지하기 위해 기존 원장에 먼저 저장한 후, 신규 원장에는 별도의 ThreadPool을 통한 비동기 방식으로 데이터를 적재했습니다. - **검증 배치 운영:** 비동기 적재 중 발생할 수 있는 누락을 방지하기 위해, 매 5분마다 Read-Only DB를 기반으로 기존 원장과 신규 원장의 데이터를 비교하고 보정하는 배치를 실행했습니다. - **고성능 이관 작업:** 수억 건의 데이터 이관을 위해 Bulk Insert를 도입하고, 네트워크 지연 최소화를 위해 마이그레이션 서버를 DB와 동일한 가용 영역(AZ)에 배치했습니다. ### 운영 중 장애 대응과 시스템 고도화 - **쿼리 최적화:** 옵티마이저의 판단 오류로 발생한 풀 스캔(Full Scan) 문제를 인덱스 힌트(Index Hint) 추가와 롤백 시스템을 통해 빠르게 해결했습니다. - **타임아웃 및 정합성 관리:** MSA 구조에서 서버 간 타임아웃 설정을 일치시키고, 외부 원천사와의 상태 불일치를 해결하기 위한 망취소(Network Cancellation) 로직을 강화했습니다. - **이벤트 처리의 신뢰성:** 아웃박스(Outbox) 패턴과 로그 기반 복구를 통해 이벤트 누락을 방지하고, 헤더에 멱등키를 포함해 중복 이벤트 처리 문제를 해결했습니다. 신규 시스템으로의 전환은 단순한 DB 교체가 아니라 시스템의 지속 가능성을 확보하는 과정입니다. 초기 설계의 완벽함보다 중요한 것은 운영 중 발생하는 예외 상황에 시스템이 스스로 대응하고 회복할 수 있는 '자가 회복 구조'를 갖추는 것이며, 이를 위해 데이터 보정 배치와 로깅 시스템 같은 안전장치를 반드시 고려해야 합니다.