diff --git a/.vscode/settings.json b/.vscode/settings.json
index 9c2cf86..88be7a4 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -2,6 +2,7 @@
"cSpell.words": ["Vitesse"],
"prettier.enable": false,
"typescript.tsdk": "node_modules/typescript/lib",
+ "editor.tabSize": 2,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
},
diff --git a/env/.env b/env/.env
index 1f84378..13e24d7 100644
--- a/env/.env
+++ b/env/.env
@@ -1 +1,2 @@
VITE_TEST=Test String
+VITE_API_BASE_URL=http://www.naver.com
diff --git a/env/.env.production b/env/.env.production
new file mode 100644
index 0000000..ba2569d
--- /dev/null
+++ b/env/.env.production
@@ -0,0 +1,2 @@
+VITE_TEST=Real Test String
+VITE_API_BASE_URL=http://www.naver.com
diff --git a/env/.env.staging b/env/.env.staging
index 1fd2d0c..5ab5084 100644
--- a/env/.env.staging
+++ b/env/.env.staging
@@ -1 +1,2 @@
VITE_TEST=Staging Test String
+VITE_API_BASE_URL=http://www.naver.com
diff --git a/package.json b/package.json
index 80d2e3a..d3da585 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"devDependencies": {
"@antfu/eslint-config": "^0.14.2",
"@iconify-json/carbon": "^1.0.12",
+ "@types/lodash": "^4.14.178",
"@types/node": "^17.0.5",
"@unocss/reset": "^0.16.4",
"@vitejs/plugin-vue": "^2.0.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1ffdc54..1eee459 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -3,6 +3,7 @@ lockfileVersion: 5.3
specifiers:
'@antfu/eslint-config': ^0.14.2
'@iconify-json/carbon': ^1.0.12
+ '@types/lodash': ^4.14.178
'@types/node': ^17.0.5
'@unocss/reset': ^0.16.4
'@vitejs/plugin-vue': ^2.0.1
@@ -35,6 +36,7 @@ dependencies:
devDependencies:
'@antfu/eslint-config': 0.14.2_eslint@8.5.0+typescript@4.5.4
'@iconify-json/carbon': 1.0.12
+ '@types/lodash': 4.14.178
'@types/node': 17.0.5
'@unocss/reset': 0.16.4
'@vitejs/plugin-vue': 2.0.1_vite@2.7.9+vue@3.2.26
@@ -459,6 +461,10 @@ packages:
resolution: {integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4=}
dev: true
+ /@types/lodash/4.14.178:
+ resolution: {integrity: sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==}
+ dev: true
+
/@types/node/17.0.5:
resolution: {integrity: sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw==}
dev: true
diff --git a/src/api/base/index.ts b/src/api/base/index.ts
new file mode 100644
index 0000000..8e1f6e8
--- /dev/null
+++ b/src/api/base/index.ts
@@ -0,0 +1,78 @@
+import { merge } from 'lodash'
+import type { AxiosError, AxiosInstance, AxiosRequestConfig, CancelTokenSource } from 'axios'
+import axios from 'axios'
+import { HTTP_METHODS } from '../../constants'
+import { getError, parseResponse } from './parse'
+import { config } from '~/configs'
+import type { RequestOptions } from '~/types/models/requestOptions'
+
+const TIMEOUT = 180000
+const { CancelToken } = axios
+const { API_BASE_URL } = config
+const axiosInstance = axios.create({
+ baseURL: API_BASE_URL,
+ timeout: TIMEOUT,
+})
+const axiosInstanceSource = CancelToken.source()
+
+export function setAxiosRequestConfig(
+ api: AxiosInstance, source: CancelTokenSource, methodType: string, url: string, options?: RequestOptions,
+) {
+ const requestConfig = merge({
+ url,
+ cancelToken: source.token,
+ method: methodType,
+ }, options)
+
+ // eslint-disable-next-line no-console
+ console.log(requestConfig)
+
+ return api(requestConfig as AxiosRequestConfig)
+}
+
+/**
+ * Attach common codes before and after api call.
+ * 1. Add cancel token for cancelling all requests.
+ * 2. Parse basic response
+ * @param {object} api
+ * @param {object} source
+ * @param {function} parse
+ * @returns {*}
+ */
+export function getHttpMethodsWithCancel(
+ api = axiosInstance,
+ source = axiosInstanceSource,
+ parse = parseResponse,
+) {
+ const getApiPromise = (methodType: string, url: string, options?: RequestOptions) =>
+ setAxiosRequestConfig(api, source, methodType, url, options)
+ .then(parse)
+ .catch((error: AxiosError) => {
+ getError(error)
+ })
+
+ return {
+ get(url: string, options?: RequestOptions) {
+ return getApiPromise(HTTP_METHODS.GET, url, options)
+ },
+ post(url: string, options?: RequestOptions) {
+ return getApiPromise(HTTP_METHODS.POST, url, options)
+ },
+ put(url: string, options?: RequestOptions) {
+ return getApiPromise(HTTP_METHODS.PUT, url, options)
+ },
+ /**
+ * Axios's delete accepts url and configs only.
+ * If you want to send data with delete, use configs.data
+ * @see https://github.com/axios/axios/issues/897#issuecomment-343715381
+ */
+ delete(url: string, options?: RequestOptions) {
+ return getApiPromise(HTTP_METHODS.DELETE, url, options)
+ },
+ cancelAPIs() {
+ source.cancel('API is canceled by cancelAPIs')
+ },
+ }
+}
+
+export default getHttpMethodsWithCancel()
diff --git a/src/api/base/parse.ts b/src/api/base/parse.ts
new file mode 100644
index 0000000..e32109a
--- /dev/null
+++ b/src/api/base/parse.ts
@@ -0,0 +1,33 @@
+import type { AxiosError, AxiosResponse } from 'axios'
+import {
+ ERROR_CODE,
+ HTTP_STATUS_CODE,
+} from '../../constants'
+import type { ResponseModel } from '~/types/models/responseModel'
+
+export const getError = (responseError: AxiosError) => {
+ const { code: statusCode, response } = responseError
+ const errorResponse = response as ResponseModel
+
+ // add API HTTP Status Code based Error Type
+ if (HTTP_STATUS_CODE.TEST === Number(statusCode)) {
+ // TODO check Error Code
+ if (ERROR_CODE.TEST_CODE === Number(errorResponse.errorCode)) {
+ // add API Error Code based Error Type
+ }
+ }
+
+ // eslint-disable-next-line no-console
+ console.log(errorResponse)
+
+ return new Error(errorResponse.errorCode)
+}
+
+export const parseResponse = (response: AxiosResponse) => {
+ const responseData: ResponseModel = response.data
+
+ // eslint-disable-next-line no-console
+ console.log(responseData)
+
+ return responseData.data
+}
diff --git a/src/configs/index.ts b/src/configs/index.ts
new file mode 100644
index 0000000..312bc10
--- /dev/null
+++ b/src/configs/index.ts
@@ -0,0 +1,3 @@
+export const config = {
+ API_BASE_URL: import.meta.env.VITE_API_BASE_URL,
+}
diff --git a/src/constants/httpStatus.ts b/src/constants/httpStatus.ts
new file mode 100644
index 0000000..a70daa9
--- /dev/null
+++ b/src/constants/httpStatus.ts
@@ -0,0 +1,17 @@
+export const HTTP_METHODS = {
+ GET: 'get',
+ POST: 'post',
+ PUT: 'put',
+ DELETE: 'delete',
+}
+
+export const HTTP_STATUS_CODE = {
+ OK: 200,
+ TEST: 999,
+ // TODO : add API Error Status Code setting
+}
+
+export const ERROR_CODE = {
+ TEST_CODE: 9991,
+ // TODO : add API Error Code setting
+}
diff --git a/src/constants/index.ts b/src/constants/index.ts
new file mode 100644
index 0000000..f52e90f
--- /dev/null
+++ b/src/constants/index.ts
@@ -0,0 +1 @@
+export * from './httpStatus'
diff --git a/src/env.d.ts b/src/env.d.ts
index d891032..daaa7f1 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -2,6 +2,7 @@
interface ImportMetaEnv {
readonly VITE_TEST: string
+ readonly VITE_API_BASE_URL: string
}
interface ImportMeta {
diff --git a/src/pages/index.vue b/src/pages/index.vue
index c0ae9f2..f047002 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -1,4 +1,6 @@
diff --git a/src/types/models/requestOptions.ts b/src/types/models/requestOptions.ts
new file mode 100644
index 0000000..a6cd624
--- /dev/null
+++ b/src/types/models/requestOptions.ts
@@ -0,0 +1,6 @@
+// Example API Request Options
+export type RequestOptions = {
+ url: string
+ data?: D
+ params?: any
+}
diff --git a/src/types/models/responseModel.ts b/src/types/models/responseModel.ts
new file mode 100644
index 0000000..fd37b20
--- /dev/null
+++ b/src/types/models/responseModel.ts
@@ -0,0 +1,7 @@
+// Example API Response Model
+export type ResponseModel = {
+ data?: any
+ success?: boolean
+ errorCode?: string
+ errorMessage?: string
+}