Skip to content

Commit ba620e1

Browse files
committed
feat: Diary 다건 조회 시 작성자 정보 같이 반환
1 parent f2a7e96 commit ba620e1

File tree

7 files changed

+197
-111
lines changed

7 files changed

+197
-111
lines changed

src/main/java/com/example/log4u/domain/diary/dto/DiaryResponseDto.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,31 @@ public static DiaryResponseDto of(
8686
.build();
8787
}
8888

89+
// DiaryWithAuthorDto 전용 메서드
90+
public static DiaryResponseDto of(
91+
DiaryWithAuthorDto dto,
92+
List<Media> media,
93+
List<String> hashtagList
94+
) {
95+
return DiaryResponseDto.builder()
96+
.diaryId(dto.diary().getDiaryId())
97+
.authorId(dto.diary().getUserId())
98+
.authorNickname(dto.authorNickname())
99+
.authorProfileImage(dto.authorProfileImage())
100+
.location(LocationDto.of(dto.diary().getLocation()))
101+
.title(dto.diary().getTitle())
102+
.content(dto.diary().getContent())
103+
.weatherInfo(dto.diary().getWeatherInfo().name())
104+
.visibility(dto.diary().getVisibility().name())
105+
.createdAt(dto.diary().getCreatedAt())
106+
.updatedAt(dto.diary().getUpdatedAt())
107+
.thumbnailUrl(dto.diary().getThumbnailUrl())
108+
.likeCount(dto.diary().getLikeCount())
109+
.mediaList(media.stream()
110+
.map(MediaResponseDto::of).toList())
111+
.hashtagList(hashtagList)
112+
.isLiked(false)
113+
.build();
114+
}
115+
89116
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.example.log4u.domain.diary.dto;
2+
3+
import com.example.log4u.domain.diary.entity.Diary;
4+
5+
public record DiaryWithAuthorDto(
6+
Diary diary,
7+
String authorNickname,
8+
String authorProfileImage
9+
) {
10+
}

src/main/java/com/example/log4u/domain/diary/repository/CustomDiaryRepository.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,25 @@
22

33
import java.util.List;
44

5-
import org.springframework.data.domain.Page;
65
import org.springframework.data.domain.Pageable;
76
import org.springframework.data.domain.Slice;
87

98
import com.example.log4u.domain.diary.SortType;
109
import com.example.log4u.domain.diary.VisibilityType;
10+
import com.example.log4u.domain.diary.dto.DiaryWithAuthorDto;
1111
import com.example.log4u.domain.diary.entity.Diary;
1212
import com.example.log4u.domain.map.dto.response.DiaryMarkerResponseDto;
1313

