DEV Community

Cover image for LLM으로 광고 카피 100개 + CTR 예측까지 — 운영자용 4단계 워크플로우
HyunSeok Jeong
HyunSeok Jeong

Posted on • Originally published at blog.trysitely.com

LLM으로 광고 카피 100개 + CTR 예측까지 — 운영자용 4단계 워크플로우

"이번 캠페인 카피 30개만 더 뽑아주세요" — 마케터의 단골 주문이었던 이 한 줄이, GPT/Claude 등장 이후 의미가 달라졌어요. 이제 100개도 5분이면 나옵니다. 그런데 정작 광고 매니저에 100개를 다 태우면 학습 분산이 깨지고, 비슷한 카피끼리 서로 잠식해서 결과가 망가져요. 이 글은 LLM으로 양산한 카피를 중복 제거 → 사전 스코어링 → A/B 후보 선별까지 가는 운영자용 4단계 파이프라인입니다.

마케터가 이 글을 읽어야 하는 이유: "LLM으로 카피 뽑기"는 이미 많이들 합니다. 뽑은 다음 어떻게 고르고 어떻게 태우는지를 아는 팀이 실제로 광고 효율을 올립니다. 이 파이프라인을 한 번 세팅해두면 다음 캠페인부터 카피 선별에 드는 시간이 반 이상 줄고, 광고 학습 분배 효율도 높아집니다.

LLM 광고 카피 파이프라인 — 양산, 임베딩 클러스터링, CTR 예측, A/B 선별 4단계
100개를 그대로 태우면 망한다. 클러스터링으로 5~7개 컨셉만 살리고, 과거 CTR로 사전 스코어링한 뒤 상위 20개만 광고에 올린다.

0단계: 왜 100개를 그대로 태우면 안 되나

처음 LLM으로 카피를 100개 뽑아 광고에 태웠을 때의 패턴 — 익숙한 사람 많을 거예요.

  1. 학습 데이터 분산: Meta·Google 광고 매니저는 카피별로 노출을 학습 분배합니다. 100개면 한 개당 노출이 100분의 1로 쪼개져, 통계적 유의에 도달하기 전에 캠페인이 끝남.
  2. 카니발리제이션: 비슷한 카피 5개가 같은 입찰 풀에서 서로 잠식. CPC만 올라가고 CTR은 안 오름.
  3. 품질 편차: LLM이 100개 중 70개는 평이하고 10개는 이상하고 20개만 쓸 만한데, 그걸 사람이 한 번도 안 거른 채 태움.
  4. 브랜드 톤 이탈: "재밌는 카피" 100개 중에 브랜드 가이드를 어기는 게 섞여 있음.

해결은 중간에 필터를 두는 것입니다. 4단계 파이프라인:

📌 파이프라인 한눈에

  1. 생성 — LLM에 페르소나·제약을 박은 프롬프트로 100개 양산
  2. 중복 제거 — 임베딩 cosine similarity로 비슷한 카피 묶어 컨셉 5~7개 추출
  3. 사전 스코어링 — 과거 캠페인 CTR 데이터로 컨셉별 예측 CTR
  4. A/B 후보 선별 — 컨셉당 1~2개씩 골라 총 10~15개만 광고로

1단계: LLM 카피 양산 — 좋은 프롬프트 패턴

핵심은 페르소나 + 제약 + 다양성 강제예요. "광고 카피 100개 줘" 같은 빈약한 프롬프트는 비슷한 100개를 만들어냅니다.

[페르소나]
당신은 30대 워킹맘을 타겟으로 하는 유아용품 브랜드의 카피라이터입니다.
브랜드 톤은 "안심·따뜻함·과장 금지"입니다.

[제품 정보]
- 제품: 무염 유아 간식 X
- USP: 6개월부터 먹일 수 있는 유일한 무염 라이스 스낵
- 가격: 한 박스 12,900원

