<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://wankikim.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://wankikim.github.io/" rel="alternate" type="text/html" /><updated>2026-08-04T13:22:02+09:00</updated><id>https://wankikim.github.io/feed.xml</id><title type="html">Michael Kim Devlog</title><subtitle>18년차 풀스택 개발자의 기술 블로그 — MSA, 핀테크, 임베디드, AI 네이티브 개발</subtitle><author><name>김완기 (Michael Kim)</name></author><entry><title type="html">지난 반년의 기록 — 펌웨어부터 클라우드까지, 그리고 블로그 이사</title><link href="https://wankikim.github.io/essay/half-year-retrospective/" rel="alternate" type="text/html" title="지난 반년의 기록 — 펌웨어부터 클라우드까지, 그리고 블로그 이사" /><published>2026-08-04T13:00:00+09:00</published><updated>2026-08-04T13:00:00+09:00</updated><id>https://wankikim.github.io/essay/half-year-retrospective</id><content type="html" xml:base="https://wankikim.github.io/essay/half-year-retrospective/"><![CDATA[<p>티스토리에서 GitHub Pages로 블로그를 옮겼습니다. 예전 글 9편도 전부 함께 이사했습니다. 이사 기념으로, 마지막 글(<a href="/essay/what-i-learned-from-15-projects/">15개 프로젝트 회고</a>) 이후 반년 동안 뭘 했는지 정리해봅니다.</p>

<h2 id="반년-동안-만든-것들">반년 동안 만든 것들</h2>

<p><strong>거래소를 Go+Rust로 재플랫폼</strong>했습니다. Java/Spring 기반이던 것을 Go 서비스 13개 + Rust 매칭 엔진 2개로 갈아엎고, 선물 청산·펀딩비 엔진까지 구현했습니다. <a href="/project/exchange-replatform-go-rust/">자세한 이야기</a></p>

<p><strong>Django 모놀리스를 Java 21 MSA로 전환 중</strong>입니다. views.py 14,646줄짜리 운영 시스템을 스트랭글러 패턴으로 19모듈로 옮기면서, NCP→AWS 무중단 마이그레이션도 마쳤습니다. <a href="/project/django-to-java-msa-strangler/">자세한 이야기</a></p>

<p><strong>레거시 PHP를 15개 마이크로서비스로 재구축해 실운영</strong>에 들어갔습니다. 13개 업종 무인매장 SaaS — 하드웨어 드라이버부터 배리어프리 접근성까지. <a href="/project/legacy-php-to-15-microservices/">자세한 이야기</a></p>

<p><strong>커피머신 펌웨어를 hex 역분석으로 재작성</strong>했습니다. 디스어셈블리에서 클럭 주파수를 역산해내던 순간이 올해의 하이라이트였습니다. <a href="/project/coffee-machine-firmware-reverse-engineering/">자세한 이야기</a></p>

<p><strong>오픈소스 로우코드 엔진을 고도화했습니다.</strong> Erupt를 기반으로 @Flow 어노테이션 하나로 API·화면·권한이 나오는 엔진을 다듬고, 그 위에 ERP 9모듈 + MES 3모듈을 얹었습니다. <a href="/project/hunikflow-lowcode-platform/">자세한 이야기</a></p>

<p>그 밖에 결제·정산 플랫폼(복식 원장 + FDS), 외국인 주거 플랫폼(4개 국어), 의류 도매 커머스(실운영 중), iOS/토스/웹 3플랫폼 아케이드 게임, Wi-Fi CSI 재실 감지 R&amp;D 같은 것들이 있었습니다. 포트폴리오(<a href="https://agentmichael.me">agentmichael.me</a>)에 정리해뒀습니다.</p>

<h2 id="반년-전의-나에게-해주고-싶은-말">반년 전의 나에게 해주고 싶은 말</h2>

<p>2월의 저는 “AI와 함께하면 빠르다”에 흥분해 있었습니다. 지금의 저는 조금 다른 걸 봅니다.</p>

<p><strong>속도는 이제 기본값입니다.</strong> 차별점은 속도가 아니라 <strong>방향</strong>입니다. 매칭 엔진 싱글톤을 코드가 아니라 Terraform으로 강제하기로 한 결정, 뉴스 본문 컬럼을 아예 스키마에서 빼서 저작권 리스크를 차단한 결정, 접근성을 규제 대응이 아니라 무인 비즈니스의 전제조건으로 다시 정의한 순간 — AI는 이런 결정을 대신해주지 않습니다. 판단의 근거가 되는 경험을 쌓는 것, 그게 여전히 사람의 일입니다.</p>

<p><strong>아키텍처는 문서가 아니라 테스트로 지켜야 합니다.</strong> ArchUnit 규칙 5개가 문서 100페이지보다 힘이 셉니다. AI가 코드를 빨리 짜는 시대일수록, 경계를 기계적으로 강제하는 장치가 중요해집니다.</p>

<p><strong>운영까지 가야 진짜입니다.</strong> 반년간 만든 것 중 자랑스러운 건 코드가 아니라, 실제로 돈이 흐르고 손님이 쓰는 시스템들입니다. 데모와 운영 사이에는 롤백 절차서, 중복 요청 방지, 삭제 보호, 무중단 마이그레이션 같은 지루한 것들이 있고, 그 지루한 것들이 실력입니다.</p>

<p>새 블로그에서는 이런 이야기들을 더 자주, 더 깊게 남겨보겠습니다.</p>]]></content><author><name>김완기 (Michael Kim)</name></author><category term="Essay" /><category term="회고" /><category term="AI 네이티브" /><category term="GitHub Pages" /><summary type="html"><![CDATA[티스토리에서 GitHub Pages로 블로그를 옮겼습니다. 예전 글 9편도 전부 함께 이사했습니다. 이사 기념으로, 마지막 글(15개 프로젝트 회고) 이후 반년 동안 뭘 했는지 정리해봅니다.]]></summary></entry><entry><title type="html">어노테이션 하나로 ERP 화면이 나온다 — 오픈소스 Erupt를 우리 플랫폼으로 고도화하기</title><link href="https://wankikim.github.io/project/hunikflow-lowcode-platform/" rel="alternate" type="text/html" title="어노테이션 하나로 ERP 화면이 나온다 — 오픈소스 Erupt를 우리 플랫폼으로 고도화하기" /><published>2026-08-04T12:00:00+09:00</published><updated>2026-08-04T12:00:00+09:00</updated><id>https://wankikim.github.io/project/hunikflow-lowcode-platform</id><content type="html" xml:base="https://wankikim.github.io/project/hunikflow-lowcode-platform/"><![CDATA[<p>ERP를 만들다 보면 어느 순간 현타가 옵니다. 엔티티 정의하고, 리포지토리 만들고, 서비스 만들고, 컨트롤러 만들고, 화면 만들고, 권한 붙이고, 엑셀 내보내기 붙이고 — 그리고 다음 도메인에서 <strong>똑같은 걸 또</strong> 합니다. 도메인 200개면 200번.</p>

<p>이 반복을 없애는 방법을 찾다가 <strong>Erupt</strong>라는 오픈소스 로우코드 프레임워크(Apache 2.0)를 만났습니다. 어노테이션 메타데이터로 CRUD 전체를 런타임에 생성한다는 발상이 정확히 제가 원하던 것이었습니다. 처음부터 만드는 대신, 이 엔진을 기반으로 잡고 우리 회사 플랫폼으로 고도화하기로 했습니다. 그렇게 나온 것이 HunikFlow입니다.</p>

<h2 id="flow-하나면-됩니다">@Flow 하나면 됩니다</h2>

<p>JPA 엔티티에 어노테이션을 선언합니다.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Flow</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"거래처 관리"</span><span class="o">)</span>
<span class="nd">@Entity</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Customer</span> <span class="o">{</span>
    <span class="nd">@FlowField</span><span class="o">(</span>
        <span class="n">views</span> <span class="o">=</span> <span class="nd">@View</span><span class="o">(</span><span class="n">title</span> <span class="o">=</span> <span class="s">"거래처명"</span><span class="o">),</span>
        <span class="n">edit</span> <span class="o">=</span> <span class="nd">@Edit</span><span class="o">(</span><span class="n">title</span> <span class="o">=</span> <span class="s">"거래처명"</span><span class="o">,</span> <span class="n">notNull</span> <span class="o">=</span> <span class="kc">true</span><span class="o">),</span>
        <span class="n">search</span> <span class="o">=</span> <span class="nd">@Search</span>
    <span class="o">)</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">name</span><span class="o">;</span>
    <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>

<p>이게 끝입니다. 런타임에 메타데이터를 읽어서:</p>

<ul>
  <li>REST API가 자동으로 열리고 (<code class="language-plaintext highlighter-rouge">/api/v1/data/{flowName}</code> 범용 엔드포인트)</li>
  <li>Angular 관리화면(테이블·검색·폼)이 같은 메타데이터로 렌더링되고</li>
  <li>행/열 단위 권한, 엑셀 내보내기까지 따라옵니다</li>
</ul>

<p>드래그앤드롭 폼 빌더 방식이 아니라 <strong>어노테이션 메타데이터 기반 런타임 CRUD</strong>라는 게 핵심입니다. 코드는 여전히 Java고, Git으로 버전 관리되고, IDE 자동완성이 됩니다. “코드 없이”가 아니라 “반복 없이”가 목표입니다.</p>

<p>물론 자동 생성만으로 안 되는 지점이 반드시 옵니다. <code class="language-plaintext highlighter-rouge">@DataProxy</code>(저장 전후 로직), <code class="language-plaintext highlighter-rouge">@PowerHandler</code>(동적 권한), <code class="language-plaintext highlighter-rouge">@OnChange</code>(필드 연동) 같은 확장 훅으로 안 되는 부분만 Java로 끼워넣는 구조입니다.</p>

<h2 id="무엇을-얹었나">무엇을 얹었나</h2>

<p>기반 엔진 위에 우리가 필요한 것들을 붙여나갔습니다. 원본에 없거나 부족했던 영역이 셋입니다.</p>

<p><strong>업무 도메인.</strong> ERP 9모듈(CRM·재무·인사·재고·커머스·자산·CS 등)과 MES 3모듈을 엔진 위에 직접 구축했습니다. MES 쪽은 도메인 깊이가 상당합니다 — OEE(설비종합효율), SPC 관리도(Cp/Cpk에 Nelson Rules 8종), BOM, MRP, Andon까지. “로우코드는 장난감”이라는 인상을 깨려면 제조 현장 수준의 도메인을 소화한다는 걸 보여줘야 했습니다.</p>

<p><strong>AI 모듈.</strong> LLM 프로바이더 14개를 통합한 AI 모듈을 새로 만들었습니다. Function Call과 SSE 스트리밍을 지원해서, 업무 화면 안에서 데이터를 두고 AI와 대화하는 흐름이 가능합니다.</p>

<p><strong>엔진 자체의 개선.</strong> 한국 업무 환경에 맞는 결재(BPM) 워크플로우, GraalJS 동적 API(화면에서 스크립트를 작성하면 REST API로 즉시 등록), 그리고 전체 코드베이스의 현대화(Java 21, Angular 21, PostgreSQL 17)까지.</p>

<h2 id="규모와-도구들">규모와 도구들</h2>

<ul>
  <li>Maven 46모듈, Java 소스 1,166개(약 98,000줄), 엔티티 217개</li>
  <li>테스트 795+, CodeQL/Qodana 정적분석 상시 가동</li>
  <li>UI 렌더러 4종(Ant Design, Element UI 등) 교체 가능</li>
</ul>

<h2 id="오픈소스-위에서-만든다는-것">오픈소스 위에서 만든다는 것</h2>

<p>이 프로젝트에서 배운 가장 큰 교훈은 <strong>“기반을 인정하면 서사가 더 강해진다”</strong>는 것입니다. 처음부터 다 만들었다고 말하고 싶은 유혹이 있지만, 실제 엔지니어링의 가치는 다른 곳에 있습니다. 좋은 기반을 고르는 안목, 원본 구조를 깊게 이해하고 침습 없이 확장하는 기술, 그리고 그 위에 실제 업무 도메인(ERP·MES)을 완성해내는 실행력. KoCoinEx를 BIZZAN에서 출발해 Go+Rust로 재플랫폼한 것과 같은 결의 이야기입니다.</p>

<p>795개 테스트는 그 확장 과정의 안전망이었습니다. 엔진 한 줄의 변경이 200개 도메인에 영향을 주는 코드에서, Claude Code와 함께 구현과 테스트를 같은 호흡으로 만들지 않았다면 이 규모의 고도화는 엄두를 못 냈을 겁니다.</p>]]></content><author><name>김완기 (Michael Kim)</name></author><category term="Project" /><category term="로우코드" /><category term="Java" /><category term="ERP" /><category term="MES" /><category term="오픈소스" /><summary type="html"><![CDATA[ERP를 만들다 보면 어느 순간 현타가 옵니다. 엔티티 정의하고, 리포지토리 만들고, 서비스 만들고, 컨트롤러 만들고, 화면 만들고, 권한 붙이고, 엑셀 내보내기 붙이고 — 그리고 다음 도메인에서 똑같은 걸 또 합니다. 도메인 200개면 200번.]]></summary></entry><entry><title type="html">레거시 PHP 1,500파일을 15개 마이크로서비스로 — 그리고 실운영까지</title><link href="https://wankikim.github.io/project/legacy-php-to-15-microservices/" rel="alternate" type="text/html" title="레거시 PHP 1,500파일을 15개 마이크로서비스로 — 그리고 실운영까지" /><published>2026-08-04T11:00:00+09:00</published><updated>2026-08-04T11:00:00+09:00</updated><id>https://wankikim.github.io/project/legacy-php-to-15-microservices</id><content type="html" xml:base="https://wankikim.github.io/project/legacy-php-to-15-microservices/"><![CDATA[<p><a href="/ai/legacy-refactoring-with-ai/">예전 글</a>에서 레거시 리팩토링 이야기를 했었는데, 그 프로젝트가 어디까지 갔는지 후속편을 쓸 때가 됐습니다. 결론부터: <strong>실운영에 들어갔습니다.</strong></p>

<h2 id="무엇을-만들었나">무엇을 만들었나</h2>

<p>스터디카페·독서실·세차장·골프연습장 같은 무인매장을 위한 키오스크 + 프랜차이즈 원격관제 SaaS입니다. 13개 업종을 하나의 플랫폼이 감당합니다. 원래는 PHP 모놀리스 1,500개 파일이었고, 지금은:</p>

<ul>
  <li><strong>Spring Boot 마이크로서비스 15개</strong> + 공용 모듈 3개 (Eureka + Gateway)</li>
  <li>Java 소스 1,432개, 컨트롤러 139개, JPA 엔티티 135개</li>
  <li>Flyway 마이그레이션 457개 (스키마 진화의 역사가 그대로 담겨 있습니다)</li>
  <li>Electron 키오스크(TS/TSX 271개) + Next.js 어드민</li>
  <li>Terraform 36개 파일로 AWS 인프라 전체 코드화</li>
</ul>

<h2 id="업종-13개를-어떻게-하나의-코드로">업종 13개를 어떻게 하나의 코드로</h2>

<p>레거시의 최대 문제는 업종별 if-else였습니다. 세차장 로직과 스터디카페 로직이 같은 함수 안에서 분기되고, 새 업종이 들어올 때마다 분기가 늘어나는 구조. 이걸 <strong>Plugin Architecture</strong>로 바꿨습니다.</p>

<p><code class="language-plaintext highlighter-rouge">IndustryPlugin</code> 인터페이스를 정의하고 Spring auto-configuration으로 업종별 플러그인을 조립합니다. Feature 플래그 23종, 상품 타입 22종을 플러그인이 선언하고, 코어는 업종을 모릅니다. 신규 업종 추가 = 플러그인 클래스 1개 구현. 실제로 이후 업종 추가 작업이 며칠 단위로 줄었습니다.</p>

<h2 id="하드웨어의-세계">하드웨어의 세계</h2>

<p>키오스크는 소프트웨어만으로 끝나지 않습니다. Electron 메인 프로세스에 하드웨어 드라이버 계층을 직접 만들었습니다. 카드 디스펜서, 현금 호퍼, 동전 호퍼, RF카드 리더, 바코드 스캐너, 릴레이, 영수증 프린터(ESC/POS), VAN 결제 모듈까지. 오프라인 모드, 하트비트, 원격 명령, 자동 업데이트 채널 분리(dev/prod)도 이 계층에서 처리합니다.</p>

<h2 id="배리어프리--법이-만든-마감">배리어프리 — 법이 만든 마감</h2>

<p>2026년부터 무인정보단말기 접근성 의무화가 단계적으로 적용됩니다. 고대비, 색 반전, 폰트 스케일, 캡션, 음성 안내, 시각 경보를 키오스크 런타임에 직접 구현했습니다. 접근성 대응 코드만 40KB가 넘습니다. 솔직히 처음엔 “규제 대응”으로 시작했는데, 만들고 나니 생각이 바뀌었습니다. 무인매장은 점원이 없습니다. 키오스크가 안 되면 그 손님은 그냥 돌아가야 합니다. 접근성은 무인 비즈니스에서 기능이 아니라 전제조건이었습니다.</p>

<h2 id="운영은-다른-종목이다">운영은 다른 종목이다</h2>

<p>만드는 것과 운영하는 것은 정말 다릅니다. 이 프로젝트에서 운영 안전장치로 만든 것들:</p>

<ul>
  <li>RDS 삭제 보호 + Terraform apply 가드</li>
  <li>배포 시 <code class="language-plaintext highlighter-rouge">CONFIRM_PROD=YES</code> 명시적 확인</li>
  <li>결제 서비스 롤백 런북 (사고 나기 전에 미리 씁니다)</li>
  <li>Saga 분산 트랜잭션 + 결제 중복 방지 처리 — 같은 결제 요청이 두 번 와도 한 번만 처리되도록 (분산 시스템에서 “정확히 한 번”은 공짜가 아닙니다)</li>
</ul>

<p>실제 결제가 흐르고 실제 매출이 도는 시스템은 코드 품질과는 또 다른 종류의 긴장감을 줍니다. 그 긴장감이 저는 좋습니다. 코드가 현실과 만나는 지점이니까요.</p>]]></content><author><name>김완기 (Michael Kim)</name></author><category term="Project" /><category term="MSA" /><category term="레거시" /><category term="Spring Boot" /><category term="키오스크" /><category term="접근성" /><summary type="html"><![CDATA[예전 글에서 레거시 리팩토링 이야기를 했었는데, 그 프로젝트가 어디까지 갔는지 후속편을 쓸 때가 됐습니다. 결론부터: 실운영에 들어갔습니다.]]></summary></entry><entry><title type="html">커피머신 펌웨어를 hex 역분석으로 다시 만든 이야기 — 클럭이 16MHz가 아니었다</title><link href="https://wankikim.github.io/project/coffee-machine-firmware-reverse-engineering/" rel="alternate" type="text/html" title="커피머신 펌웨어를 hex 역분석으로 다시 만든 이야기 — 클럭이 16MHz가 아니었다" /><published>2026-08-04T10:00:00+09:00</published><updated>2026-08-04T10:00:00+09:00</updated><id>https://wankikim.github.io/project/coffee-machine-firmware-reverse-engineering</id><content type="html" xml:base="https://wankikim.github.io/project/coffee-machine-firmware-reverse-engineering/"><![CDATA[<p>웹과 클라우드 이야기만 쓰다가 오늘은 완전히 다른 층위의 이야기입니다. 무인카페에 들어가는 커피머신 제어 보드 펌웨어를 새로 만든 경험담입니다.</p>

<h2 id="소스가-없다">소스가 없다</h2>

<p>상용 커피머신 보드(M500/M400)를 쓰는데, 제조사 소스코드가 없습니다. 있는 것은 보드에서 뽑아낸 hex 파일과 시리얼 프로토콜 스펙 엑셀 문서뿐. 기능을 고치거나 새 하드웨어에 대응하려면 방법은 하나였습니다. <strong>hex를 디스어셈블해서 동작을 복원하고, 처음부터 다시 쓰는 것.</strong></p>

<p>ATmega2560 기반으로 클린 재작성을 시작했습니다.</p>

<h2 id="클럭이-16mhz가-아니었다">클럭이 16MHz가 아니었다</h2>

<p>이 프로젝트에서 가장 기억에 남는 순간입니다. Arduino Mega 보드니까 당연히 16MHz겠거니 하고 작성했는데, 커피 추출 시퀀스 타이밍이 미묘하게 안 맞았습니다. 약 8% 빨랐습니다.</p>

<p>원본 펌웨어의 디스어셈블리로 돌아가서 Timer1의 preload 값과 prescaler를 역산해보니 답이 나왔습니다. <strong>F_CPU = 14.7456MHz.</strong> 시리얼 통신(38400 baud)에 오차가 없는 크리스탈을 쓴 겁니다. 16MHz로 가정하면 100ms 시퀀스 틱이 8% 빨라져서 추출 시간이 다 틀어집니다. 하드웨어는 “그럴 것이다”가 아니라 “그렇다”를 확인해야 한다는 교훈을 몸으로 배웠습니다.</p>

<h2 id="논블로킹으로-전부-다시-쓰기">논블로킹으로 전부 다시 쓰기</h2>

<p>원본 펌웨어의 구조적 문제는 블로킹이었습니다. EEPROM 쓰는 동안 프로토콜 응답이 늦고, ADC 읽는 동안 시퀀스가 멈추고. 재작성하면서 원칙을 하나 세웠습니다. <strong>메인 루프에서 그 무엇도 기다리지 않는다.</strong></p>

