mybookshelf

나의 서재 — 개인 가상 서재 앱 설계

1. 컨셉

평생 책을 읽어온(그리고 앞으로도 읽을) 사람을 위한, 클라우드 위에 존재하는 나만의 3D 가상 서재.

2. 핵심 기능

# 기능 설명
1 블로그 자동 수집 네이버 블로그 ‘독서,책’ 카테고리 글을 가져와 책으로 변환 (아래 §3 검증 완료)
2 책 등록(텍스트) 제목만 입력 → 책 메타데이터 API로 저자/출판사/표지/출간연도 자동 조회·매칭
3 책 등록(사진) 실물 책 촬영 → 바코드(ISBN) 인식으로 자동 등록
4 3D 가상 서재 여러 테마의 3D 책장/공간. 책 클릭 시 상세 패널(표지·저자·독후감·구절)
5 인생 시기 분류 초/중/고/대/사회초년/현재 등 시기별 책장 구분
6 추천 엔진 읽은 책 기반 추천 + 베스트셀러 연동
7 독후감/구절 책별 감상, 인상 깊은 구절 저장
8 검색/필터 제목/저자/카테고리/시기별 탐색

3. 블로그 연동 — 기술 검증 완료 ✅

대상: blog.naver.com/joekang (토리아빠의 50플러스 인사이트, 전체 2,516편)

‘독서,책’ 카테고리 = categoryNo 3, 총 168편 (예상했던 100여 편보다 많음) data/blog_posts.json에 168편 전체 목록을 이미 수집해 두었다. (scripts/fetch_blog_posts.py)

동작하는 엔드포인트

모두 로그인 없이 호출 가능. Referer: https://blog.naver.com/ 헤더가 없으면 csrf 에러가 난다.

용도 엔드포인트
카테고리 목록 m.blog.naver.com/rego/CategoryList.naver?blogId={id}
글 목록(페이징) blog.naver.com/PostTitleListAsync.naver?blogId={id}&categoryNo={no}&currentPage={p}&countPerPage=30
글 본문 m.blog.naver.com/{id}/{logNo} → se-main-container 영역 파싱
최신글 RSS rss.blog.naver.com/{id}.xml (증분 동기화용)

주의: 글 목록 응답 JSON에 잘못된 escape가 섞여 있어 json.loads가 실패한다. 정규식으로 필요한 필드만 추출해야 한다. 제목은 URL 인코딩되어 있다.

수집 결과에서 나온 설계적 발견

(1) 23년치 기록이다. 글 작성 연도가 2003년부터 2026년까지 분포한다.

2003:1  2004:2  2006:2  2007:12  2008:9  2011:2  2012:7  2014:5  2015:5
2016:17 2017:12 2018:2  2019:5   2021:1  2022:22 2023:20 2024:26 2025:13 2026:5

→ 블로그만으로 사회생활 이후 독서 이력이 자동 복원된다. 수동 입력은 초/중/고/대 시절(블로그 이전)에 집중하면 된다. 서재를 “연도별 타임라인”으로 보여주는 뷰가 자연스럽게 나온다.

(2) 제목 규칙만으로는 책을 못 뽑는다. 168편 중 [독서노트] 류 접두어가 붙은 건 77편뿐이고, 70편은 아무 패턴이 없다.

성공하는 사람들의 7가지 습관_스티븐 코비        ← 책 (제목_저자)
주역(周易), 우주의 이치를 담은 동양 최고의 철학서   ← 책이지만 형식이 다름
106세 철학자 김형석 교수님의 강연에서            ← 책 아님 (강연 후기)
1년에 책 한권도 읽지 않는 당신에게... 교보문고가 박물관으로  ← 책 아님 (서점 이야기)
‘하얀 전쟁’ 소설가 안정효작가님 별세 소식에...      ← 책 아님 (부고)

→ 정규식이 아니라 LLM으로 처리해야 한다. 파이프라인:

  1. 글 제목 + 본문 앞부분을 LLM에 투입
  2. 이 글이 특정 책에 대한 독후감인가? 판정 → 아니면 제외
  3. 맞으면 책 제목 / 저자 추출
  4. 책 검색 API로 정식 메타데이터(표지·ISBN·출판사) 매칭
  5. 사용자 검토 화면에서 확인·수정 후 서재에 확정 등록 (자동 등록 금지 — 오탐이 반드시 생긴다)

