â ã¹ãã«äžèŠ§ã«æ»ã

postgres-schema
by eco2-team
ð± ìŽìœììœ(Eco²) BE
â 0ðŽ 0ð
2026幎1æ25æ¥
SKILL.md
name: postgres-schema description: PostgreSQL ì€í€ë§ ì€ê³ ê°ìŽë. DDL ìì±, í ìŽëž ì€ê³, ìžë±ì€ ì ëµ, ë§ìŽê·žë ìŽì ì ì°žì¡°. "schema", "ddl", "table", "migration", "index", "constraint" í€ìëë¡ ížëŠ¬ê±°.
PostgreSQL Schema Design Guide
Quick Reference
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Schema Design Principles â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â 1. TEXT Ʞ볞 â VARCHAR ììž (RFC íì€ë§) â
â 2. TIMESTAMPTZ íì (íì졎 볎졎) â
â 3. ENUM ì íì (ê³ ì ê° ì§í©ë§) â
â 4. Partial Index íì© (NULL ì ìž) â
â 5. CASCADE ëª
ìì ì€ì â
â â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Naming Conventions
| Type | Pattern | Example |
|---|---|---|
| Schema | ëë©ìž ì묞ì | chat, users, auth |
| Table | snake_case ë³µìí | sessions, social_accounts |
| Column | snake_case | user_id, created_at |
| PK | id | id UUID PRIMARY KEY |
| FK Column | {entity}_id | session_id, user_id |
| Index | idx_{table}_{columns} | idx_sessions_user_updated |
| Constraint | {type}_{table}_{column} | fk_user, chk_role |
Column Type Strategy
1. TEXT Ʞ볞 ìì¹
-- ì¢ì ì: êžžìŽ ë¶íì€í 컬ëŒì TEXT
content TEXT NOT NULL,
title TEXT NOT NULL,
description TEXT,
-- ëì ì: ìì êžžìŽ ì í
title VARCHAR(255) NOT NULL, -- ì 255ìžê°?
2. VARCHAR ììž (RFC/íì€ êž°ë°ë§)
-- ìŽë©ìŒ: RFC 5321 (64 + 1 + 255 = 320)
email VARCHAR(320) NOT NULL,
-- ì íë²íž: E.164 íì€ (ìµë 15ì늬 + êµê°ìœë)
phone_number VARCHAR(20) UNIQUE,
-- ISO ìœëë¥
country_code CHAR(2), -- ISO 3166-1 alpha-2
currency_code CHAR(3), -- ISO 4217
language_code VARCHAR(5), -- BCP 47 (ko-KR)
3. ENUM ì¬ì© êž°ì€
-- ì¢ì ì: ê° ì§í©ìŽ ê³ ì ëê³ ë³ê²œìŽ ë묞 겜ì°
role VARCHAR(10) NOT NULL CHECK (role IN ('user', 'assistant')),
type VARCHAR(20) NOT NULL CHECK (type IN ('text', 'image', 'generated_image')),
-- ëì: PostgreSQL ENUM íì
(ë§ìŽê·žë ìŽì
죌ì)
CREATE TYPE message_role AS ENUM ('user', 'assistant');
ENUM vs CHECK ë¹êµ:
| ë°©ì | ì¥ì | ëšì |
|---|---|---|
| CHECK | ë§ìŽê·žë ìŽì ì¬ì, ê° ì¶ê° ê°ëš | ì€í ê°ë¥ì± |
| ENUM TYPE | íì ìì ì±, ì ì¥ ê³µê° íšìš | ê° ì¶ê° ì ALTER TYPE íì |
Timestamp Rules
-- íì: TIMESTAMPTZ ì¬ì© (íì졎 볎졎)
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- êžì§: TIMESTAMP WITHOUT TIME ZONE
created_at TIMESTAMP NOT NULL, -- íì졎 ì 볎 ìì€
SQLAlchemyìì ìë ê°±ì :
updated_at: Mapped[datetime] = mapped_column(
TIMESTAMP(timezone=True),
server_default=func.now(),
onupdate=func.now(),
)
Constraint Patterns
Foreign Key
-- CASCADE: ë¶ëªš ìì ì ììë ìì (1:N ì¢
ì êŽê³)
CONSTRAINT fk_session FOREIGN KEY (session_id)
REFERENCES chat.sessions(id) ON DELETE CASCADE,
-- SET NULL: ë¶ëªš ìì ì NULLë¡ ì€ì (ì íì ì°žì¡°)
CONSTRAINT fk_assignee FOREIGN KEY (assignee_id)
REFERENCES users.accounts(id) ON DELETE SET NULL,
-- RESTRICT: ìì ììŒë©Ž ë¶ëªš ìì ë°©ì§ (볎íž)
CONSTRAINT fk_owner FOREIGN KEY (owner_id)
REFERENCES users.accounts(id) ON DELETE RESTRICT,
Check Constraints
-- ê° ê²ìŠ
CONSTRAINT chk_role CHECK (role IN ('user', 'assistant')),
CONSTRAINT chk_type CHECK (type IN ('text', 'image', 'generated_image')),
-- ë²ì ê²ìŠ
CONSTRAINT chk_message_count CHECK (message_count >= 0),
Unique Constraints
-- ëšìŒ ì ëí¬
email VARCHAR(320) UNIQUE NOT NULL,
-- ë³µí© ì ëí¬ (ëª
ëª
ë ì ìœì¡°ê±Ž)
CONSTRAINT uq_social_provider UNIQUE (provider, provider_user_id),
Index Strategy
Ʞ볞 ìžë±ì€
-- ì¡°í íšíŽ êž°ë° ìžë±ì€
CREATE INDEX idx_sessions_user_updated
ON chat.sessions(user_id, updated_at DESC);
-- íì€í 늬 ì¡°íì©
CREATE INDEX idx_messages_session_ts
ON chat.messages(session_id, timestamp DESC);
Partial Index (ì¡°ê±Žë¶ ìžë±ì€)
-- NULL ì ìžë¡ ìžë±ì€ í¬êž° ìµì í
CREATE INDEX idx_accounts_nickname
ON users.accounts(nickname)
WHERE nickname IS NOT NULL;
-- Soft delete ì§ì
CREATE INDEX idx_sessions_active
ON chat.sessions(user_id, updated_at DESC)
WHERE is_deleted = FALSE;
Soft Delete Pattern
-- ê¶ì¥: Boolean íëê·ž
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
-- ëì: ìì ìê° êž°ë¡
deleted_at TIMESTAMPTZ,
-- ìžë±ì€ìì ìì ë í ì ìž
CREATE INDEX idx_sessions_user_updated
ON chat.sessions(user_id, updated_at DESC)
WHERE is_deleted = FALSE;
Standard DDL Template
-- 1. Schema ìì±
CREATE SCHEMA IF NOT EXISTS {schema_name};
-- 2. Table ìì±
CREATE TABLE {schema_name}.{table_name} (
-- PK
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- FK
{parent}_id UUID NOT NULL,
-- Data columns
{column} {TYPE} [NOT NULL] [DEFAULT value],
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Soft delete (ì í)
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
-- Constraints
CONSTRAINT fk_{parent} FOREIGN KEY ({parent}_id)
REFERENCES {parent_schema}.{parent_table}(id) ON DELETE CASCADE,
CONSTRAINT chk_{column} CHECK ({column} IN ('val1', 'val2'))
);
-- 3. Index ìì±
CREATE INDEX idx_{table}_{columns}
ON {schema_name}.{table_name}({columns})
[WHERE condition];
-- 4. Comment (ì í)
COMMENT ON TABLE {schema_name}.{table_name} IS 'í
ìŽëž ì€ëª
';
COMMENT ON COLUMN {schema_name}.{table_name}.{column} IS 'ì»¬ëŒ ì€ëª
';
Migration Structure
migrations/schemas/
âââ 001_users_schema.sql # users ì€í€ë§
âââ 002_auth_schema.sql # auth ì€í€ë§
âââ 003_chat_schema.sql # chat ì€í€ë§ (ì ê·)
âââ README.md # ë§ìŽê·žë ìŽì
ê°ìŽë
Review Checklist
DDL 늬뷰 ì íìží í목:
- TEXT vs VARCHAR: VARCHARë RFC íì€ êž°ë°ìžê°?
- TIMESTAMPTZ: 몚ë ìê° ì»¬ëŒìŽ íì졎 í¬íšìžê°?
- FK CASCADE: ìì ëììŽ ëª ìì ìŒë¡ ì€ì ëìëê°?
- Index: 죌ì ì¡°í íšíŽì ìžë±ì€ê° ìëê°?
- Partial Index: Soft deleteë NULL 컬ëŒì ì¡°ê±Žë¶ ìžë±ì€ ì ì©?
- Naming: 컚벀ì ì ë°ë¥Žëê°?
- NOT NULL: íì 컬ëŒì NOT NULL ì ìœìŽ ìëê°?
- DEFAULT: ì ì í Ʞ볞ê°ìŽ ì€ì ëìëê°?
Reference Files
- DDL conventions: See ddl-conventions.md
- Index strategy: See index-strategy.md
Eco² Project Notes
- ëžë¡ê·ž ì°žì¡°: https://rooftopsnow.tistory.com/132
- Ʞ졎 ì€í€ë§:
migrations/schemas/users_schema.sql - ORM: SQLAlchemy 2.0 with asyncpg
ã¹ã³ã¢
ç·åã¹ã³ã¢
60/100
ãªããžããªã®åè³ªææšã«åºã¥ãè©äŸ¡
âSKILL.md
SKILL.mdãã¡ã€ã«ãå«ãŸããŠãã
+20
âLICENSE
ã©ã€ã»ã³ã¹ãèšå®ãããŠãã
+10
â説ææ
100æå以äžã®èª¬æããã
0/10
â人æ°
GitHub Stars 100以äž
0/15
âæè¿ã®æŽ»å
3ã¶æä»¥å ã«æŽæ°ããã
0/10
âãã©ãŒã¯
10å以äžãã©ãŒã¯ãããŠãã
0/5
âIssue管ç
ãªãŒãã³Issueã50æªæº
+5
âèšèª
ããã°ã©ãã³ã°èšèªãèšå®ãããŠãã
+5
âã¿ã°
1ã€ä»¥äžã®ã¿ã°ãèšå®ãããŠãã
0/5
ã¬ãã¥ãŒ
ð¬
ã¬ãã¥ãŒæ©èœã¯è¿æ¥å ¬éäºå®ã§ã