Skip to content

Conversation

@oxdjww
Copy link
Contributor

@oxdjww oxdjww commented May 3, 2025

Summary by CodeRabbit

  • 신규 기능

    • CI/CD 자동화 및 블루-그린 배포를 위한 GitHub Actions 워크플로우가 추가되었습니다.
    • 프로덕션 환경용 docker-compose 및 Spring Boot 설정 파일이 추가되었습니다.
  • 버그 수정

    • 로그아웃 동작이 개선되어, 토큰이 올바르게 무효화되고 로그아웃 시 응답 메시지가 명확하게 반환됩니다.
  • 리팩터

    • JWT 및 Redis 기반 토큰 관리 로직이 개선되어 보안성과 일관성이 향상되었습니다.
    • Kafka 관련 코드, 의존성, 설정 파일, 테스트가 모두 제거되었습니다.
  • 문서

    • API 문서의 로그아웃 설명이 실제 동작에 맞게 수정되었습니다.
  • 환경설정/작업

    • Kafka와 관련된 모든 설정 및 서비스가 docker-compose와 환경설정 파일에서 삭제되었습니다.

@oxdjww oxdjww added the feature New feature or request label May 3, 2025
@oxdjww oxdjww self-assigned this May 3, 2025
@oxdjww oxdjww linked an issue May 3, 2025 that may be closed by this pull request
2 tasks
@coderabbitai
Copy link

coderabbitai bot commented May 3, 2025

Warning

Rate limit exceeded

@oxdjww has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 6 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 71758e3 and 3e67534.

📒 Files selected for processing (6)
  • backend/src/main/resources/application.yml (1 hunks)
  • backend/src/test/java/com/focussu/backend/BackendApplicationTests.java (1 hunks)
  • backend/src/test/java/com/focussu/backend/member/controller/MemberControllerTest.java (1 hunks)
  • backend/src/test/java/com/focussu/backend/member/service/MemberCommandServiceTest.java (1 hunks)
  • backend/src/test/java/com/focussu/backend/studyroom/controller/StudyRoomControllerTest.java (1 hunks)
  • backend/src/test/resources/application-test.yml (1 hunks)

Walkthrough

이번 변경 사항은 Kafka 관련 기능을 전면적으로 제거하고, JWT 기반 인증 및 토큰 관리를 개선하며, CI/CD 자동화 파이프라인을 도입하는 데 중점을 두었습니다. Kafka 의존성, 설정, 리스너, 테스트가 모두 삭제되었고, 토큰 저장 구조 및 로그아웃 처리가 리팩토링되었습니다. 또한, Blue-Green 배포 전략을 적용한 EC2 자동 배포(CD)와 PR 기반의 CI 워크플로우가 GitHub Actions로 추가되었습니다. 배포 및 운영 환경을 위한 Docker Compose와 Spring 프로파일 설정 파일도 새롭게 도입되었습니다.

Changes

