당근

10 개의 포스트

medium.com/daangn

태그로 필터

daangn

서버를 위한 Redux: Node.js 이벤트 소싱 라이브러리 개발기 | by Tony W | 당근 테크 블로그 | Jan, 2026 | Medium (새 탭에서 열림)

당근 프론트엔드코어 팀은 복잡해지는 내부 도구의 요구사항을 해결하기 위해 기존 CRUD 방식의 한계를 넘어 '이벤트 소싱' 패턴을 도입했습니다. 이를 위해 프론트엔드 개발자들에게 친숙한 Redux의 구조를 서버 환경으로 옮겨온 TypeScript 기반 라이브러리 'Ventyd'를 직접 개발하여 오픈소스로 공개했습니다. 이 방식은 데이터의 현재 상태뿐만 아니라 모든 변경 이력을 보존함으로써 감사 로그, 롤백, 비즈니스 인사이트 추출을 획기적으로 용이하게 만듭니다. **전통적 CRUD 방식의 한계와 이벤트 소싱의 필요성** * 기존 CRUD(Create, Read, Update, Delete) 방식은 데이터의 '최종 상태'만 저장하기 때문에, 어떤 과정을 거쳐 현재 상태에 이르렀는지에 대한 맥락을 파악하기 어렵습니다. * 승인 절차, 수정 기록 관리, 복잡한 롤백 로직 등을 구현하려면 별도의 히스토리 테이블이나 복잡한 상태 컬럼을 추가해야 하며, 이는 코드의 복잡도를 높이고 유지보수를 어렵게 만듭니다. * 이벤트 소싱은 상태를 직접 수정하는 대신 "상태를 변경시킨 모든 이벤트"를 순차적으로 기록하여, 필요할 때마다 이벤트를 재생(Replay)해 어느 시점의 상태든 완벽하게 재구성할 수 있게 합니다. **Redux 패턴을 통한 이벤트 소싱의 이해** * 이벤트 소싱 아키텍처는 프론트엔드 상태 관리 라이브러리인 Redux와 매우 유사한 구조를 가집니다. * Redux의 'Action'은 이벤트 소싱의 'Event'와 대응되며, 'Reducer'는 이전 상태와 이벤트를 결합하여 새로운 상태를 계산하는 핵심 로직 역할을 수행합니다. * 가장 큰 차이점은 Redux가 브라우저 메모리에서 상태를 관리하는 반면, 서버의 이벤트 소싱은 이 모든 이벤트를 데이터베이스에 영구적으로 저장하여 데이터의 영속성과 신뢰성을 보장한다는 점입니다. **TypeScript 퍼스트 라이브러리: Ventyd** * Ventyd는 TypeScript 환경에서 이벤트 소싱을 더 쉽고 안전하게 구현하기 위해 개발되었으며, 강력한 타입 추론을 제공합니다. * **스키마 정의**: `defineSchema`를 통해 발생 가능한 이벤트의 종류와 최종 상태(State)의 형태를 정의합니다. 이때 Valibot, Zod, TypeBox 등 다양한 검증 라이브러리를 선택하여 사용할 수 있습니다. * **리듀서 구현**: `defineReducer`를 사용해 각 이벤트가 발생했을 때 상태가 어떻게 변화하는지 선언적으로 기술합니다. * **유연한 확장성**: 특정 데이터베이스에 종속되지 않도록 설계되어 있으며, 프론트엔드와 백엔드 엔지니어가 공통의 비즈니스 로직 언어(이벤트)로 소통할 수 있는 환경을 제공합니다. 단순히 현재의 데이터 값만 저장하는 것을 넘어, 서비스의 성장 과정과 모든 변경 맥락을 자산으로 남기고 싶은 팀에게 Ventyd 도입을 추천합니다. 특히 Redux에 익숙한 엔지니어라면 낮은 학습 곡선으로도 서버 사이드에 견고한 이벤트 중심 아키텍처를 구축하고, 복잡한 비즈니스 요구사항을 깔끔하게 정리할 수 있을 것입니다.

daangn

당근페이 백엔드 아키텍처가 걸어온 여정. Money라는 하나의 작은 프로젝트부터 수십 개의 서비스를 하나의… | by Jeremy | 당근 테크 블로그 | Jan, 2026 | Medium (새 탭에서 열림)

