비동기 로직 처리하기

yoon Y·2022년 3월 20일
0

Vanilla Project

목록 보기
12/13

Vanilla JS ShoppingMall SPA 구현 과정

fetch 함수 추상화

export const request = async (url) => {
  try {
    const res = await fetch(`${API_END_POINT}${url}`); 

    if (res.ok) {
      return await res.json();
    }
    throw new Error('API 호출 오류');
    
  } catch (e) {
    alert(e.message)
  }
};

시도 1

클래스 constructor함수 내에서 비동기 처리 시 문제점
setup함수에서 비동기가 다 끝나고 나서 다음 메소들이 실행되는게 아닌,
비동기 로직이 끝날 동안 다음 메소드들이 줄줄이 실행돼서 초기 값이 빈값이 렌더링되어 오류가 생겼다

setup함수에서 setState함수를 실행하고 template함수에서 빈값일 경우 빈값을 리턴하게 하면
비동기가 끝나기 전엔 빈값이 렌더링되고, 비동기가 끝나면 setState안에서 render함수가 다시 실행되기 때문에 서버에서 받아온 값으로 렌더링될 수 있다

export default class ProductListPage extends Component {
    async setup() {
        const res = await request('/products');
        this.setState({ProductData:res});
        // 서버 요청이 완료되면 render함수를 실행하도록 setState함수로 초기화
    }

    template() {
        if(!this.state) {   //state 빈 값 처리
            return ''
        }

        return `
          <div class="ProductListPage">
            <h1>상품목록</h1>
            <ul data-component='product-list'></ul>
          </div>
          `;
    }

    mounted() {
        if(!this.state) {
            return 
        } // state 빈 값 처리
      
        const $productList = this.$target.querySelector('[data-component="product-list"]');
        new ProductList($productList, this.state);
    }
}

시도 2

비동기 로직 완료 전 렌더링 문제 해결

문제점
위 방법 처럼 기존에 비동기로 서버 데이터를 가져올 때 데이터가 다 불러지기 전에는 아무것도 렌더링되지 않을 경우 사용성 측면에서 좋지 않을 것 같았다.

해결책

  • 첫 렌더링 시 state에 서버에서 불려질 데이터 모델대로 객체를 선언하는데 값은 빈 값으로 둔다
  • 빈 값으로 렌더링을 한 번 한다
  • fetch함수를 실행하는데, 서버에서 값을 불러온 후 setState룰 실행하는 로직이 담겨있다.
    (템플릿 컴포넌트에 fetch함수 추가함)

이런 방법이면 서버 데이터가 불려지기 전에도 돔 렌더링을 먼저 할 수 있고, 원하는 디폴트 값을 넣어 사용할 수 있다.
리액트와 같은 방식이다.


// Component.js (템플릿 컴포넌트)
export default class Component {
  $target;
  props;
  state;
  constructor($target, props) {
    this.$target = $target;
    this.props = props;
    this.setup();
    this.fetch();
    this.setEventToParent();
    this.render();
  }


  
// ProductDetailPage.js
export default class ProductDetailPage extends Component {
  $selectedOptions;
  $select;

  setup() {
    this.state = {
      productData: {
        productId: '',
        productName: '',
        productPrice: '',
        imageUrl: '',
        productOptions: [],
      },
      selectedOption: {
        optionId: '',
        optionStock: 0,
        optionName: '',
        optionPrice: 0,
        count: 0,
      },
    };
  }

  async fetch() {
    const res = await request(`/products/${this.props.id}`);

    this.setState({
      productData: {
        ...res,
        productId: res.id,
        productName: res.name,
        productPrice: res.price,
      },
    });
  }
profile
#프론트엔드

0개의 댓글