uncaught exception : Location.toString 문제점과 해결책

언젠가 한번 이 에러를 꼭 잡겠다고 마음먹었는데, 오늘 갑자기 필 받아서 3시간 넘게 삽질해서 드디어 밝혀냈습니다. 원인을 알고나니 허탈하군요. orz

__________________________

Firefox의 확장기능인 Firebug를 켜둔 채로 웹 사이트를 돌아다니다보면 어떤 사이트에서 에러가 나는지, 어떤 에러가 어느 위치의 코드에서 발생했는지 친절하게 알려준다. 하지만 가끔은 원인을 알 수 없는 메시지를 보여주기도 하는데, 그 중 가장 자주 보이는 메세지가 uncaught exception : Location.toString 인 것 같다.
간단하게 예를 들어보자. 네이버 메인 페이지에 들어가면 가끔 Firebug에서 에러를 알려주는 영역에 다음과 같이 경고 아이콘이 나타나는 것을 알 수 있다.

네이버 화면의 에러
그림 1. 네이버 메인의 오류

이 아이콘을 클릭하면 에러 메시지가 나타난다.

상세 에러메시지
그림 2. Firebug 콘솔에 나타난 오류 메시지

참고로, uncaught exception: Location.toString 1 8 p 라는 메시지는 동일근원 정책(Same-Origin Policy)에 위배되는 동작을 시도할 때 나타난다. XMLHTTPRequest 로 다른 도메인에 접근하려한다거나, 도메인이 서로 다른 프레임끼리 스크립트로 접근하는 경우가 그러하다. 따라서, 오류가 발생하는 부분을 찾아 관련 행위를 하는 코드를 찾으면 해결할 수 있다. 보통 이런 경우에는 XHR이 아닌 JSONP등의 크로스 도메인이 가능한 다른 방식을 이용해서 Ajax 호출을 하거나, 프레임끼리의 도메인을 document.domain = “domain.com”; 코드로 맞추어 주면 해결가능하다(단, document.domain은 같은 도메인의 서브 도메인끼리만 가능).
그래서, 처음엔 이걸로 해결할 수 있겠다라고 생각했었다. 하지만, 안타깝지만 이 문제는 Firebug도 해결할 수 없다. 문제를 해결하기 위해 Fiddler를 이용해 onLoadComplete()가 정의되어 있는 www.js를 로컬 파일로 대체하고 searchrank() 함수를 주석처리했다.

onLoadComplete()
그림 3. onLoadComplete() 함수

그래서 잘 됐냐고? 앞서 말했듯 “해결할 수 없었다”. 심지어 onLoadComplete()를 빈 함수로 재정의해도 마찬가지였다. 그래서 Firebug의 메시지에 의존하지 않고 패턴 찾아내기로 했다. 주목할만한 사실은 오류가 항상 발생하는 것은 아니라는 것이었다. 따라서, 오류를 발생시키는 원인은 “변경 영역”에 있을 것이라는 가설을 세우고 패턴을 찾기 위해 리프레시를 수도 없이 했다. 그리고 영역이 변경될 때 오류의 발생 여부를 파악해 정리하고보니 문제가 되는 것은 “메인 배너”라는 결론에 도달했다.

메인 배너
그림 4. 메인 배너 영역

문제는 메인 배너!

분명 페이지에서 메인 배너를 제거하고 나면 단 한번의 오류도 발생하지 않는다. 하지만 모든 메인 배너가 오류를 발생시키는 것은 아니다. 이미지가 메인 배너일 때는 오류가 전혀 발생하지 않으며, 배너가 플래시일 때만 간혹 발생한다. “간혹”이라는 말의 의미는 플래시임에도 분명 오류가 발생하지 않는 경우가 존재한다는 것이다.

확인 결과, 네이버의 메인 배너는 두 가지 타입의 스크립트를 사용하고 있는데, 하나는 그림 5와 같은 비교적 단순한 플래시 광고일 때만 사용하는 코드이고 다른 하나는 그림 6과 같이 마우스 오버 등 사용자의 액션을 필요로 하는 경우에 사용하는 스크립트이다. 이 중 에러가 나지 않는 것은 오히려 더 복잡한 액션을 필요로 하는 쪽이었다(처음에는 플래시의 문제인 줄 알고 디컴파일하려고 했었다).

단순 플래시 광고
그림 5. 단순한 플래시 광고의 스크립트
uncaught exception : Location.toString
복잡한 플래시 광고의 스크립트
그림 6. 복잡한 플래시 광고의 스크립트

두 코드의 차이가 뭘까..하고 이리저리 테스트해보고 살펴보던 중 발견한 바로 저 것! document.domain! 설마…? 아무런 접근이 없는 상황에서도 단지 플래시가 존재한다는 이유만으로도 접근 권한 에러가…? 라는 의문을 가지고 그림 5의 코드에 그냥 그림 7처럼 document.domain만 삽입해보았다.

결과는…?

troubleshoot09.png
그림 7. document.domain 삽입

…잘 된다. 아는 것이 오히려 병이 된다고 했던가… 스크립트에서도 플래시에서도 다른 프레임간의 접근을 하지 않기 때문에 분명 다른 해결책이 있을 것이라고 생각했는데, 결국은 똑같은 방법. document.domain 이다. 아마도 어떤 이유에서인지 플래시 플레이어가 부모 객체에 접근을 시도하는 것 같다.

