-
Notifications
You must be signed in to change notification settings - Fork 0
feat: AWS S3 기반 파일 관리 시스템 구현 #69
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
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
c3bfb06
chore: AWS S3 의존성 및 설정 추가
ysw789 97cc571
feat: 파일 도메인 레이어 구현
ysw789 aeaa916
feat: 파일 Outbound Adapter 구현
ysw789 d344400
feat: 파일 관리 UseCase 구현
ysw789 a011c59
feat: 파일 관리 API 추가
ysw789 8310ff7
refactor: Presigned URL 캐싱 및 URL 처리 서비스 추출
ysw789 32cc484
refactor: 파일 삭제 공통 서비스 추출
ysw789 84fb194
refactor: FileScheduleService 인터페이스 제거 및 스케줄러 클래스명 변경
ysw789 03d8b2e
refactor: Presigned URL 응답 DTO 공통화 및 만료시각 epoch millis 변환
ysw789 db8f847
fix: S3 presign 실패 예외 처리 및 Redis 장애 격리
ysw789 cb71aff
refactor: FileCleanupScheduler를 adapter 레이어로 이동
ysw789 88a6a9e
fix: 파일 업로드 UseCase에 @Transactional 추가
ysw789 22b5dfe
feat: 누락된 요청 파라미터/Multipart 예외 처리 핸들러 추가
ysw789 d3260f9
chore: AWS SDK 버전 업그레이드
ysw789 d34de42
fix: S3 클라이언트 설정 개선 및 파일 확장자 정규화
ysw789 9843952
refactor: 파일 확장자 추출 엣지 케이스 처리
ysw789 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
...n/java/com/dreamteam/alter/adapter/inbound/admin/file/controller/AdminFileController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| package com.dreamteam.alter.adapter.inbound.admin.file.controller; | ||
|
|
||
| import com.dreamteam.alter.adapter.inbound.admin.file.dto.AdminUploadFileResponseDto; | ||
| import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; | ||
| import com.dreamteam.alter.adapter.inbound.common.dto.FilePresignedUrlResponseDto; | ||
| import com.dreamteam.alter.application.aop.AdminActionContext; | ||
| import com.dreamteam.alter.domain.file.port.inbound.AdminDeleteFileUseCase; | ||
| import com.dreamteam.alter.domain.file.port.inbound.AdminGetPresignedUrlUseCase; | ||
| import com.dreamteam.alter.domain.file.port.inbound.AdminUploadFileUseCase; | ||
| import com.dreamteam.alter.domain.file.type.BucketType; | ||
| import com.dreamteam.alter.domain.file.type.FileTargetType; | ||
| import com.dreamteam.alter.domain.user.context.AdminActor; | ||
| import jakarta.annotation.Resource; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.security.access.prepost.PreAuthorize; | ||
| import org.springframework.web.bind.annotation.*; | ||
| import org.springframework.web.multipart.MultipartFile; | ||
|
|
||
|
|
||
| @RestController | ||
| @RequestMapping("/admin/files") | ||
| @PreAuthorize("hasAnyRole('ADMIN')") | ||
| @RequiredArgsConstructor | ||
| public class AdminFileController implements AdminFileControllerSpec { | ||
ysw789 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| @Resource(name = "adminUploadFile") | ||
| private final AdminUploadFileUseCase adminUploadFile; | ||
|
|
||
| @Resource(name = "adminGetPresignedUrl") | ||
| private final AdminGetPresignedUrlUseCase adminGetPresignedUrl; | ||
|
|
||
| @Resource(name = "adminDeleteFile") | ||
| private final AdminDeleteFileUseCase adminDeleteFile; | ||
|
|
||
| @Override | ||
| @PostMapping | ||
| public ResponseEntity<CommonApiResponse<AdminUploadFileResponseDto>> uploadFile( | ||
| @RequestParam("file") MultipartFile file, | ||
| @RequestParam("targetType") FileTargetType targetType, | ||
| @RequestParam("bucketType") BucketType bucketType | ||
| ) { | ||
| AdminActor actor = AdminActionContext.getInstance().getActor(); | ||
| return ResponseEntity.ok(CommonApiResponse.of(adminUploadFile.execute(actor, file, targetType, bucketType))); | ||
| } | ||
|
|
||
| @Override | ||
| @GetMapping("/{fileId}/presigned-url") | ||
| public ResponseEntity<CommonApiResponse<FilePresignedUrlResponseDto>> getPresignedUrl( | ||
| @PathVariable String fileId | ||
| ) { | ||
| AdminActor actor = AdminActionContext.getInstance().getActor(); | ||
| return ResponseEntity.ok(CommonApiResponse.of(adminGetPresignedUrl.execute(actor, fileId))); | ||
| } | ||
|
|
||
| @Override | ||
| @DeleteMapping("/{fileId}") | ||
| public ResponseEntity<CommonApiResponse<Void>> deleteFile( | ||
| @PathVariable String fileId | ||
| ) { | ||
| AdminActor actor = AdminActionContext.getInstance().getActor(); | ||
| adminDeleteFile.execute(actor, fileId); | ||
| return ResponseEntity.ok(CommonApiResponse.empty()); | ||
| } | ||
| } | ||
73 changes: 73 additions & 0 deletions
73
...va/com/dreamteam/alter/adapter/inbound/admin/file/controller/AdminFileControllerSpec.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| package com.dreamteam.alter.adapter.inbound.admin.file.controller; | ||
|
|
||
| import com.dreamteam.alter.adapter.inbound.admin.file.dto.AdminUploadFileResponseDto; | ||
| import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; | ||
| import com.dreamteam.alter.adapter.inbound.common.dto.ErrorResponse; | ||
| import com.dreamteam.alter.adapter.inbound.common.dto.FilePresignedUrlResponseDto; | ||
| import com.dreamteam.alter.domain.file.type.BucketType; | ||
| import com.dreamteam.alter.domain.file.type.FileTargetType; | ||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import io.swagger.v3.oas.annotations.media.Content; | ||
| import io.swagger.v3.oas.annotations.media.ExampleObject; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
| import org.springframework.web.multipart.MultipartFile; | ||
|
|
||
|
|
||
| @Tag(name = "ADMIN - 파일 API") | ||
| public interface AdminFileControllerSpec { | ||
|
|
||
| @Operation(summary = "파일 업로드", description = "파일을 S3에 업로드하고 PENDING 상태로 저장합니다.") | ||
| @ApiResponses(value = { | ||
| @ApiResponse(responseCode = "200", description = "파일 업로드 성공"), | ||
| @ApiResponse(responseCode = "400", description = "실패 케이스", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = { | ||
| @ExampleObject(name = "유효하지 않은 파일", value = "{\"code\" : \"B022\"}"), | ||
| @ExampleObject(name = "허용되지 않는 파일 형식", value = "{\"code\" : \"B023\"}"), | ||
| @ExampleObject(name = "파일 크기 초과", value = "{\"code\" : \"B024\"}") | ||
| })) | ||
| }) | ||
| ResponseEntity<CommonApiResponse<AdminUploadFileResponseDto>> uploadFile( | ||
| @RequestParam("file") MultipartFile file, | ||
| @RequestParam("targetType") FileTargetType targetType, | ||
| @RequestParam("bucketType") BucketType bucketType | ||
| ); | ||
|
|
||
| @Operation(summary = "Presigned URL 조회", description = "모든 Private 파일 접근을 위한 Presigned URL을 조회합니다. (소유권 검증 없음)") | ||
| @ApiResponses(value = { | ||
| @ApiResponse(responseCode = "200", description = "Presigned URL 조회 성공"), | ||
| @ApiResponse(responseCode = "404", description = "존재하지 않는 파일", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = { | ||
| @ExampleObject(name = "파일 없음", value = "{\"code\" : \"B021\"}") | ||
| })) | ||
| }) | ||
| ResponseEntity<CommonApiResponse<FilePresignedUrlResponseDto>> getPresignedUrl( | ||
| @PathVariable String fileId | ||
| ); | ||
|
|
||
| @Operation(summary = "파일 삭제", description = "모든 파일을 S3에서 삭제하고 DELETED 상태로 변경합니다. (소유권 검증 없음)") | ||
| @ApiResponses(value = { | ||
| @ApiResponse(responseCode = "200", description = "파일 삭제 성공"), | ||
| @ApiResponse(responseCode = "404", description = "존재하지 않는 파일", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = { | ||
| @ExampleObject(name = "파일 없음", value = "{\"code\" : \"B021\"}") | ||
| })) | ||
| }) | ||
| ResponseEntity<CommonApiResponse<Void>> deleteFile( | ||
| @PathVariable String fileId | ||
| ); | ||
| } |
21 changes: 21 additions & 0 deletions
21
...n/java/com/dreamteam/alter/adapter/inbound/admin/file/dto/AdminUploadFileResponseDto.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.dreamteam.alter.adapter.inbound.admin.file.dto; | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import lombok.AccessLevel; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||
| @Schema(description = "파일 업로드 응답 DTO") | ||
| public class AdminUploadFileResponseDto { | ||
|
|
||
| @Schema(description = "파일 ID", example = "01959b4e-4e5f-7c3a-8d9e-0f1a2b3c4d5e") | ||
| private String fileId; | ||
|
|
||
| public static AdminUploadFileResponseDto of(String fileId) { | ||
| return new AdminUploadFileResponseDto(fileId); | ||
| } | ||
| } |
25 changes: 25 additions & 0 deletions
25
...main/java/com/dreamteam/alter/adapter/inbound/common/dto/FilePresignedUrlResponseDto.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.dreamteam.alter.adapter.inbound.common.dto; | ||
|
|
||
| import com.dreamteam.alter.domain.file.PresignedUrlResult; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import lombok.AccessLevel; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||
| @Schema(description = "Presigned URL 조회 응답 DTO") | ||
| public class FilePresignedUrlResponseDto { | ||
|
|
||
| @Schema(description = "S3 Presigned URL (유효시간 제한 있음)") | ||
| private String presignedUrl; | ||
|
|
||
| @Schema(description = "Presigned URL 만료 시각 (epoch milliseconds)") | ||
| private long expiresAt; | ||
|
|
||
| public static FilePresignedUrlResponseDto of(PresignedUrlResult result) { | ||
| return new FilePresignedUrlResponseDto(result.url(), result.expiresAt().toEpochMilli()); | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
20 changes: 20 additions & 0 deletions
20
src/main/java/com/dreamteam/alter/adapter/inbound/common/dto/FileResponseDto.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.dreamteam.alter.adapter.inbound.common.dto; | ||
|
|
||
| import com.dreamteam.alter.domain.file.entity.File; | ||
| import lombok.AccessLevel; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||
| public class FileResponseDto { | ||
|
|
||
| private String fileId; | ||
| private String url; | ||
|
|
||
| public static FileResponseDto of(File file, String url) { | ||
| return new FileResponseDto(file.getId(), url); | ||
| } | ||
| } |
22 changes: 22 additions & 0 deletions
22
...java/com/dreamteam/alter/adapter/inbound/general/file/scheduler/FileCleanupScheduler.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package com.dreamteam.alter.adapter.inbound.general.file.scheduler; | ||
|
|
||
| import com.dreamteam.alter.domain.file.port.inbound.CleanupOrphanFilesUseCase; | ||
| import jakarta.annotation.Resource; | ||
| import lombok.RequiredArgsConstructor; | ||
| import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; | ||
| import org.springframework.scheduling.annotation.Scheduled; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class FileCleanupScheduler { | ||
|
|
||
| @Resource(name = "cleanupOrphanFiles") | ||
| private final CleanupOrphanFilesUseCase cleanupOrphanFiles; | ||
ysw789 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| @Scheduled(cron = "0 0 3 * * *") | ||
| @SchedulerLock(name = "cleanupOrphanFiles", lockAtMostFor = "30m") | ||
| public void cleanupOrphanFiles() { | ||
| cleanupOrphanFiles.execute(); | ||
| } | ||
ysw789 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
65 changes: 65 additions & 0 deletions
65
...va/com/dreamteam/alter/adapter/inbound/manager/file/controller/ManagerFileController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| package com.dreamteam.alter.adapter.inbound.manager.file.controller; | ||
|
|
||
| import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; | ||
| import com.dreamteam.alter.adapter.inbound.common.dto.FilePresignedUrlResponseDto; | ||
| import com.dreamteam.alter.adapter.inbound.manager.file.dto.ManagerUploadFileResponseDto; | ||
| import com.dreamteam.alter.application.aop.ManagerActionContext; | ||
| import com.dreamteam.alter.domain.file.port.inbound.ManagerDeleteFileUseCase; | ||
| import com.dreamteam.alter.domain.file.port.inbound.ManagerGetPresignedUrlUseCase; | ||
| import com.dreamteam.alter.domain.file.port.inbound.ManagerUploadFileUseCase; | ||
| import com.dreamteam.alter.domain.file.type.BucketType; | ||
| import com.dreamteam.alter.domain.file.type.FileTargetType; | ||
| import com.dreamteam.alter.domain.user.context.ManagerActor; | ||
| import jakarta.annotation.Resource; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.security.access.prepost.PreAuthorize; | ||
| import org.springframework.web.bind.annotation.*; | ||
| import org.springframework.web.multipart.MultipartFile; | ||
|
|
||
|
|
||
| @RestController | ||
| @RequestMapping("/manager/files") | ||
| @PreAuthorize("hasAnyRole('MANAGER')") | ||
| @RequiredArgsConstructor | ||
| public class ManagerFileController implements ManagerFileControllerSpec { | ||
ysw789 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| @Resource(name = "managerUploadFile") | ||
| private final ManagerUploadFileUseCase managerUploadFile; | ||
|
|
||
| @Resource(name = "managerGetPresignedUrl") | ||
| private final ManagerGetPresignedUrlUseCase managerGetPresignedUrl; | ||
|
|
||
| @Resource(name = "managerDeleteFile") | ||
| private final ManagerDeleteFileUseCase managerDeleteFile; | ||
ysw789 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| @Override | ||
| @PostMapping | ||
| public ResponseEntity<CommonApiResponse<ManagerUploadFileResponseDto>> uploadFile( | ||
| @RequestParam("file") MultipartFile file, | ||
| @RequestParam("targetType") FileTargetType targetType, | ||
| @RequestParam("bucketType") BucketType bucketType | ||
| ) { | ||
| ManagerActor actor = ManagerActionContext.getInstance().getActor(); | ||
| return ResponseEntity.ok(CommonApiResponse.of(managerUploadFile.execute(actor, file, targetType, bucketType))); | ||
| } | ||
|
|
||
| @Override | ||
| @GetMapping("/{fileId}/presigned-url") | ||
| public ResponseEntity<CommonApiResponse<FilePresignedUrlResponseDto>> getPresignedUrl( | ||
| @PathVariable String fileId | ||
| ) { | ||
| ManagerActor actor = ManagerActionContext.getInstance().getActor(); | ||
| return ResponseEntity.ok(CommonApiResponse.of(managerGetPresignedUrl.execute(actor, fileId))); | ||
| } | ||
|
|
||
| @Override | ||
| @DeleteMapping("/{fileId}") | ||
| public ResponseEntity<CommonApiResponse<Void>> deleteFile( | ||
| @PathVariable String fileId | ||
| ) { | ||
| ManagerActor actor = ManagerActionContext.getInstance().getActor(); | ||
| managerDeleteFile.execute(actor, fileId); | ||
| return ResponseEntity.ok(CommonApiResponse.empty()); | ||
| } | ||
| } | ||
87 changes: 87 additions & 0 deletions
87
...om/dreamteam/alter/adapter/inbound/manager/file/controller/ManagerFileControllerSpec.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| package com.dreamteam.alter.adapter.inbound.manager.file.controller; | ||
|
|
||
| import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse; | ||
| import com.dreamteam.alter.adapter.inbound.common.dto.ErrorResponse; | ||
| import com.dreamteam.alter.adapter.inbound.common.dto.FilePresignedUrlResponseDto; | ||
| import com.dreamteam.alter.adapter.inbound.manager.file.dto.ManagerUploadFileResponseDto; | ||
| import com.dreamteam.alter.domain.file.type.BucketType; | ||
| import com.dreamteam.alter.domain.file.type.FileTargetType; | ||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import io.swagger.v3.oas.annotations.media.Content; | ||
| import io.swagger.v3.oas.annotations.media.ExampleObject; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
| import org.springframework.web.multipart.MultipartFile; | ||
|
|
||
|
|
||
| @Tag(name = "MANAGER - 파일 API") | ||
| public interface ManagerFileControllerSpec { | ||
|
|
||
| @Operation(summary = "파일 업로드", description = "파일을 S3에 업로드하고 PENDING 상태로 저장합니다.") | ||
| @ApiResponses(value = { | ||
| @ApiResponse(responseCode = "200", description = "파일 업로드 성공"), | ||
| @ApiResponse(responseCode = "400", description = "실패 케이스", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = { | ||
| @ExampleObject(name = "유효하지 않은 파일", value = "{\"code\" : \"B022\"}"), | ||
| @ExampleObject(name = "허용되지 않는 파일 형식", value = "{\"code\" : \"B023\"}"), | ||
| @ExampleObject(name = "파일 크기 초과", value = "{\"code\" : \"B024\"}") | ||
| })) | ||
| }) | ||
| ResponseEntity<CommonApiResponse<ManagerUploadFileResponseDto>> uploadFile( | ||
| @RequestParam("file") MultipartFile file, | ||
| @RequestParam("targetType") FileTargetType targetType, | ||
| @RequestParam("bucketType") BucketType bucketType | ||
| ); | ||
|
|
||
| @Operation(summary = "Presigned URL 조회", description = "본인이 업로드한 Private 파일 접근을 위한 Presigned URL을 조회합니다.") | ||
| @ApiResponses(value = { | ||
| @ApiResponse(responseCode = "200", description = "Presigned URL 조회 성공"), | ||
| @ApiResponse(responseCode = "403", description = "권한 없음", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = { | ||
| @ExampleObject(name = "본인 파일 아님", value = "{\"code\" : \"A005\"}") | ||
| })), | ||
| @ApiResponse(responseCode = "404", description = "존재하지 않는 파일", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = { | ||
| @ExampleObject(name = "파일 없음", value = "{\"code\" : \"B021\"}") | ||
| })) | ||
| }) | ||
| ResponseEntity<CommonApiResponse<FilePresignedUrlResponseDto>> getPresignedUrl( | ||
| @PathVariable String fileId | ||
| ); | ||
|
|
||
| @Operation(summary = "파일 삭제", description = "본인이 업로드한 파일을 S3에서 삭제하고 DELETED 상태로 변경합니다.") | ||
| @ApiResponses(value = { | ||
| @ApiResponse(responseCode = "200", description = "파일 삭제 성공"), | ||
| @ApiResponse(responseCode = "403", description = "권한 없음", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = { | ||
| @ExampleObject(name = "본인 파일 아님", value = "{\"code\" : \"A005\"}") | ||
| })), | ||
| @ApiResponse(responseCode = "404", description = "존재하지 않는 파일", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorResponse.class), | ||
| examples = { | ||
| @ExampleObject(name = "파일 없음", value = "{\"code\" : \"B021\"}") | ||
| })) | ||
| }) | ||
| ResponseEntity<CommonApiResponse<Void>> deleteFile( | ||
| @PathVariable String fileId | ||
| ); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.