con un clic
tower-http
tower-http 미들웨어 라이브러리 (CorsLayer, TraceLayer, CompressionLayer) 및 Axum 연동
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
tower-http 미들웨어 라이브러리 (CorsLayer, TraceLayer, CompressionLayer) 및 Axum 연동
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
Spring Security 5.5.x + jjwt 0.10.7 레거시 JWT 인증 - WebSecurityConfigurerAdapter, OncePerRequestFilter, javax.servlet 환경
Spring Boot 3.x + Spring Security 6.x + jjwt 0.12.x 기반 모던 JWT 인증 패턴. SecurityFilterChain Bean, 람다 DSL, jakarta.servlet, Virtual Threads 적용
Unity 6 LTS 2D 모바일 게임용 uGUI 시스템 전문 스킬. Canvas/RectTransform/TextMeshPro, 모바일 UI 패턴(팝업·무한 스크롤·광고·IAP), 성능 최적화, UI Toolkit과의 선택 기준 포함.
아크라시아(akrasia, 자제력 없음) 학술 논쟁의 핵심 구도와 주요 연구자·문헌을 빠르게 파악할 수 있는 도메인 지식 스킬. 도덕윤리교육 전공 대학원생(석/박사)이 학위논문·KCI 투고·세미나 준비 시 고대–현대–한국 학계–도덕심리학 흐름을 한 번에 짚도록 구성. <example>사용자: "아리스토텔레스의 propeteia와 astheneia 구분을 인용하려는데 출처를 알려줘"</example> <example>사용자: "데이비슨이 의지박약을 어떻게 가능하다고 봤는지 핵심 논증을 정리해줘"</example> <example>사용자: "한국 도덕교육 학계에서 아크라시아 다룬 논문 있어?"</example>
아리스토텔레스 『니코마코스 윤리학』에서 akrasia(자제력없음)와 akolasia(무절제)의 5축 차이를 정밀하게 정리한 학위논문 자료 스킬. NE VII.4 1147b20-1148b14, VII.8 1150b29-1151a28, III.10-12 1117b23-1119b18 절별 분해와 표준 학자 해석(Bostock, Broadie-Rowe, Pakaluk, Hursthouse, Charles 등)을 포함. 도덕교육 적용을 위한 두 상태 차이의 함의 및 한국어 번역어 처리 권장안 제공. <example>사용자: "akrates와 akolastos를 prohairesis 측면에서 어떻게 구분해야 하나요?"</example> <example>사용자: "NE VII.4의 ἁπλῶς akrasia가 akolasia와 어떻게 갈라지는지 절별 분해해주세요"</example> <example>사용자: "Hursthouse의 연속체 모델을 도덕교육 적용 절에서 어떻게 활용할 수 있나요?"</example>
한국 위기 대응 자원(자살·자해·정신건강·여성·청소년·노인·다문화) 핫라인과 앱·챗봇 안전 가드 응답 패턴 종합. 꿈 해몽·정신건강 앱 등 자가 진단/감정 콘텐츠 도메인에서 위험 신호 포착 시 안전한 자원 안내 문구를 작성할 때 참조. <example>사용자: "꿈 해몽 앱에 위기 안내 문구를 어떻게 넣을까?"</example> <example>사용자: "한국에서 자살예방 핫라인 번호가 어떻게 바뀌었지?"</example> <example>사용자: "정신건강 챗봇 안전 가드 응답 템플릿을 짜줘"</example>
| name | tower-http |
| description | tower-http 미들웨어 라이브러리 (CorsLayer, TraceLayer, CompressionLayer) 및 Axum 연동 |
소스: https://docs.rs/tower-http | https://github.com/tower-rs/tower-http 검증일: 2026-06-20
주의: 이 문서는 tower-http 0.6.x 기준으로 작성되었습니다. 0.7.0이 2026-05-18 릴리즈되어 Breaking Change가 있으므로 신규 프로젝트는 마이그레이션 노트를 참조하세요. 프로젝트의 Cargo.lock에서 실제 사용 버전을 반드시 확인할 것.
tower-http 0.7로의 마이그레이션 시 주요 Breaking Change (0.6 → 0.7):
- tokio, async-compression 암묵적 no-op feature 제거 (0.6.x 하위 호환용 dummy feature 삭제)
FollowRedirect: Extensions가 리다이렉트 요청으로 전달됨 (이전에는 drop). 기존 동작 유지 시preserve_extensions(false)명시 필요DefaultOnRequest,DefaultOnResponse,DefaultOnFailure,DefaultOnEos: 이제 명시적으로 request span에 parent span 설정됨GrpcCode,GrpcFailureClass가#[non_exhaustive]로 변경
tower-http는 feature flag 기반으로 필요한 미들웨어만 선택적으로 활성화한다.
# tower-http 0.6.x (현재 문서 기준) — 0.7.0이 2026-05-18 릴리즈됨, 마이그레이션 주의사항 참조
[dependencies]
tower-http = { version = "0.6", features = ["cors", "trace", "compression-full", "timeout", "limit"] }
주요 feature flag:
| Feature | 포함 미들웨어 |
|---|---|
cors | CorsLayer |
trace | TraceLayer |
compression-full | CompressionLayer (gzip, br, deflate, zstd 전부) |
compression-gzip | gzip만 |
compression-br | brotli만 |
fs | ServeDir, ServeFile |
timeout | TimeoutLayer |
limit | RequestBodyLimitLayer |
full | 모든 미들웨어 |
Cross-Origin Resource Sharing 헤더를 응답에 추가한다.
use tower_http::cors::{CorsLayer, Any};
use http::Method;
// 모든 origin 허용 (개발용) — credentials는 허용하지 않음
// credentials까지 허용하려면 CorsLayer::very_permissive() 사용
let cors = CorsLayer::permissive();
// 프로덕션 권장: 명시적 설정
let cors = CorsLayer::new()
.allow_origin(["https://example.com".parse().unwrap()])
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
.allow_headers(Any)
.max_age(Duration::from_secs(3600));
| 메서드 | 설명 | 타입 |
|---|---|---|
allow_origin() | 허용할 origin | impl Into<AllowOrigin> |
allow_methods() | 허용할 HTTP 메서드 | impl Into<AllowMethods> |
allow_headers() | 허용할 요청 헤더 | impl Into<AllowHeaders> |
expose_headers() | 브라우저에 노출할 응답 헤더 | impl Into<ExposeHeaders> |
max_age() | preflight 캐시 시간 | Duration |
allow_credentials() | 쿠키/인증 포함 여부 | bool |
use tower_http::cors::{AllowOrigin, Any};
// 모든 origin
.allow_origin(Any)
// 단일 origin
.allow_origin(["https://example.com".parse().unwrap()])
// 여러 origin
.allow_origin([
"https://example.com".parse().unwrap(),
"https://app.example.com".parse().unwrap(),
])
// 동적 판단 (클로저)
.allow_origin(AllowOrigin::predicate(|origin, _req_parts| {
origin.as_bytes().ends_with(b".example.com")
}))
주의:
allow_credentials(true)와allow_origin(Any)는 동시에 사용할 수 없다. CORS 스펙상 credentials 모드에서는 와일드카드 origin이 금지된다.
요청/응답 로깅을 위한 미들웨어. tracing 크레이트와 연동된다.
use tower_http::trace::TraceLayer;
use tracing::Level;
// 기본 사용 (HTTP 레벨 로깅)
let trace = TraceLayer::new_for_http();
use tower_http::trace::{TraceLayer, DefaultOnRequest, DefaultOnResponse};
use tracing::Level;
let trace = TraceLayer::new_for_http()
.on_request(DefaultOnRequest::new().level(Level::INFO))
.on_response(
DefaultOnResponse::new()
.level(Level::INFO)
.latency_unit(tower_http::LatencyUnit::Micros),
);
use tower_http::trace::TraceLayer;
use tracing::Span;
use http::Request;
let trace = TraceLayer::new_for_http()
.make_span_with(|request: &Request<_>| {
tracing::info_span!(
"http_request",
method = %request.method(),
uri = %request.uri(),
)
})
.on_request(|_request: &Request<_>, _span: &Span| {
tracing::info!("request started");
})
.on_response(|response: &http::Response<_>, latency: std::time::Duration, _span: &Span| {
tracing::info!(status = %response.status(), latency = ?latency, "response sent");
});
TraceLayer는 tracing 크레이트에 의존한다. subscriber를 초기화해야 로그가 출력된다.
// Cargo.toml
// tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing_subscriber::fmt()
.with_env_filter("tower_http=debug,info")
.init();
응답 본문을 자동으로 압축한다. Accept-Encoding 헤더를 기반으로 알고리즘을 선택한다.
use tower_http::compression::CompressionLayer;
// 모든 지원 알고리즘 활성화
let compression = CompressionLayer::new();
// 특정 알고리즘만
let compression = CompressionLayer::new()
.gzip(true)
.br(true)
.deflate(false)
.zstd(false);
클라이언트 Accept-Encoding의 quality 값(q=)이 높은 알고리즘을 우선 선택한다. quality 값이 동일할 때는 서버 내부 순서(deflate < gzip < brotli < zstd)가 타이브레이커로 작동한다.
주의:
Accept-Encoding: gzip;q=1.0, br;q=0.9처럼 클라이언트가 명시한 경우 quality 값이 높은 gzip이 선택된다. "zstd > brotli > gzip > deflate" 우선순위는 quality 값이 동일한 경우에만 적용된다.
CompressionLayer는 Content-Type을 보고 자동 스킵하지 않으므로 필요 시 조건부 적용을 고려한다.Axum은 tower의 Layer 트레이트를 기반으로 미들웨어를 적용한다. Router::layer() 메서드로 연결한다.
use axum::{Router, routing::get};
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
use tower_http::compression::CompressionLayer;
async fn handler() -> &'static str {
"Hello, World!"
}
let app = Router::new()
.route("/", get(handler))
.layer(CompressionLayer::new())
.layer(CorsLayer::permissive())
.layer(TraceLayer::new_for_http());
주의:
.layer()호출 순서와 실제 미들웨어 실행 순서는 역순이다. 마지막에 추가한 레이어가 가장 먼저 실행된다.
위 코드의 실행 순서:
요청 → TraceLayer → CorsLayer → CompressionLayer → handler
응답 → CompressionLayer → CorsLayer → TraceLayer
따라서 TraceLayer를 가장 바깥(마지막 .layer())에 배치해야 전체 요청/응답 사이클을 추적할 수 있다.
let app = Router::new()
.route("/api/users", get(list_users).post(create_user))
.route("/api/health", get(health_check))
.layer(CompressionLayer::new())
.layer(CorsLayer::permissive())
.layer(TraceLayer::new_for_http());
ServiceBuilder를 사용하면 선언 순서대로(위에서 아래로) 실행된다. .layer() 체이닝의 역순 혼란을 피할 수 있다.
use tower::ServiceBuilder;
let app = Router::new()
.route("/", get(handler))
.layer(
ServiceBuilder::new()
.layer(TraceLayer::new_for_http()) // 1번째 실행
.layer(CorsLayer::permissive()) // 2번째 실행
.layer(CompressionLayer::new()), // 3번째 실행
);
let api_routes = Router::new()
.route("/users", get(list_users))
.route("/posts", get(list_posts))
.layer(CorsLayer::permissive()); // API 라우트에만 CORS 적용
let admin_routes = Router::new()
.route("/dashboard", get(dashboard));
// CORS 없음
let app = Router::new()
.nest("/api", api_routes)
.nest("/admin", admin_routes)
.layer(TraceLayer::new_for_http()); // 모든 라우트에 트레이싱 적용
use axum::{Router, routing::get};
use tower_http::{
cors::{CorsLayer, Any},
trace::{TraceLayer, DefaultOnResponse},
compression::CompressionLayer,
timeout::TimeoutLayer,
limit::RequestBodyLimitLayer,
};
use http::Method;
use std::time::Duration;
use tracing::Level;
pub fn create_app() -> Router {
let cors = CorsLayer::new()
.allow_origin(["https://myapp.com".parse().unwrap()])
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
.allow_headers(Any)
.max_age(Duration::from_secs(3600));
let trace = TraceLayer::new_for_http()
.on_response(DefaultOnResponse::new().level(Level::INFO));
Router::new()
.route("/health", get(|| async { "ok" }))
.nest("/api", api_routes())
.layer(CompressionLayer::new())
.layer(RequestBodyLimitLayer::new(1024 * 1024 * 10)) // 10MB
// 주의: with_status_code는 tower-http 0.6.7+에서 도입. Cargo.toml "0.6"으로 지정하면 자동 해결됨
.layer(TimeoutLayer::with_status_code(
http::StatusCode::REQUEST_TIMEOUT,
Duration::from_secs(30),
))
.layer(cors)
.layer(trace)
}
| 실수 | 증상 | 해결 |
|---|---|---|
| feature flag 누락 | 컴파일 에러 (unresolved import) | Cargo.toml에 필요한 feature 추가 |
.layer() 순서 착각 | 로그에 CORS 에러 전 요청이 안 보임 | TraceLayer를 마지막 .layer()로 배치 |
Any origin + credentials | 브라우저 CORS 에러 | 명시적 origin 목록 사용 |
| tracing subscriber 미초기화 | TraceLayer 추가해도 로그 없음 | tracing_subscriber::fmt().init() 호출 |
| CompressionLayer + SSE | 이벤트가 버퍼링됨 | SSE 라우트에는 compression 미적용 |