Skip to content

[Feat] ad.picke.store 제휴 광고(쿠팡 파트너스·애드픽) 도메인 및 API - #435

Open
Roy-wonji wants to merge 5 commits into
devfrom
feat/ad-affiliate
Open

[Feat] ad.picke.store 제휴 광고(쿠팡 파트너스·애드픽) 도메인 및 API#435
Roy-wonji wants to merge 5 commits into
devfrom
feat/ad-affiliate

Conversation

@Roy-wonji

Copy link
Copy Markdown

요약

앱 지면에 제휴 광고 배너를 네이티브로 노출하고, 탭하면 외부 브라우저로 제휴 링크에 다이렉트되는 구조를 붙였습니다. 매체는 쿠팡 파트너스와 애드픽 두 곳입니다.

설계 문서: docs/superpowers/specs/2026-09-01-ad-picke-store-design.md

동작

  • 앱이 지면 코드로 소재를 조회해 네이티브로 렌더 (GET /api/v1/ads?slot=)
  • 실제로 화면에 그려진 시점에 노출 집계 (POST /api/v1/ads/impressions)
  • 탭하면 GET /c/{code} → 매체별 추적 파라미터 병합 후 302, 클릭은 비동기 적재
  • 관리자에서 소재 CRUD와 노출/클릭/CTR, 클릭 내역 조회

주요 결정

지면은 테이블이 아니라 enum입니다. 앱이 그릴 줄 모르는 지면을 어드민에서 만들어봐야 아무 일도 일어나지 않습니다. 지면 추가는 어차피 앱 배포와 묶입니다.

노출은 raw 로그로 쌓지 않습니다. 배너가 스크롤에 걸릴 때마다 행이 생기면 금방 수천만 건이 됩니다. 일별 카운터 upsert로 CTR을 뽑는 데 충분합니다.

클릭 로그는 익명입니다. /c/{code}는 외부 브라우저에서 열려 Authorization 헤더가 없습니다. 공개 URL에 사용자 식별자를 실으면 열거 공격과 프라이버시 문제가 생기고, 지면별 CTR과 정산 대조에는 userId가 필요 없습니다.

쿠팡은 subId를 병합하고 애드픽은 pass-through입니다. 쿠팡은 subId로 지면별 실매출이 파트너스 리포트에서 갈립니다. 애드픽은 서브아이디 파라미터 규격을 아직 확인하지 못해 원본 링크를 그대로 넘깁니다. 지면별 성과 분리만 안 될 뿐 노출·클릭·리다이렉트는 정상 동작하고, 규격이 확인되면 AdpickLinkBuilder에 파라미터명만 채우면 됩니다.

곁들여 고친 것

JwtFilter가 SecurityConfig보다 먼저 돌면서 토큰이 없으면 무조건 401을 던지고 있었습니다. 게다가 isWhitelistedstartsWith 매칭이라 "/"를 넣으면 전체 인증이 무력화됩니다. 정확히 일치할 때만 통과하는 EXACT_WHITELIST를 분리해 //error를 넣었습니다.

/error가 빠져 있던 탓에 존재하지 않는 모든 경로가 404 대신 401로 나오고 있었습니다. 실제로 https://ad.picke.store/가 인증 오류 JSON을 반환하던 원인입니다.

Swagger

광고 API는 3. 광고 API 그룹으로 분리했습니다. 기존 사용자 그룹은 FE_USED_OPERATIONS 화이트리스트로 필터링되므로 거기 넣으면 어차피 보이지 않습니다.

배포 전 확인

  • docs/db/20260901_create_ad_tables.sql 실행 필요
  • 지면(AdSlotCode) 목록은 앱팀 확정 후 채워야 합니다. 현재 HOME_FEED, BATTLE_RESULT_BOTTOM 두 개
  • 애드픽 파트너 가입 및 이용정책상 자체 앱 배너 노출 허용 여부 확인 필요

테스트

광고 도메인 테스트 통과 (링크 병합, 게재 기간 필터, 가중 로테이션, 노출 upsert, 만료 코드 처리).

전체 스위트에서 ScenarioAudioPipelineServiceTestScenarioServiceImplTest 2건이 실패하지만, 이 브랜치 변경을 걷어낸 상태에서도 동일하게 실패하는 기존 이슈입니다.

- ad 도메인 신설: 소재(AdCreative), 클릭 로그, 일별 노출 집계
- 앱 API: GET /api/v1/ads (지면별 가중 로테이션), POST /api/v1/ads/impressions
- 클릭 리다이렉트: GET /c/{code} -> 매체별 추적 파라미터 병합 후 302, 클릭 비동기 적재
- 관리자 API: 소재 CRUD, 소재별 노출/클릭/CTR, 클릭 내역 목록
- 쿠팡은 subId를 병합해 지면별 성과를 분리하고, 애드픽은 규격 확인 전까지 pass-through
- ad.picke.store 루트 공개 지면 추가 (매체 심사에서 접속 확인 대상)
- Swagger "3. 광고 API" 그룹으로 분리
- JwtFilter에 정확 일치 화이트리스트를 분리해 루트·/error가 401로 막히던 문제 해결
- 지면을 iOS 실제 화면 기준 5개로 확장하고 CPI 허용 여부를 지면 속성으로 표기
- 애드픽 서브아이디 파라미터를 picke.ad.adpick.sub-id-param 설정값으로 분리
  규격 확인 시 배포 없이 환경변수만 채우면 지면별 성과 분리가 켜진다
- 소재 등록·수정 시 제휴 링크의 lptag를 coupang.partners.id와 대조
  단축 링크에는 lptag가 없으므로 파라미터가 있을 때만 검사한다
- 링크 병합 로직을 AffiliateLinks로 추출해 매체별 빌더가 공유
- 마이그레이션 SQL은 참고용임을 명시 (운영은 ddl-auto=update로 자동 생성)
BattleVoteServiceImpl은 "오늘"을 LocalDate.now(KST)로 판단하는데
테스트는 시스템 기본 시간대의 LocalDate.now()를 썼다.

UTC 러너에서는 15:00 UTC(=KST 자정) 이후로 두 값이 하루씩 어긋나
오늘 배틀이 어제 배틀로 취급되면서 크레딧 차감 분기가 반대로 탔다.
14:42 UTC 실행은 통과하고 15:25 UTC 실행은 실패한 이유다.

테스트도 KST 기준으로 맞춘다. 광고 작업과는 무관한 기존 결함이지만 CI를 막고 있어 함께 고친다.
진입점에서 TimeZone.setDefault(Asia/Seoul)을 하고 있어 런타임 동작은 문제없었지만,
기본 시간대 설정에 기대면 일별 노출 버킷이 엉뚱한 시각에 갈릴 수 있다.
BattleVoteServiceImpl과 같은 방식으로 KST를 명시한다.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant