마티니(Martini)는 강력하고 손쉬운 웹애플리케이션 / 웹서비스개발을 위한 Golang 패키지입니다.
Go 인스톨 및 GOPATH 환경변수 설정 이후에, .go
파일 하나를 만들어 보죠..흠... 일단 server.go
라고 부르겠습니다.
package main
import "github.com/go-martini/martini"
func main() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello, 세계!"
})
m.Run()
}
마티니 패키지를 인스톨 합니다. (go 1.1 혹은 그 이상 버젼 필요):
go get github.com/go-martini/martini
이제 서버를 돌려 봅시다:
go run server.go
마티니 웹서버가 localhost:3000
에서 돌아가고 있는 것을 확인하실 수 있을 겁니다.
메일링 리스트에 가입해 주세요
데모 비디오도 있어요.
혹은 Stackoverflow에 마티니 태크를 이용해서 물어봐 주세요
GoDoc 문서(documentation)
문제는 전부다 영어로 되어 있다는 건데요 -_-;;; 나는 한글 아니면 보기 싫다! 하는 분들은 아래 링크를 참조하세요
- golang-korea
- 혹은 (RexK)의 이메일로 연락주세요.
- 사용하기 엄청 쉽습니다.
- 비간섭(Non-intrusive) 디자인
- 다른 Golang 패키지들과 잘 어울립니다.
- 끝내주는 경로 매칭과 라우팅.
- 모듈형 디자인 - 기능추가가 쉽고, 코드 꺼내오기도 쉬움.
- 유용한 핸들러와 미들웨어가 많음.
- 훌륭한 패키지화(out of the box) 기능들
- http.HandlerFunc 인터페이스와 호환율 100%
미들웨어들과 추가기능들은 martini-contrib에서 확인해 주세요.
마티니를 쉽고 빠르게 이용하시려면, martini.Classic()를 이용해 보세요. 보통 웹애플리케이션에서 사용하는 설정들이 이미 포함되어 있습니다.
m := martini.Classic()
// ... 미들웨어와 라우팅 설정은 이곳에 오면 작성하면 됩니다.
m.Run()
아래는 martini.Classic()에 자동으로 제공되는 기본 기능들 입니다.
- Request/Response 로그 기능 - martini.Logger
- 패닉 리커버리 (Panic Recovery) - martini.Recovery
- 정적 파일 서빙 - martini.Static
- 라우팅(Routing) - martini.Router
핸들러(Handlers)는 마티니의 핵심입니다. 핸들러는 기본적으로 실행 가능한 모든 형태의 함수들입니다.
m.Get("/", func() {
println("hello 세계")
})
핸들러가 반환을 하는 함수라면, 마티니는 반환 값을 http.ResponseWriter에 스트링으로 입력 할 것입니다.
m.Get("/", func() string {
return "hello 세계" // HTTP 200 : "hello 세계"
})
원하신다면, 선택적으로 상태코드도 함께 반환 할 수 있습니다.
m.Get("/", func() (int, string) {
return 418, "난 주전자야!" // HTTP 418 : "난 주전자야!"
})
핸들러들은 리플렉션을 통해 호출됩니다. 마티니는 의존성 주입을 이용해서 핸들러의 인수들을 주입합니다. 이것이 마티니를 http.HandlerFunc
인터페이스와 100% 호환할 수 있게 해줍니다.
핸들러의 인수를 입력했다면, 마티니가 서비스 목록들을 살펴본 후 타입확인(type assertion)을 통해 의존성문제 해결을 시도 할 것입니다.
m.Get("/", func(res http.ResponseWriter, req *http.Request) { // res와 req는 마티니에 의해 주입되었다.
res.WriteHeader(200) // HTTP 200
})
아래 서비스들은 martini.Classic():에 포함되어 있습니다.
- *log.Logger - 마티니의 글로벌(전역) 로그.
- martini.Context - http 요청 컨텍스트.
- martini.Params - 루트 매칭으로 찾은 인자를
map[string]string
으로 변형. - martini.Routes - 루트 도우미 서비스.
- http.ResponseWriter - http Response writer 인터페이스.
- *http.Request - http 리퀘스트.
마티니에서 루트는 HTTP 메소드와 URL매칭 패턴의 페어입니다. 각 루트는 하나 혹은 그 이상의 핸들러 메소드를 가질 수 있습니다.
m.Get("/", func() {
// 보여줘 봐
})
m.Patch("/", func() {
// 업데이트 좀 해
})
m.Post("/", func() {
// 만들어봐
})
m.Put("/", func() {
// 변경해봐
})
m.Delete("/", func() {
// 없애버려!
})
m.Options("/", func() {
// http 옵션 메소드
})
m.NotFound(func() {
// 404 해결하기
})
루트들은 정의된 순서대로 매칭된다. 들어온 요구에 처음으로 매칭된 루트가 호출된다.
루트 패턴은 martini.Params service로 액세스 가능한 인자들을 포함하기도 한다:
m.Get("/hello/:name", func(params martini.Params) string {
return "Hello " + params["name"] // :name을 Params인자에서 추출
})
루트는 별표식(*)으로 매칭 될 수도 있습니다:
m.Get("/hello/**", func(params martini.Params) string {
return "Hello " + params["_1"]
})
정규식도 사용가능합니다:
m.Get("/hello/(?P<name>[a-zA-Z]+)", func(params martini.Params) string {
return fmt.Sprintf ("Hello %s", params["name"])
})
정규식에 관하여 더 자세히 알고 싶다면 Go documentation을 참조해 주세요.
루트 핸들러는 스택을 쌓아 올릴 수 있습니다. 특히 유저 인증작업이나, 허가작업에 유용히 쓰일 수 있죠.
m.Get("/secret", authorize, func() {
// 이 함수는 authorize 함수가 resopnse에 결과를 쓰지 않는이상 실행 될 거에요.
})
RootGroup
은 루트들을 한 곳에 모아 정리하는데 유용합니다.
m.Group("/books", func(r martini.Router) {
r.Get("/:id", GetBooks)
r.Post("/new", NewBook)
r.Put("/update/:id", UpdateBook)
r.Delete("/delete/:id", DeleteBook)
})
핸들러에 미들웨어를 집어넣을 수 있는것과 같이, 그룹에도 미들웨어를 집어넣는 것이 가능합니다.
m.Group("/books", func(r martini.Router) {
r.Get("/:id", GetBooks)
r.Post("/new", NewBook)
r.Put("/update/:id", UpdateBook)
r.Delete("/delete/:id", DeleteBook)
}, MyMiddleware1, MyMiddleware2)
서비스는 핸들러의 인수목록에 주입 될 수 있는 오브젝트들을 말합니다. 서비스는 글로벌 혹은 리퀘스트 레벨단위로 주입이 가능합니다.
마타니 인스턴스는 서비스 맵핑을 쉽게 하기 위해서 inject.Injector 인터페이스를 반형합니다:
db := &MyDatabase{}
m := martini.Classic()
m.Map(db) // 서비스가 모든 핸들러에서 *MyDatabase로서 사용될 수 있습니다.
// ...
m.Run()
리퀘스트 레벨 맵핑은 핸들러안에서 martini.Context를 사용하면 됩니다:
func MyCustomLoggerHandler(c martini.Context, req *http.Request) {
logger := &MyCustomLogger{req}
c.Map(logger) // *MyCustomLogger로서 맵핑 됨
}
서비스의 강력한 기능중 하나는 서비스를 인터페이스로 맵핑이 가능하다는 것입니다. 예를들어, http.ResponseWriter를 재정의(override)해서 부가 기능들을 수행하게 하고 싶으시다면, 아래와 같이 핸들러를 작성 하시면 됩니다.
func WrapResponseWriter(res http.ResponseWriter, c martini.Context) {
rw := NewSpecialResponseWriter(res)
c.MapTo(rw, (*http.ResponseWriter)(nil)) // ResponseWriter를 NewResponseWriter로 치환(override)
}
martini.Classic() 인스턴스는 "public" 폴더안에 있는 파일들을 정적파일로써 자동으로 서빙합니다. 더 많은 폴더들은 정적파일 폴더에 포함시키시려면 martini.Static 핸들러를 이용하시면 됩니다.
m.Use(martini.Static("assets")) // "assets" 폴더에서도 정적파일 서빙.
미들웨어 핸들러는 http request와 라우팅 사이에서 작동합니다. 미들웨어 핸들러는 근본적으로 다른 핸들러들과는 다릅니다. 사용방법은 아래와 같습니다:
m.Use(func() {
// 미들웨어 임무 수행!
})
Handlers
를 이용하여 미들웨어 스택들의 완전 컨트롤이 가능합니다. 다만, 이렇게 설정하시면 이전에 Handlers
를 이용하여 설정한 핸들러들은 사라집니다:
m.Handlers(
Middleware1,
Middleware2,
Middleware3,
)
미들웨어 핸들러는 로깅(logging), 허가(authorization), 인가(authentication), 세션, 압축(gzipping), 에러 페이지 등 등, http request의 전후로 실행되어야 할 일들을 처리하기 아주 좋습니다:
// API 키 확인작업
m.Use(func(res http.ResponseWriter, req *http.Request) {
if req.Header.Get("X-API-KEY") != "비밀암호!!!" {
res.WriteHeader(http.StatusUnauthorized) // HTTP 401
}
})
Context.Next()는 선택적 함수입니다. 이 함수는 http request가 다 작동 될때까지 기다립니다.따라서 http request 이후에 실행 되어야 할 업무들을 수행하기 좋은 함수입니다.
// log before and after a request
m.Use(func(c martini.Context, log *log.Logger){
log.Println("request전입니다.")
c.Next()
log.Println("request후 입니다.")
})
마티니 핸들러들은 martini.Env
글로벌 변수를 사용하여 개발환경에서는 프로덕션 환경과는 다르게 작동하기도 합니다. 따라서, 프로덕션 서버로 마티니 서버를 배포하시게 된다면 꼭 환경변수 MARTINI_ENV=production
를 세팅해주시기 바랍니다.
깃헙에서 martini-contrib 프로젝트들을 찾아보세요. 만약에 못 찾으시겠으면, martini-contrib 팀원들에게 연락해서 하나 만들어 달라고 해보세요.
- auth - 인증작업을 도와주는 핸들러.
- binding - request를 맵핑하고 검사하는 핸들러.
- gzip - gzip 핸들러.
- render - HTML 템플레이트들과 JSON를 사용하기 편하게 해주는 핸들러.
- acceptlang -
Accept-Language
HTTP 해더를 파싱 할 때 유용한 핸들러. - sessions - 세션 서비스를 제공하는 핸들러.
- strip - URL 프리틱스를 없애주는 핸들러.
- method - 해더나 폼필드를 이용한 HTTP 메소드 치환.
- secure - 몇몇 보안설정을 위한 핸들러.
- encoder - 데이터 렌더링과 컨텐트 타입을위한 인코딩 서비스.
- cors - CORS 서포트를 위한 핸들러.
- oauth2 - OAuth2.0 로그인 핸들러. 페이스북, 구글, 깃헙 지원.
마티니 인스턴스는 http.Handler
인터페이스를 차용합니다. 따라서 Go 서버 서브트리로 쉽게 사용될 수 있습니다. 아래 코드는 구글 앱 엔진에서 작동하는 마티니 앱입니다:
package hello
import (
"net/http"
"github.com/go-martini/martini"
)
func init() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello 세계!"
})
http.Handle("/", m)
}
마티니의 Run
함수는 PORT와 HOST 환경변수를 이용하는데, 설정이 안되어 있다면 localhost:3000으로 설정 되어 집니다.
좀더 유연하게 설정을 하고 싶다면, martini.RunOnAddr
를 활용해 주세요.
m := martini.Classic()
// ...
log.Fatal(m.RunOnAddr(":8080"))
gin 과 fresh 가 마티니 앱의 라이브 리로드를 도와줍니다.
마티니는 간단하고 가벼운 패키지로 남을 것입니다. 따라서 보통 대부분의 공헌들은 martini-contrib 그룹의 저장소로 가게 됩니다. 만약 마티니 코어에 기여하고 싶으시다면 주저없이 Pull Request를 해주세요.
마티니는 Code Gangsta가 디자인 했습니다.