Skip to content

Conversation

@KimGyeongLock
Copy link
Owner

.

@gemini-code-assist
Copy link

Summary of Changes

Hello @KimGyeongLock, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 Pull Request는 프로젝트의 CI/CD 파이프라인을 강화하고 코드 품질을 향상시키기 위한 광범위한 변경 사항을 포함합니다. 주요 서비스 로직을 리팩토링하여 아키텍처를 개선하고, Docker 컨테이너의 보안을 강화하며, 최신 Java 기능을 활용하여 코드의 가독성과 유지보수성을 높였습니다. 또한, 광범위한 테스트 코드 추가를 통해 변경 사항의 안정성을 확보했습니다.

Highlights

  • CI/CD 및 코드 품질 도구 통합: SonarCloud, JaCoCo, SpotBugs, Gitleaks와 같은 CI/CD 및 코드 품질 도구들을 Gradle 빌드 시스템에 통합했습니다. 이를 통해 코드 커버리지 측정, 정적 분석, 시크릿 노출 방지 등 개발 프로세스의 견고함을 강화합니다.
  • 도커라이징 보안 강화 및 최적화: 백엔드 및 프론트엔드 Dockerfile에 non-root 사용자(appuser, 101)를 추가하여 컨테이너 보안을 강화했습니다. 또한, 프론트엔드 Dockerfile의 Nginx 기본 이미지를 nginxinc/nginx-unprivileged로 변경하고 포트를 8080으로 통일했습니다.
  • 서비스 로직 분리 및 리팩토링 (CQRS 패턴 도입): CourseService와 SummaryService의 쓰기(Command) 로직을 각각 CourseCommandService와 SummaryCommandService로 분리하여 CQRS(Command Query Responsibility Segregation) 패턴을 도입했습니다. 이는 서비스의 책임 분리를 명확히 하고 유지보수성을 높입니다.
  • Java Record 타입 도입 및 DTO 개선: 다수의 DTO(Data Transfer Object)들을 Java Record 타입으로 전환하여 불변성을 확보하고 코드의 간결성을 높였습니다. 또한, captureAI 패키지명을 capture_ai로 변경하여 일관성을 유지했습니다.
  • 보안 및 예외 처리 개선: SHA-256 해싱 실패 시 CryptoException을 발생시키도록 변경하고, JWT 토큰 공급자의 초기화 로직을 @PostConstruct로 옮겼습니다. 또한, SecurityConfig의 화이트리스트를 List<String>으로 변경하고 JWT 필터의 예외 처리 로직을 간소화했습니다.
  • 테스트 코드 추가 및 개선: 새롭게 추가되거나 리팩토링된 서비스 및 컨트롤러 로직에 대한 단위 테스트 및 통합 테스트를 대거 추가하여 코드의 신뢰성을 높였습니다. 기존의 Authorization 테스트는 제거되었습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/sonarcloud.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

안녕하세요. SonarCloud CI 연동 및 코드 품질 개선을 위한 대규모 리팩토링 작업을 리뷰했습니다. 전반적으로 매우 훌륭한 변경 사항들입니다.

주요 변경 내용

  • Jacoco, SonarQube, SpotBugs, Gitleaks 등 정적 분석 및 CI 도구 연동
  • Dockerfile에서 non-root 사용자 설정을 통한 보안 강화
  • CQRS 패턴을 적용하여 CourseService, SummaryService를 Command와 Query로 분리
  • record 타입을 적극적으로 사용하여 DTO를 리팩토링하고, 불변성(immutability) 강화
  • 다수의 유닛 테스트를 추가하여 코드 안정성 대폭 향상

코드 품질과 안정성을 높이려는 노력이 돋보이는 훌륭한 PR입니다.

주의: CRITICAL BUG
리뷰 코멘트 제약으로 인해 파일에 직접 코멘트를 남기지 못했지만, 심각한 버그가 있습니다.
JwtAuthenticationFilter에서 principal을 Long 타입으로 설정하도록 변경되었으나, SecurityUtil.getCurrentStudentId() 메소드는 여전히 principal을 String으로 캐스팅하려고 시도합니다. 이로 인해 @CheckStudentAccess를 사용하는 모든 API에서 ClassCastException이 발생하여 실패하게 됩니다. 반드시 SecurityUtil을 수정해야 합니다.

그 외 몇 가지 추가 개선 사항에 대한 의견을 코멘트로 남겼습니다.

  • build.gradle의 일부 설정 정리 및 외부화
  • gitleaks 리포트 파일의 버전 관리 제외
  • SummaryCalculator의 잠재적인 NullPointerException 방어 코드 추가

확인 부탁드립니다.

