bkdragon's log

[gin] ShouldBindJSON 본문

golang

[gin] ShouldBindJSON

bkdragon 2024. 9. 8. 15:08

gin 의 Context 에 있는 ShouldBindJSON 메서드는 JSON 데이터를 구조체로 바꿔주는 역할을 한다. 아마 요청을 읽고 Unmarshal 하는 과정이 있지 않을까 싶은데 실제 구현이 궁금해서 코드를 좀 찾아보았다.

func (c *Context) ShouldBindJSON(obj any) error {
    return c.ShouldBindWith(obj, binding.JSON)
}
func (c *Context) ShouldBindWith(obj any, b binding.Binding) error {
    return b.Bind(c.Request, obj)
}

ShouldBindJSON 는 ShouldBindWith 를 호출한다. ShouldBindWith 는 Binding 구조체와 obj를 받아서 Bind를 호출한다.

Binding 구조체를 살펴보자.

type Binding interface {
    Name() string
    Bind(*http.Request, any) error
}

인터페이스였다. Bind 메서드를 가지고 있다. 이제 구현체를 찾아보자.

var (
    JSON          BindingBody = jsonBinding{}
    XML           BindingBody = xmlBinding{}
    Form          Binding     = formBinding{}
    Query         Binding     = queryBinding{}
    FormPost      Binding     = formPostBinding{}
    FormMultipart Binding     = formMultipartBinding{}
    ProtoBuf      BindingBody = protobufBinding{}
    MsgPack       BindingBody = msgpackBinding{}
    YAML          BindingBody = yamlBinding{}
    Uri           BindingUri  = uriBinding{}
    Header        Binding     = headerBinding{}
    TOML          BindingBody = tomlBinding{}
)

포맷에 따라 다양한 구현체가 구현되어있다. 아까 binding.JSON 을 ShouldBindWith 에 넘기고 있었는데 이 구현체를 넘긴것이였다.

jsonBinding 구조체를 확인해보면 Name과 Bind 메서드를 가지고 있을 것이다.

type jsonBinding struct{}

func (jsonBinding) Name() string {
    return "json"
}

func (jsonBinding) Bind(req *http.Request, obj any) error {
    if req == nil || req.Body == nil {
        return errors.New("invalid request")
    }
    return decodeJSON(req.Body, obj)
}

func (jsonBinding) BindBody(body []byte, obj any) error {
    return decodeJSON(bytes.NewReader(body), obj)
}

func decodeJSON(r io.Reader, obj any) error {
    decoder := json.NewDecoder(r)
    if EnableDecoderUseNumber {
        decoder.UseNumber()
    }
    if EnableDecoderDisallowUnknownFields {
        decoder.DisallowUnknownFields()
    }
    if err := decoder.Decode(obj); err != nil {
        return err
    }
    return validate(obj)
}

빙고!
Name은 단순히 json이라는 문자열을 리턴해주고 있다. Bind는 예상과는 다르게 decoder 를 사용해서 스트림 방식으로 데이터를 처리한다. 이 방식은 메모리를 사용하지 않기 때문에 대용량 요청일 수록 성능적으로 이점이 있다.

'golang' 카테고리의 다른 글

[gin] Clean Architecture  (1) 2024.09.20
[gorm] 다형성 관계  (0) 2024.09.11
[go] http 통신 과정  (0) 2024.09.10
[gin] JSON  (1) 2024.09.08
[go] io 패키지  (5) 2024.09.07