PHP · 심층 가이드
PHP 파일/IO 완전 정리
스트림 래퍼로 통일된 PHP의 입출력 위에서 CSV와 JSON, 디렉터리 순회, 파일 잠금과 원자적 교체까지 데이터를 잃지 않는 파일 처리 방법을 정리합니다.
PHP 입출력의 중심에는 스트림 래퍼라는 추상화가 있습니다. fopen에 넘기는 경로가 로컬 파일일 수도, php://memory 같은 가상 스트림일 수도, https:// 로 시작하는 원격 자원일 수도 있고, 그 위에서 동작하는 함수 집합은 동일합니다. 덕분에 파일을 다루던 코드를 거의 그대로 네트워크나 메모리 버퍼로 돌릴 수 있습니다. 반대로 이 편의성 때문에 사용자 입력이 경로로 흘러들면 그대로 원격 접근이 되어 버리는 위험도 함께 따라옵니다.
파일 읽기와 파일 쓰기처럼 한 번에 처리하는 방식으로 시작하되, 파일이 커지는 순간 fopen 과 fgets 로 줄 단위 읽기로 갈아타는 감각을 먼저 만드십시오. 구조화된 데이터는 CSV 읽기와 JSON 파일 다루기가 담당하고, 대상 파일을 찾는 일은 단순한 경우 glob 패턴 검색, 하위 디렉터리까지 훑어야 하면 RecursiveDirectoryIterator 순회가 맞습니다. 동시에 여러 프로세스가 같은 파일을 건드리는 상황이라면 flock 파일 잠금과 원자적 파일 교체를 한 묶음으로 읽는 편이 좋습니다.
설정 파일이나 캐시를 갱신할 때 file_put_contents를 그대로 쓰면 위험합니다. 이 함수는 원자적이지 않아서 쓰는 도중 프로세스가 죽으면 잘린 파일이 남고, 그 사이에 읽은 다른 프로세스는 깨진 내용을 받습니다. 안전한 방법은 같은 파일시스템에 임시 파일을 쓴 뒤 rename()으로 덮어쓰는 것입니다. rename은 같은 파일시스템 안에서 원자적이라 독자는 항상 이전 버전이나 새 버전 중 하나만 봅니다. 업로드 파일 검사도 비슷한데, 확장자와 $_FILES의 type 값은 클라이언트가 보낸 문자열이라 신뢰할 수 없습니다.
01파일 읽기
작은 파일을 한 번에 읽을 때 가장 간단한 file_get_contents 예제입니다.
PHP code
<?php
$path = __FILE__;
echo file_get_contents($path) !== false ? 'loaded' : 'failed';파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
02파일 쓰기
짧은 텍스트 파일을 빠르게 쓰는 file_put_contents 예제입니다.
PHP code
<?php
$path = sys_get_temp_dir() . '/php-write.txt';
file_put_contents($path, "hello
");
echo $path;파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
03로그 이어쓰기
기존 파일 끝에 로그를 추가하는 FILE_APPEND 예제입니다.
PHP code
<?php
$path = sys_get_temp_dir() . '/app.log';
file_put_contents($path, "[info] boot
", FILE_APPEND);
echo $path;파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
04CSV 읽기
fgetcsv와 fputcsv를 함께 사용해 CSV 흐름을 이해하는 예제입니다.
PHP code
<?php
$stream = fopen('php://temp', 'r+');
fputcsv($stream, ['name', 'score']);
fputcsv($stream, ['php', 100]);
rewind($stream);
while (($row = fgetcsv($stream)) !== false) {
print_r($row);
}파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
05JSON 파일 다루기
배열을 JSON 파일로 저장했다가 다시 읽어오는 기본 예제입니다.
PHP code
<?php
$path = sys_get_temp_dir() . '/config.json';
file_put_contents($path, json_encode(['cache' => true]));
$config = json_decode(file_get_contents($path), true);
var_dump($config);파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
06디렉터리 목록 읽기
현재 디렉터리의 파일 목록을 읽는 가장 간단한 예제입니다.
PHP code
<?php
$files = scandir(__DIR__);
print_r(array_slice($files, 0, 5));파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
07RecursiveDirectoryIterator 순회
재귀적으로 디렉터리를 훑으며 파일을 처리하는 예제입니다.
PHP code
<?php
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(__DIR__, FilesystemIterator::SKIP_DOTS)
);
$count = 0;
foreach ($iterator as $file) {
$count++;
if ($count === 3) {
break;
}
}
echo $count;파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
08업로드 파일 검사
업로드 파일 배열을 다룰 때 최소한의 검증 로직을 붙이는 예제입니다.
PHP code
<?php
function validateUpload(array $file): bool {
return ($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK
&& ($file['size'] ?? 0) < 2_000_000;
}
var_dump(validateUpload(['error' => 0, 'size' => 1200]));파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
09fopen 과 fgets 로 줄 단위 읽기
큰 파일을 줄 단위로 순차 처리할 때 자주 쓰는 패턴입니다.
PHP code
<?php
$stream = fopen('php://temp', 'r+');
fwrite($stream, "a
b
");
rewind($stream);
while (($line = fgets($stream)) !== false) {
echo trim($line) . PHP_EOL;
}파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
10flock 파일 잠금
동시에 같은 파일을 쓰는 상황에서 파일 잠금을 거는 예제입니다.
PHP code
<?php
$path = sys_get_temp_dir() . '/counter.txt';
$fp = fopen($path, 'c+');
if (flock($fp, LOCK_EX)) {
fwrite($fp, "1
");
fflush($fp);
flock($fp, LOCK_UN);
}
fclose($fp);파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
11임시 파일 만들기
충돌을 피하는 임시 파일 경로를 안전하게 생성하는 예제입니다.
PHP code
<?php
$path = tempnam(sys_get_temp_dir(), 'php-');
file_put_contents($path, 'temp');
echo $path;파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
12INI 설정 읽기
간단한 설정 파일을 parse_ini_file로 읽는 예제입니다.
PHP code
<?php
$path = sys_get_temp_dir() . '/app.ini';
file_put_contents($path, "env=local
cache=true
");
print_r(parse_ini_file($path));파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
13glob 패턴 검색
패턴에 맞는 파일을 빠르게 찾는 glob 예제입니다.
PHP code
<?php
$matches = glob(__DIR__ . '/*.js');
echo is_array($matches) ? count($matches) : 0;파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
14ZipArchive 압축
파일 묶음을 배포 아카이브로 만들 때 쓰는 ZipArchive 예제입니다.
PHP code
<?php
$zipPath = sys_get_temp_dir() . '/sample.zip';
$zip = new ZipArchive();
$zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE);
$zip->addFromString('readme.txt', 'hello zip');
$zip->close();
echo $zipPath;파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
15SimpleXML 파싱
간단한 XML 문서를 읽을 때 빠르게 사용할 수 있는 SimpleXML 예제입니다.
PHP code
<?php
$xml = simplexml_load_string('<root><item>php</item></root>');
echo (string) $xml->item;파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
16DOMDocument 수정
구조를 명시적으로 다뤄야 할 때 DOMDocument를 사용하는 예제입니다.
PHP code
<?php
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->loadXML('<root/>');
$node = $dom->createElement('lang', 'php');
$dom->documentElement->appendChild($node);
echo $dom->saveXML();파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
17이미지 메타 정보 읽기
업로드 이미지나 썸네일 처리 전에 크기 정보를 읽는 예제입니다.
PHP code
<?php
$path = __DIR__ . '/og-image.png';
if (is_file($path)) {
print_r(getimagesize($path));
}파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
18stream context 로 HTTP 요청
간단한 HTTP 요청을 별도 라이브러리 없이 보내는 stream context 예제입니다.
PHP code
<?php
$context = stream_context_create([
'http' => [
'method' => 'GET',
'timeout' => 2,
'header' => "Accept: application/json
",
],
]);
$body = @file_get_contents('https://example.com', false, $context);
echo $body === false ? 'failed' : 'ok';파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
19경로 조합 헬퍼
운영체제별 경로 구분자 문제를 줄이기 위한 경로 헬퍼 예제입니다.
PHP code
<?php
function joinPath(string ...$parts): string {
return preg_replace('#/+#', '/', join('/', $parts));
}
echo joinPath(__DIR__, 'storage', 'logs', 'app.log');파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
20원자적 파일 교체
중간 상태 노출을 줄이기 위해 임시 파일 후 rename으로 교체하는 예제입니다.
PHP code
<?php
$target = sys_get_temp_dir() . '/config-live.json';
$temp = tempnam(sys_get_temp_dir(), 'cfg-');
file_put_contents($temp, json_encode(['version' => 2]));
rename($temp, $target);
echo $target;파일과 네트워크 예제는 권한, 인코딩, 잠금, 경로 처리까지 같이 확인해야 안전합니다.
파일 경로와 권한, 잠금 처리를 생략하면 로컬에서는 되더라도 배포 환경에서 자주 깨집니다.
정리하며
- 설정과 캐시 갱신은 임시 파일에 쓴 뒤 rename으로 교체해 잘린 파일을 막습니다.
- 큰 파일은 통째로 읽지 말고 fgets나 fgetcsv로 한 줄씩 스트리밍합니다.
- 업로드 검증은 확장자와 전송된 MIME 대신 finfo로 실제 내용을 판별합니다.
- 여러 프로세스가 쓰는 로그는 flock으로 구간을 감싸 줄 섞임을 방지합니다.
더 깊이 들어가고 싶다면 PHP 학습 라이브러리에서 다른 주제 가이드를 이어서 보거나, 언어 비교에서 같은 개념이 다른 언어에서 어떻게 표현되는지 확인해 보세요.