Skip to content

Commit 4845b6b

Browse files
authored
feat: 사용자 프로필 조회 및 수정 기능 구현
feat: 사용자 프로필 조회 및 수정 기능 구현
2 parents 7fedf94 + 2a039ab commit 4845b6b

30 files changed

Lines changed: 648 additions & 68 deletions

backend/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ out/
3535

3636
### VS Code ###
3737
.vscode/
38+
39+
### SpringBoot ###
40+
*.yml

backend/src/main/java/com/example/ecommercewebservice/domain/user/entity/UserRole.java renamed to backend/src/main/java/com/example/ecommercewebservice/config/UserRole.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.example.ecommercewebservice.domain.user.entity;
1+
package com.example.ecommercewebservice.config;
22

33
import lombok.Getter;
44

backend/src/main/java/com/example/ecommercewebservice/domain/redis/RedisCommon.java renamed to backend/src/main/java/com/example/ecommercewebservice/config/redis/RedisCommon.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.example.ecommercewebservice.domain.redis;
1+
package com.example.ecommercewebservice.config.redis;
22

33
import com.google.gson.Gson;
44
import lombok.RequiredArgsConstructor;

backend/src/main/java/com/example/ecommercewebservice/domain/admin/controller/AdminController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.example.ecommercewebservice.domain.user.dto.UserRoleUpdateRequest;
44
import com.example.ecommercewebservice.domain.user.service.UserService;
55
import com.example.ecommercewebservice.global.security.annotation.RoleRequired;
6-
import com.example.ecommercewebservice.domain.user.entity.UserRole;
6+
import com.example.ecommercewebservice.config.UserRole;
77
import lombok.RequiredArgsConstructor;
88
import org.springframework.http.ResponseEntity;
99
import org.springframework.web.bind.annotation.*;

backend/src/main/java/com/example/ecommercewebservice/domain/user/controller/UserController.java

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
package com.example.ecommercewebservice.domain.user.controller;
22

3-
import com.example.ecommercewebservice.domain.user.dto.LoginRequest;
4-
import com.example.ecommercewebservice.domain.user.dto.LoginResponse;
5-
import com.example.ecommercewebservice.domain.user.dto.SignupRequest;
6-
import com.example.ecommercewebservice.domain.user.dto.SignupResponse;
3+
import com.example.ecommercewebservice.config.UserRole;
4+
import com.example.ecommercewebservice.domain.user.dto.response.UserProfileResponse;
5+
import com.example.ecommercewebservice.domain.user.dto.request.UserUpdateRequestDto;
6+
import com.example.ecommercewebservice.domain.user.dto.response.UserResponseDto;
7+
import com.example.ecommercewebservice.domain.user.dto.signIn.LoginRequest;
8+
import com.example.ecommercewebservice.domain.user.dto.signIn.LoginResponse;
9+
import com.example.ecommercewebservice.domain.user.dto.signUp.SignupRequest;
10+
import com.example.ecommercewebservice.domain.user.dto.signUp.SignupResponse;
711
import com.example.ecommercewebservice.domain.user.entity.User;
812
import com.example.ecommercewebservice.domain.user.service.UserService;
913
import com.example.ecommercewebservice.global.constant.MessageConstants;
1014
import com.example.ecommercewebservice.global.dto.RsData;
15+
import com.example.ecommercewebservice.global.security.annotation.RoleRequired;
1116
import jakarta.validation.Valid;
1217
import lombok.RequiredArgsConstructor;
1318
import org.springframework.http.HttpStatus;
1419
import org.springframework.http.ResponseEntity;
15-
import org.springframework.web.bind.annotation.PostMapping;
16-
import org.springframework.web.bind.annotation.RequestBody;
17-
import org.springframework.web.bind.annotation.RequestMapping;
18-
import org.springframework.web.bind.annotation.RequestHeader;
19-
import org.springframework.web.bind.annotation.RestController;
20+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
21+
import org.springframework.web.bind.annotation.*;
2022

2123
import java.util.Map;
2224

