// ch29/ex29.1/ex29.1.go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Method : ", r.Method, "\n")
fmt.Fprint(w, "Path: ", r.URL.Path, "\n")
fmt.Fprint(w, "RawQAuery: ", r.URL.RawQuery, "\n")
fmt.Fprint(w, "/") // ❶ 웹 핸들러 등록
})
http.HandleFunc("/sub", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "/sub Method : ", r.Method, "\n")
fmt.Fprint(w, "/sub Path: ", r.URL.Path, "\n")
fmt.Fprint(w, "/sub RawQAuery: ", r.URL.RawQuery, "\n")
fmt.Fprint(w, "/sub") // ❶ 웹 핸들러 등록
})
http.ListenAndServe(":3000", nil) // ❷ 웹 서버 시작
}
HTTP 동작원리
http 의 기본포트는 80번이고, https의 기본포트는 443번이다. http의 특징은 아래와 같다.
무상태(Stateless) : HTTP는 상태를 유지하지 않는 무상태 프로토콜이다. 즉, 각각의 요청은 서로 독립적으로 처리되며 이전 요청과 관련이 없다. 이를 통해 서버는 각각의 요청을 독립적으로 처리할 수 있고, 확장성을 높일 수 있다.
요청-응답 모델(Request-Response Model): HTTP는 클라이언트가 서버에 요청을 보내고, 서버가 그 요청에 대한 응답을 반환하는 요청-응답 모델을 사용한다. 이를 통해 클라이언트와 서버 간의 효율적인 통신이 가능하다.
텍스트 기반 프로토콜: HTTP는 텍스트를 기반으로 한 프로토콜로, 요청과 응답은 텍스트 형식으로 전송된다. 이는 사람이 읽고 이해하기 쉽고, 디버깅 및 개발 과정에서 유용하다.
메서드(Method): HTTP는 다양한 메서드를 제공하여 서버에 요청을 보낼 수 있다. 가장 일반적인 메서드로는 GET(리소스를 가져오기 위한 요청), POST(리소스를 생성하기 위한 요청), PUT(리소스를 갱신하기 위한 요청), DELETE(리소스를 삭제하기 위한 요청) 등이 있다.
상태 코드(Status Code): HTTP 응답은 상태 코드와 함께 반환됩니다. 상태 코드는 요청이 성공했는지, 실패했는지 등을 나타내는 코드이다. 예를 들어, 200은 성공, 404는 찾을 수 없음, 500은 서버 내부 오류 등을 나타낸다.
HTTP는 TCP/IP 프로토콜 스택 위에서 동작하는 프로토콜로, 웹 서버와 웹 클라이언트 간의 데이터 전송을 담당합니다
IP (Internet Protocol)
IP는 인터넷에서 데이터를 주고받을 때 사용되는 프로토콜이다.
IP는 각 장치에 고유한 IP 주소를 할당하여 데이터를 전달한다. IP 주소는 IPv4에서는 32비트 주소 체계를 사용하고, IPv6에서는 128비트 주소 체계를 사용한다.
IP는 데이터 패킷을 라우팅하는 역할을 한다. 데이터 패킷은 출발지와 목적지 IP 주소를 포함하고, 이를 기반으로 네트워크 상에서 경로를 찾아 목적지까지 전달된다.
TCP (Transmission Control Protocol)
TCP는 IP 위에서 동작하는 프로토콜로, 신뢰성 있는 데이터 전송을 담당합니다.
TCP는 연결 지향형 프로토콜로, 데이터의 전송을 위해 세션을 설정하고 종료하는 과정을 포함합니다. 이를 통해 데이터의 손실이나 오류를 최소화하고 데이터의 순서를 보장합니다.
TCP는 데이터의 분할 및 재조립, 흐름 제어, 혼잡 제어 등의 기능을 제공하여 안정적이고 효율적인 데이터 전송을 보장합니다.
HTTP 쿼리 인수 사용하기
//ch29/ex29.2/ex29.2.go
package main
import (
"fmt"
"net/http"
"strconv"
)
func barHandler(w http.ResponseWriter, r *http.Request) {
values := r.URL.Query() // ❶ 쿼리 인수 가져오기
name := values.Get("name") // ❷ 특정 키값이 있는지 확인
if name == "" {
name = "World"
}
id, _ := strconv.Atoi(values.Get("id")) // ❸ id값을 가져와서 int타입 변환
fmt.Fprintf(w, "Hello %s! id:%d", name, id)
}
func main() {
http.HandleFunc("/bar", barHandler) // ❹ "/bar" 핸들러 등록
http.ListenAndServe(":3000", nil)
}
ServeMux 인스턴스 사용하기
위의 예제에서는 ListenAndServe()의 두번째 인자로 nil을 넣어서, DefaultServeMux를 사용하였는데, 이번에는 직접 ServeMux를 만들어서 사용해보자.
아래 예제에서는 http.NewServeMux() 를 사용하여 새로운 인스턴스를 생성하고, 각각의 handleFunc를 등록하였다.
//ch29/ex29.3/ex29.3.go
package main
import (
"fmt"
"net/http"
)
func main() {
mux := http.NewServeMux() // ❶ ServeMux 인스턴스 생성
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World") // ❷ 인스턴스에 핸들러 등록
})
mux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello Bar")
})
http.ListenAndServe(":3000", mux) // ❸ mux 인스턴스 사용
}
파일서버
http.Handle()에서 http.FileServer()를 사용하여 파일서버를 만들수있다.
아래의 소스를 실행하고, test.html을 웹브라우저에서 실행해보자, 아래와 같인 결과를 볼수있다.
실제 웹서비스에서는 CDN(content delivery network)서비스를 이용하는 방식으로 제공한다.