파일/경로 그룹 변경 요약
.github/workflows/ci.yml, .github/workflows/cd.yml CI(빌드/테스트), CD(EC2 Blue-Green 배포)용 GitHub Actions 워크플로우 신규 추가
backend/build.gradle, backend/src/main/resources/application-local.yml, backend/src/main/resources/application-test.yml, backend/src/main/java/com/focussu/backend/config/KafkaConfig.java, backend/src/main/java/com/focussu/backend/studyparticipation/kafka/KafkaParticipationListener.java, backend/src/test/java/com/focussu/backend/studyparticipation/StudyParticipationKafkaIntegrationTest.java, docker-compose.yml Kafka 관련 의존성, 설정, 리스너, 테스트, docker-compose 서비스 전면 삭제
backend/src/main/java/com/focussu/backend/auth/filter/LoginFilter.java JWT 토큰 저장 시 기존 토큰 삭제 후 신규 저장, 로그 추가, @slf4j 어노테이션 적용
backend/src/main/java/com/focussu/backend/auth/filter/LogoutFilter.java LogoutFilter 클래스 전체 삭제
backend/src/main/java/com/focussu/backend/auth/service/TokenService.java Redis 키 구조를 username 기반으로 변경, 토큰 역방향 조회 및 삭제 메소드 추가, Javadoc 추가
backend/src/main/java/com/focussu/backend/config/SecurityConfig.java LogoutFilter 제거, LogoutHandler 기반 로그아웃 처리로 리팩토링, JWT 인증 필터 위치 변경
backend/src/main/java/com/focussu/backend/common/BaseEntity.java @Setter 어노테이션 추가
backend/src/main/java/com/focussu/backend/config/RedisConfig.java RedisTemplate에 StringRedisSerializer 명시적 적용 및 afterPropertiesSet 호출 추가
backend/src/main/java/com/focussu/backend/auth/controller/AuthDocumentController.java 로그아웃 API 설명 문구 수정
backend/src/main/resources/application-prod.yml, backend/src/main/resources/application.yml 운영 및 기본 환경 Spring 설정 파일 신규 추가
backend/src/main/resources/http/auth.http 로그아웃 요청 Authorization 헤더를 고정 토큰 값으로 변경
docker-compose-prod.yml Blue-Green 배포 구조의 운영용 docker-compose 파일 신규 추가

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Nginx
    participant Backend-Blue
    participant Backend-Green
    participant EC2/DeployScript
    participant ECR

    User->>Nginx: HTTP 요청
    Nginx->>Backend-Blue: 프록시(초기 Live 서비스)
    EC2/DeployScript->>ECR: 새 이미지 Pull (커밋 SHA 태그)
    EC2/DeployScript->>Backend-Green: docker-compose로 새 컨테이너 기동
    EC2/DeployScript->>Backend-Green: Health Check (Actuator)
    alt Health Check 성공
        EC2/DeployScript->>Nginx: upstream을 Backend-Green으로 전환
        EC2/DeployScript->>Backend-Blue: 기존 컨테이너 중지
    else 실패
        EC2/DeployScript->>Backend-Green: 새 컨테이너 중지 및 롤백
    end
    User->>Nginx: HTTP 요청
    Nginx->>Backend-Green: 프록시(전환 후 서비스)
Loading

Poem

🐇
깡총깡총 토끼가 춤을 춰요,
Kafka는 안녕, JWT로 새로워요!
파란 초원, 초록 들판, Blue-Green 배포,
CI/CD 자동화, 토큰도 똑똑!
오늘도 코드밭에 평화가 가득,
토끼는 행복해요, 변화가 참 반갑군요! 🌱
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 8

🧹 Nitpick comments (13)
backend/src/main/java/com/focussu/backend/config/RedisConfig.java (1)

17-21: StringRedisSerializer 인스턴스 재사용 제안

new StringRedisSerializer()를 네 번 호출해도 기능은 동일하지만, 불필요한 객체 생성을 피하면 메모리·GC 비용을 줄이고 가독성도 좋아집니다.

-        template.setKeySerializer(new StringRedisSerializer());
-        template.setValueSerializer(new StringRedisSerializer());
-        template.setHashKeySerializer(new StringRedisSerializer());
-        template.setHashValueSerializer(new StringRedisSerializer());
+        StringRedisSerializer serializer = new StringRedisSerializer();
+        template.setKeySerializer(serializer);
+        template.setValueSerializer(serializer);
+        template.setHashKeySerializer(serializer);
+        template.setHashValueSerializer(serializer);
backend/src/main/java/com/focussu/backend/auth/filter/LoginFilter.java (1)

70-72: 중복 DB 호출 제거로 성능 최적화

auth.getPrincipal()에 이미 UserDetails가 들어있으므로 다시 userDetailsService.loadUserByUsername(username)를 호출할 필요가 없습니다.

-        String username = auth.getName();
-        UserDetails user = userDetailsService.loadUserByUsername(username);
+        UserDetails user = (UserDetails) auth.getPrincipal();
backend/src/main/resources/application-prod.yml (1)

37-38: 마지막 줄 개행 문자 누락

몇몇 리눅스 툴(yaml-lint 포함)에서 경고가 발생합니다. EOF에 개행을 추가해 주세요.

🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 38-38: no new line character at the end of file

(new-line-at-end-of-file)

backend/src/main/java/com/focussu/backend/config/SecurityConfig.java (1)

118-124: ObjectMapper 매번 생성 대신 Bean 재사용 권장

로그아웃 요청마다 new ObjectMapper()가 실행되어 불필요한 객체가 증가합니다.
이미 프로젝트에 주입 가능한 ObjectMapper Bean이 있을 가능성이 높으니 재사용을 권장합니다.

