-- ============================================ -- User Service Database Schema -- Database: user_service_db -- Version: 1.0.0 -- Description: 사용자 및 가게 정보 관리 -- ============================================ -- ============================================ -- 1. 데이터베이스 및 Extension 생성 -- ============================================ -- UUID 확장 활성화 CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- ============================================ -- 2. 테이블 생성 -- ============================================ -- 2.1 users 테이블 -- 목적: 사용자(소상공인) 정보 관리 CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(100) NOT NULL, phone_number VARCHAR(20) NOT NULL, email VARCHAR(255) NOT NULL, password_hash VARCHAR(255) NOT NULL, role VARCHAR(20) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', last_login_at TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 제약조건 CONSTRAINT uk_users_email UNIQUE (email), CONSTRAINT uk_users_phone_number UNIQUE (phone_number), CONSTRAINT ck_users_role CHECK (role IN ('OWNER', 'ADMIN')), CONSTRAINT ck_users_status CHECK (status IN ('ACTIVE', 'INACTIVE', 'LOCKED', 'WITHDRAWN')) ); -- users 테이블 코멘트 COMMENT ON TABLE users IS '사용자(소상공인) 정보 테이블'; COMMENT ON COLUMN users.id IS '사용자 고유 식별자 (UUID)'; COMMENT ON COLUMN users.name IS '사용자 이름'; COMMENT ON COLUMN users.phone_number IS '전화번호 (중복 불가)'; COMMENT ON COLUMN users.email IS '이메일 (로그인 ID, 중복 불가)'; COMMENT ON COLUMN users.password_hash IS 'bcrypt 암호화된 비밀번호'; COMMENT ON COLUMN users.role IS '사용자 역할 (OWNER: 소상공인, ADMIN: 관리자)'; COMMENT ON COLUMN users.status IS '계정 상태 (ACTIVE: 활성, INACTIVE: 비활성, LOCKED: 잠김, WITHDRAWN: 탈퇴)'; COMMENT ON COLUMN users.last_login_at IS '최종 로그인 시각'; COMMENT ON COLUMN users.created_at IS '생성 시각'; COMMENT ON COLUMN users.updated_at IS '수정 시각'; -- 2.2 stores 테이블 -- 목적: 가게(매장) 정보 관리 CREATE TABLE IF NOT EXISTS stores ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL, name VARCHAR(200) NOT NULL, industry VARCHAR(100) NOT NULL, address VARCHAR(500) NOT NULL, business_hours TEXT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 제약조건 CONSTRAINT uk_stores_user_id UNIQUE (user_id), CONSTRAINT fk_stores_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE ); -- stores 테이블 코멘트 COMMENT ON TABLE stores IS '가게(매장) 정보 테이블'; COMMENT ON COLUMN stores.id IS '가게 고유 식별자 (UUID)'; COMMENT ON COLUMN stores.user_id IS '사용자 ID (FK, 1:1 관계)'; COMMENT ON COLUMN stores.name IS '가게 이름'; COMMENT ON COLUMN stores.industry IS '업종 (예: 음식점, 카페)'; COMMENT ON COLUMN stores.address IS '주소'; COMMENT ON COLUMN stores.business_hours IS '영업시간 정보'; COMMENT ON COLUMN stores.created_at IS '생성 시각'; COMMENT ON COLUMN stores.updated_at IS '수정 시각'; -- ============================================ -- 3. 인덱스 생성 -- ============================================ -- 3.1 users 테이블 인덱스 -- 로그인 조회 최적화 CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); -- 전화번호 중복 검증 최적화 CREATE INDEX IF NOT EXISTS idx_users_phone_number ON users(phone_number); -- 활성 사용자 필터링 최적화 CREATE INDEX IF NOT EXISTS idx_users_status ON users(status); -- 3.2 stores 테이블 인덱스 -- User-Store 조인 최적화 CREATE INDEX IF NOT EXISTS idx_stores_user_id ON stores(user_id); -- 인덱스 코멘트 COMMENT ON INDEX idx_users_email IS '로그인 조회 성능 최적화'; COMMENT ON INDEX idx_users_phone_number IS '전화번호 중복 검증 최적화'; COMMENT ON INDEX idx_users_status IS '활성 사용자 필터링 최적화'; COMMENT ON INDEX idx_stores_user_id IS 'User-Store 조인 성능 최적화'; -- ============================================ -- 4. 트리거 생성 (updated_at 자동 갱신) -- ============================================ -- 4.1 updated_at 갱신 함수 생성 CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = CURRENT_TIMESTAMP; RETURN NEW; END; $$ LANGUAGE plpgsql; -- 4.2 users 테이블 트리거 CREATE TRIGGER trigger_users_updated_at BEFORE UPDATE ON users FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- 4.3 stores 테이블 트리거 CREATE TRIGGER trigger_stores_updated_at BEFORE UPDATE ON stores FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- ============================================ -- 5. 초기 데이터 (Optional) -- ============================================ -- 5.1 관리자 계정 (Optional - 개발/테스트용) -- 비밀번호: admin123 (bcrypt 해시) -- INSERT INTO users (id, name, phone_number, email, password_hash, role, status) -- VALUES ( -- uuid_generate_v4(), -- 'System Admin', -- '010-0000-0000', -- 'admin@kt-event.com', -- '$2a$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5GyYCdOzHxKuK', -- 'ADMIN', -- 'ACTIVE' -- ); -- ============================================ -- 6. 권한 설정 -- ============================================ -- 애플리케이션 사용자에게 권한 부여 (사용자명은 환경에 맞게 수정) -- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO user_service_app; -- GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO user_service_app; -- ============================================ -- 7. 통계 정보 갱신 -- ============================================ -- 쿼리 플래너를 위한 통계 정보 수집 ANALYZE users; ANALYZE stores; -- ============================================ -- 8. 스키마 버전 정보 -- ============================================ -- 스키마 버전 관리 테이블 (Flyway/Liquibase 사용 시 자동 생성됨) -- CREATE TABLE IF NOT EXISTS schema_version ( -- installed_rank INT NOT NULL, -- version VARCHAR(50), -- description VARCHAR(200) NOT NULL, -- type VARCHAR(20) NOT NULL, -- script VARCHAR(1000) NOT NULL, -- checksum INT, -- installed_by VARCHAR(100) NOT NULL, -- installed_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- execution_time INT NOT NULL, -- success BOOLEAN NOT NULL, -- PRIMARY KEY (installed_rank) -- ); -- ============================================ -- 9. 검증 쿼리 -- ============================================ -- 테이블 생성 확인 SELECT table_name, table_type FROM information_schema.tables WHERE table_schema = 'public' AND table_name IN ('users', 'stores') ORDER BY table_name; -- 인덱스 생성 확인 SELECT tablename, indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' AND tablename IN ('users', 'stores') ORDER BY tablename, indexname; -- 제약조건 확인 SELECT conname AS constraint_name, contype AS constraint_type, conrelid::regclass AS table_name FROM pg_constraint WHERE conrelid IN ('users'::regclass, 'stores'::regclass) ORDER BY table_name, constraint_name; -- ============================================ -- 10. 성능 튜닝 설정 (Optional) -- ============================================ -- 테이블 통계 수집 비율 조정 (필요 시) -- ALTER TABLE users SET (autovacuum_vacuum_scale_factor = 0.05); -- ALTER TABLE stores SET (autovacuum_vacuum_scale_factor = 0.05); -- 테이블 통계 수집 임계값 조정 (필요 시) -- ALTER TABLE users SET (autovacuum_analyze_threshold = 50); -- ALTER TABLE stores SET (autovacuum_analyze_threshold = 50); -- ============================================ -- END OF SCHEMA -- ============================================