자바스크립트의 함수 정의 방식: 선언식 vs 표현식 완벽 가이드
자바스크립트에서 함수를 정의하는 방법은 여러 가지가 있습니다. 그중에서도 가장 기본이 되는 두 가지 방식인 '함수 선언식'과 '함수 표현식'의 차이점을 명확히 이해하는 것은 자바스크립트 개발자라면 반드시 알아야 할 기초 지식입니다. 이번 글에서는 두 방식의 차이점과 각각의 장단점, 그리고 실무에서 어떻게 활용하는지 알아보겠습니다.
함수 선언식(Function Declaration)
함수 선언식은 function
키워드로 시작하여 함수 이름을 명시적으로 지정하는 방식입니다.
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 5
함수 선언식의 가장 큰 특징은 호이스팅(Hoisting)이 발생한다는 점입니다. 호이스팅이란 자바스크립트 엔진이 코드를 실행하기 전에 함수 선언을 스코프의 최상단으로 끌어올리는 현상을 말합니다.
// 함수가 선언되기 전에 호출해도 정상적으로 작동합니다.
console.log(add(2, 3)); // 5
function add(a, b) {
return a + b;
}
위 코드에서 add
함수는 선언되기 전에 호출되었지만, 호이스팅 덕분에 정상적으로 작동합니다. 자바스크립트 엔진이 코드를 실행하기 전에 함수 선언을 메모리에 미리 로드했기 때문입니다.
함수 선언식의 특징
- 함수에 반드시 이름이 필요합니다.
- 호이스팅이 발생하여 코드 내 어디서든 호출할 수 있습니다.
- 코드 구조가 명확하여 가독성이 좋습니다.
- 함수 자체가 스코프의 최상단으로 호이스팅되기 때문에 전체 함수 본문이 메모리에 저장됩니다.
함수 표현식(Function Expression)
함수 표현식은 함수를 변수에 할당하는 방식입니다. 함수 자체는 익명(이름이 없는)일 수도 있고, 이름이 있을 수도 있습니다.
// 익명 함수 표현식
const multiply = function(a, b) {
return a * b;
};
// 기명 함수 표현식
const divide = function division(a, b) {
return a / b;
};
console.log(multiply(2, 3)); // 6
console.log(divide(6, 2)); // 3
함수 표현식의 중요한 특징은 변수 호이스팅의 규칙을 따른다는 점입니다. 변수 자체는 호이스팅되지만, 변수에 할당된 함수는 할당 이후에만 사용할 수 있습니다.
console.log(multiply(2, 3)); // Error: multiply is not a function
// var의 경우: TypeError: multiply is not a function
// let/const의 경우: ReferenceError: Cannot access 'multiply' before initialization
var multiply = function(a, b) {
return a * b;
};
이 예제에서 multiply
변수는 호이스팅되지만, 실제 함수가 할당되는 시점은 코드가 해당 줄에 도달했을 때입니다. 따라서 함수 표현식으로 정의된 함수는 선언 이후에만 호출할 수 있습니다.
함수 표현식의 특징
- 변수에 할당하는 형태로 정의됩니다.
- 함수 선언과 달리 호이스팅이 완전히 이루어지지 않습니다(변수 호이스팅만 발생).
- 함수를 값처럼 다룰 수 있어 유연성이 높습니다.
- 콜백 함수나 즉시 실행 함수(IIFE) 패턴에 주로 사용됩니다.
함수 선언식 vs 함수 표현식: 주요 차이점
두 방식의 주요 차이점을 표로 정리해보겠습니다.
특성 | 함수 선언식 | 함수 표현식 |
---|---|---|
호이스팅 | 전체 함수가 호이스팅됨 | 변수만 호이스팅됨 (함수 본문은 호이스팅되지 않음) |
사용 시점 | 코드 내 어디서든 호출 가능 | 함수 할당 이후에만 호출 가능 |
이름 필수 여부 | 함수 이름 필수 | 이름 선택적(주로 익명 함수 사용) |
재할당 가능성 | 재할당 불가 | 변수를 통해 재할당 가능 |
유연성 | 유연성 낮음 | 유연성 높음(값으로 전달 가능) |
주요 사용 사례 | 메인 함수 정의 | 콜백, 즉시 실행 함수(IIFE), 클로저 |
실무에서의 활용: 언제 어떤 방식을 사용해야 할까?
함수 선언식이 적합한 경우
코드의 가독성과 구조화가 중요한 경우
- 주요 기능을 하는 함수들을 명확하게 구분하고 싶을 때
- 코드를 위에서 아래로 읽으면서 이해하기 쉽게 구성하고 싶을 때
함수를 선언 전에 호출해야 하는 경우
- 상호 참조가 필요한 유틸리티 함수들
- 재귀 함수 구현 시
// 전역 유틸리티 함수로 적합
function formatCurrency(amount) {
return new Intl.NumberFormat('ko-KR', {
style: 'currency',
currency: 'KRW'
}).format(amount);
}
// 재귀 함수로 적합
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
함수 표현식이 적합한 경우
함수를 값으로 사용해야 하는 경우
- 함수를 다른 함수의 인자로 전달할 때(콜백 함수)
- 함수를 객체의 속성으로 저장할 때(메서드)
- 함수를 다른 함수의 반환값으로 사용할 때(클로저)
조건부 함수 정의가 필요한 경우
- 런타임에 조건에 따라 다른 함수를 할당해야 할 때
즉시 실행 함수(IIFE) 패턴 사용 시
// 콜백 함수로 적합
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(num) {
return num * 2;
});
// 조건부 함수 정의에 적합
const getDiscount = function(isVIP) {
if (isVIP) {
return function(price) {
return price * 0.8; // VIP 20% 할인
};
} else {
return function(price) {
return price * 0.95; // 일반 5% 할인
};
}
};
// 즉시 실행 함수(IIFE)
const config = (function() {
const apiKey = "secret_key";
return {
getAPIKey: function() {
return apiKey;
}
};
})();
화살표 함수: 함수 표현식의 현대적 대안
ES6에서 도입된 화살표 함수(Arrow Function)는 함수 표현식의 간결한 형태로, 특히 콜백 함수에 많이 사용됩니다.
// 기존 함수 표현식
const add = function(a, b) {
return a + b;
};
// 화살표 함수
const add = (a, b) => a + b;
// 콜백에서의 활용
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
화살표 함수는 함수 표현식과 마찬가지로 호이스팅 측면에서 같은 규칙을 따르지만, this
바인딩 등에서 차이가 있습니다.
실제 개발 시 권장사항
모던 자바스크립트 개발에서는 다음과 같은 접근 방식이 일반적으로 권장됩니다:
주요 유틸리티 함수와 재귀 함수는 함수 선언식으로 작성
- 명확한 이름 부여와 호이스팅 활용 가능
컴포넌트 내 메서드는 함수 표현식(화살표 함수) 사용
- 간결함과 명확한 스코프 바인딩 활용
콜백 함수는 함수 표현식(화살표 함수) 사용
- 간결함과 명확한 스코프 바인딩 활용
// 권장 패턴 예시
// 1. 핵심 유틸리티 - 함수 선언식
function calculateTax(amount, taxRate) {
return amount * taxRate;
}
// 2. 컴포넌트 내 메서드 - 함수 표현식(화살표 함수)
const UserProfile = {
user: { name: '홍길동', age: 30 },
formatUser: () => {
return `${UserProfile.user.name}, ${UserProfile.user.age}세`;
}
};
// 3. 콜백 함수 - 함수 표현식(화살표 함수)
document.addEventListener('click', (event) => {
console.log('문서가 클릭되었습니다.', event.target);
});
함수 정의 방식의 성능 차이
많은 개발자들이 궁금해하는 것 중 하나는 함수 선언식과 표현식 간의 성능 차이입니다. 현대 자바스크립트 엔진에서는 두 방식 간 성능 차이는 무시할 수 있을 정도로 미미합니다.
다만, 함수 선언식은 코드가 실행되기 전에 메모리에 로드되므로 초기 파싱 단계에서 약간의 오버헤드가 있을 수 있지만, 이후 함수 호출 시점에서는 차이가 없습니다. 따라서 성능보다는 코드의 가독성, 유지보수성, 로직의 명확성 등을 기준으로 적절한 방식을 선택하는 것이 좋습니다.
함수 이름과 디버깅
기명 함수(이름이 있는 함수)는 디버깅 시 스택 트레이스에 함수 이름이 표시되어 오류를 추적하기 쉽다는 장점이 있습니다.
// 익명 함수 표현식
const errorFn1 = function() {
throw new Error('에러 발생!');
};
// 기명 함수 표현식
const errorFn2 = function namedFunction() {
throw new Error('에러 발생!');
};
// 디버깅 시 차이점
// errorFn1() 호출 시: Error: 에러 발생! at anonymous function
// errorFn2() 호출 시: Error: 에러 발생! at namedFunction
복잡한 애플리케이션이나 라이브러리 개발 시에는 이러한 점을 고려하는 것이 좋습니다.
결론
함수 선언식과 함수 표현식은 각각 고유의 특성과 장단점을 가지고 있습니다. 핵심적인 차이는 호이스팅의 동작 방식에 있으며, 이로 인해 코드 구조와 실행 흐름에 영향을 미칩니다.
- 함수 선언식은 코드의 구조화와 가독성이 중요한 경우, 또는 함수를 선언 전에 호출해야 하는 경우에 적합합니다.
- 함수 표현식은 함수를 값으로 다루어야 하는 경우나 콜백, 클로저, 즉시 실행 함수 패턴에 적합합니다.
어떤 방식을 선택하든, 일관성을 유지하고 팀의 코딩 컨벤션을 따르는 것이 중요합니다. 코드의 목적과 컨텍스트를 고려하여 가장 적합한 방식을 선택하면 더 명확하고 유지보수가 용이한 코드를 작성할 수 있습니다.
모던 자바스크립트에서는 함수 표현식, 특히 화살표 함수의 사용이 증가하는 추세이지만, 함수 선언식 역시 여전히 중요한 역할을 하고 있습니다. 두 방식을 적재적소에 활용하여 더 나은 자바스크립트 코드를 작성해보세요.