Skip to content

refactor: FRONTEND.md 기반 전체 API 계약 준수 리팩터링#32

Merged
ThonApple merged 9 commits into
developfrom
refactor/frontend-md-compliance
May 19, 2026
Merged

refactor: FRONTEND.md 기반 전체 API 계약 준수 리팩터링#32
ThonApple merged 9 commits into
developfrom
refactor/frontend-md-compliance

Conversation

@ThonApple
Copy link
Copy Markdown
Collaborator

@ThonApple ThonApple commented May 18, 2026

Summary

  • 모든 컨트롤러 @RequestMapping base URL을 /api/v1로 통일 (Auth, Jwt, User, Feed, Post, Spot, Notification)
  • UserController/me/** vs /users/** 경로 올바르게 분리
  • FeedControllerapplyapplications 경로 변경, bookmark POST/DELETE 스텁 추가
  • NotificationControllerPATCHPOST + /read-all 스텁 추가
  • SpotController/map, /search 스텁 추가
  • DTO 필드명 FRONTEND.md 완전 일치
    • SpotChecklistResponse: contenttext, isDonecompleted
    • SpotVoteOptionResponse: contentlabel, voterIds: List<String> 추가
    • SpotVoteResponse: spotId 필드 추가
    • SpotFileResponse: uploaderNickname, spotId, sizeBytes 추가, 필드명 통일
    • SpotNoteResponse: authorNickname, spotId 추가
    • SpotParticipantResponse: nickname 필드 추가
    • FeedApplyRequest: role, deposit 필드 추가
    • FeedApplicationResponse: appliedRole, deposit 필드 추가
  • SpotService — 참여자/파일/노트/투표 응답에 userRepository.findAllByIdIn() 배치 닉네임 조회 + voterIds 집계 적용

Test plan

  • ./gradlew compileJava 빌드 통과 확인 ✅
  • GET /api/v1/spots/{id}/participants — nickname 포함 응답 확인
  • GET /api/v1/spots/{id}/votes — voterIds, label 필드 확인
  • GET /api/v1/spots/{id}/checklist — text, completed 필드 확인
  • POST /api/v1/feeds/{id}/applications — 201 반환 확인
  • DELETE /api/v1/feeds/{id}/applications/me — 200 반환 확인
  • GET /api/v1/notifications/api/v1 prefix 정상 동작 확인

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added feed bookmark endpoints, spot map & search endpoints, and a "mark all notifications as read" endpoint (some currently return Not Implemented).
    • Feed applications now accept role and deposit; responses include application role/deposit, file sizes, nicknames, and per-option voter lists.
  • API Updates

    • Public API routes moved to /api/v1; feed application creation now under /applications with 201 response.
  • Database

    • Migrations add feed application role/deposit and spot file size columns.

Review Change Stack

- 모든 컨트롤러 base URL /api/v1 통일 (Auth, Jwt, User, Feed, Post, Spot, Notification)
- UserController /me vs /users 경로 분리
- FeedController apply → applications 경로 변경, bookmark 스텁 추가
- NotificationController PATCH→POST + read-all 스텁 추가
- SpotController /map, /search 스텁 추가
- DTO 필드명 FRONTEND.md 일치: content→text, isDone→completed, label, voterIds,
  uploaderNickname, authorNickname, spotId, sizeBytes 추가
- SpotService 참여자/파일/노트/투표 응답에 닉네임/voterIds 배치 조회 적용

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ThonApple ThonApple requested review from ca5tlechan and hoTan35 May 18, 2026 08:51
@ThonApple
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Controllers and SecurityConfig were versioned to /api/v1; feed application role/deposit added end-to-end with migration; notification mark-as-read changed; Spot DTOs renamed/expanded and SpotService refactored to bulk-resolve nicknames and compute per-option voter-id lists; several controller stubs added.

Changes

API Versioning, Feed, Notification, Security

Layer / File(s) Summary
Controller versioning & simulation
capstone-api/.../auth/controller/AuthController.java, .../auth/controller/JwtController.java, .../post/controller/PostController.java, .../user/controller/UserController.java, .../notification/controller/NotificationController.java, .../feed/controller/FeedController.java, .../spot/controller/SpotController.java, .../simulation/controller/SimulationController.java
Class-level @RequestMapping prefixes and controller imports updated to /api/v1 for affected controllers.
Security & login filter
capstone-common/.../global/config/SecurityConfig.java, capstone-api/.../global/filter/LoginFilter.java
SecurityConfig permit-matchers, OAuth2 baseUri, and logoutUrl updated to /api/v1/*; LoginFilter login path updated to /api/v1/auth/login.
Feed endpoints & DTOs, domain, migration
capstone-api/.../feed/controller/FeedController.java, FeedApplyRequest.java, FeedApplicationResponse.java, FeedItemResponse.java, capstone-domain/.../feed/entity/FeedApplication.java, capstone-domain/.../feed/entity/FeedApplicationRole.java, capstone-api/.../feed/service/FeedItemService.java, docs/migrations/2026-05-19_feed_application_role_deposit.sql
FeedController base path versioned; apply → POST /{feedId}/applications (201 CREATED); cancel → DELETE /{feedId}/applications/me returns ApiResponse; bookmark endpoints added as NOT_IMPLEMENTED; DTOs and entity add appliedRole and deposit; service populates fields; migration adds DB columns.
Notification endpoint behavior
capstone-api/.../notification/controller/NotificationController.java
Single-notification mark-as-read switched from PATCH/ResponseEntity to POST with @ResponseStatus(HttpStatus.NO_CONTENT) void handler; new POST /read-all stub added.
Minor feed DTO updates
capstone-api/.../feed/dto/FeedAuthorProfile.java, AddOn.java, IncludedItem.java
FeedAuthorProfile removes @JsonProperty avatar mapping; AddOn replaces note with explanation; IncludedItem replaces note with value.

Spot DTOs and Service Refactor

Layer / File(s) Summary
Spot controller endpoints
capstone-api/.../spot/controller/SpotController.java
Base path updated to /api/v1/spots; added GET /map and GET /search endpoints as NOT_IMPLEMENTED stubs.
Spot file & note DTOs + upload request
capstone-api/.../spot/dto/SpotFileResponse.java, SpotNoteResponse.java, UploadFileRequest.java, capstone-domain/.../spot/entity/SpotFile.java
SpotFileResponse replaces uploader/file fields with spotId, uploaderNickname, name, url, and sizeBytes plus new of(file,uploaderNickname) factory; SpotNoteResponse adds spotId and authorNickname and removes authorId; UploadFileRequest adds nullable sizeBytes; SpotFile entity adds sizeBytes column and migration added.
Spot participant & vote DTOs
capstone-api/.../spot/dto/SpotParticipantResponse.java, SpotVoteOptionResponse.java, SpotVoteResponse.java, SpotChecklistResponse.java
SpotParticipantResponse adds nickname and of(...); SpotVoteOptionResponse renames contentlabel, adds voterIds and of(option,voterIds); SpotVoteResponse adds spotId and reworks factory overloads; SpotChecklistResponse renames contenttext, isDonecompleted.
SpotService nickname & voter attribution
capstone-api/.../spot/service/SpotService.java
Adds resolveUserId helper and refactors participants/votes/files/notes flows to bulk-fetch nicknames, prefetch vote answers to compute per-option voterIds, and return DTOs using new of(...) factories; uploader/author ids set via fallback.

Sequence Diagram

sequenceDiagram
  participant Client
  participant SpotController
  participant SpotService
  participant UserRepository
  Client->>SpotController: GET /api/v1/spots/{id}/votes
  SpotController->>SpotService: getVotes(spotId, currentUserId)
  SpotService->>UserRepository: findAllByIds(userIds) (bulk nickname lookup)
  SpotService->>SpotService: prefetch SpotVoteAnswer rows and compute voterIds per option
  SpotService-->>SpotController: SpotVoteResponse (with option.voterIds)
  SpotController-->>Client: 200 ApiResponse<SpotVoteResponse>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

feature

Suggested reviewers

  • ca5tlechan
  • hoTan35

"A rabbit hops through versioned rails,
/api/v1 redraws the trails.
Nicknames gathered, voters shown,
Roles and deposits now are known.
Stubs await the next deploy—hip hop! 🐰"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.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 clearly describes the main change: refactoring all API contracts to comply with FRONTEND.md, focusing on API versioning and DTO field standardization.
Description check ✅ Passed The description comprehensively covers all major changes including API versioning, endpoint path changes, DTO field updates, and service layer refactoring with a detailed test plan.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 refactor/frontend-md-compliance

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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

✅ Actions performed

Full review triggered.

@ThonApple
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

✅ Actions performed

Full review triggered.

@ca5tlechan
Copy link
Copy Markdown
Collaborator

보안/회귀 리뷰 — refactor/frontend-md-compliance

URL 전체가 /api/v1 prefix로 이동했지만 SecurityConfig·LoginFilter가 구 경로를 그대로 참조하고 있어 인증 흐름 전체가 차단됩니다. 아래 항목 순서대로 수정이 필요합니다.


🔴 HIGH — 서비스 전면 차단 (즉시 수정 필요)


[1] SecurityConfig — permitAll 패턴이 구 경로 참조

파일: capstone-common/src/main/java/backend/global/config/SecurityConfig.java
위치: securityFilterChain()authorizeHttpRequests 블록

현재 코드:

.requestMatchers(HttpMethod.POST,
    "/api/users/exist",   // ← 구 경로
    "/api/users",         // ← 구 경로
    "/api/jwt/exchange"   // ← 구 경로
).permitAll()
.requestMatchers(
    "/api/auth/**",       // ← 구 경로 (OAuth 엔드포인트)
    "/api/spots/**",      // ← 구 경로
    "/api/feeds/**",      // ← 구 경로
    "/api/posts/**",      // ← 구 경로
    ...
).permitAll()

문제:
컨트롤러 prefix가 /api/v1로 변경됐지만 SecurityConfig는 여전히 구 경로를 whitelist로 지정합니다.
/api/v1/users(회원가입), /api/v1/jwt/exchange(JWT 재발급), /api/v1/auth/**(OAuth) 모두 .anyRequest().authenticated() 에 걸려 401 반환 → 신규 사용자 가입·로그인 불가.

제안 수정:

.requestMatchers(HttpMethod.POST,
    "/api/v1/users/exist",
    "/api/v1/users",
    "/api/v1/jwt/exchange"
).permitAll()
.requestMatchers(
    "/api/v1/auth/**",
    "/api/v1/spots/**",   // TODO: 인증 도입 후 제거
    "/api/v1/feeds/**",   // TODO: 인증 도입 후 제거
    "/api/v1/posts/**",   // TODO: 인증 도입 후 제거
    "/v3/api-docs/**",
    ...
).permitAll()

[2] LoginFilter — 로그인 경로 하드코딩

파일: capstone-api/src/main/java/backend/global/filter/LoginFilter.java:17

현재 코드:

private static final String LOGIN_PATH = "/api/auth/login";

문제:
AuthController/api/v1/auth로 이동했으므로 프론트엔드는 POST /api/v1/auth/login을 호출합니다. 하지만 LoginFilter는 /api/auth/login만 가로채므로 로그인 요청이 필터를 통과하지 않고 컨트롤러로 흘러가 404 반환.

제안 수정:

private static final String LOGIN_PATH = "/api/v1/auth/login";

[3] SecurityConfig — logoutUrl 하드코딩

파일: capstone-common/src/main/java/backend/global/config/SecurityConfig.java
위치: .logout(logout -> ...) 블록

현재 코드:

.logoutUrl("/api/auth/logout")

문제:
Spring Security의 logout 처리 경로가 구 prefix 그대로입니다. 프론트엔드가 /api/v1/auth/logout으로 호출하면 logout handler가 실행되지 않아 refresh token 쿠키가 지워지지 않음 → 세션 잔존 위험.

제안 수정:

.logoutUrl("/api/v1/auth/logout")

🟡 MEDIUM — 의도치 않은 접근 제어 변경


[4] /api/v1/spots/**, /api/v1/feeds/**, /api/v1/posts/** 인증 필요로 전환

문제:
위 [1]의 구 경로 패턴(/api/spots/** 등)에 있는 "TODO: 인증 도입 후 제거" 주석은 현재 이 경로들이 의도적으로 permitAll임을 나타냅니다. prefix 변경 후 새 경로들은 어떤 permitAll 패턴에도 매칭되지 않아 .anyRequest().authenticated()로 떨어집니다. 개발 중 프론트엔드 연동 테스트 시 갑자기 401이 발생할 수 있습니다.

SecurityConfig를 [1]과 같이 수정하면 해결됩니다.


🔵 LOW — 기능 회귀 (보안 무관)


[5] NotificationController — markAllAsRead 미구현 + 인증 누락

파일: capstone-api/src/main/java/backend/notification/controller/NotificationController.java:68

@PostMapping("/read-all")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void markAllAsRead(@AuthenticationPrincipal CustomUserDetails userDetails) {
    // TODO: implement markAllAsRead in NotificationService
}

문제:
userDetails를 인자로 받지만 사용하지 않으며, 서비스 호출도 없습니다. 호출 시 항상 204만 반환하여 클라이언트가 정상 처리로 오인할 수 있습니다. 머지 전 서비스 구현 또는 501 Not Implemented 반환을 권장합니다.


[6] FeedController — cancelApplication 반환 타입 변경 (204 → 200)

파일: capstone-api/src/main/java/backend/feed/controller/FeedController.java

변경 전: void + @ResponseStatus(NO_CONTENT) → HTTP 204
변경 후: ApiResponse<?> 반환, @ResponseStatus 없음 → HTTP 200

FRONTEND.md 스펙 확인 필요. 204를 기대하는 프론트엔드 코드가 있다면 회귀입니다.


✅ 확인된 항목 (이상 없음)

체크리스트 항목 결과
JWTFilter shouldNotFilter / 경로 화이트리스트 없음 — token-absent pass-through 방식이라 경로 변경 불필요 ✅
NotificationController PATCH→POST + @ResponseStatus(NO_CONTENT) + void ✅ 정상 변환됨
CORS allowedPaths /** 와일드카드 — /api/v1 포함 ✅
/me, /me/password 인증 필수 유지 .anyRequest().authenticated() 에 걸림 ✅ (단, [1] 수정 후에도 유지됨)

요약: [1][2][3]이 동시에 존재하면 회원가입·로그인·로그아웃이 모두 차단되어 서비스가 동작하지 않습니다. 세 파일을 함께 수정한 후 머지하는 것을 권장합니다.

Copy link
Copy Markdown

@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

Caution

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

⚠️ Outside diff range comments (1)
capstone-api/src/main/java/backend/feed/controller/FeedController.java (1)

53-60: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Return 201 Created from the application-creation endpoint.

This handler creates a feed application but currently returns 200. The PR’s contract/test plan expects 201 for POST /applications.

Suggested fix
 `@Operation`(summary = "피드 신청")
 `@PostMapping`("/{feedId}/applications")
+@ResponseStatus(HttpStatus.CREATED)
 public ApiResponse<FeedApplicationResponse> applyToFeed(
 		`@PathVariable` String feedId,
 		`@RequestBody` FeedApplyRequest request) {
🤖 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 `@capstone-api/src/main/java/backend/feed/controller/FeedController.java`
around lines 53 - 60, The applyToFeed handler in FeedController currently
returns a 200 OK wrapped by ApiResponse; change it to return HTTP 201 Created:
update the method to return ResponseEntity<ApiResponse<FeedApplicationResponse>>
and return
ResponseEntity.status(HttpStatus.CREATED).body(ApiResponse.success(response))
after calling feedItemService.applyToFeed(feedId, "dummy-user-id", "황호찬",
request). Ensure you import org.springframework.http.ResponseEntity and
org.springframework.http.HttpStatus and keep the existing
ApiResponse/FeedApplicationResponse usage.
🧹 Nitpick comments (1)
capstone-api/src/main/java/backend/feed/dto/FeedApplyRequest.java (1)

18-22: ⚡ Quick win

Add validation constraints for new request fields.

role and deposit are new contract inputs; guarding them at DTO level prevents invalid payloads from entering service logic.

Suggested fix
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
...
 	`@Schema`(description = "신청 역할 (SUPPORTER | PARTNER)", example = "SUPPORTER")
-	private String role;
+	`@NotBlank`
+	`@Pattern`(regexp = "SUPPORTER|PARTNER")
+	private String role;
 
 	`@Schema`(description = "보증금", example = "10000")
-	private Integer deposit;
+	`@Min`(0)
+	private Integer deposit;
🤖 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 `@capstone-api/src/main/java/backend/feed/dto/FeedApplyRequest.java` around
lines 18 - 22, FeedApplyRequest currently exposes new fields role and deposit
without validation; add bean validation annotations to ensure valid payloads:
annotate role (in class FeedApplyRequest) with `@NotBlank` and restrict allowed
values with `@Pattern`(regexp = "SUPPORTER|PARTNER") (or create/use an enum
validator if preferred), and annotate deposit with `@NotNull` and `@Min`(0) (or
`@PositiveOrZero`) to prevent negative or missing deposits; remember to import
javax.validation.constraints.* and ensure controller methods are annotated with
`@Valid` so these constraints are enforced.
🤖 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 `@capstone-api/src/main/java/backend/feed/controller/FeedController.java`:
- Around line 71-82: addBookmark and removeBookmark are stubs that return 204
without acting; wire in the bookmark service and call its methods to persist and
remove bookmarks using the feedId PathVariable (e.g., inject a BookmarkService
into FeedController and invoke bookmarkService.addBookmark(feedId, userId) in
addBookmark and bookmarkService.removeBookmark(feedId, userId) in
removeBookmark), handle/translate service errors to appropriate HTTP responses
(e.g., 404 if feed not found, 500 for server errors) and only return 204 after
the service call succeeds.

In `@capstone-api/src/main/java/backend/feed/dto/FeedApplicationResponse.java`:
- Around line 26-27: The FeedApplicationResponse DTO added fields appliedRole
and deposit but its static factory method from(...) never maps them, leaving
clients with nulls; update FeedApplicationResponse.from(<source>) to populate
appliedRole and deposit by reading the corresponding values from the source
object (e.g. call builder.appliedRole(source.getAppliedRole()) and
builder.deposit(source.getDeposit()) or map appropriate field names), and apply
the same mapping fix for the other newly added fields referenced around the same
block (the builder in from(...)) so all DTO fields are populated.

In
`@capstone-api/src/main/java/backend/notification/controller/NotificationController.java`:
- Around line 66-71: The endpoint NotificationController.markAllAsRead currently
returns 204 without doing anything; implement the missing behavior by adding a
method NotificationService.markAllAsRead(CustomUserDetails userDetails) (or
similar) that marks all notifications for the authenticated user as read, then
call that service from NotificationController.markAllAsRead using the injected
NotificationService and keep the `@ResponseStatus`(HttpStatus.NO_CONTENT) on
success; alternatively, if you prefer not to implement now, remove the
controller method or change its annotation to
`@ResponseStatus`(HttpStatus.NOT_IMPLEMENTED) to avoid falsely signaling success.

In `@capstone-api/src/main/java/backend/spot/controller/SpotController.java`:
- Around line 61-83: The controller methods getSpotMap and searchSpots currently
ignore their request parameters; either forward them to parameter-aware service
methods (e.g., add/rename and call spotService.getSpots(swLat, swLng, neLat,
neLng, category, type, status, page?, size?) for getSpotMap and
spotService.searchSpots(q, page, size) for searchSpots) or explicitly return 501
until implemented (use ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED) with a
clear message). Update the controller to call the new/updated SpotService
methods (or swap the successful ResponseEntity to a NOT_IMPLEMENTED response) so
the accepted parameters are actually used or the API contract is clearly
unimplemented.

In `@capstone-api/src/main/java/backend/spot/dto/SpotVoteResponse.java`:
- Around line 52-58: The convenience overloads of SpotVoteResponse.of are
dropping spotId by passing null; update both overloads (the methods public
static SpotVoteResponse of(SpotVote vote, List<SpotVoteOptionResponse> options)
and public static SpotVoteResponse of(SpotVote vote,
List<SpotVoteOptionResponse> options, List<Long> myVotedOptionIds)) to forward
the actual spotId (e.g., vote.getSpotId()) into the full factory call instead of
null so the DTO's spotId is populated; make the same change for the other
convenience overload referenced in the comment.

In `@capstone-api/src/main/java/backend/spot/service/SpotService.java`:
- Around line 578-582: The code currently treats only null currentUserId as
unauthenticated, allowing blank strings to be stored; update the uploader/author
selection logic to treat blank strings as unauthenticated by using a blank check
(e.g., currentUserId != null && !currentUserId.isBlank()) before using it;
replace the ternary that sets uploaderId for SpotFile.builder() and the similar
authorId logic in the SpotNote/other creation block (the branch around lines
629-633) to use this non-blank check and fall back to FALLBACK_USER_ID when
currentUserId is null or blank.

---

Outside diff comments:
In `@capstone-api/src/main/java/backend/feed/controller/FeedController.java`:
- Around line 53-60: The applyToFeed handler in FeedController currently returns
a 200 OK wrapped by ApiResponse; change it to return HTTP 201 Created: update
the method to return ResponseEntity<ApiResponse<FeedApplicationResponse>> and
return
ResponseEntity.status(HttpStatus.CREATED).body(ApiResponse.success(response))
after calling feedItemService.applyToFeed(feedId, "dummy-user-id", "황호찬",
request). Ensure you import org.springframework.http.ResponseEntity and
org.springframework.http.HttpStatus and keep the existing
ApiResponse/FeedApplicationResponse usage.

---

Nitpick comments:
In `@capstone-api/src/main/java/backend/feed/dto/FeedApplyRequest.java`:
- Around line 18-22: FeedApplyRequest currently exposes new fields role and
deposit without validation; add bean validation annotations to ensure valid
payloads: annotate role (in class FeedApplyRequest) with `@NotBlank` and restrict
allowed values with `@Pattern`(regexp = "SUPPORTER|PARTNER") (or create/use an
enum validator if preferred), and annotate deposit with `@NotNull` and `@Min`(0) (or
`@PositiveOrZero`) to prevent negative or missing deposits; remember to import
javax.validation.constraints.* and ensure controller methods are annotated with
`@Valid` so these constraints are enforced.
🪄 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

Run ID: 3785ed21-b479-4850-bf08-9ac2c1598253

📥 Commits

Reviewing files that changed from the base of the PR and between 9dd246b and 5b17f6d.

📒 Files selected for processing (16)
  • capstone-api/src/main/java/backend/auth/controller/AuthController.java
  • capstone-api/src/main/java/backend/auth/controller/JwtController.java
  • capstone-api/src/main/java/backend/feed/controller/FeedController.java
  • capstone-api/src/main/java/backend/feed/dto/FeedApplicationResponse.java
  • capstone-api/src/main/java/backend/feed/dto/FeedApplyRequest.java
  • capstone-api/src/main/java/backend/notification/controller/NotificationController.java
  • capstone-api/src/main/java/backend/post/controller/PostController.java
  • capstone-api/src/main/java/backend/spot/controller/SpotController.java
  • capstone-api/src/main/java/backend/spot/dto/SpotChecklistResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotFileResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotNoteResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotParticipantResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotVoteOptionResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotVoteResponse.java
  • capstone-api/src/main/java/backend/spot/service/SpotService.java
  • capstone-api/src/main/java/backend/user/controller/UserController.java

Comment thread capstone-api/src/main/java/backend/feed/dto/FeedApplicationResponse.java Outdated
Comment thread capstone-api/src/main/java/backend/spot/controller/SpotController.java Outdated
Comment thread capstone-api/src/main/java/backend/spot/dto/SpotVoteResponse.java
Comment thread capstone-api/src/main/java/backend/spot/service/SpotService.java Outdated
Copy link
Copy Markdown
Collaborator

@hoTan35 hoTan35 left a comment

Choose a reason for hiding this comment

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

SPOT 백엔드 프로젝트 PR #32 (refactor/frontend-md-compliance)에 대한 리뷰 결과입니다.

docs/FRONTEND.md 명세(v1.5, v1.6)를 기준으로 코드를 대조한 결과, 다수의 명세 불일치 및 구현 누락이 발견되었습니다.


🔍 PR 리뷰 결과 리포트

1. FRONTEND.md 섹션 1.5, 1.6 명세 불일치 (DTO)

  • ID 타입 불일치 (String vs Long)

    • 파일: capstone-api/src/main/java/backend/spot/dto/SpotVoteResponse.java, SpotVoteOptionResponse.java, SpotChecklistResponse.java, SpotFileResponse.java, SpotNoteResponse.java
    • 설명: FRONTEND.md에서는 모든 ID를 string으로 정의하고 있으나, 위 DTO들에서는 Long 타입을 사용하고 있습니다. 프론트엔드 라이브러리(ex. Zod, TypeScript Interface)와의 호환성을 위해 타입을 맞추거나 백엔드에서 String으로 변환하여 응답해야 합니다.
  • Spot Collaboration (1.5) 필드 누락 및 오류

    • 파일: capstone-api/src/main/java/backend/spot/dto/SpotChecklistResponse.java
      • assigneeId, assigneeNickname 필드가 누락되었습니다.
    • 파일: capstone-api/src/main/java/backend/spot/dto/SpotFileResponse.java (L44, L56)
      • sizeBytes 필드가 null로 하드코딩되어 있습니다. 명세상 필수 값이므로 실제 파일 크기를 반영해야 합니다.
  • Feed (1.6) 필드 누락 및 명명 규칙 위반

    • 파일: capstone-api/src/main/java/backend/feed/dto/FeedAuthorProfile.java (L29)
      • @JsonProperty("avatar_url") 사용으로 인해 JSON 응답이 avatar_url(snake_case)로 나갑니다. 명세는 avatarUrl(camelCase)입니다.
    • 파일: capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java
      • isRentable, myApplicationRole, myApplicationDeposit 필드가 누락되었습니다.
    • 파일: capstone-api/src/main/java/backend/feed/dto/FeedApplicationResponse.java (L30-36)
      • appliedRole, deposit 필드는 선언되어 있으나, from() 팩토리 메서드에서 빌더에 포함되지 않아 항상 null 또는 기본값으로 응답됩니다.

2. 응답 Envelope 규격 불일치

  • 파일: capstone-api/src/main/java/backend/feed/controller/FeedController.java (L74)
    • cancelApplication 메서드가 ApiResponse.success()를 반환하여 data: null이 응답됩니다.
    • FRONTEND.md 명세: { data: { feedId, status: "CANCELLED" } } 형태의 응답을 기대하고 있습니다.

3. 엔티티-DTO 간 불일치 및 마이그레이션 누락

  • 파일: capstone-domain/src/main/java/backend/feed/entity/FeedApplication.java
    • DTO(FeedApplicationResponse)에는 appliedRole, deposit이 추가되었으나, 엔티티에는 해당 필드가 없고 DB 마이그레이션 파일도 존재하지 않습니다.
    • 의견: 현재 DTO에서만 필드를 추가하고 매핑을 누락한 상태라 API가 불완전합니다. 후속 작업으로 분리하기보다는, **프론트엔드 명세 준수(Compliance)**가 목적인 PR인 만큼 엔티티 수정 및 마이그레이션을 이번 PR에 포함시키는 것이 아키텍처 일관성 측면에서 바람직합니다.

4. 스타일 및 Import 순서 위반

  • Import 순서: static→java→javax→org→com→backend 순서를 준수해야 하나, FeedController.java, SpotVoteResponse.java 등에서 backend.* 패키지가 io.swagger.*lombok.*보다 앞에 위치하고 있습니다.
  • Checkstyle: 실행 결과 약 6,700건의 경고(대부분 CRLF vs LF Newline 문제)가 발생합니다. 프로젝트 표준인 LF로 파일 엔딩을 일괄 조정할 필요가 있습니다.

5. SpotVoteOptionResponse.from() 위험성 확인

  • 확인 결과: SpotVoteOptionResponse.from()은 현재 프로젝트 내에서 직접 호출되고 있지 않습니다. 다만, SpotService.java (L314)에서 유사하게 voterIdsList.of()로 초기화하여 반환하는 로직이 있습니다. 이는 투표 생성 직후의 응답이므로 현재로서는 정상 동작으로 판단되나, 추후 from() 메서드를 무분별하게 사용할 경우 기존 투표 데이터를 조회할 때 투표자 목록이 누락될 위험이 있으므로 주의가 필요합니다.

💬 종합 의견

Request Changes (❌ 수정 필요)

프론트엔드 명세 준수를 위한 리팩토링 PR임에도 불구하고, (1) 주요 필드 누락, (2) snake_case 혼용, (3) 응답 Envelope 불일치 등 명세와 상충하는 지점이 많습니다. 특히 appliedRoledeposit은 도메인 모델까지 영향이 가는 변경사항이므로, 이를 완전히 구현하거나 혹은 명세에서 제외하는 방향으로 동기화가 필요합니다. 위 이슈들을 수정한 후 다시 리뷰 요청 부탁드립니다.

ca5tlechan 리뷰 (HIGH 보안):
- SecurityConfig permitAll/oauth/logoutUrl 경로 /api/v1 prefix로 통일
- LoginFilter LOGIN_PATH /api/auth/login → /api/v1/auth/login

CodeRabbit 리뷰:
- FeedController.applyToFeed @ResponseStatus(CREATED) 추가
- FeedController.cancelApplication 응답 envelope {feedId, status:CANCELLED}
- Feed bookmark / Notification read-all / Spot map·search 모두 501 NOT_IMPLEMENTED
- SpotVoteResponse.of() 오버로드들이 vote.getSpotId() 전달
- SpotService.resolveUserId() 도입: null + blank string 모두 fallback 처리

hoTan35 리뷰:
- FeedApplication 엔티티에 appliedRole(enum) + deposit(Integer) 컬럼 추가
- FeedApplicationRole enum 신설 (SUPPORTER, PARTNER)
- FeedApplyRequest.role 타입 String → FeedApplicationRole
- FeedApplicationResponse.from() 빌더에 appliedRole, deposit 매핑 누락 수정
- FeedAuthorProfile @JsonProperty("avatar_url") 제거 (camelCase 통일)
- FeedItemResponse 누락 필드 추가: myApplicationRole, myApplicationDeposit, isRentable

후속 작업으로 분리:
- ID Long → String 전환 (전체 도메인 영향, 별도 PR)
- SpotFile.sizeBytes, SpotChecklist.assignee* 엔티티 컬럼 + DB 마이그레이션
- markAllAsRead 실제 구현

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ThonApple
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

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

Caution

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

⚠️ Outside diff range comments (1)
capstone-api/src/main/java/backend/feed/controller/FeedController.java (1)

56-64: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove hardcoded caller identity from application flows.

Using "dummy-user-id"/fixed nickname makes all users share one identity, which breaks per-user application and cancellation behavior.

Proposed fix
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import backend.global.security.CustomUserDetails;
...
 public ApiResponse<FeedApplicationResponse> applyToFeed(
 		`@PathVariable` String feedId,
-		`@RequestBody` FeedApplyRequest request) {
-	// 추후 인증 도입 시 실제 userId, nickname으로 교체
+		`@RequestBody` FeedApplyRequest request,
+		`@AuthenticationPrincipal` CustomUserDetails userDetails) {
 	FeedApplicationResponse response = feedItemService.applyToFeed(
-			feedId, "dummy-user-id", "황호찬", request);
+			feedId, userDetails.getUserId(), userDetails.getNickname(), request);
 	return ApiResponse.success(response);
 }
...
 public ApiResponse<Map<String, String>> cancelApplication(`@PathVariable` String feedId) {
-	feedItemService.cancelApplication(feedId, "dummy-user-id");
+public ApiResponse<Map<String, String>> cancelApplication(
+		`@PathVariable` String feedId,
+		`@AuthenticationPrincipal` CustomUserDetails userDetails) {
+	feedItemService.cancelApplication(feedId, userDetails.getUserId());
 	return ApiResponse.success(Map.of(
 			"feedId", feedId,
 			"status", "CANCELLED"
 	));
 }

Also applies to: 68-74

🤖 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 `@capstone-api/src/main/java/backend/feed/controller/FeedController.java`
around lines 56 - 64, The controller currently hardcodes the caller identity
("dummy-user-id" and "황호찬") in FeedController.applyToFeed when calling
feedItemService.applyToFeed (and similarly in the cancellation endpoint), which
breaks per-user behavior; replace the hardcoded values by retrieving the
authenticated user's id and nickname (e.g., from the security
Principal/Authentication or a request header) and pass those values into
feedItemService.applyToFeed and the cancel method (match the parameter order
expected by feedItemService), or add a small adapter method to extract
userId/nickname and forward them to feedItemService so each request uses the
real caller identity.
🤖 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 `@capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java`:
- Around line 74-82: The DTO fields myApplicationRole, myApplicationDeposit, and
isRentable are declared but not populated in the static from(...) builder;
update FeedItemResponse.from(...) to set these three fields from the source
Feed/FeedApplication/availability data (use the same source objects used for
other fields), e.g. determine the current user's FeedApplication role and
deposit and compute rental availability, then call
builder.myApplicationRole(...), builder.myApplicationDeposit(...), and
builder.isRentable(...) before build(); reference FeedItemResponse.from, the
builder on FeedItemResponse, and the field names
myApplicationRole/myApplicationDeposit/isRentable when making the change.

---

Outside diff comments:
In `@capstone-api/src/main/java/backend/feed/controller/FeedController.java`:
- Around line 56-64: The controller currently hardcodes the caller identity
("dummy-user-id" and "황호찬") in FeedController.applyToFeed when calling
feedItemService.applyToFeed (and similarly in the cancellation endpoint), which
breaks per-user behavior; replace the hardcoded values by retrieving the
authenticated user's id and nickname (e.g., from the security
Principal/Authentication or a request header) and pass those values into
feedItemService.applyToFeed and the cancel method (match the parameter order
expected by feedItemService), or add a small adapter method to extract
userId/nickname and forward them to feedItemService so each request uses the
real caller identity.
🪄 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

Run ID: 3fb780c9-6a40-43f2-aa09-49da69a243e3

📥 Commits

Reviewing files that changed from the base of the PR and between 5b17f6d and 19be0ef.

📒 Files selected for processing (14)
  • capstone-api/src/main/java/backend/feed/controller/FeedController.java
  • capstone-api/src/main/java/backend/feed/dto/FeedApplicationResponse.java
  • capstone-api/src/main/java/backend/feed/dto/FeedApplyRequest.java
  • capstone-api/src/main/java/backend/feed/dto/FeedAuthorProfile.java
  • capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java
  • capstone-api/src/main/java/backend/feed/service/FeedItemService.java
  • capstone-api/src/main/java/backend/global/filter/LoginFilter.java
  • capstone-api/src/main/java/backend/notification/controller/NotificationController.java
  • capstone-api/src/main/java/backend/spot/controller/SpotController.java
  • capstone-api/src/main/java/backend/spot/dto/SpotVoteResponse.java
  • capstone-api/src/main/java/backend/spot/service/SpotService.java
  • capstone-common/src/main/java/backend/global/config/SecurityConfig.java
  • capstone-domain/src/main/java/backend/feed/entity/FeedApplication.java
  • capstone-domain/src/main/java/backend/feed/entity/FeedApplicationRole.java
💤 Files with no reviewable changes (1)
  • capstone-api/src/main/java/backend/feed/dto/FeedAuthorProfile.java
✅ Files skipped from review due to trivial changes (1)
  • capstone-api/src/main/java/backend/global/filter/LoginFilter.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • capstone-api/src/main/java/backend/feed/dto/FeedApplyRequest.java
  • capstone-api/src/main/java/backend/notification/controller/NotificationController.java
  • capstone-api/src/main/java/backend/spot/service/SpotService.java

Comment on lines +74 to +82
@Schema(description = "내 신청 역할 (SUPPORTER | PARTNER)")
private FeedApplicationRole myApplicationRole;

@Schema(description = "내 신청 보증금")
private Integer myApplicationDeposit;

@Schema(description = "대여 가능 여부", example = "false")
private Boolean isRentable;

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

Populate newly added response fields in from(...).

myApplicationRole, myApplicationDeposit, and isRentable are declared but never set in the builder, so clients always get null for them.

Proposed fix
 public static FeedItemResponse from(FeedItem feedItem, Long applicantCount, Boolean isBookmarked,
-		FeedApplicationStatus myApplicationStatus, FeedAuthorProfile authorProfile) {
+		FeedApplicationStatus myApplicationStatus, FeedApplicationRole myApplicationRole,
+		Integer myApplicationDeposit, Boolean isRentable, FeedAuthorProfile authorProfile) {
 	return FeedItemResponse.builder()
 			...
 			.myApplicationStatus(myApplicationStatus)
+			.myApplicationRole(myApplicationRole)
+			.myApplicationDeposit(myApplicationDeposit)
+			.isRentable(isRentable)
 			.authorProfile(authorProfile)
 			...
 			.build();
 }

Also applies to: 102-127

🤖 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 `@capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java` around
lines 74 - 82, The DTO fields myApplicationRole, myApplicationDeposit, and
isRentable are declared but not populated in the static from(...) builder;
update FeedItemResponse.from(...) to set these three fields from the source
Feed/FeedApplication/availability data (use the same source objects used for
other fields), e.g. determine the current user's FeedApplication role and
deposit and compute rental availability, then call
builder.myApplicationRole(...), builder.myApplicationDeposit(...), and
builder.isRentable(...) before build(); reference FeedItemResponse.from, the
builder on FeedItemResponse, and the field names
myApplicationRole/myApplicationDeposit/isRentable when making the change.

@ThonApple
Copy link
Copy Markdown
Collaborator Author

@hoTan35 @ca5tlechan 코멘트 반영해서 19be0ef 푸시했습니다. 재리뷰 부탁드립니다.

Copy link
Copy Markdown
Collaborator

@hoTan35 hoTan35 left a comment

Choose a reason for hiding this comment

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

리뷰 요약

전체적으로 DTO 필드 추가 등 일부 진척은 있으나, 체크리스트의 핵심 불일치들이 아직 남아 있습니다. 아래 인라인 코멘트와 함께 확인 부탁드립니다.

미반영 항목 요약

항목 상태
DTO 구조 불일치 (SpotSchedule, Checklist, IncludedItem, AddOn, FeedItem) ❌ 미수정
cancelApplication 응답 envelope ❌ 미수정
FeedApplication 엔티티 마이그레이션 ⚠️ DTO 껍데기만, 엔티티·팩토리 미반영
import 순서 checkstyle ⚠️ 위반 가능성
SpotVoteOptionResponse.from() dead code ✅ 런타임 위험 없음, 정리 권고

@@ -61,10 +65,27 @@ public ApiResponse<FeedApplicationResponse> applyToFeed(
}

@Operation(summary = "피드 신청 취소")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Issue] cancelApplication 응답 envelope 불일치

FRONTEND.md 명세:

responses:
  200:
    application/json:
      data:
        feedId: string
        status: CANCELLED

현재 ApiResponse.success()는 body 없이 반환되어 프론트가 data.feedId를 읽을 수 없습니다.

수정 예시:

Map<String, Object> body = Map.of("feedId", feedId, "status", "CANCELLED");
return ApiResponse.success(body);

@@ -32,6 +35,8 @@ public static FeedApplicationResponse from(FeedApplication application) {
.userId(application.getUserId())
.proposal(application.getProposal())
.status(application.getStatus())
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Issue] FeedApplicationResponse.from() — appliedRole, deposit 항상 null

FEENTEND.md 명세:

type FeedApplication = {
    appliedRole: FeedApplicationRole;  // required
    deposit: number;                   // required
    ...
};

from() 팩토리에서 두 필드를 설정하지 않아 항상 null 응답이 나갑니다. 근본 원인은 FeedApplication 엔티티에 appliedRole, deposit 컬럼 자체가 없기 때문입니다.

후속 작업으로 분리해도 되지만, 현재 상태에서는 명세상 required 필드가 null로 내려가므로 프론트 파싱이 깨질 수 있습니다. 엔티티 컬럼 추가 + 마이그레이션 스크립트를 별도 이슈/PR로 트래킹 해주세요.

- IncludedItem: note → value (FRONTEND.md PriceBreakdown.included_items)
- AddOn: note → explanation (FRONTEND.md PriceBreakdown.optional_addons)
- SpotChecklistResponse: assigneeId, assigneeNickname (nullable) 추가
- SpotVoteOptionResponse: dead code from() 제거 (호출처 없음)
- SpotService, SpotController: import 순서 정리

미반영 (별도 PR로 분리):
- SpotSchedule 구조 (proposedSlots / confirmedSlot / ScheduleSlot): 도메인 엔티티
  scheduledAt → ScheduleSlot 전환 필요, DB 마이그레이션 동반 큰 변경
- FeedController dummy-user-id 제거: 전체 코드베이스 공통 패턴(SpotController,
  ChatController 동일), 인증 통합 PR에서 일괄 처리 권장

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ThonApple
Copy link
Copy Markdown
Collaborator Author

@hoTan35 2차 리뷰 감사합니다. 항목별로 답변드립니다.

이미 반영된 항목 (19be0ef 커밋 — 재확인 부탁)

항목 상태
cancelApplication envelope Map.of("feedId", feedId, "status", "CANCELLED") 반환 (FeedController.java:69-74)
FeedApplication 엔티티 마이그레이션 appliedRole(enum) + deposit(Integer) 컬럼 추가됨, FeedApplicationResponse.from() 매핑됨 (FeedApplication.java:50-54, FeedApplicationResponse.java:38-39)

위 두 항목은 인라인 코멘트 위치(라인 67, 37)에서 실제 코드를 다시 확인 부탁드립니다.

이번 커밋(d8e5a94)에서 추가 반영

  • IncludedItem.notevalue (FRONTEND.md PriceBreakdown.included_items 명세)
  • AddOn.noteexplanation (FRONTEND.md PriceBreakdown.optional_addons 명세)
  • SpotChecklistResponse: assigneeId, assigneeNickname (nullable) 필드 추가
  • SpotVoteOptionResponse.from() dead code 제거
  • import 순서 checkstyle warning 정리

별도 PR로 분리 제안

항목 사유
SpotSchedule 구조 변경 (proposedSlots/confirmedSlot/ScheduleSlot) 도메인 엔티티 scheduledAt → 슬롯 컬렉션 전환 필요, DB 마이그레이션 동반. 이번 PR(API 계약 표면 정리) 스코프 초과
dummy-user-id 제거 (CodeRabbit 지적) SpotController/ChatController 등 전 컨트롤러 공통 패턴. 인증 통합 PR에서 일괄 처리하는 게 효율적
ID Long → String 전환 전체 도메인 엔티티 영향, 별도 PR

위 분리 항목은 머지 후 follow-up 이슈로 등록하겠습니다. 추가 확인 부탁드립니다.

@ca5tlechan
Copy link
Copy Markdown
Collaborator

2차 리뷰 — 1차 반영분 확인 + 잔여 이슈

✅ 1차 리뷰 반영 확인

항목 결과
SecurityConfig permitAll → /api/v1/** 전환 ✅ 완료
LoginFilter.LOGIN_PATH/api/v1/auth/login ✅ 완료
SecurityConfig logoutUrl/api/v1/auth/logout ✅ 완료
OAuth authorizationEndpoint.baseUri/api/v1/auth/oauth ✅ 완료 (SecurityConfig도 같이 수정됨)
markAllAsRead 501 반환으로 교체 ✅ 완료
cancelApplication 응답 envelope (feedId+status) ✅ 완료 (hoTan35 리뷰도 반영됨)
FeedApplicationResponse.from() appliedRole·deposit 누락 ✅ 완료
SpotVoteResponse.spotId null 전달 문제 vote.getSpotId() 직접 호출로 수정됨
resolveUserId() blank 처리 통일 ✅ 완료

🔴 HIGH — 신규 발견


[1] SimulationController 경로와 SecurityConfig permitAll 불일치

파일: capstone-common/src/main/java/backend/global/config/SecurityConfig.java + capstone-api/.../simulation/controller/SimulationController.java

현재 상태:

// SecurityConfig (이번 PR에서 변경됨)
.requestMatchers(HttpMethod.GET, "/api/v1/sim/**").permitAll()

// SimulationController (이번 PR에서 미변경)
@RequestMapping("/api/sim/runs")

문제:
SecurityConfig의 /api/sim/**/api/v1/sim/** 업데이트가 SimulationController 실제 경로(/api/sim/runs)와 맞지 않습니다. 수정 후 시뮬레이션 API가 .anyRequest().authenticated() 에 걸려 인증 없이 호출하던 시뮬레이션 재생 엔드포인트 전체가 401 반환.

제안 수정 (둘 중 하나):

옵션 A — SimulationController/api/v1/sim/runs로 이동:

@RequestMapping("/api/v1/sim/runs")

옵션 B — SecurityConfig에 구 경로 유지:

.requestMatchers(HttpMethod.GET, "/api/sim/**").permitAll()

🟡 MEDIUM — 잔여 이슈


[2] FeedAuthorProfile.avatarUrl@JsonProperty("avatar_url") 제거로 직렬화 키 변경

파일: capstone-api/src/main/java/backend/feed/dto/FeedAuthorProfile.java

이번 PR 변경:

-import com.fasterxml.jackson.annotation.JsonProperty;
 ...
-	@JsonProperty("avatar_url")
 	private String avatarUrl;

문제:
@JsonProperty 제거 후 이 필드는 "avatarUrl" (camelCase)로 직렬화됩니다. FRONTEND.md 또는 기존 클라이언트가 "avatar_url" (snake_case)을 기대하고 있다면 아바타 이미지가 표시되지 않는 UI 회귀 발생. FRONTEND.md 명세와 맞춰 의도적인 변경인지 확인 필요.

FRONTEND.md에 avatar_url로 명시돼 있다면 어노테이션 복원 필요:

@JsonProperty("avatar_url")
private String avatarUrl;

🔵 LOW — 잔여 이슈 (CodeRabbit 중복 지적)


[3] FeedItemResponse — 새 필드 3개가 빌더에서 누락

파일: capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java

이번 PR에서 myApplicationRole, myApplicationDeposit, isRentable 3개 필드를 선언했지만, from(FeedItem, Long, Boolean, FeedApplicationStatus, FeedAuthorProfile) 빌더에 이 값들을 채우는 코드가 없습니다. 호출 시 항상 null 반환.

myApplicationRole·myApplicationDeposit은 현재 로그인한 유저의 신청 정보가 필요해 단순 DTO 수정으로는 해결이 안 되므로, 다음 중 하나를 권장합니다:

  • 이번 PR 범위에서 제거하고 별도 이슈로 분리
  • 서비스 레이어에서 FeedApplication 조회 후 오버로드 추가

✅ 최종 의견

[1] SimulationController 경로 불일치는 배포 즉시 시뮬레이션 기능 장애로 이어지므로 머지 전 반드시 수정해야 합니다. [2]는 FRONTEND.md 명세와의 확인이 필요하고, [3]은 범위 외 작업이므로 별도 이슈 등록 후 이번 PR에서 선언부를 제거하는 것을 권장합니다.

보안·인증 관련 HIGH 이슈는 모두 해소됐습니다. 수고하셨습니다 🙌

Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
capstone-api/src/main/java/backend/spot/dto/SpotVoteOptionResponse.java (1)

32-37: ⚡ Quick win

Normalize voterIds to an empty list in the DTO factory.

If voterIds is null at Line 37, the API can emit null instead of []. Normalizing here makes the response contract safer for consumers.

Proposed fix
 public static SpotVoteOptionResponse of(SpotVoteOption option, List<String> voterIds) {
     return SpotVoteOptionResponse.builder()
         .id(option.getId())
         .label(option.getContent())
         .voteCount(option.getVoteCount())
-        .voterIds(voterIds)
+        .voterIds(voterIds == null ? List.of() : List.copyOf(voterIds))
         .build();
 }
🤖 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 `@capstone-api/src/main/java/backend/spot/dto/SpotVoteOptionResponse.java`
around lines 32 - 37, The factory method SpotVoteOptionResponse.of currently
passes voterIds through to the builder which can allow a null to be emitted;
change the method to normalize voterIds to an empty List when null (e.g.,
replace voterIds with Collections.emptyList() or new ArrayList<>() before
calling SpotVoteOptionResponse.builder().voterIds(...)) so the DTO always
returns [] instead of null; update the of(...) implementation where
id/label/voteCount are set and voterIds is supplied.
🤖 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 `@capstone-api/src/main/java/backend/spot/dto/SpotChecklistResponse.java`:
- Around line 29-33: The SpotChecklistResponse factory method
from(SpotChecklist) currently never sets the newly added fields assigneeId and
assigneeNickname, so populate these two fields when building the response: in
SpotChecklistResponse.from(SpotChecklist) extract the assignee ID and nickname
from the input SpotChecklist (e.g., spot.getAssigneeId() and
spot.getAssigneeNickname() or the appropriate getters) and set them on the
response builder/constructor alongside the other mapped fields; ensure both
assigneeId and assigneeNickname are assigned where other properties are mapped
so responses no longer return null for these fields.

---

Nitpick comments:
In `@capstone-api/src/main/java/backend/spot/dto/SpotVoteOptionResponse.java`:
- Around line 32-37: The factory method SpotVoteOptionResponse.of currently
passes voterIds through to the builder which can allow a null to be emitted;
change the method to normalize voterIds to an empty List when null (e.g.,
replace voterIds with Collections.emptyList() or new ArrayList<>() before
calling SpotVoteOptionResponse.builder().voterIds(...)) so the DTO always
returns [] instead of null; update the of(...) implementation where
id/label/voteCount are set and voterIds is supplied.
🪄 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

Run ID: b1a22b12-194f-4070-be44-a45a342f6401

📥 Commits

Reviewing files that changed from the base of the PR and between 19be0ef and d8e5a94.

📒 Files selected for processing (6)
  • capstone-api/src/main/java/backend/feed/dto/AddOn.java
  • capstone-api/src/main/java/backend/feed/dto/IncludedItem.java
  • capstone-api/src/main/java/backend/spot/controller/SpotController.java
  • capstone-api/src/main/java/backend/spot/dto/SpotChecklistResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotVoteOptionResponse.java
  • capstone-api/src/main/java/backend/spot/service/SpotService.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • capstone-api/src/main/java/backend/spot/controller/SpotController.java
  • capstone-api/src/main/java/backend/spot/service/SpotService.java

Comment on lines +29 to +33
@Schema(description = "담당자 ID (선택)", nullable = true)
private String assigneeId;

@Schema(description = "담당자 닉네임 (선택)", nullable = true)
private String assigneeNickname;
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

Map newly added assignee fields in the factory path.

Line 29-Line 33 add assigneeId/assigneeNickname, but Line 38-Line 44 never sets them. Responses built via from(SpotChecklist) will always return these as null.

Proposed fix
 public static SpotChecklistResponse from(SpotChecklist checklist) {
+    return from(checklist, null, null);
+}
+
+public static SpotChecklistResponse from(SpotChecklist checklist, String assigneeId, String assigneeNickname) {
     return SpotChecklistResponse.builder()
         .id(checklist.getId())
         .text(checklist.getContent())
         .completed(checklist.getIsDone())
+        .assigneeId(assigneeId)
+        .assigneeNickname(assigneeNickname)
         .createdAt(checklist.getCreatedAt())
         .build();
 }

Also applies to: 38-44

🤖 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 `@capstone-api/src/main/java/backend/spot/dto/SpotChecklistResponse.java`
around lines 29 - 33, The SpotChecklistResponse factory method
from(SpotChecklist) currently never sets the newly added fields assigneeId and
assigneeNickname, so populate these two fields when building the response: in
SpotChecklistResponse.from(SpotChecklist) extract the assignee ID and nickname
from the input SpotChecklist (e.g., spot.getAssigneeId() and
spot.getAssigneeNickname() or the appropriate getters) and set them on the
response builder/constructor alongside the other mapped fields; ensure both
assigneeId and assigneeNickname are assigned where other properties are mapped
so responses no longer return null for these fields.

ca5tlechan HIGH [1]:
- SimulationController @RequestMapping /api/sim/runs → /api/v1/sim/runs
  (SecurityConfig permitAll 경로 변경으로 인한 401 회귀 차단)

CodeRabbit:
- SpotVoteOptionResponse.of() voterIds null-safe (null → List.of())
- SpotChecklistResponse.from() assigneeId/assigneeNickname 명시 null 매핑

ca5tlechan LOW [3]:
- FeedItemResponse.from() 신규 3필드(myApplicationRole, myApplicationDeposit,
  isRentable) 명시 null 매핑으로 JSON 키 직렬화 보장

미반영 (의도적):
- ca5tlechan [2] FeedAuthorProfile avatar_url → avatarUrl 변경:
  FRONTEND.md 1.1 SupporterProfile/PartnerProfile 모두 avatarUrl(camelCase)
  명세이므로 의도적 변경. 프론트 동기화 필요 시 알려달라고 답글 예정.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ThonApple
Copy link
Copy Markdown
Collaborator Author

@ca5tlechan 2차 리뷰 감사합니다. 3ce48c4 커밋에 반영했습니다.

반영 (3ce48c4)

항목 처리
🔴 [1] SimulationController 경로 불일치 /api/sim/runs/api/v1/sim/runs (옵션 A 적용)
🔵 [3] FeedItemResponse 누락 3필드 from() 빌더에 명시 null 매핑 (JSON 키 보장) — 실제 채우는 로직은 별도 PR

CodeRabbit 동시 반영

  • SpotVoteOptionResponse.of() voterIds null-safe (null → List.of())
  • SpotChecklistResponse.from() assigneeId/assigneeNickname 명시 null

🟡 [2] FeedAuthorProfile avatarUrl — 의도적 변경

FRONTEND.md 1.1 SupporterProfile/PartnerProfile 모두 avatarUrl(camelCase) 명세입니다:

type SupporterProfile = {
    avatarUrl?: string;  // camelCase
    ...
};

그리고 0번 공통 규칙도 camelCase가 기본이라 @JsonProperty("avatar_url") 제거는 명세 준수입니다. 프론트 클라이언트가 아직 snake_case로 파싱한다면 프론트 측 동기화가 필요합니다 — 필요 시 알려주세요.

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

@ThonApple: 수정 내용 확인했습니다. 항목별 반영 내용과 FeedAuthorProfile.avatarUrl camelCase 변경의 FRONTEND.md 근거 설명 감사합니다. 요청하신 대로 리뷰를 진행하겠습니다.

🐇

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@hoTan35
Copy link
Copy Markdown
Collaborator

hoTan35 commented May 19, 2026

@ThonApple 답변 감사합니다. 확인 중 두 가지 미해결 항목을 발견했습니다.

1. FeedApplication DB 마이그레이션 파일 누락
appliedRole/deposit 엔티티 필드는 추가됐지만, docs/migrations/에 해당 컬럼을 추가하는 SQL 파일이 없습니다. 런타임에 feed_applications 테이블에 컬럼이 없어 에러가 발생합니다. 1차 리뷰에서 지적한 핵심 항목이고 답변에도 빠져 있어 확인 부탁드립니다.

2. SpotFileResponse.sizeBytes null 하드코딩
1차 리뷰에서 지적했는데 이번 답변에서 언급이 없었습니다. from()/of() 둘 다 여전히 sizeBytes(null)로 고정돼 있습니다. FRONTEND.md 명세상 필수 필드이므로 처리 방향(이번 PR 반영 or 별도 이슈)을 알려주세요.

[1] FeedApplication DB 마이그레이션 추가
- docs/migrations/2026-05-19_feed_application_role_deposit.sql
- applied_role VARCHAR(20) + deposit INTEGER 컬럼 추가
- 기존 row는 NULL 유지, 신규 신청부터 채워짐

[2] SpotFile.sizeBytes 엔티티/DTO/마이그레이션 완성
- SpotFile 엔티티에 size_bytes BIGINT NULL 컬럼 추가
- UploadFileRequest.sizeBytes 필드 추가 (nullable)
- SpotService.uploadFile() request 값 전달
- SpotFileResponse.from()/of() file.getSizeBytes() 매핑 (기존 null 하드코딩 제거)
- docs/migrations/2026-05-19_spot_file_size_bytes.sql

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ThonApple
Copy link
Copy Markdown
Collaborator Author

@hoTan35 두 항목 모두 ff17bfe 커밋에 반영했습니다.

[1] FeedApplication 마이그레이션 ✅

docs/migrations/2026-05-19_feed_application_role_deposit.sql 추가:

  • applied_role VARCHAR(20) NULL (enum SUPPORTER|PARTNER)
  • deposit INTEGER NULL
  • 기존 row는 NULL 유지, 신규 신청부터 채워짐
  • 기존 chat_block 마이그레이션과 동일 포맷(헤더/BEGIN-COMMIT/검증 쿼리)

[2] SpotFile.sizeBytes ✅

이번 PR에서 전체 처리:

  • SpotFile 엔티티에 size_bytes BIGINT NULL 컬럼 추가
  • UploadFileRequestsizeBytes 필드 추가 (nullable)
  • SpotService.uploadFile() 에서 request 값 전달
  • SpotFileResponse.from()/of() file.getSizeBytes() 매핑 (기존 null 하드코딩 제거)
  • docs/migrations/2026-05-19_spot_file_size_bytes.sql 추가

UploadFileRequest.sizeBytes 는 nullable 로 두었습니다 — 클라이언트가 보내지 않으면 NULL 저장, 보내면 그대로 영속화. FRONTEND.md 가 required 로 명시했지만 기존 데이터 호환을 위해 백엔드 강제는 nullable, 클라이언트 측에서 항상 보내도록 합의해주시면 됩니다.

재확인 부탁드립니다.

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (2)
capstone-api/src/main/java/backend/spot/dto/UploadFileRequest.java (1)

21-22: ⚡ Quick win

Consider adding validation for non-negative file size.

The sizeBytes field accepts any Long value, including negatives. File sizes should be non-negative.

✅ Proposed validation constraint
+import jakarta.validation.constraints.PositiveOrZero;
+
 `@Schema`(description = "파일 크기 (bytes)", example = "204800", nullable = true)
+@PositiveOrZero
 private Long sizeBytes;
🤖 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 `@capstone-api/src/main/java/backend/spot/dto/UploadFileRequest.java` around
lines 21 - 22, The sizeBytes field in UploadFileRequest currently accepts
negative values; add a non-negative validation (e.g., annotate the sizeBytes
property in class UploadFileRequest with a bean validation constraint such as
`@PositiveOrZero` or `@Min`(0)) and import the corresponding javax.validation or
jakarta.validation annotation so requests with negative file sizes are rejected
by validation.
capstone-api/src/main/java/backend/spot/dto/SpotFileResponse.java (1)

41-63: 💤 Low value

Remove the unused from() factory method or deprecate it with clear JavaDoc guidance.

The from() method is not used anywhere in the codebase—all call sites use of() and provide a proper uploaderNickname. The from() method returning null for uploaderNickname is dead code that could mislead future developers into using it and creating incomplete responses.

Either remove from() entirely or mark it @Deprecated with JavaDoc explaining when (if ever) it should be used.

🤖 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 `@capstone-api/src/main/java/backend/spot/dto/SpotFileResponse.java` around
lines 41 - 63, Remove the unused factory method SpotFileResponse.from(SpotFile)
or mark it deprecated: locate the SpotFileResponse.from(SpotFile) method and
either delete it entirely (since all call sites use of(SpotFile,String)) or
annotate it with `@Deprecated` and add JavaDoc explaining it returns a response
with a null uploaderNickname and should not be used; ensure
SpotFileResponse.of(SpotFile,String) remains the canonical factory and update
JavaDoc/comments to point callers to of(...) if you keep a deprecated shim.
🤖 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.

Nitpick comments:
In `@capstone-api/src/main/java/backend/spot/dto/SpotFileResponse.java`:
- Around line 41-63: Remove the unused factory method
SpotFileResponse.from(SpotFile) or mark it deprecated: locate the
SpotFileResponse.from(SpotFile) method and either delete it entirely (since all
call sites use of(SpotFile,String)) or annotate it with `@Deprecated` and add
JavaDoc explaining it returns a response with a null uploaderNickname and should
not be used; ensure SpotFileResponse.of(SpotFile,String) remains the canonical
factory and update JavaDoc/comments to point callers to of(...) if you keep a
deprecated shim.

In `@capstone-api/src/main/java/backend/spot/dto/UploadFileRequest.java`:
- Around line 21-22: The sizeBytes field in UploadFileRequest currently accepts
negative values; add a non-negative validation (e.g., annotate the sizeBytes
property in class UploadFileRequest with a bean validation constraint such as
`@PositiveOrZero` or `@Min`(0)) and import the corresponding javax.validation or
jakarta.validation annotation so requests with negative file sizes are rejected
by validation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd2db293-0ef1-406e-9d43-13a0bb1c4d02

📥 Commits

Reviewing files that changed from the base of the PR and between d8e5a94 and ff17bfe.

📒 Files selected for processing (10)
  • capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java
  • capstone-api/src/main/java/backend/simulation/controller/SimulationController.java
  • capstone-api/src/main/java/backend/spot/dto/SpotChecklistResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotFileResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotVoteOptionResponse.java
  • capstone-api/src/main/java/backend/spot/dto/UploadFileRequest.java
  • capstone-api/src/main/java/backend/spot/service/SpotService.java
  • capstone-domain/src/main/java/backend/spot/entity/SpotFile.java
  • docs/migrations/2026-05-19_feed_application_role_deposit.sql
  • docs/migrations/2026-05-19_spot_file_size_bytes.sql
✅ Files skipped from review due to trivial changes (2)
  • docs/migrations/2026-05-19_spot_file_size_bytes.sql
  • docs/migrations/2026-05-19_feed_application_role_deposit.sql
🚧 Files skipped from review as they are similar to previous changes (3)
  • capstone-api/src/main/java/backend/spot/dto/SpotChecklistResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotVoteOptionResponse.java
  • capstone-api/src/main/java/backend/spot/service/SpotService.java

…tiveOrZero 추가

- SpotFileResponse.from(SpotFile) dead code 제거 (호출처 없음, of() 단일화)
- UploadFileRequest.sizeBytes @PositiveOrZero 검증 추가 (음수 거부)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@ca5tlechan ca5tlechan left a comment

Choose a reason for hiding this comment

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

최종 리뷰 — Approve

3차 커밋(3ce48c4·ff17bfe·8f4b3a17)까지 확인했습니다.

반영 확인

항목 커밋
SecurityConfig / LoginFilter / logoutUrl 경로 통일 19be0ef
SimulationController 정렬 3ce48c4
FeedItemService appliedRole·deposit 빌더 추가 d8e5a94
FeedApplication 엔티티 + 마이그레이션 SQL ff17bfe
SpotFile.sizeBytes 엔티티·서비스·마이그레이션 ff17bfe
SpotFileResponse.from() dead code 제거 8f4b3a1
FeedItemResponse / SpotChecklistResponse 미구현 필드 명시 null + TODO 주석 3ce48c4
FeedAuthorProfile avatarUrl camelCase — FRONTEND.md 명세 기준 의도적 변경 확인

잔여 dummy-user-id placeholder는 인증 도입 전 임시값으로 이번 PR 스코프 외임을 확인했습니다.

머지해도 됩니다.

Copy link
Copy Markdown
Collaborator

@ca5tlechan ca5tlechan left a comment

Choose a reason for hiding this comment

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

최종 리뷰 — Approve

3차 커밋(3ce48c4·ff17bfe·8f4b3a17)까지 확인했습니다.

반영 확인

항목 커밋
SecurityConfig / LoginFilter / logoutUrl 경로 통일 19be0ef
SimulationController /api/v1/sim/runs 정렬 3ce48c4
FeedItemService appliedRole·deposit 빌더 추가 d8e5a94
FeedApplication 엔티티 + 마이그레이션 SQL ff17bfe
SpotFile.sizeBytes 엔티티·서비스·마이그레이션 ff17bfe
SpotFileResponse.from() dead code 제거 8f4b3a1
FeedItemResponse / SpotChecklistResponse 미구현 필드 명시 null + TODO 주석 3ce48c4
FeedAuthorProfile avatarUrl camelCase — FRONTEND.md 명세 기준 의도적 변경 확인

잔여 dummy-user-id placeholder는 인증 도입 전 임시값으로 이번 PR 스코프 외임을 확인했습니다.

머지해도 됩니다.

@hoTan35
Copy link
Copy Markdown
Collaborator

hoTan35 commented May 19, 2026

@ThonApple 최종 확인 결과 import 순서 위반이 4개 파일에 남아 있습니다. 수정 부탁드립니다.

규칙 (CAPSTONE.md): static → java → javax → org → com → backend
io.swagger, jakarta, lombok 은 third-party이므로 backend.* 보다 앞에 와야 합니다.

파일 현재 위반
FeedController.java:17-23 backend.*io.swagger.*(24), lombok.*(26) 앞에 위치
FeedApplicationResponse.java:5-7 backend.*io.swagger.*(8), lombok.*(9-13) 앞에 위치
SpotVoteResponse.java:6-7 backend.*io.swagger.*(8), lombok.*(9-13) 앞에 위치
SpotController.java:20-38 backend.*io.swagger.*(39), jakarta.*(41), lombok.*(42) 앞에 위치

올바른 순서 예시:

import java.*
import org.springframework.*
import io.swagger.*
import jakarta.*
import lombok.*
import backend.*

이 항목만 수정되면 나머지는 모두 확인됐습니다. 반영 후 머지해도 됩니다 ✅

FeedController, FeedApplicationResponse, SpotVoteResponse, SpotController
4개 파일에서 backend.* 그룹을 io.swagger / jakarta / lombok 뒤로 이동.

⚠️ 알려진 트레이드오프:
config/checkstyle/checkstyle.xml의 ImportOrder는 Naver Hackday 표준이라
backend.*, io.swagger.*, jakarta.*, lombok.* 모두 catch-all 그룹 6에 매칭되며
ordered=true로 알파벳 정렬을 강제한다 (b < i < j < l → backend 최상단).

이번 커밋은 CAPSTONE.md 텍스트("static→java→javax→org→com→backend")를
우선해 reorder 했으나 그 결과 checkstyle warning 8건이 새로 발생한다
(BUILD는 통과, ERROR 아닌 WARN). 향후 CAPSTONE.md와 checkstyle.xml 중
어느 쪽을 정답으로 통일할지 팀 논의 필요.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ThonApple
Copy link
Copy Markdown
Collaborator Author

@hoTan35 4개 파일 import 순서 수정해서 ef93dba 푸시했습니다.

java.* → org.* → io.swagger.* → jakarta.* → lombok.* → backend.*

⚠️ 트레이드오프 보고 (팀 논의 필요)

이번 reorder가 CAPSTONE.md 텍스트("static→java→javax→org→com→backend")는 만족하지만, config/checkstyle/checkstyle.xml (Naver Hackday 표준)의 ImportOrder 룰과는 충돌합니다.

checkstyle.xml ImportOrder 정의:

<property name="groups" value="java., javax., org., net., /com\\.(?!nhncorp|navercorp|naver)/, /(?!java\\.|javax\\.|com\\.|org\\.|net\\.)/, com.nhncorp., com.navercorp., com.naver."/>
<property name="ordered" value="true"/>
  • group 6 catch-all 정규식이 backend.*, io.swagger.*, jakarta.*, lombok.* 모두 매칭
  • ordered=true로 그룹 내 알파벳 정렬 강제 → b < i < j < l → checkstyle은 backend가 최상단에 와야 한다고 판단

이번 커밋으로 checkstyle warning 8건 신규 발생 (BUILD는 PASS, WARN 등급):

WARN [import-grouping] Wrong order for 'backend.feed.entity.FeedApplication' import
WARN [import-grouping] Extra separation in import group before 'backend.feed.entity.FeedApplication'
... (4파일 × 2건)

후속 결정 필요

  • 옵션 A: config/checkstyle/checkstyle.xml의 groups 정규식 수정 — backend.*를 별도 마지막 그룹으로 분리
  • 옵션 B: CAPSTONE.md 문구 수정 — 현 checkstyle 동작에 맞게 "공통 라이브러리/외부 → 프로젝트(backend) 알파벳 정렬"로 명문화

이번 PR은 우선 머지하고 후속 이슈로 정리하시죠. 추가 수정 사항 없으면 머지 진행하겠습니다.

ThonApple and others added 2 commits May 19, 2026 16:48
이전 문구("static→java→javax→org→com→backend")는 단순화된 표현이라
config/checkstyle/checkstyle.xml 의 ImportOrder 룰(Naver Hackday 표준,
ordered=true + catch-all regex)과 충돌했음.

실제 enforcement 기준:
- backend.*, io.*, jakarta.*, lombok.* 모두 catch-all 그룹(group 6)에 속해
  알파벳 순으로 정렬됨 (b < i < j < l → backend 최상단)

기존 코드베이스(ChatService, OAuth2UserServiceImpl 등)는 이미 이 규칙을
따르고 있으며, 직전 ef93dba 의 reorder 는 checkstyle warning 8건을 신규
유발했기에 함께 revert 됨 (b1fd2a2).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ThonApple
Copy link
Copy Markdown
Collaborator Author

@hoTan35 import 순서 항목에 대한 추가 조사 결과 보고드립니다.

결론: CAPSTONE.md 문구를 정정하는 방향으로 처리 (옵션 B)

직전 커밋(ef93dba) 으로 backend.*io.swagger.* / jakarta.* / lombok.* 뒤로 옮긴 결과, checkstyle warning 8건이 신규 발생 했습니다. 원인을 추적한 결과 config/checkstyle/checkstyle.xml (Naver Hackday 표준)의 ImportOrder 룰이 다음과 같이 정의되어 있습니다:

<property name="groups" value="java., javax., org., net., /com\\.(?!nhncorp|navercorp|naver)/, /(?!java\\.|javax\\.|com\\.|org\\.|net\\.)/, com.nhncorp., com.navercorp., com.naver."/>
<property name="ordered" value="true"/>

여기서 group 6 catch-all 정규식이 backend.*, io.swagger.*, jakarta.*, lombok.*모두 동일 그룹으로 매칭하고, ordered=true 로 알파벳 순(b < i < j < l)을 강제합니다.

즉 checkstyle 관점에서는 backend.* 가 catch-all 그룹의 알파벳순 최상단 이 정답이며, 기존 코드베이스(ChatService, OAuth2UserServiceImpl 등)도 모두 이 순서를 따르고 있습니다. CAPSTONE.md 문구("static→java→javax→org→com→backend")는 단순화된 표현이라 실제 enforcement와 충돌했습니다.

처리

  1. b1fd2a2 — 직전 reorder 커밋(ef93dba) revert (warning 8건 제거, 기존 코드베이스 일관성 복구)
  2. eedfb99docs/CAPSTONE.md 문구를 실제 checkstyle 동작 기준으로 정정
    • backend.*io.* / lombok.* 와 동일 catch-all 그룹, 알파벳 순 정렬임을 명시

이로써 PR #32 가 도입한 import warning 은 0건이며, checkstyle/PR 본문/문서가 모두 일관됩니다. 추가 확인 부탁드립니다.

@ThonApple ThonApple merged commit abac953 into develop May 19, 2026
1 check passed
@ThonApple ThonApple deleted the refactor/frontend-md-compliance branch May 19, 2026 09:29
ThonApple added a commit that referenced this pull request May 20, 2026
도메인 오너 기준 12개 follow-up 이슈 분담 및 우선순위·의존성 명시.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ThonApple added a commit that referenced this pull request May 20, 2026
도메인 오너 기준 12개 follow-up 이슈 분담 및 우선순위·의존성 명시.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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.

3 participants