Day 11 with Go-Lang

3 minute read

HTTP Post 호출

  • JSON Data POST

JSON 데이터를 POST 하는 것은 이전 글 Plain Text 를 Post 하는 것과 비슷하다. 다만, 데이터가 JSON 포맷이므로 http.Post() 의 두번째 Parameter 에 application/json 을 적고, 세 번째 Parameter 에 JSON 으로 인코딩된 데이터를 전달하면 된다. 아래 예제에서 JSON 데이터는 encoding/json 표준 패키지의 Marshal() 함수를 사용해 임의의 구조체 데이터를 JSON 으로 변경하는 방법을 사용했다.

package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
)

type Person struct {
	Name string
	Age int
}

func main() {
	person := Person{"Alex", 10}
	pbytes, _ := json.Marshal(person)
	buf := bytes.NewBuffer(pbytes)

	resp, err := http.Post("http://httpbin.org/post", "application/json", buf)
	if err != nil {
		panic(err)
	}

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	str := string(respBody)
	println(str)
}
  • XML Data POST

XML 데이터를 POST 하는 것은 위의 JSON 데이터를 POST 하는 것과 유사하다. 다만, 데이터를 XML 포맷으로 전달하므로 encoding/xml 패키지를 사용해 Marshalling 하고, MIME 타입을 application/xml 으로 지정한다.

package main

import (
	"bytes"
	"encoding/xml"
	"io/ioutil"
	"net/http"
)

type Person struct {
	Name string
	Age int
}

func main() {
	person := Person{"Alex", 10}
	pbytes, _ := xml.Marshal(person)
	buf := bytes.NewBuffer(pbytes)

	resp, err := http.Post("http://httpbin.org/post", "application/xml", buf)
	// ...(생략)...
}

지금까지 위의 POST 예제들은 간편한 http.Post() 함수를 사용했는데, 보다 세밀한 제어를 위해 Request 객체를 만들어 여러 셋팅을 변경한 후, http.Client 를 통해 호출할 수 있다.

package main

import (
	"bytes"
	"encoding/xml"
	"io/ioutil"
	"net/http"
)

type Person struct {
	Name string
	Age int
}

func main() {
	person := Person{"Alex", 10}
	pbytes, _ := xml.Marshal(person)
	buf := bytes.NewBuffer(pbytes)

	// Request 객체 생성
	req, err := http.NewRequest("POST", "http://httpbin.org/post", buf)
	if err != nil {
		panic(err)
	}

	// Content-Type 헤더 추가
	req.Header.Add("Content-Type", "application/xml")

	// Client 객체에서 Request 실행
	client := &http.Client{}
	resp, err := client.Do(req)
	defer resp.Body.Close()

	if err != nil {
		panic(err)
	}

	// Response 체크
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	str := string(respBody)
	println(str)
}

JSON 사용

  • JSON

JSON (JavaScript Object Notation) 은 데이터를 교환하기 위한 포맷으로서 그 단순함과 유연함 때문에 널리 사용되고 있다. 특히 웹 브라우저와 웹서버 사이에서 데이터를 교환하는 데 많이 사용되고 있다. JSON 포맷은 기본적으로 Key-Value Pair 의 Collection 이다.

Go 에서 JSON 을 사용하기 위해 표준패키지 encoding/json 을 사용하면 된다. 표준패키지는 표준 패키지 에 모두 자세하게 설명되어 있는데, 이 중 JSON 관련 패키지는 JSON 패키지 에서 참조할 수 있다.

  • JSON 인코딩

Go 데이터를 JSON 포맷으로 Encoding 하기 위해 encoding/json 패키지의 Marshal() 함수를 사용한다. 흔히 Go 구조체 또는 Map 데이터를 JSON 으로 Encoding 하게 되는데, 해당 Go 데이터 값을 json.Marshal() 의 Parameter 로 전달하면, JSON 으로 인코딩된 Byte 배열과 Error 객체를 리턴한다. 만약 JSON 으로 인코딩된 바이트 배열을 다시 문자열로 변경할 필요가 있다면, string(Byte 배열) 과 같이 변경이 가능하다.

한 가지 유의할 점은, JSON 의 Key 는 문자열이어야 한다. Go 구조체의 경우는 자동으로 필드명을 문자열로 사용하지만, Map 인 경우 map[string]T 처럼 Key 가 string 인 Map 만 지원한다. (Value ‘T’ 는 어떤 타입이든 상관 없음)

package main

import (
	"encoding/json"
	"fmt"
)

type Member struct {
	Name string
	Age int
	Active bool
}

