JavaScript 독학 가이드 5 - 함수와 스코프

learning by Seven Fingers Studio 16분
JavaScript함수스코프클로저화살표함수프로그래밍

똑같은 코드를 여러 번 쓰고 있다면? 함수로 만들어야 합니다. 함수는 코드를 재사용 가능한 덩어리로 만드는 거예요. 한번 만들어두면 필요할 때마다 불러 쓸 수 있죠.

함수가 뭔가요?

함수는 특정 작업을 수행하는 코드 묶음이에요.

// 함수 선언
function sayHello() {
    console.log("안녕하세요!");
}

// 함수 호출
sayHello(); // 안녕하세요!
sayHello(); // 안녕하세요!
sayHello(); // 안녕하세요!

실행 결과:

> sayHello()
안녕하세요!
> sayHello()
안녕하세요!
> sayHello()
안녕하세요!

한 번 만들어두면 몇 번이고 재사용할 수 있어요.

함수 선언 방법

1. function 키워드

function greet() {
    console.log("환영합니다!");
}

greet(); // 환영합니다!

실행 결과:

> greet()
환영합니다!

2. 함수 표현식

const greet = function() {
    console.log("환영합니다!");
};

greet(); // 환영합니다!

3. 화살표 함수 (ES6+)

const greet = () => {
    console.log("환영합니다!");
};

greet(); // 환영합니다!

화살표 함수가 요즘 가장 많이 쓰여요. 짧고 간결하거든요.

매개변수와 인자

함수에 값을 전달할 수 있어요.

function greet(name) {
    console.log(`안녕하세요, ${name}님!`);
}

greet("철수"); // 안녕하세요, 철수님!
greet("영희"); // 안녕하세요, 영희님!

실행 결과:

> greet("철수")
안녕하세요, 철수님!
> greet("영희")
안녕하세요, 영희님!
  • 매개변수(parameter): 함수 선언 시 받을 값 (name)
  • 인자(argument): 함수 호출 시 전달하는 값 ("철수")

여러 매개변수

function add(a, b) {
    console.log(a + b);
}

add(5, 3); // 8
add(10, 20); // 30

실행 결과:

> add(5, 3)
8
> add(10, 20)
30

기본값 설정

function greet(name = "손님") {
    console.log(`안녕하세요, ${name}님!`);
}

greet("철수"); // 안녕하세요, 철수님!
greet(); // 안녕하세요, 손님님! (기본값 사용)

실행 결과:

> greet("철수")
안녕하세요, 철수님!
> greet()
안녕하세요, 손님님!

반환값 (return)

함수가 결과를 돌려줄 수 있어요.

function add(a, b) {
    return a + b; // 결과를 반환
}

let result = add(5, 3);
console.log(result); // 8

실행 결과:

> console.log(result)
8

return 이후의 코드는 실행되지 않아요.

function test() {
    console.log("1");
    return;
    console.log("2"); // 실행 안 됨
}

test(); // 1만 출력

실행 결과:

> test()
1

화살표 함수 단축 문법

// 일반 형태
const add = (a, b) => {
    return a + b;
};

// 한 줄이면 중괄호와 return 생략 가능
const add = (a, b) => a + b;

// 매개변수가 하나면 괄호도 생략 가능
const double = n => n * 2;

console.log(add(5, 3)); // 8
console.log(double(4)); // 8

실행 결과:

> console.log(add(5, 3))
8
> console.log(double(4))
8

스코프 (유효 범위)

변수가 어디서 접근 가능한지를 결정하는 규칙이에요.

전역 스코프

let globalVar = "전역 변수";

function test() {
    console.log(globalVar); // 접근 가능
}

test(); // 전역 변수
console.log(globalVar); // 전역 변수

실행 결과:

> test()
전역 변수
> console.log(globalVar)
전역 변수

지역 스코프

function test() {
    let localVar = "지역 변수";
    console.log(localVar); // 접근 가능
}

test(); // 지역 변수
console.log(localVar); // 에러! 밖에서 접근 불가

블록 스코프 (let, const)

if (true) {
    let x = 10;
    const y = 20;
    var z = 30;
}

console.log(x); // 에러! let은 블록 밖에서 접근 불가
console.log(y); // 에러! const도 마찬가지
console.log(z); // 30 (var는 블록 스코프 무시)

