이 글은 프론트엔드 개발자가 결제 시스템을 전혀 도입한 적이 없는 스타트업에서 결제 개발을 시작해야하는 분들에게 도움이 되길 바라는 마음으로 작성한 글입니다.
3년 반 가까이 회사에 다니면서 제일 스트레스를 많이 받았던 구독 결제 시스템 개발.
회사에서 구독 결제 시스템 개발을 도입한 적이 없었기 때문에 팀원 모두가 고생을 많이 했던 프로젝트였는데, 고생한만큼 배운 점들도 많아서 구독 결제 시스템을 개발하는 분들이 알고 있으면 좋을 내용들을 정리해봤습니다.
참고로 프로젝트에 참여한 개발팀 구성은 다음과 같습니다. 저희 팀 모두 결제 시스템 개발 경험은 없었습니다.
- 기획자 1명
- 디자이너 1명
- 프론트엔드 개발자 1명 (저에용!🙋🏻♀️)
- 백엔드 개발자 1명
PG사 정하기
결제 시스템 개발을 위해 PG사를 먼저 결정해야 합니다.
* PG사란? 결제를 대신 처리해주는 역할을 하는 회사
참고 - https://www.payple.kr/cs/article/1/
대표적인 PG사로는 다날(링크), 토스페이먼츠(링크), NHN KCP(링크), KG이니시스(링크)가 있어요.
그럼 이 중에서 좋아보이는 거 아무거나 선택해서 하면 되는 거 아냐? 라고 생각할수도 있을텐데요.
PG사에서 신청한 회사에 대한 심사를 거친 후 사용할 수 있게 하기 때문에 회사 업종에 따라 사용 불가능한 PG사가 있습니다.
각 PG사별 서비스 불가 업종에 대한 정보는 아래 링크를 참고하시면 됩니다.
PG 심사 통과를 위한 필수 확인사항_imweb (링크) - '체크1: 온라인에서 판매가 가능한 업종인가요' 질문 확인하기
* PG사 심사 신청은 누가했나요? 저희 회사는 기획자와 경영팀에서 준비해서 서류 제출 및 신청을 했어요 :)
만약에 우리 회사가 불가 업종에 포함된다면 '페이플(링크)'이라는 업체를 이용하시면 됩니다.
여담으로 제가 개발할 당시에는 페이플 개발 문서가 노션으로 작성되어있어서 보기가 좀 불편했었는데 리뉴얼된 개발 문서는 훨씬 보기 편해졌더라구요. 대형 PG사에 비해 규모도 작고 토스페이먼츠 처럼 별도의 개발자 커뮤니티는 없어서 아쉽지만 개발하는 데 큰 불편은 없었습니다.
PG사 연동 개발이 처음에 환경을 구축하는데는 시간이 좀 걸리지만 모듈만 잘 만들어두면 크게 유지보수할 일이 거의 없더라구요.
참고로, 개발자는 사용하는 PG사가 어디까지 화면을 제공해주는지 알고 있어야 합니다.
그 이유는 PG사에서 제공해주는 화면까지 디자이너가 디자인을 하지 않도록 하기 위함입니다. (저희의 일정은 소중하니까요!)
일반적으로는 카드 등록화면이나 결제창을 다 제공해주기 때문에 아래 첨부한 이미지와 같은 화면은 별도의 디자인이 필요가 없는데, 화면 기획서만 보고 회사 분위기에 맞춰서 직접 디자인을 진행하고 계실 때가 있어서 이런 부분은 기획자와 디자이너에게 미리 말씀해주셔야 합니다.


