TIL - 20260813

juni·2026년 8월 13일

TIL

목록 보기
430/468

0813 데이터베이스 실무 심화 (1/N): PostgreSQL 기본 구조, 테이블과 관계 설계


✅ 1. 데이터베이스를 왜 깊게 봐야 하는가?

  • 웹서비스에서 데이터베이스는 단순히 데이터를 저장하는 공간이 아닙니다.
  • 고객 상담 신청, 상품 정보, 요금제, 주문, 상태 변경, 관리자 계정, 유입 분석, 알림 발송 이력 등 서비스의 핵심 기록이 모두 데이터베이스에 쌓입니다.
  • 화면과 API는 고칠 수 있지만, 데이터가 꼬이면 복구가 훨씬 어렵습니다.
프론트엔드:
사용자가 보는 화면

백엔드:
비즈니스 로직 처리

데이터베이스:
서비스의 실제 기록과 상태를 보관

➕ 1-1. DB 설계가 중요한 이유

상담 데이터가 중복 저장될 수 있음
상태 변경 이력이 사라질 수 있음
관리자 검색이 느려질 수 있음
엑셀 다운로드가 무거워질 수 있음
유입 분석이 부정확해질 수 있음
DB migration이 운영 장애로 이어질 수 있음
  • 실무에서 DB는 나중에 대충 고치기 어렵습니다.
  • 처음부터 완벽할 필요는 없지만, 최소한 “데이터가 어떤 기준으로 저장되고 연결되는지”는 명확해야 합니다.

✅ 2. PostgreSQL이란 무엇인가?

  • PostgreSQL은 오픈소스 관계형 데이터베이스입니다.
  • 안정성, SQL 표준 지원, 트랜잭션, 인덱스, JSON, 확장성 면에서 실무에서 많이 사용됩니다.
  • Prisma, TypeORM, Drizzle 같은 ORM과도 함께 사용할 수 있습니다.
PostgreSQL:
관계형 데이터베이스

주요 특징:
테이블
관계
SQL
트랜잭션
인덱스
제약조건

➕ 2-1. PostgreSQL을 쓰는 이유

  • 데이터 정합성을 지키기 좋습니다.
  • 복잡한 검색과 필터를 처리할 수 있습니다.
  • 트랜잭션으로 여러 작업을 안전하게 묶을 수 있습니다.
  • 인덱스를 활용해 관리자 목록 조회 성능을 개선할 수 있습니다.
  • 운영에서 백업, 복구, 권한 관리, 모니터링 체계가 잘 잡혀 있습니다.
상담 신청:
중복 방지, 상태 관리

관리자 목록:
검색, 필터, 정렬, 페이지네이션

주문 관리:
상태 변경, 이력 관리

유입 분석:
주문과 방문자/광고 데이터 연결

✅ 3. 관계형 데이터베이스란 무엇인가?

  • 관계형 데이터베이스는 데이터를 테이블 형태로 저장하고, 테이블 사이의 관계를 통해 데이터를 연결합니다.
  • 예를 들어 고객 상담 신청은 상품과 연결될 수 있고, 상담 상태 변경은 관리자와 연결될 수 있습니다.
Product
  ↓
Consult
  ↓
ConsultStatusHistory
  ↓
AdminUser

➕ 3-1. 관계형 DB의 핵심 개념

개념의미예시
Table데이터 묶음products, consults
Row한 건의 데이터상담 신청 1건
Column데이터 속성이름, 전화번호, 상태
Primary Key고유 식별자id
Foreign Key다른 테이블 참조productId
Constraint데이터 규칙unique, not null
Index검색 성능 개선전화번호, 상태, 생성일
  • 관계형 DB의 핵심은 “중복을 줄이고, 관계를 명확히 하고, 데이터 규칙을 DB 수준에서도 지키는 것”입니다.

✅ 4. 테이블이란 무엇인가?

  • 테이블은 같은 종류의 데이터를 저장하는 구조입니다.
  • 예를 들어 상품 데이터는 products, 상담 신청 데이터는 consults, 관리자 계정은 admin_users 테이블에 저장할 수 있습니다.
