젠데스크 티켓 분석으로 VOC 대시보드 만들기: OpenAI, BigQuery, Redash

Joshua Kim
IOTRUST : Team Blog
18 min readJul 20, 2024

들어가는 글

고객의 목소리(VOC)는 기업이 프로덕트를 개선하기 위한 중요한 요소입니다. 고객의 의견과 피드백을 팔로업하여 프로덕트를 개선하는 것은 고객 만족도를 높일 뿐 아니라, 나아가 비즈니스 성장을 이끄는 핵심 전략 중 하나인 것이죠.

하지만 수많은 고객 문의 내역을 빠르고 효과적으로 분석하거나, 이를 시각적으로 이해하기 쉽게 만드는 것은 많은 스타트업에게 꽤 도전적인 작업입니다. 텍스트라는 비정형 데이터를 정형 데이터로 변환하는 일이 필요하기 때문입니다.

Source: Lawtomated, Structured vs. Unstructured Data: What are they and why care?, April 2019.

저희 아이오트러스트는 디센트 지갑의 훌륭한 CX(Customer Experience)를 갖추고자 오래 전부터 젠데스크를 도입하여 운영하고 있는데요. 젠데스크를 포함한 CX 플랫폼을 사용하는 기업들에게, 수많은 티켓 데이터를 효율적으로 처리하고 의미 있는 인사이트를 도출하는 프로세스를 만드는 것은 매우 중요한 과제입니다. 그러나 고객 문의 유형을 분류하고 트렌드를 파악하는 일을 수작업으로 하기에는 방대한 작업량과 복잡성으로 인해 어려움이 많은데요. 이에 따라, 자동화된 분석 파이프라인과 시각화 도구의 필요성이 있습니다.

이번 아티클에서는 OpenAI의 언어 생성 모델을 활용하여 젠데스크 티켓 데이터를 분석하고, 이를 기반으로 시각화된 대시보드를 제작한 과정을 소개해드리고자 합니다. 이 글을 통해 단순히 CX팀뿐만 아니라 모든 임직원이 VOC를 한눈에 파악하고, 이를 기반으로 신속하고 효과적인 대응 전략을 수립하는 데 도움을 드릴 수 있기를 바랍니다.

전체 파이프라인

1. 데이터 수집 및 전처리

젠데스크 티켓 데이터를 수집하고 전처리하는 과정은 흔히 Data Ingestion이라고 표현하기도 합니다.

데이터 수집 및 전처리 파이프라인

데이터 수집

Zendesk® Connector

먼저 Google Workspace Marketplace에서 제공 중인 Zendesk Connector를 사용하여 CX팀에서 해결된 젠데스크 티켓 데이터를 사내 비공개 구글 시트에 자동으로 저장하고 관리합니다. 하지만 티켓이 종결 상태가 된 후 일정 기간이 지나면 구글 시트에서 해당 티켓 데이터가 소실되는 문제가 있었는데요. 이러한 데이터 소실을 방지하기 위해 구글 시트의 티켓 데이터를 BigQuery에 주기적으로 적재하도록 환경을 만들었습니다.

  • Python gspread 라이브러리를 통해 구글 시트에 접근하여 티켓 데이터를 주기적으로 가져와 사내 BigQuery의 tickets 테이블에 저장합니다.
  • 만일 티켓 데이터에 민감한 개인정보가 존재할 경우, CX팀에서 반드시 사전에 이를 제거한 후 티켓을 해결하기 때문에 개인정보의 유출은 원천적으로 차단됩니다.

데이터 전처리

전처리는 이후 단계에서의 분석 정확성을 높이고 의미 있는 인사이트를 도출하기 위한 기반을 마련하는 과정입니다. 따라서 Data Ingestion 과정에서 간단한 전처리 과정을 진행하여 데이터 정합성과 분석의 효율성을 확보할 수 있도록 했습니다. 대표적인 전처리 사례는 다음과 같습니다.

  • 시간대 변경: 기존의 created_at 칼럼은 모두 EST(동부 표준시) 시간대로 표현되어 있었는데, 이를 KST(한국 표준시) 시간대로 변환합니다.
kst = pytz.timezone('Asia/Seoul')
df['created_datetime'] = pd.to_datetime(df['created_datetime'], utc=True).dt.tz_convert(kst).dt.tz_localize(None)
  • 중복 데이터 제거: 이미 tickets 테이블에 적재가 완료된 티켓인 경우, 중복으로 저장할 필요가 없으므로 신규 티켓만을 필터링합니다.
