XNUMX๋
์ ์ ๋๋ ์์ํ๋ค
์ด ๋ฌธ์๋ Go๋ก ์๋ฒ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ๊ดํ ๊ฒ์ ๋๋ค. "Hello world!"์ ๊ฐ์ ๊ฐ๋จํ ๊ฒ๋ถํฐ ์์ํ์ฌ ๋ค์ ๊ธฐ๋ฅ์ ๊ฐ์ถ ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ๋ง๋ฌด๋ฆฌํด ๋ณด๊ฒ ์ต๋๋ค.
- HTTPS์ ๋ํด Let's Encrypt๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- API ๋ผ์ฐํฐ๋ก ์๋ํฉ๋๋ค.
โ ๋ฏธ๋ค์จ์ด ์์
.
- ์ ์ ํ์ผ์ ์ฒ๋ฆฌํฉ๋๋ค.
- ์ฌ๋ฐ๋ฅธ ์ข
๋ฃ.
Skillbox๋ ๋ค์์ ๊ถ์ฅํฉ๋๋ค. ์ค๊ธฐ ์ฝ์ค
"์ฒ์๋ถํฐ Python ๊ฐ๋ฐ์" .์๋ฆผ: "Habr"์ ๋ชจ๋ ๋ ์๋ฅผ ์ํ - "Habr" ํ๋ก๋ชจ์ ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ Skillbox ๊ณผ์ ์ ๋ฑ๋กํ ๋ 10 ๋ฃจ๋ธ ํ ์ธ.
์๋ , ์ธ์!
Go์์ ์น ์๋ฒ๋ฅผ ๋ง๋๋ ๊ฒ์ ๋งค์ฐ ๋น ๋ฅผ ์ ์์ต๋๋ค. ๋ค์์ ์์์ ์ฝ์ํ "Hello, world!"๋ฅผ ๋ฐํํ๋ ํธ๋ค๋ฌ๋ฅผ ์ฌ์ฉํ๋ ์์ ๋๋ค.
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
})
http.ListenAndServe(":80", nil)
}
๊ทธ ํ ์ดํ๋ฆฌ์ผ์ด์
์ ์คํํ๊ณ ํ์ด์ง๋ฅผ ์ด๋ฉด
์ฐ๋ฆฌ๋ ๋ค์์์ ํธ๋ค๋ฌ๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ์ฌ์ฉํ ๊ฒ์ด์ง๋ง ๋จผ์ ๋ชจ๋ ๊ฒ์ด ์ด๋ป๊ฒ ์๋ํ๋์ง ์ดํดํฉ์๋ค.
๋ท/http
ํจํค์ง๋ฅผ ์ฌ์ฉํ ์ net/http
HTTP ํธ๋ค๋ฌ
์์ฒญ์ ๋ฐ์ผ๋ฉด ํธ๋ค๋ฌ๊ฐ ์ด๋ฅผ ๊ตฌ๋ฌธ ๋ถ์ํ๊ณ ์๋ต์ ์์ฑํฉ๋๋ค. Go์ ํธ๋ค๋ฌ๋ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํ๋ฉ๋๋ค.
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
์ฒซ ๋ฒ์งธ ์์์๋ http.HandleFunc ๋์ฐ๋ฏธ ํจ์๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ด๋ ServeHTTP์์ http.ResponseWriter ๋ฐ http.Request๋ฅผ ์ฐจ๋ก๋ก ์๋ฝํ๋ ๋ ๋ค๋ฅธ ํจ์๋ฅผ ๋ํํฉ๋๋ค.
์ฆ, Golang์ ํธ๋ค๋ฌ๋ ํ๋ก๊ทธ๋๋จธ์๊ฒ ๋ง์ ๊ธฐํ๋ฅผ ์ ๊ณตํ๋ ๋จ์ผ ์ธํฐํ์ด์ค๋ก ํํ๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด ๋ฏธ๋ค์จ์ด๋ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌํ๋๋ฉฐ ์ฌ๊ธฐ์ ServeHTTP๋ ๋จผ์ ์์ ์ ์ํํ ๋ค์ ๋ค๋ฅธ ์ฒ๋ฆฌ๊ธฐ์ ServeHTTP ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค.
์์์ ์ธ๊ธํ๋ฏ์ด ํธ๋ค๋ฌ๋ ๋จ์ํ ์์ฒญ์ ๋ํ ์๋ต์ ๊ตฌ์ฑํฉ๋๋ค. ๊ทธ๋ฌ๋ ํน์ ์๊ฐ์ ์ด๋ค ํธ๋ค๋ฌ๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๊น?
๋ผ์ฐํ ์์ฒญ
์ฌ๋ฐ๋ฅธ ์ ํ์ ํ๋ ค๋ฉด HTTP ๋ฉํฐํ๋ ์๋ฅผ ์ฌ์ฉํ์ญ์์ค. ์ฌ๋ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ muxer ๋๋ ๋ผ์ฐํฐ๋ผ๊ณ ํ์ง๋ง ๋ชจ๋ ๋์ผํฉ๋๋ค. ๋ฉํฐํ๋ ์์ ๊ธฐ๋ฅ์ ์์ฒญ ๊ฒฝ๋ก๋ฅผ ๋ถ์ํ๊ณ ์ ์ ํ ํธ๋ค๋ฌ๋ฅผ ์ ํํ๋ ๊ฒ์ ๋๋ค.
๋ณต์กํ ๋ผ์ฐํ
์ ๋ํ ์ง์์ด ํ์ํ ๊ฒฝ์ฐ ํ์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ๊ฐ์ฅ ์ง๋ณด ๋ ๊ฒ ์ค ํ๋
์ผ๋ฐ์ ์ธ ์ํฉ์์ ๋ณต์กํ ํ๋ ์์ํฌ๋ก ์์ ํ๋ ค๋ฉด ํ์ค ์๋ฃจ์ ์ด ํ์ํ์ง ์์ผ๋ฉฐ ์ด๋ ๊ธฐ๋ณธ ์ฒ๋ฆฌ๊ธฐ ์ฌ์ฉ์ ํฌ๊ฒ ๋ณต์กํ๊ฒ ๋ง๋ญ๋๋ค. ๊ธฐ๋ณธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฐ๋จํ ๋ผ์ฐํฐ์ ์กฐํฉ์ผ๋ก ๋๋ถ๋ถ์ ์์ฉ ํ๋ก๊ทธ๋จ์ ๋ง๋ค ์ ์์ต๋๋ค.
์ฟผ๋ฆฌ ์ฒ๋ฆฌ
๋ํ ๋ค์ด์ค๋ ์ฐ๊ฒฐ์ "์์ "ํ๊ณ ๋ชจ๋ ์์ฒญ์ ์ฌ๋ฐ๋ฅธ ์ฒ๋ฆฌ๊ธฐ๋ก ๋ฆฌ๋๋ ์ ํ๋ ๊ตฌ์ฑ ์์๊ฐ ํ์ํฉ๋๋ค. http.Server๋ ์ด ์์ ์ ์ฝ๊ฒ ๋์ฒํ ์ ์์ต๋๋ค.
๋ค์์ ์๋ฒ๊ฐ ์ฐ๊ฒฐ ์ฒ๋ฆฌ์ ๊ด๋ จ๋ ๋ชจ๋ ์์ ์ ๋ด๋นํจ์ ๋ณด์ฌ์ค๋๋ค. ์๋ฅผ ๋ค์ด ์ด๊ฒ์ TLS ํ๋กํ ์ฝ์์ ์๋ํฉ๋๋ค. ํ์ค HTTP ์๋ฒ๋ http.ListenAndServer ํธ์ถ์ ๊ตฌํํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
์ด์ ์ข ๋ ๋ณต์กํ ์๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
Let's Encrypt ์ถ๊ฐ
๊ธฐ๋ณธ์ ์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ HTTP ํ๋กํ ์ฝ์ ํตํด ์คํ๋์ง๋ง HTTPS ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. Go์์๋ ๋ฌธ์ ์์ด ์ํํ ์ ์์ต๋๋ค. ์ธ์ฆ์์ ๊ฐ์ธ ํค๋ฅผ ๋ฐ์๋ค๋ฉด ์ฌ๋ฐ๋ฅธ ์ธ์ฆ์์ ํค ํ์ผ๋ก ListenAndServeTLS๋ฅผ ์์ฑํ๋ฉด ๋ฉ๋๋ค.
http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
๋น์ ์ ํญ์ ๋ ์ํ ์ ์์ต๋๋ค.
autocert
.
์ค์ ํ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ http.Serve์ ํจ๊ป autocert.NewListener ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค. ์ด ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด HTTP ์๋ฒ๊ฐ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋์ TLS ์ธ์ฆ์๋ฅผ ์์ ํ๊ณ ๊ฐฑ์ ํ ์ ์์ต๋๋ค.
http.Serve(autocert.NewListener("example.com"), nil)
๋ธ๋ผ์ฐ์ ์์ ์ด๋ฉด
๋ ์์ธํ ๊ตฌ์ฑ์ด ํ์ํ ๊ฒฝ์ฐ autocert.Manager๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ์์ฒด http.Server ์ธ์คํด์ค๋ฅผ ๋ง๋ค๊ณ (์ง๊ธ๊น์ง ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉํจ) ๊ด๋ฆฌ์๋ฅผ TLSConfig ์๋ฒ์ ์ถ๊ฐํฉ๋๋ค.
m := &autocert.Manager{
Cache: autocert.DirCache("golang-autocert"),
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("example.org", "www.example.org"),
}
server := &http.Server{
Addr: ":443",
TLSConfig: m.TLSConfig(),
}
server.ListenAndServeTLS("", "")
์ด๊ฒ์ ์๋ ์ธ์ฆ์ ๊ฐฑ์ ์ผ๋ก ์์ ํ HTTPS ์ง์์ ๊ตฌํํ๋ ์ฌ์ด ๋ฐฉ๋ฒ์ ๋๋ค.
์ฌ์ฉ์ ์ง์ ๊ฒฝ๋ก ์ถ๊ฐ
ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํฌํจ๋ ๊ธฐ๋ณธ ๋ผ์ฐํฐ๋ ํ๋ฅญํ์ง๋ง ๋งค์ฐ ๊ธฐ๋ณธ์ ์ ๋๋ค. ๋๋ถ๋ถ์ ์ ํ๋ฆฌ์ผ์ด์ ์๋ ์ค์ฒฉ ๋ฐ ์์ผ๋์นด๋ ๊ฒฝ๋ก ๋๋ ๊ฒฝ๋ก ํจํด ๋ฐ ๋งค๊ฐ๋ณ์ ์ค์ ์ ์ฐจ๋ฅผ ํฌํจํ์ฌ ๋ ๋ณต์กํ ๋ผ์ฐํ ์ด ํ์ํฉ๋๋ค.
์ด ๊ฒฝ์ฐ ํจํค์ง๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
API์ ๋ํ ๊ฒฝ๋ก๊ฐ ํฌํจ๋ api/v1/api.go ํ์ผ์ด ์ ๊ณต๋ฉ๋๋ค.
/ HelloResponse is the JSON representation for a customized message
type HelloResponse struct {
Message string `json:"message"`
}
// HelloName returns a personalized JSON message
func HelloName(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
response := HelloResponse{
Message: fmt.Sprintf("Hello %s!", name),
}
jsonResponse(w, response, http.StatusOK)
}
// NewRouter returns an HTTP handler that implements the routes for the API
func NewRouter() http.Handler {
r := chi.NewRouter()
r.Get("/{name}", HelloName)
return r
}
๊ธฐ๋ณธ ํ์ผ์ ๊ฒฝ๋ก์ ๋ํด api/vq ์ ๋์ฌ๋ฅผ ์ค์ ํฉ๋๋ค.
๊ทธ๋ฐ ๋ค์ ์ด๋ฅผ ๊ธฐ๋ณธ ์ ํ๋ฆฌ์ผ์ด์ ์ api/v1/ ์ ๋์ฌ ์๋์ ์๋ ๊ธฐ๋ณธ ๋ผ์ฐํฐ์ ๋ง์ดํธํ ์ ์์ต๋๋ค.
// NewRouter returns a new HTTP handler that implements the main server routes
func NewRouter() http.Handler {
router := chi.NewRouter()
router.Mount("/api/v1/", v1.NewRouter())
return router
}
http.Serve(autocert.NewListener("example.com"), NewRouter())
Go์์ ๋ณต์กํ ๊ฒฝ๋ก๋ก ์ฝ๊ฒ ์์ ํ ์ ์์ผ๋ฏ๋ก ํฌ๊ณ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์กฐํ ๋ฐ ์ ์ง ๊ด๋ฆฌ๋ฅผ ๋จ์ํํ ์ ์์ต๋๋ค.
๋ฏธ๋ค์จ์ด ์์
์ค๊ฐ ์ฒ๋ฆฌ์ ๊ฒฝ์ฐ ํ๋์ HTTP ํธ๋ค๋ฌ๋ฅผ ๋ค๋ฅธ HTTP ํธ๋ค๋ฌ๋ก ๋ํํ์ฌ ์ธ์ฆ, ์์ถ, ๋ก๊น ๋ฐ ๊ธฐํ ๊ธฐ๋ฅ์ ๋น ๋ฅด๊ฒ ์ํํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด http.Handler ์ธํฐํ์ด์ค๋ฅผ ๊ณ ๋ คํ์ฌ ์๋น์ค ์ฌ์ฉ์๋ฅผ ์ธ์ฆํ๋ ํธ๋ค๋ฌ๋ฅผ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค.
func RequireAuthentication(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !isAuthenticated(r) {
http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
return
}
// Assuming authentication passed, run the original handler
next.ServeHTTP(w, r)
})
}
๋ฏธ๋ค์จ์ด ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์๋ chi์ ๊ฐ์ ํ์ฌ ๋ผ์ฐํฐ๊ฐ ์์ต๋๋ค.
์ ์ ํ์ผ ์์
Go์ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ JavaScript ๋ฐ CSS ํ์ผ๋ฟ๋ง ์๋๋ผ ์ด๋ฏธ์ง๋ฅผ ํฌํจํ ์ ์ ์ฝํ ์ธ ์์ ์ ์ํ ๊ธฐ๋ฅ์ด ํฌํจ๋์ด ์์ต๋๋ค. http.FileServer ๊ธฐ๋ฅ์ ํตํด ์ก์ธ์คํ ์ ์์ต๋๋ค. ํน์ ๋๋ ํฐ๋ฆฌ์์ ํ์ผ์ ๋ฐฐํฌํ๋ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ๋ฐํํฉ๋๋ค.
func NewRouter() http.Handler {
router := chi.NewRouter()
r.Get("/{name}", HelloName)
// ะะฐัััะพะนะบะฐ ัะฐะทะดะฐัะธ ััะฐัะธัะตัะบะธั
ัะฐะนะปะพะฒ
staticPath, _ := filepath.Abs("../../static/")
fs := http.FileServer(http.Dir(staticPath))
router.Handle("/*", fs)
return r
๊ธฐ๋ณธ index.html ํ์ผ์ด ํฌํจ๋์ด ์์ง ์์ ๊ฒฝ์ฐ http.Dir์ ๋๋ ํ ๋ฆฌ์ ๋ด์ฉ์ ํ์ํ๋ค๋ ์ ์ ๊ธฐ์ตํ์ญ์์ค. ์ด ๊ฒฝ์ฐ ๋๋ ํ ๋ฆฌ๊ฐ ์์๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด ํจํค์ง๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. unindexed
์ฌ๋ฐ๋ฅธ ์ข ๋ฃ
Go์๋ HTTP ์๋ฒ์ ์ ์์ ์ธ ์ข ๋ฃ์ ๊ฐ์ ๊ธฐ๋ฅ๋ ์์ต๋๋ค. Shutdown() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ด ์์ ์ ์ํํ ์ ์์ต๋๋ค. ์๋ฒ๋ goroutine์์ ์์๋ ๋ค์ ์ธํฐ๋ฝํธ ์ ํธ๋ฅผ ์์ ํ๊ธฐ ์ํด ์ฑ๋์ ์ฒญ์ทจํฉ๋๋ค. ์ ํธ๋ฅผ ์์ ํ์๋ง์ ์๋ฒ๊ฐ ๊บผ์ง์ง๋ง ์ฆ์๋ ์๋์ง๋ง ๋ช ์ด ํ์ ๊บผ์ง๋๋ค.
handler := server.NewRouter()
srv := &http.Server{
Handler: handler,
}
go func() {
srv.Serve(autocert.NewListener(domains...))
}()
// Wait for an interrupt
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
// Attempt a graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx)
๊ฒฐ๋ก ์ผ๋ก์
Go๋ ๊ฑฐ์ ๋ณดํธ์ ์ธ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ถ ๊ฐ๋ ฅํ ์ธ์ด์ ๋๋ค. ๊ธฐ๋ณธ ๊ธฐ๋ฅ์ ๋งค์ฐ ๊ด๋ฒ์ํ๋ฉฐ ์ธํฐํ์ด์ค์ ๋์์ผ๋ก ๊ฐํํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ์ง์ ์ผ๋ก ์ ๋ขฐํ ์ ์๋ HTTP ์๋ฒ๋ฅผ ๊ฐ๋ฐํ ์ ์์ต๋๋ค.
Skillbox๋ ๋ค์์ ๊ถ์ฅํฉ๋๋ค.
- XNUMX๋ ์ค์ต ์ฝ์ค
"์ ๋ PRO ์น ๊ฐ๋ฐ์์ ๋๋ค" .- ๊ต์ก์ฉ ์จ๋ผ์ธ ๊ณผ์
"์ง์ ์๋ฐ ๊ฐ๋ฐ์" .- ์ค๊ธฐ ์ฝ์ค
"0์์ PRO๋ก์ PHP ๊ฐ๋ฐ์" .
์ถ์ฒ : habr.com