Skip to content

Latest commit

ย 

History

History
509 lines (366 loc) ยท 21.1 KB

README_ko_KR.md

File metadata and controls

509 lines (366 loc) ยท 21.1 KB

Negroni

GoDoc Build Status codebeat codecov

๊ณต์ง€: ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์•„๋ž˜์˜ ์ฃผ์†Œ๋กœ ๋งŽ์ด ์•Œ๋ ค์ ธ ์™”์Šต๋‹ˆ๋‹ค. github.com/codegangsta/negroni -- Github๊ฐ€ ์ž๋™์œผ๋กœ ์ด ์ €์žฅ์†Œ์— ๋Œ€ํ•œ ์š”์ฒญ์„ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚ฌ ๊ฒƒ์ด์ง€๋งŒ, ํ™•์‹คํ•œ ์‚ฌ์šฉ์„ ์œ„ํ•ด ์ฐธ์กฐ๋ฅผ ์ด๊ณณ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

Negroni๋Š” Go์—์„œ ์›น ๋ฏธ๋“ค์›จ์–ด๋กœ์˜ ์ž์—ฐ์Šค๋Ÿฌ์šด ์ ‘๊ทผ์„ ์ถ”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ž‘๊ณ , ๊ฑฐ์Šฌ๋ฆฌ์ง€ ์•Š์œผ๋ฉฐ net/http ํ•ธ๋“ค๋Ÿฌ์˜ ์‚ฌ์šฉ์„ ์ง€ํ–ฅํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๋‹น์‹ ์ด Martini์˜ ๊ธฐ๋ณธ์ ์ธ ์ปจ์…‰์„ ์›ํ•˜์ง€๋งŒ, ์ด๊ฒƒ์ด ๋„ˆ๋ฌด ๋งŽ์€ ๊ธฐ๋Šฅ์„ ํฌํ•จํ•œ๋‹ค๊ณ  ๋Š๊ปด์กŒ๋‹ค๋ฉด Negroni๊ฐ€ ์ตœ์ ์˜ ์„ ํƒ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ

Go ์„ค์น˜์™€ GOPATH๋ฅผ ์„ธํŒ…ํ•˜๋Š” ์ž‘์—…์„ ์™„๋ฃŒํ•œ ๋’ค, ๋‹น์‹ ์˜ ์ฒซ .go ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์„ธ์š”. ์šฐ๋ฆฌ๋Š” ์ด๋ฅผ server.go ๋ผ๊ณ  ๋ถ€๋ฅผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  n := negroni.Classic() // ๊ธฐ๋ณธ ๋ฏธ๋“ค์›จ์–ด๋“ค์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค
  n.UseHandler(mux)

  http.ListenAndServe(":3000", n)
}

๊ทธ๋ฆฌ๊ณ  Negroni ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค (๊ณต์ง€: go 1.1 ์ด์ƒ์ด ์š”๊ตฌ๋ฉ๋‹ˆ๋‹ค) :

go get github.com/urfave/negroni

์„œ๋ฒ„๋ฅผ ์‹คํ–‰์‹œํ‚ต๋‹ˆ๋‹ค:

go run server.go

์ด์ œ localhost:3000์—์„œ ๋™์ž‘ํ•˜๋Š” Go net/http ์›น์„œ๋ฒ„๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

ํŒจํ‚ค์ง• (Packaging)

Debian์„ ์‚ฌ์šฉ์ค‘์ด์‹œ๋ผ๋ฉด, negroni๋Š” a package์—์„œ๋„ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. apt install golang-github-urfave-negroni-dev๋ฅผ ํ†ตํ•ด์„œ ์„ค์น˜๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. (๊ธ€์„ ์ž‘์„ฑํ•  ๋‹น์‹œ, ์ด๋Š” sid ์ €์žฅ์†Œ ์•ˆ์— ์žˆ์Šต๋‹ˆ๋‹ค.)

Negroni๋Š” ํ”„๋ ˆ์ž„์›Œํฌ(Framework)์ธ๊ฐ€์š”?

Negroni๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ด๋Š” net/http๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋””์ž์ธ๋œ ๋ฏธ๋“ค์›จ์–ด ์ค‘์‹ฌ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

๋ผ์šฐํŒ…(Routing) ?