결론

uncaught exception : Location.toString 에러는 앞서 말했듯 동일근원 정책을 위반할 때 일어난다. 하지만, 플래시의 경우에는 명시적으로 동일근원 정책을 위반하지 않아도 에러가 발생한다. 이는 플래시 플레이어 내부에서 관련 사항을 위반하는 어떤 행위를 하기 때문이라고 생각한다. External Interface 관련 기능 때문일 수도 있고, 네트웍 접근을 위한 기능 때문일 수도 있다.

그래서 결론은? 결국 저 에러는 모두 document.domain 으로 통한다는 것이다. 또한, 한밤에 삽질하고 글 쓰느라 날려버린 4시간 30분이 너무 허무했다는 것이다. 그래도… 해결방법을 찾았으니 삽질의 보람이 아주 없진 않아서 다행이다.

모든 길은 document.domain 으로?
그림 8. 결국 모든 길은 document.domain 으로 통하는 거다

도구들

이 글을 쓰기 위해 사용한 도구들입니다.

  • Firebug : Firefox에서 JavaScript 등의 디버깅과 모니터링이 가능하도록 해주는 축복받은 확장기능
  • FlashTracer : 혹시 Flash 관련 메시지가 나올까 하여… 실제로 사용은 안해봤음
  • Download Embedded : 페이지내에 embed 된 파일을 다운로드 받을 수 있다.
  • Fiddler2 : 로컬 프록시로, HTTP 패킷을 캡쳐해주고 Auto Responder 기능으로 원격지 파일을 로컬 파일로 대체할 수도 있다. 서버에 파일을 업로드 할 수 없거나 할 때에 좋음. .NET Framework 필요.
    1. 에… 영규 방식은 뭔가 찝찝하잖아요 -_-;; window.onerror를 이미 쓸지도 모르고요(실제로 디버깅관련 유틸에서는 window.onerror를 많이 써요).

      정확한 원인도 파악해보고, 해결책도 찾아서 앞으로도 관련된 문제가 있을 때 원인을 정확하게 설명해주고 설득할 생각이었어요. 네이버 메인에만 관련된 문제였다면 영규 방법도 괜찮은데, 어째서 아직도 안 바꾸고 있는지… 덕분에 제대로 된 보고서를 작성해보려는 의도에서 시작해보았습니다. 첫 머리쯤에 보면 아시겠지만 분명 우리가 알고 있던 상식과는 다르게 관련 에러가 발생하는 것이니까요, 플래시에서 어떤 행동인가를 한다…라는 결론을 낼 수 있도록 테스트도 진행했고요. 누구라도 납득할만한 논리있는 보고서 작성이 목표였다고 할까요? ^^;;

      그래서, 딱 이틀배운 TR 교육 냄새 좀 나게 하려고 했죠. ㅎㅎ

  1. 결론에 첨부해도될까요??
    크로스 프레임 스크립팅 시 보안 정책이라고 알고 있었는데 동일근원 정책이라는 말도 있네요. 이 표현이 좀더 정확한것 같네요. ^^

    저도 꽤 많이 접했던 문제였어요.
    uncaught exception: Location.toString 1 8 p
    Firebug에서만 나타나는 이 오류 메세지는 javascript의 오류라면 uncaught는 아니지 않았을까 라고 가정해봅니다.

    실제로 SWF가 Document에 렌더링 되자마자
    javascript는 ActionScript의 ExternalInterface를 통해서 Flash의 Method를 호출할 수 없고 렌더링이 되고 flash 자체적으로 initialize 하고 완료되면 그때 비로서 javascript 와 ActionScript간에 호출 가능하게 됩니다.

    위에 onLoadComplete() 함수의 경우도 비어있는 상태는 상관없이 ActionScript에 의해서 동일근원 정책을 위반하는 상태라면 또한 ActionScript가 아닌 다른 ActiveX에 의해서 이러한 근원정책을 위배할 수 있는데 브라우저는 이 모든것을 포착할 수 없기 때문에 uncaught exception이 발생하는 것 같습니다.

    그런데 왜?
    “Location.toString” ActionScript가 ExternalInterface를 이용 다른 도메인의 javascript 호출을 위해 내부적으로 동일 근원정책 위반 여부를 체크하기 위해서 첫번째로 window의 Location을 획득하려는 시도 일꺼라는 생각이 듭니다.

    결론
    Flash의 ActionScript가 ExternalInterfce를 이용하여 Javascript를 호출할 때 그 호출된 javascript function이 동일근원 정책(즉 플래시가 구동되는 페이지와 호출하는 javascript가 존재하는 페이지가 서로 다른 도메인일 때, 프레임간 도메인이 틀린 상태 등)에 위반되는 상태일 때 “uncaught exception: Location.toString 1 8 p” 가 발생하고 해결책 중 하나는 document.domain 이고 플래시가 구동되는 페이지 혹은 프레임과 flash에 의해서 접근되려는 javascript의 페이지 혹은 프레임의 document.domain을 동일하게 맞춰줌으로써 해결이 가능하다.

    1. 글 잘 봤습니다. ^^ Flash 내부 동작까지는 잘 모르고 있었거든요.
      한가지 첨언을 하자면, 본문에 썼든 uncaught 오류는 JavaScript에서도 동일근원 정책을 위반하면 나타납니다.

댓글을 남겨주세요