이 문서는 NEW Web Configurator의 버전 관리 정책, 릴리즈 절차, 배포 워크플로우, 검증, 롤백, 디바이스 아키텍처, 트러블슈팅을 다룹니다. 신규 버전을 디바이스에서 실행하는 방법에 대한 단일 참조 문서입니다.
---
## 1. 개요
NEW Web Configurator는 여러 현장의 IoT 디바이스에 배포되는 Python stdlib HTTP 서버입니다. 배포는 `scripts/deploy.ps1` — PowerShell 스크립트를 사용합니다. 이 스크립트는 커밋된 `src/` 트리를 아카이브로 묶어 SSH로 전송하고, 디바이스 상의 기존 트리를 백업 및 교체한 뒤, 배포된 버전을 기록하고 systemd 서비스를 재시작합니다. 모든 프로덕션 배포는 반드시 `vMAJOR.MINOR.PATCH` 릴리즈 태그와 연결되어야 하므로, 어떤 디바이스에서든 실행 중인 정확한 버전을 언제나 식별할 수 있습니다.
---
## 2. 버전 관리 정책
### 2.1 버전 체계
세 부분으로 구성된 시맨틱 버전 관리: **`vMAJOR.MINOR.PATCH`**.
| 구분 | 올려야 하는 경우 |
|---|---|
| **MAJOR** | 하위 호환이 깨지는 변경 — 예: 기존 디바이스를 망가뜨리는 `board_config` 스키마 변경, 또는 API 삭제/이름 변경. |
| **MINOR** | 새로운 하위 호환 기능 추가 — 예: 새 설정 페이지, 랜딩 대시보드, WiFi 국가 코드 드롭다운. |
| **PATCH** | 신규 기능 없는 버그 수정 또는 소규모 수정 — 예: `db_manager` 재시도 수정, 표시 버그 수정. |
### 2.2 단일 진실 소스(Single Source of Truth)
`src/static/js/constants.js`의 `APP_VERSION`이 애플리케이션 버전의 단일 진실 소스입니다. 사이드바에 표시되며, 릴리즈 시점에 git 태그와 반드시 일치해야 합니다.
---
## 3. 릴리즈 절차
새 릴리즈를 만들려면:
1.**버전 올림 결정** — 변경 내용이 MAJOR, MINOR, PATCH 중 어느 것에 해당하는지 결정합니다(§2.1 표 참조).
2.**`APP_VERSION` 업데이트** — `src/static/js/constants.js`에서 `APP_VERSION`을 새 버전 문자열로 설정합니다(예: `'v1.0.1'`).
3.**`CHANGELOG.md` 항목 추가** — `CHANGELOG.md` 상단에 `## [vX.Y.Z] — YYYY-MM-DD` 형식으로 변경 내용을 기술하는 섹션을 추가합니다.
4.**커밋** — `constants.js`와 `CHANGELOG.md`를 함께 커밋합니다.
5.**태그** — annotated 태그를 생성합니다:
```
git tag -a vX.Y.Z -m "Release vX.Y.Z — <one-linesummary>"
```
태그는 릴리즈 시점에만 생성합니다. 개발 중간에 태그를 붙이지 마십시오. 태그는 배포되는 커밋을 정확히 표시합니다.
---
## 4. 배포
### 4.1 사전 요구사항
- 다음이 갖춰진 **Windows 개발 호스트**:
- OpenSSH `ssh` 및 `scp` (Windows 10/11 기본 포함 또는 Git for Windows).
-`PATH`에 등록된 `git`.
-`root@<device>`에 인증된 SSH 키.
- **클린(clean)** 상태의 로컬 워킹 트리 (`git status`에 변경 사항 없음).
- **`vX.Y.Z` 태그에 위치한 HEAD** (플래그 처리된 개발 빌드에는 `-AllowUntagged` 사용).
### 4.2 사용법
```powershell
.\scripts\deploy.ps1 <device-ip>
.\scripts\deploy.ps1 <device-ip> -AllowUntagged
.\scripts\deploy.ps1 <device-ip> -Rollback
```
예시:
```powershell
.\scripts\deploy.ps1 192.168.55.56
.\scripts\deploy.ps1 192.168.55.56 -AllowUntagged
```
### 4.3 배포 흐름 (스크립트 동작 상세)
1.**사전 점검(Pre-flight)** — SSH 도달 가능 여부 확인(`ssh … echo ok`), 로컬 워킹 트리가 클린 상태인지 확인, HEAD가 버전 태그에 위치하는지 확인(버전 태그가 아니면 중단, `-AllowUntagged`가 전달된 경우 제외).
3.**전송** — `scp`로 임시 tar 파일을 디바이스로 복사하고, 디바이스에서 스테이징 디렉터리에 압축 해제.
4.**백업** — 현재 디바이스의 `src/`를 `backups/src-<timestamp>/`로 복사하고, 최근 3개 백업만 유지하도록 정리.
5.**systemd 유닛 및 헬퍼 스크립트 설치** (멱등성 보장 — 해시 비교 후 변경된 경우에만 재복사; src 교체 이전에 수행하므로 실패 시 기존 src가 활성 상태를 유지). 모든 유닛은 영구 경로 `/lib/systemd/system/`에 설치됩니다(`/etc/systemd/system/` 오버레이는 tmpfs라 재부팅 시 초기화됩니다). 설치 항목:
-`web-configurator.service` (메인 유닛; 첫 배포 시 `systemctl enable`도 수행).
-`dpworld-net-recover.service` — 네트워크 적용 엔진이 사용하는 `modprobe` 전용 복구 유닛.
-`dpworld-network-apply.service.d/10-ondemand.conf` — 펌웨어 적용 유닛의 부팅 전용 `Requires`를 온디맨드로 재정의하는 드롭인.
- **펌웨어 부팅 하드닝(v1.7.1+)**: 하드닝된 부팅 스크립트 `deploy/dpworld-network-apply-hardened.sh` → `/usr/bin/`으로 복사, 그리고 두 개의 드롭인(`dpworld-network-apply.service.d/20-hardened.conf` 및 `dpworld-network-seed.service.d/20-hardened.conf`)이 펌웨어 seed/apply 유닛을 하드닝된 스크립트로 연결(라이브 `modprobe -r` 없음 — 국가 코드는 재부팅 시 적용됨). 펌웨어 원본 `/usr/bin/dpworld-network-apply.sh`는 수정하지 않습니다. [firmware-boot-hardening.md](firmware-boot-hardening.md) 참조.
- **Wi-Fi AP 유닛**: `deploy/dpworld-ap-apply.sh` → `/usr/bin/`으로 복사, 그리고 네 개의 유닛(`dpworld-ap-seed`, `dpworld-ap-apply`, `dpworld-hostapd-ap0`, `dpworld-udhcpd-ap0`). `dpworld-ap-seed.service`만 enable하며, 나머지는 apply 스크립트에서 온디맨드로 시작됩니다.
- 엔진/OTA 디렉터리도 사전 생성합니다(`/opt/fw_staging`, `/opt/fw_upload`, `/opt/config_backups`, `/home/root/network`, `/opt/config_backups/network`).
6.**교체(Swap)** — 디바이스의 `src/`를 새로 압축 해제된 트리로 교체(`rm -rf src && mv <staging>/src src`); 스테이징 tar와 디렉터리를 삭제.
7.**버전 기록** — `DEPLOYED_VERSION`을 기록하고 `deploy-history.log`에 한 줄을 추가합니다(§5 참조).
9.**검증** — 최대 약 15초 동안 폴링: `systemctl is-active web-configurator`가 `active`인지, `:9090`으로 HTTP 요청 시 200이 반환되는지 확인합니다. 성공 또는 실패를 보고합니다. 재시작/검증이 실패하면 스크립트가 4단계에서 생성한 `backups/src-<timestamp>/`로 자동 롤백합니다.
> **참고:** HEAD가 정확히 `vX.Y.Z` 태그에 있지 않으면 배포가 거부됩니다. 미태그 커밋을 플래그 처리된 개발 빌드로 배포하려면 `-AllowUntagged`를 전달하십시오(버전은 `vX.Y.Z-dev+<short-sha>`로 기록됩니다).
이 스크립트는 nginx, Java app-runner, SQLite 데이터베이스, dpworldapp 바이너리를 **절대 건드리지 않습니다**. 단, 5단계에 나열된 systemd 유닛과 헬퍼 스크립트를 설치/갱신하며(멱등성 보장, 해시가 다를 경우에만), 펌웨어 소유의 `/usr/bin/dpworld-network-apply.sh`는 수정하지 않습니다 — 하드닝은 원본을 수정하지 않고 드롭인을 통해 적용됩니다.
각 줄의 형식: `<timestamp> <version> <commit-short> <deployed_by>`.
---
## 6. 롤백
디바이스의 가장 최근 백업으로 복원하려면:
```powershell
.\scripts\deploy.ps1 <ip> -Rollback
```
이 명령은 가장 최근의 `backups/src-*` 스냅샷을 `src/`에 복원하고, 서비스를 재시작하여 정상 여부를 검증한 뒤, 롤백이 발생했음을 기록하도록 `DEPLOYED_VERSION`을 다시 기록합니다(복원된 백업 타임스탬프 포함). 백업 스냅샷은 롤백 후에도 삭제되지 않습니다.