치춘짱베리굿나이스

[프리온보딩] 220524 그룹과제 #3 본문

프로젝트/원티드 프리온보딩

[프리온보딩] 220524 그룹과제 #3

치춘 2022. 5. 25. 06:32

그룹과제 #3

공식적인 내생각

내 파트에서 할 수 있는 건 다 해서 추가 기능을 구현했다

광고 만들기 버튼을 누르면 진짜로 광고를 만들 수 있게 만들고 싶었는데 어찌저찌 작성하다보니…. 기본내용 구현보다 광고추가 기능 하나가 더 빡셌던 것 같다

중간중간 컴포넌트 분리나 합치기 등 리팩토링도 해가면서 했더니 밤을 꼴닥 새버렸다

하하

작업 내용

숫자 3자리마다 쉼표 찍어주기 - toLocaleString()

const number = 1234;
number.toLocaleString(); // 1,234

이렇게 쉬울수가

정규식은 다시 잊어버리도록 해야겠다

프로그래머 최악의 적 정규식

인자를 추가함으로써 숫자를 아라비아어, 한자 등 특정 언어로 바꾸거나 스타일을 추가할 수 있다 (달러, 엔화, 한화 등…)

promise랑 친구먹음

export const getAdListData = () => {
  const promise = new Promise((resolve: (value: IAdData) => void, reject) => {
    const data: IAdData = store.get('adsData')
    if (!data) reject();
    else resolve(data);
  })
  return promise
    .then(setFetchDelayPromise(500))
    .then((data: IAdData) => data)
    .catch(() =>
      axios
        .get('/wanted_FE_ad-list-data-set.json')
        .then(setFetchDelay(500))
        .then((response: AxiosResponse) => response.data)
    );
};

원래는 axios로 데이터 받고 바로 필터링까지 진행했는데, 데이터를 추가하려 하니 필터링을 거치지 않은 원본 데이터가 필요해서 + 로컬 스토리지에 데이터 넣었다 빼는 부분이 필요해서 fetch 함수 (getAdListData)를 개조했다

그와중에 store.get을 먼저 진행하고, 로컬 스토리지에 데이터가 존재하지 않을 경우에만 axios로 데이터를 받아와야 하기 때문에 promise + then / catch를 이용했다

로컬 스토리지에 데이터가 없었을 경우 reject를, 존재했을 경우 resolve를 통해 데이터 fetching 성공 여부를 결정하고, 실패했을 경우 (reject) catch 문을 통해 axios로 데이터를 받을 수 있도록 하였다

이중 Promise문이 되긴 했는데 어차피 useQuery 훅에서 사용하려면 Promise 형태여야 해서 (+ 지정한 동작이 끝날 때까지 기다리고 성공, 실패 여부에 따라 쉽게 추가 동작을 구현할 수 있어서) Promise를 사용했고 결과는 매우 괜찮았다

로컬 스토리지에 데이터가 없을 경우 예외처리를 복잡하게 하지 않고 바로 axios로 넘어갈 수 있기도 하고, 과제에서 요구한 delay도 쉽게 넣을 수 있어서 매우 쓸만하다

const filter = new Promise<IAdData>((resolve) => {
    if (adsFilterIndex === 0) resolve(data);
    const newData: IAdData = {
        count: 0,
        ads: [],
    }
    newData.ads = data.ads.filter((ad: IAd) => ad.status === STATUS[adsFilterIndex]);
    newData.count = newData.ads.length;
    resolve(newData);
});
filter.then((newData) => setFilteredData(newData));

커스텀 훅 내에서 필터링을 수행할 때도 Promise를 사용했다

이번에는 필터링이 끝난 데이터를 filteredData 상태에 넣어야 했기 때문

setState가 비동기로 동작하기 때문에 동작 관리를 해주지 않으면 그냥 빈 데이터가 홀랑 들어가버린다

그걸 막기 위해 then을 사용해서 후속동작으로만 필터링 데이터를 설정할 수 있게 해 주었고, 어차피 예외처리할 요인은 없으니 resolve만 사용하였다

Promise가 쓸 때마다 조금씩 헷갈려서 매번 문서 찾아가면서 하긴 하는데 그래도 많이 익숙해진듯하다

Promise 내에서 then으로 고의 딜레이 주기

export const setFetchDelay = (ms: number) => {
  return (x: AxiosResponse) => {
    return new Promise<AxiosResponse>((resolve) => {
      setTimeout(() => resolve(x), ms);
    });
  };
};

export const setFetchDelayPromise = (ms: number) => {
  return (x: IAdData) => {
    return new Promise<IAdData>((resolve) => {
      setTimeout(() => resolve(x), ms);
    });
  };
};

과제 요구사항 중 로딩 화면을 구현하고 이를 위해 임의로 딜레이를 주어야 하는 부분이 있었다

결과값이 AxiosResponse일 때 (데이터 받아올 때 axios 쓴 경우) 와 IAdData일 때 (로컬 스토리지에서 가져왔을 때) 를 다르게 구현했다

유니온타입을 사용해서 AxiosResponse일수도 IAdData일 수도 있습니다 라고 합쳐서 작성하면 해당 타입의 인수로 할당할 수 없다면서 오류가 나더라

axios.get().then(setFetchDelay(500)).then((res) => res.data);

// setFetchDelay가 반환하는 함수를 합친 모습
axios.get().then((x: AxiosResponse) => {
    return new Promise<AxiosResponse>((resolve) => {
        setTimeout(() => resolve(x), 500);
    });
}).then((res) => res.data);

사용 방법은 위와 같다

setFetchDelay에 인자로 500을 넣으면, 반환값은 아래와 같이 Promise를 사용하는 함수가 된다

Promise에 의해 setTimeout으로 500ms를 쉬어주고, 다 쉬고 나면 인자로 받은 xresolve 함수를 통해 그대로 반환해주는 형식

따라서 axios.get 에서 받은 인자를 그대로 (res) ⇒ res.data 까지 가져와서 사용할 수 있다

비동기 처리가 이렇게 간단하다

조건부 렌더링 (&& 연산자 등) / display: none

  • 조건부 렌더링: 조건의 true / false 여부가 바뀔 때마다 리렌더링되므로, 상태값도 초기화된다
    • 따라서 form 등 내용물의 초기화가 필요할 때 사용하면 좋다
  • display: none: 렌더링은 계속 되어있는 상태에서 스타일로 숨김 / 공개 처리만 하는 것이기 때문에 내용물 및 상태값도 그대로 남아있다
    • 내용물의 초기화가 필요 없거나, 값을 유지하고 싶을 때 사용하면 좋다

아무 생각 없이 display: none을 썼더니 form 안의 내용물이 계속 남아있어서…. 아예 리렌더링을 반복하는 방식을 사용했다

useEffectdependencyisOpen 상태값을 걸어줄까 했는데 form 안에서 사용하는 상태값이 너무 많아서 일일히 초기화하는 것도 일이다

일일히 일이다

Comments