products
  - id
  - name
  - carrier
  - price
  - isActive
  - createdAt

consults
  - id
  - name
  - phone
  - productId
  - status
  - createdAt

➕ 4-1. 좋은 테이블 이름

products
consults
orders
admin_users
consult_status_histories
visit_logs
lead_sources

➕ 4-2. 나쁜 테이블 이름

data
list
info
table1
user_data
temp
new_data
  • 테이블 이름만 봐도 어떤 데이터가 들어있는지 알아야 합니다.
  • data, info, temp 같은 이름은 시간이 지나면 의미를 잃습니다.

✅ 5. Row와 Column

  • Row는 테이블 안의 한 건의 데이터입니다.
  • Column은 각 데이터가 가진 속성입니다.

➕ 5-1. 상담 신청 테이블 예시

idnamephoneproductIdstatuscreatedAt
1김고객010****123410NEW2026-08-13
2이고객010****567812CALLED2026-08-13
Row:
상담 신청 1건

Column:
name, phone, productId, status, createdAt

➕ 5-2. Column 설계 기준

이 값이 반드시 필요한가?
빈 값이 가능해야 하는가?
중복되면 안 되는가?
검색 조건으로 자주 쓰이는가?
나중에 변경될 수 있는가?
개인정보인가?
이력을 남겨야 하는가?
  • Column은 단순히 값을 넣는 칸이 아닙니다.
  • 비즈니스 규칙과 운영 기준이 반영되는 자리입니다.

✅ 6. Primary Key

  • Primary Key는 각 row를 고유하게 식별하는 값입니다.
  • 보통 id를 사용합니다.
  • 같은 테이블 안에서 중복되면 안 됩니다.
CREATE TABLE consults (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  phone TEXT NOT NULL
);

➕ 6-1. Primary Key가 필요한 이유

상담 신청 1건을 정확히 찾기 위해
상태 변경할 대상을 특정하기 위해
다른 테이블에서 참조하기 위해
로그와 이력을 연결하기 위해

➕ 6-2. id 타입 후보

방식설명특징
Auto Increment1, 2, 3 증가단순함
UUID랜덤 고유 ID외부 노출에 유리
CUID/Nanoid애플리케이션 생성 IDPrisma에서 사용 가능
내부 관리자용:
Auto Increment도 충분

외부 공개 URL:
UUID/CUID 고려

보안상 추측 방지 필요:
순차 id 노출 주의
  • 내부 DB 식별자와 외부에 노출하는 식별자는 다르게 가져가는 것도 좋습니다.
  • 예를 들어 내부 id는 숫자, 외부 publicId는 UUID로 둘 수 있습니다.

✅ 7. Foreign Key

  • Foreign Key는 다른 테이블의 데이터를 참조하는 값입니다.
  • 예를 들어 상담 신청은 특정 상품과 연결될 수 있습니다.
  • 이때 consults.productId가 products.id를 참조합니다.
products.id
  ↑
consults.productId

➕ 7-1. SQL 예시

CREATE TABLE products (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL
);

CREATE TABLE consults (
  id SERIAL PRIMARY KEY,
  product_id INTEGER REFERENCES products(id),
  name TEXT NOT NULL,
  phone TEXT NOT NULL
);

➕ 7-2. Foreign Key가 필요한 이유

존재하지 않는 상품으로 상담 신청되는 것 방지
상담 신청과 상품을 정확히 연결
상품별 상담 수 집계 가능
데이터 정합성 유지

➕ 7-3. 실무 주의

참조 대상 삭제 시 어떻게 할지 정해야 함
운영 데이터가 많으면 FK 추가 migration 영향 확인
관계가 너무 많으면 삭제/수정이 복잡해질 수 있음
  • Foreign Key는 데이터 정합성에 도움이 됩니다.
  • 다만 삭제 정책과 migration 영향을 같이 생각해야 합니다.

✅ 8. 관계 종류

  • 테이블 관계는 크게 1:1, 1:N, N:M으로 나눌 수 있습니다.
관계의미예시
1:1하나가 하나와 연결사용자 ↔ 상세 프로필
1:N하나가 여러 개와 연결상품 ↔ 상담 신청
N:M여러 개가 여러 개와 연결관리자 ↔ 권한

