diff --git a/src/main/java/me/golf/blog/domain/board/api/BoardController.java b/src/main/java/me/golf/blog/domain/board/api/BoardController.java index 89aca37..091460f 100644 --- a/src/main/java/me/golf/blog/domain/board/api/BoardController.java +++ b/src/main/java/me/golf/blog/domain/board/api/BoardController.java @@ -6,6 +6,7 @@ import me.golf.blog.domain.board.domain.persist.SearchKeywordRequest; import me.golf.blog.domain.board.dto.*; import me.golf.blog.domain.member.domain.vo.Email; +import me.golf.blog.domain.member.domain.vo.Nickname; import me.golf.blog.global.common.PageCustomResponse; import me.golf.blog.global.security.principal.CustomUserDetails; import org.springframework.data.domain.Pageable; @@ -45,11 +46,11 @@ public ResponseEntity> findAll( return ResponseEntity.ok(boardReadService.findAll(keyword, pageable)); } - @GetMapping("/public/boards/{email}") + @GetMapping("/public/boards/{nickname}") public ResponseEntity> findByEmail( - @PathVariable String email, + @PathVariable String nickname, @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { - return ResponseEntity.ok().body(boardReadService.findByEmail(Email.from(email), pageable)); + return ResponseEntity.ok().body(boardReadService.getByNickname(Nickname.from(nickname), pageable)); } @PatchMapping("/boards/{boardId}") diff --git a/src/main/java/me/golf/blog/domain/board/application/BoardReadService.java b/src/main/java/me/golf/blog/domain/board/application/BoardReadService.java index 73d68c0..0a446d3 100644 --- a/src/main/java/me/golf/blog/domain/board/application/BoardReadService.java +++ b/src/main/java/me/golf/blog/domain/board/application/BoardReadService.java @@ -8,7 +8,7 @@ import me.golf.blog.domain.board.domain.vo.BoardStatus; import me.golf.blog.domain.board.dto.*; import me.golf.blog.domain.board.error.BoardNotFoundException; -import me.golf.blog.domain.member.domain.vo.Email; +import me.golf.blog.domain.member.domain.vo.Nickname; import me.golf.blog.global.common.PageCustomResponse; import me.golf.blog.global.config.RedisPolicy; import me.golf.blog.global.error.exception.ErrorCode; @@ -38,9 +38,9 @@ public PageCustomResponse findAll(final SearchKeywordRequest s } @Transactional(readOnly = true) - public PageCustomResponse findByEmail(final Email email, final Pageable pageable) { + public PageCustomResponse getByNickname(final Nickname nickname, final Pageable pageable) { - return boardRepository.findByEmail(email, pageable); + return boardRepository.findByNickname(nickname, pageable); } @Transactional(readOnly = true) diff --git a/src/main/java/me/golf/blog/domain/board/domain/persist/BoardCustomRepository.java b/src/main/java/me/golf/blog/domain/board/domain/persist/BoardCustomRepository.java index 084dd0e..8059f7e 100644 --- a/src/main/java/me/golf/blog/domain/board/domain/persist/BoardCustomRepository.java +++ b/src/main/java/me/golf/blog/domain/board/domain/persist/BoardCustomRepository.java @@ -4,6 +4,7 @@ import me.golf.blog.domain.board.dto.BoardResponse; import me.golf.blog.domain.board.dto.TempBoardListResponse; import me.golf.blog.domain.member.domain.vo.Email; +import me.golf.blog.domain.member.domain.vo.Nickname; import me.golf.blog.global.common.PageCustomResponse; import org.springframework.data.domain.Pageable; @@ -11,7 +12,7 @@ public interface BoardCustomRepository { PageCustomResponse findAllWithQuery(final SearchKeywordRequest searchKeyword, final Pageable pageable); - PageCustomResponse findByEmail(final Email email, final Pageable pageable); + PageCustomResponse findByNickname(final Nickname nickname, final Pageable pageable); PageCustomResponse findAllTempBoard(final Long memberId, final Pageable pageable); Optional getBoardDetail(final Long boardId); } diff --git a/src/main/java/me/golf/blog/domain/board/domain/persist/BoardCustomRepositoryImpl.java b/src/main/java/me/golf/blog/domain/board/domain/persist/BoardCustomRepositoryImpl.java index 27d4844..c24b777 100644 --- a/src/main/java/me/golf/blog/domain/board/domain/persist/BoardCustomRepositoryImpl.java +++ b/src/main/java/me/golf/blog/domain/board/domain/persist/BoardCustomRepositoryImpl.java @@ -10,6 +10,7 @@ import me.golf.blog.domain.board.dto.BoardResponse; import me.golf.blog.domain.board.dto.TempBoardListResponse; import me.golf.blog.domain.member.domain.vo.Email; +import me.golf.blog.domain.member.domain.vo.Nickname; import me.golf.blog.global.common.PageCustomResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -52,7 +53,7 @@ public PageCustomResponse findAllWithQuery(final SearchKeyword return getPageResponse(pageable, boards); } - public PageCustomResponse findByEmail(Email email, Pageable pageable) { + public PageCustomResponse findByNickname(Nickname nickname, Pageable pageable) { List boards = query.select(Projections.constructor(BoardAllResponse.class, board.id, board.title, @@ -61,7 +62,7 @@ public PageCustomResponse findByEmail(Email email, Pageable pa board.createTime.as("createdAt")) ) .from(board) - .innerJoin(member).on(member.id.eq(board.memberId)) + .innerJoin(member).on(member.nickname.eq(nickname)) .where(board.status.eq(BoardStatus.SAVE)) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) diff --git a/src/main/java/me/golf/blog/domain/member/api/MemberController.java b/src/main/java/me/golf/blog/domain/member/api/MemberController.java index 46c0f42..b3e3c12 100644 --- a/src/main/java/me/golf/blog/domain/member/api/MemberController.java +++ b/src/main/java/me/golf/blog/domain/member/api/MemberController.java @@ -37,6 +37,11 @@ public ResponseEntity getDetailById() { return ResponseEntity.ok().body(memberReadService.getDetailBy(this.getPrincipal())); } + @GetMapping("/public/members/{nickname}") + public ResponseEntity getDetailByNickname(@PathVariable String nickname) { + return ResponseEntity.ok(memberReadService.getDetailByNickname(nickname)); + } + // findAll @GetMapping("/public/members") public ResponseEntity> getMembers( diff --git a/src/main/java/me/golf/blog/domain/member/application/MemberReadService.java b/src/main/java/me/golf/blog/domain/member/application/MemberReadService.java index 9ac7e4d..47bc8b4 100644 --- a/src/main/java/me/golf/blog/domain/member/application/MemberReadService.java +++ b/src/main/java/me/golf/blog/domain/member/application/MemberReadService.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import me.golf.blog.domain.member.domain.persist.Member; import me.golf.blog.domain.member.domain.persist.MemberRepository; +import me.golf.blog.domain.member.domain.vo.Nickname; import me.golf.blog.domain.member.dto.MemberAllResponse; import me.golf.blog.domain.member.dto.MemberResponse; import me.golf.blog.domain.member.dto.MemberSearch; @@ -22,18 +23,32 @@ public class MemberReadService { private final MemberRepository memberRepository; @Transactional(readOnly = true) - @Cacheable(key = "#userDetails.getId()", value = RedisPolicy.MEMBER_KEY) public MemberResponse getDetailBy(final CustomUserDetails userDetails) { - Member member = memberRepository.findById(userDetails.getId()).orElseThrow( - () -> new MemberNotFoundException(ErrorCode.USER_NOT_FOUND)); + MemberResponse memberResponse = memberRepository.findById(userDetails.getId()) + .map(MemberResponse::of) + .orElseThrow(() -> new MemberNotFoundException(ErrorCode.USER_NOT_FOUND)); - return MemberResponse.of(member); + return saveCache(memberResponse); } @Transactional(readOnly = true) public PageCustomResponse getMembers(final MemberSearch memberSearch, final Pageable pageable) { - return memberRepository.findAllWithSearch(memberSearch, pageable); } + + @Transactional(readOnly = true) + public MemberResponse getDetailByNickname(final String nickname) { + + MemberResponse memberResponse = memberRepository.findByNickname(Nickname.from(nickname)) + .map(MemberResponse::of) + .orElseThrow(() -> new MemberNotFoundException(ErrorCode.USER_NOT_FOUND)); + + return saveCache(memberResponse); + } + + @Cacheable(key = "#member.memberId", value = RedisPolicy.MEMBER_KEY) + public MemberResponse saveCache(final MemberResponse member) { + return member; + } } diff --git a/src/main/java/me/golf/blog/domain/member/domain/persist/MemberRepository.java b/src/main/java/me/golf/blog/domain/member/domain/persist/MemberRepository.java index 06a5a2f..741f033 100644 --- a/src/main/java/me/golf/blog/domain/member/domain/persist/MemberRepository.java +++ b/src/main/java/me/golf/blog/domain/member/domain/persist/MemberRepository.java @@ -2,10 +2,14 @@ import me.golf.blog.domain.member.domain.vo.Email; import me.golf.blog.domain.member.domain.vo.Nickname; +import me.golf.blog.domain.member.dto.MemberResponse; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface MemberRepository extends JpaRepository, MemberCustomRepository { boolean existsByEmail(final Email email); boolean existsByNickname(final Nickname nickname); + Optional findByNickname(final Nickname nickname); } \ No newline at end of file diff --git a/src/main/java/me/golf/blog/domain/member/dto/MemberResponse.java b/src/main/java/me/golf/blog/domain/member/dto/MemberResponse.java index cc832b5..c4191a2 100644 --- a/src/main/java/me/golf/blog/domain/member/dto/MemberResponse.java +++ b/src/main/java/me/golf/blog/domain/member/dto/MemberResponse.java @@ -16,6 +16,8 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class MemberResponse implements Serializable { + + private Long memberId; private Email email; private Name name; private Nickname nickname; @@ -29,7 +31,9 @@ public static MemberResponse of(final Member member) { int now = LocalDate.now().getYear(); int age = now - memberYear; - return new MemberResponse(member.getEmail(), + return new MemberResponse( + member.getId(), + member.getEmail(), member.getName(), member.getNickname(), age, diff --git a/src/main/resources/static/docs/FollowTotal.html b/src/main/resources/static/docs/FollowTotal.html index 207b106..451f6e1 100644 --- a/src/main/resources/static/docs/FollowTotal.html +++ b/src/main/resources/static/docs/FollowTotal.html @@ -599,7 +599,7 @@

Follower

"followerId" : 1, "memberId" : 1, "nickname" : "ssar", - "createAt" : "2022-10-21" + "createAt" : "2022-11-11" } ], "pageSize" : 10, "number" : 0 @@ -616,7 +616,7 @@

Follower

"followerId" : 1, "memberId" : 1, "nickname" : "ssar", - "createAt" : "2022-10-21" + "createAt" : "2022-11-11" } ], "pageSize" : 10, "number" : 0 diff --git a/src/main/resources/static/docs/authTotal.html b/src/main/resources/static/docs/authTotal.html index 4918a03..be1ecd0 100644 --- a/src/main/resources/static/docs/authTotal.html +++ b/src/main/resources/static/docs/authTotal.html @@ -510,7 +510,7 @@

