원티드 프리온보딩 프론트엔드 코스

3주차 네번째 - 1 (TypeScript 기본 개념 정리)

syom 2022. 2. 9. 20:59

설 주가 지나고 세번째 주가 되었습니다.

이번 주에 TypeScript 로 프로젝트를 진행해야 했기 때문에 지난주에 TypeScript 를 이용하여 간단한 todolist 를 만들어보았습니다. 하지만 완전히 적응 하기에는 아직 멀은것 같아 TypeScript 로 바로 프로젝트를 진행하기 어렵지 않을까 했습니다. 하지만 이번 수업에서 TypeScript의 도움이 될만한 부분을 잘 설명해주셔서 도움이 많이 되었던 것 같습니다.

이번 글에서는 TypeScript 의 기본 개념과 특징에 대해 작성하고, 프로젝트 후기는 2편에 작성하도록 하겠습니다.


👉 TypeScript 를 시작하기 전에

관련 면접 질문 생각해보기

- TypeScript 특징은?

- TypeScript 도입의 장점/단점은?

- 스크립트 언어와 컴파일 언어 차이점

타입스크립트는 자바스크립트 언어에서 타입이 추가되는 언어입니다.
타입을 일일이 지정할 필요가 없던 자바스크립트는 유연해서 배우기 쉽고 개발이 빠르다는 장점이 있는 반면에 이 장점이 곧 단점이 되곤 했습니다. 타입스크립트를 처음 도입할 때에는 엄격하게 개발하는 것이 오히려 생산성을 떨어뜨린다고 하는 사람들도 있었습니다.
타입스크립트는 정적 타입 언어이기 때문에 자바스크립트로 변환하는 컴파일을 거쳐야합니다. 때문에 개발 단계에서 런타임 전에 에러를 잡고 문법적인 오류를 찾아낼 수 있어서 개발 속도가 빨라지고 협업 유지보수 측면에서도 코드만 보고 오해 없이 정확한 개발을 할 수 있다는 장점이 있습니다.

리액트와 웹팩(Webpack), 바벨(babel)

웹팩모듈 번들러입니다. 모듈이란 하나의 파일을 말하고, 번들러는 여러 파일을 합쳐서 하나의 파일을 만들어준 결과 파일을 말합니다.

배포를 할때에 필요한 파일은 하나의 HTML, 하나의 JS 파일이 필요합니다. 하지만 우리가 프로젝트를 진행할 때에는 여러 파일에 나눠서 작업을 하게 되는데 이를 번들러가 하나의 파일로 만들어주는 것입니다.

 

바벨컴파일러입니다. 컴파일러란 고수준의 언어를 기계가 이해하기 쉬운 저수누의 언어로 바꾸어주는 것을 말합니다. 바벨은 ES6 이상의 코드를 받아 ES5 코드로 바꾸어주는 역할을 합니다. 이는 구형 브라우저에서는 최신 문법이 동작하지 않을 수 있기 때문에 바벨을 통하여 이전 버전의 코드로 바꾸어주는 것입니다. CRA 나 Next.js 로 타입스크립트를 사용하면 기본 컴파일러가 세팅되어 있기 때문에 개발자가 따로 바벨을 설정할 필요는 없습니다.

 

 


👉 TypeScript

 

1. 함수

function formatDate(d: Date, delimiter?: string): string {
	let year = d.getFullYear();
	let month = d.getMonth() + 1;
	let day = d.getDate();

	return `${year}년 ${month}월 ${day}일`
}

const today = formatDate(new Date());

- 매개변수의 타입은 매개변수의 우측에 콜론(:)을 붙여 매개변수의 타입을 정의합니다.

- return 값의 타입은 소괄호() 우측에 콜론(:)을 붙여 타입을 정의합니다. return 값이 없을 경우 void 를 입력합니다.

- 선택적으로 인자를 전달하는 경우에는 물음표(?)를 사용합니다.

  => formatDate 라는 함수에서 매개변수 d 는 Date 객체 타입을 받아야하고 return 값은 string 이어야 합니다.

 

 

