- HTTP 웹서버 만들기
- HTTP 동작원리
- HTTP 쿼리 인수 사용하기
- ServeMux 인스턴스 사용하기
- 파일서버
- 웹서버 테스트 코드 만들기
- JSON데이터 전송
- HTTPS웹서버 만들기
HTTP 웹서버 만들기
- http.HandleFunc() 로 웹핸들러를 등록하고,
- http.ListenAndServe() 웹서버를 시작한다.
// 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)서비스를 이용하는 방식으로 제공한다.
//ch29/ex29.4/ex29.4.go
package main
import "net/http"
func main() {
http.Handle("/", http.FileServer(http.Dir("static"))) // ❶
http.ListenAndServe(":3000", nil)
}
<!--ch29/ex29.4/test.html-->
<html>
<body>
<img src="http://localhost:3000/gopher.jpg"/>
<h1>이것은 Gopher 이미지입니다.</h1>
</body>
</html>
웹서버 테스트 코드 만들기
- 아래소스는 29.3과 거의 같다. MakeWebHandler()함수를 따로 만들어 NewServeMux를 리턴하도록 하였다.
//ch29/ex29.5/ex29.5.go
package main
import (
"fmt"
"net/http"
)
func MakeWebHandler() http.Handler { // ❶ 핸들러 인스턴스를 생성하는 함수
mux := http.NewServeMux()
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")
})
return mux
}
func main() {
http.ListenAndServe(":3000", MakeWebHandler())
}
- 아래의 코드를 웹서버를 테스트하는 코드이다. 매번 웹브라우저로 테스트하는 방법은 번거롭기때문에 아래와 같이 테스트코드를 만들어서, 해당 웹서버가 정상적인지 확인할수있다.
- TestIndexHandler()는 / 경로를 테스트하여 Hello World 가 정상적으로 리턴되었는지 확인한다.
- TestBarHandler()는 /bar 경로를 테스트하여 Hello World 가 정상적으로 리턴되었는지 확인한다.
//ch29/ex29.5/ex29_5_test.go
package main
import (
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestIndexHandler(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/", nil) // ❶ / 경로 테스트
mux := MakeWebHandler() // ❷
mux.ServeHTTP(res, req)
assert.Equal(http.StatusOK, res.Code) // ❸ Code 확인
data, _ := io.ReadAll(res.Body) // ❹ 데이터를 읽어서 확인
assert.Equal("Hello World", string(data))
}
func TestBarHandler(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/bar", nil) // ➎ /bar 경로 테스트
mux := MakeWebHandler()
mux.ServeHTTP(res, req)
assert.Equal(http.StatusOK, res.Code)
data, _ := io.ReadAll(res.Body)
assert.Equal("Hello Bar", string(data))
}
JSON데이터 전송
- http는 HTML문서를 전송하는 프로토콜이지만, HTML뿐만아니라 이미지나 사운드, 기타 다양한 데이타도 전송할수있다. 이번 예제에서는 JSON데이타를 전송하는 방법을 알아보자
- 아래 예제에서는 student라는 구조체를 json.Marshal(student) 를 사용하여 json 데이타로 변경하여, 결과를 전송하는 예제이다.
//ch29/ex29.6/ex29.6.go
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type Student struct {
Name string
Age int
Score int
}
func MakeWebHandler() http.Handler { // ❶ 핸들러 인스턴스를 생성하는 함수
mux := http.NewServeMux()
mux.HandleFunc("/student", StudentHandler)
return mux
}
func StudentHandler(w http.ResponseWriter, r *http.Request) {
var student = Student{"aaa", 16, 87}
data, _ := json.Marshal(student) // ❷ Student 객체를 []byte로 변환
w.Header().Add("content-type", "application/json") // ❸ json 포맷임을 표시
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, string(data)) // ❹ 결과 전송
}
func main() {
http.ListenAndServe(":3000", MakeWebHandler())
}
//ch29/ex29.6/ex26_6_test.go
package main
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestJsonHandler(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/student", nil) // ❶ /student 경로 테스트
mux := MakeWebHandler()
mux.ServeHTTP(res, req)
assert.Equal(http.StatusOK, res.Code)
student := new(Student)
err := json.NewDecoder(res.Body).Decode(student) // ❷ 결과 변환
assert.Nil(err) // ❸ 결과 확인
assert.Equal("aaa", student.Name)
assert.Equal(16, student.Age)
assert.Equal(87, student.Score)
}
HTTPS웹서버 만들기
- 이번에는 HTTPS를 지원하는 웹서버를 만들어보자. Https는 http에 보안기능을 강화한 프로토콜이다.
- 아래의 openssl을 사용하여 인증서를 생성하자.
openssl req -new -newkey rsa:2048 -nodes -keyout localhost.key -out localhost.csr
openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt
- 생성한 인증서를 넣고, 웹서버를 실행시킨다.
//ch29/ex29.7/ex29.7.go
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World") // ❶ 웹 핸들러 등록
})
err := http.ListenAndServeTLS(":3000", "localhost.crt", "localhost.key", nil) // ❷ 웹 서버 시작
if err != nil {
log.Fatal(err)
}
}
'Go' 카테고리의 다른 글
[묘공단] Tucker의 Go 언어 프로그래밍 31장 : Todo 리스트 웹사이트 만들기 (0) | 2024.03.24 |
---|---|
[묘공단] Tucker의 Go 언어 프로그래밍 30장 : RESTful API 서버 만들기 (0) | 2024.03.22 |
[묘공단] Tucker의 Go 언어 프로그래밍 26장 : 단어검색프로그램 (0) | 2024.03.09 |
[묘공단] Tucker의 Go 언어 프로그래밍 24장~25장 (0) | 2024.02.03 |
[묘공단] Tucker의 Go 언어 프로그래밍 22장~23장 (0) | 2024.02.03 |