본문 바로가기

Node.js

간단한 서버 구현하기

서버 실행시키는 방법

터미널에 node 파일명.js => 서버실행 => 브라우저로 localhost:3000 접속

 

 

1. OK를 반환하는 서버

-모든 접속에 "OK"로 답하는 서버 만들기

const http = require("http");
const server = http.createServer((req, res) => {
    res.setHeader("Content-Type", "text/html");
    res.end("OK");
});

server.listen("3000", () => { console.log("OK 서버 시작!")})

🙋‍♀️일반적인 웹서버는 URL경로에 따라 다른 응답을 주기때문에 URL의 경로를 읽어서 다른 응답을 주는 코드를 작성해보자.

 

2. 라우터 만들기

-라우팅 :  URL경로에 따라서 다른 응답을 주는 기능.

-URL이라는 모듈을 사용하면 URL경로를 간단하게 읽어올 수 있다.

-HREF(Hypertext REFerence) : URL 구조를 간략하게 나타낸 것

https://www.example.com:80/path/to/file.html?key1=vale1#hash
프로토콜 도메인명 포트번호 웹서버 자원
대한 경로
쿼리
(추가매개변수)
hash(북마크)
서버에 전송X
https www.example.com :80 path/to/file.html ?key1=vale1 #hash

-localhost:3000/userlocalhost:3000/feed라는 두 URL이 있다고 가정하고 두 요청에 대해 다른 응답을 주는 코드를 작성

-user와 feed요청을 처리하는 서버

const http = require("http");
const url = require("url");


http.createServer((req, res) => {
//요청으로 받은 url의 pathname을 얻는다.
//parse()함수의 두번째 인수는 true는 쿼리스트링도 함께 파싱할지 여부를 설정하는 변수
    const path = url.parse(req.url, true).pathname;
    //한글을 쓰면 출력된 값이 깨지기 때문에 text/html에 charset=utf-8을 추가해준다.
    res.setHeader("Content-Type", "text/html; charset=utf-8");

    if(path === "/user"){
        res.end("[user] name: andy, age: 30");
    }else if(path === "/feed"){
        res.end(`
        <ul>
            <li>picture1</li>
            <li>picture2</li>
            <li>picture2</li>
        <ul>
        `);
    } else {
        res.statusCode = 404;
        res.end("404 page not found")
    }
})
.listen("3000", () => { console.log("라우터를 만들어보자!")})

-터미널에 node 파일명.js 

-결과

🙋‍♀️위의 코드는 요청에 대한 응답을 createServer()안에서 직접 컨트롤하는데 이 안의 콜백함수에 모든 코드를 다 추가해야 하므로 좋지 않다.

🙋‍♀️라우팅 이후의 처리를 별도의 함수를 만들어서 처리해보자.

 

3. createServer() 리팩터링하기

-메인루틴을 유지하면서도 요청별 함수만 요구사항에 맞게 변경되는 코드 작성

-라우터와 실행하는 함수코드를 나누어보기

const http = require("http");
const url = require("url");


http.createServer((req, res) => {
    const path = url.parse(req.url, true).pathname;
    res.setHeader("Content-Type", "text/html; charset=utf-8");

    //각 경로에 따른 함수 실행
    if(path === "/user"){
        user(req, res);
    }else if(path === "/feed"){
        feed(req, res);
    } else {
        notFound(req, res);
    }
})
.listen("3000", () => { console.log("라우터를 만들어보자!")})

//경로 요청을 처리하는 코드를 함수로 분리시킴.
const user = (req, res) => {
    res.end(`[uer] name : andy, age: 30`);
}
const feed = (req, res) => {
    res.end(`
    <ul>
        <li>picture1</li>
        <li>picture2</li>
        <li>picture3</li>
    </ul>
    `)
}
const notFound = (req, res) => {
    res.statusCode = 404;
    res.end("404 page not found")
}

