← 전체로 돌아가기
프로젝트 메모 docker -home-son-prj-notion

dev-portfolio-deploy & editor mode

cron이 index.html 덮어쓰니까 커스텀 파일 따로 관리할 것.

dockerpythondeploymentnotion-clone

dev.ericfromkorea.com 배포 구조

dev-portfolio-fe 도커 컨테이너로 서빙함. (python server → host 60022 → nginx) Source: /home/son/prj/dev_portfolio/fe/

⚠️ Gotcha: 매일 11:00에 cron 돌면서 generate-hub.py 실행됨. 이때 fe/index.html이랑 fe/hub.html 덮어써버림(clobber). 커스텀 내용은 무조건 다른 파일명(예: portfolio.html)으로 만들어야 살아남음.

Docker run 옵션 (generate-hub.py 내부): --user 1000:1000 -v [FE_DIR]:/app --env-file [SECRET_FILE_PATH] - -v : 호스트랑 볼륨 마운트해서 에디터로 수정하면 바로 반영되게 함.

Editor mode (Notion-style)

  • login: .editor-secret에 있는 비번으로 POST /api/login 해서 24h 토큰 받음.
  • block editor: js/editor/blockedit/ 에 구현됨. contenteditable 기반.
  • / 슬래시 메뉴, 마크다운 단축키, 인라인 툴바 지원.
  • 데이터는 JSON으로 관리 (js/data/profile.json 등).
  • 이미지 업로드는 assets/img/uploads/ 로.
  • server code 수정 시: docker restart dev-portfolio-fe JS/CSS는 마운트되어 있어서 바로 반영되는데, Python 서버 코드는 프로세스 재시작해야 함.

PermissionError 관련: gen_learn_hub() 실행 시 /var/www/learn.ericfromkorea.com/index.html 권한 문제로 터지는데, 포트폴리오 빌드 자체는 이미 성공함. 그냥 무시해도 됨.

여기서 배울 것

  1. cron으로 파일 덮어쓰기 설정할 때 주의할 것
  2. 도커에서 python 코드 수정하면 컨테이너 재시작 필수
  3. Notion 스타일 에디터는 vanilla JS로 구현 가능
원본 파일 보기 (.claude/projects/-home-son-prj-notion/memory/dev-portfolio-deploy.md)
---
name: dev-portfolio-deploy
description: How dev.ericfromkorea.com is deployed and the index.html auto-clobber gotcha
metadata: 
  node_type: memory
  type: project
  originSessionId: 066633f9-0c35-4e25-a5a5-084b3eb783e5
---

`dev.ericfromkorea.com` is served by the `dev-portfolio-fe` Docker container (`python -m http.server 8000` → host 60022 → nginx). Source: `/home/son/prj/dev_portfolio/fe/`.

**Gotcha:** a daily 11:00 cron runs `/home/son/prj/dev_portfolio/generate-hub.py`, which **overwrites `fe/index.html` and `fe/hub.html`** with the auto-generated link hub and rebuilds the container. So custom content must live in *other* filenames (e.g. `portfolio.html`, `projects.html`) + `css/`, `js/`, `assets/` — those survive the cron.

The Dockerfile does `COPY . .` and `CMD ["python","server/server.py"]` (a custom static+API server, not `http.server`). `rebuild_docker()` in generate-hub.py runs the container with `--user 1000:1000 -v {FE_DIR}:/app --env-file /home/son/prj/dev_portfolio/.editor-secret` so editor writes persist to the host source and survive rebuilds. To deploy: edit files in `fe/`, then run `python3 generate-hub.py`. The hub card link for a subdomain comes from `# hub-url:` comment in its nginx conf at `/etc/nginx/sites-available/<sub>`.

**Editor mode** (`server/server.py` + `js/editor/*`): `/login.html` → `POST /api/login` with the password in `.editor-secret` (`EDITOR_PASSWORD`; `EDITOR_SECRET` signs the HMAC token) → 24h bearer token in localStorage. Authed users get a floating edit bar on portfolio/projects/project pages. Profile + DB rows use form editors; **project detail uses a Notion-style block editor** (`js/editor/blockedit/`): contenteditable blocks, a `/` slash menu (createSlash), markdown shortcuts (`#`,`-`,`1.`,`>`,```` ``` ````,`---` via input.js), inline toolbar (B/I/S/code/link), block handle menu (delete/dupe/move), image upload. Serialization between Notion rich-text spans and editable HTML is in `blockedit/model.js`; complex blocks (columns/tables) are preserved read-only. Test tooling: `extract/cdp_*.js` drive headless Chrome via CDP to verify slash/markdown. Backend PUTs overwrite `js/data/profile.json` / `projects.json` / `details/<slug>.json`; uploads go to `assets/img/uploads/`. `POST /api/page` (store.create_page) atomically creates a new project page (appends a row + writes an empty `details/<slug>.json`, returns it) — exposed as a "+ 새 페이지" button on projects.html and the `/page` slash command in the block editor (inserts a link to the new page). **Editing the Python server code (`server/*.py`) requires `docker restart dev-portfolio-fe`** to reload (static JS/CSS update live via the mount, but the running process holds the Python code). Data is now JSON (not JS modules), loaded async via `js/data/store.js`. The editor password is in `/home/son/prj/dev_portfolio/.editor-secret` (mode 600, not in git/image); change it there and re-run generate-hub.py (or `docker restart`).

`gen_learn_hub()` at the end throws PermissionError on `/var/www/learn.ericfromkorea.com/index.html` (root-owned) — pre-existing, unrelated to the dev portfolio; the hub+container rebuild already succeeded before it.

The custom Notion reproduction is vanilla HTML/CSS/JS, fully modular (ES modules under `js/`, tokens/components under `css/`, data in `js/data/`). Pages: 프로필 = `portfolio.html`, All Projects DB = `projects.html`, per-project detail = `project.html?id=<slug>` (renders `js/data/details/<slug>.json` via `js/notion/`). Design is Apple-style (frosted-glass sticky nav, soft shadows, large radii, SF/Pretendard). Project images live under `assets/img/projects/` (~72MB; baked into the image by `COPY . .`). See [[notion-source-ids]].