개인공부/TIL(Today I Learned)

saga 쓰기 전에 ES6 Generator 공부하기

soon327 2021. 7. 7. 17:46

어제부터 4주 프로젝트에서 구현한 모달창을 리팩토링하기위해 redux middlware를 로컬에서 적용해보고 있다.

목표: 모달창의 예/아니오에 따른 콜백함수를 리덕스에 전달하여 실행시키기

현재는 Modal component에 콜백함수를 직접 넣어서 예/아니오에 따라 실행시키고 있는데 기능은 어찌저찌 되지만...
Modal component에 온갖 콜백함수가 들어가게되고 컴포넌트가 너무 무거워진다는 생각이 들었다. 공부를 좀 하다가 middleware를 써서 함수를 dispatch하면 되겠다는 생각이 들었다.

redux-toolkit을 사용했기때문에 기본으로 내장되어있는 redux-thunk를 먼저 사용해봤다.

export const modalCallback = createAsyncThunk('modal/callback', async (callback: any, thunkApi) => {
  const state = thunkApi.getState() as RootState;
  if (!state.modal.open) {
    console.log('되라되라');
  }
  return {
    payload: await callback(),
  };
});

export const modalSlice = createSlice({
  name: 'modal',
  initialState,
  reducers: {
    ...
  },
  extraReducers: {
    [modalCallback.pending.type]: (state) => {
      console.log('pending');
    },
    [modalCallback.fulfilled.type]: (state, action: PayloadAction<OpenPayload>) => {
      console.log('fulfilled:::');
    },
    [modalCallback.rejected.type]: (state) => {
      console.log('rejected');
    },
  },
});

콜백함수를 dispatch하고 미들웨어를 통해 실행시키는 건 잘되지만 예/아니오 를 눌러서 상태가 변했을 때 실행시키는 것, 즉 store를 구독해서 실행시키는 방법을 찾지 못했다.
thunkApi.getState로 state에 접근해서 어떻게 해볼까 했지만 state를 읽는 건 가능했지만 state의 변화에따라 다시 modalCallback을 실행시키는 방법은 찾지 못했다. ㅠㅠ

구글링구글링 계속하다가 다음 정보를 알아냈다.

thunk는 절대로 action에 응답을 줄수 없다. 반면 saga는 store를 구독하고 특정 작업이 디스패치될때 saga가 실행되도록 할 수 있다.

ㅇ0ㅇ
saga를 써보기로 했다.
saga는 ES6문법인 generator를 사용하기때문에 이 공부가 먼저 선행되어야 할 것 같아서 정리겸 적어본다.🤸🏻


Generator

  • 제너레이터는 함수의 실행을 중간에 멈췄다가 재개할 수 있는 독특한 문법이다.
  • function 옆에 * 을 써서 만들고, 내부에 yield 키워드를 사용한다.
  • yield 에서 함수의 실행을 멈출 수 있다.

예시코드

function* fn() {
    console.log(1);
      yield 1;
      console.log(2);
      yield 2;
      console.log(3);
      console.log(4);
      yield 3;
      return "finish"
}

const generator = fn();

next()는 다음 yield에 도달할때까지의 코드를 실행시킨다.
value는 yield 오른쪽 값을 나타내고 done은 함수 실행이 완료됐는지를 boolean으로 나타낸다.

Generator의 3가지 메소드

Generator는 3가지 메소드를 갖고있다.

  1. next : 다음 yield 값을 반환한다.
  2. return : 주어진 값을 반환하고 생성기를 종료한다.
  3. throw : 생성기로 에러를 throw한다.

next()에 인수 전달하기

generator에 외부값을 넣을 수도 있다.

function* fn() {
    const num1 = yield "첫번째 숫자를 입력해주세요";
      console.log(num1);

      const num2 = yield "두번째 숫자를 입력해주세요";
      console.log(num2);

      return num1 + num2;
}

const generator = fn();

yield를 변수에 할당한 다음에, next()에 인자를 넣으면 해당 변수에 값이 할당된다.

Generator는 값을 미리 만들어 놓지 않는다.

필요한 순간에만 연산해서 값을 주기때문에 메모리 관리 측면에서 효율적이다.

이같은 특성때문에 아래와 같은 코드도 가능하다.

function* fn() {
    let index = 0;
  while(true){
      yield index++;
  }
}
const generator = fn();

무한 반복코드를 만들어도 브라우저가 꺼지지 않는다. 메소드로 호출할때만 값을 주기 때문이다.
generator를 사용하면 쓸지 안쓸지 모르는 값들을 메모리에 저장할 필요없이 필요할 때만 값을 호출할 수 있다.

yield*을 이용해서 다른 generator 불러오기

function* gen1() {
    yield "W";
      yield "O";
      yield "R";
      yield "L";
      yield "D";
}

function* gen2() {
    yield "Hello,";
      yield* gen1();
      yield "!";
}

const generator = gen2()

+) spread syntax를 사용해서 done이 true가 될때까지 값을 펼쳐줄 수 있다.

+) yield*에서 *을 빼면 generator 객체가 출력된다.


아직 눈과 손에 익숙하지는 않지만
saga를 쓰려고 이리지리 하다보면 금방 익숙해질 것 같다.

참고: https://www.youtube.com/watch?v=qi24UqyJLgs