Golang

[Golang] Json Unmarshal 시 에러 로깅

GenieLove! 2023. 8. 16. 02:51
728x90
반응형

Json

  • json.Unmarshal 을 할 때 에러 발생 시 타입이 json 타입이 아니게 되면 오류가 발생 할 수 있다.
  • 오류 발생 시 변환 데이터와 변환하려는 타입을 알 수 없어 어떤 값인지 알기 어렵다.

Unmarshal 함수 생성

  1. 로깅을 위한 ErrorData 구조체를 생성하여, 덕 타이핑을 통해 error interface type에 속하게 한다.
  2. Unmarshal 실패 시 error 데이터를 ErrorData 구조체에 할당하여 반환한다.
  3. ErrorData에 unmarshaling하려던 데이터를 InputData field에, 값을 저장할 데이터의 타입을 TargetType field에 저장하고, unmarshaling 실패 에러를 Err field에 저장한다.
  4. ErrorData 구조체의 포인터 리시버로 UnmarshalJSON함수를 생성하여 unmarshal 시 해당 함수를 이용하게 한다.
  5. MashalJSON은 ErrorData의 메소드로 작성한다.
  6. ErrorData를 marshal or unmarshal 시 아래 생성한 함수가 호출된다.
type ErrorData struct {
	InputData  string `json:"input_data"`
	Err        error  `json:"-"`
	TargetType string `json:"target_type"`
}

func (e *ErrorData) UnmarshalJSON(data []byte) (err error) {
	defer util_error.DeferWrap(&err)
	type E ErrorData
	errorDataInner := &struct {
		*E
		ErrorString *string `json:"error"`
	}{
		E: (*E)(e),
	}
	err = json.Unmarshal(data, errorDataInner)

	if err != nil {
		return
	}

	if errorDataInner.ErrorString != nil {
		e.Err = errors.New(*errorDataInner.ErrorString)
	}

	return
}

func (e ErrorData) MarshalJSON() ([]byte, error) {
	var err error
	defer util_error.DeferWrap(&err)
	type E ErrorData
	errorDataInner := &struct {
		*E
		ErrorString *string `json:"error"`
	}{
		E: (*E)(&e),
	}

	if e.Err != nil {
		errString := e.Err.Error()
		errorDataInner.ErrorString = &errString
	}

	data, err := json.Marshal(errorDataInner)

	return data, err
}

func (e *ErrorData) IsError() bool {
	return e.Err != nil
}

func (e *ErrorData) Error() string {

	return utils.Join("error: ", e.Err.Error(), ", inputData: ", e.InputData, ", TargetType: ", e.TargetType)
}

func Unmarshal(data []byte, v any) (errorData error) {

	err := json.Unmarshal(data, v)

	if err == nil {
		return
	}

	TargetType := reflect.TypeOf(v)

	errorData = &ErrorData{
		InputData:  string(data),
		Err:        err,
		TargetType: TargetType.String(),
	}

	return
}

오류 상황

// ErrorStruct의 포인터 리시버인 UnmarshalJSON함수 내에서 json.Unmarshal(data, &errorData)로 하여 무한루프로 호출되었던 문제
// 문제 코드
func (e *ErrorData) UnmarshalJSON(data []byte) (err error) {
	defer util_error.DeferWrap(&err)
	err = json.Unmarshal(data, e)

	if err != nil {
		return
	}


	return
}

// 해결 -> 임시 구조체를 만들어서 ErrorStruct를 내부 포인터 필드로 갖도록 하여 unmarshaling을 한다.
// 수정 코드
func (e *ErrorData) UnmarshalJSON(data []byte) (err error) {
	defer util_error.DeferWrap(&err)
	type E ErrorData
	errorDataInner := &struct {
		*E
		ErrorString *string `json:"error"`
	}{
		E: (*E)(e),
	}
	err = json.Unmarshal(data, errorDataInner)

	if err != nil {
		return
	}

	if errorDataInner.ErrorString != nil {
		e.Err = errors.New(*errorDataInner.ErrorString)
	}

	return
}
728x90
반응형

'Golang' 카테고리의 다른 글

[Golang] interface addressable  (0) 2023.05.10
[Golang] interface 타입 nil 체크  (3) 2023.02.09
[Golang] iota  (0) 2022.11.20
Error Is, As  (0) 2022.10.15
[Golang] Closure  (0) 2022.04.18