CORS 에러에 대해 알아보자

2024. 4. 1. 01:33·IT

CORS란..?

현재 진행중인 국비과정에서 스프링으로 진입 전 MVC패턴에 대해 공부하고 또, JSP를 이용해서 Rest-API를 생성 후 html파일에서 API를 받아서 출력해보는 연습을 하게된 적이 있다.

학원에서 과제가 주어지면 내가 우물안의 개구리인건지 정말로 평균이상으로 개발에 적성이 잘맞는것인지는 잘 모르겠지만 어쨋든 항상 남들보다 빨리 끝내는 편이었다. API생성하는 것 까지는 누구보다 빠르게 완성하여 이제 출력만 시키면되는데 자꾸 Access어쩌고하는 에러가 뜬다...
도저히 혼자서 해결할 문제는 아닌거 같아서 열심히 구글링해서 이 에러가 CORS에러라는 사실을 알아냄.

그래서 CORS란 뭐냐?

Cross Origin Resource Sharing

CORS는 한 도메인 또는 Origin의 웹 페이지가 다른 도메인 (도메인 간 요청)을 가진 리소스에 액세스 할 수 있게하는 보안 메커니즘이다.

CORS는 서버와 클라이언트가 정해진 헤더를 통해 서로 요청이나 응답에 반응할지 결정하는 방식으로 CORS라는 이름으로 표준화 되었다. CORS는 최신 브라우저에서 구현된 동일 출처 정책(same-origin policy) 때문에 등장했다고 한다.

그럼 여기서 동일 출처 정책은 또 뭐냐 ㅋㅋ...

동일 출처 정책?

동일 출처 정책은 동일한 출처의 리소스에만 접근하도록 제한하는 것이다. 여기서 출처는 프로토콜, 호스트명, 포트가 같다는 것을 의미한다.

https://naver.com:80을 예시로 들면, https는 프로토콜, naver.com은 호스트명 80은 포트다.

왜 동일한 출처에서만 접근하도록 허용하는 것일까? 모든 출처를 허용하면 어떻게 될까?

https://bank.com 이라는 도메인 사이트가 있다 이 사이트의 api 주소는 https://bank.com/api이다. 사용자가 은행 사이트에서 로그인을 한 후 인증 토큰을 받았다. 그런데 사용자가 로그인한 상태에서 https://evil.com사이트에 접속하게 되면, https://evil.com사이트에서 https://bank.com/api로 ajax 요청을 보낼 때 유저가 획득한 인증 토큰이 자동으로 첨부되어 사용자인척하면서 요청을 보낼 수 있게 된다.

이렇게 자동으로 쿠키가 첨부되기 때문에 보안상의 이유로 브라우저는 HTTP 호출을 동일한 출처로 제한했다.

왜 CORS가 생겼을까?

그럼 왜 CORS가 필요하게 됐을까?
이전에는 동일한 도메인에서 리소스를 받아왔는데, 지금은 클라이언트에서 도메인이 다른 서버에서 제공하는 API를 사용하는 일이 많아졌다.
그래서 이전처럼 동일한 도메인간의 요청만 할 수 없어졌고 CORS가 생겼다고 한다.

그래서 CORS는 어떻게 해결하는가?

클라이언트에서 해결하기

  1. 웹 브라우저 실행 옵션이나 플러그인을 통한 동일 출처 정책 회피하기
    • 동일 출처 정책은 브라우저에서 임의로 하는 것이기 때문에 브라우저에서 동일 출처 정책을 사용하지 않으며 된다.
  2. jsonp 방식으로 json 데이터 가져오기
    • 자바스크립트 파일이나 css 파일은 동일 출처 정책에 영향을 받지 않고 가져올 수 있다.
    • 이를 이용해서 자바스크립트 파일을 가져와서 이를 json 형식으로 파싱해서 데이터를 사용할 수 있다.

이런 방법들이 있다고는 하지만 당시의 나는 어떻게든 html파일로 출력하고 플러그인을 설치하는 짓을 해서 해결하고 싶지않았다.
그래서 서버에서 해결하는 방법을 찾았고,해당 방법은 다음과 같다.

서버에서 해결하기

