프론트엔드
프론트엔드의 코딩 컨벤션
Last updated
프론트엔드의 코딩 컨벤션
Last updated
자세한 코드 예시는
외부에서 사용하지 않더라도 export
를 한다.
당연한 내용이라도 TSDoc
으로 설명을 작성한다.
타입명은 I or T
+ HTTP Method
+ 행위
+ APIRequest
형태로 만든다.
전달 방식에 따라서 body
, params
, queries
로 나눠서 받는다.
/** 게시글 수정 요청 타입 */
export interface IPatchPostAPIRequest {
params: { postId: Post["id"] };
body: Partial<ICreatePostAPIRequest["body"]>;
}
외부에서 사용하지 않더라도 export
를 한다.
당연한 내용이라도 TSDoc
으로 설명을 작성한다.
타입명은 I or T
+ HTTP Method
+ 행위
+ APIResponse
형태로 만든다
/** 게시글 수정 응답 타입 */
export interface IPatchPostAPIResponse extends IAPIResponse<Post> {}
당연한 내용이라도 TSDoc
으로 설명을 작성한다.
함수명은 HTTP Method
+ 행위
+ API
형태로 만든다
endpoint
, key
, fn
으로 분리된 객체로 만들어서 사용
/** 게시글 수정 요청 타입 */
export interface IPatchPostAPIRequest {
params: { postId: Post["id"] };
body: Partial<ICreatePostAPIRequest["body"]>;
}
/** 게시글 수정 응답 타입 */
export interface IPatchPostAPIResponse extends IAPIResponse<Post> {}
/** 게시글 수정 함수 */
export const patchPostAPI = async ({
body,
params,
}: IPatchPostAPIRequest): Promise<IPatchPostAPIResponse> => {
return fetchInstance(postApis.patch.endPoint({ params }), {
method: "PATCH",
body: JSON.stringify(body),
})
.then(fetchInstanceHandleResponse)
.catch(fetchInstanceHandleError);
};
export const postApis = {
patch: {
endPoint: ({ params }: Pick<IPatchPostAPIRequest, "params">) =>
SERVER_URL + `/apis/v1/posts/${params.postId}`,
key: ({ params }: Pick<IPatchPostAPIRequest, "params">) => [
"patch",
"posts",
params.postId,
],
fn: patchPostAPI,
},
};
// 최종적으로 api들 하나의 객체로 묶기
export const apis = {
posts: {
...postApis,
},
};
// 직접 사용
api.posts.patch.fn();
// tanstack-query 사용
const {} = useQuery({
queryKey: api.posts.getAll.key(),
queryFn: api.posts.getAll.fn(),
});
GET
요청을 제외한 API
관련 mutate
를 하나로 묶어서 관리
성공과 실패의 경우는 훅에서 처리 ( 특수한 경우가 아니라면 컴포넌트 내부에서 처리하지 않음 )
import { useRouter } from "next/navigation";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import {
apis,
ICheckUniqueTitleAPIRequest,
ICheckUniqueTitleAPIResponse,
ICreatePostAPIRequest,
ICreatePostAPIResponse,
IDeletePostAPIRequest,
IDeletePostAPIResponse,
IPatchPostAPIRequest,
IPatchPostAPIResponse,
} from "#fe/apis";
import { routes } from "#fe/constants";
import { revalidateTagForServer } from "#fe/actions/revalidateForServer";
const usePostMutations = () => {
const router = useRouter();
const queryClient = useQueryClient();
const { mutateAsync: createPostMutateAsync } = useMutation<
ICreatePostAPIResponse,
Error,
ICreatePostAPIRequest
>({
mutationFn: ({ body }) => apis.posts.create.fn({ body }),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: apis.posts.getAll.key(),
});
revalidateTagForServer(apis.posts.getAll.key());
router.replace(routes.post.url);
},
});
const { mutateAsync: patchPostMutateAsync } = useMutation<
IPatchPostAPIResponse,
Error,
IPatchPostAPIRequest
>({
mutationFn: ({ body, params }) => apis.posts.patch.fn({ body, params }),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: apis.posts.getAll.key(),
});
revalidateTagForServer(apis.posts.getAll.key());
router.replace(routes.post.url);
},
});
const { mutateAsync: deletePostMutateAsync } = useMutation<
IDeletePostAPIResponse,
Error,
IDeletePostAPIRequest
>({
mutationFn: ({ params }) => apis.posts.delete.fn({ params }),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: apis.posts.getAll.key(),
});
router.replace(routes.post.url);
},
});
const { mutateAsync: checkUniqueTitleMutateAsync } = useMutation<
ICheckUniqueTitleAPIResponse,
Error,
ICheckUniqueTitleAPIRequest
>({
mutationFn: ({ body }) => apis.posts.checkUniqueTitle.fn({ body }),
});
return {
createPostMutateAsync,
patchPostMutateAsync,
deletePostMutateAsync,
checkUniqueTitleMutateAsync,
};
};
export default usePostMutations;