[제약]
- 길이: Meta 광고 primary text 기준 한 줄 (최대 90자)
- 의문문 금지, 과장된 형용사("최고의", "유일한") 금지
- 가격 명시 금지 (정책)
- 같은 첫 단어로 시작하는 카피가 5개를 넘지 말 것

[요청]
다음 5가지 다른 각도로 각각 4개씩, 총 20개의 카피를 만들어주세요.
1. "안심" 프레임 (성분, 만든 사람)
2. "일상" 프레임 (먹이는 장면)
3. "후기" 프레임 (구매 고객의 목소리)
4. "비교" 프레임 (기존 간식과의 차이)
5. "FOMO" 프레임 (한정·시즌)

각 카피는 번호 + 카피 본문만 출력. 설명 금지.
Enter fullscreen mode Exit fullscreen mode

이렇게 프레임을 5개로 강제하면 LLM이 자연스럽게 다양성을 만들어냅니다. 한 번에 100개를 요청하지 말고 20개씩 5번 돌리면 더 안정적이에요.

운영 단계에서 한 가지 더 — 프롬프트 캐싱(Anthropic의 cache_control, OpenAI의 자동 캐시)을 걸면 페르소나·제약 부분이 재사용되어 토큰 비용이 70~80% 절감됩니다. 하루 수백 캠페인 돌리는 팀에는 필수.

💡 프롬프트 캐싱 한 줄 직관

캠페인마다 페르소나·제품 정보가 동일하다면, 그 부분을 캐시 영역으로 표시. 두 번째 호출부터는 입력 토큰 대신 캐시 히트 토큰으로 카운트되어 비용 1/10 수준.

2단계: 임베딩으로 중복 제거하기

100개 중에 사실 의미가 비슷한 카피가 30~40개씩 묶여 있어요. 사람 눈으로 골라내는 건 비효율이고 일관성도 떨어집니다. 임베딩 + cosine similarity로 자동화합니다.

핵심 수식은 두 카피의 의미적 거리:

distance(c1,c2)=1cos(vc1,vc2) \text{distance}(c_1, c_2) = 1 - \cos(\vec{v}{c_1}, \vec{v}{c_2})

거리 0이면 두 카피 의미가 거의 같음, 1이면 완전히 다른 컨셉. 거리 0.2 이하면 같은 컨셉으로 묶고 대표 1개만 살린다는 룰만 정하면 끝.

100개 카피를 임베딩한 뒤 Agglomerative Clustering(거리 0.2 임계)을 돌리면 보통 다음과 같은 결과가 나와요.

클러스터 카피 수 대표 카피
0 15개 "엄마가 먼저 먹어보고 골랐어요"
1 22개 "6개월부터 시작하는 첫 간식"
2 18개 "쌀로만 만든 진짜 무염"
3 11개 "이유식 졸업 후 어떡할지 고민이라면"
4 14개 "할머니가 좋아하시는 간식"
5 12개 "성분표를 자세히 보세요"
6 8개 "재구매율 높은 비밀"

100개 → 7개 컨셉으로 압축. 다음 단계는 이 7개에 점수를 매기는 일.

⚠️ threshold 잡기

  • 너무 작게(0.1) → 과분할, 같은 의미인데 표현 다른 게 다른 클러스터로
  • 너무 크게(0.4) → 과병합, 의미 다른 카피가 한 묶음으로

실무 기본값 0.2~0.25, 한국어는 0.25~0.30이 잘 맞는 편. 한 번 라벨링한 데이터로 자기 도메인에 맞춰 튜닝하세요.

3단계: 과거 CTR 데이터로 사전 스코어링

컨셉 7개가 나왔으면, 이제 어느 컨셉이 잘 먹힐지 광고로 태우기 전에 점수를 매겨봅니다. 과거 캠페인 데이터가 있으면 가능해요.

핵심 아이디어: 과거 카피들의 임베딩그 카피들의 실제 CTR을 가지고 회귀 또는 nearest-neighbor 모델을 학습. 새 카피의 임베딩을 넣으면 예상 CTR을 출력.

