| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 프론트엔드
- Redux
- VUE
- TS
- 백엔드개발자
- 스프링부트
- 웹개발자
- Authentication
- Front-End
- React
- 리액트
- JWT
- spring boot security
- 타입스크립트
- frontend
- 백엔드
- 정보처리기사
- 수제비
- useState
- 큐넷
- 정보처리기사 실기
- JS
- It
- TypeScript
- security
- Node.js
- spring
- spring boot
- JavaScript
- 자바스크립트
- Today
- Total
솔적솔적
시간 복잡도와 고차 함수: 실무에서 꼭 함께 생각해야 하는 이유 본문
개발을 하다 보면 시간복잡도(Big-O)와 고차 함수(map, filter, reduce 등)를 모두 접하게 되는데
이전에 언뜻 보기엔 전혀 다른 분야의 개념처럼 보였다.
- 시간복잡도는 알고리즘 이론의 영역.
- 고차 함수는 함수형 프로그래밍과 언어 문법의 영역.
하지만 실제 현업에서는 이 둘을 따로 떼어 생각하기 어렵다.
왜냐하면, 고차 함수의 사용 자체가 곧 시간복잡도와 직결되기 때문이다.
시간복잡도란?
시간복잡도란, 입력 크기에 따라 알고리즘이 얼마나 빠르게 실행되는지를 수학적으로 나타내는 지표.
- O(1): 입력 크기와 관계없이 항상 일정한 시간.
- O(n): 입력 크기에 비례해서 시간이 늘어남.
- O(n²): 입력 크기의 제곱에 비례해 시간이 늘어남.
개발자는 이 복잡도를 바탕으로 코드가 효율적인지 판단한다.
고차 함수란?
자바스크립트에서 고차 함수(Higher-Order Function)란 다음 중 하나라도 만족하는 함수를 말한.
- 함수를 인자로 받는 함수,
- 함수를 결과값으로 반환하는 함수
- 함수를 마치 클래스에서 만들어낸 '인스턴스' 처럼 취급하는 방법
- 함수를 파라미터로 넘겨줄 수도 있고 결과값으로 반환받을 수도 있는 방법
즉, 함수를 값처럼 다룰 수 있는 언어적 특성을 활용한 프로그래밍 방식이다.
- 프로그래밍의 단계를 한 단계 높일 수 있는 방법 중 하나
- 특별 대우를 받는 함수를 일급 객체가 있다.
- 일급 객체의 세가지 특징
- 변수에 할당할 수 있다.
- 다른 함수의 인자로 전달될 수 있다.
- 다른 함수의 결과로서 리턴될 수 있다.
이 특성 덕분에 map(), filter(), reduce() 같은 고차 함수가 가능해집니다.
내부적으로는 콜백 함수를 실행하며 모든 원소를 순회한다.
- 실제 고차함수 연속된 형태로 많이 쓴다.
- 맵이 순회된 결과물을 연속성을 가진다. 연속성 하나의 고차함수가 끝난 뒤에 별도에 로직을 통하지 않고 다음 로직으로 가는 것을 연속성이라고한다.
- 새로운 값을 반환하기 때문에.
- 실제 내부의 콜백함수가 작동하기 때문에 고차함수 내부인자를 함수로 받거나
- 객체 리터럴, map()
를 뜻한다.
구글링하면 대표적인 고차함수로 map, filter, reduce 같은 배열 메서드,
예시 코드로 간단하게 보이자면
const arr = [1, 2, 3, 4, 5];
// map: 모든 원소를 2배 → O(n)
const doubled = arr.map(x => x * 2);
// filter: 조건에 맞는 원소만 추출 → O(n)
const evens = arr.filter(x => x % 2 === 0);
일급 객체와 고차 함수의 관계
자바스크립트에서 함수는 일급 객체(First-Class Object)
즉, 함수는 다른 값처럼 자유롭게 다룰 수 있다.
다시 일급객체의 특징을 보자면,
일급 객체의 세 가지 특징
- 변수에 할당할 수 있다.
- 다른 함수의 인자로 전달될 수 있다.
- 다른 함수의 결과값으로 반환될 수 있다.
이 덕분에 map(), filter(), reduce() 같은 고차 함수가 가능해짐.
내부적으로 콜백 함수가 실행되며, 그 과정에서 반복문처럼 모든 원소를 순회합니다.
고차 함수와 시간복잡도의 만남
고차 함수는 내부적으로 반복문을 돌며 배열의 모든 원소를 처리합니다.
즉, 고차 함수를 쓰는 순간 자연스럽게 시간복잡도 계산이 따라와야 합니다.
단순 사용
const result = arr.map(x => x * 2); // O(n)
연속 사용
const result = arr.filter(x => x > 10).map(x => x * 2);
// filter: O(n) + map: O(n) = O(2n) ≈ O(n)
비효율적 사용
const result = arr.map(x => arr.filter(y => y === x));
// map: O(n)
// filter: O(n)
// 전체: O(n²)
→ 불필요한 중첩 호출로 성능이 급격히 저하
이 외에 자바스크립트 배열 고차함수들
.forEach(), .reduce(), .filter(), .some(), findIndex(), .map(), .find(), .sort()가 있다.
.forEach()
const testArray = [1, 2, 3, 4];
testArray.forEach(Element => console.log(Element));
여기 코드 보면 배열의 각 요소(1, 2, 3, 4)를 하나씩 꺼내서 console.log(element)를 실행한다.
forEach는 배열의 각 요소에 대해 element => console.log(element)라는 함수를 호출한다.
이 때 forEach는 함수 (element => console.log(element))를 인자로 받으므로 고차 함수로 분류된다.
.reduce()
배열의 모든 요소를 원하는 방식으로 결합하여 단 하나의 값을 생성하는 고차함수
reduce함수는 첫 번째 인자로 함수를 받는다. 이 함수는 배열의 각 요소에 대해 호출되고
이전에 계산 결과와 현재 요소를 받아서 새로운 결과를 반환한다.
const array = [1, 2, 3, 4];
const init = 0; // 초기값을 0으로 설정
const sum = array.reduce((test1, currentValue) => test1 + currentValue, init);
console.log(sum); // 10
.filter()
filter함수는 배열의 각 요소에 대해 조건을 검사하는 함수(콜백 함수)를 인자로 받는다.
이 콜백 함수는 배열의 각 요소에 대해 실행되며, true를 반환하는 요소들로만 새로운 배열을 만들어 반환.
이 코드에서 filter 함수는 다음과 같이 동작.
- filter는 배열 test의 각 요소에 대해 콜백 함수 item => item.length를 호출한다.
- 이 콜백 함수는 각 요소의 길이(item.length)를 반환. 길이가 0이 아닌 경우에는 true로 간주되므로, 모든 요소가 길이를 가지고 있어서 이 경우 filter는 모든 요소를 결과 배열에 포함시킨다.
- 최종적으로 filter는 조건을 만족하는 요소들(여기서는 모든 요소)을 포함하는 새 배열 result를 반환.
filter가 고차 함수인 이유
- 고차 함수의 정의에 부합: filter는 함수를 인자로 받기 때문에 고차 함수이다. 여기서 인자로 받은 함수는 item => item.length
- 이 콜백 함수는 배열의 각 요소에 대해 실행되며, filter 함수 내부에서 처리된 후 결과 배열을 반환한다.
const test = ['솔1', '솔2', '솔3', '솔4'];
const result = test.filter(item => item.length);
console.log(result);
.some()
고차함수는 다른 함수를 인자로 받거나 함수를 반환하는 함수를 의미
some함수의 동작 방식은 배열에서 적어도 하나의 요소가 주어진 조건을 만족하는지를 확인하는데
이 함수는 조건을 검사하기 위해 콜백 함수를 인자로 받는다.
배열의 요소 중 하나라도 이 콜백 함수에서 true를 반환하면 some함수는 true를 반환 그렇지 아니하면 false를 반환.
const numbers = [1, 2, 3, 4, 5];
const hasEvenNumber = numbers.some(num => num % 2 === 0);
console.log(hasEvenNumber); // true
- 배열 numbers는 [1, 2, 3, 4, 5]
- some() 함수는 배열의 각 요소에 대해 콜백 함수 num => num % 2 === 0을 호출.
- 콜백 함수는 각 요소가 짝수인지를 확인.
- 첫 번째 요소 1은 짝수가 아니므로 false 반환
- 두 번째 요소 2는 짝수이므로 true 반환
- some() 함수는 두 번째 요소에서 true를 반환받았기 때문에, 나머지 요소를 확인하지 않고 바로 true를 반환.
some()가 고차 함수인 이유
- some() 함수는 함수를 인자로 받기 때문에 고차 함수.
- 여기서 인자로 받은 함수는 num => num % 2 === 0이라는 콜백 함수
- 이 콜백 함수는 some() 함수 내부에서 배열의 각 요소에 대해 실행.
some()는 고차 함수. 그 이유는 some() 함수가 배열의 각 요소에 대해 조건을 검사하기 위해 콜백 함수를 인자로 받기 때문.
이 콜백 함수는 some() 함수 내부에서 실행되어, 배열의 요소들이 주어진 조건을 만족하는지 확인하는 역할을 한다.
실무에서의 교훈
- 고차 함수 = 반복문
보기에는 깔끔해 보여도, 결국 모든 원소를 순회합니다. - 중첩에 주의
filter 안에서 또 map, map 안에서 또 filter 같은 패턴은 성능 저하의 원인. - 가독성과 성능의 균형
고차 함수는 가독성을 높이지만, 성능이 중요한 구간에서는 시간복잡도를 꼭 고려해야 한다.
마무리
시간복잡도와 고차 함수는 개념적으로는 다른 출발점에서 왔지만, 실무에서는 절대 분리할 수 없는 관계입니다.
- 시간복잡도는 성능을 평가하는 잣대.
- 고차 함수는 성능 분석의 주요 대상.
따라서 개발자가 고차 함수를 쓸 때는 단순히 "코드가 깔끔하다"에서 멈추지 말고,
이 코드의 시간복잡도는 얼마일까?”를 항상 함께 고민하는 습관이 필요하다 생각한다.