개발/고민과 생각

e-commerce 개발일지 - 장바구니 설계하기

hojak99 2022. 2. 10. 02:09

이제 "판매 상품" 들을 장바구니에 담아보자.

쿠팡에서는 크게 "로켓와우" 라는 정기 구독 상품과 판매자들이 등록한 일반 결제 상품이 존재하는 것으로 보인다. 
네이버 스마트 스토어에도 판매자들이 등록하는 정기 구독 상품과 일반 결제 상품이 존재한다.
이렇게 각 서비스들은 "정기 구독 상품" 과 "일반 결제 상품" 이 존재할 수 있다. 그러나 일반적인 서비스들에서는 장바구니에 "정기 구독 상품" 과 "일반 결제 상품" 을 함께 장바구니에 담거나 하지 못하는 것으로 보인다. 

그래서 다른 서비스들과 다르게 하나의 장바구니에 "정기 구독 상품" 과 "일반 결제 상품" 을 같이 담아 한 번에 결제 할 수 있도록 하는 기능을 개발하게 된 이야기를 작성해보도록 하겠다. 결과론적으로 보면 이 결정으로 인해서 마이페이지 주문내역에서 발생하는 문제점들이 많아지게 되었다. 
(판매자가 정기 구독 상품, 일반 결제 상품을 등록하는 다른 서비스와는 다르게 내가 현재 개발하고 있는 서비스는 팀에서 모든 상품들을 등록하고 관리하게 되다 보니 가능한 기능인 것 같긴 하다) 

 

하나의 장바구니에 정기구독 상품과 단일 결제 상품이 담기게 된 계기

네이버 스마트 스토어를 이용한다고 가정을 해보도록 하겠다. 하나의 판매점에서 일반 결제 상품도 사고 싶고, 정기 구독 상품도 같이 구매를 하고 싶은데 장바구니에는 각 상품들을 한 꺼번에 담지 못하기에 정기 결제 상품과 일반 결제 상품 결제를 따로 따로 결제를 해야 한다. 즉, 2번의 결제 과정을 거쳐야 한다는 것이다. 

그렇기에 우리 서비스에서는 각 구매하고 싶은 상품들을 모두 장바구니에 담아서 한 번에 결제가 가능하도록 기획이 됐다. 마이페이지에서는 어떻게 보여줄 것인지도 같이 이야기를 했었는데 여러 상품들을 장바구니에 담아 한 번에 결제를 하면 그것은 하나의 '결제' 라고 보았기에 하나의 view 에서 결제한 모든 상품들을 보여주기로 하였다.

네이버, 쿠팡에도 없는 기능인데 내가 개발을 하려고 하니 이게 맞는건가 싶긴 하다. 하지만 항상 정답은 없으니 시도를 해보았다.

 

요구사항 파악해보기

어떤 요구사항이 있는지 먼저 파악해야 한다. 
아마 아래와 같은 요구사항들이 존재할 수 있다.

1. N명이 로그인했을 때 장바구니가 서로 연동되게 해주세요.
2. 장바구니에 담았을 때의 가격을 기준으로 구매하지 않고 있으면 1시간 뒤에는 가격을 새로고침 해주세요.
3. 장바구니에 담기만 하고 구매하지 않은 유저들을 파악하여 쿠폰을 발송하기 위해 관리자가 파악할 수 있도록 해주세요
- .... 

우선 나는 이런 요구사항들을 구현하게 되었다.

1. 장바구니에 담았을 때의 가격을 기준으로 유지해주세요.
2. N분동안 장바구니 페이지에 머물러 있으면 상품을 새로고침 해주세요.
3. 장바구니 페이지에서 이탈 시 담겨있는 모든 상품을 새로고침 해주세요.
4. 장바구니 페이지에서 주문창 이동 시 장바구니 담겨있던 가격 그대로 결제되게 해주세요.
5. 장바구니에서 상품 수량 변경 시 상품을 매번 새로고침 해주세요.
6. 장바구니에 담기만 하고 구매하지 않은 유저들을 파악하여 쿠폰을 발송하기 위해 데이터는 휘발되지 않게 해주세요.
7. 결제 중 재고 존재하지 않을 시 결제를 튕기게 해주세요.

여기서 한 가지 생각하고 넘어가야 할 점은 상품은 '재고' 가 존재하는 경우가 있다는 것이다. 사용자가 결제하는 중에 '재고' 가 부족하여 결제가 되지 않는다면 사용자에게 불편함을 안겨준 것이라고 볼 수 있다. 그래서 개발, 관리 편의성, 사용자의 경험 이 2가지를 잘 판단하여이 찰나의 순간을 통과시켜주냐 마느냐를 결정해야 한다.

 

구현 시 생각해보아야 할 점

1. 상품의 가격 변동이 일어나더라도 장바구니에 담긴 상품의 가격은 변동이 없어야 한다.
2. 장바구니에서 특정 이벤트가 발생해야만 상품의 가격을 현재 가격으로 변경해야 한다.
3. 휘발되지 않아야 한다.
4. 장바구니 페이지에서 벗어날 시 상품을 새로고침해야 한다.

우선 장바구니 페이지에서 벗어날 시 상품을 새로고침 해야 하는 것이 좀 고민이 되었다. 이것은 장부구니 상품을 조회하는 API 를 호출할 때마다 상품을 새로고침 해주는 작업을 하는 것으로 정하였다.
(물론, 조회 API 라서 빠르게 보여주어야 할텐데 상품 조회 후 업데이트 작업까지 존재하니 성능에 대해서 걱정이 되었으나 우선 더 깊게 생각하지는 않고 이 로직에 문제가 생길 시 개선하는 것으로 넘어갔다. 조회 시에는 원본 상품을 보여주고, 그 뒤에서 사용자의 데이터를 업데이트 하는 로직으로 분리시키면 나을 것 같기도 하다)

그리고 특정 이벤트가 발생해야만 상품의 가격을 현재 가격으로 변경이 되어야 하는 것에 대해서 얘기해보자. 특정 이벤트를 먼저 정의해보면 "상품 수량 변경", "삭제" 이다. 그리고 해당 이벤트가 일어난 상품에 대해서만 가격을 새로고침 시켜주어야 한다.

휘발되지 않아야 하기에 장바구니 상품을 DB 에 넣도록 하였다. 물론 redis 를 persisternce store 용으로 사용하는 것도 생각해보았으나 위험할 수도 있다는 생각이 들었다. 또한, 추후에 장바구니에 담겨져 있는 상품들을 기반으로 쿠폰이 나갈 수 있기에 우선 DB 에 넣어서 해결하였다. (사용자가 많지 않아 캐시로는 사용하지 않았다)

 

가격 관리

상품의 가격은 항상 변경될 수 있으며, 사용자는 최신의 상품 가격이 아닌 과거의 상품 가격으로 결제를 할 수가 있어졌다.
이제 상품의 가격을 잘 저장하여 악의적인 사용자가 이상한 가격으로 요청하더라도 막을 수 있도록 개발해야 한다.

그렇기에 난 상품의 가격에 대해 DB 에 쌓을 때 항상 insert 만 하도록 하여 update 가 발생하지 않는 식으로 개발하였다. 그리고 해당 상품의 id 를 다른 연관 테이블에서 fk 로 가지고 있도록 하였다. 물론 이렇게 되면서 연관된 많은 테이블에서 상품 가격의 id 를 fk 로 가질 수 밖에 없어졌긴 했다. 그래도 상품 가격 자체로 결제가 이루어지거나 하지 않을 수 있기에 항상 내가 의도한 가격으로 결제될 수 있도록 하였다. 

개발을 잘못한다면 이런 케이스가 발생할 수 있을텐데 이것은 테스트 코드를 잘짜서 해결하도록 하자. 
장바구니를 통한 결제는 장바구니 데이터를 기준으로만 결제시키면 되기 때문이다.

"장바구니 테이블에선 가격 ID 가 1 인데 결제 테이블에선 가격 ID 가 2 에요...!"

 

결제 테이블에서 장바구니를 통한 결제라는 것을 알게 해줘야 할까? 

결제를 할 수 있는 방법은 2가지가 존재하게 된다. "바로 결제" 와 "장바구니를 통한 결제" 이다.
추후에 어떤 방법을 통하여 결제를 진행했는지 알아야 할 수 있을 것으로 보고 결제 테이블에서 장바구니를 통한 결제라는 것을 알 수 있도록 하였다. 예를 들면 추후에 "장바구니에 상품을 담아서 결제하는 것이 많을까, 하나 하나 바로 결제하는 것이 많을까?" 에 대한 질문에 답변할 수 있어야 한다고 생각했다. 서비스 특성 상 "정기 구독 상품", "일반 상품" 이 존재하는데 이 상품들을 한 번에 담아서 구매하는지, "정기 구독 상품" 만 바로 구매 후 서비스 이용을 해본 뒤에 "일반 상품" 들만 장바구니에 담아서 결제하는 것이 많은지 데이터를 뽑아야할 수 있어야 한다고 생각하기 때문이다.

그렇기에 결제된 상품 데이터가 어떤 장바구니의 상품에 연결되었는지 알 수 있게 fk 를 맺어주엇다.

 

장바구니 상품 데이터 쌓기

사용자가 장바구니에 상품을 담았을 때 데이터를 어떻게 쌓아야 하는지 고민했다. 여러가지 구현 방법을 생각했는데 우선 다음과 같이 테이블 설계를 하였다.

각 테이블의 연관관계

위 테이블들의 연관관계를 보면 이제 "장바구니 아이템 테이블 1개로만으로도 구현할 수 있잖아?"  와 같은 질문이 생길 수 있다. 나도 동의하지만 내가 장바구니와 아이템 테이블로 분리한 이유는 사용자가 마지막으로 장바구니에 상품을 담은 시각 또는 담겨있는 아이템 개수 등과 같은 데이터들을 반정규화를 하기 위함이었다. 이게 좋은 설계일지는 확실히 모르겠다. 대신 이렇게 함으로 써 관리자 또는 사용자가 장바구니를 볼 때 매번 장바구니에 담긴 전체 아이템을 조회할 필요가 없기 때문에 더 효율적일 것 같다고 생각했다. 물론, 아이템 테이블에 데이터가 변경될 때마다 장바구니 테이블에도 업데이트가 일어나겠지만.. 장바구니 기능에 대한 read/write 비율을 생각하면 나는 read ㄱ 더 많이 할 것이라고 생각했다. 물론 나중에 실제 read/write 비율을 확인해야겠지만.

이렇게 해서 테이블을 설계하였다. 그리고 장바구니 아이템은 사용자가 선택한 아이템들로 구매가 가능하다. 그렇기에 장바구니 아이템에도 상태를 두었다. 담긴 상태인지, 삭제 삭제인지, 구매 완료된 상태인지, 만료 상태인지에 대해서 말이다. 

반응형