2026.03.27 - [Spring 2기 과제] - 포인트 기반 커피 주문 결제 시스템 (도메인 설계)
포인트 기반 커피 주문 결제 시스템 (도메인 설계)
https://github.com/Banhklo2/coffee-order-system.git GitHub - Banhklo2/coffee-order-system: Spring Boot 기반 포인트 기반 커피 주문 결제 시스템Spring Boot 기반 포인트 기반 커피 주문 결제 시스템. Contribute to Banhklo2/coffee-o
sudaruuu.tistory.com
문제 상황
과제를 진행하면서 다음과 같은 요구사항이 있었다.
주문 완료 후 외부 데이터 수집 플랫폼으로 주문 정보를 전송해야 한다.
(Mock API 또는 테스트 코드를 통해 구현)
기존에 테스트 코드를 활용한 검증은 여러 번 해본 경험이 있었지만,
Mock API를 직접 구현해본 경험은 없었다.
그래서 이번 과제에서는 단순 테스트 코드 대신
⭐ 외부 플랫폼을 직접 흉내 낸 Mock API를 구현해보고 싶었다.
작업 목적
- 외부 API 연동 흐름을 실제처럼 경험해보기
- 단순 단위 테스트가 아닌 HTTP 요청/응답 흐름 전체 검증
- 외부 시스템과의 통신 구조 이해
선택한 방법
✔️ 테스트 코드 대신 Mock API 선택
| 방식 | 특징 |
| 테스트 코드 | 내부 로직 검증 중심 |
| Mock API | 실제 HTTP 통신 흐름 검증 |
이번에는
- 요청 → 외부 API → 응답
- 이 전체 흐름을 직접 경험해보고 싶었기 때문에
⭐ Mock API 방식을 선택했다.
구현 과정
1. 외부 플랫폼 역할을 하는 Mock API 구현
@RestController
@RequestMapping("/external/orders")
@Slf4j
public class ExternalMockController {
@PostMapping
public ApiResponse<Void> receiveOrder(@RequestBody ExternalOrderRequest request) {
log.info("외부 플랫폼 주문 데이터 수신 - userId={}, menuId={}, amount={}",
request.getUserId(), request.getMenuId(), request.getAmount());
return ApiResponse.noContent();
}
}
⭐ 핵심 포인트
- 실제 외부 서버 대신 내 애플리케이션 내부에 API 생성
- 외부 플랫폼 역할을 그대로 흉내내는 구조
- 로그를 통해 데이터 수신 여부 확인
2. 외부 API 호출용 Client 분리
@Component
@RequiredArgsConstructor
@Slf4j
public class ExternalOrderClient {
private final RestTemplate restTemplate;
public void sendOrder(ExternalOrderRequest request) {
try {
log.info("ExternalOrderClient 진입");
restTemplate.postForEntity(
"http://localhost:8080/external/orders",
request,
Void.class
);
log.info("외부 플랫폼 전송 완료 - userId={}, menuId={}, amount={}",
request.getUserId(), request.getMenuId(), request.getAmount());
} catch (Exception e) {
log.error("외부 플랫폼 전송 실패", e);
throw e;
}
}
}
⭐ 핵심 포인트
- Service에서 직접 호출하지 않고 Client로 분리
- 외부 통신 책임을 분리하여 구조 명확화
3. 주문 성공 이후 외부 플랫폼 전송
이 부분은 설계 단계에서 미리 정의했다.
주문 생성 → 포인트 차감 → 결제 성공 → 외부 플랫폼 전송
⭐ 이유
- 결제 실패 시 외부 전송은 의미 없음
- 데이터 정합성을 위해 성공 이후에만 호출
externalOrderClient.sendOrder(
new ExternalOrderRequest(
user.getId(),
savedOrderItem.getMenu().getId(),
savedOrder.getTotalPrice()
)
);
4. Postman을 통한 실제 호출 흐름 검증
테스트 방식
- /api/orders 호출
- 내부 주문/결제 로직 수행
- /external/orders 자동 호출
- 로그 확인
⭐ 로그 결과
외부 플랫폼 전송 직전
ExternalOrderClient 진입
외부 플랫폼 주문 데이터 수신
외부 플랫폼 전송 완료
외부 플랫폼 전송 직후

⭐ 단순 테스트가 아니라
⭐ 실제 외부 API 연동 흐름을 그대로 재현
배운 점
1. Mock API는 실제 외부 API처럼 테스트할 수 있다
- 단순 단위 테스트보다 훨씬 현실적인 검증 가능
- HTTP 흐름 전체를 직접 확인 가능
2. 외부 API는 구조 분리가 중요하다
- Service에 직접 넣으면 책임이 섞인다
- Client로 분리하는 것이 훨씬 명확한 구조
3. "언제 호출할 것인가"가 더 중요하다
외부 API 연동에서 중요한 것은
어떻게 호출할지가 아니라
⭐ 언제 호출할지
아쉬운 점
현재는 동기 방식으로 구현되어 있다.
- 외부 API가 느리면 주문도 함께 지연됨
- 장애 전파 가능성 존재
⭐ 향후 개선 방향
- 비동기 처리 (@Async)
- 메시지 큐 기반 구조로 확장
느낀 점
이번 구현을 통해 외부 API 연동을 단순 개념이 아니라
실제 흐름을 만들어보면서 이해할 수 있었다.
Mock API를 활용하면 외부 시스템이 없어도 충분히 테스트가 가능하다는 것을 알게 되었다 !!
앞으로는 확장성을 고려하여 비동기 처리 방식까지 함께 적용해보고 싶다.
'트러블슈팅' 카테고리의 다른 글
| 주문 완료 후 외부 전송을 이벤트 기반으로 분리하며 겪은 문제 (0) | 2026.03.31 |
|---|---|
| 외부 API 호출 비동기 처리 적용 (@Async) (0) | 2026.03.31 |
| 예외 처리 범위 확장: GlobalExceptionHandler 리팩토링 (0) | 2026.03.30 |
| DTO 생성 방식 혼용 문제를 기준 정립으로 해결하기 (1) | 2026.03.30 |
| 기능 중심 API 설계의 한계와 도메인 중심으로의 개선 과정 (0) | 2026.03.30 |