[React] ๋ฆฌ์กํธ ์ฝ๋๋ฅผ ๊ฐ์ ํ ์ ์๋ 4๊ฐ์ง ํ
TLDR
- ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ปค๋ง ํ์ฉ
- ์ปดํฌ๋ํธ ์ฑ ์ ๋ถ๋ฆฌ
- ์กฐ๊ฑด๋ฌธ ๋์ ๊ฐ์ฒด map ์ฌ์ฉ
- React ๋ผ์ดํ์ฌ์ดํด ์ธ๋ถ์ ๋ ๋ฆฝ์ ์ธ ๋ณ์ ๋ฐฐ์น
4 React Tips
1. ์ปค๋ง ํ์ฉ
user
์ํ๋ name
, surname
, address
3๊ฐ ์์ฑ์ ๊ฐ์ง๋ฉฐ, ์ด์ ๋์ํ๋ 3๊ฐ์ input ํ๋๊ฐ ํ์ํ๋ค. ์๋ ์ฝ๋์์ ๊ฐ ํ๋๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ๋ณ๋์ ํธ๋ค๋ฌ๋ฅผ ๊ฐ๊ฐ ๋ง๋ค์ด์ ์ฌ์ฉํ๊ณ ์์ง๋ง, ์ด ํธ๋ค๋ฌ๋ค์ value๊ฐ ํ ๋ฌ๋ ์์ฑ ์ด๋ฆ๋ง ๋ค๋ฅผ ๋ฟ ๋๋จธ์ง ๋ก์ง์ด ๋์ผํ๊ธฐ ๋๋ฌธ์ ์ฝ๋ ์ค๋ณต์ด ๋ฐ์ํ๊ณ ์๋ค.
export default function App() {
const [user, setUser] = useState({
name: "",
surname: "",
address: "",
});
const handleNameChange = (e) => {
setUser((prev) => ({
...prev,
name: e.target.value,
}));
};
const handleSurnameChange = (e) => {
setUser((prev) => ({
...prev,
surname: e.target.value,
}));
};
const handleAddressChange = (e) => {
setUser((prev) => ({
...prev,
address: e.target.value,
}));
};
return (
<>
<input value={user.name} onChange={handleNameChange} />
<input value={user.surname} onChange={handleSurnameChange} />
<input value={user.address} onChange={handleAddressChange} />
</>
);
}
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์์ ์์ฃผ ์ฌ์ฉํ๋ ์ปค๋ง์ n๊ฐ์ ์ธ์๋ฅผ ๋ฐ๋ ๋์ n๊ฐ์ ํจ์๋ฅผ ๋ง๋ค์ด์ ํ๋์ ์ธ์๋ง ๋ฐ๋๋ก ๋ง๋๋ ํจํด์ด๋ค.
currying is the technique of translating a function that takes multiple arguments into a sequence of families of functions, each taking a single argument.
์ปค๋ง ํจํด์ ์ ์ฉํ์ฌ ๊ธฐ์กด ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๊ฐ ์์ฑ์ ๋์ํ๋ ํจ์๋ฅผ ๋ฐํํ๋๋ก ๋ง๋ค๋ฉด, ํน์ ํ๋์ ๋ง๋ ํธ๋ค๋ฌ๋ฅผ ๋์ ์ผ๋ก ์์ฑํ ์ ์์ด ์ค๋ณต ์ฝ๋๋ฅผ ์ค์ผ ์ ์๋ค.
export default function App() {
const [user, setUser] = useState({
name: "",
surname: "",
address: "",
});
const handleInputChange = (field) => {
// ๋ฐํ๋ ํธ๋ค๋ฌ
return (e) => {
setUser((prev) => ({
...prev,
[field]: e.target.value,
}));
};
};
return (
<>
<input value={user.name} onChange={handleInputChange("name")} />
<input value={user.surname} onChange={handleInputChange("surname")} />
<input value={user.address} onChange={handleInputChange("address")} />
{JSON.stringify(user)}
</>
);
}
2. ์ฑ ์ ๋ถ๋ฆฌ
1๊ฐ ์ปดํฌ๋ํธ์ ๋๋ฌด ๋ง์ ์ฝ๋ ๋ผ์ธ์ ํฌํจํ๋ฉด ์ ์ง๋ณด์ํ๊ธฐ ํ๋ค๊ณ ์ดํดํ๊ธฐ๋ ์ฝ์ง ์๋ค. ์ด๋ฌํ ์ปดํฌ๋ํธ๋ ์๋์ ๊ฐ์ ๊ธฐ์ค์ ๊ฐ์ง๊ณ ๋ ๋ฆฝ์ ์ธ ํ์ ๋ชจ๋๋ก ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
- UI ๋ชจ๋ : ์๊ฐ์ ์ธ ํ๋ฉด๋ง ๋ด๋น
- Logic/Model ๋ชจ๋: ๋น์ฆ๋์ค ๋ก์ง๋ง ํฌํจ e.g., ์ปค์คํ ํ
- Lib ๋ชจ๋: ์ปดํฌ๋ํธ์ ํ์ํ ์ ํธ๋ฆฌํฐ ๋ชจ์
์๋ ListComponent
๋ ๋ก์ปฌ ์ํ ๊ด๋ฆฌ, ๋ฐ์ดํฐ ํจ์นญ, ์์ดํ
์ญ์ ๋ฑ ๋ค์ํ ๋ก์ง์ด ํผ์ฌ๋์ด ์๋ค. ์ด ์ปดํฌ๋ํธ์ ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ์ฌ ๊ฐ์ ํด๋ณด์.
export function ListComponent() {
// ๋ก์ปฌ ์ํ
const [list, setList] = useState([]);
// ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ํ ๋ก์ปฌ ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ ํจ์
const fetchList = async () => {
try {
const resp = await fetch("https://www.url.com/list");
const data = await resp.json();
setList(data);
} catch {
showAlert({ text: "Something went wrong!" });
}
};
// ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ง์ดํธ ๋ ๋ fetch
useEffect(() => {
fetchList();
}, []);
// ์์ดํ
์ญ์ ๋ฐ ์ํ ์
๋ฐ์ดํธ ํจ์
const handleDeleteItem = (id) => {
return () => {
try {
fetch(`https://www.url.com/list/${id}`, {
method: "DELETE",
});
setList((prev) => prev.filter((x) => x.id !== id));
} catch {
showAlert({ text: "Something went wrong!" });
}
};
};
// ๋ฆฌ์คํธ ๋ทฐ ๋ ๋๋ง
return (
<div className="list-component">
{list.map(({ id, name }) => (
<div key={id} className="list-component__item>">
{/* 30์๋ฅผ ์ด๊ณผํ๋ ์ด๋ฆ์ ... ์๋ต ๋ถํธ ์ถ๊ฐ */}
{name.slice(0, 30) + (name.length > 30 ? "..." : "")}
<div onClick={handleDeleteItem(id)} className="list-component__icon">
<DeleteIcon />
</div>
</div>
))}
</div>
);
}
๋จผ์ Model๊ณผ UI ๋ชจ๋์์ ์ฌ์ฉํ ์ ํธ๋ฆฌํฐ ํจ์๋ฅผ ๋ถ๋ฆฌํ๋ค.
export async function getList(onSuccess) {
try {
const resp = await fetch("https://www.url.com/list");
const data = await resp.json();
onSuccess(data);
} catch {
showAlert({ text: "Something went wrong!" });
}
}
export async function deleteListItem(id, onSuccess) {
try {
fetch(`https://www.url.com/list/${id}`, {
method: "DELETE",
});
onSuccess();
} catch {
showAlert({ text: "Something went wrong!" });
}
}
export function trimName(name) {
return name.slice(0, 30) + (name.length > 30 ? "..." : "");
}
์ํ ๊ด๋ฆฌ์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ณ๋์ ์ปค์คํ ํ ์ผ๋ก ๋ถ๋ฆฌํ๋ค.
export function useList() {
const [list, setList] = useState([]);
// id๋ฅผ ๋ฐ์ ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ํธ๋ค๋ฌ ๋ฐํ
const handleDeleteItem = useCallback((id) => {
return () => {
deleteListItem(id, () => {
setList((prev) => prev.filter((x) => x.id !== id));
});
};
}, []);
useEffect(() => {
getList(setList);
}, []);
return {
list,
handleDeleteItem,
};
}
๋ฆฌ์คํธ ์์ดํ ์ ๋ณ๋์ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ์ฌ UI ๋ชจ๋์ ์์ฑํ๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๊ฐ ์์ดํ ์ ๋ ๋๋ง ๋ก์ง์ ๋ ๋ฆฝ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋ค.
export function ListComponentItem({ name, onDelete }) {
return (
<div className="list-component__item>">
{trimName(name)}
<div onClick={onDelete} className="list-component__icon">
<DeleteIcon />
</div>
</div>
);
}
์ ํธ๋ฆฌํฐ ํจ์, ์ปค์คํ ํ , UI ์ปดํฌ๋ํธ ๋ฑ์ ๋ชจ๋์ ํตํฉํ๋ค. ์ด๋ก์จ ์ฝ๋์ ์์ง๋(ํ๋์ ๋ชฉ์ ์ ์ํํ๊ธฐ ์ํ ์์ ๊ธฐ๋ฅ๋ค์ ์ฐ๊ด์ฑ์ ๋ฐ๋ผ ๋ชจ๋์ ์ ๋ชจ์๋จ๋์ง ์ฌ๋ถ)๊ฐ ๋์์ง๊ณ ์ ์ง๋ณด์๊ฐ ๋ ์ฉ์ดํด์ก๋ค.
export function ListComponent() {
const { list, handleDeleteItem } = useList();
return (
<div className="list-component">
{list.map(({ id, name }) => (
<ListComponentItem
key={id}
name={name}
onDelete={handleDeleteItem(id)}
/>
))}
</div>
);
}
3. ๊ฐ์ฒด map ์ฌ์ฉ
๋ณ์์ ์ํ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํด์ผ ํ ๋, ์กฐ๊ฑด๋ฌธ์ ์ฌ์ฉํ์ฌ ๊ฐ ์ํ์ ๋ง๋ ์ปดํฌ๋ํธ๋ฅผ ์ ํํ ์ ์๋ค. ํ์ง๋ง ์กฐ๊ฑด๋ฌธ์ด ๋ง์์ง์๋ก ๊ฐ๋ ์ฑ์ด ํ์ ํ๊ฒ ๋จ์ด์ง๋ ๋จ์ ์ด ์๋ค. ๊ฐ์ฒด ๋งต(map)์ ํ์ฉํ๋ฉด ์ฝ๋๋ฅผ ๋์ฑ ๊ฐ๊ฒฐํ๊ณ ์ ์ธ์ ์ผ๋ก ์์ฑํ ์ ์๋ค.
function Account({ type }) {
let Component = UsualAccount;
if (type === "vip") {
Component = VipAccount;
}
if (type === "moderator") {
Component = ModeratorAccount;
}
if (type === "admin") {
Component = AdminAccount;
}
return (
<div className="account">
<Component />
<AccountStatistics />
</div>
);
}
์๋์ฒ๋ผ ๊ฐ์ฒด ๋งต ACCOUNTS_MAP
์ ์ฌ์ฉํ์ฌ type
๊ณผ ์ปดํฌ๋ํธ๋ฅผ ๋งคํํ๋ฉด ์กฐ๊ฑด๋ฌธ ์์ด๋ ํ์ํ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค. type
์ด ๊ฐ์ง๋ ์ํ์ ์ข
๋ฅ๊ฐ ๋ง์์ง ์๋ก ์ด ๋ฐฉ๋ฒ์ด ํนํ ์ ์ฉํ๋ค.
const ACCOUNTS_MAP = {
vip: VipAccount,
usual: UsualAccount,
admin: AdminAccount,
moderator: ModeratorAccount,
};
function Account({ type }) {
const Component = ACCOUNTS_MAP[type];
return (
<div className="account">
<Component />
<AccountStatistics />
</div>
);
}
4. ๋ ๋ฆฝ์ ์ธ ๋ณ์ ๋ฐฐ์น
๋ฆฌ์กํธ์ ๋ผ์ดํ์ฌ์ดํด ๋ฉ์๋๋ฅผ ํ์๋ก ํ์ง ์๋ ๋ก์ง์ ์ปดํฌ๋ํธ์์ ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋ ๋ ๋ถํ์ํ๊ฒ ์ฌ์ ์ธ๋๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์๊ณ , ์์กด์ฑ์ด ๋ ๋ช ํํด์ ธ์ ์ฝ๋ ๊ฐ๋ ์ฑ์ด ์ข์์ง๋ค.
function useItemsList() {
const defaultItems = [1, 2, 3, 4, 5];
const [items, setItems] = useState(defaultItems);
const toggleArrayItem = (arr, val) => {
return arr.includes(val) ? arr.filter((el) => el !== val) : [...arr, val];
};
const handleToggleItem = (num) => {
return () => {
setItems(toggleArrayItem(items, num));
};
};
return {
items,
handleToggleItem,
};
}
defaultItems
๋ณ์์ toggleArrayItem
ํจ์๋ ํน์ ์ปดํฌ๋ํธ์ ์ข
์๋์ง ์๊ธฐ ๋๋ฌธ์ ๋
๋ฆฝ์ ์ธ ์ ํธ๋ฆฌํฐ๋ก ๋ถ๋ฆฌํ ์ ์๋ค. ํ์์ ๋ค๋ฅธ ๊ณณ์์ ์ฝ๊ฒ ์ฌ์ฌ์ฉํ ์๋ ์๋ค.
const DEFAULT_ITEMS = [1, 2, 3, 4, 5];
const toggleArrayItem = (arr, val) => {
return arr.includes(val) ? arr.filter((el) => el !== val) : [...arr, val];
};
function useItemsList() {
const [items, setItems] = useState(DEFAULT_ITEMS);
const handleToggleItem = (num) => {
return () => {
setItems(toggleArrayItem(items, num));
};
};
return {
items,
handleToggleItem,
};
}
๋ ํผ๋ฐ์ค
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ์ต์ ํ ๊ธฐ๋ฒ ๋ชจ์ (23) | 2024.12.07 |
---|---|
[Algorithm] ์ฌ๋ผ์ด๋ฉ ์๋์ฐ Sliding Window ์๊ณ ๋ฆฌ์ฆ ํบ์๋ณด๊ธฐ (2) | 2024.11.11 |
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 2 (1) | 2024.10.13 |
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 1 (0) | 2024.10.05 |
[TS] ํ์ ์คํฌ๋ฆฝํธ ๋ธ๋๋๋ ํ์ (0) | 2024.09.26 |
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ์ต์ ํ ๊ธฐ๋ฒ ๋ชจ์
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ์ต์ ํ ๊ธฐ๋ฒ ๋ชจ์
2024.12.07 -
[Algorithm] ์ฌ๋ผ์ด๋ฉ ์๋์ฐ Sliding Window ์๊ณ ๋ฆฌ์ฆ ํบ์๋ณด๊ธฐ
[Algorithm] ์ฌ๋ผ์ด๋ฉ ์๋์ฐ Sliding Window ์๊ณ ๋ฆฌ์ฆ ํบ์๋ณด๊ธฐ
2024.11.11 -
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 2
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 2
2024.10.13 -
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 1
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 1
2024.10.05