앞선 감사 네 번은 "이 코드가 제대로 도는가"를 물었다. 이번엔 작업자·노조·안전관리자·
공장장·설치기사 5인 관점으로 3단논법을 세우고 전제를 코드로 확인한 뒤 반문으로 깼다.
5인 중 2인의 논증이 완전히, 2인이 부분적으로 무너졌다.
읽기 감사 — 이 시스템은 쓰기를 전부 감사했다. 임계값 한 번 바꾼 것까지 해시 체인에
들어간다. 그런데 작업자 사진을 열어보는 것은 하나도 남지 않았다. 감시 시스템과 안전
시스템을 가르는 선이 정확히 거기라서, 나머지 프라이버시 설계(RAM 링버퍼 7.5초·전송 전
얼굴 블러·라이브뷰 부재)가 전부 이 한 줄이 없어서 검증 불가능한 주장에 머물러 있었다.
거부된 열람은 기록하지 않는다 — 그러면 감사 로그가 "본 사람"이 아니라 "요청한 사람"
목록이 되어 정작 누가 봤는지를 묻는 데 못 쓴다.
크론 생존 — 죽은 노드는 스스로 신고할 수 없어서 크론이 하트비트의 부재로 판정한다.
그런데 그 크론이 멈추면 아무도 판정하지 않는다. 통보·5분 확대·장치 이상 발행이 전부 그
한 프로세스에 걸려 있어서, 멈춘 순간부터 화면은 조용해지고 그 침묵은 "사고 없음"과
구별되지 않는다. 감시자의 생존은 감시자가 아닌 쪽이 읽어야 한다.
증거 보존 — 스냅샷 삭제가 나이 하나만 봤다. 기본 7일인데 중대재해 조사는 몇 주에서 몇
달이라, 우리가 내건 목적("사고 후 원인 분석")이 8일째에 스스로 사라졌다. 확인 처리로
자동 연장하지 않는 이유는 자동이면 사실상 전량 보존이 되어 7일 약속이 무너지기 때문이다.
증거 정책 — 프레임이 ergo_report 하나만 빼고 모든 이벤트에 붙었다. 체류 상한 초과도
포함이라, 밀폐공간 프리셋 기본값(30분) 기준으로 정상 작업자가 규정대로 일하는 동안
주기적으로 본인 사진이 서버로 갔다. 위험해서가 아니라 오래 있어서. 기준을 명문화했다:
시간 경과 자체가 조건인 이벤트에는 사진을 붙이지 않는다.
마이그레이션 2026-09-25-evidence-hold-and-view-audit.sql (README-UPLOAD 10번).
검증: phpunit 302 passed(신규 18, 스킵 0) · pytest(신규 18)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EBjuR7DW6PNNMows4KnbYU
240 lines
10 KiB
Markdown
240 lines
10 KiB
Markdown
# 카페24 업로드 안내
|
|
|
|
이 폴더의 **내용물**을 카페24 FTP **최상위**에 그대로 올리면 됩니다.
|
|
고칠 파일은 **`config.php` 하나뿐**입니다.
|
|
|
|
---
|
|
|
|
## 1. 올리는 위치 (제일 중요)
|
|
|
|
**폴더 2개만** 올리면 됩니다. `move_detect_app` 과 `www`.
|
|
|
|
```
|
|
/ <- FTP 최상위
|
|
├── (기존 사이트 파일) <- 건드리지 않습니다
|
|
├── move_detect_app/ <- [올리기 1] 통째로. 웹에서 안 보입니다
|
|
│ ├── config.php 여기만 고칩니다
|
|
│ ├── src/
|
|
│ ├── vendor/
|
|
│ ├── storage/snapshots/ 사진 저장
|
|
│ └── schema.sql DB 넣고 나면 지웁니다
|
|
└── www/ <- 원래 있던 폴더 (다른 사이트가 씀)
|
|
├── (기존 사이트 파일) <- 건드리지 않습니다
|
|
└── move_detect/ <- [올리기 2] 이 폴더만 새로 생깁니다
|
|
├── index.php
|
|
├── .htaccess
|
|
├── _preflight.php
|
|
└── assets/
|
|
```
|
|
|
|
접속 주소는 **`https://내도메인/move_detect/`** 입니다.
|
|
|
|
`README-UPLOAD.md`(이 파일)는 올릴 필요 없습니다.
|
|
|
|
**왜 `move_detect_app` 을 `www` 밖에 두나:** `config.php` 에 DB 비밀번호가, `storage/snapshots/`
|
|
에 작업자 사진이 들어갑니다. `www` 안에 있으면 같은 `www` 를 쓰는 다른 사이트 주소로 접근될
|
|
수 있습니다. 한 폴더로 묶은 건 최상위에 있는 남의 파일(`vendor/`, `config.php` 같은 흔한
|
|
이름)과 겹쳐 덮어쓰는 사고를 막기 위해서입니다.
|
|
|
|
업로드가 끝나면 FTP 클라이언트에서 권한을 지정하세요. zip 과 FTP 는 유닉스 권한을
|
|
옮기지 못하므로, 패키지에 넣어둔 권한은 올리는 순간 사라집니다.
|
|
|
|
| 대상 | 권한 |
|
|
|---|---|
|
|
| `move_detect_app/config.php` | **600** (DB 비밀번호가 들어있습니다) |
|
|
| `move_detect_app/storage/snapshots/` | 750 |
|
|
|
|
`_preflight.php` 가 실제 권한과 노출 여부를 확인해줍니다.
|
|
|
|
---
|
|
|
|
## 2. `config.php` 고치기
|
|
|
|
DB 세 줄만 카페24 관리자 페이지의 MySQL 정보로 바꿉니다.
|
|
|
|
```php
|
|
'db_dsn' => 'mysql:host=localhost;dbname=여기에_DB이름;charset=utf8mb4',
|
|
'db_user' => '여기에_MySQL아이디',
|
|
'db_pass' => '여기에_MySQL비밀번호',
|
|
```
|
|
|
|
나머지 값은 그대로 두면 됩니다.
|
|
비밀번호는 메일/메신저/깃에 올리지 마세요.
|
|
|
|
---
|
|
|
|
## 3. DB 만들기
|
|
|
|
### 처음 설치(빈 DB)
|
|
|
|
1. 카페24 관리자 → **phpMyAdmin** 열기
|
|
2. 이 앱용 DB 선택
|
|
3. **Import(가져오기)** 탭 → `schema.sql` 선택 → 실행
|
|
4. 표 11개가 생겼는지 확인
|
|
5. **서버에서 `schema.sql` 삭제** — 남겨둘 이유가 없습니다
|
|
|
|
`schema.sql` 은 항상 최신 상태입니다. 처음 설치라면 아래 마이그레이션은 **실행하지 마세요**
|
|
(이미 반영되어 있어 충돌합니다).
|
|
|
|
### 이미 쓰던 DB 업그레이드(기존 데이터 있음)
|
|
|
|
> ⚠️ 기존 DB에 `schema.sql` 을 다시 Import 하면 `CREATE TABLE` 충돌로 실패합니다.
|
|
> 대신 **마이그레이션**을 실행하세요.
|
|
|
|
**반드시 아래 순서대로, 아직 적용하지 않은 것부터 전부 실행합니다.** 하나라도 건너뛰면
|
|
그 컬럼·표를 쓰는 요청이 전부 500 이 됩니다. 순서가 중요한 이유는 뒤 파일이 앞 파일이 만든
|
|
표 이름(`move_` 접두사)을 전제하기 때문입니다.
|
|
|
|
| 순서 | 파일 | 무엇이 바뀌나 |
|
|
|---|---|---|
|
|
| 1 | `2026-07-17-p1-9-p3-2b.sql` | site / event_uuid 컬럼 + 인덱스, 기존 이벤트 site 백필 |
|
|
| 2 | `2026-07-20-move-table-prefix.sql` | 모든 표 이름에 `move_` 접두사 부여 (**이 단계를 건너뛰면 이후 전부 실패**) |
|
|
| 3 | `2026-08-06-zone-rules.sql` | 존 점유 규칙(`rules_json`) |
|
|
| 4 | `2026-08-06-heatmap-spatial.sql` | 히트맵 좌표(`spatial_x`, `spatial_y`) |
|
|
| 5 | `2026-08-06-false-positive-tag.sql` | 오탐 태그 컬럼 |
|
|
| 6 | `2026-08-07-improvement-actions.sql` | 개선 조치 표(`move_actions`) |
|
|
| 7 | `2026-08-08-actions-site.sql` | 개선 조치의 현장(site) 스코프 |
|
|
| 8 | `2026-08-09-safety-platform.sql` | 통보 채널·억제·위험성평가·인수인계·성능 시계열 표, 감사로그 해시체인, 신규 감지 유형 |
|
|
| 9 | `2026-09-04-fall-from-height.sql` | 추락(`fall_from_height`)·동시 다발 쓰러짐(`multi_down`) 유형 허용 |
|
|
| 10 | `2026-09-25-evidence-hold-and-view-audit.sql` | 증거 보존 보류(`hold_until`) + 통보 크론 생존 기록(`move_app_state`) |
|
|
|
|
1. phpMyAdmin → 해당 DB → **SQL** 탭
|
|
2. 위 표의 순서대로 파일 내용을 **하나씩** 붙여넣고 실행합니다
|
|
(각 파일 첫머리에 적용 조건이 주석으로 적혀 있습니다)
|
|
3. 각 파일 맨 아래 **검증 쿼리**로 컬럼·표가 생겼는지 확인
|
|
4. 전부 끝난 뒤에 새 코드를 배포합니다
|
|
|
|
> 이미 적용한 마이그레이션을 다시 실행하면 "Duplicate column" 같은 오류가 납니다 —
|
|
> 재실행은 안전하지 않으니, 어디까지 적용했는지 기록해 두세요. 어디까지 했는지 모르겠으면
|
|
> phpMyAdmin 의 **Structure(구조)** 탭에서 위 표의 컬럼·표가 있는지 하나씩 확인하면 됩니다.
|
|
|
|
---
|
|
|
|
## 4. HTTPS 켜기
|
|
|
|
카페24 관리자에서 **무료 SSL 인증서**를 켭니다.
|
|
|
|
선택이 아니라 필수입니다. 로그인 세션 쿠키가 `cookie_secure=true` 라서,
|
|
HTTP 로는 브라우저가 쿠키를 아예 안 보냅니다 → 로그인이 안 됩니다.
|
|
|
|
---
|
|
|
|
## 5. 점검하기
|
|
|
|
브라우저에서 엽니다:
|
|
|
|
```
|
|
https://내도메인/move_detect/_preflight.php
|
|
```
|
|
|
|
전부 **OK** 여야 합니다. 실패한 줄에 무엇을 하면 되는지 적혀 있습니다.
|
|
|
|
특히 이 둘은 카페24 요금제에 따라 없을 수 있고, 없으면 카페24에 문의해야 합니다:
|
|
|
|
| 항목 | 없으면 생기는 일 |
|
|
|---|---|
|
|
| **Argon2id** | 로그인 페이지가 즉시 죽습니다. 제일 흔한 실패 원인입니다. |
|
|
| **gd** | 이벤트는 저장되는데 **사진만 조용히 사라집니다.** 에러도 안 납니다. |
|
|
|
|
**확인이 끝나면 `www/move_detect/_preflight.php` 를 지우세요.**
|
|
|
|
---
|
|
|
|
## 6. 관리자 계정 만들기
|
|
|
|
앱에 회원가입 기능이 없습니다. 첫 계정은 직접 넣습니다.
|
|
|
|
비밀번호 해시를 만듭니다(로컬 PC 에서 실행 — 평문이 phpMyAdmin 기록에 남지 않게):
|
|
|
|
```bash
|
|
php -r "echo password_hash('여기에_진짜_비밀번호', PASSWORD_ARGON2ID), \"\n\";"
|
|
```
|
|
|
|
phpMyAdmin → SQL 탭:
|
|
|
|
```sql
|
|
INSERT INTO move_users (username, password_hash, role, site_scope, failed_attempts, locked_until)
|
|
VALUES ('admin', '여기에_해시_붙여넣기', 'admin', NULL, 0, 0);
|
|
```
|
|
|
|
---
|
|
|
|
## 7. 라즈베리파이(장비) 등록
|
|
|
|
장비마다 비밀키를 하나씩 만들어 DB 에 넣습니다.
|
|
|
|
```bash
|
|
php -r "echo bin2hex(random_bytes(32)), \"\n\";"
|
|
```
|
|
|
|
```sql
|
|
INSERT INTO move_devices (device_id, name, secret, config_version)
|
|
VALUES ('pi-01', '3라인 낙상 카메라', '여기에_비밀키_붙여넣기', 1);
|
|
```
|
|
|
|
같은 값을 파이 쪽 `/etc/move-detect/edge.env` 의 `DEVICE_SECRET` 에 넣습니다.
|
|
`CAFE24_URL` 에는 `https://내도메인/move_detect` 를 넣습니다. (앱이 서브폴더에 있으므로
|
|
뒤의 `/move_detect` 까지 반드시 포함해야 합니다.)
|
|
|
|
비밀키는 메일/메신저로 보내지 마세요.
|
|
|
|
---
|
|
|
|
## 8. 알림 켜기 (자동 실행 등록) — **이걸 안 하면 알림이 안 갑니다**
|
|
|
|
여기까지만 하면 시스템은 **감지는 하지만 아무에게도 알리지 않습니다.** 화면을 보고 있는
|
|
사람에게만 보입니다. 새벽에 넘어진 작업자를 아무도 모른 채 아침을 맞게 됩니다.
|
|
|
|
카페24 관리자 페이지 → **자동실행관리(크론)** 에서 아래 두 개를 등록하세요.
|
|
|
|
| 주기 | 실행할 명령 | 하는 일 |
|
|
|------|-------------|---------|
|
|
| 1분마다 | `/usr/bin/php /home/<계정>/move_detect_app/tools/dispatch_alerts.php` | **장치 이상·감시 성능 저하 판정** · 알림 발송 · 미확인 경보 상위 통보 · 실패 재시도 |
|
|
| 하루 1회 (새벽) | `/usr/bin/php /home/<계정>/move_detect_app/tools/maintain.php` | 사진 보존기간 정리 · 기록 용량 관리 |
|
|
|
|
`<계정>` 자리에는 카페24 FTP 계정명을 넣습니다. 경로가 헷갈리면 FTP 로 접속했을 때
|
|
`move_detect_app` 폴더가 보이는 그 위치입니다.
|
|
|
|
### 처음 켤 때는 반드시 시험 발송부터
|
|
|
|
잘못된 수신자 목록으로 첫 발송을 하면 그 현장에서는 두 번 다시 이 기능을 켜지 않게 됩니다.
|
|
크론에 넣기 전에 아래를 한 번 실행해 **누구에게 무엇이 갈지만** 확인하세요 (아무것도 보내지
|
|
않습니다).
|
|
|
|
```bash
|
|
php /home/<계정>/move_detect_app/tools/dispatch_alerts.php --dry-run
|
|
```
|
|
|
|
### 알림 경로 설정
|
|
|
|
로그인 후 상단 **[시스템 설정]** → **통보 경로** 에서 등록합니다.
|
|
|
|
- **현장 경광등**을 가장 먼저 설정하세요. 라즈베리파이가 직접 울리므로 **인터넷이 끊겨도
|
|
동작하는 유일한 경로**입니다. 정전·회선 장애 때 오히려 위험이 높아지는 시간대에 유일하게
|
|
살아 있는 알림입니다.
|
|
- 메일·웹훅은 그 위에 얹는 보조 수단이고, 위 크론이 돌아야 나갑니다.
|
|
- `config.php` 의 `public_base_url` 이 비어 있으면 디스패처는 실행을 거부합니다 — 열리지
|
|
않는 링크가 담긴 알림을 보내는 것보다 안 보내는 편이 낫기 때문입니다.
|
|
|
|
---
|
|
|
|
## 9. 설치 점검 화면
|
|
|
|
로그인 후 **`https://내도메인/move_detect/setup`** 을 엽니다.
|
|
|
|
각 단계는 실제 상태를 조회해서 표시하므로, 체크가 켜졌다면 그 단계는 **정말로 동작하고
|
|
있는 것**입니다. 장치 등록 · 장치 통신 · 카메라 감지 · 위험구역 · 통보 경로 순서대로
|
|
확인하세요.
|
|
|
|
---
|
|
|
|
## 10. 마지막 확인
|
|
|
|
- [ ] `https://내도메인/move_detect/login` 이 열린다
|
|
- [ ] admin 으로 로그인되고 대시보드가 뜬다
|
|
- [ ] `schema.sql` 을 서버에서 지웠다
|
|
- [ ] `www/move_detect/_preflight.php` 를 서버에서 지웠다
|
|
- [ ] `https://내도메인/move_detect_app/config.php` 가 안 열린다 (열리면 위치가 잘못된 것)
|
|
- [ ] 자동실행(크론) 2개가 등록되어 있다 — **없으면 알림이 나가지 않습니다** (8번)
|
|
- [ ] `[시스템 설정] → 통보 경로` 에 최소 1개가 '사용' 상태다 (현장 경광등 우선)
|
|
- [ ] `/setup` 화면의 모든 단계에 체크가 켜져 있다 (9번)
|