https://nodejs.org/ko/docs/guides/anatomy-of-an-http-transaction/
https://ko.javascript.info/fetch-crossorigin
const http = require('http'); //http 모듈 사용
const server = http.createServer((req, res) => {
//여기서 작업이 진행된다.
})
서버로 오는 HTTP 요청마다 createServer에 전달된 함수가 호출됨
HTTP 요청이 서버에 오면 node가 트랜잭션을 다루려고 req, res 객체
를 전달하며 요청 핸들러 함수를 호출한다. 요청을 실제로 처리하기 위해서는 사용하고자 하는 포트번호
를 listen()
에 전달하면 된다.
//1. 프리플라이트
//if(메소드가 options){ cors 설정을 돌려줘야한다.}
//2. 찐요청
//if(*메소드가 POST고 url이 /upper면) {대문자로 응답}
// else if (메소드가 post고 url이 lower면){ 소문자로 응답}
// eles {에러로 처리. bad request}
if (request.method === 'OPTIONS') {
response.writeHead(200, defaultCorsHeader);
}
일반적인 HTTP 메서드/동사 : GET, POST, PUT, DELETE, OPTIONS
전체 URL에서 서버, 프로토콜, 포트를 제외한 것. 세번째 슬래시 이후의 나머지 전부를 말한다.
여기서는 /upper, /lower
헤더는 소문자로만 표현된다.
요청을 보낼때 Origin이라는 헤더를 요청에 추가한다.
서버는 요청 헤더에 있는 Origin을 검사하고 요청을 받아들이기로 동의한 상태라면 특별한 헤더 Access-Control-Allow-Origin를 응답에 추가한다.
이 헤더에는 허가된 Origin에 대한 정보나 *이 명시된다.
POST/PUT 요청을 받을 때 중요한 요소이다.
핸들러에 전달된 req 객체는 ReadableStream 인터페이스를 구현하고 있다.
이 스트림의 'data', 'end'이벤트에 이벤트 리스너를 등록해서 데이터를 받는다.
'data'이벤트에서 발생시킨 청크는 Buffer이다. 이 청크가 문자열 데이터라면 배열에 수집한 다음 'end' 이벤트에서 이어 붙인 다음 문자열로 만들면 된다.
let body = [];
request.on('data', (chunk) => { //'data'이벤트에서 발생시킨 청크
body.push(chunk); //배열에 수집
}).on('end', () => { //'end'이벤트에서
body = Buffer.concat(body).toString(); //이어붙인 후 문자열로 만든다.
// 여기서 `body`에 전체 요청 바디가 문자열로 담겨있습니다.
});
오류가 발생하면 스트림에서 'error' 이벤트가 발생하면서 오류를 전달한다.
단순히 오류를 로깅만 하더라도 요청 스트림에 'error'리스너를 추가해야한다!
request.on('error', (err) => {
console.error(err.stack);
});
따로 설정하지 않으면 항상 200이다.
statusCode 프로퍼티를 설정한다.
응답헤더를 설정할때 대소문자는 중요하지 않다. 헤더를 여러 번 설정한다면 마지막에 설정한 값을 보낸다.
response.setHeader('Content-Type', 'application/json');
response.setHeader('X-Powered-By', 'bacon');
명시적인 헤더 데이터 전송 : writeHead()
response.writeHead(200, { 'Content-Type': 'application/json', 'X-Powered-By': 'bacon' });
응답 바디는 일반적인 스트림 메서드를 용해서 작성한다.
end함수에 스트림에 보낼 데이터의 마지막 비트를 선택적으로 전달할 수 있다.
response.write('<html>');
response.write('<body>');
response.write('<h1>Hello, World!</h1>');
response.write('</body>');
response.write('</html>');
response.end();
//위와 같은 예제
response.end('<html><body><h1>Hello, World!</h1></body></html>');
객체를 JSON 형식의 문자열로 반환한다.
replacer: 함수 또는 배열. 필터링하는 듯 하다.
space: 가독성을 위한 공백을 넣어준다.
const o = { name: 'Lee', gender: 'male', age: 20 };
const strPrettyObject = JSON.stringify(o, null, 2);
console.log(typeof strPrettyObject, strPrettyObject);
/*
string {
"name": "Lee", //"name" 앞에 두칸 떨어져있음
"gender": "male",
"age": 20
}
*/
const server = http.createServer((request, response) => {
if (request.method === 'OPTIONS') { //프리플라이트
response.writeHead(200, defaultCorsHeader);
}
//찐요청
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
if (request.method === 'POST' && request.url === '/upper') {
body = Buffer.concat(body).toString().toUpperCase();
response.end(JSON.stringify(body));
} else if (request.method === 'POST' && request.url === '/lower') {
body = Buffer.concat(body).toString().toLowerCase();
response.end(JSON.stringify(body));
} else {
response.statusCode = 404;
response.end();
};
});
);
response.writeHead(200, defaultCorsHeader);
});
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,
};
if문을 밖으로 빼고 그 안에서 data를 다뤄보려고 했으나 pending이 되고 데이터가 전달되지 않았다.
반대로 하니까 되는 요상한ㅋㅋㅋㅋ 뭔가 달라서 안됐겠거니 하고 바꿨다. 나중에 페어분과 이야기하면서 return을 해주지 않았기 때문이 아닐까 하는 의문을 갖게 되었다. 나중에 해봐야지.
그런데 노드 홈페이지서 트랜잭션 해부를 정독해보니까 지금 한 방법이 더 맞는 것 같긴 하다. 일단 반대로 하려면 if문 내에 let body부터 반복을 계속 해서 넣어줘야함;
데이터를 한번 받아서 그걸로 분기에 따라 가공을 해 주는 것이 훨 효율이 좋을 것 같다.
처음에 프리플라이트부터 cors 에러가 나서 한참 고생을 했다; 리퀘메서드가 options일때 와일드카드를 담은 'Access-Control-Allow-Origin': '*' 를 전달했었는데, 아마도 이것만 보내서 그랬던 건 아닐까?? 안되길래 프리플라이트 블록 내에서 end()를 해줬었는데, 프리플라이트는 해결이 되는데 찐요청에서 에러가 났다. 프리플라이트를 통과하면 찐요청으로 가야하는데 끝내버린 것 같음...
-> 1요청 1응답이어야함! 나는 1요청 2응답을 해서 오류가 났던 것.
여튼 그래서 나중에 defaultCorsHeader가 있길래 그걸 활용했다.
그리고 res.end를 보낼 때 JSON.stringify()를 해주지 않았었다.
이 모든 걸 해결하니까 작동이 잘 됨!! 긴가민가해서 다른사람들 글에서 도움을 좀 받을까 했는데, 다들 express만 써서 설명이 잘 안되어있다...ㅠㅠ 흑흑 나도 낼은 이해를 더 잘 할 수 있겠지
JSON 데이터를 가진 문자열을 객체로 변환한다.
암호화된 연결을 수신하기 위해 서버를 시작하는 데 사용되는 tls 모듈 내 Socket 내장 API
이벤트를 객체에 바인딩함
모든 헤더가 전송되었다는 신호를 서버에 보내는 데 사용되는 http모듈 내의 클래스 serverResponse 내장 API
Access-Control-Request-Headers를 포함하는 preflight request의 응답
에 사용되는 헤더로, 실제 요청때 사용할 수 있는 HTTP 헤더의 목록을 나열합니다.
요청에 Access-Control-Request-Headers 헤더가 포함되어 있을 경우, 이 헤더를 포함하여야 합니다.
CORS 허용 목록에 있는 요청 헤더
https://docs.w3cub.com/http/headers/access-control-allow-headers
- Accept,
- Accept-Language,
- Content-Language,
- Content-Type.
- 사용자 정의 헤더
Access-Control-Allow-Headers: X-Custom-Header- 다중 헤더 지원도 가능하다.
실무 관점에서 두 요청의 차이는 안전한 요청은 Origin 헤더와 함께 바로 요청이 전송되는 반면 안전하지 않은 요청은 브라우저에서 본 요청이 이뤄지기 전에 preflight 요청이라 불리는 사전 요청을 보내 퍼미션 여부를 물어본다는 점입니다.
안전한 요청은 다음과 같은 절차를 따릅니다.
→ 오리진 정보가 담긴 Origin 헤더와 함께 브라우저가 요청을 보냅니다.
← 자격 증명이 없는 요청의 경우(기본), 서버는 아래와 같은 응답을 보냅니다.
Origin 값과 동일하거나 *인 Access-Control-Allow-Origin
← 자격 증명이 있는 요청의 경우 서버는 아래와 같은 응답을 보냅니다.
Origin 값과 동일한 Access-Control-Allow-Origin
값이 true인 Access-Control-Allow-Credentials
자바스크립트를 사용해 Cache-Control이나 Content-Language, Content-Type, Expires, Last-Modified, Pragma를 제외한 응답 헤더에 접근하려면 응답 헤더의 Access-Control-Expose-Headers에 접근을 허용하는 헤더가 명시돼 있어야 합니다.