본문으로

북켓몬 AI 도서 검색·추천 · 팀 프로젝트

질문의 뜻에 맞는 책만 위로 올려
엉뚱한 추천 14권을 2권으로

사용자가 문장으로 원하는 책을 말했는데 인기 많은 책이나 신간이 먼저 올라오면, 추천 목록은 금방 믿을 수 없게 됩니다. 북켓몬은 약 10만 권에서 질문의 뜻이 가까운 책을 찾은 뒤, 장르·독자층·싫어하는 조건을 더하고 빼서 질문에 맞는 책만 위 5권으로 올립니다.
이 순위 규칙을 설계하고, 팀원 4명이 직접 채점해 효과를 쟀습니다.

엉뚱한 추천 90권 중 14권 → 2권 (15.56% → 2.22%)순위 규칙 끔/켬 · 18개 질의 상위 5권 · 오프라인 평가

내 담당순위 계산 수식과 거르는 조건 직접 설계 · 평가 기준 설계와 채점 · 추천 파이프라인과 DB 스키마 설계 주도

내가 정한 것
문장 뜻 점수를 중심에 두고 장르·독자층·싫어하는 조건을 더하고 빼는 순위 계산 · 0·1·2점 채점 기준
팀원이 맡은 것
FE·BE·AI 세 직군으로 나눈 서비스 구현 · 18개 질의 채점(팀원 4명)
내가 확인한 것
규칙을 끈 목록과 켠 목록을 18개 질의의 상위 5권(90권)으로 비교
이주형팀장 · 기획 리드AI 추천 설계·평가2026.03–05
데모 영상 라이브 피치
북켓몬을 나타내는 기존 콜라주 스티커
작동 프로토타입

질문이 추천 목록이 되는 과정

서비스 흐름
  1. 01

    원하는 책 말하기

    장르나 읽는 목적을
    문장으로 입력합니다.

  2. 02

    후보 도서 찾기

    문장의 뜻이 가까운 책을
    약 10만 권에서 찾습니다.

  3. 03내 담당

    추천 순위 조정

    장르·독자층·싫어하는 조건을
    더하고 빼 순위를 정합니다.

  4. 04

    읽을 책 고르기

    추천 목록에서 책을 고르고
    독서 기록을 남깁니다.

많은 책이 거름 장치를 지나 몇 권으로 줄어드는 그림

처음 설계를 버리고 다시 짠 과정

내 판단
  1. 문제

    처음에는 취향·인기·신간 점수를 가중합하는 수식을 설계했습니다. 돌려 보니 인기·신간 점수가 커져 사용자 맞춤의 의미가 없었습니다.

  2. 판단

    질문을 중심으로 후보를 더 뽑도록 순위 계산을 다시 짰습니다. 문장 뜻 점수에 가장 큰 비중을 두고 인기·신간 항은 뺐습니다. 장르·독자층·싫어하는 조건은 가점과 감점으로 넣었습니다.

    처음 수식과 바꾼 수식 보기
  3. 검증

    이 규칙을 끈 목록(문장 뜻 점수만)과 켠 목록을 18개 질의의 상위 5권으로 비교했습니다. 팀원 4명이 0·1·2점으로 직접 채점했습니다.

  4. 결과

    맞지 않는 책이 90권 중 14권에서 2권으로 줄었습니다.

    평가 자세히 보기
추천 기준

질문 중심 순위 계산

질문 중심·섞기·취향 중심 세 가지 비중 묶음으로 순위를 매깁니다.

첫 사용

기록이 쌓이면 개인화로 전환

취향 정보가 적을 때와 쌓인 뒤를 구분합니다.

독서 이후

기록에 따라 캐릭터 성장

독서 기록이 경험치가 되어 북켓몬이 알에서 진화합니다.

추천 품질 평가

순위 규칙을 켰을 때,
맞지 않는 책이 줄었는가

장르·독자층·싫어하는 조건을 반영하는 순위 규칙을 끈 목록(문장 뜻 점수만)과 켠 목록을 같은 질의로 비교했습니다.

전체 평가 조건과 표 보기
규칙 끔15.56%
규칙 켬2.22%

추천된 책 한 권 부적합으로 채점된 책

18개 질의 × 상위 5권 = 90권. 팀원 4명이 0·1·2점으로 직접 채점해 0점을 부적합으로 셌습니다.

오프라인 평가 결과입니다. 규칙을 고친 뒤 같은 18개 질의로 다시 잰 값이라 따로 떼어 둔 시험 질의는 없고, 실서비스 이용 효과는 추가 검증 대상입니다.

추천·검색 서비스에 붙인다면

