함수 선언 vs 표현식 (Function Declaration vs Expression)

함수 선언 vs 표현식 (Function Declaration vs Expression)

자바스크립트(JavaScript)에서 함수(Function)는 코드를 재사용하고 구조화하는 데 핵심적인 역할을 한다. 함수(Function)를 정의하는 방법은 크게 함수 선언(Function Declaration)과 함수 표현식(Function Expression) 두 가지로 나뉜다. 이 두 방식은 문법적으로 다를 뿐만 아니라, 실행 컨텍스트(Execution Context) 내에서의 동작, 스코프(Scope) 처리, 호이스팅(Hoisting) 여부 등에서 중요한 차이를 보인다. 이 포스팅에서는 함수 선언(Function Declaration)과 함수 표현식(Function Expression)의 개념, 차이점, 사용 시나리오를 다루며, 실무에서 어떻게 활용되는지 예제를 통해 살펴본다.


함수 선언(Function Declaration)은 전통적인 방식으로 코드의 가독성을 높이는 데 유리하고, 함수 표현식(Function Expression)은 변수에 함수를 할당하여 유연성과 동적 처리를 가능하게 한다. 두 방식의 장단점을 이해하고 상황에 맞게 선택하면, 코드의 품질과 유지보수성을 향상시킬 수 있다.


함수 선언(Function Declaration)이란 무엇인가?

함수 선언(Function Declaration)은 자바스크립트(JavaScript)에서 함수(Function)를 정의하는 가장 기본적인 방식이다. function 키워드 뒤에 함수 이름과 매개변수(Parameter)를 명시하며, 함수 본문(Body)을 중괄호로 감싼다. 함수 선언(Function Declaration)은 독립적인 구문으로 작성되며, 실행 컨텍스트(Execution Context)에서 호이스팅(Hoisting)이 발생한다.


기본적인 함수 선언(Function Declaration) 예제는 다음과 같다:

function sayHello(name) {
    return `안녕, ${name}!`;
}
console.log(sayHello("철수")); // 안녕, 철수!

위 코드에서 sayHello는 함수 선언(Function Declaration)을 통해 정의되었으며, 코드 어디에서든 호출할 수 있다. 이는 함수 선언(Function Declaration)이 호이스팅(Hoisting) 덕분에 선언 위치와 상관없이 실행 컨텍스트(Execution Context) 생성 단계에서 초기화되기 때문이다. 예를 들어:

console.log(greet("영희")); // 안녕, 영희!
function greet(name) {
    return `안녕, ${name}!`;
}

위 예제에서 greet 함수는 선언 전에 호출되었지만, 호이스팅(Hoisting)으로 인해 정상적으로 실행된다. 자바스크립트(JavaScript) 엔진은 코드를 실행하기 전에 함수 선언(Function Declaration)을 스코프(Scope) 최상단으로 끌어올린다. 이는 변수 선언(var)과 비슷하지만, 변수는 초기화만 되고 값은 undefined인 반면, 함수 선언(Function Declaration)은 정의 전체가 호이스팅된다.


함수 선언(Function Declaration)의 또 다른 특징은 함수 이름이 필수라는 점이다. 이름 없는 함수는 선언 방식으로 정의할 수 없다. 이는 함수 선언(Function Declaration)이 독립적인 구문으로 동작하기 때문이며, 함수 이름은 스코프(Scope) 내에서 고유한 식별자로 사용된다. 아래는 함수 선언(Function Declaration)의 기본 구조를 다시 정리한 예제다:

function calculateSum(a, b) {
    let sum = a + b;
    console.log(`합계 계산 중: ${a} + ${b}`);
    return sum;
}
let result = calculateSum(5, 3);
console.log(`결과: ${result}`); // 합계 계산 중: 5 + 3 → 결과: 8

함수 선언(Function Declaration)은 코드의 가독성을 높이고, 전역 함수나 모듈(Module)에서 주요 기능을 정의할 때 주로 사용된다. 또한, 함수 선언(Function Declaration)은 실행 흐름에서 명확한 역할을 하므로, 초보자가 이해하기 쉬운 방식으로 설계되었다.


