🐕 미니노드서버 과제 레퍼런스의 수도코드 작성
// Node.js HTTP 모듈을 불러온다.
const http = require("http");
// 포트, ip 설정
const PORT = 4999;
const ip = "localhost";
// 여기 구현한 내용은 가장 원초적인 방식입니다.
// => 아무 라이브러리도 없는 경우에는 이렇게 할 수 있구나~ 정도 이해하면 OK
// 나중에는 백엔드 라이브러리/프레임워크를 적극 사용하기를 권장. 원리 이해를 위한 과제
const server = http.createServer((request, response) => {
console.log(
`http request method is ${request.method}, url is ${request.url}`
);
const { headers, method, url } = request;
// ! 1. method, url에 따라서 조건 분기를 먼저 진행해주세요.
// 지금은 if 문으로 하지만, 나중에는 좀 더 깔끔한 방식으로 처리 가능합니다.
// ! 2. 그 다음에, request, response 객체를 활용합니다.
// ---- request 객체는 incoming message, stream, eventEmitter를 상속받아서 아래 메서드가 사용 가능합니다.
// on 메서드 : 특정 이벤트 발생 시, 전달한 callback 함수를 실행합니다. 이벤트 발생 시점은 알 수 없음으로 비동기 호출
// "data" 이벤트: 매개변수 chuck에 Buffer가 전달됩니다. 이 Buffer는 메모리가 저장하기 쉬운 형태이기 때문에 String으로 변환해야 사용 가능
// "end" 이벤트: data를 온전히 다 전달받은 다음에, end 이벤트의 callback함수가 실행된다.
// 전달받는 body가 큰 경우, "data" 이벤트 callback 함수에서 데이터를 비동기로 처리를 다 못하고 response를 보내는 경우가 생깁니다. 이러면 에러 발생
// 그 외 이벤트는 공식문서를 참고하시면 되는데 - 너무 복잡하니 지금은 이정도로 원리 이해면 OK
let body = [];
if (request.method === "POST") {
request
.on("error", (err) => {
console.error(err);
})
.on("data", (chunk) => {
// Buffer
console.log(chunk);
// body = chunk;
body.push(chunk);
console.log(body);
})
.on("end", () => {
// options가 걸릴 때
console.log("end?");
// 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();
}
console.log(body);
// cors 헤더를 넣어서 cors 에러 해결
// 모든 오리진을 허용해줘서 문제없이 요청 가능
response.writeHead(200, defaultCorsHeader);
response.write(body);
response.end();
// 주의: 위 두 줄은 다음 한 줄로 대체할 수도 있습니다.
// response.end(JSON.stringify(responseBody))
// 새로운 부분이 끝났습니다.
});
}
// OPTIONS 따로 처리해주면 좋다.
// 따로 처리하지 않으면, body가 falsy인 경우 에러
if (request.method === "OPTIONS") {
response.writeHead(200, defaultCorsHeader);
response.end();
}
});
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,
};
다음 코드는 HTTP 모듈을 활용한 Node.js에서의 백엔드 구축 코드이다.
자바스크립트와 Node.js 환경을 활용하여 백단을 구성하고자 하는 경우에 고려해야할 점들은 다음과 같다.
0. 어느 Port를 사용할것인가? / 리소스의 저장은 어디에 할 것인가?
1. 어떻게 클라이언트 측의 요청을 받고 응답할 것인가?
1.1 CORS 설정을 갖춘 서버를 구축할 것인가 SOP만 허용하는 서버를 구축할 것인가
만약 CORS 설정을 갖추고자한다면 응답의 헤더단에서 CORS와 관련된 처리들을 해주어야할 것이다.
1.2 클라이언트측에서 보낸 요청은 인코딩된 형태로 서버에 전달된다.
요청의 바디에 담긴 데이터를 가공하여 특정 행동을 해야하는 경우를 고려한다면
디코딩 방법 또한 고려해야할 것이다.
1.3 RestFul 하게 메서드와 url parameter들을 구성해야한다.
다행히도 자바스크립트 생태계에는 http 모듈 뿐만 아니라 수많은 프레임워크들을 제공되고있다.
따라서 위의 요구사항들을 대부분 만족하는 서버를 만드는 것 또한 가능하다.
express , koa , next.js 등 조금 더 발전된 형태의 프레임워크를 사용하면
더욱 더 쉽게 처리를 할 수 있지만 http 모듈을 통해서 작성한다는 가정을 하고 수도코드를 작성한다.
http 모듈을 나의 코드에서 사용하기 위해 불러온다.
포트번호 및 IP를 변수화하여 관리한다. 우선 4999번 포트를 사용한다.
불러온 모듈을 호출하여 서버가 실행될 수 있도록 코드를 선언한다.
클라이언트측에서 브라우저를 통해 요청을 전송하는 경우
브라우저는 프리플라이트 요청을 수행한다.
따라서 브라우저의 프리플라이트 요청에 대한 응답을 적절히 준비하여야한다.
클라이언트측의 요청을 method와 url파라미터를 통해 정의하고 케이스에 따라 다르게 처리한다.
유한상태기계 패턴과 같이 케이스로 처리해주면 좋을것이다.
그러나 자바스크립트의 switch 구문은 하나의 대상만 비교할 수 있기때문에 약간의 문제가 있다.
규모가 작을땐 if구문의 && 문법을 통해 하나하나 처리해주는것이 더 효율적일 수 있을것이다.
따라서 if구문을 사용한다.
if(request.method === "GET"){
데이터를 조회한 뒤 URLParmeter에 맞는 응답을 바디에 담아 전송한다.
이 때 CORS와 관련된 속성을 응답헤더에 담아 함께 전송한다.
}
if(request.method === "POST"){
HTTP 모듈에서 요청바디는 클라이언트측에서 http request header에 정의한
content-type헤더에 적힌 정보를 근거로 데이터를 어떻게 파싱할지 결정한다.
중요한 사실은 바디 데이터가 인코딩되어 들어오며 바디에 접근하여 데이터를 가공하기위해서는
당연하게도 디코딩 작업이 선행되어야 한다는 것이다.
따라서 디코딩을 진행한 뒤 데이터를 가공하고 응답을 보낸다.
}
하지만 과제에서는 클라이언트 측의 정상적인 요청이 POST 로만 전송되며
따로 데이터를 저장하지 않아도 되고
들어온 요청의 바디부분을 urlparameter의 값에 근거하여 가공한뒤 보내주기만 하면 되는 조건이었다.
따라서 실제로 구현된 코드는 다음과 같다.
const http = require("http");
const PORT = 4999;
const ip = "localhost";
모듈을 불러오고 포트번호와 ip를 변수로 만들어 관리한다.
const server = http.createServer((request, response) => {
console.log(
`http request method is ${request.method}, url is ${request.url}`
);
const { headers, method, url } = request;
서버를 실행할수 있도록 http 모듈이 갖고있는 메서드 createServer를 통해 서버를 구동시킨다.
또한 객체 디스트럭처링 할당 문법을 활용하여 커다란 request 객체에서
필요한 정보만을 추출해 담는다.
if (request.method === "OPTIONS") {
response.writeHead(200, defaultCorsHeader);
response.end();
}
브라우저 측의 Preflight 요청은 HTTP 메서드에 "OPTIONS"라는 내용을 담아 요청된다.
따라서 브라우저 측의 프리플라이트 요청을 처리해주기 위한 코드를 작성한다.
status code를 200번대로 설정하여 성공적으로 응답이 도착했음을 알리고
두번째 파라미터에 cors와 관련된 속성들을 헤더에 담아 전송한다.
let body = [];
if (request.method === "POST") {
request
.on("error", (err) => {
console.error(err);
})
.on("data", (chunk) => {
console.log(chunk);
body.push(chunk);
console.log(body);
})
.on("end", () => {
console.log("end?");
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();
}
console.log(body);
response.writeHead(200, defaultCorsHeader);
response.write(body);
response.end();
});
}
다음은 디코딩을 위한 코드들이다.
요청이 POST로 들어온 경우 우리는 클라이언트 측의 바디에 담긴 내용을 디코딩하고
urlParameter의 상태에 맞추어 데이터를 가공한 뒤
헤더에 status 코드와 cors관련 속성을 담고 바디에 가공된 데이터를 담아 전송하는 코드이다.
생소하게 느껴질 수 있는 부분은 chunk 부분과 Buffer 부분인데
데이터는 네트워크를 통해 서버로 전달되기 위해 패킷화되고 작은 단위로 나누어진 상태이기때문에
chunk 단에서 분해된 데이터들을 하나로 완성되도록 합쳐지는 것을 기다릴 필요성이 있다.
아무튼 패킷화된 데이터를 한곳으로 모은 뒤 Buffer를 이용해
클라이언트가 작성한 헤더의 속성 content-type에 맞추어 인코딩된 데이터를
다시 디코딩하는 작업을 수행한다.
그 뒤 urlparameter에 맞추어 데이터를 재가공한 뒤 전송하는 것이다.
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,
};
cors 설정은 꽤 다양한 속성들이 있다.
다만 그중에도 필수적으로 들어가야하는 CORS 헤더 속성들이 존재하는데
그 속성들은 아래 링크에서 참조할 수 있다.
https://xionwcfm.tistory.com/235
CORS / SOP가 머임
🐕 SOP (Same - origin - policy) 동일 출처 정책 동일 출처 정책은 웹 애플리케이션의 중요한 보안 모델입니다. 동일 출처 정책은 같은 출처(Origin)의 리소스만 공유가 가능하다는 정책인데 인간이 보기
xionwcfm.tistory.com
Accesee-Control-Allow-Origin 부분을 보면 내부의 값으로 "*"을 설정해준 것을 알 수 있는데
이것은 와일드카드라고도 불리는 문법으로 모든 출처에 대하여 허용을 한다는 의미를 내포한다.
물론 실제 배포 환경에서 와일드카드 문법을 사용해 모든 출처에 대해 허용을 한다면
그것은 보안적으로 굉장히 취약해지며 CORS를 하는 의미가 없어지는 행위가 될테니
개발환경에서만 사용하는 것이 좋을 것이라고 예상된다.
'코드스테이츠 프론트' 카테고리의 다른 글
코드스테이츠 프론트엔드 44기 부트캠프 수료 후기 SEB FE 44 (5) | 2023.09.02 |
---|---|
쿠키와 세션과 토큰과 캐시와 인증과 인가 (0) | 2023.04.26 |
리액트 리덕스를 간단하게 정리해보자 (0) | 2023.04.24 |
UI/UX (1) | 2023.04.13 |