React Router DOM
https://lshjju.tistory.com/135
React.js Router DOM - 리액트 라우터 돔
React Router DOM의 주요 기능 React는 '단일 페이지 애플리케이션(SPA)'을 만드는 데 주로 사용됩니다. SPA는 기본적으로 HTML 파일 하나만을 로드한 뒤, JavaScript를 통해 화면의 내용을 동적으로 바꾸는
lshjju.tistory.com
Final Build
https://stackblitz.com/edit/vitejs-vite-9fbg1z36?file=src%2Fmain.jsx
egoing - React Router DOM - StackBlitz
Next generation frontend tooling. It's fast!
stackblitz.com
New project building
여러가지 이슈로 스택블리츠에서 프로젝트를 진행하겠습니다.
https://lshjju.tistory.com/104
StackBlitz web editor - 스택블리츠 웹 에디터
스택블리츠로 새 프로젝트 생성하기 https://stackblitz.com/ 로그인 방법 선택 대쉬보드에서 new project + 버튼 - 탭 모달 - frontend 탭 - 탭react javascript - 탭 바이트 리액트가 생성됩니다.프로젝트 생성 완
lshjju.tistory.com
컴포넌트 3개 만듭니다.
import App from "./App"; //삭제
main.jsx
앞으로 특별한 안내가 없으면 main.jsx에서 코딩 합니다.
이 프로젝트에서는 App.jsx 를 사용하지 않습니다.
main.jsx에서 펑션앱을 직접 코딩합니다.
위 코드는 삭제합니다.
function App() {
return (
<div>Hello React Router DOM</div>
);
}
펑션앱을 임포트 아래 추가합니다.
function Home() {
return (
<div>
<h2>Home</h2>
Home...
</div>
);
}
function Topics() {
return (
<div>
<h2>Topics</h2>
Topics...
</div>
);
}
function Contact() {
return (
<div>
<h2>Contact</h2>
Contact...
</div>
);
}
function App() {
return (
<div>
<h1>Hello React Router DOM</h1>
<Home></Home>
<Topics></Topics>
<Contact></Contact>
</div>
);
}
컴포넌트 3개 빌딩 합니다.
펑션앱에 컴포넌트 추가 합니다.
/* null */
index.css
css 코드는 모두 삭제해도 좋습니다.
test