<ul>
  <li>ADC 센서 읽기: 논블로킹 상태 머신</li>
  <li>EEPROM 쓰기: 틱당 1바이트씩 비동기로</li>
  <li>커피 추출 시퀀스: 100ms 틱 기반 상태 머신</li>
  <li>프로토콜(키오스크와의 시리얼 통신): 최우선 처리, 워치독 리셋 후에도 부트로더 진입 보장</li>
</ul>

<p>M400 보드의 브로멕(추출 유닛)은 위치 센서가 없어서, <strong>모터 전류를 읽어 위치를 추정하고 과부하를 감지</strong>하는 상태 머신을 넣었습니다.</p>

<h2 id="검증-도구까지가-펌웨어다">검증 도구까지가 펌웨어다</h2>

<p>펌웨어만 짜서는 끝나지 않습니다. Python으로 도구를 같이 만들었습니다. 보드 없이 프로토콜을 테스트하는 시뮬레이터, 실보드 시리얼 모니터, 펌웨어 업로더, 자동 테스터. 호스트에서 도는 단위 테스트도 붙였습니다(임베디드 코드를 PC에서 컴파일해 로직만 검증하는 방식). 최종적으로 실보드에서 검증을 마치고 실제 매장 머신에 올라갔습니다.</p>

<h2 id="왜-이-일이-즐거웠나">왜 이 일이 즐거웠나</h2>

<p>18년 경력의 시작이 MFC와 임베디드 가상키보드, 통신 서버였습니다. 한 바퀴 돌아 다시 로우레벨로 내려와 보니, 그 사이에 쌓인 것들이 보였습니다. 예전엔 오실로스코프와 감으로 하던 일을, 지금은 디스어셈블리를 Claude Code에 던져 “이 타이머 설정이면 실제 주파수가 얼마냐”를 같이 계산하고, Python 시뮬레이터를 한나절 만에 만들어 검증합니다. 도구는 바뀌었지만 본질은 같습니다. <strong>기계가 실제로 어떻게 동작하는지 끝까지 파고드는 것.</strong></p>

<p>웹 개발자에게 임베디드는 멀게 느껴지지만, 논블로킹 루프는 이벤트 루프고, 시리얼 프로토콜은 결국 파싱과 상태 머신입니다. 여러분이 이미 아는 것들입니다. 기회가 되면 한번 내려와 보세요. 재미있습니다.</p>]]></content><author><name>김완기 (Michael Kim)</name></author><category term="Project" /><category term="임베디드" /><category term="역분석" /><category term="ATmega2560" /><category term="펌웨어" /><summary type="html"><![CDATA[웹과 클라우드 이야기만 쓰다가 오늘은 완전히 다른 층위의 이야기입니다. 무인카페에 들어가는 커피머신 제어 보드 펌웨어를 새로 만든 경험담입니다.]]></summary></entry><entry><title type="html">views.py 14,000줄과의 이별 — Django 모놀리스를 Java 21 MSA로 옮기는 중입니다</title><link href="https://wankikim.github.io/project/django-to-java-msa-strangler/" rel="alternate" type="text/html" title="views.py 14,000줄과의 이별 — Django 모놀리스를 Java 21 MSA로 옮기는 중입니다" /><published>2026-08-04T09:00:00+09:00</published><updated>2026-08-04T09:00:00+09:00</updated><id>https://wankikim.github.io/project/django-to-java-msa-strangler</id><content type="html" xml:base="https://wankikim.github.io/project/django-to-java-msa-strangler/"><![CDATA[<p>무인카페 SaaS 백엔드 이야기입니다. 전국 매장의 결제·주문·기기관리·포인트·정산을 처리하는 시스템인데, 처음 물려받았을 때 상태가 이랬습니다.</p>

<ul>
  <li>Django 4.2 모놀리스, <strong>views.py 단일 파일 14,646줄</strong></li>
  <li>모델 67개, URL 218개</li>
  <li>MySQL과 MongoDB가 뒤섞인 하이브리드 저장소</li>
  <li>그런데 이게 <strong>실제로 돈을 벌고 있는 운영 시스템</strong></li>
</ul>

<p>마지막 줄이 중요합니다. 돌아가는 시스템을 세워놓고 새로 만들 수는 없습니다. 그래서 스트랭글러 피그(Strangler Fig) 패턴으로 갔습니다.</p>

<h2 id="전환-전략">전환 전략</h2>

<p>한 리포에 3세대가 공존합니다. 운영 중인 Django 모놀리스, 전환 중인 Java 21 MSA, 새로 만든 Next.js 어드민. 도메인 하나씩 Java로 옮기고, 트래픽을 점진적으로 넘기고, 패리티가 검증되면 Django 쪽 코드를 죽입니다.</p>

<p>MSA 모노레포는 19모듈로 잡았습니다. 마이크로서비스 11개(store, catalog, machine, machine-gateway, payment, order, point, billing, notification, integration, analytics) + 공용 라이브러리 8개. 공용 라이브러리에는 이벤트 봉투(CloudEvents), Outbox 패턴, 보안, 관측(observability) 같은 횡단 관심사를 넣었습니다.</p>

<h2 id="아키텍처를-문서가-아니라-ci로-강제하기">아키텍처를 문서가 아니라 CI로 강제하기</h2>

<p>이번 프로젝트에서 제일 잘했다고 생각하는 것: <strong>ArchUnit 규칙 5개를 CI 게이트로 걸었습니다.</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">api → application → domain ← infrastructure</code> 의존 방향 강제</li>
  <li>도메인 레이어에 Spring import 금지</li>
  <li><code class="language-plaintext highlighter-rouge">@Transactional</code>은 application 레이어에만</li>
  <li>필드 주입 금지</li>
</ul>

<p>“헥사고날 아키텍처로 갑시다”라고 문서에 적어봐야 3개월이면 무너집니다. 테스트로 만들어두면 PR이 머지가 안 되니까 무너질 수가 없습니다. AI와 협업할 때 특히 중요합니다. Claude Code가 아무리 좋은 코드를 짜도, 아키텍처 경계는 사람이 정하고 기계가 지키게 해야 합니다.</p>

<h2 id="이벤트-파이프라인">이벤트 파이프라인</h2>

<p>서비스 간 통신은 RabbitMQ로 통일하고, 모든 이벤트를 CloudEvents 1.0 표준 형식으로 감싸서 주고받게 했습니다. 각 이벤트에는 같은 메시지가 두 번 처리되는 것을 막아주는 고유 ID(ULID), 요청 흐름을 추적하는 traceId, 어느 고객사의 이벤트인지 구분하는 tenantId가 항상 함께 실립니다. 발행 쪽은 Outbox 패턴을 <code class="language-plaintext highlighter-rouge">@OutboxPublisher</code> 어노테이션 + Relay로 라이브러리화해서, 각 서비스는 어노테이션 하나만 붙이면 됩니다. 이벤트 명세 문서만 599줄인데, 이게 서비스 간 계약서 역할을 합니다.</p>

<h2 id="ncp에서-aws로-무중단으로">NCP에서 AWS로, 무중단으로</h2>

<p>전환 중에 클라우드 이사까지 했습니다. 네이버클라우드에서 AWS로. 시나리오를 3개 만들어 비교하고, MySQL은 PK 델타 싱크, DocumentDB는 ObjectId 델타 싱크 스크립트를 직접 짜서 데이터를 따라잡게 한 뒤 컷오버했습니다. <strong>다운타임 0.</strong> 계획 문서만 134KB가 나왔는데, 마이그레이션은 계획서 쓰는 게 일의 8할이라는 걸 다시 확인했습니다.</p>

<h2 id="운영하면서-잡은-것들">운영하면서 잡은 것들</h2>

<p>커밋 로그에서 몇 개만 꺼내보면:</p>

<ul>
  <li>기기 1만 대 동시 접속 폭주 때 machine-service OOM → 태스크 증설 + 게이트웨이 인증 핫패스 타임아웃 조정(5s/2s)</li>
  <li>Netty DNS 리졸버가 1만 WebSocket 인증에서 연쇄 500 유발 → JDK 리졸버로 교체</li>
  <li>어드민 카테고리 필터 HTML 1.1MB → 208KB, 응답 8.8초 → 1.1초</li>
</ul>

<p>레거시 전환은 화려한 일이 아닙니다. 대부분은 이런 지루한 싸움의 연속입니다. 그래도 views.py 14,646줄이 서비스별 아키텍처 테스트가 걸린 19개 모듈로 바뀌어가는 걸 보면, 이 일을 하길 잘했다는 생각이 듭니다.</p>]]></content><author><name>김완기 (Michael Kim)</name></author><category term="Project" /><category term="MSA" /><category term="레거시" /><category term="Strangler Pattern" /><category term="Java" /><category term="AWS" /><summary type="html"><![CDATA[무인카페 SaaS 백엔드 이야기입니다. 전국 매장의 결제·주문·기기관리·포인트·정산을 처리하는 시스템인데, 처음 물려받았을 때 상태가 이랬습니다.]]></summary></entry><entry><title type="html">Claude Code로 거래소 만들기(3) — Java를 버리고 Go+Rust로 갈아엎은 이야기</title><link href="https://wankikim.github.io/project/exchange-replatform-go-rust/" rel="alternate" type="text/html" title="Claude Code로 거래소 만들기(3) — Java를 버리고 Go+Rust로 갈아엎은 이야기" /><published>2026-08-04T08:00:00+09:00</published><updated>2026-08-04T08:00:00+09:00</updated><id>https://wankikim.github.io/project/exchange-replatform-go-rust</id><content type="html" xml:base="https://wankikim.github.io/project/exchange-replatform-go-rust/"><![CDATA[<p>거래소 시리즈 세 번째 글입니다. <a href="/ai/building-exchange-with-claude-code-1/">1편</a>과 <a href="/ai/building-exchange-with-claude-code-2/">2편</a>에서 Java/Spring으로 거래소를 만들었다면, 이번에는 그걸 통째로 갈아엎은 이야기입니다.</p>

<h2 id="왜-갈아엎었나">왜 갈아엎었나</h2>

<p>Spring Boot 기반 거래소는 잘 돌아갔습니다. 그런데 운영을 하다 보니 두 가지가 계속 마음에 걸렸습니다.</p>

<p><strong>메모리 풋프린트.</strong> 마이크로서비스 17개를 띄우면 JVM 힙만으로도 만만치 않습니다. ECS Fargate 비용은 메모리에 비례하는데, 서비스 하나당 512MB~1GB씩 잡아먹으니 개인이 감당하기엔 부담스러웠습니다.</p>

<p><strong>매칭 엔진 레이턴시.</strong> 체결 로직은 GC 멈춤이 없어야 합니다. Java로도 튜닝하면 되지만, “이 부분만큼은 GC 자체가 없는 언어로 다시 쓰고 싶다”는 생각이 계속 들었습니다.</p>