가장 단순한 방법은 k-NN(가장 가까운 이웃) 예측이에요.

CTR^(cnew)=1kiTop-kCTR(ci) \widehat{\text{CTR}}(c_{\text{new}}) = \frac{1}{k} \sum_{i \in \text{Top-}k} \text{CTR}(c_i)

새 카피의 임베딩과 가장 가까웠던 과거 카피 kk 개(보통 5)의 실제 CTR 평균을 그대로 예측값으로 씁니다. 해석이 쉬워서 좋아요. "이 카피는 작년 X 캠페인의 카피 5개와 가장 비슷한데, 그것들 평균 CTR이 1.8이었으니 이번에도 비슷할 것"이라고 회의에서 설명할 수 있죠. 데이터가 800개 이상이면 Gradient Boosting Regressor로 한 단계 정밀도 높일 수 있고요.

7개 컨셉 카피의 예측 결과는 다음처럼 나옵니다.

컨셉 예측 CTR (%) 순위
쌀로만 만든 진짜 무염 2.41 1
엄마가 먼저 먹어보고 골랐어요 2.18 2
재구매율 높은 비밀 2.05 3
6개월부터 시작하는 첫 간식 1.94 4
할머니가 좋아하시는 간식 1.32 5
이유식 졸업 후 어떡할지 고민이라면 1.18 6
성분표를 자세히 보세요 0.94 7

📌 이 점수가 만능이 아닌 이유

과거 CTR로 학습한 모델은 과거 시즌·과거 타겟·과거 크리에이티브 분포에 맞춰져 있어요. 신상 출시처럼 도메인이 바뀌면 예측이 빗나갑니다. 점수는 순위 매기는 보조 도구로 쓰고, 절대값을 믿지는 마세요.

4단계: A/B 후보 선별

7개 컨셉 × 예상 CTR이 나왔으면, 어떻게 광고에 태울까. 실무 가이드:

시나리오 후보 수 분배 전략
주간 예산 100만 원 미만 3~5개 상위 점수 컨셉 위주, 컨셉당 1개
주간 100~500만 원 7~10개 상위 5컨셉 × 2개 변형, 하위 1~2개 챌린저
주간 500만 원 이상 10~15개 모든 컨셉 + 변형, 자동 학습 분배

💡 실험적 후보를 1~2개 섞는 이유

과거 CTR이 낮게 예측된 컨셉도 무조건 떨어진 건 아니에요. 모델이 못 본 세그먼트에서 반응이 좋을 수 있고, 새로운 패턴은 항상 그렇게 발견됩니다. 상위만 추리지 말고 하위 1-2개를 'challenger'로 섞어 학습 데이터에 다양성을 유지하세요.

최종 선별 룰 한 줄: 상위 점수 5개 + 하위 점수에서 무작위 1개 = 총 6개를 광고 매니저에 업로드. 컨셉당 변형 1~2개를 더 만들어 10~12개로 확장하면 학습 분배가 안정적이에요.

5단계: 4단계 파이프라인 한 번에 실행하기 (E2E)

각 단계 코드를 하나로 연결해서 실제로 돌릴 수 있는 흐름을 보여줍니다. 실제 환경에서는 각 함수를 라이브러리로 분리하지만, 전체 흐름을 파악하는 데 의사코드로도 충분합니다.

