Skip to content

[CHORE] Swagger @Tag로 API 그룹핑 - #47

Merged
hakSick merged 5 commits into
developfrom
feature/46-swagger-tag
Aug 6, 2026
Merged

hakSick merged 5 commits into
developfrom
feature/46-swagger-tag

Conversation

@kyeonG0210

@kyeonG0210 kyeonG0210 commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

🔗 관련 이슈

closes #46

📋 작업 내용

  • Swagger UI에서 API가 도메인별로 구분되도록 각 컨트롤러에 @tag(name, description) 추가 (12개 컨트롤러, S3Controller는 기존 적용됨)
    • IoTLightController : IoT 유도등
    • FloorController : 층/도면
    • AnalysisController : AI 분석
    • TrainingSessionController : 훈련 세션
    • TrainingScenarioController : 훈련 시나리오
    • EvacuationRouteController : 대피 경로
    • MapGraphController : 맵 그래프
    • MapGraphEditController : 맵 그래프 편집
    • UserController : 사용자
    • TrainingReportController : 훈련 리포트
    • DashboardController : 대시보드
    • BuildingController : 건물
    • S3Controller : S3 파일 업로드 (기존)

🧪 테스트 결과

  • ./gradlew compileJava 통과
  • Swagger UI(/swagger-ui.html)에서 그룹핑 확인

Summary by CodeRabbit

  • 새로운 기능

    • IoT 유도등의 방향 변경 및 Raspberry Pi 연결 주소 설정 기능을 추가했습니다.
    • 유도등 상태가 층별 WebSocket 채널로 실시간 전달됩니다.
    • 조명 상태 조회 응답에 Raspberry Pi 연결 주소가 포함됩니다.
    • 층별 조명 채널 구독 시 유효한 층인지 확인합니다.
  • 문서

    • 주요 API에 Swagger/OpenAPI 그룹명과 설명을 추가했습니다.
  • 오류 처리

    • 안내 경로 미설정, 비활성화 조명, 장치 연결 실패 상황을 구분해 안내합니다.

관리자가 유도등 방향(LEFT/RIGHT/OFF)을 라즈베리파이에 명령하고, 결과를
층 도면 화면에 웹소켓으로 실시간 반영할 수 있도록 구현.

- PATCH /lights/{id}/pi-endpoint: 유도등에 라즈베리파이 주소 설정
- PATCH /lights/{id}/direction: 방향 명령 (검증 -> Pi 호출 -> 상태 저장 -> 이벤트 발행)
- IoTLightPiClient: WebClient 기반 Pi 통신 (3초 타임아웃, 실패 시 DEVICE_UNREACHABLE)
- IoTLightDirectionStore: 실시간 방향 상태를 서버 메모리에서 관리 (DB 미저장)
- TrainingEventPublisher.publishIoTLightStatusUpdated(AfterCommit): 기존 발행 클래스에
  유도등 이벤트 메서드 추가, /topic/floors/{floorId}/lights로 발행
- StompAuthChannelInterceptor: 층 유도등 topic 구독 인가 로직 추가
- 서비스/컨트롤러/발행자/웹소켓 통합 테스트 추가
…락 수정

PR #43에서 추가된 AiAnalysisClient가 saferoute.ai-service.base-url 프로퍼티를
필수로 요구하는데, src/test/resources/application.yml에는 이 값이 없어 모든
@SpringBootTest 전체 컨텍스트 테스트가 PlaceholderResolutionException으로
실패하고 있었다. src/test/resources/application.yml이 클래스패스에서
src/main/resources/application.yml과 파일명이 같아 완전히 가려버려서 메인
쪽 기본값도 적용되지 않는 것이 원인이었다. S3Config 더미값과 동일한 방식으로
더미 base-url을 추가해 해결.

WebSocketIntegrationTest 7개 테스트가 다시 통과함을 확인.
publishTrainingStatusUpdated()에 "호출하는 지점이 없다"는 주석이 남아있었는데,
지금은 훈련 시작/종료/강제종료/타임아웃 4곳에서 publishTrainingStatusUpdatedAfterCommit을
통해 이미 호출되고 있어 사실과 다른 내용이었음. 실제 동작 기준으로 갱신.
saferoute: 하위에 잘못 위치해 있어 @value("${aws.dynamodb.table-name}")를
참조하는 DynamoDB 리포지토리들의 빈 생성이 실패하던 문제 수정
Swagger UI에서 도메인별로 API를 구분해서 볼 수 있도록
각 컨트롤러에 @tag(name, description) 추가
@kyeonG0210 kyeonG0210 self-assigned this Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

IoT 유도등의 Raspberry Pi 방향 제어 API, 엔드포인트 설정, 메모리 상태 저장, 층별 WebSocket 이벤트를 추가했습니다. 여러 컨트롤러에 Swagger 태그를 등록하고 관련 검증 및 통합 테스트를 확장했습니다.

Changes

IoT 유도등 제어 및 이벤트