<p>그래서 결정했습니다. <strong>API 서비스 13개는 Go로, 매칭 엔진 2개(현물·선물)는 Rust로.</strong> 레거시 Java 코드는 지우지 않고 <code class="language-plaintext highlighter-rouge">backend/</code> 폴더에 마이그레이션 레퍼런스로 남겨뒀습니다. 새 코드를 짜다가 “원래 로직이 뭐였지?” 싶을 때 바로 대조할 수 있어서, 이 결정이 생각보다 큰 도움이 됐습니다.</p>

<h2 id="rust-매칭-엔진">Rust 매칭 엔진</h2>

<p>핵심은 오더북입니다. 가격-시간 우선순위(price-time priority)를 자료구조로 표현하면 이렇게 됩니다.</p>

<ul>
  <li>매수 호가: <code class="language-plaintext highlighter-rouge">BTreeMap&lt;Reverse&lt;Decimal&gt;, VecDeque&lt;Order&gt;&gt;</code> — 높은 가격 우선</li>
  <li>매도 호가: <code class="language-plaintext highlighter-rouge">BTreeMap&lt;Decimal, VecDeque&lt;Order&gt;&gt;</code> — 낮은 가격 우선</li>
  <li>같은 가격 레벨 안에서는 <code class="language-plaintext highlighter-rouge">VecDeque</code>로 FIFO</li>
</ul>

<p>금액은 전부 <code class="language-plaintext highlighter-rouge">rust_decimal</code>을 썼습니다. 부동소수점으로 돈 계산을 하면 언젠가 반드시 사고가 납니다.</p>

<p>선물 엔진은 현물보다 훨씬 복잡했습니다. 청산(liquidation), 펀딩비(funding rate), TP/SL, 포지션 관리까지 — 파일 하나하나가 수백 줄짜리 상태 머신입니다. 솔직히 이 부분은 Claude Code가 없었으면 몇 달은 걸렸을 겁니다. 제가 거래소에서 5년 일하며 쌓은 도메인 지식으로 “청산 가격 계산은 이 공식이고, 엣지 케이스는 이런 게 있다”를 정확히 짚어주면, 구현과 테스트는 AI가 빠르게 채워주는 분업이 잘 맞았습니다.</p>

<h2 id="재미있었던-설계-결정-하나">재미있었던 설계 결정 하나</h2>

<p>매칭 엔진은 반드시 <strong>싱글톤</strong>이어야 합니다. 두 개가 동시에 뜨면 체결 순서가 꼬입니다. 보통은 코드에서 분산 락으로 풀지만, 저는 인프라에서 강제했습니다.</p>

<p>Terraform에서 <code class="language-plaintext highlighter-rouge">deployment_minimum_healthy_percent = 0</code>으로 설정하면, ECS가 배포할 때 기존 태스크를 먼저 죽이고 새 태스크를 띄웁니다. 순단은 생기지만 두 엔진이 동시에 존재하는 순간이 아예 없습니다. 주문은 Kafka에 durable하게 쌓여 있으니 몇 초의 순단은 문제가 되지 않고요. <strong>코드로 풀 문제와 인프라로 풀 문제를 구분하는 것</strong> — 이게 연차가 주는 감각이 아닐까 싶습니다.</p>

<h2 id="결과">결과</h2>

<ul>
  <li>Go 서비스 13개 + Rust 엔진 2개, 합쳐서 <strong>48,000줄 이상</strong></li>
  <li>MySQL 테이블 102개, Kafka 체결 파이프라인, STOMP 실시간 시세</li>
  <li>Terraform으로 AWS ECS Fargate 배포, 메모리 사용량은 Java 대비 체감 1/3 수준</li>
  <li>라이브 데모로 한동안 운영하며 검증까지 마쳤습니다 (지금은 AWS 비용 문제로 내렸습니다 — 만들면서 배울 건 다 배웠으니 미련은 없습니다)</li>
</ul>

<p>“처음부터 새로 만들었다”보다 “레거시를 남겨두고 재플랫폼했다”가 정확한 표현입니다. 그리고 재플랫폼이 훨씬 어렵습니다. 기존 동작을 전부 보존해야 한다는 제약이 있으니까요. 다음 글에서는 이 과정에서 만든 갭 분석 문서와 검증 방법을 다뤄보겠습니다.</p>]]></content><author><name>김완기 (Michael Kim)</name></author><category term="Project" /><category term="Go" /><category term="Rust" /><category term="MSA" /><category term="거래소" /><category term="Claude Code" /><summary type="html"><![CDATA[거래소 시리즈 세 번째 글입니다. 1편과 2편에서 Java/Spring으로 거래소를 만들었다면, 이번에는 그걸 통째로 갈아엎은 이야기입니다.]]></summary></entry><entry><title type="html">AI와 함께 코드를 쓴다는 것 — 15개 프로젝트를 지나며 배운 것들</title><link href="https://wankikim.github.io/essay/what-i-learned-from-15-projects/" rel="alternate" type="text/html" title="AI와 함께 코드를 쓴다는 것 — 15개 프로젝트를 지나며 배운 것들" /><published>2026-02-21T11:44:11+09:00</published><updated>2026-02-21T11:44:11+09:00</updated><id>https://wankikim.github.io/essay/what-i-learned-from-15-projects</id><content type="html" xml:base="https://wankikim.github.io/essay/what-i-learned-from-15-projects/"><![CDATA[<hr />

<p>저는 요즘 코드를 “짠다”기보다 “대화한다”는 표현이 더 맞는 것 같습니다.</p>

<p>Claude Code 앞에 앉아서 “이 시스템의 아키텍처를 이렇게 잡아볼까?”라고 말하고, 돌아오는 답변을 보면서 “아, 여기는 이렇게 하면 안 되겠다” 하고 다시 방향을 잡고. 그러다 보면 어느새 수만 줄의 코드가 만들어져 있습니다. 지난 몇 달간 이런 방식으로 15개가 넘는 프로젝트를 만들었습니다. 암호화폐 거래소, AI 부동산 분석 플랫폼, 12개짜리 마이크로서비스, 커피머신 키오스크까지. 직접 해보면서 느낀 것들을 솔직하게 나눠보려고 합니다.</p>

<hr />

<h2 id="개발자는-앞으로-어떻게-바뀌어야-할까">개발자는 앞으로 어떻게 바뀌어야 할까</h2>

<p>솔직히 말하면, 저도 처음에는 반신반의했습니다. “AI가 코드를 짜준다고? 그래봐야 간단한 스크립트 수준이겠지.” 그런데 직접 써보니까 생각이 완전히 바뀌었습니다.</p>

<p><strong>KoCoin 거래소</strong> 를 만들 때였습니다. Spring Boot로 17개 마이크로서비스 — 매칭 엔진, 선물 거래, 지갑, 마켓메이킹 봇까지. 혼자서 이걸 설계하고 구현하려면 몇 달은 걸릴 작업이었는데, Claude Code와 함께하니까 아키텍처 논의부터 Terraform 인프라 코드까지 흐름이 끊기지 않았습니다. 물론 AI가 뱉어낸 코드를 그대로 쓴 건 아닙니다. 중요한 건 <strong>내가 방향을 잡고, AI가 속도를 내주는</strong> 그 조합이었습니다.</p>

<p>이 경험을 통해 확신하게 된 게 있습니다. 앞으로 개발자에게 필요한 건 “모든 코드를 외우고 있는 능력”이 아니라 <strong>“무엇을 만들어야 하는지 정확히 아는 능력”</strong> 이라는 거요. 구체적으로 말하면 이렇습니다.</p>

<p><strong>설계 능력이 코딩 능력보다 중요해집니다.</strong> Zelotek 프로젝트에서 레거시 PHP 시스템을 12개 MSA로 전환할 때, 진짜 어려웠던 건 코드를 치는 게 아니었습니다. “어떤 서비스를 어떻게 나눌까”, “서비스 간 통신은 동기로 할까 비동기로 할까”, “분산 트랜잭션은 Saga를 쓸까 2PC를 쓸까” — 이런 아키텍처 결정이 훨씬 중요했습니다. AI는 Strangler Pattern으로 점진적 마이그레이션하자는 제안을 해줬고, 저는 그 제안의 타당성을 판단하고 우리 상황에 맞게 조정했습니다. 이게 앞으로 개발자의 역할이라고 생각합니다.</p>

<p><strong>“질문하는 능력”이 새로운 핵심 역량이 됩니다.</strong> AI에게 “로그인 기능 만들어줘”라고 하면 평범한 코드가 나옵니다. 하지만 “JWT 기반 인증인데, 리프레시 토큰은 Redis에 저장하고, Rate Limiting은 IP당 10회/15분으로 걸어야 해. XSS랑 NoSQL Injection 방어도 넣어줘”라고 하면 완전히 다른 수준의 결과가 나옵니다. Hunik 홈페이지를 만들면서 보안 점수 98/100을 달성할 수 있었던 건, 제가 “뭘 물어봐야 하는지”를 알고 있었기 때문입니다.</p>

<p><strong>풀스택이 진짜 가능해집니다.</strong> 예전에는 “풀스택 개발자”라고 하면 좀 애매한 느낌이 있었잖아요. 백엔드를 깊이 하면 프론트가 약하고, 프론트를 잘하면 인프라는 모르고. 그런데 AI와 함께하면 자기 전문 영역 외의 부분에서도 프로덕션 수준의 코드를 만들 수 있습니다. 저는 DDangZip 프로젝트에서 Spring Cloud Gateway, Eureka, Resilience4j로 MSA 인프라를 만들면서 동시에 Flutter로 모바일 앱까지 개발했습니다. 이전이었다면 최소 2~3명이 필요했을 작업입니다.</p>

<hr />

<h2 id="프로젝트를-하면서-느낀-것들">프로젝트를 하면서 느낀 것들</h2>

<h3 id="ai가-대신-해주는-게-아니라-나를-확장시켜주는-것">“AI가 대신 해주는 게 아니라, 나를 확장시켜주는 것”</h3>

<p>가장 크게 느낀 건 이겁니다. AI는 저를 대체하지 않았습니다. 대신 제가 할 수 있는 것의 범위를 넓혀줬습니다.</p>

<p>BUJA 부동산 분석 플랫폼을 만들 때, OpenAI GPT-4o-mini와 Google Gemini를 동시에 돌리는 듀얼 AI 분석 시스템을 설계했습니다. 정부 실거래가 데이터를 끌어와서 선형 회귀로 가격을 예측하고, 카카오맵으로 주변 인프라를 분석하고, 네이버 뉴스에서 부동산 정책을 모니터링하고. 이 모든 걸 Node.js 백엔드, Flutter 앱, React 어드민으로 만들었는데 — 솔직히 저 혼자의 지식만으로는 이 모든 기술을 한 프로젝트에 녹여내기 어려웠을 겁니다.</p>

<p>하지만 그렇다고 제가 아무것도 안 한 건 아닙니다. “부동산 투자 분석에 뭐가 필요한지”, “AI 두 개를 동시에 돌리면 어떤 비교 가치가 생기는지” — 이런 비즈니스 판단은 AI가 해줄 수 없는 영역이었습니다.</p>

