Skip to content

refactor: Java, SpringBoot 버전 업 & JJWT 버전 업#702

Open
sukangpunch wants to merge 5 commits intodevelopfrom
refactor/658-springboot-java-version-up
Open

refactor: Java, SpringBoot 버전 업 & JJWT 버전 업#702
sukangpunch wants to merge 5 commits intodevelopfrom
refactor/658-springboot-java-version-up

Conversation

@sukangpunch
Copy link
Contributor

관련 이슈

작업 내용

버전 업그레이드

❯ - Java 17 → 21

  • Spring Boot 3.1.5 → 3.5.11
  • Flyway 9.16.3 → 10.15.0
  • io.spring.dependency-management 1.1.4 → 1.1.7

JJWT 라이브러리 교체

❯ - jjwt:0.9.1 (단일 jar) → jjwt-api/impl/jackson:0.12.6 (모듈 분리)

  • javax.xml.bind:jaxb-api 의존성 제거 (jjwt 0.9.x 전용 workaround)
  • deprecated API 마이그레이션:
    • SignatureAlgorithmJwts.SIG.ES256 / Keys.hmacShaKeyFor()
    • setSigningKey/parseClaimsJws/getBodyverifyWith/parseSignedClaims/getPayload
    • Jwts.claims().setSubject() → 빌더 체이닝 방식
    • org.apache.tomcat.Base64java.util.Base64 (표준 라이브러리)

의존성 추가

❯ - commons-lang3 추가

CI/CD 및 인프라

❯ - GitHub Actions (ci.yml, dev-cd.yml, prod-cd.yml): JDK 17 → 21

  • Dockerfile: eclipse-temurin:17-jdkeclipse-temurin:21-jdk

특이 사항

리뷰 요구사항 (선택)

- SpringBoot 버전업 (-> 3.5.11)
- Java 버전업 (-> 21)
- JJWT 버전업 (-> 0.12.6)
- 기존 EnumUtils deprecate 대응을 위해 org.apache.commons:commons-lang3 의존성 추가
@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

Walkthrough

  1. Java 및 빌드 환경을 업그레이드했습니다.
  2. CI/CD 워크플로우와 Dockerfile에서 JDK 17 → JDK 21로 변경되었습니다.
  3. build.gradle에서 Java 호환성 및 Spring Boot(3.1.5 → 3.5.11), 의존성 관리, Flyway 버전이 상향 조정되었습니다.
  4. JJWT 라이브러리를 단일 모듈(0.9.1)에서 분리 모듈(0.12.6)로 교체하고 JWT 생성/검증 코드를 새로운 fluent API로 변경했습니다.
  5. 개인키/시크릿 처리에서 Apache Tomcat Base64 대신 Java 표준 Base64 및 Keys.hmacShaKeyFor 사용으로 전환했습니다.
  6. 테스트 코드의 Mockito 관련 어노테이션들을 @MockBean/@SpyBean에서 @MockitoBean/@MockitoSpyBean으로 다수 교체했습니다.
  7. 테스트 내부의 JWT 생성·만료 토큰 헬퍼들도 신규 API에 맞게 리팩터링되었습니다.
  8. commons-lang3 의존성이 새로 추가되었습니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Suggested reviewers

  • wibaek
  • whqtker
  • Hexeong
  • JAEHEE25
  • lsy1307
  • Gyuhyeok99
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 Java/Spring Boot/JJWT 버전 업그레이드라는 주요 변경사항을 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명은 템플릿의 주요 섹션(관련 이슈, 작업 내용)을 포함하고 있으며, 상세한 버전 업그레이드 및 JJWT 마이그레이션 정보를 제공합니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경사항이 #658 이슈의 Docker API 버전 호환성 문제 해결 목표를 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 Java/Spring Boot/JJWT 버전 업그레이드 및 CI/인프라 업데이트로 #658 범위 내에 있습니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/658-springboot-java-version-up
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sukangpunch sukangpunch added the 진행 중 자유롭게 merge 가능 label Mar 19, 2026
@sukangpunch sukangpunch marked this pull request as ready for review March 19, 2026 08:56
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
claude.md (1)

