핵심 요약

영업 담당자가 제안서 원문을 붙여넣으면 백엔드 AI가 12개 섹션으로 구조화하고, 어드민에서 섹션을 검수·보정해 고정 렌더러로 고유 URL에 발행하는 운영 도구입니다. 제안서를 데이터(내용) × 렌더러(디자인)로 분리해, 매번의 수동 조판을 없애고 건별 품질 편차를 줄였습니다. Vite SPA에 TanStack Router 타입세이프 라우팅, 서버/UI/폼 상태 3분할, Feature-Sliced Design을 적용했습니다.

프로젝트 배경

영업 건마다 제안서를 새로 디자인·조판해 시간이 들고 담당자·건별 품질 편차가 컸으며, 산출물이 PDF/PPT라 모바일 열람과 포트폴리오 연계가 불편했습니다. 발주 플랫폼 규정상 회사명·연락처 노출도 금지돼, '글만 준비하면 보낼 수 있는 웹 제안서가 나온다'를 컨셉으로 제안서를 데이터(내용) × 렌더러(디자인)로 분리해 템플릿화했습니다.

담당 범위

  • 제안서 섹션 에디터 — 12개 섹션 구조화·검수·완성도 체크
  • 제안서 발행·미리보기 및 공개 URL(proposals/[slug])
  • 포트폴리오·프로젝트 CRUD 및 발행 연동
  • AI 프롬프트 관리(prompts) — 구조화 프롬프트 운영
  • TanStack Router 타입세이프 라우팅·인증 가드·공통 API 계층

문제 정의

  • 형식이 제각각인 원문 — 제안서 원문 형식이 건마다 달라 사람이 매번 섹션을 나누고 조판해야 했습니다. 이 수작업이 작성 시간과 품질 편차의 근원이었습니다.
  • 상태의 혼재 — 서버 데이터(제안서·포트폴리오·프로젝트), 편집 UI 상태, 폼 입력이 뒤섞이면 화면이 커질수록 "지금 무엇이 원본인지" 추적이 어려워집니다.
  • AI를 그대로 믿을 수 없음 — AI 구조화 결과에는 오분류·누락이 섞입니다. 사람이 검수·보정할 편집 계층 없이 바로 발행하면 신뢰할 수 없는 산출물이 나갑니다.

기술적 의사결정

1. 제안서를 데이터 × 렌더러로 분리

  • 배경 — 바뀌는 것은 섹션 데이터뿐이고 레이아웃·디자인은 고정입니다.
  • 선택 — 12개 섹션마다 전용 에디터·렌더러가 해당 데이터만 다루고, 섹션 완성도 체크로 빠진 항목을 드러냈습니다. 결과물은 파일이 아니라 고유 URL(proposals/[slug])로 발행해 모바일 열람·재전송 없는 수정·포트폴리오 연계를 자연스럽게 했고, 회사명·연락처 같은 신원 정보는 기본값에서 구조적으로 배제했습니다.
  • 트레이드오프 — 렌더러를 자유 편집형으로 열면 표현력은 늘지만 품질 편차라는 원래 문제가 되살아납니다. 디자인을 고정하고 데이터만 바꾸는 제약을 택해 품질을 상향 평준화했습니다.

2. 서버 · UI · 폼 상태 3분할

  • 배경 — 세 종류의 상태를 한 도구로 다루면 서버 데이터가 클라이언트 Store의 사본으로 흩어집니다.
  • 선택 — 원격 데이터의 조회·캐시·무효화는 TanStack Query가, 화면 인터랙션 상태는 Jotai가, 폼 입력·검증은 React Hook Form + Zod가 맡도록 책임을 갈랐습니다. 편집·발행 흐름의 상태를 예측 가능하게 유지했습니다.

3. TanStack Router 타입세이프 라우팅 + FSD

  • 배경 — 기능이 늘어도 같은 패턴으로 확장하고, 경로·파라미터를 런타임이 아닌 타입 수준에서 보장하고 싶었습니다.
  • 선택 — 라우터 플러그인이 생성하는 routeTree로 경로·파라미터를 타입 수준에서 보장하고 진입 가드로 인증을 처리했습니다. entities·features·widgets·shared 계층으로 제안서·포트폴리오·프로젝트·프롬프트 도메인을 나눴습니다.

4. AI는 '생성'이 아니라 '구조화 + 검수 대상'으로 한정

  • 배경 — AI가 내용을 지어내면 사실·수치가 틀어지고, 외부 API 장애가 곧 발행 중단이 됩니다.
  • 선택 — 사실·수치·의미는 원문 그대로 두고, 형식이 제각각인 원문을 정해진 섹션으로 분해하는 데만 AI를 썼습니다. 구조화는 백엔드가 수행하고 어드민은 프롬프트를 관리·튜닝하며 결과 섹션을 사람이 검수·보정합니다.
  • 트레이드오프 / 보완 — AI 자동화율을 높이면 사람 손은 줄지만 오류 위험이 커집니다. AI는 초벌 분해까지, 최종 책임은 사람으로 경계를 긋고, AI 장애·거부·빈 입력에도 발행이 멈추지 않도록 규칙 기반 폴백을 뒀습니다.

Before → After

관점BeforeAfter
산출물PDF/PPT 파일고유 URL 웹 제안서
제작 방식건별 수동 디자인·조판원문 → 섹션 구조화·검수·렌더
수정·전달수정 후 파일 재전송같은 링크에 그대로 반영
상태 관리서버·UI·폼 상태 혼재Query·Jotai·RHF 3분할
신원 노출실수로 노출 위험비노출을 기본값으로 구조적 차단

결과

  • 원문 입력 → 섹션 구조화 → 검수 → 발행 전 과정 동작
  • 제안서 조판 과정 제거로 작성 시간 단축·품질 상향 평준화
  • 서버/UI/폼 상태 3분할로 편집·발행 흐름 상태 예측 가능화
  • AI 구조화 + 규칙 기반 폴백으로 AI 없이도 발행 지속
  • 회사명·연락처 비노출을 기본값으로 구조적 차단

회고

AI를 붙일 때 가장 중요한 결정은 "AI에게 어디까지 맡기지 않을 것인가"였습니다. 생성까지 맡기면 편해 보이지만 제안서에서 사실·수치가 틀어지는 건 치명적이라, AI를 '초벌 구조화'로만 한정하고 최종 판단은 사람과 규칙 기반 폴백에 남겼습니다. 그 덕에 외부 API 상태와 무관하게 발행이 멈추지 않는 시스템이 됐습니다. "데이터 × 렌더러" 분리도 같은 사고의 연장으로, 바뀌는 것과 고정할 것을 먼저 가르면 자동화의 경계가 저절로 보인다는 점을 확인했습니다.