[React] 반응형 처리하기 (w. react-responsive)

오형상·2024년 12월 24일
0

Ficket

목록 보기
19/27

오픈 티켓 화면의 반응형 구현을 위해 Figma에서 화면 설계를 진행했습니다. 아래는 PC와 모바일 화면 설계 이미지입니다.

오픈 티켓 PC 화면
오픈 티켓 PC 화면

오픈 티켓 모바일 화면
오픈 티켓 모바일 화면


설계 차이점

PC와 모바일 화면의 구성은 단순히 크기만 다른 것이 아니라 UI 구조와 인터랙션 방식에서 근본적인 차이가 있습니다:

  • PC 화면

    • 게시판 형식으로 데이터가 테이블 형태로 표시됩니다.
    • 페이징은 하단의 버튼을 통해 이동하며, 사용자가 명확하게 페이지 구분을 할 수 있습니다.
  • 모바일 화면

    • 카드형 그리드 레이아웃에 포스터 이미지가 포함되어 시각적인 요소를 강조했습니다.
    • 페이징은 무한 스크롤 방식으로 설계되어, 사용자가 연속적으로 콘텐츠를 탐색할 수 있도록 설계되었습니다.

이처럼 PC와 모바일은 UI 구조와 동작 방식에서 근본적인 차이가 있었으며, 이를 해결하기 위해 화면 크기와 디바이스에 따라 UI와 로직을 다르게 처리해야 했습니다.


검토한 기술

1. CSS Media Queries

CSS Media Queries는 가장 기본적인 반응형 웹 구현 기술로, 특정 화면 크기에서 다른 스타일을 적용할 수 있습니다.

장점

  • 브라우저에서 기본적으로 지원되므로 추가적인 라이브러리 설치가 필요하지 않음.
  • HTML과 CSS만으로 간단히 반응형 스타일을 구현 가능.
  • Tailwind CSS와 함께 사용하면 미디어 쿼리를 간결하게 작성할 수 있음.

단점

  • 구조적인 차이를 처리하기 어려움: UI 구성이나 동작 로직이 화면 크기에 따라 달라져야 할 때 CSS만으로는 해결이 어렵습니다.
  • 로직과 스타일의 분리: 유지보수 시 코드 간의 의존성이 복잡해질 수 있음.

코드 예시

/* CSS Media Queries 예제 */
body {
  font-size: 16px;
}

@media (max-width: 768px) {
  body {
    font-size: 14px;
  }
}

결론
CSS Media Queries는 스타일 변경이 주가 되는 경우에는 적합하지만, UI 구조와 동작 로직을 화면 크기에 따라 다르게 처리해야 하는 이번 프로젝트에는 한계가 있었습니다.


2. window.matchMedia (JavaScript 기반)

window.matchMedia는 브라우저에서 제공하는 Web API로, 화면 크기를 감지하고 조건에 따라 로직을 동적으로 처리할 수 있습니다.

장점

  • 추가적인 라이브러리가 필요하지 않음.
  • 화면 크기 변경에 따른 로직 처리가 가능.
  • 브라우저의 기본 API를 사용하므로 성능에 유리함.

단점

  • 복잡한 코드: 상태 관리와 이벤트 리스너 작성이 필요해 코드가 장황해질 수 있음.
  • React와의 통합 부족: React 생명주기와 직접 통합되지 않음.
  • 재사용성 부족: 동일한 로직을 다른 컴포넌트에서 사용하려면 반복 작성해야 함.

코드 예시

import React, { useState, useEffect } from "react";

const ResponsiveComponent = () => {
  const [isMobile, setIsMobile] = useState(window.matchMedia("(max-width: 768px)").matches);

  useEffect(() => {
    const mediaQuery = window.matchMedia("(max-width: 768px)");
    const handleChange = (e: MediaQueryListEvent) => setIsMobile(e.matches);

    mediaQuery.addEventListener("change", handleChange);
    return () => mediaQuery.removeEventListener("change", handleChange);
  }, []);

  return <div>{isMobile ? "모바일 화면" : "데스크탑 화면"}</div>;
};

