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

Feature/#133-마이페이지_회원_정보_수정_기능을_제작한다 #159

Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
1 change: 0 additions & 1 deletion src/main/java/doore/DooreApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ public class DooreApplication {
public static void main(String[] args) {
SpringApplication.run(DooreApplication.class, args);
}

}
6 changes: 6 additions & 0 deletions src/main/java/doore/member/api/MemberController.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.web.bind.annotation.DeleteMapping;
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.RestController;

@Validated
Expand Down Expand Up @@ -38,4 +39,9 @@ public ResponseEntity<Void> deleteMember(@LoginMember Member member) {
return ResponseEntity.noContent().build();
}

@PatchMapping("/profile/name")
public ResponseEntity<Void> updateMyPageName(@RequestBody String newName, @LoginMember Member member) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

마이페이지는 회원 본인만 접근이 가능해야합니다! URI에 회원의 정보를 받아오면 좋을 것 같습니다! "/profile/members/{memberId}"로 수정하는 것은 어떨까 제안드려봅니다!

또한 메서드명이 너무 상세합니다. 회원의 이름을 수정하는 메서드가 아닌 회원의 정보를 수정하는 메서드가 되면 좋을 것 같습니다. ex) updateMyPage
이렇게 작성하면 추후 수정할 회원 정보가 확장되면 추가하기 쉽습니다~! (이름을 수정하는 메서드, 이메일을 수정하는 메서드 등등 수정 내용이 생길때마다 메서드를 만들 수는 없으니까요!)

추가로 저희는 DTO로 request를 받아옵니다! request에 수정할 회원 정보를 담으시면 될 것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@JJimini MemberArgumentResolver에서 보면 request의 헤더를 통해 전달되는 토큰을 이용해 memberID 정보를 들고 올 수 있는데 혹시 memberID를 path variable에 한 번 더 쓰는 이유가 있을까요?

Copy link
Collaborator

Choose a reason for hiding this comment

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

우선 저희 두레 프로젝트는 URL에 memberId를 명시적으로 표현하여 RESTful API의 일관성을 유지하고자 했으며, 이것은 초기 컨벤션 논의 때 규칙으로 정하였고 그렇게 사용하고 있습니다!

memberCommandService.updateMyPageName(member.getId(), newName);
return ResponseEntity.noContent().build();
}
}
12 changes: 12 additions & 0 deletions src/main/java/doore/member/application/MemberCommandService.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,16 @@ private Team validateExistTeam(Long teamId) {
private Study validateExistStudy(Long studyId) {
return studyRepository.findById(studyId).orElseThrow(() -> new StudyException(NOT_FOUND_STUDY));
}

public void updateMyPageName(Long memberId, String newName) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

앞 리뷰와 동일합니다~ 메서드명 수정하면 좋을 것 같아요!

Member member = validateExistMember(memberId);
Member updatedMember = Member.builder()
Copy link
Collaborator

Choose a reason for hiding this comment

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

저희가 클라이언트로부터 받아온 memberId와 토큰의 memberId가 같은지(본인확인절차)가 필요할 것 같습니다~

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@JJimini 프론트의 localStorage에서 저장한 것은 클라이언트에서 바꿀 수 있어서 그 값을 받아서 굳이 확인하기보다는 저희가 보낸 토큰을 통해 복호화하여 멤버의 권한을 확인 할 필요 없이 그 사람의 myPage를 보여주는 것이 괜찮을 것 같은데 어떻게 생각하시나요?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@jimini1026 우선 저희 두레는 회원(본인) 접근 api는 2가지 검증 절차를 거칩니다.

  1. 클라이언트로 받은 값이 회원이 맞는지 확인
  2. 그 회원이 토큰에 들어있는 회원과 일치하는지 확인

1번은 클라이언트에서 변경할 수 있기 때문에 확인하는 것이고, 그 값이 맞다면 2번 확인을 통해 본인이 맞는지 확인합니다.

예를 들어, 클라이언트로부터 memberId가 1이 들어왔고 token에 있는 memberId는 5라고 가정합시다. memberId 1이 데이터베이스에 존재하면 1번 검증을 통과하지만 본인이 아니기 때문에 2번 검증에서 걸러지는 것이죠.

요시님 말씀처럼 토큰의 memberId를 가지고 해당 API에 접근할 수 있도록 한다면 어떻게 API를 요청한 사람이 본인인지 확인할 수 있는지 잘 모르겠습니다,,

.id(memberId)
.name(newName)
.googleId(member.getGoogleId())
.email(member.getEmail())
.imageUrl(member.getImageUrl())
.build();
Copy link
Collaborator

@JJimini JJimini Jun 26, 2024

Choose a reason for hiding this comment

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

이 부분은 더 간단하게 작성할 수 있는 방법이 있을 것 같아용~ 업데이트 요청이 있을 때마다 객체를 생성하여 db에 저장하면 성능 측면에서 문제가 있을 것 같아요~!

Study entity나 CurriculumItem entity, Team entity를 참고하시면 좋을 것 같습니다

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

네 좋습니다!

memberRepository.save(updatedMember);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,14 @@ void init() {
memberCommandService.transferStudyLeader(study.getId(), member.getId(), notStudyLeaderMember.getId());
});
}