이 순위 규칙이 맡을 수 있는 일
커머스 검색

문장 검색에 맞는 상품을 위로

뜻이 가까운 후보를 넓게 찾은 뒤, 조건을 더하고 빼는 순위 규칙으로 질문에 맞는 것만 상위에 둡니다.

콘텐츠 추천

싫어하는 조건은 아래로

사용자가 피하고 싶다고 말한 조건을 감점으로 넣어, 추천 목록에서 엉뚱한 항목을 줄입니다.

품질 관리

사람이 채점하는 평가 틀

규칙을 끈 목록과 켠 목록을 같은 질의로 비교하고 0·1·2점으로 채점해, 규칙 하나의 효과를 숫자로 봅니다.

평가 규모는 18개 질의 · 팀원 4명 채점이고, 실서비스 이용 효과는 아직 재지 않았습니다.

설계와 근거

궁금한 부분을 더 자세히

추천 순위를 어떻게 설계했나처음 설계한 수식, 바꿔서 구현한 순위 계산, 처음 수식 체험

처음 설계 · 4월 팀 회의록

취향·인기·신간 가중합

최종 점수 = w₁·취향 + w₂·인기 + w₃·신간
신규 사용자: 0.1 · 0.7 · 0.2
기록이 쌓인 사용자: 0.5 · 0.3 · 0.2

인기는 도서관 대출 통계, 신간은 출간일로 계산하도록 설계했습니다.

실제 구현 · 저장소의 순위 계산 코드

문장 뜻 점수가 중심인 세 가지 비중 묶음

묶음문장 뜻 점수 비중
질문 중심0.74
섞기0.57
취향 중심0.45

나머지 비중은 장르·독서 목적·서재 기록·리뷰 가점과 독자층 불일치·싫어하는 조건 감점으로 나눴습니다. 인기·신간 항은 없습니다.

바꾼 이유 처음 수식으로 돌려 보니 인기·신간 점수가 커져 사용자 맞춤의 의미가 없었습니다. 그래서 질문을 중심으로 후보를 더 뽑도록 바꿨습니다.

처음 설계 수식 체험

인기 비중을 올리면 베스트셀러가 1위로 올라옵니다

처음 설계한 수식(취향·인기·신간 가중합)을 예시 책 6권으로 돌려 볼 수 있습니다. 「콜드스타트」 묶음을 누르면 취향 점수가 낮은 베스트셀러가 1위가 됩니다. 실제 서비스에서 이 수식을 버린 이유입니다.

w₁ 개인화 S_personal0.50
w₂ 인기 S_pop0.30
w₃ 신간 S_recency0.20
지금 1위: … · 같은 6권인데 1위가 바뀝니다. 인기 비중이 커질수록 취향과 상관없이 많이 빌린 책이 위로 올라옵니다.
※ 예시 책 6권으로 처음 수식이 어떻게 움직이는지만 보여 줍니다. 위 평가 수치와는 관계가 없습니다.
무엇을 어떻게 평가했나전체 평가표와 평가 조건
측정 결과

측정된 결과

설계가 실제로 효과 있었는지를 데이터로 확인한 기록입니다.
수치는 오프라인 평가 기준이며 실서비스 성능을 단정하지 않습니다.

규칙 켬과 끔 비교 · 상위 5권
조건 Precision@5 Bad Rate@5 Δ Precision
규칙 켬 (장르·독자층·싫어하는 조건 반영) 97.78% 2.22% 기준
규칙 끔 (문장 뜻 점수만) 84.44% 15.56% Δ −13.34%p
※ 18개 쿼리(Q001~Q018) · 팀원 4명이 0/1/2점 직접 채점 · Top-5 기준. 오프라인 평가이며 실서비스 A/B 테스트는 미완.
90.52%
긍정 선호 포착율 · test
개인화 · LightFM

LightFM 개인화

합성 페르소나 1,109명 · 7:2:1 분할 · Qdrant 호출 없는 오프라인 50개 채점. 비선호 노출 1.45%(test).

LightFM val 46.84% · test 29.15%(gap 병기)

91.14%
KURE Precision@50 · 채택
벤치마크 · 임베딩

임베딩 모델 두 종 비교

CLOVA 90.09%보다 1.05%p 높았고, 응답은 0.59초 느렸습니다. 품질 쪽을 골랐습니다.

같은 Qdrant 상위 50권 조건 비교. 차이가 작아 질의를 늘려 다시 재 볼 대상.

정성적 성과

18쿼리

추천 규칙의 효과를 18개 쿼리로 채점

