안녕하세요
오늘은 js의 비동기처리를 어떻게 동기처리로 바꾸는 지와 form의 기초를 배우는 시간이었습니다.
개인적으로 언어를 효율에 맞게 비동기방식으로 만들어 놓고 힘들게 동기를 찾는지 ㅠㅠ
원망스러울 따름이었지만 아무래도 필요한 기능인 것 같아,,,,, 열심히 들으려고 노력하였습니다.
요즘 잠을 많이 자는데도 피곤한건 왜 때문일까요?!?!?!?
먼저, 지난번 포스팅에 기재하였듯이 우리의 JS는 비동기처리 방식을 가진 언어입니다.
여기에 이제,,,node.js가 합세해서 JS의 비동기처리 언어 특성을 확장하고 강조하여
js+ node.js = 비동기 그 잡채인거죠,
이 덕분에 효율적이기는 하나, 로직에서 순서대로 꼭 처리해야할 경우
특별하게 순서를 강제로 지정해주어야 합니다.
여기에 두 가지 방식을 오늘 배웠습니다.
1. callback 함수를 이용해 비동기 처리
콜백 함수는 매개변수로 함수를 받고 그 함수가 다른 함수의 작업이 완료되었을 때 호출되는 함수입니다.
그러니까 콜백함수가 불리기 전까지 실행하지 않고 있다가 콜백함수가 실행되었을 때 실행한다는 말이죠
개발자가 함수의 순서를 직접 정할 수 있다는 것입니다.
function goMart() {
console.log('마트에 가서 어떤 음료를 살지 고민한다.');
}
function pickDrink(callback) {
setTimeout(function () {
console.log('고민끝!!!');
product = '제로콜라';
price = 2000;
callback(product, price); // *매개변수로 넘긴 콜백함수 실행
}, 3000);
}
goMart();
pickDrink(function (product, price) {
console.log(`상품명 : ${product}, 가격: ${price}`);
});
pickDrink() 함수 내에 매개변수로 callback함수를 받고 있습니다. (함수 이름은 당연히 변수입니다.)
여기서 6번째 줄의 콜백함수가 없었다면,
마지막 문장에서 찍히는 console.log에는 상품명과 가격이 undefined로 나오게 됩니다.

그 이유는 (비동기적 처리 때문에) 아직 product와 price에 값이 대입되기 전에
먼저 출력하는 간단한 함수를 실행해버리기 때문입니다.
그런데 값이 넣어지고 나서 상품명과 가격이 console에 찍혀야하잖아요?
그래서 pickDrink 에 매개변수로 함수를 넣어주고
이 콜백함수 위치를 product와 price의 값이 대입되고 나서 위치시켜주면
매개변수로 넣어준 함수가 순서에 따라 콜백함수가 불렸을 때 실행되어 잘 찍히게 됩니다.

이번에는 고민끝!! 이라는 문구도 함수 차례에 맞게 먼저 출력되고
상품명과 가격도 제대로 대입되어서 출력되는 것을 볼 수 있습니다 :)
이 콜백 함수가 다 좋은데, 지옥에 빠질 수가 있습니다.
콜백 함수를 끝없이 지옥처럼 불러야하는 경우가 생긴다 말인거죠.
(이를 콜백 지옥이라 합니다)

