좋은 데이터베이스 설계 원칙과 AI 점검 프롬프트

좋은 데이터베이스 설계는 데이터가 쌓이고 바뀌어도 모순이 생기지 않도록 구조와 규칙을 정하는 일이에요. 정규화·무결성·ACID를 주문과 예약 사례로 설명하고, 내 프로젝트를 점검할 체크리스트 프롬프트를 제공합니다.

핵심 요약

AI에게 회원가입과 주문 기능을 만들어달라고 했어요. 회원가입도 되고 주문 내역도 잘 보입니다. 데이터베이스(DB)에도 값이 들어가요.

그러면 데이터베이스를 잘 설계한 걸까요?

상품 가격을 바꾸어도 과거 주문 금액이 유지되는지, 같은 요청이 두 번 들어와도 주문이 중복되지 않는지까지 확인해봐야 합니다.

좋은 데이터베이스 설계는 데이터가 쌓이고 바뀌어도 모순이 생기지 않도록 구조와 규칙을 정하는 일이에요.

이 글에서는 데이터를 표로 나누고 서로 연결하는 관계형 데이터베이스를 기준으로 설명합니다. 마지막에는 내 프로젝트를 AI에게 점검받을 수 있는 프롬프트도 준비했어요.

1. 기록 하나의 의미를 명확히 해요

테이블은 같은 종류의 기록을 모아놓은 표이고, 행은 그 안의 기록 한 건이에요.

온라인 수업을 판매한다면 다음처럼 나눌 수 있습니다.

테이블 행 하나의 의미
회원 회원 한 명
강좌 판매하는 강좌 하나
주문 구매 요청 한 건
결제 주문에 연결된 결제 시도 한 건

어떤 대상을 기록하고 서로 어떻게 연결할지 정하는 일을 데이터 모델링이라고 합니다. 여기서 먼저 답해야 할 질문은 “행 하나가 정확히 무엇인가?”예요.

주문과 결제를 같은 기록으로 생각하면 처음에는 편합니다. 하지만 결제에 실패한 뒤 다시 시도하는 순간 문제가 생겨요. 주문은 하나인데 결제 시도는 여러 번일 수 있기 때문입니다.

이를 구분하면 어떤 구매 요청에서 결제가 실패했고, 어떤 시도가 최종적으로 성공했는지 남길 수 있어요.

관계도 함께 정해야 합니다. 한 회원이 여러 주문을 만들 수 있는지, 한 주문에 여러 상품을 담을 수 있는지에 따라 구조가 달라져요.

이런 규칙이 모호하다면 AI와 PRD를 작성하는 방법부터 정리해보세요. 데이터 모델은 서비스가 실제로 작동하는 방식을 담아야 합니다.

2. 같은 사실을 여러 곳에서 따로 수정하지 않아요

상품 이름과 설명을 주문마다 복사해 저장했다고 해볼게요. 상품 정보를 따로 관리하는 곳도 없다고 가정하겠습니다.

현재 상품 설명을 고치려면 모든 주문을 찾아 수정해야 합니다. 일부만 수정하면 같은 상품인데 설명이 달라져요. 마지막 주문을 지웠을 때 상품 정보까지 사라질 수도 있습니다. 아직 주문이 없는 새 상품은 등록할 곳도 없어요.

이처럼 데이터를 추가하거나 수정하고 삭제할 때 생기는 모순을 줄이도록 구조를 정리하는 것이 정규화예요. 상품 자체의 정보는 상품 테이블에서 관리하고, 주문에서는 어떤 상품인지 연결하는 식입니다. Microsoft 정규화 설명 (새 탭에서 열림)

내 프로젝트에서는 다음 질문부터 해보세요.

같은 사실을 바꿀 때 여러 곳을 찾아서 수정해야 하나?

그렇다면 기준이 되는 기록을 한곳에 두고 연결할 수 있는지 살펴볼 만합니다.

다만 현재 값과 과거에 확정된 값은 구분해야 해요.

기록
상품의 현재 판매 가격 59,000원
지난달 주문의 구매 당시 가격 49,000원

현재 가격이 바뀌어도 과거 주문은 49,000원으로 남아야 합니다. 구매 당시 상품명이나 가격처럼 그 시점의 상태를 별도로 보존하는 기록을 스냅샷이라고 해요.

현재 가격과 구매 당시 가격은 서로 다른 사실입니다. 정규화를 한다며 과거 가격까지 없애면 주문 기록이 잘못될 수 있어요.

3. 올바른 데이터의 조건을 명시해요

데이터 무결성은 데이터가 정해진 규칙을 지키고 서로 모순되지 않는 성질을 뜻해요.

