18일차(1)[즉시실행함수 / 재귀함수 / 콜백함수]

진하의 메모장·2025년 1월 31일
2

공부일기

목록 보기
21/66
post-thumbnail

2025 / 01 / 31

오늘은 수업 시간에 즉시실행함수 / 재귀함수 / 콜백함수에 대해 배웠습니다.
재귀함수는 종종 처음으로 되돌리고 싶을 때 사용해봐서 익숙했지만
즉시실행함수와 콜백함수는 모르는 부분이 더 많았습니다.
특히 즉시실행함수는 아예 처음 보는 함수라 이런 함수가 있다는게 조금 신기했습니다.



💌 즉시실행함수

즉시실행함수(IIFE)는 정의와 동시에 실행되는 함수입니다.
특정한 범위 내에서만 변수를 사용할 수 있게 하고 싶을 때 사용합니다.
이 함수는 한 번만 실행되고 종료됩니다. (선언되자마자 바로 실행)


1. 기본 구조

  • 함수를 괄호로 감싸고, 함수 호출도 괄호로 감싸는 방식으로 사용합니다.
(function() {
  // 코드 블록
  console.log("IIFE 실행!");
})();

1) function( ) {...}

  • 익명함수(이름이 없는 함수)를 정의합니다.

2) (function( ) {...})

  • 함수를 괄호로 감싸서 함수 표현식을 만듭니다.
  • 이는 자바스크립트에서 함수가 표현식으로 처리되도록 하기 위함입니다.

3) ( )

  • 함수가 즉시 실행되도록 호출합니다.


2. 목적과 사용 이유

1) 변수 스코프 제한

  • IIFE 내부에서 정의된 변수는 함수 내부에서만 접근이 가능합니다.
  • 외부와의 충독을 방지하고 전역 공간을 오염시키지 않도록 할 수 있습니다.

2) 전역 변수 오염 방지

  • 여러 스크립트가 있을 때 전역 변수의 이름 충돌을 피하고 싶을 때 사용합니다.

3) 즉시 실행 로직 필요

  • 일부 로직을 정의하고 바로 실행해야 할 때 유용합니다.
  • 데이터를 초기화하거나 설정을 적용할 때 사용할 수 있습니다.


3. 사용 예시

1) 즉시 실행하여 초기화하기

  • 앱 초기화 코드처럼 사용할 수 있습니다.
  • 전역 변수에 영향을 주지 않고, appName과 version은 IIFE 내부에서만 작동합니다.
(function() {
  let appName = "MyApp";
  let version = "1.0";
  console.log("App initialized:", appName, version);
})();

2) 비동기 처리와 결합된 IIFE

  • 비동기 코드와 결합되어 특정 로직을 즉시 실행할 때 유용합니다.
(function() {
  setTimeout(() => {
    console.log("비동기 작업 실행");
  }, 1000);
})();


4. 장단점

장점

1) 스코프 보호

  • 외부에서 접근할 수 없는 변수를 만들어서 코드의 안정성을 높입니다.

2) 전역 변수 충돌 방지

  • 여러 코드가 있을 때, IIFE로 전역 변수를 방지할 수 있습니다.

3) 즉시 실행할 수 있는 로직

  • 코드가 한 번 실행된 뒤 더 이상 필요하지 않은 로직을 처리할 수 있습니다.

단점

1) 읽기 어려울 수 있음

  • 익명 함수와 괄호를 사용하여 작성되므로 처음 보는 사람에게는 직관적이지 않습니다.

2) 디버깅 어려움

  • IIFE 내부의 코드에서 에러가 발생하면, 그 코드만 따로 디버깅하기 어려울 수 있습니다.


💌 재귀함수

재귀함수는 자기 자신을 호출하는 함수입니다.
재귀 함수는 문제를 작은 부분 문제로 나누어 해결하는 데 사용됩니다.


1. 기본 구조

1) 기저 조건(Base Case)

  • 재귀 호출을 멈추는 조건입니다.
  • 기저 조건이 없으면 계속 호출되어 무한 루프에 빠지게 됩니다.

2) 재귀 조건(Recursive Case)

  • 자기 자신을 호출하는 부분입니다.
  • 반복적으로 자기 자신을 호출하면서 문제를 점점 더 작은 문제로 쪼갭니다.
function printNumber(n){
    // 매개변수 n의 값이 0보다 작다면 함수를 종료하겠습니다.(기저 조건)
    if(n <= 0){
        return; // 함수를 끝낸다.
    }

    printNumber(n-1); // 자기 자신을 호출(재귀 조건)
    console.log(n);
}

printNumber(10);


2. 사용 예시

1) 팩토리얼 계산

  • 주어진 수 n에 대해 n x (n-1) x (n-2) x ... x 1의 값을 구하는 연산입니다.
