From c65c23b82adbb3deea046cc47a8d1b54de7db45d Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 15 Aug 2025 00:38:49 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[refactor]=20=EB=8F=84=EC=BB=A4=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EB=B9=8C=EB=93=9C=EC=8B=9C=20JAVA=20?= =?UTF-8?q?=EB=A9=94=EB=AA=A8=EB=A6=AC=20=EC=98=B5=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#225)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 2d44f631b..ad9cf12d2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ FROM openjdk:17 ARG PORT=8000 +ENV JAVA_TOOL_OPTIONS="-Xms512m -Xmx2g -XX:+ExitOnOutOfMemoryError" EXPOSE ${PORT} From 661ec8973da47b8a5d877a991a84d82ef2974f06 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 15 Aug 2025 00:38:59 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[refactor]=20instance=20of=EB=A5=BC=20?= =?UTF-8?q?=EB=AA=A8=EB=91=90=20dtype=EC=9D=84=20=ED=86=B5=ED=95=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A5=98=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#225)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/RecordQueryRepositoryImpl.java | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordQueryRepositoryImpl.java b/src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordQueryRepositoryImpl.java index f7082b7ed..1f530f321 100644 --- a/src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordQueryRepositoryImpl.java +++ b/src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordQueryRepositoryImpl.java @@ -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())) .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 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 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, From 082151c71dc96c753f7b138cd1b24aa0bbd43e34 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 15 Aug 2025 01:37:39 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[refactor]=20=ED=97=AC=ED=8D=BC=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20(#225)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/mapper/RecordQueryMapper.java | 37 ++++++ .../service/RecordSearchService.java | 121 ++++-------------- .../validator/RecordAccessValidator.java | 74 +++++++++++ 3 files changed, 137 insertions(+), 95 deletions(-) create mode 100644 src/main/java/konkuk/thip/record/application/mapper/RecordQueryMapper.java create mode 100644 src/main/java/konkuk/thip/record/application/service/validator/RecordAccessValidator.java diff --git a/src/main/java/konkuk/thip/record/application/mapper/RecordQueryMapper.java b/src/main/java/konkuk/thip/record/application/mapper/RecordQueryMapper.java new file mode 100644 index 000000000..9bd15409d --- /dev/null +++ b/src/main/java/konkuk/thip/record/application/mapper/RecordQueryMapper.java @@ -0,0 +1,37 @@ +package konkuk.thip.record.application.mapper; + +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", unmappedTargetPolicy = ReportingPolicy.ERROR) +public interface RecordQueryMapper { + + @Mapping(target = "postId", source = "dto.postId") + @Mapping(target = "postDate", expression = "java(DateUtil.formatBeforeTime(dto.postDate()))") + @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 voteItems + ); +} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java index 387cdbbd5..9776ec281 100644 --- a/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java +++ b/src/main/java/konkuk/thip/record/application/service/RecordSearchService.java @@ -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 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()); // 총평 보기가 아닌 경우, 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); } }; // VoteItem 한번에 조회 (투표 게시물에 대한 투표 항목 조회) Map> 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); @@ -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 voteItems = + getVoteItemDtosIfApplicable(dto, voteItemQueryMap, isLocked); + + return recordQueryMapper.toPostDto(dto, content, isLiked, isWriter, isLocked, voteItems); + }) .toList(); // RecordSearchResponse 생성 @@ -113,6 +123,7 @@ public RecordSearchResponse search(RecordSearchQuery recordSearchQuery) { .build(); } + // 그룹 기록을 정렬 파라미터에 따라 조회하는 메서드 private CursorBasedList 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 getGroupRecordBySortParams(String sort, Lo }; } - private RecordSearchResponse.PostDto toPostDto(PostQueryDto dto, RoomParticipant participant, Long userId, Map> voteItemMap, Set 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 getVoteItemDtosIfApplicable(PostQueryDto dto, Map> voteItemMap, boolean isLocked) { if (RECORD.getType().equals(dto.postType())) { return List.of(); @@ -153,6 +142,7 @@ private List getVoteItemDtosIfApplicab return mapToVoteItemDtos(items, isLocked); } + // VoteItemQueryDto 목록을 RecordSearchResponse.PostDto.VoteItemDto 목록으로 변환하는 메서드 private List mapToVoteItemDtos(List items, boolean isLocked) { // voteCount를 모아 리스트로 변환 List counts = items.stream() @@ -166,71 +156,12 @@ private List mapToVoteItemDtos(List 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이어야 합니다.")); - } - - } } diff --git a/src/main/java/konkuk/thip/record/application/service/validator/RecordAccessValidator.java b/src/main/java/konkuk/thip/record/application/service/validator/RecordAccessValidator.java new file mode 100644 index 000000000..d9c598bcb --- /dev/null +++ b/src/main/java/konkuk/thip/record/application/service/validator/RecordAccessValidator.java @@ -0,0 +1,74 @@ +package konkuk.thip.record.application.service.validator; + +import konkuk.thip.common.annotation.HelperService; +import konkuk.thip.common.exception.BusinessException; +import konkuk.thip.common.exception.code.ErrorCode; + +@HelperService +public class RecordAccessValidator { + + private static final String BLURRED_STRING = "여긴 못 지나가지롱~~"; + + public 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("페이지 필터와 총평보기 필터는 동시에 적용될 수 없습니다.")); + } + } + + public 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이어야 합니다.")); + } + + } + + public 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(); + } + + public boolean isLocked(int currentPage, int bookPageSize) { + return currentPage < bookPageSize; + } +} \ No newline at end of file From 69c804751a74e1ba508f66d79356519c5c2ac9ec Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 15 Aug 2025 01:37:46 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[refactor]=20isOverview=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=20(#225)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../record/adapter/in/web/response/RecordSearchResponse.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordSearchResponse.java b/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordSearchResponse.java index 105ea2d3f..8922bb8e2 100644 --- a/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordSearchResponse.java +++ b/src/main/java/konkuk/thip/record/adapter/in/web/response/RecordSearchResponse.java @@ -25,6 +25,7 @@ public record PostDto( String content, int likeCount, int commentCount, + boolean isOverview, boolean isLiked, boolean isWriter, boolean isLocked, From 0d9b6b45bb39b80e76b65751aa8fbc11d9dbb860 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 15 Aug 2025 01:44:22 +0900 Subject: [PATCH 5/5] [refactor] DateUtil import (#225) --- .../thip/record/application/mapper/RecordQueryMapper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/record/application/mapper/RecordQueryMapper.java b/src/main/java/konkuk/thip/record/application/mapper/RecordQueryMapper.java index 9bd15409d..9d30ce5ba 100644 --- a/src/main/java/konkuk/thip/record/application/mapper/RecordQueryMapper.java +++ b/src/main/java/konkuk/thip/record/application/mapper/RecordQueryMapper.java @@ -1,5 +1,6 @@ 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; @@ -8,7 +9,7 @@ import java.util.List; -@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR) +@Mapper(componentModel = "spring", imports = DateUtil.class, unmappedTargetPolicy = ReportingPolicy.ERROR) public interface RecordQueryMapper { @Mapping(target = "postId", source = "dto.postId")