Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DDING-000] 파일 폼지 응답 presignedUrl 발급 API 구현 #270

Merged
merged 1 commit into from
Feb 16, 2025

Conversation

5uhwann
Copy link
Collaborator

@5uhwann 5uhwann commented Feb 16, 2025

🚀 작업 내용

  • 파일 타입 폼지 응답을 위한 presignedUrl 발급 API 구현

🤔 고민했던 내용

  • s3 path를 위한 userId가 포함되지 않기 때문에 우선 임시로 9999를 사용했습니다.

💬 리뷰 중점사항

Summary by CodeRabbit

  • 새로운 기능
    • 파일 업로드를 위한 프리사인 URL 생성에 폼 애플리케이션 전용 옵션이 추가되었습니다. 새로운 API 엔드포인트를 통해 별도의 요청으로 프리사인 URL을 제공하며, 기존 방식은 그대로 유지되면서 내부 파라미터 포맷이 개선되었습니다.

@5uhwann 5uhwann added 🎯리팩토링 리팩토링 및 고도화 이슈 D-0 labels Feb 16, 2025
@5uhwann 5uhwann self-assigned this Feb 16, 2025
Copy link

coderabbitai bot commented Feb 16, 2025

Walkthrough

이 PR은 S3FileAPI 인터페이스와 S3FileController 클래스에 새로운 메서드를 추가하는 작업입니다. 새롭게 도입된 getFormApplicationPreSignedUrl는 폼 애플리케이션 컨텍스트에 맞춰 presigned URL을 생성하는 엔드포인트(/upload-url/form-application)를 제공합니다. 기존의 getPreSignedUrl 메서드는 기능은 그대로 두고 파라미터 포맷팅이 조정되었습니다.

Changes

파일 경로 변경 내용 요약
src/main/java/ddingdong/.../file/api/S3FileAPi.java - getFormApplicationPreSignedUrl 메서드 추가
- getPreSignedUrl 메서드의 파라미터 포맷팅 조정
src/main/java/ddingdong/.../file/controller/S3FileController.java - getFormApplicationPreSignedUrl 메서드 추가 구현 (파일명 디코딩, 현재 시간 조회, 고정 사용자 ID 사용, 서비스 호출 및 URL 응답 반환)

Sequence Diagram(s)

sequenceDiagram
    participant Client as 클라이언트
    participant Controller as S3FileController
    participant Service as s3FileService
    participant AWS as AWS S3

    Client->>Controller: GET /upload-url/form-application?fileName=...
    Controller->>Controller: 파일명 디코딩 및 현재 시간 조회
    Controller->>Controller: GeneratePreSignedUrlRequestCommand 생성 (userId=9999L)
    Controller->>Service: generatePresignedUrlRequest(command)
    Service->>AWS: getPresignedUrl(request)
    AWS-->>Service: URL 응답
    Service-->>Controller: pre-signed URL 전달
    Controller-->>Client: UploadUrlResponse 반환
Loading

Possibly related PRs

Suggested labels

D-1

Suggested reviewers

  • wonjunYou
  • KoSeonJe
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d177fab and b59a4c8.

📒 Files selected for processing (2)
  • src/main/java/ddingdong/ddingdongBE/file/api/S3FileAPi.java (1 hunks)
  • src/main/java/ddingdong/ddingdongBE/file/controller/S3FileController.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and analyze
🔇 Additional comments (2)
src/main/java/ddingdong/ddingdongBE/file/api/S3FileAPi.java (1)

58-59: 코드 포맷팅이 개선되었습니다!

메서드 시그니처의 들여쓰기가 일관성 있게 조정되었습니다.

src/main/java/ddingdong/ddingdongBE/file/controller/S3FileController.java (1)

23-33: 기존 구현이 잘 되어있습니다!

사용자 인증, URL 디코딩, presigned URL 생성 로직이 모두 적절하게 구현되어 있습니다.

