Skip to content

[장동규] Sprint5#171

Open
dongg1122 wants to merge 15 commits intocodeit-bootcamp-spring:장동규from
dongg1122:sprint5
Open

[장동규] Sprint5#171
dongg1122 wants to merge 15 commits intocodeit-bootcamp-spring:장동규from
dongg1122:sprint5

Conversation

@dongg1122
Copy link
Copy Markdown
Collaborator

@dongg1122 dongg1122 commented Feb 24, 2026

기본 요구사항

  • 스프린트 미션#4에서 구현한 API를 RESTful API로 다시 설계해보세요.
    • API 스펙을 확인하고 본인이 설계한 API와 비교해보세요.
    • oasdiff를 활용하면 좀 더 수월하게 비교할 수 있어요.
    • API 설계에 정답은 없지만, 이어지는 요구사항과 미션을 원활히 수행하기 위해 제공된 API 스펙에 맞추어 구현해주세요.
    • 특히, 심화 요구사항에서 제공되는 프론트엔드 코드는 제공된 API 스펙을 준수해야 연동할 수 있습니다.
  • Postman을 활용해 컨트롤러를 테스트 하세요.
    • Postman API 테스트 결과를 export하여 PR에 첨부해주세요.
  • springdoc-openapi를 활용하여 Swagger 기반의 API 문서를 생성하세요.
  • Swagger-UI를 활용해 API를 테스트해보세요.

심화 요구사항

  • 다음의 정적 리소스를 서빙하여 프론트엔드와 통합해보세요. API 스펙을 준수했다면 잘 동작할거예요.

  • fe_1.0.0.zip

  • 화면 가이드

  • Railway.app을 활용하여 애플리케이션을 배포해보세요.

    • Railway.app은 애플리케이션을 쉽게 배포할 수 있도록 도와주는 PaaS입니다.
    • Railway.app에 가입하고, 배포할 GitHub 레포지토리를 연결하세요.
    • Settings > Network 섹션에서 Generate Domain 버튼을 통해 도메인을 생성하세요.
    • 생성된 도메인에 접속해 배포된 애플리케이션을 테스트해보세요.

테스트

test

Railway 도메인 주소
lucky-dedication-production-0587.up.railway.app

Postman API 테스트 결과
Sprint5.postman_collection.json

Swagger API 문서
my-api-docs.json

테스트에 사용한 프로필 이미지 및 메시지 첨부 파일
images.zip

- 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로 변경
Copy link
Copy Markdown
Collaborator

@joonfluence joonfluence left a comment

Choose a reason for hiding this comment

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

전체 요약

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);
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.

[P2] MultipartFile.getBytes()에서 IOException 발생 시 RuntimeException으로 감싸고 있습니다. 이렇게 하면 GlobalExceptionHandlerhandleException(Exception e)에서 잡혀 500 에러와 함께 스택 트레이스가 클라이언트에 노출될 수 있습니다.

UserController.toBinaryContentCreateRequestDTO()에도 동일한 패턴이 있습니다.

파일 처리 실패에 맞는 명확한 예외로 변환하는 것을 권장합니다.

Suggested change
throw new RuntimeException(e);
throw new IllegalArgumentException("파일 처리 중 오류가 발생했습니다: " + e.getMessage());

}

// 유저 생성, 수정 시 입력 받은 프로필 이미지를 Service에 전달하기 전 Optional<BinaryContentCreateRequestDTO>로 변환하는 private 메서드
private Optional<BinaryContentCreateRequestDTO> toBinaryContentCreateRequestDTO(MultipartFile file) {
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.

[P3] UserControllerMessageControllerMultipartFileBinaryContentCreateRequestDTO 변환 로직이 거의 동일하게 중복되어 있습니다. 유틸리티 클래스나 별도의 컨버터 클래스로 추출하면 유지보수성이 좋아집니다.

예시:

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);
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.

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

[P3] 응답 DTO에 byte[] bytes를 포함하면 JSON 직렬화 시 Base64 인코딩되어 응답 크기가 약 33% 증가합니다. 큰 파일의 경우 메모리 문제를 야기할 수 있습니다.

바이너리 데이터는 별도의 다운로드 엔드포인트(예: GET /api/binaryContents/{id}/download)로 제공하고, 메타데이터 조회 API에서는 bytes를 제외하는 것이 일반적인 패턴입니다.


@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ResponseEntity<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
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.

[P4] 두 가지 개선 포인트가 있습니다:

  1. @RestControllerAdvice를 사용하고 있으므로 @ResponseBody는 불필요합니다 (다른 핸들러 메서드도 동일).
  2. 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);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants