Spring Boot에서 JWT + Redis로 게스트 로그인 시스템 구현하기

2025-04-18
#JAVA

이번에 웹게임 프로젝트를 진행하면서 비회원도 게스트 계정으로 게임을 플레이할 수 있게 하는 기능이 필요했다.

따라서 평소엔 항상 회원만 관리했지만 이번에는 비회원을 관리하는 로직이 필요해졌다.

하지만 구글링을 해도 내가 원하는 방식의 게스트 로그인은 보이지 않았다.

그래서 나는 직접 게스트 로그인 시스템을 설계하고, JWT + Redis를 활용해서 구현해보았다.


목표

  • 비회원도 로그인 없이 게임 이용 가능

  • 별도 회원가입 없이 “게스트 계정”으로 임시 토큰 발급

  • 일정 시간 동안 유효한 로그인 상태 유지

  • 게스트가 임시 회원이므로 DB가 아닌 Redis에 게스트 정보 저장

  • 토큰 기반 인증 시스템 그대로 활용


전반적인 흐름

1. 게스트 로그인 요청 → /auth/guest/sign-in

게스트 로그인 API를 호출하면 아래와 같은 흐름으로 처리된다.

  1. 1

    UUID 기반 무작위 게스트 ID와 닉네임 생성

  1. 2

    해당 정보를 Redis에 저장하고 TTL 설정

  1. 3

    JWT 토큰 발급 (Access + Refresh)

  1. 4

    클라이언트는 일반 회원처럼 토큰을 받아 게임을 이용할 수 있음

Java
public TokenDto guestSignIn() { Long guestId; do { guestId = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; } while(redisUtil.existed(GUEST_KEY_PREFIX + guestId)); String nickname = RandomNicknameGenerator.generateNickname(); User guestUser = User.builder() .id(guestId) .nickname(nickname) .role(Role.GUEST) .build(); redisUtil.setData(GUEST_KEY_PREFIX + guestId, guestUser); redisUtil.setDataExpire(GUEST_KEY_PREFIX + guestId, GUEST_TTL_SECONDS); return jwtHelper.createGuestToken(guestUser); }

RandomNicknameGenerator는 "귀여운고양이", "졸린펭귄"처럼 무작위로 형용사와 명사가 조합된 닉네임을 생성해준다.

게스트 유저는 DB에 저장하지 않고 Redis에서만 관리하며 일정 시간 후에는 자동 만료된다.


2. 인증 요청 시 → JwtAuthenticationFilter + GuestDetailsService

게스트 유저도 JWT 토큰 기반으로 인증을 받는다.

Spring Security의 커스텀 UserDetailsService를 통해 Redis에서 정보를 불러온다.

Java
UserDetails userDetails = guestDetailsService.loadUserByUsername(guestId.toString());
Java
User guest = Optional.ofNullable((User) redisUtil.getData(GUEST_KEY_PREFIX + guestId)) .orElseThrow(() -> new UsernameNotFoundException("Guest not found : " + guestId)); redisUtil.setDataExpire(GUEST_KEY_PREFIX + guestId, GUEST_TTL_SECONDS); // TTL 연장 return new SecurityUserDetails(guest);
  • Redis에 정보가 있으면 TTL을 연장하면서 인증

    • 게스트의 TTL이 짧기 때문에 게임 중 TTL이 만료되면 안되기 때문에 계속해서 연장해주는 방식으로 하게 됨

  • 정보가 없으면 만료된 계정이므로 재로그인을 유도


3. 게스트 회원 전환 → /auth/guest/sign-up

게스트가 마음에 들어 게임을 더 즐기고 싶을 때, 회원 전환을 할 수 있다.

  1. 1

    게스트 토큰을 가진 상태에서 회원가입 요청

  1. 2

    Redis에서 해당 게스트 정보를 불러와 새로운 유저로 전환

  1. 3

    DB에 저장하고 Redis에서는 해당 정보 삭제

