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
feat: 유저가 선호하는 필터를 저장하는 기능을 구현한다 #453
Conversation
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.
수고하셨습니다 인수테스트가 빠진 것 같아 확인부탁드립니다
public static FiltersResponse fromMemberFilters(List<MemberFilter> memberFilters) { | ||
List<Filter> filters = memberFilters.stream() | ||
.map(MemberFilter::getFilter) | ||
.toList(); | ||
|
||
return getFiltersResponse(filters); |
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.
filter response를 두 api에서 사용하는 것 같아보이는데요 이렇게 하신 특별한 이유가 있으실까요? 만약 하나를 위해 변경하면 둘 다 변경되는 일이 생길 것 같아서요
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.
Filter 등록 후 반환과, 멤버가 선호 Filter를 등록하고 반환하는 형식은 같습니다
멤버가 선호하는 MemberFilter 역시도 Filter 테이블 안에 속하기 때문입니다.
그래서 따로 반환될 일은 없다고 생각했습니다
|
||
@ManyToOne | ||
@JoinColumn(name = "member_id", nullable = false) | ||
@OnDelete(action = OnDeleteAction.CASCADE) |
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.
on delete를 사용하신 이유가 무엇인가요?
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.
mebmer_filter 테이블은 (id, member_id, filter_id) 컬럼을 가지고, 멤버가 선호하는 필터를 나타내는 테이블입니다.
만약 member_filter테이블에서 member가 제거되거나, filter가 제거된다면 의미가 없는 column이 됩니다.
그래서 참조하는 객체가 삭제되면 해당 객체도 삭제되는 것을 의도했습니다.
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.
제가 질문이 너무 짧았네요 cascade와 orphanremoval 속성을 사용하지 않고 on delete 옵션을 사용하신 이유가 있나요?
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.
아 지금 테이블 관계가 Member <--(1:n)-- MemberFilter --(n:1)--> Filter
이렇게 되어 있습니다.
단방향 관계로 MemberFilter에서만 Member, Filter를 참조하는 구조입니다.
그래서 orphanremoval은 적용할 수 없었고, cacade = CASCADE.REMOVE 혹은 OnDelete를 적용할 수 있었습니다.
제가 알기로는 cascade로 삭제하는 것은 jpa 레벨에서 진행되는 것이고, 참조하는 레코드 수 만큼 쿼리가 나가고, OnDelete로 삭제하는 것은 db단에서 처리하기 때문에 삭제쿼리가 나가지 않고 삭제가 되는 것으로 알고 있습니다!
혹시 틀린 부분이 있다면 말씀해주세요~
return memberFilterRepository.findAllByMember(findMember(loginMember)).stream() | ||
.map(MemberFilter::getFilter) | ||
.collect(Collectors.toList()); |
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.
가독성을 위해 find member는 변수로 분리하시는 것은 어떠신가요
|
||
@Transactional(readOnly = true) | ||
public List<Filter> findMemberFilters(Long memberId, Long loginMember) { | ||
validateMember(memberId, loginMember); |
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.
id로만 비교하시는 이유가 있으신가요
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.
저번에 대화를 통해 해당 API는 RESTful 원칙을 따르기로 했는데요.
그래서 현재 /api/members/{memberId}/filters
의 구조를 가지고 있습니다.
또한 header로 로그인한 유저의 token
또한 가지고 있습니다.
따라서 서버에 들어오는 데이터는 총 두 가지입니다.
- path에서 들어오는
{memberId}
- token을 기준으로 받아오는
memberId
그래서 본인인지 검증하는 방식을 path에서 memberId와 token에서 memberId로 먼저 비교해줬습니다.
id가 pk이므로 겹칠 일이 없어서 괜찮다고 생각했기 때문입니다.
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.
제이가 하신 방법으로도 할 수 있을 것 같은데
- loginMember를 repository에서 찾아온다
- 없으면 예외를 던진다
- 있으면 path variable에 받아온 memberId와 loginMember객체의 id를 검증한다
- 로직을 수행한다
이러한 플로우로 할 수도 있었을 것 같은데 바로 id를 비교하시는 이유가 무엇인지 궁금했습니다
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.
저는 두 개의 로직을 나눴는데, 현재 모든 메서드가 검증 후 멤버를 받아서 써야하네요!
박스터 리뷰대로 바꾸는게 좋아보입니다 👍
.toList(); | ||
|
||
return filters.stream() | ||
.map(it -> new MemberFilter(member, Filter.of(it.getName(), it.getFilterType().getName()))) |
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.
.map(it -> new MemberFilter(member, Filter.of(it.getName(), it.getFilterType().getName()))) | |
.map(it -> new MemberFilter(member, it)) |
이 부분 정상 동작하나요? 이렇게 바꿔야할 것 같아서요
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.
아이고 이부분 인수테스트 하다가 발견했네요 수정했습니다!
when(memberService.findMemberFilters(any(), any())).thenReturn(filters); | ||
|
||
// then | ||
mockMvc.perform(RestDocumentationRequestBuilders.get("/members/{memberId}/filters", 1L) |
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.
static import 부탁드려요
private Long id = 0L; | ||
|
||
@Override | ||
public List<MemberFilter> findAllByMember(final Member member) { |
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.
final 지워주세요
List<Filter> result = memberService.findMemberFilters(member.getId(), member.getId()); | ||
|
||
// then | ||
assertThat(memberFilters.get(0).getFilter().getName()).isEqualTo(result.get(0).getName()); |
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.
get name으로 비교하신 이유가 있으신가요?
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.
equals hashcode 재정의를 했는데 name으로 비교를 했네요😅
assertSoftly(softly -> { | ||
softly.assertThat(result.size()).isEqualTo(memberFilters.size()); | ||
}); |
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.
하나만 검증하면 assert softly는 제거해도 되지않을까요
그리고 혹시 다른 회원이 필터를 지우면 발생하는 예외케이스도 검증해주실 수 있을까요?
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.
넵 꼼꼼한 리뷰 감사합니다
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.
수고하셧습니다아
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.
수고하셨습니다
|
||
@ManyToOne | ||
@JoinColumn(name = "member_id", nullable = false) | ||
@OnDelete(action = OnDeleteAction.CASCADE) |
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.
제가 질문이 너무 짧았네요 cascade와 orphanremoval 속성을 사용하지 않고 on delete 옵션을 사용하신 이유가 있나요?
|
||
@Transactional(readOnly = true) | ||
public List<Filter> findMemberFilters(Long memberId, Long loginMember) { | ||
validateMember(memberId, loginMember); |
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.
제이가 하신 방법으로도 할 수 있을 것 같은데
- loginMember를 repository에서 찾아온다
- 없으면 예외를 던진다
- 있으면 path variable에 받아온 memberId와 loginMember객체의 id를 검증한다
- 로직을 수행한다
이러한 플로우로 할 수도 있었을 것 같은데 바로 id를 비교하시는 이유가 무엇인지 궁금했습니다
memberFilterRepository.deleteAllByMember(member); | ||
|
||
return memberFilterRepository.saveAll(makeMemberFilters(filtersRequest, member)); |
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.
다 지우고 다 저장하는 것 밖에 방법이 없겠죠?
저는 마땅히 떠오르지 않는데 뭔가 더 좋은 방법이 있을 것 같아서요
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.
비교해서 바꾸는 방법도 있긴 할 것 같은데, 효율적일지는 모르겠어요 ㅠ
고민 조금 더 해볼게요
관리자 = memberRepository.save(Member.builder() | ||
.id(2L) | ||
.memberRole(MemberRole.ADMIN) | ||
.build()); | ||
관리자_토큰 = "Bearer " + provider.create(관리자.getId()); | ||
|
||
필터_리스트 = new FiltersRequest( | ||
List.of( | ||
new FilterRequest(FilterType.COMPANY.getName(), "광주시"), | ||
new FilterRequest(FilterType.CAPACITY.getName(), "2.00"), | ||
new FilterRequest(FilterType.CONNECTOR_TYPE.getName(), "DC_COMBO") | ||
) | ||
); | ||
|
||
생성_요청("/filters", 필터_리스트, 관리자_토큰); |
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.
다른 부분은 repo에 save를 직접하는데 필터를 저장하는 부분은 요청을 보내 저장하는 이유가 있을까요?
이렇게 되면 시간이 조금 더 걸릴 것 같아서요
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.
고생 많으셨습니다! 👍
📄 Summary
member, filter가 삭제된다면 해당하는 MemberFilter 테이블에 column도 같이 삭제됩니다.
🕰️ Actual Time of Completion
🙋🏻 More
close #452