그래서 이를 해결하고자 나온 다른 방법이 있습니다.
2. Promise 사용
일단 이 아이는 이름값을 하는 아이입니다.
Promise = 약속한다는 거죠, 뭘 약속한다는 거죠?
어떠한 결과값을 반드시 약속한 대로 반환한다는 것이죠
보통, 함수에서 에러가 나면 컴파일러가 에러를 내뱉거나 디버깅으로 찾아야하는데,
이 함수는 성공하든 실패하던 무조건 어떠한 반환값을 내놓는다는 거죠!
이 함수는 생성하는 부분과 사용하는 부분이 따로 필요합니다.
- 생성하는 코드
function promise1(flag) {
return new Promise(function (resolve, reject) {
if (flag) {
resolve(`성공! 이 때 flag 값은 ${flag}!`);
} else {
reject(`실패! 이 때 flag 값은 ${flag}!`);
}
});
}
Promise는 객체형식이라서 new를 이용해 해로 생성해주고, 매개변수로 함수를 받습니다.
이 때 함수는 정석이든 화살표 방식이든 상관없으나 resolve, reject를 꼭 인자로 받습니다.
그리고 상위의 코드는 flag가 true면 if문을 타고 resolve를 통해 결과값을 내고,
flag가 false면 else문을 타고 reject를 통해 결과값을 냅니다.
resolve = 성공값
reject = 실패값
그리고 여기까지는 단순히 promise객체를 만든 것일 뿐입니다.
진짜 사용은 따로 해줘야합니다.
promise1(5 > 3)
.then(function(result) {
console.log(result);
})
.catch(function(error) {
console.log(error);
});
사용하려면 당연히 함수를 불러주고, then과 catch라는 후위메서드를 사용하여
어떻게 사용할지 선택을 하게 됩니다.
then = 성공값
catch = 실패값
위의 함수에서는5 > 3이라는 조건식이 true이므로 then에 걸리게 되고
new Promise인 Promise1함수의 resolve부분으로 갑니다.
그럼 resolve 결과 값으로 문장이 있죠?
그 문장을 result로 받아서 consol.log에 찍히는 과정입니다.
만약 Promise1의 조건식이 5 < 3이라서 false로 인자를 받게 된다면
catch에 걸리게 되고 reject로 가서 reject에 있는 결과값인 문장을 받아서 error 변수에 넣겠죠!
이렇게 보면 그냥 callback함수 쪽이 더 편하다고 생각할 수도 있는데,
하기 실습 예제를 본다면 그렇지 않습니다.
이 예제는 1초마다 배경색이 red -> orange -> yellow -> green -> blue로 바뀌는 예제입니다.
먼저, callback함수를 사용하여 구현하였습니다.
<script>
setTimeout(function(callback){
document.body.style.background = 'red';
setTimeout(function(callback){
document.body.style.background = 'orange';
setTimeout(function(callback){
document.body.style.background = 'yellow';
setTimeout(function(callback){
document.body.style.background = 'green';
setTimeout(function(callback){
document.body.style.background = 'blue';
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);
</script>
보면 아까 본 그림과 비슷하게 콜백함수에 의해 들여쓰기가 굉장히 많이 되어있으며,
가독성도 떨어지고, 함수의 괄호 위치와 지역성 구분이 굉장히 떨어지는 상태입니다.
다음은 똑같은 예제를 promise를 활용하여 구현한 것입니다.
<script>
function bgColorChange (color) {
return new Promise(function(resolve, reject){ //promise구현부
setTimeout(function (){
let body = document.querySelector('body');
body.style.backgroundColor = color;
resolve();
}, 1000);
});
}
bgColorChange('red') // promise 사용부
.then(function() {
return bgColorChange('orange')})
.then(function() {
return bgColorChange('yellow')})
.then(function() {
return bgColorChange('green')})
.then(function() {
return bgColorChange('blue')})
</script>
함수의 목적도 더 분명해보이고, 한눈에 봐도 가독성이 굉장히 좋아졌습니다.
그 다음 배웠던 것은, 미들웨어입니다.
3. body-parser
요즘 node.js에는 body-parser가 설치되어있어 사용 시에 따로 설치하지 않아도 됩니다.
이 body-parser는 HTTP의 본문(body)를 파싱하여 사용 가능한 데이터 형태로 변환해주는 미들웨어입니다.
이 미들웨어는 use()함수와 같이 쓰여 요청(req)와 응답(res) 중간에서 여러 작업을 할 수 있습니다.
//req.body 객체를 해석할 수 있도록 body-parser 미들웨어 등록
app.use(express.urlencoded({ extended: true})); // post 요청으로 들어오는 모든 형식의 데이터를 파싱
app.use(express.json()); // json 형식으로 데이터를 주고 받겠다.
사실 어떤 개념인지 한번에 확 와닿지는 않으나 이러한 use()함수와 미들웨어 등록을 통해
우리가 데이터를 클라이언드와 서버가 주고 받을 때 중간에서 데이터를 잘 주고 받을 수 있도록
데이터를 가공하고 변형하여 약속된 형태로 조정하고
그에 필요한 처리를 수행하는 역할을 한다고 생각하면 될 것 같습니다.
이 미들웨어를 사용하여 클라이언트와 서버가 메세지를 전달받을 수 있는 메서드는 두 가지 였습니다.
4. get()
이 get() 함수는 req.query에 사용자가 입력한 정보를 담아서 서버로 전송됩니다.
app.get('/getForm', (req, res) => {
res.render('result', {
title: 'GET 요청',
userInfo: req.query,
});
});
위의 함수는 get()을 썼는데,
/getForm이라는 경로가 추가가 된다면 render를 통해 result.ejs페이지를 불러와서 화면에 띄우고
userInfo라는 변수에 req.query(클라이언트가 입력한 정보들)을 담아서 서버에 보내는 것입니다.
그럼 하기와 같이 req.query의 내용을 활용할 수 있습니다.
<body>
<h1><%= title%> 폼 결과 확인하기</h1>
<div>
<span><%= userInfo.id%></span>님 환영합니다!
</div>
<div>비밀번호는<span><%= userInfo.pw%></span>이군요?</div>
</body>
ejs를 통해 userInfo 내의 id값에 접근하여 이렇게 span태그로 화면에 띄울 수 있습니다.
5. post()
이 post()함수는 req.body에 사용자가 입력한 정보를 담아서 서버로 전송됩니다.
app.post('/postForm', (req, res) => {
res.render('result', {userInfo: req.body});
});
이 코드는 post()를 통해 경로에 /postForm이 추가가 된다면 render를 통해 result.ejs를 불러오고
userInfo라는 매개변수에 req.body(클라이언트가 입력한 정보들)을 받아서 서버로 보내게 됩니다.
그럼 하기와 같이 이 req.body를 활용할 수가 있습니다
<body>
<h1><%= title%> 폼 결과 확인하기</h1>
<div>
<span><%= userInfo.id%></span>님 환영합니다!
</div>
<div>비밀번호는<span><%= userInfo.pw%></span>이군요?</div>
</body>
get()과 같은 코드입니다.
어쨋든 get + req.query로 보내던 post + req.body로 보내던 둘 다 매개변수에 객체로 담아서 보냈으니
ejs에는 영향을 끼치지 않습니다.
그러나 여기에는 차이점이 존재합니다.
get + req.query 조합에는 파라미터에 정보가 담기므로 보안에 취약합니다.
그리고 이 URL에 정보가 담기기 때문에 URL의 길이 한정으로만 정보가 담길 수 있습니다.
그러나 브라우저에 캐시할 수 있어 이전 결과 요청을 재사용할 수 있다는 장점이 있습니다.

post + req.body조합에는 파라미터에 정보가 담기지 않아서 보안에 강합니다.
그리고 body를 통해 전송되므로 많은 양의 정보가 담길 수 있습니다.
그러나 브라우저에 캐시할 수 없어 이전 결과 요청을 재사용할 수 없다는 단점이 있습니다.

보통 데이터를 조회하거나 검색할 경우에는 get()을 많이 사용하며 (ex, 내 아이디 검색)
데이터를 수정하고 생성하는 경우에는 post()를 많이 사용한다고 합니다. (ex, 회원가입)
하여 제가 get() 메서드를 사용하여 실습한 것을 아래에 올리고 포스팅을 마치도록 하겠습니다 :)
js 코드
const express = require('express')
const app = express();
const PORT = 8080;
app.set('view engine', 'ejs');
app.set('/views', 'views');
app.use(express.urlencoded({ extended: true}));
app.use(express.json())
app.get('/', (req, res) => {
res.render('index2');
});
app.get('/register', (req, res) => {
res.render('register', {userInfo: req.query});
});
app.listen(PORT, () => {
console.log(`${PORT} is open!`);
});
index2.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>실습_get으로 정보 받기</title>
</head>
<body>
<form action="/register" method="get">
<span>이름</span>
<input type="text" name="name">
<fieldset action="/" method="get">
<legend>성별</legend>
<input type="radio" name="gender" value="female">
<label for="female">여자</label>
<input type="radio" name="gender" value="male">
<label for="male">남자</label>
</fieldset>
<fieldset action="/" method="get">
<legend>생년월일</legend>
<select name="birth_year" id="">
<% for (let i = 1990; i<= 2023; i++) {%>
<option value="<%= i%>"><%= i%></option>
<%}%>
</select>
<span>년</span>
<select name="month" id="">
<% for (let i = 1; i<= 12; i++) {%>
<option value="<%= i%>"><%= i%></option>
<%}%>
</select>
<span>월</span>
<select name="day" id="">
<% for (let i = 1; i<= 31; i++) {%>
<option value="<%= i%>"><%= i%></option>
<%}%>
</select>
<span>일</span>
</fieldset>
<fieldset action="/" method="get">
<legend>관심사</legend>
<input type="checkbox" name="interest" value="travel">
<label for="traval">여행</label>
<input type="checkbox" name="interest" value="fashion">
<label for="fashion">패션</label>
<input type="checkbox" name="interest" value="food">
<label for="food">음식</label>
</fieldset>
<fieldset>
<legend>선택</legend>
<span> 좋아하는 색상</span>
<input type="color" name="fav_color" value="#111">
</fieldset>
<button>회원가입</button>
</form>
</body>
</html>
register.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>회원가입</title>
</head>
<body>
<p>고객님의 이름은 <%= userInfo.name%>이군요!</p>
<p>고객님의 셩별은 <%= userInfo.gender%>이군요!</p>
<p>고객님의 생년월일은 <%= userInfo.birth_year%>년 <%= userInfo.birth_month%>월 <%= userInfo.birth_day%>일이군요!</p>
<p>고객님의 취미는 <%= userInfo.interest%>이군요!</p>
</body>
</html>'개발 공부한 것들 > SeSSAC 웹 풀스텍 과정 회고록' 카테고리의 다른 글
| [SeSSAC] 웹 풀스택 영등포 5기 과정_폼유효성검사, 웹 파일 업로드 (0) | 2023.08.22 |
|---|---|
| [SeSSAC] 웹 풀스택 영등포 5기 과정_dynamic form (0) | 2023.08.19 |
| [SeSSAC] 웹 풀스택 영등포 5기 과정_nodejs 미들웨어, 구조분해, 클래스 (0) | 2023.08.11 |
| [SeSSAC] 웹 풀스택 영등포 5기 과정_nodejs기초 (0) | 2023.08.09 |
| [SeSSAC] 웹 풀스택 영등포 5기 과정_ Web Resume만들기 (0) | 2023.08.04 |