➕ 8-1. 1:1 관계

admin_users
  ↓
admin_profiles
  • 한 관리자에게 상세 프로필이 하나만 있는 구조입니다.
  • 실제로는 1:1 관계를 별도 테이블로 나눌 필요가 없는 경우도 많습니다.

➕ 8-2. 1:N 관계

products
  ↓
consults

상품 1개에 상담 신청 여러 건
  • 실무에서 가장 많이 쓰는 관계입니다.
  • 상품, 상담, 주문, 상태 이력, 알림 이력에서 자주 등장합니다.

➕ 8-3. N:M 관계

admin_users
  ↕
roles
  ↕
permissions
  • N:M 관계는 보통 중간 테이블을 둡니다.
admin_users
admin_user_roles
roles
  • 관리자 권한, 태그, 카테고리, 캠페인 매핑 등에서 사용할 수 있습니다.

✅ 9. 상담 신청 도메인으로 보는 테이블 설계

  • 온라인 휴대폰 판매몰에서는 상담 신청이 핵심 데이터입니다.
  • 상담 신청 테이블은 고객 정보, 상품 정보, 유입 정보, 상태 정보를 연결해야 할 수 있습니다.

➕ 9-1. 단순한 상담 테이블

consults
  - id
  - name
  - phone
  - productId
  - status
  - memo
  - createdAt
  - updatedAt

➕ 9-2. 조금 더 실무적인 상담 테이블

consults
  - id
  - customerName
  - phone
  - productId
  - carrier
  - planName
  - status
  - source
  - visitorId
  - ipAddress
  - userAgent
  - memo
  - createdAt
  - updatedAt

➕ 9-3. 주의할 점

전화번호는 개인정보
ipAddress도 개인정보 성격 가능
memo에는 민감정보가 들어갈 수 있음
유입 source는 나중에 분석 기준이 됨
status는 상태 이력과 함께 봐야 함
  • 상담 테이블은 단순 접수 데이터가 아닙니다.
  • 운영, 마케팅, 고객 응대, 성과 분석과 모두 연결됩니다.

✅ 10. 상태값 설계

  • 상담이나 주문은 상태값이 중요합니다.
  • 상태값은 단순 문자열로 대충 넣으면 나중에 통계와 필터가 어려워집니다.

➕ 10-1. 상담 상태 예시

NEW:
신규 접수

CALLING:
연락 중

CALLED:
연락 완료

PENDING:
보류

CONVERTED:
개통/전환 완료

CANCELED:
취소

DUPLICATED:
중복

➕ 10-2. 상태값 설계 기준

상태 이름이 명확한가?
운영자가 이해할 수 있는가?
최종 상태와 진행 상태가 구분되는가?
통계 기준으로 쓸 수 있는가?
상태 변경 이력이 필요한가?

➕ 10-3. 나쁜 상태값

Y
N
done
ok
etc
처리중
완료
기타
  • 상태값은 운영 언어와 개발 언어가 맞아야 합니다.
  • 상태 코드표를 문서화해두는 것이 좋습니다.

✅ 11. 상태 변경 이력 테이블

  • 현재 상태만 저장하면 “언제 누가 어떤 상태로 바꿨는지” 알 수 없습니다.
  • 관리자 업무에서는 상태 변경 이력이 중요합니다.

➕ 11-1. 예시 구조

consult_status_histories
  - id
  - consultId
  - fromStatus
  - toStatus
  - changedByAdminId
  - reason
  - createdAt

➕ 11-2. 필요한 이유

누가 상태를 바꿨는지 확인
잘못 변경한 상태 추적
상담 처리 흐름 분석
운영자 업무 기록
분쟁/오류 대응

➕ 11-3. 실무 예시

신규 접수
  ↓
연락 중
  ↓
연락 완료
  ↓
개통 완료
  • 현재 상태는 consults.status에 저장하고, 변경 이력은 별도 테이블에 저장하는 구조가 일반적입니다.
  • 이력 테이블은 나중에 운영 신뢰도를 크게 높여줍니다.

