GS리테일 AX 블로그

AI와 데이터로 고객의 생활 가치를 혁신하는 GS리테일 AX 이야기

Data&AI

[ AI-DLC 사례 1: 검색 ] AI-DLC로 3일, 뷰티 자연어 검색을 만드는 여정

talk50838 2026. 8. 14. 14:27

 

목차


 

 

개요

9월 GS SHOP에 고객과 대화하며 상품을 찾아주는 쇼핑Agent 서비스가 오픈할 예정입니다. 기존 검색 서비스에서는 고객이 "샴푸", "손상모 샴푸"처럼 짧은 검색어를 입력했다면, 쇼핑Agent에서는 원하는 조건을 자연어 문장으로 표현할 수 있습니다.

 

"건조한 두피와 손상모발 동시에 관리하는 샴푸"


그런데 문장을 받아줄 검색이 아직 없었습니다. GS SHOP 검색의 주축은 단어 검색과 임베딩 검색이고, 문장에 담긴 여러 조건을 나눠서 다루는 단계가 없었기 때문입니다.

2026년 7월 7일부터 9일까지, 저희 검색추천파트 4명은 AI-DLC(AI-Driven Development Life Cycle) 라는 개발 방법론으로 이 문제를 풀어봤습니다. 요구사항 정의부터 운영 엔드포인트 실연동 검증까지 3일이 걸렸습니다.

결론부터 말씀드리면, 목표한 검색 품질에는 도달하지 못했습니다.

그런데 3일을 마치고 팀이 가장 많이 이야기한 것은 코드 생성 속도가 아니었습니다. AI가 저희에게 되물어온 질문들이었습니다. 저희가 쓴 문서 안에 이미 모순이 있었고, 저희는 그걸 의식하지 못하고 있었습니다.

이 글은 그 3일의 기록입니다. 잘된 부분과 아쉬운 부분을 함께 적었습니다.


1. 배경과 AI-DLC

질의는 문장으로 길어졌는데 검색은 문장을 나눠 보지 못했습니다

기존 검색이 문장형 질의를 제대로 다루지 못하는 이유는 두 가지였습니다.

하나는 질의 쪽입니다. 앞의 샴푸 질의 한 문장에는 피부 타입과 부위, 효능, 제품 종류가 한꺼번에 담겨 있습니다. 단어 검색은 어휘가 정확히 맞아떨어져야 하고, 임베딩 검색은 문장 전체의 의미는 잡아내지만 그 안에 섞인 조건을 따로 떼어 다루지는 못합니다.

다른 하나는 상품 쪽입니다. 질의에서 조건을 뽑아낸다 해도 기존 색인에는 두피 타입이나 효능 같은 뷰티 속성 정보가 없었습니다. 맞춰볼 대상이 아예 없는 셈입니다.

그래서 두 가지가 필요하다고 봤습니다.

질의 쪽에는 문장의 의도를 속성으로 구조화하는 Query Understanding, 상품 쪽에는 그 속성을 색인에 담아 매칭되게 만드는 검색 모델링입니다.

Query Understanding은 검색어를 그대로 검색에 넣는 대신, 문장 안에 담긴 조건을 항목별로 뽑아내는 단계입니다. 위 질의라면 피부 타입은 건성, 부위는 두피, 효능은 손상복구, 제품 종류는 샴푸로 나눕니다. 사람이 읽고 이해한 것을 검색 엔진이 다룰 수 있는 형태로 옮기는 셈입니다.

두 축은 같은 속성 어휘를 공유해야 비로소 만납니다. 실제로 3일 중 가장 많은 시간을 이 어휘를 맞추는 데 썼습니다.

이번 평가에 사용한 질의도 쇼핑Agent의 자동완성 질의어와 AI 추천 검색어 기능이 만들어낸 실제 질의어를 그대로 가져왔습니다. 아직 운영에 노출되지 않은 기능이지만, 곧 들어올 입력의 성격을 가장 정확히 반영한 데이터였습니다.

주제를 고를 때 기대한 것은 두 가지였습니다. 실습용 예제가 아니라 실제 업무를 올려서 방법론을 바로 적용해보는 것, 그리고 3일 뒤에 남는 결과물을 실제 업무의 밑바탕으로 쓰는 것이었습니다.