@Test
@DisplayName("[성공] 프로필 이름 수정에 성공한다.")
void updateMyPageName_프로필_이름_수정에_성공한다_성공() {
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.

여기 메서드 이름이 안바뀐 것 같아요!

String updateName = "요시";
memberCommandService.updateMyPageName(member.getId(), updateName);
Copy link
Collaborator

Choose a reason for hiding this comment

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

request를 전달하고, request 내용대로 정보가 바뀌었는지 테스트하는 코드를 짜면 좋을 것 같아요~


Member findMember = memberRepository.findById(member.getId()).get();
Copy link
Collaborator

Choose a reason for hiding this comment

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

여기서 .get()은 따로 동작하지 않아요~ 혹시 findById를 했을 때, error가 떠서 작성하신거라면, findByIdOptional 객체를 반환하기 때문에 데이터가 존재하지 않는 경우를 위한 예외 처리가 필요해서 그런겁니다!
테스트니까 .orElseThrow()로 처리하시고, 예외 처리가 정상적으로 되는지 확인하는 실패테스트를 작성해보시는 것도 좋을 것 같아요!

assertThat(findMember.getName()).isEqualTo(updateName);
}
}
16 changes: 16 additions & 0 deletions src/test/java/doore/restdocs/docs/MemberApiDocsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;

public class MemberApiDocsTest extends RestDocsTest {
Expand Down Expand Up @@ -61,4 +62,19 @@ void setUp() {
.andDo(document("delete-member"));
}

//todo: docs code

Copy link
Collaborator

Choose a reason for hiding this comment

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

todo는 진행하셨으면 삭제해주셔도 될 것 같아요~

@Test
@DisplayName("[성공] 프로필 이름 수정에 성공한다.")
void updateMyPageName_프로필_이름_수정에_성공한다() throws Exception {
String requestJson = "{\"newName\":\"요시\"}";
Copy link
Collaborator

Choose a reason for hiding this comment

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

request를 생성할 수 있다면 코드가 바뀌겠죵?

doNothing().when(memberCommandService).updateMyPageName(any(), any());
Copy link
Member

Choose a reason for hiding this comment

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

위에서 미나님이 피드백 해주셨듯이 실제 service의 return값은 dto이니 String 대신 결과 응답 dto를 만들면 좋을 거 같아요~!
거기에 맞는 requestFields도 추가해주시고요!


mockMvc.perform(RestDocumentationRequestBuilders.patch("/profile/name")
.contentType(MediaType.APPLICATION_JSON)
.content(requestJson)
.header(HttpHeaders.AUTHORIZATION, accessToken))
.andExpect(status().isNoContent())
.andDo(document("update-my-page-name"));
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

이 부분도 request를 사용하면 코드가 바뀌겠죠?! 그리고 asciidoc - member.adoc 파일에 추가하면 프론트분들이 보고 작업하신답니다~

}