[Go] HTTP Server 만들기
http 패키지
는 HTTP 클라이언트 및 서버를 구현할 때 사용합니다.
HTTP server를 만드는 방법은 4가지가 있습니다.
- No request parsing
- Manual request parsing
- Multiplexer
- Global multiplexer
크게 Multiplexer
를 사용하느냐(3, 4) 안하느냐(1, 2)로 두 가지로 나뉩니다.
1. No request parsing
가장 기본적인 구현 방법 입니다.
어떤 요청이 와도 동일한 응답만 하는 서버입니다.
package main
import (
"fmt"
"net/http"
)
type database map[string]string
func (d database) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for country, greeting := range d {
fmt.Fprintf(w, "%s: %s\n", country, greeting)
}
}
func main() {
db := database{
"kor": "안녕!",
"eng": "Hello!",
}
http.ListenAndServe("localhost:8000", db)
}
요청을 받으면 인사말을 출력하는 서버입니다.
ListenAndServer
함수는 TCP 네트워크 주소(addr)에서 수신 대기(Listen)한 다음 통신 요청들을 받아서 Serve 함수를 통해 handler 함수를 실행시킵니다.
func ListenAndServe(addr string, handler Handler) error
두 번째 인자인 Handler
타입을 확인하면 아래와 같습니다.
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
Handler
는 HTTP 요청에 대한 응답을 해줍니다.
Handler
는 ServerHTTP
를 가지고 있는 interface
입니다. 따라서 위 코드에서 ServeHTTP 함수를 가지고 있는 database를 ListenAndServe의 두 번째 인자로 넣습니다.
위에 코드는 어떤 요청에도 관계없이 항상 동일한 응답을 제공합니다.
예)
- http://localhost:8000/
- http://localhost:8000/abc
- http://localhost:8000/kor
![](https://blog.kakaocdn.net/dn/bpNE2G/btqN6Urp753/vqeTwTuEkyqI7kfuOiaHmk/img.png)
2. Manual request parsing
이번에는 ServeHTTP 함수 안에서 요청되는 경로를 구문 분석하도록 메서드를 수정하겠습니다.
package main
import (
"fmt"
"net/http"
)
type database map[string]string
func (d database) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/foo":
fmt.Fprintf(w, "kor: %s\n", d["kor"])
case "/bar":
fmt.Fprintf(w, "eng: %s\n", d["eng"])
default:
http.NotFound(w,r)
}
}
func main() {
db := database{
"kor": "안녕!",
"eng": "Hello!",
}
http.ListenAndServe("localhost:8000", db)
}
switch
문을 사용하여 request 경로 별로 다르게 출력되도록 수정하였습니다.
case에 없는 경로 라면 NotFound
를 호출하여 "404 not found error"를 출력합니다.
NotFound
함수는 "404 not found error"로 요청에 대한 응답을 하는 함수입니다.
func NotFound(w ResponseWriter, r *Request)
요청 결과를 확인해 보겠습니다.
![]() |
![]() |
![](https://blog.kakaocdn.net/dn/ck2MMz/btqN5eRjoRJ/MTeWbrw3AYBaFSNj9E0fYk/img.png)
3. Multiplexer
두 번째 방식은 케이스가 증가하면 할수록 swich 문은 복잡해지고 관리가 어렵게 됩니다.
이번에는 ServerHTTP
메서드 대신 ServeMux
를 사용하는 방법에 대해 알아보도록 하겠습니다.
ServeMux
는 HTTP 요청 멀티플렉서입니다. 패턴에 맞는 핸들러를 등록하고 패턴과 맞는 요청이 오면 패턴에 대한 핸들러를 호출하게 됩니다.
package main
import (
"fmt"
"net/http"
)
type database map[string]string
func (d database) kor (w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "kor: %s\n", d["kor"])
}
func (d database) eng (w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "eng: %s\n", d["eng"])
}
func main() {
db := database{
"kor": "안녕!",
"eng": "Hello!",
}
mux := http.NewServeMux()
mux.Handle("/kor", http.HandlerFunc(db.kor))
mux.HandleFunc("/eng", db.eng)
http.ListenAndServe("localhost:8000", mux)
}
NewServeMux
함수를 호출하여 새로운 ServeMux
를 할당받습니다.
mux := http.NewServeMux()
주어진 패턴에 대한 핸들러를 등록합니다.
mux.Handle("/kor", http.HandlerFunc(db.kor))
mux.HandleFunc("/eng", db.eng)
HandlerFunc
은 일반 함수를 HTTP 핸들러로 사용할 수 있는 어댑터 역할을 합니다.
type HandlerFunc func(ResponseWriter, *Request)
Handle
은 주어진 패턴에 대한 핸들러를 등록하는 함수입니다.
func Handle(pattern string, handler Handler)
Handle
함수 두 번째 인자에 Handler를 넣어야 하기 때문에 일반 함수를 넣기 위해선 HandlerFunc type
을 사용하여 Handler로 변환해야 합니다.
mux.Handle("/kor", http.HandlerFunc(db.kor))
HandleFunc
은 주어진 패턴에 대한 핸들러 함수를 등록하는 함수입니다.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
HandleFunc
함수 두 번째 인자는 변환 없이 함수를 직접 등록하면 됩니다.
mux.HandleFunc("/eng", db.eng)
4. Global multiplexer
일반적으로 코드는 별도의 패키지로 분할됩니다.
라우팅 핸들러를 설정하려면 ServeMux
인스턴스를 각 패키지로 전달해야 하는 번거로움이 있습니다.
Global multiplexer
인 DefaultServeMux
인스턴스를 사용하면 간단하게 사용할 수 있습니다.
package main
import (
"fmt"
"net/http"
)
type database map[string]string
func (d database) kor (w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "kor: %s\n", d["kor"])
}
func (d database) eng (w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "eng: %s\n", d["eng"])
}
func main() {
db := database{
"kor": "안녕!",
"eng": "Hello!",
}
http.Handle("/kor", http.HandlerFunc(db.kor))
http.HandleFunc("/eng", db.eng)
http.ListenAndServe("localhost:8000", nil)
}
ListenAndServe
함수 두 번째 인자에 nil
을 넣어주면 DefaultServeMux
를 사용하게 됩니다.
http.ListenAndServe("localhost:8000", nil)
Handle
및 HandleFunc
함수를 사용하여 DefaultServeMux
에 핸들러를 추가하면 됩니다.
http.Handle("/kor", http.HandlerFunc(db.kor))
http.HandleFunc("/eng", db.eng)
참고: https://www.integralist.co.uk/posts/understanding-golangs-func-type/