From 0329513edd9caab024340d6605b9c8df7e7f7c29 Mon Sep 17 00:00:00 2001 From: mangsuyo Date: Sun, 7 Sep 2025 12:31:58 +0900 Subject: [PATCH 1/5] =?UTF-8?q?refactor:=20=EB=8B=A8=EC=9D=BC=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=201=20+=20N=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20(#367)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../buddyya/feed/repository/FeedRepository.java | 10 ++++++++++ .../team/buddyya/feed/service/FeedService.java | 17 +++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/team/buddyya/feed/repository/FeedRepository.java b/src/main/java/com/team/buddyya/feed/repository/FeedRepository.java index 28891d92..e79c974d 100644 --- a/src/main/java/com/team/buddyya/feed/repository/FeedRepository.java +++ b/src/main/java/com/team/buddyya/feed/repository/FeedRepository.java @@ -5,14 +5,24 @@ import com.team.buddyya.student.domain.Student; import com.team.buddyya.student.domain.University; import java.util.List; +import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface FeedRepository extends JpaRepository { + @Override + @EntityGraph(attributePaths = { + "category", + "university", + "images" + }) + Optional findById(Long id); + Page findAllByUniversityAndCategory(University university, Category category, Pageable pageable); Page findAllByStudent(Student student, Pageable pageable); diff --git a/src/main/java/com/team/buddyya/feed/service/FeedService.java b/src/main/java/com/team/buddyya/feed/service/FeedService.java index 771dd3cf..551b9eeb 100644 --- a/src/main/java/com/team/buddyya/feed/service/FeedService.java +++ b/src/main/java/com/team/buddyya/feed/service/FeedService.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Set; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -37,6 +38,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +@Slf4j @Service @Transactional @RequiredArgsConstructor @@ -155,9 +157,20 @@ FeedUserAction getUserAction(Student student, Feed feed) { } public FeedResponse getFeed(StudentInfo studentInfo, Long feedId) { + log.info("========== [DEBUG] START: getFeed (Feed ID: {}) ==========", feedId); + + log.info("--- [DEBUG] 1. findFeedByFeedId 호출 ---"); Feed feed = findFeedByFeedId(feedId); - feed.increaseViewCount(); - return createFeedResponse(feed, studentInfo.id()); + log.info("--- [DEBUG] 1. findFeedByFeedId 완료 ---"); + + feed.increaseViewCount(); // Dirty Checking으로 인해 트랜잭션 커밋 시 UPDATE 쿼리 발생 + + log.info("--- [DEBUG] 2. createFeedResponse 호출 시작 (추가 쿼리 발생 구간) ---"); + FeedResponse response = createFeedResponse(feed, studentInfo.id()); + log.info("--- [DEBUG] 2. createFeedResponse 호출 완료 ---"); + + log.info("========== [DEBUG] END: getFeed (Feed ID: {}) ==========", feedId); + return response; } public void createFeed(StudentInfo studentInfo, FeedCreateRequest request) { From e875c065b3c38dd12235456f8e86db30f8fc661f Mon Sep 17 00:00:00 2001 From: mangsuyo Date: Sun, 7 Sep 2025 13:19:58 +0900 Subject: [PATCH 2/5] =?UTF-8?q?refactor:=20OneToMany=20=EA=B4=80=EA=B3=84?= =?UTF-8?q?=20BatchSize=20=EC=9D=B4=EC=9A=A9=20N=20+=201=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20(#367)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/team/buddyya/feed/domain/Feed.java | 2 ++ .../java/com/team/buddyya/feed/repository/FeedRepository.java | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/com/team/buddyya/feed/domain/Feed.java b/src/main/java/com/team/buddyya/feed/domain/Feed.java index f273bbd1..aca157b3 100644 --- a/src/main/java/com/team/buddyya/feed/domain/Feed.java +++ b/src/main/java/com/team/buddyya/feed/domain/Feed.java @@ -21,6 +21,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.BatchSize; @Entity @Table(name = "feed") @@ -68,6 +69,7 @@ public class Feed extends BaseTime { @OneToMany(mappedBy = "feed", cascade = CascadeType.REMOVE, orphanRemoval = true) private List comments = new ArrayList<>(); + @BatchSize(size = 100) @OneToMany(mappedBy = "feed", cascade = CascadeType.REMOVE, orphanRemoval = true) private List images = new ArrayList<>(); diff --git a/src/main/java/com/team/buddyya/feed/repository/FeedRepository.java b/src/main/java/com/team/buddyya/feed/repository/FeedRepository.java index e79c974d..2d2e3ea4 100644 --- a/src/main/java/com/team/buddyya/feed/repository/FeedRepository.java +++ b/src/main/java/com/team/buddyya/feed/repository/FeedRepository.java @@ -23,6 +23,10 @@ public interface FeedRepository extends JpaRepository { }) Optional findById(Long id); + @EntityGraph(attributePaths = { + "category", + "university", + }) Page findAllByUniversityAndCategory(University university, Category category, Pageable pageable); Page findAllByStudent(Student student, Pageable pageable); From e9069ac0b63747b1245f678d589f00b683be0477 Mon Sep 17 00:00:00 2001 From: mangsuyo Date: Sun, 7 Sep 2025 14:37:09 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refactor:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EB=B6=81=EB=A7=88=ED=81=AC=20=EB=B2=8C=ED=81=AC=20=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20N=20+=201=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0=20(#367)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/team/buddyya/feed/domain/Feed.java | 1 + .../feed/repository/BookmarkRepository.java | 8 ++ .../feed/repository/FeedLikeRepository.java | 8 ++ .../buddyya/feed/service/FeedService.java | 74 +++++++++++-------- 4 files changed, 61 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/team/buddyya/feed/domain/Feed.java b/src/main/java/com/team/buddyya/feed/domain/Feed.java index aca157b3..da0e4f52 100644 --- a/src/main/java/com/team/buddyya/feed/domain/Feed.java +++ b/src/main/java/com/team/buddyya/feed/domain/Feed.java @@ -54,6 +54,7 @@ public class Feed extends BaseTime { @Column(name = "pinned", nullable = false) private boolean pinned; + @BatchSize(size = 100) @ManyToOne(fetch = LAZY) @JoinColumn(name = "student_id", nullable = false) private Student student; diff --git a/src/main/java/com/team/buddyya/feed/repository/BookmarkRepository.java b/src/main/java/com/team/buddyya/feed/repository/BookmarkRepository.java index 343dd0e5..aa86793b 100644 --- a/src/main/java/com/team/buddyya/feed/repository/BookmarkRepository.java +++ b/src/main/java/com/team/buddyya/feed/repository/BookmarkRepository.java @@ -3,10 +3,14 @@ import com.team.buddyya.feed.domain.Bookmark; import com.team.buddyya.feed.domain.Feed; import com.team.buddyya.student.domain.Student; +import java.util.List; import java.util.Optional; +import java.util.Set; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository @@ -17,4 +21,8 @@ public interface BookmarkRepository extends JpaRepository { boolean existsByStudentAndFeed(Student student, Feed feed); Page findAllByStudent(Student student, Pageable pageable); + + @Query("SELECT b.feed.id FROM Bookmark b WHERE b.student.id = :studentId AND b.feed.id IN :feedIds") + Set findFeedIdsByStudentIdAndFeedIdsIn(@Param("studentId") Long studentId, + @Param("feedIds") List feedIds); } diff --git a/src/main/java/com/team/buddyya/feed/repository/FeedLikeRepository.java b/src/main/java/com/team/buddyya/feed/repository/FeedLikeRepository.java index 63005a57..bbd18729 100644 --- a/src/main/java/com/team/buddyya/feed/repository/FeedLikeRepository.java +++ b/src/main/java/com/team/buddyya/feed/repository/FeedLikeRepository.java @@ -3,8 +3,12 @@ import com.team.buddyya.feed.domain.Feed; import com.team.buddyya.feed.domain.FeedLike; import com.team.buddyya.student.domain.Student; +import java.util.List; import java.util.Optional; +import java.util.Set; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository @@ -13,4 +17,8 @@ public interface FeedLikeRepository extends JpaRepository { Optional findByStudentAndFeed(Student student, Feed feed); boolean existsByStudentAndFeed(Student student, Feed feed); + + @Query("SELECT fl.feed.id FROM FeedLike fl WHERE fl.student.id = :studentId AND fl.feed.id IN :feedIds") + Set findFeedIdsByStudentIdAndFeedIdsIn(@Param("studentId") Long studentId, + @Param("feedIds") List feedIds); } diff --git a/src/main/java/com/team/buddyya/feed/service/FeedService.java b/src/main/java/com/team/buddyya/feed/service/FeedService.java index 551b9eeb..e83fdd7c 100644 --- a/src/main/java/com/team/buddyya/feed/service/FeedService.java +++ b/src/main/java/com/team/buddyya/feed/service/FeedService.java @@ -75,13 +75,18 @@ protected University findUniversityByUniversityName(String universityName) { @Transactional(readOnly = true) public FeedListResponse getFeeds(StudentInfo studentInfo, Pageable pageable, FeedListRequest request) { - String keyword = request.keyword(); Student student = findStudentByStudentId(studentInfo.id()); - Page feeds = (keyword == null || keyword.isBlank()) + Page feeds = (request.keyword() == null || request.keyword().isBlank()) ? getFeedsByUniversityAndCategory(request, pageable) - : getFeedsByKeyword(student, keyword, pageable); + : getFeedsByKeyword(student, request.keyword(), pageable); Set blockedStudentIds = blockRepository.findBlockedStudentIdByBlockerId(studentInfo.id()); - List response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, studentInfo.id()); + List feedIds = feeds.getContent().stream() + .map(Feed::getId) + .toList(); + Set likedFeedIds = feedLikeRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); + Set bookmarkedFeedIds = bookmarkRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); + List response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, student, likedFeedIds, + bookmarkedFeedIds); return FeedListResponse.from(response, feeds); } @@ -91,15 +96,18 @@ public FeedListResponse getPopularFeeds( Pageable pageable, FeedListRequest request ) { + Student student = findStudentByStudentId(studentInfo.id()); University university = findUniversityByUniversityName(request.university()); - Page feeds = feedRepository - .findByLikeCountGreaterThanEqualAndUniversity( - LIKE_COUNT_THRESHOLD, - university, - pageable - ); + Page feeds = feedRepository.findByLikeCountGreaterThanEqualAndUniversity(LIKE_COUNT_THRESHOLD, university, + pageable); Set blocked = blockRepository.findBlockedStudentIdByBlockerId(studentInfo.id()); - List response = filterBlockedFeeds(feeds.getContent(), blocked, studentInfo.id()); + List feedIds = feeds.getContent().stream() + .map(Feed::getId) + .toList(); + Set likedFeedIds = feedLikeRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); + Set bookmarkedFeedIds = bookmarkRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); + List response = filterBlockedFeeds(feeds.getContent(), blocked, student, likedFeedIds, + bookmarkedFeedIds); return FeedListResponse.from(response, feeds); } @@ -125,7 +133,7 @@ public FeedListResponse getMyFeed(StudentInfo studentInfo, Pageable pageable) { Student student = findStudentByStudentId(studentInfo.id()); Page feeds = feedRepository.findAllByStudent(student, customPageable); List response = feeds.getContent().stream() - .map(feed -> createFeedResponse(feed, studentInfo.id())) + .map(feed -> createFeedResponse(feed, student)) .toList(); return FeedListResponse.from(response, feeds); } @@ -136,7 +144,15 @@ public FeedListResponse getBookmarkFeed(StudentInfo studentInfo, Pageable pageab Page bookmarks = bookmarkRepository.findAllByStudent(student, pageable); Page feeds = bookmarks.map(Bookmark::getFeed); Set blockedStudentIds = blockRepository.findBlockedStudentIdByBlockerId(studentInfo.id()); - List response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, studentInfo.id()); + List feedIds = feeds.getContent().stream() + .map(Feed::getId) + .toList(); + + Set likedFeedIds = feedLikeRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); + Set bookmarkedFeedIds = bookmarkRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); + + List response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, student, likedFeedIds, + bookmarkedFeedIds); return FeedListResponse.from(response, feeds); } @@ -157,19 +173,10 @@ FeedUserAction getUserAction(Student student, Feed feed) { } public FeedResponse getFeed(StudentInfo studentInfo, Long feedId) { - log.info("========== [DEBUG] START: getFeed (Feed ID: {}) ==========", feedId); - - log.info("--- [DEBUG] 1. findFeedByFeedId 호출 ---"); + Student student = findStudentByStudentId(studentInfo.id()); Feed feed = findFeedByFeedId(feedId); - log.info("--- [DEBUG] 1. findFeedByFeedId 완료 ---"); - - feed.increaseViewCount(); // Dirty Checking으로 인해 트랜잭션 커밋 시 UPDATE 쿼리 발생 - - log.info("--- [DEBUG] 2. createFeedResponse 호출 시작 (추가 쿼리 발생 구간) ---"); - FeedResponse response = createFeedResponse(feed, studentInfo.id()); - log.info("--- [DEBUG] 2. createFeedResponse 호출 완료 ---"); - - log.info("========== [DEBUG] END: getFeed (Feed ID: {}) ==========", feedId); + feed.increaseViewCount(); + FeedResponse response = createFeedResponse(feed, student); return response; } @@ -217,22 +224,29 @@ public void togglePin(StudentInfo studentInfo, Long feedId) { } private List filterBlockedFeeds(List feeds, Set blockedStudentIds, - Long currentStudentId) { + Student currentStudent, Set likedFeedIds, + Set bookmarkedFeedIds) { return feeds.stream() .filter(feed -> !blockedStudentIds.contains(feed.getStudent().getId())) - .map(feed -> createFeedResponse(feed, currentStudentId)) + .map(feed -> { + boolean isFeedOwner = feed.isFeedOwner(currentStudent.getId()); + boolean isLiked = likedFeedIds.contains(feed.getId()); + boolean isBookmarked = bookmarkedFeedIds.contains(feed.getId()); + FeedUserAction userAction = FeedUserAction.from(isFeedOwner, isLiked, isBookmarked); + return FeedResponse.from(feed, userAction); + }) .toList(); } + private void validateFeedOwner(StudentInfo studentInfo, Feed feed) { if (!studentInfo.id().equals(feed.getStudent().getId()) && !(studentInfo.role() == Role.OWNER)) { throw new FeedException(FeedExceptionType.NOT_FEED_OWNER); } } - private FeedResponse createFeedResponse(Feed feed, Long studentId) { - Student student = findStudentByStudentId(studentId); - FeedUserAction userAction = getUserAction(student, feed); + private FeedResponse createFeedResponse(Feed feed, Student currentStudent) { + FeedUserAction userAction = getUserAction(currentStudent, feed); return FeedResponse.from(feed, userAction); } From 90c407fc53c00c8b08afaac113590376192aec5a Mon Sep 17 00:00:00 2001 From: mangsuyo Date: Tue, 9 Sep 2025 16:58:03 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20=ED=94=BC=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=EC=9E=90=20DTO=20Projection=EC=9D=84=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20Select=20(#367)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/dto/projection/FeedAuthorInfo.java | 15 ++++++ .../feed/dto/response/feed/FeedResponse.java | 19 +++---- .../buddyya/feed/service/FeedService.java | 51 +++++++++++++++---- .../student/repository/StudentRepository.java | 14 ++++- 4 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/team/buddyya/feed/dto/projection/FeedAuthorInfo.java diff --git a/src/main/java/com/team/buddyya/feed/dto/projection/FeedAuthorInfo.java b/src/main/java/com/team/buddyya/feed/dto/projection/FeedAuthorInfo.java new file mode 100644 index 00000000..e732b382 --- /dev/null +++ b/src/main/java/com/team/buddyya/feed/dto/projection/FeedAuthorInfo.java @@ -0,0 +1,15 @@ +package com.team.buddyya.feed.dto.projection; + +import com.team.buddyya.student.domain.Role; + +public record FeedAuthorInfo( + Long id, + String name, + String country, + Role role, + String characterProfileImage, + boolean isCertificated, + boolean isDeleted, + String universityName +) { +} diff --git a/src/main/java/com/team/buddyya/feed/dto/response/feed/FeedResponse.java b/src/main/java/com/team/buddyya/feed/dto/response/feed/FeedResponse.java index 980f2f1d..db24efdd 100644 --- a/src/main/java/com/team/buddyya/feed/dto/response/feed/FeedResponse.java +++ b/src/main/java/com/team/buddyya/feed/dto/response/feed/FeedResponse.java @@ -3,6 +3,7 @@ import com.team.buddyya.feed.domain.Feed; import com.team.buddyya.feed.domain.FeedImage; import com.team.buddyya.feed.domain.FeedUserAction; +import com.team.buddyya.feed.dto.projection.FeedAuthorInfo; import com.team.buddyya.student.domain.Role; import java.time.LocalDateTime; import java.util.List; @@ -35,19 +36,19 @@ public record FeedResponse( private static final String BUDDYYA_PROFILE_IMAGE = "https://buddyya.s3.ap-northeast-2.amazonaws.com/default-profile-image/buddyya_icon.png"; - public static FeedResponse from(Feed feed, FeedUserAction userAction) { - String profileImageUrl = feed.getStudent().getRole() == Role.OWNER ? BUDDYYA_PROFILE_IMAGE - : feed.getStudent().getCharacterProfileImage(); + public static FeedResponse from(Feed feed, FeedUserAction userAction, FeedAuthorInfo authorInfo) { + String profileImageUrl = authorInfo.role() == Role.OWNER ? BUDDYYA_PROFILE_IMAGE + : authorInfo.characterProfileImage(); return new FeedResponse( feed.getId(), - feed.getStudent().getId(), + authorInfo.id(), feed.getUniversity().getUniversityName(), feed.getCategory().getName(), - feed.getStudent().getName(), - feed.getStudent().getCountry(), + authorInfo.name(), + authorInfo.country(), feed.getTitle(), feed.getContent(), - feed.getStudent().getUniversity().getUniversityName(), + authorInfo.universityName(), profileImageUrl, feed.getImages().stream() .map(FeedImage::getUrl) @@ -59,9 +60,9 @@ public static FeedResponse from(Feed feed, FeedUserAction userAction) { userAction.isLiked(), userAction.isBookmarked(), feed.isPinned(), - feed.getStudent().getIsCertificated(), + authorInfo.isCertificated(), feed.isProfileVisible(), - feed.getStudent().getIsDeleted(), + authorInfo.isDeleted(), feed.getCreatedDate() ); } diff --git a/src/main/java/com/team/buddyya/feed/service/FeedService.java b/src/main/java/com/team/buddyya/feed/service/FeedService.java index e83fdd7c..9a7aafb8 100644 --- a/src/main/java/com/team/buddyya/feed/service/FeedService.java +++ b/src/main/java/com/team/buddyya/feed/service/FeedService.java @@ -6,6 +6,7 @@ import com.team.buddyya.feed.domain.Feed; import com.team.buddyya.feed.domain.FeedImage; import com.team.buddyya.feed.domain.FeedUserAction; +import com.team.buddyya.feed.dto.projection.FeedAuthorInfo; import com.team.buddyya.feed.dto.request.feed.FeedCreateRequest; import com.team.buddyya.feed.dto.request.feed.FeedListRequest; import com.team.buddyya.feed.dto.request.feed.FeedUpdateRequest; @@ -24,10 +25,14 @@ import com.team.buddyya.student.exception.StudentException; import com.team.buddyya.student.exception.StudentExceptionType; import com.team.buddyya.student.repository.BlockRepository; +import com.team.buddyya.student.repository.StudentRepository; import com.team.buddyya.student.repository.UniversityRepository; import com.team.buddyya.student.service.FindStudentService; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -55,6 +60,7 @@ public class FeedService { private final BlockRepository blockRepository; private final UniversityRepository universityRepository; private final ReportRepository reportRepository; + private final StudentRepository studentRepository; @Transactional(readOnly = true) protected Feed findFeedByFeedId(Long feedId) { @@ -79,14 +85,16 @@ public FeedListResponse getFeeds(StudentInfo studentInfo, Pageable pageable, Fee Page feeds = (request.keyword() == null || request.keyword().isBlank()) ? getFeedsByUniversityAndCategory(request, pageable) : getFeedsByKeyword(student, request.keyword(), pageable); + Set blockedStudentIds = blockRepository.findBlockedStudentIdByBlockerId(studentInfo.id()); List feedIds = feeds.getContent().stream() .map(Feed::getId) .toList(); Set likedFeedIds = feedLikeRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); Set bookmarkedFeedIds = bookmarkRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); + Map authorInfoMap = getFeedAuthorInfoMap(feeds.getContent()); List response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, student, likedFeedIds, - bookmarkedFeedIds); + bookmarkedFeedIds, authorInfoMap); return FeedListResponse.from(response, feeds); } @@ -100,14 +108,15 @@ public FeedListResponse getPopularFeeds( University university = findUniversityByUniversityName(request.university()); Page feeds = feedRepository.findByLikeCountGreaterThanEqualAndUniversity(LIKE_COUNT_THRESHOLD, university, pageable); - Set blocked = blockRepository.findBlockedStudentIdByBlockerId(studentInfo.id()); + Set blockedStudentIds = blockRepository.findBlockedStudentIdByBlockerId(studentInfo.id()); List feedIds = feeds.getContent().stream() .map(Feed::getId) .toList(); Set likedFeedIds = feedLikeRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); Set bookmarkedFeedIds = bookmarkRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); - List response = filterBlockedFeeds(feeds.getContent(), blocked, student, likedFeedIds, - bookmarkedFeedIds); + Map authorInfoMap = getFeedAuthorInfoMap(feeds.getContent()); + List response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, student, likedFeedIds, + bookmarkedFeedIds, authorInfoMap); return FeedListResponse.from(response, feeds); } @@ -150,9 +159,9 @@ public FeedListResponse getBookmarkFeed(StudentInfo studentInfo, Pageable pageab Set likedFeedIds = feedLikeRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); Set bookmarkedFeedIds = bookmarkRepository.findFeedIdsByStudentIdAndFeedIdsIn(studentInfo.id(), feedIds); - + Map authorInfoMap = getFeedAuthorInfoMap(feeds.getContent()); List response = filterBlockedFeeds(feeds.getContent(), blockedStudentIds, student, likedFeedIds, - bookmarkedFeedIds); + bookmarkedFeedIds, authorInfoMap); return FeedListResponse.from(response, feeds); } @@ -225,7 +234,8 @@ public void togglePin(StudentInfo studentInfo, Long feedId) { private List filterBlockedFeeds(List feeds, Set blockedStudentIds, Student currentStudent, Set likedFeedIds, - Set bookmarkedFeedIds) { + Set bookmarkedFeedIds, + Map authorInfoMap) { return feeds.stream() .filter(feed -> !blockedStudentIds.contains(feed.getStudent().getId())) .map(feed -> { @@ -233,11 +243,23 @@ private List filterBlockedFeeds(List feeds, Set blocke boolean isLiked = likedFeedIds.contains(feed.getId()); boolean isBookmarked = bookmarkedFeedIds.contains(feed.getId()); FeedUserAction userAction = FeedUserAction.from(isFeedOwner, isLiked, isBookmarked); - return FeedResponse.from(feed, userAction); + FeedAuthorInfo authorInfo = authorInfoMap.get(feed.getStudent().getId()); + return FeedResponse.from(feed, userAction, authorInfo); }) .toList(); } + private Map getFeedAuthorInfoMap(List feeds) { + Set studentIds = feeds.stream() + .map(feed -> feed.getStudent().getId()) + .collect(Collectors.toSet()); + if (studentIds.isEmpty()) { + return Map.of(); + } + return studentRepository.findAuthorInfoByIdsIn(studentIds).stream() + .collect(Collectors.toMap(FeedAuthorInfo::id, Function.identity())); + } + private void validateFeedOwner(StudentInfo studentInfo, Feed feed) { if (!studentInfo.id().equals(feed.getStudent().getId()) && !(studentInfo.role() == Role.OWNER)) { @@ -247,7 +269,18 @@ private void validateFeedOwner(StudentInfo studentInfo, Feed feed) { private FeedResponse createFeedResponse(Feed feed, Student currentStudent) { FeedUserAction userAction = getUserAction(currentStudent, feed); - return FeedResponse.from(feed, userAction); + Student authorStudent = feed.getStudent(); + FeedAuthorInfo authorInfo = new FeedAuthorInfo( + authorStudent.getId(), + authorStudent.getName(), + authorStudent.getCountry(), + authorStudent.getRole(), + authorStudent.getCharacterProfileImage(), + authorStudent.getIsCertificated(), + authorStudent.getIsDeleted(), + authorStudent.getUniversity().getUniversityName() + ); + return FeedResponse.from(feed, userAction, authorInfo); } private void updateImages(Feed feed, List images) { diff --git a/src/main/java/com/team/buddyya/student/repository/StudentRepository.java b/src/main/java/com/team/buddyya/student/repository/StudentRepository.java index deccc370..7924c601 100644 --- a/src/main/java/com/team/buddyya/student/repository/StudentRepository.java +++ b/src/main/java/com/team/buddyya/student/repository/StudentRepository.java @@ -1,13 +1,23 @@ package com.team.buddyya.student.repository; +import com.team.buddyya.feed.dto.projection.FeedAuthorInfo; import com.team.buddyya.student.domain.Student; -import org.springframework.data.jpa.repository.JpaRepository; - +import java.util.List; import java.util.Optional; +import java.util.Set; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface StudentRepository extends JpaRepository { Optional findByPhoneNumber(String phoneNumber); Optional findByEmail(String email); + + @Query("SELECT new com.team.buddyya.feed.dto.projection.FeedAuthorInfo(" + + "s.id, s.name, s.country, s.role, s.characterProfileImage, s.isCertificated, s.isDeleted, u.universityName) " + + + "FROM Student s JOIN s.university u WHERE s.id IN :studentIds") + List findAuthorInfoByIdsIn(@Param("studentIds") Set studentIds); } From c451bfd2e156546c8415ba37dacd3d33ad45cef0 Mon Sep 17 00:00:00 2001 From: mangsuyo Date: Wed, 10 Sep 2025 18:18:17 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20=ED=95=99=EC=83=9D,=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=20=EB=8D=94=EB=AF=B8=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=BF=BC=EB=A6=AC=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#368)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team/buddyya/job/feed/FeedItemReader.java | 22 +++++-------------- .../job/student/StudentItemReader.java | 3 ++- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/team/buddyya/job/feed/FeedItemReader.java b/src/main/java/com/team/buddyya/job/feed/FeedItemReader.java index 5baf3be7..1f1e9f68 100644 --- a/src/main/java/com/team/buddyya/job/feed/FeedItemReader.java +++ b/src/main/java/com/team/buddyya/job/feed/FeedItemReader.java @@ -13,8 +13,6 @@ public class FeedItemReader implements ItemReader { private Long minStudentId; private Long maxStudentId; - private Long minUniversityId; - private Long maxUniversityId; public FeedItemReader(JdbcTemplate jdbcTemplate, int totalCount) { this.jdbcTemplate = jdbcTemplate; @@ -24,17 +22,11 @@ public FeedItemReader(JdbcTemplate jdbcTemplate, int totalCount) { @Override public FeedJobDTO read() throws Exception { if (minStudentId == null) { - // Student, University 테이블의 데이터 존재 여부 및 ID 범위 초기화 if (jdbcTemplate.queryForObject("SELECT COUNT(1) FROM student", Long.class) == 0) { throw new IllegalStateException("Prerequisite data (Student) is missing."); } this.minStudentId = jdbcTemplate.queryForObject("SELECT MIN(id) FROM student", Long.class); this.maxStudentId = jdbcTemplate.queryForObject("SELECT MAX(id) FROM student", Long.class); - if (jdbcTemplate.queryForObject("SELECT COUNT(1) FROM university", Long.class) == 0) { - throw new IllegalStateException("Prerequisite data (University) is missing."); - } - this.minUniversityId = jdbcTemplate.queryForObject("SELECT MIN(id) FROM university", Long.class); - this.maxUniversityId = jdbcTemplate.queryForObject("SELECT MAX(id) FROM university", Long.class); } if (counter.get() >= totalCount) { @@ -43,18 +35,16 @@ public FeedJobDTO read() throws Exception { int currentCount = counter.incrementAndGet(); long randomStudentId = ThreadLocalRandom.current().nextLong(minStudentId, maxStudentId + 1); - long randomUniversityId = ThreadLocalRandom.current().nextLong(minUniversityId, maxUniversityId + 1); - // Category는 데이터가 적다고 가정하고 SQL로 랜덤 조회, 많아진다면 동일하게 MIN/MAX 방식으로 변경 - Long randomCategoryId = jdbcTemplate.queryForObject("SELECT id FROM category ORDER BY RAND() LIMIT 1", - Long.class); + long universityId = 21L; + Long categoryId = 1L; return FeedJobDTO.builder() - .title("피드 제목 " + currentCount) - .content("피드 내용입니다. " + currentCount) + .title("Feed") + .content("" + currentCount) .profileVisible(ThreadLocalRandom.current().nextBoolean()) .studentId(randomStudentId) - .categoryId(randomCategoryId) - .universityId(randomUniversityId) + .categoryId(categoryId) + .universityId(universityId) .build(); } } diff --git a/src/main/java/com/team/buddyya/job/student/StudentItemReader.java b/src/main/java/com/team/buddyya/job/student/StudentItemReader.java index 5437b9e1..a81283f5 100644 --- a/src/main/java/com/team/buddyya/job/student/StudentItemReader.java +++ b/src/main/java/com/team/buddyya/job/student/StudentItemReader.java @@ -38,6 +38,7 @@ public StudentJobDTO read() { String profileImageUrl = String.format( "https://buddyya.s3.ap-northeast-2.amazonaws.com/default-profile-image/image__%d.png", profileImageIndex); + return StudentJobDTO.builder() .phoneNumber("010" + String.format("%08d", index)) .name("student" + index) @@ -48,7 +49,7 @@ public StudentJobDTO read() { .universityId(randomUniversityId) .role("STUDENT") .gender(gender) - .characterProfileImage("default_image_url_" + (index % 8)) + .characterProfileImage(profileImageUrl) .isBanned(false) .build(); }