PostgreSQL 데이터베이스 작업 중 테이블을 복사해야 하는 상황은 자주 발생합니다. 백업, 테스트, 데이터 마이그레이션 등 다양한 목적으로 테이블 복사가 필요한데요. 이 글에서는 pgAdmin을 활용한 여러 가지 테이블 복사 방법을 단계별로 알려드리겠습니다.
🎯 테이블 복사가 필요한 상황
- 프로덕션 데이터를 테스트 환경으로 복사
- 데이터 백업 및 복원
- 테이블 구조만 복사하여 새 테이블 생성
- 특정 데이터만 선택적으로 복사
- 다른 데이터베이스로 테이블 이동
📋 방법 1: Backup/Restore 기능 활용 (GUI)
pgAdmin의 가장 직관적인 방법으로, 마우스 클릭만으로 테이블을 복사할 수 있습니다.
Backup 단계
1단계: 테이블 선택
- pgAdmin 좌측 브라우저에서 복사할 테이블 찾기
- 테이블에 우클릭
- ‘Backup…’ 메뉴 선택
2단계: Backup 옵션 설정
General 탭:
- Filename: 백업 파일 저장 경로 및 이름 지정 (예:
users_backup.sql) - Format:
Plain– SQL 스크립트 형식 (텍스트, 편집 가능)Custom– 압축된 커스텀 형식 (권장)Tar– TAR 아카이브 형식Directory– 디렉토리 형식
Data Options 탭:
Data: 체크 – 데이터 포함Schemas: 체크 – 스키마 정보 포함Blobs: 체크 – 대용량 객체 포함
3단계: Backup 실행
- ‘Backup’ 버튼 클릭
- 진행 상황 모니터링
- 완료 메시지 확인
Restore 단계
1단계: 대상 데이터베이스 선택
- 테이블을 복원할 데이터베이스 우클릭
- ‘Restore…’ 메뉴 선택
2단계: Restore 옵션 설정
General 탭:
- Filename: 백업 파일 선택
- Format: 백업 시 선택한 형식과 동일하게 설정
- Role name: 복원 수행 권한 사용자
Data Options 탭:
Only data: 데이터만 복원Only schema: 구조만 복원- 둘 다 체크 안 함: 전체 복원
3단계: Restore 실행
- ‘Restore’ 버튼 클릭
- 완료 확인
💻 방법 2: SQL Query 사용 (추천)
SQL을 직접 작성하여 더 빠르고 유연하게 테이블을 복사할 수 있습니다.
구조와 데이터 모두 복사
-- 같은 데이터베이스 내에서 테이블 전체 복사
CREATE TABLE new_table AS
SELECT * FROM original_table;
-- 또는 더 명시적인 방법
CREATE TABLE new_table (LIKE original_table INCLUDING ALL);
INSERT INTO new_table SELECT * FROM original_table;
주의사항:
CREATE TABLE AS방식은 인덱스, 제약조건, 기본값 등이 복사되지 않습니다LIKE INCLUDING ALL방식은 모든 속성을 복사합니다
테이블 구조만 복사
-- 데이터 없이 구조만 복사
CREATE TABLE new_table (LIKE original_table INCLUDING ALL);
-- 또는
CREATE TABLE new_table AS
SELECT * FROM original_table
WHERE 1=0; -- 조건을 false로 만들어 데이터는 제외
특정 컬럼만 복사
-- 필요한 컬럼만 선택하여 복사
CREATE TABLE new_table AS
SELECT id, name, email, created_at
FROM original_table;
조건부 데이터 복사
-- 특정 조건의 데이터만 복사
CREATE TABLE active_users AS
SELECT * FROM users
WHERE status = 'active'
AND created_at >= '2024-01-01';
인덱스와 제약조건 포함 복사
-- 1단계: 구조 복사 (모든 속성 포함)
CREATE TABLE new_table (
LIKE original_table
INCLUDING DEFAULTS
INCLUDING CONSTRAINTS
INCLUDING INDEXES
);
-- 2단계: 데이터 복사
INSERT INTO new_table
SELECT * FROM original_table;
-- 3단계: 시퀀스 재설정 (필요시)
SELECT setval('new_table_id_seq',
(SELECT MAX(id) FROM new_table));
다른 스키마로 복사
-- 다른 스키마의 테이블로 복사
CREATE TABLE schema2.new_table AS
SELECT * FROM schema1.original_table;
🔧 방법 3: Query Tool 활용
pgAdmin의 Query Tool을 사용하면 복잡한 복사 작업도 쉽게 수행할 수 있습니다.
1단계: Query Tool 열기
- 상단 메뉴에서 Tools > Query Tool 선택
- 또는 단축키:
Alt + Shift + Q
2단계: SQL 쿼리 작성
-- 예시: 최근 6개월 데이터만 복사
CREATE TABLE recent_orders AS
SELECT * FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '6 months';
-- 테이블 정보 확인
SELECT COUNT(*) FROM recent_orders;
3단계: 쿼리 실행
- 실행 버튼 클릭 (F5) 또는 재생 아이콘 클릭
- 하단에서 실행 결과 확인
📊 방법 4: 대용량 테이블 복사
수백만 행 이상의 대용량 테이블을 복사할 때는 성능을 고려해야 합니다.
COPY 명령 사용
-- 1단계: 테이블을 파일로 내보내기
COPY original_table TO '/tmp/data.csv'
WITH (FORMAT CSV, HEADER true);
-- 2단계: 새 테이블 생성
CREATE TABLE new_table (LIKE original_table INCLUDING ALL);
-- 3단계: 파일에서 데이터 가져오기
COPY new_table FROM '/tmp/data.csv'
WITH (FORMAT CSV, HEADER true);
배치 처리로 복사
-- 대용량 테이블을 배치로 나눠서 복사
DO $$
DECLARE
batch_size INTEGER := 10000;
offset_val INTEGER := 0;
total_rows INTEGER;
BEGIN
-- 새 테이블 생성
CREATE TABLE IF NOT EXISTS new_table
(LIKE original_table INCLUDING ALL);
-- 전체 행 수 확인
SELECT COUNT(*) INTO total_rows FROM original_table;
-- 배치 단위로 복사
WHILE offset_val < total_rows LOOP
INSERT INTO new_table
SELECT * FROM original_table
ORDER BY id
LIMIT batch_size OFFSET offset_val;
offset_val := offset_val + batch_size;
RAISE NOTICE '진행률: %/%', offset_val, total_rows;
END LOOP;
END $$;
🔐 권한 및 소유권 복사
테이블을 복사한 후에는 권한 설정도 필요할 수 있습니다.
-- 소유권 변경
ALTER TABLE new_table OWNER TO new_owner;
-- 권한 부여
GRANT SELECT, INSERT, UPDATE, DELETE
ON new_table TO app_user;
-- 원본 테이블의 권한 확인
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'original_table';
⚠️ 주의사항 및 모범 사례
복사 전 확인사항
디스크 공간 확인
-- 테이블 크기 확인
SELECT
pg_size_pretty(pg_total_relation_size('original_table')) AS total_size,
pg_size_pretty(pg_relation_size('original_table')) AS table_size,
pg_size_pretty(pg_indexes_size('original_table')) AS indexes_size;
테이블 구조 확인
-- 테이블 정보 조회
\\d+ original_table
-- 또는 pgAdmin에서 테이블 우클릭 > Properties
복사 후 검증
-- 행 수 비교
SELECT
(SELECT COUNT(*) FROM original_table) AS original_count,
(SELECT COUNT(*) FROM new_table) AS new_count;
-- 데이터 무결성 검증
SELECT * FROM original_table
EXCEPT
SELECT * FROM new_table;
-- 비어있으면 정상 복사됨
성능 최적화 팁
인덱스 관리
-- 복사 전 인덱스 삭제 (대용량 데이터)
DROP INDEX IF EXISTS idx_original_column;
-- 데이터 복사
INSERT INTO new_table SELECT * FROM original_table;
-- 복사 후 인덱스 재생성
CREATE INDEX idx_new_column ON new_table(column_name);
-- VACUUM ANALYZE 실행
VACUUM ANALYZE new_table;
트랜잭션 활용
BEGIN;
CREATE TABLE new_table AS SELECT * FROM original_table;
-- 검증
SELECT COUNT(*) FROM new_table;
-- 문제없으면 커밋, 있으면 롤백
COMMIT;
-- ROLLBACK;
🛠️ 자동화 스크립트
정기적으로 테이블을 복사해야 한다면 스크립트를 작성하세요.
#!/bin/bash
# table_copy.sh
DB_NAME="your_database"
SOURCE_TABLE="original_table"
TARGET_TABLE="backup_$(date +%Y%m%d)_table"
psql -d $DB_NAME -c "
CREATE TABLE $TARGET_TABLE AS
SELECT * FROM $SOURCE_TABLE;
CREATE INDEX ON $TARGET_TABLE(id);
VACUUM ANALYZE $TARGET_TABLE;
"
echo "테이블 복사 완료: $TARGET_TABLE"
🔍 트러블슈팅
권한 오류
ERROR: permission denied for table original_table
해결방법:
-- 권한 확인
SELECT * FROM information_schema.table_privileges
WHERE table_name = 'original_table';
-- 관리자에게 권한 요청 또는
GRANT SELECT ON original_table TO current_user;
디스크 공간 부족
ERROR: could not extend file: No space left on device
해결방법:
- 디스크 공간 확보
- 불필요한 테이블 삭제
- 배치 처리로 나눠서 복사
제약조건 충돌
ERROR: duplicate key value violates unique constraint
해결방법:
-- 시퀀스 초기화
SELECT setval('new_table_id_seq',
COALESCE((SELECT MAX(id) FROM new_table), 1));
-- 또는 제약조건 임시 비활성화
ALTER TABLE new_table DISABLE TRIGGER ALL;
-- 데이터 복사
ALTER TABLE new_table ENABLE TRIGGER ALL;
📚 마무리
pgAdmin에서 테이블을 복사하는 방법은 상황에 따라 다양합니다. GUI를 선호한다면 Backup/Restore 기능을, 더 세밀한 제어가 필요하다면 SQL 쿼리를 활용하세요.
상황별 추천 방법:
- 간단한 전체 복사: Backup/Restore
- 조건부 복사: SQL Query
- 대용량 테이블: COPY 명령 또는 배치 처리
- 정기적 백업: 자동화 스크립트
실무에서는 복사 전후 검증을 철저히 하고, 프로덕션 환경에서는 항상 트랜잭션을 활용하여 안전하게 작업하시기 바랍니다.
pgAdmin 사용 중 궁금한 점이나 다른 팁이 있으시다면 댓글로 공유해주세요!