Contents
see ListECMAScript 2026에서 가장 주목받는 새로운 기능 중 하나인 Explicit Resource Management가 도입됩니다. C#의 using문, Python의 with문과 유사하게, JavaScript에서도 using 키워드를 통해 리소스의 자동 정리(disposal)를 안전하게 처리할 수 있게 되었습니다. 파일 핸들, 데이터베이스 연결, 네트워크 소켓 등 명시적 해제가 필요한 리소스를 다룰 때 메모리 누수와 리소스 누수를 방지하는 데 핵심적인 역할을 합니다.
기존 리소스 관리의 문제점
기존 JavaScript에서는 try-finally 패턴으로 리소스를 정리해야 했습니다. 이 방식은 코드가 장황해지고 실수로 정리 코드를 빠뜨리기 쉽습니다.
// 기존 방식: try-finally로 리소스 정리
async function readFile() {
const handle = await fs.promises.open('data.txt', 'r');
try {
const content = await handle.readFile('utf-8');
return content;
} finally {
await handle.close(); // 반드시 수동으로 닫아야 함
}
}
// 여러 리소스를 다루면 중첩이 깊어짐
async function copyFile(src, dest) {
const srcHandle = await fs.promises.open(src, 'r');
try {
const destHandle = await fs.promises.open(dest, 'w');
try {
const data = await srcHandle.readFile();
await destHandle.writeFile(data);
} finally {
await destHandle.close();
}
} finally {
await srcHandle.close();
}
}using 키워드 기본 사용법
using 키워드는 Symbol.dispose 메서드를 구현한 객체에 대해 블록 스코프를 벗어날 때 자동으로 dispose를 호출합니다.
// Symbol.dispose를 구현한 리소스 클래스
class DatabaseConnection {
#connection;
constructor(config) {
this.#connection = createConnection(config);
console.log('DB 연결 생성');
}
query(sql) {
return this.#connection.execute(sql);
}
[Symbol.dispose]() {
this.#connection.close();
console.log('DB 연결 해제');
}
}
// using 키워드로 자동 리소스 관리
function getUserData(userId) {
using db = new DatabaseConnection({ host: 'localhost' });
// 블록 끝에서 자동으로 db[Symbol.dispose]() 호출
const result = db.query(`SELECT * FROM users WHERE id = ${userId}`);
return result;
}
// 함수 종료 시 자동으로 "DB 연결 해제" 출력await using으로 비동기 리소스 관리
네트워크 연결이나 파일 핸들처럼 비동기적으로 정리해야 하는 리소스에는 await using을 사용합니다.
class AsyncFileHandle {
#handle;
static async open(path, mode) {
const instance = new AsyncFileHandle();
instance.#handle = await fs.promises.open(path, mode);
return instance;
}
async read() {
return await this.#handle.readFile('utf-8');
}
async write(data) {
await this.#handle.writeFile(data);
}
// 비동기 dispose
async [Symbol.asyncDispose]() {
await this.#handle.close();
console.log('파일 핸들 비동기 해제');
}
}
// await using으로 비동기 정리
async function processFile() {
await using file = await AsyncFileHandle.open('data.json', 'r');
const content = await file.read();
const parsed = JSON.parse(content);
return parsed;
// 함수 종료 시 await file[Symbol.asyncDispose]() 자동 호출
}DisposableStack과 AsyncDisposableStack
여러 리소스를 그룹으로 관리해야 할 때 DisposableStack을 활용합니다. 스택에 추가된 리소스는 LIFO(후입선출) 순서로 정리됩니다.
function createServerResources() {
using stack = new DisposableStack();
const db = stack.use(new DatabaseConnection({ host: 'localhost' }));
const cache = stack.use(new RedisClient({ port: 6379 }));
const logger = stack.use(new FileLogger('/var/log/app.log'));
// 에러가 발생해도 logger -> cache -> db 순서로 정리
return { db, cache, logger };
}
// 비동기 버전
async function createAsyncResources() {
await using stack = new AsyncDisposableStack();
const ws = stack.use(await WebSocket.connect('wss://api.example.com'));
const stream = stack.use(await createReadStream('/data/large.csv'));
// 블록 종료 시 stream -> ws 순서로 비동기 정리
return processData(ws, stream);
}실전 활용: Express 미들웨어에서의 사용
// Express 라우트에서 DB 트랜잭션 관리
app.post('/api/transfer', async (req, res) => {
await using tx = await db.beginTransaction();
try {
await tx.query('UPDATE accounts SET balance = balance - ? WHERE id = ?',
[req.body.amount, req.body.fromId]);
await tx.query('UPDATE accounts SET balance = balance + ? WHERE id = ?',
[req.body.amount, req.body.toId]);
await tx.commit();
res.json({ success: true });
} catch (err) {
await tx.rollback();
res.status(500).json({ error: err.message });
}
// tx가 commit/rollback 안 됐으면 자동으로 rollback 후 연결 해제
});브라우저 및 런타임 지원 현황
2026년 4월 기준, using 키워드는 Chrome 127+, Firefox 132+, Safari 18.2+에서 지원됩니다. Node.js는 22 LTS부터, Deno는 1.46+, Bun은 1.1.20+에서 사용 가능합니다. TypeScript는 5.2부터 이미 지원하고 있어, TypeScript 프로젝트에서는 즉시 활용할 수 있습니다.
Explicit Resource Management는 JavaScript의 리소스 안전성을 한 단계 끌어올리는 기능입니다. 특히 서버 사이드 Node.js 개발에서 데이터베이스 연결, 파일 핸들, 네트워크 소켓 등을 다룰 때 반드시 활용해야 할 패턴입니다.