TypeScript 실전 글 모음 | 시리즈 목차·학습 경로
이 글의 핵심
strict를 꺼 두거나 any와 as로 에러를 덮던 습관이 몇 달 뒤 런타임 버그로 돌아왔던 경험에서 출발한 학습 순서 안내입니다. 02편의 Union과 narrowing을 먼저 잡아야 제네릭 이후가 덜 꼬이는 이유, 데코레이터를 뒤로 미뤄도 되는 경우, JavaScript 글과 역할을 나눠 읽는 방법을 짧게 정리했습니다.
들어가며
이 페이지는 pkglog에 흩어져 있는 TypeScript 시리즈와 ORM 글을 한곳에 모아 둔 허브입니다. “완벽한 커리큘럼”이나 “정답 루트” 같은 것은 없다고 보시면 됩니다. 제가 다시 읽을 때나 팀에서 질문이 들어올 때 링크를 건네기 좋게 모아 둔 것이고, 아래에는 제가 실제로 거쳐 온 순서와 잘못했던 것들을 적어 두었습니다. 공식 가이드로 여기지는 마십시오.
any는 쓰지 마십시오. 꼭이요.
저도 처음에는 빨간 줄을 없애려고 any를 여기저기 붙이고 다녔습니다. 팀에서 아무도 지적하지 않았고, PR도 통과했습니다. 그런데 몇 달 뒤에 그 any 밑에 숨어 있던 것은 전부 “런타임에 터지는 버그”였습니다. unknown + narrowing(시리즈 02편에서 다룹니다)은 귀찮은 만큼만 귀찮지만, any는 나중에 이자가 붙습니다. 정 어쩔 수 없다면 eslint-disable 한 줄이라도 남겨 “여기 문제가 있다”는 흔적을 남기는 편이 낫다고 봅니다.
JavaScript 글과는 이렇게 나눠 읽는 것이 편합니다: 문법·엔진·async·모듈은 JavaScript 동작 원리: 실행 컨텍스트, 프로토타입 체인, 클로저, 이벤트 루프 쪽에서, 타입·tsconfig·제네릭·ORM 계층은 이 페이지에 링크된 typescript-series-* 글에서 보시면 됩니다. 겹치는 것처럼 보여도 실제로는 역할이 다릅니다.
저는 이 순서로 배웠습니다
아래는 “권장 로드맵”을 표로 정리한 것이 아니라, 제가 당시에 덜 고생스럽다고 느꼈던 순서입니다. 팀과 프로젝트에 맞게 순서를 바꿔도 됩니다.
먼저 01 입문에서 tsc가 돌아가게 만들고, “컴파일할 때와 실행할 때”의 차이에 대한 감을 잡았습니다. 그다음이 02 고급 타입인데, 여기서 다루는 Union과 narrowing이 뒤에 나오는 모든 내용의 바탕이 됩니다. 02를 가볍게 넘기면 04~08에서 계속 막힙니다. 03 Interface는 API와 도메인 객체의 “모양”을 잡는 데 쓰면 됩니다.
그다음 04 Generics와 05 Utility Types를 연달아 봤습니다. DTO를 한 줄씩 복사해 붙이다가 Partial / Pick을 쓰게 되는 날, 작업 효율이 확 달라집니다. 06 Decorators는 Nest나 ORM을 쓰지 않는다면 우선순위를 낮춰도 되고, “어떤 문법인지”만 봐 두어도 충분합니다. 07 고급 패턴은 라이브러리 타입을 작성할 때 빛을 발합니다. 08 REST API는 Express로 한 바퀴 돌려 보면 “타입이 끊기는 지점”이 어디인지 눈에 보입니다.
DB를 붙이게 되면 TypeORM vs Prisma가 팀에서 의견이 갈릴 때 기준을 잡는 데 도움이 되고, Prisma만 깊게 보고 싶다면 Prisma로 타입 안전한 DB 접근: 스키마, 마이그레이션, 관계 쿼리, 트랜잭션, 블로그 예제를 보시면 됩니다. 런타임 검증은 Zod와 함께 쓰는 경우가 많습니다. 설치와 설정을 다시 확인할 때는 TypeScript 시작하기 | 설치, 설정, 기본 문법을 곁에 두시면 좋습니다.
TypeScript를 배우면서 했던 실수들
- strict를 꺼 놓고 “나중에 켜겠다” — 결국 켜지 않습니다. 코드가 늘어날수록 켰을 때 쏟아지는 에러 수가 커져서 켜는 비용이 계속 오르기 때문입니다. 처음부터 켜고, 예외가 필요하면 그 이유를 적어 두십시오.
as로 단언해서 대충 넘기기 —as unknown as X는 그날 PR은 통과시켜 주지만, 다음 달에는 “왜 undefined지?”로 돌아옵니다.- API 응답을 타입만 믿고 쓰기 — 컴파일러는
fetch뒤에 무엇이 올지 모릅니다.JSON.parse도 마찬가지입니다. 외부에서 들어온 값은 Zod나 최소한의 런타임 검사로 확인하십시오. - enum에 대한 과도한 애착 — 팀에 따라 쓰는 것은 괜찮지만, 저는 Union 리터럴 +
as const쪽이 추적하기 편했습니다. (개인 의견이며, 팀 규칙을 따르면 됩니다.) 숫자 enum은Status[0]같은 역매핑 객체까지 런타임 코드로 생성되고, 숫자 enum 타입 변수에 아무 숫자나 대입해도 TypeScript 5.0 이전에는 에러가 나지 않았습니다. 또enum은 타입만 지우는 방식의 실행 방식(Node.js의 타입 스트리핑)에서는 지원되지 않고, TypeScript 5.8의erasableSyntaxOnly옵션도 이를 에러로 표시한다는 점도 요즘에는 고려 대상입니다. - 데코레이터부터 배우려고 한 것 — 06편이 나쁜 것은 아니지만 02·04편이 먼저입니다. 그렇지 않으면 “이게 왜 있지?”라는 의문만 남습니다. 게다가 TypeScript에는 데코레이터가 두 종류 있습니다. Nest·TypeORM이 쓰는 것은
experimentalDecorators플래그를 켜는 예전 방식이고, TypeScript 5.0부터 기본으로 지원하는 것은 TC39 표준 데코레이터입니다. 둘은 시그니처와 동작이 달라서, 어느 쪽 문서를 보고 있는지 확인하지 않으면 예제가 컴파일되지 않는 이유를 한참 찾게 됩니다. tsc가 통과하면 빌드도 같은 결과라고 믿기 — Vite, esbuild, SWC 같은 도구는 속도를 위해 타입 검사 없이 타입만 지우고 JavaScript를 내보냅니다. 그래서 개발 서버는 타입 에러가 있어도 잘 뜹니다. 타입 검사는 CI에서tsc --noEmit을 따로 돌려야 실제로 강제됩니다.
시리즈 본편 링크
- 01 TypeScript 시작하기
- 02 고급 타입 (Union, Intersection, Literal)
- 03 Interface
- 04 Generics
- 05 Utility Types
- 06 Decorators
- 07 조건부·템플릿 리터럴
- 08 REST API 실전
ORM·DB 쪽 (시리즈 밖)
Drizzle이나 Kysely를 쓰는 팀이라면 Drizzle ORM 같은 글이 따로 있습니다. “ORM 비교” 글이 TypeORM/Prisma에 초점을 맞추고 있더라도, 타입이 스키마와 함께 움직여야 한다는 핵심만 가져가시면 됩니다.
도구는 이 정도만 알아도 됩니다
tsconfig는 01·08 예제에 나온 설정을 팀 템플릿의 출발점으로 써도 됩니다.strict: true를 기본으로 하고, 예외는 문서로 남기십시오.paths를 쓴다면 테스트 러너와 CI에서도 똑같이 해석되도록 맞추십시오.paths는tsc가 타입 검사할 때 모듈을 찾는 규칙일 뿐, 출력된 JavaScript의 import 경로를 바꿔 주지 않습니다. 그래서tsc로 빌드한 결과를 Node로 실행하면Cannot find module '@/utils'에러가 나고, 번들러·Jest·Vitest에는 각각 같은 별칭을 따로 설정해야 합니다.- ESLint에는
@typescript-eslint를 붙이고, 포맷터는 Prettier나 Biome 중 하나만 쓰십시오. 둘 다 켜 두고 서로 충돌하게 만드는 것은 피하는 것이 좋습니다. - 테스트는 Vitest/Jest에 TS 설정을 맞추면 됩니다. E2E는 Cypress나 Playwright 글을 참고하십시오.
React·Next·Astro를 쓴다면 각 프레임워크가 요구하는 tsconfig와 경로 설정이 있으니, “우리 팀은 Express API인가, Next인가”에 맞는 글을 골라 보시면 됩니다. Next.js App Router, Astro 같은 글이 있습니다.
JavaScript → TypeScript
런타임에서만 터지는 버그는 다들 겪어 보셨을 것입니다. TypeScript를 쓰면 소스 트리 안에서는 일관성이 좋아지고, 리팩터링할 때 “어디가 깨질지”가 먼저 보입니다. 다만 “컴파일 통과 = 프로덕션 안전”은 아닙니다. HTTP·DB·JSON에서 들어온 값은 언어가 알 수 없습니다. 그래서 02편의 narrowing과 Zod를 같은 프로젝트에 함께 두는 구성이 나옵니다. 이것은 하나의 관점이므로 다르게 생각하셔도 괜찮지만, 제가 실무에서 본 사례는 대부분 이쪽이었습니다.
FAQ
Q. 글이 많은데 어디서부터 보면 되나요? A. 완전히 처음이라면 01→02→03 순서를 권합니다. JavaScript에는 익숙하고 TypeScript만 궁금하다면 04·05편을 먼저 봐도 됩니다. 01편은 설치와 옵션만 훑고 넘어가도 됩니다.
Q. 이 시리즈만으로 백엔드 실무가 가능한가요? A. 08편까지 보면 “API 한 바퀴 + 타입이 끊기지 않게 하는 법”에 대한 감은 잡힙니다. DB·마이그레이션·운영은 ORM/Prisma 글을 이어서 봐야 현실에 가까워집니다.
Q. Prisma만 쓸 예정인데 TypeORM 비교 글도 읽어야 하나요? A. 전부 읽을 필요는 없습니다. Prisma 관련 절만 골라 읽어도 되고, 레거시 TypeORM 코드가 남아 있다면 비교 글이 도움이 될 때가 있습니다.
Q. 데코레이터는 꼭 배워야 하나요? A. Nest나 일부 ORM을 쓰지 않는다면 우선순위가 낮습니다. 06편은 필요해질 때 몰아서 봐도 됩니다.
Q. 타입 에러가 너무 많이 뜹니다.
A. strict는 tsconfig 단위 옵션이라 파일마다 켜고 끌 수는 없습니다. 한꺼번에 적용하기 어렵다면 strictNullChecks, noImplicitAny처럼 strict가 묶고 있는 개별 옵션을 하나씩 켜거나, 새 코드 폴더에만 엄격한 설정을 적용하는 별도 tsconfig(project references)를 두는 방식이 현실적입니다. any 대신 unknown + 02편의 패턴을 쓰고, “에러 개수 줄이기”를 PR 목표로 삼기보다 경계(API·DB)부터 좁혀 나가십시오.
Q. enum과 Union 중 무엇을 쓰나요?
A. 팀 규칙을 따르면 됩니다. 저는 Union + as const를 선호했습니다. 숫자 enum의 역매핑 이슈를 한번 찾아보시면 그 이유를 이해하실 수 있을 것입니다.
Q. TS 5 등 최신 문법은 어디서 보나요?
A. TypeScript 시작하기 | 설치, 설정, 기본 문법과 05·07편을 함께 보면 덜 혼란스럽습니다. 버전별 변경은 공식 릴리스 노트가 가장 정확합니다. 실무에서 체감이 큰 것만 꼽으면 값의 구체 타입을 잃지 않고 형태만 검사하는 satisfies(4.9), 표준 데코레이터와 const 타입 매개변수(5.0) 정도입니다.