본문 바로가기

TIL(Today I Learned)

TIL-231020(자바 Record)

📝오늘 공부한 것

  • Record 공부
  • 커리어톤 참여하기
  • 프로그래머스 문제풀기
  • 이력서 수정하기

📌 Record

record는 불변 데이터 객체를 쉽게 생성할 수 있게 해주는 새로운 타입의 Java 클래스이다.

 

boilerplate code란 최소한의 변경으로 여러 곳에서 재사용되며, 반복적으로 비슷한 형태를 띄는 코드를 말한다.(getter, equals, hasCode, toString 등)

이러한 boilerplate code는 lombok이나 IDE로 코드를 간결하게 만들 수는 있지만 근본적으로 자바가 가지고 있는 한계를 해결할 수는 없다.

이를 극복하기 위해 추가한 기능이 record이다.

 

record 레코드명(컴포넌트1, 컴포넌트2, ...) { }

 

[ record의 목표 ]

  • 객체 지향의 사상에 맞게 데이터를 간결하게 표현하기 위한 방법을 제공한다.
  • 개발자가 동작을 확장하는 것보다 불변 데이터를 모델링하는데 집중하도록 도와준다.
  • 데이터 지향 메서드들을 자동으로 구현한다.
  • 단, java beans를 대체하기 위한 기술은 아니다.
    애너테이션 지향적인 코드를 생성하기 위한 기능도 아니다.

 

[ 클래스 vs record ]

다른 점

  • 다른 클래스를 상속받을 수 없다.
  • 암시적으로 final이며, abstract로 선언될 수 없다. 
  • 인스턴스 필드를 선언할 수 없다. static 필드는 생성이 가능하다.
    -> 헤더에서 정의한 멤버만을 record에서 관리하기 위함이다.
  • 멤버변수는 private final로 선언된다.
  • 필드별 getter가 자동으로 생성된다.
  • equals, hashcode, toString을 자동으로 생성한다.
    -> 컴파일러는 헤더를 통해 내부 필드를 추론하는데, 코드에 명시적으로 접근자와 생성자, toString, equals, hasCode를 선언하지 않아도 이에 대한 구현을 자동으로 제공한다.
  • 모든 멤버변수를 인자로 하는 public 생성자를 자동으로 생성한다.
    -> @AllArgsContructor와 유사하지만, record는 불변 데이터를 다루므로 생성자가 실행될 때 인스턴트 필드를 수정할 수 없다.
  • 기본생성자는 제공하지 않으므로 필요한 경우 직접 생성해야 한다.

비슷한 점

  • 클래스 내에서 record를 선언할 수 있다. 중첩된 record는 암시적으로 static으로 선언된다.
  • 지네릭 타입으로 선언이 가능하다.
  • 인터페이스를 구현할 수 있다.
  • body에 static 필드, static 메서드, static 이니셜라이저, 생성자, 인스턴스 메서드, 중첩 타입(클래스, 인터페이스, 열거형 등)을 선언할 수 있다.
  • record나 record의 각 컴포넌트에 애노테이션을 달수 있다.
  • new 키워드를 통해 객체화가 가능하다.

 

[ 컴팩트 생성자 ]

컴팩트 생성자는 생성자 매개 변수를 받는 부분이 사라진 형태이다.

개발자가 명시적으로 인스턴스 필드를 초기화하지 않아도 컴팩트 생성자의 마지막 부분에 초기화 구문이 자동으로 삽입된다. 일반적으로 사용하는 표준 생성자와는 달리 컴팩트 생성자 내부에서는 인스턴스 필드에 접근할 수는 있지만, 인스턴스 필드 값을 직접 변경하는 것은 불가능하다.

따라서, 컴팩트 생성자에는 컴포넌트로 들어온 값을 불변으로 만들거나 불변식을 만족하는지(유효성 체크, null check) 등의 작업을 하기에 적합하다.

 

[ record는 entity로 X, DTO로!! ]

hibernate와 같은 jpa는 프록시 생성을 위해 인수 생성자, non-final 필드, setter 및 non-final 클래스가 없는 entity에 의존한다.

JPA의 지연 로딩 방식을 사용할 때, JPA는 entity 객체의 프록시 객체를 생성한다. 프록시 객체는 원본 객체를 상속하여 생성된 확장 클래스이다. 그러나 record는 상속이 불가능하므로 entity로 사용할 수 없다.

즉, 프록시를 생성하기 위해서 entity는 불변이면 안된다.

 

쿼리 결과를 매핑할 때 객체를 인스턴스화 할 수 있도록 매개변수가 없는 생성자가 필요하다.
-> record는 매개변수가 없는 생성자를 제공하지 않는다.

(record는 불변 객체이기 때문에 setter를 사용할 수 없다. 이로 인해 모든 필드의 값을 입력한 후에 생성할 수 있다.)

 

접근자 메서드인 getter가 필수 명명 규칙을 따르지 않는다. record의 getter는 필드명을 그대로 사용한다.

-> 쿼리 결과 처리 후 수행할 getter, setter에 접근할 수 없다.

 

record는 한번 값이 정해지고나면 setter를 통해 값을 변경할 수 없는데, 자바 내부에서 데이터 가공 시 중간에 변질될 우려가 없다. 따라서 DB에 저장된 데이터의 전송 객체로 아주 적합하다.

실전프로젝트인 '행동대장'에서 DTO를 만들 때 record를 사용하였었다.
클라이언트로부터 데이터를 전달받고 클라이언트로 데이터를 전달할 때 데이터의 무결성을 위해 record를 선택하였다. 
데이터의 가공이 필요없는 단순히 데이터만 전달해야할 때 record를 사용하면 좋을것 같다.
👉🏻 프로젝트에서 record 사용하기 

 

 

 

References :

https://openjdk.org/jeps/384

https://docs.oracle.com/en/java/javase/14/language/records.html#GUID-6699E26F-4A9B-4393-A08B-1E47D4B2D263

https://velog.io/@power0080/java%EC%9E%90%EB%B0%94-record%EB%A5%BC-entity%EB%A1%9C

https://blog.hexabrain.net/399