
브라우저 내장 fetch 만으로도 통신은 가능하지만, 실제 서비스에서는 Axios를 거의 기본처럼 쓴다.
이유:
try/catch 로 한 번에 처리)baseURL, 공통 header, 토큰 설정을 인스턴스 단위로 관리할 수 있다결론: Vue + 실제 API 서버 연동에서는 Axios 인스턴스를 만드는 흐름이 거의 정석이다.
그냥 이렇게 매번 써도 되지만:
import axios from 'axios';
axios.get('/products');
axios.post('/login', payload);
실무에서는 보통 인스턴스를 한 번 만들고 그걸 재사용한다.
// src/api/axios.js
import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:8080/api', // 공통 prefix
withCredentials: true // 필요 시
});
// 요청 인터셉터
api.interceptors.request.use(config => {
// 토큰 자동 첨부 등
// config.headers.Authorization = `Bearer ${token}`;
return config;
});
// 응답 인터셉터
api.interceptors.response.use(
res => res,
error => {
// 공통 에러 처리
return Promise.reject(error);
}
);
export default api;
장점:
api.get('/products') 처럼 상대 경로만 써도 됨| 메서드 | 역할 |
|---|---|
| GET | 데이터 조회 |
| POST | 새 데이터 생성 |
| PUT | 전체 수정 |
| PATCH | 부분 수정 |
| DELETE | 삭제 |
Axios 호출 예:
api.get('/products'); // GET /products
api.post('/products', data); // POST /products
api.put(`/products/${id}`, data); // PUT /products/{id}
api.delete(`/products/${id}`); // DELETE /products/{id}
Axios 는 Promise 기반이라 async/await 형태로 사용하는 게 보통 더 깔끔하다.
try {
const res = await api.get('/products');
const products = res.data;
} catch (error) {
// error.response, error.message 등 활용
}
한 번 더 정리:
res.data 에 실제 응답 바디가 들어간다catch 에서 한 번에 처리Vue 개발 서버와 백엔드 서버가 포트가 다르면 거의 무조건 CORS 문제가 발생한다.
예:
http://localhost:5173http://localhost:8080브라우저 기준으로 출처(origin) 이 다르기 때문에, 브라우저가 기본적으로 요청을 막는다.
이 규칙이 동일 출처 정책(Same-Origin Policy).
브라우저는 특정 조건에서 Preflight(사전 요청) 을 먼저 보낸다.
이때 보내는 요청이 OPTIONS 이고, 여기서 묻는 내용은 단순하다.
이 출처(origin)에서 이 메서드/헤더로 요청 보내도 되나?
서버가 CORS 관련 헤더로 “허용” 응답을 보내야 실제 요청이 이어진다.
결론부터 말하면: CORS는 서버가 허용해 줘야 해결된다.
Spring 쪽에서 주로 쓰는 방법:
@CrossOrigin 붙이기WebMvcConfigurer 로 글로벌 CORS 설정SecurityFilterChain 에 CORS 설정 추가핵심은 이런 헤더들을 백엔드에서 설정하는 것:
Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Allow-Credentials (쿠키/인증 정보 쓸 때)개발 환경에서만 쓰는 편법도 있다.
vite.config.js 에 proxy 를 설정하면, 브라우저 입장에서는 같은 origin으로 요청하는 것처럼 보이게 만들 수 있다.
예를 들어:
// vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8080', // 백엔드 주소
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
});
이렇게 하면 프런트에서는:
api.get('/api/products');
라고 요청하고, Vite dev 서버가 내부적으로:
http://localhost:8080/products
로 프록시를 대신 날려 준다.
브라우저 입장에서는 http://localhost:5173/api/... 로 보이기 때문에 CORS 검사에 걸리지 않는다.
프런트에서 API 서버와 상호작용할 때 가장 기본적인 흐름을 Product 예시로 정리.
역할:
/products 호출핵심 포인트:
async/awaitisLoading 같은 ref)errorMessage 등)URL 패턴:
/products/:id
흐름:
useRoute() 로 id 가져오기/products/{id} 호출핵심 포인트:
흐름:
ref / reactive 로 form 상태 정의<input v-model="form.name"> 형태로 바인딩onSubmit() 에서 Axios POST 호출router.push('/products'))포인트:
흐름:
/products/{id} GET 으로 기존 데이터 조회/products/{id} 로 PUT 요청포인트:
흐름:
confirm() 으로 한 번 더 확인/products/{id}router.replace('/products') 같은 형태로 이동포인트:
replace 사용백엔드에서 이런 식으로 응답을 내려준다고 가정하면:
{
"code": "NOT_FOUND",
"message": "상품이 존재하지 않습니다.",
"status": 404
}
프런트에서는 보통:
error.response.data 를 읽어서
data.message → 화면에 보여 줄 문구data.status → 분기 처리공통 규칙 예:
401 → 로그인 페이지로 이동403 → “접근 권한 없음” 메시지404 → “존재하지 않는 리소스” 페이지500 → “서버 오류 안내” 토스트/알림이걸 Axios 인스턴스의 응답 인터셉터에서 공통 처리하는 패턴도 자주 쓴다.
Product CRUD 구조 예:
/products # 목록
/products/:id # 상세
/products/new # 등록
/products/:id/edit # 수정
각 URL 마다 별도 페이지 컴포넌트를 두고, 라우터가 흐름을 관리한다.
목록에서 아이템 클릭 → 상세 페이지 이동:
<RouterLink :to="`/products/${item.id}`">
{{ item.name }}
</RouterLink>
혹은 코드에서:
router.push(`/products/${item.id}`);
reactive 또는 ref 로 form 상태 선언v-model 로 input 과 연결submit 시 Axios 요청 호출예:
<script setup>
import { reactive } from 'vue';
import api from '@/api/axios';
import { useRouter } from 'vue-router';
const router = useRouter();
const form = reactive({
name: '',
price: 0
});
const onSubmit = async () => {
try {
await api.post('/products', form);
router.push('/products');
} catch (error) {
// 에러 처리
}
};
</script>
보통 세 가지 상태를 같이 관리한다.
isLoading
errorMessage
success 혹은 페이지 이동
간단한 구성 예:
const isLoading = ref(false);
const errorMessage = ref('');
const onSubmit = async () => {
isLoading.value = true;
errorMessage.value = '';
try {
await api.post('/products', form);
router.push('/products');
} catch (error) {
errorMessage.value = error.response?.data?.message || '요청 실패';
} finally {
isLoading.value = false;
}
};
Axios 인스턴스로 API 공통 설정 + 인터셉터를 관리
CORS 는 서버 설정이 핵심, 개발 단계에서는 Vite proxy 로 우회 가능
Product CRUD 흐름을 기준으로
라우터로 페이지 구조를 잡고
/products, /products/:id, /products/new, /products/:id/editForm 상태, 로딩 상태, 에러 상태를 반응형으로 관리해서