KoderKoder.ai
가격엔터프라이즈교육투자자용
로그인시작하기

제품

가격엔터프라이즈투자자용

리소스

문의하기지원교육블로그

법적 고지

개인정보 처리방침이용 약관보안허용 사용 정책악용 신고

소셜

LinkedInTwitter
Koder.ai
언어

© 2026 Koder.ai. All rights reserved.

홈›블로그›사용자 스토리에서 데이터베이스 스키마까지: AI가 안내하는 방법
2025년 6월 12일·7분

사용자 스토리에서 데이터베이스 스키마까지: AI가 안내하는 방법

사용자 스토리, 엔티티, 워크플로에서 실무에 맞는 데이터베이스 스키마로 전환하는 실용적 방법과 AI로 누락/규칙을 점검하는 법을 배웁니다.

사용자 스토리에서 데이터베이스 스키마까지: AI가 안내하는 방법

실제 업무에 맞는 스키마를 만드는 이유

데이터베이스 스키마는 앱이 정보를 어떻게 기억할지에 대한 설계도입니다. 실무 관점에서 보면:

  • 테이블: 정보의 ‘버킷’(예: Customers, Orders, Tickets)
  • 필드(컬럼): 각 항목에 대해 저장하는 세부사항(예: customer_name, order_date)
  • 관계: 버킷들이 어떻게 연결되는지(예: 하나의 Order는 하나의 Customer에 속하고, Customer는 여러 Order를 가질 수 있음)

스키마가 실제 업무에 맞으면 사람들의 실제 행위—생성, 검토, 승인, 예약, 할당, 취소—를 반영합니다. 화이트보드상으로 깔끔해 보이는 구조보다 실제로 필요한 데이터를 저장하는 게 중요합니다.

왜 사용자 스토리에서 시작해야 하나요?

사용자 스토리와 수용 기준은 누가 무엇을 하고, ‘완료’가 무엇인지 평이한 언어로 설명합니다. 이를 출발점으로 삼으면 중요한 세부사항(예: “환불을 누가 승인했는지 추적해야 한다” 또는 “예약은 여러 번 재조정될 수 있다”)을 놓칠 가능성이 줄어듭니다.

스토리에서 시작하면 범위(scope)에 대해 정직해집니다. 스토리(또는 워크플로)에 없으면 ‘옵션’으로 처리하고, 불필요하게 복잡한 모델을 미리 구축하지 마세요.

AI가 할 수 있는 것과 할 수 없는 것

AI는 다음을 빠르게 도와줄 수 있습니다:

  • 후보 엔티티(스토리에서 중요한 ‘사물’) 추출
  • 수용 기준이 암시하는 필드(타임스탬프, 상태, 참조 등) 제안
  • 가능성 높은 관계와 누락된 부분(예: “승인자를 저장하지 않음”) 발견

AI가 신뢰할 수 없거나 할 수 없는 것:

  • 문서화되지 않은 숨은 비즈니스 규칙이나 엣지 케이스를 알아내기
  • 단일한 ‘정답’ 수준의 세부사항(단순성 vs 유연성)을 결정하기
  • 리포팅, 보안, 컴플라이언스 요구에 완전히 맞는 스키마를 보장하기

AI는 강력한 도우미로 쓰되, 결정권자는 사람이어야 합니다.

만약 AI 도우미를 실제 개발로 빠르게 연결하고 싶다면 Koder.ai 같은 vibe-coding 플랫폼이 스키마 결정에서 React + Go + PostgreSQL 앱으로 빠르게 전환하도록 도와줄 수 있습니다—단, 모델, 제약, 마이그레이션은 여전히 여러분이 통제합니다.

기대치 설정: 반복적 작업이지 한 번에 끝나는 게 아니다

스키마 설계는 루프입니다: 초안 → 스토리에 대입해 테스트 → 누락된 데이터 발견 → 개선. 목표는 완벽한 초안이 아니라, 각 사용자 스토리에 대해 “이 워크플로가 필요한 모든 것을 저장할 수 있다”라고 설명할 수 있는 모델입니다.

입력 자료: 사용자 스토리, 수용 기준, 실제 예시

요구사항을 테이블로 바꾸기 전에 무엇을 모델링할지 명확히 하세요. 좋은 스키마는 빈 페이지에서 시작하지 않습니다—사람들이 실제로 하는 일과 나중에 필요할 증거(화면, 출력물, 엣지 케이스)에서 시작합니다.

한곳에 모아둘 전형적 입력들

사용자 스토리는 핵심이지만 그 자체만으로는 부족합니다. 다음을 모으세요:

  • 사용자 스토리 + 역할(누가 무엇을 왜 하는지)
  • 수용 기준(반드시 만족해야 할 규칙)
  • 폼/화면(사용자가 입력·선택·보는 필드)
  • 리포트/내보내기(요약, 그룹화, 필터링 필요 항목)
  • 실제 예시(샘플 주문, 송장, 티켓, 캘린더 등 대표 자료)

