나의 개발 일지

프론트엔드 페이지 라우팅 정리 - window.addEventListener("DOMContentLoaded"), window.addEventListener("popstate")

designer DK 2024. 11. 10. 17:15
728x90

인덱스 페이지(메인 홈 화면) 한 html 안에서 여러 함수를 통해서 페이지 변화를 보여주는 SPA 방식을 구현하려다보니 그 페이지가 늘어남에 따라 url주소와 페이지 간의 매칭이 점점 엉키게 되었다.

 

예를들어 버튼으로 페이지는 띄울 수 있지만 url주소를 새로고침하면 전혀 다른 곳으로 간다거나, 뒤로가기나 앞으로가기 시 에러가 발생하는 등 여러모로 url주소와 페이지 간의 라우팅 처리가 필요했다.

 

이 부분은 생각보다 굉장히 난해했고gpt랑도 감정소모?를 많이 했다...

그래도 씨름끝에 나름 내 방식 안에서 최적화 된 방법을 찾았고 일단 이 방식으로 구현해보았다.

// 주소 정보에 맞게 페이지를 반영하는 함수
function handleRoute() {
const path = window.location.pathname;
const urlParams = new URLSearchParams(window.location.search);
const searchQuery = urlParams.get("name");
const page = urlParams.get("page") || 1;

// 검색어가 있다면 검색 입력 필드에 반영
document.getElementById("search-input").value = searchQuery || "";

// 라우팅 처리
if (path === "/product") {
getProducts(page);
} else if (path.startsWith("/product/")) {
const productId = path.split("/")[2];
getProductDetail(productId);
} else if (path === "/cart") {
getCartPage();
} else if (path === "/order") {
const overallTotalPrice = sessionStorage.getItem("overallTotalPrice");
getOrderPage(overallTotalPrice);
} else if (path === "/") {
currentPage = parseInt(page);
getProducts(page);
}
}

// 새로고침 시 주소 정보에 맞게 페이지 정보 반영
window.addEventListener("DOMContentLoaded", handleRoute);

// 뒤로가기, 앞으로가기 시 주소 정보에 맞게 페이지 정보 반영
window.addEventListener("popstate", handleRoute);

 

여기서 window.location.pathname 는 현재 페이지의 '경로'를 의미.

예를 들어, URL이 https://example.com/product/123일 경우, path에는 "/product/123"가 저장

이 path 변수를 통해 라우팅의 조건을 설정할 수 있다.

 

window.location.search는 URL의 쿼리 스트링(query string) 부분을 가져온다.

예를 들어, URL이 https://example.com/product?name=shoes&page=2일 경우, window.location.search는 "?name=shoes&page=2"가 됨.

 

그리고 new URLSearchParams(...)는 쿼리 스트링을 분석하기 쉽게 객체 형태로 변환해주는데

이 객체를 통해 각각의 쿼리 파라미터를 쉽게 추출할 수 있고

 
const searchQuery = urlParams.get("name");
const page = urlParams.get("page") || 1;

 

이 코드들을 통해 searchQuery, page 각 변수에 주소로 부터 얻은 쿼리스트링 값들을 넣어줄 수 있다.

// 라우팅 처리
if (path === "/product") {
getProducts(page);
} else if (path.startsWith("/product/")) {
const productId = path.split("/")[2];
getProductDetail(productId);
} else if (path === "/cart") {
getCartPage();
} else if (path === "/order") {
const overallTotalPrice = sessionStorage.getItem("overallTotalPrice");
getOrderPage(overallTotalPrice);
} else if (path === "/") {
currentPage = parseInt(page);
getProducts(page);
}

 

라우팅 처리는 url주소로부터 가져온 path가 설정한 조건과 일치할 경우 함수를 실행시키도록 했다.

 

상품상세의 경우 특이한 방식으로 라우팅 처리를 했는데 해당 내용은 과거 포스팅에 정리해두었다.
https://designerdk.tistory.com/43

 

URL 경로에 맞게 함수 실행시켜서 경로에 따라 페이지 변화시키기

상품 상세나 카트가 추가되면서 경로가 꽤 많아졌다.효과적인 js파일들 관리를 위해 index.html이라는 하나의 html파일의 콘텐츠 컨네이너 안에서 상품 콘텐츠나 카트 콘텐츠 같은 것들이 바뀌도록

designerdk.tistory.com

 

 

 

/order의 경우도 해결하는데 애를 좀 먹었는데 함수에 함께 가져갈 overallTotalPrice 매개변수를 가져와야했다.

이런저런 시도 끝에 cart 함수에서 이 변수 값을 세션스토리지에 저장하고 다른 곳에서 가져다 쓰는 방식을 택했다.

 

아무튼 이렇게 처리한 모든 라우팅관련 부분들을 handleRoute라는 함수로 묶어준 후

아래 두 코드를 통해 각 상황에 맞게 작동하도록 만들었다.

이렇게 하면 새로고침을하든 뒤로가기를 하든 해당 url주소와 페이지 내용을 일치시킬 수 있었다.

(DOMContentLoaded로 하면 그냥 load보다 속도가 빠름. DOM만 불러지면 바로 실행됨)

// 새로고침 시 주소 정보에 맞게 페이지 정보 반영
window.addEventListener("DOMContentLoaded", handleRoute);

// 뒤로가기, 앞으로가기 시 주소 정보에 맞게 페이지 정보 반영
window.addEventListener("popstate", handleRoute);

 

 

참고로 이렇게 한 html에서의 라우팅 처리가 아닌 html간 페이지 이동은

해당 컴포넌트 안에서 window.location.href 방식으로 처리했다.

// 버튼 이벤트 리스너 추가
this.querySelector('#home-btn').addEventListener('click', () => {
window.location.href = '/';
});

this.querySelector('#login-btn').addEventListener('click', () => {
window.location.href = '/login';
});

this.querySelector('#logout-btn').addEventListener('click', () => {
sessionStorage.removeItem('token');
window.location.href = '/login';
});

this.querySelector('#admin-btn').addEventListener('click', () => {
window.location.href = '/admin/product';
});