Search

CORS는 왜 필요한가: 프론트엔드 개발자가 알아야 할 보안 정책

프론트엔드 개발을 하다 보면, 종종 이런 에러를 마주한다.
“Access to fetch at ‘https://api.example.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy.”
이 문구를 처음 보면 대부분 “서버가 나를 싫어하나?” 싶지만, 사실 이는 브라우저가 나를 지켜주는 신호다.
이 글에서는 왜 CORS(Cross-Origin Resource Sharing)가 존재하는지, 그리고 프론트엔드 개발자가 어떤 관점에서 이해해야 하는지를 이야기한다.

동일 출처 정책(Same-Origin Policy)이란

CORS를 이해하려면 먼저 동일 출처 정책(Same-Origin Policy, SOP)을 알아야 한다.
브라우저는 보안상 이유로 같은 출처(origin)에서 제공되는 리소스만 자유롭게 접근할 수 있도록 제한한다.
“같은 출처”란 프로토콜, 도메인, 포트 번호가 모두 동일한 경우를 말한다.
예:
동일 출처: https://example.comhttps://example.com/data
다른 출처: https://example.comhttps://api.example.com (서브도메인 다름)
다른 출처: https://example.comhttp://example.com (프로토콜 다름)
즉, localhost:3000에서 실행 중인 React 앱이 localhost:8000의 API 서버에 요청을 보내면,
브라우저는 출처가 다르다고 판단하고 응답 접근을 차단한다.

왜 이렇게까지 막을까?

그 이유는 바로 CSRF(Cross-Site Request Forgery, 크로스사이트 요청 위조) 공격 때문이다.
이 공격은 사용자가 로그인된 상태를 악용해, 의도치 않은 요청을 타 사이트로 보내게 만드는 방식이다.
예를 들어보자. 사용자가 은행 사이트에 로그인한 상태에서, 공격자가 만든 악성 사이트를 방문한다고 하자.
이 악성 사이트가 다음과 같은 요청을 자동으로 보낸다면?
<img src="https://bank.com/transfer?to=attacker&amount=1000000" />
HTML
복사
사용자는 클릭도 하지 않았는데 자신의 계좌에서 돈이 빠져나갈 수 있다.
하지만 브라우저가 “이건 다른 출처에서 온 요청이야!”라고 판단하고 차단한다면,
이런 공격은 더 이상 먹히지 않는다. SOP는 바로 이런 CSRF 공격의 효과를 줄이기 위해 존재한다.

그런데, 합법적인 요청도 막힌다?

문제는 우리가 실제로는 이런 요청을 자주 보낸다는 것이다.
프론트엔드와 백엔드를 분리해 개발하는 환경에서는 localhost:3000 → localhost:8000으로 요청하는 경우가 흔하다.
개발 중인 프론트엔드가 API 서버에 요청을 보내야 하지만, SOP 때문에 브라우저가 이를 차단한다.
이때 등장한 것이 바로 CORS(Cross-Origin Resource Sharing)다.

CORS란 무엇인가

CORS는 브라우저에게 “이 요청은 안전하니까 허용해도 돼”라고 알려주는 표준 메커니즘이다.
서버가 특정 출처(origin)의 요청을 신뢰한다고 명시적으로 알려주는 방식이다.
즉, 서버가 허락하면 브라우저가 통신을 허용한다.

서버에서 CORS 설정하기

서버는 응답 헤더를 통해 어떤 출처를 허용할지 지정한다.
Access-Control-Allow-Origin: https://myfrontend.com
Plain Text
복사
이 헤더가 포함된 응답을 받으면, 브라우저는 응답을 보여준다.
모든 출처를 허용하고 싶다면 다음과 같이 설정할 수도 있다.
Access-Control-Allow-Origin: *
Plain Text
복사
하지만 보안상 이유로 실제 서비스에서는 이렇게 설정하는 것은 추천하지 않는다.
API 키나 인증 정보가 포함된 요청이라면, 반드시 정확한 도메인만 허용해야 한다.
추가로 허용할 메서드나 헤더를 명시할 수도 있다.
Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Type, Authorization
Plain Text
복사