Q001~Q018 채점 하네스를 설계하고 팀원 4명이 직접 채점했습니다. 버그(오디언스 미스매치) 발견 → 신호 보강 → 정량 개선 루프를 직접 운영했습니다.

2레이어

레이어 분리로 무중단 롤링 업데이트 구조

Spring Boot(서비스)와 FastAPI(AI)를 나눠, 모델을 바꿀 때 백엔드는 그대로 두고 AI 서버만 다시 올립니다.

10만벡터

DB 스키마 설계

RDB 스키마(사용자·XP·도서·상호작용) 설계를 주도했습니다. 도서 벡터는 약 10만 건(1,024차원)을 Qdrant에 넣었습니다.

어떤 구성으로 만들었나검색·AI 서버·데이터 저장소의 연결
시스템 구성

아키텍처 & 데이터 흐름

질의에서 최종 추천까지 레이어별 역할을 분리해,
ML 모델 교체 시 무중단 롤링 업데이트가 가능한 구조를 설계했습니다.

GTE Reranker 전용 서버 분리 운영

다시 정렬하는 단계는 HCX와 gte-reranker-server 가운데 고를 수 있게 따로 떼어 두었고, 기본 설정에서는 꺼져 있습니다. ML 모델을 교체할 때 Spring Boot 백엔드는 건드리지 않고 AI 서버만 롤링 업데이트합니다. 레이어 분리 설계의 일부입니다.

근거: Nogueira & Cho, Passage Re-ranking with BERT (2019). bi-encoder(빠른 후보 압축) + cross-encoder(정밀 재정렬) 2-stage 표준 정립 [논문]
① React Frontend

사용자 인터페이스 & 인증

React + Vite SPA. Google/Kakao OAuth 2.0 소셜 로그인. 북켓몬 진화 연출·XP 대시보드 포함.

React 18ViteTypeScriptGoogle/Kakao OAuth
HTTP API
② Spring Boot: API Gateway

인증 · 라우팅 · 비즈니스 로직

소셜 로그인(OAuth2)과 독서 기록에 따른 경험치·캐릭터 단계 저장. RDB 스키마 설계 주도.

Java 21Spring Boot 3Spring SecurityJPAPostgreSQL+PostGIS
Internal API
③ FastAPI: AI Server (2-Stage Retrieve→Rerank)

벡터 추론 · 추천 파이프라인 · GTE Reranker

Spring Boot(비즈니스)↔FastAPI(AI) 레이어 분리. Stage 1 bi-encoder(KURE·Qdrant) → Stage 2 cross-encoder(GTE Reranker 전용서버 + HCX 배치리랭킹) 순서로 추천 흐름. ML 모델 교체 시 AI 서버만 롤링 업데이트. APScheduler로 대출 통계 주기 갱신.

Python 3.11FastAPILightFMGTE RerankerAPSchedulerQdrant
Vector / Cache
④ 데이터 레이어

PostgreSQL · Qdrant · Redis: 역할 분리

PostgreSQL(사용자·XP·도서 RDB)·Qdrant(10만 벡터 · 1,024차원 Cosine)·Redis(세션·캐시)로 역할 분리. Docker Compose · K3s · GitLab CI/CD · Cloudflare · NAS 인프라 위에서 운영.

PostgreSQL+PostGISQdrantRedisDocker ComposeK3sGitLab CI/CD
임베딩 비교

한국어 임베딩 두 종을 같은 조건에서 비교

한국어 임베딩 2종을 동일 환경에서 테스트

  • Precision@50
  • 레이턴시

트레이드오프를 측정해 비교하고 선택 근거에 반영

*임베딩: 문장을 숫자 벡터로 바꾼 표현 / Precision@50: 상위 50개 중 적합 비율

임베딩 모델 비교
품질을 택한 트레이드오프 · Precision@50 +1.05%p ↔ 레이턴시 +590ms
검색 단계에서 뽑는 후보가 이후 순위의 재료가 되기 때문에, 같은 Qdrant 상위 50권 조건에서 두 모델의 정확도와 응답 시간을 비교했습니다.
*Bi-encoder: 질문·문서를 따로 임베딩해 빠르게 후보 압축
선택 모델 · 최종 사용
KURE Embedding
Precision@5091.14%
vs CLOVA 대비+1.05%p
레이턴시 비용+590ms
선택 근거품질 우선 · 오프라인 프로토타입
비교 모델
CLOVA Embedding
Precision@5090.09%
KURE 대비−1.05%p
레이턴시 이점−590ms
적합 시나리오응답 속도가 더 중요할 때
결론

