[TIL-0509] 고차 함수

jiny·2025년 5월 12일

캡스톤2

목록 보기
2/22

🌟 고차 함수(Higher-Order Function)란?

  • 함수를 인자로 받거나, 함수를 결과로 반환하는 함수
  • 함수가 다른 함수를 '데이터'처럼 다루는 것
  • 함수도 일종의 값이기 때문에, 다른 값처럼 매개변수로 전달하거나 리턴값으로 반환할 수 있음

🌟 일반 함수 vs. 고차 함수

구분설명
일반 함수숫자가 문자열 같은 값을 인자로 받아서 결과를 반환함
고차 함수함수 자체를 인자로 받거나, 혹은 함수를 결과로 반환

🌟 고차 함수가 필요한 이유

  • 코드의 재사용성: 같은 패턴의 작업을 추상화하여 여러 곳에서 재사용할 수 있음
  • 코드의 가독성: 반복되는 코드를 줄이고, 더 읽기 쉬운 코드를 만들 수 있음
  • 유연성: 함수의 동작을 인자로 넘기는 함수에 따라 쉽게 바꿀 수 있음

🌟 고차 함수의 2가지 조건

1. 함수를 인자로 받는다.
2. 함수를 리턴한다.

이 중 하나만 만족해도 고차 함수이다.


고차 함수 예시

✅ 예시 1: 함수를 인자로 받는 고차 함수

function repeat(n, action) {
  for (let i = 0; i < n; i++) {
    action(i);
  }
}

// 사용 예시
repeat(5, console.log);
  • repeat 함수는 숫자 n함수 action을 인자로 받음
  • repeat(5, console.log)라고 하면, console.logaction으로 전달됨 → i값 0~4까지 콘솔에 찍게 됨
  • 즉, console.log라는 함수를 매개변수로 넘겼기 때문에 repeat는 고차 함수

✅ 예시 2: 함수를 반환하는 고차 함수

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

const sayHello = greet("Hello");
console.log(sayHello("Hyunjin")); // Hello, Hyunjin!
const sayGoodBye = greet("Good Bye");
console.log(sayGoodBye("Kim")); // Good Bye, Kim!
  • greet("Hello")새로운 함수를 리턴
  • 새로운 함수는 이름(name)을 받아서 "Hello, ${name}!"을 만들어 줌
  • 즉, 함수를 리턴하는 greet는 고차 함수

🌟 실제 자주 쓰는 고차 함수들 (배열 메서드)

함수설명예시
map배열 요소 각각을 변형해서 새 배열 생성arr.map(x => x * 2)
filter조건에 맞는 요소만 골라서 새 배열 생성arr.filter(x => x > 0)
reduce배열 요소를 누적해서 하나의 값으로arr.reduce((a, b) => a + b)
const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(num => num * 2); // [2, 4, 6, 8, 10]
const even = numbers.filter(num => num % 2 === 0); // [2, 4]
const sum = numbers.reduce((acc, cur) => acc + cur, 0); // 15

➡️ 모두 함수를 인자로 받는 고차 함수


🌟 고차 함수를 사용하면 코드가 얼마나 간결해질까?

✅ 예시 1: map을 쓰지 않은 코드 vs. 고차 함수(map)를 사용한 코드

  • 일반 방식 (반복문)

    const numbers = [1, 2, 3, 4, 5];
    const doubled = [];
    
    for (let i = 0; i < numbers.length; i++) {
      doubled.push(numbers[i] * 2);
    }
    
    console.log(doubled); // [2, 4, 6, 8, 10]
  • 고차 함수 사용 방식 (map)

    const numbers = [1, 2, 3, 4, 5];
    const doubled = numbers.map(num => num * 2);
    
    console.log(doubled); // [2, 4, 6, 8, 10]

