ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 구글 입사 면접 질문 (javascript)
    면접,이직 2021. 3. 30. 01:07

    구글에 입사하기 위해서 2020년 기준 자바스크립트 질문들에 대해서 소개드립니다.

    부제 : Callbacks, Promises, Await/Async

     

    1. Callback

    간단한 callback 예시

    const body = document.getElementsByTagName('body')[0];
    
    function callback() {
      console.log('Hello');
    }
    
    body.addEventListener('click', callback);

    EventListener 이벤트 함수도 콜백을 사용합니다.

    • 이 경우 "콜백"이라는 함수를 다른 함수 "addEventListener"에 전달합니다.
    • addEventListener가 실행되면 click 이벤트와 함께 콜백을 등록합니다.

    여기서 JavaScript를 실행하는 방법에는 두 가지가 있습니다.

     

    1. 페이지가 로드 될 때 스크립트가 한 번 실행됩니다.

    1. body가 상수로 선언되고 body 엘리먼트가 할당됩니다.
    2. callback 함수가 선언되지만, 실행되지 않습니다.
    3. addEventListener가 실행됩니다.(콜백은 addEventListener로 전달됩니다.)
    4. body에 클릭 이벤트가 일어나면 addEventListener가 실행해야될 일 중 하나로 브라우저에게 콜백 함수를 실행하라고 합니다.

     

    2. JavaScript가 실행되는 다른 시간은 이벤트가 발생할 때입니다

    1. 이번에는 콜백이 마지막으로 실행됩니다.
    2. body를 클릭하면 콜백이 실행되고 콘솔에 "Hello"가 기록됩니다.
    3. 이것은 두 번 이상, 즉 본문을 클릭 할 때마다 발생할 수 있습니다.

     

    callback의 실제 예제

    스크립트와 모듈을 비동기적으로 로드하는 loadScript라는 함수를 만들어 보겠습니다.

    function loadScript(src) {
      // script태그를 만들고 document의 head에 append 합니다.
      let script = document.createElement('script');
      script.src = src;
      document.head.append(script);
    }
    
    // 실행
    loadScript('/my/script.js');

    브라우저는 자동으로 로드를 시작하고 완료되면 실행됩니다.

    문제점

    loadScript('/my/script.js');
    // 아래의 함수는 loadScript를 기다려주지 않습니다.
    newFunction(); // 실행되지 않음!

    해결

    스크립트가 로드 될 때 실행되어야 하는 loadScript에 두 번째 인수로 콜백 함수를 추가해 보겠습니다.

    // loadScript를 정의
    function loadScript(src, callback) {
      let script = document.createElement('script');
      script.src = src;
    
      script.onload = () => callback(script);
    
      document.head.append(script);
    }
    
    // 실행
    loadScript('/my/script.js', function() {
      // script가 다 로드되면 callback이 실행됩니다.
      newFunction(); // 작동!
    });

    두 번째 인수는 작업이 완료 될 때 실행되는 함수(일반적으로 익명)입니다.

     

    callback 안의 callback

    Promise와 Async/Await 예제를 살펴보기 전에 한 가지 예시를 더 보고 넘어가죠.

    어떻게 두 개의 스크립트를 순차적으로 로드 할 수 있습니까?

    자연스러운 해결책은 두 번째 loadScript 호출을 콜백 안에 넣는 것입니다.

    loadScript('/my/script.js', function(script) {
      loadScript('/my/script2.js', function(script) {
        loadScript('/my/script3.js', function(script) {
          // 순차적으로 실행됩니다. 모든 scrip가 로드되고 실행되는 구간
        });
      })
    });

     

    2. Callback vs.Es6 Promise

    운명의 피라미드라는 말과 콜백 지옥이라는 말이 있습니다.

    콜백 지옥을 resolve(해결) 하기 위해서 만들어졌습니다.

    // promise 방식으로 loadScript를 변경하였습니다.
    function loadScript(src) {
      return new Promise(function(resolve, reject) {
        let script = document.createElement('script');
        script.src = src;
    
    	// script가 로드가 되면 callback()함수를 실행하던 것에서 resolve(script)를 호출하는 것으로 변경
        script.onload = () => resolve(script);
        script.onerror = () => reject(new Error(`Script load error for ${src}`));
    
        document.head.append(script);
      });
    }
    
    // 사용법
    let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");
    
    // 변수에 담아서 그 변수에 담긴 return new Promise 함수가 호출이 완료되면 resolve 또는 reject를 then으로 받습니다.
    promise.then(
      script => alert(`${script.src} is loaded!`), // script를 resolve로 보냈으니까 이렇게 받을 수 있습니다.
      error => alert(`Error: ${error.message}`) // reject 일 때는 error를 만들어서 보낸 것을 이렇게 받습니다.
    );
    
    promise.then(script => alert('Another handler...'));

     

    3. Sample - 1번 문제 A,B,C를 순서대로 찍으세요.

    callback

    function printString(string, callback) {
      setTimeout(
        () => {
          console.log(string)
          callback()
        },
        1 * 1000 // 1s
       )
    }
    
    
    function printAll() {
      printString("A", () => {
        printString("B", () => {
          printString("C", () => {})
        })
      })
    }
    
    printAll()

    * 중요 : 4번째 함수를 에러를 방지하기 위해서 빈 함수를 호출해야합니다.

     

    Promise

    function printString(string) {
      return new Promise((resolve, reject) => {
        setTimeout(
          () => {
            console.log(string)
            resolve()
          },
          1 * 1000
          )
      })
    }
    
    function printAll() {
      printString("A")
      .then(() => {
        return printString("B")
      })
      .then(() => {
        return printString("C")
      })
    }
    
    printAll()

     

    async/await

    function printString(string) {
      return new Promise((resolve, reject) => {
        setTimeout(
          () => {
            console.log(string)
            resolve()
          },
          1 * 1000
          )
      })
    }
    
    async function printAll() {
      await printString("A")
      await printString("B")
      await printString("C")  
    }
    
    printAll()

    new Promise로 return되는 함수를 async로 감싸주고 then으로 순서를 이을 필요 없이 await함수로 호출해줍니다.

    4. Sample - 2번 문제 알파벳 배열 ["A","B","C"] 를 사용하여 인쇄

    여기에서 조금 더 심화하면 A,B,C string 대신 배열인 ["A","B","C"] 를 제공합니다.

    function printAll() {
      const arr = ["A", "B", "C"]
      
      let index = -1
      printString(arr[++index], () => {
        printString(arr[++index], () => {
          printString(arr[++index], () => {})
        })
      })
    }
    
    printAll()

    5. Sample - 3번 문제 알파벳 배열 [A… Z]을 순서대로 인쇄

    콜백 지옥 대신에 여러번 콜백하고 싶은 심정입니다.

     

    최적화를 해보지요!

    • arr배열에 알파벳들을 담은 뒤에  printString 호출 배열 [index ++] 후 매번 1 씩 증가하는 index를 정의합니다.
    • printString 함수에서 3 개의 매개 변수를 전달합니다 (arr, index, callback의 항목).
    • 가장 중요한 것은 cb0fcb 함수를 호출하는 것입니다
    function printString(string, indx, cb){
          if(indx == 27) {
            cb('err')
          }
          cb(null, string)
    }
    
    function callback(err, str) {
     
      if(err) {
        console.log('done');
        return
      }
      console.log(str);
    }
    
    function printAll(){
      let arr = [...Array(26)].map((val, i) => String.fromCharCode(i + 65)); 
      // console.log(arr) // ["A", "B", "C", "D" ... "Z"]
    
      let index = 0;
      setTimeout(function cbOfcb() {
        let array = arr
        if(index < 27){
          printString(array[index++], index, callback); // 전달 매개변수 -  string , index , callback
          cbOfcb(); // if 조건에 맞을 때 까지 재귀 함수를 호출합니다.
        }    
      }, 1000)
    }
    
    printAll()

    6. Sample - 4번 문제 주어진 횟수만큼 콜백 API 요청하기

    // run the request. this function will call itself max. 5 times if the request fails
    request(5, callback);
    
    function request(var retries, var callback) {
      axios.post("URL").then(
        response => {
          if(response.data == 1) {
            // request가 성공적으로 호출되면 callback함수를 호출합니다.
            callback(response);
          }
          else {
            // 실패했으면 retries 횟수를 체크하고 없으면 호출하지 않습니다.
            if(retires > 0) {
              request(--retires, callback);
            }
            else {
              // 아무런 retries 횟수가 없으면 callback으로 error를 뱉습니다.
              callback([], "out of retries");
            }
          }
        })
      .catch(error => {
        // ajax 서버 오류 발생! -> 다시 리트라이 횟수만큼 리트라이해봅니다.
        if (retries > 0) {
          request(--retries, callback);
        }
        else {
          // retries가 있는지 체크하고 없으면 그냥 에러입니다.
          callback([], error);
        }
      });
    }

     

     

    감사합니다.

     

     

     

     

    출처 : medium.com/developers-tomorrow/google-javascript-technical-interview-7a20accd6ddf

Designed by Tistory.