돌아가기

차곡차곡

부품 입출고, 발주, 창고 현황을 관리하는 WMS 서비스

ReactTypeScriptStyled ComponentsThree.jsTanStack QueryZustandDockerFCMPuppeteer

부품 재고 관리 업무에서 입고, 출고, 발주, 창고 현황 확인 흐름을 웹으로 관리할 수 있도록 만든 WMS 서비스입니다. 바코드 기반 입출고 흐름과 랙 상세 적재 상태 표시를 구현하고, 팀 개발 품질을 위해 Git/Husky 자동화와 프론트엔드 컨벤션을 정비했습니다.

기간
2025.01 ~ 2025.02
팀 구성
프론트엔드 3명, 백엔드 3명
역할

입출고/부품/창고 상세 페이지, 부품 발주, FCM 알림 구현을 통해 재고 관리 주요 업무를 웹에서 처리할 수 있는 흐름 구성

바코드 생성/출력 기능과 안드로이드 스캐너 앱을 개발해 부품 입출고 과정의 효율성 향상

Husky 설정을 통해 이슈 번호 기반 커밋 메시지 관리와 커밋 전 품질 검사 흐름 자동화

Puppeteer로 robots.txt를 준수해 허용된 페이지만 크롤링하고, 약 7,000개 부품 데이터로 서비스 시연 및 초기 운영에 필요한 데이터 기반 마련

핵심 데모

전체 서비스 흐름

문제 정의

프로젝트 배경

WMS 도메인을 주제로 진행한 팀 프로젝트입니다. 일반적인 WMS의 입출고, 발주, 재고 수량 관리 기능에 더해, 창고 내 재고 위치와 적재 상태를 3D로 시각화하는 데 초점을 두었습니다. 이를 통해 관리자가 현장에 직접 가지 않아도 오피스에서 창고의 위치 기반 재고 상태를 파악할 수 있는 흐름을 목표로 했습니다. 또한 입출고 과정의 수동 입력 부담을 줄이기 위해 바코드 생성/출력 기능과 안드로이드 바코드 스캐너 앱을 함께 구성했습니다. 재고 확인은 3D 시각화로, 실제 입출고 처리는 바코드 스캔으로 연결해 WMS의 사용 흐름을 더 현실적인 형태로 설계했습니다.

해결 목표

입고, 출고, 발주 요청과 처리 상태를 웹에서 일관되게 확인할 수 있는 업무 흐름 제공. 부품의 위치, 적재 상태, 재고 현황을 한눈에 파악할 수 있는 창고 관리 화면 제공. 바코드 기반 품목 식별을 통해 입출고 과정의 수동 입력 부담 완화.

서비스 구조

시스템 아키텍처

웹 프론트엔드, 백엔드 API, FCM, 배포 환경, 안드로이드 스캐너 앱이 어떤 경계로 연결되는지 한눈에 볼 수 있도록 정리했습니다. 포트폴리오 본문에서는 개별 화면보다 먼저 전체 시스템의 책임 분리를 보여주는 위치가 더 자연스럽다고 판단해 핵심 구현 앞에 배치했습니다.

시스템 아키텍처
차곡차곡 시스템 아키텍처

핵심 구현

운영 대시보드

관리자가 서비스에 들어왔을 때 입고, 출고, 발주, 창고 현황을 빠르게 파악할 수 있도록 대시보드 화면을 구성했습니다. WMS의 반복 업무 흐름을 한곳에서 시작할 수 있게 주요 지표와 접근 경로를 우선 배치했습니다.

운영 대시보드
차곡차곡 대시보드 화면

바코드 기반 입출고 흐름

입출고 업무에서는 품목 코드와 수량을 사람이 직접 확인하고 입력하는 과정에서 시간이 오래 걸리고 입력 실수가 생길 수 있었습니다. 이를 줄이기 위해 웹에서 바코드 라벨을 출력하고, 현장에서는 안드로이드 스캐너 앱으로 라벨을 읽어 출고 피킹 API까지 연결되는 흐름을 구성했습니다. 웹에서는 react-barcode로 품목 코드를 바코드로 렌더링하고, 품목명, 품목코드, 카테고리, 수량이 함께 보이는 라벨을 인쇄했습니다. 스캐너 앱은 카메라 프리뷰 중앙 가이드 영역 안에 바코드 중심점이 들어왔을 때만 인식하도록 제한했습니다. 인식 성공 후에는 1초 디바운스와 처리 중 플래그로 중복 호출을 막아, 수동 품목 코드 입력을 스캔 1회로 대체할 수 있도록 했습니다.

웹 바코드 라벨 출력
차곡차곡 바코드 출력 흐름

안드로이드 바코드 스캔

