
이전 글에서는 `cafe_practice` 데이터베이스를 만들고,
그 안에 `cafe` 스키마까지 생성했다.
DB초보자 PostgreSQL_02.DataBase와 Schema 만들기
이번 글에서는 그 안에 실제 테이블을 만든다.
쉽게 말하면,
이전 글에서 카페를 지을 부지와 구역을 만들었다면,
이번 글에서는 고객 목록, 메뉴 목록, 주문 목록 같은
실제 데이터 표를 하나씩 배치하는 과정이다.
목차
- 이번 글에서 할 일
- Table이 무엇인지 다시 확인하기
- ERD 초안에서 만들 테이블 확인하기
- CREATE TABLE 기본 문법 이해하기
- 고객 테이블 생성과 확인
- 고객 데이터 입력과 SELECT 확인
- 직접 만들어보기: 메뉴 테이블
- 직접 만들어보기: 주문 테이블과 FK
- 직접 만들어보기: 주문상세 테이블과 FK
1. 이번 글에서 할 일
이전 글에서는
카페 주문 관리 ERD 초안을 만들었다.
그리고 cafe_practice 데이터베이스와 cafe 스키마까지 준비했다.

4개의 테이블을 만들고
1. 고객 테이블
2. 메뉴 테이블
3. 주문 테이블
4. 주문상세 테이블
샘플 데이터 입력 후 확인까지 해보자!
2. Table이 정확히 무엇인지 다시 확인하기
Table? 같은 종류의 데이터를 모아두는 표
식탁, 책상으로 예를 들면,
공부하는 책상에는 공부와 관련된 것들이 올라간다.
밥 먹는 식탁에는 밥 먹는 데 필요한 것들이 올라간다.
컴퓨터 책상에는 컴퓨터 작업에 필요한 것들이 올라간다.
물론 컴퓨터 책상에 음식이 올라올 수도 있다.
가능은 하다.
하지만 잘못하면 음식을 엎지를 수도 있고,
타이핑할 때 걸리적거릴 수도 있다.
데이터도 비슷하다.
아무 표 하나에 고객, 메뉴, 주문 정보를 전부 넣을 수도 있다.
처음에는 가능해 보인다.
하지만 데이터가 많아지면 금방 지저분해지고, 찾기도 어려워진다.
그래서 같은 종류끼리 따로 묶어서 표를 만든다.
고객 정보는 고객 Table에,
메뉴 정보는 메뉴 Table에,
주문 정보는 주문 Table에 넣는다.
즉, Table은 같은 종류의 데이터를
깔끔하게 모아두는 표라고 생각하자.
3. ERD 초안에서 만들 테이블 확인하기

확인하고 다음으로 넘어가자
| 표 | 역할 |
|---|---|
| 고객 | 손님 정보 |
| 메뉴 | 판매 메뉴 정보 |
| 주문 | 주문 한 건 |
| 주문상세 | 주문 안의 메뉴 줄 |
각각 어떤 테이블에 어떤 정보들이 들어가야 되는지 생각해보면서 다음으로 넘어가자.
4. CREATE TABLE 기본 문법 이해하기

| 개념 | 지금 이해할 정도 |
|---|---|
CREATE TABLE | 새 테이블을 만든다 |
cafe.customers | cafe 스키마 안에 customers 테이블을 만든다 |
() | 테이블 안에 들어갈 컬럼 목록 |
| 컬럼명 | 저장할 항목 이름 |
| 데이터타입 | 글자, 숫자, 날짜 같은 값의 종류 |
| 조건 | 비어도 되는지, 중복 가능한지 같은 규칙 |
, | 컬럼끼리 구분 |
; | SQL 문장 끝 |
CREATE TABLE은 말 그대로 테이블을 만든다는 SQL 명령어다.
지금까지는
고객 표, 메뉴 표, 주문 표, 주문상세 표가 필요하다고
정리하는 과정이었고 이제 실제로 만들기 전 만드는 방법을 알아본다.
CREATE TABLE 스키마명.테이블명 (
컬럼명 데이터타입 조건,
컬럼명 데이터타입 조건
);
CREATE TABLE = 말 그대로 “테이블을 만들겠다”는 명령어
스키마명.테이블명 = “어느 스키마에 어떤 테이블을 만들지”를 정하는 부분이다.
만들어 둔 스키마는 cafe이고, 만들 테이블 이름은 customers다.
– customer_id = 컬럼명(저장할 항목 이름)
( )는 테이블에 들어갈 컬럼 목록을 감싸는 문법이다. cafe 스키마 안에 customers 테이블을 만들고, 그 안에 컬럼을 넣는다.
그리고
데이터 타입 + 조건(글자, 숫자, 날짜, 날짜+시간, 참/거짓)
주의해야 할 점
파이썬과 데이터 타입이 표기가 다르다. 아직 파이썬을 모르는 사람은 신경 쓰지 않아도 된다.

