- node.js
- npm
- github์์ ํ๋ก์ ํธ๋ฅผ ํด๋ก ํ๋ค.
$ git clone https://github.com/sangbooom/AssignmentTest
- ํ๋ก์ ํธ ๋๋ ํ ๋ฆฌ์ ๋ค์ด๊ฐ๋ค.
$ cd AssignmentTest
- ์์กด์ฑ ๋ชจ๋์ ์ค์นํ๋ค.
$ npm install
- ์คํํ๋ค.
$ npm run start
- react (typescript, ํจ์ํ ์ปดํฌ๋ํธ)
- emotion.js
- redux
- axios
์ฒ์์ store์์ initialState๋ฅผ ๊ฐ์ ธ์จ๋ค.index.tsx
๊ทธ ํ์ ProblemLayout๊ณผ SimilarLayout์ ๋ ๋๋งํ๋ค. App.tsx
์์ฑํ API ์์ฒญ์ ๋น๋๊ธฐ ํต์ ์ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋๋ค.ProblemLayout/index.tsx
, SimilarLayout/index.tsx
๊ฐ๊ฐ Layout์์ axios๋ก ๋ฐ์์จ json ๋ฐ์ดํฐ๋ฅผ dispatch๋ฅผ ํตํด store์ ๊ฐฑ์ ํ๋ค.
๊ฐฑ์ ๋๋ฉด useSelector๋ก store์ ๊ฐ๋ค์ ์ง์ผ๋ณด๊ณ ์๋ ์ปดํฌ๋ํธ๋ค์ด ๋ฆฌ๋ ๋๋ง ๋๊ณ ๊ฐ์ด ๊ฐฑ์ ๋๋ค.
Layout๋ค์ด ๋ค์ ๊ฐฑ์ ๋๊ณ list - listItem ์์ผ๋ก ๋ ๋๋ง์ด ๋๋ค.
์ ์ฌ๋ฌธํญ ๋ฒํผ์ ๋๋ ์ ๋ onClickSimilarCardButton์ด ์คํ๋๋ค.
const onClickSimilarCardButton = useCallback(() => {
dispatch(changeValue({ key: "isButtonClicked", value: true }));
dispatch(changeValue({ key: "activeIndex", value: targetIndex }));
}, [dispatch, targetIndex]);
targetIndex๋ List์ปดํฌ๋ํธ์์ map์ผ๋ก ๋ฐ์์จ index๊ฐ์ index-1๋ก ์ด๊ธฐํํ๊ณ , ๋ด๊ฐ ๋๋ฅธ ๋ฒํผ์ index๋ฅผ ์ ์ฅํ ๋ณ์์ด๋ค.
dispatch๋ฅผ ํตํด isButtonClicked๋ฅผ true๋ก ๋ฐ๊ฟ์ฃผ๊ณ , SimilarLayout์ useSelector๊ฐ ๊ฐ์ง๋ฅผ ํ๊ณ ๋ฐ๋ ๊ฐ์ผ๋ก ๋ฆฌ๋ ๋๋ง์ ํ๋ค.
activeIndex๋ ํ์ฌ active ๋์ด์๋ index๋ฅผ ๋ํ๋ธ๋ค.
์ญ์ ๋ฒํผ์ ๋๋ ์ ๋ onClickDeleteButton์ด ์คํ๋๋ค.
const onClickDeleteButton = useCallback(() => {
dispatch(deleteProblem(targetIndex));
if (activeIndex === targetIndex) {
dispatch(changeValue({ key: "isButtonClicked", value: false }));
dispatch(changeValue({ key: "activeIndex", value: -1 }));
} else if (activeIndex > targetIndex) {
dispatch(changeValue({ key: "activeIndex", value: activeIndex - 1 }));
}
}, [dispatch, activeIndex, targetIndex]);
์ญ์ ๋ฅผ ๋๋ฅธ index๋ฅผ dispatch๋ก ๋๊ธฐ๊ณ reducer์์ targetIndex์ ํด๋นํ๋ ๋ฌธ์ ๋ฅผ ์ญ์ ํ๋ค.
๋ง์ฝ, activeIndex(ํ์ฌ active ๋์ด์๋ index)์ targetIndex(ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ ๋ฒํผ์ index)๊ฐ ๊ฐ๋ค๋ฉด activeIndex๋ฅผ ์ด๊ธฐํ ์ํค๊ณ ์ ์ฌ๋ฌธํญ ๋ฆฌ์คํธ๋ฅผ ๋ณด์ฌ์ฃผ์ง ์์ผ๋ฉด ๋๋ค.
๊ทธ๋ฆฌ๊ณ activeIndex๊ฐ targetIndex๋ณด๋ค ํฌ๋ค๋ฉด ์ญ์ ๋ฅผ ํ์ ๋ index๊ฐ ํ์นธ์ฉ ๋ด๋ ค์ค๊ธฐ ๋๋ฌธ์ activeIndex์ -1์ ํด์ฃผ์๋ค.
์ถ๊ฐ ๋ฒํผ์ ๋๋ ์ ๋ onClickAddButton์ด ์คํ๋๋ค.
const onClickAddButton = useCallback(() => {
dispatch(addProblem(targetIndex));
}, [dispatch, targetIndex]);
ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ ๋ฒํผ์ index๋ฅผ dispatch๋ฅผ ํตํด store์ reducer์ ๋๊ฒจ์ฃผ๊ณ ์๋ ๋ฐฉ์์ ์คํ์์ผ ๋ฐ๊ฟ์ค๋ค.
// reducer
switch (action.type) {
...
case ADD_PROBLEM:
draft.problemData.splice(
draft.activeIndex + 1,
0,
draft.similarData[action.data]
);
// ํ์ต์ง ๋ฆฌ์คํธ์ ํ์ฌ active ๋์ด์๋ index + 1์ ์ ์ฌ๋ฌธ์ ๋ฆฌ์คํธ[์ถ๊ฐ ๋ฒํผ์ ๋๋ฅธ index]๋ฅผ ์ถ๊ฐ
draft.similarData.splice(action.data, 1);
// ์ ์ฌ๋ฌธ์ ๋ฆฌ์คํธ[์ถ๊ฐ ๋ฒํผ์ ๋๋ฅธ index] ์ญ์
break;
...
}
๋ง์ฐฌ๊ฐ์ง๋ก ProblemLayout/index.tsx
์ SimilarLayout/index.tsx
์์ ๊ฐ๊ฐ problemData์ similarData ๊ฐ์ด ๋ณ๊ฒฝ ๋ ๊ฒ์ ๊ฐ์งํ๊ณ ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋๋ง ํ๋ค.
๊ต์ฒด ๋ฒํผ์ ๋๋ ์ ๋ onClickSwapButton์ด ์คํ๋๋ค.
const onClickSwapButton = useCallback(() => {
dispatch(swapSimilar(targetIndex));
}, [dispatch, targetIndex]);
ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ ๋ฒํผ์ index๋ฅผ dispatch๋ฅผ ํตํด store์ reducer์ ๋๊ฒจ์ฃผ๊ณ ์๋ ๋ฐฉ์์ ์คํ์์ผ ๋ฐ๊ฟ์ค๋ค.
๊ต์ฒด ๋ฒํผ์ ๋๋ฅธ ๋ฌธ์ ๋ฅผ ํ์ต์ง ๋ฆฌ์คํธ์ active๋ ๋ฌธ์ ์๋์ ์ถ๊ฐํ๋ค.
๊ต์ฒด ๋ฒํผ์ ๋๋ฅธ ๋ฌธ์ ๋ฅผ ์ ์ฌ๋ฌธ์ ๋ฆฌ์คํธ์์ ์ญ์ ํ๋ค.
ํ์ต์ง ๋ฆฌ์คํธ์์ active ์ํ ๋ฌธ์ ๋ฅผ ์ ์ฌ๋ฌธ์ ๋ฆฌ์คํธ์์ ์ญ์ ํ index์ ์ถ๊ฐํ๋ค.
ํ์ต์ง ๋ฆฌ์คํธ์์ active ์ํ ๋ฌธ์ ๋ฅผ ํ์ต์ง ๋ฆฌ์คํธ์์ ์ญ์ ํ๋ค.
๋ง์น ๊ต์ฒด๊ฐ ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๋๋ก ์ฐ์ฐ์ ํ๋ค.
// reducer
switch (action.type) {
...
case SWAP_SIMILAR:
draft.problemData.splice(
draft.activeIndex + 1,
0,
draft.similarData[action.data]
);
// ๊ต์ฒด ๋๋ฅธ ๋ฌธ์ ๋ฅผ ํ์ต์ง์ active๋ฌธ์ ์ ์๋์ ์ถ๊ฐ
draft.similarData.splice(action.data, 1);
// ๊ต์ฒด ๋๋ฅธ ๋ฌธ์ ๋ฅผ similarData์์ ์ญ์
draft.similarData.splice(
action.data,
0,
draft.problemData[draft.activeIndex]
);
// ํ์ต์ง์ active๋ฌธ์ ๋ฅผ similarData์์ ์ญ์ ํ index์ ์ถ๊ฐ
draft.problemData.splice(draft.activeIndex, 1);
// ํ์ต์ง์ active๋ฌธ์ ๋ฅผ problemData์์ ์ญ์
break;
...
}
immer๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ถ๋ณ์ฑ์ ์ง์ผ์ฃผ์ง ์์๋ค.
480px 576px 768px ์ผ ๋๋ ๋ฐ์ํ์ ๊ณ ๋ คํด์ผ ํ๋ ๋ง์ ๊ณ ๋ฏผ์ ํ๋ค. ์ฌ์ค ํ์ด์ง ์์ฒด๊ฐ ์ค๋งํธํฐ์ผ๋ก ๋ณด์ง ์๋ ํ์ด์ง์ธ ๊ฒ ๊ฐ๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ ๊ฐ๋จํ๊ฒ๋ง ์ฒ๋ฆฌํด์ฃผ์๋ค.
์ด๋ฏธ์ง๋ ๋น์จ์ ์ ์งํ๋ฉด์ ์์์ง๋ฉด ๋๋ฌด ์ ๋ณด์ฌ์ ๋ฐ๋ก css ์์ฑ์ ์ฃผ์ง ์์๋ค.
json์ ๋ก์ปฌ์์ AJAX๋ฅผ ํตํด ๊ฐ์ ธ์ค๋๋ฐ Layout ์ปดํฌ๋ํธ์์ ๋ฐ์์ค๋ ๊ฒ ๊ฑฐ์ฌ๋ ธ๋ค.
์ปดํฌ๋ํธ๋ view์ ๊ดํ ๊ฒ์ ๊ฐ์ ธ์ค๊ณ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ์ญํ ๋ง ํ๊ณ ์ฌ์ด๋์ดํํธ๊ฐ ์๋ ๋น๋๊ธฐ ํต์ ์ redux-saga๊ฐ ๋ด๋นํ๋๋ก ๊ด์ฌ์ฌ ๋ถ๋ฆฌ๋ฅผ ํ๋ ค๊ณ ํ๋ค.
์ฌ์ค json์ ํตํด ๋๋ฏธ๋ฐ์ดํฐ๋ก ๊ฐ๋จํ ๊ฐ์ ธ์ค๋ ๊ฒ์ด๋ผ์ ๋ฑํ ๋น๋๊ธฐ ๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ ์ด์ ๊ฐ ์์ด ๋ณด์ฌ ์ ์ฉํ์ง ์์๋ค.
๊ตณ์ด ์๋ค๋ฉด ์์ฒญ๊ณผ ์๋ต์ฌ์ด์ ๋ก๋ฉํ๋ฉด์ ๋ณด์ฌ์ค ์ ์๋ค?
์์ ํ๋ก์ ํธ์์ redux-saga ์ฐ๊ธฐ์ ์ฑ๋ฅ์์ ์ด์ ๋ณด๋ค ๊ฐ๋ฐ์ ๊ฒฝํ์ด ๋จ์ด์ง๋ ๊ฒ์ด ๋ ํฐ ๊ฒ ๊ฐ์ ์ฌ์ฉํ์ง ์๊ณ , ๊ฐ Layout ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์๋ค.
SWR์ ๋์ ํ๋ฉด ์ฑ๋ฅ์์ ์ด์ ์ด ์์๊น? ์ ํ ์์ ๊ฒ ๊ฐ๋ค.
๊ฒฝ๋ก๋ฅผ ์จ๊ธฐ๊ธฐ ์ํด Config ํ์ผ์ ์์ฑํ๋ ค ํ์ง๋ง ํฐ ์ด์๊ฐ ์๋ ๊ฒ ๊ฐ์ ์ ์ฉํ์ง ์์๋ค.
css ์์ฑ ๊ธฐ์ ์์๋ NHN ์ฝ๋ฉ์ปจ๋ฒค์ ์ ๋ฐ๋๋ค.
์ถ์ฐจ : https://nuli.navercorp.com/data/convention/NHN_Coding_Conventions_for_Markup_Languages.pdf
css ๋ฐฉ๋ฒ๋ก ์ค ํ๋์ธ BEM ๋ฐฉ์์ ์ ํธํ๋๋ฐ styled-component์ ์ ์ฉํ๋ ค๋ฉด ์ค๋ค์ดํฌ ์ผ์ด์ค์ ํ์ค์นผ ์ผ์ด์ค๋ฅผ ํผํฉํด์ ์จ์ผํ๋ค.
๊ทธ๋ฌ๊ธฐ์ ๊ฐ๋
์ฑ์ด ์ ์ข๊ณ ์ ์ง๋ณด์ ํ๊ธฐ ์ ์ข๊ณ eslint์์ ์๋ฌ๋ฅผ ๋๊ธฐ์ ๊ทธ๋ฅ ํ์ค์นผ ์ผ์ด์ค๋ก styled-component๋ฅผ ์์ฑํ๋ค.
๋ง์ฝ ํ์ต์ง, ๊ต์ฒด/์ถ๊ฐ ํ ๋ฌธ์ ๊ฐ ๋ฐฑ ๋ฌธ์ ๊ฐ ๋๋๋ค๋ฉด?? lazy-loading ์ด๋ react-virtualized๋ก ์ต์ ํ ํด์ฃผ๋ ๊ฒ ์ข์ ๊ฒ ๊ฐ๋ค. ์ด๋ฏธ์ง๋ ์ฉ๋์ ๋ง์ด ์ฐจ์งํ๊ณ ์ฑ๋ฅ์ด ๋๋ ค์ง ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ ์ฌ์ฉ์ ํ๋ฉด์ ๋ณด์ด๋ ์ด๋ฏธ์ง๋ง ์์ฒญํ๊ณ ์ฌ์ฉ์๊ฐ ์คํฌ๋กค์ ๋ด๋ ค์ ๋ค๋ฅธ ์ด๋ฏธ์ง๊ฐ ๋ณด์ฌ์ผ ํ ๋ ์ด๋ฏธ์ง๋ฅผ ์์ฒญํด์ ๋ฆฌ์์ค ์์ฒญ์ ์ค์ด๋ฉด ์ฑ๋ฅ์ด ์ต์ ํ๋ ๊ฒ์ด๋ผ ์๊ฐํ๋ค.
๋ถํ์ํ ํ๊ทธ๋ ์คํ์ผ์ด ์์ด์ผ ๋ธ๋ผ์ฐ์ ๊ฐ css๋ฅผ ๊ทธ๋ฆฌ๋ ์๊ฐ์ด ์กฐ๊ธ์ด๋๋ง ๋จ์ถ ๋ ๊ฒ์ด๊ณ ์ด๋ ์ฑ๋ฅ ์ต์ ํ๋ก ์ด์ด์ง ๊ฒ์ด๋ค. ํ์ง๋ง ๋์ css์ ์์ด์๋ ์์ง ๋ฌผ์ํ๊ฐ ๋ง๋ค. ์ํฉ์ ์ ์ ํ ์์ฑ์ธ์ง.. ์๋งจํฑ ํ๊ทธ์ ์ต์ํ์ง ์์์ ์ ์ผ๋์ง ํ์ ์ด ์ ์ ๋ค. ๋ฉด์ ์ ๋ณด๊ฒ ๋๋ค๋ฉด ์ฌ์ญค๋ณด๋ ค๊ณ ํ๋ค.
React.memo()๋ฅผ ์ด์ฉํ ์ต์ ํ? ์ฌ์ค ํฐ ์ด์ ์ด ์์ด๋ณด์ฌ ์ฌ์ฉํ์ง ์์๋ค. memo๋ ์์ ๋น๊ต๋ฅผ ํตํด ๋ณ๊ฒฝ์ฌํญ์ด ์๊ธธ ๋ ๋ฆฌ๋ ๋๋ง์ ํ๋ ๊ฒ์ด๊ณ ์์ ๋น๊ต์์ ๋ฆฌ๋ ๋๋ง์ ์ํํ๊ธฐ ์ํ ์ค๋ฒํค๋๊ฐ ์ผ๋ฐ์ ์ธ ์ปดํฌ๋ํธ ์คํ๋ณด๋ค ๋น์ฉ์ด ๋ ๋น์ ์ ์๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ด๋ค.