비동기를 동기로 바꾸는 방법
- Callback 함수
- Promise
- async / await
Callback
순차적으로 함수를 실행하고 싶을 때 함수 내부에서 함수를 부르는 것
좀더 안정적, 순차적으로 실행을 보장 받을 수 있음
매개변수로 값만 전달하는 것이 아닌 함수를 전달해보자는 아이디어 아래,
- 기존처럼 함수 내부에서 다른 함수를 호출하는 것
- 상황에 맞게 함수를 변경하여 호출하는 것
- 호출하는 수준을 넘어 그 자리에서 바로 정의해서 사용하는 것
이 가능해진다.
function multiplication(num, cb) {
let ans = 0;
setTimeout(function() {
ans = num * num;
cb(ans);
}, 1000);
}
function consoleLog(result) {
console.log(result);
}
multiplication(3, consoleLog); // 9
결과값을 콜백함수의 인자로 전달해주는 등 사용이 가능
콜백 지옥
function funcHell(cb) {
cb();
}
funcHell(function () {
console.log('1번인척 하는 새로 만든 익명 함수');
funcHell(function () {
console.log('2번인척 하는 새로 만든 익명 함수');
funcHell(function () {
console.log('3번인척 하는 새로 만든 익명 함수');
});
});
});
File System
const fs = require('fs');
node.js에서 파일을 읽을 때는 readFile 메서드를 사용함
fs.readFile('Path', 'UTF', callback(err, data)) 로, 매개변수는 1.파일위치 2.유니코드포맷 3.콜백함수를 받는다.
콜백함수의 첫번째 매개변수로는 error 코드를 반환, 두번째 매개변수는 data는 파일 읽기가 잘 되었을 때 읽은 data를 반환
const fs = require('fs');
const path = './test.txt';
fs.readFile(path, 'utf-8', (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
파일을 쓰고자 한다면 fs.writeFile() 메서드를 사용
const str = '파일 쓰기 테스트';
fs.writeFile('./test2.txt', str, 'utf-8', (err) => {
if (err) console.log(err);
});
fs.writeFile(쓰고자하는파일명, 쓸내용, 유니코드포맷, 콜백함수) 4가지 매개변수를 받으며,
콜백함수의 경우 파일 쓰기가 실패했을 경우 받아주는 err 인자만 존재한다.
File System과 비동기 프로그래밍
JS의 비동기적인 특성으로 readFile 메서드를 동시에 여러 개 실행시키면 순서를 보장할 수 없다. (각각의 스레드의 처리속도가 다르다)
const fs = require('fs');
// 순서 보장이 안됨
fs.readFile('./test.txt', 'utf-8', (err, data) => {
if (err) return console.log(err);
return console.log('1번', data.toString());
});
fs.readFile('./test.txt', 'utf-8', (err, data) => {
if (err) return console.log(err);
return console.log('2번', data.toString());
});
fs.readFile('./test.txt', 'utf-8', (err, data) => {
if (err) return console.log(err);
return console.log('3번', data.toString());
});
fs.readFile('./test.txt', 'utf-8', (err, data) => {
if (err) return console.log(err);
return console.log('4번', data.toString());
});
const fs = require('fs');
// 순서 보장이 됨 -> 다만 콜백지옥에 빠지게 됨
fs.readFile('./test.txt', 'utf-8', (err, data) => {
if (err) return console.log(err);
console.log('1번', data.toString());
fs.readFile('./test.txt', 'utf-8', (err, data) => {
if (err) return console.log(err);
console.log('2번', data.toString());
fs.readFile('./test.txt', 'utf-8', (err, data) => {
if (err) return console.log(err);
console.log('3번', data.toString());
fs.readFile('./test.txt', 'utf-8', (err, data) => {
if (err) return console.log(err);
console.log('4번', data.toString());
});
});
});
});
Promise
생성자이므로 new 로 생성
promise 가 할당되면 resolve 또는 reject 함수가 콜백될 때까지 대기하게 된다.
1. resolve는 promise가 정상적으로 이행되었을 경우
2. reject는 비정상적으로 이행되었을 경우
const promise = new Promise(function(resolve, reject) { });
resolve는 추후에 then() 으로 받으며
reject는 catch()로 받는다. resolve, reject는 데이터를 매개변수로 보낼 수 있다.
* Promise 는 해당 콜백이 나올 때까지 pending 상태가 되어 무한 대기하게 된다.
Promise의 3가지 상태
- Pending (대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
- new Promise() 를 호출하여 생성하면 기본적으로 대기 상태가 된다.
- Fulfilled (이행) : 비동기 처리가 완료되어 프로미스가 결과값을 반환해준 상태 -> resolve
- Rejected (실패) : 비동기 처리가 실패하거나 오류가 발생한 상태 -> reject
File System을 Promise로 변경하기
fs.promises 를 사용
const fs = require('fs').promises;
readFil() 메서드 사용 시, 마지막 인자를 콜백함수 넣어줄 필요 없이 바로 then()과 catch() 를 사용 가능함
파일 읽기가 성공하면 resolve / 실패하면 reject를 반환
const fs = require('fs').promises;
fs.readFile('./test.txt', 'utf-8')
.then((data) => {
console.log('1번', data);
return fs.readFile('./test.txt', 'utf-8');
})
.then((data) => {
console.log('2번', data);
return fs.readFile('./test.txt', 'utf-8');
})
.then((data) => {
console.log('3번', data);
return fs.readFile('./test.txt', 'utf-8');
})
.then((data) => {
console.log('4번', data);
return fs.readFile('./test.txt', 'utf-8');
})
.catch((err) => {
console.log(err);
throw err;
});
Promise 를 사용하여 콜백지옥을 순화시켰다.
Async / Await
function 앞에 async를 붙이면 해당 함수는 항상 Promise를 반환해준다.
async function f1() {
return 1;
//return Promise.resolve(1); 위와 동일한 의미임
}
await는 promise가 결과(resolve, reject)를 줄 때까지 기다리게 해준다.
다만 async는 함수를 정의하는 상황에서 쓰이므로 함수 정의 후 해당 함수를 외부에서 한번 사용해줘야 한다.
const lucky = false;
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (lucky) {
const profit = 3000;
resolve(profit);
} else {
reject('망했어요');
}
}, 1000);
});
/*
Promise 를 사용했다면,
promise
.then((profit) => {
console.log(`${profit}`);
})
.catch((err) => {
console.log(err);
});
*/
async function howMuch() {
try {
const result = await promise;
if (result) console.log(result);
} catch (err) {
console.log(err);
}
}
howMuch();
File System 을 async/await 로 읽어오기
const fs = require('fs').promises;
async function main() {
let result = await fs.readFile('./test2.txt', 'utf-8');
console.log('1', result);
result = await fs.readFile('./test2.txt', 'utf-8');
console.log('2', result);
result = await fs.readFile('./test2.txt', 'utf-8');
console.log('3', result);
result = await fs.readFile('./test2.txt', 'utf-8');
console.log('4', result);
}
main();
express.js
Postman
https://www.postman.com/downloads/
서버 테스트를 하는 프로그램 (백엔드 서버 테스트 시 사용)
GET 방식의 localhost:포트번호 로 send 하면 백엔드와 통신이 가능하다 (작일 프론트에서 받을 때 html을 작성해야 됐지만 이제는 그러지 않아도 됨)
다만 node로 실행한 서버는 수정사항이 생길 때마다 ctrl+c 로 서버를 꺼주고 다시 켜줘야 변경된 사항을 확인할 수 있는데,
nodemon을 사용하면 서버 코드의 변동사항이 생기면 서버를 재구동해준다!
npm i -g nodemon 으로 설치 (-g 는 폴더가 아닌, 컴퓨터에 설치받는 글로벌 옵션)
node 파일명.js 가 아닌, nodemon 파일명.js 으로 서버를 구동해준다.
Middleware 란?
서로 다른 어플리케이션이 서로 통신을 하는데 사용되는 소프트웨어
양쪽 어플리케이션 가운데에서 역할을 하는 소프트웨어
Express 에서의 Middleware는 서버에 들어온 요청이 들어와서 응답으로 나갈 때까지 거치는 모든 함수 또는 기능을 의미
express에선 app.use 또는 app.method 함수를 이용하여 미들웨어를 사용해준다.
app.use('요청주소', (req, res, next) => { });
요청 주소의 경우, 기본적으로 http://localhost:포트번호 형태이며, 요청 주소에 적어준 내용이 추가된다.
ex. app.use('/api', .......) 의 경우 http://localhost:4000/api 가 서버 주소가 되는 것임
매개변수 3개 순서대로 (최대 4개)
1. req 는 요청
2. res 는 응답
3. next 는 해당 함수가 호출되면 현재 미들웨어를 종료하고 다음 미들웨어를 실행시키는 콜백함수임
// @ts-check
const express = require('express');
const cors = require('cors');
const PORT = 4000;
const app = express();
app.use(cors());
app.use('/', (req, res, next) => {
console.log('미들웨어 1');
next(); // 미들웨어 2로 이동
});
app.use((req, res, next) => {
console.log('미들웨어 2');
next(); // 미들웨어 3로 이동
});
app.use((req, res, next) => {
console.log('미들웨어 3');
});
app.listen(PORT, () => {
console.log(`PORT NUMBER : ${PORT}`);
});
** res.send() 를 여러개 두면
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
오류가 발생함. 이럴 땐 res.send()를 하나만 보내준다.
마지막에는 꼭 listen() 을 사용하여 서버를 켜줘야 한다.
app.listen(포트번호, 서버가 정상적으로 실행될 때의 콜백함수);
CRUD
요청 메서드
1. Create ➡️ POST
2. Read ➡️ GET
3. Update ➡️ PUT / PATCH
4. Delete ➡️ DELETE
HTTP Status Codes
1. 100번대 -> 정보, 리퀘스트 받고 처리 중
2. 200번대 -> 정상 처리
3. 300번대 -> 위치 오류, 주소값 변경 필요 등 추가 동작 필요
4. 400번대 -> 프론트엔드(클라이언트) 오류, 요청 오류
5. 500번대 -> 백엔드(서버) 오류, 응답 오류
백엔드 데이터를 받는 방법
요청은 주소로 들어오므로 프론트에서 받아야 하는 정보는 주소값에 담아서 보내게 된다.
받을 url 에서 :파라미터명을 미리 정의해두면 해당 내용이 req.params 에 파라미터로 담겨서 전달된다...
// @ts-check
const express = require('express');
const PORT = 4000;
const app = express();
app.listen(PORT, () => {
console.log(`${PORT}... 서버 구동 중`);
});
app.get('/:id/:name/:gender', (req, res) => {
console.log(req.params);
res.end(`User : ${req.params}`);
});
// @ts-check
const express = require('express');
const PORT = 4000;
const app = express();
app.listen(PORT, () => {
console.log(`${PORT}... 서버 구동 중`);
});
app.get('/id/:id', (req, res) => {
console.log(req.params);
res.send(`id : ${req.params.id}`);
});
app.get('/api', (req, res) => {
res.send(`api 요청이 접수 되었습니다.`);
});
:파라미터명 으로 값을 받아오면 주소값과 혼동될 가능성이 크므로
/파라미터명/:파라미터값 형식으로 나눠줘서 써주는 것이 혼동을 줄일 수 있는 방법이다.
다만 params의 약점은 정의된 형태로만 데이터를 받을 수 있으므로 이러한 제한이 없게끔 해주는 방법은 req.query 를 사용하는 것이다.
url의 마지막에 ?를 붙인 뒤, 필드명=값&필드명=값&... 형식으로 사용하면 된다. (구분자 &)
'학원에서 배운 것 > node.js' 카테고리의 다른 글
KDT 5th 웹개발자 입문 수업 28일차 -2 (0) | 2023.03.15 |
---|---|
KDT 5th 웹개발 입문 수업 25일차 (0) | 2023.03.10 |
[포스코 x 코딩온] 주차 정리 회원 관리 서버 정리 (0) | 2023.03.10 |
KDT 5th 웹개발 입문 수업 24일차 (0) | 2023.03.09 |
KDT 5th 웹개발자 입문 수업 22일차 (0) | 2023.03.07 |