Skip to content

feat : 서버 점검 상태 확인 API 및 필터를 통해 점검 시 API 호출 차단 로직 추가#75

Merged
kon28289 merged 8 commits intodevfrom
feat/maintenance
Mar 2, 2026
Merged

feat : 서버 점검 상태 확인 API 및 필터를 통해 점검 시 API 호출 차단 로직 추가#75
kon28289 merged 8 commits intodevfrom
feat/maintenance

Conversation

@kon28289
Copy link
Copy Markdown
Contributor

@kon28289 kon28289 commented Mar 2, 2026

🚀 1. 개요

  • 클라이언트가 서버가 점검 중인지 확인할 수 있는 API를 추가합니다.
  • 서버가 점검 중이라면 화이트리스트에 존재하지 않는 API 호출을 차단하는 로직을 추가합니다.

📝 2. 주요 변경 사항

  • 서버의 상태는 maintenance 테이블을 통해 관리합니다.
  • 서버 점검 상태를 확인하는 API는 앱 실행 시에 한 번 호출합니다.
  • 앱 실행 중에는 MaintenanceFilter를 통해 서버 점검 상태를 확인하고, 점검 중이라면 화이트리스트에 존재하지 않는 API의 호출이 차단됩니다.
  • MaintenanceFilterJwtAuthenticationFilter 앞단에 위치하여 점검 상태에서는 JWT 인증 및 하위 로직까지 내려가지 않고 즉시 503을 반환합니다.

📸 3. 스크린샷 (API 테스트 결과)

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 서버 점검 모드 추가 — 관리자가 점검 상태를 확인·변경할 수 있습니다.
    • 점검 중 앱 전반에 점검 제어가 적용됩니다.
    • 점검 중 접근 시 503 상태와 명확한 한국어 안내 메시지를 반환합니다.
    • 점검 상태 확인 및 업데이트용 관리 API(조회, 수정) 추가.
    • 일부 엔드포인트는 점검 시에도 화이트리스트로 계속 접근 가능합니다.
  • 테스트

    • 점검 필터 및 서비스에 대한 단위 테스트 추가.

@kon28289 kon28289 requested a review from Juhye0k March 2, 2026 06:43
@kon28289 kon28289 self-assigned this Mar 2, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 2, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6095515 and 6b3e190.

📒 Files selected for processing (2)
  • src/main/java/com/gpt/geumpumtabackend/global/maintenance/MaintenanceFilter.java
  • src/test/java/com/gpt/geumpumtabackend/unit/global/maintenance/MaintenanceFilterTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/gpt/geumpumtabackend/global/maintenance/MaintenanceFilter.java

Walkthrough

서버 유지보수 상태 기능을 추가합니다. 유지보수 상태를 관리하는 도메인·서비스·레포지토리·컨트롤러와 요청을 차단하는 MaintenanceFilter, SecurityConfig 연동 및 관련 단위 테스트가 포함됩니다.

Changes