출고 피킹 앱은 CameraX의 ImageAnalysis로 카메라 프레임을 실시간 분석하고, 바코드가 가이드 영역에 들어오면 피킹 결과를 반환하는 구조였습니다. 하지만 ImageAnalysis는 초당 여러 프레임을 분석 콜백으로 전달하기 때문에, 바코드가 잠깐 영역 안에 머무르는 동안 여러 프레임이 모두 인식 조건을 통과할 수 있었습니다. 이 상태에서는 한 번 스캔했는데 결과 반환과 출고 피킹 처리가 여러 번 실행될 수 있었고, 단순 UI 중복이 아니라 재고 데이터 정합성 문제로 이어질 수 있었습니다. 그래서 "한 번의 스캔은 한 번만 처리된다"는 기준을 먼저 정하고, 첫 유효 프레임이 들어온 뒤에는 후속 프레임을 무시하도록 처리 상태를 관리했습니다. 인식 성공 시 처리 중 상태를 켜고 짧은 디바운스를 둔 뒤 결과를 반환하도록 정리해, 같은 바코드가 여러 프레임에서 연속으로 감지되더라도 피킹 처리는 한 번만 실행되도록 안정화했습니다. 결과 반환과 사용자 피드백은 메인 스레드에서 처리하도록 조정해 처리 완료 시점과 안내 메시지가 어긋나지 않게 했고, 카메라 권한 허용 직후에는 추가 버튼 클릭 없이 바로 스캔 화면으로 이어지도록 흐름도 함께 개선했습니다.

안드로이드 스캐너 인식 화면
안드로이드 바코드 스캐너 인식 화면

2D 랙 상세 적재 상태 시각화

창고 현황 화면에서 관리자가 실제로 판단해야 하는 정보는 "어느 랙의 어느 층에 여유 공간이 있는가"였습니다. 이를 위해 특정 랙을 선택한 뒤 확인하는 2D 상세 정보 화면을 맡아, 랙의 층별 적재율과 품목 정보를 한 화면에서 비교할 수 있게 구성했습니다. 랙의 층별 적재율을 25%, 50%, 75% 기준으로 나누어 색상으로 표시하고, 각 층에는 카테고리, 품목명, 수량 또는 비어있음 상태를 함께 보여주었습니다. 단순 수치만 보여주면 관리자가 다시 해석해야 하므로, 색상으로 여유 공간과 과적재 위험을 먼저 인지하고 상세 텍스트로 확인하는 흐름을 만들었습니다.

2D 창고 현황
차곡차곡 2D 창고 현황

3D 창고 현황

창고 내 재고 위치와 적재 상태를 3D로 표현해, 현장에 가지 않아도 창고 전체 배치를 직관적으로 파악할 수 있도록 했습니다.

3D 창고 현황
차곡차곡 3D 창고 현황 화면

발주 흐름과 FCM 알림

발주 요청은 공급업체, 창고, 품목, 수량, 납기일 중 하나만 빠져도 후속 업무가 지연될 수 있는 흐름입니다. 그래서 사용자가 발주 정보를 입력하는 단계에서 누락과 중복을 줄이고, 요청 이후에는 알림으로 상태 변화를 놓치지 않게 하는 것을 목표로 잡았습니다. 발주 폼은 공급업체와 창고를 선택한 뒤 품목을 검색해 추가하고, 수량 변경에 따라 합계와 총 발주금액이 즉시 계산되도록 구현했습니다. 동일 품목을 다시 추가하면 행을 중복 생성하지 않고 수량을 합산했으며, 필수값 검증과 최소 납기 요청일 제한을 두어 잘못된 발주 요청을 사전에 줄였습니다. 알림은 Firebase Cloud Messaging을 웹 앱에 통합해 앱 사용 중에는 toast와 알림 목록 갱신으로, 백그라운드에서는 서비스워커의 시스템 알림으로 전달했습니다.

발주 요청 화면
차곡차곡 발주 화면
FCM 알림 흐름
차곡차곡 FCM 알림 화면

프론트엔드 설계

화면 구조와 공통 컴포넌트

WMS는 입고, 출고, 품목, 창고처럼 도메인은 다르지만 "목록 확인 → 조건 검색 → 상세 확인 → 처리/수정" 흐름이 반복되는 화면이 많았습니다. 화면마다 비슷한 UI를 새로 만들면 구현 속도는 빨라 보여도, 상태 표시 방식과 사용자 피드백이 화면마다 달라질 수 있다고 판단했습니다. 이를 줄이기 위해 상세 정보는 InfoBox로, 바코드 출력과 품목 상세처럼 화면 위에 열리는 작업은 Zustand 기반 전역 Modal로 분리했습니다. 모달의 열림 상태와 크기, 제목 같은 공통 옵션을 한곳에서 다루도록 해 화면마다 모달 구조를 다시 만들지 않게 했고, Header 알림, 테이블, 필터, 상세 정보 블록도 역할별로 나누어 반복되는 업무 화면의 구조를 일관되게 유지했습니다.

데이터 패칭과 상태 관리

