DEV/WebProgramming

[React.js] Viewport와 동적으로 meta 태그 수정하기

9thxg 2023. 1. 31. 17:15

많이 부족했지만 웹 애플리케이션 기능을 모두 구현한 후에 모바일에 대응하는 작업을 했습니다. 다른 일반적인 기능 페이지의 경우 미디어 쿼리를 사용하여 반응형으로 쉽게 구현할 수 있었습니다.

 

🤔 크기가 고정된 문서를 어떻게 한눈에 보여줄까?

문제는 다른 프로젝트에서 문서와 관련된 기능을 현재 프로젝트에 옮겨 사용 중에 있었는데 해당 문서는 크기가 cm로 지정되어 있어 모바일에서 보기엔 맞지 않는 큰 크기였습니다.

 

당장 떠오르는 해결방식은 크게 두 가지였습니다.

  • 페이지 크기를 수정하여 적용하는 방식
  • 문서의 스케일을 줄이는 방식

페이지 크기를 수정하는 방식은 이미 잘 구성되어 있는 문서를 각 부분 다시 크기를 수정하고 맞춰야 하기 때문에 시간상 불가능할 것이라 판단했습니다. 그래서 문서의 스케일을 줄이는 방식을 찾아보았습니다.

 

😎 viewport meta 태그 사용하기

문서의 스케일을 줄이는 방식을 생각해 봤는데 어떤 기능을 사용해야 스케일을 줄여 구현할 수 있을까 검색을 했습니다. 검색 중에 찾은 방식이 meta태그의 viewport를 이용하여 문서를 확인하는 방식이었습니다.

 

viewport란 브라우저에서 웹 콘텐츠를 볼 수 있는 창의 영역을 말합니다. 일반적인 브라우저 환경에서는 웹 콘텐츠가 viewport 크기보다 크더라도 스크롤을 통해 접근할 수 있습니다. 모바일 기기와 같이 좁은 영역의 화면을 가지는 경우 가상 창이나 viewport에서 페이지를 영역 너비에 맞게 렌더링 하여 좁은 영역을 가지더라도 웹 콘텐츠를 볼 수 있게 만듭니다.

 

meta 태그를 사용하는 것이기 때문에 index.html을 확인해 보니 프로젝트엔 이미 viewport가 적용된 상태였습니다.

<meta
    name="viewport"
    content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
/>

 

viewport content에 들어가는 속성들에 대한 정보입니다.

  • width
    • 뷰포트의 너비를 설정하는 속성입니다. 숫자만 입력하게 되면 픽셀 값으로 설정하게 되고 100vw와 같은 값을 적용하기를 원한다면 device-width값이 사용됩니다. (높이 또한 width와 같이 적용할 수 있지만 저는 사용하지 않아서 넘어가겠습니다.)
  • initial-scale
    • 페이지가 처음 로드 되었을 때 확대/축소 값입니다. 최소 0.1 값을 가지고 최대 10의 값을 가집니다. 기본 배율이 1을 기준으로 0.1이라면 1/10 축소된 화면을 10이라면 10배 확대된 화면을 가지게 됩니다.
  • maximum-scale
    • 페이지에서 확대를 허용하는 최대 값을 적용할 수 있습니다. 3보다 작으면 접근성이 떨어집니다.(해당 사항은 확인해보지 못했습니다.) 최솟값을 0.1, 최댓값은 10, 디폴트는 10입니다. (음수값 무시)
  • minimum-scale
    • 페이지에서 축소를 허용하는 최소 값을 적용할 수 있습니다. 최솟값을 0.1, 최댓값은 10, 디폴트는 0.1입니다. (음수값 무시)
  • user-scalable
    • 유저에게 페이지를 확대/축소를 허용할지에 대한 옵션입니다. 의도하지 않은 페이지가 그려질 수 있다면 no를 설정할 수 있지만 WCAG(Web Content Accessibility Guidelines)에 위반됩니다. 또한 iOS의 모바일의 경우 완벽히 차단할 수 없습니다.(저는 크롬브라우저와 사파리를 기준으로 테스트를 진행했고 크롬의 경우 안드로이드와 iOS의 차이가 있었습니다.)
  • shrink-to-fit
    • 사파리 11 버전 이전 버전에서 뷰포트 크기보다 보여줘야 할 값이 크다면 내용을 줄여 보여주는 부분이 있는데 해당 부분을 방지할 수 있는 옵션입니다.

 

저는 다음과 같이 속성을 적용하여 사용했습니다.

<meta
    name="viewport"
    content="width=device-width, initial-scale=0.9, maximum-scale=1.0, minimum-scale=0.9, user-scalable=yes, shrink-to-fit=no"
/>

 

 

🤷‍♀️ 각각 다른 디바이스마다 다르게 적용할 순 없을까?

아이폰, 갤럭시 폰과 아이패드를 이용하여 테스트를 진행했습니다. 기기에 처음 테스트 하였을 때 아이패드의 경우 제가 의도한 대로 잘 나왔지만 아이폰, 갤럭시의 경우는 여전히 보기 힘들었습니다.

