Skip to content
This page has been auto-translated and may contain errors.View in English

임베딩 및 iframe

docs.scrimba.com

때로는 당신의 페이지에 원하는 것이 이미 다른 곳에 존재합니다: YouTube의 동영상, 지도의 위치, 다른 회사에서 운영하는 결제 양식. 이를 다시 만드는 대신 그 전체 페이지를 당신의 페이지 안에 배치할 수 있습니다. 이를 수행하는 요소는 <iframe>이며, 차용한 콘텐츠를 크기 조정, 접근성 보장, 안전하게 유지하기 위한 제어 집합이 함께 제공됩니다.

iframe 요소

**iframe**은 다른 웹 페이지를 당신의 페이지 안에 배치합니다. 이름은 "inline frame"의 약자이며, 이것이 좋은 설명입니다: 당신의 페이지에 작은 틀이 있는 창이 있고, 그 창을 통해 완전히 다른 페이지가 보입니다. 블로그 게시물의 YouTube 동영상은 거의 항상 iframe입니다.

당신이 보고 싶은 페이지를 src 속성으로 가리키면, 이는 이미지가 파일을 위해 사용하는 속성과 같습니다:

html
<iframe src="https://www.youtube.com/embed/aqz-KE-bpKQ"></iframe>

이 단일 태그는 YouTube 플레이어를 당신의 페이지로 끌어옵니다. 벽에 창을 다는 것처럼 생각해보세요: 벽은 당신의 페이지이고, 창을 통해 완전히 다른 방이 보입니다.

<iframe>은 별도의 완전한 웹 페이지를 로드하고 당신의 페이지 내에서 직사각형으로 표시합니다. src 속성은 해당 페이지의 주소를 가지고 있으며, 프레임 내의 모든 것, 즉 자신의 HTML, CSS, JavaScript는 당신의 페이지와 분리되어 독립적으로 실행됩니다.

html
<iframe src="https://www.openstreetmap.org/export/embed.html"></iframe>

열기 및 닫기 태그 사이에 작성하는 모든 것은 폴백 콘텐츠이며, 프레임을 지원하기에는 너무 오래된 브라우저에서만 표시됩니다. 실제로는 그런 경우가 없습니다. 프레임은 전적으로 속성을 통해 구성되며, 다음 섹션에서는 실제로 설정할 속성들을 다룹니다.

<iframe>은 **중첩 브라우징 컨텍스트**를 생성합니다: 당신의 문서 내에 포함된 완전하고 독립적인 문서입니다(브라우징 컨텍스트는 단일 페이지가 실행되는 환경이며, 자신의 히스토리, 자신의 전역 범위, DOM의 자신의 복사본을 가집니다). 프레임은 의미 있는 방식으로 당신의 페이지의 DOM 트리의 일부가 아닙니다; 당신의 페이지의 상자 안에 칠해지기만 하는 별도의 페이지입니다.

