TypeScript 7 설치와 tsconfig strict 첫 실행

··설명 약 10분·작성: 해비·블로그 소개

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

TypeScript 설치 후 tsconfig strict 검사가 작동하는지 어떻게 확인하나요?

TypeScript 설치 뒤에는 프로젝트의 tsc 버전과 tsconfig가 검사하는 파일을 먼저 확인합니다. 정상 Task 객체는 통과하고 title에 숫자를 넣으면 오류가 나는지 비교하면 설치 문제와 타입 문제를 나눌 수 있습니다.

적용 조건·범위: 기존 최소 예제 확인 기록: TypeScript 7.0.2 별도 최소 파일 검사: 숫자 title은 TS2322, 문자열로 수정 후 exit 0. 블로그 안내의 npm 설치, tsconfig 생성과 전체 TaskFlow 프로젝트 검사는 미실행·미확인.

본문 근거: TypeScript 7.0 설정·명령행 변화

직접 확인: 타입 오류와 수정

TypeScript 7 설치와 tsconfig strict 첫 실행 · 검증 결과 보기

검증 결과 보기: 고정된 오류 예제와 수정 예제의 실제 tsc 진단을 비교하세요. 브라우저 실시간 컴파일러는 아닙니다.

예제 크게 보기 (새 탭)
export type Task = { id: string; title: string; completed: boolean };
const task: Task = { id: '1', title: 123, completed: false };

예상 오류: TS2322: number는 string에 할당할 수 없습니다. title을 문자열로 수정합니다.

export type Task = { id: string; title: string; completed: boolean };
const task: Task = { id: '1', title: '공부하기', completed: false };
직접 실행: 환경 준비와 전체 파일

단독 검사는 tsconfig.json이 없는 새 폴더에서 example.ts 한 파일씩 교체하며 진행합니다. TypeScript가 없다면 설치 글에서 Node.js/npm·TypeScript 준비를 확인하세요. 타입 검사 성공은 코드 실행 성공과 구분합니다.

독자 질문: 처음 .ts 파일을 만들었을 때 타입 검사가 실제로 동작하는지 어떻게 확인할까요?

얻을 결과: src/types/task.ts를 만들고 정상 객체는 오류 0, title에 숫자를 넣으면 타입 오류를 확인합니다.

다음 코드를 별도 example.ts에 복사합니다.

오류 예제를 아래 코드로 교체합니다.

npx tsc --strict --noEmit example.ts

기존 최소 예제 확인 기록: TypeScript 7.0.2 별도 최소 파일 검사: 숫자 title은 TS2322, 문자열로 수정 후 exit 0. 블로그 안내의 npm 설치, tsconfig 생성과 전체 TaskFlow 프로젝트 검사는 미실행·미확인.

npx tsc --noEmit은 현재 프로젝트 tsconfig의 include 대상 전체를 검사합니다. config가 있는 폴더에서 파일명을 직접 넘기면 TypeScript 7 진단이 발생할 수 있습니다. 프로젝트는 -p .로 검사하고 아래 한 파일 검사는 config가 없는 별도 폴더에서 진행합니다.

다음 학습: 04회차에서 Task 객체·addTask 매개변수와 반환 타입을 함께 지정합니다.

설치

프로젝트 루트에서 다음 명령을 실행합니다.

npm install -D typescript

버전을 확인합니다.

npx tsc --version

TypeScript 7.x가 표시되는지 확인합니다.

tsconfig 생성

npx tsc --init

현재 TypeScript가 제공하는 기본 설정을 생성합니다.

오래된 블로그의 tsconfig를 통째로 복사하기보다 현재 버전이 생성한 설정을 기준으로 필요한 옵션을 이해하는 편이 좋습니다.

TypeScript 7의 중요한 기본 변화

TypeScript 7은 TypeScript 6에서 도입된 새로운 기본값을 이어받습니다.

대표적으로 다음 방향을 이해하면 됩니다.

strict
→ 기본 true


module
→ 기본 esnext


target
→ 최신 안정 ECMAScript 기준


types
→ 기본 []

Vite 같은 bundler 프로젝트에서는 실제 프로젝트 템플릿이 자체 tsconfig를 제공하므로 그 설정을 우선합니다.