2. 자바스크립트에는 없는 타입 종류

  any  

- any 는 어떤 타입도 지정할 수 있습니다. 하지만 아무 타입도 지정하지 않는다는 것은 자바스크립트 코드와 같기 때문에 any 타입을 사용하면 타입스크립트를 쓸 이유가 없는 것과 다름없습니다. 따라서 가능하면 쓰지 않도록 노력하는 것이 좋습니다.

  void  

- void 는 any 와는 반대로 어떤 타입도 없다는 뜻입니다. 보통 함수에서 반환값이 없는 경우에 사용합니다.

  enum  

- 열거형 enum 은 이름이 있는 정해진 값의 세트라고 생각하면 됩니다.

enum Category {
	Pasta,  
	Pizza,  
	Dessert 
}

console.log(Category.Pasta);    // 0
console.log(Category.Pizza);    // 1
console.log(Category.Dessert);  // 2

  union  

- 여러 타입이 올 수 있을 때 사용합니다. 파이프(|)로 구분합니다.

function formatDate(date: string | number | Date): string {
	// 로직 생략
	return `${year}년 ${month}월 ${day}일`
}

formatDate(20201028);
formatDate(new Date());
formatDate('20201028');

  Tuple  

 

- 튜플은 배열과 마찬가지로 데이터를 순차적으로 저장할 수 있는 자료형 중 하나이며, 타입스크립트에서는 배열에 두개 이상의 타입을 사용하고 싶을 때 주로 사용합니다.

let starArr: [number, string]= [1, '1점'];

예제와 같은 경우에서 만약 [1] 과 같이 둘 중 하나의 타입만 존재한다면 에러가 발생합니다.

  Type Alias  

- type 이라는 키워드를 사용하여 타입에 이름을 붙여서 사용할 수 있습니다. 타입을 재사용하거나 객체를 위한 타입을 정의할 때 많이 사용됩니다.

type ID = number | string;

function checkInfo(info: { id: ID; pw: string }) {

}

let id: ID = "1010";
checkInfo({ id, pw: "password" });

객체의 타입을 정의하는 방법이 두 가지가 있는데 위와 같이 type 으로 정의하는 것이 있고, 나머지 하나는 Interface 타입을 사용하는 것입니다.

 

3. 타입 추론 (Type Inference)

- 타입스크립트에서는 타입을 표기하지 않아도 타입스크립트 컴파일러가 변수에 할당된 값을 보고 타입을 추측할 수 있습니다.

- 따라서 굳이 필요없는 타입 정의는 생략하되, 나중에는 유지보수를 위해서는 타입을 정의하는 것이 좋습니다.

 

4. 인터페이스 (Interface)

- 인터페이스는 객체의 타입을 정할 때 자주 쓰입니다.

- 객체의 프로퍼티가 하나인 경우에는 콜론(:) 을 사용해서 바로 타입을 작성하는 것도 괜찮지만, 두 개 이상일 때에는 Interface 나 type 을 사용하여 타입을 정의하고 사용하는 것이 더 깔끔하고 가독성이 좋습니다.

interface IProfile {
  readonly name: string;
  age: number;
  isOnline: bool;
  favorite: string[]
}

- readonly 는 읽기만 가능한 프로퍼티로 값을 수정할 수 없습니다.

* 에러 나는 케이스와 안나는 케이스 (타입 호환성)

=> 나중에 따로 정리할 예정입니다.

* 인터페이스 확장(extends)

extends 키워드를 사용하여 기존에 정의된 인터페이스를 확장해서 사용할 수 있습니다.

(상속 받는 것과 비슷함)

interface BasicInfo {
	name: string;
	star: number;
}

interface DetailInfo extends BasicInfo {
	address: string;
	phone: string;
	position: number[];
}

const sipboon: BasicInfo = {
	name: "십분의일",
	star: 5,
};

const chroad3: DetailInfo = {
	name: "취향로3가",
	star: 5,
	address: "을지로3가",
	phone: "123-456-7890",
	position: [37.565496, 126.99142],
};

* 교차 타입(Intersection Types)

interface BasicInfo {
	name: string;
	star: number;
}

interface DetailInfo {
	position: number[];
}

type Info = BasicInfo & DetailInfo;
// Info타입을 사용하는 경우, name, star, position 세 가지 프로퍼티가 모두 필요합니다!

새로운 인터페이스를 만드는 것이 아닌 새로운 타입을 만드는 것입니다.

 

 

5. 타입 호환성(Type Compatibility)

나중에 정리하겠습니다...

 

6. 제네릭 (Generic)

- 꺽쇄(<>) 를 사용하여 유동적으로 타입이 정의되는 것입니다. (c++ 의 template <T> 같은 느낌...)

function makeArr<T>(el: T): T[] {
	return [el];
}

makeArr<number>(1);       // [1]
makeArr<string>("1점");    // ["1점"]
makeArr<boolean>(true);    // [true]

 

 


👉 리액트를 위한 TypeScript

 

1. type vs interface

- 타입스크립트 공식 문서에는 확장, 병합 가능성이나 선언등의 차이점 때문에 type 보다 interface 를 사용하라고 되어있습니다.

 

2. 함수형 컴포넌트

리액트에서 함수형 컴포넌트를 위한 Function Component 타입을 제공합니다. 줄여서 FC 로 사용할 수 있습니다

interface a {
	star: number;
}

const Stars: React.FC<a> = ({ star }) => {
	return ();
}

FC 타입을 사용하면 React.FC 타입에서 함수의 매개변수인 props 와 반환값인 JSX가 이미 정의되어 있기 때문에 굳이 리턴값의 타입을 정의하지 않아도 됩니다.

 

3. Hooks

  useState  

- 꺽쇄(<>) 안에 타입을 정의합니다.

const [like, setLike] = useState<boolean | null>(false);

  useRef  

- 꺽쇄(<>) 안에 타입을 정의합니다. ref 로 지정한 element 타입을 넣으면 됩니다.

const reviewRef = useRef<HTMLDivElement>(null);

 

4. Event Handler

- 이벤트 핸들러는 "이벤트"를 매개변수로 받습니다. 이벤트 타입은 리액트에서 제공하는 타입을 사용하면 됩니다.

- 일반적으로 타입 이름은 "[이벤트타입]Event" 로 ChangeEvent, FormEvent, TouchEvent, MouseEvent 등이 있습니다.

const updateValue = (e: React.ChangeEvent<HTMLInputElement>) => {
	setEmail(e.target.value);
};

//생략

<input onChange={updateValue} />

=> 예제 코드에서는 onChange 이벤트이므로 ChangeEvent 타입을 사용하였고, input 태그에서 발생하기 때문에 HTMLInputElement를 제네릭으로 넘겨주었습니다.

* VSCode 를 사용한다면 매개변수 e 에 마우스를 호버하면 어떤 이벤트 타입인지 나옵니다.

 

5. 기타

JSX.Element

- 변수에 JSX 를 할당해서 값처럼 사용하는 경우가 많습니다. 간단히 아래와 같이 선언할 수 있습니다.

const WarningText: JSX.Element = <p>다시 확인해주세요.</p>

전역변수

- window 에 외부 라이브러리 변수를 추가하려면 아래와 같이 해당 라이브러리에서 사용하는 전역 변수명을 추가하면 됩니다.

declare global {
	interface Window {
		kakao: any;
	}
}

- 예를들어 kakao 로그인을 위해서 라이브러리를 추가하는 경우 window.kakao 로 접근하면 됩니다. 다만 타입스크립트 프로젝트에서는 window 객체에 추가된 외부 라이브러리라 할지라도 사용하기 위해 타입을 정의하지 않으면 에러메세지가 출력됩니다.