그 분리가 핵심이며, **동일 출처 정책**에 의해 적용됩니다: 당신의 페이지와 프레임된 페이지는 같은 출처를 공유할 경우에만 서로 읽거나 스크립트할 수 있습니다(출처는 스키마, 호스트, 포트의 조합이므로 https://example.comhttps://other.com은 다른 출처입니다). 같은 출처의 프레임은 iframe.contentWindow를 통해 JavaScript로 접근할 수 있습니다; 교차 출처 프레임은 가리킬 수는 있지만 검사할 수는 없는 밀폐된 상자입니다. 거의 모든 임베드(동영상, 지도, 위젯)는 교차 출처이며, 이것이 정확히 배치할 수 있는 이유입니다.

Junoiframe 요소<iframe>은 당신의 페이지 안에 완전히 다른 웹 페이지를 표시합니다. 마치 다른 방을 들여다보는 작은 창처럼요. src로 어디를 가리키든, 이미지가 파일을 참조하는 방식과 같습니다. 블로그에 있는 YouTube 플레이어? 거의 항상 이것 중 하나입니다.
Junoiframe 요소<iframe>은 별도의 자체 포함 페이지를 로드하고 당신의 페이지의 상자에 표시합니다. 자신의 HTML, CSS, JavaScript는 당신의 페이지에서 격리됩니다. src부터 시작하여 완전히 속성을 통해 설정합니다.
Junoiframe 요소 프레임은 중첩된 브라우징 컨텍스트입니다: 자신의 범위와 DOM을 가진 완전히 분리된 문서이며, 당신의 분기가 아닙니다. 동일 출처 정책이 이를 밀폐된 상태로 유지하므로, 교차 출처 임베드는 표시될 수 있지만 당신의 페이지에서 스크립트할 수 없습니다. 그 벽은 제한이 아니라 기능입니다.

동영상, 지도, 위젯 임베딩

좋은 소식: 임베드 코드를 손으로 작성하는 경우는 거의 없습니다. 임베드하려는 사이트가 당신을 위해 작성합니다. YouTube에서 공유를 클릭한 후 임베드를 클릭하면, 복사할 준비가 된 <iframe>을 제공합니다. 지도, 음악 플레이어, 달력도 모두 같은 방식으로 작동합니다. 그들의 블록을 복사하여 HTML에 붙여넣습니다.

html
<iframe src="https://www.youtube.com/embed/aqz-KE-bpKQ" width="560" height="315"></iframe>

widthheight는 프레임이 얼마나 큰지를 픽셀 단위로 설정합니다. 이 두 숫자를 변경하면 동영상이 당신의 페이지에서 더 크거나 작아집니다.

대부분의 임베드 코드는 출처에서 직접 옵니다. 공유 또는 임베드 버튼을 찾으면, 서비스가 당신을 위해 크기 조정되고 가리키는 <iframe> 블록을 제공합니다. 단순히 붙여넣기보다는 이해할 가치가 있는 세 가지 속성이 있습니다:

html
<iframe
  src="https://www.youtube.com/embed/aqz-KE-bpKQ"
  width="560"
  height="315"
  title="Big Buck Bunny 단편 영화"
  loading="lazy"
></iframe>

widthheight는 프레임의 크기를 픽셀 단위로 설정합니다. title 속성은 스크린 리더를 위해 프레임을 이름 지으며, 링크의 텍스트를 알리는 방식대로 알립니다. 따라서 제목이 없는 프레임은 레이블이 없는 "프레임"으로 읽혀지며 아무도 도움이 되지 않습니다. loading="lazy"는 브라우저에게 리더가 프레임 근처로 스크롤할 때까지 프레임된 페이지 다운로드를 미루도록 지시하며, 이는 페이지 아래의 임베드가 초기 로드를 느리게 하지 않도록 합니다.

고정 픽셀 크기 대신 컨테이너에 맞도록 늘어나야 하는 프레임의 경우, widthheight 속성을 제거하고 CSS로 크기를 조정하며, aspect-ratio를 사용하여 동영상의 비율을 올바르게 유지합니다.

서비스가 제공하는 임베드 스니펫은 합리적인 기본값이지, 최종 결정이 아닙니다. 몇 가지 속성이 프로덕션에서 얼마나 잘 동작하는지 결정합니다.

html
<iframe
  src="https://www.youtube-nocookie.com/embed/aqz-KE-bpKQ"
  title="Big Buck Bunny 단편 영화"
  loading="lazy"
  referrerpolicy="strict-origin-when-cross-origin"
  allowfullscreen
  style="width: 100%; aspect-ratio: 16 / 9; border: 0;"
></iframe>

실제로 title은 선택 사항이 아닙니다: 접근성 기술(페이지를 소리내어 읽는 스크린 리더와 같은 소프트웨어)은 프레임을 랜드마크로 취급하고 제목을 알리므로, 누락된 제목은 읽기 순서에 빈 영역을 남깁니다. loading="lazy"는 프레임의 문서 로드를 뷰포트(페이지의 보이는 영역)에 접근할 때까지 연기합니다. 여기서는 각 프레임이 단일 자산이 아닌 완전한 페이지 로드이므로 중요합니다. **referrerpolicy**는 당신의 페이지의 URL 중 얼마나 많은 부분을 Referer 헤더의 임베드된 서비스로 보낼지 제어합니다(Referer 필드는 브라우저가 요청이 어디에서 왔는지 서버에 알리기 위해 붙이는 필드입니다). 이를 조이면 사용자에 대해 더 적게 유출됩니다. 고정 픽셀 대신 aspect-ratio로 반응형으로 크기를 조정하여 프레임이 작은 화면에서 리플로우되도록 합니다. 지킬 가치가 있는 한 가지 습관: 붙여넣기 전에 스니펫을 읽으세요. 제공자가 선택한 기본값을 상속받고 있기 때문입니다.

Juno동영상, 지도, 위젯 임베딩 거의 절대로 임베드 코드를 직접 작성하지 않습니다. YouTube, 지도, 또는 플레이어의 공유 또는 임베드 버튼을 찾으면, 붙여넣을 <iframe>을 제공합니다. 크기를 조정하려면 widthheight를 변경합니다. 이것이 대부분의 일입니다.
Juno동영상, 지도, 위젯 임베딩 출처에서 임베드 코드를 가져온 후 정리합니다. 스크린 리더가 프레임을 이름 지을 수 있도록 항상 title을 추가하고, 페이지 아래의 프레임이 첫 번째 페인트를 느리게 하지 않도록 loading="lazy"를 추가합니다. 유연한 크기 조정을 위해 픽셀 너비와 높이를 제거하고 CSS aspect-ratio를 사용합니다.
Juno동영상, 지도, 위젯 임베딩 제공자의 스니펫은 시작점입니다. 읽기 순서를 위한 title, 프레임이 전체 페이지 로드이고 단일 자산이 아니기 때문에 loading="lazy", 사용자에 대해 더 적게 유출하도록 하는 엄격한 referrerpolicy, 반응형 크기 조정을 위한 aspect-ratio. 제공자가 선택한 모든 기본값을 상속받고 있으므로, 붙여넣는 것을 읽으세요.

샌드박싱 및 권한

프레임은 완전히 다른 페이지를 실행하기 때문에, 그 페이지가 할 수 있는 것을 제한하고 싶을 수도 있습니다. sandbox 속성이 정확히 그것을 합니다: 프레임을 안전 울타리 뒤에 둡니다.

html
<iframe src="widget.html" sandbox></iframe>

sandbox만으로는, 프레임된 페이지가 가장 엄격한 설정으로 유지됩니다: 자신의 스크립트를 실행할 수 없고, 양식을 제출할 수 없으며, 새 창을 열 수 없습니다. 그러면 임베드가 실제로 필요한 특정 권한을 한 번에 하나씩 돌려줍니다. 마치 누군가에게 방을 빌려주지만 열지 않기를 원하는 보관함을 잠그는 것과 같습니다.

두 속성이 프레임이 할 수 있는 일을 제어합니다. sandbox는 프레임을 제한하고, allow는 기기 및 브라우저 기능에 대한 액세스를 부여합니다.

값 없이 sandbox를 추가하면 프레임된 페이지가 할 수 있는 거의 모든 것을 차단합니다: 스크립트, 양식, 팝업, 그리고 자신을 동일 출처로 취급합니다. 공백으로 구분된 권한 토큰을 나열하여 이를 완화합니다:

html
<iframe
  src="https://example.com/widget"
  sandbox="allow-scripts allow-forms"
></iframe>

allow-scripts는 프레임이 JavaScript를 실행하도록 하고, allow-forms는 양식을 제출하도록 하며, allow-popups는 새 창을 열도록 합니다. 임베드가 필요한 것만 허용합니다. 별개의 allow 속성은 전체 화면, 카메라, 마이크 같은 민감한 브라우저 기능을 관리합니다:

html
<iframe src="https://example.com/call" allow="camera; microphone; fullscreen"></iframe>

기능이 allow에 나열되지 않으면, 프레임된 페이지의 자신의 코드가 요청하더라도 그 기능을 사용할 수 없습니다.

여기에는 두 가지 구별되는 모델이 있으며, 이를 분리하는 것이 가치가 있습니다. sandbox는 프레임에서 기능을 제거하고; allow는 제어되는 기능에 대한 액세스를 부여합니다. 이들은 같은 것의 두 스펠링이 아닙니다.

값 없는 sandbox는 한 번에 모든 제한을 적용합니다: 스크립트 없음, 양식 제출 없음, 팝업 없음, 자동 재생 미디어 없음, 그리고 중요하게도, 프레임은 고유한 불투명 출처로 강제됩니다(다른 것과 일치하지 않는 출처이므로 프레임은 로드된 사이트를 포함하여 누구와도 동일 출처를 주장할 수 없습니다). 토큰으로 기능을 다시 추가합니다:

html
<iframe
  src="https://example.com/widget"
  sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox"
></iframe>

피해야 할 조합이 하나 있습니다. allow-scriptsallow-same-origin을 모두 자신의 출처에서 로드하는 프레임에 부여하면, 프레임된 페이지가 자신의 샌드박스를 제거할 수 있는 코드를 실행할 수 있게 되며, 이는 포인트를 무효화합니다. 이 두 가지를 프레임의 콘텐츠가 완전히 신뢰할 수 있을 때만 쌍으로 만듭니다.

allow 속성은 **권한 정책**입니다(카메라, 마이크, 지리 위치, 전체 화면 같은 민감한 기능 사용을 결정하는 브라우저 메커니즘). 기본값은 거부입니다: allow에서 이름을 지정하지 않은 기능은 스크립트가 요청하든 상관없이 프레임에서 사용할 수 없습니다. 이것이 카메라가 필요한 화상 통화 위젯을 배치하면서도 페이지의 다른 모든 프레임에 카메라를 제공하지 않는 방법입니다.

Juno샌드박싱 및 권한 프레임에 sandbox를 추가하면 임베드된 페이지가 잠금: 스크립트 없음, 팝업 없음, 문제 없음. 그러면 실제로 필요한 권한만 돌려줍니다. 마치 누군가에게 방을 빌려주지만 일부 보관함을 잠그는 것과 같습니다.
Juno샌드박싱 및 권한sandbox는 프레임을 제한하고 allow-scriptsallow-forms 같은 토큰으로 하나씩 이를 완화합니다. 별도의 allow 속성은 카메라, 마이크, 또는 전체 화면을 부여합니다. 각각에 임베드가 실제로 요청하는 것만 제공합니다.
Juno샌드박싱 및 권한 두 모델을 구분하세요: sandbox는 기능을 빼앗고, allow는 제어되는 기능을 부여하며 기본값은 거부입니다. 함정은 동일 출처 프레임에서 allow-scriptsallow-same-origin과 쌍으로 만드는 것입니다. 페이지가 자신의 샌드박스를 잠금 해제할 수 있기 때문입니다. 내부를 완전히 신뢰할 때만 그렇게 하세요.

보안 및 성능 주의 사항

임베드는 편리하지만, 각 프레임은 완전히 별도의 페이지를 로드하므로, 스택이 많으면 당신의 페이지가 느려질 수 있습니다. 두 가지 습관이 문제에 빠지지 않도록 합니다: 신뢰하는 사이트에서만 임베드합니다. 당신은 그들의 페이지를 당신의 페이지로 초대하고 있으며, 필요한 것보다 더 많은 프레임을 추가하지 않습니다.

앞서 언급한 loading="lazy" 속성도 여기서 도움이 되며, 리더가 프레임으로 스크롤할 때까지 프레임을 미룹니다. 동영상이나 지도처럼 실제 작업을 절약할 때 임베드를 사용하고, 일반 링크가 같은 일을 할 수 있으면 건너뜁니다.

추가하는 모든 프레임은 완전한 페이지 로드입니다: 자신의 HTML, 자신의 CSS, 종종 자신의 JavaScript 더미, 모두 다른 서버에서 가져옵니다. 세 개의 제3자 임베드가 있는 페이지는 정말로 네 개의 페이지가 동시에 로드되고 있으며, 이것이 임베드가 많은 페이지가 무거워 보이는 이유입니다. 화면 밖의 프레임에 loading="lazy"는 가장 저렴한 승리이고, 세 개의 임베드보다 하나의 임베드를 선호하는 것이 다음입니다.

속도 이상의 두 번째 우려가 있습니다. 당신의 페이지가 누군가 다른 사람의 프레임 안에 배치되지 않도록 보호할 수 있으며, 이는 공격자가 당신의 사이트를 프레임으로 만들고 사용자를 볼 수 없는 것을 클릭하도록 속이는 트릭을 막습니다. 그 보호는 서버에서 설정되며, 고급 수준에서는 이를 수행하는 두 가지 헤더를 다룹니다.

명확한 눈을 가지고 볼 가치가 있는 두 가지 비용이 있습니다: 프레임이 당신의 성능에 미치는 영향과 프레이밍이 당신의 보안에 할 수 있는 일.

성능 측면에서, 제3자 프레임은 단일 요청이 아닙니다; **중첩 문서 로드**입니다. 제공자의 전체 스택(마크업, 스타일, 글꼴, 분석, 스크립트)이 당신의 페이지 내에서 실행됩니다. 같은 네트워크 및 메인 스레드를 놓고 경쟁하며, 스크립트는 자신의 컨텍스트에서 실행되지만 같은 기기에서이므로, 무거운 임베드는 당신의 자신의 콘텐츠가 상호작용할 수 있게 되는 것을 지연시킬 수 있습니다. loading="lazy"는 화면 밖의 프레임을 미루고, 임베드를 통합하는 것은 단일 임베드를 튜닝하는 것을 이깁니다. 임베드가 무료라고 가정하기 전에 실제 비용을 측정합니다.

보안 측면에서, 위험은 **클릭재킹**입니다: 공격자가 당신의 페이지를 자신의 사이트의 프레임에 로드하고, 이를 보이지 않게 하거나 오버레이하고, 서명된 사용자를 실제 제어를 클릭하도록 속여서 알지 못합니다. 방어는 누가 당신의 페이지를 프레임할 수 있는지 선언하는 것이며, HTML이 아닌 서버의 응답 헤더로 설정됩니다:

  • X-Frame-Options: DENY는 당신의 페이지가 어떤 프레임에도 로드되지 않도록 거부합니다. SAMEORIGIN은 자신의 사이트만 프레임할 수 있도록 허용합니다. 이것은 더 오래되고 더 거친 제어입니다.
  • Content-Security-Policy: frame-ancestors 'self'는 현대적 교체입니다. **frame-ancestors**는 당신의 페이지를 프레임할 수 있는 출처를 정확히 나열합니다(Content-Security-Policy, 또는 CSP, 브라우저가 콘텐츠와 동작의 어떤 소스를 허용할지 알려주는 헤더입니다). X-Frame-Options를 대체하고, 한 가지 총괄 규칙보다는 특정 허용 목록을 허용하며, 로그인이나 양식이 있는 모든 것에서 설정할 것입니다.

방향에 주의합니다: sandboxallow는 당신이 임베드하는 페이지를 제어하고, frame-ancestorsX-Frame-Options는 누가 당신을 임베드할 수 있는지 제어합니다. 두 방향 모두 중요하며, 다른 장소에서 구성됩니다.

Juno보안 및 성능 주의 사항 모든 프레임은 완전히 별도의 페이지를 로드하므로, 많은 스택이 당신의 페이지를 느리게 합니다. 신뢰하는 사이트에서만 임베드하고, 개수를 낮게 유지하며, loading="lazy"에 기댑니다. 일반 링크가 같은 일을 할 수 있으면, 링크를 사용합니다.
Juno보안 및 성능 주의 사항 세 개의 임베드는 한 번에 네 개의 페이지 로드를 의미하므로, 각 프레임을 실제 비용으로 취급합니다. 화면 밖의 프레임에 대한 loading="lazy"는 저렴한 승리이고, 더 적은 임베드는 하나를 튜닝하는 것을 이깁니다. 또한 고급 수준에서 다루는 두 개의 서버 헤더가 있는 보호할 가치가 있는 프레이밍 위험도 있습니다.
Juno보안 및 성능 주의 사항 제3자 프레임은 당신의 페이지 내에서 제공자의 전체 스택을 실행하고 당신의 네트워크 및 메인 스레드를 놓고 경쟁하므로, 무료라고 가정하기 전에 측정합니다. 보안 측면에서는 당신을 프레임할 수 있는 사람을 선언하여 클릭재킹을 중지합니다: CSP의 frame-ancestors는 현대적 제어이고, X-Frame-Options는 더 오래된 것입니다. 당신이 임베드하는 페이지를 sandbox로 보호하고 당신을 임베드하는 것을 frame-ancestors로 보호하므로, 방향을 기억하세요.