1. fetchChampionRotation [TypeScript: 제네릭(Generic), 타입 지정]
async function fetchChampionRotation(): Promise<ChampionRotation> {
fetchChampionRotation함수가 반환하는 값의 타입을 제네릭을 사용하여 ChampionRotation라고 명시해줬다.
마치, 이 함수가 반환하는 값이 ChampionRotation타입의 데이터로 이루어진 Promise야!라고 말하는 거와 같다.
여기서 Promise를 쓴 이유는, 잠시 후 데이터를 반환할게라고 약속한는 형태로 동작하기 때문이다. 즉, 비동기 작업을 반환하는 의미다.
2. API Route
const response = await fetch("/api/rotation");
/page/api 폴더 안에 작성된 파일들이 자동으로 api 엔드포인트가 된다.
예를들어 pages/api/rotation.ts파일이 있으면, 위 코드처럼 작성하면 된다. 이때, 임포트는 불필요하다. 넥스트가 내부적으로 이 경로를 자동으로 처리하기 때문에 파일경로와 url경로가 매핑되어 별도의 임포트없이 사용 가능하다.
// src/app/api/rotation/route.ts
import { NextResponse } from "next/server";
export async function GET() {
const data = { message: "Hello, API Route!" };
return NextResponse.json(data);
}
// 위 코드처럼 작성하면 /api/rotation에서 GET요청을 처리할 수 있다.
3. useQuery
내가 가장 헷갈렸던 게 탄스택이다. 분명히 리액트에서는 화살표 함수를 이용하였는데, 넥스트에서는 모양새가 살짝 달랐다.
const {
data: rotationData,
isPending,
isError,
} = useQuery({
queryKey: ["championRotation"], // 이 쿼리의 고유 식별자
queryFn: async () => {
const response = await fetch("/api/rotation"); // 데이터를 가져오는 비동기 함수
return response.json();
},
});
위 코드는 리액트로 바꿔본 코드이다. queryFn에 비동기 함수를 화살표함수를 이용하여 처리하고 있다.
async function fetchChampionRotation() {
const response = await fetch("/api/rotation");
if (!response.ok) throw new Error("Failed to fetch rotation data");
return response.json();
}
useQuery({
queryKey: ["championRotation"],
queryFn: fetchChampionRotation, // 함수 참조
});
위 코드는 넥스트로 바꿔본 코드이다. 비동기 함수명을 그대로 넣어주고있다.
사실, 넥스트에서 화살표 함수도 가능한데, 프로젝트가 커질수록 동일한 로직을 여러 곳에서 사용할 일이 생기기 때문에 함수 분리가 더 편하다.
위 코드는 간결해서 재사용성이 좋은 건지 알 수 없다. 그래서 코드를 가져와봤다.
1) API 호출 로직을 분리
// src/utils/riotApi.ts
export async function fetchChampionRotation() {
const response = await fetch("https://ddragon.leagueoflegends.com/api/rotation");
if (!response.ok) {
throw new Error("Failed to fetch champion rotation data");
}
return response.json();
}
2) 클라이언트 컴포넌트에서 재사용
// src/app/rotation/page.tsx
"use client";
import { useQuery } from "@tanstack/react-query";
import { fetchChampionRotation } from "@/utils/riotApi";
export default function RotationClientComponent() {
const {
data: rotationData,
isPending: isLoading,
isError,
} = useQuery({
queryKey: ["championRotation"],
queryFn: fetchChampionRotation,
});
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error loading rotation data</div>;
return (
<ul>
{rotationData.freeChampionIds.map((id: number) => (
<li key={id}>Champion ID: {id}</li>
))}
</ul>
);
}
3) 서버 컴포넌트에서 재사용
// src/app/server-rotation/page.tsx
import { fetchChampionRotation } from "@/utils/riotApi";
export default async function RotationServerComponent() {
const rotationData = await fetchChampionRotation();
return (
<ul>
{rotationData.freeChampionIds.map((id: number) => (
<li key={id}>Champion ID: {id}</li>
))}
</ul>
);
}
4) 다른 컴포넌트에서 재사용
// src/pages/rotation-ssr.tsx
import { fetchChampionRotation } from "@/utils/riotApi";
export async function getServerSideProps() {
const rotationData = await fetchChampionRotation();
return { props: { rotationData } };
}
export default function RotationSSR({ rotationData }: { rotationData: any }) {
return (
<ul>
{rotationData.freeChampionIds.map((id: number) => (
<li key={id}>Champion ID: {id}</li>
))}
</ul>
);
}
갠플보다 팀플이 더 좋음 ^-^)b
'부캠 > TypeScript&Next' 카테고리의 다른 글
[NEXT]Basic zoom (2) middleware & supabase ssr (0) | 2024.12.21 |
---|---|
[NEXT]Basic zoom (1) route handler (0) | 2024.12.21 |
[NEXT] 마지막 갠 과제에 쓴 거 정리 (1) (1) | 2024.12.18 |
[NEXT]주요렌더링 (4) | 2024.12.09 |
[TYPESCRIPT]1주차 (2) | 2024.12.07 |