initial-scale을 더 줄여서 폰에 맞췄더니 패드에서는 더욱 줄어 작게 보였습니다. 이때 저는 태그를 동적으로 적용할 수 있을까 생각이 들었습니다. 그러기 위해선 현재 브라우저를 사용하는 기기가 무엇인지 확인할 수 있는 방법을 찾아야 했습니다.

react-device-detect라는 라이브러리를 발견하게 되었고 Selector를 사용하여 쉽게 device의 종류를 구분할 수 있었습니다.

device를 구분할 수도 있고 현재 사용하는 브라우저, 심지어 iOS, 안드로이드를 구분할 수 있습니다. 저는 모바일과 태블릿을 구분하기 위해서 isMobileOnly, isTablet 이 두 가지 Selector를 사용하여 진행했습니다.

 

처음에는 해당 태그가 다른 페이지에는 필요가 없다고 생각해서 해당 태그를 제거하고 필요할 때 불러오도록 하고 싶었습니다.

하지만 해당 태그가 존재하지 않으면 모바일에서 잘 보이지 않는 문제가 생겨 기존 방식을 유지하되 태그의 내용이 수정될 필요가 있을 때 수정하도록 변경하였습니다.

해당 프로젝트에서 태그를 수정하는 방법은 react-device-detect 라이브러리의 Selector를 이용하여 useEffect로 해당 페이지가 불러와졌을 때 각 디바이스에 맞는 태그 값을 지정해 주는 방식을 사용했습니다.

 

태그를 수정하는 방법은 document의 querySelector 함수를 사용하여 meta[name=”viewport”] 값을 입력하고 setAttribute 함수를 통해 태그의 속성값을 수정했습니다.

 

아래 적용한 예시의 코드는 메타 태그를 제거한 상태에서 구현한 코드이며 해당 코드가 메타태그가 존재하더라도 정상적으로 작동하여 그대로 두고 사용했습니다. 이점 양해 바랍니다.

// useEffect를 이용하여 페이지가 로드될 때 메타태그 삽입 및 수정
    useEffect(() => {
        // 저는 생년월일을 입력하는 부분이 있어 해당 프로세스를 기준으로 분리하여 적용하였습니다.        
        if (checkCustomer) {
            if (document.querySelector(`meta[name="viewport"]`)) {
                if (isMobileOnly) {
                    document
                        .querySelector(`meta[name="viewport"]`)
                        .setAttribute(
                            "content",
                            "width=device-width, initial-scale=0.5, maximum-scale=1.0, minimum-scale=0.5, user-scalable=yes, shrink-to-fit=no"
                        );
                } else if (isTablet) {
                    document
                        .querySelector(`meta[name="viewport"]`)
                        .setAttribute(
                            "content",
                            "width=device-width, initial-scale=0.9, maximum-scale=1.0, minimum-scale=0.9, user-scalable=yes, shrink-to-fit=no"
                        );
                } else {
                    document
                        .querySelector(`meta[name="viewport"]`)
                        .setAttribute(
                            "content",
                            "width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
                        );
                }
            } else {
            	// 다음과 같이 메타 태그를 생성하고 name과 content를 적용합니다.
                let meta = document.createElement("meta");
                meta.name = "viewport";
                if (isMobileOnly) {
                    meta.content =
                        "width=device-width, initial-scale=0.5, maximum-scale=1.0, minimum-scale=0.5, user-scalable=yes, shrink-to-fit=no";
                } else if (isTablet) {
                    meta.content =
                        "width=device-width, initial-scale=0.9, maximum-scale=1.0, minimum-scale=0.9, user-scalable=yes, shrink-to-fit=no";
                } else {
                    meta.content =
                        "width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no";
                }
                document.getElementsByTagName("head")[0].appendChild(meta);
            }
            
            // 커스텀 vh, vw를 사용하는 경우 아래 코드가 실행되어야 iOS에서 잘 적용됩니다.
            setTimeout(() => setScreenSize(), 100);
        }
    }, [checkCustomer]);

    // 위 useEffect에서 적용해주고 있지만 저는 한번 더 사용하여 적용해주고 있습니다. 
    useEffect(() => {
        setTimeout(() => setScreenSize(), 100);
    }, []);

 

👨‍💻 결과

다음과 같이 메타태그를 통해서 기기 종류에 맞게 서류를 출력할 수 있었습니다.

브라우저
iPhone13, iPad mini6

 

위와 같은 방법을 찾지 못했다면 상당한 노동력이 투자되는 방식을 택할 수밖에 없었습니다. 다행히 방법을 찾았고 react-device-detect란 라이브러리를 다른 곳에서도 활용 중에 있습니다. 사실 시간이 어느 정도 지나고 써서 그 당시에 느꼈던 고뇌와 감정, 지식이 모두 담겨있지 않지만 상당히 좋은 경험이었고 저와 같이 고민하고 찾으시는 분들이 있을 거라 생각하고 글을 작성했습니다. 필요하신 분이 제 글을 보고 도움이 되었으면 합니다! 🙏🙏🙏