•
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
복사