문제가 발생한 Controller
UserControllerTest
그냥 봤을 때 전혀 문제 없이 진행할 수 있는 테스트입니다.
User 관련한 Test 코드를 실행했을 때 상황을 보겠습니다.
유저 관련 Test만 진행했을 때 전혀 문제없이 통과합니다.
순서를 어떻게 해도 통과합니다.
단순 회원가입 기능만 작성했기 때문에 그렇게 꼬일 일이 없습니다.
그럼 전체 테스트를 돌려봤을 때 상황을 살펴보겠습니다.
전체 테스트 돌렸을 때 발생한 문제
UserControllerTest만 실패
다른 테스트는 리팩토링 과정 중에 아직 수정하지 않고 UserTest 쪽 먼저 진행 중에 글을 작성한 것입니다.
신경쓰지 않아도 됩니다.
대체 왜 UserControllerTest가 실패했을까요?
처음 이 문제를 해결하기 위해서 여러가지 방법을 생각했습니다.
글과 사진으로 자세하게 설명하겠습니다.
UserControllerTest 테스트가 통과하기 위해서는 어떻게 해야 될까?
ERD
첫 번째 방법을 말씀드리기 전에 ERD를 먼저 확인해야 됩니다.
User는 모든 엔티티와 매핑이 되어있습니다.
User끼리 테스트 했을 때는 문제가 발생하지 않았던 이유는 순수한 User만 테스트했기 때문입니다.
제가 작성한 Item, Comment, Negotiation의 테스드 코드들은 모두 User를 생성해서 사용 중입니다.
테스트 코드의 순서가 보장이 되지 않기 때문에 UserControllerTest 실행 전에 작성했는 코드들과 충돌하는 겁니다.
어떤 것이 문제인지 코드로 바로 확인하겠습니다.
첫 번째 방법
테스트가 통과했습니다.
전에 작성했던 코드와 차이점입니다.
1. userRepository.deleteAll() 메서드를 주석처리
UserControllerTest 시작 전에 Item, Nego, Comment 등 테스트 들이 먼저 실행되는 모습을 볼 수 있습니다.
이미 User와 연관관계로 매핑이 되어있고 테스트를 위해 User를 생성해서 사용했기 때문에 deleteAll()가 작동하지 않습니다.
cascade, orphanRemoval 설정이 되어있지 않은 상태이기 때문에 user를 지울 수 없습니다.
이건 마지막에 더 다루겠습니다.
2. username을 다르게 작성
username을 다르게 작성한 이유는 중복된 username 검증 메서드가 존재하기 때문입니다.
현재 상황은 중복검사 메서드에 걸리면서 테스트가 실패합니다.
따라서 중복 검사에 걸리지 않았기 때문에 테스트에 통과할 수 있었습니다.
첫 번째 이유는 UserControllerTest 이전에 실행 되는 테스트 코드의 영향을 받았기 때문입니다.
Cascade, orphanRemoval
1. CascadeType.REMOVE는 부모 엔티티가 삭제되면 자식 엔티티도 삭제됩니다.
즉, 부모가 자식의 삭제 생명 주기를 관리합니다.
테스트를 위해 User 엔티티에 영속성 전이 옵션을 추가하겠습니다.
부모 엔티티를 삭제하겠습니다.
부모 엔티티를 삭제하면서 자식도 영속성 전이 옵션으로 인해 함께 삭제됩니다.
2. orphanRemoval = true
이 옵션은 부모 엔티티가 자식 엔티티의 관계를 제거하면 자식은 고아로 취급되어 그대로 사라집니다.
테스트를 위해 고아 객체 옵션을 추가하겠습니다.
위 실험과 마찬가지로 부모 엔티티를 지워보겠습니다.
마찬가지로 잘 삭제되는 것을 확인할 수 있습니다. CascadeType.REMOVE와 동일하게 동작합니다.
그럼 여기서 무슨 차이가 있냐고 하면
부모 엔티티에 있는 자식의 관계를 끊었을 때, 그 자식이 삭제되는 것입니다.
부모 엔티티에서 자식 엔티티를 제거해보겠습니다.
delete 쿼리가 1번 발생합니다. 고아로 취급하고 그 자식만 삭제하기 때문입니다.
cascade와 orphanRemoval 비교
- 부모 엔티티 삭제
CascadeType.REMOVE와 orphanRemoval = true 는 부모 엔티티를 삭제하면 자식 엔티티도 삭제합니다.
부모 엔티티에서 자식 엔티티 remove
- CascadeType.REMOVE는 자식이 살아있지만, orphanRemoval = true는 자식 엔티티를 제거합니다.
주의점
이러한 옵션 설정을 통해 테스트를 통과하게 만들 순 있지만, 항상 조심해야 합니다.
자식 엔티티가 나도 모르는 사이에 삭제되는 일이 일어날 수 있기 때문입니다.
자식 엔티티에 딱 하나의 부모 엔티티가 연관되어 있는 경우에만 사용해하합니다.
두 번째 방법
지금 당장 테스트를 통과할 수 있게 CascadeType.REMOVE를 이용하겠습니다.
User를 지우면 연관관계 매핑때문에 발생했던 참조 무결성 오류를 해결할 수 있습니다.
프로젝트 방향이 어떻게 흘러갈지 모르는 상황이긴한데,
차례대로 Comment를 전체 지우고 Item을 전체 지우고 그 후에 User를 지우는 방식으로 해도 됩니다.
CascadeType.REMOVE를 이용해보고 이후에 프로젝트를 진행하면서 문제가 발생하면 추가하겠습니다.
CascadeType.REMOVE를 적용시켰습니다.
User를 지우겠습니다.
테스트 통과합니다.
기존 프로젝트는 연관관계 매핑을 하지 않았었는데, 이번에 리팩토링 하면서 학습 + 적용한 내용입니다.
영속성 전이에 대해 학습할 수 있었고 더 좋은 방법이 생각나거나 이 설정으로 인한 문제 발생시 그걸 해결하는 과정을 적겠습니다.
테스트 코드를 작성해서 발생할 수 있는 문제를 먼저 파악할 수 있고 올바른 방향으로 해결하기 쉽다는 것을 느꼈습니다.