```python title="ad_copy_pipeline.py"

        seen.add(label); reps.append(copy)
return reps  # 보통 7~12개로 압축
Enter fullscreen mode Exit fullscreen mode

3단계: k-NN CTR 예측

def score_copies(candidates, past_copies, past_ctrs, k=5):
knn = KNeighborsRegressor(n_neighbors=k, metric="cosine")
knn.fit(embed(past_copies), past_ctrs)
preds = knn.predict(embed(candidates))
return sorted(zip(candidates, preds), key=lambda x: -x[1])

4단계: 후보 선별

def select_candidates(scored, top_n=5):
return [c for c, _ in scored[:top_n]] + [scored[-1][0]] # 상위 + 챌린저 1

E2E 실행

if name == "main":
BRIEF = "..." # 1단계 프롬프트
PAST_COPIES = ["엄마가 직접 고른 간식", "아이 건강 먼저"]
PAST_CTRS = [2.1, 1.8]

copies  = generate_copies(BRIEF, n=100)
unique  = deduplicate(copies)
scored  = score_copies(unique, PAST_COPIES, PAST_CTRS)
final   = select_candidates(scored)
print(f"생성 {len(copies)}개 → 압축 {len(unique)}개 → 최종 {len(final)}개")
for c, ctr in scored[:len(final)]:
    print(f"  {ctr:.2f}%  {c}")
Enter fullscreen mode Exit fullscreen mode





```text title="실행 결과 예시"
생성 100개 → 압축 7개 → 최종 6개
  2.41%  쌀로만 만든 진짜 무염
  2.18%  엄마가 먼저 먹어보고 골랐어요
  2.05%  재구매율 높은 비밀
  1.94%  6개월부터 시작하는 첫 간식
  1.32%  할머니가 좋아하시는 간식
  0.94%  성분표를 자세히 보세요  ← 챌린저
Enter fullscreen mode Exit fullscreen mode

해석: 100개 → 7개 컨셉 → 예측 CTR 순위 → 상위 5개 + 챌린저 1개 = 6개만 광고 매니저에 올립니다. 나머지 94개는 "확인 완료 후 제외"된 것이지, 버린 게 아니에요. 다음 캠페인에서 성분표 프레임 카피를 다시 시도할 때 이 데이터가 baseline이 됩니다.

6단계: 운영 팁 — 실무에서 부딪히는 것들

1) 임베딩 모델 선택

OpenAI text-embedding-3-small이 가성비 좋고, 한국어도 충분히 잘 합니다. 더 좋은 한국어 성능이 필요하면 BGE-M3 (오픈소스) 같은 multilingual 모델 시도해볼 만해요.

2) 과거 데이터가 없을 때

신규 브랜드라 과거 캠페인이 없으면, 3단계는 건너뛰고 1~2단계 + 4단계의 편의적 분배(컨셉당 1~2개)만 적용. 첫 한 달 데이터 쌓이면 그때부터 학습.

3) 사람 검수는 빼지 마라

자동화의 마지막 안전장치는 사람 눈입니다. 정책 위반(가격 명시, 비교 광고), 브랜드 톤 이탈은 모델이 못 거르는 게 많아요. 광고 업로드 전에 1분만 훑기 룰 권장.

{/* TODO_HUNY: 본인 팀이 LLM 카피 양산을 실제 운영에 쓰고 있다면, 가장 효과 본 프롬프트 패턴 1개나 가장 황당했던 LLM 실수 1개를 한 단락 추가하면 좋습니다. */}

4) 카피의 다국어 변환은 따로

같은 캠페인을 영어·일본어로 확장할 때, LLM에 "한국어 카피를 영어로 번역해줘"는 잘 안 됩니다. 처음부터 각 언어의 페르소나·제약으로 다시 양산하세요. 번역은 의미는 옮기지만 광고 톤은 못 옮겨요.

마치며

LLM은 카피 작가의 일을 뺏는 도구가 아니라 카피 작가의 손을 100배로 늘리는 도구입니다. 양산은 LLM이 하지만, 컨셉 정의·페르소나 설계·최종 검수는 여전히 사람의 일이에요. 이 4단계 파이프라인을 한 번 만들어두면, 다음 캠페인부터는 카피라이터가 컨셉 5개만 적어줘도 광고에 태울 후보 10~15개가 자동으로 나옵니다.

{/* TODO_HUNY: 본인 팀에서 자동화하고 있다면 운영 스택 한 줄 추가 — "현재 우리 팀은 __를 매주 자동 실행해 __개의 카피 후보를 광고팀에 넘긴다" 식으로. */}

다음에 읽으면 좋은 글:

참고

Top comments (0)