Completion
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
function Home() {
return (
<div>
<h2>Home</h2>
Home...
</div>
);
}
function Topics() {
return (
<div>
<h2>Topics</h2>
Topics...
</div>
);
}
function Contact() {
return (
<div>
<h2>Contact</h2>
Contact...
</div>
);
}
function App() {
return (
<div>
<h1>Hello React Router DOM</h1>
<Home></Home>
<Topics></Topics>
<Contact></Contact>
</div>
);
}
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>
);
Comment ver
// React의 StrictMode를 import 합니다. 이는 애플리케이션 내의 잠재적인 문제들을 식별하는 데 도움을 줍니다.
import { StrictMode } from 'react';
// React 18에서 도입된 새로운 루트 API인 createRoot를 import 합니다.
// 이를 통해 React 요소를 DOM 컨테이너 내부에 렌더링할 수 있습니다.
import { createRoot } from 'react-dom/client';
// 애플리케이션의 전역 스타일을 정의하는 CSS 파일을 import 합니다.
import './index.css';
/**
* Home 섹션을 렌더링하는 함수 컴포넌트입니다.
* @returns {JSX.Element} Home 섹션의 UI를 포함하는 JSX 요소
*/
function Home() {
return (
<div>
<h2>Home</h2>
Home...
</div>
);
}
/**
* Topics 섹션을 렌더링하는 함수 컴포넌트입니다.
* @returns {JSX.Element} Topics 섹션의 UI를 포함하는 JSX 요소
*/
function Topics() {
return (
<div>
<h2>Topics</h2>
Topics...
</div>
);
}
/**
* Contact 섹션을 렌더링하는 함수 컴포넌트입니다.
* @returns {JSX.Element} Contact 섹션의 UI를 포함하는 JSX 요소
*/
function Contact() {
return (
<div>
<h2>Contact</h2>
Contact...
</div>
);
}
/**
* 애플리케이션의 최상위 컴포넌트입니다.
* 모든 하위 컴포넌트(Home, Topics, Contact)를 포함하고 렌더링합니다.
* @returns {JSX.Element} 애플리케이션 전체의 UI를 포함하는 JSX 요소
*/
function App() {
return (
<div>
<h1>Hello React Router DOM</h1> {/* 제목 */}
<Home></Home> {/* Home 컴포넌트 렌더링 */}
<Topics></Topics> {/* Topics 컴포넌트 렌더링 */}
<Contact></Contact> {/* Contact 컴포넌트 렌더링 */}
</div>
);
}
// ID가 'root'인 DOM 요소를 찾아 React 애플리케이션을 마운트합니다.
// createRoot를 사용하여 React 18의 동시성 모드 기능을 활용할 수 있습니다.
createRoot(document.getElementById('root')).render(
// StrictMode는 개발 모드에서만 활성화되며, 애플리케이션 내의 잠재적인 문제점(예: 오래된 라이프사이클 메서드 사용)을 경고합니다.
<StrictMode>
<App /> {/* App 컴포넌트를 StrictMode로 감싸 렌더링합니다. */}
</StrictMode>
);
React router dom install
https://lshjju.tistory.com/135
React.js Router DOM - 리액트 라우터 돔
React Router DOM의 주요 기능 React는 '단일 페이지 애플리케이션(SPA)'을 만드는 데 주로 사용됩니다. SPA는 기본적으로 HTML 파일 하나만을 로드한 뒤, JavaScript를 통해 화면의 내용을 동적으로 바꾸는
lshjju.tistory.com
포스팅 하단 인스톨 코드 참고 하세요.
3페이지 만들고 링크로 연결 합니다.
import { BrowserRouter, Route, Routes } from "react-router-dom";
위 아이들을 임포트 합니다.
// React Router DOM에서 제공하는 라우팅 관련 컴포넌트들을 import 합니다.
// BrowserRouter: HTML5 History API를 사용하여 UI를 URL과 동기화합니다.
// Route: 특정 URL 경로에 매핑될 컴포넌트를 정의합니다.
// Routes: 여러 Route 컴포넌트를 감싸고, 현재 URL과 일치하는 첫 번째 Route를 렌더링합니다.
import { BrowserRouter, Route, Routes } from "react-router-dom";
createRoot(document.getElementById('root')).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
);
최상위 컴포넌트인 앱컴포넌트를 브라우저라우터로 래핑합니다.
이제부터 앱은 브라우저라우터를 사용할 수 있습니다.
// ID가 'root'인 DOM 요소를 찾아 React 애플리케이션을 마운트합니다.
// createRoot를 사용하여 React 18의 권장 렌더링 방식을 따릅니다.
createRoot(document.getElementById('root')).render(
// StrictMode로 애플리케이션을 감싸 개발 중 잠재적인 문제점을 감지하고 경고합니다.
<StrictMode>
{/* BrowserRouter로 전체 애플리케이션을 감싸 React Router의 라우팅 기능을 활성화합니다.
<App /> 컴포넌트 및 그 자식 컴포넌트들에서 라우팅 관련 기능을 사용할 수 있게 됩니다. */}
<BrowserRouter>
<App /> {/* App 컴포넌트를 렌더링합니다. */}
</BrowserRouter>
</StrictMode>
);
function App() {
return (
<div>
<h1>Hello React Router DOM</h1>
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/topics">Topics</a>
</li>
<li>
<a href="/contact">Contact</a>
</li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/topics" element={<Topics />} />
<Route path="/contact" element={<Contact />} />
<Route path="/*" element={"Not Found"} />
</Routes>
</div>
);
}
링크 유아이를 작성합니다.
라우트를 추가 합니다.
패스를 세팅 합니다.
예외 처리 합니다.
/**
* 애플리케이션의 메인 컴포넌트입니다.
* 내비게이션 링크와 React Router를 이용한 라우팅 설정을 포함합니다.
* @returns {JSX.Element} 애플리케이션의 전체 UI를 나타내는 JSX 요소
*/
function App() {
return (
<div>
<h1>Hello React Router DOM</h1> {/* 애플리케이션의 제목 */}
<ul>
{/* 각 페이지로 이동하는 내비게이션 링크들입니다.
현재는 <a> 태그를 사용하여 페이지를 새로 고치지만,
React Router의 <Link> 컴포넌트를 사용하면 SPA(Single Page Application) 방식의 라우팅이 가능합니다. */}
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/topics">Topics</a>
</li>
<li>
<a href="/contact">Contact</a>
</li>
</ul>
{/* Routes 컴포넌트는 자식 Route들 중에서 현재 URL과 일치하는 첫 번째 Route를 찾아 렌더링합니다. */}
<Routes>
{/* 경로가 '/'일 때 Home 컴포넌트를 렌더링합니다. */}
<Route path="/" element={<Home />} />
{/* 경로가 '/topics'일 때 Topics 컴포넌트를 렌더링합니다. */}
<Route path="/topics" element={<Topics />} />
{/* 경로가 '/contact'일 때 Contact 컴포넌트를 렌더링합니다. */}
<Route path="/contact" element={<Contact />} />
{/* 정의된 어떤 경로와도 일치하지 않을 때 ('/*' 와일드카드) "Not Found" 텍스트를 렌더링합니다.
이는 404 페이지와 같은 역할을 합니다. */}
<Route path="/*" element={'Not Found'} />
</Routes>
</div>
);
}
test

