Skip to content

Commit d5f336f

Browse files
authored
refactor: 회원 검색 기능 리팩토링
# 변경점 👍 close: #14 1. 유저 검색에서 tag와 username을 모두 사용 2. 검색결과에서는 `isMyPick` 필드를 활용해서 내가 구독했는지를 확인 3. 엔티티 삭제 테스트에서 연관관계 삭제 확인 추가
1 parent 3877396 commit d5f336f

File tree

9 files changed

+159
-17
lines changed

9 files changed

+159
-17
lines changed

src/main/java/apptive/team5/subscribe/repository/SubscribeRepository.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,11 @@ public interface SubscribeRepository extends JpaRepository<Subscribe, Long> {
3939
@Modifying(clearAutomatically = true)
4040
@Query("delete from Subscribe s where s.subscriber.id = :userId or s.subscribedTo.id = :userId")
4141
void deleteByUserId(Long userId);
42+
43+
@Query("""
44+
select s from Subscribe s
45+
where s.subscriber.id = :subscriberId and
46+
s.subscribedTo.id in :subscribedToIds
47+
""")
48+
List<Subscribe> findBySubscriberIdAndSubscribedToIds(Long subscriberId, List<Long> subscribedToIds);
4249
}

src/main/java/apptive/team5/subscribe/service/SubscribeLowService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.springframework.stereotype.Service;
99
import org.springframework.transaction.annotation.Transactional;
1010

11+
import java.util.List;
12+
1113
@Transactional
1214
@RequiredArgsConstructor
1315
@Service
@@ -46,4 +48,9 @@ public int countSubscribedTobySubscriberId(Long subscribedToId) {
4648
public void deleteByUserId(Long userId) {
4749
subscribeRepository.deleteByUserId(userId);
4850
}
51+
52+
@Transactional(readOnly = true)
53+
public List<Subscribe> findBySubscriberIdAndSubscribedToIds(Long subscriberId, List<Long> subscribedToIds) {
54+
return subscribeRepository.findBySubscriberIdAndSubscribedToIds(subscriberId, subscribedToIds);
55+
}
4956
}

src/main/java/apptive/team5/user/controller/UserController.java

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