AI를 쓴다면 이런 입력들이 모델을 현실에 맞게 고정시켜 줍니다. AI는 엔티티와 필드를 빠르게 제안할 수 있지만, 제품에 맞지 않는 구조를 발명하지 않도록 실제 아티팩트가 필요합니다.

수용 기준: 제약의 숨은 원천

수용 기준에는 데이터 규칙이 가장 많이 숨어 있는 경우가 많습니다. 데이터로 명시하지 않아도 다음과 같은 문장을 찾으세요:

  • “이메일은 고유해야 한다” (고유성)
  • “상태는 Draft, Submitted, Approved가 될 수 있다” (허용값)
  • “관리자만 승인할 수 있다” (권한, 감사 필드 필요 가능성)
  • “결제가 있는 송장은 삭제할 수 없다” (참조 무결성 규칙)

초기에 고치기 쉬운 흔한 함정

모호한 스토리(“사용자는 프로젝트를 관리할 수 있다”)는 여러 엔티티와 워크플로를 숨깁니다. 또 흔한 누락은 취소, 재시도, 부분 환불, 재할당 같은 엣지 케이스입니다.

스토리 품질 빠른 체크리스트(모델링 전)

  • 행위자/역할이 명확한가?
  • 객체가 구체적인가(“데이터”나 “항목”이 아님)?
  • 적어도 하나의 실제 예시가 있는가?
  • 수용 기준에 검증과 경계가 포함되어 있는가?
  • 오류 및 ‘만약’ 케이스가 언급되었거나(또는 명시적으로 연기되었는가)?

1단계 — 스토리에서 엔티티 추출하기(명사 찾기)

테이블이나 다이어그램을 생각하기 전에 사용자 스토리를 읽고 명사를 강조하세요. 요구사항 글쓰기에서 명사는 시스템이 기억해야 할 ‘사물’을 가리키며, 종종 스키마의 엔티티가 됩니다.

간단한 정신 모델: 명사는 엔티티가 되고, 동사는 액션이나 워크플로가 된다. 예: “매니저가 기술자에게 작업을 할당한다”면 엔티티 후보는 manager, technician, job이고, “할당”은 나중에 모델링할 관계를 암시합니다.

어떤 명사가 진짜 엔티티인지 구분하는 법

