본문으로 바로가기

[사파리] 노치, 홈 네비게이터바 해결 방법

category REACT/TROUBLE SHOOTING 2025. 3. 27. 14:48

React 웹뷰로 기능을 만들다보니 디자인적으로 어려운참 많다. 

특히나 IOS 와 안드로이드를 동시에 해결하려고 하다보니 보통은 안드로이드 기준에서는 잘되는데 IOS 에서는 안되는 경우가 너무나도 많았다. (그지 같은 사파리..)

이번에도 IOS 에서만 이상하게 동작하는 문제였는데 

IOS 에서는 노치와 홈 네비게이터바 라는 개념이 있다. 노치는 화면 상단에 와이파이나 시간 개념처럼 나오는 상태창을 의미하고, 홈 네비게이터바는 하단에 바처럼 생긴 부분이고 핸드폰으로 보면 라운딩된 부분이다. 

보통 화면 전체 크기를 잡을 때 

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

이런식으로 잡기 마련인데.. 문제는 사파리 웹뷰의 경우는 노치와 홈 네비게이터 바까지 영역이 침범해서 문제가 발생한다. 

원래 AI 에게 문제를 물어보았으나 AI의 답변은 

viewport-fit=auto (기본값)

브라우저가 자동으로 결정합니다
일반적으로 contain과 동일하게 동작


viewport-fit=contain

웹 콘텐츠가 안전 영역(safe area) 내에만 표시됩니다
노치나 홈 인디케이터가 있는 영역에는 콘텐츠가 표시되지 않습니다
화면 가장자리에 검은색 여백이 생길 수 있습니다


viewport-fit=cover

웹 콘텐츠가 화면 전체를 덮습니다
노치, 라운드 코너, 홈 인디케이터가 있는 영역까지 콘텐츠가 확장됩니다
이 설정을 사용할 때는 env(safe-area-inset-*) CSS 함수와 함께 사용해야 중요 콘텐츠가 가려지지 않습니다

이런 느낌이 답변을 주었다. view port 를 cover 로 변경하고, 

env(safe-area-inset-top)    /* 상단 안전 영역 (노치 등) */
env(safe-area-inset-right)  /* 우측 안전 영역 */
env(safe-area-inset-bottom) /* 하단 안전 영역 (홈 인디케이터 등) */
env(safe-area-inset-left)   /* 좌측 안전 영역 */

env 를 사용해서 해당 영역에 대한 높이를 조절하라.. 라는 것이였는데 


테스트 해본 결과 내 아이폰에서는 이상하게 적용이 안되었다.

 

Top 으로 너무 올라가있거나 Bottom 으로 너무 내려가있거나.. 중간에 적당히 개념이 없는거 같은 느낌이였다. 

AI에게 계속 문의 해보았으나, script 로 실시간으로 사이즈를 잡으라고 대답해줬는데 

useEffect(() => {
    // iOS 감지
    const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream;
    setIsNonIOS(!isIOS); // iOS가 아닌 경우 true 설정

    appLog(`디바이스 감지: iOS=${isIOS}, 비iOS=${!isIOS}`);

    // 즉시 실행 함수로 초기 높이 설정
    const setInitialHeight = () => {
        // 실제 가시 영역 높이로 --vh 변수 설정
        const vh = window.innerHeight * 0.01;
        document.documentElement.style.setProperty('--vh', `${vh}px`);

        // 강제 리사이즈 트리거
        window.dispatchEvent(new Event('resize'));

        // 로그 기록
        appLog(`초기 높이 설정: ${window.innerHeight}px, vh: ${vh}px, 비iOS=${!isIOS}`);
    };

    // 초기 실행 및 연속 실행 (iOS의 지연 로딩을 위한 여러 번의 시도)
    setInitialHeight();

    // 연속적으로 여러 번 시도하여 정확한 크기를 얻음
    const timers = [
        setTimeout(setInitialHeight, 100),
        setTimeout(setInitialHeight, 300),
        setTimeout(setInitialHeight, 500)
    ];

    // iOS에서만 추가 설정 실행
    if (isIOS) {
        // iOS에서 Safari UI를 강제로 표시하기 위한 트릭
        const triggerScroll = () => {
            window.scrollTo(0, 1);
            setTimeout(() => {
                window.scrollTo(0, 0);
                // 스크롤 후 높이 재설정
                const vh = window.innerHeight * 0.01;
                document.documentElement.style.setProperty('--vh', `${vh}px`);
            }, 100);
        };

        // 로드 후 약간의 지연을 두고 실행
        setTimeout(triggerScroll, 300);
    }

    // 컴포넌트 언마운트 시 타이머 정리
    return () => {
        timers.forEach(timer => clearTimeout(timer));
    };
}, []);

이런 식으로 시작할때 설정해주라고 알려주고 있었다.. 
근데 이 부분도 뭔가 적용이 안되는 것이였다. 느낌상 시작하는 화면은 적절한 window.innterHeight 로 측정이 안되는거 같은 기분이 들었고. 일단 다시 초기 설정으로 돌아가기로 했다.

viewport-fit=cover

env(safe-area-inset-top)    /* 상단 안전 영역 (노치 등) */
env(safe-area-inset-right)  /* 우측 안전 영역 */
env(safe-area-inset-bottom) /* 하단 안전 영역 (홈 인디케이터 등) */
env(safe-area-inset-left)   /* 좌측 안전 영역 */

이 기존 코딩은 다 빼버리고 원래대로 일단 돌아왔다. 

그리고 설정은 

viewport-fit=contain

이렇게 변경했다. 

이렇게 변경하고 나서는 시작화면일때만 사이즈가 크게 나오고 움직이거나 터지 같은 액션을 가하면 내가 원하는 사이즈로 변경됐다. 아마 위에서 AI 가 알려준 Scrpit 가 동작은 하는거 같은데 초기 설정만 문제가 있었던거 같다. 

그리고 여러가지 시도해보다가 

const BackgroundContainer = styled('div')<ContainerProps>(({ isNonIOS }) => ({
    width: '100%',
    // iOS가 아닌 경우 100, iOS인 경우 99.5
    height: `calc(var(--vh, 1vh) * ${isNonIOS ? 100 : 99.5})`,
    overscrollBehavior: 'none',
}));


이런식으로 ios 일때만 height 를 99.5 로 수정해놨더니.. 신기하게 위의 script 가 시작부터 동작한다. 
아마 기존의 사이즈가 100일때는 화면이 꽉잡아먹어서 해당 script 가 트리거를 하지 못한 모양이다. 

아래는 해결 영상 첨부
뭔가 지잉 거리는거 같긴 한데 이정도면 기존에 비해서 아주 만족한다.


해결영상