# 공용 게시판 독립 저장소 구축 계획
## 1. 문서 상태
- 상태: 계획 수립 완료, 구현 전
- 작성일: 2026-09-10
- 개발 원본: `ShinChoongSoo/zalnas`의 `main`
- 게시 대상: 비공개 저장소 `ShinChoongSoo/zalnas-board`
- 기준 실행환경: Apache `2.4.68`, PHP `8.5.9`, MariaDB `12.3.3`
- 작업 규모: High — 저장소 분리, GitHub 인증, 릴리스 게시와 기존 게시판 회귀 검증이 필요함
이 문서는 ZALNAS에서 검증된 공용 게시판 소스만 별도 GitHub 저장소에 버전별로 게시하기 위한 실행 계획이다. `zalnas-board`는 소스를 제공하는 저장소일 뿐이며, 다른 사이트에 직접 변경을 적용하지 않는다.
## 2. 확정된 요구사항
1. 게시판은 ZALNAS 전체 저장소의 별도 branch가 아니라 독립 저장소 `ShinChoongSoo/zalnas-board`로 관리한다.
2. 개발 원본은 ZALNAS 저장소의 `modules/board`로 유지한다.
3. ZALNAS에서 검증된 공용 게시판 소스만 `zalnas-board`에 게시한다.
4. `zalnas-board/main`은 항상 가장 최근의 안정 릴리스와 같은 내용을 가리킨다.
5. 안정 버전은 Semantic Versioning 형식인 `v1.0.0`, `v1.0.1` 등으로 식별한다.
6. 다른 사이트는 필요할 때 `zalnas-board`를 확인하고 원하는 안정 버전을 PULL한다.
7. 다른 사이트의 다운로드 자동화, 테스트 적용, 운영 반영과 배포 정책은 이 작업 범위에 포함하지 않는다.
8. 공용 스킨은 `_def`, `basic`, `note`, `webzine` 네 종류만 포함한다.
9. 현재 지원하는 실행환경은 Apache `2.4.68`, PHP `8.5.9`, MariaDB `12.3.3`으로 제한한다.
## 3. 용어와 저장소 역할
### 3.1 `ShinChoongSoo/zalnas`
- 게시판을 포함한 ZALNAS 전체 사이트의 개발 원본이다.
- 공용 게시판 변경은 `modules/board`에서 관리한다.
- ZALNAS 사이트 기능과 디자인을 포함한 전체 회귀 검증을 수행한다.
- 게시 도구와 공용 파일 선별 정책을 소유한다.
### 3.2 `ShinChoongSoo/zalnas-board`
- 검증된 공용 게시판 소스만 보관하는 비공개 배포용 저장소다.
- 다른 사이트에 접속하거나 파일을 직접 적용하지 않는다.
- `main`, 버전 tag, release, manifest와 checksum을 제공한다.
- ZALNAS 전용 기능, 비밀정보, 업로드와 운영 데이터는 보관하지 않는다.
### 3.3 소비 사이트
- `zalnas-board`의 최신 안정 버전을 확인하고 가져오는 주체다.
- 자신의 테스트·운영 반영 절차와 rollback 정책을 독립적으로 관리한다.
- 소비 사이트 이름, 저장소 주소와 서버 정보는 이번 작업에 필요하지 않으며 관리 대상에도 포함하지 않는다.
## 4. 현재 조사 결과
현재 `modules/board`에는 컨트롤러, 모델, 서비스, 화면, 스킨, 자산, 설정 예제와 migration이 모여 있다. 다만 그대로 독립 저장소에 복사해서는 안 된다.
- `app/Controllers/BoardController.php`, `app/Models/BoardModel.php`, `app/Services/Board*.php`와 `modules/board`의 원본이 중복되어 있다.
- 현재 Harness는 실행 경로와 모듈 원본의 checksum 일치를 검사한다.
- `app/Views/board`와 `public/assets/board`는 로컬에서 `modules/board`를 가리키는 junction이다.
- 게시판은 ZALNAS의 Router, Controller 기반 클래스, 회원·권한, Admin/Auth, 사이트 설정과 DB 계약에 의존한다.
- `modules/board`에는 공용 대상이 아닌 스킨, ZALNAS 전용 배너, 과거 NAS 경로를 사용하는 스크립트·문서가 섞여 있다.
- 기존 ZIP/API 기반 사이트 모듈 기능은 존재하지만 별도 GitHub 저장소 게시 기능은 구현돼 있지 않다.
초기 구축에서는 실행 구조를 한꺼번에 재작성하지 않는다. `modules/board`를 게시 원본으로 강제하고 기존 checksum 일치 검사를 유지해 현재 게시판 기능과 UI에 미치는 영향을 최소화한다. 중복 실행 파일 제거와 namespace 재구성은 별도 후속 작업으로 다룬다.
## 5. 공용 저장소 포함 범위
### 5.1 포함
- `modules/board/controllers/BoardController.php`
- `modules/board/models/BoardModel.php`
- `modules/board/services/*.php`
- `modules/board/views/board`의 공통 화면과 partial
- `modules/board/views/board/skin/_def`
- `modules/board/views/board/skin/basic`
- `modules/board/views/board/skin/note`
- `modules/board/views/board/skin/webzine`
- `modules/board/assets/board`의 공통 JavaScript, CSS와 이미지
- `modules/board/assets/board/skin/_def`
- `modules/board/assets/board/skin/basic`
- `modules/board/assets/board/skin/note`
- `modules/board/assets/board/skin/webzine`
- `modules/board/assets/board/banner/default`
- `modules/board/config/config.example.php`
- `modules/board/migrations`
- 독립 저장소용 `README.md`, `VERSION`, `manifest.json`, `checksums.sha256`
- 독립 저장소 자체 검증을 위한 최소 CI와 contract test
### 5.2 제외
- `avlist`, `bookmark`, `daily`, `joinsite`, `letter`, `script` 스킨과 해당 자산
- `banner/default` 이외의 ZALNAS 전용 배너
- `modules/board/adapters`의 사이트별 adapter
- 과거 NAS 경로와 특정 서버 구성을 전제로 한 `modules/board/deploy`, `modules/board/scripts`, 일회성 `modules/board/docs`
- ZALNAS 전용 관리자 화면과 배포 설정
- `.env*`, `config/*.local.php`, 자격증명, token, 인증서 개인키
- 업로드, 첨부파일, cache, log, backup, DB dump와 운영 데이터
- `app`의 Board 실행 복사본
- 소비 사이트의 updater, workflow와 환경 설정
### 5.3 파일 선별 정책
- 게시 정책에는 허용 경로, 허용 확장자와 제외 경로를 명시한다.
- 게시 시 실제 포함 파일의 정확한 목록과 SHA-256 checksum을 생성한다.
- symlink, junction, reparse point와 저장소 밖으로 나가는 경로는 거부한다.
- 허용되지 않은 PHP 실행 파일, local 설정과 비밀정보 패턴이 발견되면 게시를 중단한다.
- 새 스킨은 명시적으로 공용 범위에 승인되기 전까지 자동으로 포함하지 않는다.
## 6. 독립 저장소 구조
`zalnas-board`의 저장소 루트는 소비 사이트의 `modules/board`에 대응하도록 구성한다.
```text
zalnas-board/
├─ controllers/
├─ models/
├─ services/
├─ views/board/
│ └─ skin/
│ ├─ _def/
│ ├─ basic/
│ ├─ note/
│ └─ webzine/
├─ assets/board/
│ ├─ skin/
│ │ ├─ _def/
│ │ ├─ basic/
│ │ ├─ note/
│ │ └─ webzine/
│ └─ banner/default/
├─ config/
├─ migrations/
├─ tests/
├─ .github/workflows/verify.yml
├─ manifest.json
├─ checksums.sha256
├─ VERSION
└─ README.md
```
독립 저장소의 정책 파일과 CI는 저장소 자체가 소유한다. ZALNAS의 게시 작업은 manifest에 등록된 공용 소스만 갱신하며 저장소 정책 파일을 임의로 덮어쓰지 않는다.
## 7. 버전과 릴리스 정책
- `zalnas-board/main`에는 검증되지 않은 중간 상태를 올리지 않는다.
- 첫 안정 기준은 `v1.0.0`으로 발행한다.
- 버그 수정은 patch, 호환 기능 추가는 minor, 호환되지 않는 변경은 major 버전을 올린다.
- 버전 tag는 생성 후 이동하거나 덮어쓰지 않는다.
- 잘못된 릴리스는 tag를 재사용하지 않고 원인을 수정한 새 patch 버전을 발행한다.
- `manifest.json`에는 최소한 다음 값을 기록한다.
- 모듈명과 버전
- 원본 ZALNAS commit
- 지원 Apache/PHP/MariaDB 버전
- 의존 기능과 필수 PHP extension
- 포함 파일과 제거 파일
- migration 목록과 적용 순서
- 전체 checksum 파일의 hash
- `checksums.sha256`에는 배포 파일별 SHA-256을 기록한다.
- GitHub release 설명에는 변경 요약, 호환성, migration 유무와 알려진 주의사항을 기록한다.
## 8. 게시 흐름
```text
ZALNAS main의 게시판 변경
→ ZALNAS 전체 검증
→ 버전과 변경 내역 확정
→ 공용 파일만 임시 staging에 export
→ 제외 항목·비밀정보·checksum 검사
→ zalnas-board 자체 검증
→ zalnas-board/main 갱신
→ 같은 commit에 vX.Y.Z tag와 release 생성
→ 원격 manifest·tag·checksum 재확인
```
이 흐름은 `zalnas-board`까지 소스를 게시하는 것으로 종료된다. 이후 다른 사이트에서 수행하는 PULL, 테스트와 운영 반영은 호출하거나 기다리지 않는다.
## 9. 구현 단계
### Phase 0. 기준선 확정과 충돌 방지
- [ ] 현재 `main`과 작업 트리가 깨끗한지 확인한다.
- [ ] `modules/board`와 `app` 실행 복사본의 checksum 일치를 확인한다.
- [ ] 공유 스킨 4종과 제외 스킨 6종의 파일 목록을 고정한다.
- [ ] 게시판 공통 의존성, DB 요구사항과 PHP extension을 목록화한다.
- [ ] 현재 게시판 Harness와 주요 URL smoke 기준을 기록한다.
완료 조건: 게시 전후 비교에 사용할 파일 목록, checksum과 회귀 검사 기준이 확보된다.
### Phase 1. 공용 파일 manifest 구축
- [ ] 공용 경로와 제외 경로를 선언하는 source manifest를 추가한다.
- [ ] 허용 확장자, symlink·reparse point 거부와 경로 이탈 방지를 구현한다.
- [ ] `_def`, `basic`, `note`, `webzine`만 허용하는 검사를 추가한다.
- [ ] `banner/default` 이외의 배너가 포함되면 실패하게 한다.
- [ ] ZALNAS local 설정, secret, upload와 운영 데이터 탐지 검사를 추가한다.
- [ ] 실제 파일 목록과 checksum을 결정론적으로 생성한다.
완료 조건: 같은 ZALNAS commit에서 반복 export했을 때 동일한 manifest와 checksum이 생성되고 제외 파일이 하나도 포함되지 않는다.
### Phase 2. 게시 export 도구 구현
- [ ] 저장소 밖 임시 경로에만 쓰는 dry-run/export 명령을 구현한다.
- [ ] 추가·수정·삭제될 공용 파일 목록을 먼저 표시한다.
- [ ] 독립 저장소의 정책 파일과 `.git`을 export 대상에서 제외한다.
- [ ] export가 실패하면 staging을 폐기하고 대상 저장소는 변경하지 않는다.
- [ ] ZALNAS 원본 파일을 export 과정에서 변경하지 않는 검사를 추가한다.
예정 인터페이스:
```powershell
php scripts/export_board_repository.php --version=1.0.0 --dry-run
php scripts/export_board_repository.php --version=1.0.0 --target=<임시-checkout-경로>
```
완료 조건: 임시 checkout에 공용 파일만 정확히 동기화되고 제거된 관리 파일도 manifest 기준으로 식별된다.
### Phase 3. 회귀·보안 검증 추가
- [ ] source manifest 허용·제외 경계 테스트를 추가한다.
- [ ] 네 공용 스킨의 view/asset 누락 검사를 추가한다.
- [ ] 제외 스킨과 ZALNAS 배너 혼입 실패 테스트를 추가한다.
- [ ] 민감정보 scanner와 redaction 테스트를 추가한다.
- [ ] PHP `8.5.9` 문법 검사를 수행한다.
- [ ] MariaDB `12.3.3` 기준 schema와 migration dry-run 검사를 추가한다.
- [ ] 기존 Board controller/model/service/view 테스트를 실행한다.
- [ ] ZALNAS 전체 Harness를 실행한다.
완료 조건: 의도적인 금지 fixture에서는 실패하고 정상 export와 기존 ZALNAS 게시판 회귀 검사는 통과한다.
### Phase 4. `zalnas-board` 비공개 저장소 준비
- [ ] 원격 저장소가 존재하는지 확인하고 없으면 비공개로 생성한다.
- [ ] 기본 branch를 `main`으로 설정한다.
- [ ] 강제 push와 tag 덮어쓰기를 금지하는 보호 정책을 설정한다.
- [ ] 독립 저장소용 README, ignore, manifest schema와 검증 workflow를 추가한다.
- [ ] ZALNAS Actions가 해당 저장소에만 쓸 수 있는 최소 권한 인증을 설정한다.
- [ ] token과 key가 log, artifact와 manifest에 노출되지 않는지 확인한다.
이 단계는 GitHub 외부 상태와 인증을 변경하므로 실제 구현 시 저장소 생성·보호 정책·secret 등록 직전에 사용자 승인을 받는다.
완료 조건: 빈 기준 저장소의 `main`과 필수 검증 정책이 준비되고 최소 권한 게시 인증이 동작한다.
### Phase 5. ZALNAS → `zalnas-board` 게시 workflow 구현
- [ ] ZALNAS `main`의 특정 commit과 새 버전을 입력받는 수동 게시 workflow를 추가한다.
- [ ] 입력 버전이 기존 최신 버전보다 높은지 검사한다.
- [ ] 대상 commit이 ZALNAS `main` 이력에 포함됐는지 검사한다.
- [ ] Phase 1~3의 export와 검증을 모두 통과해야 원격 push를 허용한다.
- [ ] `zalnas-board/main` 갱신과 `vX.Y.Z` tag를 같은 검증 commit에 게시한다.
- [ ] release를 생성하고 manifest와 checksum을 첨부한다.
- [ ] 게시 후 원격 tag, commit, manifest와 checksum을 다시 비교한다.
- [ ] 실패 시 불완전한 release를 만들지 않고 원인만 보고한다.
완료 조건: 소비 사이트에 접속하거나 변경하지 않고 `zalnas-board`에 검증된 단일 안정 버전만 게시된다.
### Phase 6. 최초 `v1.0.0` 사전 연습과 발행
- [ ] 원격 push 없이 dry-run 결과를 검토한다.
- [ ] 포함·제외 파일 목록과 ZALNAS commit을 사용자에게 보고한다.
- [ ] 임시 저장소에서 `verify.yml`을 실행한다.
- [ ] 실제 최초 게시 직전에 외부 저장소 변경 범위를 다시 확인한다.
- [ ] `zalnas-board/main`과 `v1.0.0`을 게시한다.
- [ ] GitHub에서 manifest, tag, release와 checksum을 읽기 전용으로 재검증한다.
완료 조건: `zalnas-board/main`과 `v1.0.0`이 동일한 검증 commit을 가리키며 공용 스킨 4종만 포함한다.
### Phase 7. 현재 기준 문서 정리
- [ ] `docs/site-module-plan.md`에 GitHub 독립 저장소 게시 방식을 현재 표준으로 반영한다.
- [ ] 기존 ZIP/API 배포 기능은 legacy-compatible 상태로 구분한다.
- [ ] `modules/board/README.md`에 개발 원본과 게시 범위를 기록한다.
- [ ] `docs/operations.md`에 버전 발행과 문제 릴리스 대응 절차를 연결한다.
- [ ] 과거 NAS 경로를 현재 게시 기준으로 오해하지 않도록 명확히 표시한다.
완료 조건: 현재 표준, 과거 호환 기능과 실제 운영 절차가 서로 충돌하지 않는다.
## 10. 파일 변경 예상 범위
구체적인 파일명은 구현 착수 시 현재 구조를 다시 확인해 확정한다.
| 구분 | 예상 작업 |
| --- | --- |
| `config` 또는 module distribution manifest | 공용 파일 범위와 호환성 계약 |
| `scripts` | export, manifest, checksum, secret scan 도구 |
| `scripts/tests` | 포함·제외·결정론·보안·버전 테스트 |
| `.github/workflows` | `zalnas-board` 게시 workflow |
| `modules/board` | 배포 가능한 설정 예제와 migration 정리 |
| `docs` | 현재 게시 표준과 운영 절차 갱신 |
| `zalnas-board` | 저장소 정책, CI, 공용 소스, version과 release |
초기 단계에서는 기존 게시판 화면, 라우트와 DB 데이터를 직접 변경하지 않는다. 게시 범위를 만들기 위해 실행 구조 변경이 필요하다고 확인되면 별도 단계와 rollback 계획을 먼저 추가한다.
## 11. 검증 명령 기준
로컬 ZALNAS에서는 다음 검증을 기본으로 한다.
```powershell
scripts\harness.cmd preflight
php scripts/export_board_repository.php --version=1.0.0 --dry-run
scripts\harness.cmd verify
```
독립 저장소에서는 다음 항목을 검증한다.
- manifest schema와 VERSION 일치
- 모든 배포 파일 checksum 일치
- PHP 문법 검사
- 허용된 네 스킨만 존재
- 금지 파일·민감정보·local path 부재
- Apache `2.4.68`, PHP `8.5.9`, MariaDB `12.3.3` 호환성 계약
실제 명령은 구현된 CLI와 CI 이름에 맞춰 문서에 갱신한다.
## 12. 위험과 대응
| 위험 | 대응 |
| --- | --- |
| ZALNAS 전용 파일 혼입 | 허용 경로, 제외 경로, extension, secret scanner를 모두 통과해야 게시 |
| `app` 복사본과 `modules/board` 불일치 | 기존 checksum 회귀 검사를 게시 전 필수 단계로 유지 |
| 공용 스킨 누락 | 네 스킨의 view/asset 쌍과 필수 파일 contract 검사 |
| 잘못된 버전 덮어쓰기 | tag 불변, 버전 증가 검사, force push 금지 |
| 부분 push로 `main`과 tag 불일치 | 검증 commit을 확정한 뒤 같은 commit을 push하고 원격 재확인 |
| 비공개 저장소 인증 유출 | 대상 저장소 한정 최소 권한, GitHub secret 사용, 값 redaction |
| 기존 게시판 기능·UI 손상 | 초기에는 실행 구조를 유지하고 export 기능만 추가, 전체 Harness 통과 후 게시 |
| 구형 환경 요구 발생 | 현재 릴리스는 2.4.68/8.5.9/12.3.3만 지원하고 향후 별도 호환성 계획 수립 |
## 13. Rollback 원칙
- ZALNAS의 게시 작업은 원본 소스를 수정하지 않고 임시 checkout에서 수행한다.
- 게시 실패 시 `zalnas-board`에 불완전한 tag나 release를 남기지 않는다.
- 이미 공개된 안정 tag는 삭제·이동하지 않는다.
- 문제가 발견된 릴리스는 `zalnas-board/main`에서 검증된 이전 상태로 되돌린 뒤 새 patch 버전으로 수정한다.
- GitHub 저장소 변경 전후 commit과 tag를 기록해 언제든 이전 안정 버전을 식별할 수 있게 한다.
## 14. 범위 밖 작업
다음 항목은 이번 계획에서 구현하거나 실행하지 않는다.
- 다른 사이트 이름과 저장소 목록 관리
- 다른 사이트 저장소의 remote, submodule 또는 subtree 설정
- 다른 사이트의 업데이트 확인 프로그램
- 다른 사이트의 테스트·운영 서버 파일 변경
- 다른 사이트의 자동 다운로드, 자동 적용 또는 배포 workflow
- 소비 사이트 DB migration 실행
- 공용 게시판을 Composer package로 전면 재작성
- Board 클래스 namespace와 ZALNAS MVC를 한 번에 분리하는 대규모 리팩터링
- 기존 ZIP/API 사이트 모듈 기능의 즉시 삭제
## 15. 진행 기록 규칙
- 작업 시작 시 이 문서의 상태와 해당 Phase를 갱신한다.
- 완료된 체크 항목은 `~~항목~~ (완료: YYYY-MM-DD HH:mm KST)` 형식으로 기록한다.
- 구현 중 확인된 범위 변경, 중요한 결정과 막힌 점은 해당 Phase 아래에 남긴다.
- GitHub 저장소 생성, 인증 등록과 최초 release 발행은 실행 직전 승인 여부를 기록한다.
- 모든 Phase 완료 후 유효한 운영 기준은 현재형 문서로 옮기고 이 계획은 Git 이력으로 보존한다.
## 16. 최종 완료 조건
다음 조건을 모두 만족해야 작업 완료로 판단한다.
1. `ShinChoongSoo/zalnas-board`가 별도 비공개 저장소로 존재한다.
2. `main`이 최신 안정 버전 tag와 같은 commit을 가리킨다.
3. ZALNAS의 지정된 공용 게시판 파일만 포함한다.
4. `_def`, `basic`, `note`, `webzine` 외 스킨과 ZALNAS 전용 배너가 없다.
5. `VERSION`, manifest, checksum과 Git tag가 서로 일치한다.
6. Apache `2.4.68`, PHP `8.5.9`, MariaDB `12.3.3` 기준 검증을 통과한다.
7. 비밀정보, local 설정, 업로드와 운영 데이터가 없다.
8. ZALNAS 기존 게시판 기능과 UI 회귀 검사를 통과한다.
9. 다른 사이트에는 어떠한 변경도 수행하지 않는다.
10. 게시와 rollback 절차가 현재 기준 문서에 반영된다.


