๐ Component ๊ณต์ ์ ์ํ๊ด๋ฆฌ
FrontState ์ BackEnd State๋ฅผ ์ด๋ป๊ฒ ๊ด๋ฆฌํด์ผ ํ ๊น?
์ฒ์์๋ ๋ด๋น์์ ๋ ์ด๋ธ๋ง์ผ์คํค ์ ์ด๋ป๊ฒ ๊ด๋ฆฌํด์ผ ํ ๊น ๊ณ ๋ฏผํ๋ค.
1) ๋๋ฌ์ ์ถ๊ฐํ ๋๋ง๋ค ์๋ฒ์ ์ ์ก
2) FrontState์์ ๊ด๋ฆฌํ๊ณ ์ด์ ์์ฑ์ ๋๋ฅผ ๋ ์ ์ก
GitHub์์๋ ์ด๋ป๊ฒ ๊ด๋ฆฌํ๋์ง ํ์ธํด๋ณด์๋๋ฐ ๋๋ฅด๋๋ง๋ค ์๋ฒ๋ก ์ ์ก์ด ๋๋๊ฑด๊ฐ? ํ์ง๋ง ๋ก์น์ ๋ง๋ก๋ ์ํด๋ก ์๋ฒ๊ฐ์ด ๋์ํ๋๊ฒ์ด์ง ์ค์ ๋ก ๊ทธ๋ด๋ฆฌ๊ฐ ์์ ๊ฒ์ด๋ผ ํ๋ค. ใ ใ
๊ฒฐ๊ณผ์ ์ผ๋ก 2๋ฒ์ ์ ํ
์ด์ ์์ฑํ๊ธฐ ์ ๊น์ง ์ ์ธ๊ฐ์ง ์ ๋ณด๋ ํ์ ์๊ธฐ ๋๋ฌธ์ FrontState๋ก ๊ด๋ฆฌํ๋ค ์ด์ ์์ฑ ์ ์ ์กํ๋๊ฒ์ด ๊ฒฝ์ ์ ์ด๋ผ ์๊ฐํ๋ค.
์ฒ์ SideBar๋ฅผ ๋ง๋ค ๋ IssueAdd ์์ ๋์ํ๊ฒ๋ง ๋ง๋ค๋ค ๋ณด๋ ์ํ๊ฐ์ ๋ค ๊ฐ์ง๊ณ ์๊ป ๋์๋ค.
IssueDetail์์๋ ์ฌ์ฉํ๋ ค๋ฉด ์ํ๊ฐ์ ์ ๋ถ ๋นผ์ props๋ก ์ ๋ฌ๋ฐ์์ ๋ฟ๋ฆฌ๋ ๋ฐฉ๋ฒ์ผ๋ก ๋ฐ๊พธ์
IssueDetail ๊ณผ IssueAdd ์์ ๋์ผํ๊ฒ SideBar๋ฅผ ๊ณต์ ํ๋ฉด์
๐ Issue ์์ธํ์ด์ง ์ํ๊ด๋ฆฌ ๋๊ฒฉ๋
๊ธฐ์กด ์ฝ๋
IssueList์ Cell์ ํด๋ฆญํ๋ฉด Link To ๋ก ํด๋ฆญํ ํด๋น data(state) ๋ฅผ ์ญ~~~~ ๋ด๋ ค์ฃผ๊ณ SideBar์์ ๋ณ๊ฒฝํ๋ฉด ๊ทธ ๊ฐ์ CheckedData๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ์
Recoil์ ์ฌ์ฉํ๋๋ฐ ์ ๊ณ์ ์ํ๋ฅผ ๋ด๋ ค์ค์ผ ํ๋์ง ์๋ฌธ์ด ๋ค๊ธด ํ๋ค.
IssueDetail
const IssueDetail = (): JSX.Element => {
const { state } = useLocation<IssueDataProps>();
const setCheckData = useSetRecoilState(dropCheckState);
useEffect(() => {
setCheckData({
assignee: state.assignees,
label: state.labels,
milestone: [state.milestoneInfo],
});
}, [state]);
IssueList - IssueCell
const IssueCell = ({ issues }: { issues: IssueDataProps[] }): JSX.Element => {
const decoded = decodedToken && useRecoilValue(decodedToken);
const profileURL = decoded && decoded.profileImageUrl;
const openIssue = getIssue(issues, 'OPEN');
const closedIssue = getIssue(issues, 'CLOSED');
return (
<>
{openIssue.map((issue) => {
const {
assignees,
content,
createdDateTime,
issueId,
labels,
milestoneInfo,
status,
title,
writer,
} = issue;
const elapsedTime = getElapsedTime(createdDateTime);
return (
<S.IssueCell key={uuidv4()}>
<>
<LeftBox>
<CheckBoxes />
<IssueCellContent>
<Link
to={{
pathname: `/main/issue-detail/${issueId}`,
state: issue,
}}
>
SideBar์ Dropdown ๋ฉ๋ด ํด๋ฆญ ์ ์ํ ๊ด๋ฆฌ
Recoil
export const dropCheckState = atom<dropCheckStateProps>({
key: 'dropCheckState',
default: {
assignee: [],
label: [],
milestone: [],
},
});
SideBar์์ ์ฒดํฌํ ์ ๋ณด๋ค์ ๋ฐ๋ก Recoil๋ก ๋ฃ์ด์ ๊ด๋ฆฌํ๋ค.
const handleClickAssignee = () => {
setDropCheck({
...dropCheck,
assignee: [...dropCheck.assignee, data],
});
ํด๋ฆญ ์ ์ํ๊ฐ ๋ณ๊ฒฝ๋์ด ๋ณด์ด๊ฒ ๋ง๋ค์๋ค.
๐ ํ๊ธฐ์ฐฌ ์์์ผ์ ์ฝ๋ ์ปจ๋ฒค์
1) useRecoilValue ๋ค์ด๋ฐ ๊ท์น ์ค์
๊ธฐ์กด ๋ฐฉ์
//SideBar
export const issueForm = selector({
key: 'issueForm',
get: async () => {
const response = await fetch(U.FORM);
const data = await response.json();
return data;
},
});
export const dropAssigneeState = atom({
key: 'dropAssigneeState',
default: false,
});
๊ธฐ์กด์๋ useRecoilValue์ state์ ๋ถ๋ฌ์์ ์ฌ์ฉํ๋ ๋ณ์๊ฐ์ ๊ท์น์ด ์์๋ค.
์ฝ๋๋ฅผ ๊ตฌํํ ๋๋ง๋ค ๊ทธ๋๊ทธ๋ ๋ค๋ฅด๊ฒ ์ฌ์ฉํ์๋๋ฐ, ํ์ผ์ด ๋ง์์ง๊ณ ๋ณต์กํด์ง๋ ๊ทธ ์์ ๊ท์น์ด ์์ด์ ํผ๋์ค๋ฌ์ ๊ณ ๋ค์ด๋ฐ๊ณ ๋ฏผ์ด ๊ธธ์ด์ก๋ค..
์ปจ๋ฒค์ ๋ณ๊ฒฝ
useRecoilValue๋ก ๊ด๋ฆฌํ๋ ๊ณณ์ ์ํ์ด๋ state๋ฅผ ๋ถ์ด๊ณ
๊ฐ์ ธ์ ์ฌ์ฉํ๋ ๊ณณ์์ ์ด๋ฆ ๊ทธ๋๋ก์์ state๋ฅผ ๋นผ๊ธฐ!
๊ฐ๋จํ ๊ท์น์ด์ง๋ง ๋ณ๊ฒฝํ๋๋ฐ ์๊ฐ์ด ๋ง์ด ์์๋์๊ณ , ๋์ ์ ํด๋น ํจํด์ ๋ง๋ซ์๋ ๊ณ ๋ฏผํ๊ฑฐ๋ ํท๊ฐ๋ฆฌ๋๋ถ๋ถ์ด ์ฌ๋ผ์ ธ ๋ณด๋ค ์ง๊ด์ ์ด๊ฒ ์ฝ๋๋ฅผ ์ดํดํ๊ฒ ๋ ์ฅ์ ์ด ์๊ฒผ๋ค.
2) import ์ปจ๋ฒค์ ์ค์
import๋ฅผ ๋ง์ดํ๊ณ ๊น์ด๊ฐ ๊น์ด์ง ์๋ก ๊ท์น์ด ํ์ํ๋ค๊ณ ํ๋จํด, ์๋์ ๊ฐ์ด import ์์๋ฅผ ์ ํ๋ค.
๊ธฐ๋ณธ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๋ผ์ด๋ธ๋ฌ๋ฆฌ
component
์ํ
์คํ์ผ๊ด๋ฆฌ
์ด๋ก ์ธํ ์ฅ์ ์ ํ์ผ์ ๋ฑ ์ด์์ ๋ ์ง๊ด์ ์ผ๋ก ์ฌ์ฉํ๋ ํ์ผ๋ค์ ๋ณผ ์ ์๋ค๋ ์
๋ค๋ง ์ ๋๊ฒฝ๋ก๋ฅผ ์์ง ์ค์ ํ์ง ์์์ ../ ../ ์ด ๋จ๋ฐํ๋๋ฐ ์ถ๊ฐํ ์์ ์ด๋ค.
๐BE์ ๊ณต๋ถํ REST API ์ ์ ์ ์ด์ ๋ค
์ด๋ค data๋ค์ id๊ฐ number ์ด๊ณ (๋จ์ idx๋๋)
์ด๋ค data๋ค์ id ๊ฐ string ์ด๋ค.
number๋ก ๋ค ๋ง์ถ๋ฉด ๋๋๋ฐ ์ ๊ตณ์ด string์ด ๋๋ ๋ณ์๋ค์๊ฒ ๊ทธ๋ ๊ฒ ์ค์ ํ์์๊น?
๊ทธ ์ด์ ๋ id ๊ฐ ์๋ณ์ ์์ฒด์ ์ ๋ณด๋ฅผ ๊ฐ์ง๋ ๊ฒ์ด ์ค์ํ๊ธฐ ๋๋ฌธ!!
comment data
์ค์ comment(๋๊ธ) data๋ฅผ ๋ณด๋ฉด
๋๊ธ์ id = 1 number
writer์ id = string์ธ๊ฑธ ๋ณผ ์ ์๋๋ฐ,
์ด๋ ๋๊ธ์ ์์ฒด๋ง์ ๊ณ ์ ํ ์ ๋ณด๊ฐ ์๊ธฐ ๋๋ฌธ์ idx๊ฐ์ ์ซ์๋ฅผ ํ ๊ฒ์ด๊ณ
writer์ ์ด๋ฆ์ด๋ผ๋ ์์ ์ ๊ณ ์ ํ ์์ฑ์ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ string์ ํ ๊ฒ!
data์ ๊ตฌ์กฐ๋ฅผ ์ค๊ณํ ๋ ์ ๋ง ๋ง์ ๊ณ ๋ฏผ์ ํด์ผ ํ๋ค๋ ๊ฒ์ ๋๊ผ๋ค.
POST์ PUT์ ์ฐจ์ด์ ์?
POST | X | ์์ฑ | Issue ์ถ๊ฐ | ์์ฒญํ ๋๋ง๋ค ์๋ก์ด data๊ฐ ์์ธ๋ค |
PUT | O | ์์ | Issue ์์ | ์์ฒญํ ๋๋ง๋ค ์ด์ ์ data๋ฅผ ์์ ๊ณ ๊ทธ ์๋ฆฌ๋ฅผ ๋์ ํ๋ค |
DELETE | O | - | - |
PUT์ ์ฅ์
- ๋ ๋๋ง์ผ๋ก ์ธํด ์ฌ๋ฌ๋ฒ ํธ์ถํด๋ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฅํ๋ค.
- ๋ถ๋ถ์ ์ธ ๋น๋๊ธฐ API ์์ฒญ์ด ์ ํด์ง๋ค.
POST์ ์ฃผ์์
- ์์ ํ์ง ์๊ธฐ ๋๋ฌธ์ disabled ๊ฐ์ UI ์์๊ฐ ํ์ํ๋ค.
- ๋ฌดํ๋๋ก ๋ ๋๋ง ๋๊ณ API์์ฒญ์ด ์ฌ๋ฌ๋ฒ ๋ ์ ์๋ค.
POSTMAN ๊ณต์ ๋ฐ ์ฌ์ฉ๋ฒ
BE์ API๋ฅผ ํ์ธํ๊ณ ์ฌ์ฉํ ๋ POSTMAN์ ์ฌ์ฉํ๋ฉด ์ค์๊ฐ์ผ๋ก ์์ธํ๊ฒ ์์ฒญ์ ํด๋ณผ ์ ์๋๋ฐ, ์ฌํ๊ป ๋งํฌ๋ก๋ง ๊ณต์ ํ๋ค๊ฐ ๊ทธ๋ฃน์ด ๋์ด์ ์ฌ์ฉํ๋ค. ์ ๋ง ํธ๋ฆฌํด์ ์์ผ๋ก ๋ง์ฃผํ ๋๋ง๋ค ์ฌ์ฉํ ๋ฐฉ๋ฒ์ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌ!
1) import - Link
2) New - Environment
์ ๋ฆฌ ๋งํฌ : https://github.com/ghojeong/issue-tracker/wiki/%EC%A0%95%EA%B8%B0%EB%AF%B8%ED%8C%85-%ED%9A%8C%EC%9D%98%EB%A1%9D
๐ TypeScrit ์์ ๊ฐ์ฒด(jsonํ์ ) ์ ๋ฌ ์type ์ง์
๊ฐ์ ์ํฉ ํด๊ฒฐ ๊ณผ์ :https://rrecoder.tistory.com/155
โ ์ํ๋ ๋์
LabelTabel์์ labelList ๋ฐฐ์ด์ map ๋๋ฉฐ LabelCell ์ปดํฌ๋ํธ๋ฅผ์ฌ๋ฌ๊ฐ ๋ ๋๋ง ํ๋ ์์
์ฌ๊ธฐ์ label์ ๋๊ฒจ์ค ๋ label์ ํ์ ๋ค์ ์ง์ ํด์ผ ํ๋ค.
โ ์ด์ ์ฝ๋
label ์ ๋ค์๊ณผ ๊ฐ์ด ์๊ฒผ๊ณ
label = {
backgroundColor: "#1E4174"
description: "๋ผ๋ฒจ2 ์ค๋ช
"
id: 2
textColor: "#FFFFF"
title: "๋ผ๋ฒจ2 ์ ๋ชฉ"
}
LabelProps
export interface LabelProps {
id: number;
title: string;
description: string;
backgroundColor: string;
textColor: string;
}
LabelProps๋ ์ด๋ ๊ฒ ๋ฐ๋ก ์ง์ ํด ์คฌ๊ธฐ ๋๋ฌธ์
const LabelCell = ({ label }: LabelProps): JSX.Element => {
return (
LabelTable
const LabelTable = (): JSX.Element => {
const labelList = useRecoilValue(labelListState);
const labelListArr = labelList?.labels;
return (
<IssueTableContainer>
<LabelTableHeader />
{labelListArr?.map((label: LabelProps) => (
<LabelCell key={uuidv4()} {...{ label }} />
))}
</IssueTableContainer>
);
};
โ ์์ ์ฝ๋
๊ทธ๋ ๋ค๋ฉด json์ ๊ฐ์ฒด๋ฅผ ์์ฑ์ผ๋ก ๋๊ธธ ๋ ํ์ ์ง์ ํ๋ ๋๊ฐ์ง ๋ฐฉ๋ฒ
1) ์์ฑ ์์ฒด๋ฅผ props ์ด๋ฆ์ผ๋ก ๋๊ธด๋ค
interface LabelPropss {
label: {
id: number;
title: string;
description: string;
backgroundColor: string;
textColor: string;
};
}
const LabelCell = ({ label }: LabelPropss): JSX.Element => {
return ( //์ค๋ต
LabelPropss ๋ฅผ ๋ณด๋ฉด LabelCell์ ๋๊ธธ props๋ช ์ ๋ฏธ๋ฆฌ type์ง์ ์ ํด์ฃผ๋ฉด ์๊ฐํ๋ ๋๋ก ๋๊ธธ ์ ์๋ค.
2) type ์ ์ธ ์ props๋ช ํ ๋นํด์ฃผ๊ธฐ
export interface LabelProps {
id: number;
title: string;
description: string;
backgroundColor: string;
textColor: string;
}
const LabelCell = ({ label }: { label: LabelProps })
//์ค๋ต
type ์ ์ธ ์์ LableProps ๊ฐ label์ด๋ผ๋ ๊ฒ์ ์ง์ ํ๋ฉด์ ํ์ ์ง์ ์ ํ ์ ์๋ค.
๐ ๋ฒํผ ์ธ ์์ญ ํด๋ฆญ ์ ๋ซํ๋ ๋ก์ง ๊ตฌํ
1) ref ๊ฑธ component๊ฐ ์๊ตฌ์ ์ด์ง ์์ ๋ฌธ์
๊ธฐ์กด ์ฝ๋
์ผ๋จ ref๋ฅผ ๊ฑฐ๋ ๋ถ๋ถ์ด ์ผํญ์ฐ์ฐ์๋ก ์ฒ๋ฆฌํด๋์๋๋
๊ฐ๋ ์ฑ๋ ๋จ์ด์ง๊ณ ์กฐ๊ฑด์ ๋ฐ๋ผ ref๊ฐ ์์ ์ ๋ ์์ด์ ๋ณ๊ฒฝ์ด ํ์ํ๋ค.
<ButtonBox ref={editRef}>
{issueEditTitle ? (
<TitleEditButton
startIcon={<TitleEditIcon />}
color="primary"
onClick={handleClickCompleteButton}
>
<TextGroup
type={T.SMALL}
content={TT.EDIT_COMPLETE}
color="#007AFF"
/>
</TitleEditButton>
) : (
<TitleEditButton
startIcon={<TitleEditIcon />}
color="primary"
onClick={handleClickEditButton}
id={'editButton'}
>
<TextGroup
type={T.SMALL}
content={TT.EDIT_TITLE}
color="#007AFF"
/>
</TitleEditButton>
)}
๋ณ๊ฒฝ ์ฝ๋
ButtonBox์ ๋ค์ด๊ฐ์ผ ํ Component๋ฅผ ๋ถ๋ฆฌํ ํ์
Component ๋ด๋ถ์์ ์ผํญ์ฐ์ฐ์์ ์ํด ๋๋๋๋ก ํ๋ค.
์ด๋ ๊ฒ ๋ฐ๊ฟ์ผ๋ก์จ ์ด๋ค ์กฐ๊ฑด์ด ์์ด๋ ButtonBox๋ ๊ณ ์ ๋์ด ๋ ๋๋ง ๋๋ useRef ๊ฐ ๊ฐ๋ฅํด์ง๋ค.
<ButtonBox ref={editRef}>
<IssueDetailHeaderButtonSection
state={issueEditTitle}
id={issueDetails?.issueId}
/>
<IssueDetailIssueCloseButton callback={setIssueState} />
</ButtonBox>
const IssueDetailHeaderButtonSection = ({
state,
id,
}: {
state: boolean;
id: number;
}): JSX.Element => {
return (
<>
{state ? (
<IssueDetailTitleEditCompleteButton {...{ id }} />
) : (
<IssueDetailTitleEditButton />
)}
</>
);
};
2) ๋ฒํผ ์์ญ ์ธ ํด๋ฆญ์
event์ ํ์ ๋ฌธ์
๋ฒํผ ์ธ ์์ญ์ ๋๋ ์ ์ ํธ์ง ์ฐฝ์ด ์ข ๋ฃ๋์ด์ผ ํ๋ค.
ํ์ฌ๋ ํธ์ง๋ฒํผ์ ๋๋ฌ์ผํจ ํธ์ง์ด ์ข ๋ฃ๋๋ค.
useEffect(() => {
const handleClickBody = (e: { target: HTMLInputElement }) => {
if (editRef.current && !editRef.current.contains(e.target)) {
setIssueEditTitle((prev) => !prev);
}
console.log(e.target);
console.log('current', editRef.current);
// setIssueEditTitle((prev) => !prev);
};
window.addEventListener('mousedown', handleClickBody);
return () => window.removeEventListener('mousedown', handleClickBody);
}, [editRef]);
๐ SideBar ์ ์ํ ํด๋ฆญ ์ ์๋ฒ์ PUT์์ฒญ ๋ณด๋ด๊ธฐ
์ฌ์ด๋๋ฐ์์ ๋ด๋น์๋ฅผ ๋ฐ๊พธ๊ณ ์ฌ์ด๋๋ฐDrop์ ๋ซ์๋ ์๋ฒ์ PUT์์ฒญ์ ๋ณด๋ด๋ฉฐ ์์ ํ๋ ๋ก์ง
์ผ๋จ ์ฒซ๋ฒ์งธ ๊ณ ๋ฏผํ๋ ๊ฒ์ ์๋ฒ์ ์์ฒญ์ ๋ณด๋ด๋ ํ์ด
1) SideBarDrop์์ ๋๋ฅผ ๋๋ง๋ค PUT ํ ๊ฒ์ธ์ง
2) ๋ชจ๋ฌ์ ๋ซ์์ ๋ ๋ณด๋ผ๊ฒ์ธ์ง ์ธ๋ฐ
์๋ฒ์ ์ฒดํฌํ ๋๋ง๋ค ๋ณด๋ด๋ฉด ๋ชจ๋ฌ์ ๋ซ์ ๋ ๋ณด๋ด๋ ๊ฒ๋ณด๋ค ํจ์ฌ ๋ง์ ์๋ฒ ์์ฒญ์ด ๋ฐ์ํ ๊ฒ์ด๋ผ๊ณ ์์ํด,
๋ผ์ฟค๊ณผ ๋ชจ๋ฌ์ด ๋ซํ ๋ ์์ฒญํ๋ ๋ฐฉํฅ์ผ๋ก ๊ฒฐ์ ํ๋ค!
๋ด๋น์ ํธ์ง ์์ฒญ ์ฝ๋
const dropCloseHandler = (e: MouseEvent): void => {
if (dropAssigneeElement.current?.contains(e.target as Node)) return;
if (dropLabelElement.current?.contains(e.target as Node)) return;
if (dropMilestoneElement.current?.contains(e.target as Node)) return;
setIsDropAssignee((prev: boolean) => {
const assigneeUrl =
U.ISSUES + '/' + issueDetailId.issueId + '/assignees';
if (prev)
getPut(assigneeUrl, userToken, {
assigneeIds: getValueInJson(checkedAssignee, 'id'),
});
return false;
});
setIsDropLabel(false);
setIsDropMilestone(false);
};
SideBar ์ธ ์์ญ์ ๋๋ ์ ๋์๋ return
Assignee SideBar DropDown์ธ ์์ญ์ ํด๋ฆญํด ๋ซํ๋ ์ํ๋ก ๋ณ๊ฒฝ ํ๋ ๋ก์ง (setIsDropAssignee) ์์
PUT ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ค.
PUT์์ฒญ body ๊ฐ ์ฒดํฌ๋ ๋ด๋น์๋ค์ id๋ง ๋ฐฐ์ด๋ก ์ ์กํ๋ ํ์์ด๊ธฐ ๋๋ฌธ์ ex) ['ink-0', 'Raccoon']
getValueInJson๋ก id๋ง ๋ถ๋ฌ์ค๋ ํจ์๋ฅผ ์จ์ body๋ก ๋ฃ์ด์ค๋ค.
๋ง์ดํ ์ด๋ ค์
ํ์ฌ ์ด ํจ์๋ useEffect ๋ด์ ๋ค์ด์๋๋ฐ,
๊ทธ dependencyArray์ []๋น๋ฐฐ์ด ๋ฃ์ -> ์ฒ์ ํ๋ฒ๋ง ๋์ -> ์ฒดํฌํ ๋ด๋น์(checkedAssignee๊ฐ ์ด๊ธฐ๊ฐ) 0์ผ๋ก ์์ฒญ
์ด ๋ก์ง์ด ์คํ๋์ ์๊พธ๋ง ์์ฒญ์ ํ๋ฉด ๋ด๋น์๊ฐ ์ฌ๋ผ์ง๋ ์ด๋ ค์์ ๊ฒช์์ง๋ง,
dependencyArray์ chekedData( ์ฒดํฌํ ๋ด๋น์ ์ธ ๋ ์ด๋ธ๊ณผ ๋ง์ผ์คํค๋ ํฌํจ)์ ๋ฃ์ด์ค์ผ๋ก์จ
์ฌ์ฉ์๊ฐ ์ฒดํฌํ ๋ฐ์ดํฐ๋ค์ด ๋ฐ๋๋๋ง๋ค ๋ค์ ์คํ๋๋๋ก ํ์ฌ ๋ฐ์ดํฐ ์์ ์์ฒญ์ ์ฑ๊ณตํ๋ค.
๐ ํ์ฌ๊น์ง์ ์ํฉ
๊นํ๋ธ ๋ก๊ทธ์ธ์ ํ ํ์ ๋ง๋ค๊ณ ์ถ์ ์ด์๋ค์ ์์ฑ, ํธ์งํ๊ณ
๋ ์ด๋ธ ๋ง์ผ์คํค๋๋ง๋ค์ด์ ๋ฌ ์ ์๋ค.
๋ก๊ทธ์ธ , ์ด์๋ฆฌ์คํธ (๋ฉ์ธ ํ๋ฉด)
์ด์ ์์ฑ ์์ ์ญ์
๋ ์ด๋ธ ์์ฑ ์์ ์ญ์
๋ง์ผ์คํค ์์ฑ ์์ ์ญ์
์์ฐ gif
๋ก๊ทธ์ธ๊ณผ ์ด์ ์์ฑ
๋ง์ผ์คํค ์์ฑ, ๋ ์ด๋ธ ์์ฑ
๐ ์งง์ ํ๊ณ
์ฒ์ ๋ง๋ค ๋ ์ํ๊ด๋ฆฌ๋๋ฌธ์ ์ด์ง๋ฝ๊ณ ํ๋ค์๋๋ฐ
์ ๋ง ์ ๋์ํ๋ ๊ฑธ ๋ณด๋ ๋๋ฌด ์ ๊ธฐํ๊ณ ์ฌ๋ฐ์ด์ ๊ณ์ ํด๋ฆญํ๊ณ ๋ง์ง๊ณ ์๋ค.
์ง๊ธ์ ๋ฒ์ 1์ด๊ณ ์์ง ํด๊ฒฐํด์ผํ๋๊ฒ ์ฐ๋๋ฏธ์ง๋ง, ์ผ๋จ์ ์ ํ๋ ๋ฒ์ ๋ด์์ ๋์์ด ๊ฐ๋ฅํ๋ค!!!! ๋๋ฌด ํ๋ณตํด..
๋ฌผ๋ก ์ฌ๊ธฐ์ ๋ฉ์ถ ์๊ฐ์ ์๊ณ , ๋จ์ Task๋ค์ ์งํํ ์์ !
๋๊ธ