Cohort / File(s) Summary
보안 구성 및 필터
src/main/java/com/gpt/geumpumtabackend/global/config/security/SecurityConfig.java, src/main/java/com/gpt/geumpumtabackend/global/maintenance/MaintenanceFilter.java
SecurityConfig에 MaintenanceFilter를 주입하고 필터 체인에 추가. MaintenanceFilter는 화이트리스트 경로를 허용하고, 유지보수 중인 경우 비화이트리스트 요청에 대해 503 JSON 응답을 반환.
예외 타입
src/main/java/com/gpt/geumpumtabackend/global/exception/ExceptionType.java
MAINTENANCE_IN_PROGRESS 예외 상수 추가(SERVICE_UNAVAILABLE, 코드 MT001, 메시지 "서버 점검 중입니다.").
도메인 모델
src/main/java/com/gpt/geumpumtabackend/maintenance/domain/Maintenance.java, src/main/java/com/gpt/geumpumtabackend/maintenance/domain/ServiceStatus.java
Maintenance JPA 엔티티(기본 ID 상수, status, message) 및 ServiceStatus enum(NORMAL, MAINTENANCE) 추가.
서비스 및 레포지토리
src/main/java/com/gpt/geumpumtabackend/maintenance/service/MaintenanceService.java, src/main/java/com/gpt/geumpumtabackend/maintenance/repository/MaintenanceRepository.java
유지보수 상태 조회/업데이트 로직과 isMaintenanceInProgress() 제공. JPA 리포지토리 추가.
API 및 컨트롤러
src/main/java/com/gpt/geumpumtabackend/maintenance/api/MaintenanceApi.java, src/main/java/com/gpt/geumpumtabackend/maintenance/controller/MaintenanceController.java
GET /status 및 ADMIN 권한의 PATCH /status 엔드포인트 정의 및 구현(응답 래핑 포함).
DTO
src/main/java/com/gpt/geumpumtabackend/maintenance/dto/request/MaintenanceStatusUpdateRequest.java, src/main/java/com/gpt/geumpumtabackend/maintenance/dto/response/MaintenanceStatusResponse.java
상태 업데이트 요청/응답 DTO 추가(MaintenanceStatusUpdateRequest, MaintenanceStatusResponse::from).
단위 테스트
src/test/java/com/gpt/geumpumtabackend/unit/global/maintenance/MaintenanceFilterTest.java, src/test/java/com/gpt/geumpumtabackend/unit/maintenance/service/MaintenanceServiceTest.java
MaintenanceFilter 동작(차단/화이트리스트/컨텍스트패스) 및 MaintenanceService의 생성/업데이트 흐름에 대한 테스트 추가.

Sequence Diagram

sequenceDiagram
    participant Client
    participant MaintenanceFilter
    participant MaintenanceService
    participant MaintenanceRepository
    participant Database as DB

    Client->>MaintenanceFilter: HTTP 요청 (URI)
    MaintenanceFilter->>MaintenanceService: isMaintenanceInProgress()
    MaintenanceService->>MaintenanceRepository: findById(DEFAULT_ID)
    MaintenanceRepository->>Database: SELECT maintenance
    Database-->>MaintenanceRepository: Maintenance or null
    MaintenanceRepository-->>MaintenanceService: Maintenance?
    alt 유지보수 중 && 비화이트리스트
        MaintenanceService-->>MaintenanceFilter: true
        MaintenanceFilter->>MaintenanceFilter: isWhitelisted(URI) -> false
        MaintenanceFilter->>Client: 503 JSON (MAINTENANCE_IN_PROGRESS)
    else 화이트리스트 또는 정상 상태
        MaintenanceService-->>MaintenanceFilter: false or 화이트리스트
        MaintenanceFilter->>Client: 요청 통과 (다음 필터/핸들러)
    end
Loading
sequenceDiagram
    participant Admin
    participant MaintenanceController
    participant MaintenanceService
    participant MaintenanceRepository
    participant Database as DB

    Admin->>MaintenanceController: PATCH /api/v1/maintenance/status (request)
    MaintenanceController->>MaintenanceService: updateStatus(request)
    MaintenanceService->>MaintenanceRepository: findById(DEFAULT_ID)
    MaintenanceRepository->>Database: SELECT maintenance
    Database-->>MaintenanceRepository: Maintenance or null
    alt 기존 항목 존재
        MaintenanceService->>MaintenanceService: maintenance.update(status,message)
    else 신규 항목
        MaintenanceService->>MaintenanceService: Maintenance.initialize(status,message)
    end
    MaintenanceService->>MaintenanceRepository: save(maintenance)
    MaintenanceRepository->>Database: INSERT/UPDATE
    Database-->>MaintenanceRepository: 저장 완료
    MaintenanceRepository-->>MaintenanceService: saved Maintenance
    MaintenanceService-->>MaintenanceController: MaintenanceStatusResponse
    MaintenanceController-->>Admin: 200 JSON 응답
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • Juhye0k

Poem

