๊ฐ๋ฐ์ ํ ๋ ์๋ก์ด ๋ฌด์ธ๊ฐ๋ฅผ ๋ง๋ฅ๋จ๋ฆฌ๋ ๊ฒฝ์ฐ๊ฐ ์ค์ด๋ค์ด์ ๊ทธ๋ฐ๊ฐ, ์ฝ๋๋ฅผ ์ด์๊ฒ ์ง๋ ๊ฒ์ ๊ด์ฌ์ด ๋ง์์ก๋ค. ๋๋ฅ์ ๊ฐ๋ฐํ๋ฉด์ ๋ง์ฃผ์น ๊ณ ๋ฏผ๋ค์ ๊ณต์ ํด๋ณด๋ ค๊ณ ํ๋ค. ๋ฆฌ์กํธ๋ก ๊ฐ๋ฐํ๋ค๋ณด๋ฉด ์ ์ธ์ ์ธ ์ฝ๋์ ๋ํด์ ๊ณ ๋ฏผํ๊ฒ ๋๋ค. ๋ฆฌ์กํธ ์์ฒด๊ฐ ์ ์ธํ์ด๊ธฐ ๋๋ฌธ์ผ์๋.
์ ์ธ์ ์ธ ์ฝ๋
๋ช ๋ นํ ํ๋ก๊ทธ๋๋ฐ๊ณผ ์ ์ธํ ํ๋ก๊ทธ๋๋ฐ์ ๋ํ ๊ธ๋ค์์ ํํ 'How'์ 'What'์ ์ฐจ์ด๋ก ์ค๋ช ํ๋ค.
<ul id=”list”></ul>
<script>
var arr = [1, 2, 3, 4, 5]
var elem = document.querySelector("#list");
for(var i = 0; i < arr.length; i ++) {
var child = document.createElement("li");
child.innerHTML = arr[i];
elem.appendChild(child);
}
</script>
๋ฐ๋ณต๋ฌธ์ ํตํด ๋ฐฐ์ด์ ์์๋ฅผ ์ํํ๋ฉด์ html์์๋ฅผ ์์ฑํ๊ณ ๋ณด์ฌ์ฃผ๋ ์ฝ๋์ด๋ค. ์ด๋ค ์ ์ฐจ๋ก ์ด๋ฃจ์ด์ง๋์ง๊ฐ ๋๋ฌ๋ ์๋ค.
const arr = [1, 2, 3, 4, 5];
return (
<ul>
{arr.map((elem) => (
<li>{elem}</li>
))}
</ul>
);
React์์ jsx ๋ฌธ๋ฒ์ ์ฌ์ฉํ๋ฉด ์ด๋ ๊ฒ ํํํ ์ ์๋ค. ํต์ฌ ๋ฐ์ดํฐ๋ง ์ธ๋ถ์์ ์ ๋ฌ ๋ฐ๊ณ ์ธ๋ถ์ ์ธ ๊ตฌํ์ map
ํจ์๋ฅผ ํตํด ์จ๊ฒจ์ ธ์๋ค. ๋ณต์กํ ์์
์ ์ถ์ํํ๋ค๊ณ ๋งํ ์ ์๋ค.
ํ๋จ๊ณ ๋ ์ถ์ํํ๋ค๋ฉด
<NumberListItem data={arr}/>
ํด๋น ์ปดํฌ๋ํธ ๋ด๋ถ์์ ์ด๋ค ๋ฐฉ์์ผ๋ก ๋์๊ฐ๋์ง๋ ์ ๊ฒฝ์ฐ์ง ์๊ณ , ๋ฌด์์ ๋ณด์ฌ์ค์ง๋ง ์ ๋ฌํ๋ค. ๋์ฑ ๋น ๋ฅด๊ฒ ์ฝ๋์ ์ญํ ์ ํ์ ํ ์ ์๊ฒ ๋์๋ค.
ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ํ ๋๋ ์ ์ธ์ ์ธ ์ฝ๋๋ฅผ "์ถ์ํ ๋ ๋ฒจ์ด ๋์์ง ์ฝ๋"๋ก ๋ณผ ์ ์๋ค. ํ์ง๋ง ๋ฌด์กฐ๊ฑด ๋๋ค๊ณ ์ข์ ๊ฒ์ ์๋๋ค. ์ฌ๋ฌ๊ตฐ๋ฐ์์ ์ฌํ์ฉ๋๊ณ ์๋ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ ํ์ด์ง์ ์ฝ๊ฐ์ ์์ ์ด ํ์ํ ๋ ์ข ์ข ๋ฌธ์ ๊ฐ ์๊ธด๋ค. ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ธฐ ์ํด ์กฐ๊ฑด์ ๊ฑธ๊ณ prop์ ๋ํ๋ค ๋ณด๋ฉด, ์คํ๋ ค ์ฑ ์์ด ๋ง์์ง๊ณ ๋ค์ด๋ฐ์ด ๋ชจํธํด์ง๊ธฐ๋ ํ๋ค. ๋๋ฌธ์ ์ถ์ํ์ ๋ ๋ฒจ์ ์ ์ ํ ์ ํํด์ผ ํ ํ์๊ฐ ์๋ค.
๋๋ฅ์๋ ์ ์ธ์ ์ธ ์ฝ๋๊ฐ ์์ฃผ ์ฌ์ฉ๋๊ณ ์๋ค. ์ด์ ๋๋ฒ์ ํ๋ก์ ํธ๋ฅผ ์ด์ด์ค๋ฉด์ ๋ง์ ๊ธฐ๋ฅ์ด ๊ทธ๋๋ก ๋๋ฅ์ผ๋ก ๋ค์ด์๋ค. ๊ทธ ๋๋ถํฐ ์ผ๋์ ๋๋ ๋ฆฌํฉํ ๋ง์ ํ๋ ๊ธฐ๋ถ์ผ๋ก ๊ฐ๋ฐ์ ํ ์ ์์๋ค. ๋ช๊ตฐ๋ฐ ์๊ฐํด๋ณด๋๋ก ํ๊ฒ ๋ค.
๋ฌดํ ์คํฌ๋กค
๋ฆฌํฉํ ๋ง ์ด์
๊ธฐ์กด ๊ณ ์ค๋ฝ ํฐ์ผ์์ ์์ํก์ ๋ฌดํ์คํฌ๋กค๋ก ๋ณด์ฌ์ง๋ค. ์ด๋ ๊ฒ ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ์ฐ์์ ์ผ๋ก ๋ฐ์์ค๊ณ ์ ์ ํ ๋ฐฉ์์ผ๋ก ๋ ๋๋งํ๋ ๋์์ ์ถ์ํํด์ ์ฌ์ฉํ๊ณ ์๋ค.
React Query
์ useInfiniteQuery
๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค. ๋ฌดํ์คํฌ๋กค ๋ฐ์ดํฐ๋ค์ ๊ฐ ํ์ด์ง ๋ฐ์ดํฐ๋ค์ ๋ฐฐ์ด๋ก ๋ฐ์์ค๋๋ก ์ถ์ํ๋์ด์๋ค. ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํจ์์ ์ฌ์ฉ๋ฒ ์์ฒด๋ ์ ๊ธ์ ์ ๋ฆฌ๋์ด์๋ค.
const TalkList = ({ talkList }: { talkList: ITalk[] }) => {
return (
<Wrapper>
{talkList.map((talk) => (
<TalkBubble
nickName={talk.nickName}
content={talk.content}
createdAt={talk.createdAt}
iComment={talk.iComment}
key={talk.id}
/>
))}
</Wrapper>
);
};
<TalkListWrapper isOpen={isOpen} ref={talkListRef}>
{data?.pages.map((talkList) => (
<TalkList talkList={talkList.talkList} key={talkList.lastId} />
))}
<Observation />
</TalkListWrapper>
๋ฐ์์จ ๋ฐ์ดํฐ๋ค์ ์์๊ฐ์ด ๋๋ฒ์ map
์ ํตํด ๋ ๋๋ง๋๋ค. ๊ฐ ํ์ด์ง๋ง๋ค ํ๋ฒ, ๊ทธ ์์์ ํ๋ฒ. useInfiniteQuery
์ map
ํจ์์ ๋์์ผ๋ก ์ด๋ฏธ ์ด๋์ ๋ ์ถ์ํ๋์ด ์๋ ์ฝ๋์ด๋ค.
๋ฆฌํฉํ ๋ง ์ดํ
๋๋ฅ์์๋ ์์ํก ๋ฟ๋ง ์๋๋ผ ๊ณต์ฐ, ํธ์คํธ, ๋ฉค๋ฒ ๋ชฉ๋ก ๋ฑ์์ ๋ฌดํ์คํฌ๋กค์ ์ฌ์ฉํด์ผ ํ๋ ์ํฉ์ด ๋ง์์ก๋ค. ๋ฌดํ์คํฌ๋กค ํ์ด์ง๋ฅผ ์ํด ๋งค๋ฒ ์ปดํฌ๋ํธ๋ฅผ ๋๊ฐ์ฉ ๋ง๋ค์ด ์ฐ๋๊ฑด ๋ถํธํ๋ค. ํ๋จ๊ณ ๋ ์ถ์ํํด๋ณธ๋ค๋ฉด ์๋์ ๊ฐ์ด ํด๋ณผ ์ ์์ ๊ฒ์ด๋ค.
const { infiniteListElement } = useInfiniteQueriesList<EventResponse>(
['events', keyword],
({ pageParam = 0 }) =>
EventApi.GET_EVENTS_SEARCH({ keyword, pageParam, size: 12 }),
EventLink,
);
useInifiniteQueriesList
๋ผ๋ Hook์ด๋ค. ์ฟผ๋ฆฌํค, api ํธ์ถ ํจ์(QueryFn), ๋ ๋๋งํ ์์ดํ
(ListItem), ์ด๋ ๊ฒ 3๊ฐ์ ํต์ฌ์ ๋ณด๋ง ์ ๋ฌํ๋ค. ๋ฌดํ์คํฌ๋กค๋ก ๋ฐ์์จ ๋ฐ์ดํฐ์ ListItem์ ์ด์ฉํด infiniteListElement
๋ฅผ ๋ง๋ค์ด ๋ฐํํ๋ค.
<EventList>{infiniteListElement}</EventList>
์ด๋ ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
์ ์ฒด Hook์ ์ฝ๋์ด๋ค. observer
์์ ์คํฌ๋กค์ ๋์ ์ฐพ์ผ๋ฉด ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ํจ์นญํด์ค๊ณ , map
์ผ๋ก ListItem
์ปดํฌ๋ํธ๋ฅผ ๋ฐ๋ณตํ๋ ํ๋ฆ์ด ์จ๊ฒจ์ ธ์๋ค.
export const useInfiniteQueriesList = <T,>(
queryKey: QueryKey,
apiFunction: (payload: any) => Promise<InfiniteResponse<T>>,
ListItem: (props: any) => JSX.Element,
options?: UseInfiniteQueryOptions<
InfiniteResponse<T>,
AxiosError,
InfiniteResponse<T>,
InfiniteResponse<T>,
QueryKey
>,
) => {
const { data, fetchNextPage } = useInfiniteQuery<
InfiniteResponse<T>,
AxiosError
>(queryKey, apiFunction, {
getNextPageParam: (lastPage) => lastPage.page + 1,
...options,
});
const [ref, inView] = useInView();
/* observer ๊ด๋ จ... */
const observer = (
<div className="observer" ref={ref} style={{ height: '1px' }} />
);
const listElement = data?.pages.map(({ content }) =>
content.map((item, idx) => <ListItem {...item} key={`item-${idx}`} />),
);
const isEmpty = data?.pages[0].content.length === 0;
return {
infiniteListElement: (
<>
{listElement}
{observer}
</>
),
isEmpty,
};
};
ํด๋น Hook์ ์ด์ฉํด ๋ค์ํ ํ์ด์ง์์ ๋ฌดํ์คํฌ๋กค์ ์ฌ์ฉํ ์ ์๋๋ก ์ ๋ค๋ฆญ์ ์ด์ฉํ๊ณ ์๋ค. QueryFnData์ ํ์
์ผ๋ก InfiniteResponse<T>
๋ฅผ ๋ฃ์ด์ฃผ๊ณ ์๋ค.
export interface InfiniteResponse<T> {
content: T[];
page: number;
size: number;
hasNext: boolean;
}
๋ฌดํ์คํฌ๋กค๋ก ๋ฐ์์ค๋ ๋ฐ์ดํฐ์ ํ์ ์ ๋ชจ๋ ์์ ๊ฐ์ด ํต์ผ๋์ด ์๋ค. content๋ก ๋ค์ํ ํ์ ์ ๊ฐ์ด ๋ค์ด์ฌ ์ ์๋ค. ์ฟผ๋ฆฌ ์ต์ ๋ ์ต์ ๋๋ก ๋ฐ๊ณ ์๊ธฐ ๋๋ฌธ์ ์์ ๋กญ๊ฒ ๋ฃ์ด ์ฌ์ฉํ ์ ์๋ค. ์ค์ ๋ก ์์ํก์ 2์ด๋ง๋ค pullingํ๋๋ก ํ๋ ์ต์ ์ด ์ค์ ๋์ด ์๋ค.
Overlay ์ปดํฌ๋ํธ
๋ชจ๋ฌ์ฐฝ ์ญ์ ๊ณ ์ค๋ฝ ํฐ์ผ์์๋ถํฐ ๊ณ์ ์จ์ค๋ UI์๋ค. ๋๋ฅ์ ๊ฐ๋ฐํ๋ฉด์ ๋ชจ๋ฐ์ผ์ผ ๋ ํ๋ฉด ์๋์์ ์ฌ๋ผ์ค๋๊ฒ ๋ ์์ฐ์ค๋ฝ๋ค๊ณ ์๊ฐํด์ ๋ฐํ ์ํธ๋ก, PC์์ ๋ชจ๋ฌ๋ก ์ค๋ฒ๋ ์ด๋ฅผ ๋์ฐ๋๋ก ํ๋ค.
const { isOpen, openOverlay, closeOverlay } = useOverlay();
<OverlayBox open={isOpen} onDismiss={closeOverlay}>
<SelectTicket items={tickets?.ticketItems} eventName={detail.name} />
</OverlayBox>
Popup ์ปดํฌ๋ํธ
์ด๋๋ฏผํ์ด์ง์์ ํ ์ด๋ธ ๋ฉ๋ด๋ฅผ ํด๋ฆญํ์ ๋ ๋จ๋ ํ์ ๋ฉ๋ด ์ปดํฌ๋ํธ์ด๋ค. ์ด ์ธ์๋ ํค๋์ ํ๋กํ์ ํด๋ฆญํ์ ๋, ๊ฒ์ ์ต์ ์ ๋ณ๊ฒฝํ ๋ ํ์ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ค.
<Popup options={approveWaitingOptions}>
<Icon name="threeDot" />
</Popup>;
๊ทธ๋ด ๋ Popup
์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค. children
์ผ๋ก ํ์
๋ฒํผ์ผ๋ก ์ฌ์ฉํ ์ปดํฌ๋ํธ๋ฅผ ๋ฃ์ด์ค๋ค. ์ด๋ค ์ต์
์ ๋ณด์ฌ์ค์ง PopupOptions[]
ํ์
์ ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๊ธฐ๋ง ํ๋ฉด ๋์ด๊ธฐ ๋๋ฌธ์ ์ ์ธ์ ์ธ ์ฝ๋๋ก ๋ณผ ์ ์๋ค.
const approveWaitingOptions: PopupOptions[] = [
{
text: '์น์ธํ๊ธฐ',
onClick: () => {
approveMutate({ eventId, order_uuid: data.orderUuid });
},
},
{
text: '์์ธํ ๋ณด๊ธฐ',
onClick: () => {
openOverlay({
content: 'tableViewDetail',
props: { eventId, order_uuid: data.orderUuid },
});
},
},
];
์๋ฅผ ๋ค์ด, approveWatingOptions
๋ text
์ onClick
์ ์์ฑ์ผ๋ก ๊ฐ๋ ๊ฐ์ฒด์ ๋ฐฐ์ด์ด๋ค. ์น์ธ ๋๊ธฐ์ค์ธ ์ฃผ๋ฌธ์ ์ ๋ณด๋ฅผ ํ์ธํ๊ฑฐ๋ ์ฃผ๋ฌธ์ ์น์ธํ ๋ ์ฌ์ฉํ๋ค. ์์์ ์์ฑํ๋ useOverlay
Hook์ openOverlay
ํจ์๋ฅผ ์ฌ์ฉํด ๋ชจ๋ฌ์ ์ฌ๋ ์ต์
์์ ์ ์ ์๋ค!
https://sangmin802.github.io/Study/Think/abstract%20painting/
https://toss.tech/article/frontend-declarative-code
https://toss.im/slash-21/sessions/3-3