모든 명사가 테이블이 될 필요는 없습니다. 명사가 엔티티 후보일 때:

  • 자기 식별성이 있어야 함: 특정 인스턴스를 가리킬 수 있어야 함(예: Job #1042, Customer A)
  • 시간에 따라 변한다: 라이프사이클이 있음(예: 작업이 scheduled → completed로 이동)
  • 여러 곳에서 사용된다: 여러 스토리가 참조하거나 여러 워크플로가 관여함

한 번만 등장하거나 다른 것을 설명하는 용어(예: “빨간 버튼”, “금요일”)이면 엔티티가 아닐 수 있습니다.

속성 vs 별도 엔티티(“주소”와 “태그” 테스트)

모든 세부사항을 테이블로 만드는 것은 흔한 실수입니다. 간단한 규칙:

  • 하나의 값으로 어떤 것을 설명하면 보통 속성(예: Customer.phone_number)
  • 반복되거나 공유되거나 구조화되어 있으면 별도 엔티티

두 가지 예시:

  • Address: 배송/청구 주소를 저장하고 이력을 유지하거나, 여러 고객/장소에서 재사용하면 Address는 별도 엔티티. 단일 우편 주소만 필요하고 재사용하지 않는다면 속성으로 둬도 됨.
  • Tag: 태그는 거의 항상 별도 엔티티(많대다 관계—하나의 Job에 여러 태그, 하나의 Tag가 여러 Job에 적용)입니다.

AI를 사용해 후보 엔티티 제안받기(주의해서)

AI는 스토리를 스캔해 후보 명사를 테마별로 묶어 초안 목록을 빠르게 만들 수 있습니다. 유용한 프롬프트 예: “저장해야 할 명사들을 추출하고 중복/동의어를 그룹핑해줘.”

AI 출력은 시작점으로 보고 다음 같은 후속 질문을 하세요:

  • “이 중 어느 것들이 라이프사이클이나 자체 ID가 필요한가?”
  • “어떤 것들이 실제로 상태, 카테고리, 속성인가?”
  • “동의어는 없는가(예: ‘client’ vs ‘customer’)?”

1단계의 목표는 각 엔티티를 실제 스토리에 근거해 방어할 수 있는 짧고 깔끔한 목록을 만드는 것입니다.

2단계 — 세부사항을 필드로 바꾸기(반드시 저장해야 할 것들)

엔티티(예: Order, Customer, Ticket)를 정했다면 다음은 나중에 필요할 세부사항을 캡처하는 것입니다. 데이터베이스에서 이 세부사항은 필드(혹은 속성)입니다—시스템이 잊으면 안 될 알림들입니다.

추측하지 않고 필드를 고르는 방법

사용자 스토리로 시작하고 수용 기준을 체크리스트처럼 읽으세요.

요구사항에 “사용자가 배송일로 주문을 필터링할 수 있다”고 쓰여 있으면 delivery_date는 필수입니다(다른 저장 데이터에서 신뢰성 있게 유도되지 않는 한). “누가 요청을 승인했는지와 언제인지 보여줘”라면 보통 approved_by, approved_at이 필요합니다.

실용적 테스트: 이 값이 화면에 표시되거나, 검색/정렬/감사/계산에 필요할까? 그렇다면 필드일 가능성이 큽니다.

깔끔한 필드를 위한 간단한 규칙

  • 값을 원자적으로 유지: ‘이름’과 ‘성’을 별도로 저장하면 검색/정렬에 유리합니다. 여러 값을 하나의 필드에 묶지 마세요(예: “red, blue”).
  • 일관된 타입 사용: 날짜는 날짜, 금액은 소수(decimal), 불리언은 true/false—형식 혼합(“$10”, “10 USD”, “10”)을 피하세요.
  • 중복 텍스트 피하기: 고객 주소를 주문마다 복사하지 마세요. 적절한 곳에 한 번 저장하고 참조하세요.

통제된 어휘: 상태, 타입, 카테고리

많은 스토리에 “상태”, “타입”, “우선순위” 같은 단어가 나옵니다. 이를 통제된 어휘로 처리하세요—허용값이 제한된 세트입니다.

세트가 작고 안정적이면 enum 스타일 필드로 충분합니다. 늘어나거나 라벨/권한 관리가 필요하면 별도 조회 테이블(예: status_codes)을 만들어 참조를 저장하세요.

이렇게 스토리가 신뢰할 수 있는 필드로 바뀝니다—검색 가능하고 리포트에 쓸 수 있으며 입력 오류를 줄입니다.

3단계 — 엔티티들을 관계로 연결하기

엔티티(예: User, Order, Invoice, Comment)와 필드를 나열했으면, 이제 이를 연결하세요. 관계는 스토리가 암시하는 “사물들이 어떻게 상호작용하는지” 레이어입니다.

세 가지 관계 형태(일반 언어)

일대일(1:1): “하나의 것이 정확히 하나의 다른 것을 가진다.”

  • 스토리 문구: “각 사용자마다 하나의 프로필이 있다.”
  • 모델 아이디어: User ↔ Profile (특별한 이유가 없다면 병합 가능)

일대다(1:N): “하나가 여러 개를 가질 수 있다.” (가장 흔함)

  • 스토리 문구: “사용자는 여러 주문을 가질 수 있다.”
  • 모델 아이디어: User → Order (Order에 user_id 저장)

다대다(M:N): “많은 것들이 많은 것들과 연결된다.” 추가 테이블이 필요함.

  • 스토리 문구: “주문은 여러 제품을 포함하고, 제품은 여러 주문에 포함될 수 있다.”

다대다: 조인 테이블 트릭

데이터베이스에 Order 안에 “제품 ID 목록”을 넣으면 나중에 검색/업데이트/리포팅에서 문제가 됩니다. 대신 관계 자체를 나타내는 조인 테이블을 만드세요.

예:

  • Order
  • Product
  • OrderItem(조인 테이블)

OrderItem은 보통 다음을 포함합니다:

  • order_id
  • product_id
  • 스토리에서 온 추가 세부사항(예: quantity, unit_price, discount)

스토리의 세부사항(예: 수량)은 개체 어느 쪽에도 속하지 않고 관계 위에 놓이는 경우가 많다는 점을 주목하세요.

필수 vs 선택(전문 용어 없이)

스토리는 연결이 반드시 있어야 하는지 아니면 없을 수도 있는지 알려줍니다.

  • “주문은 반드시 사용자에 속해야 한다” → 모든 Order에 user_id가 필요(비어있으면 안 됨)
  • “사용자에게 전화번호가 있을 수 있다” → phone은 널 허용
  • “상품이 물리적일 경우 주문에 배송 주소가 있을 수 있다” → 디지털 상품의 경우 shipping_address_id는 비어있을 수 있음

간단 체크: 스토리가 레코드를 생성할 때 그 링크 없이는 만들 수 없다고 암시하면 필수로 처리하세요. “할 수 있다(may)”나 예외가 있으면 선택으로 처리하세요.

스토리 문장을 관계 문장으로 바꾸기

스토리를 읽을 때 간단한 페어링으로 다시 작성하세요:

  • “사용자는 여러 코멘트를 남길 수 있다” → User 1:N Comment
  • “코멘트는 하나의 사용자에 속한다” → Comment N:1 User

스토리에 있는 모든 상호작용에 대해 이렇게 하세요. 끝날 즈음에는 ER 다이어그램 도구를 열기 전에 실제 업무 흐름에 맞는 연결된 모델이 완성되어 있을 것입니다.

4단계 — 워크플로로 상태, 이벤트, 누락을 찾아내기

두려움 없이 스키마 변경
테이블과 제약을 조정할 때 스냅샷과 롤백으로 안전하게 반복하세요.
지금 시도

사용자 스토리는 사람들이 무엇을 원하는지 알려주고, 워크플로는 일이 어떻게 진행되는지 단계별로 보여줍니다. 워크플로를 데이터로 옮기면 “저장하는 것을 잊었다”는 문제를 빌드 전에 빨리 발견할 수 있습니다.

간단한 워크플로부터 시작하세요

워크플로를 액션과 상태 변화의 순서로 적으세요. 예:

  • 생성 → Draft
  • 제출 → Submitted
  • 매니저 검토 → Approved 또는 Rejected
  • 승인 시 작업 예약 → In progress
  • 완료 → Done

굵은 단어들은 보통 status 필드(또는 작은 상태 테이블)가 되며, 허용값이 명확히 정의됩니다.

워크플로는 누락된 필드를 드러냄

각 단계를 걸어가며 “나중에 무엇을 알아야 할까?”를 묻습니다. 워크플로는 보통 다음과 같은 필드를 요구합니다:

  • 타임스탬프: submitted_at, approved_at, completed_at
  • 소유권: created_by, assigned_to, approved_by
  • 사유/맥락: rejection_reason, approval_note
  • 순서: 다단계 프로세스의 sequence

대기, 에스컬레이션, 전달이 포함되면 보통 최소 하나의 타임스탬프와 ‘현재 누가 담당인지’ 필드가 필요합니다.

워크플로는 누락된 테이블을 드러내기도 함

일부 워크플로 단계는 필드 이상의 데이터 구조를 요구합니다:

  • 감사 로그/히스토리: “누가 언제 상태를 바꿨는지”
  • 승인 기록: 다중 승인자나 조건부 승인 규칙
  • 첨부파일: 단계 중 파일 업로드가 있는 경우
  • 코멘트: 프로세스에 논의가 포함될 때

AI를 사용한 갭 체크

AI에 (1) 사용자 스토리와 수용 기준, (2) 워크플로 단계를 모두 주고 각 단계에 필요한 데이터를 나열하게 하세요(상태, 행위자, 타임스탬프, 출력물). 그런 다음 현재 필드/테이블로 지원할 수 없는 요구사항을 하이라이트하게 하세요.

Koder.ai 같은 플랫폼에서는 이 ‘갭 체크’가 특히 실용적인데, 스키마 가정을 조정하고 스캐폴딩을 재생성하며 장황한 수동 작업 없이 빠르게 반복할 수 있기 때문입니다.

키, 고유성, 기본 제약(전문 용어 없이)

사용자 스토리를 테이블로 바꿀 때 단순히 필드를 나열하는 것이 아니라 시간이 지나도 데이터를 식별하고 일관성을 유지하는 방법을 결정하는 것입니다.

기본키: 각 행의 안정적 ‘신분증’

**기본키(primary key)**는 하나의 레코드를 고유하게 식별합니다—행의 영구 신분증이라 생각하세요.

이유: 스토리는 업데이트, 참조, 이력을 암시합니다. 예: “지원 담당자가 주문을 보고 환불을 발행할 수 있다”면 주문을 가리킬 수 있는 안정적 방법이 필요합니다.

보통 내부 id(숫자 또는 UUID)를 사용해 절대 바뀌지 않게 합니다.

외래키: 테이블 간의 안전한 포인터

**외래키(foreign key)**는 한 테이블이 다른 테이블을 안전하게 가리키는 방법입니다. 예: orders.customer_id가 customers.id를 참조하면 데이터베이스가 모든 주문이 실제 고객에 속하도록 강제할 수 있습니다.

스토리 예: “사용자는 자신의 송장을 볼 수 있다” → 송장은 떠다니는 것이 아니라 고객에 붙어 있어야 합니다(종종 주문이나 구독에도 붙음).

고유성 규칙: “고유해야 한다”를 강제하기

사용자 스토리에는 종종 숨겨진 고유성 요구가 있습니다:

  • “사용자는 이메일로 가입한다” → 이메일 고유 강제(멀티테넌트라면 테넌트별 고유)
  • “재무는 송장 번호로 검색한다” → invoice_number 고유

이 규칙들은 혼란스러운 중복을 예방합니다.

인덱싱(개념적): 자주 쓰는 조회를 빠르게

인덱스는 “이메일로 고객 찾기”나 “고객별 주문 나열” 같은 검색을 빠르게 합니다. 먼저 가장 흔한 쿼리와 고유성 규칙에 맞춘 인덱스를 추가하세요.

나중에 최적화할 것: 드물게 쓰는 리포트나 추측성 필터에 대한 과도한 인덱싱은 미루세요. 시연 환경의 느린 쿼리 증거를 기반으로 최적화하세요.

데이터 일관성 유지: 실용적 정규화 체크리스트

모델을 먼저 설계
코드를 생성하기 전에 엔티티, 워크플로, 제약을 Koder.ai에서 먼저 매핑하세요.
계획하기

정규화의 목표는 상충하는 중복을 방지하는 것입니다. 같은 사실이 두 곳에 저장되면 언젠가 불일치가 발생합니다(이름의 두 철자, 두 가격, 두 개의 ‘현재’ 주소). 정규화된 스키마는 각 사실을 한 번만 저장하고 참조합니다.

초안 스키마에서 실행할 수 있는 빠른 체크리스트

1) 반복 그룹 찾기