170-170: ⚠️ Potential issue | 🟡 Minor

2. 문서 내 Spring Boot 버전 불일치 발견 📝

Line 11에서는 Spring Boot 3.5.11로 업데이트되었지만, "기술 스택 상세" 섹션(Line 170)에서는 여전히 Spring Boot 3.1.5로 표기되어 있습니다. 문서의 일관성을 위해 수정이 필요합니다.

📝 수정 제안
 ### Core Framework

-- **Spring Boot 3.1.5**: 스프링 부트
+- **Spring Boot 3.5.11**: 스프링 부트
 - **Spring Security**: JWT 기반 인증
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@claude.md` at line 170, Update the outdated Spring Boot version text "Spring
Boot 3.1.5" in the "기술 스택 상세" section to match the document's updated version
"Spring Boot 3.5.11" (as referenced earlier in the doc), ensuring all
occurrences of "Spring Boot 3.1.5" are replaced and the version string is
consistent throughout the file.
🧹 Nitpick comments (2)
src/test/java/com/example/solidconnection/security/filter/TokenAuthenticationFilterTest.java (1)

94-101: JJWT 0.12.6 마이그레이션에 따른 테스트 코드의 deprecated API 사용 확인

프로덕션 코드에서 JJWT 0.12.6의 새 API로 이미 마이그레이션이 완료되었으나, 테스트 코드에서는 deprecated API가 여전히 사용 중입니다. 이 파일의 createTokenWithExpiration 메서드에서 다음과 같은 개선이 필요합니다:

  1. Claim 설정 메서드 변경

    • setSubject()subject()
    • setIssuedAt()issuedAt()
    • setExpiration()expiration()
  2. 서명 방식 업데이트

    • signWith(SignatureAlgorithm.HS256, secret)signWith(key, Jwts.SIG.HS256) 형태로 변경하여 SecretKey 객체 사용 필요
♻️ 적용 예시
+import io.jsonwebtoken.security.Keys;
+import javax.crypto.SecretKey;
+import java.nio.charset.StandardCharsets;

 private String createTokenWithExpiration(Date expiration) {
+    SecretKey key = Keys.hmacShaKeyFor(jwtProperties.secret().getBytes(StandardCharsets.UTF_8));
     return Jwts.builder()
-            .setSubject("1")
-            .setIssuedAt(new Date())
-            .setExpiration(expiration)
-            .signWith(SignatureAlgorithm.HS256, jwtProperties.secret())
+            .subject("1")
+            .issuedAt(new Date())
+            .expiration(expiration)
+            .signWith(key, Jwts.SIG.HS256)
             .compact();
 }

참고: 유사한 deprecated API 사용이 SignOutCheckFilterTest.java, TokenAuthenticationProviderTest.java에도 있으니 함께 검토하길 권장합니다.

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

In
`@src/test/java/com/example/solidconnection/security/filter/TokenAuthenticationFilterTest.java`
around lines 94 - 101, The test method createTokenWithExpiration uses deprecated
JJWT APIs; update it to use the new builder methods and a SecretKey: replace
setSubject/setIssuedAt/setExpiration with subject()/issuedAt()/expiration(), and
replace signWith(SignatureAlgorithm.HS256, jwtProperties.secret()) by creating a
SecretKey (e.g., Keys.hmacShaKeyFor(jwtProperties.secret().getBytes())) and
calling signWith(secretKey, Jwts.SIG.HS256); update the Jwts.builder() call
accordingly in createTokenWithExpiration and apply the same pattern to
SignOutCheckFilterTest and TokenAuthenticationProviderTest.
src/main/java/com/example/solidconnection/auth/token/JwtTokenProvider.java (1)

48-50: Keys.hmacShaKeyFor() 사용에 대한 참고 사항

Keys.hmacShaKeyFor()는 키 길이에 따라 HMAC 알고리즘을 자동으로 선택합니다:

  • 32+ 바이트 → HS256
  • 48+ 바이트 → HS384
  • 64+ 바이트 → HS512

현재 설정된 JWT 시크릿이 최소 32바이트 이상인지 확인해주세요. 테스트 환경의 시크릿 "1234567-1234-1234-1234-12345678901"은 34바이트로 HS256이 적용됩니다.

💡 보안 강화를 위한 권장 사항 (선택적)

프로덕션 환경에서는 더 긴 시크릿 키(64+ 바이트)를 사용하여 HS512 알고리즘을 적용하는 것을 권장합니다. 이는 현재 기능에 영향을 주지 않으며 향후 보안 강화를 위한 제안입니다.

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

In `@src/main/java/com/example/solidconnection/auth/token/JwtTokenProvider.java`
around lines 48 - 50, The getSigningKey() method uses
Keys.hmacShaKeyFor(jwtProperties.secret().getBytes(...)) which selects
HS256/384/512 based on secret length; ensure jwtProperties.secret() is at least
32 bytes (preferably 64+ for HS512 in production) by validating its byte length
in JwtTokenProvider (e.g., in getSigningKey() or during JwtTokenProvider
initialization) and throw a clear exception or log an error if the secret is too
short so the application fails fast instead of producing a weaker-than-expected
algorithm; reference getSigningKey(), Keys.hmacShaKeyFor, and
jwtProperties.secret() when adding the validation or documentation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@build.gradle`:
- Around line 46-48: Three test files still use the old JJWT builder/signing API
and must be updated to the 0.12.6 API: in TokenAuthenticationProviderTest,
TokenAuthenticationFilterTest, and SignOutCheckFilterTest replace uses of
signWith(SignatureAlgorithm.HS256, key) with signWith(secretKey) (ensure the
test creates a SecretKey instance as used in production), and rename JWT builder
setters setSubject(...), setIssuedAt(...), setExpiration(...) to the new fluent
names subject(...), issuedAt(...), expiration(...); update imports/usages
accordingly so the tests compile against the jjwt-api 0.12.6 methods.

