SPA에 대해서 (Single Page Application)
단일 페이지 애플리케이션(Single Page Application)은 웹, 앱에서 점점 많은 정보를 제공하는 추세에 따라 발전된 모던 웹의 패러다임입니다. 정보가 많아질수록 웹을 로딩하는데 비용이 많이 들어가게 되고 사용자 경험이 점점 안좋아지게 됩니다. 따라서 해결책이 필요해지게 된거죠.
<기존의 방식>
기존에는 Link Tag 방식을 사용했습니다. 우리가 아는 링크를 클릭하게 되면 다른 페이지로 이동하게 됩니다. 그리고 그 링크에 대한 URL은 각각 다르게 설정되어 있죠. 이 방식은 각 URL에 해당되는 화면에 대한 리소스를 전부 구성하여 내려주게 됩니다. 이것이 서버 렌더링 방식이죠. 서버에서 렌더링하여 클라이언트에게 보내주게 됩니다.
서버 렌더링 방식은 새로운 페이지 요청마다 정적 리소스를 다운로드하게 되고 전체 페이지를 다시 렌더링합니다. 이는 변경이 필요없는 부분까지 다시 렌더링 하므로 비효율적입니다.
<SPA에서 사용하는 방식>
SPA에서는 웹 애플리케이션에 필요한 모든 정적 리소스를 최초에 한번 다운로드합니다. 그 후 새로운 페이지 요청 시에는 변경에 필요한 부분만 데이터를 전달하여 페이지를 갱신해줍니다. 이는 전체적인 트래픽을 감소 시킬 수 있고 변경되는 부분만 갱신하므로 새로고침이 발생하지 않는 네이티브 앱과 유사한 사용자 경험(UX)을 제공할 수 있습니다.
하지만 이 좋아보이는 방식에도 단점은 존재하죠.
# SPA 단점
- 초기 구동 속도 느림
- 사용할 정적 리소스를 한번에 다운로드 하기 때문에 초기 구동 속도가 기존의 방식보다 느립니다.
- 검색엔진 최적화(SEO) 문제
- SPA는 서버 렌더링 방식이 아닌 클라이언트 렌더링 방식을 사용합니다. 따라서 페이지의 변화가 있더라도 브JavaScript를 실행시키거나 검색엔진이 알 수 있는 변화가 일어나지 않기 때문에 사이트의 콘텐츠 정보를 수집할 수 없습니다. 하지만 이 문제는 react, angular와 같은 SPA 프레임워크에서 서버 렌더링을 지원하는 기술로 대응할 수 있습니다.
검색엔진 최적화(SEO)
검색 유저의 의도를 이해하고 이에 맞춰 웹 페이지의 콘텐츠를 제작하거나 검색 결과에 잘 노출 되도록 웹페이지의 태그와 링크 구조를 개선하여 자연 유입 트래픽을 늘리는 방법.
주요 프로세스로는 크롤링, 인덱싱, 랭킹 등이 있으며 SEO 이슈는 웹 검색 크롤러가 웹페이지를 읽어가지 못하거나 중복된 콘텐츠가 많아서 콘텐츠의 경쟁력이 떨어진다면 SEO 이슈가 있다고 한다.
위에서 궁금한 것이 하나 생깁니다. 서버 렌더링은 서버가 html에 관련된 모든걸 보내주는 방식인 것은 알겠지만 클라이언트 렌더링 방식은 어떻게 동작되는 방식인지 궁금해졌습니다. 하지만 이는 많은 분들이 알고 계실 것 같고 이미 사용하고 계실 것 같습니다.
<클라이언트 렌더링 방식에 사용되는 방법들>
1. AJAX (Asynchronous JavaScript and XML) 방식
영어 이름에서도 알 수 있듯이 자바스크립트 기반 비동기 방식입니다. 자바스크립트를 이용하여 서버와 브라우저가 데이터를 교환할 수 있는 통신 방식을 의미합니다.
AJAX 요청은 url을 변경시키지 않고 데이터를 서버로부터 가져오게 됩니다. 그 후 새로운 페이지를 로드하는 것이 아닌 변경해야할 부분을 AJAX를 통해 가져온 데이터로 갈아끼우게 되는거죠. 이 방식도 완벽하지는 않습니다.
<AJAX 문제점>
- URL을 변경시키지 않으므로 history 관리가 되지 않는다.
- 새로고침을 하면 현재의 페이지를 새로고침 하는 것이 아닌 첫 페이지로 돌아가게 된다.
- SEO 이슈를 해결하지 못한다.
2. Hash 방식
앞선 AJAX 방식의 단점 중 History가 관리되지 않는 이슈를 해결하기 위한 방식입니다. 예시로 보겠습니다.
- 기존 페이지 : http://localhost/service
- Hash 방식 : http://localhost#service
즉, 페이지 변경이 필요해서 링크를 클릭하거나 버튼을 클릭합니다. 이 때 <a href="#service"></a> 와 같이 링크 태그를 사용하면 url에 #service가 뒤에 붙게 됩니다. 여기서 사용하게 되는 #id 는 fragment identifier 혹은 hash라고 합니다.
따라서 페이지가 전체 갱신되지는 않으면서 url에는 변화를 주게 됩니다. 이 상태에서 새로고침을 하게 된다면 어떻게 될까요? http://localhost#serivce 로 재렌더링 하게 되면서 첫페이지가 아닌 현재 페이지를 유지할 수 있게 됩니다.
그렇다면 링크를 클릭했을 때 AJAX를 사용하여 데이터도 가져와야 할겁니다. 이때는 링크의 클릭 이벤트를 사용하여도 되지만 uri에 hash가 변경되었을 때 발생하는 이벤트인 hashchange 이벤트를 사용하여 원하는 로직을 수행합니다.
<Hash 문제점>
- SEO 이슈를 완벽하게 해결하지는 못한다. (cf. HashBang(#!))
3. PJAX 방식
위의 Hash 방식에서 해결하지 못한 SEO 문제를 해결하게 해주는 방식입니다. 하지만 이 방식은 IE 10 이상에서 동작합니다. PJAX 방법을 요약하자면 기존의 링크를 클릭할 때 서버로 요청보내는 부분을 커스텀해서 uri를 변경시키되 내가 원하는 요청만 하게 하는 방법입니다.
link tag는 기존의 방식과 동일하게 <a href="/service"></a> 이 처럼 사용합니다. 이 때 preventDefault를 사용하여 서버로의 요청 이벤트를 막습니다. 그 후 href attribute를 통해 얻을 수 있는 url path를 이용해서 AJAX 요청을 진행합니다.이렇게만 진행하면 url은 변경되지 않습니다. 하지만 우리는 변경하고 싶죠. 이때 사용하는 것이 pushState 메서드 입니다.
// 요청을 막는다
event.preventDefault();
// href에서 path를 가져온다
const path = event.target.getAttribute('href');
// 주소창의 url을 변경시킨다. 하지만 요청은 보내지 않는다.
history.pushState({ path }, null, path);
// AJAX 요청을 보낸다.
request(path);
서버 렌더링 방식과 AJAX 방식을 합쳐서 사용하는 느낌입니다. 페이지는 변경되어야 할 부분만 변경되고 url도 변경되어서 History가 관리되지 않는 이슈가 해결되고 hash를 사용하지 않아 SEO 이슈가 해결됩니다. 이와 같이 완벽해 보이는 방식도 단점은 존재합니다.
<PJAX 문제점>
- 새로고침 시에 변경된 url을 서버에 요청한다. 이때 AJAX 요청과 url은 같지만 AJAX 결과를 반환하는 것이 아닌 변경된 페이지 전체를 반환해줘야하기 때문에 request header에 따라 서버에서 구현을 달리 해줘야한다. (구현 복잡도 높아짐)
<정리>
History 관리 | SEO 대응 | 사용자 경험 | |
링크 방식 | ○ | ○ | X |
AJAX 방식 | X | X | ○ |
Hash 방식 | ○ | X | ○ |
PJAX 방식 | ○ | ○ | ○ |
참고
dahye-jeong.gitbook.io/javascript/javascript/2019-01-25-ajax