반응형

PostgreSQL Replication 서버 세팅 가이드

PostgreSQL의 Streaming Replication을 이용해 Primary-Standby 구성을 세팅하는 방법을 정리했다. 읽기 부하 분산, 장애 대비(failover), 백업 용도로 활용할 수 있다.

 

Replication 방식 비교

방식 단위 용도
Streaming Replication 전체 DB 클러스터 (물리적) HA, failover, 읽기 분산
Logical Replication 테이블 단위 (논리적) 선택적 복제, 버전 간 마이그레이션

이 글에서는 가장 일반적인 Streaming Replication을 다룬다.

 

환경 구성

역할 호스트 IP
Primary (쓰기) pg-primary 10.0.0.1
Standby (읽기) pg-standby 10.0.0.2

PostgreSQL 16 기준이며, 15 이상이면 동일하게 적용된다.

 

1. Primary 서버 설정

1-1. Replication 전용 계정 생성

-- Primary에서 실행
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'repl_password';

 

1-2. postgresql.conf 수정

# 외부 접속 허용
listen_addresses = '*'

# WAL 레벨 (replica 이상 필요)
wal_level = replica

# 동시 replication 연결 수
max_wal_senders = 5

# Standby가 따라잡기 위해 보관할 WAL 세그먼트
wal_keep_size = 1GB

# Replication 슬롯 수 (선택, 권장)
max_replication_slots = 5

 

1-3. pg_hba.conf에 Standby 접속 허용

# TYPE  DATABASE        USER          ADDRESS           METHOD
host    replication     replicator    10.0.0.2/32       scram-sha-256

 

1-4. Replication Slot 생성 (권장)

Slot을 사용하면 Standby가 꺼져 있는 동안에도 필요한 WAL이 삭제되지 않는다.

SELECT pg_create_physical_replication_slot('standby_slot');

주의: Standby가 장기간 꺼져 있으면 WAL이 무한히 쌓여 디스크가 가득 찬다. 운영 환경에서는 모니터링 필수.

 

1-5. Primary 재시작

sudo systemctl restart postgresql

 

2. Standby 서버 설정

2-1. 기존 데이터 디렉토리 비우기

