SPA와 Social sharing(소셜 공유)

Posted in SW개발 // Posted at 2017. 6. 26. 12:47
728x90

"소셜 크롤러(Social crawler)는 자바스크립트를 실행하지 않는다"

얼마 전, 아키텍트로 지원했던 프로젝트에서 기술지원 요청이 들어왔다.

Ajax로 점철된(?) 페이지에 소셜 공유 기능을 추가해야 하는데, 여의치 않다는 것이다.
그래서 기술적 해결방안을 요청해 왔다.

해당 프로젝트는 일종의 SPA(Single Page Application) 구조로,
전통적인 웹 구현 모델인 서버랜더링 방식이 아니라 클라이언트 랜더링 방식을 채택하고 있었다.

즉 HTML/Javascript/CSS와 같은 정적 리소스를 먼저 브라우저에 랜더링 한 후, 필요한 비즈니스 데이터를 Ajax 통신으로 가져와서 DOM 제어를 하는 방식이다.

이런 구조의 대표적인 걸림돌(?)은 SEO(검색엔진최적화)나 Open Graph 프로토콜에 기반한 소셜 공유에 취약하다는 것이다.

서버에서 비즈니스를 처리하고 동적으로 생성된 HTML을 반환하는 것이 아니라, 아무런 동적 처리가 되지 않는 HTML을 먼저 반환하기 때문에, 웹 크롤러가 가져갈 수 있는 정보가 한정적이기 대문이다.

다시말해 대부분의 웹 크롤러는 자바스크립트를 실행하지 않기에, 자바스크립트를 사용해 동적으로 구성한 정보를 웹 크롤러에게 줄 방법이 없는 것이다.


이에 대한 기술적 해결 방안으로는, 크게 다음과 같은 두 가지 전략을 생각해 볼 수 있다.

1. 서버 랜더링 방식을 살짝 가미하기
소셜 공유를 위해서는 소셜 크롤러를 위한 메타 정보를 제공해야 한다.
해당 메타 정보는 비즈니스 처리가 된 동적 정보로 구성되어야 하기 때문에 서버 랜더링 방식으로 필요한 메타정보를 구성하는 방식이다.

그리고 페이지의 나머지 처리는 Ajax 통신을 통해 동적으로 구성한다.
이 방식은 서버랜더링 + 클라이언트랜더링 방식을 하이브리드 시켜 '꿩 먹고 알 먹는' 착각(?)을 주기도 한다.

이런 구조는 다음 사이트에서 구현하고 있는 듯 하다

> http://jakduk.com

이 방식의 단점 이라면, 클라이언트 랜더링 방식이라는 아키텍처 구조를 조금 흔들어야 한다는 것이며 Angular와 같은 Full SPA 구조에서는 적용하기 힘들다는 것이다.

이를 위해 다음과 같은 두 번째 방식을 권장한다.


2. 웹 크롤러를 위해 별도의 메타 정보 제공하기
이 방식은 다음의 사이트에 잘 설명되어 있다.

> https://rck.ms/angular-handlebars-open-graph-facebook-share/ 

기존 페이지나 아키텍처 구조를 건드리지 않고도 적용할 수 있는 훌륭한 방법이다.

동적으로 생성된 메타 정보를 반환하는 별도의 로직을 만들고,
페이지 요청이 웹 크롤러로부터 온 것이라면 웹 서버의 Rewrite 모듈을 사용해 요청을 다시 작성한다.

즉 일반 사용자는 원래 페이지에 접근하고 웹 크롤러는 동적 메타 정보를 제공하는 HTML을 반환받도록 하는 것이다.

이때 메타정보를 제공하는 로직은 HTML을 반환하는 REST API로 구현하거나 별도 서버 측 페이지로 구현할 수 있다.

--------------------------------------------------------------------------------------------------------

여러모로 비교해 봤을 때, 2번 방식이 보다 깔금하게 해결 할 수 있는 솔루션인 듯 하다.

그러나 이 글을 쓰는 목적은 다른데 있다.


