Google Javascript 인터뷰 (Callbacks, Promises, Await/Async)

Jessie Lee
Cross-Platform Korea
15 min readAug 14, 2020

Google Javascript Interview Process에 대해서 정리한 글입니다.

이 글은 GP Lee 님의 Google Javascript Technical Interview 를 번역한 글입니다. Google 인터뷰 내용과 과정에 대해 상세하게 적은 글을 한국 분 들을 위해 번역해 보았습니다. 도움이 되었으면 좋겠네요 :)

원작자 님의 글에 대해 더 관심이 많으신 분들은 여기를 통해 구독해주세요!

이글을 아래의 3가지를 설명한 글입니다.

1. Callbacks

  • Javascript 에서 Callback 이 무엇인가요?
  • Callback 사용 예제

2. Callbacks vs. ES6 Promises & Await / Async

  • 왜 Promises 를 쓰나요?
  • Promises 는 무엇인가요?
  • Promises 사용 예제

3. Sample Problems

Q1. 영어 알파벳 A, B,C 를 순서대로 Callback, Promise, Async/Await 으로 출력하시오.

Q2. 배열 [A, B, C] 을 순서대로 출력하시오.

Q3. 배열 [A … Z] 을 순서대로 출력하시오.

Q4. Callback API Request 를 정해진 숫자 만큼 보내세요.

그럼 이제 첫번째 목차부터 시작해보겠습니다.

1. Callbacks

Javascript 에서 Callback 이란 무엇인가?

  • Javascript 는 웹브라우저를 통해 HTML & CSS 와 함께 웹페이지를 제작할 수 있도록합니다.
  • 웹 브라우저에는 수 많은 이벤트들이 일어납니다. 여기서 Javascript의 역할은 이러한 이벤트에 반응하여 효과가 일어나게 하는 것입니다. 이벤트의 예시로는 클릭이나 타이핑이 있습니다.
  • Callback을 하는 가장 근본적인 이유는 이벤트에 반응하여 코드가 작동될 수 있게 하기 위해서 입니다. 이벤트가 작동 될때 callback 함수를 부르기 위해서는 callback 와 이벤트를 binding 시켜주는 또 다른 함수가 필요합니다.

실제로 함수를 사용하면서 callback 을 많이 이용합니다. 예를 들어, 아래의 예시에서는 클릭했을때 Hello를 출력하는 웹페이지 입니다.

간단한 Callback 예시

const body = document.getElementsByTagName('body')[0];

function callback() {
console.log('Hello');
}
body.addEventListener('click', callback);

설명

  • 이 경우에는, 우리는 callback 이라는 함수를 다른 함수 addEventListener 로 보내고 있습니다.
  • addEventListener를 부를때, callback 은 ‘click’ event와 함께 호출됩니다.

여기서 javascript는 2가지 방법으로 작동됩니다

  1. 웹 페이지가 로딩 될때 script 는 한번 돕니다.
  • const body 가 불러지고, value가 주어지게 됩니다.
  • callback 함수가 선언되지만, 실행되지는 않습니다.
  • 함수 addEventListener가 실행됩니다.
  • addEventListener의 역할은 ‘click’ event 가 발생할시, callback 함수를 부르는 것입니다.

2. 이벤트가 일어날때 javascript는 작동합니다.

  • 이때 callback 함수가 실행됩니다.
  • callback 함수가 실행되면, ‘Hello’ 가 console 창에 찍히게 됩니다.
  • 이것은 click 이 발생할 때 마다 일 어날 수 있습니다.

이제 callback 어떻게 실생활에 사용되는지 설명해보고자 합니다.

Callback 의 적용예시

아래는 script 와 module을 동시에 로딩하는 loadScript이라는 함수 입니다.

function loadScript(src) {  // <script> tag를 만들고 페이지에 추가해 줍니다. 
// this causes the script with given src to start loading and run when complete
let script = document.createElement('script');
script.src = src;
document.head.append(script);
}
  • 이 함수의 document에 script 를 추가 해줍니다.
// script를 로딩하고 호출합니다.pathloadScript('/my/script.js');

