CafeM0ca

[Go] JSON encode/decode 본문

Programming/Go

[Go] JSON encode/decode

M0ca 2021. 1. 9. 19:49
반응형

아래 내용은 'Go 언어를 활용한 마이크로서비스 개발'을 이해하기 위해 정리한 내용입니다.
책 구매는 여기를 참조하세요. -> http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9791161751900&orderClick=LAG&Kc=

Go 언어에서 에러처리

go에서 에러는 꼭 처리해주는게 좋다. 다른 언어처럼 예외처리와 유사한 패닉과 복구를 지원하지만, 사용하는 상황이 다름

go에서 panic 함수가 프로그램의 정상 실행을 중지시키고 Go의 모든 루틴이 defer된 함수 호출을 실행하고 나면, 로그 메시지를 출력하면서 프로그램을 종료한다. 이 방식은 일반적으로 코드상의 버그를 나타내는 unexpected error를 처리하는데 사용함

잘 작성된 go 코드는 런타임 예외를 제외하고 함수를 호출한 caller 쪽에서 에러 객체를 처리한다.

encoding/json 패키지의 Marshal 함수는 위 방식으로 구현되어 있다.

func Marshal(v interface{}) ([]byte, error)

go에서는 소문자로 된 프로퍼티는 export 할 수 없다.

type helloWorldResponse struct {
  Message string // ok
  // message string // can't not export
}


func helloWorldHandler(w http.ResponseWriter, r *http.Request) {
  response := helloWorldResponse{Message: "Hello World"}  // here is ok. 
  // result is {"Message":"Hello World"}

  //  response := helloWorldResponse{message: "Hello World"}  // here isn't ok. result is {}
    data, err := json.Marshal(response)
    if err != nil {
        panic("Oppps")
    }
    fmt.Fprint(w, string(data))
}

위 코드에서 출력 결과를 보면 대문자 프로퍼티로 할 경우 잘 출력 되지만, 소문자 프로퍼티로 할 경우 go에서 export가 불가능 하기 때문에 출력 결과가 없다.

소문자로 출력하고 싶으면 encoding/json에서 구조체 필드의 속성을 구현하고 있으니 아래 방식을 사용하면 된다.

type helloWorldResponse struct {
  Message string `json:message` // Message를 message로 바꿔줌
}

위 방식을 통해 출력 방식을 제어할 수 있다.

type helloWorldResponse struct {
    Message string `json:"message"`
    Author  string `json:"-"`          // 출력하지 않는다.
    Date    string `json:",omitempty"` // 값이 비어 있으면 출력하지 않는다.
    Id      int    `json:"id,string"`  // 출력을 문자열로 변환하고 이름을 id로 바꾼다.
}

채널, 복소수, 함수는 JSON으로 인코딩 불가능

순환 데이터 구조도 마찬가지 (무한루프돎)

ResponseWriter : 스트림에 직접 쓸 수 있는 인코더/디코더

구조체를 바이트 배열로 디코딩한 다음에 response 스트림에 쓰는 것은 별로 효율적이지 않음.

response 스트림에 바로 작성하는게 훨씬 더 빠름

Go에서는 ResponseWriter라는 인터페이스를 제공함.

func helloWorldHandler(w http.ResponseWriter, r *http.Request) {
    response := helloWorldResponse{message: "Hello World"}
    data, err := json.Marshal(response)
    if err != nil {
        panic("Oppps")
    }
    fmt.Fprint(w, string(data))
}
func helloWorldHandler(w http.ResponseWriter, r *http.Request) {
    response := helloWorldResponse{Message: "HelloWorld"}
    encoder := json.NewEncoder(w)
    encoder.Encode(&response)
}

위의 함수가 바이트 배열에 저장하는 방식, 아래 함수가 바이트 배열에 저장하지 않고 HTTP 응답에 바로 쓰는 방식

벤치마킹 해보면 바이트 배열로 마샬링하는 것 보다 Encoder를 사용하는 것이 50% 정도 더 빠름

JSON으로 Go 구조체 언마샬링

여태까지 JSON을 클라이언트로 전송했음. 여기서는 입력 받는 방법에 대해 알아볼 것

일반적으로 HTTP POST 요청의 일부로 JSON을 받아들이는 서비스와 관련도니 복잡한 데이터 구조가 필요하다.

func Unmarshal(data []byte, v interface{}) error

Unmarshal 함수는 Marshal 함수와 반대로 작동함. 필요에 따라 맵, 슬라이스, 포인터를 할당함.

입력 받은 객체의 key가 구조체 필드의 이름이나 태그와 일치하는 필드를 찾음. 대소문자를 구분하진 않지만 일치하는게 좋음

기억해야할 두 가지

  1. 컴파일러를 위한 코드를 작성하지 말고 사람이 이해할 수 있는 코드를 작성한다.
  2. 코드를 작성하는 것 보다 코드를 읽는 데 더 많은 시간을 보낼 것이다.
type Reqeusts struct {
  Method string // HTTP 요청 방식을 지정한다. GET, POST, PUT
  Header Header // 서버가 수신한 요청 헤더 필드를 갖고 있음.
  Body io.ReadCloser 
}
func helloWorldHandler(w http.ResponseWriter, r *http.Request) {
    /*
                // 클라이언트에서 iotuil.ReadAll을 호출했다면 클라이언트는 자동으로 닫히지 않아서 Close()를 호출해야함.
                // 그러나 ServeHTTP 핸들러에서 사용될 때는 서버가 자동으로 요청 스트림을 닫음
                body, err := ioutil.ReadAll(r.Body)
                if err != nil {
                    http.Error(w, "Bad request", http.StatusBadRequest)
                    return
                }
        var request helloWorldRequest
                err = json.Unmarshal(body, &request)
                if err != nil {
                    http.Error(w, "Bad request", http.StatusBadRequest)
                    return
                    }
    */

    // 위 주석 코드보다 아래 코드가 33% 성능이 좋고 간단하다. encoding때와 마찬가지로 스트림에서 바로 받아오는 방식
    var request helloWorldRequest
    decoder := json.NewDecoder(r.Body)

    err := decoder.Decode(&request)
    if err != nil {
        http.Error(w, "bad Request", http.StatusBadRequest)
        return
    }

    response := helloWorldResponse{Message: "Hello " + request.Name}
    encoder := json.NewEncoder(w)
    encoder.Encode(response)
}
반응형

'Programming > Go' 카테고리의 다른 글

좋은 API 디자인  (0) 2021.03.26
[Go] Go 프로젝트 개발 환경 구축  (0) 2021.01.24
[Go] A Tour of Go Exercise: Web Crawler  (0) 2020.12.09
[Go] Mutex  (0) 2020.12.08
[Go] A tour of go Exercise: Equivalent Binary Trees 풀이  (0) 2020.12.08
Comments