운영 볼륨이 이미 존재하지 않는 완전 신규 설치에서는 먼저 Docker 앱에서 `soristudio_sori-studio-postgres` 이름의 빈 PostgreSQL 볼륨을 만들거나 `sudo docker volume create soristudio_sori-studio-postgres`를 실행한 뒤 배포한다.
```bash
sh scripts/migrate-production-db.sh status
sh scripts/migrate-production-db.sh migrate
sh scripts/migrate-production-db.sh status
sudo sh scripts/migrate-production-db.sh status
sudo sh scripts/migrate-production-db.sh migrate
sudo sh scripts/migrate-production-db.sh status
```
-`068_csp_violation_aggregates.sql`은 CSP 보고 집계 테이블을 만든다.
# UGREEN NAS Docker 앱처럼 --env-file 옵션을 넣기 어려운 GUI 배포에서는
# docker-compose.yml과 .env.production을 같은 프로젝트 위치에 두고 Compose 빌드를 실행한다.
# 앱과 DB 컨테이너는 env_file 설정으로 .env.production을 직접 읽는다.
# 운영 DB 마이그레이션 상태 확인
sh scripts/migrate-production-db.sh status
sudo sh scripts/migrate-production-db.sh status
# schema_migrations 도입 전 운영 DB가 이미 최신이면 최초 1회 기준점 기록(실제 SQL 실행 없음)
sh scripts/migrate-production-db.sh baseline
sudo sh scripts/migrate-production-db.sh baseline
# 이후 배포에서는 아직 적용되지 않은 SQL만 순서대로 실행
sh scripts/migrate-production-db.sh migrate
sudo sh scripts/migrate-production-db.sh migrate
```
### 운영 업데이트 (코드 반영)
### Compose 프로젝트명과 네트워크 일치 (v1.12.26)
이미 한 번 올려 둔 NAS에서 **새 커밋을 받아 반영**할 때는 보통 아래 순서를 따른다. 최초 설치 절차와 달리 `git clone`은 하지 않는다.
운영 기본 프로젝트명은 `sori-studio`다. `docker-compose.yml`의 최상위 `name`은 `${COMPOSE_PROJECT_NAME:-sori-studio}`를 사용하고 `.env.production`에도 `COMPOSE_PROJECT_NAME=sori-studio`를 설정한다. CLI `-p`가 환경 변수보다 우선하므로 아래 NAS 명령은 기존 운영 프로젝트를 명시한다. DB 볼륨 이름은 프로젝트명과 독립적으로 기존 `POSTGRES_VOLUME_NAME`/외부 볼륨 설정을 유지한다.
`Pool overlaps`와 `soristudio_sori-studio-network` 생성 실패가 함께 나오고 기존 `sori-studio_sori-studio-network`가 같은 대역을 쓰고 있다면 프로젝트명 불일치다. 기존 네트워크 삭제나 다른 대역 변경 전에 프로젝트명을 맞춘다. `docker-compose.yml`과 `docker-compose.yaml`이 둘 다 있어도 `-f docker-compose.yml`로 저장소 파일을 명시한다. 운영 마이그레이션·썸네일 백필 스크립트도 같은 파일을 명시한다.
이미 빌드가 성공한 직후 네트워크 생성만 실패했다면 다음 명령으로 해당 이미지를 실행한다.
```bash
sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production up -d --no-build --pull never
기본 운영 경로는 터미널에서 소스만 `git pull`하고, UGREEN Docker 앱의 기존 `sori-studio` 프로젝트에서 재배포하는 방식이다. 같은 Docker 엔진을 쓰므로 GUI와 CLI 자체가 충돌하는 것은 아니며, 프로젝트 이름과 구성 파일이 다를 때 별도 네트워크 생성 충돌이 발생한다.
1. 프로젝트의 실제 빌드 폴더에서 `git pull`한다. GUI 프로젝트의 구성 파일 경로·빌드 context가 이 폴더를 가리켜야 한다.
3. 앱이 관리하는 구성을 저장소의 최신 `docker-compose.yml`과 맞춘다. `.yaml`과 `.yml`이 함께 있는 NAS에서는 앱의 구성 파일 경로가 어느 파일인지 확인한다. 앱이 별도 사본을 저장하면 최신 `.yml` 내용을 Compose 편집기에 반영하고 저장한다. 기존 운영 `.env.production`은 유지한다.
4. 같은 화면의 `재배포`를 실행한다. `image: sori-studio:latest`, `build.context: .`, `pull_policy: build`가 있어야 소스 기반 재빌드를 요청한다. 컨테이너 시작/중지 토글은 코드 빌드를 수행하는 배포 절차와 다르다.
5. 배포 로그에서 Dockerfile의 `npm run build` 단계가 실행됐는지와 새 컨테이너의 실행 상태를 확인한다. GUI 버전별 실제 재빌드 동작은 NAS에서 확인해야 한다. Docker 앱이 빌드 정책을 무시하면 아래 CLI 경로를 사용한다.
v1.12.24~26에는 새 DB 마이그레이션 파일이 없으므로 이 버전 범위만 업데이트할 때는 DB 마이그레이션 실행이 필요하지 않다. 이후 `db/migrations`가 변경되는 배포에서는 아래 스크립트로 미적용 SQL을 처리한다. 운영자 요청에 따라 소스 동기화 이후 앱 재배포를 우선하며 GUI에 없는 자동 Git 동기화·자동 마이그레이션을 가정하지 않는다.
-`.env.production`은 Git에 포함하지 않는다. `git pull`로 덮어쓰이지 않는다. 값을 바꿀 때만 파일을 직접 수정한다.
-`public/uploads/` 업로드 파일은 Docker 볼륨(`./public/uploads`)에 있으므로, **이미지 파일만 추가·수정한 경우** 앱 재빌드 없이도 URL로 바로 보인다.
- 로컬에서 미리 확인하려면 `npm run verify` 후 NAS에서 위 명령을 실행하면 된다.
- UGREEN Docker 앱의 재배포가 너무 빨리 끝나고 앱 빌드 로그가 보이지 않으면 기존 이미지를 재사용한 것이다. 현재 Compose는 `sori-studio:latest`와 `pull_policy: build`로 앱 이미지를 다시 빌드하도록 선언하지만, NAS Docker 앱 버전이 이 값을 무시하면 터미널에서 `docker compose --env-file .env.production up -d --build`를 실행해야 한다.
- UGREEN Docker 앱의 재배포가 너무 빨리 끝나고 앱 빌드 로그가 보이지 않으면 기존 이미지를 재사용한 것이다. 현재 Compose는 `sori-studio:latest`와 `pull_policy: build`로 앱 이미지를 다시 빌드하도록 선언하지만, NAS Docker 앱 버전이 이 값을 무시하면 터미널에서 `sudo docker compose -p sori-studio -f docker-compose.yml --env-file .env.production up -d --build`를 실행해야 한다.
- 스키마 변경은 `db/migrations/`의 SQL 파일로 관리한다. 버전별 변경 내역은 `docs/update.md`를 참고한다.
- 로컬: `npm run db:migrate:dev`
- NAS 운영: `sh scripts/migrate-production-db.sh status` → `migrate`
- NAS 운영: `sudo sh scripts/migrate-production-db.sh status` → `migrate`
- 각 SQL 파일 실행과 `schema_migrations` 이력 기록은 같은 `psql --single-transaction`에서 처리되며 오류 시 함께 롤백된다.
-`schema_migrations`가 없는 기존 운영 DB에서 `posts` 테이블이 감지되면 `migrate`는 001부터 자동 실행하지 않는다. 현재 코드 기준 최신 DB라면 최초 1회 `sh scripts/migrate-production-db.sh baseline`으로 기존 파일을 적용 완료로 기록한다. 특정 번호까지만 기록하려면 예: `sh scripts/migrate-production-db.sh baseline 031`.
-`schema_migrations`가 없는 기존 운영 DB에서 `posts` 테이블이 감지되면 `migrate`는 001부터 자동 실행하지 않는다. 현재 코드 기준 최신 DB라면 최초 1회 `sudo sh scripts/migrate-production-db.sh baseline`으로 기존 파일을 적용 완료로 기록한다. 특정 번호까지만 기록하려면 예: `sudo sh scripts/migrate-production-db.sh baseline 031`.
| `npm run images:backfill-post-thumbnails` | 로컬 Node/npm 환경에서 게시물 대표 이미지용 카드 썸네일(`thumbs/*-card.webp`) 일괄 생성 |
| `npm run images:backfill-post-body-thumbnails` | 로컬 Node/npm 환경에서 게시물 본문 표시용 WebP 이미지(`thumbs/*-body-1600.webp`, `thumbs/*-body-720.webp`, `thumbs/*-body-2560.webp`) 일괄 생성 |
| `npm run images:backfill-page-cover-thumbnails` | 로컬 Node/npm 환경에서 홈 Pages 진열장 커버용 WebP 이미지(`thumbs/*-page-cover.webp`) 일괄 생성 |
| `sh scripts/backfill-post-thumbnails-production.sh` | npm 없는 NAS 호스트에서 앱 컨테이너 내부 Node로 카드 썸네일 일괄 생성 |
| `sh scripts/backfill-post-body-thumbnails-production.sh` | npm 없는 NAS 호스트에서 앱 컨테이너 내부 Node로 본문 표시용 WebP 이미지 일괄 생성 |
| `sh scripts/backfill-page-cover-thumbnails-production.sh` | npm 없는 NAS 호스트에서 앱 컨테이너 내부 Node로 Pages 커버 썸네일 일괄 생성 |
| `sudo sh scripts/backfill-post-thumbnails-production.sh` | npm 없는 NAS 호스트에서 앱 컨테이너 내부 Node로 카드 썸네일 일괄 생성 |
| `sudo sh scripts/backfill-post-body-thumbnails-production.sh` | npm 없는 NAS 호스트에서 앱 컨테이너 내부 Node로 본문 표시용 WebP 이미지 일괄 생성 |
| `sudo sh scripts/backfill-page-cover-thumbnails-production.sh` | npm 없는 NAS 호스트에서 앱 컨테이너 내부 Node로 Pages 커버 썸네일 일괄 생성 |
| `npm run navigation:refresh-recommended-favicons` | 추천 사이트 파비콘 캐시를 수동으로 전체 갱신 |
본문 표시용 WebP 이미지(`thumbs/*-body-1600.webp`, `thumbs/*-body-720.webp`, `thumbs/*-body-2560.webp`)는 게시물 저장 시 생성된다. 기존 게시물의 과거 이미지는 로컬에서는 `npm run images:backfill-post-body-thumbnails`, NAS 운영에서는 `sh scripts/backfill-post-body-thumbnails-production.sh`로 일괄 생성할 수 있다. production 래퍼는 `docker compose up -d sori-studio` 후 실행 중인 앱 컨테이너 안의 Node로 백필 스크립트를 실행하므로 NAS 호스트에 npm이 없어도 된다.
본문 표시용 WebP 이미지(`thumbs/*-body-1600.webp`, `thumbs/*-body-720.webp`, `thumbs/*-body-2560.webp`)는 게시물 저장 시 생성된다. 기존 게시물의 과거 이미지는 로컬에서는 `npm run images:backfill-post-body-thumbnails`, NAS 운영에서는 `sudo sh scripts/backfill-post-body-thumbnails-production.sh`로 일괄 생성할 수 있다. production 래퍼는 `docker compose up -d sori-studio` 후 실행 중인 앱 컨테이너 안의 Node로 백필 스크립트를 실행하므로 NAS 호스트에 npm이 없어도 된다.
Pages 진열장 커버용 WebP 이미지(`thumbs/*-page-cover.webp`, 450×600)는 페이지 저장 시 생성된다. 기존 홈 노출 페이지의 과거 이미지는 로컬에서는 `npm run images:backfill-page-cover-thumbnails`, NAS 운영에서는 `sh scripts/backfill-page-cover-thumbnails-production.sh`로 일괄 생성할 수 있다.
Pages 진열장 커버용 WebP 이미지(`thumbs/*-page-cover.webp`, 450×600)는 페이지 저장 시 생성된다. 기존 홈 노출 페이지의 과거 이미지는 로컬에서는 `npm run images:backfill-page-cover-thumbnails`, NAS 운영에서는 `sudo sh scripts/backfill-page-cover-thumbnails-production.sh`로 일괄 생성할 수 있다.
Homepage 위젯 연동은 `docs/homepage-widget.md`를 참고한다. Docker에서 Homepage가 같은 네트워크에 있으면 `http://sori-studio:3000/api/homepage-widget`을 사용한다.
## 2026-10-21 v1.12.26 — 운영 Compose 프로젝트명과 파일 선택을 일치시킨다
제공된 NAS 로그에서 빌드는 성공했지만 CLI가 `soristudio` 프로젝트의 네트워크를 새로 만들려다 기존 `sori-studio` 프로젝트의 동일 대역과 충돌했다. 기존 운영 네트워크를 재사용하도록 Compose 기본 이름을 환경 변수 기반 `sori-studio`로 맞추고, 배포 안내에는 프로젝트명을 명시한다. `.yaml`과 `.yml` 중복 경고를 줄이고 운영 스크립트도 같은 구성을 사용하도록 파일 경로를 명시한다. DB 외부 볼륨의 이름과 보관 방식은 유지한다. 사용자가 Docker 앱 배포를 선호하므로 소스 git pull 후 기존 프로젝트의 Compose 구성에서 재배포하는 경로를 기본 안내로 둔다. 앱 버전의 실제 재빌드 수행 여부는 배포 로그로 확인하며, CLI는 보조 경로로 구분한다.
## 2026-10-21 v1.12.25 — 접속자 목록은 최근 페이지만 요약한다
한 방문자가 여러 탭을 열면 접속자 행 높이가 크게 늘어나므로 이름·가장 최근 확인된 페이지·나머지 고유 페이지 수·체류·IP를 한 줄로 표시한다. 상세 목록은 hover뿐 아니라 키보드와 클릭으로도 접근할 수 있게 하며, heartbeat의 마지막 확인 순서를 사용한다. 같은 경로 중복은 표시 목록에서 제거해 `외 N개`와 펼친 목록의 수를 일치시킨다. 기존 API 집계 기준은 유지하고, 자주 확인할 수입·추이·상세·댓글을 접속자 목록 위에 배치한다.
- 운영 Compose 기본 프로젝트명을 기존 sori-studio로 고정해 중복 네트워크 생성 충돌 보정.
- 마이그레이션·썸네일 백필 운영 스크립트의 docker-compose.yml 파일 지정 추가.
- 환경 변수 예시와 sudo·프로젝트명·Compose 파일 지정 기반 운영 배포 명령 정리.
- git pull 후 UGREEN 기존 프로젝트의 Compose 구성·재배포를 사용하는 GUI 우선 절차 정리.
- 운영 스크립트 Compose 파일·환경 파일 전달을 실제 Docker 대신 대체 실행기로 검증 추가.
- Compose 기본/사용자 지정 프로젝트명·기존 외부 DB 볼륨·셸 문법·JavaScript 문법·단위 테스트 280건 검증 진행.
## v1.12.25
- 대시보드 요약·통계·차트·목록 건수에 천 단위 쉼표 표시 적용.
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.