diff --git a/src/main/java/com/ajou/hertz/domain/user/controller/UserControllerV1.java b/src/main/java/com/ajou/hertz/domain/user/controller/UserControllerV1.java
index 5c7e14f..a5537df 100644
--- a/src/main/java/com/ajou/hertz/domain/user/controller/UserControllerV1.java
+++ b/src/main/java/com/ajou/hertz/domain/user/controller/UserControllerV1.java
@@ -5,6 +5,7 @@
import java.net.URI;
import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -13,12 +14,14 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
+import com.ajou.hertz.common.auth.UserPrincipal;
import com.ajou.hertz.common.validator.PhoneNumber;
import com.ajou.hertz.domain.user.dto.UserDto;
import com.ajou.hertz.domain.user.dto.request.SignUpRequest;
import com.ajou.hertz.domain.user.dto.response.UserEmailResponse;
import com.ajou.hertz.domain.user.dto.response.UserExistenceResponse;
import com.ajou.hertz.domain.user.dto.response.UserResponse;
+import com.ajou.hertz.domain.user.dto.response.UserWithLinkedAccountInfoResponse;
import com.ajou.hertz.domain.user.service.UserCommandService;
import com.ajou.hertz.domain.user.service.UserQueryService;
@@ -27,6 +30,7 @@
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
@@ -43,6 +47,19 @@ public class UserControllerV1 {
private final UserCommandService userCommandService;
private final UserQueryService userQueryService;
+ @Operation(
+ summary = "내 정보 조회",
+ description = "내 정보를 조회합니다.",
+ security = @SecurityRequirement(name = "access-token")
+ )
+ @GetMapping(value = "/me", headers = API_MINOR_VERSION_HEADER_NAME + "=" + 1)
+ public UserWithLinkedAccountInfoResponse getMyInfoV1_1(
+ @AuthenticationPrincipal UserPrincipal userPrincipal
+ ) {
+ UserDto userDto = userQueryService.getDtoById(userPrincipal.getUserId());
+ return UserWithLinkedAccountInfoResponse.from(userDto);
+ }
+
@Operation(
summary = "회원 존재 여부 조회",
description = "전달받은 값들에 일치하는 회원이 존재하는지 확인힙니다."
@@ -83,13 +100,10 @@ public UserEmailResponse getUserEmailByPhoneV1_1(
)
@ApiResponses({
@ApiResponse(responseCode = "200"),
- @ApiResponse(
- responseCode = "409", content = @Content,
- description = """
-
[2200] 이미 다른 사용자가 사용 중인 이메일로 신규 회원을 등록하려고 하는 경우.
-
[2203] 이미 다른 사용자가 사용 중인 전화번호로 신규 회원을 등록하려고 하는 경우.
- """
- )
+ @ApiResponse(responseCode = "409", content = @Content, description = """
+
[2200] 이미 다른 사용자가 사용 중인 이메일로 신규 회원을 등록하려고 하는 경우.
+
[2203] 이미 다른 사용자가 사용 중인 전화번호로 신규 회원을 등록하려고 하는 경우.
+ """)
})
@PostMapping(headers = API_MINOR_VERSION_HEADER_NAME + "=" + 1)
public ResponseEntity signUpV1_1(
diff --git a/src/main/java/com/ajou/hertz/domain/user/dto/UserDto.java b/src/main/java/com/ajou/hertz/domain/user/dto/UserDto.java
index 263799f..15fa641 100644
--- a/src/main/java/com/ajou/hertz/domain/user/dto/UserDto.java
+++ b/src/main/java/com/ajou/hertz/domain/user/dto/UserDto.java
@@ -1,6 +1,7 @@
package com.ajou.hertz.domain.user.dto;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.util.Set;
import com.ajou.hertz.domain.user.constant.Gender;
@@ -25,6 +26,7 @@ public class UserDto {
private Gender gender;
private String phone;
private String contactLink;
+ private LocalDateTime createdAt;
public static UserDto from(User user) {
return new UserDto(
@@ -37,7 +39,8 @@ public static UserDto from(User user) {
user.getBirth(),
user.getGender(),
user.getPhone(),
- user.getContactLink()
+ user.getContactLink(),
+ user.getCreatedAt()
);
}
}
diff --git a/src/main/java/com/ajou/hertz/domain/user/dto/response/UserResponse.java b/src/main/java/com/ajou/hertz/domain/user/dto/response/UserResponse.java
index 43ab75e..79b1a98 100644
--- a/src/main/java/com/ajou/hertz/domain/user/dto/response/UserResponse.java
+++ b/src/main/java/com/ajou/hertz/domain/user/dto/response/UserResponse.java
@@ -1,27 +1,43 @@
package com.ajou.hertz.domain.user.dto.response;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import com.ajou.hertz.domain.user.constant.Gender;
import com.ajou.hertz.domain.user.dto.UserDto;
+import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
-@AllArgsConstructor(access = AccessLevel.PRIVATE)
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class UserResponse {
+ @Schema(description = "Id of user", example = "1")
private Long id;
+
+ @Schema(description = "이메일", example = "example@mail.com")
private String email;
+
+ @Schema(description = "프로필 이미지 url", example = "https://user-profile-image")
private String profileImageUrl;
+
+ @Schema(description = "생년월일")
private LocalDate birth;
+
+ @Schema(description = "성별")
private Gender gender;
+
+ @Schema(description = "연락 수단")
private String contactLink;
+ @Schema(description = "계정 생성일자 (가입일)")
+ private LocalDateTime createdAt;
+
public static UserResponse from(UserDto userDto) {
return new UserResponse(
userDto.getId(),
@@ -29,7 +45,8 @@ public static UserResponse from(UserDto userDto) {
userDto.getProfileImageUrl(),
userDto.getBirth(),
userDto.getGender(),
- userDto.getContactLink()
+ userDto.getContactLink(),
+ userDto.getCreatedAt()
);
}
}
diff --git a/src/main/java/com/ajou/hertz/domain/user/dto/response/UserWithLinkedAccountInfoResponse.java b/src/main/java/com/ajou/hertz/domain/user/dto/response/UserWithLinkedAccountInfoResponse.java
new file mode 100644
index 0000000..e5d8f56
--- /dev/null
+++ b/src/main/java/com/ajou/hertz/domain/user/dto/response/UserWithLinkedAccountInfoResponse.java
@@ -0,0 +1,49 @@
+package com.ajou.hertz.domain.user.dto.response;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+import org.springframework.util.StringUtils;
+
+import com.ajou.hertz.domain.user.constant.Gender;
+import com.ajou.hertz.domain.user.dto.UserDto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@Getter
+public class UserWithLinkedAccountInfoResponse extends UserResponse {
+
+ @Schema(description = "카카오 계정 연동 여부", example = "true")
+ private Boolean isKakaoAccountLinked;
+
+ private UserWithLinkedAccountInfoResponse(
+ Long id,
+ String email,
+ String profileImageUrl,
+ LocalDate birth,
+ Gender gender,
+ String contactLink,
+ LocalDateTime createdAt,
+ Boolean isKakaoAccountLinked
+ ) {
+ super(id, email, profileImageUrl, birth, gender, contactLink, createdAt);
+ this.isKakaoAccountLinked = isKakaoAccountLinked;
+ }
+
+ public static UserWithLinkedAccountInfoResponse from(UserDto userDto) {
+ return new UserWithLinkedAccountInfoResponse(
+ userDto.getId(),
+ userDto.getEmail(),
+ userDto.getProfileImageUrl(),
+ userDto.getBirth(),
+ userDto.getGender(),
+ userDto.getContactLink(),
+ userDto.getCreatedAt(),
+ StringUtils.hasText(userDto.getKakaoUid())
+ );
+ }
+}
diff --git a/src/test/java/com/ajou/hertz/config/TestSecurityConfig.java b/src/test/java/com/ajou/hertz/config/TestSecurityConfig.java
index c44d26d..e9acd79 100644
--- a/src/test/java/com/ajou/hertz/config/TestSecurityConfig.java
+++ b/src/test/java/com/ajou/hertz/config/TestSecurityConfig.java
@@ -4,6 +4,7 @@
import java.lang.reflect.Constructor;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.util.Set;
import org.springframework.boot.test.context.TestConfiguration;
@@ -46,7 +47,8 @@ public void securitySetUp() throws Exception {
private UserDto createUserDto() throws Exception {
Constructor userResponseConstructor = UserDto.class.getDeclaredConstructor(
Long.class, Set.class, String.class, String.class, String.class,
- String.class, LocalDate.class, Gender.class, String.class, String.class
+ String.class, LocalDate.class, Gender.class, String.class, String.class,
+ LocalDateTime.class
);
userResponseConstructor.setAccessible(true);
return userResponseConstructor.newInstance(
@@ -59,7 +61,8 @@ private UserDto createUserDto() throws Exception {
LocalDate.of(2024, 1, 1),
Gender.ETC,
"01012345678",
- "https://contack-link"
+ "https://contack-link",
+ LocalDateTime.of(2024, 1, 1, 0, 0)
);
}
}
diff --git a/src/test/java/com/ajou/hertz/unit/common/auth/service/AuthServiceTest.java b/src/test/java/com/ajou/hertz/unit/common/auth/service/AuthServiceTest.java
index dec0b03..8e82afd 100644
--- a/src/test/java/com/ajou/hertz/unit/common/auth/service/AuthServiceTest.java
+++ b/src/test/java/com/ajou/hertz/unit/common/auth/service/AuthServiceTest.java
@@ -109,7 +109,8 @@ private JwtTokenInfoDto createJwtTokenInfoDto() {
private UserDto createUserDto(long id) throws Exception {
Constructor userResponseConstructor = UserDto.class.getDeclaredConstructor(
Long.class, Set.class, String.class, String.class, String.class,
- String.class, LocalDate.class, Gender.class, String.class, String.class
+ String.class, LocalDate.class, Gender.class, String.class, String.class,
+ LocalDateTime.class
);
userResponseConstructor.setAccessible(true);
return userResponseConstructor.newInstance(
@@ -122,7 +123,8 @@ private UserDto createUserDto(long id) throws Exception {
LocalDate.of(2024, 1, 1),
Gender.ETC,
"01012345678",
- "https://contack-link"
+ "https://contack-link",
+ LocalDateTime.of(2024, 1, 1, 0, 0)
);
}
diff --git a/src/test/java/com/ajou/hertz/unit/common/kakao/service/KakaoServiceTest.java b/src/test/java/com/ajou/hertz/unit/common/kakao/service/KakaoServiceTest.java
index be7c0f1..33c9d92 100644
--- a/src/test/java/com/ajou/hertz/unit/common/kakao/service/KakaoServiceTest.java
+++ b/src/test/java/com/ajou/hertz/unit/common/kakao/service/KakaoServiceTest.java
@@ -140,7 +140,8 @@ private void verifyEveryMocksShouldHaveNoMoreInteractions() {
private UserDto createUserDto(long id) throws Exception {
Constructor userResponseConstructor = UserDto.class.getDeclaredConstructor(
Long.class, Set.class, String.class, String.class, String.class,
- String.class, LocalDate.class, Gender.class, String.class, String.class
+ String.class, LocalDate.class, Gender.class, String.class, String.class,
+ LocalDateTime.class
);
userResponseConstructor.setAccessible(true);
return userResponseConstructor.newInstance(
@@ -153,7 +154,8 @@ private UserDto createUserDto(long id) throws Exception {
LocalDate.of(2024, 1, 1),
Gender.ETC,
"01012345678",
- "https://contack-link"
+ "https://contack-link",
+ LocalDateTime.of(2024, 1, 1, 0, 0)
);
}
diff --git a/src/test/java/com/ajou/hertz/unit/domain/user/controller/UserControllerV1Test.java b/src/test/java/com/ajou/hertz/unit/domain/user/controller/UserControllerV1Test.java
index 7517076..f6fca33 100644
--- a/src/test/java/com/ajou/hertz/unit/domain/user/controller/UserControllerV1Test.java
+++ b/src/test/java/com/ajou/hertz/unit/domain/user/controller/UserControllerV1Test.java
@@ -2,11 +2,13 @@
import static com.ajou.hertz.common.constant.GlobalConstants.*;
import static org.mockito.BDDMockito.*;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.lang.reflect.Constructor;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.util.Set;
import org.junit.jupiter.api.DisplayName;
@@ -17,6 +19,7 @@
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
import org.springframework.http.MediaType;
+import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.context.event.annotation.BeforeTestMethod;
import org.springframework.test.web.servlet.MockMvc;
@@ -26,6 +29,7 @@
import com.ajou.hertz.common.auth.JwtAuthenticationFilter;
import com.ajou.hertz.common.auth.JwtExceptionFilter;
import com.ajou.hertz.common.auth.JwtTokenProvider;
+import com.ajou.hertz.common.auth.UserPrincipal;
import com.ajou.hertz.common.config.SecurityConfig;
import com.ajou.hertz.domain.user.constant.Gender;
import com.ajou.hertz.domain.user.constant.RoleType;
@@ -71,6 +75,25 @@ public void securitySetUp() throws Exception {
given(userQueryService.getDtoById(anyLong())).willReturn(createUserDto());
}
+ @Test
+ void 내_정보를_조회한다() throws Exception {
+ // given
+ long userId = 1L;
+ UserDto expectedResult = createUserDto(userId);
+ given(userQueryService.getDtoById(userId)).willReturn(expectedResult);
+
+ // when & then
+ mvc.perform(
+ get("/v1/users/me")
+ .header(API_MINOR_VERSION_HEADER_NAME, 1)
+ .with(user(createTestUser(userId)))
+ )
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(expectedResult.getId()));
+ then(userQueryService).should().getDtoById(userId);
+ verifyEveryMocksShouldHaveNoMoreInteractions();
+ }
+
@Test
void 이메일이_주어지고_주어진_이메일을_사용_중인_회원의_존재_여부를_조회한다() throws Exception {
// given
@@ -219,7 +242,8 @@ private SignUpRequest createSignUpRequest() throws Exception {
private UserDto createUserDto(long id) throws Exception {
Constructor userResponseConstructor = UserDto.class.getDeclaredConstructor(
Long.class, Set.class, String.class, String.class, String.class,
- String.class, LocalDate.class, Gender.class, String.class, String.class
+ String.class, LocalDate.class, Gender.class, String.class, String.class,
+ LocalDateTime.class
);
userResponseConstructor.setAccessible(true);
return userResponseConstructor.newInstance(
@@ -232,11 +256,16 @@ private UserDto createUserDto(long id) throws Exception {
LocalDate.of(2024, 1, 1),
Gender.ETC,
"01012345678",
- "https://contack-link"
+ "https://contack-link",
+ LocalDateTime.of(2024, 1, 1, 0, 0)
);
}
private UserDto createUserDto() throws Exception {
return createUserDto(1L);
}
+
+ private UserDetails createTestUser(Long userId) throws Exception {
+ return new UserPrincipal(createUserDto(userId));
+ }
}
\ No newline at end of file