query = f'SELECT DISTINCT id FROM `{bigquery_tickets_table_id}`'
try:
existing_ids = client.query(query).to_dataframe()
existing_ids = set(existing_ids['id'])
df = df[
~ df['id'].isin(existing_ids)
].reset_index(drop=True)
except:
df = df
  • 칼럼 필터링: 분석 목적에 맞게 칼럼 이름을 변경한 후 필요한 칼럼만을 필터링합니다.

df = df.rename(
columns={
'created_at': 'created_datetime',
...
}
)

df = df[[
'id',
'created_datetime',
...
]]

2. 토픽 분류

데이터 수집 및 전처리가 완료되어 BigQuery 테이블에 저장된 젠데스크 티켓 데이터는 이제 OpenAI의 언어 생성 모델을 활용하여 토픽 분류를 수행합니다. 이 과정은 고객 문의 내용을 넓은 범주 토픽과 구체적인 키워드로 나누어, VOC의 주요 주제를 파악하고 트렌드를 분석하는 데 도움을 줄 수 있도록 의도했습니다.

토픽 분류 파이프라인

토픽 선정하기

먼저 CX팀과 UX팀 동료 분들과 함께 이야기하는 시간을 가졌습니다. 프로덕트에 대해 늘 고민하는 다양한 분들의 의견을 들어본 것이죠. 특히, CX와 UX 관점에서 가장 중요한 토픽과 키워드가 무엇인지 충분히 공유 받은 후, 이를 토대로 토픽과 세부 키워드를 리스트업할 수 있었습니다.

  • 토픽: 넓은 범주의 키워드 (예: 결제, 주문배송, 기술 지원 등)
  • 세부 키워드: 구체적인 세부 키워드 (예: 결제 실패, 배송지 입력, 로그인 오류 등)
# 사전 정의된 토픽 목록
topics_list = [
['토픽 1', '세부 키워드 A'],
['토픽 1', '세부 키워드 B'],
['토픽 1', '세부 키워드 C'],
['토픽 1', '세부 키워드 D'],
['토픽 2', '세부 키워드 A'],
['토픽 2', '세부 키워드 B'],
['토픽 2', '세부 키워드 C'],
['토픽 2', '세부 키워드 D'],
...
]

토픽 분류 자동화하기

이 작업은 Python의 openai 라이브러리를 사용하여 각 고객 문의 텍스트 데이터를 분석하고, 사전에 정의된 적절한 토픽을 할당하는 과정으로 진행됩니다.

1. 데이터 로드

먼저 기존의 tickets 테이블에서 신규로 적재된 티켓 데이터를 읽어옵니다.

2. 프롬프트 작성하기

OpenAI가 토픽 분류를 원활하게 진행할 수 있도록 프롬프트 양식을 작성합니다. 언어 모델에 Input Data를 입력할 때 시스템 역할의 텍스트와 사용자 역할의 텍스트를 구분하여 입력할 수 있습니다.

  • 시스템(system) 프롬프트: 언어 모델의 답변 방식을 알려주는 Input Data
  • 사용자(user) 프롬프트: 구체적인 질문을 하기 위한 Input Data

특히, 언어 생성 모델에게 “반드시 인지시켜주고 싶은” 조건이 있다면 시스템 프롬프트와 사용자 프롬프트에 반복하여 알려줘야 한다는 점이 매우 중요했는데요. 가령, 분류 모델 특성상 정해진 Target 값만 리턴해야 하는데, 이 조건을 반복하여 알려주지 않는다면 새로운 토픽을 생성하거나 엉뚱한 부연설명을 리턴하는 경우가 발생할 것입니다. 따라서 시스템 프롬프트와 사용자 프롬프트를 통해 반복하여 조건을 명시해주는 것이 팁입니다.

# 시스템 프롬프트 정의하기
prompt_system = f'''
당신의 작업은 고객 문의 내역에서 ... 핵심 키워드를 분류하는 것입니다.
오로지 주어진 토픽 목록에서만 선택하여 응답해야 합니다.
아래는 당신이 선택할 수 있는 토픽 목록입니다:
{', '.join(...)}
...
'''

