본문 바로가기
웹 개발/React

리액트 반응형 모듈 사용하기 (react-responsive)

by 주식 키우는 개발자 2020. 7. 29.
반응형
반응형

 

리액트 프로젝트를 할 때 반응형 을 좀 더 쉽게 사용할 수 있는 반응형 모듈에 대해서 정리해보려고 합니다.
선정 이유 부터 셋팅까지 정리해보도록 하겠습니다.

1. 반응형 모듈 선정

결론부터 말씀드리면 react-responsive 를 선택하였는데, 그 이유를 react-responsivereact-media 두가지 모듈을 비교하며 정리해보려합니다.

1.1 대중성 및 꾸준한 업데이트

스크린샷 2020-07-10 오후 6 32 12

 

npm trends 사이트에서 react-media와 react-responsive의 트렌드를 비교해볼 수 있습니다.

최근 업데이트 날짜를 제외하고는 react-responsive가 다운로드 수도 2배가량 앞서고 있고, 2년 먼저 만들어졌으며, Git의 좋아요인 star 개수도 많은 것을 확인할 수 있습니다.
이런 수치들이 절대적이지는 않지만 오랫동안 문제없이 사용돼 왔다는 반증이기 때문에 결과적으로 대중성 및 꾸준한 업데이트 면에서는 react-responsive 모듈이 좀 더 좋은 수치를 보이는 것 같습니다.

2.2 편의성

react-media

