[코드캠프]28일차_TIL_결제 프로세스

망구씨·2023년 4월 20일
0

프론트엔드_TIL

목록 보기
24/27
post-thumbnail

결제의 역사

1. 카드사

각각의 카드사에게 승인을 받아 결제 시스템을 구축하는 방법입니다. 신한카드, 하나카드, 농협카드 등등 현존하는 다양한 카드 회사들은 지원하는 결제 방법이 전부 다릅니다.
그렇기 때문에 모든 경우를 고려하여 결제 시스템을 개발하려면 인건비와 시간이 많이 소요됩니다. 실제 비즈니스 환경에서는 이러한 방식으로 개발하는 것이 불가능하다고 볼 수 있습니다.

2. PG (Payment Gateway)
각각의 카드사와 연결하는 작업을 대신해주는 PG사를 통해 결제 시스템을 구축하는 방법입니다.
대표적인 PG사에는 NHN, 나이스페이, KG이니시스 등이 있습니다.

PG사는 카드사들과 미리 의논해서 결제를 연결하는 방법을 결정해놓고, 통합해서 관리할 수 있는 대행 시스템을 구축해놓았습니다. 해당 PG사의 가이드에 맞추어 결제 시스템을 개발하면 여러개의 카드 회사들에 맞추어 여러번의 개발 작업을 진행할 필요가 없어집니다.

일반적으로 PG사의 개발 가이드는 수백페이지 가량의 볼륨을 가진 pdf 파일로 전달되며,
asp, php, jsp등의 언어로 작성되어 있습니다.
이 가이드를 활용하여 필요한 기능을 구현하면 되지만 이 작업도 상당한 시간이 소요되는 작업입니다.


3. 결제 솔루션
PG사의 가이드에 맞추어 결제 시스템을 구축하는 작업을 대신 해주는 결제 솔루션 업체도 등장했습니다.

PG사마다 다른 개발 가이드를 가지고 있다 보니 한 업체와 거래하는 시스템을 구축한 뒤에는 다른 업체로 옮기기가 어렵습니다. 이러한 문제점을 보완해주는 업체가 바로 결제 솔루션(결제 API) 업체입니다.

결제 솔루션 업체를 이용하면 개발환경과 상관없이 원하는 PG사와의 결제시스템을 연결시킬 수 있습니다.
대표적인 결제 솔루션 업체는 아임포트, 부트페이 등이 있습니다. 중견, 중소 규모 기업의 경우 PG사와 직접 계약하는 것 보다 결제 솔루션을 이용하는 것이 효율적입니다.
❗️ 아임포트 현재 원포트로 이름 바뀜!!

포인트 충전 과정

원포트(아임포트)를 이용하는경우, 사용자가 결제를 통해 포인트를 충전하는 과정은 다음과 같습니다.

  1. 사용자가 브라우저에서 충전하기 버튼을 클릭
  2. 충전하기 창에서 원하는 금액을 선택하고 결제 버튼 클릭
  3. 아임포트에서 제공하는 Rest-API를 이용해 결제 요청
  4. 결제 성공 시 해당 결제에 대한 imp_uid와 결제 금액 등의 데이터를 돌려받음
  5. 성공한 결제 정보를 백엔드 서버로 mutation
  6. db의 유저 포인트 정보에 결제 내역을 업데이트

위와 같은 결제 과정 중 아임포트는 다음과 같은 역할을 해줍니다.

❗️창업시 결제 승인 기간은 어느 정도로 잡아야 할까요?
최소한 한 달은 잡아야 합니다.
아임포트를 사용한다고 해도 각각의 PG사, 카드사에게 승인 및 심사받는 과정을 동일하게 거쳐야 하기 때문입니다.

PG사 승인 1주일 + 카드사 심사 최소 2주일 = 총 3주일이 기본이고,
승인/심사 여부에 따라 기한이 계속 늘어날 수 있습니다.

외부 API(원포트) 사용하기

원포트 둘러보기, 초기설정

1. 회원가입, 로그인 => 어드민 페이지로!

2. 결제연동에서 내 식별코드 확인하기

3. 테스트 연동 해보기


카카오페이로 해봤습니다!!

4. 콘솔가이드로 들어와서 왼쪽 바 인증결제 연동하기 메뉴 확인

5. 포트원 라이브러리 추가!!

<script src="https://cdn.iamport.kr/v1/iamport.js"></script>

Next.js에서는 HTML에 직접 접근하기가 어렵기때문에 Head태그를 임포트해서 사용합니다.

import Head from 'next/head';
// 최상단에 next/head 의 Head 태그 호출

<Head>
	<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js" ></script>
	<script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.1.5.js"></script>
