Intro
- 이번 장에서는 아래에 게시된 과일 데이터 베이스를 만들어 보겠습니다.
- firebase를 이용해서 서버데이터를 저장하고 react로 가져와서 사용하는 작업을 하겠습니다.
- 저는 CSS 파일은 생성해 놓았지만, CSS 코드는 적용하지 않았습니다. 기호에 맞게 CSS를 적용하시면 좋습니다.
- 과일 정보 입력 -> DB추가
- 과일 정보 사이트
본론
1. 컴포넌트 레이아웃
2. 파일구조
📄 App.js
- 먼저 react-router-dom 모듈을 설치합니다.
npm install react-router-dom
npm install firebase
- firebase 데이터 조회, 수정하는 함수를 만듭니다.
- 라우터를 설정하기 위해서 컴포넌트를 가져온뒤 아래와 같이 라우터를 설정해 줍니다.
import React, { useState, useEffect } from 'react'
import { db } from './firebase'
import { collection, doc, getDocs, setDoc } from 'firebase/firestore'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import Root from './components/Root'
import Home from './components/Home'
import Error from './components/Error'
import Products from './components/Products'
import Product from './components/Product'
const App = () => {
const fruitCollection = collection(db, 'fruits')
const [fruits, setFruits] = useState([])
useEffect(() => {
async function getFruits() {
const data = await getDocs(fruitCollection)
setFruits(data.docs)
}
getFruits()
}, [])
async function makeFruit(fruit) {
await setDoc(doc(fruitCollection), {
name: fruit.name,
season: fruit.season,
color: fruit.color,
taste: fruit.taste,
count: fruit.count,
price: fruit.price,
})
}
const router = createBrowserRouter([
{
path: '/',
element: <Root />,
errorElement: <Error />,
children: [
{
index: true,
element: <Home makeFruit={makeFruit} />,
},
{
path: '/products',
element: <Products fruits={fruits} />,
},
{ path: '/products/:productId', element: <Product /> },
],
},
])
return <RouterProvider router={router} />
}
export default App
📄 firebase.js
- firebase 설정방법이나 react로 데이터를 가져오는 방법은 제 블로그 게시물을 확인해 주세요!
import { initializeApp } from 'firebase/app'
import { getFirestore } from 'firebase/firestore'
const firebaseConfig = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
appId: process.env.REACT_APP_FIREBASE_WEB_APP_ID,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
}
const app = initializeApp(firebaseConfig)
export const db = getFirestore(app)
📄 src / Root / index.js
- react-router-dom 에서 Outlet 컴포넌트를 가져와 사용합니다.
- Outlet은 자식 라우트 요소들을 랜더하기 위한 부모 라우트 요소 입니다.
import React from 'react'
import { Outlet } from 'react-router-dom'
import Nav from '../Nav'
const Root = () => {
return (
<div>
<Nav />
<Outlet />
</div>
)
}
export default Root
📄 src / Nav / index.js
import React from 'react'
import { Link } from 'react-router-dom'
const Nav = () => {
return (
<div>
<Link to="/" style={{ marginRight: '1rem' }}>
Home
</Link>
<Link to="/products">Products</Link>
</div>
)
}
export default Nav
📄 src / Home / index.js
import React from 'react'
import Form from '../Form'
const Home = ({ makeFruit }) => {
return (
<div>
<h1>🍓과일 데이터베이스🍆</h1>
<Form makeFruit={makeFruit} />
</div>
)
}
export default Home
- App 컴포넌트에서 firebase에 데이터를 수정할 수 있는 함수(makeFruit)를 받아옵니다.
- input에 값을 채우고 버튼을 입력하면 firebase에 데이터가 추가됩니다.
import React, { useState } from 'react'
const Form = ({ makeFruit }) => {
const [input, setInput] = useState({})
const changeHandler = e => {
setInput({ ...input, [e.target.name]: e.target.value })
}
const submitHandler = () => {
makeFruit(input)
}
return (
<div>
<div>
<label>이름</label>
<input type={'text'} name="name" onChange={changeHandler}></input>
</div>
<div>
<label>계절</label>
<input type={'text'} name="season" onChange={changeHandler}></input>
</div>
<div>
<label>색상</label>
<input type={'text'} name="color" onChange={changeHandler}></input>
</div>
<div>
<label>맛</label>
<input type={'text'} name="taste" onChange={changeHandler}></input>
</div>
<div>
<label>수량</label>
<input type={'number'} name="count" onChange={changeHandler}></input>
</div>
<div>
<label>가격</label>
<input type={'number'} name="price" onChange={changeHandler}></input>
</div>
<div>
<button onClick={submitHandler}>추가</button>
</div>
</div>
)
}
export default Form
📄 src / Products / index.js
- firebase에서 데이터를 전체조회하는 함수(getFruits1)를 생성합니다.
- 전체 조회한 데이터를 data에 저장합니다.
- 전체 데이터를 map() 함수를 돌리고 Item 컴포넌트에 과일 하나씩 props로 내려줍니다.
- 그리고 fruit의 id 값도 props로 내려줍니다.
- Products 컴포넌트가 랜더링되면 과일 데이터 전체가 화면에 보이게 됩니다.
import React, { useState, useEffect } from 'react'
import Item from '../Item'
import { db } from '../../../firebase'
import { getDocs, collection } from 'firebase/firestore'
const Products = () => {
const [data, setData] = useState([])
const fruitCollection = collection(db, 'fruits')
async function getFruits1() {
const data = await getDocs(fruitCollection)
setData(data.docs)
}
useEffect(() => {
getFruits1()
}, [data.length])
return (
<div>
<h1>전체 상품 리스트</h1>
<table>
<thead>
<tr>
<th>상품명</th>
<th>수확시기</th>
<th>색상</th>
<th>당도</th>
<th>수량</th>
<th>가격</th>
<th>상세정보</th>
</tr>
</thead>
<tbody>
{data.map((fruit, idx) => {
return <Item key={idx} data={fruit.data()} id={fruit.id} />
})}
</tbody>
</table>
</div>
)
}
export default Products
📄 src / Item / index.js
- 상세 정보를 눌렀을 때 라우팅이 되도록 useNaigate를 react-router-dom 에서 가져옵니다.
- 상세 정보를 눌렀을 때 id 값으로 라우팅을 합니다.
- navigate로 라우팅을 할 때 props로 전달을 두 번째 인자 안에 객체({})형태로 담아줍니다.
import React from 'react'
import { useNavigate } from 'react-router-dom'
const Item = ({ data, id }) => {
const navigate = useNavigate()
return (
<tr>
<td>{data.name}</td>
<td>{data.season}</td>
<td>{data.color}</td>
<td>{data.taste}</td>
<td>{data.count}</td>
<td>{data.price}</td>
<td>
{
<button
onClick={() => {
navigate(id, { state: data })
}}
>
상세정보
</button>
}
</td>
</tr>
)
}
export default Item
📄 src / Product / index.js
- 라우팅에서 query값을 받기 위해서 react-router-dom 에서 useParams를 가져 옵니다.
- useNavigate를 이용해서 props가 넘어 왔을 때 데이터를 받기 위해서 useLocation을 react-router-dom에서 가져옵니다.
- firebas 데이터를 수정하는 함수(updateFruit)를 생성하고 수정 버튼의 클릭 이벤트에 넣어줍니다.
- firebase 데이터를 삭제하는 함수(deleteFruit)를 생성하고 삭제 버튼의 클릭 이벤트에 넣어줍니다.
- 수정이나 삭제 버튼을 눌렀을 때 '/products'로 라우팅 되도록 만듭니다.
import React, { useState, useEffect } from 'react'
import { useParams, useLocation, useNavigate } from 'react-router-dom'
import { collection, doc, getDoc, updateDoc, deleteDoc,} from 'firebase/firestore'
import { db } from '../../../firebase'
const Product = () => {
const [fruit, setFruit] = useState({})
const { productId } = useParams()
const { state } = useLocation()
const navigate = useNavigate()
const fruitCollection = collection(db, 'fruits')
useEffect(() => {
setFruit({ ...state })
}, [])
console.log(fruit)
async function updateFruit() {
await updateDoc(doc(fruitCollection, productId), {
name: fruit.name,
season: fruit.season,
color: fruit.color,
taste: fruit.taste,
count: fruit.count,
price: fruit.price,
})
navigate('/products')
}
async function deleteFruit() {
await deleteDoc(doc(fruitCollection, productId))
navigate('/products')
}
const changeHandler = e => {
setFruit(prev => ({ ...prev, [e.target.name]: e.target.value }))
}
return (
<div>
<h1>{productId} 제품 상세 페이지</h1>
<div>
<label>상품이름</label>
<input value={fruit.name} name="name" onChange={changeHandler}/>
</div>
<div>
<label>수확시기</label>
<input value={fruit.season} name="season" onChange={changeHandler}/>
</div>
<div>
<label>상품색상</label>
<input value={fruit.color} name="color" onChange={changeHandler}/>
</div>
<div>
<label>상품당도</label>
<input value={fruit.taste} name="taste" onChange={changeHandler}/>
</div>
<div>
<label>생상수량</label>
<input value={fruit.count} name="count" onChange={changeHandler}/>
</div>
<div>
<label>상품가격</label>
<input value={fruit.price} name="price" onChange={changeHandler}/>
</div>
<div>
<button onClick={updateFruit}>수정</button>
<button onClick={deleteFruit}>제거</button>
</div>
</div>
)
}
export default Product
📄 src / Error / index.js
- 컴포넌트 라우팅을 하다가 오류가 났을 때 Error 컴포넌트를 보여줍니다.
- 안에 내용은 자유롭게 작성하도록 합니다.
import React from 'react'
const Error = () => {
return <div>Error</div>
}
export default Error
나가면서
- 지금까지 firebase를 이용해서 react에 데이터를 받아오는 과정을 통해서, 간단한 과일 데이터 베이스를 만들어 보았습니다.
- 게시판이나 Todo 리스트와 로직은 비슷합니다. 생성, 조회, 수정, 삭제하는 기능을 이용하는 것이죠.
- 추가적으로 라우팅 설정을 통해서 페이지를 동적으로 사용하도록 하였습니다.
- 스타일링은 개인의 기호에 맞게 styled-component, MUI, SASS 등의 방법을 통해서 줄 수 있습니다.
- 이번 과정을 통해서 라우팅 설정하는 방법에 대해서 한번 더 배우는 시간이 되었습니다.
- 그리고 Firebase를 통해서 클라우드에 서버를 두어서 데이터를 조작하는 방법을 알게 되었습니다.
- 향후 데이터를 서버에 저장할 때 mongodb뿐만 아니라 firebase를 이용하는 것도 좋은 방법이라는 것을 깨닫게 되었습니다.
참고