3. 자바스크립트를 해석하는 웹 크롤러

근래 웹 아키텍처에 SPA(Single Page Application)가 많이 차용되는 추세이고, 특히 앱 스러운(App Like) 모바일 웹 환경에서는 SPA가 보다 효과적이다.

또한 Angular, Vue.js, React.js와 같은 SPA 프레임워크들이 인기가 치솟고 있으며, 하이브리드 앱 환경에서도 클라이언트 랜더링 방식이 심심찮게 적용되고 있다.

이렇듯 클라이언트 랜더링 방식이 기술적 트랜드로 자리매김 하고 있고 필드에서 많이 적용되고 있음에도 웹 크롤러는 아직 과거에 머물러 있다는 점을 꼬집고 싶다.

일부 브라우저 업계에서는 클라이언트 랜더링 방식에서 SEO 등의 적용을 위해 자바스크립트의 동작을 해석하는 웹 크롤러를 만들고 있다고는 한다. 그러나 현재까지도 대부분의 웹 크롤러는 단순히 URL을 호출하고 즉시 반환되는 HTML 덩어리에만 의존하고 있는 실정이다.

아무리 진보한 아키텍처라고 해도, 주변 환경이 이를 지원하지 않는다면 아키텍처의 훼손은 어쩔 수 없이 발생할 것이다.

개인적인 희망으로는, 웹 크롤러를 현실에 맞게 진화시켜 클라이언트 랜더링 방식에서도 SEO나 소셜 공유가 가능토록 해 주길 바란다.

혹여 망설여지는게,
표준적이고 범용적인 적용이 어려울 수도 있겠다 하는 것인데...

이것 역시 , 클라이언트 랜더링의 모든 커스텀 로직을 인식할 필요는 없고 초기화 함수 정도만 인식해 줘도 쉽게 풀릴 문제가 아닌가 한다.

크롤러의 발전을 기대하며...

참고> 페이스 북 클로러 테스트 페이지
다음 URL은 페이스북 크롤러가 소셜 공유를 했을 때 어떤 정보를 가져가는지 테스트 해보기 위한 도구이다.

https://developers.facebook.com/tools/debug/sharing



'SW개발' 카테고리의 다른 글

[웹보안] 로컬 환경 셋팅과 툴 설치  (1) 2019.01.17
설정 파일의 외부화(Spring Cloud Config)  (1) 2018.03.29
좋은 소프트웨어를 위한 기본기  (0) 2016.12.14
UI 디자인트렌드  (0) 2016.12.13
HTTP2-over-QUIC  (0) 2016.12.08

[Angular] Lazy Loading

Posted in 모바일/Javascript // Posted at 2017. 1. 18. 14:29
728x90

앞서 'SPA의 단점에 대한 단상'이라는 글에서 초기 구동속도 문제에 대해 다루었었다.

SPA의 초기 구동속도 문제를 완화하기 위해, 모듈을 청크단위로 분리해서 해당 모듈이 필요한 시점이 되었을 때 관련 리소스들이 다운로드 될 수 있도록 하는 Lazy Loading 기법을 사용할 수 있다.

Angular2에서는 모듈 단위로 컴포넌트와 서비스, 파이프 등 각종 구성요소들을 논리적으로 그룹화 시키고 해당 모듈단위로 Lazy Loading을 적용할 수 있는데, 이의 구현에 대해 알아본다.

1. 시나리오는 대략 이렇다

1) 총 3개의 모듈(일반적인 웹 페이지 개념으로는 3개의 웹 페이지라고 생각하자)

2) 첫 구동 페이지(메인페이지에 해당)에서는 메인 구성을 위한 리소스만 다운로드

3) 나머지 두 개의 페이지도 그 페이지에 요청이 있을 경우에만 관련 리소스 다운로드

4) 이후에는 모든 페이지를 재 방문해도, 새로고침 없이 다운로드 된 리소스 사용

2. Angular 개발 환경은 다음과 같다.

참고로 부트스트랩 3.3.7도 사용.


