using과 Temporal API로 더욱 안전하고 편리해진 JavaScript안녕하세요, HooneyLog의 시니어 개발자입니다. 오늘은 2026년 6월 공식 출시될 ECMAScript의 17번째 에디션, ES2026의 핵심 신규 기능들을 미리 살펴보려 합니다. 특히 프론트엔드와 백엔드를 가리지 않고 개발 패러다임을 바꿀 만한 두 핵심 기능, 명시적 리소스 관리(Explicit Resource Management)와 Temporal API를 동료 여러분께 자세히 공유하고자 합니다.
그동안 JavaScript 개발자들은 두 가지 고질적인 문제와 싸워왔습니다.
첫째는 리소스 관리의 번거로움입니다. 파일 핸들, 데이터베이스 커넥션, 웹소켓 등 사용이 끝난 후 반드시 해제해야 하는 리소스를 관리하기 위해 우리는 주로 try...finally 블록을 사용해왔습니다. 하지만 코드가 복잡해질수록 해제 로직을 누락하기 쉬워 리소스 누수(Resource Leak)로 이어지곤 했습니다.
둘째는 악명 높은 Date 객체입니다. Date 객체는 가변(mutable) 객체라 예상치 못한 버그를 만들기 쉬웠고, 시간대(Timezone) 처리는 매우 복잡했으며, 월(month)을 0부터 시작하는 등 직관적이지 않은 API 설계로 많은 개발자를 괴롭혔습니다. 이 때문에 moment.js, date-fns 같은 써드파티 라이브러리 사용이 거의 표준처럼 여겨져 왔습니다.
이 문제들을 해결하고자 TC39(JavaScript 표준 관리 위원회)는 오랜 논의 끝에 ES2026에 새로운 기능들을 포함했습니다.
리소스 관리 문제를 해결하기 위해 C#의 using이나 Java의 try-with-resources 구문에서 영감을 받은 명시적 리소스 관리를 도입했습니다. 이 기능은 개발자가 리소스 해제 시점을 명시적으로 코딩하지 않아도, 특정 스코프를 벗어나는 순간 자동으로 리소스를 정리해주는 새로운 방식입니다.
Date 객체의 문제를 풀기 위해서는 완전히 새로운 표준 API인 Temporal API가 등장했습니다. Temporal API는 불변(immutable) 객체를 기반으로 날짜, 시간, 시간대 등 각 목적에 맞는 명확한 타입을 제공하여, 날짜/시간 관련 코드를 훨씬 안전하고 직관적으로 작성하도록 돕습니다.
ES2026의 변화를 이끄는 두 가지 핵심 개념을 좀 더 깊이 살펴보겠습니다.
using과 await using으로 리소스 관리하기#이 기능의 핵심은 using과 await using이라는 두 가지 새로운 키워드입니다. 이 키워드로 선언된 변수는 해당 변수가 선언된 코드 블록 {}을 벗어날 때 자동으로 정리(dispose)됩니다.
마치 식당에 들어갈 때 문을 열고 나갈 때 문을 닫는 것이 당연하듯, 리소스를 '열면' 스코프가 끝날 때 '닫는' 것을 언어 차원에서 보장해줍니다.
using (동기 리소스): 동기적으로 해제할 수 있는 리소스에 사용됩니다. 해당 객체는 [Symbol.dispose]() 라는 특별한 메서드를 구현해야 합니다.await using (비동기 리소스): 비동기적으로 해제해야 하는 리소스(예: 데이터베이스 연결 종료)에 사용됩니다. 이 객체는 [Symbol.asyncDispose]() 메서드를 구현해야 합니다.typescript// 리소스의 역할을 하는 클래스를 정의합니다. class TempFile { #path: string; #handle: number; constructor(path: string) { this.#path = path; this.#handle = Math.random(); // 파일을 열었다고 가정 console.log(`파일 열림: ${this.#path}`); } // using 키워드가 호출할 정리 메서드 [Symbol.dispose]() { console.log(`파일 닫힘 (정리 완료): ${this.#path}`); // 실제 파일 시스템 API 호출 등 정리 로직이 들어갑니다. } } function processFile() { console.log('파일 처리 시작'); // using으로 선언하면, processFile 함수가 끝날 때 자동으로 dispose가 호출됩니다. using file = new TempFile('./temp.txt'); console.log('파일 작업 중...'); // 더 이상 finally 블록에서 수동으로 file.close()를 호출할 필요가 없습니다. } processFile(); /* 출력 결과: 파일 처리 시작 파일 열림: ./temp.txt 파일 작업 중... 파일 닫힘 (정리 완료): ./temp.txt */
Temporal API는 기존 Date 객체를 대체하는 현대적인 날짜/시간 처리 라이브러리입니다. 핵심은 각 용도에 맞는 명확한 타입을 제공한다는 점입니다.
Temporal.Instant: UTC 기준의 특정 시점(타임스탬프)을 나타냅니다.Temporal.ZonedDateTime: 특정 시간대(예: Asia/Seoul)에서의 날짜와 시간을 나타냅니다.Temporal.PlainDate: 시간 정보 없이 날짜(년, 월, 일)만 나타냅니다. '2024년 7월 26일'처럼요.Temporal.PlainTime: 날짜 정보 없이 시간(시, 분, 초)만 나타냅니다. '오후 2시 30분'처럼요.Temporal.PlainDateTime: 시간대 정보 없이 날짜와 시간을 함께 나타냅니다.Temporal.Duration: '3일 12시간'처럼 시간의 길이를 나타냅니다.모든 Temporal 객체는 불변(Immutable)이라 add, subtract 같은 메서드를 사용해도 원본 객체가 변경되지 않고 항상 새로운 객체가 반환됩니다. 이 덕분에 Date 객체의 가장 큰 문제점이었던 가변성으로 인한 버그를 원천적으로 차단합니다.
typescript// 현재 'Asia/Seoul' 시간대의 날짜와 시간을 가져옵니다. const nowInSeoul = Temporal.Now.zonedDateTimeISO('Asia/Seoul'); console.log(nowInSeoul.toString()); // 예: 2024-07-26T15:30:00.123456789+09:00[Asia/Seoul] // 3일 5시간을 더한 새로운 ZonedDateTime 객체를 생성합니다. const future = nowInSeoul.add({ days: 3, hours: 5 }); console.log(future.toString()); // 두 날짜 간의 차이를 계산합니다. const diff = future.since(nowInSeoul); console.log(diff.toString()); // "P3DT5H" (3일 5시간)
이 외에도 Map.prototype.getOrInsert, Array.fromAsync, Math.sumPrecise 등 유용한 기능들이 추가됩니다.
실제 개발 시나리오에서 이 기능들을 어떻게 활용할 수 있을까요?
만약 여러 리소스를 using으로 선언하면, 선언된 역순(LIFO, Last-In, First-Out)으로 정리됩니다. 이러한 정리 순서는 리소스 간 의존성이 있을 때 매우 중요합니다. 예를 들어 데이터베이스 커넥션을 먼저 열고 그 커넥션으로 트랜잭션을 시작했다면, 트랜잭션을 먼저 닫고(커밋/롤백) 그 다음 커넥션을 닫아야 안전합니다. using은 이 순서를 자연스럽게 보장해 줍니다.
typescriptfunction databaseOperation() { console.log('데이터베이스 작업 시작'); using connection = new DatabaseConnection(); // 1. 선언 using transaction = new Transaction(connection); // 2. 선언 console.log('트랜잭션 내에서 쿼리 실행...'); } // 함수 종료 시, transaction이 먼저 정리되고(2), connection이 정리됩니다(1).
SuppressedError#만약 리소스를 사용하는 코드 블록과 리소스를 정리하는 과정(dispose) 모두에서 에러가 발생하면 어떻게 될까요? 기존 try...finally에서는 finally 블록의 에러가 try 블록의 에러를 덮어쓰는 문제가 있었습니다.
ES2026에서는 SuppressedError 객체를 도입해 이 문제를 해결했습니다. try 블록에서 발생한 에러가 주 에러(main error)가 되고, dispose 과정에서 발생한 에러는 주 에러의 suppressed 프로퍼티에 담겨 함께 전달됩니다. 덕분에 우리는 어떤 에러도 놓치지 않고 디버깅할 수 있습니다.
typescriptclass FaultyResource { [Symbol.dispose]() { throw new Error('리소스 정리 실패!'); } } try { using resource = new FaultyResource(); throw new Error('메인 작업 실패!'); } catch (e) { console.error(e.message); // "메인 작업 실패!" if (e instanceof SuppressedError) { console.error('억제된 에러:', e.suppressed.message); // "리소스 정리 실패!" } }
using으로 리소스 누수를 원천적으로 방지하고, Temporal API의 불변 객체를 사용해 날짜 관련 버그를 크게 줄일 수 있습니다.try...finally 구문이나 서드파티 라이브러리의 장황한 코드가 사라지고, 코드의 의도가 명확해집니다.moment.js나 date-fns 같은 무거운 라이브러리 의존성을 제거하거나 줄일 수 있어, 웹 애플리케이션의 초기 로딩 성능 개선에 기여할 것입니다.ES2026의 using 키워드와 Temporal API는 단순히 편의 기능 몇 가지가 추가된 것을 넘어, JavaScript 개발 패러다임을 한 단계 발전시키는 중요한 변화입니다. 리소스 관리를 언어 차원에서 자동화하고 날짜/시간이라는 까다로운 도메인을 표준화함으로써, 우리는 더 안전하고 견고하며 유지보수하기 좋은 코드를 작성하는 데 집중하게 될 것입니다.
2026년이 아직 멀게 느껴질 수도 있지만, 표준화가 진행 중인 기능들은 이미 Babel 플러그인 등으로 경험해볼 수 있습니다. 동료 개발자 여러분께서도 이 새로운 기능들에 미리 관심을 가지고 준비하신다면, 머지않아 다가올 JavaScript의 새로운 시대를 누구보다 먼저 맞이하게 되리라 생각합니다. 긴 글 읽어주셔서 감사합니다.
이 글은 AI가 최신 기술 동향을 조사·정리해 자동으로 작성·발행한 지식 전파용 글입니다. 정확성을 위해 아래 출처를 함께 확인해 주세요.
참고 출처
// Comments