당근페이 백엔드 아키텍처는 서비스의 급격한 성장과 조직의 확장에 발맞춰 계층형, 헥사고날, 그리고 클린 아키텍처 기반의 모노레포 형태로 끊임없이 진화해 왔습니다. 초기에는 빠른 기능 출시를 위해 단순한 구조를 채택했으나, 비즈니스 복잡도가 증가함에 따라 의존성 관리와 코드 응집도를 높이기 위해 구조적 제약을 강화하는 방향으로 발전했습니다. 결과적으로 아키텍처는 기술적 부채를 해결하는 수단을 넘어, 대규모 팀이 협업하며 지속 가능한 성장을 이뤄낼 수 있는 기반이 되었습니다. ### 초기 성장을 견인한 계층형 아키텍처 (Layered Architecture) * **빠른 실행력 중심:** 2021년 당근페이 출시 초기, 송금 서비스의 신속한 시장 진입을 위해 `Controller-Service-Repository`로 이어지는 직관적인 3계층 구조를 사용했습니다. * **성장통의 발생:** 서비스가 커지면서 송금, 프로모션, FDS 등 다양한 기능이 하나의 계층에 뒤섞였고, 서비스 간 순환 참조와 강한 결합이 발생해 코드 변경의 영향 범위를 예측하기 어려워졌습니다. * **기술 부채의 축적:** 모든 비즈니스 로직에 프레임워크 기술(Spring)이 깊숙이 침투하면서 테스트 작성이 까다로워지고, 순수 도메인 로직만 분리해 관리하기 어려운 구조적 한계에 직면했습니다. ### 구조적 제약을 통한 응집도 향상 (Hexagonal Architecture) * **외부 구현과의 분리:** 도메인 규칙을 중심에 두고 UI, DB, 외부 API 등 인프라 영역을 포트와 어댑터를 통해 분리하여 프레임워크에 의존하지 않는 POJO 중심의 설계를 지향했습니다. * **모듈 역할의 세분화:** 프로젝트를 핵심 규칙을 담은 `domain`, 사용자 시나리오 단위의 `usecase`, 실제 입출력을 담당하는 `adapter` 모듈로 재구성하여 의존성 방향을 한곳으로 모았습니다. * **재사용성과 테스트 용이성:** 유스케이스 단위로 로직이 응집되면서 REST API뿐만 아니라 이벤트 컨슈머, 배치 잡 등 다양한 진입점에서 동일한 비즈니스 로직을 안전하게 재사용할 수 있게 되었습니다. ### 규모 확장에 대응하는 클린 아키텍처와 모노레포 * **모노레포 도입의 배경:** 머니, 포인트, 빌링 등 도메인이 늘어남에 따라 여러 저장소를 관리하는 비용이 증가했고, 이를 효율적으로 통합 관리하기 위해 하나의 저장소에서 여러 서비스를 운영하는 모노레포 구조를 채택했습니다. * **계약 기반의 모듈 분리:** 각 도메인을 `contract(인터페이스)`와 `impl(구현체)` 모듈로 쪼개어 의존성 규칙을 강제했습니다. 다른 모듈은 `contract`만 참조하게 하여 불필요한 내부 구현 노출을 차단했습니다. * **빌드 성능 및 생산성 최적화:** Gradle의 `api`와 `implementation` 구성을 활용해 컴파일 시점의 의존성을 제어함으로써, 대규모 프로젝트임에도 불구하고 빌드 시간을 단축하고 변경 영향도를 최소화했습니다. 아키텍처에는 정답이 없으며, 조직의 규모와 비즈니스의 현재 단계에 가장 적합한 형태를 선택하는 것이 중요합니다. 당근페이의 사례처럼 초기에 과도한 설계를 지양하되, 서비스 성장 속도에 맞춰 구조적 제약을 단계적으로 도입함으로써 기술 부채를 통제하고 개발 생산성을 유지하는 전략을 권장합니다.

daangn

당근의 사용자 행동 로그 관리 플랫폼: 이벤트센터 개발기. 코드로 관리하던 사용자 행동 로그를 플랫폼으로 만든 이유 | by Suyeon Kang | 당근 테크 블로그 | Jan, 2026 | Medium (새 탭에서 열림)

