본문 바로가기

WINK-(Web & App)/React.js 스터디

[2024 여름방학 React.js 스터디] 백채린 #6주차

반응형

 

1. API 연동의 기본

 

API 연동을 하기 위한 프로젝트를 새로 만들기

 

$ npx create-react-app api-integrate

 

API 호출을 위한 axios 라이브러리 설치

 

$ cd api-integrate
$ yarn add axios

 

→ axios를 사용해서 GET(데이터 조회), PUT(데이터 수정), POST(데이터 등록), DELETE(데이터 제거) 등의 메서드로 API 요청 가능

 

axios의 사용법

 

import axios from 'axios';

// get
axios.get('/users/1');

// 데이터 등록, 첫 번째 파라미터에는 API 주소, 두 번째 파라미터에는 등록하고자 하는 정보
axios.post('/users', {
  username: 'blabla',
  name: 'blabla'
});

 

useState 와 useEffect 로 데이터 로딩하기

 

useState 를 사용하여 요청 상태를 관리하고, useEffect 를 사용하여 컴포넌트가 렌더링되는 시점에 요청을 시작하는 작업을 해보자!

 

- 요청에 대한 상태를 관리 할 때에는 3가지 상태를 관리해주어야 한다.

1. 요청의 결과

2. 로딩 상태

3. 에러

 

 

src 컴포넌트에 Users.js 를 생성하고 다음 코드를 작성

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function Users() {
  const [users, setUsers] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => { // 첫번째 파라미터로 등록하는 함수에 async 사용 X, 함수 내부에서 async를 사용하는 새로운 함수 선언
    const fetchUsers = async () => {
      try {
        // 요청이 시작 할 때에는 error 와 users 를 초기화하고
        setError(null);
        setUsers(null);
        // loading 상태를 true 로 바꿉니다.
        setLoading(true);
        const response = await axios.get(
          'https://jsonplaceholder.typicode.com/users'
        );
        setUsers(response.data); // 데이터는 response.data 안에 들어있습니다.
      } catch (e) {
        setError(e);
      }
      setLoading(false);
    };

    fetchUsers();
  }, []);

  if (loading) return <div>로딩중..</div>; // 로딩 상태가 활성화 됐을 때 로딩중.. 이라는 문구 보여줌
  if (error) return <div>에러가 발생했습니다</div>;
  if (!users) return null; // user 값이 아직 없을 때에는 nulld을 보여줌
  return (
    <ul>
      {users.map(user => ( // users 배열을 렌더링하는 작업
        <li key={user.id}>
          {user.username} ({user.name})
        </li>
      ))}
    </ul>
  );
}

export default Users;

 

컴포넌트가 잘 작동하는지 확인해보기 위해 App 컴포넌트에서 User 컴포넌트를 렌더링해보자

 

App.js

import React from 'react';
import Users from './Users';

function App() {
  return <Users />;
}

export default App;

 

에러 발생 확인하기

 

에러가 발생하는 것을 확인하기 위해 주소를 이상하게 바꿔보자

 

const response = await axios.get(
  'https://jsonplaceholder.typicode.com/users/showmeerror'
);

 

버튼을 눌러서 API 재요청하기

 

아까 구현했던 fetchUsers 함수를 바깥으로 꺼내주고, 버튼을 만들어서 해당 함수를 연결해주면 된다.

 

Users.js

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function Users() {
  const [users, setUsers] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const fetchUsers = async () => {
    try {
      // 요청이 시작 할 때에는 error 와 users 를 초기화하고
      setError(null);
      setUsers(null);
      // loading 상태를 true 로 바꿉니다.
      setLoading(true);
      const response = await axios.get(
        'https://jsonplaceholder.typicode.com/users'
      );
      setUsers(response.data); // 데이터는 response.data 안에 들어있습니다.
    } catch (e) {
      setError(e);
    }
    setLoading(false);
  };

  useEffect(() => {
    fetchUsers();
  }, []);

  if (loading) return <div>로딩중..</div>;
  if (error) return <div>에러가 발생했습니다</div>;
  if (!users) return null;
  return (
    <>
      <ul>
        {users.map(user => (
          <li key={user.id}>
            {user.username} ({user.name})
          </li>
        ))}
      </ul>
      <button onClick={fetchUsers}>다시 불러오기</button>
    </>
  );
}

