때는 바야흐로 작년 12월 중순, 그때 갑작스레 프론트쪽에서 연락이 왔다.
CORS가 발생한다는 이슈.. CORS는 사실 Backend
에서 설정이 중요하지만,
Frontend
에서도 설정을 해야하기 때문에, 누구의 잘못으로 발생하는지 바로 알기는 쉽지 않다.
프론트쪽 둘 다 그런 이슈가 발생하다니 다시 코드를 천천히 살펴보았다. 🔎
사실 그 당시에는 뭐 때문인지 몰랐는데 내가 추리한 결과를 이야기 해보겠다.
- [발단] API 호출할 때 보안을 위해 JWT 토큰으로 검증 방식으로 변경하자는 논의
- [전개] 로그인을 제외한 대부분의 API에 토큰을 헤더에 넣어 보내는 방식으로 변경함
- [위기] 프론트쪽에서 갑자기 CORS가 발생한다는 이슈
- [절정] 나 : "왜 CORS?????" 고민하면서 많은 테스트
- [결말] 어쨌든 해결함 ..ㅎ
거의 개발자 드라마;
지금보면 굉장히 간단했다.
새롭게 헤더에 "token"
이라고 추가했고, token
의 헤더가 허용이 되지 않았으니 CORS 오류가 발생한 것으로 보인다. 왜 지금 보니 굉장히 간단한걸.. 8시간 정도 붙잡고 있었던거 같은데..
물론 이 방식은 굉장히 고전적입니다. 제 머리속에서 나온 생각이라 굉장히 구린 아이디어 입니다.
아마 찾아보면 더 괜찮은 방식이 있을 수 있습니다.
게다가 React
는 전혀 모르고, 내 컴퓨터에는 세팅이 안되어 있기 때문에 실행되지도 않을 것이다.
그러면 내가 cross-origin
상황만 만들어서, 브라우저에서 내가 요청을 보내보자!
환경
- 요청 Client : AWS 서버(가짜 IP)
151.253.345.42
- Server : Spring Boot Tomcat
localhost:8080
- 헤더 내 토큰 : 헤더 내부에
token
을 담아 요청하기- 브라우저 : Chrome
나는 서버를 가지고 있어서 다른 도메인에서 보낼 수 있었다.
테스트 기기가 두개라면 서로 다른 네트워크 망에 연결해서 요청을 보내면 외부 주소로 설정되니
cross-origin
상황이 만들어진다.
❗ 물론 실제 사용할 환경에서 테스트하는게 제일 좋습니다 ❗
❗ 저는 이 방식으로 테스트를 했는데, 되도록이면 따라하지 않는 것이 좋습니다.
해결이 안될 확률도 매우 높습니다. ❗
HTTP 통신
에서 헤더에 꼭 추가해서 보내야 할 내용이 있다면 아래 확장 프로그램을 설치하자.
⚠️ 헤더에 필요한 내용이 없다면 넘어가도 괜찮다.
설치 후 Chrome 우측 상단 URL 우측에서 실행 할 수 있다.
아래와 같이 내가 보내고 싶은 헤더를 추가해서 체크해두면 준비 완료이다.
현재 요청을 보내는 곳이 151.253.345.42
이므로 Chrome에서 URL로 접속해주자.
여러분들은 요청을 보낼 곳의 IP 주소로 접속하시면 됩니다! 위의 주소는 가짜입니다..!
Spring Boot를 실행하여 서버를 연다.
허용할 IP 주소에 151.253.345.42로 작성한다.
package -;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
import java.util.List;
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(false);
// config.setAllowedOrigins(Arrays.asList("허용할 IP 주소"));
config.addAllowedOrigin("151.253.345.42");
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
config.setExposedHeaders(List.of("token","Authorization","Content-Type", "Accept"));
config.setAllowedHeaders(List.of("token","Authorization","Content-Type", "Accept"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
[Spring Security를 이용한 방식도 있다...]
Chrome에서 F12를 눌러 관리자 도구를 켠 후 Console
에 들어간다.
Console에 fetch 메소드를 호출하여, HTTP 통신
할 수 있다.
내가 설계한 API 내용대로 Body
와 method
, Header
는 자유롭게 작성하면 된다.
요청을 보낼 IP 주소
는 현재는 로컬이므로 내 IP 주소
를 써넣으면 된다. 내 IP 주소 확인 사이트
아래 응답을 받았으니 열어보도록 하자.
뭐,, 다 모르겠지만, 현재 200이지만 CORS가 발생한 것을 볼 수 있다.
❓React에서 확인한다면?
React에서 요청을 보내면 더 상세하게 알려준다.
이때 다들 바빴기 때문에, 소통이 쉽지 않아 이 오류를 시간이 꽤 지난 뒤에나 봤었다.
내용은 "token"이라는 헤더가 허용되지 않았다는 것이다.
보안상 문제 때문에 헤더의 내용도 제한을 할 수 있다.
이러면 JAVA에서 허용할 헤더에 token
을 추가하면 된다...
혼자 테스트하면 이런 내용 일절 안나온다. 그러니 여러분들은 프론트엔드와 회의하면서 수정하시면 훨씬 편하다.
개발자도구에서 Network
로 들어가보자.
열어보면 Json의 POST
방식이기 때문에 Preflight
방식으로 진행 한 것을 알 수 있다.
실제로 Preflight
를 열어보면 Header
에서 우리가 설정한 내용이 잘 담겨있다.
이때는 우리가 CORS가 발생하지 않고, 잘 허용한 세팅이 반영되었을 때 내용이다.
나는 테스트 한다고 이렇게 코드를 짰었다. 이렇게 하면 아래 151.253.345.42
만 허용된다..
만약 여러 Origin을 허용하고 싶다면, 다음과 같이 바꾸어야 한다.
addAllowedOrigin
이 아닌, setAllowedOrigins(List)
로 여러 도메인을 한번에 허용해야 한다.
나는 이 문제 때문에 계속 React
에서는 CORS가 발생하고, 내가 테스트 할 때는 잘 되고..
아오 멍청아
되도록이면 Header도 addAllowedHeader(*)
보다는 setAllowedHeaders(List)
로 제한하는 것이 좋다.
그리고 나는 JWT Token을 "token"
헤더에 넣었는데 실제 규격인 "Authorization"
헤더로 변경했다.
헤더도 허용되지 않은 내용은 CORS 정책으로 차단되기 때문에, 실제 정의된 "Authorization"
헤더를 사용하자.
나는 다들 바쁘니 혼자 해결해보겠다고, Frontend 환경에서 테스트가 아닌 임의로 상황을 만들어서 테스트 했는데, 이게 오히려 독이 되어 더 해결이 늦어졌다.
실제 개발하면서 서로 논의하고, 의견 공유를 통해서 빠르게 오류 내용을 전달해주었다면 훨씬 수월하게 해결할 수 있지 않았을까 라는 생각이 있다.
개발에서 중요한건 커뮤니케이션이라는 것을 다시 느낀다!
하지만 스스로 테스트 해보고 실제 preflight 요청 방식을 뜯어보고, 개념을 더 이해하기 좋았다.
실제 공부한 내용을 스스로 보니 더욱 이해가 빨랐다.