공공 API를 호출하는 로직을 작성했는데, 뒤이어 CORS 에러가 발생했다.

 

import { useEffect, useState } from "react";
import axios from "axios";

const GardenList = () => {
  const [gardenList, setGardenList] = useState<GardenItem[]>([]);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchGardenList = async () => {
      try {
        const apiKey = process.env.REACT_APP_API_KEY;
        const response = await axios.get(`http://api.nongsaro.go.kr/service/garden/gardenList?apiKey=${apiKey}&pageNo=1&numOfRows=50`);
        
        const items = response.data?.response?.body?.items?.item || [];
        setGardenList(items);
      } catch (error) {
        console.error('Error fetching garden list:', error);
        setError("An error occurred while fetching the data.");
      } finally {
        setLoading(false);
      }
    };

    fetchGardenList();
  }, []);

 

 

 

CORS 에러란?

CORS (Cross Origin Resource Sharing): 교차 출처 리소스 공유

 

간단히 말해 출처가 같은 경우에만 서버간 정보 공유를 할 수 있다는 정책이다.

 

 

 

URL은 아래와 같은 구조로 이루어져있다.

http://www.example.com:80/about?q=likes

프로토콜   Host   포트번호   Path   Query string

 

 

추가로, 특정 번호로 명시된게 아니라면 기본 포트번호는 보통 생략되어있다.

http는 80번, https는 443번이 디폴트이다.

 

 

 

그렇다면 이 중 동일 출처는 무엇일까?

프로토콜, 호스트, 포트번호가 같아야 동일 출처로 인정된다.

브라우저 콘솔창에 location.origin을 입력하면 출처를 확인할 수 있다.

 

 

 

+

출처가 같은지 비교하는 로직은 서버가 아닌 브라우저에서 수행한다.

따라서 CORS 정책을 위반하는 요청을 보내더라도, 서버에서는 정상적으로 응답한다.

후에 브라우저에서 이 응답을 분석하여 CORS 정책을 준수했는지 확인한다.

 

 

 

 

 

해결 방법

CORS 헤더를 통해 서버가 요청을 허용하겠다는 신호를 브라우저에게 보내준다.

 

서버단에서 Access-Control-Allow-Origin이라는 헤더에 요청을 허용할 출처(Origin)를 명시할 수 있다.

Access-Control-Allow-Origin 헤더에 유효한 값을 포함하면, 브라우저는 이를 신뢰한다.

  • 사용 예: Access-Control-Allow-Origin:http://www.example.com (특정 도메인만 허용함)
  • Access-Control-Allow-Origin: * 을 사용하여 모든 출처의 요청을 허용할 수도 있지만, 보안 상 권장되지 않는다.

 

위 방법은 백엔드 지식이 있어야 사용 가능해 보인다.

(Node.js를 아직 접해보지 못한 필자는 2시간 정도 애쓰다가... 다음 방법을 선택했다)

 

 

 

그러나 나는 프론트엔드 밖에 모른다면...

이미 만들어져있는 프록시 서버, 그 중 cors-anywhere 를 이용해보자.

 

 

cors-anywhere란?

브라우저가 아닌, 중간의 프록시 서버에서 요청을 중계하는 방식으로 CORS 에러를 해결해준다.

cors-anywhere 서버가 요청을 대신 보내고, 서버에서 응답을 받아 클라이언트로 전달한다...

즉, 브라우저가 다른 출처에 직접적으로 접근하지 않아 CORS 에러를 우회하는 것.

 

 

작동 방식은 아래와 같다.

  1. 클라이언트에서 API 요청을 보내려 하는 주소 앞에 cors-anywhere 프록시 URL을 추가한다.
  2. 프록시 서버가 요청을 대신 보내고, 받은 응답을 클라이언트로 다시 전송한다.
  3. 브라우저는 응답을 받기만 하기 때문에 CORS 에러 없이 데이터를 받아올 수 있게 된다.

 

다음은 설치 및 사용 방법이다.

// 현재 프로젝트 루트 디렉토리로 이동
git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere // 내부 cors-anywhere 디렉토리로 이동
npm install // 의존성 설치

node server.js // 서버 실행

 

위처럼 설치 및 실행에 성공했다면, 기존 코드에 헤더를 덧붙인다.

import { useEffect, useState } from "react";
import axios from "axios";

const GardenList = () => {
  const [gardenList, setGardenList] = useState<GardenItem[]>([]);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchGardenList = async () => {
      try {
        const apiKey = process.env.REACT_APP_API_KEY;
        const response = await axios.get(`http://localhost:8080/http://api.nongsaro.go.kr/service/garden/gardenList?apiKey=${apiKey}&pageNo=1&numOfRows=50`);
        				// 헤더를 덧붙인다!
        const items = response.data?.response?.body?.items?.item || [];
        setGardenList(items);
      } catch (error) {
        console.error('Error fetching garden list:', error);
        setError("An error occurred while fetching the data.");
      } finally {
        setLoading(false);
      }
    };

    fetchGardenList();
  }, []);

 

 

이제 평소처럼 로컬 서버를 실행하면, CORS 에러가 해결된 것을 확인할 수 있다.

yarn start

 

 

 

 

 

+ 한가지 더

참고로 로컬서버를 내린다거나, 브라우저를 끈 뒤 다시 실행하는 경우 CORS 에러가 다시 나타난다.

이것은 cors-anywhere 프록시 서버 또한 내려갔는데, 다시 실행하지 않았기 때문이다.

침착하게 프록시 서버를 올리고, 그 후 로컬 서버를 올려 테스트하자.

 

 

 

 

참고

Brie.log

이모저모

+ Recent posts