Negroni๋Š” BYOR (Bring your own Router, ๋‚˜์˜ ๋ผ์šฐํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ)๋ฅผ ์ง€ํ–ฅํ•ฉ๋‹ˆ๋‹ค. Go ์ปค๋ฎค๋‹ˆํ‹ฐ์—๋Š” ์ด๋ฏธ ์ข‹์€ http ๋ผ์šฐํ„ฐ๋“ค์ด ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— Negroni๋Š” ๊ทธ๋“ค๊ณผ ์ž˜ ์–ด์šฐ๋Ÿฌ์งˆ ์ˆ˜ ์žˆ๋„๋ก net/http๋ฅผ ์ „์ ์œผ๋กœ ์ง€์›ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜๋Š” Gorilla Mux๋ฅผ ์‚ฌ์šฉํ•œ ์˜ˆ์ž…๋‹ˆ๋‹ค:

router := mux.NewRouter()
router.HandleFunc("/", HomeHandler)

n := negroni.New(Middleware1, Middleware2)
// Use() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ• ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
n.Use(Middleware3)
// ๋ผ์šฐํ„ฐ์˜ ๊ฒฝ์šฐ ๋งˆ์ง€๋ง‰์— ์˜ต๋‹ˆ๋‹ค.
n.UseHandler(router)

http.ListenAndServe(":3001", n)

negroni.Classic()

negroni.Classic()์€ ๋Œ€๋ถ€๋ถ„์˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋ณธ์ ์ธ ๋ฏธ๋“ค์›จ์–ด๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • negroni.Recovery - Panic ๋ณต๊ตฌ์šฉ ๋ฏธ๋“ค์›จ์–ด
  • negroni.Logger - Request/Response ๋กœ๊น… ๋ฏธ๋“ค์›จ์–ด
  • negroni.Static - "public" ๋””๋ ‰ํ„ฐ๋ฆฌ ์•„๋ž˜์˜ ์ •์  ํŒŒ์ผ ์ œ๊ณต(serving)์„ ์œ„ํ•œ ๋ฏธ๋“ค์›จ์–ด

์ด๋Š” Negroni์˜ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๋Š”๋ฐ ํฐ ๋„์›€์ด ๋˜๋„๋ก ๋งŒ๋“ค์–ด์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•ธ๋“ค๋Ÿฌ(Handlers)

Negroni๋Š” ์–‘๋ฐฉํ–ฅ ๋ฏธ๋“ค์›จ์–ด ํ๋ฆ„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” negroni.Handler ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

type Handler interface {
  ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

๋ฏธ๋“ค์›จ์–ด๊ฐ€ ResponseWriter์— ์•„์ง ๋ฌด์–ธ๊ฐ€ ์“ฐ์ง€ ์•Š์•˜๋‹ค๋ฉด, ์ด๋Š” ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด์— ์—ฐ๊ฒฐ๋˜์–ด์žˆ๋Š” http.HandleFunc๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  // next() ์ฒ˜๋ฆฌ ์ด์ „ ์ž‘์—… ์ˆ˜ํ–‰
  next(rw, r)
  // next() ์ฒ˜๋ฆฌ ์ดํ›„ ์ž‘์—… ์ˆ˜ํ–‰
}

์ดํ›„ Use ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ํ•ธ๋“ค๋Ÿฌ ์ฒด์ธ(handler chain)์— ๋งคํ•‘ ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

n := negroni.New()
n.Use(negroni.HandlerFunc(MyMiddleware))

๋˜ํ•œ, ๊ธฐ์กด์˜ http.Handler๋“ค๊ณผ๋„ ๋งคํ•‘์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

n := negroni.New()

mux := http.NewServeMux()
// ์—ฌ๊ธฐ์— ๋ผ์šฐํŠธ๋“ค์„ ๋งคํ•‘ํ•˜์„ธ์š”

n.UseHandler(mux)

http.ListenAndServe(":3000", n)

With()

Negroni๋Š” With๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ํŽธ๋ฆฌํ•œ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. With๋Š” ํ•œ ๊ฐœ ํ˜น์€ ๊ทธ ์ด์ƒ์˜ Handler ์ธ์Šคํ„ด์Šค๋“ค์„ ๋ฐ›์•„ ๊ธฐ์กด ๋ฆฌ์‹œ๋ฒ„์˜ ํ•ธ๋“ค๋Ÿฌ๋“ค๊ณผ ์ƒˆ๋กœ์šด ํ•ธ๋“ค๋Ÿฌ๋“ค์ด ์กฐํ•ฉ๋œ ์ƒˆ๋กœ์šด Negroni ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

