다른 도메인의 socket.io 서버 접근 시, XMLHttpRequest 로드가 실패하는 문제

문제 원인을 찾는 데, 꼬박 하루 걸렸습니다.

자초지종을 이야기 하기 전에 간단히 배경 설명을 드리자면,

웹 페이지는 보안 이슈로 인해 로드 된 자신의 것과 다른 도메인의 Ajax 접근에 대해서는 기본적으로 허용하지 않도록 설계되어 있습니다. 이에 대한 표준은 Cross Origin Resource Sharing(CORS)이라는 이름으로 W3C에서 정의해두었는데, 이로 인해 웹 사이트 개발 시에는 이 점이 고려되지 않으면 상당히 큰 제약이 따르게 됩니다. 특히 요즘은 웹 서비스가 다양하게 분화되면서 각각 다른 목적의 서버들이 별개로 동작하는 경우가 많은데 이런 상황에서 CORS 이슈가 해결되지 않으면 메인이 되는 웹 사이트에서 각 서버들로 접속이 되지 않는 난감한 상황이 발생합니다.

웹으로 간단한 채팅 서비스를 만드는데, 웹 페이지에서 기본적인 UI를 제공하고 실제 채팅을 담당하는 서버는 독립적으로 운영되도록 설계하였습니다. 이런 경우 웹 페이지가 로드 된 서버와 다른 도메인으로 채팅 서버가 운영되는 경우, 위에서 이야기 한 CORS의 제약 사항에 걸리게 됩니다. 이를 해결하는 방법은 추가적인 도메인의 서버에서 '이러 이러한 도메인의 접근을 허용해줄게!’라는 선언을 해주는 것입니다.

위와 같은 이슈들은 Cross-Domain 이라는 명칭으로 불리는데 인터넷 검색을 조금만 해봐도 다양한 상황에서 해결하는 방법들이 소개되어 있습니다. 이 글은 이보다 덜 상식적인 이슈를 이야기합니다. socket.io로 네트워크 기능을 구현 시에 이 문제가 발생한다면 어떻게 대처해야 하는가가 이 글의 주제입니다.

결론부터 말씀드리면, socket.io에는 이 이슈와 관련된 open bug가 존재합니다. 버그가 있을거라고는 전혀 생각지도 못한 채 하루를 꼬박 문제 해결을 위해 고생했는데요, 이게 버그였음을 확인한 이상 후에 저처럼 고생하는 분이 없기를 바라는 마음에 글을 남겨둡니다.

오류 메시지는 다음과 같은 구문으로 보여집니다:
XMLHttpRequest cannot load [socket.io 서버]. Origin [웹 서버] is not allowed by Access-Control-Allow-Origin.  

그리고 이 이슈에 대한 해결책은 socket.io의 github 이슈에서 확인 할 수 있었습니다.

 just ran into what I think is the exact same issue and after a lot of code digging, I've found a work around. Long story short, when you tell Socket.io to listen on a port (i.e. not let it default to 80), a default HTTP request handler is created (line 69 of socket.io.js). This default handler automatically writes to and ends the request which prevents the Access-Control-Allow-Origin header from being written to the request by the Manager request handler. The solution I'm using for now is to dynamically remove this default handler in my own code (so not editing any Socket.io code here):

io = io.listen(8888);
io.server.removeListener('request', io.server.listeners('request')[0]);

This works for me. I still think there should be some sort of fix or added option around this though as I don't feel the above to necessarily be stable in the wake of future Socket.io code changes. 

socket.io로 서버를 구동하면 listen을 시작하면서, 기본 HTTP request handler가 만들어지는데, 이 핸들러가 HTTP 헤더에 Access-Control-Allow-Origin 항목을 넣는 것을 막아버리는 것 같네요. 해결 방법은 기본 핸들러를 직접 지워버리는 겁니다. 바로 이 구문이요:

io.server.removeListener('request', io.server.listeners('request')[0]);

이 이슈가 왜 아직까지 해결되지 않은채 유지되는지 모르겠습니다. 어쨌거나 제 하루를 갉아먹은 이 버그가 다른 분들에게는 손쉽게 넘어갈 수 있는 간단한 이슈로 남길 바라는 마음입니다.

0 comments:

댓글 쓰기

Powered by Blogger.

Popular Posts