home

쑥쑥 자라나는 아이들나라 도메인

안녕하세요. 저는 아이들나라 플랫폼팀 김혜민입니다.
2023년 10월 아이들나라는 쑥쑥 패키지라는 신규 기간권 상품을 오픈하였습니다. 기존 구독 시스템에 신규 비즈니스를 추가하며 작지만 발전하는 모습을 공유하고자 글을 작성하였습니다.

들어가며

기간권 상품이란 컨텐츠와 실물상품(과학키트/워크북)을 결합하여 특정 기간동안 컨텐츠를 구독하고 실물 상품을 받아볼 수 있는 상품입니다. 기존에 없던 상품정책, 주문 프로세스, 결제방식 그리고 배송관리를 구현하기 위해 플랫폼팀에서는 아래 사항들을 고민했습니다.
어떻게 다양한 옵션으로 확장성 있게 상품을 게시하고 판매 할 수 있을까?
기존 구독시스템에 신규 도메인을 어떻게 자연스럽게 연동할 수 있을까?
사용자의 유즈케이스는 무엇이고 각 도메인의 역할을 어떻게 정의할 수 있을까?
백오피스 어드민을 어떻게 최대한 빠르게 만들 수 있을까?
문제를 같이 깊이있게 고민하고자 기존 설계 한계점 부터 변경된 설계 개선점 순으로 설명합니다. 그리고 위 문제를 풀기위한 각 도메인의 정의와 비즈니스 역할 변화를 말씀드립니다.

1. 기존 설계

기존에 아이들나라의 상품 구매 프로세스는 단순했습니다.
고객은 VOD 상품구독하고 정기적으로 결제를 수행할 수 있다.
이 시스템은 3가지 도메인으로 구성되어 있었습니다.

1-1. 구독(Subscription)

상품 및 사용자 설정에 따라 정기적으로 지불하는 서비스 약정을 관리합니다.

도메인 역할

사용자는 VOD상품을 구독 혹은 구독취소 할 수 있다.
약정정보에 따라 청구를 생성한다.
다음 그림은 이벤트에 따라 구독(Subscription)의 상태가 전이되는 것을 표시했습니다.
구독 도메인은 아래 정보들을 관리합니다.
구독하고 있는 상품정보 (상품명, 가격 ..etc )
구독 주기, 기한
청구 정보 (청구 방법, 다음 청구일 및 청구 금액)
현재 회차 정보

1-2. 청구(Billing)

상품에 대해 청구서를 생성하고 이를 기반으로 결제를 요청합니다.

도메인 역할

청구를 생성하고 상태에 따른 프로세스를 관리합니다.
결제 실패 시 유예정책에 따라 청구를 처리합니다.
청구 방식은 즉시 청구, 예약 청구 두 가지 방식으로 정의했습니다.
즉시 청구는 상품 구매 후 바로 고객에게 청구하는 방식이고 예약 청구는 예약된 날짜에 고객에게 청구하는 방식입니다.
현재 구독상품의 경우 청구 시나리오는 다음과 같습니다.
1.
구독상품 구매 후 첫번째 청구가 즉시 발생.
2.
다음 회차부터 구독정책에 따라 주기적으로 청구가 예약.
3.
고객이 구독을 취소하는 경우 다음 결제가 수행되지 않도록 예약된 청구를 취소.

1-3. 결제(Payment)

결제수단에 따라 결제 혹은 환불을 수행합니다.

도메인 역할

결제를 위한 카드 정보를 관리한다.
결제수단에 따라 결제를 할 수 있다
결제된 금액에 대해 전체/부분 환불 할 수 있다.
현재 아이들나라 상품 구매시 다음과같은 결제방식을 제공하고 있습니다.
간편결제 방식 (Vendor)
토스페이, 카카오페이(오픈예정)
결제방법 (PayType)
분할납 - 상품금액을 n회차에 나누어 결제 일시납 - 상품금액을 한번에 결제
결제수단 (PayMethod)
카드, 현금

2. 변경된 설계