</Head>

6. 객체 초기화하기

  • 결제 요청하기 (코드는 Docs참고)
    pg사 코드표 확인하고 pg: "kakaopay"에 넣어주기.
declare const window: typeof globalThis & {
  IMP: any;
}; 

export default function PaymentPage(): JSX.Element {
  const onClickPayment = (): void => {
    // window.IMP;
    const IMP = window.IMP; // 생략 가능
    IMP.init("imp46081246"); // 예: imp00000000a

    IMP.request_pay(
      {
        // param
        pg: "kakaopay",
        pay_method: "card",
        // merchant_uid: "ORD20180131-0000011",
        name: "마우스",
        amount: 100,
        buyer_email: "gildong@gmail.com",
        buyer_name: "홍길동",
        buyer_tel: "010-4242-4242",
        buyer_addr: "서울특별시 강남구 신사동",
        buyer_postcode: "01181",
        m_redirect_url: "http://localhost:3000/section28/28-01-payment",
      },
      (rsp: any) => {
        // callback
        if (rsp.success === true) {
          // 결제 성공 시 로직,
          console.log(rsp);

          // 백엔드에 결제관련 더이터 넘겨주기. 즉 무테이션 실행!
          // createPoinTranscationOfLoading
        } else {
          // 결제 실패 시 로직,
        }
      }
    );
  };

  return (
    <>
      {/* 아임포트 라이브러리 */}
      <script src="https://cdn.iamport.kr/v1/iamport.js"></script>
      <button onClick={onClickPayment}>결제하기</button>
    </>
  );
}

결제 파라미터에서 pg 나 pay_method 부분에서는 어떤 PG사를 이용할 건지, 어떤 결제 방식을 사용할 건지 선택이 가능하고 name과 amount 에는 주문 이름과 결제 금액, buyer에는 구매자에 대한 정보를 추가로 넣어줄 수 있습니다.

🚨 모바일 화면으로 접속한 상태에서 아임포트 결제를 시도해보면, 결제 화면으로 넘어가며 아예 페이지의 url이 바뀐 것을 확인할 수 있습니다.

그렇기 때문에 반응형 웹 페이지를 작업할 때에는 모바일에서 결제가 완료된 후 돌아올 url까지 연결을 해줘야 합니다. m_redirect_url이라는 파라미터를 이용해서 돌아올 url을 지정할 수 있습니다.

m_redirect_url: "http://localhost:3000/section28/28-01-payment" // 모바일 결제시 돌아갈 url

하지만 m_redirect_url을 이용해서 페이지를 이동할 경우, 해당 결제 성공시 실행하도록 설정해놓은 함수를 잃어버리게 됩니다. 이 때에는 웹훅노티피케이션을 이용해 결제의 결과값을 백엔드로 전달해줄 수 있습니다.

7. 결과화면 - 모바일과 웹 모두 다 잘 작동한다!! 결제도 해봤고 결제는 100원 미만은 안됨!

테스트 결제는 자정이전에 취소됩니다.

결제가 성공하면, 아임포트로부터 해당 결제에 대한 rsp(response)를 받아올 수 있습니다.
rsp에는 해당 결제에 대한 imp_uid와 결제 금액 등의 정보가 담겨 있습니다. 이 rsp를 이용하여 포인트 충전 API에 결제 정보를 넘겨줄 수 있습니다.

8. 결제내역

관리자 페이지의 상세내역조회 에서 확인할 수 있으며 imp_로 시작되는 고유한 결제ID값도 확인할 수 있습니다. 결제가 성공한 내역에 한해서 수동 취소도 가능합니다. 테스트로 잘못된 금액이 지불되었다면 꼭 취소하기 버튼으로 환불을 진행합시다!

9. 백엔드 API로 데이터 넘겨주기

결제에 성공하면 원포트로부터 해당 결제에 대한 rsp를 받아올 수 있다고 했습니다.
rsp에는 해당 결제에 대한 imp_uid와 결제 성공 여부, 결제 금액 등의 정보가 담겨 있으며
rsp를 이용하여 포인트 충전 API에 결제 정보를 넘겨줄 수 있습니다. 결제 성공 시, rsp에는 다음과 같은 정보가 담겨 들어옵니다. 그 중 필요한 요소들을 활용하여 백엔드로 mutation을 요청하면 DB에 있는 User 정보에 결제 내역을 반영할 수 있습니다.

웹훅노티피케이션

