robert-min / handson-go

Go-lang hands on guide
0 stars 0 forks source link

Chapter5. HTTP Server - Basic, mux handler, Request with Context #14

Open robert-min opened 1 year ago

robert-min commented 1 year ago

Basic HTTP server

robert-min commented 1 year ago

Basic HTTP server 구현


func main() {
    listenAddr := os.Getenv("LISTEN_ADDR")
    if len(listenAddr) == 0 {
        listenAddr = ":8080"
    }
    log.Fatal(http.ListenAndServe(listenAddr, nil))
}
robert-min commented 1 year ago

Handler 함수 설정

robert-min commented 1 year ago

Handler 함수 설정 코드


func apiHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, world!")
}

func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "ok")
}

func setupHandlers(mux *http.ServeMux) {
    mux.HandleFunc("/api", apiHandler)
    mux.HandleFunc("/healthz", healthCheckHandler)
}

func main() {
    listenAddr := os.Getenv("LISTEN_ADDR")
    if len(listenAddr) == 0 {
        listenAddr = ":8080"
    }

    mux := http.NewServeMux()
    setupHandlers(mux)

    log.Fatal(http.ListenAndServe(listenAddr, mux))
}
robert-min commented 1 year ago

HTTP 함수 설정 테스트 코드

package main

import (
    "io"
    "log"
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestServer(t *testing.T) {
    tests := []struct {
        name     string
        path     string
        expected string
    }{
        {
            name:     "index",
            path:     "/api",
            expected: "Hello, world!",
        },
        {
            name:     "healthcheck",
            path:     "/healthz",
            expected: "ok",
        },
    }
    mux := http.NewServeMux()
    setupHandlers(mux)

    ts := httptest.NewServer(mux)
    // Shut down the server before the test function exits
    defer ts.Close()

    for _, tc := range tests {
        t.Run(tc.name, func(t *testing.T) {
            resp, err := http.Get(ts.URL + tc.path)
            respBody, err := io.ReadAll(resp.Body)
            resp.Body.Close()
            if err != nil {
                log.Fatal(err)
            }
            if string(respBody) != tc.expected {
                t.Errorf(
                    "Expected: %s, Got: %s",
                    tc.expected, string(respBody),
                )
            }
        })
    }

}
robert-min commented 1 year ago

Request 객체에 메타데이터 부착

robert-min commented 1 year ago

Request 객체에 메타데이터(고유한 아이디 값) 부착 코드


type requestContextKey struct{} // key경우 string과 같은 기본 데이터 타입이 안되기 때문에 공백의 구조체로 정의
type requestContextValue struct {
    requestID string
}

// Add Request ID
func addRequestID(r *http.Request, requestID string) *http.Request {
    c := requestContextValue{
        requestID: requestID,
    }
    currentCtx := r.Context()
    // WithValue(저장될 값의 콘텍스트를 식별하기 위한 부모 Context 객체, 맵 자료구조에서 데이터 키값을 식별하기 위한 interface 객체,
    // 데이터 자체의 interface{} 객체)
    newCtx := context.WithValue(currentCtx, requestContextKey{}, c)
    return r.WithContext(newCtx)
}

func logRequest(r *http.Request) {
    ctx := r.Context()
    v := ctx.Value(requestContextKey{})

    if m, ok := v.(requestContextValue); ok {
        log.Printf("Processing request: %s", m.requestID)
    }
}

func processRequest(w http.ResponseWriter, r *http.Request) {
    logRequest(r)
    fmt.Fprintf(w, "Request processed")
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
    requestID := "request-123-abc"
    r = addRequestID(r, requestID)
    processRequest(w, r)
}

func main() {
    listenAddr := os.Getenv("LISTEN_ADDR")
    if len(listenAddr) == 0 {
        listenAddr = ":8080"
    }

    mux := http.NewServeMux()
    mux.HandleFunc("/api", apiHandler)

    log.Fatal(http.ListenAndServe(listenAddr, mux))
}