당근은 방대한 사용자 행동 로그를 보다 효율적이고 체계적으로 관리하기 위해 기존의 Git 기반 코드 관리 방식에서 벗어나 UI 중심의 로그 관리 플랫폼인 ‘이벤트센터’를 구축했습니다. 이를 통해 복잡한 JSON 스키마 작성 과정과 수동 리뷰 절차를 자동화하여 데이터 관리 비용을 획기적으로 낮추었으며, 전사적인 로그 컨벤션을 확립해 데이터의 일관성과 분석 편의성을 동시에 확보했습니다. 결과적으로 개발자와 분석가 모두가 데이터 기반의 의사결정에만 집중할 수 있는 환경을 조성하는 데 성공했습니다. **기존 Git 기반 관리 방식의 한계** * **높은 진입장벽:** 새로운 로그 스키마를 추가하기 위해 Spark의 StructType JSON 형식을 직접 코드로 작성해야 했으며, 이는 데이터 엔지니어링 지식이 부족한 구성원에게 큰 부담이 되었습니다. * **비효율적인 프로세스:** 스키마 하나를 추가할 때마다 PR 생성, 데이터 팀의 수동 리뷰, 수정 반복 과정을 거쳐야 했기에 데이터 반영 속도가 느려지는 문제가 발생했습니다. * **일관성 없는 명명 규칙:** 이벤트 이름에 대한 강제적인 컨벤션이 없어 유사한 행동이 서로 다른 이름으로 정의되거나, snake_case와 camelCase가 혼용되는 등 데이터 정합성 관리가 어려웠습니다. **사용자 행동 로그 수집 및 처리 아키텍처** * **실시간 파이프라인:** 모바일 앱 SDK에서 발생한 이벤트는 서버를 거쳐 GCP Pub/Sub으로 전달되며, Dataflow를 통해 유효성 검증, 중복 제거, 데이터 변환(Flatten)이 실시간으로 이루어집니다. * **스키마 기반 자동 테이블 생성:** 이벤트 스키마를 정의하면 BigQuery에 해당 이벤트 전용 테이블이 자동으로 생성되며, JSON 형태의 커스텀 파라미터가 일반 컬럼으로 펼쳐져 저장되어 복잡한 쿼리 없이도 즉시 분석이 가능합니다. * **데이터 신뢰성 확보:** 스트리밍 단계에서의 단기 중복 제거와 배치 단계에서의 시간 윈도우 기반 중복 제거를 병행하여 데이터의 정확도를 극대화했습니다. **이벤트센터를 통한 로그 관리 혁신** * **UI 중심의 스키마 정의:** 코드를 직접 수정하는 대신 웹 인터페이스에서 필드명, 타입, 설명, 오너십 등을 설정할 수 있어 누구나 쉽게 로그를 설계하고 관리할 수 있습니다. * **명격한 컨벤션 적용:** '행동(Action)-서비스(Service)-대상(Object)' 구조의 명명 규칙을 시스템적으로 강제하여 이벤트 검색성을 높이고 중복 정의를 방지했습니다. * **자동화된 유효성 검사:** 스키마 변경 시 발생할 수 있는 오류를 시스템이 사전에 체크하고, 변경 사항을 즉시 데이터 파이프라인에 반영하여 운영 리소스를 최소화했습니다. 데이터의 양이 늘어날수록 로그 관리의 핵심은 '자율성'과 '통제' 사이의 균형을 잡는 것입니다. 당근의 사례처럼 로그 정의 과정을 플랫폼화하고 컨벤션을 시스템으로 강제한다면, 휴먼 에러를 줄이는 동시에 전사 구성원이 데이터라는 공통 언어를 더욱 쉽고 정확하게 사용할 수 있는 환경을 만들 수 있습니다.

daangn

당근은 왜 User Activation을 전사 공통 데이터 레이어로 만들었을까? | by Juyeon Park | 당근 테크 블로그 | Jan, 2026 | Medium (새 탭에서 열림)

당근은 단순한 액티브 유저(Active User) 수치만으로는 파악하기 어려운 사용자 행동의 원인과 흐름을 분석하기 위해 전사 공통 데이터 레이어인 'Activation 레이어'를 구축했습니다. 이를 통해 사용자의 활성 상태와 상태 전이를 일관된 기준으로 정의함으로써 데이터 신뢰성을 확보하고, 팀 간 중복 계산으로 인한 비용과 운영 리소스를 대폭 절감했습니다. 결과적으로 데이터 분석 환경을 쿼리 중심에서 시스템 중심으로 격상시켜 전사적인 의사결정 속도와 정확도를 높였습니다. **단순 지표를 넘어선 User Activation의 중요성** * 단순한 액티브 유저 수는 '무슨 일이 일어났는지'는 보여주지만, '왜' 일어났는지에 대한 해답을 주지 못하므로 유저를 상태별로 쪼개어 보는 관점이 필요합니다. * **활성 상태**: 특정 시점에 유저가 신규(New), 유지(Retained), 복귀(Reactivated), 이탈(Inactive) 중 어떤 상태인지 분류합니다. * **상태 전이**: 기간의 흐름에 따라 유저가 어떤 경로로 이동하는지(예: 유지 → 이탈) 파악하여 활동성 수준에 따른 구체적인 액션을 가능하게 합니다. * 이전에는 팀마다 이 기준을 각자 계산하여 신뢰도가 낮고 운영 안정성이 떨어졌으나, 이를 공통 레이어로 통합하여 해결했습니다. **신뢰성 확보를 위한 기준 행동의 고정** * 단순한 UI 로그(클릭 등)가 아닌, 비즈니스적 의미를 담은 **Fact 모델**을 기준으로 Activation을 계산하도록 설계했습니다. * 로그 내 파라미터에 따라 의미가 달라지는 혼선을 방지하기 위해, 사전에 정제된 Fact 레이어를 입력값으로 사용합니다. * `<fact_name>_activation_<time_grain>`과 같은 엄격한 네이밍 컨벤션을 적용하여 모델 이름만으로도 어떤 행동과 주기(일/주/월)를 기준으로 하는지 누구나 쉽게 알 수 있게 했습니다. **증분 모델(Incremental Model)을 통한 비용 최적화** * 수천만 명의 사용자 데이터를 매일 전체 재처리하는 방식은 비용 소모가 크기 때문에, dbt의 증분 모델 방식을 도입했습니다. * **FirstLast 모델**: 각 유저별 최초/직전/최근 활동일을 별도로 관리하여 전체 이력을 매번 스캔하지 않도록 했습니다. * **Activation 모델**: 당일 활동 유저 정보와 FirstLast 모델을 결합하여 상태와 복귀 간격 등을 계산하고, 결과를 다시 FirstLast 모델에 업데이트하는 순환 구조로 데이터 스캔량을 최소화했습니다. * **Activation Status 모델**: 활동이 없는 유저를 포함한 전체 유저의 현재 상태(특히 이탈 기간)를 관리하여 분석 편의성을 높였습니다. **dbt 매크로를 활용한 생산성 극대화** * 다양한 행동(앱 방문, 게시글 작성 등)과 시간 단위(Daily, Weekly, Monthly)별로 수많은 모델을 직접 구현해야 하는 번거로움을 매크로로 해결했습니다. * 복잡한 상태 계산 로직을 dbt 매크로로 표준화하여, 새로운 Activation 모델이 필요할 때 설정값만 입력하면 자동으로 수십 개의 모델이 생성되도록 자동화했습니다. * 이를 통해 데이터 엔지니어의 반복 작업을 줄이고, 분석가들이 필요할 때 즉시 공통 레이어를 확장할 수 있는 환경을 만들었습니다. 데이터를 단순히 쿼리 결과물로 보는 단계를 넘어, 시스템화된 '인프라'로 구축할 때 비로소 전사적인 데이터 활용도가 극대화됩니다. 당근의 사례처럼 상태 전이와 같은 복잡한 로직을 공통 레이어로 추상화하고 자동화한다면, 분석 효율성을 높이는 동시에 데이터 기반의 의사결정 문화를 더욱 공고히 할 수 있습니다.

daangn

당근페이 AI Powered FDS로 가는 여정: 룰엔진구축부터 LLM 적용까지 | by HyunwooKim | 당근 테크 블로그 | Nov, 2025 | Medium (새 탭에서 열림)

당근페이는 급변하는 이상거래 패턴에 유연하게 대응하기 위해 룰엔진 중심의 FDS를 구축하고, 최근에는 LLM을 결합하여 탐지 정교화와 모니터링 효율성을 극대화하고 있습니다. 초기 룰엔진은 조건, 규칙, 정책의 계층 구조로 설계되어 실시간 탐지와 제재를 가능하게 했으며, 여기에 LLM 기반의 맥락 분석을 더해 검토 시간을 단축하고 판단의 일관성을 확보했습니다. 금융 보안 규제를 준수하면서도 최신 AI 모델을 실무에 적용해 사용자 자산을 보호하는 선도적인 FDS 운영 사례를 제시합니다. **유연한 탐지를 위한 룰엔진의 구조** * 룰엔진은 조건(빌딩 블록), 규칙(조건의 조합), 정책(규칙의 묶음)의 3단계 계층 구조로 설계되어 레고 블록처럼 탐지 로직을 조립할 수 있습니다. * '가입 후 N일 이내', '송금 횟수 N건 이상'과 같은 개별 임계값을 자유롭게 변경하며 새로운 사기 패턴에 즉각적으로 대응할 수 있는 환경을 마련했습니다. * 이벤트 유입 경로는 즉시 차단이 필요한 '동기 API'와 대량의 이벤트를 실시간으로 분석하는 '비동기 스트림'으로 분리하여 처리 효율을 높였습니다. **룰엔진 기반의 위험 평가 및 사후 처리** * 유입된 모든 거래 이벤트는 설정된 정책과 규칙에 따라 위험 평가를 거치며, 그 결과에 따라 LLM 평가, 고객 서비스팀 알람, 유저 제재 등의 후속 조치가 자동 수행됩니다. * 시스템 도입 후 실시간으로 규칙을 추가하거나 변경하며 사기 트렌드를 빠르게 반영한 결과, 금융 및 수사기관으로부터의 사기 관련 정보 요청 건수가 유의미하게 감소했습니다. * 탐지 로직의 유연화는 단순 차단을 넘어 시스템 전반의 유저 상태 동기화까지 통합적으로 관리할 수 있는 기반이 되었습니다. **LLM 도입을 통한 지능형 FDS로의 진화** * 기존의 수동 검토 방식은 건당 5~20분이 소요되고 담당자마다 판단 결과가 달라질 수 있는 한계가 있어, 이를 해결하기 위해 LLM을 통한 맥락 분석 기능을 도입했습니다. * 전자금융업의 망분리 규제 문제를 해결하기 위해 '혁신금융서비스' 지정을 받았으며, AWS Bedrock의 Claude 3.5 Sonnet 모델을 활용해 보안과 성능을 모두 잡았습니다. * BigQuery의 사기 이력을 Redis에 캐싱하고, 이를 구조화된 프롬프트(XML 태그 및 JSON 형식)에 결합하여 LLM이 사기 여부와 그 근거를 일관되게 평가하도록 설계했습니다. 효율적인 FDS 운영을 위해서는 룰 기반의 명확한 통제와 AI 기반의 유연한 맥락 분석이 조화를 이루어야 합니다. 특히 LLM을 실무에 적용할 때는 규제 준수를 위한 기술적/행정적 준비와 함께, AI가 정교한 판단을 내릴 수 있도록 단계별로 명시적이고 구조화된 프롬프트를 설계하는 과정이 무엇보다 중요합니다.

daangn

