[React JS] 2. React.js
1. ๋ฆฌ์กํธ๋?
- ํ์ด์ค๋ถ์์ ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (2013)- Components๋ก ์ด๋ค์ ธ์์ด reusable / ์ฌ์ฌ์ฉ์ฑ์ด ๋ฐ์ด๋จ ex) like๊ธฐ๋ฅ, comment.js ๋ฅผ ๋ค๋ฅธ ๋ฐ์ ์ฌ์ฉ๊ฐ๋ฅ!
Real DOM VS Virtual DOMReal DOM - ๋ง์ฝ 10๊ฐ์ ๋ฆฌ์คํธ ์ค ํ๊ฐ์ง์ ๋ฆฌ์คํธ๋ง update ๋จ- ์ ์ฒด ๋ฆฌ์คํธ๋ฅผ ๋ค์ reload ํด์ผ๋จ -> super expensive
Virtual DOM- ๋ง์ฝ 10๊ฐ์ ๋ฆฌ์คํธ๊ฐ ์ค ํ๊ฐ์ง์ ๋ฆฌ์คํธ๋ง update ๋จ- ๊ทธ ๋ฐ๋ ํ๊ฐ์ง ์์ดํ ๋ง DOM์์ ๋ฐ๊ฟ์ค
์ด๋ป๊ฒ? -> virtual dom์์ ์ค๋ ์ท ์ฐ์ด๋๊ณ ์ดํด๋ณธ ๋ค ๋ฐ๋ ๋ถ๋ถ๋ง real dom์์ ๋ฐ๊ฟ์ค
2. Create React App์ผ๋ก ๋ฆฌ์กํธ ์์ํ๊ธฐ
- ์๋ babel(์ต์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฌธ๋ฒ ์ง์ํ์ง ์๋ ๋ถ๋ผ์ฐ์ ์์ ์ต์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฌธ๋ฒ์ ๋ณผ ์ ์๊ฒ ๋ณํ์์ผ์ค)์ด๋ webpack(์น์ฌ์ดํธ ๋ง๋ค๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ํ๋ ์์ํฌ๋ค์ ๋ฒ๋ค=๋ฌถ์ด์ฃผ๋ ์ญํ = ๋ง์ ๋ชจ๋๋ค์ ๊ฐ๋จํ๊ฒ ๋ง๋ค์ด์ค)
cd client
npx create-react-app . |
dot : client ์์๋ค ์ค์นํ๋ค๋ ์๋ฏธ
3. npm npx
npm(node package manager)
1) ์คํ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ด๊ณ ์๋ online repository
2) version management, dendency management ๋ฅผ ์ํ command-line utility
local VS globally
๋ก์ปฌ์ ๋ฐ์๋ node_modules์ ๋ฐ์์ง
-g ํ๋๊ทธ ์ฃผ๋ฉด ํ๋ก์ ํธ ์์ด ์๋๋ผ ์ปดํจํฐ ์์ฒด์ ๋ฐ์์ง๋ ๊ฒ
npx
- ์๋๋ create app ์ฌ์ฉํ ๋ ์ปดํจํฐ์ ๋ฐ์์ง๋ง npx๋ฅผ ํตํด npm registry์์ ์ฐพ์์ ๋ค์ด๋ก๋ ์์ด ์คํ
- ์ฅ์ : disk space ๋ญ๋น X & ํญ์ ์ต์ ๋ฒ์ ์ฌ์ฉ ๊ฐ๋ฅ
4. ๊ตฌ์กฐ ์ค๋ช
App.js
- ์ฌ๊ธฐ์ ํ์ด์ง ๋ ๋๋ง
index.js
- ์ฌ๊ธฐ ์์ App ์ปดํฌ๋ํธ๊ฐ ๋ค์ด๊ฐ๊ฒ
- document.getElementById (index.html ์ root์ ๋ณด์ฌ์ค ๊ฒ์ ์ ์ํด์ฃผ๋ ๊ฒ)
webpack์ด src๋ง ๊ด๋ฆฌํด์ค -> src๋ถ๋ถ์ ์ด๋ฏธ์ง ํ์ผ์ ๋ฃ์ด์ค์ผํจ! (public์ด ์๋๋ผ)
5. CRA to Our Boilerplate
hoc : ์ปดํฌ๋ํธ์์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ๋ function
ex) Auth(HOC)๊ฐ ์๊ณ Admin Component๊ฐ ์์ ๋ Auth ์ปดํฌ๋ํธ์์ ํด๋น ์ปดํฌ๋ํธ ์ ๊ทผ ๊ฐ๋ฅ ์ฌ๋ถ ์ฒดํฌ
6. React Router Dom
App.js์ router ๋ง๋ค๊ธฐ
cd client npm install react-router-dom --save |
import React from "react";
import {
Route,
Routes,
Link,
BrowserRouter
} from "react-router-dom";
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function Users() {
return <h2>Users</h2>;
}
function App() {
return (
<BrowserRouter>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
<hr />
{/* A <Switch> looks through its children <Route>s and
renders the first one that matches the current URL. */}
<Routes>
<Route path="/about" element={About()}>
<Route />
</Route>
<Route path="/users" element={Users()}>
<Route />
</Route>
<Route path="/" element={Home()}>
<Route />
</Route>
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
7. Flow & Axios
์์ฒญ ๋ณด๋ผ๋ Axios๋ฅผ ํตํด ๋ณด๋ (Ajax ๊ฐ์ ๊ฒ)
cd client npm install axios --save |
server
// ํ๋ก ํธ์ ํด๋น ๋ฉ์์ง ์ ๋ฌ
app.get("/api/hello", (req, res) => {
res.send("์๋
ํ์ธ์");
});
client
function LandingPage() {
// api ์๋ฒ๋ก ๋ณด๋ด๊ณ (index.js)
useEffect(() => {
axios.get("/api/hello").then((response) => console.log(response.date));
}, []);
return <div>LandingPage</div>;
}
8. CORS์ด์, Proxy ์ค์
CORS : Cross-Origin Resource Sharing
* ๋ค๋ฅธ ํฌํธ๋ฅผ ๊ฐ์ง๊ณ ์๋ ๋ ์๋ฒ ์ฌ์ด ์๋ฌด ์ค์ ์์ด request ๋ณด๋ผ ์ ์๊ฒ ๋ง์ ๊ฒ
proxy ๋ชจ๋ ๋ค์ด
cd client npm install http-proxy-middleware --save |
setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app){
app.use(
createProxyMiddleware('/api', {
target: 'http://localhost:5000/',
changeOrigin: true
})
)
};
9. Concurrently
ํ๋ก ํธ/๋ฐฑ ์๋ฒ ๋์์ ์ผ๊ธฐ ์ํด
npm install concurrently --save |
package.json script์ ์ถ๊ฐ
"dev": "concurrently \"npm run backend\" \"npm run start --prefix client\""
10. CSS Framework ์ข ๋ฅ for React.js
Ant Design: https://ant.design/
cd client npm install antd --save |
npm install antd@^4.24.0 --save |
11. Redux
Redux : ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (predictable state container)
state?
Props VS State
1) Props (properties)
- ์ปดํฌ๋ํธ ๋จ๋ผ๋ฆฌ ์ฃผ๊ณ ๋ฐ์๋ prop ํ์ฉ
- ๋ถ๋ชจ์์ ์์ ์ปดํฌ๋ํธ์๋ง ๋ณด๋ผ ์ ์์
- ๋ถ๋ชจ์์ ์์์ผ๋ก ๊ฑด๋ด๋ฐ์ ๊ฐ์ ๋ฐ๋ ์ ์์ (๋ค์ ๋ถ๋ชจ์์ ์์์๊ฒ ๊ฑด๋ด์ค์ผ ๋ฐ๋)
- immutable
<ChatMessages
messages={messages}
currentMember={member}
/>
2) State
- ์ปดํฌ๋ํธ ์์์ ๋ฐ์ดํฐ ๊ตํ
- ๊ฐ ๋ฐ๋๋ฉด ๋ฆฌ๋๋๋ง ๋จ
- mutable
state = {
message: '',
attachFile: undefined,
openMenu: false
}
-> redux๋ state๋ฅผ ๊ด๋ฆฌ
- ํ๋ฐฉํฅ์ผ๋ก๋ง ํ๋ฆ
1)Action
- ๊ฐ์ฒด
- ๋ฌด์์ด ์ผ์ด๋ฌ๋์ง ์ค๋ช
{ type: 'LIKE_ARTICLE', articleId: 42 }
2)Reducer
- action์ผ๋ก ์ธํด ์ด์ state๊ณผ ๋ณํด์ง state ๋ฆฌํด
(previousState, action) => nextState
3)Store
- ์ดํ๋ฆฌ์ผ์ด์ state์ ๊ฐ์ธ์ฃผ๋ ์ญํ
- ์ฌ๋ฌ๊ฐ์ง ๋ฉ์๋ ์์
Setting Up Redux
cd client npm install redux react-redux redux-promise redux-thunk --save |
* redux-promise & redux-thunk
- ๋ฏธ๋ค์จ์ด
- ํ์ํ ์ด์
> ๋ฆฌ๋์ค๋ฅผ ์ ์ธ ์ ์๊ฒ ๋์์ฃผ๋ ์ญํ
> redux store์ state๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฐฉ๋ฒ์ด dispatch(action)
-> action์ ๊ฐ์ฒด ํ์์ด์ด์ผํจ
-> store๋ promise/function ํํ๋ก ๋ฐ์ ๋๋ ์๊ธฐ ๋๋ฌธ์
-> promise๋ promise ๋์ฒํ๋ ๋ฒ ์๋ ค์ค
-> thunk๋ dispatch์ function ๋ฐ๋ ๋ฒ ์๋ ค์ฃผ๋ ๊ฒ
client src index.js
import { Provider } from "react-redux";
import { applyMiddleware, createStore } from "redux";
import promiseMiddleware from "redux-promis";
import ReduxThunk from "redux-thunk";
import Reducer from './_reducers';
import "antd/dist/antd.css";
const createStoreWithMiddleware = applyMiddleware(
promiseMiddleware,
ReduxThunk
)(createStore);
root.render(
<Provider
store={createStoreWithMiddleware(
Reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)}
>
<App />
</Provider>
);
_reducers ํด๋์ index.js ์ถ๊ฐ
import { combineReducers } from "redux";
//import user from "./user_reducer";
const rootReducer = combineReducers({});
export default rootReducer;
combineReducers
- store๊ฐ ์๋๋ฐ ์์ ์ฌ๋ฌ๊ฐ์ง reducers ์์ (reducer ํ๋ ์ผ์ด state๊ฐ ์ด๋ป๊ฒ ๋ณํ๋์ง ๋ณด์ฌ์ฃผ๊ณ ๋ณํ ๋ง์ง๋ง ๊ฐ์ returnํ๋ ์ญํ )
- combineReducers ์ด์ฉํด์ ๋๋ ์ง rootReducer๋ก ํ๋๋ก ํฉ์ณ์ฃผ๋ ์ญํ
chrome extension - Redux Dev Tools ๋ค์ด
12. React Hooks
React Component
1) Class Component
- ๋ ๋ง์ ๊ธฐ๋ฅ ์ฌ์ฉ ๊ฐ๋ฅ
- ์ฝ๋๊ฐ ๊ธธ์ด์ง๊ณ ๋ณต์กํด์ง
- ์ฑ๋ฅ ๋๋ ค์ง
2) Functional Component
- ์ ๊ณต ๊ธฐ๋ฅ ํ์ ๋จ
- ์ฝ๋๊ฐ ๊ฐ๋จํด์ง
- ์ฑ๋ฅ์ด ๋นจ๋ผ์ง
Functional Component์์ ๋ชป์ฐ๋ ๊ธฐ๋ฅ
๋ฆฌ์กํธ ์์ฑ ์์
- constructor ์คํ (state ๋ถ์ฌ)
- render (DOM์ ์๋ง๊ฒ ๋ฃ์ด์ ๋๋๋ง)
- ComponentMount ํตํด์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ด
ใด ์ ๊ธฐ๋ฅ๋ค์ ์๋ ๋ชป์ผ์
*HTML DOM
React Hooks update (16.8v ์ด์)
-> functional component์์ ๋ผ์ดํ ์ฌ์ดํด / state ๋ถ์ฌ ๋ค ์ฌ์ฉ ๊ฐ๋ฅ
๋น๊ต
hook์ด ๋์ค๋ฉด์ useEffect๋ก ๋ผ์ดํ์คํ์ผ ์ฌ์ฉ๊ฐ๋ฅ (์ต์ hook ๋ง์ด ์)
13. LoginPage ๋ง๋ค๊ธฐ & RegisterPage ๋ง๋ค๊ธฐ
LoginPage.js
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { loginUser } from "../../../_actions/user_action";
import { useNavigate } from "react-router-dom";
function LoginPage() {
const dispatch = useDispatch();
const navigate = useNavigate();
// Email state์
const [Email, setEmail] = useState("");
const [Password, setPassword] = useState("");
const onEmailHandler = (event) => {
setEmail(event.currentTarget.value);
};
const onPasswordHandler = (event) => {
setPassword(event.currentTarget.value);
};
const onSubmitHandler = (event) => {
// ๋ก๊ทธ์ธ ๋ฒํผ ๋๋ฅด๋ฉด ์ด ํจ์์์ ํด์ผํ ์ผ์ ํ์ง ์๊ณ ํ์ด์ง๊ฐ ๋ฆฌํ๋ ์ ๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด์
event.preventDefault();
let body = {
email: Email,
password: Password,
};
// dispatch ์ด์ฉํด์ action์ทจํ๊ณ ๊ทธ ๋ค์์ reducer ๋ผ์ดํ ์ฌ์ดํด ๋๊ฒํ๋ ๊ฒ
dispatch(loginUser(body)).then((response) => {
if (response.payload.loginSuccess) {
navigate("/");
} else {
alert("Error");
}
});
};
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100vh",
}}
onSubmit={onSubmitHandler}
>
<form style={{ display: "flex", flexDirection: "column" }}>
<label>Email</label>
<input type="email" value={Email} onChange={onEmailHandler} />
<label>Password</label>
<input type="password" value={Password} onChange={onPasswordHandler} />
<br />
<button type="submit">Login</button>
</form>
</div>
);
}
export default LoginPage;
_actions > user_action.js
import axios from "axios";
import { LOGIN_USER } from "./types";
export function loginUser(dataTosubmit) {
const request = axios
.post("/api/users/login", dataTosubmit)
.then((response) => response.data);
return {
// request๋ฅผ reduce์ ๋๊ฒจ์ฃผ๋ ์์
type: LOGIN_USER,
payload: request,
};
}
_actions > types
export const LOGIN_USER = "login_user";
_reducers > user_reducer.js
import { LOGIN_USER } from "../_actions/types";
// reduce๋ previousState + action = NextState๋ง๋๋ ์ญํ
export default function userReducer(state = {}, action) {
switch (action.type) {
case LOGIN_USER:
// state๋ฅผ ๋๊ฐ์ด ๊ฐ์ ธ์ค๊ณ
// ... spread operator = ๋๊ฐ์ด ๊ฐ์ ธ์ค๋ ๊ฒ, ๋น ์ํ
return { ...state, loginSuccess: action.payload };
default:
return state;
}
}
_reducers > index.js
import { combineReducers } from "redux";
import user from "./user_reducer";
const rootReducer = combineReducers({
user,
});
export default rootReducer;
* RegisterPage ์ญ์ ๋์ผํ ๋ฐฉ์
14. Log out ๊ธฐ๋ฅ ๋ง๋ค๊ธฐ
LandingPage.js
import React, { useEffect } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
function LandingPage() {
// api ์๋ฒ๋ก ๋ณด๋ด๊ณ (index.js)
const navigate = useNavigate();
useEffect(() => {
axios.get("/api/hello").then((response) => console.log(response.date));
}, []);
const onClickHandler = () => {
axios.get("/api/users/logout").then((response) => {
if (response.data.success) {
navigate("/login");
} else {
alert("failed logout");
}
});
};
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100vh",
}}
>
<h2>์์ํ์ด์ง</h2>
<button onClick={onClickHandler}>๋ก๊ทธ์์</button>
</div>
);
}
export default LandingPage;
15. Authentication
HOC (higher order component)
const EnhancedComponent = higherOrderComponent(WrappedComponent);
- ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ๋ฐ์ ํ ์๋ก์ด ์ปดํฌ๋ํธ ๋ฆฌํดํ๋ function
- Auth(HOC)์ ๋ค๋ฅธ ์ปดํฌ๋ํธ ์ง์ด๋ฃ์ ์ ์์
- Auth์ LandingPage๋ฅผ ๋ฃ์ผ๋ฉด
-> Auth๊ฐ backend์ request๋ฅผ ๋ ๋ฆผ
-> ํ์ฌ LandingPage ๋ค์ด์์๋ ์ฌ๋์ ์ํ์ ๋ณด๋ฅผ ์๋ฒ์์ Auth๋ก ๋ณด๋
-> Auth๊ฐ ์์ฒญ ๋ ๋ฆฌ๋ฉด์ ์ํ ๊ฐ์ ธ์์ access Authentication ํ์ธ
hoc> auth.js
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import { auth } from "../_actions/user_action";
import { useNavigate } from "react-router-dom";
// specificComponent = Page
// option
// null : ์๋ฌด๋ ์ถ์
๊ฐ๋ฅํ ํ์ด์ง
// true : ๋ก๊ทธ์ธํ ์ ์ ๋ง ์ถ์
๊ฐ๋ฅํ ํ์ด์ง
// false : ๋ก๊ทธ์ธํ ์ ์ ๋ ์ถ์
๋ถ๊ฐ๋ฅํ ํ์ด์ง
export default function (SpecificComponent, option, adminRoute = null) {
function AuthenticationCheck(props) {
const dispatch = useDispatch();
const navigate = useNavigate();
// ๋ฐฑ์๋์ request ๋ ๋ ค์ ์ฌ๋์ state ๊ฐ์ ธ์ด
useEffect(() => {
// ํ์ด์ง ์ด๋ํ ๋๋ง๋ค dispatch์๋ํด์ backend์ request๋ฅผ ์ฃผ๋ ๊ฒ
dispatch(auth()).then((response) => {
console.log(response);
// ๋ก๊ทธ์ธ ํ์ง ์์ ์ํ
if(!response.payload.isAuth){
//true์ธ๊ฒฝ์ฐ
if(option){
navigate("/login");
}
}else{
//๋ก๊ทธ์ธ ํ ์ํ
// ์ด๋๋ฏผ ํ์ด์ง์ธ๋ฐ && isAdmin์ด ์๋๊ฒฝ์ฐ
if(adminRoute && !response.payload.isAdmin){
navigate("/");
}else{
// false์ผ ๋
if(!option){
navigate("/");
}
}
}
});
}, []);
return <SpecificComponent />;
}
return AuthenticationCheck;
}
App.js
import React from "react";
import { Route, Routes, Link, BrowserRouter } from "react-router-dom";
import LandingPage from "./components/views/LandingPage/LandingPage";
import LoginPage from "./components/views/LoginPage/LoginPage";
import RegisterPage from "./components/views/RegisterPage/RegisterPage";
import Auth from "./hoc/auth";
function App() {
const NewLandingPage = Auth(LandingPage, null);
const NewLoginPage = Auth(LoginPage, false);
const NewRegisterPage = Auth(RegisterPage, false);
return (
<BrowserRouter>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/login">Login</Link>
</li>
<li>
<Link to="/register">Register</Link>
</li>
</ul>
<hr />
{/* A <Switch> looks through its children <Route>s and
renders the first one that matches the current URL. */}
<Routes>
<Route exact path="/login" element={<NewLoginPage />}>
<Route />
</Route>
<Route exact path="/register" element={<NewRegisterPage />}>
<Route />
</Route>
<Route exact path="/" element={<NewLandingPage />}>
<Route />
</Route>
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
'๐จโ๐ป 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] 3. ํ๋ก ํธ์๋ ๊ธฐ๋ณธ๊ตฌ์กฐ ์์ฑํ๊ธฐ (0) | 2023.06.18 |
[React JS] 1. Node.js (0) | 2023.05.21 |
์ต๊ทผ๋๊ธ