일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- API
- 레일즈
- 노마드코더
- 재태크
- redis
- 사업
- HTTP
- 투자
- Watcha pedia
- redis transaction
- 경제
- django
- memcached
- CU
- iamport
- 아임포트
- trouble shooting
- 노개북
- Race Condition
- Python
- 레일즈 캐시
- 주식
- 노마드코드
- 북클럽
- Cache
- transaction
- Rails
- rails cache
- restful
- Today
- Total
Stay hungry, Stay foolish
결제 개발 하기 (Redis를 이용한 race condition 대응 까지) 본문
Iamport 를 활용한 결제 시스템 개발 과정을 이야기해보려 합니다. Iamport 적용 방법, 매뉴얼 등은 공식 document 에 상세히 나와있습니다 :)
저희 서비스는 순수 B2B 만을 통해서 수익을 창출하고 있었습니다. 하지만 오랜 기간 서비스를 진행하며 제품에 대한 검증은 어느 정도 마쳤으니 B2C로 확대를 해보자!라는 게 올해의 목표였죠. 올해에만 현재까지 총 4명의.. 개발팀 퇴사를 겪으며.... 여름이 다가올 무렵 B2C 신규 프로젝트가 시작되었습니다.
일단 제품 판매를 하려면 결제가 되어야 하는데... Ruby on Rails 언어 지원이 안되는 PG 사들이 많아 시간이 오래 걸릴 수 도 있겠다 싶었습니다.
그런데 찾아보니 2년전 MVP 단계에서 간단하게 구현된 결제 모듈이 존재하더군요..?
PG사 결제연동 설루션을 제공하는 Iamport 를 통해 Rest API 로 비인증 결제를 지원하는 방식입니다.
일단 Iamport 에 대해 찾아보니 이미 다양한 고객사도 있고, RoR 로 구현된 서비스들에서도 사용하는 사례들이 보입니다. PG 사 마다 개발 공수를 들일 필요가 없어 보여 현재 상황에서 Iamport를 활용하는 것은 합리적인 선택이었습니다.
별다른 추가 보안 처리 없이 현재 코드 구현 내용을 설명드리며 그대로 사용해도 되는지 Iamport측에 문의해보니
위의 보안 처리만 확실히 되었다면 사용해도 된다고 합니다 😆 하지만 3번의 필수는 아닌 보안요소가 걸리긴 합니다.
일단은 해당 rest api를 통한 결제를 유지하고 MVP 출시 후에 토스페이먼츠나 나이스 페이먼츠의 인증결제로 변경하기로 결정했습니다.
그러면 이제 간편결제 도입이 남았습니다! 👏
개발 필요 목록을 정리하고 개발 프로세스를 정리해봅니다.
초기 설계에서 구현을 진행하며 살을 붙여 완성된 프로세스입니다.
아임 포트 공식문서에 워낙 꼼꼼히 쓰여 있어서 그대로만 구현하면 될 줄 알았습니다.. 🥲
구현 과정
먼저 결제의 State는 waiting, paid, canceled 로 관리됩니다.
1. 유저가 결제 페이지에 접근하면 서버에서는 waiting 상태의 결제 데이터를 하나 생성합니다. 그리고 해당 데이터에 추후 PG사에 결제를 요청하고 검증을 진행할 uniq id를 저장해줍니다.
2.a [웹] Iamport sdk 를 통해 간편 페이 모듈(토스페이 or 카카오페이) 을 호출합니다.
여기서부터는 모바일인지 웹인지에 따라 결제가 다른 노선으로 진행됩니다. 웹에서는 iamport sdk 가 모듈을 호출하고 결제 완료를 누르면 결제 결과를 받아 구현해놓은 Rails 서버의 검증 -> 결제 함수를 동작합니다. 검증에 이상 없고 결제 state 변경이 완료되면 결제가 완료됩니다.
2.b [모바일] 웹에서는 잇다의 페이지가 유지된 상태로 모달 창이 뜨고 콜백에 의해서 결제가 진행되는데 모바일은 잇다 페이지를 아예 벗어납니다.
그리고 결제가 완료되면 sdk에 미리 지정해놓은 url 로 redirect 되는 방법입니다. 그러면 그 과정에서 저희는 저장해놓은 데이터와 결제 결과 데이터의 검증, 결제 state 업데이트 등이 이루어져야 합니다.
카카오페이 결제창 -> 브릿지페이지 -> 최종 결제완료 페이지의 단계로 구성해서 간편 페이 결제 완료 후 브릿지 페이지에 redirect 시키고 해당 페이지에서 검증, 결제 state 변경 처리를 진행합니다.
결제 중 브릿지 페이지입니다. 나름 미적 감각 없는 저로써 만들고 귀엽게 잘 만들었다고 뿌듯했답니다 😽
해당 페이지에서 기존에 구현해 놓은 검증, 결제 state 변경 모듈이 실행됩니다.
정상적으로 결제가 완료되면 웹과 동일하게 결제 완료 페이지로 redirect 시킵니다.
이로써 결제가 끝입니다! 🙌🙌🙌 와 결제 구현 엄청 간단하네 라고 잠시 생각했습니다...
문제가 될 수 있는 경우가 생각났습니다.
해당 브릿지 페이지로 이동하기 전에 데이터가 끊긴다거나 창을 꺼버린다던지 해서 PG 사 결제는 진행이 됐지만 저희 서버에서는 결제정보가 업데이트되지 않을 수 있는 상황이 생기게 됩니다.
이를 위해 Iamport 측에서 웹훅 설정을 안내하고 있습니다. 웹훅은 Iamport 측에 결제가 완료되면 저희 쪽으로 결제 결과를 담아서 request를 날려주는 기능입니다. 쉽게 생각해서 우리가 핸드폰 문자가 왔는지 5분마다 확인하는게 아닌 문자가 오면 알람을 통해 알려주는 방식이라고 생각하면 됩니다.
그러면 웹훅만 적용하면 해결!!? 아닙니다 😿
위 프로세스 다이어그램에서 웹훅 통신인 3번과 저희쪽 검증, 결제 모듈이 작동하는 4번 의 과정은 거의 동시에 일어납니다. 순서에 보장이 없습니다. 웹훅 컨트롤러에서도 동일하게 저희 db 자원을 통해 검증, 결제 데이터 업데이트를 해줘야 하고 기존 컨트롤러에서도 해당 과정이 동일하게 이뤄지는데 이렇게 되면
앱 서버 3개를 통해 서비스가 운영되고 있는 와중에 어느 서버로 3번 과정이 진행되고 4번 요청이 들어올지 순서에 대한 보장이 이뤄지지 않습니다.
동일한 자원에 서로 다른 프로세스가 접근을 해서 자원을 업데이트시키는 일에 대한 대응이 필요합니다.
이를 CTO님과 논의 후 Redis를 통해 해당 자원에 Lock을 걸어 대응하기로 결정했습니다.
살펴보니 redis에서는 trasaction을 보장해주는 multi 라는 커맨드가 존재합니다.
코드를 간단하게 살펴보자면
( 참고로 저희 redis gem 은 4.6 버전입니다, 항상 기술 적용 전 버전 업데이트 간 변경된 내용들도 확인합시다! )
=> 위와 같이 multi 커맨드로 해당 블럭에서의 트랜잭션을 보장받고 블럭에서의 실행 결과를 array 로 돌려받습니다.
이를 이용해 아래와 같이 블럭 안에서 redis에서 unique 키값을 조회하고 세팅하는 함수를 작성합니다
def check_redis(u_id, value)
redis.multi do |transaction|
transaction.get(u_id)
transaction.set(u_id, value)
end
end
웹훅 컨트롤러나 결제 컨트롤러의 첫 단계에서 unless check_redis(u_id).first.present? 인지 체크 후 나머지 로직을 실행해 줍니다.
레디스는 기본적으로 single thread 이기 때문에 해당 트랜젝션이 보장받을 수 있습니다.
그러므로 웹훅 컨트롤러와 기존 결제 컨트롤러에서 동시에 해당 check_redis 에 요청을 한다 해도, 아래 그림처럼
먼저 요청이 들어온 쪽에서 첫 번째 인자를 무조건 nil로, 두번째 요청의 첫번째 인자는 위의 trasaction block에서 설정한 value값이 return 됩니다.
이로써 결제를 진행하며 발생할 수 있는 Race condition 까지 해결했습니다!
사실 해당 결제 개발은 B2C를 위한 개발의 시작으로 추후 티켓의 개념 등 제품의 설계가 변하며 더욱 결제 기능이 확장되긴 했습니다 😭
이번 개발을 하면서 느낀 것은 설계 단계에서 어느 정도 예외사항까지 고려를 해야 할지 결제에서 trasaction 은 어떻게 보장받아야 할지 경쟁상태는 어떻게 처리해야 할지 주니어로써 많은걸 고민해볼 수 있었던 좋은 기회였던 것 같아 너무 즐거웠습니다 😽
Reference
- Avoid race condition in Rails
- redis-rb
'개발 이야기' 카테고리의 다른 글
팀원들을 행복하게 만들어주는 과정은 생각보다 간단할 수도 있습니다 (0) | 2022.05.06 |
---|---|
S3 Image 업로드를 구현하며 깨닫게 된 것들 (0) | 2021.08.21 |
숨고(Soomgo) 웹 사이트 클론 프로젝트가 끝나고 (0) | 2021.08.01 |