자바스크립트: 일반적인 방식 vs 함수형 프로그래밍 비교 코드
1. 배열 처리: 데이터 변환
일반적인 방식 (명령형)
// 숫자 배열을 2배로 만들기
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]함수형 방식 (선언형)
// 숫자 배열을 2배로 만들기
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
 
console.log(doubled); // [2, 4, 6, 8, 10]- 명령형: “어떻게” 할지 단계별로 명시 (반복, 인덱스 관리, push)
 - 함수형: “무엇을” 할지 선언 (
map으로 변환 의도 명확) 
2. 배열 필터링: 조건부 선택
일반적인 방식
// 짝수만 선택하기
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evens = [];
 
for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        evens.push(numbers[i]);
    }
}
 
console.log(evens); // [2, 4, 6, 8, 10]함수형 방식
// 짝수만 선택하기
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evens = numbers.filter(x => x % 2 === 0);
 
console.log(evens); // [2, 4, 6, 8, 10]- 명령형: 루프와 조건문으로 단계별 구현
 - 함수형: 
filter로 의도를 직접적으로 표현 
3. 배열 집계: 합계 계산
일반적인 방식
// 배열의 모든 수 더하기
const numbers = [1, 2, 3, 4, 5];
let sum = 0;
 
for (let i = 0; i < numbers.length; i++) {
    sum += numbers[i];
}
 
console.log(sum); // 15함수형 방식
// 배열의 모든 수 더하기
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
 
console.log(sum); // 15- 명령형: 가변 변수(
sum)를 직접 조작 - 함수형: 
reduce로 축약 로직을 표현, 초기값 명시 
4. 복합 데이터 처리: 체이닝
일반적인 방식
// 짝수를 찾아서 2배로 만든 후 합계 구하기
const numbers = [1, 2, 3, 4, 5, 6];
const evens = [];
let sum = 0;
 
// 1단계: 짝수 필터링
for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        evens.push(numbers[i]);
    }
}
 
// 2단계: 2배로 만들기
const doubled = [];
for (let i = 0; i < evens.length; i++) {
    doubled.push(evens[i] * 2);
}
 
// 3단계: 합계 구하기
for (let i = 0; i < doubled.length; i++) {
    sum += doubled[i];
}
 
console.log(sum); // 24 (2*2 + 4*2 + 6*2)함수형 방식
// 짝수를 찾아서 2배로 만든 후 합계 구하기
const numbers = [1, 2, 3, 4, 5, 6];
const sum = numbers
    .filter(x => x % 2 === 0)  // 짝수 선택
    .map(x => x * 2)           // 2배로 만들기
    .reduce((acc, curr) => acc + curr, 0); // 합계
 
console.log(sum); // 24- 명령형: 단계별로 중간 배열 생성, 3개의 반복문
 - 함수형: 메서드 체이닝으로 파이프라인 구성
 
5. 객체 데이터 처리
일반적인 방식
// 사용자 목록에서 성인만 선택해서 이름 추출
const users = [
    { name: 'Alice', age: 25 },
    { name: 'Bob', age: 17 },
    { name: 'Charlie', age: 30 },
    { name: 'David', age: 16 }
];
 
const adultNames = [];
for (let i = 0; i < users.length; i++) {
    if (users[i].age >= 18) {
        adultNames.push(users[i].name);
    }
}
 
console.log(adultNames); // ['Alice', 'Charlie']함수형 방식
// 사용자 목록에서 성인만 선택해서 이름 추출
const users = [
    { name: 'Alice', age: 25 },
    { name: 'Bob', age: 17 },
    { name: 'Charlie', age: 30 },
    { name: 'David', age: 16 }
];
 
const adultNames = users
    .filter(user => user.age >= 18)
    .map(user => user.name);
 
console.log(adultNames); // ['Alice', 'Charlie']6. 상태 변경: 가변성 vs 불변성
일반적인 방식 (가변 데이터)
// 사용자 정보 업데이트 (원본 수정)
let user = { name: 'John', age: 25, city: 'Seoul' };
 
function updateUserAge(user, newAge) {
    user.age = newAge; // 원본 객체 직접 수정
    return user;
}
 
function updateUserCity(user, newCity) {
    user.city = newCity; // 원본 객체 직접 수정
    return user;
}
 
