회사에서 프로젝트를 진행하며 불편했던 점을 개선하기 위해 시작했습니다.
평상시에는 아래와 같은 방법으로 사용했지만, 마음에 들지 않아 변경하기로 했습니다.
export const fetchUser = () => backendAPI.get("/user/read");
export const createUser = (userInfo) =>
backendAPI.post("/user/create", { userInfo });
export const updateUser = (userInfo) =>
backendAPI.post("/user/update", { userInfo });
export const deleteUser = (userInfo) =>
backendAPI.post("/user/delete", { userInfo });
바로 생각나는 코드 스타일을 작성했고, 총 4가지를 작성했습니다.
그리고 오픈 톡방, 지인 개발자분들께 소스를 공유하여 선호하는 코드 번호를 물어봤습니다.
// 1번
export const fetchUser = (userId) =>
backendAPI.get("/user/read", { params: { userId } });
// 2번
export function fetchUser(userId) {
return backendAPI({
method: "get",
url: "/user/read",
params: { userId },
});
}
// 3번
export const fetchUser = (userId) => {
return backendAPI.get("/user/read", { params: { userId } });
};
// 4번
export const fetchUser = (userId) =>
backendAPI({
method: "get",
url: "/user/read",
params: { userId },
});
투표 결과를 보고 2번 코드 스타일로 작성하기로 했습니다.
직관적이고 보기 좋다는 분들이 많았기 때문입니다.
투표 결과를 토대로 작성해봤지만, 직관적이지만
중복되는 소스가 너무 많다고 리팩토링을 시작했습니다.
API를 추가할 때마다 export function
, return backendAPI
을
계속 작성해야 해서 손가락이 아파, 해당 부분을 줄이기로 했습니다.
export function fetchUser(userId) {
return backendAPI({
method: "get",
url: "/user/read",
params: { userId },
});
}
export function createUser(userInfo) {
return backendAPI({
method: "post",
url: "/user/create",
data: userInfo,
});
}
export function updateUser(userInfo) {
return backendAPI({
method: "post",
url: "/user/update",
data: userInfo,
});
}
export function deleteUser(userInfo) {
return backendAPI({
method: "post",
url: "/user/delete",
data: userInfo,
});
}
export function
과 return backAPI
를 제외해서 작성하면
오브젝트에 메소드를 추가하는 형식으로 작성할 수 있습니다.
const usersAPI = {
fetchUser: (userId) => ({
method: "get",
url: "/user/read",
params: { userId },
}),
createUser: (userInfo) => ({
method: "post",
url: "/user/create",
data: userInfo,
}),
updateUser: (userInfo) => ({
method: "post",
url: "/user/update",
data: userInfo,
}),
deleteUser: (userInfo) => ({
method: "post",
url: "/user/delete",
data: userInfo,
}),
};
그리고 기존에 있던 유틸리티 함수를 하나 만들어서
이전처럼 동작하게 구현하면 됩니다.
export function addInstace(instance, instanceObjectAPI) {
const instanceMap = ([key, callback]) => [
key,
(...parameters) => instance(callback(...parameters)),
];
return Object.fromEntries(
Object.entries(instanceObjectAPI).map(instanceMap)
);
}
const usersAPI = addInstance(backendAPI, {
fetchUser: (userId) => ({
method: "get",
url: "/user/read",
params: { userId },
}),
createUser: (userInfo) => ({
method: "post",
url: "/user/create",
data: userInfo,
}),
updateUser: (userInfo) => ({
method: "post",
url: "/user/update",
data: userInfo,
}),
deleteUser: (userInfo) => ({
method: "post",
url: "/user/delete",
data: userInfo,
}),
});
export const {
fetchUser, // 조회
createUser, // 생성
updateUser, // 수정
deleteUser, // 삭제
} = usersAPI;
이전에 비해 직관적이며, 보기 좋아졌지만
그룹화와 Method 방식을 분류시키는 함수도 작성하기로 했습니다.
export function addInstaceGroup(instance, instanceObjectAPI) {
return Object.fromEntries(
Object.entries(instanceObjectAPI).map(([key, value]) => [
key,
addInstace(instance, value),
])
);
}
export function addInstaceMethod(instance, instanceObjectAPI) {
return Object.fromEntries(
Object.entries(instanceObjectAPI)
.filter(([method]) =>
["get", "post", "put", "delete"].includes(
method.toLocaleLowerCase()
)
)
.map(([method, value]) => {
const instanceMap = ([key, callback]) => [
key,
(...parameters) =>
instance({ ...callback(...parameters), method }),
];
return [
method,
Object.fromEntries(Object.entries(value).map(instanceMap)),
];
})
);
}
export function addInstaceGroupMethod(instance, instanceObjectAPI) {
return Object.fromEntries(
Object.entries(instanceObjectAPI).map(([key, value]) => [
key,
addInstaceMethod(instance, value),
])
);
}
그룹화를 추가하면 좀 더 객체 구조로 직관적으로 볼 수 있습니다.
const backendAPIGroup = addInstaceGroup(backendAPI, {
users: {
fetchUser: () => ({
method: "get",
url: "/user/read",
}),
createUser: (userInfo) => ({
method: "post",
url: "/user/create",
data: userInfo,
}),
updateUser: (userInfo) => ({
method: "post",
url: "/user/update",
data: userInfo,
}),
deleteUser: (userInfo) => ({
method: "post",
url: "/user/delete",
data: userInfo,
}),
},
});
export const {
users: {
fetchUser, // 유저 조회
createUser, // 유저 생성
updateUser, // 유저 수정
deleteUser, // 유저 삭제
},
} = backendAPIGroup;
Method를 사용하면 get
post
put
delete
4가지로 분류해서 확인할 수 있습니다.
처음 보는 사람입장에서는 객체 구조 만 봐도 호출하는 API method 방식을 이해할 수 있습니다.
const backendAPIMethod = addInstaceMethod(backendAPI, {
get: {
fetchUser: () => ({
url: "/user/read",
}),
},
post: {
createUser: (userInfo) => ({
url: "/user/create",
data: userInfo,
}),
updateUser: (userInfo) => ({
url: "/user/update",
data: userInfo,
}),
deleteUser: (userInfo) => ({
url: "/user/delete",
data: userInfo,
}),
},
});
export const {
get: {
fetchUser, // 유저 조회
},
post: {
createUser, // 유저 생성
updateUser, // 유저 수정
deleteUser, // 유저 삭제
},
} = backendAPIMethod;
Group의 Method를 합친 방법입니다.
소스의 네이밍에 따라 소스의 범위와 사용하는 Method 방식을 바로 파악할 수 있습니다.
const backendAPIGroupMethod = addInstaceGroupMethod(backendAPI, {
users: {
get: {
fetchUser: () => ({
url: "/user/read",
}),
},
post: {
createUser: (userInfo) => ({
url: "/user/create",
data: userInfo,
}),
updateUser: (userInfo) => ({
url: "/user/update",
data: userInfo,
}),
deleteUser: (userInfo) => ({
url: "/user/delete",
data: userInfo,
}),
},
},
});
export const {
users: {
get: {
fetchUser, // 유저 조회
},
post: {
createUser, // 유저 생성
updateUser, // 유저 수정
deleteUser, // 유저 삭제
},
},
} = backendAPIGroupMethod;
오늘 작성한 코드는 만족스러웠지만
다음에 좀 더 공부해서 좋은 코드를 작성하고 싶다.