Dart Shelf는 Dart 언어로 작성된 웹 서버 프레임워크로, 간단하고 조합 가능한 웹 애플리케이션을 구축하는 데 사용됩니다.
미들웨어 기반 아키텍처: Shelf는 미들웨어(middleware) 패턴을 중심으로 설계되어 있습니다. 각 미들웨어는 HTTP 요청을 처리하고 다음 미들웨어로 전달하거나 응답을 반환할 수 있습니다.
간단한 API: 핵심 개념은 Request와 Response 객체, 그리고 이들을 연결하는 Handler 함수입니다. Handler는 Request를 받아 Response를 반환하는 함수입니다.
조합 가능성: 여러 미들웨어를 쉽게 조합하여 복잡한 웹 애플리케이션을 구성할 수 있습니다. 예를 들어 로깅, 인증, CORS 처리 등을 각각의 미들웨어로 분리할 수 있습니다.
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
void main() async {
// 간단한 핸들러 정의
var handler = const Pipeline()
.addMiddleware(logRequests())
.addHandler(_echoRequest);
// 서버 시작
var server = await io.serve(handler, 'localhost', 8080);
print('Server running on localhost:${server.port}');
}
Response _echoRequest(Request request) {
return Response.ok('Hello, World!');
}
Pipeline: 여러 미들웨어를 순차적으로 연결하는 클래스입니다.
Router: URL 경로 기반 라우팅을 제공하는 패키지(shelf_router)입니다.
Static Files: 정적 파일 서빙을 위한 미들웨어(shelf_static)도 제공됩니다.
Shelf는 Express.js나 Flask와 비슷한 철학을 가지고 있지만, Dart의 강타입 시스템과 async/await 패턴을 활용하여 더 안전하고 현대적인 웹 개발 경험을 제공합니다. 특히 Flutter 개발자들에게 백엔드 개발 시 일관된 언어 환경을 제공한다는 장점이 있습니다.
Dart create name -t server-shelf
cd test
dart run bin/server.dart
위와 같이 입력하면 localhost:8080으로 접근 시 server open
Angel3: 풀스택 웹 프레임워크로 ORM, 인증, 웹소켓 등 다양한 기능을 내장하고 있습니다. Express.js나 Django와 비슷한 포지션입니다.
Aqueduct: 과거에 인기가 있었던 프레임워크지만 현재는 개발이 중단되었습니다. 대신 Conduit이 후속 프로젝트로 이어지고 있습니다.
Conduit: Aqueduct의 후속 프로젝트로, RESTful API 개발에 특화되어 있습니다.
Serverpod: 비교적 새로운 풀스택 프레임워크로, 타입 안전성과 실시간 통신에 중점을 둡니다. 클라이언트-서버 간 코드 공유가 가능합니다.
Dart Frog: Vercel의 Next.js에서 영감을 받은 풀스택 프레임워크로, 파일 기반 라우팅과 빠른 핫 리로드를 제공합니다.
Vania: Laravel에서 영감을 받은 프레임워크로, 웹 개발에 필요한 다양한 기능을 제공합니다.
간단한 API나 마이크로서비스: Shelf가 적합합니다.
풀스택 애플리케이션: Angel3, Serverpod, Dart Frog 등이 좋은 선택입니다.
RESTful API 중심: Conduit이나 Shelf + Router 조합이 적합합니다.
빠른 프로토타이핑: Dart Frog가 좋은 선택지입니다.
각 프레임워크마다 철학과 접근 방식이 다르므로, 프로젝트의 요구사항과 개발자의 선호도에 따라 선택하시면 됩니다. Shelf는 이 중에서도 가장 미니멀하고 유연한 선택지라고 할 수 있습니다.
Dart Shelf에서 Swagger를 구성하는 방법을 설명드리겠습니다.
pubspec.yaml에 필요한 패키지들을 추가합니다:
dependencies:
shelf: ^1.4.1
shelf_router: ^1.1.4
shelf_swagger_ui: ^1.0.0
swagger_dart_code_generator: ^2.0.0
json_annotation: ^4.8.1
dev_dependencies:
build_runner: ^2.4.7
json_serializable: ^6.7.1
먼저 OpenAPI 스펙 파일을 작성합니다 (openapi.yaml):
openapi: 3.0.0
info:
title: My API
version: 1.0.0
description: API documentation for my Dart Shelf application
paths:
/users:
get:
summary: Get all users
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
post:
summary: Create a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUser'
responses:
'201':
description: User created successfully
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
CreateUser:
type: object
required:
- name
- email
properties:
name:
type: string
email:
type: string
import 'dart:io';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';
import 'package:shelf_swagger_ui/shelf_swagger_ui.dart';
void main() async {
final app = Router();
// API 라우트
app.get('/users', _getUsers);
app.post('/users', _createUser);
// Swagger UI 설정
app.mount('/docs/', swaggerUI('openapi.yaml'));
// OpenAPI 스펙 파일 서빙
app.get('/openapi.yaml', (Request request) {
final file = File('openapi.yaml');
return Response.ok(
file.readAsStringSync(),
headers: {'content-type': 'application/x-yaml'},
);
});
final handler = Pipeline()
.addMiddleware(logRequests())
.addMiddleware(_corsHeaders())
.addHandler(app);
final server = await io.serve(handler, 'localhost', 8080);
print('Server running on localhost:${server.port}');
print('Swagger UI available at: http://localhost:${server.port}/docs/');
}
Response _getUsers(Request request) {
return Response.ok(
'[{"id": 1, "name": "John", "email": "john@example.com"}]',
headers: {'content-type': 'application/json'},
);
}
Response _createUser(Request request) {
// 실제로는 request body를 파싱하고 처리해야 함
return Response(
201,
body: '{"id": 2, "name": "Jane", "email": "jane@example.com"}',
headers: {'content-type': 'application/json'},
);
}
Middleware _corsHeaders() {
return (handler) {
return (request) async {
final response = await handler(request);
return response.change(headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
});
};
};
}
만약 shelf_swagger_ui 패키지를 사용하지 않는다면, 직접 HTML을 서빙할 수도 있습니다:
app.get('/docs/', (Request request) {
return Response.ok('''
<!DOCTYPE html>
<html>
<head>
<title>API Documentation</title>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui-bundle.js"></script>
<script>
SwaggerUIBundle({
url: '/openapi.yaml',
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.presets.standalone
]
});
</script>
</body>
</html>
''', headers: {'content-type': 'text/html'});
});
서버를 실행한 후 http://localhost:8080/docs/에 접속하면 Swagger UI를 통해 API 문서를 확인하고 테스트할 수 있습니다.
이 방법으로 Shelf 애플리케이션에 Swagger 문서화를 추가할 수 있으며, API의 스펙을 시각적으로 확인하고 테스트할 수 있는 환경을 제공할 수 있습니다.