๋ฆฌ์กํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ฃผ์ํ ์๋ฌด๋ UI๋ฅผ ๋ ๋๋ง ํ๋ ๊ฒ์ด๋ค. ์ฆ, ์ฌ์ฉ์ ์
๋ ฅ์ ๋ฐ์ํ์ฌ ํ์ํ ๋ UI๋ฅผ ๋ค์ ๋ ๋๋งํ๋ ๊ฒ์ด ๋ฆฌ์กํธ์ ์ผ์ด๋ค.
์ปดํฌ๋ํธ๋ ๋จ์ง ํจ์์ด๊ธฐ ๋๋ฌธ์ ์์์ ๋ถํฐ ์๋๋ก ์คํ๋๋๋ฐ, ๊ฒฐ๊ตญ์๋ ํ๋ฉด์ ๋ฌด์ธ๊ฐ ๊ฐ์ ธ์ค๋ ๊ฒ์ด ํจ์๊ฐ ํ๋ ์ผ์ ์ ๋ถ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ์ดํํธ(=์ฌ์ดํธ ์ดํํธ)๋ ๋ฌด์์ผ๊น?
๋ฐ๋ก ํ๋ฉด์ ๋ฌด์ธ๊ฐ๋ฅผ ๋ ๋๋งํ๋ ์ผ์ด ์๋ ๋ชจ๋ ์ผ, ์ฆ ์ฑ์์ ์ผ์ด๋๋ ๋ค๋ฅธ ๋ชจ๋ ๊ฒ์ ์๋ฏธํ๋ค.
useEffect() ํ ์ ๋ฆฌ์กํธ ๋ด์ฅ ํ ์ผ๋ก ์ปดํฌ๋ํธ ํจ์ ๋ด๋ถ์์ ์ฌ์ฉํ ์ ์๋ ํน๋ณํ ์ผ์ ํ๋ ํจ์์ด๋ค. useEffect()ํ ์ ์ฌ์ฉํ๋ฉด ์ฌ์ด๋ ์ดํํธ๋ฅผ ์ ์ ํ ์ฒ๋ฆฌํ ์ ์๋ค.
useEffect(() => {...}, [ dependencies ]);
useEffect()๋ฅผ ์ ํ์ฉํ๋ฉด ์์กด์ฑ์ด ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ์๋ง ์ฌ์ด๋ ์ดํํธ ์ฝ๋๊ฐ ์คํ๋๊ธฐ ๋๋ฌธ์ ๋ฌดํ ๋ฃจํ์ ๋น ์ง์ง ์์ ์ ์๋ค.
์๋ ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๊ทธ์ธ ํ, ์๋ก๊ณ ์นจ์ด ๋์ด๋ ๋ก๊ทธ์ธ์ ์ ์งํ ์ ์๋๋ก ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ํค-๊ฐ ์๋ณ์๋ฅผ ์ ์ฅํ์ฌ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ์๋ณ์๊ฐ ์๋ ๊ฒฝ์ฐ ๋ก๊ทธ์ธ์ด ์ ์ง๋๋๋ก ์ฝ๋๋ฅผ ์์ฑํด ๋ณด์.
import React, { useState } from 'react';
import Login from './components/Login/Login';
import Home from './components/Home/Home';
import MainHeader from './components/MainHeader/MainHeader';
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const loginHandler = (email, password) => {
// We should of course check email and password
// But it's just a dummy/ demo anyways
setIsLoggedIn(true);
};
const logoutHandler = () => {
setIsLoggedIn(false);
};
return (
<React.Fragment>
<MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
<main>
{!isLoggedIn && <Login onLogin={loginHandler} />}
{isLoggedIn && <Home onLogout={logoutHandler} />}
</main>
</React.Fragment>
);
}
export default App;
์ฑ์ด ์์ํ ๋ ๋ง๋ค (๋ก๊ทธ์ธ)๋ฐ์ดํฐ๊ฐ ์ ์ง๋๋์ง ํ์ธํ๋ ์์ ์ ํ๊ธฐ ์ํด์ ๋จผ์ ๋ธ๋ผ์ฐ์ ์ ์ฅ์(๋ก์ปฌ์คํ ๋ฆฌ์ง/์ฟ ํค)์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํด์ผ ํ๋ค.
๋ธ๋ผ์ฐ์ ์ ๋ด์ฅ๋ ์ ์ฅ ๋งค์ปค๋์ฆ์ธ ๋ก์ปฌ ์คํ ๋ฆฌ์ง๋ ๋ฆฌ์กํธ์ ์๋ฌด ์๊ด์ด ์๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ ์์ ์๋ loginHandler ํจ์์์ ๋ถ๋ฌ์์ ์คํํ ์ ์๋ค.
localStorage.setItem("์๋ณ์", "์๋ณ์");
setItem()
์ ๋ธ๋ผ์ฐ์ ์์ ์ฌ์ฉํ ์ ์๋ ์ ์ญ ๊ฐ์ฒด์ด๋ค. const loginHandler = (email, password) => {
// We should of course check email and password
// But it's just a dummy/ demo anyways
//๋ก๊ทธ์ธ ํ์ผ๋ฉด 1, ๋ก๊ทธ์ธ ์ ํ์ผ๋ฉด 0์ผ๋ก ์๋ณ
localStorage.setItem("isLoggedIn", "1");
setIsLoggedIn(true);
};
loginHandler ํจ์๋ ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ ๋ฒํผ์ ํด๋ฆญํ ๋๋ง ์คํ๋๊ธฐ ๋๋ฌธ์ ์์ฃผ ์ผ์ด๋๋ ์ผ์ ์๋๊ณ , ๋ญ๊ฐ ์ ์ฅํ๊ณ ์ถ์ ๋(๋ก๊ทธ์ธ ๋ฒํผ์ด ํด๋ฆญ๋ ๋)๋ง ์คํ๋๋ค. ๋ฐ๋ผ์ ์ด ๊ฒฝ์ฐ์๋ useEffect
๊ฐ ๋ฐ๋์ ํ์ํ์ง๋ ์๋ค.
ํ์ง๋ง ์ฑ์ด ๋ค์ ์์๋๋ ๊ฒฝ์ฐ, ์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ๋ ๋ฌ๋ค๊ฐ ๋ค์ ๋์์ค๋ ๊ฒฝ์ฐ, ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ ๊ฒฝ์ฐ์๋ ์ด๋จ๊น?
๋ก์ปฌ์คํ ๋ฆฌ์ง์๋ ํค-๊ฐ ์์ด ๋จ์์๋๋ผ๋ ์๋ก๊ณ ์นจํ๋ฉด, ์ฆ ์ฑ์ด ๋ค์ ์์ํ๋ฉด App ์ปดํฌ๋ํธ ํจ์๋ ๋ค์ ์คํ๋๊ธฐ ๋๋ฌธ์ ๋ก๊ทธ์ธ์ ํ๋ ค ๋ฒ๋ฆฐ๋ค.
๋ก๊ทธ์ธ ๋ฒํผ์ ํด๋ฆญํ์ง ์์๋, ์ฆ loginHandler๊ฐ ํธ๋ฆฌ๊ฑฐ ๋์ง ์์๋ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ isLoggedIn์ ๊ฐ์ด 1์ธ ๊ฒฝ์ฐ์๋ ๋ก๊ทธ์ธ์ด ์ ์ง๋๊ฒ ํ๊ธฐ ์ํด, ์กฐ๊ฑดํจ์๋ฅผ ๊ฑธ์ด state๋ฅผ true๋ก ๋ณ๊ฒฝํด ์ฃผ์.
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
// ๋ก๊ทธ์ธ ๋ฒํผ ํด๋ฆญํ์ง ์์๋, ์ฆ loginHandler๊ฐ ํธ๋ฆฌ๊ฑฐ ๋์ง ์์๋ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ isLoggedIn์ ๊ฐ์ด 1์ธ ๊ฒฝ์ฐ์๋ ๋ก๊ทธ์ธ ์ ์งํ๊ธฐ
const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");
if (storedUserLoggedInInformation === "1") {
setIsLoggedIn(true);
}
//...
๊ทธ๋ฐ๋ฐ ์ด๋ ๊ฒ๋ง ํ๋ฉด ๋ฌดํ ๋ฃจํ์ ๋น ์ง ์๊ฐ ์๋ค.
์ด๋ด ๋ useEffect()
๋ฅผ ์ฌ์ฉํ๋ฉด ์ธ์ ์ด ์ฌ์ด๋์ดํํธ๋ฅผ ์คํํ ์ง ์ ์ดํ ์ ์๋ค.
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
// ๋ก๊ทธ์ธ ๋ฒํผ ํด๋ฆญํ์ง ์์๋-์ฆ loginHandler๊ฐ ํธ๋ฆฌ๊ฑฐ ๋์ง ์์๋, ๋ก์ปฌ์คํ ๋ฆฌ์ง์ isLoggedIn์ ๊ฐ์ด 1์ธ ๊ฒฝ์ฐ์๋ ๋ก๊ทธ์ธ ์ ์งํ๊ธฐ
//๋ฌดํ ๋ฃจํ ๋ฐฉ์ง ์ํด useEffect ์ฌ์ฉ
useEffect(() => {
const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");
if (storedUserLoggedInInformation === "1") {
setIsLoggedIn(true);
}
}, []);
//...
์ฆ, ๋ํ๋์๊ฐ ๋น์ด ์๋ ๊ฒฝ์ฐ, ์์กด์ฑ์ด ์๊ธฐ ๋๋ฌธ์ ์ฑ์ด ์ฒ์ ์์๋๋ฉด ์์กด์ฑ์ด ๋ณ๊ฒฝ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋์ด ์ดํํธ ์ฝ๋๋ ์ฑ์ด ์์๋ ๋ ๋ฑ ํ ๋ฒ๋ง ์คํ๋๋ค.
์ด์ useEffect ํจ์ ์์ ์ดํํธ ์ฝ๋๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋ฆฌ์กํธ์ ์ํด ์คํ๋๋ฏ๋ก ์์กด์ฑ์ด ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ์๋ง, ๋ชจ๋ ์ปดํฌ๋ํธ ์ฌํ๊ฐ ํ์, useEffect ์์ ๋ ์ดํํธ ์ฝ๋๊ฐ ์คํ๋๋ค.
์ด ์ฒ๋ผ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
์ ๊ฐ์ ์ผ์ ๋ฆฌ์กํธ์ ์ฃผ๋ ์๋ฌด์ธ ํ๋ฉด ๊ทธ๋ฆฌ๊ธฐ๊ฐ ์๋ ์ฌ์ด๋ ์ดํํธ
์ด๋ค. ๋ฌผ๋ก ๊ฒฐ๊ณผ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ํ์๋ UI๊ฐ ๋ฐ๋๊ธด ํ์ง๋ง ๋ง์ด๋ค.
import React, { useEffect, useState } from "react";
import Login from "./components/Login/Login";
import Home from "./components/Home/Home";
import MainHeader from "./components/MainHeader/MainHeader";
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
// ๋ก๊ทธ์ธ ๋ฒํผ ํด๋ฆญํ์ง ์์๋ ์ฆ loginHandler๊ฐ ํธ๋ฆฌ๊ฑฐ ๋์ง ์์๋, ๋ก์ปฌ์คํ ๋ฆฌ์ง์ isLoggedIn์ ๊ฐ์ด 1์ธ ๊ฒฝ์ฐ์๋ ๋ก๊ทธ์ธ ์ ์งํ๊ธฐ
useEffect(() => {
const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");
if (storedUserLoggedInInformation === "1") {
setIsLoggedIn(true);
}
}, []);
//๋ํ๋์๊ฐ ๋น์ด ์๋ ๊ฒฝ์ฐ, ์์กด์ฑ์ด ์๊ธฐ ๋๋ฌธ์ ์ฑ์ด ์ฒ์ ์์๋๋ฉด ์์กด์ฑ์ด ๋ณ๊ฒฝ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋์ด ์ฑ์ด ์์๋ ๋ ์ดํํธ ์ฝ๋๋ ๋ฑ ํ ๋ฒ๋ง ์คํ๋จ
const loginHandler = (email, password) => {
// We should of course check email and password
// But it's just a dummy/ demo anyways
//๋ธ๋ผ์ฐ์ ์ ์ฅ์(๋ก์ปฌ์คํ ๋ฆฌ์ง/์ฟ ํค)์ ์ ๋ณด ์ ์ฅํด์, ์ฑ ์์ํ ๋ ๋ง๋ค ๋ฐ์ดํฐ ์ ์ง๋์๋์ง ํ์ธ
//๋ก๊ทธ์ธ ํ์ผ๋ฉด 1, ๋ก๊ทธ์ธ ์ ํ์ผ๋ฉด 0์ผ๋ก ์๋ณ
localStorage.setItem("isLoggedIn", "1");
setIsLoggedIn(true);
};
const logoutHandler = () => {
//๋ก๊ทธ์์์ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ isLoggedIn ์์ ๊ธฐ
localStorage.removeItem("isLoggedIn");
setIsLoggedIn(false);
};
return (
<React.Fragment>
<MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
<main>
{!isLoggedIn && <Login onLogin={loginHandler} />}
{isLoggedIn && <Home onLogout={logoutHandler} />}
</main>
</React.Fragment>
);
}
export default App;
๊ฐ๋ ๋ํ๋์๊ฐ ํ์ํ ๊ฒฝ์ฐ๊ฐ ์๋ค. ์ดํํธ ํจ์๋ฅผ ์ฑ์ด ์์๋ ๋ ํ ๋ฒ๋ง ์คํํ์ง ์๊ณ ์ฌ๋ฌ๋ฒ ์คํํด์ผ ํ ๊ฒฝ์ฐ ๋ํ๋์๋ฅผ ์ค์ ํ๋ฉด ๋๋ค.
์๋ ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ํ๋์๊ฐ ์๋ ๊ฒฝ์ฐ useEffect๊ฐ ์ด๋ป๊ฒ ์๋ํ๋์ง ์์๋ณด์.
import React, { useState } from 'react';
import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';
const Login = (props) => {
const [enteredEmail, setEnteredEmail] = useState('');
const [emailIsValid, setEmailIsValid] = useState();
const [enteredPassword, setEnteredPassword] = useState('');
const [passwordIsValid, setPasswordIsValid] = useState();
const [formIsValid, setFormIsValid] = useState(false);
const emailChangeHandler = (event) => {
setEnteredEmail(event.target.value);
//๐ ๊ฐ์ ์ ํจ์ฑ ๊ฒ์ฌ
setFormIsValid(
event.target.value.includes('@') && enteredPassword.trim().length > 6
);
};
const passwordChangeHandler = (event) => {
setEnteredPassword(event.target.value);
//๐ ๊ฐ์ ์ ํจ์ฑ ๊ฒ์ฌ
setFormIsValid(
event.target.value.trim().length > 6 && enteredEmail.includes('@')
);
};
const validateEmailHandler = () => {
setEmailIsValid(enteredEmail.includes('@'));
};
const validatePasswordHandler = () => {
setPasswordIsValid(enteredPassword.trim().length > 6);
};
const submitHandler = (event) => {
event.preventDefault();
props.onLogin(enteredEmail, enteredPassword);
};
return (
<Card className={classes.login}>
<form onSubmit={submitHandler}>
<div
className={`${classes.control} ${
emailIsValid === false ? classes.invalid : ''
}`}
>
<label htmlFor="email">E-Mail</label>
<input
type="email"
id="email"
value={enteredEmail}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>
</div>
<div
className={`${classes.control} ${
passwordIsValid === false ? classes.invalid : ''
}`}
>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={enteredPassword}
onChange={passwordChangeHandler}
onBlur={validatePasswordHandler}
/>
</div>
<div className={classes.actions}>
<Button type="submit" className={classes.btn} disabled={!formIsValid}>
Login
</Button>
</div>
</form>
</Card>
);
};
export default Login;
ํ์ฌ ์ ์ฝ๋์์๋ ์ด๋ฉ์ผ/๋น๋ฐ๋ฒํธ ํค ๊ฐ์ด ์ ๋ ฅ๋ ๋๋ง๋ค ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ๊ณ ์๋๋ฐ, ๊ฐ์ ์ ํจ์ฑ ๊ฒ์ฌ ๋ก์ง์ ๋ ํธ๋ค๋ฌ์ ๊ฐ๊ฐ ์์ฑํด์ ์คํํ๊ณ ์๋ค.
์ด ๋ก์ง์ ํฉ์ณ๋ณด์. ๋ก๊ทธ์ธ ์ปดํฌ๋ํธ ๋ด์ useEffect()๋ฅผ ์ฌ์ฉํ์ฌ ํ๋์ ๋ก์ง์ผ๋ก form์ด ์ ํจํ์ง ๊ฒ์ฌํ๋ ๋ก์ง์ ์ดํํธ ์ฝ๋๋ก ๋ฃ์ผ๋ฉด ๋๋ค.
useEffect(() => {
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
}, [enteredEmail, enteredPassword]);
์ด ์ฌ์ด๋ ์ดํํธ๋ ์ด๋ฉ์ผ
์ด๋ ๋น๋ฐ๋ฒํธ
๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ํธ๋ฆฌ๊ฑฐ ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋ํ๋์์ ์
๋ ฅ๋ ์ด๋ฉ์ผ๊ณผ ๋น๋ฐ๋ฒํธ state ๊ฐ์ ๋ฃ์ด์ผ ํ๋ค.
๋ํ๋์์ ์ด ๊ฐ์ ๋ฃ์ด์ผ, ์ด๋ฉ์ผ/๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ ํธ๋ค๋ฌ์ ๋ชจ๋ ํค ์
๋ ฅ์ ๋ํด์ ๊ณ์ํด์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๋๋ฆด ์ ์๊ณ ๋ฒํผ์ด ์ ํจํ๊ฒ ์๋ํ๋ค.
์์กด์ฑ์ด ์์ด์ง๊ธฐ ๋๋ฌธ์ ์ดํํธ ์ฝ๋๋ฅผ ๊ทธ๋ฅ ์ฉ์ผ๋ก Login ํจ์ ์์ ๋ฃ์ด๋ฒ๋ฆฐ ๊ฒฉ์ด ๋์ด๋ฒ๋ฆฐ๋ค.
๊ทธ๋ฌ๋ฉด ์ปดํฌ๋ํธ๊ฐ ์ฌ๋ ๋๋ง ๋ ๋๋ง๋ค ์ดํํธ ์ฝ๋๊ฐ ์คํ๋์ด ๋ฒ๋ฆฌ๋ฏ๋ก ๊ณ์ setFormIsValid๊ฐ ์คํ๋๊ธฐ ๋๋ฌธ์ ์ฌ๋ ๋๋ง ์ฃผ๊ธฐ ์์ฒด๋ฅผ ๊ณ์ ํธ๋ฆฌ๊ฑฐํด๋ฒ๋ฆฌ๋ฏ๋ก ๋ฌดํ๋ฃจํ ์ถฉ๋์ด ์ผ์ด๋๋ค. ๐ต
๊ฐ๋จํ ๊ท์น์ด ์๋ค. ๋ฐ๋ก ์ฌ์ด๋ ์ดํํธ ํจ์์์ ์ฌ์ฉํ๋ ๊ฒ์ ์์กด์ฑ์ผ๋ก ์ถ๊ฐํ๋ฉด ๋๋ค. ๊ทธ ๋ด์ฉ์ด ๋ฐ๋ ๋๋ง ์ฌ์ด๋ ์ดํํธ๊ฐ ์คํ๋๋๋ก ์ ์ดํ๋ฉด ๋๋ค.
์ฌ๊ธฐ์๋ setFormIsValid
, enteredEmail
, enteredPassword
๋ฅผ ๋ํ๋์์ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
=> ๋ชจ๋ ๋ก๊ทธ์ธ ์ปดํฌ๋ํธ ํจ์ ์คํํ ํ, ์ด useEffect ํจ์๋ฅผ ๋ค์ ์คํ์์ผ๋ผ. setFormIsValid ๋๋ enteredEmail ๋๋ enteredPassword๊ฐ ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ์๋ง!
๐ ๊ทธ๋ฐ๋ฐ setFormIsValid
๋ ์๋ต ๊ฐ๋ฅํ๋ค. ์๋ํ๋ฉด state ์
๋ฐ์ดํธ ํจ์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฆฌ์กํธ์ ์ํด ์ ๋ ๋ณ๊ฒฝ๋์ง ์๋๋ก ๋ณด์ฅ๋๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฐ๋ผ์ ์ด๋ฐ setState ํจ์๋ค์ ์ฌ๋ ๋๋ง ์ฃผ๊ธฐ์ ๋ฐ๋ผ ๋ณํ์ง ์์ผ๋ฏ๋ก, ๋ํ๋์์ ์๋ตํด๋ ๋๋ค.
enteredEmail
, enteredPassword
๋ ๋ฐ๋ ์ ์๋ ๊ฐ์ด๋ค. ํค๋ฅผ ๋๋ฅผ ๋ ๋ง๋ค ๋ณ๊ฒฝ๋๊ธฐ ๋๋ฌธ์ ๋ํ๋์์ ๋ฃ์ด์ผ ํ๋ค.
์ด๋ ๊ฒ ์ผ๋ฐ์ ์ผ๋ก ํน์ ๋ฐ์ดํฐ(state๋ prop)๊ฐ ๋ณ๊ฒฝ๋ ๋ ๋ก์ง์ ๋ค์ ์คํํ๊ธฐ ์ํด์ useEffect๋ฅผ ์ฌ์ฉํ๋ค.
์ฅ ๊ทผ๋ฐ ์ด๊ฒ ์ฌ์ด๋์ดํํธ๋ผ๊ณ ? http ๋ฆฌํ์คํธ ๋ฐ๊ฑฐ๋ ํ์ด๋จธ ์ค์ ํ๊ฑฐ๋ ํ๋๊ฒ ์ฌ์ด๋ ์ดํํธ ์๋์ผ? ๋ผ๊ณ ์๊ฐํ ์ ์์ง๋ง ๊ธฐ์ตํ์! useEffect์ ์ฃผ์ ์๋ฌด๋ ์ฌ์ด๋ ์ดํํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฒ์ด๋ค.
http ๋ฆฌํ์คํธ
๋ฅผ ๋ง์ด ๋ ์ฌ๋ฆฌ์ง๋งํค ์
๋ ฅ์ ๋ฐ์ ์
๋ ฅ๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๊ฒ
๊ทธ์ ๋ํ ์๋ต์ผ๋ก ๋ค๋ฅธ ์ก์
์ ์คํํ๋ ๊ฒ
๋ฑ๋ ์ฌ์ด๋ ์ดํํธ์ด๋ค.์ด๋ฉ์ผ, ๋น๋ฐ๋ฒํธ ํ๋์ ํค ์
๋ ฅ์ ๋ํ ์๋ต์ผ๋ก ํด๋น ํผ์ ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ณ ์
๋ฐ์ดํธํ๋ ๊ฒ
๋ ์ฌ์ฉ์ ์
๋ ฅ ๋ฐ์ดํฐ์ ์ฌ์ด๋ ์ดํํธ์ด๋ค.useEffect๋ ๋ฌด์ธ๊ฐ(๋ก๋๋๋ ์ปดํฌ๋ํธ/์ ๋ฐ์ดํธ ๋๋ ์ด๋ฉ์ผ ์ฃผ์ ๋ฑ)์ ๋ํ ์๋ต์ผ๋ก ์คํ๋๋ ์ฝ๋๋ฅผ ๋ค๋ฃจ๋๋ฐ ๋์์ด ๋๋ค.
์ฆ, ๐ ์ด๋ค ์ก์ ์ ๋ํ ์๋ต์ผ๋ก ์คํ๋๋ ์ก์ ์ ์ฌ์ด๋ ์ดํํธ์ด๊ธฐ ๋๋ฌธ์ useEffect๋ฅผ ์ฌ์ฉํ๋ฉด ์ ์ ํ ์ฝ๋๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ค.
๋ํ๋์์๋ effect ํจ์์์ ์ฌ์ฉํ๋ "๋ชจ๋ ๊ฒ"(๋ชจ๋ ์ํ ๋ณ์์ ํจ์)์ ์ถ๊ฐํด์ผ ํ๋ค.
ํ์ง๋ง ๋ช ๊ฐ์ง ์์ธ๊ฐ ์๋ค.
์ํ ์
๋ฐ์ดํธ ๊ธฐ๋ฅ setState
๋ ๋ํ๋์๋ก ์ถ๊ฐํ ํ์๊ฐ ์๋ค.
React๋ ํด๋น ํจ์๊ฐ ์ ๋ ๋ณ๊ฒฝ๋์ง ์๋๋ก ๋ณด์ฅํ๋ค.
"๋ด์ฅ" API ๋๋ ํจ์๋ฅผ ๋ํ๋์๋ก ์ถ๊ฐํ ํ์๊ฐ ์๋ค.
fetch()
, localStorage
์ ๊ฐ์ด ๋ธ๋ผ์ฐ์ ์ ๋ด์ฅ๋ ํจ์ ๋ฐ ๊ธฐ๋ฅ์ ์ ์ญ์ ์ผ๋ก ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ฐ, ์ด๋ฌํ ๋ธ๋ผ์ฐ์ API/์ ์ญ ๊ธฐ๋ฅ์ React ๊ตฌ์ฑ ์์ ๋ ๋๋ง ์ฃผ๊ธฐ์ ๊ด๋ จ์ด ์์ผ๋ฉฐ ๋ณ๊ฒฝ๋์ง ์๋๋ค.
์ปดํฌ๋ํธ ์ธ๋ถ์์ ์ ์๋ ๋ณ์๋ ํจ์(์: ๋ณ๋์ ํ์ผ์ ์ ํฌํผ ํจ์๋ฅผ ๋ง๋ ๊ฒฝ์ฐ)๋ ๋ํ๋์๋ก ์ถ๊ฐํ ํ์๊ฐ ์๋ค.
ํจ์ ๋๋ ๋ณ์๊ฐ ์ปดํฌ๋ํธ ํจ์ ๋ด๋ถ์์ ์์ฑ๋์ง ์๊ธฐ ๋๋ฌธ์ ๋ณ๊ฒฝํด๋ ์ปดํฌ๋ํธ์ ์ํฅ์ ์ฃผ์ง ์๋๋ค. ํด๋น ๋ณ์๊ฐ ๋ณ๊ฒฝ๋๋ ๊ฒฝ์ฐ, ๋๋ ๊ทธ ๋ฐ๋์ ๊ฒฝ์ฐ์๋ ์ปดํฌ๋ํธ๋ ์ฌํ๊ฐ๋์ง ์๋๋ค.
๊ฐ๋จํ ๋งํด์ effect ํจ์์์ ์ฌ์ฉํ๋ ๋ชจ๋ "๊ฒ๋ค"์ ๋ํ๋์๋ก ์ถ๊ฐํด์ผ ํ์ง๋ง, ์ปดํฌ๋ํธ(๋๋ ์ผ๋ถ ์์ ์ปดํฌ๋ํธ)๊ฐ ๋ค์ ๋ ๋๋ง ๋์ด ์ด๋ฌํ "๊ฒ๋ค"์ด ๋ณ๊ฒฝ๋ ์ ์๋ ๊ฒฝ์ฐ์๋ง ๋ํ๋์๋ก ์ถ๊ฐํ๋ฉด ๋๋ค.
์ฆ, ์ปดํฌ๋ํธ ํจ์์ ์ ์๋ ๋ณ์๋ ์ํ, ์ปดํฌ๋ํธ ํจ์์ ์ ์๋ props ๋๋ ํจ์๋ฅผ ๋ํ๋์๋ก ์ถ๊ฐํ๋ฉด ๋๋ค.
import { useEffect, useState } from 'react';
let myTimer;
const MyComponent = (props) => {
const [timerIsActive, setTimerIsActive] = useState(false);
const { timerDuration } = props; // using destructuring to pull out specific props values
useEffect(() => {
if (!timerIsActive) {
setTimerIsActive(true);
myTimer = setTimeout(() => {
setTimerIsActive(false);
}, timerDuration);
}
}, [timerIsActive, timerDuration]);
};
์ ์ฝ๋์์
timerIsActive
๋ โ
์ข
์์ฑ์ผ๋ก ์ถ๊ฐ๋์๋ค.
์๋ํ๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ณ๊ฒฝ๋ ๋ ๋ณ๊ฒฝ๋ ์ ์๋ ์ปดํฌ๋ํธ์ state ๊ฐ์ด๊ธฐ ๋๋ฌธ์ด๋ค(์: ์ํ๊ฐ ์
๋ฐ์ดํธ๋์๊ธฐ ๋๋ฌธ์).
timerDuration
์ โ
์ข
์์ฑ์ผ๋ก ์ถ๊ฐ๋์๋ค.
์๋ํ๋ฉด ํด๋น ์ปดํฌ๋ํธ์ prop ๊ฐ์ด๊ธฐ ๋๋ฌธ์ด๋ค. ์์ ์ปดํฌ๋ํธ๊ฐ ํด๋น prop ๊ฐ์ ๋ณ๊ฒฝํ๋ฉด ํ์ ํ์ ์ปดํฌ๋ํธ๋ ๋ณ๊ฒฝ๋ ์ ์๋ค. ๊ทธ๋ฌ๋ฉด MyComponent ์ปดํฌ๋ํธ๋ ๋ค์ ๋ ๋๋ง ๋๋ค.
setTimerIsActive
๋ โ ์ข
์์ฑ์ผ๋ก ์ถ๊ฐ๋์ง ์๋๋ค.
์๋ํ๋ฉด ์์ธ ์กฐ๊ฑด์ด๊ธฐ ๋๋ฌธ์ด๋ค. setState์ ๋ํด React๊ฐ ์ ๋ ๋ณ๊ฒฝ๋์ง ์์์ ๋ณด์ฅํ๋ฏ๋ก ์ถ๊ฐํ ํ์๊ฐ ์๋ค.
myTimer
๋ โ ์ข
์์ฑ์ผ๋ก ์ถ๊ฐ๋์ง ์๋๋ค.
์๋ํ๋ฉด ์ปดํฌ๋ํธ ์ธ๋ถ ๋ณ์ ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
myTimer๋ ์ปดํฌ๋ํธ ์ธ๋ถ์์ let์ผ๋ก ์ ์๋์๊ณ ์ด๋์์๋ ๋ณ๊ฒฝํ ์ ์๊ธฐ ๋๋ฌธ์, ์ปดํฌ๋ํธ ๋ด๋ถ ๋ณ์(์ํ๋ prop)์ฒ๋ผ MyComponent ์ปดํฌ๋ํธ๊ฐ ๋ค์ ํ๊ฐ๋๋๋ก ํ๋ ์์๋ ์๋๋ค.
setTimeout
์ โ ์ข
์์ฑ์ผ๋ก ์ถ๊ฐ๋์ง ์๋๋ค.
์๋ํ๋ฉด ๋ธ๋ผ์ฐ์ ๋ด์ฅ API์ด๋ฏ๋ก React ๋ฐ ์ปดํฌ๋ํธ์ ๋
๋ฆฝ์ ์ด๊ธฐ ๋๋ฌธ์ ๋ณ๊ฒฝ๋์ง ์๋๋ค.
๊ฐ๋ ํด๋ฆฐ์ ์์ ์ด ํ์ํ ์ดํํธ๋ ์๋ค.
์ฌ์ฉ์๊ฐ ์ด๋ฉ์ผ์ ์ ๋ ฅํ๊ธฐ ์ํด ํ์ดํ์ ํ๊ณ ์๋ค๋ฉด, event.target.value๊ฐ ๊ณ์ ์ ๋ ฅ๋์ด state๊ฐ ๋ณ๊ฒฝ๋๊ณ ์๋๋ฐ ์ด๋ ํ์ ์ด์์ผ๋ก ํธ๋ํฝ์ ๋ฐ์์ํจ๋ค. ์ด๋ฐ ๊ฒฝ์ฐ, ์ฌ์ฉ์๊ฐ ํ์ดํ์ ๋ฉ์ถ ๋๋ฅผ ๊ธฐ๋ค๋ฆฐ ํ, ์ ๋ ฅ์ด ๋๋๋ฉด ์ ํจํ์ง ํ์ธํ์ฌ ๋ถํ์ํ ํธ๋ํฝ์ ์ ๊ฑฐํ ์ ์๋ค.
์ฌ์ฉ์๊ฐ ์ด๋ฉ์ผ/๋น๋ฐ๋ฒํธ ํค๋ฅผ ์
๋ ฅ ํ ํ, ์๋ฅผ ๋ค๋ฉด 500๋ฐ๋ฆฌ์ด ์ด์ ๋ฉ์ถ๋ฉด ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์คํํด๋ณด์. ์ด๋ฐ ๊ธฐ์ ์ ๋๋ฐ์ด์ฑ
์ด๋ผ๊ณ ํ๋ค. ์ฌ์ฉ์ ์
๋ ฅ์ ๋๋ฐ์ด์ค(๊ทธ๋ฃนํ)ํ๋ ๊ฒ์ด๋ค. ์ฌ์ฉ์๊ฐ ํ์ดํ์ ์ผ์ ์ค์งํ์ ๋ ๋๋ฐ์ด์ฑํ๋ ๊ฒ์ธ๋ฐ ์ด๋ ๋ธ๋ผ์ฐ์ ๋ด์ฅํจ์์ธ setTimeout
์ useEffect ๋ด๋ถ์ ์ฌ์ฉํ์ฌ ๊ตฌํํ ์ ์๋ค.
const Login = (props) => {
//...
useEffect(() => {
//500๋ฐ๋ฆฌ์ด ํ ์ ํจ์ฑ ๊ฒ์ฌํ๊ธฐ
setTimeout(() => {
console.log("์ ํจ์ฑ ๊ฒ์ฌ ใ
ใ
");
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
}, 500);
}, [enteredEmail, enteredPassword]);
//...
ํ์ฌ ๋ชจ๋ ํค ์ ๋ ฅ์ ๋ํด 5ms ํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ๊ณ ์๋ค. ํค ์ ๋ ฅ์ด ๋๋ ํ ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ํ ๋ฒ๋ง ์คํ๋ ์ ์๋๋ก, ์ฌ์ฉ์๊ฐ ๊ณ์ ํค๋ฅผ ์ ๋ ฅ์ค์ธ ๊ฒฝ์ฐ ์ฆ, ๋ค์ ํค๊ฐ ์ ๋ ฅ๋๋ฉด setTimeout์ด ์คํ๋์ง ์๋๋ก ์ง์์ผ ํ๋ค. ์ฆ, ์ฌ์ฉ์๊ฐ ํค๋ฅผ ๊ณ์ ์ ๋ ฅ ์ค์ธ ๊ฒฝ์ฐ setTimeout ํ์ด๋จธ๋ ๊ณ์ ์ง์์ ธ์ผ ํ๋ค. ์ด๋ด ๋ Clean Up ํ์ ์ ์ฌ์ฉํ๋ฉด ๋๋ค.
useEffect()๋ ์ฒซ ๋ฒ์งธ ์ธ์์์ return ๊ฐ
์ ๊ฐ์ง ์ ์๋๋ฐ, ์ต๋ช
์ ํจ์๋ฅผ ๋ฐํํ๋ ๊ฒ์ ํด๋ฆฐ์
ํจ์๋ผ๊ณ ํ๋ค.
useEffect(() => {
//500๋ฐ๋ฆฌ์ด ํ ์ ํจ์ฑ ๊ฒ์ฌํ๊ธฐ
//ํ์ด๋จธ๋ฅผ identifier ์์์ ํ ๋น
const identifier = setTimeout(() => {
console.log("์ ํจ์ฑ ๊ฒ์ฌ ใ
ใ
");
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
}, 500);
//๐ฅ ํด๋ฆฐ์
ํ์
๋ฐํ
return () => {
console.log("ํด๋ฆฐ์
");
//ํ์ด๋จธ ์ง์ฐ๊ธฐ
//clearTimeout ๋ด์ฅํจ์๋ก identifier ํ์ด๋จธ ์ญ์
clearTimeout(identifier);
};
}, [enteredEmail, enteredPassword]);
useEffectํจ์๊ฐ ์คํ๋ ๋๋ง๋ค ์คํ๋๋ค.
์ฒ์ ์คํ๋๋ ๊ฒฝ์ฐ ์ ์ธํ๊ณ ์คํ๋ ๋ ๋ง๋ค ํด๋ฆฐ์
ํจ์๊ฐ ๋จผ์ ์คํ๋๋ค.
์ดํํธ๋ก ํน์ ๋ ์ปดํฌ๋ํธ๊ฐ DOM์์ ๋ง์ดํธ ํด์ ๋ ๋๋ง๋ค ์คํ๋๋ค.
์ฆ, ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฌ์ฉ๋ ๋๋ง๋ค ์คํ๋๋ค.
ํด๋ฆฐ์ ํ์ ์ ๋ชจ๋ ์๋ก์ด ์ฌ์ด๋์ดํํธ ํจ์๊ฐ ์คํ๋๊ธฐ ์ ์, ๊ทธ๋ฆฌ๊ณ ์ปดํฌ๋ํธ๊ฐ ์ ๊ฑฐ๋๊ธฐ ์ ์ ์คํ๋๋ค.
์ฆ, ํด๋ฆฐ์ ํ์ ์ด ์คํ๋๋ ์์๋
"์ ํจ์ฑ ๊ฒ์ฌ"
"ํด๋ฆฐ์
"
> "์ ํจ์ฑ ๊ฒ์ฌ"
"ํด๋ฆฐ์
"
> "์ ํจ์ฑ ๊ฒ์ฌ"
์ด๋ฐ์์ผ๋ก ์๋ํ๋ค.