Auth

Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers -Set-Cookie: refreshToken=eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwiaWQiOiIyIiwiZXhwIjoxNjY2MzUzMTk5fQ.kROhg2Oaj4Pm2MSZOCR0xR8Tt9-O8sCHnulUiE6tIIC8zoxM8LQ_auoD7wJzg4OWiyhqno1E-AuNQjJe8vWqHQ; Path=/; Max-Age=18000; Expires=Fri, 21 Oct 2022 11:53:19 GMT; Secure; HttpOnly +Set-Cookie: refreshToken=eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwiaWQiOiIyIiwiZXhwIjoxNjY4MTk0NjU1fQ.CIt5365s4UhOXbW11AAecosJP8PgxmxD-WiZxbHhcA8IT488_xwgQ3W3QT0oTFb3bwakQMt4bYfn0M4aXIssNA; Path=/; Max-Age=18000; Expires=Fri, 11 Nov 2022 19:24:15 GMT; Secure; HttpOnly Content-Type: application/json X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block @@ -521,7 +521,7 @@

Auth

Content-Length: 213 { - "accessToken" : "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjIiLCJyb2xlIjoiUk9MRV9VU0VSIiwiZXhwIjoxNjY2MzM4Nzk5fQ.Ldk7b-QpfQmzHj83VsjDdrPHAX1c_BSKhvLdAB-Bq6QNVoaOoFkk-pQmMvOdUpmV8-Z0Xunc_rETXDw4TA-qUw", + "accessToken" : "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjIiLCJyb2xlIjoiUk9MRV9VU0VSIiwiZXhwIjoxNjY4MTgwMjU1fQ.VMQWH9A0glC6rkaSRunUkCXuiYImurt5bnah1is8jPsHz8i08pEQh6hkSG2JkXF0Se8-61Y8VgIP2IywapkmTQ", "result" : true } @@ -543,7 +543,7 @@

