본문 바로가기
Web

모바일 iframe Pinch-zoom에 대한 심층 분석과 해결 방안

by Jun_N 2024. 3. 25.

모바일 iframe Pinch-zoom에 대한 심층 분석과 해결 방안

서문

안녕하세요 채용 사이트 잡다에서 모바일 프론트 개발하고 있는 남준영입니다. 오늘은 최근 웹 개발 분야에서는 사용자의 경험을 향상시키기 위해 다양한 기술이 도입되고 있습니다. 그 중 하나가 웹페이지 내에서의 핀치 줌(Pinch Zoom) 기능입니다. 특히, 모바일 환경에서 웹 콘텐츠를 보다 편리하게 탐색할 수 있게 해주는 이 기능은 사용자 만족도를 크게 높여줍니다.

 

본 글에서는 특히 iframe 내에서의 핀치 줌 적용 과정에서 발생한 문제와 그에 대한 해결 방안에 대해 심층적으로 분석해보겠습니다.


 

본론

기존 시스템의 문제점

우선, 기존에 사용되던 시스템에서는 이미지나 일반 텍스트 데이터는 문제없이 처리되었지만, 새롭게 추가된 Jobflex의 <table> 형식 데이터 처리에 있어 문제가 발생했습니다.

 

<table> 형식 데이터가 PC 스타일로 제공되어 모바일 환경에서 화면 너비를 초과하는 현상이 나타나, 사용자가 채용 공고를 한눈에 볼 수 없는 문제가 있었습니다. 또한, 핀치 줌 기능이 이미지에만 적용되어 있어 <table> 형식 데이터에는 적용되지 않는 상태였습니다.

 

모바일 화면에서 공고 사이즈가 깨지는&nbsp;문제

 


해결 방안 탐색

iframe에 콘텐츠를 삽입하기 위한 HTML 구조를 정의하고, 핀치 줌 기능을 위해 pinch-zoom-js 라이브러리를 적용했습니다. 이 과정에서는iframe의 높이 조정, 콘텐츠의 스케일 조정 등 다양한 기술적 도전이 있었습니다.

 

핀치 줌 적용 후 콘텐츠 내용이 축소되면서 생기는 상하단의 빈 공간을 해결하기 위해, 콘텐츠의 스케일이 조정된 만큼 iframe의 높이값도 동적으로 조정하는 로직을 개발해야 했습니다. 이를 위해, window.innerWidth와 pinchElement.offsetWidth를 이용하여 적절한 스케일 값을 계산하고, 이를 기반으로 iframe 내부 콘텐츠의 높이를 재설정하는 방식을 채택했습니다.

 


해결

시도 1. Scale 조정

화면에 window의 width 사이즈에 맞게 내용 자체를 scale조절을 적용하는 방법을 적용했습니다. 하지만 <table>의 경우 <tr> <th> 요소들로 인해 화면 사이즈에 맞게 table을 조정해도 내부 내용들은 축소가 되지 않는 현상이 있었습니다. 단순히 Scale 조정만으로 해결할 수 있는 문제가 아니라 판단해 다른 방법을 찾기로 했습니다.

 

시도 2. 브라우저 확대/축소

브라우저 자체에 핀치 줌을 허용하는 방식 적용 하고자 했습니다. 하지만 공고 content 내용뿐 아니라 브라우저 전체적으로 화면이 확대/축소되기 때문에 어색한 부분이 많아 다른 방안을 찾기로 했습니다.

 

해결 방안. iframe 적용

다른 채용 사이트 공고들은 어떤 방식을 동작하는지 찾아보게 되었고 iframe으로 공고를 묶어서 보여주고 있음을 확인했습니다.

J회사의 경우 iframe형식안에 데이터들이 src로 내려와서 그대로 사용하고 있어 iframe 작업이 용이했습니다.

하지만 제 경우는 <div>…content</div> 형식으로만 내려와서 html 을 붙여주는 형식의 작업이 필요했습니다. 백엔드분께 iframe 작업이 용이하게 기존 시스템의 로직 변경을 논의하였지만 공수 및 일정 산정 문제로 프론트 내부적으로 처리하기로 했습니다.

iframe 작업을 위해 html 형식을 만들고 서버로 부터 받은 content 내용을 넣는 방식으로 작업을 진행했습니다.