개발 일정 산정하기
PG사가 정해지고 나면 개발을 진행할 수 있게 돼요. 화면 기획을 기반으로 디자이너와 개발자가 각각 개발 일정을 산정합니다.
저희가 해야할 일은 결제, 환불, 구매취소, 무료 체험, 이용권 리스트 화면 개발, 구독 여부에 따라 서비스 이용 화면 분기 처리하기, 마이페이지에 구독 결제 메뉴 추가 등등의 업무가 있었습니다.
디자이너는 2주, 저(프론트엔드)와 백엔드 개발자는 한 달을 산정했어요. 일단 반응형 웹이라서 시간이 오래 걸리기도 했고, 저희 둘 다 경험이 전혀 없기 때문에 어떤 변수가 생길지 몰라서 한 달로 정했는데 QA기간 포함해서 개발하다보니 사실 이 기간도 넉넉하지는 않았습니다...ㅠㅠ
그래서 한 가지 팁을 드리자면 개발 일정 산정하기 전에 1~2일 정도 팀원들과 화면 설계서를 토대로 wireflow를 그려보고 빠진 부분이나 불필요한 부분이 있는지 꼼꼼히 살피는 시간을 충분히 가진 후 개발 일정을 산정하시길 바랍니다. wireflow에 대한 얘기는 아래 '커뮤니케이션 잘하기' 파트에 따로 정리해두었습니다.
커뮤니케이션과 개발
커뮤니케이션 잘하기 (feat. 와이어 플로우_wireflow)
저는 구독 결제 개발에서 '개발'보다 중요한 게 '커뮤니케이션'이라고 생각합니다. 특히 구독 결제 개발 경험이 없다면 더욱 중요합니다.
사실 저희 팀은 그동안 개발해온 화면들이 단순해서 커뮤니케이션 비용이 크게 들지 않았습니다. 가끔씩 UX가 어색하거나, 기획서가 대략적인 형태로 나와서 회의를 좀 더 하는 경우는 있더라도 크게 문제가 될 만한 부분들은 없었습니다.
그런데 구독 결제 개발은 이전과는 전혀 달랐습니다.
기획서와 확정된 디자인 시안을 전달받았지만 유저 플로우나 상황별로 보여질 데이터, 정책 등이 명확하게 정해져있지 않아서 결제 개발 일정의 50%는 논의하는 시간에 할애되었습니다.
환불 신청 완료 후 다음 화면은 어떤게 보여지나요?
구독권 환불을 한 후에는 멤버십 정보 페이지에서 어떤 화면을 보여주나요?
구독 해지 신청 후에는 멤버십 정보를 보는 화면이 구독중인 상태와 달라져야 할 것 같은데 어떤 데이터를 뺄까요?
이와 같은 질문들이 개발팀에서 끊임없이 나왔습니다. 이 외에도 비즈니스 로직을 작성하는데 참고해야하는 결제 및 환불 정책이 모호하게 정해진 부분에 대해서 한참을 회의하기도 했어요.
또한 각 팀원들끼리 개별적으로 문의해서 진행된 내용들이 팀원들간에 공유가 되지않아서 했던 얘기를 또 하는 시간들이 반복되기도 했습니다.
이런 일들이 계속 발생하다보니 기획자, 디자이너, 개발자 모두가 굴레 속에서 끊임없이 일을 하는 느낌을 받았습니다.
사실 개발을 시작하기 전에 프로젝트의 큰 흐름을 읽으며 미리 빠진 내용이 있는지 정리하고 진행했어야 했는데, 이전에 복잡하지 않은 개발 작업을 할 때처럼 기획서와 디자인을 전달받고 바로 개발했던 방식대로 진행하려다가 발생한 문제였습니다.
와이어 플로우를 만들자!
'아, 더이상 이대로는 안되겠다!' 라고 생각한 저는 Wireflow를 만들게 되었습니다. 참고로 개발자인 제가 주도적으로 wireflow를 만든 이유는 이걸 만들어 본 사람이 저밖에 없었기 때문입니다.
Wireflow는 Wireframe과 User flow를 합친 말로, 와이어프레임이나 완성된 UI 화면 등으로 사용자가 제품을 수행하는 과정을 문서화하는 작업이라고 생각하면 됩니다. 아래 이미지가 Wireflow의 한 예시인데, 피그마에서 Flow chart 플러그인(링크) 를 설치해서 만들 수 있어요.

저는 이렇게 한눈에 흐름을 파악할 수 있는 문서가 있어야 같은 방향을 바라보며 프로젝트를 원활하게 진행할 수 있을 거라고 생각했습니다.
그렇게 꼬박 하루를 걸려 완성한 문서에 대한 팀원들 반응은 예상대로 좋았습니다. 다들 이제서야 전체적인 흐름이 이해된다는 피드백과 어떤 부분이 현재 부족한지 바로 보이니 회의도 효율적으로 진행할 수 있었습니다. 실제로 그동안 크고 작은 회의들을 주 평균 10회 정도 진행했었는데 3회로 줄어들어드는 효과가 있었습니다.
그리고 여담으로 개발이 끝난 후 마케팅팀에서도 마케팅 전략을 짤 때 해당 문서 덕분에 잘 이해했다는 피드백도 전달받았습니다 :)
이 글에서는 wireflow를 작성하는 방법에 대해서는 자세히 다루지 않고, 별도의 글을 추후에 작성할 예정입니다.
요점은 결제 시스템처럼 복잡한 화면 개발을 할 때 wireflow는 꼭 만드시는 걸 추천드립니다.
누가 만들어도 상관없어요. 물론 이 모든 걸 기획하는 기획자가 만드는 게 제일 베스트겠지만, 상대적으로 여유가 있는 사람이 만들면 됩니다.
사실 첫 시작은 자발적으로 했지만, '기획이랑 관련된 거니까 기획자가 해야하는 일 아닌가?' 라고 생각했었습니다. 근데 나중에 계속 개발을 하다보니 이 wireflow 작업은 지금 프로젝트에 참여하고 있는 사람이라면, 이 서비스를 잘 이해하고 있어야하니까 누구라도 해야하고 할 수 있는 일이라는 생각이 들더라구요. 그래서 언젠가 부터는 다들 자연스럽게 이 문서가 필요할 때 자발적으로 만드는 문화가 개발팀 내에서 생겼습니다.
개발하기
디자이너가 화면 디자인을 진행하는 동안 저희 개발자들은 PG사 데모와 예제를 열심히 테스트해서 결제할 수 있는 환경을 구축해놓습니다. 환경 구축이 완료되었다면, 완성된 디자인을 기반으로 퍼블리싱을 하고 api 연동을 해줍니다.
개발 과정을 얘기하기에 앞서 구독 결제 시스템 개발에서 해야할 일들을 한 번 간단하게 정리해보겠습니다.
- 결제 과정 개발
- 콘텐츠 접근 제한 개발
- 구독권 관리 페이지 개발
a. 현재 구독중인 상품보는 화면 개발
b. 환불 과정 개발
c. 구독 해지 개발
d. 구독권 변경 개발 - 결제 내역 개발
- 결제 카드 변경 개발
- 결제 메일 퍼블리싱 작업
제공하는 서비스에 따라 더 추가되는 것들이 있겠지만, 일단 결제 시스템 개발에서 필수적으로 개발해야하는 부분을 우선적으로 개발해야하는 순서로 정리해보았습니다.
그럼 이제 차근차근 하나씩 개발 과정을 살펴보겠습니다.
1. 결제 과정 개발
사실 저는 처음에 결제 과정을 개발할 때, 어디에서 결제를 요청해야하는지, 중복 결제는 어떻게 처리해야하는지 등이 참 헷갈렸습니다.
그래서 제 경험을 바탕으로 그림과 함께 각 과정마다 어떤 api를 요청하고 결과를 나타냈는지 정리해보았습니다.
다양한 결제 로직 방식 중 하나로 봐주시면 감사하겠습니다 :)

