Skip to content

feat: custom exception 추가#10

Open
jhlarry1109 wants to merge 3 commits into
mainfrom
feat/#9
Open

feat: custom exception 추가#10
jhlarry1109 wants to merge 3 commits into
mainfrom
feat/#9

Conversation

@jhlarry1109

@jhlarry1109 jhlarry1109 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

#️⃣ 연관된 이슈

관련된 이슈 번호를 작성해주세요.
Closes #9


📝 작업 내용

이번 PR에서 작업한 내용을 간략히 설명해주세요.
필요시 코드 블록이나 이미지 첨부도 가능합니다.

  • 추가/수정한 내용
  • 추가/수정한 내용
  • 추가/수정한 내용

💻 스크린샷 (선택)

UI 변경사항이 있다면 스크린샷을 첨부해주세요.


💬 리뷰 요청 사항 (선택)

리뷰어가 집중해서 봐줬으면 하는 부분이 있다면 작성해주세요.
예시: “함수 네이밍이 적절한지 봐주세요.”, “이 로직의 효율성에 대한 의견이 궁금합니다.”


Summary by CodeRabbit

릴리스 노트

  • Documentation

    • API 에러 응답 스키마를 표준화하고 상세 문서화했습니다.
    • 예외 처리 정책 및 HTTP 상태 코드 매핑을 명시했습니다.
  • Chores

    • 자동 배포 워크플로우를 추가하여 주요 브랜치 변경 시 Docker 이미지 빌드 및 배포를 자동화했습니다.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jhlarry1109, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 58 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7c06e5f5-9c14-47f9-8470-72aa4329bb9c

📥 Commits

Reviewing files that changed from the base of the PR and between bfe4607 and 8d06a74.

📒 Files selected for processing (2)
  • src/main/java/com/example/auth/global/client/UserServiceClient.java
  • src/main/java/com/example/auth/global/exception/GlobalExceptionHandler.java
📝 Walkthrough

Walkthrough

AuthException 기반 커스텀 예외 계층(BadRequestException, UnauthorizedException, GoogleOauthException, TokenNotFoundException)을 신규 도입하고, GlobalExceptionHandler로 HTTP 상태 코드에 매핑한다. 서비스 레이어의 IllegalArgumentException을 교체하며, API 스펙과 문서를 갱신한다. 또한 ECS 배포 GitHub Actions 워크플로를 추가한다.

Changes

커스텀 예외 처리 인프라 및 서비스 통합

Layer / File(s) Summary
예외 계층 구조 및 ApiResponse.fail() 추가
src/main/java/com/example/auth/global/exception/AuthException.java, ...BadRequestException.java, ...UnauthorizedException.java, ...GoogleOauthException.java, ...TokenNotFoundException.java, src/main/java/com/example/auth/global/dto/ApiResponse.java
AuthException extends RuntimeException을 기반으로 4개 서브클래스를 신규 정의하고, ApiResponsefail(String message) 정적 팩토리 메서드를 추가한다.
GlobalExceptionHandler: 예외→HTTP 응답 매핑
src/main/java/com/example/auth/global/exception/GlobalExceptionHandler.java
@RestControllerAdvice로 각 예외 타입을 400/401/502/500 상태 코드와 ApiResponse.fail() 바디로 매핑하는 핸들러를 정의한다.
서비스 레이어 예외 타입 교체
src/main/java/com/example/auth/service/AuthValidator.java, ...AuthService.java, ...TokenService.java
AuthValidator의 모든 validate* 메서드, AuthService.loginWithGoogle, TokenService.getValidRefreshTokenSubject에서 IllegalArgumentException을 각각 도메인 예외로 교체한다.
API 스펙 및 문서 업데이트
API_SPEC.yaml, docs/service-overview.md, docs/service-policy.md
ErrorResponse 스키마와 4종 에러 응답 컴포넌트를 API 스펙에 추가하고, 예외 처리 섹션을 실제 구현 기준으로 문서화한다.

ECS 배포 CI/CD 워크플로

Layer / File(s) Summary
GitHub Actions ECS 배포 워크플로
.github/workflows/deploy.yml
main 푸시 및 workflow_dispatch 시 Docker 이미지를 linux/amd64로 빌드해 ECR에 푸시하고, force-new-deployment로 ECS 서비스를 재배포한 뒤 안정화 대기 단계를 수행한다.

