diff --git a/backend/src/main/java/com/woowacourse/friendogly/member/controller/MemberController.java b/backend/src/main/java/com/woowacourse/friendogly/member/controller/MemberController.java index d8003627e..220ca1395 100644 --- a/backend/src/main/java/com/woowacourse/friendogly/member/controller/MemberController.java +++ b/backend/src/main/java/com/woowacourse/friendogly/member/controller/MemberController.java @@ -1,12 +1,17 @@ package com.woowacourse.friendogly.member.controller; +import com.woowacourse.friendogly.auth.Auth; import com.woowacourse.friendogly.common.ApiResponse; import com.woowacourse.friendogly.member.dto.request.SaveMemberRequest; +import com.woowacourse.friendogly.member.dto.response.FindMemberResponse; import com.woowacourse.friendogly.member.dto.response.SaveMemberResponse; import com.woowacourse.friendogly.member.service.MemberCommandService; +import com.woowacourse.friendogly.member.service.MemberQueryService; import jakarta.validation.Valid; import java.net.URI; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -16,9 +21,11 @@ @RequestMapping("/members") public class MemberController { + private final MemberQueryService memberQueryService; private final MemberCommandService memberCommandService; - public MemberController(MemberCommandService memberCommandService) { + public MemberController(MemberQueryService memberQueryService, MemberCommandService memberCommandService) { + this.memberQueryService = memberQueryService; this.memberCommandService = memberCommandService; } @@ -28,4 +35,16 @@ public ResponseEntity> saveMember(@RequestBody @ return ResponseEntity.created(URI.create("/members/" + response.id())) .body(ApiResponse.ofSuccess(response)); } + + @GetMapping("/mine") + public ApiResponse findMine(@Auth Long memberId) { + FindMemberResponse response = memberQueryService.findById(memberId); + return ApiResponse.ofSuccess(response); + } + + @GetMapping("/{id}") + public ApiResponse findById(@PathVariable Long id) { + FindMemberResponse response = memberQueryService.findById(id); + return ApiResponse.ofSuccess(response); + } } diff --git a/backend/src/main/java/com/woowacourse/friendogly/member/dto/response/FindMemberResponse.java b/backend/src/main/java/com/woowacourse/friendogly/member/dto/response/FindMemberResponse.java new file mode 100644 index 000000000..c9b99db9f --- /dev/null +++ b/backend/src/main/java/com/woowacourse/friendogly/member/dto/response/FindMemberResponse.java @@ -0,0 +1,20 @@ +package com.woowacourse.friendogly.member.dto.response; + +import com.woowacourse.friendogly.member.domain.Member; + +public record FindMemberResponse( + Long id, + String name, + String tag, + String email +) { + + public FindMemberResponse(Member member) { + this( + member.getId(), + member.getName().getValue(), + member.getTag(), + member.getEmail().getValue() + ); + } +} diff --git a/backend/src/main/java/com/woowacourse/friendogly/member/service/MemberQueryService.java b/backend/src/main/java/com/woowacourse/friendogly/member/service/MemberQueryService.java index 071e30665..d20dc3774 100644 --- a/backend/src/main/java/com/woowacourse/friendogly/member/service/MemberQueryService.java +++ b/backend/src/main/java/com/woowacourse/friendogly/member/service/MemberQueryService.java @@ -1,7 +1,23 @@ package com.woowacourse.friendogly.member.service; +import com.woowacourse.friendogly.exception.FriendoglyException; +import com.woowacourse.friendogly.member.domain.Member; +import com.woowacourse.friendogly.member.dto.response.FindMemberResponse; +import com.woowacourse.friendogly.member.repository.MemberRepository; import org.springframework.stereotype.Service; @Service public class MemberQueryService { + + private final MemberRepository memberRepository; + + public MemberQueryService(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } + + public FindMemberResponse findById(Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new FriendoglyException("존재하지 않는 회원입니다.")); + return new FindMemberResponse(member); + } } diff --git a/backend/src/test/java/com/woowacourse/friendogly/docs/MemberApiDocsTest.java b/backend/src/test/java/com/woowacourse/friendogly/docs/MemberApiDocsTest.java index 9dce4111b..4a948ba42 100644 --- a/backend/src/test/java/com/woowacourse/friendogly/docs/MemberApiDocsTest.java +++ b/backend/src/test/java/com/woowacourse/friendogly/docs/MemberApiDocsTest.java @@ -1,22 +1,29 @@ package com.woowacourse.friendogly.docs; +import static com.epages.restdocs.apispec.ResourceDocumentation.headerWithName; +import static com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName; import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.mockito.ArgumentMatchers.any; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper; import com.epages.restdocs.apispec.ResourceSnippetParameters; import com.epages.restdocs.apispec.Schema; +import com.woowacourse.friendogly.auth.AuthArgumentResolver; import com.woowacourse.friendogly.member.controller.MemberController; import com.woowacourse.friendogly.member.dto.request.SaveMemberRequest; +import com.woowacourse.friendogly.member.dto.response.FindMemberResponse; import com.woowacourse.friendogly.member.dto.response.SaveMemberResponse; import com.woowacourse.friendogly.member.service.MemberCommandService; import com.woowacourse.friendogly.member.service.MemberQueryService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; import org.springframework.restdocs.payload.JsonFieldType; @@ -30,6 +37,9 @@ public class MemberApiDocsTest extends RestDocsTest { @MockBean private MemberQueryService memberQueryService; + @Autowired + private AuthArgumentResolver authArgumentResolver; + @DisplayName("회원 생성 문서화") @Test void saveMember_Success() throws Exception { @@ -57,8 +67,7 @@ void saveMember_Success() throws Exception { fieldWithPath("isSuccess").type(JsonFieldType.BOOLEAN).description("요청 성공 여부"), fieldWithPath("data.id").type(JsonFieldType.NUMBER).description("회원 id"), fieldWithPath("data.name").type(JsonFieldType.STRING).description("회원 이름"), - fieldWithPath("data.tag").type(JsonFieldType.STRING) - .description("중복된 회원 이름을 식별하기 위한 고유한 문자열"), + fieldWithPath("data.tag").type(JsonFieldType.STRING).description("중복된 회원 이름을 식별하기 위한 고유한 문자열"), fieldWithPath("data.email").type(JsonFieldType.STRING).description("회원 이메일")) .requestSchema(Schema.schema("saveMemberRequest")) .responseSchema(Schema.schema("응답DTO 이름")) @@ -66,8 +75,86 @@ void saveMember_Success() throws Exception { ); } + @DisplayName("회원 단건 조회 문서화") + @Test + void findById_Success() throws Exception { + Long memberId = 1L; + FindMemberResponse response = new FindMemberResponse( + memberId, + "땡이", + "ugab3odb", + "ddang@email.com" + ); + + Mockito.when(memberQueryService.findById(any())) + .thenReturn(response); + + mockMvc.perform(RestDocumentationRequestBuilders.get("/members/{id}", memberId) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(MockMvcRestDocumentationWrapper.document("member-findById-200", + getDocumentRequest(), + getDocumentResponse(), + resource(ResourceSnippetParameters.builder() + .tag("Member API") + .summary("회원 단건 조회 API") + .pathParameters( + parameterWithName("id").description("조회하려는 회원 id") + ) + .responseFields( + fieldWithPath("isSuccess").type(JsonFieldType.BOOLEAN).description("요청 성공 여부"), + fieldWithPath("data.id").type(JsonFieldType.NUMBER).description("회원 id"), + fieldWithPath("data.name").type(JsonFieldType.STRING).description("회원 이름"), + fieldWithPath("data.tag").type(JsonFieldType.STRING).description("회원 고유 식별자"), + fieldWithPath("data.email").type(JsonFieldType.STRING).description("회원 이메일") + ) + .requestSchema(Schema.schema("FindMemberByIdRequest")) + .responseSchema(Schema.schema("FindMemberResponse")) + .build())) + ); + } + + @DisplayName("내 회원 정보 조회 문서화") + @Test + void findMine_Success() throws Exception { + Long loginMemberId = 1L; + FindMemberResponse response = new FindMemberResponse( + loginMemberId, + "땡이", + "ugab3odb", + "ddang@email.com" + ); + + Mockito.when(memberQueryService.findById(any())) + .thenReturn(response); + + mockMvc.perform(RestDocumentationRequestBuilders.get("/members/mine") + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, loginMemberId.toString())) + .andExpect(status().isOk()) + .andDo(MockMvcRestDocumentationWrapper.document("member-findMine-200", + getDocumentRequest(), + getDocumentResponse(), + resource(ResourceSnippetParameters.builder() + .tag("Member API") + .summary("내 회원 정보 조회 API") + .requestHeaders( + headerWithName("Authorization").description("로그인한 회원 id") + ) + .responseFields( + fieldWithPath("isSuccess").type(JsonFieldType.BOOLEAN).description("요청 성공 여부"), + fieldWithPath("data.id").type(JsonFieldType.NUMBER).description("회원 id"), + fieldWithPath("data.name").type(JsonFieldType.STRING).description("회원 이름"), + fieldWithPath("data.tag").type(JsonFieldType.STRING).description("회원 고유 식별자"), + fieldWithPath("data.email").type(JsonFieldType.STRING).description("회원 이메일") + ) + .responseSchema(Schema.schema("FindMemberResponse")) + .build())) + ); + } + @Override protected Object controller() { - return new MemberController(memberCommandService); + return new MemberController(memberQueryService, memberCommandService); } }