나쁜친구들 Mobx와 Next.js

김동하·2020년 12월 16일
4

next

목록 보기
2/10

세팅도 드릅게 힘들고 예제도 별로 없고 그 흔한 유튜브도 별로 없는 나쁜 녀석 Next.js 그리고 바벨 세팅의 늪에 빠져서 내가 코딩을 하는 건지 복붙 행운 뽑기를 하는 건지 모르겠는 Mobx, 두 나쁜 친구들이 만났다.

세팅 삽질을 멈추게 해주셔서 감사합니다.

next.js 참고사항

  • pages 디렉토리에서 자동으로 라우트 path가 설정된다.

  • index.js는 해당 디렉토리의 대표 파일 역할

  • Next.js는 시멘틱 태그에 유리하다.

  • export default는 하나의 파일에 여러 컴포넌트가 있을 때 대표 컴포넌트만 export하겠다는 뜻!

  • _documnet.js 는 기본 문서 형식을 지정한다. 서버사이드에서는 1회 로드!

  • _app.js는 클라이언트단의 첫 진입점이다.

  • next.js에서 가장 중요한 것은 사전 렌더링!

기본적으로 Next.js 는 모든 페이지를 사전-렌더링 합니다. 이는 클라이언트 사이드 자바스크립트로 모든 작업을 하는 대신, Next.js 가 HTML을 각 페이지들을 위해 미리 생성한다는 것을 말합니다. 사전-렌더링으 결과로 더 나은 성능과 SEO(검색엔진 최적화)를 얻을 수 있습니다.
각 생성된 HTML은 페이지들에 필요한 최소한의 자바스크립트 코드와 연결됩니다. 페이지가 브라우저에 의해 로드될 때, 자바스크립트가 실행되고 완벽히 상호작용 가능한 페이지를 만들어냅니다. (이 작업은 hydration 이라고 부릅니다.)

출처 : 사전 렌더링

next.js 구조

  • pages/ // HTML Document, Application Container, 각종 페이지 등을 작성한다.
    • _document.js // HTML Document.
    • _app.js // Application Container. 공통의 레이아웃을 작성한다.
    • _error.js // Error Page.
    • index.js // Root Page /로 시작되는 경로를 말한다.
    • hello.js // Hello Page /hello로 시작되는 경로를 말한다.
  • static/ // 정적 파일 (이미지, 파일 등)을 업로드 한다.
  • next.config.js // Next.js의 환경 설정 파일이다. 라우팅 설정, typescript, less 등의 webpack 플러그인을 설정한다.

화살표함수

_app.js 세팅

카운터 만들기

makeObservable로 관찰 대상을 정해준다. this는 현재 class고 두 번째 인자로 변수와 메서드가 들어간다.

setState없이도 잘 작동한다. 최하단에 Counter를 감싼 observerobservable로 지정한 값이 변할 때 알아서 렌더하기 때문이다.

Decorator 와 함께 사용하기

이렇게 간단하게 지켜볼 친구들한테 @를 붙이고 선언해주면 된다.

MobX 스토어 분리하기

리덕스는 하나의 앱에 단 하나의 스토어만 있지만 Mobx는 여러 개가 가능하다. Mobx의 store는 간단하다. 클래스에 observable 값이랑 함수들을 만들어주자.

프로젝트에 store 적용

Next.js의 엔트리 파일인 _app.js 에 Provider로 감싸고 counter store를 생성한다.  

inject 로 컴포넌트에 스토어 주입

inject함수는 스토어에 있는 값을 컴포넌트의 props 로 "주입"을 해준다.

그럼 이렇게 counter 컴포넌트에서도 사용이 가능하다.

만약 리덕스의 mapStateToProps / mapDispatchToProps처럼 스토어의 특정 함수나 값만 가져올 때는

함수형태로 파라매터로 전달해주면 된다!

슈퍼마켓 구현

가장 상위 컴포넌트. items라는 props로 <ShopItemList>를 내리는데 컴포넌트 자체를 props로 준다.

<ShopItemList> 컴포넌트다. items 배열을 받아서 <ShopItem>을 리턴하는데 그 것을 변수 itemList에 담는다.

itemlist를 콘솔찍으면

item은 이렇게 props받아서 뿌려준다.

렌더된 상태

장바구니

장바구니 store를 만든다.

장바구니 기본 로직이다. put, take, get total이 있다.

put은 존재하는 item이 있는지 찾고 없다면 selectedItem 배열에 추가한다. 있다면 count 값만 증가시킨다.

take은 반대로 count를 감소시키고 count가 0이되면 배열에서 삭제한다.

total은 reduce로 합을 계산하는데 객체를 활용해서 아주 잘 쓸 수 있는 reduce....

이제 observable과 action을 데코레이터로 주자. 중요한 것은 total에는 computed를 해줘야한다.

