[실무 강의] **흩어진 엑셀/CSV 파일, 파이썬으로 10분 만에 자동으로 합쳐 업무 효율 높이기** 완벽 마스터 튜토리얼

1. 학습 목표

현대 비즈니스 환경에서 데이터는 여러 부서, 혹은 여러 날짜별로 파편화되어 존재하기 마련입니다. 매달 수십 개의 엑셀(Excel) 파일이나 CSV 파일을 수작업으로 열어 복사하고 붙여넣는 작업은 단순히 시간을 낭비할 뿐만 아니라, 휴먼 에러(Human Error)를 유발하는 치명적인 원인이 됩니다. 본 강의의 목표는 파이썬(Python)의 강력한 데이터 분석 라이브러리인 Pandas를 활용하여, 수백 개의 파일을 단 몇 줄의 코드로 자동 병합하는 실무 기술을 습득하는 것입니다. 이 과정을 통해 여러분은 단순 반복 업무에서 해방되어 더 가치 있는 데이터 분석 업무에 집중할 수 있게 될 것입니다.

2. 사전 준비 사항

본 실습을 진행하기 위해 필요한 기술적 환경은 다음과 같습니다. 2026년 현재 표준적으로 사용되는 최신 안정화 버전을 기준으로 구성되었습니다.

  • 운영체제(OS): Windows 11, macOS Sequoia, 또는 Linux (Ubuntu 24.04 LTS 이상 권장)
  • 개발 환경(IDE): Visual Studio Code (VS Code) 최신 버전
  • 파이썬 버전: Python 3.12.x 이상
  • 필수 라이브러리 설치: 터미널(Terminal) 또는 명령 프롬프트(CMD)에서 아래 명령어를 입력하여 필요한 패키지를 설치하세요.
pip install pandas openpyxl xlsxwriter lxml

위 라이브러리 중 pandas는 데이터 처리를 담당하며, openpyxlxlsxwriter는 엑셀 파일(.xlsx)을 읽고 쓰는 엔진 역할을 합니다. 모든 준비가 완료되었다면 본격적인 실습으로 들어가겠습니다.

3. 단계별 실습 과정

Step 1: 작업 디렉토리 설정 및 라이브러리 임포트

가장 먼저 파이썬 스크립트가 실행될 환경을 구축해야 합니다. 실습을 위해 특정 폴더(예: data_folder) 안에 합치고자 하는 여러 개의 엑셀/CSV 파일을 모아두세요. 그 후, 파이썬 파일(merge_files.py)을 생성하고 아래 코드를 작성합니다.

import pandas as pd
import glob
import os

# 1. 파일이 저장된 경로 설정
input_path = './data_folder' # 파일들이 모여있는 폴더명
output_file = 'combined_result.xlsx' # 최종 저장될 파일명

Step 2: 병합 대상 파일 목록 자동 수집하기

파일이 10개든 1,000개든 상관없습니다. glob 라이브러리를 사용하면 특정 확장자를 가진 모든 파일을 리스트 형태로 한 번에 가져올 수 있습니다. CSV와 Excel 파일을 구분하여 수집하는 로직을 작성해 보겠습니다.

# 2. 특정 확장자(.xlsx 또는 .csv)를 가진 모든 파일 찾기
# 여기서는 모든 엑셀 파일을 대상으로 합니다.
all_files = glob.glob(os.path.join(input_path, "*.xlsx"))

if not all_files:
    print("병합할 파일을 찾을 수 없습니다. 경로를 확인해주세요.")
else:
    print(f"총 {len(all_files)}개의 파일을 찾았습니다.")

Step 3: Pandas를 이용한 데이터 통합 로직 구현

이제 수집된 파일 리스트를 순회하며 데이터를 읽어와 하나의 리스트에 담습니다. pd.concat 함수는 이 리스트에 담긴 수많은 데이터프레임(DataFrame)을 수직으로 결합하는 핵심 역할을 수행합니다.

# 3. 데이터 통합하기
li = []

for filename in all_files:
    # 각 파일을 읽어 데이터프레임으로 변환
    # index_col=None은 인덱스를 별도로 지정하지 않음을 의미
    # header=0은 첫 번째 줄을 컬럼명으로 사용함을 의미
    df = pd.read_excel(filename, index_col=None, header=0)
    
    # 파일명을 새로운 컬럼으로 추가 (데이터 출처 확인용)
    df['Source_File'] = os.path.basename(filename)
    
    li.append(df)

# 리스트에 담긴 모든 데이터프레임을 하나로 합치기
frame = pd.concat(li, axis=0, ignore_index=True)