---

Outside diff comments:
In `@claude.md`:
- Line 170: Update the outdated Spring Boot version text "Spring Boot 3.1.5" in
the "기술 스택 상세" section to match the document's updated version "Spring Boot
3.5.11" (as referenced earlier in the doc), ensuring all occurrences of "Spring
Boot 3.1.5" are replaced and the version string is consistent throughout the
file.

---

Nitpick comments:
In `@src/main/java/com/example/solidconnection/auth/token/JwtTokenProvider.java`:
- Around line 48-50: The getSigningKey() method uses
Keys.hmacShaKeyFor(jwtProperties.secret().getBytes(...)) which selects
HS256/384/512 based on secret length; ensure jwtProperties.secret() is at least
32 bytes (preferably 64+ for HS512 in production) by validating its byte length
in JwtTokenProvider (e.g., in getSigningKey() or during JwtTokenProvider
initialization) and throw a clear exception or log an error if the secret is too
short so the application fails fast instead of producing a weaker-than-expected
algorithm; reference getSigningKey(), Keys.hmacShaKeyFor, and
jwtProperties.secret() when adding the validation or documentation.

In
`@src/test/java/com/example/solidconnection/security/filter/TokenAuthenticationFilterTest.java`:
- Around line 94-101: The test method createTokenWithExpiration uses deprecated
JJWT APIs; update it to use the new builder methods and a SecretKey: replace
setSubject/setIssuedAt/setExpiration with subject()/issuedAt()/expiration(), and
replace signWith(SignatureAlgorithm.HS256, jwtProperties.secret()) by creating a
SecretKey (e.g., Keys.hmacShaKeyFor(jwtProperties.secret().getBytes())) and
calling signWith(secretKey, Jwts.SIG.HS256); update the Jwts.builder() call
accordingly in createTokenWithExpiration and apply the same pattern to
SignOutCheckFilterTest and TokenAuthenticationProviderTest.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5ad9c635-8f52-4383-b53a-b009b66270bf

📥 Commits

Reviewing files that changed from the base of the PR and between b58d893 and fe9287b.

