본문으로 바로가기

IFRAME과의 DOMAIN 간 통신

category 프로그래밍/jQuery 2018.05.03 13:52


IFRAME과의 domain 간 통신


http://softwareas.com/cross-domain-communication-with-iframes/

HTML5 시대의 업데이트 (2011 년 5 월 6 일)

이 게시물은 수년에 걸쳐 크게 논평되고 연결되어 왔으며 계속해서 많은 트래픽이 발생하므로이 중 많은 부분이 최신 브라우저와 관련성이 없음을 분명히해야합니다. 한편으로는 보안 정책을 조정하고 강화하여 여기 일부 기술을 더 이상 적합하지 않게 만들었습니다. 반면에, 그들은 먼저 도메인 간 의사 소통을 쉽게 할 수있는 기술을 도입했습니다.

최신 브라우저에서는 이러한 목적으로 postMessage 를 사용할 수 있고 사용해야합니다 .

라이브러리 지원을 사용할 수 있습니다. 다음은 모두 postMessage에 대한 인터페이스를 제공합니다 (사용 가능한 경우). 그렇지 않은 경우이 기사에 설명 된 기본 기술로 되돌아갑니다.

이제 원래 게시물로 돌아갑니다 ... (2008 년 3 월 31 일부터)

이 기사에서는 iframe이 다른 도메인에서 온 경우 iframe과 iframe 간의 통신에 대해 설명합니다. 이 작업을 효율적으로 수행 할 수있게 된 것은 현재 커뮤니티에 분명 해지고 있으며 현재 Google, Facebook 및 기타 업체의 프로덕션에서 사용되고 있으며 Ajax, 매시업 및 위젯 / 가젯의 미래에 대한 강력한 영향을 미칩니다. 나는 기술을 조사하고 기사에서 소개 된 몇 가지 데모를 작업 해왔다.

배경 : 도메인 간 통신

아이러니 컬하게도 매시업과 아약스의 세계에서는 두 가지를 함께 사용하는 것이 쉽지 않습니다. Ajax 응용 프로그램은 브라우저에서 실행되며 이러한 응용 프로그램은 어디에서 왔는지 서버와는 이야기하지 않습니다. 따라서 원본 웹 서버를 통해 모든 것을 압축해야 할 때 여러 원본의 콘텐츠를 매시 (mash)하는 것은 쉽지 않습니다. On-Demand Javascript 와 같은 문제를 해결하기 위해 몇 년 동안 해킹 사례가 발생했으며 가장 최근의 사례는 iframe과 관련된 해킹입니다.이 기사에서는이 기사에서 설명 할 것입니다. 나중에 살펴 보 겠지만 iframe 기술은 On-Demand Javascript보다 보안 성이 뛰어날뿐만 아니라 브라우저  에서 iframe 하나에서 다른 곳으로 전달하기에 더 좋은 장소이기도 합니다.

이 기사와 관련하여 데모 애플리케이션 과  가지 변형이 있습니다.

내가 2006 년 6 월 에 제임스 버크 (James Burke)의 Tagneto 블로그 에서 시작한이 해킹에 대한 첫 번째 언급은 오래 전에 일부 분기에서 사용 된 것으로 확신합니다. 이제 Mapplet 에서 Google 제작에 사용됩니다 위젯 컨테이너 통신을 위해 Shindig에서도 사용됩니다. 또한이 기술은 브라우저에서 타사 서버로 안전한 도메인 간 호출을 수행하는 가장 좋은 방법이기도합니다. 따라서 Facebook의 새로운 Javascript 클라이언트 라이브러리에채용되었습니다 .

데모

먼저,이 해킹으로 우리가 할 수있는 일을 살펴 봅시다.

데모

이 데모에서는 iframe의 내용에 영향을주는 최상위 문서에 대한 컨트롤을 제공하고 그 반대의 경우도 마찬가지입니다. 이것은이 기술을 사용하여 양방향으로 통신을 실행할 수 있음을 보여줍니다. 일반적으로이 기술은 서로 다른 도메인에있는 두 개의 브라우저 측 구성 요소 (브라우저 간 통신과는 대조적으로 실제로 서버 통신이 이루어지기는하지만) 사이의 통신입니다.

물리학 법칙 : IFRAME으로 할 수있는 것