Comment on lines +1 to +107
[
{
"RuleID": "generic-api-key",
"Description": "Detected a Generic API Key, potentially exposing access to various services and sensitive operations.",
"StartLine": 13,
"EndLine": 13,
"StartColumn": 11,
"EndColumn": 96,
"Match": "app.crypto.email-key=REDACTED\"",
"Secret": "REDACTED",
"File": "src/test/java/com/example/gradu/global/crypto/AesGcmUtilTest.java",
"SymlinkFile": "",
"Commit": "1ced46de276aa0a46c514937c68dfb8769bb392d",
"Link": "https://github.com/KimGyeongLock/GRADU/blob/1ced46de276aa0a46c514937c68dfb8769bb392d/src/test/java/com/example/gradu/global/crypto/AesGcmUtilTest.java#L13",
"Entropy": 4,
"Author": "LocKey",
"Email": "rudfhr97@naver.com",
"Date": "2025-12-21T18:49:33Z",
"Message": "test: 테스트 코드 커버리지 80% 달성",
"Tags": [],
"Fingerprint": "1ced46de276aa0a46c514937c68dfb8769bb392d:src/test/java/com/example/gradu/global/crypto/AesGcmUtilTest.java:generic-api-key:13"
},
{
"RuleID": "jwt",
"Description": "Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.",
"StartLine": 83,
"EndLine": 83,
"StartColumn": 32,
"EndColumn": 167,
"Match": "REDACTED\"",
"Secret": "REDACTED",
"File": "src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java",
"SymlinkFile": "",
"Commit": "80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0",
"Link": "https://github.com/KimGyeongLock/GRADU/blob/80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0/src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java#L83",
"Entropy": 5.384508,
"Author": "LocKey",
"Email": "rudfhr97@naver.com",
"Date": "2025-08-05T14:39:29Z",
"Message": "feat: Redis를 이용한 Refresh Token 관리 구현\n\n- Redis 설정 추가\n - RedisConfig 클래스 생성\n - RedisTemplate Bean 구성 및 Serializer 설정\n\n- RefreshToken 저장소 구현\n - RefreshTokenStore 클래스 추가\n - 토큰 저장, 검증, 삭제 기능 구현\n - 토큰 만료 시간 자동 관리\n\n- 로그아웃 기능 테스트 코드 작성\n - AuthControllerTest에 로그아웃 성공 테스트 추가\n - Redis 기반 토큰 저장소 연동 테스트\n\nResolves: #5",
"Tags": [],
"Fingerprint": "80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0:src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java:jwt:83"
},
{
"RuleID": "jwt",
"Description": "Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.",
"StartLine": 84,
"EndLine": 84,
"StartColumn": 33,
"EndColumn": 168,
"Match": "REDACTED\"",
"Secret": "REDACTED",
"File": "src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java",
"SymlinkFile": "",
"Commit": "80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0",
"Link": "https://github.com/KimGyeongLock/GRADU/blob/80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0/src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java#L84",
"Entropy": 5.434736,
"Author": "LocKey",
"Email": "rudfhr97@naver.com",
"Date": "2025-08-05T14:39:29Z",
"Message": "feat: Redis를 이용한 Refresh Token 관리 구현\n\n- Redis 설정 추가\n - RedisConfig 클래스 생성\n - RedisTemplate Bean 구성 및 Serializer 설정\n\n- RefreshToken 저장소 구현\n - RefreshTokenStore 클래스 추가\n - 토큰 저장, 검증, 삭제 기능 구현\n - 토큰 만료 시간 자동 관리\n\n- 로그아웃 기능 테스트 코드 작성\n - AuthControllerTest에 로그아웃 성공 테스트 추가\n - Redis 기반 토큰 저장소 연동 테스트\n\nResolves: #5",
"Tags": [],
"Fingerprint": "80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0:src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java:jwt:84"
},
{
"RuleID": "jwt",
"Description": "Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.",
"StartLine": 100,
"EndLine": 100,
"StartColumn": 32,
"EndColumn": 167,
"Match": "REDACTED\"",
"Secret": "REDACTED",
"File": "src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java",
"SymlinkFile": "",
"Commit": "80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0",
"Link": "https://github.com/KimGyeongLock/GRADU/blob/80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0/src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java#L100",
"Entropy": 5.384508,
"Author": "LocKey",
"Email": "rudfhr97@naver.com",
"Date": "2025-08-05T14:39:29Z",
"Message": "feat: Redis를 이용한 Refresh Token 관리 구현\n\n- Redis 설정 추가\n - RedisConfig 클래스 생성\n - RedisTemplate Bean 구성 및 Serializer 설정\n\n- RefreshToken 저장소 구현\n - RefreshTokenStore 클래스 추가\n - 토큰 저장, 검증, 삭제 기능 구현\n - 토큰 만료 시간 자동 관리\n\n- 로그아웃 기능 테스트 코드 작성\n - AuthControllerTest에 로그아웃 성공 테스트 추가\n - Redis 기반 토큰 저장소 연동 테스트\n\nResolves: #5",
"Tags": [],
"Fingerprint": "80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0:src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java:jwt:100"
},
{
"RuleID": "jwt",
"Description": "Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.",
"StartLine": 101,
"EndLine": 101,
"StartColumn": 33,
"EndColumn": 168,
"Match": "REDACTED\"",
"Secret": "REDACTED",
"File": "src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java",
"SymlinkFile": "",
"Commit": "80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0",
"Link": "https://github.com/KimGyeongLock/GRADU/blob/80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0/src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java#L101",
"Entropy": 5.434736,
"Author": "LocKey",
"Email": "rudfhr97@naver.com",
"Date": "2025-08-05T14:39:29Z",
"Message": "feat: Redis를 이용한 Refresh Token 관리 구현\n\n- Redis 설정 추가\n - RedisConfig 클래스 생성\n - RedisTemplate Bean 구성 및 Serializer 설정\n\n- RefreshToken 저장소 구현\n - RefreshTokenStore 클래스 추가\n - 토큰 저장, 검증, 삭제 기능 구현\n - 토큰 만료 시간 자동 관리\n\n- 로그아웃 기능 테스트 코드 작성\n - AuthControllerTest에 로그아웃 성공 테스트 추가\n - Redis 기반 토큰 저장소 연동 테스트\n\nResolves: #5",
"Tags": [],
"Fingerprint": "80a81972cc8f9ff1348f01ed7bd0bccdcc67a7b0:src/test/java/com/example/gradu/domain/student/controller/AuthControllerTest.java:jwt:101"
}
]