market 스토어 적용하기

이제 주입하고 기능이 필요한 컴포넌트에 가져오면 된다.

일단 ShopItemList에 inject, observer를 한다. 그리고 onPut이란 이름으로 put 액션을 준다.

Item 쪽에서 Click으로 이벤트를 준다.

장바구니에 데이터 반영하기

요렇게 하드코딩 되어있는 BasketItemList에 store를 주입해야 한다.

이렇게 연결만 해주면 Market에서 onPut 함수가 호출되면 selectedItems에 추가되고 추가된 아이템을 BasketList에서 주입되어서 반영된다!

이제 basketItem구현!

리스트를 렌더할 때는 내부 컴포넌트에도 감시해야 성능 최적화가 된다.

스토어 끼리 관계 맺어주기!

리덕스와 비슷하게 root를 만들어준다.

요렇게 만들어주면 되는데 특이한 건constructor()에 넣어주고 생성자함수의 인자로 this를 준다.

이렇게 함으로서 각 스토어들이, 현재 루트 스토어가 무엇인지 알 수 있게 된다.

이제 스토어에 가서 root를 추가해주면 된다.

이제 마지막으로 엔트리에 가서 spread로 store를 props를 풀어준다. index에서 콘솔을 찍으면

응용

market에서 상품을 가져올 때 카운터에 있는 상태만큼 가져오게 하자!!!!!! 굉장한 일이다.

root로 합쳐놨기 때문에 market에서 item을 클릭하면 marketStore에서 counterStore로 접근이 가능하다. 이제 number를 지정하고 selectedItems의 count 값으로 number를 준다.

이렇게 counterStore의 number를 주면

지정된 number 만큼 가져온다!!

드디어 MobX 의 리액트 컴포넌트 최적화

렌더할 때 성능 최적화는 중요하다. 내가 스트레스 받아서 단명하더라도 성능은 최적화해야 한다.

  • 규칙 1 : 리스트를 렌더링 할 땐, 컴포넌트에 리스트 관련 데이터만 props 로 넣자
    • 서로 다른 조건에서 변경되는 props가 같은 컴포넌트에 있다면 하나가 변경될 때 다른 것도 렌더되기 때문에 분리하는 것이 좋다!
  • 규칙 2: 세부참조 (dereference)는 최대한 늦게하자
    • 세부참조가 무엇인지 모르겠지만 일단 늦게해보자.
    • 세부참조란 객체 내부의 값을 조회하는 것이다.
  const itemList = items.map(item => (
    <BasketItem
      name={item.name}
      price={item.price}
      count={item.count}
      key={item.name}
      onTake={onTake}
    />
  ));

가령 item에서 name, price, count를 조회하는 것이 세부참조다. 이것을 하나로 퉁치자

 const itemList = items.map(item => (
    <BasketItem
      item={item}
      key={item.name}
      onTake={onTake}
    />
  ));
  • 규칙 3: 함수는 미리 바인딩, 파라미터는 내부에서 넣어주자.
    • 파라미터가 유동적일 떄는 파라미터는 안에서 넣는다.
onst ShopItemList = ({ onPut }) => {
  const itemList = items.map(item => (
    <ShopItem {...item} key={item.name} onPut={() => onPut(item.name, item.price)} />
  ));
  return <div>{itemList}</div>;
};

이것보다는

const ShopItemList = ({ onPut }) => {
  const itemList = items.map(item => (
    <ShopItem {...item} key={item.name} onPut={onPut} />
  ));
  return <div>{itemList}</div>;
};

const ShopItem = ({ name, price, onPut }) => {
  return (
    <div className="ShopItem" onClick={() => onPut(name, price)}>
      <h4>{name}</h4>
      <div>{price}원</div>
    </div>
  );
};

이렇게 파라미터 내부에서 받는다.

아직 내 인생의 최적화도 못 했는데 컴포너트 최적화를 해야한다. 어쩔 수 없다.

개선하기

첫번째로, 총합이 나타나는 부분을 컴포넌트화 해준다.

이렇게 토탈 부분만 컴포넌트로 만들고

JSX 형태로 SuperMarketTemplate에 준다. 나머진 List에서 토탈만 지우면 끝!!

출처 : https://velog.io/@velopert/MobX-3-%EC%8B%AC%ED%99%94%EC%A0%81%EC%9D%B8-%EC%82%AC%EC%9A%A9-%EB%B0%8F-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95-tnjltay61n

출처 : https://salgum1114.github.io/nextjs/2019-05-06-nextjs-static-website-1/

profile
프론트엔드 개발

2개의 댓글

comment-user-thumbnail
2020년 12월 16일

우와 진짜 어려워 보인다 !! 울 작가님 화이팅 할 수 있어요!!

1개의 답글