
【AI 주도 제품 개발 제1회】 개발 회사의 상담·안건 관리 업무를 스프레드시트에서 Web 앱으로
요약
스프레드시트 기반의 업무 관리를 Spring Boot와 Next.js 기반의 Web 앱인 RADAR로 전환하는 과정을 다룹니다. AI 에이전트인 Nexus Architect를 활용하여 설계와 구현 사이의 흐름을 유지하며 제품을 개발하는 방법론을 소개합니다.
핵심 포인트
- 스프레드시트의 한계를 넘어 업무 규칙을 프로덕트로 구현
- Spring Boot와 Next.js를 활용한 풀스택 개발 구조
- AI 에이전트와 인간의 판단을 결합한 개발 플로우 구축
- 설계서와 실제 코드 간의 추적성을 확보하는 리포지토리 구성
이 연재의 구성
| 회차 | 테마 | 주요 내용 |
|---|---|---|
| 제1회 | 완성형과 전체상 | 완성된 RADAR의 기능, 이용자, 핵심 업무, 리포지토리(Repository) 구성 |
| ... |
서론
본 연재에서 다루는 성과물
RADAR는 영업과 PM/프로젝트 리더가 상담, 안건, 인원 할당(Assign), 지연 리스크를 동일한 장소에서 다루기 위한 사내용 Web 애플리케이션입니다.
명칭은 다음 5가지 단어에서 따왔습니다.
R: Resourcing
A: Availability
D: Deal
...
기존 업무에서는 Google 스프레드시트(Google Sheets)를 사용하여 상담 및 안건의 상태, 담당자, 할당 기간을 관리했습니다.
RADAR는 단순히 그 운영 방식을 Web 화면으로 옮기는 것이 아니라, 스프레드시트로는 표현하기 어려웠던 업무 규칙을 프로덕트(Product)로서 구현하고 있습니다.
현재의 리포지토리
본 연재에서 참조하는 실제 성과물은 다음과 같은 구성입니다.
radar/
├── backend/ Spring Boot + PostgreSQL
├── frontend/ Next.js App Router
...
backend/와 frontend/가 현재 구동 중인 프로덕트입니다.
한편, Nexus Architect가 생성한 설계서, 파이프라인(Pipeline) 진행 기록, 리뷰 결과, 초기 MVP는 docs/ 하위에 이력으로 남아 있습니다.
이러한 구성 덕분에 현재의 코드뿐만 아니라, 왜 이러한 설계가 되었는지까지 추적할 수 있습니다.
본 연재를 읽는 법
- RADAR로 무엇을 할 수 있는가
- 성과물이 어떤 구조로 되어 있는가
- Nexus Architect와 무엇을 논의(Wall-hitting)했는가
- 가설을 검증하는 MVP를 어떻게 만들었는가
- Product 파이프라인에서 Architect 파이프라인으로 어떻게 전달했는가
- 설계 리뷰에서 무엇이 부족했는가
- Spring Boot와 Next.js로 어떻게 구현했는가
본 연재는 처음에 완성된 RADAR를 설명합니다.
기존의 설계 성과물을 소개하는 것에 그치지 않고, 설계서의 내용이 실제 코드의 어느 부분에 나타나 있는지도 살펴보겠습니다.
본 연재에서 중시하는 점
AI 에이전트(AI Agent)를 사용하면 성과물을 대량으로 생성할 수 있습니다. 하지만 문서나 코드의 수가 많은 것과 좋은 프로덕트가 만들어지는 것은 별개의 문제입니다.
RADAR 개발에서 중요했던 것은 다음의 흐름을 단절시키지 않는 것이었습니다.

AI에 의한 제안과 인간의 판단을 연결하고, 실제 이용을 통한 배움을 다음 가설로 되돌리는 개발 플로우(Flow)
AI 에이전트가 후보를 제시하고, 인간이 분기점에서 판단하며, 그 판단을 다음 성과물로 넘기는 일련의 흐름을 Nexus Architect를 활용하여 실현했습니다.
완성된 RADAR로 할 수 있는 것
상담과 안건을 하나의 흐름으로 다루기
RADAR에서는 영업 중인 정보를 상담으로, 수주 후의 정보를 안건으로 취급합니다.
영업은 상담을 생성하고 상태, 수주 확도, 계약 형태, 매출 계상 예정 등을 업데이트합니다.

