0Pricing
PHP Academy · 강의

PHP 동시성 모델

블로킹 입출력과 비동기 방식이 도움이 되는 지점을 이해합니다.

PHP 동시성 모델은(는) CoddyKit의 무료 PHP Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 PHP Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

PHP는 기본적으로 동기식입니다

일반적인 PHP는 프로세스 하나에서 요청 하나를 위에서 아래로 처리하며 차단형 입출력을 사용합니다. 느린 API를 호출하는 file_get_contents()는 응답 바이트가 도착할 때까지 스크립트 전체를 멈춥니다. 공유하지 않는 상태 모델(요청마다 새 프로세스를 만들고 끝나면 상태를 폐기하는 방식)은 매우 단순하지만, 작업자 하나가 한 번에 정확히 한 가지 일만 할 수 있다는 뜻이기도 합니다. 이 수업에서는 이러한 방식이 언제 문제가 되고 비동기 처리가 언제 도움이 되는지 살펴봅니다.

차단형 입출력 측정하기

계산이 아니라 대기가 실제 경과 시간을 지배하는 모습을 확인하십시오. 1초가 걸리는 요청 세 개를 순차적으로 처리하면 CPU가 내내 유휴 상태여도 약 3초가 걸립니다.

<?php
$start = microtime(true);
foreach (['a', 'b', 'c'] as $job) {
    usleep(1_000_000); // simulate a 1s blocking network call
    echo "done $job\n";
}
printf("elapsed: %.2fs\n", microtime(true) - $start); // ~3.00s

CPU 중심 작업과 입출력 중심 작업

동시성은 입출력 중심 작업, 즉 네트워크·디스크·데이터베이스를 기다리는 시간이 많은 작업에만 도움이 됩니다. CPU 중심 작업(해시 계산, 이미지 크기 조정, 구문 분석)은 단일 스레드만으로도 이미 포화되므로 비동기 처리는 일정 관리 비용만 추가합니다. 먼저 원인을 파악하십시오.

  • 느린 HTTP 호출이나 데이터베이스 질의가 많음 → 비동기 처리가 큰 효과를 냅니다.
  • 촘촘한 수치 반복문 → 이벤트 루프가 아니라 병렬 처리(프로세스/스레드)를 사용하십시오.

지금까지 PHP가 확장된 방식

역사적으로 PHP는 프로세스 수준의 동시성으로 확장되었습니다. PHP-FPM은 작업자 프로세스 풀을 유지하고, 각 프로세스는 한 번에 요청 하나를 처리하며 OS가 이들을 다중화합니다. 이는 요청 간에는 실제 동시성을 제공하지만 요청 내부에는 아무것도 제공하지 않습니다. 일반적인 차단형 코드만으로는 하나의 처리기 안에서 API 호출 50개를 동시에 분기할 수 없습니다.

이벤트 루프의 개념

비동기 PHP는 이벤트 루프를 도입합니다. 단일 스레드가 입출력 작업을 논블로킹으로 등록한 다음, 어떤 작업이든 준비될 때까지 stream_select()/epoll에서 대기하며 데이터가 도착하면 콜백을 실행합니다. 하나의 소켓에서 응답을 기다리며 유휴 상태로 머무르지 않기 때문에 한 스레드로 수천 개의 소켓을 번갈아 처리할 수 있습니다.

<?php
// Conceptual loop: poll many non-blocking streams at once
$streams = openManyNonBlockingSockets();
while ($streams) {
    $read = $streams; $write = $except = [];
    stream_select($read, $write, $except, null); // sleeps until ANY is ready
    foreach ($read as $s) {
        handleReadyStream($s); // runs only the sockets that have data now
    }
}

협력형, 선점형 아님

비동기 PHP는 협력형입니다. 작업은 입출력 지점에서 자발적으로 제어권을 양보할 때까지 실행됩니다. 선점이 없으므로 오래 걸리는 블로킹 호출이나 빽빽한 CPU 루프 하나가 전체 이벤트 루프를 멈추고 다른 모든 작업의 실행을 막습니다. 핵심 원칙은 이벤트 루프 작업 안에서 블로킹 함수(sleep, 블로킹 PDO, file_get_contents)를 절대 호출하지 않는 것입니다. 루프의 비동기 대응 함수를 사용하십시오.

PHP의 세 가지 접근법

최신 PHP는 계층화된 도구 모음을 제공합니다.

  • 파이버(PHP 8.1, 핵심 기능) — 호출 스택을 일시 중지하고 재개하는 저수준 기본 요소입니다. 완전한 프레임워크가 아니라 구성 요소에 해당합니다.
  • ReactPHP / 앰프 — 확장 기능 없이 논블로킹 입출력을 처리하도록 스트림(그리고 이제는 파이버)을 기반으로 만든 사용자 영역 이벤트 루프입니다.
  • 스울 / OpenSwoole — 코루틴, 고성능 서버, 블로킹 호출 연동 기능을 제공하는 C 확장 기능입니다.

전략을 선택하기 전에 실행 시점에 무엇을 사용할 수 있는지 확인하십시오.

<?php
echo 'Fibers: ' . (class_exists('Fiber') ? 'yes' : 'no'), PHP_EOL;
echo 'Swoole ext: ' . (extension_loaded('swoole') ? 'yes' : 'no'), PHP_EOL;
echo 'parallel ext: ' . (extension_loaded('parallel') ? 'yes' : 'no'), PHP_EOL;
echo 'PHP ' . PHP_VERSION, PHP_EOL;