Comment on lines +61 to +92
@Operation(summary = "폼 응답 - AWS S3 presignedUrl 발급 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "폼 응답 presignedUrl 발급 성공"),
@ApiResponse(responseCode = "400",
description = "AWS 오류(서버 오류)",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(implementation = ErrorResponse.class),
examples = {
@ExampleObject(name = "AWS 서비스 오류(서버 오류)",
value = """
{
"status": 500,
"message": "AWS 서비스 오류로 인해 Presigned URL 생성에 실패했습니다.",
"timestamp": "2024-08-22T00:08:46.990585"
}
"""
),
@ExampleObject(name = "AWS 클라이언트 오류(서버 오류)",
value = """
{
"status": 500,
"message": "AWS 클라이언트 오류로 인해 Presigned URL 생성에 실패했습니다.",
"timestamp": "2024-08-22T00:08:46.990585"
}
"""
)
})
)
})
@ResponseStatus(HttpStatus.OK)
@GetMapping("/upload-url/form-application")
UploadUrlResponse getFormApplicationPreSignedUrl(@RequestParam("fileName") String fileName);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

보안 검토가 필요합니다.

새로 추가된 엔드포인트에 다음과 같은 보안 관련 문제가 있습니다:

  1. @SecurityRequirement 어노테이션이 누락되어 있습니다.
  2. 인증되지 않은 사용자가 presigned URL을 발급받을 수 있습니다.

기존 /upload-url 엔드포인트와 동일한 수준의 보안이 필요해 보입니다.

다음과 같이 수정을 제안합니다:

    @Operation(summary = "폼 응답 - AWS S3 presignedUrl 발급 API")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "폼 응답 presignedUrl 발급 성공"),
            @ApiResponse(responseCode = "400",
                    description = "AWS 오류(서버 오류)",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = ErrorResponse.class),
                            examples = {
                                    @ExampleObject(name = "AWS 서비스 오류(서버 오류)",
                                            value = """
                                                    {
                                                      "status": 500,
                                                      "message": "AWS 서비스 오류로 인해 Presigned URL 생성에 실패했습니다.",
                                                      "timestamp": "2024-08-22T00:08:46.990585"
                                                    }
                                                    """
                                    ),
                                    @ExampleObject(name = "AWS 클라이언트 오류(서버 오류)",
                                            value = """
                                                    {
                                                      "status": 500,
                                                      "message": "AWS 클라이언트 오류로 인해 Presigned URL 생성에 실패했습니다.",
                                                      "timestamp": "2024-08-22T00:08:46.990585"
                                                    }
                                                    """
                                    )
                            })
            )
    })
    @ResponseStatus(HttpStatus.OK)
+   @SecurityRequirement(name = "AccessToken")
    @GetMapping("/upload-url/form-application")