당근의 GenAI 플랫폼. 안녕하세요, 당근 Tech Core의 ML Applications팀과… | by Tommy Park | 당근 테크 블로그 | Dec, 2025 | Medium (새 탭에서 열림)

당근은 급증하는 생성형 AI(GenAI) 활용 수요에 대응하기 위해 파편화된 리소스를 통합하고 개발 효율성을 극대화하는 자체 플랫폼을 구축했습니다. LLM Router와 Prompt Studio를 통해 API 관리의 병목을 제거하고, 비개발자도 코드 없이 AI 기능을 고도화할 수 있는 환경을 마련했습니다. 이를 통해 모델 제공사의 장애나 사용량 제한에 유연하게 대처하며 서비스 안정성을 확보하고 조직 전반의 AI 활용 역량을 결집하고 있습니다. **LLM Router를 통한 AI Gateway 통합** * 여러 모델 제공사(OpenAI, Anthropic, Google 등)의 계정과 API 키를 중앙에서 관리하여 보안 우려를 해소하고 운영 프로세스를 간소화했습니다. * 팀별로 분산되어 발생하던 사용량 제한(Rate Limit) 문제를 공유 자원 풀링을 통해 해결하고, 전체 서비스의 비용과 사용량을 한눈에 파악할 수 있는 통합 대시보드를 구축했습니다. * OpenAI 인터페이스를 표준 규격으로 채택하여, 클라이언트가 모델 제공사에 관계없이 동일한 SDK 코드로 다양한 모델을 교체하며 사용할 수 있도록 설계했습니다. **Prompt Studio: 비개발자 중심의 AI 실험 환경** * 엔지니어의 도움 없이 웹 UI에서 프롬프트를 작성하고 테스트할 수 있는 환경을 제공하여 PM 등 비개발 직군의 업무 자율성을 높였습니다. * 수천 개의 테스트셋을 업로드해 결과를 한꺼번에 생성하고 정량적으로 측정하는 평가(Evaluation) 기능을 통해 프롬프트의 품질을 체계적으로 검증합니다. * 버전 관리 기능을 통해 클릭 한 번으로 최신 프롬프트를 실제 서비스에 배포할 수 있으며, 이는 엔지니어의 코드 수정 없이도 빠른 이터레이션을 가능하게 합니다. **장애 대응 및 서비스 안정성 강화** * 모델 제공사 측의 일시적인 오류 발생 시 자동으로 재시도(Retry)를 수행하여 서비스 중단을 최소화합니다. * 특정 리전의 사용량 제한이나 장애 발생 시 자동으로 다른 리전으로 요청을 우회하는 리전 폴백(Region Fallback) 기능을 플랫폼 수준에서 지원합니다. * 개별 서비스 팀이 인프라 장애 대응에 신경 쓰지 않고 비즈니스 로직 개발에만 집중할 수 있는 환경을 조성했습니다. 기업 내 GenAI 도입이 늘어남에 따라 API 키와 프롬프트 관리는 단순한 운영을 넘어 서비스의 안정성과 확장성을 결정짓는 핵심 인프라가 됩니다. 당근의 사례처럼 통합 게이트웨이와 사용자 친화적인 실험 플랫폼을 선제적으로 구축한다면, 개발 부하를 줄이면서도 조직 전체의 AI 활용 노하우를 효율적으로 축적할 수 있습니다.

daangn

당근 검색 엔진, 쿠버네티스로 쉽게 운영하기 2편 — 데이터 노드 웜업 적용 | by Dongsun Shin | 당근 테크 블로그 | Dec, 2025 | Medium (새 탭에서 열림)

