KDT/TIL

8/31 TIL : File-system(JS, Promise, async/await), Routing w/o framework

ebulsok 2022. 9. 18. 18:21

๐Ÿ”Ž File-system

  • node์˜ ๋ชจ๋“ˆ์ด๊ธฐ ๋•Œ๋ฌธ์— CommonJS ๋ฐฉ์‹์œผ๋กœ ๋ถˆ๋Ÿฌ ์˜ฌ ์ˆ˜ ์žˆ์Œ
  • const fs = require('fs');

 

๐Ÿšฉ ํŒŒ์ผ์„ ์ฝ์„ ๋•Œ๋Š” readFile๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉ

  • ํŒŒ์ผ์„ ์ฝ๋Š” ๊ฒƒ์€ ์‹œ๊ฐ„์ด ํ•„์š”ํ•œ ์ž‘์—…์ด๊ธฐ ๋•Œ๋ฌธ์— ์ผ์ข…์˜ ์„œ๋ฒ„ ํ†ต์‹ ๊ณผ ๋น„์Šทํ•จ => ๋”ฐ๋ผ์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ callback์„ ์‚ฌ์šฉ
  • fs.readfile('ํŒŒ์ผ์œ„์น˜', '์œ ๋‹ˆ์ฝ”๋“œํฌ๋งท', callback(err, data) { });
  • err๋Š” ํŒŒ์ผ ์ฝ๊ธฐ๋ฅผ ์‹คํŒจํ–ˆ์„ ๋•Œ Error ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜
  • data๋Š” ํŒŒ์ผ ์ฝ๊ธฐ๋ฅผ ์„ฑ๊ณตํ–ˆ์„ ๋•Œ ์ฝ์€ data๋ฅผ ๋ฐ˜ํ™˜
const fs = require('fs');
fs.readFile('./readme.txt', 'utf-8', (err, data) => {
  if (err) console.log(err);
  else console.log(data);
});

 

๐Ÿšฉ ํŒŒ์ผ์„ ์ฝ์„ ๋•Œ๋Š” writeFile ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉ

  • fs.writeFile('ํŒŒ์ผ์œ„์น˜', data, '์œ ๋‹ˆ์ฝ”๋“œํฌ๋งท', callback(err) { } );
  • data๊ฐ€ ์—†๊ณ , ์“ฐ๊ธฐ๋ฅผ ์‹คํŒจํ–ˆ์„ ๋•Œ Error ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜
  • ํŒŒ์ผ์„ ์ดˆ๊ธฐํ™”ํ•œ ํ›„ ๋ฎ์–ด์”Œ์šฐ๋Š” ๊ฐœ๋…
const fs = require('fs');
const str = 'ํŒŒ์ผ ์“ฐ๊ธฐ๋ฅผ ์„ฑ๊ณตํ•˜๋ฉด ์ด ๋ฌธ๊ตฌ๊ฐ€ ํŒŒ์ผ์— ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค.';
fs.writeFile('./readme.txt', str, 'utf-8', (err) => {
  if (err) console.log(err);
  else console.log('writeFile succeed');
});

 

callback์€ ๊ฐ€๋…์„ฑ ์•ˆ ์ข‹๊ธฐ ๋•Œ๋ฌธ์— promise๋ผ๋Š” ๊ฐœ๋…์ด ๋‚˜์™”๊ณ  ๊ทธ ์ดํ›„ syntatic sugar(async, await)

 

๐Ÿšฉ File-system๊ณผ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

readFile ๋ฉ”์†Œ๋“œ๋ฅผ ๋™์‹œ์— ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰์‹œ์ผœ๋ณด๊ธฐ

fs.readFile('./readme.txt', 'utf-8', function (err, data) {
  if (err) throw err;
  console.log('1๋ฒˆ', data.toString());
});
fs.readFile('./readme.txt', 'utf-8', function (err, data) {
  if (err) throw err;
  console.log('2๋ฒˆ', data.toString());
});
fs.readFile('./readme.txt', 'utf-8', function (err, data) {
  if (err) throw err;
  console.log('3๋ฒˆ', data.toString());
});
fs.readFile('./readme.txt', 'utf-8', function (err, data) {
  if (err) throw err;
  console.log('4๋ฒˆ', data.toString());
});
  • callback ๋ฐฉ์‹์€ ์‚ฌ์šฉ ๋ฐ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— fs๋Š” promise ๋ฐฉ์‹ ๊ธฐ๋Šฅ์„ ์ œ๊ณต
  • ๊ธฐ์กด์˜ ๋ฉ”์†Œ๋“œ ๋ช… ๋’ค์— Sync๋ฅผ ๋ถ™์ด๋ฉด promise ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™๊ธฐ์ ์œผ๋กœ ์ž‘๋™
  • ์„œ๋ฒ„ ์ƒ์—์„œ ์ž‘๋™ํ•  ๋•Œ๋Š” ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋นˆ๋ฒˆํ•จ