📒 Files selected for processing (22)
  • .github/workflows/ci.yml
  • .github/workflows/dev-cd.yml
  • .github/workflows/prod-cd.yml
  • Dockerfile
  • build.gradle
  • claude.md
  • src/main/java/com/example/solidconnection/auth/client/AppleOAuthClient.java
  • src/main/java/com/example/solidconnection/auth/client/AppleOAuthClientSecretProvider.java
  • src/main/java/com/example/solidconnection/auth/token/JwtTokenProvider.java
  • src/test/java/com/example/solidconnection/admin/service/AdminHostUniversityServiceTest.java
  • src/test/java/com/example/solidconnection/auth/controller/RefreshTokenCookieManagerTest.java
  • src/test/java/com/example/solidconnection/auth/service/JwtTokenProviderTest.java
  • src/test/java/com/example/solidconnection/auth/service/oauth/OAuthServiceTest.java
  • src/test/java/com/example/solidconnection/auth/service/signup/SignUpTokenProviderTest.java
  • src/test/java/com/example/solidconnection/chat/service/ChatServiceTest.java
  • src/test/java/com/example/solidconnection/community/post/service/PostCommandServiceTest.java
  • src/test/java/com/example/solidconnection/mentor/service/MentorApplicationServiceTest.java
  • src/test/java/com/example/solidconnection/news/service/NewsCommandServiceTest.java
  • src/test/java/com/example/solidconnection/score/service/ScoreServiceTest.java
  • src/test/java/com/example/solidconnection/security/filter/TokenAuthenticationFilterTest.java
  • src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java
  • src/test/java/com/example/solidconnection/university/service/UnivApplyInfoQueryServiceTest.java

Comment on lines +46 to +48
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 이전 JJWT API를 사용하는 파일 검색
echo "=== SignatureAlgorithm 사용 파일 ==="
rg -n "SignatureAlgorithm" --type java

echo ""
echo "=== setSubject/setIssuedAt/setExpiration 사용 파일 ==="
rg -n "setSubject|setIssuedAt|setExpiration" --type java -g '!build/**'

Repository: solid-connection/solid-connect-server

Length of output: 3760


🏁 Script executed:

# Verify the actual migration path for JJWT 0.12.6 API
rg -A 2 "signWith\(" src/test/java/com/example/solidconnection/security/ --type java | head -20

Repository: solid-connection/solid-connect-server

Length of output: 2106


JJWT 0.12.6 마이그레이션이 세 개의 테스트 파일에서 미완료되었습니다.

build.gradle 의존성 업그레이드는 올바르게 수행되었으나, 다음 세 개의 테스트 파일이 여전히 이전 JJWT API를 사용하고 있어 컴파일 오류가 발생할 것입니다:

  1. src/test/java/com/example/solidconnection/security/authentication/TokenAuthenticationProviderTest.java
  2. src/test/java/com/example/solidconnection/security/filter/TokenAuthenticationFilterTest.java
  3. src/test/java/com/example/solidconnection/security/filter/SignOutCheckFilterTest.java

각 파일마다 다음과 같은 API 변경이 필요합니다:

