[React JS] 3. ํ๋ก ํธ์๋ ๊ธฐ๋ณธ๊ตฌ์กฐ ์์ฑํ๊ธฐ
1. Vite๋ฅผ ์ด์ฉํ ๋ฆฌ์กํธ ์์ฑ
- vite
npm init vite |
project name: ./
react ์ ํ
javascript ์ ํ
-> ๋ฆฌ์กํธ ์์ฑ
npm install |
-> node_module์์ package.json dependencies๋ค์ด ๋ค์ด๊ฐ
ํด๋๊ตฌ์กฐ ์์ฑ
components : ๊ณตํต ์ปดํฌ๋ํธ
hooks : ์ปค์คํ ํ ์ค
layout : header(navBar) , footer, side bar
pages : ํ์ด์ง๋ค
store : redux ์ํ๊ด๋ฆฌ
utils : ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ์ฌ์ฉ๋๋ ํจ์๋ค
ํ์ผ ์์ฑ
2. ESlint
1) ํ์ํ ๋ชจ๋ ์ค์น
npm install -D vite-plugin-eslint eslint eslint-config-react-app |
2) eslint plugin ์ ์ฉ
vite.config.js
import eslint from 'vite-plugin-eslint'
export default definedConfig({
plugins: [react(), eslint()],
})
3) eslint ์ค์ ํ์ผ ์์ฑ
src > .eslintrc ํ์ผ (์๋ ์๋น)
4) eslint rules ์์ฑ
extends์ react-app ๋ฃ์ด์ฃผ๊ธฐ
3. TailWindCSS
- HTM์์์ CSS๋ฅผ ๋ง๋ค์์๊ฒ ํด์ฃผ๋ CSS ํ๋ ์์ํฌ
- Bootstrap๊ณผ ๋น์ทํ๊ฒ m-1, flex์ ๊ฐ์ด ๋ฏธ๋ฆฌ ์ธํ ๋ Utility Class๋ฅผ ํ์ฉํ๋ ๋ฐฉ์์ผ๋ก HTML์์ ์คํ์ผ๋ง ํ ์ ์๋ค.
1) ๋น ๋ฅธ ์คํ์ผ๋ง ๊ฐ๋ฅ
2) class ํน์ id๋ช ์ ์์ฑํ๊ธฐ ์ํ ๊ณ ์ํ์ง ์์๋ ๋จ
3) ์ ํธ๋ฆฌํฐ ํด๋์ค๊ฐ ์ต์ํด์ง๋ ์๊ฐ์ด ํ์ํ์ง๋ง IntelliSense ํ๋ฌ๊ทธ์ธ์ผ๋ก ์ต์ํด์ง ์ ์์
install Tailwind CSS with Vite
npm install -D postcss autoprefixer tailwind npx tailwindcss init -p |
4. React Router Dom
- React Router Dom์ ์ฌ์ฉํ๋ฉด ์น ์ฑ์์ ๋์ ๋ผ์ฐํ ์ ๊ตฌํ ๊ฐ๋ฅ
- ๋ผ์ฐํ ์ด ์คํ ์ค์ธ ์ฑ ์ธ๋ถ์ ๊ตฌ์ฑ์์ ์ฒ๋ฆฌ๋๋ ๊ธฐ์กด ๋ผ์ฐํ ์ํคํ ์ฒ์ ๋ฌ๋ฆฌ, React Router Dom์ ์ฑ ๋ฐ ํ๋ซํผ์ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ๋ผ์ฐํ ์ฉ์ดํ๊ฒํจ
Single Page Application (SPA)
- ํ๋์ index.html ํ ํ๋ฆฟ ํ์ผ
- ํ๋์ ํ ํ๋ฆฟ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ด์ฉํด์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ์ด index.html์ ๋ฃ์ผ๋ฏ๋ก ํ์ด์ง๋ฅผ ๋ณ๊ฒฝํด์ฃผ๊ฒ๋จ
- ์ด๋ React Router Dom ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ์ปดํฌ๋ํธ๋ก ๋ผ์ฐํ /ํ์์ ํ๊ณ ๋ ๋๋งํ๋๋ฐ ๋์์ ์ฃผ๊ฒ ๋จ
์ค์น
npm install react-router-dom --save |
- src > index.js > react-router-dom์์ BrowserRouter๊ฐ์ ธ์จ ํ ๋ค์ ๋ฃจํธ ๊ตฌ์ฑ์์(App ๊ตฌ์ฑ ์์)๋ฅผ ๊ทธ ์์ ๋ฉํ
- BrowserRouter : HTML5 history api(pushState, replaceState ๋ฐ popstate ์ด๋ฒคํธ)๋ฅผ ์ฌ์ฉํ์ฌ UI๋ฅผ URL๊ณผ ๋๊ธฐํ๋ ์ํ๋ก ์ ์งํด์ค
์ฌ๋ฌ ์ปดํฌ๋ํธ ์์ฑ ๋ฐ ๋ผ์ฐํธ ์ ์ํ๊ธฐ
- Routes ์์ Route๋ค์ด ๋ค์ด์์
- ๋ง์ฝ ๊ฒฝ๋ก๋ฅผ ์คฌ์๋ path ๊ฒฝ๋ก๋ฅผ ํ๋์ฉ ์ฌ๊ธฐ์ ์ฐพ์๋ณด๊ณ element์ ์๋ ์ปดํฌ๋ํธ๋ฅผ ๋ณด์ฌ์ค
Routes
- ์ฑ์์ ์์ฑ๋ ๋ชจ๋ ๊ฐ๋ณ ๊ฒฝ๋ก์ ๋ํ ์ปจํ ์ด๋ / ์์ ์ญํ
- Route๋ก ์์ฑ๋ ์์ ์ปดํฌ๋ํธ ์ค์์ ๋งค์นญ๋๋ ์ฒซ๋ฒ์งธ Route๋ฅผ ๋ ๋๋ง
Route
- ๋จ์ผ ๊ฒฝ๋ก ๋ง๋๋๋ฐ ์ฌ์ฉ
- 1) path : ์ํ๋ ์ปดํฌ๋ํธ์ URL๊ฒฝ๋ก
- 2) element : ๊ฒฝ๋ก์ ๋ง๊ฒ ๋ ๋๋ง๋์ด์ผํ๋ ์ปดํฌ๋ํธ ์ง์
<Link/>๋ฅผ ์ด์ฉํด ๊ฒฝ๋ก ์ด๋
- aํ๊ทธ์ ์ ์ฌ
- ๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด ๊ฒฝ๋ก๋ฅผ ์ดํด๋ณด๊ณ ํด๋น ๊ฒฝ๋ก ์ด๋ฆ์ผ๋ก ๊ตฌ์ฑ๋ ์์๋ฅด ๋ ๋๋ง
์ค์ฒฉ ๋ผ์ฐํ (Nested Routes)
- ๋ณต์กํ ๋ ์ด์์ ์ฝ๋ ์ฌ์ฉ ๊ฐ๋ฅ
- ๋๋ถ๋ถ์ ๋ ์ด์์์ url ์ธ๊ทธ๋จผํธ์ ์ฐ๊ฒฐ๋๊ณ react router๋ ์ด๋ฅผ ์์ ํ ์์ฉ
- ex) ํค๋, ํธํฐ ๋ณด์ฌ์ฃผ๊ธฐ๊ฐ ๊ฐ๋ฅ
Outlet
- ์์ ๊ฒฝ๋ก ์์๋ฅผ ๋ ๋๋งํ๋ ค๋ฉด ๋ถ๋ชจ ๊ฒฝ๋ก ์์์์ outlet ์ฌ์ฉ
- ํ์ ๊ฒฝ๋ก๊ฐ ๋ ๋๋ง ๋ ๋ ์ค์ฒฉ๋ UI ํ์๊ฐ๋ฅ
- ๋ถ๋ชจ ๋ผ์ฐํธ๊ฐ ์ ํํ ์ผ์นํ๋ฉด ์์ ์ธ๋ฑ์ค ๋ผ์ฐํธ๋ฅผ ๋ ๋๋งํ๊ฑฐ๋ ์ธ๋ฑ์ค ๋ผ์ฐํธ๊ฐ ์์ผ๋ฉด ๋ ๋๋ง ํ์ง ์์
* ์์๋ ์ ํฌํจํ๋ ์์ ์ปดํฌ๋ํธ๋ ํ์ ์ปดํฌ๋ํธ์ ๋ชจ๋ ํ์๋๋๋ฏ
useNavigate
- ๊ฒฝ๋ก ๋ฐ๊ฟ์ค
useParams
- :style ๋ฌธ๋ฒ์ path ๊ฒฝ๋ก์ ์ฌ์ฉํ๋ค๋ฉด useParams()๋ก ์ฝ์ ์ ์์
ex) path = "invoices/:invoiceId
- Invoice ์ปดํฌ๋ํธ ์์์ ๊ฐ์ ธ์ฌ๋ useParams()๋ฅผ ์ฌ์ฉํด์ ๊ฐ์ ธ์ฌ ์ ์์
useLocation
- ์ด Hooks๋ ํ์ฌ ์์น ๊ฐ์ฒด๋ฅผ ๋ฐํ
- ํ์ฌ ์์น๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ผ๋ถ side effect๋ฅผ ์ํํ๋ ค๋ ๊ฒฝ์ฐ์ ์ ์ฉ
useRoutes
- hooks๋ฅผ ์ด์ฉํด์ ๊ฒฝ๋ก ๋ง๋ค๊ธฐ๋ ๊ฐ๋ฅ
5. CSS ์ ์ฒด ๊ตฌ์กฐ & React Icons
<div class="flex flex-col h-screen justify-between"> <header class="h-10 bg-red-500">Header</header> <main class="mb-auto h-10 bg-green-500">Content</main> <footer class="h-10 bg-blue-500">Footer</footer> </div> |
* flex-col : main ์ถ์ด ์์์ ์๋๋ก, ์์ง ์ถ์ด ์ผ์ชฝ์์ ์ค๋ฅธ์ชฝ์ผ๋ก ๋ฐ๋ (์๋๋ ๋ฐ๋)
* justify between : ๊ฐ์ด๋ฐ
* mb-auto (margin bottom auto) : ์ค๊ฐ์์ ์๋ก ์ฌ๋ ค์ค
React Icons ๋ค์ด
npm install react-icons |
App.jsx
import { Outlet, Route, Routes } from "react-router-dom";
import "./App.css";
import LandingPage from "./pages/LandingPage";
import LoginPage from "./pages/LoginPage";
import RegisterPage from "./pages/RegisterPage";
import Navbar from "./layout/Navbar";
import Footer from "./layout/Footer";
function Layout() {
return (
<div className="flex flex-col h-screen justify-between">
<Navbar />
<main className="mb-auto w-10/12 max-w-4xl mx-auto">
<Outlet />
</main>
<Footer />
</div>
);
}
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<LandingPage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegisterPage />} />
</Route>
</Routes>
);
}
export default App;
Footer > index.jsx
import React from "react";
import { AiOutlineSmile } from "react-icons/ai";
const Footer = () => {
return (
<div className="flex h-20 text-lg justify-center items-center">
All rights reserved.
<AiOutlineSmile />
</div>
);
};
export default Footer;
6. Redux ์์ฑ
userSlice.js
cd frontend npm install @reduxjs/toolkit react-redux |
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
userData: {
id: "",
email: "",
name: "",
role: 0,
image: "",
},
isAuth: false,
isLoading: false,
error: "",
};
const userSlice = createSlice({
name: "user",
initialState,
reducers: {},
extraReducers: (builder) => {},
});
export default userSlice.reducer;
store ์์ฑ
import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./userSlice";
export const store = configureStore({
reducer: {
user: userReducer,
},
});
store ์ฌ์ฉํ App์ Provider ๊ฐ์ธ์ฃผ๊ธฐ (redux store ์ ์ฅ์์ ์์ธ์คํด์ผํ๋ ๋ชจ๋ ์ค์ฒฉ ๊ตฌ์ฑ์์์์ store ์ฌ์ฉ๊ฐ๋ฅํ๊ฒ ํจ)
import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./userSlice";
export const store = configureStore({
reducer: {
user: userReducer,
},
});
7. Redux-Persist
๋ฆฌ๋์ค ์คํ ์ด์์๋ State๋ค์ ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ๋ฉด ์ด๊ธฐํ๋จ
ํ์ง๋ง Redux Persist๋ฅผ ์ด์ฉํ๋ฉด ํ์ด์ง ์๋ก๊ณ ์นจ ํ์๋ ์ํ ์ ์ง ๊ฐ๋ฅ
cd frontend npm i redux-persist |
์ง๋ ฌํ(serialize) : object ๊ฐ์ string ๊ฐ์ผ๋ก ๋ณํ (JSON.stringify)
์ญ์ง๋ ฌํ(deserialize) : string๊ฐ์ object ๊ฐ์ผ๋ก ๋ณํ (JSON.parse)
* reduxmiddleware์์๋ ๋ค์ด์ค๋ action ํน์ ๊ฐ์ด serializableํ ๊ฐ์ธ์ง ์ฒดํฌํจ (์๋๋ฉด ๊ฒฝ๊ณ )
* redux persist ์ด์ฉํ๋ฉด serializable ํ์ง ์์ function์ด ๋ค์ด์๊ธฐ ๋๋ฌธ์ option์ผ๋ก serializableCheck: false๋ก ๋
--> but, redux persist ์ฌ์ฉํ ๋๋ง ์ฒดํฌ ignoreํ๋ ์ต์ ๊ถ์ฅํจ (FLUSH ๊ฐ์๊ฒ action์ ํ์ ๋ค์)
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import userReducer from "./userSlice";
import storage from "redux-persist/lib/storage";
import {
FLUSH,
PAUSE,
PERSIST,
PURGE,
REGISTER,
REHYDRATE,
persistReducer,
persistStore,
} from "redux-persist";
const rootReducer = combineReducers({
user: userReducer,
});
const persistConfig = {
key: "root",
storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}),
});
export const persistor = persistStore(store);
* serializableํ ๊ฒ๋ค์ store์ ๋ณด๊ด์ํ๋๊ฒ ์ข์ ์๋๋
<PersistGate>
persist gate๋ก provider์ฒ๋ผ ๊ฐ์ธ์ค์ผํจ
- redux store์์ ์ง์ ๋ฐ์ดํฐ ์ฌ์ฉํ ์ ์์๋๊น์ง ์ฑ์ UI ๋๋๋ง ์ง์ฐ ๊ฐ๋ฅ
- ํ์ด์ง ๋ฆฌํ๋ ์ ๋๋ฉด store ๊ฐ ์ด๊ธฐํ๋จ -> ๊ทธ๊ฑธ rehydrateํด์ local storage๊ฐ์ ๊ฐ์ ธ์์ store์ ๋ฃ์ด์ค
๊ทธ๋ฐ๋ฐ ์ด๋ ๊ทธ ์ฌ์ด์ UI ๋๋๋ง์ด ๋๋ฉด ๊ฐ์ด ์ ์ฅ์ด ์๋์๋ ์ํ๋ก ๋์ด --> ์ด๊ฑธ ์ง์ฐ์ํค๊ธฐ ์ํ ์ฅ์น
ReactDOM.createRoot(document.getElementById("root")).render(
<BrowserRouter>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
</BrowserRouter>
);
'๐จโ๐ป Web Development > React JS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[React JS] 5. ํ์๊ฐ์ /๋ก๊ทธ์ธ/๋ก๊ทธ์์ ๊ธฐ๋ฅ ๊ตฌํํ๊ธฐ (0) | 2023.07.17 |
---|---|
[React JS] 4. ๋ฐฑ์๋ ๊ธฐ๋ณธ๊ตฌ์กฐ ์์ฑํ๊ธฐ (0) | 2023.07.15 |
[React JS] [์ฐธ์กฐ] Redux (0) | 2023.06.22 |
[React JS] 2. React.js (0) | 2023.06.08 |
[React JS] 1. Node.js (0) | 2023.05.21 |
์ต๊ทผ๋๊ธ