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을 사용중이시라면, negronia 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의 유용한 기능들을 사용하기 시작하는데 큰 도움이 되도록 만들어줄 것입니다.

핸들러(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이라고 불리는 편리한 함수를 가지고 있습니다. Runhttp.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)?

ginfresh 모두 negroni 앱의 실시간 새로고침(live reload)을 지원합니다.

Go & Negroni 초심자들이 필수적으로 읽어야하는 자료들

추가 정보

Negroni는 Code Gangsta에 의해 디자인 되었습니다.