# 2020.11-12

# 11/16

# Emotion 11

  • TS 빌드 시간 및 타입 추론 성능 개선
  • Hooks를 잘 사용함으로써 번들 사이즈가 줄어듬
  • @emotion/eslint-plugin등의 파서 성능 개선
  • 라이브러리 이름이 변경됨 (@emotion/core -> @emotion/react 등): @emotion/pkg-renaming으로 auto fix 가능

# 12/22

# React hooks로 데이터 fetch해오기

https://www.robinwieruch.de/react-hooks-fetch-data

  1. 느낀점 - 선언적인 리액트 코드에서 비동기 데이터를 페칭하는건 노깔끔... : data/loading/error 상태를 알기 위해 페치시작은 useEffect에서, 각 state는 서로 독립적인 useState에서 관리해야 한다. 그리고 이를 set하는건 또 useEffect 안에 있다. 서로 의존적인 코드니 한군데 뭉쳐있는게 좋을텐데. 예를 들어 error 핸들링을 바꾸고 싶다면 뚝 떨어진 3군데를 건드려야 하는게 짱난다.
  2. 커스텀 훅을 만들면 그나마 한 줄로 관리할 수 있는데, 이를 만들기 위해 또 위와 같은 코드를 낳아야하는게 불편.
const [{ data, isLoading, isError }, doFetch] = useDataApi(
  "https://hn.algolia.com/api/v1/search?query=redux",
  {
    hits: [],
  }
);
  1. 리듀서훅으로 위에서 따로 관리했던 state를 한 번에 관리하기.
import React, { useReducer } from "react";

const dataFetchReducer = (state, action) => {
  // 액션type에 따라 state를 mutate시킨다. 추가적인 데이터는 action.payload에서 받음.
  switch (action.type) {
    case "FETCH_INIT":
      return {
        ...state,
        isLoading: true,
        isError: false,
      };
    case "FETCH_SUCCESS":
      return {
        ...state,
        isLoading: false,
        isError: false,
        data: action.payload,
      };
    case "FETCH_FAILURE":
      return {
        ...state,
        isLoading: false,
        isError: true,
      };
    default:
      throw new Error();
  }
};

const useDataApi = (initialUrl, initialData) => {
  // useReducer: 첫번째인자는 reducer를, 두번째 인자는 initial state 오브젝트를 받는다.
  // 그리고 [mutate된 state오브젝트, dispatch함수]를 반환한다.
  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
  });

  useEffect(() => {
    const fetchData = async () => {
      // dispatch함수에 type(필수인자)넘기기
      dispatch({ type: "FETCH_INIT" });

      try {
        const result = await axios(url);
        // dispatch함수에 type, payload넘기기
        dispatch({ type: "FETCH_SUCCESS", payload: result.data });
      } catch (error) {
        dispatch({ type: "FETCH_FAILURE" });
      }
    };

    fetchData();
  }, [url]);
  return [state, setUrl];
};

# 리액트 코드 분할

https://ko.reactjs.org/docs/code-splitting.html

  1. 번들이 넘 크다면 번들을 나누면 성능up. 런타임에 여러 번들을 동적으로 만들고 불러오기(웹팩, 롤업, Browserify 등에서 지원)
  2. import 코드분할 (Webpack, Next.js)
// before
import { add } from "./math";
add(1, 2);

// after
import("./math").then((math) => {
  math.add(1, 2);
});
  1. React.lazy (서버사이드렌더링은 불가)
// before
import FooComponent from "./FooComponent"

// after
const FooComponent = React.lazy(() => import "./FooComponent")

function MyComponent() {
  return (
    // 모듈 로드에 실패시 에러 바운더리로 떨어짐
    <MyErrorBoundary>
    // lazy컴포넌트는 서스펜스 안에 감쌓여있어야함. 그래야 로드되는중 fallback보여줌
    <Suspense fallback={<Loader />}>
      <FooComponent />
    </Suspense>
    </MyErrorBoundary
  )
}

  1. 코드분할 어디서부터 할까? 시작하기 좋은 장소는 라우트. 각 컴포넌트를 lazy하게 가져와보자.

# Error boundary

https://ko.reactjs.org/docs/error-boundaries.html

  1. js의 try/catch문처럼, 컴포넌트에서 에러나면 catch되어 보여줄 UI 만들기
  2. getDerivedStateFromError나 componentDidCatch 에서 error 받고, 보여줄 ui render하는 클래스컴포넌트 만든다.
  3. 이를 컴포넌트처럼 사용. 에러캐치 원하는 컴포넌트 상위에 감싸준다
