엑셀과 CSV 데이터를 PostgreSQL로 가져오기
엑셀 파일을 바로 넣는다고 생각하면 헷갈립니다
많은 업무 데이터는 엑셀에서 시작합니다. 고객 명단, 상품 재고, 월별 매출, 상담 이력처럼 한눈에 보이는 표로 관리합니다. PostgreSQL로 데이터를 옮길 때도 "엑셀 파일을 데이터베이스에 넣는다"고 말하곤 합니다.
하지만 실제로는 보통 엑셀 파일을 CSV 파일로 저장한 뒤, CSV를 PostgreSQL 테이블에 가져옵니다.
세 가지를 구분하면 훨씬 쉽습니다.
엑셀 파일:
.xlsx파일입니다. 여러 시트, 색상, 수식, 필터, 셀 병합을 담을 수 있습니다.CSV 파일: 쉼표로 값을 나눈 단순한 텍스트 파일입니다. 색상, 수식, 셀 병합은 사라지고 값만 남습니다.
데이터베이스 테이블: 정해진 컬럼과 데이터 타입에 맞춰 데이터를 저장하는 장부입니다.
엑셀은 보기 좋게 꾸민 업무용 종이에 가깝습니다. CSV는 그 종이에서 값만 뽑아 줄줄이 적어 둔 메모장입니다. PostgreSQL 테이블은 그 값을 정해진 칸에 넣는 공식 장부입니다.
예를 들어 엑셀의 상품 재고표가 아래처럼 생겼다고 해 보겠습니다.
| 상품번호 | 상품명 | 분류 | 재고수량 | 판매가 | 최근입고일 |
|---|---|---|---|---|---|
| P001 | 무선 마우스 | 전자기기 | 35 | 19000 | 2026-06-01 |
| P002 | 키보드 | 전자기기 | 12 | 45000 | 2026-06-03 |
이 파일을 CSV로 저장하면 대략 이런 텍스트가 됩니다.
product_id,product_name,category,stock_qty,price,last_received_date
P001,무선 마우스,전자기기,35,19000,2026-06-01
P002,키보드,전자기기,12,45000,2026-06-03첫 줄은 헤더입니다. 헤더는 각 열의 이름입니다. PostgreSQL로 가져오기 전에 이 첫 줄이 테이블 컬럼과 맞는지 확인해야 합니다.
AI 생성 이미지
가져오기 전에 CSV를 먼저 점검합니다
데이터 가져오기는 INSERT, UPDATE, DELETE처럼 테이블 내용을 바꾸는 작업입니다. 따라서 바로 운영 테이블에 넣지 말고, 다음 순서로 진행하는 습관이 좋습니다.
원본 엑셀 파일을 따로 보관합니다.
엑셀에서 CSV UTF-8 형식으로 저장합니다.
CSV 파일의 첫 줄, 컬럼 순서, 날짜 형식, 숫자 형식을 확인합니다.
가능하면 실제 테이블이 아니라
import_상품명같은 검수용 테이블에 먼저 가져옵니다.SELECT로 결과를 확인한 뒤 실제 업무 테이블에 반영합니다.
특히 아래 네 가지를 확인하세요.
첫째, 헤더 이름입니다. CSV 첫 줄이 product_id,product_name,category인데 테이블 컬럼은 product_code,name,category_name이라면 그대로 가져오기 어렵습니다. 이름이 꼭 같아야 하는 것은 아니지만, 어떤 CSV 열이 어떤 테이블 컬럼으로 들어갈지 분명해야 합니다.
둘째, 컬럼 순서입니다. PostgreSQL은 CSV의 첫 번째 값을 테이블의 첫 번째 컬럼에, 두 번째 값을 두 번째 컬럼에 넣는 방식으로 처리할 수 있습니다. 순서가 어긋나면 상품명 자리에 분류가 들어가는 식의 사고가 생깁니다. 그래서 가져오기 화면이나 COPY 명령에서 컬럼 목록을 직접 지정해 주면 더 안전합니다.
셋째, 데이터 타입입니다. stock_qty는 숫자, last_received_date는 날짜로 들어가야 합니다. 그런데 CSV에 1,200개, 2026년 6월 1일처럼 적혀 있으면 바로 들어가지 않을 수 있습니다.
넷째, 빈 값입니다. 엑셀에서 아무것도 입력하지 않은 셀은 CSV에서 빈 칸으로 나옵니다. PostgreSQL에서는 이것을 빈 문자열로 볼지, 값이 없다는 뜻의 NULL로 볼지 업무 기준을 정해야 합니다.
화면 도구로 가져오기: 처음에는 이 방법이 가장 편합니다
비개발자에게는 pgAdmin이나 DBeaver 같은 화면 기반 도구의 Import 기능이 가장 편합니다. 파일을 고르고, CSV 형식을 선택하고, 구분자와 헤더 여부를 지정하면 됩니다. pgAdmin의 Import/Export Data 기능도 파일을 테이블로 가져오거나 테이블을 파일로 내보내는 방식으로 동작하며, CSV 형식, 인코딩, 헤더, 구분자 같은 옵션을 화면에서 고를 수 있습니다.1
다만 화면 도구도 종류와 실행 방식에 따라 파일을 찾는 위치가 다를 수 있습니다. 내 컴퓨터에 설치한 DBeaver에서는 내 컴퓨터의 파일을 고르는 흐름이 자연스럽지만, 웹 브라우저로 접속하는 pgAdmin Server Mode에서는 서버 저장소나 Storage Manager를 거쳐야 할 수 있습니다. 파일 선택 화면이 예상과 다르게 보이면 "이 도구가 지금 내 컴퓨터 파일을 보는지, 서버 쪽 파일을 보는지"를 먼저 확인하세요.
일반적인 흐름은 다음과 같습니다.
왼쪽 탐색기에서 가져올 대상 테이블을 찾습니다.
테이블을 마우스 오른쪽 버튼으로 클릭합니다.
Import/Export 또는 Import Data 메뉴를 선택합니다.
Import 방향을 선택합니다.
파일 형식은
CSV로 선택합니다.파일 인코딩은 한글이 있다면 보통
UTF-8을 선택합니다.첫 줄이 컬럼명이라면
Header를 켭니다.구분자는 보통 쉼표
,입니다.가져올 컬럼을 확인합니다.
실행 후 결과 메시지와 실제 데이터를 확인합니다.
여기서 중요한 것은 "성공 메시지가 떴다"로 끝내지 않는 것입니다. 반드시 가져온 뒤 조회합니다.
하려는 일: 방금 가져온 상품 재고 데이터가 제대로 들어갔는지 앞부분 20건만 확인합니다.
SELECT product_id,
product_name,
category,
stock_qty,
price,
last_received_date
FROM import_product_inventory
ORDER BY product_id
LIMIT 20;결과에서 상품번호, 상품명, 재고수량, 판매가, 날짜가 제자리에 들어갔는지 봅니다. 숫자가 비어 있거나 날짜가 이상하게 보이면 실제 테이블에 반영하지 말고 CSV를 고친 뒤 다시 가져오는 편이 안전합니다.
COPY 명령은 대량 가져오기용 도구입니다
화면 도구가 버튼이라면, COPY는 PostgreSQL에 직접 요청하는 명령입니다. COPY FROM은 파일의 데이터를 테이블로 가져오고, COPY TO는 테이블이나 조회 결과를 파일로 내보냅니다. CSV를 다룰 때는 FORMAT csv, HEADER, DELIMITER, ENCODING 같은 옵션을 함께 사용합니다.2
다만 초보자가 꼭 알아야 할 주의점이 있습니다. SQL에서 쓰는 COPY ... FROM '파일경로'는 내 노트북의 파일을 읽는다고 생각하면 안 됩니다. PostgreSQL 서버가 접근할 수 있는 위치의 파일을 읽습니다. 회사 클라우드 DB를 쓰고 있다면 내 바탕화면의 CSV 경로를 서버가 모르는 경우가 많습니다. 또한 서버 파일을 직접 읽고 쓰는 COPY는 권한이 제한될 수 있습니다.
그래서 선택 기준은 이렇게 잡으면 됩니다.
한 번 가져오고 확인하는 업무: pgAdmin, DBeaver의 Import 기능을 사용합니다.
반복해서 같은 형식의 파일을 가져오는 업무:
COPY명령을 검토합니다.파일 경로 권한 오류가 자주 나는 환경: 클라이언트 파일을 직접 올릴 수 있는 Import 기능이나, psql의
copy같은 클라이언트 쪽 방식을 검토합니다.수십만 건 이상 대량 데이터:
COPY가 더 적합합니다.
예시를 보겠습니다. 여기서는 이미 import_product_inventory라는 검수용 테이블이 있다고 가정합니다.
하려는 일: products.csv 파일의 데이터를 import_product_inventory 테이블에 가져옵니다. 첫 줄은 컬럼명이므로 데이터로 넣지 않습니다.
COPY import_product_inventory
(product_id, product_name, category, stock_qty, price, last_received_date)
FROM '/data/products.csv'
WITH (
FORMAT csv,
HEADER true,
DELIMITER ',',
ENCODING 'UTF8'
);실행 결과가 아래처럼 나오면 2건을 가져왔다는 뜻입니다.
COPY 2컬럼 이름을 괄호 안에 적은 이유는 안전 때문입니다. CSV의 열 순서와 테이블 컬럼 순서를 명확히 맞추기 위해서입니다. 생략할 수도 있지만, 초보자에게는 적어 주는 방식이 더 안전합니다.
가져오기 오류는 대부분 형식 문제입니다
CSV 가져오기가 실패하면 PostgreSQL이 어렵다기보다, CSV 파일이 테이블 규칙과 맞지 않는 경우가 많습니다. 자주 만나는 문제를 하나씩 보겠습니다.
날짜 형식이 맞지 않는 경우
PostgreSQL의 날짜 컬럼에는 보통 2026-06-01처럼 연-월-일 형식이 안전합니다. 엑셀에서 2026. 6. 1, 2026년 6월 1일, 06/01/2026처럼 보이면 가져오기 중 오류가 날 수 있습니다.
해결 방법은 간단합니다.
엑셀에서 날짜 열의 표시 형식을
yyyy-mm-dd로 맞춥니다.CSV로 저장한 뒤 메모장이나 텍스트 편집기로 실제 값이
2026-06-01인지 확인합니다.애매하면 처음에는 날짜 컬럼을 문자로 받는 검수용 테이블에 넣고, 확인 후 날짜로 변환합니다.
숫자에 쉼표나 원화 기호가 섞인 경우
엑셀에서는 19,000이나 ₩19,000처럼 보여도 보기에는 편합니다. 하지만 숫자 컬럼에 넣을 때는 문제가 될 수 있습니다. CSV는 쉼표를 열 구분자로 쓰기 때문에 19,000이 따옴표 없이 들어 있으면 두 칸으로 갈라질 수도 있습니다.
가져오기 전에는 숫자를 아래처럼 정리하는 것이 좋습니다.
좋음: 19000
주의: 19,000
주의: ₩19,000
주의: 19,000원보고서용 엑셀에서는 쉼표와 원화 기호가 보기 좋지만, 데이터베이스로 보낼 때는 계산 가능한 순수 숫자가 좋습니다.
빈 값과 NULL이 헷갈리는 경우
엑셀의 빈 셀은 사람 눈에는 그냥 비어 있는 칸입니다. PostgreSQL에서는 두 가지 의미로 나뉩니다.
빈 문자열: 글자가 없는 값
''NULL: 값 자체가 없음
예를 들어 고객의 보조 전화번호가 비어 있다면 "아직 모른다"는 뜻일 수 있습니다. 이때는 NULL이 더 자연스럽습니다. 반면 메모 칸에 아무 말도 쓰지 않은 것이라면 빈 문자열로 둬도 큰 문제가 없을 수 있습니다.
가져오기 전에 팀에서 정해야 합니다. "빈 칸은 값 없음으로 볼 것인가?" 이 규칙이 정해지면 Import 옵션의 NULL 문자열 설정이나 COPY의 NULL 옵션을 맞출 수 있습니다.
하려는 일: CSV에서 따옴표 없이 비어 있는 칸을 NULL로 보고 상품 데이터를 가져옵니다.
COPY import_product_inventory
(product_id, product_name, category, stock_qty, price, last_received_date)
FROM '/data/products.csv'
WITH (
FORMAT csv,
HEADER true,
NULL '',
ENCODING 'UTF8'
);이 예시는 빈 값을 NULL로 처리하겠다는 뜻입니다. CSV 형식에서 NULL ''는 자주 쓰이는 설정이지만, 모든 빈 칸을 무조건 NULL로 바꾸는 것이 업무상 맞는지 먼저 확인해야 합니다. 특히 ""처럼 따옴표로 감싼 빈 값과 따옴표 없이 비어 있는 값은 도구나 옵션에 따라 다르게 처리될 수 있으니, 처음 한두 건은 꼭 조회해서 확인하세요.
한글이 깨지는 경우
가져온 데이터가 무선 마우스가 아니라 이상한 기호로 보인다면 인코딩 문제일 가능성이 큽니다. 인코딩은 글자를 저장하는 방식입니다.
한글 CSV를 다룰 때는 보통 아래 순서로 해결합니다.
엑셀에서 저장할 때 CSV UTF-8을 선택합니다.
Import 화면에서 Encoding을
UTF-8로 선택합니다.그래도 깨지면 원본 파일이
CP949또는EUC-KR계열인지 확인합니다.가능하면 UTF-8로 다시 저장한 파일을 기준으로 작업합니다.
회사마다 오래된 시스템에서 내려받은 CSV는 UTF-8이 아닐 수 있습니다. 한글이 깨질 때는 SQL 문장보다 파일 저장 형식을 먼저 의심하세요.
가져온 뒤에는 바로 검수합니다
CSV 가져오기가 끝나면 최소한 세 가지를 확인합니다.
하려는 일: 가져온 전체 행 수를 확인합니다.
SELECT COUNT(*) AS total_rows
FROM import_product_inventory;CSV 파일이 헤더까지 포함해서 전체 500줄이었다면, 첫 줄은 컬럼명이므로 가져온 행 수는 보통 499건이어야 합니다.
하려는 일: 필수값이 비어 있는 상품이 있는지 확인합니다.
SELECT product_id,
product_name,
stock_qty,
price
FROM import_product_inventory
WHERE product_id IS NULL
OR product_name IS NULL
OR stock_qty IS NULL
OR price IS NULL;결과가 없다면 필수값 누락은 없는 상태입니다. 결과가 나온다면 해당 행을 CSV에서 고치거나, 업무 담당자에게 확인해야 합니다.
하려는 일: 재고수량이나 판매가가 음수인 이상 데이터를 찾습니다.
SELECT product_id,
product_name,
stock_qty,
price
FROM import_product_inventory
WHERE stock_qty < 0
OR price < 0;재고나 판매가가 음수라면 대부분 입력 오류입니다. 이렇게 가져온 뒤 검수용 SELECT를 몇 개 만들어 두면 매번 같은 기준으로 파일을 점검할 수 있습니다.
조회 결과를 CSV로 내보내기
CSV는 가져오기만 하는 형식이 아닙니다. PostgreSQL에 있는 데이터를 팀원에게 공유하거나, 보고서 도구에 넘기거나, 엑셀에서 추가 분석할 때도 CSV로 내보냅니다.
가장 쉬운 방법은 화면 도구를 사용하는 것입니다.
필요한
SELECT문을 실행합니다.결과표를 확인합니다.
결과표의 저장, 다운로드, Export 메뉴를 선택합니다.
파일 형식을 CSV로 고릅니다.
헤더 포함 여부를 선택합니다.
한글이 있다면 UTF-8로 저장합니다.
내보내기 전에도 먼저 조회 결과를 확인해야 합니다. 전체 고객 명단을 내보낼 생각이 아니었는데 개인정보까지 포함될 수 있기 때문입니다.
하려는 일: 재고가 10개 이하인 상품만 보고서용으로 조회합니다.
SELECT product_id,
product_name,
category,
stock_qty,
price
FROM import_product_inventory
WHERE stock_qty <= 10
ORDER BY stock_qty ASC,
product_id ASC;이 결과가 맞다면 CSV로 저장합니다. COPY 명령으로 조회 결과를 내보낼 수도 있습니다.
하려는 일: 재고가 10개 이하인 상품 조회 결과를 CSV 파일로 내보냅니다. 첫 줄에는 컬럼명을 포함합니다.
COPY (
SELECT product_id,
product_name,
category,
stock_qty,
price
FROM import_product_inventory
WHERE stock_qty <= 10
ORDER BY stock_qty ASC,
product_id ASC
)
TO '/data/low_stock_products.csv'
WITH (
FORMAT csv,
HEADER true,
ENCODING 'UTF8'
);COPY TO는 테이블 전체뿐 아니라 괄호 안의 조회 결과도 파일로 내보낼 수 있습니다.2 실무에서는 이 방식으로 "부족 재고 목록", "이번 달 주문 목록", "미수금 고객 목록" 같은 파일을 반복해서 만들 수 있습니다.
다만 내보내기도 조심해야 합니다. CSV 파일은 엑셀로 쉽게 열리고 메일로 쉽게 전달됩니다. 고객 전화번호, 주소, 결제 정보 같은 민감한 컬럼은 꼭 필요한 경우에만 포함하세요. COPY TO '파일경로'도 서버가 접근할 수 있는 위치에 파일을 쓰는 방식이므로, 내 컴퓨터로 바로 내려받는 화면 도구의 Export와는 파일 위치가 다를 수 있습니다.
이번 장의 핵심
엑셀 데이터를 PostgreSQL에 넣는 핵심은 "파일을 선택해서 넣기"가 아닙니다. CSV의 모양과 테이블의 모양을 맞추는 것입니다.
엑셀은 사람이 보기 좋은 표입니다. CSV는 데이터를 옮기기 좋은 중간 파일입니다. PostgreSQL 테이블은 규칙이 있는 업무 장부입니다. 이 셋의 차이를 이해하면 가져오기 오류의 대부분을 스스로 해결할 수 있습니다.
처음에는 화면 도구의 Import 기능을 사용하세요. 반복 작업이나 대량 작업이 필요해지면 COPY 명령을 배우면 됩니다. 어떤 방법을 쓰든 순서는 같습니다.
원본을 백업합니다.
CSV 헤더와 컬럼 순서를 확인합니다.
날짜, 숫자, 빈 값, 한글 인코딩을 점검합니다.
검수용 테이블에 먼저 가져옵니다.
SELECT로 행 수와 이상 데이터를 확인합니다.필요한 결과만 CSV로 내보냅니다.
이 순서를 지키면 엑셀에 흩어진 업무 데이터를 PostgreSQL 안에서 더 안전하고 반복 가능한 방식으로 관리할 수 있습니다.
참고
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.