💻 Frontend/JS, TS

[코딩온] 프론트엔드 입문 Day 46~47 (TS 입문)

hjinn0813 2024. 4. 25. 11:20
728x90

3차 프로젝트 전 마지막 수업 내용은 이름만 익히 알고 있어서 궁금했던 TypeScript였다. 수업 시간에 실제로 사용해보니 상당히 쉽고 직관적인 느낌이었다.


TypeScript

2012년에 마이크로소프트에 의해 개발된 오픈 소스 프로그래밍 언어.

JS로 컴파일되는 JS의 타입이 있는 상위집합으로, '타입이 있는 자바스크립트'라는 의미를 갖고 있다.


TypeScript의 특징

  • JS의 기본 문법에 자료형을 체크하는 기능이 추가되어, JS가 자의적으로 데이터 타입을 해석하고 코드를 실행했을 때, 의도와 다른 방식으로 쓰이지 않게 방지한다.
  • 정적 파일 언어 - 실행하지 않고도 코드의 에러를 알려준다. (실시간 디버깅)
  • TS는 JS로 변환되어야 브라우저가 해석할 수 있어서 '트랜스파일러(Transpiler)'를 사용해야 한다.
  • 변수, 함수를 만들 때 타입까지 명시해서 선언해야 한다.

TypeScript 사용하기

웹 브라우저는 ts 확장자 파일을 읽을 수 없기 때문에 JS로의 변환 과정이 필요하다.

가장 먼저 아래 명령어로 node.js 프로젝트를 생성하면, package.json 파일이 생긴다.

// node.js로 package.json 파일 만들기
$ npm init

그 다음에는 아래의 명령어로 TS 모듈을 전역으로 설치하고, 버전을 확인해서 제대로 설치됐는지 점검한다.

TS를 JS로 변환하는 명령어는 npx tsc 인데, 이렇게 하면 같은 이름의 JS 파일이 생성된다.

// 전역으로 타입스크립트 모듈 설치
$ npm install typescript -g

// 타입스크립트 버전 확인
$ npm typescript -v

// ts -> js 변환 (js 만들기)
$ npx tsc 01_Basic_type.ts

그런데 매번 이렇게 TS를 JS로 변환해서 확인해야 한다면 그 과정이 번거롭기 때문에,

node를 설치하여 JS 파일을 만들지 않고 TS를 바로 확인하는 방법을 권장한다.

// ts를 바로 확인할 수 있게 node 전역으로 설치
$ npm install ts-node -g

// js 안 만들고 ts 바로 실행
$ npx ts-node 02_TS_only_type.ts

TypeScript의 데이터 타입

TS에는 JS에도 있는 데이터 타입과 TS에만 있는 데이터 타입 등, 두 가지로 나눌 수 있다.

  • JS의 데이터 타입 - number, string, boolean, null, undefined, object, array.
  • TS에만 있는 타입 - Tuple, Enum, never, void, any

1. JS의 데이터 타입

/* 1. JS에도 있는 데이터 타입 */
// let 변수: 타입 = 값;

let num: number = 1;
let srt: string = '안녕';
let isTrue: boolean = true;
let undef: undefined;
let empty: null = null;

// 원시타입은 타입 작성하지 않아도 ts가 알아서 타입을 추론한다.
let isTrue2 = true;
let undef2;
let empty2 = null;

// 타입 추론 (암묵적으로 타입 지정됨)
let aa = 424;
let bb = 'hello';
let cc = true;
let dd = null;
let ee;

ee = 5;
ee = 'hello';
  • 기본적인 작성방법은 위 코드처럼 → let 변수: 타입 = 값;
  • 원시 자료형(primitive)에 속하는 string, number, boolean, undefined, null은 타입을 작성하지 않아도 TS가 알아서 타입을 추론한다.
  • TS는 암묵적으로 타입을 추론할 수 있다.
    let aa = 424; 라고 적어놓은 것은 let aa: number = 424; 와 같은 의미이다.