품질 지표 기준을 두었습니다.

지표 목표
검색 결과(상위 10건) 내 뷰티 상품 포함률 80% 이상
API 응답 시간 (p95) 3초 이내

 

포함률을 주 지표로 삼은 이유가 있습니다. 검색을 뷰티 카테고리로 미리 좁히지 않고 전체 상품을 대상으로 하기 때문입니다. 색인된 100만여 개 상품 중 뷰티는 2%가 되지 않는데, 뷰티 질의를 받아 상위 10건을 뷰티 상품으로 채워야 합니다. 결과에 무엇이 섞여 나오는지가 곧 질의 의도를 얼마나 이해했는지를 보여줍니다. 응답 3초는 질의 이해에 LLM 호출이 들어간 구조를 감안한 상한이었습니다.

 

3일, 4명, 그리고 절반은 이미 있던 환경

기간은 3일, 인원은 4명이었습니다. 한정된 인원으로 정해진 시간 안에 어디까지 구현할 수 있는지 확인해보는 셈이었습니다.

한 가지 짚어둘 점이 있습니다. 이 프로젝트는 백지에서 시작한 그린필드가 아닙니다.

정확히는 코드는 그린필드이고 환경은 브라운필드입니다. 새로 쓴 코드는 한 줄도 없는 상태에서 시작했지만, 검색 엔진과 랭킹 모델과 배포 환경은 이미 운영 중이었습니다. 그래서 사전 기술 문서에 무엇을 그대로 재사용하고 무엇을 새로 만들 것인지, 기존 인덱스는 건드리지 않는다는 공존 규칙을 미리 적어두었습니다.

 

이 구분이 뒤에 나올 신규와 재활용 구분으로 그대로 이어집니다.

 

AI가 일하고 사람이 결정합니다

AI-DLC는 이름 그대로 AI가 개발 생명주기를 주도하는 방법론입니다. 다만 "AI가 알아서 다 만든다"는 뜻은 아닙니다. 오히려 반대에 가깝습니다.

AI 에이전트가 요구사항 분석과 설계, 구현, 검증 같은 실제 작업을 수행합니다. 사람은 무엇을 왜 만들지 정의하고, 각 단계의 결과를 검토하고 승인하는 판단에 집중합니다.

전통적인 개발이 사람이 짜고 도구가 돕는 구조라면, AI-DLC는 AI가 짜고 사람이 결정하는 구조입니다. 개발자의 일이 사라지는 것이 아니라 무게중심이 타이핑에서 정의와 판단으로 옮겨 갑니다.

시작 전 준비물은 문서 두 개입니다.

  • vision-document.md — 무엇을 왜 만드는가. 문제 정의, 대상 사용자, MVP 범위와 제외 범위, 성공 지표, 리스크
  • technical-environment-guide.md — 어떻게 만드는가. 언어와 프레임워크, 금지 라이브러리, 기존 자산과의 공존 규칙, 테스트와 품질 게이트

이 두 문서가 이후 모든 단계의 판단 기준이 됩니다. AI가 무언가를 제안할 때도, 저희 답변이 앞뒤가 맞지 않을 때도 근거는 항상 이 문서였습니다.

작업은 세 개의 국면(Phase)으로 나뉩니다. 각 국면은 다시 여러 단계로 이뤄지고, 단계마다 산출물이 나옵니다.

국면 목적 주요 단계
Inception (기획·설계) 무엇을, 왜 만들 것인가 요구사항 분석 · 유저 스토리 · 워크플로우 계획 · 애플리케이션 설계 · 유닛 분해
Construction (구현) 어떻게 만들 것인가 (유닛별 반복) 기능 설계 · NFR 설계 · 코드 생성 → 빌드와 테스트
Operations (운영) 어떻게 배포하고 운영할 것인가 배포 · 모니터링 (향후 확장 영역)

 

 

