[WIL] 항해 5주차 회고(feat. CORS)

rara_kim·2022년 12월 18일
0

항해99

목록 보기
13/18

한주 동안의 이야기

5주차인 주특기 심화주차에는 백엔드끼리 협업하는 팀과제를 진행했다.

나만의 블로그 API 만들기라는 주제로 진행했는데, 입문주차와 숙련주차에서 진행한 개인 과제의 연장선이었다.
그런데 숙련주차에서 Lv1, Lv2의 과제 중 Lv2의 과제가 선택이었던 터라 팀원별 기능 구현 경험이 제각각이었다.

팀장으로서 어떻게 담당을 나눠야 할까 고민이 정말 많았다.
각각의 실력차도 고려해야 하고, 과제 제출일까지 완성할 수 있을 지도 고려해야 했다.
그러나 팀장이라고 혼자 결정하는 것은 있을 수 없기에, 팀원들과 회의를 통해 결정하기로 했다.

팀원들에게는 솔직하게 과제 제출 기한까지 완성을 할 수 있는지도 중요하다고 내 의견을 전달하고, 각자가 해보고 싶은 기능이 있는지 의견을 먼저 수집했다.
그 결과 각자가 지난주 과제에서 해보지 않은 기능 중에서 본인이 해보고 싶은 기능을 담당하기로 했다.

다행히 팀원들끼리의 대화가 활발해서 모르는 부분에 대한 질문이나, 진행사항 공유 등 정보 교환이 적극적으로 이루어졌다.
또, 우리팀은 매일 13시, 20시에 정기적으로 회의를 통해 코드 확인, 테스트 등을 진행했는데 덕분에 자주 리팩토링을 거치며 코드를 보다 더 깔끔하게 효율적으로 짜려고 노력했다.

그리고 나는 내 담당 기능(Spring security를 적용한 회원가입, 로그인)을 빠르게 구현하고, 팀원들의 코드를 보며 과제 요구사항에 알맞게 구현이 되었는지를 확인하며 코드를 조금씩 고쳐나갔는데, 그 과정에서 내가 생각했던 코드와 어떻게 다른지를 비교해보며 보며 많이 배울 수 있었다.

이제 6주차에는 본격적으로 프론트엔드 분들과 협업을 진행하게 되는데, 잘 할 수 있을지 걱정 또 걱정뿐이다..
이번 협업에서는 소통 제일!을 모토로 주특기 입문-심화주차에 걸쳐 배운 것들을 복습하고 앞으로 있을 클론 코딩, 실전 프로젝트에 대비해 기초를 탄탄히 해 나가고 싶다.

피할 수 없으면 즐기자!



CORS

프론트엔드와 협업을 진행할 때 CORS가 중요하다고 하는데, CORS가 도대체 뭔지 정리해보려고 한다.

CORS란 무엇인가?

CORS(Cross-Origin Resource Sharing)는 출처가 다른 자원들을 공유한다는 뜻으로, 한 출처에 있는 자원에서 다른 출처에 있는 자원에 접근하도록 하는 개념이다.
직역하면 교차되는 출처 자원들의 공유로, 다른 출처에 있는 자원을 요청한다고 하면, 이를 교차 출처 요청이라고 부른다.

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다.
- mdn

다른 출처 요청의 위험성

<img>, <script>, <frame>, <video>, <audio> 등이 웹에 등장하면서, 페이지 로딩 이후에 브라우저에서 이러한 하위 자원들을 가져올 수 있게 되었습니다. 그러므로 동일 출처, 다른 출처 모두 호출이 가능하게 되었다.

CORS 정책이 없고 모든 다른 출처 요청이 가능한 브라우저를 생각해보자.

홈페이지를 서핑하고 있는데, <script>가 심어진 evil.com 페이지를 열었다고 생각해보자.
굉장히 유용한 정보를 담고 있는 사이트이지만, 페이지를 열면서 <script>가 실행되어 은행에 Delete /account를 요청하도록 되어 있다. AJAX 호출로 은행 API를 호출하여 나의 은행 계좌를 삭제해버리는 사고가 발생한다.
따라서, 다른 출처의 접근을 막기 위해서 동일 출처 정책이 등장하게 되었다.

동일 출처 정책(Same-Origin policy)

