코딩/리액트

[새싹 성동 2기] 리액트 라우터와 사용예시

insu90 2024. 12. 6. 19:12

1. 라우터 설정 (BrowserRouter)

리액트 앱에서 라우터를 사용하려면 react-router-dom 라이브러리를 설치하고, 앱을 감싸는 BrowserRouter를 설정해야 합니다. 이를 통해 브라우저의 주소와 페이지를 동적으로 매핑할 수 있습니다.

c:\javascript\my-cra-app> npm install react-router-dom
import React from 'react';  // React 라이브러리 임포트
import ReactDOM from 'react-dom/client';  // ReactDOM 라이브러리 임포트
import './index.css';  // 스타일시트 임포트
import App from './App';  // App 컴포넌트 임포트
import reportWebVitals from './reportWebVitals';  // 웹 성능 측정 임포트
import { BrowserRouter } from 'react-router-dom';  // react-router-dom에서 BrowserRouter 임포트

const root = ReactDOM.createRoot(document.getElementById('root'));  // 'root' id를 가진 DOM 요소에 React 앱을 렌더링

root.render(
  // BrowserRouter로 App 컴포넌트를 감싸서 라우팅 기능을 사용할 수 있도록 함
  <BrowserRouter>
    <App />  {/* App 컴포넌트 렌더링 */}
  </BrowserRouter>
);

2. 라우팅할 페이지 컴포넌트 생성

index.css

/* 기본 스타일 초기화 */
* {
    margin: 0;  /* 모든 요소의 기본 마진을 제거 */
    padding: 0;  /* 모든 요소의 기본 패딩을 제거 */
    box-sizing: border-box;  /* 모든 요소의 박스 사이즈를 border-box로 설정 (패딩과 보더가 width에 포함) */
}

body {
    font-family: Arial, sans-serif;  /* 기본 글꼴을 Arial로 설정 */
    line-height: 1.6;  /* 줄 간격을 1.6으로 설정 */
    background-color: #f4f4f4;  /* 배경색을 연한 회색으로 설정 */
    color: #333;  /* 글자 색을 어두운 회색으로 설정 */
    padding: 20px;  /* body에 20px 패딩을 추가 */
}

/* 헤더 스타일 */
header {
    background-color: #35424a;  /* 헤더 배경색을 어두운 파란색으로 설정 */
    color: #ffffff;  /* 텍스트 색을 흰색으로 설정 */
    padding: 20px 0;  /* 상하 패딩을 20px로 설정 */
    text-align: center;  /* 텍스트를 중앙 정렬 */
    margin-bottom: 20px;  /* 아래쪽에 20px 마진을 추가 */
}

header h1 {
    margin: 0;  /* 헤더 제목의 기본 마진을 제거 */
}

/* 네비게이션 스타일 */
nav ul {
    list-style: none;  /* 기본 리스트 스타일을 제거 */
    padding: 0;  /* 패딩 제거 */
}

nav ul li {
    display: inline;  /* 리스트 항목을 인라인으로 배치 */
    margin: 0 10px;  /* 각 항목 간에 10px 간격 추가 */
}

nav ul li a {
    color: #ffffff;  /* 링크 색상을 흰색으로 설정 */
    text-decoration: none;  /* 링크의 밑줄을 제거 */
    font-weight: bold;  /* 링크 텍스트를 굵게 설정 */
}

nav ul li a:hover {
    text-decoration: underline;  /* 마우스를 올렸을 때 밑줄을 표시 */
}

/* 메인 콘텐츠 스타일 */
main {
    background-color: #ffffff;  /* 배경색을 흰색으로 설정 */
    padding: 20px;  /* 패딩 20px 추가 */
    border-radius: 8px;  /* 모서리를 둥글게 처리 */
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);  /* 가벼운 그림자를 추가 */
}

main h2 {
    color: #35424a;  /* 제목 색상을 어두운 파란색으로 설정 */
    margin-bottom: 10px;  /* 제목과 본문 간격을 10px로 설정 */
}

main p {
    margin-bottom: 20px;  /* 본문 내용 간격을 20px로 설정 */
}

/* 푸터 스타일 */
footer {
    background-color: #35424a;  /* 푸터 배경색을 어두운 파란색으로 설정 */
    color: #ffffff;  /* 텍스트 색을 흰색으로 설정 */
    text-align: center;  /* 텍스트 중앙 정렬 */
    padding: 10px 0;  /* 상하 패딩을 10px로 설정 */
    margin-top: 20px;  /* 푸터와 위 콘텐츠 사이에 20px 마진 추가 */
    border-radius: 8px;  /* 모서리를 둥글게 처리 */
}