기간권 상품이라는 신규 비즈니스가 추가되면서 기존 우리 시스템에서 생략 되었던 각 도메인 역할을 다시 정의하였습니다.
구독 도메인 내 상품, 주문… 도메인을 논리적으로 분리하고 서버 및 DB도 독립적으로 구성하였습니다.
주문에서 구독으로 넘어가는 프로세스는 서버간 API 호출이나 DB 조회가 아닌 kafka를 통해 event를 전달 받도록 하였습니다. 이는 상품과 주문 도메인에서 DB 또는 API 스팩이 바뀌더라도 구독 도메인에서 영향받지 않기 위함입니다.
이를 바탕으로 아이들나라는 상품과 프로모션 타입이 증가함에 따라 급증하는 주문 경우의 수를 안정적으로 서비스 할 수 있게 되었습니다.
변경된 설계
위 그림은 기존 도메인에 신규 도메인이 추가된 모습입니다.
이번 글에서는 핵심이 되는 상품, 주문, 배송 각 도메인의 상세 역할을 설명하겠습니다.

2-1. 상품(Product)

상품 도메인은 고객이 구매하는데 필요한 상품의 모든 정보를 관리합니다.

도메인 역할

상품을 생성할 수 있다.
상품정보를 조회할 수 있다.
상품정보를 수정할 수 있다.
우리가 상품 도메인을 정의할때 가장 중요하게 생각한것은 다음과 같습니다.
원하는 상품을 쉽게 생성하고 게시하고 수정할수 있다.
다양한 옵션의 상품을 만들어낼 수 있는 유연함을 가진다.
위와같은 배경을 가지고 우리는 아이들나라만의 독자적인 상품 시스템을 설계했습니다.
상품 도메인은 크게 3가지 개념으로 구성됩니다.
Product
Product 은 고객이 구매할 수 있는 특정한 개체를 대표한다.
Product 은 상품의 메타 정보를 갖는다.
Product 은 조합될 수 있는 옵션 그룹을 갖는다.
브랜드, 노출 상품명, 상품 설명, 대표 이미지, 배송상품 여부 등을 갖는다.
Product Option
Product 을 어떤 형태로 판매할 것인지에 선택지 정보이다.
고객이 자신의 선호도나 특정 선택에 따라 구매를 맞춤화할 수 있도록 상품의 옵션정보를 저장한다.
Product option 은 Product 에 따라 다른 옵션정보를 갖을 수 있다.
예를들어서 티셔츠라는 Product은 size, color 라는 Product option 을 갖을 수 있고 size 는 S, L, XL 로 구성, color 는 red, blue, black 으로 구성된다.
Product Item
Product Option 의 조합으로 생성된 구체화된 개체이다.
Product Item 은 고객이 구매할 수 있는 최소한의 단위이다. 즉, 사용자는 Product Item 을 구매한다.
아이템 이름, 가격, 판매시작일, 판매 종료일, 판매 상태, 옵션 정보 등을 갖는다.
위 내용을 바탕으로 탄생한 우리의 첫번째 상품은 다음과 같습니다.

2-2. 주문(Order)

주문 도메인은 고객이 주문 요청을 받는것 부터 결제가 완료 되기까지 전체적인 주문 라이프 사이클과 관련된 다양한 요소를 관리합니다.

도메인 역할

고객은 원하는 상품을 주문할 수 있다.
결제가 실패되면 주문이 취소 될 수 있다.
고객은 환불을 요청할 수 있다.
우리가 주문 도메인을 정의할때 가장 중요하게 생각한 것은 다음과 같습니다.
주문은 정확해야한다.
주문은 안전해야한다.
위 조건을 구현하기 위해 우리는 몇 가지 작업을 수행했습니다.

1. 주문 스냅샷 데이터 저장

우리는 정확한 주문데이터를 위해서 주문시점의 데이터를 스냅샷 형태로 저장했습니다.
뒤 프로세스에서 발생하는 결제나 배송처리는 해당 스냅샷 데이터를 기반으로 생성됩니다.
주문 요청 한건당 저장되는 스냅샷 데이터는 다음과 같습니다.
주문시점의 고객정보, 상품정보, 배송정보, 프로모션 정보
주문시 저장되는 스냅샷 데이터

2. 주문요청에 대한 유효성 검증

