React에서 귀여운 MetaMask 로고 넣는 과정

2022. 5. 18. 23:21프론트엔드/React.js

728x90

치얼쓰 커피의 1.0.0버전이 거의 완성이 되어간다.
그렇게 개발을 하던 중, 로딩 화면이 필요하게 되어서 어떻게 넣지 생각하다가
메타마스크에 들어있는 여우가 너무 귀여워서
한번 넣어봐야겠다라고 생각했다.


패키지 설치

우선 npm으로 설치한다.

npm i @metamask/logo

 

 

@metamask/logo

A browserifyable 3d metamask logo. [Live demo](http://metamask.github.io/logo/).. Latest version: 3.1.1, last published: 6 months ago. Start using @metamask/logo in your project by running `npm i @metamask/logo`. There are 7 other projects in the npm regis

www.npmjs.com

무려 '공식' 패키지다.
그렇다. 패키지 주인이 찐 메타마스크다.


출력해보기

아무 화면에서나 패키지를 불러와서 함수를 호출해준다.

import MetaFox from '@metamask/logo';

const SomeComponent = () => {
  MetaFox({
    // Dictates whether width & height are px or multiplied
    pxNotRatio: true,
    width: 500,
    height: 400,

    // To make the face follow the mouse.
    followMouse: true,
  })
  
  return null;
}

export default SomeComponent;

함수를 호출하는 것만으로도 생성된다.

그런데 여기서부터 문제가 (여럿) 있다


document가 없습니다 (next.js)

우선 가장 먼저 우릴 반기는 에러는 이 에러다.

이 에러는 프로젝트에 따라 발생하지 않을 수 있다.
왜냐하면 next.jsSSR중에 발생하는 에러기 때문이다.

document는 브라우저에서 주입하는 변수기 때문에,
서버에서는 document는 그냥 없는 변수다.

그래서 이런 조건문으로 커버를 해주자.
document가 있을 때에만 실행해주는 방법이다.

useEffect(() => {
    if (window.document !== null && window.document !== undefined) {
      MetaFox({
        // Dictates whether width & height are px or multiplied
        pxNotRatio: true,
        width: 500,
        height: 400,
    
        // To make the face follow the mouse.
        followMouse: true,
      })
    }
  }, []);

한번만 출력하기

실행이 되더라도 여우 두마리가 생성되어서
위아래로 마우스를 쳐다보는 상황이 생길 수 있다.

그 이유는 그냥 두번 호출되었기 때문이다.
플래그를 만들어서 생성이 되었는지 체크해보자.

const metaFoxOn = useRef(false);

useEffect(() => {
  if (window.document !== null && window.document !== undefined && !metaFoxOn.current) {
    MetaFox({
      // Dictates whether width & height are px or multiplied
      pxNotRatio: true,
      width: 500,
      height: 400,

      // To make the face follow the mouse.
      followMouse: true,
    })

    metaFoxOn.current = true;
  }
}, []);

useState를 이용해서 처리할 수 있을거라고 생각할 수 있지만,
실제로 해보면 state가 변경되기 전에 두번 호출되어서
제대로 체크가 안 된다.
즉발로 변경이 가능한 useRef를 사용하자.


얘 왜 안 없어져?

가장 당황스러운 점은 로딩이 끝나서 사라져야 하는데도
그냥 멀뚱멀뚱 남아있다는 점이다.

사실 함수호출만으로도 생성된다는 점을 보면 알지만,
바닐라 JS를 위한 패키지다.

아무튼 React의 동작방식과는 다르게 돌아가기 때문에, 
약간의 트릭으로 제거를 해줘야겠다.

우선 함수 호출 시, 객체를 반환한다.
그 객체를 저장했다가 컴포넌트의 라이프 사이클과 함께 제거해보자.

  const metaFox = useRef(null);
  const metaFoxOn = useRef(false);

  useEffect(() => {
    if (window.document !== null && window.document !== undefined && !metaFoxOn.current) {
      metaFox.current = MetaFox({
        // Dictates whether width & height are px or multiplied
        pxNotRatio: true,
        width: 500,
        height: 400,
    
        // To make the face follow the mouse.
        followMouse: true,
      })

      metaFoxOn.current = true;
    }

    return () => {
      delete metaFox.current;
    }
  }, []);

UI에 잘 버무리기

사실 그냥 이 MetaFox만 따로 컴포넌트로 빼는게 정석이다.
하지만 나는 야매가 좋아.
^~^

이 게시물 처음에 나왔던 화면처럼 중앙 정렬을 한번 해 보자.

const Waiting = () => { 
  const metaFox = useRef(null);
  const metaFoxOn = useRef(false);

  useEffect(() => {
    if (window.document !== null && window.document !== undefined && !metaFoxOn.current) {
      metaFox.current = MetaFox({
        // Dictates whether width & height are px or multiplied
        pxNotRatio: true,
        width: 500,
        height: 400,
    
        // To make the face follow the mouse.
        followMouse: true,
      })

      const divMetaFox = document.getElementsByClassName('metafox')

      if (divMetaFox.length > 0) {
        divMetaFox[0].insertBefore(metaFox.current.container, divMetaFox[0].firstChild);
      }

      metaFoxOn.current = true;
    }

    return () => {
      delete metaFox.current;
    }
  }, []);

  return (
    <div className='metafox' style={{ alignContent: "center", justifyContent: "center", display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", width: "100vw", height: "100vh" }}>
      <div style={{ width: "100%", textAlign: "center", fontSize: 32 }}>맛있는 커피를 끓이는 중...</div>
    </div>
  )
}

주의깊게 볼만한 부분은, 'metafox'를 클래스로 가지는 div를 가져와서
첫번째 자식으로 등록한 점이다.
그래서 MetaFox가 텍스트보다 위에 있게 된다.


인자 살펴보기

마지막으로, 함수에 들어가는 인자를 훑어보자

pxNotRatio: 비율(%)로 적용할 것인가 아닌가
width, height: 가로, 세로. pxNotRatio가 false라면, 0에서 1사이의 값을 넣자.
followMouse: 여우가 마우스를 따라가느냐
slowDrift: 천천히 떠다니느냐 (?)