이 흐름을 지탱하는 장치는 세 가지입니다.

  • 승인 게이트(Approval Gate). AI가 각 단계 산출물을 제시하면, 사람이 변경 요청이나 다음 단계 진행을 명시적으로 선택해야 넘어갑니다. AI가 앞서가지 못하도록 막는 브레이크입니다.
  • audit 로그(audit.md). 모든 사용자 입력과 AI의 결정이 시각과 함께 원문으로 쌓입니다. 왜 그렇게 결정했는지를 나중에 그대로 되짚을 수 있어, 의사결정이 재현 가능해집니다.
  • 스티어링(Steering)과 상태 추적. 팀 규칙과 컨벤션을 문서로 고정해 모든 상호작용에 주입하고, 진행 상태(aidlc-state.md)로 지금 어느 단계인지를 항상 파악합니다.

 

흐름이 고정된 절차가 아니라는 점도 특징입니다. AI가 과제의 성격과 복잡도를 판단해 어떤 단계를 실행하고 어떤 단계를 건너뛸지 제안하고, 사람이 조정합니다. 저희 프로젝트에서는 분석할 기존 로컬 코드가 없어 리버스 엔지니어링을 생략했고, MVP를 로컬에서 실행하기로 해서 인프라 설계도 생략했습니다.

 

승인 게이트가 있어도 위임하면 그만이었습니다

워크플로우 계획을 승인하는 시점에 저는 "다 해줘"라고 답했습니다. AI는 그 지시대로 애플리케이션 설계 단계의 승인 게이트를 생략하고 넘어갔습니다. 곧 마음이 바뀌어 진행을 멈추고 다시 검토했고, 그 뒤 "Inception만 승인할게"로 위임 범위를 좁혔습니다. 이 위임과 정정이 모두 audit 로그에 남아 있습니다.

 

이 방법론의 어려움은 승인하는 일이 아니라 어디까지 위임할지 스스로 정하는 일에 있었습니다.


2. 대화 속 나의 모순

AI-DLC를 하며 가장 인상 깊었던 것은 AI가 코드를 빨리 짜준다는 사실이 아니었습니다. 저희가 쓴 문서와 저희 답변 사이의 모순을 찾아 되물어 준다는 점이었습니다.

되묻는 방식도 인상적이었습니다. AI는 답변을 그냥 받아 적지 않았습니다. 앞서 확정한 문서와 대조해 충돌 지점을 찾으면, 무엇과 무엇이 왜 충돌하는지 근거를 대고 선택지를 제시한 뒤 저희가 고르게 했습니다. 그리고 그 선택과 이유가 다시 audit 로그에 남았습니다.

 

이틀 뒤에 결국 받아들였습니다

요구사항 분석 단계에서 AI가 MVP 범위를 물었습니다. 기한이 3일이라 무언가를 덜어내야 했습니다. 저희는 캐시(Valkey)와 테스트를 범위에서 뺐습니다. 핵심 파이프라인부터 완성하는 게 맞다고 판단했습니다.

AI는 그 선택을 그대로 받지 않고 되물었습니다.

  • 캐시를 빼면, 기술 문서에 적어둔 응답 p95 3초 목표와 충돌합니다. 그 문서가 캐시를 "반복 질의의 LLM과 검색 비용, 지연을 제거하는 수단"으로 명시했기 때문입니다.
  • 테스트를 빼면, 같은 문서의 CI 게이트 정책(린트, 타입, 테스트, 커버리지 80%)과 충돌합니다.

두 지적 모두 저희가 며칠 전에 쓴 문서를 근거로 하고 있었습니다.

 

솔직히 그 자리에서 전부 수용하지는 못했습니다. 기한 내 완료가 우선이라 절충했습니다. 테스트는 포함으로 바꾸고, 캐시는 제외를 유지하되 "p95 목표는 캐시 없이 재검토"라는 리스크로 문서에 남겼습니다.

흥미로운 것은 그다음입니다.

3일차에 실제 파이프라인을 실연동으로 돌려보니, 100만 건이 넘는 색인에 대한 하이브리드 kNN 검색이 약 2초를 잡아먹으며 p95 3초 목표를 실제로 위협했습니다. AI가 이틀 전에 지적하고 저희가 리스크로만 적어둔 바로 그 지점이었습니다.

