Web Server 기초

2023. 4. 6. 21:36· 코드스테이츠44기 프론트엔드
목차
  1. CORS 
  2. Mini Node Server  의사코드 
  3. Bare minimum requirements
화,수 휴가를 제출하고 여행을 다녀왔더니 정말 많은 공부가 밀려있었다 ... 오늘 화, 수 안한만큼 해야지 !! 했는데 너무 피곤했는지 늦게 일어나서 9시 12분에 출석을 해버렸다 ... ㅜ 부랴부랴 웹 서버 기초에 대해 유어클래스를 공부해보았는데 아무래도 3일치를 하루만에 하려고 하니 뇌에 과부하가 온 거 같다. 오늘은 과제 제출도 하지 못해 반딧불반에 오게 되었고 당연한 결과인 만큼 열심히 나머지 공부를 해 볼것이당 ! ! 웹 서버에 대해서는 거의 노베이스라고 해도 될 정도로 무지해 공부하는데 어려움이 많았던 거같다. 
node.js 를 사용해본적이 없어 더 헤맸었던 거같다 .. 
오늘 포스트는 오늘 공부한 web server 기초와 mini node server 과제를 의사코드로 작성해 볼 것이다.  또한 node.js 의 HTTP 트랜잭션 해부 문서를 꼼꼼히 읽고 정리해볼것이다. 
  1. cors 
  2. mini node server 과제

 

CORS 

SOP란 ?
Same-Origin Policy  : 동일 출처 정책 
같은 출처의 리소스만 공유 가능함 
출처(Origin) : 프로토콜 , 호스트 , 포트의 조합 
출처 중 하나라도 다르면 동일한 출처로 보지 않음 

ex) https://www.codestates.com:443/course
https:// 프로토콜
www.codestates.com:443 호스트
:443 포트

SOP는 왜 생겨났을까 ? 
동일 출처 정책은 잠재적으로 해로울 수 있는 문서를 분리함으로써 공격받을 수 있는 경로를 줄여줌 
ex) 네이버에 로그인 후 로그아웃을 까먹음 ->브라우저에 로그인 정보가 남아있음 -> 로그인 정보를 노리는 코드가 있는 사이트에 방문 -> 해킹당함

이를 방지하기 위해 SOP는 애초에 다른 사이트와의 리소스 공유를 제한하기 때문에 로그인 정보가 타사이트 코드에 의해 새어나가는 것을 방지 할 수 있음

한마디로 SOP는 보안상의 이유로 생긴 정책임 
보안에서는 이점이 있지만 다른 출처의 리소스를 사용하게 될 일이 너무 많음 
-> 이러한 문제 상황에서 필요한 것이 CORS 임 


CORS 란?
Cross-Origin Resource Sharing : 교차 출처 리소스 공유 
추가 HTTP 헤더를 사용하여 , 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에게 알려주는 체제 

즉 브라우저는 SOP에 의해 기본적으로 다른 출처의 리소스 공유를 막지만 CORS 를 사용하면 접근 권한을 얻을 수 있게 됨. 

CORS 동작 방식 

3가지 방법이 존재 

1. 프리플라이트 요청 
실제 요청을 보내기 전 , OPTION 메서드로 사전 요청을 보내 해당 출처 리소스에 접근 권한이 있는지부터 확인하는 것 

접근 권한이 있을 때 
클라이언트    브라우저    서버
| 실제요청 --> | 프리플라이트 요청 -->
Origin: manchoon.com
|
|   | <-- 프리플라이트 응답 
Access-Control-Allow-Origin : manchoon.com
|
|   | 실제요청 --> |
| <-- 응답  | <-- 응답  |

브라우저는 서버에 실제 요청을 보내기 전 프리플라이트로 요청을 보내고 , 응답 헤더의
Access-Control-Allow-Origin 으로 요청을 보낸 출처가 돌아오면 실제 요청을 보냄 



접근 권한이 없을 때 
클라이언트   브라우저   서버
| 실제요청 --> | 프리플라이트 요청 -->
Origin: manchoon.com
|
| <-- CORS 에러  | <-- 프리플라이트 응답 
Access-Control-Allow-Origin : 응답 헤더 에 없음 
|

요청을 보낸 출처가 접근 건한이 없다면 브라우저에서 CORS 에러를 띄우게 되고, 실제 요청은 전달 되지 않음 

프리플라이트 요청의 필요성 
1. 실제 요청을 보내기 전 권한을 확인할 수 있어 실제 요청을 통째로 보내는 것 보다 리소스 측면에서 효율적
2. CORS에 대비가 되어있지 않은 서버를 보호할 수 있음 . 
ex) 프리플라이트 요청을 먼저 보내게 되면 , 프리플라이트 요청에서 CORS 에러를 띄우게 됨 