말은 어렵지만 실제 규칙은 구체적입니다.

지켜야 할 규칙 대표적인 구현 방법
각 주문을 구분할 수 있어야 함 기본 키: 기록의 고유 식별자
주문 금액이 비어 있으면 안 됨 NOT NULL: 값 없음 금지
주문번호가 중복되면 안 됨 UNIQUE: 중복 금지
주문 금액은 0 이상이어야 함 CHECK: 값의 조건 검사
회원 연결이 필수이고 실제 회원을 가리켜야 함 외래 키와 NOT NULL

이런 장치를 제약조건이라고 합니다. 화면에서 입력값을 검사하더라도 관리자 기능이나 다른 서버 코드에서 데이터를 저장할 수 있으므로, DB에서 표현할 수 있는 핵심 규칙은 제약조건으로도 지키게 하는 편이 좋아요.

여기서 NULL은 값이 없음을 나타내는 표시예요. NOT NULL만으로는 문자열 항목에 빈 문자열("")이나 공백을 넣는 것까지 막지 못하므로, 이름 같은 필수 항목에는 그 조건도 따로 검사해야 합니다. PostgreSQL 제약조건 문서 (새 탭에서 열림)

데이터 타입도 규칙의 일부입니다. 금액은 숫자로 저장하면 계산하기 쉬워요. 원 단위 금액은 정수로, 소수 금액이 필요하면 정확한 소수를 저장하는 타입을 검토합니다. 쉼표와 통화 표시는 화면에서 붙이면 됩니다. PostgreSQL 숫자 타입 문서 (새 탭에서 열림)

다만 제약조건이 있다고 서비스의 모든 규칙이 보장되는 것은 아닙니다.

주문 금액이 0 이상인지 검사해도, 그 금액이 실제 상품 가격과 맞는지는 별도 확인이 필요해요. 서버가 상품과 할인 조건을 확인해 금액을 결정해야 합니다.

“한 회원이 같은 강좌를 중복 구매할 수 없다”는 규칙도 환불 후 재구매를 허용하는지 먼저 정해야 해요. 기술적인 규칙을 넣기 전에 서비스의 규칙부터 분명해야 합니다.

4. 여러 변경과 동시 요청에도 규칙을 지켜요

마지막 자리가 하나 남은 수업에 두 사람이 동시에 신청합니다.

두 요청이 모두 “한 자리 남았다”고 확인한 뒤 예약을 저장하면 정원을 넘길 수 있어요. 혼자 버튼을 눌러보는 테스트에서는 발견하기 어려운 문제입니다.

여기서 등장하는 개념이 트랜잭션이에요. 함께 성공하거나 실패해야 하는 DB 작업을 하나로 묶는 것입니다.

예를 들어 좌석 수를 줄이는 작업과 예약을 만드는 작업을 묶으면, 예약 생성에 실패했는데 좌석만 줄어든 상태를 막을 수 있어요. PostgreSQL 트랜잭션 문서 (새 탭에서 열림)

트랜잭션을 설명할 때 자주 나오는 ACID는 다음 성질을 말합니다. IBM ACID 설명 (새 탭에서 열림)

성질 의미
원자성 · Atomicity 묶인 작업이 전부 반영되거나 전부 취소됨
일관성 · Consistency 작업 전후에 정의된 데이터 규칙이 유지됨
격리성 · Isolation 동시에 실행되는 작업의 상호 영향을 격리 수준에 따라 제어함
지속성 · Durability 완료된 변경을 장애가 나도 보존하도록 보장함

ACID를 지원하는 DB를 쓴다고 서비스 규칙이 자동으로 완성되지는 않아요. 어떤 작업을 묶을지, 동시에 같은 기록을 바꿀 때 어떻게 처리할지는 설계해야 합니다.

표에 나온 격리 수준은 동시에 실행되는 작업들이 서로의 변경을 어디까지 볼 수 있는지 등을 정하는 기준이에요. 필요한 격리 수준과 동시 실행 처리 방법을 함께 검토해야 합니다.

정원 초과를 막으려면 남은 자리가 있을 때만 줄이는 조건부 변경이나, 같은 기록의 수정을 순서대로 처리하는 잠금 등이 필요할 수 있어요. 트랜잭션으로 묶는 것만으로 모든 동시 요청 문제가 해결되지는 않습니다. PostgreSQL 동시 실행 처리 문서 (새 탭에서 열림)

같은 요청의 재시도도 별도로 확인하세요. 주문은 저장됐는데 응답이 끊기면 사용자가 다시 누를 수 있습니다. 같은 요청을 식별해 기존 결과를 돌려주는 등의 중복 방지가 필요해요.

