주문이 출고되기까지의 과정 (1)

해당 포스트는..

부스타에서는 주문 입력과 수집부터, 출고 그리고 매출 집계까지의 과정이 모두 유기적으로 연동되어 있습니다. 부스터스만의 출고 플로우가 궁금하시다면 끝까지 주목해 주세요! (Feat. Isolation Level)

부스터스의 출고 프로세스가 탄생한 이유

3년이라는 짧은 업력 동안 빠르게 성장한 부스터스는 2년 동안 WMS 시스템을 3번 변경하였습니다. 시스템을 변경할 때마다 해당 시스템에 맞춰 업무 플로우를 변경하고 적응하는 시간을 가져야 하는 등 비효율적인 측면이 발생하였습니다. 이에 따라 데이터를 우리만의 방식으로 내재화하고 통합 관리하여 외부 시스템에 대한 의존성을 줄이자는 의견이 나왔고, 주문 수집부터 매칭까지의 단계를 부스타에서 진행할 수 있도록 하는 부스터스만의 출고 플로우가 탄생하게 되었습니다.

부스타가 뭐야?

주문 수집부터 출고 준비까지 안전하게 데이터 운반하기

출고 플로우

브랜드를 운영하기 위해서는 다양한 채널이 필요합니다. 그에 따라 다양한 형태의 주문 데이터가 발생합니다. 그래서 가장 먼저 쿠팡, 11번가, 스마트스토어 등 수많은 온라인 채널에서 발생하는 주문들과 B2B 형태의 주문을 모두 표준화하여 통합하는 작업이 필요합니다. 대부분의 채널들의 주문은 OMS를 통해 하나로 모이고, 모인 주문 데이터는 API를 통해 DB로 수집됩니다. B2B 주문이나 샘플 출고 등의 주문은 부스타에서 엑셀 업로드를 통해, 소셜미디어 공동구매 주문 건은 ERP 시스템을 통해 등록, 수집이 이루어집니다. 이처럼 제각기 다양한 형식을 가진 주문 데이터를 수집하고 표준화하여 저장하는 과정을 통해 출고 과정에서의 복잡성을 줄일 수 있습니다.

표준화된 주문 데이터를 API를 통해 WMS에 전송하기 전에 부스타에서는 반드시 주문 매칭이 이루어집니다. 주문 매칭이란 하나의 판매 구성안에 들어있는 품목을 선택하는 작업을 말합니다. 같은 판매 구성안이라도 채널에 따라 노출되는 실제 상품명 즉, 딜명이 다르기 때문에 새로운 딜명을 가진 주문이 들어올 때마다 매칭 작업이 이루어집니다. 이러한 매칭 정보를 담고 있는 매칭 정보 테이블이 구성되면, 해당 딜명의 주문이 들어올 때마다 매칭 정보 테이블에 의해 자동으로 매칭되어 WMS에 전송될 준비가 완료됩니다.

주문 매칭이 끝난 주문 데이터를 WMS 전송하면 출고 준비가 완료됩니다. WMS에 수신된 주문 데이터를 바탕으로 재고를 할당하고, 피킹(picking) 작업을 지시하며 실제 출고를 진행합니다. 주문 데이터뿐만이 아니라, 거래처, 품목, 재고 등 출고 전반의 데이터가 부스타와 WMS에 모두 연동되어 있기 때문에 실시간으로 재고의 수불 현황 또한 파악할 수 있어 재고 부족 등의 문제를 사전에 방지할 수 있습니다.

출고 프로세스 배포 후 겪었던 크리티컬한 이슈 두 가지

출고 프로세스를 개발하고 배포하여 실사용 시작 후 약 2~3달 동안은 출고 데이터 모니터링에 가장 많은 시간을 소요했습니다. 이때 데이터의 정합성이 맞지 않아 발생했던 크리티컬한 이슈 두 가지를 공유해보고자 합니다.

  1. DB 컬럼 길이 100byte 설정
    DB의 ‘수령자주소’ 컬럼의 길이가 100byte로 설정되어 있어, 주소가 긴 경우 주소 뒷부분이 잘린 채로 주문 데이터가 WMS로 전송되어 배송이 제대로 되지 못했던 이슈가 있었습니다. 도로명 주소의 건물이나 아파트명이 긴 경우에 동, 호수 등의 상세 주소가 잘린 채 송장이 출력되어 정확한 주소를 알 수 없어 해당 건들은 배송이 정상적으로 이루어질 수 없었습니다.
    이미 출고가 이루어진 건들은 기사님께 모두 연락드려 주소를 알려드렸고, 출고가 아직 진행되지 않은 건들은 DB 컬럼 길이를 200byte로 설정한 후 주소 데이터를 재할당하여 출고를 다시 정상적으로 진행하였습니다. 다행히 출고 프로세스 실사용 초기여서 해당하는 건수가 많지 않아 빠르게 대응할 수 있었지만, 조금만 더 꼼꼼히 체크했다면 발생하지 않았을 이슈이기에 아쉬운 마음이 들었습니다.

  2. 프로세스 중복 실행
    프로세스가 중복으로 실행되는 것을 방지하는 로직이 없어 같은 프로세스가 2~3번 실행되어 1개의 주문이 여러 번 출고되는 케이스가 있었습니다. ‘주문 매칭’ 프로세스가 동시에 실행되면서 하나의 주문에 대해 품목들이 중복 매칭되어 같은 주문임에도 불구하고 송장이 여러 번 출력되어 고객에게 같은 주문 건이 여러 개 배송되었습니다.
    출고가 아직 되지 않은 건들은 데이터 클렌징 후 정상 출고하였지만, 이미 출고가 이루어진 건수가 조금 많았기 때문에 실제 금액적으로 손해가 발생했던 이슈입니다. 이후 중복 프로세스가 발생하지 않도록 실행 중인 프로세스의 flag 값을 설정하여 확인하는 방식으로 중복 프로세스 방지 로직을 구축하였습니다.

