← 문제 해결 기록

회사 업무 · 솔루션 내재화 담당 · 2026.09 - 현재

세일즈 네비게이터

동료의 해커톤 데모를 플랫폼 안으로 옮기며, 인증과 데이터와 LLM 호출의 경계를 다시 그었습니다.

개요

기간
2026.09 - 현재
참여 형태
회사 업무 · 솔루션 내재화 담당
담당
네이티브 이관 설계·구현·운영 배포
목적
동료의 해커톤 데모를 CRM 사용자가 쓰는 제품 기능으로 옮기는 것: 플랫폼 인증·테넌트·데이터 안에서 동작하게 하고 회의록·딜 준비로 확장
대상
외근이 잦은 영업 담당자
사용 기술
Java · JSP · JavaScript · PostgreSQL
외근 영업 담당자가 휴대폰에서 오늘 일정 브리핑, 장소 검색과 길찾기, 음성 일정 초안, 미팅 녹음과 회의록, 딜 준비를 처리하는 CRM 모듈입니다. 원래는 다른 구성원이 만든 사내 해커톤 1위 작품(Express.js 브릿지 서버와 브라우저 앱)이었고, 서비스 도메인이 CRM과 맞아 제품에 이식하는 업무를 맡았습니다.이관 전에 원본을 분석해 같은 검색 로직이 서버·브라우저·플랫폼 규칙에 3벌로 있고, 사용자 식별과 LLM 키가 브라우저 저장소에 있어 플랫폼의 인증·테넌트 밖에서 동작한다는 점을 확인했습니다. 플랫폼 세션을 인증 주체로 삼는 단일 서버 경로로 옮긴 뒤 녹음·회의록·딜 준비 기능을 더했고, 운영 배포까지 맡았습니다. 모듈 커밋 157개 중 155개를 작성했고, 구현에는 AI 보조를 활용했습니다.

구현 기능과 기술 선택

  • 오늘 일정 브리핑 (Google Calendar 일정, 음성 합성)
  • 자연어 장소 검색과 길찾기, 음성으로 만드는 일정 초안
  • 미팅 녹음 → 화자별 스크립트와 회의록
  • 딜 준비 화면 (흐름·위험·할 일 분석)
  • 세션·테넌트·CSRF를 확인하는 인증된 진입점
  • 규칙 9개 매니페스트와 격리 환경 설치·롤백 스크립트, 스키마 증분 DDL과 롤백
  • 장소 재정렬은 원본 후보의 완전한 순열만, 회의록 요약은 원문 구간과 일치하는 발췌만 저장하는 LLM 결과 검증
  • 기능별 스위치를 설정 테이블에서 읽고(끄면 기존 동작 유지), 공급자 키는 플랫폼 키 저장소에서만 조회
분기 로직은 서버 코드에
플랫폼 규칙 엔진은 조건 노드가 단일 비교만 지원하고 에이전트 노드가 도구 호출을 지원하지 않습니다. 원본도 자연어 검색 분기를 노드로 옮기다 30개 노드짜리 플로우가 됐던 기록이 있어, 이 제약을 우회하지 않고 분기는 서버 코드에 두고 규칙은 호출 진입점으로만 썼습니다.
LLM 호출은 트랜잭션 밖에서
LLM 응답은 수십 초 걸릴 수 있어, DB 트랜잭션 안에서 기다리면 그동안 커넥션을 붙잡습니다. 호출은 트랜잭션 밖에서 하고, 저장할 때만 짧은 트랜잭션을 열어 권한과 버전을 다시 확인합니다.
LLM 결과의 서버 검증
원본은 LLM이 돌려준 인덱스 중 없는 번호를 조용히 버리고 나머지를 썼습니다. 이관하면서 장소 재정렬은 원본 후보의 완전한 순열이 아니면 거절하고, 회의록 요약은 원문 구간 번호와 정확히 일치하는 발췌만 저장하게 했습니다.
기능별 설정 스위치
공급자 승인과 키 발급 시점이 기능마다 달라, 기능별로 켜고 끌 수 있게 했습니다. 끈 상태에서는 기존 화면이 그대로 동작하고, 공급자 키는 플랫폼 키 저장소에서만 읽습니다.

트러블슈팅

해커톤 구조를 플랫폼 안으로 옮기기

문제 상황

동료의 해커톤 1위 작품은 Express 브릿지 서버, 브라우저 앱, 플랫폼 규칙 6개로 나뉜 7,770줄이었고, 플랫폼 안에서 도는 것은 규칙 6개뿐이었습니다. 같은 검색 로직이 서버·브라우저·규칙에 3벌로 있어 프롬프트 하나를 고치려면 세 곳을 고쳐야 했습니다. 사용자 식별은 브라우저가 만든 UUID와 Google 로그인뿐이었고 LLM 키를 포함한 설정 28개가 브라우저 저장소에 있어, 영업팀 계정과 권한으로는 쓸 수 없는 구조였습니다.

해결 과정

분기 로직을 둘 곳은 두 가지였습니다. 원본처럼 플랫폼 규칙 엔진의 노드로 옮기면 플랫폼 기능만으로 끝나지만, 조건 노드는 단일 비교만 지원하고 에이전트 노드는 도구 호출을 지원하지 않았습니다. 원본도 자연어 검색 분기를 노드로 옮기다 30개 노드짜리 플로우가 된 기록이 있었습니다. 그래서 분기는 서버 코드에 두고 규칙은 호출 진입점으로만 썼습니다. 공급자 키는 브라우저 대신 플랫폼의 기존 AI Portal 키 저장소에서 읽게 했습니다.