const iframePinchZoomUtil = (content: string) => (`
  <!DOCTYPE html>
  <html lang='ko'>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <meta http-equiv="Content-Language" content="ko-KR">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
        <meta name="viewport"
          content="width=device-width, initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no,target-densitydpi=medium-dpi">
        <style type="text/css">
        </style>
      </head>
      <body>
        ${content}
      </body>
  </html>`
);

export default ({ iframePinchZoomUtil });
// iframe 사용 방법

srcDoc = iframePinchZoomUtil(positionJdModel.basicInfo.jobDescription)

<iframe
  className='pinch-frame'
  allow='autoplay; fullscreen; picture-in-picture'
  sandbox="allow-same-origin allow-scripts"
  srcDoc={srcDoc}
  ref={iframeRef}
  title='iframe'
  scrolling='no'
  width='100%'
/>

기술적 해결

iframe 높이 동적 조정:

iframe 내 콘텐츠의 실제 높이를 기반으로 iframe의 높이를 동적으로 조정하는 방법을 적용했습니다.

이를 통해 사용자가 핀치 줌을 사용하여 콘텐츠를 확대/축소해도 iframe의 높이가 콘텐츠에 맞춰 자동으로 조절되어, 콘텐츠가 정상적으로 표시되도록 했습니다.

// scale 공식 
// windowWidth => 유저가 보는 화면 크기 
// offsetWidth => original width 크기
const scale = (windowWidth - padding) / pinchElement.offsetWidth;
const transHeight = scale * numericPartH; // scale조정된 만큼 height값 조정

 

 

Pinch-zoom-js 라이브러리 활용

핀치 줌 기능을 구현하기 위해 pinch-zoom-js 라이브러리를 활용하였으며, 이를 통해 iframe 내부의 콘텐츠에 대해 핀치 줌 기능을 적용할 수 있었습니다.

이 과정에서 라이브러리의 기능을 최대한 활용하면서도, iframe 내부 콘텐츠의 특성을 고려한 맞춤형 조정이 필요했습니다.

<body>
    <div class='pinch-zoom'>
      ...content
    </div>
    <script>
     //window load 후 실행해야 이미지 높이 파악 가능
     window.addEventListener('load', function () {
     const pinchElement = document.querySelector('.pinch-zoom');
     const contentHeight=window.getComputedStyle(pinchElement).getPropertyValue('height');
 
     //pinchInstance 한번 만들면 pinchContainer div가 생성되는데 이것을 pinchParent로 활용
     const pinchInstance=new PinchZoom.default(pinchElement, {
       draggableUnzoomed: false,
       minZoom: 1,
       setOffsetsOnce:true,
     });
 
     //강제로 만들어준 pinchContainer(pinchParent의 역할을 하는 div)의 style 초기화 및 높이 설정
     const pinchContainerElement = document.querySelector('.pinch-zoom-container');
     
     // scale 된 height값 지정.
     const numericPartH = parseFloat(contentHeight);
     const padding = 16; // Padding value
     const windowWidth = window.innerWidth;
     const scale = (windowWidth - padding) / pinchElement.offsetWidth;
     const transHeight = scale * numericPartH;
     const transHeightPx = transHeight + 'px';
 
     pinchContainerElement.style=null;
     pinchContainerElement.style.height= transHeightPx;
 
     //PinchZoom 재선언으로 기능동작하는 pinchContainer 생성 
     new PinchZoom.default(pinchElement, {
       draggableUnzoomed: false,
       minZoom: 1,
       setOffsetsOnce:true,
     }
     );
   });
  </script>
