CSV / Excel 내보내기·가져오기
순수 PHP fputcsv/fgetcsv와 PhpSpreadsheet로 CSV·XLSX 파일을 내보내고 가져오는 실무 패턴
10
총 상품 수
8
카테고리 수
CSV + XLSX
지원 포맷
내보내기 (Export)
현재 DB에 있는 10건의 상품 데이터를 파일로 내보냅니다.
CSV vs Excel
| 항목 | CSV | Excel(XLSX) |
|---|---|---|
| 구현 | 순수 PHP | PhpSpreadsheet |
| 스타일링 | 불가 | 가능 (색상, 폰트) |
| 수식 | 불가 | 가능 |
| 파일 크기 | 작음 | 큼 |
| 한글 처리 | BOM 필요 | 기본 지원 |
가져오기 (Import)
현재 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()