ReactPHP의 이벤트 루프
ReactPHP 이벤트 루프로 비블로킹 입출력을 실행합니다.
ReactPHP의 이벤트 루프은(는) CoddyKit의 무료 PHP Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 PHP Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
한 문장으로 보는 ReactPHP
ReactPHP는 단일 이벤트 루프를 중심으로 구축된 순수 PHP 기반의 의존성 없는 논블로킹 입출력 도구 모음입니다. 확장 기능이 필요하지 않으며, 일반 PHP에서 stream_select를 사용해 작동합니다(사용할 수 있으면 ext-event/ext-ev도 사용). 이를 통해 일반 PHP로 오래 실행되는 서버, 병렬 HTTP 클라이언트, 스트리밍 처리 흐름을 작성할 수 있습니다.
전역 루프
최신 ReactPHP(v1.2 이상)는 정적 React\EventLoop\Loop 파사드를 통해 기본 루프를 제공합니다. 어디서나 루프 인스턴스를 전달할 필요가 없습니다. 타이머로 콜백을 예약하면 루프는 더 이상 처리할 작업이 없을 때까지 실행됩니다.
<?php
require 'vendor/autoload.php';
use React\EventLoop\Loop;
Loop::addTimer(1.0, function () {
echo "fired after 1 second\n";
});
echo "scheduled\n";
// Loop::run() is auto-invoked at script shutdown in v1.2+타이머와 주기적 작업
논블로킹 타이밍은 sleep()을 대체합니다. addTimer는 한 번 실행되고 addPeriodicTimer는 반복 실행됩니다. 둘 중 하나를 취소하여 중지할 수 있습니다. 이러한 작업은 루프에 의해 구동되므로 타이머가 대기하는 동안에도 다른 작업이 계속 실행됩니다.
<?php
use React\EventLoop\Loop;
$count = 0;
$timer = Loop::addPeriodicTimer(0.5, function () use (&$count, &$timer) {
echo 'tick ' . (++$count) . "\n";
if ($count === 4) {
Loop::cancelTimer($timer); // stop after 4 ticks
}
});비동기 결과를 위한 프로미스
ReactPHP는 나중에 완료되는 모든 작업에 대해 프로미스를 반환합니다. 성공 시에는 then()을 연결하고 실패 시에는 두 번째 콜백을 연결합니다. 프로미스는 조합할 수 있으므로 콜백을 깊게 중첩하지 않고도 결과를 변환하고 연결할 수 있습니다.
<?php
use React\Promise\Promise;
function asyncDouble(int $n): Promise {
return new Promise(function ($resolve) use ($n) {
\React\EventLoop\Loop::addTimer(0.1, fn() => $resolve($n * 2));
});
}
asyncDouble(21)
->then(fn($r) => printf("result: %d\n", $r)) // 42
->catch(fn(\Throwable $e) => printf("err: %s\n", $e->getMessage()));비동기 클라이언트를 사용한 병렬 HTTP
가장 큰 장점은 하나의 스레드에서 많은 HTTP 요청을 동시에 실행하는 것입니다. React\Http\Browser는 프로미스를 반환하고, React\Promise\all()은 전체 묶음이 완료될 때까지 기다립니다. 1초가 걸리는 요청 10개가 10초가 아니라 약 1초 만에 완료됩니다.
<?php
use React\Http\Browser;
use function React\Promise\all;
$browser = new Browser();
$urls = [
'https://httpbin.org/delay/1',
'https://httpbin.org/delay/1',
'https://httpbin.org/delay/1',
];
$requests = array_map(fn($u) => $browser->get($u), $urls);
all($requests)->then(function (array $responses) {
echo 'all done: ' . count($responses) . " responses\n";
});파이버로 동기식처럼 읽히게 하기
콜백 체인은 복잡해지기 쉽습니다. ReactPHP는 react/async와 통합됩니다. 이 기능은 PHP 8.1 파이버를 사용하므로 프로미스에 await()하고 순차적인 코드를 작성할 수 있습니다. coroutine()/async() 래퍼는 함수를 파이버 내부에서 실행합니다.
<?php
use function React\Async\async;
use function React\Async\await;
use React\Http\Browser;
$main = async(function () {
$browser = new Browser();
$a = await($browser->get('https://httpbin.org/uuid'));
$b = await($browser->get('https://httpbin.org/uuid'));
echo (string) $a->getBody();
echo (string) $b->getBody();
});
$main();논블로킹 TCP 서버
ReactPHP는 서버에서 특히 강점을 발휘합니다. SocketServer가 연결을 수락하고 이벤트를 발생시키며, 각 연결 자체가 논블로킹 스트림입니다. 하나의 프로세스로 수천 개의 클라이언트를 동시에 처리할 수 있습니다.
<?php
use React\Socket\SocketServer;
use React\Socket\ConnectionInterface;
$socket = new SocketServer('127.0.0.1:8080');
$socket->on('connection', function (ConnectionInterface $conn) {
$conn->write("Welcome\n");
$conn->on('data', function ($data) use ($conn) {
$conn->write('echo: ' . $data); // streamed back, non-blocking
});
});
echo "listening on 127.0.0.1:8080\n";몇 줄로 만드는 HTTP 서버
react/http 서버는 요청을 처리할 때 Response 또는 그 프로미스를 반환하는 콜백을 사용합니다. 상시 실행 프로세스이므로 의존성을 한 번만 초기화하면 되지만, 요청 간 메모리 관리는 직접 담당해야 합니다.
<?php
use React\Http\HttpServer;
use React\Http\Message\Response;
use Psr\Http\Message\ServerRequestInterface;
use React\Socket\SocketServer;
$http = new HttpServer(function (ServerRequestInterface $request) {
return Response::json(['path' => $request->getUri()->getPath()]);
});
$http->listen(new SocketServer('0.0.0.0:8000'));
echo "http://0.0.0.0:8000\n";버퍼링 없는 스트리밍
ReactPHP 스트림은 배압을 구현하므로 읽기 가능한 스트림을 쓰기 가능한 스트림에 pipe()하고 런타임이 자동으로 처리 속도를 조절할 수 있습니다. 덕분에 파일을 RAM에 모두 적재하지 않고도 일정하고 적은 메모리 사용량으로 수 기가바이트의 데이터를 이동할 수 있습니다.
<?php
use React\Stream\ReadableResourceStream;
use React\Stream\WritableResourceStream;
$source = new ReadableResourceStream(fopen('big.log', 'r'));
$dest = new WritableResourceStream(fopen('php://stdout', 'w'));
$source->pipe($dest); // back-pressure aware, never buffers whole file치명적인 실수: 루프 차단
처리기 안에서 블로킹 함수를 호출하는 순간 모든 것이 중단됩니다. sleep(), 블로킹 PDO, URL에 대한 curl_exec, URL에서 실행하는 file_get_contents가 그 예입니다. 이러한 함수는 단일 스레드를 멈추고 모든 연결의 처리를 지연시킵니다. 지연에는 Loop::addTimer를, 쿼리에는 clue/reactphp-* 또는 비동기 데이터베이스 드라이버를, HTTP에는 Browser를 사용하는 등 ReactPHP의 대응 기능을 사용하십시오.
<?php
use React\EventLoop\Loop;
use function React\Async\await;
use React\Promise\Deferred;
// BAD: sleep(2); // freezes the entire loop
// GOOD: async delay that yields control
function delay(float $s): \React\Promise\PromiseInterface {
$d = new Deferred();
Loop::addTimer($s, fn() => $d->resolve(null));
return $d->promise();
}
// await(delay(2.0)); // inside a fiber/coroutine동시성 제한
제한 없이 작업을 다중화하면 파일 디스크립터가 고갈되거나 외부 API에 과도한 부하를 줄 수 있습니다. ReactPHP의 clue/mq-react는 동시성 상한을 두고 작업을 큐에 넣어, 나머지가 대기하는 동안 한 번에 N개의 프로미스를 처리합니다. 이렇게 하면 외부로 나가는 부하에 배압이 적용됩니다.
<?php
use Clue\React\Mq\Queue;
use React\Http\Browser;
$browser = new Browser();
// At most 10 in-flight requests across 1000 URLs
$q = new Queue(10, null, fn($url) => $browser->get($url));
$promises = array_map(fn($u) => $q($u), $thousandUrls ?? []);
\React\Promise\all($promises)->then(function ($responses) {
echo 'completed ' . count($responses) . " with cap of 10\n";
});간단한 확인
동시성을 해치지 않고 ReactPHP 처리기에서 2초 동안 어떻게 대기할 수 있습니까?
복습
ReactPHP는 순수 PHP에 비차단 입출력을 제공합니다:
- 하나의 이벤트 루프(
Loop파사드)가 타이머, 소켓, 스트림을 구동하므로 확장이 필요하지 않습니다. - 작업은 프라미스를 반환합니다.
all()은 요청을 병렬로 실행하고,react/async와 파이버를 사용하면 동기 코드처럼 보이는 코드에서await()할 수 있습니다. - 프로세스당 수천 개의 연결을 처리하는 지속형 TCP/HTTP 서버와 배압이 적용된 스트림을 구축할 수 있습니다.
- 루프를 절대 차단하지 마십시오 —
sleep이나 차단 입출력 대신 React 타이머와 비동기 드라이버를 사용하십시오.
AI 튜터와 함께 PHP을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 49
- 레슨
- 195
자주 묻는 질문
“ReactPHP의 이벤트 루프” 강의는 무료인가요?
네 — “ReactPHP의 이벤트 루프” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 PHP Academy 강의 전체를 잠금 해제할 수 있습니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“ReactPHP의 이벤트 루프”에서 뭘 배우나요?
ReactPHP 이벤트 루프로 비블로킹 입출력을 실행합니다. 브라우저에서 직접 실행하는 실습 코드로 PHP Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
PHP Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 PHP Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“ReactPHP의 이벤트 루프” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 PHP Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 PHP Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- PHP 동시성 모델
- PHP 8.1 파이버
- ReactPHP의 이벤트 루프
- Swoole을 활용한 고성능 서버