3. 데모 프로젝트 디렉터리 구조는 다음과 같다.
앞서 시나리오와 같이 총 3개의 페이지를 위한 3개의 Angular 모듈 구성(one/two/three)


4. 각 모듈의 라우팅 구성은 다음과 같다.
1) 루트 라우팅 구성

2) 하위 모듈 라우팅 구성


5. 각 모듈 구성은 다음과 같다.
1) 루트 모듈


2) 하위 모듈 구성


컴포넌트 구성은 중요한 것이 아니라서, 생략하고 프로젝트를 빌드 한다


6. 청크(Chunk)단위 모듈 로딩 확인하기
1) 메인 페이지 요청

Angular 구동을 위한 번들 파일과 메인페이지의 리소스들을 다운로드 받는다.


2) 두번째 페이지 요청
두번째 페이지에서 사용하는 모듈이 번들링된 청크파일과 리소스만 로딩



3) 세번째 페이지 요청
세번째 페이지에서 사용하는 모듈이 번들링된 청크파일과 리소스만 로딩


이제 모든 리소스가 다운로드 되었고, 이후부터는 해당 페이지들을 다시 방문해도 (비동기 통신 데이터를 제외하고는) 이미 다운로드 된 리소스들을 사용해서 반응성이 빨라지게 된다.

추가로 이 데모에서는 초기 페이지의 컴포넌트 재생성을 방지하기 위해 사용자 정의 RouteReuseStrategy를 적용해서 매번 컴포넌트 생성을 하지 않게끔(싱글톤) 하였다.

아래 이미지를 확인해 보면, 세 개의 페이지를 왔다갔다 할 때 첫번째 페이지의 컴포넌트의 경우 더 이상 객체를 생성하지 않는 것을 알 수 있다.(해당 로그는 객체의 생성자에서 기록하도록 했음)





'모바일 > Javascript' 카테고리의 다른 글

Isomorphic Javascript  (0) 2017.01.23
[Node] npm(node package manager)  (0) 2017.01.20
SPA 단점에 대한 단상  (1) 2017.01.17
[Webpack] 비동기 번들 로딩  (0) 2017.01.14
[Webpack] 자바스크립 모듈 번들러, 웹팩(Webpack)  (0) 2017.01.12

SPA 단점에 대한 단상

Posted in 모바일/Javascript // Posted at 2017. 1. 17. 16:33
728x90

모바일 시대로 접어들면서 웹 환경에도 많은 변화가 동반되었다.

그 중 SPA(Single Page Application)도 단단히 한몫을 차지하고 있는데, 항간에 들리는 SPA의 단점들을 나열해 보면 크게 세 가지로 압축되는 듯 하다.

1. 초기 구동 속도

2. 검색엔진 최적화(SEO)

3. 보안

4. IE 8 이하 지원 (추가)

사실 이것들은 SPA의 단점이라기 보다는, SPA 구조이기에 당연히 생길수 밖에 없는 상황으로 이해되어야 한다.

모든 소프트웨어 아키텍처 전략에는 트레이드 오프(Trade-off)가 존재한다. 흔히 '은탄환'은 없다는 말처럼, SPA가 모든 웹 애플리케이션에 가장 적합한 구조는 아니다.

SPA 직역하면 '단일 페이지 어플리케이션'으로, 기존의 전통적인 새로고침 방식의 웹과는 달리 필요한 정적파일을 한번에(나눠서도 가능하다) 모두 다운로드 받고, 이후 사용자와의 상호작용 가운데 필요한 데이터만 서버로부터 (비동기로) 동적으로 받게하여 트래픽의 총량을 줄이고, 네이티브 앱과 유사한 사용자 경험을 제공할 수 있는 어플리케이션 형태이다.

결국 SPA가 추구하는 핵심적인 가치는 웹의 (사용자 경험을 향상시키는, UX) 사용성이라 할 수 있으며 부가적으로 속도의 향상도 얻게 된다.(물론 속도에 큰 틀에서 보면 사용성에 포함시킬 수 있다.)