링크 탭해서 페이지 체크 합니다.
이렇게 유알엘 뒤에 링크 직접 붙여서 페이지 체크 합니다.
webcontainer.io/
webcontainer.io/topics
webcontainer.io/contact
요상한 유알엘로 접근하면 어떻게 되는지 체크 합니다.
webcontainer.io/wtf
Completion
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
function Home() {
return (
<div>
<h2>Home</h2>
Home...
</div>
);
}
function Topics() {
return (
<div>
<h2>Topics</h2>
Topics...
</div>
);
}
function Contact() {
return (
<div>
<h2>Contact</h2>
Contact...
</div>
);
}
function App() {
return (
<div>
<h1>Hello React Router DOM</h1>
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/topics">Topics</a>
</li>
<li>
<a href="/contact">Contact</a>
</li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/topics" element={<Topics />} />
<Route path="/contact" element={<Contact />} />
<Route path="/*" element={'Not Found'} />
</Routes>
</div>
);
}
createRoot(document.getElementById('root')).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
);
페이지를 리로드하지 않고 데이터를 변경 합니다.
import { BrowserRouter, Route, Routes, Link } from "react-router-dom";
링크를 탭하면 페이지를 리로드하고 있습니다.
거시기 합니다.
리액트를 이럴라고 쓰는게 아닌데요.
개선 합니다.
Link 임포트 합니다.
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/topics">Topics</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
에이태그를 링크타입으로 교체합니다.
링크 탭 해 봅니다.
이제부터는 페이지가 리로드 되지 않습니다.
Hash URL 적용 합니다.
https://lshjju.tistory.com/278
Hash URL
해시(Hash) URL, 즉 URL의 # 뒤에 붙는 부분은 '프래그먼트 식별자(Fragment Identifier)'라고도 불립니다. 과거 싱글 페이지 애플리케이션(SPA)에서 주로 사용되었으며, 현재는 HTML5 History API의 등장으로 그
lshjju.tistory.com
import { HashRouter, Route, Routes, Link } from "react-router-dom";
HashRouter 임포트 합니다.
<StrictMode>
<HashRouter>
<App />
</HashRouter>
</StrictMode>
브라우저라우터를 해시라우터로 교체합니다.
유알엘 인풋을
webcontainer.io/
이렇게 만듭니다.
스택블리츠의 경우 링크에 마우스인 하고 하단 팝업으로 체크 합니다.
유알엘에 #이 추가됩니다.
<StrictMode> {/* 개발 중에 잠재적인 문제를 식별하는 데 도움이 되는 추가 검사를 수행합니다. */}
<HashRouter> {/* 웹 브라우저의 URL 해시 부분을 기반으로 라우팅을 처리하는 라우터입니다. */}
<App /> {/* 애플리케이션의 핵심 내용을 담고 있는 App 컴포넌트를 렌더링합니다. */}
</HashRouter>
</StrictMode>
액티브 클래스 유아이 만들기
import { HashRouter, Route, Routes, NavLink } from "react-router-dom";
NavLink 임포트 합니다.
<ul>
<li><NavLink to="/">Home</NavLink></li>
<li><NavLink to="/topics">Topics</NavLink></li>
<li><NavLink to="/contact">Contact</NavLink></li>
</ul>
링크를 네브링크로 교체 합니다.
스택블리츠에서 프리뷰를 새창으로 오픈 합니다.
f12 엘레멘츠 체크
탭하면 해당 에이 태그에 액티브 클래스가 생성되는지 체크 합니다.
.active {
background-color: tomato;
text-decoration: none;
}
index.css
원래 코드 다 삭제하고 액티브 클래스 추가합니다.
링크를 탭하고 액티브 유아이를 체크 합니다.
이제부터 유저가 지금 어디에 있는지를 직관적인 유아이로 표시할 수 있습니다.
test