1. 먼저 구독권 구매를 위해 "구매하기" 버튼을 클릭합니다. 이 때, 결제 상세 정보로 넘어가기 전에 /orders api에 내가 구매하는 상품 ID 데이터를 전달해서 주문서를 만듭니다.
그리고 서버 응답에서 302 상태 코드 및 header로 { Location: /4325/checkout } 을 전달받아서 주문서 페이지로 리다이렉션합니다.
이렇게 하는 이유는 추후에 4325 라는 주문서가 중복으로 주문되는 걸 막기 위함입니다.
* 주문 번호 생성은 꼭 서버에서 해야하나요?
서버에서 하는 것을 권장합니다. 프론트엔드에서는 생성한 주문 번호가 중복인지 확인이 어렵기 때문에 주문 번호 중복 체크하는 api를 만들어서 미리 확인하거나, DB에 최종적으로 저장하기 전에 중복 체크를 해야하는 과정을 거쳐야 합니다.
이 과정을 거쳐서 프론트엔드에서 만든 주문 번호와 서버에서 만든 주문 번호가 달라지면 중복 주문 체크도 어려워지게 됩니다.
구독 서비스 같은 경우에는 주문번호와 상관없이 유저 한명이 같은 구독권을 중복으로 구매할 수 없기 때문에 주문번호를 프론트엔드에서 임시 주문 번호 생성을 하고, 서버에서 유니크한 주문 번호를 새로 생성해도 상관없습니다. 서버에서는 주문 번호 없이 유저의 구독권 유무 만으로 체크가 가능합니다. (물론 구독 서비스마다 다를 수 있습니다)
그러나 쇼핑몰이나 배달의 경우에는 다릅니다.
온라인 쇼핑몰 이용이 서툰 사용자가 이미 결제를 완료한 상태에서 뒤로가기를 눌렀는데 프론트엔드에서 새로운 주문 번호를 생성해서 이 사용자가 새로 주문을 하려는 건지, 실수인건지 프론트엔드와 서버는 알 수가 없습니다.
이 때 사용자는 실수로 결제 버튼 까지 눌렀다면 어떻게 될까요? 중복으로 주문이 들어가게 될 겁니다.
그렇기 때문에 서버에서 주문 번호를 생성하는 방식이 사이드 이펙트를 줄이고 불필요한 리소스 낭비를 방지할 수 있을 겁니다.
혹여나 서버에 주문서만 생성해놓고, 결제를 진행하지 않는 사람이 많다면 리소스 낭비가 될 수 있다고 생각할 수도 있습니다.
하지만 이 문제는 주문서 생성 후 특정 시간 내에 결제하지 않은 주문건은 삭제 처리하면 되기 때문에 문제가 되지 않는 다고 생각합니다.
2. 결제 상세 정보 페이지로 이동했다면, 4325라는 주문 ID를 가진 주문서를 서버로 요청해서 화면에 데이터를 보여줍니다. 결제를 진행하기위해 "결제하기" 버튼을 클릭합니다.
만약에 이미 orderId가 4325인 주문서가 이미 결제된 상태라면 서버에서 결제가 완료된 상태값을 전달해주기 때문에 결제 상세 정보 화면 대신에 "이미 결제가 완료된 구독권입니다"를 보여주면 됩니다.
3. "결제하기"버튼을 클릭해서 서버에 구독 결제를 요청합니다. 보통 카드 결제를 진행하면 3~5초 정도 시간이 소요되기 때문에 "결제 진행 중입니다"이라는 화면이 보여져야 합니다. 저는 모달로 구현을 했는데 결제하기와 같은 다른 버튼들을 클릭할 수 없도록 구현해야합니다.

결제가 완료되면 서버 응답에서 302 상태 코드 및 header로 { Location: /4325/result } 을 전달받아서 구독 결제 완료 페이지로 리다이렉션합니다.
4. 드디어 구독 결제가 완료되었습니다! 서버에 구독 결제 정보 데이터를 요청해서 보여줍니다.
만약 이 페이지에서 뒤로가기를 눌러 바로 직전 페이지인 /4325/checkout로 이동했다면, 2번에서 새로고침한 결과와 동일하게 "이미 결제가 완료된 구독권입니다"라고 보여질겁니다.
제가 설명한 내용 중에 디자인 패턴이 하나 적용되어있는데요, 바로 PRG 패턴이라는 것입니다 :)
상품 결제 개발을 시작하는 개발자들이 알고 있으면 좋은 패턴인데요,
PRG 패턴은 Post-Redirect-Get의 줄임말로 사용자가 중복으로 데이터를 제출하는 것을 방지하는 패턴입니다.
POST는 멱등하지 않기 때문에 중복 제출이 발생할 수 있죠. 그래서 사용자가 같은 요청을 2번 이상 보내는 것을 막기 위한 패턴이라고 볼 수 있습니다.
의미 설명에서 느끼셨을 수도 있겠지만, 결제 개발을 할 때 중복으로 결제가 될 수 있는 부분은 미리 사전에 방지를 꼭 해줘야합니다.
그럼 중복으로 데이터가 제출 될 수도 있는 경우가 언제 있을까요?
/order/4325 페이지에서 POST로 결제 요청을 하는 페이지가 있다고 예를 들어볼게요.
이 페이지에서 최초에는 "주문 완료했습니다"라고 보여졌지만, 새로고침하면 다시 POST로 결제 요청이 가게 되고 개발자가 이미 결제된 주문번호라는 것을 확인해서 "잘못된 주문 번호 입니다", "이미 결제가 완료되었습니다"라는 문구를 반환해주게 될 겁니다. 이 때 사용자 경험 측면에서 좋지 않겠죠.

