diff --git a/src/main/java/com/sofa/linkiving/domain/member/controller/MemberApi.java b/src/main/java/com/sofa/linkiving/domain/member/controller/MemberApi.java index d5aae240..29a0371d 100644 --- a/src/main/java/com/sofa/linkiving/domain/member/controller/MemberApi.java +++ b/src/main/java/com/sofa/linkiving/domain/member/controller/MemberApi.java @@ -2,6 +2,7 @@ import com.sofa.linkiving.domain.member.dto.request.LoginReq; import com.sofa.linkiving.domain.member.dto.request.SignupReq; +import com.sofa.linkiving.domain.member.dto.response.MemberProfileRes; import com.sofa.linkiving.domain.member.dto.response.TokenRes; import com.sofa.linkiving.domain.member.entity.Member; import com.sofa.linkiving.global.common.BaseResponse; @@ -21,4 +22,7 @@ public interface MemberApi { @Operation(summary = "로그아웃", description = "리프레시 토큰을 무효화하고 로그아웃 처리합니다.") BaseResponse logout(Member member, HttpServletRequest request, HttpServletResponse response); + + @Operation(summary = "내 프로필 조회", description = "로그인한 사용자의 프로필 정보를 조회합니다.") + BaseResponse getProfile(Member member); } diff --git a/src/main/java/com/sofa/linkiving/domain/member/controller/MemberController.java b/src/main/java/com/sofa/linkiving/domain/member/controller/MemberController.java index 0cbe480e..a8048b2d 100644 --- a/src/main/java/com/sofa/linkiving/domain/member/controller/MemberController.java +++ b/src/main/java/com/sofa/linkiving/domain/member/controller/MemberController.java @@ -3,6 +3,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseCookie; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -10,6 +11,7 @@ import com.sofa.linkiving.domain.member.dto.request.LoginReq; import com.sofa.linkiving.domain.member.dto.request.SignupReq; +import com.sofa.linkiving.domain.member.dto.response.MemberProfileRes; import com.sofa.linkiving.domain.member.dto.response.TokenRes; import com.sofa.linkiving.domain.member.entity.Member; import com.sofa.linkiving.domain.member.service.MemberService; @@ -66,4 +68,11 @@ private void expireCookie(HttpServletRequest request, HttpServletResponse respon .build(); response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); } + + @Override + @GetMapping("/me") + public BaseResponse getProfile(@AuthMember Member member) { + MemberProfileRes profile = memberService.getProfile(member); + return BaseResponse.success(profile, "프로필 조회에 성공하였습니다."); + } } diff --git a/src/main/java/com/sofa/linkiving/domain/member/dto/response/MemberProfileRes.java b/src/main/java/com/sofa/linkiving/domain/member/dto/response/MemberProfileRes.java new file mode 100644 index 00000000..0c75ad00 --- /dev/null +++ b/src/main/java/com/sofa/linkiving/domain/member/dto/response/MemberProfileRes.java @@ -0,0 +1,26 @@ +package com.sofa.linkiving.domain.member.dto.response; + +import java.time.LocalDateTime; + +import com.sofa.linkiving.domain.member.entity.Member; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; + +@Builder +public record MemberProfileRes( + @Schema(description = "회원 ID", example = "1") + Long id, + @Schema(description = "이메일", example = "user@example.com") + String email, + @Schema(description = "가입일", example = "2026-03-01T12:34:56") + LocalDateTime createdAt +) { + public static MemberProfileRes from(Member member) { + return MemberProfileRes.builder() + .id(member.getId()) + .email(member.getEmail()) + .createdAt(member.getCreatedAt()) + .build(); + } +} diff --git a/src/main/java/com/sofa/linkiving/domain/member/service/MemberService.java b/src/main/java/com/sofa/linkiving/domain/member/service/MemberService.java index 31e29795..da9e8adc 100644 --- a/src/main/java/com/sofa/linkiving/domain/member/service/MemberService.java +++ b/src/main/java/com/sofa/linkiving/domain/member/service/MemberService.java @@ -8,6 +8,7 @@ import com.sofa.linkiving.domain.member.dto.request.LoginReq; import com.sofa.linkiving.domain.member.dto.request.SignupReq; +import com.sofa.linkiving.domain.member.dto.response.MemberProfileRes; import com.sofa.linkiving.domain.member.dto.response.TokenRes; import com.sofa.linkiving.domain.member.entity.Member; import com.sofa.linkiving.domain.member.error.MemberErrorCode; @@ -66,4 +67,9 @@ public TokenRes login(LoginReq req) { public void logout(Member member) { redisService.delete(RedisKeyRegistry.REFRESH_TOKEN, member.getEmail()); } + + @Transactional(readOnly = true) + public MemberProfileRes getProfile(Member member) { + return MemberProfileRes.from(member); + } } diff --git a/src/test/java/com/sofa/linkiving/domain/chat/service/MessageQueryServiceTest.java b/src/test/java/com/sofa/linkiving/domain/chat/service/MessageQueryServiceTest.java index 71dc8f1e..fddde972 100644 --- a/src/test/java/com/sofa/linkiving/domain/chat/service/MessageQueryServiceTest.java +++ b/src/test/java/com/sofa/linkiving/domain/chat/service/MessageQueryServiceTest.java @@ -38,7 +38,6 @@ public class MessageQueryServiceTest { @Mock private Chat chat; - @Test @DisplayName("요청 개수 초과 데이터가 존재 시 hasNext=true 반환 및 데이터를 잘라서 반환: (요청 개수 :10개 ,데이터 :11개)") void shouldReturnHasNextTrueWhenMoreDataExists() { diff --git a/src/test/java/com/sofa/linkiving/domain/member/service/MemberServiceTest.java b/src/test/java/com/sofa/linkiving/domain/member/service/MemberServiceTest.java index 7945edfc..dc49cbdb 100644 --- a/src/test/java/com/sofa/linkiving/domain/member/service/MemberServiceTest.java +++ b/src/test/java/com/sofa/linkiving/domain/member/service/MemberServiceTest.java @@ -1,11 +1,14 @@ package com.sofa.linkiving.domain.member.service; +import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.*; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.mockito.ArgumentMatchers.*; import static org.mockito.BDDMockito.*; import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; import java.util.Base64; import org.assertj.core.api.AssertionsForClassTypes; @@ -19,6 +22,7 @@ import com.sofa.linkiving.domain.member.dto.request.LoginReq; import com.sofa.linkiving.domain.member.dto.request.SignupReq; +import com.sofa.linkiving.domain.member.dto.response.MemberProfileRes; import com.sofa.linkiving.domain.member.dto.response.TokenRes; import com.sofa.linkiving.domain.member.entity.Member; import com.sofa.linkiving.domain.member.error.MemberErrorCode; @@ -159,4 +163,23 @@ void shouldDeleteRefreshTokenOnLogout() { // then verify(redisService, times(1)).delete(any(), eq(member.getEmail())); } + + @Test + @DisplayName("프로필 조회 시 회원 정보를 응답으로 변환") + void shouldReturnProfileFromMember() { + // given + Member member = mock(Member.class); + LocalDateTime createdAt = LocalDateTime.of(2026, 3, 1, 12, 34, 56); + given(member.getId()).willReturn(1L); + given(member.getEmail()).willReturn("user@example.com"); + given(member.getCreatedAt()).willReturn(createdAt); + + // when + MemberProfileRes res = memberService.getProfile(member); + + // then + assertThat(res.id()).isEqualTo(1L); + assertThat(res.email()).isEqualTo("user@example.com"); + assertThat(res.createdAt()).isEqualTo(createdAt); + } }