Auth

{
-  "accessToken" : "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjIiLCJyb2xlIjoiUk9MRV9VU0VSIiwiZXhwIjoxNjY2MzM4Nzk5fQ.Ldk7b-QpfQmzHj83VsjDdrPHAX1c_BSKhvLdAB-Bq6QNVoaOoFkk-pQmMvOdUpmV8-Z0Xunc_rETXDw4TA-qUw",
+  "accessToken" : "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjIiLCJyb2xlIjoiUk9MRV9VU0VSIiwiZXhwIjoxNjY4MTgwMjU1fQ.VMQWH9A0glC6rkaSRunUkCXuiYImurt5bnah1is8jPsHz8i08pEQh6hkSG2JkXF0Se8-61Y8VgIP2IywapkmTQ",
   "result" : true
 }
@@ -620,7 +620,7 @@

Auth

DELETE /api/v1/auth/logout HTTP/1.1
 Host: localhost:8080
-Cookie: refreshToken=eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwiaWQiOiIxIiwiZXhwIjoxNjY2MzUzMTk5fQ.6l2VkUcFmXi-NtjXSXKY83mI7rrLbMFvKpNBjDY5Tf1r05xv8TzU6jwCGrQ02dyhlfU8S_tbphvAPuaYS3z0Zg
+Cookie: refreshToken=eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwiaWQiOiIxIiwiZXhwIjoxNjY4MTk0NjU1fQ.y9le7p82iAZz1pJo9KluL0VWZ8zVNtzILzY4E-SwMs4H39lPPzemdpWDeVQbZIOvTTEn6jH9PLIM32-HrM2i7A
@@ -655,7 +655,7 @@

