Skip to Content
WebTypeScriptTypeScript 실무01. JS 앱을 왜, 어떻게 TypeScript로 옮기는가

이번 편의 결과물: 지금 bookshelf(JS) 코드에서 타입이 있었다면 컴파일 시점에 잡혔을 오류 지점을 두 곳 확인합니다. 코드는 아직 바꾸지 않습니다. · 다루는 개념: allowJs/checkJs로 점진 전환하는 원칙, 빅뱅 전환의 위험, React 19의 타입 지형(@types/react, ReactNode), 이 과목의 최종 목표(모노레포) 미리보기

이 편에서 만드는 파일

이 편은 개념 편입니다. 새로 만들거나 고치는 파일이 없습니다. 대신 react_3를 마친 bookshelf의 기존 파일을 근거로 이야기합니다.

bookshelf/src/ ├── components/BookCard.jsx ← 지금은 props 타입을 문서·주석으로만 약속 └── hooks/useBooks.js ← 지금은 반환값 모양을 호출부가 기억해서 씀

개념 정리

지금 코드에서 타입이 못 잡아주는 지점

react_1부터 react_3까지 20편이 넘게 쌓인 bookshelf는 잘 동작합니다. 하지만 “잘 동작한다”와 “잘못 쓰면 즉시 알려준다”는 다른 이야기입니다. components/BookCard.jsxbook 객체를 받아 제목·저자·상태를 렌더링합니다.

// bookshelf/src/components/BookCard.jsx (일부) function BookCard({ book }) { return ( <li className="book-card"> <h3>{book.title}</h3> <p>{book.author}</p> </li> ) }

이 함수는 book이 무엇인지 코드 어디에도 적혀 있지 않습니다. 다음 두 호출은 모두 편집기에서 아무 경고 없이 통과하고, 브라우저에서 실행해야만 문제가 드러납니다.

<BookCard book={{ title: '클린 코드' }} /> <BookCard />

첫 번째는 author가 없어 화면에 빈 문단이 나옵니다. 두 번째는 bookundefinedbook.title을 읽는 순간 Cannot read properties of undefined 예외가 던져져 화면 전체가 하얗게 됩니다. 두 경우 모두 코드를 저장하는 순간이 아니라 그 코드가 실행되는 순간에야 알 수 있습니다. hooks/useBooks.js도 마찬가지입니다. 이 훅이 정확히 어떤 모양의 객체를 반환하는지는 훅 안을 직접 읽어야만 알 수 있고, 사용하는 쪽에서 필드 이름을 하나 잘못 적어도 편집기는 조용합니다.

타입스크립트는 이 두 문제를 코드를 실행하기 전, 편집기 위에서 바로 알려줍니다. book의 타입을 한 번 정의해 두면 book.titel처럼 오타가 난 순간 빨간 줄이 뜨고, BookCard 없이 호출하면 “필수 프로퍼티가 없다”는 오류가 즉시 나타납니다.

빅뱅 전환과 점진적 전환

bookshelf의 모든 파일을 하루 만에 .tsx/.ts로 바꾸는 방법(빅뱅 전환)도 있습니다. 이 과목은 그 대신 점진적 전환을 택합니다.

기준빅뱅 전환점진적 전환(이 과목)
작업 단위프로젝트 전체를 한 번에파일 하나씩, 편마다
전환 중 앱 상태다 바꾸기 전까지 실행 불가할 수 있음매 편이 끝날 때마다 정상 렌더링
오류 발생량수십–수백 개가 한꺼번에 쏟아짐방금 바꾼 파일 범위로 한정됨
롤백 난이도어려움(변경 범위가 큼)쉬움(파일 하나만 되돌리면 됨)
팀 작업 병행사실상 불가능(충돌 위험)다른 기능 개발과 병행 가능

실무에서 다루는 앱은 대부분 bookshelf보다 훨씬 큽니다. 빅뱅 전환은 그 규모에서 “전환이 끝날 때까지 아무 기능도 못 만든다”는 위험을 감수해야 합니다. 점진적 전환은 매 편(실무에서는 매 커밋)마다 앱이 계속 동작한다는 것을 대가로, 한동안 .js.ts 파일이 섞여 있는 상태를 견뎌야 합니다. 이 과목은 이 혼용 상태를 안전하게 유지하는 도구로 allowJscheckJs를 씁니다.

allowJs와 checkJs

옵션기본값켜면 생기는 일
allowJsfalse.js/.jsx 파일을 컴파일 대상에 포함하고, .ts 파일에서 import할 수 있게 한다
checkJsfalseallowJs가 켜진 상태에서, .js/.jsx 파일 안의 타입 오류까지 검사한다

