kakasoo

Express 프레임워크 만들기(2) 본문

프로그래밍/JavaScript

Express 프레임워크 만들기(2)

카카수(kakasoo) 2021. 5. 20. 10:31
반응형

URL에 따라 다르게 동작하기

메서드에 대한 고민은 일단 접어둡시다. 메서드는 오직 GET 하나만 가능한 서버를 만들 겁니다. 일단 편의 상 경로는 아래처럼 세 가지가 있다고 가정하고 해봅시다.

  • /
  • /cats
  • /dogs

세 가지 경로에 대해서 분기를 한다고 하면 이렇게도 가능할 것입니다.

 

const TExpress = () => (req, res) => {
    console.log("Current URL is : ", req.url);
    if (req.url === "/") {
        res.setHeader("Content-Type", "text/plain");
        res.end("root.");
    } else if (req.url === "/cats") {
        res.setHeader("Content-Type", "text/plain");
        res.end("cats.");
    } else if (req.url === "/dogs") {
        res.setHeader("Content-Type", "text/plain");
        res.end("dogs.");
    }
};

 

하지만 코드가 늘어날수록 관리하기 힘들 거 같습니다. 그러니 확장성을 위해서라도 URL에 따라 분리해주는 것이 좋을 거 같습니다. 우리는 이미 express에서 그런 코드를 보았습니다. 아래처럼 작성했습니다.

 

const app = express();

app.get('/', (req,res,next) => res.send('root.'));
app.get('/cats', (req,res,next) => res.send('cats.'));
app.get('/dogs', (req,res,next) => res.send('dogs.'));

 

이 코드는 app.get()을 실행하라는 의미가 아닙니다! 이 코드의 정확한 의미는 첫 번째 파라미터로 받는 path에 두 번째 파라미터로 받고 있는 콜백함수를 전달하는 것입니다. 여기서 이 콜백함수는 편의 상 handler 라고 부르도록 하겠습니다.

이렇게 특정 메서드에 대해 URL 경로와 handler를 파라미터로 주면, 해당 경로에 대해서 파라미터를 등록해주면 될 것입니다.

그러면 이걸 먼저 만들어보도록 할까요?

 

const TExpress = () => {
    const app = (req, res) => {
        const url = req.url;
        const method = req.method.toLowerCase();
        app[url][method](req, res);
    };

    return app;
};

 

먼저 함수의 실행을 고쳐보았습니다. 초기의 코드를 보면 처리를 담당하는 app 이라는 함수를 반환하는 형태였습니다. 그렇지만, 그렇게 작성할 경우 어떤 메서드, 어떤 path든 상관없이 하나의 로직을 거치게 됩니다.

따라서 분기 처리를 하기 위해서 app이라는 함수 자체를 고칠 필요가 있었습니다. 따라서 url과 method에 따라 다르게 들어가게끔 처리하였습니다.

하지만 이 코드에는 아직 메서드에 따라 handler를 등록해주는 로직이 없습니다. 그 코드를 추가해보겠습니다.

 

const TExpress = () => {
    const app = (req, res) => {
        const url = req.url;
        const method = req.method.toLowerCase();
        app[url][method](req, res);
    };

    app["get"] = (path, callback) => {
        if (!app[path]) {
            app[path] = {};
        }

        app[path]["get"] = callback;
    };
    return app;
};

 

app['get'] 부분이 메서드를 등록하는 함수인 것을 알 수 있습니다. 만약 application에 아직 그 path에 대한 객체 ( 나중에는 이것을 Router 라고 부르게 될 것입니다. ) 가 만들어지지 않았다면 일단 빈 객체를 만들어주고, 그 빈 객체에 대한 메서드로 handler를 지정해주면 됩니다.

( 여담입니다만, URL을 등록할 수는 있어도, 아직 URL에 따라 객체를 분리해주는 수준은 아닙니다. )

 

이제 이 코드는 동작합니다!

 

const http = require("http");
const PORT = 3000;
const TExpress = require('./TExpress.js');

const app = TExpress();
app.get("/", (req, res, next) => res.end("root."));

const server = http.createServer(app);

server.listen(PORT, () => console.log("Server is opened."));

 

아직 많은 부분에서 express에 비해 부족한 코드입니다. 하지만 겉보기만이라도 express와 비슷하게 동작하는 것 같습니다. 바로 눈에 띄는 차이점은, res.send() 함수가 express에서 만든 함수라서 아직 이 코드에서는 사용할 수 없다는 점입니다.

 

method에 따라 다르게 동작하기

사실 이 부분은 이미 다들 눈치챘을 것 같습니다. get 메서드를 등록하듯이, 다른 메서드들도 모두 등록해주면 되는 거니깐요. 다행히도 우리는 모든 메서드 이름을 스트링 배열로 받을 수가 있습니다.

 

const { METHODS } = require("http");

const TExpress = () => {
    const app = (req, res) => {
        const url = req.url;
        const method = req.method.toLowerCase();
        app[url][method](req, res);
    };

    // app["get"] = (path, callback) => {
    //     if (!app[path]) {
    //         app[path] = {};
    //     }

    //     app[path]["get"] = callback;
    // };

    METHODS.forEach((METHOD) => {
        const method = METHOD.toLowerCase();
        app[method] = (path, callback) => {
            if (!app[path]) {
                app[path] = {};
            }

            app[path][method] = callback;
        };
    });
    return app;
};

 

http 모듈에는 METHODS를 받아올 수 있습니다. 이것을 forEach문을 돌면서 각각 등록하는 과정을 거치면 됩니다. 어떤가요? 차례대로 보니까 이해가 가지 않나요? 바로 METHODS를 보여주드리면 app을 생성한 후에 app.get(path, callback)과 코드 내부가 헷갈릴까봐 따로 보여드린 것이었습니다.

 

메서드에 따라 handler를 등록해주는 함수를 만드는 것, 그것을 실행해서 handler를 등록해주는 것, 이 둘은 명확하게 다른 영역의 코드입니다.

  1. 메서드에 따라 handler를 등록해주는 함수 ( = app[methodName] )
  2. 등록된 handler를 실행해주는 함수 ( = app )
    • 실행 시에 app 함수 내부에서 req를 읽고 올바른 경로로 route 해준다.
반응형