[Go] Http Response (null vs empty slice)

ms-shin·2023년 4월 30일
0

Go

목록 보기
1/1

JSON marshaling 빈리스트 초기화 필요성

현재 회사에서 사용하는 Go 언어 같은 경우, 빈 리스트 디폴트 초기값이 nil이고, 따로 할당해주지 않으면 null로 마샬링된다.

실제로 코드를 보자

type Person struct {
	Friends []string
}

func main() {
	json1, _ := json.Marshal(Person{f1})
	json2, _ := json.Marshal(Person{f2})

	fmt.Printf("%s\n", json1)
	fmt.Printf("%s\n", json2)
}

https://go.dev/play/p/VY3ym2EwV3R

결과는 아래와 같다.

{"Friends":null}
{"Friends":[]}

하지만 프론트 입장에서는 array가 nulll로 들어오면 null check가 필요하고, 불필요한 오류를 발생시킬 수 있다는 의견이다.

GPT에서도 그렇게 해줘야 오류를 발생하는 것을 방지할 수 있다라고 하는 것 같다.

표준 패키지에서도 갑론을박

Go 표준 encoding/json Package에서도 nilasempty Tag로 알아서 빈리스트로 초기화하자는 2020년 PR도 아직 merge 되지 않았다. (2023기준

요약하면 nil-slices는 Go 언어의 구현 세부 사항이며 nil-slices는 json 인코더에 의해 일반 슬라이스로 처리되어야 합니다. 슬라이스이고 비어 있으며 다음과 같이 지정할 때와 같이 json에 표시되어야 합니다 []. nilasempty이전 버전과 호환되는 방식으로 이 새로운 동작을 활성화합니다.

https://github.com/golang/go/issues/37711?ref=morioh.com&utm_source=morioh.com

불편한 건 사실이지만, 어쩔 수 없다. 직접 만들자

사실 객체를 빈리스트로 초기화해주는 것에 대해서 불편함을 겪었던 건 사실이다. 하지만, Go와 다른 언어 간의 json으로 의사소통을 하고 있는데, Go만의 특성으로 인해 null로 줘도 된다는 주장이 조금 약하다고 생각된다. 그리고 google style guide에서도 nullable한 값은 제거하도록 권고한다.

https://google.github.io/styleguide/jsoncstyleguide.xml

프론트와 백엔드의 궁극적인 방향은 서로 에러를 일으키지 않고 서비스를 운영해나가는 것이 아닐까라는 생각이다.

그래서 JSON marshal 전에 빈리스트로 초기화해주는 유틸함수를 만들어보려고 한다.

Go의 reflect를 공부해가면서 정리해보았다.

https://www.notion.so/godoksajang/JSON-bfb15c94870042c9b91320a57decd29f

nil -> []slice 변환 코드

https://github.com/Minsoo-Shin/goutil/tree/main/nilasempty

  • nested struct에서도 적용
// https://github.com/Minsoo-Shin/goutil/tree/2243a28f046378e287b7d78c3c25225266d0fe1e/nilasempty
func InitEmptySlice(any interface{}) {
	target := reflect.ValueOf(any)
	elems := target.Elem()
	switch elems.Kind() {
	case reflect.Slice:
		if elems.IsNil() {
			elems.Set(reflect.MakeSlice(elems.Type(), 0, 0))
			return
		}

		for i := 0; i < elems.Len(); i++ {
			InitEmptySlice(elems.Index(i).Addr().Interface())
		}

	case reflect.Struct:
		for i := 0; i < elems.NumField(); i++ {
			fieldValue := elems.Field(i)
			InitEmptySlice(fieldValue.Addr().Interface())
		}
	}
}

테스트도 해보았다.

느낀점

프론트와 백엔드간에 의견을 나누던 과정에서 왜라는 질문을 많이 하게 되었고, 언어의 특성, JSON style에 대한 고민들을 많이 찾아보게된 시간이었다.

그리고 합리적으로 의사결정하기 위한 프로세스를 조금 고민해 보았다.

  • 확실한 정답이 있는 경우는 따라간다.
  • 정답이 없는 사안에 대해서는 유명 IT회사들의 guide를 참고 하면서 회사의 상황에 맞게 양측 의견의 좋은점과 나쁜점을 객관적으로 논의하고, 결정한다.

마지막으로, 여러 언어들 간의 차이점에 대해서 많이 공부하다보면 팀간의 소통에서 조금 더 잘 이해할 수 있

profile
지식을 깊게 파고드는 개발자입니다.

0개의 댓글