특히 모바일 환경에서는 트래픽 최소화와 속도 및 반응성, 사용성 등이 보다 중요한 이슈이므로 SPA 구조는 모바일 퍼스트(Mobile First) 전략에서는 거의 모범사례에 가까운 모델이라 할 수 있다.

최근 App-likeNaively Web을 지향하는 구글의 PWA(Progressive Web App) 역시 모바일 환경에서의 웹의 사용성을 향상시키는 일환으로 대두되고 있다. PWA가 SPA를 직접적으로 포함하는 개념은 아니지만 비동기 통신과 SPA 구조는 PWA와 궁합이 잘 맞는 선택이다. 개인적으로 SPA도 PWA와 같은 선상에서 이해되기를 바라는 마음이다.

위에서 언급한 SPA 단점(?)이란 것들도, 가만히 들여다 보면 SPA가 추구하는 사상에서 파생되는 당연한 특징들에 가까우며, 저런 단점들 때문에 SPA 적용에 대해 큰 의문이 든다면, 그것은 SPA 자체의 문제가 아니라 현재 어플리케이션이 SPA모델과는 어울리지 않을 가능성이 더 높다.

다시 말하지만, SPA가 모든 상황에 있어 최고의 아키텍처 전략이 아니라, SPA가 어울리는 곳에 사용했을 때만이 최적의 효과와 효율을 발휘할 수 있을 것이다.

위에서 언급한 SPA의 문제(문제라고 해 두자)들을 하나씩 살펴보자.

1. 초기 구동속도 문제
평소 우리는 많은 모바일 '네이티브 앱'을 사용한다. 당신의 스마트폰에 깔려 있는 많은 앱들을 생각해보라. 이 앱들을 사용하는 우리의 자세(?)를 돌이켜보자.

먼저 앱스토어에서 수메가(mb)에 달하는
 앱을, 몇 초에 걸쳐 다운로드 받는다. 그리고 또다시 몇 초의 시간을 설치하는데 할애한다. 아직 끝이 아니다. 설치한 앱을 처음 띄울때 초기 구동을 위한 각종 설정과 초기화 작업을 기다리며 앱 초기배너 혹은 광고배너를 무심히 바라본다.

드디어 앱이 실행되면 그때부터는 큰 기다림 없이 앱을 사용한다. 물론 이제부터는 빠른 응답과 높은 반응성을 만끽(?)하면서 말이다.

(또한 설치된 앱을 종료하고 다시 실행할 때도 약간의 시간을 감수해야 한다.)

이제 SPA로 돌아와보자. SPA 역시 모바일 네이티브 앱과 같은 효과를 웹에서 제공해 주고자 한다.
즉 앱의 사용성을 추구하고자 SPA에서도 초기에 필요한 대부분의 리소스를 다운로드 받는다. 물론 이것은 초기 구동속도를 조금 손해보고 더 많은 가치(전체적인 앱의 속도/반응성/사용성 등)를 추구하기 위함이다.

우리는 모바일 앱(극단적인 예로 모바일 게임을 떠올려 보자)을 사용하면서 초기 구동속도가 치명적인 단점이라고 생각하지 않는다. 오히려 다운로드와 설치과정 및 앱 초기 구동을 위한 약간의 시간들을 오히려 자연스럽게 받아들인다.

SPA 사상도 이와 유사하기에 초기 구동속도가 치명적인 단점이라고 언급되는 것은 곤란하다고 강조하고 싶다.

SPA의 사상은, 더 이상 웹이 단순한 웹 문서(웹 페이지)가 아닌 하나의 응용프로그램으로 바라보기에 개념의 전환이 필요한 것이다.

물론 그럼에도 불구하고 SPA도 (여전히) 웹 이기에 기존 웹의 경험을 무시할 수 없는 측면도 있을 것이다.

이런 조건 말이다. "SPA가 좋은건 알겠는데... 메인 페이지는 무조건 빨리 떠야 합니다."