-   UploadUrlResponse getFormApplicationPreSignedUrl(@RequestParam("fileName") String fileName);
+   UploadUrlResponse getFormApplicationPreSignedUrl(@AuthenticationPrincipal PrincipalDetails principalDetails,
+                                                   @RequestParam("fileName") String fileName);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Operation(summary = "폼 응답 - AWS S3 presignedUrl 발급 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "폼 응답 presignedUrl 발급 성공"),
@ApiResponse(responseCode = "400",
description = "AWS 오류(서버 오류)",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(implementation = ErrorResponse.class),
examples = {
@ExampleObject(name = "AWS 서비스 오류(서버 오류)",
value = """
{
"status": 500,
"message": "AWS 서비스 오류로 인해 Presigned URL 생성에 실패했습니다.",
"timestamp": "2024-08-22T00:08:46.990585"
}
"""
),
@ExampleObject(name = "AWS 클라이언트 오류(서버 오류)",
value = """
{
"status": 500,
"message": "AWS 클라이언트 오류로 인해 Presigned URL 생성에 실패했습니다.",
"timestamp": "2024-08-22T00:08:46.990585"
}
"""
)
})
)
})
@ResponseStatus(HttpStatus.OK)
@GetMapping("/upload-url/form-application")
UploadUrlResponse getFormApplicationPreSignedUrl(@RequestParam("fileName") String fileName);
@Operation(summary = "폼 응답 - AWS S3 presignedUrl 발급 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "폼 응답 presignedUrl 발급 성공"),
@ApiResponse(responseCode = "400",
description = "AWS 오류(서버 오류)",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(implementation = ErrorResponse.class),
examples = {
@ExampleObject(name = "AWS 서비스 오류(서버 오류)",
value = """
{
"status": 500,
"message": "AWS 서비스 오류로 인해 Presigned URL 생성에 실패했습니다.",
"timestamp": "2024-08-22T00:08:46.990585"
}
"""
),
@ExampleObject(name = "AWS 클라이언트 오류(서버 오류)",
value = """
{
"status": 500,
"message": "AWS 클라이언트 오류로 인해 Presigned URL 생성에 실패했습니다.",
"timestamp": "2024-08-22T00:08:46.990585"
}
"""
)
})
)
})
@ResponseStatus(HttpStatus.OK)
@SecurityRequirement(name = "AccessToken")
@GetMapping("/upload-url/form-application")
UploadUrlResponse getFormApplicationPreSignedUrl(@AuthenticationPrincipal PrincipalDetails principalDetails,
@RequestParam("fileName") String fileName);

Comment on lines +35 to +44
@Override
public UploadUrlResponse getFormApplicationPreSignedUrl(String fileName) {
LocalDateTime now = LocalDateTime.now();
String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8);
GeneratePreSignedUrlRequestQuery query =
s3FileService.generatePresignedUrlRequest(
new GeneratePreSignedUrlRequestCommand(now, 9999L, decodedFileName));
URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest());
return UploadUrlResponse.of(query, presingedUrl);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

코드 개선이 필요합니다.

다음과 같은 문제점들이 있습니다:

  1. 코드 중복
  • getPreSignedUrl와 대부분의 로직이 중복됩니다.
  • 공통 로직을 private 메소드로 추출하는 것이 좋습니다.
  1. 보안 문제
  • 하드코딩된 사용자 ID (9999L)는 임시 방편으로 적절하지 않습니다.
  • 실제 사용자 식별이 필요하거나, 시스템 레벨의 식별자를 사용해야 합니다.
  1. 입력 검증 부재
  • fileName 파라미터에 대한 유효성 검사가 없습니다.
  • 파일 확장자 검증이나 파일명 길이 제한 등이 필요할 수 있습니다.

다음과 같은 개선을 제안합니다:

+ private UploadUrlResponse generatePreSignedUrl(Long userId, String fileName) {
+     LocalDateTime now = LocalDateTime.now();
+     String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8);
+     GeneratePreSignedUrlRequestQuery query =
+             s3FileService.generatePresignedUrlRequest(
+                     new GeneratePreSignedUrlRequestCommand(now, userId, decodedFileName));
+     URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest());
+     return UploadUrlResponse.of(query, presingedUrl);
+ }

  @Override
  public UploadUrlResponse getPreSignedUrl(PrincipalDetails principalDetails, String fileName) {
-     LocalDateTime now = LocalDateTime.now();
-     String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8);
-     GeneratePreSignedUrlRequestQuery query =
-             s3FileService.generatePresignedUrlRequest(
-                     new GeneratePreSignedUrlRequestCommand(now, user.getId(), decodedFileName));
-     URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest());
-     return UploadUrlResponse.of(query, presingedUrl);
+     validateFileName(fileName);
+     return generatePreSignedUrl(principalDetails.getUser().getId(), fileName);
  }

  @Override
  public UploadUrlResponse getFormApplicationPreSignedUrl(String fileName) {
+     validateFileName(fileName);
-     LocalDateTime now = LocalDateTime.now();
-     String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8);
-     GeneratePreSignedUrlRequestQuery query =
-             s3FileService.generatePresignedUrlRequest(
-                     new GeneratePreSignedUrlRequestCommand(now, 9999L, decodedFileName));
-     URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest());
-     return UploadUrlResponse.of(query, presingedUrl);
+     // TODO: 시스템 레벨 식별자나 실제 사용자 ID를 사용하도록 수정
+     return generatePreSignedUrl(9999L, fileName);
  }