// ์žฌ์‚ฌ์šฉ์„ ์›ํ•˜๋Š” ๋ฏธ๋“ค์›จ์–ด๋“ค
common := negroni.New()
common.Use(MyMiddleware1)
common.Use(MyMiddleware2)

// `specific`์€ `common`์˜ ํ•ธ๋“ค๋Ÿฌ๋“ค๊ณผ ์ƒˆ๋กœ ์ „๋‹ฌ๋œ ํ•ธ๋“ค๋Ÿฌ๋“ค์ด ์กฐํ•ฉ๋œ ์ƒˆ๋กœ์šด `negroni` ๊ฐ์ฒด
specific := common.With(
	SpecificMiddleware1,
	SpecificMiddleware2
)

Run()

Negroni๋Š” Run์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ํŽธ๋ฆฌํ•œ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Run์€ http.ListenAndServe์™€ ๊ฐ™์ด ์ฃผ์†Œ ์ŠคํŠธ๋ง ๊ฐ’(addr string)์„ ๋„˜๊ฒจ๋ฐ›์Šต๋‹ˆ๋‹ค.

package main

import (
  "github.com/urfave/negroni"
)

func main() {
  n := negroni.Classic()
  n.Run(":8080")
}

๋งŒ์•ฝ ์ฃผ์†Œ ๊ฐ’์ด ์ œ๊ณต๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, PORT ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ๋Œ€์‹  ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. PORT ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋˜ํ•œ ์ •์˜๋˜์–ด์žˆ์ง€ ์•Š๋‹ค๋ฉด, ๊ธฐ๋ณธ ์ฃผ์†Œ(default address)๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ „์ฒด ์„ค๋ช…์„ ๋ณด์‹œ๋ ค๋ฉด Run์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

์ผ๋ฐ˜์ ์œผ๋กœ๋Š”, ์ข€ ๋” ์œ ์—ฐํ•œ ์‚ฌ์šฉ์„ ์œ„ํ•ด์„œ net/http ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ negroni ๊ฐ์ฒด๋ฅผ ํ•ธ๋“ค๋Ÿฌ๋กœ์„œ ๋„˜๊ธฐ๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด:

package main

import (
  "fmt"
  "log"
  "net/http"
  "time"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  n := negroni.Classic() // ๊ธฐ๋ณธ ๋ฏธ๋“ค์›จ์–ด๋“ค์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค
  n.UseHandler(mux)

  s := &http.Server{
    Addr:           ":8080",
    Handler:        n,
    ReadTimeout:    10 * time.Second,
    WriteTimeout:   10 * time.Second,
    MaxHeaderBytes: 1 << 20,
  }
  log.Fatal(s.ListenAndServe())
}

๋ผ์šฐํŠธ ์ „์šฉ ๋ฏธ๋“ค์›จ์–ด(Route Specific Middleware)

๋งŒ์•ฝ ๋‹น์‹ ์ด ๋ผ์šฐํŠธ๋“ค์˜ ๋ผ์šฐํŠธ ๊ทธ๋ฃน์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด ๊ทธ๋Ÿฐ๋ฐ ๊ทธ๊ฒƒ์€ ์‹คํ–‰๋˜์–ด์•ผํ•˜๋Š” ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ•„์š”๋กœํ•œ๋‹ค.

ํŠน์ • ๋ผ์šฐํŠธ ๊ทธ๋ฃน๋งŒ์ด ์‚ฌ์šฉํ•˜๋Š” ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์žˆ๋‹ค๋ฉด, ๊ฐ„๋‹จํ•˜๊ฒŒ Negroni ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•˜์—ฌ ๋ผ์šฐํŠธ ํ•ธ๋“ค๋Ÿฌ(route handler)๋กœ์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// admin routes๋ฅผ ์—ฌ๊ธฐ์— ์ถ”๊ฐ€ํ•˜์„ธ์š”

// ๊ด€๋ฆฌ์ž ๋ฏธ๋“ค์›จ์–ด๋“ค์„ ์œ„ํ•œ ์ƒˆ๋กœ์šด negroni ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค
router.PathPrefix("/admin").Handler(negroni.New(
  Middleware1,
  Middleware2,
  negroni.Wrap(adminRoutes),
))