react-media는 render, children(function) childrend(react element) 이 세가지 props를 이용해 내용을 렌더링 할 수 있습니다.

  • render
  • <Media queries={{ mobile: "(max-width: 599px)"}} render={() => <p>I matched!</p>} />
  • children(function)
  • <Media queries={{ mobile: "(max-width: 767px)"}}> {matches => matches.mobile ?( <p>I matched!</p> ) : ( <p>I didn't match</p> ) } </Media>
  • childrend(react element)
  • <Media queries="(max-width: 767px)"> <p>I matched!</p> </Media>

queries에는 css 방식 혹은 camelCase를 이용해 작성할 수 있습니다. px를 작성하지 않아도 자동으로 픽셀로 계산합니다.

<Media query={{ maxWidth: 599 }}></Media>
<Media query="(max-width: 599px)"></Media>

그래서 V/v-web에서 사용할 반응형 가이드를 토대로 작성해보면 아래처럼 사용할 수 있습니다.

import React from "react";
import Media from "react-media";

const GLOBAL_MEDIA_QUERIES = {
    pc: "(min-width: 1024px) and (max-width: 1279px)",
    tablet: "(min-width: 768px) and (max-width: 1023px)",
    tablet_pc: "(min-width: 768px) and (max-width: 1279px)",
    mobile_tablet: "(max-width: 1023px)",
    mobile: "(max-width: 767px)"
};

function App() {

    return (
        <div className="App">
                        <Media queries={GLOBAL_MEDIA_QUERIES}>
                {matches => {
                    console.log(matches);
                    return (
                        <>
                            {matches.pc && <p>pc </p>}
                            {matches.tablet && <p>tablet </p>}
                            {matches.tablet_pc && <p>tablet_pc </p>}
                            {matches.mobile_tablet && <p>mobile_tablet </p>}
                            {matches.mobile && <p>mobile </p>}
                        </>
                    );
                }}
            </Media>
        </div>
    );
}

export default App;

react-responsive

react-responsive에서 위와 같은 구간으로 나누면 아래처럼 작성할 수 있습니다. react-media와 다르게 각 조건들이 props로 존재하는 형태입니다.

import React from "react";
import MediaQuery from "react-responsive";

function App() {

    return (
        <div className="App">
            <MediaQuery minWidth={1024} maxWidth={1279}>
                <p>pc </p>
            </MediaQuery>
            <MediaQuery minWidth={768} maxWidth={1023}>
                <p>tablet </p>
            </MediaQuery>
            <MediaQuery minWidth={768} maxWidth={1279}>
                <p>tablet_pc </p>
            </MediaQuery>
            <MediaQuery maxWidth={1023}>
                <p>mobile_tablet </p>
            </MediaQuery>
            <MediaQuery maxWidth={767}>
                <p>mobile </p>
            </MediaQuery>
        </div>
    );
}

export default App;

이것까지만 보면 react-media와 react-responsive는 취향에 따라 선택해서 사용하면 되겠지만, react-responsive에서는 Hooks를 제공하고 있습니다.

2.3 Hooks

Hooks 는 개발자가 라이브러리의 특정 라이프사이클 과정에 갈고리로 낚아채는 것처럼 훅(hook)! 할 수 있는 api를 제공해 준 것이라 생각하면 이해하기 쉬울 것 같습니다.

react-responsive에서는 useMediaQuery 라는 Hook을 제공합니다. 아래 코드처럼 현재 useMediaQuery에 media-query 조건을 넣어주면 해당 조건에 따라서 true, false를 리턴해주게 됩니다. 변수를 통해 조건 제어를 하여 렌더링 하는 것이 위에서 사용하던 방식들 보다 깔끔하다고 생각이 됩니다.

import React from "react";
import "./App.css";
import MediaQuery from "react-responsive";
import { useMediaQuery } from "react-responsive";

function App() {
    const isPc = useMediaQuery({
        query: "(min-width: 1024px) and (max-width: 1279px)"
    });
    const isTablet = useMediaQuery({
        query: "(min-width: 768px) and (max-width: 1023px)"
    });
    const isTabletPC = useMediaQuery({ query: "(min-width: 768px) and (max-width: 1279px)" });
    const isMobileTablet = useMediaQuery({ query: "(max-width: 1023px)" });
    const isMobile = useMediaQuery({
        query: "(max-width: 767px)"
    });
    return (
        <div className="App">
            {isPc && <p>pc </p>}
            {isTablet && <p>tablet </p>}
            {isTabletPC && <p>tablet_pc </p>}
            {isMobileTablet && <p>mobile_tablet </p>}
            {isMobile && <p>mobile </p>}
        </div>
    );
}

export default App;

- 브라우저 사이즈별 가변 텍스트 대응

Hooks가 필요한 이유는 V/v-web에서 브라우저 사이즈별 가변 텍스트를 제공이 가능하기 때문입니다.
PC에서는 English 라는 텍스트를 모바일에서는 EN 이라는 텍스트를 보여주려고 할 때 단순 CSS Media-Query 만으로는 작업이 불가능합니다.
매번 두 가지 케이스를 작성해서 display : none,block;으로 분기 처리를 해야 합니다. 하지만 Hooks를 사용하면 아래처럼 해결할 수 있을 것 같습니다.

import React from "react";
import "./App.css";
import MediaQuery from "react-responsive";
import { useMediaQuery } from "react-responsive";

function App() {

    const isTabletPC = useMediaQuery({ query: "(min-width: 768px) and (max-width: 1279px)" });

    const languageText = isTabletPC ? "English" : "EN";

    return (
        <div className="App" >
            {languageText}
        </div>
    );
}

export default App;

2. react-responsive 셋팅

    • 설치typescript를 사용하는 서비스이기 때문에 아래의 @types 모듈도 설치해줘야합니다.

  • npm install @types/react-responsive
  • npm install react-responsive
  • 반응형 컴포넌트 파일 생성
     
    MediaQuery.tsx
  • import React from 'react'; import { useMediaQuery } from 'react-responsive'; const Mobile: React.FC = ({ children }) => { const isMobile = useMediaQuery({ query: '(max-width: 767px)', }); return <React.Fragment>{isMobile && children}</React.Fragment>; }; const Tablet: React.FC = ({ children }) => { const isTablet = useMediaQuery({ query: '(min-width: 768px)', }); return <React.Fragment>{isTablet && children}</React.Fragment>; }; const PC: React.FC = ({ children }) => { const isPc = useMediaQuery({ query: '(min-width: 1024px)' }); return <React.Fragment>{isPc && children}</React.Fragment>; }; const PCwide: React.FC = ({ children }) => { const isPCwide = useMediaQuery({ query: '(min-width: 1280px)' }); return <React.Fragment>{isPCwide && children}</React.Fragment>; }; export { Mobile, Tablet, PC, PCwide };
  • 위에서 알아본 Hooks를 이용해서 아래와 같이 좀 더 깔끔한 방식으로 분기 처리를 할 수도 있습니다. 아래처럼 하게 될 경우 다른 props가 들어가지 않고 네이밍이 직관적이기 때문에 좀 더 효율적일 것이라고 생각합니다. 따로 하나의 파일에 분리해놓고 필요시 해당 컴포넌트만 import 해서 사용하면 될 것 같습니다.

3. 사용 방법

  • pc 일 경우에만 보여주기
    PC일 경우에만 보여야하기 때문에 아래와 같이 <PC></PC> 태그 안에 원하는 요소를 넣어주면 됩니다.
    Gnb.tsx
import React from 'react';
import { PC } from '@components/common/MediaQuery';
// ...중략
const Gnb = () => {
  const { isActive, isExpanded, moreList, languageList } = DEMO_PROPS;

  return (
    <div className={cx('gnb')}>
        <PC>
            {/*PC에서만 보일 요소를 넣으면 됩니다. */}
        </PC>
    </div>
  );
};

export default Gnb;

댓글