export default Users;

 

 

 

 

React Router v6 튜토리얼

 

 

1. 라우팅이란?

 

사용자가 요청한 URL에 다라 알맞는 페이지를 보여주는 것

 

리액트에서 라우트 시스템을 구축하기 위해 사용할 수 있는 2가지

 

1. 리액트 라우터(React Router)

- 리액트의 라우팅 관련 라이브러리들 중에서 가장 오래됐고 많이 사용되고 있음

- 컴포넌트 기반으로 라우팅 시스템 설정

 

2. Next.js

- 리액트 프로젝트의 프레임워크

- 파일 경로 기반으로 작동

- 리액트 라우터의 대안으로 많이 사용됨

 

 

 

2. 싱글 페이지 애플리케이션이란?

 

한 개의 페이지로 이루어진 애플리케이션

→ 리액트 라우터를 사용해서 여러 페이지로 구성된 프로젝트를 만들 수 있다고 했는데 왜 싱글 페이지 애플리케이션이라고 불리는지?

 

 멀티 페이지 애플리케이션의 작동

 

사용자가 다른 페이지로 이동할 때마다 새로운 html 받아오고, 페이지 로딩할 때마다 서버에서 리소스를 전달받아 브라우저 화면에 보여줌

→ 정적인 페이지들에게는 적합하지만 사용자 인터랙션이 많은 모던 웹 애플리케이션에게는 적합하지 않은 방식

 

뷰 렌더링(리액트 같은 라이브러리 사용)을 사용자의 브라우저가 담당, 사용자와의 인터랙션이 발생하면 필요한 부분만 자바스크립트를 사용하여 업데이트하는 방식을 사용

싱글 페이지 애플리케이션: html을 한 번만 받아와서 웹 애플리케이션을 실행시키고 이후에는 필요한 데이터만 받아와서 업데이트

 

라우팅 시스템은 사용자의 브라우저 주소창의 경로에 따라 알맞은 페이지를 보여줌

(브라우저의 History API를 사용하여 브라우저의 주소창의 값만 변경하고 기존에 페이지에 띄웠던 웹 애플리케이션을 그대로 유지하면서 라우팅 설정에 따라 또 다른 페이지를 보여주는 것)

 

 

 

3. 리액트 라우터 적용 및 기본 사용법

 

1. 프로젝트 생성 및 라이브러리 설치

 

리액트 프로젝트 생성

$ yarn create react-app router-tutorial

 

해당 프로젝트 디렉터리로 이동하여 리액트 라우터 라이브러리를 설치

$ cd router-tutorial
$ yarn add react-router-dom

 

 

2. 프로젝트에 라우터 적용

 

src/index.js 파일에서 react-router-dom에 내장되어 있는 BrowserRouter라는 컴포넌트를 사용하여 감싸면 된다.

* BrowserRouter

: 웹 애플리케이션에 HTML5의 History API를 사용하여 페이지를 새로 불러오지 않고도 주소를 변경하고 현재 주소의 경로에 관련된

정보를 리액트 컴포넌트에서 사용할 수 있도록 해줌

 

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

 

 

3. 페이지 컴포넌트 만들기

 

리액트 라우터를 통해 여러 페이지로 구성된 웹 애플리케이션을 만들기 위하여 각 페이지에서 사용할 컴포넌트를 만들 차례

 

사용자가 웹 사이트에 들어오게 됐을 때 가장 먼저 보여지게 될 Home 페이지 컴포넌트와 웹 사이트를 소개하는 About 페이지 컴포넌트를 만들어보자!

 

src 디렉터리에 pages 경로를 만들고, 그 안에 다음 파일들을 생성

 

src/pages/Home.js

const Home = () => {
  return (
    <div>
      <h1>홈</h1>
      <p>가장 먼저 보여지는 페이지입니다.</p>
    </div>
  );
};

export default Home;

 

src/pages/About.js

const About = () => {
  return (
    <div>
      <h1>소개</h1>
      <p>리액트 라우터를 사용해 보는 프로젝트입니다.</p>
    </div>
  );
};

export default About;

 

 

 

4. Route 컴포넌트로 특정 경로에 원하는 컴포넌트 보여주기

 

