프론트엔드와 백엔드 간의 통신
CORS 정책
- 교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다.
- 브라우저는 SOP에 의해 기본적으로 다른 출처의 리소스 공유를 막지만, CORS를 사용하면 접근 권한을 얻을 수 있게 되는 것

- 다른 출처의 리소스를 가져오려고 했지만 SOP 때문에 접근이 불가능합니다.
CORS 설정을 통해 서버의 응답 헤더에 ‘Access-Control-Allow-Origin’을 작성하면 접근 권한을 얻을 수 있습니다.
- 에러는 CORS 때문이 아니라, SOP 때문입니다. CORS는 오히려 이 에러를 해결해줄 수 있는 방안
CORS 정책 동작 방식
- 프리플라이트 요청 (Preflight Request)
- 실제 요청을 보내기 전, OPTIONS 메서드로 사전 요청을 보내 해당 출처 리소스에 접근 권한이 있는지부터 확인하는 것을 의미합니다. 브라우저는 서버에 실제 요청을 보내기 전에 프리플라이트 요청을 보내고, 응답 헤더의 Access-Control-Allow-Origin으로 요청을 보낸 출처가 돌아오면 실제 요청을 보내게 됩니다. 만약에 요청을 보낸 출처가 접근 권한이 없다면 브라우저에서 CORS 에러를 띄우게 되고, 실제 요청은 전달되지 않습니다.
- 단순 요청 (Simple Request)
- 단순 요청은 특정 조건이 만족되면 프리플라이트 요청을 생략하고 요청을 보내는 것을 말합니다.
- 인증정보를 포함한 요청 (Credentialed Request)
- 요청 헤더에 인증 정보를 담아 보내는 요청입니다. 출처가 다를 경우에는 별도의 설정을 하지 않으면 쿠키를 보낼 수 없습니다. 민감한 정보이기 때문입니다. 이 경우에는 프론트, 서버 양측 모두 CORS 설정이 필요합니다.
CORS 정책이 필요한 이유
- 라이브 데이터(live data)
- 실제 서비스되고 있는 앱의 데이터베이스(Data Base, DB)에 적재되고 있는 데이터를 의미합니다. 유저 및 상품, 결제 등 다양한 정보들을 예로 들 수 있습니다.
- 라이브 데이터는 민감성이 높은 데이터들이 위주이기 때문에 보안이 무엇보다 중요
- 서비스 및 프로젝트가 모든 출처의 접근을 허락한다면 이러한 보안성이 현저히 낮아지고 해킹의 위험에 그대로 노출되게 되므로, 모든 출처의 접근을 허락해서는 안 되는 것입니다. 그래서 CORS 정책이 필요한 것
프론트엔드에서 CORS 정책을 우회하는 방법
- Webpack Dev Server에서 제공하는 proxy 기능을 이용하는 방법
- http-proxy-middleware 라이브러리를 깐 뒤 해당 라이브러리에서 제공하는 proxy 기능을 이용하는 방법
백엔드에서 CORS Error를 해결하는 방법
애너테이션(@)을 이용해서 CORS 설정하는 방법
@CrossOrigin(origins = "http://example.com")
@RestController
@RequestMapping("/account")
public class AccountController {
@RequestMapping(method = RequestMethod.GET, path = "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
- 클래스 수준에서 @CrossOrigin을 추가한 것
- origins 뒤로 오고 있는 특정 도메인에 대해서만 허용하고 있음을 확인할 수 있습니다. 이런 식으로 클래스 수준에서 @CrossOrigin을 추가하게 되면 클래스 내부에 작성된retrieve()와 remove() 메소드 둘 다 특정 도메인에 대해 허용하게 됩니다.
@CrossOrigin(origins = "http://example.com, http://example2.com")
...
- 만약 허용해야 하는 도메인이 여러 개라면, 도메인 뒤에 콤마(,)를 붙여 이어주면 됩니다.
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@RequestMapping(method = RequestMethod.GET, path = "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
- 특정 메서드만 CORS 설정을 할 수도 있습니다.
- class 안의 retrieve() 메소드 위에 @CrossOrigin을 붙여줌으로써 retrieve() 메소드만 허용한 것
- remove() 메소드는 CORS 설정을 해주지 않았으므로 CORS 정책에서 자유롭지 않다.
- 여기서는 다른 옵션 없이 @CrossOrigin 만 붙여줬습니다. 이렇게 붙이게 되면 모든 도메인, 모든 요청방식에 대해 허용하게 된다는 의미
글로벌 CORS 설정
package com.codestates.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.headers().frameOptions().sameOrigin()
.and()
.csrf().disable()
.cors(withDefaults()) // 1. 여기에 집중해주세요!
.formLogin().disable()
.httpBasic().disable()
.authorizeHttpRequests(authorize -> authorize
.anyRequest().permitAll()
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
// 2. 여기에 집중해주세요!
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET","POST", "PATCH", "DELETE"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("\/**", configuration);
- 1번 부분 / CORS 설정을 추가하는 것입니다. .cors(withDefaults())일 경우, corsConfigurationSource라는 이름으로 등록된 Bean을 이용
- 2번 부분 / CorsConfigurationSource Bean 생성을 통해 구체적인 CORS 정책을 설정하는 곳입니다. 모든 걸 볼 필요는 없다.
- setAllowedOrigins()을 통해 출처(Origin)에 대해 스크립트 기반의 HTTP 통신을 허용하도록 설정합니다. 현재 *로 되어 있으므로, 모든 출처에 대해 허용하겠다는 의미를 내포하고 있다고 보면 됩니다.
- setAllowedMethods()를 통해 파라미터로 지정한 HTTP Method에 대한 HTTP 통신을 허용합니다. 여러분이 익숙한 HTTP method가 보일 것입니다.
백엔드에서 API를 작성하는 방법

- Spring Rest Docs과 API 문서 스니핏으로 만들어진 API 문서
- 첫 번째 네모 박스의 http-request로 적혀 있는 부분은, 클라이언트가 이렇게 요청을 보내줘야 한다고 명시하고 있는 부분
- 두 번째 네모 박스로 시선을 옮기면, POST와 /v11/members 라고 적혀 있습니다. 해당 요청은 POST method이고, api는 /v11/members라는 것을 의미
- 밑의 줄은 accept: application/json 이라 적혀 있습니다. 해당 요청을 서버 쪽으로 보낼 때는 request body 부분을 JSON 형식으로 맞춰줄 것을 요구하는 것
- 세 번째 네모 박스는 request body의 예시 입니다. 예시에 작성되어 있는 형식대로 맞춰 POST 요청을 보내라고 요구하고 있다고 이해하면 된다.
- 네 번째 네모 박스 안에 있는 테이블은 예시로 적혀 있는 request body를 조금 더 자세히 설명하고 있는 부분입니다. 첫 번째 행의 email 필드는 string 형식이고, 이메일이라고 부가 설명