본문으로 건너뛰기
AI 도우미
🤖
안녕하세요! 프로그래밍 질문, 코드 설명, 블로그 글 검색 등을 도와드릴 수 있어요. 무엇이 궁금하신가요?
Enter를 누르거나 버튼을 클릭하여 전송 • Shift+Enter로 줄바꿈
Previous
Next
Hono 완전 가이드 | Edge에 최적화된 초고속 웹 프레임워크

Hono 완전 가이드 | Edge에 최적화된 초고속 웹 프레임워크

Hono 완전 가이드 | Edge에 최적화된 초고속 웹 프레임워크

이 글의 핵심

Express를 대체하는 차세대 Edge 웹 프레임워크 Hono. Cloudflare Workers·Vercel·Deno·Bun 등 모든 런타임에서 작동하며, Express보다 10배 빠르고 TypeScript 타입 안전성을 제공합니다.

이 글의 핵심

Hono는 Express보다 10배 빠른 Edge 웹 프레임워크입니다. Cloudflare Workers·Vercel·Deno·Bun 등 모든 런타임에서 작동하며, TypeScript 타입 안전성미들웨어 시스템으로 생산성을 극대화합니다. 번들 크기는 12KB에 불과합니다.

목차

Hono란?

Hono(炎, 일본어로 “불꽃”)는 2022년 Yusuke Wada가 개발한 Edge 우선 웹 프레임워크입니다.

🚀 핵심 특징

1. 모든 런타임 지원

// 같은 코드가 모든 런타임에서 작동
import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => c.text('Hello!'));

// Cloudflare Workers
export default app;

// Deno
Deno.serve(app.fetch);

// Bun
export default { port: 3000, fetch: app.fetch };

// Node.js
serve(app);

2. Express보다 10배 빠름

벤치마크 (초당 요청):
Express: 25,000 req/s
Fastify: 65,000 req/s
Hono: 250,000 req/s

3. TypeScript 타입 안전성

// 라우트와 응답 타입이 자동 추론됨
app.get('/user/:id', (c) => {
  const id = c.req.param('id'); // string으로 추론
  return c.json({ id, name: 'Alice' });
});

4. 가벼움

Express: 210KB
Fastify: 450KB
Hono: 12KB

Hono 시작하기

Cloudflare Workers

# 프로젝트 생성
npm create hono@latest my-app

# 템플릿 선택: cloudflare-workers
cd my-app
npm install
npm run dev
// src/index.ts
import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => {
  return c.text('Hello Hono!');
});

export default app;

Node.js

npm install hono @hono/node-server
// index.ts
import { Hono } from 'hono';
import { serve } from '@hono/node-server';

const app = new Hono();

app.get('/', (c) => c.text('Hello from Node.js!'));

serve({
  fetch: app.fetch,
  port: 3000,
});

console.log('Server running at http://localhost:3000');

Bun

bun install hono
// index.ts
import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => c.text('Hello from Bun!'));

export default {
  port: 3000,
  fetch: app.fetch,
};

라우팅

기본 라우트

import { Hono } from 'hono';

const app = new Hono();

// GET
app.get('/hello', (c) => c.text('GET /hello'));

// POST
app.post('/submit', (c) => c.text('POST /submit'));

// PUT
app.put('/update', (c) => c.text('PUT /update'));

// DELETE
app.delete('/remove', (c) => c.text('DELETE /remove'));

// 여러 메서드
app.on(['GET', 'POST'], '/multi', (c) => {
  return c.text(`Method: ${c.req.method}`);
});

// 모든 메서드
app.all('/any', (c) => c.text('Any method'));

export default app;

경로 매개변수

// /user/:id
app.get('/user/:id', (c) => {
  const id = c.req.param('id');
  return c.json({ userId: id });
});

// /post/:id/comment/:commentId
app.get('/post/:id/comment/:commentId', (c) => {
  const { id, commentId } = c.req.param();
  return c.json({ postId: id, commentId });
});

// 선택적 매개변수
app.get('/user/:id?', (c) => {
  const id = c.req.param('id') || 'default';
  return c.text(`User: ${id}`);
});

// 와일드카드
app.get('/files/*', (c) => {
  return c.text(`Path: ${c.req.path}`);
});

쿼리 파라미터

// /search?q=hello&page=2
app.get('/search', (c) => {
  const q = c.req.query('q'); // 'hello'
  const page = c.req.query('page'); // '2'
  
  // 또는 한 번에
  const { q, page } = c.req.query();
  
  return c.json({ query: q, page });
});

요청/응답

JSON 요청 처리

app.post('/user', async (c) => {
  const body = await c.req.json();
  
  return c.json({
    message: 'User created',
    user: body,
  }, 201);
});

// FormData
app.post('/upload', async (c) => {
  const formData = await c.req.formData();
  const file = formData.get('file');
  
  return c.json({ filename: file?.name });
});

// Text
app.post('/text', async (c) => {
  const text = await c.req.text();
  return c.text(`Received: ${text}`);
});

응답 타입