Phone1, Phone2, Phone3 또는 ItemA, ItemB, ItemC 같은 패턴이 보이면 별도 테이블(e.g., CustomerPhones, OrderItems)로 분리하세요. 반복 그룹은 검색·검증·확장이 어렵게 만듭니다.

2) 동일한 이름/세부사항을 여러 테이블에 복사하지 마세요

CustomerName이 Orders, Invoices, Shipments에 복사되어 있다면 진실의 원천이 여러 개입니다. 고객 세부는 Customers에 두고 다른 곳에는 customer_id만 저장하세요.

3) 동일한 것을 위한 여러 열 피하기

billing_address, shipping_address, home_address 같은 여러 컬럼은 진짜로 다른 개념이면 괜찮습니다. 그러나 여러 주소를 모델링하려면 Addresses 테이블과 type 필드를 사용하세요.

4) 조회값과 자유 텍스트 분리하기

사용자가 알려진 목록에서 선택하면(상태, 카테고리, 역할) 일관되게 enum이나 조회 테이블로 모델링하세요. “Pending” vs “pending” vs “PENDING” 같은 문제를 방지합니다.

5) 모든 비-ID 필드가 올바른 것에 의존하는지 확인

테이블에서 어떤 컬럼이 테이블의 주된 엔티티를 설명하지 않는다면 다른 곳으로 옮겨야 합니다. 예: Orders에 product_price가 있으면 “주문 시점의 가격”이라는 역사적 스냅샷인지 확인하세요.

