13 KiB
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):- 상태 ==
APPROVED아니면 거부. - 방문 종료일 경과 → 상태
EXPIRED전이 + 거부("재신청 필요"). - 방문 시작일 이전 → 거부("아직 방문일 아님").
- 블랙리스트 매칭(이름+연락처) → 403 차단.
- 이미 입장 중 → 409 중복입장.
- 금일 이미 퇴장 완료 → 재입장 불가.
- 상태 ==
- 통과 시:
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-outAPI 사용하되 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)
run-backend.cmd :: http://localhost:8080 (env.cmd로 포터블 JDK/Maven 로드)
run-frontend.cmd :: http://localhost:5173
초기 계정(DataSeeder, dev 전용): admin / security / host — 비밀번호 ChangeMe123! (최초 로그인 시 변경 요구).
운영 (Docker, PostgreSQL)
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프로파일 + FlywayV1__init.sql로 스키마 관리, DB는 named volume(db_data)에 영속.- nginx가 정적 파일 서빙 +
/api리버스 프록시.
문자 실발송(hanbank) 전제
- 서버→메시지 API 도달 확인:
nc -vz 210.104.132.59 8000 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_URLhttps 설정 (웹캠 QR 스캔은 secure context 필수). - 사내망 Docker 빌드: SSL 인스펙션 대응(사내 CA 주입 또는 내부 미러) 필요 시 Dockerfile 반영.