33
import apptive.team5.file.dto.FileUploadRequest;
44
import apptive.team5.user.dto.UserResponse;
5+
import apptive.team5.user.dto.UserSearchResponse;
56
import apptive.team5.user.dto.UserStaticsResponse;
67
import apptive.team5.user.dto.UserTagUpdateRequest;
78
import apptive.team5.user.service.UserService;
@@ -64,10 +65,11 @@ public ResponseEntity<UserResponse> deleteProfileImage(@AuthenticationPrincipal
6465

6566

6667
@GetMapping
67-
public ResponseEntity<Page<UserResponse>> getUserList(@RequestParam(required = false) String tag,
68-
@RequestParam(defaultValue = "0") int page,
69-
@RequestParam(defaultValue = "5") int size) {
70-
Page<UserResponse> response = userService.findByTag(tag, PageRequest.of(page, size));
68+
public ResponseEntity<Page<UserSearchResponse>> getUserList(@RequestParam(required = false) String searchCond,
69+
@RequestParam(defaultValue = "0") int page,
70+
@RequestParam(defaultValue = "5") int size,
71+
@AuthenticationPrincipal Long userId) {
72+
Page<UserSearchResponse> response = userService.findByTagOrUserName(userId, searchCond, PageRequest.of(page, size));
7173

7274

7375
return ResponseEntity.status(HttpStatus.OK).body(response);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package apptive.team5.user.dto;
2+
3+
import apptive.team5.global.util.S3Util;
4+
import apptive.team5.user.domain.SocialType;
5+
import apptive.team5.user.domain.UserEntity;
6+
import apptive.team5.user.domain.UserRoleType;
7+
8+
public record UserSearchResponse(
9+
Long userId,
10+
String username,
11+
String tag,
12+
String identifier,
13+
String profileImageUrl,
14+
UserRoleType userRoleType,
15+
SocialType socialType,
16+
boolean isMyPick
17+
) {
18+
19+
public UserSearchResponse(UserEntity userEntity, boolean isMyPick) {
20+
this( userEntity.getId(),
21+
userEntity.getUsername(),
22+
userEntity.getTag(),
23+
userEntity.getIdentifier(),
24+
S3Util.s3Url + userEntity.getProfileImage(),
25+
userEntity.getRoleType(),
26+
userEntity.getSocialType(),
27+
isMyPick
28+
);
29+
}
30+
}

src/main/java/apptive/team5/user/repository/QUserRepository.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,17 @@ public QUserRepository(EntityManager entityManager) {
2525
this.queryFactory = new JPAQueryFactory(entityManager);
2626
}
2727

28-
public Page<UserEntity> findByTag(String tag, Pageable pageable) {
28+
public Page<UserEntity> findByTagOrUsername(String searchCond, Pageable pageable) {
2929

3030
List<UserEntity> content = queryFactory
3131
.selectFrom(userEntity)
32-
.where(tagLike(tag))
32+
.where(tagLike(searchCond).or(usernameLike(searchCond)))
3333
.offset(pageable.getOffset())
3434
.limit(pageable.getPageSize())
3535
.fetch();
3636

3737
JPAQuery<Long> countQuery = queryFactory.select(userEntity.count())
38-
.where(tagLike(tag))
38+
.where(tagLike(searchCond).or(usernameLike(searchCond)))
3939
.from(userEntity);
4040

4141
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
@@ -44,4 +44,8 @@ public Page<UserEntity> findByTag(String tag, Pageable pageable) {
4444
private BooleanExpression tagLike(String tag) {
4545
return tag != null ? userEntity.tag.like("%"+tag+"%") : null;
4646
}
47+
48+
private BooleanExpression usernameLike(String username) {
49+
return username != null ? userEntity.username.like("%"+username+"%") : null;
50+
}
4751
}

src/main/java/apptive/team5/user/service/UserLowService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public boolean existsByTag(String tag) {
5555
}
5656

5757
@Transactional(readOnly = true)
58-
public Page<UserEntity> findByTag(String tag, Pageable pageable) {
59-
return qUserRepository.findByTag(tag,pageable);
58+
public Page<UserEntity> findByTagOrUsername(String searchCond, Pageable pageable) {
59+
return qUserRepository.findByTagOrUsername(searchCond,pageable);
6060
}
6161
}

src/main/java/apptive/team5/user/service/UserService.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import apptive.team5.user.domain.UserEntity;
1818
import apptive.team5.user.domain.UserRoleType;
1919
import apptive.team5.user.dto.UserResponse;
20+
import apptive.team5.user.dto.UserSearchResponse;
2021
import apptive.team5.user.dto.UserStaticsResponse;
2122
import apptive.team5.user.dto.UserTagUpdateRequest;
2223
import apptive.team5.user.util.TagGenerator;
@@ -26,6 +27,11 @@
2627
import org.springframework.stereotype.Service;
2728
import org.springframework.transaction.annotation.Transactional;
2829

30+
import java.util.List;
31+
import java.util.Set;
32+
import java.util.stream.Collectors;
33+
import java.util.stream.Stream;
34+
2935
@Transactional
3036
@Service
3137
@RequiredArgsConstructor
@@ -98,9 +104,20 @@ public UserResponse changeTag(UserTagUpdateRequest userTagUpdateRequest, Long us
98104
}
99105

100106
@Transactional(readOnly = true)
101-
public Page<UserResponse> findByTag(String tag, Pageable pageable) {
102-
return userLowService.findByTag(tag,pageable)
103-
.map(UserResponse::new);
107+
public Page<UserSearchResponse> findByTagOrUserName(Long subscriberId, String searchCond, Pageable pageable) {
108+
Page<UserEntity> findUsers = userLowService.findByTagOrUsername(searchCond, pageable);
109+
110+
List<Long> userIds = findUsers.stream().map(UserEntity::getId).toList();
111+
112+
Set<Long> subscribedToIds = subscribeLowService.findBySubscriberIdAndSubscribedToIds(subscriberId, userIds)
113+
.stream()
114+
.map(subscribe -> subscribe.getSubscribedTo().getId()).collect(Collectors.toSet());
115+
116+
return findUsers
117+
.map(user -> {
118+
boolean isMyPick = subscribedToIds.contains(user.getId());
119+
return new UserSearchResponse(user, isMyPick);
120+
});
104121
}
105122

106123
public UserResponse changeProfileImage(FileUploadRequest fileUploadRequest, Long userId) {

src/test/java/apptive/team5/diary/controller/DiaryControllerTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.fasterxml.jackson.core.type.TypeReference;
1818
import com.fasterxml.jackson.databind.JsonNode;
1919
import com.fasterxml.jackson.databind.ObjectMapper;
20+
import org.assertj.core.api.SoftAssertions;
2021
import org.junit.jupiter.api.BeforeEach;
2122
import org.junit.jupiter.api.DisplayName;
2223
import org.junit.jupiter.api.Test;
@@ -244,12 +245,19 @@ void updateDiary() throws Exception {
244245
void deleteDiary() throws Exception {
245246
// given
246247
DiaryEntity diary = diaryRepository.save(TestUtil.makeDiaryEntity(testUser));
248+
DiaryLikeEntity diaryLikeEntity = diaryLikeLowService.saveDiaryLike(new DiaryLikeEntity(testUser, diary));
247249
TestSecurityContextHolderInjection.inject(testUser.getId(), testUser.getRoleType());
248250

249251
// when & then
250252
mockMvc.perform(delete("/api/diaries/{diaryId}", diary.getId())
251253
.with(securityContext(SecurityContextHolder.getContext()))
252254
)
253255
.andExpect(status().isNoContent());
256+
257+
258+
SoftAssertions.assertSoftly(softly -> {
259+
softly.assertThat(diaryRepository.existsById(diary.getId())).isFalse();
260+
softly.assertThat(diaryLikeLowService.existsByUserAndDiary(testUser, diary)).isFalse();
261+
});
254262
}
255263
}

src/test/java/apptive/team5/user/controller/UserControllerTest.java

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package apptive.team5.user.controller;
22

33
import apptive.team5.diary.domain.DiaryEntity;
4+
import apptive.team5.diary.domain.DiaryLikeEntity;
5+
import apptive.team5.diary.repository.DiaryLikeRepository;
46
import apptive.team5.diary.repository.DiaryRepository;
57
import apptive.team5.global.exception.ExceptionCode;
68
import apptive.team5.global.util.S3Util;
@@ -9,6 +11,7 @@
911
import apptive.team5.user.domain.UserEntity;
1012
import apptive.team5.user.domain.UserRoleType;
1113
import apptive.team5.user.dto.UserResponse;
14+
import apptive.team5.user.dto.UserSearchResponse;
1215
import apptive.team5.user.dto.UserStaticsResponse;
1316
import apptive.team5.user.dto.UserTagUpdateRequest;
1417
import apptive.team5.user.repository.UserRepository;
@@ -61,6 +64,9 @@ class UserControllerTest {
6164
@Autowired
6265
private SubscribeRepository subscribeRepository;
6366

67+
@Autowired
68+
private DiaryLikeRepository diaryLikeRepository;
69+
6470

6571
@DisplayName("회원 정보 조회 성공")
6672
@Test
@@ -186,26 +192,71 @@ void getUserByTagSuccess() throws Exception {
186192
// given
187193
UserEntity user = TestUtil.makeUserEntity();
188194
userRepository.save(user);
195+
UserEntity subscribedToUser = TestUtil.makeDifferentUserEntity(user);
196+
userRepository.save(subscribedToUser);
197+
subscribeRepository.save(new Subscribe(user, subscribedToUser));
189198
TestSecurityContextHolderInjection.inject(user.getId(), user.getRoleType());
190199

191200
// when
192201
String response = mockMvc.perform(get("/api/users")
193-
.param("tag", user.getTag())
202+
.param("searchCond", user.getTag())
194203
.with(securityContext(SecurityContextHolder.getContext()))
195204
)
196205
.andReturn().getResponse().getContentAsString(UTF_8);
197206

198207
// then
199208
JsonNode jsonNode = objectMapper.readTree(response);
200209

201-
List<UserResponse> content = objectMapper.convertValue(
210+
List<UserSearchResponse> content = objectMapper.convertValue(
202211
jsonNode.path("content"),
203-
new TypeReference<List<UserResponse>>() {}
212+
new TypeReference<List<UserSearchResponse>>() {}
204213
);
205214

215+
206216
assertSoftly(softly -> {
207-
softly.assertThat(content.size()).isEqualTo(1);
217+
softly.assertThat(content.size()).isEqualTo(2);
208218
softly.assertThat(content.get(0).userId()).isEqualTo(user.getId());
219+
softly.assertThat(content.get(0).isMyPick()).isFalse();
220+
softly.assertThat(content.get(1).userId()).isEqualTo(subscribedToUser.getId());
221+
softly.assertThat(content.get(1).isMyPick()).isTrue();
222+
});
223+
224+
}
225+
226+
@DisplayName("username 통해 유저 조회 성공")
227+
@Test
228+
void getUserByTUsernameSuccess() throws Exception {
229+
230+
// given
231+
UserEntity user = TestUtil.makeUserEntity();
232+
userRepository.save(user);
233+
UserEntity subscribedToUser = TestUtil.makeDifferentUserEntity(user);
234+
userRepository.save(subscribedToUser);
235+
subscribeRepository.save(new Subscribe(user, subscribedToUser));
236+
TestSecurityContextHolderInjection.inject(user.getId(), user.getRoleType());
237+
238+
// when
239+
String response = mockMvc.perform(get("/api/users")
240+
.param("searchCond", user.getUsername())
241+
.with(securityContext(SecurityContextHolder.getContext()))
242+
)
243+
.andReturn().getResponse().getContentAsString(UTF_8);
244+
245+
// then
246+
JsonNode jsonNode = objectMapper.readTree(response);
247+
248+
List<UserSearchResponse> content = objectMapper.convertValue(
249+
jsonNode.path("content"),
250+
new TypeReference<List<UserSearchResponse>>() {}
251+
);
252+
253+
254+
assertSoftly(softly -> {
255+
softly.assertThat(content.size()).isEqualTo(2);
256+
softly.assertThat(content.get(0).userId()).isEqualTo(user.getId());
257+
softly.assertThat(content.get(0).isMyPick()).isFalse();
258+
softly.assertThat(content.get(1).userId()).isEqualTo(subscribedToUser.getId());
259+
softly.assertThat(content.get(1).isMyPick()).isTrue();
209260
});
210261

211262
}
@@ -216,7 +267,17 @@ void withDrawSuccess() throws Exception {
216267

217268
// given
218269
UserEntity user = TestUtil.makeUserEntity();
270+
UserEntity otherUser = TestUtil.makeDifferentUserEntity(user);
219271
userRepository.save(user);
272+
userRepository.save(otherUser);
273+
DiaryEntity diaryEntity = TestUtil.makeDiaryEntity(user);
274+
DiaryEntity otherDiary = TestUtil.makeDiaryEntity(otherUser);
275+
diaryRepository.save(diaryEntity);
276+
diaryRepository.save(otherDiary);
277+
DiaryLikeEntity diaryLikeEntity = new DiaryLikeEntity(user, diaryEntity);
278+
DiaryLikeEntity otherDiaryLikeEntity = new DiaryLikeEntity(user, otherDiary);
279+
diaryLikeRepository.save(diaryLikeEntity);
280+
diaryLikeRepository.save(otherDiaryLikeEntity);
220281
TestSecurityContextHolderInjection.inject(user.getId(), user.getRoleType());
221282

222283

@@ -227,7 +288,13 @@ void withDrawSuccess() throws Exception {
227288
.andExpect(status().isNoContent());
228289

229290
// then
230-
assertThat(userRepository.existsById(user.getId())).isFalse();
291+
assertSoftly(softly -> {
292+
softly.assertThat(userRepository.existsById(user.getId())).isFalse();
293+
softly.assertThat(diaryRepository.existsById(diaryEntity.getId())).isFalse();
294+
softly.assertThat(diaryLikeRepository.existsById(diaryLikeEntity.getId())).isFalse();
295+
softly.assertThat(diaryLikeRepository.existsById(otherDiaryLikeEntity.getId())).isFalse();
296+
softly.assertThat(diaryRepository.existsById(otherDiary.getId())).isTrue();
297+
});
231298

232299
}
233300

0 commit comments

Comments
 (0)