[둥지] 2박 3일 해커톤 회고
n8n 웹훅의 단일 엔드포인트 한계 극복, 대용량 파일 분석을 위한 비동기 처리 등 해커톤 기간 동안의 치열한 기술적 트러블슈팅과 성장 기록입니다.
이번 해커톤에서 ‘둥지’ 서비스를 통해 사회초년생의 전세 사기 문제를 기술적으로 해결하고자 했다. React 기반의 프론트엔드와 n8n이라는 No-Code 툴을 백엔드로 결합하는 하이브리드 아키텍처를 시도했는데, 이 과정에서 플랫폼의 한계와 물리적 제약 등 여러 난관에 부딪혔다. 하지만 이러한 제약 사항들은 오히려 더 견고한 설계를 고민하게 만드는 계기가 되었다.
개인적인 소감
2박 3일 동안 통합 4시간 잤나?.. 이렇게 하루 종일 코딩만 했는데도 힘들기보다는 팀원들과 함께 이루어나가는 과정이 즐거웠고, 완성하지 못한 기능에 대해 아쉬움이 남았다. 좋은 팀원들 덕분에 좋은 경험하고 “대상”이라는 좋은 결과도 얻고, 많이 배울 수 있었다!
트러블슈팅 (Trouble Shooting)
1. n8n의 Webhook URL mapping
첫 번째로 직면한 문제는 n8n 웹훅(Webhook)의 단일 엔드포인트 한계였다.
일반적인 REST API 서버와 달리, n8n은 하나의 워크플로우 당 단 하나의 URL만 생성된다. 하지만 우리 서비스는 PDF 생성, 이메일 발송, 깡통전세 위험도 분석 등 체크리스트별로 각 액션버튼을 가지고 있고, 이를 구분해서 처리해야 했다. URL 경로(Path)로 리소스를 구분할 수 없는 상황에서, 요청 본문(Body)에 ActionType이라는 식별자를 필수적으로 포함시키는 패턴을 생각해냈다. 프론트엔드에서 TypeScript 리터럴 타입으로 ActionType을 정의하고, 백엔드에서는 Switch 노드가 이 값을 기준으로 로직을 분기하도록 설계했다. 이를 통해 단일 엔드포인트 환경에서도 다형성을 가진 API 구조를 구축할 수 있었다. 또한 개발 도중 빈번했던 프론트-백 간의 규격 불일치 문제인 ‘코드 표류(Code Drift)’ 현상도 효과적으로 방지할 수 있었다.
URL은 크게 /checklist, /scan, /chatbot 3가지로 구분했다. 자세한 내용은 아래 포스트를 참고 바란다.
첫 날 밤에 계약서 꼼꼼히 살펴보기(analyzeDocuments)와 둥지 스캔하기(scanDocuments) 기능에 내부적으로 혼선이 생겨서 골치가 아팠다. 두 기능의 차이점을 강조해서 PRD를 작성해놓은 상황이었는데, 추가적인 문서들에 반영되지 않았던 것인지, 프론트 담당 팀원이 착각을 하는 바람에 전부 꼬였었다. 여기에, 자리를 비운 와중 문서 종류별 정밀 분석을 PDF, 메일로 내보내기 액션버튼을 각각 만들었더라. 동일 기능인지라 하나로 통합한 후, docType 파라미터를 추가하는 것이 경제적인 설계라고 판단해서 이 역시 수정해야 했다.
사람끼리 협업해도 소통에 구멍이 생기는데, AI도 협업하다보니 더더욱 쉽지 않았다. 최신 버전을 맞춰서 파라미터 맞추고 URL 세팅해놓으면, AI가 수정하다가 과거 문서를 참조하여 다시 바꿔놓기도 한다. 그 사실을 모르고 다른 부분 작업하다가 한참 뒤에 발견하기도 하고… 이 과정이 너무 반복되어서 결국 내가 직접 밤새서 맞췄다. 스파게티 코드를 수습하면서 React 구조를 얼추 파악하게 되었다.
src/api/에 n8n(서버)와 통신할 request, respond json의 필드와 자료형을 정의해둔다.src/pages/에는 각 페이지별로 html과 css, js까지 화면 출력과 관련된 부분이 쓰여있다.src/components/에는 각 컴포넌트에 대한 것들을 파일별로 정의해두었다.src/types/에 필요한 자료형과 API 관련한 내용들이 정의되어 있다. 그리고nginx.conf에서 WebhookURL 관련한 내용을 수정한다.
2. 비동기? 동기?
두 번째 과제는 대용량 문서 분석 시 발생하는 타임아웃 문제와 사용자 경험(UX) 최적화였다.
등기부등본이나 계약서 같은 고밀도 문서를 OCR과 LLM으로 분석하는 작업은 30초 이상의 긴 시간이 소요되었다. 초기에는 클라이언트가 응답을 기다리다 연결이 끊어지는 HTTP Timeout 문제가 발생했고, 사용자는 하염없이 로딩 화면만 바라봐야 했다. 이를 해결하기 위해 동기식 처리 방식을 비동기식 ‘Fire-and-Forget’ 패턴으로 전환했다. 사용자가 분석을 요청하면 서버는 즉시 접수 확인 응답을 보내고, 실제 무거운 분석 작업은 백그라운드에서 실행되도록 구조를 변경했다. 프론트엔드에서는 즉각적으로 “분석이 시작되었습니다”라는 완료 화면을 보여주는 ‘낙관적 UI(Optimistic UI)’를 적용하여 사용자 이탈을 막았고, 분석 결과는 이메일로 비동기 발송하는 방식으로 서비스 흐름을 재설계하여 UX로 승화시켰다.
그러다가도, 문서 정밀 분석 리포트를 화면에 출력하기 위해서는 Respond to Webhook 노드를 이용해 브라우저에 응답을 전해주어야 했다. 그래서 게약서/등기부등본/건축물대장 분석하기 기능은 동기식 처리 방식으로 다시 전환했다. HTTP Timeout 문제의 경우 nginx의 타임아웃 시간을 5분으로, API timeout을 3분으로 지정해 해결했다.
1
2
3
4
5
6
7
8
# nginx.conf
location /api/scan {
...
# 타임아웃 시간을 5분(300초)으로 연장
proxy_read_timeout 300s;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
}
1
2
3
4
5
export const apiClient = axios.create({
timeout: 180000,
headers: {
'Content-Type': 'application/json',
},
3. 데이터 형 변환
세 번째로, No-Code 툴 내부의 데이터 정합성 문제를 해결해야 했다.
n8n 워크플로우 내에서는 JSON 객체, HTML 문자열, 바이너리 데이터 등 다양한 타입이 혼재되어 노드 간 데이터 전달 시 오류가 잦았다. 특히 OCR로 추출된 비정형 텍스트를 정형 데이터로 변환하는 과정에서 데이터 유실이 발생하곤 했다. n8n 작업하면서 가장 시간이 오래걸리고 어려웠던 작업이 바로 이 데이터 전처리가 아닐까 싶다. 이를 해결하기 위해 핵심 로직의 앞뒤에 데이터를 정제(Sanitization)하는 코드 노드를 배치했다. 이 코드 노드들이 마치 API 명세서처럼 입력과 출력의 스키마를 강제하도록 만들어, No-Code 환경에서도 코드 레벨의 엄격한 타입 안정성을 확보했다.
여기서 더더욱 API 명세서의 중요성을 느꼈다. input, output의 데이터 타입과 필드명을 정해놓는 게 불필요한 작업을 줄일 수 있는 방법인 것 같다. API 명세서를 작성할 때 사용하지 않을 것들을 정의해놓는 것은 문제되지 않는데, 써야 할 것을 미리 정의해놓지 않아서 중간에 추가하는 순간 문제가 복잡해진다는 것을 뼈저리게 느꼈다.
4. 네트워크 및 보안 이슈
마지막으로, 예측 불가능한 인프라 이슈에 대한 유연한 대처가 있었다.
해커톤 현장의 네트워크 보안 정책으로 인해 AWS S3 접근이 차단되면서 파일 업로드 기능이 마비되는 위기 상황이 발생했다. 인프라 디버깅에 시간을 쏟기보다 서비스 구현이 우선이라 판단했고, 즉시 Supabase Storage로 스토리지 백엔드를 교체하는 결단을 내렸다. 이러한 빠른 의사결정 덕분에 인프라 문제에 매몰되지 않고 마감 기한 내에 안정적인 파일 업로드 기능을 구현할 수 있었다.
마치며
짧은 기간이었지만, 주어진 제약 속에서 공학적인 해답을 찾아가는 과정은 큰 자산이 되었다. 단순히 “기능을 구현했다”는 것을 넘어, “왜 이 기술을 선택했고, 어떻게 최적화했는지”를 치열하게 고민했던 이 경험은 앞으로 더 복잡한 시스템을 설계하는 데 있어 단단한 기반이 될 것이다. 무엇보다 처음 보는 코드 구조를 빠르게 파악하고 수정해나가는 스스로의 모습에서 앞으로 이 길로 나아가다가 어려움이 있어도 극복할 수 있을 것이라는 자신감과 스스로에 대한 믿음이 생겼다.

