ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] 새로 도입된 Record 무엇인가?
    Java 2024. 6. 12. 14:03

    Java11를 사용하다가 최근 플젝으로 인해 Java17로 바꾸면서 Record란 클래스를 접하게 되어 공부한 내용이다

     

     

    Record?

    • 자바 14 버전에 도입되고 16버전에서 정식 스펙으로 채택된 클래스이다
    • 데이터를 다루기 위한 데이터 클래스로 사용된다 
    • 자동으로 생성자를 만들어주고 getter, equals, hashCode, toString을 자동으로 생성
      필드를 final로 생성해줌으로써 불변 데이터로 관리 할 수 있음
    • record 내 각 필드(헤더에 나열한 컴포넌트)는 private final로 정의된다
    • 다른 클래스를 상속 받을 수 없지만, 인터페이스로는 구현이 가능하다. (extends : X, implements : O)
    • 레코드 내부에 멤버 변수(인스턴스 필드)를 선언할 수 없다. 그러나 static 변수는 생성이 가능하다. 이는 헤더에서 정의한 멤버만을 record에서 관리하기 위함이다

     

    하지만!

    스프링부트에서 Jackson 데이터바인딩할때는 아직 정상적으로 동작하지 않는 이슈가 있음

     

     

    Record는 왜 등장 하였는가?

    기존의 class

    import java.util.Objects;  
      
    public class BallReal {  
        private final int number;  
        private final String name;  
      
        public BallReal(int number, String name) {  
            this.number = number;  
            this.name = name;  
        }  
      
        public int getNumber() {  
            return number;  
        }  
      
        public String getName() {  
            return name;  
        }  
      
        @Override  
        public boolean equals(Object o) {  
            if (this == o) return true;  
            if (o == null || getClass() != o.getClass()) return false;  
            BallReal ballReal = (BallReal) o;  
            return number == ballReal.number && Objects.equals(name, ballReal.name);  
        }  
      
        @Override  
        public int hashCode() {  
            return Objects.hash(number, name);  
        }  
      
        @Override  
        public String toString() {  
            return "BallReal{" +  
                    "number=" + number +  
                    ", name='" + name + '\'' +  
                    '}';  
        }  
    }

     

    대표적으로 DTO를 구현하기 위해서는 getter, equals, hasCode, toString 과 같은 데이터 처리 나 특정 연산을 수행하기 위해 Override된 메소드를 반복해서 만들게 된다

     

    위와 같은 DTO가 실제 프로젝트에서는 엄청 많이 쓰이게 된다 

    이런 것들을 보일러 플레이트 코드라고 하는데 

    더보기

    💡보일러 플레이트 코드란?

    보일러 플레이트 코드(boilerplate code)는 소프트웨어 개발에서 반복적으로 사용되는 코드 조각을 가리킨다

    위 클래스에서는 getter, equals, hasCode, toString 이 여기에 해당한다

    원래는 이러한 보일러 플레이트 코드를 lombokdata class를 사용하여 중복된 코드의 양을 줄인다

     

    자바의 단점으로 이러한 보일러 플레이트 코드가 많다는 점이다 

    lombok으로 코드를 간결하게 만들 수 있지만 근본적으로 자바가 가지고 있는 한계를 해결하지는 못한다고 한다

     

     

    이러한 한계를 극복하기 위해 만들어진 기능중에 하나인 record가 만들어 지게 된 것이다

     

     

     

    record의 구조

    public record Ball(int number, String name) {  
    }

    이런 기존의 class에서 getter, 생성자, 재정의하는 메서드들을 자동으로 생성한다 

     


     

    getter()

     

    다른점은 getter()의 이름을 원래 get + (필드명) 이런식으로 getter의 이름을 붙이는데 
    record에서는 그냥 필드명으로 한다는 점이다

    public class Main {  
        public static void main(String[] args) {  
            Ball ball = new Ball(1, "a");  
            BallReal ballReal = new BallReal(1, "a");  
      
            //record  
            ball.name();  
            ball.number();  
              
            //기존 class
            ballReal.getName();  
            ballReal.getNumber();  
        }  
    }

     

     

     

    constructor(생성자)

     

    record에서는 생성자를 자동으로 생성해 주지만

    만약 값을 임의로 바꾸거나 추가적으로 처리해줘야 하는 부분에 대해서는 따로 정의 해줘야 한다

    여기에서 다른점은 

    기존에 생성자에서는 클래스명() 이런식으로 했었는데 record에서는 () 괄호를 빼고 사용한다 

    이러한 생성자를 컴팩트 생성자라고 한다 

     

    컴팩트 생성자 내부에서는 인스턴스 필드에 접근할 수 없다는 특징이 있다

    import java.util.Objects;  
      
    public record Ball(int number, String name) {  
        public Ball {  
            Objects.requireNonNull(name);  
            number = number + 1;  
        }  
    }

     

    이렇게 선언한 컴팩트 생성자는 일반 생성자와 같이 사용하면 된다 

    Ball ball = new Ball(1, "name");

     

     

    toString()

     

    toString()을 사용할때 다른점은 결과 값이 다르다
    기존의 toString은 {} 중괄호를 사용했는데 record에서는 [] 대괄호를 사용한다

    //record
    System.out.println(ball); // Ball[number=2, name=a]
    
    //기존 클래스
    System.out.println(ballReal); // BallReal{number=1, name='a'}

     

     

     

     

    마무리

    • record를 dto에 적용하면 좋아보인다
    • 이득볼 수 있는게 사실 크게 없긴 하지만 getter같은 데이터 클래스를 만들때 사용하는 것들을 자동으로 만들어준다는 부분에서는 좀 편리한 것 같다 
    • 읽어온 정보를 변경하지 않으려면 record 타입의 DTO가 가장 적절한 것 같다
    • record는 한번 값이 정해지면 setter를 통해 값을 변경할 수 없다 -> 자바 내부에서 데이터 가공시 중간에 변질될 우려가 없다는 것이다

     

     

    💡그럼 entity class에도 적용할 수 있지 않을까?

     

    -> 엔티티가 될 수 없다

    이유는

    • Mutability(가변성)의 제한:
      • Record는 불변(immutable)한 데이터를 나타내는데, JPA는 엔티티 클래스가 가변해야 합니다. JPA가 엔티티의 상태를 추적하고 관리하기 위해서는 엔티티 클래스의 속성을 변경할 수 있어야 한다. 하지만 Record는 불변하기 때문에 Record 클래스를 엔티티로 사용할 경우 JPA가 엔티티의 변경을 추적할 수 없다.
    • Persistence Provider의 제한:
      • 현재까지의 JPA 구현체들은 Record를 지원하지 않을 수 있다
      • JPA 구현체는 기존의 Java 클래스를 기반으로 엔티티를 관리하는 방식에 익숙하며, Record와 같은 새로운 언어 기능을 지원하기 위해서는 일부 변경이 필요할 수 있다
      • 따라서 Record를 지원하지 않는 JPA 구현체도 있을 수 있다
    • 매핑 제한
      • 쿼리 결과를 매핑 할때 객체를 인스턴스화 할 수 있도록 default생성자가 필요하다
      • 즉, 매개변수가 없는 생성자가 필요하다는 것이다
      • 하지만 record는 default 생성자를 제공하지 않는다
      • 왜냐하면 record는 불변객체이기 때문에 setter를 사용할 수 없다

     

Designed by Tistory.