그룹 프로젝트를 오랜만에 실행해 봤는데 `https://countryflagsapi.com/png/brazil` 을 불러오는 중에 에러가 발생했다. 

나라의 국기 사진을 api로 불러올 수 있는 사이트이다. 

 

아마도 cors 오류라고 생각이 들었지만 혹시 다른 api로는 될까 싶어서 이번엔 비슷한 사이트인 이곳에서 해보려고 했지만 여기는 나라코드로 분류하기 때문에 내 프로젝트에서 사용하려면 복잡해질 것 같아서 지금 상황에서 해결해야 했다.

 

CORS?

Cross Origin Resource Sharing, 교차 출처 리소스 공유

 

http 헤더 기반 메커니즘으로 한 출처에서 실행 중인 웹 어플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. CORS 에러는 브라우저가 내뿜는 것이다. 

(보안 상의 이유로, 브라우저는 교차 출처 http 요청을 제한한다. 다른 출처의 리소스를 불러오려면 그 출처에서 올바른 CORS 헤더를 포함한 응답을 반환해야 한다.)

참고: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

 

Origin

 

Origin프로토콜(http, https,,), 호스트 이름(www와 같이 기기끼리의 통신 시 각 기기가 서로를 식별하도록 할당한 이름) 및 포트의 조합이다.

같은 origin이라면 same-origin(동일 출처), 다른 origin이라면 cross-origin(교차 출처)이다.

예를 들어 URL이 https://www.example.com:443/foo 인 경우 origin은 https://www.example.com:443 이고 same-origin이고 포트가 생략되어도 상관 없다.

 

cross-origin은 origin이 정확히 맞아떨어져야 하며 그 외는 모두 cross-origin이다.

  • 도메인이 다르거나
  • 하위 도메인이 다르거나
  • 다른 프로토콜이거나
  • 다른 포트 번호이거나

 

SOP와 CORS의 관계

SOP는 동일 출처 정책이고, CORS는 교차 출처 자원 공유다. 

SOP는 다른 origin으로 요청을 보낼 수 없도록 금지하는 브라우저의 기본적인 보안 정책이다. 즉, 동일한 origin으로만 요청을 보낼 수 있도록 하는 것이다. 그러나 몇가지 예외 사항에서는 다른 origin으로 요청을 보내야 하는데 그 때 지켜야 하는 정책이 CORS인 것이다. 원래대로라면 SOP에 의해 막히게 될 요청을 풀어주는 정책이라고 할 수 있다.

 

여기서 몇 가지 예외 사항이란,

1. <script>태그로 javacript를 실행하는 경우,

2. 이미지를 렌더링하는 경우,

3. <link>태그로 스타일 시트 파일을 불러오는 경우,

4. HTML 문서를 화면에 보여주는 경우를 말한다. 

출처: https://www.rfc-editor.org/rfc/rfc6454#section-3.4.2

 

CORS 해결하기

CORS의 기본적인 동작원리는 단순하다. 브라우저는 다른 origin으로 요청을 보낼 때 origin 헤더에 자신의 origin을 설정하고, 서버로부터 응답을 받으면 Access-Control-Allow-Origin 헤더에 설정된 origin 목록에 요청의 origin 헤더 값이 포함되는 지 검사하는 것 뿐이다. 즉, 응답의Access-Control-Allow-Origin 헤더에 허용되는 origin 또는 *을 설정해주면 된다.

 

하지만 이 방법은 server에서 해결하는 방법이고 만약 server를 수정할 수 없거나 외부 api를 사용하는 경우엔 클라이언트에서 처리를 해주어야 하는데, 그 방법 중 하나가 프록시 서버를 이용하는 것이었다.

 

프록시 서버란 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해 주는 서버를 가리킨다. 즉 프론트에서 프록시 서버를 거쳐서 출처를 response와 같게 수정하고, server에 접근하도록 하는 것이다. (나중에 강의를 통해서 더 이해하고 싶다.)

 

React에서 proxy server를 구축하는 방법으로 http-proxy-middleware를 사용했다. 하지만 배포할 땐 적용이 안 되고, 개발할 때만 적용이 된다..

 

먼저 http-proxy-middleware를 설치한다.

 yarn add --dev http-proxy-middleware

 

setupProxy.js를 src 밑에 세팅한다.

const { createProxyMiddleware } = require("http-proxy-middleware");