이래서 var 안 쓰는 거예요!

클로저 (Closure)

함수가 선언된 환경을 기억하는 거예요.

function makeCounter() {
    let count = 0;

    return function() {
        count++;
        return count;
    };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

실행 결과:

> console.log(counter())
1
> console.log(counter())
2
> console.log(counter())
3

count 변수가 함수 밖에서도 유지돼요. 이게 클로저입니다.

실용적인 예제:

function createGreeting(greeting) {
    return function(name) {
        return `${greeting}, ${name}!`;
    };
}

const sayHello = createGreeting("Hello");
const sayHi = createGreeting("Hi");

console.log(sayHello("철수")); // Hello, 철수!
console.log(sayHi("영희")); // Hi, 영희!

실행 결과:

> console.log(sayHello("철수"))
Hello, 철수!
> console.log(sayHi("영희"))
Hi, 영희!

실전 예제

1. 계산기 함수

const calculator = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b,
    divide: (a, b) => a / b
};

console.log(calculator.add(10, 5)); // 15
console.log(calculator.divide(10, 2)); // 5

실행 결과:

> console.log(calculator.add(10, 5))
15
> console.log(calculator.divide(10, 2))
5

2. 배열 필터링

function filterEven(numbers) {
    return numbers.filter(n => n % 2 === 0);
}

let nums = [1, 2, 3, 4, 5, 6];
console.log(filterEven(nums)); // [2, 4, 6]

실행 결과:

> console.log(filterEven(nums))
[2, 4, 6]

3. 검증 함수

function isValidEmail(email) {
    return email.includes("@") && email.includes(".");
}

console.log(isValidEmail("test@gmail.com")); // true
console.log(isValidEmail("test")); // false

실행 결과:

> console.log(isValidEmail("test@gmail.com"))
true
> console.log(isValidEmail("test"))
false

4. 재사용 가능한 인사 함수

function createGreeter(greeting, punctuation = "!") {
    return function(name) {
        return `${greeting}, ${name}${punctuation}`;
    };
}

const formal = createGreeter("Good morning", ".");
const casual = createGreeter("Hey");

console.log(formal("Mr. Kim")); // Good morning, Mr. Kim.
console.log(casual("철수")); // Hey, 철수!

실행 결과:

> console.log(formal("Mr. Kim"))
Good morning, Mr. Kim.
> console.log(casual("철수"))
Hey, 철수!

콜백 함수

다른 함수의 인자로 전달되는 함수예요.

function processArray(arr, callback) {
    for (let item of arr) {
        callback(item);
    }
}

processArray([1, 2, 3], (num) => {
    console.log(num * 2);
});
// 출력: 2, 4, 6

실행 결과:

> processArray([1, 2, 3], (num) => { console.log(num * 2); })
2
4
6

배열 메서드에서 많이 씁니다.

let numbers = [1, 2, 3, 4, 5];

// map에 콜백 함수 전달
let doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

실행 결과:

> console.log(doubled)
[2, 4, 6, 8, 10]

자주 하는 실수

1. return 빠뜨리기

function add(a, b) {
    a + b; // return 없으면 undefined 반환
}

console.log(add(5, 3)); // undefined

실행 결과:

> console.log(add(5, 3))
undefined

2. 함수 호출 시 괄호 빠뜨리기

function greet() {
    return "Hello!";
}

console.log(greet); // 함수 자체 출력
console.log(greet()); // "Hello!" (올바름)

실행 결과:

> console.log(greet)
[Function: greet]
> console.log(greet())
Hello!

3. 화살표 함수에서 this 헷갈림

const obj = {
    name: "철수",
    sayHi: function() {
        console.log(`Hi, ${this.name}`);
    },
    sayBye: () => {
        console.log(`Bye, ${this.name}`); // undefined
    }
};

obj.sayHi(); // Hi, 철수
obj.sayBye(); // Bye, undefined (화살표 함수는 this 바인딩 다름)

다음 단계

다음 글에서는 객체와 메서드를 배웁니다. 관련된 데이터와 함수를 하나로 묶어 관리하는 방법을 알아볼 거예요.

함수는 JavaScript의 핵심이에요. 프로그래밍의 90%가 함수로 이루어져 있다고 해도 과언이 아니에요!


JavaScript 독학 가이드 시리즈:

← 블로그 목록으로