REST(Representational State Transfer)
→ URI와 HTTP 메소드를 이용해 객체화된 서비스에 접근하는 것
CRUD(Create, Read, Update, Delete)에 대응되는 HTTP 메서드
| 행위 | HTTP 메서드 | 설명 |
|---|---|---|
| 생성(Create) | POST | 새로운 데이터 추가 |
| 조회(Read) | GET | 데이터 조회 |
| 수정(Update) | PUT 또는 PATCH | 데이터 수정 |
| 삭제(Delete) | DELETE | 데이터 삭제 |
Query String(?key=value)을 사용하는 경우 (GET, DELETE)
Body를 사용하는 경우 (POST, PUT, PATCH)
✔ 조회(
GET)는 Query String을 사용
✔ 추가/수정(POST,PUT,PATCH)는 Body를 사용
✔ 삭제(DELETE)는 보통 Query String을 쓰지만, Body를 사용하는 경우도 있음
✔ 보안이 중요한 데이터는 Body에 포함하여 전송
REST 구성
(백엔드 서버 없이 테스트)
방법 1. fake server
방법 2. json-server
https://www.npmjs.com/package/json-server
json-server
전역 패키지로 설치 npm install–g json-server
Mac에서 에러 발생
🛠 권한 오류 해결 방법 (근본적인 해결책)
1️⃣ 전역 패키지 설치 경로를 변경해서 사용자의 홈 디렉터리로 이동하는 것이 가장 안전한 해결책!
mkdir -p ~/.npm-global npm config set prefix '~/.npm-global' echo 'export PATH=$HOME/.npm-global/bin:$PATH' >> ~/.zshrc # Mac 사용자는 ~/.zshrc source ~/.zshrc2️⃣ 전역 설치 대신
npx사용npx json-server --watch db.json👉 전역 설치 없이 실행할 수 있음!
3️⃣ 혹시 꼭 필요하면
sudo사용 (하지만 비추천)sudo npm install -g json-server👉 이 방법은 권장되지 않음! (관리자 권한을 자주 요구하게 되고, 파일 소유권 문제를 유발할 수 있음)
<npm 전역 패키지 설치 경로 변경 (추천)>
현재 전역 패키지 설치 경로가 /usr/local/lib/node_modules/인데,
사용자가 접근할 수 있는 홈 디렉터리로 변경하면 권한 문제를 해결할 수 있다
1️⃣ npm 글로벌 디렉터리 생성
mkdir -p ~/.npm-global
2️⃣ npm 전역 패키지 경로 변경
npm config set prefix '~/.npm-global'
3️⃣ 환경 변수에 추가
echo 'export PATH=$HOME/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc # Bash 사용하는 경우
Mac에서 zsh을 사용한다면:
echo 'export PATH=$HOME/.npm-global/bin:$PATH' >> ~/.zshrc
source ~/.zshrc
4️⃣ 다시 json-server 설치
npm install -g json-server
5️⃣ 설치 확인
json-server --version
설치가 잘 됐다면 버전 정보가 출력됨
"todos": [
{
"id": "1",
"todo": "야구장",
"desc": "프로야구 경기도 봐야합니다.",
"done": false
},
{
"id": "2",
"todo": "놀기",
"desc": "노는 것도 중요합니다.",
"done": false
},
{
"id": "3",
"todo": "Vue 학습",
"desc": "Vue 학습을 해야 합니다",
"done": false
},
{
"id": "4",
"todo": "ES6 공부",
"desc": "ES6공부를 해야 합니다",
"done": false
}
]
[GET]
[GET] 쿼리문 작성 -> Response 형태 바뀜
[POST] 데이터 추가 -> Body 작성
axios
| 기능 | axios | fetch |
|---|---|---|
| 응답 자동 JSON 변환 | ✅ O | ❌ X (response.json() 필요) |
| 요청 취소 | ✅ O (CancelToken) | ❌ X |
| 인터셉터 기능 | ✅ O | ❌ X |
| 타임아웃 설정 | ✅ O (timeout 옵션) | ❌ X |
| 브라우저 & Node.js 지원 | ✅ O | ❌ X (브라우저에서만 가능) |
| 구분 | axios | fetch |
|---|---|---|
| 모듈 설치 | 설치해야 함 (npm install —save axios) | 설치할 필요 없음 (브라우저 내장 API) |
| Promise API | 사용 | 사용 |
| 브라우저 호환성 | 뛰어남 | IE 지원하지 않음 (IE에서 사용하려면 Polyfill 라이브러리를 사용해야 함) |
| timeout 기능 | 지원 (timeout 시간 내에 응답이 오지 않으면 중단시킬 수 있음) | 지원하지 않음 |
| JSON 자동 변환 | 지원 (Content-type 정보를 이용해 자동으로 객체로 변환함) | 지원하지 않음 (수신한 JSON 데이터를 객체로 변환하는 Promise 체인을 추가해야 함) |
vite.config.js 변경(json-server를 프록시 서버로 설정)