<h3 id="실패에서-배우는-속도가-완전히-달라졌다">“실패에서 배우는 속도가 완전히 달라졌다”</h3>

<p>ERP 시스템을 만들면서 경험한 건데, GPT-4 기반 AI 고객 지원 모듈을 처음 설계할 때 구조를 잘못 잡았습니다. 티켓 시스템, 계약 관리, 생산 관리, 설치 관리 — 9개 모듈이 서로 얽혀있는데 의존 관계를 제대로 정리하지 못했거든요. 예전 같았으면 리팩토링에만 며칠을 써야 했을 겁니다. 그런데 AI와 함께하니까 “이 구조의 문제점이 뭔지”, “어떻게 풀어야 하는지”를 빠르게 논의하고, 몇 시간 만에 WebSocket 기반 실시간 알림까지 포함한 새 구조로 전환할 수 있었습니다.</p>

<p>실패 자체가 줄어든 건 아닙니다. 하지만 실패에서 회복하는 속도가 압도적으로 빨라졌습니다.</p>

<h3 id="하드웨어와-소프트웨어의-경계에서도-ai는-유효했다">“하드웨어와 소프트웨어의 경계에서도 AI는 유효했다”</h3>

<p>커피머신 키오스크 소프트웨어(M300)와 프로토콜 테스터(TestHunik)를 만들면서 의외의 발견을 했습니다. RS-232 시리얼 통신, HEX/ASCII 패킷 파싱, 0.5초 간격의 폴링 — 이런 로우레벨 하드웨어 연동 작업에서도 AI가 꽤 유용하다는 거였습니다. 물론 실제 기기에 연결해서 테스트하는 건 제가 직접 해야 했지만, 프로토콜 파서를 짜거나 우선순위 기반 명령 큐를 설계하는 과정에서는 AI의 도움이 컸습니다.</p>

<h3 id="auto-claude를-만들면서-한-단계-더-갔다">“Auto-Claude를 만들면서 한 단계 더 갔다”</h3>

<p>가장 재미있었던 프로젝트는 Auto-Claude입니다. AI 에이전트가 소프트웨어를 자율적으로 계획하고, 구현하고, 검증하는 프레임워크를 만든 거예요. 12개 터미널에서 에이전트가 병렬로 코드를 짜고, 각각 격리된 Git Worktree에서 작업하고, QA 루프가 자동으로 돌아가면서 품질을 잡고. 이걸 만들면서 “AI를 쓰는 것”에서 “AI를 만드는 것”으로 한 단계 넘어간 느낌이었습니다.</p>

<hr />

<h2 id="앞으로-어떻게-개발할-것인가">앞으로 어떻게 개발할 것인가</h2>

<p>이 모든 경험을 통해 제가 내린 결론은 하나입니다.</p>

<p><strong>AI와의 협업은 선택이 아니라 기본기가 된다.</strong></p>

<p>마치 Git이 그랬던 것처럼요. 10년 전에는 “Git을 쓸 줄 아는 것”이 스킬이었는데, 지금은 그냥 당연한 거잖아요. AI 협업도 마찬가지가 될 겁니다. 3년 후에는 “AI와 함께 개발한다”는 게 특별한 능력이 아니라, 모든 개발자의 기본 워크플로우가 될 거라고 봅니다.</p>

<p>그래서 제가 앞으로 집중하려는 방향은 이렇습니다.</p>

<p><strong>1. AI 에이전트 시스템을 더 깊이 파겠습니다.</strong> Auto-Claude를 만들면서 맛을 봤는데, 멀티 에이전트가 협업하는 구조에는 아직 풀어야 할 문제가 산더미입니다. 에이전트 간 컨텍스트 공유, 충돌 해결, 품질 보증 — 이 영역이 앞으로 소프트웨어 개발의 게임 체인저가 될 거라고 확신합니다.</p>

<p><strong>2. 아키텍처 역량을 계속 넓히겠습니다.</strong> KoCoin에서 Kafka와 17개 마이크로서비스를, Zelotek에서 Saga Pattern과 이벤트 기반 아키텍처를 경험하면서, 분산 시스템의 깊이를 느꼈습니다. AI가 코드를 더 잘 짜주게 될수록, “어떤 구조로 만들 것인가”를 결정하는 아키텍트의 가치는 더 올라갈 겁니다.</p>

<p><strong>3. 비즈니스와 기술의 연결고리가 되겠습니다.</strong> BUJA에서 부동산 투자 분석을, ERP에서 커피머신 사업의 운영 시스템을 만들면서 깨달은 건, 결국 좋은 소프트웨어는 비즈니스 문제를 정확히 이해하는 데서 출발한다는 겁니다. AI가 아무리 좋아져도, “뭘 만들어야 하는지”를 아는 사람은 여전히 필요합니다. 그리고 그게 제가 되고 싶은 개발자의 모습입니다.</p>

<hr />

<p>빈 폴더에서 시작해서 80만 줄이 넘는 코드를 AI와 함께 만들었습니다. 암호화폐 거래소의 매칭 엔진부터 커피머신의 시리얼 통신까지. 이 여정에서 가장 크게 배운 건, AI 시대의 개발자에게 가장 중요한 건 “코드를 잘 치는 것”이 아니라 <strong>“무엇을 왜 만들어야 하는지 아는 것”</strong> 이라는 사실입니다.</p>

<p>항상 자신에게 질문하는 개발자로 함께 나아가요.</p>

<hr />]]></content><author><name>김완기 (Michael Kim)</name></author><category term="Essay" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">AI와 함께 개발하면 공수가 얼마나 줄어들까? — ERP 실전 사례</title><link href="https://wankikim.github.io/ai/erp-manhour-reduction-with-ai/" rel="alternate" type="text/html" title="AI와 함께 개발하면 공수가 얼마나 줄어들까? — ERP 실전 사례" /><published>2026-02-20T16:24:13+09:00</published><updated>2026-02-20T16:24:13+09:00</updated><id>https://wankikim.github.io/ai/erp-manhour-reduction-with-ai</id><content type="html" xml:base="https://wankikim.github.io/ai/erp-manhour-reduction-with-ai/"><![CDATA[<blockquote>
  <p>1인 개발자가 AI(Claude Code)와 협업하여 엔터프라이즈급 ERP 플랫폼을 구축한 실전 경험기.<br />
기존 개발팀 대비 약 <strong>10배의 생산성 향상</strong> 을 체감한 과정을 공유합니다.</p>
</blockquote>

<hr />

<h2 id="1-프로젝트-소개">1. 프로젝트 소개</h2>

<p><strong>HunikFlow</strong> 는 한국 시장을 위한 로우코드(Low-Code) 엔터프라이즈 플랫폼입니다.</p>

<ul>
  <li><strong>백엔드</strong> : Java 21 + Spring Boot 3.5 (Maven 모듈 17개)</li>
  <li><strong>프론트엔드</strong> : Angular 21 + ng-zorro-antd 21 + @delon 21</li>
  <li><strong>아키텍처</strong> : <code class="language-plaintext highlighter-rouge">@Flow</code> + <code class="language-plaintext highlighter-rouge">@FlowField</code> 어노테이션 기반 자동 CRUD 생성</li>
  <li><strong>모듈</strong> : CRM, HR, 재무, 자산, CS, 재고, 주문/배송, 프로젝트 관리, BPM 워크플로우, eCount ERP 연동 등</li>
</ul>

<h2 id="로그인화면">로그인화면</h2>

<p><img src="/assets/images/posts/9/img-01.png" alt="" /></p>

<p><em>HunikFlow 로그인 화면 — 한국어 UI, 깔끔한 디자인</em></p>

<hr />

<h2 id="2-기존-개발팀-방식의-예상-공수">2. 기존 개발팀 방식의 예상 공수</h2>

<p>만약 이 프로젝트를 <strong>전통적인 개발팀</strong> 에서 진행했다면 어떤 규모가 필요했을까요?</p>

<h3 id="프로젝트-범위-실제-구현된-내용">프로젝트 범위 (실제 구현된 내용)</h3>

<table>
  <thead>
    <tr>
      <th>영역</th>
      <th>내용</th>
      <th>산출물</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>플랫폼 현대화</td>
      <td>14개 중국 라이브러리 교체, i18n 시스템 구축 (625개 키)</td>
      <td>Phase 1~2</td>
    </tr>
    <tr>
      <td>보안 강화</td>
      <td>RCE 취약점 5건 수정, 기술 부채 제거</td>
      <td>Phase 3</td>
    </tr>
    <tr>
      <td>환경 업그레이드</td>
      <td>Java 21, Angular 21, 모든 라이브러리 최신화</td>
      <td>Phase 4</td>
    </tr>
    <tr>
      <td>전체 리브랜딩</td>
      <td>패키지명, 클래스명, DB 테이블, API 경로 전면 변경</td>
      <td>Phase 5~6</td>
    </tr>
    <tr>
      <td>ERP 모듈 (40+ 엔티티)</td>
      <td>CRM 10개, HR 9개, 재무 6개, 자산 5개, CS 3개, 재고 4개, 주문 3개</td>
      <td>엔티티 + CRUD</td>
    </tr>
    <tr>
      <td>프로젝트 관리 모듈</td>
      <td>프로젝트, 이슈, 스프린트, 칸반 보드, 백로그</td>
      <td>신규 개발</td>
    </tr>
    <tr>
      <td>BPM 워크플로우</td>
      <td>결재 프로세스 정의/실행/승인/반려</td>
      <td>신규 개발</td>
    </tr>
    <tr>
      <td>eCount ERP 연동</td>
      <td>API 인증, 품목/창고/재고 동기화, 스케줄러</td>
      <td>외부 연동</td>
    </tr>
    <tr>
      <td>QA 테스트</td>
      <td>363개 테스트 케이스 (99.4% 통과율)</td>
      <td>자동화 스크립트</td>
    </tr>
  </tbody>
</table>

<h3 id="전통적-개발팀-예상-공수">전통적 개발팀 예상 공수</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    팀 구성: 4~5명 (백엔드 2, 프론트엔드 1, QA 1, PM/아키텍트 1)
    
    Phase 1~3 (플랫폼 현대화 + 보안)    : 3~4주 × 3명 = 9~12 man-weeks
    Phase 4   (환경 업그레이드)           : 2~3주 × 2명 = 4~6 man-weeks
    Phase 5~6 (리브랜딩)                 : 3~4주 × 2명 = 6~8 man-weeks
    ERP 모듈 (40+ 엔티티)               : 4~6주 × 2명 = 8~12 man-weeks
    프로젝트 관리 모듈                    : 3~4주 × 2명 = 6~8 man-weeks
    BPM 워크플로우                       : 2~3주 × 2명 = 4~6 man-weeks
    eCount 연동                         : 2~3주 × 1명 = 2~3 man-weeks
    QA 테스트                           : 2~3주 × 1명 = 2~3 man-weeks
    ───────────────────────────────────────────────
    합계: 약 41~58 man-weeks (약 10~15 man-months)
</code></pre></div></div>