footer p {
    margin: 0;  /* 푸터 텍스트의 마진을 제거 */
}

Home.js

export default function Home() {
    return (
        <>
            {/* 헤더 영역 */}
            <header>
                <h1>Welcome to Our Website!</h1>
                <nav>
                    <ul>
                        {/* 내비게이션 링크 */}
                        <li><a href="home.html">Home</a></li>
                        <li><a href="about.html">About</a></li>
                        <li><a href="contact.html">Contact</a></li>
                    </ul>
                </nav>
            </header>

            {/* 메인 콘텐츠 영역 */}
            <main>
                {/* 소개 섹션 */}
                <section>
                    <h2>Introduction</h2>
                    <p>We are glad to have you here. Explore our website to learn more about what we offer.</p>
                </section>

                {/* 최신 뉴스 섹션 */}
                <section>
                    <h2>Latest News</h2>
                    <p>Stay tuned for the latest updates and news from our team.</p>
                </section>
            </main>

            {/* 푸터 영역 */}
            <footer>
                <p>&copy; 2024 Our Website. All rights reserved.</p>
            </footer>
        </>
    );
};

About.js

export default function About() {
    return (
        <>
            {/* 헤더 영역 */}
            <header>
                <h1>About Us</h1>
                <nav>
                    <ul>
                        {/* 내비게이션 링크 */}
                        <li><a href="home.html">Home</a></li>
                        <li><a href="about.html">About</a></li>
                        <li><a href="contact.html">Contact</a></li>
                    </ul>
                </nav>
            </header>

            {/* 메인 콘텐츠 영역 */}
            <main>
                {/* 우리의 미션 섹션 */}
                <section>
                    <h2>Our Mission</h2>
                    <p>Our mission is to provide exceptional services and products that improve the lives of our customers.</p>
                </section>

                {/* 우리 팀 섹션 */}
                <section>
                    <h2>Our Team</h2>
                    <p>We are a group of passionate individuals committed to delivering the best results.</p>
                </section>

                {/* 우리의 역사 섹션 */}
                <section>
                    <h2>Our History</h2>
                    <p>Since our founding, we have continuously grown and expanded our services, always putting our customers first.</p>
                </section>
            </main>

            {/* 푸터 영역 */}
            <footer>
                <p>&copy; 2024 Our Website. All rights reserved.</p>
            </footer>        
        </>
    )
};

3. Routes와 Route

  • Routes: 여러 개의 Route 컴포넌트를 감싸는 역할을 합니다.
  • Route: path와 element 속성을 통해 경로와 해당 경로에서 보여줄 컴포넌트를 매핑합니다.

App.js

import { Route, Routes } from "react-router-dom";  // React Router에서 Route와 Routes 컴포넌트를 가져옵니다.
import About from "./About";  // About 컴포넌트를 가져옵니다.
import Home from "./Home";  // Home 컴포넌트를 가져옵니다.

const App = () => {
  return (
    <>
      {/* Routes 컴포넌트를 사용하여 여러 경로와 해당 경로에 맞는 컴포넌트를 렌더링합니다. */}
      <Routes>
        {/* "/" 경로로 접근하면 Home 컴포넌트를 렌더링 */}
        <Route path="/" element={<Home />} />

        {/* "/about" 경로로 접근하면 About 컴포넌트를 렌더링 */}
        <Route path="/about" element={<About />} />
      </Routes>
    </>
  );
};

export default App;  // App 컴포넌트를 내보냅니다.

4. Link 컴포넌트

<Link>는 HTML의 <a> 태그와 비슷하지만, 페이지를 새로 고침하지 않고 주소만 변경할 수 있습니다. 이를 통해 리액트 앱 내에서 페이지 전환을 할 수 있습니다.

App.js

import { Route, Routes, Link } from "react-router-dom";  // React Router에서 Route, Routes, Link 컴포넌트를 가져옵니다.
import About from "./About";  // About 컴포넌트를 가져옵니다.
import Home from "./Home";  // Home 컴포넌트를 가져옵니다.

const App = () => {
  return (
    <>
      {/* 페이지 내비게이션을 위한 링크. 'Link' 컴포넌트를 사용하여 경로 간 이동을 처리합니다. */}
      <Link to="/">홈</Link>  {/* '/' 경로로 이동하는 링크 */}
      | 
      <Link to="/about">소개</Link>  {/* '/about' 경로로 이동하는 링크 */}

      {/* Routes 컴포넌트 안에 정의된 각 경로에 따라 렌더링될 컴포넌트를 설정합니다. */}
      <Routes>
        {/* "/" 경로로 접근하면 Home 컴포넌트를 렌더링 */}
        <Route path="/" element={<Home />} />

        {/* "/about" 경로로 접근하면 About 컴포넌트를 렌더링 */}
        <Route path="/about" element={<About />} />
      </Routes>
    </>
  );
};

