Spring/[인프런 김영한 실전 스프링 부트와 JPA 활용 1]

[인프런 김영한 실전 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발] 도메인 분석 설계

h2boom 2024. 8. 29. 22:44

도메인 분석 설계

  • 회원이 상품을 주문하는 기능이 있는 예제

  • 예제 분석
    • 연관관계 설정 시 되도록 다대다 연관관계를 사용하지 말고 일대다 - 다대일 관계로 풀어서 사용해라!
      • 다대다 관계를 사용하면 필드를 추가하지 못한다.
      • 예제에서는 다양한 연관관계를 사용해보기 위해서 다대다 연관관계를 사용
    • 실무 단계에서는 회원이 주문을 참조하지 않고 주문이 회원을 참조하는 것으로 충분하다.
      • 회원이 주문을 하기 때문에 주문 리스트가 필요해 보이지만 객체 관점에서는 다르다.
        예제에서는 연관관계를 위해서 추가
    • DB 테이블에서는 다대다(N:M) 관계가 없기 때문에 카테고리와 상품의 연관관계에서 매핑 테이블이 별도로 필요하다.
    • 연관관계의 주인은 외래 키가 있는 쪽을 택하는 것이 좋다.
      • 연관관계 주인 = 두 객체 중 제어 권한(데이터 저장, 수정, 삭제)를 갖는 주체
      • 테이블에서는 1:N 관계중 N쪽이 외래 키를 가지고 있기에 연관관계의 주인으로 선택하는 것이 좋다.
      • 연관관계 주인 쪽 값을 변경해야 적용이 된다. (반대쪽은 읽기 전용으로 조회용으로 사용)
        • 연관관계 주인 쪽에서 @JoinColumn으로 외래 키 지정
        • 주인 반대 쪽에서 mappedBy 속성으로 주인의 필드명을 지정해준다.
    • Address 클래스는 임베디드 타입으로 사용된다.
      • 임베디드 타입 : 새로운 값을 직접 정의해서 사용하는 값 타입으로 유사한 속성들을 묶어 별도의 클래스로 만드는 것이다.
      • JPA가 객체를 생성할 때 리플렉션이나 프록시 기능을 사용하기 위해서 값 타입은 불변으로 설계하며 기본 생성자를 필요로 한다.
        • 기본 생성자는 public 대신 protected로 안전하게 선언하는 것이 좋다.
        • 불변으로 설계하기 위해 Setter는 없애고 생성자로만 값을 초기화할 수 있도록 설계하는 것이 좋다.
      • 일반적으로 임베디드 타입으로 사용되는 클래스에 @Embedable 어노테이션을 선언하던 객체를 사용하는 곳에서 @Embeded로 선언하던 하나만 사용해도 된다.

 

  • 데이터베이스 테이블, 컬럼명은 대문자 + _ (언더스코어)나 소문자 + _ (언더스코어)를 주로 사용한다.

 

  • 주의 사항
    • 실무에서는 엔티티 클래스에 가급적 Getter는 열어두고 Setter는 꼭 필요한 경우에만 사용하는 것이 좋다.
      • 엔티티의 데이터는 조회할 일이 많기 때문에 Getter는 모두 열어두되 Setter를 호출하면 데이터가 변하므로 변경을 추적하기 어려워지므로 명확하게 별도의 비지니스 메소드를 제공하는 것이 좋다.
    • 상속 관계 사용 시 부모 클래스에 @Inheritance로 전략을 지정해줘야한다.
      • 조인, 단일 테이블, 구현 클래스마다 테이블 전략 중 지정
    • Enum 타입 사용 시 @Enumerated(EnumType.STRING) 필수로 지정해줘야 한다.
    • 일대일 관계에서 연관관계 주인은 어디 쪽이든 상관없지만 장단점이 존재한다.
    • JPA의 ddl 스크립트를 그대로 사용해서는 안된다.
      • 보고 검증하고 참고하는 용도로 수정해서 사용할 것.
    • 모든 연관관계는 지연 로딩(LAZY)으로 설정해라!
      • @OneToOne, @ManyToOne 연관관계는 기본이 즉시 로딩이기에  fetch = FetchType.LAZY 속성으로 직접 설정해야한다.
      • 연관된 엔티티를 함께 DB에서 조회하려면 fetch join / 엔티티 그래프 기능을 사용
    • 컬렉션은 필드에서 초기화해라!
      • List와 같이 컬렉션을 사용할 때는 필드에서 초기화하는 것이 안전하다.
      • null 문제에서도 안전하다.
    • 스프링 부트 사용 시 엔티티 -> 테이블 명 설정 방식
      • 카멜 케이스 -> 언더스코어
      • .(점) -> _(언더스코어)
      • 대문자 -> 소문자
    • cascade(영속성 전이) 옵션 사용 시 해당 엔티티와 연관관계에 있는 엔티티도 함께 영속 상태로 만들 수 있다.
      • 기존에는 Member - Team 연관관계가 있을 때 em.persist(memeber), em.persist(team) 각각 해줌으로 영속화 할 수 있었지만 cascade 옵션을 지정해두면 em.persist(member)만 하더라도 team까지 영속화할 수 있다.

 

public class Order {
	
    ...
    
    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "member_id")
    private Member member;

    //연관관계 편의 메소드
    public void setMember(Member member) {
        this.member = member;
        member.getOrders().add(this);
    }
}
  • 연관관계 편의 메소드 예시
  • 양방향 연관관계의 경우 두 객체를 모두 신경써야 한다.
    • 한쪽에서 set 메소드로 양방향 연관관계를 모두 설정하게 해주는 것이 간편하고 안전하다.
    • 연관관계 주인과 상관없이 set 메소드는 빈번하게 사용하는 쪽에 정의해서 사용해주는 것이 좋다.

 

@ManyToMany
@JoinTable(name = "category_item",
	joinColumns = @JoinColumn(name = "category_id"),
    inverseJoinColumns = @JoinColumn(name = "item_id")
)
private List<Item> items = new ArrayList<>();
  • 다대다 관계에서는 매핑 테이블 지정을 위한 @JoinTable 선언이 필요하다.
    • 매핑 테이블에 들어갈 외래 키를 지정하는 것도 필요하다. (joinColums, inverseJoinColumns 속성 사용)

 

public class Category {
	
	...

	//계층 구조
	@ManyToOne
	@JoinColumn(name = "parent_id")
	private Category parent;

	@OneToMany(mappedBy = "parent")
	private List<Category> child = new ArrayList<>();
}
  • 계층 구조를 구현한 예제로 다른 엔티티를 매핑하는 방식과 동일하다.

애플리케이션 아키텍처

  • 계층형 구조
    • Controller, Web : 웹 계층
    • Service : 비지니스 로직, 트랜잭션 처리
    • Repository : JPA를 직접 사용하는 계층, 엔티티 매니저 사용
    • Domain : 엔티티가 모여있는 계층, 모든 계층에서 사용


출처: [인프런 김영한 실전 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발]

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-%ED%99%9C%EC%9A%A9-1

 

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 강의 | 김영한 - 인프런

김영한 | 실무에 가까운 예제로, 스프링 부트와 JPA를 활용해서 웹 애플리케이션을 설계하고 개발합니다. 이 과정을 통해 스프링 부트와 JPA를 실무에서 어떻게 활용해야 하는지 이해할 수 있습니

www.inflearn.com