(오류 주의) 알고 있던 지식에 잘못된 부분이 있어 후속 글을 작성했습니다. 이 글을 참고해주세요!
안녕하세요. 오랜만에 글을 적어봅니다.
오늘 개발하며 있었던 소소한 삽질 또는 깨달음?을 공유해보려 가벼운 글을 적어봅니다.
현재 개발중인 어플리케이션에서 로그인 로직을 구현하고 있습니다. Interceptor
를 활용하여 JWT 토큰을 검증하고, 유효한 토큰을 가지고 요청한 사용자라면 HttpServletRequest
에 사용자 정보로 조회한 엔티티 객체인 User
객체를 보관하여 이후 요청에 활용하려는 상황입니다.
보통 userId만을 저장하기도 하지만, 그러면 User
가 필요할 때마다 DB에 질의하여 가져와야 하기 때문에 이 방식이 굉장히 괜찮아보였습니다.
그리고 이 객체를 더 편리하게 사용하기 위해 ArgumentResolver
를 도입했습니다.
ArgumentResolver란 쉽게 말하면, Controller
로 들어오는 파라미터에 특정 어노테이션을 붙이거나 했을 때 그 argument를 가공해주는 역할을 합니다.
여기서는 HttpServeltRequest
로부터 매번 getAttribute("user")
를 하지 않기 위한 작업이라고 보시면 됩니다.
필요한 controller 메서드의 파라미터에 @Auth
라는 어노테이션을 붙여서 User
객체를 추가하면 이 argument resolver가 작동하여 HttpServeltRequest
로부터 user를 찾아 넣어줍니다.
뭔가 우아(?)해 보이는 방법인 거 같아서 굉장히 맘에 들었습니다. 하지만 눈치 채신 분들도 있겠지만 문제가 발생합니다.
맨 처음 Interceptor
를 보시면, AuthService
로부터 객체를 조회하여 가져오게 됩니다.
이번 프로젝트에서는 JPA를 사용했습니다. 그렇기 때문에 Service 계층에서는 트랜잭션을 통해 가져온 객체가 반환되고 이를 HttpServeltRequest
에 저장하였습니다.
문제는 JPA의 영속성 컨텍스트
입니다. 영속성 컨텍스트는 한 마디로 DB와 어플리케이션의 중간 계층 쯤 되는 것으로, 트랜잭션이 진행되는 동안 DB로부터 가져온 엔티티나 어플리케이션에서 영속화한 엔티티에 대한 정보를 일시적으로 메모리에서 관리해주는 컨텍스트 정도로 이해하고 있습니다.
영속성 컨텍스트가 관리해주는 엔티티 객체는 트랜잭션이 끝나면 자동으로 변경 감지와 같은 것들이 일어나며 다시 DB로 커밋되게 됩니다.
문제는 AuthService
로부터 유저를 가져오는 시점에 트랜잭션이 끝난다라는 점입니다. 이게 왜 문제일까요?
문제를 발견한 건 로그아웃 기능을 만들 때였습니다. 사용자가 로그아웃을 하면 DB에 저장된 refresh token
을 삭제하려고 했습니다. 그래서 api를 정의하고, controller에서는 argument resolver를 통해 로그아웃 할 user를 전달받고, service를 통해 user의 refresh token
을 삭제한 후 별도의 쿼리를 날리지 않았습니다.
왜냐하면 당연히 영속성 컨텍스트
가 변경 감지(더티 체킹)을 통해 저장해줄 것이라 기대했기 때문이었죠! 하지만 DB에는 아무 일도 일어나지 않았습니다. 왜냐하면 이미 이 user
는 영속성 컨텍스트가 관리하는 객체가 아니었던 것입니다..
왜 그럴까? 생각해보면 당연합니다. 이미 트랜잭션이 끝나고 커밋돼버린 user 객체는 더 이상 영속성 컨텍스트에서 관리하지도 않고 관리할 이유도 없습니다. 그걸 세션에서 꺼내서 재사용한다고 한들 영속성 컨텍스트와 이미 남남이 돼버린 객체는 수동으로 save를 호출하던지 해서 반영해야 했습니다.
또는 em.persist()
를 통해 영속 상태로 만든 뒤에 user를 변경하고 트랜잭션을 종료하든 해야한다는 걸 깨달았습니다.
그러면서 근본적으로 'DB에 매번 조회하기 싫어서 HttpServeltRequest
에 user
객체를 넣었는데 영속성 컨텍스트에서 관리되지 않으니 어떻게 하는게 좋을까? 그냥 userId
만 보관할까?' 라는 의문이 생겼습니다.
아직 이 질문에 대해 정답을 내리지 못해 주변 동료들에게도 의견을 물어보고 다른 코드를 참조해보기로 했습니다.
사소한 삽질이었지만 오랜만에 블로그에 글도 적을 겸 적어봤습니다! 너무 쉬운 주제여서 큰 도움이 안되셨다면 죄송합니다.
하지만 틈틈이 JPA 강의를 들으며 영속성 컨텍스트에 대해 배우고 있었는데 이런 주제를 마주쳐서 신기하기도 하고 정리해보고 싶어서 글을 적게 되었습니다!!
앞으로의 블로그는 이런 류의 삽질 공유나 하나의 주제를 정해 작성하는 것으로 활용해볼까 싶은 생각이 드네요!
읽어주셔서 감사합니다!! 🙇♂️