3-2. 책장 사진 수집 — 실행 완료 ✅

집에 있는 책장 사진 14장(pictures/)을 처리해 80권을 추출했다.

파이프라인: scripts/prepare_photos.py로 긴 변을 1600px로 축소(원본 4000px·2.5MB는 그대로 넣기엔 크다. EXIF 회전 보정 필수) → 비전 모델이 책등을 읽어 제목·저자 추출 → data/extracted/photos.json에 신뢰도와 함께 기록.

정확도의 한계가 분명하다. 책등은 세로쓰기에 글씨가 작고, 일부는 거꾸로 꽂혀 있거나 다른 책에 가려진다. 그래서 각 항목에 confidence(high/medium/low)를 붙였고, 사진에만 있으면서 확신이 낮은 30권은 needsReview 플래그가 붙는다. 실제 앱에서는 이 단계에 반드시 사용자 확인 화면이 필요하고, 정확도가 중요한 책은 바코드(ISBN) 스캔이 훨씬 신뢰할 만하다. 사진 인식은 “책장을 통째로 빠르게 훑는 용도”, 바코드는 “정확히 한 권을 넣는 용도”로 역할을 나누는 게 맞다.

사진에는 EMBA 교재·문제집·성경 필사 노트·동창회 명부처럼 서재에 꽂기 애매한 것도 섞여 있어 type: textbook 등으로 구분해 두었다.

다시 읽기(2026-09-19): 같은 14장을 한 장씩 잘라 확대해 다시 읽었더니 첫 추출이 절반도 못 건졌다(80 → 151권, 새 책 74권).

3-3. Phase 1 결과

항목 수치
블로그 글 수집 168편
그중 책 독후감으로 판정 128편 (40편 제외)
블로그에서 나온 책 124권
책장 사진에서 나온 책 80권
중복 제거 후 총 장서 196권
양쪽 모두에 있는 책 8권
확인 필요 30권

블로그와 사진 양쪽에 다 있는 8권(『세계는 평평하다』 『기브앤테이크』 『꾸뻬씨의 행복 여행』 등)은 독후감도 있고 실물도 집에 있는 책이다. 서재에서 특별하게 보여줄 만한 가치가 있다.

4. 데이터 모델 (초안)

User
 └─ UserBook (상태: 읽음/읽는중/읽고싶음, 시기: 초/중/고/대/성인, 평점,
              출처: 수동입력 | 사진 | 블로그, 읽은시점)
     ├─ Book (마스터: 제목, 저자, 출판사, 표지, ISBN, 카테고리, 출간연도)
     ├─ Review (독후감 본문 — 직접작성 또는 블로그에서 수집, 원본 URL, 작성일)
     └─ Quote[] (인상 깊은 구절, 페이지)

ImportCandidate (블로그에서 뽑았지만 아직 사용자 승인 전인 후보)
Shelf/Theme (3D 공간 테마 설정, 시기별 책장 매핑)

5. 기술 스택

6. 3D 서재 UI 방향

7. 로드맵 — 블로그 수집을 1순위로

