이번에 매장 예약 서비스를 개발할때 Spring data jpa로 개발하다보니 연관 관계에 대해 처음 알게되어 정리하고자 작성해보려고 한다
연관 관계
DB의 테이블은 외래키를 사용해서 방향의 개념 없이 연관 관계를 맺을수 있다 생각해보면 외래키로 연결되어 있는 테이블들은 서로 조인해서 조회할수 있다
하지만 객체는 다르다
객체의 연관관계는 참조용 필드를 가지고 있는 객체만 연관된 객체를 조회 할수 있다 방향의 개념이 있는 것이다
단방향과 양방향
객체 관계에 한쪽만 반대쪽을 참조하는 관계를 단방향 양쪽 모두 서로를 참조하는 관계를 양방향이라고 한다
단방향인경우 참조용 필드를 갖고 있는 객체에선 연관된 객체를 조회가 가능하지만 반대로 가지고 있지 않는 객체쪽에서는 조회되지 않는다
내 프로젝트에서 살펴보면 ReservationEntity 객체엔 StoreEntity와 MemberEntity를 참조하고 있다 하지만 StoreEntity와 MemberEntity에는 참조용 필드 (ReservationEntity)를 가지고 있지 않으므로 단방향 연관 관계를 설정해준것이다
(연관 관계설정을 해주었다는 사실도 잘 모른채 개발했었다.. 테이블처럼 객체들도 연결시켜야할것같은 느낌에서 한것같은데 어떻게 한건지 나도 신기하다;) 양방향으로도 설정해줄수 있다는 사실을 알고난후 생각해보았는데 매장을 조회 하는데 예약 내역까지 같이 조회될 필요가 있을까 고민하다가 아직 나는 배우는 단계이므로 양방향으로도 설정해보았다 그래서 매장 테이블에 컬럼을 추가해야하나 생각해봤는데 그럴필요가 없었다 양방향으로 설정해주기 위한 필드명만 적어주면 된다 그렇게 매장 Dto에도 같은 타입으로 적어주고 fromEntity 메소드에도 해당 필드를 받을수 있도록 수정했다
에러 발생
그렇게 연관관계를 양방향으로 설정하고 조회해보니 아래와 같은 에러가 발생했다
(fromEntity 메소드는 엔티티에서 Dto로 변환 시켜주는 메소드이다)
원인
확인해보았더니 StoreDto의 fromEntity 메소드가 ReservationDto의 fromEntity메소드를 실행시키는데 그 메소드에 StoreDto의 fromEntity 메소드를 또 실행시킨것이다 그러다보니 무한 참조가 발생해서 StackOverflowError : null 에러 발생한 것이다..
해결한 방법 - 연관 관계를 가진 엔티티 저장방식 개선
그래서 어떻게 해결해야할지 고민을 해봤더니 연관 관계를 가진 엔티티를 조회나 저장할때 이전에는 모두 파라미터로 받아 저장했었다 생각해보면 매장과 사용자의 각 id값만을 파라미터로 받아 조회와 저장을 하는 방식으로 했어야 했다 파라미터로 id값을 제외한 불필요한 필드 값들을 모두 받아줄 필요가 없었다 그래서 연관 관계를 가진 엔티티 저장 방식을 개선하려다보니 아래와 같은 절차를 밟게 됬다
1. 먼저 파라미터로 받는 예약 Dto를 수정해주었다
아래 사진과 같이 참조타입 StoreDto, MemberDto를 모두 int로 수정했다 (예약 엔티티는 그대로이다)
2. 예약 Dto가 수정됨에 따라 자연스레 fromEntity 메소드도 수정해주었다 여기서 에러를 해결하였다!!
예약 Dto는 더이상 StoreDto와 MemberDto에 참조하지 않기 때문에 무한 참조 에러가 해결되었다
3. 예약 Dto가 수정됨에 따라 서비스 로직도 수정해주었다
이전에는 파라미터로 받은 ReseravtionDto 안에 있는 StoreDto, MemberDto에서 값을 빼와서 엔티티로 변환 시켜주었지만 지금은 파라미터로 받은 int형인 매장, 이용자 고유번호를 가지고 getById를 통해 프록시 객체(매장, 이용자 엔티티)를 반환받는다 예약을 저장하기 위해 매장, 이용자의 엔티티 형식은 필요하지만 id값만 필요했었기 때문에 이 방법이 딱 맞았었다
getById는 id값을 제외한 나머지 필드에 접근했을때 query가 발생한다
즉 실제 테이블 조회를 미루고 프록시 객체를 가져온후 id 값을 제외한 나머지 필드에 접근했을때 조회해온다
이렇게 되면 저장할때에도 id값만 저장하기때문에 (id 값을 제외한 나머지 필드에 접근 X) SELECT 쿼리는 수행하지 않는다
다른 방법
id값만 저장하는 것이라면 파라미터에서 받아온 id 값을 getById를 사용하지 않고 바로 넣어주는 방법도 있다
하지만 이방법은 별로 좋지 못한 방법인것 같았다 그 이유는..
만약에 파라미터로 존재하지 않는 매장, 사용자의 고유번호를 받게 된다면 저장 자체를 수행하면 안되기 때문이다!
이렇게 해서 양방향 관계 설정과 Dto를 수정함과 동시에 무한참조 에러를 모두 해결할수 있었다
참고 링크
https://jgrammer.tistory.com/147
https://bcp0109.tistory.com/325
https://k3068.tistory.com/103
https://passionfruit200.tistory.com/386
https://hello-backend.tistory.com/164
https://blogshine.tistory.com/345
https://velog.io/@neity16/3-JPA-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-6-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91-1-%EB%8B%A8%EB%B0%A9%ED%96%A5%EC%96%91%EB%B0%A9%ED%96%A5-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EC%A3%BC%EC%9D%B8-mappedBy
'프로젝트' 카테고리의 다른 글
매장 예약 - Redis 캐시 적용 (0) | 2024.02.22 |
---|---|
매장 예약 서비스 - 동시성 문제 (0) | 2024.02.19 |
배당금 프로젝트 (1) | 2024.01.31 |
날씨 일기 프로젝트 (0) | 2024.01.27 |
계좌 시스템 개발 프로젝트 (0) | 2024.01.26 |