// JSON
app.get('/json', (c) => c.json({ message: 'Hello' }));

// Text
app.get('/text', (c) => c.text('Hello'));

// HTML
app.get('/html', (c) => c.html('<h1>Hello</h1>'));

// Redirect
app.get('/redirect', (c) => c.redirect('/new-path'));

// 커스텀 응답
app.get('/custom', (c) => {
  return new Response('Custom', {
    status: 200,
    headers: { 'X-Custom': 'Header' },
  });
});

미들웨어

내장 미들웨어

import { Hono } from 'hono';
import { logger } from 'hono/logger';
import { cors } from 'hono/cors';
import { jwt } from 'hono/jwt';
import { compress } from 'hono/compress';

const app = new Hono();

// 로깅
app.use('*', logger());

// CORS
app.use('*', cors({
  origin: 'https://example.com',
  credentials: true,
}));

// 압축
app.use('*', compress());

// JWT 인증
app.use('/api/*', jwt({
  secret: 'my-secret-key',
}));

app.get('/api/protected', (c) => {
  const payload = c.get('jwtPayload');
  return c.json({ user: payload });
});

export default app;

커스텀 미들웨어

// 실행 시간 측정
const timing = () => {
  return async (c, next) => {
    const start = Date.now();
    await next();
    const ms = Date.now() - start;
    c.res.headers.set('X-Response-Time', `${ms}ms`);
  };
};

app.use('*', timing());

// 인증 미들웨어
const auth = () => {
  return async (c, next) => {
    const token = c.req.header('Authorization');
    
    if (!token) {
      return c.json({ error: 'Unauthorized' }, 401);
    }
    
    // 토큰 검증...
    c.set('userId', 123);
    await next();
  };
};

app.use('/api/*', auth());

실전 프로젝트: REST API

// src/index.ts
import { Hono } from 'hono';
import { z } from 'zod';
import { zValidator } from '@hono/zod-validator';

const app = new Hono();

// In-memory DB (실제로는 D1 사용)
const users = [
  { id: 1, name: 'Alice', email: '[email protected]' },
  { id: 2, name: 'Bob', email: '[email protected]' },
];

// GET /users
app.get('/users', (c) => {
  return c.json(users);
});

// GET /users/:id
app.get('/users/:id', (c) => {
  const id = parseInt(c.req.param('id'));
  const user = users.find(u => u.id === id);
  
  if (!user) {
    return c.json({ error: 'User not found' }, 404);
  }
  
  return c.json(user);
});

// POST /users (Zod 검증)
const createUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

app.post('/users', zValidator('json', createUserSchema), (c) => {
  const body = c.req.valid('json');
  const newUser = {
    id: users.length + 1,
    ...body,
  };
  users.push(newUser);
  
  return c.json(newUser, 201);
});

// PATCH /users/:id
app.patch('/users/:id', async (c) => {
  const id = parseInt(c.req.param('id'));
  const body = await c.req.json();
  const user = users.find(u => u.id === id);
  
  if (!user) {
    return c.json({ error: 'User not found' }, 404);
  }
  
  Object.assign(user, body);
  return c.json(user);
});

// DELETE /users/:id
app.delete('/users/:id', (c) => {
  const id = parseInt(c.req.param('id'));
  const index = users.findIndex(u => u.id === id);
  
  if (index === -1) {
    return c.json({ error: 'User not found' }, 404);
  }
  
  users.splice(index, 1);
  return c.json({ success: true });
});

export default app;

중첩 라우팅

// users.ts
import { Hono } from 'hono';

const usersApp = new Hono();

usersApp.get('/', (c) => c.json({ users: [] }));
usersApp.get('/:id', (c) => c.json({ id: c.req.param('id') }));
usersApp.post('/', (c) => c.json({ created: true }));

// posts.ts
const postsApp = new Hono();

postsApp.get('/', (c) => c.json({ posts: [] }));
postsApp.get('/:id', (c) => c.json({ id: c.req.param('id') }));

// index.ts
const app = new Hono();

app.route('/users', usersApp);
app.route('/posts', postsApp);

export default app;

Hono vs Express vs Fastify

기능HonoExpressFastify
속도⚡⚡⚡ 초고속⚡ 보통⚡⚡ 빠름
Edge 지원✅ 완벽❌ 불가❌ 불가
TypeScript✅ 기본⚠️ 수동✅ 기본
번들 크기12KB210KB450KB
미들웨어
생태계🌱 새로움🌳 성숙🌿 성장 중

핵심 정리

Hono의 장점

  1. 압도적인 속도: Express보다 10배 빠름
  2. Edge 최적화: Cloudflare Workers·Vercel Edge
  3. 멀티 런타임: Deno·Bun·Node.js 모두 지원
  4. TypeScript 우선: 완벽한 타입 안전성
  5. 가벼움: 12KB 번들 크기

🚀 다음 단계


시작하기: npm create hono@latest로 5분 만에 프로젝트를 시작하고, Express보다 10배 빠른 API 서버를 만드세요! 🚀