Skip to content

Commit e8a8d4c

Browse files
authored
영상 파이프라인 중 메타데이터 저장 (#102)
* Feat : 조회수 추가기능 작성 * Feat : Operation 추가 * Feat : 카프카 의존성 추가 및 설정 * Refactor : 파일의 크기는 status에 포함되므로 제거 * Refactor : 응답 String으로 변경 * Feat : 트랜스코딩 메시지 프로듀싱 * Feat : 메타데이터 저장
1 parent 1110940 commit e8a8d4c

File tree

19 files changed

+240
-54
lines changed

19 files changed

+240
-54
lines changed

back/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ dependencies {
6666
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
6767

6868
implementation ("software.amazon.awssdk:s3:2.25.0")
69+
70+
implementation ("org.springframework.kafka:spring-kafka")
6971
}
7072

7173
tasks.withType<Test> {

back/src/main/java/com/back/domain/file/video/controller/VideoController.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.back.domain.file.video.dto.service.PresignedUrlResponse;
55
import com.back.domain.file.video.service.FileManager;
66
import com.back.global.rsData.RsData;
7+
import io.swagger.v3.oas.annotations.Operation;
78
import lombok.RequiredArgsConstructor;
89
import org.springframework.web.bind.annotation.GetMapping;
910
import org.springframework.web.bind.annotation.RequestParam;
@@ -15,16 +16,18 @@ public class VideoController {
1516
private final FileManager fileManager;
1617

1718
@GetMapping("/videos/upload")
19+
@Operation(summary="업로드용 URL 요청", description="파일 업로드를 위한 Presigned URL을 발급받습니다.")
1820
public RsData<UploadUrlGetResponse> getUploadUrl() {
1921
PresignedUrlResponse uploadUrl = fileManager.getUploadUrl();
20-
UploadUrlGetResponse response = new UploadUrlGetResponse(uploadUrl.url(), uploadUrl.expiresAt());
22+
UploadUrlGetResponse response = new UploadUrlGetResponse(uploadUrl.url().toString(), uploadUrl.expiresAt());
2123
return new RsData<>("200", "업로드용 URL 요청완료", response);
2224
}
2325

2426
@GetMapping("/videos/download")
27+
@Operation(summary="다운로드용 URL 요청", description="파일 다운로드를 위한 Presigned URL을 발급받습니다.")
2528
public RsData<UploadUrlGetResponse> getDownloadUrls(@RequestParam String objectKey) {
2629
PresignedUrlResponse downloadUrl = fileManager.getDownloadUrl(objectKey);
27-
UploadUrlGetResponse response = new UploadUrlGetResponse(downloadUrl.url(), downloadUrl.expiresAt());
30+
UploadUrlGetResponse response = new UploadUrlGetResponse(downloadUrl.url().toString(), downloadUrl.expiresAt());
2831
return new RsData<>("200", "다운로드용 URL 요청완료", response);
2932
}
3033
}
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package com.back.domain.file.video.dto.controller;
22

3-
import java.net.URL;
43
import java.time.LocalDateTime;
54

65
public record UploadUrlGetResponse(
7-
URL url,
6+
String url,
87
LocalDateTime expiresAt
98
) {
109
}

back/src/main/java/com/back/domain/file/video/entity/Video.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,15 @@ public class Video extends BaseEntity {
2424

2525
private Integer duration;
2626

27-
private Long fileSize;
28-
2927
@Builder(access = AccessLevel.PRIVATE)
30-
private Video(String uuid, String status, String path, Integer duration, Long fileSize) {
28+
private Video(String uuid, String status, String path, Integer duration) {
3129
this.uuid = uuid;
3230
this.status = status;
3331
this.path = path;
3432
this.duration = duration;
35-
this.fileSize = fileSize;
3633
}
3734

38-
public static Video create(String uuid, String status, String path, Integer duration, Long fileSize) {
35+
public static Video create(String uuid, String status, String path, Integer duration) {
3936
if (uuid == null || uuid.isBlank()) {
4037
throw new IllegalArgumentException("uuid cannot be null or empty");
4138
}
@@ -48,7 +45,6 @@ public static Video create(String uuid, String status, String path, Integer dura
4845
.status(status)
4946
.path(path)
5047
.duration(duration)
51-
.fileSize(fileSize)
5248
.build();
5349
}
5450
public void updateStatus(String status) {

back/src/main/java/com/back/domain/file/video/service/FileManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,13 @@ public PresignedUrlResponse getDownloadUrl(String objectKey) {
2929
LocalDateTime expiresAt = LocalDateTime.now().plusMinutes(expires);
3030
return new PresignedUrlResponse(url, expiresAt);
3131
}
32+
33+
//TODO : 테스트 작성필요
34+
public void updateVideoStatus(String videoId, String status) {
35+
try {
36+
videoService.updateStatus(videoId, status);
37+
} catch (Exception e) {
38+
videoService.createVideo(videoId, status, "/", 0);
39+
}
40+
}
3241
}

back/src/main/java/com/back/domain/file/video/service/VideoService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
public class VideoService {
1212
private final VideoRepository videoRepository;
1313

14-
public Video createVideo(String uuid, String status, String path, Integer duration, Long fileSize) {
15-
Video video = Video.create(uuid, status, path, duration, fileSize);
14+
public Video createVideo(String uuid, String status, String path, Integer duration) {
15+
Video video = Video.create(uuid, status, path, duration);
1616
return videoRepository.save(video);
1717
}
1818

back/src/main/java/com/back/domain/news/news/controller/NewsController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public RsData<NewsCreateResponse> createNews(@RequestBody NewsCreateRequest requ
4545
@Operation(summary = "뉴스 단건 조회", description = "특정 ID의 뉴스를 읽어옵니다.")
4646
public RsData<NewsGetResponse> getNews(@PathVariable("newsId") Long newsId) {
4747
News news = newsService.getNewsById(newsId);
48+
newsService.incrementViews(news);
4849
NewsGetResponse response = new NewsGetResponse(news);
4950
return new RsData<>("200", "뉴스 읽어오기 완료", response);
5051
}

back/src/main/java/com/back/domain/news/news/entity/News.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@ public class News extends BaseEntity {
2424
@OneToOne
2525
private Video video;
2626
private String content;
27+
private Integer views;
2728
@OneToMany(mappedBy = "news", cascade = CascadeType.ALL, orphanRemoval = true)
2829
private List<NewsComment> newsComment;
2930
@OneToMany(mappedBy = "news", cascade = CascadeType.ALL, orphanRemoval = true)
3031
private List<NewsLike> newsLikes = new ArrayList<>();
3132

3233
@Builder(access = AccessLevel.PRIVATE)
33-
private News(Member member, String title, Video video, String content, List<NewsComment> newsComment, List<NewsLike> newsLikes) {
34+
private News(Member member, String title, Video video, String content, Integer views, List<NewsComment> newsComment, List<NewsLike> newsLikes) {
3435
this.member = member;
3536
this.title = title;
3637
this.video = video;
3738
this.content = content;
39+
this.views = views;
3840
this.newsComment = newsComment;
3941
this.newsLikes = newsLikes;
4042
}
@@ -57,6 +59,7 @@ public static News create(Member member, String title, Video video, String conte
5759
.title(title)
5860
.video(video)
5961
.content(content)
62+
.views(0)
6063
.newsComment(new ArrayList<>())
6164
.newsLikes(new ArrayList<>())
6265
.build();
@@ -80,6 +83,10 @@ public void unLike(NewsLike newsLike) {
8083
this.newsLikes.remove(newsLike);
8184
}
8285

86+
public void incrementViews() {
87+
this.views += 1;
88+
}
89+
8390
public void addComment(NewsComment newsComment) {
8491
if (newsComment == null) {
8592
throw new IllegalArgumentException("Comment cannot be null");

back/src/main/java/com/back/domain/news/news/service/NewsService.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,20 @@ public News getNewsById(Long id) {
3434

3535
public News updateNews(Member member, News news, String title, Video video, String content) {
3636
if (!(member.getRole() == Member.Role.ADMIN)) {
37-
throw new ServiceException("403","수정 권한이 없습니다.");
37+
throw new ServiceException("403", "수정 권한이 없습니다.");
3838
}
3939
news.update(title, video, content);
4040
return newsRepository.save(news);
4141
}
4242

43+
public News incrementViews(News news) {
44+
news.incrementViews();
45+
return newsRepository.save(news);
46+
}
47+
4348
public void deleteNews(Member member, News news) {
4449
if (!(member.getRole() == Member.Role.ADMIN)) {
45-
throw new ServiceException("403","수정 권한이 없습니다.");
50+
throw new ServiceException("403", "수정 권한이 없습니다.");
4651
}
4752
newsRepository.delete(news);
4853
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.back.global.kafka;
2+
3+
import org.apache.kafka.clients.consumer.ConsumerConfig;
4+
import org.apache.kafka.common.serialization.StringDeserializer;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.kafka.annotation.EnableKafka;
8+
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
9+
import org.springframework.kafka.core.ConsumerFactory;
10+
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
11+
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
@EnableKafka
16+
@Configuration
17+
public class KafkaConfig {
18+
19+
@Bean
20+
public ConsumerFactory<String, String> consumerFactory() {
21+
Map<String, Object> props = new HashMap<>();
22+
23+
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
24+
props.put(ConsumerConfig.GROUP_ID_CONFIG, "spring-boot-group");
25+
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
26+
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
27+
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
28+
29+
return new DefaultKafkaConsumerFactory<>(props);
30+
}
31+
32+
@Bean
33+
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
34+
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
35+
factory.setConsumerFactory(consumerFactory());
36+
return factory;
37+
}
38+
}

0 commit comments

Comments
 (0)