현재 회사에서 사용하는 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 전에 빈리스트로 초기화해주는 유틸함수를 만들어보려고 한다.
https://www.notion.so/godoksajang/JSON-bfb15c94870042c9b91320a57decd29f
https://github.com/Minsoo-Shin/goutil/tree/main/nilasempty
// 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에 대한 고민들을 많이 찾아보게된 시간이었다.
그리고 합리적으로 의사결정하기 위한 프로세스를 조금 고민해 보았다.
마지막으로, 여러 언어들 간의 차이점에 대해서 많이 공부하다보면 팀간의 소통에서 조금 더 잘 이해할 수 있