이 글에서는 JavaScript에서 함수 선언문(Function Declaration)과 함수 표현식(Function Expression)의 차이점을 자세히 알아봅니다.
먼저, 다음은 함수 표현식의 예시입니다:
// 함수 표현식
let executeMe = function () {
console.log('Function Expression')
}
그리고 다음은 함수 선언문입니다:
// 함수 선언문
function executeMe() {
console.log('Function Declaration')
}
두 방식은 기능적으로 거의 동일합니다. 위 예제 모두 executeMe() 형태로 호출하면 코드 블록 {..} 내부의 코드가 그대로 실행됩니다.
하지만 분명한 차이가 존재합니다. 무엇을 하느냐가 아니라 어떤 순서로 읽히고 실행되느냐의 차이이며, 이 차이는 실제 개발에서 매우 중요합니다.
다음 코드는 정상적으로 실행됩니다:
// 함수 선언문 호출
executeMe()
// 함수 선언문
function executeMe() {
console.log('Function Declaration')
}
반면, 다음 코드는 에러를 발생시키며 실행되지 않습니다:
// 함수 호출
executeMe()
// 함수 표현식
let executeMe = function () {
console.log('Function Expression')
}
왜 첫 번째 예제(함수 선언문)는 동작하고 두 번째 예제(함수 표현식)는 실패하는 걸까요? 이를 이해하려면 JavaScript의 호이스팅(Hoisting) 개념을 먼저 알아야 합니다.
호이스팅(Hoisting)이란?
JavaScript에서 코드는 기본적으로 위에서 아래로 순차적으로 실행됩니다. 일반적인 변수는 스크립트 파일에서 선언된 이후에만 사용할 수 있지만, 함수 선언문은 브라우저가 코드를 실행하기 전에 미리 읽혀 최상단으로 끌어올려집니다(hoisted). 이것이 바로 호이스팅입니다.
즉, 브라우저의 코드 인터프리터는 함수 선언문이 호출되기 전에 항상 그 존재를 먼저 인지하고 있는 것입니다.
덕분에 함수 선언문은 스크립트 파일 내 어디에서든 호출할 수 있으며, 심지어 물리적으로 함수가 정의된 위치보다 앞에서 호출해도 정상 작동합니다. 이 점이 함수 선언문만의 고유한 특징입니다.
함수 선언문의 동작 방식
브라우저는 코드를 실행하기 전에 JS 스크립트 파일 전체를 먼저 로드(읽기)합니다. 이때 함수 선언문은 실행 순서의 최상단으로 끌어올려지므로, 인터프리터는 실제로 다음과 같은 순서로 코드를 처리합니다:
// 1. 함수 선언문
function executeMe() {
console.log('Function Declaration')
}
// 2. 함수 선언문 호출
executeMe()
심지어 코드가 물리적으로 반대 순서로 작성되어 있어도 결과는 같습니다:
// 함수 선언문 호출
executeMe()
// 함수 선언문
function executeMe() {
console.log('Function Declaration')
}
이것이 함수 선언문의 호출 위치가 정의보다 앞이든 뒤든 상관없는 이유입니다. JavaScript는 항상 함수 선언문을 먼저 로드한 뒤에 해당 함수의 호출을 실행하기 때문입니다.
함수 표현식은 호이스팅되지 않습니다
함수 표현식은 호이스팅되지 않습니다. 즉, 미리 읽히지 않으며 인터프리터가 해당 코드 줄에 도달했을 때 비로소 읽힙니다. 따라서 함수 표현식이 호출 코드보다 아래에 위치한다면, 브라우저의 인터프리터는 이를 실행할 수 없습니다.
그래서 다음 코드는 동작하지 않습니다:
// 함수 표현식 호출
executeMe()
// 함수 표현식
let executeMe = function () {
console.log('Function Expression')
}
위 코드에서 에러가 발생하는 이유는, 브라우저의 인터프리터가 함수 표현식의 존재조차 인지하기 전에 함수(executeMe())를 실행하려 했기 때문입니다.
코드 순서를 바꾸면 정상적으로 동작합니다:
// 함수 표현식
let executeMe = function () {
console.log('Function Expression')
}
// 함수 표현식 호출
executeMe()
위 코드가 작동하는 이유는 코드 순서 때문입니다. 브라우저는 위에서 아래로 읽으면서 먼저 함수 표현식이 담긴 변수 let executeMe를 읽은 후, 그다음 줄에서 executeMe() 호출을 만나 실행하게 됩니다.
그렇다면 어떤 방식을 사용해야 할까?
사실 함수 선언문과 함수 표현식 중 무엇을 선택하느냐 자체가 가장 중요한 것은 아닙니다. 어느 쪽을 선택하더라도 커리어를 좌우하지는 않으니까요. 훨씬 중요한 것은 일관성입니다. 하나의 스타일을 정했다면, 적어도 같은 프로젝트 내에서는 그 스타일을 끝까지 유지하세요.
더욱 중요한 것은 팀워크입니다. 만약 당신이 함수 표현식을 사용하는 팀에 합류한다면, 개인적 선호와 관계없이 팀의 컨벤션을 따르는 것이 코드베이스의 일관성을 지키는 길입니다.
함수 표현식을 사용하는 좋은 이유 중 하나는, 더 엄격한 코드 구조를 강제하기 때문에 코드가 더 예측 가능해진다는 점입니다. 반면 함수 선언문은 스크립트 어디서든 호출할 수 있어 유연하지만, 여러 개발자가 함께 작업하는 프로젝트에서는 오히려 일관성 없는 코드 구조로 이어질 수도 있습니다.
함수 선언문을 사용하는 것이 합리적인 경우(논쟁의 여지는 있습니다)는, 여러 함수가 서로를 호출하는 복잡한 구조에서 호출 순서 때문에 함수 표현식이 제대로 실행되지 않아 혼란스러울 때입니다.