프로미스는 미래의 값을 모델링합니다

파이버가 등장하기 전에는 비동기 PHP가 아직 준비되지 않은 결과를 프로미스로 노출했습니다. 프로미스는 나중에 결정될 값을 위한 자리 표시자입니다. then()을 사용해 콜백을 연결합니다. 이 방식도 작동하지만 중첩된 체인으로 이어지기 쉽습니다. 파이버를 사용하면 라이브러리가 이 구조를 동기 코드처럼 보이는 await 뒤로 감출 수 있습니다.

<?php
use React\Promise\Promise;

$promise = new Promise(function ($resolve) {
    // resolved later when I/O completes
    $resolve(42);
});
$promise->then(function ($value) {
    echo "got $value\n";
});

동시성과 병렬성 비교

두 개념을 정확히 구분해야 합니다.

  • 동시성 — 하나의 스레드(이벤트 루프)에서 여러 작업이 진행 중인 상태로 번갈아 실행되는 것입니다. 입출력에 적합합니다.
  • 병렬성 — 여러 코어에서 여러 작업이 동시에 실행되는 것입니다(프로세스, parallel 확장 기능, 스울 작업자). CPU 작업에 필요합니다.

이벤트 루프는 병렬성이 아니라 동시성을 제공합니다. 아래 코드 조각은 proc_open을 사용한 프로세스 수준 병렬성을 보여 줍니다. 세 작업자가 서로 다른 OS 프로세스에서 동시에 실행됩니다.

<?php
$procs = [];
foreach (range(1, 3) as $i) {
    // Each worker is a separate OS process -> real parallelism
    $procs[] = proc_open(
        "php -r 'usleep(500000); echo \"worker $i done\\n\";'",
        [1 => ['pipe', 'w']], $pipes
    );
}
foreach ($procs as $p) { proc_close($p); }
echo "all workers launched in parallel\n";

NOT 비동기를 사용하지 말아야 할 때

비동기 방식에는 비용이 따릅니다. 오래 실행되는 작업자를 사용하면 PHP가 요청 사이에 상태를 더 이상 초기화하지 않으므로 메모리 누수, 정적 캐시, 오래되어 유효하지 않은 연결을 직접 관리해야 합니다. 요청 처리기가 데이터베이스 쿼리 하나를 실행하고 반환하는 경우에는 PHP-FPM이 더 단순하고 속도도 동일하게 빠릅니다. 입출력 다중화, WebSockets/롱 폴링, 스트리밍 또는 상시 실행 서버가 필요할 때 비동기 방식을 선택하십시오.

진정한 병렬성: ext-parallel과 프로세스

CPU 연산 중심의 다중 작업을 처리할 때 PHP는 실제 OS 수준 병렬성을 실행할 수 있습니다. parallel 확장 기능은 메모리가 격리된 작업자 스레드를 생성합니다. 또는 proc_open/pcntl_fork로 프로세스를 생성할 수도 있습니다. 이벤트 루프와 달리 이러한 방식은 여러 코어를 동시에 사용하므로, 병목이 대기가 아니라 계산일 때 적합합니다.

<?php
// Requires ext-parallel — runs tasks on separate threads/cores
use parallel\Runtime;

$runtime = new Runtime();
$future = $runtime->run(function (): int {
    $sum = 0;
    for ($i = 0; $i < 1_000_000; $i++) $sum += $i; // CPU work on another core
    return $sum;
});
echo $future->value(), PHP_EOL;

간단한 확인

이벤트 루프 작업 안에서 블로킹 sleep(2)를 호출하면 어떻게 됩니까?

요약

이제 PHP 동시성에 대한 개념을 갖추었습니다.

  • 기본 PHP는 동기식이고 블로킹 방식이며 공유하지 않는 구조입니다. FPM은 하나의 요청 내부가 아니라 요청 간에 확장됩니다.
  • 비동기 방식은 논블로킹 스트림을 사용하는 단일 스레드 이벤트 루프를 통해 입출력 중심 작업을 돕습니다.
  • 이 모델은 협력형이므로 루프를 절대 블로킹해서는 안 됩니다. 긴 CPU 작업에는 동시성이 아니라 병렬성이 필요합니다.
  • PHP 도구 모음은 파이버(기본 요소), ReactPHP/앰프(사용자 영역 루프와 프로미스), 스울(코루틴 확장 기능)로 구성됩니다.

자주 묻는 질문

“PHP 동시성 모델” 강의는 무료인가요?

네 — “PHP 동시성 모델” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 PHP Academy 강의 전체를 잠금 해제할 수 있습니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“PHP 동시성 모델”에서 뭘 배우나요?

블로킹 입출력과 비동기 방식이 도움이 되는 지점을 이해합니다. 브라우저에서 직접 실행하는 실습 코드로 PHP Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

PHP Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 PHP Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“PHP 동시성 모델” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 PHP Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 PHP Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. PHP 동시성 모델
  2. PHP 8.1 파이버
  3. ReactPHP의 이벤트 루프
  4. Swoole을 활용한 고성능 서버
← PHP Academy(으)로 돌아가기