상세 컨텐츠

본문 제목

[React.js 1] project3. [감정 일기장] 만들기

23-24/React.js 1

by ssxb 2024. 1. 12. 10:00

본문

728x90

프로젝트 준비하기

요구사항 분석하기 → 리액트 앱 만들기 → 폰트 설정하기 → 이미지 준비하기

1. 요구사항 분석하기

 

Home 페이지

사용자가 앱에 접속하면 처음으로 만나는 페이지다. 인덱스 페이지라고도 부르며 경로는 ‘/’

입니다. 대문 역할을 합니다.

  • 헤더: 월 단위 일기 조회, 좌우버튼으로 날짜 이동
  • 일기 리스트: 일기 정렬, <새 일기 쓰기> 버튼으로 New 페이지로 이동해 일기 추가

New 페이지

새로운 일기를 추가하는 페이지이며 경로는 ‘/new’입니다.

  • 헤더: <뒤로 가기> 버튼
  • 날짜 입력, 감정 이미지 선태그 일기 작성 폼
  • 하단: <취소하기>, <작성 완료> 버튼, <작성 완료>버튼은 Home 페이지로 돌아감

Diary 페이지

작성한 일기를 상세히 조회하는 페이지며 경로는 ‘/diary/id’이다. Home에서 일기 리스트를 조회한 다음, 특정 일기를 클릭하면 Diary 페이지로 이동한다. 이런 페이지를 상세 또는 콘텐츠 페이지라고 한다.

  • 헤더: <뒤로 가기>, <수정하기> 버튼, <수정하기> 버튼은 Edit페이지로 이동함
  • 본문: 선택한 일기의 감정 상태와 내용

Edit 페이지

일기를 수정하는 페이지이며 경로는 ‘/edit/id’입니다. Home 또는 Diary페이지에서 <수정하기> 버튼을 클릭하면 Edit 페이지로 이동합니다. New 페이지와 유사하며 <삭제하기> 버튼이 추가되었습니다.

 

 

2. 리액트 앱 만들기

[감정 일기장] 프로젝트를 위한 새 리액트 앱을 만들고 불필요한 파일과 코드를 삭제한다.

  1. 폴더 만들기 → CORNERSTUDY/prj3
  2. npx create-react-app . 
  3. 불필요한 파일과 코드 삭제하기
    1. src/App.test.js
    2. src/logo.svg
    3. src/reportWebVitals.js
    4. setupTest.js
    5. App.js 수정
      import './App.css';
      
      function App() {
        return <div className="App"></div>;
      }
      
      export default App;
    6. index.js 수정 
    7. 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

 

 

3. 폰트 설정하기

 리액트 앱에서 사용자가 원하는 폰트를 지정할 때는 파일을 다운로드해 프로젝트에 포함하거나 웹에서 불러오는 방법이 있습니다. 이 프로젝트에서는 특정 URL로 폰트를 가져오는 ‘웹 폰트’ 방식을 이용합니다.

 

 

구글 Fonts에 접속해 폰트 가져오기

https://fonts.google.com/

해당 주소로 접속해 ‘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;
}

 

 

4. 이미지 준비하기

감정 이미지 다운로드하기

https://github.com/winterlood/one-bite-react/releases/tag/emotion

다음 링크로 접속해 프로젝트 src/img 폴더 생성,  파일명 그대로 5개의 감정 이미지를 저장한다.

 

 

이미지를 불러오는 함수 만들기

이미지 번호에 맞게 적절한 이미지를 반환하는 함수를 만드는게 필요하다.

src 폴더에서 util.js 파일을 만들고 다음과 같이 작성한다.

  • utils.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; } };
  • App.jsApp 컴포넌트에서 getEmotionImgById 함수를 호출해 모든 감정이미지가 렌더링되는지 확인한다. 
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) 서버 사이드 렌더링

 

서버 사이드 렌더링에서 페이지 라우팅은 다음과 같이 동작한다.

  1. 웹 브라우저에서 winterlood.com/blog라는 url 요청
  2. 웹 서버는 요청 url에서 경로 blog를 확인하고, blog.html을 생성해 반환
  3. 웹 브라우저는 웹 서버에서 반환된 blog.html을 보여줌

사용자가 버튼 또는 링크를 클릭해 페이지를 이동할 때는 다음과 같이 동작한다.

  1. 웹 브라우저에서 winterlood.com/books로 서비스 요청
  2. 웹 서버는 요청 url에서 경로 books를 확인하고 books.html을 생성해 반환
  3. 웹 브라우저는 웹 서버가 반환한 books.html을 보여줌, 이때 페이지가 교체되기 때문에 브라우저 새로고침이 발생함

 

