
JS는 브라우저나 Node.js에서 실제로 실행되는 언어.
TS는 JS 위에 타입 문법과 타입 검사 기능을 얹은 언어.
공식 사이트의 표현을 빌리면, TypeScript는 다음과 같다.
TypeScript is JavaScript with syntax for types
TypeScript는 JavaScript를 완전히 새로 만든 언어라기보다, JavaScript를 더 안전하게 작성할 수 있도록 도와주는 확장판에 가깝다.
예를 들어 React 컴포넌트를 작성할 때 다음처럼 props 타입을 적을 수 있다.
function UserCard({ name, age }: { name: string; age: number }) {
return <div>{name} - {age}</div>;
}
이렇게 해두면
<UserCard name="Kim" age={20} />
이거는 정상이고
<UserCard name="Kim" age="스무살" />
이거는 에디터에게 잡힌다.
즉, TypeScript는 코드를 실행하기 전에 props, state, 함수 인자, API 응답 값의 타입이 맞는지 미리 확인해주는 도구다.
React에서 TypeScript가 거의 기본처럼 쓰이는 이유도 여기에 있다.
UI를 만드는 과정에서 생기는 자잘한 실수들을 훨씬 빨리 발견할 수 있기 때문이다.
처음엔 나도 TypeScript 6.0 발표를 보고
“이번엔 어떤 기능이 추가됐지?”
정도로 생각했다.
그런데 내용을 읽어보면, 이번 릴리즈의 핵심은 전혀 다른 데 있다.
TypeScript 6.0은 기존 JavaScript 코드베이스 기반의 마지막 major version이다.
그리고 TypeScript 7.0부터는 Go로 작성된 새로운 컴파일러와 언어 서비스가 중심이 된다.
쉽게 말해, TypeScript 팀은 지금 엔진 자체를 갈아끼우는 중이다.
그래서 6.0은
“기능을 크게 늘린 버전”이라기보다
“곧 큰 구조 변화가 올 테니, 지금부터 프로젝트를 정리해두는 버전”
이라고 보는 게 더 맞다.
이걸 이해하면 6.0에 들어간 여러 변경사항이 왜 생겼는지도 같이 이해된다.
TypeScript 7.0이 주목받는 가장 큰 이유는 성능이다.
TypeScript 팀은 이미 2025년에 native preview를 공개했고,
Go 기반의 새 컴파일러와 공유 메모리 병렬 처리 구조를 통해 대형 프로젝트에서 훨씬 빠른 성능을 보여주고 있다.
즉, 7.0은 단순히 “버전 숫자 하나 올라가는 것”이 아니라
TypeScript의 속도와 개발 경험 자체를 크게 바꿀 수 있는 전환점이다.
그래서 지금 6.0에서 보이는 변화들은 대부분
7.0으로 넘어가기 전에 미리 정리해두는 작업이라고 보면 된다.
그래서 이번 버전의 변경사항을 보면 공통된 방향이 있다.
• 애매하고 헷갈리는 동작은 줄이고
• 오래된 레거시 설정은 정리하고
• 최신 런타임과 번들러 환경에 더 맞추고
• TypeScript만의 특이한 예외보다는 실제 실행 환경과 비슷하게 맞춘다
이제부터는 이걸 프론트엔드 개발자 관점에서 하나씩 살펴보자.
이 문장은 처음 보면 꽤 어렵다.
용어를 나눠서 보면 이해가 쉽다.
먼저 타입 추론이란, 우리가 타입을 직접 쓰지 않아도 TypeScript가 알아서 타입을 추측하는 것이다.
const name = "doyoung";
여기서 TypeScript는 name이 문자열이라는 걸 자동으로 안다.
이제 “문맥 민감(context-sensitive)”이라는 표현을 보자.
이건 함수가 주변 문맥에 따라 타입이 정해지는 경우를 말한다.
const arr = [1, 2, 3];
arr.map(x => x * 2);
여기서 x는 타입을 직접 쓰지 않았지만,
arr가 숫자 배열이라는 사실을 바탕으로 x를 number로 이해한다.
문제는 JavaScript의 this가 꽤 까다로운 개념이라는 점이다.
const user = {
name: "Kim",
sayHi() {
console.log(this.name);
}
};
이런 코드를 TypeScript가 분석할 때는
this가 어떤 객체를 가리키는지 신경 써야 하므로 함수 타입을 더 복잡하게 다루게 된다.
그런데 TypeScript 6.0에서는 실제로 this를 쓰지 않는 함수라면 굳이 그렇게 복잡하게 보지 않도록 바뀌었다.
그 결과 제네릭 추론이나 콜백 타입 추론이 더 자연스러워지는 방향으로 개선되었다.
예를 들어 제네릭 함수는 이렇게 생긴다.
function identity<T>(value: T): T {
return value;
}
const a = identity(123); // number
const b = identity("hi"); // string
여기서 T는 여러 타입에 대해 재사용 가능한 타입 템플릿이다.
6.0에서는 this를 안 쓰는 함수들을 덜 복잡하게 취급하기 때문에,
이런 제네릭 추론이 더 매끄럽게 작동할 수 있다.
function wrap<T>(fn: () => T) {
return fn;
}
const getValue = wrap(() => 123);
이런 코드에서 TypeScript가 반환 타입을 더 자연스럽게 이해하는 방향이라고 보면 된다.
정리하자면,
• 예전: this와 상관없는 함수도 괜히 복잡하게 보는 경우가 있었다
• 6.0: this를 안 쓰는 함수는 더 단순하게 본다
• 결과: 제네릭 추론, 콜백 추론이 더 잘 된다
React에서는 이런 변화가 훅, 콜백, 유틸 함수, generic component 쪽에서 간접적으로 체감될 수 있다.
프론트엔드 개발을 하다 보면 상대 경로가 길어질 때가 많다.
import Button from "./components/Button";
이 정도면 괜찮지만, 구조가 깊어지면 금방 이렇게 된다.
import Button from "../../../../components/Button";
이런 경로는 읽기도 어렵고, 파일 위치가 바뀌면 깨지기도 쉽다.
그래서 보통은 별칭(alias)을 사용한다.
import Button from "@/components/Button";
TypeScript 6.0에서는 여기에 더해
Node.js의 subpath imports 스타일과 맞춘 #/ 형태도 지원하게 되었다.
import Button from "#/components/Button";
핵심은 단순히 문법 하나가 늘었다는 게 아니다.
TypeScript가 이제 점점 더 실제 런타임과 번들러가 동작하는 방식에 가깝게 가고 있다는 점이 중요하다.
여기서 같이 나오는 용어가 moduleResolution이다.
이건 import 경로를 TypeScript가 어떤 규칙으로 찾을지 정하는 옵션이다.
예를 들어,
• Node.js 방식에 더 가깝게 맞추려면 **nodenext**
• Vite, Bun, Webpack 같은 번들러 환경에 맞추려면 **bundler**
를 사용하게 된다.
쉽게 말하면 이렇다.
• 예전에는 TypeScript가 “이 경로도 대충 찾아줄게” 하는 느낌이 있었다면
• 이제는 “실제 실행 환경이 찾는 방식에 맞춰 해석할게” 쪽으로 가고 있다
프론트엔드 입장에서는 상대경로 지옥을 줄이고,
번들러 친화적인 경로 구조로 더 자연스럽게 갈 수 있다는 점에서 의미가 있다.
이건 설정을 많이 만져본 사람이 아니면 처음 들을 수 있는 이야기다.
TypeScript는 브라우저 API의 타입 정보를 lib 설정을 통해 가져온다.
예를 들면 이런 식이다.
{
"compilerOptions": {
"lib": ["es2022", "dom", "dom.iterable"]
}
}
여기서 dom은 브라우저 API 타입을 뜻하고,
dom.iterable은 NodeList 같은 DOM 객체를 for...of 같은 반복문으로 다룰 때 필요한 타입 정보를 포함한다.
예를 들어,
const divs = document.querySelectorAll("div");
for (const div of divs) {
console.log(div);
}
예전에는 이런 식의 DOM 컬렉션 반복을 타입 차원에서 더 자연스럽게 쓰려면
dom.iterable을 따로 넣어야 하는 경우가 있었다.
그런데 6.0에서는 이 내용이 dom에 통합되었다.
즉, 브라우저 관련 타입 설정이 조금 더 단순해진 것이다.
프론트엔드 개발자 입장에서는 “엄청 큰 기능”은 아니지만,
설정이 덜 헷갈리고 기본 동작이 더 직관적이 되었다는 의미가 있다.
JavaScript에서 날짜와 시간을 다루는 기본 객체는 Date다.
const now = new Date();
문제는 Date가 생각보다 다루기 까다롭다는 점이다.
• 시간대 처리에서 실수하기 쉽고
• 날짜 계산이 직관적이지 않고
• “날짜만 있는 값인지, 시간까지 포함된 값인지” 의도가 코드에 잘 드러나지 않는다
그래서 등장한 것이 Temporal이다.
Temporal은 미래형 날짜/시간 API라고 볼 수 있다.
예를 들어 이런 느낌으로 사용할 수 있다.
const date = Temporal.PlainDate.from("2026-03-28");
이 코드는 “시간이 없는 순수한 날짜”라는 의도가 더 분명하다.
TypeScript 6.0에서 Temporal 관련 타입이 추가되었다는 것은, TypeScript가 최신 JavaScript 표준 흐름을 미리 반영하기 시작했다는 의미다.
당장 모든 프론트엔드 프로젝트에서 바로 쓰일 기능은 아닐 수 있지만, 앞으로 날짜와 시간 처리를 더 명확하게 다루는 방식으로 바뀔 가능성을 보여주는 신호라고 볼 수 있다.
이번 6.0에서 가장 인상 깊었던 부분 중 하나는
기본값 자체가 현대적인 JS 생태계를 기준으로 바뀌었다는 점이다.
여기서 기본값이란 tsconfig.json에 따로 적지 않아도 TypeScript가 자동으로 사용하는 값을 뜻한다.
예를 들어 아래처럼 최소한의 설정만 있다고 하자.
{
"compilerOptions": {}
}
이 경우 TypeScript는 나머지 옵션들을 기본값으로 채워 넣는다.
6.0은 이 기본값들의 방향성을 꽤 크게 바꿨다.
strict는 타입 검사를 더 엄격하게 수행하는 모드다.
즉, 6.0 이후에는 아무 설정을 하지 않아도 기본적으로 더 엄격한 타입 검사를 전제로 하게 된다.
예전에는 느슨하게 넘어가던 코드도, 이제는 더 빨리 잡을 수 있다.
function greet(name) {
return "Hello " + name.toUpperCase();
}
이 코드에서 name 타입이 명확하지 않다는 점은 strict 모드에서 더 빨리 드러난다.
이제 TypeScript는 기본적으로 느슨한 도구가 아니라, 엄격한 타입 검사를 전제로 하는 도구다.
module은 코드를 어떤 모듈 방식으로 다룰지 정하는 옵션이다.
요즘 프론트엔드 코드에서는 이런 ESM 스타일이 기본이다.
import { useState } from "react";
export function App() {}
6.0에서 module 기본값이 esnext가 되었다는 것은,
TypeScript가 이제 최신 ES 모듈 환경을 기본으로 생각한다는 뜻이다.
정리하자면,
• 예전에는 CommonJS 같은 옛날 방식도 넓게 배려했다면
• 이제는 import / export 중심의 최신 모듈 시스템이 기준이 되었다
프론트엔드 개발자 입장에서는 훨씬 자연스러운 방향이다.
여기서의 target은 TypeScript가 최종적으로 어떤 수준의 JavaScript 문법을 목표로 출력할지 정하는 옵션이다.
쉽게 말하면,
“어느 정도 최신 환경을 기준으로 코드를 내보낼 것인가?”
를 정하는 것이다.
예전에는 구형 브라우저 호환 때문에 더 오래된 문법 수준으로 맞추는 일이 많았다.
하지만 6.0은 기본 target을 최신 ECMAScript 버전 기준으로 올렸다.
이 말은 곧 TypeScript가 현대 브라우저와 현대 런타임을 기본 전제로 보기 시작했다는 뜻이다.
types는 프로젝트에서 자동으로 끌어오는 타입 패키지를 제어하는 옵션이다.
이 기본값이 바뀌었다는 것은, 앞으로는 TypeScript가 불필요한 전역 타입을 자동으로 많이 끌어오지 않겠다는 뜻이다.
왜 이게 좋을까?
예를 들어 브라우저 프로젝트인데 Node 관련 타입이 섞여 들어오면,
이상한 자동완성이나 타입 충돌이 발생할 수 있다.
즉, 이제는 “필요한 타입만 명확하게 가져오자” 는 방향으로 가는 것이다.
이 역시 느슨한 편의성보다 명확하고 예측 가능한 환경을 선택한 변화라고 볼 수 있다.
6.0에서는 target: es5 같은 오래된 옵션도 deprecated 되었다.
ES5는 오래전 JavaScript 환경을 기준으로 한 설정이다.
예전에는 오래된 브라우저를 지원하기 위해 최신 코드를 옛날 문법으로 많이 바꾸는 작업이 필요했다.
하지만 지금은 상황이 달라졌다.
현대 브라우저와 최신 런타임이 기본이 되면서,
TypeScript가 직접 레거시 환경까지 끝까지 책임지는 방식은 점점 덜 중요해졌다.
즉, 6.0의 방향은 이렇다.
• TypeScript는 타입 검사와 최신 JS 환경에 집중하고
• 오래된 환경 호환이나 레거시 변환은 번들러나 다른 빌드 도구에 더 맡긴다
이건 TypeScript가 점점 더 현대적인 개발 파이프라인 안에서 역할을 정리하고 있다는 뜻이기도 하다.
레퍼런스에서 같이 나오는 설정 중 헷갈리기 쉬운 것이 nodenext와 bundler다.
둘 다 moduleResolution과 관련이 있다.
즉, import 경로를 어떻게 해석할지 정하는 규칙이다.
이 둘을 아주 단순하게 구분하면 다음과 같다.
nodenext
Node.js가 실제로 모듈을 해석하는 방식에 가깝게 맞춘다.
Node 서버 프로젝트나 Node 런타임을 직접 타깃으로 하는 경우에 더 적합하다.
bundler
Vite, Bun, Webpack 같은 번들러 중심 환경에 맞춘다.
대부분의 프론트엔드 앱은 이쪽이 더 자연스럽다.
• Node 서버 프로젝트라면 nodenext
• 프론트엔드 앱이라면 bundler
처음에는 새 기능 몇 개를 소개하는 글처럼 보였지만,
읽고 나니 TypeScript 6.0이 말하는 메시지는 꽤 선명했다.
TypeScript는 이제
• 오래된 환경까지 폭넓게 배려하는 도구라기보다
• 최신 런타임과 번들러를 기준으로
• 더 엄격하고
• 더 예측 가능하게
• 더 현실적인 방식으로 동작하는 도구
가 되려 하고 있다.
그리고 그 방향의 중심에는 TypeScript 7.0이 있다.
7.0은 Go 기반의 새로운 컴파일러와 언어 서비스를 통해
속도와 개발 경험을 크게 바꿀 수 있는 버전이다.
그렇기 때문에 6.0은 단순한 중간 버전이 아니라,
앞으로의 TypeScript를 준비하는 실제적인 정리 작업이라고 보는 편이 맞다.
React를 하면서 TypeScript를 “그냥 같이 쓰는 것, 국룰이지” 정도로만 받아들였던 적이 있다.
props에 타입 붙이고, API 응답 타입 정리하고, 에디터에서 에러 잡아주는 정도로만 느꼈다. 나도 사실 그 정도 실력에 가까웠다.
그런데 TypeScript 6.0 발표 글을 읽으면서 생각이 조금 바뀌었다.
이번 버전은 단순히 기능 몇 개가 추가된 릴리즈가 아니었다. 오히려 TypeScript가 앞으로 어디로 가려는지를 보여주는 버전에 가까웠다.
진짜 본편은 아마 7.0부터 시작될 가능성이 크다.