-
Notifications
You must be signed in to change notification settings - Fork 1
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
Conversation
Walkthrough이 PR은 S3FileAPI 인터페이스와 S3FileController 클래스에 새로운 메서드를 추가하는 작업입니다. 새롭게 도입된 Changes
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 반환
Possibly related PRs
Suggested labels
Suggested reviewers
✨ Finishing Touches
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? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
Documentation and Community
|
|
There was a problem hiding this 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
📒 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 생성 로직이 모두 적절하게 구현되어 있습니다.
@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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
보안 검토가 필요합니다.
새로 추가된 엔드포인트에 다음과 같은 보안 관련 문제가 있습니다:
@SecurityRequirement
어노테이션이 누락되어 있습니다.- 인증되지 않은 사용자가 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.
@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); |
@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); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드 개선이 필요합니다.
다음과 같은 문제점들이 있습니다:
- 코드 중복
getPreSignedUrl
와 대부분의 로직이 중복됩니다.- 공통 로직을 private 메소드로 추출하는 것이 좋습니다.
- 보안 문제
- 하드코딩된 사용자 ID (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);
+ }
@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 처리 방식에 대해 팀과 논의가 필요합니다. 다음 중 하나를 선택하시기 바랍니다:
- 실제 사용자 인증 추가
- 시스템 레벨 식별자 사용
- 폼 응답 전용 경로 구조 설계
📝 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.
@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 ... | |
} |
🚀 작업 내용
🤔 고민했던 내용
💬 리뷰 중점사항
Summary by CodeRabbit