해킹을 이해하려면 브라우저의 iframe 및 도메인 정책에 적용 할 때 "물리 법칙"을 이해해야합니다. 일단 제약 조건을 이해하면 패턴 자체가 사소 해집니다. 이 데모 는 이러한 제약 조건을 탐색하고 설명하기 위해 작성되었으며 간단한 코드 예제가 포함되어 있습니다.

정의 I : "창"은 iframe 또는 최상위 창 (즉, "기본"페이지)을 나타냅니다. 우리의 모델에서, 우리는 나무와 같은 계층 구조를 가지고 있습니다.

Law I : 계층 구조의 모든 창은 계층 구조의 다른 창에 대한 핸들을 얻을 수 있습니다. 계층 구조 또는 그들이 어떤 도메인에서 왔는지는 중요하지 않습니다. 올바른 명령을 사용하면 항상 다른 창을 참조 할 수 있습니다. 상위 창은 "상위", "상위 .parent"등으로 액세스되거나 최상위 레벨은 "최상위"로 액세스됩니다. 하위 창은 "window.frames [0]"또는 "window.frames [name]"으로 액세스됩니다. 이름이이 경우에주의 하지 iframe이의 ID , 오히려 iframe 대응의 이름 . (90 년대 후반의 추악한 프레임과 프레임 세트와 관련된이 모든 것들의 레거시 성질을 반영합니다.) 따라서 형제 핸들을 얻으려면 "parent.frames [1]"을 사용할 수 있습니다.

Law II : Windows는 동일한 도메인에 속한 경우에만 서로의 내부 상태에 액세스 할 수 있습니다. 이것은 오히려 전체 교차 도메인 iframe에 kibosh를 넣습니다. iframe 스크립트가 서로 직접 대화 할 수 있다면 모든 일이 그렇게 쉬워 질 것입니다. 그러나 모든면에서 보안 헛소리가 발생할 것입니다. HTML 5는 iframe 사이의 명시적인 통신을 정의 하지만 폭넓게 채택되기까지 더 열심히 생각해야합니다 ...

Law III : 브라우저 보안 정책에 따라 다른 도메인 iframe이 서로의 내부 상태에 액세스하지 못하게하더라도 계층 구조의 모든 창에서 다른 창 위치 / URL을 설정할 수 있지만 읽을 수는 없습니다. 참고 :이 법률에 대한 정확한 세부 정보는 추가 조사가 필요합니다. 다시 말하지만 도메인의 출현 또는 계층 구조의 위치는 중요하지 않습니다. 그것은 항상 다른 창에서 핸들을 얻을 수 있으며 항상 "parent.frames [1] .location.href"와 같이 창 URL을 설정할 수 있습니다. 이렇게하면 창 URL은 창에 관계없이 모든 창에서 공유되는 페이지의 정보 유형 중 하나로 설정됩니다.부모가 자식 윈도우의 URL을 변경할 수는 있지만, 그 반대는 불가능합니다. 자식 창이 자신의 부모 (또는 삼촌, 형제 등)의 URL을 변경하는 것이 얼마나 이상한 지 알 수 있습니다. 내가 아는 유일한 정당성은 프레임 트랩을 벗어나는 오래된 기술입니다. 프레임 트랩을 탈출 할 때 웹 사이트가로드 될 때 최상위 URL (자체 URL과 다른 경우)을 자체 URL로 설정하여 프레임 내에 있지 않은지 확인합니다. 이렇게하면 페이지가 자체 URL로 다시로드됩니다. 그러나 이것은 특별한 경우이며이 많은 여지를 정당화 할만한 가치가있는 것으로 보이지 않습니다. 그래서 나는 왜 당신이 이것을 할 수 있는지 정말로 모르지만, 우리에게는 운이 좋다, 할 수있다!

