BlogFlow | 블로그

프론트엔드 개발과 IT 기술을 중심으로 실무 경험과 학습을 기록합니다.

타입스크립트

TypeScript 6.0 업데이트 정리: 무엇을 준비해야 할까

2026.03.27·약 21분

이 글에서 정리하는 내용

저는 이 글에서 TypeScript 6.0을 처음 접하는 주니어 개발자도 흐름을 따라올 수 있도록, 어려운 용어를 먼저 쉬운 말로 풀고 기존 방식과 비교하면서 설명해보겠습니다. 단순히 무엇이 추가됐는지 나열하는 것이 아니라, 왜 바뀌었는지와 내 프로젝트에서 무엇을 먼저 확인해야 하는지까지 한 번에 정리하겠습니다.

TypeScript 6.0은 어떤 버전인가

ChatGPT Image 2026년 3월 27일 오후 06 28 59

저는 이번 TypeScript 6.0을 볼 때 가장 먼저 “새 문법이 얼마나 늘었는가”보다 “왜 이런 정리가 들어왔는가”를 먼저 봐야 한다고 생각합니다. 이번 버전은 TypeScript 5.9와 앞으로 나올 7.0 사이를 연결하는 전환 성격이 강합니다. 그래서 겉으로는 옵션 몇 개가 바뀐 것처럼 보여도, 실제로는 오래된 설정 방식과 문법을 정리하고 현대적인 런타임 환경에 맞추는 흐름으로 이해하는 편이 훨씬 쉽습니다. 특히 주니어 개발자 입장에서는 이번 버전을 통해 tsconfig 옵션이 왜 필요한지, 그리고 브라우저 번들러 프로젝트와 Node.js 직접 실행 프로젝트를 왜 다르게 설정해야 하는지 감을 잡기 좋습니다.

이번 버전의 성격부터 짧게 잡기

# TypeScript 6.0 설치
npm install -D typescript

# 버전 확인
npx tsc -v

저는 이 명령 자체보다, 이 버전 업데이트가 어떤 의미를 가지는지 먼저 이해하는 것이 중요하다고 봅니다. 예전에는 TypeScript를 단순히 “문법 검사기”처럼 받아들이기 쉬웠지만, 6.0부터는 “내 프로젝트의 모듈 해석 방식과 출력 기준이 현대 자바스크립트 환경에 맞는가”까지 함께 확인해야 합니다. 쉽게 비교하면, 예전에는 틀린 코드만 잡아주는 검사기였다면 이제는 프로젝트 설정의 방향성까지 바로잡는 역할이 더 강해졌다고 보면 됩니다.

왜 이번 글이 기능 소개보다 설정 정리에 더 집중해야 하는가

{
  "compilerOptions": {
    "ignoreDeprecations": "6.0"
  }
}

저는 이 옵션을 “문제를 해결하는 설정”으로 보기보다, “당장 막히는 경고를 잠시 미루는 설정”으로 이해하는 편이 맞다고 생각합니다. 즉, 6.0에서 deprecated 경고를 무시할 수는 있어도, 7.0으로 가면 그 방식이 계속 유지된다고 기대하면 안 됩니다. 그래서 실무에서는 지금 6.0에서 경고를 읽고 정리하는 과정이 곧 7.0 대비 작업이 됩니다. 이 지점이 이번 글의 핵심 출발점입니다.

이번에 추가되거나 달라진 기능

저는 6.0의 새 기능을 볼 때 이름만 보고 이해하려고 하면 오히려 더 헷갈린다고 생각합니다. 예를 들어 Temporal, getOrInsert, RegExp.escape 같은 이름은 처음 보면 낯설지만, 각각 무엇을 대신하고 어떤 불편을 줄이는지만 알면 생각보다 어렵지 않습니다. 그래서 이 글에서는 기능 이름을 그대로 외우기보다, “기존에 무엇이 불편했는지”와 “새로 무엇이 편해졌는지”를 비교해서 설명하겠습니다.

이름이 낯선 기능을 먼저 쉬운 말로 보면

// 1) Temporal
// Date를 더 안전하고 명확하게 다루기 위한 시간 API

// 2) getOrInsert
// Map 안에 값이 없으면 새 값을 넣고, 있으면 기존 값을 꺼내기 쉽게 해주는 방식