문제점

  • script 는 ‘asynchronous’ 합니다. 즉, 함수들이 동시에 실행 되고 종료 되는 것이 아니라, 여러함수를 한번에 실행시킬 수 있고, 하나의 함수가 종료 되지 않아도 다른 함수가 실행되기 시작할 수 있다는 것입니다.
loadScript('/my/script.js'); // the script has "function newFunction() {…}"
// the code below loadScript
// doesn't wait for the script loading to finish
newFunction(); // no such function!

해결책

  • loadScript의 두번째 변수로 callback 함수를 불러보겠습니다. script가 로딩 되면서 이 함수는 호출됩니다.
// define function loadScriptfunction loadScript(src, callback) { 

let script = document.createElement('script');
script.src = src;
script.onload = () => callback(script);

document.head.append(script);}
// call function
loadScript('/my/script.js', function() {
// script 가 로딩되어야지만 작동
newFunction();
...
});
  • 이렇게 두번째 변수는 callback 함수로 action이 완료되어야지만 작동 되는 함수 입니다.

Callback in Callback

ES6 Promise & Async/Await 의 예시로 넘어가기 전, callback 예제를 하나더 집고 넘어가겠습니다.

  • 어떻게 두 개의 script를 순차적으로 로딩할까요>
  • loacScript 함수안에 callback 함수를 넣어주는 것입니다.
loadScript('/my/script.js', function(script) {   loadScript('/my/script2.js', function(script) {    loadScript('/my/script3.js', function(script) {      
// ...continue after all scripts are loaded
});
})});

우리는 이제 callback함수를 어떻게 쓰는지에 대한 이해를 했습니다. 이제 ES6 Promise로 넘어가겠습니다.

2. Callback vs. ES6 Promise

왜 Promise 을 쓰나요?

  • callback 함수가 중첩 될 수록 코드는 더 관리하기 어려워집니다. 여기서 코드의 중첩이란, 특정 코드가 종료 될때까지 기다리지 않고 바로 다음코드를 실행한다는 문제입니다. 이것을 해결하기 위해서 Promise 를 사용합니다.

Promise 은 무엇인가요?

  • Promise은 ES6가 callback 문제를 해결하기 위해 소개했습니다.

Promise를 이용한 loadScript 함수

// Promise 방식으로 함수를 정의하는 것function loadScript(src) {  
return new Promise(function(resolve, reject) {
let script = document.createElement('script');
script.src = src;
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"); promise.then( script => alert(`${script.src} is loaded!`),
error => alert(`Error: ${error.message}`)
);
promise.then(script => alert('Another handler...'));
  • Promise 는 순차적으로 함수를 실행시키는 것을 가능하게 합니다. loadScript 가 먼저 실행되고, then 이 실행됩니다.
  • Promise에 then을 원하는 횟수만큼 부를 수 있습니다.

Promises 사용예제

3. 문제 예시

(Q1 — *Callback) 영어 글자 A,B,C 를 순서대로 출력하시오.

먼저, 함수 printString을 만들어서 console에 string을 출력하고, 변수를 가진 callback 함수를 setTimeout() 함수를 가지고 시간차를 둡니다.

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

A, B, C를 순서대로 출력합시다.

function printAll() {  
printString("A", () => {
printString("B", () => {
printString("C", () => {})
})
})
}
printAll()

(Q1 — *Promise) 영어 글자 A,B,C 를 순서대로 출력하시오.

function printString(string) {  
return new Promise((resolve, reject) => {
setTimeout(
() => {
console.log(string)
resolve()
},
1 * 1000
)
})
}

A, B, C를 순서대로 출력합시다.

Promise 에서는 then을 이용해서 다음 함수로 넘어갑니다. 이경우에는, 다른 printString() 함수를 부릅니다.

function printString(string) {  
return new Promise((resolve, reject) => {
setTimeout(
() => {
console.log(string)
resolve()
},
1 * 1000
)
})
}

(Q1 — *Await Async) 영어 글자 A,B,C 를 순서대로 출력하시오.

Await Async 는 Promise에 기반을 둔 언어 입니다.

쓰임

function printAll() {  
printString("A")
.then(() => {
return printString("B")
})
.then(() => {
return printString("C")
})
}
printAll()

Async Await function

async function printAll() {  
await printString("A")
await printString("B")
await printString("C")
}
printAll()

(Q2 — *Callback) 배열 [A,B,C]를 출력하시오.

인터뷰에서 문제를 해결했을때, 질문을 항상 살짝 변형 시켜서 물어봅니다.

그래서 이 두번째 질문은 첫번째 질문을 조금 변형 시킨 것 입니다.

그냥 string 형태의 A, B ,C 가 아닌 이것을 배열 형태로 만들었습니다.

Callback Way

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

printString(arr[++index], () => {
printString(arr[++index], () => {
printString(arr[++index], () => {})
})
})
}
printAll()

