[React] 브라우저 히스토리 API

권준혁·2020년 11월 2일
6

React

목록 보기
20/20
post-thumbnail

안녕하세요!

브라우저 히스토리 API는 직접적으로 React와 연관된 건 아니지만,
SPA에서 자주 쓰이는 react-router-dom처럼, 클라이언트사이드 라우팅을 구현하려면, javascript의 브라우저 히스토리 API에 대한 이해가 필요합니다.

SPA는 최초 요청시 서버에서 첫 페이지를 처리하고 이후의 라우팅은 클라이언트에서 처리하는 웹 애플리케이션입니다.
브라우저가 서버로 페이지 전환요청하지 않고, 페이지 전환 및 히스토리API를 이용한 뒤로가기가 가능해야 합니다.

먼저 window.history속성을 이용해 클라이언트 사이드 라우팅을 구현해보고, react-router-dom을 이용한 라우팅도 만들어보겠습니다.

Window.history속성과 History

Window객체의 history 읽기 전용 속성은 History객체(브라우저)로의 참조를 반환합니다. History 객체는 브라우저의 세션 기록(현재 페이지를 불러온 탭 혹은 프레임이 방문했던 페이지)을 조작할 때 사용합니다.
브라우저 세션기록을 조작하기 위해서 History객체의 참조를 반환하는 Window.history 읽기 전용 속성을 이용하는 것입니다.

MDN : History 바로가기를 보면 API에서 제공하는 메서드와 속성을 확인할 수 있습니다.

클라이언트 사이드 라우팅만들기

브라우저에는 히스토리에 state를 저장하는 스택(stack)이 존재합니다.
History API에 대해 직접 실습해보기위해 CRA로 프로젝트를 만들고 App.js를 수정해보겠습니다.

// App.js
import React ,{Component} from 'react';
class App extends Component {
  componentDidMount () {
    window.onpopstate = function (event) {
      console.log(`location : ${document.location}, state : ${event.state}`)
    }
  }
  render () {
    return (
      <div>
        <button onClick={()=> window.history.pushState('v1','','/page1')}>
          page1
        </button>
        <button onClick={()=> window.history.pushState('v2','','/page2')}>
          page1
        </button>
      </div>
    )
  }
}
export default App;

먼저 ComponentDidMount() 생명주기로 컴포넌트가 마운트되는 시점에 이벤트를 등록합니다.
window객체에 history의 state가 pop되면 콘솔에 정보를 출력합니다. (onpopstate)

그리고 render()함수에 버튼 두개를 작성합니다.

onClick이벤트가 발생시 state를 push합니다.
pushState() 함수의 인자들은 state, title(사파리제외 역할 없음), url 이렇게 세개입니다.

실행해보겠습니다.

첫 화면

page1버튼을 눌렀을 때

page2버턴을 눌렀을 때

주소창이 변하는 걸 볼 수 있습니다.
뒤로가기 버튼을 누르면 onpopstate 이벤트가 발생하면서 로그를 출력합니다.
이 기능이 클라이언트 사이드에서 라우팅 처리가 되는 것입니다.

이어서, App.js 코드를 조금 수정해보겠습니다.

import React ,{Component} from 'react';

class App extends Component {
  state = {                                // (1)
    pageName : ''
  }
  componentDidMount () {
    window.onpopstate =  (event) => {
      this.onChangePage(event.state)
    }
  }
  onChangePage = pageName => {
    this.setState({pageName})
  }
  onClick1 = () => {
    const pageName = 'page1';
    window.history.pushState(pageName, '', '/page1');
    this.onChangePage(pageName)  
  }
  onClick2 = () => {
    const pageName = 'page2';
    window.history.pushState(pageName, '', '/page2');
    this.onChangePage(pageName)  
  }
  render () {
    const {pageName} = this.state;
    return (
      <div>
        <button onClick={this.onClick1}>page1</button>
        <button onClick={this.onClick2}>page2</button>
        {!pageName && <Home/>}                // (2)
        {pageName==='page1'&&<Page1/>}
        {pageName==='page2'&&<Page2/>}
      </div>
    )
  }
}
function Home(){            
  return <h2>여기는 홈페이지입니다. 원하는 페이지 버튼을 클릭하세요.</h2>
}
function Page1() {
  return <h2>여기는 Page1입니다.</h2>
}
function Page2() {
  return <h2>여기는 Page2입니다.</h2>
}
export default App;

코드가 조금 길어졌습니다.
(1) React답게 pageName을 state로 관리하도록 바꿨습니다.
onChangePage는 컴포넌트의 state를 변경하는 함수입니다.
onClick1과 onClick2은 이전코드에의 하드코딩했던 이벤트처리를 따로 함수로 꺼냈습니다.
(2) render()함수 내에서 컴포넌트의 현재 state에 따라 각각 다른 화면을 보여줍니다.

실행해보면 잘 작동하는 것을 확인할 수 있습니다.
서버에 요청을 보내지않고 클라이언트 사이드에서 사용자의 요청에 따라 페이지가 변화합니다.
서버와 통신이 꼭 필요한 기능들을 제외하곤 클라이언트사이드에서 사용자가 원하는 페이지를 보여줄 수 있습니다.

다음엔 react-router-dom에 대해서 포스팅하겠습니다.
react-router-dom은 내부적으로 브라우저 히스토리 API를 사용합니다.

읽여주셔서 감사합니다!

profile
웹 프론트엔드, RN앱 개발자입니다.

0개의 댓글