์ƒ์„ธ ์ปจํ…์ธ 

๋ณธ๋ฌธ ์ œ๋ชฉ

[๋…ธ๋“œ 2ํŒ€] #15. AWS์™€ GCP๋กœ ๋ฐฐํฌํ•˜๊ธฐ

24-25/Node.js 2

by sksmsyena 2025. 1. 24. 10:00

๋ณธ๋ฌธ

728x90

๐ŸŒŸํ‚ค์›Œ๋“œ: ์„œ๋ฒ„, ๋ฐฐํฌ, ๊นƒํ—ˆ๋ธŒ, AWS, GCP


1. ์„œ๋น„์Šค ์šด์˜์„ ์œ„ํ•œ ํŒจํ‚ค์ง€

1. morgan๊ณผ express-session

- ๋ฏธ๋“ค์›จ์–ด๋“ค์„ ๋ฐฐํฌ์šฉ์„ ์„ค์ •ํ•œ๋‹ค.

//app.js
...
sequelize.sync({ force: false })
  .then(() => {
    console.log('๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์„ฑ๊ณต');
  })
  .catch((err) => {
    console.error(err);
  });

if (process.env.NODE_ENV === 'production') {
  app.enable('trust proxy');
  app.use(morgan('combined'));
  app.use(
    helmet({
      contentSecurityPolicy: false,
      crossOriginEmbedderPolicy: false,
      crossOriginResourcePolicy: false,
    }),
  );
  app.use(hpp());
} else {
  app.use(morgan('dev'));
}
app.use(express.static(path.join(__dirname, 'public')));
...

-> process.env.NODE_EW๋Š” ๋ฐฐํฌ ํ™˜๊ฒฝ์ธ์ง€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ธ์ง€๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜์ด๋‹ค.
->combined ๋ชจ๋“œ๋Š” dev ๋ชจ๋“œ์— ๋น„ํ•ด ๋” ๋งŽ์€ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋กœ๊ทธ๋กœ ๋‚จ๊ธฐ๋ฏ€๋กœ ์ถ”ํ›„ ๋ฒ„๊ทธ๋ฅผ ํ•ด๊ฒฐํ•  ๋•Œ ๋” ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
 
- express-session์„ ๋ฐฐํฌ์šฉ์œผ๋กœ ์„ค์ •ํ•œ๋‹ค.

//app.js
...
app.use(cookieParser(process.env.COOKIE_SECRET));
const sessionOption = {
  resave: false,
  saveUninitialized: false,
  secret: process.env.COOKIE_SECRET,
  cookie: {
    httpOnly: true,
    secure: false,
  },
  store: new RedisStore({ client: redisClient }),
};
if (process.env.NODE_ENV === 'production') {
  sessionOption.proxy = true;
  // sessionOption.cookie.secure = true;
}
app.use(session(sessionOption));
app.use(passport.initialize());
...

->๋ฐฐํฌ ํ™˜๊ฒฝ์ผ ๋•Œ๋Š” proxy์™€ cookie.secure๋ฅผ true๋กœ ๋ฐ”๊พผ๋‹ค.
 

2. ์‹œํ€„๋ผ์ด์ฆˆ

- ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋„ ๋ฐฐํฌ ํ™˜๊ฒฝ์œผ๋กœ ์„ค์ •ํ•œ๋‹ค.
- ์‹œํ€„๋ผ์ด์ฆˆ์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ•˜๋“œ ์ฝ”๋”ฉ ๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์— json์„ ์ง€์šฐ๊ณ  js๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

//config/config.js
require('dotenv').config();

module.exports = {
  development: {
    username: 'root',
    password: process.env.SEQUELIZE_PASSWORD,
    database: 'nodebird',
    host: '127.0.0.1',
    dialect: 'mysql',
  },
  test: {
    username: "root",
    password: process.env.SEQUELIZE_PASSWORD,
    database: "nodebird_test",
    host: "127.0.0.1",
    dialect: "mysql"
  },
  production: {
    username: 'root',
    password: process.env.SEQUELIZE_PASSWORD,
    database: 'nodebird',
    host: '127.0.0.1',
    dialect: 'mysql',
    logging: false,
  },
};