데이터 타입
글자 = VARCHAR 또는 TEXT
숫자 = INTEGER
날짜 = DATE
날짜+시간 = TIMESTAMP
참/거짓 = BOOLEAN
조건
비어 있으면 안 된다 = NOT NULL
중복되면 안 된다 = UNIQUE
기본값을 자동으로 넣는다 = DEFAULT
기본키로 사용한다 = PRIMARY KEY
즉,
name(컬럼명) VARCHAR(50) NOT NULL
name 이라는 컬럼을 만들고,
글자는 최대 50자까지 넣고,
비어 있으면 안 된다.
아래처럼 한 줄로 써도 실행됨
CREATE TABLE cafe.customers (customer_id INTEGER, name VARCHAR(50));
사람이 보기에 읽기에 편하기 위해서 줄을 나눈다.
CREATE TABLE cafe.customers (
customer_id INTEGER,
name VARCHAR(50)
);
괄호 안에 컬럼을 여러 개 적을 때는 컬럼과 컬럼을 구분하기 위해 ,(콤마)를 쓰고, 마지막 컬럼 뒤에는 붙이지 않는다.
;(세미콜론)은 SQL 문장이 끝났다는 표시다.

지금부터 처음 보는 문법이 등장할 수 있다.
외우기보다는 우선
하나하나 의미를 이해하고,
해석할 수 있는 정도까지만
보고 사용해보면서 지나가자!
5. 고객 테이블 생성
각 컬럼들이 왜 필요한지 , 어떤 타입이고 어떤 조건이 있어야 하는지
그 중에 중요한 컬럼과 이유를 한 번 생각해보자.
생각나는 데로 한 번 어딘가 메모해보고 아래를 펼쳐보자.
설명 하듯 말을 한다면 이렇게!
고객 테이블은 말 그대로
손님 정보를 저장하는 표다.
그렇다면 손님 한 명을 저장할 때
어떤 정보가 필요할까?
이름은 필요할 것이다.
연락처도 필요할 수 있다.
이메일도 있으면 좋다.
그리고 언제 가입했는지도 기록할 수 있다.
그런데 여기서 가장 중요한 항목은
단순히 이름이 아니다.
바로 고객 번호다.
왜냐하면 이름은 중복될 수 있기 때문이다.
예를 들어 카페 회원 중에김민수가 두 명 있을 수 있다.
이때 이름만으로는
두 사람을 정확히 구분하기 어렵다.
그래서 데이터베이스에서는
고객 한 명을 정확히 구분하기 위한
고유 번호를 둔다.
이 값이 고객 테이블의 PK가 된다.
표로 정리하면 이렇게!
| 항목 | 필요한 이유 | 타입 | 조건 |
|---|---|---|---|
| 고객 번호 | 고객 한 명을 구분 | 숫자 | PK, 자동 생성 |
| 이름 | 고객 이름 저장 | 글자 | 비어 있으면 안 됨 |
| 연락처 | 연락 가능한 정보 저장 | 글자 | 선택 가능 |
| 이메일 | 고객 식별/연락용 | 글자 | 중복 방지 |
| 가입일 | 가입 날짜 기록 | 날짜 | 기본값 오늘 |
고객 테이블에서 제일 중요한 컬럼은
고객 번호다.
고객 번호는
고객 한 명을 정확히 찾기 위한 기준이고,
중복되면 안 되며,
비어 있어도 안 된다.
이제 이 내용을 바탕으로
고객 테이블을 실제 SQL로 만들어본다.
5-1 SQL 실제로 작성하기!
고객 데이터 테이블 생성 SQL
CREATE TABLE cafe.customers (
customer_id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(50) NOT NULL,
phone VARCHAR(20),
email VARCHAR(100) UNIQUE,
joined_at DATE DEFAULT CURRENT_DATE
);
| SQL | 의미 |
|---|---|
CREATE TABLE cafe.customers | cafe 스키마 안에 customers 테이블 생성 |
customer_id INTEGER | 고객 번호는 숫자 |
GENERATED ALWAYS AS IDENTITY | 번호 자동 생성 |
PRIMARY KEY | 고객 한 명을 구분하는 기본키 |
name VARCHAR(50) | 이름은 최대 50자 글자 |
NOT NULL | 이름은 반드시 입력 |
phone VARCHAR(20) | 연락처는 글자 |
email VARCHAR(100) | 이메일은 최대 100자 글자 |
UNIQUE | 이메일 중복 방지 |
joined_at DATE | 가입일은 날짜 |
DEFAULT CURRENT_DATE | 입력하지 않으면 오늘 날짜 |
이 표를 보고 어떻게 작성하면 좋을지 미리 노트에 적어보고
고객 데이터 테이블 SQL을 펼쳐보고 비교해보는 것도 좋을 것 같다.
실행 결과