✅ 12. 상품 테이블 설계

  • 상품 테이블은 고객 화면과 상담 신청, 관리자 상품 관리에 모두 영향을 줍니다.
  • 통신사, 기종, 용량, 색상, 요금제, 혜택, 노출 여부 등을 어떻게 나눌지 정해야 합니다.

➕ 12-1. 단순 상품 테이블

products
  - id
  - name
  - carrier
  - price
  - imageUrl
  - isActive
  - createdAt
  - updatedAt

➕ 12-2. 실무적으로 확장된 상품 구조

products
  - id
  - modelName
  - carrier
  - category
  - isActive
  - displayOrder
  - createdAt
  - updatedAt

product_options
  - id
  - productId
  - storage
  - color
  - price
  - subsidy
  - extraSupport
  - isActive

➕ 12-3. 왜 옵션을 나눌까?

같은 기종에 용량/색상/가격이 여러 개
옵션별 지원금이 다를 수 있음
고객 신청 시 선택한 옵션을 저장해야 함
관리자 수정 범위가 명확해짐
  • 처음에는 하나의 상품 테이블로 시작해도 됩니다.
  • 하지만 용량/색상/요금제/지원금 조합이 많아지면 옵션 테이블 분리가 필요합니다.

✅ 13. 관리자 계정과 권한 테이블

  • 관리자 기능이 늘어나면 계정과 권한 설계가 필요합니다.
  • 모든 관리자에게 같은 권한을 주면 운영 실수가 커질 수 있습니다.

➕ 13-1. 단순 구조

admin_users
  - id
  - email
  - passwordHash
  - name
  - role
  - isActive
  - createdAt

➕ 13-2. 확장 구조

admin_users
  - id
  - email
  - passwordHash
  - name
  - isActive

roles
  - id
  - name

permissions
  - id
  - code
  - description

admin_user_roles
  - adminUserId
  - roleId

role_permissions
  - roleId
  - permissionId

➕ 13-3. 실무 기준

작은 규모:
role 컬럼 하나로 시작 가능

권한이 많아짐:
roles/permissions 분리

민감 작업:
엑셀 다운로드, 상태 변경, 상품 삭제, 관리자 관리
  • 처음부터 복잡한 RBAC를 만들 필요는 없습니다.
  • 하지만 엑셀 다운로드, 상태 변경, 관리자 관리 같은 민감 작업은 권한 기준을 가져야 합니다.

✅ 14. 유입 분석 테이블

  • 광고/쇼핑/자연유입/카카오/문자 등 유입 경로를 분석하려면 상담 신청과 유입 정보를 연결해야 합니다.
  • 단순히 source 문자열 하나만 저장해도 시작은 가능하지만, 운영 분석이 커지면 별도 구조가 필요합니다.

➕ 14-1. 단순 구조

consults
  - source
  - utmSource
  - utmMedium
  - utmCampaign
  - visitorId

➕ 14-2. 확장 구조

visit_logs
  - id
  - visitorId
  - ipAddress
  - userAgent
  - referer
  - utmSource
  - utmMedium
  - utmCampaign
  - createdAt

consults
  - id
  - visitorId
  - source
  - productId
  - createdAt

➕ 14-3. 분석 가능 항목

유입경로별 상담 신청 수
상품별 광고 전환 수
네이버 쇼핑 유입 대비 신청 수
자연검색 유입 신청 수
광고 캠페인별 신청 성과
  • 유입 분석은 나중에 붙이기보다 처음부터 최소한의 식별자를 남겨두는 것이 좋습니다.
  • visitorId, utm, referer, source 정도는 설계 기준을 정해야 합니다.

✅ 15. 중복 신청 방지 설계

  • 상담 신청에서는 중복 데이터가 자주 발생할 수 있습니다.
  • 같은 전화번호로 여러 번 신청하거나, 같은 고객이 다른 상품으로 다시 신청할 수 있습니다.

➕ 15-1. 중복 판단 기준 후보

전화번호 기준
전화번호 + 상품 기준
전화번호 + 날짜 기준
전화번호 + 캠페인 기준
IP + 전화번호 기준

➕ 15-2. 예시