</body>
  1. 윈도우가 로드된 이후에 이미지 높이 파악이 되기 때문에 윈도우가 로드된 후에 스크립트를 실행합니다.
  2. ‘pinch-zoom’ 클래스를 가진 HTML 요소를 선택합니다. 이 요소의 높이를 contentHeight에 저장합니다 (실제 iframe content들의 height값)
  3. PinchZoom 인스턴스를 생성. 여기서 draggableUnzoomed 옵션을 false로 설정하여 확대되지 않은 상태에서는 드래그를 방지하고, minZoom을 1로 설정하여 최소 확대 비율을 지정합니다. setOffsetsOnce 옵션을 true로 설정하여 오프셋을 한 번만 설정합니다.
  4. pinchZoom 라이브러리에서 자동으로 pinch-zoom-container 클래스를 가진 HTML 요소를 생성합니다. (이 html 요소는 scale, transfrom이 자동으로 설정됩니다.)
  5. pinchElement의 가로 길이에 따라 이미지의 확대 높이 값을 계산하고, 이 값을 transHeightPx에 저장합니다.
  6. pinchContainerElement의 스타일을 초기화하고, 높이를 transHeightPx 값으로 설정합니다.
  7. PinchZoom 인스턴스를 다시 생성하여 pinchContainerElement에 핀치 줌 기능을 적용합니다. 이 때도 동일하게 draggableUnzoomed, minZoom, setOffsetsOnce 옵션을 설정합니다.

 

핀치 줌 시 발생하는 문제 해결:

iframe에서 height가 150px로 고정되어 있기에 pinch-zoom-container가 생성되어도 크기가 150px로 보이게 됩니다.

따라서 iframe에 접근해서 content의 height값만큼 높이를 다시 설정해줘야 합니다. 해당 부분은 AutoHeightIFrame로 컴포넌트화 작업하였습니다.

import React, { useEffect, useRef, useState } from 'react';

interface IFrameProps {
  srcDoc: string;
}
const AutoHeightIFrame: React.FC<IFrameProps> = ({ srcDoc }) => {
  const iframeRef = useRef<HTMLIFrameElement>(null);
  const [iframeHeight, setIframeHeight] = useState<number>(0);
  useEffect(() => {
    const iframe = iframeRef.current;
    const handleLoad = () => {
      if (iframe && iframe.contentWindow) {
        const { body } = iframe.contentDocument || iframe.contentWindow!.document;
        setIframeHeight(body.offsetHeight);
      }
    };
    if (iframe) {
      iframe.addEventListener('load', handleLoad);
    }
    return () => {
      if (iframe) {
        iframe.removeEventListener('load', handleLoad);
      }
    };
  }, []);
  return (
    <iframe
      className='pinch-frame'
      allow='autoplay; fullscreen; picture-in-picture'
      sandbox="allow-same-origin allow-scripts"
      srcDoc={srcDoc}
      ref={iframeRef}
      title='iframe'
      scrolling='no'
      width='100%'
      height={iframeHeight}
    />
  );
};
export default AutoHeightIFrame;
iframe 작업이 끝난&nbsp;공고

결론

iframe 내에서의 핀치 줌 기능 구현 과정은 다양한 기술적 도전을 포함하고 있었습니다. 이 과정에서 기존의 문제점을 식별하고, 여러 가지 해결 방안을 모색하며, 최종적으로 사용자에게 더 나은 콘텐츠 탐색 경험을 제공할 수 있는 방법을 찾아내는 것이 중요했습니다.

 

iframe 은 보안문제로 인해 자체적으로 style적용이 막혀있을뿐더러 transform과 같은 css요소를 가져오는데도 문제가 많았습니다. iframe 자체적으로 스타일을 적용하려고 해서 의미없는 작업도 많이 했고 pinch-zoom-js 라이브러리 자체 문제로 인해서 css 컨트롤이 많이 필요했었는데 html 내부에서 디버깅이 원활하지 않아서 시간이 많이 지체된점이 아쉽게 느껴집니다.

 

하지만 <table>을 내려주는 형식 자체가 PC 사이즈에 맞춰져 있을 뿐더러 모바일에서는 축소돼서 들어가 기존 비율도 중요하기 때문에 데이터 의존적이라는 문제가 있습니다. 누군가 비율이 깨지는 (가로 세로 비율에서 세로가 너무 작은) 관리되지 않은 table 데이터를 넘겨주게 된다면 매우 작게 보인다는 단점이 있습니다.

 

 

최종적으로, 이번 프로젝트를 통해 Iframe과 관련된 깊은 이해를 얻을 수 있었으며, 특히 핀치 줌 기능 구현에 있어서 중요한 기술적 인사이트를 얻을 수 있었습니다. 디버깅과 문제 해결 과정에서 얻은 교훈은 앞으로의 개발 작업에 큰 도움이 될 것이라 생각합니다.