학습용 최소 tsconfig

순수 TypeScript TaskFlow를 위한 간단한 예제입니다.

{
  "compilerOptions": {
    "strict": true,
    "noEmit": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "target": "es2025"
  },
  "include": [
    "src"
  ]
}

환경에 따라 target 값은 달라질 수 있습니다.

중요한 것은 옵션을 무조건 복사하는 것이 아니라 실행 환경에 맞추는 것입니다.

strict

strict는 여러 엄격한 타입 검사 옵션을 묶어 활성화합니다.

{
  "compilerOptions": {
    "strict": true
  }
}

TypeScript 7에서는 기본값도 true지만 학습 문서에서는 의도를 명시하기 위해 직접 적어도 됩니다.

noEmit

이번 과정은 타입 검사를 중심으로 진행하므로 JavaScript 출력이 필요하지 않은 단계에서는 noEmit을 사용할 수 있습니다.

{
  "compilerOptions": {
    "noEmit": true
  }
}

검사:

npx tsc --noEmit

Vite 프로젝트에서도 build 전에 타입 검사를 별도로 수행할 때 자주 사용하는 방식입니다.

첫 TypeScript 파일

src/task.ts:

type Task = {
  id: string;
  title: string;
  completed: boolean;
};


const task: Task = {
  id: '1',
  title: 'TypeScript 설치',
  completed: false,
};


console.log(task);

검사:

npx tsc --noEmit

오류가 없으면 첫 환경 준비는 완료입니다.

오류 재현

title에 number를 넣습니다.

const task: Task = {
  id: '1',
  title: 123,
  completed: false,
};

TypeScript는 string property에 number를 할당할 수 없다고 알려줍니다.

TypeScript 7에서 오래된 설정 주의

TypeScript 7에서는 TypeScript 6에서 deprecated된 일부 설정이 hard error가 될 수 있습니다.

예를 들어 오래된 moduleResolution node/node10, baseUrl 중심 설정, target es5 같은 설정을 새 프로젝트의 기본 예제로 사용하지 않습니다.

이 부분은 22회차에서 별도로 점검합니다.

자주 발생하는 오류

tsc 명령을 찾지 못합니다

global 설치를 전제로 하지 말고 npx를 사용합니다.

npx tsc --version

전체 node_modules 타입 오류가 보입니다

프로젝트 설정과 include, types 구성을 확인합니다.

TypeScript 7에서는 types 기본값 변화가 있으므로 필요한 global @types package가 있다면 명시적으로 추가할 수 있습니다.

tsconfig와 명령행 파일 경로를 섞습니다

tsconfig가 있는 프로젝트에서는 project 단위 검사 흐름을 사용하는 편이 단순합니다.

npx tsc --noEmit

위 설치 흐름은 tsconfig를 사용하는 프로젝트 검사입니다. 아래 example.ts 비교는 tsconfig가 없는 별도 폴더의 단일 파일 검사이므로 두 명령을 섞지 않습니다. TypeScript 7에서는 config가 있는 폴더에 파일명을 직접 넘기면 진단이 발생할 수 있으므로 프로젝트에서는 -p .를 사용합니다.

원리 확인: TypeScript 7.0 설정·명령행 변화 (2026-10-12). 아래 기대 결과와 기존 실행 기록은 구분해서 읽습니다.

학습 완료 체크리스트

검사 대상 파일, 사용한 tsconfig, 설치된 tsc 버전을 함께 말해 보세요. 정상 title은 허용되고 숫자 title은 거부되며 --noEmit으로 JavaScript 파일이 생기지 않는 이유를 설명하는 것이 첫 실행의 완료 기준입니다.


작은 변형으로 확인하기

정상 예제의 completed를 문자열 false로 바꾸면 boolean 할당 오류가 나야 합니다. false로 복원하면 오류가 사라집니다. --noEmit은 콘솔 출력을 실행하는 명령이 아닙니다.

선택 실습: 할 일 앱에 적용하기

TaskFlow는 작업을 추가·완료·삭제하는 할 일 앱입니다. 이 선택 실습에서는 JavaScript 코드를 TypeScript로 확장합니다.

