우리가 인터넷 서비스에서 결제를 통해 포인트를 충전하는 과정은
일반적으로 다음과 같습니다.
여기서, 결제 프로세스를 개발하는 방법에도 역사가 있습니다.
각각의 카드사에게 승인을 받아 결제 시스템을 구축하는 방법입니다.
신한카드, 하나카드, 농협카드 등등 현존하는 다양한 카드 회사들은 지원하는 결제 방법이 전부 다릅니다.
그렇기 때문에 모든 경우를 고려하여 결제 시스템을 개발하려면 인건비와 시간이 많이 소요됩니다. 실제 비즈니스 환경에서는 이러한 방식으로 개발하는 것이 불가능하다고 볼 수 있습니다.
각각의 카드사와 연결하는 작업을 대신해주는 PG사를 통해 결제 시스템을 구축하는 방법입니다.
대표적인 PG사에는 NHN, 나이스페이, KG이니시스 등이 있습니다.
PG사는 카드사들과 미리 의논해서 결제를 연결하는 방법을 결정해놓고, 통합해서 관리할 수 있는 대행 시스템을 구축해놓았습니다.
해당 PG사의 가이드에 맞추어 결제 시스템을 개발하면 여러개의 카드 회사들에 맞추어 여러번의 개발 작업을 진행할 필요가 없어집니다.
일반적으로 PG사의 개발 가이드는 수백페이지 가량의 볼륨을 가진 pdf 파일로 전달되며,
asp, php, jsp등의 언어로 작성되어 있습니다.
이 가이드를 활용하여 필요한 기능을 구현하면 되지만 이 작업도 상당한 시간이 소요되는 작업입니다.
PG사의 가이드에 맞추어 결제 시스템을 구축하는 작업을 대신 해주는 결제 솔루션 업체도 등장했습니다.
PG사마다 다른 개발 가이드를 가지고 있다 보니 한 업체와 거래하는 시스템을 구축한 뒤에는 다른 업체로 옮기기가 어렵습니다. 이러한 문제점을 보완해주는 업체가 바로 결제 솔루션(결제 API) 업체입니다.
결제 솔루션 업체를 이용하면 개발환경과 상관없이 원하는 PG사와의 결제시스템을 연결시킬 수 있습니다.
대표적인 결제 솔루션 업체는 아임포트, 부트페이 등이 있습니다.
중견, 중소 규모 기업의 경우 PG사와 직접 계약하는 것 보다 결제 솔루션을 이용하는 것이 효율적입니다.
아임포트(I’mport)를 이용하는 경우,
사용자가 결제를 통해 포인트를 충전하는 과정은 다음과 같습니다.
위와 같은 결제 과정 중 아임포트는 다음과 같은 역할을 해줍니다.
💡 창업시 결제 승인 기간은 어느 정도로 잡아야 할까요?
최소한 한 달은 잡아야 합니다.
아임포트를 사용한다고 해도 각각의 PG사, 카드사에게 승인 및 심사받는 과정을
동일하게 거쳐야 하기 때문입니다.
PG사 승인 1주일 + 카드사 심사 최소 2주일 = 총 3주일이 기본이고,
승인/심사 여부에 따라 기한이 계속 늘어날 수 있습니다.
아임포트를 사용하기 위해서는 아임포트 아이디가 필요합니다.
아임포트 홈페이지로 접속해주세요.
페이지 접속 후, 시작하기 버튼을 눌러서 회원가입 페이지로 들어갑니다.
계정이 없을 경우에는 회원 가입을 진행해주시고,
이미 가입 한 적이 있을 경우에는 로그인을 진행해주세요.
아임포트에 로그인을 하면 관리자 페이지에 접속할 수 있게 됩니다.
관리자 콘솔을 클릭하면 나오는 포트 어드민 콘솔에서 한 번 더 로그인을 해줍니다.
관리자 페이지에서는
아임포트를 사용하기 위한 기본적인 설정부터
아임포트가 연결시켜준 PG사의 정보와 실제 결제내역까지 확인할 수 있습니다.
아임포트에서 제공해주는 Rest-API들의 가이드문서 역시 관리자 콘솔에서 확인하실 수 있습니다.
아임포트를 사용하기 위해서는
먼저 사용할 PG사를 선택해야 합니다.
관리자 페이지에서 시스템 설정 - PG설정 (일반결제 및 정기결제)
페이지로 이동합니다.
PG 설정 페이지에서는 결제에 필요한 PG사를 설정할 수 있습니다.
PG사 선택 창을 클릭하면 여러종류의 PG사들이 나타납니다.
그 중 KG이니시스(웹표준결제창)
을 선택해봅시다.
💡 위의 화면과 달리 기본 PG사가 없으시다면 왼쪽 목록에 있는 PG사 추가 버튼으로 추가하셔야 합니다.
PG설정으로 PG사를 설정할 경우
앞으로 이뤄지는 모든 결제 과정에서는
해당 PG사로 연결되어 결제가 이루어지게 됩니다.
💡 반드시,
테스트모드
에 체크가 되어 있는지 확인해주세요.
테스트모드가 아닌 경우에는 실제로 결제가 이루어집니다.
(KG이니시스 PG사 테스트의 경우 결제를 하면 계좌에서 금액이 출금되지만 당일 자정 이전에 모두 결제가 취소되어 환급됩니다)
가상 계좌를 이용한 무통장 입금은 어떻게 구현할 수 있을까요?
아임포트를 이용한 무통장 입금이 진행되는 과정을 살펴봅시다.
하지만 이 단계에서 문제가 발생합니다.
브라우저에서 결제가 이루어지는 것이 아니기 때문에, 결제 완료 시점에 결제된 금액과 imp_uid를 받아서 백엔드로 보내주는 것이 불가능합니다.
이러한 작업을 위해 아임포트는 웹훅노티피케이션(Webhook notification)
을 제공합니다.
웹훅노티피케이션이란 특정 이벤트가 발생했을 때 타 서비스나 응용 프로그램으로 알림(Notification)을 보내는 기능입니다. 웹훅노티피케이션을 활용하면 다음과 같은 기능을 구현할 수 있습니다.
📖 자세한 사항은 아임포트 DOCS 페이지의 Webhook 부분을 참고해주세요.
https://docs.iamport.kr/tech/webhook
💡 이 외에도 아임포트에서는 영수증, 정기 예약 결제등 다양한 기능을 가진 Rest-API를 제공합니다. 자세한 사항이 궁금하다면 Rest-API 가이드를 참고해주세요.
https://api.iamport.kr/
📖 이 내용은 아임포트 DOCS 페이지와 Github에도 잘 설명되어 있습니다.
해당 페이지를 참고해보세요!
Docs: https://docs.iamport.kr/implementation/payment
Github: https://github.com/iamport
아임포트를 프로젝트 내에서 직접 사용하기 위해서는
아임포트 라이브러리를 head
부분에 추가해줘야 합니다.
HTML의 경우 직접 head 태그 안에 script 태그를 추가할 수 있지만
Next.js에서는 HTML에 직접 접근하기가 어렵기 때문에
Next.js에서 제공하는 Head 태그를 import해서 이용합니다.
아임포트 라이브러리를 추가하기 위한 script 태그는 앞서 import한 Head 태그로 감싸서
결제 기능을 사용할 페이지의 return 안에 추가해주시면 됩니다.
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>
라이브러리를 추가했다면
아임포트를 프로젝트에서 사용하기 위해서
사용하려는 아임포트의 가맹점 식별코드를 설정
해줘야 합니다.
그리고 window.IMP 으로 아임포트를 불러와서 제어할 수 있습니다.
const IMP = window.IMP;
IMP.init('가맹점 식별코드');
가맹점 식별코드는
아임포트 관리자 페이지의 시스템 설정 - 내 정보 - 가맹점 식별코드
에서 확인할 수 있습니다.
가맹점 식별코드에 있는 식별코드를
window.IMP.init 에 넣어 설정합니다.
식별 코드까지 설정이 완료됐다면
이제 아임 포트의 결제창을 불러올 수 있습니다.
IMP.request_pay({
pg: "html5_inicis",
pay_method: "card",
//merchant_uid: "결제 번호",
name: "아이리버 무선 마우스 외 1개",
amount: 10000,
buyer_email: "이메일@gmail.com",
buyer_name: "홍길동",
buyer_tel: "010-4242-4242",
buyer_addr: "서울특별시 강남구 신사동",
buyer_postcode: "01181",
m_redirect_url : "/"
}, function (rsp) { // callback
if (rsp.success) {
alert('결제가 성공했습니다.');
// 결제 성공 로직
} else {
alert('결제에 실패했습니다.');
// 결제 실패 로직
}
});
위의 코드를 IMP.init(””)으로 식별 코드를 설정했던 페이지 안에 새로 추가해줍니다.
이 코드를 추가하면, 아임포트 관리자 페이지에서 설정해놓은 PG사의 결제 페이지를 프로젝트에서 사용할 수 있게 됩니다.
결제가 성공하느냐 실패하느냐에 따라서 실행되는 로직을 만들어줄 수도 있습니다.
결제 파라미터에서 pg 나 pay_method 부분에서는
어떤 PG사를 이용할 건지, 어떤 결제 방식을 사용할 건지 선택
이 가능하고
name과 amount 에는 주문 이름과 결제 금액
,
buyer에는 구매자에 대한 정보
를 추가로 넣어줄 수 있습니다.
📖 결제 파라미터의 정보는 아래의 DOCS 페이지를 참고해주세요.
https://docs.iamport.kr/tech/imp
모두 적용이 됐다면 프로젝트에서는 위와 같이
프로젝트에 설정된 PG사의 결제 페이지가 오픈 됩니다.
결제 페이지에서는 실제로 결제가 가능
하며
PG사마다 상이할 수 있지만 테스트 버전이라면 기본적으로
당일 자정 전에는 모두 취소되어 환불됩니다.
위의 그림과 같이,
결제 과정까지 성공했다면 결제에 성공했을 때 설정했던 로직이 실행이 되고
반대로 실패했을 때에는 결제 실패에 설정했던 로직을 실행시킬 수 있습니다.
🔔 프로세스에 문제가 없는데 결제에 실패하셨다면, 결제 금액을 한번 확인해보세요.
100원 미만으로는 결제를 할 수 없습니다!
결제가 성공하면, 아임포트로부터 해당 결제에 대한 rsp(response)를 받아올 수 있습니다.
rsp에는 해당 결제에 대한 imp_uid와 결제 금액 등의 정보가 담겨 있습니다.
이 rsp를 이용하여 포인트 충전 API에 결제 정보를 넘겨줄 수 있습니다.
아임포트의 관리자 페이지의 결제승인내역
페이지에서
현재까지의 결제 내역 (실패 및 성공) 들을 확인할 수 있습니다.
결제내역 페이지에서는 결제된 내역들에 대한 정보를 볼 수 있고
'imp_'
로 시작되는 고유한 결제 ID 값도 확인할 수 있습니다.
결제가 성공한 내역에 한해서 수동 취소도 가능합니다.
테스트로 잘못된 금액이 지불되었다면 꼭 취소하기 버튼으로 환불을 진행해주세요.
우리는 앞서, 결제에 성공하면 아임포트로부터 해당 결제에 대한 rsp를 받아올 수 있다고 했습니다.
rsp에는 해당 결제에 대한 imp_uid와 결제 성공 여부, 결제 금액 등의 정보가 담겨 있으며
rsp를 이용하여 포인트 충전 API에 결제 정보를 넘겨줄 수 있습니다.
결제 성공 시, rsp에는 다음과 같은 정보가 담겨 들어옵니다.
그 중 필요한 요소들을 활용하여 백엔드로 mutation을 요청하면
DB에 있는 User 정보에 결제 내역을 반영할 수 있습니다.
모바일에서 결제시에는 화면이 조금 달라집니다.
구글 크롬 브라우저의 개발자 도구를 이용해서 확인해볼까요?
개발자 도구에서 좌측 상단의 표시된 버튼을 누르면 모바일 화면을 확인할 수 있습니다.
개발자 도구로 모바일 화면에서 지금까지 실습한 url에 접속해볼까요?
모바일 화면으로 접속한 상태에서 아임포트 결제를 시도해보면, 결제 화면으로 넘어가며 아예 페이지의 url이 바뀐 것을 확인할 수 있습니다.
그렇기 때문에 반응형 웹 페이지를 작업할 때에는 모바일에서 결제가 완료된 후 돌아올 url까지 연결을 해줘야 합니다.
m_redirect_url이라는 파라미터를 이용해서 돌아올 url을 지정할 수 있습니다.
하지만 m_redirect_url을 이용해서 페이지를 이동할 경우,
해당 결제 성공시 실행하도록 설정해놓은 함수를 잃어버리게 됩니다.
이 때에는 웹훅노티피케이션
을 이용해 결제의 결과값을 백엔드로 전달해줄 수 있습니다.
📖 모바일 결제 정보는 아래 DOCS 페이지를 참고해주세요.
https://docs.iamport.kr/implementation/payment#mobile-web
결제를 완료해서 백엔드로 createPointTransaction 등의 API 요청을 보낼 때
결제가 이루어진 시점의 시간을 db에 함께 저장하게 됩니다.
이 때, 이 시간을 새로 생성하는 작업
은 절대 프론트엔드 서버에서 하면 안됩니다.
사용자가 PC의 시각을 실제와 다르게 조작해서 사용하는 경우, 프론트엔드 서버에서 생성한 시간 역시 조작된 시각을 따라가기 때문입니다.
그렇기 때문에 시간을 생성하는 작업은 반드시 백엔드에서 이루어져야 합니다.
또한, 이 시간은 통상적으로 국제 표준시인 UTC 시간을 이용합니다.
📖 UTC 시간이란 협정 세계시(Coordinated Universal Time/Universal Time Coordinated, UTC)로, 국제 표준시로 사용되고 있습니다.
이러한 시간 관련 처리를 도와주는 라이브러리도 있습니다.
Moment.js는 높은 주간 다운로드 수를 가진 라이브러리로,
잘 이용하면 시간과 관련 있는 작업을 매우 간편하게 처리할 수 있습니다.
이외에도 Moment.js가 제공하는 다양한 시간 관련 기능이 있습니다.
자세한 사항은 공식 문서를 참고해주세요.
사이트에서 이벤트 할인 상품을 판매한다고 가정해볼까요?
자정이 지나면 이벤트를 마감하고 이벤트 가격에 더 이상 제품을 구매할 수 없도록 막아야 합니다.
이것을 구현하는 가장 간단한 방법은 관리자 페이지에 이벤트 마감 버튼을 만들어두는 것입니다. 이벤트 마감 버튼을 누르면 더 이상 이벤트 상품을 구매할 수 없도록 막는 것이죠.
하지만 이 경우 이벤트 종료 시간까지 담당자가 대기하고 있어야 하는 데다가, 실수로 이벤트 종료 시간보다 이른 시간이나 늦은 시간에 버튼을 누르게 될 가능성이 있습니다.
어떻게 하면 정확하고 편리하게 이러한 시간 관련 제어를 할 수 있을까요?
이런 경우 시간 관련하여 이벤트를 발생 시켜주면 됩니다.
이벤트를 발생시키는 방법에는 크게 두 가지가 있습니다.
2번과 같은 작업을 해주는 기능을 크론탭(Crontab)
이라고 합니다.
크론탭을 이용하면 관리자가 정해진 시간까지 대기하거나,
그 시간에 클릭 등의 이벤트를 직접 발생시킬 필요가 없어집니다.
💡 리눅스 기반 OS에는 크론탭이 기본으로 깔려있습니다.
일단 3분짜리 타이머를 만들어볼까요?
const TastQueuePage(){
const onClickTimer = () => {
console.log("=======시작~~=======")
setTimeout(() => {
console.log("1초 뒤에 실행된답니다 😎️")
}, 1000)
console.log("=======끝~~=======")
}
return <button onClick={onClickTimer}>시작~~</button>;
}
export default TastQueuePage
그리고 실행된 서버에서 시작 버튼을 누르면 콘솔에 다음과 같이 찍히는 것을 확인할 수 있습니다.
코드 순서대로 라면 함수의 끝 부분의 ‘끝’ 콘솔보다 setTimeout 안에 있는 콘솔이 먼저 실행되어야 할 것 같은데, 순서가 뒤집혀있습니다.
검증을 위해 setTimeout을 0초로 바꾸어 다시 한 번 실행해봅시다.
const TastQueuePage(){
const onClickTimer = () => {
console.log("=======시작~~=======")
setTimeout(() => {
console.log("0초 뒤에 실행된답니다 😎️")
}, 0)
console.log("=======끝~~=======")
}
return <button onClick={onClickTimer}>시작</button>;
}
export default TastQueuePage
0초로 변경한 뒤에도 콘솔이 찍히는 순서에는 변화가 없음을 알 수 있습니다.
왜 그런 걸까요?
바로 자바스크립트의 동작 원리
때문입니다.
우리는 이전에 CallStack
에 대해 배웠습니다.
const TastQueuePage = () => {
const onClickTimer = () => {
console.log("=======시작~~=======")
setTimeout(() => {
console.log("1초 뒤에 실행된답니다 😎️")
}, 1000)
console.log("=======끝~~=======")
}
return <button onClick={onClickTimer}>시작</button>;
}
export default TastQueuePage
위와 같은 코드를 프론트엔드 서버에서 실행하면, 콜스택에 다음과 같이 Stack이 쌓입니다.
실행 순서는 다음과 같습니다.
위와 같은 순서에 따라 onClickTimer 함수 내에서 setTimeout이 가장 마지막에 실행되는 것입니다.
여기서 TaskQueue에 있는 함수를 CallStack으로 보내는 역할을 하는 일꾼이 있는데
그것을 스레드(Thread)
라고 합니다.
자바 스크립트는 싱글 스레드
방식을 가지고 있습니다.
조금 더 구체적으로 표현하자면 싱글 이벤트 루프 스레드
라고도 합니다.
핵심은, CallStack이 비어야만 TaskQueue에 있는 작업을 CallStack으로 가져온다는 것입니다.
CallStack이 작업 중이라면 setTimeout에 설정한 1초가 이미 지났다고 하더라도 TaskQueue 안의 작업이 CallStack으로 들어오지 못합니다.
다음과 같은 코드를 입력하고 yarn dev 해봅시다.
const TastQueuePage = () => {
const onClickTimer = () => {
console.log("=======시작!!!!=======")
setTimeout(() => {
console.log("1000초 뒤에 실행될 거예요!!!")
}, 1000)
for(let i=0; i<=9000000000; i+=1){
sum = sum + 1
}
console.log("=======끝!!!!=======")
}
return <button onClick={onClickTimer}>시작!!!</button>;
}
export default TastQueuePage
페이지가 그려진 뒤 콘솔을 열고 시작 버튼을 눌러봅시다.
클릭 후 1초가 훨씬 지났는데도 ‘끝’ 콘솔이 뜨지 않는 것을 확인하료 수 있습니다.
setTimeout이 TaskQueue에 들어가서 대기하고 있지만, 반복문이 종료되어 CallStack이 비워지지 않으면 TaskQueue에 있는 setTimeout은 실행되지 못하기 때문입니다.
setInterval, setTimeout처럼 CallStack에 쌓이지 않고 Background, TaskQueue로 넘겨지는 작업을 비동기 작업
이라고 합니다.
axios도 비동기 처리가 이루어지는 대표적인 라이브러리 중 하나 입니다.
스레드가 하나인 언어를 싱글 스레드 언어, 스레드가 여러개인 언어를 멀티 스레드 언어라고 합니다.
💡 자바스크립트는 싱글 스레드 언어입니다.
멀티 스레드 언어에는 자바, 파이썬 등이 있습니다.
그렇다면 이런 의문이 들 수 있습니다.
❓ 일하는 스레드가 여러개라고 하면 멀티 스레드가 싱글 스레드보다 좋은 것 아닌가요?
결론부터 말하자면 반드시 그런 것은 아닙니다.
멀티 스레드 언어의 경우 동시에 여러가지 작업을 처리하고 있는 것처럼 보이지만,
사실은 여러 개의 스레드가 여러가지 작업을 번갈아가며 빠르게 수행하고 있는 것과 같습니다.
멀티 스레드의 경우에도 하나의 요청에 대한 응답을 기다렸다가 다음 작업으로 이동해야 하는 것은 동일합니다. (이러한 작업을 컨텍스트 스위칭이라고 합니다.)
그렇기 때문에 멀티 스레드라고 해서 싱글 스레드보다 두드러지게 빠르지는 않습니다.
오히려 자바스크립트와 같은 이벤트 루프 싱글 스레드
의 경우
오래 걸리는 작업을 TaskQueue로 빼서 처리하기 때문에 높은 퍼포먼스를 낼 수 있습니다.
위에서 설명한 멀티 스레드 예시처럼
하나의 요청에 대한 응답이 와야만 다음 작업을 시작할 수 있는 방식을
블로킹(blocking) 방식
이라고 합니다.
그리고 이벤트 루프 싱글 스레드처럼
하나의 작업이 진행되는 동안 시간이 오래 걸리는 작업은 따로 마련한 공간에 던져
실행하는 방식을 논블로킹(non-blocking) 방식
이라고 합니다.
💡 그렇다면 자바스크립트의 특징은 무엇인가요?
싱글 이벤트 루프 스레드 / 논블로킹 방식입니다.