Skip to content

[fix] elasticsearch 수정 및 배포 적용#150

Open
ghkddlscks19 wants to merge 9 commits intodevelopfrom
test/search/chan
Open

[fix] elasticsearch 수정 및 배포 적용#150
ghkddlscks19 wants to merge 9 commits intodevelopfrom
test/search/chan

Conversation

@ghkddlscks19
Copy link
Contributor

@ghkddlscks19 ghkddlscks19 commented Sep 18, 2025

#️⃣ Issue Number

📝 요약(Summary)

elasticsearch 수정 및 배포 적용

🛠️ PR 유형

어떤 변경 사항이 있나요?

  • 새로운 기능 추가
  • 버그 수정
  • CSS 등 사용자 UI 디자인 변경
  • 코드에 영향을 주지 않는 변경사항(오타 수정, 탭 사이즈 변경, 변수명 변경)
  • 코드 리팩토링
  • 주석 추가 및 수정
  • 문서 수정
  • 테스트 추가, 테스트 리팩토링
  • 빌드 부분 혹은 패키지 매니저 수정
  • 파일 혹은 폴더명 수정
  • 파일 혹은 폴더 삭제

📸스크린샷 (선택)

💬 공유사항 to 리뷰어

✅ PR Checklist

PR이 다음 요구 사항을 충족하는지 확인하세요.

  • 커밋 메시지 컨벤션에 맞게 작성했습니다.
  • 변경 사항에 대한 테스트를 했습니다.(버그 수정/기능에 대한 테스트).

Summary by CodeRabbit

  • Refactor
    • 공개 API 경로를 /api/... 로 통일(클럽, 피드, 채팅, 인증, 사용자, 지갑/결제, 알림, 검색, 스케줄/정산, 이미지 업로드). 클라이언트는 새 경로로 업데이트 필요.
    • 검색 인덱스 createdAt 포맷을 초 단위로 단일화하여 응답 일관성 향상.
  • Bug Fixes
    • 동호회 생성 시 멤버 수가 null인 경우 1로 초기화하여 누락 증가 문제 방지.
  • Chores
    • 배포를 AWS ASG 롤링 리프레시로 전환, stage 브랜치로 트리거 변경.
    • CORS에 https://buddkit.com 도메인 추가.

@ghkddlscks19 ghkddlscks19 requested review from NamYeonW00, geleego and gkdudans and removed request for geleego and gkdudans September 18, 2025 08:35
@coderabbitai
Copy link

coderabbitai bot commented Sep 18, 2025

Walkthrough

배포 워크플로우가 SSH/Compose 방식에서 AWS ASG 롤링 리프레시로 전환되었고, 다수의 컨트롤러 베이스 경로가 "/api" 프리픽스로 일괄 변경되었습니다. 메시지 저장 로직은 kakaoId 대신 userId를 사용하도록 업데이트되었고, Club 생성 시 멤버 수 증가 시점과 null 가드가 추가되었습니다. ES 매핑의 createdAt 형식이 초 단위로 통일되었습니다.

Changes

Cohort / File(s) Summary
CI/CD: GitHub Actions → AWS ASG 배포
.github/workflows/github-actions.yml
배포 브랜치 deploy→stage, secret APPLICATION→APPLICATION_STAGE, SSH 배포 제거 후 aws-actions 자격 설정 및 ASG Instance Refresh 추가(롤링, MinHealthyPercentage=50).
API 경로 프리픽스 통일 (/api …)
src/main/java/.../chat/controller/ChatRoomRestController.java, .../chat/controller/MessageRestController.java, .../club/controller/ClubController.java, .../feed/controller/FeedController.java, .../feed/controller/FeedMainController.java, .../image/controller/ImageController.java, .../notification/controller/NotificationController.java, .../payment/controller/PaymentController.java, .../schedule/controller/ScheduleController.java, .../search/controller/SearchController.java, .../settlement/controller/SettlementController.java, .../user/controller/AuthController.java, .../user/controller/UserController.java, .../wallet/controller/WalletController.java
클래스/메서드 수준 @RequestMapping/@PostMapping 경로를 /api 프리픽스로 변경. 다른 로직 변화 없음.
메시지 저장: 식별자 변경 및 조회 추가
src/main/java/.../chat/service/MessageService.java, src/main/java/.../user/repository/UserRepository.java
saveMessage 파라미터 kakaoId→userId로 변경; 사용자 조회를 findByUserId로 전환. UserRepository에 findByUserId(Long) 추가.
보안 및 관찰성
src/main/java/.../global/config/SecurityConfig.java, src/main/java/.../global/filter/JwtAuthenticationFilter.java
화이트리스트 경로를 /api 프리픽스로 이관; CORS에 https://buddkit.com 추가; JWT 필터에 진단 로그 추가.
클럽 문서/ES 매핑: createdAt 포맷 단일화
src/main/java/.../club/document/ClubDocument.java, src/main/resources/elasticsearch/club-create-settings.json, src/main/resources/elasticsearch/club-mapping.json
createdAt을 LocalDateTime→String으로 변경하고 포맷을 "yyyy-MM-dd'T'HH:mm:ss"로 고정; ES 매핑/세팅도 동일 포맷으로 축소.
클럽 멤버 수 처리 로직 조정
src/main/java/.../club/entity/Club.java, src/main/java/.../club/service/ClubService.java
incrementMemberCount에 null 가드 추가; 클럽 생성 시 멤버 수 증가 시점을 엔티티 생성 직후(저장 전)로 이동하고 이후 중복 증가 제거.
로깅 보강
src/main/java/.../user/controller/AuthController.java
kakaoLogin 흐름에 디버그 로그 추가(토큰/유저/토큰 발급 등), 예외 로깅 추가.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Dev as Developer
    participant GH as GitHub Actions
    participant AWS as AWS (IAM)
    participant ASG as Auto Scaling Group

    Dev->>GH: Push to branch "stage"
    GH->>GH: Build (Gradle) / Docker build & push
    GH->>AWS: Configure AWS credentials
    GH->>ASG: start-instance-refresh (Rolling, MinHealthyPercentage=50)
    ASG-->>GH: Instance refresh started