// 3) RegExp.escape
// 사용자가 입력한 문자열을 정규식에서 안전하게 쓸 수 있게 바꿔주는 도구

저는 이 세 가지를 이렇게 먼저 이해하는 편이 가장 쉽다고 봅니다. Temporal은 기존 Date를 다룰 때 생기던 애매함을 줄이기 위한 시간 관련 API입니다. getOrInsertMap에 값이 없을 때 직접 has로 확인하고 set하고 다시 get하던 흐름을 더 간단하게 생각하게 도와주는 이름입니다. RegExp.escape는 사용자가 입력한 특수문자를 정규식에서 실수 없이 검색 문자열로 쓰도록 바꿔주는 도구라고 이해하면 됩니다. 즉, 어려워 보이는 이름이지만 실제로는 “날짜 다루기”, “맵 기본값 넣기”, “정규식 안전 처리”라는 익숙한 문제를 조금 더 편하게 다루게 해주는 변화입니다.

기본값도 함께 바뀌었기 때문에 체감 변화가 더 크다

{
  "compilerOptions": {
    "strict": false,
    "target": "es2022",
    "module": "esnext"
  }
}

저는 이 부분이 주니어 개발자가 가장 놓치기 쉬운 지점이라고 봅니다. TypeScript 6.0은 단순히 옵션 몇 개를 deprecated 처리한 것에서 끝나지 않고, 기본값 자체도 더 현대적인 방향으로 이동했습니다. 이제 strict는 기본적으로 true이고, module 기본값은 esnext, target 기본값은 현재 지원하는 최신 연도 기준 ECMAScript 버전으로 잡힙니다. 쉽게 비교하면 예전에는 “필요하면 엄격 모드를 켠다”에 가까웠다면, 이제는 “필요하면 엄격 모드를 명시적으로 끈다”에 더 가깝습니다. 따라서 예전에 기본값에 기대어 작성한 프로젝트라면, 업그레이드 뒤 새 에러가 늘어나는 이유가 코드 자체보다 기본 설정 변화에 있을 수 있습니다.

subpath imports와 현대적인 경로 별칭

{
  "name": "my-app",
  "type": "module",
  "imports": {
    "#/*": "./src/*"
  }
}

// 사용 예시
import { formatPrice } from "#/utils/formatPrice.ts";

저는 이 기능을 볼 때 기존의 @/ 별칭과 비교해서 이해하면 쉽다고 봅니다. 기존에는 번들러 설정이나 tsconfig path 매핑으로만 별칭을 맞추는 경우가 많았습니다. 그런데 6.0에서는 Node 계열 환경과 더 잘 맞는 방식으로 #/ 형태의 subpath imports를 지원합니다. 쉽게 말해, 상대경로 ../../../를 길게 쓰지 않으면서도 런타임 기준과 타입 검사 기준을 더 가깝게 맞추는 방향이라고 이해하면 됩니다.

Temporal, getOrInsert, RegExp.escape처럼 표준 API 타입이 더 풍부해졌다

// Temporal 예시
const now = Temporal.Now.instant();
const tomorrow = now.add({ hours: 24 });

// Map 기본값 처리 예시
const scoreMap = new Map<string, number>();

if (!scoreMap.has("hebi")) {
  scoreMap.set("hebi", 0);
}

const currentScore = scoreMap.get("hebi")!;
scoreMap.set("hebi", currentScore + 1);

// RegExp.escape 예시
const keyword = "a+b";
const safeKeyword = RegExp.escape(keyword);
const regex = new RegExp(safeKeyword, "g");

저는 이 부분을 이름보다 상황으로 이해하는 편이 더 쉽다고 봅니다. 먼저 Temporal은 날짜와 시간을 더 명확하게 다루기 위한 API입니다. 기존 Date는 시간대, 문자열 파싱, 값 변경 방식 때문에 헷갈리는 경우가 많았는데, Temporal은 이런 지점을 더 분명하게 나눠서 다루려는 흐름입니다. 주니어 개발자 기준으로는 “Date보다 더 분리되고 명확한 시간 API” 정도로 먼저 잡아도 충분합니다.