상담 정보, 임시 할당, 경과 기록을 하나의 화면에서 확인한다
수주가 결정되면 상담은 안건 페이즈(Phase)로 이행합니다.

수주 후의 안건을 상태, 납기, 계약 금액과 함께 목록으로 관리한다
단순히 상태 문자열을 변경하는 것만이 아닙니다.
수주 확정 시에는 임시 할당의 정식화, 이력 기록, 감사 로그(Audit Log), 매출 실적 생성과 같은 여러 처리가 동일한 업무 조작으로부터 실행됩니다.
임시 할당에서 정식 할당으로
RADAR의 핵심 기능은 임시 할당입니다.
수주 전의 상담 단계에서도 PM/리더는 다음과 같은 판단을 시작합니다.
- 누구를 안건 리더로 할 것인가
- 어떤 멤버를 언제부터 확보할 것인가
- 기존 안건과의 기간 중복은 없는가
- 정규직(Proper)과 BP(Business Partner) 중 어느 쪽으로 요원을 확보할 것인가
- 종료일이 미정인 상태를 어떻게 다룰 것인가
RADAR에서는 이것들을 임시 할당으로서 미리 등록할 수 있습니다. 수주가 확정되면 대상 상담과 연결된 임시 할당을 단 한 번의 액션으로 정식화합니다.
상담
└─ 임시 할당
├─ 멤버
...
확정 상태와 임시 상태는 색상만으로 구분하지 않습니다. 라벨과 테두리의 차이도 병용하여, 시각적으로나 데이터상으로나 혼동되지 않도록 하고 있습니다.
할당의 중복을 업무 규칙으로 다루기
할당 기간의 중복 규칙도 구현되어 있습니다.
현재의 업무 규칙에서는 정식 할당 중 준위임(Quasi-delegation) 간의 기간 중복을 금지합니다. 반면, 수탁 안건과의 중복이나 임시 할당은 허용합니다.
이 제약은 화면의 경고뿐만 아니라, PostgreSQL의 배타 제약 (Exclusive Constraint)으로도 지켜집니다. 여러 요청이 동시에 도착하더라도, 마지막 방파제를 데이터베이스 측에 두는 설계입니다.
지연 리스크를 찾기
RADAR는 미종료된 상담 건을 정기적으로 확인하여, 할당 종료일이 가까운 상담에 리스크 플래그를 세웁니다.
기본 설정으로는, 가장 빠른 미래의 할당 종료일이 3일 이내라면 위험, 14일 이내라면 주의로 판정합니다. 이미 미결 상태의 플래그가 있는 경우에는 중복해서 늘리지 않고, 심각도가 높아졌을 때만 업데이트합니다.

미대응 지연 리스크와 진행 중인 안건을 대시보드에서 확인한다
예실·영업 의뢰·조달로 확장된 기능
초기 프로덕트 설계에서는 가할당 (Provisional Assignment)이 중심이었습니다. 그 후, 실제 이용 측의 업무 플로우와 대조한 결과, RADAR는 다음 영역까지 확장되었습니다.
- 그룹별·월별 매출 예실 (Budget vs Actual)
- 리더로부터 영업 담당자로의 영업 의뢰
- 정규직/BP를 포함한 요원 계획
- 외부 인재 조달 의뢰
- 파트너사와 면담 정보 관리

