default 메서드란
설계 확장을 위한 도구
Java 8 이전에는 인터페이스에 추상 메서드만 선언할 수 있었다. 문제는 인터페이스를 이미 여러 구현체가 구현하고 있는 상황에서 메서드 하나를 추가하면 모든 구현체가 깨짐
이문제를 해결하기 위해 default 메서드가 나왔다
public interface Repository<T> {
Optional<T> findById(Long id);
default T getOrNull(Long id) {
return findById(id).orElse(null);
}
}
- 인터페이스에 기본 구현을 제공
- 기존 구현체를 깨지 않고 기능 추가 가능
무엇을 담당하는 메서드일까?
모든 구현체에서 동일하게 동작해야 하는 공통 행위
- 구현체마다 바뀌면 안되는 로직
- 맥락을 몰라도 실행 가능한 로직
- 단순하고 중립적인 로직
Repository에서 default 메서드가 허용되는 경우
1) Optional 처리 보조
default User findOrNull(Long id) {
return findById(id).orElse(null);
}
- Optional을 계속 풀어야 하는 번거로움 감소
- null이면 어떻게 할지는 Service가 결정
2) 조회 패턴 통일
default boolean exists(Long id) {
return findById(id).isPresent();
}
- 단순히 있다 / 없다만 알려줌
3) 의미 없는 예외 (제한적 허용)
default User findOrThrow(Long id) {
return findById(id)
.orElseThrow(() -> new IllegalStateException("User not found"));
}
온전하게 허용되는 경우는 아니지만 가능한 이유는
- HTTP 없음
- ErrorCode 없음
- 비즈니스 메시지 없음
- 비정상 상태라는 표현만 사용
내가 생각해 봐야 할 것
이번 프로젝트에 팀 코드 컨벤션으로 공통 커스텀 예외처리를 적용하였다. default 메서드 예외처리도 다음과 같이 작성했는데
default User findOrThrow(Long id) {
return findById(id)
.orElseThrow(() -> new NotFoundException(ErrorCode.USER_NOT_FOUND));
}
리뷰를 받다보니 예외처리 위치가 레포지토리까지 들어와야 할까 라는 질문을 받았다.
스스로 생각해본 결과 비즈니스 로직의 해석이 레포지토리, 데이터 계층까지 내려왔다고 판단했다. 그 근거는
- Repository가 응답 규약을 이미 알고있음
- HTTP 404가 결정됨
- ErrorCode는 비즈니스 로직의 예외처리 상수집합인데 ErrorCode 수정시 Repoository도 수정 필요
이와 같은 근거로 서비스단에서 처리되어야 하는게 맞다고 생각했다.
배운 점
default 메서드는 공통 행위를 담는 곳이지 의미를 결정하는 곳이 아니다!
'Dev. > Spring' 카테고리의 다른 글
| [Spring] AOP 정리 (0) | 2025.12.03 |
|---|---|
| [Spring] Interceptor 정리 (1) | 2025.12.02 |
| [Spring] 스프링이 싱글톤 빈을 생성하는 과정 (0) | 2025.11.21 |
| [Spring] Lombok 생성자 @NoArgsConstructor, @AllArgsConstructor, @RequiredArgsConstructor (1) | 2025.11.20 |
| [Spring] 응답DTO에 엔티티 의존성을 주입해야 하는 경우 (0) | 2025.11.19 |