728x90
✅ 이전 포스팅
2024.12.13 - [Java/쇼핑몰] - 쇼핑몰 7 - [이메일로 비밀번호 변경 URL 전송]
쇼핑몰 7 - [이메일로 비밀번호 변경 URL 전송]
✅ 이전 포스팅2024.12.02 - [Java/쇼핑몰] - 쇼핑몰 - 6 [Spring Security + JWT + OAuth2 + Redis] 쇼핑몰 - 6 [Spring Security + JWT + OAuth2 + Redis]✅ 이전 포스팅2024.11.19 - [Java/쇼핑몰] - 쇼핑몰 - 5 [spring security + JWT + Red
magicmk.tistory.com
이전 포스팅을 올리고 한 달이 넘는 시간이 지났다.
그동안 골프, 웹툰 등에 빠져서 책도 많이 읽지 않고 나태한 나날을 보냈다..
쇼핑몰 개발은 거의 끝나가는데 블로그는 올리지도 않고 반성해야겠다. ㅠㅠ
상품을 주문하기 위해서는 회원의 배송지를 설정할 수 있어야 하기 때문에 이번 포스팅에서는
배송지 로직을 작성해보도록 하겠다.
✅ 컨트롤러
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/address")
public class AddressController implements AddressControllerDocs{
private final AddressService addressService;
@GetMapping
public ResponseEntity<Response<?>> addressList(Authentication authentication) {
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
List<AddressListResponseDTO> result = addressService.addressList(userDetails);
return buildResponse(HttpStatus.OK, "배송지 목록 조회 성공", result);
}
@PostMapping
public ResponseEntity<Response<?>> saveAddress(@RequestBody @Valid AddressSaveRequestDTO parameter, Authentication authentication) {
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
Long result = addressService.saveAddress(parameter, userDetails);
return buildResponse(HttpStatus.CREATED, "배송지 등록 성공", result);
}
@PutMapping("/{addressId}")
public ResponseEntity<Response<?>> updateAddress(@PathVariable("addressId") Long addressId, @RequestBody @Valid AddressUpdateRequestDTO parameter, Authentication authentication) {
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
Long result = addressService.updateAddress(addressId, parameter, userDetails);
return buildResponse(HttpStatus.OK, "배송지 수정 성공", result);
}
@DeleteMapping("/{addressId}")
public ResponseEntity<Response<?>> deleteAddress(@PathVariable("addressId") Long addressId, Authentication authentication) {
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
addressService.deleteAddress(addressId, userDetails);
return buildResponse(HttpStatus.OK, "배송지 삭제 성공", null);
}
@PostMapping("/defaultAddress/{addressId}")
public ResponseEntity<Response<?>> updateDefaultAddress(@PathVariable("addressId") Long addressId, Authentication authentication) {
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
Long result = addressService.chooseDefaultAddress(addressId, userDetails);
return buildResponse(HttpStatus.OK, "기본 배송지 설정 성공", result);
}
}
✅ 서비스
@Service
@RequiredArgsConstructor
public class AddressService {
private final MemberRepository memberRepository;
private final AddressRepository addressRepository;
/**
* 사용자의 모든 주소 정보를 조회하여 AddressListResponseDTO 리스트로 반환합니다.
*
* @param userDetails 현재 로그인된 사용자의 정보를 담고 있는 객체
* @return 사용자의 주소 정보를 포함하는 AddressListResponseDTO 리스트
*/
public List<AddressListResponseDTO> addressList(UserDetailsImpl userDetails) {
Member member = getMember(userDetails.getUserId());
List<AddressListResponseDTO> DTOList = new ArrayList<>();
addressRepository.findAllByMember(member).forEach(address -> {
AddressListResponseDTO dto = AddressListResponseDTO.builder()
.id(address.getId())
.name(address.getName())
.addr(address.getAddr())
.addrDetail(address.getAddrDetail())
.addrNickName(address.getAddrName())
.phone(address.getPhone())
.request(address.getRequest())
.defaultType(address.getDefaultType())
.build();
DTOList.add(dto);
});
return DTOList;
}
/**
* 사용자의 주소 정보를 저장합니다.
*
* @param parameter 주소 저장 요청 데이터를 담고 있는 DTO 객체
* @param userDetails 현재 인증된 사용자의 정보를 담고 있는 객체
* @return 저장된 주소의 ID
* @throws UsernameNotFoundException 사용자 정보가 존재하지 않을 경우 발생
*/
public Long saveAddress(AddressSaveRequestDTO parameter, UserDetailsImpl userDetails) {
Member member = memberRepository.findById(userDetails.getUserId())
.orElseThrow(() -> new UsernameNotFoundException("존재하지 않는 사용자입니다."));
Address address = Address.builder()
.name(parameter.getName())
.addr(parameter.getAddr())
.addrName(parameter.getAddrNickName())
.addrDetail(parameter.getAddrDetail())
.phone(parameter.getPhone())
.zipCode(parameter.getZipCode())
.request(parameter.getRequest())
.member(member)
.build();
Address savedAddress = addressRepository.save(address);
return savedAddress.getId();
}
/**
* 사용자의 배송지 정보를 업데이트합니다.
*
* @param addressId 업데이트할 배송지의 ID
* @param parameter 배송지 업데이트 요청 데이터를 담고 있는 DTO 객체
* @param userDetails 현재 인증된 사용자의 정보를 담고 있는 객체
* @return 업데이트된 배송지의 ID
* @throws UsernameNotFoundException 사용자 정보가 존재하지 않을 경우 발생
* @throws NotFoundException 배송지 정보가 존재하지 않을 경우 발생
* @throws AccessDeniedException 로그인된 사용자의 배송지가 아닐 경우 발생
*/
public Long updateAddress(Long addressId, AddressUpdateRequestDTO parameter, UserDetailsImpl userDetails) {
Member member = getMember(userDetails.getUserId());
Address address = getAddress(addressId);
if (!address.getMember().getId().equals(member.getId())) {
throw new AccessDeniedException("로그인된 회원의 배송지가 아닙니다.");
}
address.updateAddress(parameter);
return address.getId();
}
/**
* 사용자의 배송지 정보를 삭제합니다.
*
* @param addressId 삭제할 배송지의 ID
* @param userDetails 현재 인증된 사용자의 정보를 담고 있는 객체
* @throws AccessDeniedException 로그인된 사용자의 배송지가 아닐 경우 발생
*/
public void deleteAddress(Long addressId, UserDetailsImpl userDetails) {
Member member = getMember(userDetails.getUserId());
Address address = getAddress(addressId);
if (!address.getMember().getId().equals(member.getId())) {
throw new AccessDeniedException("로그인된 회원의 배송지가 아닙니다.");
}
addressRepository.deleteById(addressId);
}
/**
* 사용자의 특정 배송지를 기본 배송지로 설정합니다.
*
* <p>현재 로그인한 사용자의 배송지 목록 중 기존 기본 배송지를 해제하고,
* 선택한 배송지를 기본 배송지로 설정합니다.</p>
*
* @param addressId 기본 배송지로 설정할 배송지의 ID
* @param userDetails 로그인한 사용자의 정보
* @return 기본 배송지로 설정된 배송지의 ID
* @throws AccessDeniedException 선택한 배송지가 로그인한 사용자의 배송지가 아닌 경우
*/
@Transactional
public Long chooseDefaultAddress(Long addressId, UserDetailsImpl userDetails) {
Member member = getMember(userDetails.getUserId());
Address address = getAddress(addressId);
if (!address.getMember().getId().equals(member.getId())) {
throw new AccessDeniedException("로그인된 회원의 배송지가 아닙니다.");
}
addressRepository.resetDefaultTypeForMember(member.getId());
address.updateDefaultType('Y');
return address.getId();
}
private Member getMember(Long userId) {
return memberRepository.findById(userId)
.orElseThrow(() -> new UsernameNotFoundException("존재하지 않는 사용자입니다."));
}
private Address getAddress(Long addressId) {
return addressRepository.findById(addressId)
.orElseThrow(() -> new NotFoundException("존재하지 않는 배송지입니다."));
}
}
✅ DTO
🟨 AddressListResponseDTO
@Data
@Builder
public class AddressListResponseDTO {
private Long id;
private String name;
private String addr;
private String addrDetail;
private String addrNickName;
private String phone;
private String request;
private char defaultType;
}
🟨 AddressSaveRequestDTO
@Data
@Schema(description = "배송지 등록에 필요한 요청 정보", requiredProperties = {"name", "addr", "addrDetail", "phone", "zipCode"})
public class AddressSaveRequestDTO {
@Schema(description = "받는 분 이름")
@NotBlank(message = "해당 값은 필수값 입니다.")
private String name;
@Schema(description = "주소")
@NotBlank(message = "해당 값은 필수값 입니다.")
private String addr;
@Schema(description = "배송지 별명")
private String addrNickName;
@Schema(description = "상세주소")
@NotBlank(message = "해당 값은 필수값 입니다.")
private String addrDetail;
@Schema(description = "받는 분 연락처")
@NotBlank(message = "해당 값은 필수값 입니다.")
private String phone;
@Schema(description = "우편번호")
@NotBlank(message = "해당 값은 필수값 입니다.")
private String zipCode;
@Schema(description = "요청 사항")
private String request;
}
🟨 AddressUpdateRequestDTO
@Data
@Schema(description = "배송지 수정에 필요한 요청 정보", requiredProperties = {"name", "addr", "addrDetail", "phone", "zipCode"})
public class AddressUpdateRequestDTO {
@Schema(description = "받는 분 이름")
@NotBlank(message = "해당 값은 필수값 입니다.")
private String name;
@Schema(description = "주소")
@NotBlank(message = "해당 값은 필수값 입니다.")
private String addr;
@Schema(description = "배송지 별명")
private String addrNickName;
@Schema(description = "상세주소")
@NotBlank(message = "해당 값은 필수값 입니다.")
private String addrDetail;
@Schema(description = "받는 분 연락처")
@NotBlank(message = "해당 값은 필수값 입니다.")
private String phone;
@Schema(description = "우편번호")
@NotBlank(message = "해당 값은 필수값 입니다.")
private String zipCode;
@Schema(description = "요청 사항")
private String request;
}
✅ Repository
public interface AddressRepository extends JpaRepository<Address, Long> {
@Modifying
@Query("update Address a set a.defaultType = 'N' where a.member.id = :memberId and a.defaultType = 'Y'")
void resetDefaultTypeForMember(@Param("memberId") Long memberId);
List<Address> findAllByMember(Member member);
}
✅ 마무리
아무래도 단순한 CRUD라서 코드를 설명할 것이 딱히 없는 것 같다.
포스팅하는 것이 간단하니 리마인드 하는 겸 자주자주 올려야겠다...
이래놓고 또 잉여롭게 살겠지...
'Java > 쇼핑몰' 카테고리의 다른 글
쇼핑몰 7 - [이메일로 비밀번호 변경 URL 전송] (0) | 2024.12.13 |
---|---|
쇼핑몰 - 6 [Spring Security + JWT + OAuth2 + Redis] (2) | 2024.12.02 |
쇼핑몰 - 5 [spring security + JWT + Redis 로그인] (0) | 2024.11.19 |
쇼핑몰 - 4 [회원 가입 구현] (0) | 2024.11.12 |
쇼핑몰 - 3 [GitHub Actions를 통한 CI/CD] (3) | 2024.11.06 |