이 오류는 아마 Novel 엔티티를 생성할 때 나온 오류였다. 검색을 하니 해당 테이블을 참조하는 FK 때문에 Novel 테이블의 row를 삭제하거나 변경할 수 없었다는 의미.
Novel을 참조하는 FK는 오로지 User밖에 없기에 User로 가서 무엇이 문제인지 확인했다. 근데... 뭐 잘못된 게 없는데요?
인터넷 검색을 통해 Cascade도 넣어주고 orphanRemoval도 넣어주었는데, 변한 건 하나도 없이 오류는 계속됐다. 그러다 문득 예약어에 관한 글을 봤다. 과거에 SpringSecurity를 사용할 때 user라는 엔티티를 쓰면 예약어로 문제가 생길 수 있다는 글이 떠올랐고. 그래서 user를 Member로 고쳐주는 작업을 했다.
그렇게 다 됐나 싶었는데, 또 생기는 오류!
흠... 이건 예상보다 가볍게 해결이 됐는데, 바로 내가 과거에 초기 테스트를 한다고 Member 클래스를 미리 생성해 놨기 때문에 Spring이 중복된 이름이 있다고 알려주는 것이었다.
때문에 과거에 생성된 클래스를 삭제해주니 완전히 해결됐었다.
위의 두 개를 해결하니 더이상 novel의 FK에 문제가 있다고 나오지 않았다. 그래서 난 mariadb에 user라는 예약어가 있어서 member로 바꿔주니 해결이 된 줄 알았다. 근데 찾아보니 mariadb에는 user라는 예약어가 없었다. 혹시 몰라서 챗 GPT한테도 물어보니 없다고 한다.
엥...?
그럼 어떻게 해결이 된 거지..? 라는 의문과 함께... 흠...
아무튼 그래서 나는 원하는 novel_id를 가진 chapter만 반환하는 쿼리를 짜고 싶었다. 그래서 처음에는 아래 쿼리처럼 작성했는데, 오류가 났다.
return em.createQuery("select c from Chapter c where c.novel_id = :id", Chapter.class).setParameter("id", novelId).getResultList();
위 쿼리의 문제는 무엇일까? 바로 where문에 있다.
나는 Chapter에 Novel을 novel_id로 연결했디 때문에 당연히 c.novel_id를 하면 자연스럽게 Chapter c가 가지고 있는 novel_id를 조회할 것이라고 생각했다.
그러나 아니였다..!
일단 저 novel_id가 문제였다. novel_id는 column을 통해 db에 저장한 컬럼 이름으로 실제 novel 클래스에서는 id로 지정이 되어 있다. 그렇기 때문에 spring은 아무리 novel_id를 가져오라고 해봤자 도대체 그게 뭔지 모르는 것이다. 때문에 db에 저장된 값이 아닌 novel에 설정해 준 값 id로 넣어줘야 한다.
그렇다면 c.id로 해주면 될까?
아니. 저렇게 하면 Chapter의 id를 가지고 온다. 그렇기 때문에 c.novel.id로 작성해서 Chapter에서 novel을 가지고 와서 해당 novel의 id를 추가로 가지고 오게 해야 한다.
아래처럼 작성하면 테스트를 무사 통과하는 걸 볼 수 있다.
return em.createQuery("select c from Chapter c where c.novel.id = :id", Chapter.class).setParameter("id", novelId).getResultList();
원래 오늘은 각 소설의 챕터를 가져오는 컨트롤러와 뷰를 만들려고 했는데, 위의 에러들을 처리하느라 시간이 다 갔다. 결론은 생각보다 단순한 거 같았지만, 그걸 찾아가는 데에는 시간이 꽤 걸렸다...