/* array: 요소의 타입 명시 필수! */
// 1. 요소가 한 가지 타입인 경우
let numArr: number[] = [1, 2, 3, 4, 5];
let strArr: Array<string> = ['가', '나', '다', '라'];

// 2. 요소가 여러 타입인 경우
let arr1: (number | string)[] = [1, 2, 3, '가', '나', '다'];
let arr2: Array<number | string> = [1, 2, 3, '가', '나', '다'];

let arr3: (boolean | null | number[])[] = [true, null, false, [4, 22]];
let arr4: Array<boolean | null | number[]> = [true, null, false, [4, 22]];

// 어떤 자료형이든 상관없이 들어갈 수 있는 배열 (any 사용 지양)
let arrAny: any[] = [8, 3, true, 'a', 'b', null];

/* object */
let obj1: object = {
  name: 'layla',
  gender: 'female',
};
  • 배열 데이터 작성은 만약 요소가 한 가지 타입인 경우라면,
    → let 배열이름: 타입[] = [실제 배열 내용];
    → let 배열이름: Array<타입> = [실제 배열 내용];
  • 배열의 요소가 여러 타입인 경우에는 OR 연산자로 연결해서 작성한다.
  • 배열에 어떤 자료형이든 상관없이 모두 들어가게 하려면 Any 타입으로 설정한다.
    그러나 부득이한 경우가 아니라면 사용을 지양한다.
  • 객체 데이터 역시 변수에 담으면서 데이터의 타입을 명시한다.

2. TS에만 있는 데이터 타입 

/* 2. TS에만 있는 데이터 타입 */
/* Tuple: 개수, 데이터 타입, 순서가 정해져있는 배열 */

let drink: [string, number] = ['americano', 4500];

drink[0] = '라떼'; // 배열의 요소에 index로 접근 가능
// drink[0] = 5100; -> ERROR: 정해진 타입이 아니면 수정 불가

drink.push('push하기');
console.log(drink); // 배열 출력 

// drink[3]= '수정불가' -> ERROR: 타입을 지정하지 않은 요소 수정 불가능

// spread 연산자로 배열 요소 전부 보여주기
console.log(...drink);

/* readonly: 길이를 특정하고 타입과 순서를 완벽히 고정하려고 사용 */
let drink2: readonly [string, number] = ['바나나우유', 2000];
// drink2[0] = '딸기우유' -> ERROR: 값 수정 불가
// drink2.push() -> ERROR: 추가 불가능

Tuple

  • 기본적인 형태는 JS의 배열 데이터와 같지만, 요소의 길이와 타입이 고정되어있는 배열이다.
  • 위 코드를 보면, drink라는 배열은 문자와 숫자 데이터 타입을 가지고 있고, 요소는 '아메리카노'와 '4500'이 들어있는 배열이다. 명시한 데이터 타입 순서대로 배열 내부 요소의 위치가 결정된다.
  • index로 배열의 요소에 접근하고 수정도 가능하지만, 같은 데이터 타입끼리만 수정할 수 있다. (문자 → 문자)
  • 배열이기 때문에 JS에서처럼 push() 메서드로 배열에 요소를 집어넣을 수 있고, spread 연산자로 배열의 요소를 전부 꺼내어 확인할 수 있다.
  • readonly는 말 그대로 '읽기 전용'이다. 데이터타입을 명시하기 전에 readonly 키워드를 사용하면, 해당 배열의 길이, 데이터 타입, 순서가 고정되어 수정이 불가능하다.

/* enum */

enum Auth {
  admin = 0,
  user = 1,
  guest = 2,
}

// 객체는 아니지만 점 접근법으로 접근 가능
console.log(Auth.admin);
console.log(Auth.user);
console.log(Auth.guest);

// 경우에 따라 제한하기
const userType: number = 2;

if (userType !== Auth.admin) {
  console.log('관리자 권한 없음');
  if (userType !== Auth.user) {
    console.log('게스트 계정입니다');
  }
}

