Skip to content

⚙️ chore: Flyway 및 공통 API 응답 기반 구성#10

Merged
JoonKyoLee merged 6 commits into
developfrom
chore/flyway-common-response
Jul 15, 2026
Merged

⚙️ chore: Flyway 및 공통 API 응답 기반 구성#10
JoonKyoLee merged 6 commits into
developfrom
chore/flyway-common-response

Conversation

@JoonKyoLee

@JoonKyoLee JoonKyoLee commented Jul 15, 2026

Copy link
Copy Markdown
Member

#️⃣ Issue Number

📝 요약(Summary)

  • Flyway 의존성과 기본 설정을 추가해 이후 기능별 migration을 적용할 수 있는 기반을 구성했습니다.
  • 공통 성공/실패 응답 포맷과 전역 예외 처리 구조를 도입해 API 응답 규약을 일관되게 맞췄습니다.
  • X-Device-Id 헤더를 공통 계층에서 검증하도록 Interceptor를 추가하고, 유저 등록 API를 제외한 요청에 공통 규칙이 적용되도록 구성했습니다.
  • Spring Security 기본 차단 정책을 해제하고, 현재 단계에서는 요청 헤더 규칙 중심으로 API 진입 제어가 가능하도록 정리했습니다.

📝 리뷰 요청사항

  • 초기 스키마를 한 번에 고정하지 않고, 기능 구현 시점에 맞춰 migration을 추가할 수 있도록 Flyway 기반만 먼저 구성했습니다.
  • 성공 응답과 실패 응답을 동일한 envelope 구조로 맞춰 이후 도메인 API가 같은 응답 계약을 따르도록 정리했습니다.
  • X-Device-Id 검증은 컨트롤러마다 반복하지 않고 Interceptor에서 공통 처리하도록 구성했습니다.
  • 현재 보안 설정은 인증/인가 도입 전 단계이므로 permitAll()로 열고, API 공통 규칙은 MVC 계층에서 우선 강제하도록 분리했습니다.

💻 테스트 결과

  • ./gradlew -q compileKotlin 기준으로 공통 응답/예외 처리 및 디바이스 헤더 처리 코드가 정상 컴파일되는지 확인했습니다.
  • X-Device-Id 누락 시 공통 예외 응답으로 처리되는 구조를 코드 기준으로 반영했습니다.
  • 유저 등록 API(/api/v1/users/register)는 헤더 검증 대상에서 제외되도록 설정했습니다.
  • 실제 엔드포인트 호출 기반 검증은 후속 기능 API 구현 이후 추가 확인 예정입니다.

📌 포인트

  • 기능 구현 전에 응답 형식과 예외 포맷을 먼저 고정해 이후 API의 일관성을 확보할 수 있는 기반을 만들었습니다.
  • DB 스키마를 선반영하지 않고, 정책이 확정되는 기능부터 migration을 쌓는 방향으로 설계해 초기 과설계를 피했습니다.
  • 디바이스 식별 헤더를 공통 계층으로 끌어올려 컨트롤러 중복과 누락 가능성을 줄였습니다.

✍️ 회고

  • 이번 작업에서는 기능 구현보다 먼저 “공통 규약”을 세우는 것이 이후 개발 속도와 일관성에 더 중요하다고 판단했습니다.
  • 특히 응답 포맷, 예외 처리, 요청 헤더 규칙을 먼저 정리해두면 이후 도메인 기능 구현 시 비즈니스 로직에 더 집중할 수 있다는 점을 확인했습니다.
  • 스키마 역시 한 번에 고정하지 않고 기능 단위로 점진적으로 도입하는 방향이 현재 정책 불확실성에 더 잘 맞는다고 정리할 수 있었습니다.

✅ 리뷰 반영

  • 405 응답의 Allow 헤더 손실 가능성과 광역 예외 처리로 인한 4xx → 500 변환 가능성을 확인했습니다.
  • 전역 예외 처리기를 ResponseEntityExceptionHandler 기반으로 조정해 Spring MVC 기본 상태 매핑을 유지하도록 보완했습니다.
  • 공통 응답 포맷은 유지하면서도, 프레임워크가 제공하는 HTTP 헤더와 상태 정보가 보존되도록 수정했습니다.

Summary by CodeRabbit

  • New Features

    • Added standardized API response envelope for both success and error outcomes.
    • Added request handling for device identification via X-Device-Id (available for downstream processing).
    • Introduced centralized REST error handling with consistent error payloads and mapped HTTP statuses.
    • Updated API access rules to allow requests without authentication.
  • Bug Fixes

    • Missing or blank X-Device-Id now returns a clear client error.
    • Error responses now consistently include status-appropriate error codes and messages.
  • Chores / Infrastructure

    • Enabled Flyway database migrations (including test profile) and configured migration locations.