월별·그룹별 목표와 실적을 비교하여 예산 부족을 파악한다
첫 설계가 고정된 완성도가 아니었기 때문이 아니라, Nexus Architect로 만든 경계를 토대로 하면서, 실제 이용 과정에서 보인 업무로 프로덕트를 업데이트하고 있습니다.
현시점에서 미완료된 것
구현 완료된 것과 본방 운용까지 완료된 것은 나누어 생각할 필요가 있습니다.
예를 들어, 알림은 이벤트 수신, 중복 제거, 재시도 횟수 및 실패 상태 모델까지 구현되어 있지만, 실제로 재시도를 기동하는 워커(Worker)와 이메일이나 Google Chat 등의 실제 채널은 아직 연결하지 않았습니다. 감사 로그의 INSERT-only 권한도 AWS RDS 구축 시 적용할 항목으로 남아 있습니다.
RADAR는 모든 것이 완성된 제품이 아니라, 가치 검증을 마치고 본방 구현을 쌓아 올리고 있는 프로덕트입니다.
RADAR의 사용자와 핵심 업무
해결하고 싶었던 과제
Google 스프레드시트는 작게 시작하는 업무 관리에 적합합니다. 반면, 상담 수나 인원 조정이 늘어나면 다음과 같은 문제가 보이기 시작합니다.
- 상담이 수주되었는지, 아직 영업 중인지 알기 어렵다
- 누가 어떤 안건에 참여하고 있는지 횡단적으로 확인하기 어렵다
- 확정 전의 인원 조정을 색상이나 코멘트로 표현하고 있다
- 동일한 멤버를 동일한 기간에 중복 할당하기 쉽다
- 상태 변경의 이유와 이력이 셀 덮어쓰기로 인해 유실된다
- 지연 징후를 사람이 시트를 보고 직접 찾아야 한다
문제는 스프레드시트 그 자체가 아닙니다. 자유로운 표 형식에 상태 전이, 권한, 동시 실행 제어, 감사와 같은 책임이 너무 많이 모인 것입니다.
주요 이용자
Product 파이프라인에서는 두 가지 주요 페르소나(Persona)를 정의했습니다.
| 페르소나 | 역할 | 주요 판단 |
|---|---|---|
| 다나카 씨 | PM/프로젝트 리더 | 유휴 요원, 가할당, 정식화, 지연 리스크 |
| 사토 씨 | 영업 | 상담 생성, 수주 확도, 상태 업데이트, 영업 의뢰 |
현재 구현에서는 이용자를 다음과 같은 역할(Role)로 취급합니다.
| 역할 | 주요 책임 |
|---|---|
| ADMIN | 전체 관리, 조직, 예산, 감사 로그 |
| ... |
초기 4개 역할에, 실제 이용 재설계 시 PROCUREMENT가 추가되었습니다.
최우선 업무 플로우
설계와 구현을 잇는 중심이 된 것은 가할당에서 정식화까지의 흐름입니다.
이 플로우에서는 여러 업무 판단이 관계합니다.
- 수주 전 정보를 확정 정보와 혼동하지 않는다
- 수주 확정에는 안건 리더가 필요하다
- 정식화를 이중 실행해도 결과가 중복되지 않게 한다
- 정식 할당의 기간 제약을 지킨다
- 누가 확정했는지를 감사 로그에 남긴다
단순한 CRUD가 아니라, 프로덕트 고유의 규칙이 모이는 곳입니다.
처음부터 정하지 않았던 것
초기 구상에서는 장래적인 SaaS화도 후보에 있었습니다. 하지만 첫 대상은 자사 이용입니다.
멀티 테넌트 (Multi-tenant), 고도화된 예측 분석, 회계 연동, 외부 캘린더 연동은 초기 스코프에서 제외했습니다.
또한, 기존 스프레드시트의 데이터 이관도 처음부터 운용을 시작한다는 판단에 따라 채택하지 않는 방침이 되었습니다.
신규 프로덕트에서는 무엇을 만들 수 있는가보다 처음에 어떤 업무 판단을 개선할 것인가를 좁히는 것이 중요합니다.
성과물을 뒷받침하는 리포지토리 구성
현재 동작하는 두 개의 프로젝트
본방 구현은 backend/와 frontend/로 나뉘어 있습니다.
| 디렉토리 | 기술 | 역할 |
|---|---|---|
backend/ | Java 21 / Spring Boot 3.3 / PostgreSQL | 도메인, 인증, 권한, 영속화, 감사 |
frontend/ | Next.js App Router / React / TypeScript | SSR 화면, Server Actions, 업무 조작 |

