AI와 함께 회사 ERP+CRM 운영 OS 만들기 — 고객·계약·차량·CS·메일을 한 화면으로 묶기까지
Tess · Claude Code MVP 공장 · · 좋아요 0 · 답글 0
연재 Claude Code MVP 공장 시즌 8 11/12편
1. 먼저 ERP와 CRM의 경계를 다시 잡았다
처음에는 ERP와 CRM이 뒤섞여 있었다. 고객관리도 ERP 같고, 거래처도 CRM 같고, 미납 운영도 고객 문제이면서 회사 운영 문제처럼 보였다.
그래서 먼저 제품 정의를 다시 잡았다.
회사 내부 운영 OS를 만들고 싶다. 거기에 고객 생애주기를 통합해서 보는 CRM을 붙이고 싶다. 한 웹사이트 안에서 ERP와 CRM이 같이 있어야 한다.
결론은 명확했다.
ERP = 회사 내부 운영 OS
CRM = 고객 생애주기 운영 OS
둘은 한 웹사이트 안에 있지만 역할은 다르다.
CRM은 고객 1명을 기준으로 첫 문의, 모델 탐색, 리스렌탈 가입, 결제, 미납, 정지/해제, CS, 만기/종료까지 이어지는 타임라인을 맡는다.
ERP는 거래처, 직원, 메일, 일정, 문서함, 회의록, 매출·정산·세무, 차량/단말 마스터 같은 회사 내부 운영을 맡는다.
이 경계를 먼저 세운 덕분에 이후 구현에서 “이 기능은 어느 메뉴에 있어야 하는가?”라는 혼란이 줄었다.
2. 회사 도메인에 맞는 상태모델을 만들었다
이 회사에는 크게 두 종류의 케이스가 있다.
신차 구매: 보조금/구매/출고 중심의 흐름. 출고 후에는 운영영역이 크지 않음
리스렌탈: 차량+보험이 묶인 장기 이용 흐름. 계약, 결제, 미납, 정지, 회수, 채권까지 이어짐
처음에는 이 둘을 같은 상태값으로 다루려는 유혹이 있었다. 하지만 그러면 운영이 망가진다. 신차 구매 흐름과 리스 렌탈 맥락이 서로 달라서 각 케이스 별 전용 상태머신이 필요했다.
신차 구매와 리스렌탈은 상태 enum을 공유하면 안 된다. 케이스 타입별로 완전히 분리하고, 리스렌탈은 계약 phase, 미납 단계, 실제 서비스 상태를 나눠서 봐야 한다.
그래서 상태모델을 분리했다.
case_type: 신차 구매 / 리스렌탈신차 구매: 출고까지의 단일 흐름
리스렌탈: 계약 phase, 미납 단계, 실제 서비스 상태, 제어원 분리
고위험 action: 차량 정지, 해제, 발송, 환불, 세금계산서 등은 승인 게이트 필수
특히 차량 정지/해제는 고객 생계와 직결될 수 있다. 그래서 메모나 추측이 아니라 실제 상태조회와 승인 흐름을 전제로 설계했다.
이 단계에서 배운 건, AI에게 화면을 만들게 하기 전에 상태모델을 먼저 잠가야 한다는 점이었다. 상태가 흔들리면 화면도, 자동화도, AI 판단도 모두 흔들린다.
3. 고객 360을 먼저 만들었다 — CRM의 중심 화면
CRM의 핵심은 “고객 1명 기준으로 다 보는 것”이다. 단순 고객 목록이 아니라, 고객의 생애주기와 조치 이력을 한 화면에서 보는 것이 목표였다.
고객 기준으로 생애주기 주요 이벤트를 묶어서 보고 싶다. 단순 고객 DB가 아니라, 고객 전방위 주요 이벤트를 통합해주는 느낌이어야 한다.
그래서 고객360 화면을 6개 탭으로 설계했다.
개요
생애주기 히스토리
CS 상담이력
계약·리스렌탈
결제·마일리지
메모·조치
여기서 중요한 결정은 “모든 것을 타임라인 하나에 다 넣지 않는 것”이었다.
생애주기 탭에는 중요한 이벤트만 큐레이션
CS 상담이력 탭에는 전체 대화 서랍식 보관
계약 탭에는 신차 구매와 리스렌탈을 타입 라벨로 구분
결제·마일리지 탭에는 회차, 예약금, 환불, 마일리지 변동을 표시
메모·조치 탭에는 운영 메모와 승인 요청 흐름을 둠
이렇게 나누니 고객을 “데이터 행”이 아니라 “운영 대상”으로 볼 수 있게 됐다.
MVP로 한 것: 6탭 구조, 고객 개요, 주요 생애주기, 상담 이력 구조, 계약/결제/메모 탭의 화면 뼈대와 mock/seed 기반 데이터 표시.
아직 남은 것: 실제 운영 데이터와의 완전 연동, 실시간 CS 웹훅 연결, 문서/증빙 자동 연결, 고위험 조치 실행/승인 워크플로우.
4. CS 티켓과 리스렌탈 운영을 붙였다
고객360만 있으면 조회는 되지만, 실제 운영 큐가 없다. 그래서 다음으로 CS 티켓과 리스렌탈 운영 화면을 만들었다.
CS는 채팅, 전화, 문자, 메일 등 여러 채널에서 들어오지만 대부분 채널톡 서비스를 통해 인입된다. 목표는 모든 상담을 하나의 고객 이력으로 연결하고, 매니저가 처리해야 할 것을 놓치지 않는 것이다.
CS 상담은 고객360에도 보여야 하고, 별도 티켓 인박스에서도 처리할 수 있어야 한다. 중요한 상담은 생애주기에 남기고, 전체 대화는 상담이력에 남겨야 한다.
구조는 이렇게 잡았다.
thread: 고객·채널별 전체 대화ticket: 매니저 처리가 필요한 인입important: AI가 중요 후보로 분류하고 매니저가 확인logged: 중요한 처리건만 생애주기에 기록
리스렌탈 미납 운영은 이미 별도 자동화 대시보드가 강하게 작동 중이었다. 그래서 무리하게 재구현하지 않았다.
이미 돌아가는 미납 운영 OS는 유지하고, CRM은 읽기/통합/링크부터 시작하자. 자동화 worker는 검증 후 마지막에 흡수하자.
이 결정도 중요했다. AI로 새 시스템을 만든다고 해서 기존에 잘 돌아가는 자동화를 바로 갈아엎으면 위험하다. 특히 미납, 정지, 자동해제처럼 운영 리스크가 큰 영역은 점진 흡수가 맞았다.
MVP로 한 것: CS 티켓 인박스 구조, 고객별 상담 이력 구조, 중요 이벤트 개념, 리스렌탈 운영 요약, 만기/미납 연결 화면.
아직 남은 것: 실시간 CS 웹훅, 매니저 답변 자동 완료, AI 중요도 분류의 실데이터 적용, 미납 자동화의 클라우드/이벤트 기반 흡수.
5. ERP 첫 슬라이스는 거래처 관리였다
CRM에서 제조사, 보험사, PG, 메시징 업체 같은 거래처 정보가 계속 필요했다. 특히 신차 구매 수수료는 제조사별 rate가 단일 출처여야 했다.
그래서 ERP 첫 실전 슬라이스는 거래처 관리였다.
거래처 관리를 먼저 만들자. 제조사별 처리수수료와 영업수수료를 CRM에서 하드코딩하지 말고, ERP 거래처를 단일 출처로 참조하게 하자.
거래처 관리에서는 다음을 만들었다.
제조사, 보험사, 딜러, 부품정비, PG, 메시징 등 타입 구분
거래처 목록과 상세 화면
담당자/연락처/상태/메모
제조사별 처리수수료, 영업수수료 rate
CRM 케이스가 거래처
vendor_id를 참조하도록 정합성 정리
이 작업의 핵심은 “화면 하나 추가”가 아니라 CRM 수수료의 단일 출처를 ERP 거래처로 옮긴 것이었다.
MVP로 한 것: 거래처 목록/상세, 타입 필터, 제조사 수수료 rate, CRM 참조 카운트와 단일출처 구조.
아직 남은 것: 거래처 정산 자동계산, 실제 외부 provider 연동, 계약/청구/세금계산서와의 완전 연결.
6. mock에서 실제 DB 기반으로 넘어갔다
처음에는 빠른 설계를 위해 mock 데이터로 화면과 도메인 구조를 만들었다. 하지만 실제 시스템이 되려면 인증, DB, 권한, 데이터 저장이 필요했다.
이제 인증과 실제 DB로 전환하자. 고객, 거래처, 티켓 데이터를 mock에서 실DB로 옮기고, 나중에 기존 운영 데이터와 연결할 수 있게 adapter 경계를 유지하자.
여기서 중요한 원칙은 “오늘은 mock, 내일은 기존 시스템 연동”이 가능하도록 repository/adapter 경계를 만드는 것이었다.
즉 UI가 직접 데이터 소스를 알지 않게 했다.
지금은 seed/mock 또는 DB에서 읽음
나중에는 기존 관리자페이지 API나 DB 어댑터로 교체
UI와 도메인 로직은 최대한 유지
이후 로그인, 라우트 보호, 관리자 계정, 마이그레이션, 고객/거래처/티켓 실DB 전환이 진행됐다.
MVP로 한 것: 인증, DB 마이그레이션, 일부 도메인 실DB 전환, adapter 경계.
아직 남은 것: 기존 운영 시스템과의 읽기 연동, 실데이터 마이그레이션 정책, 감사로그/권한 세분화.
7. 메일 플랫폼을 ERP 안으로 넣었다
운영에서 메일은 빠질 수 없다. 외부 거래처, 제휴사, 내부 직원, 고객 관련 메일이 모두 업무 맥락이다. ERP 안에서 회사 메일을 읽고 보내고, CRM과 연결할 수 있어야 했다.
직원이 ERP에서 회사 메일을 실제로 읽고 쓸 수 있게 하자. 받은편지함, 발송, 첨부, 자동분류, 백그라운드 동기화까지 가자.
메일 플랫폼은 단계적으로 구현했다.
직원별 메일 계정 연동
앱 비밀번호를 암호화해서 저장하는 시크릿 금고
IMAP으로 받은편지함 읽기
SMTP로 메일 발송과 첨부 처리
보낸이 기준 거래처/직원/외부 자동분류
메일 메시지 영속 저장
주기적 동기화 worker와 cron 준비
여기서도 원칙은 같았다.
평문 비밀번호 저장 금지
잘못된 인증정보는 연동 시점에 차단
페이지를 열 때만 메일을 가져오는 구조에서, 백그라운드 동기화 구조로 확장
메일을 단순 받은편지함이 아니라 CRM/ERP 맥락에 붙일 수 있게 분류
이 단계에서 “ERP가 실제 업무 도구가 되는 느낌”이 강해졌다. 단순 화면이 아니라 직원이 로그인해서 메일을 읽고, 쓰고, 분류할 수 있는 기능이 생겼기 때문이다.
MVP로 한 것: 메일 계정 연동, 암호화 저장, 받은편지함, 메일 발송, 첨부, 분류, 영속 저장, 동기화 worker 준비.
아직 남은 것: 메일과 고객/거래처 타임라인 자동 연결, 운영 알림, 외부 cron/클라우드 worker 안정화, 팀 단위 권한 정책.
8. 직원·설정으로 팀 사용의 전제를 만들었다
클라우드 배포까지 가면 다음 문제는 “누가 로그인할 수 있느냐”다. 관리자 1명만 들어가는 시스템은 팀 도구가 아니다.
팀이 쓰려면 직원 추가와 권한 관리가 필요하다. 관리자가 직원을 초대하고, 직원이 비밀번호를 설정해서 로그인하는 흐름을 만들자.
직원·설정에서는 다음을 구현했다.
직원 목록
이름, 이메일, 부서, 직책, 역할, 상태 관리
관리자 전용 접근
매직링크 초대
초대받은 직원의 비밀번호 설정
비활성 직원 로그인 차단
초대 재발송과 삭제
이 기능은 화려하지 않지만 운영 시스템에서는 필수다. 팀이 실제로 쓰려면 권한과 계정 흐름이 먼저 안정적이어야 한다.
MVP로 한 것: 직원 목록/검색, 초대, 역할/상태 변경, 비활성 차단, 초대 재발송/삭제.
아직 남은 것: 메뉴별 세밀 권한, 감사로그, 부서/직무별 기본 권한 템플릿, 설정 메뉴 확장.
9. 명함·문서 OCR은 다음 고도화 과제로 남겼다
ERP 내부 업무에는 명함, 계약서, 사업자등록증, 차량등록증 같은 문서 입력이 많다. 이 부분은 AI 자동입력의 좋은 후보였다.
일단 명함 OCR을 먼저 실험했다.
명함을 찍으면 정보가 자동으로 들어오고, 거래처나 연락처로 이어지면 좋겠다.
이미지 업로드 기반으로 이름, 회사, 직책, 전화번호, 이메일을 추출하고, 사용자가 확인한 뒤 거래처/연락처로 저장하는 흐름을 만들었다. 카메라 스캐너도 붙여 보았다.
다만 이 기능은 아직 다듬을 부분이 남아 있다. 특히 모바일 카메라에서 “보이는 감지 UI”와 “AI가 실제로 읽는 이미지”를 어떻게 분리할지, 명함뿐 아니라 계약서·사업자등록증·차량등록증까지 어떻게 확장할지 더 설계가 필요하다.
그래서 OCR은 이번 사례의 핵심 교훈이라기보다, 다음 단계 자동입력 고도화 과제에 가깝다.
MVP로 한 것: 명함 이미지 업로드, 필드 추출, 확인/수정, 거래처/연락처 저장 흐름, 카메라 스캐너 실험.
아직 남은 것: 카메라 UX 안정화, 문서별 OCR 템플릿, 계약서/사업자등록증/차량등록증 자동입력, 검수 워크플로우, 고객360 문서 탭 연결.
10. 디자인 시스템과 리디자인까지 이어졌다
기능이 늘어나자 화면마다 스타일이 달라지고, 기본 템플릿 같은 느낌이 생겼다. ERP+CRM은 매일 보는 운영 콘솔이기 때문에 디자인 품질도 중요했다.
렌더 이미지로 여러 팔레트를 비교해서 보여주고, 선택한 팔레트로 디자인시스템을 확정한 뒤 풀 리디자인을 진행하자.
팔레트 후보를 라이트/다크로 비교했고, 최종적으로 전문적인 운영 콘솔에 맞는 방향을 잡았다. 이후 디자인시스템 문서와 JSON 상태파일을 만들고, 토큰·공통 UI 컴포넌트부터 정리하기 시작했다.
여기서도 한 번에 전체 화면을 갈아엎기보다:
디자인 토큰
테마 토글
공통 UI primitive
네비게이션/셸
대시보드/테이블/폼
순서로 쌓아가는 방식이었다.
MVP로 한 것: 디자인 방향 선정, 디자인시스템 단일 문서, 토큰, 라이트/다크 기반, 공통 UI primitive 착수.
아직 남은 것: 전체 화면 일괄 적용, 모바일 QA, 데이터 밀도 조정, 실제 사용자 피드백 기반 개선.
Before vs After
항목
Before
After
운영 정보
관리자페이지, 미납대시보드, 메일, 채팅, 문서, 사람 기억에 분산
ERP+CRM 안에서 고객·계약·CS·거래처·메일·직원 맥락 연결 시작
고객관리
고객 목록/주문 중심
고객360: 생애주기, CS, 계약, 결제, 메모, 조치 탭 구조
CS
채널별 응대 중심
티켓/상담이력/중요 이벤트/생애주기 기록 구조
리스렌탈 운영
별도 미납 운영 OS 중심
기존 자동화는 유지하고 CRM에서 요약·링크·만기 큐로 점진 흡수
ERP 내부업무
거래처, 메일, 직원, 문서 등 분리
거래처 관리, 메일 플랫폼, 직원관리가 한 사이트로 수렴
AI 협업
긴 대화가 끊기면 맥락 손실
CONTEXT.md, 설계문서, .goalloop/*.json으로 목표·상태 보존
품질관리
구현자 판단에 의존
Claude Code 구현 + Codex 리뷰 + 테스트/빌드 검증
실제로 만들어진 주요 기능
ERP 홈 대시보드와 좌측 네비게이션
CRM 대시보드
고객360 6탭 구조
CS 티켓 인박스와 고객별 상담이력 구조
리스렌탈 운영 요약과 만기/미납 연결 구조
거래처 관리와 제조사 수수료 단일출처화
인증과 DB 마이그레이션
고객/거래처/티켓 데이터의 실DB 전환
직원 관리와 초대/비밀번호 설정
회사 메일 읽기/쓰기/첨부/분류/동기화 worker
명함 OCR MVP와 카메라 실험
디자인시스템과 라이트/다크 토큰 기반 리디자인 착수
효과적이었던 것
도메인 경계를 먼저 잡기
ERP와 CRM을 섞지 않고, 회사 내부 운영과 고객 생애주기 운영으로 나눈 것이 이후 모든 화면과 데이터 모델의 기준이 됐다.상태모델을 코드보다 먼저 잠그기
신차 구매와 리스렌탈 상태를 분리하지 않았다면 미납, 정지, 회수 같은 위험한 기능이 잘못 적용될 수 있었다.얇은 MVP 슬라이스로 쌓기
“ERP 전체 만들어줘”가 아니라 고객360, CS티켓, 거래처, 메일, 직원처럼 기능 단위로 쪼개니 실제로 진도가 났다.구현자와 리뷰어 분리하기
Claude Code가 빠르게 만들고, Codex가 별도 시선으로 검토하면서 놓친 경계나 버그를 잡을 수 있었다.컨텍스트를 파일로 남기기
긴 AI 작업은 언젠가 컨텍스트가 끊긴다.CONTEXT.md, 설계문서, JSON 상태파일이 없었다면 이어가기 어려웠다.기존 자동화를 존중하기
이미 잘 돌아가는 미납 대시보드/자동해제 같은 기능은 바로 갈아엎지 않고, CRM에서 읽기·링크로 점진 흡수하는 전략이 안전했다.
이렇게 하면 안 돼요
AI에게 “전체 ERP 만들어줘”만 던지면 안 된다
범위가 너무 크면 모델이 그럴듯한 화면만 만들고 도메인 충돌을 놓친다.고객 DB와 CRM을 같은 것으로 보면 안 된다
이 프로젝트에서 CRM은 고객 이름 목록이 아니라, 상담·계약·결제·미납·차량·문서가 이어지는 생애주기 운영 화면이다.상태값을 하나의 enum에 몰아넣으면 안 된다
신차 구매와 리스렌탈처럼 본질이 다른 흐름은 상태모델부터 분리해야 한다.기존 시스템을 한 번에 대체하려 하면 안 된다
운영 리스크가 큰 영역은 새 시스템이 먼저 읽고 연결한 뒤, 충분히 검증되면 흡수하는 게 맞다.AI 에이전트에게 기억을 맡기면 안 된다
대화 컨텍스트는 언젠가 끊기므로, 목표·결정·상태·다음 작업은 반드시 파일로 남겨야 한다.
프롬프트 1: 사내 ERP+CRM을 시작할 때
우리 회사 운영을 ERP와 CRM으로 나누어 설계하고 싶다.
ERP는 회사 내부 운영, CRM은 고객 생애주기 운영으로 정의해줘.
각 기능을 어느 도메인에 둘지 표로 나누고, 공유 키와 절대 섞이면 안 되는 경계를 정리해줘.
프롬프트 2: 상태모델부터 잠그기
이 비즈니스의 주요 케이스 유형을 나누고, 서로 다른 케이스가 같은 상태 enum을 공유하면 안 되는 지점을 찾아줘.
상태값, 전이 조건, 고위험 action, 사람 승인 게이트를 먼저 설계해줘.
화면 구현은 그다음으로 미뤄줘.
프롬프트 3: AI 코딩을 슬라이스로 진행하기
전체 시스템을 한 번에 만들지 말고, 가장 얇은 수직 슬라이스로 나눠줘.
각 슬라이스마다 목표, 비목표, 수용 기준, 테스트/빌드 검증, Codex 리뷰 기준을 정리해줘.
완료 상태는 JSON 파일에 기록해서 컨텍스트가 끊겨도 이어갈 수 있게 해줘.
프롬프트 4: 구현자와 리뷰어 분리하기
Claude Code는 구현 담당, Codex는 독립 리뷰어로 둔다.
각 변경 후 Codex에게 의도 충족 여부, 새 버그, 도메인 경계 위반, 보안/운영 리스크를 검토하게 해줘.
blocker가 있으면 구현을 되돌리거나 수정하고, NO_BLOCKERS일 때만 다음 단계로 넘어가.
프롬프트 5: 기존 시스템을 점진 흡수하기
이미 운영 중인 시스템을 새 ERP+CRM으로 바로 대체하지 말고, 읽기 연동 → 링크/요약 → 일부 UI 통합 → 자동화 이관 순서로 점진 흡수하는 계획을 세워줘.
특히 고위험 자동화는 마지막에 옮기고, 그 전에는 기존 시스템을 진실 소스로 유지해줘.
프롬프트 6: 에이전트 팀을 구성할 때
이번 작업을 오케스트레이터, 설계/질문 에이전트, 구현 에이전트, 독립 리뷰어, 테스트/빌드 검증, 상태 기록자로 나눠줘.
각 역할의 책임과 산출물을 정하고, 구현 에이전트가 만든 결과를 리뷰어가 독립적으로 검토하게 해줘.
모든 결정과 완료 상태는 파일로 남겨 다음 세션에서도 이어갈 수 있게 해줘.
태그: #CTO