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 5 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);
}

}
12 changes: 11 additions & 1 deletion src/main/java/doore/member/api/MemberController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import doore.member.application.MemberCommandService;
import doore.member.application.MemberQueryService;
import doore.member.application.dto.request.MemberUpdateRequest;
import doore.member.application.dto.response.MemberAndMyTeamsAndStudiesResponse;
import doore.member.domain.Member;
import doore.resolver.LoginMember;
Expand All @@ -12,6 +13,7 @@
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.RestController;

@Validated
Expand Down Expand Up @@ -44,9 +46,17 @@ public ResponseEntity<Void> deleteMember(@LoginMember final Member member) {
return ResponseEntity.noContent().build();
}

@PatchMapping("/profile/members/{memberId}")
public ResponseEntity<Void> updateMyPage(@PathVariable Long memberId,
@RequestBody MemberUpdateRequest memberUpdateRequest,
@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.

파라미터에 final 붙여주시면 좋을 것 같아요!

memberCommandService.updateMyPage(member.getId(), memberId, memberUpdateRequest);
return ResponseEntity.noContent().build();
}

@GetMapping("/members/{memberId}")
public ResponseEntity<MemberAndMyTeamsAndStudiesResponse> getSideBarInfo(@PathVariable final Long memberId,
@LoginMember final Member member) {
@LoginMember final Member member) {
return ResponseEntity.ok(memberQueryService.getSideBarInfo(memberId, member.getId()));
}
}
17 changes: 17 additions & 0 deletions src/main/java/doore/member/application/MemberCommandService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static doore.team.exception.TeamExceptionType.NOT_FOUND_TEAM;

import doore.login.application.dto.response.GoogleAccountProfileResponse;
import doore.member.application.dto.request.MemberUpdateRequest;
import doore.member.domain.Member;
import doore.member.domain.StudyRole;
import doore.member.domain.TeamRole;
Expand Down Expand Up @@ -100,4 +101,20 @@ private Team validateExistTeam(final Long teamId) {
private Study validateExistStudy(final Long studyId) {
return studyRepository.findById(studyId).orElseThrow(() -> new StudyException(NOT_FOUND_STUDY));
}

public void updateMyPage(Long tokenMemberId, Long pathMemberId, MemberUpdateRequest memberUpdateRequest) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

여기도 final 붙여주세요! 다른 코드에도 동일하게 파라미터와 객체에 붙여주시면 좋을 것 같아요~

if (!tokenMemberId.equals(pathMemberId)) {
throw new MemberException(UNAUTHORIZED);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

updateMyPage라는 메서드의 역할과 맞지 않는 것 같아요! 다른 메서드로 빼서 작성해보는건 어떨까요?? 예시로 study domain의 나의 스터디 조회 메서드를 참고하시면 좋을 것 같아요~!


Member member = validateExistMember(pathMemberId);
Member updatedMember = Member.builder()
.id(pathMemberId)
.name(memberUpdateRequest.getNewName())
.googleId(member.getGoogleId())
.email(member.getEmail())
.imageUrl(member.getImageUrl())
.build();
memberRepository.save(updatedMember);
Copy link
Collaborator

Choose a reason for hiding this comment

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

    public void updateName(String name) {
        this.name = name;
    }

member entity에 해당 코드를 작성하시면 builder를 이용해서 객체를 생성하지 않아도 데이터베이스에 값이 변경될 것 같아요~ 저희가 수정할 데이터는 이름 하나니까용

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package doore.member.application.dto.request;

import lombok.Data;

@Data
public class MemberUpdateRequest {
private String newName;
}
Copy link
Collaborator

@JJimini JJimini Aug 2, 2024

Choose a reason for hiding this comment

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

저희는 request를 record로 생성하고 있습니다!
Dto를 Record로 생성하는 것에 대해

Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import static doore.member.domain.TeamRoleType.ROLE_팀원;
import static doore.member.domain.TeamRoleType.ROLE_팀장;
import static doore.member.exception.MemberExceptionType.NOT_FOUND_MEMBER;
import static doore.member.exception.MemberExceptionType.UNAUTHORIZED;
import static doore.team.exception.TeamExceptionType.NOT_FOUND_TEAM;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertAll;

import doore.helper.IntegrationTest;
import doore.login.application.dto.response.GoogleAccountProfileResponse;
import doore.member.application.dto.request.MemberUpdateRequest;
import doore.member.domain.Member;
import doore.member.domain.StudyRole;
import doore.member.domain.TeamRole;
Expand Down Expand Up @@ -229,4 +231,40 @@ 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.

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

MemberUpdateRequest request = new MemberUpdateRequest();
request.setNewName("요시");
Copy link
Collaborator

Choose a reason for hiding this comment

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

setter로 값을 설정하면 값을 바꾼 의도를 파악하기 쉽지 않는 문제 등 다양한 문제가 있어서 사용하지 않고 있어요~!
request에서 buildernew를 이용해서 생성 가능하도록 만들 수 있을 것 같아요! 여기서는 값이 간단하니, requestnew를 사용하는 방법도 좋겠네요!


memberCommandService.updateMyPage(member.getId(), member.getId(), request);

Member findMember = memberRepository.findById(member.getId()).orElseThrow();
assertThat(findMember.getName()).isEqualTo("요시");
}

@Test
@DisplayName("[실패] 프로필 이름 수정 시 유효하지 않은 회원이면 실패한다.")
void updateMyPageName_유효하지_않은_회원이_프로필_이름_수정을_시도하면_실패한다() {
Long invalidMemberId = 10L;

MemberUpdateRequest request = new MemberUpdateRequest();
request.setNewName("요시");
Copy link
Collaborator

Choose a reason for hiding this comment

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

위의 코멘트와 동일합니다!


assertThatThrownBy(() -> {
memberCommandService.updateMyPage(member.getId(), invalidMemberId, request);
}).isInstanceOf(MemberException.class).hasMessage(NOT_FOUND_MEMBER.errorMessage());
}

@Test
@DisplayName("[실패] 프로필 이름 수정 시 권한이 없으면 실패한다.")
void updateMyPageName_권한이_없는_회원이_프로필_이름_수정을_시도하면_실패한다() {
MemberUpdateRequest request = new MemberUpdateRequest();
request.setNewName("요시");
Copy link
Collaborator

Choose a reason for hiding this comment

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

동일합니다!


assertThatThrownBy(() -> {
memberCommandService.updateMyPage(2L, member.getId(), request);
Copy link
Collaborator

Choose a reason for hiding this comment

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

저희가 권한이라는 단어는 직위와 관련하여 사용하고 있어요~ 권한이 없으면 실패한다라는 네이밍도 좋지만 본인이 아닐 경우 프로필 이름 수정은 실패한다 등의 이름으로 바꿔주면 더 좋을 것 같아요!

그리고 tokenMemberIdinvalid한 값으로 설정해주셨는데, 저희가 검증하는 flow는 클라이언트로부터 받아오는 값이 토큰의 값과 같은지 확인한다 입니다 그래서 invalid한 값은 memberId로 변경하시면 더 좋을 것 같아용

}).isInstanceOf(MemberException.class).hasMessage(UNAUTHORIZED.errorMessage());
}
}
55 changes: 13 additions & 42 deletions src/test/java/doore/restdocs/docs/MemberApiDocsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,18 @@
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import doore.member.application.dto.response.MemberAndMyTeamsAndStudiesResponse;
import doore.member.application.dto.request.MemberUpdateRequest;
import doore.restdocs.RestDocsTest;
import doore.study.application.dto.response.StudyNameResponse;
import doore.team.application.dto.response.MyTeamsAndStudiesResponse;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
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;
import org.springframework.restdocs.payload.ResponseFieldsSnippet;
import org.springframework.restdocs.request.PathParametersSnippet;

