Skip to content

Commit 436dc7c

Browse files
authored
[리팩터링] Axios (#97)
* fix: 폴더명 변경 * feat: 환경변수 추가 constants에 환경변수 추가하였습니다. * chore: 절대경로 수정, experimentalDecorators활성화 Library 하위 절대경로를 @Library로 통합하였습니다. Constants 절대경로를 추가하였습니다. Decorator 사용을 위해 experimentalDecorators활성화하였습니다. * chore: 일부룰 오프 @typescript-eslint/lines-between-class-members 프로퍼티간의 공백 룰을 오프하였습니다. class-methods-use-this 메서드에서 this를 사용하지 않는 경우가 존재하여 룰을 오프하였습니다. * fix: 기존 파일 api 유틸리티 이동 * feat: static으로 설정한 exception에 사용할 상수 변수 * feat: exception interface 추가 * feat: axios 요청별 error * feat: axios 레핑 exception이 포함된 interceptors 오버라이드 * feat: auth repository, service, decorator 작성 authService 기본 내보내기 * feat: Users repository, service, decorator 작성 userService 기본 내보내기 * feat: api 내보내기 추가 * feat: 인증 데코레이터 추가 * fix: 코드 수정 절대경로 수정 axios 코드 리팩터링으로 인한 수정 modal에 필요없는 코드 삭제 * fix: 절대경로 수정 * fix: 이전코드 삭제 * feat: images domains 추가 * fix: 변수 분리 상수 변수를 외부에서 가져오기로 수정 * chore: tilog-api update * fix: code smell
1 parent 9543f97 commit 436dc7c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+315
-434
lines changed

.eslintrc.json

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
"plugin:prettier/recommended"
2323
],
2424
"rules": {
25+
"@typescript-eslint/lines-between-class-members": "off",
26+
"class-methods-use-this": "off",
2527
"import/order": [
2628
"error",
2729
{

constants/environment/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { TILOG_API } = process.env;
2+
3+
export default TILOG_API;

lib/api/auth/authRepository.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { AxiosInstance } from "axios";
2+
3+
import { AuthApi, Configuration } from "@til-log.lab/tilog-api";
4+
5+
export default class AuthRepository extends AuthApi {
6+
constructor(
7+
axios?: AxiosInstance,
8+
basePath?: string,
9+
configuration?: Configuration
10+
) {
11+
super(configuration, basePath, axios);
12+
}
13+
}

lib/api/auth/authService.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
2+
3+
import AuthRepository from "@Library/api/auth/authRepository";
4+
import validateToken from "@Library/api/auth/validateTokenDecorator";
5+
import ExceptionInterface from "@Library/api/exception/interface";
6+
7+
export default class AuthService {
8+
constructor(
9+
private readonly authRepository: AuthRepository,
10+
private readonly axios: AxiosInstance
11+
) {}
12+
@validateToken()
13+
deleteRefreshToken(
14+
options?: AxiosRequestConfig<ExceptionInterface>
15+
): Promise<AxiosResponse<void, ExceptionInterface>> {
16+
return this.authRepository.usersAuthControllerDeleteRefreshToken({
17+
...options,
18+
withCredentials: true,
19+
});
20+
}
21+
22+
async getAccessTokenUsingRefreshToken(
23+
userAgent?: string,
24+
options?: AxiosRequestConfig<ExceptionInterface>
25+
): Promise<void> {
26+
const { data } =
27+
await this.authRepository.usersAuthControllerGetAccessTokenUsingRefreshToken(
28+
userAgent,
29+
{ ...options, withCredentials: true }
30+
);
31+
this.axios.defaults.headers.common.Authorization = `bearer ${data.accessToken}`;
32+
}
33+
}

lib/api/auth/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import TILOG_API from "@Constants/environment";
2+
import AuthRepository from "@Library/api/auth/authRepository";
3+
import AuthService from "@Library/api/auth/authService";
4+
import httpClient from "@Library/api/httpClient";
5+
6+
const authRepository = new AuthRepository(httpClient.http, TILOG_API);
7+
const authService = new AuthService(authRepository, httpClient.http);
8+
9+
export default authService;
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* eslint-disable func-names */
2+
/* eslint-disable no-param-reassign */
3+
4+
import http from "@Library/api";
5+
import httpClient from "@Library/api/httpClient";
6+
import getAccessTokenToAxiosHeader from "@Library/api/utility/getAccessTokenToAxiosHeader";
7+
import isTokenExpired from "@Library/api/utility/isTokenExpired";
8+
9+
export default function validateToken() {
10+
return (
11+
_target: any,
12+
_propertyKey: string,
13+
descriptor: PropertyDescriptor
14+
) => {
15+
const originMethod = descriptor.value;
16+
descriptor.value = async function (...args: unknown[]) {
17+
const accessToken = getAccessTokenToAxiosHeader(httpClient.http);
18+
19+
if (!accessToken || isTokenExpired(accessToken)) {
20+
await http.authService.getAccessTokenUsingRefreshToken();
21+
}
22+
return originMethod.apply(this, args);
23+
};
24+
};
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const UNKNOWN_LOCATION = "Axios_Unknown";
2+
export const REQUEST_LOCATION = "Axios_Request";
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const INTERNAL_SERVER_ERROR = 500;

lib/api/exception/interface/index.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import MessageInterface from "lib/messages/interface";
2+
3+
export default interface ExceptionInterface {
4+
readonly statusCode: number;
5+
readonly requestLocation: string;
6+
readonly message: MessageInterface;
7+
}

lib/api/exception/requestError.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { REQUEST_LOCATION } from "@Library/api/exception/constant/requestLocation";
2+
import { INTERNAL_SERVER_ERROR } from "@Library/api/exception/constant/statusCode";
3+
import ExceptionInterface from "@Library/api/exception/interface";
4+
import { NETWORK_ERROR_MESSAGE } from "@Library/messages/constants/error";
5+
import MessageInterface from "@Library/messages/interface";
6+
7+
export default class RequestError implements ExceptionInterface {
8+
readonly statusCode: number;
9+
readonly requestLocation: string;
10+
readonly message: MessageInterface;
11+
12+
constructor() {
13+
this.statusCode = INTERNAL_SERVER_ERROR;
14+
this.requestLocation = REQUEST_LOCATION;
15+
this.message = NETWORK_ERROR_MESSAGE;
16+
}
17+
}

lib/api/exception/responseError.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import ExceptionInterface from "@Library/api/exception/interface";
2+
import { NOT_EXIST_MESSAGE } from "@Library/messages/constants/error";
3+
import MessageInterface from "@Library/messages/interface";
4+
5+
export default class ResponseError implements ExceptionInterface {
6+
readonly statusCode: number;
7+
readonly requestLocation: string;
8+
readonly message: MessageInterface;
9+
10+
constructor(errorObject: ExceptionInterface) {
11+
this.statusCode = errorObject.statusCode;
12+
this.requestLocation = errorObject.requestLocation;
13+
this.message = errorObject.message;
14+
15+
if (!this.message) {
16+
this.message = NOT_EXIST_MESSAGE;
17+
}
18+
}
19+
}

lib/api/exception/unknownError.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { UNKNOWN_LOCATION } from "@Library/api/exception/constant/requestLocation";
2+
import { INTERNAL_SERVER_ERROR } from "@Library/api/exception/constant/statusCode";
3+
import ExceptionInterface from "@Library/api/exception/interface";
4+
import { NETWORK_ERROR_MESSAGE } from "@Library/messages/constants/error";
5+
import MessageInterface from "@Library/messages/interface";
6+
7+
export default class UnknownError implements ExceptionInterface {
8+
readonly statusCode: number;
9+
readonly requestLocation: string;
10+
readonly message: MessageInterface;
11+
12+
constructor() {
13+
this.statusCode = INTERNAL_SERVER_ERROR;
14+
this.requestLocation = UNKNOWN_LOCATION;
15+
this.message = NETWORK_ERROR_MESSAGE;
16+
}
17+
}

lib/api/httpClient.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios";
2+
3+
import RequestError from "@Library/api/exception/requestError";
4+
import ResponseError from "@Library/api/exception/responseError";
5+
import UnknownError from "@Library/api/exception/unknownError";
6+
7+
class HttpClient {
8+
public http: AxiosInstance;
9+
10+
constructor(config: AxiosRequestConfig) {
11+
this.http = axios.create(config);
12+
this.interceptors();
13+
}
14+
private interceptors = () => {
15+
this.http.interceptors.response.use(
16+
(response) => response,
17+
(error) => this.interceptorError(error)
18+
);
19+
};
20+
21+
private interceptorError = (error: AxiosError) => {
22+
// NOTE: 서버에서 응답한 상태
23+
if (error.response) {
24+
const { data } = error.response;
25+
return Promise.reject(new ResponseError(data));
26+
}
27+
28+
// NOTE: 서버에서 응답하지 못한 상태
29+
if (error.request) {
30+
return Promise.reject(new RequestError());
31+
}
32+
33+
// NOTE: 요청, 응답이 모두 이루지지 않은 상태
34+
return Promise.reject(new UnknownError());
35+
};
36+
}
37+
38+
export default new HttpClient({});

lib/api/index.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import authService from "@Library/api/auth";
2+
import usersService from "@Library/api/users";
3+
4+
export default {
5+
authService,
6+
usersService,
7+
};

lib/api/users/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import TILOG_API from "@Constants/environment";
2+
import httpClient from "@Library/api/httpClient";
3+
import UserRepository from "@Library/api/users/userRepository";
4+
import UserService from "@Library/api/users/userService";
5+
6+
const userRepository = new UserRepository(httpClient.http, TILOG_API);
7+
const usersService = new UserService(userRepository);
8+
9+
export default usersService;

lib/api/users/userRepository.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { AxiosInstance } from "axios";
2+
3+
import { UserApi, Configuration } from "@til-log.lab/tilog-api";
4+
5+
export default class UserRepository extends UserApi {
6+
constructor(
7+
axios?: AxiosInstance,
8+
basePath?: string,
9+
configuration?: Configuration
10+
) {
11+
super(configuration, basePath, axios);
12+
}
13+
}

lib/api/users/userService.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { AxiosRequestConfig, AxiosResponse } from "axios";
2+
3+
import validateToken from "@Library/api/auth/validateTokenDecorator";
4+
import ExceptionInterface from "@Library/api/exception/interface";
5+
import UserRepository from "@Library/api/users/userRepository";
6+
7+
import {
8+
GetMeResponseDto,
9+
GetUserProfileResponseDto,
10+
SetSettingRequestBodyDto,
11+
SetSettingRequestBodyDtoSettingTypeEnum,
12+
} from "@til-log.lab/tilog-api";
13+
14+
export default class UserService {
15+
constructor(private readonly userRepository: UserRepository) {}
16+
17+
@validateToken()
18+
getMe(
19+
options?: AxiosRequestConfig<ExceptionInterface>
20+
): Promise<AxiosResponse<GetMeResponseDto, ExceptionInterface>> {
21+
return this.userRepository.usersControllerGetMe(options);
22+
}
23+
24+
getUserProfile(
25+
userId: number,
26+
options?: AxiosRequestConfig<ExceptionInterface>
27+
): Promise<AxiosResponse<GetUserProfileResponseDto, ExceptionInterface>> {
28+
return this.userRepository.usersControllerGetUserProfile(userId, options);
29+
}
30+
31+
@validateToken()
32+
setSetting(
33+
content: string | null,
34+
settingType: SetSettingRequestBodyDtoSettingTypeEnum,
35+
options?: AxiosRequestConfig<ExceptionInterface>
36+
): Promise<AxiosResponse<void, ExceptionInterface>> {
37+
const setSettingRequestBodyDto: SetSettingRequestBodyDto = {
38+
content,
39+
settingType,
40+
};
41+
return this.userRepository.usersControllerSetSetting(
42+
setSettingRequestBodyDto,
43+
options
44+
);
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { AxiosInstance } from "axios";
2+
3+
export default function getAccessTokenToAxiosHeader(
4+
axios: AxiosInstance
5+
): string | null {
6+
const accessToken = axios.defaults.headers.common.Authorization;
7+
if (!accessToken) {
8+
return null;
9+
}
10+
return accessToken.toString().slice(7);
11+
}

lib/auth/utility/isTokenExpired.ts lib/api/utility/isTokenExpired.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ export default function isTokenExpired(
66
const payloadBase64 = token.split(".")[1];
77
const decodedJson = Buffer.from(payloadBase64, "base64").toString();
88
const decoded = JSON.parse(decodedJson);
9-
const exp = decoded.exp;
9+
const { exp } = decoded;
1010
return Date.now() >= exp * 1000;
1111
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import axiosInstance from "@Api/axiosInstance";
1+
import httpClient from "@Library/api/httpClient";
2+
23
import { GetAccessTokenUsingRefreshTokenResponse } from "@til-log.lab/tilog-api";
34

45
export default function setAccessTokenToAxiosHeader(
56
accessToken: GetAccessTokenUsingRefreshTokenResponse["accessToken"]
67
) {
7-
axiosInstance.defaults.headers.common[
8-
"Authorization"
9-
] = `bearer ${accessToken}`;
8+
httpClient.http.defaults.headers.common.Authorization = `bearer ${accessToken}`;
109
}

lib/auth/clientSideAuthentication.ts

-31
This file was deleted.

lib/auth/index.ts

-4
This file was deleted.

0 commit comments

Comments
 (0)