TaskFlow 복사본에 TypeScript를 설치하고 src/types/task.ts를 첫 TypeScript 파일로 추가합니다.

선택: 연재 프로젝트와 전체 자료

연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.

TaskFlow 프로젝트 적용

TypeScript 7 설치와 tsconfig strict 첫 실행 · 단계별 TaskFlow

기존 JavaScript 14단계 화면을 사용하며 이번 단계는 TypeScript 문서·타입·설정을 준비합니다.

예제 크게 보기 (새 탭)

시작·완료 파일과 적용 순서에서 이번 변경 파일의 전체 내용과 적용 순서를 확인합니다.

시작본과 이번 변경

TypeScript란 무엇인가: JavaScript와 차이 정리의 완료 체크포인트를 기준으로 이어갑니다. 변경하지 않는 파일은 그 시작 상태를 유지합니다.

기존 package.json에 TypeScript 개발 의존성을 추가하고 tsconfig.json의 include를 src로 설정합니다. src/types/task.ts를 새로 만든 뒤 tsc -p . --noEmit으로 검사합니다. 기존 JavaScript 실행 경로는 별개로 유지됩니다.

이전 체크포인트

typescript-step-02-intro

현재 상태

TaskFlow는 JavaScript로 동작하고 TypeScript compiler가 없습니다.

이번 목표

TaskFlow 복사본에 TypeScript를 설치하고 src/types/task.ts를 첫 TypeScript 파일로 추가합니다.

변경 파일

package.json
tsconfig.json
src/types/task.ts
전체 파일·코드 보기

src/types/task.ts

export type Task = {
  id: string;
  title: string;
  completed: boolean;
};

타입 검사

npx tsc --noEmit

오류가 없어야 합니다.

중간부터 시작하는 전체 파일과 적용 순서

이번 글 시작 ZIP · 이번 글 완료 ZIP

시작 ZIP은 직전 프로젝트 단계 typescript-step-02-intro의 실제 완료 파일입니다. 프로젝트 순서를 이어가는 기준이며 사이트의 기술 읽기 순서와는 구분합니다.

  1. 시작 ZIP을 새 폴더에 풉니다. 이 폴더가 작업 루트이며 기존 체크포인트 파일을 별도로 찾을 필요가 없습니다.
  2. package.json·tsconfig.json·타입 계약 검증 파일을 추가합니다. 설치 후 npm run typecheck와 npm run build를 실행합니다.
  3. 아래 변경 파일을 같은 상대 경로에 추가하거나 전체 교체합니다. 표시하지 않은 파일과 images는 시작본 그대로 유지합니다. package-lock.json은 완료 ZIP에 있는 파일을 함께 적용합니다.
  4. 작업 루트에서 명령을 실행하고 예상 결과를 확인합니다. 완료 ZIP은 직접 적용한 결과를 비교하는 기준입니다.

추가·교체 파일

  • package.json — 전체 교체
  • CHANGES.md — 전체 교체
  • README.md — 전체 교체
  • tsconfig.json — 추가
  • scripts/test.mjs — 추가
  • scripts/check.mjs — 추가
  • public/data/tasks.json — 추가
  • tests/contracts.ts — 추가
  • package-lock.json — 추가

실행과 확인

npm ci
npm run dev
npm run typecheck
npm test
npm run build

개발 서버는 실행 상태로 두고 다른 터미널에서 검사 명령을 실행하세요. Node.js 24.18.0에서 검증했습니다. JavaScript는 http://127.0.0.1:4173, TypeScript는 Vite 실행 출력의 주소로 접속합니다.

예상 결과: strict 검사에서 정상 계약은 통과하고 잘못된 completed 타입은 기대한 컴파일 오류로 검증됩니다.

기존 ZIP 검증 기록: TypeScript strict 검사·긍정/부정 타입 계약·런타임 검사·Vite 빌드 통과. 시작 ZIP에 이 글의 변경 파일만 적용한 결과와 완료 ZIP의 모든 파일 내용이 일치했습니다. 브라우저 화면 배치·실제 기기·외부 CDN 가용성은 이 검사에 포함하지 않았습니다.