당근 검색 플랫폼팀은 쿠버네티스(ECK) 환경에서 Elasticsearch 클러스터를 운영하며, 롤링 리스타트 시 발생하는 레이턴시 급증 문제를 해결하기 위해 '데이터 노드 웜업(Warmup)' 시스템을 구축했습니다. 단순히 Pod가 실행되는 것을 넘어 샤드 복구와 캐시 예열이 완료된 후에만 다음 노드를 재시작하도록 제어함으로써, 피크 타임에도 서비스 영향 없이 안정적인 배포가 가능해졌습니다. 이를 통해 운영자의 모니터링 부담을 제거하고 언제든 안심하고 배포할 수 있는 환경을 마련했습니다. **롤링 리스타트와 콜드 캐시의 위험성** * Elasticsearch는 페이지 캐시, 쿼리 캐시 등 다양한 메모리 캐시에 크게 의존하므로, 재시작 직후 캐시가 비어 있는 '콜드 캐시' 상태에서는 성능이 급격히 저하됩니다. * 쿠버네티스의 기본 롤링 업데이트는 Pod의 준비 상태(Ready)만 확인하고 다음 노드를 재시작하기 때문에, 준비되지 않은 노드에 트래픽이 몰리며 전체 검색 레이턴시가 수 초까지 치솟는 장애가 발생할 수 있습니다. * 노드 한 대가 내려간 동안 남은 노드들이 모든 부하를 감당해야 하며, 복제본(Replica) 샤드가 없는 상태에서 다른 노드에 문제가 생기면 클러스터가 'Red' 상태로 변해 가용성이 무너질 위험이 큽니다. **안전한 배포를 위한 단계별 웜업 전략** * 목표는 배포 중에도 P99 레이턴시를 평소 수준으로 유지하고, 클러스터 상태가 'Yellow'에서 다시 'Green'이 된 것을 확인한 후 다음 단계로 넘어가는 것입니다. * 이를 위해 노드 재시작 후 세 가지 단계를 거칩니다: 1) 데이터 노드가 클러스터에 정상 합류할 때까지 대기, 2) 할당된 샤드들의 데이터 복구(Recovery) 완료 확인, 3) 실제 검색 쿼리를 미리 실행하여 캐시를 채우는 과정입니다. * 특히 샤드 복구가 완료되지 않은 상태에서 웜업을 시작하면 데이터가 없는 상태에서 쿼리를 날리는 꼴이 되므로, 반드시 인덱싱 상태를 모니터링하는 로직이 포함되어야 합니다. **사이드카 패턴 기반의 웜업 시스템 구현** * Elasticsearch 컨테이너와 함께 실행되는 별도의 `warmup-sidecar`를 도입하여 노드의 상태를 정밀하게 추적합니다. * 사이드카는 API를 통해 해당 노드의 샤드들이 모두 'Started' 상태인지 확인하고, 실제 운영 환경에서 발생하는 검색 트래픽(Traffic Replay)을 신규 노드에 미리 쏘아주어 메모리에 데이터를 올립니다. * 이 모든 과정이 완료되어야만 쿠버네티스의 Readiness Probe를 통과하게 설계하여, ECK 오퍼레이터가 노드 웜업이 끝날 때까지 다음 Pod의 재시작을 자동으로 대기하도록 제어했습니다. 대규모 트래픽을 처리하는 상태 기반(Stateful) 시스템에서는 인프라 수준의 단순한 헬스체크만으로는 부족하며, 애플리케이션 내부의 데이터 준비 상태를 고려한 정교한 배포 전략이 필수적입니다. 데이터 노드 웜업 도입으로 배포 시간은 기존보다 길어졌지만, 시간에 구애받지 않고 24시간 언제든 안전하게 시스템을 업데이트할 수 있는 운영 안정성을 확보하게 되었습니다.

daangn

당근 데이터 지도를 그리다: 컬럼 레벨 리니지 구축기. SQL 파싱으로 구축한 컬럼 레벨 데이터 리니지 | by Jin-won Park | 당근 테크 블로그 | Dec, 2025 | Medium (새 탭에서 열림)

당근마켓(당근) 데이터 가치화팀은 데이터의 흐름을 투명하게 파악하여 신뢰성을 높이기 위해 SQL 파싱 기반의 **컬럼 레벨 데이터 리니지(Column-level Lineage)** 시스템을 구축했습니다. 기존의 테이블 단위 추적으로는 해결하기 어려웠던 연쇄 장애 대응과 민감 정보(PII) 관리 문제를 해결하기 위해, 모든 BigQuery 쿼리 로그를 분석하여 데이터 간의 세부 의존 관계를 시각화했습니다. 이를 통해 당근의 복잡한 데이터 생태계에서 변경 영향도를 정교하게 분석하고 장애 복구 시간을 단축하는 성과를 거두었습니다. ### 데이터 흐름의 불투명성으로 인한 문제점 * **연쇄 실패 대응의 어려움**: 특정 테이블의 파이프라인이 실패했을 때 이를 참조하는 하위 테이블들을 즉각 파악할 수 없어, 수동으로 쿼리를 전수 조사하며 문제를 해결해야 했습니다. * **스키마 변경의 불확실성**: 원천 데이터(MySQL 등)의 컬럼을 삭제하거나 타입을 변경할 때, 해당 컬럼을 사용하는 수많은 파생 테이블 중 어떤 곳에 장애가 발생할지 예측하기 어려웠습니다. * **민감 정보 추적 불가**: PII(개인정보)가 여러 가공 단계를 거치며 어떤 테이블의 어떤 컬럼으로 흘러가는지 파악되지 않아 보안 관리 측면에서 한계가 있었습니다. ### 컬럼 레벨 리니지 도입의 기술적 의사결정 * **테이블 레벨의 한계**: BigQuery의 기본 기능을 통한 테이블 단위 추적은 뷰(View)의 기저 테이블을 정확히 파악하기 어렵고, 세부 컬럼의 변화를 감지하지 못하는 단점이 있었습니다. * **오픈소스(OpenLineage) 대비 효율성**: 다양한 조직이 각기 다른 환경(Airflow, 노트북 등)에서 쿼리를 실행하는 당근의 특성상, 모든 환경에 계측 코드를 심는 방식보다는 중앙화된 BigQuery 로그를 분석하는 방식이 운영 부담이 적다고 판단했습니다. * **SQL 파싱 접근법**: 실행된 모든 SQL의 이력이 남는 `INFORMATION_SCHEMA.JOBS` 뷰를 활용하여, 실행 환경과 관계없이 모든 쿼리로부터 의존성을 추출하는 방식을 채택했습니다. ### 시스템 아키텍처 및 추출 프로세스 * **기술 스택**: 대량의 쿼리 병렬 처리를 위해 **Spark**를 활용하고, SQL 파싱 및 AST(Abstract Syntax Tree) 분석을 위해 **sqlglot** 라이브러리를 사용하며, **Airflow**로 주기적인 추출 프로세스를 자동화했습니다. * **데이터 수집 및 분석**: 모든 GCP 프로젝트에서 쿼리 로그를 수집한 뒤, sqlglot으로 쿼리 구조를 분석하여 `Source Column -> Target Column` 관계를 도출합니다. * **엣지 케이스 처리**: `SELECT *`와 같은 와일드카드 쿼리는 테이블 메타데이터를 결합해 실제 컬럼명으로 확장하고, 복잡한 CTE(Common Table Expressions)나 서브쿼리 내의 의존성도 AST 탐색을 통해 정확하게 추적합니다. ### 데이터 지도를 통한 실질적 변화 * **정교한 영향도 분석**: 특정 컬럼 수정 시 다운스트림에서 이를 참조하는 모든 컬럼을 즉시 확인하여 사전에 장애를 예방할 수 있게 되었습니다. * **거버넌스 강화**: 데이터의 원천부터 최종 활용 단계까지의 흐름을 시각화함으로써 데이터 가계도(Data Genealogy)를 완성하고, 데이터 보안 및 품질 관리 수준을 한 단계 높였습니다. * **운영 효율화**: 장애 발생 시 영향 범위를 데이터 지도를 통해 한눈에 파악함으로써 원인 파악과 복구에 소요되는 리소스를 획기적으로 줄였습니다. 데이터 플랫폼의 규모가 커질수록 수동 관리는 불가능해지므로, 초기부터 SQL 로그를 활용한 자동화된 리니지 체계를 구축하는 것이 중요합니다. 특히 실행 환경이 파편화된 조직일수록 애플리케이션 계측보다는 쿼리 엔진의 로그를 파싱하는 접근법이 빠른 도입과 높은 커버리지를 확보하는 데 유리합니다.

