Develop
현대 JavaScript 마스터하기: ES6+와 함수형 프로그래밍의 완벽 가이드
issuemaker99
2025. 3. 26. 10:55
728x90
소개: 왜 지금 최신 JavaScript를 배워야 하는가?
2015년 ES6(ECMAScript 2015)의 등장은 JavaScript 역사에서 가장 혁명적인 변화였습니다. 그 이후로도 매년 새로운 기능이 추가되면서 JavaScript는 더욱 강력하고 표현력이 풍부한 언어로 발전해왔습니다. 하지만 여전히 많은 개발자들이 이전 방식의 코딩에 머물러 있습니다.
이 글을 읽어야 하는 이유:
- 💼 취업 시장에서 경쟁력 확보 (최신 JS 기술은 필수 요구사항)
- 🚀 생산성 30% 이상 향상 가능
- 🔍 가독성과 유지보수성이 뛰어난 코드 작성 능력 획득
- 🛡️ 더 적은 버그와 예상치 못한 동작 방지
자, 이제 현대 JavaScript의 세계로 뛰어들어 봅시다!
ES6+ 핵심 문법 총정리
1. 화살표 함수: 간결함의 극치
// 기존 방식
function add(a, b) {
return a + b;
}
// 화살표 함수
const add = (a, b) => a + b;
// 본문이 여러 줄일 때
const calculate = (a, b) => {
const result = a * b;
return result + 5;
};
💡 Pro Tip: 화살표 함수는 this 바인딩을 상속받기 때문에 메서드나 생성자로 사용하지 않는 것이 좋습니다.
2. 구조 분해 할당: 깔끔한 데이터 추출
// 객체 구조 분해
const person = { name: '김개발', age: 28, job: '프론트엔드 개발자' };
const { name, age } = person;
console.log(name); // '김개발'
// 중첩 객체 구조 분해
const user = {
id: 1,
details: {
firstName: '지훈',
lastName: '박'
}
};
const { details: { firstName } } = user;
console.log(firstName); // '지훈'
// 배열 구조 분해
const colors = ['빨강', '파랑', '녹색'];
const [red, blue] = colors;
console.log(red, blue); // '빨강' '파랑'
// 기본값 설정
const [first = '기본값', second] = [];
console.log(first); // '기본값'
3. 스프레드 연산자와 레스트 파라미터
// 배열 합치기
const fruits = ['사과', '바나나'];
const moreFruits = [...fruits, '오렌지', '망고'];
console.log(moreFruits); // ['사과', '바나나', '오렌지', '망고']
// 객체 복사 및 확장
const defaultSettings = { theme: 'dark', fontSize: 16 };
const userSettings = { ...defaultSettings, fontSize: 18 };
console.log(userSettings); // { theme: 'dark', fontSize: 18 }
// 함수에서 레스트 파라미터 사용
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
4. 템플릿 리터럴: 문자열 조작의 혁명
const name = '김코딩';
const job = '개발자';
const age = 28;
// 기존 방식
const greeting = name + '님은 ' + age + '세 ' + job + '입니다.';
// 템플릿 리터럴
const betterGreeting = `${name}님은 ${age}세 ${job}입니다.`;
// 여러 줄 문자열도 쉽게 작성
const multiLine = `
안녕하세요,
${name}님!
환영합니다.
`;
5. 모듈 시스템: 코드 구조화의 핵심
// math.js - 내보내기
export const PI = 3.14159;
export function square(x) { return x * x; }
export default class Calculator { /* ... */ }
// app.js - 가져오기
import Calculator, { PI, square } from './math.js';
import * as MathUtils from './math.js';
console.log(PI); // 3.14159
console.log(square(4)); // 16
6. 클래스: 객체 지향 프로그래밍의 간결한 문법
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
return `안녕하세요, ${this.name}입니다.`;
}
// 정적 메서드
static create(name, age) {
return new Person(name, age);
}
// getter와 setter
get profile() {
return `${this.name}, ${this.age}세`;
}
set profile(value) {
[this.name, this.age] = value.split(', ');
}
}
// 상속
class Employee extends Person {
constructor(name, age, position) {
super(name, age);
this.position = position;
}
sayHello() {
return `${super.sayHello()} ${this.position}로 일하고 있습니다.`;
}
}
함수형 프로그래밍: 미래를 위한 패러다임
함수형 프로그래밍은 단순한 문법적 편의를 넘어서 코드를 바라보는 전혀 다른 관점을 제시합니다. 복잡성을 관리하고 버그를 줄이는 이 접근법은 JavaScript에서 특히 강력합니다.
1. 순수 함수: 예측 가능성의 기반
// ❌ 순수하지 않은 함수 - 외부 상태에 의존
let total = 0;
function addToTotal(value) {
total += value; // 부작용: 외부 변수 변경
return total;
}
// ✅ 순수 함수 - 동일 입력, 동일 출력, 부작용 없음
function add(a, b) {
return a + b;
}
순수 함수의 장점:
- 테스트 용이성
- 예측 가능한 결과
- 병렬 처리 가능
- 캐싱 최적화 가능
2. 불변성: 안전한 상태 관리
// ❌ 변경 가능한 접근법
function addItem(cart, item) {
cart.push(item); // 원본 배열 수정
return cart;
}
// ✅ 불변 접근법
function addItem(cart, item) {
return [...cart, item]; // 새 배열 반환
}
// 객체 불변 업데이트
function updateUser(user, props) {
return { ...user, ...props };
}
const user = { name: '김철수', age: 30 };
const updatedUser = updateUser(user, { age: 31 });
// user는 변경되지 않고, updatedUser는 새 객체
3. 고차 함수: 추상화의 마법
// 함수를 인자로 받는 함수
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 함수를 반환하는 함수
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplier(2);
const triple = multiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// 실용적인 예: 디바운스 함수
function debounce(fn, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, args), delay);
};
}
const debouncedSearch = debounce(searchAPI, 300);
4. 함수 합성: 작은 조각에서 복잡한 기능 구축
// 함수 합성 유틸리티
const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x);
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x);
// 작은 함수들
const addTax = price => price * 1.1;
const formatPrice = price => `₩${price.toFixed(2)}`;
const discount = rate => price => price * (1 - rate);
// 함수들을 조합하여 새로운 함수 생성
const calculateFinalPrice = pipe(
discount(0.1), // 10% 할인 적용
addTax, // 세금 추가
formatPrice // 가격 형식화
);
console.log(calculateFinalPrice(1000)); // "₩990.00"
실전 예제: 코드 Before & After
아래 예제를 통해 최신 JavaScript와 함수형 프로그래밍이 실제 코드를 얼마나 개선할 수 있는지 확인해 보세요.
사용자 데이터 처리 예제
Before (ES5 + 명령형):
function getUserData(users) {
var result = [];
for (var i = 0; i < users.length; i++) {
var user = users[i];
if (user.active && user.age >= 18) {
var userData = {
name: user.firstName + ' ' + user.lastName,
email: user.email
};
result.push(userData);
}
}
// 이름으로 정렬
result.sort(function(a, b) {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});
return result;
}
After (ES6+ + 함수형):
const getUserData = (users) => users
.filter(({ active, age }) => active && age >= 18)
.map(({ firstName, lastName, email }) => ({
name: `${firstName} ${lastName}`,
email
}))
.sort((a, b) => a.name.localeCompare(b.name));
비동기 데이터 처리
Before (콜백 지옥):
function loadUserData(userId, callback) {
getUser(userId, function(error, user) {
if (error) {
callback(error);
return;
}
getFriends(user.id, function(error, friends) {
if (error) {
callback(error);
return;
}
getPhotos(user.id, function(error, photos) {
if (error) {
callback(error);
return;
}
callback(null, {
user: user,
friends: friends,
photos: photos
});
});
});
});
}
After (async/await + 구조 분해 할당):
async function loadUserData(userId) {
try {
const user = await getUser(userId);
const [friends, photos] = await Promise.all([
getFriends(user.id),
getPhotos(user.id)
]);
return { user, friends, photos };
} catch (error) {
console.error('데이터 로딩 중 오류:', error);
throw error;
}
}
성능 최적화 팁
현대 JavaScript 기능을 사용하면서도 성능을 최적화하는 방법을 알아봅시다:
- 구조 분해 할당 최적화:
- // 깊은 중첩 객체에서 자주 접근하는 프로퍼티만 추출 const { user: { profile: { firstName, lastName } } } = response;
- 스프레드 연산자 vs Object.assign:
- // 작은 객체는 스프레드 연산자가 더 읽기 쉽고 직관적 const newObj = { ...obj, newProp: value }; // 많은 속성을 가진 큰 객체는 Object.assign이 약간 더 빠를 수 있음 const bigNewObj = Object.assign({}, bigObj, { newProp: value });
- 배열 메서드 체이닝 최적화:
- // ❌ 비효율적: 각 메서드마다 새 배열 생성 const result = numbers .filter(n => n % 2 === 0) .map(n => n * 2) .reduce((sum, n) => sum + n, 0); // ✅ 효율적: 한 번의 순회로 모든 작업 수행 const result = numbers.reduce((sum, n) => { if (n % 2 === 0) { return sum + (n * 2); } return sum; }, 0);
- 메모이제이션으로 계산 캐싱:
- function memoize(fn) { const cache = new Map(); return (...args) => { const key = JSON.stringify(args); if (cache.has(key)) return cache.get(key); const result = fn(...args); cache.set(key, result); return result; }; } // 피보나치 수열 계산 최적화 const fibonacci = memoize(n => { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); });
결론 및 다음 단계
최신 JavaScript 문법과 함수형 프로그래밍의 원칙을 익히는 것은 단순히 트렌드를 따르는 것이 아닌, 더 나은 코드를 작성하기 위한 필수 과정입니다. 이 두 가지를 함께 활용하면:
- 🧩 코드의 복잡성을 효과적으로 관리할 수 있습니다
- 🐞 버그가 발생할 확률을 크게 줄일 수 있습니다
- 📚 코드베이스의 가독성과 유지보수성이 향상됩니다
- 🚀 개발 속도와 생산성이 높아집니다
LIST