Completion
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import { HashRouter, Route, Routes, NavLink } from 'react-router-dom';
function Home() {
return (
<div>
<h2>Home</h2>
Home...
</div>
);
}
function Topics() {
return (
<div>
<h2>Topics</h2>
Topics...
</div>
);
}
function Contact() {
return (
<div>
<h2>Contact</h2>
Contact...
</div>
);
}
function App() {
return (
<div>
<h1>Hello React Router DOM</h1>
<ul>
<li>
<NavLink to="/">Home</NavLink>
</li>
<li>
<NavLink to="/topics">Topics</NavLink>
</li>
<li>
<NavLink to="/contact">Contact</NavLink>
</li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/topics" element={<Topics />} />
<Route path="/contact" element={<Contact />} />
<Route path="/*" element={'Not Found'} />
</Routes>
</div>
);
}
createRoot(document.getElementById('root')).render(
<StrictMode>
<HashRouter>
<App />
</HashRouter>
</StrictMode>
);
페이지에 서브 페이지와 링크를 추가 합니다.
<Route path="/topics/*" element={<Topics />} />
토픽스 패스에 /* 추가합니다.
function Topics() {
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<NavLink to="/topics/1">HTML</NavLink>
</li>
<li>
<NavLink to="/topics/2">JS</NavLink>
</li>
<li>
<NavLink to="/topics/3">React</NavLink>
</li>
</ul>
<Routes>
<Route path="/1" element={"HTML is ..."} />
<Route path="/2" element={"JS is ..."} />
<Route path="/3" element={"React is ..."} />
</Routes>
</div>
);
}
링크 몇개 만듭니다.
라우트 설치하고 내부 데이터 추가 합니다.
// 토픽스 페이지를 나타내는 컴포넌트입니다.
// 이 컴포넌트 자체 내에 또 다른 중첩 라우팅을 포함하고 있습니다.
function Topics() {
return (
<div>
<h2>Topics</h2> {/* 토픽스 페이지의 제목입니다. */}
<ul> {/* 토픽 목록을 위한 비순서 목록입니다. */}
<li>
<NavLink to="/topics/1">HTML</NavLink>
{/* HTML 토픽으로 이동하는 NavLink입니다. */}
</li>
<li>
<NavLink to="/topics/2">JS</NavLink>
{/* JS 토픽으로 이동하는 NavLink입니다. */}
</li>
<li>
<NavLink to="/topics/3">React</NavLink>
{/* React 토픽으로 이동하는 NavLink입니다. */}
</li>
</ul>
{/*
이 Routes는 /topics/* 경로 아래에서 동작하는 중첩 라우팅입니다.
예를 들어, URL이 "/topics/1"일 경우, 이 Routes 내부에서는 path="/1"이 매칭됩니다.
*/}
<Routes>
<Route path="/1" element={"HTML is ..."} />
{/* 경로가 "/topics/1"일 때 "HTML is ..."를 표시합니다. */}
<Route path="/2" element={"JS is ..."} />
{/* 경로가 "/topics/2"일 때 "JS is ..."를 표시합니다. */}
<Route path="/3" element={"React is ..."} />
{/* 경로가 "/topics/3"일 때 "React is ..."를 표시합니다. */}
</Routes>
</div>
);
}
test
잘 작동하는지 체크합니다.