Gorilla Mux๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ์•„๋ž˜๋Š” ์„œ๋ธŒ ๋ผ์šฐํ„ฐ(subrouter)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค:

router := mux.NewRouter()
subRouter := mux.NewRouter().PathPrefix("/subpath").Subrouter().StrictSlash(true)
subRouter.HandleFunc("/", someSubpathHandler) // "/subpath/"
subRouter.HandleFunc("/:id", someSubpathHandler) // "/subpath/:id"

// "/subpath" ๋Š” subRouter์™€ ๋ฉ”์ธ ๋ผ์šฐํ„ฐ(main router)์˜ ์—ฐ๊ฒฐ ๋ณด์žฅ์„ ์œ„ํ•ด ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค
router.PathPrefix("/subpath").Handler(negroni.New(
  Middleware1,
  Middleware2,
  negroni.Wrap(subRouter),
))

With()๋Š” ๋ผ์šฐํ„ฐ ๊ฐ„ ๊ณต์œ ์‹œ ๋ฐœ์ƒํ•˜๋Š” ๋ฏธ๋“ค์›จ์–ด ์ค‘๋ณต์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

router := mux.NewRouter()
apiRoutes := mux.NewRouter()
// api ๋ผ์šฐํŠธ๋“ค์„ ์—ฌ๊ธฐ์— ์ถ”๊ฐ€ํ•˜์„ธ์š”
webRoutes := mux.NewRouter()
// web ๋ผ์šฐํŠธ๋“ค์„ ์—ฌ๊ธฐ์— ์ถ”๊ฐ€ํ•˜์„ธ์š”

// ๋ผ์šฐํ„ฐ๊ฐ„ ๊ณต์œ ๋  common ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค
common := negroni.New(
	Middleware1,
	Middleware2,
)

// common ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ 
// api ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์œ„ํ•œ ์ƒˆ๋กœ์šด negroni ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค
router.PathPrefix("/api").Handler(common.With(
  APIMiddleware1,
  negroni.Wrap(apiRoutes),
))
// common ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ 
// web ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์œ„ํ•œ ์ƒˆ๋กœ์šด negroni ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค
router.PathPrefix("/web").Handler(common.With(
  WebMiddleware1,
  negroni.Wrap(webRoutes),
))

๋ฒˆ๋“ค ๋ฏธ๋“ค์›จ์–ด(Bundled Middleware)

Static

์ด ๋ฏธ๋“ค์›จ์–ด๋Š” ํŒŒ์ผ๋“ค์„ ํŒŒ์ผ ์‹œ์Šคํ…œ(filesystem)์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ, ์š”์ฒญ์„ ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด๋กœ ๋„˜๊น๋‹ˆ๋‹ค. ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ์— ๋Œ€ํ•ด 404 File Not Found๋ฅผ ์œ ์ €์—๊ฒŒ ๋ฐ˜ํ™˜ํ•˜๊ธธ ์›ํ•˜๋Š” ๊ฒฝ์šฐ http.FileServer๋ฅผ ํ•ธ๋“ค๋Ÿฌ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์‚ดํŽด๋ณด์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ œ:

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  // "๋ฏธ๋“ค์›จ์–ด(middleware)"์˜ ์—ญํ• ๋ณด๋‹ค๋Š” "์„œ๋ฒ„์™€ ๊ฐ™์€(server-like)" ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ๋ฅผ ์›ํ•  ๋•Œ 
  // http.FileServer๋ฅผ ์‚ฌ์šฉํ•œ ์˜ˆ์ œ
  // mux.Handle("/public", http.FileServer(http.Dir("/home/public")))  

  n := negroni.New()
  n.Use(negroni.NewStatic(http.Dir("/tmp")))
  n.UseHandler(mux)

  http.ListenAndServe(":3002", n)
}

์œ„ ์ฝ”๋“œ๋Š” /tmp ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ๋ถ€ํ„ฐ ํŒŒ์ผ์„ ์ œ๊ณตํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํŒŒ์ผ ์‹œ์Šคํ…œ ์ƒ์˜ ํŒŒ์ผ์— ๋Œ€ํ•œ ์š”์ฒญ์ด ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ํ”„๋ก์‹œ๋Š” ๋‹ค์Œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Recovery