비정규화를 허용해도 되는 경우(나중에 결정)

의도적으로 복제할 때가 있습니다:

  • 리포팅/성능: 미리 집계된 합계나 요약 테이블
  • 캐싱: 계산 비용이 큰 값을 저장
  • 감사/이력: 구매 시점의 이름 등 과거 현실을 보존하기 위한 복사

핵심은 의도적으로 하는 것임을 문서화하고, 어떤 필드가 출처인지와 복사 업데이트 방식을 기록하는 것입니다.

AI가 도와주는 부분과 사람이 결정하는 부분

AI는 의심스러운 중복(반복 컬럼, 유사 필드명, 일관성 없는 상태 필드)을 찾아 분할을 제안할 수 있습니다. 하지만 단순성·유연성·성능 간의 트레이드오프는 사람이 제품 사용 방식을 기반으로 결정해야 합니다.

저장된 값 vs 계산값: 무엇을 DB에 둘까

유용한 규칙: 나중에 재생성할 수 없는 사실은 저장하고, 나머지는 계산하세요.

저장 대 계산(파생) 데이터

저장 데이터는 진실의 출처입니다: 개별 라인 아이템, 타임스탬프, 상태 변경, 누가 무엇을 했는지. 계산(파생) 데이터는 그 사실들에서 만들어지는 값들: 합계, 카운터, is overdue 같은 플래그, 현재 재고 같은 롤업입니다.

같은 입력에서 두 값을 계산할 수 있다면 사실(facts)을 저장하고 나머지를 계산하는 것이 안전합니다. 그렇지 않으면 모순이 생깁니다.

파생값 저장이 불일치를 초래하는 이유

파생값은 입력이 바뀔 때마다 달라집니다. 입력과 결과를 둘 다 저장하면 워크플로와 엣지 케이스(수정, 환불, 부분 배송, 소급 변경) 전반에서 동기화를 유지해야 합니다. 하나라도 누락되면 데이터가 서로 다른 이야기를 하게 됩니다.

예: order_items와 함께 order_total을 저장하면 수량을 바꾸거나 할인을 적용했을 때 총액이 완벽히 업데이트되지 않을 위험이 있습니다.

워크플로로 무엇을 저장해야 할지 결정하기(이력과 스냅샷)

워크플로는 언제 역사적 진실을 저장해야 하는지 알려줍니다. 사용자가 ‘그 당시의 값’을 알아야 한다면 스냅샷을 저장하세요.

주문 예시:

  • 라인 아이템과 가격(사실)
  • 체크아웃 시점의 캡처된 order_total(스냅샷)—세금, 할인, 가격 규칙이 나중에 바뀔 수 있기 때문

재고는 보통 입고·판매·조정 같은 이동으로부터 계산합니다. 감사가 필요하면 이동을 저장하고 리포팅 성능을 위해 주기적 스냅샷을 선택적으로 저장합니다.

로그인 트래킹의 경우 last_login_at을 이벤트 타임스탬프로 저장하세요. “지난 30일간 활성인가?” 같은 것은 계산으로 유지하세요.

