Skip to content

Commit b559217

Browse files
committed
refactor: 코드 리뷰 반영(QueryDSL 적용 전)
1 parent 906220f commit b559217

File tree

6 files changed

+120
-38
lines changed

6 files changed

+120
-38
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.example.log4u.common.dto;
2+
3+
import java.util.List;
4+
5+
import org.springframework.data.domain.Page;
6+
import org.springframework.data.domain.Slice;
7+
8+
public record PageResponse<T>(
9+
List<T> content,
10+
PageInfo pageInfo
11+
) {
12+
// 오프셋 기반 (검색용)
13+
public static <T> PageResponse<T> of(Page<T> page) {
14+
return new PageResponse<>(
15+
page.getContent(),
16+
PageInfo.of(page)
17+
);
18+
}
19+
20+
// 커서 기반 (무한 스크롤용)
21+
public static <T> PageResponse<T> of(Slice<T> slice, Long nextCursor) {
22+
return new PageResponse<>(
23+
slice.getContent(),
24+
PageInfo.of(slice, nextCursor)
25+
);
26+
}
27+
28+
public record PageInfo(
29+
Integer page,
30+
int size,
31+
Long totalElements,
32+
Integer totalPages,
33+
boolean hasNext,
34+
Long nextCursor
35+
) {
36+
// Page용 팩토리 메서드
37+
public static PageInfo of(Page<?> page) {
38+
return new PageInfo(
39+
page.getNumber(),
40+
page.getSize(),
41+
page.getTotalElements(),
42+
page.getTotalPages(),
43+
page.hasNext(),
44+
null
45+
);
46+
}
47+
48+
// Slice용 팩토리 메서드
49+
public static PageInfo of(Slice<?> slice, Long nextCursor) {
50+
return new PageInfo(
51+
null,
52+
slice.getSize(),
53+
null,
54+
null,
55+
slice.hasNext(),
56+
nextCursor
57+
);
58+
}
59+
}
60+
}

src/main/java/com/example/log4u/domain/diary/controller/DiaryController.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.example.log4u.domain.diary.controller;
22

3-
import java.util.List;
4-
53
import org.springframework.http.HttpStatus;
64
import org.springframework.http.ResponseEntity;
75
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -14,6 +12,7 @@
1412
import org.springframework.web.bind.annotation.RequestParam;
1513
import org.springframework.web.bind.annotation.RestController;
1614

15+
import com.example.log4u.common.dto.PageResponse;
1716
import com.example.log4u.domain.diary.dto.DiaryRequestDto;
1817
import com.example.log4u.domain.diary.dto.DiaryResponseDto;
1918
import com.example.log4u.domain.diary.service.DiaryService;
@@ -41,17 +40,14 @@ public ResponseEntity<Void> createDiary(
4140
}
4241

