[Java Spring] 시간의 흐름을 테스트 코드에 적용하자 : 로그인 토큰 만료 시나리오 테스트
·
Java
배경현재 제가 진행 중인 토이 프로젝트에서는 로그인을 위해 액세스 토큰과 리프레시 토큰을 사용하고 있습니다. 이 중 액세스 토큰은 security가 직접 유효성과 만료 여부를 확인해주고 있지만, 리프레시 토큰은 제가 직접 작성한 코드에서 유효성을 검증하고 있는데요. 따라서 검증 기능의 테스트를 위해 `만료된 리프레시 토큰을 사용하면 예외가 발생하는 시나리오`를 가정하여 테스트 코드를 작성하게 되었고, 그 과정에 대해 정리해보려고 합니다.첫 번째 아이디어 : Thread.sleep()유효한 리프레시 토큰을 생성한 후, TTL이 지나서 만료되는 것을 확인해야 하기 때문에 시간의 흐름을 반영하는 테스트가 필요합니다. 첫 번째로 생각해볼 수 있는 방법은 토큰 생성 후 `Thread.sleep()`을 이용하는 것..
[Java Spring] 초대 링크 가입 로직 트러블슈팅 (3) - Redis 트랜잭션 활성화 시, 값을 조회했을 때 null이 조회되는 이슈
·
Java
배경 [Java Spring] 초대 링크를 통한 가입 로직 트러블슈팅 (2) : 예외 발생 시 데이터 롤백을 위해 Redis 트배경제가 진행했던 프로젝트에서는 초대 링크를 통해 관리하는 펫 그룹에 다른 유저를 가입시킬 수 있는 기능이 있으며, 해당 기능은 다음과 같은 요구 사항을 가지고 있습니다.초대 링크는 uuidseondays.tistory.com(위 포스팅에서 이어집니다) 제가 진행했던 프로젝트에서는 초대 링크를 통해 관리하는 펫 그룹에 다른 유저를 가입시킬 수 있는 기능이 있으며, 해당 기능은 다음과 같은 요구 사항을 가지고 있습니다.초대 링크는 uuid 값을 이용하여 생성되고 해당 uuid 값은 Redis에 저장된다.초대 링크 값을 통해 가입 요청이 들어오면, MySQL에 저장된 유저, 펫 정보..
[Java Spring] 초대 링크를 통한 가입 로직 트러블슈팅 (2) : 예외 발생 시 데이터 롤백을 위해 Redis 트랜잭션 적용 시도하기
·
Java
배경제가 진행했던 프로젝트에서는 초대 링크를 통해 관리하는 펫 그룹에 다른 유저를 가입시킬 수 있는 기능이 있으며, 해당 기능은 다음과 같은 요구 사항을 가지고 있습니다.초대 링크는 uuid 값을 이용하여 생성되고 해당 uuid 값은 Redis에 저장된다.초대 링크 값을 통해 가입 요청이 들어오면, MySQL에 저장된 유저, 펫 정보와 Redis에 링크 uuid 값이 있는지를 확인하고 가입을 진행한다.초대 링크는 1회용이다. 즉 생성된 초대 링크를 누군가 사용 완료한다면 다른 사람이 사용할 수 없게 Redis에서 해당 uuid 값이 삭제되어야 한다.이를 위해 하나의 메서드에서 여러 건의 JPA 작업과  Redis 작업을 함께 진행하고 있으며, 해당 작업들이 하나의 트랜잭션 안에서 진행되도록 `@Trans..
[Java Spring] 초대 링크를 통한 가입 로직 트러블슈팅 (1) : Redis 동시성 문제 해결
·
Java
배경제가 진행했던 프로젝트에서는 초대 링크를 통해 관리하는 펫 그룹에 다른 유저를 가입시킬 수 있는 기능이 있으며, 해당 기능은 다음과 같은 요구 사항을 가지고 있습니다.초대 링크는 uuid 값을 이용하여 생성되고 해당 uuid 값은 Redis에 저장된다.초대 링크 값을 통해 가입 요청이 들어오면, Redis에 링크 uuid 값이 있는지 확인하고 가입을 진행한다.초대 링크는 1회용이다. 즉 생성된 초대 링크를 누군가 사용 완료한다면 다른 사람이 사용할 수 없게 Redis에서 해당 uuid 값이 삭제되어야 한다.조건에 따라서 하나의 초대 링크에 여러 유저의 요청이 동시에 들어오더라도, 오직 첫 번째 요청만 정상 처리되고 나머지 유저의 요청은 링크 만료 예외가 터지며 가입이 실패해야 합니다.하지만 동일한 링..
[Spring Data Redis] 불필요한 Redis 메모리 점유 현상 개선하기 : Spring Data Redis에서 TTL 미적용 문제 발생
·
Java
배경프로젝트에서 새롭게 로그인을 하는 경우 리프레시 토큰을 발급하여 Redis에 저장하도록 로직이 구현되어 있고, Redis 사용은 Spring Data Redis의 `Repository` 인터페이스를 통해 저장하고 있습니다.해당 토큰 entity의 TTL은 7일인데요. 따라서 생성된 지 7일 후면 Redis에서 토큰의 정보가 모두 사라져야 합니다. 그런데 어느 날 우연히 Redis의 sets 밑에 refresh set이 남아있는 것을 발견하게 되었는데, refresh set의 내부에는 정황상 리프레시 토큰값으로 보이는 value들이 가득했습니다.방금 확인한 Redis는 배포 이전에 로컬에서 로그인 테스트를 진행하며 사용했던 테스트 DB로, 사용하지 않은지 7일을 넘어 꽤 되었기 때문에 모든 토큰값이 지..
트랜잭션 격리 수준과 동시성 제어 이야기 (2) : SERIALIZABLE과 Deadlock
·
etc
포스팅 목표 트랜잭션 격리 수준과 동시성 제어 이야기 (1) : @Transactional과 synchronized들어가기 전.. Spring @Transactional with synchronized keyword doesn't workLet's say I have a java class with a method like this (just an example) @Transactional public synchronized void onRequest(Request request) { if (request.shouldAddBook()) { if (seondays.tistory.com(이전 포스팅에서 이어집니다) 이번 포스팅에서는 MySQL InnoDB 트랜잭션의 격리 수준을 `REPEATABLE READ..