✅ 예시 2: filter를 쓰지 않은 코드 vs. 고차 함수(filter)를 사용한 코드

  • 일반 방식 (반복문)

    const scores = [90, 55, 78, 30, 88];
    const passed = [];
    
    for (let i = 0; i < scores.length; i++) {
      if (scores[i] >= 60) {
        passed.push(scores[i]);
      }
    }
    
    console.log(passed); // [90, 78, 88]
  • 고차 함수 사용 방식 (filter)

    const scores = [90, 55, 78, 30, 88];
    const passed = scores.filter(score => score >= 60);
    
    console.log(passed); // [90, 78, 88]

➡️ 이처럼 고차 함수를 사용하면 반복문, 조건문, 임시 변수 선언 없이 한 줄로 같은 결과를 얻을 수 있음


✍️ 구현 예시

여행 기간(시작일, 종료일)을 수정하는 함수를 고차 함수로 구현하였다.

const handleDateChange = (key: 'startDate' | 'endDate') => (e: React.ChangeEvent<HTMLInputElement>) => {
  setTravelPeriod(prev => ({ ...prev, [key]: e.target.value }));
}

위 함수를 이해하기 위해서 위 함수와 관련 있는 코드를 설명한다.

  • 여행 기간(시작일, 종료일) 상태는 시작일과 종료일을 명확하게 접근하기 위해서 객체 형태로 관리하고 있다.
    const [travelPeriod, setTravelPeriod] = useState({
      startDate: '',
      endDate: '',
    });
  • 여행 시작일에 대한 input 필드의 onChange 속성handleDateChange 함수를 연결하고 있다.
    <input
      type="date"
      value={travelPeriod.startDate}
      onChange={handleDateChange("startDate")} // 👈 이 부분 주목
      min={today}
    />
  • 마찬가지로, 여행 종료일에 대한 input 필드의 onChange 속성handleDateChange 함수를 연결하고 있다.
    <input
      type="date"
      value={travelPeriod.endDate}
      onChange={handleDateChange("endDate")} // 👈 이 부분 주목
      min={travelPeriod.startDate || today}
    />

이제 다시 handleDateChange 고차 함수를 이해해보자.

const handleDateChange = (key: 'startDate' | 'endDate') => (e: React.ChangeEvent<HTMLInputElement>) => {
  setTravelPeriod(prev => ({ ...prev, [key]: e.target.value }));
};
  • 이 함수는 고차 함수(Higher-Order Function)로,

    • 매개변수로 키 값('startDate' 또는 'endDate')를 받고,
    • 이벤트 핸들러를 반환
  • 동작 방식

    <input onChange={handleDateChange('startDate')} />
    1. handleDateChange('startDate') 호출
      👉 "startDate"라는 값을 받아서
      👉 아래와 같은 onChange 핸들러 함수를 만들어 반환

      (e) => {
        setTravelPeriod(prev => ({ ...prev, startDate: e.target.value }));
      }

      👉 즉, handleDateChange 함수 자체이벤트 핸들러를 만들어내는 고차 함수이지, 이벤트 핸들러가 아님

    2. 이 반환된 함수가 onChange에 전달되어
      👉 input의 값이 바뀔 때마다 실행되는 이벤트 핸들러 역할을 하게 됨

  • 위 구조를 쓰는 이유

    장점설명
    재사용성시작일/종료일 모두 하나의 함수로 처리 가능 (key만 바꾸면 됨)
    가독성불필요하게 핸들러를 2개 만들 필요 없음
    객체 상태 유지prev => ({ ...prev, [key]: value })기존 값을 보호하면서 변경하는 패턴

    만약 이렇게 안 쓰면 코드가 중복됨

    const handleStartDateChange = (e) => {
      setTravelPeriod(prev => ({ ...prev, startDate: e.target.value }));
    };
    const handleEndDateChange = (e) => {
      setTravelPeriod(prev => ({ ...prev, endDate: e.target.value }));
    };

    💡 위 함수들을 하나로 줄이기 위해 key를 매개변수로 받는 고차함수를 쓴 것임

0개의 댓글