window.history
: History 객체에 접근할 수 있는 읽기 전용 속성
history.back(); // 뒤로 가기
history.forward(); // 앞으로 가기
history.go(-2); // 뒤로 2번 가기
: 세션 기록 스택(history)에 상태를 추가하는 메소드
현재 페이지를 다시 로드하거나 이동하지 않고도 브라우저의 URL을 변경할 수 있다.
history.pushState(state, title[, url])
state
: 새로운 세션 기록 항목에 연결할 상태 객체popstate
이벤트가 발생하는데, 이 때 이벤트 객체의 state
속성에 해당 상태를 복제본이 담겨있다.title
: 상태에 대한 짧은 제목title
매개변수를 무시하지만, 미래에 쓰일 수도 있음)url
(optional) : 새로운 세션 기록 항목의 URLconst state = { 'page_id': 1, 'user_id': 5 };
const title = '';
const url = 'hello-world.html';
history.pushState(state, title, url);
세션 기록 스택(history)의 제일 최근 항목을 주어진 데이터, 제목, URL로 대체하는 메소드
history.replaceState(stateObj, title[, url])
popstate
이벤트: 사용자가 브라우저의 히스토리를 탐색(history.back()
, history.forward()
, history.go()
)하거나 앞으로/뒤로 가기 버튼을 클릭할 때 발생하는 이벤트
window.addEventListener('popstate', (e) => {
// 히스토리 상태 변경에 대한 처리
})
window.location
Location 객체에 접근할 수 있는 읽기 전용 속성
location.assign('https://www.naver.com'); // 네이버로 이동
location = 'https://www.naver.com'; // 네이버로 이동
location.reload(true); // 서버로부터 현재 페이지 강제로 다시 로드
window.location.pathname
: /
와 URL의 경로를 담은 문자열을 반환한다.
// e.g. location = 'https://www.naver.com/abc/1234'
console.log(location.pathname); // '/abc/1234'
네비게이션 바에 있는 버튼을 클릭하면 updateURL()
함수가 실행되고,
해당 함수 내에서 pushState()
메소드를 이용해 히스토리 객체에 새로운 항목을 추가한다.
전달할 상태와 타이틀이 없기 때문에 state는 null, title은 빈 문자열을 주었고, URL로는 원하는 경로를 전달한다.
이렇게 하고 버튼을 클릭하면 주소창의 경로가 전달한 path로 업데이트되는 것을 확인할 수 있다.
function App() {
const updateURL = (path) => {
window.history.pushState(null, '', path);
}
return (
<>
<nav>
<button onClick={() => updateURL('/')}>Home으로 가기</button>
<button onClick={() => updateURL('/about')}>About으로 가기</button>
</nav>
</>
);
}
아직까지는 경로의 변화에 따라 화면은 업데이트되지는 않는다.
이제 이 경로를 받아와서 경로별로 원하는 컴포넌트를 렌더링하도록 구현해보자.
location.pathname
속성을 사용해 경로별로 컴포넌트 렌더링하기location 객체를 활용하면 현재 페이지의 URL 정보에 접근할 수 있다.
그 중에서도 pathname
속성을 이용해 경로를 받아올 수 있다.
먼저 현재 경로를 저장할 currentPath
상태를 만들어준다.
그리고 updateURL()
함수가 실행될 때 currentPath
상태를 업데이트하도록 해준다.
이렇게 하면 About 버튼을 클릭했을 때 → 주소창이 .../about
으로 업데이트되고, → location.pathname
속성으로 이 값을 받아와 상태에 /about
이라는 텍스트가 저장된다.
function App() {
const [currentPath, setCurrentPath] = useState(window.location.pathname);
const updateURL = (path) => {
window.history.pushState(null, "", path);
setCurrentPath(window.location.pathname);
};
return (
<>
<nav>
<button onClick={() => updateURL("/")}>Home으로 가기</button>
<button onClick={() => updateURL("/about")}>About으로 가기</button>
</nav>
<Route path="/" component={<Root />} />
<Route path="/about" component={<About />} />
</>
);
}
const Route = ({ currentPath, path, children }) => {
return window.location.pathname === path ? component : null;
// pathname이 /이라면 <Root /> 컴포넌트를 렌더링한다.
// pathname이 /about이라면 <About /> 컴포넌트를 렌더링한다.
};
const Root = () => {
return <div>Home</div>;
};
const About = () => {
return <div>About</div>;
};
그리고 Route라는 컴포넌트는 props로 경로와 렌더링할 컴포넌트를 받아서 location.pathname
이 전달한 경로(path
)와 같다면 컴포넌트를 렌더링하도록 한다.
/
경로일 경우 <Root />
컴포넌트가, /about
경로일 경우 <About />
컴포넌트가 렌더링된다.여기까지 이제 경로에 따라 다른 컴포넌트가 렌더링되도록 했다.
하지만 브라우저의 뒤로 가기/앞으로 가기 기능은 제대로 동작하지 않는다.
앞서 pushState()
를 이용해 히스토리 스택에 상태를 추가해줬기 때문에 뒤로 가기, 앞으로 가기를 눌렀을 때 주소창은 변경되지만 그에 따른 화면이 렌더링되도록 하는 것은 따로 구현해줘야 한다.
이를 해결하기 위해서는 popState 이벤트를 처리해줘야 한다.
popState 이벤트는 뒤로 가기/앞으로 가기를 누르면 발생하는데, 이 이벤트를 listen해서 popState 이벤트가 발생할 때마다 currentPath
를 업데이트시켜주면 된다.
useEffect
훅을 사용하여 컴포넌트가 마운트될 때 이벤트 리스너를 등록하고, 컴포넌트가 언마운트될 때 리스너를 제거해준다.
useEffect(() => {
window.addEventListener('popstate', handlePopState);
return () => {
window.removeEventListener('popstate', handlePopState);
};
}, []);
// popState 이벤트가 발생할 때마다 currentPath를 업데이트해준다.
function handlePopState() {
setCurrentPath(window.location.pathname);
}
이렇게 하면 이제 브라우저에서 뒤로 가기/앞으로 가기를 통해 경로가 변경될 때마다 currentState
상태가 업데이트되고, 화면이 제대로 리렌더링된다.
오 ㅎㅎ 저도 목요일에 히스토리 관리하는 거 했었는데 잘만드셨네요!
정리도 좋습니다!