서버와 통신하기 전에, 개념을 확실하게 정리하고 넘어가고싶어서 해당 개념을 정리하기로 했다. Axios를 사용했던 경험은 있지만 모르는 부분도 많은 상태에서 항상 급급하게 코드만 작성해왔던 것 같아 이번에는 꼼꼼하게 정리하고 구조를 이해해서 작성해보고자 한다.
출처 링크
https://velog.io/@bcl0206/Ajax의-개념-등장배경-장점
https://velog.io/@surim014/AJAX란-무엇인가
https://hstory0208.tistory.com/entry/비동기-통신-Ajax와-Axios의-차이점
https://velog.io/@kysung95/개발상식-Ajax와-Axios-그리고-fetch
https://inpa.tistory.com/entry/AXIOS-📚-설치-사용
https://ramincoding.tistory.com/entry/React-Axios-간단하게-사용하기
사용자 경험 향상, 웹 애플리케이션의 성능개선에 도움
Ajax 이전 웹페이지는 서버로부터 새로운 HTML을 받아 웹페이지 전체를 처음부터 다시 렌더링 하는 방식이였다.
해당 단점을 극복하고자 Ajax의 개념 등장
XMLHttpRequest
객체 사용fetch
사용jQuery
사용Axios
사용 (React or Vue 환경)Promise
객체로 해주기 때문에 response 데이터를 다루기 쉬움Ajax를 효율적으로 활용할 수 있게 도와주는 라이브러리
Promise
기반으로, XMLHttpRequest
객체를 사용할 때 보다 코드를 더 간결하게 작성할 수 있음catch
할 수 있음JSON
으로 변환해주기 때문에, parsing
이나 stringify
과정이 필요없음간단하게 사용할 때는 fetch, 확장성을 고려할 경우에는 axios 사용이 더 좋음
axios({
url: 'https://test/api/cafe/list/today', // 통신할 웹문서
method: 'get', // 통신할 방식
data: { // 인자로 보낼 데이터
foo: 'diary'
}
});
config 객체 종류
사용예시
// GET 요청
axios({
method: 'get',
url: url,
params: {
id: 1,
category: 'review'
},
headers: {
Authorization: 'Bearer YourAccessToken'
}
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.error(err);
});
// POST 요청
axios({
method: 'post',
url: url,
data: {
name: 'Dev',
title: 'good review'
}
});
.then
함수인자로 받아 객체에 담겨진 데이터를 응답 데이터라고 한다.response.data: {}, // 서버가 제공한 응답(데이터)
response.status: 200, // `status`는 서버 응답의 HTTP 상태 코드
response.statusText: 'OK', // `statusText`는 서버 응답으로 부터의 HTTP 상태 메시지
response.headers: {}, // `headers` 서버가 응답 한 헤더는 모든 헤더 이름이 소문자로 제공
response.config: {}, // `config`는 요청에 대해 `axios`에 설정된 구성(config)
// `request`는 응답을 생성한 요청
// 브라우저: XMLHttpRequest 인스턴스
// Node.js: ClientRequest 인스턴스(리디렉션)
response.request: {}
axios.get(url[, config])
axios.post(url, data[, config])
axios.put(url, data[, config])
axios.delete(url[, config])
axios.request(config)
/ axios.head(url[, config])
/ axios.options(url[, config])
/ axios.create([config])
// user에게 할당된 id 값과 함께 요청을 합니다.
axios.get('/user?ID=12345')
.then(function (response) {
// 성공했을 때
console.log(response);
})
.catch(function (error) {
// 에러가 났을 때
console.log(error);
})
.finally(function () {
// 항상 실행되는 함수
});
// 위와는 같지만, 옵션을 주고자 할 때는 이렇게 요청을 합니다.
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.finally(function () {
// always executed
});
// async/await 를 쓰고 싶다면 async 함수/메소드를 만듭니다.
async function getUser() {
try {
const response = await axios.get('/user?ID=12345');
console.log(response);
} catch (error) {
console.error(error);
}
}
Message Body
에 담아서 보낸다.axios.post("url", {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
// response
}).catch(function (error) {
// 오류발생시 실행
})
axios.put("url", {
username: "",
password: ""
})
.then(function (response) {
// response
}).catch(function (error) {
// 오류발생시 실행
})
💡 POST, PUT, PATCH 차이점
POST
: Create / 생성 / 여러 번 호출 시 매번 다른 결과 생성PUT
: Update / 갱신, 생성 / 여러 번 호출 시 동일한 결과PATCH
: Partial Update / 일부 갱신 / 지정한 변경사항 일부만 변경
일반적으로 body가 비어있다
axios.delete('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
하지만 query나 params가 많아져서 헤더에 많은 정보를 담을 수 없을 경우에는
- 두번째 인자에 data를 추가해서 사용한다.
axios.delete('/user?ID=12345',{
data: {
post_id: 1,
comment_id: 13,
username: "foo"
}
})
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
💡 보안적 문제 (PUT, DELETE)
PUT과 DELETE는 사용할 때 제대로 처리하지 않으면 보안적 문제가 발생할 수 있다.
- PUT : 사용자 권한, 인증 관리 필요 (사용자 자신의 리소스만 업데이트 가능하도록)
- DELETE : 실수로 데이터가 삭제되는 것을 방지 (복구 가능한 삭제 기능 구현)
메서드를 사용할 때 보안적 처리하는 방법
1. 인증 (Authentication)
2. 권한 부여 (Authorization)
3. 접근 제어 (Control Access)
4. 입력 유효성 검사 (Input Validation)
XMLHttpRequest 객체를 사용해서 통신했을 때는 문제가 없었지만, fetch or axios를 활용하는 경우에는 문제가 발생했었다.
정상적으로 통신되는 XHR 코드
var file = inputFileList[sendIndex].file;
if (!file) {
console.error("파일을 선택하세요.");
return;
}
var xhr = new XMLHttpRequest();
var serverUrl = `/upload/image/${inputFileList[sendIndex].name}`; // 실제 서버 URL로 수정해야 합니다.
xhr.open("POST", serverUrl, true);
xhr.onload = function () {
console.log(xhr);
if (xhr.status === 200) {
console.log("파일 업로드 성공");
} else {
console.error("파일 업로드 실패");
}
};
xhr.onerror = function () {
console.log(xhr);
console.error("파일 업로드 오류");
};
xhr.send(file);
XMLHttpRequest방식
fetch API 방식
Content-Type: application/json
이 아닌 Content-Type: multipart/form-data
로 요청을 보낸다. let file = inputFileList[sendIndex].file;
if (!file) {
console.error("파일을 선택하세요.");
return;
}
try {
const formData = new FormData();
formData.append("file", file);
const serverUrl = `/upload/image/${inputFileList[sendIndex].name}`;
const response = await fetch(serverUrl, {
method: "POST",
body: formData,
});
if (response.ok) {
console.log("파일 업로드 성공");
} else {
console.error("파일 업로드 실패");
}
} catch (error) {
console.error("파일 업로드 오류", error);
}
let file = inputFileList[sendIndex].file;
if (!file) {
console.error("파일을 선택하세요.");
return;
}
try {
const serverUrl = `/upload/image/${inputFileList[sendIndex].name}`;
const response = await fetch(serverUrl, {
method: "POST",
body: file, // 파일 객체를 직접 body에 전달
});
if (response.ok) {
console.log("파일 업로드 성공");
} else {
console.error("파일 업로드 실패");
}
} catch (error) {
console.error("파일 업로드 오류", error);
}
let file = inputFileList[sendIndex].file;
if (!file) {
console.error("파일을 선택하세요.");
return;
}
try {
const serverUrl = `/upload/image/${inputFileList[sendIndex].name}`;
const response = await axios.post(serverUrl, file, {
headers: {
"Content-Type": "multipart/form-data", // FormData를 전송할 때 필요한 헤더 설정
},
});
if (response.status === 200) {
console.log("파일 업로드 성공");
} else {
console.error("파일 업로드 실패");
}
} catch (error) {
console.error("파일 업로드 오류", error);
}
→ 해당 문제는 formData형식으로 보내서 계속 발생했던 문제였다.
axios 내용은 따로 api폴더에 관리하는 방식으로 해서 관리하는 방향으로 수정했다.
axios 폴더 코드
import axios from "axios";
// 이미지 업로드
export const uploadImage = async (fileInfo) => {
try {
const request = await axios.post(
`/upload/image/${fileInfo.name}`,
fileInfo.file,
{
headers: {
"Content-Type": "multipart/form-data", // FormData를 전송할 때 필요한 헤더 설정
},
}
);
return request.data;
} catch (error) {
return "upload Image Faile";
}
};
const uploadFile = async () => {
try {
const file = inputFileList[sendIndex];
if (!file) {
console.log("파일 존재하지 않음");
}
// API 통신
await uploadImage(file); // uploadImage 함수가 완료될 때까지 기다림
console.log("파일 업로드 성공");
} catch (error) {
console.error("파일 업로드 오류", error);
}
};