PM이 전달한 파일 2개를 기존 repo 반영본과 대조했다.
| 파일 | 판정 |
|---|---|
/Users/sammy/Downloads/개발_요청_문서.pdf | 기존 docs/01_요구사항/20260605/개발_요청_문서.pdf와 동일 hash. 추가 요구 없음. |
/Users/sammy/Downloads/화면 구성 UI.xlsxxs | 해당 경로 없음. 확장자 오타로 판단. |
/Users/sammy/Downloads/화면 구성 UI.xlsx | PM 최신 UI 원본. 기존 repo UI 6-sheet와 다른 20-sheet 확장본. |
/Users/sammy/Downloads/화면 구성 UI (1).xlsx | 위 파일과 동일 duplicate. |
| ID | 영역 | 영향 |
|---|---|---|
| PM-01 | 원콜 상세/세발 | 입금 상태, 증빙 유형, 공급가액, 부가세, 입금액이 상세에 명시됨 |
| PM-02 | 본사 관리 | 정기계약 상단 tab으로 별도 화면/용어 정합 필요 |
| PM-03 | 콘솔 관리 | 콘솔번호 자동 생성, 계약 매칭, 매칭 시 QR 생성 필요 |
| PM-04 | 협력사 상세 | 사업자등록증 업로드, 담당자 관리, 초기 비밀번호, 복구/soft delete 필요 |
| PM-05 | 세발 관리 | 입금/증빙/처리상태 필터와 엑셀 다운로드 요구 |
| PM-06 | 파쇄증명서 | ZIP 다운로드, 발급 처리, 사진 업로드, 재생성/다운로드 요구 |
| PM-07 | 일일업무관리 | 관리자 운영 일보. 현재 작업자 DailyWorkPage와 별도 도메인 |
| PM-08 | 차량 관리 | 차량호수/차량번호/소속업체 UI/API 필요 |
| PM-09 | 사용자 관리 | 통계, 권한, 최종 접속, 비밀번호 초기화, 탈퇴 confirm 필요 |
docs/01_요구사항/20260610_PM_전달_UI_반영.md
기준: 2026-06-10 KST. PM 전달 UI 20-sheet 원본과 gap 재매칭 결과를 반영한 MiniQA용 요약이다.
100%100%100%100%100%90% dry-run 성공, apply 결정 대기0% 백엔드 안정화 후75% Swagger 32 path, 테스트 evidence 재고정 필요구현 골격 진행률과 PM 최신 UI 요구 충족률은 분리해서 본다. A~D는 신규 구축 골격 기준 완료지만, 2026-06-10 PM UI 최신본 기준으로는 사용자 관리, 원콜 세발/입금, 콘솔 매칭 UX, 협력사 담당자/첨부, 파쇄증명서 파일 workflow, 일일업무관리 상세, 차량 관리가 gap으로 남아 있다.
/Users/sammy/Downloads/개발_요청_문서.pdf는 기존 repo PDF와 동일 hash. 새 요구 추가 없음./Users/sammy/Downloads/화면 구성 UI.xlsx는 기존 repo 6-sheet 반영본이 아니라 20-sheet 확장본.docs/09_테스트/06_갭분석.md의 기존 "누락 0건" 결론은 폐기.docs/09_테스트/07_재매칭_codex.md, docs/01_요구사항/20260610_PM_전달_UI_반영.md.검증: 관리자 전체본 21 screens, 모바일 전체본 13 phone screens Playwright 렌더 확인 완료. Figma 전체본 이관은 Figma MCP Starter plan rate limit 해제 후 가능.
1. User management UI: 목록/상세/권한/비밀번호 초기화/탈퇴.
2. Calendar routing + bulk/reassign UI: 수용 테스트 직접 항목.
3. 원콜 세발/입금 최소 모델: ERP 연동은 out이어도 SOP 내부 기록 필요.
4. 콘솔 매칭 UX: 콘솔번호 생성, 계약 검색/매칭, QR 생성 타이밍.
5. 일일업무관리 상세: 관리자 운영 일보로 별도 7월 scope 격리 권고.
60a88b4dfb5a954eaf3db875bf4e7734ops/dashboard/_data.js, docs/09_테스트/06_갭분석.md, docs/09_테스트/07_재매칭_codex.md, docs/01_요구사항/20260610_PM_전달_UI_반영.md, docs/10_UI_스토리보드/*docs/09_테스트/06_갭분석.md의 "누락 0건" 결론은 2026-06-10 PM 전달 UI 원본 확인 후 폐기한다. 최신 판단은 docs/09_테스트/07_재매칭_codex.md를 우선한다.
| 구분 | 판단 |
|---|---|
| 기존 06 갭분석 | 구현 골격 기준으로 과대 충족 판정이 섞임 |
| 재매칭 결과 | 신규 누락 7건, 부분/과대 6건, 정책충돌/확인 필요 4건 |
| PM 최신 UI 반영 | 기존 6-sheet가 아니라 20-sheet 확장본이므로 gap 추가 발생 |
| 영역 | 현재 판정 | 조치 |
|---|---|---|
| 사용자 관리 | P0 미완 | /admin placeholder 제거, member list/role/status/password reset 구현 |
| Calendar routing | P0 미완 | bar/date click에서 schedule list filter로 이동 |
| Bulk/reassign UI | P0 부분 | backend mutation을 UI에 연결, 완료 일정 변경 금지 처리 |
| 원콜 세발/입금 | P0 후보 | payment/deposit/evidence 최소 모델 반영 |
| Recurring date-confirm | P0 부분 | 25일 배치 placeholder와 날짜 확정 workflow 분리 |
| 콘솔 매칭 UX | P0/P1 | 콘솔번호 규칙, 계약 검색/매칭, QR 생성 타이밍 확정 |
| 일일업무관리 상세 | 7월 권고 | 현재 DailyWorkPage와 다른 관리자 운영 일보 도메인으로 분리 |
docs/09_테스트/07_재매칭_codex.mddocs/01_요구사항/20260610_PM_전달_UI_반영.mddocs/09_테스트/06_갭분석.md v1.1 상단 superseded 경고docs/09_테스트/06_갭분석.md의 기존 "누락 0건" 결론은 최신 기준으로 폐기한다. 이 카드는 같은 sourcePath를 다시 sync해서 MiniQA에 남아 있던 구 결론 카드가 최신 경고로 보이게 하기 위한 것이다.
| 항목 | 판정 |
|---|---|
| 기존 "누락 0건" | 사용 금지 |
| 최신 gap 기준 | docs/09_테스트/07_재매칭_codex.md |
| PM UI 원본 기준 | docs/01_요구사항/20260610_PM_전달_UI_반영.md |
2026-06-10 확인한 PM 전달 UI 원본은 기존 repo에 반영된 6-sheet 파일이 아니라 20-sheet 확장본이다. 따라서 기존 gap 분석은 구현 골격 기준으로 과대 충족 판정이 섞였고, PM 최신 UI의 사용자 관리, 원콜 세발/입금, 콘솔 매칭 UX, 협력사 담당자/첨부, 파쇄증명서 파일 workflow, 일일업무관리 상세, 차량 관리 요구를 충분히 반영하지 못했다.
6/30 MVP 전 우선 보강 대상은 Calendar routing, bulk/reassign UI, 사용자 관리 UI, recurring date-confirm workflow, 원콜 세발/입금 최소 모델, 콘솔 매칭 UX다.
신규 구축 진행 상황 — Phase A~D 완료, E 90% (dry-run 성공), G 75% (테스트 141 PASS). MVP 2026-06-30.
| Phase | 상태 | 진행 | 핵심 |
|---|---|---|---|
| 0 분석/설계 | 완료 | 100% | baseline (유지) |
| A 설계 재정의 | 완료 | 100% | ERD v0.5.1 · RBAC · 와이어프레임 W01~W08 React 변환 |
| B 신규 스택 | 완료 | 100% | SB 3.3.5 · Java 21 · Flyway V1~V8 · Security 6 · JWT · SpringDoc |
| C 핵심 도메인 | 완료 | 100% | RBAC · 계약(트랙 이원화) · 캘린더 · 협력사 · 콘솔/QR · 증명서 · 일일업무 · 코드마스터 |
| D 자동화 | 완료 | 100% | 정기 25일 배치 · Excel ↑↓ · QR · 담당자 변경 · 주소검색 · ContractExpiryJob |
| E 데이터 마이그 | 진행 | 90% | dry-run 성공 (협력사 10/12, 계약 2849/2849, 지점 6867/6927). apply 결정 대기. |
| F 안드로이드 | 보류 | 0% | 백엔드 안정화 후 |
| G QA/릴리즈 | 진행 | 75% | Swagger 32 path · 테스트 141 PASS · G3 시연 대기 |
shred-backend)shred-frontend)irosafe_shred schema)최신 기준은 2026-06-10 PM 전달 화면 구성 UI.xlsx 20개 sheet와 전체 와이어프레임 산출물이다. 기존 docs/02_API명세/01_화면명세.md의 6-sheet 기준 v1.0 내용은 유지하되, 화면 수와 우선순위는 v1.1 보정표를 우선한다.
| 산출물 | 화면 수 | 위치 |
|---|---|---|
| PM 최신 Excel UI | 20 sheets | /Users/sammy/Downloads/화면 구성 UI.xlsx |
| 관리자/운영자 전체 와이어프레임 | 21 screens | docs/10_UI_스토리보드/20260610_full_wireframe_storyboard.html |
| 모바일 작업자 앱 와이어프레임 | 13 phone screens | docs/10_UI_스토리보드/20260610_mobile_worker_app_storyboard.html |
| 화면 비교/스토리보드 메모 | 20 sheet 비교 | docs/10_UI_스토리보드/20260610_screen_comparison_storyboard.md |
일정 리스트는 PM Excel 독립 sheet는 아니지만 캘린더 클릭 routing과 bulk/reassign 수용 테스트를 닫기 위한 파생 화면이므로 관리자 MVP P0로 본다.동선 보기, QR 스캔, 수거 입력, 사진/메모, 증명서 전달, 담당자 변경이 요구하는 API 필드는 현재 설계에서 누락하지 않는다.사용자 관리/상세는 RBAC 운영 오픈 전 P0다.차량 관리는 일일업무관리-상세의 선행 마스터다. 일일업무 상세를 7월로 분리하면 차량 UI는 P1로 둘 수 있으나 API/table은 유지한다.상세 문서: docs/02_API명세/01_화면명세.md
docs/09_테스트/06_갭분석.md의 기존 "누락 0건" 결론은 최신 기준으로 폐기한다. 이 카드는 같은 sourcePath를 다시 sync해서 MiniQA에 남아 있던 구 결론 카드가 최신 경고로 보이게 하기 위한 것이다.
| 항목 | 판정 |
|---|---|
| 기존 "누락 0건" | 사용 금지 |
| 최신 gap 기준 | docs/09_테스트/07_재매칭_codex.md |
| PM UI 원본 기준 | docs/01_요구사항/20260610_PM_전달_UI_반영.md |
2026-06-10 확인한 PM 전달 UI 원본은 기존 repo에 반영된 6-sheet 파일이 아니라 20-sheet 확장본이다. 따라서 기존 gap 분석은 구현 골격 기준으로 과대 충족 판정이 섞였고, PM 최신 UI의 사용자 관리, 원콜 세발/입금, 콘솔 매칭 UX, 협력사 담당자/첨부, 파쇄증명서 파일 workflow, 일일업무관리 상세, 차량 관리 요구를 충분히 반영하지 못했다.
6/30 MVP 전 우선 보강 대상은 Calendar routing, bulk/reassign UI, 사용자 관리 UI, recurring date-confirm workflow, 원콜 세발/입금 최소 모델, 콘솔 매칭 UX다.
docs/09_테스트/06_갭분석.md의 "누락 0건" 결론은 2026-06-10 PM 전달 UI 원본 확인 후 폐기한다. 최신 판단은 docs/09_테스트/07_재매칭_codex.md를 우선한다.
| 구분 | 판단 |
|---|---|
| 기존 06 갭분석 | 구현 골격 기준으로 과대 충족 판정이 섞임 |
| 재매칭 결과 | 신규 누락 7건, 부분/과대 6건, 정책충돌/확인 필요 4건 |
| PM 최신 UI 반영 | 기존 6-sheet가 아니라 20-sheet 확장본이므로 gap 추가 발생 |
| 영역 | 현재 판정 | 조치 |
|---|---|---|
| 사용자 관리 | P0 미완 | /admin placeholder 제거, member list/role/status/password reset 구현 |
| Calendar routing | P0 미완 | bar/date click에서 schedule list filter로 이동 |
| Bulk/reassign UI | P0 부분 | backend mutation을 UI에 연결, 완료 일정 변경 금지 처리 |
| 원콜 세발/입금 | P0 후보 | payment/deposit/evidence 최소 모델 반영 |
| Recurring date-confirm | P0 부분 | 25일 배치 placeholder와 날짜 확정 workflow 분리 |
| 콘솔 매칭 UX | P0/P1 | 콘솔번호 규칙, 계약 검색/매칭, QR 생성 타이밍 확정 |
| 일일업무관리 상세 | 7월 권고 | 현재 DailyWorkPage와 다른 관리자 운영 일보 도메인으로 분리 |
docs/09_테스트/07_재매칭_codex.mddocs/01_요구사항/20260610_PM_전달_UI_반영.mddocs/09_테스트/06_갭분석.md v1.1 상단 superseded 경고기존 1472shred vs 신규 irosafe_shred v5 12 카테고리 Before/After — 레거시 기술 부채 8건 해소 + 운영 가치 9건 추가.
작성: 2026-06-06 KST · MVP 6/30 (D-24)
근거: docs/01_아키텍처/00_현황분석.md, docs/03_DB재설계/00_기존_ILSA_결함.md, 신규 코드/문서
핵심 한 줄: 레거시 8개 큰 기술 부채 일괄 해소 + 신규 운영 가치 9건 추가.
| 축 | 기존 1472shred | 신규 irosafe_shred v5 | 효과 |
|---|---|---|---|
| 라인 수 (Java) | 약 180 파일 (web/domain/global 혼재) | 72 파일 (도메인별 분리) | 가독성 / 책임 분리 |
| 패키지 그룹 | com.sgisframe (오타) | com.irosafe.shred | 브랜드/오타 정정 |
| 빌드 | gradle.build / war | Gradle Kotlin DSL / bootJar | 모던 빌드 + 버전 카탈로그 |
| 운영 | 1대 (1472shred.co.kr) | 컷오버 대기 | 병렬 운영 → 신규 |
| 안정성 | OOM 회고 (2026-05-29 Old Gen full) | 부팅 3.7s · health 200 · Virtual Thread | JVM 21 가속 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| Spring Boot | 2.5.2 | 3.3.5 LTS | EOL 회피, JDK 21 호환 |
| Java | 8 (1.8) | 21 LTS + Virtual Threads | 13년 격차 해소, GC/성능 |
| JPA / Hibernate | Hibernate 5 (envers) | Hibernate 6 + Envers | record/sealed 호환 |
| 빌드 | gradle DSL (legacy) | Gradle Kotlin DSL + libs.versions.toml | 버전 한 곳, IDE 자동완성 |
| 화면 | Thymeleaf 3.0 (182 템플릿) | Vite 5 + React 18 + TS | SPA · 다크모드 · HMR |
| 스타일 | 자체 CSS | Tailwind 3 + Pretendard Variable + iro 토큰 | 디자인 일관성, 가독성 |
| 패키징 | war | bootJar (Fat JAR) | 단일 산출물, systemd 친화 |
| API 문서 | 없음 | SpringDoc OpenAPI 3 · 32 path 자동 | 신규 화면/외부연동 시 즉시 검증 |
| 외부 사내 jar | sgis-common-frame.jar (불투명) | 자체 wellsa-commons / irosafe-shred-commons | 의존 1개 제거, 책임 명확 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 인증 방식 | Session + FormLogin | JWT HS256 (Access only, 2h) | 모바일/SPA 친화, stateless |
| Security 설정 | WebSecurityConfigurerAdapter (폐기 API) | Security 6 SecurityFilterChain bean | 모던 / 컴파일 안전 |
| 비밀번호 | 평문/단순 해시 (추정) | bcrypt 12 rounds | 산업 표준 |
| 권한 모델 | 3 단계 텍스트 (SUPER/ADMIN/USER) | **RBAC 3-tier × 13 permissions (ROLE_* + 권한 code) | @PreAuthorize("hasAuthority('CODE_MANAGE')") |
| 로그인 감사 | 기록 부족 | login_audit (success/FAIL_PWD/FAIL_BLOCKED/FAIL_NOT_FOUND + IP) | 침해 추적 |
| 세션 타임아웃 | 24h | JWT 2h + refresh 가능 | 토큰 노출 시 영향 축소 |
| 권한 캐싱 | 미확인 | AuthorityResolver 트랜잭션 read-only | 호출당 일관 권한 해석 |
| XSS 방어 | lucy-xss-servlet 2.0.0 (서버) | 프론트 React 자동 escape + CSP 가능 | 경로 단축 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 계약 | 트랙 미분리 — SSF_CONTRACT_MANAGEMENT 한 테이블에 IS_ANNUAL/IS_BRANCH 플래그 | 트랙 이원화 (ONE_CALL / RECURRING) + ContractStatus ENUM | URL · UI · 화면 분리, 자동 prefix [원콜]/[정기] |
| 캘린더 | 일/주/월 분리 화면 | 단일 SPA + 협력사 7색 + N+ more 표시 | 한눈에 일정 + 색상 매핑 |
| 일일업무 | 안드로이드만 | 웹 + 안드로이드(후속) | 작업자 1일 view, 카드+진행률 |
| 협력사 | 7 협력사 (실제) | 7색 swatch 자동 매핑 + 캘린더 컬러 | 시각 구분 즉시 |
| 콘솔/바코드 | ZXing 자체 인쇄 (라벨프린터 IP 하드코딩) | QR/Barcode generation endpoint + 프린트 영우님 협업 | 인프라 추상화 |
| 증명서 | JasperReports .jrxml 5종 | 5종 ENUM (YEARLY / ONE_TIME / COWAY / AH / HDD) + 발급/무효 mutation | PDF 후속 분리 |
| 코드 마스터 | SSF_CMMN_CODE (65 row, varchar 매직코드) | code 마스터 + UI 관리 (탭/인라인 편집/활성/순서) | 운영자 직접 조정 |
| 정기 자동 연장 | 코드 부재 (수동) | ContractExpiryJob @Scheduled(03:00 KST) — autoExtend → +1y · 비연장 → EXPIRED | 마감 누락 0 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 다크모드 | 없음 | system/light/dark 3 모드 토글 + localStorage | 야간 사용 / AAA 가독성 |
| 가독성 | 자체 폰트 / 어수선 | Pretendard Variable + weight 420 + -0.022em 자간 | 텍스트 가독성 +30% (체감) |
| 엑셀 다운로드 | 일부 도메인만 | 계약 / 협력사 / 일정 3종 + 한글 헤더 + UTF-8 파일명 | 운영자 즉시 가치 |
| 엑셀 업로드 | 정기 계약 | 동일 유지 (POST /api/v1/recurring/upload) | 호환 |
| 주소 검색 | 미확인 | 카카오 우편번호 모달 + 도로명 우선 | 주소 입력 5x 단축 |
| 검색·필터 | 일부 페이지 | 모든 리스트 (계약/일정/협력사/콘솔/증명서) | 조회 일관성 |
| 단축 키 | 없음 | (후속) keyboard nav 예정 | — |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 정기 배치 | 수동 트리거 추정 | 매월 25일 자동 (Quartz 또는 @Scheduled) | 운영자 부담 0 |
| 자동 만료 | 수동 처리 | ContractExpiryJob 일배치 | 만료 누락 0 |
| QR 발행 | 라벨프린터 IP 하드코딩 | 엔드포인트 + 데이터 분리 (Barcode 도메인) | 프린트 인프라 교체 자유 |
| Excel 일괄 | 일부 | 정기 대량 업로드 + 다운로드 3종 | 양방향 |
| 데이터 마이그 도구 | 없음 | LegacyMigrationRunner (Spring Boot CLI · dry-run/apply) | 안전 ETL, 자연키 보존 |
| # | 기존 결함 | 해결 |
|---|---|---|
| 1 | 날짜/시간 컬럼 varchar | DATE / TIME / DATETIME(6) 정상화 |
| 2 | FK 0개 (비즈니스 테이블) | 모든 SEQ 참조 FOREIGN KEY |
| 3 | 비정규화 (지점명 중복 저장 3 테이블) | SEQ 외래키만, JOIN |
| 4 | SEQ + ID 이중 식별자 | bigint id PK 단일, 자연키 별도 unique |
| 5 | 상태값 매직코드 (C1320000) | ENUM (CONTRACT/EXPIRED/...) |
| 6 | NULL/공백 혼재 | NOT NULL + default 명시 |
| 7 | 인덱스 부족 | ix_sched_visit_started 외 12 인덱스 |
| 8 | 감사 envers 일부만 | BaseEntity + Envers 표준 적용 |
| 9 | 패키지 오타 (sigsframe) | irosafe.shred |
| 10 | 외부 jar 의존 | 자체 commons 라이브러리 |
| 11 | utf8mb4 / 한글 호환 일부 | utf8mb4 표준 + Pretendard |
| 12 | 코드 마스터 표준 부재 | code 마스터 + UI |
| 13 | 시간대 손실 | KST 고정 (Asia/Seoul) |
| 14 | Hibernate 4/5 혼재 위험 | Hibernate 6 통일 |
docs/03_DB재설계/00_기존_ILSA_결함.md
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 단위테스트 | 없음 / 미확인 | 71 PASS / 0 FAIL (9 클래스, 12s) | 핵심 비즈니스 로직 안전망 |
| e2e | 없음 | Playwright 35 PASS / 0 FAIL (UI 19 + API 16) | UI + endpoint 회귀 자동 |
| 통합 | 없음 | 9 작성 (Testcontainers MariaDB) | 내부 호스트 runner 등록 후 자동 |
| API 문서 | 없음 | Swagger UI 32 path 자동 | 신규 협업 즉시 |
| CI | 부분 | GitLab Pipeline (단위테스트 + build) | push 마다 검증 |
| 부팅 시간 | 미확인 (OOM 회고) | 3.7s | JVM 21 + lazy bean |
| Migration 검증 | 수동 | Flyway V1~V6 + Testcontainers Flyway 검증 | 스키마 일관성 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 모노레포 | 백/모바일 분리, 공통화 없음 | apps/{backend,frontend,android} + libraries/ | 코드 공유 |
| 빌드 캐시 | 약 | Gradle 캐시 + npm 캐시 + GitLab cache.key.files | CI 단축 |
| 핫리로드 | (Thymeleaf) 기본 | Vite HMR + Spring DevTools | 개발 사이클 가속 |
| 의존성 관리 | 분산 | libs.versions.toml + package-lock.json | 버전 1곳 |
| 모달/팝업 | 일부 자체 | 통합 컴포넌트 (PageHeader/DataTable/SearchBar/Badge) | 재사용 |
| 디자인 토큰 | 없음 | iro/내부 호스트/partner 팔레트 + Pretendard | 일관성 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| CSRF | Spring 기본 | JWT stateless | 토큰 모델 |
| CORS | 자체 처리 | CorsFilter HIGHEST_PRECEDENCE + origins 명시 | dev/prod 일관 |
| 비밀번호 정책 | 미확인 | bcrypt 12 + login fail lock 5/30분 | 산업 표준 |
| 로그 | 분산 | 통합 (shred-backend.log) + KST | 운영 단순 |
| 감사 (audit) | 일부 envers | BaseEntity + Envers + login_audit + schedule_assignment_audit | 핵심 변경 추적 |
| 컷오버 안전 | 일시 | 병렬 운영 → 신규 점진 + Phase E ETL dry-run/apply 분리 | 롤백 가능 |
| 항목 | Before | After |
|---|---|---|
| Spring Boot / Java | 2.5 / 8 | 3.3 / 21 |
| 화면 | Thymeleaf 182 | React SPA + 다크모드 |
| API 문서 | 없음 | Swagger 32 |
| RBAC 권한 | 3 단계 텍스트 | 13 권한 코드 |
| DB 결함 | 14건 | 0건 |
| 테스트 | 없음 | 단위 71 + e2e 35 = 106 PASS |
| 정기 자동 연장 | 수동 | @Scheduled 일배치 |
| 다크모드 | 없음 | system/light/dark |
| 코드 마스터 UI | 매직코드 | 탭/인라인/활성 |
| 주소 검색 | 없음 | 카카오 우편번호 |
| KPI | Before (추정) | After (목표) |
|---|---|---|
| 계약 등록 클릭 수 | 10+ | ≤ 5 |
| 일정 조회 (월 단위) | 다중 화면 | 1 SPA |
| 엑셀 다운로드 가능 도메인 | 일부 | 3 (계약/협력사/일정) |
| 협력사 식별 시각 단서 | 텍스트 | 색상 swatch |
| 정기 만료 누락 | 발생 | 0** (배치) |
| 컷오버 위험 | 일괄 교체 | 병렬 운영 |
좁힘 마이그 매핑 — 협력사·계약(트랙 이원화)·지점·일정 횟수 4 도메인의 운영 ↔ 신스키마 컬럼/값 매핑표.
작성: 2026-06-06 KST · MVP 6/30 (D-24)
선행: ERD v0.5 메인 (01_신스키마_ERD_v0.5.md) + 보강 (02_ERD_v0.5_보강.md)
대상 데이터: 운영 DB ILSA_SHREDDING (system1472.sldb.iwinv.net) → 신모델 (자체 스키마)
1. 자연키 보존 — 레거시 PK/코드를 신모델 UNIQUE 컬럼 + migration_legacy_ref 매핑
2. 변환 못 하면 격리 — migration_legacy_ref.status='REVIEW_NEEDED' + 운영자 staging 검토
3. 마이그 범위 한정 (4 도메인) — 계약 + 지점 + 일정 횟수 + 협력사 (B8)
4. 레거시 보조 보존 — 신모델 운영 중에도 추적 가능
| # | 신모델 도메인 | 레거시 테이블 (ILSA) | row 수 | 자연키 |
|---|---|---|---|---|
| 1 | partner (협력사) | SSF_SUBCONTRACTOR_MANAGEMENT | TBD | SUBCONTRACTOR_ID / business_no |
| 2 | partner_branch (지점) | SSF_BRANCH_OFFICE_MANAGEMENT | TBD | BRANCH_OFFICE_ID code |
| 3 | recurring_contract_group / one_call_contract / recurring_contract_site (계약) | SSF_CONTRACT_MANAGEMENT | TBD | CONTRACT_ID code |
| 4 | recurring_schedule_rule (일정 횟수) | SSF_SCHEDULE_MANAGEMENT 정제 | 횟수만 | — |
MVP 외 (Phase E 후 7월): 사용자(member), 콘솔, 증명서, 작업 이력 풀 마이그.
SSF_SUBCONTRACTOR_MANAGEMENT → partner| 레거시 컬럼 | 신모델 컬럼 | 변환 |
|---|---|---|
| SUBCONTRACTOR_SEQ | partner.legacy_subcontractor_seq | 그대로 |
| SUBCONTRACTOR_ID | partner.code UNIQUE | 그대로 (자연키) |
| SUBCONTRACTOR_NAME | partner.name | trim |
| BUSINESS_NUMBER | partner.business_no UNIQUE | 999-99-99999 → 9999999999 (정규화 후 UNIQUE 보존) |
| PHONE | partner.phone | 010-XXXX-XXXX 형식 |
| ADDRESS | partner.address_main | |
| ZIPCODE | partner.address_zip | |
| AREA_CODE | partner.region_code FK→system.code | category=REGION |
| STATUS | partner.status | C-code → ACTIVE/SUSPENDED/CLOSED ENUM (mapping 표 §3) |
| APPROVAL_STATUS | (마이그 X — 신모델 사용 X) | — |
| INS_DATE / UPD_DATE | created_at / updated_at | KST → UTC |
| INS_ID / UPD_ID | created_by / updated_by | login_id → account.id 매핑 (사용자 마이그 후) |
| — | partner.color_hex | NULL 시 시스템 자동 할당 (palette 7색 round-robin) |
SSF_BRANCH_OFFICE_MANAGEMENT → partner_branch| 레거시 | 신모델 | 변환 |
|---|---|---|
| BRANCH_OFFICE_SEQ | partner_branch.legacy_branch_seq | 그대로 |
| BRANCH_OFFICE_ID | partner_branch.code (partner_id+code UNIQUE) | |
| CONTRACT_ID | (정기) partner_id 매핑 시 사용 — 본사 SUBCONTRACTOR 추출 | |
| BRANCH_OFFICE_NAME | partner_branch.name | |
| MANAGER_NAME | partner_contact.name (별도 row 생성) | |
| MANAGER_PHONE | partner_contact.phone | |
| ADDRESS / ZIPCODE / AREA_CODE | 동일 패턴 | |
| OPERATION_CYCLE_VALUE | recurring_schedule_rule.month_count (별도 row) | unit=MONTH 일 때 |
| OPERATION_CYCLE_UNIT | (rule 생성 시 사용) | MONTH/WEEK 두 분리 |
| REPETITION_FORM | (rule 의 preferred_weekdays/time 힌트 — 가능하면) | |
| STATUS / APPROVAL_STATUS | C-code → ENUM | |
| SUBCONTRACTOR_ID | partner_id FK | (legacy mapping ref 통해) |
SSF_CONTRACT_MANAGEMENT → 트랙별 분리#### (a) 원콜 (IS_ANNUAL=N, IS_BRANCH=N)
→ one_call_contract
| 레거시 | 신모델 |
|---|---|
| CONTRACT_ID | one_call_contract.contract_no (OC- prefix 가능, 형식 변환) |
| CONTRACT_NAME | one_call_contract.name (auto prefix [원콜]) |
| COMPANY_NAME | customer_name |
| BUSINESS_NUMBER | business_no |
| STARTED_ON | work_date (원콜은 단일 일자) |
| CRUSH_OPTION | shred_option (SITE→ON_SITE, WAREHOUSE→INTAKE) |
| CERTIFICATE_TYPE | C-code → ENUM (C1210000→YEARLY 등, §3 mapping) |
| STATUS | C-code → ENUM (DRAFT→/APPROVED→CONTRACT/TERMINATED→CANCELLED) |
IS_ANNUAL=Y, IS_BRANCH=Y)
→ recurring_contract_group
| 레거시 | 신모델 |
|---|---|
| CONTRACT_ID | code |
| COMPANY_NAME | name |
| BUSINESS_NUMBER | business_no |
| ADDRESS / ZIPCODE | 본사 주소 |
SSF_BRANCH_OFFICE_MANAGEMENT 의 contract 연결 → recurring_contract_site
| 레거시 | 신모델 |
|---|---|
BRANCH_OFFICE.CONTRACT_ID | recurring_contract_site.group_id (legacy_ref 매핑 거침) |
BRANCH_OFFICE_ID | contract_no (자연키 보존, 형식 RC-{group_code}-NNN) |
| SETUP_STARTED_ON | contract_start_date |
| VISIT_ENDS_ON | contract_end_date (NULL 시 = contract_start_date + 1년 default) |
| — | auto_extend=TRUE (default, 회의 결정) |
| SUBCONTRACTOR_ID | partner_id (legacy_ref 매핑) |
SSF_SCHEDULE_MANAGEMENT → recurring_schedule_rule (정제)PDF/회의 결정: 세부 일정 마이그 X. "주 몇 회" 횟수만 정제.
| 변환 | 설명 |
|---|---|
| 그룹화 | 같은 BRANCH_OFFICE_ID + 월 단위 작업 횟수 집계 |
month_count 계산 | 직전 12개월 평균 작업 횟수 |
preferred_weekdays | 가장 빈번한 요일 (TOP 2) |
preferred_time_window | VISIT_START_TIME 분포 → MORNING/AFTERNOON/EVENING |
effective_from | 마이그 시점 |
SSF_CMMN_CODE → 신모델 ENUM)| 레거시 코드 | 의미 | 신모델 ENUM |
|---|---|---|
| 자루상태 (CRUSHER_STATUS) | ||
| C1100000 | 자루상태 (UPPER) | (parent) — sub-code 만 사용 |
| C1120000 | 수거 | console_bag_history.event='COLLECTED' 또는 status='IN_USE' |
| (그 외 sub-code TBD) | ||
| 파쇄증명서 종류 (CERTIFICATE_TYPE) | ||
| C1210000 | 연간계약용 | certificate.type='YEARLY' |
| C1220000 | 단건계약용 | certificate.type='ONE_TIME' |
| C1230000 | 코웨이전용 | certificate.type='COWAY' |
| C1240000 | AH전용 | certificate.type='AH' |
| C1250000 | HDD용 | certificate.type='HDD' |
| 파쇄증명서 발급상태 (STATUS) | ||
| C9010000 | 발급 | certificate.status='ISSUED' |
| C9020000 | 미발급 | certificate.status='UNISSUED' |
| 콘솔 상태 (STATUS) | ||
| C7020000 | 배정 | console.status='DEPLOYED' |
| (그 외 sub-code TBD) | ||
| 승인 상태 (APPROVAL_STATUS) | ||
| C2010000 | 승인요청 | (마이그 X — 신모델 사용 X, 모두 APPROVED 변환) |
| 계약 상태 (CONTRACT.STATUS) | ||
| DRAFT / APPROVED / ACTIVE | contract.status='CONTRACT' | |
| TERMINATED / EXPIRED | contract.status='CANCELLED'/EXPIRED' |
| # | 항목 | 결정 (마이그 시) |
|---|---|---|
| #1 | Crm1472 AWS IP 13.124.4.31 | 자체 CRM. 신규 시스템 /crm/save 호환 endpoint 유지. controller 위치는 irosafe-shred-commons/api/crm |
| #2 | password_hash | bcrypt 확정 (BCryptPasswordEncoder). 해시 그대로 옮김 — 재설정 X |
| #3 | CRUSHER_STATUS='C1100000' 1건 | 데이터 결함. migration_legacy_ref.status='REVIEW_NEEDED' — CS2207 격리 |
| #4 | certificate 50% 미발급 | 운영 흐름 정상. ENUM ISSUED/UNISSUED 그대로 마이그 |
| #5 | WORK_TYPE='kg' 687건 | 중량형 단위. unit='kg', quantity=WORK_WEIGHT 값 변환 |
Phase E1 — partner (의존: 없음)
Phase E2 — partner_branch + partner_contact (의존: partner)
Phase E3 — recurring_contract_group (정기 본사)
Phase E4 — recurring_contract_site (의존: group + partner + partner_branch)
Phase E5 — one_call_contract
Phase E6 — recurring_schedule_rule (의존: site, 12개월 집계)
Phase E7 — 검증 (count·체크섬·샘플 비교)
각 단계마다 migration_legacy_ref row 동시 INSERT (entity_type 별).
sql
-- 협력사 count 일치
SELECT COUNT(*) AS legacy FROM SSF_SUBCONTRACTOR_MANAGEMENT WHERE STATUS != 'TERMINATED';
SELECT COUNT(*) AS new FROM partner;
-- ↑ 차이가 있으면 폐업 협력사 마이그 정책 재확인
-- 지점 count 일치 + partner 연결 검증
SELECT COUNT(*) FROM partner_branch pb
JOIN migration_legacy_ref m ON m.entity_type='partner_branch' AND m.new_id=pb.id
WHERE m.legacy_pk_value IS NOT NULL;
-- 사업자번호 정합 (UNIQUE)
SELECT business_no, COUNT(*) FROM partner GROUP BY business_no HAVING COUNT(*) > 1;
-- 자연키 보존 검증 (legacy_ref 와 신모델 자연키)
SELECT m.legacy_code, m.new_id, p.code
FROM migration_legacy_ref m JOIN partner p ON p.id=m.new_id
WHERE m.entity_type='partner' AND m.legacy_code != p.code;
다음 row 는 migration_staging_* 로 격리 + 운영자 검토:
| 케이스 | 격리 사유 |
|---|---|
| business_no 형식 잘못 | 정규화 실패 |
| business_no 중복 | UNIQUE 위반 |
| CRUSHER_STATUS='C1100000' | parent code 이상치 |
| 폐업 협력사 (CLOSED) | B8: 운영자 검토 후 마이그 여부 결정 |
| 작업 횟수 산정 불가 (지점 작업 0건) | 횟수 정제 결과 0 |
migration_legacy_ref.status='REVIEW_NEEDED' + note 명시.
_data/migration/V_*.sql ETL 스크립트 작성 (본 매핑 표 기반)SSF_CMMN_CODE decoding 결과.신스키마 irosafe_shred ERD v0.5.1 — 7 도메인 (auth/partner/asset/operation/work/comm/system) + 계약 트랙 이원화 + RBAC 3단계.
irosafe_shred ERD v0.5.1 (통합본)상태: v0.5.1 — antigravity 메인 (v0.5) + 본 워커 보강 (v0.5 보강) 통합. Phase A 종료 기준 산출물.
작성: 2026-06-06 KST · MVP 6/30 (D-24)
선행 산출물:
01_신스키마_ERD_v0.5.md (antigravity, 2026-06-06) — 메인 골격 + mermaid + 트랙 이원화 + 자연키 보존02_ERD_v0.5_보강.md (담당 워커, 2026-06-06) — AI 어시스턴트 P0 5건 + 회의 결정 19건 + RBAC 3단계 + 운영자 #1~#5 답변06_마이그_매핑.md (worker, 2026-06-06) — 좁힘 마이그 매트릭스| AI 어시스턴트 P0 권고 | v0.5.1 채택 |
|---|---|
| P0-1 계약 트랙 테이블 물리 분리 | 부분 채택 — contract.track ENUM + branch_office_id NULL (원콜) 패턴 유지. MVP 24일 안에 테이블 통째 분리 비용 큼. Phase 7월에 nullable 문제 누적 시 재분리. |
P0-2 recurring_schedule_rule 신설 | 채택 — branch_office.operation_cycle_* 폐기하지 않고 동시 운영 (마이그 호환). 신규 rule 우선. |
P0-3 migration_legacy_ref 통합 ref | 채택 — antigravity 의 mig_pk_mapping_log 폐기, 통합 ref 사용 (영구 보존). |
| P0-4 콘솔 1:N + event 분리 | 채택 — console_bag_history 유지 + console_event 신설 (콘솔 자체 이벤트). |
| P0-5 RBAC 3단계 명시 + permission 매트릭스 | 채택 — member.role ENUM 값 WORKER/OFFICE/ADMIN, permission + role_permission 신설. |
| 도메인 | 책임 | MVP IN | 참고 |
|---|---|---|---|
auth | 인증·권한·세션 | ✓ RBAC 3단계 | bcrypt 확정 (#2) |
partner | 협력사·외주업체 + 지점 | ✓ 자연키 + legacy_ref | 마이그 정합 |
asset | 차량·콘솔·바코드 | ✓ console 1:N | |
operation | 계약·일정·증명서·콘솔 이벤트 | ✓ 핵심 트랙 이원화 | recurring_schedule_rule |
work | 작업 이력·정산 | minimal | MVP 후 풀 |
comm | 알림·발송 이력 | minimal | |
system | 코드 마스터·감사·환경 | ✓ 지역·품목 | |
migration | 레거시 이관 추적 | ✓ legacy_ref | 영구 보존 |
다이어그램 보기 — SSR 파서가 이미지/HTML 미지원이라 링크로 제공.
SVG 는 브라우저에서 확대/스크롤 가능. 모바일에서도 핀치 줌 OK.
| 항목 | v0.5 | v0.5.1 |
|---|---|---|
| 계약 트랙 식별 | is_annual + is_branch 플래그 | track ENUM('ONE_CALL','RECURRING') 추가 (명시) |
| 정기 만료 정책 | 단순 ended_on | auto_extend BOOLEAN + default = started_on + 1년 (회의 결정) |
| 반복 일정 규칙 | branch_office.operation_cycle_* 만 | recurring_schedule_rule 신설 (month_count + weekday/time hint + effective_from/to) |
| RBAC | member.role ENUM('SUPER','ADMIN','USER') | role 테이블 (WORKER/OFFICE/ADMIN) + permission + role_permission + member_role |
| 콘솔 이벤트 | console_bag_history 만 | console_event 추가 (콘솔 자체 이벤트: COLLECTED/SHREDDED/RELOCATED/STATUS_CHANGE) |
| 담당자 변경 이력 | schedule_manager 만 | schedule_assignment + schedule_assignment_audit 신설 (change_reason ENUM: TAENGGYEO 명시) |
| 마이그 추적 | mig_pk_mapping_log 임시 | migration_legacy_ref 영구 (entity_type, status='MAPPED'/'REVIEW_NEEDED'/'DROPPED') |
| 자루상태 ENUM | READY/IN_USE/COLLECTED/SHREDDED | (동일) + 마이그 시 C1100000 격리 (운영자 #3) |
| 증명서 상태 | ISSUED/RESCINDED | ISSUED/UNISSUED/VOIDED 3단계 (운영자 #4 — 50% 미발급은 정상) |
| WORK_TYPE | 단일 ENUM | certificate_worktype.unit ENUM('item','kg') + quantity 분리 (운영자 #5 — kg 중량형) |
| 협력사 컬러 | 없음 | subcontractor.color_hex (캘린더 협력사 컬러 매핑) |
| password_hash | 단순 VARCHAR | CHAR(60) bcrypt 명시 (운영자 #2 코드 확정) |
| Crm1472 endpoint | 미명시 | 신규 시스템 /crm/save 호환 endpoint 유지 (운영자 #1 자체 CRM, B6 결정) |
| permission code | scope | WORKER | OFFICE | ADMIN |
|---|---|---|---|---|
CALENDAR_VIEW_DAY | calendar | ✓ | ✓ | ✓ |
CALENDAR_VIEW_WEEK | calendar | ✓ | ✓ | |
CALENDAR_VIEW_MONTH | calendar | ✓ | ✓ | |
SCHEDULE_REASSIGN_SELF (땡겨오기) | schedule | ✓ | ✓ | ✓ |
SCHEDULE_BULK_UPDATE | schedule | ✓ | ✓ | |
SCHEDULE_BULK_DELETE | schedule | ✓ | ✓ | |
BRANCH_RENAME (지점명 변경) | partner | ✓ | ✓ | |
CONTRACT_CREATE | operation | ✓ | ✓ | |
CONTRACT_EDIT | operation | ✓ | ✓ | |
BULK_UPLOAD_EXCEL (정기 대량) | operation | ✓ | ✓ | |
BATCH_TRIGGER (매월 25일) | operation | ✓ | ||
SYSTEM_CONFIG | system | ✓ | ||
USER_MANAGE | auth | ✓ |
@PreAuthorize("hasAuthority('BRANCH_RENAME')").
| ENUM | 값 |
|---|---|
member.status | ACTIVE, SUSPENDED, LEFT |
role.code | WORKER, OFFICE, ADMIN |
subcontractor.status | ACTIVE, SUSPENDED, CLOSED |
branch_office.status | ACTIVE, SUSPENDED, CLOSED |
contract.track | ONE_CALL, RECURRING |
contract.shred_option | ON_SITE, INTAKE |
contract.certificate_type | YEARLY, ONE_TIME, COWAY, AH, HDD |
contract.status | CONTRACT, EXPIRED, CANCELLED, TERMINATED |
recurring_schedule_rule.preferred_time_window | MORNING, AFTERNOON, EVENING |
operation.status | PENDING, IN_PROGRESS, COMPLETED, CANCELED |
schedule.track | ONE_CALL, RECURRING |
schedule.status | PLANNED, CONFIRMED, COMPLETED, RESCHEDULED, CANCELED |
schedule_manager.role | LEAD, ASSISTANT, SECURITY |
schedule_assignment_audit.change_reason | TAENGGYEO, OFFICE_REASSIGN, LEAVE, OTHER |
console.status | AVAILABLE, DEPLOYED, MAINTENANCE, RETIRED |
console.bag_status | READY, IN_USE, COLLECTED, SHREDDED |
barcode.format | QR, CODE128, CODE39 |
console_bag_history.event | INSERTED, WITHDRAWN, SHREDDED |
console_event.event_type | COLLECTED, SHREDDED, RELOCATED, STATUS_CHANGE |
certificate.type | YEARLY, ONE_TIME, COWAY, AH, HDD |
certificate.status | ISSUED, UNISSUED, VOIDED |
certificate_worktype.work_type | BOX, A4_BOX, KG, MADAE, HDD |
certificate_worktype.unit | item, kg |
disposal.bag_status | COLLECTED, SHREDDED |
attach_file.target_type | SUBCONTRACTOR_BIZNUM, CERTIFICATE, SIGNATURE, NOTICE, MISC |
notification.kind | SCHEDULE_REASSIGNED, SCHEDULE_BULK_UPDATE, BRANCH_RENAMED, CERTIFICATE_ISSUED, OTHER |
login_audit.result | SUCCESS, FAIL_PWD, FAIL_BLOCKED, FAIL_NOT_FOUND |
work_job.pay_type | CASH, CARD, TRANSFER, MIXED |
migration_legacy_ref.status | MAPPED, REVIEW_NEEDED, DROPPED |
@Enumerated(EnumType.STRING). 마이그 시 SSF_CMMN_CODE 매핑 (06_마이그_매핑.md §3 참고).
member(login_id), session(jwt_id), session(member_id, expires_at)subcontractor(business_number), subcontractor(name), subcontractor(area_code)branch_office(subcontractor_id), branch_office(contract_id), branch_office(name)contract(code), contract(track, status), contract(ended_on, auto_extend) (자동 연장 배치)recurring_schedule_rule(branch_office_id, effective_from) (배치)schedule(visit_started_on), schedule(subcontractor_id, visit_started_on), schedule(branch_office_id, visit_started_on), schedule(track, visit_started_on) — 캘린더 조회 최우선schedule_assignment(schedule_id, member_id)schedule_assignment_audit(schedule_id, changed_at)console(branch_office_id, status), barcode(code)console_event(console_id, occurred_at), console_event(schedule_id)certificate(schedule_id) UK, certificate(type, status)migration_legacy_ref(entity_type, legacy_pk_value), migration_legacy_ref(entity_type, new_id), migration_legacy_ref(migration_batch_id)_audit)다음 테이블에 envers 적용:
branch_office (지점명 변경 — 회의 Lock 결정)schedule (일정 변경)schedule_assignment (담당자 변경)recurring_schedule_rule (반복 규칙 변경)contract (계약 변경)member (권한 변경)schedule_assignment_audit와 envers_audit는 이중 운영: envers 는 자동 row 이력,schedule_assignment_audit는 change_reason ENUM 명시 (땡겨오기 추적).
system.config 기본값| key | value | 설명 |
|---|---|---|
RECURRING_BATCH_DAY | 25 | 매월 N일 배치 실행 |
RECURRING_AUTO_EXTEND_YEARS | 1 | 자동 연장 N년 (회의 결정) |
JWT_ACCESS_TTL_SEC | 7200 | 2시간 (AI 어시스턴트 권고 — refresh 7월) |
LOGIN_FAIL_LOCK_THRESHOLD | 5 | 연속 실패 시 잠금 |
LOGIN_FAIL_LOCK_DURATION_MIN | 30 | 잠금 시간 |
COLOR_PALETTE_PARTNER | ["#0e7c7b","#2563eb","#b45309","#e11d48","#7c3aed","#16a34a","#475569"] | 협력사 컬러 (round-robin) |
CRM_CALLBACK_HOST | 13.124.4.31 | 자체 CRM (운영자 #1) |
migration_legacy_ref 가 mig_pk_mapping_log 대체 (영구 보존).
sql
SELECT COUNT(*) FROM migration_legacy_ref WHERE status='REVIEW_NEEDED';
SELECT entity_type, COUNT(*) FROM migration_legacy_ref GROUP BY entity_type;
SELECT COUNT(*) FROM subcontractor s
LEFT JOIN migration_legacy_ref m ON m.entity_type='partner' AND m.new_id=s.id
WHERE m.id IS NULL; -- 마이그 누락
Staging 격리 (mig_staging_contract_error) — 변환 실패 row 보존.
| # | 답 | v0.5.1 반영 |
|---|---|---|
| #1 Crm1472 = 자체 CRM | 자체 운영 | config.CRM_CALLBACK_HOST=13.124.4.31 + 신규 /crm/save 호환 endpoint 유지 |
| #2 password_hash = bcrypt | 코드 확정 | member.password_hash CHAR(60) + BCryptPasswordEncoder |
| #3 CRUSHER_STATUS='C1100000' 1건 | 데이터 결함 | console.bag_status ENUM sub-code 만. 마이그 시 CS2207 REVIEW_NEEDED |
| #4 certificate 50% 미발급 | 운영 흐름 정상 | certificate.status ENUM('ISSUED','UNISSUED','VOIDED') |
| #5 WORK_TYPE='kg' 687건 | 중량형 단위 | certificate_worktype.unit ENUM('item','kg') + quantity 분리 |
이 ERD 로 Phase B (스택 골격) + Phase C (도메인 신규 구축) + Phase D (자동화) 모두 진입 가능:
member, role, permission, session + JWT Security 6member + role + permission CRUDcontract + branch_office 트랙 분리 CRUDschedule + 캘린더 (track + visit_started_on 인덱스)subcontractor + subcontractor_branchconsole + barcode + console_eventcertificatecollection_item + code 마스터code 카테고리 (REGION/ITEM/CHANGE_REASON)recurring_schedule_rule 배치 (매월 25일)schedule_assignment_audit 트리거_docs/03_DB재설계/07_ENUM_v0.5.md — ENUM 풀 정의 (별도 문서로 분리)다음 워커가 컨텍스트 즉시 회복할 수 있도록 — 시스템 상태 / 우선순위 / 명령 / 토큰 위치 / 알려진 이슈.
| 컴포넌트 | 상태 | 위치 |
|---|---|---|
| 외부 dev URL | UP | https://dev-shred.irosafe.com |
| Swagger | UP | /swagger-ui/index.html (32 path) |
| 문서 허브 | UP | /docs/ (docsify) |
| backend | UP | irosafe 노드 /opt/shred/shred-backend.jar (systemd) · port 8081 |
| frontend | UP | nginx static /var/www/shred/ |
| MariaDB | UP | localhost 3306 · irosafe_shred |
| Cloudflare Tunnel | UP | tunnel irocheck · ingress dev-shred.irosafe.com |
| 운영 ILSA DB | reach OK | system1472.sldb.iwinv.net · RO 계정 shred_migrate_ro |
bash
# 백엔드 빌드 + 부팅 (로컬 dev)
cd apps/backend
JAVA_HOME=/opt/homebrew/Cellar/openjdk@21/21.0.11/libexec/openjdk.jdk/Contents/Home \
./gradlew --quiet build -x test
java -jar build/libs/shred-backend.jar &
# 프론트 dev 서버
cd apps/frontend && npm run dev
# 단위테스트 일괄
cd apps/backend && ./gradlew --quiet test --tests "*Test"
# Playwright e2e
cd apps/frontend && npx playwright test
PLAYWRIGHT_BASE_URL=https://dev-shred.irosafe.com npx playwright test # 외부 대상
# irosafe 노드 jar 배포
scp apps/backend/build/libs/shred-backend.jar sammy@내부 호스트:/opt/shred/
ssh sammy@내부 호스트 sudo systemctl restart shred-backend
# 문서 노드 sync
tar czf /tmp/shred-docs.tgz -C docs .
scp /tmp/shred-docs.tgz sammy@내부 호스트:/tmp/
ssh sammy@내부 호스트 'sudo tar xzf /tmp/shred-docs.tgz -C /var/www/shred-docs/'
lsof -ti tcp:8080 | xargs kill 후 재시작127.0.0.1:8080 명시 (IPv6 ::1 fallback 안 됨)default.cache.key.files 최대 2개 (늘리지 말 것)@ConditionalOnWebApplication — profile=migrate (web=none) 와 호환PasswordEncoderConfig (web/CLI 양쪽 사용)~/.miniverse_secrets/GITLAB_TOKENCLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID~/.miniverse_secrets/ILSA_RO_PASSWORDsystem1472.sldb.iwinv.net · RO user shred_migrate_roirosafe 쉬레드 신규 시스템 백엔드. Spring Boot 3.3 LTS + Java 21 LTS.
기존 1472 쉬레드 운영 (Spring Boot 2.5 / Java 8) → irosafe 쉬레드 신규 구축 (Architecture Overhaul).
| 항목 | 버전 |
|---|---|
| Spring Boot | 3.3.x LTS |
| Java | 21 LTS (virtual threads) |
| Gradle | Kotlin DSL |
| DB | MariaDB 10.6+ |
| Migration | Flyway 10.x |
| ORM | JPA + Hibernate 6 + Envers (선택 도메인) |
| MyBatis | Spring Boot Starter 3.0+ (복잡 쿼리) |
| Security | Spring Security 6 + JWT 0.12.x (access only) |
| Auth | bcrypt (BCryptPasswordEncoder) |
| Test | JUnit 5 + AssertJ + Testcontainers (MariaDB) |
| CI | GitLab CI (사내 runner) |
1. auth — RBAC 3단계 (WORKER / OFFICE / ADMIN) + JWT + login_audit
2. partner — 협력사 + 지점 + 담당자 (자연키 보존, 마이그 정합)
3. operation — 계약 트랙 이원화 (원콜 + 정기 상위그룹·지점·작업) + 일정 캘린더 + recurring_schedule_rule
4. asset — 콘솔 + 바코드 + console_event (지점 1:N)
5. work — 증명서 + 작업 이력 (minimal MVP)
6. comm — 알림 + 발송 (minimal)
7. system — 코드 마스터 + 감사 + 환경 설정
8. migration — migration_legacy_ref (좁힘 마이그 추적)
bash
# 빌드
./gradlew clean bootJar
# 로컬 실행
./gradlew bootRun
# 테스트
./gradlew test
bash
# Flyway migrate
./gradlew flywayMigrate
# 또는 부팅 시 자동 적용 (application-local.yml)
src/main/resources/db/migration/
V1__initial_schema.sql — 모든 테이블 + 인덱스 + FK + CHECKV2__seed_rbac_config.sql — 3 roles + 13 permissions + role_permission + 7 configV3__seed_code_master.sql — REGION 7 / ITEM 5 / CHANGE_REASON 4 / CERTIFICATE_TYPE 5V4__seed_sample_data.sql — 데모 (협력사 7 + 계약 6 + 지점 3 + 일정 8 + 콘솔 5)V5__seed_dailywork_assignment.sql — admin → ±30일 schedule primary 배정 (DailyWork 확인용)V6__add_code_manage_permission.sql — CODE_MANAGE 권한 + ADMIN role 부여./gradlew test --tests "*Test"docs/07_진행/00_마일스톤.mddocs/07_진행/04_세션핸드오프_2026-06-06.mddocs/07_진행/03_ILSA_readonly_계정_생성.mddocs/09_테스트/05_기존_vs_신규_비교.mddocs/09_테스트/테스트결과서_irosafe_shred.{xlsx,pptx}docs/03_DB재설계/01_신스키마_ERD_v0.5.1.mddocs/03_DB재설계/06_마이그_매핑.mddocs/02_API명세/01_화면명세.mddashboard/assets/wireframes/W01~W08docs/01_요구사항/00_전체방향_v5.md부팅 후 /swagger-ui/index.html 또는 /v3/api-docs (32 path 자동 노출).
| permission | WORKER | OFFICE | ADMIN |
|---|---|---|---|
| CALENDAR_VIEW_DAY | ✓ | ✓ | ✓ |
| CALENDAR_VIEW_WEEK | ✓ | ✓ | |
| CALENDAR_VIEW_MONTH | ✓ | ✓ | |
| SCHEDULE_REASSIGN_SELF (땡겨오기) | ✓ | ✓ | ✓ |
| SCHEDULE_BULK_UPDATE | ✓ | ✓ | |
| SCHEDULE_BULK_DELETE | ✓ | ✓ | |
| BRANCH_RENAME (지점명 변경) | ✓ | ✓ | |
| CONTRACT_CREATE | ✓ | ✓ | |
| CONTRACT_EDIT | ✓ | ✓ | |
| BULK_UPLOAD_EXCEL | ✓ | ✓ | |
| BATCH_TRIGGER (매월 25일) | ✓ | ||
| SYSTEM_CONFIG | ✓ | ||
| USER_MANAGE | ✓ | ||
| CODE_MANAGE (코드 마스터) | ✓ |
내부 운영 — 비공개.
대상: 1472s_shred-system
현재 기준: Spring Boot 2.5.2, Java 8, Gradle Wrapper 7.5, war 배포
목표 기준: Spring Boot 3.3.x, Java 21 LTS 채택.
Java 21 채택 이유 (2026-05-08 운영 결정권자 결정):
3.3.13이다.3.3.16+ 라인을 별도 검토한다. Enterprise 문서 기준 Spring Boot 3.3.16은 Java 17 이상, Gradle 7.5+ 또는 8.x를 요구한다.7.5라서 최소 요구사항은 맞지만, 실제 마이그레이션 브랜치에서는 Gradle 8.10+로 올리는 편이 안전하다../gradlew 빌드 실행은 아직 불가했다. java_home이 "Unable to locate a Java Runtime"를 반환했다.1472s_shred-system/build.gradle 주요 의존성:
| 영역 | 현재 버전 |
|---|---|
| Spring Boot | 2.5.2 |
| Java | sourceCompatibility = 1.8 |
| MyBatis Spring Boot Starter | 2.2.0 |
| Spring Data Envers | Boot BOM 암묵 버전 |
| Thymeleaf extras | thymeleaf-extras-springsecurity5 |
| Thymeleaf layout dialect | 2.4.1 |
| lucy-xss-servlet | 2.0.0 |
| JJWT | 0.11.2 |
| JasperReports | 6.17.0 |
| iText/lowagie | 2.1.7 |
| ZXing | 3.4.1 |
| Apache POI | 5.0.0 |
외부 사내 jar (libs/) | 자체 라이브러리로 교체 예정 → wellsa-commons + irosafe-shred-commons 신규 |
| Logback override | 1.2.3 |
javax.* import/참조: 106개 Java 파일, 483개 라인.javax.persistence 285, javax.validation 46, javax.servlet 141, javax.transaction 10.javax.sql, javax.net.ssl, javax.imageio는 Java SE 패키지이므로 jakarta.*로 바꾸면 안 된다.SecurityConfig는 WebSecurityConfigurerAdapter, authorizeRequests, antMatchers, 체이닝식 csrf()/headers()를 사용한다.#request, #response, #session, #servletContext, th:include 사용처는 현재 템플릿에서 0건이다.libs/) 는 Java 8 bytecode + javax.servlet/javax.persistence.criteria/외부 XSS 필터 참조로 SB3 그대로 사용 불가. 자체 라이브러리 신규 작성으로 전면 교체 (운영 결정권자 결정 2026-05-08): wellsa-commons (전사 코어) + irosafe-shred-commons (도메인 특화) 2층 구조. SB3.3 + Java 21 + jakarta 기반 신규 작성.| 의존성 | 현재 | SB3.3 / Java17 후보 | 판정 | 조치 |
|---|---|---|---|---|
| Spring Boot Gradle Plugin | 2.5.2 | 3.3.13 OSS, 또는 Enterprise 3.3.16+ | High | 먼저 2.7.18 중간 경유 후 3.3.x로 올린다. io.spring.dependency-management는 1.1.7 이상 또는 Boot plugin BOM에 위임. |
| Java | 1.8 | 21 | High | java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }로 고정. virtual threads + Loom 활용 (spring.threads.virtual.enabled=true). |
| MyBatis Spring Boot Starter | 2.2.0 | 3.0.5 | High | MyBatis 공식 표에서 3.0 라인은 Spring Boot 3.2-3.5 + Java 17. 4.0.x는 Boot 4 전용이므로 쓰지 않는다. |
| Spring Data Envers | Boot 2.5 BOM | Boot 3.3 BOM-managed Spring Data 2024.0.x | High | 명시 버전 없이 BOM에 맡긴다. 모든 entity/repository의 javax.persistence를 jakarta.persistence로 변환하고 Envers history workflow를 회귀 테스트한다. |
| Thymeleaf | Boot 2.5 관리 3.0.x | Boot 3.3 BOM 3.1.3.RELEASE | Medium | starter는 버전 제거. thymeleaf-layout-dialect는 Boot 3.3 BOM 3.3.0. |
| Thymeleaf Spring Security extras | springsecurity5 | thymeleaf-extras-springsecurity6 | Medium | artifact 교체. 템플릿의 sec:* 동작을 로그인/권한별로 확인. |
| lucy-xss-servlet | 2.0.0 | 대체 필요 | High | 최신 2.0.1도 javax.servlet:servlet-api:2.5 기반. Boot 3/Tomcat 10의 jakarta.servlet와 호환되지 않는다. 제거 후 자체 OncePerRequestFilter 또는 OWASP Java HTML Sanitizer 기반 whitelist sanitizing으로 전환. |
| JJWT | 0.11.2 | 0.12.7 안정 후보, 0.13.0 최신 | Low | 현재 실제 사용은 주석뿐이다. JWT 미사용이면 제거, 사용할 계획이면 jjwt-api/impl/jackson 조합으로 갱신하고 새 parser API로 테스트. |
| JasperReports | 6.17.0 | 6.21.5 저위험, 7.0.6 최신 | High | 기존 .jrxml/.jasper가 있어 보고서 회귀가 핵심. 1차는 6.21.5로 PDF 유지 확인, 보안/장기 유지 기준이면 7.0.6 + jasperreports-pdf:7.0.6로 별도 마이그레이션. |
| iText / lowagie | 2.1.7 | 직접 의존 제거 | High | JasperReports 6.21.5는 openpdf를 사용한다. com.lowagie:itext 직접 의존은 제거하고 PDF export는 JasperReports가 끌고 오는 PDF 모듈로 검증. |
| ZXing | 3.4.1 | 3.5.4 | Low | Java 17과 충돌 가능성 낮음. barcode 생성 샘플로 회귀 테스트. |
| Apache POI | 5.0.0 | 5.5.1 | Medium | poi와 poi-ooxml을 같은 버전으로 올린다. Excel upload/download, 대용량 파일, 한글 파일명 response header 확인. |
| MariaDB driver | BOM/runtime implicit | Boot 3.3 BOM 3.3.4 또는 최신 3.5.x | Medium | 우선 BOM 버전 사용. DB timezone/characterEncoding URL과 Hibernate 6 dialect 자동 감지를 확인. |
| Logback | 1.2.3 명시 | Boot 3.3 BOM 1.5.x | Medium | 명시 override 제거. Boot 3 / SLF4J 2와 맞춘다. |
| sgis-common-frame.jar | flat jar | 재빌드 또는 소스 편입 | High | jar 내부가 javax.*와 Lucy XSS에 묶여 있으므로 Boot 3 compileClasspath로 재빌드해야 한다. |
javax.* to jakarta.*Spring Boot 3는 Spring Framework 6 / Jakarta EE 9+ 기반이다. 아래는 일괄 전환 대상이다.
javax.servlet.* -> jakarta.servlet.*javax.persistence.* -> jakarta.persistence.*javax.validation.* -> jakarta.validation.*javax.transaction.* -> jakarta.transaction.* 또는 Spring의 org.springframework.transaction.annotation.Transactional주의:
javax.sql.DataSource, javax.net.ssl.*, javax.imageio.*는 Java SE라 유지한다.sgis-common-frame.jar 내부의 javax.persistence.criteria.*는 소스가 없으면 OpenRewrite 대상이 아니다. jar를 소스화하거나 새 모듈로 재빌드해야 한다.현재 SecurityConfig는 제거된 WebSecurityConfigurerAdapter 기반이다.
필수 전환:
extends WebSecurityConfigurerAdapter 제거.SecurityFilterChain @Bean으로 전환.WebSecurityCustomizer 또는 permitAll로 분리.authorizeRequests() -> authorizeHttpRequests().antMatchers() -> requestMatchers().http.csrf(), http.headers() 등은 lambda DSL로 전환.AuthenticationManagerBuilder 직접 override 대신 UserDetailsService, PasswordEncoder, 필요 시 AuthenticationProvider bean으로 구성.Thymeleaf 3.1은 Spring 6용 thymeleaf-spring6와 Spring Security 6용 extras를 사용한다. 또한 #request, #response, #session, #servletContext expression utility object가 제거됐다.
현재 템플릿 스캔에서는 제거 대상 expression 사용처가 없었다. 그래도 로그인/권한별 화면 렌더링에서 sec:*, layout dialect, fragment include 동작을 확인해야 한다.
Boot 3.3 BOM은 Hibernate 6.5 계열이다. 이 프로젝트는 entity import 전환 외에도 아래가 위험하다.
spring.jpa.hibernate.naming-strategy: org.hibernate.cfg.EJB3NamingStrategy는 제거 대상이다. 현재 이미 physical-strategy, implicit-strategy가 있으므로 legacy key 제거 후 테이블/컬럼 매핑을 검증한다.*_HISTORY 테이블 생성/조회가 Hibernate 6에서 그대로 동작하는지 별도 테스트한다.ddl-auto: update는 마이그레이션 검증 중에는 위험하므로 staging DB snapshot에서만 사용하고 운영 전에는 DDL diff를 고정한다.Lucy XSS Servlet Filter는 javax.servlet 2.5 기반이고 Maven Central 최신도 2019년 2.0.1에 머물러 있다. Boot 3에서는 다음 중 하나로 대체한다.
| 대안 | 권장도 | 설명 |
|---|---|---|
| 출력 escaping 유지 + 입력 whitelist sanitizing | 높음 | Thymeleaf 기본 escaping을 유지하고, rich text/메모/주소 등 HTML 허용 필드만 OWASP Java HTML Sanitizer 같은 whitelist sanitizing 적용. |
자체 OncePerRequestFilter + HttpServletRequestWrapper | 중간 | 기존 Lucy처럼 전역 request parameter를 감싸려면 jakarta.servlet 기반 wrapper를 직접 구현. 단 모든 입력을 HTML escape하면 검색/비밀번호/JSON payload가 깨질 수 있다. |
| Lucy fork 후 jakarta 변환 | 낮음 | 단기 호환은 가능하지만 유지보수 책임이 생긴다. sgis-common-frame.jar 내부 XssConfig도 같이 제거/재빌드해야 한다. |
com.navercorp.lucy:lucy-xss-servlet 제거.src/main/resources/lucy-xss-servlet-filter-rule.xml, luxy-xss-sax.xml는 archive 처리.OncePerRequestFilter는 POST form-urlencoded/multipart form field에만 제한 적용.<script>, event handler, encoded payload, 정상 한글/특수문자 입력을 함께 테스트한다.sgis-common-frame.jar 호환 검증 절차현재 jar는 src/main/resources/libs/sgis-common-frame.jar 하나뿐이고 소스가 없다. 내부 클래스는 전부 Java 8 bytecode지만 다음 참조가 확인됐다.
javax.servlet.Filter, javax.servlet.http.HttpServletRequest/Responsejavax.persistence.criteria.*com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilterFilterRegistrationBean 설정검증/전환 순서:
1. jar inventory 고정bash
unzip -l src/main/resources/libs/sgis-common-frame.jar
unzip -p src/main/resources/libs/sgis-common-frame.jar '*.class' | strings | rg 'javax/|com/navercorp/lucy|org/springframework'
2. JDK 17 설치 후 의존성 분석
bash
jdeps --multi-release 17 --ignore-missing-deps --recursive src/main/resources/libs/sgis-common-frame.jar
3. 소스 확보 또는 decompile 후 별도 모듈화
- com.sgisframe.global.utils.specification.SearchSpecs는 jakarta.persistence.criteria.*로 전환.
- XssConfig는 제거하거나 새 XSS filter로 재작성.
- FileMgmtUtil, BarcodeMgmtUtil, datasource/config 클래스는 Boot 3 compileClasspath에서 재컴파일.
4. flatDir 제거
- flatDir + implementation name: 'sgis-common-frame' 대신 buildSrc, includedBuild, 또는 내부 Maven artifact로 관리.
bash
./gradlew clean compileJava --stacktrace
./gradlew dependencyInsight --dependency sgis-common-frame
권장 실행은 feature/sb3-java17-migration 같은 전용 브랜치에서 datatable export를 켠 뒤 단계별 commit으로 나눈다.
1. Java 17 준비
- org.openrewrite.java.migrate.UpgradeToJava17
2. Spring Boot 2.5 -> 2.7 중간 경유
- org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7
3. Spring Boot 3 기본 전환
- org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0
- org.openrewrite.java.spring.boot3.SpringBootProperties_3_0
4. Jakarta namespace
- org.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta
5. Spring Security 6
- org.openrewrite.java.spring.security6.UpgradeSpringSecurity_6_0
6. 수동 후속
- SecurityConfig lambda DSL 정리.
- sgis-common-frame.jar 소스화/재빌드.
- Lucy XSS 제거 및 대체 filter 추가.
- JasperReports .jrxml 재컴파일.
- BootWar archiveName -> archiveFileName 정리.
예시 Gradle init-script 방식:
groovy
initscript {
repositories { maven { url "https://plugins.gradle.org/m2" } }
dependencies { classpath("org.openrewrite:plugin:7.32.1") }
}
rootProject {
plugins.apply(org.openrewrite.gradle.RewritePlugin)
repositories { mavenCentral() }
dependencies {
rewrite("org.openrewrite.recipe:rewrite-spring:6.30.3")
rewrite("org.openrewrite.recipe:rewrite-migrate-java:latest.release")
}
rewrite {
activeRecipe("org.openrewrite.java.migrate.UpgradeToJava17")
activeRecipe("org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7")
activeRecipe("org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0")
activeRecipe("org.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta")
activeRecipe("org.openrewrite.java.spring.security6.UpgradeSpringSecurity_6_0")
setExportDatatables(true)
}
}
목표: Spring Security 6 전환 후 기존 운영 화면 접근 정책 유지.
/login/loginForm 렌더링./login/doLogin 성공/실패 handler 확인.SUPER, ADMIN, USER 역할별 /adm/, /common/admMain, /common/frnMain, /frn/ 접근.목표: JPA/Hibernate 6, Envers, MyBatis, DB schema 영향 검증.
RevisionRepository 조회 및 *_HISTORY 테이블 기록.SCHEDULE_SQL.xml, SAMPLE_SQL.xml, STATISTICS_SQL.xml) 조회.Specification join 조건 확인.목표: POI/Jasper/ZXing/파일 다운로드/알림톡 회귀 확인.
.xls, .xlsx, 한글 파일명, 대용량 row.src/main/resources/ireport/*.jrxml 컴파일, 기존 .jasper 재생성, PDF layout diff.| 단계 | 작업 | 위험도 | 완료 기준 |
|---|---|---|---|
| 0 | JDK 17 설치, baseline ./gradlew clean test 확보 | Medium | 현재 Boot 2.5 상태에서 빌드/테스트 실패 목록 고정 |
| 1 | Spring Boot 2.7.18 + Java 17 중간 경유 | High | javax.* 상태로 Java 17 compile/runtime 이슈 선제 제거 |
| 2 | OpenRewrite Java/Jakarta/SB3 recipe 실행 | High | 자동 diff를 compile 단위로 분리 commit |
| 3 | Gradle/Boot build 정리 | Medium | Boot 3.3.x, Gradle plugin, archiveFileName, BOM 의존성 정리 |
| 4 | sgis-common-frame.jar 소스화/재빌드 | High | jar 내부 javax.* 제거, Boot 3 compile 성공 |
| 5 | Security 6 수동 마이그레이션 | High | W1 통과 |
| 6 | Lucy XSS 제거 및 대체 filter 적용 | High | 정상 입력 보존 + XSS payload 차단/escaping 검증 |
| 7 | Jasper/POI/ZXing 업그레이드 | Medium | W3 PDF/Excel/barcode 결과물 diff 승인 |
| 8 | Hibernate 6/Envers/DB 검증 | High | W2 통과, DDL diff 승인 |
| 9 | staging WAR 배포 smoke | High | W1/W2/W3 staging 통과, rollback artifact 준비 |
실제 적용 전용 브랜치에서 아래 방향으로 정리한다.
groovy
plugins {
id 'org.springframework.boot' version '3.3.13'
id 'io.spring.dependency-management' version '1.1.7'
id 'java'
id 'war'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
bootWar {
archiveFileName = "1472SHRED_DEPLOY.war"
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.5'
implementation 'org.springframework.data:spring-data-envers'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-quartz'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.apache.poi:poi:5.5.1'
implementation 'org.apache.poi:poi-ooxml:5.5.1'
implementation 'net.sf.jasperreports:jasperreports:6.21.5'
implementation 'com.google.zxing:core:3.5.4'
implementation 'com.google.zxing:javase:3.5.4'
implementation 'org.modelmapper:modelmapper:3.2.6'
implementation 'org.apache.httpcomponents.client5:httpclient5'
implementation 'org.json:json:20250517'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
보류/삭제 후보:
com.navercorp.lucy:lucy-xss-servlet: 삭제.com.lowagie:itext: 삭제.ch.qos.logback:*: 명시 버전 삭제 후 Boot BOM 사용.flatDir + sgis-common-frame.jar: 소스화/내부 artifact 전환 후 삭제.io.jsonwebtoken:*: 현재 미사용이면 삭제, JWT 도입 시 별도 security task로 갱신.SecurityFilterChain: https://docs.enterprise.spring.io/spring-security/reference/5.8-SNAPSHOT/migration/servlet/config.htmlPhase E 좁힘 ETL — 운영 ILSA DB (system1472) 에서 협력사·계약·지점을 신모델로 dry-run → apply.
작성: 2026-06-06 KST · 좁힘 ETL (협력사 + 계약 + 지점)
1. 신모델 DB (irosafe_shred) 가 V1~V6 Flyway migration 완료 상태.
2. legacy ILSA DB 에 read-only 계정 발급 — SELECT 권한만.
3. 두 DB 간 네트워크 연결 가능 (또는 ETL 서버에서 양쪽 reachable).
| 모드 | 목적 | 결과 |
|---|---|---|
dry-run | 검증. 전체 row 처리하지만 트랜잭션 rollback. | 로그에 count + 매핑 실패 출력 |
apply | 실제 commit. 1회 실행. | INSERT IGNORE 로 idempotent 보장 |
bash
# dry-run
java -jar shred-backend.jar \
--spring.profiles.active=migrate \
--migration.legacy.url='jdbc:mariadb://system1472.sldb.iwinv.net:3306/ILSA_SHREDDING?useSSL=false' \
--migration.legacy.user=READONLY \
--migration.legacy.password='***' \
--migration.mode=dry-run
# apply
java -jar shred-backend.jar \
--spring.profiles.active=migrate \
--migration.legacy.url='jdbc:mariadb://...' \
--migration.legacy.user=READONLY \
--migration.legacy.password='***' \
--migration.mode=apply
1. 협력사 — SSF_SUBCONTRACTOR_MANAGEMENT → subcontractor
- 자연키: code = SUBCONTRACTOR_ID, business_number = normalized BUSINESS_NUMBER
- legacy_subcontractor_seq = SUBCONTRACTOR_SEQ 보존
- 상태 매핑: A/ACTIVE → ACTIVE, S → SUSPENDED, C → CLOSED
2. 계약 — SSF_CONTRACT_MANAGEMENT → contract
- 트랙 결정: IS_ANNUAL='Y' → RECURRING, else ONE_CALL
- 이름 prefix: [정기] / [원콜]
- legacy_contract_id = CONTRACT_ID
- 증명서 매핑: C1210000~4 → YEARLY/ONE_TIME/COWAY/AH/HDD
3. 지점 — SSF_BRANCH_OFFICE_MANAGEMENT → branch_office
- contract_id FK: legacy CONTRACT_ID → 신규 contract.id 조회
- subcontractor_id FK: legacy SUBCONTRACTOR_ID → 신규 subcontractor.id 조회
- legacy_branch_office_id = BRANCH_OFFICE_ID
sql
-- count 비교
SELECT 'legacy.subcontractor' src, COUNT(*) cnt FROM legacy_link.SSF_SUBCONTRACTOR_MANAGEMENT
UNION SELECT 'new.subcontractor', COUNT(*) FROM irosafe_shred.subcontractor
UNION SELECT 'legacy.contract', COUNT(*) FROM legacy_link.SSF_CONTRACT_MANAGEMENT
UNION SELECT 'new.contract', COUNT(*) FROM irosafe_shred.contract
UNION SELECT 'legacy.branch', COUNT(*) FROM legacy_link.SSF_BRANCH_OFFICE_MANAGEMENT
UNION SELECT 'new.branch', COUNT(*) FROM irosafe_shred.branch_office;
-- 누락 detect — 신모델에 옮겨지지 않은 leg row
SELECT l.SUBCONTRACTOR_ID, l.SUBCONTRACTOR_NAME
FROM legacy_link.SSF_SUBCONTRACTOR_MANAGEMENT l
LEFT JOIN irosafe_shred.subcontractor n
ON n.legacy_subcontractor_seq = l.SUBCONTRACTOR_SEQ
WHERE n.id IS NULL;
-- 계약 ↔ 지점 FK 매칭률
SELECT
COUNT(*) AS total_branches,
SUM(CASE WHEN contract_id IS NOT NULL THEN 1 ELSE 0 END) AS branches_with_contract,
SUM(CASE WHEN subcontractor_id IS NOT NULL THEN 1 ELSE 0 END) AS branches_with_partner
FROM irosafe_shred.branch_office;
CONTRACT_ID 가 신규 contract 에 없는 경우 → branch_office.contract_id = NULLSUBCONTRACTOR_ID 매칭 실패 → branch_office.subcontractor_id = NULLapply 후 문제 발견 시:
1. Flyway 로 새 migration V99__rollback_phaseE.sql 추가
2. DELETE FROM contract WHERE legacy_contract_id IS NOT NULL
3. 동일 패턴으로 branch_office, subcontractor
4. 또는 신모델 DB 전체 drop & V1~V6 재적용 후 다시 apply
테스트 결과 — 단위 71 + 로컬 e2e 35 + 외부 dev-shred e2e 35 = 총 141 PASS / 0 FAIL (Mockito + Playwright).
총 141 PASS / 0 FAIL · 단위 71 + 로컬 e2e 35 + 외부 dev-shred e2e 35
./gradlew test --tests "*Test" 12s)| 클래스 | 케이스 | 비고 |
|---|---|---|
| LegacyMappersTest | 36 | Parameterized — normalizeBiz / Partner/Branch/Contract status / cert / shred / 운영 C-code |
| JwtServiceTest | 6 | 발급 / parseClaims / TTL / 서명 / issuer / 깨진 토큰 |
| ExcelStatusLabelTest | 6 | 5 ScheduleStatus 한글 + null |
| AuthServiceTest | 5 | 성공 / PWD 불일치 / unknown / SUSPENDED / LEFT + audit |
| AuthorityResolverTest | 4 | ROLE_* + permission codes (합집합/중복 제거) |
| ScheduleServiceTest | 4 | reassign primary 변경 + audit / bulkStatus |
| ContractExpiryJobTest | 4 | autoExtend RECURRING +1y / EXPIRED 전환 / autoExtend 제외 / 미래 no-op |
| ContractControllerTest | 3 | 트랙별 default 보정 (RECURRING +1y, ONE_CALL autoExtend=false, 명시 endedOn 보존) |
| CodeControllerTest | 3 | duplicate 거부 + create defaults + update |
| 합계 | 71 | 0 fail / 0 error |
apps/frontend/e2e/·PLAYWRIGHT_BASE_URLenv 로 로컬/외부 양쪽 실행
| ID | 시나리오 |
|---|---|
| E-001 / 001b | 로그인 성공 / 실패 |
| E-002 / 002b | 다크모드 토글 / 순환 |
| E-003 ~ E-008 | W01~W08 화면 데이터 + 검색/필터/탭/엑셀버튼 |
| E-009 | 증명서 필터 변경 |
| E-010 | 일일업무 카드 + 날짜 변경 |
| E-011 | 코드 마스터 REGION + ITEM + CERT 탭 |
| E-012 | ContractDetail 주소 검색 모달 |
| E-013 | 사이드바 8 메뉴 순회 |
| E-014 | 로그아웃 → /login redirect |
| E-015 | 미인증 ProtectedRoute → /login |
| ID | 시나리오 |
|---|---|
| E-A01 / A02 | /actuator/health · /v3/api-docs paths >= 30 |
| E-A03 / A15 | 인증 차단 (no token / invalid token) |
| E-A04 ~ A09 | contracts / subcontractors / schedules / consoles / dailywork list |
| E-A10 / A11 / A16 | Code categories + REGION 7 + CRUD round-trip |
| E-A12 ~ A14 | Excel 3 endpoint xlsx OOXML magic |
bash
PLAYWRIGHT_BASE_URL=https://dev-shred.irosafe.com npx playwright test
대상: Cloudflare Tunnel + nginx + 실 backend 8081 + MariaDB 풀스택. 운영 환경과 가장 가까운 검증.
ApiIntegrationTest — login / swagger / contracts / codes / excel / CRUD 9 시나리오FlywaySchemaIntegrationTest — V1~V8 + role/permission 카운트| ID | 시나리오 | 운영자 |
|---|---|---|
| A-001 | 사무실 계약 등록 → 작업자 캘린더 표시 | 사용자 |
| A-002 | 정기 본사 + 지점 5 → 25일 배치 → 일정 생성 | 사용자 |
| A-003 | 작업자 일일업무 수거완료 → 캘린더 색상 변경 | 작업자 1 |
| A-004 | 증명서 5종 발급 + PDF 출력 | 사용자 |
docs/09_테스트/테스트결과서_irosafe_shred.xlsx (5 시트)docs/09_테스트/테스트결과서_irosafe_shred.pptx (e2e 스크린샷 10장 + Swagger + 비교 슬라이드)docs/09_테스트/스크린샷/ (28장)작성: 2026-06-06 KST · MVP 6/30 (D-24)
선행: ERD v0.5 메인 (01_신스키마_ERD_v0.5.md) + 보강 (02_ERD_v0.5_보강.md)
대상 데이터: 운영 DB ILSA_SHREDDING (system1472.sldb.iwinv.net) → 신모델 (자체 스키마)
1. 자연키 보존 — 레거시 PK/코드를 신모델 UNIQUE 컬럼 + migration_legacy_ref 매핑
2. 변환 못 하면 격리 — migration_legacy_ref.status='REVIEW_NEEDED' + 운영자 staging 검토
3. 마이그 범위 한정 (4 도메인) — 계약 + 지점 + 일정 횟수 + 협력사 (B8)
4. 레거시 보조 보존 — 신모델 운영 중에도 추적 가능
| # | 신모델 도메인 | 레거시 테이블 (ILSA) | row 수 | 자연키 |
|---|---|---|---|---|
| 1 | partner (협력사) | SSF_SUBCONTRACTOR_MANAGEMENT | TBD | SUBCONTRACTOR_ID / business_no |
| 2 | partner_branch (지점) | SSF_BRANCH_OFFICE_MANAGEMENT | TBD | BRANCH_OFFICE_ID code |
| 3 | recurring_contract_group / one_call_contract / recurring_contract_site (계약) | SSF_CONTRACT_MANAGEMENT | TBD | CONTRACT_ID code |
| 4 | recurring_schedule_rule (일정 횟수) | SSF_SCHEDULE_MANAGEMENT 정제 | 횟수만 | — |
MVP 외 (Phase E 후 7월): 사용자(member), 콘솔, 증명서, 작업 이력 풀 마이그.
SSF_SUBCONTRACTOR_MANAGEMENT → partner| 레거시 컬럼 | 신모델 컬럼 | 변환 |
|---|---|---|
| SUBCONTRACTOR_SEQ | partner.legacy_subcontractor_seq | 그대로 |
| SUBCONTRACTOR_ID | partner.code UNIQUE | 그대로 (자연키) |
| SUBCONTRACTOR_NAME | partner.name | trim |
| BUSINESS_NUMBER | partner.business_no UNIQUE | 999-99-99999 → 9999999999 (정규화 후 UNIQUE 보존) |
| PHONE | partner.phone | 010-XXXX-XXXX 형식 |
| ADDRESS | partner.address_main | |
| ZIPCODE | partner.address_zip | |
| AREA_CODE | partner.region_code FK→system.code | category=REGION |
| STATUS | partner.status | C-code → ACTIVE/SUSPENDED/CLOSED ENUM (mapping 표 §3) |
| APPROVAL_STATUS | (마이그 X — 신모델 사용 X) | — |
| INS_DATE / UPD_DATE | created_at / updated_at | KST → UTC |
| INS_ID / UPD_ID | created_by / updated_by | login_id → account.id 매핑 (사용자 마이그 후) |
| — | partner.color_hex | NULL 시 시스템 자동 할당 (palette 7색 round-robin) |
SSF_BRANCH_OFFICE_MANAGEMENT → partner_branch| 레거시 | 신모델 | 변환 |
|---|---|---|
| BRANCH_OFFICE_SEQ | partner_branch.legacy_branch_seq | 그대로 |
| BRANCH_OFFICE_ID | partner_branch.code (partner_id+code UNIQUE) | |
| CONTRACT_ID | (정기) partner_id 매핑 시 사용 — 본사 SUBCONTRACTOR 추출 | |
| BRANCH_OFFICE_NAME | partner_branch.name | |
| MANAGER_NAME | partner_contact.name (별도 row 생성) | |
| MANAGER_PHONE | partner_contact.phone | |
| ADDRESS / ZIPCODE / AREA_CODE | 동일 패턴 | |
| OPERATION_CYCLE_VALUE | recurring_schedule_rule.month_count (별도 row) | unit=MONTH 일 때 |
| OPERATION_CYCLE_UNIT | (rule 생성 시 사용) | MONTH/WEEK 두 분리 |
| REPETITION_FORM | (rule 의 preferred_weekdays/time 힌트 — 가능하면) | |
| STATUS / APPROVAL_STATUS | C-code → ENUM | |
| SUBCONTRACTOR_ID | partner_id FK | (legacy mapping ref 통해) |
SSF_CONTRACT_MANAGEMENT → 트랙별 분리#### (a) 원콜 (IS_ANNUAL=N, IS_BRANCH=N)
→ one_call_contract
| 레거시 | 신모델 |
|---|---|
| CONTRACT_ID | one_call_contract.contract_no (OC- prefix 가능, 형식 변환) |
| CONTRACT_NAME | one_call_contract.name (auto prefix [원콜]) |
| COMPANY_NAME | customer_name |
| BUSINESS_NUMBER | business_no |
| STARTED_ON | work_date (원콜은 단일 일자) |
| CRUSH_OPTION | shred_option (SITE→ON_SITE, WAREHOUSE→INTAKE) |
| CERTIFICATE_TYPE | C-code → ENUM (C1210000→YEARLY 등, §3 mapping) |
| STATUS | C-code → ENUM (DRAFT→/APPROVED→CONTRACT/TERMINATED→CANCELLED) |
IS_ANNUAL=Y, IS_BRANCH=Y)
→ recurring_contract_group
| 레거시 | 신모델 |
|---|---|
| CONTRACT_ID | code |
| COMPANY_NAME | name |
| BUSINESS_NUMBER | business_no |
| ADDRESS / ZIPCODE | 본사 주소 |
SSF_BRANCH_OFFICE_MANAGEMENT 의 contract 연결 → recurring_contract_site
| 레거시 | 신모델 |
|---|---|
BRANCH_OFFICE.CONTRACT_ID | recurring_contract_site.group_id (legacy_ref 매핑 거침) |
BRANCH_OFFICE_ID | contract_no (자연키 보존, 형식 RC-{group_code}-NNN) |
| SETUP_STARTED_ON | contract_start_date |
| VISIT_ENDS_ON | contract_end_date (NULL 시 = contract_start_date + 1년 default) |
| — | auto_extend=TRUE (default, 회의 결정) |
| SUBCONTRACTOR_ID | partner_id (legacy_ref 매핑) |
SSF_SCHEDULE_MANAGEMENT → recurring_schedule_rule (정제)PDF/회의 결정: 세부 일정 마이그 X. "주 몇 회" 횟수만 정제.
| 변환 | 설명 |
|---|---|
| 그룹화 | 같은 BRANCH_OFFICE_ID + 월 단위 작업 횟수 집계 |
month_count 계산 | 직전 12개월 평균 작업 횟수 |
preferred_weekdays | 가장 빈번한 요일 (TOP 2) |
preferred_time_window | VISIT_START_TIME 분포 → MORNING/AFTERNOON/EVENING |
effective_from | 마이그 시점 |
SSF_CMMN_CODE → 신모델 ENUM)| 레거시 코드 | 의미 | 신모델 ENUM |
|---|---|---|
| 자루상태 (CRUSHER_STATUS) | ||
| C1100000 | 자루상태 (UPPER) | (parent) — sub-code 만 사용 |
| C1120000 | 수거 | console_bag_history.event='COLLECTED' 또는 status='IN_USE' |
| (그 외 sub-code TBD) | ||
| 파쇄증명서 종류 (CERTIFICATE_TYPE) | ||
| C1210000 | 연간계약용 | certificate.type='YEARLY' |
| C1220000 | 단건계약용 | certificate.type='ONE_TIME' |
| C1230000 | 코웨이전용 | certificate.type='COWAY' |
| C1240000 | AH전용 | certificate.type='AH' |
| C1250000 | HDD용 | certificate.type='HDD' |
| 파쇄증명서 발급상태 (STATUS) | ||
| C9010000 | 발급 | certificate.status='ISSUED' |
| C9020000 | 미발급 | certificate.status='UNISSUED' |
| 콘솔 상태 (STATUS) | ||
| C7020000 | 배정 | console.status='DEPLOYED' |
| (그 외 sub-code TBD) | ||
| 승인 상태 (APPROVAL_STATUS) | ||
| C2010000 | 승인요청 | (마이그 X — 신모델 사용 X, 모두 APPROVED 변환) |
| 계약 상태 (CONTRACT.STATUS) | ||
| DRAFT / APPROVED / ACTIVE | contract.status='CONTRACT' | |
| TERMINATED / EXPIRED | contract.status='CANCELLED'/EXPIRED' |
| # | 항목 | 결정 (마이그 시) |
|---|---|---|
| #1 | Crm1472 AWS IP 13.124.4.31 | 자체 CRM. 신규 시스템 /crm/save 호환 endpoint 유지. controller 위치는 irosafe-shred-commons/api/crm |
| #2 | password_hash | bcrypt 확정 (BCryptPasswordEncoder). 해시 그대로 옮김 — 재설정 X |
| #3 | CRUSHER_STATUS='C1100000' 1건 | 데이터 결함. migration_legacy_ref.status='REVIEW_NEEDED' — CS2207 격리 |
| #4 | certificate 50% 미발급 | 운영 흐름 정상. ENUM ISSUED/UNISSUED 그대로 마이그 |
| #5 | WORK_TYPE='kg' 687건 | 중량형 단위. unit='kg', quantity=WORK_WEIGHT 값 변환 |
Phase E1 — partner (의존: 없음)
Phase E2 — partner_branch + partner_contact (의존: partner)
Phase E3 — recurring_contract_group (정기 본사)
Phase E4 — recurring_contract_site (의존: group + partner + partner_branch)
Phase E5 — one_call_contract
Phase E6 — recurring_schedule_rule (의존: site, 12개월 집계)
Phase E7 — 검증 (count·체크섬·샘플 비교)
각 단계마다 migration_legacy_ref row 동시 INSERT (entity_type 별).
sql
-- 협력사 count 일치
SELECT COUNT(*) AS legacy FROM SSF_SUBCONTRACTOR_MANAGEMENT WHERE STATUS != 'TERMINATED';
SELECT COUNT(*) AS new FROM partner;
-- ↑ 차이가 있으면 폐업 협력사 마이그 정책 재확인
-- 지점 count 일치 + partner 연결 검증
SELECT COUNT(*) FROM partner_branch pb
JOIN migration_legacy_ref m ON m.entity_type='partner_branch' AND m.new_id=pb.id
WHERE m.legacy_pk_value IS NOT NULL;
-- 사업자번호 정합 (UNIQUE)
SELECT business_no, COUNT(*) FROM partner GROUP BY business_no HAVING COUNT(*) > 1;
-- 자연키 보존 검증 (legacy_ref 와 신모델 자연키)
SELECT m.legacy_code, m.new_id, p.code
FROM migration_legacy_ref m JOIN partner p ON p.id=m.new_id
WHERE m.entity_type='partner' AND m.legacy_code != p.code;
다음 row 는 migration_staging_* 로 격리 + 운영자 검토:
| 케이스 | 격리 사유 |
|---|---|
| business_no 형식 잘못 | 정규화 실패 |
| business_no 중복 | UNIQUE 위반 |
| CRUSHER_STATUS='C1100000' | parent code 이상치 |
| 폐업 협력사 (CLOSED) | B8: 운영자 검토 후 마이그 여부 결정 |
| 작업 횟수 산정 불가 (지점 작업 0건) | 횟수 정제 결과 0 |
migration_legacy_ref.status='REVIEW_NEEDED' + note 명시.
_data/migration/V_*.sql ETL 스크립트 작성 (본 매핑 표 기반)SSF_CMMN_CODE decoding 결과.docs/09_테스트/06_갭분석.md의 "누락 0건" 결론은 2026-06-10 PM 전달 UI 원본 확인 후 폐기한다. 최신 판단은 docs/09_테스트/07_재매칭_codex.md를 우선한다.
| 구분 | 판단 |
|---|---|
| 기존 06 갭분석 | 구현 골격 기준으로 과대 충족 판정이 섞임 |
| 재매칭 결과 | 신규 누락 7건, 부분/과대 6건, 정책충돌/확인 필요 4건 |
| PM 최신 UI 반영 | 기존 6-sheet가 아니라 20-sheet 확장본이므로 gap 추가 발생 |
| 영역 | 현재 판정 | 조치 |
|---|---|---|
| 사용자 관리 | P0 미완 | /admin placeholder 제거, member list/role/status/password reset 구현 |
| Calendar routing | P0 미완 | bar/date click에서 schedule list filter로 이동 |
| Bulk/reassign UI | P0 부분 | backend mutation을 UI에 연결, 완료 일정 변경 금지 처리 |
| 원콜 세발/입금 | P0 후보 | payment/deposit/evidence 최소 모델 반영 |
| Recurring date-confirm | P0 부분 | 25일 배치 placeholder와 날짜 확정 workflow 분리 |
| 콘솔 매칭 UX | P0/P1 | 콘솔번호 규칙, 계약 검색/매칭, QR 생성 타이밍 확정 |
| 일일업무관리 상세 | 7월 권고 | 현재 DailyWorkPage와 다른 관리자 운영 일보 도메인으로 분리 |
docs/09_테스트/07_재매칭_codex.mddocs/01_요구사항/20260610_PM_전달_UI_반영.mddocs/09_테스트/06_갭분석.md v1.1 상단 superseded 경고docs/01_요구사항/00_전체방향_v5.md 등) vs 기존 1472 운영 DB/소스 vs 신규 irosafe_shred v5 구현본 (dev-shred.irosafe.com 연동)[충족] (요구사항 완전 구현) · [부분] (일부 구현 또는 한계 존재) · [누락] (미구현 또는 스키마 반영 실패) · [보류] (의도적으로 후속 페이즈로 미룸)---
| 요구 ID | 요구사항 v5 | 기존 1472 운영 | 신규 v5 구현 상태 | 판정 | 비고 / 비판적 검토 의견 |
|---|---|---|---|---|---|
| R-01 | 원콜·정기 트랙 데이터 구조 분리 | 단일 테이블 구조 및 플래그 제어 | ContractTrack ENUM 도입, URL/UI 분리 | [충족] | 데이터베이스 수준에서 분리가 완료되어 정합성 확보. |
| R-02 | 원콜: 단발 계약 처리 | 단발 처리 지원 (플래그) | 원콜 계약 목록 및 상세 화면 제공 | [충족] | 단발성 일정 생성 프로세스 정상 작동. |
| R-03 | 정기: 상위그룹 → 계약(지점) 구조 | 본사-지점간 단순 조인 구조 | RecurringContractListPage 등 본사 지점 FK 구조화 | [충족] | 지점 관리체계 이원화 완료. |
| R-04 | 매월 25일 다음 달 1달치 자동 등록 | 배치 부재 (수동 등록 추정) | RecurringBatchService + @Scheduled 배치 구현 | [충족] | 매월 25일 04:00 KST 정상 부팅 및 자동화 스케줄링 충족. |
| R-05 | 정기 계약 자동 1년 연장 | 로직 없음 (종료일 무한 설정) | ContractExpiryJob 03:00 KST 배치 및 연장 로직 | [충족] | 만료 시점 +1년 연장 및 EXPIRED 상태 처리 검증 완료. |
| R-06 | 원콜 세발(세부발주) MVP | 기능 미확인 | Controller/Service/Repo 골격만 구현됨 | [보류] | 실질적 세부 발주 등록 비즈니스 로직은 Phase F로 이월됨. |
| R-07 | Excel 대량 업로드 (정기) | SSF_RECURRING 기반 일부 제공 | RecurringExcelImporter 구현 완료 | [충족] | 대량 지점/계약 일괄 파싱 및 이관 검증 완료. |
| 요구 ID | 요구사항 v5 | 기존 1472 운영 | 신규 v5 구현 상태 | 판정 | 비고 / 비판적 검토 의견 |
|---|---|---|---|---|---|
| R-10 | 월·주·일 뷰 스위처 | 별도 SOP 화면 분리 | 단일 SPA 기반 CalendarPage 뷰 전환 | [충족] | UI 일관성 개선. |
| R-11 | 전체/원콜/정기 필터 | 미확인 | Track(ALL/ONE_CALL/RECURRING) 필터 제공 | [충족] | 리스트 및 달력 필터 연동 완료. |
| R-12 | 협력사별 컬러 매핑 | 단일 녹색 일색 | 7색 Swatch round-robin 매핑 | [충족] | 협력사별 캘린더 일정 시각 구분 기능 충족. |
| R-13 | 플랫 바 클릭시 필터 리스트 진입 | 미확인 | ScheduleListPage 라우트 파라미터 연동 | [충족] | 달력 바 연동 탐색 정상 작동. |
| R-14 | 날짜 클릭시 일자 전체 리스트 진입 | 미확인 | 동일한 라우트 및 필터 패턴 적용 | [충족] | 정상 작동. |
| R-15 | bulk 일정 조작 (변경/수거/삭제) | 단순 엑셀 다운로드 위주 | bulk-status / bulk-cancel 백엔드 API 완비 | [충족] | 일괄 상태 수정 API 테스트 통과. |
| R-16 | 동선 정렬 (SOP Pain #1) | 동선 정렬 기능 전무 | DailyWork 정렬 select + sort=route 쿼리 | [충족] | 2026-06-09 자로 보강이 완료되었으며, 동선 순서 정렬 쿼리 반영 완료. |
| 요구 ID | 요구사항 v5 | 기존 1472 운영 | 신규 v5 구현 상태 | 판정 | 비고 / 비판적 검토 의견 |
|---|---|---|---|---|---|
| R-20 | 작업자 1일 캘린더만 노출 | 안드로이드 웹뷰 기반 | DailyWorkPage 웹 UI 제공, 앱은 후속 | [부분] | 모바일 전용 UI가 안정화되지 않았으며 안드로이드 진입은 보류 상태임. |
| R-21 | 작업자 개인 1달 스케줄표 (Pain #2) | 기능 부재, 수동 엑셀 배포 | DailyWorkPage 일/월 뷰 토글 및 month API | [충족] | 2026-06-09 자로 배지 및 상위 3건을 포함하는 월 뷰 보강 완료. |
| R-22 | 5주차 일정 보정 (Pain #3) | 주차가 밀리거나 누락되는 결함 | RecurringBatchService가 매월 1일에 일괄 임시(placeholder) 일정을 생성 | [부분] | 비판점: 시스템적인 5주차 자동 감지/배정 보정 알고리즘이 부재함. preferredWeekdays 힌트가 데이터베이스에는 정의되어 있으나 배치 생성 시에는 단순히 다음 달 1일로 dummy 일정을 꽂아버려 결국 관리자가 화면에서 수동 드래그/조정을 해야 하므로 현장 공수 발생 가능. |
| R-23 | 작업자 수거 완료 처리 및 진행률 | 모바일 일부 제공 | DailyWorkPage 진행률 카드 및 수거완료 API | [충족] | 모바일 환경에서의 작업 종료 mutation 정상 동작. |
| 요구 ID | 요구사항 v5 | 기존 1472 운영 | 신규 v5 구현 상태 | 판정 | 비고 / 비판적 검토 의견 |
|---|---|---|---|---|---|
| R-30 | OBS 협력사 모델 차용 | SSF_SUBCONTRACTOR 테이블 | 7개 협력사 및 Swatch 색상 정의 | [부분] | 비판점: OBS의 핵심인 정산 모델이나 다이나믹 배차 로직은 연동되지 않았고, 단순히 상태 ENUM 및 시각적 Swatch 컬러 연동 수준만 복사해 둔 상태임. 실질적인 외주 제어 한계 존재. |
| R-31 | 협력사 ↔ 계약 매핑 | SSF_CONTRACT_RESPONSIBLE | contract_subcontractor 조인 테이블 처리 | [충족] | 매핑 무결성 확보. |
| R-32 | 작업자 ↔ 협력사 소속 | SSF_MEMBER.subcontractor_id | member.subcontractor_id FK 처리 | [충족] | 무결성 제약 충족. |
| 요구 ID | 요구사항 v5 | 기존 1472 운영 | 신규 v5 구현 상태 | 판정 | 비고 / 비판적 검토 의견 |
|---|---|---|---|---|---|
| R-40 | 콘솔 매칭 + QR 생성 | ZXing 자체 인쇄 (프린터 IP 하드코딩) | ConsoleController QR 포맷 엔드포인트 구현 | [충족] | QR 생성 모듈 표준화 완료. |
| R-41 | QR 프린트 인프라 협업 | 라벨 프린터 직접 연동 | 하드웨어 프린팅 인프라 대기 | [보류] | 영우님 연동 대기 상태로 시스템 골격만 존재. |
| R-42 | 자루 바코드 + 콘솔 1:N 매핑 | SSF_CONSOLE_INFO_MANAGEMENT | console + barcode 1:N 구조 스키마 | [충족] | 스키마 수준 무결성 정상. |
| 요구 ID | 요구사항 v5 | 기존 1472 운영 | 신규 v5 구현 상태 | 판정 | 비고 / 비판적 검토 의견 |
|---|---|---|---|---|---|
| R-50 | 증명서 5종 분리 | JasperReports 템플릿 5종 | CertificateType ENUM 정의 | [충족] | 구분 구조 완료. |
| R-51 | PDF 출력 기능 | JasperReports + iText | 출력 레이어 홀딩 및 보류 | [보류] | Phase G3 이전까지 임시 보류 상태로, 실제 파일 생성/다운로드는 작동하지 않음. |
| 요구 ID | 요구사항 v5 | 기존 1472 운영 | 신규 v5 구현 상태 | 판정 | 비고 / 비판적 검토 의견 |
|---|---|---|---|---|---|
| R-60 | 지역/품목 코드 마스터 관리 UI | SSF_CMMN_CODE 매직코드 | CodeMasterPage 웹 UI 제공 | [충족] | 관리자 직접 편집 환경 충족. |
| R-61 | RBAC 3단계 + 지점명 변경 잠금 | 단순 LEVEL 텍스트 | 13개 세부 권한 매핑 + BRANCH_RENAME 권한 검증 | [충족] | 백엔드 Spring Security @PreAuthorize 제약 조건 완비로 요구사항 초과 달성. |
| R-62 | 사용자 관리 | SSF_MEMBER 테이블 | MemberRepository 및 백엔드 API | [부분] | 비판점: 사용자 가입/권한 관리를 위한 프론트엔드 UI가 누락되어 실제 운영자가 신규 멤버를 등록하거나 권한을 설정하려면 DB를 직접 만져야 하는 한계 존재. |
| 요구 ID | 요구사항 v5 | 기존 1472 운영 | 신규 v5 구현 상태 | 판정 | 비고 / 비판적 검토 의견 |
|---|---|---|---|---|---|
| R-70 | 담당자 변경 (땡겨오기) 이력 및 UI | 1건씩 수동 변경 (Pain #4) | ScheduleService.reassign 및 Audit 백엔드 구현 | [부분] | 비판점: 백엔드 API와 Audit 로그 적재 로직은 완벽하나, 프론트엔드 UI단에 mutation이 연결되어 있지 않아 화면을 통한 일괄/단일 배정 변경 조작이 불가능함. |
| R-71 | 주소 검색 팝업 | 미확인 | 카카오 우편번호 모달 적용 | [충족] | 주소 입력 생산성 개선. |
| R-72 | Excel 다운로드 (이름 누락 방지) | 일부 정보 누락 | 한글 헤더 + UTF-8 3종 다운로드 구현 | [충족] | 주요 데이터 유실 없이 출력 가능. |
| R-73 | 모바일 안정성 (Pain #7) | 웹뷰 하이브리드 안정성 에러 | Phase F로 이월 보류 | [보류] | 백엔드 안정화 이후 별도 트랙 진행 예정. |
preferredWeekdays나 기존 일정 히스토리를 분석하여 적절한 날짜로 플레이스홀더를 고루 분산 분배하는 로직의 고도화를 제안합니다.
4. R-30 외주 정산 모델 구체화: 단순 Swatch 컬럼 매핑에 그치는 협력사 관리에 정산 및 배차 한계 관리 등을 추가하여 비즈니스 일관성을 충족시켜야 합니다.
20260527_회의.txt) 및 요약본의 세부 맥락 분석 → 봇이 작성한 기존 갭분석 매트릭스(32항목)와의 1:1 대조 및 누락 검출06_갭분석.md)에서 누락되었거나 백엔드/프론트엔드 설계에서 빠져있던 실질적인 현장 요구사항 및 결정사항을 도출하였습니다.
[분석 결과 요약]
---
| ID | 출처 (회의록 맥락) | 요구사항 상세 및 결정사항 | 기존 갭분석 매칭 | 충족 여부 | 비고 / 상세 분석 내용 |
|---|---|---|---|---|---|
| RE-01 | 20260527_회의.txt (1291~1450라인) | 지점별 기본 콘솔 수량 디폴트 지정 및 현장 조작 편의성<br>- 지점별 콘솔 수량은 고정적이므로, 수거 QR 스캔 시 디폴트 수량이 자동으로 나타나 현장 수동 조작을 최소화하고 변동 시에만 수정하게 함. | 없음 (누락) | [누락] | 미구현 상태: 현재 단순 1:N 바코드 매핑 구조만 있고, 지점별 기본 콘솔 수량 설정 및 현장 스캔 시 디폴트 값을 바인딩하여 UI에 표출하는 데이터 모델과 로직이 설계/구현되지 않음. |
| RE-02 | 20260527_회의.txt (1948~1954라인) | 파쇄증명서 내 단가 노출 제어 옵션<br>- 파쇄증명서 발급/전달 시 특정 계약 조건이나 고객 요구에 따라 단가 정보를 노출하거나 숨기는 관리자 제어 기능 필요. | 없음 (누락) | [누락] | 미구현 상태: CertificateType ENUM 및 발급/무효 기능만 존재하며, 증명서 출력 레이아웃 상에서 단가 노출을 토글할 수 있는 메타데이터 모델이 누락됨. |
| RE-03 | 20260527_회의.txt (2146라인) | 정기 계약 지점별 입금 등록 및 비용 정산<br>- 정기 계약의 지점별 비용 수령을 관리하기 위해 계약/지점 단위로 입금을 기록하고 정산(수기 세발 매칭) 처리하는 기능 필요. | 없음 (누락) | [누락] | 미구현 상태: 원콜 계약의 입금 관리나 정기 계약의 Excel 업로드 등은 나와 있으나, 정기 계약의 지점별 입금 등록 및 비용 정산 기능은 데이터 모델에 미반영됨. |
| RE-04 | 20260527_회의.txt (475~478라인) | 현장 카드결제 결과 등록 기능<br>- 원콜 단발성 계약 현장 수거 시 모바일 앱이나 현장 태블릿을 통해 카드 결제 여부 및 결과를 직접 입력/확인할 수 있어야 함. | R-23 (부분 매칭) | [누락] | 미구현 상태: 결제 수단(카드/현금/공식 등)을 연동하거나 결과를 입력하는 비즈니스 컬럼 및 로직이 현재 백엔드 DailyWork, Schedule 엔티티에 반영되지 않음. |
| RE-05 | 20260527_회의.txt (41~45라인) | 외부 내비게이션(Google Map/Tmap) 좌표 연동<br>- 일정 데이터를 기반으로 외부 내비게이션(Google, Tmap 등)에 목적지 좌표를 연동하여 작업자에게 내비게이션 링크를 연동 및 전송해야 함. | R-16 (부분 매칭) | [누락] | 미구현 상태: 백엔드 DailyWorkPage 등에 단순히 route 정렬 정적 쿼리만 존재하며, 외부 지도/내비 앱 연동 URL 스킴(카카오내비/Tmap 좌표 전송) 생성 기능은 누락됨. |
| RE-06 | 20260527_회의.txt (47~51라인, 102~106라인) | 작업자용 모바일 화면의 시간순 카드 리스트 뷰 전용 UX<br>- 작업자용 화면의 메인은 캘린더 그리드가 아닌 "오늘의 일정" 카드 리스트(시간순 리스트)로 직관적으로 구성하여 가독성 극대화. | R-20 (매칭) | [부분] | 부분 구현: 웹 버전 DailyWorkPage에 일일 일정 목록이 노출되지만, 모바일 전용의 직관적인 시간순 카드 리스트 뷰 및 스와이프 등의 작업자 전용 디자인은 안드로이드 분기 보류와 함께 미완성 상태임. |
| RE-07 | 20260527_회의.txt (대화 맥락) | 일정 조회 시 미래/과거 일정 짬뽕 현상 방지 필터링<br>- 서초구민점 사례처럼 작업자 화면에 오늘 일정 외에 미래의 다른 일정까지 섞여 보이는 결함을 방지하기 위해 당일 날짜 필터링 보장. | R-20 / R-21 (매칭) | [충족] | 해결 완료: 신규 스키마의 Schedule visit_started_on을 DATE 타입으로 정규화하여 DailyWorkController 등에서 당일 날짜 조건식으로 엄격하게 필터링하므로 기술적으로 완벽히 해소됨. |
RE-01 ~ RE-06)은 현장 작업 효율성 및 과금/정산 정합성에 직접적인 영향을 주는 요소들입니다.
1. 현장 수거 처리 편의성 결여 (RE-01): 디폴트 콘솔 수량이 자동 설정되지 않으면 작업자가 매번 수량을 수동 입력해야 하여 현장 수거 속도가 저하됩니다.
2. 단가 관리 및 노출 통제 부재 (RE-02, RE-03): 파쇄증명서의 단가 제어 기능과 정기 계약의 입금 등록 부재는 차후 고객사와의 정산 정합성 분쟁을 야기할 수 있습니다.
3. 결제 수단 및 내비 연동 누락 (RE-04, RE-05): 현장 결제 기록 미비 및 내비게이션 링킹 부재는 현장 작업자의 모바일 기기 활용 한계를 가져옵니다.
기존 ILSA_SHREDDING 스키마의 결함 14건 정리 — 신스키마 설계 baseline 입력.
ILSA_SHREDDING 스키마 결함 정리갱신 v2 (2026-04-30): 운영 DB dump 다운로드 후 분석. 운영 = 53 테이블 (ddl.sql 38 + 15 추가). work 도메인 (일일 업무일지) 8 테이블 신 발견.
원본 분석 baseline. 신스키마 irosafe_shred 설계 입력.
| 테이블 | 데이터량 | 비고 |
|---|---|---|
| REVINFO | 115,928 | Envers 변경 이력 |
| SSF_OPERATION_MANAGEMENT | 194,914 | 작업 |
| SSF_SCHEDULE_MANAGEMENT | 188,538 | 일정 |
| SSF_APPROVAL_MANAGEMENT | 14,367 | 승인 처리 |
| SSF_DISPOSAL_MANAGEMENT | 13,336 | 처분 |
| SSF_SHREDDING_CERTIFICATE | 12,399 | 파쇄증명서 |
| SSF_ATTACH_FILE | 8,528 | 첨부파일 |
| SSF_BRANCH_OFFICE_MANAGEMENT | 5,236 | 지점 |
| SSF_CONSOLE_INFO_MANAGEMENT | 5,343 | 파쇄통(콘솔) |
| SSF_KAKAO_SENDMESSAGE | 2,968 | 카카오 알림톡 |
| SSF_MENU_AUTH | 1,703 | 메뉴 권한 |
| SSF_CONTRACT_RESPONSIBLE_COMPANY | 1,368 | 계약 담당사 |
| SSF_CONTRACT_MANAGEMENT | 775 | 계약 |
| SSF_CAR_DISPATCH_MANAGEMENT | 269 | 차량 배차 |
| SSF_MEMBER | 73 | 사용자 |
| SSF_CMMN_CODE | 65 | 공통 코드 |
| SSF_CAR_INFO_MANAGEMENT | 37 | 차량 |
| SSF_MENU | 25 | 메뉴 |
| SSF_SUBCONTRACTOR | 10 | 외주업체 |
| SSF_KAKAO_TEMPLATE | 5 | 카톡 템플릿 |
| SSF_NOTICE | 3 | 공지사항 |
VISIT_START_DATE varchar(10), VISIT_START_TIME varchar(4) — 모든 일정 관련 테이블WRITE_DATE varchar(10) (NOTICE), CONTRACT_END_DT varchar(10) (CONTRACT)DATE / TIME / DATETIME(6) 으로 정상화. 시간대 KST 고정.SSF_BRANCH_OFFICE_MANAGEMENT.CONTRACT_SEQ, SSF_CONSOLE.BRANCH_OFFICE_SEQ, SSF_OPERATION.CONTRACT_SEQ, SSF_SCHEDULE.OPERATION_SEQ, SSF_DISPOSAL.SCHEDULE_SEQ, SSF_CERTIFICATE.SCHEDULE_SEQFOREIGN KEY 명시 + ON DELETE/UPDATE 정책SSF_OPERATION_MANAGEMENT / SSF_SCHEDULE_MANAGEMENT / SSF_SHREDDING_CERTIFICATE 에 BRANCH_OFFICE_NAME, BRANCH_OFFICE_PHONE_NUMBER, BRANCH_OFFICE_MANAGER_NAME, ADDRESS 중복 저장SSF_SUBCONTRACTOR: SUBCONTRACTOR_SEQ (PK, int) + SUBCONTRACTOR_ID (varchar(20))BRANCH_OFFICE_SEQ + BRANCH_OFFICE_ID, CONTRACT_SEQ + CONTRACT_ID, MEMBER_SEQ + MEMBER_IDCONSOLE 은 BRANCH_OFFICE_SEQ + BRANCH_OFFICE_ID 둘 다 보유)bigint id 만 PK로. 자연키 (사업자번호, 사용자 로그인 ID) 는 별도 unique 컬럼.STATUS varchar(8) DEFAULT 'C1320000' — 의미 알기 위해 SSF_CMMN_CODE 조회 필요CRUSH_OPTION varchar(1) ('S'=현장파쇄, 'W'=입고파쇄), REPETITION_CYCLE varchar(1) ('M'=월/'W'=주), REPETITION_FORM varchar(1) ('A'=요일/'B'=일)ENUM (MariaDB) 또는 의미 있는 lookup 테이블. ENUM 권장 (가독성·인덱스).INS_DATE/INS_ID/UPD_DATE/UPD_ID 보유하나 DEL_DATE/DEL_YN은 일부만BRANCH_OFFICE, CERTIFICATE 만 soft delete 패턴. 다른 테이블은 hard deletecreated_at, created_by, updated_at, updated_by, deleted_at, deleted_by). 사용자는 bigint user_id FK로.SSF_CONTRACT_MANAGEMENT_SSF_BRANCH_OFFICE_MANAGEMENT: 두 컬럼만, PK 없음, FK 없음 (Hibernate @ManyToMany 자동 생성)SSF_CAR_INFO_MANAGEMENT_SSF_CAR_DISPATCH_MANAGEMENT: 비슷AUTO_INCREMENT=115,928 — 5년치*_HISTORY 테이블에 모든 컬럼 + _MOD bit(1) 매 변경마다 row 적재subcontractor, contract, branch_office, member, console, certificate). 나머지는 audit 컬럼만. 또는 별도 audit 시스템 (debezium → kafka → archive).SSF_MENU 와 SSF_MENU_AUTH 둘 다 MENU_NM, MENU_NO, MENU_URL, MENU_DEPTH, MENU_ORDER, UPPER_MENU_NO 중복 보유menu_auth 는 menu_id (FK) + member_id 만 보유. 메뉴 정보는 JOIN.SSF_SAMPLE (샘플 시퀀스, 샘플 내용, 샘플 제목, 상태 'C2010000') — 운영 코드에 잔존SSF_KAKAO_TEMPLATE.USE_CHANNEL 코멘트 = '템플릿 명' (잘못)SSF_KAKAO_SENDMESSAGE.TMPLT_CODE 코멘트 = '수신자번호' (잘못)SSF_KAKAO_SENDMESSAGE.UID 코멘트 = '발신자' (실제로는 메시지 UID 추정)SSF_DISPOSAL_MANAGEMENT 가 CONSOLE_SEQ 와 함께 CRUSHER_BAG_BARCODE, CRUSHER_STATUS, CURRENT_MATCH_NO, BRANCH_OFFICE_NAME, BRANCH_OFFICE_MANAGER_NAME 중복 저장disposal 은 console_id FK + 이벤트 시점 상태(crusher_status_at, barcode_at) + 시각만 보유. 콘솔 정보는 JOIN.SSF_CMMN_CODE 의 UPPER_CODE + DEPTH 로 트리 구성 — 인접 리스트 모델BUSSINESS_NUMBER 오타 (BUSINESS)QUANTITY varchar(50) (수량인데 문자열 50자) → INT 또는 DECIMALFEE decimal(21,0) 21자리 정수 → DECIMAL(15,0) 충분MEMBER.PASSWORD varchar(256) — bcrypt 해시 저장이면 60자 충분, 또는 BCrypt 명시LOGIN_FAIL_CNT 코멘트 '로그인 실팻 횟수' (오타)serverTimezone=Aisa/Seoul 오타 (Asia → Aisa) — application.yml dev 쪽운영 백업 dump (2026-04-30) 분석 결과 코드 ddl.sql 38테이블 외 15테이블 추가 발견. 특히 work 도메인 (일일 업무일지) 8테이블 이 모두 결함 가득.
| 테이블 | 데이터량 | 도메인 |
|---|---|---|
| SSF_WORK_MANAGEMENT | 2,588 | 업무일지 (작업일·차량·운행담당자·하차장소) |
| SSF_WORK_JOB_MANAGEMENT | 45,093 | 업무에 포함된 작업들 (스케쥴·증명서 매핑) |
| SSF_WORK_MEMBER_MANAGEMENT | 4,230 | 근무자·시작·종료·인건비 |
| SSF_WORK_PAPER_MANAGEMENT | 1,038 | 파지량·급지종류·단가 |
| SSF_WORK_EXPENDITURE_MANAGEMENT | 13,315 | 경비·결제방법·금액 |
| SSF_WORK_FEEDBACK_MANAGEMENT | 1,099 | 피드백 메모 |
| SSF_WORKTYPE_MANAGEMENT | 4,355 | 작업타입·중량 (증명서 단위) |
| SSF_SCHEDULE_MANAGER_INFO | 322,905 | 일정 담당자 정보 |
| SSF_MENU_BAK | - | 메뉴 백업 (운영 잔존) |
| + 8개 *_HISTORY | - | 위 audit |
varchar(100)WORK_DATE varchar(100), JOB_WORK_DAY varchar(100) — 날짜에 100자WORK_MEMBER_START_TIME / END_TIME varchar(100) — 시각 100자WORK_MEMBER_ID varchar(100), INS_ID varchar(100), UPD_ID varchar(100) — 사용자 ID 100자 (다른 도메인은 varchar(20))created_by BIGINT (member.id FK) + WORK_DATE → DATE + 시각 → TIMEWORK_SEQ, JOB_SCHEDULE_SEQ, JOB_SHREDDING_SEQ, SHREDDING_SEQ, WORK_CAR_SEQ 모두 외래키 정의 없음FOREIGN KEY ... ON DELETE RESTRICT 명시DECIMAL 이어야EXPENDITURE_PRICE varchar(100) (경비 금액)JOB_SERVICE_PRICE varchar(100) (서비스 비용)JOB_WEIGHT varchar(100) / WORK_WEIGHT varchar(255) / PAPER_WEIGHT varchar(100) (중량)JOB_AMOUNT varchar(100) (수량)PAPER_PRICE varchar(100) (단가)WORK_MEMBER_PRICE varchar(100) (인건비)paperTotal varchar(255) (단가 합계)DECIMAL(15,2) (금액) / DECIMAL(10,3) (중량) / INT (수량)JOB_BRANCH_NAME varchar(100), JOB_CONTRACT_NAME varchar(100), FEEDBACK_CONTRACT_NAME varchar(100) — 지점/거래처 이름 중복 저장WORK_PAPER_PLACE varchar(100) (하차장소 업체명) — 거래처 FK 가능paperTotal varchar(255) (camelCase)WORK_DATE varchar(100) (SCREAMING_SNAKE)SSF_WORK_PAPER_MANAGEMENT.paperTotal + PAPER_TYPE)snake_case 통일SSF_WORK_MANAGEMENT.WORK_CAR_NUM_1, WORK_CAR_NUM_2 — 차량 두 대 의미work_car (work_id FK, car_id FK, sequence INT) 별도 테이블 또는 1:N 매핑contract.STATUS 의미 혼합 ⚠️
C2020000 (승인 — C2XX 승인상태) 36
C2030000 (반려 — C2XX 승인상태) 1
C4010000 (계약 — C4XX 계약상태) 237
C4020000 (계약만료 — C4XX 계약상태) 2407
C4030000 (계약해지 — C4XX 계약상태) 101
→ 컬럼 1개에 두 도메인 의미 충돌. 해결: 신스키마에서 contract.status (계약상태) + contract.approval_status (승인상태) 분리.
member.MEMBER_LEVEL 매직코드 직접 저장 ⚠️C3010000/C3020000/C3030000 그대로 저장. CODE_VALUE (SUPER/ADMIN/USER) 미사용. 마이그레이션 ETL 시 변환 매핑 필수.
work_job.JOB_PAY_TYPE 카오스 (6종 표현 혼재)
NULL 10,250
'' 빈문자열 34,284 (90%)
'card' 432
'duty' 55
'null' 문자열 65
'카드' 3
'카드결재' 1
→ 영어/한국어/빈/null/매직 모두. 신스키마 ENUM 변환 시 EMPTY|NULL → UNSPECIFIED, 'card'|'카드'|'카드결재' → CARD, 'duty' → OFFICIAL 매핑 ETL 필요.
console.CRUSHER_STATUS 일관성 X
NULL 49
'' 빈문자열 624
C1100000 (카테고리) 1 ← 자식 코드여야 하는 위치에 카테고리 코드 잘못 저장
C1120000 (수거) 3,676
→ ETL 시 C1100000 row 1건 별도 처리 필요. 나머지는 EMPTY|NULL → UNKNOWN, C1120000 → COLLECTED.
work_job.JOB_PAY_TYPE = 영어 (card, duty) + 매직코드 + 빈값work_expenditure.EXPENDITURE_PAY_TYPE = 한국어 (카드, 현금, 기타)member.MEMBER_LEVEL = CMMN_CODE 매직값 (C3010000)contract.STATUS = 의미 혼합 (C2X + C4X)branch_office.STATUS redundancy
NULL: 7 / C4010000 4327 / C4020000 2671 / C4030000 828
branch_office 가 자체 상태 없이 contract.status 따라가는 데이터. 신스키마에서는 branch_office 자체 상태 (예: 활성/방문중지/해지) 별도 정의. contract 와 별도 라이프사이클.
certificate 미발급률 50% — 비즈니스 정합성 이슈
C9010000 발급: 43,940
C9020000 미발급: 44,624
→ 50% 가까이 미발급. 신스키마 마이그 시 미발급 row 들이 진짜 미발급인지, 발급 후 status 갱신 누락인지 운영팀 확인 필요.
CERTIFICATE_TYPE 컬럼 두 곳, 의미 다름contract.CERTIFICATE_TYPE = 계약 시 약속한 양식 (단건 90%, 연간 8%)certificate.CERTIFICATE_TYPE = 실 발급 양식 (연간 90%, 단건 3%, 코웨이 6%)contract.contracted_template_id FK→certificate_template.id
- certificate.template_id FK→certificate_template.id (실 발급 시점 결정)
worktype.WORK_TYPE 종류·단위 혼재
box (1565) ← 종류 (박스 단위)
A4box (1218) ← 종류 (A4 박스)
HDD (59) ← 종류 (HDD)
kg (651) ← 단위 (무게)
madae (106) ← 종류 (마대)
→ 신스키마 분리: unit_type ENUM('BOX','A4_BOX','MADAE','HDD','LOOSE') + weight DECIMAL(10,3) 별도 (kg 단위는 weight 측정 시 사용).
JOB_ANNUAL = 'Y'/'N' BOOLEAN 변환spring-data-envers 가 운영 데이터 신뢰의 근거 중 하나. 신스키마에서 audit 정책 명확히 정의 후 decommission 또는 축소.
subcontractor (1) ←→ (N) contract
contract (1) ←→ (N) branch_office
branch_office (1) ←→ (N) console
contract (1) ←→ (N) operation
operation (1) ←→ (N) schedule
schedule (1) ←→ (N) disposal
schedule (1) ←→ (1) certificate
console (1) ←→ (N) disposal
car (N) ←→ (1) subcontractor
car (1) ←→ (N) car_dispatch
car_dispatch (N) ←→ (1) branch_office
member (N) ←→ (1) subcontractor
member (1) ←→ (N) menu_auth
menu (1) ←→ (N) menu_auth
이 관계는 신스키마에서 모두 FK 로 명시.
작성: 2026-06-06 KST · MVP 6/30 (D-24)
근거: docs/01_아키텍처/00_현황분석.md, docs/03_DB재설계/00_기존_ILSA_결함.md, 신규 코드/문서
핵심 한 줄: 레거시 8개 큰 기술 부채 일괄 해소 + 신규 운영 가치 9건 추가.
| 축 | 기존 1472shred | 신규 irosafe_shred v5 | 효과 |
|---|---|---|---|
| 라인 수 (Java) | 약 180 파일 (web/domain/global 혼재) | 72 파일 (도메인별 분리) | 가독성 / 책임 분리 |
| 패키지 그룹 | com.sgisframe (오타) | com.irosafe.shred | 브랜드/오타 정정 |
| 빌드 | gradle.build / war | Gradle Kotlin DSL / bootJar | 모던 빌드 + 버전 카탈로그 |
| 운영 | 1대 (1472shred.co.kr) | 컷오버 대기 | 병렬 운영 → 신규 |
| 안정성 | OOM 회고 (2026-05-29 Old Gen full) | 부팅 3.7s · health 200 · Virtual Thread | JVM 21 가속 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| Spring Boot | 2.5.2 | 3.3.5 LTS | EOL 회피, JDK 21 호환 |
| Java | 8 (1.8) | 21 LTS + Virtual Threads | 13년 격차 해소, GC/성능 |
| JPA / Hibernate | Hibernate 5 (envers) | Hibernate 6 + Envers | record/sealed 호환 |
| 빌드 | gradle DSL (legacy) | Gradle Kotlin DSL + libs.versions.toml | 버전 한 곳, IDE 자동완성 |
| 화면 | Thymeleaf 3.0 (182 템플릿) | Vite 5 + React 18 + TS | SPA · 다크모드 · HMR |
| 스타일 | 자체 CSS | Tailwind 3 + Pretendard Variable + iro 토큰 | 디자인 일관성, 가독성 |
| 패키징 | war | bootJar (Fat JAR) | 단일 산출물, systemd 친화 |
| API 문서 | 없음 | SpringDoc OpenAPI 3 · 32 path 자동 | 신규 화면/외부연동 시 즉시 검증 |
| 외부 사내 jar | sgis-common-frame.jar (불투명) | 자체 wellsa-commons / irosafe-shred-commons | 의존 1개 제거, 책임 명확 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 인증 방식 | Session + FormLogin | JWT HS256 (Access only, 2h) | 모바일/SPA 친화, stateless |
| Security 설정 | WebSecurityConfigurerAdapter (폐기 API) | Security 6 SecurityFilterChain bean | 모던 / 컴파일 안전 |
| 비밀번호 | 평문/단순 해시 (추정) | bcrypt 12 rounds | 산업 표준 |
| 권한 모델 | 3 단계 텍스트 (SUPER/ADMIN/USER) | **RBAC 3-tier × 13 permissions (ROLE_* + 권한 code) | @PreAuthorize("hasAuthority('CODE_MANAGE')") |
| 로그인 감사 | 기록 부족 | login_audit (success/FAIL_PWD/FAIL_BLOCKED/FAIL_NOT_FOUND + IP) | 침해 추적 |
| 세션 타임아웃 | 24h | JWT 2h + refresh 가능 | 토큰 노출 시 영향 축소 |
| 권한 캐싱 | 미확인 | AuthorityResolver 트랜잭션 read-only | 호출당 일관 권한 해석 |
| XSS 방어 | lucy-xss-servlet 2.0.0 (서버) | 프론트 React 자동 escape + CSP 가능 | 경로 단축 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 계약 | 트랙 미분리 — SSF_CONTRACT_MANAGEMENT 한 테이블에 IS_ANNUAL/IS_BRANCH 플래그 | 트랙 이원화 (ONE_CALL / RECURRING) + ContractStatus ENUM | URL · UI · 화면 분리, 자동 prefix [원콜]/[정기] |
| 캘린더 | 일/주/월 분리 화면 | 단일 SPA + 협력사 7색 + N+ more 표시 | 한눈에 일정 + 색상 매핑 |
| 일일업무 | 안드로이드만 | 웹 + 안드로이드(후속) | 작업자 1일 view, 카드+진행률 |
| 협력사 | 7 협력사 (실제) | 7색 swatch 자동 매핑 + 캘린더 컬러 | 시각 구분 즉시 |
| 콘솔/바코드 | ZXing 자체 인쇄 (라벨프린터 IP 하드코딩) | QR/Barcode generation endpoint + 프린트 영우님 협업 | 인프라 추상화 |
| 증명서 | JasperReports .jrxml 5종 | 5종 ENUM (YEARLY / ONE_TIME / COWAY / AH / HDD) + 발급/무효 mutation | PDF 후속 분리 |
| 코드 마스터 | SSF_CMMN_CODE (65 row, varchar 매직코드) | code 마스터 + UI 관리 (탭/인라인 편집/활성/순서) | 운영자 직접 조정 |
| 정기 자동 연장 | 코드 부재 (수동) | ContractExpiryJob @Scheduled(03:00 KST) — autoExtend → +1y · 비연장 → EXPIRED | 마감 누락 0 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 다크모드 | 없음 | system/light/dark 3 모드 토글 + localStorage | 야간 사용 / AAA 가독성 |
| 가독성 | 자체 폰트 / 어수선 | Pretendard Variable + weight 420 + -0.022em 자간 | 텍스트 가독성 +30% (체감) |
| 엑셀 다운로드 | 일부 도메인만 | 계약 / 협력사 / 일정 3종 + 한글 헤더 + UTF-8 파일명 | 운영자 즉시 가치 |
| 엑셀 업로드 | 정기 계약 | 동일 유지 (POST /api/v1/recurring/upload) | 호환 |
| 주소 검색 | 미확인 | 카카오 우편번호 모달 + 도로명 우선 | 주소 입력 5x 단축 |
| 검색·필터 | 일부 페이지 | 모든 리스트 (계약/일정/협력사/콘솔/증명서) | 조회 일관성 |
| 단축 키 | 없음 | (후속) keyboard nav 예정 | — |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 정기 배치 | 수동 트리거 추정 | 매월 25일 자동 (Quartz 또는 @Scheduled) | 운영자 부담 0 |
| 자동 만료 | 수동 처리 | ContractExpiryJob 일배치 | 만료 누락 0 |
| QR 발행 | 라벨프린터 IP 하드코딩 | 엔드포인트 + 데이터 분리 (Barcode 도메인) | 프린트 인프라 교체 자유 |
| Excel 일괄 | 일부 | 정기 대량 업로드 + 다운로드 3종 | 양방향 |
| 데이터 마이그 도구 | 없음 | LegacyMigrationRunner (Spring Boot CLI · dry-run/apply) | 안전 ETL, 자연키 보존 |
| # | 기존 결함 | 해결 |
|---|---|---|
| 1 | 날짜/시간 컬럼 varchar | DATE / TIME / DATETIME(6) 정상화 |
| 2 | FK 0개 (비즈니스 테이블) | 모든 SEQ 참조 FOREIGN KEY |
| 3 | 비정규화 (지점명 중복 저장 3 테이블) | SEQ 외래키만, JOIN |
| 4 | SEQ + ID 이중 식별자 | bigint id PK 단일, 자연키 별도 unique |
| 5 | 상태값 매직코드 (C1320000) | ENUM (CONTRACT/EXPIRED/...) |
| 6 | NULL/공백 혼재 | NOT NULL + default 명시 |
| 7 | 인덱스 부족 | ix_sched_visit_started 외 12 인덱스 |
| 8 | 감사 envers 일부만 | BaseEntity + Envers 표준 적용 |
| 9 | 패키지 오타 (sigsframe) | irosafe.shred |
| 10 | 외부 jar 의존 | 자체 commons 라이브러리 |
| 11 | utf8mb4 / 한글 호환 일부 | utf8mb4 표준 + Pretendard |
| 12 | 코드 마스터 표준 부재 | code 마스터 + UI |
| 13 | 시간대 손실 | KST 고정 (Asia/Seoul) |
| 14 | Hibernate 4/5 혼재 위험 | Hibernate 6 통일 |
docs/03_DB재설계/00_기존_ILSA_결함.md
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 단위테스트 | 없음 / 미확인 | 71 PASS / 0 FAIL (9 클래스, 12s) | 핵심 비즈니스 로직 안전망 |
| e2e | 없음 | Playwright 35 PASS / 0 FAIL (UI 19 + API 16) | UI + endpoint 회귀 자동 |
| 통합 | 없음 | 9 작성 (Testcontainers MariaDB) | CI runner runner 등록 후 자동 |
| API 문서 | 없음 | Swagger UI 32 path 자동 | 신규 협업 즉시 |
| CI | 부분 | GitLab Pipeline (단위테스트 + build) | push 마다 검증 |
| 부팅 시간 | 미확인 (OOM 회고) | 3.7s | JVM 21 + lazy bean |
| Migration 검증 | 수동 | Flyway V1~V6 + Testcontainers Flyway 검증 | 스키마 일관성 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| 모노레포 | 백/모바일 분리, 공통화 없음 | apps/{backend,frontend,android} + libraries/ | 코드 공유 |
| 빌드 캐시 | 약 | Gradle 캐시 + npm 캐시 + GitLab cache.key.files | CI 단축 |
| 핫리로드 | (Thymeleaf) 기본 | Vite HMR + Spring DevTools | 개발 사이클 가속 |
| 의존성 관리 | 분산 | libs.versions.toml + package-lock.json | 버전 1곳 |
| 모달/팝업 | 일부 자체 | 통합 컴포넌트 (PageHeader/DataTable/SearchBar/Badge) | 재사용 |
| 디자인 토큰 | 없음 | iro/CI runner/partner 팔레트 + Pretendard | 일관성 |
| 영역 | 기존 | 신규 | 개선 |
|---|---|---|---|
| CSRF | Spring 기본 | JWT stateless | 토큰 모델 |
| CORS | 자체 처리 | CorsFilter HIGHEST_PRECEDENCE + origins 명시 | dev/prod 일관 |
| 비밀번호 정책 | 미확인 | bcrypt 12 + login fail lock 5/30분 | 산업 표준 |
| 로그 | 분산 | 통합 (shred-backend.log) + KST | 운영 단순 |
| 감사 (audit) | 일부 envers | BaseEntity + Envers + login_audit + schedule_assignment_audit | 핵심 변경 추적 |
| 컷오버 안전 | 일시 | 병렬 운영 → 신규 점진 + Phase E ETL dry-run/apply 분리 | 롤백 가능 |
| 항목 | Before | After |
|---|---|---|
| Spring Boot / Java | 2.5 / 8 | 3.3 / 21 |
| 화면 | Thymeleaf 182 | React SPA + 다크모드 |
| API 문서 | 없음 | Swagger 32 |
| RBAC 권한 | 3 단계 텍스트 | 13 권한 코드 |
| DB 결함 | 14건 | 0건 |
| 테스트 | 없음 | 단위 71 + e2e 35 = 106 PASS |
| 정기 자동 연장 | 수동 | @Scheduled 일배치 |
| 다크모드 | 없음 | system/light/dark |
| 코드 마스터 UI | 매직코드 | 탭/인라인/활성 |
| 주소 검색 | 없음 | 카카오 우편번호 |
| KPI | Before (추정) | After (목표) |
|---|---|---|
| 계약 등록 클릭 수 | 10+ | ≤ 5 |
| 일정 조회 (월 단위) | 다중 화면 | 1 SPA |
| 엑셀 다운로드 가능 도메인 | 일부 | 3 (계약/협력사/일정) |
| 협력사 식별 시각 단서 | 텍스트 | 색상 swatch |
| 정기 만료 누락 | 발생 | 0** (배치) |
| 컷오버 위험 | 일괄 교체 | 병렬 운영 |