Loading
sequenceDiagram
    autonumber
    participant API as MessageService.saveMessage
    participant UR as UserRepository
    participant CR as ChatRoomRepository
    participant MSG as MessageRepository

    API->>UR: findByUserId(userId)
    UR-->>API: Optional<User>
    API->>CR: findById(chatRoomId)
    CR-->>API: Optional<ChatRoom>
    API->>MSG: save(new Message(user, chatRoom, text))
    MSG-->>API: Message
    API-->>API: map to ChatMessageResponse
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

Chat, User

Suggested reviewers

  • geleego
  • NamYeonW00
  • choigpt

Poem

귀가 팔랑, 코드가 찰랑—띠링!
/api 길 깔고 나는 총총 달린다.
토끼 발로 ASG 톡톡, 인스턴스가 빙글.
초만 세는 시간표, 메시지는 또 쏙.
한 명, 두 명—멤버 수는 꽉! 🥕
오늘도 배포완료, 야호!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning PR 설명은 템플릿의 요약과 PR 유형을 채우고 있으나 필수 정보들이 недостат하여 검토에 어려움이 있습니다; 이슈 번호가 비어 있고 요약이 매우 간단하여 변경된 파일/영향 범위, 마이그레이션·검증 방법, 롤백 계획 등이 명시되어 있지 않습니다. 또한 체크리스트 항목과 리뷰어에게 요청할 중점 확인 사항이 비어 있어 리뷰 효율이 떨어질 가능성이 큽니다. 결과적으로 현재 설명만으로는 배포 시 위험도와 필요한 검증 절차를 판단하기 어렵습니다. 다음 항목을 보완해 주세요: 이슈 번호 추가, 요약에 주요 변경사항을 항목별로 명시(예: Elasticsearch createdAt 포맷·매핑 변경; 다수 컨트롤러의 "/api" 경로 추가; MessageService kakaoId→userId 파라미터명 변경 및 UserRepository.findByUserId 추가; Club.memberCount 안전 처리 변경; CI/CD에서 SSH→AWS ASG 롤링 리프레시로 전환), 배포 전/후 검증 방법(ES 인덱스 마이그레이션 필요 여부, API 호환성 확인 절차, 통합/스모크 테스트 체크리스트)과 롤백 플랜을 기재하고 PR 체크리스트를 업데이트해 주세요. 또한 리뷰어가 중점적으로 봐야 할 브레이킹 체인지나 클라이언트 영향 범위 등을 명확히 적어 검토 속도와 안전성을 높이시기 바랍니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed 제목 "[fix] elasticsearch 수정 및 배포 적용"은 PR에서 수행된 Elasticsearch 매핑/포맷 변경과 배포 파이프라인(ASG 롤링 리프레시) 적용을 핵심으로 간결하게 요약하고 있어 변경사항과 관련성이 높습니다. 표현은 불필요한 잡음 없이 읽기 쉬우며 실제 변경점의 일부를 정확히 반영합니다. 다만 이 PR에는 다수의 컨트롤러 경로에 대한 "/api" 접두사 추가, MessageService 파라미터명 변경 및 UserRepository에 findByUserId 추가 등 다른 중요 변경도 포함되어 있어 제목이 모든 주요 변경을 완전히 포괄하지는 않습니다.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test/search/chan

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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

Caution

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

⚠️ Outside diff range comments (4)
src/main/java/com/example/onlyone/global/filter/JwtAuthenticationFilter.java (3)

69-72: /api 마이그레이션 불일치로 로그아웃 차단 가능

현재 코드가 "/auth/logout"만 허용합니다. "/api/auth/logout"로 접속하면 탈퇴 사용자 로그아웃이 차단될 수 있습니다.

다음과 같이 두 경로를 모두 허용하거나 프리픽스 기준으로 판별하세요.

-                    if (!"/auth/logout".equals(request.getRequestURI())) {
+                    String uri = request.getRequestURI();
+                    if (!"/auth/logout".equals(uri) && !"/api/auth/logout".equals(uri)) {
                         throw new CustomException(ErrorCode.USER_WITHDRAWN);
-                    }
+                    }

59-61: NPE 가능성: kakaoId 클레임 누락 처리

claims.get("kakaoId")가 null이면 NPE로 500이 날 수 있습니다. 명시적으로 UNAUTHORIZED 처리하세요.

-            String kakaoIdString = claims.get("kakaoId").toString();
+            Object kakaoIdClaim = claims.get("kakaoId");
+            if (kakaoIdClaim == null) {
+                throw new CustomException(ErrorCode.UNAUTHORIZED);
+            }
+            String kakaoIdString = kakaoIdClaim.toString();

53-57: 긴급: JJWT API/버전 충돌 — 수정 필요

  • build.gradle에 io.jsonwebtoken:jjwt-api/impl/jackson 0.11.5와 0.12.4가 모두 선언되어 있습니다 (build.gradle, lines 58–65).
  • JJWT 0.12.x에서는 파서 API가 변경되어 기존 호출(예: Jwts.parser().setSigningKey(key).build().parseClaimsJws(token))이 0.12.x와 호환되지 않습니다 — 0.12.x는 verifyWith()/parseSignedClaims() 등 새 API를 사용합니다.
  • 조치(택1):
    1. 의존성을 0.11.5로 통일(0.12.4 제거)하여 기존 코드를 유지하거나,
    2. JwtAuthenticationFilter.java (src/main/java/com/example/onlyone/global/filter/JwtAuthenticationFilter.java, 해당 라인 약 53–57)에서 파서 호출을 0.12.x 규약으로 마이그레이션(예: Jwts.parser().verifyWith(key).build().parseSignedClaims(jwt).getPayload()) 하여 코드 수정.
src/main/java/com/example/onlyone/domain/chat/controller/MessageRestController.java (1)

