728x90
JWT 토큰 모듈 설계 및 구현기
도입
MSA 구조에서 인증/인가 처리의 핵심은 토큰 기반 인증이다.
우리는 BFF가 직접 토큰 발급/검증 로직을 갖지 않도록 하기 위해 public-token이라는 전용 모듈을 만들어 책임을 분리했다.
이 글에서는 JWT 발급 및 검증을 위한 모듈을 설계하고 테스트한 과정을 공유한다.
설계 목표
- JWT 발급/검증 기능을 재사용 가능한 공용 모듈로 구성
- BFF, Auth 등 다양한 서비스에서 쉽게 DI하여 사용 가능하게 구성
.env또는application.yml을 통해 시크릿 키와 만료 시간 등 설정값을 외부화- WebFlux 환경에서도 간단히 연동 가능하도록 설계
설계 고려사항
- 키 보안: 서명 키는 외부 설정으로 주입받아야 하며, 256bit 이상을 권장
- 만료 처리: 토큰 유효 기간 설정은 환경에 따라 유연해야 함
- Claims 설계: 최소한 subject를 기반으로 식별 가능해야 하며, 확장을 고려하여 Map 기반 claims 구조 사용
- 서명 알고리즘: JJWT에서 기본 제공하는 HMAC-SHA256 알고리즘 사용
- Spring 의존도 낮춤: POJO 기반으로 설계하여 테스트가 쉽고 모듈 분리가 가능하게 함
구현 설명
JwtTokenProvider
토큰 발급, 검증, Claims 파싱 기능을 담당하는 클래스이다.
public class JwtTokenProvider {
public String issueToken(String subject, Map<String, Object> claims);
public Claims parse(String token);
public boolean isValid(String token);
public String getSubject(String token);
}
TokenConfig
Spring Boot에서 설정값을 바인딩하고 JwtTokenProvider Bean을 생성한다.
@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class TokenConfig {
@Bean
public JwtTokenProvider jwtTokenProvider(JwtProperties props) {
return new JwtTokenProvider(props.getSecret(), props.getExpiration());
}
}
JwtProperties
application.yml 또는 .env에서 설정을 받아오는 용도다.
jwt:
secret: ${TOKEN_KEY:dev-secret}
expiration: ${TOKEN_EXPIRATION:3600}
테스트 전략
단위 테스트는 다음 항목에 대해 작성되었다.
- 토큰 발급 후 subject 및 claims 파싱 테스트
- 유효한 토큰과 위조된 토큰에 대한
isValid테스트 - 만료 시간 테스트 (추가 예정)
class JwtTokenProviderTest {
private JwtTokenProvider tokenProvider;
@BeforeEach
void setUp() {
String secret = "mysecretkeymysecretkeymysecretkey"; // 최소 256bit (32자리 이상)
long expiration = 3600; // 1시간
tokenProvider = new JwtTokenProvider(secret, expiration);
}
@Test
void 토큰_정상_생성_및_파싱() {
// given
String subject = "user-123";
Map<String, Object> claims = new HashMap<>();
claims.put("role", "USER");
// when
String token = tokenProvider.issueToken(subject, claims);
Claims parsed = tokenProvider.parse(token);
// then
assertThat(parsed.getSubject()).isEqualTo(subject);
assertThat(parsed.get("role")).isEqualTo("USER");
}
@Test
void 토큰_유효성_검사() {
String token = tokenProvider.issueToken("user-456", Collections.emptyMap());
assertThat(tokenProvider.isValid(token)).isTrue();
assertThat(tokenProvider.isValid(token + "tampered")).isFalse(); // 위조된 토큰
}
@Test
void 토큰에서_subject_추출() {
String token = tokenProvider.issueToken("user-789", Collections.emptyMap());
String subject = tokenProvider.getSubject(token);
assertThat(subject).isEqualTo("user-789");
}
}
테스트는 POJO 기반 구조 덕분에 Spring 없이 순수하게 JUnit5 + AssertJ로 작성했다.
회고 및 확장 가능성
만족한 점
- 모듈 분리로 인증 로직을 각 서비스에서 재사용 가능하게 되었다
- 설정값 외부화로 환경에 따라 키, 만료시간 등을 쉽게 교체 가능
- 테스트 가능한 구조를 확보하여 추후 토큰 유형 변경에도 유연함
향후 확장 아이디어
- Refresh Token 기능 추가 및 Redis 연동 구조 도입
access,refresh,admin등 용도별 토큰 분리- 인증 정책 로깅 및 메트릭 (e.g., 발급 실패율, 만료 추적 등)
BFF와 인증 서비스가 깔끔하게 토큰 로직을 위임할 수 있는 구조가 완성되었고, 이 구조는 이후 Outbox, Kafka 이벤트 흐름과도 자연스럽게 연결될 수 있다.
728x90
'BindProject' 카테고리의 다른 글
| [Backend] Auth모듈 : Validator 만들기 (0) | 2025.06.18 |
|---|---|
| [기획] BFF에서의 필터링 전략과 정합성 보장 설계의 관해 (2) | 2025.06.18 |
| [Backend] BFF 구현 하기 앞서 구상 및 정리 (0) | 2025.06.17 |
| [중간 회고] 여기까지 구현 하면서... (1) | 2025.06.17 |
| [Backend] Outbox 패턴 기반 메시지 전송 시스템 설계 (0) | 2025.06.17 |