같은 전화번호가 같은 상품에 당일 다시 신청:
중복 처리

같은 전화번호가 다른 상품에 신청:
신규 또는 관심 상품 추가 처리

같은 전화번호가 한 달 뒤 신청:
신규 상담으로 볼 수도 있음

➕ 15-3. DB 설계 기준

unique constraint를 걸 것인가?
애플리케이션 로직으로 판단할 것인가?
중복 신청도 이력으로 남길 것인가?
운영자가 중복을 병합할 수 있게 할 것인가?
  • 중복 방지는 단순히 unique 하나로 끝나지 않을 수 있습니다.
  • 운영 정책을 먼저 정해야 DB 제약조건을 정할 수 있습니다.

✅ 16. Soft Delete

  • 실무에서는 데이터를 바로 삭제하지 않고 deletedAt을 남겨 숨기는 경우가 많습니다.
  • 이를 Soft Delete라고 합니다.

➕ 16-1. 예시

products
  - id
  - name
  - deletedAt
삭제 버튼 클릭
  ↓
DB row 삭제 X
  ↓
deletedAt에 시간 저장
  ↓
일반 조회에서 제외

➕ 16-2. 필요한 이유

실수 삭제 복구
상담/주문과 연결된 상품 기록 유지
운영 이력 보존
감사 로그와 연결

➕ 16-3. 주의

모든 조회에 deletedAt 조건 필요
unique constraint와 충돌 가능
관리자 복구 기능 필요할 수 있음
데이터가 계속 쌓임
  • 상품, 배너, 관리자 계정, 상담 메모 등은 Soft Delete를 고려할 수 있습니다.
  • 다만 상담/주문 같은 원본 데이터는 삭제보다 보존 정책을 더 신중히 봐야 합니다.

✅ 17. Timestamp 컬럼

  • 거의 모든 테이블에는 생성일과 수정일이 필요합니다.
createdAt:
데이터 생성 시간

updatedAt:
마지막 수정 시간

deletedAt:
삭제 또는 숨김 처리 시간

➕ 17-1. 필요한 이유

최근 상담 조회
일자별 신청 수 집계
운영자 수정 시점 확인
문제 발생 시간 추적
배포 전후 데이터 변화 확인

➕ 17-2. 실무 기준

createdAt은 거의 필수
updatedAt도 대부분 필요
deletedAt은 soft delete 대상에 필요
timezone 기준 통일
관리자 화면 표시 형식 통일
  • 시간 데이터는 통계와 운영 분석의 기준입니다.
  • 저장 기준과 표시 기준을 혼동하지 않도록 해야 합니다.

✅ 18. 정규화와 비정규화

  • 정규화는 중복을 줄이고 테이블을 역할별로 나누는 것입니다.
  • 비정규화는 조회 성능이나 운영 편의를 위해 일부 중복을 허용하는 것입니다.

➕ 18-1. 정규화 예시

products:
상품 기본 정보

product_options:
용량/색상/가격 옵션

consults:
상담 신청 정보

장점:

중복 감소
수정 일관성 증가
데이터 정합성 향상

단점:

조회 시 join 증가
쿼리 복잡도 증가

➕ 18-2. 비정규화 예시

consults
  - productId
  - productNameSnapshot
  - carrierSnapshot
  - subsidySnapshot

필요한 이유:

상담 당시 상품명/지원금 보존
나중에 상품 정보가 바뀌어도 당시 기록 유지
관리자 목록에서 빠르게 표시
  • 상담 신청 당시의 상품 정보는 나중에 상품이 수정되어도 유지되어야 할 수 있습니다.
  • 이런 경우 snapshot 컬럼을 두는 것이 실무적으로 유리합니다.

✅ 19. Snapshot 데이터

  • Snapshot은 특정 시점의 값을 그대로 저장하는 방식입니다.
  • 주문, 상담, 계약, 결제처럼 “당시 조건”이 중요한 데이터에 자주 사용됩니다.

➕ 19-1. 상담 신청 snapshot 예시

consults
  - productId
  - productNameSnapshot
  - planNameSnapshot
  - supportAmountSnapshot
  - carrierSnapshot