31-46: 요청 바디의 userId 신뢰는 가장 위험 — 인증 사용자에서 강제 주입 필요

sendMessage가 request.getUserId()를 그대로 사용하면 임의 사용자 가장이 가능합니다. deleteMessage는 세션 사용자 기반이라 불일치입니다. 인증 사용자에서 userId를 취해 서비스로 전달하세요.

적용 예시:

-        ChatMessageResponse response =
-                messageService.saveMessage(chatRoomId, request.getUserId(), request.getText());
+        Long actorId = userService.getCurrentUser().getUserId();
+        ChatMessageResponse response =
+                messageService.saveMessage(chatRoomId, actorId, request.getText());
🧹 Nitpick comments (22)
src/main/java/com/example/onlyone/domain/club/entity/Club.java (2)

59-61: builder 사용 시 memberCount null 위험 — 기본값 보존 필요

  • Lombok @builder는 필드 초기값을 무시합니다. 현재 @NotNull + nullable=false인데 builder 경로에서 null이 들어갈 수 있어 제약 위반 위험이 남아있습니다.

아래처럼 @Builder.Default를 추가해 builder에서도 0L이 기본 적용되게 해주세요.

-    @NotNull
-    private Long memberCount = 0L;
+    @NotNull
+    @Builder.Default
+    private Long memberCount = 0L;

100-106: increment/decrement 대칭성 및 null 가드 보완

  • increment에서만 null을 가드하고 decrement는 null 시 NPE/음수 방지가 불완전합니다.

아래처럼 보완해 주세요.

     public void decrementMemberCount() {
-        this.memberCount = Math.max(0L, this.memberCount - 1);
+        if (this.memberCount == null || this.memberCount <= 0) {
+            this.memberCount = 0L;
+        } else {
+            this.memberCount--;
+        }
     }
src/main/java/com/example/onlyone/domain/club/document/ClubDocument.java (2)

13-16: 미사용 import 정리

  • LocalDateTime, JsonFormat, DateFormat(별도 import)은 더 이상 사용되지 않습니다. 제거해 경고를 없애 주세요.
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatter;
-
-import com.fasterxml.jackson.annotation.JsonFormat;

또한 org.springframework.data.elasticsearch.annotations.DateFormat 단일 import가 필요 없다면 제거하세요(와일드카드로 이미 포함 또는 미사용).