export default App;  // App 컴포넌트를 내보냅니다.
 

5. 가변 데이터 전달: URL 파라미터와 쿼리 문자열

  • URL 파라미터 (useParams): URL 경로에 포함된 동적인 데이터를 전달할 때 사용됩니다. 예: /profile/:userid → useParams 훅으로 파라미터값을 추출합니다.

App.js

import { Route, Routes, Link } from "react-router-dom";  // React Router에서 Route, Routes, Link 컴포넌트를 가져옵니다.
import About from "./About";  // About 컴포넌트를 가져옵니다.
import Home from "./Home";  // Home 컴포넌트를 가져옵니다.
import Profile from "./Profile";  // Profile 컴포넌트를 가져옵니다.

const App = () => {
  return (
    <>
      {/* 페이지 내비게이션을 위한 링크. 'Link' 컴포넌트를 사용하여 경로 간 이동을 처리합니다. */}
      <Link to="/">홈</Link>  {/* '/' 경로로 이동하는 링크, Home 컴포넌트로 이동 */}
      | 
      <Link to="/about">소개</Link>  {/* '/about' 경로로 이동하는 링크, About 컴포넌트로 이동 */}
      | 
      <Link to="/info">정보</Link>  {/* '/info' 경로로 이동하는 링크, About 컴포넌트로 이동 */}
      |
      {/* 각 프로파일에 대한 링크 생성 */}
      <Link to="/profile/mrgo">고길동 프로파일</Link>  {/* '/profile/mrgo' 경로로 이동, 고길동 프로파일을 보여줌 */}
      | 
      <Link to="/profile/mrhong">홍길동 프로파일</Link>  {/* '/profile/mrhong' 경로로 이동, 홍길동 프로파일을 보여줌 */}
      | 
      <Link to="/profile/mrshin">신길동 프로파일</Link>  {/* '/profile/mrshin' 경로로 이동, 신길동 프로파일을 보여줌 */}

      {/* Routes 컴포넌트 내에 정의된 경로에 맞는 컴포넌트를 렌더링 */}
      <Routes>
        <Route path="/" element={<Home />} />  {/* "/" 경로로 접근하면 Home 컴포넌트를 렌더링 */}
        <Route path="/about" element={<About />} />  {/* "/about" 경로로 접근하면 About 컴포넌트를 렌더링 */}
        <Route path="/info" element={<About />} />  {/* "/info" 경로로 접근하면 About 컴포넌트를 렌더링 */}
        <Route path="/profile/:userid" element={<Profile />} />  {/* "/profile/:userid" 경로로 접근하면 Profile 컴포넌트를 렌더링 */}
      </Routes>
    </>
  );
};

export default App;  // App 컴포넌트를 내보냅니다.

Profile.js

import { useParams } from "react-router-dom";  // react-router-dom에서 useParams 훅을 가져옵니다. 이 훅은 URL의 파라미터를 가져오는 데 사용됩니다.

const userDatas = {  // 사용자 정보 객체. 각 사용자의 프로필 데이터가 포함되어 있습니다.
    mrgo: {
        name: "고길동",  // 고길동의 이름
        desc: "둘리를 싫어하는 자",  // 고길동의 설명
    },
    mrhong: {
        name: "홍길동",  // 홍길동의 이름
        desc: "호부호형을 원하는 자",  // 홍길동의 설명
    },
};

const Profile = () => {
    const param = useParams();  // URL에서 파라미터를 가져옵니다. 이 경우 :userid를 가져옵니다.
    console.log(param);  // 파라미터를 콘솔에 출력하여 확인합니다.

    // userDatas 객체에서 URL 파라미터에 해당하는 사용자 정보를 가져옵니다.
    const userData = userDatas[param.userid];

    return (
        <>
            {
                userData ? (  // 만약 사용자 데이터가 존재하면
                    <div>
                        <p>이름: {userData.name}</p>  {/* 사용자 이름 표시 */}
                        <p>설명: {userData.desc}</p>  {/* 사용자 설명 표시 */}
                    </div>
                ) : (  // 사용자 데이터가 없다면
                    <div>일치하는 사용자가 없습니다.</div>  {/* 일치하는 사용자가 없다는 메시지 표시 */}
                )
            }
        </>
    );
};

