‘이거 무시해도 되나요?’ — 네덜란드어 편지를 다루기 위해 Handle It NL을 만들다
요약
네덜란드어 편지를 분석하여 단순 번역을 넘어 사용자가 실제로 취해야 할 행동과 의미를 파악하는 도구 Handle It NL이 개발되었습니다. 이 시스템은 문서를 분류하고, 핵심 정보를 추출하며, 최종적으로 주소 검증이나 캘린더 이벤트 생성 같은 실질적인 후속 조치를 수행합니다.
핵심 포인트
- 단순 번역을 넘어 '무엇을 해야 하는지'에 초점을 맞춤.
- 문서 유형(지불/예약/조치 등) 분류 및 핵심 정보 추출 기능 제공.
- AI 모델이 해석하고, 결정론적 도구가 실제 행동을 실행하는 구조를 채택함.
- 행동이 필요 없는 경우('No Action Needed')도 정확히 판단할 수 있는 능력이 중요함.
이 글은 Hacktoberfest Weekend Challenge: Build for a Friend 제출물입니다.
제가 만든 것
저는 네덜란드에 거주하는 외국인들을 위한 네덜란드어 편지 문제 해결사 Handle It NL을 만들었습니다.
이것은 제 아내를 위해 만들었는데, 그녀가 최근 네덜란드로 이사 와서 네덜란드어를 배우고 있지만, 지방 자치 단체나 정부에서 온 편지에는 가끔 어려움을 느낍니다.
흥미로운 점은 번역 자체가 전체 문제가 아니었다는 것입니다.
누군가는 네덜란드어 편지를 영어로 완벽하게 번역할 수 있지만, 여전히 다음과 같은 질문을 남기게 됩니다:
“좋아요... 하지만 제가 실제로 무엇을 해야 하는 건가요?”
심각한가요?
무시해도 되나요?
뭔가를 지불해야 하나요?
마감일이 있나요?
어딘가를 방문해야 하나요?
무엇을 가져가야 할까요?
아무것도 안 하면 어떻게 되나요?
이것이 Handle It NL의 아이디어가 되었습니다.
단순히 또 다른 번역 도구 역할을 하는 대신, 네덜란드어 편지를 설명, 결정, 그리고 안전한 다음 행동으로 바꾸려고 시도합니다.
사용자가 업로드하는 것은 다음과 같습니다:
- 사진;
- 스크린샷;
- JPG/PNG/WEBP 이미지;
- 또는 스캔된 PDF.
Handle It NL은 문서를 다음 중 하나로 분류합니다:
- 지불 필요 (Payment required)
- 예약 / 방문 (Appointment / visit)
- 조치 필요 (Action required)
- 정보만 포함 (Information only)
- 혼합 (Mixed)
- 검토 필요 (Needs review)
UI는 네 가지 질문에 초점을 맞춥니다:
WHAT IS THIS?
CAN I IGNORE IT?
...
또한 다음을 추출할 수 있습니다:
- 발신자 (sender);
- 제목 (subject);
- 지불 금액 (payment amount);
- 지불 참조 번호 (payment reference);
- 마감일 (deadline);
- 예약 날짜/시간 (appointment date/time);
- 방문 장소 (visit location);
- 가져갈 문서 또는 물품 (documents or items to bring);
- 편지를 무시했을 때의 결과 (consequence of ignoring the letter when supported);
- 유용한 네덜란드 행정 용어.
하지만 제가 가장 흥미롭다고 느낀 부분은 모델이 편지를 이해한 후에 일어나는 일입니다.
Handle It NL은 실제 도구로 유용할 수 있다고 결정합니다.
예를 들어, 다음과 같은 작업을 할 수 있습니다:
- PDOK을 통해 네덜란드 주소 검증하기;
.ics캘린더 이벤트 준비하기;- Google Calendar 링크 생성하기;
- 약속이나 마감일 로컬에 저장하기;
- 선택된 네덜란드 행정 절차에 대한 선별된 안내 확인하기.
그리고 때로는 올바른 조치가 단순히 다음과 같을 때가 있습니다:
NO ACTION NEEDED
예를 들어, 정보성 동네 공지사항이라고 해서 AI 에이전트가 도구를 사용할 수 있는 경우에 무조건 마감일이나 캘린더 이벤트를 생성해서는 안 됩니다.
행동을 하지 않기로 결정하는 능력은 프로젝트의 중요한 부분이 되었습니다.
애플리케이션의 규칙
Handle It NL을 구축하면서 하나의 규칙이 핵심이 되었습니다:
모델이 해석한다. 결정론적 도구(Deterministic tools)가 실행한다.
언어 모델은 문서를 이해하고 어떤 기능이 도움이 될지 결정할 수 있도록 허용됩니다.
하지만 행동이 발생한 것처럼 가장하는 것은 할 수 없습니다.
예를 들어:
- PDOK은 주소가 실제로 검증되었는지 여부를 결정합니다;
- Python은 실제
.ics파일을 생성합니다; - SQLite는 마감일이 정말로 저장되었는지 여부를 결정합니다;
- 백엔드는 약속이 이미 존재하는지 여부를 결정합니다;
- UI는 해당 도구들이 결과를 반환한 후에만 도구 결과를 표시합니다.
이는 제가 생성형 AI의 매우 전형적인 문제점을 발견했을 때 특히 중요해졌습니다.
저는 같은 약속 PDF를 두 번 업로드했습니다.
날짜와 시간은 같았지만, 모델이 생성한 약속 설명은 약간씩 달랐습니다.
따라서 이전 버전에서는 두 개의 약속을 저장했습니다.
이는 마감일을 다루는 시스템에게는 허용되지 않습니다.
그래서 저는 아키텍처를 변경했습니다.
이제 약속은 모델이 생성한 제목을 신뢰하는 대신, 주로 다음을 사용하여 중복 제거됩니다:
date/time + physical location
또한 원본 업로드 문서의 SHA-256 해싱도 추가했습니다.
same source document
↓
same SHA-256 hash
...
만약 정확히 동일한 PDF나 이미지를 다시 업로드하면, Handle It NL은 모델에게 또 다른 미묘하게 다른 버전을 생성하도록 요청하는 대신 검증된 해석을 재사용할 수 있습니다.
모델이 문서를 이해했지만 제가 기대했던 구조의 최종 JSON을 반환하지 못하는 또 다른 실패 사례가 나타났습니다.
원래에는 이로 인해 UI가 실수로 다음과 같이 보이게 할 수 있었습니다:
No action needed
이는 위험했습니다.
최종 버전에서는 구조화된 출력 복구(structured-output repair)를 먼저 시도합니다.
만약 여전히 신뢰할 수 있는 결과를 생성할 수 없다면, 애플리케이션은 문서가 무해하다고 가장하는 대신 명시적으로 다음과 같이 표시합니다:
MANUAL REVIEW NEEDED
데모
라이브 데모
👉 https://nl-expat-copilot-1.onrender.com
공개 데모는 Render에 호스팅됩니다.
개인 정보 보호와 안전을 위해, 실제 개인 서신 대신 합성된 네덜란드 정부 스타일 문서를 공개 데모에서 사용합니다.
데모 시나리오 1 — 지방 자치 단체 예약
합성된 Burgerzaken(시민 서비스) 예약 서신에는 다음과 같은 정보가 포함되어 있습니다:
12 October 2026
10:20
Stadsplein 1, Nieuwegein
...
Handle It NL은 다음을 수행할 수 있습니다:
- 문서를 예약으로 식별하고;
- 이를 쉬운 영어로 설명하며;
- 정확한 예약 시간을 추출하고;
- 사용자가 지참해야 할 것을 식별하며;
- PDOK를 통해 주소를 검증하고;
- 캘린더 이벤트를 준비하며;
- 예약을 로컬에 저장합니다.
결과 화면에는 설명과 에이전트의 실제 도구 호출(tool calls)이 모두 표시됩니다.
데모 시나리오 2 — 결제 요청서 (Payment letter)
합성된 CJIB 스타일의 편지에는 다음 내용이 포함됩니다:
Amount (금액)
Payment deadline (결제 마감일)
Payment reference (결제 참조 번호)
Handle It NL은 다음을 수행할 수 있습니다:
- 이를 결제가 필요한 것으로 분류하고;
- 금액과 마감일을 추출하며;
- 편지가 무엇을 요청하는지 설명하고;
- 마감일을 저장하며;
- 관련 절차 안내를 제공합니다.
이 시스템은 절대 결제를 수행하지 않습니다.
또한 사용자에게 원본 문서나 공식 포털에서 결제 정보를 직접 확인하도록 상기시킵니다.
데모 시나리오 3 — 정보만 포함된 경우 (Information only)
또 다른 합성 편지는 단순히 정보 전달을 위한 지역 공지문입니다.
수신자가 할 일이 전혀 없습니다.
Handle It NL은 이를 다음과 같이 식별합니다:
INFORMATION ONLY (정보 전용)
NO ACTION NEEDED (조치 필요 없음)
마감일이 저장되지 않습니다.
캘린더 이벤트가 준비되지 않습니다.
불필요한 도구 호출도 없습니다.
저는 이 시나리오가 중요하다고 생각합니다. 왜냐하면 에이전트(agent)가 도구를 가지고 있다는 것과 실제로 사용해야 한다는 것을 동일시해서는 안 되기 때문입니다.
저장된 마감일 대시보드 (Saved deadline dashboard)
중요한 약속과 마감일은 애플리케이션 상단에 표시됩니다.
저장된 항목은 나중에 클릭하여 다음을 포함한 전체 분석 결과를 복원할 수 있습니다:
- 이것은 무엇인가?
- 무시해도 되는가?
- 내가 무엇을 해야 하는가?
- 마감일
- 약속 상세 정보
- 아무것도 하지 않으면 무슨 일이 발생하는가?
- 무엇을 가져와야 하는가?
- 네덜란드 용어
- 확인된 위치
- 캘린더 준비
- 에이전트 추적 (Agent Trace)
저장된 항목은 확인 대화 상자를 통해 제거할 수도 있습니다.
제가 이 시스템을 만든 사람이 말한 것
이 애플리케이션을 그녀에게 보여주었더니, 반응이 정말 놀라웠습니다. 그녀는 이것이 Google Translate 같은 일반적인 번역 도구보다 훨씬 유용하다고 즉시 느꼈습니다.
그 이유는 이 애플리케이션이 네덜란드어 편지 내용을 단순히 영어로 번역하는 것에 그치지 않기 때문입니다. 또한, 외국인 거주자(expat)가 이해하기 쉬운 방식으로 편지의 의미와 맥락을 설명하려고 노력합니다. 예를 들어, 편지가 얼마나 심각하거나 긴급한지, 조치가 필요한지 여부, 중요한 날짜나 마감일이 무엇인지, 그리고 사용자가 어느 정도의 주의를 기울여야 하는지를 알려줄 수 있습니다.
이는 Belastingdienst(세무국), 지방 자치 단체, 건강 보험 제공업체 또는 기타 네덜란드 정부 기관에서 온 공식 서신에 특히 유용합니다. 그러한 편지를 번역을 마친 후에도, 네덜란드 시스템에 익숙하지 않은 사람은 그것이 단순히 정보 전달 목적의 서신인지, 지불 요청서인지, 경고문인지, 아니면 즉각적인 조치가 필요한 것인지 여전히 이해하지 못할 수 있습니다.
이 애플리케이션은 "이 편지가 무슨 내용을 담고 있나요?"라는 질문에 답하는 것을 넘어, "이것이 나에게 무엇을 의미하며, 얼마나 중요한지, 그리고 다음에 무엇을 해야 하는가?"라는 질문까지 답함으로써 그 간극을 메워줍니다. 바로 이 추가적인 설명의 레이어가 그녀의 눈길을 사로잡았습니다. 단순히 번역 도구처럼 작동하는 것이 아니라, 애플리케이션은 외국인 거주자가 네덜란드 행정 커뮤니케이션을 이해하고 처리하도록 특별히 설계된 비서(assistant) 같은 느낌을 줍니다.
코드
GitHub 저장소:
👉 https://github.com/Ahitagni07/nl-expat-copilot
이 저장소에는 다음 내용이 포함되어 있습니다:
nl-expat-copilot/
│
├── frontend/
...
구축 방법 (How I Built It)
높은 수준의 아키텍처는 다음과 같습니다:
네덜란드어 편지 / 스캔된 PDF
│
▼
...
Angular 프론트엔드
프론트엔드는 Angular 22를 사용합니다.
다음 기능을 제공합니다:
- 드래그 앤 드롭(drag-and-drop) 문서 업로드;
- 이미지 및 스캔된 PDF 지원;
- 저장된 마감일 대시보드;
- 문서 해석(document interpretation);
- 실행 가능한 체크리스트(action checklist);
- 마감일 및 약속 제시(deadline and appointment presentation);
- 검증된 위치 카드(verified-location cards);
- 캘린더 액션(calendar actions);
- Agent Trace;
- 저장된 마감일에 대한 삭제 확인.
The 프론트엔드는 다음과 같은 상대 API 경로를 의도적으로 사용합니다:
/api/health
/api/analyze
/api/deadlines
따라서 프로덕션 배포 시 해당 호출들을 프론트엔드 호스트를 통해 프록시(proxy)할 수 있습니다.
FastAPI 백엔드
FastAPI는 신뢰하는 애플리케이션 계층입니다.
이것은 다음을 담당합니다:
- 업로드된 파일 수신;
- 파일 크기/유형 유효성 검사;
- 스캔된 PDF 렌더링;
- 모델에 다중 모드(multimodal) 요청 전송;
- 모델의 도구 호출 처리;
- Python 도구 실행;
- 구조화된 응답 유효성 검사;
- 마감일 저장;
- 약속 중복 제거;
- 문서 분석 캐싱;
- 최종 결과를 Angular로 반환.
스캔된 PDF
스캔된 PDF는 PyMuPDF를 사용하여 렌더링됩니다.
현재 데모 제한 사항은 다음과 같습니다:
Maximum PDF size: 20 MB
Maximum pages analyzed: 5
Render resolution: 140 DPI
...
페이지는 다중 모드 모델로 전송되기 전에 최적화된 이미지로 변환됩니다.
AI 모델
현재 사용되는 모델은 다음과 같습니다:
deepseek/deepseek-v4.1-flash
저는 OpenRouter를 통해 이 모델에 접근합니다.
이 모델은 다음을 담당합니다:
- 문서 읽기;
- 목적 이해;
- 분류(classification);
- 구조화된 추출(structured extraction);
- 도구가 유용한지 결정하기.
모델 설정은 애플리케이션 코드 외부에 있습니다:
OPENROUTER_MODEL=deepseek/deepseek-v4.1-flash
따라서 모델을 변경해도 시스템의 나머지 부분을 다시 작성할 필요가 없습니다.
도구 호출(Tool calling)
모델은 다음 도구를 요청할 수 있습니다:
lookup_official_process()
verify_dutch_address()
prepare_calendar_event()
...
모델 자체는 이 Python 함수들을 실행하지 않습니다.
일반적인 약속 흐름(appointment flow)은 다음과 같습니다:
모델이 문서를 읽음
↓
verify_dutch_address()
...
Angular UI에서는 이 순서를 **에이전트 추적(Agent Trace)**으로 노출합니다.
이것은 저에게 중요했습니다. 왜냐하면 사용자가 다음 두 가지의 차이를 볼 수 있기 때문입니다:
"AI는 이 주소가 존재한다고 생각함"
과:
"PDOK가 실제로 이 주소를 검증했음"
PDOK 주소 검증
네덜란드 주소의 경우, 저는 공개된 PDOK Location API를 사용합니다.
예를 들어, 지방 자치 단체 우편물에 다음 내용이 포함되어 있다면:
Stadsplein 1
3431 LZ Nieuwegein
에이전트는 다음을 요청할 수 있습니다:
verify_dutch_address()
백엔드는 PDOK를 호출하고 검증된 위치 정보를 모델과 프론트엔드에 반환합니다.
캘린더 통합
약속 관련 우편물의 경우, Handle It NL은 다음을 호출할 수 있습니다:
prepare_calendar_event()
이것은 다음과 같이 생성합니다:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
