Record Class 적용기 근데 TMI를 곁들인

진주원(JooWon Jin)·2024년 12월 4일
0

FrameCheckMate

목록 보기
2/3
post-thumbnail

서론

프로젝트에 대해서 면접관은 정말 다양한 질문을 한다. Spring Project를 했다 하면 질문하는 것 중의 하나는 다음과 같다.

Java는 몇 버전을 사용하셨고 이유가 뭔가요 ?

  • 나는 보통 Spring과의 호환성을 위해 버전 업을 했다고 얘기한다.

정확하게 원하는 답변이 뭔지는 잘 모르겠지만 틀린 말은 아니라고 생각한다. 실제로 많이 사용하는 Spring Boot 3.0부터는 Java 17이 필수다.

이러한 이류로 Java 17을 사용한다면 17의 특징을 살려 프로그래밍을 해보는 것도 좋다 생각한다.

Java 17

Java 17에는 다양한 변화가 적용되었다. 대표적인 키워드로는 다음과 같다.

  • Record class
  • Sealed class
  • Stream.toList() 사용 가능
  • ZGC의 등장

다양한 변경점이 있지만 오늘 내가 다루고 실제로 적용한 주제는 Record Class이다.

Record Class

Java 14에 등장한 불변(immutable)객체를 쉽게 생성할 수 있도록 하는 유형의 클래스

자세한 설명 참고 : https://www.baeldung.com/java-record-keyword

불변 객체 (Immutable Object)

객체 생성 이후 내부 상태가 변하지 않는 객체 → Read Only 메서드만을 제공 (대표적으로 String 클래스 객체들)

  • 불변 객체의 가장 큰 장점은 동기화를 고려하지 않아도 된다는 것이다.

Java 17 이전에도 불변 객체라는 개념은 존재했고 당연히 정의 가능했다. 그렇다면 Java 진영은 왜 17에 Record라는 별도의 클래스까지 도입했을까?

Baeldung에는 다음과 같이 기술되어 있다.

  1. There’s a lot of boilerplate code
  2. We obscure the purpose of our class: to represent a person with a name and address
  • 요약하자면 ‘단순히 필드만 정의한 불변 객체에 쓸데 없는 코드가 너무 많다’ 이런 거다.

그냥 만들어보자 : Before Record

public class Person {

    private final String name;
    private final String address;

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

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

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } else if (!(obj instanceof Person)) {
            return false;
        } else {
            Person other = (Person) obj;
            return Objects.equals(name, other.name)
              && Objects.equals(address, other.address);
        }
    }
    
    // standard getters
}
  • private final로 캡슐화된 필드, getter, 전체 생성자, (equals & hashcode)형제들은 Java에서 Immutable Data Class를 기술하는데 사용되는 코드이다.
  • 모든 클래스를 기술할 때 위의 내용을 다 포함해야 한다. 가독성도 떨어지고 코드의 길이가 너무 길다.

우리에게는 Lombok이 있다 !

  • 맞는 말이다. 하지만 Lombok에는 한 가지 문제점이 있다. 바로 Lombok 라이브러리가 필요하다는 것이다. (Lombok 안쓰는 프로젝트가 어디 있어? 라고 묻는다면 사실 나도 잘 모르겠다)
  • 그렇다면 사용하는 관점을 바라볼 필요가 있다. Lombok은 불변 객체를 생성 즉 정의하기 위해 사용하는 것이 아닌 복잡한 데이터 모델 정의 시 사용한다.

Record 적용 시 : After Record

public record Person (String name, String address) {}
  • 정말 단순하다. Record Class를 선언하고 필드를 추가한 것만으로 다음과 같은 내용이 추가된다.
    • equals, hashCode, toString 메서드
    • private 및 final 필드
    • public 생성자
  • 어떻게 가능한거지 ?
    • 컴파일러 !

컴파일러는 레코드의 목적(단순히 데이터를 담는 불변 객체)을 이해하고, 이에 필요한 메서드와 필드를 자동으로 생성

Record와 DTO

위에서는 Java 17에서 사용되는 Record Class에 대해 알아보았다. 그렇다면 어디에 쓸까? 다양한 활용법이 있지만 내가 적용한 것은 DTO이다.

DTO(Data Transfer Object)

계층간 데이터 교환을 위해 사용되는 객체로서 불변성을 보장한다.

  • 자세한 설명은 주제를 벗어나는 거 같으니 생략하고 ‘불변성’에 집중해보겠다.
    • DTO는 Record Class가 적용되기 굉장히 적합하다.

기존 DTO

@Getter
@NoArgsConstructor
public class AssignCardWorkRequest {

    private UUID workerId;

    private String workerEmail;

    @JsonFormat(shape = JsonFormat.Shape.STRING)
    private LocalDateTime startDate;

    @JsonFormat(shape = JsonFormat.Shape.STRING)
    private LocalDateTime endDate;

    private String description;
}

Record Class 변경

import com.fasterxml.jackson.annotation.JsonFormat;

import java.time.LocalDateTime;
import java.util.UUID;

public record AssignCardWorkRequest(
    UUID workerId,
    String workerEmail,
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
    LocalDateTime startDate,
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
    LocalDateTime endDate,
    String description
) {}
profile
Young , Wild , Free

0개의 댓글