TypeScript ํ๋ก์ ํธ์ ๊ท๋ชจ๊ฐ ์ปค์ง๋ฉด์ ์ด๋ ๋ ๋ฌธ๋ ๊นจ๋ซ๊ฒ ๋ฉ๋๋ค. ์ฝ๋๋ฅผ ์์ ํ๊ณ ์ ์ฅํ ๋๋ง๋ค VS Code์ ํ์ ์ถ๋ก (IntelliSense)์ด ๋ฒ๋ฒ ์ด๊ณ , tsc ๋น๋ ์๊ฐ์ ํ์ผ์์ด ๋์ด๋ฉ๋๋ค. ์ด๋ ๋จ์ํ ๋ถํธํจ์ ๋์ด, ์ฐ๋ฆฌ์ ์์คํ ๊ฐ๋ฐ ์์ฐ์ฑ๊ณผ ํ๋ฆ์ ๋์ด๋ฒ๋ฆฌ๋ ์ฌ๊ฐํ ๋ฌธ์ ๋ก ์ด์ด์ง๋๋ค.
๋๋ฆฐ ์ปดํ์ผ์ ์ ๋ฐ์ํ๋ฉฐ, ์ฐ๋ฆฌ๋ ์ด ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์์๊น์?
์ค๋์ TypeScript์ ์ปดํ์ผ ํ์ ์ฑ๋ฅ์ ์ ํ์ํค๋ ์ฃผ๋ฒ๋ค์ ์ฌ์ธต ๋ถ์ํ๊ณ , ๋ณต์กํ ํ์ ์ฐ์ฐ๋ถํฐ ํ๋ก์ ํธ ์ค์ ๊น์ง ์์ฐ๋ฅด๋ ๊ตฌ์ฒด์ ์ด๊ณ ์ค์ฉ์ ์ธ ์ต์ ํ ์ ๋ต์ ์์ธํ ์์๋ณด๊ฒ ์ต๋๋ค.
1. ์ ์ปดํ์ผ ์ฑ๋ฅ์ด ์ค์ํ๊ฐ?: ๋จ์ํ ์๋ ๊ทธ ์ด์์ ๋ฌธ์
๋๋ฆฐ ์ปดํ์ผ์ ๋จ์ํ '๊ธฐ๋ค๋ฆฌ๋ ์๊ฐ'์ด ๊ธธ์ด์ง๋ ๊ฒ๋ง์ ์๋ฏธํ์ง ์์ต๋๋ค.
- ์ง์ฐ๋๋ ํผ๋๋ฐฑ: ์ฝ๋ ์๋ํฐ์์ ํ์ ์ค๋ฅ๋ฅผ ํ์ธํ๋ ๋ฐ ๋ช ์ด์ฉ ๊ฑธ๋ฆฐ๋ค๋ฉด, ์ค์๊ฐ์ผ๋ก ์ฝ๋๋ฅผ ์์ ํ๊ณ ๊ฒ์ฆํ๋ TypeScript์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ด ์ฌ๋ผ์ง๋๋ค.
- ์ธ์ง ๋ถํ ์ฆ๊ฐ: ์ฆ์ ๋ฒ๋ฒ ์์ ๊ฐ๋ฐ์์ ์ง์ค๋ ฅ์ ํฉํธ๋ฆฌ๊ณ , ๋ฌธ์ ํด๊ฒฐ์ ํ๋ฆ์ ๋ฐฉํดํ์ฌ ๋ถํ์ํ ์ธ์ง ๋ถํ๋ฅผ ์ ๋ฐํฉ๋๋ค.
- CI/CD ์๊ฐ ์ฆ๊ฐ: ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ๋ฟ๋ง ์๋๋ผ, ์ฝ๋๋ฅผ ๋น๋ํ๊ณ ๋ฐฐํฌํ๋ CI/CD ํ์ดํ๋ผ์ธ์ ์๊ฐ๋ ๊ธธ์ด์ ธ ์ ์ฒด ํ์ ์์ฐ์ฑ์ ์ํฅ์ ๋ฏธ์นฉ๋๋ค.
๊ฒฐ๊ตญ ์ปดํ์ผ ์ฑ๋ฅ์ ๊ณง ๊ฐ๋ฐ์ ๊ฒฝํ(DX, Developer Experience) ๊ณผ ์ง๊ฒฐ๋๋ ํต์ฌ์ ์ธ ์์์ ๋๋ค.
2. ์ฃผ๋ฒ 1: ๊ณผ๋ํ๊ฒ ๋ณต์กํ ํ์ ์ฐ์ฐ
์ปดํ์ผ ์ฑ๋ฅ ์ ํ์ ๊ฐ์ฅ ํํ ์์ธ์ ํ์ ์ด์์ผ๋ก ๋ณต์กํ๊ณ ๊ด๋ฒ์ํ ํ์ ์ฐ์ฐ์ ๋๋ค.
๊ฐ. ๊ฑฐ๋ํ ์ ๋์จ๊ณผ Mapped Types์ ํจ์
Mapped Types๋ ๋งค์ฐ ๊ฐ๋ ฅํ์ง๋ง, ์์ฒ ๊ฐ์ ํค๋ฅผ ๊ฐ์ง ๊ฑฐ๋ํ ๊ฐ์ฒด ํ์ ์ ์ง์ ์ ์ฉํ๋ ์๊ฐ ์ปดํ์ผ๋ฌ์ ์ ๋ชฝ์ด ์์๋ฉ๋๋ค.
๐ฉ Bad: ๋ชจ๋ ๊ฒฝ์ฐ์ ์๋ฅผ ๋ฏธ๋ฆฌ ๊ณ์ฐํ๋ ์ ๊ทผ๋ฒ
์๋ฐฑ ๊ฐ์ ์์ด์ฝ ์ ๋ณด๋ฅผ ๋ด์ ๊ฐ์ฒด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก, ๋ชจ๋ ์์ด์ฝ์ boolean prop์ผ๋ก ๋ฐ์ ์ ์๋ Icon ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ ๋ค๊ณ ๊ฐ์ ํด ๋ด ์๋ค.
// icons.ts - 500+๊ฐ์ ์์ด์ฝ์ด ์๋ค๊ณ ๊ฐ์
export const ICONS = {
add: '<svg>...</svg>',
arrowDown: '<svg>...</svg>',
// ...
wallet: '<svg>...</svg>',
};
export type IconName = keyof typeof ICONS; // "add" | "arrowDown" | ...
// Bad: 500๊ฐ ์ด์์ ์ ๋์จ์ ๋ํด Mapped Type์ ์คํ
// ์ปดํ์ผ๋ฌ๋ Icon ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฉ๋ ๋๋ง๋ค ์ด ๊ฑฐ๋ํ ํ์
์ ๊ณ์ฐํด์ผ ํ๋ค.
type IconProps = {
[K in IconName]?: boolean; // 500๋ฒ ์ด์์ ์ฐ์ฐ ๋ฐ์
};
function Icon(props: IconProps) {
// ...
}
์ด ๋ฐฉ์์ Icon ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฉ๋ ๋๋ง๋ค 500๊ฐ๊ฐ ๋๋ ์ต์ ๋ ํ๋กํผํฐ๋ฅผ ๊ฐ์ง ๊ฑฐ๋ํ ํ์ ์ ๊ณ์ฐํ๊ฒ ๋ง๋ญ๋๋ค. ์ค์ ๋ก๋ ํ๋ ๊ฐ์ ์์ด์ฝ๋ง ์ฌ์ฉํ๋๋ฐ๋ ๋ง์ด์ฃ .
๐ Good: ์ ๋ค๋ฆญ์ ํตํ '์ง์ฐ' ๋ฐ '์ถ๋ก ' ํ์ฉ
๋ ๋์ ์ ๊ทผ๋ฒ์ ์ฌ์ฉ ์์ ์ ํ์ํ ๋งํผ๋ง ํ์ ์ ์ถ๋ก ํ๋๋ก ์ ๋ค๋ฆญ์ ํ์ฉํ๋ ๊ฒ์ ๋๋ค. ํ์ ๊ณ์ฐ์ ์์ ์ '๋ฏธ๋ฆฌ'์์ 'ํ์ํ ๋'๋ก ๋ฏธ๋ฃจ๋ ์ ๋ต์ ๋๋ค.
// Good: ์ ๋ค๋ฆญ์ ์ฌ์ฉํด ์ค์ ์ ๋ฌ๋ props๋ง ํ์
์ผ๋ก ์ถ๋ก
type IconProps<T extends IconName> = {
[K in T]?: boolean;
};
function Icon<T extends IconName>(props: IconProps<T>) {
// ...
}
// ์ฌ์ฉ ์์ ์๋ "add", "wallet" ๋ ๊ฐ์ ํค์ ๋ํด์๋ง ํ์
์ฐ์ฐ์ด ์ผ์ด๋๋ค.
<Icon add wallet />;
์ด์ฒ๋ผ ์ ๋ค๋ฆญ์ ์ฌ์ฉํ๋ฉด, TypeScript๋ Icon ์ปดํฌ๋ํธ๊ฐ ์ค์ ๋ก ์ฌ์ฉ๋ ๋ ์ ๋ฌ๋ ํ๋กญ(add, wallet)๋ง์ ๊ธฐ๋ฐ์ผ๋ก ํ์ ์ ๊ณ์ฐํ๋ฏ๋ก ์ปดํ์ผ ๋ถ๋ด์ด ๊ทน์ ์ผ๋ก ์ค์ด๋ญ๋๋ค.
๋. ์กฐ๊ฑด๋ถ ํ์ (Conditional Types)์ ์๋์น ์์ ๋ถ๋ฐฐ
์กฐ๊ฑด๋ถ ํ์ ์ด ์ ๋ค๋ฆญ ์ ๋์จ ํ์ ์ ๋ง๋๋ฉด, ๊ฐ ์ ๋์จ ๋ฉค๋ฒ์ ๋ํด ๊ฐ๋ณ์ ์ผ๋ก ํ์ ์ ๊ณ์ฐํ๋ ๋ถ๋ฐฐ ๋ฒ์น(Distributive Conditional Types) ์ด ์ ์ฉ๋ฉ๋๋ค. ์ด๋ ๋๋ก ์ฑ๋ฅ ์ ํ์ ์๋์น ์์ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํฉ๋๋ค.
๐ฉ Bad: ๋ถ๋ฐฐ ๋ฒ์น์ผ๋ก ์ธํด ํ์ ์ด ๋๋ฌด ๋์ด์ง๋ ๊ฒฝ์ฐ
API ๋ผ์ฐํธ ๊ฒฝ๋ก("/users", "/users/:id") ์ค ํ๋ผ๋ฏธํฐ๊ฐ ํฌํจ๋ ๊ฒฝ๋ก๋ง ์ถ์ถํ๋ ์ ํธ๋ฆฌํฐ ํ์ ์ ๋ง๋ ๋ค๊ณ ๊ฐ์ ํด ๋ด ์๋ค.
type Routes = "/users" | "/users/:id" | "/posts" | "/posts/:id";
// `:id`๊ฐ string์ ์๋ธํ์
์ด๋ฏ๋ก ๋ชจ๋ Routes๊ฐ string์ผ๋ก ๋ณํ๋์ด ๋ฒ๋ฆฐ๋ค.
type RoutesWithParams<T> = T extends `/${string}/:id` ? T : never;
// ์๋: "/users/:id" | "/posts/:id"
// ์ค์ ๊ฒฐ๊ณผ: string
// ์? ("users" extends `...`) | ("/users/:id" extends `...`) | ... ๋ก ๋ถ๋ฐฐ๋์ด
// ์๋์ ๋ค๋ฅด๊ฒ ๋์ํ๋ฉฐ ๋ถํ์ํ ์ฐ์ฐ์ ์ํํ๋ค.
type DynamicRoutes = RoutesWithParams<Routes>;
๐ Good: []๋ก ๋ถ๋ฐฐ ๋ฐฉ์งํ๊ธฐ
์ ๋์จ ํ์ ์์ฒด๋ฅผ ํ๋์ ๋จ์๋ก ๊ฒ์ฌํ๊ณ ์ถ๋ค๋ฉด, ํ์ ์ ๋๊ดํธ([])๋ก ๊ฐ์ธ ํํ๋ก ๋ง๋ค์ด ๋ถ๋ฐฐ๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
// Good: [T] extends [U] ํํ๋ก ๊ฐ์ธ ๋ถ๋ฐฐ๋ฅผ ๋ฐฉ์งํ๋ค.
type RoutesWithParams<T> = [T] extends [`/${string}/:id`] ? T : never;
// ์ด์ ์ ๋์จ ์ ์ฒด๊ฐ ํ๋์ ๋จ์๋ก ์ทจ๊ธ๋์ด ์๋๋๋ก ๋์ํ์ง ์๋๋ค.
// ์ด๋ฐ ๊ฒฝ์ฐ์ ๋ค๋ฅธ ์ ๊ทผ์ด ํ์ํ์ง๋ง, ๋ถ๋ฐฐ ๋ฐฉ์ง ์๋ฆฌ๋ ๋์ผํ๋ค.
// ๋ ์ ์ ํ ๋ถ๋ฐฐ ๋ฐฉ์ง ์์
type Wrap<T> = [T] extends [any] ? T[] : T;
type T1 = Wrap<string | number>; // ๊ฒฐ๊ณผ: (string | number)[] -> ๋ถ๋ฐฐ๋์ง ์์
๋ถ๋ฐฐ ๋ฒ์น์ ์ดํดํ๊ณ ์ ์ดํ๋ ๊ฒ๋ง์ผ๋ก๋ ๋ณต์กํ ํ์ ์ ์ฐ์ฐ ํ์๋ฅผ ํฌ๊ฒ ์ค์ผ ์ ์์ต๋๋ค.
๋ค. ์ฌ๊ท ํ์ (Recursive Types)์ ๋ฌดํ ๋ฃจํ
์๊ธฐ ์์ ์ ์ฐธ์กฐํ๋ ์ฌ๊ท ํ์ ์ ๋งค์ฐ ๊ฐ๋ ฅํ์ง๋ง, ์ข ๋ฃ ์กฐ๊ฑด์ด ๋ช ํํ์ง ์์ผ๋ฉด ์ปดํ์ผ๋ฌ๋ฅผ ๋ฌดํ ๋ฃจํ์ ๋น ๋จ๋ ค "Type instantiation is excessively deep and possibly infinite." ์๋ฌ๋ฅผ ๋ฐ์์ํต๋๋ค.
๐ฉ Bad: ์ข ๋ฃ ์กฐ๊ฑด ์๋ ๋ฌดํ ์ฌ๊ท
// Bad: ๊น์ด๊ฐ ๊น์ด์ง๋ฉด ๋ฌดํ ๋ฃจํ์ ๋น ์ง ์ ์๋ ํ์
type DeepFlatten<T> = T extends object ? DeepFlatten<T[keyof T]> : T;
๐ Good: ์ฌ๊ท ๊น์ด ์ ํ ์ฅ์น ์ถ๊ฐํ๊ธฐ
์์ ํ ์ฌ๊ท ํ์ ์ ๋ง๋ค๋ ค๋ฉด, ์ฌ๊ท์ ๊น์ด๋ฅผ ์ถ์ ํ๊ณ ํน์ ๊น์ด์ ๋๋ฌํ๋ฉด ์ฐ์ฐ์ ์ค๋จํ๋ 'ํ๋ก ์ฐจ๋จ๊ธฐ'๋ฅผ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
// Good: ์ฌ๊ท ๊น์ด๋ฅผ ์ถ์ ํ๋ Depth ํ๋ผ๋ฏธํฐ ์ถ๊ฐ
type DeepFlatten<T, Depth extends any[] = []> = Depth['length'] extends 10 // ์ต๋ ๊น์ด 10์ผ๋ก ์ ํ
? T
: T extends object
? DeepFlatten<T[keyof T], [...Depth, any]>
: T;
3. ๋ฐฉ์ด์ ๊ตฌ์ถ: tsconfig.json๊ณผ ํ๋ก์ ํธ ์ค์
๋ณต์กํ ํ์ ์ ์์ ํ๊ธฐ ์ ์, ๊ฐ๋จํ ์ค์ ๋ณ๊ฒฝ๋ง์ผ๋ก๋ ํฐ ํจ๊ณผ๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
๊ฐ. incremental: true
์ปดํ์ผ๋ฌ๊ฐ ๋ง์ง๋ง ์ปดํ์ผ ์ํ๋ฅผ .tsbuildinfo ํ์ผ์ ์ ์ฅํด๋๊ณ , ๋ค์ ์ปดํ์ผ ์ ๋ณ๊ฒฝ๋ ํ์ผ๋ง ๋ค์ ๊ฒ์ฌํฉ๋๋ค. ๋๊ท๋ชจ ํ๋ก์ ํธ์์ ํ์์ ์ธ ์ต์ ์ ๋๋ค.
// tsconfig.json
{
"compilerOptions": {
"incremental": true
}
}
๋. skipLibCheck: true
node_modules์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ํ์ ์ ์ธ ํ์ผ(.d.ts)์ ๋ํ ํ์ ๊ฒ์ฌ๋ฅผ ๊ฑด๋๋๋๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ ์ ์ด๋ฏธ ๊ฒ์ฆ๋์๋ค๊ณ ๊ฐ์ ํ์ฌ ์ ์ฒด ์ปดํ์ผ ์๊ฐ์ ํฌ๊ฒ ๋จ์ถ์ํฌ ์ ์์ต๋๋ค. ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์์ ํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
// tsconfig.json
{
"compilerOptions": {
"skipLibCheck": true
}
}
๋ค. ํ๋ก์ ํธ ๋ถ๋ฆฌ๋ฅผ ํตํ ํจ๊ณผ์ ์ธ ๊ด๋ฆฌ (references)
๊ฑฐ๋ํ ๋ชจ๋ ธ๋ ํฌ(Monorepo) ํ๋ก์ ํธ์ ๊ฒฝ์ฐ, TypeScript์ ํ๋ก์ ํธ ๋ ํผ๋ฐ์ค(Project References) ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ํ๋ก์ ํธ๋ฅผ ์ฌ๋ฌ ๊ฐ์ ์์ ํ์ ํ๋ก์ ํธ๋ก ๋ถ๋ฆฌํ ์ ์์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด tsc --build ๋ช ๋ น์ ํตํด ๋ณ๊ฒฝ๋ ํ์ ํ๋ก์ ํธ์ ๊ทธ์ ์์กดํ๋ ํ๋ก์ ํธ๋ง ์ง๋ฅ์ ์ผ๋ก ์ฌ๋น๋ํ์ฌ ์ ์ฒด ๋น๋ ์๊ฐ์ ํ๊ธฐ์ ์ผ๋ก ์ค์ผ ์ ์์ต๋๋ค.
๊ฒฐ๋ก : ์ฑ๋ฅ์ ์ง์์ ์ธ ๊ด์ฌ์ ๋์
TypeScript ์ปดํ์ผ ์ฑ๋ฅ์ ํ๋ฒ ํด๊ฒฐํ๊ณ ๋๋๋ ๋ฌธ์ ๊ฐ ์๋๋ผ, ํ๋ก์ ํธ๊ฐ ์ฑ์ฅํจ์ ๋ฐ๋ผ ์ง์์ ์ผ๋ก ๊ด๋ฆฌํด์ผ ํ๋ ๋์์ ๋๋ค.
๋๋ฆฐ ์ปดํ์ผ ์๋๋ก ๊ณ ํต๋ฐ๊ณ ์๋ค๋ฉด, ๋ค์์ ์ฒดํฌ๋ฆฌ์คํธ๋ฅผ ๋ฐ๋ผ ํ๋ก์ ํธ๋ฅผ ์ ๊ฒํด ๋ณด์ธ์.
- โ ๊ฑฐ๋ ์ ๋์จ์ Mapped Type์ ์ง์ ์ฌ์ฉํ๊ณ ์์ง ์์๊ฐ? -> ์ ๋ค๋ฆญ์ผ๋ก ์ง์ฐ์ํค์ธ์.
- โ Conditional Type์ด ์๋์น ์๊ฒ ๋ถ๋ฐฐ๋์ด ๋ณต์กํ ์ฐ์ฐ์ ์ ๋ฐํ๋๊ฐ? -> [T]๋ก ๊ฐ์ธ ๋ถ๋ฐฐ๋ฅผ ๋ฐฉ์งํ์ธ์.
- โ ์ข ๋ฃ ์กฐ๊ฑด์ด ๋ถ๋ถ๋ช ํ Recursive Type์ด ์๋๊ฐ? -> ์ฌ๊ท ๊น์ด ์ ํ ์ฅ์น๋ฅผ ์ถ๊ฐํ์ธ์.
- โ tsconfig.json์ "incremental": true ์ "skipLibCheck": true ๊ฐ ์ค์ ๋์ด ์๋๊ฐ?
- โ ๋ชจ๋ ธ๋ ํฌ๋ผ๋ฉด ํ๋ก์ ํธ ๋ ํผ๋ฐ์ค๋ฅผ ํ์ฉํด ํ๋ก์ ํธ๋ฅผ ๋ถ๋ฆฌํ๋๊ฐ?
๋๋ํ ํ์ ์ค๊ณ์ ์ฌ๋ฐ๋ฅธ ํ๋ก์ ํธ ์ค์ ์ ๋จ์ํ ์ปดํ์ผ ์๊ฐ์ ์ค์ด๋ ๊ฒ์ ๋์ด, ์ฐ๋ฆฌ์ ๊ฐ๋ฐ ๊ฒฝํ์ ๋์ฑ ์พ์ ํ๊ณ ์ฆ๊ฒ๊ฒ ๋ง๋ค์ด์ฃผ๋ ์ต๊ณ ์ ํฌ์์ ๋๋ค.
'๐บJS & TS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| "์ฝ๋ฉ 1๋ ๋ชฐ๋ผ๋ OK! 10๋ถ ๋ง์ ๋ด ๋์ค์ฝ๋ ์๋ฒ์ ๋ด ๋ง๋ค๊ธฐ" (0) | 2025.10.01 |
|---|---|
| ์ฌ์ฉ์๊ฐ ์ฐฝ ๋ซ๊ธฐ ์ , 1์ด ๋ง์ ํ์ด์ง ๋์ฐ๋ ๋น๊ฒฐ (0) | 2025.09.25 |
| ํ์ ์คํฌ๋ฆฝํธ ํ์ ํธํ์ฑ (4) | 2025.08.28 |
| ๐ JavaScript this์ ๋ฐ์ธ๋ฉ ์๋ฒฝ ๊ฐ์ด๋ (2) | 2025.08.26 |
| ๐ TypeScript์ Tailwind CSS๋ก ๊ฐ๋ฐ ํ๊ฒฝ ๊ตฌ์ถ (1) | 2025.08.26 |