추정 코드 리뷰 노력

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 토끼가 예외를 잡아내네,
BadRequest, Unauthorized, 하나씩 정리해,
GlobalHandler가 깔끔히 응답하고,
ECR에 이미지 올려 ECS 달려가네 🚀
커스텀 예외로 세상이 더 맑아졌다네! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 4

❌ Failed checks (4 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목 'Feat/#9'는 이슈 번호만 포함하고 실제 변경사항(custom exception 구현)을 명확히 설명하지 못함. PR 제목을 'Add custom exception handling with error responses'와 같이 구체적인 변경사항을 명시하는 형태로 개선해주세요.
Description check ⚠️ Warning PR 설명이 대부분 빈 템플릿 상태이며, 작업 내용 체크박스가 미완성이고 실제 변경사항이 기록되지 않았음. 작업 내용 섹션에서 추가된 예외 클래스, API 스펙, 배포 워크플로우, 서비스 정책 문서 등의 변경사항을 체크박스와 함께 구체적으로 작성해주세요.
Out of Scope Changes check ⚠️ Warning GitHub Actions 배포 워크플로우(.github/workflows/deploy.yml)는 이슈 #9의 'custom exception' 요구사항과 무관한 변경사항으로 판단됨. GitHub Actions 배포 워크플로우 변경사항을 별도의 이슈/PR로 분리하거나, 현재 PR의 목적과 연관성을 설명해주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Linked Issues check ✅ Passed 이슈 #9의 'custom exception 추가' 요구사항은 충족되었으나, PR에는 GitHub Actions 배포 워크플로우 등 추가 변경사항이 포함되어 있음.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#9

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.

@jhlarry1109 jhlarry1109 changed the title Feat/#9 feat: custom exception 추가 Jun 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
.github/workflows/deploy.yml (3)

15-15: ⚡ Quick win

배포 추적성 개선: latest 대신 고유 태그 사용 권장

IMAGE_TAG: latest를 사용하면 어떤 커밋이 배포되었는지 추적이 어렵고, 롤백 시 이전 이미지를 식별할 수 없습니다. Git SHA나 타임스탬프 기반 태그를 권장합니다.

♻️ Git SHA 기반 태그 사용 예시
 env:
   AWS_REGION: ap-northeast-2
   AWS_ACCOUNT_ID: 727452759104
   ECR_REPOSITORY: momentlit/auth
   ECS_CLUSTER: default
   ECS_SERVICE: momentlit-auth-service
-  IMAGE_TAG: latest
+  IMAGE_TAG: ${{ github.sha }}

또는 latest와 SHA 태그를 함께 푸시하여 추적성과 편의성을 모두 확보할 수 있습니다:

      - name: Build and push Docker image
        run: |
          IMAGE_URI_BASE=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${ECR_REPOSITORY}
          
          docker buildx build \
            --platform linux/amd64 \
            --provenance=false \
            -t ${IMAGE_URI_BASE}:${{ github.sha }} \
            -t ${IMAGE_URI_BASE}:latest \
            . \
            --push
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml at line 15, The IMAGE_TAG environment variable
is set to "latest" which does not provide deployment traceability since multiple
commits will all point to the same tag, making it impossible to identify which
specific commit was deployed or to properly rollback. Replace the IMAGE_TAG
environment variable with a unique identifier such as github.sha (the commit
hash). Additionally, when building and pushing the Docker image in the docker
buildx build step, tag the image with both the unique SHA-based tag and the
"latest" tag to maintain both traceability for debugging and a convenient latest
reference for quick deployments.

58-63: 💤 Low value

타임아웃 및 실패 처리 고려

aws ecs wait services-stable은 기본적으로 최대 40분(40회 폴링 × 60초)까지 대기합니다. 배포 실패 시 긴 대기 후에야 워크플로가 실패하므로, 적절한 타임아웃 설정을 고려해 주세요.

⏱️ 타임아웃 설정 예시
       - name: Wait for ECS service stable
+        timeout-minutes: 15
         run: |
           aws ecs wait services-stable \
             --cluster $ECS_CLUSTER \
             --services $ECS_SERVICE \
             --region $AWS_REGION
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 58 - 63, The aws ecs wait
services-stable command has a default maximum wait time of 40 minutes, which
means the workflow will take a long time to fail if the ECS service fails to
stabilize. Add a timeout-minutes property to the "Wait for ECS service stable"
step to set a reasonable timeout limit (e.g., 10-15 minutes), ensuring the
workflow fails faster in case of deployment issues. This can be added at the
same indentation level as the run property in the YAML step definition.

24-37: 공급망 보안: GitHub Actions를 SHA 해시로 고정하기

현재 모든 GitHub Actions가 버전 태그(@v4, @v5 등)로 참조되어 있습니다. 악의적인 태그 변조에 대비해 다음과 같이 SHA 해시로 고정하는 것이 보안 모범 사례입니다:

  • actions/checkout@v4actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
  • aws-actions/configure-aws-credentials@v5aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708
  • aws-actions/amazon-ecr-login@v2aws-actions/amazon-ecr-login@d539f0932e70871a027e9d5a9d8fc38589180a64
  • docker/setup-buildx-action@v3docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 24 - 37, The GitHub Actions in the
deploy workflow are currently pinned to version tags (such as `@v4`, `@v5`, `@v2`,
`@v3`) which are vulnerable to tag tampering attacks. Replace all version tag
references with their corresponding SHA hash values for supply chain security:
update actions/checkout from `@v4` to the provided SHA hash, update
aws-actions/configure-aws-credentials from `@v5` to its SHA hash, update
aws-actions/amazon-ecr-login from `@v2` to its SHA hash, and update
docker/setup-buildx-action from `@v3` to its SHA hash. This ensures the workflow
uses immutable commit references instead of mutable tags.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/deploy.yml:
- Around line 17-24: The GitHub Actions workflow needs security hardening to
follow the principle of least privilege. Add a top-level permissions block to
the workflow (at the same level as the jobs section) to explicitly declare
minimal required permissions, and then add persist-credentials: false parameter
to the checkout@v4 step to prevent Git credentials from being stored in the
runner environment. This ensures the workflow only requests and retains the
minimum permissions necessary for the build and deploy operation.

In `@API_SPEC.yaml`:
- Around line 25-61: The error response examples in the responses section
(BadRequestError, UnauthorizedError, GoogleOauthError, InternalServerError)
contain error codes embedded in the message field as prefixes like "[ERROR:
Request/BadRequest]". Extract these error code prefixes from the message field
and create a separate `code` field for each error response example. Update
BadRequestError to have code "REQUEST_BAD_REQUEST", UnauthorizedError to have
code "AUTH_UNAUTHORIZED", GoogleOauthError to have code "AUTH_OAUTH_GOOGLE", and
InternalServerError to have an appropriate code value. Keep only the Korean
error message text (without the prefix) in the message field for each example.
- Around line 13-24: The ErrorResponse schema in API_SPEC.yaml violates the
coding guideline requiring error responses to have both 'code' and 'message'
fields as separate properties. Currently, the error code is embedded as a string
prefix in the 'message' field (e.g., "[ERROR: Request/BadRequest]"), forcing
clients to parse strings instead of using structured data. Add a new 'code'
property to the ErrorResponse schema as a string type, remove the error code
prefix from the message example, and update the 'required' array to include both
'code' and 'message'. Additionally, ensure that the ApiResponse DTO class and
GlobalExceptionHandler's response creation logic are modified to populate the
separate 'code' and 'message' fields independently rather than combining them
into a single message string.

