사다리 타기 미션을 수행하면서, 이런 밑줄을 자주 마주하게 됐습니다.
처음에는 `인텔리제이는 인텔리전트하니까~`라는 이유만으로 변경을 했었습니다.
정말 그럴까요?
그렇다면, 질문! 변경 후 다가올 파급효과에 대해 알고 있나요?
Record가 뭔가요?
우선, Record 클래스에 대해 간단하게 정리해 보겠습니다.
- Java 16부터 공식 기능으로 채택
- 불변(immutable) 객체를 쉽게 생성해 주는 클래스의 한 종류
- record 필드에 인스턴스(instance) 변수를 선언할 수 없고, 정적(static) 변수만 가능
- abstract로 선언 불가
- 레코드와 컴포넌트는 암시적으로 final로 선언
- getter, toString, equals, hashCode 자동 생성
Convert to a Record는 리팩터링이 아니다
중요한 부분은 다음과 같습니다.
- 레코드와 컴포넌트는 암시적으로 final로 선언
- getter, toString, equals, hashCode 자동 생성
일반 클래스를 Record로 변경했을 때 어떤 일이 일어나는지 코드로 확인해 보겠습니다.
Ladder 일반 클래스입니다.
final로 선언된 멤버가 있고 생성자, getter가 있습니다.
record로 변경해 보겠습니다.
오! 엄청 간단해졌어요!
record 이름 옆에 컴포넌트(component)로 멤버 변수가 들어갔고, 생성자와 getter가 사라졌네요.
오.. 가독성 좋게 리팩터링 된 건가? 하는 생각에 기분이 좋아집니다.
그렇다면, 다시 class로 convert 해볼까요?
뭔가 달라졌어요.
우선, 클래스에 final이 붙었네요.
getter 메서드도 getLines에서 lines로 이름이 바뀌었어요.
Override 하지 않은 equals, hashCode, toString이 생겼어요.
일반 클래스를 record로 자동 변환했을 때, 리팩터링인 줄 알았는데 기능이 추가되는군요!
코드 커버리지(Code Coverage)에 영향을 줄 수 있다
IntelliJ가 추천해 준 대로 했을 뿐인데, 의도하지 않은 코드들이 추가될 수 있다는 문제가 있었어요.
의도하지 않은 코드가 생긴다는 것은, 불필요한 코드가 생성되었다는 의미이기도 합니다.
그렇다면, 코드 커버리지에도 영향을 줄 수 있지 않을까요?
여기서 잠깐! 코드 커버리지가 무엇인가요?
테스트 케이스가 코드를 얼마나 커버하고 있는지 나타내는 지표
프로덕션 로직을 테스트 코드로 검증하고 있는 지 확인해 볼 수 있는 유용한 지표가 됩니다.
다시 본론으로 돌아와서, 코드 커버리지를 확인해보겠습니다.
record로 정의된 Line 클래스입니다.
Line의 수치를 보면 Method 80%, Line 90% 입니다.
이상합니다.
분명 모든 기능에 대해 테스트 코드를 작성했는데, 왜 수치가 100%가 아닐까요?
일반 class로 정의된 Line을 분석해보겠습니다.
Line의 모든 수치가 100%로 나왔네요!
답은 record의 특징에 있었습니다.
Line을 record로 정의한 순간, equals, hashCode, toString 재정의 메서드가 커버되지 않았기 때문입니다.
사실상 해당 로직은 프로덕션 코드에는 사용되지 않은 코드인데, 재정의된 메서드 때문에 커버리지 수치를 떨어뜨리는 문제가 발생했습니다.
의도하지 않은 record 사용은 커버리지에 불리하게 작용할 수 있다는 점을 유의해야 겠습니다.
그래서, 도메인에 Record를 써도 될까요?
제 의견은 ‘도메인에 record 사용을 지양한다’입니다.
record는 그 자체로 불변 객체임을 한 눈에 알 수 있고, 코드 가독성이 좋아진다는 장점이 있지만
hashCode, equals, toString과 같은 메서드를 묵시적으로 Override 하기 때문에, 앞서 말한 부작용이 발생할 수 있습니다.
통상적으로 Record는 도메인이 아닌 DTO에 사용합니다.
openJdk 공식 문서에 의하면 record는 불변 데이터를 위한 이동 수단으로 사용하는 데 목적이 있습니다.
Enhance the Java programming language with records, which are classes that act as transparent carriers for immutable data. Records can be thought of as nominal tuples.
정리
- Record를 잘 알고 쓰자.
- 특히, 레코드와 컴포넌트는 암시적으로 final로 선언된다.
- 특히, getter, toString, equals, hashCode 자동 Override된다.
- Convert to Record는 리팩터링이 아니다.
- 코드 커버리지(Code Coverage)에 영향을 고려하자.
- 도메인에 record 사용을 지양하는 것이 좋다.
'Java' 카테고리의 다른 글
상태를 공유하는 시나리오 테스트 DynamicTest (25) | 2024.04.01 |
---|---|
의존성 역전을 언제 쓰나요? (3) | 2024.03.25 |
돈은 머니머니해도 BigDecimal를 사용하자 (22) | 2024.03.18 |
객체 생성 방식에 기준이 있나요? (28) | 2024.03.11 |