axios 인스턴스 생성 -> 백엔드 접속정보로 create -> instance intercept (if(error){ retrun response.data } throw error) -> export
import axios from "axios";
const api = axios.create({
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=UTF-8",
Accept: "application/json",
},
baseURL: "http://3.36.113.120:1337/api",
});
api.interceptors.response.use(
function (response) {
return response;
},
function (error) {
return Promise.reject(error);
}
);
export default api;
fetch ? 를 하는것은 아니고, get요청으로 data를 fetch한다하여 fetch함수를 만들었다.
?populate=*
을 붙여줘야한다.export const fetchMember = async () => {
const res: AxiosResponse<Entity<MemberEntity>> = await api.get(
"/members?populate=*"
);
return res.data;
};
만약 res만 할경우, 위와같이 response 전체에해당하는 정보가 나오는데,
res.data를 리턴할 경우 아래 fetchMemberData와 같은 형태로 data만 빼낼수 있다!
strapi 에서 data 부분에 data와 meta 정보를 같이 제공해주기 때문에!
지금 api를 호출할 db table은 총 4개이다. (나중에 수정될 수 있지만!)
Fine, Member, Room, Todo
먼저 Member만 보았을때, 위에서 생성한 fetchMember()
를 호출한다면?
const [member, setMember] = useState<MemberEntity[]>([]);
const fetchData = async () => {
const res = await fetchMember();
setMember(
res.data.map((d) => {
return { id: d.id, ...d.attributes };
})
);
};
console.log(member);
useEffect(() => {
fetchData();
}, []);
각각의 api를 호출해줄때마다 위와같이 state를 set해줘야하고, 각각의 data를 fetch해주는 함수를 만들어서, 비동기로 get요청에따른 response값을 setState를 해줘야한다.
여기서 map을 돌려서 내가 원하는 타입의 값으로 반환해주는 작없을 한다.
제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법이다. 한번의 선언으로 다양한 타입에 재사용이 가능하다는 장점이 있다.
즉, 미래에 사용할 타입을 미리 지정해준다는 느낌이다.
우린 Member에 관련한 db table에서 필요한 값은 memberId, email, name, profileImg, createAt, updatedAt, publichedAt이다.
그런데 strapi에서 data에 담겨있는값의 형태는 {id : ..., attributes: {...} } 이다.
interface MemberEntity {
data: {
id: number;
attributes: {
id?: number;
memberId: string;
email: string;
name: string;
profileImg: string;
createdAt?: Date;
updatedAt?: Date;
publishedAt?: Date;
};
};
meta: any;
}
한단계 더 나아가서 생각해보자면, Member 말고 만약 Room과 관련된 table의 api를 확인해보자면,
attributes 내의 값만 다르고 나머지는 동일하다,
그렇다면 전체 Entity에 해당하는 type을 먼저만들자!
interface Entity<T> {
data: {
id: number;
attributes: T;
}[];
meta: any;
}
그리고, MemberEntity를 빼주자!
interface MemberEntity {
id?: number;
memberId: string;
email: string;
name: string;
profileImg: string;
createdAt?: Date;
updatedAt?: Date;
publishedAt?: Date;
}
이렇게 하면, 각각의 db table별로 entity를 만들면 전체 Entity 는 공유할 수 있다.
그런데, 이걸 모든 api를 요청할 때마다 반복되는 코드이니, useAPI라는 hook으로 빼보자!
import { useState, useEffect } from 'react';
interface Options {
isFetch: boolean;
}
export function useAPI<T>(fetchFn: /* fetch fn에 대한 타입 */any ,options: Options) {
const [data, setData] = useState<T[]>();
// fetchFn을 호출해서 데이터 처리
const fetchData = () => {
}
useEffect(() => {
// isFetch가 true이면 fetchData
}, [fetchFn])
return {
data,
}
}
options를 준 이유는 ?
지금이야 처음에 렌더링될 때 바로 fetch를 해오지만, 나중엔 버튼 클릭시, 뭐 이런 기능이 필요할 수 있기에 isFetch라는 boolean값으로 원할 때 사용할 수 있도록 하기 위해!
return을 generic 타입으로 해줌으로 재사용할 수 있음.
fetchFn 이 들어올때마다 useEffect으로 fetchData() 호출!
import { AxiosResponse } from "axios";
import { useState, useEffect } from "react";
import api from "../services/api";
interface Options {
isFetch: boolean;
}
export function useAPI<T>(fetchFn: () => Promise<Entity<T>>, options: Options) {
const [data, setData] = useState<T[]>([]);
// fetchFn을 호출해서 데이터 처리
const fetchData = async () => {
const res = await fetchFn();
setData(
res.data.map((d) => {
return { id: d.id, ...d.attributes };
})
);
};
useEffect(() => {
// isFetch가 true이면 fetchData
if (options.isFetch) {
fetchData();
}
}, [fetchFn]);
return {
data,
fetchData,
};
}
const { data: member } = useAPI<MemberEntity>(fetchMember, { isFetch: true });
먼저 member와 관련된 fetchMember를 호출할 것이다.
그런데, 데이터타입은 MemberEntity 를 반환할 것이고, data라는 변수의 이름을 디스트럭쳐링을 이용해 member라는 새로운 변수의 이름으로 할당해주었다.
<Entity<MemberEntity>>
의 형태로 console이 잘찍혔다!
var o = {p: 42, q: true};
var {p, q} = o;
console.log(p); // 42
console.log(q); // true
var a, b;
({a, b} = {a: 1, b: 2});
var o = {p: 42, q: true};
var {p: foo, q: bar} = o;
console.log(foo); // 42
console.log(bar); // true