<p>현실적으로 <strong>최소 3~4개월</strong> , 인건비 기준 <strong>1.5억~2억 원</strong> 수준입니다.</p>

<hr />

<h2 id="3-ai와-1인-개발자의-실제-소요-시간">3. AI와 1인 개발자의 실제 소요 시간</h2>

<h3 id="실제-투입-공수">실제 투입 공수</h3>

<table>
  <thead>
    <tr>
      <th>단계</th>
      <th>기간</th>
      <th>작업 내용</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Phase 1~3</td>
      <td>2일</td>
      <td>14개 라이브러리 교체, i18n 625키, RCE 5건 수정</td>
    </tr>
    <tr>
      <td>Phase 4</td>
      <td>1일</td>
      <td>Java 21 + Angular 21 + 전체 라이브러리 업그레이드</td>
    </tr>
    <tr>
      <td>Phase 5~6</td>
      <td>2일</td>
      <td>패키지/클래스/DB/API 전면 리네이밍, 중국어 제거</td>
    </tr>
    <tr>
      <td>Phase 7</td>
      <td>0.5일</td>
      <td>런타임 버그 수정 (로그인, API 라우팅)</td>
    </tr>
    <tr>
      <td>ERP 모듈</td>
      <td>1일</td>
      <td>40개 엔티티 + DataProxy + KPI 컨트롤러</td>
    </tr>
    <tr>
      <td>프로젝트 관리</td>
      <td>1일</td>
      <td>10개 엔티티 + 칸반 보드 + 백로그 + 커스텀 API</td>
    </tr>
    <tr>
      <td>eCount 연동</td>
      <td>1일</td>
      <td>API 인증 + 동기화 서비스 + 대시보드</td>
    </tr>
    <tr>
      <td>QA 테스트</td>
      <td>1.5일</td>
      <td>363 TC 작성 + 실행 + 버그 수정 + 재테스트</td>
    </tr>
    <tr>
      <td><strong>합계</strong></td>
      <td><strong>~10일</strong></td>
      <td><strong>1인 개발자 + Claude Code</strong></td>
    </tr>
  </tbody>
</table>

<p><strong>10 man-days vs 41 ~ 58 man-weeks = 약 20 ~ 30배 효율 차이</strong></p>

<p>물론 단순 비교는 어렵습니다. 로우코드 프레임워크 위에서 작업했고, AI가 반복 패턴을 빠르게 생성했기 때문입니다. 그러나 핵심 포인트는 명확합니다:</p>

<blockquote>
  <p><strong>“AI는 반복적인 코드 생성과 패턴 적용에서 인간 개발자를 압도적으로 능가한다.”</strong></p>
</blockquote>

<hr />

<h2 id="4-ai가-특히-강력했던-영역">4. AI가 특히 강력했던 영역</h2>

<h3 id="4-1-대규모-리팩토링--패키지클래스-전면-리네이밍">4-1. 대규모 리팩토링 — 패키지/클래스 전면 리네이밍</h3>

<p>Phase 5에서 <code class="language-plaintext highlighter-rouge">xyz.erupt.*</code> → <code class="language-plaintext highlighter-rouge">kr.hunik.flow.*</code>로 전면 리네이밍을 진행했습니다.</p>

<ul>
  <li>Java 패키지: 수백 개 파일의 import/package 변경</li>
  <li>Maven POM: groupId, artifactId, module 이름 일괄 변경</li>
  <li>DB 테이블: <code class="language-plaintext highlighter-rouge">e_</code> → <code class="language-plaintext highlighter-rouge">hf_</code> 접두사 변경</li>
  <li>API 경로: <code class="language-plaintext highlighter-rouge">/erupt-api/</code> → <code class="language-plaintext highlighter-rouge">/api/v1/</code> 변경</li>
  <li>Angular 컴포넌트: 셀렉터, 모듈명 변경</li>
</ul>

<p>전통적 방법이라면 IDE의 리팩토링 기능 + 수동 확인으로 1~2주가 걸릴 작업을, AI와 함께 <strong>2일 만에 완료</strong> 했습니다. AI는 패턴을 인식하고 일관성 있게 적용하는 데 탁월합니다.</p>

<h3 id="4-2-반복-엔티티-생성--40개-erp-모듈">4-2. 반복 엔티티 생성 — 40개 ERP 모듈</h3>

<p>40개 이상의 엔티티를 <code class="language-plaintext highlighter-rouge">@Flow</code> + <code class="language-plaintext highlighter-rouge">@FlowField</code> 어노테이션으로 정의했습니다. AI에게 “CRM 모듈에 거래처, 담당자, 제품, 영업기회, 견적서, 수주, 계약, 청구서, 영업활동, 분석 엔티티를 만들어줘”라고 요청하면, 각 엔티티의 필드 정의부터 관계 매핑, DataProxy, 검색 조건까지 일관된 패턴으로 생성합니다.</p>

<h3 id="crm-거래처-관리">CRM 거래처 관리</h3>

<p><img src="/assets/images/posts/9/img-02.png" alt="" /></p>

<p><em>CRM 거래처 목록 — 13건의 테스트 데이터가 자동 생성된 모습</em></p>

<p>###HR 직원 관리</p>

<p><img src="/assets/images/posts/9/img-03.png" alt="" /></p>

<p><em>HR 직원 관리 — 조직도 트리뷰 + 43명의 직원 데이터</em></p>

<h3 id="4-3-프론트엔드-커스텀-모듈--칸반-보드">4-3. 프론트엔드 커스텀 모듈 — 칸반 보드</h3>

<p>프로젝트 관리 모듈의 칸반 보드는 <code class="language-plaintext highlighter-rouge">@Flow</code> 어노테이션만으로는 구현할 수 없는 커스텀 UI입니다. Angular CDK Drag &amp; Drop을 활용한 칸반 보드, 백로그 뷰, 이슈 상세 패널을 AI가 설계부터 구현까지 담당했습니다.</p>

<h3 id="프로젝트-crud">프로젝트 CRUD</h3>

<p><img src="/assets/images/posts/9/img-04.png" alt="" /></p>

<p><em>프로젝트 관리 — 자동 생성된 CRUD 테이블</em></p>

<h3 id="이슈-관리">이슈 관리</h3>

<p><img src="/assets/images/posts/9/img-05.png" alt="" /></p>

<p><em>이슈 트래커 — 프로젝트별 이슈 목록, 스프린트/백로그 이동 오퍼레이터</em></p>

<h3 id="칸반-보드">칸반 보드</h3>

<p><img src="/assets/images/posts/9/img-06.png" alt="" /></p>

<p><em>칸반 보드 — 드래그 앤 드롭 기반의 이슈 상태 관리</em></p>

<h3 id="4-4-외부-api-연동--ecount-erp">4-4. 외부 API 연동 — eCount ERP</h3>

<p>eCount의 OAPI V2 API 스펙을 분석하고, 인증(Zone 조회 → 로그인 → SESSION_ID), 품목/창고/재고 동기화, 스케줄러, 대시보드까지 하루 만에 구현했습니다.</p>

<ul>
  <li><strong>3,531건</strong> 품목 동기화</li>
  <li><strong>39개</strong> 창고 매핑</li>
  <li><strong>2,388건</strong> 재고 현황 동기화</li>
</ul>

<h3 id="4-5-qa-자동화--363-테스트-케이스">4-5. QA 자동화 — 363 테스트 케이스</h3>

<p>AI가 테스트 스크립트를 작성하고 실행한 뒤, 실패한 케이스의 원인을 분석하여 수정까지 진행했습니다.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    Phase 1 단위 테스트:    95 TC → 94 PASS (98.9%)
    Phase 2 기능 테스트:    56 TC → 56 PASS (100%)
    Phase 3 시나리오 테스트: 56 TC → 54 PASS (96.4%)
    워크플로우 QA:          16 TC → 16 PASS (100%)
    성숙도 90% E2E:       140 TC → 139 PASS (99.3%)
    ────────────────────────────────────────────
    총합: 363 TC → 359 PASS, 3 FAIL, 1 SKIP (99.4%)
</code></pre></div></div>

<hr />

<h2 id="5-ai가-약했던-영역-솔직한-평가">5. AI가 약했던 영역 (솔직한 평가)</h2>

<h3 id="5-1-복잡한-런타임-디버깅">5-1. 복잡한 런타임 디버깅</h3>

<p>Phase 7에서 로그인이 안 되는 버그를 찾는 데 시간이 걸렸습니다. <code class="language-plaintext highlighter-rouge">@FlowRouter</code>의 <code class="language-plaintext highlighter-rouge">skipAuthIndex</code> 계산이 API 경로 변경(<code class="language-plaintext highlighter-rouge">/erupt-api</code> → <code class="language-plaintext highlighter-rouge">/api/v1</code>)으로 인해 깨진 것인데, AI가 여러 시도 끝에 해결했습니다. 컴파일 에러가 아닌 런타임 동작 오류는 AI도 시행착오가 필요합니다.</p>

<h3 id="5-2-프레임워크-내부-동작-이해">5-2. 프레임워크 내부 동작 이해</h3>

<p>HunikFlow(구 Erupt)의 내부 동작 — 특히 <code class="language-plaintext highlighter-rouge">FlowUtil.validateFlowValue()</code> → <code class="language-plaintext highlighter-rouge">flowInsertDataProcess()</code> → <code class="language-plaintext highlighter-rouge">DataProxy.beforeAdd()</code> 실행 순서 같은 미묘한 부분은 코드를 깊이 분석한 후에야 파악할 수 있었습니다.</p>

<h3 id="5-3-uiux-디자인-판단">5-3. UI/UX 디자인 판단</h3>

<p>기능적으로는 완벽하지만, 색상 선택이나 레이아웃 배치 같은 디자인 감각은 여전히 인간의 영역입니다. AI는 “작동하는 UI”는 빠르게 만들지만, “아름다운 UI”는 디자이너의 눈이 필요합니다.</p>

<hr />

<h2 id="6-결과물--실제-동작하는-화면">6. 결과물 — 실제 동작하는 화면</h2>

<h3 id="메인-대시보드-20-메뉴">메인 대시보드 (20+ 메뉴)</h3>

<h3 id="메인-대시보드">메인 대시보드</h3>

<p><img src="/assets/images/posts/9/img-07.png" alt="" /></p>

<p><em>사이드바에 20개 이상의 모듈이 표시됨 — CRM, HR, 자산, CS, 재고, 주문/배송, 경리, 프로젝트 관리 등</em></p>

<h3 id="프로젝트-관리-모듈-신규-개발">프로젝트 관리 모듈 (신규 개발)</h3>

<table>
  <thead>
    <tr>
      <th>화면</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>프로젝트</td>
      <td>프로젝트 CRUD — 키, 이름, 리더, 유형, 상태</td>
    </tr>
    <tr>
      <td>이슈</td>
      <td>이슈 관리 — 이슈 키, 제목, 유형, 상태, 우선순위</td>
    </tr>
    <tr>
      <td>칸반</td>
      <td>칸반 보드 — Drag &amp; Drop 상태 전환</td>
    </tr>
  </tbody>