# 개별 프롬프트 정의하기
prompt_individual = f'''
아래는 고객 문의 내역입니다.
이 텍스트에서 하나의 핵심 토픽을 추출하세요.

고객 문의 내역:
{text}

추출 형식: 토픽
제한 사항:
1. ...
2. ...
3. 아래는 당신이 선택할 수 있는 토픽 목록입니다:
{', '.join(...)}

추출 결과:
'''

3. OpenAI API 호출하기

각 티켓의 텍스트 데이터를 순회하며 chat.completion.create() 엔드포인트를 통해 할당된 토픽을 추출합니다.

  • temperature 파라미터는 0으로 주었습니다. 분류 모델 특성상 사전에 정의된 Target 값만을 출력하도록 의도되어야 하므로, Deterministic한 답변을 받을 수 있도록 했습니다.

What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
- OpenAI Platform

result = openai_client.chat.completions.create(
model = 'gpt-4',
max_tokens = ...,
n = 1,
temperature = 0,
stop = None,
messages = [
{"role": "system", "content": prompt_system},
{"role": "user", "content": prompt_individual}
]
)

# 토픽 결과를 Empty Lists에 기록하기
topic_result = result.choices[0].message.content.replace('\'', '').replace('\"', '').replace('[', '').replace(']', '').strip()
topic_results_list.append(topic2_result)

4. 토픽 분류 결과 저장하기

각 티켓 별로 분류된 토픽 데이터를 tickets_topics 테이블에 저장합니다. tickets 테이블에 이미 존재하는 칼럼을 중복으로 적재할 경우 스토리지 비용의 증가로 이어지므로 꼭 필요한 아래 4개의 칼럼만 적재했습니다.

  • ticket_id: Primary Key 역할을 하는 티켓의 고유 ID
  • created_datetime: 티켓 생성일시
  • topic_1: 넓은 범주의 토픽
  • topic_2: 구체적인 세부 키워드

3. 문의 내용 요약

토픽 분류 작업이 완료된 후, 이번에는 OpenAI 언어 생성 모델을 통해 요약을 수행합니다. 이를 통해 고객 문의 내용의 핵심 내용을 빠르게 파악할 수 있도록 간결하고 명확한 하나의 문장으로 요약될 수 있도록 합니다.

사내의 한 임원 분으로부터 이런 이야기를 들었는데요.

“VOC 감을 항상 유지하기 위해 매일 젠데스크 티켓을 하나하나 정독하고 있어요. 하지만 고객 문의 내역의 양이 너무 방대하기 때문에 시간을 상당히 많이 소비하고 있어요.”

따라서 요약을 통해 사내 임직원 분들이 티켓의 전체 내용을 읽지 않고도 주요 내용을 빠르게 파악할 수 있도록 도와드릴 수 있을 것입니다. 또한, 요약된 데이터를 기반으로 트렌드 분석을 수행하거나, 반복적인 문제를 신속하게 식별할 수도 있겠죠.

문의 내용 요약 파이프라인

요약 자동화하기

이 작업 역시 Python의 openai 라이브러리를 사용하여 각 고객 문의 텍스트 데이터를 분석하고, 이를 하나의 문장으로 요약하는 과정으로 진행됩니다.

1. 데이터 로드

먼저 기존의 tickets 테이블에서 신규로 적재된 티켓 데이터를 읽어옵니다.

2. 프롬프트 작성하기

OpenAI가 요약을 원활하게 진행할 수 있도록 프롬프트 양식을 작성합니다. 프롬프트를 작성하는 과정에서 염두에 둔 내용들은 다음과 같습니다.

  • 비즈니스 도메인 배경을 잘 알려주어야 합니다. 고객 문의 내용의 맥락과 핵심 문장을 선별하기 위해서는 기업이 활동 중인 마켓과 운영 중인 프로덕트의 특성을 알아야 하기 때문입니다.
  • 반드시 한국어로 요약하도록 지시해야 합니다. 저희는 글로벌 기업이라 고객 문의 내용이 영어, 한국어, 중국어, 일본어 등 다양한 언어로 이루어져 있는데요. 요약은 반드시 한국어로 되도록 해야 할 것입니다.
  • 원천적으로 민감한 개인정보는 제거된 상태이지만, 한 번 더 고객의 인적사항, 이메일, URL, 소셜 미디어 계정 등을 한 번 더 제거하도록 알려줘야 합니다.
