Skip to content

[BE] [REFACTOR] JWT 클래스 유지보수성 향상 - JwtUtils 단일화 및 클레임 키 상수화#252

Merged
discipline24 merged 4 commits intomainfrom
20260215_#209_토큰_관련_유지보수성_향상
Feb 28, 2026

Hidden character warning

The head ref may contain hidden characters: "20260215_#209_\ud1a0\ud070_\uad00\ub828_\uc720\uc9c0\ubcf4\uc218\uc131_\ud5a5\uc0c1"
Merged

[BE] [REFACTOR] JWT 클래스 유지보수성 향상 - JwtUtils 단일화 및 클레임 키 상수화#252
discipline24 merged 4 commits intomainfrom
20260215_#209_토큰_관련_유지보수성_향상

Conversation

@cksdid202
Copy link
Contributor

@cksdid202 cksdid202 commented Feb 27, 2026

Summary

  • JwtParser, JwtProvider 두 클래스를 JwtUtils로 단일화
  • 클레임 키("uid", "role") 상수화로 하드코딩 제거
  • @PostConstruct init(), SecretKey 초기화, validationToken() 등 중복 코드 제거
  • getUserIdFromToken() 반환 타입을 UUID로 통일
  • TokenEncryptor의 불필요한 @Configuration 어노테이션 제거
  • 테스트 코드 JwtUtils 적용 및 RefreshTokenServiceTest TokenEncryptor 목 누락 버그 수정

Test plan

  • 기존 단위 테스트 통과 확인 (AuthServiceTest, RefreshTokenServiceTest)
  • WebMvcTest 인증 흐름 확인 (TemplateControllerTest, AttendanceRoundControllerTest)
  • 로그인 → 액세스 토큰 발급 정상 동작 확인
  • 리프레시 토큰으로 액세스 토큰 재발급 정상 동작 확인
  • 로그아웃 정상 동작 확인

Summary by CodeRabbit

  • 리팩토링
    • 인증 토큰 처리 로직이 통합되어 내부 구조와 유지보수성이 개선되었습니다.
  • 새로운 기능
    • 리프레시 토큰에 암호화가 적용되어 보안이 강화되었습니다.
    • 토큰 생성·검증·인증 구성의 기능이 확장되어 인증 흐름이 보다 일관되게 동작합니다.
  • 테스트
    • 인증 관련 테스트들이 새로운 유틸리티 기반으로 갱신되었습니다.

@cksdid202 cksdid202 linked an issue Feb 27, 2026 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

Walkthrough

JwtProvider를 제거하고 JwtParser를 JwtUtils로 대체하여 토큰 생성·검증·추출 및 리프레시 암호화 기능을 JwtUtils로 통합했으며, 관련 서비스·필터·테스트들의 의존성을 JwtUtils로 교체했습니다.

Changes

Cohort / File(s) Summary
JWT 유틸리티 통합
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtProvider.java, backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtUtils.java
JwtProvider 제거. JwtParserJwtUtils로 클래스명/공개 API 변경 및 확장: 토큰 생성(createToken, createRefreshToken), 만료/클레임 추출(getExpiration, getUserIdFromToken, getRoleFromToken), 인증 객체 생성(getAuthentication), 검증(validationToken) 추가. TokenEncryptor 의존성 및 만료 설정 필드 추가.
토큰 암호화 정리
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java
불필요한 Configuration import 제거(기능 변경 없음).
필터 및 서비스 업데이트
backend/src/main/java/org/sejongisc/backend/common/auth/filter/JwtAuthenticationFilter.java, backend/src/main/java/org/sejongisc/backend/common/auth/service/AuthService.java, backend/src/main/java/org/sejongisc/backend/common/auth/service/RefreshTokenService.java
의존성 및 호출부를 JwtProvider/JwtParser에서 JwtUtils로 교체. 토큰 생성·검증·추출 호출을 새로운 JwtUtils API로 업데이트. 일부 로직에서 리프레시 토큰 암호화/복호화 흐름 연계.
테스트 파일 업데이트
backend/src/test/java/org/sejongisc/backend/attendance/controller/AttendanceRoundControllerTest.java, backend/src/test/java/org/sejongisc/backend/attendance/controller/TestSecurityConfig.java, backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java, backend/src/test/java/org/sejongisc/backend/auth/service/AuthServiceTest.java, backend/src/test/java/org/sejongisc/backend/auth/service/RefreshTokenServiceTest.java, backend/src/test/java/org/sejongisc/backend/backtest/controller/TemplateControllerTest.java
모든 테스트에서 JwtProvider/JwtParser 참조를 JwtUtils로 변경. RefreshTokenServiceTest에는 TokenEncryptor 모킹 추가 및 관련 스텁/검증 업데이트.
경량 선언 변경
backend/src/main/java/org/sejongisc/backend/common/auth/filter/JwtAuthenticationFilter.java
필드명/타입 변경: JwtParser jwtParserJwtUtils jwtUtils 및 해당 메서드 호출 교체.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant RefreshAPI as RefreshEndpoint
  participant RefreshSvc as RefreshTokenService
  participant TokenEnc as TokenEncryptor
  participant JwtUtils as JwtUtils
  participant UserRepo as UserRepository

  Client->>RefreshAPI: POST /auth/refresh (refreshToken)
  RefreshAPI->>RefreshSvc: validateAndRefresh(refreshToken)
  RefreshSvc->>TokenEnc: decrypt(refreshToken)
  TokenEnc-->>RefreshSvc: rawRefreshToken
  RefreshSvc->>JwtUtils: getUserIdFromToken(rawRefreshToken)
  JwtUtils-->>RefreshSvc: userId
  RefreshSvc->>UserRepo: findActiveUser(userId)
  UserRepo-->>RefreshSvc: User
  RefreshSvc->>JwtUtils: createToken(userId, role, email)
  JwtUtils-->>RefreshSvc: accessToken
  RefreshSvc->>JwtUtils: createRefreshToken(userId)
  JwtUtils-->>TokenEnc: (returns encrypted refresh token) 
  JwtUtils-->>RefreshSvc: refreshToken
  RefreshSvc-->>RefreshAPI: tokens
  RefreshAPI-->>Client: 200 { accessToken, refreshToken }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

