상세 컨텐츠

본문 제목

[Nomad Coin] Rest API - #6.2 MarshalText

Go/Blockchain

by Gopythor 2022. 7. 20. 05:03

본문

728x90
반응형

#6.2 MarshalText

 

{
	URL:         "http://localhost:4000/blocks",
	Method:      "POST",
	Description: "Add A Block",
	Payload:     "data:string",
	},
  • URL을 완전한 URL(Full)로 바꾸고 싶다.
  • 어떤 방법으로 struct가 encode될 때 URL을 추가하고 싶다.
  • 왜냐하면 API에서 URL을 클릭하게 하고 싶어서 그렇다.
  • 목표는 http://localhost:4000/ string을 모든 URL에 더해주는 것이다.
  • 이걸 위해서 interface의 도움을 받아야 한다.
  • interface는 함수의 청사진 같은 것이다

  • Stringer라는 interface가 있다. 이건 String이라는 하나의 method만 구현시킨다.
  • 꼭 대문자로 시작하는 String이어야 하고, 매개변수를 받지 않고, string을 return해줘야 한다.
  • struct나 type이 interface를 구현하면 fmt package로 출력할 때 어떻게 보일지 조절 할 수 있다.
  • 이 interface의 경우 구현하면, struct의 형태를 fmt Print로 조절할 수 있다.
  • Go에서는 모든 interface가 내재적으로 구현되어 있다.
  • 이 말은 Go에게 Stringer interface라고 말해줄 필요가 없다.
  • 파이선, 자바, 자바스크립트에서의 상속이 없다.
  • 예를 들어 URLDescription에 implement Stringer라고 해줄 필요가 없다.
  • 가이드대로 method를 구현하면 된다.
  • 구현하는 순간 URLDescription이 내재적으로 Stringer interface를 구현해준다.
func (u URLDescription) String() string{
	return "Hello I'm the URL Description"
}
  • URLDescription을 받는 receiver함수를 만들고 이름은 String으로 하고 string을 return하게 할 것이다.
  • 가이드에 따르면 string을 return해야 하니까 return "Hello I'm the URL Description"
  • 이제 URLDescription이 Stringer interface를 구현했다.
  • method만 정의해주면 된다.
  • 이게 중요한 이유는 URLDescription을 출력할 때 벌어질 일 때문이다.
func main() {
	fmt.Println(URLDescription{
		URL:         "/",
		Method:      "GET",
		Description: "See Documentation",
	})
	http.HandleFunc("/", documentation)
	fmt.Printf("Listening on http://localhost%s", port)
	log.Fatal(http.ListenAndServe(port, nil))
}
  • main에 가짜 URLDescription을 만들어 볼 것이다.

  • 이 URLDescription을 fmt.Println으로 출력할 것이다.
  • 출력해보면 URL, Method, Description이 나오지 않을 것이다.
  • fmt package는 Stringer interface를 자주 사용한다.
  • fmt package가 뭔가를 format해야될 때 String method를 찾을 것이다.
  • method가 구현돼어 있으면 호출하는 것이다.
  • 어떻게 출력될 지 선택할 수 있는 것이다. 이 경우에는 "Hello I'm the URL Description"를 출력한다.
func documentation(rw http.ResponseWriter, r *http.Request) {
	data := []URLDescription{
		{
			URL:         "/",
			Method:      "GET",
			Description: "See Documentation",
		},
		{
			URL:         "http://localhost:4000/blocks",
			Method:      "POST",
			Description: "Add A Block",
			Payload:     "data:string",
		},
	}
	fmt.Println(data)
	rw.Header().Add("Content-Type", "application/json")
	json.NewEncoder(rw).Encode(data)
}

  • data를 print해보면, fmt.Println(data) 어떻게 될지 확인해보자.
  • "Hello I'm the URL Description"의 배열들이 나왔다.
  • package가 interface에 정의된 method를 호출해준다.

  • Stringer 함수를 삭제하고 출력하면 /blocks, POST, Add a Block, data같은 정보들이 나온다.
  • 원한다면 Stringer inteface를 커스터마이징 할 수 있다.
  • Marshal, Unmarshal할 때, Field의 결과물을 수정할 수 있는 interface가 있다.
  • Interface를 json으로 변환할 때, 중간에 끼어들어서 Field가 json에서 어떻게 보일지를 원하는 대로 바꿀 수 있다.
  • 그러기 위해서는 type이 필요하다.
  • Go에서는 원하는 어떤 type이든 만들 수 있다.
  • type의 이름은 URL로 하고, 이 string을 URL로 바꿀 것이다.
