[React : mini-project] CRUD (1) - Create

문지은·2023년 7월 29일
0

React

목록 보기
16/24
post-thumbnail

Title과 Content를 작성할 수 있는 간단한 폼을 만들고 DB에 저장해보자.

사용하는 패키지

  • bootstrap 5.3.0
  • react-router-dom v6
  • axios
  • json-server

포스트 생성 폼 만들기

Input State 바인딩

  • titlesetTitle이라는 두 가지 변수를 useState 훅을 사용하여 선언
    • title은 현재 입력된 글의 제목을 저장하는 상태
    • setTitletitle 상태를 업데이트하는 함수
  • 글의 제목을 입력받는 input 요소를 생성
  • value 속성을 통해 title 상태를 입력값으로 설정
  • onChange 이벤트 핸들러를 통해 입력 값이 변경될 때마다 setTitle 함수를 호출하여 title 상태를 업데이트

src/App.js

import React from 'react'
import { useState } from 'react';

function App() {
  const [title, setTitle] = useState('');
  return (
    <div className='container'>
      <div className='mb-3'>
        <label className='form-label'>Title</label>
        <input 
          className='form-control'
          value={title}
          onChange={(event) => {
            setTitle(event.target.value)
          }}
        />
      </div>
      <button className='btn btn-primary'>Post</button>
    </div>
  )
}

export default App;

Textarea State 바인딩

src/App.js

import React from 'react'
import { useState } from 'react';

function App() {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');

  return (
    <div className='container'>
      <div className='mb-3'>
        <label className='form-label'>Title</label>
        <input 
          className='form-control'
          value={title}
          onChange={(event) => {
            setTitle(event.target.value)
          }}
        />
      </div>
      <div className='mb-3'>
        <label className='form-label'>Body</label>
        <textarea 
          className='form-control'
          value={body}
          onChange={(event) => {
            setBody(event.target.value)
          }}
        />
      </div>
      <button className='btn btn-primary'>Post</button>
    </div>
  )
}

export default App;
  • 버튼 클릭시 콘솔 창에 title, body 내용 출력하기

src/App.js

import React from 'react'
import { useState } from 'react';

function App() {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');

  const onSubmit = () => {
    console.log(title, body)
  }

  return (
    <div className='container'>
      <div className='mb-3'>
        <label className='form-label'>Title</label>
        <input 
          className='form-control'
          value={title}
          onChange={(event) => {
            setTitle(event.target.value)
          }}
        />
      </div>
      <div className='mb-3'>
        <label className='form-label'>Body</label>
        <textarea 
          className='form-control'
          value={body}
          onChange={(event) => {
            setBody(event.target.value)
          }}
          rows={20}
        />
      </div>
      <button 
          className='btn btn-primary'
          onClick={onSubmit}
          >Post</button>
    </div>
  )
}

export default App;

DB에 데이터 저장하기

json-server 설치

npm: json-server

npm install -g json-server
  • 설치 확인

  • 프로젝트 루트 폴더에 db.json 파일 생성하기

db.json

json-server --watch db.json --port 3001
  • 실행 확인

DB에 데이터 저장하기

  • axios 설치
$ npm install axios

src/App.js

import React from 'react'
import { useState } from 'react';

import axios from 'axios';

function App() {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');

  const onSubmit = () => {
    axios.post('http://localhost:3001/posts', {
      title: title,
      body: body
    })
  }

  return (
    <div className='container'>
      <div className='mb-3'>
        <label className='form-label'>Title</label>
        <input 
          className='form-control'
          value={title}
          onChange={(event) => {
            setTitle(event.target.value)
          }}
        />
      </div>
      <div className='mb-3'>
        <label className='form-label'>Body</label>
        <textarea 
          className='form-control'
          value={body}
          onChange={(event) => {
            setBody(event.target.value)
          }}
          rows={20}
        />
      </div>
      <button 
          className='btn btn-primary'
          onClick={onSubmit}
          >Post</button>
    </div>
  )
}

export default App;
  • title과 body를 작성한 후 Post 버튼 누른 후 개발자 도구의 Network를 확인하면 post 요청이 실행되었음을 확인할 수 있다.

  • Payload 탭에서 전달한 데이터도 확인할 수 있음

  • db.json 파일도 수정되었다.

참고할만한 링크

[React] axios란? (feat. Fetch API)

DB 실행 명령어 스크립트에 넣기

  • 아래 명령어가 헷갈릴 수 있으니 스크립트에 넣어보자.
json-server --watch db.json --port 3001
  • package.json 파일의 scripts 항목에 db라는 이름으로 명령어를 넣는다.

  • 이제 아래 명령어로 서버를 실행할 수 있다.
npm run db

React Router로 페이지 추가하기

  • React Router 설치
npm install react-router-dom
  • 설치 확인

컴포넌트 만들기

생성 Form

  • 위에서 작성했던 생성 Form을 BlogForm이라는 컴포넌트로 따로 빼서 작성

src/components/BlogForm.jsx

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