BE

Poem

🐰 안녕 옛 Provider, 환영할게 Utils!
토큰 만들고 검증하며 춤추네,
리프레시 감싸 암호화까지,
서비스와 필터가 손을 잡고,
작지만 든든한 JWT 연대기! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.16% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 변경 사항의 핵심을 명확하게 요약합니다. JwtParser와 JwtProvider를 JwtUtils로 단일화하고 클레임 키를 상수화하는 유지보수성 개선 리팩토링 작업을 잘 나타냅니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 20260215_#209_토큰_관련_유지보수성_향상

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
backend/src/test/java/org/sejongisc/backend/backtest/controller/TemplateControllerTest.java (1)

85-87: JWT 인증 목 스텁은 헬퍼 메서드로 추출해도 좋겠습니다.

동일 stubbing 반복이 많아, 헬퍼로 묶으면 테스트 가독성과 변경 내성이 좋아집니다.

리팩터 예시
+    private void stubAuthenticated(UUID uid) {
+        when(jwtUtils.validationToken(TOKEN)).thenReturn(true);
+        when(jwtUtils.getAuthentication(TOKEN)).thenReturn(인증토큰(uid));
+    }
...
-        when(jwtUtils.validationToken(TOKEN)).thenReturn(true);
-        when(jwtUtils.getAuthentication(TOKEN)).thenReturn(인증토큰(uid));
+        stubAuthenticated(uid);

Also applies to: 104-106, 121-123, 152-154, 176-178

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/src/test/java/org/sejongisc/backend/backtest/controller/TemplateControllerTest.java`
around lines 85 - 87, Extract a reusable helper in TemplateControllerTest that
encapsulates the repeated JWT stubbing: create a private method (e.g.,
mockJwtFor(String token, Long uid)) that performs
when(jwtUtils.validationToken(token)).thenReturn(true) and
when(jwtUtils.getAuthentication(token)).thenReturn(인증토큰(uid)), then replace the
duplicated stubbing blocks (the occurrences around lines shown) with calls to
mockJwtFor(TOKEN, uid) to improve readability and maintainability.
backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java (2)

78-99: 테스트 설정 개선 제안.

현재 @InjectMocks로 선언된 authControllersetUp()에서 수동으로 다시 생성되어 mock 주입이 무시됩니다. @InjectMocks를 제거하거나, 수동 생성을 제거하고 Mockito의 자동 주입을 활용하는 것이 명확합니다.

♻️ 개선 제안

옵션 1: @InjectMocks 제거 (현재 수동 생성 유지 시)

-    `@InjectMocks`
-    AuthController authController;
+    AuthController authController;

옵션 2: 수동 생성 제거 (Mockito 자동 주입 활용 시)
setUp()에서 authController = new AuthController(...) 라인을 제거하고 @InjectMocks가 자동으로 mock을 주입하도록 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java`
around lines 78 - 99, The test currently re-creates authController inside
setUp(), which overrides any `@InjectMocks-driven` Mockito injection; remove the
manual instantiation line authController = new AuthController(authService,
refreshTokenService, authCookieHelper) so Mockito can inject the mocks into the
`@InjectMocks` authController, and ensure mocks are initialized (e.g., annotate
the test class with `@ExtendWith`(MockitoExtension.class) or call
MockitoAnnotations.openMocks(this) in a `@BeforeEach`) to enable automatic
injection.

