Tucker의 Go 언어 프로그래밍 책과 유튜브를 통해 학습 중입니다.

HTTP 동작 원리
웹 브라우저에 도메인(ex. https://www.google.com) 을 입력한 뒤 엔터를 누르면,
웹 브라우저는 도메인 네임 시스템(DNS)에 해당 도메인에 해당하는 IP 주소를 요청함.

만약, IP주소의 목적지가 컴퓨터라면 포트 번호는 수신한 데이터를 놓을 컴퓨터 내 창구와 같음.
IP주소는 컴퓨터 자체를, 포트번호는 해당 컴퓨터 내 데이터를 수신할 수 있는 창구를 의미함.
* 컴퓨터 포트는 0 ~ 65535번 포트를 가지고 있음.
포트 없이 https://www.google.com 을 입력하면, 기본 포트 번호로 요청을 전송함.
- http는 80, https는 443
즉, https://www.google.com은 https://www.google.com:443 으로,
http://www.google.com은 http://www.google.com:80으로 요청을 보냄.
https://는 데이터를 보내는 통신 규약으로 HTTPS를 사용하겠다는 것을 나타냄.
(HTTPS는 HTTP에 보안 기능을 추가한 통신 규약)
HTTP는 하이퍼텍스트 전송 규약(HyperText Transfer Protocol)의 약자로 하이퍼텍스트를 전송하는 통신 규약임.
* 하이퍼 텍스트란,
- 하이퍼링크를 포함한 멀티미디어 텍스트로 문자뿐 아니라 그림, 이미지 등의 멀티미디어를 포함하고 다른 문서로 연결되는 링크를 제공하는 문서 포맷
웹에서 하이퍼텍스트 문서를 사용하기 때문에 문자, 이미지, 음악, 동영상 등을 볼 수 있고 링크를 통해 다른 페이지로 연결할 수 있음.
하이퍼텍스트 문서를 만들 수 있는 문서 포맷이 하이퍼텍스트 마크업 언어(HyperText Markup Language)의 약자인 HTML 포맷임.
웹서버란, 특정 포트에서 대기하며 사용자의 HTTP 요청에 HTTP 응답을 전송하는 서버를 말함.
일반적으로 HTML 문서를 전송함.
mux 사용하기
- "/", "/bar" 등의 경로에 따라 다르게 분배하는 것을 라우터/mux라고 부름.
- 멀티플렉서(Multiplexer)의 약자로 여러 입력 중 하나를 선택해서 반환하는 디지털 장치를 의미.
- 기존과 다른 점은 mux라는 인스턴스를 만들어서 거기에 등록하여 인스턴스를 넘겨주는 방식으로 바꿈.
* 웹 서버는 각 URL에 해당하는 핸들러들을 등록한 다음 HTTP 요청이 왔을 때 URL에 해당하는 핸들러를 선택해서 실행하는 방식.
* 핸들러를 선택하고 실행하는 구조체 이름이 Mux를 제공한다고 하여, ServeMux라고 부름.
package main
import (
"fmt"
"net/http"
)
func barHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello Bar!")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
})
mux.HandleFunc("/bar", barHandler)
http.ListenAndServe(":3000", mux)
}
→ 결과는 동일하게 웹에서 확인할 수 있음!


func barHandler(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "World"
}
fmt.Fprintf(w, "Hello %s!", name)
}
- URL에서 name이라는 아큐먼트를 뽑아내서 그 값을 Hello Name이 출력되게하고,
만약 Name이 없다면, Hello World를 출력되게 만드는 코드
- URL 뒤에 ?를 붙여 쿼리 인수가 시작됨을 표시하고, 각 인수는 key=value 형태로 입력함.
ex) http://localhost:3000/bar?name=<value>
2개 이상의 인수를 사용할 때는 &를 사용해 인수들을 연결함.
→ 출력 결과를 확인해보면, 아래와 같이 name 값을 가지고 출력하는 것을 확인할 수 있음.


