Slim 프레임워크가 독자적인 방식이 아닌 PHP 표준 권고안(PSR)을 완벽하게 준수하고 있다는 것은 실무에서 매우 강력한 장점입니다. HTTP 메시지를 정의하는 PSR-7과 미들웨어 파이프라인을 정의하는 PSR-15의 개념을 이해하여, 특정 프레임워크에 종속되지 않는 재사용 가능한 코드를 작성하는 방법을 배웁니다.
1. PSR-7: HTTP 메시지 표준 인터페이스
과거의 PHP 개발자들은 $_GET, $_POST, $_SERVER 같은 전역 변수(Superglobals)에 직접 접근하여 코드를 작성했습니다. PSR-7은 이러한 HTTP 요청(Request)과 응답(Response)을 불변(Immutable) 객체로 추상화한 PHP 커뮤니티의 표준입니다. Slim의 라우터 콜백에 주입되는 $request와 $response 객체가 바로 이 규격을 따릅니다.
PSR-7 객체는 상태가 변경되지 않습니다(Immutable). 예를 들어 $response->withStatus(404)를 호출하면 기존 객체의 상태가 변하는 것이 아니라, 상태 코드가 404로 설정된 새로운 복제본(Clone) 객체가 생성되어 반환됩니다. 따라서 반드시 $response = $response->withStatus(404); 처럼 변수에 다시 할당하거나 체이닝(Chaining)을 통해 Return 해야만 정상적으로 동작합니다.
2. PSR-15: 미들웨어 표준 인터페이스
PSR-15는 이 PSR-7 기반의 Request 객체를 받아 가공한 뒤 Response 객체를 반환하는 미들웨어(Middleware) 파이프라인의 표준 규격입니다. 커스텀 미들웨어 클래스를 작성할 때는 반드시 MiddlewareInterface를 구현(implements)해야 합니다.
ApiTokenMiddleware.php
<?php
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Server\MiddlewareInterface;
use Slim\Psr7\Response as SlimResponse;
// PSR-15 표준을 완벽히 준수하는 커스텀 미들웨어
class ApiTokenMiddleware implements MiddlewareInterface
{
// PSR-15의 유일한 요구사항: process 메서드 구현
public function process(Request $request, RequestHandler $handler): Response
{
// 1. 요청(Request) 검사 (안쪽 계층으로 들어가기 전)
$token = $request->getHeaderLine('X-API-Key');
if ($token !== 'secret_123') {
// [검증 실패] : 다음 계층(라우터)으로 보내지 않고, 즉시 401 에러를 담아 반환 (단락 평가)
$response = new SlimResponse();
$response->getBody()->write('Invalid Token');
return $response->withStatus(401);
}
// 2. [검증 성공] : RequestHandler를 통해 다음 미들웨어(또는 최종 코어 라우터)로 Request를 넘겨줌
// 그리고 비즈니스 로직 처리가 모두 끝난 후 되돌아온 Response 객체를 받음
$response = $handler->handle($request);
// 3. 응답(Response) 가공 후 바깥 계층으로 반환 (밖으로 빠져나올 때)
return $response->withHeader('X-Processed-By', 'ApiTokenMiddleware');
}
}
?>
Onion Architecture Pipeline Execution Log
[Info] GET /api/data 요청 수신
> Pipeline Execution Started...
[Middleware 1] 요청 진입 (LoggingMiddleware)
[Middleware 2] 요청 진입 (ApiTokenMiddleware)
└ 토큰 검증 성공 ('secret_123' 일치)
[Core Router] 비즈니스 로직 실행 및 200 OK 응답 객체 생성
[Middleware 2] 응답 반환 전 (ApiTokenMiddleware)
└ 헤더 추가: X-Processed-By=ApiTokenMiddleware
[Middleware 1] 응답 반환 전 (LoggingMiddleware)
[Success] 클라이언트에게 최종 Response 객체 반환 완료
[Info] GET /api/data 요청 수신 (토큰 없음)
> Pipeline Execution Started...
[Middleware 1] 요청 진입 (LoggingMiddleware)
[Middleware 2] 요청 진입 (ApiTokenMiddleware)
└ ⛔ 토큰 검증 실패 (Invalid Token)
└ [Short-Circuit] 코어 라우터로 진입하지 않고 즉시 차단!
[Middleware 1] 응답 반환 전 (LoggingMiddleware)
[Error] 클라이언트에게 401 Unauthorized Response 반환 완료
PSR 표준의 압도적인 장점: 프레임워크 상호 호환성
과거에는 각 프레임워크(Laravel, CodeIgniter 등)마다 미들웨어를 작성하는 규칙과 Request 객체의 모양이 제각각이어서 코드를 재사용할 수 없었습니다. 하지만 PSR-15를 준수하는 미들웨어 클래스로 작성해두면, 현재 만들고 있는 Slim 앱 뿐만 아니라 Zend Expressive(Mezzio) 등 다른 PSR-15 호환 프레임워크로 시스템을 이전하더라도 코드 단 한 줄 수정 없이 미들웨어를 100% 재사용할 수 있다는 놀라운 이점이 있습니다.
양파(Onion) 아키텍처 파이프라인 흐름
PSR-15 미들웨어는 흔히 양파 껍질 구조에 비유됩니다. 위의 콘솔 실행 로그 목업에서 볼 수 있듯, Request가 껍질(미들웨어 1 -> 2)을 차례대로 뚫고 코어(라우터)로 들어갔다가, 처리가 완료된 후 Response로 감싸져서 역순(미들웨어 2 -> 1)으로 바깥 껍질로 빠져나오는 흐름이 핵심입니다. 또한 인증에 실패할 경우 코어까지 들어가지 않고 미들웨어 단계에서 즉시 튕겨 나오는(단락 평가) 모습도 확인할 수 있습니다.