바람직한 코드

이전부터 Node.js로 작고 큰 프로젝트들을 해왔지만 뚜렷한 코드 작성의 방향없이 생각나는 대로 집어 쓰는 경우가 많았다. “되기만 하면 된다”는 생각을 버려야 한다. 난 요즘 코드를 작성할 때 내가 만든 프로그램이 오픈소스가 되었을 때 사람들에게 당당하게 보여줄 수 있는 코드를 쓰려 노력하고 있다.

노드로 서버를 구축하면 항상 쓰는 Express 작성 방식부터가 엉망이었다. 처음에 책에서 하란 대로 하는데 되니까 그저 서버가 돌아가는 것에 만족하고 사용해왔다가, 여러 강의와 오픈 소스들을 살펴보니 Express 서버(app) 자체를 Class화하여 효율적으로 관리할 수 있는 방법이 있었다.

이전의 주먹구구식 Express

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
var express = require("express");
var app = express();
var http = require("http").createServer(app);
var path = require("path"),
bodyParser = require("body-parser"),
cookieParser = require("cookie-parser"),
static = require("serve-static"),
errorHandler = require("errorhandler"),
expressErrorHandler = require("express-error-handler"),
expressSession = require("express-session"),
ejs = require("ejs"),
fs = require("fs"),
url = require("url"),
cors = require("cors");

app.set("port", process.env.PORT || 3000);
app.use(express.json());
app.use(bodyParser.json());
app.use(
bodyParser.urlencoded({
extended: true,
})
);
app.use("/public", express.static(__dirname + "/public"));
app.use(cookieParser());
app.use(
expressSession({
secret: "my key",
resave: true,
saveUninitialized: true,
})
);
app.use(cors());
var router = express.Router();

// 404 에러 페이지 처리
var errorHandler = expressErrorHandler({
static: {
404: "./public/404.html",
},
});

app.use(expressErrorHandler.httpError(404));
app.use(errorHandler);

http.listen(app.get("port"), function () {
console.log("server started - port: " + app.get("port"));
});

가관이지만, 사실 이 형편없는 코드만으로도 서버는 정상적으로 돌아간다. 하지만 내가 노드를 쓰는 사람을 뽑는 채용 담당자라면 미들웨어가 이렇게 엉켜있고 재선언할 리 없는 변수나 라이브러리를 var로 남발하는 등의 엉성한 코드를 쓰는 사람에게 프로젝트를 믿고 맡길 수는 없을 것 같다.

보다 나은 Express

먼저 app.jsserver.js, app.js로 분할한다. (이름은 상관없다)

Express 관련 코드를 app에 담고, 그 apphttphttps로 오픈할 server 소스로 나누어 작성한다.

먼저 app.js를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
const express = require("express");
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
const expressSession = require("express-session");
const cors = require("cors");
const logger = require("morgan");

class App {
constructor() {
this.app = express();
this.setViewEngine();
this.setMiddleWare();
this.setStatic();
this.setLocals();
this.getRouting();
this.errorHandler();
}

setMiddleWare() {
// HTTP -> HTTPS Redirection
this.app.use((req, res, next) => {
if (req.secure) {
next();
} else {
const to = `https://${req.hostname}${req.url}`;
res.redirect(to);
}
});
this.app.use(logger("dev"));
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: false }));
this.app.use(cookieParser());
this.app.use(
expressSession({
secret: "my key",
resave: true,
saveUninitialized: true,
})
);
this.app.use(cors());
}

setViewEngine() {
this.app.set("view engine", "ejs");
this.app.set("views", "./public");
this.app.engine("html", require("ejs").renderFile);
}

setStatic() {
this.app.use("/public", express.static(__dirname + "/public"));
}

setLocals() {
this.app.use((req, res, next) => {
this.app.locals.isLogin = true;
next();
});
}

getRouting() {
this.app.use(require("./controllers"));
}

errorHandler() {
this.app.use((req, res, _) => {
res.status(404).render("404.html");
});

this.app.use((err, req, res, _) => {
res.status(500).render("500.html");
});
}
}

module.exports = new App().app;

코드만 봐도 Express에서 미들웨어를 설정하는지, 뷰엔진을 설정하는지, 라우터를 설정하는지, 에러핸들링을 하는지 알 수 있다. 만약에 미들웨어를 추가할 일이 생겼다면 setMiddleware()를 보고 여기가 미들웨어 넣는 자리구나하고 넣으면 된다.

이 Express app을 Class로 관리하면 또 다른 장점이 있는데, 서버 앱을 하나의 인스턴스로 관리할 수 있다. 비동기식 실행과 스레딩의 장점으로 노드를 CDN 서버 등으로 많이 활용하는데 접속된 인스턴스가 몇 개인지 관리하거나 접속 인스턴스의 갯수 제한을 두는 등의 작업을 편하게 할 수 있다.

이제 이 app을 기반으로 서버를 열면 된다. server.js를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
const app = require("./app.js");
const https = require("https");
const options = require("./config/pem_config").options;
const httpPort = 80;
const httpsPort = 443;

https.createServer(options, app).listen(httpsPort, () => {
console.log(`HTTPS: Express listening on port ${httpsPort}`);
});

app.listen(httpPort, () => {
console.log(`HTTP: Express listening on port ${httpPort}`);
});

작성해놓은 app을 불러와서 httphttps 모두로 열어주면 된다. 당연히 https는 발급한 SSL 인증서 정보를 같이 넣어주어야 한다. SSL 인증서로 https 서버를 구축하는 방법은 여기에 포스팅해두었다. 학습용으로 만드는 토이 프로젝트가 아닌 이상 배포시에는 https를 당연히 지원해야 한다. 그래서 위에 app.js에는 http 접속을 https로 리다이렉트하는 미들웨어가 작성되어 있다.

이 방법이 완벽한 방법은 아닐 수 있다. 분명 더 세세하게 분리되어 있거나, 다른 좋은 방법으로 express 서버를 관리하는 방법도 많을 것이다. 중요한 것은 질 나쁜 코드를 작성하는 것을 끊임없이 지양하는 것에 있다고 생각한다.