Axum Hello World
cargo build
•
async@awaits-MacBook-Air 260412_1712_axum_helloworld % cargo build
cargo run
•
async@awaits-MacBook-Air 260412_1712_axum_helloworld % cargo run
Explain
Overview
This project demonstrates the smallest useful Axum application:
•
One GET / route
•
Plain-text response body
•
Async request handling on the Tokio runtime
•
No shared application state, middleware, or database
Axum sits on top of Hyper and Tower. You define routes with a Router, attach handlers, bind a TCP listener, and serve requests with axum::serve.
Requirements
Tool | Version used | Notes |
Rust | 1.84+ | Edition 2021 |
Cargo | 1.84+ | Bundled with Rust |
OS | Any | macOS, Linux, and Windows |
No external services (database, Redis, etc.) are required.
Project Structure
260412_1712_axum_helloworld/
├── Cargo.toml # Crate metadata and dependencies
├── README.md # This document
└── src/
└── main.rs # Application entry point and route handlers
Plain Text
복사
Dependencies
Defined in Cargo.toml:
Crate | Version | Role |
axum | 0.8 | Web framework: routing, handlers, HTTP serving |
tokio | 1.x | Async runtime (full features: I/O, macros, multi-thread scheduler) |
Axum pulls in Hyper, Tower, and HTTP types transitively. You do not need to add them explicitly for this example.
How It Works
1. Route definition
let app = Router::new().route("/", get(hello_world));
Rust
복사
•
Router::new() creates an empty router.
•
.route("/", get(...)) registers GET / and maps it to the hello_world handler.
•
Handlers are async functions. Axum runs them on the Tokio runtime.
2. Handler
async fn hello_world() -> &'static str {
"Hello, World!"
}
Rust
복사
Returning &'static str tells Axum to send a 200 OK response with Content-Type: text/plain; charset=utf-8 and the string as the body. No manual response building is required for this simple case.
3. TCP listener
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
Rust
복사
The server listens on port 3000 on all network interfaces (0.0.0.0). Use 127.0.0.1:3000 if you only want local access.
4. HTTP server
axum::serve(listener, app).await?;
Rust
복사
axum::serve accepts connections on the listener and dispatches HTTP requests to the router. It supports HTTP/1.1 and HTTP/2 (when enabled via features).
Request flow
Client (browser/curl)
│
▼
TcpListener (port 3000)
│
▼
axum::serve
│
▼
Router ──► GET / ──► hello_world() ──► "Hello, World!"
Plain Text
복사
Build and Run
From the project root:
cd /Users/async/glory/Schedule/code/200518_0157_rs/axum/260412_1712_axum_helloworld
cargo run
Bash
복사
First run downloads and compiles dependencies; later runs are faster.
Expected console output:
Server listening on <http://localhost:3000>
Plain Text
복사
Press Ctrl+C to stop the server.
Release build (optional)
For optimized binaries:
cargo run --release
Bash
복사
Verify the Server
With the server running, in another terminal:
curl <http://localhost:3000>
Bash
복사
Expected response:
Hello, World!
Plain Text
복사
You can also open http://localhost:3000 in a browser; the same text should appear.
HTTP details
Item | Value |
Method | GET |
Path | / |
Status | 200 OK |
Body | Hello, World! |
Content-Type | text/plain; charset=utf-8 (inferred by Axum) |
Other paths (e.g. /foo) return 404 Not Found because no route is registered for them.
Configuration
Setting | Location | Default value |
Listen address | src/main.rs | 0.0.0.0:3000 |
Axum version | Cargo.toml | 0.8 |
Tokio features | Cargo.toml | full |
To change the port, edit the LISTEN_ADDR constant in src/main.rs and update the println! message if desired.
Common Commands
Command | Description |
cargo run | Build and run in debug mode |
cargo build | Compile without running |
cargo build --release | Optimized build |
cargo check | Type-check without full link |
cargo clean | Remove target/ build artifacts |
Troubleshooting
Address already in use
If port 3000 is taken:
Error: failed to bind TCP listener
Plain Text
복사
Fix: Stop the other process using port 3000, or change LISTEN_ADDR to another port (e.g. 0.0.0.0:8080).
On macOS/Linux, find the process:
lsof -i :3000
Bash
복사
Cannot connect from another machine
Binding to 127.0.0.1 only accepts local connections. This project uses 0.0.0.0, which accepts remote connections if your firewall allows it.
Compile errors after upgrading Axum
Axum 0.8 changed path parameter syntax from :name to {name}. This project has no path parameters, so upgrades are straightforward. See the Axum 0.8 announcement for breaking changes when extending the app.
Next Steps
Ideas for extending this server:
1.
More routes — Add GET /health for health checks.
2.
JSON responses — Return Json(...) with serde for structured APIs.
3.
Path parameters — Use /hello/{name} (Axum 0.8 syntax).
4.
Middleware — Logging, CORS, or compression via Tower layers.
5.
Shared state — Router::with_state for database pools or config.
6.
Tests — axum::Router can be tested with tower::ServiceExt without binding a real port.
References
License
This example is provided as-is for learning purposes. Add a license file if you plan to distribute or publish the crate.
axum/260412_1712_axum_helloworld/src/main.rs
use axum::{routing::get, Router};
/*
use 키워드: 외부 creates (axum)에 정의된 기능을 현재 소스코드 스코프로 가져와 긴 경로 생략 가능하게 함
중괄호 { ... } (Use Group): 동일한 루트 경로(axum)에서 여러 아이템(routing::get, Router)을 한 줄로 묶어 효율적으로 수입(Import)하는 문법
Router: Axum 프레임워크의 핵심 타입으로, 특정 HTTP 경로(URL)와 이를 처리할 핸들러 함수를 매핑해주는 웹 서버의 이정표 역할
routing::get: HTTP 메서드 중 조회 목적의 GET 요청만을 필터링하여 특정 핸들러에 연결해주는 라우팅 매칭 헬퍼 함수
*/
const LISTEN_ADDR: &str = "0.0.0.0:3001";
/*
const 키워드: 컴파일 시점에 값이 결정되는 불변의 '상수'를 정의 (프로그램 실행 중 절대 변경 불가)
LISTEN_ADDR: 상수의 이름으로, 관례에 따라 전체 대문자와 언더바(_) 스네이크 케이스로 명명
: &str: 상수의 타입을 명시한 것으로, 메모리에 고정된 문자열 데이터의 주소와 길이를 가리키는 고정 크기 문자열 슬라이스 타입 (Rust에서 const 정의 시 타입 명시는 필수)
= "0.0.0.0:3001";: 웹 서버가 요청을 기다릴(Listen) 주소와 포트를 지정
0.0.0.0: 특정 IP가 아닌, 서버 컴퓨터에 할당된 모든 네트워크 인터페이스(랜카드)로부터의 접속을 전부 허용하겠다는 의미
3001: 외부 클라이언트가 이 웹 서버에 접속하기 위해 통과해야 하는 문 번호(포트 번호)
*/
async fn hello_world() -> &'static str {
"Hello, World!"
}
/*
hello_world: 루트 경로 `/`에 대한 HTTP 요청을 처리하는 핸들러(요청 처리 함수)
async fn: 비동기 함수를 선언하는 키워드 — 함수 내부에서 I/O 대기 등이 발생해도 스레드를 블로킹하지 않고 다른 작업에 양보할 수 있음
호출 측에서는 이 함수의 결과를 사용할 때 반드시 `.await`로 완료를 기다려야 함
Axum은 Tokio 비동기 런타임 위에서 동작하므로, 핸들러를 async fn으로 작성하는 것이 일반적
-> &'static str: 함수의 반환 타입 — 프로그램 전체 수명(static) 동안 유효한 문자열 슬라이스의 참조
Axum은 이 반환값을 HTTP 200 OK 응답으로 자동 변환하며, Content-Type은 text/plain으로 설정됨
단순 텍스트 응답의 경우 IntoResponse 트레이트 덕분에 Response 타입을 직접 만들 필요 없음
"Hello, World!": 함수 본문의 마지막 표현식 — 세미콜론(;)이 없으므로 이 값이 함수의 반환값이 됨
*/
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(hello_world));
let listener = tokio::net::TcpListener::bind(LISTEN_ADDR)
.await
.expect("failed to bind TCP listener");
println!("Server listening on http://localhost:3001");
axum::serve(listener, app).await.expect("server error");
}
/*
#[tokio::main]: 절차적 매크로(Procedural Macro) 속성 — async fn main()을 일반 fn main()으로 자동 변환해 줌
변환된 main은 (1) Tokio 멀티스레드 런타임을 생성하고 (2) async 본문을 그 런타임에서 실행한 뒤 완료될 때까지 대기함
이 속성이 없으면 Rust 표준 main은 async를 직접 사용할 수 없음
Router::new(): 비어 있는 라우터를 생성 — 아직 등록된 경로(route)가 없는 상태
.route("/", get(hello_world)): 메서드 체이닝으로 경로를 등록
"/": URL 루트 경로와 매칭 (예: http://localhost:3001/)
get(hello_world): HTTP GET 메서드 요청이 들어오면 hello_world 핸들러를 호출하도록 연결
Axum은 핸들러의 인자 타입과 반환 타입을 컴파일 시점에 추론하여 타입 안전성을 보장함
let app: 위에서 구성한 라우터를 app 변수에 담아 이후 serve에 전달
tokio::net::TcpListener::bind(LISTEN_ADDR): 지정한 주소(LISTEN_ADDR)에 TCP 소켓을 바인딩하여 연결 대기 상태로 만듦
비동기 함수이므로 .await로 OS의 바인딩 작업이 끝날 때까지 현재 태스크를 일시 정지(suspend)함
반환 타입은 Result<TcpListener, Error> — 성공 시 Ok(listener), 실패 시 Err(예: 포트 이미 사용 중)
.expect("failed to bind TCP listener"): Err이면 에러 메시지와 함께 패닉(panic) — 학습용 데모에서는 단순 처리로 충분
println!(...): 서버가 정상적으로 바인딩되었음을 콘솔에 출력 (개발자 확인용)
axum::serve(listener, app): listener로 들어오는 TCP 연결을 받아 HTTP 요청으로 파싱하고, app 라우터에 맞는 핸들러로 분배(dispatch)
.await: 서버가 종료 신호(예: Ctrl+C)를 받거나 치명적 오류가 발생할 때까지 이 지점에서 대기
.expect("server error"): serve 중 치명적 오류 발생 시 패닉 — 데모 앱에서의 단순 에러 처리
*/
Rust
복사
Cargo.toml
[package]
name = "axum_helloworld"
version = "0.1.0"
edition = "2021"
description = "Minimal Axum Hello World web server"
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
Rust
복사
안녕하세요
•
관련 기술 문의와 R&D 공동 연구 사업 관련 문의는 “glory@keti.re.kr”로 연락 부탁드립니다.
Hello 
•
For technical and business inquiries, please contact me at “glory@keti.re.kr”