JSon 데이터 전송
- JSON은 자바스크립트 오브젝트 표기법(JavaScript Object Notaion)의 약자로 자바스크립트에서 오브젝트를 표현하는 방법으로 사용되는 포맷
* JSON 표기 규칙
- 오브젝트 시작은 {로 표기하고 }로 종료
- 각 필드는 "key":value 형태로 표기
- 각 필드는 ,로 구분
- 배열은 []로 표기
- 문자열은 ""로 묶어서 표기
{
"Name":"훈이",
"Age":16,
"Score":90
}
type User struct {
FirstName string
LastName string
Email string
CreatedAt time.Time
}
type fooHandler struct{}
// ServeHTTP implements http.Handler.
func (f *fooHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
user := new(User)
err := json.NewDecoder(r.Body).Decode(user)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, "Bad Requset: ", err)
return
}
user.CreatedAt = time.Now()
data, _ := json.Marshal(user)
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, string(data))
}
- Json 데이터를 담을 struct인 User struct를 새로 만들어줌.
- *fooHandler의 Request에 Json이 오기 때문에 그것을 읽어야해서 (user struct의 값) 값을 채워줄 인스턴스를 생성
- 해당 값을 Json 형태로 파싱을 해야해서, json형태의 NewDecoder로 Request의 바디에 있는 Json를 reader로 받음.
바디에서 데이터를 읽어서 user struct의 형태로 Decode함(decode해서 값을 채워줌).
user형태의 Json 파일 형태가 아닌 경우에 error를 뱉음.
- err가 nil이 아닌 경우 Header로 Bad Request를 보내고, Response로 Bad Request와 error를 뱉어줌.
- user의 CreateAt을 현재 시간으로 변경함.
- 바뀐 User 값을 다시 Json으로 리턴하기 위해, Marshal을 사용하여 Json 형태로 encoding을 해줌.



→ http://localhost:3000/foo를 호출하면 Hello foo!가 아닌 Bad Request EOF(End Of File)이 나오게 됨.
- 데이터가 없다는 의미로, 데이터를 body에 넣어서 보내야 함.
(강의에서는 크롬 앱인 Advanced REST client를 사용하셨는데, 25/1/3 기준으로 해당 앱은 사용할 수 없다고 나옴.)

웹서버 생성 코드
package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
type User struct {
FirstName string `json:"First_name"`
LastName string `json:"Last_name"`
Email string `json:"email"`
CreatedAt time.Time `json:"Created_at"`
}
func MakeWebHandler() http.Handler { // 핸들러 인스턴스를 생성하는 함수
mux := http.NewServeMux()
mux.HandleFunc("/user", UserHandler)
return mux
}
func UserHandler(w http.ResponseWriter, r *http.Request) {
var user = User{"Hello", "World", "helloworld@gmail.com", time.Time{}}
data, _ := json.Marshal(user) // User 객체를 []byte로 변환
w.Header().Add("content-type", "application/json") // Json 포맷임을 표시
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, string(data)) // 결과 전송
}
func main() {
http.ListenAndServe(":3000", MakeWebHandler())
}
테스트 실행 코드
package main
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestJsonHandler(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/user", nil) // /user 경로 테스트
mux := MakeWebHandler()
mux.ServeHTTP(res, req)
assert.Equal(http.StatusOK, res.Code)
user := new(User)
err := json.NewDecoder(res.Body).Decode(user) // 결과 반환
assert.Nil(err) // 결과 확인
assert.Equal("Hello", user.FirstName)
assert.Equal("World", user.LastName)
assert.Equal("helloworld@gmail.com", user.Email)
assert.Equal(time.Time{}, user.CreatedAt)
}

'Language > Golang' 카테고리의 다른 글
| Golang (Go언어) Go로 만드는 웹 (4) (1) | 2025.01.08 |
|---|---|
| Golang (Go언어) Go로 만드는 웹 (3) (0) | 2025.01.07 |
| Golang (Go언어) Go로 만드는 웹 (1) (0) | 2025.01.02 |
| Golang (Go언어) 제네릭 프로그래밍(Generic Programming) 3/3 (0) | 2025.01.01 |
| Golang (Go언어) 제네릭 프로그래밍(Generic Programming) 2/3 (1) | 2025.01.01 |