</table>

<h3 id="erp-모듈">ERP 모듈</h3>

<table>
  <thead>
    <tr>
      <th>화면</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>CRM</td>
      <td>CRM 거래처 — 코드, 이름, 업종, 사업자번호, 등급, 상태</td>
    </tr>
    <tr>
      <td>HR</td>
      <td>HR 직원 관리 — 조직도 트리 + 직원 목록 (43명)</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="7-생산성-비교-요약">7. 생산성 비교 요약</h2>

<table>
  <thead>
    <tr>
      <th>지표</th>
      <th>전통 개발팀 (4~5명)</th>
      <th>1인 + AI (Claude Code)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>총 기간</strong></td>
      <td>3~4개월</td>
      <td>~10일</td>
    </tr>
    <tr>
      <td><strong>투입 인력</strong></td>
      <td>4~5명</td>
      <td>1명</td>
    </tr>
    <tr>
      <td><strong>총 공수</strong></td>
      <td>41~58 man-weeks</td>
      <td>~2 man-weeks</td>
    </tr>
    <tr>
      <td><strong>인건비 (추정)</strong></td>
      <td>1.5~2억 원</td>
      <td>0.15~0.2억 원</td>
    </tr>
    <tr>
      <td><strong>테스트 커버리지</strong></td>
      <td>수동 QA 위주</td>
      <td>363 자동화 TC (99.4%)</td>
    </tr>
    <tr>
      <td><strong>코드 일관성</strong></td>
      <td>개발자별 차이</td>
      <td>AI 기반 균일한 패턴</td>
    </tr>
    <tr>
      <td><strong>리팩토링 속도</strong></td>
      <td>1~2주</td>
      <td>2일</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="8-핵심-교훈">8. 핵심 교훈</h2>

<h3 id="ai와-협업할-때-효과적인-전략">AI와 협업할 때 효과적인 전략</h3>

<ol>
  <li><strong>명확한 패턴을 먼저 정의하라</strong>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">@Flow</code> + <code class="language-plaintext highlighter-rouge">@FlowField</code> 같은 반복 패턴이 있으면 AI가 수십 개 엔티티를 일관되게 생성합니다.</li>
    </ul>
  </li>
  <li><strong>PDCA 사이클을 적용하라</strong>
    <ul>
      <li>Plan → Design → Do → Check → Act 순서로 진행하면 AI가 맥락을 놓치지 않습니다.</li>
      <li>Gap Analysis(설계 대비 구현 비교)로 누락을 자동 검출합니다.</li>
    </ul>
  </li>
  <li><strong>테스트를 AI에게 맡겨라</strong>
    <ul>
      <li>테스트 케이스 작성, 실행, 실패 원인 분석, 버그 수정까지 AI가 순환합니다.</li>
      <li>363개 TC를 수동으로 작성했다면 최소 1주일은 걸렸을 것입니다.</li>
    </ul>
  </li>
  <li><strong>디버깅은 충분한 컨텍스트를 제공하라</strong>
    <ul>
      <li>“로그인이 안 돼”보다 “로그인 API가 401을 반환하는데, FlowSecurityInterceptor의 skipAuthIndex 계산이 의심된다”가 훨씬 빠릅니다.</li>
    </ul>
  </li>
  <li><strong>외부 API 연동은 문서를 먼저 읽혀라</strong>
    <ul>
      <li>eCount 연동 시 API 문서 URL을 AI에게 제공하니 스펙 분석부터 구현까지 자동으로 진행했습니다.</li>
    </ul>
  </li>
</ol>

<hr />

<h2 id="9-결론">9. 결론</h2>

<p>AI는 <strong>개발 보조 도구가 아니라 페어 프로그래밍 파트너</strong> 입니다.</p>

<p>특히 엔터프라이즈 ERP처럼 반복 패턴이 많고, 대규모 리팩토링이 필요하며, 다양한 모듈을 빠르게 구축해야 하는 프로젝트에서 AI의 가치는 극대화됩니다.</p>

<p><strong>10일 만에 엔터프라이즈급 ERP를 구축한 경험</strong> 은 AI 시대의 소프트웨어 개발이 어디로 향하고 있는지를 보여줍니다. 중요한 것은 AI를 단순한 코드 생성기로 쓰지 않고, 설계-구현-검증의 전체 사이클에 통합하는 것입니다.</p>

<blockquote>
  <p><strong>“개발자의 역할은 코드를 직접 타이핑하는 것에서, 올바른 방향을 설정하고 AI의 결과물을 검증하는 것으로 변하고 있다.”</strong></p>
</blockquote>

<hr />

<p><em>이 글은 실제 프로젝트 경험을 바탕으로 작성되었으며, 모든 스크린샷은 실제 동작하는 시스템에서 캡처한 것입니다.</em></p>

<p><strong>작성자</strong> : KimWanKi (<a href="mailto:kwg@hunik.kr">kwg@hunik.kr</a>)<br />
<strong>회사</strong> : Hunik Inc. (<a href="https://www.hunik.kr">https://www.hunik.kr</a>)<br />
<strong>프로젝트</strong> : HunikFlow — Low-Code Enterprise Platform</p>

<p><img src="/assets/images/posts/9/img-08.png" alt="" /> <img src="/assets/images/posts/9/img-09.png" alt="" /> <img src="/assets/images/posts/9/img-10.png" alt="" /> <img src="/assets/images/posts/9/img-11.png" alt="" /> <img src="/assets/images/posts/9/img-12.png" alt="" /> <img src="/assets/images/posts/9/img-13.png" alt="" /> <img src="/assets/images/posts/9/img-14.png" alt="" /></p>]]></content><author><name>김완기 (Michael Kim)</name></author><category term="AI" /><summary type="html"><![CDATA[1인 개발자가 AI(Claude Code)와 협업하여 엔터프라이즈급 ERP 플랫폼을 구축한 실전 경험기. 기존 개발팀 대비 약 10배의 생산성 향상 을 체감한 과정을 공유합니다.]]></summary></entry><entry><title type="html">주말에 심심해서 만든 부동산 분석앱</title><link href="https://wankikim.github.io/ai/weekend-real-estate-app/" rel="alternate" type="text/html" title="주말에 심심해서 만든 부동산 분석앱" /><published>2026-02-13T11:18:49+09:00</published><updated>2026-02-13T11:18:49+09:00</updated><id>https://wankikim.github.io/ai/weekend-real-estate-app</id><content type="html" xml:base="https://wankikim.github.io/ai/weekend-real-estate-app/"><![CDATA[<p><img src="/assets/images/posts/8/img-01.png" alt="" /></p>

<p>##</p>

<h2 id="-기술-스택">🛠 기술 스택</h2>

<h3 id="backend">Backend</h3>

<ul>
  <li>Node.js 18+</li>
  <li>Express.js</li>
  <li>MongoDB (Mongoose)</li>
  <li>JWT</li>
  <li>Firebase Admin SDK</li>
  <li>OpenAI API</li>
  <li>Gemini</li>
</ul>

<h3 id="mobile">Mobile</h3>

<ul>
  <li>Flutter 3.24+</li>
  <li>Provider (상태관리)</li>
  <li>Dio (HTTP 통신)</li>
  <li>fl_chart (차트)</li>
  <li>firebase_messaging (푸시)</li>
</ul>

<h3 id="admin">Admin</h3>

<ul>
  <li>React 18</li>
  <li>Vite</li>
  <li>Material-UI</li>
  <li>Axios</li>
</ul>

<p>주말에 누워있다가 부동산 관련 뉴스를 보다가 “어..저긴 왜케 비싸!”</p>

<p>하다가 만든 앱.</p>

<p>실거래가는 정부에서 제공해주는 데이터 가져왔고. (공공데이터)</p>

<p>카카오 API로 주변 인프라 데이터 읽어와서 분석을 맡겼습니다.</p>

<p>머..네이버 부동산 (호가) 연동하면 더 정확하고 확실하게 나오긴하겠지만..</p>

<p>귀차니즘!!</p>]]></content><author><name>김완기 (Michael Kim)</name></author><category term="AI" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Claude Code 로 거래소 만들기(2)</title><link href="https://wankikim.github.io/ai/building-exchange-with-claude-code-2/" rel="alternate" type="text/html" title="Claude Code 로 거래소 만들기(2)" /><published>2026-02-13T08:06:28+09:00</published><updated>2026-02-13T08:06:28+09:00</updated><id>https://wankikim.github.io/ai/building-exchange-with-claude-code-2</id><content type="html" xml:base="https://wankikim.github.io/ai/building-exchange-with-claude-code-2/"><![CDATA[<p>전편에 이어서..</p>

<p><img src="/assets/images/posts/7/img-01.png" alt="" /> <img src="/assets/images/posts/7/img-02.png" alt="" /> <img src="/assets/images/posts/7/img-03.png" alt="" /></p>

<p>머 어느정도 완료는 되었습니다.</p>

<p>더 이상 고도화 할 필요는 없어서 여기서 멈추지만 주문 체결 선물 포지션 청산 까지 구현은 했고</p>

<p>TPS 도 잘 나오네요. 더 고도화 하다간 제 컴터가 먼저 하늘나라 갈꺼 같아서…</p>

<h3 id="시스템-아키텍처">시스템 아키텍처</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    [사용자 브라우저] → [CloudFront/S3] → [ALB] → [API Gateway :8080]
                                                          │
                        ┌──────────────────────────────────┼──────────────────────────┐
                        │                                  │                          │
                  [UCenter :6001]                  [Exchange API :6004]        [Market :6003]
                  (회원/인증/KYC)                   (현물거래 API)              (시세/WebSocket)
                        │                                  │                          │
                  [OTC API :6002]                [Exchange Engine :6005]      [Futures API :6009]
                  (P2P 거래)                      (주문 매칭 엔진)            (선물거래 API)
                        │                                  │                          │
                  [Wallet :6006]                [Futures Engine :6008]         [Chat :6007]
                  (입출금)                        (선물 매칭 엔진)              (메시징)
                        │                                  │                          │
                  [Admin :6010]                   [Robot :20000]               [Redis 7]
                  (관리자 API)               (시장조성 봇 - 바이낸스 추종)      (캐시/세션)
                        │                                  │                          │
                        └──────────────[MySQL 8.0]─────────┼────[MongoDB 7.0]─────────┘
                                                           │
                                                      [Kafka 3.6]
                                                  (이벤트 스트리밍)
</code></pre></div></div>

<h3 id="핵심-기능">핵심 기능</h3>

<p><strong>거래 기능</strong> 은 현물 거래(실시간 오더북 + 캔들차트), 선물 거래(레버리지 지원), OTC P2P 법정화폐 거래(에스크로), 그리고 바이낸스/OKX/HTX 시세를 추종하는 자동 시장조성 봇까지 포함합니다.</p>

<p><strong>사용자 기능</strong> 으로는 이메일/SMS 인증 회원가입, KYC 본인인증(신분증 업로드), 2FA, 다중 화폐 지갑(입출금), 추천인 시스템(커미션 추적)이 있습니다.</p>

