CORS 알았다고 생각하면 끝인 줄 알았지? 진짜 정확하게 알았어? 는 제 얘기입니다.
제가 기존까지 알고 있던 CORS는 대강 이정도였습니다.
기존에 CORS에 대한 내 지식
- CORS 등장 배경
- 기존에 브라우저에서는 SOP라는 단일 오리진 정책이 있었다. 이는 동일한 오리진인 경우에만 요청을 주고 받을 수 있게 하는 정책이다.
- 하지만 웹이 발전함에 따라 다른 오리진 간에도 요청을 주고 받는 일이 필요해졌고 이때 등장한 것이 CORS이다.
- CORS란
- 크로스 오리진 어쩌고 정책으로 두 서버가 오리진이 달라도 요청을 주고 받을 수 있게 하는 허용하는 정책이다.
- 브라우저는 CORS를 위반했는지를 여러가지 방법으로 검사한다.
- 그중에 하나가 preflight 요청인데 미리 본요청을 보내기 전에 OPTION이라는 Http Method로 사전 요청을 보낸다.
- 이때 preflight 요청이 실패하면 브라우저는 CORS를 위반했다고 판단하기 때문에 본요청도 안 간다.
- 스프링 허용 방법
- 스프링에는 WebMvcConfigure인가를 상속받아 Cors 설정을 허용하는 메서드를 재정의하던가
- Spring Security을 통해 관련 설정을 한 빈을 등록하여 허용 오리진이나 헤더 등등을 설정해야 한다.
이 정도로 알고 대강 나는 다 알고 있다 라고 생각했는데 문제는 서비스 배포 전날 터졌습니다.
문제 발생 🚨 삐용삐용 🚨
갑자기 프론트엔드 개발자에게서 CORS가 뜨는데????라는 문의를 받고 멘탈이 팡하고 터졌습니다.
당시 상황은 이러했습니다.
1. 이미 우리 백엔드 코드에는 다음과 같이 모든 출처와 Method, 헤더를 허용하는 CORS 설정이 되어있었고
2. 나는 운영 배포 직전에 특정 path에 접근할 경우 로그인된 상태인지를 검증하는 인터셉터를 구현해 개발 서버에 올렸었다.
3. 해당 인터셉터를 제외한 다른 백엔드 코드들은 이미 QA가 끝났다. 즉 다른 부분에서는 CORS 에러가 발생하지않았다.
결론은 내 인터셉터 웨구래?
내 인터셉터에서만 문제가 발생하는 것이 파악되었기 때문에 해당 인터셉터에 구현이 잘못된 부분이 있는지를 체크하였다.
인터셉터는 쿠키를 통해 가지고 온 인증값을 검증하는 로직으로 이루어져 있었는데, 눈을 씻고 앞으로 돌아 뒤로 돌아 보아도 이상함은 없었다.
그런데 에러가 발생하는 응답에 메시지가 "쿠키가 없어서 회원 정보를 찾을 수 없습니다. 다시 로그인해주세요."였다.
위와 같이 인증 인터셉터에서는 해당 요청을 CredentialManager에게 위임하여 인증값을 추출하고 값을 받게 되는데,
당시에는 Cookie를 통해 인증 값을 주고 받고 있었기 때문에 CookieCredentialManager를 구현하여 사용하고 있었다.
CookieCredentialManager에서는 쿠키 자체가 없을 때 해당 메시지를 가진 예외를 터트리는데 이를 통해 요청에 쿠키가 오지 않는다는 것을 알게 되었다.
쿠키가 오지 않고 있기 때문에 프론트 쪽의 문제인가 싶어서 함께 디버깅을 하였지만 프론트는 쿠키를 보내고 있었고 백엔드 서버의 로깅에서는 쿠키를 잡을 수 없었다.
그렇게 관련 문제들을 서치해보았지만 Config 설정 말고는 이유를 알 수 없었고 당장은 인터셉터를 제거하고 릴리즈 하기로 했다.(🦠 찝찝)
마침 그 다음날 새 기능 회의에서 전체 비공개 서비스였던 공개 서비스로 전환하기로 하였고 해당 이슈는 잊혀져가는 듯 했다.
CORS 설정은 아니지만 CORS를 잘 이해하자..
그렇게 다른 기능 구현을 신나게 하고 있던 와중, 다른 팀의 러쉬가 비슷한 문제를 겪고 있는 것을 확인했다.
인터셉터에서 헤더 값을 가지고 오지 못하는 문제였는데 다른 크루가 CORS 때문이라는 것을 알려주었다.
알고보니 Prefilght 요청일 경우 기본적으로 아래의 2개 또는 3개의 헤더값만 포함하여 요청을 나가게 된다.
- Origin
- Access-Control-Request-Method
- Access-Control-Request-Headers(Optional)
따라서 Cookie 또는 Authorization Header를 찾을 수 없게 되는 것이다.
사전 요청에서는 인증 검증이 실패할 수도 있지만, 사전 요청의 요점은 서버가 특정 오리진, HTTP 메서드, 헤더를 허용하는지 검사 하는 것이기 때문에 우리 서버의 인증은 불필요하다고 생각했다.
따라서 이를 해결하기 위해 다음과 같이 preflight 요청일 경우 검증을 무시하는 로직을 작성하여 해결하였다.
'우아한테크코스 6기 > 3단계' 카테고리의 다른 글
Loki, Promtail, Grafana를 사용한 로그 모니터링 구축 (0) | 2024.08.13 |
---|---|
우아한 스키마 관리를 위한 flyway 도입 (3) | 2024.08.13 |
Logback MDC로 쉽게 요청 추적하기 (0) | 2024.08.11 |
NoResourceFoundException 에 대해 알아보자 (0) | 2024.08.11 |
팀 로깅 전략 구상기 (0) | 2024.08.05 |