Layer / File(s) Summary
유도등 명령 흐름
build.gradle, src/main/java/com/saferoute/domain/device/..., src/main/java/com/saferoute/global/api/..., src/test/java/com/saferoute/domain/device/...
Pi 엔드포인트 설정과 방향 변경 API를 추가했습니다. 서비스는 유효성 검증 후 Pi에 명령을 전송하고 방향 상태를 저장합니다. 성공 및 오류 응답과 관련 테스트를 추가했습니다.
유도등 WebSocket 이벤트
src/main/java/com/saferoute/infrastructure/websocket/..., src/test/java/com/saferoute/infrastructure/websocket/...
층별 유도등 토픽과 이벤트 메시지를 추가했습니다. 구독 시 층 존재 여부를 확인하고, 상태 이벤트를 트랜잭션 커밋 후 발행합니다. 단위 및 통합 테스트를 추가했습니다.
API 문서 및 설정 정리
src/main/java/com/saferoute/domain/*/controller/*, src/main/resources/application.yml
도메인별 컨트롤러에 Swagger @Tag를 추가했습니다. WebFlux 의존성과 애플리케이션 설정 구조를 갱신했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers: songmin0111

Sequence Diagram(s)

sequenceDiagram
  participant IoTLightController
  participant IoTLightService
  participant IoTLightPiClient
  participant IoTLightDirectionStore
  participant TrainingEventPublisher
  IoTLightController->>IoTLightService: 방향 변경 요청
  IoTLightService->>IoTLightPiClient: Raspberry Pi 방향 명령 전송
  IoTLightService->>IoTLightDirectionStore: 방향 상태 저장
  IoTLightService->>TrainingEventPublisher: 커밋 후 상태 이벤트 발행
  IoTLightService-->>IoTLightController: 방향 변경 응답
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning IoT 조명 제어, WebSocket 이벤트, Raspberry Pi 연동, 설정 변경 등 #46과 무관한 변경이 다수 포함되었습니다. Swagger @Tag 변경만 유지하고 IoT 조명 및 WebSocket 관련 변경은 별도의 pull request로 분리하십시오.
Docstring Coverage ⚠️ Warning Docstring coverage is 22.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 컨트롤러별 Swagger @Tag 추가와 API 그룹핑이라는 실제 변경을 명확하게 설명합니다.
Linked Issues check ✅ Passed 12개 대상 컨트롤러에 @Tag가 추가되어 #46의 도메인별 Swagger API 그룹핑 요구사항을 충족합니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/46-swagger-tag

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/java/com/saferoute/domain/device/service/IoTLightService.java`:
- Around line 152-154: Serialize commands per lightId in the flow containing
iotLightPiClient.sendDirection, iotLightDirectionStore.update, and
trainingEventPublisher.publishIoTLightStatusUpdatedAfterCommit so each light’s
Pi call, state update, and event registration execute in request order. Use a
per-light command queue or equivalent version/order validation, while allowing
commands for different lights to proceed independently and preventing stale
responses from overwriting state or publishing outdated events.

In
`@src/test/java/com/saferoute/infrastructure/websocket/service/TrainingEventPublisherTest.java`:
- Around line 146-150: Update the assertions for the IoT light status event in
TrainingEventPublisherTest to also verify that message.data().updatedAt() is not
null, alongside the existing envelope occurredAt check.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e86f3f2-4231-4b82-a7e7-a9a96bdddef5

📥 Commits

Reviewing files that changed from the base of the PR and between 304a614 and 153e3e0.

📒 Files selected for processing (36)
  • build.gradle
  • src/main/java/com/saferoute/domain/analysis/controller/AnalysisController.java
  • src/main/java/com/saferoute/domain/building/controller/BuildingController.java
  • src/main/java/com/saferoute/domain/dashboard/DashboardController.java
  • src/main/java/com/saferoute/domain/device/client/IoTLightPiClient.java
  • src/main/java/com/saferoute/domain/device/client/PiLightDirectionRequest.java
  • src/main/java/com/saferoute/domain/device/controller/IoTLightController.java
  • src/main/java/com/saferoute/domain/device/dto/request/ChangeLightDirectionRequest.java
  • src/main/java/com/saferoute/domain/device/dto/request/UpdatePiEndpointRequest.java
  • src/main/java/com/saferoute/domain/device/dto/response/IoTLightResponse.java
  • src/main/java/com/saferoute/domain/device/dto/response/LightDirectionResponse.java
  • src/main/java/com/saferoute/domain/device/entity/IoTLight.java
  • src/main/java/com/saferoute/domain/device/service/IoTLightDirectionStore.java
  • src/main/java/com/saferoute/domain/device/service/IoTLightService.java
  • src/main/java/com/saferoute/domain/evacuation/controller/EvacuationRouteController.java
  • src/main/java/com/saferoute/domain/evacuation/controller/MapGraphController.java
  • src/main/java/com/saferoute/domain/evacuation/controller/MapGraphEditController.java
  • src/main/java/com/saferoute/domain/floor/controller/FloorController.java
  • src/main/java/com/saferoute/domain/report/controller/TrainingReportController.java
  • src/main/java/com/saferoute/domain/training/controller/TrainingScenarioController.java
  • src/main/java/com/saferoute/domain/training/controller/TrainingSessionController.java
  • src/main/java/com/saferoute/domain/user/controller/UserController.java
  • src/main/java/com/saferoute/global/api/error/IoTLightErrorCode.java
  • src/main/java/com/saferoute/global/api/response/IoTLightSuccessCode.java
  • src/main/java/com/saferoute/infrastructure/websocket/dto/IoTLightEventMessage.java
  • src/main/java/com/saferoute/infrastructure/websocket/dto/IoTLightStatusEventData.java
  • src/main/java/com/saferoute/infrastructure/websocket/dto/TrainingEventType.java
  • src/main/java/com/saferoute/infrastructure/websocket/security/StompAuthChannelInterceptor.java
  • src/main/java/com/saferoute/infrastructure/websocket/service/TrainingEventPublisher.java
  • src/main/resources/application.yml
  • src/test/java/com/saferoute/domain/device/controller/IoTLightControllerTest.java
  • src/test/java/com/saferoute/domain/device/service/IoTLightServiceTest.java
  • src/test/java/com/saferoute/infrastructure/websocket/security/StompAuthChannelInterceptorTest.java
  • src/test/java/com/saferoute/infrastructure/websocket/service/TrainingEventPublisherTest.java
  • src/test/java/com/saferoute/infrastructure/websocket/service/WebSocketIntegrationTest.java
  • src/test/resources/application.yml

Comment on lines +152 to +154
iotLightPiClient.sendDirection(light.getPiEndpoint(), light.getCode(), direction);
iotLightDirectionStore.update(light.getId(), direction);
trainingEventPublisher.publishIoTLightStatusUpdatedAfterCommit(light, direction);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

유도등별 명령 순서를 직렬화하세요.

동일 lightId에 대한 두 요청은 동시에 Pi 호출을 수행할 수 있습니다. 이전 요청의 Pi 응답이 늦게 도착하면 최신 요청이 저장한 방향을 이전 방향으로 덮어쓰고, 오래된 상태 이벤트를 발행할 수 있습니다. Pi의 실제 방향과 메모리 상태 및 WebSocket 이벤트가 불일치합니다.

Pi 응답 수신, 상태 저장, 이벤트 등록을 유도등별 단일 명령 큐 또는 순서 보장 가능한 버전 처리로 직렬화하세요.

🤖 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/saferoute/domain/device/service/IoTLightService.java`
around lines 152 - 154, Serialize commands per lightId in the flow containing
iotLightPiClient.sendDirection, iotLightDirectionStore.update, and
trainingEventPublisher.publishIoTLightStatusUpdatedAfterCommit so each light’s
Pi call, state update, and event registration execute in request order. Use a
per-light command queue or equivalent version/order validation, while allowing
commands for different lights to proceed independently and preventing stale
responses from overwriting state or publishing outdated events.

Comment on lines +146 to +150
assertThat(message.eventType()).isEqualTo(TrainingEventType.IOT_LIGHT_STATUS_UPDATED);
assertThat(message.floorId()).isEqualTo(floorId);
assertThat(message.occurredAt()).isNotNull();
assertThat(message.data().lightId()).isEqualTo(light.getId());
assertThat(message.data().direction()).isEqualTo(IoTLightDirection.LEFT);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

data.updatedAt을 검증하세요.

현재 테스트는 envelope의 occurredAt만 검증합니다. IoTLightStatusEventData.updatedAt이 null이어도 테스트가 통과합니다.

수정 예시
         assertThat(message.occurredAt()).isNotNull();
         assertThat(message.data().lightId()).isEqualTo(light.getId());
         assertThat(message.data().direction()).isEqualTo(IoTLightDirection.LEFT);
+        assertThat(message.data().updatedAt()).isNotNull();
📝 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
assertThat(message.eventType()).isEqualTo(TrainingEventType.IOT_LIGHT_STATUS_UPDATED);
assertThat(message.floorId()).isEqualTo(floorId);
assertThat(message.occurredAt()).isNotNull();
assertThat(message.data().lightId()).isEqualTo(light.getId());
assertThat(message.data().direction()).isEqualTo(IoTLightDirection.LEFT);
assertThat(message.eventType()).isEqualTo(TrainingEventType.IOT_LIGHT_STATUS_UPDATED);
assertThat(message.floorId()).isEqualTo(floorId);
assertThat(message.occurredAt()).isNotNull();
assertThat(message.data().lightId()).isEqualTo(light.getId());
assertThat(message.data().direction()).isEqualTo(IoTLightDirection.LEFT);
assertThat(message.data().updatedAt()).isNotNull();
🤖 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/test/java/com/saferoute/infrastructure/websocket/service/TrainingEventPublisherTest.java`
around lines 146 - 150, Update the assertions for the IoT light status event in
TrainingEventPublisherTest to also verify that message.data().updatedAt() is not
null, alongside the existing envelope occurredAt check.

@hakSick
hakSick merged commit 48ef85d into develop Aug 6, 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] Swagger @Tag로 API 그룹핑

2 participants