
function createAdvancedFilter(minValue, maxValue) {
return function(arr) {
return arr.filter(function(num) {
return num > minValue && num < maxValue;
});
};
}
const filterBetween10and50 = createAdvancedFilter(10, 50);
console.log(filterBetween10and50([5, 12, 8, 130, 44])); // [12, 44]
클로저를 한마디로 정의하면
외부 함수 내에서 정의된 내부 함수가 외부 함수의 변수에 접근할수있다.
대표사진에서 보이는 선물 포장의사진은 일종의 감싸는 것을 표현하는거같습니다. (스코프를 감싼)
그렇다면 클로저를 이용하여 구현을하면 좋은 이유가 뭘까요?
클로저를 이용하여 구현하면 좋은 이유는 "상태를 감싸고 안전하게 보호하는" 기능을 제공하기 때문입니다.
선물 포장의 이미지처럼, 클로저는 데이터를 감싸서 외부로부터 보호하고, 필요한 부분만 노출시킬 수 있게 해줍니다.
이렇게 함으로써 프로그램의 캡슐화와 데이터 은닉을 효과적으로 구현할 수 있습니다.
구체적으로, 클로저를 이용하여 구현하는 장점은 다음과 같습니다:
클로저의 장점을 다섯 가지 유형으로 나누어 예시와 함께 살펴보겠습니다.
각 장점은 실용적이고 구체적인 상황에서 클로저를 사용할 이유를 설명하는 데 도움이 될 것입니다.
상태 유지와 추적: 클로저를 사용하면 함수가 실행된 후에도 변수의 상태를 유지할 수 있습니다. 이로 인해 상태를 추적하고, 함수 간에 상태를 공유하거나 변경된 값을 기억하는 데 유용합니다. 예를 들어, 카운터나 상태 관리가 필요한 로직을 간결하게 구현할 수 있습니다.
데이터 은닉과 캡슐화: 클로저를 사용하면 외부에서 직접 접근할 수 없는 상태를 내부에 숨길 수 있습니다. 이는 객체 지향에서의 캡슐화와 비슷한 개념으로, 중요한 데이터나 상태를 보호하면서 필요한 기능만 외부에 노출할 수 있게 해줍니다.
함수형 프로그래밍에서의 유용성: 클로저는 함수형 프로그래밍에서 중요한 개념으로, 불변성(immutable)과 고차 함수(higher-order functions)와 잘 맞아, 더 선언적이고 깔끔한 코드를 작성할 수 있게 도와줍니다.
간결한 코드: 클로저를 활용하면 코드를 더 간결하고, 모듈화하여 재사용성을 높일 수 있습니다. 복잡한 상태 관리나 콜백을 보다 쉽게 다룰 수 있습니다.
비동기 처리에서 유용: 클로저는 비동기 코드에서 특정 상태를 유지할 때 매우 유용합니다. 예를 들어, setTimeout이나 Promise와 함께 사용할 때, 클로저는 상태나 데이터를 유지하면서 비동기 작업이 완료될 때까지 해당 데이터를 사용할 수 있도록 해줍니다.
클로저의 장점을 다섯 가지 유형으로 나누어 예시와 함께 살펴보겠습니다.
각 장점은 실용적이고 구체적인 상황에서 클로저를 사용할 이유를 설명하는 데 도움이 될 것입니다.
1. 상태 유지와 추적
클로저를 사용하지 않았을 때와 클로저를 사용했을 때의 예제를 비교해 보겠습니다.
1. 클로저를 사용하지 않은 예제
function advancedFilter(arr, minValue, maxValue) {
return arr.filter(function(num) {
return num > minValue && num < maxValue;
});
}
console.log(advancedFilter([5, 12, 8, 130, 44], 10, 50)); // [12, 44]
- advancedFilter 함수는 arr, minValue, maxValue를 매개변수로 받아서 조건에 맞는 숫자들만 필터링해서 반환합니다.
- minValue와 maxValue는 매번 함수 호출 시마다 외부에서 전달받아야 하므로, 함수 호출 시마다 조건을 명시적으로 넣어줘야 합니다.
- 이 방식의 문제점은 매번 minValue, maxValue를 전달해야 한다는 점입니다. 여러 번 같은 조건을 사용할 경우 코드가 반복되고 관리하기 불편할 수 있습니다.
2. 클로저를 사용한 예제
function createAdvancedFilter(minValue, maxValue) {
return function(arr) {
return arr.filter(function(num) {
return num > minValue && num < maxValue;
});
};
}
const filterBetween10and50 = createAdvancedFilter(10, 50);
console.log(filterBetween10and50([5, 12, 8, 130, 44])); // [12, 44]
- createAdvancedFilter 함수는 minValue와 maxValue를 미리 설정하고, 이를 기억하는 내부 함수를 반환합니다.
- 이 내부 함수는 arr를 인자로 받아 필터링을 수행합니다.
- filterBetween10and50는 클로저로 minValue와 maxValue를 기억하고 있어, 반복적으로 같은 범위의 필터링을 할 때마다 매개변수를 전달할 필요가 없습니다.
- 이렇게 하면 조건이 고정된 필터를 여러 번 재사용할 수 있어 코드가 더 간결하고 유연해집니다.
클로저가 제공하는 상태를 기억하는 특성은 상태가 중요한 로직에서 큰 장점을 제공합니다.
주로 조건에 맞는 데이터만 필터링하거나, 반복적으로 사용해야 할 필터 조건을 고정하고 재사용할 때 클로저의 장점이 크게 발휘됩니다.
2. 데이터 은닉과 캡슐화
클로저는 함수가 외부 상태를 "기억"하고, 상태를 은닉하고 관리할 수 있도록 도와주는 강력한 도구입니다.
이를 활용하면 객체의 내부 데이터와 메서드를 안전하게 숨기고, 외부에서 해당 데이터에 접근하거나 수정하는 방식에 제약을 두는 캡슐화와 데이터 은닉을 구현할 수 있습니다.
캡슐화를 위한 클로저 예시
function createCounter() {
let count = 0; // 내부 상태는 외부에서 접근할 수 없음
return {
increment() {
count++;
console.log(count);
},
decrement() {
if (count > 0) {
count--;
console.log(count);
} else {
console.log("Count cannot go below 0");
}
},
getCount() {
return count; // count 값은 메서드를 통해서만 접근 가능
}
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1
console.log(counter.getCount()); // 1
- count 변수는 클로저 내부에 숨겨져 있으며, 외부에서 직접 접근할 수 없습니다. 대신 increment, decrement, getCount 메서드를 통해서만 count 값을 변경하거나 조회할 수 있습니다.
- 이렇게 함으로써 캡슐화가 구현되어, 외부에서는 count 값을 직접 변경할 수 없고, 제공된 메서드를 통해서만 상태를 변경할 수 있게 됩니다.
3. 함수형 프로그래밍에서의 유용성
클로저는 상태를 캡슐화하고 외부로부터 격리하기 때문에 함수 합성에 매우 유용합니다.
* 함수형 프로그래밍에서 함수 합성은 두 개 이상의 함수를 결합하여 새로운 함수를 만드는 방식입니다.
각각의 함수가 자신의 내부 상태를 관리하면서 외부와의 의존을 최소화합니다.
function add(x) {
return function(y) {
return x + y; // 'x'와 'y'를 더해서 반환
};
}
function multiply(x) {
return function(y) {
return x * y; // 'x'와 'y'를 곱해서 반환
};
}
// add5는 'x'가 5인 add 함수의 반환값
const add5 = add(5); // 'y'가 들어올 자리가 마련됨
// multiplyBy3은 'x'가 3인 multiply 함수의 반환값
const multiplyBy3 = multiply(3); // 'y'가 들어올 자리가 마련됨
// add5(2)는 'y'가 2로 들어가서 5 + 2 = 7을 반환
// 그 결과 7을 multiplyBy3(7)로 넘기면, 'y'는 7이 되고 3 * 7 = 21을 계산
const result = multiplyBy3(add5(2)); // (2 + 5) * 3
console.log(result); // 21
- 이 코드에서는 클로저가 사용되어 x 값을 기억하는 함수가 반환됩니다. 클로저는 반환된 함수가 외부 함수의 변수 (x)를 기억하고 있는 구조입니다.
- 이 코드는 함수형 프로그래밍의 핵심 개념인 고차 함수, 클로저, 함수 합성, 재사용성, 유연성 등을 잘 활용하여 여러 장점을 제공합니다. 특히, 상태를 캡슐화하고 유연하게 함수 조합을 할 수 있다는 점에서 매우 강력한 방식입니다. 이를 통해 코드의 유지보수성과 확장성을 높이고, 가독성도 향상시킬 수 있습니다.
4. 간결한 코드
클로저는 상태 관리와 콜백 처리를 간편하게 만들 수 있기 때문에, 코드를 간결하고 모듈화하며 재사용성을 높일수있습니다.
function fetchData(callback) {
const data = "비동기 데이터"; // 예시로 데이터를 만들어 놓습니다.
setTimeout(() => {
callback(data); // 일정 시간 후에 데이터를 전달합니다.
}, 1000);
}
function createLogger() {
return function(message) {
console.log(`Log: ${message}`);
};
}
const log = createLogger(); // 로그를 출력하는 함수 생성
fetchData(log); // 데이터를 받으면 로그 출력
- createLogger는 클로저를 사용하여 log라는 간단한 로깅 기능을 반환하는 함수입니다. 이 함수는 나중에 호출될 때 동적으로 로그를 출력합니다.
- fetchData 함수는 콜백을 받습니다. setTimeout을 사용하여 비동기적으로 데이터를 가져온 후, 데이터를 처리할 callback 함수가 호출됩니다.
- 간결함: createLogger로 생성된 log 함수는 fetchData에서 비동기적으로 반환되는 데이터를 간단히 처리할 수 있도록 도와줍니다.
- 모듈화: log 함수는 재사용이 가능하며, 다른 작업에서도 동일한 로깅 기능을 사용할 수 있습니다.
5. 비동기 처리에서 유용
상태 유지 덕분에 여러 비동기 작업이 동시에 실행되더라도, 각 작업이 독립적으로 상태를 관리할 수 있습니다.
function asyncTask(message) {
let result = 0;
return function(callback) {
setTimeout(() => {
result++;
callback(`${message}: ${result}`);
}, 1000);
};
}
const task1 = asyncTask('Task 1');
const task2 = asyncTask('Task 2');
task1((message) => {
console.log(message); // Task 1: 1
});
task2((message) => {
console.log(message); // Task 2: 1
});
각 asyncTask 함수는 자신만의 result 상태를 클로저를 통해 관리합니다. 이로 인해 각 작업의 컨텍스트를 별도로 유지할 수 있습니다.
6. 리액트에서 동적 폼 관리 (클로저 활용)
import React, { useState } from 'react';
function DynamicForm() {
const [formData, setFormData] = useState({ name: '', email: '' });
// handleChange 함수는 동적 필드를 처리하는 함수
const handleChange = (field) => {
return (e) => {
setFormData({
...formData,
[field]: e.target.value, // e.target.value로 입력된 값 받아오기
});
};
};
return (
<div>
<input
type="text"
placeholder="Name"
value={formData.name}
onChange={handleChange('name')} // 동적으로 name 필드의 핸들러 생성
/>
<input
type="email"
placeholder="Email"
value={formData.email}
onChange={handleChange('email')} // 동적으로 email 필드의 핸들러 생성
/>
<p>Name: {formData.name}</p>
<p>Email: {formData.email}</p>
</div>
);
}
export default DynamicForm;
- handleChange 함수는 클로저를 활용하여 동적 폼 필드에 대해 상태를 추적하고 관리할 수 있습니다.
- handleChange 함수 내부에서 formData와 field를 클로저로 기억하고 있으며, 이로 인해 각 필드에 대해 적절한 값을 업데이트하는 데 유용합니다.
클로저는 이처럼 함수가 선언된 환경의 변수를 기억하고 활용할 수 있도록 해줍니다.
이를 통해 상태 유지, 데이터 은닉, 고차 함수 활용, 함수형 프로그래밍 등 다양한 장점을 활용할 수 있습니다.
클로저는 코드의 유연성과 재사용성을 높이며, 비동기 코드나 복잡한 상태 관리 로직에서도 유용하게 사용됩니다.
클로저를 잘 활용하면 코드가 더 깔끔하고 유지보수하기 쉬워지며, 함수형 프로그래밍에 중요한 개념으로도 자주 사용됩니다.
캡슐화를 통해 유지보수가 쉬운 함수형 프로그래밍을 위해, 클로저 개념을 활용해서 개발해볼까요?

'언어 > JAVASCRIPT' 카테고리의 다른 글
[스코프] 내가 정리한 리액트 스코프 완벽 가이드: 변수와 함수의 유효 범위 깊이 이해하기 (0) | 2025.03.19 |
---|---|
9장 타입변환과 단축평가 (0) | 2024.09.27 |
실행 콘텍스트 (Execution context) (1) | 2023.12.25 |
[반복문] for() forEach() map() filter() 어떨때 사용?? (0) | 2023.10.04 |
자바스크립트의 핵심개념 (0) | 2022.07.04 |