console.log('원본:', user);
updateUserAge(user, 26);
console.log('나이 변경 후:', user); // 원본이 변경됨
updateUserCity(user, 'Busan');
console.log('도시 변경 후:', user); // 원본이 또 변경됨함수형 방식 (불변 데이터)
// 사용자 정보 업데이트 (새 객체 생성)
const user = { name: 'John', age: 25, city: 'Seoul' };
 
const updateUserAge = (user, newAge) => ({
    ...user,
    age: newAge // 새 객체 생성
});
 
const updateUserCity = (user, newCity) => ({
    ...user,
    city: newCity // 새 객체 생성
});
 
console.log('원본:', user);
const userWithNewAge = updateUserAge(user, 26);
console.log('나이 변경 후 새 객체:', userWithNewAge);
console.log('원본 유지:', user); // 원본 그대로
 
const userWithNewCity = updateUserCity(userWithNewAge, 'Busan');
console.log('최종:', userWithNewCity);- 명령형: 원본 데이터 직접 수정 (가변성)
 - 함수형: 스프레드 연산자로 새 객체 생성 (불변성)
 
7. 부작용(Side Effects) 처리
일반적인 방식 (부작용 있음)
// 전역 변수에 의존하는 비순수 함수
let counter = 0;
 
function processItems(items) {
    const results = [];
    
    for (let i = 0; i < items.length; i++) {
        counter++; // 외부 상태 변경 (부작용)
        console.log(`처리 중: ${items[i]}`); // 로그 출력 (부작용)
        results.push(items[i].toUpperCase());
    }
    
    return results;
}
 
const items = ['apple', 'banana', 'cherry'];
console.log(processItems(items));
console.log('처리된 항목 수:', counter); // 전역 상태에 의존함수형 방식 (순수 함수)
// 순수 함수로 부작용 분리
const processItems = (items) => 
    items.map(item => item.toUpperCase());
 
const logProgress = (item, index) => 
    console.log(`처리 중 ${index + 1}: ${item}`);
 
const countItems = (items) => items.length;
 
// 사용
const items = ['apple', 'banana', 'cherry'];
 
// 부작용이 있는 작업은 별도로 처리
items.forEach(logProgress);
 
// 순수 함수로 데이터 변환
const results = processItems(items);
console.log(results);
 
// 순수 함수로 카운트
const count = countItems(items);
console.log('처리된 항목 수:', count);- 명령형: 함수 내에서 외부 상태 변경과 로그 출력
 - 함수형: 순수 함수와 부작용을 분리
 
8. 조건부 로직 처리
일반적인 방식
// 사용자 등급별 할인율 적용
const users = [
    { name: 'Alice', type: 'premium', purchaseAmount: 1000 },
    { name: 'Bob', type: 'regular', purchaseAmount: 500 },
    { name: 'Charlie', type: 'premium', purchaseAmount: 1500 }
];
 
const processedUsers = [];
 
for (let i = 0; i < users.length; i++) {
    let discount = 0;
    let finalAmount = users[i].purchaseAmount;
    
    if (users[i].type === 'premium') {
        discount = 0.2; // 20% 할인
    } else if (users[i].type === 'regular') {
        discount = 0.1; // 10% 할인
    }
    
    finalAmount = users[i].purchaseAmount * (1 - discount);
    
    processedUsers.push({
        ...users[i],
        discount: discount,
        finalAmount: finalAmount
    });
}
 
console.log(processedUsers);함수형 방식
// 사용자 등급별 할인율 적용
const users = [
    { name: 'Alice', type: 'premium', purchaseAmount: 1000 },
    { name: 'Bob', type: 'regular', purchaseAmount: 500 },
    { name: 'Charlie', type: 'premium', purchaseAmount: 1500 }
];
 
const getDiscount = (userType) => {
    const discounts = {
        premium: 0.2,
        regular: 0.1,
        default: 0
    };
    return discounts[userType] || discounts.default;
};
 
const applyDiscount = (user) => {
    const discount = getDiscount(user.type);
    const finalAmount = user.purchaseAmount * (1 - discount);
    
    return {
        ...user,
        discount,
        finalAmount
    };
};
 
