안녕하세요 이번 주제는 배달 음식을 시킬때 장바구니를 구현을 하면서 겪었던 문제점 입니다.
저희는 배달의 민족 클론코딩이기때문에 아래 사진과 비슷하게 장바구니 UI를 생각했습니다.
지희가 원하는 서비스는
- 장바구니에 같은 매장 음식들만 담을 수 있다.
- 같은 메뉴이지만 메뉴옵션이 다르면 다른 메뉴로 장바구니에 담아진다.
- 수량은 한번씩만 변경할 수 있다.
- 메뉴에서 같은 (메뉴+옵션)을 선택한다면 수량이 증가한다
위와같은 로직을 구현해야했습니다.
저는 레디스 저장소를 사용했습니다.
인메모리저장소를 사용한이유는?
왜냐하면 장바구니 특성상 유저들이 대부분 장바구니에는 담았다 지웠다 많이 조회가 많이 일어나기도 해서 db에 매번 I/O를 것보다 속도가 더 빠르다고 생각하기도 하였고,
추가적으로 장바구니 메뉴는 담아뒀던 메뉴가 사라진다고해서 서비스에 지장이 많지 않다고 생각했습니다. 그래서 인메모리 저장소를 사용했습니다
레디스를 선택한이유는?
MenhCache랑 Redis 둘중 가장 많이 비교를 합니다. 그런데 저희는 여러 자료구조를 지원하기떄문에
레디스를 사용했으며 또한 싱글스레드라서 동시성문제에대해서 안전하다고 생각했기때문에
이전에 세션클러스터링을 할때 레디스를 선택했기때문에 그대로 사용했습니다.
첫번째 문제점은 바로 각 유저마다 메뉴를 담는데 담는 메뉴가 옵션이 다르면 다른 메뉴로 생각해야된다는 것입니다.
그래서 저는 처음에 UUID나 LocalDateTime을 생각했습니다.
그렇다면 담을때의 key가 다르니까 상관이 없겠다라고 했지만 유저가 다시 메뉴 화면으로가서
메뉴에서 같은 (메뉴+옵션)을 선택한다면 수량이 증가해야하는 문제가 생기는것입니다.
그래서 고유한 key값을 만들기위해서 어떤식으로 조합을 하면 좋을지 생각을했습니다.
메뉴아이디들의 합을 생각해보기도 하였고, 메뉴+메뉴옵션들의 암호화 등등 여러가지생각했습니다.
1. 합을 생각한다면 합이 같을수도있는 문제가 발생합니다.
2. 암호화하는것도 시간이 걸리기때문에 시간을 줄이기위해서 인메모리를 사용하기도 했는데 이러한 방법이 저희가 목표로했던 방향과 다르다고 생각했습니다.
그래서 내린 결론은 아래 코드와 같습니다.
처음에 제가 ItemDto로 만들때 메뉴아이디와 + 옵션들의 ID를 _를 통해 이어주어서 고유한 key를 만들어 내는것입니다.
메뉴+메뉴옵션이 동일한지 알수있기때문입니다.
두번쨰 메뉴에서 같은 (메뉴+옵션)을 선택한다면 수량이 증가한다
고민했기보다는 생각해야했던 부분은 바로 1개일때 유저가 - 를 클릭한다면입니다.
그렇게 된다면 0개가 되기때문에 사라지거나 아니면 1개를 유지하기위했어야했습니다.
이와 같이 1개 이상인 경우만 수량을 감소시키는걸로 정했습니다.
세번째 매장같은지 판별
매장이 다르면 기존의 존재하는 메뉴들을 다 지워주어야합니다.
그래서 매장이 같은지 다른지를 판별해야하는데 위에서는 장바구니를 담는것 까진 완료가 되었습니다.
그러나 유저가 어떤매장을 가지는지 판별하기가 힘듭니다. 그래서 저는 따로 hash를 이용해
"STOREI_ID" : {"user1" : "2" }, {"user2" : "1" } 이와같이 구현을 하였습니다
네번째 장바구니 정합성 문제
저는 지금은 메뉴와 메뉴옵션 가게 목록들이 캐시가 안된상태입니다.
이제 후에는 캐시 적용을 생각하고 있습니다. 왜냐하면 목록이 많아지고 밥시간때면 유저들이 요청하는게 많아질텐데 사장님도 가격변경이 적다고 생각을했습니다. 그래서 캐싱을 해놓으면 더 속도가 빨라질것이라 생각을 하고 작업을 진행했습니다. 그런데 문제점이 있습니다.
1. 캐시되어있는 메뉴를 장바구니에 넣습니다.
2. 사장님이 가격을 변경합니다
3. 장바구니에는 변경된가격이아니라 변경전 메뉴가격이 존재합니다.
이런경우는 어떻게 해결해야하는지 팀원분과 상의했습니다.
의견중 하나는 조회를 할때 마다 장바구니에서 정합성을 검사를 하자입니다.
-> 이 의견의 단점은 장바구니에 잦은 조회나 변경으로인해서 빠르게 응답하기위해 사용했던 레디스인데 조회가 느려질뿐더러 매번 장바구니를 볼때마다 DB에 요청을 해야한다는것입니다. 그리고 요청을 하고 한참후에 주문을 할수도있는경우가있어서 여전히 문제점이 남아있습니다.
다른하나는 배달 주문하기를 할때 정합성을 검사하자
아래와 사진과 같이 (장바구니페이지에서)배달 주문하기를 누를때입니다.
그러나 그다음 페이지인 주문하기 페이지에서 이제 주소를 다시 정리하거나 요청사항 등이있으며 저희는 구현되지않았지만 결제 부분까지 존재합니다.
그렇다면 주문하기 페이지에 유저가머무른 동안 만약 사장님이 가격을 변경한다면 발생되는 문제가 어떤 악의적인 유저가 옛날에 담아두고 주문하기만 눌어 놓고 나중에 주문하는것입니다.
나중에 주문을 하게된다면 정합성이 맞지않게되는 문제가 생깁니다.
🔍주문을 생성할때 검사하자
그래서 저희는 배달주문하기를 클릭하면 주문을 생성할때 정합성을 검사하자는것으로 결론을 정했습니다.
주문생성시에 단계별로 정합성들을 체크해가면
저희가 걱정하는 정합성 문제를 해결할 수 있기때문입니다.
느낀점
장바구니 라는게 고려해야하는 제가 예상한것보다 많았습니다.
그리고 결제부분은 매우 민감한 문제입니다.
주문생성시에 팀원분이 비롯 작성했지만 그래서 제가 낸 의견은
상점이 열었는지, 가격이 맞는지 등 정합성을 확인하는 부분이 존재하기때문에
속도가 매우 오래걸립니다. 그렇지만 민감한 문제인만큼 신중해야한다는 것을 느꼈습니다.
'Delivery' 카테고리의 다른 글
레디스 테스트코드 문제점 - TestContainer도입 (0) | 2022.08.19 |
---|---|
Redis 장바구니 - 2 (Redis pipelining) (0) | 2022.08.18 |
리펙토링 디비조회 (0) | 2022.08.09 |
캐싱전략, 메모리 (0) | 2022.08.05 |
레디스를 선택한 이유, 세션클러스터링 (0) | 2022.08.05 |