192-194: OAuth 테스트의 stub 유효성 확인 필요.

jwtUtils.createToken()createRefreshToken() stub이 설정되어 있지만, AuthController@InjectMocks가 아닌 수동 생성(Line 86)으로 초기화되므로 이 mock이 실제로 주입되지 않을 수 있습니다. 테스트가 현재 통과한다면, 실제 토큰 생성 로직이 다른 서비스(예: AuthService)에서 처리되고 있을 가능성이 있습니다.

이 경우 해당 stub 코드는 제거하거나, 실제 토큰 생성이 일어나는 서비스의 mock을 통해 stubbing해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java`
around lines 192 - 194, The jwtUtils.createToken/createRefreshToken stubs are
ineffective because AuthController is constructed manually (not via
`@InjectMocks`), so jwtUtils won’t be injected; either remove these jwtUtils stubs
or update the test to stub the actual component that produces tokens (e.g., mock
AuthService and stub AuthService.createToken()/createRefreshToken() or change
test to use `@InjectMocks` for AuthController so jwtUtils is injected) — locate
references to jwtUtils.createToken, jwtUtils.createRefreshToken, AuthController
construction, and AuthService in the test and apply one of these fixes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtUtils.java`:
- Around line 134-140: Wrap the call to
customUserDetailsService.loadUserByUsername(userId) in a try/catch and normalize
any thrown runtime exceptions into a JwtException (or rethrow JwtException) so
JWT filter sees a JWT-related error (returning 401) instead of allowing a 500;
also ensure you validate userId (null/blank check) before calling
loadUserByUsername and include the original exception message as context when
creating the JwtException. Target the block around
customUserDetailsService.loadUserByUsername(userId) in JwtUtils (the userId null
check and the subsequent loadUserByUsername call).

In
`@backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java`:
- Line 66: Remove the unused JwtUtils mock and all its stubbing in the test:
delete the field declaration "@Mock JwtUtils jwtUtils" and remove any when(...)
/ doReturn(...) stubbing that references jwtUtils in AuthControllerTest (these
stubs are not injected into AuthController which is constructed in
setUp(authService, refreshTokenService, authCookieHelper)); also remove any
now-unused imports or helper references related to JwtUtils.

---

Nitpick comments:
In
`@backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java`:
- Around line 78-99: The test currently re-creates authController inside
setUp(), which overrides any `@InjectMocks-driven` Mockito injection; remove the
manual instantiation line authController = new AuthController(authService,
refreshTokenService, authCookieHelper) so Mockito can inject the mocks into the
`@InjectMocks` authController, and ensure mocks are initialized (e.g., annotate
the test class with `@ExtendWith`(MockitoExtension.class) or call
MockitoAnnotations.openMocks(this) in a `@BeforeEach`) to enable automatic
injection.
- Around line 192-194: The jwtUtils.createToken/createRefreshToken stubs are
ineffective because AuthController is constructed manually (not via
`@InjectMocks`), so jwtUtils won’t be injected; either remove these jwtUtils stubs
or update the test to stub the actual component that produces tokens (e.g., mock
AuthService and stub AuthService.createToken()/createRefreshToken() or change
test to use `@InjectMocks` for AuthController so jwtUtils is injected) — locate
references to jwtUtils.createToken, jwtUtils.createRefreshToken, AuthController
construction, and AuthService in the test and apply one of these fixes.

In
`@backend/src/test/java/org/sejongisc/backend/backtest/controller/TemplateControllerTest.java`:
- Around line 85-87: Extract a reusable helper in TemplateControllerTest that
encapsulates the repeated JWT stubbing: create a private method (e.g.,
mockJwtFor(String token, Long uid)) that performs
when(jwtUtils.validationToken(token)).thenReturn(true) and
when(jwtUtils.getAuthentication(token)).thenReturn(인증토큰(uid)), then replace the
duplicated stubbing blocks (the occurrences around lines shown) with calls to
mockJwtFor(TOKEN, uid) to improve readability and maintainability.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21bbbbc and 686e44e.

📒 Files selected for processing (12)
  • backend/src/main/java/org/sejongisc/backend/common/auth/filter/JwtAuthenticationFilter.java
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtProvider.java
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtUtils.java
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java
  • backend/src/main/java/org/sejongisc/backend/common/auth/service/AuthService.java
  • backend/src/main/java/org/sejongisc/backend/common/auth/service/RefreshTokenService.java
  • backend/src/test/java/org/sejongisc/backend/attendance/controller/AttendanceRoundControllerTest.java
  • backend/src/test/java/org/sejongisc/backend/attendance/controller/TestSecurityConfig.java
  • backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java
  • backend/src/test/java/org/sejongisc/backend/auth/service/AuthServiceTest.java
  • backend/src/test/java/org/sejongisc/backend/auth/service/RefreshTokenServiceTest.java
  • backend/src/test/java/org/sejongisc/backend/backtest/controller/TemplateControllerTest.java