주문은 고객의 요청을 받는 인입점이기 때문에 고객에게 정확하고 빠른 피드백을 주어야합니다.
예를들어 주문이 완료 되었는데 재고가 없어 취소가 된다면 고객에게 안좋은 경험을 줄 것입니다.
그래서 우리는 이런 문제를 방지하기 위해 주문 요청시 유효성 검증을 강화했습니다.
타입
비즈니스 validation
사용자 검증
아이들나라 회원인가?베타관계의 상품을 이미 이용하고 있지 않은가?
상품 검증
주문한 상품정보가 아이들나라의 상품정보와 동일한가?가격 확인, 옵션 정보 확인상품이 구매가능한 상태인가?상품 판매 시작일, 종료일
프로모션 검증
해당 프로모션이 현재 적용가능한가?프로모션 기간 확인해당 프로모션을 주문한 상품에 적용할 수 있는가?프로모션을 적용한 금액은 실제 상품가격보다 적은가?
재고 검증
해당 상품의 재고가 남아있는가?

3. 주문상태 정의

우리는 주문의 라이프사이클을 관리하기위해 주문의 상태를 다음과 같이 정의했습니다.
PRE_ORDERED
임시주문 상태입니다.
고객이 상품 안내페이지에서 상품을 선택 후 주문서 페이지로 넘어갈 때 PRE_ORDERED 상태가 됩니다.
사용자 정보, 고객이 선택한 상품, 적용된 프로모션의 스냅샷 정보가 저장됩니다.
ORDERED
주문요청이 완료된 상태입니다.
주문 요청에 대해 유효성 검증이 통과하고 결제요청까지 완료했습니다.
결제, 배송지 정보에 대한 스냅샷이 저장됩니다.
CANCELED
주문이 취소된 상태입니다.
결제가 실패하면 주문이 취소됩니다.
사용자는 다시 주문요청을 해야합니다.
REFUND
주문이 환불된 상태입니다.
사용자가 주문에 대한 환불을 요청한 케이스입니다.
고객이 지불한 금액에 대해 전체/부분 환불을 진행합니다.

2-3. 배송(Delivery)

배송 도메인은 구독 주기에 따라 정기적으로 배송을 생성하고 상태를 관리합니다.

도메인 역할

정기적으로 배송정보를 생성한다.
고객은 배송지 정보를 수정할 수 있다.
관리자는 송장번호를 업로드 할 수 있다.
우리가 배송 도메인을 정의할때 가장 중요하게 생각한 것은 다음과 같습니다.
사용자가(고객, 관리자) 배송처리 상태를 인지할 수 있어야 한다.
여기서 배송처리의 상태란 상품 준비부터 고객이 상품을 받기까지의 전체 과정이라고 할 수 있습니다.
그런데 배송자체를 아이들나라에서 하지 않고 별도의 업체가 담당하기때문에 배송의 처리 상태를 내부적으로 인지할 수 있는 방법이 없었습니다. 이를 해결하기 위해 팀에서 고민한 방법은 두 가지 였습니다.
1.
배송추적 api 를 제공해주는 솔루션 적용
1.
배송처리 단계를 우리가 컨트롤 할 수 있는 범위로 최소한으로 정의
결론적으로 우리는 짧은 기간최소한의 스펙으로 구현하기 위해 두번째 방법을 선택했고 솔루션 적용은 추후 개선사항으로 두었습니다.
우리는 내부적으로 컨트롤 할 수 있는 범위 내에서 배송처리단계를 다음과 같이 정의했습니다.
어드민에서 관리자가 주문을 확인하면 상품준비 중이라고 간주한다.
어드민에서 배송업체가 송장번호를 입력하면 배송시작이라고 간주한다.
배송시작 후 3일뒤에 배송완료로 간주한다.
아래 다이어그램은 이벤트에 따른 배송상태가 전이되는 모습을 표현했습니다.
배송상태
WAITING
배송대기중 상태입니다.
결제가 완료되면 배송요청이 생성됩니다.
사용자는 배송지 정보를 변경할 수 있습니다.
READY
배송준비중 상태입니다.
관리자가 배송대기중인 대상에 대해 배송지정 하고, 송장번호를 입력할 준비를 합니다.
배송이 지정되면 사용자는 배송지 정보를 변경할 수 없습니다.
CANCELED
배송취소 상태입니다.
고객에 의해 환불이나 중도해지 요청이 들어왔을때 더이상 배송을 진행하지 않도록 중단 시킵니다.
배송준비중(READY) 이후에 환불/중도해지 요청이 인입되면 메뉴얼 프로세스로 처리됩니다.
DELIVERYING
배송중 상태입니다.
송장번호가 입력되면 배송중으로 간주합니다.
COMPLETED
배송완료 상태입니다.
배송 후 3일이 지나면 일괄 배송 완료처리 합니다.
물론 위에 정의한 상태 외에도 회수, 교환과 같은 처리도 있습니다. 우리는 발생빈도와 일정을 고려하여 phase1 에서는 Edge 케이스를 메뉴얼 프로세스로 처리하는 것으로 대체했습니다.

