경력기술서
큐피스트
2024.06 ~ 재직 중
[케밋] 알림·스케줄 Worker 아키텍처 재편 및 플랫폼 인프라 고도화
NestJS · Kafka · ECS/Fargate · EventBridge · Datadog · Redis · AWS CDK
2026.03 ~ 2026.05
배경 · 문제
42개 Worker가 단일 ECS Task(4vCPU/16GB)에 묶여 운영되며 다음 문제가 누적된 상태였습니다.
- 모든 워커가 한 Task에서 동시에 부팅돼 기동이 오래 걸리고, 메모리가 부족해 일부 워커가 아예 실행되지 못하는 문제
- Consumer를
cron_restart로 주기마다 강제 재시작해 시간당 약 40회 리밸런싱·메시지 중복이 발생하고, 재시작 시점마다 CPU가 90%까지 치솟는 문제
- 푸시 알림 컨슈머가 단일 인스턴스로만 동작해, 피크 시간대 알림이 몰리면 Consumer LAG가 수만 건까지 쌓이고 해소에 6시간 넘게 걸리는 처리 지연
- idle Batch 프로세스가 약 7.7GB를 상시 점유하는 자원 낭비
접근 · 의사결정
cron_restart 제거 + Graceful Shutdown 래퍼로 Consumer 13개 교체 → 리밸런싱·메시지 중복 해소
- 단일 push consumer에 몰리던 알림을 도메인별(추천/채팅/기타) 전용 ECS 서비스·Kafka 토픽으로 분리 → 도메인 간 간섭 제거, 도메인별 독립 스케일링
- 관계 알림을 CDC 우회 경로에서 Kafka 직접 발행으로 전환 → 불필요한 우회 제거·실시간성 향상
- Batch를 PM2 cron에서 EventBridge + ECS RunTask로 분리, 실행 이력·DLQ·실패 알림 확보
- 알림 Worker에 Consumer LAG 기반 ECS Auto Scaling 적용, 기존 방식에서 새 방식으로의 전환은 롤백을 대비하여 feature flag 기반 무중단 cutover로 진행
결과 · 성과
- 피크 Consumer LAG 37,402 → 778건(약 48배↓), 해소 시간 389분 → 9분
- 리밸런싱 시간당 약 40회 → 0회
- 실측 기반 Worker Task 4vCPU/16GB → 1vCPU/6GB 축소, idle 약 7.7GB 해소
- 인프라 비용 연 약 USD 2,800 절감
- 공통 Worker 운영 구조로 일반화 → 케밋에서 검증 후 타 서비스로 확장 적용 가능한 기반 마련
트러블슈팅 · 배운 점
- push 분리 작업의 무중단 롤백을 위해 둔 feature flag가, 조회될 때마다 설정을 매번 직렬화하면서 CPU를 끌어올려 피크 시간 API latency가 20초까지 치솟던 문제를 발견했습니다 → 즉시 서버 수를 늘려 피크를 넘긴 뒤, 전환이 끝난 flag를 제거해 직렬화 비용을 없애 근본 해결했습니다. 무중단을 위한 안전장치가 역으로 비용이 될 수 있음을 확인했습니다
- Batch command가 완료돼도 커넥션 풀이 이벤트 루프를 붙잡아 ECS task가 종료되지 않고 10분 이상 RUNNING으로 남는 문제를 발견했습니다 → 완료 시 명시적으로 프로세스를 종료하도록 전환하고, at-least-once 트리거에 대비해 중복 실행 방지 락을 함께 적용했습니다
- 배포 중 Batch 종료 로직과 잔존 PM2 cron 설정이 충돌해 재시작 루프가 발생했습니다 → 코드 변경과 운영 설정 변경을 같은 배포 단위로 묶어야 한다는 기준을 세워 재발을 방지했습니다
[케밋] Google·Apple 기반 구독 정기권 시스템 구축
NestJS · PostgreSQL · Redis · Kafka(MSK) · Google Play / App Store API
2025.10 ~ 2025.11
배경 · 문제
일본 진출용 로컬 BM이자 한국에도 공통 적용할 수 있도록, Google·Apple 스토어 기반 월 단위 자동갱신 구독 정기권 시스템을 신규 구축했습니다. 결제 성공·실패·유예·일시중지·만료·취소·환불 등 다양한 구독 상태를 처리하고, 무제한 대화·프로필 보기·좋아요 지급 등 혜택의 지급·동기화를 자동화했습니다.
접근 · 의사결정
- 스토어 실시간 알림(Google RTDN(Pub/Sub), Apple Server Notification·JWS)을 Webhook으로 수신 → MSK publish → Validation Worker가 영수증 검증 후 상태 반영하는 비동기 구조 설계
- Google·Apple의 인증·알림 포맷 차이(Apple JWT(ES256) 직접 생성, JWS의 x5c 인증서 체인을 Apple Root CA까지 검증 후 payload 파싱, Google Pub/Sub push 인증)를 전략 패턴 기반 인터페이스로 분리해 스토어별 정책 변경에 대응
- 알림 중복은 notification 단위 유니크 제약으로, 순서 역전은 이벤트 타임스탬프 비교로 최신 상태만 반영하도록 분리 처리하고, Validation Worker에 멱등성 검증을 적용해 구독 상태 정합성 확보
- Webhook 수신 후 내부 처리 구간은 Outbox로 발행 정합성을 보장하고, 스토어→서버 인바운드 알림 유실은 1일 1회 주기 재검증 Worker로 보완해 이중 안전장치 구성
- 혜택 지급·회수를 자동화하고, 중복 지급은 지급 단위 유니크 제약을 최종 방어선으로 두고 Redis SETNX 분산 락으로 경합을 1차 차단
- 결제 실패 grace 처리와 환불 시 즉시 혜택 종료·소모성 아이템 회수, 사용 후 환불 악용은 이용정지로 대응
결과 · 성과
- Google·Apple 양 스토어 결제·혜택 지급 기반을 신규 구축해 한국·일본 구독 정기권 출시 지원
- Webhook + 주기 재검증 이중화로 스토어 알림 누락 상황에서도 구독 상태 동기화 보장
- 혜택 지급·회수 자동화로 수동 운영 개입을 제거하고, 혜택 누락·중복 지급으로 인한 CS 리스크 감소
트러블슈팅 · 배운 점
- 다운그레이드는 두 가지 이유로 구현이 까다로웠습니다.
- 즉시 적용되는 업그레이드와 달리 다운그레이드는 다음 갱신부터 적용되는 예약 상태를 유지해야 하는데, 두 스토어의 표현 방식이 근본적으로 달랐음 — Apple은 알림 subtype(또는 플랜 tier 비교)으로, Google은 linkedPurchaseToken과 lineItems의 이전 항목 유무로 판정 → 스토어별 신호를
DOWNGRADE/UPGRADE 공통 이벤트 타입으로 변환하고, 다운그레이드는 현재 플랜은 두고 예약 플랜에만 반영해 상위 혜택을 현재 주기 끝까지 유지
- 구독 갱신 주기(샌드박스도 수 시간 단위)를 기다려야 하고 상태 조합이 많은 데다 검증이 Validation Worker에서 비동기로 일어나 자동 재현이 어려웠음 → dev 서버에서 실제 스토어 알림을 흘리며 상태 전이를 로그로 추적해 케이스별로 검증
[케밋] 일본 서비스 출시를 위한 18+ 본인·나이 인증 시스템 구축
NestJS · Redis · PostgreSQL · Kafka(MSK) · Amazon Rekognition · Liquid eKYC API
2025.09 ~ 2025.10
배경 · 문제
일본은 데이팅 앱 이용에 18세 이상 본인·나이 인증을 규제로 요구합니다. 일본 서비스 출시를 위해 Liquid eKYC 기반 인증 결과 조회, 프로필 이미지 비교, 인증 상태 반영으로 이어지는 인증 플로우와, 인증 이벤트 비동기 처리 및 인증 수단·이미지의 완전 삭제까지 포함한 규제 대응 구조를 설계했습니다.
접근 · 의사결정
- 인증 요청 → eKYC 결과 조회 워커 → Amazon Rekognition 기반 프로필·인증 이미지 비교 워커 → 인증 상태 반영·알림으로 이어지는 단계를 MSK 토픽으로 분리한 비동기 파이프라인으로 설계
- 검증된 생년월일로 만 18세 미만을 차단하고, 인증 미완료 유저에게는 채팅 발신 알림을 제어해 규제 요건 충족
- 기존 메시지 처리 구조의 이벤트 유실 가능성을 발견해 Outbox 패턴으로 인증 이벤트 처리 정합성을 확보 — 발행 방식은 사내 CDC 인프라와 Polling Publisher를 비교한 뒤, 인증 이벤트는 빈도가 낮아 CDC 운영 부담 대비 이득이 적다고 판단하여 앱 스케줄러 기반 Polling Publisher로 단순하게 구현
- Liquid 기밀 데이터 삭제 API latency(5~30초)를 고려해 탈퇴 로직과 분리한 비동기 삭제 워커를 두고, Liquid·S3에 저장된 PII를 자동 삭제
- 일본 외 국가 확장을 대비해 country-restricted 가드로 국가별 게이트를 두고, 알림·에러 메시지를 i18n으로 처리
결과 · 성과
- 일본 18세 본인인증 요건을 충족해 서비스 정식 심사 통과에 기여
- Outbox 기반 이벤트 처리 구조로 인증 이벤트 유실 가능성을 제거하고, 운영 기간 내 미처리 이벤트 0건 유지
- 기밀 데이터 자동 삭제 구조를 구축해 일본 규제·감사 대응 체계 마련
[케밋] 관심사 태그 기반 친구 추천 시스템 구현
NestJS · Redis · PostgreSQL · Elasticsearch
2025.05 ~ 2025.06
배경 · 문제
프로필 기반 탐색의 한계를 보완하고 취향 기반 매칭 경험을 강화하기 위해, 관심사·라이프스타일·MBTI·연애성향 등 프로필 태그를 기반으로 친구를 추천하는 탐색 탭을 구현했습니다. 인기 태그 랭킹과 태그별 사용자 추천을 제공해 태그 기반 탐색·매칭 경험을 개선했습니다.
접근 · 의사결정
- 태그 등록·리뷰 카운트를 Redis ZSET(ZINCRBY)으로 실시간 누적하고, 주기 배치 워커가 카테고리별 랭킹을 ZUNIONSTORE로 머지해 인기 태그 목록을 산정
- 태그별 사용자 추천은 매력점수·성별(키)·거리·공통 관심사 등 서로 다른 조건의 후보를 Elasticsearch msearch로 묶어 단일 요청으로 처리
- 추천 결과는 변동이 잦고 캐시 적중률이 낮아 Redis 캐시 대신 Elasticsearch 직접 조회로 단순화
- 일본 진출을 고려해 태그 데이터를 언어별로 분리 관리할 수 있도록 설계
결과 · 성과
- 태그 카운트는 실시간으로 누적하고 랭킹은 주기 배치로 산정하는 구조로, 서로 다른 추천 조건을 단일 요청으로 처리
- Elasticsearch msearch 기반 복합 쿼리로 P95 500ms 달성
- 추천 정확도 개선 A/B 테스트 결과, 추천 경로 좋아요 전송 수 +7%, ARPDAU +12%
트러블슈팅 · 배운 점
- 여러 태그 쿼리를 msearch로 합산할 때 같은 유저가 중복 노출돼 Set으로 중복을 제거했는데, 중복 제거 후 개수로 다음 페이지 존재 여부를 판정하던 탓에 수천 명 규모의 태그에서도 페이지네이션이 조기 종료되는 문제를 발견 → 다음 페이지 판정을 중복 제거 전 원본 쿼리 합계 기준으로 바꿔 해결
[케밋] 위치 기반 동네 탐색 개발
NestJS · Redis · PostgreSQL · Elasticsearch · H3
2025.02 ~ 2025.03
배경 · 문제
지역 기반 만남 수요에 대응하고 가까운 친구 탐색 경험을 제공하기 위해 위치 기반 동네 친구 추천 기능을 구축했습니다. H3 기반 geosharding과 Redis 캐싱으로 위치 데이터를 효율적으로 관리하는 추천 구조를 설계했습니다.
접근 · 의사결정
- 위경도를 H3 hex 셀로 변환해 성별 Redis sorted set에 저장하고, 지역 밀집도에 따라 셀 크기를 분기해 추천 풀의 크기를 조절
- 추천은 Redis에서 인접 hex 후보를 1차로 좁힌 뒤 Elasticsearch에서 선호설정·차단·연결·like 관계를 필터링하는 2단계 구조로 설계
- 이미 추천한 유저는 72시간 동안 재추천에서 제외하고, 정규 추천과 섞이지 않도록 추천 이력을 별도 테이블로 분리
- 추가 추천은 재화 소비로 제공하고, 소비·추천 이력 기록을 트랜잭션으로 묶어 정합성 확보
결과 · 성과
- H3 geosharding과 Redis 캐싱으로 P95 250ms 이하 응답 속도 달성
- A/B 테스트 결과 결제 +12%(p=0.02), D+1 리텐션 +0.7%, 신규 남성 LTV day6 기준 +26.6%
- 위치 기반 동네 탐색 출시 후 DAU 대비 해당 기능 일간 사용률 40% 기록
사내 공통 재화 관리 시스템 재구축
NestJS · Redis · PostgreSQL · S3
2025.04
배경 · 문제
기존 재화 시스템은 유저별 잔액 합계만 보유해 지급 단위별 추적이 불가능했고, 사용 순서·소멸 정책이 없어 미사용 재화가 무한정 쌓이는 회계 리스크가 있었습니다. 적립·사용·소멸 이력을 추적할 수 있는 공통 재화 관리 시스템으로 전면 재구축했습니다.
접근 · 의사결정
- 지급 단위별로 잔여량을 추적하는 상세 이력 테이블을 신설하고, 먼저 지급된 재화부터 차감하는 FIFO 사용 로직을 핵심 도메인 규칙으로 설계(동시간 지급 시 유료 우선)
- 유료 5년·무료 3개월의 소멸 정책을 정책 적용일자를 키로 관리해 향후 정책 변경에 대응하고, 보상 지급 시 만료 시각이 리셋되도록 처리
- 만료 재화 처리는 커서 페이지네이션 기반 배치 워커로 구현하고, 소멸 내역도 사용 이력에 함께 기록해 감사 추적성 확보
- 소멸 사전 고지를 위해 대상 유저를 Redis Set으로 수집하고, 캐시 장애를 대비해 S3로 이중화
- 기존 데이터는 잔액 유무로 분리해 청크 단위로 마이그레이션하고, 잔액이 없는 유저도 빈 이력을 남겨 마이그레이션 누락과 구분할 수 있게 한 뒤 전후 잔액을 검증
결과 · 성과
- FIFO 기반 사용·소멸 정책과 이력 추적 구조를 도입해 감사 대응이 가능한 재화 관리 체계 구축
- 소멸 알림 도입 후 4주간 재화 사용률 약 +15% (도입 직전 동기간 대비)
- 공통 라이브러리로 구현해 멀티 브랜드에서 동일 구조를 재사용할 수 있는 기반 마련
트러블슈팅 · 배운 점
- 잔액이 있어도 소비가 실패하는 정합성 문제가 두 가지 원인으로 발생했습니다.
- 트랜잭션 누락 — 일부 무료 재화 지급 경로에서 합계 증가와 상세 생성이 한 트랜잭션으로 묶이지 않아, 중간 실패 시 합계는 늘었지만 상세가 생성되지 않았습니다 → 지급 경로를 트랜잭션으로 묶어 원자적으로 커밋되게 하고, 상세 생성 함수의 트랜잭션 인자를 필수로 변경해 누락을 차단했습니다
- read replica 지연 — 차감 시 상세를 읽는 조회가 트랜잭션 밖에서는 복제본으로 향해, 지급 직후 소비하면 복제 지연으로 방금 만든 상세가 아직 보이지 않아 차감에 실패했습니다 → 소비 흐름을 트랜잭션 경계로 묶어 같은 커넥션에서 조회와 차감이 일관되게 일어나도록 수정했습니다
[케밋] 서비스 안정화 및 글로벌 확장 대응을 위한 백엔드 개선
NestJS · Redis · PostgreSQL · Elasticsearch · ECS/Fargate · Kafka
2024.06 ~ 진행 중
배경 · 문제
단독 백엔드로 케밋을 운영하며 성능·정합성·악성유저 대응·해외 확장 등 안정화 과제를 지속적으로 처리했습니다. 트래픽 증가와 일본 진출에 맞춰 병목과 운영 리스크를 발견하는 대로 개선했습니다.
성능 · 동시성
- 피크 시간 API 응답이 20초까지 치솟은 장애를 Datadog Continuous Profiler로 근본 원인을 추적해, 유저 수만큼 증폭되던 동기 JSON 딥클론·설정 재파싱이 이벤트 루프를 포화시킨 원인임을 규명하고 제거 → 이벤트 루프 지연 71ms → 0.2ms(약 350배↓) · 처리량 +14.5% 확보
- 이후 같은 응답 지연이 재발했을 때는 원인이 다시 이벤트 루프 포화인데도 CPU·동기 헬스체크로는 정상으로 보여 포화된 task가 죽지 않고 계속 트래픽을 받는 것이 실질 문제임을 규명 → 포화가 CPU가 아닌 이벤트 루프 이용률(ELU)에서만 드러남을 확인해 ELU를 관측 지표로 추가하고, 포화 시 함께 실패하는 헬스체크로 되돌려 문제 task를 조기에 걸러내도록 개선
- 태그 랭킹 조회를 Redis pipeline으로 묶어 라운드트립을 280회에서 4회로 줄여 p50 응답을 약 11배(481ms → 42ms) 단축
- 알림 전송 로직을 개선해 메시지당 DB 호출을 약 400회에서 수 회로(약 98%↓) 줄이고 Worker 메모리 사용을 낮춤
- 좋아요 전송·성향 테스트 저장 등 동시성 이슈를 Redis 분산 락과 원자적 upsert로 해결해 중복 처리·정합성 오류 제거
악성유저 대응
- 악성유저 등록 시 기기 식별자·전화번호를 함께 차단하고 재가입·재로그인 시점에 검증해 번호 변경 우회를 차단
- 피해 유저 보상을 일정 기간 내 상호작용으로 한정하고 지급 이력 플래그로 중복 지급을 제거해 보상 누수를 차단
글로벌 확장 (일본 진출)
- 하드코딩된 리소스를 제거하고 언어코드 기반 리소스 로딩 + 템플릿 치환 구조로 리팩터링해 다국어 처리를 일반화
- 국가별 운영 환경(DB·SMS 전송·리소스)을 분리해 한국·일본을 동시에 서비스할 수 있는 구조 확보
Dev 인프라 (EC2 → ECS)
- 수동 git pull·재기동에 의존하고 API·워커가 한 EC2에 묶여 메모리가 부족하던 dev 환경을, 7개 서비스를 ECS로 옮겨 배포를 자동화하고 리소스를 격리
- 수동 재기동이 사라지면서 백엔드·프론트엔드 모두 서버 접속 없이 배포할 수 있게 되어 팀 배포 편의성과 생산성 개선
포커스미디어코리아
2022.07 ~ 2024.05
엘리베이터 TV 광고 플랫폼 백엔드 시스템 개발 및 운영
Node.js · Spring Boot · DynamoDB · PostgreSQL · Redis
엘리베이터 TV 광고 플랫폼 내재화
- 엘리베이터 TV 광고 송출 플랫폼의 백엔드 시스템을 개발하고, 메시지 큐 기반 Event-Driven 아키텍처로 점진적 전환
- 광고 캠페인 관리 및 송출 API 개발
- 광고 도메인 특성에 맞게 읽기/쓰기 분리 구조를 설계하고, DynamoDB 기반 데이터 모델링으로 잦은 요구사항 변경에 대응
- 외부 업체에 의존하던 엘리베이터 TV 광고 송출 시스템을 사내 내재화하여 플랫폼 개발·운영 주도권 확보
사용자 인증·인가 시스템 개발
- 외부·내부 시스템을 위한 사용자 인증·인가 시스템을 Redis를 주 저장소로 사용하여 구현
- RBAC(Role-Based Access Control)를 직접 구현하여 권한 체계 세분화
- 다중 IdP 연동을 위한 Strategy 패턴 적용
- Redis 휘발성 리스크를 고려한 비동기 데이터 동기화 및 복구 메커니즘 구현
- IdP 인스턴스 싱글톤화 및 Redis 캐시 도입으로 인증 서버 통신 지연 P95 10ms 유지
프로모션 관리 시스템 리뉴얼
- 기존 PHP 기반 이벤트 관리 시스템을 서버리스·이벤트 기반 아키텍처로 전면 리뉴얼
- TypeScript 기반 Command API 및 Spring Cloud Function 기반 Consumer 개발
- React 기반으로 프로모션 관리 페이지와 엔드유저 프로모션 페이지 개발
- 프로모션 관리 자동화로 운영 및 개발 업무 부담 감소