Skip to content

Commit d64e041

Browse files
committed
feat: #44 내 정보 조회 API 구현
1 parent 73d4d3a commit d64e041

File tree

8 files changed

+138
-19
lines changed

8 files changed

+138
-19
lines changed

src/main/java/com/ajou/hertz/domain/user/controller/UserControllerV1.java

+21-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.net.URI;
66

77
import org.springframework.http.ResponseEntity;
8+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
89
import org.springframework.validation.annotation.Validated;
910
import org.springframework.web.bind.annotation.GetMapping;
1011
import org.springframework.web.bind.annotation.PostMapping;
@@ -13,12 +14,14 @@
1314
import org.springframework.web.bind.annotation.RequestParam;
1415
import org.springframework.web.bind.annotation.RestController;
1516

17+
import com.ajou.hertz.common.auth.UserPrincipal;
1618
import com.ajou.hertz.common.validator.PhoneNumber;
1719
import com.ajou.hertz.domain.user.dto.UserDto;
1820
import com.ajou.hertz.domain.user.dto.request.SignUpRequest;
1921
import com.ajou.hertz.domain.user.dto.response.UserEmailResponse;
2022
import com.ajou.hertz.domain.user.dto.response.UserExistenceResponse;
2123
import com.ajou.hertz.domain.user.dto.response.UserResponse;
24+
import com.ajou.hertz.domain.user.dto.response.UserWithLinkedAccountInfoResponse;
2225
import com.ajou.hertz.domain.user.service.UserCommandService;
2326
import com.ajou.hertz.domain.user.service.UserQueryService;
2427

