Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ out/

### VS Code ###
.vscode/

### SpringBoot ###
*.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.ecommercewebservice.domain.user.entity;
package com.example.ecommercewebservice.config;

import lombok.Getter;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.ecommercewebservice.domain.redis;
package com.example.ecommercewebservice.config.redis;

import com.google.gson.Gson;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.example.ecommercewebservice.domain.user.dto.UserRoleUpdateRequest;
import com.example.ecommercewebservice.domain.user.service.UserService;
import com.example.ecommercewebservice.global.security.annotation.RoleRequired;
import com.example.ecommercewebservice.domain.user.entity.UserRole;
import com.example.ecommercewebservice.config.UserRole;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
package com.example.ecommercewebservice.domain.user.controller;

import com.example.ecommercewebservice.domain.user.dto.LoginRequest;
import com.example.ecommercewebservice.domain.user.dto.LoginResponse;
import com.example.ecommercewebservice.domain.user.dto.SignupRequest;
import com.example.ecommercewebservice.domain.user.dto.SignupResponse;
import com.example.ecommercewebservice.config.UserRole;
import com.example.ecommercewebservice.domain.user.dto.response.UserProfileResponse;
import com.example.ecommercewebservice.domain.user.dto.request.UserUpdateRequestDto;
import com.example.ecommercewebservice.domain.user.dto.response.UserResponseDto;
import com.example.ecommercewebservice.domain.user.dto.signIn.LoginRequest;
import com.example.ecommercewebservice.domain.user.dto.signIn.LoginResponse;
import com.example.ecommercewebservice.domain.user.dto.signUp.SignupRequest;
import com.example.ecommercewebservice.domain.user.dto.signUp.SignupResponse;
import com.example.ecommercewebservice.domain.user.entity.User;
import com.example.ecommercewebservice.domain.user.service.UserService;
import com.example.ecommercewebservice.global.constant.MessageConstants;
import com.example.ecommercewebservice.global.dto.RsData;
import com.example.ecommercewebservice.global.security.annotation.RoleRequired;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

