기능 개발

행운 캐시 보내기: 단순히 캐시를 선물하는 기능에서 랜덤 캐시 뽑기 기능으로의 변화

DAU 207% 증가와 사용자 참여 985배 향상을 이끌어낸 행운 캐시 시스템 개발

2023-08 ~ 2023-11
4 months
DynamoDBNode.jsFirebase Dynamic LinksAWS CLIAWS Lambda

프로젝트 개요

기존 캐시워크 앱의 친구 간 30 캐시 고정 보상 시스템을 재 개발하여, 랜덤 보상과 시간 제한이 결합된 '행운 캐시' 시스템으로 개편했습니다.

이 프로젝트에서 DynamoDB 설계부터 대용량 트래픽을 견디는 백엔드 아키텍처를 구현하였습니다.

특히 자정 시점의 트래픽 스파이크에 대응하는 스케일링 시스템과 복잡한 NoSQL 데이터 모델링을 처음으로 실제 프로덕션 환경에 적용한 경험이었습니다.

주요 목표

이 프로젝트의 핵심 목표는 다음과 같았습니다:

  1. 사용자 참여도 증가: 고정 보상(30 캐시)을 랜덤 보상으로 전환하여 사용자 참여 유인 강화
  2. 앱 일일 활성 사용자(DAU) 증가: 매일 자정 리셋되는 캐시 발송 제한과 시간 만료 메커니즘을 통하여 매일 접속 유도
  3. 휴면 계정 재활성화: 특별 보상과 자동화된 알림 시스템(유저 활성화 목적)을 통한 장기 미접속 사용자 복귀 촉진

핵심 기능

매일 100명의 친구에게 발송

  • 매일 최대 100명의 친구에게 행운 캐시 발송 가능
  • 발송 횟수는 매일 자정에 초기화
  • 동일 친구에게는 하루 1회만 발송 가능 친구 목록 화면. 친구 이름은 개인 정보로 가렸습니다!친구 목록 화면. 친구 이름은 개인 정보로 가렸습니다!

랜덤 보상 시스템

  • 슬롯 머신 형태의 뽑기 방식으로 캐시 수령
  • 3개의 동일한 금액이 나오면 해당 금액 당첨
  • 다양한 금액대의 랜덤 보상 500 캐시 당첨500 캐시 당첨

미활동 계정 활성화 알림톡 발송

  • 오랜 기간 미접속 사용자 로그인 시 1,000 캐시 특별 보상
  • 재방문 유도를 위한 SMS/카카오톡 자동 알림 발송
  • Dynamic Link 를 통한 원활한 사용자 경험 제공 (현재는 지원 중단된..) 카카오 알림톡카카오 알림톡

시간 제한 시스템

  • 발송된 캐시는 당일 자정까지만 유효
  • 자정 이내 미수령 시 자동 소멸

성과

구현 후 다음과 같은 성과를 달성했습니다:

  • 메시지 발송 유저 수: 255명 → 251,098명 (약 985배 증가)
  • 캐시 전환율(수령 후 재발송): 54% → 91% (68% 증가)
  • 메신저 일일 활성 사용자(DAU): 13만 → 40만 (207% 증가)
  • 재활성화된 휴면 계정: 6,869명

기술 스택

  • 백엔드: Node.js, Nest.js, AWS Lambda (행운 캐시 통계 산출용)
  • 데이터베이스: DynamoDB
  • 알림 시스템: Firebase Dynamic Links, Solapi (카카오톡 메시지 API, SMS)

기술적 도전과 해결책

1. 자정 트래픽 스파이크 관리

자정이 되면 사용자들이 캐시를 보내려고 일제히 앱에 접속하여 DynamoDB 테이블 읽기/쓰기 사용량이 급증하는 현상이 발생하였습니다. DynamoDB Throttle 발생 -> Incremental RetryDynamoDB Throttle 발생 -> Incremental Retry 해결책

  • AWS CLI를 통한 시간 기반 스케일링 정책을 구현하였습니다.
  • 자정 15분 전부터 자정 1시간 후까지 프로비저닝 용량을 자동으로 증가하도록 설정하였습니다. 시간 지정 Scale 정책 추가시간 지정 Scale 정책 추가
