diff --git a/src/main/java/com/slatto/domain/video/controller/VideoController.java b/src/main/java/com/slatto/domain/video/controller/VideoController.java
index 0afd8e1a..2bd7aa6f 100644
--- a/src/main/java/com/slatto/domain/video/controller/VideoController.java
+++ b/src/main/java/com/slatto/domain/video/controller/VideoController.java
@@ -10,12 +10,15 @@
import com.slatto.global.response.ApiResponse;
import com.slatto.global.response.code.CommonSuccessCode;
import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Positive;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -29,6 +32,7 @@
import org.springframework.web.bind.annotation.ResponseStatus;
@Validated
+@Tag(name = "Video", description = "프로젝트 영상 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/projects/{projectId}/videos")
@@ -38,15 +42,16 @@ public class VideoController {
@PatchMapping("/{videoId}")
@Operation(summary = "영상 수정",
- description = "프로젝트에 등록된 영상의 제목과 메모를 수정합니다.
" +
- "수정하지 않을 부분은 null로 넣으면 됩니다.")
+ description = "프로젝트 멤버가 영상의 제목과 메모를 수정합니다. " +
+ "변경하지 않을 값은 생략하거나 null로 전달할 수 있으며, 제목과 메모 중 하나 이상은 입력해야 합니다.")
public ApiResponse updateVideo(
+ @AuthenticationPrincipal Long memberId,
+ @Parameter(description = "프로젝트 ID", example = "10")
@PathVariable @Positive Long projectId,
+ @Parameter(description = "영상 ID", example = "1")
@PathVariable @Positive Long videoId,
@Valid @RequestBody VideoUpdateReqDTO request
) {
- // TODO: 인증/인가 구현 후 JWT에서 memberId 추출하도록 변경
- Long memberId = 1L;
return ApiResponse.success(
CommonSuccessCode.OK,
videoService.updateVideo(memberId, projectId, videoId, request)
@@ -54,13 +59,14 @@ public ApiResponse updateVideo(
}
@DeleteMapping("/{videoId}")
- @Operation(summary = "영상 삭제", description = "프로젝트에 등록된 영상을 삭제합니다.")
+ @Operation(summary = "영상 삭제", description = "프로젝트 멤버가 프로젝트에 등록된 영상을 삭제합니다.")
public ApiResponse deleteVideo(
+ @AuthenticationPrincipal Long memberId,
+ @Parameter(description = "프로젝트 ID", example = "10")
@PathVariable @Positive Long projectId,
+ @Parameter(description = "영상 ID", example = "1")
@PathVariable @Positive Long videoId
) {
- // TODO: 인증/인가 구현 후 JWT에서 memberId 추출하도록 변경
- Long memberId = 1L;
return ApiResponse.success(
CommonSuccessCode.OK,
videoService.deleteVideo(memberId, projectId, videoId)
@@ -69,13 +75,17 @@ public ApiResponse deleteVideo(
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
- @Operation(summary = "영상 등록", description = "프로젝트에 새로운 YouTube 영상을 등록합니다.")
+ @Operation(
+ summary = "영상 등록",
+ description = "프로젝트 멤버가 YouTube 영상을 등록합니다. YouTube Data API로 공개 여부와 재생 가능 여부를 확인하며, " +
+ "같은 프로젝트에 이미 등록된 영상이나 비공개·재생 불가능 영상은 등록할 수 없습니다."
+ )
public ApiResponse createVideo(
+ @AuthenticationPrincipal Long memberId,
+ @Parameter(description = "프로젝트 ID", example = "10")
@PathVariable @Positive Long projectId,
@Valid @RequestBody VideoCreateReqDTO request
) {
- // TODO: 인증/인가 구현 후 JWT에서 memberId 추출하도록 변경
- Long memberId = 1L;
return ApiResponse.success(
CommonSuccessCode.CREATED,
videoService.createVideo(memberId, projectId, request)
@@ -83,14 +93,20 @@ public ApiResponse createVideo(
}
@GetMapping
- @Operation(summary = "영상 목록 조회", description = "프로젝트 멤버가 프로젝트에 등록된 영상을 커서 방식으로 조회합니다.")
+ @Operation(
+ summary = "영상 목록 조회",
+ description = "프로젝트 멤버가 등록 영상을 최신순으로 조회합니다. 첫 요청에서는 cursor를 생략하고, " +
+ "다음 페이지는 이전 응답의 nextCursor를 전달합니다. size 기본값은 20이며 최대 100입니다."
+ )
public ApiResponse getVideos(
+ @AuthenticationPrincipal Long memberId,
+ @Parameter(description = "프로젝트 ID", example = "10")
@PathVariable @Positive Long projectId,
+ @Parameter(description = "이전 응답의 nextCursor. 첫 페이지에서는 생략합니다.", example = "9")
@RequestParam(required = false) @Positive Long cursor,
+ @Parameter(description = "조회 개수. 생략 시 20, 최대 100입니다.", example = "20")
@RequestParam(required = false) @Min(1) @Max(100) Integer size
) {
- // TODO: 인증/인가 구현 후 JWT에서 memberId 추출하도록 변경
- Long memberId = 1L;
return ApiResponse.success(
CommonSuccessCode.OK,
videoService.getVideos(memberId, projectId, cursor, size)
diff --git a/src/main/java/com/slatto/domain/video/controller/YoutubeController.java b/src/main/java/com/slatto/domain/video/controller/YoutubeController.java
index 9bee96be..7bfc8cbc 100644
--- a/src/main/java/com/slatto/domain/video/controller/YoutubeController.java
+++ b/src/main/java/com/slatto/domain/video/controller/YoutubeController.java
@@ -6,13 +6,16 @@
import com.slatto.global.response.ApiResponse;
import com.slatto.global.response.code.CommonSuccessCode;
import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+@Tag(name = "YouTube", description = "YouTube 영상 URL 및 등록 가능 여부 검증 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/videos/youtube")
@@ -21,12 +24,16 @@ public class YoutubeController {
private final VideoService videoService;
@PostMapping("/validate")
- @Operation(summary = "YouTube URL 검증", description = "YouTube 영상 정보와 프로젝트 내 중복 등록 여부를 검증합니다.")
+ @Operation(
+ summary = "YouTube URL 검증",
+ description = "프로젝트 멤버가 등록 전에 YouTube URL을 검증합니다. URL에서 영상 ID를 추출한 뒤 " +
+ "프로젝트 내 중복 여부와 YouTube의 공개·재생 가능 상태를 확인하고 영상 정보를 반환합니다. " +
+ "이 API는 영상을 저장하지 않습니다."
+ )
public ApiResponse validateYoutubeUrl(
+ @AuthenticationPrincipal Long memberId,
@Valid @RequestBody YoutubeValidateReqDTO request
) {
- // TODO: 인증/인가 구현 후 JWT에서 memberId 추출하도록 변경
- Long memberId = 1L;
return ApiResponse.success(
CommonSuccessCode.OK,
videoService.validateYoutubeUrl(memberId, request)
diff --git a/src/main/java/com/slatto/domain/video/dto/request/VideoRequest.java b/src/main/java/com/slatto/domain/video/dto/request/VideoRequest.java
index 7b3a3d95..469030d6 100644
--- a/src/main/java/com/slatto/domain/video/dto/request/VideoRequest.java
+++ b/src/main/java/com/slatto/domain/video/dto/request/VideoRequest.java
@@ -25,12 +25,12 @@ public record VideoUpdateReqDTO(
public record YoutubeValidateReqDTO(
@NotBlank(message = "YouTube URL은 필수입니다.")
@Size(max = 500, message = "YouTube URL은 최대 500자까지 입력할 수 있습니다.")
- @Schema(example = "https://www.youtube.com/watch?v=abc123")
+ @Schema(description = "검증할 YouTube 영상 URL", example = "https://www.youtube.com/watch?v=abc123")
String youtubeUrl,
@NotNull(message = "프로젝트 ID는 필수입니다.")
@Positive(message = "프로젝트 ID는 양수여야 합니다.")
- @Schema(example = "10")
+ @Schema(description = "영상 등록 여부를 확인할 프로젝트 ID", example = "10")
Long projectId
) {
}
@@ -39,15 +39,15 @@ public record YoutubeValidateReqDTO(
public record VideoCreateReqDTO(
@NotBlank(message = "YouTube URL은 필수입니다.")
@Size(max = 500, message = "YouTube URL은 최대 500자까지 입력할 수 있습니다.")
- @Schema(example = "https://www.youtube.com/watch?v=abc123")
+ @Schema(description = "등록할 YouTube 영상 URL", example = "https://www.youtube.com/watch?v=abc123")
String youtubeUrl,
@NotBlank(message = "영상 제목은 필수입니다.")
@Size(max = 255, message = "영상 제목은 최대 255자까지 입력할 수 있습니다.")
- @Schema(example = "프로젝트 명")
+ @Schema(description = "프로젝트에 표시할 영상 제목", example = "촬영 콘셉트 참고 영상")
String title,
- @Schema(example = "영상에 관련된 메모", nullable = true)
+ @Schema(description = "영상 관련 메모", example = "오프닝 연출 참고", nullable = true)
String memo
) {
}
diff --git a/src/main/java/com/slatto/domain/video/dto/response/VideoResponse.java b/src/main/java/com/slatto/domain/video/dto/response/VideoResponse.java
index 1f61baab..c185a9e3 100644
--- a/src/main/java/com/slatto/domain/video/dto/response/VideoResponse.java
+++ b/src/main/java/com/slatto/domain/video/dto/response/VideoResponse.java
@@ -31,20 +31,21 @@ public record VideoDeleteResDTO(
@Schema(description = "YouTube URL 검증 응답")
public record YoutubeValidateResDTO(
- @Schema(example = "true") boolean valid,
- @Schema(example = "abc123") String youtubeVideoId,
- @Schema(example = "영상 제목") String title,
- @Schema(example = "https://img.youtube.com/vi/abc123/maxresdefault.jpg") String thumbnailUrl,
- @Schema(example = "1018") int durationSeconds,
- @Schema(example = "true") boolean playable,
- @Schema(example = "등록 가능한 영상입니다.") String message
+ @Schema(description = "프로젝트에 등록 가능한지 여부", example = "true") boolean valid,
+ @Schema(description = "URL에서 추출한 YouTube 영상 ID", example = "abc123") String youtubeVideoId,
+ @Schema(description = "YouTube에서 조회한 영상 제목", example = "영상 제목") String title,
+ @Schema(description = "YouTube 썸네일 URL", example = "https://img.youtube.com/vi/abc123/maxresdefault.jpg")
+ String thumbnailUrl,
+ @Schema(description = "영상 길이(초)", example = "1018") int durationSeconds,
+ @Schema(description = "외부 서비스에서 재생 가능한지 여부", example = "true") boolean playable,
+ @Schema(description = "검증 결과 안내 메시지", example = "등록 가능한 영상입니다.") String message
) {
}
@Schema(description = "영상 등록 응답")
public record VideoCreateResDTO(
@Schema(example = "1") Long videoId,
- @Schema(example = "프로젝트 명") String title,
+ @Schema(example = "촬영 콘셉트 참고 영상") String title,
@Schema(example = "https://img.youtube.com/vi/abc123/maxresdefault.jpg") String thumbnailUrl,
@Schema(example = "1018") Integer durationSeconds,
@Schema(example = "false") boolean bookmarked,
@@ -61,6 +62,7 @@ public static VideoCreateResDTO from(Video video) {
@Schema(description = "영상 목록 조회 응답")
public record VideoListResDTO(
+ @Schema(description = "조회된 영상 목록")
List items,
@Schema(description = "다음 페이지 조회 커서", example = "9", nullable = true)
Long nextCursor,
@@ -69,9 +71,10 @@ public record VideoListResDTO(
) {
}
+ @Schema(description = "영상 목록 항목")
public record VideoItemResDTO(
@Schema(example = "10") Long videoId,
- @Schema(example = "프로젝트 명") String title,
+ @Schema(example = "촬영 콘셉트 참고 영상") String title,
@Schema(example = "https://img.youtube.com/vi/abc123/maxresdefault.jpg") String thumbnailUrl,
@Schema(example = "true") boolean bookmarked,
@Schema(example = "IN_PROGRESS") String progressStatus,