π μ΄κ΄μ νκ² λ λ°°κ²½
κΈ°μ‘΄μ μ λ¬μ§μμΌν°λ Next.js, react-query, shadcn/ui μ νκ²½μμ κ°λ°μ μ§ννμλ€. 짧μ μκ° μμ λΉ λ₯Έ κ°λ°, λΉλ μκ° λ¨μΆ, νμ₯ ν μ μλ 컀μ€ν μ‘°κ±΄μ΄ μ κΈ°μ μ μ±ν μ΄μ μ΄κΈ°λ νλ€. (μ°Έκ³ μν°ν΄)
1μ°¨,2μ°¨ λ°°ν¬κΉμ§λ§ ν΄λ μ°λ¦¬κ° μ νν κΈ°μ μ΄ μ¬λ°λ₯΄λ€κ³ νλ¨νλ€.
3μ°¨ μ€νμ μ€λΉνκΈ° μ μ, μ΄μ μ λΉ λ₯Έ κ°λ°λ‘ μΈν΄ λ―Έλ€λμλ μ½λ κ°μ μμ μ μμν κ²μ΄ μ΄κ΄μ μμ΄μ μ΄λ€.
1οΈβ£ κ°λ°μλ§λ€ λ€λ₯Έ μ½λ μ€νμΌ
μ°λ¦¬μ 컨벀μ μ μ νκΈ΄ νμ§λ§, νμ΄μ§λ§λ€ μμ±ν κ°λ° μ½λμ μ€νμΌμ΄ μ¨μ ν μΌμΉνμ§λ μμλ€.
κ·Έλμ μ€λ₯κ° λ°μ νμλ ν΄λΉ μ½λλ₯Ό μμ±νμ§ μμ μ¬λμ΄ κ°μ μ μ§ννλ €κ³ νλ©΄ κ°λ° λΉμ©μ΄ λ§μ΄ νμνλ€.
2οΈβ£ μ§κ΄μ μ΄μ§ μμ μ½λ λ² μ΄μ€
ν μ΄λΈ μ체μ λν κ³΅ν΅ μ»΄ν¬λνΈλ μ§μ ꡬνν΄ λμκ³ , μΈλΆμμ ν μ΄λΈ 리μ€νΈ λ°μ΄ν°λ₯Ό λΆλ¬μ€κ³ μ£Όμ νλ λ°©μμΌλ‘ μ¬μ©νμλ€.
νμ§λ§, APIλ§λ€ λ°μ΄ν°λ₯Ό λ³ννλ λ°©μ, νμ΄μ§ μλ₯Ό κ°μ Έμ€λ λ°©μμ΄ λ¬λΌ νμ΄μ§ λ΄λΆμ λ¨μν 리μ€νΈ μ»΄ν¬λνΈλ§ 보μ¬μ£Όλ κ²μ΄ μλ λ°μ΄ν°λ₯Ό ν΄λΌμ΄μΈνΈ νκ²½μ λ§κ² λ³ννλ λ‘μ§λ€μ΄ μμ¬ μμ΄ νμ΄μ§ λμμ μ§κ΄μ μΌλ‘ νμ νκΈ° μ΄λ €μ λ€.
3οΈβ£ APIλ§λ€ λ€λ₯Έ μ€νμ λ§μΆ° λ³ννλ κ³Όμ μ λΆμ°ν
μ λ¬μ§μμΌν°μμ νΈμΆνλ API λλ©μΈμ μλμ κ°λ€.
- seller-api (μ λ¬)
- partner-api (κ΄λ¦¬μ)
- order-api (μ£Όλ¬Έ/κ²°μ )
- main-api (μ€κ³ λλΌ λ©μΈ)
- edge-api (μ΄λ―Έμ§)
ν΄λΉ APIλ₯Ό κ°λ°ν νλ€μ΄ λ€λ₯΄λ€ 보λ λ°μ΄ν°λ₯Ό λ°ννλ νμμ΄ λ€λ₯΄κ² μ€κ³λμ΄ μμλ€.
κ·Έλμ ν΄λΌμ΄μΈνΈ λ΄λΆμ μΌλ‘ κ³΅ν΅ μ»΄ν¬λνΈλ₯Ό λ§λ€μ΄λμμ§λ§, λ§μ½ ν νμ΄μ§μ μ¬λ¬ λλ©μΈμ APIλ₯Ό νΈμΆνμ κ²½μ°μλ μ΄λ₯Ό ν΄λΌμ΄μΈνΈ νμμ λ§κ² λ³ννλ κ³Όμ μ΄ νμ΄μ§ λ΄λΆμ μ¬λ¬ μ νΈμ΄ λ€μ΄κ°μΌλ‘μ¨ κ°λ μ±μ΄ λ¨μ΄μ§λ νμμ΄ λ°μνμλ€.
λλΆμ΄ λ³ννλ κ³Όμ μ λͺ¨λ λͺ¨λλ‘ κ΄λ¦¬νμ§ μμκΈ° λλ¬Έμ,
ν΄λΉ λ°©μ λν κ°λ°μλ§λ€ μ€νμΌμ΄ λ¬λΌμ Έ μ½λμ νλ¦μ νμ
νκΈ° μ΄λ €μ λ€.
νμ§λ§, μ΄λλ―Ό μμ€ν μ νΉμ±μ ν μ΄λΈμ μ¬μ©νλ νμ΄μ§, μ 보λ₯Ό 보μ¬μ£Όλ νμ΄μ§λ μΌκ΄μ±μ κ°μ§λλ‘ κ΅¬νλλ κ²μ΄ νμνκ³
λ·°μ μμλ§μ΄ μλ λ΄λΆ μ½λ μμ€ν λ λμΌνκ² κ΄λ¦¬λμ΄μΌ μΆκ° κΈ°λ₯μ λν΄ μ¬λ¬ νμ΄μ§λ₯Ό ν λ²μ λμνλ κ²μ΄ κ°λ₯ν΄μ§λ€κ³ λκ»΄μ‘λ€.
μ΄ 3κ°μ§μ μ΄μ λ‘ λλ μ΄λ€ νΉμ κ·κ²©μ λ°λ₯Έ μ½λ λ² μ΄μ€λ₯Ό κ°μ§λ©΄μ νμ₯μ±μ κ³ λ €ν κΈ°μ μ λμ μ΄ νμνλ€κ³ νλ¨νλ€.
π€ Refineμ κ°λ΅ν νΉμ§
κ·Έλ κ² μ°Ύκ² λ κΈ°μ μ΄ λ°λ‘ Refineμ΄λ€. 곡μ λ¬Έμμλ μλμ κ°μ΄ μ€λͺ λμ΄ μλ€.
React κΈ°λ° λ΄λΆ λꡬ, κ΄λ¦¬μ ν¨λ, λμ보λ λ° μν°νλΌμ΄μ¦κΈ λΉμ¦λμ€ μ ν리μΌμ΄μ μ ꡬμΆνλλ‘ μ€κ³λ μ€νμμ€ νλ μμν¬μ΄λ€. CRUD, μν κ΄λ¦¬μ κ°μ μΌλ°μ μΈ κΈ°λ₯μ μ²λ¦¬νκΈ° μν μλν μΈνΈλ₯Ό μ 곡νμ¬ κ°λ° νλ‘μΈμ€λ₯Ό κ°μννλ€.
μ΄λ₯Ό ν΅ν΄ κ°λ°μλ λ°λ³΅μ μΈ μ½λ© μμ μ νΌνκ³ νλ‘μ νΈμ λ 볡μ‘ν μΈ‘λ©΄μ μ§μ€ν μ μλ€.
κΈ°λ³Έμ μΌλ‘ ν΄λΉ κΈ°μ μ Ant Design, Material UI, Shadcn/uiμ κ°μ UI νλ μμν¬μ ν΅ν©λ κ°λ₯νλ€.
Next.js, React Router, Remixλ μ§μνλ κ°λ ₯ν toolλ‘ μ¬μ©λκ³ μμλ€.
κ·Έλ λ€λ©΄ μ΄λ€ λ°λ³΅μ μΈ μ½λ© μμ μ νΌν μ μλ κ²μΌκΉ?
refineμ΄ μ μνκ³ μλ 컨μ μ Headless, Resource, Provider, Hook μ΄ 4κ°μ§μ΄λ©° μμΈν λ΄μ©μ λ¬Έμλ₯Ό ν΅ν΄ νμΈν μ μκΈ°μ ν΅μ¬ 컨μ μΈ providerμ resourceλ§ μ΄ν΄λ³΄κ² λ€.
1οΈβ£ Providerμ κ°λ
providerμλ data-provider, authentication-provider, access control-provider, notification-providerλ± λ€μν providerκ° μ‘΄μ¬νλ€. ν΅μ¬μ data-providerμ΄λ©° μ΄λ λ°±μλ λ°μ΄ν°μ get, post, path, deleteμ κ°μ λ°μ΄ν° μμ μ μ²λ¦¬νλ providerμ΄λ€.
import { DataProvider } from '@refinedev/core';
export const provider = ({
httpClient,
}: {
httpClient: AxiosInstance;
}): DataProvider => ({
getOne: async ({ resource, id, meta }) => {
const url = `${apiUrl}${resource ? `/${resource}` : ''}${id ? `/${id}` : ''}`;
try {
const { data } = await httpClient[requestMethod](url, { headers });
return { data: data.data || data.meta };
} catch (error) {
return Promise.reject(getHttpError(error));
}
},
update: async ({ resource, id, variables, meta }) => {
...
},
create: async ({ resource, variables, meta }) => {
...
},
deleteOne: async ({ resource, id, variables, meta }) => {
...
},
getList: async ({ resource, pagination, sorters, filters, meta }) => {
...
},
getApiUrl: () => apiUrl || '',
});
μ΄λ κ² providerλ΄λΆμλ getOne, update, create, deleteOne, getList, getApiUrlκ³Ό κ°μ λ©μλλ€μ΄ μ μλμ΄ μμΌλ©°
λ°±μλμ λλ©μΈμ λ°λΌ κ° providerλ₯Ό λ§λ€μ΄ λ°μ΄ν°λ₯Ό κ΄λ¦¬νλ κ²μ΄ κ°λ₯ν ꡬ쑰μ΄λ€.
2οΈβ£ Resourceμ κ°λ
리μμ€λ‘ λλ©μΈλ³ entityλ₯Ό μ μνκ³ , ν΄λΉ νμ΄μ§μμ κ°λ¨ν API νΈμΆ λ°©μμΌλ‘ μ½λμ λμμ μΆμννλ κ²μ΄ κ°λ₯ν΄μ§λ€.
μλμ μ½λλ‘ μ΄ν΄ ν΄λ³΄μ.
import { Refine } from "@refinedev/core";
export const App = () => {
return (
<Refine
resources={[
{
name: "seller",
list: "/account",
show: "/account/:id",
edit: "/account/:id/edit",
},
]}
>
{/* ... */}
</Refine>
);
};
resourceμ μ΄λ¦μΌλ‘ sellerλ₯Ό μ μν΄ λκ³ , μ λ¬μ 리μ€νΈλ₯Ό λΆλ¬μ€λ νμ΄μ§μ κ²½λ‘λ₯Ό listμ μ§μ νκ³ , νΉμ μ λ¬μ μ 보λ₯Ό 보μ¬μ£Όλ νμ΄μ§μ κ²½λ‘λ₯Ό showμ μ§μ νκ³ , νΉμ μ λ¬μ μ 보λ₯Ό νΈμ§νλ κ²½λ‘λ₯Ό showμ μ§μ νλ μ½λμ΄λ€.
μ΄λ κ² λλ©΄, /account νμ΄μ§λ useListλΌλ ν μ νΈμΆνλ©΄ providerμ getListκ° μλμΌλ‘ νΈμΆλκ³
/account/:id νμ΄μ§μμλ useShowλΌλ ν μ νΈμΆνλ©΄ providerμ getOneμ΄ μλμΌλ‘ νΈμΆλκ³ ,
/accoun/:id/edit νμ΄μ§μμλ useUpdateλΌλ ν μ νΈμΆνλ©΄ providerμ updateμ΄ μλμΌλ‘ νΈμΆλλ€.
μ¦, λ΄κ° νμ΄μ§μμ μ¬μ©ν apiμ μμμ 미리 μ μν΄ λ μΌλ‘μ¨ νμ΄μ§ λ΄λΆμμλ ν΄λΉ κ³Όμ μ ꡬνν νμ μμ΄ μλ΅ κ°μ λν λ°μ΄ν° λ λλ§ λ° μ¬μ©μμ λμμλ§ μ§μ€ν μ μκ² λλ κ²μ΄λ€.
π μ°λ¦¬κ° κ²μ¦ν λ΄μ©λ€
refineμ κΈ°μ μ체λ λ무λ 맀λ ₯μ μΌλ‘ λ€κ°μμΌλ, Next.js , react-query, shadcn/uiμ κ°μ κΈ°λ³Έμ μΈ ν΄λ‘λ μ΄λ―Έ μ λ¬μ§μμΌν°μ κΈ°λ₯μ λ§λ€μ΄κ° μ μλ λ₯λ ₯μ΄ μλ μνμμ ν΄λΉ κΈ°μ μ μ΄κ΄νλ κ²μΌλ‘ μμ 3κ°μ§μ λ¬Έμ λ₯Ό ν΄κ²°ν΄ μ€ μ μμκΉ..? μ λν κ²μ¦ μ μ°¨λ νμνλ€κ³ μκ°νλ€.
β interceptor λ‘μ§μ μ¬μ© κ°λ₯ μ¬λΆ λ° λ°μ΄ν° λ³ν κ³Όμ μ μμΉ
λ¨Όμ , μ λ¬μ κ΄λ¦¬μμ μΈμ¦/μΈκ° λ‘μ§μ Axiosμ interceptorμ λ΄λΆμμ κ΄λ¦¬λκ³ μμκΈ°μ ν΄λΉ λͺ¨λμ κ·Έλλ‘ μ μ©ν μ μλμ§ νμ ν΄ λ³΄μλ€. ν΄λΉ λͺ¨λμ μ μ©νλ κ²μ΄ κ°λ₯νκ³ , app routerμμ server componentμ client componentμ λ°λΌ λ€λ₯΄κ² μ€νλ μ μλλ‘ λΆλ¦¬νλ κ²λ κ°λ₯νμλ€.
μ΄λ₯Ό κΈ°λ°μΌλ‘ μμ λ¬Έμ μλ λ°μ΄ν° λ³ν κ³Όμ μ λΆμ°νλ₯Ό ν΄κ²°νλ κ²μ΄ κ°λ₯νμ§ κ²μ¦νλ€.
μ°μ data providerλ₯Ό κ° λλ©μΈμΌλ‘ λΆλ¦¬ν΄ 보μλ€.
- sellerProvider
- adminProvider
- orderProvider
- mainProvider
- edgeProvider
κ·Έλ¦¬κ³ λ°μ΄ν°λ₯Ό λ°ννμ¬ ν΄λΌμ΄μΈνΈμκ² μ λ¬ν λλ λμΌν κ·κ²©μ κ°μ§λλ‘ κ° providerμ λ°μ΄ν° λ³ν κ³Όμ μ λμ νμλ€.
// sellerProvider
...
getOne: async ({ resource, id, meta }) => {
const url = `${apiUrl}${resource ? `/${resource}` : ''}${id ? `/${id}` : ''}`;
const { headers, method } = meta ?? {};
const requestMethod = (method as MethodTypes) ?? 'get';
try {
const { data } = await httpClient[requestMethod](url, { headers });
return { data: data.data || data.meta };
} catch (error) {
return Promise.reject(getHttpError(error));
}
},
...
// orderProvider
...
getList: async ({ resource, pagination, sorters, filters, meta }) => {
const [countUrl] = resource.split('/list');
const url = `${apiUrl}/${resource}`;
const { headers: headersFromMeta, method, convertData } = meta ?? {};
...
try {
const { data, headers } = await httpClient[requestMethod](
`${url}?${queryString ? `${queryString}&` : ''}page=${pagination?.current}&size=${pagination?.pageSize}`,
{
headers: headersFromMeta,
},
);
if (convertData) {
const clientData = convertData({ data: data.data.results });
return {
data: clientData,
total: data.data.page.totalCount,
};
}
return {
data: data.data.results || data.data,
total: data.data.page.totalCount,
};
} catch (error) {
return Promise.reject(getHttpError(error));
}
},
...
μμ μ½λμ²λΌ μ λ¬μ μ£Όλ¬Έ/κ²°μ λλ©μΈμμ λ°μ΄ν°λ₯Ό κ°κ³΅νλ νμμ΄ λ€λ₯΄κ² ꡬνλμ΄ μμμ§λ§, λ°μ΄ν°λ₯Ό λ°ννλ ꡬ쑰λ₯Ό κ°μ²΄ νμμΌλ‘ ν΅μΌμν΄μΌλ‘μ¨ ν΄λΉ λ°μ΄ν°λ₯Ό μ¬μ©νλ κ³³μμλ data.{λ°μ΄ν°νμ } μΌλ‘ μ κ·Όνμ¬ μ¬μ©νλ κ²μ΄ κ°λ₯ν΄μ‘λ€.
μ΄λ₯Ό ν΅ν΄ λΆμ°λ μ½λλ€μ providerλΌλ κ°μ²΄ μμμ λλ©μΈλ³λ‘ κ΄λ¦¬ν μ μκ² λμλ€.
β Data Table νΈμΆ λ°©μ
κΈ°μ‘΄μ tableμ νΈμΆνλ ꡬ쑰λ μλμ κ°μλ€.
export default function AccountPage() {
// ν
μ΄λΈ νλ¨ νμ΄μ§λ€μ΄μ
λ²νΌ νΈλ€λ¬ 컀μ€ν
ν
const { previousBtnInfo, handleNextBtn, handlePageSize } =
useDataTableHandlers({ isNamePageSize: true });
const filter = ν
μ΄λΈ νν° κ²μμΌλ‘ API νΈμΆ 쿼리νλΌλ―Έν° λ°μμ€κΈ°;
const { data: sellerList, error } = useQuery(μ
λ¬ λ¦¬μ€νΈ λΆλ¬μ€κΈ°);
const { data: pageCount } = useQuery(리μ€νΈ κ΄λ ¨ νμ΄μ§ μ 보 λΆλ¬μ€κΈ°);
// λ€μ νμ΄μ§ λ€μ΄μ
λ²νΌ μ΄λ²€νΈ κ°μ²΄
const nextBtnInfo = {
handleNextBtn,
disabled: isNextBtnDisable({
dataLength: sellerList?.length,
pageCount,
query,
}),
};
return (
<ContentLayout className="p-5">
<Filter />
<DataTable
columns={columns}
data={sellerList || []}
previousBtnInfo={previousBtnInfo}
nextBtnInfo={nextBtnInfo}
pageCount={pageCount}
pageSize={Number(query.pageSize) || 10}
handlePageSize={handlePageSize}
/>
</ContentLayout>
);
}
Data Tableμ μ¬μ©νκΈ° μν΄μλ ν μ΄λΈμ μ£Όμ ν 리μ€νΈ μ 보, ν μ΄λΈ νλ¨ νμ΄μ§ λ€μ΄μ λ²νΌ μ 보 λ° νΈλ€λ¬, 리μ€νΈ ν μ΄λΈμ νμ΄μ§ μ¬μ΄μ¦ λ° νμ΄μ§ μλ€μ νμ΄μ§ λ¨μμ λ§λλ κ³Όμ μ΄ νμνλ€.
보μμ λͺ¨λ μ½λλ₯Ό 곡κ°ν μ μμ΄ μμΆνμμ§λ§, ν΄λΉ νμ΄μ§ λ΄λΆμμλ useQuery νΈμΆ μ selectλ₯Ό μ¬μ©νλ κ³Όμ λ ν¬ν¨λμ΄ μμλ€.
λλΆμ΄ filterμ κ²μ λ²νΌ μ΄λ²€νΈλ‘ λ§λ€μ΄μ§ queryλ₯Ό API νΈμΆ μ, 쿼리 νλΌλ―Έν°λ‘ μ λ¬νκΈ° μν κ³Όμ κ³Ό ν μ΄λΈμ κ΄λ¦¬νλ νμ΄μ§ λ€μ΄μ μ λν μ 보λ₯Ό μΈλΆμμ μ£Όμ λ°μ μ¬μ©νλ λ‘μ§μΌλ‘ μΈν΄ κ°λ μ±μ΄ λ¨μ΄μ§λ μνμλ€.
refineμμλ React tableμ νμ©ν useTable ν μ μ 곡νκ³ μμκ³ , ν΄λΉ ν μ μ¬μ©ν λ, resourceλ₯Ό λͺ μν΄ μ£Όλ©΄ ν΄λΉ ν λ΄λΆμμ resourceμ λ§λ providerλ₯Ό νΈμΆνμ¬ λ°νν λ°μ΄ν°λ₯Ό λ΄λΆμ μΌλ‘ λΏλ €μ£Όλλ‘ κ΅¬νλμ΄ μμλ€.
μ΄λ₯Ό μ μ©ν μ½λμ΄λ€.
export default function AccountPage() {
const { params } = useParsed();
return (
<DataTable
columns={columns}
refineCoreProps={{
resource: μ
λ¬λ¦¬μ€νΈμ 보,
meta: params?.filters,
dataProviderName: μ
λ¬μ 곡μ,
queryOptions: { refetchOnMount: true },
}}
FilterComponent={Filter}
/>
);
}
κ°μ νμ΄μ§λ₯Ό ꡬνν λ΄μ©μΈλ°, μ΄μ λ²μ λ³΄λ€ νμ€ν μ½λμ μμ μ€μ΄λ€μλ€λ κ²μ νμΈν μ μλ€.
μ½λκ° μ€μ΄λ€μλ€κ³ ν΄μ μ’μ μ½λλΌκ³ λ 보μ₯ν μ μκΈ°μ μ§κ΄μ±μ κ²μ¦ν΄ 보μ.
Data Tableμ λ§λ€κΈ° μν΄ refineCorePropsλ‘ μμμ μ 보, ν μ΄λΈ νν° μ 보λ₯Ό params.filtersλ‘, dataProviderμ μ΄λ¦μ λͺ μμ μΌλ‘ μμ±νκ³ , μνλ queryOptionsκ³Ό ν΄λΉ νμ΄μ§μμ νμν Filter Componentλ₯Ό μ£Όμ νμλ€.
μ΄λ€ μμμΌλ‘, μ΄λ€ APIλ₯Ό νΈμΆνκ³ , μ΄λ€ νν°λ₯Ό μ μ©ν 건μ§λ§
μ μΈμ μΌλ‘ μ λ¬νλ©΄ ν μ΄λΈμ λ§λ€ μ μκ² λμλ€.
μ΄μ λ³΄λ€ ν¨μ¬ κ°λ°μ κ²½νμ΄ κ°μ λμμμ κ²½νν μ μμλ€.
β Filter Component μ¬μ© λ°©μ
κΈ°μ‘΄μ μ¬μ©νκ³ μλ Filter componentμ ꡬ쑰μ΄λ€.
export default function Filter() {
const { query } = useRouter();
const methods = useForm<FormValues>({
defaultValues: { ...defaultValues, ...query },
});
const { getValues, setValue } = methods;
// νν°λ₯Ό queryλ‘ μ λ¬νμ¬ pageμμ ν΄λΉ queryλ₯Ό νμ±νμ¬ νΈμΆ νλΌλ―Έν°λ‘ μ λ¬νκΈ°μν push
const onSubmit = () => {
push({ query: { ...query, ...getValues(), page: 1 } });
};
// api λ§λ€ pageSize, sizeλ‘ μ λ¬ν μ§ νμ
νλ effect
useEffect(
function setPagesize() {
if (query.pageSize) setValue('pageSize', Number(query.pageSize));
},
[query?.pageSize],
);
return (
<Form {...methods}>
// ... νν° μμλ€ λ λλ§
</Form>
);
}
νν°λ€μ λν κ²μ λ²νΌμ submitν¨μλ‘ push μ΄λ²€νΈλ₯Ό λ°μμν€λ©°, μ΄λ₯Ό ν΅ν΄ pageμμλ pathμ query λ³νλ₯Ό κ°μ§νμ¬ API νΈμΆ μ, ν΄λΉ νν°λ₯Ό νΈμΆ νλΌλ―Έν°λ‘ λ³ννμ¬ μνλ νν°κ°μ ν΄λΉνλ 리μ€νΈλ₯Ό λ€μ λΆλ¬μ€λ κ΅¬μ‘°λ‘ μ€κ³λμ΄ μμλ€.
μΆκ°λ‘ λλ©μΈλ§λ€ νμ΄μ§μ μ¬μ΄μ¦λ₯Ό νΈμΆνλ νλΌλ―Έν°μ keyκ° pageSize λλ sizeλ‘ λλμ΄ μμ΄ μ΄λ₯Ό κ²μ¦νμ¬ setValueλ₯Ό μ§ννλ effectλ ꡬνλμ΄ μμλ€.
κ·Έλ¬λ refineμμ μ 곡νλ useTableκ³Ό data-providerλ₯Ό μ¬μ©νκ² λλ©΄μ Filter componentμ μ¬μ© λ°©μλ λ°λκ² λμλ€.
export default function Filter<TData>({ ids, getColumn }: FilterProps<TData>) {
const methods = useForm<FormValues>({
defaultValues: { ...defaultValues },
});
const { handleSubmit, reset, control, getValues } = methods;
const onClickSearch = () => {
const filterValues = getFilterValues({ formValue: getValues() });
setFilterValue({
ids,
filters: [...filterValues] as LogicalFilter[],
getColumn,
});
};
return (
<Form {...methods}>
...
</Form>
);
}
API νΈμΆ μ pageSizeμΈμ§, sizeμΈμ§ κ²μ¦νλ λ΄μ©μ provider λ΄λΆλ‘ λ€μ΄κ°κ² λμ΄
νν° μ»΄ν¬λνΈμμλ νν° κ²μμ λ°μλλonClickSearch μ΄λ²€νΈμμ useTableμ κ²μ 쑰건μΌλ‘ μ£Όμ
ν filterμ κ°λ§ νμμ λ§κ²
κ°κ³΅νμ¬ setFilterValueμ κ³Όμ λ§ κ±°μΉκ² λμλ€.
ν μ΄λΈμ λ§λλ λ°©μλ, νν°λ₯Ό λ§λλ λ°©μλ UIμλ§ μ§μ€ν μ μλ κ΅¬μ‘°λ‘ κ°μ λ κ²μ νμΈν μ μλ€.
+ λ€λ₯Έ λΌμ΄λΈλ¬λ¦¬μμ νΈνμ±
μ λ¬μ§μμΌν°μ νμ₯μ±μ κ³ λ €νμ κ²½μ°, μΆνμ λ λ€λ₯Έ κΈ°λ₯λ€μ΄ μꡬλμμ λ λ€λ₯Έ κΈ°μ λ€μ μ€μΉν μ μλμ§ νμΈν΄ 보λ κ³Όμ λ μ§ννλ€. νμΈ κ²°κ³Ό, refineμμ μ¬μ©νκ³ μλ TanStack queryμ λ²μ μ 5 μ΄μμΌλ‘ μ¬λ¦¬μ§λ§ μμΌλ©΄ λ€λ₯Έ λΌμ΄λΈλ¬λ¦¬λ μμνκ² μ€μΉλ¨μ νμ νμλ€.
μ΄λ¬ν κ·Όκ±°λ₯Ό κΈ°λ°μΌλ‘ μ΄μ μ λ°κ²¬λμλ 3κ°μ§μ λ¬Έμ λ₯Ό ν΄κ²°ν΄ μ£Όλ κ²μΌλ‘ νλ¨νμ¬,
μ΅μ’ μ μΌλ‘ μ λ¬μ§μμΌν°μ κ°λ° νκ²½μ refineμΌλ‘ μ΄κ΄νλ κ²μΌλ‘ κ²°μ νμλ€...!!!!!!
π ν΄λꡬ쑰λ κ°νΈνμ..!
μΆκ°λ‘ μ λ¬μ§μμΌν° 1μ°¨,2μ°¨ κ°λ°μ λΉμ ν΄λꡬ쑰λ μλμ κ°μλ€.
βββ π src
β βββ π api
β βββ π components
β βββ π constants
β βββ π hooks
β βββ π lib
β βββ π pages
β βββ π styles
β βββπ types
...
βββ tsconfig.json
νλ‘ νΈμλ κ°λ°μλΌλ©΄ μ΅μν ν΄λκ΅¬μ‘°μΌ κ²μ΄λ€.
νμ§λ§, 1μ°¨, 2μ°¨ κ°λ°κ³Ό μ€λ₯ κ°μ μμ μ μ§ννλ©΄μ ν΄λΉ μμ μ μν κ°λ°μ κ²½νμ λΉν¨μ¨μ 체κ°νκ² λμλ€.
κ³Όμ μ μλμ κ°λ€.
- νΉμ νμ΄μ§μμ μ€λ₯ λ°μ
- ν΄λΉ νμ΄μ§λ₯Ό νμνκΈ° μν΄ src/pages/{νμ΄μ§ μ΄λ¦} μ κ·Ό
- μ€λ₯ λ°μ μ»΄ν¬λνΈ νμ
- ν΄λΉ μ»΄ν¬λνΈ μ κ·Όμ μν΄ components/{νμ΄μ§ μ΄λ¦} ν΄λλ‘ μ΄λ
- λ΄λΆμ μΌλ‘ apiλ μμ, μ νΈ ν¨μμμ μ€λ₯κ° λ°μνλ€λ©΄ λ€μ {api, constants, utils}/{νμ΄μ§ μ΄λ¦} ν΄λλ‘ μ΄λ
λͺ¨λκ° μκ² μ§λ§, νλ‘μ νΈμμ 1κ° λλ 2κ°μ νμ΄μ§λ§ μλ κ²μ΄ μλκΈ° λλ¬Έμ ν΄λΉ κ³Όμ μ μ§ννκΈ° μν΄μλ vscode μ’μΈ‘μμ μ€ν¬λ‘€μ μμλλ‘ μ§ννλ κ³Όμ μ΄ κΈΈκ³ μ€λ λ°μνκ² λμλ€.
λ²κ·Έλ₯Ό μμ νλ κ²λ³΄λ€ ν΄λλ₯Ό μ°Ύλ κ³Όμ μΌλ‘ μΈν΄ νΌλ‘λκ° μ¦κ°νλ κ²μ λκΌλ€.
βββ π src
β βββ π identifier
β βββ π resources
β βββ π shared //μ
λ¬ & κ΄λ¦¬μ λλ©μΈμμ 곡μ λλ ν΄λ
β β βββ π components
β β βββ π hooks
β β βββ π api
...
β βββ π admin
β β βββ π shared // κ΄λ¦¬μ λλ©μΈμμ 곡μ λλ ν΄λ
β β βββ π account
...
β βββ π seller
β β βββ π shared // μ
λ¬ λλ©μΈμμ 곡μ λλ ν΄λ
β β βββ π info
βββ π components // μ»΄ν¬λνΈ π νμν΄λλͺ
(νμ€μΉΌμΌμ΄μ€.tsx)
βββ π hooks // 컀μ€ν
ν
π νμν΄λλͺ
(μΉ΄λ©μΌμ΄μ€.tsx)
βββ π utils // μ νΈν¨μ π νμν΄λλͺ
(μΉ΄λ©μΌμ΄μ€.tsx)
βββ π types
β βββ index.ts // types, constants, models, zods π νμν΄λλͺ
(index.ts)
βββ π constants
β βββ index.ts
βββ π models
β βββ index.ts
βββ π zods
β βββ index.ts
βββ π api // api ν΅μ κ΄λ ¨
...
β βββ π app
β β βββ π admin
β β βββ π seller
β β βββ _app.tsx
β β βββ _document.tsx
β β βββ index.tsx
...
βββ tsconfig.json
κ·Έλμ, refineμΌλ‘ μ΄κ΄ μμ μ μ§ννλ©΄μ ν΄λ ꡬ쑰λ₯Ό μμ κ°μ΄ κ΄μ¬μ¬λ³λ‘ κ°νΈνλ μμ λ ν¨κ» μ§ννμ¬ κ°λ°μ κ²½νμ κ°μ νκ³ μ νλ€. μ°Έκ³ νλ λ΄μ©μ FSD μν€ν μ³μ΄λ€.
μ΅μμ ν΄λλ€μ νμ΄μ§ κΈ°λ°μΌλ‘ λλλ©΄μ, λ΄λΆμ μΌλ‘ components, hooks, types, utils, constants, zods, api λ±κ³Ό κ°μ ν΄λλ₯Ό λ§λ€μ΄ νΉμ νμ΄μ§ ν΄λμμ κΈ°λ₯λ€μ νμνκ³ , ꡬνν μ μλλ‘ κ΅¬μ‘°λ₯Ό λ§λ€μ΄ μ€ν¬λ‘€μ μμλλ μκ°μ λ¨μΆνλ€.
λ¬Όλ‘ νμ΄μ§κ° λμ΄λ μλ‘ ν΄λμ κΉμ΄μ μκ° λμ΄λλ κ²μ΄ λ¨μ μ΄ λ μλ μλ€κ³ μκ°νλ€.
νμ§λ§, μ€ν¬λ‘€μ΄ λ¨μΆλλ μ΄μ μ΄ μμκΈ°μ ν΄λΉ ꡬ쑰λ₯Ό μ¬μ©νκ³ μ νλ€.
π€ κ·Έλμ μ΄κ΄ μ΄ν, κ°λ°μ κ²½νμ μ΄λ»κ² λ¬λΌμ‘μκΉ..?
μ²μμλ refineμ΄ μ 곡νλ ν κ³Ό ꡬ쑰λ₯Ό λ²μ΄λμ§ μκ² μ½λλ₯Ό μμ±ν΄μΌ νλ κ²μ΄ μ΄μ λ²μ μμλ μ‘΄μ¬νμ§ μμκΈ°μ λΆνΈν¨μ μ΄λνκΈ°λ νμλ€.
νμ§λ§, μ€νλ € refineμ΄λΌλ νΉμ κ·κ²©μ λ§κ² μμ±ν΄μΌ νλ κ²μ΄ μΌκ΄μ± μλ μ½λλ₯Ό λ§λ€μ΄μ£ΌμκΈ° λλ¬Έμ κ°λ°μλ§λ€ λ€λ₯Έ μ½λ μ€νμΌμ΄ λν μΆμλμ΄ λ΄κ° ꡬνν μ½λκ° μλμ΄λ μμ νλ κ²μ΄ μμν΄μ‘λ€.
λλΆμ΄ refineμ΄ μ ν΄μ€ νμ΄ μμ§λ§, κ·Έ μμμ μ»΄ν¬λνΈμ λ‘μ§μ 컀μ€ν νλ κ³Όμ λ κ°λ₯νλ€λ μ₯μ μ΄ μμκΈ°μ
κ°λ°μκ° μνλ μΆμν λ°©μμ΄ μλ€λ©΄ λμ νλ κ²λ λ¬Έμ μμ΄ μ§νν μ μμλ€.
μ₯κΈ°μ μΌλ‘ 보μμ λ, Refine μ΄κ΄ μμ μΌλ‘ μ λ¬μ§μμΌν°μ νμ₯κ³Ό μ μ§/보μμ κ°λ° μμ°μ±μ λμΌ μ μκ² λμλ€.
π©π»π» κ°λ°μ μΌλ‘ λ΄κ° λκΌλ λ΄μ©
μ λ¬μ§μμΌν° μΉ κ°λ°ν 리λ©μ μ§ννλ©°, νλ‘μ νΈλ₯Ό κ°λ°νκ³ μΌμ κ΄λ¦¬λ₯Ό νκ³ μλ κ²½νλ μλ‘μ΄ λμ μ μ°¨μμμ μλ―Έμλ κ²½νμ΄μλ€. νμ§λ§, νλ‘μ νΈμ κ·λͺ¨λ₯Ό ν€μλκ°λ μμ μμ μ’μ DXλ₯Ό μ 곡νλ©΄μ λΉ λ₯΄κ² νμ₯ν μ μλ λ°©λ²μ μ½λλ² μ΄μ€μμ κ³ λ―Όν μ μμλ€λ κ²μ΄ κ°λ°μμ μμΌλ₯Ό λνμ£Όκ² λ μκ°μ΄μλ κ² κ°λ€.
νΉν, μ΄λ₯Ό λ¨μν μ΄λ€ ν¨ν΄μ λμ νλ μ°¨μμμμ ν΄κ²°μ μ΄ μλ κΈ°μ μ μΈ μ°¨μμμ λ°©λ²μ λͺ¨μνκ³ κ²μ¦νλ κ³Όμ μ΄ ν₯λ―Έλ‘μ λ μκ°μ΄μλ€.
'WEB > Next.js' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
μ΄λ―Έμ§ μ΅μ νλ‘ μ±λ₯ κ°μ νκΈ° ( feat. FEW ) (0) | 2024.08.23 |
---|---|
Link νκ·Έμ SEO (0) | 2023.09.26 |
Next.js - V13 (0) | 2022.11.02 |
Next-auth (0) | 2022.07.12 |