개발은 재밌어야 한다
article thumbnail
반응형

Java record: 불변 객체의 간결한 정의와 활용




Java 14부터 도입된 record는 데이터를 간단하게 표현하고 불변성을 쉽게 유지할 수 있는 강력한 도구입니다. record는 데이터 클래스를 간결하게 작성하는 데 유용하며, 데이터를 안전하게 다루면서도 코드의 가독성을 높여 줍니다. 이 글에서는 Java record가 무엇인지, 어떻게 사용하는지, 그리고 JavaScript의 Object.freeze()와의 간단한 비교를 통해 불변 객체를 다루는 두 가지 방법을 알아보겠습니다.

Java record란?


Java의 record는 데이터를 저장하기 위한 간단한 클래스입니다. 기존의 자바 클래스를 만들 때는 getter, toString(), equals(), hashCode() 메서드를 수동으로 작성해야 했습니다. 그러나 record를 사용하면 이러한 메서드들이 자동으로 생성되며, 모든 필드는 기본적으로 불변입니다.

기본 예시:

public record Person(String name, int age) {}



위 코드에서 Person이라는 레코드는 name과 age라는 두 필드를 가지고 있습니다. record의 가장 큰 장점은 getter와 같은 메서드들을 자동으로 생성해준다는 점입니다. 예를 들어, 다음과 같이 사용할 수 있습니다.

public class Main {
    public static void main(String[] args) {
        Person person = new Person("Alice", 30);
        System.out.println(person.name());  // Alice 출력
        System.out.println(person.age());   // 30 출력
    }
}



record는 불변성을 유지하므로 객체의 필드를 변경할 수 없습니다. 즉, person.name이나 person.age를 수정하려고 하면 컴파일 오류가 발생합니다.

record의 내부 동작


record는 필드에 대한 자동 생성자와 getter 메서드를 제공합니다. 위에서 선언한 Person 클래스는 다음과 같이 확장된 클래스와 동일한 효과를 냅니다.

public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String name() {
        return name;
    }

    public int age() {
        return age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Person[name=" + name + ", age=" + age + "]";
    }
}



기존에 수동으로 작성해야 했던 코드가 record를 사용하면 단 몇 줄로 대체됩니다. 이는 코드의 가독성을 높이고 유지보수를 쉽게 만들어 줍니다.

record vs. Lombok


record와 Lombok은 모두 코드를 간결하게 만들어주는 기능을 제공하지만, 둘 사이에는 몇 가지 중요한 차이점이 있습니다.

불변성

  • record: record는 기본적으로 불변입니다. 모든 필드는 final로 선언되며, 한 번 값이 설정되면 변경할 수 없습니다.
  • Lombok: Lombok은 선택적으로 불변성을 제공할 수 있습니다. @Data 어노테이션을 사용하면 게터와 세터가 모두 자동 생성되며, 객체가 가변일 수 있습니다. 불변성을 원하면 @Value를 사용해야 합니다.


@Data // 가변 객체
public class Person {
    private String name;
    private int age;
}

@Value // 불변 객체
public class Person {
    private String name;
    private int age;
}

자동 생성 메서드


• record: record는 자동으로 getter, toString(), equals(), hashCode()를 제공합니다. 게터 메서드는 필드명과 같은 이름을 갖습니다.
• Lombok: Lombok에서는 @Getter, @Setter, @ToString, @EqualsAndHashCode 등의 어노테이션을 통해 개별적으로 메서드를 자동 생성합니다.

@Getter
@Setter
@ToString
@EqualsAndHashCode
public class Person {
    private String name;
    private int age;
}

사용 방식

  • record: record는 Java의 언어 차원에서 지원되는 기능입니다. 별도의 라이브러리나 어노테이션 없이 사용할 수 있으며, 문법적으로 간결합니다. 단 자바 버전이 14이상 이여야합니다!!
  • Lombok: Lombok은 외부 라이브러리입니다. Lombok을 사용하려면 프로젝트에 의존성을 추가하고, 어노테이션을 붙여야 합니다.


