#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은 이에 맞춰서 포맷될 것이다.
출처: 노마드코더 노마드코인 강의
댓글 영역