CORS요..? 흠,, 혹시 백엔드 쪽 문제 아닌가요..?
프론트엔드 개발자로서 HTTP 통신은 프론트에서 다루는 가장 중요한 기술 중 하나라고 생각합니다.
빌트인 객체인 ① XMLHttpRequest 부터 ② fetch, ③ axios 까지 ajax 요청을 위한 다양한 빌트인 객체와 라이브러리가 존재합니다.
HTTP 리소스 요청 시에 자주 발생하는 CORS에러에 대해 프론트엔드 개발자가 백엔드 개발자에게 '백 쪽 문제 같은데 확인해주실 수 있나요?' 를 외칠 수 있는 명분을 만드는 과정에 대해서 이야기해봅니다.
가장 널리 사용되는 axios 모듈을 바탕으로 설명해볼까 합니다!
① 헷갈리지 않는 기본 요청 보일러플레이트 만들어 놓기 🔥
② URL은 기본으로 🔥
③ config 설정하기 🔥
새내기 개발자의 입장에서 백엔드 URL에 바로 요청을 보내다보면, 어디서부터 뭐가 잘못됐는 지 헷갈리는 경우가 많습니다.
그래서 저의 경우는 기본적으로 작성해 둔 axios 보일러플레이트를 바탕으로 하나씩 차근차근 작업해 나갑니다.
axios는 XMLHttpRequest 빌드인 객체에서 사용 가능한 HTTP 요청의 대부분을 지원합니다
axios 요청 시, 보낼 수 있는 Method (axios 모듈 참고)
axios 라이브러리에서 제공하는 인터페이스 확인하기
해당 메서드에서 cmd + 클릭 / ctrl + 클릭을 통해 다운 받은 axios 라이브러리에 접근이 가능합니다
따라서 제일 기본인 GET 요청을 바탕으로 무료로 JSON 데이터를 제공하고 있는 URL에 GET 요청을 보내 기본적으로 HTTP 통신이 가능한 상황인 지 파악해야 합니다.
URL에 접속하여 정확한 정보를 넘겨주는 지 확인해보는 것도 좋겠죠?
해당 코드는 다음과 같습니다.
$ npm i axios / $ yarn add axios
import axios from "axios";
import { useEffect, useState } from "react";
function Axios1() {
const [res, setRes] = useState([]);
useEffect(() => {
console.log(res);
}, [res]);
const fetchData = async () => {
try {
await axios
.get("https://jsonplaceholder.typicode.com/todos/")
.then((res) => setRes(res.data.slice(0, 10)))
.catch((err) => console.log(err));
} catch (err) {
console.log(err);
}
};
return (
<div
style={{ borderBottom: "3px solid black", width: "100%", height: "50vh" }}
>
<h1>axios</h1>
<button type="button" onClick={fetchData}>
fetching!
</button>
</div>
);
}
export default Axios1;
결과를 볼까요?
정상적으로 데이터가 넘어오는 것을 확인할 수 있습니다.
해당 배열을 바탕으로 객체 내부의 프로퍼티 키를 매핑 돌려서 원하는 구조와 형식으로 만들 수도 있습니다.
그렇다면 이제 실전입니다.
진행 중인 프로젝트의 백엔드 도메인으로 URL을 바꿔서 적용해봅니다.
하지만 흔히 MERN 스택(Mongo Express React Node) 과 같이 모든 서버가 같은 node.js 환경에서 두 개의 터미널을 열어 로컬 환경에서 작업할 수 있는 것은 아닙니다.
(나의 로컬환경에서 포트만 다르게 3000 <-> 8080처럼 작업할 경우가 적다는 얘기입니다)
현재 저의 경우 스프링 서버와 작업을 진행 중이며, 스프링 서버는 nginx를 사용하여 현재 배포되어 있는 상태입니다.
따라서 로컬에서 https로 배포되어 있는 서버에 요청을 보내야하는 상황이기 때문에 필히 CORS가 발생할 수밖에 없겠죠..?
따라서 우리는 일종의 보험을 깔아두는 것처럼, 프론트에서 할 수 있는 최대한의 오픈 마인드를 갖추는 것이 필요합니다.
'모든 것을 다 허락할 거야' 와 같은 마인드죠
이런 정보는 header에 담게 되는데, HTTP 요청 시에 header 정보를 담아 요청을 보낼 수 있습니다.
axios 에서 header에 정보를 담기 위해서는 config를 설정해줘야 합니다.
HTTP 요청 시, 두 번째 파라미터에 config를 설정해줄 수 있습니다.
axios.get('https://api.velog.io/api/v1/posts', {
...
})
url과 달리 config에는 (?) 가 붙은 것을 알 수 있습니다. 이는 옵션을 뜻하므로 넣어줘도 되고 안넣어줘도 된다는 것을 의미합니다.
config로 설정할 수 있는 프로퍼티는 다음과 같습니다.
export interface AxiosRequestConfig {
url?: string;
method?: Method;
baseURL?: string;
transformRequest?: AxiosTransformer | AxiosTransformer[];
transformResponse?: AxiosTransformer | AxiosTransformer[];
headers?: any;
params?: any;
paramsSerializer?: (params: any) => string;
data?: any;
timeout?: number;
timeoutErrorMessage?: string;
withCredentials?: boolean;
adapter?: AxiosAdapter;
auth?: AxiosBasicCredentials;
responseType?: ResponseType;
xsrfCookieName?: string;
xsrfHeaderName?: string;
onUploadProgress?: (progressEvent: any) => void;
onDownloadProgress?: (progressEvent: any) => void;
maxContentLength?: number;
validateStatus?: ((status: number) => boolean) | null;
maxBodyLength?: number;
maxRedirects?: number;
socketPath?: string | null;
httpAgent?: any;
httpsAgent?: any;
proxy?: AxiosProxyConfig | false;
cancelToken?: CancelToken;
decompress?: boolean;
transitional?: TransitionalOptions
}
예를 들면
const myConfig = {
baseURL: 'https://api.velog.io/'
withCredentials: true,
headers: {
Authorization: `Bearer ${myToken}`,
},
}
와 같은 방식으로 config를 설정해준 뒤, axios 요청에 함께 넣어줍니다.
const loadUser = async () => {
try {
axios
const result = await axios
.get('/api/v1/users/profile', myConfig)
.then((res) => console.log('result :', res.data))
console.log(result)
} catch (err) {
console.log(err)
}
}
loadUser 함수와 같은 형식의 axios 요청을 보내게 될 것입니다.
① 헤더에 약속된 토큰을 넣어주고
② withCredentials: true로 설정한다면
거의 무적의 상태라고 볼 수 있습니다.
그럼 이제 요청을 보내볼까요?
이제 당신이 할 일을 끝냈습니다
백엔드 개발자분께 대화를 요청해보세요!
저도 위와 같은 프로세스를 경험하면서, 무조건 저의 문제라고 생각했던 부분이 의외로 백엔드에서 설정을 덜 한 부분이였던 경험이 있습니다.
모르는 만큼 더 많이 찾아보고, 백엔드 분들께 조심스럽게 요청을 한 경험이 있네요.
무작정 해보지 않고 물어보는 것보다는 내가 할 수 있는 경우의 수를 모두 작업해 본 뒤에 추가 확인 요청을 한다면, 협업시에 생길 수 있는 작은 다툼을 최소화할 수 있다고 생각합니다.
모두들 화이팅입니다!