🙋‍♀️해당 경로에 접속하면 언제나 같은 결과만 보여주기 때문에 매개변수에 따라 동적으로 응답이 변경되도록 쿼리를 추가해보자.

4. 동적으로 응답하기

-user()함수는 응답으로 고정된 name과 age정보를 반환했다.

-user()함수를 수정해서 매개변수에 따라 동적으로 응답이 변경되도록 수정해본다.

-url의 query부분에 name, age정보를 추가해본다.

const http = require("http");
const url = require("url");


http.createServer((req, res) => {
    const path = url.parse(req.url, true).pathname;
    res.setHeader("Content-Type", "text/html; charset=utf-8");

    if(path === "/user"){
        user(req, res);
    }else if(path === "/feed"){
        feed(req, res);
    } else {
        notFound(req, res);
    }
})
.listen("3000", () => { console.log("라우터를 만들어보자!")})

const user = (req, res) => {
	//쿼리스트링 데이터를 userInfo에 할당한다.
    const userInfo = url.parse(req.url, true).query
    //결과값으로 이름과 나이를 설정한다.
    res.end(`[uer] name : ${userInfo.name}, age: ${userInfo.age}`);
}
const feed = (req, res) => {
    res.end(`
    <ul>
        <li>picture1</li>
        <li>picture2</li>
        <li>picture3</li>
    </ul>
    `)
}
const notFound = (req, res) => {
    res.statusCode = 404;
    res.end("404 page not found")
}

-서버에 접속하면서 query부분에 아무런 값도 주지 않았기 때문에  undefined가 뜬다.

-url의 query에 매개변수로 name과 age를 추가해본다.

매개변수가 하나일 때 : ?키=값
매개변수가 두개 이상 일때  : ?키=값&키=값

🙋‍♀️위의 코드는 분기문에서 모든 요청을 분석한다. 지금은 함수가 3개뿐이지만 함수가 더 많아지면 유지보수가 매우 힘들어진다. 유지보수성을 높이기 위해 라우터를 리팩토링해본다.

 

5. 라우터 리팩토링하기

-분기문에 사용되는 매개변수가 같은 패턴을 보일 때는 맵 자료구조가 유용하다.

const http = require("http");
const url = require("url");


http.createServer((req, res) => {
    const path = url.parse(req.url, true).pathname;
    res.setHeader("Content-Type", "text/html; charset=utf-8");

    //urlMap에 path가 있는지 확인한다.
    if(path in urlMap){
        //ulrMap에 path값으로 매핑된 함수 실행
        urlMap[path](req, res);
    } else {
        notFound(req, res);
    }
})
.listen("3000", () => { console.log("라우터를 만들어보자!")})

//경로 요청을 처리하는 코드를 함수로 분리시킴.
const user = (req, res) => {
    const userInfo = url.parse(req.url, true).query
    res.end(`[uer] name : ${userInfo.name}, age: ${userInfo.age}`);
}
const feed = (req, res) => {
    res.end(`
    <ul>
        <li>picture1</li>
        <li>picture2</li>
        <li>picture3</li>
    </ul>
    `)
}
const notFound = (req, res) => {
    res.statusCode = 404;
    res.end("404 page not found")
}
//라우터 규칙 매핑 키로 path가 들어가고 값에 함수를 할당하였다.
const urlMap = {
    "/": () => {},
    "/user": user,
    "/feed": feed,
}

🙋‍♀️위의 기본 웹서버로도 서비스가 가능하지만 기능이 많이 부족하다.

🙋‍♀️실전에서 사용하는 서버라면 라우터에서 HTTP메서드로 각각 요청을 받는 기능, 템플릿 엔진 기능, 미들웨어 등의 기능을 제공해야한다.

728x90

'Node.js' 카테고리의 다른 글

npm  (0) 2023.08.13
익스프레스 프레임워크 사용하  (0) 2023.08.13
Formdata  (0) 2023.06.29
브라우저-서버의 요청-응답  (0) 2023.06.29
널병합 연산자 / 옵셔널 체이닝  (0) 2023.06.29