기존 계획은 블로그 연동을 후반부에 뒀지만, 검증 결과 168편이 즉시 확보되므로 순서를 바꾼다. 수동 입력부터 시작하면 서재가 텅 빈 채로 몇 주가 가지만, 블로그 수집을 먼저 하면 첫날부터 100권 이상 꽂힌 서재가 나온다. 3D 서재는 책이 어느 정도 차 있어야 감동이 생긴다.

  1. Phase 1 — 블로그·사진 수집 + 서재 시드 ✅ (196권)
  2. Phase 2 — 3D 가상 서재 + 목록 뷰 ✅ (app/, 아티팩트로 배포)
  3. Phase 3 — 분류 뷰 · 수정 · 학창 시절 입력 ✅
    • 분류: 196권을 11개 카테고리로 직접 분류(data/categories.json) → 넷플릭스식 가로 줄 UI
    • 수정: 상세 패널에서 제목·저자·분류·시기 수정. needsReview 30권을 여기서 정리한다
    • 학창 시절: 제목만 넣으면 초/중/고/대 칸에 꽂힌다. 블로그 이전 시기는 이 경로로만 채울 수 있다
    • 저장: 아티팩트 db 기능(문서 library/user). 실패하면 브라우저 저장으로 자동 대체
    • 학창 시절 시드: data/memory_books.json에 기억으로 복원한 66권(초등 13 · 중·고 11 · 대학 42)을 넣고 빌드 때 합친다. 서명까지 기억나는 책은 확정, 저자·분야만 기억나는 30건은 ‘확인 필요’로 꽂혀 앱에서 제목을 고치면 풀린다. 기존 책 id(b0…b195)가 밀리지 않도록 반드시 배열 맨 뒤에 붙인다
    • 책 id 대장 data/book_ids.json (2026-09-19): 사용자가 고친 내용은 id("b"+번호)에 저장되는데, 예전에는 번호가 곧 배열 순번이라 사진 책을 새로 넣으면(제목순 정렬) 뒤쪽 책과 기억 목록 66권의 id가 모두 밀렸다. 이제 빌드가 제목→번호 대장을 두고 새 책에만 끝 번호를 준다. 빠진 책의 번호는 다시 쓰지 않는다. 제목을 바로잡을 때는 대장의 키도 함께 바꿔야 그 책의 수정 기록이 따라간다.
  4. Phase 4 — 표지 이미지·책 소개·텍스트로 책 추가 (완료, 2026-09-19) scripts/book_info.py가 카카오 책 검색으로 책마다 data/book_info/<제목>.json(저자·출판사·ISBN·소개글 등)과 <제목>.jpg(120x174 썸네일)를 저장한다.
    • python scripts/book_info.py --all: 등록된 모든 책(books.json + 기억 목록 중 제목이 있는 책). 받은 책은 건너뛴다. 223/232권 확보.
    • python scripts/book_info.py "제목 / 저자" (또는 파일·직접 입력): 텍스트로 책을 추가한다. books.json에 sources: ["input"]로 들어가고 앱에서는 “텍스트로 직접 추가한 책”으로 보인다. 앱 id가 밀리지 않도록 빌드 때 맨 끝에 붙인다.
    • scripts/build_app.py가 표지를 data URI로, 소개글을 상세 패널의 “책 소개”로 넣는다(아티팩트 CSP가 외부 이미지를 막으므로).
    • 검색은 “제목이 우리 제목으로 시작 + 저자 일치” 판본을 우선한다. 그래도 몇 권(삼국지, VALUE 밸류, 켈로그 마케팅 바이블, 등소평 전기, 내 모습 그대로)은 다른 판본·다른 책이 잡혀 확인이 필요하다.
    • 테스트: python scripts/test_book_info.py

    책 API 선택 경위 — 중요

    • 알라딘: 쓸 수 없다. OpenAPI 신규 키 발급이 2026-09-04에 종료됐고 기존 키도 2026-10-30에 정지된다.
    • 구글 북스: 이 환경에서 IP 차단(즉시 429).
    • 카카오 책 검색: 채택. 무료·즉시 발급·하루 30만 건. KAKAO_REST_API_KEY 사용자 환경변수. 첫 검색 결과는 소개글이 빈 판본이 많아 20개를 받아 고른다.
    • 네이버 책 검색: 쓸 수 없다. 검색 API가 NAVER API HUB(네이버 클라우드)로 이관되면서 ‘쇼핑·책·전문자료’는 제외되어 2026-07-31에 종료(기존 키 포함, 대체 API 없음). API HUB에도 /search/v1/book 경로가 없다(404). 4-1. 첫 화면: 왼쪽 메뉴 + 서고 (three.js, 2D/3D, 2026-09-19)
    • 첫 화면은 늘 2D · 런던 도서관 · 소설·문학 서고. 입구(로비) 대화상자는 없앴다. 보기 방식·배경은 브라우저에 기억하지 않는다(열 때마다 같은 자리에서 시작).
    • 왼쪽 메뉴: 보는 방식(2D / 3D) · 서고(분류 · 권수) · 배경 테마 · 책갈피 색 범례. 서고를 누르면 그 서고로 이동(멀면 잠깐 어두워졌다 도착), 지금 있는 서고가 자동으로 표시된다. ☰ 버튼으로 접으면 아래쪽 서고 버튼 줄이 대신 나온다.
    • 휴대폰(폭 760px 이하): 메뉴는 ☰로 꺼내는 서랍(서고를 고르면 닫힘), 아래 서고 버튼 줄은 늘 보임. 검색은 돋보기를 누르면 바 아래로 펼침. 상세 패널은 아래에서 올라오는 시트이고, 책은 화면 위쪽에 오게 선다. 2D는 세로 화면에서 서가 한 칸 너비가 다 들어오게 물러서고 첫 칸을 가운데에 둔다. 걷기 버튼·입력칸을 손가락 크기로 키우고(입력칸 16px: iOS 확대 방지), 안전 영역(노치)을 비우고, 화소 비율을 1.5로 줄인다.
    • 한 칸 = 3단 × 5권(황동 번호판 1~15), 표지가 앞을 보게 기대 세움. 뒤 겹친 책 두께 = 독후감 분량. 책 상태는 책갈피 색. 칸 머리판 “서가 NN · 분류 · 1/4”(번호는 서고 순서대로 이어 붙임).
    • 3D: 서고마다 방 하나, 모든 방이 이어져 있다. 방을 옆으로 나란히 놓고 방 사이 벽(책장 두 개가 등을 맞댐)에 문간을 뚫었다. 문간 윗인방 양쪽에 “경영·경제 →” 같은 금박 안내판. 위층 갤러리는 문간 위로 이어진다. 첫 방에만 입구 문과 독서 책상.
      • 끌기=둘러보기, 바닥 클릭=걷기(옆 방 바닥을 누르면 문간 너머로), ↑↓/WS=걷기, ←→=돌기, AD=옆걸음.
      • 휠·두 손가락·+/−는 제자리 확대·축소(시야각 26°~76°). 걷기와 분리해서, 확대·축소하다 다른 서고로 끌려가지 않는다.
      • 벽·책상·사다리는 통과하지 않고, 방 사이는 문간으로만 다닌다. 지금 방 ±2개만 그린다(성능).
    • 2D: 모든 서고의 칸을 한 줄로 이어 붙인 긴 서가. 분류가 시작되는 자리에 세로 금박 표지판 기둥(분류명 · 권수). 끌기=밀기, ←→=쭉 넘기기, 휠·↑↓=확대·축소(서가 높이의 1.3배까지).
    • 배경 테마 5가지(왼쪽 메뉴에서 선택): 누르면 지금 자리에서 바로 바뀐다.
      1. 런던 도서관 — 마호가니, 위층 갤러리, 격자 천장, 황동 펜던트, 붉은 러너, 사다리·책상·초록 램프
      2. 애플 스토어 — 밝은 원목 선반, 하얀 벽·천장, 둥근 빛 패널, 석재 바닥, 흰 간판에 검은 글씨
      3. 라스베가스 — 검은 옻칠 서가, 금장, 핑크·시안 네온 띠, 흑백 대리석 바닥, 붉은 카펫, 샹들리에, 네온 글씨
      4. 해변가(야외) — 흰 나무 서가, 모래, 하늘·해, 바다, 야자수. 천장 없고 입구 쪽이 트여 있음
      5. 산속(야외) — 통나무 서가, 판석 바닥, 체크무늬 러그, 소나무 숲, 설산, 사다리·책상 테마 = 결·바닥·천장·카펫 무늬(캔버스로 다시 그림) + 재질 색 + 조명 + 안개 + 장식 책 색 + 글자판 스타일 + 구조 플래그. 야외 풍경(하늘 돔·바다/초원·나무·산)은 따로 그리고, 하늘은 카메라를 따라다닌다.
    • 표지 클릭 → 책 앞으로(3D 14, 2D 20 거리), 넓은 화면에선 오른쪽 상세 패널에, 휴대폰에선 아래 시트에 안 가리게 비켜 선다.
  5. Phase 5 — 바코드(ISBN) 스캔 등록
  6. Phase 6 — 추천 엔진 + 베스트셀러 연동
  7. Phase 7 — 3D 테마 확장, 구절 하이라이트, 공유

8. 남은 확인 사항