77-77: 포맷 상수화 및 null‑safe 포맷팅

  • 포맷 문자열 하드코딩은 중복/오타 위험이 있습니다.
 public class ClubDocument {
+    private static final DateTimeFormatter ES_DATE_FMT =
+        DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
 ...
-                .createdAt(club.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")))
+                .createdAt(club.getCreatedAt() == null ? null : club.getCreatedAt().format(ES_DATE_FMT))
.github/workflows/github-actions.yml (4)

60-65: ASG 롤링 리프레시 완료 대기 추가

  • 현재는 트리거만 하고 종료합니다. 배포 안정성을 위해 완료까지 대기하세요.
-      - name: Auto Scaling Group Refresh
-        run: |
-          aws autoscaling start-instance-refresh \
-            --auto-scaling-group-name onlyone-buddkit-back-asg \
-            --strategy "Rolling" \
-            --preferences "MinHealthyPercentage=50"
+      - name: Start ASG Instance Refresh
+        id: asg_refresh
+        run: |
+          REFRESH_ID=$(aws autoscaling start-instance-refresh \
+            --auto-scaling-group-name onlyone-buddkit-back-asg \
+            --strategy "Rolling" \
+            --preferences "MinHealthyPercentage=50" \
+            --query 'InstanceRefreshId' --output text)
+          echo "id=$REFRESH_ID" >> $GITHUB_OUTPUT
+
+      - name: Wait ASG Instance Refresh Completed
+        run: |
+          aws autoscaling wait instance-refresh-completed \
+            --auto-scaling-group-name onlyone-buddkit-back-asg \
+            --instance-refresh-id ${{ steps.asg_refresh.outputs.id }}

추가로, 러너에 AWS CLI가 기본 탑재되어 있는지 확인해 주세요. 없으면 aws-cli 설치 스텝이 필요합니다.


45-51: latest 태그만 사용: 불변 이미지 태깅 권장

  • latest만 사용하면 롤백/재현성 저하 및 캐시 이슈가 발생합니다. 커밋 SHA로 불변 태그를 함께 푸시하고, 런치 템플릿/유저데이터에서 해당 태그를 사용하도록 전환을 권장합니다.
       - name: Server build & push
         uses: docker/build-push-action@v6
         with:
           context: ./
           file: ./Dockerfile
           platforms: linux/arm64
           push: true
-          tags: ${{ secrets.DOCKER_REPO }}:latest
+          tags: |
+            ${{ secrets.DOCKER_REPO }}:latest
+            ${{ secrets.DOCKER_REPO }}:${{ github.sha }}

또한 대상 인스턴스가 ARM(Graviton)인지 확인해 주세요. x86_64라면 플랫폼을 추가해야 합니다.


39-42: docker/login-action 최신 버전 권장

  • v1은 구버전입니다. v3로 업데이트 권장.
-      - name: Login to DockerHub
-        uses: docker/login-action@v1
+      - name: Login to DockerHub
+        uses: docker/login-action@v3

3-6: 수동 트리거 지원과 개행

  • stage 브랜치 이외에 수동 실행(workflow_dispatch)도 함께 두면 운영 편의성이 높습니다. 또한 파일 말미 개행이 없습니다(YAMLlint).
 on:
   push:
     branches: [ "stage" ]
+  workflow_dispatch: {}

파일 끝에 개행 추가해 주세요.

src/main/java/com/example/onlyone/domain/user/repository/UserRepository.java (1)

12-12: findByUserId는 findById로 대체 가능

  • 엔티티 PK가 userId라면 JPA 기본 findById로 동일 목적을 달성할 수 있습니다. 메서드 남기려면 호출부 일관성(예: OAuth 전용 findByKakaoId, 일반 조회 findById) 정리를 권장합니다.

해당 메서드를 사용 중인 서비스에서 findById로 대체 가능한지 확인해 주세요.

src/main/java/com/example/onlyone/domain/wallet/controller/WalletController.java (1)

8-8: 미사용 import 제거

  • jakarta.xml.bind.annotation.XmlType는 사용되지 않습니다. 제거하세요.
src/main/resources/elasticsearch/club-create-settings.json (1)

98-101: 확인: createdAt 포맷(초 단위) 일치 — 선택적 개선 권장

검증 결과 src/main/resources/elasticsearch/club-create-settings.json, src/main/resources/elasticsearch/club-mapping.json 및 src/main/java/com/example/onlyone/domain/club/document/ClubDocument.java 모두 "yyyy-MM-dd'T'HH:mm:ss" 로 일치하므로 리포지토리 내에서는 포맷 불일치로 인한 인덱싱 실패 근거 없음.

  • 권장(선택): 외부에서 밀리초 포함 타임스탬프가 유입될 가능성이 있으면 매핑에 멀티 포맷을 허용하세요. 예시(diff):
-        "format": "yyyy-MM-dd'T'HH:mm:ss"
+        "format": "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSS"
  • 중복 정리 권고: mappings 정의가 두 곳에 중복되어 있으니 드리프트 방지를 위해 단일 소스로 관리(예: mapping.json은 mappings만, create-settings.json은 settings만)하세요. 파일: src/main/resources/elasticsearch/club-mapping.json, src/main/resources/elasticsearch/club-create-settings.json. 참고: src/main/java/com/example/onlyone/domain/club/document/ClubDocument.java
src/main/java/com/example/onlyone/domain/payment/controller/PaymentController.java (1)

22-22: /api 프리픽스 적용 — 외부 연동 URL 동기화 필요

토스 결제 성공/실패 리다이렉트·콜백 URL(관리 콘솔/프런트 환경변수)이 새 베이스 패스로 동기화됐는지 확인하세요.

src/main/java/com/example/onlyone/domain/notification/controller/NotificationController.java (1)

74-75: size 최대값 미검증(문서 불일치)

주석에 “최대 100”이라 되어 있으나 검증이 없습니다. 쿼리 부하 방지를 위해 상한을 강제하세요.

-        @RequestParam(defaultValue = "20") int size) {
+        @RequestParam(defaultValue = "20") int size) {
+        if (size > 100) size = 100;
src/main/java/com/example/onlyone/domain/search/controller/SearchController.java (1)

44-48: 사양-구현 불일치: keyword 필수(2자 이상) 규칙 미강제

문서상 keyword는 필수/2자 이상이지만 파라미터는 optional이며 길이 검증도 없습니다. 서비스 단에서 처리하지 않는다면 컨트롤러에서 400을 반환하도록 보완하세요. city/district 동시 제공 규칙도 함께 검증 권장.

예:

-    public ResponseEntity<?> searchClubs(
+    public ResponseEntity<?> searchClubs(
             @RequestParam(required = false) String keyword,
             @RequestParam(required = false) String city,
             @RequestParam(required = false) String district,
             @RequestParam(required = false) Long interestId,
             @RequestParam(defaultValue = "MEMBER_COUNT") SearchFilterDto.SortType sortBy,
             @RequestParam(defaultValue = "0") int page) {
-        
+        if (keyword == null || keyword.trim().length() < 2) {
+            return ResponseEntity.badRequest().body(CommonResponse.fail("keyword must be at least 2 characters"));
+        }
+        if ((city == null) ^ (district == null)) {
+            return ResponseEntity.badRequest().body(CommonResponse.fail("city and district must be provided together"));
+        }

(CommonResponse.fail 형태는 프로젝트 구현에 맞게 조정)

Also applies to: 51-56

src/main/java/com/example/onlyone/domain/image/controller/ImageController.java (2)

23-23: 개별 메서드에 /api 프리픽스 적용

동작상 문제는 없으나, 일관성을 위해 클래스 레벨 @RequestMapping 적용을 고려해볼 수 있습니다.


24-33: 폴더 타입 검증 강화 제안

imageFolderType을 String으로 받으면 오입력이 200/500로 이어질 수 있습니다. enum 바인딩 또는 사전 검증을 권장합니다.

예:

-    public ResponseEntity<CommonResponse<PresignedUrlResponseDto>> generatePresignedUrl(
-            @PathVariable String imageFolderType,
+    public ResponseEntity<CommonResponse<PresignedUrlResponseDto>> generatePresignedUrl(
+            @PathVariable ImageFolderType imageFolderType,
             @Valid @RequestBody PresignedUrlRequestDto request) {
 
-        PresignedUrlResponseDto response = imageService.generatePresignedUrlWithImageUrl(
-                imageFolderType,
+        PresignedUrlResponseDto response = imageService.generatePresignedUrlWithImageUrl(
+                imageFolderType.name(),
                 request.getFileName(),
                 request.getContentType(),
                 request.getImageSize()
         );

서비스 시그니처가 String만 받는다면 enum→name() 변환으로 호환 유지 가능합니다.

src/main/java/com/example/onlyone/global/config/SecurityConfig.java (1)

76-83: CORS: 와일드카드/자격증명 조합은 위험 — 프로파일 분리 권장

allowedOriginPatterns에 'https://*.ngrok-free.app'와 allowCredentials(true)가 함께 사용됩니다. 운영 프로파일에서는 정확한 도메인만 허용하고, 와일드카드는 dev/test로 제한하는 구성이 안전합니다.

src/main/java/com/example/onlyone/domain/club/service/ClubService.java (2)

127-158: 정원 체크 경쟁 상태 가능성 — 잠금/제약 고려

count→비교→삽입 사이 경쟁으로 초과 가입 가능성이 있습니다. PESSIMISTIC_WRITE(클럽 행 잠금) 또는 ‘정원 초과’ 방지를 위한 최종 검증/DB 제약(조건부 업데이트) 적용을 검토하세요.


95-97: 에러코드 도메인 불일치(스케줄) — 의미 맞는 코드로 교체 권장

MEMBER_CANNOT_MODIFY_SCHEDULE 대신 클럽 수정 문맥에 맞는 에러코드를 사용하세요.

src/main/java/com/example/onlyone/domain/chat/service/MessageService.java (2)

43-50: saveMessage의 userId 파라미터 — 컨트롤러에서 인증 사용자만 전달되도록 보장 필요

컨트롤러 수정으로 위조 가능성은 해소되지만, 서비스 계층에서도 신뢰 경계가 명확해지도록 User를 직접 받거나 SecurityContext에서 조회하는 방식으로의 리팩터를 권장합니다.


76-86: 응답 senderId가 kakaoId — 식별자 일관성 재검토

저장/검증은 userId 기반인데 응답은 kakaoId를 돌려줍니다. 클라이언트 계약 확인 후 userId로 통일하거나 둘 다 제공하도록 DTO를 조정하는 것이 혼란을 줄입니다.

src/main/java/com/example/onlyone/domain/user/controller/AuthController.java (1)

67-68: 로그 레벨 조정 제안

성공 로그는 info로, 상세 정보는 debug로 제한해 로그 노이즈와 비용을 줄이세요.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a2c58cf and 170dfad.

📒 Files selected for processing (24)
  • .github/workflows/github-actions.yml (3 hunks)
  • src/main/java/com/example/onlyone/domain/chat/controller/ChatRoomRestController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/chat/controller/MessageRestController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/chat/service/MessageService.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/club/controller/ClubController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/club/document/ClubDocument.java (3 hunks)
  • src/main/java/com/example/onlyone/domain/club/entity/Club.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/club/service/ClubService.java (2 hunks)
  • src/main/java/com/example/onlyone/domain/feed/controller/FeedController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/feed/controller/FeedMainController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/image/controller/ImageController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/notification/controller/NotificationController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/payment/controller/PaymentController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/schedule/controller/ScheduleController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/search/controller/SearchController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/settlement/controller/SettlementController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/user/controller/AuthController.java (3 hunks)
  • src/main/java/com/example/onlyone/domain/user/controller/UserController.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/user/repository/UserRepository.java (1 hunks)
  • src/main/java/com/example/onlyone/domain/wallet/controller/WalletController.java (1 hunks)
  • src/main/java/com/example/onlyone/global/config/SecurityConfig.java (2 hunks)
  • src/main/java/com/example/onlyone/global/filter/JwtAuthenticationFilter.java (1 hunks)
  • src/main/resources/elasticsearch/club-create-settings.json (1 hunks)
  • src/main/resources/elasticsearch/club-mapping.json (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-16T00:20:44.262Z
Learnt from: ghkddlscks19
PR: GoormOnlyOne/OnlyOne-Back#145
File: src/main/java/com/example/onlyone/domain/user/service/UserService.java:84-99
Timestamp: 2025-09-16T00:20:44.262Z
Learning: JWT 인증에서 카카오 ID와 유저 ID 분리 시, JWT subject를 userId 기반으로 통일하고 findByKakaoId()는 OAuth 로그인 시에만 사용하는 것이 일관성 있는 아키텍처입니다.

Applied to files:

  • src/main/java/com/example/onlyone/domain/user/repository/UserRepository.java
🪛 actionlint (1.7.7)
.github/workflows/github-actions.yml

54-54: the runner of "aws-actions/configure-aws-credentials@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🪛 YAMLlint (1.37.1)
.github/workflows/github-actions.yml

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

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

🔇 Additional comments (17)
src/main/java/com/example/onlyone/domain/wallet/controller/WalletController.java (1)

20-20: 베이스 경로 변경에 따른 소비자 영향 점검

  • "/api/users/wallet"로의 변경은 클라이언트/게이트웨이 라우팅/문서 스펙에 영향이 큽니다. 리다이렉트/백워드 호환(임시 alias) 필요 여부를 확인해 주세요.
src/main/java/com/example/onlyone/domain/club/controller/ClubController.java (1)

20-20: 베이스 경로 "/api/clubs" 변경 검증

  • 외부 호출자, 스웨거 문서, 시큐리티 매칭(permitAll/인가 규칙) 업데이트가 모두 반영됐는지 확인 바랍니다. 구 경로에 대한 임시 호환이 필요한지 여부도 점검해주세요.
src/main/java/com/example/onlyone/domain/club/document/ClubDocument.java (1)

19-21: @setting 파일명/경로 불일치 확인 필요

src/main/java/com/example/onlyone/domain/club/document/ClubDocument.java의 @setting이 '/elasticsearch/club-settings.json'을 참조하는데, 리포지토리에는 'club-create-settings.json'과 'club-settings.json' 둘 다 존재합니다. 다음 중 하나를 적용하세요:

  • 의도한 파일이 club-create-settings.json이면 ClubDocument의 @setting을 '/elasticsearch/club-create-settings.json'로 수정.
  • 의도한 파일이 club-settings.json이면 club-create-settings.json을 삭제하거나 파일명을 club-settings.json으로 통일.
  • 빌드/배포 시 src/main/resources/elasticsearch/*.json이 최종 패키지에 포함되는지 확인.
src/main/resources/elasticsearch/club-mapping.json (1)

43-46: 확인 — createdAt 포맷(매핑 ↔ 앱) 일치; 운영 인덱스 혼재 여부 확인 필요

  • 검증 결과: src/main/resources/elasticsearch/club-mapping.json 및 src/main/resources/elasticsearch/club-create-settings.json의 "format": "yyyy-MM-dd'T'HH:mm:ss"와 src/main/java/com/example/onlyone/domain/club/document/ClubDocument.java에서 사용 중인 DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")가 일치합니다. 레포 내에서 createdAt 포맷 혼재 증거 없음.
  • 리스크: Instant.now().toString() 등 다른 포맷을 생성하는 코드가 존재하므로, 외부에서 ES로 직접 쓰거나 과거에 다른 포맷으로 색인된 문서가 있다면 인덱싱 실패/정렬 왜곡 발생 가능 — 운영 인덱스(_source.createdAt 샘플) 및 ingestion 경로 확인 필요.
  • 권장 (옵션): 외부 입력 가능성이 있으면 매핑을 관대하게 설정하여 호환성 확보. 예:
-      "format": "yyyy-MM-dd'T'HH:mm:ss"
+      "format": "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSS"

운영 인덱스에서 createdAt 포맷 샘플(예: _source.createdAt 5건) 또는 인제스트 경로를 공유하세요.

src/main/java/com/example/onlyone/domain/chat/controller/ChatRoomRestController.java (1)

15-15: /api 프리픽스 적용 확인

경로 변경 OK. 연관 클라이언트 호출·스웨거 경로 동기화만 확인 부탁드립니다.

src/main/java/com/example/onlyone/domain/feed/controller/FeedMainController.java (1)

28-28: /api 프리픽스 적용 확인

문제 없습니다. 캐싱/SEO 등 외부 노출 경로가 있다면 리다이렉트 설정도 함께 점검하세요.

src/main/java/com/example/onlyone/domain/notification/controller/NotificationController.java (1)

20-20: /api 프리픽스 적용 확인

정상입니다. 푸시/알림센터 링크가 하드코딩된 곳은 없는지 확인만 부탁드립니다.

src/main/java/com/example/onlyone/domain/search/controller/SearchController.java (1)

16-16: /api 프리픽스 적용 확인

경로 변경 OK. 검색 페이지/딥링크의 베이스 URL 업데이트 여부만 확인해 주세요.

src/main/java/com/example/onlyone/domain/schedule/controller/ScheduleController.java (1)

22-22: /api 프리픽스 적용 확인 — 추가 검증 필요

경로 변경 자체는 OK. 보안/문서/클라이언트 동기화(스웨거, 프런트, 게이트웨이/LB 리라이트, CORS) 점검 필요.

  • 실행 결과: 원래 스크립트의 첫 정규식이 PCRE2 오류로 실패하여 클래스 레벨 @RequestMapping 중 '/api' 미사용 항목 전수검사가 완료되지 않았음.
  • 로그아웃 경로 발견: src/main/java/com/example/onlyone/global/filter/JwtAuthenticationFilter.java (약 70행) 에서 "/auth/logout" 사용됨 — 이 경로를 '/api' 하위로 이동할지 예외로 둘지 의도 확인 필요.
  • 재검증 권장(간단·안정형):
#!/bin/bash
# 클래스 레벨 @RequestMapping 전수 출력(수동검토)
rg -n --type java '^\s*@RequestMapping' -C2

# @RequestMapping 블록 중 '/api' 문자열이 없는 항목만 빠르게 필터링(출력은 수동검토 필요)
rg -n --type java '^\s*@RequestMapping' -C2 | rg -v '/api'
src/main/java/com/example/onlyone/domain/feed/controller/FeedController.java (1)

26-26: /api 프리픽스 변경에 따른 호환성 점검 요청

보안/문서/클라이언트 동기화 확인 부탁드립니다: SecurityConfig 화이트리스트, CORS, Swagger 서버 URL, API 게이트웨이/프론트 호출 경로. 기존 비-/api 경로에 대한 일시적 리다이렉트/관용 처리 필요 여부도 검토해주세요.

src/main/java/com/example/onlyone/domain/chat/controller/MessageRestController.java (1)

23-23: 경로 변경 LGTM

컨트롤러 베이스 경로의 /api 일괄 전환과 일치합니다.

src/main/java/com/example/onlyone/domain/club/service/ClubService.java (1)

53-55: 리더 UserClub 생성 전 memberCount 선증가 — 널 가드/롤백 보장 확인

@transactional이라 예외 시 롤백되지만, Club.incrementMemberCount()가 null 초기화→증가를 안전하게 처리하는지 재확인 부탁드립니다(요청대로라면 1로 초기화).

src/main/java/com/example/onlyone/domain/settlement/controller/SettlementController.java (1)

20-20: 경로 변경 LGTM — 호출자/문서 동기화 확인

클라이언트와 API 문서가 /api 프리픽스에 맞게 반영되었는지 확인 부탁드립니다.

src/main/java/com/example/onlyone/domain/user/controller/UserController.java (1)

28-28: 경로 변경 LGTM — 보안 화이트리스트 영향 없음 확인

화이트리스트가 필요 없는 보호 API이므로 영향 미미하나, 프런트 호출 경로/Swagger 서버 URL 업데이트 여부만 확인 바랍니다.

src/main/java/com/example/onlyone/domain/chat/service/MessageService.java (1)

157-159: JDK 요구사항 확인(toList)

Stream.toList()는 JDK 16+. 빌드 JDK가 17 이상인지 파이프라인에서 확인 바랍니다.

src/main/java/com/example/onlyone/domain/user/controller/AuthController.java (1)

71-76: 예외 로깅 시 민감정보 포함 여부 점검

현재 메시지 중심 로깅이지만, 스택트레이스에 토큰/코드가 노출되지 않도록 상위 계층 로거 설정도 재검토 바랍니다.

src/main/java/com/example/onlyone/global/config/SecurityConfig.java (1)

55-70: AUTH_WHITELIST의 /api 전환 일관성 확인 — 로컬 재검증 필요

샌드박스에서 자동 스크립트가 실패했습니다. 아래 수정된 스크립트를 로컬에서 실행해 /api로 통일되지 않은 공개(비-/api) 매핑이 있는지(의도적 제외: /api, /ws, /actuator, /swagger, /v3, /error, /favicon) 결과를 올려 주세요.

#!/bin/bash
set -euo pipefail
TMP=$(mktemp)
rg -l --type java -S '@(RequestMapping|GetMapping|PostMapping|PutMapping|DeleteMapping|PatchMapping|MessageMapping)\s*\(' src/main/java > "$TMP" || true
if [ ! -s "$TMP" ]; then
  echo "NO_MAPPING_FILES"
  rm -f "$TMP"
  exit 0
fi

python3 - "$TMP" <<'PY'
import re,sys
tmp = sys.argv[1]
exclude_prefixes = ('/api','/ws','/actuator','/swagger','/v3','/error','/favicon')
pattern = re.compile(r'@(RequestMapping|GetMapping|PostMapping|PutMapping|DeleteMapping|PatchMapping|MessageMapping)\s*\(([^)]*)\)', re.S)
found = []
with open(tmp,'r',encoding='utf-8') as fh:
    files = [l.strip() for l in fh if l.strip()]
for f in files:
    try:
        s = open(f,'r',encoding='utf-8').read()
    except Exception as e:
        print(f"ERROR reading {f}: {e}", file=sys.stderr)
        continue
    for m in pattern.finditer(s):
        inside = m.group(2)
        lits = re.findall(r'"([^"]+)"', inside)
        for lit in lits:
            p = lit.strip()
            if not p.startswith('/'):
                continue
            if not any(p.startswith(pref) for pref in exclude_prefixes):
                lineno = s[:m.start()].count('\n') + 1
                found.append(f"{f}:{lineno}:{m.group(1)} -> {p}")
if not found:
    print("NO_NON_API_MAPPINGS_FOUND")
else:
    for line in found:
        print(line)
PY

rm -f "$TMP"

Comment on lines +53 to 55
- name: AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

aws-actions/configure-aws-credentials 버전 업데이트

  • actionlint 경고대로 v2는 더 이상 권장되지 않습니다. v4로 올리세요.
-      - name: AWS credentials
-        uses: aws-actions/configure-aws-credentials@v2
+      - name: AWS credentials
+        uses: aws-actions/configure-aws-credentials@v4
         with:
           aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
           aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
           aws-region: ap-northeast-2

가능하면 OIDC(Role to Assume)로 전환해 키 제거도 검토하세요.

📝 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
- name: AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
- name: AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
aws-region: ap-northeast-2
🧰 Tools
🪛 actionlint (1.7.7)

54-54: the runner of "aws-actions/configure-aws-credentials@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

Comment on lines +57 to +58
@Field(type = FieldType.Date, pattern = "yyyy-MM-dd'T'HH:mm:ss")
private String createdAt;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

createdAt을 String으로 저장 + Date 필드 매핑: 타임존 주의

  • 문자열로 포맷팅해 전송할 때 타임존 정보를 잃습니다. ES는 타임존 미지정 값을 UTC로 해석하므로, 서버 로컬(예: Asia/Seoul) 시간으로 포맷팅 시 9시간 오프셋 문제가 생길 수 있습니다.

대안:

  • 가능하면 LocalDateTime 유지 + 멀티 패턴(혹은 strict_date_optional_time)으로 파싱.
  • 문자열 유지 시 서버 표준 시간대를 UTC로 고정하여 포맷팅하거나 오프셋 포함 패턴 사용(운영 제약이 없다면).
🤖 Prompt for AI Agents
In src/main/java/com/example/onlyone/domain/club/document/ClubDocument.java
around lines 57-58, the createdAt field is declared as String while annotated as
an Elasticsearch Date, which loses timezone info when formatted; change the
field type to java.time.LocalDateTime (or java.time.OffsetDateTime/Instant if
you need explicit offsets) and update mapping to accept multi-patterns (or use
strict_date_optional_time with offset) so ES parses correctly; alternatively, if
you must keep String, ensure all code formats timestamps to UTC or include the
offset in the string (e.g., use ISO_OFFSET_DATE_TIME) before indexing and update
the @Field pattern to match the offset-aware format.

Comment on lines +34 to 55
log.error("🔍 [DEBUG] kakaoLogin 시작 - code={}", code);

try {
// 1. 인증 코드로 카카오 액세스 토큰 받기
String kakaoAccessToken = kakaoService.getAccessToken(code);
log.error("✅ [DEBUG] kakaoAccessToken={}", kakaoAccessToken);

// 2. 카카오 액세스 토큰으로 사용자 정보 받기
Map<String, Object> kakaoUserInfo = kakaoService.getUserInfo(kakaoAccessToken);
log.error("✅ [DEBUG] kakaoUserInfo={}", kakaoUserInfo);

// 3. 사용자 정보 저장 또는 업데이트
Map<String, Object> loginResult = userService.processKakaoLogin(kakaoUserInfo, kakaoAccessToken);
User user = (User) loginResult.get("user");
boolean isNewUser = (boolean) loginResult.get("isNewUser");
log.error("✅ [DEBUG] userId={}, isNewUser={}", user.getUserId(), isNewUser);

// 4. JWT 토큰 생성 (Access + Refresh)
Map<String, String> tokens = userService.generateTokenPair(user);
log.error("✅ [DEBUG] tokens 생성됨 - accessToken={}, refreshToken={}",
tokens.get("accessToken"), tokens.get("refreshToken"));

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

심각: 인증 코드/토큰 등 민감정보 로그 유출

code, kakaoAccessToken, access/refreshToken, kakaoUserInfo를 그대로 로그로 남기고 있으며 log.error 레벨입니다. 즉시 제거/마스킹하세요.

적용 예시(민감정보 미출력, 로그 레벨 조정):

-        log.error("🔍 [DEBUG] kakaoLogin 시작 - code={}", code);
+        log.debug("kakaoLogin 시작");

-            log.error("✅ [DEBUG] kakaoAccessToken={}", kakaoAccessToken);
+            log.debug("kakaoAccessToken 수신");

-            log.error("✅ [DEBUG] kakaoUserInfo={}", kakaoUserInfo);
+            log.debug("kakaoUserInfo 수신");

-            log.error("✅ [DEBUG] userId={}, isNewUser={}", user.getUserId(), isNewUser);
+            log.debug("user resolved - userId={}, isNewUser={}", user.getUserId(), isNewUser);

-            log.error("✅ [DEBUG] tokens 생성됨 - accessToken={}, refreshToken={}",
-                    tokens.get("accessToken"), tokens.get("refreshToken"));
+            log.debug("tokens 생성 완료");

또한 성공 로그는 info, 실패는 warn/error로 일관되게 사용하세요.

📝 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
log.error("🔍 [DEBUG] kakaoLogin 시작 - code={}", code);
try {
// 1. 인증 코드로 카카오 액세스 토큰 받기
String kakaoAccessToken = kakaoService.getAccessToken(code);
log.error("✅ [DEBUG] kakaoAccessToken={}", kakaoAccessToken);
// 2. 카카오 액세스 토큰으로 사용자 정보 받기
Map<String, Object> kakaoUserInfo = kakaoService.getUserInfo(kakaoAccessToken);
log.error("✅ [DEBUG] kakaoUserInfo={}", kakaoUserInfo);
// 3. 사용자 정보 저장 또는 업데이트
Map<String, Object> loginResult = userService.processKakaoLogin(kakaoUserInfo, kakaoAccessToken);
User user = (User) loginResult.get("user");
boolean isNewUser = (boolean) loginResult.get("isNewUser");
log.error("✅ [DEBUG] userId={}, isNewUser={}", user.getUserId(), isNewUser);
// 4. JWT 토큰 생성 (Access + Refresh)
Map<String, String> tokens = userService.generateTokenPair(user);
log.error("✅ [DEBUG] tokens 생성됨 - accessToken={}, refreshToken={}",
tokens.get("accessToken"), tokens.get("refreshToken"));
log.debug("kakaoLogin 시작");
try {
// 1. 인증 코드로 카카오 액세스 토큰 받기
String kakaoAccessToken = kakaoService.getAccessToken(code);
log.debug("kakaoAccessToken 수신");
// 2. 카카오 액세스 토큰으로 사용자 정보 받기
Map<String, Object> kakaoUserInfo = kakaoService.getUserInfo(kakaoAccessToken);
log.debug("kakaoUserInfo 수신");
// 3. 사용자 정보 저장 또는 업데이트
Map<String, Object> loginResult = userService.processKakaoLogin(kakaoUserInfo, kakaoAccessToken);
User user = (User) loginResult.get("user");
boolean isNewUser = (boolean) loginResult.get("isNewUser");
log.debug("user resolved - userId={}, isNewUser={}", user.getUserId(), isNewUser);
// 4. JWT 토큰 생성 (Access + Refresh)
Map<String, String> tokens = userService.generateTokenPair(user);
log.debug("tokens 생성 완료");
🤖 Prompt for AI Agents
In src/main/java/com/example/onlyone/domain/user/controller/AuthController.java
around lines 34 to 55, remove or mask all sensitive values from logs (code,
kakaoAccessToken, tokens, full kakaoUserInfo) and stop logging them at error
level; instead log only non-sensitive context (e.g., "kakaoLogin started") and
safe identifiers (if needed, a non-sensitive userId or masked id) at info level
for success and use warn/error only for failures without sensitive details.
Update each debug log to omit the token/code/userinfo payloads (or replace with
a constant like "<masked>" if you must indicate presence) and change log.error
-> log.info for success traces; ensure any catch blocks log exception
messages/errors (not tokens) at warn/error level.

Comment on lines +56 to 59
// 5. refreshToken Redis에 저장 (local dev 시 주석 처리 가능)
// redisTemplate.opsForValue()
// .set(user.getUserId().toString(), tokens.get("refreshToken"), Duration.ofMillis(REFRESH_TOKEN_EXPIRE_TIME));

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

리프레시 토큰 미보관(주석 처리) — 무효화/로테이션 불가

운영에서 refreshToken 저장이 비활성화되면 강제 로그아웃/블랙리스트가 불가합니다. 프로파일/환경변수로 저장 기능을 토글하도록 처리하거나 최소한 운영 프로파일에서는 활성화하세요.

🤖 Prompt for AI Agents
In src/main/java/com/example/onlyone/domain/user/controller/AuthController.java
around lines 56 to 59, the refresh token persistence to Redis is commented out
which prevents token invalidation/rotation in production; restore and gate this
behavior behind a configuration flag (e.g., application.profile or an env var
like ENABLE_REFRESH_TOKEN_STORE) so that: when the flag is true (and in
production profile) the code writes the refresh token to Redis with the proper
expiration, otherwise skip storing for local/dev; wire this flag from
application.properties or Environment, default to true for production profiles,
and add logging for success/failure of the Redis write so ops can detect storage
issues.

// 사용자 상태 확인 - 탈퇴한 사용자인 경우 인증 거부, GUEST와 ACTIVE는 허용
Optional<User> userOpt = userRepository.findByKakaoId(kakaoId);
log.error("userOpt = {}", userOpt.orElse(null));
if (userOpt.isPresent()) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

PII/노이즈 로그: user 엔티티 직렬화 로깅 지양

에러 레벨로 엔티티 전체를 출력하면 PII 노출·로그 스팸 우려가 큽니다. presence 중심의 디버그 로그로 축소하세요.

다음 변경을 권장합니다.

-            log.error("userOpt = {}", userOpt.orElse(null));
+            if (log.isDebugEnabled()) {
+                log.debug("User lookup by kakaoId={} present={}", kakaoId, userOpt.isPresent());
+            }
📝 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
if (userOpt.isPresent()) {
if (log.isDebugEnabled()) {
log.debug("User lookup by kakaoId={} present={}", kakaoId, userOpt.isPresent());
}
if (userOpt.isPresent()) {
🤖 Prompt for AI Agents
In src/main/java/com/example/onlyone/global/filter/JwtAuthenticationFilter.java
around line 65, the code logs the entire user entity at error level which risks
PII exposure and noisy logs; replace that with a presence-focused, non-PII
debug/info log (e.g., "user present for token" or "user found" and optionally
log only a non-sensitive identifier or masked id), remove serialization of the
full entity from logs, and lower the log level from error to debug/info so only
presence is recorded without exposing sensitive fields.

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