다음으로 getOrInsert라는 이름은 처음 보면 어렵지만, 실제로는 “맵에 값이 없으면 기본값을 넣고 바로 꺼내 쓰고 싶다”는 아주 익숙한 상황과 연결됩니다. 위 예시처럼 원래는 has로 확인하고, 없으면 set하고, 다시 get해서 써야 했습니다. 이 흐름을 더 간단하게 다루도록 도와주는 개념이 getOrInsert라고 보면 됩니다. 즉, 새로운 자료구조를 배우는 것이 아니라 기존 Map 사용 흐름을 덜 번거롭게 만드는 방향입니다.

마지막으로 RegExp.escape는 정규식을 직접 다뤄본 적이 많지 않은 주니어 개발자에게 특히 중요합니다. 사용자가 a+b, (test), [abc]처럼 특수문자가 들어간 문자열을 입력했을 때, 이것을 그대로 정규식에 넣으면 검색 의미가 달라질 수 있습니다. 이때 RegExp.escape는 “이건 정규식 문법이 아니라 그냥 문자 그대로 찾고 싶다”는 의도를 안전하게 만들어주는 도구입니다. 쉽게 말하면, 검색창 입력값을 정규식에 넣을 때 생길 수 있는 사고를 줄여주는 기능입니다.

변화 제가 실무에서 보는 의미
#/ subpath imports 긴 상대경로를 줄이면서 런타임과 타입 해석을 더 비슷하게 맞추기 좋습니다.
Temporal / getOrInsert / RegExp.escape 날짜 처리, Map 기본값 처리, 정규식 안전 처리처럼 실무에서 자주 만나는 문제를 더 편하게 다루는 방향입니다.

실무에서 바로 영향 받는 tsconfig 변화

저는 이번 6.0에서 가장 중요한 파트를 이 부분이라고 봅니다. 실제 업무에서는 “새 기능을 안 써도 되는 경우”는 많지만, tsconfig 기본값과 권장 방향이 바뀌면 빌드 결과와 에러 메시지가 바로 달라질 수 있습니다. 특히 주니어 개발자가 많이 혼란스러워하는 지점은 “내가 tsconfig를 거의 안 건드렸는데 왜 갑자기 에러가 늘었지?”라는 부분인데, 이건 코드가 망가졌다기보다 TypeScript가 이제 더 명확한 기준을 요구하기 때문인 경우가 많습니다.

types와 rootDir은 이제 더 명시적으로 보는 편이 안전하다

{
  "compilerOptions": {
    "rootDir": "./src",
    "outDir": "./dist",
    "types": ["node"]
  },
  "include": ["src"]
}

저는 이 설정을 TypeScript 6.0 대응의 기본 출발선으로 봅니다. 먼저 types는 이제 기본값이 []라고 생각하는 편이 안전해졌기 때문에, Node 내장 모듈이나 테스트 전역 타입을 쓰는 프로젝트라면 필요한 패키지를 직접 넣는 습관이 중요해졌습니다. 예를 들어 process, Buffer, __dirname, describe, it 같은 것이 갑자기 인식되지 않으면 코드를 의심하기 전에 types: ["node"], types: ["jest"]처럼 필요한 타입 패키지를 명시했는지 먼저 확인하는 편이 좋습니다. 쉽게 말하면 예전에는 TypeScript가 “보이는 타입을 웬만하면 같이 가져오던 편”이었다면, 이제는 “필요한 타입을 조금 더 명확하게 적어주세요”에 가까워졌다고 이해하면 됩니다.

또한 rootDir도 예전처럼 입력 파일 전체를 보고 자동 추론된다고 기대하기보다, 6.0에서는 tsconfig.json이 있는 디렉터리를 기본 기준으로 본다고 이해하는 편이 더 정확합니다. 그래서 소스 폴더가 src로 명확하다면 rootDir: "./src"를 직접 적어두는 편이 출력 구조를 예측하기 쉽습니다. 비교하면 예전에는 “TypeScript가 알아서 맞춰주겠지”에 가까웠고, 지금은 “내가 프로젝트 구조를 명확하게 알려주는 편이 더 안전하다”에 가깝습니다.

baseUrl과 moduleResolution은 프로젝트 성격에 맞게 다시 선택해야 한다

