[Java] - 자바에서 null을 안전하게 다루는 방법

링딩·2023년 4월 16일
0

Computer Science

목록 보기
36/49




1. null을 안전히 다루고 싶다.


🔎 null이 무엇일까?

각각 다르게 정의하고 있지만 큰 부류로

A. 객체가 없거나 상태가 정의되지 않은 상태.

¹ String str = ""; 와 같이 “빈 값”이 객체에 할당되어 있기 때문에 null과는 다르며,

² 0도 0이라는 값이 할당된 상태이기 때문에 null과는 전혀 다르다.



Null로 인한 문제들

1) null 참조

  • 특별한 값이 없음을 나타내려고 null을 도입했고 이 값을 사용하려고 할 때 오류를 내도록 설계
  • 두 참조값이 null일 때 두 참조는 동일하다고 판단

2) 소프트웨어 결함 통계

  • 구글 플레이 앱 약 1000개를 조사해보니 NullPointer 문제가 150여 개 발생

-> 그만큼 null과 관련된 Exception이나 오류가 많이 흔히 발생하고 있다는 뜻



2. null을 안전하게 다루는 방법

1) @NonNullApi를 패키지 레벨에 선언

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

2. 기본으로 null을 쓰지 말자

  • 선언문이나 조건문에서 null이 꼭 필요한 경우 말고는 사용하지 말자.
 /**
  * 이렇게 사용하지 말고
  */
 private void wrongWay() {
   User user = null;
 }
 
 /**
  * 이렇게 사용하세요.
  */
 private void correctWay() {
   User user = new User();
 }

만일 메소드에서 null이 리턴될 수도 있지 않을까?

A. Optional을 이용해.

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

+) Optional의 orElseGet으로 '빈 객체'를 리턴 해주자

 /**
  * 사용 ㄴㄴ
  */
 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); //Optional
   optionalUser.ifPresent(response::setUser);
   
   return response;
 }
 
 
 //빈 객체로 리턴해주자
 
  private User getUser(int userId) {
   return userRepository
      .findById(userId)
      .orElseGet(() -> User.builder().build());
 }
 



3. 조건문에서 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();
 }

이렇게 null인 경우 예외로 던져주자



4. Client가 받아야 되는 Response가 null이여도 null을 리턴하지 말자

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

  • API에 null을 최대한 쓰지 말아라

    • null로 지나치게 유연한 메서드를 만들지 말고 명시적인 메서드를 만들어라
      -> API에 null을 받아서 분기처리 하지말고 애초에 null이 있을 때 메서드와 없을 때 메서드를 나눠서 만들어라
    • null을 반환하지 말라
      null을 반환하지 말고 예외를 던져라
      빈 반환 값은 빈 컬렉션이나 Null 객체를 활용하라
    • 메소드의 인자를 받을 때 Objects.requireNonNull()을 사용하여 방어하자
  • 사전 조건과 사후 조건을 확인하라: “계약에 의한 설계”

    • Spring의 Assert 클래스
  • (상태와 같이) null의 범위를 지역(클랫, 메서드)에 제한하라

    • 상태와 비슷하게 null도 지역적으로 제한하면 큰 문제가 안된다
    • 클래스와 메서드를 작게 만들어라
    • 설계가 잘 된 코드에선 null의 위험도 줄어든다
  • 초기화를 명확히 하라



출처

eastglow 님의 글을 참조하여 작성하였습니다.

위에서 추천해주신 자료 입니다

webgori 님의 글을 참조하여 작성하였습니다.

profile
초짜 백엔드 개린이

0개의 댓글