Expand Down Expand Up @@ -44,7 +46,7 @@ public ResponseEntity<RsData<SignupResponse>> signup(@Valid @RequestBody SignupR
public ResponseEntity<RsData<LoginResponse>> login(@Valid @RequestBody LoginRequest loginRequest) {
// 로그인 로직 구현 - 예외는 GlobalExceptionHandler에서 처리
LoginResponse loginResponse = userService.login(loginRequest);

// 성공 시 - 로그인 응답 반환
RsData<LoginResponse> rsData = new RsData<>(String.valueOf(HttpStatus.OK.value()), MessageConstants.LOGIN_SUCCESS, loginResponse);
return ResponseEntity.ok(rsData);
Expand All @@ -60,4 +62,32 @@ public ResponseEntity<Map<String, String>> logout(@RequestHeader("Authorization"
}
return ResponseEntity.badRequest().body(Map.of("error", "유효하지 않은 인증 정보입니다."));
}

// 사용자 프로필(정보) 조회
@GetMapping("/me")
@RoleRequired(UserRole.USER) // USER 권한이 있는 사용자만 접근 가능
public ResponseEntity<RsData<UserProfileResponse>> getMyProfile(@AuthenticationPrincipal User user) { // @AuthenticationPrincipal 어노테이션을 사용하여 현재 로그인한 사용자 정보를 가져와서 User에 주입, 메서드 파라미터에만 사용할 수 있다.
UserProfileResponse profile = userService.getMyProfile(user);
RsData<UserProfileResponse> rsData = new RsData<>(
String.valueOf(HttpStatus.OK.value()),
"프로필 조회 성공",
profile
);
return ResponseEntity.ok(rsData);
}

// 사용자 프로필(정보) 수정
@PutMapping("/me")
@RoleRequired(UserRole.USER)
public ResponseEntity<RsData<UserResponseDto>> updateProfile(
@AuthenticationPrincipal User user,
@Valid @RequestBody UserUpdateRequestDto request) {
UserResponseDto response = userService.updateProfile(user.getUserId(), request);
RsData<UserResponseDto> rsData = new RsData<>(
String.valueOf(HttpStatus.OK.value()),
"프로필 수정 성공",
response
);
return ResponseEntity.ok(rsData);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
package com.example.ecommercewebservice.domain.user.dto;

import com.example.ecommercewebservice.domain.user.entity.UserRole;
import com.example.ecommercewebservice.config.UserRole;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
* 사용자 권한 업데이트 요청 DTO
*
* 사용 위치:
* - AdminController - 관리자가 사용자 권한을 변경할 때 사용
* - UserService.updateUserRole() - 사용자 권한 변경 비즈니스 로직에서 사용
*
* 용도:
* - 관리자가 다른 사용자의 역할/권한을 변경하기 위한 요청 데이터를 담는 객체
* - 사용자 권한 관리를 위한 관리자 전용 기능에서 사용
*/
@Getter
@NoArgsConstructor
public class UserRoleUpdateRequest {
/**
* 변경할 사용자 역할 (ROLE_USER, ROLE_ADMIN 등)
*/
private UserRole role;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.example.ecommercewebservice.domain.user.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
* 사용자 주소 수정 요청을 위한 DTO
*
* 사용 위치:
* - UserUpdateRequestDto.addresses - 사용자 프로필 수정 시 주소 정보를 담는 객체
* - UserService.updateAddresses() - 주소 정보 업데이트 로직에서 사용
*
* 용도:
* - 사용자의 주소 정보를 추가, 수정, 삭제하기 위한 요청 데이터를 담는 객체
* - addressId가 null이면 새 주소 추가, 있으면 기존 주소 수정
* - 기본 주소는 반드시 하나만 존재해야 함
*/
@Getter
@NoArgsConstructor
public class AddressUpdateRequestDto {
private Long addressId; // null이면 새 주소 추가, 있으면 수정 또는 삭제

@NotBlank(message = "수취인 이름은 필수입니다.")
private String recipient;

@NotBlank(message = "우편번호는 필수입니다.")
@Pattern(regexp = "^\\d{5}$", message = "우편번호는 5자리 숫자여야 합니다.")
private String postalCode;

@NotBlank(message = "주소는 필수입니다.")
private String address;

@NotBlank(message = "전화번호는 필수입니다.")
@Pattern(regexp = "^01(?:0|1|[6-9])-(?:\\d{3}|\\d{4})-\\d{4}$",
message = "올바른 전화번호 형식이 아닙니다. (예: 010-1234-5678)")
private String phone;

private boolean isDefault;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.example.ecommercewebservice.domain.user.dto.request;

import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

/**
* 사용자 프로필 수정 요청을 위한 DTO
*
* 사용 위치:
* - UserController.updateProfile() - PUT /api/users/me 엔드포인트에서 사용
* - UserService.updateProfile() - 프로필 수정 비즈니스 로직에서 사용
*
* 용도:
* - 사용자의 비밀번호, 이름, 전화번호, 주소 정보를 수정하기 위한 요청 데이터를 담는 객체
* - 모든 필드는 선택적으로 수정 가능
* - 주소 정보는 추가, 수정, 삭제가 가능
*/
@Getter
@NoArgsConstructor
public class UserUpdateRequestDto {
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$",
message = "비밀번호는 최소 8자 이상이며, 영문자, 숫자, 특수문자를 포함해야 합니다.")
private String password;

private String username;

@Pattern(regexp = "^01(?:0|1|[6-9])-(?:\\d{3}|\\d{4})-\\d{4}$",
message = "올바른 전화번호 형식이 아닙니다. (예: 010-1234-5678)")
private String phoneNumber;

private List<AddressUpdateRequestDto> addresses;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.example.ecommercewebservice.domain.user.dto.response;

import com.example.ecommercewebservice.domain.user.entity.Address;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
* 주소 정보 응답 DTO
*
* 사용 위치:
* - AddressController - 주소 관련 API 응답으로 사용
* - UserService.getAddresses() - 사용자의 주소 목록 조회 시 사용
*
* 용도:
* - 사용자의 주소 정보를 클라이언트에 반환하기 위한 객체
* - 주소 정보만을 독립적으로 반환할 때 사용
* - UserResponseDto.AddressResponseDto와 유사하지만 독립적인 응답으로 사용
*/
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AddressResponse {
/**
* 주소 식별자
*/
private Long addressId;

/**
* 수령인 이름
*/
private String recipient;

/**
* 우편번호
*/
private String postalCode;

/**
* 상세 주소
*/
private String address;

/**
* 배송지 연락처
*/
private String phoneNumber;

/**
* 기본 배송지 여부
*/
private boolean isDefault;

/**
* 주소 등록 일시
*/
private LocalDateTime createdAt;

/**
* Address 엔티티를 AddressResponse DTO로 변환
*
* @param address 변환할 Address 엔티티
* @return 생성된 AddressResponse 객체
*/
public static AddressResponse from(Address address) {
return AddressResponse.builder()
.addressId(address.getAddressId())
.recipient(address.getRecipient())
.postalCode(address.getPostalCode())
.address(address.getAddress())
.phoneNumber(address.getPhoneNumber())
.isDefault(address.isDefault())
.createdAt(address.getCreatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.example.ecommercewebservice.domain.user.dto.response;

import com.example.ecommercewebservice.domain.user.entity.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserProfileResponse {
private Long userId;
private String email;
private String username;
private String phoneNumber;
private List<AddressResponse> addresses;
private String role;
private LocalDateTime createdAt;

public static UserProfileResponse from(User user) {
return UserProfileResponse.builder()
.userId(user.getUserId())
.email(user.getEmail())
.username(user.getActualUsername())
.phoneNumber(user.getPhoneNumber())
.addresses(user.getAddresses().stream()
.map(AddressResponse::from)
.collect(Collectors.toList()))
.role(user.getRoles().getFirst())
.createdAt(user.getCreatedAt())
.build();
}
}
Loading