// 시간 기반 DynamoDB 스케일링 정책 설정 예시
const scheduledScaling = {
  beforeMidnight: {
    startTime: '23:45',
    readCapacity: 2000,
    writeCapacity: 1000
  },
  afterMidnight: {
    startTime: '00:00',
    endTime: '01:00',
    readCapacity: 3000,
    writeCapacity: 1500
  }
};

2. DynamoDB 데이터 모델링

MySQL과 달리 DynamoDB는 Partition Key와 Sort Key만으로 인덱스를 구성해야 하는 제약이 있습니다.

DynamoDB 설계는 이 페이지에서 더 자세히 확인하실 수 있습니다.

해결책

  • 행운 캐시 기능을 분석하고 액세스 패턴 기반으로 키 구조를 설계하였습니다.
  • 다양한 패턴의 조회를 할 수 있도록 GSI 인덱스를 구성하였습니다.
// 행운 캐시 테이블 키 구조 예시
{
  "from.to": "user123.user456",
  "date": "2024-12-15T14:30:00.000Z",
  "from": "user123",
  "to": "user456",
  "isReceiverActive": true,
  "receivedAmount": 500,
  "receivedType": "WIN_CASH",
  "receivedAt": "2024-12-15T15:10:25.000Z",
  "previousVersion": false
}

3. 앱을 사용하지 않는 유저를 다시 활성화 하기 위해 Dynamic Link를 구현

앱을 사용하지 않는 사용자를 앱으로 효과적으로 유도할 수 있는 유입 경로가 필요하였고, 이를 해결하기 위해 행운 캐시를 보낼 때 카카오 알림톡(또는 SMS) 를 같이 발송하도록 하였습니다.

해결책

  • Firebase Dynamic Links 활용한 서버 측 딥링크 생성 기능을 구현
  • 카카오톡/SMS 알림과 통합하여 효율적인 사용자 재유입 경로 제공

배운 점

기술적 측면

  1. NoSQL 데이터 모델링: 관계형 DB와 다른 DynamoDB의 접근 방식을 이해하고, 쿼리 패턴에 최적화된 설계를 학습했습니다.

  2. 트래픽 관리: 예측 가능한 부하 증가(자정 스파이크)에 대한 자동화된 대응책을 적용하여 예측 가능한 대규모 부하를 관리하는 노하우를 익혔습니다.

회고와 개선점

프로젝트가 전반적으로 성공적이었지만, 몇 가지 개선할 수 있는 부분이 있었습니다:

  1. 사전 부하 테스트: 자정 스파이크에 발생하는 고부하를 사전에 예상했었다면 초기 몇 일간 자정 시간대에 발생한 성능 이슈를 예방할 수 있었을 것입니다.

  2. DynamoDB 테이블 구조 설계:

  • GSI(Global Secondary Index) 를 만들 때 모든 속성을 포함하여 만들었습니다. 이렇게 하니 DDB 에 PutItem 시 모든 인덱스에 Throttle 이 발생하는 문제가 있었고,
  • 인덱스에 사용되지 않을 데이터까지 불필요하게 저장하도록 하였습니다.
  • 당시에는 DynamoDB 가 어떤 특성을 갖는 DB 인지를 알지 못해 이렇게 개발했지만..,
  • GSI 는 사실상 또 다른 테이블과 같기 때문에 쿼리 패턴 측면에서 보다 면밀한 고려가 필요했음을 배울 수 있었습니다.

결론

캐시워크의 행운 캐시 보내기 기능은 기술적으로도 도전적인 프로젝트였으며, 비즈니스 성과 역시 명확하게 드러난 작업이었다고 생각합니다.

이 프로젝트를 통해 저는 DynamoDB의 효율적인 데이터 모델링과 대규모 트래픽 처리를 위한 확장성 있는 시스템 설계 능력을 크게 향상시켰습니다. 특히 자정 시점의 트래픽 스파이크 관리는 예측 가능한 부하 증가에 대응하는 클라우드 아키텍처 최적화의 중요성을 실감하는 계기가 되었습니다.

서버와 클라이언트 간의 API 설계와 의사소통 과정에서의 경험은 기술적 결정이 개발 효율성과 시스템 성능에 미치는 영향을 균형 있게 판단하는 능력을 기르는 데 큰 도움이 되었습니다. 이런 경험은 향후 더 복잡한 백엔드 시스템을 설계하고 구현하는 데 있어 소중한 기반이 될 것이라 생각합니다.