# Standby에서 실행
sudo systemctl stop postgresql
sudo rm -rf /var/lib/postgresql/16/main/*

 

2-2. pg_basebackup으로 Primary 복제

Primary의 전체 데이터를 Standby로 복사한다.

sudo -u postgres pg_basebackup \
    -h 10.0.0.1 \
    -U replicator \
    -D /var/lib/postgresql/16/main \
    -Fp -Xs -P -R \
    -S standby_slot

옵션 설명:

옵션 설명
-Fp Plain 포맷 (바로 사용 가능)
-Xs WAL을 스트리밍으로 전송
-P 진행률 표시
-R standby.signal 파일과 연결 정보 자동 생성
-S 사용할 Replication Slot 이름

 

-R 옵션이 자동으로 생성하는 파일들:

# standby.signal (빈 파일, 존재 자체가 Standby 모드를 의미)

# postgresql.auto.conf에 추가됨
primary_conninfo = 'host=10.0.0.1 user=replicator password=repl_password'
primary_slot_name = 'standby_slot'

 

2-3. Standby 전용 설정 (postgresql.conf)

# Standby에서 읽기 쿼리 허용
hot_standby = on

# 읽기 쿼리와 복제 충돌 시 대기 시간 (기본 30s)
max_standby_streaming_delay = 30s

 

2-4. Standby 시작

sudo systemctl start postgresql

 

3. Replication 상태 확인

Primary에서 확인:

-- 연결된 Standby 목록
SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn,
       pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes
FROM pg_stat_replication;

 

Standby에서 확인:

-- Standby 수신 상태
SELECT status, received_lsn, latest_end_lsn,
       latest_end_time, sender_host
FROM pg_stat_wal_receiver;

-- 복제 지연 시간 (PostgreSQL 10+)
SELECT now() - pg_last_xact_replay_timestamp() AS replication_lag;

 

Slot 상태 확인:

SELECT slot_name, active, restart_lsn,
       pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS retained_bytes
FROM pg_replication_slots;

 

4. Synchronous Replication (선택)

기본 Streaming Replication은 비동기(async)다. Primary가 커밋 후 Standby에 전파하므로, Primary 장애 시 최근 트랜잭션이 유실될 수 있다.

데이터 유실이 허용되지 않는 경우 동기 모드를 사용한다.

Primary의 postgresql.conf:

# Standby의 application_name과 매칭
synchronous_standby_names = 'FIRST 1 (standby1)'

# 동기 커밋 레벨
synchronous_commit = on

 

Standby의 primary_conninfo에 application_name 추가:

primary_conninfo = 'host=10.0.0.1 user=replicator password=repl_password application_name=standby1'

 

동기 모드 레벨:

synchronous_commit 보장 범위 성능
on Standby 디스크 flush 완료 가장 느림
remote_write Standby OS에 전달 완료 중간
remote_apply Standby에서 읽기 가능 가장 느림 + 읽기 일관성

주의: 동기 모드에서 Standby가 죽으면 Primary의 모든 쓰기가 멈춘다. Standby를 2대 이상 두거나 FIRST 1 (standby1, standby2)처럼 설정해야 안전하다.

 

5. Failover (수동 승격)

Primary에 장애가 발생하면 Standby를 Primary로 승격한다.

# Standby에서 실행
sudo -u postgres pg_ctl promote -D /var/lib/postgresql/16/main

# 또는 SQL로
SELECT pg_promote();

승격 후 standby.signal 파일이 자동 삭제되고, 쓰기가 가능해진다.

 

승격 후 해야 할 일:

# 1. 애플리케이션의 DB 연결을 새 Primary(10.0.0.2)로 변경

# 2. 기존 Primary 복구 후 Standby로 전환
#    (pg_basebackup으로 새 Primary에서 다시 복제)

# 3. 기존 Replication Slot 정리 (새 Primary에서)
SELECT pg_drop_replication_slot('standby_slot');

 

6. 자동 Failover (Patroni)

수동 failover는 운영에서 한계가 있다. 자동 failover가 필요하면 Patroni를 사용한다.

# 아키텍처
┌─────────┐     ┌─────────┐     ┌─────────┐
│ Patroni │     │ Patroni │     │  etcd    │
│ + PG    │────▶│ + PG    │────▶│ cluster  │
│ Primary │     │ Standby │     │ (DCS)    │
└─────────┘     └─────────┘     └─────────┘
      │               │
      └───────┬───────┘
              ▼
        ┌───────────┐
        │ HAProxy / │
        │ PgBouncer │
        └───────────┘

 

Patroni 최소 설정 (patroni.yml):

scope: pg-cluster
name: pg-node1

etcd3:
  hosts: 10.0.0.10:2379

bootstrap:
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576  # 1MB
    postgresql:
      use_pg_rewind: true
      parameters:
        wal_level: replica
        max_wal_senders: 5
        max_replication_slots: 5

postgresql:
  listen: 0.0.0.0:5432
  connect_address: 10.0.0.1:5432
  data_dir: /var/lib/postgresql/16/main
  authentication:
    superuser:
      username: postgres
      password: postgres_pass
    replication:
      username: replicator
      password: repl_password

Patroni가 리더 선출, 자동 failover, pg_rewind를 통한 기존 Primary 재합류를 모두 처리한다.

 

7. Logical Replication (테이블 단위)

특정 테이블만 복제하거나, 다른 버전의 PostgreSQL 간 복제가 필요할 때 사용한다.

Primary (Publisher):

# postgresql.conf
wal_level = logical

-- Publication 생성
CREATE PUBLICATION my_pub FOR TABLE users, orders;

-- 또는 전체 테이블
CREATE PUBLICATION my_pub FOR ALL TABLES;

 

Standby (Subscriber):

-- 테이블 스키마는 미리 생성되어 있어야 함

-- Subscription 생성
CREATE SUBSCRIPTION my_sub
    CONNECTION 'host=10.0.0.1 dbname=myapp user=replicator password=repl_password'
    PUBLICATION my_pub;

 

Streaming vs Logical 비교:

항목 Streaming Logical
복제 단위 전체 클러스터 테이블 선택 가능
Standby 쓰기 불가 (읽기 전용) 가능
PG 버전 호환 동일 메이저 버전 다른 버전 가능
DDL 복제 자동 수동 (스키마 변경 별도)
Failover promote 가능 promote 불가
성능 오버헤드 낮음 상대적으로 높음

 

8. 운영 모니터링 쿼리 모음

-- 1. 복제 지연 (Primary에서)
SELECT client_addr,
       state,
       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS lag_pretty
FROM pg_stat_replication;

-- 2. 복제 지연 시간 (Standby에서)
SELECT CASE
    WHEN pg_last_wal_replay_lsn() = pg_last_wal_receive_lsn() THEN 0
    ELSE EXTRACT(EPOCH FROM now() - pg_last_xact_replay_timestamp())
END AS lag_seconds;

-- 3. Slot의 WAL 보관량 (디스크 사용량 감시)
SELECT slot_name, active,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots;

-- 4. 현재 서버가 Primary인지 Standby인지 확인
SELECT pg_is_in_recovery();  -- true = Standby, false = Primary

 

정리: 단계별 체크리스트

단계 서버 작업
1 Primary replication 계정 생성
2 Primary postgresql.conf 수정 (wal_level, max_wal_senders)
3 Primary pg_hba.conf에 Standby 허용
4 Primary Replication slot 생성, 재시작
5 Standby pg_basebackup -R 실행
6 Standby hot_standby = on, 시작
7 양쪽 pg_stat_replication / pg_stat_wal_receiver 확인

 

HA가 필요하면 Streaming Replication + Patroni 조합이 가장 검증된 구성이다. 테이블 단위 선택적 복제나 버전 간 마이그레이션이 필요한 경우에만 Logical Replication을 사용한다.

반응형
반응형

PostgreSQL은 기본 설정이 매우 보수적이다. 서버 사양에 맞게 튜닝하지 않으면 하드웨어의 10%도 활용하지 못한다. 실무에서 효과가 큰 순서대로 정리했다.

 

1. 메모리 설정

가장 효과가 큰 튜닝 영역이다. 대부분의 성능 문제는 여기서 해결된다.

shared_buffers

PostgreSQL이 데이터를 캐시하는 공유 메모리 영역이다. 기본값은 128MB로 너무 작다.

# 권장: 전체 RAM의 25%
# 16GB RAM 서버 기준
shared_buffers = 4GB

 

effective_cache_size

OS 파일 캐시를 포함하여 PostgreSQL이 사용할 수 있는 총 캐시 크기의 추정치다. 쿼리 플래너가 인덱스 스캔 여부를 결정할 때 참고한다. 실제 메모리를 할당하지는 않는다.

# 권장: 전체 RAM의 50~75%
effective_cache_size = 12GB

 

work_mem

정렬, 해시 조인 등 쿼리 내부 연산에 사용하는 메모리다. 연결 x 쿼리 x 연산 수만큼 곱해지므로 너무 크게 잡으면 OOM이 발생한다.

# 공식: RAM / max_connections / 4 (대략적 기준)
# 16GB RAM, 200 connections 기준
work_mem = 20MB

 

maintenance_work_mem

VACUUM, CREATE INDEX, ALTER TABLE 등 유지보수 작업에 사용하는 메모리다.

# 권장: 512MB ~ 2GB
maintenance_work_mem = 1GB

 

16GB RAM 서버 기준 요약:

파라미터 기본값 권장값
shared_buffers 128MB 4GB
effective_cache_size 4GB 12GB
work_mem 4MB 20MB
maintenance_work_mem 64MB 1GB

 

2. WAL (Write-Ahead Log) 설정

쓰기 성능에 직접적인 영향을 주는 설정이다.

wal_buffers

# shared_buffers의 1/32, 최소 64MB
wal_buffers = 128MB

 

checkpoint 관련

체크포인트가 너무 자주 발생하면 I/O 스파이크가 생긴다.

# 체크포인트 간격 늘리기 (기본 5min)
checkpoint_timeout = 15min

# WAL 최대 크기 (기본 1GB)
max_wal_size = 4GB
min_wal_size = 1GB

# 체크포인트 I/O를 분산 (기본 0.9)
checkpoint_completion_target = 0.9

 

synchronous_commit

트랜잭션 커밋 시 WAL이 디스크에 기록될 때까지 기다릴지 결정한다.

# 쓰기 성능 향상 (약간의 데이터 손실 가능성 감수)
synchronous_commit = off

주의: off로 설정하면 크래시 시 최근 수백 ms의 트랜잭션이 유실될 수 있다. 로그성 데이터에는 괜찮지만, 결제 등 중요 데이터에는 on을 유지해야 한다.

 

3. 커넥션 관리

max_connections

PostgreSQL은 커넥션당 프로세스를 fork한다. 커넥션이 많을수록 컨텍스트 스위칭 비용이 증가한다.

# 직접 연결: 실제 필요한 수 + 여유분
max_connections = 200

 

Connection Pooler 사용 (권장)

애플리케이션이 수백~수천 커넥션을 사용한다면 PostgreSQL에 직접 연결하지 말고 PgBouncer를 사용한다.

# pgbouncer.ini
[databases]
myapp = host=127.0.0.1 port=5432 dbname=myapp

[pgbouncer]
listen_port = 6432
pool_mode = transaction    # 트랜잭션 단위로 커넥션 재사용
max_client_conn = 1000     # 클라이언트 최대 연결
default_pool_size = 50     # DB 실제 연결 수

이렇게 하면 PostgreSQL의 max_connections는 100 이하로 낮추고, 애플리케이션은 PgBouncer에 1000개까지 연결할 수 있다.

 

4. VACUUM과 Autovacuum

PostgreSQL은 MVCC 구조상 UPDATE/DELETE 후 죽은 튜플(dead tuple)이 쌓인다. VACUUM이 이를 정리한다. Autovacuum이 꺼져 있거나 너무 느리면 테이블 bloat과 성능 저하가 발생한다.

# autovacuum 기본 활성화 (절대 끄지 말 것)
autovacuum = on

# 워커 수 (대규모 DB에서 증가)
autovacuum_max_workers = 5

# 더 자주, 더 빠르게 실행
autovacuum_vacuum_cost_delay = 2ms      # 기본 2ms (이전 버전은 20ms)
autovacuum_vacuum_cost_limit = 1000     # 기본 -1 (vacuum_cost_limit 사용)

# 트리거 임계값 조정
autovacuum_vacuum_threshold = 50        # 기본 50
autovacuum_vacuum_scale_factor = 0.05   # 기본 0.2 (5%로 낮추면 더 자주 실행)

 

큰 테이블에 대한 개별 설정:

-- 1억 행 테이블은 scale_factor 0.01 (1%)로 설정
ALTER TABLE huge_table SET (
    autovacuum_vacuum_scale_factor = 0.01,
    autovacuum_analyze_scale_factor = 0.005
);

 

dead tuple 확인:

SELECT relname, n_dead_tup, n_live_tup,
       round(n_dead_tup::numeric / NULLIF(n_live_tup, 0) * 100, 2) AS dead_pct,
       last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;

 

5. 쿼리 분석 (EXPLAIN ANALYZE)

튜닝의 핵심은 느린 쿼리를 찾아서 고치는 것이다.

느린 쿼리 로깅 활성화:

# 200ms 이상 걸리는 쿼리 로깅
log_min_duration_statement = 200ms

 

EXPLAIN ANALYZE 사용법:

EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT * FROM orders
WHERE user_id = 12345
  AND created_at > '2025-01-01';

 

실행 계획에서 주의할 키워드:

키워드 의미 대응
Seq Scan 전체 테이블 스캔 인덱스 추가 검토
Sort (disk) 디스크 정렬 발생 work_mem 증가 또는 인덱스
Hash Join (batches > 1) 해시가 메모리 초과 work_mem 증가
Nested Loop (rows 큼) 비효율적 조인 조인 조건/인덱스 확인

 

6. 인덱스 전략

기본 B-tree 인덱스:

-- 단일 컬럼
CREATE INDEX idx_orders_user_id ON orders (user_id);

-- 복합 인덱스 (선택도 높은 컬럼을 앞에)
CREATE INDEX idx_orders_user_created ON orders (user_id, created_at DESC);

 

Partial Index (조건부 인덱스):

특정 조건의 행만 인덱싱하여 크기를 줄이고 성능을 높인다.

-- 활성 주문만 인덱싱
CREATE INDEX idx_orders_active ON orders (created_at)
    WHERE status = 'active';

-- NULL이 아닌 행만
CREATE INDEX idx_users_email ON users (email)
    WHERE email IS NOT NULL;

 

Covering Index (INCLUDE):

인덱스만으로 쿼리를 완료하여 테이블 접근을 제거한다 (Index-Only Scan).

-- user_id로 검색하고 name, email을 반환하는 쿼리에 최적
CREATE INDEX idx_users_covering ON users (user_id) INCLUDE (name, email);

 

사용하지 않는 인덱스 찾기:

SELECT indexrelname, idx_scan, pg_size_pretty(pg_relation_size(indexrelid)) AS size
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;

사용되지 않는 인덱스는 쓰기 성능만 떨어뜨린다. 과감히 삭제한다.

 

7. 디스크 I/O 설정

# SSD 사용 시 (random I/O가 빠르므로 낮게)
random_page_cost = 1.1        # 기본 4.0 (HDD 기준)
seq_page_cost = 1.0

# 동시 I/O 워커
effective_io_concurrency = 200   # SSD: 200, HDD: 2

random_page_cost를 낮추면 플래너가 인덱스 스캔을 더 적극적으로 선택한다. SSD를 사용하는데 기본값(4.0)을 두면 불필요한 Seq Scan이 발생할 수 있다.

 

8. pg_stat_statements로 모니터링

실제 운영에서 느린 쿼리를 찾는 가장 효과적인 방법이다.

# postgresql.conf
shared_preload_libraries = 'pg_stat_statements'

# DB에서 확장 활성화
CREATE EXTENSION pg_stat_statements;

 

총 실행 시간 기준 상위 쿼리:

SELECT query,
       calls,
       round(total_exec_time::numeric, 2) AS total_ms,
       round(mean_exec_time::numeric, 2) AS avg_ms,
       rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

 

I/O가 많은 쿼리:

SELECT query,
       shared_blks_read + shared_blks_hit AS total_blks,
       round(100.0 * shared_blks_hit / NULLIF(shared_blks_read + shared_blks_hit, 0), 2) AS hit_pct
FROM pg_stat_statements
ORDER BY shared_blks_read DESC
LIMIT 10;

hit_pct가 낮은 쿼리는 캐시 미스가 잦다는 뜻이므로 인덱스나 쿼리 최적화가 필요하다.

 

9. 전체 설정 요약 (postgresql.conf)

16GB RAM, SSD, 200 커넥션 기준 템플릿이다.

# Memory
shared_buffers = 4GB
effective_cache_size = 12GB
work_mem = 20MB
maintenance_work_mem = 1GB

# WAL
wal_buffers = 128MB
checkpoint_timeout = 15min
max_wal_size = 4GB
min_wal_size = 1GB
checkpoint_completion_target = 0.9

# Disk I/O (SSD)
random_page_cost = 1.1
effective_io_concurrency = 200

# Connections
max_connections = 200

# Autovacuum
autovacuum_max_workers = 5
autovacuum_vacuum_scale_factor = 0.05

# Logging
log_min_duration_statement = 200ms
shared_preload_libraries = 'pg_stat_statements'

 

정리: 튜닝 우선순위

순위 영역 효과
1 쿼리 최적화 + 인덱스 10x ~ 1000x
2 메모리 설정 2x ~ 10x
3 커넥션 풀링 2x ~ 5x
4 WAL / Checkpoint 1.5x ~ 3x (쓰기)
5 Autovacuum 튜닝 bloat 방지 (장기적)
6 디스크 I/O 설정 1.2x ~ 2x

 

설정을 변경하기 전에 pg_stat_statements로 현재 병목을 먼저 파악하는 것이 가장 중요하다. 병목을 모르고 설정만 바꾸는 것은 효과가 제한적이다.

반응형
반응형

PostgreSQL 계정 및 권한 관리 가이드

PostgreSQL에서 DB 생성부터 계정 생성, 권한 부여/삭제, 스키마 변경 시 권한 자동 유지까지 실무에서 자주 쓰는 명령어를 정리했습니다.

 

1. 데이터베이스 생성

-- 기본 생성
CREATE DATABASE myapp;

-- 인코딩, 소유자 지정
CREATE DATABASE myapp
    OWNER myuser
    ENCODING 'UTF8'
    LC_COLLATE 'en_US.UTF-8'
    LC_CTYPE 'en_US.UTF-8';

 

2. 계정(Role) 생성

PostgreSQL에서는 사용자와 그룹 모두 ROLE을 사용합니다.

-- 일반 사용자
CREATE ROLE myuser WITH LOGIN PASSWORD 'secretpass';

-- DB 생성 권한 포함
CREATE ROLE myuser WITH LOGIN PASSWORD 'secretpass' CREATEDB;

-- Superuser
CREATE ROLE myadmin WITH LOGIN PASSWORD 'adminpass' SUPERUSER;

 

주요 옵션 정리:

옵션 설명
LOGIN 접속 허용 (없으면 그룹 역할만 가능)
SUPERUSER 모든 권한 (주의해서 사용)
CREATEDB DB 생성 권한
CREATEROLE 역할 생성 권한

 

3. 권한 부여 (GRANT)

DB 접속 권한:

GRANT CONNECT ON DATABASE myapp TO myuser;

 

스키마 사용 권한:

GRANT USAGE ON SCHEMA public TO myuser;

-- 스키마 내 객체 생성까지 허용
GRANT USAGE, CREATE ON SCHEMA public TO myuser;

 

테이블 권한:

-- 특정 테이블
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE users TO myuser;

-- 스키마 내 모든 기존 테이블
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO myuser;

-- 읽기 전용
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;

 

시퀀스 권한 (INSERT 시 serial/identity 컬럼 사용에 필요):

GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO myuser;

 

4. 스키마 변경 시 권한 자동 유지 (DEFAULT PRIVILEGES)

GRANT ON ALL TABLES실행 시점의 기존 테이블에만 적용됩니다. 이후 새로 생성되는 테이블에는 권한이 없습니다. 이 문제를 해결하는 것이 ALTER DEFAULT PRIVILEGES입니다.

-- 앞으로 생성되는 테이블에 자동 권한 부여
ALTER DEFAULT PRIVILEGES IN SCHEMA public
    GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO myuser;

-- 앞으로 생성되는 시퀀스에 자동 권한 부여
ALTER DEFAULT PRIVILEGES IN SCHEMA public
    GRANT USAGE ON SEQUENCES TO myuser;

-- 읽기 전용 계정용
ALTER DEFAULT PRIVILEGES IN SCHEMA public
    GRANT SELECT ON TABLES TO readonly_user;

주의: ALTER DEFAULT PRIVILEGES실행한 역할(Role)이 생성하는 객체에만 적용된다. 예를 들어, postgres로 실행했으면 postgres가 만드는 테이블에만 적용된다. 다른 역할이 만드는 객체에도 적용하려면 FOR ROLE을 지정해야 한다.

-- deployer 역할이 생성하는 테이블에도 적용
ALTER DEFAULT PRIVILEGES FOR ROLE deployer IN SCHEMA public
    GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO myuser;

 

5. 실전: 완전한 권한 설정 스크립트

새 DB와 계정을 만들고, 기존 + 미래 테이블 모두에 권한을 부여하는 전체 흐름입니다.

-- 1) 계정 생성
CREATE ROLE myuser WITH LOGIN PASSWORD 'secretpass';

-- 2) DB 생성
CREATE DATABASE myapp OWNER postgres;

-- 3) DB 접속 (psql -d myapp 또는 \c myapp)

-- 4) 스키마 권한
GRANT CONNECT ON DATABASE myapp TO myuser;
GRANT USAGE ON SCHEMA public TO myuser;

-- 5) 기존 객체 권한
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO myuser;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO myuser;

-- 6) 미래 객체 자동 권한
ALTER DEFAULT PRIVILEGES IN SCHEMA public
    GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO myuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
    GRANT USAGE ON SEQUENCES TO myuser;

 

6. 권한 삭제 (REVOKE)

GRANT의 반대다. 문법도 거의 동일합니다.

-- 테이블 권한 삭제
REVOKE INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public FROM myuser;

-- 스키마 권한 삭제
REVOKE USAGE ON SCHEMA public FROM myuser;

-- DB 접속 권한 삭제
REVOKE CONNECT ON DATABASE myapp FROM myuser;

-- DEFAULT PRIVILEGES 삭제
ALTER DEFAULT PRIVILEGES IN SCHEMA public
    REVOKE SELECT, INSERT, UPDATE, DELETE ON TABLES FROM myuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
    REVOKE USAGE ON SEQUENCES FROM myuser;

 

7. 권한 확인

-- 역할 목록 확인
\du

-- 특정 테이블 권한 확인
\dp users

-- DB 권한 확인
\l

-- DEFAULT PRIVILEGES 확인
\ddp

 

8. 계정 삭제

계정을 삭제하려면 소유 객체와 권한을 먼저 정리해야 합니다.

-- 소유 객체를 다른 역할로 이전
REASSIGN OWNED BY myuser TO postgres;

-- 남은 권한 모두 삭제
DROP OWNED BY myuser;

-- 계정 삭제
DROP ROLE myuser;

 

정리

목적 명령
기존 테이블 권한 GRANT ... ON ALL TABLES
미래 테이블 자동 권한 ALTER DEFAULT PRIVILEGES
권한 회수 REVOKE ... FROM
계정 삭제 REASSIGN OWNED BY + DROP OWNED BY + DROP ROLE

핵심은 GRANT ON ALL TABLESALTER DEFAULT PRIVILEGES함께 사용해야 기존 + 미래 테이블 모두 권한이 적용된다는 것 입니다.

반응형
반응형

SMTP 테스트: 첨부파일 포함 메일 보내기

SMTP 서버를 개발하거나 디버깅할 때, 첨부파일 포함 메일을 빠르게 보내는 방법에 대해서 정리해 보았습니다.

1. swaks (추천)

SMTP 전용 테스트 도구로, 첨부파일을 포함한 거의 모든 시나리오를 한 줄로 처리가능.

# 설치
sudo apt install swaks   # Debian/Ubuntu
brew install swaks       # macOS

# 첨부파일 포함 전송
swaks --to user@example.com --from sender@example.com \
      --server localhost --port 2525 \
      --attach report.pdf \
      --auth --auth-user user --auth-password pass

--attach 옵션 하나로 끝. 여러 파일은 --attach를 반복하면 됩니다.

2. Python

별도 설치 없이 Python 표준 라이브러리만으로 가능.

python3 -c "
import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg['From'] = 'from@example.com'
msg['To'] = 'to@example.com'
msg['Subject'] = 'Test'
msg.set_content('Hello')

with open('report.pdf', 'rb') as f:
    msg.add_attachment(f.read(), maintype='application',
                       subtype='octet-stream', filename='report.pdf')

s = smtplib.SMTP('localhost', 2525)
s.login('user', 'pass')
s.send_message(msg)
s.quit()
"

EmailMessage가 MIME multipart 구조를 자동으로 만들어 줌.

3. curl

curl은 SMTP를 지원하지만, 첨부파일은 MIME 형식의 raw 이메일 파일을 직접 작성해야 함.

먼저 이메일 파일(email.txt)을 작성한다:

From: sender@example.com
To: user@example.com
Subject: Test with attachment
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="BOUNDARY"

--BOUNDARY
Content-Type: text/plain

Hello, this is the body.

--BOUNDARY
Content-Type: application/octet-stream; name="report.pdf"
Content-Disposition: attachment; filename="report.pdf"
Content-Transfer-Encoding: base64

(base64 인코딩된 파일 내용)
--BOUNDARY--

첨부파일을 base64로 인코딩해서 넣고 전송:

# 첨부파일 인코딩
base64 report.pdf >> email.txt
echo "--BOUNDARY--" >> email.txt

# 전송
curl --url smtp://localhost:2525 \
     --mail-from sender@example.com \
     --mail-rcpt user@example.com \
     --upload-file email.txt \
     --user "user:pass"

정리

도구 첨부파일 난이도
swaks --attach 한 줄 쉬움
Python add_attachment() 쉬움
curl MIME 직접 작성 번거로움

간단한 테스트라면 swaks가 가장 편하고, 설치 없이 해야 한다면 Python이 차선입니다.

반응형
반응형

Vite를 이용해서 React 프로젝트를 시작하는 예시입니다.

 

Vite 이란?

Vite은 한마디로 웹 빌드 툴입니다. 아래 링크에서 자세한 내용을 확인할 수 있습니다.

Vite | Next Generation Frontend Tooling

 

Vite을 사용하면, 빠른 HMR이 지원되어 변경한 코드를 바로 확인할 수 있으며, 운영환경에 배포될 에셋들을 최적화시킬 수 있습니다. Vite은 frontend framework 를 관리해주는 빌드 툴이라고 생각하면 될거 같습니다.

Vite은 react 뿐 아니라 vanilla, vue 등의 template도 지원합니다. 여기서는 react에 focus하도록 하겠습니다.

 

 

React 프로젝트 시작하기

Vite를 이용해서 프로젝트를 시작하려면, node.js 가 설치되어 있어야 합니다. 18이상을 지원한다고 되어 있는데, 저는 LTS 버전인 22버전을 사용했습니다.

 

아래 명령어를 실행합니다.

npm create vite@latest

 

먼저 프로젝트 이름을 지정합니다. 저는 그냥 enter를 입력해서 "vite-project" 라는 이름으로 진행하겠습니다.

 

다음은 framework를 선택합니다. Arrow Key (up, down)을 이용해서 이동가능하고, 여기서는 React를 선택합니다.

 

 

마지막으로 variant를 선택합니다. 저는 TypeScript를 선택했습니다.

 

프로젝트가 생성이 되었습니다.

아래와 같은 메시지가 나올 것입니다.

 

처음에 프로젝트 이름을 vite-project로 지정했기 때문에, 같은 이름으로 프로젝트 폴더가 생성되었습니다.

 

아래 명령으로 프로젝트 폴더로 이동합니다.

cd vite-project

 

그리고, 필요한 모듈을 설치해 줍니다. (npm install 과 같은 명령어 입니다.)

npm i

 

모듈이 설치되면, 아래 명령어로 실행합니다.

npm run dev

 

아래와 비슷한 화면이 나오면서 실행되면 정상입니다.

 

웹 브라우저를 열어서 주소창에 http://localhost:5173 을 입력합니다. 그러면 아래와 같은 기본 웹 앱이 나옵니다.

 

가운데, count is 0 라고 되어 있는 버튼을 누르면 카운트가 올라갑니다.

 

 

vscode 사용하기

vscode 사용자의 경우, vite-project 폴더에서 다음과 같이 입력합니다. 그러면 vscode 프로그램이 현재 프로젝트 폴더를 열어줍니다.

code .

 

아래와 같이 나옵니다.

 

 

이제부터 vscode를 이용하여 프로젝트를 편집할 수 있습니다.

 

반응형

'프로그래밍 > React' 카테고리의 다른 글

[React] npx create-react-app issue  (0) 2025.01.28
[React] Component 와 속성  (0) 2021.09.11
[React] 시작하기  (0) 2021.08.20
반응형

오랜만에 리액트 앱을 개발할 일이 생겨서 체크해 보다가 이상한 이슈를 만나게 되었습니다. 

 

대략 아래와 같은 에러 메시지가 나옵니다.

npm ERR! node_modules/react
npm ERR!   react@"^19.0.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^18.0.0" from @testing-library/react@13.4.0
npm ERR! node_modules/@testing-library/react
npm ERR!   @testing-library/react@"^13.0.0" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.

`npm install --no-audit --save @testing-library/jest-dom@^5.14.1 @testing-library/react@^13.0.0 @testing-library/user-event@^13.2.1 web-vitals@^2.1.0` failed

 

내용을 살펴보면.. react 19를 설치했는데, @testing-library/react@13.4.0 은 react 18을 필요로 한다.. 는 이야기네요..


stackoverflow 나 facebook github 이슈에 리포팅 되어 있는 이슈지만 해결책은 react 18로 다운 그레이드하라는 식의 이야기 밖에 없네요.. 사실 react 18로 다운 그레이드 하는 것도 수월하지 않고.. 근본적인 해결책은 아니다 싶었습니다.

 

 

문제 해결을 위해 원인이 무엇인가에 대해서 살펴 보았습니다..

 

먼저 npx로 생성하는 리액트 앱의 dependencies를 살펴보면, "cra-template", "react", "react-dom", "react-scripts" 이렇게 4가지로 나오네요..

 

react 와 @testing-library/react 간의 호환성 문제였기 때문에, 위의 4개 모듈 중에 어떤 모듈이 @testing-library/react 를 설치하는지 확인해봐야 겠습니다.

 

확인을 위해, temp 폴더를 생성하고 각각의 모듈을 설치해서 dependencies를 살펴보았습니다. 그 결과 cra-template 모듈의 template.json 파일에서 @testing-library들이 포함되어 있는 것을 확인하였습니다.

 

temp 폴더에 설치한 cra-template 모듈에 있는 template.json 파일을 열고 @testing-library/react의 버전을 편집합니다. (temp/node_modules/cra-template/template.json)

npm 사이트에서 확인해 본 결과, 현재16.2.0 이 최신이네요.. 

업데이트 하는 김에, 다른 dependencies들의 버전도 모두 최신버전으로 올려주었습니다. (아래 캡쳐 화면 참고)

 

cra-template 모듈을 편집했다면, 이제 실제 react 프로젝트를 생성하면 되는데, 생성시 template을 우리가 편집한 cra-tempate 모듈을 바라볼 수 있게 옵션으로 지정해 줍니다.

 

아래와 같은 폴더 구조에서, Projects/react 위치에서 다음과 같이 template 옵션을 지정하여 실행합니다.

npx create-react-app my-app --template file:./temp/node_modules/cra-template

 

그러면 오류 없이 프로젝트가 잘 생성될 것입니다.

 

 

결론적으로 cra-template 모듈을 관리하는 facebook 측에서 dependencies를 업데이트 해줘야 해결이 되는 문제인데.. facebook 에서는 정책을 변경해서, react 프로젝트 생성 시, framework와 함께 프로젝트를 생성하는 방법을 권장하고 있습니다. 

 

https://react.dev/learn/start-a-new-react-project

 

Start a New React Project – React

The library for web and native user interfaces

react.dev

 

내용을 간략히 요약하자면, 웹 사이트 혹은 웹 앱을 개발할 때, react 를 이용하는 framework을 사용하는 것을 권장하니, 이제부터는 create-react-app은 대신, create-next-app 이나 create-remix 등의 명령어를 이용해서 프로젝트를 생성하라고 하네요..

 

여튼 아직 create-react-app을 필요로 하시는 분들을 위해 작성한 포스팅이니, 잘 활용하시면 좋겠습니다.

 

 

요약

1. react 19는 create-react-app 으로 프로젝트 생성시 에러가 발생함

2. 이유는 create-react-app 에서 참조하는 모듈 cra-template 에서, react 18을 참조하는 오래된 라이브러리를 참조하고 있기 때문임

3. 이를 우회하기 위해서, cra-template 을 다운로드 해서, 참조 모듈의 버전을 올려주고,

4. create-react-app 명령어 실행시 편집한 버전의 cra-template 경로를 template 옵션으로 지정해 주면 됨

 

 

반응형

'프로그래밍 > React' 카테고리의 다른 글

[React] vite를 이용하여 프로젝트 시작하기  (1) 2025.06.15
[React] Component 와 속성  (0) 2021.09.11
[React] 시작하기  (0) 2021.08.20
반응형

먼저 간략하게 Singleton 패턴이 무엇인지 알아보고 진행하겠습니다.

 

Singleton 패턴 이란?

Singleton 패턴은 전체 프로그램 스콥에서, 특정 클래스를 사용할 때, 단 하나의 인스턴스만 생성해서 사용하는 패턴입니다. 하나의 인스턴스를 사용하게 되면 다음과 같은 장점이 있습니다.

 

- 공유 자원에 대해서 동시 접근을 제한할 수 있습니다.

- 전역에서 사용가능한 리소스를 생성할 수 있습니다.

- 프로그램 스콥에서 단 하나의 인스턴스만 생성해서 사용하기 때문에 메모리 낭비를 막을 수 있습니다.

 

모듈 레벨의 Singleton

python에서는 기본적으로 모든 module 은 singleton 으로 정의되어 있습니다. 다음의 예시는 하나의 프로그램 내에서 실행하거나 혹은 동일 shell에서 실행해야 확인이 가능합니다.

 

## singleton.py

# shared_variable에 초기값 지정
shared_variable = "init value"

 

## module1.py

import singleton

# 원래 shared_variable에 있는 내용을 확인
print(singleton.shared_variable)

# shared_variable에 내용을 추가
singleton.shared_variable += ", some text"

 

## module2.py

import singleton

# shared_variable에 있는 내용을 확인
print(singleton.shared_variable)

 

실행결과

 

 

클래스 레벨의 Singleton

이번에는 클래스를 정의해서 singleton 패턴을 구현해 보도록 하겠습니다. 

 

python에서는 클래스의 생성자에서 특정 attribute을 정의해서 항상 같은 값을 바라보는 결과를 리턴하게 처리하면 됩니다.

class SingletonClass():
    def __new__(cls):
        if not hasattr(cls, "instance"):
            cls.instance = super().__new__(cls)
        return cls.instance

 

 

python shell에서 다음과 같이 실행해 보면, SingletonClass로 생성한 instance의 결과가 같다는 것을 알 수 있습니다.

 

 

 

 

반응형

'프로그래밍 > Python' 카테고리의 다른 글

decorator 사용하기  (0) 2024.06.06
9. 파일 관리  (0) 2019.02.21
8. 파일 읽고 쓰기  (0) 2019.02.21
7. 정규 표현식  (0) 2019.01.09
6. 문자열 다루기  (0) 2019.01.08
반응형

이번에는 PostgreSQL 데이터베이스를 백업하고 복구하는 방법에 대해서 살펴보겠습니다. database 이름은 testdb 라고 가정하였으며, 백업해서 생성할 파일은 db_backup_file.sql 입니다.

 

DB 백업하기

DB 백업을 위해서는 pg_dump 명령어를 사용합니다.

 

백업시에 많이 사용되는 기본 옵션들과 같이 적어 보았습니다.

pg_dump -h <server_host> -p <port> -U <username> -d <db_name> -f <file_name>

 

 

만약 DB 서버에서 root 권한으로 백업을 한다면, 아래와 같이 db_name과 file_name만 적어줘도 됩니다.

pg_dump testdb -f db_backup_file.sql

 

 

f 옵션으로 파일 이름을 지정하는 대신 redirect를 이용할 수도 있습니다.

pg_dump testdb > db_backup_file.sql

 

 

DB 복구하기

위에서 백업한 파일은 sql 구문형태로 저장이 됩니다. 물론 복구시의 최적화를 위해, 제약사항들을 나중에 추가하는 방식으로 기록됩니다. 따라서 데이터 복구를 위해서는 sql 파일을 실행해 주면 됩니다.

psql -h <server_host> -p <port> -U <username> -d <db_name> -f <file_name>

 

혹은

psql -h <server_host> -p <port> -U <username> -d <db_name> < <file_name>

 

 

DB서버에서 root 권한으로 복구를 한다면, db_name과 file_name 만 있으면 됩니다.

psql testdb -f db_backup_file.sql

 

혹은

psql testdb < db_backup_file.sql

 

 

대용량 DB 다루기

1. 대용량 DB를 백업하거나 복구할 때는, gzip / gunzip 을 이용할 수 있습니다.

pg_dump testdb | gzip > db_backup_file.sql.gz

 

 

복구시에는 반대로 압축을 풀면서 스트림을 전달해 주면 됩니다.

gunzip -c db_backup_file.sql.gz | psql testdb

 

 

아니면 cat 명령어를 이용하여 다음과 같이 사용해도 결과는 같습니다.

cat db_backup_file.sql.gz | gunzip | psql testdb

 

 

 

2. 압축을 선호하지 않는다면, split 명령어를 이용하여 적당한 크기로 분할할 수 있습니다.

pg_dump testdb | split -b 2G - db_backup_file.sql

 

 

합치는 것은 cat 명령어를 이용하면 됩니다. 

cat db_backup_file.sql* | psql testdb

 

 

 

3. 커스텀 dump 포맷을 사용하는 경우

=> 이 경우 백업 결과, 압축 포맷으로 저장되기 때문에, pg_restore를 이용해서 복구를 해야 합니다.

pg_dump -Fc testdb > db_backup_file.sql

 

pg_restore를 이용하여 복구

 

pg_restore -d testdb db_backup_file.sql

 

 

유용한 팁

아래 명령어를 이용하면, src DB 서버에서 백업을 하면서, 동시에 target DB 서버에 restore를 할 수 있습니다.

pg_dump -h <src_db_host> <db_name> | psql -h <target_db_host> <db_name>

 

 

 

 

반응형

+ Recent posts