3. 운영 / 어드민

효율적인 주문/배송 확인 및 CS 처리를 위해서는 주문의 전반적인 정보를 확인할 수 있도록 백오피스 어드민이 반드시 필요한 상황이었습니다.
어드민을 개발할때, 우리가 작업의 우선순위를 결정하는데 고려했던 가장 큰 요인은 2가지 였습니다.
서비스 오픈전, 2주안에 개발해야하는 짧은 일정
제한된 리소스 - FE 개발 지원이 어려움
위 제한사항을 바탕으로 우리는 이런 결정을 내렸습니다.
가장 빠르게 할 수 있는 방법으로 하자.!
이런 결정을통해 우리는 몇가지 action item 을 실행했습니다.
1.
스펙아웃
PO와 논의하여 오픈 후 반드시 필요한 기능을 제외하고 스펙아웃했습니다.
미구현된 기능은 고객센터에서 대응할 수 있도록 메뉴얼 프로세스를 작성했고 비교적 CS 문의가 적게 들어올 수 있는 기능들은 개발팀에서 확인 대응하는것으로 정했습니다.
1.
Frontend 템플릿 재사용
FE개발지원이 어려운 부분은 다른 부서에서 사용하고있던 어드민 템플릿을 가져와서 우리에게 필요한 기능으로 커스터마이징 했습니다. UI 디자인이나 사용자 인증 및 권한 기능을 재사용할 수 있어서 시간을 많이 절감할 수 있었습니다.
1.
개발스택 단순화
어드민은 여러 도메인 데이터를 수집해서(aggregate) 사용자에게 보여주는 기능이 대부분입니다. 우리는 어드민에 필요한 데이터를 view 용 테이블로 만들어 제공하는 방법도 생각했지만 시간 안에 구현하는 것은 무리라고 판단했습니다.
우리는 제일 간단한 방법으로 각 도메인에 필요한 어드민용 api 를 별도로 만들어서 admin 에서 조합해서 사용하는 방식으로 구현했습니다.
어드민에 필요한 주요 데이터
짧은 일정이었지만 위의 액션아이템을 바탕으로 우리는 꽤나 멋진 백오피스 어드민을 만들었습니다.
(사용자에게 어드민 사용하기 편하다는 피드백도 받았다.)
통합주문관리 페이지

마치며

우리는 2개월 이라는 짧은 일정에 제한된 리소스로 성공적으로 프로젝트를 마무리 할 수 있었습니다.
이 프로젝트를 진행하며 좋았던 점
팀 내 서버개발자가 다같이 하나의 프로젝트를 진행한 적은 처음이었는데, 도메인을 정의하는 과정에 서로 생각을 공유하며 라포를 형성했습니다.
타팀과 최대한 컨텍스트를 맞추기위해 매 스프린트 끝나고 데모를 진행했는데, 미처 파악하지 못했던 부분들이나 이슈들이 데모를 통해 미리 발견되어서 리스크관리가 잘되었습니다.
플랫폼팀은 제한된 환경속에서 작업의 우선순위를 세웠고 PO와 논의를 통해 스펙을 재정의하면서 주체적으로 일하고 있음을 느꼈습니다.
아쉬운 점
아직 도메인이 성숙해지기 위해서 시간이 좀 더 필요할 것 같습니다. 현재 아이들나라는 구독형 상품이 주력 상품이기 때문에 전반적인 설계가 구독형 주문에 초점을 두고 있습니다.
아직 해야할 future work가 많습니다. 우리가 작업하면서 현실과 타협했던 부분들을 앞으로 차차 개선해야합니다.
현재는 쑥쑥패키지 상품 뿐이지만, 앞으로 아이들나라에서 아이들에게 필요한 모든 상품을 판매할 수 있는 그날이 오길 기대합니다.
 아이들나라 쑥쑥패키지 구매하러가기