- 프로젝트명 : "가시죠, 백오피스 만들기 프로젝트"
- 프로젝트 소개 : 사용자 Role에 따라 인가할 수 있는 관리자 기능을 만드는 프로젝트입니다.
- 사용 기술: #Java #Spring Boot #JPA #MySQL #Redis
GitHub: https://github.com/k-jaehyun/IForest.git
어제에 이어 오늘도 팀프로젝트를 진행했다.
계획 했던 소셜로그인 기능을 구현해보지는 못하고, 대신 어제 구현했던 로그아웃의 버그를 해결했다.
로그인 이후 시간이 지나 토큰이 만료 되어 인증 할 수 없게 되었을 때,
다시 로그인을 시도하자 "이미 로그인 되어 있습니다." 라는 문구가 나왔다.
일회성 로그인 기능이라니! 서비스가 운영되지 못할 매우 중대한 사항이다.
발급되어있는 만료된 토큰이 문제이기 때문에 이러한 문제가 발생했다.
따라서 특정 시간이 지난 후 테이블에서 자동으로 토큰이 삭제되도록 할 필요가 있다고 판단하였다.
일단 '자동 삭제'라면 일정 시간마다 특정 메서드가 동작하게 만들 필요가 있었다. 생각난것은 스프링의 스케줄링 기능이었다. 이 기능을 사용하면 특정 시간에 주기적으로 특정 코드를 실행시킬 수 있기 때문이었다.
이 스케줄링기능 활성화를 위해 먼저 @EnableScheduling를 달아주었다.
자 생각을 해보자. 자동으로 메서드가 동작 할 방법은 찾았으니 이제 삭제 할 방법을 생각해내면 된다.
토큰의 만료시간에 맞춰서 삭제해야하므로 토큰이 언제 생성되었는지 확인 할 수 있는 createdTime 필드가 토큰 안에 필요 할 것이다.
물론 생성 될 때 생성자로 함께 생성되도록 만들어줬다.
그럼 이제 토큰을 삭제 할 메서드를 만들면 된다!
우선 아까 적용시켜놓았던 스케줄링 기능을 사용하기 위해 @Scheduled 어노테이션을 달아준다.
옵션으로는
가 있다. (테이블이 작을 때) 동작하는데 오래 걸리는 무거운 메서드가 아니라서 fixedRate를 사용했다.
일정 시간 이전에 생성된 모든 토큰을 불러와서 삭제해야하므로 쿼리문을 날려 줄 수 있는 deleteByCreatedTimeBefore
를 레포지토리에 작성한뒤, 현재시간으로부터 한시간 전의 시간을 넣어줬다.
모든 준비가 끝나고 프로그램을 돌려봤다.
(이 때는 시간설정을 1초마다 동작하고, 5초 전 토큰을 지우도록 테스트했다.)
그런데 두둥! Unexpected error occurred in scheduled task
라는 오류 메세지가 떴다.
설명은 다음과 같았다.
org.springframework.dao.InvalidDataAccessApiUsageException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call
No EntityManager라면 영속성에 의해 관리되지 않고 있기 때문에 remove를 실행 할 수 없다는 얘기인데... 그럼 간단히 @Transactional을 걸어주면 삭제가 되겠네!? 그렇지만 왜..?
그 이유는 "Spring Data JPA 의 delete" 메서드와 내가 레포지토리에 만들어준 "JPA Repository의 delete" 메서드는 서로 다르기 때문이다!
- Spring Data JPA의 delete : 이미 트랜잭션 범위에서 실행되며, 영속성 컨텍스트에 관리되는 엔티티를 대상으로 동작한다.
- JPA Repository의 delete: @Transactional 어노테이션이 없으면 트랜잭션 범위 밖에서 실행되어 데이터베이스와의 일관성을 보장하기 어렵다.
따라서 하나의 트랜잭션으로 묶이며, 트랜잭션 내에서 영속성 컨텍스트가 엔티티를 관리하도록 @Transactional을 사용해줘야하는 것이다.
영속성을 걸어주고 다시 실행했더니 성공적인 결과를 얻을 수 있었다!!!
서버에서 발생한 버그 때문에 다시는 서비스를 이용하지 못할뻔한 해당 유저는,
다행히 만료된 토큰이 테이블에서 삭제되며 다시 로그인 하여 서비스를 이용 할 수 있었다.
서비스의 평화를 지키는 코딩맨만 믿으라구!