디지털 사이니지를 위한 프랑스어 음성 비서

프랑스 소매점 직원이 말로 디지털 사이니지 콘텐츠를 만들 수 있도록 제가 개발한 Go 서비스입니다.

내 역할: 단독 개발. 8개월 동안 작성된 110개 커밋 중 109개를 제가 작성했습니다.

2025년 10월부터 2026년 5월까지

음성을 선택한 이유

이 기능은 매일 매장 화면을 바꾸는 제빵사, 정육점 직원, 생선가게 직원들을 위해 만들었습니다. 상품이나 가격 하나를 바꾸려고 콘텐츠 관리 시스템을 열고 여러 양식을 채우는 사람은 많지 않습니다.

프랑스어로 시스템에 말하면, 그 대화를 바탕으로 완성된 상품 슬라이드를 받는 것이 목표였습니다.

동작 방식

처음에는 Raspberry Pi에서 직접 만든 음성 활동 감지 기능을 사용해 Python으로 시제품을 만들었습니다. 가능성을 확인한 뒤 Go 서비스로 다시 만들었습니다. 음성 간 대화에는 WebSocket을 통한 OpenAI Realtime API를 사용하고, 텍스트만으로 충분할 때는 구조화된 JSON 출력을 제공하는 Chat Completions를 사용합니다.

핵심은 약 4,400줄 규모의 단계별 대화 오케스트레이터입니다. 각 대화를 애플리케이션 선택, 상품 식별, 필드 완성, 확인 단계로 진행시킵니다. 음성과 텍스트가 같은 오케스트레이터를 사용하며, 부분 결과는 Server-Sent Events로 전송해 인터페이스에서 진행 상황을 보여줍니다.

애플리케이션 선택은 YAML 파일로 설정합니다. 테마마다 트리거 단어가 있고, 일치하는 단어가 없으면 AI를 대체 수단으로 사용합니다. 저는 7개 소매 업종을 다루는 상품 카탈로그도 만들었습니다.

해결해야 했던 문제

프랑스어 상품명. “Filet de bar”와 “bar en filet”는 같은 상품이지만, 단순 문자열 비교로는 알 수 없습니다. 먼저 Unicode NFD 정규화로 악센트에 영향받지 않고 비교한 뒤, 프랑스어 복합 상품명의 표기 차이를 별도로 처리했습니다.

대화 메모리. 첫 버전은 대화를 크기 제한 없는 맵에 저장하고 삭제하지 않았습니다. 테스트 때는 괜찮아 보였지만 결국 운영 환경에서 메모리가 소진됐습니다. 이를 직접 만든 LRU 캐시로 교체했습니다.

API 접근. 하위 시스템마다 OpenAI 키의 접근 범위를 분리했고, API 키는 일정한 시간이 걸리는 방식으로 비교합니다.

이후의 확장

시간이 지나며 이 서비스는 회사의 공용 AI 백엔드가 됐습니다. 고객용 챗봇을 이 서비스로 옮기면서 세션 저장소, 요청 속도 제한, 이메일로의 인계 기능, 241줄의 프랑스어 시스템 프롬프트와 약 800줄의 테스트도 함께 넣었습니다. 공개 화면에 표시되는 콘텐츠를 위한 AI 검수 기능도 추가했습니다.

검수 서비스에 장애가 나더라도 모든 화면이 멈추지 않도록, 검수는 의도적으로 실패 시 통과하도록 설계했습니다. 팀에서 함께 결정한 선택입니다.

수치로 보면

서비스에는 약 12,400줄의 Go 코드와 1,800줄의 테스트가 있으며, 4개 라우트 그룹에 15개가 넘는 엔드포인트가 있습니다.

연락하기

원격 프리랜서 업무가 가능합니다. 이메일로 연락해 주시면 가장 빠르게 답변할 수 있습니다.