export default Profile;  // Profile 컴포넌트를 내보냅니다.
  • 쿼리 문자열 (useLocation, useSearchParams): URL의 ?key=value 형태로 전달되는 데이터를 처리할 수 있습니다. 예: /about?detail=true → useLocation 또는 useSearchParams 훅을 사용하여 쿼리 문자열을 추출하고 조건부 렌더링을 할 수 있습니다.
c:\javascript\my-cra-app> npm install qs

About.js - useLocation

import { useLocation } from "react-router-dom";  // react-router-dom에서 useLocation 훅을 가져옵니다.
import qs from 'qs';  // qs는 쿼리 문자열을 파싱하는 라이브러리입니다.

export default function About() {
    // useLocation 훅을 사용하여 현재 URL의 위치 정보 (location 객체)를 가져옵니다.
    const location = useLocation();  // 현재 URL에 대한 정보가 담긴 객체
    console.log(location);  // 콘솔에 location 객체 출력 (디버깅용)

    // location.search는 URL에서 '?' 이후의 쿼리 문자열을 포함합니다.
    // qs.parse()는 이를 파싱하여 객체로 변환해줍니다. { ignoreQueryPrefix: true }는 '?'를 무시하도록 합니다.
    const query = qs.parse(location.search, { ignoreQueryPrefix: true });
    console.log(query);  // 파싱된 쿼리 객체를 콘솔에 출력 (디버깅용)

    return (
        <>
            <header>
                <h1>About Us</h1>
                <nav>
                    <ul>
                        {/* 각 페이지로 이동할 수 있는 링크들 */}
                        <li><a href="home.html">Home</a></li>
                        <li><a href="about.html">About</a></li>
                        <li><a href="contact.html">Contact</a></li>
                    </ul>
                </nav>
            </header>

            <main>
                {/* query.detail === 'true'가 참일 경우 상세 내용 출력 */}
                {
                    query.detail === 'true' && <h2>상세 내용입니다.</h2>
                }
                {/* 페이지의 기본 내용 */}
                <section>
                    <h2>Our Mission</h2>
                    <p>Our mission is to provide exceptional services and products that improve the lives of our customers.</p>
                </section>

                <section>
                    <h2>Our Team</h2>
                    <p>We are a group of passionate individuals committed to delivering the best results.</p>
                </section>

                <section>
                    <h2>Our History</h2>
                    <p>Since our founding, we have continuously grown and expanded our services, always putting our customers first.</p>
                </section>
            </main>

            <footer>
                <p>&copy; 2024 Our Website. All rights reserved.</p>
            </footer>
        </>
    )
};

About.js - useSearchParams

import { useSearchParams } from "react-router-dom";  // useSearchParams 훅을 가져옵니다.

export default function About() {
    // useSearchParams 훅을 사용하여 쿼리 파라미터를 가져옵니다.
    const [searchParam, setSearchParam] = useSearchParams();  // searchParam은 현재 쿼리 파라미터를 반환합니다.

    // URL에서 'detail' 파라미터의 값을 가져옵니다.
    const detail = searchParam.get('detail');  // detail 파라미터가 있으면 그 값을 반환합니다.
    console.log(detail);  // 콘솔에 detail 값을 출력하여 확인합니다.

    return (
        <>
            <header>
                <h1>About Us</h1>
                <nav>
                    <ul>
                        <li><a href="home.html">Home</a></li>  {/* Home 링크 */}
                        <li><a href="about.html">About</a></li>  {/* About 링크 */}
                        <li><a href="contact.html">Contact</a></li>  {/* Contact 링크 */}
                    </ul>
                </nav>
            </header>

            <main>
                {/* detail 파라미터가 'true'일 경우 상세 내용을 표시 */}
                {detail === 'true' && <h2>상세 내용입니다.</h2>}

                {/* About 페이지의 기본 내용 */}
                <section>
                    <h2>Our Mission</h2>
                    <p>Our mission is to provide exceptional services and products that improve the lives of our customers.</p>
                </section>

                <section>
                    <h2>Our Team</h2>
                    <p>We are a group of passionate individuals committed to delivering the best results.</p>
                </section>

                <section>
                    <h2>Our History</h2>
                    <p>Since our founding, we have continuously grown and expanded our services, always putting our customers first.</p>
                </section>
            </main>

            <footer>
                <p>&copy; 2024 Our Website. All rights reserved.</p>
            </footer>
        </>
    )
};

 

6. 서브 라우트 (중첩 라우트)

Route 컴포넌트를 중첩하여, 하나의 경로에서 다른 라우트를 포함할 수 있습니다. 예를 들어, Profile 페이지 내에서 다른 경로를 중첩시킬 수 있습니다.