동일 출처 정책(Same-origin policy)은 다른 출처로부터 조회된 자원들의 읽기 접근을 막아 다른 출처 공격을 예방한다.
그러나, 다른 출처에서 얻은 이미지를 담는 <img>, 외부 주소를 담는 <link> 같은 여러 태그들을 허용한다. 동일 출처 정책의 정확한 구현 명세는 없지만 최신의 브라우저들은 일정 규칙을 따르고 있다.(RFC6454)

동일 출처 정책은 다른 출처 자원을 가져오는 것을 굉장히 제한적으로 허용한다.
따라서 다른 출처 리소스에 접근성을 높이기 위해서 CORS가 등장했다.

CORS 접근제어에 사용되는 시나리오(3가지)

1️⃣단순 요청(Simple Request)

Preflight 요청 없이 바로 요청을 보내며, 아래와 같은 조건을 만족해야한다.

  • 메서드 : GET, POST, HEAD
  • Content-Type은 아래 셋 중 하나여야 한다.
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • 헤더 : Accept, Accept-Language, Content-Language, Content-Type 만 허용 한다.

2️⃣사전 요청(Preflight Request)

사전 요청은 OPTIONS 메서드를 통해 다른 도메인 리소스에 요청이 가능한지 확인하는 작업이다.
요청이 가능한 것을 확인하면 실제 요청을 보내게 된다.

Preflight Request

  • origin : 어디서 요청을 했는지 서버에 알려주는 주소
  • access-control-request-method : 실제 요청이 보낼 HTTP 메서드
  • access-control-request-headers : 실제 요청에 포함된 header

Preflight Response

  • access-control-allow-origin : 서버가 허용하는 출처
  • access-control-allow-methods : 서버가 허용하는 HTTP 메서드 리스트
  • access-control-allow-headers : 서버가 허용하는 header 리스트
  • access-control-max-age : 프리 플라이트 요청의 응답을 캐시에 저장하는 시간

Preflight Response의 응답 코드는 200대여야하고 Body는 비어있는 것이 좋다.

3️⃣신용 요청(Credentialed Request)

인증 관련 헤더를 포함할 때 사용하는 요청으로, 쿠키, 인증 헤더, TLS 클라이언트 인증서 등의 신용정보와 함께 요청한다.
기본적으로, CORS 정책은 다른 출처 요청에 인증정보 포함을 허용하지 않지만, 요청에 인증을 포함하는 플래그가 있거나 access-control-allow-credentials를 true로 설정 한다면 요청할 수 있다.

  • 클라이언트
    쿠키 또는 JWT 토큰을 담아 보낼 경우 credentials : include 를 포함하여 보낸다.

  • 서버
    Access-Control-Allow-Credentials : true 로 설정해야 클라이언트의 인증 포함 요청에 허용이 가능하다.

CORS 해결방법

프론트 프록시 서버 설정

프론트 서버에서 백엔드로 요청을 보낼 때, 대상의 URL을 변경한다.

직접 헤더 설정

직접 헤더에 설정을 추가한다.

백엔드 설정(Spring Boot)

config 클래스를 만들고 CORS의 출처 및 설정 관리를 할 수 있다.

Spring Boot에서 CORS 설정하기

Spring Security를 적용하는 경우엔 SecurityConfig 클래스에서 같이 설정해줄 수 있다.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
	http.csrf().disable();
    
    http.authorizeRequests()
        .antMatchers("/api/auth/**").permitAll()
        .anyRequest().authenticated();
        
    // 이 설정을 해주지 않으면 밑의 corsConfigurationSource가 적용되지 않는다.
    http.cors()
    	.and()
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .apply(new JwtConfig(jwtUtil, om));
    
    return http.build();
}


@Bean
public CorsConfigurationSource corsConfigurationSource(){
    CorsConfiguration config = new CorsConfiguration();
    
    config.addAllowedOrigin("http://localhost:3000");
    
    config.addExposedHeader(JwtUtil.AUTHORIZATION_HEADER);
    
    config.addAllowedMethod("*");
    
    config.addAllowedHeader("*");
    
    config.setAllowCredentials(true);

    config.validateAllowCredentials();

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", config);
    
    return source;
}


📚참고
CORS란 무엇인가?
CORS란

profile
느리더라도 꾸준하게

0개의 댓글