요구사항 분석하기 → 리액트 앱 만들기 → 폰트 설정하기 → 이미지 준비하기
Home 페이지
사용자가 앱에 접속하면 처음으로 만나는 페이지다. 인덱스 페이지라고도 부르며 경로는 ‘/’
입니다. 대문 역할을 합니다.
New 페이지
새로운 일기를 추가하는 페이지이며 경로는 ‘/new’입니다.
Diary 페이지
작성한 일기를 상세히 조회하는 페이지며 경로는 ‘/diary/id’이다. Home에서 일기 리스트를 조회한 다음, 특정 일기를 클릭하면 Diary 페이지로 이동한다. 이런 페이지를 상세 또는 콘텐츠 페이지라고 한다.
Edit 페이지
일기를 수정하는 페이지이며 경로는 ‘/edit/id’입니다. Home 또는 Diary페이지에서 <수정하기> 버튼을 클릭하면 Edit 페이지로 이동합니다. New 페이지와 유사하며 <삭제하기> 버튼이 추가되었습니다.
[감정 일기장] 프로젝트를 위한 새 리액트 앱을 만들고 불필요한 파일과 코드를 삭제한다.
import './App.css';
function App() {
return <div className="App"></div>;
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App />
);
4. 실행 npm run start
리액트 앱에서 사용자가 원하는 폰트를 지정할 때는 파일을 다운로드해 프로젝트에 포함하거나 웹에서 불러오는 방법이 있습니다. 이 프로젝트에서는 특정 URL로 폰트를 가져오는 ‘웹 폰트’ 방식을 이용합니다.
구글 Fonts에 접속해 폰트 가져오기
해당 주소로 접속해 ‘Nanum Pen Script’와 ‘Yeon Sung’ 폰트를 검색하고 Styles 섹션 → Select Regular 400을 클릭해 폰트를 선택한다. 2개의 폰트가 [Selected families]창에 정상적으로 추가되었다면 웹 폰트 주소를 환인하기 위해 <style> 태그 항목에서 @import~로 시작하는 웹 폰트주소를 복사해 index.css에 붙여 다음과 같이 수정한다.
@import url('https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&family=Yeon+Sung&display=swap');
body{
font-family: "Nanum Pen Script";
margin: 0px;
}
감정 이미지 다운로드하기
https://github.com/winterlood/one-bite-react/releases/tag/emotion
다음 링크로 접속해 프로젝트 src/img 폴더 생성, 파일명 그대로 5개의 감정 이미지를 저장한다.
이미지를 불러오는 함수 만들기
이미지 번호에 맞게 적절한 이미지를 반환하는 함수를 만드는게 필요하다.
src 폴더에서 util.js 파일을 만들고 다음과 같이 작성한다.
import emotion1 from "./img/emotion1.png";
import emotion2 from "./img/emotion2.png";
import emotion3 from "./img/emotion3.png";
import emotion4 from "./img/emotion4.png";
import emotion5 from "./img/emotion5.png";
export const getEmotionImgById = (emotionid) => {
const targetEmotionId = String(emotionid); // emotionId가 숫자일 수 있으므로 명시적 형 변환
switch (targetEmotionId) {
case "1":
return emotion1;
case "2":
return emotion2;
case "3":
return emotion3;
case "4":
return emotion4;
case "5":
return emotion5;
default:
return null;
}
};
import './App.css';
import { getEmotionImgById } from './util';
function App() {
return (
<div className="App">
<img alt="감정1" src={getEmotionImgById(1)} />
<img alt="감정2" src={getEmotionImgById(2)} />
<img alt="감정3" src={getEmotionImgById(3)} />
<img alt="감정4" src={getEmotionImgById(4)} />
<img alt="감정5" src={getEmotionImgById(5)} />
</div>
);
}
export default App;
라우팅은 경로를 의미하는 Route와 진행을 뜻하는 ing가 합쳐진 단어로 ‘경로를 지정하는 과정’이라는 뜻이다.
서울에서 사는 사람이 부산에 사는 사람에게 핸드폰으로 메시지를 보낼 때 이 메시지 데이터가 핸드폰에서 다른 핸드폰으로 바로 전송되는 것이 아니라 중간중간 설치된 "라우터"라 불리는 장비를 거친다. 서울에서 부산까지 한 번에 데이터를 전송하기에 거리가 너무 멀기 때문이다.
이처럼 라우팅은 “데이터 전달을 목적으로 최적의 경로를 찾아 데이터를 전송하는 과정”이라고 정의할 수 있다.
페이지 라우팅은 요청에 따라 적절한 페이지를 반환하는 일련의 과정이다.
도메인 주소가 https://winterlood.com인 웹서비스에서 winterlood/blog 또는 winterlood.com/books와 같은 url로 페이지 요청을 하면, 웹서비스는 요청한 blog, books 페이지를 사용자에게 보낸다. 이때 요청 url에서 도메인 주소 뒤에 붙는 blog, books를 ‘경로(path)’라고 한다.
결국 페이지 라우팅은 “URL 요청 경로에 맞게 적절한 페이지를 보여주는 과정”이다.
페이지 라우팅의 구현은 웹 페이지를 어디서 만드느냐에 따라 서버 사이드(Server Side) 렌더링과 클라이언트 사이드(Client Side) 렌더링 두 가지로 구분한다. 리액트는 브라우저에 페이지를 만드는 '클라이언트 사이드 렌더링' 방식을 채택한다.
1) 서버 사이드 렌더링
서버 사이드 렌더링에서 페이지 라우팅은 다음과 같이 동작한다.
사용자가 버튼 또는 링크를 클릭해 페이지를 이동할 때는 다음과 같이 동작한다.
서버 사이드 렌더링은 이처럼 웹 브라우저에 표시할 페이지를 웹 서버에 만들어 전달한다.
2) 클라이언트 사이드 렌더링
리액트 앱은 html 파일이 하나뿐인 단일 페이지 애플리케이션이므로 클라이언트 사이드 렌더링으로 페이지를 라우팅한다.
이는 페이지를 브라우저가 직접 만드는데 다음과 같이 동작한다.
클라이언트 사이드 렌더링의 핵심은 사용자가 보는 페이지를 웹서버가 아닌 브라우저가 완성한다는 점이다.
브라우저는 처음 접속할 때만 서버에게 데이터를 요청하며, 페이지를 이동할 때는 별도의 요청을 하지 않는다.
리액트 라우터(React Router)는 Remix 팀에서 제작한 오픈소스 라이브러리이다. 이 페이지 라우팅 전용 라이브러리를 이용하여 필요한 기능을 손쉽게 구현할 수 있다.
npm i react-router-dom
터미널에 위 명령어를 입력해 리액트 라우터를 설치한다.
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
BrowserRouter에는 브라우저의 주소 변경을 감지하는 기능이 있다.
페이지 역할을 담당할 컴포넌트를 만들어 구성한다. src/pages 폴더에 각각 만든다.
const Home = () => {
return (
<div>Home 페이지</div>
);
};
export default Home;
const New = () => {
return (
<div>New 페이지</div>
);
};
export default New;
const Diary = () => {
return (
<div>Diary 페이지</div>
);
};
export default Diary;
const Edit = () => {
return (
<div>Edit 페이지</div>
);
};
export default Edit;
react-router-dom이 제공하는 Routes와 Route 컴포넌트를 이용한다.
import { Routes, Route } from "react-router-dom";
import "./App.css";
import Home from "./pages/Home";
import New from "./pages/New";
import Diary from "./pages/Diary";
import Edit from "./pages/Edit";
function App() {
return (
<div className="App">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/new" element={<New />} />
<Route path="/diary" element={<Diary />} />
<Route path="/edit" element={<Edit />} />
</Routes>
</div>
);
}
export default App;
Routes
자식인 Route 컴포넌트에서 설정한 경로와 요청 URL을 비교한다. 그리고 정확히 일치하는 컴포넌트를 element 속성에 전달해 렌더링한다. 따라서 설정하지 않은 경로로 접근하면 Routes는 아무것도 페이지에 렌더링하지 않으며, 콘솔에 잘못된 경로로 접근했다고 경고 메시지를 출력한다.
경로로 분리된 페이지 간의 이동 방법을 알아보자.
HTML에서는 <a> 태그를 이용해 페이지를 이동했다면, 리액트 라우터로 페이지 라우팅을 구현하는 앱에서는 Link 컴포넌트를 이용한다. to 속성에 경로를 지정해 페이지를 이동한다.
💡 <Link to=’이동할 경로’>링크 이름</Link>
클라이언트 사이드 렌더링은 페이지를 서버에 요청하지 않고 브라우저가 직접 이동시키므로 Link 컴포넌트를 사용한다.
⇒ 브라우저에서 컴포넌트만 교체하는 식으로 렌더링하므로 이동 속도가 매우 빠르다.
import { Routes, Route, Link } from "react-router-dom";
(...)
function App() {
return (
<div className="App">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/new" element={<New />} />
<Route path="/diary" element={<Diary />} />
<Route path="/edit" element={<Edit />} />
</Routes>
<div>
<Link to={"/"}>Home</Link>
<Link to={"/new"}>New</Link>
<Link to={"/diary"}>Diary</Link>
<Link to={"/edit"}>Edit</Link>
</div>
</div>
);
}
export default App;
동적경로란 특정 아이템을 나타내는 id처럼 값이 변하는 요소를 URL에 포함하는 경우를 말한다.
1. URL 파라미터
URL에 유동적인 값을 넣는 방법으로, 보통 유동적인 값은 중괄호로 표기한다.
주로 id나이름을 이용해 특정 데이터를 조회할 때 사용한다.
https://localhost:3000/diary/{id}
2. 쿼리 스트링
물음표(?) 뒤에 key=value 문법으로 URL에 유동적인 값을 포함하는 방법이다. 만약 유동적인 값을 두 개 이상 포함해야 한다면 &로 구분한다.
보통 게시물 리스트에서 사용자가 정렬 조건을 선택하거나 현재 조회하는 게시판의 페이지를 표현할 때 사용한다.
https://localhost:3000?sort=latest
URL 파라미터로 경로 설정하기
Diary페이지는 특정 id를 가진 일기를 상세 조회할 때 사용한다.
동적 경로가 포함된 페이지를 라우팅하려면, Route 컴포넌트에서 URL 파라미터 방식으로 전달해야 한다.
<Route path="/diary/:id" element={<Diary />} />
URL 파라미터 값 불러오기
앞서 URL 파라미터로 전달한 일기 id를 불러와 Diary 페이지에서 사용하도록 작성한다. 이때 react-router-dom이 제공하는 리액트 훅 useParams를 이용한다.
import { useParams } from "react-router-dom";
const Diary = () => {
const { id } = useParams();
return (
<div>
<div>{id}번 일기</div>
<div>Diary 페이지</div>
</div>
);
};
export default Diary;
useParams은 브라우저에서 URL을 입력하면 이 경로에 포함된 URL 파라미터를 객체 형태로 반환한다.
이 객체의 파라미터 값을 사용하려면 객체를 구조 분해 할당해 필요한 값만 꺼내 쓰면 된다.
쿼리 스트링으로 값 불러오기
쿼리 스트링은 URL 경로 다음에 ?로 구분하므로 URL 파라미터처럼 페이지 라우팅을 위한 별도의 경로 설정이 필요 없다.
react-router-dom의 useSearchParams 훅을 이용하여 URL에 있는 쿼리 스트링 값을 꺼내 사용할 수 있다.
import { useSearchParams } from "react-router-dom";
const Home = () => {
const [searchParams, setSearchParams] = useSearchParams();
console.log(searchParams.get("sort"));
return (
<div>Home 페이지</div>
);
};
export default Home;
이제 리액트 라우터를 이용한 페이지 라이팅을 모두 완료했습니다.
1. 특정 URL로 폰트를 가져오는 방식을 (웹 폰트) 방식이라고 한다.
2. 데이터 전달을 목적으로 최적의 경로를 찾아 데이터를 전송하는 과정을 ( 라우팅 )이라고 한다.
3. ( 동적경로 )란 특정 아이템을 나타내는 id처럼 값이 변하는 요소를 URL에 포함하는 경우를 말한다.
4. 동적 경로의 종류는 ( URL 파라미터 ) 와 ( 쿼리 스트링 )이 있다.
5. 클라이언트 사이드 렌더링은 페이지를 서버에 요청하지 않고 브라우저가 직접 이동시키므로 ( Link 컴포넌트 )를 사용한다
6.
npm i react-router-dom
터미널에 위 명령어를 입력해 ( 리액트 라우터 )를 설치한다.
7. ( useParams )은 브라우저에서 URL을 입력하면 이 경로에 포함된 URL 파라미터를 객체 형태로 반환한다.
1. 리액트 라우터를 사용하여 홈 페이지와 소개 페이지를 만드는 React 앱을 만들어보세요.
2. 홈 페이지에서는 "홈 페이지"라는 간단한 텍스트를 표시하고, 소개 페이지에서는 "소개 페이지"라는 간단한 텍스트를 표시하세요.
3. 네비게이션 바를 추가하여 홈과 소개 페이지로 이동할 수 있는 링크를 제공하세요.
4. / 경로로 접근하면 홈 페이지를, /about 경로로 접근하면 소개 페이지를 보여주도록 설정하세요.
정답:
// App.js
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
const Home = () => <h2>홈 페이지</h2>;
const About = () => <h2>소개 페이지</h2>;
const App = () => {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">홈</Link>
</li>
<li>
<Link to="/about">소개</Link>
</li>
</ul>
</nav>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
</div>
</Router>
);
};
export default App;
출처 : 이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023)
Corner React.js 1
Editor: dalpaeng4
[React.js 1] 16. 리덕스 라이브러리 이해하기, 17. 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 (0) | 2024.01.19 |
---|---|
[React.js 1] 9장. 컴포넌트 트리에 데이터 공급하기 (1) | 2024.01.05 |
[React.js 1] 7-8장. useReducer와 상태관리 & 최적화 (0) | 2023.12.29 |
[React.js 1] p2. 할 일 관리 앱 만들기(2) (1) | 2023.12.22 |
[React.js 1] p2. 할 일 관리 앱 만들기 (0) | 2023.12.01 |