결론
window.matchMedia는 간단한 로직에는 유용하지만, 유지보수성과 가독성이 낮아지고, 여러 컴포넌트에서 반복적으로 사용하기엔 비효율적이라고 판단했습니다.


3. React-Responsive

React-Responsive는 React 환경에서 반응형 UI를 구현할 수 있는 라이브러리로, 화면 크기나 디바이스 상태에 따라 조건부 렌더링을 간편하게 처리할 수 있습니다.

장점

  • React에 최적화: React 훅(useMediaQuery)을 통해 조건부 렌더링과 상태 관리를 간단히 처리할 수 있음.
  • 다양한 조건 지원: 화면 크기뿐만 아니라, 색상 모드(다크/라이트), 디바이스 방향(가로/세로), 해상도 등도 지원.
  • 재사용성: 여러 컴포넌트에서 쉽게 사용할 수 있어 코드 중복을 줄임.
  • 가독성 향상: CSS 스타일 변경뿐 아니라 UI 구조나 로직을 조건에 따라 변경할 때도 간결하게 처리 가능.

단점

  • 추가적인 라이브러리 설치가 필요.
  • SSR(Server-Side Rendering) 환경에서는 초기 상태 처리가 필요할 수 있음.

코드 예시

import React from "react";
import { useMediaQuery } from "react-responsive";

const ResponsiveComponent = () => {
  const isMobile = useMediaQuery({ query: "(max-width: 768px)" });

  return <div>{isMobile ? "모바일 화면" : "데스크탑 화면"}</div>;
};

결론
React-Responsive는 React 환경에 최적화되어 있어, UI 구조와 로직 모두를 유연하게 처리할 수 있다는 점에서 가장 적합한 선택이었습니다.


최종 선택: React-Responsive

위 세 가지 기술을 검토한 결과, React-Responsive를 선택했습니다. 이유는 다음과 같습니다:

  1. 구조적 차이를 쉽게 해결 가능
    모바일과 PC에서 UI 구조와 동작 방식이 크게 다르기 때문에, 조건부 렌더링이 필요했습니다. React-Responsive는 useMediaQuery를 통해 크기 조건에 따라 완전히 다른 컴포넌트를 렌더링할 수 있어 적합했습니다.

  2. 로직과 스타일을 통합적으로 관리
    화면 크기에 따라 데이터 요청 방식(API 호출)이나 동작 로직이 달라져야 하는 상황에서, React-Responsive는 로직과 스타일을 한 곳에서 처리할 수 있어 유지보수가 쉬웠습니다.

  3. 가독성과 재사용성
    window.matchMedia 방식보다 코드가 간결하고, 다양한 컴포넌트에서 재사용할 수 있어 작업 생산성을 높일 수 있었습니다.

  4. 커뮤니티와 확장성
    React-Responsive는 오픈소스 라이브러리로 커뮤니티 지원이 활발하며, 화면 크기 외에도 다양한 브라우저 조건(색상 모드, 방향 등)을 처리할 수 있어 확장성이 뛰어났습니다.


설치하기

React-Responsive를 설치하려면 다음 명령어를 사용합니다:

npm install react-responsive
npm install @types/react-responsive

적용하기

import { useMediaQuery } from "react-responsive";
import MobileEventOpenList from "../../components/eventScheduledOpen/MobileOpenEventList.tsx";
import PcEventOpenList from "../../components/eventScheduledOpen/PcOpenEventList.tsx";
import MobileHeader from "../../components/@common/MobileHeader.tsx";
import UserHeader from "../../components/@common/UserHeader.tsx";

const EventScheduledOpen = () => {
  const isMobile: boolean = useMediaQuery({ query: "(max-width: 768px)" });

  return (
    <div>
      {isMobile ? (
        <div>
          <MobileHeader title={"오픈 티켓"} />
          <MobileEventOpenList />
        </div>
      ) : (
        <div>
          <UserHeader />
          <PcEventOpenList />
        </div>
      )}
    </div>
  );
};

export default EventScheduledOpen;

0개의 댓글