서버 사이드 렌더링은 이처럼 웹 브라우저에 표시할 페이지를 웹 서버에 만들어 전달한다.

  • 장점: 검색 엔진을 최적화하며, 처음 접속할 때 속도가 빠르다.
  • 단점
    • 사용자가 페이지를 이동할 때마다 서버가 새로운 페이지를 생성해 제공하려면 많은 연산을 수행하게 되므로 수많은 요청이 이루어지는 서비스라면 서버에 부하가 걸릴 위험성이 높다.
    • 페이지를 이동할 때마다 브라우저는 서버가 제공하는 페이지를 기다려야 하기 때문에 속도가 느리다.

 

 

2) 클라이언트 사이드 렌더링

 

리액트 앱은 html 파일이 하나뿐인 단일 페이지 애플리케이션이므로 클라이언트 사이드 렌더링으로 페이지를 라우팅한다.

이는 페이지를 브라우저가 직접 만드는데 다음과 같이 동작한다.

  1. 웹 브라우저가 winterlood.com/blog로 서비스 요청
  2. 웹 서버는 요청 url의 경로를 따지지 않고 페이지의 틀 역할을 하는 index, html과 자바스크립트 애플리케이션인 리액트 앱을 함께 반환
  3. 웹 브라우저는 서버에서 제공된 index.html 페이지를 보여주고 → 자바스크립트로 이루어진 리액트 앱 실행 →리액트 앱은 현재 경로에 맞는 페이지를 보여줌
  4. 사용자가 페이지를 이동하면 웹 브라우저는 서버에서 받은 리액트 앱을 실행해 자체적으로 페이지를 교체

 

클라이언트 사이드 렌더링의 핵심은 사용자가 보는 페이지를 웹서버가 아닌 브라우저가 완성한다는 점이다.

브라우저는 처음 접속할 때만 서버에게 데이터를 요청하며, 페이지를 이동할 때는 별도의 요청을 하지 않는다.

  • 장점: 페이지를 이동할 때 브라우저에서 페이지를 직접 교체하므로 속도가 훨신 빠르다, 페이지를 빠르게 교체한다.
  • 단점: 서버가 html 파일과 자바스크립트 애플리케이션을 함께 제공하기 때문에, 처음 사이트 접속시 서버 사이드 렌더링보다 속도가 느리다.

 

리액트 라우터로 페이지 라우팅하기

 

리액트 라우터란?

리액트 라우터(React Router)는 Remix 팀에서 제작한 오픈소스 라이브러리이다. 이 페이지 라우팅 전용 라이브러리를 이용하여 필요한 기능을 손쉽게 구현할 수 있다.

 

 

리액트 라우터 설치하기

npm i react-router-dom

터미널에 위 명령어를 입력해 리액트 라우터를 설치한다.

 

프로젝트에 라우터 적용하기

  • index.js                                                                                                                                                                              리액트 라우터가 제공하는 BrowserRouter 컴포넌트로 App을 감싼다.
    import { BrowserRouter } from 'react-router-dom';
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <BrowserRouter>
    	   <App />
      </BrowserRouter>
    );

BrowserRouter에는 브라우저의 주소 변경을 감지하는 기능이 있다.

 

페이지 컴포넌트 만들기

페이지 역할을 담당할 컴포넌트를 만들어 구성한다. src/pages 폴더에 각각 만든다.

  • Home.js: 인덱스 페이지
  • New.js: 새 일기 작성 페이지
  • Diary.js: 일기 상세 조회 페이지
  • Edit.js: 일기 수정하거나 삭제 페이지
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 컴포넌트를 이용한다.

  • App.js
    • Route가 여러 Route 컴포넌트를 감싸며 현재 URL에 맞게 적절한 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 컴포넌트를 사용한다.

⇒ 브라우저에서 컴포넌트만 교체하는 식으로 렌더링하므로 이동 속도가 매우 빠르다.

 

  • App.js                                                                                                                                                                             모든 페이지로 이동할 수 있는 링크를 만든다.
    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 파라미터 방식으로 전달해야 한다.

  • App.js
    <Route path="/diary/:id" element={<Diary />} />

 

URL 파라미터 값 불러오기

 

앞서 URL 파라미터로 전달한 일기 id를 불러와 Diary 페이지에서 사용하도록 작성한다. 이때 react-router-dom이 제공하는 리액트 훅 useParams를 이용한다.

  • Diary.js
    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에 있는 쿼리 스트링 값을 꺼내 사용할 수 있다.

  • Home.js
    • useSearchParams가 반환한 첫 번째 요소에서 sort 값을 불러와 콘솔에 출력한다.
      import { useSearchParams } from "react-router-dom";
      
      const Home = () => {
          const [searchParams, setSearchParams] = useSearchParams();
          console.log(searchParams.get("sort"));
      
          return (
              <div>Home 페이지</div>
          );
      };
      
      export default Home;

이제 리액트 라우터를 이용한 페이지 라이팅을 모두 완료했습니다.

 

 


Quiz 

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

 

728x90

관련글 더보기