์ด ๋ฏธ๋“ค์›จ์–ด๋Š” panic๋“ค์„ ๊ฐ์ง€ํ•˜๊ณ  500 ์‘๋‹ต ์ฝ”๋“œ(response code)๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์‘๋‹ต ์ฝ”๋“œ ๋˜๋Š” ๋ฐ”๋””(body)๋ฅผ ์“ธ ๊ฒฝ์šฐ, ํด๋ผ์ด์–ธํŠธ๋Š” ์ด๋ฏธ HTTP ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ๋ฐ›์•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์ ์ ˆํ•œ ์‹œ์ ์— 500 ์ฝ”๋“œ๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒƒ์— ์‹คํŒจํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ถ”๊ฐ€์ ์œผ๋กœ PanicHandlerFunc๋Š” Sentry ๋˜๋Š” Aribrake์™€ ๊ฐ™์€ ์—๋Ÿฌ ๋ณด๊ณ  ์„œ๋น„์Šค์— 500 ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋ถ™์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ:

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    panic("oh no")
  })

  n := negroni.New()
  n.Use(negroni.NewRecovery())
  n.UseHandler(mux)

  http.ListenAndServe(":3003", n)
}

์œ„ ์ฝ”๋“œ๋Š” ๊ฐ ์š”์ฒญ์— ๋Œ€ํ•ด 500 Internal Server Error ๋ฐ˜ํ™˜ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. PrintStack ๊ฐ’์ด true (๊ธฐ๋ณธ ๊ฐ’)๋กœ ์„ค์ •๋˜์–ด์žˆ๋‹ค๋ฉด ์š”์ฒญ์ž(requester)์—๊ฒŒ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค(stack trace) ๊ฐ’์„ ์ถœ๋ ฅํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋กœ๊น… ๋˜ํ•œ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

PanicHandlerFunc๋ฅผ ์‚ฌ์šฉํ•œ ์˜ˆ์ œ:

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    panic("oh no")
  })

  n := negroni.New()
  recovery := negroni.NewRecovery()
  recovery.PanicHandlerFunc = reportToSentry
  n.Use(recovery)
  n.UseHandler(mux)

  http.ListenAndServe(":3003", n)
}

func reportToSentry(info *negroni.PanicInformation) {
    // Sentry์—๊ฒŒ ์—๋Ÿฌ๋ฅผ ๋ณด๊ณ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”
}

๋ฏธ๋“ค์›จ์–ด๋Š” STDOUT์— ๊ธฐ๋ณธ์œผ๋กœ ๊ฐ’๋“ค์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ SetFormatter() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ์ถœ๋ ฅ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹น์—ฐํžˆ HTMLPanicFormatter๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊น”๋”ํ•œ HTML๋กœ๋„ ์—๋Ÿฌ ์ƒํ™ฉ์„ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

package main

import (
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    panic("oh no")
  })

  n := negroni.New()
  recovery := negroni.NewRecovery()
  recovery.Formatter = &negroni.HTMLPanicFormatter{}
  n.Use(recovery)
  n.UseHandler(mux)

  http.ListenAndServe(":3003", n)
}

Logger

์ด ๋ฏธ๋“ค์›จ์–ด๋Š” ์„œ๋ฒ„์— ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ๊ณผ ์‘๋‹ต๋“ค์„ ๊ธฐ๋กํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ œ:

package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  n := negroni.New()
  n.Use(negroni.NewLogger())
  n.UseHandler(mux)

  http.ListenAndServe(":3004", n)
}

์œ„ ์ฝ”๋“œ๋Š” ๊ฐ ์š”์ฒญ์— ๋Œ€ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด ์ถœ๋ ฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

[negroni] 2017-10-04T14:56:25+02:00 | 200 |      378ยตs | localhost:3004 | GET /

๋ฌผ๋ก , SetFormat ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์‚ฌ์šฉ์ž๋งŒ์˜ ๋กœ๊ทธ ํฌ๋งท(log format) ๋˜ํ•œ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋กœ๊ทธ ํฌ๋งท์€ LoggerEntry ๊ตฌ์กฐ์ฒด ๋‚ด๋ถ€์˜ ํ•„๋“œ๋“ค๋กœ ๊ตฌ์„ฑ๋œ ํ…œํ”Œ๋ฆฟ ๋ฌธ์ž์—ด์ž…๋‹ˆ๋‹ค.

