-
0. 들어가며
안녕하세요.지금부터 8개월간 준비하고 게시한 WMS 서비스를 소개하려 합니다.
저희 회사 풀필먼트 개발팀이 WMS를 오픈하기까지의 내용과 개발적인 도전에 대한 내용들의 설명이며 프로젝트를 끝내고 돌아보며 회고하기 위해서 작성한 글입니다.- WMS란?
- WMS 개발 이전에 회사는 어떻게 재고 관리를 하고 있었을까요?
- 내제화
- 개발적인 도전
- 마무리
1. WMS란?
WMS(Warehouse Management System, 창고 관리 시스템)는 물류센터의 전반적인 작업을 시스템화한 것입니다.
반복되는 수작업을 시스템화하여 효율적으로 운영할 수 있도록 돕습니다.
물류센터 내 다양한 작업을 시스템화하고 있으며, 재고 및 상품 관리와 같은 작은 작업에서부터 입고 및 출고와 같은 큰 작업까지 창고 내의 모든 작업을 총괄합니다.
2. WMS 개발 이전에 회사는 어떻게 재고 관리를 하고 있었을까요?
엑셀
회사의 초기 단계에서는 엑셀을 사용하여 재고 관리를 했습니다.
당시에는 사업의 규모가 크지 않았고, 작업자가 관리할 수 있는 정도의 규모였습니다.
엑셀은 정말 잘 만든 프로그램으로, 재고를 쉽고 편리하게 관리할 수 있었습니다.
그러나 서비스가 커짐에 따라 사용자의 피로도가 증가했고, 창고에는 더 많은 기능을 다루는 전문적인 솔루션이 필요하게 되었습니다.외부 솔루션
개발팀과 운영팀은 단기간에 WMS를 개발할 수 없다고 판단해, WMS 개발 전까지 외부 솔루션을 사용하기로 결정했습니다.
외부 솔루션의 단점- 고정 지출: 외부 솔루션의 단점 중 하나는 매달 고정 지출이 발생한다는 점이었습니다. 특정 서비스를 추가할 때마다 수십만 원을 지불해야 했습니다.
- 장애 발생 시 대처 불가능: 외부 솔루션에 장애가 발생하면 저희 팀은 할 수 있는 것이 없었습니다. 모든 로케이션 및 재고 정보가 외부 솔루션에 묶여 있어, 외부 솔루션의 장애는 회사의 장애로 이어졌습니다. 이에 따라 장애를 대비하는 별도의 개발이 필요했고, 이는 개발 속도를 늦추는 원인이 되었습니다.
- 커스터마이징 불가능: 가장 큰 단점은 회사 프로세스에 맞게 커스터마이징 할 수 없다는 것이었습니다. 식자재 당일 배송이라는 회사 도메인 특성상 타 회사와 다른 많은 제약 조건이 있었습니다. 예를 들어, 재고가 부족해도 어떻게든 물건을 구해 출고해야 한다는 점입니다. 이러한 문제로 기존 시스템의 기능에 억지로 맞추다 보니 현장에서는 불필요한 작업이 증가했고, 작업의 순서가 꼬이게 되면 출고를 할 수 없는 상황에까지 이르렀습니다.
많은 단점으로 인해 개발팀과 현장 모두 내재화를 최우선 목표로 삼았습니다.
3. 내재화
'실용주의 프로그래머'
사용자처럼 생각하기 위해 사용자와 함께 일하라
먼저 현장이 어떻게 일하는지, 무엇이 불편한지를 직접 체험했습니다.물건을 나르고 움직이면서 현장 직원들과 유대감을 쌓을 수 있었고, 무엇이 필요한지, 어느 부분이 불편한지를 어렴풋이 알 수 있었습니다.
내재화 시작
이전 TMS 프로젝트가 끝난 후 바로 WMS 프로젝트에 착수했습니다.
기획 단계와 설계 단계를 거치면서 외부 솔루션을 먼저 도입한 것에 따른 단점들이 나타났습니다.
외부 솔루션은 여러 회사를 상대하다 보니 많은 기능이 포함되어 있었고, 현장은 이미 이 기능들에 익숙해져 있었습니다.
따라서 저희의 프로젝트가 외부 솔루션을 대체하려면 현장에서 사용하고 있는 모든 기능을 동일하게 구현해야 한다는 것을 의미했습니다.
프로젝트의 규모는 생각보다 커지게 되었습니다.
많은 기능을 아사나(협업도구)를 통해 스프린트 단위로 계획하며 작업했습니다.배포
현장과 타 서비스 개발팀의 도움으로 성공적으로 WMS를 오픈할 수 있었습니다. 한 달여간의 현장 테스트 덕분에 큰 이슈 없이 시스템이 동작하였습니다.
현장에서는 시스템 작업이 줄어들어 작업자 리소스가 감소했고, 현장에서 원하는 정보를 더 많이 제공할 수 있게 되었습니다.
수치적으로 몇 분이 줄어들었지만, 시스템이 작업자 의존적이지 않게 되었습니다.
현장이 아닌 오피스에서도 개선점이 있었습니다.
외부 솔루션에서 연동이 어려웠던 재고 정보와 로케이션 정보를 내제화할 수 있었습니다.
이 덕분에 오피스 운영 업무를 더 쉽게 할 수 있게 되었습니다.
4. 개발적인 도전
WMS 개발에 들어가기 전에 팀 내부적으로 새로운 도전을 하기를 원했습니다.
여기서 도전이란 단순히 새로운 기술을 도입하는 것이 아니라, 이전 프로젝트에서 아쉬웠던 점과 개선하고 싶은 점들을 나열하여 저희 프로젝트에 도움이 될 만한 것들을 선정하는 것이었습니다.
4-1. Kafka
저희 회사는 MSA 구조를 채택하여 도메인별로 서비스를 나누었습니다.
모든 서비스를 한 곳에 넣는다면 수정 및 신규 개발의 경제성이 떨어질 수 있다고 판단했기 때문입니다.
도메인별로 집중할 수 있도록 하여 생산성과 성공 가능성을 높였습니다.
MSA 구조에서 서비스 간 정보 전달을 위해 API와 이벤트 방식을 적절히 섞어 사용했습니다.
기존에는 메시지 플랫폼으로 Kafka와 SQS를 동시에 사용하고 있었으나, 관리 포인트를 줄이기 위해 하나로 통일하기로 했습니다.
메시지 플랫폼 선택
SQS + SNS 구조와 Kafka는 모두 느슨한 결합과 지연 처리와 같은 결과를 가져올 수 있습니다.
이 둘 중 어느 것을 선택할지 결정하는 것은 굉장히 어려운 과정이었습니다.
SQS + SNS는 쉬운 구성이라는 장점이 있었고, 반면에 Kafka는 회사 내부에서 이미 활발히 사용되고 있었습니다.
또한 Kafka는 과거 메시지 이력 확인 등의 기능도 사용할 수 있었습니다.
위와 같은 이유 등으로 Kafka를 메시지 플랫폼으로 선택되었습니다.
Vector 제거
Kafka와 SQS에 메시지를 발행하기 위해 프로듀서로 Vector를 사용했었습니다
vector는 한국에서는 생소하지만, 로그, 메트릭, 트레이스를 수집하고 변환하여 라우팅하는 역할을 담당합니다.
애플리케이션은 로그만 쌓고, 설정 파일만 Kafka용, SQS용으로 만들어 놓으면 쌓은 로그를 Vector가 각각 퍼블리싱했습니다.
하지만 메시지 발행과 관리를 고려했을 때, 스프링 애플리케이션에 프로듀서 기능을 포함시키는 것이 필요하다고 판단했습니다.
그래서 Vector를 제거하고 Spring Kafka를 도입했습니다.
Spring kafka 도입
Kafka 대응을 위해 회사의 애플리케이션에 Spring Kafka를 도입했습니다.
막상 Spring Kafka를 도입하고 테스트하면서 몇 가지 이슈가 발생했습니다.
- 트랜잭션 매니저 문제: WMS와 연동되는 타 서비스가 Kafka 트랜잭션 매니저를 사용하고 있었습니다. 이벤트 발행에 실패할 경우 재시도를 하지 않아, 이벤트를 받지 못한 WMS에서 출고가 누락되는 문제가 발생했습니다. WMS는 빠른 출고가 이루어져야 했기 때문에, 실패 메시지를 먼저 처리하고 나중에 성공 메시지를 받아도 괜찮도록 멱등성 있게 설계했습니다. 또한, isolation_level을 uncommitted로 수정했습니다.
- 리트라이 토픽 설정 문제: 리트라이 토픽 설정 시 ISR 오류가 발생했습니다. min.insync.replicas=2인데 ISR을 1로 설정한 것이 원인이었습니다. 테스트 설정을 그대로 사용해 발생한 문제였지만, 배포 중에 발견하여 쉽게 고칠 수 있었습니다.
이외에도 Spring Kafka로 변경하면서 리트라이 정책, 메시지 수신 등 여러 정책을 정해야 했습니다.
더 많은 내용은 이전 글에서 다뤘습니다.
4-2. 테스트 코드 도입
이전 프로젝트에서는 테스트 코드가 없었고 수동적인 테스트를 진행했습니다.
프로젝트를 하면서 테스트 코드의 필요성을 절실히 느꼈기에, 새로운 프로젝트에서는 테스트 코드를 도입하고자 했습니다.
팀원들도 테스트 코드 도입에 찬성하여, 전반적인 기본 세팅을 하고 팀원들에게 소개했습니다.기본 세팅은 Testcontainers를 사용하여 실제와 같은 환경을 만들려고 했습니다.
테스트의 구조
인수 테스트는 사용자 스토리(시나리오)에 맞춰 수행하는 테스트이다.
테스트 코드는 가독성을 우선시하여 한글로 작성했고, 체이닝 구조로 더 읽기 쉽게 만들었습니다.
테스트는 대부분 인수 테스트로 구성되었으며, 주로 재고 관련 테스트와 입고, 출고 테스트로 이루어졌습니다.
인수 테스트를 기반으로 테스트 코드를 작성한 이유는 사용자 관점에서 테스트를 진행하기 위해서였습니다.
물류센터에서의 복잡한 작업을 코드로 옮겨 시나리오대로 가동하는지를 확인하고자 했습니다.
이는 차후 유지보수 시 기능의 개발 이유와 행위를 쉽게 유추할 수 있게 도와줄 것이라고 생각했습니다.
이번 프로젝트는 재고 등 정합성을 요구하는 기능들이 많았기 때문에, 테스트 코드를 도입함으로써 더 과감하게 코드를 수정할 수 있게 되었습니다.
4-3. Spring version up
회사는 기본 프로젝트 템플릿을 보유하고 있었지만, 오랜 시간 동안 업데이트되지 않아 Spring Boot의 버전이 오래된 상태였습니다.
내부 보안 라이브러리에서 이슈가 발생했고, 이를 해결하기 위해 Spring Security의 버전을 올려야 했습니다.
Spring Security의 버전을 올리면서 다른 여러 버전도 함께 업데이트해야 했고, 최종적으로 Spring Boot 3까지 버전을 올렸습니다.
4-4. 재고 시스템 Locking
재고 정합성 유지를 위해 Locking에 대해 깊게 공부할 필요가 있었습니다.
먼저, 다른 회사들은 락을 어떻게 사용하는지 리서치를 진행했습니다.- 풀필먼트 입고 서비스팀에서 분산락을 사용하는 방법 - Spring Redisson
- WMS 재고 이관을 위한 분산 락 사용기
- 선물하기 시스템의 상품 재고는 어떻게 관리되어질까?
- MySQL을 이용한 분산락으로 여러 서버에 걸친 동시성 관리
대부분 Redis를 사용해 분산 락을 구현하는 것을 확인했습니다.
Galera Cluster와 Locking
먼저, 저희가 사용하는 데이터베이스에 대해 알아야 했습니다.
저희 회사는 Galera Cluster를 사용하고 있었고, 이에 대해 공부했습니다.
Galera Cluster는 특색 있는 클러스터링 방식으로, 이를 제대로 이해하지 않고 사용하면 안 된다는 것을 깨달았습니다. 재고 시스템에서 데이터 정합성을 유지하기 위해 MVCC와 Locking에 대해 고민했습니다.
Locking
현재 상황에서 어떻게 Lock을 잡아야 하는지, 그리고 재고 Locking에 대해 공부했습니다.
저희의 사용자 수와 트래픽을 고려했을 때, Redis와 같은 추가적인 관리 포인트를 늘리기보다 DB에서 관리하는 Lock을 사용하는 것이 더 적합하다고 판단했습니다.
5. 마무리
이번 회사에서 운 좋게 연달아 중요한 프로젝트를 맡게 되었습니다.
재고를 담당하는 WMS를 개발했고, 틈틈이 TMS 유지 보수도 진행했습니다.
신규 WMS 프로젝트를 시작할 때 기술적으로 새로운 도전을 하고 싶었고, 이를 통해 많은 것을 배웠습니다.
또한, 공부한 내용을 바탕으로 글을 작성하여 공유할 수 있어 좋았습니다.
여러 가지 기술적인 도전과 성장을 통해, 앞으로도 더 나은 시스템을 개발하는 데 기여할 수 있기를 기대합니다.
개발적인 복잡성보다 실제 현장의 복잡성은 더 큽니다.
결국 엔지니어와 기획자의 목표는 이러한 복잡성을 줄이고 기능적으로 해결하는 것입니다.
저희의 노력이 빛을 발하듯, 현장 피드백도 매우 긍정적이어서 만족스러웠습니다.
외부 솔루션 사용 시 매일 밤마다 출고에 대한 불안이 있었으나, 이 부분이 저희 팀을 앞으로 나아가게 만든 원동력이 되었습니다.
Ps. 본문에서 다루지 않은 다른 주제들에 대해서'회고' 카테고리의 다른 글
육각형 개발자 (0) 2024.04.19 2023 상반기 회고 (0) 2023.06.06 성장. (0) 2023.03.14 TMS 프로젝트를 마치고 (0) 2023.03.11 2022 회고 (0) 2023.01.01