Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public record MemberSearchResult(
String nickname,
String imageUrl,
String aliasName,
String aliasColor,
int followerCount
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public record RoomPlayingDetailViewResponse(
String progressStartDate,
String progressEndDate,
String category,
String categoryColor,
String roomDescription,
int memberCount,
int recruitCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import java.util.List;

@Builder
public record RoomRecruitingDetailViewResponse(
boolean isHost,
boolean isJoining,
Expand All @@ -15,6 +16,7 @@ public record RoomRecruitingDetailViewResponse(
String progressEndDate,
String recruitEndDate,
String category,
String categoryColor,
String roomDescription,
int memberCount,
int recruitCount,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package konkuk.thip.room.adapter.out.persistence;

import konkuk.thip.common.exception.EntityNotFoundException;
import konkuk.thip.common.exception.code.ErrorCode;
import konkuk.thip.common.util.Cursor;
import konkuk.thip.common.util.CursorBasedList;
import konkuk.thip.room.adapter.in.web.response.RoomGetHomeJoinedListResponse;
import konkuk.thip.room.adapter.in.web.response.RoomRecruitingDetailViewResponse;
import konkuk.thip.room.adapter.in.web.response.RoomSearchResponse;
import konkuk.thip.room.adapter.out.persistence.function.RoomQueryFunction;
import konkuk.thip.room.adapter.out.persistence.repository.RoomJpaRepository;
import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository;
import konkuk.thip.room.application.port.out.RoomQueryPort;
import konkuk.thip.room.application.port.out.dto.RoomQueryDto;
import konkuk.thip.room.domain.Category;
Expand All @@ -24,6 +27,7 @@
public class RoomQueryPersistenceAdapter implements RoomQueryPort {

private final RoomJpaRepository roomJpaRepository;
private final CategoryJpaRepository categoryJpaRepository;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

CategoryJpaRepository 의존 추가는 타당하지만, 쿼리 경로 최적화를 고려해 주세요.
현재 서비스 레이어에서 카테고리 색상 조회가 개별 DB 왕복을 유발합니다. Room 관련 조회(projection/DTO)에 categoryColor를 조인해 한 번에 가져오도록 리포지토리 쿼리를 확장하면, 상세 화면 1회 호출당 추가 쿼리 1회 비용을 제거할 수 있습니다.

원하시면 RoomJpaRepository에 카테고리 색상 조인을 포함한 전용 프로젝션(예: RoomDetailProjection{..., categoryColor})을 추가하는 패치를 제안드릴게요.

🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java
around line 30, adding CategoryJpaRepository is acceptable but causes per-room
DB roundtrips for category color; instead extend RoomJpaRepository to return a
projection/DTO that includes categoryColor (e.g., RoomDetailProjection { ...,
String categoryColor }), implement a repository query (JPQL/@Query or join
fetch) that joins Room -> Category and selects the category color into that
projection, update the adapter/service to use this new projection for room
detail queries and remove the per-room CategoryJpaRepository lookups so the
color is retrieved in a single DB call.


@Override
public int countRecruitingRoomsByBookAndStartDateAfter(String isbn, LocalDate currentDate) {
Expand Down Expand Up @@ -101,4 +105,12 @@ public List<RoomQueryDto> findRoomsByCategoryOrderByDeadline(Category category,
public List<RoomQueryDto> findRoomsByCategoryOrderByPopular(Category category, int limit, Long userId) {
return roomJpaRepository.findRoomsByCategoryOrderByMemberCount(category.getValue(), limit, userId);
}

// TODO : 리펙토링 대상
@Override
public String findAliasColorOfCategory(Category category) {
return categoryJpaRepository.findAliasColorByValue(category.getValue()).orElseThrow(
() -> new EntityNotFoundException(ErrorCode.CATEGORY_NOT_FOUND)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@

import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

public interface CategoryJpaRepository extends JpaRepository<CategoryJpaEntity, Long> {

Optional<CategoryJpaEntity> findByValue(String value);

// TODO : 리펙토링 대상
@Query("select a.color " +
"from CategoryJpaEntity c join c.aliasForCategoryJpaEntity a " +
"where c.value = :categoryValue")
Optional<String> findAliasColorByValue(@Param("categoryValue") String categoryValue);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,9 @@ public interface RoomQueryPort {

List<RoomQueryDto> findRoomsByCategoryOrderByPopular(Category category, int limit, Long userId);


/**
* 임시 메서드
* TODO 리펙토링 대상
*/
String findAliasColorOfCategory(Category category);
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public RoomGetMemberListResponse getRoomMemberList(Long roomId) {
.nickname(user.getNickname())
.imageUrl(user.getAlias().getImageUrl())
.aliasName(user.getAlias().getValue())
.aliasColor(user.getAlias().getColor())
.followerCount(user.getFollowerCount())
.build();
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import konkuk.thip.room.adapter.in.web.response.RoomPlayingDetailViewResponse;
import konkuk.thip.room.application.port.in.RoomShowPlayingDetailViewUseCase;
import konkuk.thip.room.application.port.out.RoomCommandPort;
import konkuk.thip.room.application.port.out.RoomQueryPort;
import konkuk.thip.room.domain.Room;
import konkuk.thip.room.application.port.out.RoomParticipantCommandPort;
import konkuk.thip.room.domain.RoomParticipants;
Expand All @@ -24,14 +25,15 @@ public class RoomShowPlayingDetailViewService implements RoomShowPlayingDetailVi
private static final int TOP_PARTICIPATION_VOTES_COUNT = 3;

private final RoomCommandPort roomCommandPort;
private final RoomQueryPort roomQueryPort;
private final BookCommandPort bookCommandPort;
private final RoomParticipantCommandPort roomParticipantCommandPort;
private final VoteQueryPort voteQueryPort;

@Override
@Transactional(readOnly = true)
public RoomPlayingDetailViewResponse getPlayingRoomDetailView(Long userId, Long roomId) {
// 1. Room 조회, Book 조회
// 1. Room 조회, Book 조회, Category와 연관된 Alias 조회
Room room = roomCommandPort.getByIdOrThrow(roomId);
Book book = bookCommandPort.findById(room.getBookId());

Expand Down Expand Up @@ -66,6 +68,7 @@ private RoomPlayingDetailViewResponse buildResponse(Long userId, Room room, Book
.currentPage(roomParticipants.getCurrentPageOfUser(userId))
.userPercentage(roomParticipants.getUserPercentageOfUser(userId))
.currentVotes(topParticipationVotes)
.categoryColor(roomQueryPort.findAliasColorOfCategory(room.getCategory())) // TODO : 리펙토링 대상
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,27 +53,28 @@ private RoomRecruitingDetailViewResponse buildResponse(
RoomParticipants participants,
List<RoomRecruitingDetailViewResponse.RecommendRoom> recommendRooms
) {
return new RoomRecruitingDetailViewResponse(
participants.isHostOfRoom(userId),
participants.isJoiningToRoom(userId),
room.getId(),
room.getTitle(),
room.getCategory().getImageUrl(),
room.isPublic(),
DateUtil.formatDate(room.getStartDate()),
DateUtil.formatDate(room.getEndDate()),
DateUtil.formatAfterTime(room.getStartDate()),
room.getCategory().getValue(),
room.getDescription(),
participants.calculateMemberCount(),
room.getRecruitCount(),
book.getIsbn(),
book.getImageUrl(),
book.getTitle(),
book.getAuthorName(),
book.getDescription(),
book.getPublisher(),
recommendRooms
);
return RoomRecruitingDetailViewResponse.builder()
.isHost(participants.isHostOfRoom(userId))
.isJoining(participants.isJoiningToRoom(userId))
.roomId(room.getId())
.roomName(room.getTitle())
.roomImageUrl(room.getCategory().getImageUrl())
.isPublic(room.isPublic())
.progressStartDate(DateUtil.formatDate(room.getStartDate()))
.progressEndDate(DateUtil.formatDate(room.getEndDate()))
.recruitEndDate(DateUtil.formatAfterTime(room.getStartDate()))
.category(room.getCategory().getValue())
.categoryColor(roomQueryPort.findAliasColorOfCategory(room.getCategory())) // TODO : 리펙토링 대상
.roomDescription(room.getDescription())
.memberCount(participants.calculateMemberCount())
.recruitCount(room.getRecruitCount())
.isbn(book.getIsbn())
.bookImageUrl(book.getImageUrl())
.bookTitle(book.getTitle())
.authorName(book.getAuthorName())
.bookDescription(book.getDescription())
.publisher(book.getPublisher())
.recommendRooms(recommendRooms)
.build();
Comment on lines +56 to +78
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Builder 전 데이터 조회 분리 및 중복 제거 — 두 서비스에서 동일 패턴 반복

  • Playing/Recruiting 상세 모두에서 categoryColor 조회가 동일하게 반복됩니다. 지역 변수로 분리하고, 공통 유틸/컴포넌트로 추출하면 재사용성과 테스트 용이성이 좋아집니다.

간단한 로컬 정리 예시:

@@
-        return RoomRecruitingDetailViewResponse.builder()
+        final String categoryColor = roomQueryPort.findAliasColorOfCategory(room.getCategory()); // TODO : 리펙토링 대상
+        return RoomRecruitingDetailViewResponse.builder()
@@
-                .categoryColor(roomQueryPort.findAliasColorOfCategory(room.getCategory()))      // TODO : 리펙토링 대상
+                .categoryColor(categoryColor)
                 .roomDescription(room.getDescription())

더 나아가 CategoryColorProvider 컴포넌트를 도입해(인터페이스 + 기본 구현) 두 서비스에서 주입받아 사용하면, 추후 캐싱/폴백 정책을 한 곳에서 관리할 수 있습니다. 원하시면 해당 컴포넌트 초안까지 만들어 드리겠습니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return RoomRecruitingDetailViewResponse.builder()
.isHost(participants.isHostOfRoom(userId))
.isJoining(participants.isJoiningToRoom(userId))
.roomId(room.getId())
.roomName(room.getTitle())
.roomImageUrl(room.getCategory().getImageUrl())
.isPublic(room.isPublic())
.progressStartDate(DateUtil.formatDate(room.getStartDate()))
.progressEndDate(DateUtil.formatDate(room.getEndDate()))
.recruitEndDate(DateUtil.formatAfterTime(room.getStartDate()))
.category(room.getCategory().getValue())
.categoryColor(roomQueryPort.findAliasColorOfCategory(room.getCategory())) // TODO : 리펙토링 대상
.roomDescription(room.getDescription())
.memberCount(participants.calculateMemberCount())
.recruitCount(room.getRecruitCount())
.isbn(book.getIsbn())
.bookImageUrl(book.getImageUrl())
.bookTitle(book.getTitle())
.authorName(book.getAuthorName())
.bookDescription(book.getDescription())
.publisher(book.getPublisher())
.recommendRooms(recommendRooms)
.build();
// Extract categoryColor once to remove duplication and improve testability
final String categoryColor = roomQueryPort.findAliasColorOfCategory(room.getCategory()); // TODO : 리펙토링 대상
return RoomRecruitingDetailViewResponse.builder()
.isHost(participants.isHostOfRoom(userId))
.isJoining(participants.isJoiningToRoom(userId))
.roomId(room.getId())
.roomName(room.getTitle())
.roomImageUrl(room.getCategory().getImageUrl())
.isPublic(room.isPublic())
.progressStartDate(DateUtil.formatDate(room.getStartDate()))
.progressEndDate(DateUtil.formatDate(room.getEndDate()))
.recruitEndDate(DateUtil.formatAfterTime(room.getStartDate()))
.category(room.getCategory().getValue())
.categoryColor(categoryColor) // TODO : 리펙토링 대상
.roomDescription(room.getDescription())
.memberCount(participants.calculateMemberCount())
.recruitCount(room.getRecruitCount())
.isbn(book.getIsbn())
.bookImageUrl(book.getImageUrl())
.bookTitle(book.getTitle())
.authorName(book.getAuthorName())
.bookDescription(book.getDescription())
.publisher(book.getPublisher())
.recommendRooms(recommendRooms)
.build();
🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/room/application/service/RoomShowRecruitingDetailViewService.java
around lines 56 to 78, the categoryColor is being looked up inline in the
builder causing duplicated lookup logic across services; extract the category
color lookup into a local variable (e.g., String categoryColor =
roomQueryPort.findAliasColorOfCategory(room.getCategory())) and use that
variable in the builder, and then refactor by introducing a
CategoryColorProvider interface with a default implementation (or a simple
utility) that encapsulates roomQueryPort.findAliasColorOfCategory(...) so this
service and the similar Playing/Recruiting detail service can accept the
provider via constructor injection and call
provider.getCategoryColor(room.getCategory()), centralizing the logic for easier
reuse, testing, caching and future fallback handling.

}
}