빙응의 공부 블로그

[Spring]실전! 스프링 부트와 JPA 활용1 - 설계의 주의점 본문

Spring/인프런_개념

[Spring]실전! 스프링 부트와 JPA 활용1 - 설계의 주의점

빙응이 2024. 3. 11. 17:01

📝요구사항 분석 

필요한 기능을 분석하는 단계이다. 

  • 회원
    • 회원은 이름, 주소를 통해 등록할 수 있다. 
    • 회원은 자신의 이름, 주소, 주문 내역을 조회할 수 있다. 
    • 회원은 여러개를 주문할 수 있다. 
  • 상품
    • 상품은 이름, 가격, 재고수량을 가지고 있다.
    • 상품을 주문하면 재고수량이 줄어든다.
    • 상품의 종류로는 도서, 음반, 영화가 있으며 각각 사용하는 속성이 다르다. 
  • 주문
    • 주문은 상품을 주문한 회원과 배송 정보, 주문 날짜, 주문 상태를 가지고 있다.
    • 주문 상태는 주문, 취소로 표현할 수 있다.
    • 주문은 여러 개의 주문 아이템을 가지고 있다. 
  • 주문 상품
    • 주문 상품은 주문한 상품 정보와 주문금액, 주문 수량 정보를 가지고 있다.
    • 주문 상품은 상품에서 상품 정보를 조회한다. 
  • 배송
    • 주문 시 하나의 배송 정보를 가지고 있다.
  • 카테고리
    • 상품의 카테고리를 구분하기 위해 사용된다.
  • 주소
    • 값 타입으로 회원과 배송에 사용된다.

 

📝연관관계 매핑 분석

요구사항 관계
회원은 여러개를 주문할 수 있다.  회원 : 주문 (일대다) 
주문은 여러 개의 주문 상품을 가지고 있다.  주문 : 주문 상품(일대다)
주문 상품상품에서 상품 정보를 조회한다. 주문상품 : 상품(다대일)
주문 시 하나의 배송 정보를 가지고 있다 주문 : 배송(일대일)
상품카테고리를 구분하기 위해 사용된다. 상품 : 카테고리(다대다)

 

실습 주의점!

일반적으로 상품, 카테고리는 다대다 관계를 사용하지 않으며 그저 예제 공부를 위해 다대다로 할 것이다.

또한 주문상품, 상품 관계를 제외하고 모두 양방향으로 하여 연습을 할 것이다.!

 

참고로 외래 키가 잇는 곳을 연관관계 주인으로 지정할 것이다.

 

 

📝엔티티 개발 주의점!

🚩@Getter와 @Setter 허용 범위

 

실무에서는 가급적으로 @Getter를 열어두고 @Setter는 필요한 경우만 사용한다.

참고: 이론적으로 Getter, Setter 모두 제공하지 않고, 꼭 필요한 별도의 메서드를 제공하는게 가장 이상적이다. 하지만 실무에서 엔티티의 데이터는 조회할 일이 너무 많으므로, Getter의 경우 모두 열어두는 것이 편리하다. Getter는 아무리 호출해도 호출 하는 것 만으로 어떤 일이 발생하지는 않는다. 하지만 Setter는 문제가 다르다. Setter를 호출하면 데이터가 변한다. Setter를 막 열어두면 가까운 미래에 엔티티가 도대체 왜 변경되는지 추적 하기 점점 힘들어진다. 그래서 엔티티를 변경할 때는 Setter 대신에 변경 지점이 명확하도록 변경을 위한 비즈니스 메서드를 별도로 제공해야 한다.

 

 

🚩값 타입은 변경에 닫혀있게 설계한다.

@Embeddable
@Getter
public class Address {
  private String city;
  private String street;
  private String zipcode;

  public Address(String city, String street, String zipcode) {
    this.city = city;
    this.street = street;
    this.zipcode = zipcode;
  }
  protected Address(){};
}

@Setter를 제거하고, 생성자에서 값을 모두 초기화해서 불변 클래스를 만들어야 한다.

또한 임베디드 타입은 자바 기본 생성자를 public 또는 protected로 설정해야한다.

 

🚩모든 연관관계는 지연로딩으로 설정한다.

  • 즉시로딩은 예측이 어렵고, 어떤 SQL이 실행될지 추적이 어렵다. 특히 N+1 문제가 자주 발생
  • 실무에서 모든 연관관계는 지연로딩으로 설정 
  • 연관관계와 함께 DB를 조회해야 하면, 페치 조인 활용 
  • @XToOne은 기본이 즉시 로딩이므로 직접 지연로딩을 설정해야한다. 

🚩컬렉션은 필드에서 초기화한다.

컬렉션은 필드에서 바로 초기화 하는 것이 안전하다.

  • null 문제에서 안전해진다.
  • 하이버네이트는 엔티티를 영속화할 때, 컬랙션을 감싸서 하이버네이트의 내장 컬렉션으로 변경한다.
    • 그렇기에 문제가 발생할 수 있어 그냥 엔티티 필드에서 초기화하자!
  @OneToMany(mappedBy = "parent")
  private List<Category> child = new ArrayList<>();

🚩연관삭제 CASCADE 전략

  @OneToMany(mappedBy = "order",cascade = CascadeType.ALL)
  private List<OrderItem> orderItems = new ArrayList<>();

  @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  @JoinColumn(name = "delivery_id")
  private Delivery delivery;

기본적인 값 세팅 후 DB에 저장하면 연관관계에 의해 다른 테이블에 자동 저장이 가능하다.

CascadeType.All은 이러한 활동의 저장, 삭제를 모두 한다는 것을 의미한다.

 

🚩연관관계 편의 메서드 

연관관계 상에서 메서드 관리를 편하게 하기 위한 메서드이다. 

양방향 관계에서 사용한다.

그 이유는 JPA적으로는 연관관계에서 연관관계 주인만 설정하면 활동에 문제 없지만

객체적 입장에서는 둘다 관계를 해줘야 하기 때문에 사용한다.