Spring 개발일지

[팀프로젝트] MSA 기반 티켓팅 프로그램 - 결제창 자동 실행 API 구현

김둘리 2026. 5. 14. 09:02

포스트맨으로 결제 생성하고 test.html 수정하는 번거로움을 없애기 위해 브라우저 URL 입력만으로 토스 결제창이 자동으로 뜨는 API를 만들었다.


1. 기존 테스트 방식의 번거로움

1. 포스트맨으로 POST /internal/v1/payments 호출
2. 응답에서 orderId 복사
3. test.html 열어서 orderId 수정
4. 브라우저에서 test.html 열기
5. 결제하기 버튼 클릭

매번 orderId를 복사해서 test.html을 수정하는 게 번거로웠다.


2. 해결 방법

백엔드 API가 HTML을 직접 반환하면 브라우저가 그걸 렌더링해서 토스 결제창을 자동으로 띄울 수 있다.

브라우저 주소창에 URL 입력
→ 결제 생성 (orderId 자동 생성)
→ HTML 응답 (토스 결제창 자동 실행)
→ 끝!

3. 왜 Spring이 브라우저를 직접 열 수 없어?

Spring 백엔드는 이것만 할 수 있다.

요청 받기 → 로직 처리 → 응답 반환 (JSON, HTML, Redirect 등)

브라우저를 직접 열거나 조작하는 건 불가능하다. 브라우저가 HTML 응답을 받아서 렌더링하는 거다. 토스 결제창은 JavaScript로만 띄울 수 있어서 HTML을 반환하는 방식을 선택했다.


4. 구현

PaymentInternalController에 GET /payment-page 엔드포인트를 추가했다.

@GetMapping("/payment-page")
public ResponseEntity getPaymentPage(
        @RequestParam UUID bookingId,
        @RequestParam UUID userId,
        @Positive @RequestParam Integer amount) {

    PaymentResult result = paymentCommandService.createPayment(
            new CreatePaymentCommand(bookingId, userId, amount)
    );

    String html = """
                const tossPayments = TossPayments("test_ck_QbgMGZzorzmyQnGjmOXkVl5E1em4");
                tossPayments.requestPayment("카드", {
                  amount: %d,
                  orderId: "%s",
                  orderName: "First Ticket 예매",
                  customerName: "김토스",
                  successUrl: "http://localhost:8085/api/v1/payments/confirm-redirect",
                  failUrl: "http://localhost:8085/fail"
                });
              """.formatted(result.amount(), result.orderId()); return ResponseEntity.ok() .contentType(MediaType.TEXT_HTML) .body(html); }

@Positive로 amount 양수 검증을 추가했다.


5. 사용 방법

브라우저 주소창에 입력하면 끝이다!

http://localhost:8085/internal/v1/payments/payment-page?bookingId=ff1e8400-e29b-41d4-a716-446655440001&userId=aa1e8400-e29b-41d4-a716-446655440002&amount=50000

⚠️ bookingId는 매번 다른 UUID 사용 (unique 제약 때문에 중복 불가)


6. 두 가지 테스트 방법

방법 1 - 포스트맨 (기존)
POST /internal/v1/payments → orderId 받음 → test.html 수정 → 결제

방법 2 - 브라우저 URL (신규)
GET /internal/v1/payments/payment-page?bookingId=xxx&userId=xxx&amount=xxx → 끝!

7. Booking 서비스 연동 후 변경 계획

Booking 서비스가 완성되면 결제 생성은 Booking이 담당한다.

// 변경 전 (현재)
@GetMapping("/payment-page")
public ResponseEntity<String> getPaymentPage(
        @RequestParam UUID bookingId,
        @RequestParam UUID userId,
        @RequestParam Integer amount) {
    PaymentResult result = paymentCommandService.createPayment(...); // 제거 예정
    ...
}

// 변경 후 (Booking 연동 후)
@GetMapping("/payment-page")
public ResponseEntity<String> getPaymentPage(
        @RequestParam String orderId,
        @RequestParam Integer amount) {
    // Booking이 생성한 orderId로 바로 결제창 실행
    ...
}
포스트맨 → Booking 예매 확정 API → orderId 응답
브라우저 → GET /internal/v1/payments/payment-page?orderId=xxx&amount=xxx

파라미터만 바꾸면 돼서 수정이 간단하다.


마무리

백엔드가 HTML을 반환하면 브라우저가 렌더링해서 JavaScript를 실행한다는 점을 활용했다.

프론트 없는 백엔드 개발 환경에서 테스트 편의성을 크게 높일 수 있었다.