Skip to content

Commit

Permalink
Merge pull request #53 from Team-INSERT/feat/httpClient
Browse files Browse the repository at this point in the history
feat : httpClient 정의
  • Loading branch information
Ubinquitous authored Mar 9, 2024
2 parents 3243a51 + 654dc8d commit 7faf89b
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 6 deletions.
128 changes: 128 additions & 0 deletions apis/httpClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
import { exception } from "@/constants/exception.constant";
import { Storage } from "@/storage";
import { TOKEN } from "@/constants/token.constant";
import { requestInterceptors, responseInterceptors } from "./interceptors";
import refresh from "./refresh";

export interface HttpClientConfig {
baseURL?: string;
timeout?: number;
headers?: { Authorization?: string };
}

export class HttpClient {
private api: AxiosInstance;

private static clientConfig: HttpClientConfig;

constructor(url: string, axiosConfig: HttpClientConfig) {
this.api = axios.create({
...axiosConfig,
baseURL: `${axiosConfig.baseURL}${url}`,
});
HttpClient.clientConfig = { headers: { Authorization: "" } };
this.setting();
}

get(requestConfig?: AxiosRequestConfig) {
return this.api.get("", { ...HttpClient.clientConfig, ...requestConfig });
}

getById(requestConfig?: AxiosRequestConfig) {
return this.api.get("/:id", {
...HttpClient.clientConfig,
...requestConfig,
});
}

post(data: unknown, requestConfig?: AxiosRequestConfig) {
return this.api.post("", data, {
...HttpClient.clientConfig,
...requestConfig,
});
}

put(data: unknown, requestConfig?: AxiosRequestConfig) {
return this.api.put("", data, {
...HttpClient.clientConfig,
...requestConfig,
});
}

delete(requestConfig?: AxiosRequestConfig) {
return this.api.delete("", {
...HttpClient.clientConfig,
...requestConfig,
});
}

private setting() {
HttpClient.setCommonInterceptors(this.api);

this.api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response.data.code === exception.code.TOKEN_403_2) {
await refresh();
return this.api(originalRequest);
}

return Promise.reject(error);
},
);
}

static setAccessToken() {
const accessToken = Storage.getItem(TOKEN.ACCESS);
HttpClient.clientConfig.headers = {
...HttpClient.clientConfig.headers,
Authorization: accessToken ?? undefined,
};
}

static removeAccessToken() {
Storage.setItem(TOKEN.ACCESS, "");
}

private static setCommonInterceptors(instance: AxiosInstance) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
instance.interceptors.request.use(requestInterceptors as any);
instance.interceptors.response.use(responseInterceptors);
}
}

const axiosConfig: HttpClientConfig = {
baseURL: "https://buma.wiki/",
timeout: 10000,
};

// eslint-disable-next-line
export default {
static: new HttpClient("api/docs", axiosConfig),
docs: new HttpClient("api/docs/find/title", axiosConfig),
refreshToken: new HttpClient("api/auth/refresh/access", axiosConfig),
myuser: new HttpClient("api/user", axiosConfig),
user: new HttpClient("api/user/id", axiosConfig),
oauth: new HttpClient("api/auth/oauth/bsm", axiosConfig),
logout: new HttpClient("api/auth/bsm/logout", axiosConfig),
create: new HttpClient("api/docs/create", axiosConfig),
update: new HttpClient("api/docs/update", axiosConfig),
updateType: new HttpClient("api/docs/update/docsType", axiosConfig),
version: new HttpClient("api/docs/find/", axiosConfig),
different: new HttpClient("api/docs/find/version", axiosConfig),
lastModified: new HttpClient("api/docs/find/modified", axiosConfig),
search: new HttpClient("api/docs/find/all/title", axiosConfig),
updateTitle: new HttpClient("api/docs/update/title", axiosConfig),
deleteDocs: new HttpClient("api/docs/delete/", axiosConfig),
authority: new HttpClient("api/set/authority", axiosConfig),
getMyLike: new HttpClient("api/thumbs/up/get", axiosConfig),
getLike: new HttpClient("api/docs/thumbs/up/get", axiosConfig),
createLike: new HttpClient("api/thumbs/up/create", axiosConfig),
deleteLike: new HttpClient("api/thumbs/up/delete", axiosConfig),
isLike: new HttpClient("api/docs/like", axiosConfig),
revalidateDocs: new HttpClient("api/revalidate-docs", axiosConfig),
revalidateUpdate: new HttpClient("api/revalidate-update", axiosConfig),
revalidateVersion: new HttpClient("api/revalidate-version", axiosConfig),
};
1 change: 1 addition & 0 deletions apis/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as httpClient } from "./httpClient";
36 changes: 36 additions & 0 deletions apis/interceptors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { AxiosRequestConfig, AxiosResponse } from "axios";
import { Storage } from "@/storage";
import { TOKEN } from "@/constants/token.constant";