-                            ObjectMapper mapper = new ObjectMapper();
-                            mapper.writeValue(response.getWriter(), Map.of("message", "로그아웃 성공"));
+                            objectMapper.writeValue(response.getWriter(), Map.of("message", "로그아웃 성공"));

필드 주입:

private final ObjectMapper objectMapper;
.github/workflows/ci.yml (2)

28-31: working-directory 누락으로 루트 디렉터리에 Gradle 캐시가 생성될 수 있습니다

setup-gradle 단계도 working-directory: ./backend를 명시해 주면 캐시 키 충돌 및 잘못된 wrapper 탐색을 방지할 수 있습니다.

-      - name: Setup Gradle
-        uses: gradle/actions/setup-gradle@v3
+      - name: Setup Gradle
+        uses: gradle/actions/setup-gradle@v3
+        with:
+          gradle-home-cache-includes: |
+            ~/.gradle/caches
+        working-directory: ./backend

39-41: 빌드 명령에 --no-daemon 옵션 추가 권장

CI 환경에서는 Gradle 데몬이 단계 간 재사용되지 않아 메모리만 추가로 소비하게 됩니다. --no-daemon을 지정하면 빌드 컨테이너의 메모리 사용량을 줄일 수 있습니다.

-        run: ./gradlew build # 'build' 태스크는 기본적으로 'test' 태스크를 포함하여 실행합니다.
+        run: ./gradlew build --no-daemon
docker-compose-prod.yml (2)

21-27: healthcheck 주기가 짧아 배포 스크립트와 충돌 가능성

start_period: 60s, interval: 15s, retries: 5 설정은 대체로 무난하지만, Spring Boot 기동 시간이 길어질 경우 첫 healthcheck 실패로 unhealthy 상태가 찍힐 수 있습니다. 배포 스크립트에서 동일한 60 초 내 응답을 기대한다면 괜찮지만, 그렇지 않다면 start_period를 90 ~ 120 초로 늘려 두는 편이 안전합니다.


58-63: 호스트 경로 하드코딩으로 EC2 디렉터리 구조 변경 시 위험

/home/ubuntu/app/... 절대경로를 볼륨으로 마운트하고 있습니다. 운영 서버 사용자 또는 경로가 바뀌면 컨테이너가 기동하지 못하는 치명적 장애가 발생합니다.

  1. EC2 UserData 또는 Ansible 스크립트에서 해당 경로가 항상 존재하도록 보장하거나
  2. env_file + 상대경로 또는 Named Volume 사용을 고려해 주세요.
backend/src/main/java/com/focussu/backend/auth/service/TokenService.java (2)

31-35: 토큰 중복 저장 시 기존 TTL 초기화 여부 검토

saveToken이 동일 사용자의 토큰을 덮어쓸 때, 기존 TTL은 새로 설정되므로 예상치 못한 세션 연장(side-effect)이 발생할 수 있습니다. 만약 “로그인 시점 기준으로만 TTL 유지”가 요구사항이면 OK이지만, “최초 발급 시점 기준 TTL 고정”이 요구라면 토큰 저장 전에 EXISTS 체크가 필요합니다.


86-88: isTokenNotRevoked → 부정어 이중 사용으로 가독성 저하

isTokenNotRevoked()는 “차단되지 않았다”는 이중 부정 형태입니다. isTokenValid() 또는 isActiveToken()과 같이 긍정 표현으로 변경하면 코드 이해가 쉬워집니다.

.github/workflows/cd.yml (3)

1-12: 병렬 배포 방지: concurrency 설정 추가 권장

현재 워크플로우는 main 브랜치에 여러 푸시가 동시에 일어나면 병렬로 배포가 실행될 수 있어 충돌이 발생할 수 있습니다. 워크플로우 상단에 concurrency 블록을 추가하여 동일 브랜치에서 중복된 실행을 방지하는 것을 권장합니다.

 name: CD - Deploy to EC2 (Blue-Green)
+concurrency:
+  group: deploy-${{ github.ref }}
+  cancel-in-progress: true
 on:
   push:
     branches: [ main ]
   workflow_dispatch:

63-82: Docker 이미지 빌드·푸시 간소화: docker/build-push-action 활용 제안

현재 수동으로 docker builddocker push 단계를 수행하고 있는데, docker/build-push-action@v3를 사용하면 buildx 기반의 캐시·병렬 빌드 및 멀티 플랫폼 지원이 가능해 빌드 속도와 안정성을 높일 수 있습니다.