->JS ํŒŒ์ผ์ด๋ฏ€๋กœ dotenv ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
โ€ป username ์†์„ฑ์ด๋‚˜ host ์†์„ฑ์€ ๊ฐ๊ฐ ์•„์ด๋””์™€ DB์„œ๋ฒ„ ์ฃผ์†Œ ์—ญํ• ์„ ํ•˜๋ฏ€๋กœ ์ˆจ๊ธฐ๋Š” ๊ฒŒ ์ข‹๋‹ค.
โ€ป ๋ฐฐํฌ ํ™˜๊ฒฝ์—์„œ๋Š” ์–ด๋–ค ์ฟผ๋ฆฌ๊ฐ€ ์ˆ˜ํ–‰๋˜๋Š”์ง€ ์ˆจ๊ธฐ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

//.env
COOKIE_SECRET=nodebirdsecret
KAKAO_ID=5d4daf57becfd72fd9c919882552c4a6
SEQUELIZE_PASSWORD=nodejsbook
REDIS_HOST=redis-18954.c92.us-east-1-3.ec2.cloud.redislabs.com
REDIS_PORT=18954
REDIS_PASSWORD=JwTwGgKM4P0OFGStgQDgy2AcXvZjX4dc

->๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” .env ํŒŒ์ผ์— ์ž…๋ ฅํ•œ๋‹ค.
 

3. cross-env

- cross-env ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋™์ ์œผ๋กœ process.env(ํ™˜๊ฒฝ ๋ณ€์ˆ˜)๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

//package.json
{
  "name": "nodebird",
  "version": "0.0.1",
  "description": "์ต์Šคํ”„๋ ˆ์Šค๋กœ ๋งŒ๋“œ๋Š” SNS ์„œ๋น„์Šค",
  "main": "server.js",
  "scripts": {
    "start": "cross-env NODE_ENV=production PORT=80 pm2 start server.js -i 0",
    "dev": "nodemon server",
    "test": "jest"
  },
  ...

->์„œ๋ฒ„ ์‹คํ–‰์„ ์œ„ํ•œ npm ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‘ ๊ฐœ๋กœ ๋‚˜๋ˆˆ๋‹ค.
->npm start(๋ฐฐํฌ ํ™˜๊ฒฝ), npm run dev(๊ฐœ๋ฐœ ํ™˜๊ฒฝ) ์Šคํฌ๋ฆฝํŠธ์ด๋‹ค.
 

4. sanitize-html, csurf

- sanitize-html๊ณผ csurf ํŒจํ‚ค์ง€๋Š” ๊ฐ๊ฐ XSS, CSRF ๊ณต๊ฒฉ์„ ๋ง‰๊ธฐ ์œ„ํ•œ ํŒจํ‚ค์ง€์ด๋‹ค.
 
- XXS๋Š” ์•…์˜์ ์ธ ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ์ดํŠธ์— ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ๊ณต๊ฒฉ์ด๋‹ค.
->์•…์„ฑ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒŒ์‹œ๊ธ€์ด๋‚˜ ๋Œ“๊ธ€ ๋“ฑ์„ ์—…๋กœ๋“œํ•  ๋•Œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํฌํ•จ๋œ ํƒœ๊ทธ๋ฅผ ์˜ฌ๋ฆฌ๋ฉด, ๋‚˜์ค‘์— ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๊ฐ€ ๊ทธ ๊ฒŒ์‹œ๊ธ€์ด๋‚˜ ๋Œ“๊ธ€์„ ๋ณผ ๋•Œ ํ•ด๋‹น ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋˜์–ด ์˜ˆ๊ธฐ์น˜ ๋ชปํ•œ ๋™์ž‘์„ ํ•˜๊ฒŒ ๋œ๋‹ค. (์„œ๋ฒ„์—์„œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒŒ์‹œ๊ธ€์„ ์—…๋กœ๋“œํ•  ๋•Œ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌํ•ด์„œ, ์กด์žฌํ•œ๋‹ค๋ฉด ์ œ๊ฑฐํ•ด์•ผ ํ•œ๋‹ค.)

const sanitizeHtml=require('sanitize-html');

const html="<script>location.href='https://gilbut.co.kr'</script>";
console.log(sanitizeHtml(html));

->์‚ฌ์šฉ์ž๊ฐ€ ์—…๋กœ๋“œํ•œ HTML์„ sanitize-html ํ•จ์ˆ˜๋กœ ๊ฐ์‹ธ๋ฉด ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” ํƒœ๊ทธ๋‚˜ ์Šคํฌ๋ฆฝํŠธ๋Š” ์ œ๊ฑฐ๋œ๋‹ค. 
 
- CSRF๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์˜๋„์น˜ ์•Š๊ฒŒ ๊ณต๊ฒฉ์ž๊ฐ€ ์˜๋„ํ•œ ํ–‰๋™์„ ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ณต๊ฒฉ์ด๋‹ค.
-> ex) ํŠน์ • ํŽ˜์ด์ง€์— ๋ฐฉ๋ฌธํ•  ๋•Œ ์ €์ ˆ๋กœ ๋กœ๊ทธ์•„์›ƒ๋˜๊ฑฐ๋‚˜ ๊ฒŒ์‹œ๊ธ€์ด ์จ์ง€๋Š” ํ˜„์ƒ์„ ์œ ๋„ํ•  ์ˆ˜ ์žˆ๋‹ค.
->๊ณต๊ฒฉ์„ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ ๋‚ด๊ฐ€ ํ•œ ํ–‰๋™์ด ๋งž๋‹ค๋Š” ์‚ฌ์‹ค์„ ์ธ์ฆํ•ด์•ผ ํ•˜๊ณ , ์ด๋•Œ CSRF ํ† ํฐ์ด ์‚ฌ์šฉ๋œ๋‹ค.
โ€ป ์ƒํ™ฉ์— ๋”ฐ๋ผ CSRF ํ† ํฐ์„ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋‹ค๋ฅด๋ฏ€๋กœ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•œ๋‹ค.
 

