rfp-match:1

이지예·2026년 9월 17일

개발

목록 보기
30/58

왜 이 프로젝트를 시작했나

네트워크 보안장비를 다루는 기업의 기술영업 직무는 공공기관 입찰 대응이 핵심 업무 중 하나다. 입찰공고에 딸린 제안요청서(RFP)에는 "처리성능 10Gbps 이상", "국제 보안인증 등급 EAL2 이상", "특정 기관 보안적합성 검증필" 같은 요구사항이 수십 줄로 박혀 있다.

담당자가 하는 일은 이 요구사항을 자사 제품 카탈로그와 한 줄씩 대조해서 충족 여부를 판단하는 것이다. 이 작업은 두 가지 특징을 가진다.

  1. 반복적이다 — 공고마다, 제품마다 똑같은 대조 작업을 반복한다.
  2. 틀리면 비싸다 — 충족 여부를 잘못 판단하면 입찰 참여 자체가 무의미해지거나, 최악의 경우 실격 사유가 된다.

이 두 특징이 겹치는 지점이 자동화 후보다. 그래서 "입찰공고를 자동으로 모으고, 요구사항과 제품 스펙을 대조해서 갭분석표를 만들어주는 시스템"을 만들기로 했다.

문제를 정확히 쪼개기

기획 단계에서 가장 신경 쓴 부분은 "무엇을 자동화하고 무엇을 자동화하지 않을 것인가"를 명확히 가르는 것이었다. 처음부터 전부 자동화하려고 하면 프로젝트가 산으로 간다.

자동화 대상

  • 공고 수집: Open API로 주기 폴링, 키워드 필터링, DB 캐싱
  • 요구사항 ↔ 제품 스펙 대조 판정: 이게 이 프로젝트의 핵심이다

자동화하지 않은 것

  • RFP 원문(첨부파일) 해석: 공공 Open API는 공고 메타데이터만 줄 뿐, 첨부된 RFP 문서 자체를 구조화해서 주지 않는다. PDF/HWP 파싱과 자연어 처리까지 손대면 범위가 무한히 커진다. 요구사항 항목 등록은 사람이 원문을 읽고 입력하는 반자동 방식으로 못 박았다.
  • 애매한 판정의 최종 확정: 나중에 다시 다루겠지만, 자동 판정 엔진이 자신 없는 항목은 "확인불가"로 남기고 사람에게 넘기도록 설계했다.

이 경계를 초반에 명확히 그은 게 이후 개발 전체의 방향을 잡아줬다. "완전 자동화"가 아니라 "반복 작업은 기계가, 판단이 필요한 부분은 사람이" 라는 원칙이다.

MVP 범위

기능을 4개로 쪼갰다.

  1. 공공 입찰공고 Open API 연동 → 보안장비 관련 공고 자동 수집
  2. 공고별 요구사항 항목 등록 (수동 입력)
  3. 자사 제품 스펙 DB 관리 (CRUD)
  4. 요구사항 ↔ 제품 스펙 자동 매칭 → 갭분석 리포트

이 중 4번이 압도적으로 중요하다. 1~3번은 사실 CRUD와 배치 작업이라 기술적으로 특별할 게 없다. 반면 4번은 "처리성능 10Gbps"와 "네트워크 처리량 20Gbps"가 같은 항목을 가리킨다는 걸 코드가 판단해야 하고, "EAL2"와 "EAL4"의 대소를 비교해야 하고, "10Gbps"와 "8000Mbps"를 같은 단위로 환산해서 비교해야 한다. 이건 단순 API 연동이 아니라 직접 설계해야 하는 도메인 규칙이다.

기술 스택 선정

구분선택이유
BackendJava 17, Spring Boot 3.4도메인 규칙을 타입으로 명확히 표현하고 싶었다. record, sealed 등 최신 문법도 써보고 싶었고
DBH2(로컬) / MySQL 8(배포)인증키 없이도 바로 클론해서 띄워볼 수 있어야 한다는 게 우선순위였다
FrontendReact 18 + TypeScript + Vite갭분석 결과를 표와 차트로 보여줘야 해서 타입 안전성이 중요했다
외부 연동공공 데이터 포털 Open API실시간 조회 대신 스케줄러 폴링 + DB 캐싱 구조로 설계

DB를 로컬은 H2, 배포는 MySQL로 나눈 이유는 단순하다. "클론하고 바로 실행"이 안 되는 포트폴리오는 아무도 끝까지 안 본다. 인증키 발급 절차나 DB 설치를 먼저 요구하면 리뷰어가 중간에 이탈한다. 그래서 기본 프로필은 H2 인메모리 + 예시 데이터 자동 적재로 잡고, 운영에 가까운 검증이 필요할 때만 MySQL 프로필로 전환하도록 나눴다.

아키텍처 초안

[React Client]
     │ REST/JSON
     ▼
[Spring Boot API Server]
     ├─ BidController          → 공고 목록/상세
     ├─ RequirementController  → 공고별 요구사항 등록/조회
     ├─ ProductController      → 제품 스펙 CRUD
     ├─ MatchController        → 매칭 실행 + 갭분석 조회
     ├─ BidSyncScheduler       → Open API 주기 폴링 → DB 저장
     │
     ▼
[MySQL] BidAnnouncement / RequirementItem / ProductSpec / MatchResult

설계에서 가장 먼저 정한 원칙은 "공고는 실시간 조회하지 않는다" 였다. 사용자가 화면을 열 때마다 외부 API를 호출하면, 외부 API가 느리거나 죽어 있을 때 내 서비스까지 같이 죽는다. 대신 스케줄러가 하루 1~2회 수집해서 DB에 캐싱하고, 화면은 항상 DB만 본다. 이 결정 하나로 "외부 API 장애 = 서비스 장애"라는 등식을 끊을 수 있었다. (이 부분의 구체적인 장애 대응은 4편에서 다룬다.)

로드맵

Phase범위
Phase 1 (MVP)Open API 연동, 제품 스펙 CRUD, 요구사항 수동 등록, 수치/등급/Boolean 매칭
Phase 2갭분석 리포트 시각화, 매칭 결과 저장·재조회
Phase 3텍스트형 요구사항 규칙 확장, 제안서 초안 내보내기
Phase 4배포, API 문서 공개

다음 글에서는 이 프로젝트의 핵심인 매칭 엔진을 다룬다. "요구사항 문구와 제품 스펙 항목명이 다른데 어떻게 같은 항목이라고 판단하는가", "10Gbps와 EAL2를 어떻게 같은 파이프라인으로 비교하는가" 같은 실제 설계 문제를 풀어간 과정을 정리했다.


이 시리즈는 개인 학습 프로젝트 개발 기록입니다. 코드에 등장하는 기관명·제품명은 전부 가상이며, 실제 연동 대상 서비스의 명칭·엔드포인트는 기술적 세부사항 보호를 위해 일반화해서 서술합니다.

profile
github: @dlwldP

0개의 댓글