본문으로 건너뛰기
윤승규
프로젝트 목록
진행중 (V1 운영 전환 완료, V2 진행)혼합

더맛있는하루 ERP - 수입 축산물 유통 통합 ERP

축산물·수산물 수입/도매 회사의 SQL Server 레거시 ERP(92개 테이블)를 Next.js + Prisma 기반 웹 ERP로 전면 재구축 — 수입(오퍼→선적→통관→원가배부)부터 주문·재고·출고·입금매칭·정산까지 전 업무를 하나로 연결하고, 은행/냉동창고/관세청/팝빌을 자동 연동한 실운영 시스템

기간
2026.03 ~ 현재
역할
풀스택 개발 (최다 기여) · 레거시 마이그레이션/QA 담당
구분
(주)더맛있는하루 (SI 외주)
스택
Next.js 16, React 19, TypeScript 5, Prisma 7
더맛있는하루 ERP - 수입 축산물 유통 통합 ERP

프로젝트 배경

배경 & 목적

(주)더맛있는하루는 우육·돈육·계육 등 냉동 축산물을 해외에서 수입해 국내 도매로 유통하는 무역·유통 회사다. 기존에는 SQL Server 기반 레거시 ERP(92개 테이블, FK/주석 부재)를 쓰면서도 실제 업무의 상당 부분이 시스템 밖에 있었다.

  • ▶수입 업무는 메일로 계약서를 받아 엑셀 오퍼시트/송금리스트로 관리
  • ▶출고지시서·거래명세서는 팩스 수기 발송, B/L 문서는 구글 드라이브에 산재
  • ▶입금 확인은 은행 문자를 보고 수기 대조
  • ▶레거시는 LOT 중심 구조인데 실제 업무는 B/L(선하증권) 중심으로 동작

이를 해결하기 위해 레거시 1:1 복제를 금지하고 업무 도메인 기준으로 재설계하는 원칙 아래, 수입-통관-입고-판매-정산 전 과정을 웹 ERP 하나로 연결하는 것이 목표였다. 핵심 전환: ① LOT 중심 → B/L 중심 재설계 ② 재고 차감 시점을 출고완료 → 주문 입력 즉시로 이동 ③ 팩스/카카오톡/메일/OCR 문서 업무 자동화 ④ 은행·창고·관세청 외부 데이터 자동 수집.

내 역할과 기여도

  • ▶팀 구성: 개발 6인 (git 기여자 기준. 외부연동 2, 창고 연동 1, 기본 기능 2, 마이그레이션/QA 겸임)
  • ▶커밋 기여: 전체 2,494건 중 1,278건 (약 51%, 최다 기여)
  • ▶담당 범위 (커밋/문서 근거):
    • ▶주문 도메인 메인 — 주문 칸반 보드, 신규 주문 다단계 위저드, 주문 수정/취소, 출고지시(HWPX 문서 생성 포함)
    • ▶입금 매칭 — 입금자명 자동 매칭, 수금 FIFO 배분, 은행 수집 공급자 전환(Popbill → Poooling Connect)
    • ▶레거시 데이터 마이그레이션 — MSSQL → PostgreSQL 이관 스크립트, 정합성 진단 도구
    • ▶고객사 테스트 계획/QA, 모바일 UI(바텀시트 주문 상세 등), 전표 네이티브 이미지 공유(카카오)
  • ▶의사결정 참여: 요구사항 범위 조율(V1/V2 스코프 분리), 데이터 동기화 리스크 사전 고지 등 고객 커뮤니케이션

핵심 구현

1. 수입(무역) 파이프라인

오퍼(견적) → 발주 → 선적/컨테이너 → 통관 → 입고검수 → 부대비용 배부 → 원가 확정까지 전 과정 관리. 관세청 UNI-PASS 폴링으로 통관 진행 자동 추적, FOB/운임/관세/보험 등 부대비용을 수량/중량/금액 기준으로 LOT별 배부해 착지 원가 산출.

2. B/L 중심 재고 관리

레거시 LOT 구조를 B/L(선하증권) 중심으로 재설계. InventoryLedger 원장을 유일한 진실 원천으로 두고 Balance는 파생 집계로만 사용. 자사재고/공급자소유(위탁) 구분, 창고 간 이동, 재고조정, 미착 재고 배정까지 지원.

3. 주문 관리 (칸반 + 다단계 위저드)

거래처→품목→B/L·단가 배정→배차→가전표로 이어지는 7단계 위저드로 주문 생성. 8단계 DB 상태머신과 별개로 현업이 원하는 3열 칸반은 파생 stage 계산 함수로 구현. 한 주문에 자사재고 라인과 사매입(라인별 공급처 상이) 라인 혼합 지원. 재고 차감은 주문 입력 즉시.

4. 은행 입금 자동 매칭 & 정산

