Personal Archive & Gutenberg Experience
PocketRisu·RisuBard 의 채팅을 웹소설처럼, 전자책처럼 읽는 읽기 전용 서고.
A read-only reader & archive that turns your PocketRisu or RisuBard chats into something you can sit down and read. (Korean UI)
Warning
PAGE 에는 로그인이 없어요. 서고를 열 수 있는 사람은 누구나 채팅을 전부 읽을 수 있어요. 내 컴퓨터, 또는 Tailscale·집 LAN 처럼 나만 들어오는 네트워크 안에서만 여세요. 인터넷에 그대로 열지 마세요. save 폴더는 읽기 전용으로만 붙어요(PAGE 는 save 에 쓰지 않아요).
PAGE 는 PocketRisu 나 RisuBard 의 save 를 읽기 전용으로 열어, 쌓아 둔 채팅을 한 권의 책처럼 보여주는 작은 웹앱이에요. 대화하던 화면에서는 잘 안 보이던 이야기를, 스크롤로 내려 읽거나 책장을 넘기듯 읽을 수 있어요.
동시에 개인 서고이기도 해요. 원본 save 가 수정되거나 지워져도, PAGE 는 읽어 둔 사본과 이전 버전을 따로 보관해요.
![]() |
![]() |
| 책처럼 넘기기 · 두 쪽 펼침 | 폰 · 다크 테마 |
![]() |
![]() |
| 내가 만든 테마로 읽기 | 폰 · 서고 |
- 서고 — 이어 읽기 책상, 캐릭터별 서가와 책등(분량만큼 높이, 읽는 중이면 띠), 캐릭터 대표 이미지, 보관함.
- 두 가지 읽기 방식 — 웹소설처럼 세로 스크롤, 또는 책처럼 쪽 넘기기(넓은 화면은 두 쪽 펼침, 키보드·탭·스와이프).
- 읽던 자리 기억 — 위치를 쪽 번호가 아니라 메시지·문단·글자 단위로 저장해요. 글꼴·크기·모드를 바꿔도, 다른 기기에서 열어도 같은 자리로.
- 책갈피 · 형광펜 — 문장을 골라 칠하고, 원하는 곳으로 바로 이동. 칠한 문장은 채팅별로, 또는 서고 전체에서 모아 볼 수 있어요.
- 타이포그래피와 테마 — 명조(MaruBuri)·고딕, 크기·줄 간격·문단 간격·폭·양쪽 정렬, 밝게·어둡게·세피아, 말풍선 보기. CSS 로 직접 만드는 테마(파일로 주고받기)와 글꼴 올리기(woff2·ttf·otf — 아이폰에서도 원하는 글꼴로).
- 나만의 서고로 — 캐릭터·채팅 이름을 PAGE 안에서만 바꾸기, 상태창 같은 조각을 정규식으로 숨기기(캐릭터 카드의 표시용 정규식을 후보로 제안), 내 메시지 숨기고 이야기만 읽기, 카드 정규식의 본문 그림 표시(표정·삽화 — 조건부 그림과 상태창 꾸밈 제외).
- 보존 — 텍스트와 채팅이 참조하는 이미지를 중복 없이 복사. 원본이 바뀌면 버전으로, 지워지면 "원본에서 지워짐" 으로 남겨요 — RisuAI 에서 지운 채팅도 그대로 읽을 수 있어요. 새 채팅은 알아서 따라오고, 바로 반영하고 싶으면 서고의 지금 동기화.
- 가져오기 — RisuAI 에서 내보낸 채팅 JSON(채팅 내보내기 파일)을 올려 "가져온 채팅" 서고로. PocketRisu 가 없어도 쓸 수 있고, 같은 채팅을 다시 올리면 새 버전으로 쌓이며, 지울 때는 서고 사본·책갈피까지 완전히 지워요.
- 내보내기 — EPUB(애플 북스·킨들 등, 그림·정규식 포함 여부와 그림 크기 선택), PDF(A5, 쪽 번호·책갈피), RisuAI 로 다시 가져오는 JSON.
PAGE 는 남의 집에 들어가 책만 읽고 나오는 손님처럼 동작해요.
- save 에 절대 쓰지 않아요. SQLite 를
mode=ro로 열고, 컨테이너에는:ro로만 붙여요. - PocketRisu·RisuBard 에 로그인하지 않아요. HTTP API 를 쓰지 않고 파일만 읽어요(쓰던 세션을 밀어내지 않게).
- 로그에 내용을 남기지 않아요. 요청 본문·채팅 내용·이름은 기록하지 않아요.
- 신뢰하지 않는 원문은 HTML 로 넣지 않아요. 메시지는 비활성 문서에서 파싱해 허용된 블록만 그리고, 엄격한 CSP(
default-src 'self')로 제공해요. - 로그인이 없어요. 접근 보호는 네트워크에 맡겨요 — 위 경고를 꼭 지켜 주세요.
필요한 것: Docker(Compose), 그리고 PocketRisu 의 save 폴더(risuai.db 가 있는 곳)나 RisuBard 의 save 폴더(characters·kv 가 있는 곳). 이미지는 amd64·arm64(애플 실리콘 Mac·라즈베리파이(64비트 OS) 등) 둘 다 있어요.
Windows 에서 Docker 없이 쓰려면 아래 Windows (Docker 없이) 를 보세요.
Windows · Mac 에서도 Docker Desktop 으로 돌아갈 거예요(직접 확인은 Linux 에서 했어요). 경로는
.env에 그 컴퓨터의 경로로 적으면 돼요(예: WindowsC:/Users/me/pocketrisu/save). 아래sudo chown줄은 Linux 에서만 필요해요.
git clone https://github.com/Sallos725/PAGE.git && cd PAGE
cp .env.example .env # PAGE_SAVE_DIR 를 내 save 경로로
mkdir -p ./page-data && sudo chown 1000:1000 ./page-data # 데이터 폴더는 uid 1000 이 쓸 수 있어야 해요
docker compose up -dPocketRisu·RisuBard 없이 RisuAI 채팅 JSON 만으로도 쓸 수 있어요(가져오기). 이때는 docker-compose.yml 에서 PAGE_SOURCES 줄과 save 폴더 마운트 줄을 지우고 올리세요. 열면 서고 머리의 가져오기 에서 JSON 을 올려요.
save 폴더는 uid 1000 이 읽을 수 있어야 해요(Linux: 필요하면 chmod -R a+rX <save 폴더>).
그다음 http://127.0.0.1:6008 을 열면 돼요. 처음 열면 save 를 읽어 서고를 채워요(백그라운드).
PDF 내보내기는 같이 올라오는 page-pdf 컨테이너(헤드리스 Chromium, 호스트 포트·외부 통신 없음)가 만들어요. 이미지가 약 900MB 예요. PDF 가 필요 없으면 docker-compose.yml 에서 page-pdf 서비스, page 의 depends_on·pdf 네트워크·PAGE_PDF_URL, 맨 아래 networks.pdf 를 지우면 돼요.
| 변수 | 설명 |
|---|---|
PAGE_SAVE_DIR |
읽을 save 폴더(PocketRisu 또는 RisuBard, 알아서 구분해요). 컨테이너에는 읽기 전용으로 붙어요. |
PAGE_USER |
선택. 컨테이너 실행 사용자(기본 1000:1000). 아래 "RisuBard" 참고. |
PAGE_DATA_HOST_DIR |
PAGE 데이터(서고 사본·읽기 상태·테마·글꼴, 기본 ./page-data). uid 1000 이 쓸 수 있어야 해요 (PAGE_USER 를 바꾸면 그 사용자 소유로). |
PAGE_HOST_PORT |
호스트 포트 (기본 6008). |
PAGE_HOST_BIND |
여는 주소 (기본 127.0.0.1 — 이 컴퓨터에서만). |
PAGE_VERSION |
이미지 버전 (기본 1.2.0). |
TZ |
시간대 (기본 Asia/Seoul). |
컨테이너 안 서버 설정(동기화 주기, 용량 경고 등)은 PAGE_* 환경 변수로 바꿀 수 있어요 — server/src/config.ts.
.env 의 PAGE_SAVE_DIR 를 RisuBard 의 save 폴더로 바꾸면 돼요. PocketRisu 에서 RisuBard 로 이사했다면 원본 이름(PAGE_SOURCES 의 main)은 그대로 두세요 — RisuBard 가 PocketRisu 의 채팅 id 를 그대로 가져가서, 같은 채팅이 책갈피·형광펜까지 서고에서 그대로 이어져요.
Linux 에서 RisuBard 를 root 로 돌리고 있다면 save 파일이 root 만 읽을 수 있게(0600) 저장돼요. 이때는 .env 에 PAGE_USER=0:0 을 넣고, 데이터 폴더 소유자도 바꿔 주세요(PAGE 는 특권(capability)을 모두 버린 root 로 돌아서, 다른 사용자 소유 폴더에는 못 써요).
sudo chown -R 0:0 <PAGE_DATA_HOST_DIR>
docker compose up -d
chmod 로 권한을 열어도 RisuBard 는 저장할 때마다 파일을 0600 으로 새로 써서 금방 다시 막혀요. 그래서 PAGE_USER 를 써요.
되돌릴 때는 PAGE_USER 줄을 지우고 sudo chown -R 1000:1000 <PAGE_DATA_HOST_DIR> 한 뒤 다시 올리세요.
Windows·Mac 의 Docker Desktop 에서는 필요 없어요.
PAGE_HOST_BIND 에 내 네트워크 안의 주소를 넣어요 — 예를 들어 이 컴퓨터의 Tailscale 주소(100.x.y.z)나 집 LAN 주소(192.168.x.y). 0.0.0.0(모든 주소)은 쓰지 마세요. 공유기 포트 포워딩도 하지 마세요.
HTTPS 가 필요하면 리버스 프록시(예: Nginx Proxy Manager, Caddy)를 앞에 두되, 그 도메인도 사설망(Tailscale 등) 안에서만 열리게 하세요.
리버스 프록시 도메인을 page 서비스의 environment 에 PAGE_ALLOWED_HOSTS: "page.example.ts.net" 처럼 적어 두면 DNS rebinding 을 막아요. 적으면 목록에 있는 주소(호스트 또는 호스트:포트, 쉼표로 구분)로 연 요청만 받아요 — 직접 여는 주소(예: 100.x.y.z)도 같이 적어 두세요. 루프백(127.0.0.1·localhost)은 늘 허용돼요.
git pull # 새 docker-compose.yml·.env.example 받기
# .env 의 PAGE_VERSION 을 새 버전으로 바꾼 뒤
docker compose pull && docker compose up -d백업은 PAGE_DATA_HOST_DIR 폴더만 챙기면 돼요. 채팅 사본이 들어 있으니 백업도 암호화해서 보관하는 걸 권해요.
Docker 없이 압축만 풀어 쓰는 Windows x64(64비트) 포터블판이에요.
- Releases 에서
PAGE-<버전>-win-x64.zip을 받아요(옆의.sha256으로 확인할 수 있어요). - zip 을 원하는 폴더(예:
C:\PAGE)에 풀어요. zip 안에 바로PAGE.exe·bin·app이 들어 있어서 그 폴더에 그대로 놓여요. 경로가 너무 길거나 OneDrive 처럼 동기화되는 폴더는 피하는 게 좋아요. PAGE.exe를 더블클릭하면 검은 창(서버)이 뜨고 브라우저가http://127.0.0.1:8787을 열어요. 창을 닫으면 꺼져요.- 처음 실행할 때 "Windows의 PC 보호" 창이 뜨면 추가 정보 → 실행 을 눌러요(코드 서명을 하지 않아서 뜨는 안내예요).
- 쓰는 법은 두 가지예요.
- 가져오기 — RisuAI 에서 내보낸 채팅 JSON 을 서고 머리의 가져오기로 올려요.
- 원본 연결 — 이 PC 에서 PocketRisu 나 RisuBard 를 쓰고 있다면 서고 머리의 원본 연결 에서 그 폴더(안에
save폴더가 있는 곳)를 고르세요. 읽기 전용으로만 열어요.
- 업데이트 — PAGE 를 끄고, 새 zip 의 압축을 풀 때 대상 폴더를 지금 PAGE 폴더(
PAGE.exe가 있는 곳, 예:C:\PAGE)로 골라 덮어써요. Windows 가 처음 제안하는PAGE-…-win-x64새 폴더가 아니에요. 데이터(data폴더)는 zip 에 없어서 그대로 남아요.
- 데이터는
PAGE.exe옆data폴더에 있어요. 백업은 그 폴더를 복사하면 돼요. - 포트를 바꾸려면
PAGE.exe옆에page.env를 만들고PAGE_PORT=8788처럼 적어요. - 이 컴퓨터(127.0.0.1)에서만 열려요. 폰 등 다른 기기에서 열려면 Docker 판을 쓰세요.
- PDF 내보내기는 Docker 판에서만 돼요. EPUB·JSON 은 포터블에서도 돼요.
# 이미지 대신 직접 빌드
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build
# 서버 (Node 24, node:sqlite, Fastify)
cd server && npm ci && npm test && npm run build
# 웹 (Svelte 5, Vite)
cd web && npm ci && npm test && npm run check && npm run build
# E2E (Playwright, 합성 save 로 실제 서버를 띄운다)
cd web && npm run e2e # chromium
scripts/e2e-webkit-docker.sh # webkit — 호스트 라이브러리 없이 Playwright 이미지에서
# 이미지 스모크 (합성 save)
scripts/deploy-smoke.sh
# 스크린샷 다시 찍기 (직접 지은 데모 이야기)
node scripts/demo/screenshots.mjs테스트 픽스처와 데모는 모두 합성 데이터예요. 실제 채팅은 저장소 어디에도 들어가지 않아요.
이슈로 남겨 주세요. 개인 프로젝트라 답이 느릴 수 있어요.
- save 해독 코드는 PocketRisu (GPL-3.0, RisuAI 기반)에서 가져왔어요 —
NOTICE.md. - 본문 글꼴 마루 부리 © NAVER Corp., SIL Open Font License 1.1.
- PAGE 는 PocketRisu·RisuBard·RisuAI 와 공식적인 관계가 없는 개인 프로젝트예요.