@@ -44,7 +46,7 @@ public ResponseEntity<RsData<SignupResponse>> signup(@Valid @RequestBody SignupR
4446
public ResponseEntity<RsData<LoginResponse>> login(@Valid @RequestBody LoginRequest loginRequest) {
4547
// 로그인 로직 구현 - 예외는 GlobalExceptionHandler에서 처리
4648
LoginResponse loginResponse = userService.login(loginRequest);
47-
49+
4850
// 성공 시 - 로그인 응답 반환
4951
RsData<LoginResponse> rsData = new RsData<>(String.valueOf(HttpStatus.OK.value()), MessageConstants.LOGIN_SUCCESS, loginResponse);
5052
return ResponseEntity.ok(rsData);
@@ -60,4 +62,32 @@ public ResponseEntity<Map<String, String>> logout(@RequestHeader("Authorization"
6062
}
6163
return ResponseEntity.badRequest().body(Map.of("error", "유효하지 않은 인증 정보입니다."));
6264
}
65+
66+
// 사용자 프로필(정보) 조회
67+
@GetMapping("/me")
68+
@RoleRequired(UserRole.USER) // USER 권한이 있는 사용자만 접근 가능
69+
public ResponseEntity<RsData<UserProfileResponse>> getMyProfile(@AuthenticationPrincipal User user) { // @AuthenticationPrincipal 어노테이션을 사용하여 현재 로그인한 사용자 정보를 가져와서 User에 주입, 메서드 파라미터에만 사용할 수 있다.
70+
UserProfileResponse profile = userService.getMyProfile(user);
71+
RsData<UserProfileResponse> rsData = new RsData<>(
72+
String.valueOf(HttpStatus.OK.value()),
73+
"프로필 조회 성공",
74+
profile
75+
);
76+
return ResponseEntity.ok(rsData);
77+
}
78+
79+
// 사용자 프로필(정보) 수정
80+
@PutMapping("/me")
81+
@RoleRequired(UserRole.USER)
82+
public ResponseEntity<RsData<UserResponseDto>> updateProfile(
83+
@AuthenticationPrincipal User user,
84+
@Valid @RequestBody UserUpdateRequestDto request) {
85+
UserResponseDto response = userService.updateProfile(user.getUserId(), request);
86+
RsData<UserResponseDto> rsData = new RsData<>(
87+
String.valueOf(HttpStatus.OK.value()),
88+
"프로필 수정 성공",
89+
response
90+
);
91+
return ResponseEntity.ok(rsData);
92+
}
6393
}
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
package com.example.ecommercewebservice.domain.user.dto;
22

3-
import com.example.ecommercewebservice.domain.user.entity.UserRole;
3+
import com.example.ecommercewebservice.config.UserRole;
44
import lombok.Getter;
55
import lombok.NoArgsConstructor;
66

7+
/**
8+
* 사용자 권한 업데이트 요청 DTO
9+
*
10+
* 사용 위치:
11+
* - AdminController - 관리자가 사용자 권한을 변경할 때 사용
12+
* - UserService.updateUserRole() - 사용자 권한 변경 비즈니스 로직에서 사용
13+
*
14+
* 용도:
15+
* - 관리자가 다른 사용자의 역할/권한을 변경하기 위한 요청 데이터를 담는 객체
16+
* - 사용자 권한 관리를 위한 관리자 전용 기능에서 사용
17+
*/
718
@Getter
819
@NoArgsConstructor
920
public class UserRoleUpdateRequest {
21+
/**
22+
* 변경할 사용자 역할 (ROLE_USER, ROLE_ADMIN 등)
23+
*/
1024
private UserRole role;
1125
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.example.ecommercewebservice.domain.user.dto.request;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Pattern;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
/**
9+
* 사용자 주소 수정 요청을 위한 DTO
10+
*
11+
* 사용 위치:
12+
* - UserUpdateRequestDto.addresses - 사용자 프로필 수정 시 주소 정보를 담는 객체
13+
* - UserService.updateAddresses() - 주소 정보 업데이트 로직에서 사용
14+
*
15+
* 용도:
16+
* - 사용자의 주소 정보를 추가, 수정, 삭제하기 위한 요청 데이터를 담는 객체
17+
* - addressId가 null이면 새 주소 추가, 있으면 기존 주소 수정
18+
* - 기본 주소는 반드시 하나만 존재해야 함
19+
*/
20+
@Getter
21+
@NoArgsConstructor
22+
public class AddressUpdateRequestDto {
23+
private Long addressId; // null이면 새 주소 추가, 있으면 수정 또는 삭제
24+
25+
@NotBlank(message = "수취인 이름은 필수입니다.")
26+
private String recipient;
27+
28+
@NotBlank(message = "우편번호는 필수입니다.")
29+
@Pattern(regexp = "^\\d{5}$", message = "우편번호는 5자리 숫자여야 합니다.")
30+
private String postalCode;
31+
32+
@NotBlank(message = "주소는 필수입니다.")
33+
private String address;
34+
35+
@NotBlank(message = "전화번호는 필수입니다.")
36+
@Pattern(regexp = "^01(?:0|1|[6-9])-(?:\\d{3}|\\d{4})-\\d{4}$",
37+
message = "올바른 전화번호 형식이 아닙니다. (예: 010-1234-5678)")
38+
private String phone;
39+
40+
private boolean isDefault;
41+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.example.ecommercewebservice.domain.user.dto.request;
2+
3+
import jakarta.validation.constraints.Pattern;
4+
import lombok.Getter;
5+
import lombok.NoArgsConstructor;
6+
7+
import java.util.List;
8+
9+
/**
10+
* 사용자 프로필 수정 요청을 위한 DTO
11+
*
12+
* 사용 위치:
13+
* - UserController.updateProfile() - PUT /api/users/me 엔드포인트에서 사용
14+
* - UserService.updateProfile() - 프로필 수정 비즈니스 로직에서 사용
15+
*
16+
* 용도:
17+
* - 사용자의 비밀번호, 이름, 전화번호, 주소 정보를 수정하기 위한 요청 데이터를 담는 객체
18+
* - 모든 필드는 선택적으로 수정 가능
19+
* - 주소 정보는 추가, 수정, 삭제가 가능
20+
*/
21+
@Getter
22+
@NoArgsConstructor
23+
public class UserUpdateRequestDto {
24+
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$",
25+
message = "비밀번호는 최소 8자 이상이며, 영문자, 숫자, 특수문자를 포함해야 합니다.")
26+
private String password;
27+
28+
private String username;
29+
30+
@Pattern(regexp = "^01(?:0|1|[6-9])-(?:\\d{3}|\\d{4})-\\d{4}$",
31+
message = "올바른 전화번호 형식이 아닙니다. (예: 010-1234-5678)")
32+
private String phoneNumber;
33+
34+
private List<AddressUpdateRequestDto> addresses;
35+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.example.ecommercewebservice.domain.user.dto.response;
2+
3+
import com.example.ecommercewebservice.domain.user.entity.Address;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
import java.time.LocalDateTime;
10+
11+
/**
12+
* 주소 정보 응답 DTO
13+
*
14+
* 사용 위치:
15+
* - AddressController - 주소 관련 API 응답으로 사용
16+
* - UserService.getAddresses() - 사용자의 주소 목록 조회 시 사용
17+
*
18+
* 용도:
19+
* - 사용자의 주소 정보를 클라이언트에 반환하기 위한 객체
20+
* - 주소 정보만을 독립적으로 반환할 때 사용
21+
* - UserResponseDto.AddressResponseDto와 유사하지만 독립적인 응답으로 사용
22+
*/
23+
@Getter
24+
@NoArgsConstructor
25+
@AllArgsConstructor
26+
@Builder
27+
public class AddressResponse {
28+
/**
29+
* 주소 식별자
30+
*/
31+
private Long addressId;
32+
33+
/**
34+
* 수령인 이름
35+
*/
36+
private String recipient;
37+
38+
/**
39+
* 우편번호
40+
*/
41+
private String postalCode;
42+
43+
/**
44+
* 상세 주소
45+
*/
46+
private String address;
47+
48+
/**
49+
* 배송지 연락처
50+
*/
51+
private String phoneNumber;
52+
53+
/**
54+
* 기본 배송지 여부
55+
*/
56+
private boolean isDefault;
57+
58+
/**
59+
* 주소 등록 일시
60+
*/
61+
private LocalDateTime createdAt;
62+
63+
/**
64+
* Address 엔티티를 AddressResponse DTO로 변환
65+
*
66+
* @param address 변환할 Address 엔티티
67+
* @return 생성된 AddressResponse 객체
68+
*/
69+
public static AddressResponse from(Address address) {
70+
return AddressResponse.builder()
71+
.addressId(address.getAddressId())
72+
.recipient(address.getRecipient())
73+
.postalCode(address.getPostalCode())
74+
.address(address.getAddress())
75+
.phoneNumber(address.getPhoneNumber())
76+
.isDefault(address.isDefault())
77+
.createdAt(address.getCreatedAt())
78+
.build();
79+
}
80+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.example.ecommercewebservice.domain.user.dto.response;
2+
3+
import com.example.ecommercewebservice.domain.user.entity.User;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
import java.time.LocalDateTime;
10+
import java.util.List;
11+
import java.util.stream.Collectors;
12+
13+
@Getter
14+
@NoArgsConstructor
15+
@AllArgsConstructor
16+
@Builder
17+
public class UserProfileResponse {
18+
private Long userId;
19+
private String email;
20+
private String username;
21+
private String phoneNumber;
22+
private List<AddressResponse> addresses;
23+
private String role;
24+
private LocalDateTime createdAt;
25+
26+
public static UserProfileResponse from(User user) {
27+
return UserProfileResponse.builder()
28+
.userId(user.getUserId())
29+
.email(user.getEmail())
30+
.username(user.getActualUsername())
31+
.phoneNumber(user.getPhoneNumber())
32+
.addresses(user.getAddresses().stream()
33+
.map(AddressResponse::from)
34+
.collect(Collectors.toList()))
35+
.role(user.getRoles().getFirst())
36+
.createdAt(user.getCreatedAt())
37+
.build();
38+
}
39+
}

0 commit comments

Comments
 (0)