Completion
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import { HashRouter, Route, Routes, NavLink } from "react-router-dom";
function Home() {
return (
<div>
<h2>Home</h2>
Home...
</div>
);
}
function Topics() {
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<NavLink to="/topics/1">HTML</NavLink>
</li>
<li>
<NavLink to="/topics/2">JS</NavLink>
</li>
<li>
<NavLink to="/topics/3">React</NavLink>
</li>
</ul>
<Routes>
<Route path="/1" element={"HTML is ..."} />
<Route path="/2" element={"JS is ..."} />
<Route path="/3" element={"React is ..."} />
</Routes>
</div>
);
}
function Contact() {
return (
<div>
<h2>Contact</h2>
Contact...
</div>
);
}
function App() {
return (
<div>
<h1>Hello React Router DOM</h1>
<ul>
<li>
<NavLink to="/">Home</NavLink>
</li>
<li>
<NavLink to="/topics">Topics</NavLink>
</li>
<li>
<NavLink to="/contact">Contact</NavLink>
</li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/topics/*" element={<Topics />} />
<Route path="/contact" element={<Contact />} />
<Route path="/*" element={"Not Found"} />
</Routes>
</div>
);
}
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<HashRouter>
<App />
</HashRouter>
</StrictMode>
);
유아이를 반복문으로 개선 합니다.
페이지 콘텐츠를 데이터 바인딩 합니다.
var contents = [
{ id: 1, title: "HTML", description: "HTML is ..." },
{ id: 2, title: "JS", description: "JS is ..." },
{ id: 3, title: "React", description: "React is ..." },
];
현재 네비 엘아이를 반복문으로 만든다면 유지보수가 훨씬 효율적일 것입니다.
개선 합니다.
펑션홈 아래에 어레이 하나 초기화 합니다.
// 토픽 데이터 배열입니다. 각 객체는 ID, 제목, 설명을 포함합니다.
// 이 배열은 애플리케이션 전체에서 공유되는 데이터 소스로 사용됩니다.
var contents = [
{ id: 1, title: "HTML", description: "HTML is ..." },
{ id: 2, title: "JS", description: "JS is ..." },
{ id: 3, title: "React", description: "React is ..." },
];
<ul>
{contents.map(item => (
<li key={item.id}>
<NavLink to={"/topics/" + item.id}>{item.title}</NavLink>
</li>
))}
</ul>
Topics
반복문으로 토픽 링크를 빌딩 합니다.
<ul> {/* 토픽 목록을 위한 비순서 목록입니다. */}
{/* map()은 배열의 각 항목을 변환하여 새로운 배열을 반환하며,
React는 이 배열의 JSX 요소들을 효율적으로 렌더링합니다. */}
{/* 각 리스트 항목에는 고유한 'key' prop을 넣어주는 것이 중요합니다.
(여기서는 item.id를 사용) */}
{contents.map(item => (
<li key={item.id}>
{/* key prop은 React가 리스트를 효율적으로 업데이트하는 데 사용됩니다. */}
<NavLink to={"/topics/" + item.id}>{item.title}</NavLink>
{/* 각 토픽 상세 페이지로 이동하는 링크입니다. */}
</li>
))}
</ul>
https://lshjju.tistory.com/173
React에서 map 함수 사용하기
React에서 map 함수 사용하기React에서 반복되는 요소들을 렌더링할 때는 주로 JavaScript의 map() 함수를 사용합니다. 이 함수는 배열의 각 요소를 순회하면서 새로운 배열을 만들어주는데, React에서는
lshjju.tistory.com
function Topic() {
return (
<div>
<h3>Topic</h3>
Topic...
</div>
);
}
펑션 토픽스 위에 펑션 토픽을 추가 합니다.
<Routes>
<Route path="/:topic_id" element={<Topic />} />
</Routes>
Topics
유저가 탭한 링크가 패스로 세팅되어야 합니다.
그러기 위해 펑션토픽스 라우트를 업그레이드 합니다.
유저가 탭한 유알엘이 브라우저 유알엘영역에 있습니다.
즉 브라우저 유알엘 영역이 현재의 유알엘을 알고 있습니다.
/: 는 브라우저 유알엘영역 아이디가 무엇인지 알려달라는 코드입니다.
그런데 이 기능을 사용하려면 유즈파람스가 필요 합니다.
유즈파람스 임포트 하러 갑니다.
<Routes>
{/*
이 Routes는 '/topics/*' 경로 아래에서 동작하는 중첩 라우팅입니다.
URL이 "/topics/1"일 경우, 이 Routes 내부에서는 path="/:topic_id"가 매칭되며,
':topic_id' 부분은 Topic 컴포넌트로 전달되어 사용됩니다.
*/}
<Route path="/:topic_id" element={<Topic />} />
{/* URL의 동적 파라미터에 따라 Topic 컴포넌트를 렌더링합니다. */}
</Routes>
import { HashRouter, Route, Routes, NavLink, useParams } from "react-router-dom";
토픽아이디 밸류를 가져오기 위해 useParams를 임포트합니다.
var params = useParams();
Topic
유즈파람스 기능을 사용하기 위해 리턴 위에 파람스를 초기화 합니다.
// useParams 훅을 사용하여 URL에서 동적인 파라미터(예: /topics/123의 123)를 추출합니다.
var params = useParams();
console.log("params", params, params.topic_id);
Topic
파람스 변수 아래에 로그 찍어 봅니다.
토픽스 React링크 찍고 콘솔에서 아이디3 체크합니다.
topic_id 는 라우트 패스에서 유즈파람스에게 요청한 네임입니다.
// 디버깅 목적으로 params 객체와 topic_id 값을 콘솔에 출력합니다.
console.log('params', params, params.topic_id);

var topic_id = params.topic_id;
const found_topic = contents.find(item => item.id === Number(topic_id));
const selected_topic = found_topic || {
title: "Sorry",
description: "Not Found",
};
Topic
파람스 배리어블 아래에 파람토픽아이디 초기화 합니다.
즉 유즈파람스로 얻은 아이디를 사용합니다.
find() 메서드를 사용해서 `topic_id`에 해당하는 항목을 찾습니다.
A = B || C
B가 트루라면 A에 세팅하세요.
아니라면 C를 A에 세팅하세요.
여기서는 found_ topic에 데이터가 있다면 트루입니다.
데이터가 없다면 스트링데이터로 예외 처리 합니다.
// 추출된 파라미터 객체에서 'topic_id' 값을 가져옵니다.
var topic_id = params.topic_id;
// contents 배열에서 URL 파라미터로 받은 topic_id와 일치하는 토픽을 찾습니다.
// URL 파라미터는 문자열이므로 Number()를 사용하여 숫자로 변환합니다.
const found_topic = contents.find((item) => item.id === Number(topic_id));
// 일치하는 토픽을 찾지 못한 경우를 대비하여 기본값(Not Found)을 설정합니다.
const selected_topic = found_topic || {
title: 'Sorry',
description: 'Not Found',
};
<div>
<h3>{selected_topic.title}</h3>
{selected_topic.description}
</div>
Topic
리턴에 셀렉티드토픽 타이틀과 디스크립션을 데이터 바인딩 합니다.
<div>
<h3>{selected_topic.title}</h3> {/* 선택된 토픽의 제목을 표시합니다. */}
{selected_topic.description} {/* 선택된 토픽의 설명을 표시합니다. */}
</div>
test
잘 작동하는지 체크 합니다.


Completion
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import {
HashRouter,
Route,
Routes,
NavLink,
useParams,
} from 'react-router-dom';
function Home() {
return (
<div>
<h2>Home</h2>
Home...
</div>
);
}
var contents = [
{ id: 1, title: 'HTML', description: 'HTML is ...' },
{ id: 2, title: 'JS', description: 'JS is ...' },
{ id: 3, title: 'React', description: 'React is ...' },
];
function Topic() {
var params = useParams();
var topic_id = params.topic_id;
const found_topic = contents.find((item) => item.id === Number(topic_id));
const selected_topic = found_topic || {
title: 'Sorry',
description: 'Not Found',
};
console.log('params', params, params.topic_id);
return (
<div>
<h3>{selected_topic.title}</h3>
{selected_topic.description}
</div>
);
}
function Topics() {
return (
<div>
<h2>Topics</h2>
<ul>
{contents.map((item) => (
<li key={item.id}>
<NavLink to={'/topics/' + item.id}>{item.title}</NavLink>
</li>
))}
</ul>
<Routes>
<Route path="/:topic_id" element={<Topic />} />
</Routes>
</div>
);
}
function Contact() {
return (
<div>
<h2>Contact</h2>
Contact...
</div>
);
}
function App() {
return (
<div>
<h1>Hello React Router DOM</h1>
<ul>
<li>
<NavLink to="/">Home</NavLink>
</li>
<li>
<NavLink to="/topics">Topics</NavLink>
</li>
<li>
<NavLink to="/contact">Contact</NavLink>
</li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/topics/*" element={<Topics />} />
<Route path="/contact" element={<Contact />} />
<Route path="/*" element={'Not Found'} />
</Routes>
</div>
);
}
createRoot(document.getElementById('root')).render(
<StrictMode>
<HashRouter>
<App />
</HashRouter>
</StrictMode>
);
Comment ver
// React의 엄격 모드(StrictMode)를 import 합니다. 이는 개발 단계에서 잠재적인 문제점을 식별하는 데 도움을 줍니다.
import { StrictMode } from 'react';
// React 18에서 도입된 createRoot 함수를 import 합니다. 이 함수는 React 애플리케이션을 DOM에 마운트하는 데 사용됩니다.
import { createRoot } from 'react-dom/client';
// 전역 스타일을 정의하는 CSS 파일을 import 합니다.
import './index.css';
// React Router DOM에서 제공하는 라우팅 관련 컴포넌트 및 훅을 import 합니다.
import {
HashRouter, // Hash 기반의 라우팅을 구현합니다. URL에 # 문자를 사용하여 경로를 구분합니다.
Route, // 특정 URL 경로에 매핑될 컴포넌트를 정의합니다.
Routes, // 여러 Route 컴포넌트를 감싸고, 현재 URL과 일치하는 첫 번째 Route를 렌더링합니다.
NavLink, // 현재 경로와 일치할 때 활성화된 스타일을 적용할 수 있는 <Link>의 특별한 버전입니다.
useParams, // URL 경로의 매개변수(parameter) 값을 객체 형태로 가져오는 훅입니다.
} from 'react-router-dom';
/**
* Home 섹션의 내용을 렌더링하는 함수 컴포넌트입니다.
* @returns {JSX.Element} Home 섹션을 나타내는 JSX 요소
*/
function Home() {
return (
<div>
<h2>Home</h2>
Home...
</div>
);
}
// 토픽(Topic) 목록을 담고 있는 배열입니다. 각 토픽은 id, title, description을 가집니다.
var contents = [
{ id: 1, title: 'HTML', description: 'HTML is ...' },
{ id: 2, title: 'JS', description: 'JS is ...' },
{ id: 3, title: 'React', description: 'React is ...' },
];
/**
* 특정 토픽의 상세 내용을 렌더링하는 함수 컴포넌트입니다.
* URL 파라미터를 통해 토픽 ID를 받아 해당 토픽의 정보를 표시합니다.
* @returns {JSX.Element} 특정 토픽의 상세 내용을 나타내는 JSX 요소
*/
function Topic() {
// useParams 훅을 사용하여 URL에서 동적인 파라미터(예: /topics/123의 123)를 추출합니다.
var params = useParams();
// 추출된 파라미터 객체에서 'topic_id' 값을 가져옵니다.
var topic_id = params.topic_id;
// contents 배열에서 URL 파라미터로 받은 topic_id와 일치하는 토픽을 찾습니다.
// URL 파라미터는 문자열이므로 Number()를 사용하여 숫자로 변환합니다.
const found_topic = contents.find((item) => item.id === Number(topic_id));
// 일치하는 토픽을 찾지 못한 경우를 대비하여 기본값(Not Found)을 설정합니다.
const selected_topic = found_topic || {
title: 'Sorry',
description: 'Not Found',
};
// 디버깅 목적으로 params 객체와 topic_id 값을 콘솔에 출력합니다.
console.log('params', params, params.topic_id);
return (
<div>
<h3>{selected_topic.title}</h3> {/* 선택된 토픽의 제목을 표시합니다. */}
{selected_topic.description} {/* 선택된 토픽의 설명을 표시합니다. */}
</div>
);
}
/**
* 토픽 목록과 하위 토픽 상세 페이지를 렌더링하는 함수 컴포넌트입니다.
* 중첩 라우팅을 사용하여 특정 토픽을 클릭했을 때 상세 내용을 보여줍니다.
* @returns {JSX.Element} 토픽 목록과 중첩 라우팅을 포함하는 JSX 요소
*/
function Topics() {
return (
<div>
<h2>Topics</h2>
<ul>
{/* contents 배열을 순회하며 각 토픽에 대한 링크를 생성합니다. */}
{contents.map((item) => (
<li key={item.id}>
{/* NavLink 컴포넌트를 사용하여 라우팅 링크를 생성합니다.
'to' 속성은 이동할 경로를 지정하며, 동적으로 토픽 ID를 포함합니다. */}
<NavLink to={'/topics/' + item.id}>{item.title}</NavLink>
</li>
))}
</ul>
{/* Topics 컴포넌트 내에서 중첩 라우팅을 설정합니다.
'/'는 현재 경로(예: /topics)를 기준으로 하위 경로를 정의합니다.
':topic_id'는 URL에서 변수 값을 받아 Topic 컴포넌트로 전달합니다. */}
<Routes>
<Route path="/:topic_id" element={<Topic />} />
</Routes>
</div>
);
}
/**
* Contact 섹션의 내용을 렌더링하는 함수 컴포넌트입니다.
* @returns {JSX.Element} Contact 섹션을 나타내는 JSX 요소
*/
function Contact() {
return (
<div>
<h2>Contact</h2>
Contact...
</div>
);
}
/**
* 애플리케이션의 메인 컴포넌트입니다.
* 메인 내비게이션 링크와 라우팅 설정을 포함합니다.
* @returns {JSX.Element} 애플리케이션의 전체 UI를 나타내는 JSX 요소
*/
function App() {
return (
<div>
<h1>Hello React Router DOM</h1> {/* 애플리케이션의 제목 */}
<ul>
{/* NavLink 컴포넌트를 사용하여 각 메인 페이지로 이동하는 링크를 생성합니다. */}
<li>
<NavLink to="/">Home</NavLink>
</li>
<li>
<NavLink to="/topics">Topics</NavLink>
</li>
<li>
<NavLink to="/contact">Contact</NavLink>
</li>
</ul>
{/* Routes 컴포넌트는 자식 Route들 중에서 현재 URL과 일치하는 첫 번째 Route를 찾아 렌더링합니다. */}
<Routes>
{/* 경로가 '/'일 때 Home 컴포넌트를 렌더링합니다. */}
<Route path="/" element={<Home />} />
{/* 경로가 '/topics/*'일 때 Topics 컴포넌트를 렌더링합니다.
'/*'는 /topics/ 뒤에 어떤 경로가 오더라도 Topics 컴포넌트가 렌더링되도록 합니다.
이는 Topics 컴포넌트 내에서 다시 중첩 라우팅이 처리될 수 있게 합니다. */}
<Route path="/topics/*" element={<Topics />} />
{/* 경로가 '/contact'일 때 Contact 컴포넌트를 렌더링합니다. */}
<Route path="/contact" element={<Contact />} />
{/* 정의된 어떤 경로와도 일치하지 않을 때 ('/*' 와일드카드) "Not Found" 텍스트를 렌더링합니다.
이는 404 페이지와 같은 역할을 합니다. */}
<Route path="/*" element={'Not Found'} />
</Routes>
</div>
);
}
// ID가 'root'인 DOM 요소를 찾아 React 애플리케이션을 마운트합니다.
// createRoot를 사용하여 React 18의 권장 렌더링 방식을 따릅니다.
createRoot(document.getElementById('root')).render(
// StrictMode로 애플리케이션을 감싸 개발 중 잠재적인 문제점을 감지하고 경고합니다.
<StrictMode>
{/* HashRouter로 전체 애플리케이션을 감싸 React Router의 라우팅 기능을 활성화합니다.
URL의 해시(#) 부분을 사용하여 경로를 관리하므로 서버 측 설정 없이 SPA 라우팅이 가능합니다. */}
<HashRouter>
<App /> {/* App 컴포넌트를 렌더링합니다. */}
</HashRouter>
</StrictMode>
);
출처
https://wikibook.co.kr/react-rev-ebook/
생활코딩! React 리액트 프로그래밍 (개정판) (ebook): 처음 프로그래밍을 시작하는 입문자의 눈높이
세상에서 리액트를 가장 쉽게 설명한 입문서! 생활코딩은 일반인에게 프로그래밍을 알려주는 것을 목적으로 하는 비영리 교육 활동입니다. 이 책은 생활코딩에서 제공하는 수업 가운데 리액트
wikibook.co.kr

'생활코딩! React 리액트 프로그래밍' 카테고리의 다른 글
| egoing - Context API - stackblitz ver (0) | 2025.09.06 |
|---|---|
| egoing - styled component - stackblitz ver (0) | 2025.09.05 |
| egoing - React - delete (0) | 2025.06.25 |
| egoing - React - update (0) | 2025.06.24 |
| egoing - React - create (0) | 2025.06.23 |