public class MemberApiDocsTest extends RestDocsTest {
private String accessToken;
Expand Down Expand Up @@ -71,40 +64,18 @@ void setUp() {
}

@Test
@DisplayName("[성공] 사이드바에 들어가는 정보를 조회한다.")
void getSideBarInfo_사이드바에_들어가는_정보를_조회한다_성공() throws Exception {
//given
final Long memberId = 1L;
final List<StudyNameResponse> studyResponses = List.of(
new StudyNameResponse(1L, "알고리즘 스터디"),
new StudyNameResponse(2L, "개발 스터디")
);
final List<MyTeamsAndStudiesResponse> response = List.of(
new MyTeamsAndStudiesResponse(1L, "BDD", studyResponses)
);
final MemberAndMyTeamsAndStudiesResponse memberAndMyTeamsAndStudiesResponse = new MemberAndMyTeamsAndStudiesResponse(
1L, "이름", "프로필사진", response);
final PathParametersSnippet pathParameters = pathParameters(
parameterWithName("memberId").description("사이드바 정보목록을 조회하는 회원 ID")
);
final ResponseFieldsSnippet responseFieldsSnippet = responseFields(
numberFieldWithPath("id", "멤버 ID"),
stringFieldWithPath("name", "멤버 이름"),
stringFieldWithPath("imageUrl", "멤버 프로필 경로"),
numberFieldWithPath("myTeamsAndStudies[].teamId", "팀 ID"),
stringFieldWithPath("myTeamsAndStudies[].teamName", "팀 이름"),
numberFieldWithPath("myTeamsAndStudies[].teamStudies[].id", "팀에 포함되는 스터디 id"),
stringFieldWithPath("myTeamsAndStudies.[].teamStudies[].name", "팀에 포함되는 스터디 이름")
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

다른 api 테스트 코드가 삭제된 것 같아요ㅠㅠ 복구해주시면 좋을 것 같습니다ㅠㅠ

@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)
.updateMyPage(any(Long.class), any(Long.class), any(MemberUpdateRequest.class));

//when
when(memberQueryService.getSideBarInfo(any(), any())).thenReturn(memberAndMyTeamsAndStudiesResponse);

//then
mockMvc.perform(get("/members/{memberId}", memberId)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("get-sidebar-info", pathParameters, responseFieldsSnippet));
Copy link
Collaborator

Choose a reason for hiding this comment

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

이것도용ㅠㅠ

mockMvc.perform(RestDocumentationRequestBuilders.patch("/profile/members/{memberId}", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(requestJson)
.header(HttpHeaders.AUTHORIZATION, accessToken))
.andExpect(status().isNoContent())
.andDo(document("update-my-page-name", pathParameters(
parameterWithName("memberId").description("회원 id"))));
}
}