// 이전 방식 예시
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@/*": ["*"]
    },
    "moduleResolution": "node"
  }
}

// 6.0에서 더 권장되는 방향 예시
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    },
    "moduleResolution": "bundler"
  }
}

저는 이 차이를 아주 단순하게 설명하면 이렇게 정리합니다. baseUrl은 예전에는 path 매핑의 공통 기준점처럼 많이 썼지만, 실제로는 의도하지 않은 모듈까지 찾는 lookup root처럼 동작할 수 있어서 타입 검사 결과와 실제 실행 결과가 어긋나는 원인이 되곤 했습니다. 주니어 개발자 기준으로 더 쉽게 말하면, “내가 원하는 폴더만 기준으로 삼는 줄 알았는데, TypeScript가 생각보다 넓게 찾는다”는 문제가 생길 수 있다는 뜻입니다. 그래서 6.0에서는 baseUrl을 빼고 paths 값에 실제 경로를 더 명확히 적는 쪽이 권장됩니다.

또한 moduleResolution: node는 오래된 Node 해석 규칙에 가깝기 때문에, 웹 번들러 중심 프로젝트라면 bundler, Node.js 런타임을 직접 타는 프로젝트라면 nodenext로 옮겨가는 방향을 먼저 검토하는 편이 좋습니다. 이것도 어렵게 느껴질 수 있지만, 단순하게는 “누가 이 파일을 실제로 읽고 연결하느냐”에 따라 고르는 문제라고 보면 됩니다. Vite나 Next.js 같은 번들러가 읽는다면 bundler, Node.js 자체가 읽는다면 nodenext를 먼저 떠올리는 식으로 접근하면 덜 헷갈립니다.

프로젝트 유형 제가 먼저 검토하는 moduleResolution
Next.js, Vite, Bun, 번들러 중심 웹 앱 bundler
Node.js가 직접 모듈을 읽는 서버/CLI nodenext

주의해야 할 deprecated 문법과 명령

ChatGPT Image 2026년 3월 27일 오후 06 29 01

저는 6.0 업그레이드에서 실제로 개발자를 가장 당황하게 만드는 부분이 여기라고 봅니다. tsconfig는 경고가 나와도 감이 잡히는 편이지만, 예전 문법이나 예전 실행 습관은 갑자기 “왜 안 되지?”라는 느낌을 주기 쉽기 때문입니다. 특히 오래된 블로그 글이나 예전 사내 템플릿을 참고해 프로젝트를 만들었다면 이 파트에서 걸릴 가능성이 있습니다.

// 1) import assert 문법은 with 문법으로 옮겨가는 편이 안전합니다.
import data from "./data.json" with { type: "json" };

// 이전에 보던 형태
// import data from "./data.json" assert { type: "json" };

// 2) namespace를 선언할 때 module Foo {} 방식은 피해야 합니다.
namespace Utils {
  export const version = "6.0";
}

// 3) tsconfig.json이 있는 폴더에서 단일 파일만 검사하고 싶다면
//    의도를 명확히 적어야 합니다.
//    npx tsc --ignoreConfig sample.ts

저는 이 세 가지를 특히 먼저 점검하는 편입니다. 첫째, JSON import 예제에서 예전에 쓰던 assert { type: "json" } 형태를 아직 볼 수 있는데, 현재는 with { type: "json" } 쪽으로 옮겨가는 흐름이라고 이해하면 됩니다. 주니어 개발자 기준에서는 “예전 블로그 글 예제와 지금 공식 문서 예제가 다르게 보일 수 있다” 정도를 먼저 알고 있으면 충분합니다.

둘째, namespace 선언에서 module Foo {} 형태를 예전 코드에서 볼 수 있는데, 이제는 namespace Foo {}로 고쳐두는 편이 안전합니다. 셋째, tsconfig.json이 있는 폴더에서 tsc 파일명.ts처럼 실행하면 예전처럼 조용히 넘어가는 것이 아니라, 설정 파일이 무시된다는 사실을 더 분명하게 알려줍니다. 즉, TypeScript 6.0은 “대충 맞아 보이면 통과”보다 “어떤 의도로 실행하는지 명확히 써 달라”는 방향으로 가고 있다고 이해하면 됩니다.

stableTypeOrdering은 누구에게 필요한가

{
  "compilerOptions": {
    "stableTypeOrdering": true
  }
}

저는 이 옵션을 모든 프로젝트에 기본 적용해야 하는 플래그로 보지는 않습니다. 이 옵션은 6.0과 7.0 사이에서 타입 순서 차이로 생길 수 있는 선언 출력 차이나 미묘한 추론 차이를 진단할 때 도움이 됩니다. 다만 장기적으로 항상 켜두는 기능이라기보다, 업그레이드 검증 과정에서 비교를 쉽게 해주는 도구에 가깝습니다. 쉽게 비교하면, 평소 주행용 옵션이 아니라 점검할 때 잠깐 켜보는 정비 모드에 더 가깝다고 보시면 됩니다.

정리

저는 TypeScript 6.0을 볼 때 “화려한 새 기능 버전”보다 “현대적인 자바스크립트 환경에 맞춰 오래된 설정을 정리하는 버전”으로 받아들이는 편이 가장 이해하기 쉽다고 생각합니다. 특히 Temporal, getOrInsert, RegExp.escape처럼 처음 보면 어려워 보이는 이름도 결국은 날짜 다루기, Map 기본값 처리, 정규식 안전 처리라는 익숙한 문제를 더 편하게 풀기 위한 변화라고 보면 훨씬 부담이 줄어듭니다. 그래서 실무에서는 기능 이름을 외우는 것보다 “이 기능이 어떤 불편을 줄여주는가”를 먼저 이해하는 편이 더 중요합니다.

또한 새 API 타입을 반갑게 보는 것보다 먼저 types, rootDir, paths, moduleResolution, import attributes 문법을 점검하는 것이 중요합니다. 웹 앱이라면 번들러 기준으로, Node.js 서버라면 런타임 기준으로 설정을 나눠 보는 습관을 들이면 6.0뿐 아니라 앞으로의 7.0 대응도 훨씬 수월해집니다. 저는 결국 이번 업데이트의 핵심을 “TypeScript가 더 어려워졌다”가 아니라 “프로젝트 의도를 더 명확하게 적어야 하는 시점이 왔다”로 정리하고 싶습니다.

많이 받는 질문

Q. TypeScript 6.0은 바로 올려도 괜찮은가요?
저는 대부분의 프로젝트에서 바로 검토할 가치는 충분하다고 봅니다. 다만 tsconfig를 오래전에 만들어 거의 손대지 않았거나, 오래된 import 문법과 baseUrl 패턴을 많이 쓰는 프로젝트라면 먼저 로컬에서 빌드와 타입 체크를 돌려보고 deprecated 경고를 정리하는 순서가 안전합니다.

Q. Next.js나 Vite 프로젝트는 무엇부터 보면 되나요?
저는 이런 프로젝트라면 먼저 moduleResolution을 bundler 방향으로 이해하고, baseUrl 대신 paths에 실제 경로를 적는 방식으로 옮길 수 있는지부터 확인합니다. 그리고 Node 전역이 필요한 경우에는 types: ["node"]가 필요한지도 함께 확인합니다.

Q. deprecated 경고가 떠도 ignoreDeprecations만 넣고 넘어가면 안 되나요?
저는 임시 대응으로는 가능하지만 장기 대응으로는 권하지 않습니다. 그 설정은 TypeScript 6.0에서 경고를 잠깐 누르는 용도에 가깝고, 7.0에서는 deprecated 옵션이 실제로 제거될 수 있기 때문에 결국은 지금 정리해두는 편이 전체 작업량을 줄이기 쉽습니다.

Q. strict가 기본값으로 true가 되면 기존 프로젝트는 무조건 깨지나요?
저는 무조건 깨진다고 보지는 않습니다. 다만 예전에 기본값 false에 기대고 있었던 프로젝트라면 null 처리, 암묵적 any, 함수 인자 타입 같은 부분에서 에러가 더 많이 보일 수 있습니다. 이런 경우에는 우선 strict: false를 명시해 당장 빌드를 안정화하고, 이후에 항목별로 strict 대응을 진행하는 순서가 현실적입니다.

Q. Temporal, getOrInsert, RegExp.escape는 꼭 지금 바로 외워야 하나요?
저는 그럴 필요는 없다고 봅니다. 이 기능들은 이름을 외우는 것보다, 각각이 어떤 문제를 줄이기 위해 나온 것인지 이해하는 편이 훨씬 중요합니다. 날짜를 더 명확하게 다루는 흐름이 Temporal, Map 기본값 처리를 더 간단하게 만드는 방향이 getOrInsert, 사용자 입력을 정규식에 안전하게 넣는 도구가 RegExp.escape라고만 잡아도 충분합니다.

이 글이 마음에 드세요?

RSS 피드를 구독하세요!

댓글 남기기