- claudedocs 폴더 구조 재정리: archive/sessions, guides/migration·mobile·universal-list, refactoring 분류 - 오래된 세션 컨텍스트/체크리스트 문서 정리 (아카이브 이동 또는 삭제) - AuthContext → authStore(Zustand) 전환 시작, RootProvider 간소화 - GenericCRUDDialog 공통 다이얼로그 컴포넌트 추가 - PermissionDialog 삭제 → GenericCRUDDialog로 대체 - RankDialog/TitleDialog GenericCRUDDialog 기반으로 리팩토링 - toast-utils.ts 삭제 (미사용) - fileDownload.ts 개선, excel-download.ts 정리 - menuStore/themeStore Zustand 셀렉터 최적화 - useColumnSettings/useTableColumnStore 기능 보강 - 세금계산서/견적/작업자화면/결재 등 소규모 개선 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.6 KiB
4.6 KiB
폴더블 기기(Galaxy Fold) 레이아웃 대응 가이드
작성일: 2026-01-09 적용 파일:
AuthenticatedLayout.tsx,globals.css
문제 현상
Galaxy Fold 같은 폴더블 기기에서 넓은 화면 ↔ 좁은 화면 전환 시:
- 사이트 너비가 정확히 계산되지 않음
- 전체 레이아웃이 틀어짐
- 화면 전환 후에도 이전 크기가 유지됨
원인 분석
1. window.innerWidth의 한계
// 기존 코드
window.addEventListener('resize', () => {
setIsMobile(window.innerWidth < 768);
});
- 폴더블 기기에서 화면 전환 시
window.innerWidth값이 즉시 업데이트되지 않음 resize이벤트가 불완전하게 발생
2. CSS 100vh / 100vw 문제
/* 기존 */
height: 100vh; /* h-screen */
- Tailwind의
h-screen은100vh로 계산됨 - 폴더블 기기에서 viewport units가 늦게 재계산되어 레이아웃 깨짐
해결 방법
1. visualViewport API 사용
window.visualViewport는 실제 보이는 viewport 크기를 더 정확하게 반환합니다.
// src/layouts/AuthenticatedLayout.tsx
useEffect(() => {
const updateViewport = () => {
// visualViewport API 우선 사용 (폴더블 기기에서 더 정확)
const width = window.visualViewport?.width ?? window.innerWidth;
const height = window.visualViewport?.height ?? window.innerHeight;
setIsMobile(width < 768);
// CSS 변수로 실제 viewport 크기 설정
document.documentElement.style.setProperty('--app-width', `${width}px`);
document.documentElement.style.setProperty('--app-height', `${height}px`);
};
updateViewport();
// resize 이벤트
window.addEventListener('resize', updateViewport);
// visualViewport resize 이벤트 (폴드 전환 감지)
window.visualViewport?.addEventListener('resize', updateViewport);
return () => {
window.removeEventListener('resize', updateViewport);
window.visualViewport?.removeEventListener('resize', updateViewport);
};
}, []);
2. CSS 변수 + dvw/dvh fallback
/* src/app/[locale]/globals.css */
:root {
/* 폴더블 기기 대응 - JS에서 동적으로 업데이트됨 */
--app-width: 100vw;
--app-height: 100vh;
/* dvh/dvw fallback (브라우저 지원 시 자동 적용) */
--app-height: 100dvh;
--app-width: 100dvw;
}
| 단위 | 설명 |
|---|---|
vh/vw |
초기 viewport 기준 (고정) |
dvh/dvw |
Dynamic viewport - 동적으로 변함 |
svh/svw |
Small viewport - 최소 크기 기준 |
lvh/lvw |
Large viewport - 최대 크기 기준 |
3. 레이아웃에서 CSS 변수 사용
// 기존: h-screen (100vh 고정)
<div className="h-screen flex flex-col">
// 변경: CSS 변수 사용 (동적 업데이트)
<div className="flex flex-col" style={{ height: 'var(--app-height)' }}>
작동 원리
┌─────────────────────────────────────────────────────┐
│ 폴드 전환 발생 │
│ ↓ │
│ visualViewport resize 이벤트 발생 │
│ ↓ │
│ updateViewport() 실행 │
│ ↓ │
│ CSS 변수 업데이트 (--app-width, --app-height) │
│ ↓ │
│ 레이아웃 즉시 재계산 │
└─────────────────────────────────────────────────────┘
브라우저 지원
| API/속성 | Chrome | Safari | Firefox | Samsung Internet |
|---|---|---|---|---|
visualViewport |
61+ | 13+ | 91+ | 8.0+ |
dvh/dvw |
108+ | 15.4+ | 101+ | 21+ |
visualViewport미지원 시 →window.innerWidth/Heightfallbackdvh/dvw미지원 시 → JS에서 계산한 값으로 대체
관련 파일
| 파일 | 역할 |
|---|---|
src/layouts/AuthenticatedLayout.tsx |
viewport 감지 및 CSS 변수 업데이트 |
src/app/[locale]/globals.css |
CSS 변수 선언 및 fallback |