원포트에서 제공하는 웹훅노티피케이션에 대해 알아봅시다.
가상계좌를 이용한 무통장 입금은 어떻게 구현할까요? 아래는 원포트를 이용한 무통장 입금이 진행되는 과정입니다.

  1. 사용자가 무통장 입금을 선택
  2. 아임포트에서 제공하는 API를 이용해 가상 계좌 생성
  3. 결제 중이던 웹브라우저를 실행 종료하고, 가상 계좌를 발급한 은행 서비스에 접속
  4. 가상 계좌 유효기간 내에 입금 완료

아임포트를 연동하면 프론트단에 결제하는 화면을 띄울 수 있습니다. 여기서 결제를 하게되면, 결제ID(imp_uid)라는 데이터를 받게끔 되어있었죠.

이 imp_uid를 Backend에 전송하여 데이터베이스에서 결제 정보를 관리할 수 있도록 만들었습니다.
그런데, 무통장입금(가상계좌)을 하거나 모바일에서 결제를 하는 경우에는 현재 페이지를 잃어버릴 수 있으므로 imp_uid를 받을 수 없었죠.

그래서 필요한 것이 아임포트 웹 훅 노티피케이션 이었습니다. 여기에 Backend-API를 rest방식으로 하나 만들고 API 주소를 입력해 놓으면,

아임포트에서 결제가 완료/취소되었을 때, 무통장입금에 성공했을 때 등의 상황에서 아임포트가 Backend-API로 결제내역을 보내줍니다.
이를 받은 api가 결제 데이터를 정리해서 데이터베이스에 저장하도록 만들어 놓으면 되겠죠?!(백엔드 개발자의 역할)

웹훅노티피케이션이란 특정 이벤트가 발생했을 때 타 서비스나 응용 프로그램으로 알림(Notification)을 보내는 기능입니다. 웹훅노티피케이션을 활용하면 다음과 같은 기능을 구현할 수 있습니다.

  1. 가상계좌를 이용한 무통장 입금
  2. 결제 취소
  3. 예약 결제
  4. 환불
    이 외에도 원포트에서는 영수증, 정기 예약 결제등 다양한 기능을 가진 Rest-API를 제공합니다.
    자세한건 Docs 참고!!!

시간관련 이벤트

결제를 완료해서 백엔드로 createPointTransaction 등의 API 요청을 보낼 때 결제가 이루어진 시점의 시간을 db에 함께 저장하게 됩니다.

이 때, 이 시간을 새로 생성하는 작업절대 프론트엔드 서버에서 하시면 안됩니다.
사용자가 PC의 시각을 실제와 다르게 조작해서 사용하는 경우, 프론트엔드 서버에서 생성한 시간 역시 조작된 시각을 따라가기 때문입니다.

그렇기 때문에 시간을 생성하는 작업은 반드시 백엔드에서 이루어져야 합니다. 또한, 이 시간은 통상적으로 국제 표준시인 UTC 시간을 이용합니다.

UTC 시간이란 협정 세계시(Coordinated Universal Time/Universal Time Coordinated, UTC)로, 국제 표준시로 사용되고 있습니다.

Moment.js 라이브러리

이러한 시간 관련 처리를 도와주는 라이브러리도 있습니다. Moment.js는 높은 주간 다운로드 수를 가진 라이브러리로, 잘 이용하면 시간과 관련 있는 작업을 매우 간편하게 처리할 수 있습니다.
moment.js

이외에도 Moment.js가 제공하는 다양한 시간 관련 기능이 있습니다.

정기결제 크론탭 이해

사이트에서 이벤트 할인상품을 판매한다고 가정해볼 때, 자정이 지나면 이벤트를 마감하고 더이상 이벤트 가격으로 상품을 구매할 수 없도록 막아야합니다.

이것을 구현하는 가장 간단한 방법은 담당자가 대기하고있다가 시간에 맞춰서 이벤트 마감 버튼을 누르는 것입니다. 하지만 이 경우 이벤트 종료 시간까지 담당자가 대기하고 있어야 하는 데다가, 실수로 이벤트 종료 시간보다 이른 시간이나 늦은 시간에 버튼을 누르게 될 가능성이 있습니다.
이경우 시간관련 이벤트를 발생시켜 주면 됩니다!!

이벤트를 발생시키는 방법

이벤트를 발생시키는 방법에는 크게 두 가지가 있습니다.

  1. 클릭, 내용 입력 등 직접 이벤트 발생
  2. 특정 시간에 자동으로 이벤트 발생

2번과 같은 작업을 해주는 기능을 크론탭(Crontab)이라고 합니다. 크론탭을 이용하면 관리자가 정해진 시간까지 대기하거나, 그 시간에 클릭 등의 이벤트를 직접 발생시킬 필요가 없어집니다. 리눅스 기반 OS에는 크론탭이 기본으로 깔려있습니다.

이벤트 루프 (Event Loop)