# 시스템 프롬프트 정의하기
prompt_system = f'''
당신의 작업은 고객 문의 내역을 한국어 한 문장으로 요약하는 것입니다.
... 서비스 기업의 고객임을 기억하세요.
요약은 반드시 한국어 한 문장으로 제공되어야 하며, 민감한 개인정보나 링크는 반드시 제거되어야 합니다.
'''

# 개별 프롬프트 정의하기
prompt_individual = f'''
아래는 고객 문의 내역입니다.
이 텍스트를 한국어 하나의 문장으로 요약하세요.

고객 문의 내역:
{text}

추출 형식: 한국어 한 문장
제한 사항:
1. ... 서비스 기업의 고객임을 기억하세요.
2. 반드시 한국어로 요약하세요. (단, 번역이 불가능한 고유 단어는 영어 가능)
3. 오로지 한 문장으로만 응답하세요.
4. 민감한 개인정보는 반드시 제거하세요. (예: ... 등)

추출 결과:
'''

3. OpenAI API 호출하기

각 티켓의 텍스트 데이터를 순회하며 chat.completion.create() 엔드포인트를 통해 요약 문장을 추출합니다.

  • 이번에도 temperature 파라미터는 0으로 주었습니다. 요약 모델은 분류 모델보다는 응답의 개방성이 높은 특성을 지니고 있기는 하지만, 상상력이 가미되지 않은 팩트 그대로의 요약을 의도하는 것이 적절하기 때문입니다.
result = openai_client.chat.completions.create(
model = 'gpt-4',
max_tokens = ...,
n = 1,
temperature = 0,
stop = None,
messages = [
{"role": "system", "content": prompt_system},
{"role": "user", "content": prompt_individual}
]
)

# 요약 결과를 Empty Lists에 기록하기
summary_result = result.choices[0].message.content.replace('\'', '').replace('\"', '').replace('[', '').replace(']', '').strip()
summaries_list.append(summary_result)

4. 요약 결과 저장하기

각 티켓 별로 생성된 요약문 데이터를 tickets_summary 테이블에 저장합니다. tickets 테이블에 존재하는 칼럼을 중복으로 적재할 경우 스토리지 비용의 증가로 이어지므로 아래 3개의 칼럼만 적재했습니다.

  • ticket_id: Primary Key 역할을 하는 티켓의 고유 ID
  • created_datetime: 티켓 생성일시
  • summary: 요약문

4. 대시보드 시각화

데이터 수집 및 전처리, 토픽 분류, 요약 과정은 매일 주기적으로 오케스트레이션이 진행되며, 최종적으로 고객 문의 데이터를 시각화하여 사내 임직원 분들이 쉽게 팔로업하고 분석할 수 있도록 했습니다. 나아가, 데이터 시각화는 Redash를 사용하여 대시보드를 구성했으며, 이를 통해 다양한 통계와 트렌드를 한눈에 파악할 수 있도록 했습니다.

대시보드 시각화 파이프라인

데이터 시각화 목표와 대시보드 구성

우선, 데이터 시각화의 주요 목표는 다음과 같이 정했습니다.

  • 고객 문의 유형 및 트렌드 파악: 다양한 문의 유형과 그 변화를 시각적으로 표현하여 쉽게 이해할 수 있도록 합니다.
  • 빠르고 정확한 VOC 분석: 시각화된 데이터를 통해 신속하게 VOC를 분석하고 대응 전략을 수립할 수 있도록 도와드립니다.
  • 사내 임직원의 이해와 소통 제고: 모든 임직원 분들이 고객 문의 내역을 직관적으로 빠르게 이해하고, 높은 공감대를 형성할 수 있도록 도와드립니다.

목표에 맞게 대시보드는 다음과 같은 컨텐츠로 구성되도록 했습니다.

  • 주제별 비율: 티켓 수 및 비율 (Word Cloud, Pie Chart 활용)
주제별 비율
  • 주제별 트렌드: 티켓 수 및 비율, 이전 기간 대비 증감 (Table, Area Chart 활용)
주제별 트렌드
  • 각 주제의 세부 키워드별 트렌드: 티켓 수 및 비율, 이전 기간 대비 증감 (Table, Area Chart 활용)
각 주제의 세부 키워드별 트렌드
  • 각 주제의 세부 키워드별 티켓 요약: 요약문, 티켓 바로가기 링크 (Table 활용)