-      - name: Build and tag Docker image
+      - name: Build and Push Docker image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ steps.determine_tag.outputs.tag }}
-       run: |
-          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG ./backend \
-            --build-arg JAR_FILE=build/libs/*.jar
-      - name: Push Docker image to ECR
-        env:
-          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
-          IMAGE_TAG: ${{ steps.determine_tag.outputs.tag }}
-        run: docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
+       uses: docker/build-push-action@v3
+       with:
+         context: ./backend
+         push: true
+         tags: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ steps.determine_tag.outputs.tag }}
+         build-args: |
+           JAR_FILE=build/libs/*.jar
+         cache-from: type=registry,ref=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:cache
+         cache-to: type=registry,ref=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:cache,mode=max

145-153: .env 파일 보안 강화: 권한 설정 추가

배포 스크립트에서 .env 파일에 민감 정보를 쓰고 있지만, 파일 권한이 기본(0644)으로 설정되어 노출 리스크가 있습니다. 생성 직후 chmod 600을 적용해 읽기·쓰기 권한을 소유자에게만 제한하세요.

-            echo "ECR_REPOSITORY=$ECR_REPOSITORY" >> $APP_DIR/.env
+            echo "ECR_REPOSITORY=$ECR_REPOSITORY" >> $APP_DIR/.env
+            # .env 파일 권한을 소유자만 읽고 쓰도록 설정
+            chmod 600 $APP_DIR/.env
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7fc2b5 and 71758e3.

📒 Files selected for processing (20)
  • .github/workflows/cd.yml (1 hunks)
  • .github/workflows/ci.yml (1 hunks)
  • backend/build.gradle (0 hunks)
  • backend/src/main/java/com/focussu/backend/auth/controller/AuthDocumentController.java (1 hunks)
  • backend/src/main/java/com/focussu/backend/auth/filter/LoginFilter.java (3 hunks)
  • backend/src/main/java/com/focussu/backend/auth/filter/LogoutFilter.java (0 hunks)
  • backend/src/main/java/com/focussu/backend/auth/service/TokenService.java (1 hunks)
  • backend/src/main/java/com/focussu/backend/common/BaseEntity.java (1 hunks)
  • backend/src/main/java/com/focussu/backend/config/KafkaConfig.java (0 hunks)
  • backend/src/main/java/com/focussu/backend/config/RedisConfig.java (2 hunks)
  • backend/src/main/java/com/focussu/backend/config/SecurityConfig.java (2 hunks)
  • backend/src/main/java/com/focussu/backend/studyparticipation/kafka/KafkaParticipationListener.java (0 hunks)
  • backend/src/main/resources/application-local.yml (1 hunks)
  • backend/src/main/resources/application-prod.yml (1 hunks)
  • backend/src/main/resources/application.yml (1 hunks)
  • backend/src/main/resources/http/auth.http (1 hunks)
  • backend/src/test/java/com/focussu/backend/studyparticipation/StudyParticipationKafkaIntegrationTest.java (0 hunks)
  • backend/src/test/resources/application-test.yml (0 hunks)
  • docker-compose-prod.yml (1 hunks)
  • docker-compose.yml (0 hunks)
💤 Files with no reviewable changes (7)
  • backend/build.gradle
  • backend/src/main/java/com/focussu/backend/config/KafkaConfig.java
  • backend/src/test/resources/application-test.yml
  • backend/src/main/java/com/focussu/backend/studyparticipation/kafka/KafkaParticipationListener.java
  • docker-compose.yml
  • backend/src/main/java/com/focussu/backend/auth/filter/LogoutFilter.java
  • backend/src/test/java/com/focussu/backend/studyparticipation/StudyParticipationKafkaIntegrationTest.java
🧰 Additional context used
🪛 Gitleaks (8.21.2)
backend/src/main/resources/http/auth.http

18-18: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

🪛 YAMLlint (1.35.1)
backend/src/main/resources/application-local.yml

[error] 38-38: no new line character at the end of file

(new-line-at-end-of-file)

backend/src/main/resources/application-prod.yml

[error] 38-38: no new line character at the end of file

(new-line-at-end-of-file)

.github/workflows/cd.yml

[error] 93-93: syntax error: expected ',' or '}', but got '{'

(syntax)

🪛 actionlint (1.7.4)
.github/workflows/cd.yml

92-92: could not parse as YAML: yaml: line 92: did not find expected ',' or '}'

(syntax-check)

🔇 Additional comments (4)
backend/src/main/java/com/focussu/backend/auth/controller/AuthDocumentController.java (1)

91-91: 문서 설명 업데이트 확인

로그아웃 기능에 대한 설명이 LogoutFilter에서 LogoutHandler로 수정되었습니다. 이는 코드베이스에서 로그아웃 처리 방식의 변경을 정확히 반영합니다.

backend/src/main/resources/application.yml (1)

1-8: 기본 애플리케이션 설정 추가

Spring Boot 애플리케이션의 기본 설정이 적절히 구성되었습니다. 로컬 프로필이 활성화되고, 애플리케이션 이름과 포트가 지정되었습니다.

backend/src/main/java/com/focussu/backend/config/SecurityConfig.java (1)

106-109: JWT 필터 위치 변경 영향 확인 요청

jwtAuthenticationFilterUsernamePasswordAuthenticationFilter보다 앞에 두면
/auth/login 요청에도 JWT 검증 로직이 실행됩니다.
헤더가 없는 로그인 요청에서 매번 디버그/경고 로그가 찍히거나, 예외 처리 순서가 꼬일 가능성이 있으니 의도한 동작인지 재확인 부탁드립니다.

docker-compose-prod.yml (1)

10-21: 내부-외부 포트 및 SERVER_PORT 값 불일치 여부 확인

backend-blue 컨테이너는 호스트 :컨테이너 포트를 8080:8080으로 매핑하면서 SERVER_PORT=8080을 지정했습니다. 이는 정상입니다.
그러나 backend-green에서 동일한 설정을 복제하지 않으면 Nginx 라우팅 스크립트가 혼동될 수 있으므로, 두 인스턴스 모두 동일한 내부 포트를 사용(예: 8080)하고 호스트 포트만 달리하거나, 각 컨테이너의 SERVER_PORT와 Actuator health URL이 정확히 대응되는지 재확인해 주세요.

POST http://localhost:8080/auth/logout
Content-Type: application/json
Authorization: Bearer {{TOKEN}}
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0QGdtYWlsLmNvbSIsImlhdCI6MTc0NjI4Njg4NCwiZXhwIjoxNzQ2MzIyODg0fQ.IGEBvODGFL2L8DO7TGhOM0F30rr5RS1V-lTnKZTG2nw
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

hardcoded JWT 토큰 보안 위험

고정된 JWT 토큰을 코드에 직접 포함시키는 것은 보안 위험을 초래할 수 있습니다. 이 토큰은 실제 인증 정보를 포함할 수 있으며, 만료되었더라도 코드 저장소에 영구적으로 기록됩니다.

다음과 같이 변수를 사용하는 것이 좋습니다:

-Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0QGdtYWlsLmNvbSIsImlhdCI6MTc0NjI4Njg4NCwiZXhwIjoxNzQ2MzIyODg0fQ.IGEBvODGFL2L8DO7TGhOM0F30rr5RS1V-lTnKZTG2nw
+Authorization: Bearer {{TOKEN}}
📝 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
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0QGdtYWlsLmNvbSIsImlhdCI6MTc0NjI4Njg4NCwiZXhwIjoxNzQ2MzIyODg0fQ.IGEBvODGFL2L8DO7TGhOM0F30rr5RS1V-lTnKZTG2nw
Authorization: Bearer {{TOKEN}}
🧰 Tools
🪛 Gitleaks (8.21.2)

18-18: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

Comment on lines +34 to +38

security:
jwt:
secret-key: de740246db0e808083b1bbd8afb062dfcf5e661f20cb41cc7283366d1d8fcccbdbacf3ef8af9ac7159c9ac3a4302fc2321dba0d510a5695a62dd9728ea1ade417542d25e6d80312bc66b13852621f3be0a0188c9e122fedb30186455a4ee2f6ad6bf6daac10db65e9386f3bdf099bee7a18f2e1ef572ab212d7c3a87248d5f1e55243559adfc539df7a2d9110840a825ef27d14899ce630eed7e5366485b642d0f516f468b20af3a6bd67051f00be7a27f122c785f80091bcff6e510330a6f3f30629397a4439281559647cd9c6922d560fd2bd158b527357ff01377eb5333ea9e77f48908b9f96781eaa0fc5f67e7fa4d54d6088ef26c61e43ffb647412b
expiration-time: 86400
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

JWT 보안 설정의 개선 필요

JWT 비밀 키가 직접 설정 파일에 하드코딩되어 있습니다. 이는 보안에 취약할 수 있으며, 특히 코드가 버전 관리 시스템에 저장될 때 위험합니다.

비밀 키는 환경 변수나 별도의 보안 저장소(예: AWS Secrets Manager, HashiCorp Vault)를 통해 관리하는 것이 좋습니다. 다음과 같이 변경을 고려해보세요:

security:
  jwt:
-    secret-key: de740246db0e808083b1bbd8afb062dfcf5e661f20cb41cc7283366d1d8fcccbdbacf3ef8af9ac7159c9ac3a4302fc2321dba0d510a5695a62dd9728ea1ade417542d25e6d80312bc66b13852621f3be0a0188c9e122fedb30186455a4ee2f6ad6bf6daac10db65e9386f3bdf099bee7a18f2e1ef572ab212d7c3a87248d5f1e55243559adfc539df7a2d9110840a825ef27d14899ce630eed7e5366485b642d0f516f468b20af3a6bd67051f00be7a27f122c785f80091bcff6e510330a6f3f30629397a4439281559647cd9c6922d560fd2bd158b527357ff01377eb5333ea9e77f48908b9f96781eaa0fc5f67e7fa4d54d6088ef26c61e43ffb647412b
+    secret-key: ${JWT_SECRET_KEY}
    expiration-time: 86400

또한 파일 끝에 개행 문자가 없습니다. 대부분의 리눅스 도구에서는 파일 끝에 개행 문자가 있어야 합니다.

📝 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
security:
jwt:
secret-key: de740246db0e808083b1bbd8afb062dfcf5e661f20cb41cc7283366d1d8fcccbdbacf3ef8af9ac7159c9ac3a4302fc2321dba0d510a5695a62dd9728ea1ade417542d25e6d80312bc66b13852621f3be0a0188c9e122fedb30186455a4ee2f6ad6bf6daac10db65e9386f3bdf099bee7a18f2e1ef572ab212d7c3a87248d5f1e55243559adfc539df7a2d9110840a825ef27d14899ce630eed7e5366485b642d0f516f468b20af3a6bd67051f00be7a27f122c785f80091bcff6e510330a6f3f30629397a4439281559647cd9c6922d560fd2bd158b527357ff01377eb5333ea9e77f48908b9f96781eaa0fc5f67e7fa4d54d6088ef26c61e43ffb647412b
expiration-time: 86400
security:
jwt:
secret-key: ${JWT_SECRET_KEY}
expiration-time: 86400
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 38-38: no new line character at the end of file

(new-line-at-end-of-file)

Comment on lines +6 to +14
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import java.time.LocalDateTime;

@MappedSuperclass
@Getter
@Setter
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

BaseEntity에 Setter 추가에 대한 우려

BaseEntity 클래스에 @Setter 어노테이션을 추가하면 createdAt, updatedAt, isDeleted와 같은 중요 필드가 자유롭게 수정될 수 있습니다. 이는 데이터 무결성에 위험을 초래할 수 있습니다.

특히 createdAt은 JPA에서 updatable = false로 마크되어 있지만, Lombok의 @Setter는 여전히 직접 변경이 가능하게 합니다. 다음과 같이 필요한 필드에만 개별 setter를 제공하는 것이 안전합니다:

@MappedSuperclass
@Getter
-@Setter
public abstract class BaseEntity {

    @CreationTimestamp
    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;

    @UpdateTimestamp
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    @Column(name = "is_deleted")
    private Boolean isDeleted = false;
    
+   public void setIsDeleted(Boolean isDeleted) {
+       this.isDeleted = isDeleted;
+   }
}

Comment on lines +68 to 88

// 1. 인증 성공 후 사용자 정보 가져오기
String username = auth.getName();
// 반드시 UserDetails 로드해서 토큰 생성
UserDetails user = userDetailsService.loadUserByUsername(username);
String jwt = jwtTokenUtil.generateToken(user);
tokenService.saveToken(jwt, user.getUsername());

// 2. 새로운 JWT 토큰 생성
String newJwt = jwtTokenUtil.generateToken(user);

// 3. (추가) Redis에서 해당 사용자의 기존 토큰 삭제
tokenService.removeTokenByUsername(user.getUsername());

// 4. 새로운 토큰을 Redis에 저장
tokenService.saveToken(newJwt, user.getUsername());
log.info("[LOGIN FILTER] Save {} Token..", user.getUsername());

// 5. 응답 설정
res.setCharacterEncoding("UTF-8");
res.addHeader("Authorization", "Bearer " + jwt);
res.addHeader("Authorization", "Bearer " + newJwt);
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(res.getWriter(), new AuthenticationResponse(jwt));
mapper.writeValue(res.getWriter(), new AuthenticationResponse(newJwt));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

SecurityContext 미설정으로 인증 정보 유실 가능

successfulAuthentication에서 SecurityContextHolder.getContext().setAuthentication(auth) 호출이나
super.successfulAuthentication(req, res, chain, auth) 위임이 빠져 있습니다.
로그인 직후 같은 요청 흐름(예: 후속 필터, 이벤트 퍼블리셔)에서 인증 객체가 필요하다면 현재 구현으로는 null 이 될 수 있습니다.

@@
-        // 5. 응답 설정
+        // 5. SecurityContext 저장
+        SecurityContextHolder.getContext().setAuthentication(auth);
+
+        // 6. 응답 설정
📝 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
// 1. 인증 성공 후 사용자 정보 가져오기
String username = auth.getName();
// 반드시 UserDetails 로드해서 토큰 생성
UserDetails user = userDetailsService.loadUserByUsername(username);
String jwt = jwtTokenUtil.generateToken(user);
tokenService.saveToken(jwt, user.getUsername());
// 2. 새로운 JWT 토큰 생성
String newJwt = jwtTokenUtil.generateToken(user);
// 3. (추가) Redis에서 해당 사용자의 기존 토큰 삭제
tokenService.removeTokenByUsername(user.getUsername());
// 4. 새로운 토큰을 Redis에 저장
tokenService.saveToken(newJwt, user.getUsername());
log.info("[LOGIN FILTER] Save {} Token..", user.getUsername());
// 5. 응답 설정
res.setCharacterEncoding("UTF-8");
res.addHeader("Authorization", "Bearer " + jwt);
res.addHeader("Authorization", "Bearer " + newJwt);
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(res.getWriter(), new AuthenticationResponse(jwt));
mapper.writeValue(res.getWriter(), new AuthenticationResponse(newJwt));
}
// 1. 인증 성공 후 사용자 정보 가져오기
String username = auth.getName();
UserDetails user = userDetailsService.loadUserByUsername(username);
// 2. 새로운 JWT 토큰 생성
String newJwt = jwtTokenUtil.generateToken(user);
// 3. (추가) Redis에서 해당 사용자의 기존 토큰 삭제
tokenService.removeTokenByUsername(user.getUsername());
// 4. 새로운 토큰을 Redis에 저장
tokenService.saveToken(newJwt, user.getUsername());
log.info("[LOGIN FILTER] Save {} Token..", user.getUsername());
// 5. SecurityContext 저장
SecurityContextHolder.getContext().setAuthentication(auth);
// 6. 응답 설정
res.setCharacterEncoding("UTF-8");
res.addHeader("Authorization", "Bearer " + newJwt);
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(res.getWriter(), new AuthenticationResponse(newJwt));
}

Comment on lines +15 to +18
jpa:
hibernate:
ddl-auto: create
show-sql: false
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

프로덕션에서 ddl-auto: create 사용 시 데이터 손실 위험

애플리케이션 재시작 때마다 스키마를 드롭·재생성하므로 서비스 데이터가 모두 삭제됩니다.
운영 환경에서는 validate, none 또는 마이그레이션 툴(Flyway/Liquibase) 사용을 권장합니다.

-    hibernate:
-      ddl-auto: create
+    hibernate:
+      ddl-auto: validate   # 또는 none
📝 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
jpa:
hibernate:
ddl-auto: create
show-sql: false
jpa:
- hibernate:
- ddl-auto: create
+ hibernate:
+ ddl-auto: validate # 또는 none
show-sql: false

Comment on lines +28 to +36
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
# with:
# gradle-version: wrapper # 프로젝트의 Gradle Wrapper 사용을 명시 (선택 사항)

# 4. gradlew 실행 권한 부여: Gradle Wrapper 스크립트에 실행 권한을 줍니다.
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
working-directory: ./backend # backend 디렉토리에서 실행
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

chmod 단계를 Setup Gradle 이전으로 이동하는 것을 권장합니다

gradle/actions/setup-gradle@v3는 내부적으로 ./gradlew를 호출할 수 있습니다. 현재 순서에서는 gradlew의 실행 권한이 아직 부여되지 않은 상태이므로, OS에 따라 퍼미션 오류가 발생할 가능성이 있습니다. 안전하게 권한 부여를 먼저 수행한 뒤 Gradle 설정을 실행하는 편이 좋습니다.

-      - name: Setup Gradle
-        uses: gradle/actions/setup-gradle@v3
+      - name: Grant execute permission for gradlew
+        run: chmod +x ./gradlew
+        working-directory: ./backend
+
+      - name: Setup Gradle
+        uses: gradle/actions/setup-gradle@v3

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +55 to +68
ValueOperations<String, String> valueOps = redisTemplate.opsForValue();
// "TOKEN_*" 패턴의 키 조회 (성능 주의)
Set<String> keys = redisTemplate.keys(TOKEN_PREFIX + "*");

if (keys != null) {
for (String key : keys) {
String storedToken = valueOps.get(key);
if (token.equals(storedToken)) {
// 키에서 접두사 제거 후 username 반환
return Optional.of(key.substring(TOKEN_PREFIX.length()));
}
}
}
return Optional.empty(); // 토큰을 찾지 못함
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

redisTemplate.keys() 사용은 운영환경에서 심각한 성능 저하와 블로킹을 유발합니다

keys(TOKEN_*) 명령은 Redis 단일 스레드를 블로킹하며, 키 수가 많아질수록 응답 시간이 급격히 늘어납니다. 토큰 검증마다 호출되면 서비스 전체가 느려질 수 있습니다.
권장 방법:

  1. SCAN 커서 기반 순회를 사용해 점진적으로 탐색하거나
  2. 애초에 token → username 매핑을 추가로 저장하여 O(1) 조회가 가능하도록 설계 변경
  3. 또는 레디스 해시 구조(HSET userTokens username token) 사용

즉시 개선이 필요합니다.

-Set<String> keys = redisTemplate.keys(TOKEN_PREFIX + "*");
+// TODO: KEYS 명령 대신 SCAN 커서 기반 조회로 변경
+Cursor<String> cursor = redisTemplate.scan(
+        ScanOptions.scanOptions()
+                   .match(TOKEN_PREFIX + "*")
+                   .count(1000)
+                   .build());

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +91 to +94
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with: { aws-access-key-id: ${ { secrets.AWS_ACCESS_KEY_ID } }, aws-secret-access-key: ${ { secrets.AWS_SECRET_ACCESS_KEY } }, aws-region: ${ { env.AWS_REGION } } }

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

YAML 구문 오류 수정 필요

with: { aws-access-key-id: ${ { secrets.AWS_ACCESS_KEY_ID } }, ... } 부분에서 ${ { ... } } 공백으로 인해 YAML 파싱 오류가 발생합니다. 아래처럼 블록 매핑으로 변경하거나 ${{ ... }} 문법을 올바르게 사용하세요.

-      - name: Configure AWS credentials
-        uses: aws-actions/configure-aws-credentials@v4
-        with: { aws-access-key-id: ${ { secrets.AWS_ACCESS_KEY_ID } }, aws-secret-access-key: ${ { secrets.AWS_SECRET_ACCESS_KEY } }, aws-region: ${ { env.AWS_REGION } } }
+      - name: Configure AWS credentials
+        uses: aws-actions/configure-aws-credentials@v4
+        with:
+          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+          aws-region: ${{ env.AWS_REGION }}
🧰 Tools
🪛 actionlint (1.7.4)

92-92: could not parse as YAML: yaml: line 92: did not find expected ',' or '}'

(syntax-check)

🪛 YAMLlint (1.35.1)

[error] 93-93: syntax error: expected ',' or '}', but got '{'

(syntax)

@oxdjww oxdjww merged commit cefa19c into main May 3, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DEPLOY] 2차 배포

2 participants