사용자의 브라우저 주소 경로에 따라 원하는 컴포넌트를 보여주기 위해서 Route 라는 컴포넌트를 통해 라우트 설정을 해주어야 한다.

 

Route  컴포넌트의 사용

<Route path="주소규칙" element={보여 줄 컴포넌트 JSX} />

 

→ Route 컴포넌트는 Routes 컴포넌트 내부에서 사용되어야 함

 

App 컴포넌트를 Route 컴포넌트를 사용하여 라우트 설정을 해보자!

 

src/App.js

import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
    </Routes>
  );
};

export default App;

 

 

5. Link 컴포넌트를 사용하여 다른 페이지로 이동하는 링크 보여주기

 

Link 컴포넌트를 사용하여 다른 페이지로 이동하는 링크를 보여주는 방법을 알아보자!

 

* Link 컴포넌트에는 페이지를 새로 불러오는 것을 막고 History API를 통해 브라우저 주소의 경로만 바꾸는 기능이 내장되어 있다.

 

Link  컴포넌트의 사용

<Link to="경로">링크 이름</Link>

 

Home 페이지에서 About 페이지로 이동할 수 있도록 Link 컴포넌트를 Home 페이지 컴포넌트에서 사용해보자!

src/pages/Home.js

import { Link } from 'react-router-dom';

const Home = () => {
  return (
    <div>
      <h1>홈</h1>
      <p>가장 먼저 보여지는 페이지입니다.</p>
      <Link to="/about">소개</Link>
    </div>
  );
};

export default Home;

 

 

 

4. URL 파라미터와 쿼리스트링

 

페이지 주소를 정의할 때 유동적인 값을 사용할 경우

ex) /profile/velopert(URL 파라미터), /articles?**page=1&keyword=react(쿼리스트링)

* URL 파라미터: 주소의 경로에 유동적인 값을 넣는 형태, ID 또는 이름을 사용하여 특정 데이터를 조회할 때 사용

* 쿼리 스트링: 주소 뒷부분에 ? 문자열 이후에 key=value 로 값을 정의하며 & 로 구분을 하는 형태, 데이터 조회에 필요한 옵션을 전달할 때 사용

1. URL 파라미터

 

URL 파라미터를 사용하는 방법을 알아보자!

 

src/pages/Profile.js (Profile 컴포넌트를 pages 경로에 작성)

import { useParams } from 'react-router-dom';

const data = {
  velopert: {
    name: '김민준',
    description: '리액트를 좋아하는 개발자',
  },
  gildong: {
    name: '홍길동',
    description: '고전 소설 홍길동전의 주인공',
  },
};

const Profile = () => {
  const params = useParams();
  const profile = data[params.username];

  return (
    <div>
      <h1>사용자 프로필</h1>
      {profile ? (
        <div>
          <h2>{profile.name}</h2>
          <p>{profile.description}</p>
        </div>
      ) : (
        <p>존재하지 않는 프로필입니다.</p>
      )}
    </div>
  );
};

export default Profile;

 

- URL 파라미터는 useParams 라는 Hook을 사용하여 객체 형태로 조회

- URL 파라미터의 이름은 라우트 설정을 할 때 Route 컴포넌트의 path props를 통하여 설정

 

src/App.js (App컴포넌트 파일을 열어서 새로운 라우트를 다음과 같이 설정)

import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';
import Profile from './pages/Profile';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="/profiles/:username" element={<Profile />} />
    </Routes>
  );
};

export default App;

 

src/pages/Home.js

import { Link } from 'react-router-dom';

const Home = () => {
  return (
    <div>
      <h1>홈</h1>
      <p>가장 먼저 보여지는 페이지입니다.</p>
      <ul>
        <li>
          <Link to="/about">소개</Link>
        </li>
        <li>
          <Link to="/profiles/velopert">velopert의 프로필</Link>
        </li>
        <li>
          <Link to="/profiles/gildong">gildong의 프로필</Link>
        </li>
        <li>
          <Link to="/profiles/void">존재하지 않는 프로필</Link>
        </li>
      </ul>
    </div>
  );
};

export default Home;

 

 

2. 쿼리스트링

 

라우트에서 쿼리스트링을 사용하는 방법을 알아보자!

 

쿼리스트링을 사용할 때는 URL 파라미터와 달리 Route 컴포넌트를 사용할 때 별도로 설정해야되는 것은 없다.

 

