[developic] `undefined` cannot be serialized as JSON 에러 해결

sue·2021년 7월 21일
1

developic project

목록 보기
23/28

Server Error
Error: Error serializing .initialState.user.auth.error returned from getServerSideProps in "/".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.

정말 간단한 부분에서 발생했는데 한참을 헤맸던 에러였다.

먼저 내용을 읽어보면 getServerSideProps에서 .initialState.user.auth.error를 직렬화하는 과정에서 에러가 발생했고, undefined은 JSON으로 직렬화할 수 없으니 null을 사용하거나 생략하라고 친절히 말해주고 있다.

여기서 주목해야 할 것은 .initialState.user.auth.error 이다.
말 그대로 사용자 인증의 error 부분에서 에러가 생긴 상황이란 얘기다(에러탈트붕괴현상...🤦‍♀️).
나는 error가 그 error를 말하는 거라는 생각을 못해 괜히 서버 코드를 뒤적거리고 멀쩡한 코드만 지웠다가 추가했다가 시간을 낭비했는데 결국 에러 내용에 답이 있었다.

export const authServersiceAction = async (context: ContextType): Promise<void> => {
  const cookie = context.req ? context.req.headers.cookie : '';
  axios.defaults.headers.Cookie = '';
  if (context.req && cookie) {
    axios.defaults.headers.Cookie = cookie;
  }
  await context.store.dispatch(authAction(null)); // authAction에서 에러 발생
};

// 로그인 인증
export const authAction = createAsyncThunk<User, null, { rejectValue: MyKnownError }>(
  'user/auth',
  async (_, { rejectWithValue }) => {
    try {
      const { data } = await axios.get(`/auth`);
      return data;
    } catch (e) {
      console.error(e);
      return rejectWithValue({ message: e.response.data });
    }
  }
);

서버사이드렌더링 시 쿠키를 공유하는 함수에서 authAction을 디스패치하는 코드 한줄을 지우니 에러가 발생하지 않았다. 이것을 바탕으로 추적해보니 authAction은 로그인 인증에 관한 thunk 함수인데 여기서 에러를 리턴하는 과정에서 e.response.dataundefined이 담긴 것이 원인이었다.

그렇다면 e.response.data에 왜 undefined이 담겼을까?

이는 리덕스 툴킷의 createAsyncThunk으로 비동기를 처리하는 과정에서 rejectWithValue 속성을 사용하는데, rejectWithValue의 값인 RejectValueundefined으로 정의되어 있기 때문이다. 아래를 보면 확인할 수 있다.


declare type BaseThunkAPI<S, E, D extends Dispatch = Dispatch, RejectedValue = undefined> = {
    dispatch: D;
    getState: () => S;
    extra: E;
    requestId: string;
    signal: AbortSignal;
    rejectWithValue(value: RejectedValue): RejectWithValue<RejectedValue>;
};

그래서 thunk 함수에서 예상되는 오류 형식을 다음과 같이 인터페이스로 만들어 오류 값으로 반환하는 작업까지 해주었는데, 이것 대신 e.response.data를 사용해서 발생한 문제였다.

interface MyKnownError {
  message: string;
}

{ rejectValue: MyKnownError } 

다음과 같이 e.message로 수정하여 오류를 해결할 수 있었다.

export const authAction = createAsyncThunk<User, null, { rejectValue: MyKnownError }>(
  'user/auth',
  async (_, { rejectWithValue }) => {
    try {
      const { data } = await axios.get(`/auth`);
      return data;
    } catch (e) {
      console.error(e);
      return rejectWithValue({ message: e.message }); 
    }
  }
);

에러를 해결할 때, 구글링도 중요하지만 에러 내용, 그리고 내가 작성한 코드를 좀 더 파헤쳐보는 습관을 가져야겠다.

0개의 댓글