1. CORS 복습
SOP(Same-Origine Policy)가 적용되기때문에 출처가 동일하지 않으면 에러가 발생한다.
출처가 동일하다는 것은 프로토콜+호스트+포트번호가 모두 같다는 뜻이다. 출처가 동일하면 뒤에 다른 패스가 붙어도 동일출처로 인식한다.
출처를 판단하는 주체는 브라우저이다.
CORS는 SOP로 인해 발생하는 에러들을 예외처리 하기 위해서 설정된다.
CORS 설정으로 인해 다른 출처의 리소스를 사용할 수 있게 된다.
1) CORS의 동작방식
- 프리플라이트 요청
실제 요청을 보내기 전에 OPTIONS 메서드로 출처가 같은지 확인하는 요청을 서버에 먼저 보낸다. 네트워크 자체에서 OPTIONS 메서드로 사전요청을 보내고, Origin 헤더가 자동으로 설정된다. 서버에서 출처를 정의한 헤더에 들어있는 링크(Access-Control-Allow-Origin)를 브라우저가 확인하고 출처가 동일하다면 실제 요청을 보내게 된다. 이후 서버가 실제 요청을 받아서 수행하고 응답을 클라이언트에게 전해준다. - 단순 요청
메서드 조건, 헤더에 대한 조건, 컨텐츠 타입에 대한 조건을 만족하면 프리플라이트 없이 요청과 응답을 바로 해준다. 대부분의 ajax 요청은 text/xml, application/json 타입인데, 단순요청에는 해당하지 않는다.
단, ajax 이외의 요청은 CORS가 일어나지 않기때문에 postman으로 api를 확인했을경우에는 CORS 에러가 발생하지 않는다. - 인증 정보를 포함한 요청
사용자 인증 정보를 담은 쿠키를 함께 요청하며 보내기때문에 클라이언트와 서버 모두 세팅이 필요하다.- 클라이언트 : fetch의 경우 {credentials: include} / axios.get(url, {withCredentials : true} 옵션을 설정해주어야 한다. 옵션을 설정해주면 요청 안에 쿠키가 담겨서 서버에게 가게 된다.
- 서버 : Access-Control-Allow-Credentials : true / Access-Control-Allow-Origin : '특정출처(ex : localhost:3000)'
=> 서로 쿠키를 주고받는 세팅을 해준다
2) CORS 설정방법
(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" });
});
2. node.js로 HTTP 서버 만들기 복습
1) 기본 서버 구조
const http = require("http");
const PORT = 4999;
const ip = "localhost";
// 서버객체.
// 이 안에서 모든 정보가 들어오기때문에 모든 로직이 여기에 담김
const server = http.createServer((request, response) => {
// 여기서 작업이 진행됨
});
// 서버 켜기. 포트번호, 콜백함수.
// 콜백함수 내에는 서버가 켜졌는지 아닌지 확인하는 용도.
server.listen(PORT, ip, () => {
console.log(`http server listen on ${ip}:${PORT}`);
});
// CORS를 위한 헤더 설정이 들어간 변수
const defaultCorsHeader = {
// 출처를 설정하는 헤더
"Access-Control-Allow-Origin": "*",
// 허용할 메서드를 설정하는 헤더
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
// 허용할 헤더를 설정하는 헤더
// content-type : 컨텐츠의 타입을 알려주는 용도
// accept : 명령하는 용도
"Access-Control-Allow-Headers": "Content-Type, Accept",
// 프리플라이트의 내용을 캐싱 설정 : 초단위 = 10초동안 프리플라이트가 안감
"Access-Control-Max-Age": 10,
};
2) 요청받은 데이터를 가공해 응답으로 보내기
const http = require("http");
const PORT = 4999;
const ip = "localhost";
// 서버객체.
// 이 안에서 모든 정보가 들어오기때문에 모든 로직이 여기에 담김
const server = http.createServer((request, response) => {
// 여기서 작업이 진행됨
console.log(
// 현재 요청받은 메서드와 url을 확인하는 용도
`http request method is ${request.method}, url is ${request.url}`
);
// 만약 메서드가 OPTIONS라면 : 200 상태코드와 CORS 헤더를 응답함
if (request.method === "OPTIONS") {
response.writeHead(200, defaultCorsHeader);
response.end(); // 요청을 종료 = 응답 종료
}
// 만약 메서드가 POST라면
if (request.method === "POST") {
let body = []; // 데이터를 담을 변수
request
.on("data", (chunk) => {
// 요청이 들어오면 웹브라우저가 데이터를 전송할때
// 데이터를 조각내어 chunk에 담아 callback 함수의 인자로 전달
body.push(chunk); // body 변수에 담아줌
})
.on("end", () => {
// 더이상 들어올 정보가 없으면 end 다음의 callback 함수를 실행
// 데이터의 조각인 chunk를 모아서 문자열로 변환한뒤 body에 담음
body = Buffer.concat(body).toString();
if (request.url === "/upper") {
response.writeHead(200, defaultCorsHeader);
response.end(body.toUpperCase()); // 응답
} else {
response.writeHead(200, defaultCorsHeader);
response.end(body.toLowerCase());
}
});
}
});
// 서버 켜기. 포트번호, 콜백함수.
// 콜백함수 내에는 서버가 켜졌는지 아닌지 확인하는 용도.
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",
// 허용할 헤더를 설정하는 헤더
// content-type : 컨텐츠의 타입을 알려주는 용도
// accept : 명령하는 용도
"Access-Control-Allow-Headers": "Content-Type, Accept",
// 프리플라이트의 내용을 캐싱 설정 : 초단위 = 10초동안 프리플라이트가 안감
"Access-Control-Max-Age": 10,
};
3. Express
Express : Node.js 환경에서 웹 서버 또는 API 서버를 제작하기 위해 사용되는 프레임워크.
Node.js HTTP 모듈로 작성한 서버와 다른점은 미들웨어 추가가 가능하고 라우터를 제공한다는 것이다.
1) 설치
npm install express
2) 기본 웹 서버 구조
const express = require("express"); // express 모듈 불러오기
const app = express(); // 서버 시작
const port = 4999; // 사용할 포트
// 루트 url(/) 또는 라우트 요청에 Hello World! 로 응답
// 기타 요청은 404로 응답
// express는 node 기반이기 때문에 node에서 제공하는 오브젝트들을 호출 가능하다
app.get("/", (req, res) => {
res.send("Hello World!");
});
// 서버 켜기
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
3) 라우팅
기본 구조
app.method(path, handler)
// 예시
app.get('/about', (req, res)=>{
res.send('got a get reques at /about')
})
라우트는 각 하나 이상의 핸들러 함수를 가질 수 있다.
node.js로 코드를 작성한 경우
const requestHandler = (req, res) => {
if(req.url === '/lower') {
if (req.method === 'GET') {
res.end(data)
} else if (req.method === 'POST') {
req.on('data', (req, res) => {
// 실행할 코드...
})
}
}
}
Express로 제공되는 라우터 기능을 사용할 경우
// 라우팅(Routing) : 메서드와 url로 분기점 만들기.
const router = express.Router()
router.get('/lower', (req, res) => {
res.send(data);
})
router.post('/lower', (req, res) => {
res.json(req.body.toLowerCase());
})
4) 미들웨어(Middleware)
미들웨어 함수는 요청(req)객체, 응답(res) 객체, 어플리케이션 요청-응답 사이클 도중 다음의 미들웨어 함수에 대한 엑세스 권한을 갖는 함수이다. 요약하자면 클라이언트에게 요청을 받고, 응답하려는 중간에서 목적에 맞게 처리를 하는 함수들이라고 말할 수 있다. 미들웨어를 이용하면 번거로울 수 있는 작업들을 보다 쉽게 적용할 수 있다.
📝 미들웨어 함수가 하는 일
- 어떤 코드든지 실행하기
- 요청과 응답 객체를 수정하기
- 요청-응답의 순환을 끝내기
- 다음 미들웨어를 호출하기
다음 미들웨어 함수에 대한 엑세스는 next() 함수를 이용해 다음 미들웨어로 현재 요청을 넘길 수 있다.
미들웨어는 순차적으로 처리되는 방식임으로 순서가 매우 중요하다.
(1) body-parser / express.json()
post 요청 등에 포함된 body(payload)를 구조화 할때 사용하는 미들웨어이다. 아래와 같은 기본 형태로 사용한다.
npm install body-parser
const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();
app.post('/users', jsonParser, function (req, res) {
// 실행할 코드
})
Express v4.16.0 부터 내장 미들웨어인 express.json()을 사용해 사용이 더 쉬워졌다.
// post 요청 등에 포함된 body를 구조화.
// strict 는 배열 및 개체만 수락할지의 여부를 활성/비활성화 한다.
// => false의 경우 json.parse가 허용하는 모든 항목들을 수용한다
const jsonParser = express.json({ strict: false });
app.post("/upper", (req, res) => {
res.status(201).json(req.body.toUpperCase());
});
(2) CORS
CORS 미들웨어를 사용해 CORS 처리를 더 쉽게 할 수 있다.
npm install cors
const cors = require('cors'); // 모듈 불러오기
// 모든 요청에 대해 허용하기
app.use(cors());
// 특정 요청에 대해서만 허용하기
app.get('/products/:id', cors(), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for a Single Route'})
})
(3) app.use()
app.use()는 모든 요청에 미들웨어를 실행하며, app.http메서드()는 해당 요청에서 미들웨어가 실행된다.
이렇게 직접 미들웨어를 만들수도 있다.
const myLogger = function (req, res, next) {
// 요청이 올때마다 해당 메서드와 url을 콘솔에 출력한다
console.log(`http request method is ${req.method}, url is ${req.url}`);
next(); // 여기서 요청-응답이 끝나지 않음으로 다음 미들웨어를 실행한다
};
app.use(myLogger);
***************************************
요약
- 미들웨어는 요청을 받고 응답을 해주는 그 사이에서 목적에 맞게 작동하는 함수들이다.
- 미들웨어는 모든 코드를 실행하고, 요청-응답의 개체를 변경하고, 요청-응답 주기를 종료하거나 스택의 다음 미들웨어를 호출할 수 있다. install을 통해서 미들웨어를 설치하여 사용할수도 있고 직접 미들웨어를 작성하여 사용할수도 있다.
***************************************
https://expressjs.com/ko/guide/using-middleware.html
https://blog.naver.com/PostView.nhn?blogId=dlaxodud2388&logNo=221708863522
'study > TIL' 카테고리의 다른 글
230209 - Express 서버 기본 제작, 클라이언트 - 서버 연결 (0) | 2023.02.09 |
---|---|
230208 - 서버 CRUD 실습과제 (0) | 2023.02.08 |
230206 - CORS (0) | 2023.02.06 |
230202 - React 데이터 흐름, Effect Hook (0) | 2023.02.02 |
230201 - REST API 복습, Postman 사용하기 (0) | 2023.02.01 |