이번강의에서는 이전 과제에 대한 피드백과, typescript에 대한 전반적인 지식을 다루었습니다.
(해당 게시글은 실제 강의 내용을 그대로 옮겨적은것은 아니며, 주관적인 이해 및 판단으로 요약되어진 글입니다.)
대표적으로 제출된 각 팀들의 과제물에 대하여 피드백을 진행해주셨고, 대략적인 피드백은 아래와 같았습니다.
전역 상태를 다루는 방법은 전역 상태 Lib 뿐일까?
시간을 비롯한 기타 옵션이 다루어진 Data들에 대한 값이 있는 page에서 다른 page로 이동하였고, 다시 돌아올시. 이러한 옵션들을 보관하기 위해선 전역을 꼭 사용할 필요가 없습니다.
기본적으로 사용하는 URL이 이를 대체할수 있기 때문입니다.
오히려 URL을 사용하는것이 더욱 좋을 수도 있다고 하셨습니다.
서버로부터 받아오는 데이터를 깔끔히 처리하는것이 좋다.
단순 계산 로직 / 요청을 날리는 비즈니스 관련 로직 / UI관련 로직
이러한 것은 가능한 분리 하는것이 좋다고 하셨습니다.
의존성 줄이기
이전에도 다루었던 문제긴 하였지만,
set과 같은 함수는 useEffect에 넣을 필요가 없고,
특정 함수가 useEffect의 의존성으로서 필요하다면
useEffect에서 함수를 생성하고 실행시키는것이 좋은 방법이라고 말씀하셨습니다.
보는 사람이 쉽게 볼 수 있도록 하는 코드란
함수를 의미에 맞게 사용하는것이 좋다
예를 들어 함수를 합치는 로직의 경우, forEach등을 통하여 로직을 짜는것보다는, reduce 함수를 사용하는것이 조금 더 의미에 맞다고 하셨습니다.
커링을 사용해서 함수를 좀 더 간결리 할 수 있다 하셨습니다.
예를 들어, 똑같은 param을 사용하는 함수들이라면, 커링을 사용한 공통 함수를 만들고, 2번째 값만을 변경하여 간단하게 사용할 수 있다 하셨습니다.
export const dataConverter = {
getRoas: (start, ended) => {
return returnConvert(start, ended, 'roas');
},
getCost: (start, ended) => {
return returnConvert(start, ended, 'cost');
},
getImp: (start, ended) => {
return returnConvert(start, ended, 'imp');
},
};
=>함수를 담은 객체
export const dataConverter = {
getRoas: returnConvert('roas'),
getCost: returnConvert('cost'),
getImp: returnConvert('imp'),
};
=> 커링 적용 함수 작성- 적용 후
타입스크립트는 점점 프론트엔드 계열에서는 뉴 노멀이 되어가고있습니다. 21년 이후부터는 오히려 자바스크립트가 레거시 코드로 취급받을 정도입니다.
현 시점에서 타입스크립트는 필수에 가까운 skill set이 되어가고 있습니다.
자바스크립트는 동적언어입니다.
또한 매우 자유도가 높은 언어입니다. 변수에는 정말 다양한 값이 들어갈 수 있습니다.
이러한 특성을 가지기 때문에 런타임 시점, 실제로 동작하기 이전까지는 결과값을 대략적으로 예상은 하지만, 정밀하게 예측은 할수는 없습니다.
그렇기 때문에, 개발환경에서는 크게 무리가 없는 결과물이, 실제 프로덕션 환경에서는 예상치 못했던 값들이 들어올수있습니다.
이런 문제점을 갖고 있는 자바스크립트에서, 타입스크립트를 사용한다면 어떠한 변화가 있을지 살펴보겠습니다.
이러한 예상치 못한 결과들을, 개발환경에서 변수 들을 정적타입으로 명시하여 비교적 쉽게 막을 수 있도록 도와줍니다.
또한, 사용해보면 생각외로 편리하다고 느껴질수 있습니다. 타입을 한번 잘 정리하면 이후 해당 값이 가지고 있는것을 판별하는게 매우 쉬워지기 때문입니다. (자동 완성기능 또한 제공)
문서화 기능을 가지고 있습니다.
다른사람의 코드 또는 Lib등을 보게되면, 이러한 코드가 어떠한 타입을 사용하는지를 알수 있게되어, 추상화의 기능으로서 코드를 알아보기 쉽게 합니다.
또한, 백엔드 개발자와 초기 셋팅을 통해 값을 정리해둔다면, 이후 백엔드 측과 많은 소통을 할 필요성이 줄어들것입니다.
이러한 이유로 타입스크립트는 오늘날 많이 사용되어지고 있습니다.
++ gitHub의 Difinitely Typed라는 프로젝트에는 타입 정보만을 모아놓은 라이브러리가 있습니다.
++ 타입스크립트는 런타임시에 다 사라지는, 일종의 개발환경 툴입니다.
이전 강의에서도 잠깐 다루었었는데, 자바스크립트의 타입은 원시형과 참조형이 있습니다. 참조형은 원시형이 아닌 값, 즉 Obj이고, 원시형은 Bool,String,number 등이 있습니다.
이러한 타입에서 원시형 타입은 의미에 맞게 사용하면 되지만,
Obj 타입은 범위가 매우 넓어집니다.
Obj 하위에는 Arr,객체,펑션,클래스 등 범위가 매우 넓어지기때문입니다.
그리고 모든 타입을 포함하는 any 타입이 있습니다.
이 any타입은 모든 타입을 포함하기때문에, 사용이 많아 질경우 타입스크립트를 사용하는 의미가 퇴색되어지기 때문에 사용이 지양되어 집니다.
unknown타입또한 모든 값을 할당 받을 수 있습니다. 하지만 unknown 타입의 경우 연산을 하는 경우 any와는 다르게 타입에 대하여 경고를 해줍니다.
any와 unknown 타입과 같이 모든 타입을 받을 수 있는 타입을 superset이라고도 합니다.
추가적으로 어떠한 값도 가지지 않는 Void.
Null과 undefined 타입.
그리고 아무 값도 할당 할수 없는, 아무것도 가지지 않음 또는 불가능을 나타내는 Never 타입등이 있습니다.
타입스크립트에서는 합집합과 교집합 연산이 있습니다.
합집합의 경우 |를 사용하고, (union)
교집합의 경우 &를 사용합니다.(intersection)
예시로서, string과 number 타입을 사용으로 두 가지 종류의 연산을 할 경우를 생각해보겠습니다.
string | number의 경우 합집합 적인 의미이긴하지만, 이 타입으로 지정할경우 두 타입 모두 사용할수 있는 함수만 호출할수있습니다.
하지만 &으로 지정할경우, 아무런 함수도 호출할수 없습니다.
공통되는 부분이 전혀 없기 때문입니다.
하지만 이러한 & 연산은 객체와 같은 값으로서 사용할때는 조금 달라집니다.
예를 들어, type C객체 = A객체 & B객체가 있다면
C객체는 a와 b의 값을 모두 사용 할 수 있습니다.
반대로 |를 사용하였다면, 공통되는 부분의 값만을 사용할수있습니다.
제네릭은 타입을 인자로 받아, 더 정확하게 사용할 수 있게 해주는 도구입니다.
예를 들어 useState의 경우, 특정 지네릭을 사용하지 않았다면 타입을 자동으로 추론해주는데, 이것은 아래와 같은 형식으로 되어있습니다.
useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
지네릭 S 를 사용하여서, 어떠한 값이 들어올경우 들어오는 time에 타입을 자동으로 추론하여, 그 타입을 고정시켜준다는 의미입니다.
타입스크립트를 사용하는 경우, 유니온 | 을 사용하여 여러가지 타입이 존재 할 수 있습니다. 예를 들어 null | 특정 Type 이거나, string | string[] 등의 상황이 있을 수 있습니다.
이런 상황에서는 타입가드라는것이 필요합니다. 이것은 if를 비롯하여 여러방식으로 이루어집니다.
대표적인 방식인 if의 경우
특정 props의 타입이 null | string이라면
if(null) return처리를 통해
if 이후의 문장에서는 props를 string으로서 구분하게 됩니다.
null 또는 undefined와 같은 값이 들어가있는 경우, 타입가드가 거의 필수적으로 필요하게 됩니다.
다른 방법으서, 객체의 경우 in을 통하여 타입가드를 할 수 있습니다.
타입 A와 타입 B를 | 을 통해 받는경우,
props를 in을 통하여 타입가르를 할 수 있습니다.
추가적으로, 타입스크립트는 몇가지 예외적인 타입가드 처리가 있습니다.
filter와 reduce와 같은것인데,
대표적인 예시는 아래와 같습니다.
const arr = ["a", "b", "c", "d", 1, 2, 3, 4]
const onlyNumber = arr.filter(character => typeof value === "number")
console.log(onlyNumber)
=> 결과는 빈 배열
const arr = ["a", "b", "c", "d", 1, 2, 3, 4]
const onlyNumber = arr.filter((character): character is number => isNumber(character))
const isNumber = (value: any): value is number => {
return typeof value === "number"
}
=> is Number를 사용하여 타입 체크 필요.
const res = arr.reduce((acc, cur) => {
return [...acc, cur.id + cur.name]
}, [])
=> acc[] never오류 발생
const res = arr.reduce<string[]>((acc, cur) => {
return [...acc, cur.id + cur.name]
}, [])
=> reduce에 필요로 하는 제네릭값 필요
1,타입 단언은 여러번 할필요가 없습니다.
타입스크립트는 타입만 제대로 명시되어 있다면, 이후 타입또한 알아서 추론하기 때문입니다.
2.any 타입은 제한적인 상황에서 사용 가능합니다.
예를 들어, 들어오는 값이 숫자 뿐만 아니라 여러가지 값이 들어오는 경우, 이를 체크하기 위한 함수를 만들기 위해선 any 타입을 사용 할 수 있을것입니다.
const isNumber = (value: any): value is number => {
return typeof value === "number"
}
또는, try, catch 구문에서 catch의 err타입은 꼭 객체만 넘어오는것이 아니기때문에, any를 사용하는 경우가 많습니다.
export type Props = {
theme: 'main' | 'basic';
type: 'submit' | 'button' | 'reset';
disabled?: boolean;
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
와 같이 Props의 타입또한 같이 명시해주는것이 좋습니다.
++ TS에서 사용하는 타입과 인터페이스의 기능적인 차이는 없습니다. 하지만 의미적인 차이가 있을 수 있습니다.
사실 타입스크립트에 대해선 약간 두려워하는 감이 있엇는데, 강의를 통해 조금 더 잘 이해할 수 있게 됐던것 같습니다.
또한 피드백에서 말씀해주신 param을 전역 상태로 사용하는것이나, 의미에 맞는 함수 사용, 그리고 커링이라는 기법을 사용하여 함수를 간결히 사용하는 방법 등에 대한 내용도 새로운 시야를 볼 수 있게 해줬던것 같습니다.
기억에 남는것 위주로 정리해보자면 아래와 같습니다.