추천 결과를 한 번에 보여 주는 화면이라 0.59초가 늘어도 품질 차이를 택했습니다. 차이가 1.05%p로 작아, 질의를 늘려 다시 재 볼 대상입니다.

※ 동일 평가 환경 비교 KURE = 한국어 임베딩 모델 · 트레이드오프 선택은 프로젝트 컨텍스트(오프라인 프로토타입, 품질 우선)에 의거
2-Stage Retrieve → Rerank

Reranker를 파이프라인에 분리 운영한 이유

Stage 1빠름
Bi-encoder 검색

KURE Embedding으로 Qdrant에서 후보 100권을 빠르게 추출. 벡터 내적만 계산해 대규모 코퍼스에서도 낮은 지연을 유지.

Stage 2정밀
Cross-encoder 리랭크

켜면 GTE Reranker 서버가 질문과 책을 함께 읽고 최종 5권을 다시 정렬합니다. 기본 설정에서는 꺼져 있는 선택 단계입니다.

설계 근거 빠른 검색과 느리지만 정밀한 재정렬을 단계로 나누는 방식입니다. Nogueira & Cho, Passage Re-ranking with BERT (2019) [논문] 참고. 모델은 Alibaba-NLP/gte-multilingual-reranker-base를 씁니다.

*Cross-encoder: 질문·문서를 함께 넣어 정밀 채점 / GTE Reranker: 검색 결과를 다시 정밀 정렬하는 모델 / RAG: 검색한 내용으로 답을 생성하는 구조

기능을 더 자세히 보기개인화·독서 기록·도서관 대출 연동
핵심 기능

핵심 기능

추천 파이프라인, 콜드스타트(데이터 없는 신규 유저 추천 문제) 전환, 게이미피케이션 리텐션을 하나의 제품으로 묶었습니다.

추천 · 순위 계산

질문 중심으로 합산하는 여러 신호

문장 뜻 점수에 장르·독서 목적·서재 기록·리뷰 가점과 독자층 불일치·싫어하는 조건 감점을 더해 순위를 정합니다. 처음 설계한 취향·인기·신간 수식을 버린 이유는 「추천 순위를 어떻게 설계했나」에 있습니다.

콜드스타트

한국인 페르소나 데이터로 콜드스타트 세팅

NVIDIA에서 제공한 Nemotron 한국인 합성 페르소나 100만 건(CC-BY-4.0)을 시드와 시뮬 평가에 활용했습니다. 취향 기록이 없는 신규 사용자를 흉내 내 추천을 시험하는 데 썼습니다.

평가 · Eval

적용 전후를 18개 쿼리로 채점한 추천 규칙

18개 쿼리(Q001~Q018)에 사람이 0·1·2점을 매기는 채점 기준을 만들고, 규칙 적용 전후를 비교했습니다. 어긋난 추천(오디언스 미스매치)을 찾으면 신호를 보강하고 다시 채점했습니다.

개인화 · LightFM

LightFM 개인화

합성 페르소나 1,109명 데이터를 7:2:1로 나눠 학습한 LightFM 모델이 Top-50에서 Top-20을 추립니다. 긍정 선호 포착율 90.52%, 비선호 노출 1.45%(test). 전체 카탈로그 Positive Hit@20은 val 46.84% · test 29.15%입니다.

리텐션

게이미피케이션 리텐션

북켓몬은 추천받은 책을 읽고, 독서 기록을 남기면 경험치(XP)를 제공하는 서비스입니다. 사용자는 이 경험치로 나의 독서 파트너 북켓몬을 키울 수 있습니다. 책을 추천받고 다시 방문할 이유를 게이미피케이션으로 만듭니다.

실행 연동

도서관 대출 연동

도서관정보나루(data4library.kr)와 실시간 API로 연동해 추천받은 책의 대여 가능 여부를 확인 가능합니다. 추천이 실제 대출로 이어지게 하려는 기능입니다.

프로젝트 배경과 한계팀 구성·기간·추가 검증이 필요한 부분
프로젝트 배경

학습할 데이터는 사용자가 다시 찾아와야 생깁니다.

처음에는 모델 학습이 가장 큰 문제라고 봤습니다. 그런데 추천이 한 번으로 끝나면 학습할 데이터가 쌓이지 않습니다. 그래서 사용자에게 맞춘 추천과 함께, 독서 기록으로 캐릭터를 키우는 요소를 넣어 다시 찾아올 이유를 만들었습니다.