3분 주기 은행 거래 수집(멱등 적재) → 입금자명 정규화·점수제 자동 매칭 → 거래처별 FIFO 배분 → 입금 충족 시 후속 자동화(출고지시). 거래처 원장(미수/미지급), 매출 인식 규칙 4종(출고/완납/입금/계산서 시점), 월 정산·익월 10일 세금계산서 발행 관행 반영.

5. 문서 업무 자동화

출고지시서 HWPX(한글 OWPML 직접 조립) 생성, 헤드리스 Chromium PDF 렌더, 팝빌 팩스 예약발송/실패 자동 재시도, 카카오톡 네이티브 이미지 공유(전표·세금계산서·단가표 등 10+ 화면), 토큰 기반 무인증 외부 공유 링크, 전표 버전 스냅샷 관리.

6. OCR 파이프라인 (무역 서류 6종)

오퍼시트·사매입 전표·B/L·검역증·단가표·사업자등록증을 OpenAI Structured Outputs로 인식. 모든 필드가 {value, confidence, rawExcerpt} 구조로 반환되어 저신뢰 필드를 UI에서 강조하고 사람이 검수. 사매입 전표는 OCR 결과의 B/L번호·이력번호를 실제 재고 LOT과 자동 매칭(등록→확인→확정 2단계).

7. 냉동창고 WMS 크롤링 (Python 워커)

거래 냉동창고 5곳의 서로 다른 WMS에 로그인해 재고/수불/계근 3채널을 크롤링(크롤러·파서 15종), ERP DB에 적재. 계근 실중량이 들어오면 주문을 자동 출고 전환하고 금액을 재계산.

8. 시황/환율 데이터

USDA PSD, 관세청 무역통계, 환율, 축산물 시세, RSS 뉴스를 주기 수집해 시황 분석 화면 제공.

9. 운영 편의

Alt 단축키 시스템(IME/모달 오발화 방지), 사용자별 테이블 컬럼 커스터마이즈, 모바일 바텀시트 주문 상세, 미트박스(B2B 오픈마켓) 매출 엑셀 대량 취합(멱등 커밋+롤백 스크립트), 메뉴 단위 권한 오버라이드, 전 API 감사 로그, MDX 인앱 매뉴얼.


성과

트러블슈팅

[!important] 면접용 STAR 기법 — 코드/문서에서 확인되는 실제 사례

사례 1: 한글 입금자명 자동 매칭 — 오매칭 장애를 점수제 + 별칭 학습으로 재설계

상황 (Situation) 은행은 입금자명을 10자 안팎으로 자르고, 앞에 은행명을 붙이며, 전각 문자를 섞어 보낸다. 초기 매칭 규칙은 "글자가 포함되면 후보"였는데 길이 하한이 없어, 한 거래처 이름 안에 포함된 다른 거래처의 3글자가 걸려 엉뚱한 거래처로 자동 매칭되는 실제 오매칭이 발생했다 (코드 주석에 사례 기록).

과제 (Task) 오매칭은 미수금/미지급금 장부를 오염시키므로, "틀리게 매칭하느니 매칭하지 않는" 방향으로 자동 매칭 엔진을 재설계해야 했다.

행동 (Action)

  • ▶normalizePartnerKey: 은행명 접두어 제거 + 전각→반각 정규화 (web/src/lib/popbill/auto-match.ts)
  • ▶겹침 종류/길이별 점수제 도입: 정확일치 3점 / 접두일치(3자+) 2점 / 중간포함(4자+) 1점 — 최고점이 유일할 때만 매칭, 동점이면 중단
  • ▶수동 매칭 결과를 BusinessPartnerRemitterAlias로 학습 → 이후 exact-match 우선
  • ▶별칭 충돌(같은 별칭에 거래처 2곳) 시 ALIAS_CONFLICT로 수동검토 전환 + 담당자 실시간 알림 + 매칭 화면 딥링크, 충돌 시나리오 seed 스크립트로 테스트
  • ▶매입 전용 거래처 입금은 매입환입 오분기를 막기 위해 SUPPLIER_ONLY_DEPOSIT 수동 확인으로 분리
  • ▶크론은 최근 3일 재조회 + source 복합키 멱등 적재 + advisory lock으로 수동 실행과 직렬화

결과 (Result)

  • ▶은행 문자 수기 대조 업무가 자동 매칭 + 예외만 수동검토하는 구조로 전환
  • ▶FIFO 자동 배분 시 사용자 입력 단가를 덮어쓰던 버그 등 실사용 테스트 이슈를 수정하며 안정화
  • ▶은행 수집 공급자도 Popbill → Poooling Connect로 이중 수집·비교 페이지를 거쳐 무중단 전환

사례 2: 영업사원 15명 동시 주문 — Redis Lua 기반 실시간 재고 예약(티켓팅)

상황 (Situation) 같은 B/L 재고를 영업사원 15명 이상이 동시에 보면서 주문한다. A가 100박스를 입력하는 순간 B의 화면 가용수량이 즉시 줄어야 하고, 재고보다 많이 파는 오버부킹은 절대 발생하면 안 됐다 (재고 차감 시점을 "주문 입력 즉시"로 옮긴 설계의 전제 조건).