➕ 19-2. 필요한 이유

상품명이 나중에 바뀌어도 상담 당시 상품명 유지
지원금이 바뀌어도 신청 당시 조건 유지
운영자와 고객 간 안내 기준 보존
분쟁 발생 시 당시 조건 확인

➕ 19-3. 주의

원본 product와 snapshot의 역할 구분
어떤 값을 snapshot으로 남길지 기준 필요
snapshot 값은 나중에 자동으로 바뀌지 않음
  • snapshot은 중복처럼 보이지만 실무에서는 중요한 기록입니다.
  • 특히 통신 상품처럼 가격/지원금/혜택이 자주 바뀌는 서비스에서는 유용합니다.

✅ 20. 제약조건 Constraint

  • 제약조건은 DB가 데이터 규칙을 지키게 만드는 장치입니다.
  • 백엔드에서 검증하더라도 DB 제약조건이 있어야 최종 방어선이 됩니다.
제약조건의미예시
NOT NULL비어 있으면 안 됨전화번호
UNIQUE중복 금지관리자 이메일
FOREIGN KEY참조 무결성productId
CHECK값 범위 제한price >= 0
DEFAULT기본값status = NEW

➕ 20-1. 예시

CREATE TABLE admin_users (
  id SERIAL PRIMARY KEY,
  email TEXT NOT NULL UNIQUE,
  password_hash TEXT NOT NULL,
  is_active BOOLEAN NOT NULL DEFAULT true
);

➕ 20-2. 실무 기준

필수값은 NOT NULL
중복되면 안 되는 값은 UNIQUE
참조 관계는 FK 고려
숫자 범위는 CHECK 고려
기본 상태는 DEFAULT 사용
  • 백엔드 validation은 UX와 에러 처리를 위한 것이고, DB constraint는 최종 데이터 보호 장치입니다.
  • 둘 다 필요합니다.

✅ 21. Index 기본

  • 인덱스는 검색과 정렬 속도를 높이는 구조입니다.
  • 관리자 목록에서 상태, 날짜, 전화번호, 상품명, 유입경로로 검색/필터를 많이 한다면 인덱스가 중요합니다.

➕ 21-1. 인덱스가 필요한 후보

consults.status
consults.createdAt
consults.phone
consults.productId
consults.source
orders.status
orders.createdAt
admin_users.email

➕ 21-2. 예시

CREATE INDEX idx_consults_status_created_at
ON consults (status, created_at);

➕ 21-3. 주의

인덱스가 많다고 무조건 좋은 것은 아님
쓰기 성능과 저장공간 비용 증가
실제 쿼리 패턴 기준으로 추가
EXPLAIN으로 확인 필요
  • 인덱스는 나중에 더 깊게 다룰 주제입니다.
  • 지금은 “관리자 검색/필터 성능은 DB 인덱스와 직접 연결된다” 정도를 기억하면 됩니다.

✅ 22. ERD란 무엇인가?

  • ERD는 Entity Relationship Diagram의 약자입니다.
  • 테이블과 테이블 사이의 관계를 그림으로 표현한 것입니다.
  • 복잡한 서비스를 설계할 때 ERD를 보면 데이터 구조를 빠르게 이해할 수 있습니다.

➕ 22-1. 간단한 ERD 예시

products 1 ─── N consults
consults 1 ─── N consult_status_histories
admin_users 1 ─── N consult_status_histories
visitors 1 ─── N consults

➕ 22-2. ERD에 표시할 것

테이블 이름
Primary Key
Foreign Key
주요 컬럼
1:N / N:M 관계
삭제 정책

➕ 22-3. 좋은 ERD 기준

핵심 테이블이 한눈에 보임
관계 방향이 명확함
중간 테이블 역할이 보임
상태 이력/로그 테이블이 구분됨
  • ERD는 개발자뿐 아니라 기획/운영 흐름을 설명할 때도 도움이 됩니다.
  • 1인 개발자라도 주요 테이블 ERD는 간단히 그려두는 것이 좋습니다.

✅ 23. 현재 프로젝트 기준 핵심 테이블 후보

  • 온라인 휴대폰 판매몰 기준으로는 아래 테이블들이 핵심 후보입니다.
