Action

필드별 컴포넌트 개발

아래와 같은 필수 입력 필드의 값을 순차적으로 생성 및 추천하는 컴포넌트들을 개발했습니다.

상품명

sequenceDiagram
    autonumber
    participant Client as Client<br/>(상품 등록 화면)
    participant API as AI 상품등록 API
    participant LLM as LLM API

    Client->>API: 썸네일 이미지
    API->>LLM: 이미지 + 프롬프트
    LLM-->>API: 상품명 생성
    API-->>Client: 상품명 (AI 제안 UI 표시)
          

이미지 업로드 후 상품명이 생성되기까지의 레이턴시가 길면 사용자가 기다려주지 않을 것이라 판단해, 업로드된 상품 이미지들 중 썸네일로 노출되는 제일 첫 이미지만을 사용했습니다. 썸네일 이미지로부터 생성된 상품명은 자동으로 필드에 채워지지 않고, 키보드 상단에 AI 제안 뱃지 형태로 노출됩니다. 사용자가 뱃지를 탭하면 채택되고, 무시하면 직접 입력을 이어갈 수 있어 사용자의 기존 상품 등록 경험을 해치지 않도록 설계되었습니다.

카테고리

sequenceDiagram
    autonumber
    participant Client as Client<br/>(상품 등록 화면)
    participant Category as 카테고리 추천 API

    Client->>Category: 상품명
    Category-->>Client: 카테고리 ID 리스트<br/>(추천 카테고리 캐러셀 노출)
            

카테고리 분류는 도메인이 좁고 표현이 정형화되어 있어 LLM을 사용할 필요가 없었습니다. 팀에서는 번개장터 텍스트 데이터로 학습시킨 경량 텍스트 인코더를 이미 확보해 두었기에, 이 인코더를 상품 DB에서 수집한 (상품명, 카테고리 ID) 페어 데이터로 fine-tuning하여 상품명으로 카테고리 ID를 예측하는 분류기를 만들었습니다. 추천 카테고리 캐러셀 영역에서 버튼을 4개 노출하는 디자인이었기 때문에 HitRatio@4를 목표 metric으로 학습을 진행했고, 상품명을 받아 4개의 카테고리 ID를 응답하는 별도 API를 제작해 Client가 직접 호출하도록 통합했습니다.

상품 옵션

sequenceDiagram
    autonumber
    participant Client as Client<br/>(상품 등록 화면)
    participant API as AI 상품등록 API
    participant Bunjang as 옵션 스키마 API
    participant LLM as LLM API

    Client->>API: 상품명, 카테고리 ID
    API->>Bunjang: 카테고리 ID
    Bunjang-->>API: 해당 카테고리의 옵션 목록 (Nullable)

    opt 옵션 목록이 존재하는 경우
        API->>LLM: 상품명, 옵션 목록 + 프롬프트
        LLM-->>API: 상품명에서 추출한 옵션명 (Nullable)
    end

    API-->>Client: 옵션 정보 ID (Nullable)
          

옵션 추천 기능은 선택지가 매우 많은 카테고리에서의 이탈을 방지하기 위한 것이었습니다. 예를 들어, 신발 카테고리는 목록에 한국·미국· 유럽 사이즈까지 존재하기 때문에, 사용자가 하나하나 살피며 자신에게 맞는 옵션을 고르게 하는 것은 피로감을 유발할 수 있다고 판단했습니다. 그래서 카테고리 추천과 같은 맥락에서 찾는 옵션을 선제적으로 제안하는 기능을 도입하기로 했습니다.

이 기능 역시 카테고리 추천 모델처럼 자체 분류 모델로 구현할 수도 있었습니다. 하지만 상품 옵션은 카테고리별로 스키마가 다르기 때문에 학습 데이터와 모델 아티팩트를 카테고리별로 별도로 관리해야 했습니다. 이에 더해 옵션 추가, 제거 같은 스키마 변경이 있을 때마다 데이터 갱신과 재학습이 필요해 관리 부담이 컸습니다. 반면 LLM은 프롬프트에 옵션 목록을 그때그때 주입하는 방식으로 스키마 변경에 즉시 대응할 수 있습니다. 이 관리 부담 대비 이점을 근거로 LLM 추출 방식을 선택했고, 카테고리 ID로 옵션 스키마 API에서 스키마를 조회한 뒤 스키마가 존재하는 경우에만 LLM을 호출해 옵션명을 추출하는 구조로 구현했습니다.

상품 설명

sequenceDiagram
    autonumber
    participant Client as Client<br/>(상품 등록 화면)
    participant API as AI 상품등록 API
    participant LLM as LLM API

    Client->>API: 썸네일 이미지, 상품명, 카테고리
    API->>LLM: 이미지, 상품명, 카테고리 + 프롬프트
    LLM-->>API: 상품설명 생성
    API-->>Client: 상품설명 (AI 제안 UI 표시)
          

상품 설명은 이미지, 상품명, 카테고리 세 가지 정보를 통합해 생성합니다. 이때 상품 설명이 너무 길게 생성되어 상품 등록 지면을 과하게 덮어버리는 것을 방지하기 위해, UI 영역의 크기에 맞도록 텍스트 길이를 통제해야 했습니다. 그래서 첫 문장은 상품 제목 성격의 한 줄, 다음 두 문장은 이미지에서 보이는 특징 묘사, 마지막 한 문장은 구매를 촉진하는 문구로 마무리하는 규약을 프롬프트에 명시했습니다.

배포 후에는 카테고리별 생성 결과와 실제 등록된 텍스트를 대조하여 ROUGE-L 스코어를 계산해 모니터링했습니다. 이를 통해 텍스트 생성 결과가 실제로 채택되고 있는지, 채택되었다면 얼마나 원본 그대로 사용되었는지를 대략적으로 가늠할 수 있었습니다.

상품 가격

sequenceDiagram
    autonumber
    participant Client as Client<br/>(상품 등록 화면)
    participant API as AI 상품등록 API
    participant LLM as LLM API
    participant Search as 상품 검색 엔진
    participant Pricer as 추천 가격 계산 API

    Client->>API: 상품명, 카테고리
    API->>LLM: 상품명, 카테고리 + 프롬프트
    LLM-->>API: 이 상품이 검색될 만한 검색어
    API->>Search: 검색어
    Search-->>API: 검색된 상품 목록 (판매중)

    alt 카테고리가 같은 상품이 충분한 경우
        API->>Pricer: 상품명, 검색된 상품 목록
        Pricer-->>API: 추천 가격 범위 (min, max)
    else 상품이 부족한 경우
        Note over API: 가격 추천 불가
    end

    API-->>Client: 추천 가격 범위 또는 빈 결과
          

등록하려는 상품의 현재 시세를 기준으로 가격을 제안해야 하기 때문에, 먼저 LLM에게 “이런 카테고리와 상품명을 가진 상품이 검색될 수 있을만한 검색어”를 생성하도록 한 뒤 번개장터 상품 검색 엔진에 입력했습니다. 검색된 상품들 중 같은 카테고리 상품들의 가격 분포를 기준으로 대표 가격을 추정하게 되는데, 이때 검색된 상품이 적으면 이 추정치의 variance가 높아져 값을 신뢰할 수 없게 됩니다. 그래서 LLM이 생성한 검색어로 확보한 검색 결과가 충분할 때만 가격을 추천하도록 설계했습니다. 검색 결과가 충분할 때는 검색된 상품 목록을 상품명과의 텍스트 유사도와 판매 완료 여부를 종합해 reranking한 뒤, 상위에 정렬된 상품들의 가격 중위값을 기준으로 추천 가격 범위를 계산해 반환합니다.