이번 프로젝트를 진행하면서 JPA Entity 클래스의 설계를 더욱 견고하게 하기 위해 @NoArgsConstructor(access = AccessLevel.PROTECTED) 기능을 활용하게 되었습니다. 이 접근 방식을 통해 객체의 상태를 관리하고, 명확한 설계 의도를 표현하는 방법을 최적화할 수 있었습니다. 특히, JPA를 사용하는 Spring 애플리케이션에서 엔티티 클래스의 기본 생성자 접근을 제어하는 것이 얼마나 중요한지 알게 되었습니다.
1. JPA와 기본 생성자
JPA는 엔티티 클래스를 인스턴스화할 때 기본 생성자를 사용합니다. 따라서, 모든 엔티티 클래스는 기본 생성자를 제공해야 합니다. 기본 생성자가 없으면 JPA는 엔티티 클래스를 인스턴스화할 수 없고, 이는 곧 런타임 예외로 이어집니다.
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long profileId;
@Column(nullable = false, unique = true)
private String userId;
@Column(nullable = false)
private String nickname;
@Column(nullable = false)
private String introduce;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "account_id", nullable = false, columnDefinition = "bigint")
private Account account;
}
위 코드는 JPA가 요구하는 기본 생성자를 제공하지만, 기본 생성자가 공개(public)되어 있어 외부에서 자유롭게 인스턴스화할 수 있습니다. 이는 객체의 불완전한 상태를 초래할 수 있습니다.
2. @NoArgsConstructor(access = AccessLevel.PROTECTED)의 도입
기본 생성자의 접근 제어를 protected로 설정하면 외부에서 직접적으로 객체를 생성할 수 없도록 제한할 수 있습니다. 대신, JPA와 같은 프레임워크에서는 여전히 기본 생성자를 사용할 수 있습니다.
@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long profileId;
@Column(nullable = false, unique = true)
private String userId;
@Column(nullable = false)
private String nickname;
@Column(nullable = false)
private String introduce;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "account_id", nullable = false, columnDefinition = "bigint")
private Account account;
}
3. 장점
- 객체 불변성 강화: 외부에서 기본 생성자를 사용할 수 없으므로 객체 생성 시 필드가 불완전하게 초기화될 가능성을 줄입니다.
- 명확한 설계 의도 표현: 기본 생성자가 protected로 설정되어 클래스 설계 의도를 명확히 합니다. 이는 클래스의 인스턴스화가 제한됨을 의미합니다.
- JPA와의 호환성: 기본 생성자가 protected로 설정되어도 JPA는 여전히 이를 사용하여 엔티티를 인스턴스화할 수 있습니다.
4. 단점 및 고려 사항
- 테스트 코드 작성 시 번거로움: 기본 생성자를 사용할 수 없으므로 빌더 패턴이나 다른 생성 방법을 사용해야 합니다.
- 외부에서 객체 생성 제약: 외부에서 객체를 생성할 필요가 있는 경우, 추가적인 접근 방법(예: 공개 생성자, 정적 팩토리 메서드 등)을 제공해야 합니다.
5. 빌더 패턴 활용
다음은 JoinRequest를 통해 사용자가 회원 가입을 요청할 때, Profile 객체를 빌더 패턴을 통해 생성하는 예시입니다. 빌더 패턴을 사용하면 객체 생성 시 필요한 필드를 명확히 하고, 객체의 불완전한 상태를 방지할 수 있습니다.
Profile profile = Profile.builder()
.userId(joinRequest.profile().userId())
.nickname(joinRequest.profile().nickname())
.introduce("소개 글") // 기본 소개 글 설정
.account(account)
.build();
6. 결론
@NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용하면 JPA와 호환되면서 외부 접근을 제한할 수 있습니다. 이는 클래스의 인스턴스화를 제한하여 객체 상태를 더 잘 관리하고, 설계 의도를 명확히 표현할 수 있는 장점이 있습니다. 따라서, JPA 엔티티 클래스에서 객체의 불변성을 강화하고자 할 때 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 고려해보는 것이 좋습니다.
이번 프로젝트에서 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 통해 JPA 엔티티 클래스의 설계를 개선하고, 객체의 상태를 더 잘 관리할 수 있었습니다. 이를 통해 설계 의도를 명확히 표현하고, 엔티티 클래스를 더욱 견고하게 만들 수 있었습니다.
'Spring > Spring Boot' 카테고리의 다른 글
[Spring Boot] JPA @MapsId를 이용한 FK를 PK로 사용하기 (0) | 2024.07.23 |
---|---|
[Spring Boot] Flyway를 이용한 DB 형상관리 (0) | 2024.06.26 |
[Spring Boot]스프링 부트에서 ddl-auto 사용 방법 (0) | 2024.06.03 |
[Spring Boot] Lombok 어노테이션 정리 (0) | 2024.05.23 |
[Spring] Path Variable vs Query Parameter (0) | 2024.03.29 |