products:
상품 기본 정보

product_options:
용량/색상/지원금 옵션

consults:
상담 신청

consult_status_histories:
상담 상태 변경 이력

admin_users:
관리자 계정

roles / permissions:
관리자 권한

visit_logs:
방문/유입 로그

lead_sources:
유입 소스 관리

alimtalk_logs:
알림톡 발송 이력

export_jobs:
엑셀 Export 작업

audit_logs:
관리자 주요 작업 이력

➕ 23-1. 우선순위

1순위:
consults, products, admin_users

2순위:
consult_status_histories, product_options

3순위:
visit_logs, alimtalk_logs, audit_logs

4순위:
export_jobs, roles, permissions
  • 처음부터 모든 테이블을 완벽히 만들 필요는 없습니다.
  • 하지만 상담, 상품, 관리자, 상태 이력은 초기에 구조를 잡아두는 것이 좋습니다.

✅ 24. 나쁜 DB 설계 신호

하나의 테이블에 모든 데이터가 들어감
컬럼명이 data1, data2, etc처럼 모호함
상태값이 제각각 저장됨
중복 데이터 기준이 없음
삭제 시 관련 데이터가 같이 사라짐
검색이 느린데 인덱스 기준이 없음
상태 변경 이력이 없음
운영자가 누가 수정했는지 알 수 없음
상품 정보 변경 후 과거 상담 조건도 바뀌어 보임

➕ 24-1. 특히 위험한 구조

consults 테이블에 JSON으로 모든 정보 저장
상품 가격/지원금 변경 이력 없음
상담 상태 이력 없음
관리자 작업 audit log 없음
운영 DB에서 직접 수정하는 습관
  • JSON 컬럼은 유용하지만 모든 것을 JSON으로 넣으면 검색, 정합성, migration이 어려워집니다.
  • 핵심 도메인 데이터는 명확한 컬럼과 관계로 설계하는 것이 좋습니다.

✅ 25. 실무 체크리스트

➕ 25-1. 테이블 설계 체크리스트

  1. 테이블 이름만 봐도 의미가 명확한가?
  2. 각 테이블의 책임이 하나로 정리되어 있는가?
  3. Primary Key가 명확한가?
  4. Foreign Key 관계가 필요한 곳에 있는가?
  5. 필수값에 NOT NULL이 있는가?
  6. 중복되면 안 되는 값에 UNIQUE가 있는가?
  7. 상태값이 코드표로 관리되는가?
  8. createdAt/updatedAt이 있는가?

➕ 25-2. 상담 도메인 체크리스트

  1. 상담 신청과 상품이 연결되어 있는가?
  2. 상담 당시 상품 정보 snapshot이 필요한가?
  3. 전화번호 중복 기준이 정해져 있는가?
  4. 상담 상태값이 명확한가?
  5. 상태 변경 이력이 남는가?
  6. 누가 상태를 바꿨는지 기록되는가?
  7. 유입 source/visitorId가 저장되는가?
  8. 개인정보 컬럼 보안 기준이 있는가?

➕ 25-3. 관리자 운영 체크리스트

  1. 관리자 계정이 별도 테이블로 관리되는가?
  2. 관리자 이메일은 unique인가?
  3. 권한 구조가 현재 규모에 맞는가?
  4. 엑셀 다운로드 같은 민감 기능 권한이 있는가?
  5. 관리자 주요 작업 audit log가 필요한가?
  6. 삭제보다 soft delete가 필요한 데이터가 있는가?
  7. 검색/필터에 필요한 인덱스 후보가 정리되어 있는가?
  8. 운영자가 실수했을 때 추적할 수 있는가?

✅ 26. AI에게 DB 설계를 점검시킬 때 좋은 질문법

  • AI에게 DB 설계를 물어볼 때는 서비스의 핵심 흐름과 운영 요구사항을 함께 알려줘야 합니다.
  • 단순히 “테이블 설계해줘”라고 하면 너무 일반적인 답이 나옵니다.

➕ 26-1. 좋은 질문 예시

