웹 애플리케이션은 단순한 라우터 전환없이 도메인 자체로만 구성하여 단순한 컨텐츠를 제공할수도 대부분의 사이트는 다양한 경로를 통해서 여러가지 컨텐츠를 제공하게 한다. 다양한 경로에 접근하기 위해서 React는 Link라는 컴포넌트를 제공하는데 해당 컴포넌트에 대해서 알아보고 더 나아가서 네비게이션 바를 구성할 때 사용하는 NavLink컴포넌트에 대해서도 알아보자.
Link컴포넌트는 React에서 제공하는 경로 전환 기능이다.
만일 시작(home)페이지에서 특정 페이지로 이동하거나 그 반대로 이동할 때 직접 URL을 입력하는건 매우 비현실적이다.
대신에 우리는 보통 페이지에 링크를 제공한다.
import styles from "./Header.module.css";
const Header = (): JSX.Element => {
return (
<header className={styles.header}>
<nav>
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/info">Info</a>
</li>
<li>
<a href="/product">Product</a>
</li>
<li>
<a href="/mypage">Mypage</a>
</li>
</ul>
</nav>
</header>
);
};
export default Header;
위와같이 "a"요소를 이용해서 "href"속성을 이용하면 이동하려는 URL을 직접 지정해줄 수 있다.
다만 이 방법에는 문제가 있다.
링크를 클릭할 때 새로고침 아이콘이 잠깐 깜빡이면서 "X"모양으로 바뀌었다가 다시 새로고침 아이콘으로 되는걸 볼 수 있다.
이위같은 현상이 발생하는 이유는 실제로 우리가 웹사이트를 지원하는 서버에 "해당 페이지를 렌더링 하게 보내줄래?"라고 요청을 전송하기 때문이다.
그리고 그 서버는 SPA를 구성하는 싱글 HTML페이지를 제공한다.
다만 이 부분에서 해당 컴포넌트의 모든 JavaScript를 다시 로딩한다.
즉, React 애플리케이션 전체를 다시 로딩하고 React 애플리케이션을 재시작한다.
이러한 불필요한 작업은 사이트의 성능에도 영향을 미쳐서 되도록 피해야 한다.
왜 피해야 할까?
우린 이미 모든 JavaScript코드를 처음에 받아왔는데 또 다시 모든 JavaScript코드를 다시 로딩한다는건 굉장히 불필요한 행동이기 때문이다.
또한 Context
나 전체 애플리케이션의 state
를 잃게되서 초기화된다.
결국 이러한 경로전환은 SPA의 모든 장점이 사라지게 되는 것이다.
결국 우리는 새로운 요청을 보내는 대신 요청으로 인한 기본설정을 막고 React Router가 새 URL을 알고 새 URL에 맞는 적절한 요소를 로딩하도록 해야 한다.
이러한 역할을 해주는 기능이 바로 Link
컴포넌트이다.
Link
컴포넌트를 사용하기 위해선 해당 컴포넌트를 react-router-dom
패키지에서 import해줘야 한다.
import { Link } from "react-router-dom";
Link
컴포넌트는 말 그대로 링크를 만들 수 있다.
<li>
<Link to="/">Home</Link>
</li>
기본 "a"요소 대신에 사용하고 "href"속성 대신에 to
속성을 사용하게 된다.
to
속성에는 우리가 가려고 하는 링크를 지정해 줄 수 있다.
Link
컴포넌트는 기본적으로 그 요소에 대한 클릭을 감시한다.
또한 링크를 클릭했을 HTTP요청을 전송하는 브라우저 기본설정을 막아주게 된다.
그 대신에 단순히 Route정의를 확인하여 그에 맞춰 페이지를 렌더링하고 적절한 컨텐츠를 로딩하게 된다.
위와같이 "a"요소를 사용할때와는 달리
Link
컴포넌트를 이용한 경로변환은 서버로 HTTP요청없이 렌더링만 바꿔주는걸 확인할 수 있다.
따라서 React에서 Link
컴포넌트는 SPA구성에 있어서 빠질 수 없는 기능이다.
Link
컴포넌트는 RouteProvider
안에서 렌더링 될 경우에만 작용한다.
function App() {
return (
<div className="App">
<Header />
<RouterProvider router={router} />
</div>
);
}
위와같이 Link
컴포넌트가 정의된 "Header"컴포넌트를 RouteProvider
컴포넌트 외부에다가 정의하면 아래와 같이 에러가 발생하면서 Link
컴포넌트는 작동을 안하게 된다.
현재 만든 네비게이션 바("Header"컴포넌트)에는 어떤 링크가 활성상태인지를 알 수 없다.
물론 애플리케이션을 개발한 사람들 입장에서는 URL을 확인할 수 있고 페이지 콘텐츠를 봐서 그게 어떤 페이지인지 알 수도 있지만 어떤식으로든 강조하여서 UX적인 측면에서 좋게 만드는게 일반적이다.
위 방법 또한 react-router-dom
으로 간단하게 구성할 수 있다.
.header nav ul li a {
text-decoration: none;
color: black;
}
.header nav ul li a:hover,
.header nav ul li a.linkActive {
text-decoration: underline;
}
먼저 활성인 링크를 강조하기 위해 활성화 될 때 효과를 줄 style을 정의해준다.
여기서 "a"요소로 style을 지정한 이유는
Link
컴포넌트는 일반적으로 "a"요소를 렌더링 한다.
이 단계까지 했다고 해서 아직 어떤 링크가 활성화 단계인지는 알 수 없다.
react-router-dom
은 이를 위해 Link
컴포넌트의 대용물을 제공한다.
그게 바로 NavLink
컴포넌트이다.
NavLink
컴포넌트는 Link
컴포넌트와 동일하게 사용한다.
import { Link, NavLink } from "react-router-dom";
import styles from "./Header.module.css";
const Header = (): JSX.Element => {
return (
<header className={styles.header}>
<nav>
<ul>
<li>
<NavLink to="/">Home</NavLink>
</li>
<li>
<NavLink to="/product">Product</NavLink>
</li>
<li>
<NavLink to="/info">Info</NavLink>
</li>
<li>
<NavLink to="/mypage">Mypage</NavLink>
</li>
</ul>
</nav>
</header>
);
};
export default Header;
다만 NavLink
에는 특수한 속성이 있다.
<li>
<NavLink to="/" className={({isActive}) => (isActive ? "A" : "b")}>
Home
</NavLink>
</li>
NavLink
에다가 className
속성을 추가하면 실제로 문자열을 받는 일반적인 "className"이 아니라 함수를 받는 속성이 된다.
해당 콜백함수는 객체를 인자로 받으며 객체 안에는 isActive
속성을 할당할 수 있다.
isActive
는 "boolean"형식이며 해당 링크가 활성화 된다면 isActive
의 속성은 true이고, 활성화되어 있지 않다면 false이다.
즉, 활성화 되어 있는 상태인지 아닌지에 따라서 css를 다르게 적용해줄 수 있는 것이다.
import { Link, NavLink } from "react-router-dom";
import styles from "./Header.module.css";
const Header = (): JSX.Element => {
return (
<header className={styles.header}>
<nav>
<ul>
<li>
<NavLink
to="/"
className={({ isActive }) =>
isActive ? styles.linkActive : "undefined"
}
>
Home
</NavLink>
</li>
<li>
<NavLink
to="/product"
className={({ isActive }) =>
isActive ? styles.linkActive : "undefined"
}
end
>
Product
</NavLink>
</li>
<li>
<NavLink
to="/info"
className={({ isActive }) =>
isActive ? styles.linkActive : "undefined"
}
>
Info
</NavLink>
</li>
<li>
<NavLink
to="/mypage"
className={({ isActive }) =>
isActive ? styles.linkActive : "undefined"
}
>
Mypage
</NavLink>
</li>
</ul>
</nav>
</header>
);
};
export default Header;
위와같이 NavLink
를 모든 링크에 활성화 해준다면 isActive
값에 따라서 css를 다르게 적용하는걸 확인할 수 있다.
NavLink
의 또 다른 옵션으로는 end
옵션이 있다.
<li>
<NavLink
to="/product"
className={({ isActive }) =>
isActive ? styles.linkActive : "undefined"
}
end
>
Product
</NavLink>
</li>
end
속성의 역할은 현재 활성인 Route의 URL뒤가 NavLink
에서 지정한 to
옵션의 경로로 끝나면 해당 링크를 활성으로만 간주해야 하는 역할이다.
이게 무슨 말일까?
예를 들어서 "/product/info"경로로 접근했다고 가정해보자.
그럼 "/product"라는 경로에서 "info"경로가 추가된 상황인 것이다.
그럼 항상 "/product"경로는 중복경로로 앞에 존재하는데 해당 경로는 우리 눈에는 보이지 않지만 페이지에 항상 렌더링 하는 동작을 하게 된다.
따라서 end
옵션을 통해서 해당 NavLink
의 to
옵션으로 지정된 경로가 URL의 끝일때만 해당 링크를 활성화하는걸로 간주하게 하는 것이다.
아마 불필요한 렌더링과 동작을 막아주기 위한 옵션이 아닐까 생각한다.
React에서 경로전환에 사용되는 Link
, NavLink
컴포넌트에 대해서 알아보았다.
Link
컴포넌트는 React 프로젝트를 진행하다보면 빠질수가 없는 컴포넌트이다.
NavLink
또한 자주 사용하지는 않지만 NavLink
만이 가지고 있는 isActive
, end
옵션을 통해서 기존에 분기문을 통해서 css를 활성화 시켰던 네비게이션 바를 좀 더 가독성 좋게 구성할 수 있다는 장점을 가지고 있다.
물론 Link
컴포넌트로 네비게이션 바를 구성할 수 있지만 개인적으로 NavLink
를 통해서 네비게이션 바를 구축하는걸 선호하는 바이다.
https://velog.io/@daydreamplace/TIL-NavLink%EC%9D%98-end%EC%9D%98-%EC%9D%98%EB%AF%B8
https://reactrouter.com/en/main/components/nav-link
https://www.udemy.com/course/best-react/learn/lecture/36126122#overview
https://www.udemy.com/course/best-react/learn/lecture/36126072#overview