역할팀장 · 기획 리드 · AI 추천 파이프라인설계·평가 하네스·수식 설계 본인 주도
기여FE·BE·AI 3직군 팀장 협업RDB 스키마 설계 · 평가 하네스 · 수식 본인 주도
기간2026.03 ~ 053개월 · NLP 부트캠프 종합 프로젝트
팀FE · BE · AI 크로스팀GitLab 협업 · 실명 비공개
현재 상태작동 프로토타입오프라인 평가 완료 · 데모 영상 존재
타사 자산HyperCLOVA X · data4library.kr서비스 내 사용 · 재배포 아님
돌아보기

한계 & 다음 단계

오프라인 평가로 측정한 것과 측정하지 못한 것을 솔직하게 기록합니다.

행동·개인화 신호 효과: 제한적 측정

룰 ablation에서 행동·개인화 신호(리뷰·서재·audience) 효과는 "프로필 제외 평가" 구조상 제한적으로 측정됐습니다. 행동 신호 효과를 검증하려면 실제 유저 로그 기반 온라인 평가가 필요합니다.

→ 온라인 A/B 테스트 인프라 구성 → 룰·가중치 실시간 검증 루프 필요

LightFM 오프라인 근사: 실서비스 루프와 차이 존재 · val/test 간 gap

LightFM candidate-eval은 Qdrant 호출 없는 오프라인 근사(50개 채점) 기준입니다. val 46.84% · test 29.15%. 두 수치를 함께 보고하며, 실서비스 추천 루프와 완전히 동일한 환경 평가는 미완입니다.

→ Qdrant 연동 온라인 평가 루프 구성 필요

최적 조합 Precision 100%·Bad Rate 0% (오프라인 특정 구성 결과)

최적 조합(llm_search_query × dense × current) Top-5 결과는 오프라인 특정 구성 평가 기준이며, 모든 조합과 실서비스 성능을 뜻하지 않습니다.

→ 다양한 조합·실서비스 조건에서 추가 검증 필요

RAGAS(RAG 평가 프레임워크) · 다음 단계, 아직 미구현

현재 평가는 팀원 직접 채점 기반 질의-추천 적합성 중심입니다. RAGAS(Es et al., arXiv:2309.15217, 2023)의 faithfulness·answer_relevancy·context_recall 지표는 미측정입니다. HCX 리랭킹 단계의 hallucinate 여부를 정량화하는 데 적합하지만, 현재 미구현 상태입니다.

→ 향후 온라인 A/B 테스트와 병행해 RAGAS 기반 retrieval 품질 평가 적용 계획
사용한 기술 보기각 구성 요소에 쓰인 도구
사용한 기술

기술 스택

AI / LLM

  • HyperCLOVA X
  • KURE Embedding
  • GTE Reranker
  • LightFM

Backend

  • Java 21 · Spring Boot 3
  • Spring Security · JPA
  • Python 3.11 · FastAPI
  • APScheduler

Frontend

  • React 18
  • Vite
  • TypeScript

Infra / Data

  • PostgreSQL+PostGIS
  • Qdrant (1,024d)
  • Redis
  • Docker · K3s · GitLab CI/CD
이주형 · 북켓몬

엉뚱한 추천을
90권 중14권 → 2권

읽고 싶은 책을 문장으로 말하면, 질문의 뜻에 맞는 책만 위 5권으로 올려 주는 도서 검색·추천 서비스입니다. 순위를 매기는 규칙을 설계하고, 팀원 4명이 직접 채점해 효과를 쟀습니다.

01 이렇게 돌아갑니다
  1. 문장으로 말하기
  2. 비슷한 책 100권 찾기
  3. 순위를 매겨 5권 고르기내 담당
  4. 읽고 기록 남기기

후보는 100권 → 50권 → 20권 → 5권으로 줄어듭니다(설계 기준).

02 다시 짠 계산

취향·인기·신간을 섞어 점수 매기기

질문의 뜻을 중심에 두고, 장르·독자층·싫어하는 조건을 더하고 빼기

처음 수식은 인기·신간 점수가 커져 사용자 맞춤의 의미가 없었습니다.

03 내가 맡은 것
  • 순위 계산 수식과 거르는 조건 직접 설계
  • 평가 기준 설계와 채점
  • 추천 파이프라인과 DB 스키마 설계 주도
  • 팀장 · 기획 리드
04 결과2권

90권 중 맞지 않는 책 14권에서 2권으로 (15.56% → 2.22%)

규칙 끔
규칙 켬

18개 질의 × 상위 5권 · 팀원 4명이 0·1·2점으로 직접 채점 · 같은 질의로 다시 잰 오프라인 평가입니다.

05 다시 오게 만드는 장치
알부화진화

독서 기록이 경험치가 되어 북켓몬이 자랍니다.

설계 비교와 평가 표는 원래 페이지에 있습니다.

GitHub 저장소