본 글에서 사용된 Next.js는 app router를 적용하였습니다.
/app/api/vault
와 같이 폴더를 만들어주면, site_url/api/vault
로 api 요청을 보낼 수 있습니다.
이때, vault의 폴더 내부에는 route.js라는 이름의 파일을 만들어주어야 합니다.
미리 dummy.json이라는 이름의 더미 데이터 파일을 만들어 둡니다.
[
{
"id": 1,
"name": "WETH/USDT",
"chain": "arbitrum",
"v_address": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
},
{
"id": 2,
"name": "WETH/ARB",
"chain": "arbitrum",
"v_address": "0x82aF49447D8a07e3bd95BD0d56f35241523f1234"
},
{
"id": 3,
"name": "USDC/USDT",
"chain": "optimism",
"v_address": "0x123449447D8a07e3bd95BD0d56f35241523f5678"
},
{
"id": 4,
"name": "WETH/USDC",
"chain": "optimism",
"v_address": "0x38B26d26e575b70AE37f7390A22711500773A00E"
},
{
"id": 5,
"name": "WETH/DAI",
"chain": "arbitrum",
"v_address": "0xbc73933C8CA2b32ef3fB6B6aC8a53070EB05F9fd"
}
]
그리고, route.js 파일에 다음과 같은 코드를 작성해줍니다.
import { NextResponse } from "next/server";
import vault from "@/dummy.json";
export async function GET(request) {
const { searchParams } = new URL(request.url);
const name = searchParams.get("name");
const lowerCaseName = name?.toLowerCase();
const vaultData = vault.filter((item) => {
const lowerCaseItemName = item.name.toLowerCase();
return lowerCaseItemName.includes(name ?? "");
});
return NextResponse.json({ vaultData });
}
위 코드를 간단하게 살펴보면, 해당 url에 GET 요청으로 name 파라미터를 받을 경우에 해당 name을 가진 vaultData 를 반환해주고 있습니다.
이렇게 매우 간단하게 api 하나를 만들었습니다!
그럼 이제 이렇게 만든 API를 클라이언트 측에서 사용해보겠습니다.
본 글에서는 data fetching, loading handling 등을 한번에 가능하게 해주는 RTK query로 api를 사용할 예정입니다.
RTK query는 리덕스와 함께 사용할 수도 있고, 따로 사용할 수도 있는 본 글에서는 함께 사용하는 것을 기준으로 구현해보겠습니다.
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const vaultApi = createApi({
reducerPath: "vaultApi",
tagTypes: ["vault"],
baseQuery: fetchBaseQuery({
baseUrl: `${process.env.NEXT_PUBLIC_SITE_URL}/api`, // NEXT_PUBLIC_SITE_URL 관련해서는 아래에서 설명 예정
}),
endpoints: (builder) => ({
search: builder.query({
query: (q) => `vault?name=${q}`,
providesTags: (result, error, arg) => [{ type: "vault", search: arg }],
}),
}),
});
리덕스 툴킷의 createApi를 이용해서 위와 같이 어떤 url로 어떤 요청을 보낼지 정의해둡니다.
다음으로 reducer를 정의해서 사용할때와 마찬가지로 리덕스의 store 설정해줘야 합니다.
import { configureStore } from "@reduxjs/toolkit";
import { vaultApi } from "./vaultApi";
const store = configureStore({
reducer: {
[vaultApi.reducerPath]: vaultApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(vaultApi.middleware),
});
export default store;
이렇게 만든 vaultApi를 use ~ Query
형태의 훅으로 사용할 수 있습니다.
import { vaultApi } from "@/stores/vaultApi";
export default function VaultList() {
const { data, error, isFetching } = vaultApi.useSearchQuery(search);
return (
<>
<ul>
{isFetching ? (
<Stack spacing={2}>
<Skeleton variant="rounded" width={1020} height={118} />
<Skeleton variant="rounded" width={1020} height={118} />
<Skeleton variant="rounded" width={1020} height={118} />
<Skeleton variant="rounded" width={1020} height={118} />
<Skeleton variant="rounded" width={1020} height={118} />
</Stack>
) : (
data?.vaultData.map((vault) => {
return (
<li key={vault.v_address} className={classes.vaultListContainer}>
{ /* ... 중략 ... */ }
</li>
);
})
)}
</ul>
</>
);
이렇게 손쉽게 검색 기능이 적용된 data fetching, caching, error handling, loading handling 기능을 완료했습니다!
앞서 stores/vautlApi.js
에서 NEXT_PUBLIC_SITE_URL
이 나왔습니다. Next.js에서는 크게 3가지 방식으로 환경변수 기능을 지원합니다.
먼저 1번은 구동 환경이 development, production, test인지에 따라 각기 다른 값을 가지는 환경변수입니다. 이는 서버와 브라우저(서버 컴포넌트 & 클라이언트 컴포넌트)에서 모두 참조할 수 있는 환경변수입니다. development는 next dev로 구동하면 세팅되는 개발환경이고, production은 next build 후, next start로 구동할 때 세팅됩니다. 마지막으로 test는 테스트 환경에서 세팅될 수 있습니다.
두번째로 .env 파일을 설정해두면, 구동 환경에 맞는 파일이 자동으로 적용됩니다.
1) .env 파일 : 모든 환경에서 공통적으로 적용할 디폴트 환경변수를 정의한다. 가장 우선순위가 낮다.
2) .env.development 파일: 개발 환경(process.env.NODE_ENV === 'development') 에서 적용된다.
3) .env.production 파일: 배포/빌드 환경(process.env.NODE_ENV === 'production') 에서 적용된다.
4) .env.test 파일: 테스트 환경(process.env.NODE_ENV === 'test') 에서 적용된다.
5) .env.local 파일 : 가장 우선순위가 높다. 다른 파일들에 정의된 값들을 모두 덮어쓴다.(오버라이드)
마지막으로 NEXTPUBLIC은 브라우저 참조용 prefix입니다. 해당 prefix가 적용된 채로 환경변수를 세팅하면 서버와 브라우저 모두에서 환경변수에 접근이 가능합니다.
vercel은 next.js를 이용해서 웹 호스팅하기 매우 간단한 서비스입니다. 프로젝트의 main branch는 자동으로 production 모드로 감지된 후 배포됩니다. 추가적으로 특정 브랜치를 preview 모드로 배포할 수도 있는데, 저는 dev 브랜치를 preview 모드로 배포하는 설정을 해뒀습니다.
다음으로 환경변수를 사용하여 배포하기 위해선 프로젝트 셋팅에서 아래와 같이 각 배포 환경 별로 환경변수를 설정해주면 됩니다.
이제 main 브랜치 혹은 dev 브랜치로 병합을 할때마다 자동으로 배포가 진행됨을 확인할 수 있습니다!