Code
Search code…
모바일 갤러리 상세에서 위쪽 빈 여백을 없애고 제목을 표지 옆으로 옮겨 표지를 키우며, 표지를 누르면 화면 전체로 확대한다. 확대 버튼의 키보드 포커스와 모바일 경고·정보 줄이 닫기 버튼에 가리지 않게 한다. web 제품 기준 PRODUCT.md와 문서 정리를 함께 담는다.
검증: a8e006be(main 839c76a1과 같은 tree)에서 run-unit -Workspace web(lint·타입 검사·172 suites·1500건) 통과, 320·390·1280px 화면 캡처 확인, main에서 web 이미지 빌드와 develop compose config 통과. NOT RUN: backend·crawler 단위·통합(해당 코드 변경 없음), 개발 stack 기동 확인, 실제 기기 터치 확인.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2Ex-Hentai
Hitomi·공개 E-Hentai 한국어 작품을 개인 서버에 수집하고 검색·열람하는 갤러리 시스템
![]() |
![]() |
2Ex-Hentai는 개인 서버에서 운영하는 갤러리 수집·열람 시스템입니다. Hitomi와 공개 E-Hentai의 한국어 작품을 원본 그대로 서버 저장소에 수집하고, 웹과 모바일(PWA)에서 검색하고 이어 읽을 수 있게 합니다. 서버와 저장 공간을 직접 관리하는 개인 운영자를 위한 것이며, Windows Docker Desktop에서 개발하고 NAS 같은 Linux 서버에서 Docker Compose로 운영합니다.
성인 콘텐츠: 성인용 작품을 다룹니다. 처음 방문하면 만 19세 이상인지 묻고 그 답을 브라우저에만 저장하는 자기 확인이며, 본인 인증이 아닙니다. 접근 범위와 이용 책임은 서버 운영자에게 있습니다.
사용 예 / 기능 / 구성 / 빠른 시작 / 더 알아보기
사용 예
현재 운영 환경은 2코어 NAS 한 대이며 갤러리 수만 개와 원본 수백만 장을 보관합니다(운영 환경 기준선).
- 매일 19:30(Asia/Seoul)에 Hitomi, 21:30에 공개 E-Hentai의 새 한국어 작품을 증분 수집하고, 북마크한 작가·그룹의 새 작품은 Web Push 알림으로 받습니다.
- 작품 캡처 한 장이 있으면 홈 검색창의 이미지 검색에 파일을 올리거나 붙여넣어 비슷한 페이지가 있는 작품을 찾습니다.
- 휴대폰 웹툰 모드로 읽던 작품을 PC의 두 페이지 보기에서 같은 페이지부터 이어 읽습니다.
- Hitomi 수집이 원본 사이트의 요청 제한(429)으로 멈추면 예약된 자동 재개를 기다리고, 같은 작품이 두 번 들어오면 중복 후보를 나란히 비교해 남길 작품을 고릅니다.
기능
- 수집: Hitomi를
최신 수집·증분 동기화·전체 크롤링·명시 범위로 수집합니다. Hitomi에서 빠진 작품은 공개 E-Hentai의 매일 예약 증분과 관리자 수동 수집으로 보완합니다. - 검색: 제목, 포함·제외·하나라도 일치 태그(한국어 번역·자동완성), 쪽수·게시일 범위로 찾고 조건을 저장합니다. 로그인하면 이미지 유사도 검색도 씁니다.
- 열람: 한 페이지·두 페이지·웹툰 뷰어의 읽던 위치를 자동 저장하고, 대시보드에서 읽은 양과 선호 태그를 봅니다.
- 개인화: 갤러리·작가·그룹 북마크와 사용자별 태그 차단을 씁니다. 북마크한 작가·그룹이나 저장한 검색에 새 작품이 들어오면 Web Push로 알리고, 로그인 전 브라우저 기록은 계정에 합칩니다.
- 계정: passkey로만 로그인하며, 가입을 허용하면(
USER_REGISTRATION_ENABLED) 사용자마다 기록과 설정을 따로 가집니다. - 운영 관리: 수집 진행·실시간 로그·저장 통계·중복 후보·전역 블랙리스트·이미지 색인·썸네일·GC를 운영 관리 화면에서 다룹니다. 관리자 확인이 필요한 오류로 수집이 멈추거나 crawler가 응답하지 않으면 관리자에게 Push로 알리고(관리자 운영 알림), 새 릴리스가 나오면 화면에 안내합니다.
설계
- 수집 재개: 재개 위치는 DB의 checkpoint와 ID coverage에 기록합니다. 실패한 갤러리는 건너뛰지 않고 그 자리에서 멈춘 뒤 이어 갑니다(크롤러 복구).
- 원본 사이트 요청: 요청 간격·동시성 상한과
Retry-After를 지킵니다. 404가 몰리면 삭제로 판정하기 전에 알려진 정상 갤러리로 사이트 변경 여부를 확인합니다(업스트림 이상 회로). - 저장: 원본은 재인코딩하지 않고 보관하며, 목록 카드는 미리 만든 폭 480px WebP 축소본을 씁니다(카드 썸네일 축소본).
- 이미지 검색·중복 검사: 별도 벡터 DB 없이 PostgreSQL의 pHash 색인으로 비슷한 페이지와 중복 후보를 찾습니다. 중복은 자동 삭제하지 않고 관리자가 고릅니다(이미지 검색, 중복 검사).
- 보안: passkey와 HttpOnly cookie, Cloudflare Turnstile, 짧은 viewer grant로 계정과 원본 접근을 보호합니다(Media와 viewer 접근).
구성
| 서비스 | 하는 일 |
|---|---|
| Nginx | 웹 화면과 /api/*를 받는 단일 진입점 (localhost:30000) |
| web | 탐색, 뷰어, 대시보드, 개인화와 운영 화면 (Next.js) |
| backend | 공개 API, passkey 인증, 사용자 데이터, 이미지 검색, 관리자 명령 전달 (NestJS) |
| crawler | 수집·원본 저장, checkpoint 재개, 썸네일·이미지 색인, 중복 검사, GC (NestJS) |
| PostgreSQL·Redis·Silo | 메타데이터·원장·작업 상태, 세션·cache, 원본과 축소본 객체(MinIO 호환 PGSTY Silo) |
| log-reader·byedpi | 운영 화면용 컨테이너 로그 읽기, crawler 전용 SOCKS 우회 경로 |
브라우저는 Nginx의 /api/*만 호출하고, 수집 명령은 backend가 내부 crawler API로 전달합니다. 서비스 경계와 데이터 흐름 그림은 아키텍처, 화면 동작은 Web 화면, 요청·응답과 권한은 API 계약에 있습니다.
빠른 시작
Windows Docker Desktop(Linux 컨테이너)과 Docker Compose v2, PowerShell, Git이 필요하며, 실행·빌드·테스트는 모두 Compose 안에서 합니다.
git clone https://git.2ex.dev/hanyul/2Ex-Hentai.git; cd 2Ex-Hentai
if (-not (Test-Path .env.develop)) { Copy-Item .env.example .env.develop } # 다음 줄 전에 아래 안내대로 값을 채운다
docker compose --env-file .env.develop -f compose.yml -f compose.build.yml up -d --build --wait
Start-Process http://localhost:30000
첫 up 전에 .env.develop의 CHANGE_ME_* 비밀 값을 개발 시작의 안내대로 채워야 backend가 시작합니다. Turnstile·Web Push 키는 비워 두면 그 기능만 꺼집니다.
설치 후 첫 사용
- 홈에서 연령 확인을 마칩니다. 새 설치에는 아직 갤러리가 없습니다.
- 로그인 창의 새 패스키 등록에서 아이디 칸에
ADMIN_SECURITY_KEY값을 넣으면ADMIN_USERNAME관리자 passkey가 등록됩니다. 등록 뒤 키 정리는 관리자 보안 키, 도메인·HTTPS 조건은 인증과 passkey를 봅니다. - 운영 관리에서 Hitomi나 E-Hentai 수집을 시작합니다. 처음에는
최신 수집으로 몇 건만 받아 보세요. - 이미지로 찾으려면 운영 관리 → 이미지 색인에서 색인을 만든 뒤, 로그인한 상태로 홈 검색창의 이미지 검색을 씁니다.
운영 서버에 배포
운영 서버는 소스를 빌드하지 않고 compose.yml로 레지스트리의 latest 이미지를 받습니다. 외부에서 접속하려면 HTTPS 도메인(passkey 조건)이 필요하고, 로봇 검증을 쓰려면 실제 Turnstile 위젯 키를 넣습니다. 릴리스 파일과 .env.production을 운영 반영대로 준비한 운영 디렉터리에서 실행합니다.
docker login git.2ex.dev
sh ops/deploy-production.sh # 끝에 서비스 상태를 출력한다
업데이트·이미지 게시는 레지스트리 게시와 배포를 따릅니다.
더 알아보기
| 알고 싶은 것 | 문서 |
|---|---|
| 전체 문서 목록과 남은 작업 | 문서 색인 |
| 운영, 수집·색인·GC, 배포 | 운영 가이드 |
| 외부 공개용 Cloudflare 설정 | Cloudflare 설정 |
| 수집 재개·장애 복구 | 크롤러 복구, E-Hentai 크롤링 |
| 서비스 구조와 API | 아키텍처, API 계약, Web 화면 |
| 계정·개인화와 중복 관리 | 사용자 계정·개인화, 중복 검사 |
| 개발 규칙과 검증 | 작업 가이드, 테스트 안내 |
기여와 라이선스
문제 제보와 변경 제출은 기여 안내를 참고하세요. 개인 사용 목적의 저장소이며, 공개 조회 여부와 별개로 별도 라이선스 파일은 제공하지 않습니다.