<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

# 전역 상태 관리에 대한 단상 (stale-while-revalidate) by jbee

https://jbee.io/react/thinking-about-global-state

  1. A, B 컴포넌트에 모두 필요한 api응답값 처리: 1. 둘다 호출 2. Lift state up 3. 전역 redux
  2. 전역 리덕스의 문제: 내가 원하는 state가 원하는 시점에 존재한다는 보장이X. 그리고 (내가 원하는 만큼)최신 데이터도X. => 걍 데이터가 필요할때마다 api호출하는게 깔끔. 다만 캐싱해서 비용을 업애버렷
  3. stale-while-revalidate란 HTTP 캐시컨트롤 스펙이 있음
Cache-Control: max-age=<seconds>, stale-while-revalidate=<seconds>
# ex) Cache-Control: max-age=1, stale-while-revalidate=59
# 1초내 재요청하면 캐시값 그대로 /60초 내에 반복발생시에도 캐시값 그대로, 다만 백그라운드에서 새삥으로 갈아치움 / 60초 이후에 재요청하면 다시 서버요청
  1. 이 swr을 리액트에 적용한 대표적 라이브러리들: swr, react-query, rtk-query.
  2. 강점: 데이터 가져오는&접근하는 코드가 동일. 주기적으로 revalidate해서 똑똑한 캐싱. (+ 비동기 요청에 따른 status처리, 실패에 따른 retry 처리)
  3. 굳이 전역상태관리가 필요할 때?: 테마, 다국어처리, 상태 넘 복잡해서 프론트 자체적으로 정규화해서 관리/최적화필요 => Redux나 Recoil 쓸 수 있음.

# 12/23

# React suspense

https://ko.reactjs.org/docs/concurrent-mode-suspense.html

React 16.6+ suspend: 유예하다

  1. 비동기(데이터, 이미지, 스크립트 등) 작업을 기다릴 때 씀. "컴포넌트! 니 데이터가 아직 준비 안됐어".
  2. suspense없는 기존방식: 렌더링 직후 불러오기
  3. 화면에 컴포넌트가 렌더링 완료 이후에야 비로소 useEffect로 데이터 불러옴
  4. 워터폴 problem: 컴포넌트가 중첩되어있다면 위의 로딩이 풀리는 시점에야 비로소 아래 데이터 가져오기 시작. 사실 필요 데이터들은 병렬로 불릴 수 있었음에도! 근데 promise.all등으로 병렬로 부른다 하면 첫 로딩이 넘 길어짐.
  5. suspense 도입: 데이터 부른 직후 렌더링 할 수 있음. 데이터는 그 후에 불러와짐!
  6. 아래 코드 보면 로딩분기 if문이 없다.
  7. 경쟁상태(Race condition)존재x (a를 불러온 후에 이에 따라 b를 불러야한다는 등..). 시간에 대한 것을 그다지 고려x해도 되기 때문. 데이터 있다 치자~ 하고 코딩. 에러는 try/catch가 아니고 에러 바운더리로 처리.
const resource = fetchProfileData(); // promise가 아님! Relay등 라이브러리의 suspense통합에서 나온 특수객체

function ProfilePage() {
  // 1. 렌더링 시도
  return (
    <Suspense fallback={<h1>Loading profile...</h1>}>
      <ProfileDetails user={resource.user} /> 2. 렌더링 시도(트리로 타고
      내려옴). 근데 user데이터가 없어서 렌더링 정지
      <Suspense fallback={<h1>Loading posts...</h1>}>
        // 4. 가장 가까운 서스펜스 Fallback찾음.
        <ProfileTimeline posts={resource.posts} /> // 3. 위에 기다지 않고 바로 렌더링
        시도. posts데이터가 없어서 렌더링 정지
      </Suspense>
    </Suspense>
  );
}

# 12/25

# React is becoming a black box

https://jaredpalmer.com/blog/react-is-becoming-a-black-box

  1. 리액트가 어떻게 돌아가는지 모르는 사람들이 늘어남(hook, concurrent mode등 나오면서). 원래 리액트 의도는 "너는 뭘(what) 구현할지 생각만 해, 어떻게(how) 구현할진 우리가 해줄게"였는데 how가 어려워짐.
  2. 리액트 엔지니어링 디렉터(톰 오치노) 입장: new 페북 빠르게/빨라보이게 하기 위해 CM개발중.. 아직 더 할게 많다. Vaporware(개발 중부터 요란하게 선전하지만, 실제로는 완성될 가능성이 없는 소프트웨어) 아니구 많은 리서치로 만들고있음.

