상세페이지 카피를 쓸 때마다 감으로 문구를 고르는 게 늘 마음에 걸렸다. “촉촉한”, “가성비”, “선물용으로 딱” 같은 표현이 정말 잘 먹히는 건지, 아니면 그냥 내가 자주 봐서 익숙한 건지 구분이 안 됐다. 그래서 이번엔 감을 데이터로 바꿔봤다. 사람들이 리뷰에 실제로 많이 쓰는 긍정 표현을 형태소 분석으로 세어보고, 그중에서 카피 후보를 뽑는 작업이다.

먼저 분명히 해둘 게 있다. 이 글의 모든 리뷰·수치·표는 합성(더미) 데이터다. 실제 상품이나 고객 리뷰가 아니라, 설명을 위해 내가 지어낸 가짜 문장들이다. 브랜드명도 전부 “제품X” 같은 가상 이름으로 바꿨다.

전체 흐름은 어떻게 되나?

이 작업은 “리뷰 텍스트 뭉치”를 넣으면 “카피 후보 표”가 나오는 파이프라인이다. 큰 그림부터 보자.

flowchart LR
  A[리뷰 원문<br/>합성 데이터]:::raw --> B[형태소 분석<br/>KoNLPy]:::proc
  B --> C[명사·형용사만<br/>추출]:::proc
  C --> D[감성 사전과<br/>매칭]:::proc
  D --> E[빈도 x 감성<br/>2축 랭킹]:::proc
  E --> F[카피 후보 표]:::out
  classDef raw fill:#fff3bf,stroke:#e67700,color:#8a5a00;
  classDef proc fill:#e7f5ff,stroke:#1c7ed6,color:#10548f;
  classDef out fill:#d3f9d8,stroke:#2f9e44,color:#1d6b2c;

핵심은 가운데 세 단계다. 리뷰를 단어로 쪼개고, 그중 의미 있는 품사만 남기고, 긍정 감성을 띤 표현에 가중치를 준다. 그러면 “많이 나오면서 동시에 긍정적인” 표현이 위로 떠오른다.

형태소 분석이 왜 필요한가?

한국어는 띄어쓰기만으로는 단어를 제대로 못 나눈다. “촉촉해서”, “촉촉하고”, “촉촉한”은 사람 눈엔 다 같은 말이지만, 컴퓨터가 공백으로만 자르면 전부 다른 단어로 센다. 그래서 형태소(morpheme), 즉 뜻을 가진 가장 작은 말 단위로 쪼개고 기본형으로 묶어주는 도구가 필요하다. 그게 KoNLPy(코엔엘파이)다. 한국어 형태소 분석기 여러 개를 파이썬에서 쓰게 묶어놓은 라이브러리다.

띄어쓰기 방식과 형태소 방식의 차이를 표로 보자.

원문단순 공백 분리형태소 분석 후(명사·형용사)
“향이 은은하고 촉촉해요”향이 / 은은하고 / 촉촉해요향 / 은은하다 / 촉촉하다
”촉촉한 느낌이 좋아요”촉촉한 / 느낌이 / 좋아요촉촉하다 / 느낌 / 좋다
”은은한 향 만족스럽네요”은은한 / 향 / 만족스럽네요은은하다 / 향 / 만족스럽다

공백 분리로는 “촉촉해요”와 “촉촉한”이 따로 세지지만, 형태소 분석을 거치면 둘 다 “촉촉하다”로 합쳐진다. 이렇게 기본형으로 묶어야 빈도수가 의미를 갖는다.

리뷰를 어떻게 단어로 쪼개나?

KoNLPy에는 분석기가 여러 개 들어 있는데, 여기선 Okt(옛 이름 트위터 분석기)를 쓴다. 설치가 비교적 쉽고 신조어에 무던한 편이라 리뷰 텍스트에 무난하다. 아래는 합성 리뷰 뭉치를 넣어 명사와 형용사만 뽑는 예시다.

# 합성(더미) 데이터입니다 — 실제 리뷰가 아닙니다.
from konlpy.tag import Okt
from collections import Counter
 
reviews = [
    "향이 은은하고 촉촉해서 좋아요",
    "촉촉한 느낌이 정말 만족스럽네요",
    "은은한 향 덕분에 선물용으로 딱이에요",
    "가성비 좋고 촉촉한 마무리 마음에 들어요",
    "발림성이 부드럽고 향도 은은해서 재구매합니다",
]
 
okt = Okt()
 
def extract_terms(text):
    # norm=True: 문장을 정규화, stem=True: 기본형으로 환원
    tagged = okt.pos(text, norm=True, stem=True)
    # 명사(Noun)와 형용사(Adjective)만 남긴다
    return [word for word, pos in tagged if pos in ("Noun", "Adjective")]
 
all_terms = []
for r in reviews:
    all_terms.extend(extract_terms(r))
 
freq = Counter(all_terms)
for term, cnt in freq.most_common(10):
    print(f"{term}\t{cnt}")

여기서 두 옵션이 핵심이다. stem=True가 “촉촉한”, “촉촉해서”를 모두 “촉촉하다”로 되돌려주고, 품사 필터가 조사(“이”, “가”)나 어미처럼 뜻 없는 조각을 걸러낸다. 명사는 무엇에 대한 말인지(향, 느낌), 형용사는 어떤 느낌인지(촉촉하다, 은은하다)를 담고 있어서, 카피 재료로는 이 둘이 가장 쓸모 있다.

위 코드를 돌리면 대략 이런 빈도가 나온다(합성 데이터 기준).