@@ -27,6 +30,7 @@
2730
import io.swagger.v3.oas.annotations.media.Content;
2831
import io.swagger.v3.oas.annotations.responses.ApiResponse;
2932
import io.swagger.v3.oas.annotations.responses.ApiResponses;
33+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
3034
import io.swagger.v3.oas.annotations.tags.Tag;
3135
import jakarta.validation.Valid;
3236
import jakarta.validation.constraints.Email;
@@ -43,6 +47,19 @@ public class UserControllerV1 {
4347
private final UserCommandService userCommandService;
4448
private final UserQueryService userQueryService;
4549

50+
@Operation(
51+
summary = "내 정보 조회",
52+
description = "내 정보를 조회합니다.",
53+
security = @SecurityRequirement(name = "access-token")
54+
)
55+
@GetMapping(value = "/me", headers = API_MINOR_VERSION_HEADER_NAME + "=" + 1)
56+
public UserWithLinkedAccountInfoResponse getMyInfoV1_1(
57+
@AuthenticationPrincipal UserPrincipal userPrincipal
58+
) {
59+
UserDto userDto = userQueryService.getDtoById(userPrincipal.getUserId());
60+
return UserWithLinkedAccountInfoResponse.from(userDto);
61+
}
62+
4663
@Operation(
4764
summary = "회원 존재 여부 조회",
4865
description = "전달받은 값들에 일치하는 회원이 존재하는지 확인힙니다."
@@ -83,13 +100,10 @@ public UserEmailResponse getUserEmailByPhoneV1_1(
83100
)
84101
@ApiResponses({
85102
@ApiResponse(responseCode = "200"),
86-
@ApiResponse(
87-
responseCode = "409", content = @Content,
88-
description = """
89-
<p>[2200] 이미 다른 사용자가 사용 중인 이메일로 신규 회원을 등록하려고 하는 경우.
90-
<p>[2203] 이미 다른 사용자가 사용 중인 전화번호로 신규 회원을 등록하려고 하는 경우.
91-
"""
92-
)
103+
@ApiResponse(responseCode = "409", content = @Content, description = """
104+
<p>[2200] 이미 다른 사용자가 사용 중인 이메일로 신규 회원을 등록하려고 하는 경우.
105+
<p>[2203] 이미 다른 사용자가 사용 중인 전화번호로 신규 회원을 등록하려고 하는 경우.
106+
""")
93107
})
94108
@PostMapping(headers = API_MINOR_VERSION_HEADER_NAME + "=" + 1)
95109
public ResponseEntity<UserResponse> signUpV1_1(

src/main/java/com/ajou/hertz/domain/user/dto/UserDto.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.ajou.hertz.domain.user.dto;
22

33
import java.time.LocalDate;
4+
import java.time.LocalDateTime;
45
import java.util.Set;
56

67
import com.ajou.hertz.domain.user.constant.Gender;
@@ -25,6 +26,7 @@ public class UserDto {
2526
private Gender gender;
2627
private String phone;
2728
private String contactLink;
29+
private LocalDateTime createdAt;
2830

2931
public static UserDto from(User user) {
3032
return new UserDto(
@@ -37,7 +39,8 @@ public static UserDto from(User user) {
3739
user.getBirth(),
3840
user.getGender(),
3941
user.getPhone(),
40-
user.getContactLink()
42+
user.getContactLink(),
43+
user.getCreatedAt()
4144
);
4245
}
4346
}
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,52 @@
11
package com.ajou.hertz.domain.user.dto.response;
22

33
import java.time.LocalDate;
4+
import java.time.LocalDateTime;
45

56
import com.ajou.hertz.domain.user.constant.Gender;
67
import com.ajou.hertz.domain.user.dto.UserDto;
78

9+
import io.swagger.v3.oas.annotations.media.Schema;
810
import lombok.AccessLevel;
911
import lombok.AllArgsConstructor;
1012
import lombok.Getter;
1113
import lombok.NoArgsConstructor;
1214

13-
@AllArgsConstructor(access = AccessLevel.PRIVATE)
14-
@NoArgsConstructor(access = AccessLevel.PRIVATE)
15+
@AllArgsConstructor(access = AccessLevel.PROTECTED)
16+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
1517
@Getter
1618
public class UserResponse {
1719

20+
@Schema(description = "Id of user", example = "1")
1821
private Long id;
22+
23+
@Schema(description = "이메일", example = "[email protected]")
1924
private String email;
25+
26+
@Schema(description = "프로필 이미지 url", example = "https://user-profile-image")
2027
private String profileImageUrl;
28+
29+
@Schema(description = "생년월일")
2130
private LocalDate birth;
31+
32+
@Schema(description = "성별")
2233
private Gender gender;
34+
35+
@Schema(description = "연락 수단")
2336
private String contactLink;
2437

38+
@Schema(description = "계정 생성일자 (가입일)")
39+
private LocalDateTime createdAt;
40+
2541
public static UserResponse from(UserDto userDto) {
2642
return new UserResponse(
2743
userDto.getId(),
2844
userDto.getEmail(),
2945
userDto.getProfileImageUrl(),
3046
userDto.getBirth(),
3147
userDto.getGender(),
32-
userDto.getContactLink()
48+
userDto.getContactLink(),
49+
userDto.getCreatedAt()
3350
);
3451
}
3552
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.ajou.hertz.domain.user.dto.response;
2+
3+
import java.time.LocalDate;
4+
import java.time.LocalDateTime;
5+
6+
import org.springframework.util.StringUtils;
7+
8+
import com.ajou.hertz.domain.user.constant.Gender;
9+
import com.ajou.hertz.domain.user.dto.UserDto;
10+
11+
import io.swagger.v3.oas.annotations.media.Schema;
12+
import lombok.AccessLevel;
13+
import lombok.Getter;
14+
import lombok.NoArgsConstructor;
15+
16+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
17+
@Getter
18+
public class UserWithLinkedAccountInfoResponse extends UserResponse {
19+
20+
@Schema(description = "카카오 계정 연동 여부", example = "true")
21+
private Boolean isKakaoAccountLinked;
22+
23+
private UserWithLinkedAccountInfoResponse(
24+
Long id,
25+
String email,
26+
String profileImageUrl,
27+
LocalDate birth,
28+
Gender gender,
29+
String contactLink,
30+
LocalDateTime createdAt,
31+
Boolean isKakaoAccountLinked
32+
) {
33+
super(id, email, profileImageUrl, birth, gender, contactLink, createdAt);
34+
this.isKakaoAccountLinked = isKakaoAccountLinked;
35+
}
36+
37+
public static UserWithLinkedAccountInfoResponse from(UserDto userDto) {
38+
return new UserWithLinkedAccountInfoResponse(
39+
userDto.getId(),
40+
userDto.getEmail(),
41+
userDto.getProfileImageUrl(),
42+
userDto.getBirth(),
43+
userDto.getGender(),
44+
userDto.getContactLink(),
45+
userDto.getCreatedAt(),
46+
StringUtils.hasText(userDto.getKakaoUid())
47+
);
48+
}
49+
}

src/test/java/com/ajou/hertz/config/TestSecurityConfig.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.lang.reflect.Constructor;
66
import java.time.LocalDate;
7+
import java.time.LocalDateTime;
78
import java.util.Set;
89

910
import org.springframework.boot.test.context.TestConfiguration;
@@ -46,7 +47,8 @@ public void securitySetUp() throws Exception {
4647
private UserDto createUserDto() throws Exception {
4748
Constructor<UserDto> userResponseConstructor = UserDto.class.getDeclaredConstructor(
4849
Long.class, Set.class, String.class, String.class, String.class,
49-
String.class, LocalDate.class, Gender.class, String.class, String.class
50+
String.class, LocalDate.class, Gender.class, String.class, String.class,
51+
LocalDateTime.class
5052
);
5153
userResponseConstructor.setAccessible(true);
5254
return userResponseConstructor.newInstance(
@@ -59,7 +61,8 @@ private UserDto createUserDto() throws Exception {
5961
LocalDate.of(2024, 1, 1),
6062
Gender.ETC,
6163
"01012345678",
62-
"https://contack-link"
64+
"https://contack-link",
65+
LocalDateTime.of(2024, 1, 1, 0, 0)
6366
);
6467
}
6568
}

src/test/java/com/ajou/hertz/unit/common/auth/service/AuthServiceTest.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ private JwtTokenInfoDto createJwtTokenInfoDto() {
109109
private UserDto createUserDto(long id) throws Exception {
110110
Constructor<UserDto> userResponseConstructor = UserDto.class.getDeclaredConstructor(
111111
Long.class, Set.class, String.class, String.class, String.class,
112-
String.class, LocalDate.class, Gender.class, String.class, String.class
112+
String.class, LocalDate.class, Gender.class, String.class, String.class,
113+
LocalDateTime.class
113114
);
114115
userResponseConstructor.setAccessible(true);
115116
return userResponseConstructor.newInstance(
@@ -122,7 +123,8 @@ private UserDto createUserDto(long id) throws Exception {
122123
LocalDate.of(2024, 1, 1),
123124
Gender.ETC,
124125
"01012345678",
125-
"https://contack-link"
126+
"https://contack-link",
127+
LocalDateTime.of(2024, 1, 1, 0, 0)
126128
);
127129
}
128130

src/test/java/com/ajou/hertz/unit/common/kakao/service/KakaoServiceTest.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ private void verifyEveryMocksShouldHaveNoMoreInteractions() {
140140
private UserDto createUserDto(long id) throws Exception {
141141
Constructor<UserDto> userResponseConstructor = UserDto.class.getDeclaredConstructor(
142142
Long.class, Set.class, String.class, String.class, String.class,
143-
String.class, LocalDate.class, Gender.class, String.class, String.class
143+
String.class, LocalDate.class, Gender.class, String.class, String.class,
144+
LocalDateTime.class
144145
);
145146
userResponseConstructor.setAccessible(true);
146147
return userResponseConstructor.newInstance(
@@ -153,7 +154,8 @@ private UserDto createUserDto(long id) throws Exception {
153154
LocalDate.of(2024, 1, 1),
154155
Gender.ETC,
155156
"01012345678",
156-
"https://contack-link"
157+
"https://contack-link",
158+
LocalDateTime.of(2024, 1, 1, 0, 0)
157159
);
158160
}
159161

src/test/java/com/ajou/hertz/unit/domain/user/controller/UserControllerV1Test.java

+31-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
import static com.ajou.hertz.common.constant.GlobalConstants.*;
44
import static org.mockito.BDDMockito.*;
5+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
56
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
67
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
78

89
import java.lang.reflect.Constructor;
910
import java.time.LocalDate;
11+
import java.time.LocalDateTime;
1012
import java.util.Set;
1113

1214
import org.junit.jupiter.api.DisplayName;
@@ -17,6 +19,7 @@
1719
import org.springframework.context.annotation.Import;
1820
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
1921
import org.springframework.http.MediaType;
22+
import org.springframework.security.core.userdetails.UserDetails;
2023
import org.springframework.test.context.event.annotation.BeforeTestMethod;
2124
import org.springframework.test.web.servlet.MockMvc;
2225

@@ -26,6 +29,7 @@
2629
import com.ajou.hertz.common.auth.JwtAuthenticationFilter;
2730
import com.ajou.hertz.common.auth.JwtExceptionFilter;
2831
import com.ajou.hertz.common.auth.JwtTokenProvider;
32+
import com.ajou.hertz.common.auth.UserPrincipal;
2933
import com.ajou.hertz.common.config.SecurityConfig;
3034
import com.ajou.hertz.domain.user.constant.Gender;
3135
import com.ajou.hertz.domain.user.constant.RoleType;
@@ -71,6 +75,25 @@ public void securitySetUp() throws Exception {
7175
given(userQueryService.getDtoById(anyLong())).willReturn(createUserDto());
7276
}
7377

78+
@Test
79+
void 내_정보를_조회한다() throws Exception {
80+
// given
81+
long userId = 1L;
82+
UserDto expectedResult = createUserDto(userId);
83+
given(userQueryService.getDtoById(userId)).willReturn(expectedResult);
84+
85+
// when & then
86+
mvc.perform(
87+
get("/v1/users/me")
88+
.header(API_MINOR_VERSION_HEADER_NAME, 1)
89+
.with(user(createTestUser(userId)))
90+
)
91+
.andExpect(status().isOk())
92+
.andExpect(jsonPath("$.id").value(expectedResult.getId()));
93+
then(userQueryService).should().getDtoById(userId);
94+
verifyEveryMocksShouldHaveNoMoreInteractions();
95+
}
96+
7497
@Test
7598
void 이메일이_주어지고_주어진_이메일을_사용_중인_회원의_존재_여부를_조회한다() throws Exception {
7699
// given
@@ -219,7 +242,8 @@ private SignUpRequest createSignUpRequest() throws Exception {
219242
private UserDto createUserDto(long id) throws Exception {
220243
Constructor<UserDto> userResponseConstructor = UserDto.class.getDeclaredConstructor(
221244
Long.class, Set.class, String.class, String.class, String.class,
222-
String.class, LocalDate.class, Gender.class, String.class, String.class
245+
String.class, LocalDate.class, Gender.class, String.class, String.class,
246+
LocalDateTime.class
223247
);
224248
userResponseConstructor.setAccessible(true);
225249
return userResponseConstructor.newInstance(
@@ -232,11 +256,16 @@ private UserDto createUserDto(long id) throws Exception {
232256
LocalDate.of(2024, 1, 1),
233257
Gender.ETC,
234258
"01012345678",
235-
"https://contack-link"
259+
"https://contack-link",
260+
LocalDateTime.of(2024, 1, 1, 0, 0)
236261
);
237262
}
238263

239264
private UserDto createUserDto() throws Exception {
240265
return createUserDto(1L);
241266
}
267+
268+
private UserDetails createTestUser(Long userId) throws Exception {
269+
return new UserPrincipal(createUserDto(userId));
270+
}
242271
}

0 commit comments

Comments
 (0)