# 12/26

# Concurrent mode랑 suspense로 좋은 UX 만들기

https://ko.reactjs.org/blog/2019/11/06/building-great-user-experiences-with-concurrent-mode-and-suspense.html

  1. 리액트는 퍼포먼스튜닝(React.memo)등 api제공은 했지만 이보다 하이레벨의 패턴에 opinionated하진 않았었음(모든앱이 빠르길 원하는것도 아니고, 진입장벽 낮추기 위해). but 더 큰 이상을 보기로함. 코드가 더 복잡해지더라도 말야 for UX, 스피드, 다양디바이스, 다양네트워크.
  2. 외부에는 기반작업인 context api랑 훅부터 내놓음. 페북 내부에는 c.m와 relay로 유연하고 더 app-like한 뉴페북 만들어봄. 첫로딩만 중요한게 아님. 앱안에서 인터렉션할 때 화면이 덜바뀌고 즉시 반응 보이는걸 더 빠르다고 느낌.
  3. 전통방식: 로딩스피너로 컴포넌트 렌더먼저 하고, mount시에 데이터 fetch한다 / Suspense방식: 로딩될때까지 화면바꾸지 않고 기다리다 스피너 없이 렌더
  4. 라우터 자체에서 미리 불러올 api들을 정의하면 어떨까? 링크에 hover하면 그때부터 바로 불러오는거지 (다만 데이터 열라 잡아먹음)

# 12/27

# React v17.0 (2020.10)

https://reactjs.org/blog/2020/10/20/react-v17.html

  1. 2.5년만의 메이저 업데이트. 사용자에게 뉴피쳐는 없고, 리액트 내부 업글이다. 나중에 릴리즈할 피쳐들 위한 디딤돌
  2. 메이저 업데이트는 바뀐 api 대응때문에 사실 챌린징한 일이었음(legacy context api deprecation등은 자동코드변경도 불가능). 그래서 이번 v17부터는 점진적인(gradual) 업뎃 가능하도록. 여기는 17쓰고, 다른곳은 lazy load로 16쓰고 할 수 있단거지(권장하는건 아님. 불가피할 때 쓸 수 있도록). 이를 가능하게 하기 위해 리액트 내부 이벤트 시스템을 많이 바꿔야 됐음. 그래서 이번 17이 뉴피쳐 없지만 메이저 업데이트인것임.
  3. 이벤트 델리게이션 내부변경: button에 onClick달면 리액트 내부적으로는 document노드에 버블링시킨다(성능개선때문). 근데 점진업뎃으로 리액트 버전 2개씩 쓰면 여기서 문제가 생김. 그래서 17부터는 document가 아닌 root DOM에 붙이기로 함.
    1. 외에도 전체적으로 이벤트 관련 내부 변경이 많다. 조금 더 DOM/JavaScript 기본 이벤트와 가까워지고 예상했던대로 리액트 이벤트 코드가 동작하게 됨.
  4. useEffect 클린업 function: 기존엔 동기로 불렸는데 이제 비동기로! 좋네. 동기로 하고싶으면 useLayoutEffect 쓰삼.

# 12/28

# 웹팩 개념들

https://joshua1988.github.io/webpack-guide/concepts/overview.html https://velog.io/@pop8682/%EB%B2%88%EC%97%AD-%EC%99%9C-babel-preset%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%98%EA%B3%A0-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80-yhk03drm7q

  1. entry: 여기있는 파일들을 변환할거시다
  2. output: 변환한 결과물은 여기에 저장할거시다
  3. loader: (module.rules[]에 넣는다) js말고 다른 자원(html, css, image, font등)도 변환하고싶다. 특정 파일(정규식으로 검사)에 ㅁㅁ로더를 쓴다고 명시. (자주사용: babel-loader, sass, file, ts) (use에 배열로 로더를 넣으면 오른쪽에서 왼쪽으로 적용)
  4. plugin: 변환된 결과물의 형태를 바꿈. (e.g. HtmlWebpackPlugin - 빌드 결과물로 html 파일 생성, ProgressPlugin - 웹팩빌드 진행률 표시, @babel/plugin-transform-arrow-functions: 바벨로 arrow function es5로 컴파일)
  5. mode: development | production - 에 따라 웹팩 결과물 다르게.
  6. preset: 플러그인 하나하나 설치하기 귀찮. 모아놓은 세트가 preset. (e.g. @babel/preset-react, @babel/preset-typescript)