Auth

POST /api/v1/public/auth/reissue HTTP/1.1
 Host: localhost:8080
-Cookie: refreshToken=eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwiaWQiOiIzIiwiZXhwIjoxNjY2MzUzMTk5fQ.ZuEkudrOXH-bmIAZAuctaXcLzmYK2me_MeDYiGVM0Xyta9FTkf0xvTcoU9eZL2bYTzoMeZA_wnwUe-Ft36zyIw
+Cookie: refreshToken=eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiUk9MRV9VU0VSIiwiaWQiOiIzIiwiZXhwIjoxNjY4MTk0NjU1fQ.TASbIijVOr4wH0NrwItXd1yGDPY26R12cg-sgs-_Ctu6Xd5dzMwnpZzZrUDwHj9AwdHhDCSETyxbz64G8DMlQQ
@@ -677,7 +677,7 @@

Auth

Content-Length: 213 { - "accessToken" : "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjMiLCJyb2xlIjoiUk9MRV9VU0VSIiwiZXhwIjoxNjY2MzM4Nzk5fQ.ZuwzCCAdBrqPnjQiq9i7uuDUN35f0MXDtKng_qax0W584tZnyywuUVJV3f0s1Qlfrr5bQHdlar3-hHMHM54iEQ", + "accessToken" : "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjMiLCJyb2xlIjoiUk9MRV9VU0VSIiwiZXhwIjoxNjY4MTgwMjU1fQ.03-m_ob-spf71kmksDRizbCAYK-_zxUvj84E8GgAiN6bucilEWHHqMf76zFOkEb5ughtQQ54K14X7V1tFd6BIA", "result" : true }
@@ -688,7 +688,7 @@

