구현기/웹 프로젝트

React로 Full Height 화면을 만들었던 과정

Sadie Kim 2023. 6. 11. 16:36


브라우저 창에 꽉꽉 차는 화면.

Height를 full로 하기 위한 여정을 기록한다.

먼저 간단하게 전체 화면을 구성하는 컨테이너 div태그의 height를 100vh로 주었다.
vh란, 뷰포트 높이에 따라 달라지는 단위이다. 1vh는 뷰포트 높이의 1%이므로, 100vh는 뷰포트의 100%, 즉 full 높이를 말한다고 볼 수 있다.

이렇게 하니 정말 화면이 꽉꽉 차 보인다.
하지만 모바일 화면으로 접속해 보니 이상했다.


간격이 적용되지 않는 문제는 둘째치고, 화면이 묘하게 아래로 처져 있었다.

찾아보니 모바일에서는 vh 계산을 할 때 상단에 위치한 url바와 하단의 네비게이션바의 높이까지 포함하여 계산한다고 한다. 그래서 조금씩 어긋나게 되는 것.

구글링을 통해 해결방안을 구했다.
먼저 url바와 네비게이션바의 높이를 포함하지 않은 나만의 vh를 구할 필요가 있다.*

 - 2024 현재는 이 모바일 URL바의 높이를 제외한 뷰포트 높이를 동적으로 가져오는 dvh 단위를 지원하므로, 이 아래 단락에 서술된 --vh를 새로 만드는 수고로움을 덜 수 있다.


파일에 다음 코드를 추가한다.

  useEffect(() => {
    let vh = 0;
    vh = window.innerHeight * 0.01;
    document.documentElement.style.setProperty("--vh", `${vh}px`);
  }, [window.innerHeight]);

setProperty()를 통해 새로운 스타일 프로퍼티를 지정할 수 있다.
위의 식은 윈도우, 즉 뷰포트의 안쪽 화면 크기(innerHeight)가 바뀔 때마다 vh를 계산한 후 --vh라는 이름의 프로퍼티를 해당 값으로 set해 준다.

그리고 화면에 꽉 채우고자 하는 컨테이너 div태그의 style 코드 중 height 값을 다음과 같이 바꾼다.

height: "calc(var(--vh, 1vh) * 100)"

mui styled를 쓰고 있어서 jsx문법을 따르고 있다.

var()이란 사용자 지정 속성 또는 CSS 변수의 값을 가져올 때 사용한다. 첫 번째 인수는 값을 가져올 사용자 지정 속성의 이름이며, 두 번째 인수는 지정 속성이 유효하지 않을 경우 사용하는 대체값이다.
calc()는 css 내에서 계산식을 지정할 때 쓰는 함수이다.

즉 위 식을 풀어 쓰면 'height의 값으로 지정해 놓았던 --vh 값에 100을 곱한 값을 지정한다. 만약 --vh가 없으면, 1vh에 100을 곱한 값을 지정한다'라고 할 수 있다.

해당 변경사항을 반영하면!


원하던 대로 모바일에서도 반듯하게 꽉찬 화면을 얻을 수 있게 되었다.

그러나 또 문제가 있었다.

만약 화면에 담긴 내용이 100vh의 높이를 넘길 경우, 윗 부분이 말려 올라가고 아랫부분은 바탕색이 깨진다. 즉 제대로 안 나온다.
아무래도 바탕이 되는 컨테이너 div의 height를 100vh로 고정해서 생긴 일 같다.

이를 해결하기 위해 min-height를 사용했다.
컨테이너 div태그의 style 코드 중 height: 로 설정했던 부분을 minHeight: 로 변경해 주었다. 즉 다음과 같이 변경했다.

// height: "calc(var(--vh, 1vh) * 100)",
  minHeight: "calc(var(--vh, 1vh) * 100)",

min-height는 요소의 최소 높이를 설정하는 값이므로, 해당 값을 위에서 구한 height값으로 변경하고 height를 픽스하던 코드를 주석처리해 주면, content가 오버플로우되는 페이지에서 일부 화면이 잘리는 문제가 사라질 것이다.


수정 결과 화면이 잘리는 문제가 해결되었다.
그런데 다른 멀쩡하던 화면에서 문제가 생겼다.


갑자기 화면 내용이 위로 몰렸다.
왤까~
한참 디버깅한 끝에 알아낸 이유는 다음과 같다.

현재 나의 컨테이너 div 컴포넌트 구성은 다음과 같다.

    <AppWrapper>
        <App>{children}</App>
    </AppWrapper>

AppWrapper는 background image를 설정하는 div 스타일드 컴포넌트이고, App은 display: flex와 같은 요소 배치를 담당하는 div 스타일드 컴포넌트이다.
내가 지금까지 신나게 height를 조정했던 부분은 AppWrapper였다.

스타일 선언 코드는 다음과 같다.

const AppWrapper = styled("div")({
  width: "100vw",
  minHeight: "calc(var(--vh, 1vh) * 100)",
  backgroundImage: `url(${RiverFord})`,
  backgroundPosition: "center",
  backgroundSize: "100% 100%",
  backgroundAttachment: "fixed",
});

const App = styled("div")({
  padding: "0.5rem",
  display: "flex",
  backgroundColor: "none",
  flexDirection: "column",
  alignItems: "center",
  justifyContent: "center",
  width: "100%",
  height: "100%",
});

보다시피 App의 height는 100%이다. 즉 부모요소인 AppWrapper의 height에 의존하고 있다.

그런데 border: 1px solid red를 찍어서 확인해 보니 현재 작아진 건 App 컴포넌트였다.
부모요소에 있던 height가 사라졌으니, 이에 따른 상대값을 호출하지 못해서 생긴 일이다.

 

해결책 :
우선 App에 있던 height 속성을 지우고, AppWrapper에 display:'flex'를 추가해 주었다.
display flex와 각 속성에 대해 상세히 설명해 주는 사이트의 링크를 첨부한다.

display 'flex' 선언 시 flex-direction(쌓는 방향 지정 속성) 기본값은 'row'(가로로 쌓기)이고, align-items(교차 축 정렬방법 속성)의 기본값은 'stretch'(컨테이너의 교차 축을 채우기 위해 item의 너비 늘림)이다.

따라서 현재 나의 상태의 경우, AppWrapper에서 display: 'flex'만 선언해 주면 여러 기본값이 적용되어 children 컴포넌트에 해당하는 App의 가로방향의 교차 축 너비, 즉 height가 자동으로 늘어나게 된다.


해결 되었다!