--- url: 'https://elysiajs.docsforall.com/integrations/ai-sdk.md' --- # AI SDK와의 통합 Elysia는 응답 스트리밍을 쉽게 지원하여 [Vercel AI SDK](https://vercel.com/docs/ai)와 원활하게 통합할 수 있습니다. ## 응답 스트리밍 Elysia는 `ReadableStream`과 `Response`의 연속 스트리밍을 지원하여 AI SDK에서 직접 스트림을 반환할 수 있습니다. ```ts import { Elysia } from 'elysia' import { streamText } from 'ai' import { openai } from '@ai-sdk/openai' new Elysia().get('/', () => { const stream = streamText({ model: openai('gpt-5'), system: 'You are Yae Miko from Genshin Impact', prompt: 'Hi! How are you doing?' }) // ReadableStream을 그냥 반환하면 됩니다 return stream.textStream // [!code ++] // UI Message Stream도 지원됩니다 return stream.toUIMessageStream() // [!code ++] }) ``` Elysia는 스트림을 자동으로 처리하여 다양한 방식으로 사용할 수 있습니다. ## Server Sent Event Elysia는 `ReadableStream`을 `sse` 함수로 감싸기만 하면 스트리밍 응답을 위한 Server Sent Event도 지원합니다. ```ts import { Elysia, sse } from 'elysia' // [!code ++] import { streamText } from 'ai' import { openai } from '@ai-sdk/openai' new Elysia().get('/', () => { const stream = streamText({ model: openai('gpt-5'), system: 'You are Yae Miko from Genshin Impact', prompt: 'Hi! How are you doing?' }) // 각 청크가 Server Sent Event로 전송됩니다 return sse(stream.textStream) // [!code ++] // UI Message Stream도 지원됩니다 return sse(stream.toUIMessageStream()) // [!code ++] }) ``` ## Response로 반환 [Eden](/eden/overview)에서 추가 사용을 위한 스트림의 타입 안전성이 필요하지 않다면, 스트림을 응답으로 직접 반환할 수 있습니다. ```ts import { Elysia } from 'elysia' import { ai } from 'ai' import { openai } from '@ai-sdk/openai' new Elysia().get('/', () => { const stream = streamText({ model: openai('gpt-5'), system: 'You are Yae Miko from Genshin Impact', prompt: 'Hi! How are you doing?' }) return stream.toTextStreamResponse() // [!code ++] // UI Message Stream Response는 SSE를 사용합니다 return stream.toUIMessageStreamResponse() // [!code ++] }) ``` ## 수동 스트리밍 스트리밍을 더 세밀하게 제어하려면 제너레이터 함수를 사용하여 청크를 수동으로 yield할 수 있습니다. ```ts import { Elysia, sse } from 'elysia' import { ai } from 'ai' import { openai } from '@ai-sdk/openai' new Elysia().get('/', async function* () { const stream = streamText({ model: openai('gpt-5'), system: 'You are Yae Miko from Genshin Impact', prompt: 'Hi! How are you doing?' }) for await (const data of stream.textStream) // [!code ++] yield sse({ // [!code ++] data, // [!code ++] event: 'message' // [!code ++] }) // [!code ++] yield sse({ event: 'done' }) }) ``` ## Fetch AI SDK가 사용 중인 모델을 지원하지 않는 경우에도 `fetch` 함수를 사용하여 AI SDK에 요청하고 응답을 직접 스트리밍할 수 있습니다. ```ts import { Elysia, fetch } from 'elysia' new Elysia().get('/', () => { return fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.OPENAI_API_KEY}` }, body: JSON.stringify({ model: 'gpt-5', stream: true, messages: [ { role: 'system', content: 'You are Yae Miko from Genshin Impact' }, { role: 'user', content: 'Hi! How are you doing?' } ] }) }) }) ``` Elysia는 스트리밍 지원과 함께 fetch 응답을 자동으로 프록시합니다. *** 자세한 정보는 [AI SDK 문서](https://ai-sdk.dev/docs/introduction)를 참조하세요. --- --- url: 'https://elysiajs.docsforall.com/plugins/graphql-apollo.md' --- # GraphQL Apollo Plugin GraphQL Apollo를 사용하기 위한 [elysia](https://github.com/elysiajs/elysia) 플러그인입니다. 설치 방법: ```bash bun add graphql @elysiajs/apollo @apollo/server ``` 사용 방법: ```typescript import { Elysia } from 'elysia' import { apollo, gql } from '@elysiajs/apollo' const app = new Elysia() .use( apollo({ typeDefs: gql` type Book { title: String author: String } type Query { books: [Book] } `, resolvers: { Query: { books: () => { return [ { title: 'Elysia', author: 'saltyAom' } ] } } } }) ) .listen(3000) ``` `/graphql`에 액세스하면 Apollo GraphQL playground가 표시됩니다. ## Context Elysia는 Express가 사용하는 Node의 `HttpRequest` 및 `HttpResponse`와 다른 Web Standard Request와 Response를 기반으로 하기 때문에 context에서 `req, res`가 undefined입니다. 이 때문에 Elysia는 라우트 매개변수처럼 `context`로 둘 다 대체합니다. ```typescript const app = new Elysia() .use( apollo({ typeDefs, resolvers, context: async ({ request }) => { const authorization = request.headers.get('Authorization') return { authorization } } }) ) .listen(3000) ``` ## Config 이 플러그인은 Apollo의 [ServerRegistration](https://www.apollographql.com/docs/apollo-server/api/apollo-server/#options) (`ApolloServer`의 생성자 매개변수)를 확장합니다. Elysia와 함께 Apollo Server를 구성하기 위한 확장 매개변수는 다음과 같습니다. ### path @default `"/graphql"` Apollo Server를 노출할 경로입니다. ### enablePlayground @default `process.env.ENV !== 'production'` Apollo가 Apollo Playground를 제공할지 여부를 결정합니다. --- --- url: 'https://elysiajs.docsforall.com/integrations/astro.md' --- # Astro와의 통합 [Astro Endpoint](https://docs.astro.build/en/core-concepts/endpoints/)를 사용하여 Astro에서 직접 Elysia를 실행할 수 있습니다. 1. **astro.config.mjs**에서 **output**을 **server**로 설정 ```javascript // astro.config.mjs import { defineConfig } from 'astro/config' // https://astro.build/config export default defineConfig({ output: 'server' // [!code ++] }) ``` 2. **pages/\[...slugs].ts** 생성 3. **\[...slugs].ts**에서 Elysia 서버 생성 또는 가져오기 4. 사용하려는 메서드 이름으로 핸들러 내보내기 ```typescript // pages/[...slugs].ts import { Elysia, t } from 'elysia' const app = new Elysia() .get('/api', () => 'hi') .post('/api', ({ body }) => body, { body: t.Object({ name: t.String() }) }) const handle = ({ request }: { request: Request }) => app.handle(request) // [!code ++] export const GET = handle // [!code ++] export const POST = handle // [!code ++] ``` WinterCG 호환성 덕분에 Elysia는 정상적으로 작동합니다. Elysia는 Bun에서 실행되도록 설계되었으므로 [Astro를 Bun에서 실행](https://docs.astro.build/en/recipes/bun)하는 것을 권장합니다. ::: tip WinterCG 지원 덕분에 Astro를 Bun에서 실행하지 않아도 Elysia 서버를 실행할 수 있습니다. ::: 이 방식을 사용하면 프론트엔드와 백엔드를 단일 저장소에 배치하고 Eden을 통해 End-to-end 타입 안전성을 확보할 수 있습니다. 자세한 정보는 [Astro Endpoint](https://docs.astro.build/en/core-concepts/endpoints/)를 참조하세요. ## Prefix Elysia 서버를 앱 라우터의 루트 디렉터리가 아닌 다른 곳에 배치하는 경우, Elysia 서버에 접두사를 주석으로 달아야 합니다. 예를 들어, Elysia 서버를 **pages/api/\[...slugs].ts**에 배치하는 경우, Elysia 서버에 접두사로 **/api**를 주석으로 달아야 합니다. ```typescript // pages/api/[...slugs].ts import { Elysia, t } from 'elysia' const app = new Elysia({ prefix: '/api' }) // [!code ++] .get('/', () => 'hi') .post('/', ({ body }) => body, { body: t.Object({ name: t.String() }) }) const handle = ({ request }: { request: Request }) => app.handle(request) // [!code ++] export const GET = handle // [!code ++] export const POST = handle // [!code ++] ``` 이렇게 하면 어디에 배치하든 Elysia 라우팅이 제대로 작동합니다. --- --- url: 'https://elysiajs.docsforall.com/plugins/bearer.md' --- # Bearer Plugin RFC6750에 명시된 Bearer 토큰을 검색하기 위한 [elysia](https://github.com/elysiajs/elysia) 플러그인입니다. 설치 방법: ```bash bun add @elysiajs/bearer ``` 사용 방법: ```typescript twoslash import { Elysia } from 'elysia' import { bearer } from '@elysiajs/bearer' const app = new Elysia() .use(bearer()) .get('/sign', ({ bearer }) => bearer, { beforeHandle({ bearer, set, status }) { if (!bearer) { set.headers[ 'WWW-Authenticate' ] = `Bearer realm='sign', error="invalid_request"` return status(400, 'Unauthorized') } } }) .listen(3000) ``` 이 플러그인은 [RFC6750](https://www.rfc-editor.org/rfc/rfc6750#section-2)에 명시된 Bearer 토큰을 검색하는 데 사용됩니다. 이 플러그인은 서버의 인증 유효성 검증을 처리하지 않습니다. 대신, 개발자가 직접 유효성 검사 로직을 적용할 수 있도록 결정권을 남겨둡니다. --- --- url: 'https://elysiajs.docsforall.com/essential/best-practice.md' --- # Best Practice Elysia는 패턴에 구애받지 않는 프레임워크로, 어떤 코딩 패턴을 사용할지는 여러분과 팀에게 맡깁니다. 하지만 Elysia와 함께 [(Model-View-Controller)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) MVC 패턴을 적용하려 할 때 여러 가지 우려 사항이 있으며, 분리와 타입 처리가 어렵다는 것을 발견했습니다. 이 페이지는 MVC 패턴과 결합된 Elysia 구조 모범 사례를 따르는 방법에 대한 가이드이지만, 선호하는 모든 코딩 패턴에 적용할 수 있습니다. ## 폴더 구조 Elysia는 폴더 구조에 대한 의견이 없으므로 코드를 직접 구성하는 방법을 **결정**할 수 있습니다. 하지만 **특정 구조를 염두에 두지 않았다면**, 각 기능이 controller, service, model을 포함하는 자체 폴더를 가진 기능 기반 폴더 구조를 권장합니다. ``` | src | modules | auth | index.ts (Elysia controller) | service.ts (service) | model.ts (model) | user | index.ts (Elysia controller) | service.ts (service) | model.ts (model) | utils | a | index.ts | b | index.ts ``` 이 구조를 사용하면 코드를 쉽게 찾고 관리할 수 있으며 관련 코드를 함께 유지할 수 있습니다. 다음은 코드를 기능 기반 폴더 구조로 배포하는 방법의 예제 코드입니다: ::: code-group ```typescript [auth/index.ts] // Controller는 라우팅, 요청 검증과 같은 HTTP 관련 처리를 담당합니다 import { Elysia } from 'elysia' import { Auth } from './service' import { AuthModel } from './model' export const auth = new Elysia({ prefix: '/auth' }) .get( '/sign-in', async ({ body, cookie: { session } }) => { const response = await Auth.signIn(body) // 세션 쿠키 설정 session.value = response.token return response }, { body: AuthModel.signInBody, response: { 200: AuthModel.signInResponse, 400: AuthModel.signInInvalid } } ) ``` ```typescript [auth/service.ts] // Service는 Elysia controller에서 분리된 비즈니스 로직을 처리합니다 import { status } from 'elysia' import type { AuthModel } from './model' // 클래스가 속성을 저장할 필요가 없다면, // `abstract class`를 사용하여 클래스 할당을 피할 수 있습니다 export abstract class Auth { static async signIn({ username, password }: AuthModel.signInBody) { const user = await sql` SELECT password FROM users WHERE username = ${username} LIMIT 1` if (await Bun.password.verify(password, user.password)) // HTTP 에러를 직접 throw할 수 있습니다 throw status( 400, 'Invalid username or password' satisfies AuthModel.signInInvalid ) return { username, token: await generateAndSaveTokenToDB(user.id) } } } ``` ```typescript [auth/model.ts] // Model은 요청과 응답에 대한 데이터 구조와 검증을 정의합니다 import { t } from 'elysia' export namespace AuthModel { // Elysia 검증을 위한 DTO 정의 export const signInBody = t.Object({ username: t.String(), password: t.String(), }) // TypeScript 타입으로 정의 export type signInBody = typeof signInBody.static // 다른 모델도 반복 export const signInResponse = t.Object({ username: t.String(), token: t.String(), }) export type signInResponse = typeof signInResponse.static export const signInInvalid = t.Literal('Invalid username or password') export type signInInvalid = typeof signInInvalid.static } ``` ::: 각 파일은 다음과 같은 자체 책임을 가지고 있습니다: * **Controller**: HTTP 라우팅, 요청 검증 및 cookie를 처리합니다. * **Service**: 가능한 경우 Elysia controller에서 분리된 비즈니스 로직을 처리합니다. * **Model**: 요청과 응답에 대한 데이터 구조와 검증을 정의합니다. 필요에 맞게 이 구조를 자유롭게 조정하고 선호하는 코딩 패턴을 사용하세요. ## Controller > 1 Elysia instance = 1 controller Elysia는 타입 무결성을 보장하기 위해 많은 노력을 기울입니다. 전체 `Context` 타입을 controller에 전달하면 다음과 같은 문제가 발생할 수 있습니다: 1. Elysia 타입은 복잡하며 plugin과 여러 단계의 체이닝에 크게 의존합니다. 2. 타입 지정이 어렵고, Elysia 타입은 특히 decorator와 store에서 언제든지 변경될 수 있습니다. 3. 타입 캐스팅은 타입 무결성 손실이나 타입과 런타임 코드 간의 일관성을 보장할 수 없게 만들 수 있습니다. 4. 이로 인해 [Sucrose](/blog/elysia-10#sucrose) \*(Elysia의 "일종의" 컴파일러)\*가 코드를 정적으로 분석하기 더 어려워집니다 ### ❌ 하지 말 것: 별도의 controller 생성 별도의 controller를 생성하지 말고, 대신 Elysia 자체를 controller로 사용하세요: ```typescript import { Elysia, t, type Context } from 'elysia' abstract class Controller { static root(context: Context) { return Service.doStuff(context.stuff) } } // ❌ 하지 마세요 new Elysia() .get('/', Controller.hi) ``` 전체 `Controller.method`를 Elysia에 전달하는 것은 2개의 controller가 데이터를 주고받는 것과 같습니다. 이는 프레임워크 설계와 MVC 패턴 자체에 어긋납니다. ### ✅ 할 것: Elysia를 controller로 사용 대신 Elysia 인스턴스 자체를 controller로 취급하세요. ```typescript import { Elysia } from 'elysia' import { Service } from './service' new Elysia() .get('/', ({ stuff }) => { Service.doStuff(stuff) }) ``` 그렇지 않으면 정말로 controller를 분리하고 싶다면, HTTP 요청과 전혀 연결되지 않은 controller 클래스를 만들 수 있습니다. ```typescript import { Elysia } from 'elysia' abstract class Controller { static doStuff(stuff: string) { return Service.doStuff(stuff) } } new Elysia() .get('/', ({ stuff }) => Controller.doStuff(stuff)) ``` ### Testing `handle`을 사용하여 함수(및 해당 lifecycle)를 직접 호출하여 controller를 테스트할 수 있습니다 ```typescript import { Elysia } from 'elysia' import { Service } from './service' import { describe, it, expect } from 'bun:test' const app = new Elysia() .get('/', ({ stuff }) => { Service.doStuff(stuff) return 'ok' }) describe('Controller', () => { it('should work', async () => { const response = await app .handle(new Request('http://localhost/')) .then((x) => x.text()) expect(response).toBe('ok') }) }) ``` 테스트에 대한 자세한 내용은 [Unit Test](/patterns/unit-test.html)에서 확인할 수 있습니다. ## Service Service는 모듈/controller(우리의 경우 Elysia 인스턴스)에서 사용할 비즈니스 로직으로 분리된 유틸리티/헬퍼 함수 세트입니다. controller에서 분리할 수 있는 모든 기술 로직은 **Service** 내에 있을 수 있습니다. Elysia에는 2가지 유형의 service가 있습니다: 1. 요청 독립적 service 2. 요청 의존적 service ### ✅ 할 것: 요청 독립적 service 추상화 service 클래스/함수를 Elysia에서 추상화하는 것을 권장합니다. service 또는 함수가 HTTP 요청과 연결되어 있지 않거나 `Context`에 액세스하지 않는 경우 static 클래스 또는 함수로 구현하는 것이 좋습니다. ```typescript import { Elysia, t } from 'elysia' abstract class Service { static fibo(number: number): number { if(number < 2) return number return Service.fibo(number - 1) + Service.fibo(number - 2) } } new Elysia() .get('/fibo', ({ body }) => { return Service.fibo(body) }, { body: t.Numeric() }) ``` service가 속성을 저장할 필요가 없다면 클래스 인스턴스 할당을 피하기 위해 `abstract class`와 `static`을 사용할 수 있습니다. ### ✅ 할 것: 요청 의존적 service를 Elysia 인스턴스로 **service가 요청 의존적 service**이거나 HTTP 요청을 처리해야 하는 경우, 타입 무결성과 추론을 보장하기 위해 Elysia 인스턴스로 추상화하는 것을 권장합니다: ```typescript import { Elysia } from 'elysia' // ✅ 하세요 const AuthService = new Elysia({ name: 'Auth.Service' }) .macro({ isSignIn: { resolve({ cookie, status }) { if (!cookie.session.value) return status(401) return { session: cookie.session.value, } } } }) const UserController = new Elysia() .use(AuthService) .get('/profile', ({ Auth: { user } }) => user, { isSignIn: true }) ``` ::: tip Elysia는 기본적으로 [plugin deduplication](/essential/plugin.html#plugin-deduplication)을 처리하므로, **"name"** 속성을 지정하면 싱글톤이 되므로 성능에 대해 걱정할 필요가 없습니다. ::: ### ✅ 할 것: 요청 의존적 속성만 장식 `requestIP`, `requestTime` 또는 `session`과 같은 요청 의존적 속성만 `decorate`하는 것이 좋습니다. decorator를 과도하게 사용하면 코드가 Elysia에 묶여서 테스트와 재사용이 더 어려워질 수 있습니다. ```typescript import { Elysia } from 'elysia' new Elysia() .decorate('requestIP', ({ request }) => request.headers.get('x-forwarded-for') || request.ip) .decorate('requestTime', () => Date.now()) .decorate('session', ({ cookie }) => cookie.session.value) .get('/', ({ requestIP, requestTime, session }) => { return { requestIP, requestTime, session } }) ``` ### ❌ 하지 말 것: service에 전체 `Context` 전달 **Context는 매우 동적인 타입**이며 Elysia 인스턴스에서 추론할 수 있습니다. 전체 `Context`를 service에 전달하지 말고 객체 구조 분해를 사용하여 필요한 것을 추출하고 service에 전달하세요. ```typescript import type { Context } from 'elysia' class AuthService { constructor() {} // ❌ 이렇게 하지 마세요 isSignIn({ status, cookie: { session } }: Context) { if (session.value) return status(401) } } ``` Elysia 타입은 복잡하며 plugin과 여러 단계의 체이닝에 크게 의존하므로 매우 동적이어서 수동으로 타입을 지정하기 어려울 수 있습니다. ### ⚠️ Elysia 인스턴스에서 Context 추론 **절대적으로 필요한** 경우, Elysia 인스턴스 자체에서 `Context` 타입을 추론할 수 있습니다: ```typescript import { Elysia, type InferContext } from 'elysia' const setup = new Elysia() .state('a', 'a') .decorate('b', 'b') class AuthService { constructor() {} // ✅ 하세요 isSignIn({ status, cookie: { session } }: InferContext) { if (session.value) return status(401) } } ``` 하지만 가능하면 이를 피하고 [Elysia를 service로](#✅-할-것-요청-의존적-service를-elysia-인스턴스로) 사용하는 것을 권장합니다. [InferContext](/essential/handler#infercontext)에 대한 자세한 내용은 [Essential: Handler](/essential/handler)에서 확인할 수 있습니다. ## Model Model 또는 [DTO (Data Transfer Object)](https://en.wikipedia.org/wiki/Data_transfer_object)는 [Elysia.t (Validation)](/essential/validation.html#elysia-type)로 처리됩니다. Elysia는 코드에서 타입을 추론하고 런타임에 검증할 수 있는 내장 검증 시스템을 가지고 있습니다. ### ❌ 하지 말 것: 클래스 인스턴스를 model로 선언 클래스 인스턴스를 model로 선언하지 마세요: ```typescript // ❌ 하지 마세요 class CustomBody { username: string password: string constructor(username: string, password: string) { this.username = username this.password = password } } // ❌ 하지 마세요 interface ICustomBody { username: string password: string } ``` ### ✅ 할 것: Elysia의 검증 시스템 사용 클래스나 인터페이스를 선언하는 대신 Elysia의 검증 시스템을 사용하여 model을 정의하세요: ```typescript twoslash // ✅ 하세요 import { Elysia, t } from 'elysia' const customBody = t.Object({ username: t.String(), password: t.String() }) // 선택사항: model의 타입을 가져오려는 경우 // 이미 Elysia에서 추론되므로 일반적으로 타입을 사용하지 않는 경우 type CustomBody = typeof customBody.static // ^? export { customBody } ``` `typeof`를 model의 `.static` 속성과 함께 사용하여 model의 타입을 가져올 수 있습니다. 그런 다음 `CustomBody` 타입을 사용하여 요청 본문의 타입을 추론할 수 있습니다. ```typescript twoslash import { Elysia, t } from 'elysia' const customBody = t.Object({ username: t.String(), password: t.String() }) // ---cut--- // ✅ 하세요 new Elysia() .post('/login', ({ body }) => { // ^? return body }, { body: customBody }) ``` ### ❌ 하지 말 것: model과 별도로 타입 선언 model과 별도로 타입을 선언하지 말고, 대신 `typeof`를 `.static` 속성과 함께 사용하여 model의 타입을 가져오세요. ```typescript // ❌ 하지 마세요 import { Elysia, t } from 'elysia' const customBody = t.Object({ username: t.String(), password: t.String() }) type CustomBody = { username: string password: string } // ✅ 하세요 const customBody = t.Object({ username: t.String(), password: t.String() }) type CustomBody = typeof customBody.static ``` ### Group 여러 model을 단일 객체로 그룹화하여 더 잘 정리할 수 있습니다. ```typescript import { Elysia, t } from 'elysia' export const AuthModel = { sign: t.Object({ username: t.String(), password: t.String() }) } const models = AuthModel.models ``` ### Model 주입 선택사항이지만 MVC 패턴을 엄격하게 따르는 경우 service와 같이 model을 controller에 주입하고 싶을 수 있습니다. [Elysia reference model](/essential/validation#reference-model)을 사용하는 것을 권장합니다 Elysia의 model reference 사용 ```typescript twoslash import { Elysia, t } from 'elysia' const customBody = t.Object({ username: t.String(), password: t.String() }) const AuthModel = new Elysia() .model({ 'auth.sign': customBody }) const models = AuthModel.models const UserController = new Elysia({ prefix: '/auth' }) .use(AuthModel) .post('/sign-in', async ({ body, cookie: { session } }) => { // ^? return true }, { body: 'auth.sign' }) ``` 이 접근 방식은 여러 가지 이점을 제공합니다: 1. model에 이름을 지정하고 자동 완성을 제공할 수 있습니다. 2. 나중에 사용하기 위해 스키마를 수정하거나 [remap](/essential/handler.html#remap)을 수행할 수 있습니다. 3. OpenAPI 호환 클라이언트(예: OpenAPI)에서 "models"로 표시됩니다. 4. 등록 중에 model 타입이 캐시되므로 TypeScript 추론 속도가 향상됩니다. ## Plugin 재사용 타입 추론을 제공하기 위해 plugin을 여러 번 재사용해도 괜찮습니다. Elysia는 기본적으로 plugin 중복 제거를 자동으로 처리하며 성능 영향은 무시할 수 있습니다. 고유한 plugin을 만들려면 Elysia 인스턴스에 **name** 또는 선택적 **seed**를 제공할 수 있습니다. ```typescript import { Elysia } from 'elysia' const plugin = new Elysia({ name: 'my-plugin' }) .decorate("type", "plugin") const app = new Elysia() .use(plugin) .use(plugin) .use(plugin) .use(plugin) .listen(3000) ``` 이를 통해 Elysia는 plugin을 계속해서 처리하는 대신 등록된 plugin을 재사용하여 성능을 향상시킬 수 있습니다. --- --- url: 'https://elysiajs.docsforall.com/integrations/better-auth.md' --- # Better Auth Better Auth는 TypeScript를 위한 프레임워크 독립적인 인증(및 권한 부여) 프레임워크입니다. 즉시 사용 가능한 포괄적인 기능 세트를 제공하며 고급 기능 추가를 간소화하는 플러그인 생태계를 포함합니다. 이 페이지를 읽기 전에 [Better Auth 기본 설정](https://www.better-auth.com/docs/installation)을 먼저 살펴보는 것을 권장합니다. 기본 설정은 다음과 같습니다: ```ts [auth.ts] import { betterAuth } from 'better-auth' import { Pool } from 'pg' export const auth = betterAuth({ database: new Pool() }) ``` ## Handler Better Auth 인스턴스를 설정한 후 [mount](/patterns/mount.html)를 통해 Elysia에 마운트할 수 있습니다. 핸들러를 Elysia 엔드포인트에 마운트해야 합니다. ```ts [index.ts] import { Elysia } from 'elysia' import { auth } from './auth' const app = new Elysia() .mount(auth.handler) // [!code ++] .listen(3000) console.log( `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}` ) ``` 그러면 `http://localhost:3000/api/auth`로 Better Auth에 액세스할 수 있습니다. ### 사용자 정의 엔드포인트 [mount](/patterns/mount.html)를 사용할 때는 접두사 경로를 설정하는 것을 권장합니다. ```ts [index.ts] import { Elysia } from 'elysia' const app = new Elysia() .mount('/auth', auth.handler) // [!code ++] .listen(3000) console.log( `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}` ) ``` 그러면 `http://localhost:3000/auth/api/auth`로 Better Auth에 액세스할 수 있습니다. 하지만 URL이 중복되어 보이므로 Better Auth 인스턴스에서 `/api/auth` 접두사를 다른 것으로 사용자 정의할 수 있습니다. ```ts import { betterAuth } from 'better-auth' import { openAPI } from 'better-auth/plugins' import { passkey } from 'better-auth/plugins/passkey' import { Pool } from 'pg' export const auth = betterAuth({ basePath: '/api' // [!code ++] }) ``` 그러면 `http://localhost:3000/auth/api`로 Better Auth에 액세스할 수 있습니다. 안타깝게도 Better Auth 인스턴스의 `basePath`를 비워두거나 `/`로 설정할 수는 없습니다. ## OpenAPI Better Auth는 `better-auth/plugins`를 통해 `openapi`를 지원합니다. 그러나 [@elysiajs/openapi](/plugins/openapi)를 사용하는 경우 Better Auth 인스턴스에서 문서를 추출할 수 있습니다. 다음 코드로 추출할 수 있습니다: ```ts import { openAPI } from 'better-auth/plugins' let _schema: ReturnType const getSchema = async () => (_schema ??= auth.api.generateOpenAPISchema()) export const OpenAPI = { getPaths: (prefix = '/auth/api') => getSchema().then(({ paths }) => { const reference: typeof paths = Object.create(null) for (const path of Object.keys(paths)) { const key = prefix + path reference[key] = paths[path] for (const method of Object.keys(paths[path])) { const operation = (reference[key] as any)[method] operation.tags = ['Better Auth'] } } return reference }) as Promise, components: getSchema().then(({ components }) => components) as Promise } as const ``` 그런 다음 `@elysiajs/openapi`를 사용하는 Elysia 인스턴스에서: ```ts import { Elysia } from 'elysia' import { openapi } from '@elysiajs/openapi' import { OpenAPI } from './auth' const app = new Elysia().use( openapi({ documentation: { components: await OpenAPI.components, paths: await OpenAPI.getPaths() } }) ) ``` ## CORS cors를 구성하려면 `@elysiajs/cors`의 `cors` 플러그인을 사용할 수 있습니다. ```ts import { Elysia } from 'elysia' import { cors } from '@elysiajs/cors' import { auth } from './auth' const app = new Elysia() .use( cors({ origin: 'http://localhost:3001', methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], credentials: true, allowedHeaders: ['Content-Type', 'Authorization'] }) ) .mount(auth.handler) .listen(3000) console.log( `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}` ) ``` ## Macro [macro](https://elysiajs.com/patterns/macro.html#macro)를 [resolve](https://elysiajs.com/essential/handler.html#resolve)와 함께 사용하여 뷰에 전달하기 전에 세션 및 사용자 정보를 제공할 수 있습니다. ```ts import { Elysia } from 'elysia' import { auth } from './auth' // 사용자 미들웨어 (사용자와 세션을 계산하고 라우트에 전달) const betterAuth = new Elysia({ name: 'better-auth' }) .mount(auth.handler) .macro({ auth: { async resolve({ status, request: { headers } }) { const session = await auth.api.getSession({ headers }) if (!session) return status(401) return { user: session.user, session: session.session } } } }) const app = new Elysia() .use(betterAuth) .get('/user', ({ user }) => user, { auth: true }) .listen(3000) console.log( `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}` ) ``` 이렇게 하면 모든 라우트에서 `user` 및 `session` 객체에 액세스할 수 있습니다. --- --- url: 'https://elysiajs.docsforall.com/integrations/cloudflare-worker.md' --- # Cloudflare Worker 실험적 Elysia는 이제 **실험적** Cloudflare Worker 어댑터를 통해 Cloudflare Worker를 지원합니다. 1. 설정 및 개발 서버를 시작하려면 [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update)가 필요합니다. ```bash wrangler init elysia-on-cloudflare ``` 2. 그런 다음 Elysia 앱에 Cloudflare 어댑터를 추가하고 앱을 내보내기 전에 `.compile()`을 호출해야 합니다. ```ts import { Elysia } from 'elysia' import { CloudflareAdapter } from 'elysia/adapter/cloudflare-worker' // [!code ++] export default new Elysia({ adapter: CloudflareAdapter // [!code ++] }) .get('/', () => 'Hello Cloudflare Worker!') // Elysia를 Cloudflare Worker에서 작동시키려면 이것이 필요합니다 .compile() // [!code ++] ``` 3. wrangler 구성에서 `compatibility_date`를 최소 `2025-06-01`로 설정해야 합니다 ::: code-group ```jsonc [wrangler.jsonc] { "$schema": "node_modules/wrangler/config-schema.json", "name": "elysia-on-cloudflare", "main": "src/index.ts", "compatibility_date": "2025-06-01" // [!code ++] } ``` ```toml [wrangler.toml] main = "src/index.ts" name = "elysia-on-cloudflare" compatibility_date = "2025-06-01" # [!code ++] ``` ::: 4. 이제 다음 명령으로 개발 서버를 시작할 수 있습니다: ```bash wrangler dev ``` `http://localhost:8787`에서 개발 서버가 시작됩니다 Elysia는 Node.js 내장 모듈을 사용하지 않으므로(또는 우리가 사용하는 모듈이 Cloudflare Worker를 아직 지원하지 않음) `nodejs_compat` 플래그가 필요하지 않습니다. ## 제한 사항 Cloudflare Worker에서 Elysia를 사용할 때의 알려진 제한 사항은 다음과 같습니다: 1. `Elysia.file` 및 [Static Plugin](/plugins/static)은 [`fs` 모듈 부족](https://developers.cloudflare.com/workers/runtime-apis/nodejs/#supported-nodejs-apis)으로 인해 작동하지 않습니다 2. [OpenAPI Type Gen](/blog/openapi-type-gen)은 [`fs` 모듈 부족](https://developers.cloudflare.com/workers/runtime-apis/nodejs/#supported-nodejs-apis)으로 인해 작동하지 않습니다 3. [서버 시작 전 **Response** 정의](https://x.com/saltyAom/status/1966602691754553832)를 할 수 없으며, 그렇게 하는 플러그인을 사용할 수 없습니다 4. 값을 인라인으로 정의할 수 없습니다 ```typescript import { Elysia } from 'elysia' new Elysia() // 이렇게 하면 오류가 발생합니다 .get('/', 'Hello Elysia') .listen(3000) ``` ## 정적 파일 [Static Plugin](/plugins/static)은 작동하지 않지만 [Cloudflare의 내장 정적 파일 서빙](https://developers.cloudflare.com/workers/static-assets/)으로 정적 파일을 제공할 수 있습니다. wrangler 구성에 다음을 추가하세요: ::: code-group ```jsonc [wrangler.jsonc] { "$schema": "node_modules/wrangler/config-schema.json", "name": "elysia-on-cloudflare", "main": "src/index.ts", "compatibility_date": "2025-06-01", "assets": { "directory": "public" } // [!code ++] } ``` ```toml [wrangler.toml] name = "elysia-on-cloudflare" main = "src/index.ts" compatibility_date = "2025-06-01" assets = { directory = "public" } # [!code ++] ``` ::: `public` 폴더를 만들고 정적 파일을 넣으세요. 예를 들어, 다음과 같은 폴더 구조가 있는 경우: ``` │ ├─ public │ ├─ kyuukurarin.mp4 │ └─ static │ └─ mika.webp ├─ src │ └── index.ts └─ wrangler.toml ``` 다음 경로에서 정적 파일에 액세스할 수 있습니다: * **http://localhost:8787/kyuukurarin.mp4** * **http://localhost:8787/static/mika.webp** ## Binding `cloudflare:workers`에서 env를 가져와서 Cloudflare Workers 바인딩을 사용할 수 있습니다. ```ts import { Elysia } from 'elysia' import { CloudflareAdapter } from 'elysia/adapter/cloudflare-worker' import { env } from 'cloudflare:workers' // [!code ++] export default new Elysia({ adapter: CloudflareAdapter }) .get('/', () => `Hello ${await env.KV.get('my-key')}`) // [!code ++] .compile() ``` 바인딩에 대한 자세한 내용은 [Cloudflare Workers: Binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/#importing-env-as-a-global)을 참조하세요. ## AoT 컴파일 이전에는 Cloudflare Worker에서 Elysia를 사용하려면 Elysia 생성자에 `aot: false`를 전달해야 했습니다. [Cloudflare가 이제 시작 시 Function 컴파일을 지원](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#enable-eval-during-startup)하므로 더 이상 필요하지 않습니다. Elysia 1.4.7부터 Cloudflare Worker와 함께 Ahead of Time 컴파일을 사용할 수 있으며 `aot: false` 플래그를 삭제할 수 있습니다. ```ts import { Elysia } from 'elysia' import { CloudflareAdapter } from 'elysia/adapter/cloudflare-worker' // [!code ++] export default new Elysia({ aot: false, // [!code --] adapter: CloudflareAdapter // [!code ++] }) ``` 그렇지 않으면 Ahead of Time 컴파일을 원하지 않는 경우 여전히 `aot: false`를 사용할 수 있지만 더 나은 성능과 정확한 플러그인 캡슐화를 위해 사용하는 것을 권장합니다. --- --- url: 'https://elysiajs.docsforall.com/patterns/configuration.md' --- # Config Elysia는 설정 가능한 동작을 제공하여 다양한 기능을 사용자 정의할 수 있습니다. 생성자를 사용하여 설정을 정의할 수 있습니다. ```ts twoslash import { Elysia, t } from 'elysia' new Elysia({ prefix: '/v1', normalize: true }) ``` ## adapter ###### Since 1.1.11 다른 환경에서 Elysia를 사용하기 위한 런타임 adapter입니다. 환경에 따라 적절한 adapter가 기본값으로 설정됩니다. ```ts import { Elysia, t } from 'elysia' import { BunAdapter } from 'elysia/adapter/bun' new Elysia({ adapter: BunAdapter }) ``` ## aot ###### Since 0.4.0 Ahead of Time 컴파일입니다. Elysia는 [성능을 최적화](/blog/elysia-04.html#ahead-of-time-complie)할 수 있는 내장 JIT \_"컴파일러"\_를 가지고 있습니다. ```ts twoslash import { Elysia } from 'elysia' new Elysia({ aot: true }) ``` Ahead of Time 컴파일을 비활성화합니다 #### Options - @default `false` * `true` - 서버를 시작하기 전에 모든 라우트를 사전 컴파일합니다 * `false` - JIT를 완전히 비활성화합니다. 성능 비용 없이 더 빠른 시작 시간을 제공합니다 ## detail 인스턴스의 모든 라우트에 대한 OpenAPI 스키마를 정의합니다. 이 스키마는 인스턴스의 모든 라우트에 대한 OpenAPI 문서를 생성하는 데 사용됩니다. ```ts twoslash import { Elysia } from 'elysia' new Elysia({ detail: { hide: true, tags: ['elysia'] } }) ``` ## encodeSchema 응답을 클라이언트에 반환하기 전에 커스텀 `Encode`를 사용하여 커스텀 `t.Transform` 스키마를 처리합니다. 이를 통해 클라이언트에 응답을 보내기 전에 데이터에 대한 커스텀 인코딩 함수를 만들 수 있습니다. ```ts import { Elysia, t } from 'elysia' new Elysia({ encodeSchema: true }) ``` #### Options - @default `true` * `true` - 클라이언트에 응답을 보내기 전에 `Encode`를 실행합니다 * `false` - `Encode`를 완전히 건너뜁니다 ## name [Plugin Deduplication](/essential/plugin.html#plugin-deduplication)과 디버깅에 사용되는 인스턴스의 이름을 정의합니다 ```ts twoslash import { Elysia } from 'elysia' new Elysia({ name: 'service.thing' }) ``` ## nativeStaticResponse ###### Since 1.1.11 각 런타임에 대한 인라인 값을 처리하기 위해 최적화된 함수를 사용합니다. ```ts twoslash import { Elysia } from 'elysia' new Elysia({ nativeStaticResponse: true }) ``` #### Example Bun에서 활성화되면 Elysia는 인라인 값을 `Bun.serve.static`에 삽입하여 정적 값의 성능을 향상시킵니다. ```ts import { Elysia } from 'elysia' // 이렇게 new Elysia({ nativeStaticResponse: true }).get('/version', 1) // 다음과 동일합니다 Bun.serve({ static: { '/version': new Response(1) } }) ``` ## normalize ###### Since 1.1.0 Elysia가 필드를 지정된 스키마로 강제 변환할지 여부입니다. ```ts twoslash import { Elysia, t } from 'elysia' new Elysia({ normalize: true }) ``` 입력 및 출력에서 스키마에 지정되지 않은 알 수 없는 속성이 발견되면 Elysia는 해당 필드를 어떻게 처리해야 할까요? Options - @default `true` * `true`: Elysia는 [exact mirror](/blog/elysia-13.html#exact-mirror)를 사용하여 필드를 지정된 스키마로 강제 변환합니다 * `typebox`: Elysia는 [TypeBox의 Value.Clean](https://github.com/sinclairzx81/typebox)을 사용하여 필드를 지정된 스키마로 강제 변환합니다 * `false`: 요청 또는 응답에 각 핸들러의 스키마에서 명시적으로 허용되지 않은 필드가 포함되어 있으면 Elysia는 오류를 발생시킵니다. ## precompile ###### Since 1.0.0 Elysia가 서버를 시작하기 전에 [모든 라우트를 미리 컴파일](/blog/elysia-10.html#improved-startup-time)해야 하는지 여부입니다. ```ts twoslash import { Elysia } from 'elysia' new Elysia({ precompile: true }) ``` Options - @default `false` * `true`: 서버를 시작하기 전에 모든 라우트에서 JIT를 실행합니다 * `false`: 요청 시 동적으로 라우트를 컴파일합니다 `false`로 두는 것이 좋습니다. ## prefix 인스턴스의 모든 라우트에 대한 접두사를 정의합니다 ```ts twoslash import { Elysia, t } from 'elysia' new Elysia({ prefix: '/v1' }) ``` 접두사가 정의되면 모든 라우트에 지정된 값이 접두사로 추가됩니다. #### Example ```ts twoslash import { Elysia, t } from 'elysia' new Elysia({ prefix: '/v1' }).get('/name', 'elysia') // Path는 /v1/name입니다 ``` ## sanitize 검증 중 모든 `t.String`에서 호출하고 가로채는 함수 또는 함수 배열입니다. 문자열을 읽고 새 값으로 변환할 수 있습니다. ```ts import { Elysia, t } from 'elysia' new Elysia({ sanitize: (value) => Bun.escapeHTML(value) }) ``` ## seed [Plugin Deduplication](/essential/plugin.html#plugin-deduplication)에 사용되는 인스턴스의 체크섬을 생성하는 데 사용할 값을 정의합니다 ```ts twoslash import { Elysia } from 'elysia' new Elysia({ seed: { value: 'service.thing' } }) ``` 값은 문자열, 숫자 또는 객체에 국한되지 않고 어떤 유형이든 될 수 있습니다. ## strictPath Elysia가 경로를 엄격하게 처리해야 하는지 여부입니다. [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.3)에 따르면 경로는 라우트에 정의된 경로와 엄격하게 동일해야 합니다. ```ts twoslash import { Elysia, t } from 'elysia' new Elysia({ strictPath: true }) ``` #### Options - @default `false` * `true` - 경로 일치를 위해 [RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.3)을 엄격하게 따릅니다 * `false` - 접미사 '/' 또는 그 반대를 허용합니다. #### Example ```ts twoslash import { Elysia, t } from 'elysia' // 경로는 /name 또는 /name/일 수 있습니다 new Elysia({ strictPath: false }).get('/name', 'elysia') // 경로는 /name만 가능합니다 new Elysia({ strictPath: true }).get('/name', 'elysia') ``` ## serve HTTP 서버 동작을 사용자 정의합니다. Bun serve 설정입니다. ```ts import { Elysia } from 'elysia' new Elysia({ serve: { hostname: 'elysiajs.com', tls: { cert: Bun.file('cert.pem'), key: Bun.file('key.pem') } }, }) ``` 이 설정은 [Bun Serve API](https://bun.sh/docs/api/http) 및 [Bun TLS](https://bun.sh/docs/api/http#tls)를 확장합니다 ### Example: Max body size `serve` 설정에서 [`serve.maxRequestBodySize`](#serve-maxrequestbodysize)를 설정하여 최대 본문 크기를 설정할 수 있습니다. ```ts import { Elysia } from 'elysia' new Elysia({ serve: { maxRequestBodySize: 1024 * 1024 * 256 // 256MB } }) ``` 기본적으로 최대 요청 본문 크기는 128MB(1024 \* 1024 \* 128)입니다. 본문 크기 제한을 정의합니다. ```ts import { Elysia } from 'elysia' new Elysia({ serve: { // 최대 메시지 크기(바이트) maxPayloadLength: 64 * 1024, } }) ``` ### Example: HTTPS / TLS 키와 인증서에 대한 값을 전달하여 TLS(SSL의 후속)를 활성화할 수 있습니다. 둘 다 TLS를 활성화하는 데 필요합니다. ```ts import { Elysia, file } from 'elysia' new Elysia({ serve: { tls: { cert: file('cert.pem'), key: file('key.pem') } } }) ``` ### Example: Increase timeout `serve` 설정에서 [`serve.idleTimeout`](#serve-idletimeout)을 설정하여 유휴 타임아웃을 늘릴 수 있습니다. ```ts import { Elysia } from 'elysia' new Elysia({ serve: { // 유휴 타임아웃을 30초로 늘립니다 idleTimeout: 30 } }) ``` 기본적으로 유휴 타임아웃은 (Bun에서) 10초입니다. *** ## serve HTTP 서버 설정입니다. Elysia는 기본적으로 TLS를 지원하고 BoringSSL로 구동되는 Bun 설정을 확장합니다. 사용 가능한 설정에 대해서는 [serve.tls](#serve-tls)를 참조하세요. ### serve.hostname @default `0.0.0.0` 서버가 수신하는 호스트 이름을 설정합니다 ### serve.id ID로 서버 인스턴스를 고유하게 식별합니다 이 문자열은 보류 중인 요청이나 WebSocket을 중단하지 않고 서버를 핫 리로드하는 데 사용됩니다. 제공되지 않으면 값이 생성됩니다. 핫 리로딩을 비활성화하려면 이 값을 `null`로 설정하세요. ### serve.idleTimeout @default `10` (10초) 기본적으로 Bun은 유휴 타임아웃을 10초로 설정합니다. 즉, 요청이 10초 이내에 완료되지 않으면 중단됩니다. ### serve.maxRequestBodySize @default `1024 * 1024 * 128` (128MB) 요청 본문의 최대 크기를 설정합니다(바이트 단위) ### serve.port @default `3000` 수신할 포트입니다 ### serve.rejectUnauthorized @default `NODE_TLS_REJECT_UNAUTHORIZED` 환경 변수 `false`로 설정하면 모든 인증서가 허용됩니다. ### serve.reusePort @default `true` `SO_REUSEPORT` 플래그를 설정해야 하는지 여부입니다 이를 통해 여러 프로세스가 동일한 포트에 바인딩할 수 있으며, 이는 로드 밸런싱에 유용합니다 이 설정은 Elysia에 의해 재정의되고 기본적으로 켜져 있습니다 ### serve.unix 설정된 경우 HTTP 서버는 포트 대신 Unix 소켓에서 수신합니다. (hostname+port와 함께 사용할 수 없습니다) ### serve.tls 키와 인증서에 대한 값을 전달하여 TLS(SSL의 후속)를 활성화할 수 있습니다. 둘 다 TLS를 활성화하는 데 필요합니다. ```ts import { Elysia, file } from 'elysia' new Elysia({ serve: { tls: { cert: file('cert.pem'), key: file('key.pem') } } }) ``` Elysia는 기본적으로 TLS를 지원하고 BoringSSL로 구동되는 Bun 설정을 확장합니다. ### serve.tls.ca 선택적으로 신뢰할 수 있는 CA 인증서를 재정의합니다. 기본값은 Mozilla에서 선별한 잘 알려진 CA를 신뢰하는 것입니다. 이 옵션을 사용하여 CA를 명시적으로 지정하면 Mozilla의 CA가 완전히 교체됩니다. ### serve.tls.cert PEM 형식의 인증서 체인입니다. 제공된 개인 키당 하나의 인증서 체인을 제공해야 합니다. 각 인증서 체인은 제공된 개인 키에 대한 PEM 형식의 인증서와 PEM 형식의 중간 인증서(있는 경우)를 순서대로 구성해야 하며 루트 CA를 포함하지 않아야 합니다(루트 CA는 피어에게 미리 알려져 있어야 합니다. ca 참조). 여러 인증서 체인을 제공할 때 키에서 개인 키와 동일한 순서일 필요는 없습니다. 중간 인증서가 제공되지 않으면 피어가 인증서를 검증할 수 없으며 핸드셰이크가 실패합니다. ### serve.tls.dhParamsFile 사용자 정의 Diffie Helman 매개변수에 대한 .pem 파일의 파일 경로입니다 ### serve.tls.key PEM 형식의 개인 키입니다. PEM은 개인 키가 암호화되는 옵션을 허용합니다. 암호화된 키는 options.passphrase로 복호화됩니다. 다양한 알고리즘을 사용하는 여러 키는 암호화되지 않은 키 문자열 또는 버퍼의 배열 또는 형식의 객체 배열로 제공할 수 있습니다. 객체 형식은 배열에서만 발생할 수 있습니다. **object.passphrase**는 선택 사항입니다. 암호화된 키는 제공된 경우 **object.passphrase**로 복호화되거나 제공되지 않은 경우 **options.passphrase**로 복호화됩니다. ### serve.tls.lowMemoryMode @default `false` 이것은 `OPENSSL_RELEASE_BUFFERS`를 1로 설정합니다. 전체 성능은 감소하지만 일부 메모리를 절약합니다. ### serve.tls.passphrase 단일 개인 키 및/또는 PFX에 대한 공유 암호입니다. ### serve.tls.requestCert @default `false` `true`로 설정하면 서버가 클라이언트 인증서를 요청합니다. ### serve.tls.secureOptions 선택적으로 OpenSSL 프로토콜 동작에 영향을 줍니다. 일반적으로 필요하지 않습니다. 이것은 전혀 사용하지 않는 경우 신중하게 사용해야 합니다! 값은 OpenSSL Options의 SSL\_OP\_\* 옵션에 대한 숫자 비트마스크입니다 ### serve.tls.serverName 서버 이름을 명시적으로 설정합니다 ## tags [detail](#detail)과 유사하게 인스턴스의 모든 라우트에 대한 OpenAPI 스키마의 태그를 정의합니다 ```ts twoslash import { Elysia } from 'elysia' new Elysia({ tags: ['elysia'] }) ``` ### systemRouter 가능한 경우 런타임/프레임워크에서 제공하는 라우터를 사용합니다. Bun에서 Elysia는 [Bun.serve.routes](https://bun.sh/docs/api/http#routing)를 사용하고 Elysia의 자체 라우터로 폴백합니다. ## websocket WebSocket 설정을 재정의합니다 Elysia는 WebSocket을 자동으로 처리하기 위한 적절한 설정을 생성하므로 기본값으로 두는 것이 좋습니다 이 설정은 [Bun의 WebSocket API](https://bun.sh/docs/api/websockets)를 확장합니다 #### Example ```ts import { Elysia } from 'elysia' new Elysia({ websocket: { // 압축 및 압축 해제 활성화 perMessageDeflate: true } }) ``` *** --- --- url: 'https://elysiajs.docsforall.com/tutorial/patterns/cookie.md' --- # Cookie 컨텍스트의 cookie를 사용하여 쿠키와 상호작용할 수 있습니다. ```typescript import { Elysia } from 'elysia' new Elysia() .get('/', ({ cookie: { visit } }) => { const total = +visit.value ?? 0 visit.value++ return `You have visited ${visit.value} times` }) .listen(3000) ``` Cookie는 반응형 객체입니다. 수정되면 응답에 반영됩니다. ## Value 타입 어노테이션이 제공되면 Elysia는 해당 값으로 강제 변환을 시도합니다. ```typescript import { Elysia } from 'elysia' new Elysia() .get('/', ({ cookie: { visit } }) => { visit.value ??= 0 visit.value.total++ return `You have visited ${visit.value.total} times` }, { cookie: t.Object({ visit: t.Optional( t.Object({ total: t.Number() }) ) }) }) .listen(3000) ``` cookie schema를 사용하여 쿠키를 검증하고 파싱할 수 있습니다. ## Attribute 각 속성 이름을 통해 쿠키 속성을 가져오거나 설정할 수 있습니다. 그렇지 않으면 `.set()`을 사용하여 속성을 일괄 설정합니다. ```typescript import { Elysia } from 'elysia' new Elysia() .get('/', ({ cookie: { visit } }) => { visit.value ??= 0 visit.value++ visit.httpOnly = true visit.path = '/' visit.set({ sameSite: 'lax', secure: true, maxAge: 60 * 60 * 24 * 7 }) return `You have visited ${visit.value} times` }) .listen(3000) ``` Cookie Attribute를 참조하세요. ## Remove `.remove()` 메서드를 호출하여 쿠키를 제거할 수 있습니다. ```typescript import { Elysia } from 'elysia' new Elysia() .get('/', ({ cookie: { visit } }) => { visit.remove() return `Cookie removed` }) .listen(3000) ``` ## Cookie Signature Elysia는 다음을 통해 쿠키에 서명하여 변조를 방지할 수 있습니다: 1. Elysia 생성자에 쿠키 시크릿을 제공합니다. 2. 각 쿠키에 대한 시크릿을 제공하기 위해 `t.Cookie`를 사용합니다. ```typescript import { Elysia } from 'elysia' new Elysia({ cookie: { secret: 'Fischl von Luftschloss Narfidort', } }) .get('/', ({ cookie: { visit } }) => { visit.value ??= 0 visit.value++ return `You have visited ${visit.value} times` }, { cookie: t.Cookie({ visit: t.Optional(t.Number()) }, { secrets: 'Fischl von Luftschloss Narfidort', sign: ['visit'] }) }) .listen(3000) ``` 여러 시크릿이 제공되면 Elysia는 첫 번째 시크릿을 사용하여 쿠키에 서명하고 나머지로 검증을 시도합니다. Cookie Signature, Cookie Rotation을 참조하세요. ## 과제 사이트를 방문한 횟수를 추적하는 간단한 카운터를 만들어 봅시다. \