162 lines
6.4 KiB
Markdown
162 lines
6.4 KiB
Markdown
# 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` 모듈 범위에 적용
|
|
- 향후 마이크로서비스 분할 시 조정 가능
|