2. 단순 요청 ( Simple Request) 
특정 조건이 만족되면 프리플라이트 요청을 생략하고 요청을 보내는 것 

클라이언트   서버  
| 실제 요청 -->
Origin : manchoon.com
| 요청 수행 
| <-- 응답
Access-Control-Allow-Origin : * 
|
만족 조건 
1. GET, HEAD, POST 요청 중 하나여야 함
2. 자동으로 설정되는 헤더 외에, Accept, Accept-Language, Content-Language, Content-Type 헤더의 값만 수동으로 설정할 수 있음
3. Content-Type 헤더에는 application/x-www-form-urlencoded, multipart/form-data, text/plain 값만 허용됨

하지만 위의 조건들은 모두 만족시키기는 어려우므로 참고만 ! 

3. 인증정보를 포함한 요청 (Credentialed Request)
요청 헤더에 인증 정보를 담아 보내는 요청 
출처가 다를 경우 별도의 설정을 하지 않으면 쿠키를 보낼 수 없음 . 따라서 프론트,서버 양측 모두 CORS 설정이 필요함

프론트 측 :  요청 헤더에 withCredentials : true 를 넣어줘야 함
서버 측: 응답 헤더에 Access-Control-Allow-Credentials : true 를 넣어줘야 함
(서버 측에서 Access-Control-Allow-Origin 을 설정할 때, 모든 출처를 허용한다는 뜻의 와일드카드(*)로 설정하면 에러가 발생함 . 인증 정보를 다루는 만큼 출처를 정확하게 설정해주어야 함)


CORS 설정 방법

1. Node.js 서버 / 2. Express 서버 


// 1. Node.js 서버 

const http = require('http');

const server = http.createServer((request, response) => {
// 모든 도메인
  response.setHeader("Access-Control-Allow-Origin", "*");

// 특정 도메인
  response.setHeader("Access-Control-Allow-Origin", "https://codestates.com");

// 인증 정보를 포함한 요청을 받을 경우
  response.setHeader("Access-Control-Allow-Credentials", "true");
})
//2. Express 서버 

const cors = require("cors");
const app = express();

//모든 도메인
app.use(cors());

//특정 도메인
const options = {
  origin: "https://codestates.com", // 접근 권한을 부여하는 도메인
  credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가
  optionsSuccessStatus: 200, // 응답 상태 200으로 설정
};

app.use(cors(options));

//특정 요청
app.get("/example/:id", cors(), function (req, res, next) {
  res.json({ msg: "example" });
});

 

 


Mini Node Server  의사코드 

Bare minimum requirements

이번 과제에서 구현하는 웹 서버의 기능은 매우 단순합니다. 클라이언트의 액션(버튼 클릭)에 따라 각기 다른 HTTP 요청을 서버로 보내고, HTTP 요청에 담아 보낸 단어를 소문자 또는 대문자로 응답을 받아 화면에 보여 줍니다.

[그림] Mini Node Server 예시

Endpoint(URL)Method기능

/lower POST 문자열을 소문자로 만들어 응답해야 합니다
/upper POST 문자열을 대문자로 만들어 응답해야 합니다

[표] Endpoint에 따른 메서드와 기능 설명

 

  • POST에 문자열을 담아 요청을 보낼 때는 HTTP 메시지의 body(payload)를 이용합니다.
  • 서버는 요청에 따른 적절한 응답을 클라이언트로 보내야 합니다.
  • CORS 관련 헤더를 OPTIONS 응답에 적용해야 합니다.
    • 클라이언트의 preflight request에 대한 응답을 돌려줘야 합니다.
    • preflight request에 대한 응답 헤더는 이미 작성되어 있습니다.
const http = require('http');

const server = http.createServer((request, response) => {

response.writeHead(200, defaultCorsHeader);

const defaultCorsHeader = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Accept',
  'Access-Control-Max-Age': 10
};
})
[코드] preflight request에 대한 응답 헤더
 
const http = require('http');

const PORT = 4999;

const ip = 'localhost';

const server = http.createServer((request, response) => {
  console.log(
    `http request method is ${request.method}, url is ${request.url}`
  );
  response.writeHead(200, defaultCorsHeader);
  response.end('hello mini-server sprints');
});

server.listen(PORT, ip, () => {
  console.log(`http server listen on ${ip}:${PORT}`);
});

const defaultCorsHeader = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Accept',
  'Access-Control-Max-Age': 10
};

