Skip to content

Story: E-12-S-01 키워드 목록 API 개발

메타

항목
Story IDE-12-S-01
EpicE-12 Keyword Analysis
상태ready-for-dev
우선순위P0
규모M (2pt)
담당BE (하록)

사용자 스토리

As a 광고를 운영하는 셀러, I want 캠페인 내 키워드별 성과 데이터를 조회, So that 어떤 키워드가 순이익을 내는지/잃는지 분석할 수 있다.


수락 기준 (Acceptance Criteria)

AC-01: 키워드 목록 조회

항목내용
Given셀러가 캠페인 상세 페이지에 있고, 해당 캠페인에 키워드 데이터가 있음
When키워드 목록 API를 호출 (campaignId, startDt, endDt)
Then키워드별 집계 데이터 반환 (키워드명, 노출수, 클릭수, 광고비, 주문수, 전환매출, 판매량)

AC-02: 순이익 계산 포함

항목내용
Given셀러의 원가(마진율) 데이터가 있음
When키워드 목록 API 응답 생성
Then각 키워드별 순이익 계산하여 반환 (순이익 = 전환매출 - 원가 - 광고비×1.1)

AC-03: 정렬 및 페이지네이션 지원

항목내용
Given키워드 수가 많은 캠페인
WhensortBy, sortOrder, page, size 파라미터와 함께 호출
Then정렬된 결과를 페이지 단위로 반환 (totalElements, totalPages 포함)

AC-04: 원가 미입력 시 처리

항목내용
Given셀러가 원가를 입력하지 않음 (purchaseYn = 'N')
When키워드 목록 API 호출
Then순이익 필드는 null로 반환, ROAS 등 다른 지표는 정상 반환

AC-05: 데이터 없음 처리

항목내용
Given해당 캠페인이 비검색 광고만 운영 (키워드 없음) 또는 기간 내 데이터 없음
When키워드 목록 API 호출
Then빈 배열 반환 (dataList: [], totalElements: 0)

태스크 분해

Task 1: API 엔드포인트 설계 AC-01

  • [ ] 1.1: POST /ads/campaign/{campaignId}/keywords 엔드포인트 생성
  • [ ] 1.2: Request DTO 정의 (startDt, endDt, sortBy, sortOrder, page, size)
  • [ ] 1.3: Response DTO 정의 (아래 스펙 참조)

Task 2: 데이터 조회 로직 AC-01, AC-03

  • [ ] 2.1: CoupangAdKeywordDaily 테이블에서 키워드별 집계 쿼리
  • [ ] 2.2: 정렬 로직 구현 (순이익, 광고비, ROAS 등)
  • [ ] 2.3: 페이지네이션 처리

Task 3: 순이익 계산 로직 AC-02, AC-04

  • [ ] 3.1: 원가 데이터 조회 (SettlementMargin 또는 Product)
  • [ ] 3.2: 키워드별 순이익 계산 (전환매출 - 원가 - 광고비×1.1)
  • [ ] 3.3: purchaseYn = 'N'인 경우 순이익 null 처리

Task 4: 예외 처리 AC-05

  • [ ] 4.1: 키워드 데이터 없음 시 빈 배열 반환
  • [ ] 4.2: 캠페인 ID 유효성 검증
  • [ ] 4.3: API 에러 응답 표준화

Task 5: 테스트 작성

  • [ ] 5.1: 단위 테스트 (계산 로직)
  • [ ] 5.2: 통합 테스트 (API 엔드포인트)

Dev Notes

API 스펙

Request

typescript
POST /ads/campaign/{campaignId}/keywords

{
  startDt: "YYYYMMDD",
  endDt: "YYYYMMDD",
  sortBy: "netProfit" | "adCost" | "roas" | "impressions" | "clicks",
  sortOrder: "asc" | "desc",
  page: number,      // 1부터 시작
  size: number       // 10, 20, 50, 100
}

Response

typescript
{
  totalElements: number,
  totalPages: number,
  pageNo: number,
  pageSize: number,
  summary: {
    totalAdCost: number,
    totalNetProfit: number | null,
    purchaseYn: "Y" | "N",
    excludedCount: number       // 제외키워드 등록 수
  },
  dataList: [{
    keyword: string,
    impressions: number,
    clicks: number,
    adCost: number,
    adCostRatio: number,        // 광고비 비중 (%)
    cpc: number | null,         // 클릭 0이면 null
    orders: number,
    cvr: number | null,         // 클릭 0이면 null
    revenue: number,
    roas: number | null,        // 광고비 0이면 null
    netProfit: number | null,   // 원가 미입력 시 null
    netProfitRatio: number | null,
    quantity: number,
    isExcluded: boolean         // 제외키워드 등록 여부
  }]
}

데이터 소스

sql
-- CoupangAdKeywordDaily 테이블
SELECT
  keyword,
  SUM(exposureCount) as impressions,
  SUM(clickCount) as clicks,
  SUM(adCost) as adCost,
  SUM(orders) as orders,
  SUM(quantity) as quantity,
  SUM(conversionAmount) as revenue
FROM CoupangAdKeywordDaily
WHERE vendorId = :vendorId
  AND campaignId = :campaignId
  AND adDate BETWEEN :startDt AND :endDt
GROUP BY keyword
ORDER BY :sortBy :sortOrder
LIMIT :size OFFSET :offset;

참고 구현

항목경로
기존 캠페인 목록 APIbackend/src/ads/campaign/list
순이익 계산 로직backend/src/ads/profit-calculator

충돌 감지

항목상태설명
기존 코드 충돌✅ 없음신규 엔드포인트
DB 스키마 변경✅ 없음기존 테이블 사용

References

출처경로/링크참조 섹션
Epic Specepic-specs/E-12-keyword-analysis.md로직 2: 데이터 소스
광고분석 화면 구조.context/global/ads-analysis-screen.mdAPI 구조

핸드오프 전 체크리스트

문맥 & 요구사항

  • [x] 사용자 스토리가 명확한가?
  • [x] 수락 기준이 Given-When-Then 형식인가?
  • [x] 모든 엣지 케이스가 정의되었는가?

재해 예방

  • [x] 기존 코드에 유사 기능 확인했는가?
  • [x] 충돌 감지 항목에 ⚠️가 없는가?

AI Agent 최적화

  • [x] Dev Notes가 충분히 상세한가?
  • [x] References가 정확한가?
  • [x] 태스크 분해가 구체적인가?

최종 점검

  • [x] 상태가 ready-for-dev로 설정되었는가?

검증 결과: ✅ PASS 검증일: 2026-01-28


생성일: 2026-01-28생성자: 📋 Penny

장사왕 Product Team