데이터 중복을 피하기 위한 트랜잭션 격리 수준 적용

주문 매칭까지 이루어진 데이터를 WMS 시스템에 전송하면 실제 출고가 이루어집니다. 이 단계부터는 고객과 직접적으로 연결되는 단계이기 때문에 데이터 정합성 관리를 엄격하게 해주어야 합니다. 이러한 데이터의 정합성을 지키기 위해 MySQL의 격리 수준을 사용하여 WMS에 API 전송이 중복 전송이 되지 않도록 방어하였습니다.

트랜잭션의 격리 수준(Transaction Isolation Level)
여러 트랜잭션이 동시에 처리될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 여부를 결정하는 것이다.
격리(고립) 수준이 높은 순서대로 SERIALIZABLE, REPEATABLE READ, READ COMMITTED, READ UNCOMMITED가 존재한다.

트랜잭션 격리수준

만약 격리 수준이 따로 설정되어 있지 않은 데이터베이스라면, SELECT의 중복으로 인해 중복 데이터가 전송될 수 있습니다. 이를 막기 위해 트랜잭션 단위의 작업으로 쿼리 수행을 수정할 수 있습니다. 현재 부스타 DB에 적용되어 있는 REPEATABLE READ에 대한 조금 더 자세한 설명을 덧붙여보자면 아래와 같습니다.

REPEATABLE READ는 MySQL의 InnoDB 스토리지 엔진에서 기본으로 사용되는 격리 수준입니다. Inno DB 스토리지 엔진은 트랜잭션이 Rollback 될 가능성에 대비해 변경되기 전 레코드를 언두(Undo)공간에 백업해두고 실제 레코드 값을 변경합니다. 동일한 레코드에 대해 여러 버전의 데이터가 존재하는 이러한 변경 방식을 MVCC(Multi Version Concurrency Control, 다중 버전 동시성 제어)라고 합니다. MVCC를 통해 트랜잭션이 롤백된 경우에 데이터를 복원할 수 있을 뿐만 아니라, 서로 다른 트랜잭션 간에 접근할 수 있는 데이터를 세밀하게 제어할 수 있습니다.

REPEATABLE READ 방식을 이해하기 위해 쉬운 예시를 들어 설명해 보자면 아래와 같습니다.

  1. 트랜잭션 A가 실행 중입니다.
  2. 트랜잭션 A가 시작한 후, 트랜잭션 B가 같은 데이터를 변경합니다.
  3. 트랜잭션 A는 트랜잭션 B가 데이터를 변경했는지 알 수 없습니다.
  4. 트랜잭션 A가 끝날 때까지 같은 데이터를 조회하면 항상 같은 결과가 반환됩니다.

위 표의 괄호 속 설명과 같이, Inno DB 스토리지 엔진에서는 REPEATABLE READ 수준에서 MVCC와 갭 락으로 인해 PHANTOM READ가 발생하지 않습니다.

위의 예시를 한 번 더 활용해 이번에는 갭 락에 대해 설명해보도록 하겠습니다.

갭 락(Gap lock) : 레코드 자체가 아니라 레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 것. 레코드와 레코드 사이의 간격에 새로운 레코드가 INSERT 되는 것을 제어하는 것이다.

1번 과정에서 트랜잭션 A가 실행되면 Inno DB에서는 해당 쿼리를 실행하면서 레코드 사이의 간격에 갭 락이 실행됩니다. 트랜잭션 B는 트랜잭션 A와 같은 데이터를 변경(예를 들어 삽입인 경우)하려 하지만 갭 락으로 인해 해당 범위에 새로운 레코드를 삽입할 수 없습니다. 트랜잭션 A가 종료될 때까지 새로운 레코드가 삽입되지 않았기 때문에 조회 결과는 변하지 않았습니다.
=> PHANTOM READ 발생 X

출고 프로세스를 운영하며

WMS 출고 프로세스는 고객과 직접적으로 연결되는 데이터를 다루기 때문에 데이터의 무결성(Integrity)정합성(Consistency)이 더욱 중요합니다. 이 두 가지 속성이 지켜지지 않았을 때 금전적 손해로 직결될 수 있기 때문에 더욱 책임감을 가지고 프로세스를 운영하고 있습니다. 출고 과정에서 예상치 못한 이슈들이 발생할 때면 마음이 조급해지기도 합니다. 하지만 반대로 생각하면 또 하나의 예외 처리가 추가되어 보다 견고한 프로세스가 만들어지고 있다는 생각에 뿌듯하기도 합니다.

WMS 시스템에 주문 데이터를 예쁘게 전달하는 프로세스를 운영하다 보니 우리가 전달한 데이터를 기반으로 하여금 실제 창고에서는 어떤 플로우로 상품을 피킹하고 출고하는지 궁금해졌습니다.
그래서 저희 Tech 팀 전원은 WMS 창고로 견학을 다녀왔습니다!!!
이번 글이 주문이 출고되는 과정에서 주문 데이터의 정합성에 대해 작성한 글이라면, 다음 글에서는 Tech 팀의 창고 견학을 토대로 실제 창고에서 주문이 출고되는 현장에 대해 풀어볼 예정입니다. ‘주문이 출고되기까지의 과정’ 2편을 기다려주세요! 그럼, 안녕~

김승언
김승언
부스터스 Tech팀에서 백엔드와 스몰토크를 담당하고 있습니다. 데이터 서빙과 정제에 관심이 많습니다.