자바8에서의 null처리

2018, Jul 12    

다년간 개발자로 일해 본 사람이라면 절대 null이 올수 없다고 설계한 값들은 가정에 지나지 않는다는 것을 알 것이다.
서비스 운영을 하면 예기치 않은 케이스가 계속 발생하고, 필수값으로 설정했지만 null값이 발생하여
신경써 주지 않으면 NullPointerException을 만나게 된다.
아래는 흔히 볼 수 있는 null처리를 신경쓴 코드이다.

//null에 대해 위험한 로직
public String getCarInsuranceName(Person person) {
    return person.getCar().getInsurance().getName();
}

//NullException을 신경쓴 로직 : if가 많은 깊은 의심.
public String getCarInsuranceNameNullCheck(Person person) {

    if(person != null) {
        Car car = person.getCar();

        if(car != null) {
            Insurance insurance = car.getInsurance();
            if(insurance != null) {
                return insurance.getName();
            }
        }
    }

    return "Unknown";
}

//NullException을 신경쓴 로직 : 너무 많은 출구
public String getCarInsuranceNameNullCheck2(Person person) {

    if(person != null) {
        return "Unknown";
    }

    Car car = person.getCar();
    if(car != null) {
        return "Unknown";
    }

    Insurance insurance = car.getInsurance();
    if(insurance == null) {
        return "Unknown";
    }
    return insurance.getName();
}

getCarInsuranceNameNullCheck 함수는 null에 대해 안전한 알고리즘이지만, 많은 if로 인해 들여쓰기가 많아져서 정작 이 알고리즘이 하고자 하는 바가 무엇인지 잘 눈에 들어오지 않는다는 단점이 있다.
getCarInsuranceNameNullCheck2 함수는 중첩 if블록을 없앴지만, 메서드에 네 개의 출구가 생겨 반환되는 기본값 ‘Unknown’이 반복 된다는 점에서 유지보수가 어려워진다. 반환로직이 변경된다면 4번의 코드변경이 필요하고 오타등의 실수가 있을 수 있다. 물론 ‘Unknown’을 상수로 만들어 사용하는 것도 방법이겠지만, 만약 누군가가 null일 수도 있다는 사실을 깜박하게 된다면 무용지물 일 것이다.

다른 언어에서의 null 처리

책에서 그루비가 소개되어 있는데, 코틀린에서의 null 처리 방식과 비슷하여 코틀린을 예를 들어 보려 한다.
코틀린의 경우 인자를 선언할때 명시적으로 null을 허용할지 안할지를 명시하게 되어 있다.
null을 허용한다는 표식(?)을 하지 않으면 기본적으로 null인 값이 함수로 전달 될때 NullPointerException이 발생한다.

fun kotlinFun1(s: String) : Int{ // null이 전달 될시 이부분에서 Exception발생!
  return s.toInt()  
}
//함수에서 null을 허용하지 않기 때문에 함수 호출 부분에서 syntax 오류가 발생한다.
kotlinFun1(null)

fun kotlinFun2(s: String?) : Int{ // ?를 붙이면 null이 전달되어도 오류가 발생하지 않음
  //s가 null인 경우에 대한 처리를 해주지 않으면 syntax오류가 발생함.
  return s.toInt()  
}

자바에서라면 s.toInt() 부분에서 런타임 Exception이 발생했겠지만, ?키워드를 붙어 주지 않았으므로 함수 호출부분에서 syntax 오류가 발생한다. kotlinFun2 함수의 경우 함수 인자에 ?를 붙임으로서 null을 허용한다고 명시 하였으나, s.toInt 부분에 s에 대한 null처리를 하지 않았으므로 syntax오류가 발생함으로서 컴파일 자체가 되지 않는다.

결론적으로 그루비나 코틀린 같은 언어에서는 변수 자체에 null일수도 있는지 아닌지를 명시적으로 선언하도록 되어 있으며, null을 허용하는 경우에는 그에 따른 안전한 null처리를 해주지 않으면 컴파일 자체가 되지 않도록 함으로서 null에 대한 안정성을 주고 있다.

자바에서는?


자바8에서는 하스켈과 스칼라의 ‘선택형값’ 개념의 영향을 받아서 Optional이라는 새로운 클래스를 제공한다.
Optional는 null일수 있고 아닐수도 있는 값을 감싸는 클래스이다.
Optional<Car> c 과 같이 선언했을때, Optional안의 객체가 null인 경우, null을 할당 하는 것이 아니라 Optional.empty 로 접근되는 빈값을 가진 Optional로 표현된다.

null자체를 참조하려고 하면 NullPointerException이 발생하지만 Optional.empty을 활용하면 다양한 방식으로 null부터 안전한 코드를 이전보다는 가독성있게, 쉽게 만들수 있다.(고 한다..)

Optional에 대한 사용 패턴은 다음 페이지에 소개 하도록 하겠다.