Conversation
- ProfileCreateRequestDTO와 AttachmentCreateRequestDTO의 구조가 동일해 통합 - 유저 프로필, 메시지 첨부파일 생성 시 통합된 BinaryContentCreateRequestDTO를 적용
- 유저 생성/수정 시 실제 프로필 이미지 업로드 기능 구현 - 메시지 생성/수정 시 실제 첨부파일 업로드 기능 구현 - 관련 DTO 수정
- 문서화를 위한 dependency 추가 - UserController 제공된 API 스펙에 맞게 수정 - UserStatus lastAccessTime 필드명을 lastActiveAt로 변경 - 관련 DTO 수정
- 강사님 피드백 반영 - Service에서 IllegalStateException가 IllegalArgumentException로 발생하도록 수정 - GlobalExceptionHandler에서 IllegalArgumentException을 400으로 처리
- ChanelController 제공된 API 스펙에 맞게 수정 - 관련 DTO 수정 - PublicCreateRequestDTO를 PublicChannelCreateRequestDTO로 이름 변경 - PrivateCreateRequestDTO를 PrivateChannelCreateRequestDTO로 이름 변경
- 채널에 메시지가 없을 경우 404 에러 발생 로직 삭제 - 채널에 메시지가 없는 경우 lastMessageAt가 null을 갖도록 로직 변경
- MessageController 제공된 API 스펙에 맞게 수정 - 관련 DTO 수정 - 메시지 수정 시 첨부파일과 같이 수정할 수 있는 로직 삭제
- ReadStatusController 제공된 API 스펙에 맞게 수정 - 관련 DTO 수정 - ReadStatus lastReadTime 필드명을 lastReadAt로 변경
- AuthController 제공된 API 스펙에 맞게 수정 - 관련 DTO 수정
- BinaryContentController 제공된 API 스펙에 맞게 수정 - 관련 DTO 수정 - BinaryContent 엔티티 size 필드 추가 - BinaryContent 엔티티 content 필드명을 bytes로 변경
There was a problem hiding this comment.
전체 요약
Sprint4의 API를 RESTful 구조로 재설계하고, Swagger(springdoc-openapi)를 통한 API 문서 자동 생성, 프론트엔드 정적 리소스 서빙, 파일 업로드를 위한 Multipart 처리를 추가해주셨는데, 특히 아래와 같은 점이 좋았습니다.
좋은 점
@Controller+@ResponseBody→@RestController전환, URL 경로/api프리픽스 통일이 깔끔합니다.- 하이픈 기반 path variable → camelCase 전환, 응답 DTO에
createdAt/updatedAt추가 등 API 스펙 표준화가 잘 이루어졌습니다. BasicChannelService.toChannelDetailResponseDTO()에서 메시지가 없을 때 예외를 던지던 것을null로 처리한 수정이 좋습니다.- 주석 정리(주석 처리된 코드 제거)도 코드 가독성 향상에 도움이 됩니다.
| file.getContentType() | ||
| ); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException(e); |
There was a problem hiding this comment.
[P2] MultipartFile.getBytes()에서 IOException 발생 시 RuntimeException으로 감싸고 있습니다. 이렇게 하면 GlobalExceptionHandler의 handleException(Exception e)에서 잡혀 500 에러와 함께 스택 트레이스가 클라이언트에 노출될 수 있습니다.
UserController.toBinaryContentCreateRequestDTO()에도 동일한 패턴이 있습니다.
파일 처리 실패에 맞는 명확한 예외로 변환하는 것을 권장합니다.
| throw new RuntimeException(e); | |
| throw new IllegalArgumentException("파일 처리 중 오류가 발생했습니다: " + e.getMessage()); |
| } | ||
|
|
||
| // 유저 생성, 수정 시 입력 받은 프로필 이미지를 Service에 전달하기 전 Optional<BinaryContentCreateRequestDTO>로 변환하는 private 메서드 | ||
| private Optional<BinaryContentCreateRequestDTO> toBinaryContentCreateRequestDTO(MultipartFile file) { |
There was a problem hiding this comment.
[P3] UserController와 MessageController에 MultipartFile → BinaryContentCreateRequestDTO 변환 로직이 거의 동일하게 중복되어 있습니다. 유틸리티 클래스나 별도의 컨버터 클래스로 추출하면 유지보수성이 좋아집니다.
예시:
public class MultipartFileConverter {
public static BinaryContentCreateRequestDTO toDTO(MultipartFile file) { ... }
public static List<BinaryContentCreateRequestDTO> toDTOList(List<MultipartFile> files) { ... }
}| UserSummaryResponseDTO create(UserCreateRequestDTO userCreateRequestDTO); | ||
| // User find(UUID userId); | ||
| UserSummaryResponseDTO create(UserCreateRequestDTO userCreateRequestDTO, | ||
| Optional<BinaryContentCreateRequestDTO> binaryContentCreateRequestDTO); |
There was a problem hiding this comment.
[P3] Optional을 메서드 파라미터로 사용하는 것은 Java에서 일반적으로 안티패턴으로 간주됩니다 (SonarSource 규칙 참고). MessageService.create()에도 동일한 패턴이 있습니다.
@Nullable을 사용하거나, 메서드 오버로딩으로 프로필 이미지가 있는 경우/없는 경우를 분리하는 것이 더 관용적입니다.
// 방법 1: @Nullable 사용
UserSummaryResponseDTO create(UserCreateRequestDTO dto, @Nullable BinaryContentCreateRequestDTO profile);
// 방법 2: 메서드 오버로딩
UserSummaryResponseDTO create(UserCreateRequestDTO dto);
UserSummaryResponseDTO create(UserCreateRequestDTO dto, BinaryContentCreateRequestDTO profile);| String fileName, | ||
| long size, | ||
| String contentType, | ||
| byte[] bytes |
There was a problem hiding this comment.
[P3] 응답 DTO에 byte[] bytes를 포함하면 JSON 직렬화 시 Base64 인코딩되어 응답 크기가 약 33% 증가합니다. 큰 파일의 경우 메모리 문제를 야기할 수 있습니다.
바이너리 데이터는 별도의 다운로드 엔드포인트(예: GET /api/binaryContents/{id}/download)로 제공하고, 메타데이터 조회 API에서는 bytes를 제외하는 것이 일반적인 패턴입니다.
|
|
||
| @ExceptionHandler(MethodArgumentNotValidException.class) | ||
| @ResponseBody | ||
| public ResponseEntity<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { |
There was a problem hiding this comment.
[P4] 두 가지 개선 포인트가 있습니다:
@RestControllerAdvice를 사용하고 있으므로@ResponseBody는 불필요합니다 (다른 핸들러 메서드도 동일).e.getMessage()는 Spring 내부의 매우 긴 메시지를 그대로 반환합니다. 필드별 에러 메시지만 추출하면 클라이언트에 더 유용한 응답을 줄 수 있습니다.
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
String errorMessage = e.getBindingResult().getFieldErrors().stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.joining(", "));
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}
기본 요구사항
심화 요구사항
다음의 정적 리소스를 서빙하여 프론트엔드와 통합해보세요. API 스펙을 준수했다면 잘 동작할거예요.
fe_1.0.0.zip
화면 가이드
Railway.app을 활용하여 애플리케이션을 배포해보세요.
테스트
Railway 도메인 주소
lucky-dedication-production-0587.up.railway.app
Postman API 테스트 결과
Sprint5.postman_collection.json
Swagger API 문서
my-api-docs.json
테스트에 사용한 프로필 이미지 및 메시지 첨부 파일
images.zip