Law IV : URL의 조각 식별자 (예 : http://example.com/blah#fragmentID와 같이 #로 시작하는 끝에있는 비트)를 변경하면 페이지가 다시로드되지 않습니다. 다른 Ajax 해킹, 북마크 및 페이지 기록을 허용하는 고유 URL 에 익숙하다면 이미 익숙 할 것 입니다. 일반적으로 문서의 "href"속성을 변경하면 문서의 재로드가 발생하지만 조각 식별자 만 변경하면 변경되지 않습니다. 이 지식을 사용하여 페이지 내용을 눈에 띄게 변경하지 않고 스크립트가 스크립트를 검사하고 사용하게하는 방식으로 URL을 기호 적으로 변경할 수 있습니다.

교차 도메인 재미와 이익을위한 물리 법칙의 활용 - 도메인 간 해킹 (URL 폴링 버전)

위의 법률은 도메인 간 통신을 수행하는 데 필요한 모든 것입니다. 이 기술은 Window A가 Window B를 제어하려고한다고 가정하면 간단합니다.

  • 창 A는 창 B의 조각 식별자를 변경합니다.
  • 윈도우 B는 프래그먼트 식별자를 폴링하고 변경을 통지하고, 프래그먼트 식별자에 따라 자신을 업데이트한다.

따 - ​​다 !!! 그 모든 것이 영광 스럽습니다. 물론 이것이 모두 작동하는 이유를 이해하려면 물리 법칙을 알아야했습니다. 이는 단순히 창 A와 창 B가 하나의 공통된 상태 인 URL (URL)과 조각 식별자 만 조작하여 URL을 변경하지 못하게한다는 사실에 의존합니다. 예를 들어, 데모에서 iframe의 URL은 http://ajaxpatterns.org/crossframe/#orange로 변경되고 iframe 스크립트가이를 인식하면 색상을 업데이트합니다.

몇 가지 관찰 :

  • 이것은 어느 방향 으로든 작동합니다. 부모와 자식, 부모와 자식. 데모에서 보여 주듯이.
  • 양 당사자의 협력이 필요합니다. 브라우저 보안 메커니즘을 우회하는 것은 마법 같은 방법이 아닙니다. 윈도우 A가 윈도우 B의 프래그먼트 식별자를 변경하면 윈도우 B가 변경 사항을 처리합니다. 창의 식별자를 폴링 (polling)하는 것은 윈도우 B에 달려있다.
  • 프래그먼트 식별자 폴링은 고유 URL 패턴 에서 사용 된 것과 똑같은 기술 입니다.

몇 가지 단점이 있습니다 : (a) 폴링은 전체 응용 프로그램 속도를 늦추 죠. (b) 폴링은 항상 약간의 지연 시간을 필요로합니다 (그리고 항상 a와 b의 균형 관계가 있습니다 - 응답이 빠를수록 응용 프로그램이 더 많이 사용됩니다). (c) URL이 눈에 띄게 바뀝니다 (최상위 창을 조작하려는 경우). 우리는 이제 이들을 다루는 두 번째 기술을 고려할 것입니다 (다른 단점을 소개하는 방법 임에도 불구하고).

교차 도메인 해킹 (MARATHON 버전)

다음은 더 이상 URL을 폴링하거나 변경하지 않는 변형입니다. Juliene Le Comte의 블로그에서 그 사실을 알게되었고 , 심지어 도서관으로 포장했습니다.

Law II를 되돌아 보면, "Windows는 같은 도메인에 속하면 서로의 내부 상태에만 액세스 할 수 있습니다". 당시 나는이 소리를 나쁜 것처럼 만들었지 만 데이비드 브렌트 (David Brent)가 말하기를 "알다시피, 모든 구름 ..."이라고 말합니다. 법률은 "도메인 간 iframe이 서로의 장난감을 가지고 놀 수 없다"( Demeter 's 법 의 비공식 버전을 의역)라고 말하면 불량합니다 하지만 " 똑같은 도메인 iframe 이 서로의 장난감을 가지고 놀  있습니다"라고 생각하면 좋습니다. 이것이 우리가 여기에서 악용 할 것입니다.

데모는 기능이 동일하지만 iframe을 생성하는 과정이 포함되어 있으므로 시각적 인 즐거움을 위해 그대로 유지하고 시각적으로 볼 수 있습니다. 일반적으로, 물론, 그들은 보이지 않을 것이며, 응용 프로그램은 이전 데모와 정확히 똑같이 보일 것입니다.

이 기법의 작동 방식은 다음과 같습니다.

  • 창 A가 창 B를 호출하려고 할 때마다 창 B와 동일한 도메인에있는 하위 창 iframe "B2"를 생성합니다. URL에는 실행중인 명령 (CGI 매개 변수, 조각 식별자 또는 다른 URL 패턴으로 목적지 스크립트에 의해 인식됩니다).
  • 창 B2가 시작되면 Javascript는 URL을 검사하고, 창 B에서 핸들을 가져오고, URL (예 : CGI 매개 변수)에 따라 창 B를 업데이트합니다.
  • 창 B2는 자급 자족 한 논리로 자신을 파괴합니다.

따라서이 경우 전달되는 모든 메시지에 대해 수명이 짧은 새로운 iframe을 만듭니다. iframe은 업데이트하려는 윈도우와 동일한 도메인에서 가져온 것이기 때문에 윈도우의 내부 상태를 변경할 수 있습니다. 시작시에 우리에게만 유용합니다. 그 이후에는 더 이상 (이전의 조각 식별자 트릭을 제외하고는) 통신 할 수 없으므로 원래 윈도우에서 직접 할 수 있습니다.

창 B2는 창 A에서 명령을 받아 들여 창 B로 전달하기 때문에 때때로 프록시라고합니다. 나는 명성의 Pheidippides라고 생각하고 싶습니다 메시지를 전달한 다음 고귀한 만료 기간을 거칩니다. 인생의 모든 사명은 하나의 메시지를 전달하는 것입니다.

이 기술은 자체 단점도 함께 제공됩니다. 분명히 단점은 모든 호출에 대해 새 iframe을 만들어야한다는 것입니다.이 경우 서버로 이동해야합니다. 그러나 캐싱을 사용하면 브라우저 내부에서 발생해야하는 모든 작업이 브라우저 내부에서 발생하기 때문에 피할 수 있습니다. 따라서 iframe 요소를 만들고 삭제하는 데 드는 처리 비용 일뿐입니다. 앞의 변형은 결코 DOM 구조를 변경시키지 않았거나 서버를 호출하지 않았습니다.

또한 두 버전의 해킹에서 문자열 형식으로 요청을 표현해야하는 번거 로움이 있습니다. 두 가지 패턴 모두에서 요청을 창 URL에 포함시켜야하기 때문입니다. 이 해킹의 영감 확장 이 지역의 일부 미개발 약속이있다. 하위 도메인을 설정하고 타사 웹 사이트를 가리 키도록 DNS를 업데이트합니다. 이전 document.domain hack 과 결합하면 iframe에 의존하지 않고 iframe이 교차 도메인 iframe과 통신 할 수있는 상황이 발생합니다. 이 기사에서 설명한 기술은 브라우저 간 통신에 관한 것이지만이 iframe-to-iframe도 가능하다고 생각합니다.

세 번째 해킹이 발생합니다 : 창 크기 모니터링

Piers Lawson 이 새로 만든 세 번째 해킹 은 창 크기의 다공성 특성과 window.resize () 사용을 기반으로합니다. 프래그먼트 ID는 여기의 첫 번째 기법과 같이 사용되지만 폴링 대신 창 크기 조정 이벤트를 사용하여보다 직접적인 트리거를 발생시킵니다.

응용 분야

도메인 간 IFRAME에서 IFRAME 호출 ... 및 위젯 / 가젯

매시업의 세계에서 iframe은 한 곳에서 다른 곳으로 콘텐츠를 배포하는 간단한 방법입니다. 그러나이 문제는 iframe 간의 상호 작용이 제한적입니다. 순수한 형태로, 당신은 하나의 미니 웹 브라우저로 끝납니다. iframe이 서로 통신 할 수있게되면 더 좋아집니다. 예를 들어 연락처 위젯과지도 위젯을 사용하여 iGoogle을 열어 놓은 경우를 상상해보십시오. 연락처를 클릭하면지도 위젯이 연락처 위치에 주목하고 초점을 맞 춥니 다. 이것은 가젯 - 투 - 가제트 통신을 통해 가능 합니다. 여기서는 여기에 설명 된 iframe 해킹에서 작동하는 publish-subscribe 형식입니다. 그리고지도에 관해서는 Google지도에서 작동하는 특별한 형태의 가젯 인 Google Mapplet을 확인 하고이 기술에 의존하십시오.

가제트의 측면에서, 또 다른 어플리케이션은 가제트와 그 컨테이너 사이의 통신이고, 이것은 내가 Shindig에서보고 있었던 것입니다. 예를 들어, 가젯에서 선언 할 수있는 동적 높이 기능이 있습니다. 이렇게하면 가제트 개발자에게 "업데이트했습니다. 이제 내 높이를 변경하십시오."라는 API를 제공합니다. iframe은 자체 높이를 변경할 수 없습니다. 그 일을 부모에게 말해야합니다. 그리고 가젯은 컨테이너 (예 : iGoogle)와는 다른 도메인의 iframe에 있기 때문에 교차 도메인, 교차 iframe 메시지가 필요합니다. 그래서,이 기술 ( "rpc"- shindig 용어로 원격 절차 적 호출)을 사용하여 메시지를 컨테이너에 전달합니다.

도메인 간 브라우저 - 서버 호출

브라우저에서 타사 서버로 호출하는 데 가장 잘 알려진 기술은 On-Demand Javascript , 일명 Javascript API 인 JSON / JSONP입니다. 2005 년에는 Delicious가 가장 좋은 사례였습니다. 이제는 Web 2.0 API에서 큰 시간을 보냈습니다. Yahoo! 이런 방식으로 거의 모든 API를 노출했으며 Google은 JSON을 통해 RSS 콘텐츠와 같은 데이터도 제공합니다.

프로그래밍 방식으로 새 스크립트 요소를 생성하여 작동합니다. 요소는 외부 Javascript URL을 가리 킵니다. 타사 Javascript 실행에 대한 제한이 없기 때문에 브라우저는 스크립트를 충실하게 가져 와서 실행하므로 일반적으로 변수를 업데이트하거나 이벤트 처리기를 호출하여 스크립트를 "반환"하도록 작성됩니다.

On-Demand Javascript에는 두 가지 주요 보안 문제가 있습니다. 첫째, API 제공 업체 (예 : 야후!)는 자신의 웹 페이지에서 스크립트를 실행할 수 있기 때문에 많이 신뢰해야합니다. 스크립트 태그 메커니즘이 관련되어있어 위생적으로 사용할 수 없습니다. 그들이 악의적이거나 철저히 지장이 없다면 사용자는 자신의 암호를 묻거나 데이터를 어딘가에 보내거나 데이터를 완전히 파괴 할 수있는 악의적 인 스크립트를 실행하게 될 수 있습니다. 웹 앱이 할 수있는 것이 무엇이든간에, 심지어 단순한 가치를 되 찾으려는 경우에도 제 3 자의 스크립트가 가능하기 때문입니다. 매커니즘은 새로운 범퍼 스티커가 필요할 때 페라리에 열쇠를 넘겨 주도록 강요합니다. 둘째, 외부 웹 사이트를 중단하고 외부 자바 스크립트를 사용하는 것은 무엇입니까? 자신의 사이트가 제 3 자 JS API를 호출하기위한 스크립트를 임베드 할 수 있다면, 악의적 인 제 4 자도 그렇게 할 수 있습니다. 공개, 읽기 전용 데이터의 경우에는 문제가 없지만 사용자의 쿠키를 사용하여 제 3 자에게 권한있는 통화를하는 경우에는 어떻게해야합니까? 그러면 제 4 자의 웹 앱은 브라우저에서 제 3 자에게 전화를 할 수있는 능력을 발휘할 것이며, "getBankBalance ()"대신 "transferFunds ()"와 같은 악의적 인 호출 일 수 있습니다. ". 도덕적 인면 : Javascript API는 공개 데이터를 제공하는 데만 사용할 수 있습니다.

우와! 공용 데이터 만? Google의 교차 도메인 API에 대한 비극적 인 제한 사항입니다. 진정으로 유용한 매시업은 개인적이어야합니다. 사용자가 사이트 X에게 사이트 Y가 데이터를 읽을 수 있음을 알리는 OAuth 스타일의 API가 점점 더 많이 제공 될 것입니다. 하지만 브라우저에서 어떻게 작동할까요? 사이트 Y는 브라우저에서 사용할 수 있도록 데이터를 노출 할 수 있지만 사이트 X가 실행 중일 때만 어떻게 할 수 있습니까? On-Demand Javascript에서는 작동하지 않습니다. 사이트 Y는 리퍼러 헤더를 읽고 전화가 어디서 왔는지 알 수 있지만 누구나 가짜 헤더로 명령 줄 클라이언트를 작성할 수 있습니다.

사실,이 기사에서 설명한 iframe 해킹을 사용하는 것이 해결책입니다. 앞서 언급했듯이, 페이스 북은 OAuth와 본질적으로 동일한 "위임장"위임 모델입니다 (BTA, 제 동료 인 Jeremy Ruston 에게 "위임장"OAuth 유추에 대한 감사의 말을 전합니다. OAuth와는 조금 다른 맥락에서 말하고있다).

Facebook API와 관련된 메커니즘을 너무 많이 보지는 못했지만 근본적으로 Marathon 기술의 변형을 사용하는 것처럼 보입니다. 메모리에는 항상 존재하지 않는 보이지 않는 facebook.com iframe이 있습니다. 웹 애플리케이션이 Facebook 호출을 할 때마다 Facebook JS 라이브러리는 메시지를 동일한 도메인에 항상 존재하는 프레임으로 전달하는 새로운 "프록시"iframe을 생성하여 Facebook에 대한 늪지 표준 XHR 호출을 만듭니다. 이제는 다른 도메인으로 XHR 호출을하고 있습니다.이 도메인은 별도의 iframe에서 가져 오므로 해결할 수 있습니다. XHR 호출이 반환되면 메시지가 응용 프로그램에 반환됩니다 (이 문제는 불필요하다고 생각하지만 서버에서 호스팅해야하는 다른 동일한 도메인 iframe을 통해 발생 함). 프록시 iframe이 사라집니다.

페이스 북에 공개되는 모든 Facebook은 페이스 북에 로그인하는 사용자에 의존하는 표준 웹 서비스입니다. 자바 스크립트가 전혀 포함되어 있지 않습니다. 사용자는 로그인해야하며 응용 프로그램이 Facebook 세부 정보에 액세스 할 수있는 권한이 있어야합니다. 실제로 애플리케이션 개발자가 URL을 등록해야하기 때문에 사용자는 특정 웹 사이트 URL이 Facebook 호출을하도록 허용합니다. 앞에서 설명한 iframe 알고리즘을 살펴보면 Facebook에서이 애플리케이션 (및 사용자가 신뢰하는 다른 애플리케이션) 만 데이터에 액세스 할 수 있음을 확인할 수 있습니다. Facebook.com iframe (동작이 IFrame에 의해 제어되고 조작 될 수 없음)은 상위 창 URL을 검사하고이를 XHR 호출의 일부로 서버에 전달해야합니다.

제 3 자 API 제공 업체를 신뢰해야하는 교차 도메인 자바 스크립트의 첫 번째 관심사에 대해 iframe 기술이이 문제를 극복한다고 생각합니다. Facebook.com은 서버에서 임의의 Javascript를 실행하지 않습니다. 물론 도메인에 호스팅해야하는 Facebook 라이브러리 및 Facebook 제공 iframe을 신뢰해야하지만 설치 전에 감사 할 수 있습니다. 최상위 애플리케이션 내부에서 콜백 메소드를 호출하는 것만이 설정됩니다. 당신은 도서관을 검사하고 이것이 일어날 수있는 모든 것을 보장 할 수 있습니다.

따라서 도메인 간 iframe 기반 통신은 On-Demand Javascript를 괴롭힌 두 가지 문제를 해결합니다. 그것은 이다 그러나 약간 더 복잡.

결론

네, 다소 복잡한 기술입니다. 사실 그것이 해결하는 문제를 이해하는 것은 정말로 어려운 부분입니다! 일단 당신이 그것을 이해하고 일단 물리학 법칙을 이해하면, 그 트릭은 사실 아주 간단합니다.

이 기법은 Shindig와 같은 가제트 컨테이너에 매우 중요합니다. OAuth가 시작되면 주류 응용 프로그램 및 API에서 많이 사용되는 기술을 접하게 될 것입니다.

HTML 5를 사용하면 프레임 간 메시징으로 iframe과 iframe 간 통신에 대한 해킹이 불필요하게됩니다. 실제로 앞서 언급 한 크로스 도메인 라이브러리는 오페라가 실제로 이러한 해킹이 필요로하는 모든 것을 지원하지 않기 때문에 오페라를 위해 이미이 기술을 사용하고 있습니다. 그러나 교차 도메인 호출에 iframe을 사용한다는 개념은 Windows가 서로 대화하는 방식과 상관없이 여전히 존재합니다.


댓글을 달아 주세요