백엔드 개발자 블로그

null 리턴은 왜 나쁠까? 본문

테크 블로그 리뷰

null 리턴은 왜 나쁠까?

backend-dev 2024. 3. 13. 14:37

null 리턴 나쁜점

코드를 읽는 사람 입장에서 복잡성을 만듭니다. 

null 값 하나로 문맥 정보를 추론해야하므로 개발자의 생산성이 떨어질 수 있습니다.

 

 

 해결안

로그에 맥락 남기기

주석은 실제 코드가 아니므로, 로그에 맥락을 남겨 맥락을 쉽게 이해할 수 있도록 하는 것이 좋습니다.

예외 처리나 로깅을 통해 맥락을 코드에 명시적으로 드러내봅시다.

class UserRepository {
  fun findByName(name: String): User {
    val result: User? = db.getUserBy(name)
	  if (result == null) {
		throw IllegalStateException("""|
	      |인사관리 시스템과 동기화 되지 않은 유저의 이름을 입력한 경우 이 메시지를 볼 수 있습니다.
	      |매주 월->화 넘어가는 자정에 인사 관리 시스템과의 데이터 동기화가 수행되므로, 새로운 사람이 월요일이 아닌 다른 날짜에 입사하지 않았는지 확인하십시오.
	      |다음 주 월요일까지 기다리거나, 수동 동기화를 실행하면 문제가 해결될 수 있습니다.
	      |
	      |인사 관리 시스템과의 데이터 동기화 로직은 UserRepositorySync 클래스를 참고하십시오.
	      |문제가 된 name=[$name]
	      """.trimMargin())
	  }
      return result
  }
}

 

 

맥락 처리를 위한 기능 만들기

먼저 if문을 사용하여 맥락 처리를 해봅시다.

재시도를 해도 null인 경우를 설명하지 못합니다.

val user: User? = userRepository.findByName("김토스")
if (user == null) {
  // user가 null이면 유저가 인사관리 시스템과 동기화 되지 않은 경우임.
  // 동기화를 한 번 트리거 시켜주도록 하자
  userRepositorySync.trigger()
}

val user2: User? = userRepository.findByName("김토스")
print(user2!!) // 위에서 동기화를 한 번 시켜주었기 때문에 null일 수 없다

 

 

retryHandlerWhenMissing 함수를 통해 맥략 처리를 해봅시다.

if를 통한 맥락 표현을 없앴고, user에 null이 안들어갑니다.

하지만 retryHandlerWhenMissing 함수가 무조건 실행 됩니다.

val user: User = userRepository.findByName(
    name = "김토스",
    retryHandlerWhenMissing={ userRepositorySync.trigger() }
)

print(user) // non-null type

 

필요할 때만 제공하기

 

적절한 기본값을 주거나 필요할 때만 기능을 사용할 수 있도록 제공하는 방식으로 바꿔봅시다.

기본값 "김토스", ResyncWhenUsermissing로 필요할 때만 사용하도록 구현해봅시다.

val user: User = userRepository
  .withRetryPolicy(ResyncWhenUserMissing()) // 이 라인을 삭제해도 findByName() 호출에는 문제 없음
  .findByName("김토스")

print(user) // non-null type

retryHandlerWhenMissing 에 무엇을 넣어야 하는지 파악해야 되는 문제를 줄일 수 있게 되었고,

로직을 그대로 노출하지 않으면서도 코드의 의도를 더 분명히 드러낼 수 있게 되었습니다.

 

 

하지만, 이런 식으로 코드를 작성하게 되면 UserRepository를 구현하기 까다로워집니다.

import org.springframework.data.jpa.repository.JpaRepository

interface UserRepository : JpaRepository<User, Long> {
    // 이건 Spring Framework가 알아서 해주는데,
    fun findByName(name: String): User

    // 이건 어떻게 하지?
    fun withRetryPolicy(retryPolicy: RetryPolicy): UserRepository
}

 

 

 

 

이런 식으로 개선하다보면 팀원들의 부담이 덜어줄 수 있는 코드륵 작성할 수 있게 됩니다.