Step 4: 데이터 정제 및 최종 엑셀 저장

합쳐진 데이터에 결측치(NaN)가 있거나 중복된 행이 있을 수 있습니다. 실무에서는 병합 후 간단한 정제 과정을 거치는 것이 좋습니다. 마지막으로 결과를 엑셀 파일로 저장하며 마무리합니다.

# 4. 데이터 정제 (선택 사항: 중복 제거)
frame.drop_duplicates(inplace=True)

# 5. 최종 결과 저장
try:
    frame.to_excel(output_file, index=False, engine='xlsxwriter')
    print(f"성공! 모든 파일이 '{output_file}'로 병합되었습니다.")
except Exception as e:
    print(f"저장 중 오류가 발생했습니다: {e}")

4. 결과 확인 및 실무 적용 팁

위 코드를 실행하면 combined_result.xlsx 파일이 생성됩니다. 파일을 열어보면 각기 다른 파일에 흩어져 있던 데이터들이 하나의 시트에 깔끔하게 정리되어 있을 것입니다. 특히 Source_File 컬럼을 통해 해당 데이터가 원래 어떤 파일에서 왔는지 역추적도 가능합니다.

실무에서 발생할 수 있는 변수 대응법

  • 컬럼명이 서로 다른 경우: Pandas의 concat은 컬럼명이 다르면 자동으로 새로운 컬럼을 생성하여 옆으로 붙입니다. 만약 컬럼명을 통일해야 한다면 df.rename(columns={'구이름': '신이름'}) 명령어를 사용하여 병합 전 이름을 맞춰주어야 합니다.
  • 대용량 파일 처리: 파일 하나의 용량이 수 GB에 달한다면 chunksize 옵션을 활용하여 데이터를 쪼개서 읽어오는 방식을 고려해야 메모리 부족(OOM) 오류를 방지할 수 있습니다.
  • CSV 파일 병합: 만약 CSV 파일을 합치고 싶다면 pd.read_excel 대신 pd.read_csv(filename, encoding='utf-8-sig')를 사용하면 됩니다. 한글 깨짐 방지를 위해 utf-8-sig 인코딩을 권장합니다.

이제 여러분은 파이썬을 활용해 단순 반복 업무를 자동화하는 첫걸음을 떼었습니다. 이 코드를 기반으로 매일 아침 자동으로 보고서를 생성하거나, 실시간으로 들어오는 데이터를 수집하는 시스템으로 확장해 보시기 바랍니다. 기술은 복잡한 것을 단순하게 만드는 데 그 진가가 있습니다. 오늘 배운 내용을 실무에 바로 적용하여 업무 시간을 획기적으로 단축해 보세요.

댓글 남기기


Warning: getimagesize(): http:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Warning: getimagesize(http://imgnews.naver.net/image/5447/2023/08/29/0000043464_001_20230829144201995.jpg): Failed to open stream: no suitable wrapper could be found in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/media.cls.php on line 1158

Fatal error: Uncaught ErrorException: md5_file(/hosting/apdldk/html/wp-content/litespeed/css/91ab2877cde0c90f7d18f6a75cb0d208.css.tmp): Failed to open stream: No such file or directory in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimizer.cls.php:148 Stack trace: #0 [internal function]: litespeed_exception_handler() #1 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimizer.cls.php(148): md5_file() #2 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(845): LiteSpeed\Optimizer->serve() #3 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(338): LiteSpeed\Optimize->_build_hash_url() #4 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(265): LiteSpeed\Optimize->_optimize() #5 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimize.cls.php(226): LiteSpeed\Optimize->_finalize() #6 /hosting/apdldk/html/wp-includes/class-wp-hook.php(341): LiteSpeed\Optimize->finalize() #7 /hosting/apdldk/html/wp-includes/plugin.php(205): WP_Hook->apply_filters() #8 /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/core.cls.php(464): apply_filters() #9 [internal function]: LiteSpeed\Core->send_headers_force() #10 /hosting/apdldk/html/wp-includes/functions.php(5481): ob_end_flush() #11 /hosting/apdldk/html/wp-includes/class-wp-hook.php(341): wp_ob_end_flush_all() #12 /hosting/apdldk/html/wp-includes/class-wp-hook.php(365): WP_Hook->apply_filters() #13 /hosting/apdldk/html/wp-includes/plugin.php(522): WP_Hook->do_action() #14 /hosting/apdldk/html/wp-includes/load.php(1308): do_action() #15 [internal function]: shutdown_action_hook() #16 {main} thrown in /hosting/apdldk/html/wp-content/plugins/litespeed-cache/src/optimizer.cls.php on line 148