// enum 값 자동 할당
enum productType {
  food,
  clothes,
  shoes,
}
console.log(productType.food); // 0 출력

// 문자형 enum
enum Cafe {
  americano = '아메리카노',
  latte = '라떼',
}

enum cola {
  coca, // 0
  pepsi, // 1
  zero, // 2
  sprite = '스프라이트',
  // 숫자와 문자 같이 사용할 수 있지만 권장하진 않음
}

Enum

  • 특정 값들을 모아서 나열하기 위한 데이터 타입. 값에 미리 이름을 정의하고 사용한다.
  • 타입을 명시하고 그룹 이름 뒤에 속성과 값을 입력한다. key와 value가 짝을 이루고 있고, 점 접근법으로 접근할 수 있다는 부분에서는 JS의 객체 데이터와 비슷하지만 차이가 있다.
    → enum은 선언한 뒤에 내용을 추가/삭제할 수 없고, value는 문자나 숫자만 가능하다.
  • userType의 경우에 따라 다른 메시지를 보여주는 코드는, userType이라는 변수가 2라는 숫자 데이터를 갖고 있다고 선언하고 해당 변수를 if문에 대입해서 작성했다.
  • productType처럼, 값을 넣지 않는다면 자동으로 숫자가 할당되어 선언된다.
  • cola처럼 숫자와 문자 데이터를 같이 사용할 수는 있지만 권장하지는 않는다.

/* any */

let myVal: any = 1;
console.log(myVal);

myVal = '문자열로 바꿈';
console.log(myVal);

myVal = [1, 2, '배열', true, ['안녕', null, { name: '객체' }]];
console.log(myVal);

Any

  • 어떤 데이터 타입이든 넣을 수 있어서 오류가 발생되지 않는다.
  • JS에서 변수를 선언하고 사용할 때와 동일하게 마음대로 값을 할당하고 수정할 수 있다.
    하지만 any를 사용한다면 TS를 사용하는 의미가 없어서 사용을 지양하는걸 추천한다.
  • 빈 배열이 필요하거나, 받아오는 데이터 타입이 확실하지 않거나, 할당해야하는 타입을 모르는(외부 라이브러리 사용 등) 경우에만 사용한다.

/* interface */

// 개발자가 직접 정의한 객체의 상세 타입을 선언
interface Student {
  name: string;
  isPassed: boolean;
}

// interface 확장하기
// 특정 키워드 필요없이 확장할 key: value 입력하면 됨
interface Student {
  age: number;
}

const Student2: Student = {
  name: 'layla',
  isPassed: true,
  age: 1,
};

//  interface 상속하기
interface BaseballPlayer extends Student {
  readonly position: string;
  height: number;
  backNumber?: number;
  // key 뒤에 물음표 작성하면 있어도 없어도 문제없는 옵셔널한 키 지정 가능
}

const Lee: BaseballPlayer = {
  name: '이정후',
  isPassed: false,
  age: 27,
  position: '외야수',
  height: 185,
  // backnumber는 옵셔널한 키라서 작성하지 않아도 에러 없음
};

// Lee.position = '타자' -> readonly여서 변경 불가능

Interface

  • 개발자가 직접 정의하는 타입 중 하나로, 여러 오브젝트의 타입을 정의하는 규칙.
    쉽게 말해서, 객체의 value들이 각각 어떤 타입을 갖고 있는지 하나의 이름으로 묶어서 가져다 쓰는 것.
    interface 자체는 객체가 아니므로 선언 종료시 세미콜론을 사용한다.
  • 위 코드에서는 Student라는 interface를 만들어서 이름은 문자 데이터, isPassed는 불리언 타입이라고 명시했다.
    추가적으로 확장을 위해, 특정 키워드 입력 없이 바로 key: value type을 작성했다.
    그리고 const로 Student2라는 객체를 만들어서 interface 이름 불러오고, 해당 객체의 실제 value를 입력했다.
  • interface의 상속은 extends 키워드를 사용한다.
    위 코드에서는 BaseballPlayer에 Student를 상속해와서, BaseballPlayer라는 interface를 사용해 만들어질 객체는 Student에서 사용한 이름, isPassed, 나이에 추가적으로 포지션, 키, 백넘버를 입력하도록 설정했다.
    포지션에 readonly가 되어있어서 수정이 불가능하고, 백넘버 뒤에 물음표로 '옵셔널한 키'가 되도록 설정했기 때문에 객체를 만들때 입력하지 않아도 오류가 뜨지 않는다.