5. pm2

- pm2๋Š” ์›ํ™œํ•œ ์„œ๋ฒ„ ์šด์˜์„ ์œ„ํ•œ ํŒจํ‚ค์ง€์ด๋‹ค.
- ๊ฐ€์žฅ ํฐ ๊ธฐ๋Šฅ์€ ์„œ๋ฒ„๊ฐ€ ์—๋Ÿฌ๋กœ ์ธํ•ด ๊บผ์กŒ์„ ๋•Œ ์„œ๋ฒ„๋ฅผ ๋‹ค์‹œ ์ผœ์ค€๋‹ค.
- ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์‹ฑ์„ ์ง€์›ํ•ด ๋…ธ๋“œ ํ”„๋กœ์„ธ์Šค ๊ฐœ์ˆ˜๋ฅผ ํ•œ ๊ฐœ ์ด์ƒ์œผ๋กœ ๋Š˜๋ฆด ์ˆ˜ ์žˆ๋‹ค.
ex) ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ์š”์ฒญ์ด ์˜ฌ ๋•Œ ์•Œ์•„์„œ ์š”์ฒญ์„ ์—ฌ๋Ÿฌ ๋…ธ๋“œ ํ”„๋กœ์„ธ์Šค์— ๊ณ ๋ฅด๊ฒŒ ๋ถ„๋ฐฐํ•œ๋‹ค.
- ๋‹จ์ ์œผ๋กœ๋Š” ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋”ฉ์ด ์•„๋‹ˆ๋ฏ€๋กœ ์„œ๋ฒ„์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ฐ™์€ ์ž์›์„ ๊ณต์œ ํ•˜์ง€๋Š” ๋ชปํ•œ๋‹ค. (๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜์ง€ ๋ชปํ•ด์„œ ํ”„๋กœ์„ธ์Šค ๊ฐ„์— ์„ธ์…˜์ด ๊ณต์œ ๋˜์ง€ ์•Š๊ฒŒ ๋œ๋‹ค.)
->์„ธ์…˜์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด ๋ฉค์ผ€์‹œ๋“œ(Memcached)๋‚˜ ๋ ˆ๋””์Šค(Redis) ๊ฐ™์€ ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•œ๋‹ค.
->npm i pm2(pm2 ์„ค์น˜)
-> nodemon ๋Œ€์‹  pm2๋ฅผ ์“ฐ๋„๋ก npm start ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค.

->node๋‚˜ nodemon ๋ช…๋ น์–ด์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ๋…ธ๋“œ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰๋œ ํ›„ ์ฝ˜์†”์— ๋‹ค๋ฅธ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.
 
- ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋Œ๊ณ  ์žˆ๋Š” ๋…ธ๋“œ ํ”„๋กœ์„ธ์Šค๋ฅผ ํ™•์ธํ•  ๋ฐฉ๋ฒ•: npx pm2 list

->ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค ์ •๋ณด ํ‘œ์‹œ
- pm2 ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ฝ˜์†”์— npx pm2 kill ์ž…๋ ฅํ•œ๋‹ค.
- ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด npx pn2 reload all์„ ์ž…๋ ฅํ•œ๋‹ค. (๋‹ค์šดํƒ€์ž„์ด ๊ฑฐ์˜ ์—†์ด ์„œ๋ฒ„๊ฐ€ ์žฌ์‹œ์ž‘๋˜์–ด ์ข‹๋‹ค.)
 
- ๋…ธ๋“œ์˜ cluster ๋ชจ๋“ˆ์ฒ˜๋Ÿผ ํด๋Ÿฌ์Šคํ„ฐ๋ง์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” pm2์˜ ํด๋Ÿฌ์Šคํ„ฐ๋ง ๋ชจ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

//package.json
{
  "name": "nodebird",
  "version": "0.0.1",
  "description": "์ต์Šคํ”„๋ ˆ์Šค๋กœ ๋งŒ๋“œ๋Š” SNS ์„œ๋น„์Šค",
  "main": "server.js",
  "scripts": {
    "start": "cross-env NODE_ENV=production PORT=80 pm2 start server.js -i 0",
    "dev": "nodemon server",
    "test": "jest"
  },

->pm2 start server.js ๋Œ€์‹  pm2 start server.js -i 0  ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. (0์€ ํ˜„์žฌ CPU ์ฝ”์–ด ๊ฐœ์ˆ˜๋งŒํผ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค)
โ€ป npx pm2 kill && npm start ์—ฐ๋‹ฌ์•„ ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๋‹ค.

 
- npx pm2 monit ๋กœ ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋ง ๊ฐ€๋Šฅํ•˜๋‹ค.

 
6. winston

- ์‹ค์ œ ์„œ๋ฒ„๋ฅผ ์šด์˜ํ•  ๋•Œ console.log์™€ console.error๋ฅผ ๋Œ€์ฒดํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋“ˆ์ด๋‹ค.
- console ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋“ค์€ ์–ธ์ œ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํŒŒ์•…ํ•˜๊ธฐ ํž˜๋“ค ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์„œ๋ฒ„๊ฐ€ ์ข…๋ฃŒ๋˜๋Š” ์ˆœ๊ฐ„ ๋กœ๊ทธ๋“ค๋„ ์‚ฌ๋ผ์ ธ๋ฒ„๋ฆฐ๋‹ค. -> ์ด์™€ ๊ฐ™์€ ์ƒํ™ฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋กœ๊ทธ๋ฅผ ํŒŒ์ผ์ด๋‚˜ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” winston์„ ์‚ฌ์šฉํ•œ๋‹ค.
npm i winston ์„ค์น˜

//logger.js
const { createLogger, format, transports } = require('winston');

const logger = createLogger({
  level: 'info',
  format: format.json(),
  transports: [
    new transports.File({ filename: 'combined.log' }),
    new transports.File({ filename: 'error.log', level: 'error' }),
  ],
});

if (process.env.NODE_ENV !== 'production') {
  logger.add(new transports.Console({ format: format.simple() }));
}

module.exports = logger;

->winston ํŒจํ‚ค์ง€์˜ createLogger ๋ฉ”์„œ๋“œ๋กœ logger๋ฅผ ๋งŒ๋“ ๋‹ค.
->์ธ์ˆ˜ logger์— ๋Œ€ํ•œ ์„ค์ •์œผ๋กœ level, format, transports ๋“ฑ์ด ์žˆ๋‹ค.
->logger ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋‹ค๋ฅธ ํŒŒ์ผ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. (info, warn, error ๋“ฑ์˜ ๋ฉ”์„œ๋“œ)
 
- ๊ฐœ๋ฐœ์šฉ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•œ ํ›„ http://localhost:8001/abcd์— ์ ‘์†ํ•˜๋ฉด 404 Not Found ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

->๋กœ๊ทธ๋ฅผ ์ฝ˜์†”์—๋งŒ ์ถœ๋ ฅํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํŒŒ์ผ๋กœ๋„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.
 
โ€ป winston-daily-rotate-file์ด๋ผ๋Š” ๋กœ๊ทธ๋ฅผ ๋‚ ์งœ๋ณ„๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํŒจํ‚ค์ง€๋„ ์žˆ๋‹ค. (๊ณต์‹ ๋ฌธ์„œ ์ฐธ์กฐ)
 

7. helmet, hpp

- ์„œ๋ฒ„์˜ ๊ฐ์ข… ์ทจ์•ฝ์ ์„ ๋ณด์™„ํ•ด์ฃผ๋Š” ํŒจํ‚ค์ง€์ด๋ฉฐ, ์ต์Šคํ”„๋ ˆ์Šค ๋ฏธ๋“ค์›จ์–ด๋กœ์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
npm i helmet hpp (๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋ฐฐํฌ ํ™˜๊ฒฝ์ผ ๋•Œ๋งŒ ์ ์šฉํ•˜๋ฉด ๋œ๋‹ค.)
โ€ป helmet๊ณผ hpp๊ฐ€ ๋ฐฉ์–ดํ•ด์ฃผ๋Š” ์ทจ์•ฝ์  ๋ชฉ๋ก์€ ๊ฐ๊ฐ์˜ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•œ๋‹ค. (๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฐฐํฌ ์ „์— ์ด ๋‘ ํŒจํ‚ค์ง€๋ฅผ ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.)

 
8. connect-redis

- ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ์„ธ์…˜ ๊ณต์œ ๋ฅผ ์œ„ํ•ด ๋ ˆ๋””์Šค์™€ ์ต์Šคํ”„๋ ˆ์Šค๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ํŒจํ‚ค์ง€์ด๋‹ค.
- ๊ธฐ์กด์—๋Š” ์„œ๋ฒ„๊ฐ€ ์ข…๋ฃŒ๋˜์–ด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋‚ ์•„๊ฐ€๋ฉด ์ ‘์†์ž๋“ค์˜ ๋กœ๊ทธ์ธ์ด ๋ชจ๋‘ ํ’€๋ ค๋ฒ„๋ฆฐ๋‹ค.
-> ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์„ธ์…˜ ์•„์ด๋””์™€ ์‹ค์ œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•œ๋‹ค.
npm i redis connect-redis  (๋ ˆ๋””์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด connect-redis ํŒจํ‚ค์ง€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ ˆ๋””์Šค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค.)
โ€ป ์ฑ… 694-698์„ ์ฐธ๊ณ ํ•ด ์„ค์น˜ํ•œ๋‹ค.
 

//app.js
...
const hpp = require('hpp');
const redis = require('redis');
const RedisStore = require('connect-redis').default;

dotenv.config();
const redisClient = redis.createClient({
  url: `redis://${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`,
  password: process.env.REDIS_PASSWORD,
});
redisClient.connect().catch(console.error);
const pageRouter = require('./routes/page');
...
const sessionOption = {
  resave: false,
  saveUninitialized: false,
  secret: process.env.COOKIE_SECRET,
  cookie: {
    httpOnly: true,
    secure: false,
  },
  store: new RedisStore({ client: redisClient }),
};
...

-> connect-redis ํŒจํ‚ค์ง€๋กœ๋ถ€ํ„ฐ RedisStore ๊ฐ์ฒด๋ฅผ requireํ•œ๋‹ค.
->redis ํŒจํ‚ค์ง€์˜ crateClient ๋ฉ”์„œ๋“œ๋กœ redisClient ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. 
->express-session ๋ฏธ๋“ค์›จ์–ด์—๋Š” store ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
 
=>์„ธ์…˜ ์ •๋ณด๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ๋Œ€์‹  ๋ ˆ๋””์Šค์— ์ €์žฅ๋˜์–ด ๋กœ๊ทธ์ธ ํ›„ ์„œ๋ฒ„๋ฅผ ๊ป๋‹ค ์ผœ๋„ ๋กœ๊ทธ์ธ์ด ์œ ์ง€๋œ๋‹ค.
 

9. nvm, n

- nvm-setup.zip์„ ๋‚ด๋ ค๋ฐ›์•„ ์••์ถ• ํ•ด์ œํ•œ ํ›„ ์‹คํ–‰์‹œ์ผœ ์„ค์น˜ํ•˜๋ฉด ์ฝ˜์†”์— nvm ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.
โ€ป ๋งฅ, ๋ฆฌ๋ˆ…์Šค์—์„œ๋Š” n ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
- ๋…ธ๋“œ ๋ฒ„์ „์„ ์—…๊ทธ๋ ˆ์ด๋“œ ํ•œ ํ›„ ๊ธฐ์กด npm ํŒจํ‚ค์ง€๋“ค์ด ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ npm rebuild ๋ช…๋ น์–ด๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
 

2. ๊นƒ๊ณผ ๊นƒํ—ˆ๋ธŒ ์‚ฌ์šฉํ•˜๊ธฐ

- AWS์™€ GCP์— ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์—…๋กœ๋“œ ํ•˜๊ธฐ ์œ„ํ•ด ๊นƒ์„ ์ด์šฉํ•œ๋‹ค.
- ๊นƒ(Git): ๋ถ„์‚ฐํ˜• ๋ฒ„์ „ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ / ๊นƒํ—ˆ๋ธŒ(GitHub): ๊นƒ์œผ๋กœ๋ถ€ํ„ฐ ์—…๋กœ๋“œํ•œ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์„œ๋ฒ„์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ์›๊ฒฉ ์ €์žฅ์†Œ์ด๋‹ค.
- ๊นƒ์€ ํ•˜๋‚˜์˜ ์ปดํ“จํ„ฐ์—์„œ ์ฝ”๋“œ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ๊นƒํ—ˆ๋ธŒ์— ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์—…๋กœ๋“œํ•˜๋ฉด ์—ฌ๋Ÿฌ ์‚ฌ๋žŒ์ด ์ฝ”๋“œ๋ฅผ ๊ณตํ†ต์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
 
1. ๊นƒ ์„ค์น˜ํ•˜๊ธฐ
- ๊นƒ์„ ์„ค์น˜ํ•œ ๋‹ค์Œ ์ œ๋Œ€๋กœ ์„ค์น˜๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ฝ˜์†”์— git --version ๋กœ ๊นƒ ๋ฒ„์ „์„ ํ™•์ธํ•œ๋‹ค.

//.gitignore
node_modules
uploads
*.log
coverage

 
โ€ป ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” .env ํŒŒ์ผ๋„ ๊นƒ์— ์ถ”๊ฐ€ํ•˜์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค. ๋ฐฐํฌ์šฉ ์„œ๋ฒ„์—์„œ ์ง์ ‘ .env ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด ๋น„๋ฐ€ ํ‚ค๋ฅผ ์ ์–ด์ฃผ๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค.
 

2. ๊นƒํ—ˆ๋ธŒ ์‚ฌ์šฉํ•˜๊ธฐ

- ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ ํ›„ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
- ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๊นƒ์— ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด git init -> git add . (๋ชจ๋“  ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ) -> git config --global user.email " "
-> git config --global user.name " " -> git commit -m "Initial commit" 
- ๋น„๋ฐ€๋ฒˆํ˜ธ ์šฉ๋„๋กœ ์‚ฌ์šฉํ•  ํ† ํฐ์„ ๋ฐœ๊ธ‰๋ฐ›์•„ git remote add origin https://์•„์ด๋””:ํ† ํฐ@github.com/์•„์ด๋””/node-deploy
- ํ›„ ๊นƒํ—ˆ๋ธŒ์— ์—…๋กœ๋“œ ํ•œ๋‹ค.
โ€ป ํ† ํฐ์€ ์ƒ์„ฑ ํ›„ ํ•œ ๋ฒˆ๋งŒ ๋ณด์—ฌ์ฃผ๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ ๋ณต์‚ฌํ•ด๋‘ฌ์•ผ ํ•œ๋‹ค. 
- git push origin master ๋กœ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๊นƒํ—ˆ๋ธŒ์— ์—…๋กœ๋“œํ•œ๋‹ค.
 

3. AWS ์‹œ์ž‘ํ•˜๊ธฐ

- ์„œ๋น„์Šค๋ฅผ ํด๋ผ์šฐ๋“œ์— ๋ฐฐํฌํ•œ๋‹ค. 
- AWS ๊ณ„์ •์„ ์ƒ์„ฑํ•˜๊ณ  ๊ฒฐ์ œ ์ •๋ณด๋ฅผ ๋“ฑ๋กํ•œ ํ›„ ํšŒ์› ๊ฐ€์ž…์„ ์™„๋ฃŒํ•œ๋‹ค. 
โ€ป ์ž์„ธํ•œ ๋ฐฉ๋ฒ•์€ 713-718p๋ฅผ ์ฐธ๊ณ ํ•œ๋‹ค.
 
- ๊ณ„์ •์„ ๋งŒ๋“  ํ›„ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
โ€ป ์ž์„ธํ•œ ๋ฐฉ๋ฒ•์€ 719-721p๋ฅผ ์ฐธ๊ณ ํ•œ๋‹ค.

โ€ป ์‹ค์Šต์„ ๋งˆ์น˜๊ณ  ๋‚˜๋ฉด, Delete ๋ฉ”๋‰ด์—์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ญ์ œํ•ด ์š”๊ธˆ์ด ๋ถ€๊ณผ๋˜์ง€ ์•Š๋„๋ก ํ•ด์•ผํ•œ๋‹ค.

 

4. AWS์— ๋ฐฐํฌํ•˜๊ธฐ

->์ธ์Šคํ„ด์Šค ํ™”๋ฉด์—์„œ Connect using SSH ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ๋‹ค.

->SSH๋Š” Lightsail ์ธ์Šคํ„ด์Šค์™€ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค.
- MySQL์„ ์„ค์น˜ํ•˜๊ณ  ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์„ค์ •ํ•œ๋‹ค. (์ฑ… 724-725p๋ฅผ ์ฐธ๊ณ ํ•œ๋‹ค)
- MySQL ์„ค์น˜๊ฐ€ ์™„๋ฃŒ๋œ ํ›„, git clone ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด ๊นƒํ—ˆ๋ธŒ์— ์˜ฌ๋ ธ๋˜ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋‚ด๋ ค ๋ฐ›๋Š”๋‹ค.
git clone https://github.com/์•„์ด๋””/node-deploy

- ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— Lightsail์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋น„ํŠธ๋‚˜๋ฏธ ์•„ํŒŒ์น˜ ์„œ๋ฒ„๊ฐ€ ์ผœ์ ธ์žˆ์–ด ๊บผ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
sudo ./ctlscript.sh stop apache
โ€ป ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด sudo pm2 logs --err ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•ด ์–ด๋–ค ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
 
- ํผ๋ธ”๋ฆญ IP๋ฅผ ํ˜ธ๊ฐ€์ธํ•ด ์‹ค์ œ๋กœ ์ ‘์†ํ•ด ๋ณธ๋‹ค. http://ํผ๋ธ”๋ฆญIP

5. GCP ์‹œ์ž‘ํ•˜๊ธฐ

- GCP ์ฝ˜์†” ์›น ์‚ฌ์ดํŠธ์— ์ ‘์†ํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ๊ตฌ๊ธ€ ๊ณ„์ •์œผ๋กœ ๊ฒฐ์ œ ๊ณ„์ •์„ ๋งŒ๋“ ๋‹ค. 
- ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“  ํ›„ ์™ธ๋ถ€ IP ์˜† SSH๋ฅผ ๋ˆ„๋ฅด๊ณ  ํ•ด๋‹น ์ธ์Šคํ„ด์Šค์˜ ์ฝ˜์†”๋กœ ์ ‘๊ทผํ•œ๋‹ค.
โ€ป ์ฑ… 729-735p ๋ฅผ ์ฐธ๊ณ ํ•ด ๊ณ„์ •๊ณผ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
 

 

6. GCP์— ๋ฐฐํฌํ•˜๊ธฐ

-SSH๊ฐ€ ๋ธŒ๋ผ์šฐ์ € ์ƒˆ ์ฐฝ์—์„œ ์‹คํ–‰๋˜๋ฉด SSH์— ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•œ๋‹ค. 
sudo su

->์šฐ๋ถ„ํˆฌ์™€ MySQL ์„ค์น˜๊ฐ€ ์™„๋ฃŒ๋œ ํ›„, ๊นƒํ—ˆ๋ธŒ์— ์˜ฌ๋ ธ๋˜ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋‚ด๋ ค ๋ฐ›๋Š”๋‹ค.

->node-deploy ํด๋”๋กœ ์ด๋™ํ•ด npm ํŒจํ‚ค์ง€๋“ค์„ ์„ค์น˜ํ•˜๊ณ  ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

->์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋˜์—ˆ์œผ๋‹ˆ ์‹ค์ œ๋กœ ์™ธ๋ถ€ IP๋กœ ์ ‘์†ํ•ด NodeBird ํ™”๋ฉด์ด ๋ณด์ด๋ฉด ์„ฑ๊ณต์ด๋‹ค.
โ€ป ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ ํ•˜๊ธฐ ์œ„ํ•ด์„  sudo npx pm2 reload all ์„ ์ž…๋ ฅํ•œ๋‹ค.


Quiz

1. (               )๋Š” ๋ฐฐํฌ ํ™˜๊ฒฝ์ธ์ง€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ธ์ง€๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜์ด๋‹ค.
2. (          ) ๋ชจ๋“œ๋Š” dev ๋ชจ๋“œ์— ๋น„ํ•ด ๋” ๋งŽ์€ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋กœ๊ทธ๋กœ ๋‚จ๊ธฐ๋ฏ€๋กœ ์ถ”ํ›„ ๋ฒ„๊ทธ๋ฅผ ํ•ด๊ฒฐํ•  ๋•Œ ๋” ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
3. ์‹œํ€„๋ผ์ด์ฆˆ์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ•˜๋“œ ์ฝ”๋”ฉ ๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์— json์„ ์ง€์šฐ๊ณ  (  )๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
4.  (    )๋Š” ์›ํ™œํ•œ ์„œ๋ฒ„ ์šด์˜์„ ์œ„ํ•œ ํŒจํ‚ค์ง€์ด๋‹ค.
5. ์‹ค์ œ ์„œ๋ฒ„๋ฅผ ์šด์˜ํ•  ๋•Œ console.log์™€ console.error๋ฅผ ๋Œ€์ฒดํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋“ˆ์€ (      )์ด๋‹ค.
6. ๊นƒ์œผ๋กœ๋ถ€ํ„ฐ ์—…๋กœ๋“œํ•œ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์„œ๋ฒ„์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ์›๊ฒฉ ์ €์žฅ์†Œ๋Š” (     )์ด๋‹ค.
7. ์„œ๋น„์Šค๋ฅผ ํด๋ผ์šฐ๋“œ์— ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•ด (     )์™€ (    )๋ฅผ ์ด์šฉํ•œ๋‹ค.
8. ๋ฏธ๋“ค์›จ์–ด๋“ค์„ ๋ฐฐํฌ์šฉ์œผ๋กœ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ๋นˆ์นธ์— ๋“ค์–ด๊ฐˆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์‹œ์˜ค.

//app.js
...
if ((1) === 'production') {  --- ๋ฐฐํฌ ํ™˜๊ฒฝ์ธ์ง€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ธ์ง€๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜
  app.enable('trust proxy');
  (2) --- ๋ฐฐํฌํ™˜๊ฒฝ์ผ ๋•Œ morgan์„ combined๋ชจ๋“œ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ
} else {
  app.use(morgan('dev'));
}
app.use(express.static(path.join(__dirname, 'public')));
...

9. XXS ๊ณต๊ฒฉ์„ ๋ง‰๊ธฐ ์œ„ํ•ด HTML์„ sanitize-html ํŒจํ‚ค์ง€์˜ ๋„์›€์„ ๋ฐ›์•„ ๋ง‰์•„์•ผ ํ•œ๋‹ค. ๋นˆ์นธ์— ๋“ค์–ด๊ฐˆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์‹œ์˜ค.

const sanitizeHtml=require('sanitize-html');

const html="<script>location.href='https://gilbut.co.kr'</script>";
(1) ---์‚ฌ์šฉ์ž๊ฐ€ ์—…๋กœ๋“œํ•œ HTML์„ sanitize-thml ํ•จ์ˆ˜๋กœ ๊ฐ์‹ผ๋‹ค.

Answer

1. process.env.NODE_EW
2. combined
3. js
4. pm2
5. winston
6. ๊นƒํ—ˆ๋ธŒ(GitHub)
7. AWS, GCP
8.

//app.js
...
if (process.env.NODE_ENV === 'production') {  --- (1)๋ฐฐํฌ ํ™˜๊ฒฝ์ธ์ง€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ธ์ง€๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜
  app.enable('trust proxy');
  app.use(morgan('combined')); --- (2)๋ฐฐํฌ ํ™˜๊ฒฝ์ผ ๋•Œ morgan์„ combined๋ชจ๋“œ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ
} else {
  app.use(morgan('dev'));
}
app.use(express.static(path.join(__dirname, 'public')));
...

9.

const sanitizeHtml=require('sanitize-html');

const html="<script>location.href='https://gilbut.co.kr'</script>";
console.log(sanitizeHtml(html));  ---(1)

 
์ถœ์ฒ˜) ์กฐํ˜„์ •, ใ€ŒNode.js ๊ต๊ณผ์„œ ๊ฐœ์ • 3ํŒใ€, ๊ธธ๋ฒ—(2022), 10์žฅ

 
Corner node.js 2ํŒ€

Editor : Igumi

728x90

๊ด€๋ จ๊ธ€ ๋”๋ณด๊ธฐ