데이터베이스
Database
구조화된 데이터를 체계적으로 저장, 관리, 검색할 수 있는 시스템입니다. DBMS(Database Management System)를 통해 데이터의 생성, 조회, 수정, 삭제(CRUD) 작업과 동시성 제어, 보안, 백업/복구를 처리합니다.
Database
구조화된 데이터를 체계적으로 저장, 관리, 검색할 수 있는 시스템입니다. DBMS(Database Management System)를 통해 데이터의 생성, 조회, 수정, 삭제(CRUD) 작업과 동시성 제어, 보안, 백업/복구를 처리합니다.
데이터베이스(Database)는 조직의 핵심 자산인 데이터를 체계적으로 관리하는 시스템입니다. 파일 시스템과 달리 데이터 중복을 최소화하고, 무결성을 보장하며, 동시 접근을 제어합니다. 모든 현대 애플리케이션의 기반이 됩니다.
RDBMS(관계형 데이터베이스)는 테이블 기반으로 데이터를 저장하며, SQL을 사용합니다. PostgreSQL, MySQL, Oracle, SQL Server가 대표적입니다. ACID 트랜잭션을 보장하여 금융, 주문 등 정합성이 중요한 데이터에 적합합니다.
NoSQL은 비관계형 데이터베이스로, 유연한 스키마와 수평 확장이 장점입니다. 문서형(MongoDB), 키-값(Redis), 컬럼패밀리(Cassandra), 그래프(Neo4j) 등 다양한 유형이 있습니다. 대용량 데이터, 빠른 변화, 유연한 구조가 필요할 때 적합합니다.
NewSQL은 RDBMS의 ACID 보장과 NoSQL의 확장성을 결합한 데이터베이스입니다. CockroachDB, YugabyteDB, Google Spanner 등이 있으며, 분산 환경에서도 강한 일관성이 필요한 경우 사용됩니다.
데이터베이스 선택 시 고려 사항: 데이터 구조(정형/비정형), 일관성 요구 수준, 확장 방식(수직/수평), 쿼리 패턴(복잡한 조인/단순 조회), 트랜잭션 필요 여부, 팀의 기술 숙련도 등을 종합적으로 검토해야 합니다.
# PostgreSQL 연결 및 CRUD - Python + psycopg2
import psycopg2
from contextlib import contextmanager
class DatabaseService:
"""PostgreSQL 데이터베이스 서비스"""
def __init__(self, connection_string: str):
self.conn_string = connection_string
@contextmanager
def get_connection(self):
"""연결 관리 (Context Manager)"""
conn = psycopg2.connect(self.conn_string)
try:
yield conn
conn.commit()
except Exception as e:
conn.rollback()
raise e
finally:
conn.close()
def create_user(self, name: str, email: str) -> int:
"""사용자 생성 (Create)"""
with self.get_connection() as conn:
with conn.cursor() as cur:
cur.execute(
"""INSERT INTO users (name, email, created_at)
VALUES (%s, %s, NOW())
RETURNING id""",
(name, email)
)
return cur.fetchone()[0]
def get_user(self, user_id: int) -> dict:
"""사용자 조회 (Read)"""
with self.get_connection() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT id, name, email FROM users WHERE id = %s",
(user_id,)
)
row = cur.fetchone()
if row:
return {"id": row[0], "name": row[1], "email": row[2]}
return None
def update_user(self, user_id: int, name: str) -> bool:
"""사용자 수정 (Update)"""
with self.get_connection() as conn:
with conn.cursor() as cur:
cur.execute(
"UPDATE users SET name = %s WHERE id = %s",
(name, user_id)
)
return cur.rowcount > 0
def delete_user(self, user_id: int) -> bool:
"""사용자 삭제 (Delete)"""
with self.get_connection() as conn:
with conn.cursor() as cur:
cur.execute("DELETE FROM users WHERE id = %s", (user_id,))
return cur.rowcount > 0
# 사용 예시
db = DatabaseService("postgresql://admin:secret@localhost/myapp")
# CRUD 작업
user_id = db.create_user("홍길동", "hong@example.com")
user = db.get_user(user_id)
print(f"생성된 사용자: {user}")
// MongoDB 연결 및 CRUD - Node.js + Mongoose
const mongoose = require('mongoose');
// 스키마 정의
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model('User', userSchema);
class DatabaseService {
/**
* MongoDB 연결
*/
async connect(connectionString) {
await mongoose.connect(connectionString);
console.log('MongoDB 연결 성공');
}
/**
* 사용자 생성 (Create)
*/
async createUser(name, email) {
const user = new User({ name, email });
await user.save();
return user;
}
/**
* 사용자 조회 (Read)
*/
async getUser(userId) {
return await User.findById(userId);
}
/**
* 조건 검색
*/
async findUsers(query) {
return await User.find(query).sort({ createdAt: -1 });
}
/**
* 사용자 수정 (Update)
*/
async updateUser(userId, updates) {
return await User.findByIdAndUpdate(
userId,
updates,
{ new: true } // 수정된 문서 반환
);
}
/**
* 사용자 삭제 (Delete)
*/
async deleteUser(userId) {
const result = await User.findByIdAndDelete(userId);
return result !== null;
}
}
// 사용 예시
const db = new DatabaseService();
await db.connect('mongodb://localhost:27017/myapp');
// CRUD 작업
const user = await db.createUser('홍길동', 'hong@example.com');
console.log(`생성된 사용자: ${user.name}`);
-- ============================================
-- 데이터베이스 및 테이블 생성
-- ============================================
CREATE DATABASE myapp;
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_status ON users(status);
-- ============================================
-- CRUD 기본 작업
-- ============================================
-- Create (생성)
INSERT INTO users (name, email)
VALUES ('홍길동', 'hong@example.com');
-- Read (조회)
SELECT * FROM users WHERE status = 'active';
-- Update (수정)
UPDATE users
SET name = '김철수'
WHERE id = 1;
-- Delete (삭제)
DELETE FROM users WHERE id = 1;
-- ============================================
-- 고급 쿼리
-- ============================================
-- 조인 (JOIN)
SELECT u.name, o.order_id, o.total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.created_at > '2024-01-01';
-- 집계 함수
SELECT status, COUNT(*) as user_count
FROM users
GROUP BY status
HAVING COUNT(*) > 10;
-- 서브쿼리
SELECT name, email
FROM users
WHERE id IN (
SELECT user_id FROM orders
WHERE total > 100000
);
-- ============================================
-- 트랜잭션
-- ============================================
BEGIN;
UPDATE accounts SET balance = balance - 50000
WHERE id = 'A001';
UPDATE accounts SET balance = balance + 50000
WHERE id = 'A002';
COMMIT; -- 성공 시
-- ROLLBACK; -- 실패 시
"핵심 비즈니스 데이터는 PostgreSQL로 트랜잭션을 보장하고, 세션이나 캐시는 Redis, 로그나 분석 데이터는 MongoDB가 좋겠습니다. 각 DB의 장점을 살리는 폴리글랏 퍼시스턴스 패턴이에요."
"RDBMS는 스키마가 고정되고 ACID 트랜잭션을 보장해서 금융, 주문 같은 중요 데이터에 적합합니다. NoSQL은 스키마가 유연하고 수평 확장이 쉬워서 대용량, 빠른 변화가 필요한 데이터에 적합합니다. 실제로 커머스에서 상품/주문은 PostgreSQL, 검색은 Elasticsearch, 세션은 Redis를 조합했습니다."
"모든 데이터를 하나의 MongoDB 컬렉션에 임베딩하고 있는데, 이 데이터는 자주 변경되는 참조 데이터예요. 별도 컬렉션으로 분리하고 lookup을 쓰거나, 아예 이 부분은 RDBMS로 분리하는 것도 방법이에요. 데이터 특성에 맞게 재설계해보세요."
단순 CRUD 앱에 복잡한 DB 스택은 불필요합니다. 시작은 단순하게, 필요할 때 확장하세요. YAGNI 원칙을 기억하세요.
정기 백업과 복구 테스트는 필수입니다. 장애 시 복구 시간(RTO)과 데이터 손실 허용치(RPO)를 정의하고 대비하세요.
연결 암호화(SSL/TLS), 접근 제어(최소 권한 원칙), SQL Injection 방지(파라미터 바인딩)는 필수입니다.
커넥션 풀링 사용, 인덱스 설계 최적화, 쿼리 실행 계획 분석, 모니터링 및 알림 설정, 마이그레이션 전략 수립.