module.exports = function (app) {
  app.use(
    createProxyMiddleware("/png", {
      target: "https://countryflagsapi.com",
      changeOrigin: true,
    }),
  );
};
  • createProxyMiddleware("/png", {})의 "/png"는 "/png"로 시작하는 endpoint를 가진 api와 모두 매칭시킨다
  • target에는 url의 endpoint를 제외한 출처만 명시한다. https://countryflagsapi.com/png/brazil의 경우, endpoint를 제외한 /png를 제외한 https://countryflagsapi.com로 명시한다.
  • changeOrigin는 호스트 헤더의 출처를 대상 URL로 변경 하는지 여부입니다. CORS 처리를 위해 출처를 수정해주어야 합니다. 기본값은 false이다.
더보기

**endpoint ?

API란 두 시스템, 어플리케이션이 상호작용할 수 있게 하는 프로토콜의 총 집합이라면, endpoint는 API가 서버에서 리소스에 접근할 수 있도록 가능하게 하는 URL이다. 일종의 진입점이다.

 

api 호출하는 파일

url의 endpoint인 "/png/~"로 호출합니다. proxt를 세팅하면 서버를 재시작해준다.

<ListItemAvatar>
  <Avatar
    alt="국기"
    src={`/png/${data?.country}`}
  />
</ListItemAvatar>

 

결과

나라 국기 이미지가 잘 출력됨을 알 수 있다.

 

 

항상 cors 에러,,cors 에러,, 많이 들어보았지만 이렇게 맞닥뜨린 건 처음같다. 옛날에 공부해 본 적은 있는데 이렇게 실전에서 해결..?하고 나니까 좀 더 이해가 된 느낌이다. 나중에 다시 만나도 반갑게 해결할 수 있겠지?

 

 

참고 사이트:

https://www.datoybi.com/http-proxy-middleware/

https://xiubindev.tistory.com/115

https://it-eldorado.tistory.com/163

https://evan-moon.github.io/2020/05/21/about-cors/

'프로젝트 > 엘리스 2차' 카테고리의 다른 글

useEffect called twice in Strict Mode?  (0) 2022.05.19

2차 프로젝트 수행 중에 마주친 이슈였다! 

마이페이지 창을 들어가는데 만약 로그인이 안된 상태라면 바로 로그인 창으로 이동하게 useEffect를 사용해서 처음 렌더링될 때 검사하도록 아래처럼 코드를 작성했었다.

하.지.만, useEffect 안의 코드가 두번 실행이 되는 것이다!

useEffect(() => {
    getUserData();
    getUserLogs();

    if (!isLoggedin) {
      alert("로그인을 먼저 해주세용")
      navigate(ROUTES.LOGIN.link, { replace: true });
    }
  }, []);

 

코치님과의 오피스아워 시간으로 strict 모드를 제거하면 두번 실행되는 것이 멈춘다는 것을 알 수 있었다. 하지만 이것은 단지 리액트에서의 버그인 걸로 넘어갔었다.

 

하지만!

 

useEffect(() => { ... }, []) 는 strict 모드 하에서는 두 번 실행되며, 이것은 버그가 아니라 애초부터 의도된 것이다!

https://ko.reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects

위의 리액트 공식문서를 본다면 strict 모드에서 react는 예상치 못한 부작용 검사를 위해 아래의 함수라면 의도적으로 이중으로 호출하여 찾을 수 있게 한다는 것을 알 수 있다!

나는 useEffect 안에서 setState 함수를 사용하고 있었기 때문에 그 조건에 부합하였기 때문에 두번 호출해준 것이었다!

 

공식문서 내용을 찬찬히 살펴보다가 facebook의 react 깃헙 레포지토리의 issue까지 접근할 수 있었다.

https://github.com/facebook/react/issues/20090#issuecomment-715927125

그리고 이곳에 모든 내용들이 더 자세하게 적혀져 있는 걸 알 수 있었다!

앞으로는 이렇게 막히는 문제들이 생길 때 공식문서도 보고 구글링도 해야 하지만 이렇게 직접 해당 프레임워크를 개발한 리포의 이슈들에서도 찾아보는 습관을 가져야 겠다!

구글링을 했을 때는 이렇게 고치면 되겠군. 으로 그쳤었지만 이곳에서는 직접 만든 개발자분으로부터 자세하게 해당 이슈에 대해서 설명해주신 것을 볼 수 있어서 신기했다.

 

위 이슈도 다시 찬찬히 읽어보면서 공부 해야겠다.

 

https://github.com/facebook/react

 

GitHub - facebook/react: A declarative, efficient, and flexible JavaScript library for building user interfaces.

A declarative, efficient, and flexible JavaScript library for building user interfaces. - GitHub - facebook/react: A declarative, efficient, and flexible JavaScript library for building user interf...

github.com

 

+ Recent posts