In `@src/main/java/com/example/auth/global/dto/ApiResponse.java`:
- Around line 3-9: The ApiResponse record currently lacks a code field, and the
fail method only accepts a message parameter with error codes embedded as string
prefixes. Add a code field to the ApiResponse record to store error codes
separately, then update the fail method signature to accept both code and
message as distinct parameters. This will ensure error responses follow a
consistent structure with properly separated code and message fields, allowing
clients to parse error codes structurally rather than relying on string parsing.

In `@src/main/java/com/example/auth/service/TokenService.java`:
- Around line 52-56: Remove the unnecessary `IllegalArgumentException` from the
catch block in the TokenService token validation logic. The
`jwtProvider.validateToken()` method only throws `JwtException` and its
subclasses through JJWT's parseSignedClaims() method, so catching
`IllegalArgumentException` is redundant and can mask actual bugs. Change the
catch clause to only catch `JwtException`, keeping the same
UnauthorizedException throw statement that handles the validation failure.

---

Nitpick comments:
In @.github/workflows/deploy.yml:
- Line 15: The IMAGE_TAG environment variable is set to "latest" which does not
provide deployment traceability since multiple commits will all point to the
same tag, making it impossible to identify which specific commit was deployed or
to properly rollback. Replace the IMAGE_TAG environment variable with a unique
identifier such as github.sha (the commit hash). Additionally, when building and
pushing the Docker image in the docker buildx build step, tag the image with
both the unique SHA-based tag and the "latest" tag to maintain both traceability
for debugging and a convenient latest reference for quick deployments.
- Around line 58-63: The aws ecs wait services-stable command has a default
maximum wait time of 40 minutes, which means the workflow will take a long time
to fail if the ECS service fails to stabilize. Add a timeout-minutes property to
the "Wait for ECS service stable" step to set a reasonable timeout limit (e.g.,
10-15 minutes), ensuring the workflow fails faster in case of deployment issues.
This can be added at the same indentation level as the run property in the YAML
step definition.
- Around line 24-37: The GitHub Actions in the deploy workflow are currently
pinned to version tags (such as `@v4`, `@v5`, `@v2`, `@v3`) which are vulnerable to tag
tampering attacks. Replace all version tag references with their corresponding
SHA hash values for supply chain security: update actions/checkout from `@v4` to
the provided SHA hash, update aws-actions/configure-aws-credentials from `@v5` to
its SHA hash, update aws-actions/amazon-ecr-login from `@v2` to its SHA hash, and
update docker/setup-buildx-action from `@v3` to its SHA hash. This ensures the
workflow uses immutable commit references instead of mutable tags.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cd01533e-96ae-4e6b-bfb4-f1198e0debf5