시간을 화면에 보여줄 때 발생할 수 있는 이슈

싸이월드때 했던 방식을 응용해서 3분짜리 타이머를 만들어봅시다.

// 싸이월드때 했던 내용 복습
// setInterval(() => {
//   document.getElementById("timer")?.innerText = "2:59";
// }, 1000);

export default function TastQueuePage(){

	const onClickTimer = () => {

		console.log("=======시작~~=======")

		setTimeout(() => {
			console.log("1초 뒤에 실행된답니다 😎️")
		}, 1000)

		console.log("=======끝~~=======")
	}

	return <button onClick={onClickTimer}>시작~~</button>;
}

그리고 실행된 서버에서 시작버튼을 누르면 콘솔에 이렇게 찍힙니다.

코드의 순서대러라면 1초 뒤에 실행된다는 setTimeout이 먼저 실행되어야 하는데 순서가 뒤집혀있습니다.
0초로 변경한 뒤에도 콘솔이 찍히는 순서에는 변화가 없습니다. 이것은 자바스크립트의 동작 원리 때문입니다!

Task Queue


위와 같은 코드를 프론트엔드 서버에서 실행하면, 콜스택에 다음과 같이 Stack이 쌓입니다.
실행순서는 아래와 같습니다.

  1. callStack에서 onClickTimer 함수가 실행됩니다. (Stack - Last In First Out / LIFO 구조)
  2. Background에 setTimeout()을 보내서 실행합니다.
  3. setTimeout()이 TaskQueue로 전달되어 쌓입니다. (Queue - First In First Out / FIFO 구조)
  4. TaskQueue에 쌓이는 함수는 CallStack이 다 비워진 다음 가장 마지막에 실행됩니다.

위와 같은 순서에 따라 onClickTimer 함수 내에서 setTimeout이 가장 마지막에 실행되는 것입니다.
여기서 TaskQueue에 있는 함수를 CallStack으로 보내는 역할을 하는 일꾼이 있는데
그것을 스레드(Thread)라고 합니다.

싱글 스레드

자바 스크립트는 싱글 스레드 방식을 가지고 있습니다.

조금 더 구체적으로 표현하자면 싱글 이벤트 루프 스레드라고도 합니다.
핵심은, CallStack이 비어야만 TaskQueue에 있는 작업을 CallStack으로 가져온다는 것입니다.

CallStack이 작업 중이라면 setTimeout에 설정한 1초가 이미 지났다고 하더라도 TaskQueue 안의 작업이 CallStack으로 들어오지 못합니다. 다음과 같은 코드를 입력하고 yarn dev 해봅시다.

export default function TastQueuePage(){

	const onClickTimer = () => {

		console.log("=======시작!!!!=======")

		setTimeout(() => {
			console.log("1초 뒤에 실행될 거예요!!!")
		}, 1000)

		for(let i=0; i<=9000000000; i+=1){
			sum = sum + 1
		}

		console.log("=======끝!!!!=======")
	}

	return <button onClick={onClickTimer}>시작!!!</button>;
}

프로세스와 스레드

프로세스와 스레드는 각각 다음과 같이 정의됩니다.

프로세스: 실행되어있는 프로그램

스레드: 프로세스 안에서 동작하는 일꾼

스레드가 하나인 언어를 싱글 스레드 언어, 스레드가 여러개인 언어를 멀티 스레드 언어라고 합니다.

자바스크립트는 싱글 스레드 언어입니다.
멀티 스레드 언어에는 자바, 파이썬 등이 있습니다.

그렇다면 이런 의문이 들 수 있습니다.
일하는 스레드가 여러개라고 하면 멀티 스레드가 싱글 스레드보다 좋은 것 아닌가요?
결론부터 말하자면 반드시 그런 것은 아닙니다.

멀티 스레드 언어의 경우 동시에 여러가지 작업을 처리하고 있는 것처럼 보이지만,
사실은 여러 개의 스레드가 여러가지 작업을 번갈아가며 빠르게 수행하고 있는 것과 같습니다.

멀티 스레드의 경우에도 하나의 요청에 대한 응답을 기다렸다가 다음 작업으로 이동해야 하는 것은 동일합니다. (이러한 작업을 컨텍스트 스위칭이라고 합니다.)
그렇기 때문에 멀티 스레드라고 해서 싱글 스레드보다 두드러지게 빠르지는 않습니다.

오히려 자바스크립트와 같은 이벤트 루프 싱글 스레드의 경우
오래 걸리는 작업을 TaskQueue로 빼서 처리하기 때문에 높은 퍼포먼스를 낼 수 있습니다.

profile
Slow and steady wins the race.

0개의 댓글

관련 채용 정보