function BlogForm() {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');

  const onSubmit = () => {
      axios.post('http://localhost:3001/posts', {
      title: title,
      body: body
      })
  }
  return (
    <div>
        <h1>Create a blog post</h1>
        <div className='mb-3'>
            <label className='form-label'>Title</label>
            <input 
            className='form-control'
            value={title}
            onChange={(event) => {
                setTitle(event.target.value)
            }}
            />
        </div>
        <div className='mb-3'>
            <label className='form-label'>Body</label>
            <textarea 
            className='form-control'
            value={body}
            onChange={(event) => {
                setBody(event.target.value)
            }}
            rows={20}
            />
        </div>
        <button 
            className='btn btn-primary'
            onClick={onSubmit}
            >Post</button>
    </div>
  )
}

export default BlogForm
  • 부트스트랩을 이용하여 NavBar 컴포넌트 만들기

src/components/Navbar.jsx

import React from 'react'

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

function NavBar() {
  return (
    <nav className="navbar navbar-dark bg-dark">
        <div className="container">
          <Link className="navbar-brand" to="/">Home</Link>
            <ul className="navbar-nav">
              <li className="nav-item">
              </li>
            </ul>
        </div>
      </nav>
  )
}

export default NavBar

페이지 컴포넌트 만들기

  • src 디렉터리에 pages 경로를 만들고 그 안에 파일 생성하기
    • HomPage
    • CreatePage
    • EditPage
    • ListPage
  • CreatePage에는 위에서 만든 BlogForm 컴포넌트 넣기

src/pages/CreatePage.jsx

import React from 'react'
import BlogForm from '../components/BlogForm'

function CreatePage() {
  return (
    <div>
        <BlogForm />
    </div>
  )
}

export default CreatePage
  • App.js 수정

src/App.js

import React from 'react'

import {
  BrowserRouter as Router,
  Routes,
  Route,
  Link
} from 'react-router-dom';

// components
import NavBar from './components/NavBar';

// pages
import HomePage from './pages/HomePage';
import CreatePage from './pages/CreatePage';
import EditPage from './pages/EditPage';
import ListPage from './pages/ListPage';

function App() {

  return (
    <Router>
      <NavBar/>

      <div className='container'>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/blogs" element={<ListPage />} />
          <Route path="/blogs/create" element={<CreatePage />} />
          <Route path="/blogs/edit" element={<EditPage />} />
        </Routes>
      </div>
    </Router>
  )
}

export default App;

코드 리팩토링

  • routes.js 파일을 만들어 라우트 정보들을 담은 파일 따로 만들고,
  • App.js 에서는 map 함수를 이용하여 routes 파일의 route 들을 하나씩 불러와서 Route 컴포넌트 출력

src/routes.js

import HomePage from './pages/HomePage';
import CreatePage from './pages/CreatePage';
import EditPage from './pages/EditPage';
import ListPage from './pages/ListPage';

const routes = [
  {
    path:'/',
    element: <HomePage />
  },
  {
    path:'/blogs',
    element: <ListPage />
  },
  {
    path:'/blogs/create',
    element: <CreatePage />
  },
  {
    path:'/blogs/edit',
    element: <EditPage />
  },
]

export default routes;

src/App.js

import React from 'react'

import {
  BrowserRouter as Router,
  Routes,
  Route,
  Link
} from 'react-router-dom';

// components
import NavBar from './components/NavBar';

// pages
import HomePage from './pages/HomePage';
import CreatePage from './pages/CreatePage';
import EditPage from './pages/EditPage';
import ListPage from './pages/ListPage';

// routes
import routes from './routes';


function App() {

  return (
    <Router>
      <NavBar/>

      <div className='container'>
        <Routes>
          {routes.map((route) => {
            return <Route path={route.path} element={route.element} />
          })}
        </Routes>
      </div>
    </Router>
  )
}

export default App;
  • map 함수를 사용할 때 key를 작성하지 않으면 개발자도구에 오류 메시지가 출력됨
  • key로 route의 path를 지정해줌

src/App.js

import React from 'react'

import {
  BrowserRouter as Router,
  Routes,
  Route,
  Link
} from 'react-router-dom';

// components
import NavBar from './components/NavBar';

// pages
import HomePage from './pages/HomePage';
import CreatePage from './pages/CreatePage';
import EditPage from './pages/EditPage';
import ListPage from './pages/ListPage';

// routes
import routes from './routes';


function App() {

  return (
    <Router>
      <NavBar/>

      <div className='container'>
        <Routes>
          {routes.map((route) => {
            return <Route key={route.path} path={route.path} element={route.element} />
          })}
        </Routes>
      </div>
    </Router>
  )
}

export default App;
  • 오류메시지 없어진 것 확인
  • Navbar에 ListPage, CreatePage를 NavLink 컴포넌트를 이용해서 추가하기

src/components/NavBar.jsx

import React from 'react'

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

function NavBar() {
  return (
    <nav className="navbar navbar-dark bg-dark">
        <div className="container">
          <Link className="navbar-brand" to="/">Home</Link>
            <ul className="navbar-nav">
              <li className="nav-item">
              <NavLink
                  className={({ isActive }) => "nav-link" + (isActive ? " activated" : "")}
                  aria-current="page"
                  to="/blogs">
                  Blogs</NavLink>
              </li>
              <li className="nav-item">
                <NavLink
                  className={({ isActive }) => "nav-link" + (isActive ? " activated" : "")}
                  aria-current="page"
                  to="/blogs/create">
                  Create</NavLink>
              </li>
            </ul>
        </div>
      </nav>
  )
}

export default NavBar
  • 실행 결과
  • 이후 진행될 실습에서는 Create NavLink를 지우고 진행한다.
profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글