# npx

https://blog.npmjs.org/post/162869356040/introducing-npx-an-npm-package-runner (npm 5.2이상이면 자동설치)

  1. 로컬 패키지에서 script run할때 직접 ./node_modules/... 들어가지 않고도 간단커맨드로 실행 가능(npx mocha)
  2. 로컬에 굳이 설치하기 싫은 일회성 커맨드 클라우드에서 실행 (npx create-react-app foo). 일단 설치하고 실행 후 자동제거시킴. 이게 cra팀도 더 좋은게 사람들이 항상 최신버전 쓸테니까.

# 12/29

# Parcel

https://parceljs.org/ https://kdydesign.github.io/2020/09/23/parcel-intro/

  1. 빠르고(캐싱 덕 - 웹팩 20s/파슬9.9초/파슬2회차2.6초) 설정 필요없는(러닝커브 낮음) 번들러
  2. 코드분할이 기본(모듈 필요시 자동으로 하위번들 처리)
  3. 플러그인 없이 html, css, file 등 처리, babel, postcss, posthtml 등 여러 트랜스파일러 내장
  4. 오픈소스X

# 12/30

# Snowpack

https://heropy.blog/2020/10/31/snowpack/ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import https://www.daleseo.com/js-module-import/

  1. 번들러가 아님
    1. babel, ts등으로 파일 빌드하고 -> js의 import/export문법으로 브라우저에서 개별 로드.
  2. 개빠름, 변경사항 브라우저 즉반영, TS/JSX/CSS 모듈 등 기본지원, 다른 번들러(웹팩, 롤업 등)랑 통합가능
  3. 의존성관리: node_modules의 모든 의존성 모듈을 js로 바꾸고 브라우저에서 직접실행(js의 import문법)
  4. snowpack.config.js로 관리

# 12/31

# Webpack 5 릴리즈노트 (2020.10)

https://webpack.js.org/blog/2020-10-10-webpack-5-release/

  1. 2년만의 메이저 업데이트(리액트랑 비슷하네). 아키텍쳐 개선과 이게 수반해야만 가능한 피쳐릴리즈. 4랑 5 둘다 메인테이닝할거임-완성본x. Breaking changes가끝났다는 의미로 릴리즈함.
  2. 웹팩은 오픈소스. 기업이 주도X. 코로나 때문에 큰타격입음. 상황 나아질때까지 월에 10일만 pay줄 수 있다(컨트리뷰터/메인테이너들이 후원금으로 돈을 받는군!).
  3. 릴리즈 방향
    1. 캐싱 - Persistent caching으로 빌드 퍼포먼스 업, Long term cachibng으로 알고리즘 업
    2. 번들사이즈, 트리셰이킹 업
    3. 웹 플랫폼과 호환성 업
    4. 4를 breaking changes업게 만드느라 짰던 이상한 코드 clean up

# 리액트 API콜 어디서? constructor vs componentWillMount vs componentDidMount

https://medium.com/@santoshpunase/integrating-apis-in-react-js-constructor-vs-componentwillmount-vs-componentdidmount-e0b98c3efecd

최근에 함수형컴포넌트+useEffect만 쓰다가 강의찍기위해 클래스컴포넌트 보는데 api콜 어디서 했는지 헷갈려서 찾아봄.

  1. contructor에서? X: initial state랑 이벤트핸들러 바인딩이 목적인 곳. 데이터페칭은 사이드이펙트 일으킬 수 있어 여기서 하지 말아라. 일단 setState 자체도 막아둠.
  2. componentWillMount에서? X: render전에 불리니 여기 둬야 가장 빠르지 않을까 하는데 그렇지도 않은게 직후에 render가 불려 로딩상태 피할 수 없음. 그리고 SSR에서도 불리기 때문에(추가 데이터 페칭 불가한..) api가 불필요하게 2번 불리게 됨. 그리고 이 생명주기 메서드가 deprecated되었슴.
  3. componentDidMount: 걍 mount 되고 부르는게 젤 깔끔.