233 lines
13 KiB
Markdown
233 lines
13 KiB
Markdown
# IT센터 출입자관리시스템(ACS) 워크플로우
|
|
|
|
> 문서 작성일: 2026-07-03
|
|
> 대상: `C:\ai-dev\workspace\acs` (Spring Boot 3.4.5 / Java 21 · React 19 · Vite 6)
|
|
> 목적: 방문자 사전신청 → 승인 → 출입증 발송 → 입·출입 체크 → 재실현황/리포트까지의 전체 업무 흐름 정리
|
|
|
|
---
|
|
|
|
## 1. 시스템 개요
|
|
|
|
IT센터를 방문하는 외부 방문자의 **사전신청·승인·출입·통계**를 관리하는 웹 시스템.
|
|
출입통제 하드웨어(게이트)는 `AccessControlGateway` 인터페이스로 추상화되어 있어 현재는 Mock으로 동작하고, 추후 실제 장비 연동이 가능하다.
|
|
|
|
| 구분 | 내용 |
|
|
|------|------|
|
|
| 백엔드 | Spring Boot 3.4.5 / Java 21 · Spring Security(세션) · JPA · H2(dev)/PostgreSQL(prod) · POI · ZXing · Flyway |
|
|
| 프론트엔드 | React 19 · Vite 6 · TypeScript · react-router 7 |
|
|
| 포트 | API `8080`, 웹 `5173`(dev) / nginx `80`(prod) |
|
|
| 인증 | 세션 기반, 최초 로그인 시 비밀번호 강제 변경 |
|
|
|
|
---
|
|
|
|
## 2. 역할(Role)과 접근 범위
|
|
|
|
| 역할 | 주요 권한 | 접근 화면 |
|
|
|------|-----------|-----------|
|
|
| `ADMIN` | 전체 관리, 모든 승인/반려, 사용자·블랙리스트·리포트 | 대시보드, 방문신청, 승인함, 출입콘솔, 블랙리스트, 리포트 |
|
|
| `SECURITY` | 입·출입 콘솔(체크인/아웃), 재실현황, 배지 발급, 리포트 | 대시보드, 방문신청, 출입콘솔, 리포트 |
|
|
| `HOST` | 방문 사전신청 등록, 담당 방문 확인 | 대시보드, 방문신청, 출입콘솔 |
|
|
| (비로그인) | 방문자 공개 출입증(`/pass/:token`), 키오스크(`/kiosk`) | 공개 페이지 |
|
|
|
|
> 라우팅 기준: `frontend/src/App.tsx`. `승인함(/approvals)`·`블랙리스트(/blacklist)`는 ADMIN 전용, `리포트(/reports)`는 SECURITY·ADMIN.
|
|
|
|
---
|
|
|
|
## 3. 방문 신청 생명주기 (상태 머신)
|
|
|
|
`VisitStatus` (backend/entity/VisitStatus.java) 기준 상태 전이:
|
|
|
|
```
|
|
신청 등록 승인
|
|
[DRAFT] ───────────────▶ [PENDING] ───────────────▶ [APPROVED] ──▶ 출입 가능
|
|
│ │
|
|
│ 반려 │ 방문일 경과(체크인 시도)
|
|
▼ ▼
|
|
[REJECTED] [EXPIRED]
|
|
|
|
[PENDING] 또는 [APPROVED] ──── 취소 ────▶ [CANCELLED]
|
|
```
|
|
|
|
| 상태 | 의미 | 진입 조건 |
|
|
|------|------|-----------|
|
|
| `DRAFT` | 임시 저장 | 신청 초안 |
|
|
| `PENDING` | 승인 대기 | 신청 제출 |
|
|
| `APPROVED` | 승인됨(출입 가능, qrToken 발급) | 관리자 승인 |
|
|
| `REJECTED` | 반려됨 | 관리자 반려 |
|
|
| `CANCELLED` | 신청 취소됨 | 신청자/관리자 취소 |
|
|
| `EXPIRED` | 방문일 경과로 만료 | 방문 종료일 이후 체크인 시도 시 자동 전이 |
|
|
|
|
> 핵심 규칙: **승인 시점(ApprovalService.approve)** 에 `qrToken = UUID`가 발급되고, 이 토큰으로 출입증(QR)·공개 페이지·문자 발송이 이루어진다.
|
|
|
|
---
|
|
|
|
## 4. 전체 업무 워크플로우 (End-to-End)
|
|
|
|
```
|
|
[HOST/담당자] [ADMIN] [방문자] [SECURITY/게이트]
|
|
│ │ │ │
|
|
①방문 사전신청 ───────────▶ ②승인함 검토 │ │
|
|
(개별 or 엑셀 업로드) │ │ │
|
|
│ ③승인 / 반려 │ │
|
|
│ │ (승인 시 qrToken) │ │
|
|
│ ├── ④출입증 문자발송 ─▶ 휴대폰 링크 수신 │
|
|
│ │ ⑤공개 출입증(/pass/:token) │
|
|
│ │ │ QR 확인 │
|
|
│ │ │ │
|
|
│ │ └── ⑥방문일 현장 도착 ────▶ ⑦체크인(QR/이름)
|
|
│ │ │ ├ 블랙리스트 검증
|
|
│ │ │ ├ 상태/일자 검증
|
|
│ │ │ └ 게이트 오픈 + 입장기록
|
|
│ │ ⑧재실현황 표시
|
|
│ │ └── ⑨퇴장 ───────────────▶ ⑩체크아웃(OUT 기록)
|
|
│ │ │
|
|
└───────────────── ⑪대시보드 통계 / ⑫방문 리포트(엑셀) ────────────────────┘
|
|
```
|
|
|
|
### 단계별 상세
|
|
|
|
**① 방문 사전신청 (HOST/ADMIN/SECURITY)** — `POST /api/visit-requests`
|
|
- 방문자 정보(이름·연락처·회사), 방문 구역(Zone), 방문 기간(visitFrom~visitTo), 담당 호스트 지정.
|
|
- 개별 등록 또는 **엑셀 일괄 업로드**(`POST /api/visit-requests/upload`, `ExcelImportService`).
|
|
- 상태 → `PENDING`.
|
|
|
|
**② ③ 승인/반려 (ADMIN)** — `GET /api/visit-requests/pending` → `POST /api/approvals/{id}/approve|reject`
|
|
- `PENDING` 상태만 처리 가능(이미 처리된 건은 409 conflict).
|
|
- 승인: 상태 → `APPROVED`, **`qrToken` 발급**, `Approval` 기록 저장.
|
|
- 반려: 상태 → `REJECTED`, 사유(comment) 기록.
|
|
|
|
**④ 출입증 발송 (자동)** — `PassNotifier`
|
|
- 승인 직후 QR PNG(240px)를 생성하여 방문자에게 발송.
|
|
- 발송 실패는 승인 트랜잭션을 롤백하지 않음 (catch & log — 승인은 정상 처리).
|
|
- Provider: `dev`(로그만, `LoggingPassNotifier`) / `hanbank`(사내 메시지 API로 LMS 실발송, `HanbankMessagePassNotifier`).
|
|
- 문자에는 `ACS_PUBLIC_BASE_URL` 기반 공개 출입증 링크 포함.
|
|
|
|
**⑤ 공개 출입증 (방문자, 비로그인)** — `GET /pass/:token` → `PublicPassController`
|
|
- 방문자가 휴대폰에서 링크 접속 → QR/방문정보 확인.
|
|
- secure context(HTTPS/localhost)에서만 카메라·안전 접속 보장.
|
|
|
|
**⑥ ⑦ 입장 체크인 (SECURITY/게이트 or 키오스크)** — `POST /api/access/check-in`
|
|
- 식별: QR 토큰 스캔 또는 방문자 이름 검색(`GET /api/access/search?q=`).
|
|
- **검증 순서** (`AccessService.checkIn`):
|
|
1. 상태 == `APPROVED` 아니면 거부.
|
|
2. 방문 종료일 경과 → 상태 `EXPIRED` 전이 + 거부("재신청 필요").
|
|
3. 방문 시작일 이전 → 거부("아직 방문일 아님").
|
|
4. 블랙리스트 매칭(이름+연락처) → 403 차단.
|
|
5. 이미 입장 중 → 409 중복입장.
|
|
6. 금일 이미 퇴장 완료 → 재입장 불가.
|
|
- 통과 시: `AccessEvent(IN)` 기록 + `gateway.openGate()` 호출(게이트 오픈).
|
|
- **늦은 도착 허용**: 같은 '일자'면 시간은 엄격히 보지 않음. 실제 입장 시각은 `access_events.event_at`에 별도 기록.
|
|
|
|
**⑧ 재실현황 (SECURITY/ADMIN)** — `GET /api/access/inside`
|
|
- 마지막 이벤트가 `IN`인 방문자 = 현재 재실 중. 이름·회사·구역·호스트·입장시각 표시.
|
|
- 금일 전체 출입기록: `AccessService.listTodayRecords` (입장/퇴장/재실여부 포함).
|
|
|
|
**⑨ ⑩ 퇴장 체크아웃** — `POST /api/access/check-out`
|
|
- 입장 기록이 없으면 409(퇴장 불가).
|
|
- `AccessEvent(OUT)` 기록. 재실현황에서 제외됨.
|
|
|
|
**⑪ 대시보드 통계** — `GET /api/stats/summary` → `StatsController`
|
|
- 오늘의 방문/승인대기/재실 인원 등 요약 집계.
|
|
|
|
**⑫ 방문 리포트 (SECURITY/ADMIN)** — `GET /api/reports/visits.xlsx?from=&to=`
|
|
- 기간별 방문 내역을 Apache POI로 엑셀 생성·다운로드(`ReportService`).
|
|
|
|
---
|
|
|
|
## 5. 키오스크 셀프 체크인 흐름 (비로그인)
|
|
|
|
`GET /kiosk` → `KioskPage` — 입구 무인 단말에서 방문자가 직접 QR을 스캔하여 셀프 체크인/아웃.
|
|
- 백엔드는 동일한 `check-in`/`check-out` API 사용하되 operator = null(셀프서비스).
|
|
- 웹캠 QR 스캔은 secure context 필요(`useQrScanner.ts`).
|
|
|
|
---
|
|
|
|
## 6. 블랙리스트 흐름 (ADMIN)
|
|
|
|
`GET/POST /api/blacklist`, `DELETE /api/blacklist/{id}` → `BlacklistController`
|
|
- 이름+연락처 기준 차단 명단 관리.
|
|
- **체크인 시 자동 검증**: `BlacklistService.blockReason()`이 매칭되면 입장 403 차단(사유 표시).
|
|
|
|
---
|
|
|
|
## 7. 컴포넌트 데이터 흐름 (백엔드 계층)
|
|
|
|
```
|
|
Controller → Service → Repository(JPA) → DB(H2/PostgreSQL)
|
|
│ │
|
|
│ ├─ ApprovalService ─▶ QrService(ZXing) ─▶ PassNotifier(SMS)
|
|
│ ├─ AccessService ───▶ AccessControlGateway(Mock/실장비)
|
|
│ │ └─ BlacklistService
|
|
│ └─ ReportService(POI) / StatsService / ExcelImportService(POI)
|
|
│
|
|
└─ 공통: GlobalExceptionHandler(예외→ApiResponse), ApiResponse(응답 래퍼), SecurityConfig(세션 인가)
|
|
```
|
|
|
|
주요 엔티티: `User`, `Visitor`, `VisitRequest`, `Approval`, `AccessEvent`, `Zone`, `Blacklist`
|
|
(공통 `BaseEntity` — JPA auditing으로 생성/수정 시각 자동 기록)
|
|
|
|
---
|
|
|
|
## 8. 배포/운영 워크플로우
|
|
|
|
### 로컬 개발 (H2)
|
|
```cmd
|
|
run-backend.cmd :: http://localhost:8080 (env.cmd로 포터블 JDK/Maven 로드)
|
|
run-frontend.cmd :: http://localhost:5173
|
|
```
|
|
초기 계정(DataSeeder, dev 전용): `admin` / `security` / `host` — 비밀번호 `ChangeMe123!` (최초 로그인 시 변경 요구).
|
|
|
|
### 운영 (Docker, PostgreSQL)
|
|
```bash
|
|
cd infra
|
|
cp .env.example .env # POSTGRES_PASSWORD, WEB_PORT, ACS_SMS_PROVIDER, ACS_PUBLIC_BASE_URL 설정
|
|
docker compose up -d --build # db + app(:8080) + web(nginx :80)
|
|
docker compose run --rm seed # 사용자 시드 적재
|
|
docker compose logs -f app # Flyway 마이그레이션/기동 로그
|
|
```
|
|
- `prod` 프로파일 + Flyway `V1__init.sql`로 스키마 관리, DB는 named volume(`db_data`)에 영속.
|
|
- nginx가 정적 파일 서빙 + `/api` 리버스 프록시.
|
|
|
|
### 문자 실발송(hanbank) 전제
|
|
1. 서버→메시지 API 도달 확인: `nc -vz 210.104.132.59 8000`
|
|
2. `ACS_SMS_PROVIDER=hanbank`, `ACS_PUBLIC_BASE_URL`=외부 접속 가능한 실제 URL(localhost 금지) 설정 후 재기동.
|
|
|
|
---
|
|
|
|
## 9. 구현 현황 (7단계)
|
|
|
|
| 단계 | 내용 | 상태 |
|
|
|------|------|------|
|
|
| 1 | 스캐폴딩 (pom, 설정, 공통 클래스, 전역 예외/응답 래퍼, JPA auditing) | ✅ |
|
|
| 2 | 인증·인가 (세션 로그인, 역할 기반 권한, 시드) | ✅ |
|
|
| 3 | 방문 사전신청 + 승인 (신청/취소/엑셀 업로드, 승인/반려 시 qrToken 발급) | ✅ |
|
|
| 4 | 입·출입 체크인/체크아웃 + 재실현황 (AccessEvent, Gateway+Mock, 중복입장·만료 검증) | ✅ |
|
|
| 5 | QR/배지 발급 (ZXing PNG, 배지 인쇄 화면) | ✅ |
|
|
| 6 | 블랙리스트(체크인 차단) + 대시보드 통계 + 방문 리포트 엑셀(POI) | ✅ |
|
|
| 7 | Flyway V1__init.sql(prod) + Docker Compose(db·app·web nginx) + Python 사용자 시드 | ✅ |
|
|
|
|
---
|
|
|
|
## 10. 주요 API 요약
|
|
|
|
| 분류 | 엔드포인트 |
|
|
|------|-----------|
|
|
| 인증 | `POST /api/auth/login` · `/logout` · `GET /api/auth/me` · `POST /api/auth/change-password` |
|
|
| 구역 | `GET /api/zones` |
|
|
| 방문신청 | `GET/POST /api/visit-requests` · `GET .../pending` · `POST .../{id}/cancel` · `POST .../upload`(엑셀) |
|
|
| 승인 | `POST /api/approvals/{id}/approve` · `/reject` |
|
|
| 출입 | `POST /api/access/check-in` · `/check-out` · `GET /api/access/inside` · `/search?q=` |
|
|
| 출입증 | `GET /api/passes/{id}` · `/qr.png` · 공개 `/pass/:token` |
|
|
| 통계/리포트 | `GET /api/stats/summary` · `GET /api/reports/visits.xlsx?from=&to=` |
|
|
| 블랙리스트 | `GET/POST /api/blacklist` · `DELETE /api/blacklist/{id}` (ADMIN) |
|
|
|
|
> API 스모크 테스트: `backend/test-api.http`
|
|
|
|
---
|
|
|
|
## 11. 향후 연동 포인트
|
|
|
|
- **실 출입장비 연동**: `AccessControlGateway` 구현체를 Mock → 실장비 드라이버로 교체.
|
|
- **HTTPS/카메라 스캔**: 사내 도메인·인증서 확보 후 nginx 443 TLS 블록 + `ACS_PUBLIC_BASE_URL` https 설정 (웹캠 QR 스캔은 secure context 필수).
|
|
- **사내망 Docker 빌드**: SSL 인스펙션 대응(사내 CA 주입 또는 내부 미러) 필요 시 Dockerfile 반영.
|