# IT센터 출입자관리시스템 (Access Control System) IT센터 방문자/출입자 관리 웹 시스템. 방문 사전신청·승인, 입·출입 체크인, QR/배지 발급, 통계·리포트·블랙리스트를 목표로 하며, 출입통제 하드웨어는 추후 연동 가능하도록 추상화한다. ## 기술 스택 - **백엔드**: Spring Boot 3.4.5 / Java 21 (JDK 26 빌드) · Spring Security(세션) · JPA · H2(dev)·PostgreSQL(prod) · POI · ZXing · Flyway - **프론트엔드**: React 19 · Vite 6 · TypeScript · react-router 7 - **포트**: API `8080`, 웹 `5173` ## 역할 | 역할 | 권한 | |------|------| | `ADMIN` | 전체 관리, 사용자/블랙리스트/리포트, 모든 승인 | | `SECURITY` | 입·출입 콘솔(체크인/아웃), 재실현황, 배지 발급 | | `HOST` | 방문 사전신청 등록, 담당 방문 승인 | ## 실행 (로컬 개발, H2) ```cmd :: 백엔드 (http://localhost:8080) run-backend.cmd :: 프론트엔드 (http://localhost:5173) run-frontend.cmd ``` > 두 스크립트는 `C:\ai-dev\scripts\env.cmd`(포터블 JDK/Maven/Node)를 먼저 로드한다. ### 초기 계정 (DataSeeder, dev 전용) | 아이디 | 비밀번호 | 역할 | |--------|----------|------| | admin | ChangeMe123! | ADMIN | | security | ChangeMe123! | SECURITY | | host | ChangeMe123! | HOST | 최초 로그인 시 비밀번호 변경이 요구된다. ## 주요 API - `POST /api/auth/login` · `POST /api/auth/logout` · `GET /api/auth/me` · `POST /api/auth/change-password` - `GET /api/zones` - `GET/POST /api/visit-requests` · `GET /api/visit-requests/pending` · `POST /api/visit-requests/{id}/cancel` · `POST /api/visit-requests/upload`(엑셀) - `POST /api/approvals/{id}/approve` · `POST /api/approvals/{id}/reject` - `POST /api/access/check-in` · `POST /api/access/check-out` · `GET /api/access/inside` · `GET /api/access/search?q=` - `GET /api/passes/{id}` · `GET /api/passes/{id}/qr.png`(배지 QR) - `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](backend/test-api.http) 참고. ## 구현 현황 - [x] **1단계** 스캐폴딩 (pom, 설정, 공통 클래스, 전역 예외/응답 래퍼, JPA auditing) - [x] **2단계** 인증·인가 (세션 로그인, 역할 기반 권한, 시드) - [x] **3단계** 방문 사전신청 + 승인 (신청/취소/엑셀 업로드, 승인/반려 시 qrToken 발급) + 프론트 화면 - [x] **4단계** 입·출입 체크인/체크아웃 + 재실현황 (`AccessEvent`, `AccessControlGateway`+Mock, QR/이름 체크인, 재실 목록, 중복입장·만료 검증) + 출입콘솔 화면 - [x] **5단계** QR/배지 발급 (ZXing PNG `/api/passes/{id}/qr.png`, 배지 인쇄 화면) - [x] **6단계** 블랙리스트(체크인 시 차단) + 대시보드 통계 집계 + 방문 리포트 엑셀(POI) 다운로드 - [x] **7단계** Flyway `V1__init.sql`(prod) + Docker Compose(db·app·web nginx) + Python 사용자 시드 ## Docker 실행 (prod, PostgreSQL) ```bash cd infra cp .env.example .env # 값 수정 (아래 참고) docker compose up -d --build # db + app(:8080) + web(nginx :80) docker compose run --rm seed # 사용자 시드 적재 (admin/security/host) docker compose logs -f app # 기동/마이그레이션 로그 ``` - 백엔드는 `prod` 프로파일로 기동, Flyway가 스키마를 생성/검증한다. - 웹(nginx)이 정적 파일 + `/api` 리버스 프록시를 담당한다. - DB는 named volume(`db_data`)에 영속 — 재기동해도 데이터 유지(dev H2와 다름). ### .env 주요 값 | 변수 | 설명 | |------|------| | `POSTGRES_PASSWORD` | DB 비밀번호 (실제값으로 변경) | | `WEB_PORT` | 웹 공개 포트 (기본 80) | | `ACS_SMS_PROVIDER` | `dev`(로그만) / `hanbank`(사내 API로 LMS 실발송) | | `ACS_PUBLIC_BASE_URL` | 문자 링크가 가리키는 주소 — **방문자 휴대폰에서 접속 가능한 실제 URL** (localhost 금지) | | `ACS_SMS_API_URL` | 사내 메시지 API 주소 (기본 `http://210.104.132.59:8000`) | ### 문자 실발송(hanbank) 전제 1. 서버에서 메시지 API 도달 확인: `nc -vz 210.104.132.59 8000` 2. `ACS_SMS_PROVIDER=hanbank`, `ACS_PUBLIC_BASE_URL`=외부 접속 URL 로 설정 후 재기동 ### HTTPS 적용 (TLS 종료는 nginx가 담당) 브라우저 → **nginx(web, TLS 종료)** → 내부 http → Spring(app) 구조. 앱은 `server.forward-headers-strategy=framework` (prod에 반영됨)로 nginx의 `X-Forwarded-Proto`를 신뢰해 Secure 쿠키·https 리다이렉트를 처리한다. TLS 오버레이가 준비돼 있다 — 인증서만 넣으면 된다: 1. **인증서 준비** — `infra/certs/{fullchain.pem,privkey.pem}` (사내 CA 발급 / 공인 CA / 테스트용 자체서명). 자체서명 예: `openssl req -x509 -newkey rsa:2048 -nodes -days 825 -keyout infra/certs/privkey.pem -out infra/certs/fullchain.pem -subj "/CN="` (`infra/certs/*.pem`은 `.gitignore`로 커밋 제외.) 2. **`.env`**: `ACS_PUBLIC_BASE_URL=https://<도메인>`, `ACS_COOKIE_SECURE=true`. 3. **기동** (base + TLS 오버레이): ```bash cd infra docker compose -f docker-compose.yml -f docker-compose.tls.yml up -d --build ``` → nginx가 `443` TLS 종료 + `80→443` 리다이렉트. `frontend/nginx-tls.conf` 사용. `ACS_COOKIE_SECURE=true`는 위처럼 **실제 HTTPS로 서비스될 때만** 켠다(평문 http에서 켜면 쿠키 미전송 → 로그인 불가). - 출입콘솔의 **웹캠 QR 스캔**은 secure context(HTTPS 또는 localhost)에서만 동작 → HTTPS면 `http://내부IP`에서도 카메라 사용 가능. - 방문자 공개 링크(`/pass/{token}`)도 HTTPS 도메인이면 휴대폰에서 안전하게 열린다. ### 사내망 빌드 참고 `docker compose build`는 컨테이너 안에서 npm/maven 의존성을 받는다. dev 서버는 공개망 직접 접근이 되어 대개 그대로 성공하지만, 사내 SSL 인스펙션에 걸려 인증서 오류가 나면 Dockerfile에 사내 CA 주입 또는 내부 미러 설정이 필요하다(그때 별도 반영). ## 디렉터리 ``` backend/ Spring Boot (com.itcenter.acs) frontend/ React + Vite docs/ 워크플로우·시퀀스 다이어그램·이슈/유의사항 문서 ``` ## 문서 - [docs/workflow.md](docs/workflow.md) — 전체 업무 워크플로우(서술형) - [docs/workflow-sequence.md](docs/workflow-sequence.md) — 시퀀스/상태 다이어그램(Mermaid) - [docs/issues-and-guidelines.md](docs/issues-and-guidelines.md) — 이슈 정리 및 유의사항(규칙) ## 빌드 노트 - JDK 26 환경에서 Lombok은 **1.18.46** + maven-compiler-plugin `annotationProcessorPaths` 설정이 필요하다 (Spring Boot가 핀한 1.18.38은 JDK 26에서 애너테이션 처리가 동작하지 않음). `backend/pom.xml`에 반영됨.