결국 시연을 준비하면서 캐시를 넣었습니다. 질의 이해와 임베딩, 하이브리드 검색까지의 결과를 캐싱하고 결과가 매번 달라지는 리랭킹만 제외하는 형태였습니다.

저희가 뺀 것이 이틀 뒤에 돌아왔습니다.

 

정의하지 못한 것에는 근거를 들고 왔습니다

두 번째는 성격이 다릅니다. 모순이 아니라 공백이었습니다.

임베딩 모델의 서빙 방식은 팀에서 아직 확정하지 못한 항목이었습니다. 기존 프로젝트들이 TorchServe를 쓰고 있으니 그걸 따라가야 하나 싶었지만, 최근 흐름을 알지 못했습니다. 그래서 답변 대신 요청을 적었습니다. 요즘 기준으로 좋은 방식을 제안해달라고요.

AI는 학습된 지식으로 답하지 않고 웹을 검색했습니다. 그리고 후보였던 TorchServe가 2025년 8월에 저장소가 아카이브되어 더 이상 패치되지 않는다는 사실을 확인한 뒤, 대안 세 가지를 비교해 제시했습니다.

옵션 특징 적합성
TEI (Text Embeddings Inference) 임베딩 전용, 동적 배칭, 가벼운 이미지 표준 인코더 계열이면 우선 권장
NVIDIA Triton 멀티 프레임워크, 커스텀 아키텍처 수용 비표준 모델이거나 서빙 스택 통일 시
vLLM 고처리량, LLM과 임베딩 통합 스택 두 가지를 한 스택으로 묶을 때

 

저희는 TEI를 목표 서빙 방식으로 택했습니다. 개발 중에는 임베딩을 인터페이스 뒤로 추상화하고 mock으로 진행했고, 마지막 날 사내에 배포된 TEI 서비스에 붙여 검증까지 마쳤습니다. 1024차원 정규화 벡터를 0.3~0.5초에 돌려주는 것을 확인했습니다.

AI는 문서에 없는 빈칸을 억지로 채우는 대신 근거를 들고 와서 판단을 사람에게 넘겼습니다. 이 태도가 신뢰를 만들었습니다.


3. 우리가 만든 것

자연어 질의를 받아 의도와 속성을 구조화하고, 의미와 어휘와 속성을 함께 결합해 검색하고, 판매 지표로 재정렬해 돌려주는 내부 API입니다.

무엇을 새로 만들고 무엇을 그대로 썼는가

새로 만든 영역(Greenfield)

질의 쪽

  • 쿼리 언더스탠딩 — Bedrock의 Claude Haiku를 tool use로 호출해, 자유 텍스트가 아니라 구조화된 속성으로만 답하도록 강제했습니다. 추출하는 속성은 효능, 피부 고민, 피부 타입, 적합 대상, 타깃 부위, 감각, 제형, 함유 성분, 무첨가의 9종입니다.
  • 질의 임베딩 서빙 — 오픈소스 임베딩 모델(BGE-M3, 1024차원)을 TEI로 서빙해 질의를 실시간으로 인코딩합니다.

상품 쪽 · 검색 모델링

  • 뷰티 속성 벡터 색인 — 뷰티 속성 facet을 함께 담은 벡터 색인을 구성했습니다. 상품 벡터는 배치가 사전에 적재하고, 질의 인코딩과 반드시 같은 모델과 차원, 같은 정규화를 씁니다. 어느 한쪽이 어긋나면 유사도 자체가 무의미해집니다.
  • 하이브리드 QueryDSL 생성 — 상품명 대상 BM25는 필수 조건으로, 벡터 kNN과 속성 facet은 가점 조건으로 결합했습니다.

그 외

  • 검색 API와 성과 검증 도구 — FastAPI 기반 엔드포인트, 3열 비교 데모, 오프라인 평가 하네스

뷰티 도메인에 특화된 임베딩 모델 자체의 고도화는 이번 범위 밖이고, MVP와 병렬로 진행되는 별도 과제입니다.

