Compare commits
11 commits
forge/role
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c1d4498b7 | |||
| fe0552a2fe | |||
| e6a5e9fa25 | |||
| 44c2b11152 | |||
| 42ccb025ab | |||
| ce8732abc8 | |||
| a6cb360317 | |||
| 0b3210a40d | |||
| 9a1ad656ff | |||
| 93e432b6cd | |||
| 049c1c9dcb |
8 changed files with 818 additions and 0 deletions
3
.forge/role-aa-001-attempt-1-run-f88341c79b7c.md
Normal file
3
.forge/role-aa-001-attempt-1-run-f88341c79b7c.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# role-aa-001-attempt-1-run-f88341c79b7c
|
||||||
|
|
||||||
|
Forge 이슈 작업 브랜치 `forge/role-aa-001-attempt-1-run-f88341c79b7c`.
|
||||||
3
.forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6.md
Normal file
3
.forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# role-ta-001-attempt-1-run-9fa6a6b8d5c6
|
||||||
|
|
||||||
|
Forge 이슈 작업 브랜치 `forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6`.
|
||||||
131
docs/adr/ADR-001-controller-service-repository-boundaries.md
Normal file
131
docs/adr/ADR-001-controller-service-repository-boundaries.md
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
# 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<RoleResponse> 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<Role, Long> {
|
||||||
|
Optional<Role> 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 활용으로 보일러플레이트 감소
|
||||||
|
- 공통 응답/예외 처리基础设施建设
|
||||||
132
docs/adr/ADR-002-error-contract.md
Normal file
132
docs/adr/ADR-002-error-contract.md
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
# 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<FieldError> details
|
||||||
|
) {
|
||||||
|
public record FieldError(String field, Object rejectedValue, String message) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalExceptionHandler.java
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(BusinessException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex, HttpServletRequest request) {
|
||||||
|
ErrorResponse response = ErrorResponse.of(ex, request.getRequestURI());
|
||||||
|
return ResponseEntity.status(ex.getHttpStatus()).body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> 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:**
|
||||||
|
- 오류 응답 클래스 추가 작성 필요
|
||||||
|
- 기존 예외 처리 코드 마이그레이션 필요
|
||||||
119
docs/adr/ADR-003-transaction-boundary.md
Normal file
119
docs/adr/ADR-003-transaction-boundary.md
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
# 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<Role> 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에서 트랜잭션 동작 검증
|
||||||
35
docs/adr/README.md
Normal file
35
docs/adr/README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# 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 검토 및 업데이트
|
||||||
137
legacy-transition-audit-evidence.md
Normal file
137
legacy-transition-audit-evidence.md
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
# 레거시 전환 분석 감사 증적 정리
|
||||||
|
|
||||||
|
**문서 버전**: 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 플랫폼 마이그레이션 |
|
||||||
|
| 증적 | 감사 추적에 사용되는 근거 자료 |
|
||||||
|
| 블루-그린 배포 | 무중단 배포 전략 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**문서 종료**
|
||||||
258
legacy-transition-evidence-inventory.json
Normal file
258
legacy-transition-evidence-inventory.json
Normal file
|
|
@ -0,0 +1,258 @@
|
||||||
|
{
|
||||||
|
"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/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue