게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌18 - HTML&DOM - 7년 전 등록

History API

주소를 내 마음대로!

안녕하세요. 이번 시간에는 History API에 대해 알아보겠습니다.

제 블로그를 보시면 페이지가 깜빡이지 않는데도 내용도 바뀌고 주소도 바뀝니다. 물론 리액트 기술을 사용하였기 때문에 가능한 일이지만, 실제로는 하나의 페이지로 만들어진 웹입니다. 싱글 페이지 애플리케이션(SPA)이라고 하죠.

SPA의 단점은 주소가 바뀌지 않는다는 것입니다. 초창기에는 주소 뒤에 #(해쉬) #!(해쉬뱅)을 붙이고 뒤에 하위 주소를 넣었습니다. www.zerocho.com/#!/category/javascript처럼요. 하지만 이 방식은 뭔가 찜찜합니다. #!이라는 주소에 무언가 의미가 있는 것도 아니고 이질적인 느낌이 듭니다. 또한 서버는 # 뒷 부분을 제대로 된 주소라고 생각하지 않습니다.

undefined

따라서 해쉬뱅 대신 브라우저에서 제공하는 주소 API를 사용해 주소를 바꾸게 되었습니다. 바로 History API입니다. 요즘 웬만한 SPA의 라우터들은 이 API를 사용하고 있습니다. IE도 10부터 가능합니다.

이 API는 기존 history 객체(window.history)를 그대로 활용합니다. 따라서 자바스크립트로 뒤로가기(history.back())와 앞으로 가기(history.forward()), 지정한 위치로 가기(history.go(인덱스))를 모두 사용할 수 있습니다. 하지만 SPA일 경우를 가정하고 있기 때문에 이번 시간에는 위 메소드는 사용하지 않습니다.

주소 내역은 하나의 목록입니다. 뒤로가기, 앞으로가기는 목록 안에서 이동하는 것입니다. 따라서 목록에 새로운 주소를 추가하면 페이지를 이동한 셈이 됩니다. 목록에 주소를 추가하기 위한 메소드가 HTML5에서 생겼습니다.

바로 history.pushState()history.replaceState()입니다. 예제를 보며 사용방법을 익혀봅시다. html 파일을 만들어 다음 코드를 입력하세요. 저는 일부러 book 폴더를 만든 후 그 안에 파일을 생성했습니다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>History API</title>
</head>
<body>
<div id="state"></div>
<button id="pushState">pushState</button>
<button id="replaceState">replaceState</button>
<script>
  document.querySelector('#pushState').addEventListener('click', function () {
    history.pushState({ data: 'pushpush' }, 'title을 pushState로', '/pushpush')
  });
  document.querySelector('#replaceState').addEventListener('click', function () {
    history.replaceState({ data: 'replace' }, 'title을 replaceState로', '/replace');
  });
  window.addEventListener('popstate', function () {
    console.log('popstate', history.state);
    document.querySelector('#state').innerHTML = JSON.stringify(history.state);
  });
</script>
</body>
</html>

undefined

웹스톰을 통해 실행한 것이라 주소가 좀 지저분합니다. 아마 여러분은 그냥 /book/history.html일 것입니다. 

html에 두 개의 버튼이 있습니다. pushStatereplaceState인데요. pushState를 누르면 주소가 바뀝니다. 그리고 뒤로가기 버튼이 활성화됩니다. 페이지는 새로 갱신되지 않았는데 주소만 바뀐 효과가 나타난 것이죠.

undefined

코드를 보시면 pushState 버튼을 눌렀을 때 history.pushState({data: 'pushpush'}, 'title을 pushState로', '/pushpush')하도록 되어있습니다. 첫 번째 인자는 바뀐 주소와 함께 저장할 데이터 객체이고, 두 번째 인자는 바꿀 제목, 세 번째 인자는 바꿀 주소입니다. 

주소와 함께 데이터도 저장할 수 있기 때문에 매우 유용합니다. 이 데이터에 바뀔 페이지의 정보들을 담아두고 클라이언트에서 정보를 활용해 새로운 페이지를 렌더링하면 됩니다. 정보는 history.state로 접근할 수 있습니다. 

두 번째 인자는 제목인데 브라우저에서 아직 제목 바꾸는 것까지는 구현하지 않았습니다. 그냥 빈 문자열을 넣어 두는 정도로 하시면 됩니다.

세 번째 인자는 바뀔 주소입니다. 위의 예시에서는 /pushpush를 주었기 때문에 절대 경로로 처리가 되었습니다. 만약 그냥 pushpush나 ./pushpush로 줬다면 상대경로로 처리가 되어 주소가 localhost:63342/book/pushpush가 되었을 것입니다.

뒤로 가기를 누르면 원래 주소로 돌아갑니다. 앞으로가기를 눌러서 /pushpush로 되돌아 갈 수도 있습니다.

원래 주소에서 replaceState를 눌러봅시다.

undefined

이번에는 뒤로가기가 활성화되지 않고 주소만 바꿉니다. pushState와의 차이점을 아시겠나요?

pushState는 주소 목록에 새로운 주소를 추가합니다. /book/history.html을 이전 주소로 두고, 새로운 주소로 /pushpush를 추가한 것입니다. 이전 주소가 남아있기 때문에 뒤로가기로 /book/history.html로 되돌아갈 수 있습니다.

하지만 replaceState는 이전 주소를 없애고 바꿀 주소를 넣습니다. /book/history.html이라는 주소 기록을 지우고 /replace를 추가하는 것입니다. 따라서 /book/history.html에 더는 접근할 수 없습니다.

pushState와 replaceState를 활용하여 프레임워크 없이 싱글 페이지 애플리케이션을 만들어보세요! 페이지 주소에 대한 데이터는 다시 말씀드리지만 history.state에 들어있기 때문에 언제든지 사용할 수 있습니다.

아, pushState와 replaceState로 주소를 바꾼 후, 뒤로가기나 앞으로가기를 했을 때 발생하는 이벤트가 있습니다. 바로 popstate 이벤트입니다. 예제처럼 윈도우에 이벤트 리스너를 연결해 두면 뒤로가기나 앞으로가기를 눌렀을 때 이벤트가 발생합니다. pushState를 여러 번 눌렀다가 뒤로가기를 해보면 #state에 history.state 정보가 뜹니다.

undefined

주의할 점은 pushState와 replaceState를 할 때는 이벤트가 발생하지 않는다는 것입니다. pushState 또는 replaceState를 한 후, 뒤로가기나 앞으로가기를 눌렀을 때만 발생합니다. popstate 이벤트 발생 후 history.state에 접근하면 이전 state를 가져올 수 있습니다. 따라서 이전 페이지도 그 정보들을 활용해 다시 렌더링할 수 있습니다.

History API, 별 거 아니죠? 다만 새로고침을 눌렀을 경우에는 없는 페이지라고 뜨기 때문에 이 부분은 서버사이드 렌더링으로 해결하셔야 합니다. SPA에서 주소를 자연스럽게 바꿀 수 있다는 것은 대단한 장점입니다. 물론 리액트 라우터나 앵귤러 라우터는 이를 이미 활용하고 있지만, 내부 원리를 알았다는 것에 의의를 두시면 됩니다.

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

6개의 댓글이 있습니다.
2년 전
안녕하세요. 글 잘 봤습니다.
사이트 내부에서는 load된 페이지에서 뒤로가기 버튼을 눌렀을 경우, history 정보로 페이지를 찾아가는데요.
외부에서 (게시판 뷰)페이지에 직접 접속하였을 경우에 위로가기 버튼을 누르면 (이전페이지로 일반적 경우에는 당연히)사이트를 빠져나가 버리게 되는데요. 혹시 이경우에는 뒤로가기 버튼을 눌렀을 경우, 게시판 리스으로 갈수 있는 방법은 없는지요?
2년 전
페이지에 접속할 때 history api를 사용해서 게시판으로갔다가 다시 게시글로 이동시키면 히스토리 구조가 게시글-리스트-게시글이 됩니다. 이러면 뒤로가기 시 리스트로 가게됩니다.
2년 전
설명감사합니다.
한가지 궁금한것은, 설명글을 읽고 테스트코드를 작성하여, pushState 버튼을 클릭하여 실행되는 function에 추가하여 /pushState 로 이동한 페이지에서 특정페이지_0 을 load 하였고, 다른 페이지 /pushState_1 에서는 특정페이지_1 을 로드하였습니다. 그런데, 버튼클릭시에는 페이지가 로드가 되는데, 뒤로가기를 하였을경우에는 history.state 정보등은 잘 읽어오나, load로 불러온 페이지는 변경이 되지 않았습니다. 불러온 페이지와 관련된 DOM에 대한 별다른 처리가 없어서 그런듯 한데, load로 불러온 페이지도 이전 페이지로 돌릴수 있는 방법이 있는지요?
2년 전
질문드리고 다시한번 설명글 잘 읽어봤습니다. history.state.xxx 로 접근하였습니다. 좋은 설명 다시한번 감사드립니다.
2년 전
설명 정말 잘보고 갑니다!! 하나 궁금한게 있는데 새로고침을 눌렀을 때 없는 페이지라고 뜨는 이유는 루트 경로 뒤에 다른 경로가 붙어서 거기서 부터 다시 경로로 인식되어 없는 페이지라고 인식되는게 맞을까요?? 예를 들면 https://domain.com/index.html 에서 https://domain.com/push/ 으로 변경 되있고 여기서 새로고침을 하게 된다면 https://domain.com/push/index.html이 되어 없는페이지로 인식하게 되는 건지 궁금합니다.
2년 전
아뇨 말 그대로 push라는 경로 그 자체를 서버에서 인식 못하기 때문입니다.
2년 전
답변까지 너무 감사합니다 : )
2년 전
gg
3년 전
직접쓰신 Node.js교과서 보다가 궁금한게있어서 검색해보다가 신기하게 이 블로그 방문하게 되었네요.. 정말 설명이 평이하네요 항상 감사합니다.
6년 전
pushState를 여러 번 눌렀다가 뒤로가기를 해보면 #state에 history.state 정보가 뜹니다.
--> 여기서 여러번이 아닌 한번만 누르고 뒤로가기 하면 null이던데 이유를 알고 싶어요~
6년 전
push(넣기) 한번 하시고 뒤로가기pop (꺼냄) 하셨으면 당연히 다시 비었겠지요...?