WebApp 안에 다른 WebApp 넣어서 보여주기

최근 몇 일간 이미 만들어진 WebApp A안에 다른 WebApp B이 하나의 페이지처럼 들어가 보이도록 하는 작업을 했다. 사용자는 A에는 접근할 수 있지만, B에는 (대부분) 접근할 수 없는 상태.

정석(?)은 사실 이런 식으로 해야 할 것 같지만:

  • B를 RESTful API 혹은 어떤 web API를 갖는 서비스로 작성하고 (Service B)
  • B의 RESTful API를 보여주는 view에 해당하는 WebApp을 작성하고 (App B)
  • A는 ‘Service B’를 써서 특정 페이지를 만든다

하지만 그렇게 하지는 않고, 여름에 인턴으로 잠시 일했던 puzzlet군이 사용했던 방법을 좀 고쳐서 썼다.

Web B가 html5로 작성한거라서 http proxy 비슷한거 만들기는 편했다.

인증

다행히 이 두 웹앱은 인증 서비스는 공유한다. 그래서 A에서 인증한 shared-secret을 가지고 만든 데이터를 B에게 주고 이걸로 인증하게 했다. 그리고 B에 접속할 떄 사용하는 세션 키를 A의 세션에 저장하고 꺼내서 쓰게 했다.

페이지 접근

A 의 특정 URL 밑에 오는 것은 B의 URL로 접근하게 했다. 다만 일부 접근할 수 없는 URI에 대한 처리는 있다. 이런게 없었다면 그냥 reverse proxy쓰거나 하면 될 듯도.

web_app_A_host/page_B/... 이면 실제론  B의 페이지에 접근하게 했다. 실제론 URI가 완전히 맵핑되는 건 아니고, 특정 변환을 해주긴해야 해서 reverse-proxy를 쓰진 않았다.

HTTP 응답 rewrite

http 응답은 몇 가지 조건을 걸고 rewrite했다.

1.  http 헤더에 Location 필드가 있는 경우

30x나 201, 202인 경우 이게 붙어서 오는데, 이번 경우엔 201/202는 안 써서 그냥 30x에 Location field를 rewrite하게 했다.

2. html 페이지

href, src 등으로 오는 것 전부 rewrite.

그리고 나서 css, js, 실제 내용에 해당하는 부분을 찾았다. 그리곤 다음과 같이 합쳐서 내보냈다. html5lib이 xpath 로 DOM에 접근할 수 있어서 엄청 간단히 했다.

rewrite-html

3. css 추가 처리

CSS의 경우 골치 아픈 문제가 생기더라. B에서 온 CSS 셀렉터에 해당하는게 A 안에도 있어서 레이아웃까지 깨지고 있었다. Orz.

그래서 위에서 한 것처럼 특정 class 밑에 B의 내용이 오게 강제하고, B에서 오는 모든 css 에는 셀렉터마다 (…) 맨 앞에 저 embedded가 붙게 했다. cssutils로 해석한 다음 각 셀렉터를 하나하나 업데이트하는 코드를 짰다.

약간 느려지긴 하던데 어차피 저건 정적으로 캐싱할 수 있으니까 신경 끄기로 (야).

결론(?)

  • http 요청하는 클라이언트 흉내내기는 재밌더라
  • CSS 때문에 귀찮아.
  • B가 사실 상 페이지 1개짜리 앱이니 그냥 했지, 안 그랬으면 RESTful API로 서비스 분리하는게 훨씬 속 편할 것 같다.