각 주제의 세부 키워드별 티켓 요약
  • 전체 티켓 데이터: 생성일시, 주제, 세부 키워드, 요약문, 티켓 바로가기 링크 (Table 활용)
전체 티켓 데이터

나가는 글

CX팀과 UX/UI팀 동료 분들과 함께 진행한 이번 VOC 대시보드 프로젝트에서는 젠데스크 티켓 데이터 수집 및 전처리, OpenAI를 통한 토픽 분류와 요약, 그리고 Redash 시각화까지 전체 프로세스로 진행되었습니다. 이 과정을 통해 사내 VOC 팔로업 시간이 단축되고 고객 이해도가 높아짐으로써 더 좋은 프로덕트를 만들어가는 과정에 중요한 역할을 할 것입니다.

기대 효과

이번 프로젝트의 최종 결과물인 Redash 대시보드를 통해 다음과 같은 효과를 기대할 수 있을 것입니다.

1. 신속한 의사결정 지원

고객 문의 데이터를 빠르게 모니터링하고 분석함으로써, 빠르게 변화하는 고객의 요구에 신속히 대응할 수 있습니다. 이를 통해 고객 만족도와 충성도를 높일 수 있습니다.

2. 문제 해결 시간 단축

토픽 분류와 요약을 통해 문제의 본질을 빠르게 파악하고, 적절한 해결책을 제시함으로써 문제 해결 시간을 단축할 수 있습니다.

3. 내부 커뮤니케이션 개선

모든 임직원이 동일한 데이터와 인사이트를 공유함으로써, 협업와 커뮤니케이션 비용을 줄이는 데 기여할 수 있습니다.

4. 프로덕트 개선

VOC를 반영하여 프로덕트를 지속적으로 개선함으로써, 제품의 시장 경쟁력을 높일 수 있습니다.

개인적인 회고

기존에는 분류나 텍스트 요약 모델을 사내에 도입하기 위해 직접 ML 모델을 학습하고 MLOps 인프라를 구축하는 등 도입과 관리의 비용이 상당히 높은 편이었습니다. 그러나 최근 GPT의 성능이 빠르게 향상됨에 따라, 단순한 Language Generation 작업을 넘어, 더 많은 영역의 작업을 수행할 수 있게 되었습니다. 즉, 이제 GPT는 일반화된 추론 모델로 발전하고 있습니다.

이번 프로젝트에서 AI 엔지니어링 작업은 매우 간소했습니다. 작업 시간과 비용이 과거에 상상할 수 없을 정도로 줄어든 것이죠. “너무 간단해서 누구나 할 수 있겠는데?”라는 생각이 들 정도로 쉽게 작업을 완료할 수 있었습니다. Train Dataset을 준비하거나 Target Labeling을 진행할 필요도 없이, OpenAI의 언어 생성 모델을 활용할 수 있었던 것이죠.

아래 그림은 제가 개인적으로 최적의 Ensemble Classification Model을 학습 시키기 위해 Jupyter Notebook으로 각 분류 모델을 시각화한 것인데요. 간단한 분류 모델을 학습시키기 위해 정말 많은 시간을 투자해야 했습니다.

과거 최적의 Ensemble 분류 모델을 찾기 위해 시각화한 본인의 Jupyter Notebook

만약 언어 생성 모델의 발전이 없었다면, VOC 분석을 통한 사내 가치 창출이 훨씬 어려웠을 것입니다. AI 전문성을 지닌 사람들만 접근 가능한 영역이었으며 진입장벽이 높았던 것이죠. 그러나 이제 진입장벽이 매우 낮아졌음을 느꼈고, OpenAI를 비롯한 다양한 Generative AI 모델을 통해 기업 내부의 수많은 문제를 적시에 해결하고 가치를 창출하는 일이 가능해졌습니다. 이는 기업의 경쟁력을 강화하는 것을 넘어, 생존 자체를 위해서도 매우 중요한 과정일 것입니다.

이번 프로젝트를 통해 VOC에 대한 이해의 제고와 공감대 형성에 기여할 수 있었으며, 나아가 더 나은 프로덕트를 만들 수 있기를 기대합니다. 많은 분들께서도 VOC를 분석하고 이를 프로덕트에 반영함으로써, 결국 기업의 성장과 발전을 도모하기를 바랍니다. 감사합니다.

VoC Solutions: Why Do Businesses Need It?

--

--