@JoonKyoLee JoonKyoLee self-assigned this Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 93e5c68d-78da-41a7-a0ed-eb2aba7371cb

📥 Commits

Reviewing files that changed from the base of the PR and between e19fca2 and 828879b.

📒 Files selected for processing (2)
  • src/main/kotlin/com/zerost/api/common/exception/ErrorCode.kt
  • src/main/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt

📝 Walkthrough

Walkthrough

The change adds shared API response and exception handling, enforces X-Device-Id on API routes except registration, permits all requests through Spring Security, and enables Flyway migrations for application and test profiles.

Changes

Common API and infrastructure foundations

Layer / File(s) Summary
Response and exception contracts
src/main/kotlin/com/zerost/api/common/response/*, src/main/kotlin/com/zerost/api/common/exception/*
Adds standardized success/error response models, error codes, business exceptions, and global handlers for business, validation, framework, and runtime exceptions.
Device header processing
src/main/kotlin/com/zerost/api/common/device/*, src/main/kotlin/com/zerost/api/common/config/WebMvcConfig.kt
Validates X-Device-Id on /api/v1/** requests, stores the trimmed value as a request attribute, and excludes user registration.
Security filter policy
src/main/kotlin/com/zerost/api/common/config/SecurityConfig.kt
Disables CSRF and form login while permitting all HTTP requests.
Flyway migration setup
build.gradle.kts, src/main/resources/application.yaml, src/main/resources/application-test.yml
Adds Flyway dependencies and enables migrations using the configured classpath location in regular and test profiles.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately reflects the main changes: Flyway setup plus common API response infrastructure.
Description check ✅ Passed The description includes the required issue number, summary, review notes, and test results sections, and they are sufficiently filled out.
Linked Issues check ✅ Passed The changes cover Flyway setup, common response envelopes, global exception handling, X-Device-Id interception, and permissive security as requested in #9.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes are evident; the added security, response, exception, and migration configs all support issue #9.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/flyway-common-response

Comment @coderabbitai help to get the list of available commands.

@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: 2

🧹 Nitpick comments (2)
src/main/kotlin/com/zerost/api/common/config/WebMvcConfig.kt (1)

13-16: 🔒 Security & Privacy | 🔵 Trivial

Verify that the interceptor is not being treated as an authentication boundary.

If X-Device-Id becomes security-sensitive, confirm that every relevant endpoint is handled by MVC and add tests for the registration path, trailing-slash variants, and any non-MVC endpoints. Spring recommends using the security filter chain for security enforcement rather than MVC interceptors. (docs.spring.io)

🤖 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/kotlin/com/zerost/api/common/config/WebMvcConfig.kt` around lines 13
- 16, Review deviceIdInterceptor registration in addInterceptors and ensure it
is used only for non-security concerns, not as an authentication or
authorization boundary. If X-Device-Id is security-sensitive, move enforcement
into the Spring Security filter chain and add coverage for registration,
trailing-slash variants, and non-MVC endpoints.

Source: MCP tools

src/main/kotlin/com/zerost/api/common/response/ApiResponse.kt (1)

23-28: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Make failure() generic for typed responses. ApiResponse.failure() is fixed to ApiResponse<Nothing>, so it won’t compose with typed endpoints without a cast. Making failure() generic (or the wrapper covariant) keeps the shared response contract usable for ApiResponse<T> handlers.

🤖 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/kotlin/com/zerost/api/common/response/ApiResponse.kt` around lines
23 - 28, Update the ApiResponse.failure function to be generic over the response
payload type and return ApiResponse<T> instead of ApiResponse<Nothing>, while
preserving its unsuccessful status, null data, and supplied ApiErrorResponse.
Ensure typed endpoint handlers can use failure without casts.
🤖 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 `@src/main/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt`:
- Around line 46-58: Update handleMethodNotAllowed to preserve the Allow header
for 405 responses by copying ex.supportedHttpMethods into the ResponseEntity
headers, or delegate handling to Spring’s default resolver while retaining the
existing ApiResponse body and status.
- Around line 61-74: Update GlobalExceptionHandler.handleException so it does
not convert all exceptions into 500 responses. Extend
ResponseEntityExceptionHandler or add explicit handlers for Spring MVC 4xx
exceptions, including HandlerMethodValidationException,
HttpMessageNotReadableException, MissingServletRequestParameterException, and
MethodArgumentTypeMismatchException, while retaining the internal-server-error
response for genuine unhandled failures.

---

Nitpick comments:
In `@src/main/kotlin/com/zerost/api/common/config/WebMvcConfig.kt`:
- Around line 13-16: Review deviceIdInterceptor registration in addInterceptors
and ensure it is used only for non-security concerns, not as an authentication
or authorization boundary. If X-Device-Id is security-sensitive, move
enforcement into the Spring Security filter chain and add coverage for
registration, trailing-slash variants, and non-MVC endpoints.

In `@src/main/kotlin/com/zerost/api/common/response/ApiResponse.kt`:
- Around line 23-28: Update the ApiResponse.failure function to be generic over
the response payload type and return ApiResponse<T> instead of
ApiResponse<Nothing>, while preserving its unsuccessful status, null data, and
supplied ApiErrorResponse. Ensure typed endpoint handlers can use failure
without casts.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 73488dc3-b8ad-4ba4-be96-108ff0e31c15

📥 Commits

Reviewing files that changed from the base of the PR and between a11a24a and 7af44d1.

📒 Files selected for processing (12)
  • build.gradle.kts
  • src/main/kotlin/com/zerost/api/common/config/SecurityConfig.kt
  • src/main/kotlin/com/zerost/api/common/config/WebMvcConfig.kt
  • src/main/kotlin/com/zerost/api/common/device/DeviceConstants.kt
  • src/main/kotlin/com/zerost/api/common/device/DeviceIdInterceptor.kt
  • src/main/kotlin/com/zerost/api/common/exception/BusinessException.kt
  • src/main/kotlin/com/zerost/api/common/exception/ErrorCode.kt
  • src/main/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt
  • src/main/kotlin/com/zerost/api/common/response/ApiErrorResponse.kt
  • src/main/kotlin/com/zerost/api/common/response/ApiResponse.kt
  • src/main/resources/application-test.yml
  • src/main/resources/application.yaml

Comment thread src/main/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt Outdated
Comment thread src/main/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt Outdated

@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: 2

Caution

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

⚠️ Outside diff range comments (1)
src/main/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt (1)

88-101: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Narrow the RuntimeException fallback.

An application-defined RuntimeException annotated with @ResponseStatus can be converted to this 500 response instead of retaining its declared status. Restrict this handler to an application-specific unexpected-exception type, or explicitly preserve status-bearing exceptions. Spring defines @ResponseStatus as an exception-to-status mapping. (docs.spring.io)

🤖 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/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt`
around lines 88 - 101, Update handleRuntimeException in GlobalExceptionHandler
so the fallback does not catch application RuntimeExceptions carrying
`@ResponseStatus`; narrow it to the project’s unexpected-exception type, or
explicitly bypass status-bearing exceptions so Spring can apply their declared
status. Preserve the existing 500 ApiResponse behavior for genuinely unexpected
runtime failures.
🤖 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 `@src/main/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt`:
- Around line 54-86: Update handleExceptionInternal so every supported 4xx
status, including 404, 406, and 415, produces an ApiErrorResponse with the
corresponding status-specific ErrorCode instead of INTERNAL_SERVER_ERROR. Add
the necessary ErrorCode mappings or preserve the exception’s status-aware
response body, and add tests covering these MVC error statuses.
- Around line 54-86: Update handleExceptionInternal to preserve Spring’s
already-committed-response guard by delegating through
super.handleExceptionInternal with the constructed response, or by checking
response.isCommitted before constructing and returning the API error. Keep the
existing status-based response mapping for uncommitted responses.

---

Outside diff comments:
In `@src/main/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt`:
- Around line 88-101: Update handleRuntimeException in GlobalExceptionHandler so
the fallback does not catch application RuntimeExceptions carrying
`@ResponseStatus`; narrow it to the project’s unexpected-exception type, or
explicitly bypass status-bearing exceptions so Spring can apply their declared
status. Preserve the existing 500 ApiResponse behavior for genuinely unexpected
runtime failures.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8014b6b8-0d87-49b0-bf5b-2d108826ee64

📥 Commits

Reviewing files that changed from the base of the PR and between 7af44d1 and e19fca2.

📒 Files selected for processing (1)
  • src/main/kotlin/com/zerost/api/common/exception/GlobalExceptionHandler.kt

@JoonKyoLee JoonKyoLee merged commit 73171b2 into develop Jul 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

⚙️ [chore] Flyway 및 공통 API 응답 기반 구성

1 participant