3 minute read

자바스크립트 특징

자바는 파라미터로 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)

예를 들어 연속적으로 다음과 같은 행동을 하고 싶다.

  1. DB에서 A데이터를 조회
  2. B데이터를 조회
  3. 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

콜백함수 원칙

  1. 이름이나 익명의 함수를 사용하라
  2. 콜백함수로 파라매터 전달
  3. 콜백함수가 실행 되기 전에 함수임을 명확하게 하기
  4. Call 과 Apply를 통한 this 보호
  5. 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