왜 만들었나
앱 마케팅에는 보통 서로 다른 도구가 셋 붙습니다. 사용자 데이터를 모으는 CRM, 광고 성과를 재는 MMP, 메시지를 보내는 발송 도구입니다. 셋이 분리되어 있으면 같은 사람이 도구마다 다른 ID로 존재하고, 광고로 들어온 사람에게 말을 걸려면 데이터를 밖으로 내보내야 합니다.
Danal Pulse는 이 셋을 한 시스템에 둡니다. 그리고 다날의 서버 안에서 돌아갑니다. 결제 사업자로서 다루는 데이터의 성격상, 고객 데이터가 외부 SaaS로 나가지 않는 것이 선택이 아니라 요건이기 때문입니다.
무엇을 하는가
수집
Android · iOS · Flutter · React Native · Web SDK가 이벤트, 프로필, 기기, 구매를 보냅니다. 이벤트는 앱과 환경으로 분리되어 저장됩니다.
식별
익명 ID, 로그인 ID, 기기, 쿠키를 한 사람으로 잇습니다. 식별자는 앱마다 다른 네임스페이스로 토큰화되어 앱 간 대조가 불가능합니다.
귀속 (MMP)
클릭·노출·설치·재방문을 광고 채널에 귀속합니다. 부정 설치는 실시간 차단과 사후 태깅으로 나눠 처리합니다.
세그먼트
행동·속성·귀속·참여 조건을 조합해 오디언스를 만듭니다. 발행된 리비전만 캠페인에 쓸 수 있습니다.
실행
Push, 인앱, 웹푸시, SMS/LMS/MMS, 카카오, 이메일, 웹훅. 단발 캠페인과 다단계 저니를 모두 지원합니다.
측정
발송 → 도달 → 열람 → 전환을 캠페인별로 잇고, 홀드아웃 대조군으로 실제 증분을 봅니다.
구조
세 개의 프로세스와 하나의 데이터베이스입니다.
| 구성 | 역할 | 기술 |
|---|---|---|
danal-pulse-api | 수집 엔드포인트와 관리 API. 콘솔과 SDK가 붙는 곳. | Kotlin · Spring Boot · JDK 25 |
danal-pulse-web | 운영 콘솔. 마케터·운영자가 쓰는 화면. | Next.js · React |
danal-pulse-push-worker | 대량 푸시 발송 전담. API와 별도 프로세스. | API와 같은 산출물, 웹 계층만 끔 |
| MySQL | 모든 상태. 큐도 여기에 있습니다. | MySQL 9.7 |
메시지 브로커가 없습니다. Kafka나 Redis 같은 미들웨어를 두지 않고 DB를 큐로 씁니다. 온프레미스에서 구성요소가 하나 늘어나면 그만큼 운영·공급망 부담이 늘어나는데, 현재 규모에서 그 대가를 치를 이유가 없다고 판단했습니다.
설계 원칙
추정한 값을 관측한 값처럼 보여주지 않습니다
모델이 추정한 수치와 실제로 관측한 수치를 절대 합산하지 않습니다. 재현율처럼 원리적으로 계산할 수 없는 지표는 그럴듯한 숫자를 만들지 않고 null과 함께 그 이유를 반환합니다. 예측값에는 예측이라는 표시가 붙습니다.
동의는 발송 직전에 확인합니다
대상자 명단을 미리 확정해두고 발송하면, 그사이 수신을 거부한 사람에게도 나갑니다. Pulse는 발송 단위마다 자격 조건을 다시 조회하므로 철회가 즉시 반영됩니다. 광고성 메시지의 야간 발송은 별도 동의 없이 차단됩니다.
민감정보는 애초에 받지 않습니다
카드번호·CVC·PIN·인증 비밀정보는 이벤트 페이로드에서 거부됩니다. 고객번호·IP·User-Agent·광고 ID는 원문으로 저장하지 않고 앱별로 분리된 키로 토큰화합니다. 푸시 토큰은 암호화해 보관합니다.
기록은 지우지 않고 덧붙입니다
발송 시도, 콜백 수신, 승인 이력은 append-only입니다. 재발송은 원본을 덮어쓰지 않고 새 시도로 남습니다.
지금 어디까지 됐나
기능설계서가 정의한 요구사항 133개 전부가 구현과 자동화 테스트를 갖췄습니다.
| 구분 | 수 | 의미 |
|---|---|---|
| VERIFIED | 113 (85.0%) | 구현과 테스트 근거가 확인됨 |
| 외부 게이트 대기 | 20 (15.0%) | 코드는 끝났고 외부 시스템 접속만 남음 |
| 미구현 | 0 | — |
다날의 Firebase 프로젝트가 아직 생성되지 않았습니다. 발송 경로는 전부 구현·검증되어 있으나 현재는 DRY_RUN(실제로 보내지 않고 결과만 기록)으로만 동작합니다.
실발송에 필요한 선행 작업: Firebase 프로젝트 생성 → 앱 등록 → 서비스 계정 발급 → 온프레미스 시크릿 배치 → 앱별 자격증명 등록. 이후 발송 모드를 전환하면 됩니다.
12개 운영 릴리스 게이트(사내 IdP 연동, 광고 6개사 인증, 메시지 공급자 실계정, 서명된 실기기 E2E, MySQL 운영 인증, Oracle 연계, DMZ 구성 등)는 아직 하나도 통과하지 않았습니다. 이것들은 코드로 닫을 수 없고, 다날 실환경 자산과 실계정이 있어야 수행할 수 있습니다.
완료율 85%는 개발 지표이지 운영 인증률이 아닙니다. 두 숫자를 섞어 읽으면 안 됩니다.
대량 발송
가장 최근에 보강한 부분입니다. 이전 구조는 수신자를 한 명씩 순차로 보내면서 캠페인 잠금을 발송 내내 붙들고 있어, 대상자가 많아지면 사실상 발송이 불가능했습니다.
현재는 발송 요청이 계획만 하고 즉시 반환하고, 전담 워커가 수신자 구간을 나눠 처리합니다. Firebase 배치 API로 최대 500건씩 묶어 보내고, 쿼터 거부를 만나면 해당 Firebase 프로젝트 단위로 물러섰다가 재개합니다. 진행률·대기 사유·실패 구간은 콘솔에서 실시간으로 볼 수 있고, 잘못 나가는 발송은 킬 스위치로 즉시 멈출 수 있습니다.
다만 처리량 수치는 측정된 적이 없습니다. 실제 성능은 Firebase 프로젝트의 쿼터와 망 구성이 결정하며, 실계정이 생긴 뒤에야 잴 수 있습니다. 그전까지 어떤 성능 수치도 확정으로 기록하지 않습니다.