실전 예: 5개의 사용자 스토리에서 ER 모델로

지원 티켓 앱을 예로 들어 5개의 사용자 스토리에서 간단한 ER 모델(엔티티 + 필드 + 관계)로 가고, 하나의 워크플로로 검증해보겠습니다.

5개 스토리 → 명사 → 엔티티

  1. 고객은 제목, 설명, 카테고리를 포함한 지원 티켓을 생성할 수 있다.
  2. 상담원은 티켓을 자신이나 다른 상담원에게 할당할 수 있다.
  3. 상담원은 티켓에 내부 노트와 공개 회신을 추가할 수 있다.
  4. 고객은 티켓이 업데이트되거나 닫혔을 때 이를 볼 수 있다.
  5. 매니저는 티켓이 얼마나 오래 열린 상태였는지와 누가 닫았는지 추적할 수 있다.

이 명사들에서 핵심 엔티티를 도출합니다:

  • User(고객, 상담원, 매니저)
  • Ticket
  • Message(공개 회신 + 내부 노트)
  • Category
  • TicketEvent(감사/이력)

필드와 관계(간단한 ER 모델)

  • User: id, name, email, role
  • Category: id, name
  • Ticket: id, subject, description, status, created_at, updated_at, closed_at
    • 관계: Ticket.category_id → Category.id
    • 관계: Ticket.requester_id → User.id (고객)
    • 관계: Ticket.assignee_id → User.id (상담원, nullable)
  • Message: id, ticket_id, author_id, body, is_internal, created_at
    • 관계: Message.ticket_id → Ticket.id
    • 관계: Message.author_id → User.id
  • TicketEvent: id, ticket_id, actor_id, type, from_status, to_status, created_at

워크플로 매핑: 생성 → 업데이트 → 닫기

  • 생성: Ticket 삽입(status = “open”, created_at), TicketEvent(type = “created”) 삽입
  • 업데이트(할당, 회신): Message 삽입 또는 Ticket.assignee_id 업데이트, TicketEvent(type = “assigned”/“replied”, updated_at) 삽입
  • 닫기: Ticket.status = “closed” 업데이트, closed_at 설정, TicketEvent(type = “closed”, actor_id = closer) 삽입

‘전/후’: AI가 누락된 제약을 잡아냄

전(흔한 누락): Ticket에 assignee_id는 있지만, 오직 상담원만 할당될 수 있다는 규칙을 보장하지 않음.

후(수정): AI가 이를 지적하고 실제 규칙을 추가합니다: assignee는 role = “agent”인 User여야 한다 (애플리케이션 검증이나 DB 제약/정책으로 구현). 이렇게 하면 나중에 “고객에게 할당됨” 같은 잘못된 데이터로 리포트가 깨지는 것을 막습니다.

스키마 검증: 모든 스토리를 추적 가능해야 함

실제 미리보기 공유
공유할 준비가 되면 자체 커스텀 도메인으로 실제 앱을 배포하세요.
도메인 설정

스키마는 모든 사용자 스토리를 데이터로 신뢰성 있게 답할 수 있을 때만 ‘완료’입니다. 가장 단순한 검증은 각 스토리를 집어들고 묻는 것입니다: “이 데이터베이스로 이 질문에 항상 답할 수 있는가?” 가능성이 “아마도”라면 모델에 누락이 있는 것입니다.

각 스토리를 데이터베이스 질문으로 바꾸기

모든 사용자 스토리를 리포트나 API가 묻는 질문으로 바꿔보세요. 예:

  • 리포트: “지난 30일간 고객별 오픈 주문과 합계를 보여줘.”
  • 권한: “이 상점에서 환불을 승인할 수 있는 사용자는 누구인가?”
  • 엣지 케이스: “배송 주소 없이 주문이 존재할 수 있나? 디지털 아이템은?”
  • 삭제: “고객을 삭제하면 주문, 송장, 노트는 어떻게 되는가?”

스토리를 명확한 질문으로 표현할 수 없으면 그 스토리가 불명확한 것입니다. 표현은 할 수 있는데 스키마로 답할 수 없다면 필드, 관계, 상태/이벤트, 제약 중 하나가 부족한 것입니다.

샘플 데이터로 빠른 정합성 검사하기

핵심 테이블마다 5–20행 정도의 작은 데이터셋을 만들고 정상 케이스와 까다로운 케이스(중복, 누락값, 취소)를 포함하세요. 그 데이터로 스토리를 시뮬레이션하면 “어떤 주소가 사용되었는지 알 수 없다”거나 “누가 변경을 승인했는지 저장할 곳이 없다” 같은 문제를 빨리 발견할 수 있습니다.

AI를 사용해 처리되지 않은 케이스 찾기

AI에 각 스토리별 검증 질문(엣지 케이스, 삭제 시나리오 포함)을 생성하게 하고, 그 질문에 답하려면 어떤 데이터가 필요한지 나열하게 하세요. 그 목록을 스키마와 비교하면 불일치가 구체적 작업 항목으로 드러납니다.