/* type */

type Score = 'A+' | 'A' | 'B' | 'C' | 'D' | 'F';
interface HighSchoolStudent extends Student {
  // student에서 name, isPassed, age 키 상속
  result: Score;
  
  /* Indexable */
  // key의 값을 해당 인터페이스 사용하는 객체를 선언할 때 지정하는 경우,
  // key를 지정하지 않은 상태에서 해당 key의 타입을 지정할 수 있음
  // -> grade는 숫자 타입(객체)이고 여기에 성적이 value로 들어감
  [grade: number]: Score;
}

const Tom: HighSchoolStudent = {
  name: 'Tom',
  age: 17,
  isPassed: true,
  result: 'A+',
  1: 'B', // [grade: number]: Score;
};

// 객체 value의 타입 지정하기
type Toy = {
  name: string;
  price: number;
};

const barbie: Toy = {
  name: 'barbie',
  price: 34000,
};

// 값을 특정짓는(한계를 두는) type 생성
type Gender = 'female' | 'male';
const layla: Gender = 'female';
// const lily: Gender = 'Female'; -> ERROR: 대소문자 구분

// 값의 타입을 제한하는 type 생성
type name = string;
const laylaName: name = 'layla';

type NumAndStr = number | string;
const numAndSrtVal1: NumAndStr = 1;
const numAndSrtVal2: NumAndStr = '1';

Type

  • 인터페이스와 마찬가지로, 개발자가 복잡한 타입에 별칭을 정의해서 관리하는 데이터 타입.
    타입의 이름을 설정하고, 여기에 들어올 값의 종류까지 만들어서 관리할 수 있다.
  • 위 코드에서는 Score라는 타입을 만들고, 해당 타입의 값으로 6개의 학점을 설정했다.
    그 다음 HighSchoolStudent 라는 인터페이스에 Student를 상속해와서 result라는 key가 가지게 될 value의 타입이 미리 지정한 Score가 되도록 했다.
  • 만약 해당 인터페이스를 사용하는 객체를 선언하면서 새로운 key를 입력하는 경우, 인덱스 시그니처(indexable)를 이용해서 새로운 key를 지정할 수 있다. → 위 코드에서는 1학년때 B학점을 맞았다는걸 표시하기 위해, 만약 grade에 숫자 데이터가 들어오면 해당 key의 value를 (우리가 미리 지정한) Score라는 데이터 타입으로 보여주겠다고 설정한 것이다.
  • 객체의 value에 들어올 데이터 타입을 설정하여 객체를 만들 수 있고, value의 개수나 타입을 제한하여 변수를 선언하고 값을 할당할 때 정해진 범위나 타입만 들어오게 할 수 있다. OR 연산자로 다수의 타입을 지정할 수 있다.
💡 여기서 잠깐!
interface와 type 모두 개발자가 직접 만드는 커스텀 타입이지만 차이점이 있다!
* interface - 다수의 value의 타입을 한번에 지정해서 사용한다. 확장과 상속 가능.

* type - 타입 하나를 만들고 여기에 들어갈 value의 종류와 개수까지 지정해서 사용한다. 확장 불가능.


여기까지가 TypeScript에 대한 기본적인 내용이었다.

TS에서의 함수 선언법과 React에서 어떻게 사용하는지도 기록해야 하는데 그것까지 여기에 적으면 글이 너무 길어질 것 같아서 다음 포스팅에 적으려고 한다.

728x90