5-2. 생성 결과 확인!
생성 결과 확인 _ SQL
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_schema = 'cafe'
AND table_name = 'customers';
| 코드 | 의미 |
|---|---|
SELECT table_schema, table_name | 스키마 이름과 테이블 이름을 보여줘 |
FROM information_schema.tables | PostgreSQL이 관리하는 테이블 목록에서 찾아봐 |
WHERE table_schema = 'cafe' | 스키마 이름이 cafe인 것만 |
AND table_name = 'customers' | 그중 테이블 이름이 customers인 것만 |
; | SQL 문장 끝 |
아래와 같이
table_schema >>> cafe
table_name >>> customers
이렇게 확인되면 완료다.


6. 고객 데이터 입력하기
고객 데이터 입력은 INSERT INTO를 사용해 고객 샘플 데이터를 넣어보는 과정이다.
이번에 사용할 SQL을 먼저 확인하자!
| 순서 | 사용할 SQL | 목적 |
|---|---|---|
| 1 | INSERT INTO | 테이블에 새 데이터를 넣는다 |
| 2 | VALUES | 실제로 넣을 값을 적는다 |
| 3 | SELECT * FROM | 입력된 데이터를 전체 확인한다 |
| 4 | ORDER BY | 보기 좋게 정렬해서 확인한다 |
| 개념 | 의미 |
|---|---|
INSERT INTO | 어느 테이블에 데이터를 넣을지 지정 |
(name, phone, email) | 값을 넣을 컬럼 선택 |
VALUES | 실제 입력할 데이터 |
customer_id 생략 | 자동 생성되기 때문 |
joined_at 생략 | 기본값 오늘 날짜가 들어가기 때문 |
작성할 SQL을 말로 먼저 풀어보자.
먼저 customers 테이블에
고객 이름, 연락처, 이메일을 입력한다.
이때 고객 번호는 직접 입력하지 않는다.
왜냐하면 customer_id는 자동으로 만들어지도록 설정했기 때문이다.
가입일도 직접 입력하지 않는다.
왜냐하면 joined_at은 값을 넣지 않으면
오늘 날짜가 자동으로 들어가도록 설정했기 때문이다.
즉, 우리가 직접 넣을 값은
이름, 연락처, 이메일 정도다.
데이터를 넣은 뒤에는
정말 잘 들어갔는지 확인해야 한다.
그래서 SELECT로 customers 테이블을 조회한다.
그리고 고객 번호 순서대로 보기 위해ORDER BY customer_id를 붙여 정렬해서 확인한다.
정리하면 흐름은 이렇다.
말로 풀어본 것을 SQL 작성하듯 적어보자.
데이터를 입력한다(INSERT).
어느 테이블에 넣을지 지정한다(INTO cafe.customers).
(name, phone, email) 정보를 넣는다.
실제로 들어갈 정보는
('포치타', '010-1111-1111', 'pochita@example.com')
('덴지', '010-2222-2222', 'denji@example.com')
('레제', '010-3333-3333', 'reze@example.com')
퍼즐 맞추듯이 SQL 키워드와 구문을 끼워 맞춘다면
INSERT INTO cafe.customers (name, phone, email)
VALUES
('포치타', '010-1111-1111', 'pochita@example.com'),
('덴지', '010-2222-2222', 'denji@example.com'),
('레제', '010-3333-3333', 'reze@example.com');
입력하고 실행 결과 확인하기