재활용한 영역(Brownfield)

  • OpenSearch 클러스터 — 기존 운영 인덱스는 건드리지 않고 벡터 색인만 별도로 구성
  • 기존 랭킹 모델 — 모델을 내장하지 않고 HTTP API 호출로 재사용
  • 기존 키워드 검색 API — 데모의 비교 기준(Baseline)으로 읽기 전용 호출
  • 운영 EKS 클러스터 — 향후 별도 Deployment로 배포

새로 도입한 외부 의존은 Bedrock 하나였습니다.

 

시간을 가장 많이 쓴 곳은 프롬프트가 아니었습니다

파이프라인의 첫 관문은 질의를 속성으로 번역하는 일입니다.

 

여기서 예상보다 시간을 많이 쓴 부분은 프롬프트가 아니라 어휘였습니다.

색인의 속성 값은 정해진 목록으로 관리됩니다. LLM이 아무리 정확하게 뽑아도 표기가 다르면 매칭되지 않습니다. 그래서 실제 속성 데이터를 분석해 색인의 폐쇄 어휘를 확인하고, LLM 추출을 그 표준값에 정렬했습니다. 피부 타입은 3종, 피부 고민은 6종으로 이미 잘 정규화된 고품질 신호였습니다.

잡티는 잡티개선으로, 모공은 모공케어로 바꿨습니다. 민감성은 피부 타입이 아니라 피부 고민으로, 저자극은 적합 대상으로 보냈습니다. 이 정렬 이후 속성 매칭률이 올라갔습니다.

속성을 결합하는 방식도 한 번 뒤집었습니다. 실제 색인 스키마를 받아보니 무첨가 관련 필드가 배제 조건이 아니라 무향이나 무색소처럼 긍정 속성으로 저장돼 있었습니다. 배제로 걸려던 것을 가점으로 바꿨습니다.

핵심은 속성을 결과를 걸러내는 하드 필터가 아니라 상위로 끌어올리는 가점으로 얹었다는 점입니다. 자연어에서 뽑은 속성은 완벽하지 않기 때문에, 필터로 걸면 맞는 상품까지 사라집니다.

 

이틀은 mock으로, 마지막 날에 실연동으로

3일 중 이틀은 외부 시스템에 접근하지 않고 개발했습니다. 내부망 전용 API들이라 로컬에서 붙을 수 없었기 때문입니다.

이것도 AI가 되물어준 결과였습니다. 유닛을 나누는 단계에서 저희는 "외부 시스템은 전부 실연동하겠다"고 답했는데, AI가 두 가지를 짚었습니다. 임베딩 서빙 방식이 미정이라고 저희가 직접 문서에 써놨다는 것, 그리고 내부망 전용 API들은 로컬 실행 환경에서 접근할 수 없다는 것이었습니다.

이틀 전에 저희가 쓴 제약을 저희가 잊고 있었습니다.

그래서 모든 외부 의존을 mock | real 설정 스위치로 감쌌습니다. mock으로 전 파이프라인이 동작하는 상태를 먼저 만들고, 3일차에 엔드포인트가 확보되는 순서대로 하나씩 실연동으로 켰습니다. 임베딩 서빙, 쿼리 언더스탠딩, 검색 색인 순이었습니다.

지금 할 수 없는 것을 정확히 짚어준 되물음이 나중에 할 수 있는 구조를 만들어준 셈입니다.

 

마지막 날, 결과는 어땠는가

외부 3종(임베딩 서빙의 TEI, 쿼리 언더스탠딩의 Bedrock, 검색 색인의 OpenSearch)을 모두 실연동한 상태로 전체 평가셋을 측정했습니다.

 

성능 목표는 통과했지만 핵심 품질 지표는 목표에 미치지 못했습니다.

눈여겨볼 점은 리랭킹을 거치면 포함률이 오히려 떨어졌다는 것입니다. 판매 지표 기반의 기존 랭킹 모델이 자연어 검색의 관련성 신호와 항상 같은 방향을 보지는 않는다는 뜻이었습니다.

이것을 발견할 수 있었던 이유가 있습니다. 같은 질의에 대해 기존 키워드 검색과 하이브리드 검색, 그리고 리랭킹까지 적용한 결과를 나란히 놓는 3열 비교 데모를 만들어뒀기 때문입니다. 리랭킹 전후를 따로 보지 않았다면 최종 53%만 보고 원인을 짐작하고 있었을 것입니다.