이런 문제를 방지하기 위해 결제가 완료되고 나면 결제 완료 결과 페이지로 이동시키는 PRG 패턴을 적용하게 됩니다.
PRG 패턴 적용 방법은 POST 요청이 성공적으로 완료되었을 때, GET으로 결제 완료 페이지를 리다이렉트해주면 됩니다. 그럼 사용자가 실수로 완료 화면에서 새로고침을 하더라도 "이미 결제가 완료되었습니다"가 아닌 구독 결제 완료 화면을 계속 보게 될거고, 중복 결제 요청도 진행되지 않겠죠.
물론 PRG 패턴을 적용했다고해서 백엔드나 프론트 측에서 이미 결제가 완료되었다는 화면을 처리하지 않아도 된다는 의미는 아닙니다.
뒤로가기 혹은 의도적으로 다시 POST로 중복 요청이 갈 수 있기 때문에 중복 결제는 반드시 막아줘야합니다.

만약에 그럼에도 계속 이해가 가지 않는다면 네이버 쇼핑, 쿠팡, 오늘의 집 등 다양한 웹 사이트에서 직접 결제해보며 제가 작성한 로직을 다시 확인해본다면 이해가 될겁니다 :)
대부분 결제를 다루는 웹 사이트가 이러한 방식을 채택하고 있습니다.
이 부분의 개발을 끝냈다면, 제일 중요한 부분을 완료했기 때문에 사실 50% 개발했다고 봐도 무방하다고 생각합니다. 물꼬를 틀고 제일 큰 산 하나를 넘은 거죠.
2. 콘텐츠 접근 제한 개발
콘텐츠 접근 제한을 개발할 때 보통 아래 4가지 방법 중 하나를 선택하여 개발하게 됩니다.
- 하드 페이월 : 무료 컨텐츠를 제공하지 않고, 유료 구독을 해야만 콘텐츠 전체를 열람할 수 있는 방식
ex. 타임지 - 소프트 / 미터제 페이월 : 미리보기 형태로 콘텐츠 일부를 보여주고 구독을 유도하거나, 무료로 볼 수 있는 컨텐츠 개수를 정해둔 후 초과하면 유료 구독을 요구하는 방식
ex. 네이버 프리미엄 - 프리미엄 페이월 : 일반적인 컨텐츠는 무료로 제공하는데, 프리미엄 콘텐츠인 경우에는 유료로 제공하는 방식
ex. 유튜브 채널별 멤버십 - 타임월 : 콘텐츠를 일정 시간 동안 무료로 제공하고 이후에 유료화 하는 방식
ex. 네이버 웹툰
저는 1, 2, 3번 방식을 적용한 경험이 있는데, 컨텐츠 노출 방식에 따라 프론트엔드 개발자가 고려해야할 부분은 각기 다릅니다.
- 하드 페이월, 프리미엄 페이월, 타임월 : SEO
- 소프트 / 미터제 페이월: 자연스럽게 컨텐츠가 잘려 보이게 하기
사실 SEO는 어떤 컨텐츠를 다루든 간에 필수적으로 신경써야하는 부분이지만,
컨텐츠 노출이 전혀 없는 하드 페이월이나 프리미엄 페이월, 타임월에도 검색엔진에 노출되도록 신경을 써야한다는 부분이 특별히 다르다고 볼 수 있을 것 같습니다.
예를 들어, 타임지는 하드 페이월이 적용된 상태인데 컨텐츠 내용을 전혀 볼 수 없지만 검색엔진에서는 노출이 되는 모습을 볼 수 있습니다.
혹시나 사용자가 관련 내용을 검색하면 유입이 되도록 하는 거죠.


그렇기 때문에 컨텐츠 내용이 없더라도 <meta name="description"> 등을 활용해서 검색엔진에 노출이 되게 해서 지속적으로 사용자 유입이 발생할 수 있도록 해야하는 게 프론트엔드에서 해야할 역할입니다.
이 때 보여질 컨텐츠 내용은 기획자와 논의해서 정하면 됩니다. 첫 문단 혹은 요약한 내용을 노출하는 방식 등으로 말이죠.
참고로, 어떤 내용을 표현할지 정해지면 백엔드에서 미리 선처리하여 컨텐츠를 자른 후, 해당 데이터를 프론트에서 그대로 보여주면 됩니다.
* 컨텐츠를 <div style="display:none">으로 해도 검색엔진 노출이 되나요?
네, 됩니다! 실제로 시도해봤고, 검색엔진에 노출이 되는 것을 확인했었습니다.
구글봇은 '어떤 CSS 속성을 적용했나'보나, dom과 내용을 확인하는 것으로 보여집니다.
다만, SEO 성능적인 측면에서 이야기가 분분하므로 안전하게 <meta name="description">를 사용하는 것을 추천드립니다.

