Node JS 모듈 , fs readfile 모듈을 사용하여 과제를 풀어보자.
Node Js 비동기적으로 어떤일을 처리한다 <= 노드JS가 추구하는 철학.
Recap (서버 클라이언트 아직 안배우신분 패스!)
Client = 요청하는 주체 (API가 이 요청/응답을 정리해서 상호작용 매개, 이때 상호작용은 HTTP라는 통신규약 = 프로토콜 을 사용)
Server = 응답하는 주체
Node js = 자바스크립트 실행창, 실행환경 *상세설명 : Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임
Client가 Server 로부터 요청에 응답(데이터 받아온다던가)을 받아서 이를 보여줄때
비동기통신으로 데이터를 주고받는 기술인 AJAX(서버에 새로고침없이 요청을 할수있게 도와준다. AJAX를 쓰기위해 리액트를 씀. 리액트는 일종의 도구 ) 있다. 이때 Server는 자바스크립트 런타임이며, 비동기적으로 작동하는 Node.js를 사용해서 구축한다.
서버를 만들때 Node.js를 사용하려고하면, 이와 함께 번들링 되어있는 여러가지 모듈이 있으므로 require문법을 이용해서 불러서 사용할 수 있다. 대표적인 모듈 : fs, http, url, path ...
자바스크립트만 알면 클라이언트,서버 개발이 가능해진다. <= Node.js <=자바스크립트 런타임~!
코드가 늘어나면 정리정돈 배열 객체 함수 등으로 정돈을 해줄 수 있다.
객체를 정리정돈하는 도구 ? 모듈
이전에 과제를 했던 것에서 모듈을 사용했었는데 복습을 하는도중에 모듈에 대해서 정확히 모르고 넘어감을 인지했다.
과제의 예시를 통해 모듈에 대해서 알아보자.
const fs = require("fs");
const getDataFromFile = function (filePath, callback) {
// TODO: fs.readFile을 이용해 작성합니다
};
// getDataFromFile('README.md', (err, data) => console.log(data));
module.exports = {
getDataFromFile
};
module.exports 라고 쓰는것은 약속이다.
파일에 여러 기능이 있는데, getDataFromfile이 모듈 바깥에서 사용할 수 있도록 export하겠다.
이런의미이다.
모듈을 가져올때
require구문을 이용하여 가지고온다.
const fs = require('fs'); // 파일 시스템 모듈을 불러옵니다
const dns = require('dns'); // DNS 모듈을 불러옵니다
// 이제 fs.readFile 메소드 등을 사용할 수 있습니다!
모듈은 객체 정리도구여서 fs.readFile을 사용할 수있는것 <= 객체 밸류가져올때 obj={name:'sophie'} =>obj.name쓴것처럼
3rd-party 모듈사용
npm install을 통하여 설치한다.
여기서 의문점이 생겼다. express라는 프레임워크를 사용할때도 npm install, require을 통해 가져왓는데.. 프레임워크와 모듈?
그래서 차이점을 공부해보자.
라이브러리 (모듈과 비슷한개념) : 정보의 저장소 | 프레임워크 : 뼈대, 틀 |
부품이 되는 소프트웨어의 집합 | 소프트웨어의 형식을 제공 |
작은 기능들을 함수 단위로 만들어 제공 | 사용 규칙이 정해져있음 |
연장상자 -> 라이브러리 망치,톱,줄자 -> 함수 망치는 기능만 제공, 어떻게 사용할지는 사용자가 결정 |
이미 지어진 건물 -> 프레임 워크 일정한 규칙에 따라 집을 채워 나감 |
가져다 사용하는 개념 | 제공된 틀안으로 들어가 주어진 규칙을 지켜나가며 사용하는것 |
프로그래밍 개발과 개발자 사이에 플러그인,라이브러리,프레임워크를 서드파티로 볼 수 있다.
express도 서드파티의 일부이므로 다운받아서 사용했었던것.
파일을 다루는 모듈 (file system)
https://nodejs.org/dist/latest-v14.x/docs/api/fs.html#fs_fs_readfile_path_options_callback
File system | Node.js v14.18.2 Documentation
nodejs.org
fs.readfile을 보면
fs.readFile / fs.readFileSync 비동기식 사용 동기식 사용 두가지를 다 만나볼수있다.
const fs = require('fs'); // 파일 시스템 모듈을 불러옵니다
// 이제 fs.readFile 메소드 등을 사용할 수 있습니다!
코드 해석 : 모듈을 사용하기위해 require의 리턴값을 fs라는 변수에 담는다.
fs.readFile(path[, options], callback)
- 첫번째 인자 : path
예를들어보자.
data.txt라는 파일에 Hello bomba and comong을 쓰고 저장해보자.
이것을 fs.readFile모듈을 통하여 가져오려면
fs.readFile('data.txt'); <- 이렇게 사용
- 두번째 인자 : options
data.txt를 저장할때 'utf8' 이라는 방식으로 저장했다. 읽어올때도 이방식으로 읽어온다.
- 3번째 인자 :callback함수가 가져아할 방식(문법)이 공식문서에 나와있다.
fs.readFile('test.txt', 'utf8', (err, data) => {
if (err) {
throw err; // 에러를 던집니다.
}
console.log(data);
});
const fs = require('fs');
//Sync
console.log(1);
let data = fs.readFileSync('data.txt','utf8');
cohnsole.log(data);
//Async
console.log(2)
fs.readFile('data.txt','utf8',function(err,data){
console.log(data);
})
에러가 없으면 null 이 있다. 성공했으면 데이터의 값으로 data.txt의 내용을 전달하고 호출한다.
위의 코드는 콘솔로그에 찍힌게 비슷하게 보일지라도 세부적으로 들어가면 많이 다르다.
const fs = require('fs');
//Sync
console.log(1);
let data = fs.readFileSync('data.txt','utf8');
cohnsole.log(data);
//Async
console.log(2)
fs.readFile('data.txt','utf8',function(err,data){
console.log(3)
console.log(data);
})
console.log(4)
위의 Async를 실행해보면 2 4 3 Hello bomba and comong 이찍힌다.
2 => 4 => 파일이 읽는작업이끝난이후에 readFile에 콜백으로 전달한 익명함수를 readFile이 실행해서 두번째 인자의값인 Hello bomba and comong 을 전달하고 그때서야 3이 실행 => 마지막 Hello bomba and comong이 실행
이제 과제로 돌아가 응용을 해보자.
모두 풀어본 과제니. 내가 주의해야할 점은?
promiseall로 많은 요청을 한번에 전달해야되는데 await로 하나씩 전달해서 오래걸린 사례가 있고한다.
await이 가장 신문법이며, 자주사용한다고해서 최고의 방법은 아니라는 말이다.
상황에 따라 promise all로 데이터를 모두 받아와서 한번에 처리해야할 경우도있음을 생각하고 적절히 사용하자.
일차적으로 fs.readFile을 사용해야하니, fs.readFile문법을 사용하여 불러온다.
위에서 언급한것과 같이 콜백함수에 들어가야할 문법을 참고해보자
fs.readFile('test.txt', 'utf8', (err, data) => {
if (err) {
throw err; // 에러를 던집니다.
}
console.log(data);
});
01_callback.js
fs.readFile(filePath,'utf-8',(err,data))
에러이면? err가 뜨고 데이터는 null 이 되어야하며
아니면? err는 null이 되고 data가 나오도록
const fs = require("fs");
const getDataFromFile = function (filePath, callback) {
// TODO: fs.readFile을 이용해 작성합니다
// getDataFromFile('README.md', (err, data) => console.log(data));
module.exports = {
getDataFromFile
};
-------------------------------
const fs = require("fs");
const getDataFromFile = function (filePath, callback) {
// TODO: fs.readFile을 이용해 작성합니다
fs.readFile(filePath, "utf-8",(err,data)=>{
if(err){
callback(err,null)
}else{
callback(null,data)
}
})
};
// getDataFromFile('README.md', (err, data) => console.log(data));
module.exports = {
getDataFromFile
};
아래 주석을 제거하면, data값이 나와야하기에 'README.md' 파일에 있는 => ##promiseAll이 리턴된다.
02_promiseConstructor
const fs = require("fs");
const getDataFromFilePromise = filePath => {
return new Promise() => {
};
// getDataFromFilePromise('README.md').then(data => console.log(data));
module.exports = {
getDataFromFilePromise
};
---------------------------
const fs = require("fs");
const getDataFromFilePromise = filePath => {
return new Promise((resolve, reject) => {
fs.readFile(filePath, "utf8", (에러, 결과) => {
if (에러) {
reject(에러);
} else {
resolve(결과);
}
});
});
};
// getDataFromFilePromise('README.md').then(data => console.log(data));
module.exports = {
getDataFromFilePromise
};
예전에 Promise개념에서 봤듯이
프로미스를 쓸때는 문법이 필요하다.
resolve,reject 성공실패 판정기계이다.
그렇기에 resolve,reject를 사용해서 구현해줘야한다.
(err,data)말고 다른값을 넣어도되나 확인해봤다. 상관이 없었다.
03
const path = require('path');
const { getDataFromFilePromise } = require('./02_promiseConstructor');
const user1Path = path.join(__dirname, 'files/user1.json');
const user2Path = path.join(__dirname, 'files/user2.json');
// HINT: getDataFromFilePromise(user1Path) 맟 getDataFromFilePromise(user2Path) 를 이용해 작성합니다
const readAllUsersChaining = () => {
return getDataFromFilePromise(user1Path)
.then((user1) => {
console.log(user1)
return getDataFromFilePromise(user2Path).then((user2) => {
console.log(user2)
return '[' + user1 + ',' + user2 + ']';
});
})
.then((text) => JSON.parse(text));
};
readAllUsersChaining().then(data=>console.log(data))
module.exports = {
readAllUsersChaining
파일을 통해서 확인해 볼수도 있지만 콘솔을 찍어보면 json화 되어있는것을 볼수있다.
파일 읽기의 결과가 문자열이므로 이것을 json.parse()를 통해서 일반 객체로 만들어줘야한다. 그래서 json.text를 하는것
여기서 중요한점. readAlluserChaining은 프로미스를 뱉어내기에, 결과값을 볼수없다.
결과값을 보고싶으면 readAlluserChaining().then(data=console.log(data))를 해야만 볼수있다.
04_promiseAll
mdn을 통하여 우선 어떻게 쓰이는지 알아야한다.
const { resolve } = require('path');
const path = require('path');
const { promise } = require('sinon');
const { getDataFromFilePromise } = require('./02_promiseConstructor');
const user1Path = path.join(__dirname, 'files/user1.json');
const user2Path = path.join(__dirname, 'files/user2.json');
const readAllUsers = () => {
// const user1 =getDataFromFilePromise(user1Path).then((결과)=>JSON.parse(결과))
// const user2 =getDataFromFilePromise(user2Path).then((결과)=>JSON.parse(결과))
// // TODO: Promise.all을 이용해 작성합니다
// return Promise.all([user1,user2]).then((결과)=>{ //결과=내용을 받아온 변수일뿐
// return 결과
// })
const user1 = getDataFromFilePromise(user1Path)
const user2 = getDataFromFilePromise(user2Path)
return Promise.all([user1,user2])
.then(texts => {
return texts.map((text) => JSON.parse(text))
})
}
readAllUsers()
module.exports = {
readAllUsers
}
위 아래 두가지 방법이있다.
위는 조금 직관적인? 아래는 map을 사용해서 보여주는. 두가지 다 사용이 가능하다!
아래가 좀더 간지나지않나?ㅋㅋㅋㅋ
05_asyncAwait.js
const { json } = require('express');
const path = require('path');
const { getDataFromFilePromise } = require('./02_promiseConstructor');
const user1Path = path.join(__dirname, 'files/user1.json');
const user2Path = path.join(__dirname, 'files/user2.json');
const readAllUsersAsyncAwait = async () => {
// TODO: async/await 키워드를 이용해 작성합니
const user1 = await getDataFromFilePromise(user1Path)
const user2 = await getDataFromFilePromise(user2Path)
return [user1,user2].map((text)=>{ return JSON.parse(text)})
}
// readAllUsersAsyncAwait();
module.exports = {
readAllUsersAsyncAwait
}