복습 : 라우팅(routing)
라우팅
이란 출발지에서 목적지까지의 경로를 결정하는 기능이다. 애플리케이션의 라우팅은 사용자가 태스크를 수행하기 위해 어떤 화면(view)에서 다른 화면으로 화면을 전환하는 내비게이션을 관리하기 위한 기능을 의미한다. 일반적으로 라우팅은 사용자가 요청한 URL 또는 이벤트를 해석하고 새로운 페이지로 전환하기 위해 필요한 데이터를 서버에 요청하고 페이지를 전환하는 위한 일련의 행위를 말한다.브라우저가 화면을 전환하는 경우는 다음과 같다.
1. 브라우저의 주소창에 URL을 입력하면 해당 페이지로 이동한다.
2. 웹페이지의 링크(a 태그)를 클릭하면 해당 페이지로 이동한다.
3. 브라우저의 뒤로가기 또는 앞으로가기 버튼을 클릭하면 사용자 방문 기록(history)의 뒤 또는 앞으로 이동한다. history 관리를 위해서는 각 페이지는 브라우저의 주소창에서 구별할 수 있는 유일한 URL을 소유해야 한다.
전통적 링크 방식
: link tag를 클릭하면 href 어트리뷰트 값인 리소스 경로가 URL의 path에 추가되어 주소창에 나타나고 해당 리소스를 서버에 요청 (서버 사이드 렌더링(SSR))Ajax 방식
: 서버로부터 웹페이지가 응답되면 화면 전체를 새롭게 렌더링해야 하는데 페이지 일부만 갱신하고도 동일한 효과를 볼 수 있도록 하는 것. 내비게이션 클릭 이벤트를 캐치하고 preventDefault 메서드를 사용해 서버로의 요청을 방지한다. 이후, href 어트리뷰트에 path을 사용하여 ajax 요청을 하는 방식. 필요한 리소스 중복 요청을 방지할 수 있고 새로고침이 없는 사용자 경험을 구현할 수 있다는 장점이 있지만 history 관리가 되지 않는 단점. SEO 이슈 존재.Hash 방식
: Ajax를 보완한 방식. URI의 fragment identifier(#service)의 고유 기능인 앵커(anchor)를 사용. 내비게이션이 클릭되면 hash가 추가된 URI가 주소창에 표시된다. 단, URL이 동일한 상태에서 hash만 변경되면 브라우저는 서버에 어떠한 요청도 하지 않는다. 즉, URL의 hash는 변경되어도 서버에 새로운 요청을 보내지 않으며 따라서 페이지가 갱신되지 않는다. hash는 요청을 위한 것이 아니라 fragment identifier(#service)의 고유 기능인 앵커(anchor)로 웹페이지 내부에서 이동을 위한 것이기 때문이다. 또한 hash 방식은 서버에 새로운 요청을 보내지 않으며 따라서 페이지가 갱신되지 않지만 페이지마다 고유의 논리적 URL이 존재하므로 history 관리에 아무런 문제가 없다. hash 방식은 url의 hash가 변경하면 발생하는 이벤트인 hashchange 이벤트를 사용해 hash의 변경을 감지하고 url의 hash를 취득해 필요한 ajax 요청을 수행한다. hash 방식의 단점은 url에 불필요한 #이 들어간다는 것이다. 일반적으로 hash 방식을 사용할 때 #!을 사용하기도 하는데 이를 해시뱅(Hash-bang)이라고 부른다. SEO 이슈 존재.Pjax 방식
: HTML5의 History API인 pushState와 popstate 이벤트를 사용한 pjax(pushState + ajax) 방식. pjax 방식에서 사용하는 history.pushState 메서드는 주소창의 url을 변경하지만 HTTP 요청을 서버로 전송하지는 않는다. 따라서 페이지가 갱신되지 않는다. 하지만 페이지마다 고유의 URL이 존재하므로 history 관리에 아무런 문제가 없다. 또한 hash를 사용하지 않으므로 SEO에도 문제가 없다. pjax 방식은 서버 렌더링 방식과 ajax 방식이 혼재되어 있는 방식으로 서버의 지원이 필요출처 - 모던 자바스크립트 Deep Dive : Single Page Application & Routing
filter의 ALL / TODO / DONE을 눌렀을 때 hash 방식의 라우터 구현
hashchange 이벤트를 통해 hash 변경을 캐치할 수 있음
window.addEventListener('hashchange', (e) => console.log(e))
class Router {
routes = [];
notFoundCallback = () => {};
addRoute(url, callback) {
this.routes.push({
url,
callback,
});
return this;
}
checkRoute() {
const currentRoute = this.routes.find(
route => route.url === window.location.hash,
);
if (!currentRoute) {
this.notFoundCallback();
return;
}
currentRoute.callback();
}
init() {
window.addEventListener('hashchange', this.checkRoute.bind(this));
if (!window.location.hash) {
window.location.hash = '#/';
}
this.checkRoute();
}
setNotFound(callback) {
this.notFoundCallback = callback;
return this;
}
}
document.addEventListener('DOMContentLoaded', () => {
const router = new Router();
const todoList = new TodoList();
const routeCallback = status => () => {
todoList.filterTodo(status);
document.querySelector(
`input[type='radio'][value='${status}']`,
).checked = true;
};
router
.addRoute('#/all', routeCallback('ALL'))
.addRoute('#/todo', routeCallback('TODO'))
.addRoute('#/done', routeCallback('DONE'))
.setNotFound(routeCallback('ALL'))
.init();
});
참고 : closure
onClickRadioBtn(event) {
const { value } = event.target;
// console.log(value);
// this.filterTodo(value);
window.location.href = `#/${value.toLowerCase()}`;
}
기타 참고자료
JavaScript로 라우터를 만들어 간단한 SPA를 구현해보기
SPA에서 어떻게 페이지의 이동이 이루어질까? (hash mode, history mode)
VanillaJS를 이용한 SPA에서 라우팅을 구현하는 방법 (feat. history API)
[JavaScript] HTML5 History API, history 패키지 (feat. react-router-dom)
💬 라우팅 복습 하면서 딥다이브 복습.. 강의는 23분 짜리라 다 설명해줄 수 없어 다시 찾아보았다.
이번 강의를 통해 몇 가지 개념에 대해서는 내가 잘 모르고 있음을 깨닫게 되었다.
실 예제를 보고서 모르는 것을 맞닥뜨리며 서칭한 자료를 토대로 학습중...