export const requestInterceptors = async (
requestConfig: AxiosRequestConfig,
) => {
if (requestConfig.headers) {
requestConfig.headers.Authorization = Storage.getItem(TOKEN.ACCESS);
}

const urlParams = requestConfig.url?.split("/:") || [];
if (urlParams.length < 2) return requestConfig;

const paramParsedUrl = urlParams
?.map((paramKey) => {
return requestConfig.params[paramKey];
})
.join("/");

urlParams?.forEach((paramKey: string) => {
delete requestConfig.params[paramKey];
}, {});

return {
...requestConfig,
url: paramParsedUrl,
};
};

export const responseInterceptors = async (response: AxiosResponse) => {
return {
...response,
data: response.data,
};
};
20 changes: 20 additions & 0 deletions apis/refresh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { TOKEN } from "@/constants/token.constant";
import { Storage } from "@/storage";
import axios from "axios";

const instance = axios.create({
baseURL: process.env.NEXT_PUBLIC_BASE_URL,
});

const refresh = async () => {
try {
const { data } = await instance.put("/api/auth/refresh/access", {
refreshToken: `${Storage.getItem(TOKEN.REFRESH)}`,
});
Storage.setItem(TOKEN.ACCESS, data.accessToken);
} catch (err) {
Storage.clear();
}
};

export default refresh;
4 changes: 1 addition & 3 deletions constants/authority.constant.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
const authority = {
export const authority = {
ADMIN: "ADMIN",
READONLY: "BANNED",
USER: "USER",
};

export default authority;
4 changes: 1 addition & 3 deletions constants/exception.constant.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const exception = {
export const exception = {
code: {
IMG_400_1: "IMG-400-1",
DOCS_404_1: "DOCS-404-1",
Expand All @@ -20,5 +20,3 @@ const exception = {
SERVER_ERROR: 500,
},
};

export default exception;
4 changes: 4 additions & 0 deletions constants/token.constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const TOKEN = {
ACCESS: "access_token",
REFRESH: "refresh_token",
} as const;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@typescript-eslint/parser": "^7.1.0",
"@vanilla-extract/css": "^1.14.1",
"autoprefixer": "^10.4.18",
"axios": "^1.6.7",
"babel-loader": "^9.1.3",
"babel-plugin-styled-components": "^2.1.4",
"babel-plugin-transform-remove-imports": "^1.7.1",
Expand Down
48 changes: 48 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions storage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
type LocalStorageKey = "access_token" | "refresh_token" | "autoComplete";

export class Storage {
private static isWindowAvailable() {
return typeof window !== "undefined";
}

static getItem(key: LocalStorageKey) {
if (typeof window !== "undefined") return localStorage.getItem(key);
}

static setItem(key: LocalStorageKey, value: string) {
if (typeof window === "undefined") return;
localStorage.setItem(key, value);
}

static delItem(key: LocalStorageKey) {
if (typeof window === "undefined") return;
localStorage.removeItem(key);
}

static clear() {
if (this.isWindowAvailable()) localStorage.clear();
}
}

0 comments on commit 7faf89b

Please sign in to comment.