그리고 이 3열 비교는 1장에서 말씀드린 그 재검토 때 요청한 것입니다. 전체를 위임했다가 되잡아 설계를 다시 들여다보던 시점에, 리랭킹이 얼마나 기여하는지 눈으로 보고 싶다고 요청해서 2열이 3열이 됐습니다. 위임을 회수한 덕에 만들어진 화면이 3일 뒤에 반직관적인 결과를 잡아냈습니다.



기반 자체는 견고했습니다. 자동화 테스트는 두 유닛 합산 약 90건, 커버리지 약 88%로 CI 게이트를 상시 통과했고, 실제 색인의 매핑은 저희가 설계 단계에서 가정한 스키마와 정확히 일치했습니다.


4. 시행착오

앞에서 AI가 모순을 되물어준 이야기를 했다면, 이번에는 반대입니다. AI가 되묻지 않아서 놓친 것들입니다.

 

"제한한다"는 필터였을까요, 범위였을까요

저희는 요구사항에 이렇게 적었습니다.

검색 대상을 뷰티 13개 대카테고리로 제한한다.

머릿속 의도는 평가와 측정의 범위였습니다. 결과 중 뷰티 상품이 얼마나 포함됐는지를 재는 기준이었을 뿐, 검색 자체를 뷰티로 가두려던 것이 아니었습니다.

AI는 문장을 문자 그대로 받아들여 QueryDSL 하드 필터로 구현했습니다. 그리고 이번에는 되묻지 않았습니다.

문장 자체에는 모순이 없었기 때문입니다. 다른 문서와도 충돌하지 않았습니다. 어긋난 곳은 문서와 저희 머릿속 의도 사이였고, 그건 AI가 볼 수 없는 영역이었습니다.

 

뒤늦게 발견하고 되잡았을 때, 고쳐야 할 곳은 코드만이 아니었습니다. 요구사항과 유저 스토리, 애플리케이션 설계, 두 유닛의 기능 설계, 그리고 코드까지 같은 해석을 공유하고 있었습니다.

이때 단계마다 산출물을 남겨둔 것이 도움이 됐습니다. 고쳐야 할 곳을 하나씩 찾아 헤매지 않고, 문서를 따라가며 한 번에 맞출 수 있었습니다.

그리고 재해석 덕에 지표가 살아났습니다. 13개 대카테고리로 하드 필터링하는 동안에는 뷰티 포함률이 언제나 100%였습니다. 필터가 보장하니 측정할 것이 없었습니다. 필터를 걷어내고 전체 상품을 대상으로 검색하게 되자 비로소 뷰티 포함률이 의미 있는 품질 지표가 됐습니다.

앞에서 본 53%라는 숫자는 이 재해석 이후에야 존재할 수 있는 숫자입니다.

 

코드는 나눌 수 있었지만 기록은 나눌 수 없었습니다

4명이 3일 안에 개발을 완료하고자, Unit A와 Unit B 두 개의 유닛으로 나누어 움직였습니다.

Unit A · Query Understanding + 검색 API는 검색 파이프라인 본체를 맡았습니다. 질의에서 속성을 뽑고, 질의를 벡터로 인코딩하고, 세 신호를 결합한 QueryDSL로 조회하고, 기존 랭킹 모델로 재정렬해 응답을 만드는 흐름 전체입니다. 단계별 타임아웃과 실패 시 폴백 처리도 여기 속했습니다.

Unit B · 검색 모델링 + 대시보드는 성과를 눈으로 확인하는 영역을 맡았습니다. 3열 비교 데모와, 평가셋 전체를 돌려 뷰티 포함률과 응답 시간을 집계하는 오프라인 평가 하네스입니다. 평가는 시간이 오래 걸리니 비동기 잡으로 돌리고 진행률을 화면에서 폴링하게 했고, 실패한 질의는 사유를 펼쳐볼 수 있게 만들었습니다.