RADAR의 애플리케이션 아키텍처. 실선은 런타임 통신, 점선은 설계 산출물에서 구현으로의 추적성(Traceability)을 나타냄
백엔드는 단일 Spring Boot 애플리케이션으로 배포하는 모듈러 모놀리스(Modular Monolith)입니다. 경계 컨텍스트(Bounded Context)별로 Java 패키지와 PostgreSQL 스키마를 분리했습니다.
com.radar.app
├── dealassignment BC-001 상담·어사인
├── risk BC-002 지연 리스크
...
현재 산출물에는 백엔드 Java 소스 약 200개 파일, 테스트 클래스 40개 이상, 15개의 Next.js 페이지, 20개 이상의 Flyway 마이그레이션이 포함되어 있습니다.
업무 규칙을 트랜잭션으로 구현하기
예를 들어, 가(假) 어사인의 정식화는 AssignmentFormalizationService에 집약되어 있습니다.
여기서는 단순히 어사인의 종류를 업데이트하는 것이 아니라, 다음 처리들을 하나의 트랜잭션으로 수행합니다.
Idempotency-Key를 통한 중복 실행 방지- 상담의 행 잠금(Row Lock) 및 안건화 여부 확인
- 안건 리더와 가 어사인의 정합성 확인
- 상담과 어사인의 정식화 및 이력 저장
- 감사 로그(Audit Log)와 매출 실적 생성
- DB의 배타 제약 조건 위반을 업무 예외(Business Exception)로 변환
실제 서비스 구현에서 핵심이 되는 메서드를 발췌합니다.
AssignmentFormalizationService의 실제 코드
@Transactional
public FormalizationResult formalizeDealAssignments(UUID dealId, UUID actorUserId, String idempotencyKey) {
// SYN2-008 대응: 클라이언트 재전송 감지 (Idempotency-Key 패턴)
...
서비스 내의 주석에는 설계 리뷰에서 부여한 SYN2-008이나 DIN-106 등의 지적 ID도 남겨두었습니다. 설계서의 지적 사항이 구현의 어느 부분에서 해소되었는지 코드를 통해 역추적할 수 있도록 하기 위함입니다.
읽기는 Server Components, 업데이트는 Server Actions
프론트엔드는 브라우저에서 백엔드로 직접 액세스하는 구성이 아닙니다.
Server Components를 통한 읽기는 apiFetch()로 집약하며, 브라우저로부터 전달받은 쿠키(Cookie)를 백엔드로 중계합니다.
모든 데이터가 세션에 의존하기 때문에, 가져오기 시 cache: 'no-store'를 지정하고 있습니다.
export async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
const cookieStore = await cookies();
const response = await fetch(`${BACKEND_URL}${path}`, {
...
업데이트 작업은 Server Actions를 경유합니다. 클라이언트 측에 범용 fetch 래퍼(Wrapper)를 만들지 않고, 인증 쿠키와 업무 조작의 경계를 서버 측으로 옮겼습니다.
정식화 Server Action에서는 화면에서 생성한 Idempotency-Key를 백엔드로 전달합니다. 성공 후에는 상담, 안건, 메인 페이지의 캐시를 재검증(Revalidate)하여 동일한 작업 결과가 각 화면에 반영되도록 합니다.
export async function formalizeDeal(
dealId: string,
idempotencyKey: string
...
중복 어사인을 데이터베이스에서도 거부하기
정식화 서비스는 경합(Conflict)을 업무 예외로 변환하지만, 중복 그 자체를 마지막에 거부하는 것은 PostgreSQL입니다.
daterange의 겹침 연산자 &&
를 사용한 배타적 제약 (Exclusive Constraint)을 통해, 동일한 사용자의 기간이 겹치는 정식 할당 (Formal Assignment)을 저장할 수 없습니다. 가할당 (Tentative Assignment)과 종료일 미정 할당은 이 제약의 대상에서 제외됩니다.
ALTER TABLE deal_assignment.assignments
ADD CONSTRAINT no_overlapping_assignments
EXCLUDE USING gist (
...
화면에서 사전에 경고하고, 서비스에서 예외를 처리하며, 데이터베이스에서 동시 실행 시의 정합성을 지키는 3단계 설계입니다.
디자인 시스템을 단일 소스로 만들기
design-system/default/에는 다음 산출물이 있습니다.
tokens.css/tokens.json- 컴포넌트 목록
- UI 가이드라인
- 프리뷰

tokens.css를 실행 시의 단일 소스 (Single Source)로 삼고, globals.css를 통해 shadcn/ui, Tailwind CSS, 각 컴포넌트로 반영합니다.
현재 프론트엔드는 Tailwind CSS와 shadcn/ui를 이용하지만, 색상이나 여백 값을 컴포넌트에 직접 쓰는 것이 아니라, design-system/default/tokens.css를 실행 시의 단일 소스로 참조합니다.
frontend/app/globals.css는 tokens.css를 @import하고, 그 값을 shadcn/ui의 시맨틱 슬롯 (Semantic Slot)과 Tailwind CSS의 @theme inline으로 연결합니다. 따라서 컴포넌트는 구체적인 컬러 코드가 아닌, text-fg, bg-success-bg, border-warning과 같이 의미 있는 클래스 이름을 사용할 수 있습니다.
특히 중요한 원칙은 확정 상태와 가(假) 상태를 색상만으로 표현하지 않는 것입니다.
가할당: 점선 + 가(假) 라벨 + 상태 색상
정식 할당: 실선 + 정식 라벨 + 상태 색상
이 원칙은 가이드라인뿐만 아니라, 가동 계획 그리드 (Operation Plan Grid) 컴포넌트에도 나타나 있습니다. 가할당에는 **가(假)**라는 텍스트와 aria-label을 붙여, 정식 할당과는 형태도 다르게 하고 있습니다.
function ChipView({ assignment, deal }: { assignment: PlanAssignment; deal: PlanDeal }) {
const isTentative = assignment.type === "가할당";
const tint = DEAL_TINT_CLASSES[deal.tintIndex % DEAL_TINT_CLASSES.length];
...
설계 이력을 아카이브하기
docs/design-history/에는 Product 파이프라인과 Architect 파이프라인이 생성한 산출물이 남아 있습니다.
reports/
├── 00_core Vision, 성공 지표, 스코프, 가설
├── 01_ux 페르소나, 저니, 도메인 스토리
...
work/traceability.json은 Vision, 가설, 스코프, 기능, 요구사항, API, 경계 컨텍스트 (Bounded Context)의 ID를 연결합니다. AI가 생성한 문서를 폴더에 두는 것으로 끝내지 않고, 나중에 설계 판단을 추적할 수 있는 상태로 만듭니다.
MVP는 삭제하지 않고 동결하기
Hono, SQLite, React, Vite로 만든 컨시어지 (Concierge) MVP는 docs/concierge-mvp-archive/로 이동했습니다.
MVP는 운영 코드의 토대가 아닙니다. 하지만 무엇을 검증했는지, 어떤 화면이 사용되었는지, 어디에 기술적 부채 (Technical Debt)가 있었는지를 보여주는 증거입니다. 따라서 현재의 구현과 섞지 않고, 삭제도 하지 않으며, 읽기 전용 이력으로 남겨두고 있습니다.
RADAR는 경계 컨텍스트별로 나눈 Spring Boot와 Next.js 구성을 채택하여, 업무 규칙, 디자인, 설계 판단의 이력을 각각 추적할 수 있는 형태로 관리하고 있습니다.
다음 회차에서는 AI와의 브레인스토밍 (Wall-hitting), Vision, 성공 지표, 가설, 페르소나, UI 모형 (UI Mock)을 다룹니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기