외부 결제나 이메일 발송은 DB 작업을 취소해도 함께 취소되지 않습니다. 외부 작업은 성공했는데 내부 기록에 실패한 경우를 확인하고 복구할 방법도 정해야 해요.

5. 접근 권한과 삭제 이후까지 설계해요

주문에 회원 ID가 들어 있다고 다른 회원의 접근이 자동으로 차단되지는 않습니다.

외래 키는 연결된 회원이 존재하는지 검사합니다. 지금 요청한 사람이 그 주문을 볼 권한이 있는지는 서버나 DB의 접근 정책에서 따로 검사해야 해요.

팀이 함께 사용하는 서비스라면 어느 팀의 데이터인지도 구분해야 합니다. 조회뿐 아니라 수정과 삭제에서도 같은 경계를 지켜야 해요.

삭제 규칙도 연결 관계를 만들 때 함께 정하세요.

상품을 삭제했더니 과거 주문까지 사라지면 곤란합니다. 반면 임시 문서를 삭제할 때 그 문서에만 속한 임시 항목을 함께 지우는 것은 자연스러울 수 있어요.

외래 키에는 연결된 기록이 있으면 삭제를 막거나, 연결된 기록도 함께 지우거나, 연결 값을 비우는 동작 등을 지정할 수 있습니다. 데이터의 의미에 맞게 선택해야 해요. PostgreSQL 외래 키와 삭제 규칙 (새 탭에서 열림)

회원 탈퇴도 마찬가지입니다. 삭제할 개인정보와 연결을 끊을 기록, 별도 근거에 따라 보관할 기록을 구분하세요. 화면에서 숨기는 것과 실제로 삭제하는 것도 다른 처리입니다.

6. 실제 조회 방식과 구조 변경을 고려해요

데이터가 쌓이면 어떻게 찾는지가 중요해집니다.

사용자가 매번 “내 주문을 최신순으로” 본다면 회원 ID로 찾고 주문 시각으로 정렬하는 작업이 반복돼요.

이런 조회를 돕는 장치가 인덱스입니다. 책의 색인처럼 원하는 기록을 찾는 데 도움을 줘요. 다만 저장 공간을 차지하고 데이터 변경 때 관리 비용이 들기 때문에 모든 컬럼에 붙이는 것이 좋은 기준은 아닙니다. PostgreSQL 인덱스 문서 (새 탭에서 열림)

AI에게 인덱스를 추천받을 때는 어떤 조회를 위한 것인지도 설명하게 하세요. 성능을 위해 구조를 복잡하게 바꾸기 전에는 실제 조회와 측정 결과를 확인하는 편이 좋습니다.

기능을 추가하면서 구조를 바꿀 방법도 필요해요.

이미 회원이 천 명 있는데 새 필수 항목을 추가한다면 기존 회원의 빈 값을 어떻게 처리할까요? 이름을 바꾼 컬럼을 예전 코드가 계속 사용하고 있지는 않을까요?

기존 데이터와 실행 중인 코드를 고려해 변경 순서를 정하고 기록으로 남겨야 합니다. 이 과정은 스키마와 마이그레이션 설명에서 이어서 볼 수 있어요.

내 프로젝트를 점검하는 체크리스트 프롬프트

프로젝트 파일을 읽을 수 있는 AI 코딩 도구에 아래 프롬프트를 넣어보세요. 대괄호 안을 내 서비스에 맞게 바꾸면 됩니다.

프로젝트를 읽을 수 없는 채팅에서는 개인정보와 비밀값을 제외한 설계 문서를 함께 제공하세요.

내 프로젝트의 관계형 데이터베이스 설계를 검토해줘.
나는 비개발자이므로 용어는 처음 등장할 때 쉽게 설명해줘.

서비스: [예: 온라인 수업 예약 서비스]
핵심 흐름: [예: 회원가입 → 수업 선택 → 결제 → 예약 확인]
반드시 지켜야 할 규칙: [예: 정원 초과 금지, 본인 예약만 조회]
운영 데이터 유무: [있음 / 없음 / 모름]

먼저 요구사항 문서, DB 스키마, 마이그레이션, 주요 조회·저장 코드와 접근 권한 검사를 읽어줘.
지금은 검토만 하고 파일이나 DB를 변경하지 마.

다음 원칙으로 점검해줘.

1. 데이터 모델링
- 각 테이블의 행 하나가 무엇을 의미하는가?
- 서로 다른 종류의 기록을 섞거나 불필요하게 나눈 곳이 있는가?
- 고유 식별자와 테이블 사이의 관계가 실제 사용 방식과 맞는가?