결과

3벌이던 검색 로직은 서버 경로 하나로 합쳐졌고, 모든 외부 API·LLM 호출이 플랫폼 인증을 거치게 되어 영업팀 계정으로 쓰는 운영 배포까지 진행했습니다.

본인 범위: 원본 분석, 네이티브 이관 설계·구현·운영 배포, 회의록·딜 준비 기능 추가. 원본 해커톤 작품은 다른 구성원이 만들었습니다.

통과하던 검사가 실제 엔진 경로를 타지 않았던 문제

문제 상황

규칙의 결과를 화면 표시 이름으로 참조하던 초기 검사는 JSON 파싱만 확인하고 통과했습니다. 그런데 설치된 엔진은 결과를 표시 이름이 아니라 내부 ID 키로 저장해, 실제 실행에서는 'Can not parse json result'로 실패하는 경로였습니다.

해결 과정

검사가 엔진의 실제 실행 경로를 그대로 타도록 바꿨습니다. 실제 JSP 선언과 서비스 본문을 설치된 플랫폼 클래스에 대고 컴파일한 뒤, 규칙 실행 → 시스템 액션 → 결과 해석까지 엔진 코드를 그대로 돌리고 DB 조회와 HTTP 전송만 오프라인 대역으로 바꿨습니다. 결과 참조는 내부 ID 기반으로 고치고, 예전 표시 이름 참조는 반드시 실패해야 통과하는 검사로 남겼습니다.

결과

규칙 9개 모두 한글·따옴표·줄바꿈·배열·null·소수 입력에서 ID 기반 참조가 왕복하는 것을 확인했습니다. 이 과정에서 규칙 설정 ID가 비어 있으면 서로 다른 규칙도 같은 키로 충돌한다는 엔진 동작을 찾아 운영 문서에 주의 사항으로 남겼습니다.

LLM을 기다리는 동안 바뀐 녹음을 덮어쓰지 않기

문제 상황

회의록 요약은 녹음 전사본 전체를 LLM에 보내고 결과를 저장합니다. LLM 응답은 수십 초 걸릴 수 있어 트랜잭션 안에서 기다리면 그동안 DB 커넥션을 붙잡고, 그사이 녹음이나 전사본이 바뀌면 오래된 입력으로 만든 요약이 최신 데이터를 덮어쓸 수 있었습니다. 원본은 LLM 결과 중 맞지 않는 부분을 조용히 버리고 나머지를 썼습니다.

해결 과정

트랜잭션 안에서 행을 잠근 채 기다리면 정합성은 쉽게 지켜지지만, 외부 대기가 DB 커넥션을 묶는 것은 StoLink에서 전체 API를 멈추게 했던 원인과 같았습니다. 그래서 LLM 호출은 트랜잭션 밖에서 하고, 저장할 때만 짧은 트랜잭션을 열어 녹음을 다시 읽고 호출 전과 비교했습니다. 다르면 덮어쓰지 않고 409로 알려, 사용자가 다시 요청해야 하는 수고를 감수하는 대신 최신 데이터를 지켰습니다. LLM 응답은 종료 사유·형식·크기를 검사하고, 장소 재정렬은 원래 후보의 완전한 순열일 때만, 요약은 원문 구간과 정확히 일치하는 발췌일 때만 저장합니다.

결과

외부 호출을 기다리는 동안 DB 커넥션을 붙잡지 않게 됐고, 그사이 바뀐 녹음은 덮어쓰지 않고 충돌로 드러납니다. 요약에는 원문에 없는 문장이 저장되지 않습니다.

StoLink 장애(외부 대기가 DB 커넥션을 붙잡던 문제)와 같은 원칙입니다.

결과와 성과

  • 로컬 브릿지 서버로 돌던 해커톤 데모를 CRM 운영 배포까지 이관 (네비게이터 PR 43건)
  • 브라우저는 인증된 진입점 하나만 호출하고, 모든 외부 API·LLM 호출이 플랫폼 세션·테넌트 인증을 거치도록 일원화
  • 실제 엔진 경로를 타는 네이티브 검사로 규칙 9개의 결과 참조를 확인, 오프라인 검사 415개 통과 (PR #41 시점)

회고

배운 점
프로토타입을 제품으로 옮길 때 먼저 바뀌는 것은 기능이 아니라 경계였습니다. 누가 인증 주체인지, 데이터와 키가 어디 있는지를 정하고 나서야 기능을 옮길 수 있었습니다. 엔진의 실제 실행 경로를 타지 않는 검사는 통과해도 이관이 맞다는 근거가 되지 못한다는 점도 확인했습니다.
아쉬운 점
실사용 지표(사용자 수, 녹음 건수)를 아직 모으지 않았고, 일부 외부 공급자 연동은 격리 환경과 가짜 공급자 기준으로 먼저 검증했습니다.
보완 계획
사용 지표를 수집하고, 지금은 24KB로 제한한 회의록 요약 입력을 긴 녹음에 맞게 나눠 요약하는 방식을 검토합니다.