자연스럽게 컨텐츠가 잘려서 보이는 방식에 대해서는 구현 전에 2가지 사항을 먼저 생각해야합니다.
- a. 사용자가 따로 설정하지 않아도 자동으로 페이월이 적용되는 방식
- b. 사용자가 직접 수동으로 페이월을 적용하는 방식
a. 사용자가 따로 설정하지 않아도 자동으로 페이월이 적용되는 방식
첫번째 방식은 무료로 컨텐츠를 제공하다가 유료화로 변경되었을 때 생각해야하는 부분입니다.
기존에 무료로 발행된 글이 1000개가 있는데 여기에 수동으로 글마다 페이월을 적용할 수 없기 때문에 적절한 비율을 지정해서 노출되게 합니다. 적절한 비율은 주관적이라서 딱 어느 정도라고 말하기 어렵지만, 발행된 글 중에서 가장 짧은 글과 가장 긴 글로 테스트해보며 정하시면 됩니다.
이 때, 에디터로 작성된 글이라면 대게 <div style="color:#000000">개발은 재밌어</div> 와 같은 형식으로 되어있을텐데요.
노출 비율을 정할 때 html 태그까지 포함된 글자수를 기준으로 할 것인지, html 태그 개수를 기준으로 할 것인지 고민하게 됩니다.
저희 같은 경우에는 html 태그까지 포함된 노출 비율을 지정해서 데이터를 가공했습니다.
그 이유는 일부 작성자의 경우 Enter 없이 길게 글을 작성하는 사람이 있어서 태그 개수 기준으로 했을 때 상당히 많은 양의 글을 노출해야하는 문제가 있었습니다. 작성자마다 제각기 다른 형태로 글을 작성했기 때문에 예외 상황을 예측하기가 어려웠습니다.
그래서 간단하게 html 태그까지 포함된 글자수를 기준으로 노출 비율을 정했습니다.
이렇게 글자수로 끊게 되면 <div style="color:#000000>개발은 와 같이 미완성된 태그로 끝나서 오류가 발생할 수 있다고 생각하실 수도 있는데, 브라우저에서는 미완성된 태그일지라도 알아서 완성된 태그의 형태로 만들어서 표현해주기 때문에 괜찮습니다.
다만 글 내용이 매끄럽게 끝나지 않기 때문에 기획자, 디자이너와 얘기해서 블러처리를 글이 끝나는 지점보다 더 높은 상태에 적용이 되도록 사전에 미리 얘기를 해두면 좋습니다.
b. 사용자가 직접 수동으로 페이월을 적용하는 방식


에디터에 페이월 기능을 삽입해서 사용자가 직접 원하는 곳에 페이월을 삽입하는 기능이며, 대부분의 유료 컨텐츠 사이트에서 적용하는 방식이라고 보시면 될 것 같습니다. TinyMCE나 CKEditor를 사용한다면 커스텀 플러그인을 만들어서 추가해주시면 됩니다.
앞서 말씀드렸던 글자수를 기준으로 내용을 끊는 부분은 아무래도 매끄럽지 않게 내용이 끊기는 단점이 있는데, 수동으로 적용하는 방식은 문장과 문장 사이에 적용을 하다보니 자연스럽게 글이 끊긴다는 장점이 있습니다.
참고로 저희 팀이 페이월을 구현했던 방식을 소개하며, 콘텐츠 접근 제한 개발 내용을 마무리하겠습니다.
크게 어렵지 않기 때문에 쉽게 구현 가능합니다.
1. 유료 게시선 혹은 페이월을 이미지로 만듭니다. 그리고 이 이미지에 'paywall'이라는 class를 지정해줍니다.
그러면 이 이미지의 태그는 <img class="paywall" src="..." />와 같은 형식으로 구성되어있을 겁니다.

