[Spring] null을 안전하게 다루는 방법

null처리에 대해서 맘에 드는 글이 없어서 직접 정리하게 되었음

 

프로젝트를 시작할 때 @NonNullApi를 패키지 레벨에 선언해놓자.

 @NonNullApi
 package kr.webgori.lolien.api;

패키지에 package-info.java 파일을 만들고, @NonNullApi 애너테이션을 선언하면 패키지 하위의 class method의 인자 또는 리턴이 null일 경우 IDE에서 Warning으로 알려준다.

 

기본으로 null을 쓰지 말자

 /**
  * 이렇게 사용하지 마시고.
  */
 private void wrongWay() {
   User user = null;
 }
 
 /**
  * 이렇게 사용하세요.
  */
 private void correctWay() {
   User user = new User();
 }

선언문이나 조건문에서 null이 꼭 필요한 경우 말고는 사용하지 말자

 

method에서 null이 리턴될수도 있을 때는?

 /**
  * 이렇게 사용하지 마시고.
  */
 private User getUser(int userId) {
   return userRepository.findByUserId(userId);
 }
 
 /**
  * 이렇게도 사용하지 마시고.
  */
 private User getUser(int userId) {
   return userRepository.findById(userId).orElse(null);
 }
 
 /**
  * 이렇게 사용하세요.
  */
 private Optional<User> getUser(int userId) {
   return userRepository.findById(userId);
 }
 
 /**
  * 이렇게도 사용해보세요.
  */
 private UserResponse getUserInfo(int userId) {
   UserResponse response = UserResponse.builder().build();
 
   int userId = 1;
   Optional<User> optionalUser = getUser(userId);
   optionalUser.ifPresent(response::setUser);
   
   return response;
 }

Optional을 사용하자

Optional은 method return 값이 null일수도 있을 때만 사용한다.

 

Optional의 orElseGet으로 빈 객체를 리턴하자.

 /**
  * 이렇게 사용하지 마시고.
  */
 private User getUser(int userId) {
   return userRepository.findByUserId(userId);
 }
 
 /**
  * 이렇게도 사용하지 마시고.
  */
 private User getUser(int userId) {
   return userRepository.findById(userId).orElse(null);
 }
 
 /**
  * 이렇게 사용하세요.
  */
 private User getUser(int userId) {
   return userRepository
      .findById(userId)
      .orElseGet(() -> User.builder().build());
 }

 

조건문에서 null인지 확인이 필요하다면 == null을 사용하자.

 /**
  * 이렇게 사용하지 마시고.
  */
 private User getUserName(int userId) {
   User user = getUser(userId);
   
   if (Objects.isNull(user)) {
     throw new IllegalArgumentException("사용자를 찾을 수 없습니다.");
   }
   
   return user.getName();
 }
 
 /**
  * 이렇게도 사용하지 마시고.
  */
 private User getUserName(int userId) {
   Optional<User> user = getUser(userId);
   
   boolean present = user.isPresent();
   
   if (!present) {
     throw new IllegalArgumentException("사용자를 찾을 수 없습니다.");
   }
   
   return user.getName();
 }
 
 /**
  * 이렇게 사용하세요.
  */
 private User getUserName(int userId) {
   User user = getUser(userId);
   String userName = user.getName();
   
   if (userName == null) {
     throw new IllegalArgumentException("사용자를 찾을 수 없습니다.");
   }
   
   return user.getName();
 }

 

Client가 받을 Response에 null을 리턴하지 마세요.

@JsonInclude(NON_NULL)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserResponse {
  private UserDto user;
}
{}

@JsonInclude(NON_NULL) 애너테이션을 사용하면 null일 경우 JSON Key를 리턴하지 않습니다.

 

참고

null 서바이벌 가이드