[ 1. HTTP 통신 기본 구조 : HandleFunc]
func main() {
http.HandleFunc("/", helloWorld)
}
func helloWorld(w http.ResponseWriter, r *http.Request) {
fmt.Println("Hello World")
}
기본적으로 main 함수에서 http.Handlefunc 메소드를 호출하게 되면,
- addr과 실행할 함수(=/= method)
- 반드시 실행한 함수는 http.ResponseWriter와 *http.Request를 가져야 함
⇒ 현재 상태로는 서버가 실행되자마자 종료
func main() {
http.HandleFunc("/", helloWorld)
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Println(err)
panic(err)
}
}
http.ListenAndServe를 통해 특정 포트를 열어 둠 (서버 유지)
- ListenAndServe의 retrun이 nil이 아닐 때 ⇒ 에러 발생으로 판단
- nil은 에러가 없음을 의미하는 error type과 비교할 수 있는 유일 값
govar err error = nil // nil은 error타입 if err != nil { ... } if err != otherErrorType { ... }
- panic ⇒ 강제 종료
⇒ 아래에서 ListenAndServe추가 설명
[ 2. Mux vs DispatcherServlet ]
HTTP 요청 처리 방식 in JAVA
- 모든 HTTP 요청을 dispatcherServlet에서 확인
- HandlerMapping+@RequestMapping을 통해 어떤 Controller + 메서드가 처리할지 결정
- 요청 처리
⇒ 즉 모든 요청을 하나의 enterance로 받아서, mapping 방식으로 처리 (= 중앙 처리 시스템)
HTTP 요청 처리 방식 in GO
- Listen과 동시에 해당 요청을 처리할 mux 선택 (자바에서는 스프링 내부에서 자동 선택)
- 선택된 mux가 핸들러를 찾고
- 핸들러에서 serveHTTP를 실행함으로써 연결된 함수 실행
⇒ 중앙에서 매핑해주는게 아니라, 내가 원하는 mux를 선택하고, 그에 연결된 핸들러로 특정 함수 실행
실제 전체 코드 (in custom Mux)
mux := http.NewServeMux()
mux.HandleFunc("/about", aboutPage)
http.ListenAndServe(":8080", mux)
세부 동작
- ListenAndServe를 통해 특정 포트를 열고, 해당 포트를 처리하는 라우터(mux)를 선택
- nil이면, http.DefaultServeMux라는 기본 라우터
- 라우터(핸들러)는 경로에 따른 핸들러 매핑 정보를 소유go
map[string]Handler{ "/hello": helloHandler, "/about": aboutHandler, }
- 내부 동작 : HandleFunc의 매개변수로 주어진 함수를 http.HandlerFunc로 래핑 (HandleFunc의 역할)
- 내부 래핑 과정go
http.HandleFunc("/", helloWorld) 혹은 mux.HandleFunc("/about", aboutPage) ↓ 내부에서 http.DefaultServeMux.Handle("/", http.HandlerFunc(helloWorld)) mux.Handle("/about", http.HandlerFunc(aboutPage))
- 내부 래핑 과정
- 원하는 핸들러를 찾은 이후 .ServeHTTP(w, r) 호출
http.HandlerFunc(helloWorld).ServeHTTP(w, r)
= 함수 실행
gomux.ServeHTTP(w, r) // (ServeMux가 호출됨) → 핸들러를 찾음: http.HandlerFunc(helloWorld) → ServeHTTP(w, r) 실행 → 내부에서 helloWorld(w, r) 호출
⇒ mux의 ServeHTTP : 핸들러를 찾음
⇒ 핸들러의 ServeHTTP : 내부적으로 특정 함수 실행