Auth

{
-  "accessToken" : "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjMiLCJyb2xlIjoiUk9MRV9VU0VSIiwiZXhwIjoxNjY2MzM4Nzk5fQ.ZuwzCCAdBrqPnjQiq9i7uuDUN35f0MXDtKng_qax0W584tZnyywuUVJV3f0s1Qlfrr5bQHdlar3-hHMHM54iEQ",
+  "accessToken" : "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6IjMiLCJyb2xlIjoiUk9MRV9VU0VSIiwiZXhwIjoxNjY4MTgwMjU1fQ.03-m_ob-spf71kmksDRizbCAYK-_zxUvj84E8GgAiN6bucilEWHHqMf76zFOkEb5ughtQQ54K14X7V1tFd6BIA",
   "result" : true
 }
diff --git a/src/main/resources/static/docs/memberTotal.html b/src/main/resources/static/docs/memberTotal.html index ec8d15e..930cbac 100644 --- a/src/main/resources/static/docs/memberTotal.html +++ b/src/main/resources/static/docs/memberTotal.html @@ -666,9 +666,10 @@

Member

Pragma: no-cache Expires: 0 X-Frame-Options: DENY -Content-Length: 157 +Content-Length: 175 { + "memberId" : 1, "email" : "ilgolc@naver.com", "name" : "kim3", "nickname" : "ssar", @@ -685,6 +686,7 @@

Member

{
+  "memberId" : 1,
   "email" : "ilgolc@naver.com",
   "name" : "kim3",
   "nickname" : "ssar",
@@ -713,6 +715,11 @@ 

Member

+

memberId

+

Number

+

회원 식별자

+ +

email

String

이메일

diff --git a/src/main/resources/static/docs/replyTotal.html b/src/main/resources/static/docs/replyTotal.html index 6b3cd46..4503960 100644 --- a/src/main/resources/static/docs/replyTotal.html +++ b/src/main/resources/static/docs/replyTotal.html @@ -601,7 +601,7 @@

Reply

{ "data" : [ { "comment" : "안녕하세요, 테스트 입니다.", - "createDate" : "2022-10-21T15:53:10.671525", + "createDate" : "2022-11-11T23:24:06.334438", "createdBy" : "ilgolc@naver.com" } ], "totalPage" : 1, @@ -619,7 +619,7 @@

Reply

{
   "data" : [ {
     "comment" : "안녕하세요, 테스트 입니다.",
-    "createDate" : "2022-10-21T15:53:10.671525",
+    "createDate" : "2022-11-11T23:24:06.334438",
     "createdBy" : "ilgolc@naver.com"
   } ],
   "totalPage" : 1,
diff --git a/src/test/java/me/golf/blog/domain/board/api/BoardControllerTest.java b/src/test/java/me/golf/blog/domain/board/api/BoardControllerTest.java
index c178f4e..4ab38d3 100644
--- a/src/test/java/me/golf/blog/domain/board/api/BoardControllerTest.java
+++ b/src/test/java/me/golf/blog/domain/board/api/BoardControllerTest.java
@@ -162,7 +162,7 @@ void findByEmail() throws Exception {
         PageCustomResponse response = PageCustomResponse.of(new PageImpl<>(boards, pageable, 1));
 
 
-        when(boardReadService.findByEmail(any(), any())).thenReturn(response);
+        when(boardReadService.getByNickname(any(), any())).thenReturn(response);
 
         mockMvc.perform(get("/api/v1/public/boards/ilgolc@naver.com")
                 .accept(MediaType.APPLICATION_JSON))
diff --git a/src/test/java/me/golf/blog/domain/board/application/BoardServiceTest.java b/src/test/java/me/golf/blog/domain/board/application/BoardServiceTest.java
index 140df89..1250899 100644
--- a/src/test/java/me/golf/blog/domain/board/application/BoardServiceTest.java
+++ b/src/test/java/me/golf/blog/domain/board/application/BoardServiceTest.java
@@ -119,7 +119,7 @@ void findByEmail() {
         Pageable pageable = PageRequest.of(0, 10);
 
         // when
-        List responses = boardReadService.findByEmail(GivenMember.GIVEN_EMAIL, pageable).getData();
+        List responses = boardReadService.getByNickname(GivenMember.GIVEN_NICKNAME, pageable).getData();
 
         // then
         assertThat(responses.get(0).getTitle()).isEqualTo(GIVEN_TITLE);
diff --git a/src/test/java/me/golf/blog/domain/member/api/MemberControllerTest.java b/src/test/java/me/golf/blog/domain/member/api/MemberControllerTest.java
index 30e4c95..114dd0f 100644
--- a/src/test/java/me/golf/blog/domain/member/api/MemberControllerTest.java
+++ b/src/test/java/me/golf/blog/domain/member/api/MemberControllerTest.java
@@ -46,16 +46,9 @@
 class MemberControllerTest extends AbstractContainerBaseTest {
     @Autowired MockMvc mockMvc;
     @MockBean MemberService memberService;
-    ObjectMapper objectMapper;
+    @Autowired ObjectMapper objectMapper;
     @MockBean MemberReadService memberReadService;
 
-    @BeforeEach
-    public void init() {
-        objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
-        objectMapper.registerModule(new JavaTimeModule());
-        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
-    }
-
     @Test
     @DisplayName("요청을 받아 정상적으로 생성 컨트롤러가 동작한다.")
     void createTest() throws Exception {
@@ -121,6 +114,7 @@ void findMemberTest() throws Exception {
                 .andExpect(status().isOk())
                 .andDo(document("member/findById",
                         responseFields(
+                                fieldWithPath("memberId").description("회원 식별자"),
                                 fieldWithPath("email").description("이메일"),
                                 fieldWithPath("name").description("이름"),
                                 fieldWithPath("nickname").description("닉네임"),
@@ -222,6 +216,18 @@ void deleteTest() throws Exception {
                 .andDo(print());
     }
 
+    @Test
+    @DisplayName("회원 조회 시 200 ok 상태코드 반환")
+    void getMemberTest() {
+        // given
+        String nickname = GIVEN_NICKNAME.nickname();
+
+        // when
+
+        // then
+
+    }
+
     private ResultActions getCreate(String body) throws Exception {
         return mockMvc.perform(post("/api/v1/public/members").content(body)
                 .contentType(MediaType.APPLICATION_JSON));
diff --git a/src/test/java/me/golf/blog/domain/member/application/MemberServiceTest.java b/src/test/java/me/golf/blog/domain/member/application/MemberServiceTest.java
index 17dc589..1382d4d 100644
--- a/src/test/java/me/golf/blog/domain/member/application/MemberServiceTest.java
+++ b/src/test/java/me/golf/blog/domain/member/application/MemberServiceTest.java
@@ -155,4 +155,29 @@ void delete() {
 
         assertThat(member.getActivated()).isFalse();
     }
+
+    @Test
+    @DisplayName("닉네임으로 회원의 정보를 검색해볼 수 있다.")
+    void getByNickname() {
+        // given
+
+        // when
+        MemberResponse member = memberReadService.getDetailByNickname(GIVEN_NICKNAME.nickname());
+
+        // then
+        assertThat(member.getMemberId()).isEqualTo(memberId);
+    }
+
+    @Test
+    @DisplayName("없는 닉네임이면 조회할 수 없다.")
+    void getByNicknameFailByUnknownNickname() {
+        // given
+        String unknownNickname = "fdsajfdas";
+
+        // when
+        Throwable exception = catchThrowable(() -> memberReadService.getDetailByNickname(unknownNickname));
+
+        // then
+        assertThat(exception).isInstanceOf(MemberNotFoundException.class);
+    }
 }
\ No newline at end of file