-
Notifications
You must be signed in to change notification settings - Fork 0
[refactor] 기록장 조회 api 리팩토링 #226
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c65c23b
661ec89
4108949
082151c
69c8047
0d9b6b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,7 @@ public record PostDto( | |
| String content, | ||
| int likeCount, | ||
| int commentCount, | ||
| boolean isOverview, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAPI 응답 변경에 대한 하위 호환성 확인 필요
또한 이 필드가 기록 타입과 투표 타입 모두에 적용되는 것이 맞는지 확인이 필요합니다. 🏁 Script executed: #!/bin/bash
# API 문서나 클라이언트 코드에서 RecordSearchResponse 사용 확인
rg "RecordSearchResponse|PostDto" --type-add 'docs:*.{md,adoc,txt}' -t docs -t java -t kotlin -t swift -A 2Length of output: 7142 🏁 Script executed: #!/bin/bash
# Display the PostDto record definition around the isOverview field
rg -n "record PostDto" -A 10 src/main/java/konkuk/thip/record/adapter/in/web/response/RecordSearchResponse.javaLength of output: 453 🏁 Script executed: #!/bin/bash
# PostDto 에 isOverview 필드가 포함되었는지 확인
rg -n "isOverview" -A3 src/main/java/konkuk/thip/record/adapter/in/web/response/RecordSearchResponse.java
# RecordQueryMapper 의 toPostDto 시그니처 확인
rg -n "PostDto toPostDto" -A5 src/main/java/konkuk/thip/record/application/mapper/RecordQueryMapper.javaLength of output: 645 추가 확인: 다음 지점에서
또한 🤖 Prompt for AI Agents |
||
| boolean isLiked, | ||
| boolean isWriter, | ||
| boolean isLocked, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,26 +4,24 @@ | |
| import com.querydsl.core.types.OrderSpecifier; | ||
| import com.querydsl.core.types.dsl.CaseBuilder; | ||
| import com.querydsl.core.types.dsl.NumberExpression; | ||
| import com.querydsl.core.types.dsl.StringExpression; | ||
| import com.querydsl.jpa.impl.JPAQueryFactory; | ||
| import konkuk.thip.common.entity.StatusType; | ||
| import konkuk.thip.common.util.Cursor; | ||
| import konkuk.thip.post.adapter.out.jpa.QPostJpaEntity; | ||
| import konkuk.thip.record.adapter.out.jpa.QRecordJpaEntity; | ||
| import konkuk.thip.record.adapter.out.jpa.RecordJpaEntity; | ||
| import konkuk.thip.record.adapter.out.persistence.constants.SortType; | ||
| import konkuk.thip.record.application.port.out.dto.PostQueryDto; | ||
| import konkuk.thip.record.application.port.out.dto.QPostQueryDto; | ||
| import konkuk.thip.user.adapter.out.jpa.QUserJpaEntity; | ||
| import konkuk.thip.vote.adapter.out.jpa.QVoteJpaEntity; | ||
| import konkuk.thip.vote.adapter.out.jpa.VoteJpaEntity; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| import java.time.LocalDateTime; | ||
| import java.util.List; | ||
|
|
||
| import static konkuk.thip.common.post.PostType.*; | ||
| import static konkuk.thip.common.post.PostType.RECORD; | ||
| import static konkuk.thip.common.post.PostType.VOTE; | ||
|
|
||
|
|
||
| @Repository | ||
|
|
@@ -62,11 +60,11 @@ private BooleanBuilder buildMyRecordCondition(Long roomId, Long userId) { | |
| BooleanBuilder where = new BooleanBuilder(); | ||
|
|
||
| BooleanBuilder voteCondition = new BooleanBuilder(); | ||
| voteCondition.and(post.instanceOf(VoteJpaEntity.class)) | ||
| voteCondition.and(post.dtype.eq(VOTE.getType())) | ||
|
Comment on lines
-65
to
+63
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LGTM |
||
| .and(vote.roomJpaEntity.roomId.eq(roomId)); | ||
|
|
||
| BooleanBuilder recordCondition = new BooleanBuilder(); | ||
| recordCondition.and(post.instanceOf(RecordJpaEntity.class)) | ||
| recordCondition.and(post.dtype.eq(RECORD.getType())) | ||
| .and(record.roomJpaEntity.roomId.eq(roomId)); | ||
|
|
||
| where.and(voteCondition.or(recordCondition)) | ||
|
|
@@ -99,7 +97,7 @@ private BooleanBuilder buildRecordVoteCondition(Long roomId, Integer pageStart, | |
| BooleanBuilder where = new BooleanBuilder(); | ||
|
|
||
| BooleanBuilder voteCondition = new BooleanBuilder(); | ||
| voteCondition.and(post.instanceOf(VoteJpaEntity.class)) | ||
| voteCondition.and(post.dtype.eq(VOTE.getType())) | ||
| .and(vote.roomJpaEntity.roomId.eq(roomId)); | ||
|
|
||
| if (isOverview) { | ||
|
|
@@ -110,7 +108,7 @@ private BooleanBuilder buildRecordVoteCondition(Long roomId, Integer pageStart, | |
| } | ||
|
|
||
| BooleanBuilder recordCondition = new BooleanBuilder(); | ||
| recordCondition.and(post.instanceOf(RecordJpaEntity.class)) | ||
| recordCondition.and(post.dtype.eq(RECORD.getType())) | ||
| .and(record.roomJpaEntity.roomId.eq(roomId)); | ||
|
|
||
| if (isOverview) { | ||
|
|
@@ -128,27 +126,19 @@ private BooleanBuilder buildRecordVoteCondition(Long roomId, Integer pageStart, | |
| // Case: pageExpr (Record, Vote 분기) | ||
| private NumberExpression<Integer> pageExpr() { | ||
| return new CaseBuilder() | ||
| .when(post.instanceOf(RecordJpaEntity.class)).then(record.page) | ||
| .when(post.instanceOf(VoteJpaEntity.class)).then(vote.page) | ||
| .when(post.dtype.eq(RECORD.getType())).then(record.page) | ||
| .when(post.dtype.eq(VOTE.getType())).then(vote.page) | ||
| .otherwise(0); | ||
| } | ||
|
|
||
| // Case: isOverviewExpr (총평 여부를 정렬 기준으로 사용) | ||
| private NumberExpression<Integer> isOverviewExpr() { | ||
| return new CaseBuilder() | ||
| .when(post.instanceOf(RecordJpaEntity.class)).then(record.isOverview.castToNum(Integer.class)) | ||
| .when(post.instanceOf(VoteJpaEntity.class)).then(vote.isOverview.castToNum(Integer.class)) | ||
| .when(post.dtype.eq(RECORD.getType())).then(record.isOverview.castToNum(Integer.class)) | ||
| .when(post.dtype.eq(VOTE.getType())).then(vote.isOverview.castToNum(Integer.class)) | ||
| .otherwise(0); | ||
| } | ||
|
|
||
| // Case: postTypeExpr ("RECORD" or "VOTE") | ||
| private StringExpression postTypeExpr() { | ||
| return new CaseBuilder() | ||
| .when(post.instanceOf(RecordJpaEntity.class)).then(RECORD.getType()) | ||
| .when(post.instanceOf(VoteJpaEntity.class)).then(VOTE.getType()) | ||
| .otherwise(FEED.getType()); | ||
| } | ||
|
|
||
| private BooleanBuilder buildCursorPredicateForSortType(SortType sortType, Cursor cursor) { | ||
| BooleanBuilder builder = new BooleanBuilder(); | ||
|
|
||
|
|
@@ -198,7 +188,7 @@ private OrderSpecifier<?>[] getOrderSpecifiers(SortType sortType) { | |
| private QPostQueryDto selectPostQueryDto() { | ||
| return new QPostQueryDto( | ||
| post.postId, | ||
| postTypeExpr(), //추후에 상속 구조 해지시 type 필드로 구분 | ||
| post.dtype, //추후에 상속 구조 해지시 type 필드로 구분 | ||
| post.createdAt, | ||
| pageExpr(), | ||
| user.userId, | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,38 @@ | ||||||
| package konkuk.thip.record.application.mapper; | ||||||
|
|
||||||
| import konkuk.thip.common.util.DateUtil; | ||||||
| import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; | ||||||
| import konkuk.thip.record.application.port.out.dto.PostQueryDto; | ||||||
| import org.mapstruct.Mapper; | ||||||
| import org.mapstruct.Mapping; | ||||||
| import org.mapstruct.ReportingPolicy; | ||||||
|
|
||||||
| import java.util.List; | ||||||
|
|
||||||
| @Mapper(componentModel = "spring", imports = DateUtil.class, unmappedTargetPolicy = ReportingPolicy.ERROR) | ||||||
| public interface RecordQueryMapper { | ||||||
|
|
||||||
| @Mapping(target = "postId", source = "dto.postId") | ||||||
| @Mapping(target = "postDate", expression = "java(DateUtil.formatBeforeTime(dto.postDate()))") | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain날짜 포맷팅 null 체크 필요
필요시 null 체크 추가: -@Mapping(target = "postDate", expression = "java(DateUtil.formatBeforeTime(dto.postDate()))")
+@Mapping(target = "postDate", expression = "java(dto.postDate() != null ? DateUtil.formatBeforeTime(dto.postDate()) : null)")🏁 Script executed: #!/bin/bash
# DateUtil.formatBeforeTime 메서드의 null 처리 확인
ast-grep --pattern 'public static String formatBeforeTime($_) {
$$$
}'Length of output: 883 날짜 포맷팅 시 NullPointerException 방지용 null 체크 추가 필요 RecordQueryMapper.java에서
아래와 같이 null 체크를 추가해 주세요: - @Mapping(target = "postDate", expression = "java(DateUtil.formatBeforeTime(dto.postDate()))")
+ @Mapping(target = "postDate", expression = "java(dto.postDate() != null ? DateUtil.formatBeforeTime(dto.postDate()) : null)")📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| @Mapping(target = "postType", source = "dto.postType") | ||||||
| @Mapping(target = "page", source = "dto.page") | ||||||
| @Mapping(target = "userId", source = "dto.userId") | ||||||
| @Mapping(target = "nickName", source = "dto.nickName") | ||||||
| @Mapping(target = "profileImageUrl",source = "dto.profileImageUrl") | ||||||
| @Mapping(target = "content", source = "content") | ||||||
| @Mapping(target = "likeCount", source = "dto.likeCount") | ||||||
| @Mapping(target = "commentCount", source = "dto.commentCount") | ||||||
| @Mapping(target = "isOverview", source = "dto.isOverview") | ||||||
| @Mapping(target = "isLiked", source = "isLiked") | ||||||
| @Mapping(target = "isWriter", source = "isWriter") | ||||||
| @Mapping(target = "isLocked", source = "isLocked") | ||||||
| @Mapping(target = "voteItems", source = "voteItems") | ||||||
| RecordSearchResponse.PostDto toPostDto( | ||||||
| PostQueryDto dto, | ||||||
| String content, | ||||||
| boolean isLiked, | ||||||
| boolean isWriter, | ||||||
| boolean isLocked, | ||||||
| List<RecordSearchResponse.PostDto.VoteItemDto> voteItems | ||||||
| ); | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,15 +6,16 @@ | |||||||||||||
| import konkuk.thip.common.exception.code.ErrorCode; | ||||||||||||||
| import konkuk.thip.common.util.Cursor; | ||||||||||||||
| import konkuk.thip.common.util.CursorBasedList; | ||||||||||||||
| import konkuk.thip.common.util.DateUtil; | ||||||||||||||
| import konkuk.thip.post.application.port.out.PostLikeQueryPort; | ||||||||||||||
| import konkuk.thip.record.adapter.in.web.response.RecordSearchResponse; | ||||||||||||||
| import konkuk.thip.record.adapter.out.persistence.RecordSearchSortParams; | ||||||||||||||
| import konkuk.thip.record.adapter.out.persistence.RecordSearchTypeParams; | ||||||||||||||
| import konkuk.thip.record.application.mapper.RecordQueryMapper; | ||||||||||||||
| import konkuk.thip.record.application.port.in.dto.RecordSearchQuery; | ||||||||||||||
| import konkuk.thip.record.application.port.in.dto.RecordSearchUseCase; | ||||||||||||||
| import konkuk.thip.record.application.port.out.RecordQueryPort; | ||||||||||||||
| import konkuk.thip.record.application.port.out.dto.PostQueryDto; | ||||||||||||||
| import konkuk.thip.record.application.service.validator.RecordAccessValidator; | ||||||||||||||
| import konkuk.thip.room.application.port.out.RoomParticipantCommandPort; | ||||||||||||||
| import konkuk.thip.room.domain.RoomParticipant; | ||||||||||||||
| import konkuk.thip.vote.application.port.out.VoteQueryPort; | ||||||||||||||
|
|
@@ -32,6 +33,7 @@ | |||||||||||||
| import java.util.stream.IntStream; | ||||||||||||||
|
|
||||||||||||||
| import static konkuk.thip.common.post.PostType.RECORD; | ||||||||||||||
| import static konkuk.thip.common.post.PostType.VOTE; | ||||||||||||||
|
|
||||||||||||||
| @Slf4j | ||||||||||||||
| @Service | ||||||||||||||
|
|
@@ -44,8 +46,10 @@ public class RecordSearchService implements RecordSearchUseCase { | |||||||||||||
| private final PostLikeQueryPort postLikeQueryPort; | ||||||||||||||
| private final RoomParticipantCommandPort roomParticipantCommandPort; | ||||||||||||||
|
|
||||||||||||||
| private final RecordAccessValidator recordAccessValidator; | ||||||||||||||
| private final RecordQueryMapper recordQueryMapper; | ||||||||||||||
|
|
||||||||||||||
| private static final int DEFAULT_PAGE_SIZE = 10; | ||||||||||||||
| private static final String BLURRED_STRING = "여긴 못 지나가지롱~~"; | ||||||||||||||
|
|
||||||||||||||
| @Override | ||||||||||||||
| @Transactional(readOnly = true) | ||||||||||||||
|
|
@@ -68,27 +72,23 @@ public RecordSearchResponse search(RecordSearchQuery recordSearchQuery) { | |||||||||||||
| // Type에 따라 그룹기록, 내 기록 조회 분기처리 | ||||||||||||||
| CursorBasedList<PostQueryDto> cursorBasedList = switch(RecordSearchTypeParams.from(recordSearchQuery.type())) { | ||||||||||||||
| case GROUP -> { | ||||||||||||||
| validateGroupRecordFilters(pageStart, pageEnd, isPageFilter, isOverview, book.getPageCount(), roomParticipant.getUserPercentage()); | ||||||||||||||
| recordAccessValidator.validateGroupRecordFilters(pageStart, pageEnd, isPageFilter, isOverview, book.getPageCount(), roomParticipant.getUserPercentage()); | ||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Boolean null 처리 누락으로 NPE 가능 — null-safe 비교로 수정 필요
적용 diff: - if(!isOverview) {
+ if (Boolean.FALSE.equals(isOverview)) {
if(pageStart == null) pageStart = 0;
if(pageEnd == null) pageEnd = book.getPageCount();
}추가로, 기본값 주입 로직( Also applies to: 78-80 🤖 Prompt for AI Agents |
||||||||||||||
| // 총평 보기가 아닌 경우, pageStart와 pageEnd를 default 값 주입 | ||||||||||||||
| if(!isOverview) { | ||||||||||||||
| if(pageStart == null) { | ||||||||||||||
| pageStart = 0; | ||||||||||||||
| } | ||||||||||||||
| if(pageEnd == null) { | ||||||||||||||
| pageEnd = book.getPageCount(); | ||||||||||||||
| } | ||||||||||||||
| if(pageStart == null) pageStart = 0; | ||||||||||||||
| if(pageEnd == null) pageEnd = book.getPageCount(); | ||||||||||||||
| } | ||||||||||||||
| yield getGroupRecordBySortParams(recordSearchQuery.sort(), roomId, userId, cursor, pageStart, pageEnd, isOverview); | ||||||||||||||
| } | ||||||||||||||
| case MINE -> { | ||||||||||||||
| validateMyRecordFilters(pageStart, pageEnd, isPageFilter, isOverview, recordSearchQuery.sort()); | ||||||||||||||
| recordAccessValidator.validateMyRecordFilters(pageStart, pageEnd, isPageFilter, isOverview, recordSearchQuery.sort()); | ||||||||||||||
| yield recordQueryPort.searchMyRecords(roomId, userId, cursor); | ||||||||||||||
| } | ||||||||||||||
|
Comment on lines
+84
to
86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainMINE 케이스에 정렬(sort) 파라미터가 반영되지 않습니다 — 요구사항 확인 필요
필요시, GROUP과 유사한 정렬 위임 메서드를 도입하는 방안을 고려해 주세요. 아래 스크립트로 포트/리포지토리에 MINE 정렬 메서드 존재 여부를 확인할 수 있습니다. 🏁 Script executed: #!/bin/bash
# MINE 정렬 관련 메서드 존재 여부 확인
rg -n "searchMyRecords\(" -A 3
rg -n "searchMyRecordsBy" -A 2
rg -n "RecordSearchSortParams" -A 10
fd -a "RecordQueryPort.java" | xargs -I{} rg -n "searchMyRecords" {}
fd -a "RecordQueryRepository" | xargs -I{} rg -n "searchMyRecords" {}Length of output: 7788 MINE 케이스에 sort 파라미터가 실제로 적용되지 않음 — 구현 수정 필요
확인 위치:
🤖 Prompt for AI Agents |
||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| // VoteItem 한번에 조회 (투표 게시물에 대한 투표 항목 조회) | ||||||||||||||
| Map<Long, List<VoteItemQueryDto>> voteItemQueryMap = voteQueryPort.findVoteItemsByVoteIds(cursorBasedList.contents().stream() | ||||||||||||||
| .filter(postQueryDto -> postQueryDto.postType().equals("VOTE")) | ||||||||||||||
| .filter(postQueryDto -> postQueryDto.postType().equals(VOTE.getType())) | ||||||||||||||
| .map(PostQueryDto::postId) | ||||||||||||||
| .collect(Collectors.toSet()), userId); | ||||||||||||||
|
Comment on lines
+91
to
93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NPE 위험: equals 호출 순서 통일 필요 (상수-우선 비교)
적용 diff: - .filter(postQueryDto -> postQueryDto.postType().equals(VOTE.getType()))
+ .filter(postQueryDto -> VOTE.getType().equals(postQueryDto.postType()))📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
|
|
@@ -99,7 +99,17 @@ public RecordSearchResponse search(RecordSearchQuery recordSearchQuery) { | |||||||||||||
|
|
||||||||||||||
| // 게시물 DTO 변환 | ||||||||||||||
| var postDtos = cursorBasedList.contents().stream() | ||||||||||||||
| .map(postQueryDto -> toPostDto(postQueryDto, roomParticipant, userId, voteItemQueryMap, likedPostIds)) | ||||||||||||||
| .map(dto -> { | ||||||||||||||
| boolean isLocked = recordAccessValidator.isLocked(roomParticipant.getCurrentPage(), dto.page()); | ||||||||||||||
| boolean isWriter = dto.userId().equals(userId); | ||||||||||||||
| boolean isLiked = likedPostIds.contains(dto.postId()); | ||||||||||||||
| String content = isLocked ? recordAccessValidator.createBlurredString(dto.content()) : dto.content(); | ||||||||||||||
|
|
||||||||||||||
| List<RecordSearchResponse.PostDto.VoteItemDto> voteItems = | ||||||||||||||
| getVoteItemDtosIfApplicable(dto, voteItemQueryMap, isLocked); | ||||||||||||||
|
|
||||||||||||||
| return recordQueryMapper.toPostDto(dto, content, isLiked, isWriter, isLocked, voteItems); | ||||||||||||||
| }) | ||||||||||||||
|
Comment on lines
+102
to
+112
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain잠재적 NPE 및 소폭의 가독성 개선 제안
적용 diff: - boolean isWriter = dto.userId().equals(userId);
+ boolean isWriter = java.util.Objects.equals(dto.userId(), userId);추가(선택): 파일 상단 import로 대체 가능 import java.util.Objects;🏁 Script executed: #!/bin/bash
# RecordSearchService 파일에서 map(dto -> ...) 주변 코드 확인
sed -n '80,140p' src/main/java/konkuk/thip/record/application/service/RecordSearchService.java
# RecordAccessValidator에서 createBlurredString의 null 처리 확인
fd RecordAccessValidator.java | xargs sed -n '1,200p'Length of output: 6849 Objects.equals로 NPE 방지 제안
수정 위치:
적용 예시 diff: - boolean isWriter = dto.userId().equals(userId);
+ boolean isWriter = Objects.equals(dto.userId(), userId);선택: 파일 상단에 import 추가 import java.util.Objects;🤖 Prompt for AI Agents |
||||||||||||||
| .toList(); | ||||||||||||||
|
|
||||||||||||||
| // RecordSearchResponse 생성 | ||||||||||||||
|
|
@@ -113,6 +123,7 @@ public RecordSearchResponse search(RecordSearchQuery recordSearchQuery) { | |||||||||||||
| .build(); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // 그룹 기록을 정렬 파라미터에 따라 조회하는 메서드 | ||||||||||||||
| private CursorBasedList<PostQueryDto> getGroupRecordBySortParams(String sort, Long roomId, Long userId, Cursor cursor, Integer pageStart, Integer pageEnd, Boolean isOverview) { | ||||||||||||||
| return switch(RecordSearchSortParams.from(sort)) { | ||||||||||||||
| case LATEST -> recordQueryPort.searchGroupRecordsByLatest(roomId, userId, cursor, pageStart, pageEnd, isOverview); | ||||||||||||||
|
|
@@ -121,29 +132,7 @@ private CursorBasedList<PostQueryDto> getGroupRecordBySortParams(String sort, Lo | |||||||||||||
| }; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| private RecordSearchResponse.PostDto toPostDto(PostQueryDto dto, RoomParticipant participant, Long userId, Map<Long, List<VoteItemQueryDto>> voteItemMap, Set<Long> likedPostIds) { | ||||||||||||||
| boolean isLocked = participant.getCurrentPage() < dto.page(); | ||||||||||||||
| boolean isWriter = dto.userId().equals(userId); | ||||||||||||||
| String content = isLocked ? createBlurredString(dto.content()) : dto.content(); | ||||||||||||||
|
|
||||||||||||||
| return RecordSearchResponse.PostDto.builder() | ||||||||||||||
| .postId(dto.postId()) | ||||||||||||||
| .postDate(DateUtil.formatBeforeTime(dto.postDate())) | ||||||||||||||
| .postType(dto.postType()) | ||||||||||||||
| .page(dto.page()) | ||||||||||||||
| .userId(dto.userId()) | ||||||||||||||
| .nickName(dto.nickName()) | ||||||||||||||
| .profileImageUrl(dto.profileImageUrl()) | ||||||||||||||
| .content(content) | ||||||||||||||
| .likeCount(dto.likeCount()) | ||||||||||||||
| .commentCount(dto.commentCount()) | ||||||||||||||
| .isLiked(likedPostIds.contains(dto.postId())) | ||||||||||||||
| .isWriter(isWriter) | ||||||||||||||
| .isLocked(isLocked) | ||||||||||||||
| .voteItems(getVoteItemDtosIfApplicable(dto, voteItemMap, isLocked)) | ||||||||||||||
| .build(); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // 투표 게시물인 경우 VoteItem DTO 목록을 생성하는 메서드 | ||||||||||||||
| private List<RecordSearchResponse.PostDto.VoteItemDto> getVoteItemDtosIfApplicable(PostQueryDto dto, Map<Long, List<VoteItemQueryDto>> voteItemMap, boolean isLocked) { | ||||||||||||||
| if (RECORD.getType().equals(dto.postType())) { | ||||||||||||||
| return List.of(); | ||||||||||||||
|
|
@@ -153,6 +142,7 @@ private List<RecordSearchResponse.PostDto.VoteItemDto> getVoteItemDtosIfApplicab | |||||||||||||
| return mapToVoteItemDtos(items, isLocked); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // VoteItemQueryDto 목록을 RecordSearchResponse.PostDto.VoteItemDto 목록으로 변환하는 메서드 | ||||||||||||||
| private List<RecordSearchResponse.PostDto.VoteItemDto> mapToVoteItemDtos(List<VoteItemQueryDto> items, boolean isLocked) { | ||||||||||||||
| // voteCount를 모아 리스트로 변환 | ||||||||||||||
| List<Integer> counts = items.stream() | ||||||||||||||
|
|
@@ -166,71 +156,12 @@ private List<RecordSearchResponse.PostDto.VoteItemDto> mapToVoteItemDtos(List<Vo | |||||||||||||
| return IntStream.range(0, items.size()) | ||||||||||||||
| .mapToObj(i -> RecordSearchResponse.PostDto.VoteItemDto.of( | ||||||||||||||
| items.get(i).voteItemId(), | ||||||||||||||
| isLocked ? createBlurredString(items.get(i).itemName()) : items.get(i).itemName(), | ||||||||||||||
| isLocked ? recordAccessValidator.createBlurredString(items.get(i).itemName()) : items.get(i).itemName(), | ||||||||||||||
| percentages.get(i), | ||||||||||||||
| items.get(i).isVoted() | ||||||||||||||
| )) | ||||||||||||||
| .toList(); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| private String createBlurredString(String contents) { | ||||||||||||||
| if (contents == null || contents.isEmpty()) { | ||||||||||||||
| return contents; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| int originalLength = contents.length(); | ||||||||||||||
| int blurLen = BLURRED_STRING.length(); | ||||||||||||||
|
|
||||||||||||||
| // 필요한 전체 반복 횟수 계산 | ||||||||||||||
| int repeat = originalLength / blurLen; | ||||||||||||||
|
|
||||||||||||||
| StringBuilder sb = new StringBuilder(originalLength); | ||||||||||||||
|
|
||||||||||||||
| // 몫 만큼 반복 | ||||||||||||||
| for (int i = 0; i < repeat + 1; i++) { | ||||||||||||||
| sb.append(BLURRED_STRING); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| return sb.toString(); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| private void validateGroupRecordFilters(Integer pageStart, Integer pageEnd, Boolean isPageFilter, Boolean isOverview, int bookPageSize, double currentPercentage) { | ||||||||||||||
| if(!isPageFilter && !isOverview) { // 어떤 필터도 적용되지 않는 경우 | ||||||||||||||
| if (pageStart != null || pageEnd != null) { | ||||||||||||||
| throw new BusinessException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("어떤 필터도 적용되지 않는 경우 pageStart와 pageEnd는 null이어야 합니다.")); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| if(!isPageFilter && isOverview) { // 총평보기 필터만 적용된 경우 | ||||||||||||||
| if (pageStart != null || pageEnd != null) { | ||||||||||||||
| throw new BusinessException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("총평보기 필터만 적용된 경우 pageStart와 pageEnd는 null이어야 합니다.")); | ||||||||||||||
| } | ||||||||||||||
| if (currentPercentage < 80) { | ||||||||||||||
| throw new BusinessException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("총평보기 필터가 적용된 경우 현재 독서 진행률은 80% 이상이어야 합니다.")); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| if(isPageFilter && !isOverview) { // 페이지 필터만 적용된 경우는 pageStart와 pageEnd가 null이여도 됨 | ||||||||||||||
| if(pageStart != null && (pageStart < 0 || pageStart > bookPageSize)) { | ||||||||||||||
| throw new BusinessException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("pageStart는 책의 페이지 범위 내에 있어야 합니다.")); | ||||||||||||||
| } | ||||||||||||||
| if(pageEnd != null && (pageEnd < 0 || pageEnd > bookPageSize)) { | ||||||||||||||
| throw new BusinessException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("pageEnd는 책의 페이지 범위 내에 있어야 합니다.")); | ||||||||||||||
| } | ||||||||||||||
| if(pageStart != null && pageEnd != null && pageStart > pageEnd) { | ||||||||||||||
| throw new BusinessException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("pageStart는 pageEnd보다 작아야 합니다.")); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| if(isPageFilter && isOverview) { // 페이지 필터와 총평보기 필터가 동시에 적용된 경우 | ||||||||||||||
| throw new BusinessException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("페이지 필터와 총평보기 필터는 동시에 적용될 수 없습니다.")); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| private void validateMyRecordFilters(Integer pageStart, Integer pageEnd, Boolean isPageFilter, Boolean isOverview, String sort) { | ||||||||||||||
| // 모든 파라미터중 하나라도 null이 아닌 경우 예외 발생 | ||||||||||||||
| if (pageStart != null || pageEnd != null || isPageFilter || isOverview || sort != null) { | ||||||||||||||
| throw new BusinessException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("내 기록 조회에서는 roomId, type, cursor를 제외한 모든 파라미터는 null이어야 합니다.")); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
도커 컨테이너 실행시 JVM 의 힙 메모리 크기를 제한하셨군요!
좀 찾아보니 컨테이너 전체의 메모리를 제한하고, 이 중 일정 비율을 힙 메모리에 할당하는 방식도 있는 것 같은데, 저희는 우선 테이블 구조 수정으로 쿼리 성능을 먼저 개선하는 것이 최우선순위 인 것 같네요 하하