Usage

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

(Q3) 알파벳 전체를 배열 에 담아 출력하시오.

이문제는 어떻게 해결할까요? 아래는 잘못된 방법 인 callback을 여러번 부르는 방법입니다.

function cbHell() {   

XX(param, () => { // 1 callback
XX(param, () => { // 2 callback
XX(param, () => { // 3 callback
... // forever (hell)
})
})
})
}
printAll()

이렇게 해결하면 안되죠, 그래서 이 함수를 최적화 시켜 보겠습니다.

이 경우에는 알파벳 26 글자를 26개의 ‘callback hell’ 을 만들면서 부르는 것이 아니라, callback 속에서 callback을 만드는 방법으로 부르도록 하겠습니다.

function

  • 먼저 우리는 arr 라는 배열을 정의하고 그속에 모든 알파벳, [“A”, ”B”, “C”, … “Z”] 를 담겠습니다.
  • 우리는 index를 정의하겠습니다. 이 index는 printString()을 부를때 마다 1씩 증가하는 형태로 만들겠습니다.
  • printString 함수는 3가지 변수를 pass 하도록 하겠습니다 ( an item from arr, index, callback )
  • 그리고 함수 cbOfcb 를 부르도록 하겠습니다.
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){
// string , index , callback 의 변수를 pass 해줍니다.
printString(array[index++], index, callback);
cbOfcb(); // call itself
}
}, 1000)
}
printAll()

printString function

  • printAll 와 callback 함수 사이의 중간역할을 해줍니다.
  • index 가 27 이되면, cb err 를 보내줍니다.
  • 이게 아니면, 우리는 cb null & string을 보내줍니다.

callback function

  • 만약 err 가 보내지게 되면, done을 출력하고 return합니다.
  • 만약이게 아니라면, string(str)을 출력해줍니다.
function callback(err, str) {   
if(err) {
console.log('done');
return
}

console.log(str);
}

(Q4) Make Callback API Request for a given number of times

javascript 에서 ‘ascychronous’ 하게 API request를 보내기도 합니다. 하나의 request 보냈을 경우 request가 실패할 가능성이 있기 때문에 오히려 여러번 request를 보내는 것이 더 효과적입니다.

request function

  • axios post request

request 가 성공적이라면

  • response.data 에 어떤 값이 들어오게 됩니다. 이경우에는 (response.data ==1) 입니다.

If not (else)

  • retry가 0 이상이면 다시 request를 보냅니다.
  • 더 이상 남은 retries가 없다면, callback 함수를 부릅니다.
// run the request. this function will call itself max. 5 times if the request failsrequest(5, callback); function request(var retries, var callback) {                            
axios.post("URL").then(
response => {
if(response.data == 1) {
// request successful, return response (Data)
callback(response);
}
else {
// request failed
// retry, if any retries left
if(retires > 0) {
request(--retires, callback);
}
else {
// no retries left, calling callback with error
callback([], "out of retries");
}
}
})
.catch(error => {
// ajax error occurred
if (retries > 0) {
request(--retries, callback);
}
else {
// no retries left
callback([], error);
}
});
}

callback function

  • Q3 의 callback function 과 비슷하게 parameter의 값을 확인해줍니다. 이 parameter 의 값으로는 error 혹은 data가 return 될 수 있습니다.

if error

  • return error

Else

  • print data
// your callback gets executed automatically once the data is receivedvar callback = (data, error) => {    
// consume data
if (error) {
console.error(error);
return;
}
console.log(data);
};

Thank you!!

--

--