Java
public TokenDto guestSignUp(SignUpReq signUpReq, SecurityUserDetails userDetails){ if (!userDetails.getRole().equals(Role.GUEST)) { throw new CustomException(AuthExceptionCode.INVALID_GUEST); } Long guestId = userDetails.getId(); User guest = Optional.ofNullable((User) redisUtil.getData(GUEST_KEY_PREFIX + guestId)) .orElseThrow(() -> new CustomException(AuthExceptionCode.INVALID_USERID)); String encodedPassword = passwordEncoder.encode(signUpReq.password()); User newUser = userRepository.save(signUpReq.toEntityFromGuest(encodedPassword, guest)); redisUtil.deleteData(GUEST_KEY_PREFIX + guestId); return jwtHelper.createToken(newUser, false); }
  • 이 과정에서 기존 게스트 경험치/스코어를 그대로 이전할 수 있도록 ID를 유지한다.

  • 회원 전환 후, 일반 유저로서 Access/Refresh 토큰이 재발급된다.


인증 필터는 어떻게 구성했는가?

JwtAuthenticationFilter에서 토큰을 읽고, GUEST인지 USER인지 구분하여 적절한 인증 서비스를 호출한다.

Java
String role = tokenProvider.getRole(accessToken); UserDetails userDetails; if (role.equals(String.valueOf(Role.GUEST))) { Long guestId = tokenProvider.getUserId(accessToken); userDetails = guestDetailsService.loadUserByUsername(guestId.toString()); } else { String username = tokenProvider.getUsername(accessToken); userDetails = userDetailsService.loadUserByUsername(username); }

Redis TTL은 왜 필요한가?

게스트 계정은 임시로 생성되는 계정이기 때문에, 굳이 DB에 영구적으로 저장할 필요성을 느끼지 못했다.

오히려 DB에 저장하는 순간부터 불필요한 데이터가 쌓일 수 있고 짧은 시간 사용하고 사라지는 계정 특성상 가벼운 저장소인 Redis가 더 적합하다고 판단했다.

그래서 게스트 계정은 로그인 시점부터 일정 시간 동안만 Redis에 존재하며 그 이후엔 TTL 설정을 통해 자동으로 만료된다.

Java
redisUtil.setDataExpire(GUEST_KEY_PREFIX + guestId, GUEST_TTL_SECONDS);

또한, 사용자가 계속해서 활동 중이라면 TTL을 갱신하여 접속 상태를 유지시킬 수 있다. 일정 시간 이상 아무 활동이 없다면 자동으로 로그아웃되며 Redis에서도 정보가 삭제된다.

다만, Redis 역시 메모리 기반 저장소이기 때문에 게스트 사용자가 많아질 경우 부담이 될 수 있다는 점도 문제가 될 수 있다.

앞으로 캐시 용량이나 구조를 조정하거나 게스트 상태를 더 가볍게 유지할 수 있는 다른 대안들도 고려해볼 예정이다.


정리하며

비회원 사용자의 흐름을 "게스트"라는 개념으로 통합하고,

회원과 동일한 JWT 인증 시스템을 재사용하면서 DB가 아닌 Redis로 부담을 줄인 구조를 만들 수 있었다.

  • 빠른 체험 진입

    회원가입 없이도 바로 게임에 입장할 수 있어 진입 장벽이 낮다.

  • 적당한 보안 수준 유지

    JWT + Redis TTL을 통해 기본적인 인증 및 만료 처리를 할 수 있다.

  • 전환율 증가 유도

    먼저 체험해보고 재미를 느낀 후 자연스럽게 회원 가입으로 이어질 가능성이 높다.

 

이번에 구현한 게스트 로그인 시스템이 완벽하거나 정답이라고 생각하지는 않는다.

아직 부족한 부분도 많고, 더 나은 구조로 개선할 여지도 충분히 있다.

앞으로도 계속 리팩토링하고 발전시켜 나가면서 더 나은 방식으로 다듬어갈 계획이다.