SPA에서도 이런 상황에 대한 기술적 대안을 마련할 수 있다.
초기 페이지에서 모든 리소스를 다운받지 않고, 리소스를 청크(Chunk) 단위로 묶어서 해당 리소스에 대한 요청이 있을 때만 다운로드 받도록 하는 방법을 적용하면 초기 구동속도 문제를 많이 완화시킬 수 있다.

많은 자바스크립트 모듈 번들러에서 Lazy Loading 혹은 비동기 모듈로딩이라는 이름으로 이런 시나리오를 지원하며, 우리의 훌륭한 Angular에서도 모듈단위로 구성요소를 묶고 청크단위로 다운로드 받게 할 수 있다.

심지어 SPA에 PWA를 조합하면 보다 뛰어난 사용성을 부가적으로 제공할 수도 있다. 초기 구동에 필요한 필수적 파일을 웹쉘로 구성하고 백그라운드에서 서비스워커를 통해 비동기 리소스 탐방(?)을 실현할 수 있다.

다시 강조하지만, SPA에서 초기 구동속도는 더 이상 결정적인 단점으로 논해져서는 안된다.
오히려 현재 적용하려는 웹 사이트가 SPA 사상과 맞지 않는 것이 아닌가 살펴보기 바란다.


2. 검색엔진 최적화(SEO) 문제
SEO는 서버랜더링 방식이 아닌 자바스크립트 기반 비동기 연동 모델(클라이언트 랜더링 방식)에서는 항상 이슈가 되어 왔던 주제이다. SPA로 가면서 극단적(?) 자바스크립트 기반 웹이 되다 보니 그 문제가 더욱 부각되는 것이다. 구글 크롬 브라우저는 최신 웹 트랜드를 반영해서 자사의 검색엔진에서 클라이언트 래더링 방식도 검색 봇(Bot)이 컨텐츠를 가져갈 수 있도록 개선하고 있지만 기타 다른 브라우저에서는 그 방향성이 불투명하다.

그러나 이 문제 역시 비슷한 맥락에서 말할 수 있다.

다시 말하지만, SPA로 구현된 웹은 더 이상 단순한 정보제공을 위한 웹 문서(페이지)가 아니다.
SPA 결과물을 하나의 응용프로그램으로 본다면 기존 웹 페이지의 정보제공이란 측면의 SEO가 맞지 않을 수도 있다는 것이다.

생각해 보자. 모바일 네이티브 앱을 만들면서 SEO를 걱정하는 이는 드물 것이다.
(다시 극단적인 예로, 모바일 게임을 만들면서 SEO가 안된다고 걱정하는 것을 상상해보라)

SPA를 적용하면서도 개념은 여전히 웹 페이지에 머물러 있다면 둘 중 하나일 것이다.

SPA 사상을 이해하지 못했거나 해당 프로젝트가 SPA와 어울리지 않거나!

(다시한번) 그럼에도 불구하고 SPA도 웹이기에 SEO가 되어야 한다면 역시 대안 기술이 존재한다.

prerender.io 같은 상용 솔루션도 존재하고, Angular나 React같은 SPA 프레임워크에서도 서버 랜더링이라는 이름으로 SEO에 대응하고 있다.  

Angular의 경우 Angular Universal 이라는 SEO 대응 기술이 이미 존재한다.
아직까지 Node.js나 ASP.NET 서버 환경에서만 서버 랜더링을 지원하지만 향후 다른 서버플랫폼도 지원을 계속 추가해 나갈 것이며, 중요한 것은 SPA로 구현된 모든 페이지가 SEO가 되어야 하는 것은 아닐 것이다.

따라서 SEO에 노출되어야 하는 전략적인 페이지들을 선별해서 서버랜더링 기술을 적용하면 될 것이다.


3. 보안문제
SPA의 보안문제로 자주 언급되는 것이 사용자 정보가, 기존 서버기반 세션이 아닌 클라이언트 기반 쿠키라는 것이다. 

