JavaScript
웹을 개발할 때 자바스크립트는 필수적으로 만나게 되는 언어입니다. 하지만, 자바스크립트가 주력 언어가 아니기에 소홀히 했고, 헷갈리는 문법이나 개념을 필요할 때마다 찾아보고 프로그래밍 해왔습니다. 그러다 보니 자바스크립트의 개념들이 파편적으로 흩어져 더욱 저를 혼란스럽게 만드는 느낌입니다. 그래서 이번 포스트에선 자바스크립트에 대해 헷갈리는 키워드를 정리하고자 합니다.
Var과 Let 그리고 const
var은 함수 범위(Function scope)이고 Let은 블록 범위(Block scope)를 가집니다. 그래서 var의 경우 같은 함수 안에 있는 변수는 공유하게 되며, 예상치 못한 값이 변수에 들어갈 우려가 있습니다.(golang에서 캡쳐 기능과 유사)
그리고, var 변수로 전역 변수를 설정하게 되면, window객체의 속성에 들어가게 됩니다. 또한, 중복 선언과 선언문 이전 접근이 가능합니다.
const는 let과 마찬가지로 블록 범위를 가지고, 중복 선언이 불가능합니다. 차이점은 const는 상수 선언이라는 점입니다.(하지만 객체의 속성은 변경이 가능하고 객체의 속성도 상수로 만들고 싶다면 Object.freeze함수를 사용)
this 와 함수, this와 에로우 함수
this는 현재 실행 중인 함수의 컨텍스트(객체)를 가리킵니다. 그리고, 객체에서 this는 해당 객체를 가리킵니다.
함수에서 사용되는 this의 경우, this는 함수를 호출한 객체에 바인딩(런타임 바인딩) 됩니다. 예를 들어, 함수a가 있다고 하면 object1.a 에서의 this는 obect1를 가리키고, object2.a 에서의 this는 object2를 가리킵니다.(object1과 object2는 a 함수를 대입받은 메서드 a가 존재합니다.) 하지만 callback 함수로써 함수a를 대입받고 callback함수를 실행시키면, 해당 콜백 함수 단독으로만 쓰이게 되어 전역 객체에 바인딩 되게 됩니다. 이러한 런타임 바인딩을 통제하려면 bind함수를 통해 해당 함수의 this를 고정적인 객체와 바인딩 시키면 됩니다.
그리고, 위에서 설명한 bind 과정을 통해 고정적인 객체와 바인딩하는 함수가 에로우 함수입니다. 에로우 함수는 호출 시점에서의 상위 영역의 this와 자동적으로 bind 하게 됩니다. 이때, 바인딩 우선 순위는 에로우 함수가 bind를 사용하는 방법보다 높습니다.
추가적으로, 전역 객체의 메서드에서 (a 함수 또는 object.a 메서드를 대입한)콜백 함수를 호출하는 경우, 콜백 함수는 호출 객체 또는 바인드에 따라 this가 정해져 있을 수 있습니다. 하지만, 에로우 함수를 콜백 함수로 사용할 경우 해당 상위 스코프에서 this를 가져옵니다.(우선순위가 높기 때문)
콜백 지옥과 Promise와 Async&Await
콜백 함수는 다른 함수의 인자로 전달 되는 함수를 말합니다. 주로 콜백 함수는 비동기 작업과 함께 쓰입니다. 비동기 함수가 끝나고 후처리를 하기 위해 비동기 콜백 함수를 사용합니다. 이때, 여러 비동기 작업을 순서대로 처리하는 코드를 작성할 때, 콜백 지옥에 빠지기 쉽습니다. 이러한 콜백 지옥은 코드를 읽기 어렵게 만들고 유지 보수하기 어렵게 만드는 단점이 있습니다.
프로미스는 자바스크립트에서 비동기 처리에 사용되는 객체입니다. 이 객체는 상태(state)와 결과(result)를 가집니다. 상태는 세 가지로 나뉘는데, ‘pending’은 작업이 아직 완료되지 않은 상태를 나타내고, ‘fulfilled’는 성공적으로 완료된 상태를, ‘rejected’는 실패한 상태를 나타냅니다. 각 상태에는 undefined, 결과값, 또는 에러가 result로 전달됩니다. 프로미스는 생성되는 즉시 실행됩니다.
then 함수는 프로미스가 ‘fulfilled’ 상태가 되었을 때 실행되는 콜백 함수를 실행합니다. 이 콜백 함수는 프로미스에서 전달한 결과값을 인자로 받을 수 있습니다. catch 함수는 프로미스가 ‘rejected’ 상태가 되었을 때 실행되는 콜백 함수로, 마찬가지로 프로미스에서 정의한 결과값을 인자로 받을 수 있습니다. 마지막으로, finally 함수는 프로미스 작업이 성공하든 실패하든 항상 실행됩니다.
프로미스의 then 및 catch 메서드 안에서는 return을 통해 새로운 프로미스를 전달할 수 있습니다. 이 경우 문자열을 반환하더라도 해당 문자열은 자동으로 프로미스로 감싸집니다. 이러한 방식을 프로미스 체이닝이라고 합니다. 프로미스 체이닝을 통해 콜백 지옥을 효과적으로 해결할 수 있습니다.
프로미스의 정적(static) 메서드로는 Promise.all과 Promise.allSettled가 있습니다. Promise.all은 여러 개의 프로미스를 배열로 받아 모든 프로미스가 성공적으로 완료되었을 때 성공을 반환하며, 하나라도 reject된 경우 전체를 reject합니다. 반면에 Promise.allSettled는 모든 프로미스가 완료된 후에 반환되며, 각 프로미스의 상태와 결과를 알려줍니다. 이를 통해 모든 프로미스의 성공과 실패를 추적할 수 있습니다.
Async와 Await을 사용하면 프로미스 체이닝 방식이 아닌 더 간결한 코드를 작성할 수 있습니다. Async은 함수 앞에 붙는 키워드로 해당 함수를 비동기 함수로 만들고, 결과값을 프로미스 객체로 만듭니다. 그리고 await 키워드는 비동기 함수들을 순차적으로 작업할 수 있습니다.