4342
@GetMapping
44-
public ResponseEntity<List<DiaryResponseDto>> searchDiaries(
43+
public ResponseEntity<PageResponse<DiaryResponseDto>> searchDiaries(
4544
@RequestParam(required = false) String keyword,
4645
@RequestParam(defaultValue = "LATEST") String sort,
4746
@RequestParam(defaultValue = "0") int page
4847
) {
49-
List<DiaryResponseDto> diaries = diaryService.searchDiaries(
50-
keyword,
51-
sort,
52-
page
48+
return ResponseEntity.ok(
49+
diaryService.searchDiaries(keyword, sort, page)
5350
);
54-
return ResponseEntity.ok(diaries);
5551
}
5652

5753
@GetMapping("/{diaryId}")

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

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

33
import java.util.List;
44

5+
import com.example.log4u.domain.diary.VisibilityType;
6+
import com.example.log4u.domain.diary.entity.Diary;
57
import com.example.log4u.domain.media.dto.MediaRequestDto;
68

79
import jakarta.validation.constraints.NotBlank;
@@ -18,4 +20,16 @@ public record DiaryRequestDto(
1820
String visibility,
1921
List<MediaRequestDto> mediaList
2022
) {
23+
public static Diary toEntity(Long userId, DiaryRequestDto diaryRequestDto, String thumbnailUrl) {
24+
return Diary.builder()
25+
.userId(userId)
26+
.title(diaryRequestDto.title)
27+
.content(diaryRequestDto.content)
28+
.latitude(diaryRequestDto.latitude)
29+
.longitude(diaryRequestDto.longitude)
30+
.weatherInfo(diaryRequestDto.weatherInfo)
31+
.visibility(VisibilityType.valueOf(diaryRequestDto.visibility))
32+
.thumbnailUrl(thumbnailUrl)
33+
.build();
34+
}
2135
}

src/main/java/com/example/log4u/domain/diary/entity/Diary.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,6 @@ public class Diary extends BaseEntity {
5353
@Builder.Default
5454
private Long likeCount = 0L;
5555

56-
public static Diary toEntity(Long userId, DiaryRequestDto request, String thumbnailUrl) {
57-
return Diary.builder()
58-
.userId(userId)
59-
.title(request.title())
60-
.content(request.content())
61-
.latitude(request.latitude())
62-
.longitude(request.longitude())
63-
.weatherInfo(request.weatherInfo())
64-
.visibility(VisibilityType.valueOf(request.visibility()))
65-
.thumbnailUrl(thumbnailUrl)
66-
.build();
67-
}
68-
6956
public void update(DiaryRequestDto request, String newThumbnailUrl) {
7057
this.title = request.title();
7158
this.content = request.content();

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import java.util.List;
44

5+
import org.springframework.data.domain.Page;
56
import org.springframework.data.domain.Pageable;
7+
import org.springframework.data.domain.Slice;
68
import org.springframework.data.jpa.repository.JpaRepository;
79
import org.springframework.data.jpa.repository.Query;
810
import org.springframework.data.repository.query.Param;
@@ -23,7 +25,7 @@ public interface DiaryRepository extends JpaRepository<Diary, Long> {
2325
CASE WHEN :sort = 'POPULAR' THEN d.likeCount
2426
ELSE d.createdAt END DESC
2527
""")
26-
List<Diary> searchDiaries(
28+
Page<Diary> searchDiaries(
2729
@Param("keyword") String keyword,
2830
@Param("visibilities") List<VisibilityType> visibilities,
2931
@Param("sort") String sort,
@@ -34,10 +36,10 @@ List<Diary> searchDiaries(
3436
SELECT d FROM Diary d
3537
WHERE d.userId = :userId
3638
AND d.visibility IN :visibilities
37-
AND (:cursorId IS NULL OR d.id < :cursorId)
38-
ORDER BY d.id DESC
39+
AND (:cursorId IS NULL OR d.diaryId < :cursorId)
40+
ORDER BY d.diaryId DESC
3941
""")
40-
List<Diary> findByUserIdAndVisibilityInAndCursorId(
42+
Slice<Diary> findByUserIdAndVisibilityInAndCursorId(
4143
@Param("userId") Long userId,
4244
@Param("visibilities") List<VisibilityType> visibilities,
4345
@Param("cursorId") Long cursorId,

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

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
import java.util.List;
44
import java.util.Map;
55

6+
import org.springframework.data.domain.Page;
7+
import org.springframework.data.domain.PageImpl;
68
import org.springframework.data.domain.PageRequest;
9+
import org.springframework.data.domain.Slice;
10+
import org.springframework.data.domain.SliceImpl;
711
import org.springframework.stereotype.Service;
812
import org.springframework.transaction.annotation.Transactional;
913

14+
import com.example.log4u.common.dto.PageResponse;
1015
import com.example.log4u.domain.diary.VisibilityType;
1116
import com.example.log4u.domain.diary.dto.DiaryRequestDto;
1217
import com.example.log4u.domain.diary.dto.DiaryResponseDto;
@@ -40,33 +45,32 @@ public class DiaryService {
4045
public void saveDiary(Long userId, DiaryRequestDto request) {
4146
String thumbnailUrl = mediaService.extractThumbnailUrl(request.mediaList());
4247
Diary diary = diaryRepository.save(
43-
Diary.toEntity(userId, request, thumbnailUrl)
48+
DiaryRequestDto.toEntity(userId, request, thumbnailUrl)
4449
);
4550
mediaService.saveMedia(diary.getDiaryId(), request.mediaList());
4651
}
4752

4853
// 다이어리 검색
4954
@Transactional(readOnly = true)
50-
public List<DiaryResponseDto> searchDiaries(
55+
public PageResponse<DiaryResponseDto> searchDiaries(
5156
String keyword,
5257
String sort,
5358
int page
5459
) {
55-
List<Diary> diaries = diaryRepository.searchDiaries(
60+
Page<Diary> diaryPage = diaryRepository.searchDiaries(
5661
keyword,
5762
List.of(VisibilityType.PUBLIC),
5863
sort,
5964
PageRequest.of(page, SEARCH_PAGE_SIZE)
6065
);
6166

62-
return getDiaryResponsesWithMedia(diaries);
67+
return PageResponse.of(mapToDtoPage(diaryPage));
6368
}
6469

6570
// 다이어리 상세 조회
6671
@Transactional(readOnly = true)
6772
public DiaryResponseDto getDiary(Long userId, Long diaryId) {
68-
Diary diary = diaryRepository.findById(diaryId)
69-
.orElseThrow(NotFoundDiaryException::new);
73+
Diary diary = findDiaryOrThrow(diaryId);
7074

7175
validateDiaryAccess(diary, userId);
7276

@@ -76,24 +80,27 @@ public DiaryResponseDto getDiary(Long userId, Long diaryId) {
7680

7781
// 다이어리 목록 (프로필 페이지)
7882
@Transactional(readOnly = true)
79-
public List<DiaryResponseDto> getDiariesByCursor(Long userId, Long targetUserId, Long cursorId) {
83+
public PageResponse<DiaryResponseDto> getDiariesByCursor(Long userId, Long targetUserId, Long cursorId) {
8084
List<VisibilityType> visibilities = determineAccessibleVisibilities(userId, targetUserId);
8185

82-
List<Diary> diaries = diaryRepository.findByUserIdAndVisibilityInAndCursorId(
86+
Slice<Diary> diaries = diaryRepository.findByUserIdAndVisibilityInAndCursorId(
8387
targetUserId,
8488
visibilities,
8589
cursorId != null ? cursorId : Long.MAX_VALUE,
8690
PageRequest.of(0, CURSOR_PAGE_SIZE)
8791
);
8892

89-
return getDiaryResponsesWithMedia(diaries);
93+
Slice<DiaryResponseDto> dtoSlice = mapToDtoSlice(diaries);
94+
95+
Long nextCursor = !dtoSlice.isEmpty() ? dtoSlice.getContent().getLast().diaryId() : null;
96+
97+
return PageResponse.of(dtoSlice, nextCursor);
9098
}
9199

92100
// 다이어리 수정
93101
@Transactional
94102
public void updateDiary(Long userId, Long diaryId, DiaryRequestDto request) {
95-
Diary diary = diaryRepository.findById(diaryId)
96-
.orElseThrow(NotFoundDiaryException::new);
103+
Diary diary = findDiaryOrThrow(diaryId);
97104

98105
validateDiaryOwner(diary, userId);
99106

@@ -108,15 +115,31 @@ public void updateDiary(Long userId, Long diaryId, DiaryRequestDto request) {
108115
// 다이어리 삭제
109116
@Transactional
110117
public void deleteDiary(Long userId, Long diaryId) {
111-
Diary diary = diaryRepository.findById(diaryId)
112-
.orElseThrow(NotFoundDiaryException::new);
118+
Diary diary = findDiaryOrThrow(diaryId);
113119

114120
validateDiaryOwner(diary, userId);
115121

116122
mediaService.deleteMedia(diaryId);
117123
diaryRepository.delete(diary);
118124
}
119125

126+
private Diary findDiaryOrThrow(Long diaryId) {
127+
return diaryRepository.findById(diaryId)
128+
.orElseThrow(NotFoundDiaryException::new);
129+
}
130+
131+
// Page용 매핑 메서드
132+
private Page<DiaryResponseDto> mapToDtoPage(Page<Diary> page) {
133+
List<DiaryResponseDto> content = getDiaryResponsesWithMedia(page.getContent());
134+
return new PageImpl<>(content, page.getPageable(), page.getTotalElements());
135+
}
136+
137+
// Slice용 매핑 메서드
138+
private Slice<DiaryResponseDto> mapToDtoSlice(Slice<Diary> slice) {
139+
List<DiaryResponseDto> content = getDiaryResponsesWithMedia(slice.getContent());
140+
return new SliceImpl<>(content, slice.getPageable(), slice.hasNext());
141+
}
142+
120143
// 다이어리 + 미디어 같이 반환
121144
private List<DiaryResponseDto> getDiaryResponsesWithMedia(List<Diary> diaries) {
122145
if (diaries.isEmpty()) {

0 commit comments

Comments
 (0)