표현품사빈도
촉촉하다형용사3
은은하다형용사3
명사3
좋다형용사2
느낌명사1
만족스럽다형용사1
가성비명사1

그냥 많이 나온 단어를 쓰면 되나?

여기서 함정이 하나 있다. 빈도만 보면 “향”, “느낌”, “제품” 같은 밋밋한 명사가 상위를 차지하기 쉽다. 자주 나오긴 하지만 카피로 쓰기엔 밍밍한 말들이다. 카피에 힘을 주는 건 감성을 담은 표현, 즉 “촉촉하다”, “만족스럽다” 쪽이다.

그래서 빈도 하나만 보지 말고 감성 점수라는 두 번째 축을 더한다. 긍정 표현을 모아둔 작은 사전을 만들어, 각 표현이 긍정인지 아닌지 표시해주는 것이다.

flowchart TD
  A[추출된 표현]:::in --> B{감성 사전에<br/>있나?}:::q
  B -->|긍정 목록에 있음| C[감성 점수 +1]:::pos
  B -->|목록에 없음| D[감성 점수 0<br/>중립 취급]:::neu
  C --> E[빈도 x 감성으로<br/>최종 정렬]:::out
  D --> E
  classDef in fill:#e7f5ff,stroke:#1c7ed6,color:#10548f;
  classDef q fill:#fff3bf,stroke:#e67700,color:#8a5a00;
  classDef pos fill:#d3f9d8,stroke:#2f9e44,color:#1d6b2c;
  classDef neu fill:#f1f3f5,stroke:#868e96,color:#495057;
  classDef out fill:#f3f0ff,stroke:#7048e8,color:#4b2fa8;

감성 사전은 거창할 필요 없다. 처음엔 손으로 만든 작은 긍정어 목록으로 시작해도 충분하다. 리뷰를 돌려보며 새로 눈에 띄는 긍정 표현을 계속 추가해 키워가면 된다.

# 합성 긍정 감성 사전 — 실무에선 도메인에 맞게 키워간다
positive_lexicon = {
    "촉촉하다", "은은하다", "만족스럽다", "부드럽다",
    "좋다", "가성비", "재구매",
}
 
def sentiment_score(term):
    return 1 if term in positive_lexicon else 0
 
rows = []
for term, cnt in freq.items():
    s = sentiment_score(term)
    # 빈도와 감성을 곱해 '자주 나오면서 긍정적인' 표현을 위로
    rows.append((term, cnt, s, cnt * s))
 
rows.sort(key=lambda x: (x[3], x[1]), reverse=True)
 
print("표현\t빈도\t감성\t점수")
for term, cnt, s, score in rows:
    print(f"{term}\t{cnt}\t{s}\t{score}")

카피 후보는 어떤 표로 나오나?

빈도와 감성을 곱해 정렬하면, 밋밋한 명사는 감성 점수 0 때문에 자연히 아래로 밀리고 감성 형용사가 위로 올라온다. 합성 데이터로 돌린 최종 표는 이렇게 나온다.

순위표현빈도감성최종 점수카피 활용 예
1촉촉하다313”하루 종일 촉촉하게”
2은은하다313”은은하게 퍼지는 향”
3좋다212(너무 일반적 — 보류)
4만족스럽다111”만족스러운 마무리”
5가성비111”부담 없는 가성비”
-300단독 카피엔 약함
-느낌100단독 카피엔 약함

여기서 사람의 판단이 들어간다. “좋다”는 점수는 높아도 너무 흔해서 카피로는 밋밋하니 보류하고, “촉촉하다”·“은은하다”처럼 구체적인 감각어를 골라 카피 문장으로 다듬는다. 데이터는 후보를 좁혀줄 뿐, 최종 문장은 사람이 쓴다. 이 순서를 지키는 게 중요하다.

이 방법의 한계는 무엇인가?

써보면서 느낀 약점도 솔직히 적어둔다. 과신하면 곤란한 지점들이다.

flowchart TB
  subgraph 주의점
    L1[반어·비꼼 못 잡음<br/>좋다고 썼지만 비판일 수도]:::warn
    L2[형태소 오분석<br/>신조어·오타에 약함]:::warn
    L3[감성 사전 편향<br/>내가 넣은 단어만 긍정]:::warn
    L4[빈도=효과 아님<br/>클릭·전환과는 별개]:::warn
  end
  classDef warn fill:#ffe3e3,stroke:#e03131,color:#a01818;

특히 마지막이 핵심이다. 리뷰에 많이 나온 표현이 곧 잘 팔리는 카피라는 보장은 없다. 이건 “후보를 추리는” 도구지 “효과를 증명하는” 도구가 아니다. 뽑은 카피가 실제로 먹히는지는 상세페이지 A/B 테스트나 클릭률로 따로 확인해야 한다. 감성 사전도 결국 내가 넣은 단어만 긍정으로 잡으니, 주기적으로 리뷰를 다시 훑어 사전을 손봐야 편향이 줄어든다.

정리하면

  • 형태소 분석(KoNLPy)으로 리뷰를 기본형 단어로 쪼개면 “촉촉한/촉촉해서”가 하나로 묶여 빈도가 의미를 갖는다.
  • 빈도만 보지 말고 감성 점수를 곱해 밋밋한 명사를 걸러내고 감각적인 긍정 형용사를 위로 올린다.
  • 나온 표는 카피 후보일 뿐, 최종 문장 선택과 효과 검증(A/B·클릭률)은 사람과 실측의 몫이다.

감으로 고르던 카피를 “왜 이 표현을 골랐는지” 설명할 수 있게 된 것만으로도 충분히 남는 장사였다.