응답
응답은 실제로 support\Response
객체이며, 이 객체를 쉽게 생성하기 위해 webman은 몇 가지 헬퍼 함수를 제공합니다.
임의의 응답 반환
예제
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman');
}
}
response 함수 구현은 다음과 같습니다:
function response($body = '', $status = 200, $headers = array())
{
return new Response($status, $headers, $body);
}
먼저 빈 response
객체를 생성한 후 적절한 위치에서 $response->cookie()
$response->header()
$response->withHeaders()
$response->withBody()
를 사용하여 반환 내용을 설정할 수도 있습니다.
public function hello(Request $request)
{
// 객체 생성
$response = response();
// .... 비즈니스 로직 생략
// 쿠키 설정
$response->cookie('foo', 'value');
// .... 비즈니스 로직 생략
// HTTP 헤더 설정
$response->header('Content-Type', 'application/json');
$response->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
// .... 비즈니스 로직 생략
// 반환할 데이터 설정
$response->withBody('반환할 데이터');
return $response;
}
json 반환
예제
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return json(['code' => 0, 'msg' => 'ok']);
}
}
json 함수 구현은 다음과 같습니다:
function json($data, $options = JSON_UNESCAPED_UNICODE)
{
return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
}
xml 반환
예제
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
$xml = <<<XML
<?xml version='1.0' standalone='yes'?>
<values>
<truevalue>1</truevalue>
<falsevalue>0</falsevalue>
</values>
XML;
return xml($xml);
}
}
xml 함수 구현은 다음과 같습니다:
function xml($xml)
{
if ($xml instanceof SimpleXMLElement) {
$xml = $xml->asXML();
}
return new Response(200, ['Content-Type' => 'text/xml'], $xml);
}
뷰 반환
app/controller/FooController.php
파일을 새로 생성하여 다음과 같이 작성합니다.
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return view('foo/hello', ['name' => 'webman']);
}
}
app/view/foo/hello.html
파일을 새로 생성하여 다음과 같이 작성합니다.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>webman</title>
</head>
<body>
hello <?=htmlspecialchars($name)?>
</body>
</html>
리다이렉트
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return redirect('/user');
}
}
redirect 함수 구현은 다음과 같습니다:
function redirect($location, $status = 302, $headers = [])
{
$response = new Response($status, ['Location' => $location]);
if (!empty($headers)) {
$response->withHeaders($headers);
}
return $response;
}
헤더 설정
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman', 200, [
'Content-Type' => 'application/json',
'X-Header-One' => 'Header Value'
]);
}
}
또한 header
및 withHeaders
메서드를 사용하여 개별 또는 일괄적으로 헤더를 설정할 수 있습니다.
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman')
->header('Content-Type', 'application/json')
->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
}
}
헤더를 미리 설정하고 마지막에 반환할 데이터를 설정할 수도 있습니다.
public function hello(Request $request)
{
// 객체 생성
$response = response();
// .... 비즈니스 로직 생략
// HTTP 헤더 설정
$response->header('Content-Type', 'application/json');
$response->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
// .... 비즈니스 로직 생략
// 반환할 데이터 설정
$response->withBody('반환할 데이터');
return $response;
}
쿠키 설정
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman')
->cookie('foo', 'value');
}
}
쿠키를 미리 설정하고 마지막에 반환할 데이터를 설정할 수도 있습니다.
public function hello(Request $request)
{
// 객체 생성
$response = response();
// .... 비즈니스 로직 생략
// 쿠키 설정
$response->cookie('foo', 'value');
// .... 비즈니스 로직 생략
// 반환할 데이터 설정
$response->withBody('반환할 데이터');
return $response;
}
cookie 메서드의 전체 파라미터는 다음과 같습니다:
cookie($name, $value = '', $max_age = 0, $path = '', $domain = '', $secure = false, $http_only = false)
파일 스트림 반환
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response()->file(public_path() . '/favicon.ico');
}
}
- webman은 대용량 파일을 전송할 수 있습니다.
- 대용량 파일(2M 초과)에 대해 webman은 전체 파일을 한 번에 메모리로 읽지 않고 적절한 시점에 파일을 세그먼트로 읽고 전송합니다.
- webman은 클라이언트 수신 속도에 따라 파일 읽기 및 전송 속도를 최적화하여 최대한 빠른 속도로 파일을 전송하면서 메모리 사용량을 최소화합니다.
- 데이터 전송은 논블로킹으로 이루어지며 다른 요청 처리에 영향을 주지 않습니다.
- file 메서드는 자동으로
if-modified-since
헤더를 추가하고 다음 요청 시if-modified-since
헤더를 확인하여 파일이 수정되지 않았으면 304를 반환하여 대역폭을 절약합니다. - 전송된 파일은 자동으로 적절한
Content-Type
헤더와 함께 브라우저에 전송됩니다. - 파일이 존재하지 않을 경우 자동으로 404 응답으로 전환됩니다.
파일 다운로드
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response()->download(public_path() . '/favicon.ico', '파일명.ico');
}
}
download 메서드는 file 메서드와 기본적으로 유사하지만, 차이점은 다음과 같습니다:
- 다운로드할 파일명을 설정하면 파일이 다운로드되고 브라우저에 표시되지 않습니다.
- download 메서드는
if-modified-since
헤더를 확인하지 않습니다.
출력 가져오기
일부 라이브러리는 파일 내용을 표준 출력에 직접 인쇄하므로 데이터가 명령줄 터미널에 인쇄되고 브라우저에 전송되지 않습니다. 이 경우 ob_start();
ob_get_clean();
을 사용하여 데이터를 변수에 캡처한 다음 데이터를 브라우저로 전송해야 합니다. 예를 들어:
<?php
namespace app\controller;
use support\Request;
class ImageController
{
public function get(Request $request)
{
// 이미지 생성
$im = imagecreatetruecolor(120, 20);
$text_color = imagecolorallocate($im, 233, 14, 91);
imagestring($im, 1, 5, 5, 'A Simple Text String', $text_color);
// 출력 캡처 시작
ob_start();
// 이미지 출력
imagejpeg($im);
// 이미지 내용 얻기
$image = ob_get_clean();
// 이미지 전송
return response($image)->header('Content-Type', 'image/jpeg');
}
}
청크 응답
때때로 우리는 응답을 청크로 전송하고 싶습니다. 다음 예제를 참조할 수 있습니다.
<?php
namespace app\controller;
use support\Request;
use support\Response;
use Workerman\Protocols\Http\Chunk;
use Workerman\Timer;
class IndexController
{
public function index(Request $request): Response
{
// 연결 가져오기
$connection = $request->connection;
// HTTP 패킷 본문 타이머로 전송
$timer = Timer::add(1, function () use ($connection, &$timer) {
static $i = 0;
if ($i++ < 10) {
// HTTP 패킷 본문 전송
$connection->send(new Chunk($i));
} else {
// 더 이상 필요 없는 타이머 삭제, 메모리 누수 방지
Timer::del($timer);
// 빈 Chunk 패킷을 출력하여 클라이언트에 응답 종료 알림
$connection->send(new Chunk(''));
}
});
// Transfer-Encoding: chunked가 포함된 HTTP 헤더 먼저 출력, HTTP 패킷 본문은 비동기 전송
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}
큰 모델을 호출하는 경우, 다음 예제를 참조할 수 있습니다.
composer require webman/openai
<?php
namespace app\controller;
use support\Request;
use Webman\Openai\Chat;
use Workerman\Protocols\Http\Chunk;
class ChatController
{
public function completions(Request $request)
{
$connection = $request->connection;
// https://api.openai.com 국내에서 접근할 수 없는 경우 https://api.openai-proxy.com 주소를 사용할 수 있습니다.
$chat = new Chat(['apikey' => 'sk-xx', 'api' => 'https://api.openai.com']);
$chat->completions(
[
'model' => 'gpt-3.5-turbo',
'stream' => true,
'messages' => [['role' => 'user', 'content' => 'hello']],
], [
'stream' => function($data) use ($connection) {
// openai 인터페이스에서 데이터가 반환될 때 브라우저에 전송
$connection->send(new Chunk(json_encode($data, JSON_UNESCAPED_UNICODE) . "\n"));
},
'complete' => function($result, $response) use ($connection) {
// 응답 종료 시 오류가 있는지 확인
if (isset($result['error'])) {
$connection->send(new Chunk(json_encode($result, JSON_UNESCAPED_UNICODE) . "\n"));
}
// 빈 chunk 반환은 응답 종료를 나타냅니다.
$connection->send(new Chunk(''));
},
]);
// 먼저 HTTP 헤더를 반환하고 이후 데이터는 비동기로 반환
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}