-
Notifications
You must be signed in to change notification settings - Fork 0
[DEPLOY] 2차 배포 #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DEPLOY] 2차 배포 #21
Conversation
|
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 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. 📒 Files selected for processing (6)
Walkthrough이번 변경 사항은 Kafka 관련 기능을 전면적으로 제거하고, JWT 기반 인증 및 토큰 관리를 개선하며, CI/CD 자동화 파이프라인을 도입하는 데 중점을 두었습니다. Kafka 의존성, 설정, 리스너, 테스트가 모두 삭제되었고, 토큰 저장 구조 및 로그아웃 처리가 리팩토링되었습니다. 또한, Blue-Green 배포 전략을 적용한 EC2 자동 배포(CD)와 PR 기반의 CI 워크플로우가 GitHub Actions로 추가되었습니다. 배포 및 운영 환경을 위한 Docker Compose와 Spring 프로파일 설정 파일도 새롭게 도입되었습니다. Changes
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: 프록시(전환 후 서비스)
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this 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()가 실행되어 불필요한 객체가 증가합니다.
이미 프로젝트에 주입 가능한ObjectMapperBean이 있을 가능성이 높으니 재사용을 권장합니다.- 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-daemondocker-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/...절대경로를 볼륨으로 마운트하고 있습니다. 운영 서버 사용자 또는 경로가 바뀌면 컨테이너가 기동하지 못하는 치명적 장애가 발생합니다.
- EC2 UserData 또는 Ansible 스크립트에서 해당 경로가 항상 존재하도록 보장하거나
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 build→docker 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
📒 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 필터 위치 변경 영향 확인 요청
jwtAuthenticationFilter를UsernamePasswordAuthenticationFilter보다 앞에 두면
/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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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)
|
|
||
| security: | ||
| jwt: | ||
| secret-key: de740246db0e808083b1bbd8afb062dfcf5e661f20cb41cc7283366d1d8fcccbdbacf3ef8af9ac7159c9ac3a4302fc2321dba0d510a5695a62dd9728ea1ade417542d25e6d80312bc66b13852621f3be0a0188c9e122fedb30186455a4ee2f6ad6bf6daac10db65e9386f3bdf099bee7a18f2e1ef572ab212d7c3a87248d5f1e55243559adfc539df7a2d9110840a825ef27d14899ce630eed7e5366485b642d0f516f468b20af3a6bd67051f00be7a27f122c785f80091bcff6e510330a6f3f30629397a4439281559647cd9c6922d560fd2bd158b527357ff01377eb5333ea9e77f48908b9f96781eaa0fc5f67e7fa4d54d6088ef26c61e43ffb647412b | ||
| expiration-time: 86400 |
There was a problem hiding this comment.
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.
| 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)
| import lombok.Setter; | ||
| import org.hibernate.annotations.CreationTimestamp; | ||
| import org.hibernate.annotations.UpdateTimestamp; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| @MappedSuperclass | ||
| @Getter | ||
| @Setter |
There was a problem hiding this comment.
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;
+ }
}|
|
||
| // 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)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| // 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)); | |
| } |
| jpa: | ||
| hibernate: | ||
| ddl-auto: create | ||
| show-sql: false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
프로덕션에서 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.
| jpa: | |
| hibernate: | |
| ddl-auto: create | |
| show-sql: false | |
| jpa: | |
| - hibernate: | |
| - ddl-auto: create | |
| + hibernate: | |
| + ddl-auto: validate # 또는 none | |
| show-sql: false |
| - 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 디렉토리에서 실행 |
There was a problem hiding this comment.
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@v3Committable suggestion skipped: line range outside the PR's diff.
| 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(); // 토큰을 찾지 못함 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redisTemplate.keys() 사용은 운영환경에서 심각한 성능 저하와 블로킹을 유발합니다
keys(TOKEN_*) 명령은 Redis 단일 스레드를 블로킹하며, 키 수가 많아질수록 응답 시간이 급격히 늘어납니다. 토큰 검증마다 호출되면 서비스 전체가 느려질 수 있습니다.
권장 방법:
SCAN커서 기반 순회를 사용해 점진적으로 탐색하거나- 애초에
token → username매핑을 추가로 저장하여 O(1) 조회가 가능하도록 설계 변경 - 또는 레디스 해시 구조(
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.
| - 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 } } } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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)
Summary by CodeRabbit
신규 기능
버그 수정
리팩터
문서
환경설정/작업