let data = fs.readFileSync('./readme.txt');
console.log('1๋ฒˆ', data.toString());
data = fs.readFileSync('./readme.txt');
console.log('2๋ฒˆ', data.toString());
data = fs.readFileSync('./readme.txt');
console.log('3๋ฒˆ', data.toString());
data = fs.readFileSync('./readme.txt');
console.log('4๋ฒˆ', data.toString());
  • ๋”ฐ๋ผ์„œ ์„œ๋ฒ„ ์ƒ์—์„œ fs๋ฅผ ์“ธ ๋•Œ๋Š” Promise๋ฅผ ์จ์•ผ ํ•จ
  • Promise์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š” fs.promises๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉ
  • fs.promises๋Š” ์Šค์Šค๋กœ ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•˜์—ฌ resolve, reject๋ฅผ ๋ฐ˜ํ™˜ํ•จ

 

๐Ÿšฉ Promise

  • ์ผ์ข…์˜ ํด๋ž˜์Šค, ๋”ฐ๋ผ์„œ new๋กœ ์‚ฌ์šฉ
  • const promise = new Promise(function(resolve, reject) { });
  • resolve, reject๋ผ๋Š” 2๊ฐœ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ callback ํ•จ์ˆ˜๋กœ ๋ฐ›์•„์„œ ์‚ฌ์šฉ, ๋ฐ์ดํ„ฐ๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Œ
  • promise๊ฐ€ ํ• ๋‹น๋˜๋ฉด resolve/reject๊ฐ€ callback ๋  ๋•Œ๊นŒ์ง€ ๋ฌดํ•œ ๋Œ€๊ธฐ(pending ์ƒํƒœ)
  • resolve๋Š” then์œผ๋กœ, reject๋Š” catch๋กœ ๋ฐ›์Œ
const promise = new Promise((resolve, reject) => {
  const name = 'bulsok';
  if (name === 'bulsok') {
    setTimeout(() => {
      resolve('My name is bulsok');
    }, 3000);
  } else {
    reject('My name is not bulsok');
  }
});
promise
  .then((data) => {
    console.log(data);
  })
  .catch((err) => {
    console.log(err);
  });
const fs = require('fs').promises;
fs.readFile('./readme.txt', 'utf-8')
  .then((data) => {
    console.log('1๋ฒˆ', data.toString());
    return fs.readFile('./readme.txt', 'utf-8');
  })
  .then((data) => {
    console.log('2๋ฒˆ', data.toString());
    return fs.readFile('./readme.txt', 'utf-8');
  })
  .then((data) => {
    console.log('3๋ฒˆ', data.toString());
    return fs.readFile('./readme.txt', 'utf-8');
  })
  .then((data) => {
    console.log('4๋ฒˆ', data.toString());
  })
  .catch((err) => {
    throw err;
  });

 

  • function ์•ž์— async๋ฅผ ๋ถ™์ด๋ฉด ํ•ญ์ƒ Promise๋ฅผ ๋ฐ˜ํ™˜, await ํ‚ค์›Œ๋“œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • await๋Š” promise๊ฐ€ ๊ฒฐ๊ณผ(resolve, reject)๋ฅผ ๊ฐ€์ ธ๋‹ค ์ค„ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆผ
  • ํ•จ์ˆ˜๋ฅผ ์„ ์–ธ๋งŒ ํ•˜๊ณ  ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๋ฅผ ์ฃผ์˜ํ•  ๊ฒƒ
const fs = require('fs').promises;
async function main() {
  let data = await fs.readFile('./readme.txt', 'utf-8');
  console.log('1๋ฒˆ', data);
  data = await fs.readFile('./readme.txt', 'utf-8');
  console.log('2๋ฒˆ', data);
  data = await fs.readFile('./readme.txt', 'utf-8');
  console.log('3๋ฒˆ', data);
  data = await fs.readFile('./readme.txt', 'utf-8');
  console.log('4๋ฒˆ', data);
}
main();

 

๐Ÿ”Ž Routing: ๋„คํŠธ์›Œํฌ ์ƒ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ๋•Œ ์ตœ์ ์˜ ๊ฒฝ๋กœ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ณผ์ •

route.js

// @ts-check
const fs = require('fs').promises;

async function getPosts() {
  const jsonPosts = await fs.readFile('database.json', 'utf-8');
  return JSON.parse(jsonPosts).posts;
}

async function savePosts(posts) {
  const content = {
    posts,
  };
  return fs.writeFile('database.json', JSON.stringify(content), 'utf-8');
}

