CI4 Playground v4.7.3
한국어문서

CSV / Excel 내보내기·가져오기

순수 PHP fputcsv/fgetcsvPhpSpreadsheet로 CSV·XLSX 파일을 내보내고 가져오는 실무 패턴

10
총 상품 수
8
카테고리 수
CSV + XLSX
지원 포맷
내보내기 (Export)

현재 DB에 있는 10건의 상품 데이터를 파일로 내보냅니다.


CSV vs Excel
항목CSVExcel(XLSX)
구현순수 PHPPhpSpreadsheet
스타일링불가가능 (색상, 폰트)
수식불가가능
파일 크기작음
한글 처리BOM 필요기본 지원
가져오기 (Import)
CSV 파일 가져오기
.csv 파일만 허용 · 헤더 행(첫 줄) 자동 스킵

Excel 파일 가져오기
.xlsx 파일만 허용 · 첫 번째 시트 사용

가져오기는 기존 데이터에 추가됩니다. 초기화
현재 DB 데이터 10건
ID 상품명 카테고리 가격 재고 등록일
11 애플 아이폰 15 스마트폰 1,250,000원 45 2026-05-27 13:56:50
12 삼성 갤럭시 S24 스마트폰 1,190,000원 62 2026-05-27 13:56:50
13 LG 그램 노트북 노트북 1,890,000원 23 2026-05-27 13:56:50
14 애플 맥북 에어 M3 노트북 1,690,000원 17 2026-05-27 13:56:50
15 Sony WH-1000XM5 헤드폰 449,000원 88 2026-05-27 13:56:50
16 삼성 QLED TV 55인치 TV 1,350,000원 12 2026-05-27 13:56:50
17 다이슨 에어랩 미용가전 699,000원 34 2026-05-27 13:56:50
18 닌텐도 스위치 OLED 게임기 399,000원 56 2026-05-27 13:56:50
19 아이패드 프로 12.9 태블릿 1,590,000원 29 2026-05-27 13:56:50
20 로지텍 MX Master 3 주변기기 139,000원 102 2026-05-27 13:56:50
CSV 내보내기
// CSV 내보내기 — 순수 PHP
public function exportCsv()
{
    $products = $db->table('products')
                   ->get()->getResultArray();

    ob_start();
    $fp = fopen('php://output', 'w');

    // UTF-8 BOM: Excel에서 한글 깨짐 방지
    fputs($fp, "\xEF\xBB\xBF");

    // 헤더 행
    fputcsv($fp, ['ID', '상품명', '카테고리', '가격', '재고']);

    // 데이터 행
    foreach ($products as $row) {
        fputcsv($fp, [
            $row['id'],
            $row['name'],
            $row['category'],
            $row['price'],
            $row['stock'],
        ]);
    }
    fclose($fp);
    $body = ob_get_clean();

    return $this->response
        ->setHeader('Content-Type', 'text/csv; charset=UTF-8')
        ->setHeader('Content-Disposition',
            'attachment; filename="products.csv"')
        ->setBody($body);
}
CSV 가져오기
// CSV 가져오기 — 순수 PHP
public function importCsv()
{
    $file = $this->request->getFile('csv_file');

    // 파일 유효성 검사
    if (!$file || !$file->isValid()) {
        return redirect()->back()
            ->with('error', '파일을 선택해주세요.');
    }

    $fp = fopen($file->getTempName(), 'r');

    // UTF-8 BOM 제거
    $bom = fread($fp, 3);
    if ($bom !== "\xEF\xBB\xBF") {
        rewind($fp); // BOM 없으면 처음으로
    }

    fgetcsv($fp); // 헤더 행 스킵
    $count = 0;

    while (($row = fgetcsv($fp)) !== false) {
        if (count($row) < 5) continue;

        $db->table('products')->insert([
            'name'     => trim($row[1]),
            'category' => trim($row[2]),
            'price'    => (float) $row[3],
            'stock'    => (int)   $row[4],
        ]);
        $count++;
    }
    fclose($fp);

    return redirect()->back()
        ->with('success', "{$count}건 가져오기 완료");
}
UTF-8 BOM이란?

BOM(Byte Order Mark)은 파일 첫 3바이트 0xEF 0xBB 0xBF로, UTF-8 인코딩임을 알리는 서명입니다.

  • Excel은 BOM이 없으면 CSV를 ANSI(EUC-KR)로 읽어 한글이 깨집니다.
  • 내보낼 때 fputs($fp, "\xEF\xBB\xBF")로 BOM을 먼저 씁니다.
  • 가져올 때 fread($fp, 3)으로 BOM 3바이트를 확인 후 스킵합니다.
Excel 내보내기 (PhpSpreadsheet)
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Alignment;

public function exportExcel()
{
    $products = $db->table('products')
                   ->get()->getResultArray();

    $spreadsheet = new Spreadsheet();
    $sheet = $spreadsheet->getActiveSheet();
    $sheet->setTitle('상품 목록');

    // 헤더 행 데이터
    $headers = ['ID', '상품명', '카테고리', '가격', '재고'];
    foreach ($headers as $col => $header) {
        $sheet->setCellValue(chr(65 + $col).'1', $header);
    }

    // 헤더 스타일 (파란 배경 + 흰 글자)
    $sheet->getStyle('A1:E1')->applyFromArray([
        'font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF']],
        'fill' => [
            'fillType'   => Fill::FILL_SOLID,
            'startColor' => ['rgb' => '0D6EFD'],
        ],
        'alignment' => [
            'horizontal' => Alignment::HORIZONTAL_CENTER,
        ],
    ]);

    // 데이터 입력
    foreach ($products as $i => $row) {
        $r = $i + 2;
        $sheet->setCellValue('A'.$r, $row['id']);
        $sheet->setCellValue('B'.$r, $row['name']);
        $sheet->setCellValue('C'.$r, $row['category']);
        $sheet->setCellValue('D'.$r, $row['price']);
        $sheet->setCellValue('E'.$r, $row['stock']);
    }

    // 가격 열 숫자 포맷
    $sheet->getStyle('D2:D'.($i+2))
          ->getNumberFormat()
          ->setFormatCode('#,##0');

    // 열 너비 자동
    foreach (range('A', 'E') as $col) {
        $sheet->getColumnDimension($col)->setAutoSize(true);
    }

    ob_start();
    (new Xlsx($spreadsheet))->save('php://output');
    $body = ob_get_clean();

    return $this->response
        ->setHeader('Content-Type',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
        ->setHeader('Content-Disposition',
            'attachment; filename="products.xlsx"')
        ->setBody($body);
}
Excel 가져오기 (PhpSpreadsheet)
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;

public function importExcel()
{
    $file = $this->request->getFile('excel_file');

    $reader = new XlsxReader();
    $spreadsheet = $reader->load($file->getTempName());

    // 첫 번째 시트를 배열로 변환
    $rows = $spreadsheet->getActiveSheet()->toArray();

    $count = 0;
    foreach ($rows as $i => $row) {
        if ($i === 0) continue; // 헤더 스킵

        $db->table('products')->insert([
            'name'     => trim($row[1]),
            'category' => trim($row[2]),
            'price'    => (float) $row[3],
            'stock'    => (int)   $row[4],
        ]);
        $count++;
    }

    return redirect()->back()
        ->with('success', "{$count}건 가져오기 완료");
}
설치 방법
# PhpSpreadsheet 설치
composer require phpoffice/phpspreadsheet

# 지원 포맷
# 읽기: xlsx, xls, csv, ods, html ...
# 쓰기: xlsx, xls, csv, ods, html, pdf ...
PhpSpreadsheet 주요 기능
  • 다중 시트$spreadsheet->createSheet()
  • 셀 병합$sheet->mergeCells('A1:C1')
  • 수식$sheet->setCellValue('A1', '=SUM(B1:B10)')
  • 차트PhpOffice\PhpSpreadsheet\Chart\*
  • 이미지 삽입Drawing 클래스 사용
  • 비밀번호 보호$sheet->getProtection()