# 2023-10-09 - [x] MapStruct 사용해보기(ModelMapper와 차이 파악) - [x] 연관관계 설정 연관관계 설정 팀과 회원 - 1:N의 관계 - 객체의 경우 - 팀 -> 회원의 연관관계 1개 - 팀 <- 회원의 연관관계 1개 - 테이블의 경우 - 회원 테이블에 팀 PK를 가짐으로써 양방향 연관관계가 생긴다. - 팀 <- -> 회원 연관관계 - 객체의 일대다, 다대일 관계에서 연관 관계의 주인은 외래키를 관리하는 곳이 주인이 된다. - 테이블 기준으로 생각해보면 회원 row마다 팀의 pk를 가질 순 있어도 하나의 팀 row에서 팀에 속한 여러 회원의 pk를 가질 순 없다. - 연관관계의 주인은 외래키를 가지게 되는 N쪽이라고 생각할 수 있고 여기서는 회원쪽이 연관관계의 주인이 된다. oneToMany를 써야 하는 경우는 정말 많지만 JPA 동작 원리 상 oneToMany만을 사용하는 것은 권장하지 않는다. - oneToMany는 연관관계의 주인이 되는 외래키가 다른 테이블에 있다. 이런 구조 때문에 연관관계 데이터가 변경되는 과정에서 불필요한 update가 실행된다. oneToMany의 성능 이슈를 해결하고자 한다면 ManyToOne과 같이 쓴다. - 관련하여 mappedBy와 JoinColumn 등을 정확히 선언해서 사용 - Service 메서드에서는 Entity를 그대로 리턴하지 않고 Entity를 적절히 변환한 객체 (info와 같은) 를 리턴하도록 한다. ---- MapStruct 사용 - 복잡한 애플리케이션을 여러 개의 계층으로 나누어 개발하는 건 각 계층의 관심 측면만을 전문적으로 다룬다는 점에서 의의가 있다. - 이 때 각 Layer마다 사용하고 전달되는 객체는 적절한 컨버팅 로직을 통해서 격리되고 변환되어야 한다. - 특정 Layer에서 사용되는 객체가 다른 Layer로 전달되는 과정에서 적절한 컨버팅이 없다면 Layer간의 의존 관계가 깨질 수도 있다. ---> Q. 순환 참조 문제로 의존관계가 깨질 수도 있다는 걸 말하는 걸까? - 경우에 따라서는 특정 기술 사용에 따른 예상치 못한 예외가 발생할 수도 있다. - Spring JPA 기반의 lazy loading 관련한 예외 등등) - Layer 간의 객체 변환 로직은 개발자가 일일이 직접 구현해도 되지만 - 반복, 불필요한 코드가 많아짐 - 단순한 실수 -> 개발 생산성 떨어짐 -> 이를 해결하기 위해 매핑 라이브러리에는 ModelMapper, MapStruct 라이브러리가 있다. 동작방식 차이 (ModelMapper, MapStruct) - ModelMapper는 리플렉션 기반, 실제 매핑 로직 파악이 어려움 - MapStruct는 코드 생성 방식으로 동작, 생성된 코드를 통해 매핑 로직을 쉽게 파악할 수 있다. - MapStruct는 컴파일 타임에 매핑 오류를 인지하고 설정에 따라 빌드 시 에러 던짐 - 성능 측면에서 MapStruct 가 훨씬 좋다고 알려져 있다. -> 컴파일 시 오류 확인 --- ModelMapper와 같이 리플렉션을 통해 구현된 기능들이 느린 이유 정의 - 런타임에 결정된 클래스 타입을 컴파일 때 결정한 것처럼 행동할 수 있게 하는 API 단점 - 컴파일러 최적화가 불가능하다. - 왜? 리플렉션은 런타임 때 동적으로 클래스를 생성하기 때문에 당연히 JVM 컴파일러가 최적화할 여지가 없다. 정확히 JIT 컴파일러가 동작할 수 없음 - JIT컴파일러가 동작하지 않아서 매번 미리 명시된 클래스 타입이 맞는지, 생성자가 존재하는지, 생성자에 대한 validatio 과정이 들어간다. - static 영역에서 클래스 정보에 대한 discover 과정이 필요 - 모든 인자들이 박싱/언박싱 과정을 거쳐야 함 - 성능 이슈 외에도 - 런타임에서의 접근 허용에 따른 보안상의 문제 - private 메서드 노출 문제 -> 리플렉션을 사용하려면 프레임워크 혹은 라이브러리 등을 만들 때와 같은 합당한 이유가 있어야 함 ---