TA 역할 Spring 경계 smoke (role-ta-live-v5-001)
All checks were successful
ci / test (pull_request) Successful in 5s
All checks were successful
ci / test (pull_request) Successful in 5s
This commit is contained in:
parent
a715e432f4
commit
48d28a2098
1 changed files with 162 additions and 0 deletions
162
docs/adr/ADR-001-spring-boundary-contracts-error-tx.md
Normal file
162
docs/adr/ADR-001-spring-boundary-contracts-error-tx.md
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
# ADR-001: Spring 전환 경계, 계약, 오류 및 트랜잭션 결정
|
||||
|
||||
**날짜:** 2025-07-14
|
||||
**상태:** 수락됨
|
||||
**결정자:** TA 아키텍트
|
||||
|
||||
---
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
runtime-role-matrix-live 프로젝트는 기존 레거시 런타임에서 Spring Boot 기반 런타임으로 전환 중이다. 전환 과정에서 다음 사항에 대한 명확한 경계와 계약이 필요하다:
|
||||
|
||||
- Spring 컴포넌트와 비-Spring 레거시 간의 경계
|
||||
- 컴포넌트 간 계약(인터페이스) 정의
|
||||
- 예외 처리 정책
|
||||
- 트랜잭션 전파 및 경계 관리
|
||||
|
||||
---
|
||||
|
||||
## 결정
|
||||
|
||||
### 1. Spring 경계 (Boundary)
|
||||
|
||||
| 영역 | 경계 전략 | 설명 |
|
||||
|------|-----------|------|
|
||||
| **도입점** | `@SpringBootApplication` 메인 클래스 | 외부 요청의 단일 진입점 |
|
||||
| **내부 컴포넌트** | `@Configuration` + `@Bean` | Spring 컨테이너 관리 대상 |
|
||||
| **외부 의존성** | `Adapter` 패턴 | 비-Spring 라이브러리를 Spring Bean으로 감싸기 |
|
||||
| **레거시 통합** | `@Qualifier` 주입 | Bean 이름으로 명시적 의존성 해결 |
|
||||
|
||||
**경계 원칙:**
|
||||
- Spring 컨테이너 내부에서만 `@Autowired`, `@Inject` 사용
|
||||
- 컨테이너 외부 레거시 코드는 `ApplicationContext`에서 Bean 조회 후 전달
|
||||
- 순환 참조 방지: 생성자 주입 우선, `@Lazy` 활용
|
||||
|
||||
### 2. 계약 (Contract)
|
||||
|
||||
**인터페이스 계층:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Presentation Layer (@Controller) │
|
||||
│ DTO / Request / Response │
|
||||
└─────────────────┬───────────────────┘
|
||||
│
|
||||
┌─────────────────▼───────────────────┐
|
||||
│ Application Layer (@Service) │
|
||||
│ Service Interface │
|
||||
└─────────────────┬───────────────────┘
|
||||
│
|
||||
┌─────────────────▼───────────────────┐
|
||||
│ Domain Layer (Entity) │
|
||||
│ Domain Model │
|
||||
└─────────────────┬───────────────────┘
|
||||
│
|
||||
┌─────────────────▼───────────────────┐
|
||||
│ Infrastructure (@Repository) │
|
||||
│ Repository Interface │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**계약 규칙:**
|
||||
- 각 계층은 하위 계층 인터페이스에만 의존 (의존성 역전 원칙)
|
||||
- `Service` → `Repository` 계약: JPA `JpaRepository` 확장 또는 커스텀 인터페이스
|
||||
- `Controller` → `Service` 계약: 명시적 서비스 인터페이스 정의
|
||||
- DTO는 불변(immutable) 객체로 설계, `record` 또는 `final` 클래스 사용
|
||||
|
||||
### 3. 오류 처리 (Error Handling)
|
||||
|
||||
| 계층 | 처리 방식 | 구현 |
|
||||
|------|-----------|------|
|
||||
| **도메인** | 도메인 예외 (`DomainException`) | 비즈니스 규칙 위반 시 발생 |
|
||||
| **인프라** | 데이터 접근 예외 (`DataAccessException`) | Spring의 추상화 예외 활용 |
|
||||
| **애플리케이션** | 애플리케이션 예외 (`ApplicationException`) | 도메인 예외 감싸기, 로깅 |
|
||||
| **프레젠테이션** | `@ControllerAdvice` | 일관된 HTTP 응답 반환 |
|
||||
|
||||
**예외 계층 구조:**
|
||||
|
||||
```
|
||||
RuntimeException
|
||||
├── DomainException (비즈니스 로직 오류)
|
||||
├── ApplicationException (애플리케이션 수준 오류)
|
||||
│ └── ResourceNotFoundException
|
||||
│ └── InvalidStateException
|
||||
└── InfrastructureException (외부 시스템 오류)
|
||||
└── DataAccessException (Spring)
|
||||
└── ExternalServiceException
|
||||
```
|
||||
|
||||
**@ControllerAdvice 규칙:**
|
||||
- `ErrorResponse` DTO 반환: `{ "code", "message", "timestamp", "path" }`
|
||||
- HTTP 상태 매핑: 400(Bad Request), 404(Not Found), 409(Conflict), 500(Internal)
|
||||
- 민감 정보 제외: 예외 스택 트레이스 클라이언트 노출 금지
|
||||
- 로깅: `ERROR` 레벨, 요청 ID 포함
|
||||
|
||||
### 4. 트랜잭션 (Transaction)
|
||||
|
||||
| 시나리오 | 전파 정책 | 설명 |
|
||||
|----------|-----------|------|
|
||||
| **Service → Repository** | `REQUIRED` (기본) | 기존 트랜잭션 참여 또는 신규 생성 |
|
||||
| **Service → Service** | `REQUIRED` | 같은 트랜잭션 내에서 실행 |
|
||||
| **읽기 전용** | `readOnly=true` | SELECT 최적화 |
|
||||
| **다중 데이터소스** | `REQUIRES_NEW` | 독립 트랜잭션 보장 |
|
||||
| **비트랜잭션 작업** | `NOT_SUPPORTED` | 기존 트랜잭션 일시 중단 |
|
||||
|
||||
**트랜잭션 경계 규칙:**
|
||||
- `@Transactional`은 public 메서드에만 적용 (프록시 제약)
|
||||
- 클래스 레벨 `@Transactional`보다 메서드 레벨 우선
|
||||
- 롤백: `RuntimeException`, `Error` 자동 롤백, 체크 예외는 명시적 `rollbackFor` 필요
|
||||
- 격리 수준: `DEFAULT`(데이터소스 기본값) 유지, 필요 시 명시적 지정
|
||||
|
||||
**트랜잭션 순서도:**
|
||||
|
||||
```
|
||||
[요청 수신]
|
||||
│
|
||||
▼
|
||||
[Controller] ──── 예외 ────▶ @ControllerAdvice ──▶ ErrorResponse
|
||||
│
|
||||
▼
|
||||
[Service @Transactional]
|
||||
│
|
||||
├── 성공 ──▶ [Repository] ──▶ Commit
|
||||
│
|
||||
├── DomainException ──▶ Rollback
|
||||
│
|
||||
└── ApplicationException ──▶ Rollback + 로깅
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 대안 검토
|
||||
|
||||
### 대안 A: 모든 코드를 한 번에 Spring 전환
|
||||
- **단점:** 리스크 높음, 점진적 검증 불가
|
||||
- **선택 안 함**
|
||||
|
||||
### 대안 B: 경계 없이 자유 주입
|
||||
- **단점:** 순환 참조, 테스트 어려움
|
||||
- **선택 안 함**
|
||||
|
||||
### 대안 C: 체크 예외 기반 트랜잭션
|
||||
- **단점:** 명시적 롤백 선언 필요, 실수 가능성
|
||||
- **선택 안 함**
|
||||
|
||||
---
|
||||
|
||||
## 결과 (Consequences)
|
||||
|
||||
** positif:**
|
||||
- 명확한 계층 분리 → 유지보수성 향상
|
||||
- 일관된 예외 처리 → 디버깅 용이
|
||||
- 명시적 트랜잭션 경계 → 데이터 무결성 보장
|
||||
- Spring 표준 패턴 적용 → 팀 역량 일관화
|
||||
|
||||
** 부정적:**
|
||||
- 초기 학습 곡선 (ADR 숙지 필요)
|
||||
- 레거시 통합 시 어댑터 추가 개발 필요
|
||||
|
||||
**제한:**
|
||||
- 본 ADR은 `role-ta` 모듈 범위에 적용
|
||||
- 향후 마이크로서비스 분할 시 조정 가능
|
||||
Loading…
Add table
Add a link
Reference in a new issue