📥 Commits

Reviewing files that changed from the base of the PR and between 9d6e19a and bfe4607.

📒 Files selected for processing (14)
  • .github/workflows/deploy.yml
  • API_SPEC.yaml
  • docs/service-overview.md
  • docs/service-policy.md
  • src/main/java/com/example/auth/global/dto/ApiResponse.java
  • src/main/java/com/example/auth/global/exception/AuthException.java
  • src/main/java/com/example/auth/global/exception/BadRequestException.java
  • src/main/java/com/example/auth/global/exception/GlobalExceptionHandler.java
  • src/main/java/com/example/auth/global/exception/GoogleOauthException.java
  • src/main/java/com/example/auth/global/exception/TokenNotFoundException.java
  • src/main/java/com/example/auth/global/exception/UnauthorizedException.java
  • src/main/java/com/example/auth/service/AuthService.java
  • src/main/java/com/example/auth/service/AuthValidator.java
  • src/main/java/com/example/auth/service/TokenService.java

Comment on lines +17 to +24
jobs:
deploy:
name: Build and Deploy Auth
runs-on: ubuntu-latest

steps:
- name: Checkout source code
uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

보안 강화: permissions 블록 추가 및 persist-credentials: false 설정 필요

정적 분석에서 지적한 대로, 최소 권한 원칙을 위해 명시적 권한 블록을 추가하고, checkout 단계에서 자격 증명 지속을 비활성화해야 합니다.

🛡️ 권장 수정 사항
 jobs:
   deploy:
     name: Build and Deploy Auth
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
+      id-token: write

     steps:
       - name: Checkout source code
         uses: actions/checkout@v4
+        with:
+          persist-credentials: false
📝 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
jobs:
deploy:
name: Build and Deploy Auth
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
jobs:
deploy:
name: Build and Deploy Auth
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout source code
uses: actions/checkout@v4
with:
persist-credentials: false
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 23-24: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 24-24: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 17 - 24, The GitHub Actions
workflow needs security hardening to follow the principle of least privilege.
Add a top-level permissions block to the workflow (at the same level as the jobs
section) to explicitly declare minimal required permissions, and then add
persist-credentials: false parameter to the checkout@v4 step to prevent Git
credentials from being stored in the runner environment. This ensures the
workflow only requests and retains the minimum permissions necessary for the
build and deploy operation.

Source: Linters/SAST tools

Comment thread API_SPEC.yaml
Comment on lines +13 to +24
ErrorResponse:
type: object
properties:
message:
type: string
example: "[ERROR: Request/BadRequest] Refresh Token을 입력해주세요."
data:
nullable: true
example: null
required:
- message
- data

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

에러 응답 스키마가 코딩 가이드라인을 위반합니다.

코딩 가이드라인은 "API error responses should have a consistent format with 'code' and 'message' fields"를 요구하지만, 현재 ErrorResponse 스키마는 code 필드가 없고 messagedata 필드만 있습니다. 에러 코드가 메시지 문자열에 접두사로 포함되어 있어(예: "[ERROR: Request/BadRequest]") 클라이언트가 구조화된 데이터 대신 문자열 파싱을 해야 합니다.

다음과 같이 구조화된 스키마로 변경하는 것을 권장합니다:

ErrorResponse:
  type: object
  properties:
    code:
      type: string
      example: "REQUEST_BAD_REQUEST"
    message:
      type: string
      example: "Refresh Token을 입력해주세요."
    data:
      nullable: true
      example: null
  required:
    - code
    - message

이 변경은 ApiResponse DTO와 GlobalExceptionHandler의 응답 생성 로직도 함께 수정해야 합니다.

As per coding guidelines, "API error responses should have a consistent format with 'code' and 'message' fields."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@API_SPEC.yaml` around lines 13 - 24, The ErrorResponse schema in
API_SPEC.yaml violates the coding guideline requiring error responses to have
both 'code' and 'message' fields as separate properties. Currently, the error
code is embedded as a string prefix in the 'message' field (e.g., "[ERROR:
Request/BadRequest]"), forcing clients to parse strings instead of using
structured data. Add a new 'code' property to the ErrorResponse schema as a
string type, remove the error code prefix from the message example, and update
the 'required' array to include both 'code' and 'message'. Additionally, ensure
that the ApiResponse DTO class and GlobalExceptionHandler's response creation
logic are modified to populate the separate 'code' and 'message' fields
independently rather than combining them into a single message string.

Source: Coding guidelines

Comment thread API_SPEC.yaml
Comment on lines +25 to +61
responses:
BadRequestError:
description: "Invalid request input."
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
message: "[ERROR: Request/BadRequest] Refresh Token을 입력해주세요."
data: null
UnauthorizedError:
description: "Authentication failed or token is invalid."
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
message: "[ERROR: Auth/Unauthorized] 유효하지 않은 Refresh Token입니다."
data: null
GoogleOauthError:
description: "Google OAuth request failed."
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
message: "[ERROR: Auth/Oauth/Google] Google Access Token을 발급받을 수 없습니다."
data: null
InternalServerError:
description: "Unexpected server error."
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
message: "[ERROR: ?/?] 서버 내부 오류가 발생했습니다."
data: null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

에러 응답 컴포넌트의 예시가 구조화되지 않은 에러 코드를 포함합니다.

각 에러 응답 예시의 message 필드에 "[ERROR: Request/BadRequest]" 같은 접두사가 포함되어 있습니다. 위에서 언급한 대로 code 필드를 별도로 분리하면, 예시도 다음과 같이 업데이트되어야 합니다:

BadRequestError:
  description: "Invalid request input."
  content:
    application/json:
      schema:
        $ref: "`#/components/schemas/ErrorResponse`"
      example:
        code: "REQUEST_BAD_REQUEST"
        message: "Refresh Token을 입력해주세요."
        data: null
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@API_SPEC.yaml` around lines 25 - 61, The error response examples in the
responses section (BadRequestError, UnauthorizedError, GoogleOauthError,
InternalServerError) contain error codes embedded in the message field as
prefixes like "[ERROR: Request/BadRequest]". Extract these error code prefixes
from the message field and create a separate `code` field for each error
response example. Update BadRequestError to have code "REQUEST_BAD_REQUEST",
UnauthorizedError to have code "AUTH_UNAUTHORIZED", GoogleOauthError to have
code "AUTH_OAUTH_GOOGLE", and InternalServerError to have an appropriate code
value. Keep only the Korean error message text (without the prefix) in the
message field for each example.

Source: Coding guidelines

Comment on lines 3 to +9
public record ApiResponse<T> (
String message,
T data
){}
){
public static <T> ApiResponse<T> fail(String message) {
return new ApiResponse<>(message, null);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

에러 응답 포맷을 code/message 분리 구조로 맞춰주세요.

현재 fail(String message)messagedata=null만 반환해서, 에러 코드를 문자열 접두사로 붙이는 구현에 의존하게 됩니다. 이 구조는 전역 예외 응답의 계약을 불안정하게 만들고, 클라이언트가 code를 구조적으로 파싱할 수 없습니다.

변경 예시
-public record ApiResponse<T> (
-    String message,
-    T data
-){
-    public static <T> ApiResponse<T> fail(String message) {
-        return new ApiResponse<>(message, null);
-    }
+public record ApiResponse<T> (
+    String code,
+    String message,
+    T data
+){
+    public static <T> ApiResponse<T> fail(String code, String message) {
+        return new ApiResponse<>(code, message, null);
+    }
 }

As per coding guidelines, "API error responses should have a consistent format with 'code' and 'message' fields."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/example/auth/global/dto/ApiResponse.java` around lines 3 -
9, The ApiResponse record currently lacks a code field, and the fail method only
accepts a message parameter with error codes embedded as string prefixes. Add a
code field to the ApiResponse record to store error codes separately, then
update the fail method signature to accept both code and message as distinct
parameters. This will ensure error responses follow a consistent structure with
properly separated code and message fields, allowing clients to parse error
codes structurally rather than relying on string parsing.

