기초문법

9개의 게시글

TypeScript

[TypeScript] 제네릭(Generic)

November 29, 2025

타입스크립트를 공부하다 보면 함수나 인터페이스 뒤에 붙은 <T> 같은 기호를 본 적이 있을 것이다. 이것이 바로 제네릭(Generic) 이다. 제네릭은 단일 타입이 아닌 다양한 타입에서 동작하는 컴포넌트를 작성할 수 있게 해주는 타입스크립트의 놀라운 기능이다. 쉽게 말해 타입을 변수처럼 유연하게 다루는 도구라고 볼 수 있다. 1. 제네릭이 필요한 이유 제네릭을 왜 써야 하는지 이해하기 위해, 인수로 받은 값을 그대로 반환하는 간단한 함수 func를 만들어보자. 시도 1: any 타입 사용하기 다양한 타입을 받아야 하니, 일단 매개변수 타입을 any로 설정해 보았다. TypeScript function func(value: any) { return value; } let num = func(10); let str = func("string"); 이 코드는 잘 동작하는 것처럼 보이지만 치명적인 단점이 있다. 반환값의 타입마저 any가 되어버린다는 점이다. TypeScript num.toUpperCase(); // ❌ 런타임 오류 발생! num에는 분명 숫자 10이 들어있지만, 타입스크립트는 이를 any로 인식하기 때문에 문자열 메서드인 .toUpperCase()를 써도 에러를 잡아내지 못한다. 결국 실행 시점에 프로그램이 뻗어버리는 위험한 상태가 된다. 시도 2: unknown 타입 사용하기 그렇다면 any보다 안전한 unknown을 쓰면 어떨까? TypeScript function func(value: unknown) { return value; } let num = func(10); // num의 타입은 unknown // num.toFixed(); // ❌ 오류 발생 (unknown에는 메서드 사용 불가) unknown은 안전하지만 너무 엄격하다. 값을 사용하려면 매번 typeof 등을 이용해 타입 좁히기를 해야 한다. TypeScript if (typeof num === "number") { num.toFixed(); // 귀찮은 과정이 필요함 } 우리가 원하는 건 단순하다. "숫자를 넣으면 숫자가 반환되고, 문자를 넣으면 문자가 반환되는" 그런 유연한 함수다. 이럴 때 제네릭이 필요하다. --- 2. 제네릭(Generic) 함수 제네릭(Generic) 이라는 단어는 '일반적인', '포괄적인'이라는 뜻을 가진다. 즉, 모든 타입의 값을 두루두루 포괄하여 적용할 수 있는 범용적인 함수를 만든다는 의미다. 제네릭 기본 문법 <T> 제네릭 함수는 함수 이름 뒤에 꺾쇠 < >를 열고 타입 변수를 선언하여 사용한다. 보통 T (Type의 약자)를 많이 쓴다. TypeScript // <T>: 타입 변수 선언 // (value: T): 매개변수 타입 // : T : 반환값 타입 function func<T>(value: T): T { return value; } 이제 이 함수를 호출할 때 전달하는 인수의 타입에 따라 T가 결정된다. TypeScript let num = func(10); // 1. 인수 10(number)이 전달됨 // 2. T가 number로 추론됨 // 3. 매개변수와 반환값 타입이 모두 number로 결정됨 // 결과: num은 number 타입 ✅ TypeScript let str = func("string"); // 1. 인수 "string"이 전달됨 // 2. T가 string으로 추론됨 // 결과: str은 string 타입 ✅ 이제 any처럼 타입 안정성을 잃지도 않고, unknown처럼 귀찮게 타입을 좁힐 필요도 없다. --- 3. 명시적으로 타입 지정하기 대부분의 경우 타입스크립트가 타입을 알아서 잘 추론해 주지만, 가끔은 우리가 직접 타입을 지정해야 할 때가 있다. 예를 들어 배열을 넣었을 때, 튜플 타입으로 추론되길 원한다면 다음과 같이 명시할 수 있다. TypeScript // 자동으로 추론시키면? let arr = func([1, 2, 3]); // T는 number[] 로 추론됨 // 명시적으로 지정하면? let tuple = func<[number, number, number]>([1, 2, 3]); // T는 [number, number, number] 튜플 타입으로 결정됨 함수 호출 시 <타입>을 직접 적어주면, 타입스크립트는 추론하지 않고 우리가 적어준 타입을 T에 할당한다. --- 요약 1. 문제점: any는 타입 검사를 포기하고, unknown은 사용하기 너무 번거롭다. 2. 해결책: 제네릭을 사용하면 입력값의 타입에 따라 반환값의 타입이 유연하게 결정된다. 3. 문법: 함수 이름 뒤에 <T>를 붙여 타입 변수를 선언하고 사용한다. 4. 사용: 일반적으로는 타입이 자동 추론되지만, 필요하다면 <Type>을 명시하여 호출할 수 있다.

TypeScriptStudyGenerics기초문법
TypeScript

[TypeScript] 함수(Function) 타입 정의하기

November 27, 2025

