| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- 개발 공부
- 리액트
- 프론트엔드
- 입문
- 비전공자
- 모던 자바스크립트 딥 다이브
- Github
- 코딩테스트
- 프로그래머스
- CSS
- 이벤트
- 국비지원교육
- react
- useRef
- useMemo
- 메가바이트스쿨
- 개발자취업부트캠프
- 내일배움카드
- styled-components
- 자료구조
- GIT
- JavaScript
- next.js
- 모던 딥 다이브 자바스크립트
- 자바스크립트
- 패스트캠퍼스
- 알고리즘
- 공식문서
- MegabyteSchool
- TypeScript
- Today
- Total
개발 기록 남기기✍️
모바일 환경에서 키패드 등장에 따른 반응형 구현하기 본문
💥 Issue
마주친 상황은 다음과 같습니다.
☝🏻 다음 버튼은 기본적으로 화면 하단에 위치하고, 키패드가 등장하면 버튼은 줄어든 화면만큼 키패드와 함께 올라가야 해요
뭐야~ 레이아웃 컴포넌트의 height를 100vh로 설정하면 끝나는거 아님?


모바일 환경에서는 높이를 100vh로 설정하더라도 동적 툴바(주소, 네비게이션바)로 인해 스크롤이 발생하기 때문이죠!
해당 현상은 다음 속성을 통해 해결할 수 있습니다.
.fill-screen {
width:100vw;
height: 100vh; /* 새 단위를 지원하지 않는 소수의 브라우저를 위해 */
height:100dvh;
}
- dvh(Dynamic Viewport Height) : svh/lvh 사이에서 탭의 유무에 따라 동적으로 변화
- svh(Small Viewport Height) : 사용자가 볼 수 있는 가장 작은 viewport 높이. viewport 높이에서 모든 인터페이스 요소를 제거한 높이
- lvh(Large Viewport Height) : 사용자가 볼 수 있는 가장 큰 viewport 높이. viewport 높이에서 모든 인터페이스 요소를 포함한 높이
오 그럼 이제 다 해결된거 아님?
문제를 해결하기 위해 작성했던 코드는 다음과 같아요.
export default function Home() {
return (
<main className="flex fill-screen p-5 flex-col items-center justify-between">
<input
className="w-full border-slate-500 border-[1px] p-2"
type="number"
placeholder="내용을 입력해주세요"
/>
<button className="w-full p-4 bg-yellow-400 rounded-xl" type="button">
다음
</button>
</main>
);
}
잘 동작했을까요?


어림도 없었습니다.
아니 그럼 이 문제를 어떻게 해결할 수 있단 말임?
문제를 해결하기 위해서는 모바일 브라우저에서의 viewport에 대해 짚고 넘어가야 해요.
🔎 모바일 브라우저의 document viewport
모바일 브라우저에는 Layout viewport와 Visual viewport 2개의 뷰포트가 존재합니다.

- Layout viewport : 스크롤로 인해 화면 밖으로 표시되는 콘텐츠를 포함한 웹 페이지의 전체 레이아웃
- Visual viewport : 사용자 디바이스 내에서 웹 페이지의 현재 표시되는 부분
가상 키보드(On-Screen Keyboard), 줌인 / 줌아웃과 같은 이벤트는 layout viewport에 영향을 주지 않고 visual viewport의 변경을 일으킵니다.
이 때문에 레이아웃 컴포넌트의 height를 동적으로 설정해도 키패드 등장 시 height가 줄어들지 않았던 것이었어요!
따라서 Visual Viewport Web API를 사용하여 visual viewport에 resize 이벤트가 발생했을 때 visual viewport의 height를 가져와야 합니다.
🧐 window.innerHeight로는 안되나요?
Android, iOS Chrome에서는 키보드가 열리면 window.visualViewport.height와 window.innerHeight가 같이 줄어들지만, iOS Safari 브라우저에서는 visualViewport값이 독립적으로 변경되기 때문에 visualViewport에 대한 이벤트 리스너를 추가해주어야 합니다.

사파리….. 이 자식….. 마음에 안들어…..!!!!!
🌿 Solve
"use client";
import React from "react";
export default function Home() {
const containerRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
const handleResize = () => {
if (!containerRef.current) return;
const height = window.visualViewport
? window.visualViewport.height
: window.innerHeight;
containerRef.current.style.height = `${height}px`;
requestAnimationFrame(handleResize);
};
if (window.visualViewport) {
window.visualViewport.addEventListener("resize", handleResize);
}
return () => {
if (window.visualViewport) {
window.visualViewport.removeEventListener("resize", handleResize);
}
};
}, []);
return (
<main
ref={containerRef}
className="flex fill-screen p-5 flex-col items-center justify-between"
>
<input
className="w-full border-slate-500 border-[1px] p-2"
type="number"
placeholder="내용을 입력해주세요"
/>
<button className="w-full p-4 bg-yellow-400 rounded-xl" type="button">
다음
</button>
</main>
);
}
| iOS Safari | iOS Chrome | Android Chrome |
![]() |
![]() |
![]() |
사파리에서 프레임 뚝뚝 끊기는게 마음에 안드네요 증맬…. 🫤
웹에서 앱 기능을 구현하려니 계속 한계에 부딪히게 되는데, 진짜 앱 배워야 하나... 고민만 남은 트러블 슈팅이였습니다.🫠
'개발 일기장' 카테고리의 다른 글
| [에러일지] onKeyDown 이벤트 중복 실행 문제 (0) | 2023.07.27 |
|---|---|
| [패스트캠퍼스] 기업연계 프로젝트 'Money Bridge' 회고 (0) | 2023.07.05 |
| [패스트캠퍼스] 프론트엔드 부트캠프 4기 과정 후기 (3) | 2023.07.02 |
| [패스트캠퍼스] 프론트엔드 부트캠프 12주차 후기✨(과제 끝!!!) (0) | 2023.03.05 |
| [패스트캠퍼스] 프론트엔드 부트캠프 11주차 후기✨ (0) | 2023.02.25 |