+ private void validateFileName(String fileName) {
+     if (fileName == null || fileName.trim().isEmpty()) {
+         throw new IllegalArgumentException("파일 이름은 필수입니다.");
+     }
+     // TODO: 파일 확장자 및 길이 검증 로직 추가
+ }

사용자 ID 처리 방식에 대해 팀과 논의가 필요합니다. 다음 중 하나를 선택하시기 바랍니다:

  1. 실제 사용자 인증 추가
  2. 시스템 레벨 식별자 사용
  3. 폼 응답 전용 경로 구조 설계
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public UploadUrlResponse getFormApplicationPreSignedUrl(String fileName) {
LocalDateTime now = LocalDateTime.now();
String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8);
GeneratePreSignedUrlRequestQuery query =
s3FileService.generatePresignedUrlRequest(
new GeneratePreSignedUrlRequestCommand(now, 9999L, decodedFileName));
URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest());
return UploadUrlResponse.of(query, presingedUrl);
}
public class S3FileController {
// ... other fields and methods ...
@Override
public UploadUrlResponse getPreSignedUrl(PrincipalDetails principalDetails, String fileName) {
- LocalDateTime now = LocalDateTime.now();
- String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8);
- GeneratePreSignedUrlRequestQuery query =
- s3FileService.generatePresignedUrlRequest(
- new GeneratePreSignedUrlRequestCommand(now, user.getId(), decodedFileName));
- URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest());
- return UploadUrlResponse.of(query, presingedUrl);
+ validateFileName(fileName);
+ return generatePreSignedUrl(principalDetails.getUser().getId(), fileName);
}
@Override
public UploadUrlResponse getFormApplicationPreSignedUrl(String fileName) {
- LocalDateTime now = LocalDateTime.now();
- String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8);
- GeneratePreSignedUrlRequestQuery query =
- s3FileService.generatePresignedUrlRequest(
- new GeneratePreSignedUrlRequestCommand(now, 9999L, decodedFileName));
- URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest());
- return UploadUrlResponse.of(query, presingedUrl);
+ validateFileName(fileName);
+ // TODO: 시스템 레벨 식별자나 실제 사용자 ID를 사용하도록 수정
+ return generatePreSignedUrl(9999L, fileName);
}
+ private UploadUrlResponse generatePreSignedUrl(Long userId, String fileName) {
+ LocalDateTime now = LocalDateTime.now();
+ String decodedFileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8);
+ GeneratePreSignedUrlRequestQuery query =
+ s3FileService.generatePresignedUrlRequest(
+ new GeneratePreSignedUrlRequestCommand(now, userId, decodedFileName));
+ URL presingedUrl = s3FileService.getPresignedUrl(query.generatePresignedUrlRequest());
+ return UploadUrlResponse.of(query, presingedUrl);
+ }
+
+ private void validateFileName(String fileName) {
+ if (fileName == null || fileName.trim().isEmpty()) {
+ throw new IllegalArgumentException("파일 이름은 필수입니다.");
+ }
+ // TODO: 파일 확장자 및 길이 검증 로직 추가
+ }
// ... potentially other methods ...
}

@5uhwann 5uhwann merged commit e6a4584 into develop Feb 16, 2025
4 checks passed
@5uhwann 5uhwann deleted the refactor/DDING-000 branch February 16, 2025 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
D-0 🎯리팩토링 리팩토링 및 고도화 이슈
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant