setHeader('Content-Type', 'text/csv; charset=UTF-8'); $response->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"'); $response->setHeader('Pragma', 'no-cache'); $response->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate'); // UTF-8 BOM (한글 엑셀 호환) $output = "\xEF\xBB\xBF"; // 헤더 행 $output .= csv_encode_row($headers); // 데이터 행 foreach ($rows as $row) { $output .= csv_encode_row(array_values((array) $row)); } $response->setBody($output); $response->send(); exit; } } if (! function_exists('csv_encode_row')) { /** * 배열 한 행을 CSV 문자열로 변환 * * @param array $fields * @return string */ function csv_encode_row(array $fields): string { $escaped = []; foreach ($fields as $field) { $val = (string) ($field ?? ''); // 쌍따옴표 이스케이프 및 감싸기 if (str_contains($val, '"') || str_contains($val, ',') || str_contains($val, "\n") || str_contains($val, "\r")) { $val = '"' . str_replace('"', '""', $val) . '"'; } $escaped[] = $val; } return implode(',', $escaped) . "\r\n"; } }