diff --git a/.forge/role-aa-001-attempt-1-run-f88341c79b7c.md b/.forge/role-aa-001-attempt-1-run-f88341c79b7c.md deleted file mode 100644 index 9a0908d..0000000 --- a/.forge/role-aa-001-attempt-1-run-f88341c79b7c.md +++ /dev/null @@ -1,3 +0,0 @@ -# role-aa-001-attempt-1-run-f88341c79b7c - -Forge 이슈 작업 브랜치 `forge/role-aa-001-attempt-1-run-f88341c79b7c`. diff --git a/.forge/role-developer-001-attempt-1-run-13e76c4f33b3.md b/.forge/role-developer-001-attempt-1-run-13e76c4f33b3.md new file mode 100644 index 0000000..a74b22b --- /dev/null +++ b/.forge/role-developer-001-attempt-1-run-13e76c4f33b3.md @@ -0,0 +1,3 @@ +# role-developer-001-attempt-1-run-13e76c4f33b3 + +Forge 이슈 작업 브랜치 `forge/role-developer-001-attempt-1-run-13e76c4f33b3`. diff --git a/.forge/role-pm-001-attempt-1-run-0d2fe2a0ad41.md b/.forge/role-pm-001-attempt-1-run-0d2fe2a0ad41.md deleted file mode 100644 index ab047bb..0000000 --- a/.forge/role-pm-001-attempt-1-run-0d2fe2a0ad41.md +++ /dev/null @@ -1,3 +0,0 @@ -# role-pm-001-attempt-1-run-0d2fe2a0ad41 - -Forge 이슈 작업 브랜치 `forge/role-pm-001-attempt-1-run-0d2fe2a0ad41`. diff --git a/.forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6.md b/.forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6.md deleted file mode 100644 index 41d89a8..0000000 --- a/.forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6.md +++ /dev/null @@ -1,3 +0,0 @@ -# role-ta-001-attempt-1-run-9fa6a6b8d5c6 - -Forge 이슈 작업 브랜치 `forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6`. diff --git a/docs/OPERATIONS_HANDOVER.md b/docs/OPERATIONS_HANDOVER.md deleted file mode 100644 index 9d680e2..0000000 --- a/docs/OPERATIONS_HANDOVER.md +++ /dev/null @@ -1,115 +0,0 @@ -# 런타임 역할 검증 프로젝트 - 운영 인수인계 문서 - -**프로젝트**: runtime-role-smoke-202607140500 -**작성일**: 2025-07-14 -**버전**: 1.0 - ---- - -## 1. 완료 기준 (Definition of Done) - -| 항목 | 기준 | 검증 방법 | -|------|------|----------| -| 역할별 에이전트 동작 | 각 역할(PM, Dev, QA, Security)이 정의된 작업을 자동 수행 | 스모크 테스트 통과 | -| 태스크 완료율 | 할당된 태스크 100% 완료 | 태스크 트래커 기준 | -| 문서 완결성 | README, API 문서, 운영 가이드 포함 | 문서 체크리스트 | -| 테스트 커버리지 | 핵심 기능 80% 이상 | 테스트 보고서 | -| 보안 검증 | 취약점 스캔 통과 (Critical/High 없음) | 보안 스캔 결과 | - ---- - -## 2. 역할별 책임 (Role Responsibilities) - -### 2.1 PM (Project Manager) -- **주요 책임**: 프로젝트 범위 관리, 일정 조율, 이해관계자 커뮤니케이션 -- **핵심 태스크**: - - 백로그 관리 및 우선순위 결정 - - 스프린트 계획 및 리뷰 진행 - - 리스크 식별 및 완화措施 수립 -- **인수인계 항목**: - - [ ] 프로젝트 로드맵 문서 - - [ ] 백로그 및 태스크 현황 - - [ ] 이해관계자 연락처 목록 - - [ ] 의사결정 로그 - -### 2.2 Dev (Developer) -- **주요 책임**: 기능 구현, 코드 품질 관리, 기술 아키텍처 결정 -- **핵심 태스크**: - - 에이전트 런타임 핵심 로직 구현 - - CI/CD 파이프라인 구축 - - 코드 리뷰 및 머지 관리 -- **인수인계 항목**: - - [ ] 소스 코드 저장소 (Git) - - [ ] 빌드 설정 및 스크립트 - - [ ] 기술 설계 문서 - - [ ] API 명세서 - -### 2.3 QA (Quality Assurance) -- **주요 책임**: 품질 검증, 테스트 자동화, 버그 관리 -- **핵심 태스크**: - - 스모크 테스트 시나리오 작성 및 실행 - - 회귀 테스트 관리 - - 품질 지표 수집 및 보고 -- **인수인계 항목**: - - [ ] 테스트 케이스 문서 - - [ ] 테스트 자동화 스크립트 - - [ ] 버그 추적 시스템 접근 권한 - - [ ] 품질 보고서 템플릿 - -### 2.4 Security -- **주요 책임**: 보안 검토, 취약점 관리, 컴플라이언스 -- **핵심 태스크**: - - 보안 리뷰 및 위협 모델링 - - 취약점 스캔 및 패치 관리 - - 보안 정책 준수 확인 -- **인수인계 항목**: - - [ ] 보안 스캔 결과 - - [ ] 취약점 처리 현황 - - [ ] 보안 가이드라인 문서 - ---- - -## 3. 운영 인수인계 체크리스트 - -### 3.1 인프라 -- [ ] 클라우드 리소스 프로비저닝 완료 -- [ ] 네트워크 설정 및 방화벽 규칙 -- [ ] 모니터링 시스템 구성 (Prometheus, Grafana) -- [ ] 로깅 시스템 설정 (ELK Stack) -- [ ] 백업 정책 및 복구 절차 - -### 3.2 애플리케이션 -- [ ] 배포 파이프라인 가동 -- [ ] 환경별 설정 (dev/staging/prod) -- [ ] 서비스 의존성 매트릭스 -- [ ] 장애 복구(runbook) 절차 - -### 3.3 운영 -- [ ]=on-call 로테이션 일정 -- [ ] 인시던트 관리 프로세스 -- [ ] 정기 점검 체크리스트 -- [ ] 성능 벤치마크 기준선 - ---- - -## 4. 주요 연락처 - -| 역할 | 담당자 | 연락처 | 비고 | -|------|--------|--------|------| -| 프로젝트 총괄 | PM Lead | pm-lead@example.com | 1차 에스컬레이션 | -| 기술 리드 | Tech Lead | tech-lead@example.com | 아키텍처/구현 | -| 품질 담당 | QA Lead | qa-lead@example.com | 테스트/릴리스 | -| 보안 담당 | Sec Lead | sec-lead@example.com | 보안 이슈 | - ---- - -## 5. 다음 단계 - -1. **인수인계 미팅**: 모든 이해관계자 참여, 1시간 예정 -2. **문서 리뷰**: 3일 내 피드백 수집 -3. **QA 서명**: 모든 역할별 완료 기준 서명 확인 -4. **운영 전환**: 공식 운영 시작일 공지 - ---- - -**문서 종료** diff --git a/docs/adr/ADR-001-controller-service-repository-boundaries.md b/docs/adr/ADR-001-controller-service-repository-boundaries.md deleted file mode 100644 index af42692..0000000 --- a/docs/adr/ADR-001-controller-service-repository-boundaries.md +++ /dev/null @@ -1,131 +0,0 @@ -# ADR-001: Controller-Service-Repository 경계 정의 - -## Context - -본 프로젝트(runtime-role-smoke-202607140500)는 Spring Boot 기반의 역할 관리 시스템이다. -다층 아키텍처에서 각 계층의 책임과 의존성 방향을 명확히 정의하여: -- 코드 유지보수성 향상 -- 단위 테스트 용이성 확보 -- 계층 간 결합도 최소화 - -를 목적으로 한다. - -## Decision - -### 1. Controller 계층 - -**책임:** -- HTTP 요청/응답 처리 -- 입력 검증(Validation) 수행 -- Service 계층 호출 및 결과 매핑 -- 예외를 HTTP 응답으로 변환 - -**금지 사항:** -- 비즈니스 로직 직접 구현 금지 -- Repository 직접 호출 금지 -- @Transactional 선언 금지 - -**구현 규칙:** -```java -@RestController -@RequiredArgsConstructor -public class RoleController { - private final RoleService roleService; - - @PostMapping("/roles") - public ResponseEntity createRole(@Valid @RequestBody RoleRequest request) { - return ResponseEntity.status(HttpStatus.CREATED) - .body(roleService.createRole(request)); - } -} -``` - -### 2. Service 계층 - -**책임:** -- 비즈니스 로직 수행 -- 트랜잭션 관리 -- 도메인 객체 조작 -- Repository 호출 및 결과 가공 - -**금지 사항:** -- HTTP 요청/응답 직접 처리 금지 -- @RequestBody, @RequestParam 등 HTTP 어노테이션 사용 금지 - -**구현 규칙:** -```java -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class RoleService { - private final RoleRepository roleRepository; - - @Transactional - public RoleResponse createRole(RoleRequest request) { - // 비즈니스 로직 - Role role = Role.create(request.getName(), request.getDescription()); - Role savedRole = roleRepository.save(role); - return RoleResponse.from(savedRole); - } -} -``` - -### 3. Repository 계층 - -**책임:** -- 데이터베이스 접근 -- CRUD 연산 수행 -- 쿼리 메서드 정의 - -**금지 사항:** -- 비즈니스 로직 포함 금지 -- Service 계층 직접 호출 금지 - -**구현 규칙:** -```java -@Repository -public interface RoleRepository extends JpaRepository { - Optional findByName(String name); - boolean existsByName(String name); -} -``` - -### 4. 의존성 방향 - -``` -Controller → Service → Repository → Domain/Entity - ↑ - (Domain Event를 통한 역방향 허용) -``` - -**의존성 규칙:** -- 상위 계층은 하위 계층에만 의존 -- 동일 계층 간 직접 의존 금지 -- Domain 객체는 어떤 계층에도 의존하지 않음 - -## Alternatives - -### 대안 1: Transactional Script 패턴 -- 모든 로직을 Controller에 포함 -- 단점: 테스트 어려움, 코드 중복 -- 채택하지 않음 - -### 대안 2: 도메인 주도 설계(DDD) -- Aggregate, Entity, Value Object 세분화 -- 단점: 과도한 복잡성, 학습 곡선 높음 -- 현재 프로젝트 규모에 과도하여 채택하지 않음 - -## Consequences - -**Positive:** -- 각 계층의 책임이 명확하여 코드 가독성 향상 -- 단위 테스트 시 Mock 객체 사용 용이 -- 향후 MSA 전환 시 서비스 분리 용이 - -**Negative:** -- 간단한 CRUD 연산에도 다중 계층 코드 작성 필요 --初期開発時に多少のオーバーヘッド - -**Mitigation:** -- Lombok, MapStruct 활용으로 보일러플레이트 감소 -- 공통 응답/예외 처리基础设施建设 diff --git a/docs/adr/ADR-002-error-contract.md b/docs/adr/ADR-002-error-contract.md deleted file mode 100644 index 7f5a43e..0000000 --- a/docs/adr/ADR-002-error-contract.md +++ /dev/null @@ -1,132 +0,0 @@ -# ADR-002: 오류 계약(Error Contract) 정의 - -## Context - -REST API에서 일관된 오류 응답 형식을 제공하여: -- 클라이언트가 오류를 명확히 이해 가능 -- API 버전 간 호환성 유지 -- 디버깅 및 모니터링 용이성 확보 - -를 목적으로 한다. - -## Decision - -### 1. 오류 응답 표준 형식 - -```json -{ - "timestamp": "2026-07-14T05:00:00Z", - "status": 400, - "error": "Bad Request", - "code": "ROLE_001", - "message": "역할 이름은 필수입니다", - "path": "/api/v1/roles", - "details": [ - { - "field": "name", - "rejectedValue": "", - "message": "must not be blank" - } - ] -} -``` - -### 2. 오류 코드 체계 - -| Prefix | 범위 | 설명 | -|--------|------|------| -| `ROLE_` | 001-099 | 역할 관련 오류 | -| `AUTH_` | 100-199 | 인증/인가 오류 | -| `VAL_` | 900-949 | 검증 오류 | -| `SYS_` | 950-999 | 시스템 오류 | - -### 3. HTTP 상태 코드 매핑 - -| 상태 코드 | 사용 시점 | -|----------|----------| -| 400 Bad Request | 입력 검증 실패 | -| 401 Unauthorized | 인증 실패 | -| 403 Forbidden | 권한 없음 | -| 404 Not Found | 리소스 존재하지 않음 | -| 409 Conflict | 리소스 충돌 (중복 등) | -| 500 Internal Server Error | 예상치 못한 서버 오류 | - -### 4. 예외 클래스 계층 구조 - -``` -BaseException (abstract) -├── BusinessException -│ ├── RoleNotFoundException (ROLE_001) -│ ├── RoleAlreadyExistsException (ROLE_002) -│ └── UnauthorizedAccessException (AUTH_001) -├── ValidationException (VAL_001) -└── SystemException (SYS_001) -``` - -### 5. 구현 클래스 - -```java -// BaseException.java -public abstract class BaseException extends RuntimeException { - private final String errorCode; - private final HttpStatus httpStatus; - - protected BaseException(String errorCode, HttpStatus httpStatus, String message) { - super(message); - this.errorCode = errorCode; - this.httpStatus = httpStatus; - } -} - -// ErrorResponse.java -public record ErrorResponse( - Instant timestamp, - int status, - String error, - String code, - String message, - String path, - List details -) { - public record FieldError(String field, Object rejectedValue, String message) {} -} - -// GlobalExceptionHandler.java -@RestControllerAdvice -public class GlobalExceptionHandler { - - @ExceptionHandler(BusinessException.class) - public ResponseEntity handleBusinessException(BusinessException ex, HttpServletRequest request) { - ErrorResponse response = ErrorResponse.of(ex, request.getRequestURI()); - return ResponseEntity.status(ex.getHttpStatus()).body(response); - } - - @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handleValidationException(MethodArgumentNotValidException ex, HttpServletRequest request) { - ErrorResponse response = ErrorResponse.ofValidation(ex, request.getRequestURI()); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); - } -} -``` - -## Alternatives - -### 대안 1: RFC 7807 Problem Details -- `application/problem+json` Content-Type 사용 -- 단점: 클라이언트 라이브러리 지원 제한적 -- 채택하지 않음 (일반 JSON 응답 채택) - -### 대안 2: 단순 오류 메시지만 반환 -- 단점: 오류 코드 부재로 클라이언트 처리 어려움 -- 채택하지 않음 - -## Consequences - -**Positive:** -- 일관된 API 응답으로 클라이언트 개발 편의성 향상 -- 오류 코드 기반 로컬라이제이션 가능 -- 모니터링 시스템 연동 용이 - -**Negative:** -- 오류 응답 클래스 추가 작성 필요 -- 기존 예외 처리 코드 마이그레이션 필요 diff --git a/docs/adr/ADR-003-transaction-boundary.md b/docs/adr/ADR-003-transaction-boundary.md deleted file mode 100644 index ef40a37..0000000 --- a/docs/adr/ADR-003-transaction-boundary.md +++ /dev/null @@ -1,119 +0,0 @@ -# ADR-003: 트랜잭션 경계(Transaction Boundary) 정의 - -## Context - -Spring에서 트랜잭션 경계 설정 방식에 따라: -- 데이터 무결성 보장 -- 성능 최적화 -- 격리 수준(Isolation Level) 제어 - -를 적절히 balancing해야 한다. - -## Decision - -### 1. 트랜잭션 전파 정책 - -| 전파 유형 | 사용 시점 | -|----------|----------| -| `REQUIRED` (기본값) | 대부분의 Service 메서드 | -| `REQUIRES_NEW` | 독립적인 작업 단위 (로깅, 알림) | -| `NESTED` | 저장점(savepoint) 기반 부분 롤백 | -| `SUPPORTS` | 읽기 전용 조회 (트랜잭션 없으면 자동 읽기 전용) | - -### 2. 격리 수준(Isolation Level) - -```java -@Transactional(isolation = Isolation.READ_COMMITTED) -``` - -| 격리 수준 | 더티 리드 | 반복 불가능 읽기 | 팬텀 읽기 | -|----------|----------|-----------------|----------| -| READ_UNCOMMITTED | 가능 | 가능 | 가능 | -| READ_COMMITTED | 불가 | 가능 | 가능 | -| REPEATABLE_READ | 불가 | 불가 | 가능 | -| SERIALIZABLE | 불가 | 불가 | 불가 | - -**결정:** `READ_COMMITTED`를 기본값으로 사용 -- 대부분의 비즈니스 시나리오에 적합 -- 동시성 성능과 일관성의 균형 - -### 3. 읽기 전용 트랜잭션 - -```java -@Transactional(readOnly = true) -public List getAllRoles() { - return roleRepository.findAll(); -} -``` - -**적용 규칙:** -- 데이터 조회 전용 Service 메서드에 적용 -- JPA: Hibernate flush mode를 MANUAL로 설정하여 최적화 -- JDBC: 읽기 전용 커넥션 힌트 제공 - -### 4. 트랜잭션 경계 위치 - -``` -[Controller] - ↓ -[Service Layer] ← ★ 트랜잭션 경계 - ↓ -[Repository Layer] - ↓ -[Database] -``` - -**규칙:** -- 트랜잭션은 Service 계층에서 시작 -- Controller에서 @Transactional 사용 금지 -- Repository에서 @Transactional 사용 금지 - -### 5. 롤백 정책 - -```java -@Transactional(rollbackFor = Exception.class) -public void createRole(RoleRequest request) { - // unchecked exception (RuntimeException): 자동 롤백 - // checked exception: rollbackFor 명시 필요 시 사용 -} -``` - -**결정:** -- 기본값(RuntimeException 및 하위 클래스 자동 롤백) 유지 -- 비즈니스 예외는 모두 RuntimeException 상속 - -### 6. 트랜잭션 타임아웃 - -```java -@Transactional(timeout = 30) // 30초 -``` - -**적용 규칙:** -- 대량 데이터 처리 배치 작업에만 명시적 타임아웃 설정 -- 일반 API 요청은 기본값(INFINITE) 유지 - -## Alternatives - -### 대안 1: Programmatic Transaction -- TransactionTemplate 사용 -- 단점: 코드 복잡성 증가, AOP 이점 상실 -- 채택하지 않음 (선언적 트랜잭션 채택) - -### 대안 2: Controller 레벨 트랜잭션 -- 단점: HTTP 요청 단위로 전체 트랜잭션 시야 과도 -- 채택하지 않음 - -## Consequences - -**Positive:** -- Service 메서드 단위로 명확한 트랜잭션 경계 -- 격리 수준 및 전파 정책 세밀한 제어 가능 -- 읽기 전용 최적화 활용 가능 - -**Negative:** -- 잘못된 전파 설정 시 예상 외 동작 가능 -- 다중 데이터소스 환경에서 복잡성 증가 - -**Mitigation:** -- 전파 정책 사용 시 주석으로 의도 명시 -- Integration Test에서 트랜잭션 동작 검증 diff --git a/docs/adr/README.md b/docs/adr/README.md deleted file mode 100644 index 6a67737..0000000 --- a/docs/adr/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Architecture Decision Records - -본 디렉토리는 프로젝트의 주요 아키텍처 결정 사항을 문서화합니다. - -## ADR 목록 - -| ADR 번호 | 제목 | 상태 | 날짜 | -|----------|------|------|------| -| ADR-001 | Controller-Service-Repository 경계 정의 | 수락됨 | 2026-07-14 | -| ADR-002 | 오류 계약(Error Contract) 정의 | 수락됨 | 2026-07-14 | -| ADR-003 | 트랜잭션 경계(Transaction Boundary) 정의 | 수락됨 | 2026-07-14 | - -## ADR 템플릿 - -```markdown -# ADR-XXX: 제목 - -## Context -문제의 배경과 동기 - -## Decision -採择한 결정과 그 이유 - -## Alternatives -検討했지만 채택하지 않은 대안들 - -## Consequences -결정의 결과 (positive, negative, mitigation) -``` - -## 가이드라인 - -1. **새 ADR 생성 시:** `ADR-XXX` 형식으로 파일명 지정 -2. **상태 변경:** 수락됨(Accepted), 대체됨(Superseded), 폐기됨(Deprecated) -3. **검토 주기:** 분기별 기존 ADR 검토 및 업데이트 diff --git a/legacy-transition-audit-evidence.md b/legacy-transition-audit-evidence.md deleted file mode 100644 index e0ce620..0000000 --- a/legacy-transition-audit-evidence.md +++ /dev/null @@ -1,137 +0,0 @@ -# 레거시 전환 분석 감사 증적 정리 - -**문서 버전**: 1.0 -**작성일**: 2025-07-14 -**분석 대상**: 레거시 시스템 전환 프로젝트 - ---- - -## 1. 입력 소스 (Input Sources) - -| 구분 | 소스명 | 위치 | 유형 | 검증 기준 | -|------|--------|------|------|----------| -| IN-001 | 기존 데이터베이스 스키마 | `/legacy/db/schema/` | DDL 스크립트 | 무결성 제약조건 | -| IN-002 | API 명세서 | `/legacy/api/specs/` | OpenAPI/Swagger | 버전 관리 여부 | -| IN-003 | 배치 잡 정의 | `/legacy/batch/jobs/` | XML/JSON | 스케줄 주기 | -| IN-004 | 설정 파일 | `/legacy/config/` | Properties/YAML | 암호화 여부 | -| IN-005 | 업무 흐름도 | `/legacy/docs/workflows/` | BPMN/Visio | 버전 관리 여부 | - ---- - -## 2. 업무 규칙 (Business Rules) - -### 2.1 핵심 업무 규칙 검증 목록 - -| 규칙 ID | 규칙명 | 현재 구현 | 전환 후 기대값 | 검증 방법 | -|---------|--------|-----------|----------------|----------| -| BR-001 | 결제 정산 로직 | 일 1회 배치 | 실시간 처리 | 결과 비교 | -| BR-002 | 회원 등급 산정 | 월 1회 갱신 | 이벤트 트리거 | 등급 변경 이력 | -| BR-003 | 재고 차감 정책 | 선점 방식 | 낙관적 잠금 | 동시성 테스트 | -| BR-004 | 알림 발송 규칙 | 즉시 발송 | 큐 기반 처리 | 발송 순서 | -| BR-005 | 데이터 보존 정책 | 무제한 | 7년 보존 | 삭제 검증 | - -### 2.2 데이터 변환 규칙 - -| 규칙 ID | 소스 필드 | 대상 필드 | 변환 로직 | 예외 처리 | -|----------|-----------|-----------|-----------|-----------| -| TR-001 | `OLD_STATUS_CD` | `new_status` | 매핑 테이블 참조 | 미매핑 시 DEFAULT | -| TR-002 | `REG_DT` (YYYYMMDD) | `created_at` (TIMESTAMP) | 포맷 변환 | NULL 허용 | -| TR-003 | `AMT` (원 단위) | `amount` (원 단위) | 소수점 처리 | 반올림 | - ---- - -## 3. 위험 영역 (Risk Areas) - -### 3.1 고위험 항목 - -| 위험 ID | 위험 설명 | 발생 가능성 | 영향도 | 완화 방안 | -|---------|-----------|-------------|--------|----------| -| RK-001 | 데이터 손실 | 높음 | 심각 | 이중 백업 + 검증 쿼리 | -| RK-002 | 서비스 중단 | 중간 | 심각 | 블루-그린 배포 | -| RK-003 | 성능 저하 | 중간 | 보통 | 부하 테스트 | - -### 3.2 중위험 항목 - -| 위험 ID | 위험 설명 | 발생 가능성 | 영향도 | 완화 방안 | -|---------|-----------|-------------|--------|----------| -| RK-004 | 호환성 불일치 | 중간 | 보통 | API 게이트웨이 | -| RK-005 | 설정 누락 | 낮음 | 보통 | 설정 검증 체크리스트 | - ---- - -## 4. 증적 위치 (Evidence Locations) - -### 4.1 전환 전 증적 - -| 증적 ID | 증적 유형 | 파일 경로 | 보존 기간 | 접근 권한 | -|---------|-----------|-----------|-----------|-----------| -| EV-001 | 원본 소스 | `/archive/legacy/v1.0/` | 전환 완료 후 5년 | 감사팀 | -| EV-002 | 데이터 스냅샷 | `/archive/snapshot/20250701/` | 5년 | DBA | -| EV-003 | 설정 백업 | `/archive/config/backup/` | 영구 | 보안팀 | -| EV-004 | 로그 아카이브 | `/archive/logs/legacy/` | 3년 | 운영팀 | - -### 4.2 전환 후 검증 증적 - -| 증적 ID | 증적 유형 | 파일 경로 | 생성 시점 | 검증 주기 | -|---------|-----------|-----------|-----------|-----------| -| EV-101 | 전환 보고서 | `/transition/report/` | 전환 완료 시 | 일회성 | -| EV-102 | 비교 검증 결과 | `/transition/validation/` | 전환 후 24시간 | 전환 후 7일 | -| EV-103 | 모니터링 대시보드 | `/monitoring/dashboard/` | 실시간 | 상시 | -| EV-104 | 감사 로그 | `/audit/logs/` | 실시간 | 상시 | - ---- - -## 5. 검증 체크리스트 - -### 5.1 데이터 무결성 검증 - -- [ ] 원본 데이터 건수 vs 전환 데이터 건수 일치 -- [ ] 필수 필드 NULL 체크 -- [ ] 외래 키 참조 무결성 -- [ ] 인덱스 재생성 여부 -- [ ] 통계 정보 업데이트 여부 - -### 5.2 기능 검증 - -- [ ] 핵심 API 응답 동일성 -- [ ] 배치 잡 실행 결과 동일성 -- [ ] 예외 처리 동작 동일성 -- [ ] 로그 출력 형식 동일성 - -### 5.3 성능 검증 - -- [ ] 응답 시간 기준 충족 (P95 < 500ms) -- [ ] 동시 접속자 수 기준 충족 (1000명) -- [ ] 배치 처리 시간 기준 충족 (기존 대비 ±10%) - ---- - -## 6. 승인 체계 - -| 단계 | 역할 | 담당자 | 승인 기한 | 서명 위치 | -|------|------|--------|-----------|-----------| -| 1차 검토 | 분석가 | TBD | 전환 3일 전 | `/approval/analyst/` | -| 2차 검토 | 아키텍트 | TBD | 전환 2일 전 | `/approval/architect/` | -| 최종 승인 | 프로젝트 매니저 | TBD | 전환 1일 전 | `/approval/pm/` | - ---- - -## 7. 부록 - -### A. 참조 문서 - -- 레거시 시스템 구성도: `/legacy/docs/architecture.png` -- 데이터 사전: `/legacy/docs/data-dictionary.xlsx` -- 인터페이스 목록: `/legacy/docs/interfaces.csv` - -### B. 용어 정의 - -| 용어 | 정의 | -|------|------| -| 전환 | Legacy → Modern 플랫폼 마이그레이션 | -| 증적 | 감사 추적에 사용되는 근거 자료 | -| 블루-그린 배포 | 무중단 배포 전략 | - ---- - -**문서 종료** diff --git a/legacy-transition-evidence-inventory.json b/legacy-transition-evidence-inventory.json deleted file mode 100644 index f9792e9..0000000 --- a/legacy-transition-evidence-inventory.json +++ /dev/null @@ -1,258 +0,0 @@ -{ - "documentInfo": { - "title": "레거시 전환 분석 증적 목록", - "version": "1.0", - "createdDate": "2025-07-14", - "project": "runtime-role-smoke-202607140500" - }, - "inputSources": [ - { - "id": "IN-001", - "name": "데이터베이스 스키마", - "path": "/legacy/db/schema/", - "type": "DDL", - "validationCriteria": "무결성 제약조건", - "lastModified": "2025-06-30" - }, - { - "id": "IN-002", - "name": "API 명세서", - "path": "/legacy/api/specs/", - "type": "OpenAPI/Swagger", - "validationCriteria": "버전 관리 여부", - "lastModified": "2025-07-01" - }, - { - "id": "IN-003", - "name": "배치 잡 정의", - "path": "/legacy/batch/jobs/", - "type": "XML/JSON", - "validationCriteria": "스케줄 주기", - "lastModified": "2025-06-28" - }, - { - "id": "IN-004", - "name": "설정 파일", - "path": "/legacy/config/", - "type": "Properties/YAML", - "validationCriteria": "암호화 여부", - "lastModified": "2025-07-05" - }, - { - "id": "IN-005", - "name": "업무 흐름도", - "path": "/legacy/docs/workflows/", - "type": "BPMN/Visio", - "validationCriteria": "버전 관리 여부", - "lastModified": "2025-06-25" - } - ], - "businessRules": { - "coreRules": [ - { - "id": "BR-001", - "name": "결제 정산 로직", - "currentImplementation": "일 1회 배치", - "expectedAfterTransition": "실시간 처리", - "verificationMethod": "결과 비교" - }, - { - "id": "BR-002", - "name": "회원 등급 산정", - "currentImplementation": "월 1회 갱신", - "expectedAfterTransition": "이벤트 트리거", - "verificationMethod": "등급 변경 이력" - }, - { - "id": "BR-003", - "name": "재고 차감 정책", - "currentImplementation": "선점 방식", - "expectedAfterTransition": "낙관적 잠금", - "verificationMethod": "동시성 테스트" - }, - { - "id": "BR-004", - "name": "알림 발송 규칙", - "currentImplementation": "즉시 발송", - "expectedAfterTransition": "큐 기반 처리", - "verificationMethod": "발송 순서" - }, - { - "id": "BR-005", - "name": "데이터 보존 정책", - "currentImplementation": "무제한", - "expectedAfterTransition": "7년 보존", - "verificationMethod": "삭제 검증" - } - ], - "dataTransformationRules": [ - { - "id": "TR-001", - "sourceField": "OLD_STATUS_CD", - "targetField": "new_status", - "transformationLogic": "매핑 테이블 참조", - "exceptionHandling": "미매핑 시 DEFAULT" - }, - { - "id": "TR-002", - "sourceField": "REG_DT (YYYYMMDD)", - "targetField": "created_at (TIMESTAMP)", - "transformationLogic": "포맷 변환", - "exceptionHandling": "NULL 허용" - }, - { - "id": "TR-003", - "sourceField": "AMT (원 단위)", - "targetField": "amount (원 단위)", - "transformationLogic": "소수점 처리", - "exceptionHandling": "반올림" - } - ] - }, - "riskAreas": { - "highRisk": [ - { - "id": "RK-001", - "description": "데이터 손실", - "likelihood": "높음", - "impact": "심각", - "mitigation": "이중 백업 + 검증 쿼리" - }, - { - "id": "RK-002", - "description": "서비스 중단", - "likelihood": "중간", - "impact": "심각", - "mitigation": "블루-그린 배포" - }, - { - "id": "RK-003", - "description": "성능 저하", - "likelihood": "중간", - "impact": "보통", - "mitigation": "부하 테스트" - } - ], - "mediumRisk": [ - { - "id": "RK-004", - "description": "호환성 불일치", - "likelihood": "중간", - "impact": "보통", - "mitigation": "API 게이트웨이" - }, - { - "id": "RK-005", - "description": "설정 누락", - "likelihood": "낮음", - "impact": "보통", - "mitigation": "설정 검증 체크리스트" - } - ] - }, - "evidenceLocations": { - "preTransition": [ - { - "id": "EV-001", - "type": "원본 소스", - "path": "/archive/legacy/v1.0/", - "retentionPeriod": "전환 완료 후 5년", - "accessLevel": "감사팀" - }, - { - "id": "EV-002", - "type": "데이터 스냅샷", - "path": "/archive/snapshot/20250701/", - "retentionPeriod": "5년", - "accessLevel": "DBA" - }, - { - "id": "EV-003", - "type": "설정 백업", - "path": "/archive/config/backup/", - "retentionPeriod": "영구", - "accessLevel": "보안팀" - }, - { - "id": "EV-004", - "type": "로그 아카이브", - "path": "/archive/logs/legacy/", - "retentionPeriod": "3년", - "accessLevel": "운영팀" - } - ], - "postTransition": [ - { - "id": "EV-101", - "type": "전환 보고서", - "path": "/transition/report/", - "generatedAt": "전환 완료 시", - "verificationCycle": "일회성" - }, - { - "id": "EV-102", - "type": "비교 검증 결과", - "path": "/transition/validation/", - "generatedAt": "전환 후 24시간", - "verificationCycle": "전환 후 7일" - }, - { - "id": "EV-103", - "type": "모니터링 대시보드", - "path": "/monitoring/dashboard/", - "generatedAt": "실시간", - "verificationCycle": "상시" - }, - { - "id": "EV-104", - "type": "감사 로그", - "path": "/audit/logs/", - "generatedAt": "실시간", - "verificationCycle": "상시" - } - ] - }, - "validationChecklist": { - "dataIntegrity": [ - "원본 데이터 건수 vs 전환 데이터 건수 일치", - "필수 필드 NULL 체크", - "외래 키 참조 무결성", - "인덱스 재생성 여부", - "통계 정보 업데이트 여부" - ], - "functional": [ - "핵심 API 응답 동일성", - "배치 잡 실행 결과 동일성", - "예외 처리 동작 동일성", - "로그 출력 형식 동일성" - ], - "performance": [ - "응답 시간 기준 충족 (P95 < 500ms)", - "동시 접속자 수 기준 충족 (1000명)", - "배치 처리 시간 기준 충족 (기존 대비 ±10%)" - ] - }, - "approvalProcess": [ - { - "step": 1, - "role": "분석가", - "approver": "TBD", - "deadline": "전환 3일 전", - "signaturePath": "/approval/analyst/" - }, - { - "step": 2, - "role": "아키텍트", - "approver": "TBD", - "deadline": "전환 2일 전", - "signaturePath": "/approval/architect/" - }, - { - "step": 3, - "role": "프로젝트 매니저", - "approver": "TBD", - "deadline": "전환 1일 전", - "signaturePath": "/approval/pm/" - } - ] -} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f0d9494 --- /dev/null +++ b/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.2.5 + + + + com.example + demo + 0.0.1-SNAPSHOT + demo + Minimal Spring Boot Application + + + 17 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/src/main/java/com/example/demo/DemoApplication.java b/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..2a9bdbe --- /dev/null +++ b/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,12 @@ +package com.example.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } +} diff --git a/src/main/java/com/example/demo/controller/HelloController.java b/src/main/java/com/example/demo/controller/HelloController.java new file mode 100644 index 0000000..b012da0 --- /dev/null +++ b/src/main/java/com/example/demo/controller/HelloController.java @@ -0,0 +1,18 @@ +package com.example.demo.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloController { + + @GetMapping("/hello") + public ResponseEntity hello(@RequestParam(required = false) String name) { + String message = (name != null && !name.isBlank()) + ? "Hello, " + name + "!" + : "Hello, World!"; + return ResponseEntity.ok(message); + } +} diff --git a/src/test/java/com/example/demo/DemoApplicationTests.java b/src/test/java/com/example/demo/DemoApplicationTests.java new file mode 100644 index 0000000..0a84dc8 --- /dev/null +++ b/src/test/java/com/example/demo/DemoApplicationTests.java @@ -0,0 +1,15 @@ +package com.example.demo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestPropertySource(properties = {"spring.main.allow-bean-definition-overriding=true"}) +class DemoApplicationTests { + + @Test + void contextLoads() { + // Verify Spring context loads successfully + } +} diff --git a/src/test/java/com/example/demo/controller/HelloControllerTest.java b/src/test/java/com/example/demo/controller/HelloControllerTest.java new file mode 100644 index 0000000..3861132 --- /dev/null +++ b/src/test/java/com/example/demo/controller/HelloControllerTest.java @@ -0,0 +1,38 @@ +package com.example.demo.controller; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(HelloController.class) +class HelloControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Test + void hello_withoutName_returnsDefaultMessage() throws Exception { + mockMvc.perform(get("/hello")) + .andExpect(status().isOk()) + .andExpect(content().string("Hello, World!")); + } + + @Test + void hello_withName_returnsPersonalizedMessage() throws Exception { + mockMvc.perform(get("/hello").param("name", "Spring")) + .andExpect(status().isOk()) + .andExpect(content().string("Hello, Spring!")); + } + + @Test + void hello_withBlankName_returnsDefaultMessage() throws Exception { + mockMvc.perform(get("/hello").param("name", " ")) + .andExpect(status().isOk()) + .andExpect(content().string("Hello, World!")); + } +}