Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- spread 연산자
- critical rendering path
- Unity Editor
- Packet Network
- Spring Boot
- Google Developer API
- server
- draganddrop
- docker
- Camera Movement
- Git
- screencapture
- MySQL
- java
- react
- Unity IAP
- --watch
- rpg server
- OverTheWire
- SDK upgrade
- springboot
- Camera Zoom
- Digital Ocean
- nodejs
- mongoDB
- unity
- Google Refund
- express
- css framework
- linux
Archives
- Today
- Total
우당탕탕 개발일지
[방치RPG 서버 제작기] 4. 액세스 토큰 발급 , 기본 데이터 저장하기 본문
Spring Boot로 JWT 기반 방치형 게임 서버 만들기
오늘은 방치형 RPG 게임 서버에서 "게스트 로그인"을 구현해보고, 로그인 시 JWT 토큰을 발급하고, 유저에게 기본 장비를 지급하는 구조를 만들어봤다.
1. JWT가 뭐고 왜 쓰는 걸까?
JWT(Json Web Token)는 로그인한 유저를 식별할 수 있는 서버 인증 토큰이야. AccessToken 안에 userId 같은 정보를 암호화해서 담아두고, 이후 요청에선 DB 조회 없이 인증할 수 있게 해주지.
2. application.yml 설정
profile별로 jwt.secret을 다르게 설정해줘야 해.
spring:
profiles:
active: dev
---
spring:
config:
activate:
on-profile: dev
datasource:
...
jwt:
secret: dev-super-secret-key
expiration: 7200000
---
spring:
config:
activate:
on-profile: prod
datasource:
...
jwt:
secret: prod-super-secret-key
expiration: 3600000
주의할 점은 jwt:는 spring: 하위에 있으면 안 되고 같은 레벨에 둬야 한다는 것!
3. JwtProvider 클래스 만들기
@Component
public class JwtProvider {
@Value("${jwt.secret}")
private String secretKey;
@Value("${jwt.expiration:7200000}")
private long expirationTime;
private Key getSigningKey() {
return Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
}
public String createToken(Long userId) {
Date now = new Date();
return Jwts.builder()
.setSubject(String.valueOf(userId))
.setIssuedAt(now)
.setExpiration(new Date(now.getTime() + expirationTime))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public Long getUserIdFromToken(String token) {
return Long.parseLong(Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody()
.getSubject());
}
public boolean isTokenValid(String token) {
try {
Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token);
return true;
} catch (JwtException e) {
return false;
}
}
}
4. UserService에서 게스트 로그인 처리
public class UserService {
public User guestLogin(String deviceId) {
Optional<User> optional = userRepository.findByDeviceId(deviceId);
User user;
if (optional.isPresent()) {
user = optional.get();
user.setNewlyCreated(false);
} else {
user = new User(deviceId);
user.setNewlyCreated(true);
userRepository.save(user);
}
return user;
}
}
5. UserDataService에서 유저 데이터 조회 + 기본 지급 처리
@Service
@RequiredArgsConstructor
public class UserDataService {
private final EquipmentRepository equipmentRepository;
private final ObjectMapper objectMapper = new ObjectMapper();
public String getAllUserDataAsJson(Long userId) {
List<Equipment> equipments = equipmentRepository.findAllByUserId(userId);
List<EquipmentResponse> equipmentResponses = equipments.stream()
.map(e -> new EquipmentResponse(e.getItemId(), e.getLevel(), e.getCount()))
.collect(Collectors.toList());
UserGameData data = UserGameData.builder()
.equipments(equipmentResponses)
.build();
try {
return objectMapper.writeValueAsString(data);
} catch (JsonProcessingException e) {
throw new RuntimeException("직렬화 실패", e);
}
}
public void createInitialData(Long userId) {
Equipment defaultWeapon = new Equipment(userId, 1, 1, 1);
equipmentRepository.save(defaultWeapon);
}
}
6. UserController에서 최종 로그인 응답 처리
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/user")
public class UserController {
private final UserService userService;
private final UserDataService userDataService;
private final JwtProvider jwtProvider;
@PostMapping("/guest-login")
public ResponseEntity<UserResponse> guestLogin(@RequestBody GuestLoginRequest request) {
User user = userService.guestLogin(request.getDeviceId());
if (user.isNewlyCreated()) {
userDataService.createInitialData(user.getId());
}
String token = jwtProvider.createToken(user.getId());
String userData = userDataService.getAllUserDataAsJson(user.getId());
UserResponse response = new UserResponse(token, userData);
HttpStatus status = user.isNewlyCreated() ? HttpStatus.CREATED : HttpStatus.OK;
return new ResponseEntity<>(response, status);
}
}
7. 응답 DTO 구조
public class UserResponse {
private String accessToken;
private String userData;
public UserResponse(String accessToken, String userData) {
this.accessToken = accessToken;
this.userData = userData;
}
}
주의!
각 domain 이 되는 클래스와 Response DTO 클래스 에는 다음처럼 Lombok 어노테이션이 붙어야한다. 그렇지 않으면 Json 직렬화에서 에러가 발생할 수 있다.
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.Builder;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class EquipmentResponse {
private int id;
private int level;
private int count;
}
+ 추가)
이미지에서 보면, skills 가 없어서 "null" 이라고 나온다. 이 문자열을 클라이언트에서 받을 경우 파싱 에러가 발생할 수 있으므로, 기본 List를 생성하도록 처리해주자.
import lombok.*;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserGameData {
@Builder.Default
private List<EquipmentResponse> equipments = new ArrayList<>(); //기본적으로 List생성
@Builder.Default
private List<SkillResponse> skills = new ArrayList<>();
}
성공적으로 동작한다
다음엔 백업기능과 엑세스토큰을 이용한 인증을 만들어보자..!
수정완료
'Server > 방치RPG 서버' 카테고리의 다른 글
[방치RPG 서버 제작기] 6. 전송 암호화, DB테이블 구조 분리하기 (0) | 2025.04.25 |
---|---|
[방치RPG 서버 제작기] 5. 액세스 토큰 + 리프레시 토큰 관리 및 갱신 (0) | 2025.04.23 |
[방치RPG 서버 제작기] 3. 방치 RPG 유저 데이터 관리 (이론) (0) | 2025.04.21 |
[방치RPG 서버 제작기] 2. 게스트 로그인 및 엑세스 토큰 발급 (2) | 2025.02.02 |
[방치RPG 서버 제작기] 1. 프로젝트 생성 + 통신 구현 (1) | 2025.01.26 |