그런데 세션은 쿠키 아닌가? 세선도 클라이언트에는 쿠키형태로 저장되고 모든 요청의 헤더에 그 값이 전달된다. 단지 쿠키와 다른점은 서버측에도 클라이언트 쿠키와 매핑되는 세션정보가 저장되어 있다는 것만 다를 뿐 이다.

보안관점에서 보면 쿠키의 가장 큰 문제는 '도용'이다. 쿠키 정보의 기밀성이나 변조는 쉽게 방어할 수 있지만 누군가의 쿠키를 그대로 복사해서 재사용하는 것은 조금더 높은 방어기술을 요구한다.

그런데 이런 쿠키도용은 서버세션이라는 시나리오에서도 동일하게 적용된다. 물론 서버측에 세션만료시간내에 도용해야 한다는 제약이 존재하지만, 요즘 쿠키는 대부분 브라우저 쿠키로 영구적으로 저장하지 않으니 별반 차이가 없다고 보는 것이 맞다.

따라서 사용자 정보저장이라는 측면에서는 SPA의 쿠키나, 기존 서버랜더링 방식의 세션이나 별반 다를 것이 없다. 그리고 요즘 왠만한데는 쿠키 잘 쓰지도 않는다.

SPA의 보안이라는 측면에서 더욱 심각한 것은 핵심로직이 자바스크립트로 구현된다는 점이다.

기존에는 대부분의 비즈니스 로직이 서버에서 수행되어 최종 결과만을 (HTML 덩어리로) 전달받았을 뿐인데, SPA에서는 필요한 데이터만 전달받고 많은 비즈니스 기능을 클라이언트에서 수행하기 때문에 핵심 로직이 노출될 수 있다는 점이 문제라면 문제일 것이다.

따라서 SPA 보안문제를 언급하려면 이 관점에서 풀어나가야 한다.

이것은 별다른 방법이 없다.
핵심로직은 서버에서 수행하도록 하고 중요한 유효성 검사는 양측 모두에서 수행하도록 해야 한다.

유효성 검사의 양측 모두 구현은, 굳이 SPA가 아니더라도 기존방식에서도 그렇게 해 왔었고 해야 되는 것이었다. SPA라서 더욱 신경써야 할 포인트는 바로 클라이언트에서 수행되는 핵심로직을 최소화시키는 것이다.

자바스크립트 난독화만으로 해결될 문제가 아니기에 설계시 고민해야 하는 부분이다.
실제로 게임 어플리케이션을 만들 때 이런 고민을 한다.


4. IE 8 이하 지원
SPA가 비단 Angular, ReactJS로만 만들 수 있는 것은 아니지만, 진정한 SPA 다운 결과물을 만들기 위해서는 이런 프레임워크의 도움이 절실하다. 
그리고 이런 최신의 자바스크립트 프레임워크들은 IE8을 지원대상에서 제외하고 있는 추세이다.
따라서 SPA를 진정 원한다면 IE8 이하를 과감히 버리는 것을 고려해야 한다.

그렇게 하지 못한다면, IE8용 대안을 별도로 마련해야 하는데 대국민 서비스에 규모가 큰 조직이라면 해 볼만 하다. 그러나 서비스 특징과 비용 효율 측면을 따져보고 결정할 일이다.

-----------------------------------------------------------------

하나를 얻으면 하나를 잃게 되는 것이 세상사가 아닌가 한다.

SPA로 얻는 가치가 있다면 SPA로 잃는 가치도 있을 것이다. 그런데 SPA에서 언급되는 저러한 단점들이 과연 단점이라 할 수 있는지 의문이 든다. 

심지어 SPA의 특징으로 인해 부각되는 몇 가지 문제상황들은 대안 기술로 해결될 수 있는 부분이 많다.

결론을 내리자.
SPA 자체를 보지 말고, 현재 수행하는 프로젝트의 특성을 먼저 살펴보기 바란다.
지금 개발하려는 애플리케이션이 SPA의 사상과 일치하는지 말이다.

당신의 프로젝트에 SPA를 적용하지 않는다고 해서 SPA가 서운해 하거나 하지는 않을 것이다.