SPA
- single page application.
- spa가 무엇인지 알려면 기본적으로 웹이 어떻게 동작하는지 알아야 함.
등장 배경
- 일반적인 경우 여러 html 파일을 생성해 브라우저 내에서 a태그를 통해 새로고침하듯 이동함.
- 새로운 파일을 로드하지 않기 위해 웹 어플리케이션이 나옴(기존은 웹페이지).
- 정적 파일을 웹 서버로 제공할 뿐 아니라 PHP, Java, Node.js 등을 이용해 동적으로 HTML을 생성해서 제공하는 방식.
- 다만 렌더링 시점이 섞이는 문제 발생 → SPA 등장!
- 서버는 API만 처리하고 모든 렌더링을 클라이언트에서 하는 방식.
- 이후 동작은 url을 보고 어떤 페이지를 그릴지 동적으로 처리함.
장점
- 초기 렌더링은 시간이 걸리지만, 처음 로딩 후 네트워크 부담이 줄어드는 효과가 있음.
hashbang
- url 뒤에 #을 이용해 어떤 페이지를 갈지 처리하는 방식.
- 화면을 새로고침하지 않음.
- #은 같은 페이지 내의 요소를 가리킬 때 많이 사용됨.
- 이 hash를 통해 어떤 페이지를 렌더링할지 정하는 로직을 구현하여 씀.
import {Homepage} from "Homepage.js" export default function App({$target}) { const homepage = new Homepage({$target}); this.render = () => { const {hash} = window.location; if(hash === '') { Homepage.render(); } else if(hash === '#list'){ //List rendering } else { //not found page } } this.render(); window.addEventListener('hashchange', () => { this.render(); }) }
history api
- 브라우저에서 페이지 로등을 하면, 세션 히스토리라는 것을 가짐.
- 세션 히스토리는 페이지를 이동할 때마다 쌓이며, 이를 통해 뒤로가기, 앞으로 가기 등의 이동이 가능함.
pushState
,replaceState
두 개의 함수로 화면 이동없이 현재 url을 업데이트할 수 있음.pushState
: 세션 히스토리에 새 url 상태를 쌓음.replaceState
: 세션 히스토리에 새 url 상태를 쌓지 않고, 현재 url을 대체함.
- #에 비해 (/)로 구분영역을 나누기 때문에 일반 url 형식을 따름. querystring도 자유롭게 붙일 수 있음. ex)/list?page=2&limit=10
history.pushState
- history.pushState(state, title[, url]);
- state : history.state에서 꺼내쓸 수 있는 값. 안쓸 땐 null.
- title : 거의 대부분의 브라우저에서 지원하지 않는 기능. 빈 string 넣기.
- url : 세션 히스토리에 새로 넣을 url. a태그나 location.href와 다르게 url이 변경된다고 해서 화면이 리로드되지 않음.
history.replaceState
- history.replaceState(stateObj, title[, url])
- state, title은 위와 동일.
- 세션 히스토리에서 현재 url과 대체할 url. 효과는 위와 동일.
URL을 탐색하는 것이 아닌(URL로 이동하지 않음) 순수하게 주소 값만 이동함.
/list 등으로 url이 이동했을 때 새로고침을 하면 브라우저가 list.html을 찾기 때문에 404 error를 반환함. 이때 main 페이지로 가도록 예외처리를 하는 등 라우팅 처리에 대한 기본 근간이 되는 history api, 내부 원리를 알고 있어야 문제에 대처할 수 있음.
const onPushState = () => { const url = location.pathname; if(url === '/') { document.querySelector("#app").innerHTML = `<h1>Home</h1>` } else if (url === '/list') { document.querySelector("#app").innerHTML = `<h1>List</h1>` } window.addEventListener('click', e=> { if(e.target.className === 'link') { e.preventDefault(); history.pushState(null,null,e.target.href); onPushState(e.target.href.replace()); } }) onPushState(); }