기본으로 제공되는 코드는 위와 같다 . 문제를 해결해보기 전 의사코드를 작성해 볼 것이다. 

const http = require('http'); //Node.js HTTP 모듈을 불러오기 위함 

const PORT = 4999; // 포트번호는 4999로 설정 

const ip = 'localhost'; // ip는 localhost 즉 내 컴퓨터를 의미 
//node 웹 서버 애플리케이션은 웹 서버 객체를 만들어야 함  이 때 createServer를 사용함 
//서버로 오는 HTTP 요청마다 createServer에 전달된 함수가 한 번씩 호출됨
//HTTP 요청이 서버에 오면 node가 트랜잭션을 다루려고 request와 response 객체를 전달하며 요청 핸들러 함수를 호출함. 
const server = http.createServer((request, response) => {
  console.log(
    `http request method is ${request.method}, url is ${request.url}`
  );
  //요청을 처리할 때, 우선은 메서드와 URL을 확인한 후 이와 관련된 적절한 작업을 실행하려고 함 . 적절한 작업은 request 프로퍼티를 이용 ! 
  const { headers, method, url } = request;


//   메소드와 url에 따라 다르게 실행되어야 함 -> if문 사용 
//   1.메소드가 POST이면서 대문자로 변경할 때 즉 url이  /upper 
//   2.메소드가 POST이면서 소문자로 변경할 때 즉 url이 /lower 

//   분기를 한 후 조건에 맞게 요청을 실행
//   POST 요청이므로 요청 body를 'data'와 'end' 이벤트로 받음 
//   각 'data' 이벤트에서 발생시킨 청크는 Buffer
//   이 청크가 문자열 데이터라는 것을 알고 있다면 이 데이터를 배열에 수집한
//   다음 'end' 이벤트에서 이어 붙인 다음 문자열로 만드는 것이 가장 좋음 
//   우리는 문자열 데이터로 받을 것이기 때문에 배열에 저장할 것 
//  그 후 저장한 배열 데이터를 조건에 맞게 toUpperCase() ,toLowerCase() 실행 
//  ex) body = body.toUpperCase(); 

//   let body = [];
//   request.on('data', (chunk) => {
//   body.push(chunk);
//   }).on('end', () => {
//   body = Buffer.concat(body).toString();
//   // 여기서 `body`에 전체 요청 바디가 문자열로 담겨있습니다.
//   });
     
// cors 헤더를 넣어서 cors 에러 해결 , defaultCorsHeader 에서는 *로 모든 오리진 허용 
// 따라서 문제 없이 요청이 가능함 

  response.writeHead(200, defaultCorsHeader);
  response.end('hello mini-server sprints');
});

//요청을 실제로 처리하려면 listen 메서드가 server 객체에서 호출되어야 함. 
//대부분은 서버가 사용하고자 하는 포트 번호를 listen에 전달하기만 하면 됨
//따라서 아래와 같이 작성 
server.listen(PORT, ip, () => {
  console.log(`http server listen on ${ip}:${PORT}`);
});

const defaultCorsHeader = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Accept',
  'Access-Control-Max-Age': 10
};

 

위의 의사코드를 바탕으로 코드를 작성해보겠다. 

const http = require('http'); //Node.js HTTP 모듈을 불러오기 위함 

const PORT = 4999; // 포트번호는 4999로 설정 

