docs(rules): 서식 본문 정화·정화 전 비용 검사·숫자 env 표기 규칙

- security: 사용자가 쓴 HTML 은 서버가 허용 목록으로 다시 정화한다, 밖에서 온 HTML 은 정화 전에 비용부터 잰다,
  브라우저에서 외부 HTML 을 고칠 때는 떨어진 문서 안에서 끝낸다
- env-structure: 숫자 env 는 앞자리 0 없는 10진수, nginx 도 읽는 크기 값은 6자리 이하
- nestjs·vue3·docker 스킬: 골격 표(정화·사전 검사·편집기·값 규칙 짝 파일)와 체크리스트, 발송 경로 4MB,
  컨테이너의 npm install 이 lock 을 깨뜨린 사례(2026-10-27)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 07:49:41 +09:00
parent d807cb4441
commit 13888af2c2
5 changed files with 35 additions and 4 deletions
+8 -1
View File
@@ -68,7 +68,9 @@ sudo docker compose --env-file .env.production logs -f backend
- `frontend/nginx.conf` 는 **템플릿**이다. 컨테이너가 뜰 때 이미지 entrypoint 가 `frontend/nginx-env.envsh` 로 값을 만들고
`${NGINX_*}` 만 envsubst 로 채운다 (nginx 의 `$uri` 등은 그대로). 값은 루트 env 에서 온다:
- 본문 한도: `/api/mail-attachments` = `MAIL_ATTACHMENT_MAX_SIZE_MB` + 1MB(기본 21m), `/api/files` = `UPLOAD_MAX_SIZE_MB` + 1MB(기본 6m),
그 밖은 1m(백엔드 JSON 파서 한도). 루트 env 한 곳만 고치면 백엔드와 nginx 가 함께 바뀐다 (frontend 컨테이너만 다시 만들면 된다).
**메일 발송 `POST /api/messages` = 4m 고정**(서식 본문·긴 인용 — 계약 2절, 백엔드도 이 경로만 4MB 파서. 정확히 이 경로만이라 `/api/messages/{id}` 는 1m),
그 밖은 1m(백엔드 JSON 파서 한도). 앞의 두 값은 루트 env 한 곳만 고치면 백엔드와 nginx 가 함께 바뀐다 (frontend 컨테이너만 다시 만들면 된다).
JSON 한도(1m·4m)는 계약과 백엔드 코드의 고정값이라 env 로 바꾸지 않는다 — 바꾸려면 계약·백엔드 파서·`frontend/nginx.conf` 를 함께 고친다.
- 시간 제한: `/api/messages` 90s, `/api/mail-attachments` 150s (`proxy_read_timeout`).
- 캐시: `index.html`(직접 요청·`/`·SPA 폴백 모두)은 `Cache-Control: no-cache` — 배포 뒤 옛 index.html 이 지워진 해시 자산을 부르지 않게.
`/assets/`(Vite 산출물 — 파일명에 내용 해시)만 `max-age=30일, public, immutable` 이다.
@@ -322,6 +324,11 @@ node --env-file=.env.development mail/scripts/setup.mjs
`npm ci` 가 `Invalid: lock file's @emnapi/wasi-threads@1.2.1 does not satisfy @emnapi/wasi-threads@1.2.3` 로 실패했다(되돌림).
lock 은 의존성을 실제로 바꿀 때만 컨테이너 안 `npm install <패키지>` 로 고치고, 고친 뒤에는 **임시 폴더에 복사해 `npm ci`** 로 확인한다
(저장소 폴더를 그대로 마운트하면 호스트의 Windows `node_modules` 를 읽어 엉뚱하게 실패한다).
- ⚠️ **2026-10-27 에는 컨테이너의 `npm install <패키지>` 도 같은 항목을 지웠다** — backend 에 `htmlparser2@^12.0.0` 을 직접 의존성으로 더하자
`@emnapi/core`·`@emnapi/runtime` 과 중첩 `chokidar` 가 빠져 `npm ci` 가 위와 같은 오류로 실패했다.
그 패키지는 이미 lock 에 같은 판으로 있었으므로(sanitize-html 의 의존성) npm 이 루트 `dependencies` 에 더한 한 줄만 원래 lock 에 옮기고,
임시 복사본에서 `npm ci`·`npm ci --omit=dev` 와 `require` 로 확인했다 — **lock 에 없던 패키지에는 쓸 수 없는 방법**이다.
새 패키지를 들이기 전에 npm 11.6.2 의 `npm install` 뒤에도 `npm ci` 가 통과하도록 lock 을 바로잡아야 한다 (release-ops 몫, 아직 원인 미확인).
- **dev backend 의 `/app/dist` 는 익명 볼륨이다.** 호스트에서 `npm run build` 를 하면 바인드 마운트된 `backend/dist` 가 지워졌다
다시 생기면서 컨테이너의 Nest watch 가 끊겨 앱이 옛 코드로 계속 돈다 — 그래서 산출물을 호스트와 나눴다.
`nest-cli.json` 의 `deleteOutDir` 는 마운트 지점을 지우려다 EBUSY 로 죽으므로, dev `command` 는 기동 때 볼륨 내용만 비우고