Udemy React
리액트에서 <a> 태그 대신 <Link> 컴포넌트를 쓰는 이유
taytay
2024. 12. 7. 00:45
import { createContext, useState, useEffect } from "react";
const NavigationContext = createContext();
const NavigationProvider = ({ children }) => {
// 1️⃣ 초기 경로를 설정 (브라우저의 현재 URL 경로를 React 상태로 동기화)
// `window.location.pathname`은 브라우저의 현재 경로를 반환
// 초기화 시 앱 상태(currentPath)가 브라우저 URL과 일치하도록 설정
const [currentPath, setCurrentPath] = useState(window.location.pathname);
// 2️⃣ 브라우저의 뒤로가기/앞으로가기 버튼 클릭 시 상태 업데이트
// 브라우저는 popstate 이벤트를 발생시키므로, 이를 감지해 React 상태와 URL을 동기화
// popstate 이벤트는 pushState로 추가된 경로에서도 동작함
useEffect(() => {
const handler = () => {
setCurrentPath(window.location.pathname); // 브라우저의 현재 경로를 React 상태와 동기화
// ❓ window.location.pathname이 어떻게 자동으로 바껴서 이 값으로 상태변경을 할 수 있는 걸까?
};
window.addEventListener("popstate", handler);
// 컴포넌트가 언마운트될 때 이벤트 리스너를 제거하여 메모리 누수 방지
return () => {
window.removeEventListener("popstate", handler);
};
}, []);
// ✅ useEffect의 빈 배열([])은 컴포넌트가 처음 렌더링될 때만 실행되도록 설정
// 이벤트 리스너는 등록 후 브라우저에서 계속 동작하므로 popstate 이벤트 발생 시마다 handler 실행
// 3️⃣ 사용자가 링크를 클릭했을 때 경로 변경
// URL 변경과 React 상태 업데이트를 모두 수행
const navigate = (to) => {
// 1. 브라우저의 history 객체를 업데이트 (pushState로 URL만 변경)
// pushState를 사용하면 브라우저 주소창(URL)이 변경되지만 페이지 새로고침은 발생하지 않음
// => 기존의 `window.location`을 사용하면 페이지가 새로고침되지만,
// pushState는 history 객체를 통해 추가된 경로로 이동만 처리하므로 SPA의 특성을 유지 가능
window.history.pushState({}, "", to);
// 2. React 상태를 업데이트하여 컴포넌트를 다시 렌더링
// URL 변경만으로는 React 컴포넌트가 리렌더링되지 않으므로 상태를 수동으로 변경
setCurrentPath(to);
};
// NavigationContext를 통해 currentPath와 navigate 함수를 하위 컴포넌트에 제공
return (
<NavigationContext.Provider value={{ currentPath, navigate }}>
{children}
</NavigationContext.Provider>
);
};
export { NavigationProvider };
export default NavigationContext;
주석 내용 요약
- 초기화
- useState로 현재 경로를 초기화하여 React 상태와 브라우저의 URL을 동기화.
- 앱 로드 시 window.location.pathname을 가져와 초기 상태로 설정.
- popstate 이벤트
- 브라우저의 뒤로가기/앞으로가기 버튼 클릭 시 popstate 이벤트가 발생.
- 이벤트 핸들러에서 React 상태를 업데이트하여 URL과 상태를 일치시킴. -> ❓ 이때 왜 ``setCurrentPath(window.location.pathname);`` 코드로 상태값을 바꾸는지? 어떻게 자동으로 바뀌는지??
- useEffect의 클린업 함수로 이벤트 리스너를 제거해 메모리 누수 방지.
- navigate 함수
- 사용자가 링크를 클릭했을 때 pushState로 URL을 변경.
- React 상태를 업데이트하여 컴포넌트를 리렌더링.
- pushState만 호출하면 React 상태가 갱신되지 않으므로 상태를 명시적으로 변경해야 함.
❓질문 1) window.location.pathname이 어떻게 "자동으로 바뀌는지"는 브라우저의 동작과 관련!!
브라우저가 URL을 바꾸는 상황은 크게 두 가지로 나눌 수 있다.
1️⃣ 히스토리 변경 시 (pushState, replaceState)
- 자바스크립트에서 history.pushState() 또는 history.replaceState()를 호출하면:
- **브라우저의 주소창(URL)**이 변경됩니다.
- 하지만 페이지가 새로고침되지는 않아요 (SPA 특징).
- popstate 이벤트는 발생하지 않습니다.
(이 이벤트는 브라우저의 "뒤로가기/앞으로가기" 동작과 관련이 있기 때문이에요.)
2️⃣ 사용자가 뒤로가기/앞으로가기를 눌렀을 때
- 사용자가 브라우저의 "뒤로가기/앞으로가기" 버튼을 눌렀을 때:
- 브라우저는 히스토리 스택에서 이전 또는 다음 URL로 이동합니다.
- 이 과정에서 브라우저가 주소창(URL)을 자동으로 변경합니다.
- popstate 이벤트가 발생합니다.
- 이벤트 핸들러(addEventListener)를 통해 URL 변경에 따른 동작을 처리할 수 있습니다.
👉 중요한 점: URL 변경과 window.location.pathname
- 유저가 "뒤로가기"나 "앞으로가기"를 눌렀을 때, 브라우저는 자동으로 URL과 함께 **window.location.pathname**도 최신 상태로 업데이트합니다.
따라서 우리는 항상 현재 경로를 정확히 추적할 수 있습니다.
1️⃣ window.location.pathname이란?
- window.location.pathname은 **현재 브라우저 주소의 경로(path)**를 나타냅니다. 예를 들어:
- URL: https://example.com/dropdown
- window.location.pathname: "/dropdown"
- 브라우저는 주소창(URL)이 변경될 때, 이 값을 자동으로 업데이트합니다.
- 이때, URL을 변경은 1. 사용자가 뒤로가기/앞으로가기를 누르거나 2. pushState에 의해 히스토리가 변경될 때 일어나는데,
브라우저가 window.location 객체의 값을 최신 상태로 유지하기 때문에, 핸들러에서 window.location.pathname을 사용하여 브라우저가 popstate 이벤트를 발생시킬 때마다 최신의 경로(path)**를 얻기 위해 window.location.pathname을 읽습니다.
2️⃣ popstate 이벤트와 window.location.pathname의 연결
- popstate 이벤트는 브라우저 히스토리(뒤로가기/앞으로가기)가 변경될 때 발생합니다.
- 브라우저는 사용자가 "뒤로가기" 또는 "앞으로가기" 버튼을 누를 때:
- URL을 바꿉니다 (주소창에 표시되는 경로를 변경).
- 이 변경 사항에 맞춰 window.location.pathname을 자동으로 업데이트합니다.
- 업데이트된 window.location.pathname을 popstate 이벤트를 통해 개발자가 확인할 수 있도록 알려줍니다.
3️⃣ 핵심 요약
- 브라우저는 사용자가 "뒤로가기" 버튼을 누르거나, 히스토리 변경 시(pushState) URL을 이전/이후 히스토리 항목으로 변경합니다.
- 변경된 URL에 따라 window.location객체가 업데이트되면서, 이 안의 pathname속성도 브라우저에 의해 자동으로 업데이트됩니다.
- 우리는 popstate 이벤트를 활용해 이 변경 사항을 감지하고 최신 경로를 React 상태로 반영합니다.
❓질문 2) OK. 브라우저가 내부적으로 URL을 변경하고, window.location.pathname도 업데이트하니까 우리는 popstate이벤트로 유저의 뒤로/앞으로가기를 감지하여 setter function으로 상태값을 변경해 화면의 리렌더링을 일으키면 된다. 그런데, 왜 굳이 전체 Url 중에서 pathname을 쓰는 것일까?
window.location.pathname을 이용해서 currentPath 상태값을 정의하는 이유는 다음과 같습니다:
요약하자면, pathname만 사용하는 이유는 불필요한 URL 구성 요소를 제외하고, 경로 기반의 라우팅 로직에 집중하기 위해서입니다
- 경로만 필요하기 때문
전체 URL에는 protocol(예: https://), hostname(예: example.com), port(예: :3000), 그리고 query parameters(예: ?id=123) 등이 포함됩니다. 하지만 일반적으로 라우팅 로직에서 중요한 건 pathname(예: /home), 즉 경로 부분뿐입니다.
window.location.pathname을 사용하면 경로만 추출할 수 있어, 다른 정보에 신경 쓰지 않고 경로에 따른 UI 변경에 집중할 수 있습니다. - 환경에 따른 URL 차이 처리
개발 환경에서는 URL이 http://localhost:3000/home과 같을 수 있고, 배포 환경에서는 https://myapp.com/home과 같을 수 있습니다.
이처럼 protocol, hostname, port 등이 환경마다 달라질 수 있기 때문에, 항상 동일한 기준으로 상태를 관리하려면 경로(/home)만 사용하는 것이 더 간단하고 안전합니다. - 유지 보수성
애플리케이션이 커지면서 URL 구조가 변경될 가능성이 있습니다. 예를 들어, https://example.com/products가 https://example.com/store/products로 바뀌어도 pathname만 비교하면 코드에 추가적인 수정이 필요 없습니다. - SPA(Single Page Application) 특성
SPA에서는 새로고침 없이 경로 변경에 따라 화면을 업데이트합니다. 이를 위해 경로 상태를 currentPath로 관리하고, pushState와 popstate 이벤트를 활용해 URL과 상태를 동기화해야 합니다.
window.location.pathname은 초기 경로를 React 상태에 바로 반영해 애플리케이션이 처음 로드될 때 올바른 화면을 렌더링하도록 도와줍니다.