// 재귀함수 응용 : 팩토리얼 계산
function factorial(n){
    // 매개변수 n의 값이 0보다 작다면 함수를 종료하겠습니다.
    if(n === 0 || n === 1){
        return 1; // 함수를 끝낸다.
    }

    return n * factorial(n-1);
}

console.log(factorial(5)); // 120


3. 핵심 포인트

1) 기저 조건

  • 재귀 함수에서 반드시 기저 조건이 필요합니다.
  • 기저 조건이 없으면 함수는 무한히 자기 자신을 호출하게 되어 스택 오버플로우(Stack Overflow) 오류가 발생합니다.
  • 기저 조건이 만족되면 재귀 호출을 중지하고 값을 반환합니다.

2) 재귀 조건

  • 자기 자신을 다시 호출하는 방식으로 재귀 함수가 동작합니다.
  • 이 과정에서 문제를 점점 더 작은 문제로 나누어 해결합니다.

3) 메모리 사용

  • 재귀 함수는 각 호출이 스택에 쌓이므로, 너무 깊은 재귀 호출은 메모리 소비가 커지고, 성능이 떨어질 수 있습니다. (상황에 따라 반복문이 더 효율적일 때가 있습니다.)


4. 장단점

장점

1) 간결하고 직관적

  • 복잡한 문제를 단순화하여 쉽게 표현할 수 있습니다.

2) 문제를 나누어 해결

  • 복잡한 문제를 점차적으로 해결해나가서 더 쉽게 처리가 가능합니다.

단점

1) 성능 문제

  • 재귀 함수는 매번 새로운 함수 호출을 스택에 쌓기 때문에 메모리 사용이 많고 성능이 떨어질 수 있습니다.

2) 무한 재귀

  • 기저 조건을 잘못 설정하거나, 재귀 종료가 없는 경우 무한 재귀에 빠져서 스택 오버플로우가 발생할 수 있습니다.


💌 콜백함수

콜백함수는 다른 함수의 인수로 전달되어 실행되는 함수입니다.
어떤 함수에 인수로 전달되어 그 함수가 끝난 후 실행됩니다.


1. 기본 구조

  • 어떤 작업이 끝난 후에 실행되어야 할 함수를 다른 함수에 넘겨주는 방식입니다.
  • 다른 함수가 그 작업을 마친 뒤, 전달된 콜백 함수를 실행하는 구조입니다.
// 콜백 함수
function greet(name, callback){
    console.log(`hello, ${name}!`);
    callback();

}

function sayGoodBye(){
    console.log("Goodbye!");
}

greet("Alice",sayGoodBye);
// hello, Alice!
// Goodbye!
  • 여기서 greet 함수는 sayGoodbye라는 콜백 함수를 받습니다.
  • greet 함수가 끝난 후, 콜백 함수인 sayGoodbye가 호출됩니다.


2. 사용 예시

1) 배열의 forEach

  • num.forEach( )는 배열 num의 각 요소에 대해 주어진 콜백 함수를 실행합니다.
  • forEach 메서드는 콜백 함수를 배열의 각 요소에 대해 호출하며, 콜백 함수에는 두 개의 인수가 전달됩니다.
const num = [1,2,3,4,5];

num.forEach((item, index)=>{
    console.log(`배열의 요소는 ${item}이고 인덱스는 [${index}]입니다.`);
})

/* 출력 결과
배열의 요소는 1이고 인덱스는 [0]입니다.
배열의 요소는 2이고 인덱스는 [1]입니다.
배열의 요소는 3이고 인덱스는 [2]입니다.
배열의 요소는 4이고 인덱스는 [3]입니다.
배열의 요소는 5이고 인덱스는 [4]입니다.
*/

2) 주어진 횟수만큼 반복작업 수행

  • repeat 함수는 두 개의 매개변수를 받습니다.
  • number 만큼 반복하면서 그에 맞게 콜백 함수가 호출됩니다.
  • for 반복문으로 i가 0부터 number - 1까지 반복하면서 매번 callback(i)를 실행합니다.
  • callback(i)는 현재 반복 중인 인덱스 값(i)을 콜백 함수로 전달합니다.
function repeat(number, callback) {
    for (let i = 0; i < number; i++) {
        callback(i); 
    }
}

function callback(i) {
    console.log(`인덱스 : ${i}`); 
}

repeat(5, callback);

/* 출력 결과
인덱스 : 0
인덱스 : 1
인덱스 : 2
인덱스 : 3
인덱스 : 4
*/

3) 숫자 배열 필터링(홀수 / 짝수 구분)