Choose a reason for hiding this comment

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

high

gitleaks-report.json과 같은 리포트 파일은 버전 관리에 포함되어서는 안 됩니다. 민감한 정보가 포함될 수 있으며(현재는 REDACTED 처리되었지만), 레포지토리를 불필요하게 무겁게 만듭니다. 이 파일을 삭제하고 .gitignore에 추가하여 앞으로 커밋되지 않도록 하는 것이 좋습니다.

for (Course c : courses) {
if (!isPassGrade(c.getGrade())) continue;

if (!RowAssembler.isPassGrade(c.getGrade())) continue;

Choose a reason for hiding this comment

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

high

c.getGrade()null일 수 있습니다. 현재 RowAssembler.isPassGrade 메소드는 null을 인자로 받으면 NullPointerException을 발생시킬 수 있습니다. isPassGrade를 호출하기 전에 null 체크를 추가하는 것이 안전합니다.

Suggested change
if (!RowAssembler.isPassGrade(c.getGrade())) continue;
if (c.getGrade() == null || !RowAssembler.isPassGrade(c.getGrade())) continue;

properties {
property "sonar.projectKey", "gradu-backend"
property "sonar.projectName", "GRADU Backend"
property "sonar.host.url", "http://localhost:9000"

Choose a reason for hiding this comment

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

medium

SonarQube 호스트 URL이 http://localhost:9000으로 하드코딩되어 있습니다. 로컬 환경에서는 문제가 없지만, CI/CD 환경이나 다른 개발자 환경에서는 주소가 달라질 수 있습니다. gradle.properties나 환경 변수를 통해 주입받도록 변경하는 것을 권장합니다. 예를 들어, System.getenv("SONAR_HOST_URL") ?: "http://localhost:9000" 와 같이 설정할 수 있습니다.

@@ -1,4 +1,5 @@
.env
.gitleaks.toml

Choose a reason for hiding this comment

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

low

gitleaks-report.json과 같은 리포트 파일이나 spotbugs, jacoco가 생성하는 빌드 산출물들이 버전 관리에 포함되지 않도록 .gitignore에 관련 패턴을 추가하는 것을 권장합니다.

.gitleaks.toml
gitleaks-report.json
build/reports/

Comment on lines +96 to +98
html.required = true
xml.required = true
}

Choose a reason for hiding this comment

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

low

SpotBugs XML 리포트 생성 설정이 두 군데에서 다르게 지정되어 혼란을 줄 수 있습니다. spotbugsMain 블록(96-98라인)에서는 htmlxml 리포트를 모두 활성화했지만, tasks.withType(SpotBugsTask) 블록(103-104라인)에서는 xml을 다시 비활성화하고 있습니다. 후자의 설정이 적용되겠지만, 일관성을 위해 spotbugsMain 블록에서 xml.required = true 설정을 제거하거나 false로 통일하는 것이 좋습니다.

@sonarqubecloud
Copy link

@KimGyeongLock KimGyeongLock merged commit 5a4a4a6 into main Dec 21, 2025
2 checks 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.

2 participants