AI를 안전하게 사용하고 스키마를 유지보수 가능하게 유지하기

AI는 데이터 모델링을 빠르게 해주지만, 민감 정보 유출이나 잘못된 가정 하드코딩 위험을 키울 수 있습니다. 매우 빠른 어시스턴트로 취급하되 가드레일을 두세요.

AI에 무엇을 공유하고 무엇을 피할지

모델링에 충분히 현실적이되 안전하게 익명화된 입력을 공유하세요:

  • 익명화된 사용자 스토리(고객명, 제품명, 위치 이름 변경)
  • 수용 기준 및 엣지 케이스(예: “14일 이내 환불”)
  • 예시 필드와 가짜 데이터(예: invoice_total: 129.50, status: "paid")
  • 현재 CSV 헤더/기존 테이블 구조(구조는 보통 안전함; 실제 콘텐츠는 위험할 수 있음)

피해야 할 것:

  • 실명, 이메일, 전화번호, 주소 등 개인식별정보(PII)
  • 실제 주문 이력, 지원 티켓, 내부 메모
  • API 키, DB 자격증명, 민감한 스크린샷

현실감 있는 데이터가 필요하면 형식을 맞춘 합성 샘플을 생성하세요—프로덕션 행을 복사하지 마세요.

스키마 옆에 가정 명시하기

스키마는 보통 “모두가 다르게 가정했다”는 문제로 실패합니다. ER 모델 옆(또는 같은 저장소)에 짧은 결정 로그를 남기세요:

  • 정의(예: “활성 계정이란 무엇인가?”)
  • 제약(예: “사용자는 여러 조직에 속할 수 있다”)
  • 트레이드오프(예: “감사 목적상 송장에 통화 코드 저장”)

이렇게 하면 AI 출력이 일회성 산출물이 아니라 팀 지식이 됩니다.

변화에 대비하기: 버전 관리와 마이그레이션

스키마는 스토리와 함께 진화합니다. 다음을 지키세요:

  • 스키마 변경 버전 관리(Git의 마이그레이션 파일)
  • 가능하면 되돌릴 수 있는 마이그레이션 작성
  • 시드와 샘플 쿼리 업데이트로 변경사항 테스트 가능하게 하기
  • AI가 생성한 마이그레이션도 코드 리뷰처럼 검토

Koder.ai 같은 플랫폼을 쓰면 스키마 변경 시 스냅샷과 롤백 같은 가드레일을 활용하고, 깊은 커스터마이징이 필요할 때 소스 코드를 내보내어 전통적 검토 과정을 밟을 수 있습니다.

반복 가능한 간단한 작업 흐름

  1. 스토리 익명화 + 5–10개의 합성 예시 생성
  2. AI에 엔티티, 필드, 관계, 제약 제안 요청
  3. 팀 리뷰; 가정 기록
  4. 마이그레이션 구현; 작은 ‘스토리 추적’ 테스트 실행(각 스토리가 모델로 만족되는지 확인)
  5. 스토리가 바뀔 때 반복; 스키마와 노트 동기화 유지

자주 묻는 질문

사용자 스토리에서 데이터베이스 엔티티를 어떻게 추출하나요?

스토리를 읽고 시스템이 기억해야 할 ‘사물’을 나타내는 명사를 강조하세요(예: Ticket, User, Category).

명사를 엔티티로 올릴 기준은:

  • 고유 ID가 필요할 때
  • 시간이 지나며 상태가 변할 때(라이프사이클이 있을 때)
  • 여러 스토리에서 참조될 때

각 엔티티는 특정 스토리 문장을 들어 정당화할 수 있는 짧은 목록으로 유지하세요.

언제 어떤 것을 필드로 하고 언제 별도 테이블로 만들어야 하나요?

“속성 vs 엔티티” 테스트를 사용하세요:

  • 한 레코드를 설명하는 단일 값이라면 필드로 둡니다(예: customer.phone_number).
  • 반복되거나 공유되거나 구조화되어 있거나 이력 관리가 필요하면 별도 테이블로 만듭니다(예: 여러 주소, 태그, 첨부파일).

빠른 단서: 언젠가 “여러 개”가 필요하다면 별도 테이블이 필요할 가능성이 큽니다.

수용 기준은 필드와 제약으로 어떻게 번역되나요?

수용 기준을 저장 체크리스트로 취급하세요. 필터/정렬/표시/감사에 필요하다고 명시되면, 그것은 저장되어야 하거나(또는 신뢰할 수 있게 파생되어야) 합니다.

예:

  • “누가 승인했는지와 언제인지 보여줘야 함” → approved_by, approved_at
  • “배송일로 필터링” → delivery_date
  • “이메일은 고유해야 함” → email에 고유 제약/인덱스
스토리 텍스트를 테이블 관계(1:1, 1:N, M:N)로 어떻게 바꾸나요?