이 절의 전체 파일은 ZIP과 일치합니다. 위쪽 원문의 짧은 예제나 생략 표시가 있는 부분 코드는 개념·변경 위치 설명용입니다. 서버 저장과 인증은 제공하지 않으며 JSON fixture는 로컬 읽기 실습용입니다.

복사할 변경 파일 전체 코드

package.json — 전체 파일

{
  "name": "taskflow-typescript-checkpoints",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite --host 127.0.0.1",
    "start": "vite --host 127.0.0.1",
    "typecheck": "tsc --noEmit",
    "build": "tsc --noEmit && vite build",
    "test": "node scripts/test.mjs"
  },
  "devDependencies": {
    "typescript": "7.0.2",
    "vite": "8.3.0"
  },
  "dependencies": {}
}

tsconfig.json — 전체 파일

{
  "compilerOptions": {
    "strict": true,
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "lib": [
      "ES2022",
      "DOM"
    ],
    "jsx": "react-jsx",
    "noEmit": true,
    "verbatimModuleSyntax": true,
    "skipLibCheck": true,
    "types": []
  },
  "include": [
    "src",
    "tests/contracts.ts"
  ]
}

scripts/test.mjs — 전체 파일

import { spawnSync } from 'node:child_process';
const compiler = spawnSync(process.execPath, ['node_modules/typescript/bin/tsc', '--noEmit', 'false', '--outDir', '.test-output', '--pretty', 'false'], {stdio:'inherit'});
if (compiler.status !== 0) process.exit(compiler.status ?? 1);
const test = spawnSync(process.execPath, ['scripts/check.mjs'], {stdio:'inherit'});
process.exit(test.status ?? 1);

scripts/check.mjs — 전체 파일

import assert from 'node:assert/strict';
import { existsSync } from 'node:fs';
const load = name => import('../.test-output/src/'+name+'.js');
const has = name => existsSync(new URL('../.test-output/src/'+name+'.js',import.meta.url));
let count=0;
if(has('core/tasks')) {
 const c=await load('core/tasks'); const initial=[{id:'1',title:'공부',completed:false}];
 const added=c.addTask(initial,'새 작업');assert.equal(initial.length,1);assert.equal(added.length,2);
 assert.equal(c.toggleTask(added,'1')[0].completed,true);assert.equal(initial[0].completed,false);
 assert.equal(c.deleteTask(added,'missing').length,2);assert.equal(c.deleteTask(added,'1').length,1);count++;
}
if(has('core/selection')) {const c=await load('core/selection');assert.equal(c.getSelectedTask([],null),null);assert.equal(c.getSelectedTask([],'missing'),null);assert.equal(c.readTitle(null),null);assert.equal(c.readTitle({title:7}),null);assert.equal(c.readTitle({title:'ok'}),'ok');count++;}
if(has('core/collection')) {const c=await load('core/collection');assert.equal(c.first([]),undefined);assert.equal(c.last([1,2]),2);if(c.findById){assert.equal(c.findById([], 'x'),undefined);assert.equal(c.getValue({title:'ok'},'title'),'ok');}count++;}
if(has('core/edit')) {const c=await load('core/edit');const t=c.createTask({title:'ok'});assert.equal(t.completed,false);assert.notEqual(c.createTask({title:'ok'}).id,t.id);assert.equal(c.updateTask(t,{completed:true}).completed,true);assert.equal(t.completed,false);count++;}
if(has('core/state')) {const c=await load('core/state');assert.equal(c.getStateMessage({status:'idle'}),'대기 중');assert.equal(c.getStateMessage({status:'loading'}),'불러오는 중');assert.equal(c.getStateMessage({status:'success',data:[]}), '0개 작업');assert.equal(c.getStateMessage({status:'error',error:new Error('failed')}),'failed');count++;}
if(has('guards/task')) {const c=await load('guards/task');assert.equal(c.isTaskArray([]),true);for(const bad of [null,{},[{id:'1',title:1,completed:false}],[{id:'1',title:'x',completed:'no'}]])assert.equal(c.isTaskArray(bad),false);const api=await load('api/tasks');globalThis.fetch=async()=>({ok:false});await assert.rejects(()=>api.getTasks(),/요청/);globalThis.fetch=async()=>({ok:true,json:async()=>({bad:true})});await assert.rejects(()=>api.getTasks(),/형식/);globalThis.fetch=async()=>({ok:true,json:async()=>[]});assert.deepEqual(await api.getTasks(),[]);count++;}
if(has('components/TaskList')) {const React=await import('react');const {renderToStaticMarkup}=await import('react-dom/server');const {default:C}=await load('components/TaskList');const out=renderToStaticMarkup(React.createElement(C,{tasks:[{id:'1',title:'<unsafe>',completed:false}],onToggle(){},onDelete(){}}));assert(out.includes('&lt;unsafe&gt;'));assert(out.includes('삭제'));assert.equal((out.match(/<li/g)||[]).length,1);count++;}
if(has('components/TaskDetail')) {const React=await import('react');const {renderToStaticMarkup}=await import('react-dom/server');const {default:C}=await load('components/TaskDetail');assert(renderToStaticMarkup(React.createElement(C,{task:null})).includes('선택'));count++;}
if(has('features/tasks/mapper')) {const c=await load('features/tasks/mapper');assert.deepEqual(c.toTask({task_id:'x',task_title:'title',done:true}),{id:'x',title:'title',completed:true});count++;}
console.log(`PASS ${count} runtime groups; compile-time positive/negative contracts checked`);

