Notice
Recent Posts
Recent Comments
Link
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

사고쳤어요

[React / ts] 모바일 대응하기 본문

웹 풀스택

[React / ts] 모바일 대응하기

kevinmj12 2025. 5. 5. 17:05

웹 페이지를 제작할 때 화면 크기가 변경되었을 때 화면이 잘 출력되도록 대응하는 것은 매우 중요하다.

화면 크기에 맞춰 잘 보여지도록 개발을 하더라도 화면 크기에 따라 의도하지 않은 화면이 보여질 수 있다.

예를 들어 위의 페이지에서는 화면이 정상적으로 잘 보여진다.

그러나 모바일 환경 또는 화면 크기를 줄이면 의도하지 않은대로 화면이 깨지는 것을 볼 수 있다.

이런 일이 발생하지 않도록 화면 크기에 대응하여 보여지도록 해보자.

 

미디어 쿼리(@media)

const BooksListStyle = styled.div<BooksListStyleProps>`
  display: grid;
  grid-template-columns: ${({ view }) =>
    view === "grid" ? "repeat(4, 1fr)" : "repeat(1, 1fr)"};
  gap: 24px;

  @media (max-width: 768px) {
    grid-template-columns: ${({ view }) =>
    view === "grid" ? "repeat(2, 1fr)" : "repeat(1, 1fr)"};
  }
`;

미디어 쿼리를 사용하면 화면 크기에 따라 다른 스타일을 적용할 수 있다.

위 코드에서는 @media (max-width: 768px)를 통해 가로가 0~768px일 때 스타일을 지정해주었고

grid-template-columns를 2로 설정함으로써 가로 공간을 확보하고 스타일이 깨지지 않도록 하였다.

 

또는 hook을 사용하여 현재 화면이 모바일인지 여부를 간단하게 확인할 수도 있다.

// useMediaQuery.ts

import { useEffect, useState } from "react";

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

  useEffect(() => {
    const mediaQuery = window.matchMedia(`(max-width: ${768}px)`);

    const handleChange = (e: MediaQueryListEvent) => {
      console.log(e.matches);
      setIsMobile(e.matches);
    };

    // 초기 값 설정
    setIsMobile(mediaQuery.matches);

    // 이벤트 리스너 등록
    mediaQuery.addEventListener("change", handleChange);

    // 정리(clean up)
    return () => mediaQuery.removeEventListener("change", handleChange);
  }, []);

  return { isMobile };
};

 

// BooksList.tsx

interface BooksListProps {
  books: Book[];
}

const BooksList: React.FC<BooksListProps> = ({ books }) => {
  const [view, setView] = useState<ViewMode>("grid");
  const location = useLocation();

  const { isMobile } = useIsMobile();

  useEffect(() => {
    const params = new URLSearchParams(location.search);
    if (params.get(QUERYSTRING.VIEW)) {
      setView(params.get(QUERYSTRING.VIEW) as ViewMode);
    }
  }, [location.search]);

  return (
    <BooksListStyle view={view} isMobile={isMobile}>
      {books.map((book) => (
        <BookItem key={book.id} book={book} view={view} />
      ))}
    </BooksListStyle>
  );
};

interface BooksListStyleProps {
  view: ViewMode;
  isMobile: boolean;
}

const BooksListStyle = styled.div<BooksListStyleProps>`
  display: grid;
  grid-template-columns: ${({ view, isMobile }) =>
    view === "grid"
      ? isMobile
        ? "repeat(2, 1fr)"
        : "repeat(4, 1fr)"
      : "repeat(1, 1fr)"};
  gap: 24px;

  @media (max-width: 768px) {
    grid-template-columns: ${({ view }) =>
      view === "grid" ? "repeat(2, 1fr)" : "repeat(1, 1fr)"};
  }
`;

export default BooksList;

useMediaQuery.ts에서 useEffect를 통해 화면 크기가 변경되어 768px 미만 / 이상일 때마다 isMobile값을 업데이트한다.

BooksList.ts에서는 이를 활용하여 isMobile일때는 repeat(2, 1fr), 아닐 때는 repeat(4, 1fr)로 설정하였다.