✨ Feat: 1:1 쪽지 채팅 기능 구현 + WebSocketStompBroker 설정 리펙토링 + 주석 추가#20
✨ Feat: 1:1 쪽지 채팅 기능 구현 + WebSocketStompBroker 설정 리펙토링 + 주석 추가#20
Conversation
- /topic → /sub, /app → /pub prefix 통일 - /ws → /ws-stomp 엔드포인트 변경 - WebSocketConfig → WebSocketStompBrokerConfig 이름 변경 및 chat 도메인으로 이동 - ChatStompController 메시지 발송 경로 수정 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- NoteController 추가: 채팅방 생성/목록 조회 API - ChatRoom 엔티티에 1대1 채팅용 필드 추가 (post, hostUser, guestUser) - ChatRoomRepository에 중복 채팅방 체크 쿼리 추가 - ChatRoomService에 createNote, findMyNoteRooms 메서드 추가 - ChatRoomResponse에 1대1 채팅방 정보 필드 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WalkthroughNote(1:1) 채팅 기능과 관련 엔드포인트 및 도메인 모델이 추가되었고, WebSocket STOMP 설정이 기존 클래스에서 새로운 설정 클래스로 대체되며 STOMP 목적지 접두사가 변경되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant NoteController
participant UserRepository
participant PostRepository
participant ChatRoomService
participant ChatRoomRepository
participant Database
Client->>NoteController: POST /api/note/rooms/{postId}
NoteController->>UserRepository: 테스트 사용자 조회 (id=1)
UserRepository-->>NoteController: User 반환
NoteController->>PostRepository: post 조회 (postId)
PostRepository-->>NoteController: Post 반환
NoteController->>ChatRoomService: createNote(post, guestUser)
ChatRoomService->>ChatRoomRepository: findByPostAndHostUserAndGuestUser(post, host, guest)
ChatRoomRepository-->>ChatRoomService: Optional<ChatRoom>
alt 없음
ChatRoomService->>ChatRoomRepository: save(new NOTE ChatRoom)
ChatRoomRepository->>Database: 저장
Database-->>ChatRoomRepository: 저장 완료
end
ChatRoomRepository-->>ChatRoomService: ChatRoom 반환
ChatRoomService-->>NoteController: ChatRoom 반환
NoteController-->>Client: 201 Created + ChatRoomResponse
Client->>NoteController: GET /api/note/rooms
NoteController->>UserRepository: 테스트 사용자 조회 (id=1)
UserRepository-->>NoteController: User 반환
NoteController->>ChatRoomService: findMyNoteRooms(user)
ChatRoomService->>ChatRoomRepository: findByHostUserOrGuestUser(host, guest)
ChatRoomRepository->>Database: 쿼리
Database-->>ChatRoomRepository: List<ChatRoom>
ChatRoomRepository-->>ChatRoomService: List<ChatRoom>
ChatRoomService-->>NoteController: List<ChatRoom>
NoteController-->>Client: 200 OK + List<ChatRoomResponse>
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 분 Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/com/be/sportizebe/domain/chat/dto/request/ChatPresenceRequest.java (1)
9-11: JOIN/LEAVE 요청 필드명 불일치로 null 바인딩 발생
ChatPresenceRequest는userId/nickname을 정의하고 있으나, 테스트 클라이언트와ChatSendRequest는senderUserId/senderNickname을 사용 중입니다. 클라이언트가{senderUserId: 1, senderNickname: "hoon"}을 전송할 때 DTO의 해당 필드는 null로 바인딩되어,ChatStompController의join()및leave()메서드에서 null 값이 그대로 사용됩니다. 그 결과 "null 님이 입장했습니다" 같은 잘못된 메시지가 생성되고 세션 레지스트리에 null 값이 저장됩니다.🔧 제안 수정 (JsonAlias로 하위호환)
+import com.fasterxml.jackson.annotation.JsonAlias; import lombok.Getter; import lombok.NoArgsConstructor; `@Getter` `@NoArgsConstructor` public class ChatPresenceRequest { private Long roomId; + `@JsonAlias`("senderUserId") private Long userId; + `@JsonAlias`("senderNickname") private String nickname; }
🤖 Fix all issues with AI agents
In `@src/main/java/com/be/sportizebe/domain/chat/controller/NoteController.java`:
- Around line 36-38: Replace the hardcoded test user lookup in NoteController
with authentication-based user resolution: modify the controller methods (e.g.,
createChatRoom and any other methods referencing guestUser at the other
occurrence) to accept `@AuthenticationPrincipal` CustomUserDetails userDetails as
a parameter and obtain the User via userDetails.getUser() (or via
userRepository.findById(userDetails.getId()) if necessary), then remove the User
guestUser = userRepository.findById(1L)... hardcoded lines and use the
authenticated user instead.
In
`@src/main/java/com/be/sportizebe/domain/chat/dto/response/ChatRoomResponse.java`:
- Around line 32-42: ChatRoomResponse.from() accesses lazy associations (post,
hostUser, guestUser) outside a transactional context when called from
NoteController.createChatRoom() and NoteController.getMyChatRooms(), causing
LazyInitializationException; fix by ensuring these associations are initialized
before mapping: either add `@Transactional`(readOnly = true) to the controller
methods createChatRoom() and getMyChatRooms(), or modify the repository query
that loads ChatRoom (use JOIN FETCH or `@EntityGraph` on the repository method
used by the service) so post/hostUser/guestUser are eagerly fetched, or
initialize the lazy fields inside the service method (e.g., touch
getPost()/getHostUser()/getGuestUser() or use Hibernate.initialize) before
calling ChatRoomResponse.from().
In `@src/main/java/com/be/sportizebe/domain/chat/entity/ChatRoom.java`:
- Around line 31-33: The createGroup() flow currently saves a ChatRoom without
setting the non-nullable field maxMembers, causing runtime constraint
violations; update the createGroup() method to assign a sensible maxMembers
value (e.g., same logic used in createNote() or a defined default) before
calling save() and ensure every ChatRoom creation path sets maxMembers. Also
annotate the chatRoomType enum field in ChatRoom with
`@Enumerated`(EnumType.STRING) to persist enum names instead of ordinals to avoid
future data corruption when enums are reordered.
In `@src/main/java/com/be/sportizebe/domain/chat/service/ChatRoomService.java`:
- Around line 32-35: Replace the reference-equality check of Long IDs in
ChatRoomService (the hostUser.getId() == guestUser.getId() block) with a
value-equality check to avoid incorrect results and caching pitfalls; use a
null-safe comparison such as Objects.equals(hostUser.getId(), guestUser.getId())
(or hostUser.getId().equals(guestUser.getId()) after null checks) and keep the
existing IllegalArgumentException throw when they match.
In
`@src/main/java/com/be/sportizebe/domain/chat/websocket/config/WebSocketStompBrokerConfig.java`:
- Around line 17-40: The WebSocket config currently ignores the injected
allowedOrigins and hardcodes "*" in registerStompEndpoints; update the
StompEndpointRegistry registration in registerStompEndpoints (the
registry.addEndpoint("/ws-stomp") call) to use the allowedOrigins property
instead of the literal "*" by passing the parsed allowedOrigins values to
setAllowedOriginPatterns (e.g., split comma-separated allowedOrigins, trim
entries, and supply as varargs) so the endpoint honors the same CORS policy as
CorsConfig and avoid allowing a global wildcard.
🧹 Nitpick comments (3)
src/main/java/com/be/sportizebe/domain/chat/entity/ChatRoom.java (1)
34-35: Enum은 STRING 저장 권장 (ordinal 위험)기본값(ORDINAL) 저장은 enum 순서 변경 시 데이터가 깨질 수 있습니다. 안정성을 위해 STRING 저장을 권장합니다.
♻️ 제안 수정
+ `@Enumerated`(EnumType.STRING) private ChatRoomType chatRoomType; // 채팅방 타입 (단체, 1:1)src/main/java/com/be/sportizebe/domain/chat/service/ChatRoomService.java (1)
52-59:findAll()메서드의 용도 명확화 고려
findAll()이 NOTE와 GROUP 타입을 모두 반환합니다. 만약 그룹 채팅방만 조회하는 용도라면findAllGroupRooms()같은 명칭이나 타입 필터링을 추가하면 의도가 더 명확해집니다. 현재 설계가 의도적이라면 무시해도 됩니다.src/main/java/com/be/sportizebe/domain/chat/controller/NoteController.java (1)
40-41: 예외 처리 일관성 개선 권장
postRepository.findById에서IllegalArgumentException을 사용하고 있습니다. 프로젝트 전반에 커스텀 예외(예:PostNotFoundException)나 일관된 예외 처리 전략이 있다면 이에 맞춰 수정하는 것이 좋습니다. 현재 구현도 동작에는 문제가 없습니다.
| // TODO: 인증 로직 개발 후 @AuthenticationPrincipal User user로 변경 | ||
| User guestUser = userRepository.findById(1L) | ||
| .orElseThrow(() -> new RuntimeException("테스트 유저가 없습니다.")); |
There was a problem hiding this comment.
하드코딩된 테스트 유저 ID - 프로덕션 배포 전 반드시 제거 필요
TODO 주석으로 인지하고 계시지만, 하드코딩된 유저 ID(1L)는 보안상 심각한 문제가 될 수 있습니다. 프로덕션 배포 전에 반드시 @AuthenticationPrincipal을 사용한 인증 로직으로 교체해야 합니다.
제공된 코드 스니펫에서 CustomUserDetailService와 CustomUserDetails가 이미 구현되어 있으므로, 아래와 같이 적용 가능합니다:
🔒 인증 로직 적용 예시
`@PostMapping`("/rooms/{postId}")
public ResponseEntity<BaseResponse<ChatRoomResponse>> createChatRoom(
`@Parameter`(description = "게시글 ID") `@PathVariable` Long postId,
`@AuthenticationPrincipal` CustomUserDetails userDetails) {
User guestUser = userDetails.getUser(); // 또는 userRepository.findById(userDetails.getId())
// ...
}Also applies to: 53-55
🤖 Prompt for AI Agents
In `@src/main/java/com/be/sportizebe/domain/chat/controller/NoteController.java`
around lines 36 - 38, Replace the hardcoded test user lookup in NoteController
with authentication-based user resolution: modify the controller methods (e.g.,
createChatRoom and any other methods referencing guestUser at the other
occurrence) to accept `@AuthenticationPrincipal` CustomUserDetails userDetails as
a parameter and obtain the User via userDetails.getUser() (or via
userRepository.findById(userDetails.getId()) if necessary), then remove the User
guestUser = userRepository.findById(1L)... hardcoded lines and use the
authenticated user instead.
| if (r.getPost() != null) { | ||
| builder.postId(r.getPost().getId()); | ||
| } | ||
| if (r.getHostUser() != null) { | ||
| builder.hostUserId(r.getHostUser().getId()) | ||
| .hostUsername(r.getHostUser().getUsername()); | ||
| } | ||
| if (r.getGuestUser() != null) { | ||
| builder.guestUserId(r.getGuestUser().getId()) | ||
| .guestUsername(r.getGuestUser().getUsername()); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find ChatRoom entity file
find . -type f -name "ChatRoom.java" | grep -E "(domain|entity)"Repository: Sportize/be
Length of output: 121
🏁 Script executed:
# Search for ChatRoom entity definition to check fetch types
rg -n "@ManyToOne|@OneToOne|@OneToMany|FetchType" --type=java -A3 | grep -A10 "class ChatRoom"Repository: Sportize/be
Length of output: 37
🏁 Script executed:
# Look for the from() method in ChatRoomResponse
rg -n "from\(" --type=java -B2 -A5 src/main/java/com/be/sportizebe/domain/chat/dto/response/ChatRoomResponse.javaRepository: Sportize/be
Length of output: 439
🏁 Script executed:
# Search for service layer usage of ChatRoomResponse
rg -n "ChatRoomResponse\.from|toDTO|\.from\(" --type=java | grep -i serviceRepository: Sportize/be
Length of output: 555
🏁 Script executed:
# Check for `@Transactional` annotations in chat service
find . -type f -name "*ChatService*.java" -o -name "*ChatRoomService*.java" | head -3Repository: Sportize/be
Length of output: 129
🏁 Script executed:
# Read ChatRoom entity to check fetch types
cat src/main/java/com/be/sportizebe/domain/chat/entity/ChatRoom.javaRepository: Sportize/be
Length of output: 1552
🏁 Script executed:
# Read ChatRoomService to verify transaction handling
cat src/main/java/com/be/sportizebe/domain/chat/service/ChatRoomService.javaRepository: Sportize/be
Length of output: 2328
🏁 Script executed:
# Search for all usages of ChatRoomResponse.from() in the codebase
rg -n "ChatRoomResponse\.from|ChatRoomResponse\.builder" --type=java -B3 -A3Repository: Sportize/be
Length of output: 1690
🏁 Script executed:
# Find controller classes that might use ChatRoomResponse
find . -type f -name "*ChatController*.java" -o -name "*ChatRoomController*.java"Repository: Sportize/be
Length of output: 37
🏁 Script executed:
# Check if there are any tests or other files that use ChatRoomResponse
rg -n "ChatRoomResponse" --type=java | head -20Repository: Sportize/be
Length of output: 1364
🏁 Script executed:
# Read NoteController to check for `@Transactional` annotations
cat src/main/java/com/be/sportizebe/domain/chat/controller/NoteController.javaRepository: Sportize/be
Length of output: 2767
트랜잭션 컨텍스트 외부에서 호출되는 Lazy Loading 엔티티 접근 - 중대한 문제
ChatRoomResponse.from() 메서드가 NoteController의 createChatRoom() (라인 46)과 getMyChatRooms() (라인 57-58)에서 호출되는데, 컨트롤러에 @Transactional 어노테이션이 없어서 서비스 메서드의 트랜잭션이 종료된 후에 실행됩니다.
ChatRoom 엔티티의 post, hostUser, guestUser 모두 FetchType.LAZY로 설정되어 있으므로, from() 메서드의 라인 33-41에서 이들 엔티티에 접근할 때 LazyInitializationException이 발생합니다.
해결 방안:
- 컨트롤러 메서드에
@Transactional(readOnly = true)추가 - 저장소 쿼리에
JOIN FETCH또는@EntityGraph적용으로 eager loading 사용 - 서비스 레이어 내에서 lazy-loaded 엔티티를 먼저 초기화한 후 반환
🤖 Prompt for AI Agents
In
`@src/main/java/com/be/sportizebe/domain/chat/dto/response/ChatRoomResponse.java`
around lines 32 - 42, ChatRoomResponse.from() accesses lazy associations (post,
hostUser, guestUser) outside a transactional context when called from
NoteController.createChatRoom() and NoteController.getMyChatRooms(), causing
LazyInitializationException; fix by ensuring these associations are initialized
before mapping: either add `@Transactional`(readOnly = true) to the controller
methods createChatRoom() and getMyChatRooms(), or modify the repository query
that loads ChatRoom (use JOIN FETCH or `@EntityGraph` on the repository method
used by the service) so post/hostUser/guestUser are eagerly fetched, or
initialize the lazy fields inside the service method (e.g., touch
getPost()/getHostUser()/getGuestUser() or use Hibernate.initialize) before
calling ChatRoomResponse.from().
| @Column(nullable = false) | ||
| private Integer maxMembers; // 채팅방 최대 정원 | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/main/java/com/be/sportizebe/domain/chat/entity/ChatRoom.javaRepository: Sportize/be
Length of output: 2007
🏁 Script executed:
find . -type f -name "*.sql" -o -name "*Migration*" | grep -i chat | head -20Repository: Sportize/be
Length of output: 37
🏁 Script executed:
rg "ChatRoom" -t java --max-count=50 | grep -E "(new ChatRoom|ChatRoom\.)" | head -30Repository: Sportize/be
Length of output: 882
🏁 Script executed:
rg "maxMembers" -t java -A 3 -B 3Repository: Sportize/be
Length of output: 3105
🏁 Script executed:
rg "chatRoomType" -t java -A 2 -B 2Repository: Sportize/be
Length of output: 2862
🏁 Script executed:
find . -type f \( -name "*.sql" -o -name "*.xml" \) -path "*/migration*" -o -path "*/db/*" 2>/dev/null | head -20Repository: Sportize/be
Length of output: 37
🏁 Script executed:
find . -type d -name "db" -o -name "migration" -o -name "flyway" -o -name "liquibase" 2>/dev/nullRepository: Sportize/be
Length of output: 37
🏁 Script executed:
cat -n src/main/java/com/be/sportizebe/domain/chat/service/ChatRoomService.java | head -80Repository: Sportize/be
Length of output: 2783
createGroup() 메서드에서 maxMembers를 설정해야 합니다.
createGroup() 메서드 (line 20-26)에서 maxMembers를 설정하지 않은 채로 save()를 호출하면, NOT NULL 제약으로 인해 런타임 예외가 발생합니다. createNote()처럼 maxMembers에 적절한 값을 설정하거나, 모든 생성 경로에서 반드시 설정되도록 보장해 주세요.
추가로, chatRoomType enum에 @Enumerated(EnumType.STRING) 어노테이션을 추가하여 ordinal 저장 방식을 문자열 저장으로 변경하는 것이 좋습니다. 현재 방식은 향후 enum 순서를 변경할 때 기존 데이터가 손상될 위험이 있습니다.
🤖 Prompt for AI Agents
In `@src/main/java/com/be/sportizebe/domain/chat/entity/ChatRoom.java` around
lines 31 - 33, The createGroup() flow currently saves a ChatRoom without setting
the non-nullable field maxMembers, causing runtime constraint violations; update
the createGroup() method to assign a sensible maxMembers value (e.g., same logic
used in createNote() or a defined default) before calling save() and ensure
every ChatRoom creation path sets maxMembers. Also annotate the chatRoomType
enum field in ChatRoom with `@Enumerated`(EnumType.STRING) to persist enum names
instead of ordinals to avoid future data corruption when enums are reordered.
| // 자기 자신에게 채팅 불가 | ||
| if (hostUser.getId() == guestUser.getId()) { | ||
| throw new IllegalArgumentException("자신의 게시글에는 채팅을 시작할 수 없습니다."); | ||
| } |
There was a problem hiding this comment.
Long 객체 비교 시 == 대신 equals() 사용 필요
Long 타입에 == 연산자를 사용하면 객체 참조를 비교합니다. Java의 Long 캐싱은 -128 ~ 127 범위만 지원하므로, ID가 이 범위를 벗어나면 동일한 값이어도 false를 반환할 수 있습니다.
🐛 수정 제안
// 자기 자신에게 채팅 불가
- if (hostUser.getId() == guestUser.getId()) {
+ if (hostUser.getId().equals(guestUser.getId())) {
throw new IllegalArgumentException("자신의 게시글에는 채팅을 시작할 수 없습니다.");
}🤖 Prompt for AI Agents
In `@src/main/java/com/be/sportizebe/domain/chat/service/ChatRoomService.java`
around lines 32 - 35, Replace the reference-equality check of Long IDs in
ChatRoomService (the hostUser.getId() == guestUser.getId() block) with a
value-equality check to avoid incorrect results and caching pitfalls; use a
null-safe comparison such as Objects.equals(hostUser.getId(), guestUser.getId())
(or hostUser.getId().equals(guestUser.getId()) after null checks) and keep the
existing IllegalArgumentException throw when they match.
| @Value("${cors.allowed-origins}") | ||
| private String allowedOrigins; | ||
|
|
||
| @Override | ||
| public void configureMessageBroker(MessageBrokerRegistry registry) { | ||
| // 구독(sub) : 접두사로 시작하는 메시지를 브로커가 처리하도록 설정합니다. 클라이언트는 이 접두사로 시작하는 주제를 구독하여 메시지를 받을 수 있습니다. | ||
| // 예를 들어, 소켓 통신에서 사용자가 특정 메시지를 받기위해 "/sub"이라는 prefix 기반 메시지 수신을 위해 Subscribe합니다. | ||
| // 구독(브로드캐스트) prefix | ||
| registry.enableSimpleBroker("/sub"); // /sub/** 패턴의 모든 구독을 SimpleBroker가 자동 처리 | ||
|
|
||
| // 발행(pub) : 접두사로 시작하는 메시지는 @MessageMapping이 달린 메서드로 라우팅됩니다. 클라이언트가 서버로 메시지를 보낼 때 이 접두사를 사용합니다. | ||
| // 예를 들어, 소켓 통신에서 사용자가 특정 메시지를 전송하기 위해 "/pub"라는 prefix 기반 메시지 전송을 위해 Publish 합니다. | ||
| // 클라이언트가 서버로 메시지 보낼 떄 prefix | ||
| registry.setApplicationDestinationPrefixes("/pub"); | ||
| } | ||
|
|
||
| // 각각 특정 URL에 매핑되는 STOMP 엔드포인트를 등록하고, 선택적으로 SockJS 폴백 옵션을 활성화하고 구성합니다. | ||
| @Override | ||
| public void registerStompEndpoints(StompEndpointRegistry registry) { | ||
| // ws endpoint | ||
| // < 일단 이건 테스트/호환성 때문에 주석처리 해놓음 > 순수 WebSocket임 | ||
| registry.addEndpoint("/ws-stomp") | ||
| .setAllowedOriginPatterns("*") | ||
| .withSockJS(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "WebSocketStompBrokerConfig.java" -type fRepository: Sportize/be
Length of output: 149
🏁 Script executed:
cat -n "src/main/java/com/be/sportizebe/domain/chat/websocket/config/WebSocketStompBrokerConfig.java"Repository: Sportize/be
Length of output: 2201
🏁 Script executed:
rg "allowedOrigins" --type java -B 2 -A 2Repository: Sportize/be
Length of output: 1477
프로퍼티를 주입했으나 하드코딩된 와일드카드로 보안 정책 우회
allowedOrigins 프로퍼티를 주입해 놓고 registerStompEndpoints() 메서드에서 실제로는 "*"를 허용하고 있습니다. 같은 프로퍼티를 사용하는 CorsConfig에서는 올바르게 처리하고 있으므로, WebSocket 설정도 일관되게 수정해야 합니다.
💡 제안 수정
registry.addEndpoint("/ws-stomp")
- .setAllowedOriginPatterns("*")
+ .setAllowedOriginPatterns(allowedOrigins.split("\\s*,\\s*"))
.withSockJS();🤖 Prompt for AI Agents
In
`@src/main/java/com/be/sportizebe/domain/chat/websocket/config/WebSocketStompBrokerConfig.java`
around lines 17 - 40, The WebSocket config currently ignores the injected
allowedOrigins and hardcodes "*" in registerStompEndpoints; update the
StompEndpointRegistry registration in registerStompEndpoints (the
registry.addEndpoint("/ws-stomp") call) to use the allowedOrigins property
instead of the literal "*" by passing the parsed allowedOrigins values to
setAllowedOriginPatterns (e.g., split comma-separated allowedOrigins, trim
entries, and supply as varargs) so the endpoint honors the same CORS policy as
CorsConfig and avoid allowing a global wildcard.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/main/java/com/be/sportizebe/domain/chat/controller/NoteController.java`:
- Around line 40-41: The current orElseThrow in NoteController uses
IllegalArgumentException which leads to a 500 from the global handler; change
the exception to one that yields 404 by throwing
ResponseStatusException(HttpStatus.NOT_FOUND, "게시글을 찾을 수 없습니다: " + postId) or
your project’s CustomException (e.g., ResourceNotFoundException) instead of
IllegalArgumentException when resolving Post via postRepository.findById so the
controller returns HTTP 404 for missing posts.
♻️ Duplicate comments (1)
src/main/java/com/be/sportizebe/domain/chat/controller/NoteController.java (1)
36-38: 테스트 유저 ID 하드코딩은 프로덕션 보안 리스크이 부분은 이전 리뷰에서 이미 지적된 사항과 동일합니다. 인증 사용자 기반으로 교체가 필요합니다.
Also applies to: 53-55
| Post post = postRepository.findById(postId) | ||
| .orElseThrow(() -> new IllegalArgumentException("게시글을 찾을 수 없습니다: " + postId)); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Search for global exception handling (`@ControllerAdvice`, `@ExceptionHandler`, ResponseStatusException)
rg -n --type=java '@ControllerAdvice|@ExceptionHandler|ResponseStatusException' -A 3Repository: Sportize/be
Length of output: 1676
🏁 Script executed:
# View the NoteController.java file to understand context
fd -t f 'NoteController.java' --exec cat -n {} \;Repository: Sportize/be
Length of output: 3211
🏁 Script executed:
# Check if there's any specific mapping for IllegalArgumentException
rg -n --type=java 'IllegalArgumentException' -B 2 -A 2Repository: Sportize/be
Length of output: 3641
🏁 Script executed:
# Get the full GlobalExceptionHandler to see how Exception.class is handled
cat -n src/main/java/com/be/sportizebe/global/exception/GlobalExceptionHandler.javaRepository: Sportize/be
Length of output: 2250
🏁 Script executed:
# Check if there's a CustomException being used elsewhere and understand the error handling pattern
rg -n --type=java 'class CustomException|CustomException(' -A 2 | head -30Repository: Sportize/be
Length of output: 181
🏁 Script executed:
# Check what BaseResponse looks like and the error code pattern
fd -t f 'BaseErrorCode.java|CustomException.java' --exec cat -n {} \;Repository: Sportize/be
Length of output: 793
IllegalArgumentException은 500 응답을 반환합니다. 404로 명시적으로 처리하세요.
전역 예외 핸들러의 @ExceptionHandler(Exception.class)가 IllegalArgumentException을 가로채서 500 응답을 반환합니다. 프로젝트에서 이미 확립된 CustomException 패턴을 활용하거나 ResponseStatusException(HttpStatus.NOT_FOUND, ...)로 변경하여 정확한 404 상태 코드를 반환해야 합니다.
현재 코드
Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("게시글을 찾을 수 없습니다: " + postId));🤖 Prompt for AI Agents
In `@src/main/java/com/be/sportizebe/domain/chat/controller/NoteController.java`
around lines 40 - 41, The current orElseThrow in NoteController uses
IllegalArgumentException which leads to a 500 from the global handler; change
the exception to one that yields 404 by throwing
ResponseStatusException(HttpStatus.NOT_FOUND, "게시글을 찾을 수 없습니다: " + postId) or
your project’s CustomException (e.g., ResourceNotFoundException) instead of
IllegalArgumentException when resolving Post via postRepository.findById so the
controller returns HTTP 404 for missing posts.
#️⃣ Issue Number
📝 요약(Summary)
🛠️ PR 유형
어떤 변경 사항이 있나요?
📸스크린샷 (선택)
💬 공유사항 to 리뷰어
✅ PR Checklist
PR이 다음 요구 사항을 충족하는지 확인하세요.
Summary by CodeRabbit
릴리스 노트
새 기능
개선사항
기타
✏️ Tip: You can customize this high-level summary in your review settings.