From 1a7b2d112f800a7c15be23c9fd81268ab6534f33 Mon Sep 17 00:00:00 2001 From: bagsunyoung Date: Thu, 1 May 2025 01:00:33 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EC=9E=84=EC=8B=9C2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build.gradle.kts | 2 +- .../example/booktree/config/RedisConfig.java | 74 +++--- .../controller/PopularPostController.java | 88 +++---- .../service/PopularPostService.java | 218 +++++++++--------- .../post/controller/PostController.java | 4 +- 5 files changed, 193 insertions(+), 193 deletions(-) diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index 9bdef9ce..6acb2d87 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -66,7 +66,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-oauth2-client") // Redis 의존성 - implementation ("org.springframework.boot:spring-boot-starter-data-redis") + // implementation ("org.springframework.boot:spring-boot-starter-data-redis") diff --git a/backend/src/main/java/com/example/booktree/config/RedisConfig.java b/backend/src/main/java/com/example/booktree/config/RedisConfig.java index f77c8d0a..d479480d 100644 --- a/backend/src/main/java/com/example/booktree/config/RedisConfig.java +++ b/backend/src/main/java/com/example/booktree/config/RedisConfig.java @@ -1,37 +1,37 @@ -package com.example.booktree.config; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.StringRedisTemplate; - -@Configuration -public class RedisConfig { - - - @Value("${spring.data.redis.host}") - private String redisHost; - - @Value("${spring.data.redis.port}") - private int redisPort; - - @Value("${spring.data.redis.password:}") // password가 비어있을 수도 있으니까 기본값은 빈 문자열로 - private String redisPassword; - - @Bean - public RedisConnectionFactory redisConnectionFactory() { - LettuceConnectionFactory factory = new LettuceConnectionFactory(redisHost, redisPort); - if (!redisPassword.isBlank()) { - factory.setPassword(redisPassword); - } - return factory; - } - - @Bean - public StringRedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { - return new StringRedisTemplate(connectionFactory); - } - -} +//package com.example.booktree.config; +// +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.data.redis.connection.RedisConnectionFactory; +//import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +//import org.springframework.data.redis.core.StringRedisTemplate; +// +//@Configuration +//public class RedisConfig { +// +// +// @Value("${spring.data.redis.host}") +// private String redisHost; +// +// @Value("${spring.data.redis.port}") +// private int redisPort; +// +// @Value("${spring.data.redis.password:}") // password가 비어있을 수도 있으니까 기본값은 빈 문자열로 +// private String redisPassword; +// +// @Bean +// public RedisConnectionFactory redisConnectionFactory() { +// LettuceConnectionFactory factory = new LettuceConnectionFactory(redisHost, redisPort); +// if (!redisPassword.isBlank()) { +// factory.setPassword(redisPassword); +// } +// return factory; +// } +// +// @Bean +// public StringRedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { +// return new StringRedisTemplate(connectionFactory); +// } +// +//} diff --git a/backend/src/main/java/com/example/booktree/popularpost/controller/PopularPostController.java b/backend/src/main/java/com/example/booktree/popularpost/controller/PopularPostController.java index 28b2da81..52573eec 100644 --- a/backend/src/main/java/com/example/booktree/popularpost/controller/PopularPostController.java +++ b/backend/src/main/java/com/example/booktree/popularpost/controller/PopularPostController.java @@ -1,44 +1,44 @@ - -package com.example.booktree.popularpost.controller; - -import com.example.booktree.post.dto.response.PostResponseDto; -import com.example.booktree.post.entity.Post; -import com.example.booktree.popularpost.service.PopularPostService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/v1/popular") -@Slf4j -@Tag(name = "실시간 인기 게시글 관리 컨트롤러") -public class PopularPostController { - - private final PopularPostService popularPostService; - - // 실시간 인기 게시글 목록 가져오기 - - @GetMapping("/get/posts/{mainCategoryId}") - @Operation( - summary = "실시간 조회수 높은 게시글 불러오기 기능", - description = "Redis를 통한 실시간 조회수가 높은 게시글을 불러오는 기능", - tags = "실시간 인기 게시글 관리 컨트롤러" - ) - public ResponseEntity> getPopularPosts( - @RequestParam(defaultValue = "6") int limit, @PathVariable Long mainCategoryId) { - - log.info("뭔데 : " + mainCategoryId); - List result = popularPostService.getPopularPosts(limit, mainCategoryId); - - return ResponseEntity.ok(result); - } - - -} - +// +//package com.example.booktree.popularpost.controller; +// +//import com.example.booktree.post.dto.response.PostResponseDto; +//import com.example.booktree.post.entity.Post; +//import com.example.booktree.popularpost.service.PopularPostService; +//import io.swagger.v3.oas.annotations.Operation; +//import io.swagger.v3.oas.annotations.tags.Tag; +//import lombok.RequiredArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.http.ResponseEntity; +//import org.springframework.web.bind.annotation.*; +// +//import java.util.List; +// +//@RestController +//@RequiredArgsConstructor +//@RequestMapping("/api/v1/popular") +//@Slf4j +//@Tag(name = "실시간 인기 게시글 관리 컨트롤러") +//public class PopularPostController { +// +// private final PopularPostService popularPostService; +// +// // 실시간 인기 게시글 목록 가져오기 +// +// @GetMapping("/get/posts/{mainCategoryId}") +// @Operation( +// summary = "실시간 조회수 높은 게시글 불러오기 기능", +// description = "Redis를 통한 실시간 조회수가 높은 게시글을 불러오는 기능", +// tags = "실시간 인기 게시글 관리 컨트롤러" +// ) +// public ResponseEntity> getPopularPosts( +// @RequestParam(defaultValue = "6") int limit, @PathVariable Long mainCategoryId) { +// +// log.info("뭔데 : " + mainCategoryId); +// List result = popularPostService.getPopularPosts(limit, mainCategoryId); +// +// return ResponseEntity.ok(result); +// } +// +// +//} +// diff --git a/backend/src/main/java/com/example/booktree/popularpost/service/PopularPostService.java b/backend/src/main/java/com/example/booktree/popularpost/service/PopularPostService.java index 48a68ab2..ae680c77 100644 --- a/backend/src/main/java/com/example/booktree/popularpost/service/PopularPostService.java +++ b/backend/src/main/java/com/example/booktree/popularpost/service/PopularPostService.java @@ -1,109 +1,109 @@ - -package com.example.booktree.popularpost.service; - -import com.example.booktree.exception.BusinessLogicException; -import com.example.booktree.exception.ExceptionCode; -import com.example.booktree.post.dto.response.PostResponseDto; -import com.example.booktree.post.entity.Post; -import com.example.booktree.post.repository.PostRepository; -import com.example.booktree.post.service.PostService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.data.redis.core.ZSetOperations; -import org.springframework.stereotype.Service; - -import java.security.Key; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -@Slf4j -public class PopularPostService { - - private final StringRedisTemplate redisTemplate; - private final PostService postService; - private static final String REDIS_KEY = "popular:posts:"; - private final String defaultImageUrl = "https://booktree-s3-bucket.s3.ap-northeast-2.amazonaws.com/BookTree+%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB+%E1%84%8B%E1%85%B5%E1%84%86%E1%85%B5%E1%84%8C%E1%85%B5+%E1%84%8E%E1%85%AC%E1%84%8C%E1%85%A9%E1%86%BC%E1%84%87%E1%85%A9%E1%86%AB.png"; - - - // 메인 카테고리 별 게시글 조회 시 인기순위에 반영 - public void increasePopularity(Long postId, Long mainCategoryId) { - String key = getMonthlyKey(mainCategoryId); - redisTemplate.opsForZSet().incrementScore(key, postId.toString(), 1); - } - - - // 메인 카테고리 별 인기 게시글 TOP N 조회 -// 메인 카테고리 별 인기 게시글 TOP N 조회 - public List getPopularPosts(int limit, Long mainCategoryId) { - String key = getMonthlyKey(mainCategoryId); - - Set> postScores = redisTemplate.opsForZSet() - .reverseRangeWithScores(key, 0, limit - 1); - - log.info("postScore : " + postScores.toString()); - - - if (postScores == null || postScores.isEmpty()) { - throw new BusinessLogicException(ExceptionCode.VIEW_NOT_FOUND); - } - - // Redis에서 가져온 postId와 score를 기반으로 DB에서 조회 - List ids = new ArrayList<>(); - - List scores = new ArrayList<>(); // score를 저장할 리스트 추가 - - for (ZSetOperations.TypedTuple tuple : postScores) { - String postId = tuple.getValue(); - Double score = tuple.getScore(); - log.info("Post ID: {}, Score: {}", postId, score); // score 출력 - ids.add(Long.parseLong(postId)); - - - scores.add(score); // score 리스트에 추가 - - } - - // 순서 보장을 안해줌 - List posts = postService.findAllById(ids); - - // 순서 보장을 위해 Redis에 있던 순서대로 정렬 - Map postMap = posts.stream() - .collect(Collectors.toMap(Post::getId, p -> p)); - - // PostResponseDto 리스트 생성 (score 포함) - List response = ids.stream() - .map(id -> { - Post post = postMap.get(id); - double score = scores.get(ids.indexOf(id)); // score 가져오기 - return PostResponseDto.builder() - .postId(post.getId()) - .title(post.getTitle()) - .imageUrl(post.getImageList().isEmpty() ? defaultImageUrl : post.getImageList().get(0).getImageUrl()) - .viewCount(post.getView()) - .score(score) // score 설정 - .createdAt(post.getCreatedAt()) - .modifiedAt(post.getModifiedAt()) - .build(); - }) - .collect(Collectors.toList()); - - return response; // PostResponseDto 리스트 반환 - } - - - // 현재 날짜에 따라 월별 키 생성 - private String getMonthlyKey(Long mainCategoryId) { - LocalDate now = LocalDate.now(); - String monthKey = now.format(DateTimeFormatter.ofPattern("yyyy-MM")); - return REDIS_KEY + mainCategoryId + ":month:" + monthKey; - } -} - +// +//package com.example.booktree.popularpost.service; +// +//import com.example.booktree.exception.BusinessLogicException; +//import com.example.booktree.exception.ExceptionCode; +//import com.example.booktree.post.dto.response.PostResponseDto; +//import com.example.booktree.post.entity.Post; +//import com.example.booktree.post.repository.PostRepository; +//import com.example.booktree.post.service.PostService; +//import lombok.RequiredArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.data.redis.core.StringRedisTemplate; +//import org.springframework.data.redis.core.ZSetOperations; +//import org.springframework.stereotype.Service; +// +//import java.security.Key; +//import java.time.LocalDate; +//import java.time.format.DateTimeFormatter; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.Map; +//import java.util.Set; +//import java.util.stream.Collectors; +// +//@Service +//@RequiredArgsConstructor +//@Slf4j +//public class PopularPostService { +// +// private final StringRedisTemplate redisTemplate; +// private final PostService postService; +// private static final String REDIS_KEY = "popular:posts:"; +// private final String defaultImageUrl = "https://booktree-s3-bucket.s3.ap-northeast-2.amazonaws.com/BookTree+%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB+%E1%84%8B%E1%85%B5%E1%84%86%E1%85%B5%E1%84%8C%E1%85%B5+%E1%84%8E%E1%85%AC%E1%84%8C%E1%85%A9%E1%86%BC%E1%84%87%E1%85%A9%E1%86%AB.png"; +// +// +// // 메인 카테고리 별 게시글 조회 시 인기순위에 반영 +// public void increasePopularity(Long postId, Long mainCategoryId) { +// String key = getMonthlyKey(mainCategoryId); +// redisTemplate.opsForZSet().incrementScore(key, postId.toString(), 1); +// } +// +// +// // 메인 카테고리 별 인기 게시글 TOP N 조회 +//// 메인 카테고리 별 인기 게시글 TOP N 조회 +// public List getPopularPosts(int limit, Long mainCategoryId) { +// String key = getMonthlyKey(mainCategoryId); +// +// Set> postScores = redisTemplate.opsForZSet() +// .reverseRangeWithScores(key, 0, limit - 1); +// +// log.info("postScore : " + postScores.toString()); +// +// +// if (postScores == null || postScores.isEmpty()) { +// throw new BusinessLogicException(ExceptionCode.VIEW_NOT_FOUND); +// } +// +// // Redis에서 가져온 postId와 score를 기반으로 DB에서 조회 +// List ids = new ArrayList<>(); +// +// List scores = new ArrayList<>(); // score를 저장할 리스트 추가 +// +// for (ZSetOperations.TypedTuple tuple : postScores) { +// String postId = tuple.getValue(); +// Double score = tuple.getScore(); +// log.info("Post ID: {}, Score: {}", postId, score); // score 출력 +// ids.add(Long.parseLong(postId)); +// +// +// scores.add(score); // score 리스트에 추가 +// +// } +// +// // 순서 보장을 안해줌 +// List posts = postService.findAllById(ids); +// +// // 순서 보장을 위해 Redis에 있던 순서대로 정렬 +// Map postMap = posts.stream() +// .collect(Collectors.toMap(Post::getId, p -> p)); +// +// // PostResponseDto 리스트 생성 (score 포함) +// List response = ids.stream() +// .map(id -> { +// Post post = postMap.get(id); +// double score = scores.get(ids.indexOf(id)); // score 가져오기 +// return PostResponseDto.builder() +// .postId(post.getId()) +// .title(post.getTitle()) +// .imageUrl(post.getImageList().isEmpty() ? defaultImageUrl : post.getImageList().get(0).getImageUrl()) +// .viewCount(post.getView()) +// .score(score) // score 설정 +// .createdAt(post.getCreatedAt()) +// .modifiedAt(post.getModifiedAt()) +// .build(); +// }) +// .collect(Collectors.toList()); +// +// return response; // PostResponseDto 리스트 반환 +// } +// +// +// // 현재 날짜에 따라 월별 키 생성 +// private String getMonthlyKey(Long mainCategoryId) { +// LocalDate now = LocalDate.now(); +// String monthKey = now.format(DateTimeFormatter.ofPattern("yyyy-MM")); +// return REDIS_KEY + mainCategoryId + ":month:" + monthKey; +// } +//} +// diff --git a/backend/src/main/java/com/example/booktree/post/controller/PostController.java b/backend/src/main/java/com/example/booktree/post/controller/PostController.java index b6b8b124..1c0cb50a 100644 --- a/backend/src/main/java/com/example/booktree/post/controller/PostController.java +++ b/backend/src/main/java/com/example/booktree/post/controller/PostController.java @@ -4,7 +4,7 @@ import com.example.booktree.exception.ExceptionCode; //import com.example.booktree.popularpost.service.PopularPostService; //import com.example.booktree.popularpost.service.PopularPostService; -import com.example.booktree.popularpost.service.PopularPostService; +//import com.example.booktree.popularpost.service.PopularPostService; import com.example.booktree.post.dto.request.PostRequestDto; import com.example.booktree.post.dto.response.*; @@ -40,7 +40,7 @@ public class PostController { private final PostService postService; - private final PopularPostService popularPostService; + //private final PopularPostService popularPostService; private final String defaultImageUrl = "https://booktree-s3-bucket.s3.ap-northeast-2.amazonaws.com/BookTree+%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB+%E1%84%8B%E1%85%B5%E1%84%86%E1%85%B5%E1%84%8C%E1%85%B5+%E1%84%8E%E1%85%AC%E1%84%8C%E1%85%A9%E1%86%BC%E1%84%87%E1%85%A9%E1%86%AB.png"; From cf5ca45bcbc1075097d3b89b017f581b156f5f22 Mon Sep 17 00:00:00 2001 From: bagsunyoung Date: Thu, 1 May 2025 02:22:59 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[=20fix=20]=20:=20#427=20=EB=B8=94=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=EC=97=90=20=EC=B2=A8?= =?UTF-8?q?=EB=B6=80=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=8D=B8=EB=84=A4?= =?UTF-8?q?=EC=9D=BC=EB=A1=9C=20=EC=97=B0=EA=B2=B0=EB=90=98=EA=B2=8C=20?= =?UTF-8?q?=EB=B0=B1=EC=97=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/booktree/post/dto/response/PostResponseDto.java | 5 +++++ .../java/com/example/booktree/post/service/PostService.java | 2 ++ 2 files changed, 7 insertions(+) diff --git a/backend/src/main/java/com/example/booktree/post/dto/response/PostResponseDto.java b/backend/src/main/java/com/example/booktree/post/dto/response/PostResponseDto.java index a0406241..c182bc18 100644 --- a/backend/src/main/java/com/example/booktree/post/dto/response/PostResponseDto.java +++ b/backend/src/main/java/com/example/booktree/post/dto/response/PostResponseDto.java @@ -20,4 +20,9 @@ public class PostResponseDto { private String imageUrl; private double score; + private String content; // 추가 + private String username; // 추가 (user.getNickname() 또는 user.getName()) + private Long categoryId; // 추가 + private String category; // 추가 (category.getName()) + } diff --git a/backend/src/main/java/com/example/booktree/post/service/PostService.java b/backend/src/main/java/com/example/booktree/post/service/PostService.java index d6469004..23fac3c1 100644 --- a/backend/src/main/java/com/example/booktree/post/service/PostService.java +++ b/backend/src/main/java/com/example/booktree/post/service/PostService.java @@ -501,6 +501,7 @@ public Page getPagedPostsByBlog(Long blogId, int page, int size .viewCount(post.getView()) .createdAt(post.getCreatedAt()) .modifiedAt(post.getModifiedAt()) + .imageUrl(post.getImageList().isEmpty() ? null : post.getImageList().get(0).getImageUrl()) // 추가! .build()); } @@ -550,6 +551,7 @@ public Page getPopularPostsByBlog(Long blogId, int page, int si .viewCount(post.getView()) .createdAt(post.getCreatedAt()) .modifiedAt(post.getModifiedAt()) + .imageUrl(post.getImageList().isEmpty() ? null : post.getImageList().get(0).getImageUrl()) // 추가 .build()); }