유연성

  • record: record는 필드가 불변이고, 자동으로 생성되는 메서드들이 고정되어 있어 구조적으로 제한적입니다. 추가적으로 메서드를 정의할 수는 있지만, 세세한 조정은 어렵습니다.
  • Lombok: Lombok은 다양한 어노테이션을 제공하며, 객체의 상태나 메서드에 대해 더 많은 유연성을 제공합니다. 가변 객체와 불변 객체 모두 생성할 수 있고, 특정 메서드만 선택적으로 생성할 수 있습니다.

결론적으로, record는 불변 객체를 간결하게 정의할 때 적합하고, Lombok은 유연성이 필요하거나 객체의 상태를 수정할 필요가 있을 때 유용합니다.

JavaScript의 Object.freeze()와의 간단한 비교


Java에서 record를 사용해 불변 객체를 만드는 방식과 비슷한 예시로 JavaScript의 Object.freeze()를 들 수 있습니다. Object.freeze()는 객체를 동결하여, 그 객체의 프로퍼티가 변경되지 않도록 합니다. 예시로 두 개념을 비교해 보면

JavaScript Object.freeze() 예시:

const person = Object.freeze({ name: "Alice", age: 30 });

console.log(person.name);  // Alice 출력
console.log(person.age);   // 30 출력

// person.name = "Bob";  // 값 변경 불가능, 오류는 발생하지 않지만 수정되지 않음.
console.log(person.name);  // 여전히 Alice 출력



JavaScript에서는 객체의 불변성을 명시적으로 설정해야 합니다. Object.freeze()는 객체 자체를 동결하지만, 내부의 중첩된 객체까지 불변으로 만들지는 않습니다. Java의 record는 이런 면에서 더 강력한 불변성을 제공합니다.

불변 객체가 왜 중요한가?


불변 객체는 다양한 면에서 유용합니다. 특히, 데이터를 변경할 수 없는 특성은 동시성 문제를 방지하고, 데이터를 안전하게 유지하는 데 큰 역할을 합니다. 불변 객체를 사용하면 코드의 예측 가능성이 높아지고, 의도치 않은 데이터 변경을 방지할 수 있습니다.

불변 객체의 장점:

  • 동시성 문제 해결: 여러 스레드에서 객체를 안전하게 공유할 수 있음.
  • 데이터 무결성 보장: 의도치 않은 상태 변경을 방지
  • 디버깅과 유지보수 용이: 객체 상태가 변하지 않으므로 예측 가능한 동작을 보장.


record 활용 예시


record는 간단한 데이터 클래스 외에도 다양한 곳에서 사용할 수 있습니다. 예를 들어, 데이터 전송 객체(DTO)나 데이터 모델을 정의할 때 record는 매우 유용합니다. 다음은 데이터 모델을 record로 정의한 예시입니다.

public record Book(String title, String author, double price) {}

public class Main {
    public static void main(String[] args) {
        Book book = new Book("Effective Java", "Joshua Bloch", 45.99);
        System.out.println(book);  // Book[title=Effective Java, author=Joshua Bloch, price=45.99] 출력
    }
}



여기서 Book 객체는 불변이며, 이를 통해 데이터 전송 중 데이터가 의도치 않게 수정되는 것을 방지할 수 있습니다.


정리


Java의 record는 불변 객체를 간결하고 쉽게 만들 수 있는 강력한 도구입니다. 기존에 반복적으로 작성해야 했던 코드들을 자동으로 처리해주고, 데이터 클래스에서 흔히 요구되는 기능들을 기본적으로 제공합니다. JavaScript의 Object.freeze()와 마찬가지로 객체의 불변성을 보장하지만, record는 더 강력한 타입 안정성과 불변성을 지원합니다.

불변 객체를 적절히 활용하면 프로그램의 안전성은 물론 가독성과 유지보수성을 높일 수 있습니다. record는 데이터 중심 애플리케이션에서 특히 유용하며, 앞으로 다양한 상황에서 활용될 수 있는 중요한 개념입니다.

반응형

'JAVA' 카테고리의 다른 글

Java 접근 제어자 이해하기  (0) 2024.10.22
Java Stream API 사용법  (0) 2024.10.21
Java Enum 사용법과 활용 예시  (10) 2024.10.18
profile

개발은 재밌어야 한다

@ghyeong

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!