try/finally 지옥 탈출 — JS using, 그런데 "3대 브라우저 지원"은 아직이다
블록을 빠져나갈 때 정리를 알아서 해주는 using 선언. 세 엔진이 구현을 마쳤다는 소식이 돌지만, 정식 사파리 기준으로는 아직이다. 문법을 정리하고, 퍼블리셔가 지원 소식을 읽는 법으로 다시 본다.
이런 코드, 한 번쯤 써봤을 거예요.
const conn = await db.connect();
try {
const file = await openLog();
try {
await conn.query('...');
await file.write('done');
} finally {
await file.close();
}
} finally {
await conn.release();
}
열었으면 닫아야 하고, 중간에 에러가 나도 닫아야 하니 try/finally로 감싸요.
자원이 하나 늘 때마다 들여쓰기가 한 겹씩 깊어지고요.
정작 하려던 일인 쿼리 한 줄, 쓰기 한 줄은 그 계단 맨 안쪽에 묻혀요.
using은 이 계단을 덜어내는 문법이에요.
{
// conn과 file이 [Symbol.asyncDispose]를 갖고 있다면
await using conn = await db.connect();
await using file = await openLog();
await conn.query('...');
await file.write('done');
} // 블록을 나가는 순간 file → conn 순서로 자동 정리
using으로 선언한 값은 블록을 빠져나갈 때 [Symbol.dispose]()가 자동으로 불려요.
await using이면 [Symbol.asyncDispose]()가 끝날 때까지 기다려주고요.
정상 종료든 return이든 에러든, 어느 길로 나가도 정리는 꼭 한 번 돌고, 순서는 선언의 역순이에요.
나중에 연 것부터 닫는 거죠.
손으로 짠 finally 계단이 하던 일과 똑같아요.
정리할 수 있는 객체를 직접 만드는 것도 어렵지 않아요.
그 심볼 이름으로 메서드 하나만 있으면 돼요.
function lock(name) {
console.log(`${name} 잠금`);
return {
[Symbol.dispose]() {
console.log(`${name} 해제`);
},
};
}
{
using a = lock('A');
using b = lock('B');
// ...작업
} // "B 해제" → "A 해제"
Node에선 이미 쓸 데가 있어요.
fs/promises의 open()이 돌려주는 파일 핸들이 Symbol.asyncDispose를 갖고 있어서, await using file = await open(path) 한 줄이면 닫기를 잊을 일이 없어요.
자원을 조건에 따라 모았다가 한꺼번에 정리해야 할 땐 DisposableStack이라는 통도 있고요.
퍼블리셔 눈엔 낯선 그림이 아니에요.
태그를 열었으면 닫아야 하고, 안쪽에서 연 태그를 먼저 닫아야 하죠.
자원도 같아요.
그 짝 맞추기를 예전엔 사람이 들여쓰기로 지켰다면, 이제는 문법이 지켜줘요.
“3대 브라우저 전부 지원”이라는 소식
8월에 사파리가 using을 지원하면서 마지막 공백이 메워졌다는 기사가 돌았어요.
V8, SpiderMonkey, WebKit, 세 엔진이 전부 구현을 마쳤다는 내용이었죠.
틀린 말은 아니에요.
그런데 퍼블리셔로 일하며 몸에 밴 습관이 하나 있어요.
”지원한다”는 소식을 보면 기사보다 지원표부터 여는 거요.
표를 열어보면 이래요.
크롬과 엣지는 134부터, 파이어폭스는 141부터 정식으로 돼요.
Node는 24부터고요.
사파리는 8월의 Technology Preview 250에 들어갔을 뿐, 9월에 나온 정식 사파리 27에는 아직 없어요.
iOS 사파리도 마찬가지고요.
그래서 이 기능은 아직 Baseline이 아니에요.
카드에 ‘Limited availability’가 뜨는 게 그 뜻이에요.
엔진에 들어간 것과, 내 사용자의 브라우저에 깔린 것은 다른 이야기예요.
Technology Preview는 개발자가 미리 만져보라고 내놓는 실험판이에요.
아이폰 사용자가 많은 서비스라면, iOS 사파리가 빠진 지원표는 ‘아직’으로 읽는 게 맞아요.
그럼 언제 쓰나
코드가 어디서 도느냐로 갈려요.
서버 코드는 런타임을 내가 고르니까 쉬워요.
Node 24 이상에서 돌린다면 지금 바로 써도 돼요.
브라우저 코드는 두 갈래예요.
TypeScript는 5.2부터 using을 이해하고, 낮은 타깃으로 빌드하면 try/finally 형태로 풀어 써줘요.
다만 Symbol.dispose 같은 심볼은 문법이 아니라 런타임에 있어야 하는 값이라, 지원하지 않는 브라우저를 위해선 폴리필을 따로 챙겨야 해요.
그 수고가 아깝다면 정식 사파리에 들어올 때까지 기다리는 것도 충분히 합리적이에요.
지원표를 보고, 폴리필을 따져보고, 들일 시점을 고르는 일.
CSS 새 속성을 들일 때 하던 순서를 JS 문법에도 그대로 쓰면 돼요.
브라우저가 격주로 버전을 올리는 요즘엔 그 표가 바뀌는 속도가 빨라졌을 뿐이에요.
이 글의 사파리 칸은 당분간 비워둘게요.
거기가 초록으로 바뀌는 날, 여기에 한 줄 덧붙이겠습니다.
댓글