import { useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';
function App() {
const [todos, setTodos] = useState([
{ id: 1, checked: true, text: "자바스크립트 공부하기" },
{ id: 2, checked: false, text: "리액트 공부하기" },
{ id: 3, checked: false, text: "할 일 목록 앱 만들기" },
]);
return (
<TodoTemplate>
<TodoInsert />
<TodoList todos={todos} />
</TodoTemplate>
);
}
export default App;
import TodoListItem from './TodoListItem';
import './TodoList.css';
export default function TodoList({ todos }) {
return (
<div className="TodoList">
{todos.map((todo) => (
<TodoListItem key={todo.id} todo={todo}/>
))}
</div>
);
}
import './TodoListItem.css';
import { MdCheckBox, MdCheckBoxOutlineBlank, MdRemoveCircleOutline } from 'react-icons/md';
const TodoListItem = ({ todo }) => {
const { id, checked, text } = todo;
return (
<div className="TodoListItem">
<div className={checked ? 'checkBox checked' : 'checkBox'}>
{checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
<div className="text">{text}</div>
</div>
<div className="remove">
<MdRemoveCircleOutline />
</div>
</div>
);
};
export default TodoListItem;
import { useState } from 'react';
import './TodoInsert.css';
import { MdAdd } from 'react-icons/md';
const TodoInsert = () => {
const [value, setValue] = useState('');
const changeValue = e => setValue(e.target.value);
return (
<form className="TodoInsert">
<input type="text" placeholder="할일을 입력하세요." value={value} onChange={changeValue}/>
<button type="submit">
<MdAdd />
</button>
</form>
);
};
export default TodoInsert;
import { useRef, useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';
function App() {
const [todos, setTodos] = useState([
{ id: 1, checked: true, text: '자바스크립트 공부하기' },
{ id: 2, checked: false, text: '리액트 공부하기' },
{ id: 3, checked: false, text: '할 일 목록 앱 만들기' },
]);
const nextId = useRef(4);
// Create ⭐️⭐️⭐️
const insertTodo = (text) => {
const newTodos = todos.concat({ id: nextId.current, checked: false, text });
setTodos(newTodos);
nextId.current++;
};
return (
<TodoTemplate>
<TodoInsert insertTodo={insertTodo} />
<TodoList todos={todos} />
</TodoTemplate>
);
}
export default App;
import { useState } from 'react';
import './TodoInsert.css';
import { MdAdd } from 'react-icons/md';
const TodoInsert = ({ insertTodo }) => {
const [value, setValue] = useState('');
const changeValue = (e) => setValue(e.target.value);
const onSubmit = e => {
e.preventDefault();
insertTodo(value);
setValue("");
}
return (
<form className="TodoInsert" onSubmit={onSubmit}>
<input type="text" placeholder="할일을 입력하세요." value={value} onChange={changeValue} />
<button type="submit" >
<MdAdd />
</button>
</form>
);
};
export default TodoInsert;
button의 type="submit"은 form이 submit되기 때문에 form의 onSubmit에 함수를 넣어주자
CRUD
create read update delete
import { useRef, useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';
function App() {
const [todos, setTodos] = useState([
{ id: 1, checked: true, text: '자바스크립트 공부하기' },
{ id: 2, checked: false, text: '리액트 공부하기' },
{ id: 3, checked: false, text: '할 일 목록 앱 만들기' },
]);
const nextId = useRef(4);
const insertTodo = (text) => {
const newTodos = todos.concat({ id: nextId.current, checked: false, text });
setTodos(newTodos);
nextId.current++;
};
// Delete ⭐️⭐️⭐️
const removeTodo = (id) => {
const newTodos = todos.filter((todo) => todo.id !== id);
setTodos(newTodos);
};
return (
<TodoTemplate>
<TodoInsert insertTodo={insertTodo} />
<TodoList todos={todos} removeTodo={removeTodo} />
</TodoTemplate>
);
}
export default App;
import TodoListItem from './TodoListItem';
import './TodoList.css';
export default function TodoList({ todos, removeTodo }) {
return (
<div className="TodoList">
{todos.map((todo) => (
<TodoListItem key={todo.id} todo={todo} removeTodo={removeTodo} />
))}
</div>
);
}
import './TodoListItem.css';
import { MdCheckBox, MdCheckBoxOutlineBlank, MdRemoveCircleOutline } from 'react-icons/md';
const TodoListItem = ({ todo, removeTodo }) => {
const { id, checked, text } = todo;
return (
<div className="TodoListItem">
<div className={checked ? 'checkBox checked' : 'checkBox'}>
{checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
<div className="text">{text}</div>
</div>
<div className="remove" onClick={() => removeTodo(id)}> {/* id 전달 */}
<MdRemoveCircleOutline />
</div>
</div>
);
};
export default TodoListItem;
import { useRef, useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';
function App() {
const [todos, setTodos] = useState([
{ id: 1, checked: true, text: '자바스크립트 공부하기' },
{ id: 2, checked: false, text: '리액트 공부하기' },
{ id: 3, checked: false, text: '할 일 목록 앱 만들기' },
]);
const nextId = useRef(4);
const insertTodo = (text) => {
const newTodos = todos.concat({ id: nextId.current, checked: false, text });
setTodos(newTodos);
nextId.current++;
};
const removeTodo = (id) => {
const newTodos = todos.filter((todo) => todo.id !== id);
setTodos(newTodos);
};
// Update ⭐️⭐️⭐️⭐️⭐️
const toggleTodo = (id) => {
const newTodos = todos.map((todo) =>
todo.id === id ? { ...todo, checked: !todo.checked } : todo
);
setTodos(newTodos);
};
return (
<TodoTemplate>
<TodoInsert insertTodo={insertTodo} />
<TodoList todos={todos} removeTodo={removeTodo} toggleTodo={toggleTodo} />
</TodoTemplate>
);
}
export default App;
const toggleTodo = (id) => {
const newTodos = todos.map((todo) =>
todo.id === id ? { ...todo, checked: !todo.checked } : todo
);
setTodos(newTodos);
};
이 코드 아주 중요하다 !! ⭐️⭐️⭐️⭐️⭐️
import TodoListItem from './TodoListItem';
import './TodoList.css';
export default function TodoList({ todos, removeTodo, toggleTodo }) {
return (
<div className="TodoList">
{todos.map((todo) => (
<TodoListItem key={todo.id} todo={todo} removeTodo={removeTodo} toggleTodo={toggleTodo} />
))}
</div>
);
}
import './TodoListItem.css';
import { MdCheckBox, MdCheckBoxOutlineBlank, MdRemoveCircleOutline } from 'react-icons/md';
const TodoListItem = ({ todo, removeTodo, toggleTodo }) => {
const { id, checked, text } = todo;
return (
<div className="TodoListItem">
<div className={checked ? 'checkBox checked' : 'checkBox'} onClick={() => toggleTodo(id)}>
{checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
<div className="text">{text}</div>
</div>
<div className="remove" onClick={() => removeTodo(id)}>
<MdRemoveCircleOutline />
</div>
</div>
);
};
export default TodoListItem;
npm run build
배포 가능한 형태로 빌드한 결과물 ⬇️
npm i http-server -g
← 웹 서버를 설치
cd build
← 빌드 디렉토리로 이동
index.html <= 기본 페이지. 해당 페이지에서 빌드된 JS 파일을 로딩
npx http-server
← 웹 서버를 실행. 현재 디렉토리를 Web Document Root 디렉토리로 설정한 상태로 웹 서버를 실행.
import { createContext } from "react";
const TodoContext = createContext();
export default TodoContext;
import { useRef, useState } from 'react';
import TodoContext from './TodoContext';
export default function TodoProvider({ children }) {
const [todos, setTodos] = useState([
{ id: 1, checked: true, text: '자바스크립트 공부하기' },
{ id: 2, checked: false, text: '리액트 공부하기' },
{ id: 3, checked: false, text: '할 일 목록 앱 만들기' },
]);
const nextId = useRef(4);
// create
const insertTodo = (text) => {
const newTodos = todos.concat({ id: nextId.current, checked: false, text });
setTodos(newTodos);
nextId.current++;
};
// delete
const removeTodo = (id) => {
const newTodos = todos.filter((todo) => todo.id !== id);
setTodos(newTodos);
};
// update
const toggleTodo = (id) => {
const newTodos = todos.map((todo) =>
todo.id === id ? { ...todo, checked: !todo.checked } : todo
);
setTodos(newTodos);
};
return (
<TodoContext.Provider value={{ todos, insertTodo, removeTodo, toggleTodo }}>
{children}
</TodoContext.Provider>
);
}
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';
import TodoProvider from './components/TodoProvider';
function App() {
return (
<TodoProvider>
<TodoTemplate>
<TodoInsert />
<TodoList />
</TodoTemplate>
</TodoProvider>
);
}
export default App;
TodoInsert.js
import { useContext, useState } from 'react';
import './TodoInsert.css';
import { MdAdd } from 'react-icons/md';
import TodoContext from './TodoContext';
const TodoInsert = () => {
const { insertTodo } = useContext(TodoContext);
const [value, setValue] = useState('');
const changeValue = (e) => setValue(e.target.value);
const onSubmit = (e) => {
e.preventDefault();
insertTodo(value);
setValue('');
};
return (
<form className="TodoInsert" onSubmit={onSubmit}>
<input type="text" placeholder="할일을 입력하세요." value={value} onChange={changeValue} />
<button type="submit">
<MdAdd />
</button>
</form>
);
};
export default TodoInsert;
TodoList.js
import TodoListItem from './TodoListItem';
import './TodoList.css';
import { useContext } from 'react';
import TodoContext from './TodoContext';
export default function TodoList() {
const { todos } = useContext(TodoContext);
return (
<div className="TodoList">
{todos.map((todo) => (
<TodoListItem key={todo.id} todo={todo} />
))}
</div>
);
}
TodoListItem.js
🚨 todo 는 context 변수가 아니고 부모에게 받는 props 다 !
import { useContext } from 'react';
import './TodoListItem.css';
import { MdCheckBox, MdCheckBoxOutlineBlank, MdRemoveCircleOutline } from 'react-icons/md';
import TodoContext from './TodoContext';
const TodoListItem = ({ todo }) => {
const { removeTodo, toggleTodo } = useContext(TodoContext);
const { id, checked, text } = todo;
return (
<div className="TodoListItem">
<div className={checked ? 'checkBox checked' : 'checkBox'} onClick={() => toggleTodo(id)}>
{checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
<div className="text">{text}</div>
</div>
<div className="remove" onClick={() => removeTodo(id)}>
<MdRemoveCircleOutline />
</div>
</div>
);
};
export default TodoListItem;
정적 서비스 : 파일 고정. 파일을 요청해야만 내려갔다.
⬇️
동적 서비스 : CGI 에만 요청해서 CGI가 해당 파일만 제공
여러 명의 요청을 처리할 수 있도록 멀티 쓰레드 환경으로 : 자바 servlet/JSP
서버가 내뱉는 최종적인 결과물은 html 문서이다.
MPA : Multiple Page Application
SPA : 최초 index.html 가 한 번에 내려오고 라우팅을 통해 필요에 따라 보여지게 된다.
import { BrowerRouter, Routes, Route } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>
);
}
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
loader: async () => {
const data = await fetchDataFromHome();
return data;
},
errorElement: <ErrorPage />
},
{
path: "/about",
element: <About />,
},
{
path: "/contact",
element: <Contact />,
},
]);
function App() {
return <RouterProvider router={router} />;
Home.js
export default function Home() {
return (
<div>
<h1>Home</h1>
<h2>가장 먼저 보이는 페이지</h2>
</div>
);
}
About.js
export default function About() {
return (
<div>
<h1>About</h1>
<h2>리액트 라우트 연습</h2>
</div>
);
}
import { BrowserRouter } from 'react-router-dom'
import Home from './Home';
function App() {
return (
<BrowserRouter></BrowserRouter>
);
}
export default App;
Route 컴포넌트 : 주소 패턴에 따라 다른 컴포넌트를 제공
ex) http://localhost:3000/ ⇒ Home 컴포넌트가 제공
http://localhost:3000/about ⇒ About 컴포넌트가 제공
<Routes>
<Route path="주소 규칙" element={보여 줄 컴포넌트 JSX} />
</Routes>
import { BrowserRouter, Route, Routes } from 'react-router-dom'
import Home from './Home';
import About from './About';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/about" element={<About />}/>
</Routes>
</BrowserRouter>
);
}
export default App;
http://localhost:3000/ ⇒ Home 컴포넌트가 제공
http://localhost:3000/about ⇒ About 컴포넌트가 제공
Link 컴포넌트 => 클릭하면 다른 주소로 이동시켜주는 컴포넌트
<Link to="경로">링크 이름</Link>
import { BrowserRouter, Link, Route, Routes } from 'react-router-dom'
import Home from './Home';
import About from './About';
function App() {
return (
<BrowserRouter>
<ul>
<li><Link to="/">홈</Link></li>
<li><Link to="/about">소개</Link></li>
</ul>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/about" element={<About />}/>
</Routes>
</BrowserRouter>
);
}
export default App;
import { BrowserRouter, Link, Route, Routes } from 'react-router-dom'
import Home from './Home';
import About from './About';
function App() {
return (
<BrowserRouter>
<ul>
<li><Link to="/">홈</Link></li>
<li><Link to="/about">소개</Link></li>
<li><Link to="/info">정보</Link></li>
</ul>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/about" element={<About />}/>
<Route path="/info" element={<About />}/>
</Routes>
</BrowserRouter>
);
}
export default App;
/profile/honggildong
/profile?name=honggildong&age=23
:파라미터이름
형식으로 설정import { useParams } from "react-router-dom";
const users = {
mrgo: {
name: '고길동',
desc: "둘리를 싫어하는 자"
},
mrhong: {
name: '홍길동',
desc: "호부호형을 원하는 자"
}
};
// http://localhost:3000/profile/mrgo
// ~~~~
// userid 변수 이름으로 전달
// 주소에 포함된 사용자 식별자(여기에서는 userid)에 사용자 정보를 출력
export default function Profile() {
// 주소에 포함된 파라미터를 추출
const params = useParams();
// 파라미터에서 userid의 값을 추출해서
// 해당 값을 이용해서 users 객체에서 일치하는 사용자 정보를 추출
const profile = users[params.userid];
return (
<>
{
profile ? (
<>
<h1>{profile.name}</h1>
<h2>{profile.desc}</h2>
</>
) : (
<h1>일치하는 사용자가 없습니다.</h1>
)
}
</>
)
}
🔑
const params = useParams();
const profile = user[params.userid];
import { BrowserRouter, Link, Route, Routes } from 'react-router-dom'
import Home from './Home';
import About from './About';
import Profile from './Profile';
function App() {
return (
<BrowserRouter>
<ul>
<li><Link to="/">홈</Link></li>
<li><Link to="/about">소개</Link></li>
<li><Link to="/info">정보</Link></li>
<li><Link to="/profile/mrgo">고길동 프로필</Link></li>
<li><Link to="/profile/mrhong">홍길동 프로필</Link></li>
<li><Link to="/profile/none">없는 프로필</Link></li>
</ul>
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/about" element={<About />} />
<Route path="/info" element={<About />} />
<Route path="/profile/:userid" element={<Profile />} />
</Routes>
</BrowserRouter>
);
}
export default App;
🔑
<Route path="/profile/:userid" element={<Profile />} />
....?query_name=query_value&query_name2=query_value2
ex) ?name=hong&age=23&phone=01029822002
const [searchParams, setSearchParams] = useSearchParams();
~~~~~~~~~~~~
- 첫 번째 값은 쿼리 파라미터를 조회하거나 수정하는 메서드들이 담긴 객체를 반환
- get 메서드를 통해 특정 쿼리 파라미터를 조회
- set 메서드를 통해 특정 쿼리 파라미터를 업데이트
- 만약 조회 시 쿼리 파라미터가 존재하지 않는다면 null을 반환
- 두 번째 값은 쿼리 파라미터를 객체 형태로 업데이터할 수 있는 함수를 반환
npm install qs
사용하는 방법 ⬇️
const location = useLocation();
const queries = qs.parse(location.search, { ignoreQueryPrefix: true });
~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
| ?를 파싱에서 제외
<-- 쿼리 문자열을 이름, 값 형식으로 반환
ex) { name: "hong", age: 23 }
import { useLocation } from "react-router-dom";
import qs from 'qs';
export default function About() {
// 쿼리 스트링을 추출 ==> ?aaaa=aaaa&bbbb=bbbb&cccc=cccc
const location = useLocation();
const queries = qs.parse(location.search, { ignoreQueryPrefix: true });
console.log(queries);
// http://localhost:3000/about?detail=true&name=hong&age=23 형태로 요청하면
// { detail: 'true', name: 'hong', age: '23' }
// ~~~~~ ~~~~~ ~~~~ <== 쿼리 스트링으로 전달되는 모든 값은 문자열 타입을 가짐
return (
<div>
<h1>About</h1>
<h2>리액트 라우트 연습</h2>
{
queries.detail === "true" && <h2>상세 내역입니다.</h2>
}
</div>
);
}
http://localhost:3000/about
http://localhost:3000/about?
http://localhost:3000/about?none=true
http://localhost:3000/about?none=
http://localhost:3000/about?deatil=true ⇐ 상세 내용이 출력
http://localhost:3000/about?detail=false
http://localhost:3000/about?detail=
import { useSearchParams } from "react-router-dom";
export default function About() {
/*
const location = useLocation();
const queries = qs.parse(location.search, { ignoreQueryPrefix: true });
*/
const [searchParams, setSearchParams] = useSearchParams();
const detail = searchParams.get("detail");
return (
<div>
<h1>About</h1>
<h2>리액트 라우트 연습</h2>
{
detail === "true" && <h2>상세 내역입니다.</h2>
}
</div>
);
}
searchParams를 사용하면 별도로 파싱할 필요 없이 get으로 가져오면 되니 간편하다 !
복잡한 애플리케이션의 URL과 화면 계층을 효과적으로 관리
Outlet과 children 속성을 활용해 계층 구조를 반영한 라우팅을 설정
특정 라우트의 하위 경로로 구성된 라우트
부모 라우트의 레이아웃을 공유하면서 자식 컴포넌트를 렌더링
🔑 Outlet
import { Link, Outlet } from "react-router-dom";
export default function Profiles() {
return (
<>
<h1>사용자 목록</h1>
<ul>
<li><Link to="/profiles/mrgo">고길동 프로필</Link></li>
<li><Link to="/profiles/mrhong">홍길동 프로필</Link></li>
<li><Link to="/profiles/none">없는 프로필</Link></li>
</ul>
<hr/>
{/* Route의 children으로 들어오는 JSX 엘리먼트를 보여주는 역할 */}
<Outlet />
</>
)
}
import { BrowserRouter, Link, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profiles from "./Profiles";
function App() {
return (
<BrowserRouter>
<ul>
<li><Link to="/">홈</Link></li>
<li><Link to="/about">소개</Link></li>
<li><Link to="/info">정보</Link></li>
{/*
<li><Link to="/profile/mrgo">고길동 프로파일</Link></li>
<li><Link to="/profile/mrhong">홍길동 프로파일</Link></li>
<li><Link to="/profile/none">없는 프로파일</Link></li>
*/}
<li><Link to="/profiles">프로파일</Link></li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/info" element={<About />} />
{/*
<Route path="/profile/:userid" element={<Profile />} />
*/}
<Route path="/profiles" element={<Profiles />}>
<Route path=":userid" element={<Profile />} />
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
중첩된 라우트와 Outlet 컴포넌트를 이용해 각 페이지(컴포넌트)에서 공통적으로 보여줘야 하는 레이아웃을 처리할 때 유용
import { Link, Outlet } from "react-router-dom";
import "./Layout.css";
export default function Layout() {
return (
<div>
<header>
<ul>
<li><Link to="/">홈</Link></li>
<li><Link to="/about">소개</Link></li>
<li><Link to="/info">정보</Link></li>
<li><Link to="/profiles">프로파일</Link></li>
</ul>
</header>
<main>
<Outlet />
</main>
</div>
)
}
둘러싸야만 서브라우팅이 되고 컴포넌트들이 자식이 되어 Outlet으로 간다.
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profiles from "./Profiles";
import Profile from "./Profile";
import Layout from "./Layout";
function App() {
return (
<BrowserRouter>
<Routes>
<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>
</BrowserRouter>
);
}
export default App;
Layout으로 둘러싸주기 !! (중첩 라우트 사용)
Link 컴포넌트를 사용하지 않고 페이지를 이동할 때 사용하는 훅 함수
import { Link, Outlet, useNavigate } from "react-router-dom";
import "./Layout.css";
export default function Layout() {
const navigate = useNavigate();
return (
<div>
<header>
<ul>
<li><Link to="/">홈</Link></li>
<li><Link to="/about">소개</Link></li>
<li><Link to="/info">정보</Link></li>
<li><Link to="/profiles">프로파일</Link></li>
</ul>
<button onClick={() => navigate(-1)}>이전 페이지로 이동</button>
<button onClick={() => navigate("/info")}>정보 페이지로 이동</button>
</header>
<main>
<Outlet />
</main>
</div>
)
}
🔑 이전 페이지로 이동 :
onClick={() => navigate(-1)}
링크에서 사용하는 경로가 현재 라우트의 경로와 일치하는 경우 특정 스타일 또는 CSS 클래스를 적용하는 컴포넌트
이 컴포넌트를 사용하면 style 또는 className을 설정할 때 { isActive: boolean } 을 매개변수로 전달받는 함수를 정의할 수 있다.
import { Link, NavLink, Outlet, useNavigate } from "react-router-dom";
import "./Layout.css";
export default function Layout() {
const navigate = useNavigate();
return (
<div>
<header>
<ul>
<li><Link to="/">홈</Link></li>
<li><NavLink to="/about" style={
({ isActive }) => isActive ? { color: "red" } : undefined
}>소개</NavLink></li>
<li><Link to="/info">정보</Link></li>
<li><Link to="/profiles">프로파일</Link></li>
</ul>
<button onClick={() => navigate(-1)}>이전 페이지로 이동</button>
<button onClick={() => navigate("/info")}>정보 페이지로 이동</button>
</header>
<main>
<Outlet />
</main>
</div>
)
}
Route 컴포넌트의 path props의 값으로 *
를 사용하면 아무 텍스트나 매칭한다는 뜻이 되며, 라우트 엘리먼트의 상단에 위치하는 라우트의 규칙을 모두 확인하고, 일치하는 라우트가 없다면 이 라우트가 화면에 나타나게 됨
const NotFound = () => {
return (
<div style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 64,
position: "absolute",
width: "100%",
height: "100%",
}}>404 Not Found</div>
);
}
export default NotFound;
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profiles from "./Profiles";
import Profile from "./Profile";
import Layout from "./Layout";
import NotFound from "./NotFound";
function App() {
return (
<BrowserRouter>
<Routes>
<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>
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
export default App;
컴포넌트를 화면에 보여주는 순간 다른 컴포넌트(페이지)로 이동할 때 사용
페이지를 리다이렉트할 때 사용
const Login = () => {
return (
<h1>로그인 페이지</h1>
)
}
export default Login;
import { Navigate } from "react-router-dom";
const MyPage = () => {
const isLoggedIn = false;
if (!isLoggedIn) {
return <Navigate to="/login" replace={true} />
}
return (
<h1>마이 페이지</h1>
)
}
export default MyPage;
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profiles from "./Profiles";
import Profile from "./Profile";
import Layout from "./Layout";
import NotFound from "./NotFound";
import Login from "./Login";
import MyPage from "./MyPage";
function App() {
return (
<BrowserRouter>
<Routes>
<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 path="/login" element={<Login />} />
<Route path="/mypage" element={<MyPage />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
export default App;
import { Link, NavLink, Outlet, useNavigate } from "react-router-dom";
import "./Layout.css";
export default function Layout() {
const navigate = useNavigate();
return (
<div>
<header>
<ul>
<li><Link to="/">홈</Link></li>
<li><NavLink to="/about" style={
({ isActive }) => isActive ? { color: "red" } : undefined
}>소개</NavLink></li>
<li><Link to="/info">정보</Link></li>
<li><Link to="/profiles">프로파일</Link></li>
<li><Link to="/mypage">마이페이지</Link></li>
</ul>
<button onClick={() => navigate(-1)}>이전 페이지로 이동</button>
<button onClick={() => navigate("/info")}>정보 페이지로 이동</button>
</header>
<main>
<Outlet />
</main>
</div>
)
}
const isLoggedIn = false;
이기 때문에 로그인 페이지로 이동한다.
<Navigate to="/login" replace={true} />
프로파일 → 마이페이지로 이동하면 히스토리에 다음과 같이 기록 => /profile → /login
이전 페이지로 이동을 클릭하면 => /profile
<Navigate to="/login" replace={false} />
프로파일 → 마이페이지로 이동하면 히스토리에 다음과 같이 기록 => /profile → /mypage → /login
이전 페이지로 이동을 클릭하면 => /mypage → /login
const isLoggedIn = true;
로 바꾸면 마이페이지가 나온다.
replace={true} 로 해놓자 !!