6-1. 고객 데이터 입력 결과 확인하기
정말 제대로 들어갔는지 확인하는 방법
이때 사용하는 SQL은 SELECT다.
SELECT * -- SELECT "보여줘." * <<<모든 것
FROM cafe.customers -- "안에 있는" cafe "스키마 안에 있는 테이블에서"
ORDER BY customer_id; -- ORDER BY "정렬해서 보여줘." "뭘? customer_id를 기준으로"


이제 고객 테이블 만들기는 여기까지다.
5-1과 5-2를 다시 보면서, 아래 테이블들을 어떻게 만들면 좋을지 한 번 고민해보자.
테이블 만들기까지만 각자 시도해보고, 다음 글에서 함께 확인해보자!

난이도는 초보자 기준으로 임의로 정한 것이다.
주문 테이블부터는 FK라는 놈이 등장한다.
그러다 보면 그냥 “뭔데! 에이씨. 몰라!” 하고 싶을 수 있다.
그럴 때는 고객 테이블과 메뉴 테이블부터 만들어보면서 충분히 익히고 이해한 뒤,
천천히 다음으로 넘어가길 바란다.
7. 메뉴 테이블 직접 만들어보기 (난이도 ★☆☆☆☆)
| 컬럼 | 의미 | 타입 | 조건 |
|---|---|---|---|
menu_id | 메뉴 번호 | INTEGER | 자동 생성, PK |
name | 메뉴 이름 | VARCHAR(100) | NOT NULL |
price | 가격 | INTEGER | NOT NULL |
category | 메뉴 분류 | VARCHAR(50) | 선택 |
is_available | 판매 여부 | BOOLEAN | 기본값 TRUE |
여기서 중요한 컬럼은 menu_id다.
메뉴 이름은 나중에 바뀔 수 있다.
하지만 menu_id는 메뉴 하나를 구분하는 기준이 된다.
힌트는 이렇다.
8. 주문 테이블 직접 만들어보기(난이도★★★☆☆)
| 컬럼 | 의미 | 타입 | 조건 |
|---|---|---|---|
order_id | 주문 번호 | INTEGER | 자동 생성, PK |
customer_id | 주문한 고객 번호 | INTEGER | NOT NULL, FK |
ordered_at | 주문 날짜와 시간 | TIMESTAMP | 기본값 현재 시간 |
status | 주문 상태 | VARCHAR(20) | 기본값 'ordered' |
여기서 중요한 컬럼은 customer_id다.
왜냐하면 주문은 반드시
어떤 고객이 한 주문인지 연결되어야 하기 때문이다.
즉, orders.customer_id는customers.customer_id를 가리키는 FK가 된다.
9. 주문상세 테이블 직접 만들어보기(난이도★★★★★)
| 컬럼 | 의미 | 타입 | 조건 |
|---|---|---|---|
order_item_id | 주문상세 번호 | INTEGER | 자동 생성, PK |
order_id | 주문 번호 | INTEGER | NOT NULL, FK |
menu_id | 메뉴 번호 | INTEGER | NOT NULL, FK |
quantity | 수량 | INTEGER | NOT NULL |
unit_price | 주문 당시 가격 | INTEGER | NOT NULL |
여기서 중요한 컬럼은 order_id와 menu_id다.
order_id는
이 주문상세가 어떤 주문에 속하는지 알려준다.
menu_id는
이 주문상세가 어떤 메뉴인지 알려준다.
즉, 주문상세 테이블은
주문과 메뉴를 연결해주는 중간 테이블 역할을 한다.
