일주일에 IDE 한 개 만들기 — RP2040 산업용 컨트롤러용 통합 개발 환경 Going.IDE

자사 RP2040 보드 시리즈(GO-RP2040-CB08 + I²C/SPI 확장보드)를 위한 전용 IDE를 일주일 만에 만들었다. 위자드로 프로젝트 생성 → Scriban 템플릿이 user.ino를 뽑아내고 → arduino-cli 가 빌드/업로드 → 시리얼 모니터로 검증 → 가이드 다이얼로그가 Claude에 보낼 프롬프트를 클립보드로 복사 → HMI 연동 문서 자동 생성 → 매뉴얼 PDF 자동 캡처/빌드. 마지막엔 Inno Setup 인스톨러로 묶어 단일 .exe 배포. 5/15 빈 솔루션 → 5/22 v1.2.0.5 + PICO-IO8R 두 번째 메인 보드까지 7일.
가장 큰 교훈: AI 가 손에 익으니 IDE 한 개의 ROI가 달라진다. 골격을 그날 안에 박고, 다음 6일은 실 보드에서 굴리며 부딪힌 함정들을 고치는 사이클로 굴러갔다. 이 글은 그 함정들 하이라이트 7개와, 기능을 묶는 방식이다.
1. 무엇을 만들었나
Going.IDE 메인 화면 — 한 창에 프로젝트 탐색기 / 코드 편집기 / 시리얼 모니터 / 출력·문제 패널 모두 통합. 우상단 [가이드] 버튼이 5/20 신설.
Going.IDE 는 RP2040 기반 산업용 PLC/HMI 시스템의 Arduino 코드를 사용자가 한 창에서 처리할 수 있게 만든 .NET 8 WinForms IDE다. Arduino IDE 가 “코드 편집기 + arduino-cli wrapper” 라면, Going.IDE 는 자사 보드 카탈로그 + 메모리 모델 + Modbus 통신 자동 생성 + HMI 연동 가이드 까지 묶어 “RP2040 산업 보드를 처음 잡은 사람이 한 시간 안에 동작시킬 수 있게” 만든 것에 가깝다.
시스템 구성:
[사용자 클릭] → [Wizard 7 step] → [GpjProject JSON] → [Scriban 템플릿]
↓
[Serial Monitor] ← [arduino-cli upload] ← [arduino-cli compile] ← [user.ino + Designer.cs]
↓
[HMI 연동 가이드 → Claude → SenvasHMI reference 문서 생성]
↓
[매뉴얼 PDF 자동 빌드 + Inno Setup 인스톨러]
2. 7일 흐름
날짜별 한 줄 요약. 커밋 메시지에서 추렸다.
| 날짜 | 핵심 |
|---|---|
| 5/15 (1일차) | 솔루션 골격 → Core(BoardCatalog/MemoryLayoutResolver/ScribanCodeGen/GpjProject) → Editor(ScintillaNET) → Build(arduino-cli wrapper) → Serial Monitor → Wizard 7step → Shell(DockPanel) — 하루에 전부 |
| 5/16 (2일차) | 빌드 통합 다듬기, EVERY_MS 매크로, I²C 스캐너 다이얼로그, 다크 테마, MCP 통합 기반, 매뉴얼 4 Tier 골격 |
| 5/17 | (휴식 / 현장 보드 디버깅) |
| 5/18 (3일차) | 실 보드 첫 통합. UF2 bootstrap, RP2040 USB CDC 시리얼 디버그, I²C 스캔, 인스톨러 1차, MCP 도구로 보드 정보 노출, 매뉴얼 실제 캡처 7장 |
| 5/19 (4일차) | 펌웨어 안정화 — _usage.md 영문 사용법 자동 생성, PCF8574 polarity 회귀 수정, INPUT_PULLDOWN, prefer-markdown skill |
| 5/20 (5일차) | 가이드 다이얼로그 신설 — 도구 → 가이드 → 클립보드 복사 → Claude 자동 launch 패턴 + 4 자리 버전 시스템 |
| 5/21 (6일차) | Modbus TCP 안정화 대장정 — 현장 검증된 per-client buffer, SimpleModbusTcp 표준, MCP encoding 한국어 mojibake 픽스, 매뉴얼 PDF 표지 로고 + 24장 0 placeholder, v1.2.0.5 인스톨러 배포 |
| 5/22 (7일차) | PICO-IO8R 두 번째 메인 보드 등록 — JSON + 핀맵 + 예제 4종, BoardCatalog 회귀 가드 추가 |
3. 기능 묶음
기능별로 모아본 단면. 각 영역의 “왜 이 선택” 만.
보드 카탈로그 — JSON-driven, 코드 변경 없이 보드 추가
위자드 Step 2 — 메인 보드 카드는 JSON 파일에서 자동 로드. 캡처 시점(5/18)엔 CB08 한 종, 5/22 에 PICO-IO8R이 같은 카드 형태로 한 장 더 추가됐다.
data/boards/*.json 한 파일이 곧 보드 한 개. BoardLoader 가 디렉터리 스캔으로 다 읽고 BoardCatalog 에 담는다. 새 보드 추가 = JSON 한 개 + 이미지 한 장 + 테스트 가드 한 줄. 5/22 의 PICO-IO8R 추가가 정확히 이 패턴으로 30분 만에 끝났다.
{
"id": "PICO-IO8R",
"type": "main",
"mcu": "RP2040",
"fqbn": "rp2040:rp2040:rpipico",
"pins": { "digital_input": [...], "digital_output": [...], "uart": [...] },
"memory_model": { "P": {...}, "M": {...}, "D": {...} },
"communications": [{ "id": "modbus_rtu_1", "type": "modbus_rtu", "uart": "Serial1" }]
}
코드 생성 — Scriban + 골든 마스터 테스트
위자드가 만든 GpjProject JSON 을 Framework.ino.scriban + Modbus.ino.scriban 에 통과시켜 컴파일 가능한 .ino 를 출력한다. 5/15 첫날 5조합 골든 마스터 테스트 25개를 박았기 때문에 이후 일주일 동안 템플릿을 수십 번 손봐도 무엇이 깨졌는지 즉시 알 수 있었다.
펌웨어가 사용자 함수에 노출하는 추상은 단순하다:
__P[i],__M[i],__D[i]— 비트/워드 메모리 영역 (Modbus 와 1:1)EVERY_MS(n) { ... },EVERY_SEC(n) { ... }— 주기 실행 매크로readBit/writeBit/readWord/writeWord— 메모리 접근 헬퍼userSetup()/userLogic()— 사용자가 채우는 두 함수
userLogic() 은 매 loop 호출이라 rate limit 이 없다(사용자가 EVERY_MS 로 명시). 이 결정 하나로 펌웨어가 단순해졌다.
위자드 7단계
Step 1 (프로젝트 정보) → Step 2 (메인보드) → Step 3 (확장보드) → Step 4 (통신) → Step 5 (메모리) → Step 6 (핀) → Step 7 (검토 + 생성). 각 step 은 IWizardStep 인터페이스 + Apply(GpjProject) 메서드로 동일 패턴. 한국어 글리프 descender 가 버튼에서 잘리는 사소한 픽스부터(bb7e988), 확장보드 추가 시 P 영역 자동 확장까지 사용자 친화를 누적했다.
MCP 통합 — Claude 가 직접 IDE 조작
도구 → AI 어시스턴트 설치. 체크 두 개 + [설치] 한 번으로 Claude Desktop 의 claude_desktop_config.json 과 Claude Code 의 .claude.json 양쪽에 going-ide MCP 서버가 등록된다. 사용자는 Claude 클라이언트만 재시작하면 IDE 도구 11종을 자연어로 사용할 수 있다.
Going.IDE.McpServer 가 stdio JSON-RPC 로 Claude Desktop / Claude Code 에 연결되고, HTTP 로 IDE 의 RemoteApi 와 양방향 통신한다. ide_get_project, ide_list_boards, ide_read_file, ide_write_file, ide_build, ide_apply_example 같은 도구로 AI가 프로젝트 상태를 읽고 코드를 고치고 빌드까지 띄울 수 있다. 이 통합이 일주일 안에 IDE 한 개를 만들 수 있게 한 배경이다.
가이드 다이얼로그 — “초보가 무엇부터 누를지” 직접 안내
5/20 신설. data/guides/guides.json 의 카테고리/항목을 카드 형태로 FlowLayoutPanel 에 펼치고, 항목 클릭 → 미리 정의된 프롬프트를 클립보드 복사 → Claude 자동 launch + 사용자에게 Ctrl+V 안내. 초기엔 자동 입력까지 시도했지만(d933d63) 불안정해서 클립보드 + 안내 단일 패턴으로 수렴.
매뉴얼 PDF — 자동 캡처 + Markdown→HTML→PDF
docs/매뉴얼_Going.IDE.md 를 PowerShell + Pandoc + wkhtmltopdf 로 빌드. 스크린샷 24장은 SendKeys 자동 캡처 스크립트로(docs/images/). 표지 로고는 자사 CI 공식 이미지(blue02). 빌드 한 번에 PDF 50 페이지 완성, 인스톨러에 함께 패키징.
인스톨러 — Inno Setup + Self-contained 게시
scripts/build-installer.ps1 가 Shell + McpServer 두 .NET 프로젝트를 self-contained 로 게시 → arduino-cli.exe + 보드 카탈로그 + 예제 + 매뉴얼 PDF + Modbus TCP 표준 문서까지 staging 에 모아 → ISCC.exe 가 단일 Going.IDE.Setup-1.2.0.5.exe 출력. 사용자 PC에 .NET 8 Runtime 별도 설치 불필요.
4. 트러블슈팅 하이라이트 7개
겪은 함정 중 인상 깊었던 것만.
(1) W5500 SPI noise — 14MHz → 4MHz → per-client buffer
Modbus TCP 응답이 가끔 깨졌다. addr=0x70 인데 0x78 로 들어오거나, qty=100 인데 116 으로. 처음엔 SPI clock 을 Wiznet default 14MHz 에서 4MHz 로 낮춰 노이즈를 줄였다. 효과는 있었지만 근본 원인이 아니었다.
진짜 원인: 펌웨어의 글로벌 단일 buffer + 멀티 클라이언트 동시 접속. HMI 가 한 채널로만 폴링할 때는 안 보였는데 두 채널이 겹치면 buffer overwrite. 현장 산업용 보드(Going_Industrial 펌웨어)의 per-client struct + server.accept() 패턴으로 refactor 한 뒤로는 SPI 를 14MHz 로 돌려도 깨끗했다.
struct TcpSlot {
EthernetClient client;
uint8_t buf[256];
uint16_t len;
};
TcpSlot _slots[4];
void modbusLoop() {
EthernetClient newClient = _mbTcpServer.accept();
if (newClient) {
newClient.setConnectionTimeout(50); // ← stop() 1초 hang 방지의 핵심
// ... 빈 슬롯에 할당
}
for (auto& slot : _slots) { /* 각 슬롯 독립 polling */ }
}
(2) Going.Basis MasterTCP socket leak
HMI 쪽(Going.Basis 라이브러리의 ModbusTCPMaster). cleanup() 이 if (bIsOpen) 가드 안에 있어서 — 연결 실패 시 cleanup 자체를 못 부르고 소켓이 줄줄 샜다. 3시간 만에 4개 소켓 leak, 그다음 OperationCanceledException 폭포.
수정: cleanup unconditional, Shutdown(Both) → Close(), 500ms backoff, KeepAlive 5s/1s via SIO_KEEPALIVE_VALS, 연속 timeout 3회 시 강제 disconnect + SchedulerStopException throw + drain. Going.Basis v1.2.29-local 로 출고.
(3) PCF8574 polarity — 두 번 잘못 짚었다
GO-PCF8574-IN08 의 옵토커플러(KPC18T1)는 emitter-follower: 24V 인가 → emitter HIGH → PCF8574 raw bit = 1 = active-HIGH. 즉 invert 불필요, logic: "positive" 가 정답.
두 차례 잘못한 적이 있다:
- 회로 안 보고 “옵토면 invert 필요하겠지” 라고
negative로 둠 → 24V 미인가인데 모든 채널 1 로 보임 → 사용자 신고 - 다시 보고
negative그대로 두면서 회귀 테스트까지 그렇게 박음 → 또 신고
두 번째 신고 후 회로도(85-GI-I00_a GO-PCF8574-IN08.asc 의 _IN1 신호: U2.4 emitter ↔ U1.4 PCF) 보고 확정. BoardCatalogJsonsTests.PCF8574_IN08_Logic_MustMatchActiveHighSchematic 회귀 가드로 박았다. 회로 미확인 + 학습된 직관이 두 번 같은 자리에서 넘어진 사례.
(4) RP2040 USB CDC 출력이 안 보임 — DTR/RTS
시리얼 모니터를 띄워도 보드의 Serial.println(...) 출력이 안 보였다. arduino-cli 콘솔에서는 보이는데. 시간을 한참 썼다. 결국 SerialPort.DtrEnable = true; RtsEnable = true; 한 줄. RP2040 USB CDC 가 호스트의 DTR 라인을 보고 “터미널 붙었나” 판단한다. 우리 .NET WinForms 에서 디폴트가 false 였다.
(5) 시리얼 모니터 자동 재연결 — _suspendAutoReconnect
펌웨어 업로드 직후 USB 가 재열거되면서 포트가 잠시 사라진다. 모니터가 reconnect 시도하다 실패하면 사용자가 수동으로 다시 연결해야 했다. 해결: 업로드 시작 시 _suspendAutoReconnect = true, 끝나면 ResumeAfterUpload() 가 wait 후 자동 reconnect 시도.
함정: early return 가지에서 _suspendAutoReconnect = false 를 빠뜨려 두 번째 업로드부터는 안 됨. 수정 한 줄 후 안정.
(6) 빌드 락 — Going.IDE.Shell 떠 있으면 dll 잠김
dotnet test 가 bin\Debug\...\Going.IDE.Core.dll 을 복사하려는데 Shell.exe가 잡고 있어서 MSB3027 error. 해결은 직관적(“Shell 종료”) 인데 진짜 교훈은 Tests 프로젝트의 csproj 가 Shell 의 .dll 까지 끌어다 자기 bin 에 복사하려는 구조 였다는 것. 별도 출력 디렉터리로 분리하거나 ProjectReference를 줄여야 더 깨끗해진다(아직 미수정).
(7) 한국어 mojibake — McpServer Console encoding
가이드 다이얼로그로 클립보드에 복사 → Claude Desktop 에서 한글 프롬프트 입력 → MCP 가 받은 한국어가 깨져서 연결 → ?곌껐 같은 mojibake.
원인: Korean Windows 의 .NET Console 기본 인코딩 = CP949. Claude 가 보내는 stdin JSON-RPC 는 UTF-8 byte sequence 인데 CP949 decoder 가 lead byte 를 invalid 로 처리 → ? 치환. JSON 안에서 한국어가 다 깨져나옴.
수정은 Program.cs 두 줄.
Console.InputEncoding = System.Text.Encoding.UTF8;
Console.OutputEncoding = System.Text.Encoding.UTF8;
같은 함정에 stdio MCP 다 노출된다. Korean/Japanese/Chinese Windows 에서 한 줄로 막혀 있던 진입장벽.
5. AI-coding 패턴 — 무엇이 일주일 안 가능하게 했나
Going.IDE 가 일주일 안에 만들어진 결정적 이유는 AI 가 손에 익은 패턴들 이다. 그중 셋만:
골든 마스터 테스트로 템플릿 자유롭게 손보기
5/15 첫날에 Scriban 템플릿의 5 보드 조합(CB08 only / CB08+IN08 / CB08+RY08 / CB08+IN08+RY08 / CB08+W5500) 골든 마스터 25 테스트를 박았다. AI 가 템플릿을 수십 번 손봐도 “어디가 달라졌는지” 가 diff 한 번으로 보인다. 일주일 동안 템플릿이 폭발적으로 변경됐지만 회귀 0건.
매뉴얼 자동 캡처
scripts/capture-*.ps1 PowerShell 스크립트들이 IDE 를 띄우고 SendKeys 로 메뉴를 열어 PNG 캡처. 24장 placeholder 를 전부 자동 채웠다. 매뉴얼 PDF 가 한 번에 50 페이지 완성되면, 다음 기능 만들면서도 “매뉴얼 미반영” 부담이 줄어든다.
MCP + skill 시스템 — AI 가 자기 영역 자동 인식
data/mcp-skill/modbus-tcp-patterns.md 같은 문서에 frontmatter 로 키워드(W5500, MasterTCP, EthernetServer, FC03/FC06, …) 를 박아두면 Claude 가 Modbus TCP 작업 시 자동 로드. 사용자가 “Modbus 안정성 어떻게 하지?” 라고 묻기 전에 AI 가 이미 표준 패턴을 알고 있는 상태. 메뉴 클릭 한 번 없이 AI 가 작업 영역의 표준을 가져온다.
6. 마무리 — 1주일 결과물
| 항목 | 결과 |
|---|---|
| 솔루션 프로젝트 | 12개 (Core / Modules.* × 8 / Shell / Tests / McpServer) |
| 단위 테스트 | 447개 통과 |
| 보드 카탈로그 | 5개 (CB08 / IN08 / RY08 / W5500-LINK / PICO-IO8R) |
| 예제 카탈로그 | 7개 (보드별 분리) |
| 매뉴얼 | 50페이지 PDF, 스크린샷 24장 |
| 인스톨러 | Going.IDE.Setup-1.2.0.5.exe (self-contained, .NET 8 별도 설치 X) |
| MCP 도구 | 11종 (project / boards / files / build / examples) |
다음 단계는 추가 보드 라인업 (현재 PICO-IO8R 한 종 추가 — RS-232 + FRAM 차별) 과 HMI 도구 통합 (SenvasHMI 와의 .gudx 양방향). 그리고 가장 중요한 건 — 현장 사용자 한 명이라도 진짜로 잡고 PLC 만들어보는 것.
IDE 는 만들어두면 어차피 자기가 자기를 키운다. 가이드 다이얼로그 항목 한 줄 추가하는 비용이 매뉴얼 챕터 한 절 추가하는 비용만큼 가벼워졌다. 이 단계까지 1주일 — AI 코딩이 IDE 라는 카테고리의 비용 곡선을 다시 그려놨다는 게 가장 큰 발견이다.
댓글
로그인 없이 이름만 적고 남기실 수 있어요. 남긴 댓글은 바로 게시됩니다.
댓글을 불러오는 중…