API 없는 ERP에 새 업무 화면을 붙일 때는 조회와 입력을 같은 경로로 해결할 필요가 없습니다. 이 사례의 대상은 Delphi 기반 외부 ERP였고, 데이터베이스는 읽기만 허용됐습니다. 원본 프로그램이나 DB를 임의로 바꾸지 않으면서 조회 화면과 입력 자동화를 연결하는 것이 과제였습니다.
당시 선택한 방법은 한 가지 통합 기술로 모든 일을 해결하는 것이 아니라 읽기와 쓰기의 경로를 분리하는 것이었습니다. 조회는 읽기 전용 MS SQL 연결로 처리하고, 입력은 기존 ERP 화면에서 사람이 하던 순서를 RPA로 재현했습니다.
이 글은 2026년 4월의 구현 기록을 바탕으로 구조와 점검 기준을 다시 정리한 사례입니다. 특정 ERP 제품의 공식 연동 방법이나 현재 운영 상태 전체를 설명하는 문서는 아닙니다.

먼저 쓰기 권한과 업무 경계를 확인했습니다
레거시 시스템이라고 해서 화면 자동화를 바로 붙이면 안 됩니다. 먼저 공식 API, 공급사 지원 범위, 데이터베이스 권한, 변경 가능한 업무 절차를 확인해야 합니다. 이 사례에서는 데이터베이스 조회는 가능했지만 직접 쓰기는 허용되지 않았습니다.
| 질문 | 당시 확인한 범위 | 설계에 준 영향 |
|---|---|---|
| API가 있는가 | 사용할 수 있는 API가 없었음 | 별도 게이트웨이와 화면 자동화 검토 |
| DB에 쓸 수 있는가 | 조회만 가능 | 직접 UPDATE를 입력 경로에서 제외 |
| 원본 ERP를 바꿀 수 있는가 | 외부 프로그램이라 수정 범위가 제한됨 | ERP 바깥에서 기능을 감싸는 구조 선택 |
| 입력 결과를 확인할 수 있는가 | ERP 화면과 조회 결과로 확인 필요 | 저장 후 검증 단계를 별도로 설계 |
읽기 전용이라는 조건은 불편했지만 안전 경계를 분명하게 만들었습니다. 데이터베이스에 직접 쓰는 우회 방법을 찾기보다, 원래 프로그램이 제공하는 입력 흐름을 통과하도록 설계했습니다.
읽기는 DB, 쓰기는 RPA로 나눴습니다
조회
Flutter 앱 → 로컬 FastAPI 게이트웨이 → 읽기 전용 MS SQL
입력
Flutter 앱 → 로컬 작업 요청 → pywinauto → ERP 화면
확인
ERP 저장 결과 → 화면 또는 허용된 조회 경로로 재확인
조회는 ERP 화면을 한 칸씩 탐색하는 것보다 데이터베이스에서 필요한 값을 읽는 편이 단순했습니다. Flutter 앱은 로컬 주소의 FastAPI 게이트웨이에 요청하고, 게이트웨이는 허용된 쿼리만 실행해 자동완성과 조회 화면에 필요한 값을 돌려줬습니다.
입력은 pywinauto가 ERP 창을 찾아 사람이 하던 순서를 수행했습니다. 직접 DB에 쓰지 않고 기존 프로그램의 검증과 저장 절차를 통과하려는 선택이었습니다. 다만 DB 조회 권한이 있다는 사실만으로 화면 자동화까지 허용되는 것은 아닙니다. 도입 전에는 ERP 사용 계정의 업무 권한, 공급사의 지원·계약 범위, 실제 입력에 대한 담당자 승인을 별도로 확인해야 합니다. 이 글은 다른 조직의 화면 자동화 허가를 대신하지 않습니다.
두 경로가 같은 규칙을 보게 했습니다
읽기와 쓰기를 분리하면 검증 기준이 갈라질 수 있습니다. 앱에서는 허용한 값을 RPA가 거부하거나, RPA는 입력했지만 조회 화면에서 다른 형식으로 보일 수 있습니다. 그래서 필수값, 형식, 업무상 허용 범위를 공통 검증 모듈에서 관리했습니다.
- 앱에서 작업을 보내기 전에 필수값과 형식을 검사합니다.
- RPA는 대상 ERP 창과 업무 화면이 맞는지 다시 확인합니다.
- 입력 직전에는 사용자가 확인할 값을 표시합니다.
- 저장 후에는 성공 메시지만 믿지 않고 실제 결과를 다시 조회합니다.
- 확인할 수 없으면 성공으로 기록하지 않고 검토 대상으로 남깁니다.
같은 검증 코드를 쓴다는 사실만으로 업무 규칙이 항상 맞는 것은 아닙니다. 공급사 업데이트나 현장 규칙 변경이 있으면 기준 자체를 다시 확인해야 합니다.
화면 자동화는 성공 경로보다 실패 경로가 중요합니다
RPA는 ERP 창의 위치, 대화상자, 로딩 시간, 포커스 변화에 영향을 받습니다. 정상 입력 한 번보다 중간에 멈췄을 때 무엇을 남기고 어떻게 복구할지가 운영 품질을 좌우합니다.
| 실패 상황 | 필요한 처리 |
|---|---|
| ERP 창을 찾지 못함 | 재시도 횟수를 제한하고 사용자에게 창 상태 확인 요청 |
| 예상하지 못한 팝업 | 임의 클릭하지 않고 화면과 단계만 기록 |
| 저장 응답을 확인하지 못함 | 중복 입력을 피하기 위해 결과 조회 후 재시도 판단 |
| 앱 또는 게이트웨이 종료 | 진행 중 작업을 성공 처리하지 않고 미확인 상태로 남김 |
| ERP 화면 변경 | 운영 입력을 중단하고 선택자와 절차를 테스트 환경에서 재검증 |
pywinauto의 구체적인 선택자와 대기 방식은 프로그램 버전에 따라 달라집니다. 그대로 복사할 설정 대신 pywinauto 공식 문서와 대상 ERP의 실제 컨트롤 구조를 함께 확인해야 합니다.
저장 버튼을 눌렀는데 결과가 불분명하다면
이 구간에서는 “실패했으니 재입력”보다 “이미 저장됐는지 확인”을 먼저 해야 합니다. 다음은 이 사례의 저장 후 검증 원칙을 실제 복구 절차로 옮길 때 사용할 판단 순서입니다.
- 해당 작업을 미확인 상태로 남기고 추가 입력을 멈춥니다. 요청 시각, 대상 업무, 마지막으로 확인된 단계를 기록합니다.
- 허용된 조회 경로나 ERP 화면에서 대상 거래를 찾습니다. 전표번호처럼 확정 식별자가 있다면 우선 대조하고, 없다면 거래처·날짜·금액 등 비교 항목을 업무 담당자와 정합니다.
- 일치하는 결과가 하나 확인되면 요청 내용과 실제 저장값을 대조합니다. 후보가 여러 개이거나 일부 값만 반영됐다면 사람이 확인하도록 넘깁니다.
- 조회에 안 보인다는 사실만으로 미저장을 단정하지 않습니다. 확인 범위가 충분한지 판단한 뒤, 재입력이 필요한 경우에만 담당자 승인 아래 재개합니다.
어떤 필드 조합이 거래를 유일하게 식별하는지는 ERP마다 다릅니다. 이 글은 대상 시스템의 키 구조나 조회 반영 시점을 검증하지 않았으므로 임의의 중복 판정 쿼리를 제공하지 않습니다. 복구 상태를 별도로 설계하는 방법은 관찰·검증·복구 루프 설계와 연결되고, 긴 작업의 인계는 비동기 작업 브리지 사례에서 참고할 수 있습니다.
로컬 게이트웨이도 운영 대상입니다
당시에는 앱을 시작할 때 FastAPI 게이트웨이를 함께 실행하고, 앱을 종료하면 생성한 프로세스도 정리하도록 구성했습니다. 목적은 중복 프로세스와 포트 충돌을 줄이는 것이었습니다.
- 기존 프로세스가 사용 중인 포트를 임의로 종료하지 않습니다.
- 이번 앱이 만든 프로세스와 사용자가 따로 실행한 프로세스를 구분합니다.
- 허용된 로컬 호출만 받고 외부 네트워크에 불필요하게 노출하지 않습니다.
- 로그에는 고객 정보와 인증값을 남기지 않습니다.
- 정상 종료, 실패, 취소 각각의 정리 조건을 둡니다.
로컬 주소에만 연결된다는 사실은 인증이나 업무 권한 검사를 대신하지 않습니다. 구현을 검토할 때는 클라이언트에서 임의 SQL을 받지 않는 허용 쿼리 방식, 매개변수 바인딩, 요청 주체 확인, 조회·입력 권한 분리를 확인합니다. 이는 보완 설계 기준이며 당시 구현에 모두 적용됐다고 주장하지 않습니다.
FastAPI 자체의 기능과 보안 설정은 버전에 따라 달라질 수 있으므로 FastAPI 공식 문서를 기준으로 확인해야 합니다.
개발 기록: 화면 관찰에서 견적서·ERP 구조로
2026-04-03 기록: 등록 폼을 먼저 관찰했다
ERP 캡처에서 확인한 신규 등록은 별도 팝업이 아니라 하단 폼의 신규 N 버튼을 눌러 같은 화면 안에서 시작하는 방식이었다. 창 목록 PowerShell 스크립트는 MainWindowTitle이 빈 문자열이 아닌 프로세스만 고르도록 필터를 수정해 결과를 얻었다. 창 목록 조회 성공은 ERP 입력 성공이 아니다. 파서 출력과 ERP 필드 매핑표 확정, RPA 입력 스크립트 설계·구현은 당시 남은 일이었다.
소카가 스크린샷을 공유 폴더에 저장하고 조정자가 읽는 전달 방식도 이때의 구상이다. 실행 환경은 원문에 “같은 노트북에서 동시 실행 불가”와 “업무용·개인용 노트북 역할 분리”가 함께 있어 물리 장비 구성을 확정할 수 없다. 어느 사용자 세션에서 ERP GUI가 보이는지 다시 확인해야 하며, 문장만으로 환경 모순을 해소하지 않았다.
2026-04-05·2026-04-06 기록: 계산기 코드와 견적 업무 요구를 분리했다
첫 산출물은 SwiftUI iPad 계산기의 CalculatorEngine, ViewModel, 히스토리·UI 패널, AdMob placeholder와 CalculatorApp.xcodeproj였다. Mac mini 개발 환경은 전체 Xcode가 아닌 CommandLineTools 기준이라 xcodebuild 빌드·런타임 검증을 하지 못했다. 코드 트리 생성은 앱 배포나 광고 수익화 성공이 아니다.
4월 6일에는 실제 문제를 엑셀 수식·거래처 확인·PDF 변환이 반복되는 견적서 업무로 좁혔다. 우선 범위는 Windows 앱의 수동 입력·복붙, 자동 계산, PDF 저장이었다. HP Config 엑셀에서 사양을 채우고 삼성·LG로 확장해 ERP 수주접수까지 연결하는 것은 후속 구상이다. 이미 만든 iPad 계산기 코드와 앞으로 검증할 Windows 견적 앱을 같은 완료 상태로 묶지 않았다.
2026-04-07 기록: Windows MVP와 남은 연결 단계
당시 기록은 Flutter Windows MVP 기본 기능 구현 후 PDF·Excel 출력 레이아웃을 다듬는 단계라고 남겼다. 벤더 Config 자동 반영과 ERP 수주접수 연동까지 끝냈다는 뜻은 아니다. 별도 ERP 영업기회 자동화는 Phase 1 파서와 Phase 2 RPA 완료로 보고됐지만, 메일 수신 → 파싱 → ERP 자동 등록 → Telegram 알림을 잇는 Phase 3는 준비 중이었다. 공개 원문에는 이를 다시 재현할 전체 로그나 소스가 없으므로 이 단계 보고를 현재 운영 보증으로 바꾸지 않는다.
모델이 달라도 이어받을 수 있도록 배경·결정 이유·완료 범위를 남기는 핸드오프 원칙과 reports/ 구조 설계도 함께 정리했다. 보고 경로의 설계 완료는 실제 결과 회수 시험과 별개다. 이 연표는 4월 23일의 DB 조회·RPA 입력 분리 구조로 이어진 요구와 구현 범위를 설명하며, 새 빌드·ERP 입력 테스트 결과를 추가한 것은 아니다.
이 사례에서 확인한 범위
읽기 경로와 쓰기 경로를 분리하자 구현 책임과 실패 지점을 설명하기 쉬워졌습니다. 다만 이 글에는 처리량 벤치마크, 장기간 무중단 운영 결과, 모든 ERP 화면에 대한 회귀 테스트가 포함되어 있지 않습니다. 화면 자동화가 공식 API보다 우수하다는 뜻도 아닙니다. 공식 연동 수단이 없고 원본 시스템 변경이 제한된 조건에서 사용한 대안입니다.
사례 기준일: 2026-04-23
재편집일: 2026-09-03
확인한 범위: Delphi 기반 외부 ERP, 읽기 전용 MS SQL, Flutter·FastAPI 조회 경로와 pywinauto 입력 경로의 설계 기록
미확인 범위: 현재 운영 상태, 장기 안정성, 다른 ERP 제품에서의 재현성
편집 보완: 2026-09-10. 기존 사례와 작성·재편집 날짜를 유지하며 판단 절차와 관련 글을 보강했습니다. 이 날짜는 새 설치·성능 시험 또는 현재 운영 상태 확인을 뜻하지 않습니다.
통합한 원문과 편집 범위
아래 게시 기록의 관련 사례를 이 글로 통합했습니다. 각 원문의 당시 보고를 정리한 것으로, 현재 상태를 새로 검증했다는 뜻은 아닙니다.
- 견적서 앱 개발: Windows MVP에서 ERP 연동 구상까지 (2026-04-07 게시)
- 견적서 앱 기획: 아이패드 계산기보다 먼저 해결할 영업 업무 (2026-04-06 게시)
- 아이패드 계산기 앱 개발: SwiftUI MVP 코드와 빌드 검증의 경계 (2026-04-05 게시)
- ERP 영업기회 자동화 설계: 화면 구조와 필드 매핑을 살핀 날 (2026-04-03 게시)