스프링 CORS

  1. @CrossOrigin 어노테이션 사용하기
    메소드 레벨 및 글로벌 레벨에서 srping mvc 애플리케이션에서 spring cors를 지원하는 방법이다. sprign mvc는 @CorssOrigin 어노테이션을 제공한다. 이 어노테이션은 어노테이션이 달린 메소드 또는 타입을 교차 출처를 허용하는 것으로 표시한다.
  2. @CrossOrigin(origin="*", allowedHeaders = "*") @Controller public class MainController { @GetMapping(path = "/") public String main(Model model) { return "main"; } }
  • origins
    • 허용된 출처, 이 값은 pre-flight 응답과 실제 응답 모두에 access-control-allow-origin헤더에 배치된다.
  • allowedHeaders
    • 실제 요청 중에 사용할 수 있는 요청 헤더 목록이다. pre-flight의 응답 헤더인 access-control-allow-header에 값이 사용된다.
  • allowCredential
    • 브라우저가 요청과 관련된 쿠키를 포함해야 되는지 여부를 결정한다.
    • 이 값이 true이면, pre-flight 응답에는 값이 true로 설정된 access-control-allow-credentials 헤더가 포함된다.

하지만 나는 아직 스프링을 제대로 배운적도 없고 현재 진행하고 있는건 JSP로 해결해야 하기에 내가 사용할 수 있는 방법이 아니었다.

  1. CorsFilter 사용하기
    서블릿 필터 인터페이스를 이용하여 개발되었다. 웹 서버의 모든 리소스의 요청을 가로채서 Cross domain request인지 체크하여 실제 요청 페이지에 전달하기전에 적절한 CORS 정책과 해더들을 적용한다.
  • Access-Control-Allow-Origin
    • 도메인 간 요청을 할 수 있는 권한이 부여된 도메인을 지정한다.
  • Access-Control-Allow-Credentials
    • 도메인 간 요청에 credential 권한이 있는지 없는지 지정한다.
  • Access-Control-Expose-Headers
    • 노출하기에 안전한 헤더를 나타낸다.
  • Access-Control-Max-Age
    • pre-flighted 요청이 얼마만큼의 시간동안 캐시되는지
  • Access-Control-Allow-Methods
    • 리소스에 접근할 때 메소드가 허용되는지
  • Access-Control-Allow-Headers
    • 어떤 헤더 필드 네임이 실제 요청에서 사용할 수 있는지 가리킨다.
package filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;

@WebFilter("/*")
public class CORS implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletResponse resp = (HttpServletResponse)response;

        resp.setHeader("Access-Control-Allow-Origin", "*");
        resp.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
        resp.setHeader("Access-Control-Allow-Headers", "Content-Type");

        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

}

해당 코드를 작성하고나서야 비로소 문제가 해결되었다...
거의 한시간넘도록 구글링하며 이것저것 찾아보고 이것저것 다 시도해봐도 안되다가 필터에서 리스폰스 헤더를 추가하면 된다는 사실을 알게되고 해결하였다.

ps. 사실 이거 해결하고도 PUT이랑 DELETE에서 또 다시 CORS에러 나서 정신병걸릴뻔했다 ㅋㅋ..
다시 에러난 이유는
java resp.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
이 코드에서 Methods를 Method라고 적어놨더라.. 30분동안 개고생하다 오타하나 고치고 해결했다..
오타났는대도 불구하고 GET이랑 POST는 안되던게 됬던 터라 오타인줄 상상도 못하고 있었다..ㅠㅠ
다들 제대로 작동안하면 오타는 없는지 다시 한번 확인해보도록 하여요 !

'IT' 카테고리의 다른 글

Spring 웹 애플리케이션 마이그레이션: 파일 권한 문제 해결기  (0) 2024.12.06
팀 프로젝트 회고 [Pet Hub]  (0) 2024.06.26
팀 프로젝트 회고 [Smart Farm]  (0) 2024.06.26
MVC 패턴의 낯선 세계  (0) 2024.04.10
객체지향의 5가지 원칙, SOLID에 대한 이해  (0) 2024.04.02
'IT' 카테고리의 다른 글
  • 팀 프로젝트 회고 [Pet Hub]
  • 팀 프로젝트 회고 [Smart Farm]
  • MVC 패턴의 낯선 세계
  • 객체지향의 5가지 원칙, SOLID에 대한 이해
Komorebi__
Komorebi__
Back-End 개발 학습일지
  • Komorebi__
    KOMOREBI@Controller
    Komorebi__
  • 전체
    오늘
    어제
    • 분류 전체보기 (74)
      • Today I Learned (7)
      • Language (1)
        • Java (1)
        • Python (0)
        • C (0)
      • Framework&Tools (5)
        • Spring (3)
        • GIT (2)
        • React (0)
      • IT (8)
        • 혼자 만들어 본거 (2)
      • Algorithm (2)
        • Baekjoon (34)
      • Mac (1)
      • 외국어 공부 (15)
        • 日本語の本の翻訳 (7)
        • English conversation (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    버전관리
    github
    형상관리
    git
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
Komorebi__
CORS 에러에 대해 알아보자
상단으로

티스토리툴바