const processedUsers = users.map(applyDiscount);
console.log(processedUsers);9. 에러 처리
일반적인 방식
// 나이 검증 및 처리
function processUsers(users) {
    const adults = [];
    const errors = [];
    
    for (let i = 0; i < users.length; i++) {
        try {
            if (typeof users[i].age !== 'number' || users[i].age < 0) {
                throw new Error(`Invalid age for ${users[i].name}`);
            }
            
            if (users[i].age >= 18) {
                adults.push({
                    ...users[i],
                    status: 'adult'
                });
            }
        } catch (error) {
            errors.push(error.message);
        }
    }
    
    return { adults, errors };
}함수형 방식
// 나이 검증 및 처리
const isValidAge = (age) => 
    typeof age === 'number' && age >= 0;
 
const isAdult = (age) => age >= 18;
 
const validateUser = (user) => 
    isValidAge(user.age) 
        ? { success: true, data: user }
        : { success: false, error: `Invalid age for ${user.name}` };
 
const processAdult = (user) => 
    isAdult(user.age) 
        ? { ...user, status: 'adult' }
        : null;
 
const processUsers = (users) => {
    const validations = users.map(validateUser);
    
    const errors = validations
        .filter(v => !v.success)
        .map(v => v.error);
    
    const adults = validations
        .filter(v => v.success)
        .map(v => processAdult(v.data))
        .filter(Boolean);
    
    return { adults, errors };
};10. 성능 비교
성능 테스트 예제
// 성능 테스트용 큰 배열
const largeArray = Array.from({ length: 1000000 }, (_, i) => i);
 
// 명령형 방식 성능 측정
console.time('명령형');
const imperativeResult = [];
for (let i = 0; i < largeArray.length; i++) {
    if (largeArray[i] % 2 === 0) {
        imperativeResult.push(largeArray[i] * 2);
    }
}
console.timeEnd('명령형');
 
// 함수형 방식 성능 측정
console.time('함수형');
const functionalResult = largeArray
    .filter(x => x % 2 === 0)
    .map(x => x * 2);
console.timeEnd('함수형');
 
console.log('결과 길이 동일:', imperativeResult.length === functionalResult.length);- 명령형: 일반적으로 더 빠름 (단일 루프, 메서드 호출 오버헤드 없음)
 - 함수형: 가독성이 좋지만 중간 배열 생성으로 메모리 사용량 증가
 
결론
| 측면 | 일반적인 방식 (명령형) | 함수형 방식 | 
|---|---|---|
| 가독성 | 단계별 구현으로 복잡함 | 의도가 명확하고 선언적 | 
| 재사용성 | 특정 상황에 특화됨 | 작은 함수 조합으로 재사용 용이 | 
| 디버깅 | 중간 상태 확인 가능 | 순수 함수로 예측 가능 | 
| 성능 | 일반적으로 더 빠름 | 중간 배열 생성으로 오버헤드 | 
| 메모리 | 효율적 | 불변성으로 메모리 사용량 증가 | 
| 테스트 | 상태 의존성으로 복잡 | 격리된 순수 함수로 간단 | 
실무 권장사항:
- 성능이 중요한 대용량 데이터: 명령형 방식
 - 비즈니스 로직, 데이터 변환: 함수형 방식
 - 복잡한 상태 관리: 적절한 혼합 사용
 
두 방식 모두 상황에 따른 장단점이 있으므로, 문제의 특성과 요구사항에 맞는 선택이 중요합니다.
Footnotes
- 
https://www.manuelsanchezdev.com/blog/functional-vs-imperative-javascript-performance ↩ ↩2
 - 
https://flaviocopes.com/javascript-loops-map-filter-reduce-find/ ↩ ↩2
 - 
https://dev.to/joelnet/map-filter-reduce-vs-for-loops-syntax-2k5l ↩ ↩2
 - 
https://codeburst.io/examples-in-javascript-functional-programming-part-1-c9e2df8a411a ↩
 - 
https://www.linkedin.com/pulse/javascript-mutable-immutable-bharath-kumar-murugan ↩
 - 
https://corner.buka.sh/understanding-mutable-and-immutable-data-in-javascript-a-beginners-guide/ ↩
 - 
https://www.freecodecamp.org/news/mutability-vs-immutability-in-javascript/ ↩
 - 
https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/ ↩
 - 
https://www.freecodecamp.org/news/pure-function-vs-impure-function/ ↩
 - 
https://dev.to/vonheikemen/dealing-with-side-effects-and-pure-functions-in-javascript-16mg ↩
 - 
https://www.reddit.com/r/javascript/comments/4nvcd5/performancewise_is_the_for_loop_better_than_map/ ↩