프로그램 활성화시키면 로컬환경에서 API 테스트 시, CORS 문제 해결.

프론트에서 직접 서버에 리소스 요청을 했더니 서버에서 따로 설정을 안 해줘서 CORS 에러가 발생한다면 모든 출처를 허용한 서버 대리점을 통해 요청을 진행됩니다.
하지만 무료 프록시 서버 대여 서비스들은 악용 사례 때문에 API 요청 횟수 제한을 두어 실전에서는 사용이 어렵습니다.
실전에서는 직접 프록시 서버를 구축하여 사용.
직접 서버에서 HTTP 헤더 설정을 통해 출처를 허용하게 설정하는 것이 가장 정석적인 해결책!!
각 서버의 문법에 맞게 HTTP 헤더를 추가해주면 됩니다.
CORS에 연관된 HTTP 헤더 값 종류
# 헤더에 작성된 출처만 브라우저가 리소스를 접근할 수 있도록 허용함.
# * 이면 모든 곳에 공개되어 있음을 의미한다.
Access-Control-Allow-Origin : https://naver.com
# 리소스 접근을 허용하는 HTTP 메서드를 지정해 주는 헤더
Access-Control-Request-Methods : GET, POST, PUT, DELETE
# 요청을 허용하는 해더.
Access-Control-Allow-Headers : Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization
# 클라이언트에서 preflight 의 요청 결과를 저장할 기간을 지정
# 60초 동안 preflight 요청을 캐시하는 설정으로, 첫 요청 이후 60초 동안은 OPTIONS 메소드를 사용하는 예비 요청을 보내지 않는다.
Access-Control-Max-Age : 60
# 클라이언트 요청이 쿠키를 통해서 자격 증명을 해야 하는 경우에 true.
# 자바스크립트 요청에서 credentials가 include일 때 요청에 대한 응답을 할 수 있는지를 나타낸다.
Access-Control-Allow-Credentials : true
# 기본적으로 브라우저에게 노출이 되지 않지만, 브라우저 측에서 접근할 수 있게 허용해주는 헤더를 지정
Access-Control-Expose-Headers : Content-Length
서버에 response 헤더 값으로 Access-Control 설정을 해줌.
var http = require('http');
const PORT = process.env.PORT || 3000;
var httpServer = http.createServer(function (request, response) {
// Setting up Headers
response.setHeader('Access-Control-Allow-origin', '*'); // 모든 출처(orogin)을 허용
response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); // 모든 HTTP 메서드 허용
response.setHeader('Access-Control-Allow-Credentials', 'true'); // 클라이언트와 서버 간에 쿠키 주고받기 허용
// ...
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.end('ok');
});
httpServer.listen(PORT, () => {
console.log('Server is running at port 3000...');
});
Access-Control-Allow-Origin 헤더 값으로 *를 사용하면 보안이 허술해지기 때문에 출처를 직접 명시해주는 것이 정석!!
response.setHeader('Access-Control-Allow-origin', 'https://velog.io');
import javax.servlet.*;
public class CORSInterceptor implements Filter {
private static final String[] allowedOrigins = {
"http://localhost:3000", "http://localhost:5500", "http://localhost:5501",
"http://127.0.0.1:3000", "http://127.0.0.1:5500", "http://127.0.0.1:5501"
};
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestOrigin = request.getHeader("Origin");
if(isAllowedOrigin(requestOrigin)) {
// Authorize the origin, all headers, and all methods
((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Origin", requestOrigin);
((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Headers", "*");
((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Methods",
"GET, OPTIONS, HEAD, PUT, POST, DELETE");
HttpServletResponse resp = (HttpServletResponse) servletResponse;
// CORS handshake (pre-flight request)
if (request.getMethod().equals("OPTIONS")) {
resp.setStatus(HttpServletResponse.SC_ACCEPTED);
return;
}
}
// pass the request along the filter chain
filterChain.doFilter(request, servletResponse);
}
private boolean isAllowedOrigin(String origin){
for (String allowedOrigin : allowedOrigins) {
if(origin.equals(allowedOrigin)) return true;
}
return false;
}
}
// 스프링 서버 전역적으로 CORS 설정
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8080", "http://localhost:8081") // 허용할 출처
.allowedMethods("GET", "POST") // 허용할 HTTP method
.allowCredentials(true) // 쿠키 인증 요청 허용
.maxAge(3000) // 원하는 시간만큼 pre-flight 리퀘스트를 캐싱
}
}
특정 컨트롤러에만 CORS 적용하고 싶다면
@Controller
@CrossOrigin(origins = "*", methods = RequestMethod.GET)
public class customController {
// 특정 메소드에만 CORS 적용 가능
@GetMapping("/url")
@CrossOrigin(origins = "*", methods = RequestMethod.GET)
@ResponseBody
public List<Object> findAll(){
return service.getAll();
}
}

[
{
"AllowedHeaders": [
"Authorization"
],
"AllowedMethods": [
"GET",
"HEAD"
],
"AllowedOrigins": [
"http://www.example.com"
],
"ExposeHeaders": [
"Access-Control-Allow-Origin"
]
}
]
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
</init-param>
<init-param>
<param-name>cors.exposed.headers</param-name>
<param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
</init-param>
<init-param>
<!-- 쿠키 통신을 안하는데 이걸 true로 하면 4XX 서버 에러가 뜬다 -->
<param-name>cors.support.credentials</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>cors.preflight.maxage</param-name>
<param-value>10</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
nginx.conf 파일 안에 location / 부분에다가 add_header 값으로 헤더 설정을 추가
location / {
root html;
add_header 'Access-Control-Allow-Origin' '*';
index index.html index.htm;
}
CORS 정리
CORS가 뭔지, SOP 등이 뭔지는 알고 있지만 구체적인 깊이와 명확한 해결 방안에 대한 회고가 필요하다고 느껴 다시 블로그를 작성
스프링에서는 CORS 보안이 취약할 수 있다는 의견을 들어 Next.js에서 어떻게 해결하는지 프로젝트를 통해서 적용해보고 작성해보자.
🖇️ CORS 해결 방법