개발하던 중, CSR 환경에서 API를 호출하려 하니 아래와 같은 CORS 에러가 발생했습니다.
Access to fetch at 'http://192.168.109.254:32488/api/menu?service=PUBLIC' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Spring 서버 측에서 CORS 설정이 되어 있지 않았기 때문에 브라우저가 직접 API를 호출할 수 없었고, 이를 해결하기 위해 Nuxt 서버에서 프록시(proxy)를 활용해 우회하는 방식으로 대응하고자 했습니다.
// server/middleware/proxy.ts
export default defineEventHandler((event) => {
console.log('middleware');
});
모든 요청은 middleware를 거거치므로 여기서 프록시 처리를 시도했지만,
이 방식은 SSR에서만 작동하고 CSR 환경에서는 호출되지 않아 실패했습니다.
export default defineNuxtConfig({
runtimeConfig: {
springApiBaseUrl: '',
},
nitro: {
routeRules: {
'/spring/api/**': {
proxy: process.env.NUXT_SPRING_API_BASE_URL + '/api/**',
},
},
},
}
이 방식은 production 환경에서 문제가 발생했습니다.
/portal이라는 context-path 하위로 Nuxt 앱이 배포되다 보니 실제 요청 경로가 /portal/spring/api/** 가 되어 프록시가 정상 작동하지 않았습니다.
deployment.yml
env:
- name: NODE_ENV
value: production
- name: NUXT_APP_BASE_URL
value: '/portal'
개발 환경에서도 /portal context를 동일하게 맞추면 해결될 수도 있었지만, deployment.yml에서 주입된 환경변수는 런타임 시점에서만 접근 가능하므로 routeRules에는 사용할 수 없었습니다.
// server/routes/spring/[...].ts
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
const targetURL = config.springApiBaseUrl + event.node.req.url?.replace(/^\/spring/, '');
return proxyRequest(event, targetURL);
});
위와 같이 Nuxt의 server/routes 디렉토리에 직접 핸들러를 구성하면, /spring/* 경로로 들어온 요청을 원하는 Spring API로 안전하게 프록시 처리할 수 있습니다.
이 방식은 CSR, SSR 환경 모두에서 정상 작동하며, CORS 에러 없이 브라우저에서 API 요청이 가능합니다.