///
Search

01_Introduce Microservices

Go에서는 net/http로 간단한 웹 서버 만들수 있다.
http 프로토콜을 사용해 다른 서버에 요청 보낼수 있다.
소스코드 (파일 이름을 main.go 라고 가정)
package main import ( "fmt" "log" "net/http" //net/http ) func main() { port := 8080 http.HandleFunc("/helloworld", helloWorldHandler) log.Printf("Server starting on port %v\n", port) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) } func helloWorldHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello World\n") }
Go
복사
소스코드를 집어넣고 cmd에 아래의 명령어를 입력한다.
go run main.go
Go
복사
실행하는 화면 즉 웹상에서 확인하려면 인터넷 주소창에 다음과 같은 주소를 입력한다.
localhost 환경 (즉 본인 컴퓨터에서 실습할때)
localhost:8080/helloworld
Go
복사
필자는 ssh 접속으로 실습해서 이렇게 작성을 해야한다.
http://127.0.0.1:8080/helloworld
Go
복사
이 예제는 HelloWorld를 보여주기 위한 작업이다.
막 실습을 여러번 하면서 똑같은 포트 즉 여기서 쓰인 8000번을 또 쓰면 분명 에러가 나온다.
끄고 다시 실습 하거나 아니면 다른 포트를 써준다.
에러 문구
error : bind: address already in use exit status1
Go
복사
에러 해결 방법
(base) glory@Gloryui-MacBookPro reading_writing_json_2 % go run reading_writing_json_2.go 2021/01/17 15:51:28 Server starting on port 8080 2021/01/17 15:51:28 listen tcp :8080: bind: address already in use exit status 1 (base) glory@Gloryui-MacBookPro reading_writing_json_2 % sudo lsof -i:8080 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME Google 967 glory 29u IPv4 0xc61c10000000025d 0t0 TCP localhost:5000000000tp-alt (ESTABLISHED) reading_w 2858 glory 3u IPv6 0xc61c1000000000d5 0t0 TCP *:htt000000000 reading_w 2858 glory 7u IPv6 0xc61c1000000000f5 0t0 TCP localhos0000000000 (ESTABLISHED) reading_w 2858 glory 8u IPv6 0xc61c100000000075 0t0 TCP localhost:http-a000000000 (CLOSE_WAIT) (base) glory@Gloryui-MacBookPro reading_writing_json_2 % sudo kill -9 967 (base) glory@Gloryui-MacBookPro reading_writing_json_2 % sudo kill -9 2858 (base) glory@Gloryui-MacBookPro reading_writing_json_2 % go run reading_writing_json_2.go 2021/01/17 15:52:27 Server starting on port 8080
Go
복사

Go 구조체를 JSON으로 마샬링하기

내장 표준 라이브러리인 encoding/json 패키지를 이용하면 인코딩 디코딩 작업을 쉽게 쓴다.
Go에서도 Java랑 비슷한 마샬 함수랑 언마샬 함수가 있다.
Marshal : 프로그램상의 데이터 구조를 바이너리로 표현하는 직렬화 과정
UnMarshal: 바이너리로 표현된 데이터를 프로그램상의 데이터 구조로 역직렬화 과정
JSON 데이터를 인코딩하기 위해 encoding/json 패키지는 마샬링 함수를 제공한다.
Go언어는 런타임 예외를 처리하고 호출한 함수(Calling function)쪽으로 상세한 에러 객체를 리턴한다.
이러한 방식은 Marshal에서 잘 구현되어있다.
아래는 main2.go 파일내용이다.
package main import ( "encoding/json" /*내장 표준 라이브러리인 "encoding/json" 사용 방법*/ /*인코딩 및 디코딩 Go <-> json 작업 수행*/ "fmt" "log" "net/http" ) type helloWorldResponse struct {//구조체 Message string } func main() { port := 8080 http.HandleFunc("/helloworld", helloWorldHandler) log.Printf("Server starting on port %v\n", port) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) } func helloWorldHandler(w http.ResponseWriter, r *http.Request) { response := helloWorldResponse{Message: "Hello World"} data, err := json.Marshal(response) if err != nil { panic("Ooops") } fmt.Fprint(w, string(data)) }
Go
복사
위의 코드에서 보면 Marshal 함수가 런타임 패닉으로 인해 주어진 객체에 대해 JSON으로 인코딩된 바이트 배열을 생성할 수 없는 경우 이를 포착해 문제를 자세히 설명하는 에러 객체를 호출한 함수로 리턴한다.
즉 런타임 패닉 걸려서 JSON으로 인코딩이 안되면 자세히 설명해준다.
(중요)Go에서는 구조체 필드 태그를 사용하면 출력이 어떻게 표시되는지 더 잘 제어할 수 있다.
main3.go 파일
package main import ( "encoding/json" "fmt" "log" "net/http" ) type helloWorldResponse struct { Message string `json:"message"`//구조체 필드 태그 붙인것임 } func main() { port := 7080 http.HandleFunc("/helloworld", helloWorldHandler) log.Printf("Server starting on port %v\n", port) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) } func helloWorldHandler(w http.ResponseWriter, r *http.Request) { response := helloWorldResponse{Message: "HelloWorld"} data, err := json.Marshal(response) if err != nil { panic("Ooops") } fmt.Fprint(w, string(data)) }
Go
복사
위의 코드에서 이 부분을 중심적으로 보면
type helloWorldResponse struct { Message string `json:"message"`//구조체 필드 태그 붙인것임 }
Go
복사
이부분에서 필드 태그를 사용해 출력을 더 많이 제어할 수 있다는 점이다.
진짜 놀라운 사실은 객체 타입을 변환할 수도 있고 필요하다면 필드를 모두 무시할 수도 있다.
type helloWorldResponse struct { //출력 필드를 message로 바꾼다. Message string `json:"message"`//구조체 필드 태그 붙인것임 //이 필드를 출력하지 않는다. Author string `json:"-"` //값이 비어있으면 필드를 출력하지 않는다. Date string `json:",omitempty"` //출력을 문자열로 변환하고 이름을 "id"로 바꾼다. ID int `json:"id,string"` }
Go
복사
채널, 복소수 및 함수는 JSON으로 인코딩 할 수 없다.
Go에서 구조체를 바이트 배열로 디코딩한 다음 이를 응답 스트림에 쓸 수도 있는데, 이는 효율적이지 못하고, 직접 스트림에 쓸 수 있는 인코더 및 디코더를 제공한다.
Marshal의 결과를 바이트 배열에 저장하는 대신 HTTP응답에 바로 쓸 수도 있다.
전체 main3.go 코드
package main import ( "encoding/json" "fmt" "log" "net/http" ) type helloWorldResponse struct { Message string `json:"message"`//구조체 필드 태그 붙인것임 } func main() { port := 7080 http.HandleFunc("/helloworld", helloWorldHandler) log.Printf("Server starting on port %v\n", port) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) } func helloWorldHandler(w http.ResponseWriter, r *http.Request) { response := helloWorldResponse{Message: "HelloWorld"} data, err := json.Marshal(response) if err != nil { panic("Ooops") } fmt.Fprint(w, string(data)) }
Go
복사
방식 main3.go 코드 일부분
func helloWorldHandler(w http.ResponseWriter, r *http.Request) { response := helloWorldResponse{Message: "HelloWorld"} data, err := json.Marshal(response) if err != nil { panic("Ooops") } fmt.Fprint(w, string(data)) }
Go
복사
바이트 배열로 마샬링하는 것 보다 Encoder를 사용하는 것이 더 빠르다.
이를 벤치마킹으로 표현할 수도 있다.
벤치마킹용 코드 main3_bench.go
// GO 벤치마킹 package main import ( "encoding/json" "fmt" "io/ioutil" "testing" ) type Response struct { Message string } func BenchmarkHelloHandlerVariable(b *testing.B) { b.ResetTimer() var writer = ioutil.Discard response := Response{Message: "Hello World"} for i := 0; i < b.N; i++ { data, _ := json.Marshal(response) fmt.Fprint(writer, string(data)) } } func BenchmarkHelloHandlerEncoder(b *testing.B) { b.ResetTimer() var writer = ioutil.Discard response := Response{Message: "Hello World"} for i := 0; i < b.N; i++ { encoder := json.NewEncoder(writer) encoder.Encode(response) } } func BenchmarkHelloHandlerEncoderReference(b *testing.B) { b.ResetTimer() var writer = ioutil.Discard response := Response{Message: "Hello World"} for i := 0; i < b.N; i++ { encoder := json.NewEncoder(writer) encoder.Encode(&response) } }
Go
복사
명령어
go test -v -run="none" -bench=. -benchtime="5s" -benchmem
Go
복사

JSON을 구조체로 언마샬링하기