Skip to content
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

[BE] Refactor/#406 토픽 권한을 가진 회원 목록 조회 시 공개 여부를 함께 반환하도록 변경 #412

Merged
merged 24 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9dfea1f
refactor: 사용하지 않는 MemberRepository 메서드 삭제
yoondgu Sep 13, 2023
afc314c
refactor: 회원 업데이트 부분 변경으로 시그니처 변경
yoondgu Sep 13, 2023
7be1f71
feat: 회원 정보 수정 API 구현 및 테스트 작성
yoondgu Sep 14, 2023
1613287
test: JwtTokenProviderTest 작성
yoondgu Sep 14, 2023
6d9b305
chore: 로컬 환경용 더미데이터 sql 작성
yoondgu Sep 14, 2023
b90630c
chore: 로컬 환경 data.sql을 위한 서브모듈 변경
yoondgu Sep 14, 2023
016496f
docs: 기능 명세서 및 테스트 코드 용어 정리 (유저, 멤버 -> 회원)
yoondgu Sep 14, 2023
5ffb6cb
chore: 로그 환경설정 파일 디렉터리 분리
yoondgu Sep 14, 2023
318228f
feat: 닉네임 중복 검증 구현
yoondgu Sep 14, 2023
bec7adf
refactor: 회원의 이메일 Unique 제약조건 삭제
yoondgu Sep 14, 2023
fc6ba2f
refactor: 모호한 메서드명 수정
yoondgu Sep 14, 2023
31f1d06
refactor: Email이 Unique하지 않음에 따라 테스트에서 사용하는 조회 쿼리 변경
yoondgu Sep 14, 2023
4232fe2
refactor: 내 정보 수정 API URI 변경
yoondgu Sep 14, 2023
f21911f
refactor: 토픽 권한 회원 목록 조회 API를 접근 정보(권한 회원 목록 및 공개 여부) 조회로 명세 변경
yoondgu Sep 14, 2023
190255f
fix: 디렉터리 분리에 따른 로그 설정 파일 appender 경로 수정
yoondgu Sep 14, 2023
443b456
fix: 디렉터리 분리에 따른 로그 설정 파일 appender 경로 수정
yoondgu Sep 14, 2023
e60fa9c
refactor: 실수로 바꾼 기존 메서드명 원복
yoondgu Sep 14, 2023
8f71050
refactor: 불필요한 import문 제거
yoondgu Sep 14, 2023
f4c5b93
refactor: 불필요한 접근제어자, 중복 코드 제거
yoondgu Sep 14, 2023
754336d
docs: Restdocs API 네이밍 반영
yoondgu Sep 14, 2023
8a12ba7
chore: 충돌 해결을 위한 merge
yoondgu Sep 15, 2023
4d50edc
fix: 내 정보 수정 RestDocs 스니펫 누락 추가
yoondgu Sep 15, 2023
17641cd
chore: 충돌 해결을 위한 merge
yoondgu Sep 15, 2023
f54e896
chore: 충돌 해결을 위한 merge
yoondgu Sep 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions backend/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,19 @@

- 핀 상세 조회

#### 유저
#### 회원

- 유저 핀 생성
- 회원 핀 생성

- 유저 핀 정보 수정
- 회원 핀 정보 수정
- name, description 만 수정 가능하다.
- description 은 1000자 까지만 가능하다.

- 유저 핀 삭제
- 회원 핀 삭제
- Delete 는 Soft Delete

---

- 유저 핀 목록 조회
- 회원 핀 목록 조회
- 페이지 네이션 (무한 스크롤, 일단 15개)

4 changes: 2 additions & 2 deletions backend/src/docs/asciidoc/bookmark.adoc
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
== 즐겨찾기

=== 토픽을 유저의 즐겨찾기에 추가
=== 토픽을 회원의 즐겨찾기에 추가

operation::bookmark-controller-test/add-topic-in-bookmark[snippets='http-request,http-response']


=== 유저의 토픽 즐겨찾기 삭제
=== 회원의 토픽 즐겨찾기 삭제

operation::bookmark-controller-test/delete-topic-in-bookmark[snippets='http-request,http-response']
14 changes: 7 additions & 7 deletions backend/src/docs/asciidoc/member.adoc
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
== 유저
== 회원

=== 유저 목록 조회
=== 회원 목록 조회

operation::member-controller-test/find-all-member[snippets='http-request,http-response']

=== 유저 단일 조회
=== 회원 단일 조회

