이제부터 만든 서비스를 호출하기 위해 presentation Layer
설계를 해보자.
그전까지는 다른곳에서 가져다 쓸 mycoupon-core
에서 작업하였고, 지금은 mycore-api
에서 작업을 진행하겠다.
- CouponIssueController
// mycouponapi/controller/CouponIssueController.java
package com.example.mycouponapi.controller;
import com.example.mycouponapi.dto.CouponIssueRequestDto;
import com.example.mycouponapi.dto.CouponIssueResponseDto;
import com.example.mycouponapi.service.CouponIssueRequestService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class CouponIssueController {
private final CouponIssueRequestService couponIssueRequestService;
@PostMapping("/v1/issue")
public CouponIssueResponseDto issueV1(@RequestBody CouponIssueRequestDto body) {
couponIssueRequestService.issueRequestV1(body);
return new CouponIssueResponseDto(true, null);
}
}
조금씩 변화하는 걸 보여주기 위해 제일 초기 버전인 v1
으로 api를 작성하였다.
편한 Dto 사용을 위해 record 타입을 사용하였다. record는 간단하게 말하자면 불변객체를 쉽게 만들 수 있는 타입이며, 불변을 보장하는 것외에도 getter, ToString, hashCode 등 기본으로 제공하는 기능들이 많으니 궁금하면 더 알아보자.
- CouponIssueRequestDto
// mycouponapi/dto/CouponIssueRequestDto
package com.example.mycouponapi.dto;
public record CouponIssueRequestDto(long userId,long couponId) {}
- CouponIssueResponseDto
// mycouponapi/dto/CouponIssueResponseDto
package com.example.mycouponapi.dto;
public record CouponIssueResponseDto(boolean isSuccess, String comment) {}
이 얼마나 간단한가!! 만약 record를 쓰지 않고 dto를 작성해야 한다면 다음과 같이 작성이 될 것이다.
- CouponIssueRequestDto_1
// mycouponapi/dto/CouponIssueRequestDto_1
package com.example.mycouponapi.dto;
import lombok.*;
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class CouponIssueRequestDto_1 {
private long userId;
private long couponId;
}
보통 객체 생성을 위해 빌더패턴을 자주 이용하기 때문에 @Builder
도 붙여주었다.
Builder패턴을 사용하지 않으려면 정적 팩토리 메서드 패턴을 써서 각 객체 생성을 메서드로 정의해 주면 된다.
위와 비교해보면 정말 간단해지지 않았는가? 하지만 이런 record 타입도 단점이 존재하니 유의해서 쓰자.
- CouponIssueRequestService
// mycouponapi/dto/CouponIssueRequestService
package com.example.mycouponapi.service;
import com.example.mycouponapi.dto.CouponIssueRequestDto;
import com.example.mycouponcore.service.CouponIssueService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
@Slf4j
public class CouponIssueRequestService {
private final CouponIssueService couponIssueService;
public void issueRequestV1(CouponIssueRequestDto requestDto) {
couponIssueService.issue(requestDto.couponId(), requestDto.userId());
log.info("쿠폰 발급 완료. couponId: %s, userId: %s"
.formatted(requestDto.couponId(), requestDto.userId()));
}
}
위와 같이 mycoupon-core
에 있는 빈을 가져다 쓸 수 있는건 메인 함수에서 mycoupon-core
에 있는 빈을 import해주었기 때문이다.
- MycouponApiApplication
// mycouponapi/MycouponApiApplication.java
package com.example.mycouponapi;
import com.example.mycouponapi.dto.CouponIssueRequestDto_1;
import com.example.mycouponcore.MyCouponCoreConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@Import(MyCouponCoreConfiguration.class) // 여기서 core쪽 bean을 사용하기 위해 import해줌
@SpringBootApplication
public class MycouponApiApplication {
public static void main(String[] args) {
System.setProperty("spring.config.name", "application-core, application-api");
SpringApplication.run(MycouponApiApplication.class, args);
}
}
마지막으로 예외가 발생했을 때 exception을 잡아주기 위해 excetionHandler 를 만들어 주자.
- CouponControllerAdvice
// mycouponapi/exception/handler/CouponControllerAdvice.java
package com.example.mycouponapi.exception.handler;
import com.example.mycouponapi.dto.CouponIssueResponseDto;
import com.example.mycouponcore.exception.CouponIssueException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class CouponControllerAdvice {
@ExceptionHandler(CouponIssueException.class)
public CouponIssueResponseDto couponIssueExceptionHandler(CouponIssueException exception) {
return new CouponIssueResponseDto(false, exception.getErrorCode().message);
}
}
예외가 터지면 점점 위로 올라오기 때문에 잡아서 따로 처리를 해줘야 내가 원하는 핸들링을 해줄 수 있다.
이제 한번 테스트를 진행해보자.
- 정상 응답 테스트
- 중복 예외 발생 테스트(같은 요청 한번 더 보내면)
나머지 db 값을 바꿔가며 다른 예외가 발생하는 것을 확인해 보는 것이 좋다.
'토이프로젝트 > 선착순 이벤트 쿠폰 시스템' 카테고리의 다른 글
Mysql 기반 선착순 쿠폰 발급 기능 개발 (5) (0) | 2024.05.23 |
---|---|
Mysql 기반 선착순 쿠폰 발급 기능 개발 (4) (0) | 2024.04.30 |
Mysql 기반 선착순 쿠폰 발급 기능 개발 (2) (0) | 2024.04.16 |
Mysql 기반 선착순 쿠폰 발급 기능 개발 (1) (0) | 2024.04.08 |
2. 프로젝트 환경 설정 (0) | 2024.04.06 |