<p><strong>관리자 기능</strong> 은 회원 관리, KYC 승인 워크플로우, 거래쌍/코인 설정, 주문 모니터링, 재무 리포트, 봇 제어까지 60개 이상의 관리 페이지로 구성됩니다.</p>

<p>이걸 aws 테라폼으로 배포한다고 할때..</p>

<h2 id="성능-분석-사양별-수용-가능-유저-수">성능 분석: 사양별 수용 가능 유저 수</h2>

<p>이 시스템의 성능 병목 지점을 분석하고, 서버 사양별로 수용 가능한 유저 수를 추정했습니다.</p>

<h3 id="병목-지점-분석">병목 지점 분석</h3>

<table>
  <thead>
    <tr>
      <th>구간</th>
      <th>병목 요소</th>
      <th>영향</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>매칭 엔진</strong></td>
      <td>심볼당 단일 스레드 synchronized 처리</td>
      <td>단일 거래쌍 최대 처리량 제한</td>
    </tr>
    <tr>
      <td><strong>Gateway</strong></td>
      <td>Redis Rate Limiter + Connection Pool</td>
      <td>동시 접속 수 제한</td>
    </tr>
    <tr>
      <td><strong>Kafka</strong></td>
      <td>브로커 수 × 파티션 수</td>
      <td>메시지 처리 처리량 제한</td>
    </tr>
    <tr>
      <td><strong>MySQL</strong></td>
      <td>커넥션 풀(HikariCP max 50) + I/O</td>
      <td>주문/체결 영속화 병목</td>
    </tr>
    <tr>
      <td><strong>WebSocket</strong></td>
      <td>Market 서비스 메모리 + 네트워크 대역폭</td>
      <td>실시간 시세 구독자 수 제한</td>
    </tr>
  </tbody>
</table>

<h3 id="사양별-성능-추정표">사양별 성능 추정표</h3>

<h4 id="tier-1-스테이징개발-환경-410월">Tier 1: 스테이징/개발 환경 (~$410/월)</h4>

<table>
  <thead>
    <tr>
      <th>구성 요소</th>
      <th>사양</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ECS Fargate</td>
      <td>서비스당 0.5 vCPU / 1GB RAM (12개 서비스)</td>
    </tr>
    <tr>
      <td>MySQL</td>
      <td>db.t3.micro (2 vCPU, 1GB)</td>
    </tr>
    <tr>
      <td>Redis</td>
      <td>cache.t3.micro</td>
    </tr>
    <tr>
      <td>Kafka</td>
      <td>kafka.t3.small × 2 브로커</td>
    </tr>
    <tr>
      <td>MongoDB</td>
      <td>t3.micro EC2</td>
    </tr>
    <tr>
      <td>지표</td>
      <td>수치</td>
    </tr>
  </tbody>
  <tbody>
    <tr>
      <td><strong>동시 접속 유저</strong></td>
      <td>300 ~ 500명</td>
    </tr>
    <tr>
      <td><strong>주문 처리량</strong></td>
      <td>~200 건/초 (단일 거래쌍)</td>
    </tr>
    <tr>
      <td><strong>WebSocket 구독자</strong></td>
      <td>~1,000명</td>
    </tr>
    <tr>
      <td><strong>오더북 깊이</strong></td>
      <td>100 호가</td>
    </tr>
    <tr>
      <td><strong>API 응답 시간</strong></td>
      <td>50~200ms (p95)</td>
    </tr>
    <tr>
      <td><strong>일일 활성 유저</strong></td>
      <td>~2,000명</td>
    </tr>
  </tbody>
</table>

<h4 id="tier-2-소규모-프로덕션-1200월">Tier 2: 소규모 프로덕션 (~$1,200/월)</h4>

<table>
  <thead>
    <tr>
      <th>구성 요소</th>
      <th>사양</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ECS Fargate</td>
      <td>서비스당 1 vCPU / 2GB RAM</td>
    </tr>
    <tr>
      <td>MySQL</td>
      <td>db.t3.medium (2 vCPU, 4GB)</td>
    </tr>
    <tr>
      <td>Redis</td>
      <td>cache.t3.small</td>
    </tr>
    <tr>
      <td>Kafka</td>
      <td>kafka.m5.large × 2 브로커</td>
    </tr>
    <tr>
      <td>MongoDB</td>
      <td>t3.small EC2</td>
    </tr>
    <tr>
      <td>지표</td>
      <td>수치</td>
    </tr>
  </tbody>
  <tbody>
    <tr>
      <td><strong>동시 접속 유저</strong></td>
      <td>1,000 ~ 3,000명</td>
    </tr>
    <tr>
      <td><strong>주문 처리량</strong></td>
      <td>~800 건/초 (단일 거래쌍)</td>
    </tr>
    <tr>
      <td><strong>WebSocket 구독자</strong></td>
      <td>~5,000명</td>
    </tr>
    <tr>
      <td><strong>오더북 깊이</strong></td>
      <td>100 호가</td>
    </tr>
    <tr>
      <td><strong>API 응답 시간</strong></td>
      <td>20~100ms (p95)</td>
    </tr>
    <tr>
      <td><strong>일일 활성 유저</strong></td>
      <td>~10,000명</td>
    </tr>
  </tbody>
</table>

<h4 id="tier-3-중규모-프로덕션-3500월">Tier 3: 중규모 프로덕션 (~$3,500/월)</h4>

<table>
  <thead>
    <tr>
      <th>구성 요소</th>
      <th>사양</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ECS Fargate</td>
      <td>엔진 2 vCPU / 4GB, API 서비스 1 vCPU / 2GB (Multi-AZ)</td>
    </tr>
    <tr>
      <td>MySQL</td>
      <td>db.r6g.large (2 vCPU, 16GB) + Read Replica 1대</td>
    </tr>
    <tr>
      <td>Redis</td>
      <td>cache.r6g.large (클러스터 모드)</td>
    </tr>
    <tr>
      <td>Kafka</td>
      <td>kafka.m5.large × 3 브로커</td>
    </tr>
    <tr>
      <td>MongoDB</td>
      <td>r6g.large EC2</td>
    </tr>
    <tr>
      <td>지표</td>
      <td>수치</td>
    </tr>
  </tbody>
  <tbody>
    <tr>
      <td><strong>동시 접속 유저</strong></td>
      <td>5,000 ~ 15,000명</td>
    </tr>
    <tr>
      <td><strong>주문 처리량</strong></td>
      <td>~3,000 건/초 (단일 거래쌍)</td>
    </tr>
    <tr>
      <td><strong>WebSocket 구독자</strong></td>
      <td>~20,000명</td>
    </tr>
    <tr>
      <td><strong>오더북 깊이</strong></td>
      <td>100 호가</td>
    </tr>
    <tr>
      <td><strong>API 응답 시간</strong></td>
      <td>10~50ms (p95)</td>
    </tr>
    <tr>
      <td><strong>일일 활성 유저</strong></td>
      <td>~50,000명</td>
    </tr>
  </tbody>
</table>

<h4 id="tier-4-대규모-프로덕션-8000월">Tier 4: 대규모 프로덕션 (~$8,000+/월)</h4>

<table>
  <thead>
    <tr>
      <th>구성 요소</th>
      <th>사양</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ECS Fargate</td>
      <td>엔진 4 vCPU / 8GB, API 2 vCPU / 4GB (3-AZ)</td>
    </tr>
    <tr>
      <td>MySQL</td>
      <td>db.r6g.xlarge (4 vCPU, 32GB) + Read Replica 2대</td>
    </tr>
    <tr>
      <td>Redis</td>
      <td>cache.r6g.xlarge (클러스터 모드, 3 샤드)</td>
    </tr>
    <tr>
      <td>Kafka</td>
      <td>kafka.m5.2xlarge × 3 브로커</td>
    </tr>
    <tr>
      <td>MongoDB</td>
      <td>r6g.xlarge EC2 (레플리카셋)</td>
    </tr>
    <tr>
      <td>지표</td>
      <td>수치</td>
    </tr>
  </tbody>
  <tbody>
    <tr>
      <td><strong>동시 접속 유저</strong></td>
      <td>20,000 ~ 50,000명</td>
    </tr>
    <tr>
      <td><strong>주문 처리량</strong></td>
      <td>~8,000 건/초 (단일 거래쌍)</td>
    </tr>
    <tr>
      <td><strong>WebSocket 구독자</strong></td>
      <td>~80,000명</td>
    </tr>
    <tr>
      <td><strong>오더북 깊이</strong></td>
      <td>100 호가</td>
    </tr>
    <tr>
      <td><strong>API 응답 시간</strong></td>
      <td>5~30ms (p95)</td>
    </tr>
    <tr>
      <td><strong>일일 활성 유저</strong></td>
      <td>~200,000명</td>
    </tr>
  </tbody>
</table>

<h3 id="요약-비교표">요약 비교표</h3>

<table>
  <thead>
    <tr>
      <th>항목</th>
      <th>Tier 1 (스테이징)</th>
      <th>Tier 2 (소규모)</th>
      <th>Tier 3 (중규모)</th>
      <th>Tier 4 (대규모)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>월 비용</strong></td>
      <td>~$410</td>
      <td>~$1,200</td>
      <td>~$3,500</td>
      <td>~$8,000+</td>
    </tr>
    <tr>
      <td><strong>동시접속</strong></td>
      <td>300~500</td>
      <td>1K~3K</td>
      <td>5K~15K</td>
      <td>20K~50K</td>
    </tr>
    <tr>
      <td><strong>주문/초</strong></td>
      <td>~200</td>
      <td>~800</td>
      <td>~3,000</td>
      <td>~8,000</td>
    </tr>
    <tr>
      <td><strong>DAU</strong></td>
      <td>~2K</td>
      <td>~10K</td>
      <td>~50K</td>
      <td>~200K</td>
    </tr>
    <tr>
      <td><strong>적합 용도</strong></td>
      <td>개발/테스트</td>
      <td>소규모 런칭</td>
      <td>중견 거래소</td>
      <td>대형 거래소</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p><strong>참고</strong> : 위 수치는 코드 구조, 자료구조 특성, 인프라 사양을 기반으로 한 이론적 추정치입니다. 실제 성능은 거래쌍 수, 주문 패턴, 네트워크 환경에 따라 달라질 수 있으며, 프로덕션 배포 전 반드시 부하 테스트를 수행해야 합니다. 프로젝트에는 1,000명 동시 사용자를 시뮬레이션하는 부하 테스트 프레임워크(<code class="language-plaintext highlighter-rouge">loadtest/</code>)가 포함되어 있습니다.</p>
</blockquote>

<p>답이 없습니다 ㅋㅋ</p>

<p>다들 즐거운 AI 생활 되세용</p>]]></content><author><name>김완기 (Michael Kim)</name></author><category term="AI" /><summary type="html"><![CDATA[전편에 이어서..]]></summary></entry></feed>