Spring

[Spring] Optional의 활용: Repository, Service, 그리고 ResponseEntity의 역할

0and24 2024. 12. 6. 17:05

이전 글에서 스트림, 람다를 소개하면서 Optional 키워드도 같이 소개를 한적이 있었는데 설명이 많이 부족하여 다시 설명을 하려고 합니다.

1. Repository 계층에서 Optional을 사용하는 이유

Repository 계층은 데이터베이스와의 직접적인 인터페이스 역할을 하며, 값이 존재하지 않을 가능성을 처리하는 것이 중요합니다.

이유 1: null-safe 코드 작성

  • Optional을 사용하면 null 반환 대신 Optional.empty()를 반환하여 null 처리를 강제합니다.
  • 이는 NullPointerException을 방지하고 호출자가 명시적으로 결과를 처리하게 만듭니다.

이유 2: 값의 유무를 명확히 표현

  • 단일 객체를 반환하는 메서드에서 "값이 없을 수도 있음"을 명확히 알릴 수 있습니다.
  • 예를 들어, findById와 같은 메서드는 엔티티를 찾지 못했을 때 null 대신 Optional.empty()를 반환합니다.

Spring Data JPA와의 통합

  • Spring Data JPA는 findById와 같은 메서드에서 기본적으로 Optional을 반환하기 때문에 현업에서도 이를 따르는 것이 일반적입니다.
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

2. Service 계층에서 Optional을 사용하는 경우

서비스 계층에서는 비즈니스 로직을 처리하고, 호출자(주로 Controller)에게 데이터를 전달하는 역할을 합니다. 따라서 Optional을 사용할지 여부는 상황에 따라 달라질 수 있습니다.

서비스 계층에서 Optional을 반환

  • 이유: 호출자에게 데이터의 유무를 명확히 전달할 필요가 있는 경우.
  • 사용 상황: 호출자가 결과가 없을 가능성을 처리해야 할 때.
public Optional<User> getUserById(Long id) {
    return userRepository.findById(id);
}

Optional을 해제하고 처리

  • 이유: 비즈니스 로직에 따라 명확한 값 또는 예외를 반환해야 할 때.
  • 사용 상황: 호출자가 null 또는 Optional을 처리하지 않아도 되도록 간결하게 처리하고자 할 때.
public User getUserById(Long id) {
    return userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException("User not found"));
}

3. Controller에서 ResponseEntity를 사용하는 이유

ResponseEntity의 역할

  • ResponseEntity는 HTTP 응답의 상태 코드, 헤더, 본문(body)을 명시적으로 제어할 수 있는 클래스로, RESTful API 설계에서 매우 유용합니다.

ResponseEntity를 사용하는 이유

  • 명확한 상태 코드 설정
    • 성공, 실패, 리다이렉션 등을 명확히 표현할 수 있습니다.
    • 예: 200 OK, 201 CREATED, 404 NOT FOUND, 500 INTERNAL SERVER ERROR 등.
return ResponseEntity.status(HttpStatus.CREATED).body("Resource created");
  • 응답 헤더 설정
    • HTTP 헤더를 동적으로 추가하거나 수정할 수 있습니다.
    return ResponseEntity.ok()
            .header("Custom-Header", "value")
            .body("Response with header");
  • 클라이언트와의 명확한 소통
    • 상태 코드와 응답 내용을 명확히 전달하여 클라이언트가 응답을 올바르게 처리할 수 있게 합니다.

 

일반적으로 ResponseEntity를 사용하는 경우

  • RESTful API 개발 시, 상태 코드와 응답 본문을 명확히 정의해야 하는 대부분의 경우.
  • 예외 상황에 따른 다양한 상태 코드를 처리하고자 할 때.

4. Optional과 ResponseEntity의 조합

서비스 계층에서 Optional을 반환받은 결과를 컨트롤러에서 처리하여 ResponseEntity로 반환하는 패턴은 매우 일반적입니다.

@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
    Optional<User> user = userService.getUserById(id);
    return user.map(ResponseEntity::ok)
               .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).body(null));
}

결론

  • Repository 계층에서는 Optional을 반환하는 것이 일반적입니다. 이는 데이터의 유무를 명확히 표현하고, null-safe 코드를 작성하기 위함입니다.
  • Service 계층에서는 상황에 따라 Optional을 반환하거나 해제합니다. 호출자가 값을 직접 처리해야 한다면 Optional을 반환하고, 그렇지 않다면 명확한 값 또는 예외를 반환합니다.
  • Controller에서는 ResponseEntity를 사용하여 HTTP 응답을 명확히 정의하고, 클라이언트와의 소통을 용이하게 합니다.

이전 글:

2024.12.01 - [IT] - 자바 람다식과 스트림: 사용법, 연습법, 그리고 Optional까지

 

자바 람다식과 스트림: 사용법, 연습법, 그리고 Optional까지

1. 소개: 람다식과 스트림의 중요성자바 8은 함수형 프로그래밍의 요소를 도입하면서 개발자들에게 함수를 값으로 다루는 방식과 더불어 데이터 처리 방식을 혁신적으로 바꾸는 도구를 제공했

0and24.tistory.com

 

참고 자료:

 

Optional (Java SE 11 & JDK 11 )

If a value is present, returns the result of applying the given Optional-bearing mapping function to the value, otherwise returns an empty Optional. This method is similar to map(Function), but the mapping function is one whose result is already an Optiona

docs.oracle.com

 

Spring : ResponseEntity를 사용해야 하는 이유

ResponseEntity를 사용해야 하는 이유 우리는 왜 Restful API를 만드는 것일까요? Restful API를 만드는 가장 큰 이유는 Client Side를 정형화된 플랫폼이 아닌 모바일, PC, 어플리케이션 등 플랫폼에 제약을 두

dev-splin.github.io