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까지 영속화할 수 있다.
- 실무에서는 엔티티 클래스에 가급적 Getter는 열어두고 Setter는 꼭 필요한 경우에만 사용하는 것이 좋다.
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 - 웹 애플리케이션 개발]
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 강의 | 김영한 - 인프런
김영한 | 실무에 가까운 예제로, 스프링 부트와 JPA를 활용해서 웹 애플리케이션을 설계하고 개발합니다. 이 과정을 통해 스프링 부트와 JPA를 실무에서 어떻게 활용해야 하는지 이해할 수 있습니
www.inflearn.com