operation::member-controller-test/find-member-by-id[snippets='http-request,http-response']

=== 유저의 나의 지도 목록 조회
=== 회원의 나의 지도 목록 조회

operation::member-controller-test/find-my-all-topics[snippets='http-request,http-response']

=== 유저의 나의 핀 목록 조회
=== 회원의 나의 핀 목록 조회

operation::member-controller-test/find-my-all-pins[snippets='http-request,http-response']

=== 유저의 모아보기 조회
=== 회원의 모아보기 조회

operation::member-controller-test/find-all-topics-in-atlas[snippets='http-request,http-response']

=== 유저의 즐겨찾기 조회
=== 회원의 즐겨찾기 조회

operation::member-controller-test/find-all-topics-in-bookmark[snippets='http-request,http-response']
6 changes: 3 additions & 3 deletions backend/src/docs/asciidoc/permission.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ operation::permission-controller-test/add-permission[snippets='http-request,http

operation::permission-controller-test/delete-permission[snippets='http-request,http-response']

=== 토픽에 권한을 가진 유저 목록 조회
=== 토픽에 권한을 가진 회원 목록 조회

operation::permission-controller-test/find-all-topic-permissions[snippets='http-request,http-response']
operation::permission-controller-test/find-topic-access-detail-by-topic-id[snippets='http-request,http-response']

=== 토픽에 권한을 가진 유저 단일 조회
=== 토픽에 권한을 가진 회원 단일 조회

operation::permission-controller-test/find-permission-by-id[snippets='http-request,http-response']
2 changes: 1 addition & 1 deletion backend/src/docs/asciidoc/pin.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ operation::pin-controller-test/find-all[snippets='http-request,http-response']

operation::pin-controller-test/find-by-id[snippets='http-request,http-response']

=== 멤버별 핀 목록 조회
=== 회원별 핀 목록 조회

operation::pin-controller-test/find-all-pins-by-member-id[snippets='http-request,http-response']

Expand Down
2 changes: 1 addition & 1 deletion backend/src/docs/asciidoc/topic.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ operation::topic-controller-test/find-all-by-order-by-updated-at-desc[snippets='

operation::topic-controller-test/find-all-best-topics[snippets='http-request,http-response']

=== 멤버별 토픽 목록 조회
=== 회원별 토픽 목록 조회

operation::topic-controller-test/find-all-topics-by-member-id[snippets='http-request,http-response']

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.mapbefine.mapbefine.member.application;

import com.mapbefine.mapbefine.auth.domain.AuthMember;
import com.mapbefine.mapbefine.member.domain.Member;
import com.mapbefine.mapbefine.member.domain.MemberRepository;
import com.mapbefine.mapbefine.member.dto.request.MemberUpdateRequest;
import com.mapbefine.mapbefine.member.exception.MemberErrorCode;
import com.mapbefine.mapbefine.member.exception.MemberException.MemberConflictException;
import java.util.NoSuchElementException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class MemberCommandService {

private final MemberRepository memberRepository;

public MemberCommandService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}

public void updateInfoById(AuthMember authMember, MemberUpdateRequest request) {
Member member = findMemberById(authMember.getMemberId());
String nickName = request.nickName();

validateNicknameDuplicated(nickName);

member.update(nickName);
}

private Member findMemberById(Long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new NoSuchElementException("findMemberById; memberId=" + memberId));
}

private void validateNicknameDuplicated(String nickName) {
if (memberRepository.existsByMemberInfoNickName(nickName)) {
throw new MemberConflictException(MemberErrorCode.ILLEGAL_NICKNAME_ALREADY_EXISTS, nickName);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -98,21 +98,14 @@ private static String createNickname(String nickname) {
private static String createNicknameSuffix() {
return randomUUID()
.toString()
.replaceAll("-", "")
.replace("-", "")
.substring(0, DEFAULT_NICKNAME_SUFFIX_LENGTH);
}

public void update(
String nickName,
String email,
String imageUrl
String nickName
) {
memberInfo = MemberInfo.of(
nickName,
email,
imageUrl,
memberInfo.getRole()
);
memberInfo = memberInfo.createUpdatedMemberInfo(nickName);
}

public void addTopic(Topic topic) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class MemberInfo {
@Column(nullable = false, length = 20, unique = true)
private String nickName;

@Column(nullable = false, unique = true)
@Column(nullable = false)
Copy link
Collaborator

@junpakPark junpakPark Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2. email은 nullable하더라도 unique 하면 더 좋지 않을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 이 부분은 #408 PR에 해당되는 커밋인데요! 커밋에만 적고 자세히 말씀을 못드렸네용
아래와 같은 이유로 변경했는데 어떻게 생각하시나요??image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 2번째 경우는 두번째 회원 가입을 막아야한다고 생각하긴 했어요!
아이디가 다중으로 필요한 경우는 보통 어뷰저일 가능성이 높다고 생각해서요 ㅋㅋ
(차단을 당했거나, 여러 아이디로 분란을 조장하거나 등등)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cpot5620 @kpeel5839 의견은 어떠신가요??

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 오히려 회원가입을 막게되면 다중 플랫폼을 제공하는 의미가 다소 퇴색될 것 같기도 하다는 생각이 드네용

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 unique할 필요는 없다고 생각해요 !!!!!!

만약, unique하게 갈거라면 어떤 OAuth로 연동되어있는지도 알려주는 기능이 필요할 것 같아요.

private String email;

@Column(nullable = false)
Expand Down Expand Up @@ -93,6 +93,11 @@ private static void validateRole(Role role) {
}
}

public MemberInfo createUpdatedMemberInfo(String nickName) {

return MemberInfo.of(nickName, this.email, this.imageUrl.getImageUrl(), this.role);
}

public String getImageUrl() {
return imageUrl.getImageUrl();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@

public interface MemberRepository extends JpaRepository<Member, Long> {

Optional<Member> findByMemberInfoEmail(String email);

boolean existsByMemberInfoEmail(String email);

Optional<Member> findByOauthIdOauthServerId(Long oauthServerId);
Optional<Member> findById(Long id);

Optional<Member> findByOauthId(OauthId oauthId);

boolean existsByMemberInfoNickName(String nickName);

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
public enum Role {

ADMIN("ROLE_ADMIN", "운영자"),
USER("ROLE_USER", "로그인 유저"),
USER("ROLE_USER", "로그인 회원"),
GUEST("ROLE_GUEST", "손님");

private final String key;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.mapbefine.mapbefine.member.dto.request;

public record MemberUpdateRequest(
String nickName
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public enum MemberErrorCode {
ILLEGAL_EMAIL_NULL("05002", "이메일은 필수로 입력해야합니다."),
ILLEGAL_EMAIL_PATTERN("05003", "올바르지 않은 이메일 형식입니다."),
MEMBER_NOT_FOUND("05400", "존재하지 않는 회원입니다."),
ILLEGAL_NICKNAME_ALREADY_EXISTS("05900", "이미 존재하는 닉네임입니다."),
;

private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.mapbefine.mapbefine.member.exception;

import com.mapbefine.mapbefine.common.exception.BadRequestException;
import com.mapbefine.mapbefine.common.exception.ConflictException;
import com.mapbefine.mapbefine.common.exception.ErrorCode;
import com.mapbefine.mapbefine.common.exception.NotFoundException;

Expand All @@ -18,5 +19,11 @@ public MemberNotFoundException(MemberErrorCode errorCode, Long id) {
}
}

public static class MemberConflictException extends ConflictException {
public MemberConflictException(MemberErrorCode errorCode, String value) {
super(new ErrorCode<>(errorCode.getCode(), errorCode.getMessage(), value));
}
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,31 @@

import com.mapbefine.mapbefine.auth.domain.AuthMember;
import com.mapbefine.mapbefine.common.interceptor.LoginRequired;
import com.mapbefine.mapbefine.member.application.MemberCommandService;
import com.mapbefine.mapbefine.member.application.MemberQueryService;
import com.mapbefine.mapbefine.member.dto.request.MemberUpdateRequest;
import com.mapbefine.mapbefine.member.dto.response.MemberDetailResponse;
import com.mapbefine.mapbefine.member.dto.response.MemberResponse;
import com.mapbefine.mapbefine.pin.dto.response.PinResponse;
import com.mapbefine.mapbefine.topic.dto.response.TopicResponse;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/members")
public class MemberController {

private final MemberCommandService memberCommandService;
private final MemberQueryService memberQueryService;

public MemberController(MemberQueryService memberQueryService) {
public MemberController(MemberCommandService memberCommandService, MemberQueryService memberQueryService) {
this.memberCommandService = memberCommandService;
this.memberQueryService = memberQueryService;
}

Expand Down Expand Up @@ -72,4 +78,12 @@ public ResponseEntity<List<TopicResponse>> findAllTopicsInBookmark(AuthMember au
return ResponseEntity.ok(responses);
}

@LoginRequired
@PatchMapping("/my/profiles")
public ResponseEntity<Void> updateMyInfo(AuthMember authMember, @RequestBody MemberUpdateRequest request) {
memberCommandService.updateInfoById(authMember, request);

return ResponseEntity.ok().build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@

import com.mapbefine.mapbefine.permission.domain.Permission;
import com.mapbefine.mapbefine.permission.domain.PermissionRepository;
import com.mapbefine.mapbefine.permission.dto.response.PermissionDetailResponse;
import com.mapbefine.mapbefine.permission.dto.response.PermissionResponse;
import com.mapbefine.mapbefine.permission.dto.response.PermissionMemberDetailResponse;
import com.mapbefine.mapbefine.permission.dto.response.PermissionedMemberResponse;
import com.mapbefine.mapbefine.permission.dto.response.TopicAccessDetailResponse;
import com.mapbefine.mapbefine.permission.exception.PermissionException.PermissionNotFoundException;
import com.mapbefine.mapbefine.topic.domain.Publicity;
import com.mapbefine.mapbefine.topic.domain.Topic;
import com.mapbefine.mapbefine.topic.domain.TopicRepository;
import com.mapbefine.mapbefine.topic.domain.TopicStatus;
import com.mapbefine.mapbefine.topic.exception.TopicErrorCode;
import com.mapbefine.mapbefine.topic.exception.TopicException.TopicNotFoundException;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -16,22 +23,35 @@
public class PermissionQueryService {

private final PermissionRepository permissionRepository;
private final TopicRepository topicRepository;

public PermissionQueryService(PermissionRepository permissionRepository) {
public PermissionQueryService(PermissionRepository permissionRepository, TopicRepository topicRepository) {
this.permissionRepository = permissionRepository;
this.topicRepository = topicRepository;
}

public List<PermissionResponse> findAllTopicPermissions(Long topicId) {
return permissionRepository.findAllByTopicId(topicId)
public TopicAccessDetailResponse findTopicAccessDetailById(Long topicId) {
Publicity publicity = findTopicPublicityById(topicId);
/// TODO: 2023/09/15 이럴거면 topic.getPermissions 로 하는 게 나을 수도 있나? TopicController 에서 하는 게 더 자연스러운 것 같기도..
Copy link
Collaborator

@junpakPark junpakPark Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 TODO처럼 생각합니당!
Member의 권한 목록을 멤버에서 확인하고 있는거랑 일관성을 맞출 수 있을 것 같아요 ㅋㅋ

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저두요 !!

List<PermissionedMemberResponse> permissionedMembers = permissionRepository.findAllByTopicId(topicId)
.stream()
.map(PermissionResponse::from)
.map(PermissionedMemberResponse::from)
.toList();

return new TopicAccessDetailResponse(publicity, permissionedMembers);
}

private Publicity findTopicPublicityById(Long topicId) {
return topicRepository.findById(topicId)
.map(Topic::getTopicStatus)
.map(TopicStatus::getPublicity)
.orElseThrow(() -> new TopicNotFoundException(TopicErrorCode.TOPIC_NOT_FOUND, topicId));
}

public PermissionDetailResponse findPermissionById(Long permissionId) {
public PermissionMemberDetailResponse findPermissionById(Long permissionId) {
Permission permission = permissionRepository.findById(permissionId)
.orElseThrow(() -> new PermissionNotFoundException(PERMISSION_NOT_FOUND, permissionId));

return PermissionDetailResponse.from(permission);
return PermissionMemberDetailResponse.from(permission);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
@Getter
public class Permission extends BaseTimeEntity {

// TODO 매핑 테이블인데 Id를 가져야 할까?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 값타입 컬렉션 - map을 써보는 건 어떨까 생각하긴 했어요 ㅋㅋ

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
import com.mapbefine.mapbefine.permission.domain.Permission;
import java.time.LocalDateTime;

public record PermissionDetailResponse(
public record PermissionMemberDetailResponse(
Long id,
LocalDateTime updatedAt,
MemberDetailResponse memberDetailResponse
) {

public static PermissionDetailResponse from(Permission permission) {
return new PermissionDetailResponse(
public static PermissionMemberDetailResponse from(Permission permission) {
return new PermissionMemberDetailResponse(
permission.getId(),
permission.getUpdatedAt(),
MemberDetailResponse.from(permission.getMember())
Expand Down
Loading