아키텍처
제가 개발할 때 중요하게 생각하는 원칙은 명확한 책임 분리입니다. 아키텍처 패턴과 관계없이 각 계층의 책임이 명확히 분리되어야 합니다.
계층 구조 예시 (MVC Pattern)
계층형 아키텍처의 대표적인 예시로, Controller-Service-Repository 3계층 구조가 있습니다:
┌────────────────────────┐
│ Controller Layer │ ← HTTP 요청/응답만
├────────────────────────┤
│ Service Layer │ ← 비즈니스 로직만
├────────────────────────┤
│ Repository Layer │ ← 데이터베이스 접근만
└────────────────────────┘핵심 원칙:
- Controller는 Service만 호출: Repository 직접 호출 금지
- Service는 Repository 계층의 책임을 침범하지 않음: 직접 쿼리 작성 금지 (유틸리티 클래스, 외부 서비스 호출은 가능)
- 각 계층은 단일 책임: 계층 간 책임 명확히 분리
Controller 계층
역할
Controller는 HTTP 요청/응답만 처리합니다.
- HTTP 요청 수신 및 검증
- Service 계층 호출
- HTTP 응답 생성 (상태 코드, 헤더 포함)
- DTO 변환 (Request DTO → Service, Domain → Response DTO)
허용된 작업
| |
금지된 작업
| |
Service 계층
역할
Service는 비즈니스 로직만 처리합니다.
- 비즈니스 규칙 구현
- 트랜잭션 관리
- 도메인 객체 조작
- 외부 서비스 호출 (이벤트 발행, 외부 API 등)
- 여러 Repository 조합
허용된 작업
| |
금지된 작업
| |
Repository 계층
역할
Repository는 데이터베이스 접근만 처리합니다.
- CRUD 작업
- 쿼리 실행
- 데이터 영속성 관리
허용된 작업
| |
금지된 작업
| |
계층 간 데이터 흐름
요청 흐름 (Request → Domain)
| |
응답 흐름 (Domain → Response)
| |
서비스 간 의존성
같은 계층 간 호출 금지
| |
해결 방법 1: Repository 직접 호출
| |
해결 방법 2: 이벤트 기반 통신
| |
계층별 테스트 전략
Controller 테스트
| |
Service 테스트
| |
Repository 테스트
| |
DTO vs Entity 분리 (필수)
Entity는 절대 외부 노출 금지
| |
DTO 변환 위치
원칙: Controller에서만 DTO 변환
| |
순환 참조 방지
| |
보안 (Security)
민감정보 로깅 금지
| |
예외 메시지에 민감정보 포함 금지
| |
SQL Injection 방지
| |
성능 (Performance)
N+1 쿼리 문제 해결
| |
@Transactional(readOnly = true) 활용
| |
페이징 처리
| |
트랜잭션 (Transaction)
트랜잭션 범위 최소화
| |
전파 옵션 이해
| |
동시성 처리 (Concurrency)
Optimistic Locking
| |
Pessimistic Locking
| |
대용량 데이터 처리
배치 처리
| |
EntityManager flush/clear 패턴
| |
체크리스트
Controller 작성 시:
- Service만 의존하는가? (Repository 의존 금지)
- HTTP 요청/응답만 처리하는가?
- 비즈니스 로직이 없는가?
- DTO 변환만 수행하는가?
- Entity를 직접 반환하지 않는가?
Service 작성 시:
- Repository만 의존하는가? (다른 Service 의존 금지)
- 비즈니스 로직만 포함하는가?
- HTTP 관련 코드가 없는가? (ResponseEntity, HttpStatus 등)
- 트랜잭션을 적절히 관리하는가?
- @Transactional(readOnly = true)를 조회 메서드에 적용했는가?
- N+1 문제를 해결했는가?
- 민감정보를 로깅하지 않는가?
Repository 작성 시:
- 데이터베이스 접근만 수행하는가?
- 비즈니스 로직이 없는가?
- 이벤트 발행이 없는가?
- 트랜잭션 관리가 없는가? (Service에서 관리)
보안:
- SQL Injection 방지를 위해 Prepared Statement를 사용하는가?
- 민감정보(비밀번호, 토큰)를 로깅하지 않는가?
- 예외 메시지에 민감정보를 포함하지 않는가?
성능:
- N+1 문제를 Fetch Join 또는 @EntityGraph로 해결했는가?
- 페이징 처리를 적용했는가?
- 대용량 데이터 처리 시 배치 처리를 적용했는가?
동시성:
- 동시성 이슈가 있는 경우 Optimistic 또는 Pessimistic Locking을 적용했는가?
- @Version을 사용한 낙관적 잠금을 고려했는가?