DevBoi

[Java] Record 패턴 본문

Language/[Java]

[Java] Record 패턴

HiSmith 2024. 4. 28. 00:06
반응형

불변 DTO의 성격인 Record와 DTO간의 매핑을 코드를 자동생성해주는 라이브러리 Mapstruct에 대한 정리를 해보자

 

1. Record

Record는 불변성을 보장하는 타입이다. 따라서 신뢰성이 높아

그래서 주로 Request,Response,Configuration에 대한 정보를 담아서 사용한다.

 

아래와 같은 레코드의 소스가 있다고 가정하자

public record MemberRequest(
  @Schema(description = "닉네임", example = "스미스") String nickname,
  @Schema(description = "프로필 이미지 링크", example = "이미지 주소") String imgUrl,
  @Schema(description = "이름", example = "김테스트") String name
  ){

  }

 

이는, 결국 아래의 클래스 소스와 동일하다.

package com.boiler.core.backend.api.member.request;


import io.swagger.v3.oas.annotations.media.Schema;

import java.util.Objects;

public final class MemberRequest {
  @Schema(description = "닉네임", example = "스미스")
  private final String nickname;
  @Schema(description = "프로필 이미지 링크", example = "이미지 주소")
  private final String imgUrl;
  @Schema(description = "이름", example = "김테스트")
  private final String name;

  public MemberRequest2(
    @Schema(description = "닉네임", example = "스미스") String nickname,
    @Schema(description = "프로필 이미지 링크", example = "이미지 주소") String imgUrl,
    @Schema(description = "이름", example = "김테스트") String name
  ) {
    this.nickname = nickname;
    this.imgUrl = imgUrl;
    this.name = name;
  }

  @Schema(description = "닉네임", example = "스미스")
  public String nickname() {
    return nickname;
  }

  @Schema(description = "프로필 이미지 링크", example = "이미지 주소")
  public String imgUrl() {
    return imgUrl;
  }

  @Schema(description = "이름", example = "김테스트")
  public String name() {
    return name;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == this) return true;
    if (obj == null || obj.getClass() != this.getClass()) return false;
    var that = (MemberRequest2) obj;
    return Objects.equals(this.nickname, that.nickname) &&
      Objects.equals(this.imgUrl, that.imgUrl) &&
      Objects.equals(this.name, that.name);
  }

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

  @Override
  public String toString() {
    return "MemberRequest2[" +
      "nickname=" + nickname + ", " +
      "imgUrl=" + imgUrl + ", " +
      "name=" + name + ']';
  }


}

 

눈 여겨 봐야하는 부분은 아래의 4가지이다.

1. equals함수에서 자동으로 객체의 값으로 비교하여, 검증하는 함수를 제공

2. toString 자동 생성

3. getter,setter가 아닌, 프로퍼티 직접 호출 및 setter 없음

4. final 선언

 

결국은 클래스파일로 작성할 수 있지만, 편의성을 위해 레코드 타입으로 작성하는 것이다.

 

Nest한 구조의 Record타입 객체를 쓰면 아래와 같다.

public record MemberTestDto(
  @NotNull String nickname,
  @NotBlank @Size(min = 5) int imgCount,
  @NotBlank MemberAddress name
) {
  public record MemberAddress(
    String addressName,
    String addressNo
  ) {

  }

}

 

이를 클래스 타입으로 변환하는것도 아래와 같다.

package com.boiler.core.backend.api.member.request;


import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import org.jetbrains.annotations.NotNull;

import java.util.Objects;

public final class MemberTestDto {
  @NotNull
  private final String nickname;
  private final @NotBlank @Size(min = 5) int imgCount;
  private final @NotBlank MemberAddress name;

  public MemberTestDto(
    @NotNull String nickname,
    @NotBlank @Size(min = 5) int imgCount,
    @NotBlank MemberAddress name
  ) {
    this.nickname = nickname;
    this.imgCount = imgCount;
    this.name = name;
  }

  @NotNull
  public String nickname() {
    return nickname;
  }

  public @NotBlank @Size(min = 5) int imgCount() {
    return imgCount;
  }

  public @NotBlank MemberAddress name() {
    return name;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == this) return true;
    if (obj == null || obj.getClass() != this.getClass()) return false;
    var that = (MemberTestDto) obj;
    return Objects.equals(this.nickname, that.nickname) &&
      this.imgCount == that.imgCount &&
      Objects.equals(this.name, that.name);
  }

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

  @Override
  public String toString() {
    return "MemberTestDto[" +
      "nickname=" + nickname + ", " +
      "imgCount=" + imgCount + ", " +
      "name=" + name + ']';
  }

  public static final class MemberAddress {
    private final String addressName;
    private final String addressNo;

    public MemberAddress(
      String addressName,
      String addressNo
    ) {
      this.addressName = addressName;
      this.addressNo = addressNo;
    }

    public String addressName() {
      return addressName;
    }

    public String addressNo() {
      return addressNo;
    }

    @Override
    public boolean equals(Object obj) {
      if (obj == this) return true;
      if (obj == null || obj.getClass() != this.getClass()) return false;
      var that = (MemberAddress) obj;
      return Objects.equals(this.addressName, that.addressName) &&
        Objects.equals(this.addressNo, that.addressNo);
    }

    @Override
    public int hashCode() {
      return Objects.hash(addressName, addressNo);
    }

    @Override
    public String toString() {
      return "MemberAddress[" +
        "addressName=" + addressName + ", " +
        "addressNo=" + addressNo + ']';
    }
  
  
    }

}
반응형

'Language > [Java]' 카테고리의 다른 글

[Java] Generic !  (0) 2023.07.15
[Java] mapMulti 사용  (0) 2023.07.07
[Java] static block 및 instance block  (0) 2023.06.26
[Java] 제네릭을 조금 잘 써보면 어떨까  (0) 2023.06.18
[Java] Mapstruct Spring 적용하기  (0) 2023.06.14