Modern JavaScript ES2024 문법 실전 해설
그래서 ES2024 전후의 JavaScript를 읽는 더 좋은 방법은 “더 짧게 쓸 수 있다”가 아니라, 모호함을 줄이고, 반복 보일러플레이트를 안전하게 덜고, 비동기 의도를 더 분명히 만들 수 있는가를 기준으로 보는 것입니다.
핵심은 지원 여부보다 설계 효과다
대부분 팀은 새 문법이 런타임이나 번들러에서 지원되는지만 먼저 봅니다. 물론 필요하지만 그게 전부는 아닙니다.
더 중요한 질문은 아래입니다.
- 이 문법이 읽는 사람의 모호함을 줄이는가
- 반복되는 boilerplate를 안전하게 대체하는가
- 실패 동작을 더 분명히 드러내는가
- 팀이 일관되게 쓸 수 있어서 리뷰가 쉬워지는가
새 문법의 가치는 결국 코드베이스의 기본 규칙을 얼마나 더 선명하게 만들 수 있느냐에 달려 있습니다.
optional chaining과 nullish coalescing은 이미 기준을 바꿨다
이 두 기능은 이제 익숙하지만, 여전히 중요합니다. 방어적 코드를 쓰는 기본 방식을 바꿨기 때문입니다.
잘 맞는 경우:
- 외부 입력 경계에서 nullability가 실제로 존재할 때
- fallback이 falsy와 missing을 구분해야 할 때
- 중첩 객체 접근 boilerplate를 줄여야 할 때
위험한 경우:
- 원래 더 앞에서 강제해야 할 invariant를 뒤로 미룰 때
- 데이터 구조 문제를 optional chaining으로 덮을 때
즉 이 연산자들은 경계 처리에는 탁월하지만, 도메인 모델링 부실을 감추는 수단이 되어서는 안 됩니다.
grouping 계열 기능은 intent를 코드로 드러낸다
배열과 객체를 그룹핑하는 기능은 단순히 줄 수를 줄여서 좋은 것이 아닙니다. “이 데이터를 어떤 기준으로 묶는다”는 의도를 코드에 직접 드러내기 때문에 좋습니다.
매번 임시 reducer를 손으로 쓰는 대신 아래 같은 질문이 코드에서 바로 읽힙니다.
- 카테고리별로 묶기
- 상태별 버킷으로 나누기
- 키 기준으로 테이블 만들기
리포팅, 대시보드, UI 집계 로직이 많은 코드베이스일수록 이런 intent 노출은 꽤 큰 차이를 만듭니다.
Promise 유틸리티는 문법이 아니라 실패 정책이다
최신 Promise helper는 편의 기능처럼 보이지만, 실제로는 실패 정책을 드러내는 도구입니다.
Promise.all()은 fail-fast 병렬 작업을 뜻하고Promise.allSettled()는 부분 성공을 허용하는 모델을 뜻하며Promise.any()는 first-success 전략을 표현합니다
이 차이를 의식하지 않고 쓰면 비동기 코드는 금방 읽기 어려워집니다. 현대 JavaScript의 좋은 사용법은 대체로 “무엇이 더 짧은가”가 아니라, 비동기 의미가 더 정직하게 드러나는가를 보는 쪽입니다.
편의 문법은 게으름을 숨기기도 한다
최신 문법의 위험은 편리함이 곧 모델링 게으름으로 이어질 수 있다는 점입니다.
대표적인 예시는:
- 유효성 검증 대신 optional chaining만 늘어나는 경우
- 너무 늦은 시점에 fallback default를 붙이는 경우
- spread 문법이 많아져 객체 소유권이 흐려지는 경우
- 보기엔 세련됐지만 실제로는 비용이 큰 helper 체인이 쌓이는 경우
그래서 새 문법은 correctness뿐 아니라, 경계를 더 강하게 만들었는지 아니면 더 얇게 보이게만 만들었는지까지 같이 봐야 합니다.
예시: 경계 처리가 분명한 최신 JavaScript
function normalizeUser(input) {
const email = input?.email?.trim()?.toLowerCase();
const locale = input?.preferences?.locale ?? "en-US";
if (!email) {
throw new Error("email is required");
}
return {
id: input.id,
email,
locale,
marketingOptIn: input.preferences?.marketingOptIn ?? false,
};
}
이 예시가 괜찮은 이유는 새 문법을 썼기 때문이 아니라, 아래가 모두 보이기 때문입니다.
- optional access는 외부 입력 경계에만 머무른다
- 필수 invariant는 여전히 강제한다
- fallback default가 의미 있게 선언된다
짧아졌지만, 더 중요한 것은 의도가 더 깨끗해졌다는 점입니다.
팀 규칙이 문법 자체보다 더 중요하다
최신 JavaScript가 비싸지는 순간은 개발자마다 다른 식으로 도입할 때입니다.
좋은 팀은 보통 이런 기준을 정합니다.
- optional chaining을 어디까지 허용할지
??와||를 언제 구분할지- Promise helper별 실패 정책을 어떻게 읽을지
- hand-written reducer를 언제 built-in 패턴으로 바꿀지
이 기준이 없으면 최신 문법은 업그레이드가 아니라 파편화를 만듭니다.
자주 보는 안티패턴
- invariant 검증 대신 optional chaining만 쓰는 경우
- 새 문법이면 무조건 더 읽기 좋다고 가정하는 경우
- Promise coordination 스타일이 팀 내에서 제각각인 경우
- spread와 destructuring이 지나쳐 데이터 흐름이 흐려지는 경우
- 팀 규칙보다 문법 도입 속도가 더 빠른 경우
문제는 최신 JavaScript가 너무 고급이라서가 아니라, 아키텍처 의도 없이 도입되기 때문입니다.
리뷰 체크리스트
- 이 기능이 짧게 만드는가, 아니면 의도를 더 분명히 만드는가
- boilerplate를 줄이는가, 아니면 모델링 문제를 숨기는가
- 비동기 실패 동작을 코드만 보고도 이해할 수 있는가
- default와 null handling이 여전히 분명한가
- 유사한 코드에서 팀이 같은 규칙으로 쓰고 있는가
마무리 판단
최신 JavaScript는 nullability, fallback, 비동기 의도를 더 정직하게 드러낼 때 가장 유용합니다. 반대로 새 문법으로 모호함을 압축만 하면 곧 유지보수 비용으로 돌아옵니다.
Continue Reading
다음으로 읽기 좋은 글
TypeScript Utility Types 실전 가이드
TypeScript 유틸리티 타입을 DTO, 업데이트 payload, selector, 파생 타입 설계에 어떻게 써야 하는지, 어디서부터는 가독성을 해치는지 정리합니다.
💬 LanguageTypeScript Generics 실전 가이드
TypeScript 제네릭을 어디서 쓰면 API 계약이 더 강해지고, 어디서부터는 타입 퍼즐이 되는지 실무 기준으로 정리합니다.
📚 IT 이야기웹 브라우저가 개발자의 일상을 바꾼 순간
초기 웹 브라우저가 문서 뷰어를 넘어 애플리케이션 실행 환경이 되기까지의 변화를 개발자 관점에서 풀어봅니다.
📱 MobilePWA 완전 가이드: 앱스토어 없이 설치 가능한 웹앱
Service Worker, Web App Manifest, 오프라인 지원, 푸시 알림, 홈 화면 설치까지 포함해 PWA를 실무 기준으로 설명합니다.
다음 탐색