2. 사용하는 에디터 툴바에 페이월을 추가하는 버튼을 생성해줍니다. TinyMCE 같은 경우에는 해당 예제(링크)를 참고해서 만들어주시면 됩니다. 이 때 정규식을 통해 이미 페이월이 추가되어있으면 중복으로 추가되지 않도록 해야합니다.
3. 이렇게 만들어진 기능을 통해 사용자가 원하는 영역에 페이월을 추가하게 되면, 최종적으로 받는 컨텐츠 데이터는 이런 형태일 겁니다.
<p>다음에 소개할 내용은 개발자 이야기 입니다.</p>
<img class="paywall" src="..." />
<p>개발자는 늘 공부를 해야하는 사람입니다.</p>
이 데이터를 서버에서 구독 여부에 따라 가공하여 프론트로 전달하게 됩니다.
img 태그 체크 방법은 정규식을 사용하거나, <img class="paywall" src="..." /> 태그 통째로 일치하는 게 있는지 체크한 후 아래와 같이 가공합니다.
- 무료 사용자: <img class="paywall" src="..." /> 부분부터 데이터를 잘라서 전달
- 유료 사용자: <img class="paywall" src="..." /> 부분만 제거한 데이터를 전달
4. 프론트에서는 전달받은 데이터로 다음과 같이 컨텐츠 페이지를 구현합니다.
- 무료 사용자: 퍼블리와 같이 구독 유도 버튼 화면 구현
- 유료 사용자: 온전한 글 공개
3. 구독권 관리 페이지 개발
구독권 관리 페이지 개발에서는 크게 아래 4가지를 중점으로 개발하게 됩니다.
a. 현재 구독중인 상품보는 화면 개발
b. 환불 과정 개발
c. 구독 해지 개발
d. 구독권 변경 개발
a. 현재 구독중인 상품보는 화면 개발
기능을 구현하기보다는 데이터를 화면에 뿌려주기만 하면 되는 부분이라서 개발하면서 크게 어렵거나 신경써야하는 부분은 없었습니다.
기획을 어떻게 하느냐에 따라서 화면 구성이 달라지기 때문에 대표적인 예시를 들기는 어렵지만 여러 구독 사이트를 확인했을 때 공통된 부분만 가볍게 정리하고 넘어가겠습니다.
- 기본적으로 보여줘야하는 데이터: 결제 상품, 결제 수단, 결제 예정일, 결제 예정 금액, 이용 기간, 이용권 관련 안내 사항
- 추가되어야 하는 기능: 구독 갱신 결제를 실패한 경우에 실패 사유 보여주기 (실패했을 때 재결제 횟수와 결제 시간대 정하기)
- 추가되어야 하는 버튼: 이용권 해지, 이용권 변경
b. 환불 과정 개발
환불 과정을 개발할 때 신경써서 개발해야할 점은 환불 금액 계산입니다.
환불은 중요한 부분이기 때문에 정책이 다소 늦게 정해지면, 개발 마무리 단계에 작업을 할 수도 있습니다.
저희 같은 경우에는 경영팀, 기획팀, 개발팀이 다같이 환불 정책에 대해서 논의를 했습니다.
일단 7일 이내에 사용한 이력이 없는 경우 전체 환불이 되는 것에 대해서는 이견이 없었으나, 중도 해지시 환불 금액을 어떻게 계산해야할지 한참 논의한 기억이 있습니다.
저희는 일단 다양한 구독 플랫폼을 직접 사용해보면서 어떤 식으로 계산하는지 체크를 했고, 2가지 방식을 추렸습니다.
- 일정 기간 이후 부터는 구독권이 만료될 때까지 환불 불가
- 환불 시점을 기준으로 일할 계산해서 환급
구현하는 입장에서나, 회사 입장을 생각했을 때 이득이 되는 것은 당연히 1번입니다. 하지만 사용자 입장에서 생각했을 때 좋은 경험은 아니죠.
저희는 잠깐의 이득보다는 사용자 입장을 고려하여 2번으로 선택해서 작업을 진행했습니다.
환불 금액 계산은 서버에서 진행을 했고, /checkout이라는 api를 통해 환불 금액을 전달받아서 보여주는 방식으로 구현했었습니다.
서버에서 환불 금액을 전달받는 이유는 다음과 같습니다.
- 프론트에서 환불 금액을 계산할 경우, 비즈니스 로직 노출 우려
- 프론트에서 환불 금액 계산해서 서버로 전달할 때, 악의적인 사용자가 환불 금액 데이터를 변조하여 서버로 요청할 가능성 有
- ex. 데이터 위변조 사례
스팀(Steam) - 데이터 위변조를 통해 무제한으로 금액을 등록한 사례 (https://www.bitdefender.com/en-us/blog/hotforsecurity/valve-patches-steam-wallet-flaw-that-allowed-unlimited-addition-of-fake-funds) - 환불을 진행할 때 필요한 각종 데이터(ex. 사용 일수, 쿠폰 사용 여부 등등)를 서버로 요청해야 할 수 있는데, 비효율적으로 서버와 프론트가 서로 작업을 진행해야하는 문제
위와 같은 이유들로 결국 서버에서 한 번 더 검증과정을 거쳐야하기 때문에, 처음부터 서버에서 환불 금액을 전달받는 게 효율적입니다.
최종적으로 사용자가 환불 버튼을 누르고 나면 서버에서 처리하는 시간이 다소 걸렸기 때문에, 그 사이에 중복으로 연달아 버튼 클릭을 할 수 없게 하는 작업도 함께 진행했습니다.
b. 구독 해지 개발
구독 해지시 사용자가 혼란을 느끼지 않도록 어떤 데이터를 보여주고 안 보여줄지 정하기만 하면, 프론트 입장에서 구독 해지 개발 자체는 어려운 점은 없었습니다.
- 구독 해지 버튼 클릭시: 결제 상품과 이용 기간을 제외한 데이터를 숨김처리하고, 구독 해지 취소 버튼 보여주기
- 구독 해지 취소 버튼 클릭시: 구독 해지하면서 숨김 처리했던 데이터를 다시 보여주고, 구독 취소 버튼 보여주기
d. 구독권 변경 개발
구독권 변경이 사실 개발할 때 제일 많이 헷갈리고 까다로웠던 부분입니다.
사실 변경 개발 자체가 어려웠다기 보다는 변경 과정에서 발생하는 부분 환불 정책이 정해지는 과정이 쉽지 않았던 것으로 기억합니다.
정책이 확실히 정해지지 못하고 바뀌던 순간들이 있었거든요.
저희 팀이 겪었던 경우에 대해서 간단하게 정리한다면 다음과 같습니다. (이용권 이름은 실제 사용된 이름과 다릅니다.)
- 베이직 이용권 무료 체험 ➡️ 프리미엄 이용권으로 업그레이드
- 프리미엄 이용권 무료 체험 ➡️ 베이직 이용권으로 다운그레이드
- 베이직 이용권 ➡️ 프리미엄 이용권 업그레이드
- 프리미엄 이용권 ➡️ 베이직 이용권 다운그레이드
보기에는 4가지 밖에 없어서 간단해보이지만 깊게 들어가면,
무료 체험 이용권을 변경할 때 남은 기간만큼 업그레이드를 시킬지, 아니면 바로 결제 후 이용하게 할지
기존에 할인된 이용권을 구매했을 때, 업그레이드나 다운그레이드 할 때 정가 기준으로 부분 환불해줄지, 아니면 구매한 가격 기준으로 환불해줄지 등에 대해서 많은 논의가 오고 갔었습니다.
부분 환불 정책 또한 회사에서 정하기 나름이라 어떤 게 정답이고, 틀리다라고 정할 수 없지만
결론을 내릴 때 내가 사용자라면 어떤 게 더 나을지 고민하며 정책에 대해 논의하고 정한 기억이 납니다.
정책을 정한 후 프론트 개발 과정은 다음과 같습니다.
- 현재 구독중인 상품은 선택이 불가능 하도록 한다.
- 상품 선택시 서버에서 부분 환불 가격을 전달받아 사용자에게 보여준다.
- 변경을 진행한다.
앞서 b. 환불 과정 개발과 동일하게 부분 환불 가격을 서버에서 전달받아서 보여줍니다.
4. 결제 내역 개발
결제 내역 개발은 사실 크게 어려운 부분이 없습니다. 결제 히스토리만 보여주면 되기 때문입니다.
다만, 결제 상태별로 보여져야 할 부분이 다르기 때문에 상황별로 히스토리에 보여줄 항목을 정리를 해야합니다.
- 무료 체험을 포함한 결제 완료 (환불, 상품 변경 등을 진행하지 않은 상태)
ex. 결제 상품, 결제 금액, 결제 방법, 결제 결과(ex, 결제 완료, 전액 환불 등), 결제 시간 - 전액 환불 / 부분 환불
ex. 결제 상품, 결제 금액, 결제 방법, 결제 결과, 취소 사유, 취소 시간
5. 결제 카드 변경 개발
구독 서비스는 정기적으로 결제가 진행되기 때문에 결제 카드를 등록하는 게 일반적입니다.
이 때, 사용자가 중간에 결제 카드 변경을 원하는 경우가 반드시 있기 때문에 결제 카드 변경 개발이 진행되어야 합니다.
저희 팀 같은 경우에는 사용하고 있던 PG사(페이플)에서 결제 카드 변경 모듈을 제공해줬기 때문에 기능 구현 자체는 빠르게 진행되었으나, 구독 결제 시 카드 등록 화면과 구독 후 결제 카드 변경 화면을 하나로 통합할지, 별개로 구현할 것인지에 대한 고민이 있었습니다.
일단 카드 등록과 변경 화면을 하나로 통합하면 조금이라도 개발 시간을 단축시킬 수 있다는 장점이 있었으나, 사용자가 결제 도중에 '마이페이지 > 결제 카드 등록 변경' 화면으로 이동을 해서 진행해야한다는 게 매끄럽지 않고 유저 이탈이 발생할 수 있다는 단점이 있었습니다.
반대로 별도로 개발하면 개발 시간은 다소 늘어날 수 있으나, 페이지 이동없이 결제 페이지에서 모든 게 해결이 되니 UX측면에서 좋다는 장점이 있었습니다.
최종적으로는 카드 등록과 변경 화면을 하나로 통합하는 방면으로 진행했습니다.
당시에는 개발 시간이 충분하지 않았기 때문에 UX를 고려하는 것보다 출시를 빨리 하는 게 목적이었기 때문에 추후에 유저로 부터 피드백을 전달받거나 리뉴얼을 진행하게 될 때 구현을 하면 좋을 것 같다고 의견을 나눴습니다.
그래도 저는 프론트엔드 엔지니어로써 결제 페이지에서 '마이페이지 > 결제 카드 등록 변경' 화면으로 이동 후 사용자가 직접 뒤로가기를 통해 이전 페이지로 이동하는 경험을 하지 않도록하기 위해, 결제 카드 등록 변경 url로 이동시 쿼리 파람에 prevUrl을 추가하여 결제 카드 등록 변경 후 자동으로 이전 페이지로 이동할 수 있도록 구현을 했습니다.
그리고 PG사마다 다를 수 있는데, 페이플의 경우 카드 확인을 위해 100원이 결제되었다가 취소되는 과정을 한 번 거치게 됩니다.
이 부분은 PG사 테스트하면서 알게되는 부분이라서 기획자가 알 수 없기 때문에 개발자는 기획자에게 이를 공유하여, 100원 결제 후 취소 과정에서 사용자가 혼란을 느끼지 않도록 고지하는 부분을 추가해줘야 합니다.
6. 결제 메일 작업하기
모든 결제 개발이 완료되고나면, 사용자에게 결제 완료 및 갱신을 알려주는 결제 메일 퍼블리싱 작업을 해줘야합니다.

회사마다 다를 수 있지만 저희팀 같은 경우에는 프론트 개발자는 디자이너가 결제 완료 메일 디자인을 해주면, 해당 디자인을 바탕으로 퍼블리싱해서 서버 개발자에게 전달하는 일만 했습니다.
그러면 서버 개발자는 HTML 코드를 전달 받아서 동적으로 변경될 데이터 부분(ex. 닉네임, 상품명, 결제일, 결제금액, 이용기간, 다음 결제일, 결제 방법 등)만 수정해준 후, 결제 완료와 함께 DB에 등록되어있는 사용자 메일로 결제가 정상적으로 완료되었다는 것을 처리해줍니다.
사실 이메일 퍼블리싱 작업 자체는 간단해서 금방 끝나는데, 작업하기 전에 미리 알고 있으면 더 빨리 끝낼 수 있는 부분들을 간단하게 정리해보았습니다.
- 이메일에서 사용할 html 코드를 작성할 때 css 스타일은 인라인 스타일로만 작성해야 적용됩니다. 내부 스타일 시트나 외부 스타일 시트를 사용해서 css 스타일을 적용한 뒤 메일을 전송하면 스타일 적용이 되지 않은 상태를 볼 수 있습니다.
- 이메일 업체마다 디자인이 다르게 보여질 수 있기 때문에, 특정 메일 업체에서만 테스트하지말고 최대한 다양한 곳에서 테스트를 진행해봐야 합니다.
- 겪었던 문제 및 해결 방법
a. 하이웍스에서 내용이 잘려보이는 문제 ➡️ height: 100%을 적용하여 문제 해결
b. Gmail에서 특정 글자의 색이 보라색으로 보이는 문제 ➡️ 글자색으로 검정색을 표현할지라도 모두 인라인스타일로 글자색을 고정하여 문제 해결 - Gmail에서는 display:flex;가 적용되지 않습니다.
- Gmail의 flex 관련 이슈 내용: https://stackoverflow.com/questions/39376710/flex-grid-properties-are-deleted-in-gmail-email
- Gmail에서 지원하는 CSS 속성: https://developers.google.com/workspace/gmail/design/css?hl=ko
QA 그리고 배포
Payple의 경우 로컬 환경에서는 결제가 진행되지 않기 때문에 운영 서버가 아닌 테스트용 서버가 별도로 필요합니다.
저희 회사의 경우 배포 전에 테스트만 전용으로 하는 서버가 따로 존재했기 때문에 해당 서버에서 충분히 기획자, 디자이너, 개발자 모두가 QA에 참여해서 테스트를 진행한 후 배포를 진행했습니다.
참고로 당시에 테스트용 서버에서 결제 테스트를 하기 위해서 페이플 고객센터에 직접 문의를 했어야 하는 걸로 기억하는데, 이 부분은 확인을 한 번 해보셔야할 겁니다!
그리고 드디어 정식 배포를 하게 됩니다.
다행히 배포하고 나서 문제없이 결제가 진행되었고, 개발팀 모두 꼼꼼히 QA를 한 덕분에 웹의 경우 지금까지 단 한건의 결제 오류도 발생하지 않았습니다.
그 때 했으면 좋았을 것들
지나고 나서 보니 그 때 했으면 좋았을 것들이 있어서 간략하게 소개해보려고 합니다.
테스트 코드 작성
결제 시스템 개발을 하며 테스트 코드 작성을 하지 못했던 점이 제일 큰 아쉬움으로 남습니다.
항상 개발 일정에 쫓겨서 테스트 코드를 생각할 여유가 없었는데, 오히려 테스트 코드 작성을 했더라면 더 빨리 끝낼 수 있지 않았을까? 라는 생각이 들었습니다.
특히 e2e 테스트를 적용했다면 매번 여러 계정으로 로그인해서 결제, 환불, 취소 등의 테스트를 번거롭게 하지 않았을 것 같아요.
좀 더 효율적으로 개발하지 못한 점에 대해서 아쉬움이 많이 남았습니다.
구독 환불 및 해지시 사유 받기
만약에 기획에 구독 해지, 환불 등의 설문조사가 빠져 있다면 반드시 추가해야 합니다.
혹시나 개발 일정상 이것까지 넣으면 빠듯할 것 같아서 뺐다고 하신다면 그 배려는 감사히 받고 배포를 한 뒤 나중에라도 꼭 넣어야합니다.
왜냐하면 소비자 의견을 듣고 서비스 품질을 어떻게 개선할지 판단할 수 있는 요소가 되기 때문입니다.
경험상 환불 데이터나 해지 데이터는 계속 쌓여가는데 이유를 모르면 서비스 개선이 어려워서 소비자 니즈와 상관없는 기획으로 삽질만 계속하는 리뉴얼을 하게 됩니다.
저희는 초기에는 있었다가 추후에 두번째 리뉴얼 할 때 해지 사유를 제거한 케이스인데, 이번에 결제 리뉴얼 할 때 사유를 꼭 받아야 한다고 어필해서 추가하게 되었기 때문에 다음 구독 서비스 개선 때 참고할 수 있을 것 같아요 :)
마무리하며
사실 결제 시스템 개발이 끝나고 나면 바로 이어서 구독 관리를 할 수 있는 관리자 페이지까지 만들어야 진정한 끝이라고 볼 수 있습니다만, 이 글에서 다 풀어내기에는 내용이 길어져서 나중에 기회가 될 때 정리해보도록 하겠습니다.
위 글에서는 담지 못한 슬픈 여담이 하나 있는데 배포를 하기 1주일 전 청천벽력같은 이야기를 전달받습니다.
저희 비즈니스 모델에 수정이 생겨서 기획이 변경되었어요.
단순히 상품 추가 수준이 아니라서 화면 기획과 로직도 새로 작성해야하는 상황이 발생한 겁니다...

지금 그 때를 다시 생각하면 회사 이익을 위한 결정이니까 그럴 수 있다고 생각을 하는데, 당시에는 그동안 팀원 모두가 스트레스 받으며 주말 작업과 야근을 해왔는데 다시 작업을 해야한다고 하니 정말 많이 힘들었습니다.
회의실 안에서 다들 그동안의 고충을 토로하고, 다시 개발 일정을 산정해서 개발을 진행합니다.
새로 산정한 개발 일정은 3주.
출시를 더 미룰 수 없었기 때문에 지금 결정이 최종 결정이라고 정하고 다시 개발을 진행 후 배포를 했다는 슬픈 이야기였습니다😂
모두가 처음 구독 결제 시스템 개발에 참여했기에 우여곡절이 많았지만, 제 커리어에서 커뮤니케이션 스킬부터 개발 실력까지 정말 많이 성장할 수 있었던 프로젝트였습니다. 참고로 결제 개발은 모듈화만 잘 해두면 처음에만 난이도가 높을 뿐 나중에 유지보수할 때는 어렵지 않게 하는 작업이 됩니다.
정말 엄청 긴 글이었는데 끝까지 읽어주셔서 감사합니다👍
궁금한 점이 있으시다면 편하게 댓글 남겨주세요!