1414
public interface CustomDiaryRepository {
15-
Page<Diary> searchDiaries(
16-
String keyword,
17-
List<VisibilityType> visibilities,
18-
SortType sort,
19-
Pageable pageable
20-
);
2115

22-
Slice<Diary> findByUserIdAndVisibilityInAndCursorId(
16+
Slice<DiaryWithAuthorDto> findByUserIdAndVisibilityInAndCursorId(
2317
Long userId,
2418
List<VisibilityType> visibilities,
2519
Long cursorId,
2620
Pageable pageable
2721
);
2822

29-
Slice<Diary> searchDiariesByCursor(
23+
Slice<DiaryWithAuthorDto> searchDiariesByCursor(
3024
String keyword,
3125
List<VisibilityType> visibilities,
3226
SortType sort,

src/main/java/com/example/log4u/domain/diary/repository/CustomDiaryRepositoryImpl.java

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
import java.util.List;
44

5-
import org.springframework.data.domain.Page;
6-
import org.springframework.data.domain.PageImpl;
75
import org.springframework.data.domain.Pageable;
86
import org.springframework.data.domain.Slice;
97
import org.springframework.stereotype.Repository;
@@ -12,18 +10,18 @@
1210
import com.example.log4u.common.util.PageableUtil;
1311
import com.example.log4u.domain.diary.SortType;
1412
import com.example.log4u.domain.diary.VisibilityType;
13+
import com.example.log4u.domain.diary.dto.DiaryWithAuthorDto;
1514
import com.example.log4u.domain.diary.entity.Diary;
1615
import com.example.log4u.domain.diary.entity.QDiary;
1716
import com.example.log4u.domain.hashtag.entity.QDiaryHashtag;
1817
import com.example.log4u.domain.hashtag.entity.QHashtag;
1918
import com.example.log4u.domain.like.entity.QLike;
2019
import com.example.log4u.domain.map.dto.response.DiaryMarkerResponseDto;
21-
import com.example.log4u.domain.media.entity.QMedia;
20+
import com.example.log4u.domain.user.entity.QUser;
2221
import com.querydsl.core.types.OrderSpecifier;
2322
import com.querydsl.core.types.Projections;
2423
import com.querydsl.core.types.dsl.BooleanExpression;
2524
import com.querydsl.jpa.JPAExpressions;
26-
import com.querydsl.jpa.impl.JPAQuery;
2725
import com.querydsl.jpa.impl.JPAQueryFactory;
2826

2927
import lombok.RequiredArgsConstructor;
@@ -38,42 +36,10 @@ public class CustomDiaryRepositoryImpl implements CustomDiaryRepository {
3836
private final QLike like = QLike.like;
3937
private final QDiaryHashtag diaryHashtag = QDiaryHashtag.diaryHashtag;
4038
private final QHashtag hashtag = QHashtag.hashtag;
41-
private final QMedia media = QMedia.media;
39+
private final QUser user = QUser.user;
4240

4341
@Override
44-
public Page<Diary> searchDiaries(
45-
String keyword,
46-
List<VisibilityType> visibilities,
47-
SortType sort,
48-
Pageable pageable
49-
) {
50-
// 조건 생성
51-
BooleanExpression condition = createSearchCondition(keyword, visibilities, null);
52-
53-
// 쿼리 실행
54-
JPAQuery<Diary> query = queryFactory
55-
.selectFrom(diary)
56-
.where(condition);
57-
58-
// 전체 카운트 조회
59-
Long total = queryFactory
60-
.select(diary.count())
61-
.from(diary)
62-
.where(condition)
63-
.fetchOne();
64-
65-
// 데이터 조회
66-
List<Diary> content = query
67-
.orderBy(createOrderSpecifier(sort))
68-
.offset(pageable.getOffset())
69-
.limit(pageable.getPageSize())
70-
.fetch();
71-
72-
return new PageImpl<>(content, pageable, total != null ? total : 0);
73-
}
74-
75-
@Override
76-
public Slice<Diary> findByUserIdAndVisibilityInAndCursorId(
42+
public Slice<DiaryWithAuthorDto> findByUserIdAndVisibilityInAndCursorId(
7743
Long userId,
7844
List<VisibilityType> visibilities,
7945
Long cursorId,
@@ -104,9 +70,15 @@ public Slice<Diary> findByUserIdAndVisibilityInAndCursorId(
10470
}
10571
}
10672

107-
// limit + 1로 다음 페이지 존재 여부 확인
108-
List<Diary> content = queryFactory
109-
.selectFrom(diary)
73+
// 다이어리와 작성자 정보를 함께 조회
74+
List<DiaryWithAuthorDto> content = queryFactory
75+
.select(Projections.constructor(DiaryWithAuthorDto.class,
76+
diary,
77+
user.nickname,
78+
user.profileImage
79+
))
80+
.from(diary)
81+
.join(user).on(diary.userId.eq(user.userId))
11082
.where(condition)
11183
.orderBy(diary.createdAt.desc(), diary.diaryId.desc())
11284
.limit(pageable.getPageSize() + 1)
@@ -117,7 +89,7 @@ public Slice<Diary> findByUserIdAndVisibilityInAndCursorId(
11789
}
11890

11991
@Override
120-
public Slice<Diary> searchDiariesByCursor(
92+
public Slice<DiaryWithAuthorDto> searchDiariesByCursor(
12193
String keyword,
12294
List<VisibilityType> visibilities,
12395
SortType sort,
@@ -162,9 +134,15 @@ public Slice<Diary> searchDiariesByCursor(
162134
}
163135
}
164136

165-
// limit + 1로 다음 페이지 존재 여부 확인
166-
List<Diary> content = queryFactory
167-
.selectFrom(diary)
137+
// 다이어리와 작성자 정보를 함께 조회
138+
List<DiaryWithAuthorDto> content = queryFactory
139+
.select(Projections.constructor(DiaryWithAuthorDto.class,
140+
diary,
141+
user.nickname,
142+
user.profileImage
143+
))
144+
.from(diary)
145+
.join(user).on(diary.userId.eq(user.userId))
168146
.where(condition)
169147
.orderBy(createOrderSpecifier(sort))
170148
.limit(pageable.getPageSize() + 1)

src/main/java/com/example/log4u/domain/diary/service/DiaryService.java

Lines changed: 77 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.example.log4u.domain.diary.VisibilityType;
1717
import com.example.log4u.domain.diary.dto.DiaryRequestDto;
1818
import com.example.log4u.domain.diary.dto.DiaryResponseDto;
19+
import com.example.log4u.domain.diary.dto.DiaryWithAuthorDto;
1920
import com.example.log4u.domain.diary.entity.Diary;
2021
import com.example.log4u.domain.diary.exception.NotFoundDiaryException;
2122
import com.example.log4u.domain.diary.exception.OwnerAccessDeniedException;
@@ -54,27 +55,27 @@ public Slice<DiaryResponseDto> searchDiariesByCursor(
5455
Long cursorId,
5556
int size
5657
) {
57-
Slice<Diary> diaries = diaryRepository.searchDiariesByCursor(
58+
Slice<DiaryWithAuthorDto> diaries = diaryRepository.searchDiariesByCursor(
5859
keyword,
5960
List.of(VisibilityType.PUBLIC),
6061
sort,
6162
cursorId != null ? cursorId : Long.MAX_VALUE,
6263
PageRequest.of(0, size)
6364
);
64-
return mapToDtoSlice(diaries);
65+
return this.mapDiaryWithAuthorToDtoSlice(diaries);
6566
}
6667

6768
// 다이어리 목록 (프로필 페이지)
6869
@Transactional(readOnly = true)
6970
public Slice<DiaryResponseDto> getDiaryResponseDtoSlice(Long userId, Long targetUserId, Long cursorId, int size) {
7071
List<VisibilityType> visibilities = determineAccessibleVisibilities(userId, targetUserId);
71-
Slice<Diary> diaries = diaryRepository.findByUserIdAndVisibilityInAndCursorId(
72+
Slice<DiaryWithAuthorDto> diaries = diaryRepository.findByUserIdAndVisibilityInAndCursorId(
7273
targetUserId,
7374
visibilities,
7475
cursorId != null ? cursorId : Long.MAX_VALUE,
7576
PageRequest.of(0, size)
7677
);
77-
return mapToDtoSlice(diaries);
78+
return this.mapDiaryWithAuthorToDtoSlice(diaries);
7879
}
7980

8081
// 다이어리 수정
@@ -90,25 +91,93 @@ public void deleteDiary(Diary diary) {
9091
diaryRepository.delete(diary);
9192
}
9293

94+
@Transactional(readOnly = true)
95+
public PageResponse<DiaryResponseDto> getMyDiariesByCursor(Long userId, VisibilityType visibilityType,
96+
Long cursorId, int size) {
97+
List<VisibilityType> visibilities =
98+
visibilityType == null ? List.of(VisibilityType.PUBLIC, VisibilityType.PRIVATE, VisibilityType.FOLLOWER) :
99+
List.of(visibilityType);
100+
101+
Slice<DiaryWithAuthorDto> diaries = diaryRepository.findByUserIdAndVisibilityInAndCursorId(
102+
userId,
103+
visibilities,
104+
cursorId != null ? cursorId : Long.MAX_VALUE,
105+
PageRequest.of(0, size)
106+
);
107+
108+
Slice<DiaryResponseDto> dtoSlice = this.mapDiaryWithAuthorToDtoSlice(diaries);
109+
110+
Long nextCursor = !dtoSlice.isEmpty() ? dtoSlice.getContent().getLast().diaryId() : null;
111+
112+
return PageResponse.of(dtoSlice, nextCursor);
113+
}
114+
115+
@Transactional(readOnly = true)
116+
public PageResponse<DiaryResponseDto> getLikeDiariesByCursor(Long userId, Long targetUserId, Long cursorId,
117+
int size) {
118+
List<VisibilityType> visibilities = determineAccessibleVisibilities(userId, targetUserId);
119+
120+
Slice<Diary> diaries = diaryRepository.getLikeDiarySliceByUserId(
121+
targetUserId,
122+
visibilities,
123+
cursorId != null ? cursorId : Long.MAX_VALUE,
124+
PageRequest.of(0, size)
125+
);
126+
127+
Slice<DiaryResponseDto> dtoSlice = this.mapDiarySliceToDtoSlice(diaries);
128+
129+
Long nextCursor = !dtoSlice.isEmpty() ? dtoSlice.getContent().getLast().diaryId() : null;
130+
131+
return PageResponse.of(dtoSlice, nextCursor);
132+
}
133+
93134
private Diary findDiaryOrThrow(Long diaryId) {
94135
return diaryRepository.findById(diaryId)
95136
.orElseThrow(NotFoundDiaryException::new);
96137
}
97138

98139
// Page용 매핑 메서드
99140
private Page<DiaryResponseDto> mapToDtoPage(Page<Diary> page) {
100-
List<DiaryResponseDto> content = getDiaryResponsesWithMediaAndHashtags(page.getContent());
141+
List<DiaryResponseDto> content = getDiaryResponse(page.getContent());
101142
return new PageImpl<>(content, page.getPageable(), page.getTotalElements());
102143
}
103144

104145
// Slice용 매핑 메서드
105-
private Slice<DiaryResponseDto> mapToDtoSlice(Slice<Diary> slice) {
106-
List<DiaryResponseDto> content = getDiaryResponsesWithMediaAndHashtags(slice.getContent());
146+
private Slice<DiaryResponseDto> mapDiarySliceToDtoSlice(Slice<Diary> slice) {
147+
List<DiaryResponseDto> content = getDiaryResponse(slice.getContent());
148+
return new SliceImpl<>(content, slice.getPageable(), slice.hasNext());
149+
}
150+
151+
// DiaryWithAuthor Slice 매핑 메서드
152+
private Slice<DiaryResponseDto> mapDiaryWithAuthorToDtoSlice(Slice<DiaryWithAuthorDto> slice) {
153+
List<DiaryResponseDto> content = getDiaryResponseWithAuthor(slice.getContent());
107154
return new SliceImpl<>(content, slice.getPageable(), slice.hasNext());
108155
}
109156

157+
// 다이어리 + 작성자 + 미디어 + 해시태그 같이 반환
158+
private List<DiaryResponseDto> getDiaryResponseWithAuthor(List<DiaryWithAuthorDto> diaryWithAuthorDtoList) {
159+
if (diaryWithAuthorDtoList.isEmpty()) {
160+
return List.of();
161+
}
162+
163+
List<Long> diaryIds = diaryWithAuthorDtoList.stream()
164+
.map(dto -> dto.diary().getDiaryId())
165+
.toList();
166+
167+
Map<Long, List<Media>> mediaMap = mediaService.getMediaMapByDiaryIds(diaryIds);
168+
Map<Long, List<String>> hashtagMap = hashtagService.getHashtagMapByDiaryIds(diaryIds);
169+
170+
return diaryWithAuthorDtoList.stream()
171+
.map(dto -> DiaryResponseDto.of(
172+
dto,
173+
mediaMap.getOrDefault(dto.diary().getDiaryId(), List.of()),
174+
hashtagMap.getOrDefault(dto.diary().getDiaryId(), List.of())
175+
))
176+
.toList();
177+
}
178+
110179
// 다이어리 + 미디어 + 해시태그 같이 반환
111-
private List<DiaryResponseDto> getDiaryResponsesWithMediaAndHashtags(List<Diary> diaries) {
180+
private List<DiaryResponseDto> getDiaryResponse(List<Diary> diaries) {
112181
if (diaries.isEmpty()) {
113182
return List.of();
114183
}
@@ -204,44 +273,4 @@ public void checkDiaryExists(Long diaryId) {
204273
throw new NotFoundDiaryException();
205274
}
206275
}
207-
208-
@Transactional(readOnly = true)
209-
public PageResponse<DiaryResponseDto> getMyDiariesByCursor(Long userId, VisibilityType visibilityType,
210-
Long cursorId, int size) {
211-
List<VisibilityType> visibilities =
212-
visibilityType == null ? List.of(VisibilityType.PUBLIC, VisibilityType.PRIVATE, VisibilityType.FOLLOWER) :
213-
List.of(visibilityType);
214-
215-
Slice<Diary> diaries = diaryRepository.findByUserIdAndVisibilityInAndCursorId(
216-
userId,
217-
visibilities,
218-
cursorId != null ? cursorId : Long.MAX_VALUE,
219-
PageRequest.of(0, size)
220-
);
221-
222-
Slice<DiaryResponseDto> dtoSlice = mapToDtoSlice(diaries);
223-
224-
Long nextCursor = !dtoSlice.isEmpty() ? dtoSlice.getContent().getLast().diaryId() : null;
225-
226-
return PageResponse.of(dtoSlice, nextCursor);
227-
}
228-
229-
@Transactional(readOnly = true)
230-
public PageResponse<DiaryResponseDto> getLikeDiariesByCursor(Long userId, Long targetUserId, Long cursorId,
231-
int size) {
232-
List<VisibilityType> visibilities = determineAccessibleVisibilities(userId, targetUserId);
233-
234-
Slice<Diary> diaries = diaryRepository.getLikeDiarySliceByUserId(
235-
targetUserId,
236-
visibilities,
237-
cursorId != null ? cursorId : Long.MAX_VALUE,
238-
PageRequest.of(0, size)
239-
);
240-
241-
Slice<DiaryResponseDto> dtoSlice = mapToDtoSlice(diaries);
242-
243-
Long nextCursor = !dtoSlice.isEmpty() ? dtoSlice.getContent().getLast().diaryId() : null;
244-
245-
return PageResponse.of(dtoSlice, nextCursor);
246-
}
247276
}

0 commit comments

Comments
 (0)