Source: Coding guidelines

Comment on lines +52 to +56
try {
jwtProvider.validateToken(refreshToken);
} catch (JwtException | IllegalArgumentException exception) {
throw new UnauthorizedException("유효하지 않은 Refresh Token입니다.");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# JwtProvider.validateToken() 메서드의 예외 선언 확인
rg -nP -A 10 'void\s+validateToken\s*\(' --type=java -g '*JwtProvider*'

Repository: MomentLit/Auth

Length of output: 40


🌐 Web query:

JJWT 0.12.6 JwtParser parseSignedClaims exceptions

💡 Result:

In JJWT 0.12.6, the JwtParser.parseSignedClaims(CharSequence) method is designed to parse and verify signed JWTs (JWS). Because this operation involves both parsing and cryptographic verification, it can throw various exceptions derived from the base io.jsonwebtoken.JwtException class [1][2]. Common exceptions include: 1. SignatureException: Thrown if the JWT signature verification fails [3]. 2. ExpiredJwtException: Thrown if the JWT has expired (e.g., the 'exp' claim is in the past) [4][3]. 3. UnsupportedJwtException: Thrown if the parsed JWT does not match the expected format or type (e.g., if you call parseSignedClaims on a token that is not a JWS) [1][2][3]. 4. MalformedJwtException: Thrown if the JWT string is not correctly formatted or is otherwise unparseable [1][2]. 5. MissingClaimException: Thrown if a required claim is missing. 6. PrematureJwtException: Thrown if the JWT is not yet valid (e.g., the 'nbf' claim is in the future). Since these exceptions all extend from JwtException, it is recommended to wrap the parseSignedClaims call in a try-catch block to handle authentication or validation failures [1][2][3]: try { Claims claims = Jwts.parser.verifyWith(secretKey).build.parseSignedClaims(token).getPayload; // Token is valid and trusted } catch (JwtException e) { // Token is invalid, expired, or failed signature verification } Note that in version 0.12.6, JwtParser is immutable, and you must use the Jwts.parser method to obtain a JwtParserBuilder, configure it, and call build before parsing [5][6][7]. Using the incorrect method for the token type (e.g., trying to parse an unsigned JWT with parseSignedClaims) will trigger an UnsupportedJwtException [1][2].

Citations:


🏁 Script executed:

fd -type f -name "*.java" | head -20

Repository: MomentLit/Auth

Length of output: 229


🏁 Script executed:

find . -type f -name "*JwtProvider*" 2>/dev/null

Repository: MomentLit/Auth

Length of output: 124


🏁 Script executed:

rg "class JwtProvider" --type=java -A 5

Repository: MomentLit/Auth

Length of output: 615


🏁 Script executed:

cat -n src/main/java/com/example/auth/service/TokenService.java | head -80

Repository: MomentLit/Auth

Length of output: 2958


🏁 Script executed:

cat -n src/main/java/com/example/auth/global/security/JwtProvider.java

Repository: MomentLit/Auth

Length of output: 4740


IllegalArgumentException 포착을 제거하세요.

JwtProvider.validateToken()은 JJWT의 parseSignedClaims()을 통해 오직 JwtException 및 그 하위 예외만 던집니다. IllegalArgumentException은 이 검증 과정에서 발생하지 않으므로, 이를 포착하는 것은 불필요하며 실제 버그를 은폐할 수 있습니다. catch (JwtException exception)으로 변경하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/example/auth/service/TokenService.java` around lines 52 -
56, Remove the unnecessary `IllegalArgumentException` from the catch block in
the TokenService token validation logic. The `jwtProvider.validateToken()`
method only throws `JwtException` and its subclasses through JJWT's
parseSignedClaims() method, so catching `IllegalArgumentException` is redundant
and can mask actual bugs. Change the catch clause to only catch `JwtException`,
keeping the same UnauthorizedException throw statement that handles the
validation failure.

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.

[✨ Feature] custom exception

1 participant