daangn

매번 다 퍼올 필요 없잖아? 당근의 MongoDB CDC 구축기 | by Seungki Kim | 당근 테크 블로그 | Dec, 2025 | Medium (새 탭에서 열림)

당근 데이터 가치화 팀은 서비스 성장에 따른 데이터 규모 증가로 기존 MongoDB 전체 덤프 방식이 유발하던 DB 부하와 데이터 적재 지연 문제를 해결하기 위해 Flink CDC를 도입했습니다. 이를 통해 전체 데이터를 매번 조회하지 않고 변경된 로그만 캡처하여 BigQuery로 적재함으로써 DB CPU 부하를 60% 이하로 안정화하고, 2시간 이내 데이터 전달이라는 서비스 수준 목표(SLO)를 달성했습니다. 결과적으로 운영 효율성과 데이터 분석의 실시간성을 동시에 확보하는 파이프라인을 구축할 수 있었습니다. **기술 스택 선정: 왜 Flink CDC인가?** * **MongoDB Change Stream 네이티브 지원**: 별도의 커넥터 개발 없이 MongoDB의 고수준 변경 이벤트 API인 Change Stream을 안정적으로 구독할 수 있으며, resume token과 Flink의 체크포인트 기능을 연동해 장애 시에도 정확한 시점부터 재시작이 가능합니다. * **Exactly-Once 처리 보장**: 분산 파일 시스템에 상태를 주기적으로 저장하는 체크포인트 전략을 통해 장애가 발생하더라도 데이터 중복이나 누락 없이 '정확히 한 번' 처리를 보장합니다. * **통합 파이프라인 구축**: 변경 데이터 추출(CDC)부터 데이터 정제, 변환, BigQuery로의 적재(Sink)까지 하나의 Job 안에서 End-to-End로 처리할 수 있어 운영 복잡도가 낮습니다. * **병렬 처리 기반의 확장성**: TaskManager를 늘려 처리량을 선형적으로 확장할 수 있어, 데이터 이벤트가 폭증하는 상황에서도 유연하게 대응할 수 있습니다. **CDC 기반 아키텍처 및 데이터 흐름** * **Change Stream 활용**: MongoDB의 모든 쓰기 연산을 기록하는 Oplog를 Change Stream을 통해 실시간으로 구독하여 insert, update, delete 이벤트를 수신합니다. * **단계별 배치 파이프라인**: 2시간 이내의 SLO 충족과 운영 안정성을 위해 실시간 스트리밍 대신 매시간(hourly) 배치 방식을 채택했습니다. * **Schema Evolution**: 스키마 저장소와 BigQuery 테이블을 비교하여 변경된 필드를 자동으로 반영합니다. * **Extract & Merge**: 최근 변경 이벤트에서 중복을 제거하고 추출하여 JSON 형태의 Raw 테이블에 병합합니다. * **Materialize**: 최종적으로 스키마를 적용해 사용자가 분석하기 쉬운 테이블 형태로 구체화합니다. * **배치 방식의 이점**: 시간 윈도우를 통해 지연된 이벤트를 안정적으로 회수할 수 있고, 장애 발생 시 실패 구간을 명확히 정의해 재처리(Backfill)하기가 용이합니다. **실용적인 결론** 대규모 트래픽이 발생하는 서비스 환경에서 운영 데이터베이스의 부하를 최소화하면서 분석용 데이터를 확보하려면 CDC 도입이 필수적입니다. 특히 MongoDB와 같이 스키마가 유연한 NoSQL 데이터를 BigQuery와 같은 정형 데이터 저장소로 옮길 때는, Flink CDC와 같은 통합 처리 엔진을 활용해 변환 로직과 확장성을 동시에 확보하는 것이 운영 효율 측면에서 매우 유리합니다. 실시간성이 극도로 중요하지 않다면 배치 단계를 결합해 데이터 정합성과 멱등성을 보장하는 구조를 고려해볼 수 있습니다.