2. 정규화와 과거 기록
- 같은 사실을 여러 곳에서 따로 수정해야 하는가?
- 추가·수정·삭제 때문에 다른 정보가 모순되거나 사라지는가?
- 현재 값을 참조할 항목과 당시 값을 보존할 항목을 구분했는가?
- 가격 변경이 과거 주문 기록에 영향을 주는가?
- 의도적인 중복이 있다면 목적과 일치시킬 방법이 명확한가?

3. 데이터 무결성
- 금액·날짜·시각·전화번호 등의 타입과 단위가 적절한가?
- 필수값, 중복 금지, 허용 범위와 외래 키가 실제로 정의되어 있는가?
- NULL, 빈 문자열, 기본값의 의미와 허용 조건이 명확한가?
- 서비스 규칙 중 DB 제약조건으로 보장하는 것과 서버 로직으로 보장하는 것은 무엇인가?
- 주문·결제·예약 상태가 서로 모순될 경로가 있는가?

4. 트랜잭션·동시 요청·재시도
- 함께 성공하거나 실패해야 하는 DB 변경이 묶여 있는가?
- 두 요청이 동시에 들어와도 정원·재고 등의 규칙을 지키는가?
- 트랜잭션만으로 해결되지 않는 경합이 있는가?
- 같은 요청을 다시 보내면 기록이나 외부 작업이 중복되는가?
- 외부 결제 등과 DB 기록이 어긋났을 때 복구할 방법이 있는가?

5. 접근 권한과 데이터 수명
- 사용자·팀별로 조회·추가·수정·삭제 권한을 실제로 검사하는가?
- 다른 사용자나 팀의 데이터에 접근할 경로가 있는가?
- 회원이나 상품을 지우면 연결된 기록은 어떻게 되는가?
- 보존해야 할 이력이 사라지거나 불필요한 개인정보가 남는가?

6. 조회와 구조 변경
- 자주 쓰는 필터·정렬·연결 조회에 맞는 인덱스가 있는가?
- 각 인덱스 제안이 어떤 조회를 위한 것인지 설명해줘.
- 측정 자료가 없으면 성능 문제를 확정하지 마.
- 기존 데이터가 있는 상태에서도 제안한 변경을 적용할 수 있는가?
- 기존 값을 채우는 작업, 배포 순서, 복구 방법이 필요한가?

결과는 다음 순서로 작성해줘.

A. 현재 구조와 핵심 규칙을 쉬운 말로 요약

B. 항목별 점검표
판정은 다음 중 하나로 표시해줘.
문제 확인 / 확인 범위에서 문제 없음 / 확인 불가 / 해당 없음

C. 확인된 문제의 상세 내용
- 근거가 되는 파일·테이블·컬럼·코드 위치
- 문제가 발생하는 구체적인 사용자 상황
- 심각도와 판단 이유
- 현재 요구사항을 만족하는 가장 작은 개선안
- 개선에 따른 복잡도와 기존 데이터에 미치는 영향
- 수정 후 확인할 테스트와 기대 결과

D. 내가 결정해야 할 질문과 추천 작업 순서

코드만 읽은 판단과 실제 DB에서 확인한 사실을 구분해줘.
스키마 파일에 있다는 이유만으로 운영 DB에도 적용됐다고 판단하지 마.
실행하지 않은 테스트를 통과했다고 말하지 마.
확인하지 못한 항목을 근거 없이 문제로 단정하지 마.

필수 정보가 부족하면 중요한 질문부터 한 번에 하나씩 해줘.
현재 요구사항과 관계없는 확장 기능은 추가로 설계하지 마.

AI의 답변에서는 어떤 상황에서 어떤 데이터가 잘못되는지를 먼저 확인하세요.

“회원 연락처를 바꾸면 일부 주문에는 예전 연락처가 남습니다”라는 설명이 있다면 그 연락처의 목적을 살펴보세요. 현재 연락처를 보여줘야 하는지, 주문 당시 연락처를 보존하려는 것인지에 따라 수정 여부가 달라집니다.

좋은 점검은 서비스가 기억해야 할 사실을 이해하고, 그 사실이 실제 사용 과정에서도 유지되는지 확인합니다. 취소, 재구매, 팀 공유 같은 기능을 추가할 때도 같은 체크리스트로 달라진 규칙을 확인해보세요.

#기초#데이터베이스#프롬프트#바이브코딩

인스타그램 @ddukddak.build · 페이스북 뚝딱