ํ ์คใ ฃSLASH 22 - Effective Component ์ง์ ๊ฐ๋ฅํ ์ฑ์ฅ๊ณผ ์ปดํฌ๋ํธ ์์์ ์ฐธ๊ณ ํ์ฌ ๊ธ์ ์์ฑํ์์ต๋๋ค.
์ ํ์ ํญ์ ๋ฐ๋๋ค.
- ๋ณ๊ฒฝ์ฌํญ์ ์์ฃผ ๋ฐ์ํ๋ค
- ์ข์ ์ ํ์ ์ํด์๋ ๋ณ๊ฒฝ์ ๋ฐ๋์ ํ์ํ๋ค
- ๋ณ๊ฒฝ์ ์์ธก ๋ถ๊ฐ๋ฅํ๋ค
๋ณ๊ฒฝ์ ๋์ํ๊ธฐ
Item์ด๋ผ๋ ์ปดํฌ๋ํธ ์ด๋ฆ๋ง ๋ณด๊ณ ๋ ์ปดํฌ๋ํธ๊ฐ ์ด๋ค ์ญํ ์ ํ๋์ง, ์ด๋ป๊ฒ ๋ณด์ฌ์ง๋์ง ์ ์ ์๋ค.
๋ณ๊ฒฝ์ ์ ์ฐํ๊ฒ ๋์ํ๋๋ก ์ปดํฌ๋ํธ ๋ง๋ค๊ธฐ
์ปดํฌ๋ํธ?
์ปดํฌ๋ํธ๋ 3๊ฐ์ง ํน์ง์ ๊ฐ๋๋ค.
1. ์ปดํฌ๋ํธ๋ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ค
์ปดํฌ๋ํธ๋ ๋๊ฐ์ง ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ค.
- ์ธ๋ถ์์ ์ฃผ์ ๋ฐ์ ๋ฐ์ดํฐ
- ๋ด๋ถ ๋ฐ์ดํฐ(์ํ)
2. ์ปดํฌ๋ํธ๋ UI๋ฅผ ๋ณด์ฌ์ค๋ค.
์ปดํฌ๋ํธ๋ ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ๋ณด์ฌ์ค ๊ฒ์ธ์ง๋ฅผ ๊ฒฐ์ ํ๋ค.
3. ์ปดํฌ๋ํธ๋ ์ฌ์ฉ์์ ์ํธ์์ฉํ๋ค.
Headless ๊ธฐ๋ฐ์ ์ถ์ํํ๊ธฐ: ๋ณํ๋ ๊ฒ vs ์๋์ ์ผ๋ก ๋ณํ์ง ์๋ ๊ฒ
Headless?
UI๋ฅผ ๊ด์ฌ์ฌ์์ ์ ์ธํ์ฌ ์ค๋ก์ง ๋ฐ์ดํฐ์๋ง ์ง์คํด์ ๋ชจ๋ํํ๋ ํจํด
Headless๋ ํ๊ฐ์ง ๋ฌธ์ ์๋ง ์ง์คํ๊ธฐ ๋๋ฌธ์ ๋ ๋ง์ ๊ณณ์์ ์ฌ์ฉํ ์ ์๊ณ ๋ณํ์ ์ ์ฐํ๊ฒ ๋์ํ ์ ์๋ค.
๋ฐ์ดํฐ ์ถ์ํ
UI๋ ๋์์ธ์ ์์กดํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ดํฐ์ UI๋ ๋ถ๋ฆฌํ ํ์๊ฐ ์๋ค. ์ฆ, UI๋ฅผ ๊ด์ฌ์ฌ์์ ์ ์ธํด ๋ฐ์ดํฐ์๋ง ์ง์คํ๋ค.
์บ๋ฆฐ๋ ์ปดํฌ๋ํธ์ ๋ฐ์ดํฐ๋ฅผ ์ถ์ํํด๋ณด์.
function Calendar() {
const { headers, body, view } = useCalendar();
return (
<Table>
<Thead>
<Tr>
{headers.weekDays.map(({ key, value }) =>
<Th key={key}>{format(value, 'E', { locale })}</Th>
</Tr>
</Thead>
<TBody>
{body.value.map(({ key, value: days }) => (
<Tr key={key}>
{days.map(({ key, value }) => (
<Td key={key}{getDate(value)}</Td>
))}
</Tr>
))}
</Tbody>
</Table>
)
}
๋ฌ๋ ฅ์ ๊ตฌ์ฑํ๋๋ฐ ํ์ํ ๋ฐ์ดํฐ๋ฅผ useCalendar
์ ์์ํ์๋ค.
๋์์ธ์ด ๋ค๋ฅด๋๋ผ๋ useCalendar
hooks๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ค์ ํ๋ก์ ํธ์์ ๋ฐ์ดํฐ ์ถ์ํํ๊ธฐ
ํ์ฌ ํ๋ก์ ํธ์์ ๋ก๊ทธ์ธ, ํ์๊ฐ์ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ๋ฐํ๊ณ ์๋ค.
์๊ตฌ์ฌํญ
- ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๋๋ง๋ค ๊ฐ์ ๊ฒ์ฌํ์ฌ ์๋ฌ๋ฉ์์ง๋ฅผ ๋ณด์ฌ์ค๋ค.
- ๋ชจ๋ ๊ฐ์ด ์ ํจํ๋ฉด ๋ฒํผ์ด ํ์ฑํ๋๋ค.
๋ฐ์ดํฐ ์ถ์ํ
์๊ตฌ์ฌํญ์ ๋ฐ๋ฅด๋ฉด ๋ชจ๋ input์ ๋๊ฐ์ง ๋ฐ์ดํฐ๋ฅผ ๊ฐ๋๋ค.
- value: ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฐ
- error: value๋ฅผ ๊ฒ์ฌํ ๊ฒฐ๊ณผ
useInput์ด๋ผ๋ ์ปค์คํ ํ ์ผ๋ก input ๋ฐ์ดํฐ๋ฅผ ์ถ์ํํ ์ ์๋ค.
import { useState } from 'react';
export const useInput = () => {
const [value, setValue] = useState('');
const [error, setError] = useState(false);
return { value, setValue, error, setError };
};
useInput์ ์ฌ์ฉํ์ฌ ๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ๊ตฌ์ฑํด๋ณด์.
function Login() {
const username = useInput();
const password = useInput();
const inputList = [
{
id: 'username',
label: '์์ด๋',
valid() {
return this.value.length >= 5;
},
...username,
},
{
id: 'password',
label: '๋น๋ฐ๋ฒํธ',
valid() {
return this.value.length >= 8;
},
...password,
},
];
...
return (
<>
<Heading className="text-2xl font-bold">{PAGE['LOGIN']}</Heading>
<AuthForm mode="login" inputList={inputList} onSubmit={handleFormSubmit} />
{isError && <ErrorMessage className="mt-4">{LOGIN_ERROR.LOGIN_FAILED}</ErrorMessage>}
<>
);
}
Login ์ปดํฌ๋ํธ๋ ๋๊ฐ์ง ๋ฌธ์ ์ ์ ๊ฐ๊ณ ์๋ค.
๋ฌธ์ ์
1. ๋ก๊ทธ์ธ ํ์ด์ง๋ input์ด 2๊ฐ ๋ฐ์ ์๋๋ฐ๋ inputList๋ฅผ ํ์ ํ๊ธฐ ์ด๋ ต๋ค.
- ํ์๊ฐ์ ์ ๋ก๊ทธ์ธ ํ์ด์ง๋ณด๋ค input์ด 3๊ฐ ๋ ๋ง๋ค. ์ฝ๋๊ฐ ํจ์ฌ ๊ธธ์ด์ง๊ณ ํ์ ํ๊ธฐ ์ด๋ ค์์ง ๊ฒ์ด๋ค.
- ์ ์ ๊ธธ์ด์ง๋ inputList๋ ์ปดํฌ๋ํธ๋ฅผ ํ์ ํ๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ ๋ค.
2. setState๊ฐ ๋น๋๊ธฐ์ ์ผ๋ก ๋์ํ๊ธฐ ๋๋ฌธ์ valid ๋ฉ์๋๊ฐ ๋ฐ๋ก ์ง์ ๊ฐ์ ๊ฒ์ฌํ๋ค.
- valid๋ผ๋ ๋ฉ์๋๋ก state์ ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ณ ์๋ค.
- setState๋ก ๊ฐ์ ๋ณ๊ฒฝํ๋ฉด ์ฆ๊ฐ์ ์ผ๋ก state์ ๋ฐ์๋์ง ์๋๋ค (์ด ๋ถ๋ถ์ด ์ดํด๊ฐ ์๊ฐ๋ฉด ์ด ๋ฌธ์๋ฅผ ์ฐธ๊ณ )
- ๋ฐ๋ผ์, ํ์ฌ state ๊ฐ์ผ๋ก ๊ฒ์ฌ๋ฅผ ํ๋ ๊ฒ์ด ์๋๋ผ ์ง์ ์ ๊ฐ์ ๊ฒ์ฌํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์ํ๋๋๋ก ๋์ํ๊ณ ์์ง ์๋ค.
๋ค์ ๋ฐ์ดํฐ ์ถ์ํํ๊ธฐ
input์ ๋ํด์ ๋ค์ ์๊ฐํด๋ณด๋ฉด
export const useInput = () => {
const [value, setValue] = useState('');
const [error, setError] = useState(false);
const handleInputChange = ({target: { id, value }}: { target: HTMLInputElement; }) => {
setValue(value);
setError(!REGEXP[id].test(value));
};
return { value, error, handleInputChange };
};
๊ฒฐ๊ณผ
UI๋ฅผ ๊ทธ๋ฆฌ๋๋ฐ ํ์ํ ๋ชจ๋ inputList๋ฅผ useAuthForm์ ์์ํด์ ์ฝ๋์ ๊ฐ๋ ์ฑ์ ๋์ผ ์ ์์๋ค.