Node JS net 모듈을 이용한 HTTP 서버 구축

DW J·2022년 8월 15일
0

project_httpserver

목록 보기
1/1

1. 프로젝트 개요

nodejs의 net 모듈을 이용하여 http서버를 구축하고 클라이언트와 서버간 데이터 통신을 이해하기 위한 프로젝트

2. 프로젝트 조건

  1. node.js의 net모듈(tcp api 모듈)을 활용하여 http 서버를 구축하고 다음의 기능을 구현
    • node를 제외한 외부 라이브러리를 사용하지 않는다 - http, express등
    • root url(localhost:port)로 접속시 메인 화면 출력
    • 다른 페이지로 넘어가는 링크를 제공한다 (음식선택페이지, 404에러 페이지, 500에러 페이지)
  2. 음식선택 페이지 기능 요구사항
    • html 확장자가 없는 url로 접근 ex) localhost:port/eat.html > localhost:port/eat
    • 버튼을 클릭하면 음식메뉴가 랜덤으로 화면에 표시 된다
    • 음식메뉴는 서버에 api를 호출하여 가져온다
  3. 잘못 된 url을 요청하면 404 에러 응답이 오는 케이스를 구현한다
  4. 서버 내부에서 오류가 발생하면 500 에러 응답이 오는 케이스를 구현한다
  5. 브라우저에서 favicon.ico 요청이 오는데 favicon이미지를 반환하여 favicon이 나오도록 구현한다

3. 프로젝트 구현 로직

3-1) Server

1. const { createServer, Socket } = require('net'); // // http서버 구축을 위한 net모듈
2. const path = require('path'); // path의 조합을 path모듈
3. const js = require('fs') // 해당 디렉토리 여부, 파일의 생성/수정/삭제/읽기 등을 위한 fs모듈
4. const port = 3030; // 서버의 기본  포트 번호

client에서 Request(요청)이 오면 내부 로직에 의해 Response(응답)처리
client에서 connect로 접속하면 이 Listener로 Socket Client가 들어오는 형태
const server = createServer(client) {

	client.on('data', (data) => {
    	// client에서 write()를 하면, 이곳을 통해 메세지를 받아 write()를 해준다
        // 여기에 Response(응답) 로직 구현
    });
    
};

해당 포트에서 수신을 대기하는 서버를 만듬
server.listen(port, () => {});

3-2) Client

// ---------------------------------------------
// client
// ---------------------------------------------
const client = new Socket(); // Socket을 생성
client.connect(port, 'localhost', () => {}); // 포트, url로 접속

client.on('data', serverData => {
	// server에서 write()하여 보내준 데이터를 이곳에서 처리
    client.destroy();
});

이 프로젝트를 진행 하면서 발생했었던 문제

  1. 요청, 응답, Header, Body등 HTTP 통신을 제대로 이해하지 못한 상태로 프로젝트를 진행하다 보니 서버에서 요청이나 응답을 할 때 어떤식으로 보내야 하는지에 대한 로직 구현의 어려움이 발생
  2. 링크의 확장자가 없는 상태에서 클라이언트(브라우저) 요청이 오는데 해당 확장자를 파싱할 수 있는 방법을 알지 못해 로직내부에 하드코딩
  3. 언제 어떤파일이 요청으로 을지 모르는 상태에서 하드코딩을 해놓은 상태라 파일이 동적으로 추가될 때 마다 동일한 양의 로직이 생성됨
  4. 이해도가 없는 상태에서 기능구현에만 초점을 맞춰 읽기도 불편하고 엉망진창임

이 외에도 무수히 많은 문제점이 있겠지만 직접 느낀 문제점들만 나열..
아래는 Server/Client 전체 로직

3-3) Server / Client

// ---------------------------------------------
// server
// ---------------------------------------------
const { createServer, Socket } = require('net');
const path = require('path');
const fs = require('fs');
const port = 3030;