Profiles.js

import { Link, Outlet } from 'react-router-dom';  // Link와 Outlet 컴포넌트를 react-router-dom에서 가져옵니다.

export default function Profiles() {
    return (
        <>
            <h3>사용자 목록</h3>
            {/* 사용자 프로파일로 이동하는 링크들 */}
            <Link to="/profiles/mrgo">고길동 프로파일</Link> 
            | <Link to="/profiles/mrhong">홍길동 프로파일</Link>
            | <Link to="/profiles/mrshin">신길동 프로파일</Link>

            {/* 서브 라우트가 렌더링될 위치 */}
            <Outlet />  
        </>
    );
}

 

App.js

import { Route, Routes, Link } from "react-router-dom";  // React Router에서 필요한 컴포넌트들을 가져옵니다.
import About from "./About";  // About 컴포넌트
import Home from "./Home";  // Home 컴포넌트
import Profile from "./Profile";  // Profile 컴포넌트
import Profiles from "./Profiles";  // Profiles 컴포넌트

const App = () => {
  return (
    <>
      {/* 네비게이션 링크 */}
      <Link to="/">홈</Link> | <Link to="/about">소개</Link> | <Link to="/info">정보</Link>
      {/* 프로파일 목록으로 이동하는 링크 */}
      | <Link to="/profiles">프로파일</Link>

      {/* Routes 설정 */}
      <Routes>
        <Route path="/" element={<Home />} />  {/* 홈 경로 설정 */}
        <Route path="/about" element={<About />} />  {/* 소개 경로 설정 */}
        <Route path="/info" element={<About />} />  {/* 정보 경로 설정 */}
        <Route path="/profiles" element={<Profiles />}>  {/* 프로파일 목록 페이지 */}
          <Route path=":userid" element={<Profile />} />  {/* 특정 사용자의 프로파일을 보여주는 서브 라우트 */}
        </Route>
      </Routes>
    </>
  );
};

export default App;

 

7. 공통 레이아웃 컴포넌트

중첩 라우트와 Outlet 컴포넌트를 이용해 각 페이지에서 공통적으로 보여줘야 하는 레이아웃을 처리할 때 유용합니다.

 

Layout.js

import { Link, Outlet } from "react-router-dom";  // react-router-dom에서 Link와 Outlet을 가져옵니다.

export default function Layout() {
    return (
        <>
            {/* 헤더 영역: 네비게이션 메뉴 */}
            <header style={{ background: 'lightgray', padding: 16, fontSize: 24 }}>
                {/* 각 메뉴를 Link로 생성. 클릭 시 해당 경로로 이동 */}
                <Link to="/">홈</Link> |
                <Link to="/about">소개</Link> |
                <Link to="/info">정보</Link> |
                <Link to="/profiles">프로파일</Link>
            </header>
            <main>
                {/* 서브 라우트가 렌더링될 위치 */}
                <Outlet />  {/* Outlet은 하위 경로에서 컴포넌트를 렌더링합니다. */}
            </main>
        </>
    );
}

 

App.js

import { Route, Routes } from "react-router-dom";  // react-router-dom에서 Route와 Routes를 가져옵니다.
import About from "./About";  // About 컴포넌트
import Home from "./Home";  // Home 컴포넌트
import Profile from "./Profile";  // Profile 컴포넌트
import Profiles from "./Profiles";  // Profiles 컴포넌트
import Layout from "./Layout";  // Layout 컴포넌트 (메뉴와 Outlet 포함)

const App = () => {
  return (
    <>
      {/* Routes: URL 경로에 맞는 컴포넌트를 렌더링 */}
      <Routes>
        {/* Layout 컴포넌트로 전체 페이지 감싸기 */}
        <Route element={<Layout />}>
          {/* 기본 홈 경로 */}
          <Route path="/" element={<Home />} />
          {/* 소개 페이지 경로 */}
          <Route path="/about" element={<About />} />
          {/* 정보 페이지 경로 */}
          <Route path="/info" element={<About />} />
          {/* 프로파일 목록 페이지 경로 */}
          <Route path="/profiles" element={<Profiles />}>
            {/* 프로파일 서브 라우트 경로 (사용자별 프로파일 페이지) */}
            <Route path=":userid" element={<Profile />} />
          </Route>
        </Route>
      </Routes>
    </>
  );
};
export default App;

 

 

 

 

 

 

*생성형 AI 활용한 클라우드&보안 전문가 양성캠프 과정의 교육내용 정리 자료입니다.