checkJsallowJs가 꺼져 있으면 켤 수 없습니다. .js 파일을 아예 컴파일 대상에서 빼놓고 그 안의 타입만 검사할 방법은 없기 때문입니다. 이 과목은 02편에서 allowJs: true, checkJs: false로 시작합니다. 아직 전환하지 않은 .jsx 파일은 컴파일에는 포함되어 화면에 정상적으로 렌더링되지만, 타입 오류 검사 대상에서는 빠집니다. 파일을 하나씩 .tsx로 바꿀 때마다 그 파일만 checkJs를 켠 것과 같은 효과가 생깁니다. 04편부터 이 흐름을 실제로 진행합니다.

React 19의 타입 지형

React 자체는 타입 선언을 포함하지 않습니다. @types/react, @types/react-dom 패키지가 useState, JSX.Element, 이벤트 객체 같은 타입을 제공합니다. 이 과목에서 자주 마주칠 타입은 아래와 같습니다.

타입쓰이는 곳
ReactNode문자열, 숫자, JSX 엘리먼트, null 등 자식으로 올 수 있는 모든 값의 합집합children prop 타입(04편)
ReactElementJSX 엘리먼트만(문자열·숫자 제외)특정 컴포넌트만 자식으로 받고 싶을 때
ChangeEvent<T>, FormEvent<T>DOM 이벤트 객체에 대상 요소 타입까지 붙인 제네릭 타입입력·폼 핸들러(05편)
JSX.Element컴포넌트 함수가 반환하는 값의 타입컴포넌트 반환 타입을 명시할 때

JSX 문법을 쓰는 파일은 확장자가 .tsx여야 합니다. .ts 파일 안에 <BookCard /> 같은 태그를 쓰면 컴파일러가 이를 비교 연산자로 오해해 오류를 냅니다. 04편에서 컴포넌트 파일을 .jsx에서 .tsx로 바꿀 때 이 규칙이 그대로 적용됩니다.

이 과목의 최종 목표: bookshelf-ts 모노레포

02편부터 22편까지 거치는 전체 경로를 미리 봅니다.

bookshelf-ts/ ← pnpm 워크스페이스(14편부터) ├── apps/ │ └── web/ ← bookshelf(JS)가 전부 .tsx/.ts로 전환된 상태 ├── packages/ │ ├── shared-types/ ← 도메인 타입·zod 스키마 공유(15편) │ └── ui/ ← BookCard 등 재사용 컴포넌트, tsdown 빌드(17~18편) └── .github/workflows/ci.yml ← 타입 체크·린트·테스트·빌드 자동화(21편)

02편에서는 아직 apps/·packages/ 구분 없이 bookshelf-ts/ 단일 프로젝트로 시작합니다. 03편에서 이 구조를 나누는 이유를 먼저 짚고, 실제 전환은 14편부터 진행합니다.

직접 해보기

hooks/useBooks.js를 열어, 이 훅이 반환하는 객체에 어떤 필드가 있는지 코드만 보고 나열해 보세요. 그다음 BookListPage.jsx에서 그 필드들을 실제로 어떤 이름으로 쓰고 있는지 대조해 보세요.

useBooksuseQuery를 감싸고 있다면 반환값은 data, isLoading, isError, error 등입니다. 이 이름을 하나라도 다르게 적어 써도(isloading처럼) 지금은 편집기가 알려주지 않고, 실행해서 화면이 이상하게 나와야 알아챌 수 있습니다. 06편에서 이 훅에 반환 타입을 명시하면 이런 오타가 바로 빨간 줄로 표시됩니다.

자주 하는 실수

증상원인고치는 법
전환을 시작하자마자 파일 수십 개에서 오류가 쏟아진다.js를 전부 .ts로 확장자만 바꾸고 strict 옵션을 처음부터 최고 강도로 켬allowJs로 기존 .js는 그대로 두고, 파일 하나를 .tsx로 바꿀 때만 그 파일의 오류를 본다
.jsx 파일 안 JSX 태그에서 알 수 없는 오류가 난다확장자를 .ts로만 바꾸고 .tsx로 바꾸지 않음JSX를 쓰는 파일은 반드시 .tsx
checkJs를 켰는데 오류가 안 남allowJs가 꺼져 있어 checkJs 자체가 무시됨allowJs: true를 먼저 켠다

확인 문제

문제 14지선다
지금의 BookCard.jsx에서 book prop 없이 컴포넌트를 호출해도 편집기가 조용한 이유는 무엇입니까
문제 24지선다
이 과목이 빅뱅 전환 대신 점진적 전환을 선택한 가장 큰 이유는 무엇입니까
문제 34지선다
allowJs가 false인 상태에서 checkJs를 true로 설정하면 어떻게 됩니까
문제 44지선다
JSX 태그를 쓰는 파일의 확장자를 tsx가 아니라 ts로 지정하면 생기는 문제는 무엇입니까
문제 54지선다
이 과목의 최종 결과물 구조에서 packages/shared-types가 담당하는 역할은 무엇입니까

참고 자료

Last updated on