함수 표현식(Function Expression)이란 무엇인가?

함수 표현식(Function Expression)은 함수(Function)를 변수에 할당하는 방식으로 정의한다. function 키워드를 사용하지만, 함수 이름은 선택 사항이며, 변수 선언(var, let, const)과 결합된다. 함수 표현식(Function Expression)은 표현식(Expression)으로 취급되므로, 호이스팅(Hoisting) 동작이 함수 선언(Function Declaration)과 다르다.


기본적인 함수 표현식(Function Expression) 예제는 다음과 같다:

const sayHi = function(name) {
    return `안녕, ${name}!`;
};
console.log(sayHi("영희")); // 안녕, 영희!

위 코드에서 sayHi는 변수이며, 익명 함수(Anonymous Function)가 할당되었다. 함수 표현식(Function Expression)은 변수에 할당되기 때문에, 변수의 스코프(Scope)와 호이스팅(Hoisting) 규칙을 따른다. 예를 들어:

console.log(sayBye("철수")); // TypeError: sayBye is not a function
const sayBye = function(name) {
    return `안녕히 가세요, ${name}!`;
};

위 예제에서 sayByeconst로 선언되었으므로, 선언 전에 호출하면 에러가 발생한다. 이는 함수 표현식(Function Expression)이 호이스팅(Hoisting)되지 않는다는 뜻이 아니라, 변수 선언만 호이스팅되고 초기화는 실행 시점에 이루어지기 때문이다. var를 사용하면 다르게 동작한다:

console.log(sayLater("영희")); // undefined is not a function
var sayLater = function(name) {
    return `나중에 봐요, ${name}!`;
};

위 코드에서 sayLater는 호이스팅(Hoisting)으로 undefined로 초기화되며, 함수가 할당되기 전에 호출되면 에러가 발생한다. 함수 표현식(Function Expression)은 함수 이름을 생략할 수 있어 익명 함수(Anonymous Function)로 자주 사용되지만, 이름을 붙일 수도 있다(네임드 함수 표현식, Named Function Expression):

const factorial = function calc(n) {
    if (n <= 1) return 1;
    return n * calc(n - 1);
};
console.log(factorial(5)); // 120
// console.log(calc(5)); // ReferenceError: calc is not defined

위 예제에서 calc는 함수 내부에서 재귀 호출에 사용되지만, 외부 스코프(Scope)에서는 접근할 수 없다. 이는 함수 표현식(Function Expression)의 유연성을 보여준다.


함수 표현식(Function Expression)은 변수에 할당되므로 동적 프로그래밍에 유리하다. 예를 들어, 조건에 따라 다른 함수를 할당할 수 있다:

let operation;
if (Math.random() > 0.5) {
    operation = function(a, b) {
        return a + b;
    };
} else {
    operation = function(a, b) {
        return a * b;
    };
}
console.log(operation(2, 3)); // 5 또는 6 (랜덤)

이처럼 함수 표현식(Function Expression)은 런타임(Runtime)에서 함수(Function)를 동적으로 정의할 수 있어, 복잡한 로직에서 강력한 도구로 활용된다.


함수 선언(Function Declaration)과 함수 표현식(Function Expression)의 차이점

함수 선언(Function Declaration)과 함수 표현식(Function Expression)은 여러 측면에서 차이가 있다. 아래에서 주요 차이점을 상세히 분석한다.


1. 호이스팅(Hoisting) 동작

- 함수 선언(Function Declaration): 전체 함수가 호이스팅(Hoisting)되어 선언 위치와 상관없이 호출 가능.

sayBefore("철수"); // 안녕, 철수!
function sayBefore(name) {
    console.log(`안녕, ${name}!`);
}

- 함수 표현식(Function Expression): 변수 선언만 호이스팅(Hoisting)되며, 초기화는 실행 시점에 이루어진다.