func main() {
	// Go 데이터
	mem := Member{"Alex", 10, true}

	// JSON 인코딩
	jsonBytes, err := json.Marshal(mem)
	if err != nil {
		panic(err)
	}

	// JSON 바이트를 문자열로 변경
	jsonString := string(jsonBytes)

	fmt.Println(jsonString)
}
  • JSON 디코딩

JSON 으로 인코딩된 데이터를 다시 Decoding 하기 위해서, encoding/json 패키지의 Unmarshal() 함수를 사용한다. Unmarshal() 함수의 첫 번째 Parameter 에는 JSON 데이터를, 두 번째 Parameter 에는 출력할 구조체 (혹은 Map) 을 포인터로 지정한다. Return 값은 Error 객체이고, 에러가 없는 경우, 두 번째 Paramter 에 원래 데이터가 복원된다.

package main

import (
	"encoding/json"
	"fmt"
)

type Member struct {
	Name string
	Age int
	Active bool
}

func main() {
	// 테스트용 JSON 데이터
	jsonBytes, _ := json.Marshal(Member{"Tim", 1, true})

	// JSON 디코딩
	var mem Member
	err := json.Unmarshal(jsonBytes, &mem)
	if err != nil {
		panic(err)
	}

	fmt.Println(mem)
}

JSON 디코딩에서 만약 매칭되는 필드가 구조체에 없다면, 그 값은 무시되고 계속 처리된다. 즉, JSON 에 10 개의 Key-Value Pair 가 있는데, 출력 구조체는 그 중 3개의 필드만 가지고 있다면 3개 필드만 채워지고 나머지 키값은 무시된다.

또한 Go 의 JSON 패키지는 출력 구조체나 Map 을 미리 정의하지 않고 디코딩할 수도 있다.

XML 사용

  • XML 인코딩

Go 데이터를 XML 포맷으로 Encoding 하기 위해서는 encoding/xml 패키지의 Marshal() 함수를 사용한다. 아래 예제는 Go 구조체를 XML 로 Encoding 하는 코드인데, 해당 Go 데이터 값을 xml.Marshal() 의 Parameter 로 전달하면 Marshal() 함수는 XML 로 인코딩된 Byte 배열과 Error 객체를 Return 한다.

package main

import "encoding/xml"

type Member struct {
	Name string
	Age int
	Active bool
}

func main() {
	mem := Member{"Alex", 10, true}

	xmlBytes, err := xml.Marshal(mem)
	if err != nil {
		panic(err)
	}

	xmlString := string(xmlBytes)
	println(xmlString)
}
  • XML 디코딩

XML 로 Encoding 된 데이터를 다시 GO 타입으로 Decoding 하기 위해 encoding/xml 패키지의 Unmarshal() 함수를 사용한다. Unmarshal() 함수의 첫 Parameter 에는 XML 데이터를, 두 번째 Parameter 에는 출력할 구조체 포인터를 지정한다. Return 값은 Error 객체이고, 에러가 없는 경우, 두 번째 Parameter 에 원래 데이터가 복원된다.

package main

import (
	"encoding/xml"
	"fmt"
)

type Member struct {
	Name string
	Age int
	Active bool
}

func main() {
	// 테스트용 데이터
	xmlBytes, _ := xml.Marshal(Member{"Tim", 1, true})

	// XML Decoding
	var mem Member
	err := xml.Unmarshal(xmlBytes, &mem)
	if err != nil {
		panic(err)
	}

	fmt.Println(mem)
}
  • XML 파일 읽기

흔히 XML 로 저장된 파일을 읽어 Go 타입으로 Decoding (혹은 Deserialization) 해 사용하는 경우가 많은데, 아래 예제는 이를 보여주는 것이다. 즉, 아래와 같은 XML Text 파일을 읽어 이를 Members 구조체로 Decoding 한 예이다.

<Members>
	<Member>
		<Name>Alex</Name>
		<Age>10</Age>
		<Active>true</Active>
	</Member>
	<Member>
		<Name>Tim</Name>
		<Age>12</Age>
		<Active>false</Active>
	</Member>
</Members>
package main

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"
)

type Member struct {
	Name string
	Age int
	Active bool
}

type Members struct {
	Member []Member
}

func main() {
	// xml 파일 Open
	fp, err := os.Open("test.xml")
	defer fp.Close()
	if err != nil {
		panic(err)
	}

	// XML 파일 읽기
	buf, err := ioutil.ReadAll(fp)
	var members Members
	xmlErr := xml.Unmarshal(buf, &members)
	if xmlErr != nil {
		panic(xmlErr)
	}

	fmt.Println(members)
}

Reference : 예제로 배우는 Go 프로그래밍