자바스크립트에서 함수를 설명할 때 가장 중요한 것은 "어떤 매개변수를 받아서, 어떤 값을 반환하는가" 이다. 타입스크립트도 마찬가지다. 단지 그 설명에 타입만 추가하면 된다. 1. 기본적인 함수 타입 정의 함수의 타입은 매개변수의 타입과 반환값의 타입으로 결정된다. 1-1. 함수 선언식 매개변수 뒤에 타입을 적고, 소괄호 뒤에 반환값의 타입을 적는다. TypeScript function func(a: number, b: number): number { return a + b; } 💡 반환값 타입 추론 타입스크립트는 return문을 보고 반환값의 타입을 자동으로 추론할 수 있다. 따라서 반환 타입은 생략해도 무방하다. TypeScript function func(a: number, b: number) { return a + b; // 자동으로 number 반환으로 추론됨 } 1-2. 화살표 함수 화살표 함수도 선언식과 동일한 방식으로 타입을 정의한다. TypeScript const add = (a: number, b: number): number => a + b; // 반환값 타입 생략 가능 const add2 = (a: number, b: number) => a + b; --- 2. 매개변수 다루기 함수의 매개변수를 정의할 때 사용할 수 있는 유용한 기능들이다. 2-1. 매개변수 기본값 (Default Parameter) 매개변수에 기본값이 설정되어 있다면, 타입스크립트가 해당 기본값을 기준으로 타입을 자동으로 추론한다. TypeScript function introduce(name = "조태민") { console.log(name : ${name}); } // introduce(1); // ❌ 오류: string 타입에 number 할당 불가 만약 기본값과 다른 타입으로 명시하거나, 다른 타입의 값을 인수로 전달하면 에러가 발생한다. 2-2. 선택적 매개변수 (Optional Parameter) 매개변수가 있어도 되고 없어도 되는 경우, 변수명 뒤에 물음표(?)를 붙여 선택적 매개변수로 만들 수 있다. TypeScript function introduce(name = "조태민", tall?: number) { console.log(name : ${name}); // tall은 number | undefined 타입이 됨 if (typeof tall === "number") { console.log(tall : ${tall + 10}); } } introduce("조태민", 178); // ✅ introduce("조태민"); // ✅ (tall 생략 가능) 주의할 점: 1. 선택적 매개변수의 타입은 자동으로 undefined와 유니온 된 타입(Type | undefined)이 된다. 따라서 사용 시 타입 좁히기가 필요할 수 있다. 2. 선택적 매개변수는 필수 매개변수보다 앞에 올 수 없다. 항상 뒤에 배치해야 한다. TypeScript // ❌ 오류 발생: 필수 매개변수(age)는 선택적 매개변수(tall) 뒤에 올 수 없음 // function introduce(name = "조태민", tall?: number, age: number) {} --- 3. 나머지 매개변수 (Rest Parameter) 가변적인 개수의 인수를 배열 형태로 받을 때 사용하는 나머지 매개변수(...rest)의 타입 정의 방법이다. 3-1. 배열로 정의하기 일반적으로는 특정 타입의 배열(Type[])로 정의한다. TypeScript function getSum(...rest: number[]) { let sum = 0; rest.forEach((it) => (sum += it)); return sum; } getSum(1, 2, 3); // [1, 2, 3]으로 전달됨 3-2. 튜플로 정의하기 (개수 제한) 만약 인수의 개수를 정확히 제한하고 싶다면 튜플 타입을 사용할 수 있다. TypeScript // 정확히 숫자 3개만 받을 수 있음 function getSum(...rest: [number, number, number]) { let sum = 0; rest.forEach((it) => (sum += it)); return sum; } getSum(1, 2, 3); // ✅ // getSum(1, 2, 3, 4); // ❌ 오류: 3개만 허용됨 --- 요약 1. 기본 정의: 매개변수와 반환값의 타입을 명시한다. (반환값은 추론 가능) 2. 기본값: param = value 형태이며, 타입이 자동 추론된다. 3. 선택적 매개변수: param?: type 형태이며, 필수 매개변수 뒤에 와야 한다. 4. 나머지 매개변수: ...rest: type[] 형태이며, 튜플을 쓰면 개수 제한이 가능하다.

TypeScriptStudyFunction기초문법
TypeScript

[TypeScript] void 타입과 never 타입

November 26, 2025

이번에는 함수의 반환값과 관련된 특수한 타입인 void 와 never 에 대해 알아본다. 얼핏 보면 비슷해 보일 수 있지만, 쓰임새와 의미가 명확히 다르다. 1. void 타입 void는 '아무런 값도 없음' 을 의미하는 타입이다. 1-1. 함수의 반환값으로 사용 주로 값을 반환하지 않는 함수의 반환 타입으로 사용된다. 자바스크립트에서는 함수에서 아무것도 반환하지 않으면 암묵적으로 undefined를 반환하지만, 타입스크립트에서는 이를 void로 표현한다. ts function func2(): void { console.log("Hello"); } 1-2. 변수의 타입으로 사용 변수에도 void 타입을 지정할 수 있지만, 실용성은 거의 없다. void 타입 변수에는 오직 undefined만 할당할 수 있기 때문이다. ts let a: void; a = undefined; // 가능 // a = 1; // ❌ 불가능 > 참고: strictNullChecks 옵션 만약 tsconfig.json에서 strictNullChecks 옵션을 false로 설정하면, void 타입 변수에 null 값을 담을 수도 있다. (하지만 엄격한 옵션을 켜두는 것이 권장된다.) > --- 2. never 타입 never는 '불가능' 을 의미하는 타입이다. 즉, 절대 발생할 수 없는 값이나 상태를 나타낼 때 사용한다. 2-1. 함수가 종료되지 않을 때 함수가 무한 루프를 돌아 영원히 종료되지 않는다면, 이 함수는 어떤 값도 반환할 수 없다(반환 자체가 불가능). 이때 never를 사용한다. ts function func3(): never { while (true) { // 무한 루프 } } 2-2. 의도적으로 오류를 발생시킬 때 함수 실행 도중 에러를 던져서(throw) 강제로 실행을 중단시키는 경우에도, 정상적인 반환이 불가능하므로 never 타입을 사용한다. ts function func4(): never { throw new Error(); } 2-3. 변수에 사용 시 (가장 엄격한 타입) 변수의 타입을 never로 정의하면, 그 변수에는 어떤 값도 담을 수 없다. 심지어 모든 타입의 치트키인 any 타입의 값조차도 never 타입 변수에는 할당할 수 없다. ts let anyVar: any; let a: never; // a = 1; // ❌ 오류 // a = undefined; // ❌ 오류 // a = null; // ❌ 오류 // a = anyVar; // ❌ 오류 (any도 안 됨) 이러한 특징 때문에 never는 보통 코드의 흐름상 절대 도달할 수 없는 코드임을 명시하거나, 조건문에서 모든 케이스를 다루었는지 검사(Exhaustiveness Checking)할 때 유용하게 쓰인다. --- 요약 1. void: "비어있음". 아무 값도 반환하지 않는 함수에 사용한다. (undefined만 호환) 2. never: "불가능". 영원히 끝나지 않거나 에러를 던지는 함수에 사용한다. (그 어떤 값도 호환되지 않음)

TypeScriptStudy기초문법TypeSafety
TypeScript

[TypeScript] Any 타입과 Unknown 타입

November 26, 2025

타입스크립트를 쓰다 보면 타입을 미리 알 수 없거나, 유연하게 처리해야 할 상황이 생긴다. 이때 사용할 수 있는 것이 any와 unknown이다. 두 타입 모두 모든 값을 허용한다는 공통점이 있지만, 안정성 면에서 큰 차이가 있다. 1. Any 타입 (치트키) any 타입은 타입스크립트의 모든 타입 검사를 무력화시키는 일종의 치트키(Cheat Key) 같은 타입이다. 1-1. 무제한의 자유 일반적으로 타입스크립트는 초기화된 값을 기준으로 타입을 추론하거나, 명시된 타입 외의 값을 넣으면 에러를 뱉는다. 하지만 any를 쓰면 어떤 타입의 값이든 할당할 수 있다. ts let anyVar: any = 10; anyVar = "hello"; // 문제없음 anyVar = true; // 문제없음 anyVar = {}; // 문제없음 심지어 해당 값에 어떤 메서드를 쓰든, 어떤 연산을 하든 터치하지 않는다. ts anyVar.toUpperCase(); // 런타임에 에러가 날 수도 있지만, 컴파일은 통과됨 anyVar.toFixed(); anyVar.a; 1-2. 모든 타입에 할당 가능 any 타입의 변수는 다른 정적인 타입(number 등)의 변수에도 자유롭게 할당할 수 있다. ts let anyVar: any = 10; let num: number = 20; num = anyVar; // ❌ 문제 발생! num은 number 타입인데 any가 들어와 버림 1-3. Any의 위험성 any는 편해 보이지만 매우 위험하다. 컴파일 시점에 에러를 잡지 못하고 런타임(실행 중)에 에러가 터질 수 있기 때문이다. any를 남발하면 타입스크립트를 쓰는 이유가 사라진다. 따라서 정말 어쩔 수 없는 경우를 제외하고는 사용하지 않는 것을 강력히 권장한다. --- 2. Unknown 타입 (안전한 Any) unknown 타입은 any와 비슷하게 모든 타입의 값을 저장할 수 있지만, 훨씬 더 안전하다. 2-1. 모든 값을 받을 수 있다 any처럼 변수에 어떤 타입의 값이든 넣을 수 있다. ts let unknownVar: unknown; unknownVar = ""; unknownVar = 1; unknownVar = () => {}; 2-2. 하지만 사용은 엄격하다 값을 넣을 때는 자유롭지만, 그 값을 사용하거나 다른 변수에 넣을 때는 엄격한 제한이 걸린다. 1) 다른 타입의 변수에 할당 불가unknown 타입의 값은 오직 any나 unknown 타입의 변수에만 할당할 수 있다. ts let num: number = 10; let unknownVar: unknown = 20; // num = unknownVar; // ❌ 에러 발생 (Type 'unknown' is not assignable to type 'number') 2) 연산 및 메서드 사용 불가 값의 타입이 확실하지 않기 때문에 연산이나 메서드 호출도 막혀 있다. ts let unknownVar: unknown = 30; // unknownVar 2; // ❌ 에러 발생 // unknownVar.toUpperCase(); // ❌ 에러 발생 정리하자면 unknown은 "값을 저장하는 행위"만 가능하고, 그 값을 사용하려면 반드시 타입을 확인하는 과정을 거쳐야 한다. --- 3. Unknown 타입 올바르게 사용하기 (타입 좁히기) unknown 타입의 값을 사용하려면, 해당 값이 어떤 타입인지 확실히 검사해줘야 한다. 이를 타입 좁히기(Type Narrowing) 라고 한다. ts let unknownVar: unknown = 10; if (typeof unknownVar === "number") { // 이 블록 안에서는 unknownVar가 number 타입으로 취급된다. console.log(unknownVar 2); // ✅ 정상 작동 } 조건문을 통해 타입이 확인되면, 타입스크립트는 해당 블록 내부에서 변수의 타입을 자동으로 추론해 준다. --- 요약 1. Any 타입: 모든 검사를 무시하는 치트키. 타입 안정성을 해치므로 사용을 지양해야 한다. 2. Unknown 타입: 모든 값을 저장할 수 있지만, 사용하려면 타입을 검사해야 한다. any보다 안전하다. 3. 결론: 변수의 타입을 미리 알 수 없다면 any 대신 unknown을 사용하는 것이 훨씬 안전하다.

TypeScriptStudy기초문법TypeSafety
TypeScript

[TypeScript] 열거형(Enum) 타입

November 26, 2025

열거형(Enum) 타입은 이름 그대로 여러 개의 값을 나열하는 용도로 사용한다. 자바스크립트에는 존재하지 않고 오직 타입스크립트에서만 사용할 수 있는 특별한 타입이다. 1. 숫자형 Enum (Numeric Enum) 가장 기본적인 형태의 Enum이다. 유저의 권한(Role)처럼 관련된 상수들의 집합을 정의할 때 유용하다. 1-1. 기본 자동 할당 별도로 값을 지정하지 않으면 0부터 1씩 증가하는 값이 자동으로 할당된다. ts enum Role { ADMIN, // 0 할당 (자동) USER, // 1 할당 (자동) GUEST, // 2 할당 (자동) } const user1 = { name: "탬니", role: Role.ADMIN, // 0 }; const user2 = { name: "홍길동", role: Role.USER, // 1 }; const user3 = { name: "아무개", role: Role.GUEST, // 2 }; 이렇게 Enum을 사용하면 role: 0처럼 의미를 알 수 없는 숫자 대신 Role.ADMIN을 사용하여 코드의 가독성을 높일 수 있다. 1-2. 값 직접 할당 및 자동 증가 물론 0이 아닌 다른 숫자를 할당할 수도 있다. 시작하는 멤버에 값을 주면, 그 뒤의 멤버들은 자동으로 1씩 증가한다. ts enum Role { ADMIN = 10, // 10 할당 USER, // 11 할당 (자동) GUEST, // 12 할당 (자동) } 물론 모든 멤버에 각각 다른 값을 직접 지정하는 것도 가능하다. ts enum Role { ADMIN = 0, USER = 1, GUEST = 2, } --- 2. 문자열 Enum (String Enum) Enum의 멤버에는 숫자뿐만 아니라 문자열 값도 할당할 수 있다. 이를 문자열 Enum이라고 부른다. ts enum Language { korean = "ko", english = "en", } const user1 = { name: "이정환", role: Role.ADMIN, language: Language.korean, // "ko" }; 왜 문자열 Enum을 쓸까? 문자열을 직접 타이핑하다 보면 오타가 발생할 수 있다. 예를 들어 "ko"라고 적어야 하는데 "kos", "KO-kr" 등으로 실수할 수 있는데, Enum을 사용하면 Language.korean처럼 자동 완성을 이용하므로 실수를 방지하고 코드를 안전하게 작성할 수 있다. --- 3. Enum은 컴파일되어도 사라지지 않는다 보통 타입스크립트의 타입(Type Alias, Interface 등)은 컴파일 시점에 모두 제거된다. 하지만 Enum은 컴파일 결과에 객체로 남는다. 컴파일 전 (TypeScript) ts enum Role { ADMIN, USER, GUEST, } 컴파일 후 (JavaScript) js var Role; (function (Role) { Role[Role["ADMIN"] = 0] = "ADMIN"; Role[Role["USER"] = 1] = "USER"; Role[Role["GUEST"] = 2] = "GUEST"; })(Role || (Role = {})); Enum은 내부적으로 자바스크립트 객체로 변환된다. 따라서 런타임(실행 중)에도 Role.ADMIN과 같이 값으로 접근하여 사용할 수 있다. --- 요약 1. Enum: 연관된 상수들을 모아놓은 타입으로 자바스크립트에는 없는 기능이다. 2. 숫자형 Enum: 0부터 숫자가 자동 할당되며, 시작 값을 지정하면 1씩 자동 증가한다. 3. 문자열 Enum: 의미 있는 문자열 값을 지정해 오타 방지 및 가독성을 높일 수 있다. 4. 컴파일: 다른 타입들과 달리 컴파일 후에도 사라지지 않고 자바스크립트 객체로 남는다.

TypeScriptStudy기초문법Enum
TypeScript

[TypeScript] 타입 별칭(Type Alias)과 인덱스 시그니처(Index Signature)

November 26, 2025

매번 복잡한 객체 타입을 정의할 때마다 긴 코드를 반복해서 작성하는 것은 비효율적이다. 이번에는 타입을 변수처럼 정의해 재사용성을 높여주는 타입 별칭과, 객체의 프로퍼티를 유연하게 관리하게 해주는 인덱스 시그니처에 대해 알아본다. 1. 타입 별칭 (Type Alias) 타입 별칭을 이용하면 변수를 선언하듯 타입을 별도로 정의하고 이름을 붙여줄 수 있다. 1-1. 기본 정의 및 사용 type 키워드를 사용하여 정의한다. ts // 타입 별칭 정의 type User = { id: number; name: string; nickname: string; birth: string; bio: string; location: string; }; 이렇게 만든 User 타입은 변수의 타입을 정의할 때 간편하게 사용할 수 있다. ts let user: User = { id: 1, name: "탬니", nickname: "taemni", birth: "2xxx.xx.xx", bio: "반갑습니다~", location: "xx시", }; let user2: User = { id: 2, name: "홍길동", nickname: "길동이", birth: "1xxx.xx.xx", bio: "안녕하세요", location: "xx시", }; 매번 객체 리터럴로 타입을 길게 명시하지 않아도 되므로 코드의 가독성이 좋아지고 중복이 줄어든다. 1-2. 스코프 (Scope) 규칙 타입 별칭도 변수처럼 스코프의 규칙을 따른다. 1. 동일한 스코프 내 중복 선언 불가: 같은 이름(User)으로 두 번 정의하면 에러가 발생한다. 2. 다른 스코프 내 중복 선언 가능: 함수 내부 등 다른 스코프라면 같은 이름을 사용할 수 있다. ts type User = { id: number; name: string; // ... }; // type User = {}; // ❌ 에러 발생 (같은 스코프) function test() { type User = string; // ✅ 가능 (함수 내부 스코프) } 위 코드에서 test 함수 내부의 User는 string 타입이고, 함수 외부의 User는 객체 타입이 된다. 1-3. 컴파일 시점 제거 타입 별칭은 타입스크립트의 문법이므로 컴파일되어 자바스크립트로 변환되면 흔적도 없이 사라진다. 런타임 코드에 영향을 주지 않는다. --- 2. 인덱스 시그니처 (Index Signature) 인덱스 시그니처는 객체의 키(Key)와 값(Value)의 규칙만 정의하여 타입을 유연하게 만드는 문법이다. 2-1. 사용하는 이유 예를 들어 국가 코드를 저장하는 객체가 있다고 가정해보자. 국가가 3개라면 일일이 정의할 수 있지만, 만약 100개가 넘는다면? ts // 100개를 다 적으려면 너무 힘들다... type CountryCodes = { Korea: string; UnitedState: string; UnitedKingdom: string; // ... (나머지 97개) }; 이럴 때 인덱스 시그니처를 사용하면 간단하게 정의할 수 있다. 2-2. 문법 및 사용 [key: 타입]: 타입 형태로 작성한다. ts type CountryCodes = { [key: string]: string; }; let countryCodes: CountryCodes = { Korea: "ko", UnitedState: "us", UnitedKingdom: "uk", Brazil: "bz", // 자유롭게 추가 가능 }; 위 코드는 "key가 string 타입이고, value도 string 타입인 모든 프로퍼티를 허용한다" 는 의미이다. 따라서 Korea, Brazil 등 어떤 문자열 키가 들어와도 값이 문자열이라면 모두 허용된다. 2-3. 특정 프로퍼티와 혼용 및 주의사항 인덱스 시그니처를 사용하면서 동시에 필수 프로퍼티를 명시할 수도 있다. ts type CountryNumberCodes = { [key: string]: number; Korea: number; // Korea는 반드시 있어야 함 }; ⚠️ 주의할 점: 인덱스 시그니처와 개별 프로퍼티를 섞어 쓸 때, 개별 프로퍼티의 타입은 반드시 인덱스 시그니처 타입과 일치하거나 호환되어야 한다. ts type CountryNumberCodes = { [key: string]: number; // 모든 값은 number여야 함 // Korea: string; // ❌ 에러 발생! (string은 number에 할당 불가) }; 인덱스 시그니처가 number를 반환한다고 선언했으므로, Korea 속성 또한 number여야 규칙이 깨지지 않는다. --- 요약 1. 타입 별칭: type 키워드로 타입을 변수처럼 정의해 재사용한다. 컴파일 시 사라진다. 2. 인덱스 시그니처: [key: T]: U 문법으로 객체의 속성을 유연하게 정의한다. 3. 주의: 인덱스 시그니처와 함께 쓰는 구체적인 속성은 인덱스 시그니처의 타입과 호환되어야 한다.

TypeScriptStudy기초문법Type AliasIndex Signature
TypeScript

[TypeScript] 객체(Object) 타입과 구조적 타이핑

November 26, 2025

이번에는 타입스크립트에서 객체(Object) 의 타입을 정의하는 방법과 구조적 타입 시스템, 그리고 선택적/읽기 전용 프로퍼티 같은 특수 문법에 대해 알아본다. 1. object 타입 타입스크립트에서 객체의 타입을 정의하는 가장 간단한 방법은 object 키워드를 사용하는 것이다. ts let user: object = { id: 1, name: "탬니", }; 하지만 이렇게 object로 타입을 정의하면 한 가지 치명적인 문제가 발생한다. ts // user.id; // ❌ 에러 발생 (Property 'id' does not exist on type 'object') 왜 에러가 날까?object 타입은 해당 변수가 '객체이다' 라는 사실만 알려줄 뿐, 그 객체 안에 어떤 프로퍼티가 있는지에 대한 정보는 전혀 없기 때문이다. 따라서 실제 프로퍼티에 접근하려고 하면 에러가 발생한다. 우리가 원하는 것은 객체의 구체적인 구조까지 타입으로 정의하는 것이다. 이때 사용하는 것이 바로 객체 리터럴 타입이다. 2. 객체 리터럴 타입 객체 리터럴 타입은 중괄호 {}를 열고, 객체가 가져야 할 프로퍼티의 이름과 타입을 직접 나열하여 정의한다. ts let user: { id: number; name: string; } = { id: 1, name: "탬니", }; console.log(user.id); // 1 (정상 작동) 이제 타입스크립트는 user 객체 안에 id가 숫자형이고, name이 문자열이라는 것을 정확히 인지한다. 점 표기법을 사용할 때 자동 완성이 지원되고, 타입 검사도 수행된다. 구조적 타입 시스템 (Structural Type System) 여기서 알 수 있는 중요한 사실은, 타입스크립트가 프로퍼티를 기준으로 객체의 타입을 정의한다는 점이다. - 명목적 타입 시스템 (Nominal Type System): C, Java처럼 클래스나 인터페이스의 '이름'을 기준으로 타입을 따지는 방식. - 구조적 타입 시스템 (Structural Type System): 타입스크립트처럼 이름이 무엇이든 "어떤 구조(프로퍼티)를 가지고 있느냐" 를 기준으로 타입을 따지는 방식. 마치 "이름(name)과 색깔(color)이 있으면 그건 강아지 객체야"라고 판단하는 것과 같다. 이를 프로퍼티 베이스드 타입 시스템(Property-based Type System) 이라고도 부른다. ts let dog: { name: string; color: string; } = { name: "뽀삐", color: "white", }; 3. 특수한 프로퍼티 정의하기 객체 타입을 정의할 때 상황에 따라 프로퍼티를 선택적으로 만들거나, 수정할 수 없게 만들 수 있다. 3-1. 선택적 프로퍼티 (Optional Property) 객체를 다루다 보면 특정 프로퍼티가 있을 수도 있고 없을 수도 있는 경우가 있다. 예를 들어 이름은 있지만 id는 아직 발급되지 않은 유저가 있을 수 있다. 이때 프로퍼티 이름 뒤에 물음표(?) 를 붙여주면 된다. ts let user: { id?: number; // 있어도 되고 없어도 됨 name: string; } = { id: 1, name: "탬니", }; // id가 없는 객체를 할당해도 에러가 나지 않음 user = { name: "홍길동", }; 주의할 점: 선택적 프로퍼티라 하더라도, 만약 값이 존재한다면 반드시 정의된 타입(number)이어야 한다. 엉뚱한 타입(string 등)을 넣으면 에러가 발생한다. 3-2. 읽기 전용 프로퍼티 (Readonly Property) API 키나 고유 ID처럼, 객체 생성 시 한 번 값이 정해지면 절대 바뀌면 안 되는 값들이 있다. 이때는 프로퍼티 이름 앞에 readonly 키워드를 붙인다. ts let config: { readonly apiKey: string; // 읽기 전용 } = { apiKey: "my-secret-key", }; // config.apiKey = "hacked"; // ❌ 에러 발생 (읽기 전용 속성이므로 할당 불가) readonly를 사용하면 의도치 않게 중요한 데이터가 수정되는 것을 코드 레벨에서 방지할 수 있다. --- 요약 1. object 타입: 단순히 "객체임"만 명시하므로 프로퍼티 접근이 불가능하다. 지양하는 것이 좋다. 2. 객체 리터럴 타입: { key: type } 형태로 구조를 명확히 정의한다. (구조적 타이핑) 3. 선택적 프로퍼티 (?): 해당 프로퍼티가 없어도 에러가 나지 않는다. 4. 읽기 전용 프로퍼티 (readonly): 초기화 이후 값을 수정할 수 없다.

TypeScriptStudy기초문법Object
TypeScript

[TypeScript] 배열(Array)과 튜플(Tuple)

November 26, 2025

이번에는 타입스크립트의 배열(Array) 과 튜플(Tuple) 타입에 대해 살펴본다. 배열은 자바스크립트의 배열과 크게 다르지 않지만, 튜플은 자바스크립트에는 없는 타입스크립트만의 특별한 타입이다. 1. 배열 (Array) 타입스크립트에서 배열 타입을 정의하는 방법은 크게 두 가지가 있다. 1-1. 기본 타입 정의 (type[]) 가장 일반적인 방법은 변수명 뒤에 타입[] 형태로 정의하는 것이다. ts let numArr: number[] = [1, 2, 3]; let strArr: string[] = ["hello", "im", "taemmi"]; 1-2. 제네릭 문법 (Array<type>) Array<배열요소타입> 형태로도 정의할 수 있다. 꺾쇠(< >)와 함께 타입을 작성하는 것을 제네릭(Generic) 이라고 부른다. ts let boolArr: Array<boolean> = [true, false, true]; > Note: 두 방식은 기능상 차이가 없으나, 보통 타이핑이 더 간편한 첫 번째 방식(type[])을 주로 사용한다. > 1-3. 다양한 타입의 요소를 갖는 배열 (Union Type) 배열 안에 숫자와 문자열이 동시에 들어가야 한다면 어떻게 해야 할까? 이럴 때는 소괄호와 바(|)를 이용해 유니온(Union) 타입으로 정의하면 된다. ts // string 또는 number가 들어갈 수 있는 배열 let multArr: (string | number)[] = [1, "hello"]; - (string | number): 배열의 요소가 문자열이거나 숫자여야 함을 의미한다. 1-4. 다차원 배열 배열 안에 배열이 들어가는 2차원 이상의 배열은 []를 연달아 작성하여 정의한다. ts let doubleArr: number[][] = [ [1, 2, 3], [4, 5], ]; --- 2. 튜플 (Tuple) 튜플은 자바스크립트에는 없는 개념으로, 길이와 각 요소의 타입이 고정된 배열을 의미한다. 2-1. 튜플 정의하기 일반 배열과 달리 대괄호 [] 안에 각 인덱스별로 들어갈 타입을 명시한다. ts // 길이가 2이고, 각각 number 타입인 튜플 let tup1: [number, number] = [1, 2]; // 길이가 3이고, 순서대로 number, string, boolean인 튜플 let tup2: [number, string, boolean] = [1, "2", true]; 이렇게 정의하면 정해진 순서와 타입, 그리고 길이를 엄격하게 지켜야 한다. 2-2. 튜플 사용 시 주의사항 (배열 메서드) 튜플은 컴파일되면 결국 자바스크립트의 배열(Array) 로 변환된다. 따라서 push나 pop 같은 배열 메서드를 사용하면 고정된 길이를 무시하고 요소를 추가하거나 삭제할 수 있다. ts let tup1: [number, number] = [1, 2]; // ❌ 에러를 뱉지 않음 (런타임에서는 배열이기 때문) tup1.push(1); tup1.push(1); 타입스크립트가 이런 메서드 호출까지 막아주지는 못하므로, 튜플을 쓸 때는 배열 메서드 사용에 각별히 주의해야 한다. 2-3. 튜플은 언제 쓸까? 예를 들어 회원 정보를 저장하는 2차원 배열이 있다고 가정해보자. [이름, 아이디] 순서로 저장하고 싶다. 일반 배열을 쓴다면 실수로 순서를 바꿔 넣어도 자바스크립트는 알아채지 못한다. ts // JS 방식 (실수 가능성 있음) const users = [ ["탬니", 1], ["홍길동", 2], [3, "아무개"], // ❌ 순서가 바뀌었지만 에러 없음 ]; 하지만 튜플을 사용하면 이러한 실수를 즉시 잡아낼 수 있다. ts // TS 튜플 사용 const users: [string, number][] = [ ["탬니", 1], ["홍길동", 2], // [3, "아무개"], // ❌ 컴파일 에러 발생! (순서 틀림) ]; 이처럼 데이터의 순서와 타입이 중요한 경우 튜플을 사용하면 훨씬 안전하게 코드를 작성할 수 있다. --- 요약 1. 배열: number[] 혹은 Array<number>로 정의한다. 유니온 타입을 써서 여러 타입을 담을 수도 있다. 2. 튜플: 길이와 타입이 고정된 배열이다 ([string, number]). 3. 주의: 튜플도 결국 배열이라 push 등의 메서드에는 뚫릴 수 있으니 주의해서 사용하자.

TypeScriptStudy기초문법ArrayTuple
TypeScript

[TypeScript] 원시 타입(Primitive Type)과 리터럴 타입(Literal Type)

November 26, 2025

TypeScript의 가장 기초가 되는 원시 타입(Primitive Type) 과 값 자체가 타입이 되는 리터럴 타입(Literal Type) 에 대해 정리해 본다. 1. 원시 타입 (Primitive Type) 원시 타입이란 동시에 하나의 값만 저장할 수 있는 타입을 의미한다. 배열이나 객체 같은 비원시 타입들이 동시에 여러 개의 값을 저장할 수 있는 것과 달리, number, string, boolean 등은 딱 하나의 값만 가질 수 있다. > 참고: 실습 시 tsconfig.json의 isolateModules 옵션이 true라면, 파일 내에 export 키워드가 하나 이상 있어야 에러가 발생하지 않는다. > 1-1. number 타입 자바스크립트에서 숫자를 의미하는 모든 값을 포함한다. 단순 정수뿐만 아니라 소수, 음수, 그리고 Infinity, NaN 같은 특수 값도 포함된다. ts // number let num1: number = 123; // 양수 let num2: number = -123; // 음수 let num3: number = 0.123; // 실수 let num4: number = -0.123; // 음의 실수 let num5: number = Infinity; // 무한대 let num6: number = -Infinity; // 음의 무한대 let num7: number = NaN; // Not a Number 💡 타입 주석 (Type Annotation) 변수명 뒤에 콜론(:)과 함께 타입을 정의하는 문법을 ‘타입 주석’ 또는 ‘타입 어노테이션’이라고 부른다. 주의사항number 타입으로 정의된 변수에는 문자열 등 다른 값을 넣을 수 없으며, 해당 타입에 맞지 않는 메서드(예: toUpperCase)는 사용할 수 없다. ts // 에러 발생 예시 // num1 = 'hello'; // ❌ Type 'string' is not assignable to type 'number'. // num1.toUpperCase(); // ❌ Property 'toUpperCase' does not exist on type 'number'. 1-2. string 타입 문자열을 의미하는 타입이다. 큰따옴표("), 작은따옴표('), 백틱( ), 템플릿 리터럴을 모두 포함한다. ts // string let str1: string = "Hello"; let str2: string = 'Hello'; let str3: string = Hello; let str4: string = Hello ${num1}; // 템플릿 리터럴 1-3. boolean 타입 참(true)과 거짓(false)만을 저장하는 타입이다. ts // boolean let bool1: boolean = true; let bool2: boolean = false; 1-4. null과 undefined 타입 각각 오직 null 값과 undefined 값 하나만 포함하는 타입이다. ts // null let null1: null = null; // undefined let unde1: undefined = undefined; --- 2. strictNullChecks 옵션 (엄격한 null 검사) 자바스크립트에서는 변수를 초기화할 때 임시로 null을 넣어두곤 했다. 하지만 타입스크립트에서는 number 타입에 null을 넣으면 에러가 발생한다. null은 number 타입에 포함되지 않기 때문이다. ts let numA: number = null; // ❌ 기본적으로 에러 발생 만약 null을 다른 타입의 임시 값으로 허용하고 싶다면 tsconfig.json에서 설정을 변경해야 한다. tsconfig.json 설정 JSON { "compilerOptions": { "strictNullChecks": false, // ... } } - strictNullChecks: null 값을 변수에 할당하는 것을 엄격하게 검사할지 여부를 결정하는 옵션이다. - true (기본값): null 타입이 아닌 변수에는 null을 할당할 수 없다. (strict 옵션이 켜져 있으면 자동으로 켜진다.) - false: 모든 타입의 변수에 null을 자유롭게 할당할 수 있다. > Note: 안전한 타이핑을 위해 특별한 이유가 없다면 strictNullChecks는 true로 유지(또는 삭제하지 않음)하는 것이 좋다. > --- 3. 리터럴 타입 (Literal Type) 타입스크립트에는 string, number처럼 범용적인 타입 외에, 딱 하나의 값만 포함하는 타입도 존재한다. 이를 리터럴(값) 타입이라고 부른다. 변수의 타입을 특정 값 그 자체로 지정하면, 해당 변수는 그 값 이외의 어떤 값도 가질 수 없게 된다. ts // 숫자 리터럴 타입 let numA: 10 = 10; // numA = 12; // ❌ 10 이외의 값 할당 불가 // 문자열 리터럴 타입 let strA: "Hello" = "Hello"; // 불리언 리터럴 타입 let boolA: true = true; --- 요약 1. 원시 타입: 하나의 값만 저장한다 (number, string, boolean, null, undefined). 2. strictNullChecks: null 값을 엄격하게 관리하는 옵션이다. 안전한 코드를 위해 켜두는 것을 권장한다. 3. 리터럴 타입: 값 자체가 타입이 되어, 지정된 값만 허용하는 타입이다.

TypeScriptStudy기초문법Primitive TypeLiteral Type