스토리 문장을 관계 문장으로 다시 써보세요:

  • “고객은 여러 주문을 가질 수 있다” → 1:N (customer_id를 orders에 둠)
  • “주문은 여러 제품을 포함한다” → M:N (조인 테이블 order_items 추가)

관계 자체에 데이터(수량, 가격, 역할 등)가 있다면 그 데이터는 조인 테이블에 둡니다.

Many-to-many 관계를 올바르게 모델링하는 방법은?

M:N은 두 외래키와 관계 고유의 필드를 저장하는 조인 테이블로 모델링하세요.

전형적 패턴:

  • orders
  • products
  • (, , , )
워크플로가 누락된 테이블이나 필드를 어떻게 찾아주나요?

워크플로를 단계별로 따라가며 “이 일이 나중에 발생했음을 증명하려면 무엇이 필요할까?”를 물어보세요.

일반적으로 추가되는 항목:

  • 타임스탬프: submitted_at, closed_at
  • 행위자: created_by, ,
우선 추가해야 할 제약(키, 고유성, 인덱스)은 무엇인가요?

우선 다음을 추가하세요:

  • 각 테이블의 안정적인 기본키(id)
  • 관계를 위한 외래키(orders.customer_id → customers.id)
  • 요구사항에서 나온 고유성 규칙(이메일, 송장번호)

그다음 자주 사용하는 조회를 위한 인덱스(예: , , )를 추가하세요. 추측성 인덱싱은 실제 쿼리 패턴을 보고 미루는 것이 안전합니다.

스키마가 적절히 정규화되었는지 어떻게 알 수 있나요?

간단한 일관성 검사:

  • Phone1/Phone2 같은 반복 그룹이 보이면 자식 테이블로 분리하세요.
  • 동일 사실이 여러 테이블에 복제되어 있으면 하나의 출처를 정하고 참조하세요.
  • 테이블의 주된 엔티티가 아닌 것을 설명하는 컬럼은 다른 곳으로 옮기세요.

성능·리포팅·스냅샷 같은 명확한 이유가 있을 때만 의도적으로 비정규화하고, 어떤 필드가 진실의 근거인지 문서화하세요.

데이터베이스에 무엇을 저장하고 무엇을 계산해야 하나요?

나중에 재생성할 수 없는 사실은 저장하고, 그렇지 않으면 계산하세요.

저장에 적합:

  • 이벤트와 타임스탬프
  • 라인 아이템과 과거 가격
  • 감사용 ‘누가 무엇을 했나’

계산에 적합:

  • 라인아이템에서 유도되는 합계
  • 날짜로 계산되는 is overdue 같은 플래그

같은 파생값을 저장한다면 어느 시점에 어떻게 동기화할지 명확히 정하고 엣지 케이스를 테스트하세요.

스키마 설계를 빠르게 하기 위해 AI를 안전하게 어떻게 사용하나요?

초안 생성용으로 AI를 활용하고, 결과는 항상 실물 아티팩트와 대조하세요.

실무 프롬프트 예시:

  • “이 스토리들에서 후보 엔티티와 동의어를 추출해줘.”
  • “수용 기준이 암시하는 필드(타임스탬프, 행위자, 상태)를 나열해줘.”
  • “이 워크플로에서 각 단계에 어떤 데이터가 필요한지 알려줘.”

가드레일:

  • 입력은 익명화(PII 제거)해서 제공
  • 스키마 옆에 가정(assumptions) 로그를 남기기
목차
실제 업무에 맞는 스키마를 만드는 이유입력 자료: 사용자 스토리, 수용 기준, 실제 예시1단계 — 스토리에서 엔티티 추출하기(명사 찾기)2단계 — 세부사항을 필드로 바꾸기(반드시 저장해야 할 것들)3단계 — 엔티티들을 관계로 연결하기4단계 — 워크플로로 상태, 이벤트, 누락을 찾아내기키, 고유성, 기본 제약(전문 용어 없이)데이터 일관성 유지: 실용적 정규화 체크리스트저장된 값 vs 계산값: 무엇을 DB에 둘까실전 예: 5개의 사용자 스토리에서 ER 모델로스키마 검증: 모든 스토리를 추적 가능해야 함AI를 안전하게 사용하고 스키마를 유지보수 가능하게 유지하기자주 묻는 질문
공유
Koder.ai
Koder로 나만의 앱을 만들어 보세요 지금!

Koder의 힘을 이해하는 가장 좋은 방법은 직접 체험하는 것입니다.

무료로 시작데모 예약
order_items
order_id
product_id
quantity
unit_price

하나의 컬럼에 ID 목록을 넣지 마세요—조회, 업데이트, 무결성 검증이 어려워집니다.

assigned_to
closed_by
  • 사유/메모: rejection_reason
  • “누가 언제 상태를 바꿨나”를 추적해야 하면 단일 필드를 덮어쓰지 말고 이벤트/감사 테이블을 추가하세요.

    email
    customer_id
    status + created_at
    order_total
  • AI가 생성한 마이그레이션도 일반 코드처럼 리뷰하고 Git으로 버전 관리하기