일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 자바스크립트
- 큐
- useState
- mysemester
- 회고
- 30daysdowoonchallenge
- 생활코딩
- 코드스테이츠
- 백준
- 프로토타입
- 운영체제
- UI
- vercel
- UX
- 해시테이블
- 카카오
- Til
- redux
- level1
- 프로그래머스
- 스택
- CSS
- 자료구조
- superstarjypnation
- Next.js
- html
- React
- REST_API
- javascript
- web
- Today
- Total
데굴데굴
TIL: 2022-10-13 본문
⚙️ 오늘 배운 주제
Node.js로 서버 만들기 (+CORS 에러 다루기)
🐹 오늘의 기분
오늘 학습도 만만치 않았다. SOP과 CORS 개념 볼 때까지만 해도 음~ 그렇군~ 이 상태였는데 실습 넘어오면서부터 머리에 물음표가 가득차기 시작했다. 당최 무슨 소린지 모르겠어서 같은 영상을 한 다섯 번은 본 것 같다. node.js의 http 모듈 관련 공식 문서를 참고해가며 하는 실습이었는데 거기도 한 번에 이해되지는 않아서 몇 번이고 다시 읽었다. 일단 예제에 있는대로 써보면서 이해해보려 했으나... 쉽지 않았다. CORS 에러를 처리하는 코드를 아직 이해 못했다. 분명 잘 분기해줬다고 생각했는데 자꾸 오류가 났음. 이 부분은 더 깊게 공부를 해보고 글에 추가해봐야겠다.
🗝 키워드
http 모듈, SOP, CORS, 프리플라이트 요청, 단순 요청, 인증 요청
🗣 스스로에게 설명
SOP
Same-Origin Policy
같은 출처(origin)의 리소스만 공유가 가능하다는 원칙이다.
origin은 fetch가 시작하는 위치로, 경로는 제외하고 서버 이름만 포함한다.
Origin: <scheme> "://" <hostname> [ ":" <port> ]
아래 이미지를 보면 <scheme>과 <hostname>, <port>가 어떤 부분인지 쉽게 알 수 있다.
path 직전까지를 origin으로 보면 된다. (port는 생략 가능)
origin은 개발자 도구에서 확인할 수 있다. (location 객체의 origin을 출력)
내 블로그에서는 이렇게 나온다.
SOP 정책에 따라 요청을 보내는 곳의 origin과 요청을 받는 곳의 origin이 같아야 리소스를 공유할 수 있다.
origin 같은 것, 다른 것 예시 (포트번호까지)
origin의 일치 여부는 브라우저가 판단한다고 한다.
SOP는 왜 필요한가?
만약 다른 리소스에서 정보를 자유롭게 접근할 수 있다면 정보가 유출될 위험이 높아진다.
특히 웹은 개발자 도구를 통해 어떤 작업이 이루어지고 있는지를 대부분 열람할 수 있기에 타 리소스의 접근 차단이 더욱 요구된다.
SOP로 다른 리소스와의 정보 공유를 제한함으로써 보안을 높일 수가 있다.
이 원칙이 있기에 우리는 안전한 브라우징을 할 수 있다.
SOP는 다른 리소스와의 정보 공유를 제한한다고 했는데, 그럼 다른 리소스가 필요한 경우에는 어떻게 해야 할까?
CORS
Cross-Origin Resource Sharing
이 때 필요한 것이 CORS이다.
CORS는 말 그대로 origin이 다른 리소스를 공유하는 것이다.
mdn에서는 아래와 같이 설명하고 있다.
추가 HTTP 헤더를 사용하여 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한 부여
여기서 추가 HTTP 헤더는 Access-Control-Allow-Origin
을 말한다.
CORS 동작 방식
1. 프리플라이트 요청 (Preflight request)
Preflight 요청이란 서버에 영향을 미칠 수 있는 메소드(POST, PUT, DELETE 등)를 쓰기 전에 해당 서버가 이 요청의 접근 권한을 허용하고 있는지 OPTIONS 메소드를 이용한 사전 요청으로 미리 확인해보는 과정이다.
A preflight request is automatically issued by a browser
mdn을 보면 'Preflight 요청은 브라우저가 자동으로 보낸다'고 나와있다. ( 🔗 )
따라서 우리는 응답 메시지를 다뤄주면 된다.
브라우저의 네트워크 탭을 보면 요청들을 볼 수 있다.
POST /lower을 보냈을 때의 결과다.
POST 요청만 보냈음에도 불구하고 브라우저가 보낸 preflight 요청이 하나 더 생겨있다.
Preflight 옆의 동그란 화살표를 누르면 어떤 요청에 대한 preflight 요청인지 노란색 하이라이트로 표시해준다.
Preflight 요청을 클릭하여 Request 메시지와 Response 메시지가 어떻게 오고 갔는지 확인할 수 있다.
Request Message
OPTIONS /lower HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: POST
Connection: keep-alive
Host: localhost:4999
Origin: null
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Access-Control-Request-Method
: 접근 허가를 얻을 메소드
Response Message
HTTP/1.1 200 OK
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
Date: Thu, 13 Oct 2022 07:44:53 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
Access-Control-Allow-Origin
: 공유를 허용할 리소스 origin
-> 위 메시지에서는 '*'로 입력되어있는데 이는 모든 리소스를 허용한다는 뜻이다.
실습이라 이렇게 썼지만, 실제로는 저렇게 쓰면 안 되고 꼭 공유를 허용할 리소스의 origin만 적어주어야 한다!
Access-Control-Allow-Methods
: 접근을 허용할 메소드들
위에서는 GET, POST, PUT, DELETE, OPTIONS를 허용하고 있다.
Access-Control-Allow-Header
: 접근을 허용할 헤더
Access-Control-Max-Age
: 요청을 캐시해둘 시간(초)
왜 프리플라이트 요청을 보내야 하는가?
- 미리 권한 확인 -> 처음부터 모든 요청을 보내는 것보다 리소스 절약 가능
- CORS에 대비되어 있지 않은 서버 보호 가능
2. 단순 요청 (Simple Request)
특정 조건이 만족되면 Preflight 요청을 생략하고 바로 실제 요청을 보낸다.
하지만 이 조건이 매우 복잡해서 거의 모든 요청이 Preflight 요청을 보낸다고 봐도 된다고 한다.
조건의 자세한 내용은 mdn 문서에 잘 나와있다. (접근 제어 시나리오 부분)
교차 출처 리소스 공유 (CORS) - HTTP | MDN
교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라
developer.mozilla.org
3. 인증정보를 포함한 요청 (Credentialed Request)
요청 헤더에 인증 정보를 담아 보내는 요청이다.
요청을 보낼 때 별도의 설정을 하지 않으면 쿠키를 같이 보낼 수 없다.
인증 정보 요청을 다룰 때에는 프론트와 백 전부 CORS 설정을 해야 한다.
프론트에서는 fetch 사용 시 withCredentials : true
를 설정하고,
서버에서는 Access-Control-Allow-Credentials
를 true로 설정하면 인증 정보를 포함한 요청도 받을 수 있다.
(이 부분은 직접 써보지 않아서 잘 모르겠다... )
CORS에 대해 설명이 굉장히 잘 된 글이 있어서 올려둔다.
CORS는 왜 이렇게 우리를 힘들게 하는걸까?
이번 포스팅에서는 웹 개발자라면 한번쯤은 얻어맞아 봤을 법한 정책에 대한 이야기를 해보려고 한다. 사실 웹 개발을 하다보면 CORS 정책 위반으로 인해 에러가 발생하는 상황은 굉장히 흔해서
evan-moon.github.io
Node.js http 모듈로 서버 만들기
Node.js 공식 문서의 내용을 참고하여 정리했다.
HTTP 트랜잭션 해부 | Node.js
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
nodejs.org
require로 http 모듈을 불러온다.
const http = require("http");
서버의 포트와 ip를 지정하고, createServer로 서버를 만들어 server 변수에 할당한다.
createServer로 서버를 만들면 request 객체와 response 객체가 생성된다.
const PORT = 4999;
const ip = "localhost";
const server = http.createServer((request, response) => {
// 여기에 서버 동작 내용을 구현함
}
서버가 생성되었으면 서버로 들어온 요청을 처리해야 한다.
request 객체에 method, url 프로퍼티가 내장되어 있어 이를 이용해 분기하면 된다.
GET /user 요청이 들어왔을 경우를 가정해봤다.
const server = http.createServer((request, response) => {
// 여기에 서버 동작 내용을 구현함
if (request.method === "GET" && request.url === "/user") {
// 동작 내용
}
}
이렇게 요청별로 하나하나 if/else문으로 구현해줘야 한다.
POST나 PUT 요청에 들어온 body를 가공하려면 조금 더 복잡해진다.
POST /user 요청이 들어온 경우로 가정해보겠다.
const server = http.createServer((request, response) => {
// 여기에 서버 동작 내용을 구현함
if (request.method === "GET" && request.url === "/user") {
// 동작 내용
} else if (request.method === "POST" && request.url === "/user") {
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// 변수 body에 request에 들어온 body의 내용이 문자열로 담겨 있다
});
}
}
Node.js로 CORS 에러 처리하기
브라우저가 preflight 요청을 보냈을 때 응답으로 보내줄 헤더를 작성한다.
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,
};
응답 객체의 writeHead 메소드로 상태 코드와 헤더를 넣어준다.
response.writeHead(200, defaultCorsHeader);
Preflight 요청은 options 메소드로 들어오므로 분기해준다.
const server = http.createServer((request, response) => {
// 여기에 서버 동작 내용을 구현함
if (request.method === "OPTIONS") {
response.writeHead(200, defaultCorsHeader);
response.end();
}
response.writeHead(200, defaultCorsHeader);
}
실습할 때 어째서인지 밑에 response.writeHead(200, defaultCorsHeader) 이 코드를 한 번 더 써줘야 cors 에러가 발생하지 않았다. 아직 이 부분은 설명을 듣지 못해서 이유는 잘 모르겠다... 애초에 내가 코드를 잘못 짰을 수도..?
이유를 알게 되면 추가해야겠다.
찾아보니 cors 모듈이 따로 있다고 한다.
그걸 이용하면 더 편할 듯하다.
[NODE] 📚 cors 모듈 - CORS 간편 설정하기
CORS 허용 설정 하는 방법 Node.js 서버 프로젝트에서 cors(cross origin resource sharing) 문제를 해결하는 방법은 크게 2가지가 있다. 하나는 직접 헤더를 명시해서 출처(origin)을 필터링하는 것이고, 다른..
inpa.tistory.com
❓ 막히는 or 막혔던 부분
CORS 에러 처리
🔍 공부가 더 필요한 부분
readableStream, writableStream
http 헤더 (content-type 등)
'Lesson > TIL' 카테고리의 다른 글
TIL: 2022-10-17 (0) | 2022.10.19 |
---|---|
TIL: 2022-10-14 (0) | 2022.10.14 |
TIL: 2022-10-12 (0) | 2022.10.12 |
TIL: 2022-10-11 (0) | 2022.10.11 |
TIL: 2022-10-07 (0) | 2022.10.07 |