💤 Files with no reviewable changes (2)
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtProvider.java
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java

AuthService authService;
@Mock UserService userService;
@Mock JwtProvider jwtProvider;
@Mock JwtUtils jwtUtils;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find AuthController class definition and check for JwtUtils dependency
find . -type f -name "AuthController.java" | head -5

Repository: SISC-IT/sisc-web

Length of output: 148


🏁 Script executed:

# Search for AuthController class definition and constructor
rg -n "class AuthController" -A 50 --type java | grep -E "(class AuthController|JwtUtils|public AuthController|private.*JwtUtils)" | head -30

Repository: SISC-IT/sisc-web

Length of output: 409


🏁 Script executed:

# Check the exact setUp method in the test file
rg -n "void setUp\|AuthController.*new" backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java -A 3 -B 1

Repository: SISC-IT/sisc-web

Length of output: 42


🏁 Script executed:

# Check if jwtUtils is used anywhere in the test file
rg -n "jwtUtils\." backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java | head -20

Repository: SISC-IT/sisc-web

Length of output: 575


🏁 Script executed:

# Read AuthController class definition to see constructor parameters
cat -n backend/src/main/java/org/sejongisc/backend/common/auth/controller/AuthController.java | head -60

Repository: SISC-IT/sisc-web

Length of output: 3319


🏁 Script executed:

# Read the test file setUp method and AuthController instantiation
sed -n '75,95p' backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java

Repository: SISC-IT/sisc-web

Length of output: 844


🏁 Script executed:

# Check the test methods containing jwtUtils stubbing to see if they use it
sed -n '180,210p' backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java

Repository: SISC-IT/sisc-web

Length of output: 1467


🏁 Script executed:

# Check other test methods with jwtUtils stubbing
sed -n '340,365p' backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java

Repository: SISC-IT/sisc-web

Length of output: 1237


🏁 Script executed:

# Check if jwtUtils is referenced anywhere else besides stubbing
rg -n "jwtUtils" backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java

Repository: SISC-IT/sisc-web

Length of output: 607


사용되지 않는 mock 필드 제거 필요.

AuthController 클래스는 JwtUtils를 의존성으로 가지지 않으며, 테스트의 setUp() 메서드(라인 86)에서 AuthController(authService, refreshTokenService, authCookieHelper)만으로 생성합니다. jwtUtils mock은 선언되고 라인 192, 194, 355, 356, 378, 379에서 stubbing되지만 실제로 주입되지 않아 사용되지 않는 코드입니다. 이 mock 필드와 모든 관련 stubbing 문들을 제거하세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java`
at line 66, Remove the unused JwtUtils mock and all its stubbing in the test:
delete the field declaration "@Mock JwtUtils jwtUtils" and remove any when(...)
/ doReturn(...) stubbing that references jwtUtils in AuthControllerTest (these
stubs are not injected into AuthController which is constructed in
setUp(authService, refreshTokenService, authCookieHelper)); also remove any
now-unused imports or helper references related to JwtUtils.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@backend/src/main/java/org/sejongisc/backend/common/auth/service/AuthService.java`:
- Around line 31-35: Remove the unused JwtParser field from AuthService to fix
the compilation error: delete the declaration "private final JwtParser
jwtParser;" (and any related unused imports) since all JWT operations in
AuthService use jwtUtils (look at usages around jwtUtils at lines where tokens
are parsed/validated). Keep the other fields (jwtUtils, RefreshTokenRepository,
ApplicationEventPublisher) and let `@RequiredArgsConstructor` generate the correct
constructor without JwtParser.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 686e44e and 697b91f.

📒 Files selected for processing (1)
  • backend/src/main/java/org/sejongisc/backend/common/auth/service/AuthService.java

Copy link
Contributor

@discipline24 discipline24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다~ 코멘트들 resolve 부탁드려요

throw new CustomException(ErrorCode.UNAUTHORIZED);
}

String accessToken = jwtUtils.createToken(user.getUserId(), user.getRole(), user.getEmail());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분은 PENDING_MEMBER 검증 이전으로 올린 이유가 궁금합니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음 그 부분은 제가 반영되기 전 코드에서 작업한 것 같아서 수정해야 할 것 같습니당

@discipline24 discipline24 merged commit 4553f52 into main Feb 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🚀 [기능개선][JWT] 토큰 관련 유지보수성 향상

2 participants