From 6b93fd3cc92d844fa3bb648aa16477102fe010bc Mon Sep 17 00:00:00 2001 From: genTe <128919388+wdgWon@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:27:28 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[Fix=20=F0=9F=AA=9B]=20api=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20get=EC=9D=84=20post=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/auth/{getEmailAuth.ts => postEmailAuth.ts} | 6 +++--- src/constants/endPoints.ts | 4 ++-- .../auth/{getEmailAuth.mock.ts => postEmailAuth.mock.ts} | 6 +++--- src/mocks/handlers.ts | 4 ++-- src/services/caches/useUserInfoData.ts | 6 +++--- src/services/queries/userInfoOptions.ts | 8 ++++---- src/types/domain/apiDomain.d.ts | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) rename src/api/auth/{getEmailAuth.ts => postEmailAuth.ts} (65%) rename src/mocks/auth/{getEmailAuth.mock.ts => postEmailAuth.mock.ts} (53%) diff --git a/src/api/auth/getEmailAuth.ts b/src/api/auth/postEmailAuth.ts similarity index 65% rename from src/api/auth/getEmailAuth.ts rename to src/api/auth/postEmailAuth.ts index 8f7eaaac..857f475b 100644 --- a/src/api/auth/getEmailAuth.ts +++ b/src/api/auth/postEmailAuth.ts @@ -1,12 +1,12 @@ -import { getEmailAuthResponseType } from "api-models" +import { postEmailAuthResponseType } from "api-models" import { ENDPOINTS } from "@constants/endPoints" import { authInstance } from "../axiosInstance" -export const getEmailAuth = async () => { +export const postEmailAuth = async () => { //TODO: accessToken을 소셜 로그인과 이메일 로그인으로 분리하기위해 접두어로 'email', 'github' 등을 붙여야해서 slice로 잘라내는 작업 필요 - const { data } = await authInstance.get( + const { data } = await authInstance.post( ENDPOINTS.EMAIL_AUTH, ) diff --git a/src/constants/endPoints.ts b/src/constants/endPoints.ts index 4370735b..c4d3c308 100644 --- a/src/constants/endPoints.ts +++ b/src/constants/endPoints.ts @@ -3,8 +3,8 @@ const VARIABLE_URL = "/api/v1" export const ENDPOINTS = { GITHUB_LOGIN: `${VARIABLE_URL}/auth/login/github`, EMAIL_LOGIN: `${VARIABLE_URL}/auth/login`, - EMAIL_REFRESH: `${VARIABLE_URL}/auth/refresh`, - EMAIL_AUTH: `${VARIABLE_URL}/auth/login/me`, + EMAIL_REFRESH: `${VARIABLE_URL}/auth/reissue`, + EMAIL_AUTH: `${VARIABLE_URL}/auth/me`, EMAIL_SIGNUP: `${VARIABLE_URL}/users/signup`, GET_USER_NICKNAME: `${VARIABLE_URL}/users?keyword=`, GET_USER_PROFILE: (userId: number) => `${VARIABLE_URL}/users/${userId}`, diff --git a/src/mocks/auth/getEmailAuth.mock.ts b/src/mocks/auth/postEmailAuth.mock.ts similarity index 53% rename from src/mocks/auth/getEmailAuth.mock.ts rename to src/mocks/auth/postEmailAuth.mock.ts index 2be41f5f..5689186f 100644 --- a/src/mocks/auth/getEmailAuth.mock.ts +++ b/src/mocks/auth/postEmailAuth.mock.ts @@ -1,10 +1,10 @@ -import { getEmailAuthResponseType } from "api-models" +import { postEmailAuthResponseType } from "api-models" import { rest } from "msw" import { ENDPOINTS } from "@constants/endPoints" -export const getEmailAuth = rest.get(ENDPOINTS.EMAIL_AUTH, (_, res, ctx) => { - const response: getEmailAuthResponseType = { +export const postEmailAuth = rest.post(ENDPOINTS.EMAIL_AUTH, (_, res, ctx) => { + const response: postEmailAuthResponseType = { id: 1, nickname: "admin", profileImageUrl: null, diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts index 2bcdfdc5..83d034e2 100644 --- a/src/mocks/handlers.ts +++ b/src/mocks/handlers.ts @@ -4,7 +4,7 @@ import allProjectHandlers from "@pages/HomePage/mocks" import { projectsHandlers, userInfoHandlers } from "@pages/ProfilePage/mocks" import { projectDetailHandlers } from "@pages/ProjectDetailPage/mocks" -import { getEmailAuth } from "./auth/getEmailAuth.mock" +import { postEmailAuth } from "./auth/postEmailAuth.mock" import { postEmailLogin } from "./auth/postEmailLogin.mock" import { postEmailRefresh } from "./auth/postEmailRefresh.mock" @@ -14,7 +14,7 @@ export const handlers = [ ...allProjectHandlers, postEmailRefresh, postEmailLogin, - getEmailAuth, + postEmailAuth, ...userInfoHandlers, ...projectsHandlers, ] diff --git a/src/services/caches/useUserInfoData.ts b/src/services/caches/useUserInfoData.ts index 3ec5c2a4..6114d475 100644 --- a/src/services/caches/useUserInfoData.ts +++ b/src/services/caches/useUserInfoData.ts @@ -1,11 +1,11 @@ -import { useQuery } from "@tanstack/react-query" +import { postEmailAuth } from "@api/auth/postEmailAuth" -import { getEmailAuth } from "@/api/auth/getEmailAuth" +import { useQuery } from "@tanstack/react-query" import { QUERYKEY } from "@constants/queryKey" export const useUserInfoData = () => { - return useQuery>>({ + return useQuery>>({ queryKey: [QUERYKEY.USER_INFO], }).data } diff --git a/src/services/queries/userInfoOptions.ts b/src/services/queries/userInfoOptions.ts index 78f26d45..6d321f7e 100644 --- a/src/services/queries/userInfoOptions.ts +++ b/src/services/queries/userInfoOptions.ts @@ -1,12 +1,12 @@ -import { UseQueryOptions } from "@tanstack/react-query" +import { postEmailAuth } from "@api/auth/postEmailAuth" -import { getEmailAuth } from "@/api/auth/getEmailAuth" +import { UseQueryOptions } from "@tanstack/react-query" import { QUERYKEY } from "@constants/queryKey" export const userInfoOptions: UseQueryOptions< - Awaited> + Awaited> > = { queryKey: [QUERYKEY.USER_INFO], - queryFn: getEmailAuth, + queryFn: () => postEmailAuth(), } diff --git a/src/types/domain/apiDomain.d.ts b/src/types/domain/apiDomain.d.ts index cd3e94f7..85177f94 100644 --- a/src/types/domain/apiDomain.d.ts +++ b/src/types/domain/apiDomain.d.ts @@ -164,7 +164,7 @@ declare module "api-models" { } /* 인증 관련 */ - export type getEmailAuthResponseType = UserSummary + export type postEmailAuthResponseType = UserSummary export type postEmailRefreshPayload = { refreshToken: string From 7ff60db00f2e6fd0c390b00b3f5891dadd534456 Mon Sep 17 00:00:00 2001 From: genTe <128919388+wdgWon@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:28:34 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[Change=20=F0=9F=9A=9C=20]=20=ED=8E=B8?= =?UTF-8?q?=EC=9D=98=EC=84=B1=20=EC=9C=84=ED=95=B4=20api=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=EC=9D=BC=EB=95=8C=20retry=EB=A5=BC=200=EB=B2=88?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/App.tsx b/src/App.tsx index 7b7bb12a..693012c8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,9 +13,11 @@ const queryClient = new QueryClient({ gcTime: 1000 * 60 * 3, staleTime: 1000 * 60, refetchOnWindowFocus: false, + retry: 0, }, mutations: { gcTime: 1000 * 60 * 3, + retry: 0, }, }, }) From 9cdf65ca799bd269c0a4fcba3dad1a4063d9df8e Mon Sep 17 00:00:00 2001 From: genTe <128919388+wdgWon@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:29:26 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[Fix=20=F0=9F=AA=9B]=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=EC=97=90=20=EC=B4=88=EA=B8=B0=EA=B0=92?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=8B=A4=EC=A0=9C=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=EC=9D=B4=20=EC=95=84=EB=8B=88=EB=9D=BC=20=EA=B3=B5=EB=B0=B1=20?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=20=EB=8B=B4=EA=B8=B0=EB=8A=94=20=ED=98=84?= =?UTF-8?q?=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/authToken.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stores/authToken.ts b/src/stores/authToken.ts index 234333bf..c5080f51 100644 --- a/src/stores/authToken.ts +++ b/src/stores/authToken.ts @@ -8,8 +8,10 @@ class AuthToken { private REFRESH_KEY: string constructor() { - this.accessToken = "" - this.refreshToken = "" + this.accessToken = + localStorage.getItem(VITE_AUTH_JWT_TOKEN_STORAGE_KEY) ?? "" + this.refreshToken = + localStorage.getItem(VITE_REFRESH_JWT_TOKEN_STORAGE_KEY) ?? "" this.ACCESS_KEY = VITE_AUTH_JWT_TOKEN_STORAGE_KEY this.REFRESH_KEY = VITE_REFRESH_JWT_TOKEN_STORAGE_KEY } From 8584a1a90369eb06c2d21e3b5ec73692b927e8cb Mon Sep 17 00:00:00 2001 From: genTe <128919388+wdgWon@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:30:42 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[Fix=20=F0=9F=AA=9B]=20=ED=97=A4=EB=8D=94?= =?UTF-8?q?=EC=97=90=20Authorization=20=EB=88=84=EB=9D=BD=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/axiosInstance.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/api/axiosInstance.ts b/src/api/axiosInstance.ts index c6dd0969..ee9ebc74 100644 --- a/src/api/axiosInstance.ts +++ b/src/api/axiosInstance.ts @@ -1,4 +1,4 @@ -import axios, { isAxiosError } from "axios" +import axios, { AxiosError, isAxiosError } from "axios" import authToken from "@stores/authToken" @@ -15,18 +15,21 @@ export const authInstance = axios.create({ //TODO: 소셜 로그인 로직 추가 예정 authInstance.interceptors.request.use( async (config) => { - // const accessToken = authToken.getAccessToken() - // const refreshToken = authToken.getRefreshToken() + const accessToken = authToken.getAccessToken() + const refreshToken = authToken.getRefreshToken() - // if (!refreshToken) { - // throw new AxiosError("Login Required") - // } + if (!refreshToken) { + throw new AxiosError("Login Required") + } + + if (!accessToken) { + const currentAccessToken = await postEmailRefresh({ refreshToken }) + authToken.setAccessToken(currentAccessToken) + config.headers.Authorization = `Bearer ${currentAccessToken}` + } + + config.headers.Authorization = `Bearer ${accessToken}` - // if (!accessToken) { - // const currentAccessToken = await postEmailRefresh({ refreshToken }) - // authToken.setAccessToken(currentAccessToken) - // config.headers.Authorization = `Bearer ${currentAccessToken}` - // } return config }, (error) => { From 934c2d5b29e57110fc744fe2bea0342faa26393c Mon Sep 17 00:00:00 2001 From: genTe <128919388+wdgWon@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:31:55 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[Feat=20=E2=9C=8F=EF=B8=8F]=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=EC=97=90=EB=8F=84=20api=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EB=B3=B4=EB=82=BC=20=EC=88=98=20=EC=9E=88=EA=B2=8C=20proxy=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vite.config.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/vite.config.ts b/vite.config.ts index d24c9078..ab108371 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,6 +6,15 @@ import tsconfigPaths from "vite-tsconfig-paths" export default defineConfig(({ command }) => ({ plugins: [react(), tsconfigPaths(), splitVendorChunkPlugin()], publicDir: command === "serve" ? "public" : false, + server: { + proxy: { + "/serve": { + target: "http://3.39.156.144:8080", + changeOrigin: true, + rewrite: (path) => path.replace(/^\/serve/, ""), + }, + }, + }, build: { chunkSizeWarningLimit: 1600, rollupOptions: { From 85c9e193998b692d66ab6b3b0447efc558d21011 Mon Sep 17 00:00:00 2001 From: genTe <128919388+wdgWon@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:56:40 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[Change=20=F0=9F=9A=9C=20]=20payload=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=97=90=20=EC=9D=98=ED=95=9C=20api=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/auth/postEmailRefresh.ts | 17 ++++++----------- src/types/domain/apiDomain.d.ts | 2 ++ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/api/auth/postEmailRefresh.ts b/src/api/auth/postEmailRefresh.ts index aa6f1328..6ec12b45 100644 --- a/src/api/auth/postEmailRefresh.ts +++ b/src/api/auth/postEmailRefresh.ts @@ -2,23 +2,18 @@ import { postEmailRefreshPayload, postEmailRefreshResponseType, } from "api-models" -import { AxiosRequestConfig } from "axios" import { ENDPOINTS } from "@constants/endPoints" import { baseInstance } from "../axiosInstance" -export const postEmailRefresh = async ( - { refreshToken }: postEmailRefreshPayload, - config: AxiosRequestConfig = {}, -) => { - const { - data: { accessToken }, - } = await baseInstance.post( +export const postEmailRefresh = async ({ + refreshToken, +}: postEmailRefreshPayload) => { + const { data } = await baseInstance.post( ENDPOINTS.EMAIL_REFRESH, - {}, - { ...config, headers: { Authorization: `Bearer ${refreshToken}` } }, + { refreshToken }, ) - return accessToken + return data } diff --git a/src/types/domain/apiDomain.d.ts b/src/types/domain/apiDomain.d.ts index 85177f94..424a3bd0 100644 --- a/src/types/domain/apiDomain.d.ts +++ b/src/types/domain/apiDomain.d.ts @@ -172,6 +172,8 @@ declare module "api-models" { export type postEmailRefreshResponseType = { accessToken: string + refreshToken: string + user: UserSummary } export type postEmailLoginPayload = { From 7aabd916570e9e202b9a1484b9485e61b8f0038c Mon Sep 17 00:00:00 2001 From: genTe <128919388+wdgWon@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:57:20 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[Feat=20=E2=9C=8F=EF=B8=8F]=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=EC=97=90=EB=9F=AC=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=9C=20=EC=97=90=EB=9F=AC=20=ED=95=B8=EB=93=A4=EB=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/axiosInstance.ts | 66 +++++++++++++++++++++++++++--------- src/constants/customError.ts | 9 +++++ 2 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 src/constants/customError.ts diff --git a/src/api/axiosInstance.ts b/src/api/axiosInstance.ts index ee9ebc74..066e6b19 100644 --- a/src/api/axiosInstance.ts +++ b/src/api/axiosInstance.ts @@ -1,7 +1,9 @@ -import axios, { AxiosError, isAxiosError } from "axios" +import axios, { isAxiosError } from "axios" import authToken from "@stores/authToken" +import { LogoutError, PermissionError } from "@constants/customError" + import { postEmailRefresh } from "./auth/postEmailRefresh" const { VITE_BASE_URL } = import.meta.env @@ -18,17 +20,37 @@ authInstance.interceptors.request.use( const accessToken = authToken.getAccessToken() const refreshToken = authToken.getRefreshToken() + config.headers.Authorization = `Bearer ${accessToken}` + if (!refreshToken) { - throw new AxiosError("Login Required") + const permission = new PermissionError() + + if (import.meta.env.DEV) console.error(permission) + + throw permission } if (!accessToken) { - const currentAccessToken = await postEmailRefresh({ refreshToken }) - authToken.setAccessToken(currentAccessToken) - config.headers.Authorization = `Bearer ${currentAccessToken}` - } + try { + const data = await postEmailRefresh({ refreshToken }) + authToken.setAccessToken(data.accessToken) + authToken.setRefreshToken(data.refreshToken) + config.headers.Authorization = `Bearer ${data.accessToken}` + } catch (refreshError) { + if ( + isAxiosError(refreshError) && + refreshError.response?.status === 401 + ) { + const logout = new LogoutError() - config.headers.Authorization = `Bearer ${accessToken}` + if (import.meta.env.DEV) console.error(logout) + + throw logout + } + + throw refreshError + } + } return config }, @@ -41,13 +63,14 @@ authInstance.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config - if (isAxiosError(error) && error.status === 401) { + if (isAxiosError(error) && error.response?.status === 401) { const refreshToken = authToken.getRefreshToken() try { - const currentAccessToken = await postEmailRefresh({ refreshToken }) - authToken.setAccessToken(currentAccessToken) - originalRequest.headers.Authorization = `Bearer ${currentAccessToken}` + const data = await postEmailRefresh({ refreshToken }) + authToken.setAccessToken(data.accessToken) + authToken.setRefreshToken(data.refreshToken) + originalRequest.headers.Authorization = `Bearer ${data.accessToken}` // 재발급된 엑세스 토큰으로 재요청 return baseInstance(originalRequest) @@ -56,12 +79,23 @@ authInstance.interceptors.response.use( 1. 🟨 로그아웃 api 요청 2. 🟨 react-query의 유저 정보 캐싱 초기화 3. ✅ accessToken, refreshToken 초기화 */ - authToken.removeAccessToken() - authToken.removeRefreshToken() - if (import.meta.env.DEV) { - console.error() + if ( + isAxiosError(refreshError) && + refreshError.response?.status === 401 + ) { + authToken.removeAccessToken() + authToken.removeRefreshToken() + + if (import.meta.env.DEV) { + console.error("로그아웃 처리됩니다.") + } + + const logout = new LogoutError() + + throw logout } - throw new Error("권한이 없습니다. 로그인 해주세요.") + + throw refreshError } } return Promise.reject(error) diff --git a/src/constants/customError.ts b/src/constants/customError.ts new file mode 100644 index 00000000..9eadef0a --- /dev/null +++ b/src/constants/customError.ts @@ -0,0 +1,9 @@ +export class LogoutError extends Error { + name = "Logout" + message = "로그아웃 처리됩니다." +} + +export class PermissionError extends Error { + name = "PermissionError" + message = "권한이 없습니다." +} From 0cfb5c09948e09e601c2dfe585304583770649c9 Mon Sep 17 00:00:00 2001 From: genTe <128919388+wdgWon@users.noreply.github.com> Date: Mon, 11 Mar 2024 18:08:35 +0900 Subject: [PATCH 8/9] =?UTF-8?q?[Fix=20=F0=9F=AA=9B]=20=EB=B9=8C=EB=93=9C?= =?UTF-8?q?=20=EC=8B=A4=ED=8C=A8=ED=95=9C=20=EB=AA=A9=EC=97=85=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mocks/auth/postEmailRefresh.mock.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mocks/auth/postEmailRefresh.mock.ts b/src/mocks/auth/postEmailRefresh.mock.ts index 0f163575..b28c0e35 100644 --- a/src/mocks/auth/postEmailRefresh.mock.ts +++ b/src/mocks/auth/postEmailRefresh.mock.ts @@ -8,6 +8,12 @@ export const postEmailRefresh = rest.post( (_, res, ctx) => { const response: postEmailRefreshResponseType = { accessToken: "mock-accessToken", + refreshToken: "mock-refreshToken", + user: { + id: 0, + nickname: "admin", + profileImageUrl: null, + }, } return res(ctx.status(200), ctx.json(response)) }, From 9f441fa80f4506b15232abb42cbd1d9fa7ef7d8f Mon Sep 17 00:00:00 2001 From: genTe <128919388+wdgWon@users.noreply.github.com> Date: Mon, 11 Mar 2024 23:44:58 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[Chore=20=F0=9F=9A=80]=20PR=20=EA=B4=80?= =?UTF-8?q?=EC=8B=AC=EC=82=AC=20=EB=B6=84=EB=A6=AC=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EB=A6=AC=EC=85=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vite.config.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/vite.config.ts b/vite.config.ts index ab108371..d24c9078 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,15 +6,6 @@ import tsconfigPaths from "vite-tsconfig-paths" export default defineConfig(({ command }) => ({ plugins: [react(), tsconfigPaths(), splitVendorChunkPlugin()], publicDir: command === "serve" ? "public" : false, - server: { - proxy: { - "/serve": { - target: "http://3.39.156.144:8080", - changeOrigin: true, - rewrite: (path) => path.replace(/^\/serve/, ""), - }, - }, - }, build: { chunkSizeWarningLimit: 1600, rollupOptions: {