const routes = [
  // ๋ธ”๋กœ๊ทธ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ค๋Š” API
  {
    url: '/posts',
    id: 'undefined',
    method: 'GET',
    callback: async () => {
      const posts = await getPosts();

      return {
        statusCode: 200,
        body: {
          posts,
          totalCount: posts.length,
        },
      };
    },
  },

  // ํŠน์ • ID์˜ ๋ธ”๋กœ๊ทธ ๊ธ€์„ ๊ฐ€์ ธ์˜ค๋Š” API
  {
    url: '/posts',
    id: 'number',
    method: 'GET',
    callback: async (id) => {
      const posts = await getPosts();

      if (!id) {
        return {
          statusCode: 404,
          body: 'NOT FOUND',
        };
      }

      const result = posts.find((post) => post.id === id);
      if (!result) {
        return {
          statusCode: 404,
          body: 'ID NOT FOUND',
        };
      }

      return {
        statusCode: 200,
        body: result,
      };
    },
  },

  // ์ƒˆ๋กœ์šด ๊ธ€์„ ์˜ฌ๋ฆฌ๋Š” API
  {
    url: '/posts',
    id: 'undefined',
    method: 'POST',
    callback: async (id, newPost) => {
      const posts = await getPosts();

      posts.push({
        id: posts[posts.length - 1].id + 1,
        title: newPost.title,
        content: newPost.content,
      });

      savePosts(posts);

      return {
        statusCode: 200,
        body: '์ƒˆ๋กœ์šด ๊ธ€์ด ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
      };
    },
  },

  // ํŠน์ • ID์˜ ๊ธ€์„ ์ˆ˜์ •ํ•˜๋Š” API
  {
    url: '/posts',
    id: 'number',
    method: 'PUT',
    callback: async (id, modiPost) => {
      const posts = await getPosts();

      if (!id) {
        return {
          statusCode: 404,
          body: 'NOT FOUND',
        };
      }

      const result = posts.find((post) => post.id === id);
      if (!result) {
        return {
          statusCode: 404,
          body: 'ID NOT FOUND',
        };
      }

      posts[id - 1].title = modiPost.title;
      posts[id - 1].content = modiPost.content;

      savePosts(posts);

      return {
        statusCode: 200,
        body: '๊ธ€์ด ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
      };
    },
  },

  // ํŠน์ • ID์˜ ๊ธ€์„ ์‚ญ์ œํ•˜๋Š” API
  {
    url: '/posts',
    id: 'number',
    method: 'DELETE',
    callback: async (id) => {
      const posts = await getPosts();

      if (!id) {
        return {
          statusCode: 404,
          body: 'NOT FOUND',
        };
      }

      const result = posts.find((post) => post.id === id);
      if (!result) {
        return {
          statusCode: 404,
          body: 'ID NOT FOUND',
        };
      }

      posts.splice(id - 1, 1);
      savePosts(posts);

      return {
        statusCode: 200,
        body: '๊ธ€์ด ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
      };
    },
  },
];

module.exports = {
  routes,
};

main.js

// @ts-check
const http = require('http');
const { routes } = require('./route.js');

// ์„œ๋ฒ„ ์ •์˜
const server = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'application/json; charset=utf-8');

  const urlArr = req.url ? req.url.split('/') : [];
  let id;
  if (urlArr.length > 2) id = parseInt(urlArr[2], 10);
  else id = undefined;

  async function main() {
    const route = routes.find(
      (_route) =>
        req.url &&
        req.method &&
        '/' + urlArr[1] === _route.url &&
        // req.url.search(_route.url) !== -1 &&
        _route.method === req.method &&
        typeof id === _route.id
    );

    if (!route) {
      console.log('ํ•ด๋‹น API๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.');
      res.statusCode = 404;
      res.end('NOT FOUND');
    } else {
      let newPost;
      if (req.method === 'POST' || req.method === 'PUT') {
        newPost = await new Promise((resolve, reject) => {
          req.setEncoding('utf-8');
          req.on('data', (data) => {
            if (data !== undefined) {
              resolve(JSON.parse(data));
            } else {
              reject();
            }
          });
        });
      }

      const result = await route.callback(id, newPost);

      console.log(result.body);
      res.statusCode = result.statusCode;
      res.end(JSON.stringify(result.body));
    }
  }

  main();
});

// ์„œ๋ฒ„ ์‹คํ–‰
const PORT = 4000;
server.listen(PORT, () => {
  console.log(`ํ•ด๋‹น ์„œ๋ฒ„๋Š” ${PORT}๋ฒˆ ํฌํŠธ์—์„œ ์ž‘๋™ ์ค‘์ž…๋‹ˆ๋‹ค.`); // ์„œ๋ฒ„ ์ฝ˜์†”
});

database.json

{"posts":[{"content":"hello, back-end","id":1,"title":"first"},{"content":"hello, back-end","id":2,"title":"second"},{"content":"hello, back-end","id":3,"title":"third"}]}