[S4]Chapter12. Coz’ Mini Hackathon

박현석·2022년 12월 17일
1

코드스테이츠

목록 보기
40/40
post-thumbnail

웹 개발을 위한 서버 기초

서버와 클라이언트의 개념

  • 서버는 ‘제공하는 주체’라고 이해할 수 있다.
  • 서버란 네트워크에서 다른 컴퓨터나 소프트웨어와 가틍ㄴ 클라이언트에게 서비스를 제공하는 컴퓨터라고 할 수 있다.
  • 클라이언트는 서버의 반대 개념으로 이해하면 좋다.
  • 서버가 제공자의 관점이라면 클라이언트는 제공 받는 대상이다.
  • 수강생에게 교육 서비스를 제공하는 관점에서 코드스테이츠는 서버가 될 수 있다.
  • 수강생은 교육 서비스를 제공 받을 수 있으므로 클라이언트가 될 수 있다.

서버의 종류

  • 서버의 종류가 구분될 수 있는 이유는 서버마다 역할이 다르기 때문이다.
  • 웹 서버
    - 웹 서비스를 제공하기 위한 서버 컴퓨터 입니다.
    - Apache, IIS, NginX와 같은 웹 서버 소프트웨어를 사용하여 웹 서비스 할 수 있습니다.
  • 웹 애플리케이션 서버
    - 웹 애플리케이션을 제공하기 위한 서버 컴퓨터 입니다.
    - Tomcat, WebLogic, WebSphere와 같은 웹 애플리케이션 서버 소프트웨어를 사용하여 웹 애플리케이션 서비스를 제공할 수 있습니다.
  • 데이터베이스 서버
    - 데이터베이스를 제공하기 위한 서버 컴퓨터 입니다.
    - Oracle, MS-SQL, MySQL과 같은 데이터베이스 소프트웨어를 사용하여 서비스 할 수 있습니다.
  • 파일 전송 서버
    - 대용량의 파일을 빠르게 주고 받기 위한 서버 컴퓨터 입니다.
    - VS-FTPD, IIS와 같은 소프트웨어를 사용하여 서비스 할 수 있습니다.
  • 메일 서버
    - 메일 서비스를 위한 서버 컴퓨터 입니다.
    - Send-mail, Microsoft Exchange Server와 같은 소프트웨어를 사용하여 서비스 할 수 있습니다.
  • 인쇄 서버
    - 공간의 제약을 극복하고 인쇄를 할 수 있도록 하는 서버 컴퓨터 입니다.
    - 인쇄기 제품과 구성에 따라 해당 서비스를 지원하는 다양한 소프트웨어를 사용하여 서비스 할 수 있습니다.
  • 실제 개발을 하면 서버의 사용 목적에 따라 운영 서버와 개발 서버로 분리하여 사용하는 것을 쉽게 볼 수 있다.
  • 개발자는 특정 개발 단계가 완료되면 개발 내용을 서비스의 종류에 맞는 ‘개발 서버’에 적용
  • 프론트 개발자는 개발 웹 서버에 개발 내용을 적용하고 백엔드 개발자는 개발 웹 애플리케이션 서버에 개발 내용을 적용
  • 데이터베이스와 관련한 내용은 개발 데이터베이스 서버에 반영
  • 하나의 제품에 여러 개발 서버를 나누는 이유는 프론트 개발자와 백엔드 개발자의 포지션을 구분하는 것 처럼 개발 영역을 명확히 하여 관리와 개발을 쉽게 하기 위함
  • 서버가 감당해야 할 트래픽의 분산을 위함이고 서버의 장애에 대응하기 위함

서버와 클라이언트의 통신

서버클라이언트
서비스제공 한다제공 받는다
통신(행위) 요청요청 받는다요청 한다
통신(행위) 응답응답 한다응답 받는다
![](https://user-images.githubusercontent.com/58800295/195823583-0bd009c1-8874-4807-9cf5-f6433272a9b1.png) ### 서버 구성과 서버 환경 설정 - 서버 구성 - 서버 컴퓨터를 구성하는 관점 - 서버 컴퓨터의 종류, 서버 컴퓨터의 대수, 네트워크 정책, 서버 수용 인원 예측 등을 구성 - 서버 환경 설정 - 구성된 특정 컴퓨터 서버가 서비스를 제공할 수 있도록 환경을 구성하는 과정 - 웹 서버는 Apache, IIS, NginX 와 같은 웹 서버 소프트웨어를 이용하여 웹 서비스를 가능케 하는 서버 환경 설정을 의미 - 웹 애플리케이션 서버는 Tomcat, WebLogic 과 같은 웹 애플리케이션 서버 소프트웨어를 이용하여 웹 애플리케이션을 구동하여 서비스 가능케 하는 서버 환경 설정을 의미 - 데이터베이스 서버는 Oracle, MS-SQL 과 같은 데이터베이스 솔루션 소프트웨어를 이용하여 데이터베이스를 관리할 수 있는 서비스를 제공할 수 있는 서버 환경 설정을 의미 - 따라서 서버 구성 작업을 진행 한 후 서버가 구성되면 각 컴퓨터 서버(물리 서버)에 서버가 서비스할 목적에 맞는 서버 환경 설정을 진행해야 합니다. 서버 구성 + 서버 환경 설정 작업에 해당하는 서버 셋팅 작업이 완료되면 개발자는 개발한 제품을 연관된 개발, 운영 서버에 ‘배포’하여 웹 서비스 준비를 할 수 있습니다. 이후 클라이언트(고객)는 우리 제품(제공 관점에서 서버)을 이용(요청과 응답)할 수 있습니다.

프론트엔드와 백엔드 간의 통신

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 형식이고, 이메일이라고 부가 설명
profile
선한 영향력을 주는 사람

0개의 댓글