daangn

매번 다 퍼올 필요 없잖아? 당근의 MongoDB CDC 구축기 | by Seungki Kim | 당근 테크 블로그 | Dec, 2025 | Medium (새 탭에서 열림)

당근은 서비스 성장에 따른 데이터 규모 확대와 이로 인한 MongoDB 부하 문제를 해결하기 위해 기존의 전체 덤프 방식 대신 Flink CDC를 도입했습니다. 이를 통해 DB 부하를 60% 이하로 안정화하면서도 2시간 이내 데이터 전달이라는 SLO(Service Level Objective)를 충족하는 성과를 거두었습니다. 결과적으로 확장성 있는 파이프라인을 구축하여 서비스 안정성과 데이터 분석 효율성을 동시에 확보했습니다. **기존 방식의 한계와 CDC 도입 배경** * **성능적 한계:** 기존에는 Spark Connector를 사용해 전체 데이터를 덤프했으나, 데이터가 늘어날수록 DB CPU 사용률이 급증(Spike)하고 적재 시간이 길어지는 문제가 발생했습니다. * **안정성 문제:** 2시간 내 데이터 적재라는 목표를 맞추려면 DB 부하가 너무 커지고, 부하를 줄이면 적재 시간이 지연되는 트레이드오프 상황에 직면했습니다. * **CDC의 필요성:** `updated_at` 같은 특정 필드에 의존하는 증분 적재 방식은 스키마 변경이나 누락에 취약하므로, DB의 변경 로그(Oplog)를 직접 읽어 변경분을 추적하는 CDC 방식이 최적의 대안으로 선정되었습니다. **Flink CDC를 최종 선택한 기술적 이유** * **Change Stream 네이티브 지원:** MongoDB의 Change Stream 기능을 활용해 변경 로그를 안정적으로 읽어오며, resume token과 체크포인트를 연동하여 장애 발생 시에도 중단된 지점부터 정확히 재개할 수 있습니다. * **정확히 한 번(Exactly-Once) 보장:** 강력한 체크포인트 메커니즘을 통해 상태를 외부 스토리지(GCS/S3 등)에 보존하므로 데이터 중복이나 누락 없는 처리가 가능합니다. * **통합 파이프라인 구성:** CDC 데이터 추출부터 변환(Transform), 적재(Sink)까지 하나의 Job 내에서 엔드투엔드(End-to-End)로 처리할 수 있어 운영 복잡도가 낮습니다. * **병렬 처리 기반의 확장성:** TaskManager를 늘림으로써 처리량을 선형적으로 확장할 수 있어, 이벤트가 급증하는 상황에도 유연하게 대응할 수 있습니다. **CDC 기반 데이터 파이프라인 아키텍처** * **실시간 구독 및 적재:** MongoDB에서 발생하는 모든 변경 이벤트(insert, update, delete)를 Flink CDC가 실시간으로 수집하여 BigQuery로 전송합니다. * **효율적인 배치 전략:** 실시간 스트리밍 대신 1시간 단위(Hourly) 배치 방식을 채택하여 시스템 복잡도를 낮추고, 장애 시 재처리의 용이성과 멱등성을 확보했습니다. * **단계별 후처리 프로세스:** 1. **Schema Evolution:** 스키마 저장소와 비교하여 BigQuery 테이블의 필드를 자동 업데이트합니다. 2. **Extract & Merge:** 최신 변경 이벤트를 추출해 중복을 제거하고 원본 형태의 Raw 테이블에 병합합니다. 3. **Materialize:** 최종적으로 스키마를 적용해 분석에 최적화된 테이블로 구체화합니다. 대규모 트래픽 환경에서 운영 DB의 부하를 최소화하면서 데이터 가용성을 높이려면, 무조건적인 전체 조회보다는 CDC를 통한 변경분 추적 방식이 필수적입니다. 특히 데이터 모델 변환이 잦은 NoSQL 환경이라면 Flink CDC와 같은 통합 도구를 활용해 파이프라인을 단순화하고, 서비스의 SLO에 맞춰 배치와 스트리밍 중 적절한 주기를 선택하는 것이 운영 안정성 측면에서 권장됩니다.