쿼리스트링을 화면에 띄워보는 작업을 해보자!

 

src/pages/About.js (About페이지 컴포넌트를 다음과 같이 수정)

import { useLocation } from 'react-router-dom';

const About = () => {
  const location = useLocation();

  return (
    <div>
      <h1>소개</h1>
      <p>리액트 라우터를 사용해 보는 프로젝트입니다.</p>
      <p>쿼리스트링: {location.search}</p>
    </div>
  );
};

export default About;

 

* useLocation라는 Hook

: location 객체를 반환하며 이 객체는 현재 사용자가 보고있는 페이지의 정보를 지니고 있음

pathname 현재 주소의 경로 (쿼리스트링 제외)
search 맨 앞의 ? 문자 포함한 쿼리스트링 값
hash 주소의 # 문자열 뒤의 값
state 페이지로 이동할때 임의로 넣을 수 있는 상태 값
key location 객체의 고유 값,
초기에는
 default 이며 페이지가 변경될때마다 고유의 값이 생성됨

 

 

 

5. 중첩된 라우트

 

 

게시글 목록 페이지에서 게시글을 열었을 때, 게시글의 하단에 목록을 보여줘야한다면? 

중첩된 라우트를 사용하여 기존보다 좀 더 나은 방식으로 구현

 

src/App.js

import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Home from './pages/Home';
import Profile from './pages/Profile';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="/profiles/:username" element={<Profile />} />
      <Route path="/articles" element={<Articles />}>
        <Route path=":id" element={<Article />} />
      </Route>
    </Routes>
  );
};

export default App;

 

Articles 컴포넌트에서 리액트 라우터에서 제공하는 Outlet 이라는 컴포넌트를 사용해주어야 한다.

* Outlet: Route  children 으로 들어가는 JSX 엘리먼트를 보여주는 역할을 하는 컴포넌트

// Outlet 컴포넌트를 통해 보여지는 내용
<Route path=":id" element={<Article />} />

 

src/pages/Articles.js

import { Link, Outlet } from 'react-router-dom';

const Articles = () => {
  return (
    <div>
      <Outlet />
      <ul>
        <li>
          <Link to="/articles/1">게시글 1</Link>
        </li>
        <li>
          <Link to="/articles/2">게시글 2</Link>
        </li>
        <li>
          <Link to="/articles/3">게시글 3</Link>
        </li>
      </ul>
    </div>
  );
};

export default Articles;

 

→ Outlet 컴포넌트가 사용된 자리에 중첩된 라우트가 보여지게 된다.

 

 

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

 

중첩된 라우트와 Outlet 은 페이지끼리 공통적으로 보여줘야 하는 레이아웃이 있을때도 유용하게 사용

 

중첩된 라우트를 통해 공통 레이아웃 컴포넌트를 사용해보자!

 

src/Layout.js (공통 레이아웃을 위한Layout컴포넌트를 src 디렉터리에 만들기)

import { Outlet } from 'react-router-dom';

const Layout = () => {
  return (
    <div>
      <header style={{ background: 'lightgray', padding: 16, fontSize: 24 }}>
        Header
      </header>
      <main>
        <Outlet />
      </main>
    </div>
  );
};

export default Layout;

 

src/App.js (App 컴포넌트 수정하기)

import { Route, Routes } from 'react-router-dom';
import Layout from './Layout';
import About from './pages/About';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Home from './pages/Home';
import Profile from './pages/Profile';

const App = () => {
  return (
    <Routes>
      <Route element={<Layout />}>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/profiles/:username" element={<Profile />} />
      </Route>
      <Route path="/articles" element={<Articles />}>
        <Route path=":id" element={<Article />} />
      </Route>
    </Routes>
  );
};

export default App;

 

 

2. index props

 

Route 컴포넌트에는 index 라는 props가 있고, 이 props 는 path="/"와 동일한 의미를 가진다.

 

src/App.js (Home컴포넌트가 사용된Route컴포넌트를 다음과 같이 변경)

import { Route, Routes } from 'react-router-dom';
import Layout from './Layout';
import About from './pages/About';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Home from './pages/Home';
import Profile from './pages/Profile';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/profiles/:username" element={<Profile />} />
      </Route>
      <Route path="/articles" element={<Articles />}>
        <Route path=":id" element={<Article />} />
      </Route>
    </Routes>
  );
};