- `signWith(SignatureAlgorithm.HS256, key)` → `signWith(secretKey)`
- `setSubject()` → `subject()`
- `setIssuedAt()` → `issuedAt()`
- `setExpiration()` → `expiration()`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@build.gradle` around lines 46 - 48, Three test files still use the old JJWT
builder/signing API and must be updated to the 0.12.6 API: in
TokenAuthenticationProviderTest, TokenAuthenticationFilterTest, and
SignOutCheckFilterTest replace uses of signWith(SignatureAlgorithm.HS256, key)
with signWith(secretKey) (ensure the test creates a SecretKey instance as used
in production), and rename JWT builder setters setSubject(...),
setIssuedAt(...), setExpiration(...) to the new fluent names subject(...),
issuedAt(...), expiration(...); update imports/usages accordingly so the tests
compile against the jjwt-api 0.12.6 methods.

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
`@src/test/java/com/example/solidconnection/security/authentication/TokenAuthenticationProviderTest.java`:
- Line 121: 테스트의 토큰 만료 시간이 너무 짧아 간헐적 실패를 유발하므로 TokenAuthenticationProviderTest에서
Date(System.currentTimeMillis() + 1000)로 생성한 만료 시간을 늘리세요: expiration(...)에 더 긴
TTL(예: 몇 만 밀리초 또는 Duration/TimeUnit을 사용해 30s~5min 범위)으로 변경하거나 상수(EXPIRATION_MS)로
추출해 안정적인 값으로 설정하고, 필요하면 테스트 전용 고정 Clock/시간 제어를 도입해 토큰 만료 타이밍을 안정화하세요; 변경 대상 식별자:
TokenAuthenticationProviderTest, expiration(...) 호출,
Date(System.currentTimeMillis() + ...).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d6c228ef-fa3b-43a8-9364-6a01490514b6

📥 Commits

Reviewing files that changed from the base of the PR and between fe9287b and d03f945.

📒 Files selected for processing (3)
  • src/test/java/com/example/solidconnection/security/authentication/TokenAuthenticationProviderTest.java
  • src/test/java/com/example/solidconnection/security/filter/SignOutCheckFilterTest.java
  • src/test/java/com/example/solidconnection/security/filter/TokenAuthenticationFilterTest.java
✅ Files skipped from review due to trivial changes (1)
  • src/test/java/com/example/solidconnection/security/filter/SignOutCheckFilterTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/com/example/solidconnection/security/filter/TokenAuthenticationFilterTest.java

.signWith(SignatureAlgorithm.HS256, jwtProperties.secret())
.subject(String.valueOf(id))
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + 1000))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

2) 유효 토큰 TTL이 1초라서 테스트가 간헐적으로 깨질 수 있습니다.

Line 121의 + 1000은 통합 테스트 환경에서 너무 짧습니다. 인증 처리 지연 시 만료되어, 정상 인증/없는 사용자 케이스가 INVALID_TOKEN으로 바뀌어 CI flaky를 유발할 수 있습니다.

🔧 제안 수정안
 class TokenAuthenticationProviderTest {
+    private static final long VALID_TOKEN_TTL_MS = 5 * 60 * 1000L;
+
@@
     private String createValidToken(long id) {
         return Jwts.builder()
                 .subject(String.valueOf(id))
                 .issuedAt(new Date())
-                .expiration(new Date(System.currentTimeMillis() + 1000))
+                .expiration(new Date(System.currentTimeMillis() + VALID_TOKEN_TTL_MS))
                 .signWith(Keys.hmacShaKeyFor(jwtProperties.secret().getBytes(StandardCharsets.UTF_8)))
                 .compact();
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.expiration(new Date(System.currentTimeMillis() + 1000))
private String createValidToken(long id) {
return Jwts.builder()
.subject(String.valueOf(id))
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + VALID_TOKEN_TTL_MS))
.signWith(Keys.hmacShaKeyFor(jwtProperties.secret().getBytes(StandardCharsets.UTF_8)))
.compact();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/com/example/solidconnection/security/authentication/TokenAuthenticationProviderTest.java`
at line 121, 테스트의 토큰 만료 시간이 너무 짧아 간헐적 실패를 유발하므로
TokenAuthenticationProviderTest에서 Date(System.currentTimeMillis() + 1000)로 생성한
만료 시간을 늘리세요: expiration(...)에 더 긴 TTL(예: 몇 만 밀리초 또는 Duration/TimeUnit을 사용해
30s~5min 범위)으로 변경하거나 상수(EXPIRATION_MS)로 추출해 안정적인 값으로 설정하고, 필요하면 테스트 전용 고정
Clock/시간 제어를 도입해 토큰 만료 타이밍을 안정화하세요; 변경 대상 식별자: TokenAuthenticationProviderTest,
expiration(...) 호출, Date(System.currentTimeMillis() + ...).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

리팩터링 진행 중 자유롭게 merge 가능

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: spring boot 버전 업

1 participant