const ip = 'localhost'; // ip는 localhost 즉 내 컴퓨터를 의미 
//node 웹 서버 애플리케이션은 웹 서버 객체를 만들어야 함  이 때 createServer를 사용함 
//서버로 오는 HTTP 요청마다 createServer에 전달된 함수가 한 번씩 호출됨
//HTTP 요청이 서버에 오면 node가 트랜잭션을 다루려고 request와 response 객체를 전달하며 요청 핸들러 함수를 호출함. 
const server = http.createServer((request, response) => {
  console.log(
    `http request method is ${request.method}, url is ${request.url}`
  );
  //요청을 처리할 때, 우선은 메서드와 URL을 확인한 후 이와 관련된 적절한 작업을 실행하려고 함 . 적절한 작업은 request 프로퍼티를 이용 ! 
  const { headers, method, url } = request;


//   메소드와 url에 따라 다르게 실행되어야 함 -> if문 사용 
//   1.메소드가 POST이면서 대문자로 변경할 때 즉 url이  /upper 
//   2.메소드가 POST이면서 소문자로 변경할 때 즉 url이 /lower 

//   분기를 한 후 조건에 맞게 요청을 실행
//   POST 요청이므로 요청 body를 'data'와 'end' 이벤트로 받음 
//   각 'data' 이벤트에서 발생시킨 청크는 Buffer
//   이 청크가 문자열 데이터라는 것을 알고 있다면 이 데이터를 배열에 수집한
//   다음 'end' 이벤트에서 이어 붙인 다음 문자열로 만드는 것이 가장 좋음 
//   우리는 문자열 데이터로 받을 것이기 때문에 배열에 저장할 것 
//  그 후 저장한 배열 데이터를 조건에 맞게 toUpperCase() ,toLowerCase() 실행 
//  ex) body = body.toUpperCase(); 

//   let body = [];
//   request.on('data', (chunk) => {
//   body.push(chunk);
//   }).on('end', () => {
//   body = Buffer.concat(body).toString();
//   // 여기서 `body`에 전체 요청 바디가 문자열로 담겨있습니다.
//   });
     
let body = [];

  if (request.method === "POST") {
    request
      .on("error", (err) => {
        console.error(err);
      })
      .on("data", (chunk) => {
        // Buffer
        // body = chunk;
        body.push(chunk);
      })
      .on("end", () => {
        // options가 걸릴 때
        // concat []의 요소를 합쳐주었다.
        // from Buffer 그 자체 => toString으로 스트링으로 변환
        body = Buffer.concat(body).toString();

        response.on("error", (err) => {
          console.error(err);
        });

        response.statusCode = 200;
        response.setHeader("Content-Type", "application/json");

        if (url === "/upper") {
          body = body.toUpperCase();
        } else if (url === "/lower") {
          body = body.toLowerCase();
        }

        // cors 헤더를 넣어서 cors 에러 해결
        // 모든 오리진을 허용해줘서 문제없이 요청 가능
        response.writeHead(200, defaultCorsHeader);
        response.write(body);
        response.end();
       
      });
  }
  // OPTIONS 따로 처리해주면 좋다.
  // 따로 처리하지 않으면, body가 falsy인 경우 에러
  if (request.method === "OPTIONS") {
    response.writeHead(200, defaultCorsHeader);
    response.end();
  }
});

//요청을 실제로 처리하려면 listen 메서드가 server 객체에서 호출되어야 함. 
//대부분은 서버가 사용하고자 하는 포트 번호를 listen에 전달하기만 하면 됨
//따라서 아래와 같이 작성 
server.listen(PORT, ip, () => {
  console.log(`http server listen on ${ip}:${PORT}`);
});

const defaultCorsHeader = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Accept',
  'Access-Control-Max-Age': 10
};

'코드스테이츠44기 프론트엔드' 카테고리의 다른 글

섹션2를 마무리하며 KTP회고  (0) 2023.04.10
Section 2 기술면접 준비  (0) 2023.04.10
클라이언트 ajax 요청  (0) 2023.04.03
Postman 사용하기  (0) 2023.03.30
REST API 완벽 이해하기  (0) 2023.03.29
  1. CORS 
  2. Mini Node Server  의사코드 
  3. Bare minimum requirements
'코드스테이츠44기 프론트엔드' 카테고리의 다른 글
  • 섹션2를 마무리하며 KTP회고
  • Section 2 기술면접 준비
  • 클라이언트 ajax 요청
  • Postman 사용하기
배트리버
배트리버
🐾 사람 좋아, 개발 좋아 🐾 궁금한 건 끝까지 파고들고, 배운 건 즐겁게 나누는 개발자의 놀이터
배트리버
리트리버의 개발 놀이터
배트리버
전체
오늘
어제
  • 분류 전체보기
    • 네트워크
    • 기초 셋팅
    • 오늘의 일기
    • 리액트
    • 코테 준비
      • 프로그래머스
      • 백준
    • 코드스테이츠44기 프론트엔드
    • HTML-CSS-JavaScript
      • HTML
      • CSS
      • JavaScript
    • 자료구조&알고리즘
    • TypeScript
    • Git
    • Tip
    • 프로젝트
    • Next.js
    • 트러블슈팅

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • KPT 회고
  • 리액트쿼리
  • 네트워크
  • 코드스테이츠 프론트엔드
  • BFS
  • 티스토리챌린지
  • 오블완
  • 탄스택쿼리
  • 자바스크립트 비동기
  • 코드스테이츠 44기
  • 코드스테이츠
  • 자바스크립트
  • 리액트 상태관리
  • 리액트
  • 코드스테이츠 블로깅
  • 프로젝트 회고
  • 코드스테이츠 44기 프론트엔드
  • 코드스테이츠 회고록
  • 타입스크립트문법
  • 프로그래머스

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
배트리버
Web Server 기초
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.