과제 (Task) 콘서트 티켓팅과 동일한 동시성 문제를 B2B 도매 주문 화면에서 해결해야 했다.

행동 (Action)

  • ▶Redis Lua 스크립트로 예약 원자성 확보: reservation:{id}(TTL 15분) + B/L별 예약 카운터 + 사용자별 예약 Set
  • ▶화면 가용수량 = DB availableQuantity − Redis 예약 합산, Socket.IO로 전 사용자 실시간 갱신
  • ▶TTL 없는 카운터는 별도 보정 job으로 정리, 세션 종료 시 해당 사용자 예약 일괄 해제

결과 (Result)

  • ▶다중 영업사원 환경에서 오버부킹 없이 "주문 입력 즉시 차감" 정책 운영 가능
  • ▶설계 문서(03-도메인/실시간-재고예약-시스템.md) 기반 2026-04-06 구현 완료

사례 3: 창고 크롤링 — 전역 폴링을 이벤트 기반 활성 큐 + 서킷브레이커로 전환

상황 (Situation) 초기 구조는 활성 주문이 1건이라도 있으면 전체 창고 WMS를 주기 크롤해, 타사 시스템 부하·세션 만료·로그인 차단 위험이 있었다. 창고마다 인증 방식과 HTML 구조도 전부 달랐다.

과제 (Task) 필요한 창고만, 필요한 시점에, 차단당하지 않게 크롤하는 구조로 바꿔야 했다.

행동 (Action)

  • ▶ActiveWarehouseQueue 테이블 도입: 주문 상태 전환(출고지시)과 같은 Prisma 트랜잭션 안에서 창고를 큐에 upsert/제거해 상태-큐 정합성 보장 (web/src/lib/warehouse-queue.ts)
  • ▶워커는 5분 tick과 Redis warehouse-queue:activated 신호 중 먼저 오는 쪽에 깨어남 — 주문 즉시 반영 + 유휴 시 저비용 (storage/src/storage/scheduler/active_queue_runner.py)
  • ▶채널별(수불/계근) 연속 실패 3회 시 서킷브레이커 오픈, 복구는 운영자 API 리셋으로만 — 차단 상태에서의 무한 재시도 방지
  • ▶connectors/parsers/sinks 계층 분리로 신규 창고 추가 시 파일 3개만 작성하면 되는 구조, 창고별 크롤 타임아웃·동시성 세마포어 제한
  • ▶크롤 결과 기반 출고 "자동 전환"은 오탐 리스크를 검토해 의도적으로 reconcile 데이터 축적으로 후퇴시키는 결정도 병행

결과 (Result)

  • ▶ECS Fargate 상시 1태스크로 Playwright 세션을 재사용하며 5개 창고 × 3채널 안정 수집
  • ▶계근 실중량 수신 → 주문 자동 출고 전환 → 금액 재계산 파이프라인의 데이터 공급원으로 동작

사례 4: 계근 실중량 기반 자동 출고 + 중량 정산 (축육 도메인 특화)

상황 (Situation) 축산물(비규격품)은 주문 시점의 견적중량과 실제 출고 계근중량이 다르다. 실중량이 확정되어야 매출액이 확정되고, 차액은 거래처와 정산해야 한다. 기존에는 이 과정이 전부 수기였다.

과제 (Task) 크롤링된 계근/수불 데이터를 주문과 자동 대사하고, 금액 재계산과 차액 정산을 시스템화해야 했다.

행동 (Action)

  • ▶계근 우선, 없으면 수불 출고량을 쓰는 B/L 번호 기준 OR 매칭 구현 (web/src/lib/sales-order/movement-match.ts)
  • ▶전 라인 매칭 시 SHIPPED 자동 전환, 부분 매칭은 partiallyShipped로 별도 추적 (sales-order-auto-ship.ts, 917줄)
  • ▶라인 금액 → 주문 합계 → 매출 동기화로 이어지는 재계산 체인 구성, 견적/실적 이중 금액 축 유지
  • ▶차액은 SalesOrderWeightSettlement/Adjustment/Resolution 3단 모델로 추가청구/환불/수금상계 관리
  • ▶자동 팩스 발송은 오매칭 시 "회수 불가능한 팩스"가 나가는 리스크 때문에 의도적으로 비활성화(AUTO_FINAL_INVOICE_FAX_ENABLED=false) — 이행 자동/발송 수동/마감 수동 3축 분리, Redis 락으로 크론-수동 중복 방지

결과 (Result)

  • ▶창고 데이터 수신부터 출고 전환·금액 확정까지 자동화하되, 대외 발송만 사람이 확인하는 안전한 자동화 경계 확립
  • ▶실중량 확정(FINALIZED) 시 확정매입 자동 생성으로 비규격품 정산 업무 연결