Negroni 대 다른 Go 미들웨어 솔루션: 무엇이 특별한 걸까?
Ethan Miller
Product Engineer · Leapcell

Negroni 라이브러리 소개
Negroni는 HTTP 미들웨어에 중점을 둔 라이브러리입니다. 크기가 작고 비침투적이며 표준 라이브러리 net/http
의 핸들러 사용을 권장합니다. 이 기사에서는 이 라이브러리에 대한 자세한 소개를 제공합니다.
I. 미들웨어를 사용하는 이유
개발 과정에서 통계, 로깅, 디버깅 등과 같은 몇 가지 일반적인 논리 코드가 있으며 이러한 논리는 각 핸들러에서 필요할 수 있습니다. 이러한 코드를 각 핸들러에 하나씩 추가하면 번거로울 뿐만 아니라 오류 및 누락이 발생하기 쉽습니다.
핸들러의 시간 소모량을 계산하는 예를 들어 보겠습니다. 시간 소모량을 계산하는 코드가 각 핸들러에 추가되면 예는 다음과 같습니다.
package main import ( "fmt" "net/http" "time" ) func index(w http.ResponseWriter, r *http.Request) { start := time.Now() fmt.Fprintf(w, "leapcell page") fmt.Printf("index elasped:%fs", time.Since(start).Seconds()) } func greeting(w http.ResponseWriter, r *http.Request) { start := time.Now() name := r.FormValue("name") if name == "" { name = "world" } fmt.Fprintf(w, "hello %s", name) fmt.Printf("greeting elasped:%fs\n", time.Since(start).Seconds()) } func main() { mux := http.NewServeMux() mux.HandleFunc("/", index) mux.HandleFunc("/greeting", greeting) http.ListenAndServe(":8000", mux) }
그러나 이 접근 방식에는 다음과 같은 많은 단점이 있습니다.
- 유연성 부족: 새 핸들러가 추가될 때마다 시간 소모량을 계산하는 이 코드를 추가해야 하며 이러한 코드는 실제 핸들러 논리와 직접적인 관련이 없습니다.
- 누락되기 쉬움: 핸들러를 작성할 때 이러한 코드를 추가하는 것을 잊기 쉽고, 특히 가능한 모든 반환 경로를 고려할 때 코딩 부담이 증가합니다.
- 수정에 도움이 되지 않음: 통계 코드에 오류가 있거나 조정해야 하는 경우 관련된 모든 핸들러를 수정해야 합니다.
- 새로운 논리 추가가 어려움: 다른 통계 논리를 추가해야 하는 경우 모든 핸들러의 코드도 변경해야 합니다.
Go 언어의 클로저 기능을 활용하여 실제 핸들러 코드를 함수로 캡슐화하고 이 함수에서 추가적인 논리를 실행할 수 있습니다. 다음과 같이 표시됩니다.
func elasped(h func(w http.ResponseWriter, r *http.Request)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path start := time.Now() h(w, r) fmt.Printf("path:%s elasped:%fs\n", path, time.Since(start).Seconds()) } } func index(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "leapcell page") } func greeting(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") if name == "" { name = "world" } fmt.Fprintf(w, "hello %s", name) } func main() { mux := http.NewServeMux() mux.HandleFunc("/", elasped(index)) mux.HandleFunc("/greeting", elasped(greeting)) http.ListenAndServe(":8000", mux) }
이 예에서는 핸들러와 관련 없는 추가 코드가 elasped
함수에 배치됩니다. 핸들러 함수를 등록할 때 원래 핸들러 함수를 직접 사용하는 대신 elasped
함수로 캡슐화합니다. 실제로 elasped
와 같은 함수는 미들웨어입니다. 원래 핸들러 함수를 캡슐화하고 새 핸들러 함수를 반환하여 실제 처리 논리 전후에 코드를 삽입하는 데 편리하며 추가, 수정 및 유지 관리에 유용합니다.
II. Negroni 빠른 시작
(I) 설치
다음 명령을 실행하여 Negroni 라이브러리를 설치합니다.
$ go get github.com/urfave/negroni
(II) 사용 예
package main import ( "fmt" "net/http" "github.com/urfave/negroni" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.Classic() n.UseHandler(mux) http.ListenAndServe(":8080", n) }
Negroni는 사용하기 비교적 쉽고 http.Handler
와 편리하게 작동할 수 있습니다. negroni.Classic()
은 몇 가지 일반적으로 사용되는 미들웨어를 제공합니다.
- negroni.Recovery: 프로그램 실행 과정에서 발생하는
panic
으로부터 복구하는 데 사용됩니다. 핸들러 코드에서panic
이 발생하면 이 미들웨어가 예외를 잡고 프로그램이 종료되는 것을 방지합니다. - negroni.Logger: 요청 및 응답에 대한 기본 정보를 기록하여 로깅 기능을 구현합니다.
- negroni.Static:
public
디렉터리에서 정적 파일 서비스를 제공할 수 있습니다.
n.UseHandler(mux)
를 호출하여 이러한 미들웨어를 멀티플렉서 mux
에 적용합니다. 프로그램을 실행한 후 브라우저에 localhost:3000
을 입력하면 콘솔에 다음과 유사한 출력이 표시됩니다.
[negroni] 2025-03-22T18:48:53+08:00 | 200 | 10.9966ms | localhost:8080 | GET /
III. Negroni의 심층 기능
(I) negroni.Handler 인터페이스
negroni.Handler
인터페이스는 미들웨어의 실행 프로세스에 대한 보다 유연한 제어를 제공합니다. 인터페이스는 다음과 같이 정의됩니다.
type Handler interface { ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) }
작성된 미들웨어의 서명은 func(http.ResponseWriter, *http.Request, http.HandlerFunc)
이거나 negroni.Handler
인터페이스를 구현해야 합니다. 예를 들어:
func RandomMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { if rand.Int31n(100) <= 50 { fmt.Fprintf(w, "hello from RandomMiddleware") } else { next(w, r) } } func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.New() n.Use(negroni.HandlerFunc(RandomMiddleware)) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
위의 코드는 임의의 미들웨어를 구현합니다. 응답이 RandomMiddleware
미들웨어에서 직접 반환될 확률이 50%이고 실제 핸들러 함수가 실행될 확률이 50%입니다. 프로그램을 실행한 후 브라우저에서 localhost:8080
을 계속 새로 고쳐 효과를 관찰합니다.
실제로 func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
는 작성하기 편리한 방법입니다. n.Use
를 호출할 때 negroni.HandlerFunc
로 캡슐화되고 negroni.HandlerFunc
는 negroni.Handler
인터페이스를 구현합니다. 다음은 관련 코드입니다.
// src/github.com/urfave/negroni/negroni.go type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { h(rw, r, next) }
마찬가지로 net/http
에서 func(http.ResponseWriter, *http.Request)
도 http.HandlerFunc
로 캡슐화되어 http.Handler
인터페이스를 구현합니다.
(II) negroni.With 메서드
미들웨어가 여러 개인 경우 n.Use()
메서드를 사용하여 하나씩 추가하는 것은 번거롭습니다. Negroni는 하나 이상의 negroni.Handler
매개변수를 허용하고 새 객체를 반환하는 With()
메서드를 제공합니다. 예는 다음과 같습니다.
func Middleware1(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { fmt.Println("Middleware A begin") next(w, r) fmt.Println("Middleware A end") } func Middleware2(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { fmt.Println("Middleware B begin") next(w, r) fmt.Println("Middleware B end") } func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.New() n = n.With( negroni.HandlerFunc(Middleware1), negroni.HandlerFunc(Middleware2), ) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
(III) Run 메서드
Negroni 객체는 서버 프로그램을 편리하게 실행하는 데 사용되는 Run()
메서드를 제공합니다. 이 메서드는 http.ListenAndServe()
과 동일한 주소(Addr) 매개변수를 허용합니다. 예는 다음과 같습니다.
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.New() n.UseHandler(mux) n.Run(":8080") }
포트가 지정되지 않은 경우 Run()
메서드는 PORT
환경 변수를 사용하려고 시도합니다. PORT
환경 변수도 설정되지 않은 경우 기본 포트 :8080
이 사용됩니다.
(IV) http.Handler로 사용
Negroni는 net/http
프로그램에서 쉽게 사용할 수 있으며 negroni.Negroni
객체를 http.Handler
로 직접 해당 메서드에 전달할 수 있습니다. 예는 다음과 같습니다.
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.Classic() n.UseHandler(mux) s := &http.Server{ Addr: ":8080", Handler: n, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } s.ListenAndServe() }
IV. 내장 미들웨어
(I) Static
negroni.Static
미들웨어는 지정된 디렉터리의 파일 서비스를 제공할 수 있습니다. 예제 코드는 다음과 같습니다.
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello world") }) n := negroni.New() n.Use(negroni.NewStatic(http.Dir("./public"))) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
프로그램의 실행 디렉터리에 public
디렉터리를 만들고 그 안에 일부 파일(1.txt
, 2.jpg
등)을 넣습니다. 프로그램이 실행된 후 브라우저에서 localhost:8080/1.txt
및 localhost:8080/2.jpg
에 접속하여 이러한 파일을 요청할 수 있습니다. 해당 파일을 찾을 수 없는 경우 Static
미들웨어는 요청을 다음 미들웨어 또는 핸들러 함수로 전달합니다. 예를 들어 브라우저에 localhost:3000/none - exist.txt
를 입력하면 hello world
의 응답이 표시됩니다.
(II) Logger
"빠른 시작" 섹션에서 이 미들웨어는 negroni.Classic()
을 통해 사용되었습니다. 요청 정보를 기록하기 위해 단독으로 사용할 수도 있으며 SetFormat()
메서드를 호출하여 로그 형식을 설정할 수 있습니다. 예는 다음과 같습니다.
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello world") }) n := negroni.New() logger := negroni.NewLogger() logger.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}") n.Use(logger) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
위의 코드는 로그 형식을 [{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}
로 설정합니다. 즉, 응답 상태, 시간 소모 및 UserAgent
를 기록합니다.
V. 타사 미들웨어
내장 미들웨어 외에도 Negroni에는 많은 타사 미들웨어가 있습니다. 전체 목록은 https://github.com/urfave/negroni?tab=readme-ov-file#third-party-middleware를 참조하십시오.
VI. 결론
Negroni는 미들웨어 기능에 중점을 두고 너무 번거롭고 거의 사용되지 않는 멋진 기능이 없습니다. 비침투적인 설계 기능 덕분에 표준 라이브러리 net/http
및 기타 웹 라이브러리(gorilla/mux
등)와 원활하게 작동할 수 있습니다. 이 기능은 개발자가 HTTP 서비스를 구축할 때 큰 편의를 제공합니다. 개발자는 로깅, 오류 처리 및 요청 통계와 같은 다양한 기능을 달성하기 위해 실제 필요에 따라 미들웨어를 유연하게 선택하고 결합할 수 있으며 기존 코드 아키텍처를 대규모로 변경할 필요가 없습니다.
전반적으로 Negroni는 Go 언어 HTTP 서비스 개발에서 매우 가치 있는 라이브러리입니다. 효율적이고 유연하며 유지 관리하기 쉬운 웹 애플리케이션 개발을 추구하는 사람들에게 Negroni는 의심할 여지 없이 심층적인 이해와 광범위한 적용을 할 가치가 있는 훌륭한 도구입니다.
Leapcell: 최고의 서버리스 웹 호스팅
마지막으로 Go 서비스를 배포하기 위한 최고의 플랫폼인 **Leapcell**을 소개하고 싶습니다.
🚀 좋아하는 언어로 빌드
JavaScript, Python, Go 또는 Rust로 간편하게 개발하십시오.
🌍 무료로 무제한 프로젝트 배포
사용한 만큼만 지불하십시오. 요청도 없고 요금도 없습니다.
⚡ 사용한 만큼 지불, 숨겨진 비용 없음
유휴 요금 없이 원활한 확장성만 제공됩니다.
🔹 Twitter에서 팔로우하세요: @LeapcellHQ