🐰 점검의 밤, 필터를 세웠네
관리자의 손길로 상태를 바꾸고
화이트리스트는 살짝 열어두며
서버도 한숨 돌릴 수 있게끔
토끼가 축하해요, 조용한 개편 축제 🌙

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 변경 사항의 주요 내용(서버 점검 상태 확인 API 및 필터를 통한 API 호출 차단)을 명확하고 구체적으로 설명하고 있습니다.
Description check ✅ Passed PR 설명이 템플릿의 모든 필수 섹션(개요, 주요 변경 사항, 스크린샷)을 포함하고 있으며, 각 섹션에서 충분한 정보를 제공하고 있습니다.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/maintenance

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
Copy Markdown
Contributor

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

🧹 Nitpick comments (1)
src/main/java/com/gpt/geumpumtabackend/maintenance/service/MaintenanceService.java (1)

21-25: 생성 케이스에서 상태/메시지 할당이 중복됩니다.

orElseGet에서 이미 요청값으로 생성하고 있어 바로 아래 maintenance.update(...)가 신규 생성 흐름에서는 중복입니다. 기본값으로 생성 후 update 1회로 통일하면 의도가 더 분명해집니다.

♻️ 정리 예시
     public MaintenanceStatusResponse updateStatus(MaintenanceStatusUpdateRequest request) {
         Maintenance maintenance = maintenanceRepository.findById(Maintenance.DEFAULT_ID)
-                .orElseGet(() -> Maintenance.initialize(request.status(), request.message()));
+                .orElseGet(() -> Maintenance.initialize(ServiceStatus.NORMAL, null));

         maintenance.update(request.status(), request.message());

         return MaintenanceStatusResponse.from(maintenanceRepository.save(maintenance));
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/gpt/geumpumtabackend/maintenance/service/MaintenanceService.java`
around lines 21 - 25, The code currently constructs a new Maintenance with
request values inside maintenanceRepository.findById(...).orElseGet(() ->
Maintenance.initialize(request.status(), request.message())) and then
immediately calls maintenance.update(request.status(), request.message()),
causing duplicate assignment on creation; change the orElseGet to create a
default/empty Maintenance (e.g. use Maintenance.initialize() or another no-arg
initializer) so that you always obtain a maintenance instance and then call
maintenance.update(request.status(), request.message()) exactly once; reference
the Maintenance.initialize(...) call and the maintenance.update(...) invocation
to locate and modify the creation path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/com/gpt/geumpumtabackend/global/exception/ExceptionType.java`:
- Line 79: The enum entry MAINTENANCE_IN_PROGRESS in ExceptionType uses an
invalid prefix "MT001"; update its error code to follow the allowed prefixes
(e.g., change "MT001" to a valid M-prefixed code such as "M001" or another
unique M... value) so it conforms to the ExceptionType prefix convention; ensure
the new code is unique within ExceptionType and update any references to
MAINTENANCE_IN_PROGRESS if they rely on the old string.

In
`@src/main/java/com/gpt/geumpumtabackend/global/maintenance/MaintenanceFilter.java`:
- Around line 45-48: The filter currently uses request.getRequestURI() which
includes the context path and can break whitelist matching; in MaintenanceFilter
replace usage of getRequestURI() with request.getServletPath() (used where
requestUri is assigned) so isWhitelisted(...) compares the servlet path, and
keep existing logic around isWhitelisted(...) and
maintenanceService.isMaintenanceInProgress() before calling
filterChain.doFilter(...); after the change, run or add tests that simulate a
non-empty context-path to ensure isWhitelisted(String) still matches expected
paths.

In `@src/main/java/com/gpt/geumpumtabackend/maintenance/domain/Maintenance.java`:
- Around line 13-16: Add the soft-delete SQL behavior to the Maintenance entity
by annotating the Maintenance class with `@SQLDelete` using an UPDATE statement
that sets deleted_at = NOW() for the row with the given id (follow the same
pattern used in the User entity); locate the Maintenance class declaration
(public class Maintenance extends BaseEntity) and add the `@SQLDelete`(...)
annotation above it and consider masking any sensitive fields on delete if your
User pattern does so.

In
`@src/test/java/com/gpt/geumpumtabackend/unit/global/maintenance/MaintenanceFilterTest.java`:
- Around line 22-24: The test class MaintenanceFilterTest currently does not
extend the project's unit test base; modify the class declaration so
MaintenanceFilterTest extends BaseUnitTest to comply with the
JUnit5+Mockito+AssertJ convention used across tests; locate the class named
MaintenanceFilterTest and update its signature to inherit from BaseUnitTest (and
add any missing import for BaseUnitTest) so the test uses the shared
setup/teardown and utilities provided by BaseUnitTest.

In
`@src/test/java/com/gpt/geumpumtabackend/unit/maintenance/service/MaintenanceServiceTest.java`:
- Around line 23-25: The test class MaintenanceServiceTest must extend the
project test base; update the class declaration to extend BaseUnitTest (i.e.,
change "class MaintenanceServiceTest" to "class MaintenanceServiceTest extends
BaseUnitTest") and adjust imports/annotations as needed (for example remove or
keep `@ExtendWith`(MockitoExtension.class) only if BaseUnitTest already configures
Mockito/JUnit extensions) so the file conforms to the unit test base class rule.
- Around line 33-69: Add unit tests to cover exception and boundary scenarios
around MaintenanceService.updateStatus: include tests that pass a null message
and an empty message in MaintenanceStatusUpdateRequest to assert expected
handling (e.g., message stored as null/empty or validation exception), a test
for invalid or disallowed status transitions (if business rules exist) by
invoking updateStatus with such transitions and asserting the thrown exception
or no-op, and tests that simulate repository failures by stubbing
maintenanceRepository.save(...) to throw an exception to verify error handling;
locate these in MaintenanceServiceTest and use the existing patterns
(Maintenance.initialize, MaintenanceStatusUpdateRequest, maintenanceRepository
mocks, and verify calls) or add parameterized tests to cover boundary
permutations.

---

Nitpick comments:
In
`@src/main/java/com/gpt/geumpumtabackend/maintenance/service/MaintenanceService.java`:
- Around line 21-25: The code currently constructs a new Maintenance with
request values inside maintenanceRepository.findById(...).orElseGet(() ->
Maintenance.initialize(request.status(), request.message())) and then
immediately calls maintenance.update(request.status(), request.message()),
causing duplicate assignment on creation; change the orElseGet to create a
default/empty Maintenance (e.g. use Maintenance.initialize() or another no-arg
initializer) so that you always obtain a maintenance instance and then call
maintenance.update(request.status(), request.message()) exactly once; reference
the Maintenance.initialize(...) call and the maintenance.update(...) invocation
to locate and modify the creation path.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 927eb5b and 6095515.

📒 Files selected for processing (13)
  • src/main/java/com/gpt/geumpumtabackend/global/config/security/SecurityConfig.java
  • src/main/java/com/gpt/geumpumtabackend/global/exception/ExceptionType.java
  • src/main/java/com/gpt/geumpumtabackend/global/maintenance/MaintenanceFilter.java
  • src/main/java/com/gpt/geumpumtabackend/maintenance/api/MaintenanceApi.java
  • src/main/java/com/gpt/geumpumtabackend/maintenance/controller/MaintenanceController.java
  • src/main/java/com/gpt/geumpumtabackend/maintenance/domain/Maintenance.java
  • src/main/java/com/gpt/geumpumtabackend/maintenance/domain/ServiceStatus.java
  • src/main/java/com/gpt/geumpumtabackend/maintenance/dto/request/MaintenanceStatusUpdateRequest.java
  • src/main/java/com/gpt/geumpumtabackend/maintenance/dto/response/MaintenanceStatusResponse.java
  • src/main/java/com/gpt/geumpumtabackend/maintenance/repository/MaintenanceRepository.java
  • src/main/java/com/gpt/geumpumtabackend/maintenance/service/MaintenanceService.java
  • src/test/java/com/gpt/geumpumtabackend/unit/global/maintenance/MaintenanceFilterTest.java
  • src/test/java/com/gpt/geumpumtabackend/unit/maintenance/service/MaintenanceServiceTest.java

@kon28289 kon28289 requested a review from Juhye0k March 2, 2026 08:25
@kon28289 kon28289 merged commit b4c1d07 into dev Mar 2, 2026
4 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.

2 participants