export default App;

 

→ index prop은 상위 라우트의 경로와 일치하지만, 그 이후에 경로가 주어지지 않았을때 보여지는 라우트를 설정할때 사용

 

 

 

6.  리액트 라우터 부가기능

 

리액트 라우터에서 제공하는 웹 애플리케이션에서 라우팅에 관련된 작업을 할 때 사용할 수 있는 유용한 API

 

1. useNavigate

 

Link 컴포넌트를 사용하지 않고 다른 페이지로 이동을 해야 하는 상황에 사용하는 Hook

 

src/Layout.js

import { Outlet, useNavigate } from 'react-router-dom';

const Layout = () => {
  const navigate = useNavigate();

  const goBack = () => {
    // 이전 페이지로 이동
    navigate(-1);
  };

  const goArticles = () => {
    // articles 경로로 이동
    navigate('/articles');
  };

  return (
    <div>
      <header style={{ background: 'lightgray', padding: 16, fontSize: 24 }}>
        <button onClick={goBack}>뒤로가기</button>
        <button onClick={goArticles}>게시글 목록</button>
      </header>
      <main>
        <Outlet />
      </main>
    </div>
  );
};

export default Layout;

 

 

2. NavLink

 

링크에서 사용하는 경로가 현재 라우트의 경로와 일치하는 경우 특정 스타일 또는 CSS 클래스를 적용하는 컴포넌트

 

style 또는 className을 설정할 때 { isActive: boolean } 을 파라미터로 전달받는 함수 타입의 값을 전달

 

ex)

<NavLink 
  style={({isActive}) => isActive ? activeStyle : undefined} 
/>
<NavLink 
  className={({isActive}) => isActive ? 'active' : undefined} 
/>

 

 

3. NotFound 페이지 마들기

 

사전에 정의되지 않는 경로에 사용자가 진입했을 때 보여주는 페이지

 

src/pages/NotFound.js

const NotFound = () => {
  return (
    <div
      style={{
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: 64,
        position: 'absolute',
        width: '100%',
        height: '100%',
      }}
    >
      404
    </div>
  );
};

export default NotFound;

 

src/App.js

import { Route, Routes } from 'react-router-dom';
import Layout from './Layout';
import About from './pages/About';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Home from './pages/Home';
import NotFound from './pages/NotFound';
import Profile from './pages/Profile';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/profiles/:username" element={<Profile />} />
      </Route>
      <Route path="/articles" element={<Articles />}>
        <Route path=":id" element={<Article />} />
      </Route>
      <Route path="*" element={<NotFound />} />
      // * : wildcard 문자, 아무 텍스트나 매칭한다는 뜻
    </Routes>
  );
};

export default App;

 

 

4. Navigate zjavhsjsxm 

 

컴포넌트를 화면에 보여주는 순간 다른 페이지로 이동을 하고 싶을 때 사용하는 컴포넌트

 

src/pages/Login.js

const Login = () => {
  return <div>로그인 페이지</div>;
};

export default Login;

src/pages/MyPage.js

import { Navigate } from 'react-router-dom';

const MyPage = () => {
  const isLoggedIn = false;

  if (!isLoggedIn) {
    return <Navigate to="/login" replace={true} />;
  }

  return <div>마이 페이지</div>;
};

export default MyPage;

 

→ isLoggedIn 값이 false 이면 Navigate 컴포넌트를 통해 /login 경로로 이동

 

src/App.js

import { Route, Routes } from 'react-router-dom';
import Layout from './Layout';
import About from './pages/About';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Home from './pages/Home';
import Login from './pages/Login';
import MyPage from './pages/MyPage';
import NotFound from './pages/NotFound';
import Profile from './pages/Profile';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/profiles/:username" element={<Profile />} />
      </Route>
      <Route path="/articles" element={<Articles />}>
        <Route path=":id" element={<Article />} />
      </Route>
      <Route path="/login" element={<Login />} />
      <Route path="/mypage" element={<MyPage />} />
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
};

export default App;

 

 /mypage 경로로 이동해보면 페이지가 로딩되는 순간 바로 Login 페이지로 이동된다.

반응형