type URL string

type URLDescription struct {
	URL         URL    `json:"url"`
	Method      string `json:"method"`
	Description string `json:"description"`
	Payload     string `json:"payload"`
}
  • string이지만 type인 URL을 만들었다.
func documentation(rw http.ResponseWriter, r *http.Request) {
	data := []URLDescription{
		{
			URL:         URL("/"),
			Method:      "GET",
			Description: "See Documentation",
		},
		{
			URL:         URL("/blocks"),
			Method:      "POST",
			Description: "Add A Block",
			Payload:     "data:string",
		},
	}
  • Marshal 함수의 공식 문서를 보면, Marshal은 json으로 encoding한 interface를 return해준다.
  • MarshalText라는 것이 있다. MarshalText는 Field가 json string으로써 어떻게 보일지 결정하는 method이다.
  • MarshalText는 encoding package에서 오는 것이다.

  • TextMarshlaer에 MarshalText를 보면 이게 시그니처다.
  • MarshalText는 byte slice와 error를 return해야 한다.
  • Marshal함수가 struct를 json으로 변환할 때 자동으로 호출해준다.
func (u URL) MarshalText() ([]byte, error){
	return []byte("hello!"), nil
}
  • URL type 이 TextMarshaler interface를 구현하게 할 것이다.
  • URL func를 만들고, MarshalText 함수를 구현할 것이다.
  • 여기에서 URL이 어떻게 json으로 Marshal될지를 정할 수 있다.
  • 일단 return "Hello"를 해보자.
  • 그전에 byte slice와 error를 return해야 한다.
  • interface를 정확하게 구현했다면, 누가 이 interface를 호출하든 이걸 호출할 것이다.
func (u URL) MarshalText() number {
	return []byte("hello!"), nil
}

func (u URL) MarshalText() int {
	return []byte("hello!"), nil
}
  • 이름이 MarshalText여도 여기에 number같은 걸 입력하면 실행되지 않을 것이다.
  • int를 return해도 아무도 이 함수를 호출하지 못할 것이다.
  • 이름은 MarshalText로 했지만 시그니처가 틀리면, 절대 구현되지 않는다.
  • TextMarshaler를 구현하고 싶으면 이대로 해야 한다.
  • URL이 TextMarshaler 같은게 된거 같기도 하다.

  • 모두 hello 해주고 있다.
  • Marshal에 끼어들어서 원하는 텍스트로 바꿨다.
  • 정확한 이름과, 시그니처를 사용해서 구현하기만 하면 끝이다.
  • TextMarshaler interface를 구현하겠다고 선언해서가 아니라 MarshalText를 통해서 구현했다.
  • 정확한 자리에 입력하기만 하면 많은 interface를 구현할 수 있다.
func (u URL) String() string {
	return "hi"
}
  • 했던 것처럼 Stringer함수를 만든다면 이름은 String(), string을 retrun해야 하고, return "hi" 이렇게 하면 URL이 TextMarshaler와 Stringer interface를 구현하게 된 것이다.
  • 이렇게 하면 URL이 TextMarshaler와 Stringer interface를 구현하게 된 것이다.
  • 내재적으로 구현한 것이라서 써줄 필요는 없다. 그냥 method를 구현해주면 된다.
func (u URL) MarshalText() ([]byte, error) {
	url := fmt.Sprintf("http://localhost%s%s", port, u)
	return []byte(url), nil
}
  • 진짜로 url을 구성해보자.
  • fmt.Sprintf만 해주면 된다.Sprintf 함수는 string과 원하는 포맷을 받고 value를 보내주면, 포맷된 string을 return해준다.
  • 포맷하고 싶은 string은 "http://localhost"
  • string(%s) port를 입력하고, URL string을 하나 더 입력해준다.
  • port그리고 실제 URL인 u를 추가하고 URL이 완성되었으니 return해준다.
  • 이렇게 MarshalText를 만들어서 interface를 구현했고, 원했던 걸 이뤘다.
  • /blocks라고 쓰면, MarshalText가 원하는 대로 포맷해준다.

  • 만드는 모든 URL은 이에 맞춰서 포맷될 것이다.

 

출처: 노마드코더 노마드코인 강의

728x90
반응형

관련글 더보기

댓글 영역