1편에서는 프로젝트를 진행한 배경과 전체 구조를 정리했다. 이번 글에서는 수집한 리뷰 데이터를 바탕으로 광고 의심 리뷰가 어떤 특징을 가지는지 확인하고, 이를 모델링에 활용할 변수로 정리한 과정을 다룬다.
이번 단계의 핵심은 단순한 데이터 탐색이 아니다.
네이버 플레이스 리뷰 데이터에는 광고 여부를 나타내는 정답 라벨이 존재하지 않기 때문에, 먼저 광고 리뷰를 설명할 수 있는 패턴을 확인하고 분류 기준으로 연결하는 과정이 필요했다.
1. 분석 목적
1.1. EDA 목적
이번 EDA의 목적은 광고 리뷰와 일반 리뷰를 구분할 수 있는 기준을 찾는 것이다.
리뷰 데이터에는 광고 여부에 대한 정답 라벨이 없기 때문에 바로 모델을 적용하기보다
먼저 광고 리뷰가 어떤 형태를 가지는지 확인하는 과정이 필요했다.
구체적으로는 다음 질문에 답하고자 했다.
- 광고 리뷰는 일반 리뷰와 어떤 차이를 가지는가
- 그 차이는 텍스트 안에서 나타나는가
- 작성자 행동 정보에서도 차이가 나타나는가
- 어떤 변수가 실제 분류에 활용될 수 있는가
1.2. 분석 방향
이번 분석은 광고 리뷰를 단순 키워드 문제로 보지 않고 문장 패턴과 작성자 행동 패턴을 함께 확인하는 방향으로 진행했다.
즉, 리뷰 본문에 나타나는 표현 방식뿐 아니라 리뷰 작성자의 활동 정보까지 함께 고려해 광고 의심 패턴을 확인하고자 했다.
2. 데이터 확인
이번 프로젝트에서는 Selenium을 활용해 네이버 플레이스 음식점 리뷰를 수집했다. 리뷰 본문만 가져온 것이 아니라 음식점 정보, 작성자 정보, 방문 정보까지 함께 저장하도록 구성했다. 실제 크롤링 코드에서는 아래와 같은 형태로 row 데이터를 만든다.
row_data = {
"region_name": region_name,
"place_name": place_name,
"user_nickname": nick,
"user_review_cnt": rev_cnt,
"photo_cnt": photo_cnt,
"review_text": content,
"review_tags": tags,
"visit_date": v_date,
"visit_cnt": v_cnt,
"attached_photo_cnt": attached_photo_cnt,
}
2.1. 주요 변수 구성
수집한 데이터는 크게 두 가지로 나눌 수 있다.
텍스트 정보
- 리뷰 본문
- 리뷰 태그
- 음식점명
행동 정보
- 작성자의 누적 리뷰 수
- 작성자의 누적 사진 수
- 방문 횟수
- 첨부 사진 수
즉, 이번 데이터는 “무슨 말을 했는가”와 “어떤 사용자가 남긴 리뷰인가”를 함께 볼 수 있는 구조였다.
3. 가설 설정
EDA를 시작하기 전에 먼저 광고 리뷰에 대한 가설을 세웠다. 이 과정에서는 팀원들과 함께 실제 수집 리뷰를 살펴보며 광고 의심 리뷰와 일반 리뷰의 차이를 정리했고 네이버 플레이스의 블로그 리뷰나 체험단 후기에서 자주 보이는 표현도 함께 참고했다.
3.1. 리뷰 길이 가설
광고 리뷰는 단순 감상보다 설명과 안내를 함께 포함하는 경우가 많기 때문에, 일반 리뷰보다 문장이 길 가능성이 높다고 봤다.
3.2. 강조 표현 가설
광고 리뷰는 아래와 같은 강조 표현이 반복될 가능성이 높다고 판단했다.
- 완전
- 정말
- 너무
- 진짜
- 최고
- 강추
- 느낌표
- 반복 웃음 표현
즉, 단순 긍정 감상보다 과장되거나 적극적인 문체가 나타날 수 있다고 가정했다.
3.3. 음식점명 반복 가설
실제 후기에서는 음식점명을 한 번만 쓰고 “여기”, “이 집”처럼 자연스럽게 바뀌는 경우가 많다.
반면 광고 리뷰는 음식점명 자체를 반복적으로 노출할 가능성이 있다고 보았다.
3.4. 안내형 문장 가설
광고 리뷰는 후기보다 방문 가이드에 가까운 문장 구조를 가질 수 있다고 봤다.
예를 들어 다음과 같은 표현이다.
- 주차 가능
- 예약 필수
- 웨이팅 있음
- 오시는 길
- 꿀팁
- 참고사항
3.5. 행동 정보 가설
작성자의 활동 정보에서도 차이가 나타날 수 있다고 판단했다.
- 리뷰를 많이 남긴 사용자와 적게 남긴 사용자
- 사진 활동이 많은 사용자와 적은 사용자
- 첫 방문인데도 매우 상세한 리뷰를 남긴 경우
이런 패턴은 텍스트만으로 설명되지 않는 보조 신호가 될 수 있다고 봤다.
4. 데이터 전처리
4.1. 리뷰 길이 확인
먼저 리뷰 본문에서 불필요한 UI 문구를 제거하고 리뷰 길이를 계산했다.
df["review_text"] = df["review_text"].astype(str).str.replace(
r"\s*더보기\s*", "", regex=True)
df["review_len"] = df["review_text"].str.len()
이 과정은 단순 전처리처럼 보이지만 중요했다. “더보기”와 같은 문구가 남아 있으면 실제 리뷰 길이가 왜곡될 수 있기 때문이다.
리뷰를 직접 읽어보면 차이가 비교적 분명했다.
- 짧은 리뷰는 감상 중심으로 끝나는 경우가 많았다
- 긴 리뷰는 메뉴, 분위기, 위치, 주차, 예약 등 설명이 함께 들어가는 경우가 많았다
즉, 광고 의심 리뷰는 일반 후기보다 설명과 정보 전달의 비중이 높은 구조를 가지는 경우가 많았다.
4.2. 강조 표현 확인
문체 차이도 꽤 뚜렷하게 보였다. 광고 의심 리뷰에서는 강조 표현이 반복적으로 등장하는 경우가 많았다.
EMPHA_RE = r"(완전|정말|너무|진짜|대박|최고|레전드|무조건|강추)"
df["empha_cnt"] = (
df["review_text"].fillna("").astype(str)
.str.replace(r"\s+", "", regex=True)
.str.count(EMPHA_RE)
)
df["excl_cnt"] = df["review_text"].str.count(r"!+")
df["laugh_cnt"] = df["review_text"].str.count(r"(ㅋ{2,}|ㅎ{2,})")
여기서 중요했던 건 긍정 표현 자체보다 강조형 어휘가 반복적으로 누적되는 방식이었다. 실제 리뷰도 충분히 긍정적일 수 있지만 광고 리뷰는 톤 자체가 더 적극적이고 과장된 경우가 많았다.
4.3. 음식점명 반복 확인
광고 의심 리뷰에서는 음식점명이 반복적으로 등장하는 경우가 눈에 띄었다.
이를 반영하기 위해 음식점명에서 핵심 키워드를 추출하고 리뷰 본문에서 등장 횟수를 계산했다.
df["place_name_core"] = df["place_name"].apply(place_core_regex)
review_ns = df["review_text"].fillna("").astype(str).str.replace(r"\s+", "", regex=True)
core_ns = df["place_name_core"].fillna("").astype(str).str.replace(r"\s+", "", regex=True)
df["place_name_cnt"] = [txt.count(core) if (core and len(core) > 1) else 0 for txt, core in zip(review_ns, core_ns)]
이 변수는 단순 단어 빈도보다 의미가 있었다.일반 리뷰는 경험 중심으로 흐르지만 광고 리뷰는 매장 중심 서술을 하는 경우가 많기 때문이다.
4.4. 안내형 문장 확인
광고 리뷰를 읽다 보면 후기라기보다 설명문처럼 느껴지는 경우가 있었다. 이를 반영하기 위해 안내형 표현을 변수로 정리했다.
GUIDE_RE = r"(오는방법|오시는길|길찾기|지도|주차|예약|웨이팅|대기|시간대|팁|꿀팁|참고)"
df["guide_cnt"] = (
df["review_text"].fillna("").astype(str)
.str.replace(r"\s+", "", regex=True)
.str.count(GUIDE_RE))
이 변수의 핵심은 단순 키워드 수가 아니다. 리뷰가 경험 공유형인지 방문 유도형인지를 간접적으로 보여준다는 점이 중요했다.
4.5. 태그 및 이모지 확인
리뷰 본문 외에도 태그와 이모지 사용 여부를 함께 확인했다.
df["tags_cnt"] = df["review_tags"].str.split(",").str.len().fillna(0).astype(int)
df["emoji_cnt"] = (
df["review_text"].fillna("").astype(str)
.apply(lambda x: emoji.emoji_count(x)))
이모지와 태그 자체가 광고를 뜻하는 것은 아니다. 다만 광고 리뷰는 분위기나 감정을 더 적극적으로 드러내는 경우가 많아
표현 방식의 적극성을 설명하는 보조 변수로 활용할 수 있었다.
5. 행동 데이터 확인
텍스트만으로는 리뷰 신뢰도를 충분히 설명하기 어렵다고 판단해 작성자 행동 정보도 함께 봤다.
5.1. 작성자 리뷰 수 확인
df["user_review_cnt"] = (df["user_review_cnt"].astype(str).str.extract(r"(\d+)")[0].fillna(0).astype(int))
작성자의 누적 리뷰 수는 활동 이력이 많은 사용자와 적은 사용자를 구분하는 기본 지표로 활용할 수 있었다.
5.2. 작성자 사진 수 확인
df["photo_cnt"] = (df["photo_cnt"].astype(str).str.extract(r"(\d+)")[0].fillna(0).astype(int))
사진 활동 수는 텍스트와는 다른 차원에서 작성자의 활동 수준을 보여줬다.
5.3. 첫 방문 여부 확인
df["is_first_visit"] = (df["visit_cnt"] == 1).astype(int)
특히 “첫 방문인데도 지나치게 상세한 리뷰를 남기는 경우”는 텍스트만 봤을 때보다 행동 데이터를 같이 볼 때 더 해석이 쉬웠다.
6. 광고성 키워드 확인
EDA를 진행하면서 일부 리뷰는 특정 표현만으로도 광고 여부를 강하게 시사한다는 점을 확인했다. 예를 들어 협찬, 리뷰어, 체험단과 같은 표현은 광고성 리뷰의 명확한 신호로 볼 수 있었다.
cleaned = df["corrected_text"].apply(clean_text)
df["ad_cnt"] = cleaned.str.count(r"협찬|리뷰어|체험단").fillna(0).astype(int)
이 과정을 통해 광고 여부가 비교적 명확한 리뷰는 규칙 기반으로 먼저 라벨을 부여하고 그 외 애매한 패턴을 가진 리뷰는 이후 모델이 판단하도록 방향을 정리했다.
7. 피처 정의
이제 EDA에서 확인한 패턴을 실제 변수로 정리한다.
FEATURE_COLS = [
"review_len",
"place_name_cnt",
"user_review_cnt",
"photo_cnt",
"empha_cnt",
"emoji_cnt",
"is_first_visit",
"excl_cnt",
"tags_cnt",
"laugh_cnt",
"sent_score",
]
결국 광고 리뷰는 하나의 단일 기준으로 판단되는 것이 아니라 문장 구조 + 강조 표현 + 안내성 + 작성자 행동 패턴이 함께 결합된 형태로 정의되었다.
8. 정리
이번 글에서는 네이버 플레이스 음식점 리뷰 데이터를 바탕으로 광고 의심 리뷰가 어떤 특징을 가지는지 확인하고 이를 모델링에 활용할 변수로 정리하는 과정을 정리했다.
처음에는 단순히 광고성 키워드를 찾는 문제처럼 보였지만 실제 데이터를 읽어보면 광고 리뷰는 훨씬 복합적인 특징을 가진다.
문장 길이, 강조 표현, 음식점명 반복, 안내형 문장, 작성자 활동 패턴 등 여러 요소가 함께 작용한다는 점을 확인할 수 있었다.
다음 글에서는 이번 EDA에서 정의한 변수들을 바탕으로 어떤 방식으로 라벨링 기준을 만들고 실제 모델링을 진행했는지 정리해보려고 한다.
'Project' 카테고리의 다른 글
| [Kaggle] IEEE-CIS Fraud Detection - 모델링 (3) (0) | 2026.01.13 |
|---|---|
| [Team Project] 네이버 플레이스 광고/체험단 리뷰 판별 모델 개발 - 아이디어 선정 (1) (0) | 2026.01.08 |
| [Kaggle] IEEE-CIS Fraud Detection - EDA (2) (0) | 2026.01.08 |
| [Kaggle] IEEE-CIS Fraud Detection - 이상거래 탐지 문제 정의와 데이터 구조 (1) (0) | 2026.01.08 |
HELLO WORLD
안녕하세요. 데이터로 말하는 분석가 모모입니다.
데이터를 구조화하고 분석하는 과정과 실무에 활용되는 도구 중심의 내용을 기록합니다.