① 배열 순회 후 출력

  • filterArray: 배열을 순회하며 짝수는 newArray에 추가하고, 짝수와 홀수를 각각 출력합니다.
  • isEven: 짝수를 출력합니다.
  • odd: 홀수를 출력합니다.
// filterArray 함수는 배열을 순회하면서 짝수만 새로운 배열에 추가하고 반환
function filterArray(array, callback) {
  let newArray = [];  // 짝수를 저장할 배열

  for (let i of array) {
    if (i % 2 == 0) {
      newArray.push(i);  // 짝수일 경우 newArray에 추가
      isEven(i);  // 짝수 출력
    } else {
      odd(i);  // 홀수 출력
    }
  }

  return newArray;  // 짝수만 담긴 배열 반환
}

// 짝수를 출력하는 함수
function isEven(i) {
  console.log("짝수 : " + i);
}

// 홀수를 출력하는 함수
function odd(i) {
  console.log("홀수 : " + i);
}

// filterArray 함수 호출 후 결과 확인
const result = filterArray([1, 2, 3, 4], isEven);
console.log("짝수 배열:", result);  // 반환된 짝수 배열 출력

/* 출력 결과
홀수 : 1
짝수 : 2
홀수 : 3
짝수 : 4
짝수 배열: [ 2, 4 ]
*/

② 배열 순회 후 객체 분할 대입 사용

  • 리턴 값을 2개를 받은 후 객체 분할 대입을 사용하였습니다.
  • 분할 대입에 관련한 내용은 추후 포스팅하겠습니다!
function filterArray1(numArray, IsEven) {
  let evenNumbers = [];
  let oddNumbers = [];
  for (let i = 0; i < numArray.length; i++) {
    if (IsEven(numArray[i])) {
      evenNumbers.push(numArray[i]);
    } else {
      oddNumbers.push(numArray[i]);
    }
  }
  return { even: evenNumbers, odd: oddNumbers };
}

function IsEven(number) {
  return number % 2 == 0;
}

console.log(filterArray1([1, 2, 3, 4], IsEven));

/* 출력 결과
{ even: [ 2, 4 ], odd: [ 1, 3 ] }
*/


3. 장단점

장점

1) 비동기 작업 처리

  • 콜백 함수는 비동기 작업에서 매우 유용합니다.
  • 시간이 걸리는 작업에서 작업이 끝난 후에 실행할 코드를 지정할 수 있습니다.

2) 코드의 재사용성

  • 다양한 함수에 인수로 전달할 수 있기 때문에 같은 로직을 재사용할 수 있습니다.
  • 코드의 중복을 줄이고 유연성을 높일 수 있습니다.

3) 함수형 프로그래밍에서의 활용

  • 함수형 프로그래밍에서 중요한 개념으로, 함수 자체를 다른 함수에 인수로 전달하여 조합하거나 추상화하는 데 활용됩니다.
  • 코드의 가독성 및 유지보수성을 높일 수 있습니다.

단점

1) 콜백 지옥 (Callback Hell)

  • 콜백 함수가 여러개 중첩되면 코드가 매우 복잡해지고 읽기 어려워지는 현상이 발생합니다.
  • 여러 비동기 작업을 순차적으로 처리하려면 콜백이 계속 중첩되며, 이는 코드의 가독성과 유지보수성을 크게 떨어뜨립니다.

2) 예외 처리 어려움

  • 콜백 함수 내에서 발생한 에러를 처리하는 것이 어려운 경우가 많습니다.
  • 에러를 처리하려면 콜백 함수마다 일일이 에러 처리 코드를 작성해야 하기 때문에 중복되고 관리하기 어려운 코드가 될 수 있습니다.

3) 상태 관리 어려움

  • 콜백 함수가 많아질수록 코드 내에서 상태를 추적하고 관리하는 것이 어려워질 수 있습니다.

4) 디버깅의 어려움

  • 비동기 코드에서 콜백 함수는 주로 나중에 실행되므로, 코드 실행 흐름을 추적하는 데 어려움을 겪을 수 있습니다.
  • 콜백이 여러 개 중첩되어 있을 경우, 문제가 발생한 부분을 정확히 찾아내는 것이 복잡하고 시간이 오래 걸릴 수 있습니다.



18일차(1) 후기

  • 콜백함수의 응용 문제를 풀 때, 콜백함수라는 것을 의식하고 써본적이 없어서 그런지.. 어떻게 작동되고 넘어가는지 이해하는게 조금 어려웠습니다.
  • 아직은 함수를 사용하는게 미숙한 부분이 있고, 함수 활용이 살짝 어려운 것 같습니다.
  • 함수를 사용하는데 헷갈리는 것이 없도록 차근차근 공부하겠습니다! ・。゚(゚^ω^゚)。゚・
profile
૮꒰ ྀི〃´꒳`〃꒱ა

0개의 댓글