From bb5f663e48738b18b9366d803fb1b7cb6c7cfc0e Mon Sep 17 00:00:00 2001 From: zenn Date: Fri, 19 Jun 2026 11:45:40 +0900 Subject: [PATCH] chore: simplify NAS to production-only deployment --- .env.example | 3 +- AGENTS.md | 11 +- README.md | 1 + compose.test.nas.yaml | 7 - compose.test.yaml | 11 - docs/deploy.md | 467 ++++++++++++++++-------------------------- docs/history.md | 11 +- docs/migrations.md | 67 ++++++ docs/spec.md | 17 +- docs/update.md | 4 + 10 files changed, 278 insertions(+), 321 deletions(-) delete mode 100644 compose.test.nas.yaml delete mode 100644 compose.test.yaml create mode 100644 docs/migrations.md diff --git a/.env.example b/.env.example index 3280a57..7b10e9a 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,3 @@ -PLAYUNIARE_TEST_HTTP_PORT=41808 PLAYUNIARE_PRODUCTION_HTTP_PORT=41809 VITE_CARD_ASSET_BASE_URL=/card-assets -VITE_SOCKET_SERVER_URL=http://localhost:31873 +VITE_SOCKET_SERVER_URL=http://playuniare.sori.studio diff --git a/AGENTS.md b/AGENTS.md index 603333a..af8e588 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -157,9 +157,13 @@ function createGameCardInstances(importedCards) { - 운영 환경에서는 Socket.IO 포트를 브라우저에 직접 노출하지 않고 같은 도메인의 `/socket.io` 경로로 리버스 프록시한다. - 컨테이너 내부 포트 중복은 허용되지만 NAS 호스트 공개 포트 중복은 금지한다. -- 테스트·운영 HTTP 호스트 포트는 Compose에 숫자를 직접 추가하지 않고 - `PLAYUNIARE_TEST_HTTP_PORT`, `PLAYUNIARE_PRODUCTION_HTTP_PORT` 환경변수로 - 관리한다. +- 운영 HTTP 호스트 포트는 Compose에 숫자를 직접 추가하지 않고 + `PLAYUNIARE_PRODUCTION_HTTP_PORT` 환경변수로 관리한다. +- 기능 검증은 로컬에서 수행하고 NAS에는 운영 컨테이너만 배포한다. +- 운영 데이터가 생기면 마이그레이션 코드는 Git으로 관리하고, 백업과 재실행 + 안전성을 확인한 뒤 실행한다. +- NAS에서 즉석 데이터 변경 명령을 사용한 경우 같은 변경을 정식 + 마이그레이션 코드와 문서로 남긴다. - URL 길이 문제를 고려해 장기적으로 덱 코드 또는 `deckId` 방식을 도입한다. - URL, 로그, Git에 사용자 토큰이나 비밀값을 남기지 않는다. - 오류가 있는 카드 한 장 때문에 전체 덱 불러오기가 중단되지 않도록 오류 @@ -174,6 +178,7 @@ function createGameCardInstances(importedCards) { - 화면 변경 → `docs/map.md` - 배포 변경 → `docs/deploy.md` - 개발 명령 변경 → `docs/development.md` +- 데이터 구조·마이그레이션 변경 → `docs/migrations.md` 문서 업데이트 누락을 금지한다. diff --git a/README.md b/README.md index 6fc3512..4740739 100644 --- a/README.md +++ b/README.md @@ -23,5 +23,6 @@ npm run dev - 개발: `docs/development.md` - NAS 배포: `docs/deploy.md` +- 데이터 마이그레이션: `docs/migrations.md` - 프로젝트 명세: `docs/spec.md` - AI 작업 규칙: `AGENTS.md` diff --git a/compose.test.nas.yaml b/compose.test.nas.yaml deleted file mode 100644 index 4273f74..0000000 --- a/compose.test.nas.yaml +++ /dev/null @@ -1,7 +0,0 @@ -services: - playuniare-test-web: - volumes: - - type: bind - source: /volume1/docker/projects/apps/uniare/config/www/images - target: /srv/uniare-card-images - read_only: true diff --git a/compose.test.yaml b/compose.test.yaml deleted file mode 100644 index fad7385..0000000 --- a/compose.test.yaml +++ /dev/null @@ -1,11 +0,0 @@ -name: playuniare-test - -services: - playuniare-test-web: - build: - context: . - dockerfile: Dockerfile - container_name: playuniare-test-web - restart: unless-stopped - ports: - - '${PLAYUNIARE_TEST_HTTP_PORT:-41808}:80' diff --git a/docs/deploy.md b/docs/deploy.md index 1c66b39..09b8403 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -1,325 +1,256 @@ -# UGREEN NAS Docker 배포 안내 +# UGREEN NAS 운영 배포 안내 -## 문서 목적 +## 운영 원칙 -이 문서는 터미널과 Docker에 익숙하지 않은 사용자가 Play Uniare의 테스트 -버전과 운영 버전을 UGREEN NAS에서 실행하고 업데이트하는 절차를 설명한다. +- 기능 테스트는 개발 Mac에서 수행한다. +- NAS에는 운영 컨테이너만 실행한다. +- 로컬 빌드와 Docker 검증을 통과한 커밋만 NAS에 배포한다. +- NAS에서 소스 코드를 직접 수정하지 않는다. +- 운영 주소는 현재 `http://playuniare.sori.studio`다. +- HTTPS는 인증서와 리버스 프록시 구성이 완료된 뒤 전환한다. -현재 프로젝트는 Vue 정적 프런트엔드만 포함한다. 실시간 서버가 추가되면 -`compose.yaml`에 백엔드 서비스와 상태 저장소를 함께 추가한다. +현재 프로젝트는 Vue 정적 프런트엔드만 포함한다. 실시간 서버가 추가되면 같은 +Compose 프로젝트 안에 Node.js 서비스를 추가한다. -## 배포 방식 +## 배포 구조 ```text 사용자 브라우저 ↓ -NAS 포트 또는 리버스 프록시 +http://playuniare.sori.studio ↓ -Play Uniare 컨테이너의 Nginx +NAS 리버스 프록시 + ↓ +127.0.0.1:41809 + ↓ +Play Uniare Nginx 컨테이너 ├── / → Vue 빌드 결과 └── /card-assets/ → 기존 Uniare 카드 이미지 ``` -Docker 이미지를 만들 때 Node.js로 Vue를 빌드하고, 실행할 때는 가벼운 -Nginx가 빌드 결과를 제공한다. - -카드 이미지는 다음 기존 폴더를 복사하지 않고 읽기 전용으로 연결한다. +카드 이미지는 복사하지 않고 다음 폴더를 읽기 전용으로 연결한다. ```text /volume1/docker/projects/apps/uniare/config/www/images ``` -## NAS에서 처음 한 번 준비하기 +## 로컬 배포 전 검증 -### 1. Docker 앱 설치 +개발 Mac의 프로젝트 폴더에서 실행한다. -UGREEN NAS의 앱 센터에서 Docker 또는 Container Manager에 해당하는 앱을 -설치한다. 모델과 UGOS 버전에 따라 앱 이름이 다를 수 있다. - -### 2. 프로젝트 저장 폴더 준비 - -NAS 공유 폴더에 다음과 같은 폴더를 만든다. - -```text -/volume1/docker/projects/apps/ -├── uniare/ -│ └── config/ -│ └── www/ -│ ├── index.html -│ └── images/ -└── playuniare/ +```bash +npm install +npm run build +docker compose build ``` -이 프로젝트의 NAS 경로는 다음으로 고정한다. +필요하면 로컬 Docker 컨테이너도 실행한다. -```text -/volume1/docker/projects/apps/playuniare +```bash +docker compose up -d ``` -### 3. SSH 사용 여부 결정 +브라우저에서 다음 주소를 확인한다. -UGREEN 관리 화면에서 Docker Compose 프로젝트를 직접 등록할 수 있다면 GUI를 -우선 사용한다. GUI에서 Git 저장소를 직접 받을 수 없거나 업데이트가 어려운 -경우에만 SSH 터미널을 사용한다. +```text +http://localhost:41809 +``` -SSH를 활성화할 때는 관리자 화면에서 포트와 계정을 확인한다. NAS 비밀번호를 -문서, `.env`, Git 저장소에 기록하지 않는다. +로컬 Mac에는 NAS 카드 폴더가 없으므로 `/card-assets` 확인은 NAS 배포 후 +수행한다. -## 테스트 버전 실행 +검증이 끝나면 로컬 컨테이너를 종료한다. -테스트 버전은 외부 도메인에 연결하기 전에 NAS 내부에서 확인하는 용도다. +```bash +docker compose down +``` -### 1. NAS에 프로젝트 받기 +## NAS 최초 설치 -NAS 터미널에서 프로젝트 저장 폴더로 이동한다. +### 1. SSH로 NAS 접속 + +개발 Mac 터미널에서 실행한다. + +```bash +ssh NAS사용자명@192.168.50.146 +``` + +실제 SSH 사용자명은 NAS 설정에 맞게 사용한다. + +### 2. 저장소 복제 + +NAS 터미널에서 실행한다. ```bash cd /volume1/docker/projects/apps -``` - -처음 한 번만 저장소를 복제한다. - -```bash git clone https://git.sori.studio/zenn/playuniare.sori.studio.git playuniare cd playuniare ``` -비공개 저장소가 인증을 요구하면 비밀번호 대신 Git 서버에서 발급한 접근 -토큰을 사용하는 것을 권장한다. 토큰을 명령문이나 문서에 저장하지 않는다. +비공개 저장소가 인증을 요구하면 Git 서버의 접근 토큰을 사용한다. 토큰을 +문서나 `.env`에 저장하지 않는다. -### 2. NAS 전용 환경변수 파일 만들기 - -프로젝트를 처음 설치한 경우 예시 파일을 복사한다. +### 3. NAS 환경변수 파일 생성 ```bash cp .env.example .env ``` -기본 포트는 다음과 같다. +기본 내용: ```text -PLAYUNIARE_TEST_HTTP_PORT=41808 PLAYUNIARE_PRODUCTION_HTTP_PORT=41809 +VITE_CARD_ASSET_BASE_URL=/card-assets +VITE_SOCKET_SERVER_URL=http://playuniare.sori.studio ``` -NAS에서 이미 사용 중인 포트라면 `.env`를 열어 앞쪽 숫자만 다른 값으로 -변경한다. `.env`는 Git에 올라가지 않으므로 NAS별 설정을 안전하게 유지할 수 -있다. +현재 Socket.IO 서버는 구현 전이므로 `VITE_SOCKET_SERVER_URL`은 아직 실제 +기능에 사용되지 않는다. -현재 Docker 컨테이너가 공개하고 있는 포트는 다음 명령으로 확인한다. +### 4. 포트 사용 여부 확인 ```bash docker ps --format "table {{.Names}}\t{{.Ports}}" ``` -사용 중인 숫자를 피해서 `1024`보다 큰 포트를 선택한다. - -### 3. 테스트 컨테이너 빌드 및 실행 - -```bash -docker compose \ - -f compose.test.yaml \ - -f compose.test.nas.yaml \ - up -d --build -``` - -이 명령은 다음 작업을 한 번에 수행한다. - -- 최신 소스 코드로 Docker 이미지 생성 -- 테스트 컨테이너 실행 -- NAS의 `PLAYUNIARE_TEST_HTTP_PORT`와 컨테이너의 80 포트 연결 - -### 4. 브라우저에서 확인 - -NAS의 내부 IP가 `192.168.0.20`이라면 다음 주소로 접속한다. +`41809`가 이미 사용 중이면 `.env`의 값을 다른 포트로 바꾼다. ```text -http://192.168.0.20:41808 +PLAYUNIARE_PRODUCTION_HTTP_PORT=41819 ``` -실제 NAS IP와 `.env`에 입력한 테스트 포트에 맞춰 접속한다. +이 경우 리버스 프록시 대상 포트도 같은 값으로 변경해야 한다. -### 5. 테스트 로그 확인 - -화면이 열리지 않으면 다음을 실행한다. +### 5. 운영 컨테이너 실행 ```bash -docker compose \ - -f compose.test.yaml \ - -f compose.test.nas.yaml \ - ps - -docker compose \ - -f compose.test.yaml \ - -f compose.test.nas.yaml \ - logs --tail=100 -``` - -`Up` 또는 `running` 상태인지 확인한다. - -### 6. 테스트 버전 중지 - -```bash -docker compose \ - -f compose.test.yaml \ - -f compose.test.nas.yaml \ - down -``` - -컨테이너는 제거되지만 소스 코드와 Docker 이미지는 남아 있다. - -## 운영 버전 최초 배포 - -### 1. 배포 전 로컬 검증 - -개발 컴퓨터에서 다음 명령이 성공해야 한다. - -```bash -npm install -npm run build -``` - -그 후 변경 사항을 커밋하고 원격 저장소에 푸시한다. - -### 2. NAS에서 운영 컨테이너 실행 - -NAS 프로젝트 폴더에서 실행한다. - -```bash -cd /volume1/docker/projects/apps/playuniare -git pull docker compose \ -f compose.yaml \ -f compose.nas.yaml \ up -d --build ``` -운영 컨테이너는 기본적으로 NAS의 `41809` 포트를 사용한다. `.env`의 -`PLAYUNIARE_PRODUCTION_HTTP_PORT`를 변경했다면 해당 포트를 사용한다. +상태 확인: -```text -http://NAS_IP:41809 +```bash +docker compose \ + -f compose.yaml \ + -f compose.nas.yaml \ + ps ``` -### 3. 도메인 연결 +`playuniare-web`이 `healthy` 또는 `running`이면 정상이다. -UGREEN NAS 또는 사용 중인 리버스 프록시에서 다음과 같이 연결한다. +### 6. 리버스 프록시 연결 + +현재 연결 기준: ```text -외부 도메인: https://playuniare.sori.studio +외부 주소: http://playuniare.sori.studio 내부 대상: http://127.0.0.1:41809 ``` -- 외부 접속은 HTTPS를 사용한다. -- `.env`에서 운영 포트를 변경했다면 리버스 프록시의 내부 대상 포트도 같은 - 값으로 변경한다. -- 공유기에서 컨테이너 포트를 그대로 외부에 노출하기보다 리버스 프록시를 - 사용한다. -- 인증서 자동 갱신이 설정되어 있는지 확인한다. +`.env`에서 포트를 변경했다면 내부 대상 포트도 변경한다. -### 4. 카드 이미지 연결 확인 +### 7. 배포 확인 -브라우저에서 실제 카드 파일 하나를 직접 연다. +메인 화면: ```text -http://NAS_IP:41809/card-assets/ua03/EX01BT_HTR-2-001.png +http://playuniare.sori.studio ``` -이미지가 보이면 읽기 전용 마운트와 Nginx 경로가 정상이다. 파일명이 실제로 -존재하는 카드인지 먼저 확인한다. +카드 이미지: -이미지가 보이지 않으면 NAS 터미널에서 원본 경로를 확인한다. - -```bash -ls /volume1/docker/projects/apps/uniare/config/www/images +```text +http://playuniare.sori.studio/card-assets/ua03/EX01BT_HTR-2-001.png ``` -시리즈 폴더가 보여야 한다. 다음 명령으로 컨테이너 내부 연결도 확인한다. - -```bash -docker exec playuniare-web ls /srv/uniare-card-images -``` - -이 경로 역시 같은 시리즈 폴더를 보여야 한다. - -Play Uniare의 Nginx 작업 프로세스는 기존 카드 파일 소유자와 같은 UID -`1000`의 `cardreader` 사용자로 실행된다. 원본 카드 폴더는 계속 읽기 전용으로 -마운트되므로 시뮬레이터가 파일을 수정하거나 삭제할 수 없다. +두 주소가 모두 열리면 현재 정적 배포와 카드 자산 연결은 정상이다. ## 새 버전 업데이트 -### 안전한 권장 순서 +### 개발 Mac -1. 개발 컴퓨터에서 `npm run build`를 실행한다. -2. 변경 내용을 Git에 커밋하고 푸시한다. -3. NAS에서 테스트 버전을 먼저 업데이트한다. -4. 테스트 주소에서 주요 기능을 확인한다. -5. 문제가 없으면 운영 버전을 업데이트한다. +1. 기능을 구현한다. +2. 관련 문서를 수정한다. +3. `npm run build`를 실행한다. +4. 필요한 경우 로컬 Docker로 검증한다. +5. Git에 커밋하고 푸시한다. -### 테스트 버전 업데이트 +### NAS ```bash cd /volume1/docker/projects/apps/playuniare git pull -docker compose \ - -f compose.test.yaml \ - -f compose.test.nas.yaml \ - up -d --build -``` -브라우저에서 `http://NAS_IP:41808`을 확인한다. `.env`에서 테스트 포트를 -변경했다면 해당 포트로 접속한다. - -### 운영 버전 업데이트 - -```bash -cd /volume1/docker/projects/apps/playuniare -git pull docker compose \ -f compose.yaml \ -f compose.nas.yaml \ up -d --build ``` -브라우저에서 실제 도메인을 확인한다. - -### 사용하지 않는 Docker 이미지 정리 - -업데이트를 여러 번 하면 이전 이미지가 NAS 용량을 차지할 수 있다. +배포 후 반드시 확인한다. ```bash -docker image prune +docker compose \ + -f compose.yaml \ + -f compose.nas.yaml \ + ps + +docker compose \ + -f compose.yaml \ + -f compose.nas.yaml \ + logs --tail=100 ``` -삭제 대상 확인 질문이 나오면 내용을 읽고 `y`를 입력한다. `-a` 옵션이나 -볼륨 삭제 명령은 데이터 유실 가능성이 있으므로 이 문서에서는 사용하지 않는다. +마지막으로 실제 도메인에서 메인 화면과 주요 기능을 확인한다. + +## 기존 NAS 테스트 컨테이너 제거 + +이전에 `playuniare-test-web`을 실행했다면 한 번만 정리한다. + +```bash +docker rm -f playuniare-test-web +``` + +컨테이너가 없다는 메시지가 나오면 이미 제거된 것이므로 무시해도 된다. + +테스트용 Docker 네트워크가 남아 있다면 확인 후 제거한다. + +```bash +docker network ls +docker network rm playuniare-test_default +``` + +네트워크가 사용 중이거나 존재하지 않으면 무리하게 삭제하지 않는다. ## 이전 버전으로 되돌리기 -문제가 생기면 먼저 현재 커밋을 확인한다. +최근 커밋 확인: ```bash git log --oneline -10 ``` -정상 동작했던 커밋 식별자를 확인한 뒤 다음처럼 전환한다. +정상 동작했던 커밋으로 전환: ```bash git checkout 정상커밋식별자 + docker compose \ -f compose.yaml \ -f compose.nas.yaml \ up -d --build ``` -복구가 끝난 뒤 개발 컴퓨터에서 문제를 수정하고 정상 브랜치에 새 커밋을 -만든다. NAS에서 임의로 소스 파일을 직접 수정하지 않는다. - -다시 최신 `main`으로 돌아갈 때: +복구 후 개발 Mac에서 문제를 수정해 새로운 커밋을 만든다. 다시 최신 +`main`으로 돌아갈 때: ```bash git checkout main git pull + docker compose \ -f compose.yaml \ -f compose.nas.yaml \ @@ -328,163 +259,113 @@ docker compose \ ## 운영 점검 명령 +상태: + ```bash docker compose -f compose.yaml -f compose.nas.yaml ps +``` + +최근 로그: + +```bash docker compose -f compose.yaml -f compose.nas.yaml logs --tail=100 +``` + +재시작: + +```bash docker compose -f compose.yaml -f compose.nas.yaml restart ``` -- `ps`: 실행 상태 확인 -- `logs`: 최근 오류 확인 -- `restart`: 같은 이미지로 컨테이너 재시작 +중지 및 제거: -## `port is already allocated` 오류 +```bash +docker compose -f compose.yaml -f compose.nas.yaml down +``` -다음 오류는 Docker 이미지 빌드 문제가 아니라 NAS 호스트 포트 충돌이다. +## 포트 충돌 + +다음 오류는 NAS 호스트 포트가 다른 서비스에 사용 중이라는 뜻이다. ```text Bind for 0.0.0.0:<포트> failed: port is already allocated ``` -먼저 실패하며 생성된 테스트 컨테이너를 정리한다. - -```bash -docker compose \ - -f compose.test.yaml \ - -f compose.test.nas.yaml \ - down -``` - -현재 사용 중인 Docker 포트를 확인한다. +사용 중인 포트 확인: ```bash docker ps --format "table {{.Names}}\t{{.Ports}}" ``` -`.env`에서 테스트 포트를 사용하지 않는 숫자로 변경한다. +`.env`의 `PLAYUNIARE_PRODUCTION_HTTP_PORT`를 사용하지 않는 숫자로 변경하고 +다시 실행한다. 컨테이너 내부의 80 포트는 다른 컨테이너와 같아도 괜찮다. -```text -PLAYUNIARE_TEST_HTTP_PORT=41818 -``` +## 카드 이미지가 403인 경우 -그다음 다시 실행한다. - -```bash -docker compose \ - -f compose.test.yaml \ - -f compose.test.nas.yaml \ - up -d --build -``` - -컨테이너 내부의 80 포트는 다른 컨테이너와 같아도 괜찮다. `.env`에 설정하는 -NAS 호스트 포트만 겹치지 않으면 된다. - -## 카드 이미지가 `403 Forbidden`인 경우 - -로그에 다음 내용이 있으면 Nginx 작업 사용자가 NAS 카드 파일을 읽지 못한 -것이다. +로그에 다음 내용이 있으면 NAS ACL에 의한 읽기 권한 문제다. ```text open() "/srv/uniare-card-images/..." failed (13: Permission denied) ``` -현재 Docker 이미지는 기존 카드 파일 소유자 UID `1000`에 맞춘 `cardreader` -사용자를 사용한다. 최신 소스를 받은 후 이미지를 반드시 다시 빌드한다. +현재 이미지는 카드 파일 소유자 UID `1000`과 맞춘 `cardreader` 사용자를 +사용한다. 최신 소스를 받고 반드시 이미지를 다시 빌드한다. ```bash -cd /volume1/docker/projects/apps/playuniare git pull - -docker compose \ - -f compose.test.yaml \ - -f compose.test.nas.yaml \ - down - -docker compose \ - -f compose.test.yaml \ - -f compose.test.nas.yaml \ - up -d --build +docker compose -f compose.yaml -f compose.nas.yaml up -d --build ``` -Nginx 워커 사용자를 확인하려면: +Nginx 워커 확인: ```bash -docker exec playuniare-test-web ps aux +docker exec playuniare-web ps aux ``` `nginx: worker process`가 `cardreader`로 표시되어야 한다. -카드 파일 읽기를 직접 확인하려면: +카드 읽기 확인: ```bash -docker exec playuniare-test-web \ +docker exec playuniare-web \ su -s /bin/sh cardreader -c \ 'head -c 10 /srv/uniare-card-images/ua03/EX01BT_HTR-2-001.png >/dev/null && echo READ_OK' ``` -`READ_OK`가 나오면 다음 주소에서 카드가 보여야 한다. +## 데이터 마이그레이션 -```text -http://NAS_IP:41808/card-assets/ua03/EX01BT_HTR-2-001.png -``` +현재는 데이터베이스가 없으므로 마이그레이션할 데이터도 없다. 방 상태 저장, +사용자 계정 또는 덱 저장 데이터베이스가 추가되면 +`docs/migrations.md`의 원칙을 따른다. -NAS에서 카드 파일 소유자의 UID가 `1000`이 아닌 경우에는 임의로 폴더 권한을 -`777`로 변경하지 말고, 실제 UID를 확인한 뒤 Docker 실행 사용자 정책을 -조정한다. +NAS에서 SQL이나 임시 명령을 직접 입력하는 방식은 초기에는 빠르지만, 같은 +변경을 다시 재현하거나 복구하기 어렵다. 운영 데이터가 생긴 뒤에는 반드시 +Git에 포함된 버전 관리 마이그레이션 코드를 실행하는 방식으로 전환한다. ## 배포 체크리스트 - [ ] 로컬 `npm run build` 성공 +- [ ] 필요한 로컬 Docker 검증 성공 - [ ] 관련 문서 업데이트 - [ ] Git 커밋과 푸시 완료 -- [ ] 테스트 컨테이너 재빌드 -- [ ] 테스트 URL 주요 기능 확인 +- [ ] NAS `git pull` 완료 - [ ] 운영 컨테이너 재빌드 -- [ ] 실제 도메인 HTTPS 접속 확인 -- [ ] `/card-assets//` 이미지 접속 확인 -- [ ] 카드 이미지 마운트가 읽기 전용인지 확인 -- [ ] 브라우저 새로고침과 직접 URL 접근 확인 -- [ ] 오류 로그 확인 +- [ ] 컨테이너 상태와 로그 확인 +- [ ] `http://playuniare.sori.studio` 확인 +- [ ] `/card-assets//` 확인 - [ ] `.env`, 토큰, 비밀번호가 Git에 없는지 확인 -## 향후 실시간 서버 추가 시 +## 향후 실시간 서버 -프런트엔드만 있을 때는 Nginx 컨테이너 하나면 충분하다. Socket.IO 서버가 -추가되면 다음 항목이 필요하다. - -- Node.js 서버 컨테이너 -- WebSocket을 지원하는 리버스 프록시 설정 -- 프런트엔드와 서버의 환경변수 -- 방 상태 백업 또는 Redis 도입 여부 -- 컨테이너 재시작 시 방이 종료된다는 안내 - -로컬 개발 서버의 기본 포트는 `31873`으로 정한다. Docker 네트워크 안에서는 -다른 컨테이너가 같은 내부 포트를 사용해도 문제가 없다. 충돌하는 것은 NAS -호스트에 공개한 포트다. - -운영 환경에서는 Socket.IO 서버 포트를 NAS 외부에 직접 공개하지 않는 것을 -원칙으로 한다. +Socket.IO 서버가 추가되면 브라우저에 서버 포트를 직접 공개하지 않는다. ```text 브라우저 - → https://playuniare.sori.studio/socket.io + → http://playuniare.sori.studio/socket.io → Play Uniare Nginx → Docker 내부 Socket.IO 서버:31873 ``` -이 구조에서는 NAS의 다른 서비스가 호스트 포트 `31873`을 사용 중이어도 -Socket.IO 컨테이너가 해당 포트를 호스트에 공개하지 않는 한 충돌하지 않는다. -실시간 서버 구현 시 `expose`만 사용하고 `ports`는 추가하지 않는 방향을 +Docker 내부에서는 다른 컨테이너와 같은 포트를 사용해도 충돌하지 않는다. +Socket.IO 서비스는 `ports` 대신 내부 통신용 `expose`를 사용하는 방향을 우선한다. - -이 구조가 변경되면 반드시 이 문서와 `docs/history.md`를 함께 수정한다. - -## NAS 전용 Compose 파일을 분리한 이유 - -- `compose.yaml`: 어느 컴퓨터에서도 검사하고 빌드할 수 있는 공통 운영 설정 -- `compose.nas.yaml`: NAS에만 존재하는 카드 이미지 절대 경로 연결 -- `compose.test.yaml`: 공통 테스트 설정 -- `compose.test.nas.yaml`: 테스트 컨테이너의 NAS 카드 이미지 연결 - -개발 Mac에는 `/volume1/...` 경로가 없으므로 NAS 전용 파일을 사용하지 않는다. -NAS에서는 반드시 공통 파일과 NAS 전용 파일을 함께 지정한다. diff --git a/docs/history.md b/docs/history.md index d2ac11f..12ab4ef 100644 --- a/docs/history.md +++ b/docs/history.md @@ -24,7 +24,8 @@ - UGREEN NAS에서 재현 가능한 배포를 위해 Docker Compose를 사용한다. - Vue 빌드에는 Node.js를 사용하고 실행 이미지는 Nginx를 사용한다. -- 테스트와 운영 컨테이너를 서로 다른 포트로 분리한다. +- 초기에는 테스트와 운영 컨테이너를 분리했으며 이후 운영 단일 구조로 + 단순화했다. ## 2026-06-19: 기존 카드 이미지 읽기 전용 공유 @@ -58,3 +59,11 @@ - 카드 파일 소유자 UID가 `1000`인 것을 확인했다. - Nginx 워커를 UID `1000`의 `cardreader` 사용자로 실행하도록 변경했다. - Vue 정적 파일에는 빌드 단계에서 명시적인 읽기 권한을 적용했다. + +## 2026-06-19: 로컬 테스트와 NAS 운영 단일화 + +- 기능 및 Docker 검증은 개발 Mac에서 수행한다. +- NAS에는 운영 컨테이너 하나만 유지한다. +- 테스트 Compose 파일과 테스트 포트 환경변수를 제거했다. +- 운영 주소를 `http://playuniare.sori.studio`로 확인했다. +- 영구 데이터가 생길 때 적용할 마이그레이션 운영 원칙을 문서화했다. diff --git a/docs/migrations.md b/docs/migrations.md new file mode 100644 index 0000000..fd23b1e --- /dev/null +++ b/docs/migrations.md @@ -0,0 +1,67 @@ +# 데이터 마이그레이션 운영 원칙 + +## 현재 상태 + +현재 Play Uniare에는 데이터베이스와 영구 서버 데이터가 없다. 따라서 지금은 +마이그레이션 명령을 실행할 필요가 없다. + +## 마이그레이션이 필요한 시점 + +다음 기능 중 하나가 추가되면 마이그레이션 체계를 도입한다. + +- 사용자 계정 +- 저장된 덱 +- 게임 기록 +- 장기 유지되는 방 상태 +- Redis 외의 영구 데이터베이스 +- 기존 저장 데이터의 구조 변경 + +## 기본 원칙 + +1. 모든 마이그레이션 코드는 Git으로 관리한다. +2. 파일명에 실행 순서와 목적이 드러나야 한다. +3. 같은 마이그레이션을 두 번 실행해도 데이터가 망가지지 않아야 한다. +4. 운영 실행 전에 데이터베이스를 백업한다. +5. 로컬 또는 복제 데이터로 먼저 검증한다. +6. 성공 여부를 기록하는 마이그레이션 테이블을 사용한다. +7. NAS에서 즉석 SQL을 직접 입력하는 것은 긴급 복구 외에는 금지한다. +8. 긴급 명령을 사용했다면 같은 내용을 정식 마이그레이션 코드로 남긴다. + +## 예정 디렉터리 + +데이터베이스 기술이 결정되면 다음과 같은 구조를 사용한다. + +```text +server/ +└── migrations/ + ├── 0001_create_rooms.js + └── 0002_add_room_expiration.js +``` + +실제 도구가 Prisma, Drizzle 또는 다른 마이그레이션 시스템으로 결정되면 해당 +도구의 표준 디렉터리를 우선한다. + +## 배포 순서 + +일반적인 운영 배포 순서: + +1. 데이터베이스 백업 +2. 새 코드 가져오기 +3. 마이그레이션 실행 +4. 애플리케이션 컨테이너 재빌드 +5. 상태와 로그 확인 +6. 주요 데이터 조회 검증 + +마이그레이션과 애플리케이션 코드의 선후 관계가 다르면 해당 릴리스의 +`docs/update.md`에 정확한 순서를 기록한다. + +## 파괴적 변경 + +열 삭제, 데이터 타입 축소, 대량 데이터 재작성은 한 번에 수행하지 않는다. + +1. 새 필드 추가 +2. 기존 데이터를 새 필드로 복사 +3. 애플리케이션을 새 필드로 전환 +4. 충분한 확인 기간 후 이전 필드 제거 + +복구 불가능한 변경은 사용자 승인과 백업 확인 없이 실행하지 않는다. diff --git a/docs/spec.md b/docs/spec.md index 7a93bde..29d483d 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -77,11 +77,12 @@ - 로컬 Socket.IO 서버 기본 포트는 `31873`이다. - Docker 컨테이너 내부 포트는 다른 컨테이너와 같아도 충돌하지 않는다. - NAS 호스트에 공개하는 포트는 다른 서비스와 겹치면 안 된다. -- 테스트 HTTP 호스트 포트 기본값은 `41808`, 운영 기본값은 `41809`다. -- HTTP 호스트 포트는 `.env`의 `PLAYUNIARE_TEST_HTTP_PORT`와 +- NAS 운영 HTTP 호스트 포트 기본값은 `41809`다. +- 운영 HTTP 호스트 포트는 `.env`의 `PLAYUNIARE_PRODUCTION_HTTP_PORT`로 변경한다. +- 기능 테스트는 로컬에서 수행하고 NAS에는 운영 컨테이너만 배포한다. - 운영 브라우저는 Socket.IO 포트를 직접 호출하지 않고 - `https://playuniare.sori.studio/socket.io`처럼 같은 도메인의 리버스 프록시 + `http://playuniare.sori.studio/socket.io`처럼 같은 도메인의 리버스 프록시 경로를 사용한다. - 포트 번호는 코드에 하드코딩하지 않고 환경변수와 Compose 설정으로 관리한다. @@ -89,7 +90,7 @@ ```text VITE_CARD_ASSET_BASE_URL=/card-assets -VITE_SOCKET_SERVER_URL=https://playuniare.sori.studio +VITE_SOCKET_SERVER_URL=http://playuniare.sori.studio ``` 실제 운영 값은 `.env`에 두고 Git에 커밋하지 않는다. @@ -120,3 +121,11 @@ Play Uniare 컨테이너 내부: `cardreader` 사용자로 실행한다. - 카드 이미지 URL은 `/card-assets//` 형식을 사용한다. - 같은 Play Uniare 도메인으로 제공되므로 별도의 CORS 설정이 필요하지 않다. + +## 배포 환경 + +- 로컬 개발: Vite 개발 서버 +- 로컬 배포 검증: Docker Compose +- NAS 운영: `compose.yaml`과 `compose.nas.yaml` +- 운영 주소: `http://playuniare.sori.studio` +- NAS 테스트 컨테이너는 별도로 유지하지 않는다. diff --git a/docs/update.md b/docs/update.md index c4e759e..ce82fa5 100644 --- a/docs/update.md +++ b/docs/update.md @@ -17,3 +17,7 @@ 변경하고 `port is already allocated` 복구 절차를 추가했다. - NAS ACL로 인한 카드 이미지와 favicon의 403 오류를 해결하기 위해 Nginx 워커 UID와 정적 파일 권한을 조정했다. +- 테스트는 로컬에서 수행하고 NAS에는 운영 컨테이너만 배포하도록 Compose와 + 배포 문서를 단순화했다. +- 운영 주소 `http://playuniare.sori.studio`와 향후 데이터 마이그레이션 + 원칙을 문서화했다.