diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveCommentOutputDTO.java b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveCommentOutputDTO.java index 00a0a30..6e1cd74 100644 --- a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveCommentOutputDTO.java +++ b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveCommentOutputDTO.java @@ -23,7 +23,8 @@ public class ArchiveCommentOutputDTO { @Builder public static class CommentDTO { private Long id; - private String writer; + private String commentWriter; + private String commentWriterProfileImg; private String content; private boolean isDeleted; private LocalDateTime createdDateTime; diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveListDTO.java b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveListDTO.java index 9e16f60..4169a64 100644 --- a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveListDTO.java +++ b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveListDTO.java @@ -18,6 +18,7 @@ public static class ArchiveSimpleDTO { private Long id; private String title; private String writerNickname; + private String writerProfileImg; // 작성자 프로필 이미지 추가 private String thumbnail; private int commentCount; private int view; // 조회수 추가 diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveOutputDTO.java b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveOutputDTO.java index c8bfa36..b15dca8 100644 --- a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveOutputDTO.java +++ b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveOutputDTO.java @@ -16,7 +16,9 @@ @AllArgsConstructor public class ArchiveOutputDTO { private Long id; + private String writerUsername; private String writerNickname; + private String writerProfileImg; private String title; private String description; private LocalDate startDate; diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/entity/ArchiveMember.java b/sequence_member/src/main/java/sequence/sequence_member/archive/entity/ArchiveMember.java index 050935e..f14bbe4 100644 --- a/sequence_member/src/main/java/sequence/sequence_member/archive/entity/ArchiveMember.java +++ b/sequence_member/src/main/java/sequence/sequence_member/archive/entity/ArchiveMember.java @@ -43,7 +43,7 @@ public class ArchiveMember extends BaseTimeEntity { @JoinColumn(name = "archive_id") private Archive archive; - @Column(name = "profile_img") + @Column(name = "profile_img", columnDefinition = "TEXT") private String profileImg; // 멤버의 프로필 이미지 URL @Builder diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveRepository.java b/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveRepository.java index dee37c0..c1d2663 100644 --- a/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveRepository.java +++ b/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveRepository.java @@ -20,30 +20,50 @@ public interface ArchiveRepository extends JpaRepository { // 기본 조회 - 삭제되지 않은 아카이브만 Optional findByIdAndIsDeletedFalse(Long id); + // 작성자 정보를 함께 조회하는 최적화된 메서드 (FETCH JOIN 사용) + @Query("SELECT a FROM Archive a JOIN FETCH a.writer WHERE a.id = :id AND a.isDeleted = false") + Optional findByIdAndIsDeletedFalseWithWriter(@Param("id") Long id); + // 전체 목록 조회 - 삭제되지 않은 것만 Page findByIsDeletedFalse(Pageable pageable); // 상태별 조회 - 삭제되지 않은 것만 Page findByStatusAndIsDeletedFalse(Status status, Pageable pageable); + // 상태별 조회 - 작성자 정보 포함 (FETCH JOIN 사용) + @Query("SELECT a FROM Archive a JOIN FETCH a.writer WHERE a.status = :status AND a.isDeleted = false") + Page findByStatusAndIsDeletedFalseWithWriter(@Param("status") Status status, Pageable pageable); + // 카테고리별 조회 - 삭제되지 않은 것만 Page findByCategoryAndIsDeletedFalse(Category category, Pageable pageable); // 카테고리와 상태로 조회 - 삭제되지 않은 것만 Page findByCategoryAndStatusAndIsDeletedFalse(Category category, Status status, Pageable pageable); + // 카테고리와 상태로 조회 - 작성자 정보 포함 (FETCH JOIN 사용) + @Query("SELECT a FROM Archive a JOIN FETCH a.writer WHERE a.category = :category AND a.status = :status AND a.isDeleted = false") + Page findByCategoryAndStatusAndIsDeletedFalseWithWriter(@Param("category") Category category, @Param("status") Status status, Pageable pageable); + // 제목으로 검색 - 삭제되지 않은 것만 Page findByTitleContainingIgnoreCaseAndIsDeletedFalse(String title, Pageable pageable); // 제목으로 검색하고 상태로 필터링 - 삭제되지 않은 것만 Page findByTitleContainingIgnoreCaseAndStatusAndIsDeletedFalse(String title, Status status, Pageable pageable); + // 제목으로 검색하고 상태로 필터링 - 작성자 정보 포함 (FETCH JOIN 사용) + @Query("SELECT a FROM Archive a JOIN FETCH a.writer WHERE a.title LIKE %:title% AND a.status = :status AND a.isDeleted = false") + Page findByTitleContainingIgnoreCaseAndStatusAndIsDeletedFalseWithWriter(@Param("title") String title, @Param("status") Status status, Pageable pageable); + // 카테고리와 제목으로 검색 - 삭제되지 않은 것만 Page findByCategoryAndTitleContainingIgnoreCaseAndIsDeletedFalse(Category category, String title, Pageable pageable); // 카테고리와 제목으로 검색하고 상태로 필터링 - 삭제되지 않은 것만 Page findByCategoryAndTitleContainingIgnoreCaseAndStatusAndIsDeletedFalse(Category category, String title, Status status, Pageable pageable); + // 카테고리와 제목으로 검색하고 상태로 필터링 - 작성자 정보 포함 (FETCH JOIN 사용) + @Query("SELECT a FROM Archive a JOIN FETCH a.writer WHERE a.category = :category AND a.title LIKE %:title% AND a.status = :status AND a.isDeleted = false") + Page findByCategoryAndTitleContainingIgnoreCaseAndStatusAndIsDeletedFalseWithWriter(@Param("category") Category category, @Param("title") String title, @Param("status") Status status, Pageable pageable); + // 멤버 ID로 아카이브 검색 - 삭제되지 않은 것만 @Query("SELECT a FROM Archive a JOIN a.archiveMembers am WHERE am.member.id = :memberId AND a.isDeleted = false") Page findByMemberId(@Param("memberId") Long memberId, Pageable pageable); diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/service/ArchiveService.java b/sequence_member/src/main/java/sequence/sequence_member/archive/service/ArchiveService.java index 65f8e70..c66fe68 100644 --- a/sequence_member/src/main/java/sequence/sequence_member/archive/service/ArchiveService.java +++ b/sequence_member/src/main/java/sequence/sequence_member/archive/service/ArchiveService.java @@ -191,7 +191,7 @@ public ArchiveListDTO getAllArchives(int page, SortType sortType, String usernam } Pageable pageable = createPageableWithSort(page, sortType); - Page archivePage = archiveRepository.findByStatusAndIsDeletedFalse(Status.평가완료, pageable); + Page archivePage = archiveRepository.findByStatusAndIsDeletedFalseWithWriter(Status.평가완료, pageable); List archives = archivePage.getContent().stream() .map(archive -> { @@ -203,6 +203,7 @@ public ArchiveListDTO getAllArchives(int page, SortType sortType, String usernam .id(archive.getId()) .title(archive.getTitle()) .writerNickname(archive.getWriter().getNickname()) + .writerProfileImg(archive.getWriter().getProfileImg()) .thumbnail(archive.getThumbnail()) .commentCount(archive.getComments().size()) .view(archive.getView()) @@ -236,15 +237,15 @@ public ArchiveListDTO searchArchives( Page archivePage; if (category != null && keyword != null && !keyword.trim().isEmpty()) { - archivePage = archiveRepository.findByCategoryAndTitleContainingIgnoreCaseAndStatusAndIsDeletedFalse( + archivePage = archiveRepository.findByCategoryAndTitleContainingIgnoreCaseAndStatusAndIsDeletedFalseWithWriter( category, keyword.trim(), Status.평가완료, pageable); } else if (category != null) { - archivePage = archiveRepository.findByCategoryAndStatusAndIsDeletedFalse(category, Status.평가완료, pageable); + archivePage = archiveRepository.findByCategoryAndStatusAndIsDeletedFalseWithWriter(category, Status.평가완료, pageable); } else if (keyword != null && !keyword.trim().isEmpty()) { - archivePage = archiveRepository.findByTitleContainingIgnoreCaseAndStatusAndIsDeletedFalse( + archivePage = archiveRepository.findByTitleContainingIgnoreCaseAndStatusAndIsDeletedFalseWithWriter( keyword.trim(), Status.평가완료, pageable); } else { - archivePage = archiveRepository.findByStatusAndIsDeletedFalse(Status.평가완료, pageable); + archivePage = archiveRepository.findByStatusAndIsDeletedFalseWithWriter(Status.평가완료, pageable); } List archives = archivePage.getContent().stream() @@ -257,6 +258,7 @@ public ArchiveListDTO searchArchives( .id(archive.getId()) .title(archive.getTitle()) .writerNickname(archive.getWriter().getNickname()) + .writerProfileImg(archive.getWriter().getProfileImg()) .thumbnail(archive.getThumbnail()) .commentCount(archive.getComments().size()) .view(archive.getView()) @@ -287,9 +289,17 @@ private Pageable createPageableWithSort(int page, SortType sortType) { return PageRequest.of(page, 18, sort); } + // 댓글 작성자의 프로필 이미지를 가져오는 헬퍼 메서드 + private String getCommentWriterProfileImg(String writerNickname) { + return memberRepository.findByNickname(writerNickname) + .map(MemberEntity::getProfileImg) + .orElse("default.png"); // 기본 이미지 + } + // Archive 엔티티를 DTO로 변환 private ArchiveOutputDTO convertToDTO(Archive archive, String username, int viewCount) { List memberDTOs = archive.getArchiveMembers().stream() + .filter(archiveMember -> !archiveMember.getMember().getId().equals(archive.getWriter().getId())) .map(archiveMember -> ArchiveOutputDTO.ArchiveMemberDTO.builder() .username(archiveMember.getMember().getUsername()) .nickname(archiveMember.getMember().getNickname()) @@ -319,7 +329,8 @@ private ArchiveOutputDTO convertToDTO(Archive archive, String username, int view for (ArchiveComment parentComment : parentComments) { ArchiveCommentOutputDTO.CommentDTO parentDTO = ArchiveCommentOutputDTO.CommentDTO.builder() .id(parentComment.getId()) - .writer(parentComment.getWriter()) + .commentWriter(parentComment.getWriter()) + .commentWriterProfileImg(getCommentWriterProfileImg(parentComment.getWriter())) .content(parentComment.isDeleted() ? "삭제된 댓글입니다." : parentComment.getContent()) .isDeleted(parentComment.isDeleted()) .createdDateTime(parentComment.getCreatedDateTime()) @@ -333,7 +344,8 @@ private ArchiveOutputDTO convertToDTO(Archive archive, String username, int view for (ArchiveComment childComment : childComments) { ArchiveCommentOutputDTO.CommentDTO childDTO = ArchiveCommentOutputDTO.CommentDTO.builder() .id(childComment.getId()) - .writer(childComment.getWriter()) + .commentWriter(childComment.getWriter()) + .commentWriterProfileImg(getCommentWriterProfileImg(childComment.getWriter())) .content(childComment.isDeleted() ? "삭제된 댓글입니다." : childComment.getContent()) .isDeleted(childComment.isDeleted()) .createdDateTime(childComment.getCreatedDateTime()) @@ -347,7 +359,9 @@ private ArchiveOutputDTO convertToDTO(Archive archive, String username, int view return ArchiveOutputDTO.builder() .id(archive.getId()) + .writerUsername(archive.getWriter().getUsername()) .writerNickname(archive.getWriter().getNickname()) + .writerProfileImg(archive.getWriter().getProfileImg()) .title(archive.getTitle()) .description(archive.getDescription()) .startDate(archive.getStartDate()) diff --git a/sequence_member/src/main/java/sequence/sequence_member/member/entity/EducationEntity.java b/sequence_member/src/main/java/sequence/sequence_member/member/entity/EducationEntity.java index 6990e3e..1182eca 100644 --- a/sequence_member/src/main/java/sequence/sequence_member/member/entity/EducationEntity.java +++ b/sequence_member/src/main/java/sequence/sequence_member/member/entity/EducationEntity.java @@ -2,7 +2,7 @@ import jakarta.persistence.*; import lombok.Data; - +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import sequence.sequence_member.global.enums.enums.Degree; import sequence.sequence_member.global.enums.enums.ProjectRole; @@ -18,6 +18,7 @@ @Entity @Data +@EqualsAndHashCode(callSuper = false) @Table(name="education") @NoArgsConstructor public class EducationEntity extends BaseTimeEntity { diff --git a/sequence_member/src/main/resources/application.yml b/sequence_member/src/main/resources/application.yml index 1e66f7f..e5161b2 100644 --- a/sequence_member/src/main/resources/application.yml +++ b/sequence_member/src/main/resources/application.yml @@ -96,6 +96,7 @@ management: # 이 설정은 /actuator/health 엔드포인트에서 헬스 체크 정보를 항상 상세히 보여주도록 설정합니다. 기본적으로, 헬스 체크 엔드포인트는 요약된 상태 정보만 제공하며, 상세 정보는 노출되지 않습니다. prometheus: enabled: true + prometheus: metrics: export: