04. NodeJS 서버 실행하기

Kim Sang Yeob·2023년 1월 13일
0

NodeJS Book 3th

목록 보기
4/9
post-thumbnail

4.1 요청과 응답하기

1) 서버와 클라이언트

  • 서버와 클라이언트가 서버로 요청(request)을 보냄
  • 서버는 요청을 처리
  • 처리 후 클라이언트로 응답(response)을 보냄

2) 노드로 http 서버 만들기

  • http 요청에 응답하는 노드 서버
    • createServer로 요청 이벤트에 대기
    • req 객체는 요청에 관한 정보가, res 객체는 응답에 관한 정보가 담겨 있음
const http = require('http');

const server = http.createServer((req, res) => {
    res.write('<h1>Hello Node!</h1>');
    res.write('<p>Hello server</p>');
    res.end('<p>Hello ZeroCho</p>');
})
    .listen(8080);
server.on('listning', () => {
    console.log('8080번 포트에서 서버 대기 중입니다.');
});
server.on('error', (error) => {
    console.error(error);
});

3) localhost와 포트

  • localhost는 컴퓨터 내부 주소
    • 외부에서는 접근 불가능
  • 포트는 서버 내에서 프로세스를 구분하는 번호
    • 기본적으로 http 서버는 80번 포트 사용(생략가능, https는 443)
    • 다른 포트로 데이터베이스나 다른 서버 동시 연결 가능

4) fs로 HTML 읽어 제공하기

  • 응답하고자 하는 데이터의 정보를 writeHead를 통해서 제공한다
const http = require('http');
const fs = require('fs').promises;

const server = http.createServer(async (req, res) => {
    try{
        res.writeHead(200, { 'Content-Type': 'text/html; charset-utf-8' });
        const data = await fs.readFile('./server.html');
        res.end(data); 
    } catch(error){
        console.error(err);
        res.writeHead(200, { 'Content-Type': 'text/plain; charset-utf-8' });
        res.end(err.message);
    }
})
    .listen(8080);
server.on('listning', () => {
    console.log('8080번 포트에서 서버 대기 중입니다.');
});

4.2 REST API와 라우팅

1) REST API

  • 서버에 요청을 보낼 때는 주소를 통해 요청의 내용을 표현
    • /index.html이면 index.html을 보내달라는 뜻
    • 항상 html을 요구할 필요는 없음
    • 서버가 이해하기 쉬운 주소가 좋음
  • REST API (Representational State Transfer)
    • 서버의 자원을 정의하고 자원에 대한 주소를 지정하는 방법
    • /user이면 사용자 정보에 관한 정보를 요청하는 것
    • /post면 게시글에 관련된 자원을 요청하는 것
  • HTTP 요청 메서드
    • GET : 서버 자원을 가져오라고 할 때 사용
    • POST : 서버에 자원을 새로 등록하고자 할 때 사용(또는 뭘 써야할 지 애매할 때)
    • PUT : 서버의 자원을 요청에 들어있는 자원으로 치환하고자 할 때 사용
    • PATCH : 서버 자원의 일부만 수정하고자 할 때 사용
    • DELETE : 서버의 자원을 삭제하고자 할 때 사용

2) HTTP 프로토콜

  • 클라이언트가 누구든 서버와 HTTP 프로토콜로 소통 가능
    • iOS, 안드로이드, 웹이 모든 같은 주소로 요청을 보낼 수 있음
    • 서버와 클라이언트의 분리
  • RESTful
    • REST API를 사용한 주소 체계를 이용하는 서버
    • GET /user는 사용자를 조회하는 요청, POST /user는 사용자를 등록하는 요청
HTTP 메서드주소역할
GET/restFront.html 파일 제공
GET/aboutabout.html 파일 제공
GET/users사용자 목록 제공
GET기타기타 정적 파일 제공
POST/users사용자 등록
PUT/users/사용자id해당 id의 사용자 수정
DELETE/users/사용자id해당 id의 사용자 제거

3) REST API 만들기

ZeroCho Github repo

4.3 쿠기와 세션 이해하기

1) 쿠키의 필요성

  • 요청에는 한 가지 단점이 있다
    • 누가 요청을 보냈는지 모름(IP 주송와 브라우저 정보 정도만 앎)
    • 로그인을 구현하면 해결
    • 쿠키와 세션이 필요
  • 쿠키 : 키=값의 쌍
    • name = zerocho
    • 매 요청마다 서버에 동봉해서 보냄
    • 서버는 쿠키를 읽어 누구인지 파악

2) 쿠키 서버 만들기

  • writeHead : 요청 헤더에 입력하는 메서드
  • Set-Cookie : 브라우저에게 쿠키를 설정하라고 명령
  • 매 요청마다 서버에 동봉해서 보냄
const http = require('http');

http.createServer((req, res) => {
    console.log(req.url, req.headers.cookie);
    res.writeHead(200, { 'Set-Cookie' : 'mycookie = test'});
    res.end('Hello Cookie');
})
    .listen(8083, () => {
        console.log('8083번 포트에서 서버 대기 중입니다!');
    });

3) 헤더와 본문

  • http 요청과 응답은 헤더와 본문을 가짐
    • 헤더는 요청 또는 응답에 대한 정보를 가짐
    • 본문은 주고받는 실제 데이터
    • 쿠키는 부가적인 정보이므로 헤더에 저장

4) 쿠키로 나를 식별하기

ZeroCho Github repo

  • 쿠키명=쿠키값 : 기본적인 쿠키의 값. mycookie=test또는 name=zerocho같이 설정
  • Expires=날짜 : 만료 기한. 이 기한이 지나면 쿠키는 제거된다. 기본값은 클라이언트가 종료될 때까지
  • Max-age=초 : Expires와 비슷하지만 날짜 대신 초를 입력할 수 있다. 해당초가 지나면 쿠기가 제거된다. Expires보다 우선한다.
  • Domain=도메인명 : 쿠키가 전송될 도메인을 특정할 수 있다. 기본값은 현재 도메인이다.
  • Path=URL : 쿠키가 전송될 URL을 특정할 수 있다. 기본값은 '/'이고 이 경우 모든 URL에서 쿠키를 전송할 수 있다.
  • HttpOnly : 설정 시 자바스크립트에서 쿠키에 접근할 수 없다. 쿠키 조작을 방지하기 위해 설정하는 것이 좋다.

6) 세션 사용하기

  • 쿠키의 정보는 노출되고 수정되는 위험이 있음
    • 중요한 정보는 서버에서 관리하고 클라이언트에는 세션 키만 제공
    • 서버에 세션 객체(session) 생성 후, uniquelnt(키)를 만들어 속성명으로 사용
    • 속성 값에 정보를 저자하고 uniquelnt를 클라이언트에 보냄
  • 소스코드 ZeroCho Github repo

4.4 https와 http2

1) https

  • 웹 서버에 SSL 암호화를 추가하는 모듈
    • 오고 가는 데이터를 암호화해서 중간에 다른 사람이 요청을 가로채더라도 내용을 확인할 수 없음
    • 요즘에는 https 적용이 필수(개인 정보가 있는 곳은 특히)

2) https 서버

const https = require('https');
const fs = require('fs').promises;

https.createServer({
    cert: fs.readFileSync('도메인 인증서 경로'),
    key: fs.readFileSync('도메인 비밀키 경로'),
    ca: [
        fs.readFileSync('상위 인증서 경로'),
        fs.readFileSync('상위 인증서 경로'),
    ],
}, (req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html; charset-utf-8' });
    res.write('<h1>Hello Node!</h1>');
    res.end('<p>Hello Server!</p>')
})
    .listen(443, () => {
        console.log('443번 포트에서 서버 대기 중입니다');
    });

3) http2

  • SSL 암호화에 더불어 최신 HTTP 프로토콜이 http/2를 사용하는 모듈
    • 요청 및 응답 방식이 기존 http/1.1보다 개선됨
    • 웹의 속도도 개선됨

4) http2 적용 서버

const http2 = require('http2');
const fs = require('fs');

http2.createCecureSever({
    cert: fs.readFileSync('도메인 인증서 경로'),
    key: fs.readFileSync('도메인 비밀키 경로'),
    ca: [
        fs.readFileSync('상위 인증서 경로'),
        fs.readFileSync('상위 인증서 경로'),
    ],
}, (req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html; charset-utf-8' });
    res.write('<h1>Hello Node!</h1>');
    res.end('<p>Hello Server!</p>')
})
    .listen(443, () => {
        console.log('443번 포트에서 서버 대기 중입니다');
    });

4.5 cluster

1) cluster

  • 기본적으로 싱글 스레드인 노드가 CPU 코어를 모두 사용할 수 있게 해주는 모듈
    • 포트를 공유하는 노드 프로세스를 여러 개 둘 수 있음
    • 요청이 많이 들어왔을 때 병렬로 실행된 서버의 개수만큼 요청이 분산됨
    • 서버에 무리가 덜 감
    • 코어가 8개인 서버가 있을 때 : 보통은 코어 하나만 활용
    • cluster로 코어 하나당 노드 프로세스 하나를 배정 가능
    • 성능이 8배가 되는 것은 아니지만 개선됨
    • 단점 : 컴퓨터 자원(메모리, 세션 등) 공유 못함
    • Redis 등 별도 서버로 해결

2) 서버 클러스터링

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if(cluster.isMaster){
    console.log(`마스터 프로세스 아이디: ${process.pid}`);
    // CPU 개수만큼 워커를 생산
    for(let i = 0; i < numCPUs; i += 1){
        cluster.fork();
    }
    // 워커가 종료되었을 때
    cluster.on('exit', (worker, code ,signal) => {
        console.log(`${worker.process.pid}번 워커가 종료되었습니다.`);
        console.log('code', code, 'signal', signal);
        // cluster.fork(); // 워커 하나 종료되면 새로 하나 켜주기
    });
} else{
    // 워커들이 포트에서 대기
    http.createServer((req, res) => {
        res.writeHead(200, {'Content-Type' : 'text/html; charset=utf-8'});
        res.write('<h1>Hello Node!</h1>');
        res.end('<p>Hello Cluster!</p>');
        setTimeout(() => { // 워커 존재를 확인하기 위해 1초마다 강제 종료
            process.exit(1);
        }, 1000);
    }).listen(8086);

    console.log(`${process.pid}번 워커 실행`);
}
profile
Studying NodeJS...

0개의 댓글