두 유닛이 만나는 지점은 두 개로 제한했습니다. 공유 DTO와 검색 서비스의 공개 메서드 두 개입니다. 이 계약을 먼저 고정한 덕에, Unit A가 아직 빈 껍데기일 때도 Unit B 개발이 끝까지 진행될 수 있었습니다. 상대편 구체 클래스를 참조하지 않고 구조적 계약에만 의존하게 만들어서, 한쪽이 다른 쪽 코드를 건드릴 이유를 없앴습니다.

그래서 병합할 때 애플리케이션 코드 충돌은 한 건도 없었습니다.

정작 부딪힌 곳은 다른 데였습니다.

 

병합 커밋의 충돌 파일은 다섯 개였고 전부 문서와 설정이었습니다. audit 로그, 상태 추적 문서, 요구사항 문서, 유저 스토리 문서, 환경 설정 예시 파일.

두 유닛이 각자 AI-DLC를 돌렸으니 각 세션이 자기 대화 기록을 audit 로그에 남깁니다. Unit A가 112줄, Unit B가 130줄을 추가했습니다. 내용은 전혀 겹치지 않습니다. 서로 다른 세션의 서로 다른 대화입니다. 그런데 둘 다 파일 끝에 덧붙이기 때문에, git이 보는 것은 "같은 위치를 양쪽이 고쳤다"였습니다.

append-only 파일이 병합에 가장 취약합니다. 코드는 소유 경계를 나눠서 충돌을 막을 수 있었지만, audit 로그와 상태 문서는 프로젝트 전체의 단일 기록이라 나눌 수가 없습니다. 해결 방향도 코드와 반대였습니다. 코드 충돌은 한쪽을 고르는 것이 정답인 경우가 많은데, audit 로그에서 한쪽을 고르면 그 세션의 의사결정 기록이 통째로 사라집니다. 충돌 해결 문제가 아니라 누락 방지 문제였습니다.

그래서 병합 직전에 파일별 처리 정책을 문서로 고정했습니다. audit 로그와 상태 문서는 반드시 양쪽 내용의 합집합으로 두고 절대 덮어쓰지 않는다, 공유 DTO는 계약 소유자 쪽을 기준으로 삼는다는 식입니다.

한계도 남았습니다. 정책은 "시간순으로 병합한다"였는데 그건 지키지 못했습니다. 한쪽 세션의 타임스탬프가 초 단위였던 반면 다른 쪽은 날짜 단위여서 정렬 키가 없었습니다. 결국 한쪽 기록 블록이 다른 쪽 기록 사이에 통째로 끼워진 형태가 됐습니다. 정책은 세웠지만 집행할 근거가 없었던 셈입니다.

audit 로그를 세션별 파일로 쪼개고 타임스탬프 형식을 규칙으로 못 박는 것이 다음 과제입니다.

한 가지 덧붙이면, 요구사항 문서와 유저 스토리 문서가 충돌한 원인은 앞에서 이야기한 그 재해석이었습니다. 두 세션이 각각 같은 요구사항 문장을 고쳤기 때문입니다.

코드는 소유 경계로 나눌 수 있었지만 요구사항은 나눌 수 없었습니다. 요구사항 변경은 언제나 전역 사건입니다.

기록을 남기는 것이 핵심인 방법론이라면, 그 기록을 어떻게 합칠지도 처음부터 설계했어야 했습니다.


5. 소감

요구사항을 쓰는 방식이 바뀌었습니다

AI는 문서와 문서 사이의 모순은 잘 잡아줍니다. 캐시를 빼면 응답 목표와 충돌한다는 지적처럼, 서로 다른 문서에 적힌 두 진술이 어긋나면 근거를 들어 되묻습니다.

하지만 문서와 저희 머릿속 의도의 간극은 잡아주지 못합니다. "제한한다"는 한 단어가 검색 필터인지 측정 범위인지는 문서만 봐서는 알 수 없습니다. 그 문장은 어떤 다른 문서와도 충돌하지 않았고, 그래서 아무도 되묻지 않았습니다.

그 뒤로는 그런 동사를 쓸 때 필터인지 범위인지를 문서에 명시하게 됐습니다.

자연스럽게 문서가 길어졌습니다. 3일 동안 남은 산출물 문서가 5,800여 줄, 애플리케이션 코드가 3,800여 줄입니다. 문서가 코드보다 많습니다.