sayAfter("영희"); // TypeError: sayAfter is not a function
const sayAfter = function(name) {
    console.log(`안녕, ${name}!`);
};

이 차이는 코드 실행 흐름에서 큰 영향을 미친다. 함수 선언(Function Declaration)은 코드 구조가 명확할 때 유리하고, 함수 표현식(Function Expression)은 실행 시점에 따라 동작을 제어할 때 유용하다.


2. 이름 유무

- 함수 선언(Function Declaration): 이름이 필수다.

function noName() { // 유효
    console.log("이름 있음");
}

- 함수 표현식(Function Expression): 이름이 선택 사항이다.

const anonymous = function() { // 익명
    console.log("이름 없음");
};
const named = function myFunc() { // 네임드
    console.log("이름 있음");
};

네임드 함수 표현식은 디버깅 시 호출 스택(Call Stack)에 함수 이름이 표시되어 유용하다.


3. 스코프(Scope)와 할당

- 함수 선언(Function Declaration): 독립적인 구문으로 스코프(Scope) 최상단에 정의된다.

if (true) {
    function inside() {
        console.log("내부");
    }
}
inside(); // 내부 (비엄격 모드)

- 함수 표현식(Function Expression): 변수에 할당되므로 스코프(Scope) 규칙을 따른다.

"use strict";
if (true) {
    const insideExpr = function() {
        console.log("내부");
    };
    insideExpr(); // 내부
}
// insideExpr(); // ReferenceError

엄격 모드(Strict Mode)에서는 함수 선언(Function Declaration)의 블록 스코프(Block Scope) 동작이 제한된다.


실무 예제

실무에서 함수 선언(Function Declaration)과 함수 표현식(Function Expression)을 어떻게 활용하는지 다양한 사례를 통해 살펴본다.


1. 모듈(Module) 내 주요 함수

function processData(data) {
    return data * 2;
}
export { processData };

함수 선언(Function Declaration)은 모듈(Module)에서 주요 기능을 정의할 때 명확하다.


2. 이벤트 핸들러

const handleClick = function(event) {
    console.log("클릭됨:", event.target);
};
document.querySelector("button").addEventListener("click", handleClick);

함수 표현식(Function Expression)은 이벤트 리스너에 동적으로 할당하기 좋다.


3. 즉시 실행 함수(IIFE)

(function() {
    console.log("즉시 실행");
})();

함수 표현식(Function Expression)은 IIFE로 초기화 로직을 캡슐화한다.


4. 조건적 함수 정의

let logger;
if (process.env.NODE_ENV === "development") {
    logger = function(msg) {
        console.log("DEV:", msg);
    };
} else {
    logger = function() {};
}
logger("테스트"); // 개발 환경에서만 출력

5. 재귀 함수

const fibonacci = function fib(n) {
    if (n <= 1) return n;
    return fib(n - 1) + fib(n - 2);
};
console.log(fibonacci(6)); // 8

6. 클래스(Class) 내부 메서드

class Calculator {
    add = function(a, b) {
        return a + b;
    };
}
const calc = new Calculator();
console.log(calc.add(2, 3)); // 5

성능과 선택 기준

장점

- 함수 선언(Function Declaration): 가독성, 호이스팅(Hoisting)으로 유연한 호출.

- 함수 표현식(Function Expression): 동적 할당, 스코프(Scope) 제어.


한계

- 함수 선언(Function Declaration): 블록 스코프(Block Scope)에서 혼란 가능.

- 함수 표현식(Function Expression): 선언 전 호출 불가.

주요 로직은 함수 선언(Function Declaration), 동적 로직은 함수 표현식(Function Expression)으로 선택한다.


마무리

함수 선언(Function Declaration)과 함수 표현식(Function Expression)은 자바스크립트(JavaScript)에서 함수(Function)를 정의하는 두 가지 핵심 방식이다. 각각의 특성을 이해하고 적절히 사용하면 코드의 효율성과 가독성을 높일 수 있다.


+ Recent posts