mimizae 님의 블로그
마이페이지 min-height 문제 본문
37기 DIVE SOPT에서 진행한 CareNA 프로젝트에서의 트러블 슈팅 과정을 담은 글입니다! 🙌🏻

💡 문제점
CareNA의 마이페이지를 구현하는 과정에서, 페이지의 콘텐츠 양과 상관없이 항상 화면 전체 높이를 채우도록 만들고 싶었다.
하지만 실제로 페이지를 렌더링해보니, 화면 전체 높이를 차지하기는커녕 페이지 내부 콘텐츠의 높이에 따라 화면이 줄어드는 문제가 발생했다. 특히 콘텐츠가 적은 페이지에서는 하단에 배경이 비어 보이는 현상이 눈에 띄게 나타났다.

그래서 global.css를 확인해 보니 이미 이렇게
body와 #root 에 다음과 같이 min-height 를 설정해두고 있었다… 왜지!! 왜 전체 높이가 안 먹힐까!
body {
min-height: 100vh;
min-height: 100dvh;
}
#root {
min-height: 100vh;
min-height: 100dvh;
}
💡 문제 상황의 구조
우리 라우팅 구조는 최상위에서 Layout 컴포넌트가 공통 레이아웃을 담당하고, 각 페이지는 Outlet 을 통해 그 하위에 렌더링되는 형태였다.
🔻 Router.tsx 코드 전문 🔻
export interface RouteHandle {
header?: HeaderVariant;
title?: string;
}
export const router = createBrowserRouter([
{
path: ROUTE_PATH.LOGIN,
element: <LoginPage />,
},
{
element: <ProtectedRoute />,
children: [
{
path: ROUTE_PATH.LAYOUT,
element: <Layout />,
children: [
{
index: true,
element: <Navigate to={ROUTE_PATH.HOME} replace />,
},
{
path: ROUTE_PATH.HOME,
element: <HomePage />,
handle: { header: "main" } satisfies RouteHandle,
},
{
path: ROUTE_PATH.MY_PAGE,
element: <MyPage />,
handle: {
header: "back",
title: "마이페이지",
} satisfies RouteHandle,
},
{
path: ROUTE_PATH.HEALTH_TIP,
element: <HealthTipPage />,
handle: {
header: "back",
title: "생활 속 건강 팁",
} satisfies RouteHandle,
},
{
path: ROUTE_PATH.HEALTH_TIP_DETAIL,
element: <HealthTipDetailPage />,
handle: {
header: "back",
title: "건강 팁 상세",
} satisfies RouteHandle,
},
{
path: "*",
element: <></>, // TODO: 추후 NotFound 컴포넌트 추가
},
],
},
],
},
]);
구조를 단순화하면 다음과 같다.
- body
- #root
- Layout
- Header
- main
- Outlet (각 페이지 컴포넌트)
- Layout
- #root
즉, 실제 페이지 컴포넌트들은 Layout의 자식 요소였다.
이때 중요한 점은…!!!! body와 #root에 설정한 min-height가 자동으로 모든 하위 컴포넌트에 적용되지 않는다는 사실이었다.
💡 원인
문제의 핵심은 height와 min-height가 상속되는 속성이 아니라는 점에 있다.
(이걸 단단히 착각하고 있었다 ㅠㅠ)
1️⃣ height는 상속되지 않는다
CSS에서 height는 기본적으로 상속되지 않는다.
자식 요소의 높이는 부모의 높이를 자동으로 따라가지 않으며, 부모의 높이를 기준으로 계산되기 위해서는 부모 요소에 명시적인 height 값이 존재해야 한다.
즉, 부모가 height: auto인 상태라면 자식이 height: 100%를 가져도 기준이 되는 값이 없기 때문에 실제 높이가 계산되지 않는다!!!!
2️⃣ min-height 역시 전파 되지 않는다!
min-height 또한 상속되는 속성이 아니다.
부모 요소에 min-height: 100vh를 설정했다고 해서 그 값이 자식 요소에게 자동으로 적용되지는 않는다.
이 경우 브라우저는 다음과 같이 동작한다.
- body와 #root는 최소 화면 높이를 보장
- 그 안의 Layout, main, 페이지 컴포넌트는 콘텐츠 크기만큼만 높이를 차지
- 결과적으로 페이지 전체 높이가 아닌 내용 기반 높이로 레이아웃이 결정됨…!!!!
🤔 왜 width에서는 이런 문제가 덜 느껴질까?
이 문제는 width와 비교하면 더 이해하기 쉽다.
width: 100%는 부모의 가로 폭을 기준으로 계산되는데, 대부분의 경우 부모 요소는 이미 화면 너비를 차지하고 있다.
그래서 별다른 설정 없이도 가로 길이는 자연스럽게 맞아떨어진다… ☠️☠️
💡 해결
다시 한번 상기해 보자면, 문제의 원인은 body와 #root에는 화면 높이에 대한 기준이 있었지만 실제로 화면을 채워야 하는 페이지 컴포넌트에는 그 기준이 전달되지 않았다는 점이었다.
height나 min-height는 상속되는 속성이 아니기 때문에, 부모 요소가 아무리 min-height: 100vh를 가지고 있어도 자식 요소는 기본적으로 자신의 콘텐츠 높이만큼만 렌더링된다.
그래서 해결 방법은 레이아웃 구조를 더 복잡하게 바꾸는 것이 아니라, 전체 높이가 필요한 페이지 루트 요소에 직접 높이를 명시하는 것이었다.
return (
<div className="min-h-dvh bg-white px-[2rem]">
...
</div>
);
이렇게 각 페이지의 최상위 div에 min-h-dvh를 지정해주면,
- 해당 페이지 컴포넌트가 뷰포트 높이를 기준으로 레이아웃을 계산하게 되고
- 내부 콘텐츠가 적더라도 화면을 꽉 채우며
- 하위 요소들의 flex, sticky, absolute 레이아웃도 안정적으로 동작한다
결국 이 문제는 왜 body에 높이를 줬는데 적용되지 않지?가 아니라, 어떤 요소가 실제로 화면 전체를 책임져야 하는가?를 명확히 하지 않았던 것에 가까웠다…!!!!
'FE' 카테고리의 다른 글
| drop-down 공통 컴포넌트 구현 (0) | 2026.02.18 |
|---|---|
| button 공통 컴포넌트 구현 (0) | 2026.02.16 |
| ticker 구현기 (0) | 2026.02.09 |
| RouteHandle은 뭐고, 왜 라우트 단에서 헤더를 관리할 수 있을까? (0) | 2026.02.03 |
| React Route 초기 세팅 (0) | 2026.02.02 |