JS 콜백함수
자바스크립트 특징
자바는 파라미터로 function 타입을 넣을 수 없다. 하지만 자바스크립트는 가능하다.
- 자바스크립트에서 모든 것은 객체다. 심지어 함수도 객체이다.
- 함수를 변수처럼 그리고 다름 함수의 리턴값으로 사용이 가능하게 된다.
- 자바스크립트는 파라미터로 function 타입을 넣을 수 있다.
참고블로그: 바로가기
function first(param) {
console.log(1);
// 인자가 함수 인지를 확인. function 타입을 넣었다.
if (typeof param === "function") {
param();
}
}
fucntion second() {
console.log(2);
}
//함수가 함수를 호출하고있는 기이한 현상.
//자바스크립트는 함수를 인자로 할 수 있다.
first(second)
콜백함수 동작방식과 목적
함수를 콜백으로 다른 함수의 인자처럼 사용 할 경우에는 오직 합수의 정의만을 넘겨주면 된다. 즉, 함수의 이름만을 넘겨주면 된다. 굳이 함수라고 하여 끝에 () 같은 것을 붙여줄필요가 없다.
setInterval(callback, 1000) //() 생략가능
/*
* param1: function Type
* param2: Number Type
*/
function setInterval(callback, time) {
//time 스레드 관련 로직 작성
callback();
}
- 함수의 인자로 전달된 함수의 경우에는 언제든 원하는 시점에 실행을 시킬수가 있다.(매우 중요한한 특징이다.)
- 즉, 콜백함수는 전달 받은 즉시 바로 실행이 될 필요가 없다.
- 함수의 이름처럼 “called back” , 함수의 내부의 어느 특정시점에 실행을 한다.
기본
콜백함수 사용전
first();
second();
function first(param) {
console.log(1);
}
function second(param) {
console.log(2);
}
인터프리터이기에 위 처럼 작성하면 순차적으로 잘나온다.
그럼 콜백함수는 필요없지 않나..? 왜 사용하는걸까 다음을 보자.
콜백함수 사용(1)
예를 들어 다음과 같은 경우가 있다.
- 사용자가 first() 이후에 console.log(3) 를 실행하고 싶다.
- 사용자가 first() 이후에 console.log(4) 를 실행하고 싶다.
function first(param) {
console.log(1);
param;
}
fucntion second() {
console.log(2);
}
//함수가 함수를 호출하고있는 기이한 현상.
//자바스크립트는 함수를 인자로 할 수 있다.
first(second)
이럴때 콜백함수를 쓰면 안정적으로 실행이 가능하다.
즉
보통 남이 사용할 공통 코드를 만들때 콜백함수를 사용하는듯 하다.
콜백함수 사용(2)
간단하게 만들어본다.
const dog = function(callback) {
console.log('동물의 왕국 Welcome...!');
callback();
}
dog(function(sound) {
console.log('멍멍');
})
콜백함수 사용(3)
예를 들어 연속적으로 다음과 같은 행동을 하고 싶다.
- DB에서 A데이터를 조회
- B데이터를 조회
- C데이터를 조회
db.collection('post').findOne(selectA, function()) {
db.collection('post').findOne(selectB, function()) {
db.collection('post').findOne(selectC, function()) {
}
}
}
이렇게 하면 구현이 가능하다. 하지만 이 디자인은 단점이 보인다. 그건 바로 옆으로 길어진다는 점이다. 자바스크립트를 선택한 이상 악으로 깡으로 쓸 수 밖에없다.
참고로 이런 부분은 프로미스(Promise) 으로 커버가 가능하다.
콜백함수는 클로저다(Callback Functions Are Closures)
우리가 다른 함수의 인자로 콜백함수를 전달할 때, 전달받은 함수의 특정시점에 그 콜백함수가 동작을 한다. 마치 전달받은 함수가 이미 콜백함수를 내부에서 정의 한 것처럼 말이다. 이 말은 콜백은 클로저라는 말과 같다.
간단하게 설명을하면 전달된 콜백함수는 콜백함수를 포함한 함수 내부의 인자에 접근이 가능하고 심지어 전역변수에도 접근이 가능한 상태가 된다. 자세한 설명은 전에 작성한 포스팅을 참조한다.
(자바스크립트 호이스팅과 클로저)[https://git.blang.co.kr/html-css-js-jquery/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85%EA%B3%BC-%ED%81%B4%EB%A1%9C%EC%A0%80/]
//객체를 생성합니다.
// 다른 함수의 콜백함수로 전달한 메서드를 정의합니다.
var clientData = {
id: 094545,
fullName: "Not Set",
// setUserName clientData의 메서드입니다.
setUserName: function (firstName, lastName) {
// this는 clientData라는 객체를 지칭하고 있습니다.
this.fullName = firstName + " " + lastName;
}
}
function getUserInput(firstName, lastName, callback ) {
// 지역변수를 넣지 않음
// Now save the names
callback (firstName, lastName);
}
getUserInput ("Barack", "Obama", clientData.setUserName);
console.log (clientData.fullName);// 값에 설정되지 않음
// fullName 프로퍼티가 window object의 인자로 세팅됨
console.log (window.fullName); // Barack Obama
콜백함수 원칙
- 이름이나 익명의 함수를 사용하라
- 콜백함수로 파라매터 전달
- 콜백함수가 실행 되기 전에 함수임을 명확하게 하기
- Call 과 Apply를 통한 this 보호
- this를 사용한 메서드를 콜백으로 사용시 문제
기록
CASE1. setTimeout() 으로 동기식 처리하기
/** 동적페이지 생성 **/
const dynamicContent = 'Hello, world! Current time is: ' + new Date().toLocaleTimeString();
const strTemp = '동적값';
var htmlDoc;
function setContent()
{
// (STEP1). 함수1안에 함수2를 만든다.
function addContent() {
console.log('STEP1');
htmlDoc = `<!DOCTYPE html><html><head>`
htmlDoc += `<link rel='stylesheet' type='text/css' href='css/test_css.css'>`
htmlDoc += `<title>업데이트 대상 리스트</title></head>`
htmlDoc += `<body><h1>${dynamicContent}</h1>`
htmlDoc += `<div class='table-col'>`
htmlDoc += `<table>`
htmlDoc += `<thread>`
htmlDoc += `<tr><th scope='col'>회원번호</th><th scope='col'>이름</th></tr>`
htmlDoc += `</thread>`
htmlDoc += `<tbody>`
// (STEP2). 함수2내부에서 함수1의 콜백함수인자를 호출한다. (동기식처리 수행)
console.log('STEP2');
setTimeout(()=>{
console.log('STEP2-1');
//동기식처리1
Member.getMember('', (err, data) => {
if (err) res.status(500).send(null, { message:err.message || "에러"});
data.forEach(function(row) {
htmlDoc += `<tr><td>${row.회원번호}</td><td>${row.이름}</td></tr>`;
});
});
//동기식처리2
setTimeout(()=>{
console.log('STEP2-2');
htmlDoc += `</tbody>`
htmlDoc += `</table>`
htmlDoc += `</div>`
htmlDoc += `</body></html>`;
console.log('동기식 조립결과: ' + htmlDoc);
res.send(htmlDoc);
},200); //마지막처리는 타이머를 넉넉히 준다.
},100)
} //[END setContent1 function...]
// (STEP3). 함수1안에서 최종호출한다.
console.log('STEP3');
addContent();
}
//비동기처리 실행
setContent();
위 STEP 별로 처리하니까 성공했다!!
Leave a comment