Search

값이 없음을 표현하는 여러가지 방법

태그
프로그래밍 일반
작성 상태
작성 완료
작성일
2024/01/20
참고 링크
참고 링크 2

매직값 반환하기

매직값이란, 함수의 반환 타입에 부합하면서 특별한 의미가 부여된 값을 말한다.
예를 들어, 회원의 나이를 반환하는 함수 getAge()에서 회원의 나이가 등록되지 않은 경우 -1을 반환하는 경우가 매직값을 이용한 경우다.
public class Member{ private Integer age; // 생성자 생략 int getAge() { if(age == null) { return -1; } return age; } }
Java
복사

매직값은 위험하다

매직값은 결국 그 함수의 내부 코드를 분석하거나, 관련 문서를 읽어야만 그 의미를 알고 그 함수를 호출하는 코드에서 이를 적절히 처리할 수 있다. 그렇지 않은 경우 문제가 생길 수 있고, 그 문제를 찾기조차 힘들 것이다.
아래 코드는 회원의 통계를 담당하는 클래스에서, 회원의 평균 나이를 계산하는 코드다.
public MemberStats { private List<Member> members; // 생성자 생략 public Integer getMeanAge() { if(members.isEmpty()) { return null; } int sum = sumAge(); return sum / members.size(); } private int sumAge() { int sum = 0; for(Member member : members) { sum += member.getAge(); } return sum; } }
Java
복사
논리적으로 오류를 찾을 수 없다. 하지만, Member.getAge()에서 매직값을 사용한 것 때문에 문제가 생긴다.
회원의 나이가 등록되지 않았다면 -1을 반환하기 때문에, 회원의 나이의 합을 구할때 문제가 생긴다.
게다가 이는 테스트코드로 확인하기 쉽지 않다. 애초에 이 코드를 작성한 개발자는 회원의 나이가 없을 경우를 생각조차 하지 못했다. 따라서, 회원의 나이가 등록되지 않은 경우를 테스트하는 것을 생각하지 못했을 것이기 때문이다.
매직값을 사용하는 것 보단 후술할 null을 반환하는 것이 좋다.

Null 혹은 Optional 반환하기

이런 코드는 java의 컬렉션 API에서 쉽게 찾을 수 있다. 아래는 Queue 인터페이스의 일부다.
인터페이스이므로 그 구현 코드는 없지만, 예외적인 상황(큐가 비어있는 경우) null을 반환한다고 적혀있다.
null 안전성을 지원하지 않는 언어들에선 null 확인을 하지 않으면 NPE가 발생한다. 이는 분명히 문제가 되기 때문에, null을 반환하는 것을 지양하기도 한다. 그러나 오류가 있는지도 모르고 넘어가는 것 보다는 오류가 있음을 발견할 수 있는 것이 더 나을 것이다.

Optional

null일 가능성이 있는 객체를 감싸는 Wrapper 클래스다.
Wrapper 클래스기 때문에, 실제로 원하는 객체를 얻기 위해선 메서드를 통해 이를 꺼내야 한다. 이때, Optional에서 제공하는 메서드들은 null을 확인하는 로직을 필요로하기때문에 NPE 방지에 도움이 된다.
아래는 Stream 에서 최대 요소를 반환하는 메서드의 정의다. Optional을 반환해 이 메서드를 호출하는 코드에서 예외적인 상황을 처리할 수 있도록 해준다.
null 안전성을 지원하는 언어에서는 null이 될 수 있는 타입으로 반환형을 지정하고, 그렇지 않은 경우 Optional을 반환하면 예외적인 상황을 안전하게 전달할 수 있다.

null object pattern

null이나 Optional 대신 특별한 인스턴스를 반환하는 방법이다.
예를 들어, List를 반환하는 메서드에서, 어떤 예외적인 상황이 발생했을 때 비어있는 List를 반환하는 경우가 있다. 아래 코드는 특정 회원이 작성한 게시글 목록을 조회하는 메서드다. 게시글이 없는 경우, 비어있는 리스트를 반환한다.
List<Post> getPosts(Member member) { ... return Collections.emptyList(); ... }
Java
복사
아래는 회원이 작성한 게시글 중 특정 해시태그가 사용되었는지 확인하는 메서드다.
boolean hasTag(Member member, Tag tag) { List<Post> posts = getPosts(member); long count = posts.stream() .filter(post -> post.getTags().contains(tag)) .count(); return count > 0; }
Java
복사
이 코드에서 getPosts 가 null 대신 비어있는 리스트를 제공하기 때문에 별도의 코드 없이 깔끔한 코드를 작성할 수 있다.

예상하지 못한다면?

사각형을 표현하는 클래스가 있다고 하자. 사각형이라면 당연히 각 변의 길이가 0보다 커야 할 것이다.
다이어그램을 그려주는 프로그램이 있다고 하자. 적절한 위치에 사각형 그림을 그려주기 위해서는 적절한 크기를 가지는 사각형 인스턴스를 생성해주는 클래스가 있을 것이다.
public class ContentsBoxMaker { public Box make(String contents) { ... return new Box(0,0,0,0); ... } }
Java
복사
위 코드는 어떤 예외적인 상황에서 네 변의 길이가 모두 0인 직사각형 인스턴스를 반환한다.
화면에 그림을 그려주는 클래스가 이 사실을 모른다면?
화면을 그려주는 클래스를 작성한 개발자는 다음과 같이 생각했다.
직사각형의 크기가 0일리는 없어! 왜냐하면 직사각형 내부에 콘텐츠를 표시해야 하니까!
아주 합당한 가정이다. 하지만, 실제로는 그렇지 않았고 오류가 발생한 뒤에야 이 사실을 알고 황당해 할 것이다.

예외 투척하기

말 그대로 적당한 값을 반환할 수 없는 경우 예외를 발생시키는 것이다.