public/data/tasks.json — 전체 파일

[
  {
    "id": "server-1",
    "title": "서버 작업 불러오기",
    "completed": false,
    "createdAt": "2026-09-20T02:00:00.000Z",
    "dueAt": null
  },
  {
    "id": "server-2",
    "title": "fetch 상태 확인",
    "completed": true,
    "createdAt": "2026-09-20T03:00:00.000Z",
    "dueAt": null
  }
]

tests/contracts.ts — 전체 파일

import type { Task } from '../src/types/task.js';
const valid: Task = {id:'1',title:'공부',completed:false};
// @ts-expect-error completed must be boolean
const invalid: Task = {id:'1',title:'공부',completed:'no'};
void valid; void invalid;

완료 체크포인트

typescript-step-03-install

다음 학습

다음 글에서는 string, number, boolean, array, object, 함수 매개변수와 반환 타입을 TaskFlow 코드에 실제로 적용합니다.

다음 글: TypeScript 기본 타입: 객체와 함수까지 strict로 익히기

글 소개·학습 준비·전체 목차
TypeScript 7 설치와 tsconfig strict 첫 실행

TypeScript · 3 / 22 · 필수 학습 · TypeScript 7 설치와 tsconfig

설치한 TypeScript의 strict 검사가 실제로 동작하는지 어떻게 확인할까요? 프로젝트의 tsc 버전을 확인하고 tsconfig가 포함한 .ts 파일을 검사합니다. 정상 객체와 숫자를 넣은 title을 번갈아 검사하면 환경 준비와 타입 오류를 구분할 수 있습니다. noEmit 검사는 JavaScript 실행과 별개입니다.

TypeScript 7은 Go로 포팅된 네이티브 컴파일러를 사용하며 기존 TypeScript 6과 같은 type-checking 의미를 최대한 유지하면서 빌드 성능을 크게 개선한 버전입니다.

이번 글에서 배우는 내용

  • TypeScript 설치
  • tsc 버전 확인
  • tsconfig 생성
  • strict
  • noEmit
  • include
  • module과 target 기본 방향
  • 첫 .ts 파일 검사

단독 학습 선수 지식

JavaScript 객체·함수, Node.js·npm 명령 실행을 알면 됩니다. 이전 TaskFlow 파일 없이 직접 확인 예제로 학습할 수 있습니다.

목차

이 글이 도움이 되었나요?

조회 중

읽음 기록

읽음 — / 전체 22개 · 필수 21개

이 글을 쓴 해비

웹사이트를 제작·운영하고, 개인 서비스와 자동화·AI 도구를 만듭니다. 직접 만들며 배운 내용과 문제 해결 과정을 BlogFlow에 기록합니다.

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기