안녕하세요. 꿀발자입니다. 웹 개발에서 CORS 오류는 데이터 통신을 방해하는 흔한 문제 중 하나입니다. 특히, “Cross-Origin Request Blocked” 오류는 서버와 클라이언트의 설정이 맞지 않아 발생하는 경우가 많습니다. 이번 글에서는 이러한 오류의 원인을 분석하고, 서버 및 클라이언트 측에서 해결할 수 있는 방법을 알아보겠습니다. 또한 프록시 서버를 이용한 우회 방법과 개발 시 유용한 팁까지 자세히 다룰 예정입니다.
Cross-Origin Request Blocked 오류의 원인
Cross-Origin Request Blocked 오류는 주로 서버의 CORS 설정 부재, 프리플라이트 요청 실패, 자격 증명 포함 요청 실패의 경우에 발생합니다. (위키백과)
- 서버의 CORS 설정 부재: 서버가 CORS 헤더를 제대로 설정하지 않으면 브라우저에서 요청이 차단됩니다.
- 프리플라이트 요청 실패: 브라우저가 사전 요청(OPTIONS 메소드)을 보내고, 서버가 적절한 응답을 반환하지 않을 경우 발생합니다.
- 자격 증명 포함 요청 실패: 인증 헤더나 쿠키가 포함된 요청에서 서버가 이를 허용하지 않는 경우입니다.
서버 CORS 설정
서버 측에서 CORS 설정을 하여 문제를 해결하려면 적절한 응답 헤더를 추가해야 합니다. 다음은 주요 설정 항목과 예제입니다. (tosspayments doc)
Access-Control-Allow-Origin | 허용할 출처를 명시, 모든 출처 허용(*) |
Access-Control-Allow-Methods | 허용할 HTTP 메소드(GET, POST, PUT 등) 설정 |
Access-Control-Allow-Headers | 허용할 요청 헤더 명시 |
Access-Control-Allow-Credentials | 자격 증명 포함 요청 허용 시(true) |
Ex1. CORS 설정 – Node.js
const express = require('express'); const app = express(); // CORS 설정 미들웨어 app.use((req, res, next) => { // 허용할 출처 res.header('Access-Control-Allow-Origin', 'http://example.com'); // 허용할 메소드 res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); // 허용할 헤더 res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // 인증 포함 요청 허용 res.header('Access-Control-Allow-Credentials', 'true'); next(); }); app.get('/', (req, res) => { res.send('CORS 설정 완료!'); }); app.listen(3000, () => { console.log('서버가 3000번 포트에서 실행 중입니다.'); });
Ex2. CORS 설정 – JAVA
dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' }
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import java.util.Arrays; @SpringBootApplication public class CorsExampleApplication { public static void main(String[] args) { SpringApplication.run(CorsExampleApplication.class, args); } @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(Arrays.asList("http://example.com")); // 허용할 출처 config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); // 허용할 메소드 config.setAllowedHeaders(Arrays.asList("Content-Type", "Authorization")); // 허용할 헤더 config.setAllowCredentials(true); // 인증 포함 요청 허용 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); // 모든 경로에 CORS 설정 적용 return new CorsFilter(source); } }
클라이언트 CORS 해결
클라이언트 CORS 해결을 하는 방법으로는 JSONP 사용, 브라우저 확장 프로그램 사용, 프록시 서버 사용하여 해결 할 수 있습니다. 서버 설정을 변경할 수 없는 경우, 클라이언트에서 우회하는 방법을 사용할 수 있습니다. (mozilla doc)
- JSONP 사용: 서버가 JSONP를 지원할 경우 GET 요청에 한해 데이터를 가져올 수 있지만 권장하지 않습니다.
- 브라우저 확장 프로그램: 개발 중 CORS 오류를 무시하게 하는 확장 프로그램을 사용할 수 있습니다. (배포 환경에서는 비추천)
- 프록시 서버 사용: 프록시 서버는 클라이언트와 타겟 서버 사이에 위치하여 동일 출처 정책을 우회할 수 있습니다.
Ex1. 프록시 서버 구현 – Node.js
const express = require('express'); const { createProxyMiddleware } = require('http-proxy-middleware'); const app = express(); app.use('/api', createProxyMiddleware({ target: 'http://example.com', // 실제 타겟 서버 changeOrigin: true, })); app.listen(3000, () => { console.log('프록시 서버가 3000번 포트에서 실행 중입니다.'); });
Ex2. 프록시 서버 구현 – JAVA
dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.cloud:spring-cloud-starter-gateway' }
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; @SpringBootApplication public class ProxyServerApplication { public static void main(String[] args) { SpringApplication.run(ProxyServerApplication.class, args); } @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("proxy_route", r -> r.path("/api/**") // 클라이언트가 /api로 요청하는 경우 .uri("http://example.com") // 타겟 서버 .filters(f -> f.rewritePath("/api/(?<segment>.*)", "/${segment}")) // 경로 재작성 ) .build(); } }
https://honey-dev.com/웹-보안-개발자가-알아야-할-기본-사항/
CORS 오류 피하는 팁
개발 시 CORS 오류를 피하는 팁으로 개발 환경에서 CORS 비활성화, 환경 분리, 자동화된 CORS 설정이 있습니다.
- 개발 환경에서 CORS 비활성화: 브라우저의 보안 설정을 임시로 변경하거나, 개발용 확장 프로그램을 사용합니다.
- 환경 분리: 프론트엔드와 백엔드를 동일한 도메인 또는 서브도메인에서 동작하도록 설정합니다.
- 자동화된 CORS 설정: 서버 설정을 코드화하여 일관성을 유지하고, 배포 과정에서 자동으로 적용되도록 합니다.
Final Thoughts
Cross-Origin Request Blocked 오류는 서버와 클라이언트 간의 신뢰할 수 없는 데이터 요청을 방지하기 위한 브라우저의 보안 메커니즘입니다. 하지만 이로 인해 개발자들은 데이터 통신 문제에 직면하기도 합니다. 이번 글에서는 서버와 클라이언트 모두에서 해결할 수 있는 다양한 방법을 살펴보았습니다. 적절한 CORS 설정과 우회 방법을 활용하여 이러한 문제를 효과적으로 해결해 보세요!