// 이 코드 아래에 추가하기
// 개발 서버(개발시에만 동작을 함)
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
App.vue(App.vue를 axios를 이용해 /api/todos/1를 요청하고, 그 결과를 콘솔에 출력)
// 절대 경로 → 어디에서 실행하든 항상 같은 서버로 요청
const url = 'http://localhost:3000/todos/1';
// 상대 경로 → 현재 실행 중인 프론트엔드 서버를 기준으로 요청
const url = '/api/todos/1';
✅ 두 코드의 차이점!
const url = '<http://localhost:3000/todos/1>'; const url = '/api/todos/1';👉 두 코드의 의미는 다름!
1️⃣ 첫 번째 코드 (
http://localhost:3000/todos/1)const url = '<http://localhost:3000/todos/1>'; axios.get(url);
- 절대 경로 (Absolute URL)
- 요청이 항상
http://localhost:3000/todos/1로 직접 전송됨- 클라이언트가 어디에서 실행되든 항상 같은 URL로 요청함
🔹 예제 (프론트엔드가
http://localhost:5173에서 실행 중일 때)🚀 요청이 그대로
http://localhost:3000/todos/1로 전송됨✔ 백엔드 서버가
localhost:3000에 존재해야 함
2️⃣ 두 번째 코드 (
/api/todos/1)const url = '/api/todos/1'; axios.get(url);
- 상대 경로 (Relative URL)
- 요청하는 도메인(출처, origin)을 기준으로 상대적으로 URL을 해석함.
- 현재 실행 중인 프론트엔드 서버의 출처(origin)에 따라 달라짐.
🔹 예제 (프론트엔드가
http://localhost:5173에서 실행 중일 때)🚀 요청이
http://localhost:5173/api/todos/1로 전송됨!💡 이 경우 백엔드가
localhost:3000이면, CORS 오류가 발생할 수 있음!✔ 해결 방법 → 프록시 설정 (
package.json에proxy추가)"proxy": "<http://localhost:3000>"👉 그러면
axios.get('/api/todos/1')하면 자동으로http://localhost:3000/api/todos/1로 요청이 전송됨! 🎯
✅ 정리
코드 설명 요청 결과 '<http://localhost:3000/todos/1'>절대 경로 → 어디에서 실행하든 항상 같은 서버로 요청 http://localhost:3000/todos/1'/api/todos/1'상대 경로 → 현재 실행 중인 프론트엔드 서버를 기준으로 요청 예: http://localhost:5173/api/todos/1👉 즉, 실행 환경에 따라 백엔드 서버 주소가 다를 경우, 상대 경로를 사용하고 프록시를 설정하는 것이 좋음!
Promise와 async-await
<template>
<div><h2>콘솔을 확인합니다.</h2></div>
</template>
<!-- async/await axios를 이용해서 todo 목록을 얻어 출력 -->
<script setup>
import axios from 'axios';
const listUrl = '/api/todos';
const todoUrlPrefix = '/api/todos/';
// 전체 목록을 조회한 후 한 건씩 순차적으로 순회하며 조회하기
const requestAPI = async () => {
let todoList;
let response = await axios.get(listUrl);
todoList = response.data;
console.log('# TodoList : ', todoList);
for (let i = 0; i < todoList.length; i++) {
response = await axios.get(todoUrlPrefix + todoList[i].id);
console.log(`# ${i + 1}번째 Todo : `, response.data);
}
};
requestAPI();
</script>
✅ async/await 개념
async (비동기 함수 선언)
- 함수 앞에 async를 붙이면 비동기 함수가 됨
- async 함수 내부에서는 await을 사용할 수 있음
await (비동기 처리 대기)
- await은 Promise(비동기 작업)가 완료될 때까지 기다림
- await axios.get(url) → 요청이 끝날 때까지 코드 실행을 멈춤
✅ 코드 실행 흐름
1️⃣ 전체 TODO 목록을 가져오기
- await axios.get(listUrl)을 호출해 전체 목록을 가져옴
- 응답을 todoList에 저장하고, 콘솔에 출력
2️⃣ 반복문을 돌며 개별 TODO 조회
- for 루프를 이용해 todoList의 각 항목을 순차적으로 조회
- await axios.get(todoUrlPrefix + todoList[i].id)로 하나씩 가져옴
- 요청이 완료될 때까지 기다렸다가 콘솔에 출력
axios.get() 메서드
axios.get(url, config)
GET요청: Query String은 자동으로 URL 인코딩됨
POST요청:application/json일 경우 자동 인코딩되지 않음. 직접 인코딩 필요!
axios.get(url): /api/todos로 요청을 보내고 응답을 받음await을 사용하여 응답이 올 때까지 기다림response객체를 콘솔에 출력하여 데이터 확인<template>
<div><h2>콘솔을 확인합니다.</h2></div>
</template>
<script setup>
import axios from 'axios'; // Axios 라이브러리 불러오기
const requestAPI = async () => {
const url = '/api/todos'; // 요청을 보낼 API 주소
const response = await axios.get(url); // 서버에 GET 요청을 보내고 응답을 기다림
console.log('# 응답객체 : ', response); // 응답 객체 전체를 출력
};
requestAPI(); // 함수 실행
</script>
axios.response 객체
| 속성 | 설명 | 예시 |
|---|---|---|
data | 수신된 응답 데이터(서버에서 반환한 실제 응답 데이터) | { "id": 1, "title": "Example" } |
config | 요청시에 사용된 config 옵션(설정 값) | { method: "get", url: "https://..." } |
headers | 백엔드 API 서버가 응답할 때 사용된 응답 HTTP 헤더 | { "content-type": "application/json" } |
request | 요청 객체 (브라우저 XHR, Node.js에서는 http.ClientRequest) | {} |
status | 서버가 응답한 HTTP 상태 코드 | 200, 404, 500 등 |
statusText | 서버의 HTTP 상태를 나타내는 문자열 정보(HTTP 상태 메시지) | "OK", "Not Found" 등 |
axios.post() 메서드
axios.post(url, data, config)<template>
<div><h2>콘솔을 확인합니다.</h2></div>
</template>
<script setup>
import axios from 'axios';
const requestAPI = async () => {
const url = '/api/todos';
let data = { todo: '윗몸일으키기 3세트', desc: '너무 빠르지 않게...' };
const resp1 = await axios.post(url, data);
console.log(resp1.data);
};
requestAPI();
</script>
기타 axios 함수
axios.get(url, config)
axios.post(url, data, config)
axios.put(url, data, config)
axios.delete(url, config)
axios 기본 설정 변경
config 값을 전달하지 않으면 기본값이 사용됨
기본값의 변경
에러 처리
try~catch
// 개발자용
try {
const response = await axios.get(url, { timeout: 900 });
console.log("# 응답객체 : ", response);
} catch (e) {
console.log("## 다음 오류가 발생했습니다.");
if (e instanceof Error) console.log(e.message);
else console.log(e);
}
async/await 사용<script setup>
import axios from 'axios';
const requestAPI = async () => {
const url = '/api/todos';
try {
const response = await axios.get(url, { timeout: 900 }); // ⏳ 응답을 기다림
console.log('# 응답객체 : ', response);
} catch (e) {
console.log('## 다음 오류가 발생했습니다.');
if (e instanceof Error) console.log(e.message);
else console.log(e);
}
};
requestAPI();
</script>
then/catch 사용<script setup>
import axios from 'axios';
const requestAPI = async () => {
const url = '/api/todos2';
axios
.get(url, { timeout: 900 }) // ⏳ 비동기 요청 (응답을 기다리지 않음)
.then((response) => {
console.log('# 응답객체 : ', response);
})
.catch((e) => {
console.log('에러=================');
console.log(e);
if (e instanceof Error) console.log(e.message);
else console.log(e);
});
};
requestAPI();
</script>
✅ async/await vs then/catch 차이점 비교 표
| 구분 | async/await 방식 | then/catch 방식 |
|---|---|---|
| 코드 스타일 | 동기 코드처럼 읽기 쉬움 | 콜백 체이닝 방식 |
| 응답 처리 방식 | await으로 응답을 기다린 후 변수에 저장 | 비동기적으로 진행, then()에서 응답 처리 |
| 에러 처리 방식 | try...catch로 예외 처리 | catch()에서 예외 처리 |
| 실행 흐름 | 요청이 끝날 때까지 다음 코드 실행 안 됨 | 요청이 끝나지 않아도 다음 코드가 실행됨 |
| 가독성 | ✅ 좋음 (더 직관적) | ❌ 콜백 체이닝이 길어지면 가독성 저하 가능 |
| 추천 상황 | - 순차적인 요청이 필요한 경우 (ex: API 응답을 받고 추가 요청) - 가독성이 중요한 경우 - 여러 개의 비동기 호출이 있는 경우 | - 간단한 요청일 때 - 병렬적으로 여러 요청을 처리해야 할 때 ( Promise.all()과 함께 사용) |
| 예제 코드 | js try { const response = await axios.get(url); console.log(response); } catch (e) { console.error(e.message); } | js axios.get(url) .then(response => console.log(response)) .catch(e => console.error(e.message)); |
결론:
✅ 순차적 요청이 필요하고 가독성이 중요한 경우 → async/await
✅ 단순한 비동기 요청이거나 여러 요청을 병렬 처리할 경우 → then/catch
👩💻 "내가 만든 웹사이트에서 다른 서버에 데이터를 요청했는데, 오류가 뜬다?" 🤯
👉 그게 바로 크로스 오리진(CORS) 문제
🔍 CORS가 발생하는 이유?
웹 브라우저는 보안 정책 때문에,
🚫 다른 출처(origin)의 서버에서 데이터를 가져오지 못하게 막음
✅ 출처(origin)란?
"출처(origin)" =
프로토콜 + 도메인 + 포트번호
http://example.com(출처 A)https://example.com(출처 B, 프로토콜 다름)http://api.example.com(출처 C, 도메인 다름)http://example.com:3000(출처 D, 포트 다름)💡 출처가 다르면?
👉 브라우저가 보안 정책(Same-Origin Policy) 때문에 요청을 차단함!
크로스 오리진(Cross Origin) 문제
“브라우저는 자신의 오리진과 다른 오리진의 API 서버와 통신할 때 문제가 발생한다”는 개념
해결 방법
백엔드 API 서버측에서 CORS(Cross Origin Resource Sharing)라는 기능을 제공
Access-Control-Allow-Origin HTTP 헤더를 추가하면 됨!프론트엔드 애플리케이션을 호스팅하는 웹서버에 프록시(Proxy)를 설치 또는 설정
개발 단계에서 주로 사용
프록시 서버 사용(브라우저가 직접 요청 못 하게 우회)
웹 브라우저 대신, 내 서버가 요청을 보내게 하면 됨
→ CORS 문제는 브라우저 보안 정책 때문에 발생하는 거라, 브라우저가 아닌 서버 간 통신에서는 발생하지 않음
→ 프록시 서버를 만들어 백엔드 서버와 직접 통신하면 해결
프록시를 이용한 우회