JiSoo's Devlog
[Udemy React 완벽 가이드] 리액트 + TypeScript 본문
타입스크립트는 자바스크립트의 '슈퍼셋' 언어
자바스크립트를 기반으로 하되 더 확장된 프로그래밍 언어
타입스크립트는 리액트와 달리 자바스크립트 라이브러리가 아니기 때문에 자바스크립트의 기존 기능을 기반으로 새로운 기능을 만들거나 기능을 확장하지 않는다
가장 중요한 건 정적 타입의 특징을 갖는다는 것 !!
런타임에 오류의 원인을 찾을 필요 없이 코드를 작성할 때 바로 오류가 표시된다
TypeScript: JavaScript With Syntax For Types. (typescriptlang.org)
npm install typescript
자바스크립트 문법에 타입 표기 구문이 추가되었다
컴파일러를 사용하려면
npx tsc
변수 선언
기본형 타입 : 숫자형, 문자열, 논리형
let age: number;
age = 12;
let age:number = 12;
해당 변수에 저장할 자료형 지정
값을 나중에 할당할 때 실수형을 포함해 숫자형이면 다 가능
let userName: string;
userName = 'Max';
let isInstructor: boolean;
isInstructor = true;
number과 string 모두 소문자
대문자로 쓰면 자바스크립트의 Number 객체가 된다
배열 및 객체 유형
let hobbies: string[];
hobbies = ['sports', 'cooking'];
string 뒤에 대괄호를 붙이면 문자열 배열을 만들 수 있다
타입을 설정해주지 않으면 기본 타입이 any가 되는데 이렇게 하면 저장할 값의 타입에 대해 알려줄 게 없다는 뜻이 된다
하지만 이 타입은 예비적으로 사용되는 타입이므로 사용하지 않는 게 좋다
let person: {
name: string;
age: number;
}
person = {
name:'Jisoo',
age:24
}
객체의 타입 지정
let people: {
name: string;
age: number;
}[];
객체 배열도 설정 가능
타입 추론
기본적으로 타입스크립트는 가능한 많은 타입을 유추하려고 한다
타입 추론 기능을 활용해 코드를 작성하는 게 권장되는 방식
→ 불필요한 타입 지정을 하지 않아도 되기 때문
유니온 타입
유니온 타입은 타입을 정의할 때 한 개 이상의 타입을 사용할 수 있다
let course: string | number = "apple";
course = 123;
첫 번째 타입 뒤에 파이프 문자(|)를 넣고 뒤에 다른 타입을 추가하면 된다
값과 타입을 좀 더 유연하게 정의할 수 있게 해 준다
타입 별칭(Type Alias)
기본 타입을 만들어 거기에 복잡한 타입을 정의해 두고 그 타입을 사용하는 것
type Person = {
name: string;
age: number;
};
let person: Person;
let people: Person[];
type 키워드를 사용해서 type 새로운 이름 = 타입 정의
이 기능은 타입스크립트에만 존재하기 때문에 자바스크립트로 컴파일하면 코드에서 사라지게 된다
한 번만 정의해서 필요한 모든 곳에 반복해서 사용할 수 있다
함수 유형
function add(a: number, b: number) {
return a + b;
}
함수의 매개변수에도 타입을 지정할 수 있다
반환값이 갖는 타입을 통해 함수 타입을 추론하는 것
function add(a: number, b: number): number {
return a + b;
}
명시적으로 지정도 가능하지만 꼭 지정해야 할 이유가 없다면 지정하지 않은 게 좋다
함수에서 타입을 사용할 때는 매개변수의 타입뿐만 아니라 반환 값의 타입도 생각해야 한다
function print(value: any) {
console.log(value);
}
이런 식으로 return문이 없고 반환값이 없다면 반환 타입이 void가 된다
void는 함수에 반환값이 없다는 걸 뜻하고 함수에만 있는 특수한 타입이다
제네릭 타입
함수 안에서만 사용할 수 있는 타입이고 보통 Type의 T를 따서 사용하지만 어떤 식별자든 상관없다
제네릭 타입을 사용해 타입스크립트에게 any 타입이 아니라는 걸 알려줬기 때문에 updatedArray가 숫자 배열이라는 것을 알게 된다
대신 array 배열과 value 값이 같은 타입을 가져야 한다는 걸 알려줬다
어떤 타입이든 사용할 수 있지만 특정 타입을 사용해 함수를 실행하고 나면 해당 타입으로 고정되어 동작한다
유연성과 타입 안정성 측면에서 모두 도움이 된다
let numbers: number[] = [1, 2, 3];
이렇게 명시적으로 할당하는 것은 실제로 문법적 대안이다
실제 유형은 Array, 모든 배열은 Array 유형이고 Array도 제네릭 유형이다
let numbers: Array<number> = [1, 2, 3];
꺾쇠괄호 <>를 사용해 제네릭 유형을 정의할 수 있을 뿐만 아니라 제네릭 유형을 사용하고 사용해야 하는 자리 표시자 유형을 명시적으로 설정할 수도 있다
타입스크립트 기반 프로젝트 생성
npx create-react-app my-app --template typescript
파일 확장자가 .tsx인 이유는 그 안에서 JSX 문법을 사용하기 때문
JSX 문법을 사용할 때 개발 툴에 불필요한 경고창이 뜨지 않게 하려면 확장자로 .tsx를 사용해야 한다
타입스크립트를 자바스크립트로 컴파일하는 단계가 있기 때문에 우리가 직접 변환할 필요가 없다
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
이게 자바스크립트 라이브러리에 타입 표기 기능을 추가해 준다
리액트에서 props는 언제나 객체 형태
function Todos(props: { items: string[] }) {
props에는 컴포넌트에 추가한 키-값 쌍만이 아니라 특별한 프로퍼티인 children이 있다
props를 사용하도록 정의한 모든 컴포넌트에 대해 객체에 미리 정의된 props를 매번 추가해야 하는 건 번거로운 일이다
→ 함수형 컴포넌트를 제네릭 함수로 변환해 이용하기
import React from "react";
const Todos:React.FC = (props) => {
FC는 FunctionComponent라는 타입으로 리액트 패키지에 내장된 타입 정의
이렇게 타입을 정의함으로써 이 함수가 함수형 컴포넌트로 동작한다는 걸 명확히 하는 것이다
타입스크립트와 개발 툴은 React.FC라는 타입 표기로 이 함수가 받는 값이 props 객체라는 걸 이해하게 된다
React.FC 자체가 제네릭 타입이기 때문에 새로 정의한 타입을 props 객체에 합칠 수 있다
리액트와 타입스크립트로 함수형 컴포넌트를 만들려면 React.FC 타입을 함수형 컴포넌트 상수 옆에 사용하고
홑화살괄호 <{}>를 붙인 다음
사이에 필요한 형태의 props를 정의하면 된다
import React from "react";
const Todos: React.FC<{ items: string[] }> = (props) => {
return (
<ul>
{props.items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
};
export default Todos;
function App() {
return (
<div>
<Todos items={["Learn React", "Learn TypeScript"]} />
</div>
);
}
리액트에서 타입스크립트를 사용하는 것의 장점이 컴포넌트 안에서 작업할 때 자동 완성 기능도 사용할 수 있다는 것!!
props를 새롭게 만들어 사용한다면 제네릭 타입 추가하고 홑화살괄호 안에 새로운 props 타입 정의하면 끝
데이터 모델 추가하기
타입스크립트를 사용할 때는 미리 프로퍼티를 정의하고 해당 프로퍼티에 어떤 타입을 가진 값이 저장되는지 명확히 밝혀야 한다
class Todo {
id: string;
text: string;
constructor(todoText: string) {
this.text = todoText;
this.id = new Date().toISOString();
}
}
export default Todo;
이 클래스는 새로운 객체를 생성하는 생성자 역할만이 아니라 타입 역할도 한다
const Todos: React.FC<{ items: Todo[] }> = (props) => {
이렇게 하면 items는 객체로 채워진 배열이고 그 객체는 Todo 클래스의 정의에 부합하는 객체이다
양식 제출
const submitHandler = (event: React.FormEvent) => {
event.preventDefault();
};
여기서 React.FormEvent는 리액트 패키지가 제공하는 특수 타입
event 객체 타입은 폼 제출 이벤트를 수신하면 자동적으로 받게 된다
MouseEvent 타입도 있는데 이건 onClick 이벤트 리스너를 등록하면 받게 된다
refs 및 useRef 작업
const todoTextInputRef = useRef()
return (
<form onSubmit={submitHandler}>
<label htmlFor="text">Todo text</label>
<input type="text" id="text" ref={todoTextInputRef}/>
<button>Add Todo</button>
</form>
);
저렇게만 하면 레퍼런스가 입력창에 연결될 거라는 걸 알지 못하지 때문에
레퍼런스에 저장될 데이터가 어떤 타입인지 명확히 밝혀야 한다
const todoTextInputRef = useRef<HTMLInputElement>()
위처럼 하면 저기서 생성하면 레퍼런스가 HTMLInputElement와 연결된다는 게 명확해진다
모든 돔 요소들은 미리 정의된 타입을 가진다
input 요소의 타입은 HTMLInputElement
button 요소의 타입은 HTMLButtonElement
paragraph 요소의 타입은 HTMLParagraphElement
레퍼런스에 다른 요소가 할당되어 있을 수 있기 때문에 시작 값을 넣어줘야 한다
const todoTextInputRef = useRef<HTMLInputElement>(null);
레퍼런스에는 항상 current 프로퍼티가 있고 여기에 실제 값이 들어있다
const enteredText = todoTextInputRef.current?.value;
물음표를 추가해 타입스크립트에게 일단 값에 접근해 보고 접근이 가능하다면 그때 입력값을 가져와 enteredText에 저장하라고 알리는 것이다
접근이 불가능하면 아직 연결이 안 된 상태고 null이 enteredText에 저장된다
값이 null 이 아닌, 레퍼런스와 요소가 연결되었다는 걸 알고 있다면 물음표 대신 느낌표를 사용할 수 있다
느낌표는 이 값이 null이 될 수 있다는 건 알지만 이 시점에는 절대 null이 아니라는 걸 확신하는 경우에 사용해야 한다
const enteredText = todoTextInputRef.current!.value;
함수타입 정의
const NewTodo: React.FC<{ onAddTodo: () => void }> = (props) => {
onAddTodo는 반환값이 필요 없어 void로 지정
매개변수를 받아야 한다면
const NewTodo: React.FC<{ onAddTodo: (text: string) => void }> = (props) => {
타입이 never이라면 어떤 값도 추가될 수 없다
const [todos, setTodos] = useState<Todo[]>([]);
타입이 Todo로 구성된 배열
const addTodoHandler = (todoText: string) => {
const newTodo = new Todo(todoText);
setTodos((prevTodos) => {
return prevTodos.concat(newTodo);
});
};
이전 state를 기반으로 state를 업데이트하기 위해 함수 형태로 state 업데이트
concat() 메서드를 사용해 새로운 배열 생성
함수의 인수 타입을 정의하는 건 선택 사항이다
const TodoItem: React.FC<{
text: string;
onRemoveTodo: (event: React.MouseEvent) => void;
}> = (props) =>
const TodoItem: React.FC<{
text: string;
onRemoveTodo: () => void;
}> = (props) =>
bind()는 자바스크립트에서 제공하는 메서드로 실행할 함수를 미리 설정할 수 있다
<TodoItem
key={item.id}
text={item.text}
onRemoveTodo={props.onRemoveTodo.bind(null, item.id)}
/>
item.id를 onRemoveTodo가 매개변수로 받게 된다
tsconfig.json
'target'은 작성한 코드를 어떤 자바스크립트 버전을 변환할 건지 결정
"target": "es5",
'lib'에 포함된 라이브러리는 어떤 타입이 타입스크립트에서 기본으로 지원되는지 결정
"lib": [
"dom",
"dom.iterable",
"esnext"
],
'allowJs'는 .js 파일 포함 여부 결정
일반 자바스크립트 파일을 프로젝트에 둘 건지 일반 자바스크립트 파일로부터 뭔가 가져올 때 오류를 표시하지 않을 건지
"allowJs": true,
'stric' 옵션은 매우 중요한데 true로 설정하면 이 프로젝트에 가장 엄격한 설정이 적용된다
다른 검증 기능도 활성화되지만 묵시적 any 타입을 잡아내는 기능이 가장 중요하다
"strict": true,
'jsx' 옵션은 JSX 코드를 지원할 건지 결과물로 어떤 코드를 생성할 건지 결정
"jsx": "react-jsx"
'Frontend > Typescript' 카테고리의 다른 글
[타입스크립트로 블록체인 만들기] Typescript Blockchain (0) | 2024.04.20 |
---|---|
[타입스크립트로 블록체인 만들기] Classes and interfaces (0) | 2024.04.17 |
[타입스크립트로 블록체인 만들기] Functions (0) | 2024.03.31 |
[타입스크립트로 블록체인 만들기] Overview of Typescript (0) | 2024.03.31 |