l.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}")

์œ„ ๊ตฌ์กฐ๋Š” ์ด์™€ ๊ฐ™์ด ์ถœ๋ ฅ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค - [200 18.263ยตs] - Go-User-Agent/1.1

Third Party Middleware

์•„๋ž˜๋Š” ํ˜„์žฌ(2018.10.10) Negroni์™€ ํ˜ธํ™˜๋˜๋Š” ๋ฏธ๋“ค์›จ์–ด๋“ค์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ๋งํฌ๋˜๊ธฐ๋ฅผ ์›ํ•œ๋‹ค๋ฉด ์ž์œ ๋กญ๊ฒŒ PR์„ ๋ณด๋‚ด์ฃผ์„ธ์š”.

Middleware Author Description
authz Yang Luo ACL, RBAC, ABAC Authorization middlware based on Casbin
binding Matt Holt Data binding from HTTP requests into structs
cloudwatch Colin Steele AWS cloudwatch metrics middleware
cors Olivier Poitrey Cross Origin Resource Sharing (CORS) support
csp Awake Networks Content Security Policy (CSP) support
delay Jeff Martinez Add delays/latency to endpoints. Useful when testing effects of high latency
New Relic Go Agent Yadvendar Champawat Official New Relic Go Agent (currently in beta)
gorelic Jingwen Owen Ou New Relic agent for Go runtime
Graceful Tyler Bunnell Graceful HTTP Shutdown
gzip phyber GZIP response compression
JWT Middleware Auth0 Middleware checks for a JWT on the Authorization header on incoming requests and decodes it
JWT Middleware Marcelo Fuentes JWT middleware for golang
logrus Dan Buch Logrus-based logger
oauth2 David Bochenski oAuth2 middleware
onthefly Alexander Rรธdseth Generate TinySVG, HTML and CSS on the fly
permissions2 Alexander Rรธdseth Cookies, users and permissions
prometheus Rene Zbinden Easily create metrics endpoint for the prometheus instrumentation tool
prometheus Xabier Larrakoetxea Prometheus metrics with multiple options that follow standards and try to be measured in a efficent way
render Cory Jacobsen Render JSON, XML and HTML templates
RestGate Prasanga Siripala Secure authentication for REST API endpoints
secure Cory Jacobsen Middleware that implements a few quick security wins
sessions David Bochenski Session Management
stats Florent Messa Store information about your web application (response time, etc.)
VanGoH Taylor Wrobel Configurable AWS-Style HMAC authentication middleware
xrequestid Andrea Franz Middleware that assigns a random X-Request-Id header to each request
mgo session Joel James Middleware that handles creating and closing mgo sessions per request
digits Bilal Amarni Middleware that handles Twitter Digits authentication
stats Chirag Gupta Middleware that manages qps and latency stats for your endpoints and asynchronously flushes them to influx db
Chaos Marc Falzon Middleware for injecting chaotic behavior into application in a programmatic way

์˜ˆ์ œ

Alexander Rรธdseth๋Š” Negroni ๋ฏธ๋“ค์›จ์–ด ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ผˆ๋Œ€์ธ mooseware๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

Prasanga Siripala๋Š” ์›น ๊ธฐ๋ฐ˜์˜ Go/Negroni ํ”„๋กœ์ ํŠธ๋“ค์„ ์œ„ํ•œ ํšจ์œจ์ ์ธ ๋ผˆ๋Œ€ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค : Go-Skeleton

์ฝ”๋“œ ์‹ค์‹œ๊ฐ„ ์ƒˆ๋กœ๊ณ ์นจ(Live code reload)?

gin๊ณผ fresh ๋ชจ๋‘ negroni ์•ฑ์˜ ์‹ค์‹œ๊ฐ„ ์ƒˆ๋กœ๊ณ ์นจ(live reload)์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

Go & Negroni ์ดˆ์‹ฌ์ž๋“ค์ด ํ•„์ˆ˜์ ์œผ๋กœ ์ฝ์–ด์•ผํ•˜๋Š” ์ž๋ฃŒ๋“ค

์ถ”๊ฐ€ ์ •๋ณด

Negroni๋Š” Code Gangsta์— ์˜ํ•ด ๋””์ž์ธ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.