품목 관리처럼 조회, 수정, 삭제가 이어지는 화면에서는 서버에서 받은 데이터와 사용자가 편집 중인 값을 같은 상태로 다루면 저장 전 변경사항이 화면 전체에 섞일 수 있습니다. 그래서 서버 상태는 TanStack Query로 관리하고, 수정 모드의 입력값은 로컬 form state로 분리했습니다. 쿼리 키는 화면마다 문자열로 직접 작성하지 않고 한곳에서 선언한 값을 참조하도록 정리했습니다. 이를 통해 목록, 상세, 검색 조건처럼 서로 연결된 데이터의 캐시 범위를 명확히 하고, 수정이나 삭제가 성공했을 때 어떤 데이터를 다시 가져와야 하는지 일관되게 관리했습니다. 상세 조회처럼 자주 바뀌지 않는 데이터는 `staleTime`을 조정해 불필요한 재요청을 줄였습니다.

협업과 자동화

개발 품질과 작업 추적 자동화

프론트엔드 3명이 동시에 작업하면서 코드 스타일, import 순서, 커밋 메시지 형식이 사람마다 달라지면 리뷰할 때 변경 내용보다 형식 문제에 시간을 쓰게 됩니다. 이를 줄이기 위해 커밋 전에 자동으로 확인할 수 있는 기준을 만들고, 작업 이력을 이슈 단위로 추적하기 쉬운 형태로 정리했습니다. Husky의 pre-commit 단계에서는 staged 파일에 ESLint와 Prettier를 적용해 커밋 전에 기본적인 코드 스타일을 맞췄고, import 정렬 규칙도 함께 적용해 파일마다 import 순서가 달라지는 문제를 줄였습니다. prepare-commit-msg 단계에서는 브랜치명에 포함된 이슈 번호를 커밋 메시지 앞에 자동으로 붙여, 커밋 로그만 봐도 어떤 작업과 연결된 변경인지 확인할 수 있게 했습니다. Jira와 Git을 연동해 이슈 번호가 포함된 브랜치를 만들면 작업 상태가 갱신되고, 브랜치가 병합되면 완료 상태로 이어지도록 설정해 팀원이 수동으로 상태를 바꾸는 부담을 줄였습니다.

결과와 회고

성과

WMS 프로젝트 우수상을 수상하며 서비스의 완성도를 인정받을 수 있었습니다. 특히 창고 재고를 3D로 확인하는 시각화 흐름과, 웹 바코드 출력 및 스캐너 앱을 활용한 입출고 처리 흐름이 프로젝트의 주요 강점으로 평가되었습니다. 3D 창고 화면은 재고 위치와 적재 상태를 직관적으로 보여주어 WMS의 핵심 업무인 재고 파악을 더 쉽게 만들었고, 바코드 기능은 품목 코드를 직접 입력하던 과정을 스캔 중심의 흐름으로 바꾸어 입출고 처리의 현실감을 높였습니다. 개발 과정에서는 팀 컨벤션을 초기에 정리하고 지속적으로 지키는 것을 목표로 삼았습니다. FSD 기반 디렉토리 구조, 공통 컴포넌트, 커밋 메시지 규칙, 코드 포맷팅 기준을 맞춰두어 도메인 학습과 구현을 병행해야 하는 일정 속에서도 코드 스타일 차이로 인한 조율 비용을 줄이고 완성도를 유지할 수 있었습니다.

배운 점과 다음 개선

프로젝트를 시작할 때는 WMS 도메인에 대한 이해가 부족했기 때문에, 먼저 입고, 출고, 발주, 재고 관리가 어떤 흐름으로 이어지는지 팀원들과 함께 조사했습니다. 이 과정을 통해 모든 팀원이 같은 업무 흐름을 이해한 상태에서 화면과 기능을 설계할 수 있었고, 개발 중에도 큰 의사소통 비용 없이 하나의 플로우를 맞춰갈 수 있었습니다. 도메인을 학습하는 데 시간이 필요해 개발 일정은 촉박했지만, 초기에 팀 컨벤션을 정리해둔 덕분에 구현 단계에서는 속도를 낼 수 있었습니다. 3D 창고 화면에 필요한 에셋도 특정 담당자에게만 맡기지 않고 팀원 모두가 후보를 찾고 검토하면서, 서비스에서 표현하려던 창고 환경에 맞는 에셋을 더 빠르게 확보할 수 있었습니다. 아쉬웠던 점은 FSD 구조를 처음 사용하면서 초반에 디렉토리 기준을 정하고 기능 위치를 판단하는 데 시간이 많이 들었다는 점입니다. 프로젝트 막바지에는 개발 속도를 우선하다 보니 일부 구조가 FSD의 참조 방향과 맞지 않는 부분도 생겼습니다. 다음에는 초기 구조를 정하는 것에서 끝내지 않고, 개발 중간중간 리팩토링을 통해 더 완성도 높은 구조로 다듬어가고 싶습니다. 반대로 프로젝트 규모가 커질수록 FSD가 기능 단위의 책임을 분리하는 데 유용한 아키텍처라는 점도 알게 되었습니다. 쿼리 키 역시 처음에는 잘못된 키를 작성해 캐시 갱신이 의도대로 되지 않는 실수를 겪었지만, 이후 한곳에서 관리하도록 정리하면서 조회, 수정, 삭제 이후의 데이터 갱신 흐름을 더 안정적으로 관리할 수 있었습니다.