const server = createServer((clientSocket) => { // 2
    clientSocket.on('data', (data) => { // 4
         // buffer 데이터 > string 데이터로 변경
        const requestMessage = data.toString();
        // 요청온 데이터 중 첫번 째 줄(메소드, URI, 버전) 따로 변수에 저장
        const [first] = requestMessage.split('\r\n'); 
        // 각각의 변수명으로 저장
        let [method, resource, version] = first.split(' ');

        // 만약 URI가 "/"값이면 index.html 넣어줌
        if (resource[resource.length - 1] === "/") {
            resource += 'index';
        }

        const file = resource.split("/");
        const fileName = file.pop();
        console.log("파일명 >>>>>>> ", fileName);

        const filePath = file.join("/");
        console.log("폴더명 >>>>>>>", filePath);

        const fullPath = path.join(process.cwd(), filePath);

        try {
            if (fileName === "index") { // index 페이지
                const content = fs.readFileSync(path.join(fullPath, 'index.html')); // 동기
    
                clientSocket.write(Buffer.from(`HTTP/1.1 200 OK\r\n`));
                clientSocket.write(Buffer.from(`Content-Type: text/html\r\n`));
                clientSocket.write(Buffer.from(`Content-Length: ${content.length}\r\n`));
                clientSocket.write(Buffer.from(`\r\n`));
                clientSocket.write(content);
                clientSocket.write(Buffer.from(`\r\n`));
                clientSocket.end();
            }
            else if (fileName === "what_to_eat") { // what_to_eat 페이지
                const content = fs.readFileSync(path.join(fullPath, 'what_to_eat.html')); // 동기
    
                clientSocket.write(Buffer.from(`HTTP/1.1 200 OK\r\n`));
                clientSocket.write(Buffer.from(`Content-Type: text/html; text/javascript;\r\n`));
                clientSocket.write(Buffer.from(`\r\n`));
                clientSocket.write(content);
                clientSocket.write(Buffer.from(`\r\n`));
                clientSocket.end();
            }
            else if (fileName === "randomMenu") {
                const content = require(path.join(fullPath, 'randomMenu.js')).getmenu();
    
                clientSocket.write(Buffer.from(`HTTP/1.1 200 OK\r\n`));
                clientSocket.write(Buffer.from(`Content-Type: text/plain;\r\n`));
                clientSocket.write(Buffer.from(`\r\n`));
                clientSocket.write(content);
                clientSocket.write(Buffer.from(`\r\n`));
                clientSocket.end();
            }
            else if (fileName === "error500") {
                require(path.join(process.cwd(), '/server/', 'error.js')).geterror();
            }
            else {
                // 404 error
                const content = `
                    <!doctype html>
                    <html lang="en">
                        <head>
                            <meta charset="UTF-8">
                            <<meta name="viewport" content="width=device-width, initial-scale=1.0">
                            <meta http-equiv="X-UA-Compatible" content="ie=edge">
                            <title>Document</title>
                        </head>
                        <body>
                            <h1>404 Error</h1>
                        </body>
                    </html>
                `;
    
                clientSocket.write(Buffer.from(`HTTP/1.1 404 ${resource} is not found\r\n`));
                clientSocket.write(Buffer.from(`Content-Type: text/html\r\n`));
                clientSocket.write(Buffer.from(`Content-Length: ${content.length}\r\n`));
                clientSocket.write(Buffer.from(`\r\n`));
                clientSocket.write(content);
                clientSocket.write(Buffer.from(`\r\n`));
                clientSocket.end();
            }
        }
        catch (error) {
            console.log(error);
            const content = `
                <!doctype html>
                <html lang="en">
                    <head>
                        <meta charset="UTF-8">
                        <<meta name="viewport" content="width=device-width, initial-scale=1.0">
                        <meta http-equiv="X-UA-Compatible" content="ie=edge">
                        <title>Document</title>
                    </head>
                    <body>
                        <h1>${error}</h1>
                    </body>
                </html>
            `;

            clientSocket.write(Buffer.from(`HTTP/1.1 500 Internal Server error\r\n`));
            clientSocket.write(Buffer.from(`Content-Type: text/html\r\n`));
            clientSocket.write(Buffer.from(`Content-Length: ${content.length}\r\n`));
            clientSocket.write(Buffer.from(`\r\n`));
            clientSocket.write(content);
            clientSocket.write(Buffer.from(`\r\n`));
            clientSocket.end();
        }
    });
});

server.listen(port, () => {
    console.log(`Server Listen`);
});

// ---------------------------------------------
// client
// ---------------------------------------------
const client = new Socket();
client.connect(port, 'localhost', () => {});

client.on('data', serverData => {
    console.log(`[client] data >>>>>> `, serverData);
    client.destroy();
});

Git URL - https://github.com/jungcolor/app_project/tree/main/tcp-socket-exercises

profile
잘하는것보다 꾸준히하는게 더 중요하다

0개의 댓글