Lesson 4. Node.js에서 웹 서버 만들기
4장에서 다룰 내용
- Node.js와 npm을 사용한 기본 웹 서버 생성
- 브라우저에서의 요청 처리 및 결과 보내기 코드 작성
- 브라우저에서의 웹 서버 실행
4.1. 웹 서버의 이해
- 웹 서버: Node.js 웹 애플리케이션의 기본. 이미지/HTML 웹 페이지를 앱에서 읽어들여 사용자에게 보여준다.
- 웹 서버: 데이터 읽기 및 처리를 통한, 인터넷상의 요청에 대한 응답을 위해 설계된 소프트웨어.
HTTP(Hypertext Transfer Protocol)이라는 인터넷상에서 웹 페이지 열람 및 데이터 전송을 위해 세계적으로 표준화된 시스템을 따른다.
- 서버와 클라이언트가 통신하는 보편적 방법은 HTTP 요청을 사용하는 것.
가장 자주 접하고 사용하는 2개의 HTTP 메소드는 다음과 같다.
1) GET: 서버로부터 정보를 요청한다. 보통 서버는 브라우저에서 볼 수 있는 컨텐츠로 응답한다.
2) POST: 서버로 정보를 전송하낟. 서버는 데이터 처리 후 HTML 페이지로 응답하거나, 애플리케이션 내 다른 페이지로 이동시킨다.
최근 대부분의 웹 애플리케이션은 HTTP Secure(HTTPS)로 변환되고 있다. 이는 HTTP로 전달되는 모든 데이터를 암호화해준다.
- 요청-응답 관계: 사용자와 애플리케이션 간의 통신 채널을 가능하게 한다.
데이터 묶음은 요청의 형태로 애플리케이션 서버로 전송되고, 서버가 요청을 처리한 후 응답 형태로 데이터 묶음을 다시 만들어 전송한다. 이 과정은 인터넷상의 대부분의 상호작용에서 발생한다.
ex. https://www.google.com을 방문할 때 - 브라우저는 구글 서버로 요청을 보내고, 구글 서버는 순차적으로 응답하며 구글 검색 랜딩 페이지를 렌더링한다.
- 요청-응답 사이클
- 사용자는 브라우저에 URL을 입력한다.
- HTTP 요청이 URL 연관 서버에 전달된다.
- 사용자 요청에 응답할 내용을 정하기 위해, 물리 서버에서 서버 로직이 실행된다.
- HTTP 응답이 HTML/json/일반 텍스트/다른 포맷으로 사용자에 전달된다.
- 사용자는 보통 HTML 페이지의 형태로 응답을 받으며, 이는 브라우저 창에 나타난다.
웹 서버는 클라이언트로부터 요청(request)을 받고, 응답(response)을 돌려준다.
4.2. npm으로 애플리케이션 초기화
- Node.js 웹 애플리케이션을 시작하기 전에, 터미널에서 프로젝트 디렉토리 초기화 필요.
프로젝트 디렉토리를 생성하고, 디렉토리 내부에서 프로젝트 초기화.
mkdir simple_server
npm init
- 텍스트 에디터로 main.js 파일 생성, 프로젝트 폴더 루트에 저장.
이 파일은 코어 애플리케이션 파일로의 역할을 하며, 사용자에게 웹 페이지를 보여준다.
4.3. 애플리케이션 코딩
- http 모듈: Node.js를 설치할 때 같이 설치된 핵심 라이브러리. 이 모듈을 이용해 웹 서버를 구축할 수 있다.
- http-status-codes 패키지를 사용: 애플리케이션의 응답에 HTTP 상태 코드가 필요한 곳에 사용할 상수를 제공.
- 터미널에서 애플리케이션 종속 모듈로 http-status-codes 패키지를 저장하기 위해 다음 명령어 사용.
npm i http-status-codes -S
- main.js에서 간단한 웹 서버 구현
const port = 3000,
http = require("http"),
httpStatus = require("http-status-codes"),
app = http.createServer((request, response) => {
console.log("Recived an incoming request!");
response.writeHead(httpStatus.OK, {
"Content-Type" : "text/html"
});
let responseMessage = "<h1>Hello, Universe!</h1>";
response.write(responseMessage);
response.end();
console.log(`Sent a response: ${responseMessage}`);
});
app.listen(port);
console.log(`The server has started and is listening on port number: ${port}`);
const port = 3000;
- 3000번 포트는 보통 웹 서버 개발 시 자주 사용한다. 숫자에 큰 의미는 없으며, 다른 예외 상황 발생시 변경 가능하다.
- 80포트는 HTTP, 443포트는 HTTPS 용도로 사용된다.
const http = require("http");
const httpStatus = require("http-status-codes");
- require()를 사용해 http 모듈을 가져와 상수로 저장한다. 이때 상수로 저장하는 이유는 변수를 재할당 할 이유가 없기 때문이다. 이를 위해서는 http-status-codes 패키지가 필요하다.
const app = http.createServer((request, responce) => {});
- http 변수를 HTTP 모듈에 대한 참조로 사용하고, 해당 모듈의 createServer 함수를 사용해 서버를 만들어, app 변수에 결과 서버를 저장한다.
- createServer 함수는 새로운 http.Server 인스턴스를 생성한다.
이는 HTTP 통신을 수행하기 위한 도구들이 포함되어 있는 Node.js의 내장 클래스다.
새로 만들어진 서버 인스턴스로 앱은 HTTP 요청을 받을 준비를 하고 HTTP 응답을 전송한다.
- createServer의 매개변수는 서버에서 어떤 이벤트가 발생할 때마다 동작하는 콜백 함수다. 서버가 실행 중이고, 누군가가 루트 URL(홈페이지)에 접속하면, HTTP 요청 이벤트가 이 콜백 함수를 깨우고 사용자 코드를 수행한다.
const app = http.createServer((request, response) => {
console.log("Received an incoming request!");
response.writeHead(httpStatus.OK, {
"Content-Type" : "text/html"
});
- 시스템은 클라이언트로부터 요청을 받았음을 기록하고 콜백 함수의 response 매개변수를 사용해 처음 요청을 보낸 사용자에게 응답한다.
- 첫 번째 줄에서는 writeHead 메소드를 사용해 응답의 HTTP 헤더의 기본 속성을 정의한다.
HTTP 헤더는 요청이나 응답에서 전송되는 내용을 설명하는 정보 필드를 포함한다.
헤더 필드에는 날짜, 토큰, 요청 및 응답의 출처에 대한 정보, 연결 유형을 설명하는 데이터가 포함될 수 있다.
- 여기에서 시스템은 httpStatus.OK를 돌려준다. 이는 응답 코드 200으로 표현되며, 서버가 요청을 성공적으로 받았고 HTML 콘텐츠로 응답을 할 것임을 의미한다. 이 코드 블록에 의해 시스템은 로컬 변수인 responseMessage를 HTML 메세지 응답에 할당한다.
+ 200: HTTP 상태 코드로는 OK. HTML 응답 헤더에서 콘텐츠 응답에 문제가 없음을 나타낸다. 명시적인 숫자보다 httpStatus.OK를 사용하는 것이 더 좋다. HTTP 응답 코드를 보기 위해서는 Node.js REPL 셸에서 http.STAUS_CODES 를 입력하자.
app = http.createServer((request, response) => {
console.log("Recived an incoming request!");
response.writeHead(httpStatus.OK, {
"Content-Type" : "text/html"
});
let responseMessage = "<h1>Hello, Universe!</h1>";
response.write(responseMessage);
response.end();
console.log(`Sent a response: ${responseMessage}`);
});
- write를 사용해 HTML 형식의 출력문으로 응답을 시작한다.
- end를 사용해 응답을 종료한다. 이때 반드시 end를 사용해 더이상의 응답은 없다고 서버에게 확인시켜 줘야 하며, 그렇지 않을 경우 서버는 커넥션을 끊지 않고 계속 연결하고 있어 요청을 받을 수 없는 상태로 남게 된다.
app.listen(port);
console.log(`The server has started and is listening on port number: ${port}`);
- 서버 인스턴스인 app으로 3000번 포트로부터 요청을 받을 준비가 되었다고 listen 메소드로 알린다.
+ response 객체는 애플리케이션 전반에서 함수와 함수 사이에 정보 전달을 수행한다. response 객체 내 writeHead, write 함수는 객체에서 데이터를 추가/제거하는 기능을 한다.
+ 포트 넘버를 지정하지 않았다면 운영체제가 임의로 포트를 지정해준다. 이 포트 넘버는 웹 브라우저를 통해 웹 서버가 실행되고 있는지 확인하는데 사용된다.
- Node.js에서의 콜백
Node.js의 높은 효율성과 속도 뒤에는 콜백 함수가 있다.
콜백은 익명 함수이며, 다른 함수가 종료될 때 사용되도록 설정되어 있다.
콜백 사용의 장점은 다른 코드를 실행하기 전 원 함수의 실행 종료를 기다릴 필요가 없다는 것이다.
- 클라이언트-서버 모델의 콜백
1. 다수의 클라이언트가 서버에 연달아 요청을 보낸다.
2. 요청이 서버에 도달하면 비동기로 처리된다.
3. 서버는 각 요청 처리를 위해 시간을 할당한다. 응답이 준비되면 콜백이 신호를 보낸다.
4. 응답은 요청 순으로 오지 않을 수 있다.
5. 클라이언트는 서버 측의 요청 처리 순으로 응답을 받는다.
4.4. 애플리케이션의 실행
터미널에서 프로젝트 디렉토리로 이동해 node main 명령을 입력한다.
그 후 브라우저를 열어 URL 입력란에 localhost:3000을 입력한다.
애플리케이션을 종료하기 위해서는 Ctrl+C를 터미널에서 입력한다.
4.5. 요약
1) package.json 파일을 통해 새로운 Node.js 애플리케이션 설정
2) http 모듈, createServer 메소드를 사용하여 - 터미널 작업을 통해 웹 서버를 생성