필드 Null 체크 없애기, Optional<String> vs String?

2023. 10. 13. 18:22Java

728x90

최근 게시판 프로젝트로 끝까지 한번 해보자 라는 프로젝트를 진행하고 있던 와중,

참 거슬리는 코드가 눈에 들어왔다.

보이는가 저 거슬리는 if 문들,,,

저게 틀린 코드라고 생각하지는 않았기에 저렇게 적었다만은,

뭐랄까 참 지저분해 보이는 코드라서 리팩토링 욕구(?)가 솟았다. 

if 문을 보면 짖는 개

그래서 어떻게 할까 고민을 하던 와중 먼저

저 코드의 의미, 기능을 생각해보니 다음과 같았다.

 

MemberUpdateDTO 의 Null 이 아닌 필드들만 DB에 업데이트 하기!

 

즉 필드의 null 체크를 좀 더 야무지게 할 수 있는 방법을 찾으면 됐다.

그러다 Java 8 부터 도입된 Optional 을 사용하면 훨씬 더 안전하고,

Java 표준 스펙에 의거한 코드를 만들 수 있지 않을 까 생각했다.

 

그래서 MemberUpdateDTO 의 null 이 올수 있는 필드를 Optional<String> 으로 바꿨다.

아 노란줄 뭔데

하지만 틀렸죠? 경고문 떠버렸죠?

바꾸고 나서 자아도취 중, 바로 IDE 에서 경고문을 보냈다.

어째서, 왜, why 라는 생각과 함께, 경고문을 긁어서 검색을 해보니

나와 비슷한 고민을 한 사람들이 있었고, 야무진 반박의 글을 찾을 수 있었다.

Stephen Cellbourne, Java 8 Contributor

Of course, people will do what they want.
But we did have a clear intention when adding this feature,and it was not to be a general purpose Maybe or Some type, as much as many people would have liked us to do so. 

Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.

The key here is the focus on use as a return type. The class is definitively not intended for use as a property of a Java Bean. Witness to this is that Optional does not implement Serializable, which is generally necessary for widespread use as a property of an object.

[번역]
물론 사람들은 자신이 원하는 것을 할 것입니다.
하지만 이 기능을 추가할 때 저희는 분명한 의도를 가지고 있었고, 많은 사람이 원하는 것처럼
일반적인 목적의 Maybe 또는 Some 타입이 아니었습니다.

우리의 의도는 "결과가 없음"을 표현하는 명확한 방법이 필요한
라이브러리 메서드 반환 유형에 대해 제한된 메커니즘을 제공하는 것이었고,
이러한 경우 null을 사용하면 오류가 발생할 가능성이 압도적으로 높았습니다.

여기서 핵심은 return type으로 사용하는 데 초점을 맞추는 것입니다.
이 클래스는 Java Bean의 속성(해석: class 의 필드)으로 사용하기 위한 것이 아닙니다.
이를 증명하는 것은 Optional이 일반적으로 객체의 프로퍼티로 광범위하게 사용되는 데 필요한 Serializable을 구현하지 않는다는 점입니다.

[출처] : https://blog.joda.org/2014/11/optional-in-java-se-8.html

즉, 필드의 타입을 Optional 로 바꾸는 것은 Optional의 설계에

어긋나는 코드 스타일이기에 '지양' 해야한다라고 알 수 있다.

 

그렇다면 설계자의 의도에 맞게! 메서드의 return 타입, 즉

getter 의 return type 으로 Optional<String> 으로 바꿔보자 라고 생각했다.

 

그러고 나서, 저 원문의 첫 문단이 어딘가에서 인용된 글임을 보고 링크를 타고 건너갔더니,

다른 외국 형님의 stackoverflow 답변이었다.

 

Brian Goetz

근데 답변자가 Brian Goetz 라는 또다른 Java 8 contributor 이다,,,

내용인 즉슨 다음과 같다.

Of course, people will do what they want.
But we did have a clear intention when adding this feature, and it was not to be a general purpose Maybe type, as much as many people would have liked us to do so.

Our intention was to provide a limited mechanism for library method return types
where there needed to be a clear way to represent "no result",
and using null for such was overwhelmingly likely to cause errors.

For example, you probably should never use it for something that returns an array of results, or a list of results; instead return an empty array or list.
You should almost never use it as a field of something or a method parameter.
I think routinely using it as a return value for getters would definitely be over-use.

[의역]
위랑 비슷하게, overuse 하지 말라 등등,,,
getter 의 return value 로 쓰는 것은 over-use 다 ^^7

[출처] : https://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type/26328555#26328555

아 ㅋㅋㅋㅋㅋㅋ 내가 했던 것 또한 결국 over-use 이다.

그러면 걍 냅둬야하나...?

if 문을 보면 구역질이 난다고

고민을 해도해도 결국 null 을 야무지게 해결하기 위해서 생각한 건 Optional 이였다.

그래서 생각해보니, Optional 의 static 메서드들을 사용하면 가능하겠다는 생각이 들었다.

 

그래서 다음과 같이, DTO 를 수정하는 대신 다음과 같이 updateMember 메서드를 다음과 같이 바꿨다.

확실히 if 문을 도배하는 것 보다 훨씬 가독성이 좋다고 생각이 든다.

특히, null 필드 체크를 더 해야한다면, 가독성은 더더욱 올라가는 코드라고 생각이 들었다.

하지만 동시에 걱정도 있었다.

보기는 좋은 데, 효율성이 좋을까? 엄연히 기본형 타입에서 참조형 타입으로 바꿨는 데?

이에 대한 내 결론은 맞는 말이지만, 그 효율성의 저하가 미미하기에

바꾼 코드가 훨씬 좋다고 생각한다.

 

이 경우, 

오버헤드 <<<<< 가독성이 주는 이로움

라고 생각해, 코드를 바꾸는 것에 대해 이의는 없는 걸로!

 

P.S.

예전에 JavaScript 배웠을 때 Optional Chaining 이면 한 방에 됐을 텐데,,, 

꽃이 지고 나서야 꽃인 줄 알았다,,, ㅠ,,, 

728x90