낭비처럼 보이지만, 하나의 문장을 다시 해석했을 때 고쳐야 할 곳을 요구사항부터 코드까지 한 번에 찾아낼 수 있었던 것은 그 문서 덕분이었습니다.

승인 게이트는 편하기만 한 방식이 아니었습니다

단계마다 질문에 답을 채우는 일에는 분명한 피로가 있었습니다. 그래서 중간에 전체를 위임했고, 다시 되잡았습니다.

돌아보면 이 방법론의 실제 난점은 승인하는 일이 아니라 위임과 통제 사이의 적정선을 찾는 일이었습니다. 너무 촘촘하게 붙잡으면 속도가 나오지 않고, 한 번에 다 맡기면 설계가 의도에서 조용히 멀어집니다.

3일 안에 부족한 점을 눈으로 확인했습니다

무엇을 왜 만들지, 어떻게 만들지를 문서로 먼저 정의하고 시작한 덕에 3일 안에 실제로 동작하는 결과까지 갈 수 있었습니다.

품질은 목표에 미달했고 그대로 서비스할 수는 없습니다. 그런데 이번 3일의 성과를 "동작하는 API를 만들었다"로만 정리하고 싶지는 않습니다.

더 값진 것은 뷰티 포함률 53%, 리랭킹이 5%p를 깎는다는 숫자를 손에 쥔 것입니다. 감으로 "괜찮은 것 같다"가 아니라 측정된 수치로 부족한 지점을 지목할 수 있게 됐습니다.

이것이 가능했던 이유는 데모와 오프라인 평가를 나중에 붙이는 부가 기능이 아니라 처음부터 별도 유닛으로 설계했기 때문입니다. 만드는 것과 재는 것을 같이 계획한 덕에, 3일 뒤에 무엇을 개선해야 하는지를 추측이 아니라 데이터로 정할 수 있었습니다.


앞으로

이번 실측으로 드러난 과제를 바탕으로 다음 방향을 정리하고 있습니다. 팀 논의를 거쳐 확정할 예정입니다.

품질 개선이 최우선입니다. 지렛대로 보고 있는 것은 세 가지입니다. 대화체 질의에 대한 이해도를 높이는 것, 속성 매칭을 정교화해서 뽑은 속성이 색인에 더 잘 맞물리게 하는 것, 그리고 관련성을 깎고 있는 리랭킹 정책을 다시 보는 것입니다.

임베딩 고도화. 뷰티 속성과 상품 상세 텍스트를 반영한 도메인 특화 임베딩으로 색인을 교체하고, 전후를 오프라인 평가로 비교해 검증할 계획입니다.

성능 유지. p95는 목표 안이지만 여유가 크지 않습니다. 실연동에서 확인된 하이브리드 kNN 병목이 튜닝 대상입니다.

운영 준비. 인증과 배포, 모니터링은 이번 범위에서 제외했습니다. 실제 서비스로 올리기 전에 다뤄야 할 영역입니다. 이 API가 최종적으로 받아야 할 입력은 쇼핑Agent가 던지는 문장형 질의이므로, 그 연결까지가 남은 과제입니다.


맺음말

캐시와 응답 목표의 충돌을 짚어준 것도 AI였고, "제한한다"를 하드 필터로 구현한 것도 AI였습니다. 좋은 결과는 결국 사람이 얼마나 또렷하게 정의하느냐에 달려 있었습니다.

3일 동안 가장 많이 배운 것은 AI가 코드를 얼마나 빨리 쓰는지가 아니었습니다. 저희가 무엇을 얼마나 모호하게 적고 있었는지였습니다.

비슷한 방식으로 실제 업무 과제에 AI 주도 개발을 적용해보려는 분들께, 시행착오까지 담은 이번 기록이 작은 참고가 되기를 바랍니다.

 

 

 

 

 

 

 

 

 

 

강슬기 Ari | AX본부 홈쇼핑AX부문 검색추천파트(H)

 

검색 서비스 개발을 담당하고 있습니다

사용자의 검색 의도를 파악해 가치 있는 경험으로 연결하며,

탄탄하고 안정적인 검색 시스템을 만들어갑니다.