[Final Project] 모바일에서 브라우저 주소창을 고려한 css height 설정 - svh

liinyeye·2024년 8월 12일
0

Project

목록 보기
41/44

각기 다른 레이아웃을 가진 페이지마다 동일한 컴포넌트를 사용하면서, 모바일 환경에서 다른 높이 값 때문에 어떻게 해결해야할지 이런 저런 방법을 써보며 고생을 좀 많이 했다.

모바일 환경에서 더 정확한 전체 높이 계산을 위해 찾은 방법으로는

import { useEffect } from "react";

export function useVhFix() {
  useEffect(() => {
    function setVH() {
      let vh = window.innerHeight * 0.01;
      document.documentElement.style.setProperty("--vh", `${vh}px`);
    }

    setVH();
    window.addEventListener("resize", setVH);

    return () => window.removeEventListener("resize", setVH);
  }, []);
}
.full-height {
  height: 100vh; /* 폴백 */
  height: calc(var(--vh, 1vh) * 100);
}

모바일 특히 Safari에서는 주소 표시줄이 100vh에 포함되지 않아 레이아웃 문제가 발생했는데, 이를 위해 정확한 vierport 높이 계산을 하는 것을 도와준다.

실제 viewport 높이의 1%를 계산하여 --vh라는 CSS 사용자 정의 속성(변수)에 설정하고 화면 크기가 변경될 때마다(예: 기기 회전, 주소 표시줄 숨김/표시) 높이를 재계산하고 CSS 변수를 업데이트해준다.

그리고 컴포넌트 언마운트 시 이벤트 리스너를 제거하여 메모리 누수를 방지해준다.

이 훅은 최상위 컴포넌트 레이아웃에서 호출하는데, 해당 훅을 사용하려면 클라이언트 컴포넌트로 바꿔야하고 최상위 레이아웃에서는 메타데이터를 사용해야하기 때문에 따로 컴포넌트를 만들어줘서 최상위 레이아웃을 감싸준다.

"use client";

import Providers from "./_providers";
import PathObserver from "./(main)/diary/_components/PathObserver";
import { useVhFix } from "@/hooks/useVhFix";

export default function ClientWrapper({ children }: { children: React.ReactNode }) {
  useVhFix();

  return (
    <div className="full-height flex flex-col">
      <PathObserver />
      <Providers>{children}</Providers>
    </div>
  );
}
import type { Metadata, Viewport } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Providers from "./_providers";
import PathObserver from "./(main)/diary/_components/PathObserver";
import ClientWrapper from "./ClientWrapper";

// const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: {
    template: "%s | PAi",
    default: "PAi"
  },
  description: "Generated by create next app"
  // icons : {
  //   // favicon 위치
  //   icon :
  // }
};

export function generateViewport(): Viewport {
  return {
    width: "device-width",
    initialScale: 1,
    maximumScale: 1,
    userScalable: false,
    viewportFit: "cover"
  };
}

export default function RootLayout({
  children
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className="font-custom">
        <ClientWrapper>{children}</ClientWrapper>
      </body>
    </html>
  );
}

https://css-tricks.com/the-trick-to-viewport-units-on-mobile/

min-height: -webkit-fill-available;

하지만 지금 찾은 방법으로도 자식요소의 사이즈가 헤더를 포함한 높이까지 꽉 채워서 보이는 문제는 해결되지 않았다. 이렇게 저렇게 확인을 해봤을 때, 모바일에서는 h-[calc(100vh-4.5rem)]로 헤더를 제외한 높이를 줘도 계속 스크롤이 생기는 걸로 봐서는 제대로 높이값을 계산하지 못하고 있는 것 같았다.

그래서 찾아보니 기존의 vh랑은 또 다른 화면 높이 단위가 있었다.

dvh

dvh는 Dynamic Viewport Height.
주소표시줄 표시 여부에 따라서 단위가 변함

svh

svh는 Small Viewport Height.
주소표시줄이 표시돼 있을 때를 기준으로 하는 단위

lvh

lvh는 Large Viewport Height.
주소표시줄이 감춰져 있을 때를 기준으로 하는 단위

주의할 점은 dvh는 여러 곳에 사용하면 화면을 전부 다시 그리게 돼 성능 문제가 있을 수 있다. 그럴 때 svh나 lvh를 선택해 사용하면 된다고 한다.

이렇게 간단한 문제였다니....
진작에 구글링 할걸 그랫다................


참고자료

profile
웹 프론트엔드 UXUI

0개의 댓글