온라인 휴대폰 판매몰의 PostgreSQL 테이블 구조를 점검하고 싶어.

서비스 상황:
1. 고객은 상품 상세에서 상담 신청을 함
2. 상담 신청에는 이름, 전화번호, 선택 상품, 통신사, 요금제, 유입 source, visitorId가 들어감
3. 관리자는 상담 목록을 검색/필터하고 상태를 변경함
4. 상담 상태 변경 이력을 남기고 싶음
5. 상품은 기종, 통신사, 용량, 색상, 지원금, 추가지원금이 있음
6. 상품 정보가 나중에 바뀌어도 상담 당시 조건은 보존하고 싶음
7. 관리자 권한은 처음에는 단순 role로 시작하지만 나중에 permission 구조로 확장할 수 있음
8. 네이버 광고/쇼핑/자연유입과 상담 신청을 연결해 분석하고 싶음
9. 개인정보 로그와 DB 보안도 고려해야 함

요청:
- 핵심 테이블 후보
- 테이블 간 관계
- 상담 신청 테이블 설계
- 상품/옵션 테이블 설계
- 상태 변경 이력 테이블 설계
- 유입 분석 테이블 설계
- 중복 신청 방지 기준
- snapshot 컬럼이 필요한 부분
- index 후보
- soft delete 적용 후보
- Prisma schema로 옮길 때 주의할 점
을 실무 기준으로 정리해줘.

➕ 26-2. AI 답변 검증 기준

  1. 상담 신청과 상품 관계를 명확히 잡는가?
  2. 상태 변경 이력 테이블을 제안하는가?
  3. 상품 정보 snapshot 필요성을 이해하는가?
  4. 개인정보 컬럼 보안과 마스킹을 고려하는가?
  5. 중복 신청 기준을 단순 unique 하나로만 끝내지 않는가?
  6. 관리자 검색/필터 인덱스 후보를 제안하는가?
  7. 현재 규모에 비해 과도한 테이블 분리를 강요하지 않는가?
  8. 운영자가 실제로 쓰는 흐름을 기준으로 설계하는가?

📌 요약

  • 데이터베이스는 단순 저장소가 아니라 서비스의 실제 상태와 기록을 보관하는 핵심 시스템입니다.
  • PostgreSQL은 테이블, 관계, 트랜잭션, 인덱스, 제약조건을 통해 실무 서비스의 데이터 정합성과 운영 안정성을 지키는 데 적합합니다.
  • 테이블은 책임이 명확해야 하며, products, consults, admin_users, consult_status_histories처럼 이름만 봐도 의미가 보여야 합니다.
  • Primary Key는 row를 고유하게 식별하고, Foreign Key는 상품과 상담 신청처럼 서로 다른 테이블의 관계를 안전하게 연결합니다.
  • 상담 신청 도메인에서는 고객 정보, 상품, 상태, 유입 source, visitorId, IP, 메모, 생성일을 어떤 기준으로 저장할지 신중히 설계해야 합니다.
  • 상담/주문 상태는 단순 문자열이 아니라 운영 코드표로 관리해야 하며, 상태 변경 이력 테이블을 두면 누가 언제 어떤 상태로 바꿨는지 추적할 수 있습니다.
  • 상품은 기종과 옵션을 분리할지 고민해야 하며, 용량/색상/지원금 조합이 많아지면 product_options 같은 테이블 분리가 유리합니다.
  • 상품명, 지원금, 요금제처럼 상담 당시 조건이 중요한 값은 snapshot 컬럼으로 남겨야 나중에 상품 정보가 바뀌어도 과거 상담 기록이 흔들리지 않습니다.
  • 중복 신청 방지는 전화번호, 상품, 날짜, 캠페인 등 운영 정책을 먼저 정한 뒤 DB unique나 애플리케이션 로직으로 구현해야 합니다.
  • 관리자 검색/필터 성능은 status, createdAt, phone, productId, source 같은 인덱스 후보와 직접 연결됩니다.
  • 다음 내용에서는 이 구조를 실제 ORM에 옮기는 관점으로 Prisma Schema 설계와 Migration 전략을 다루면 좋습니다.

0개의 댓글