저장소를 git clone 한 후 , 필요한 package 를 추가적으로 설치한다.
npm install -g typescript : typescript 패키지@types/react @types/react-dom : 타입 정의 파일@typescript-eslint/eslint-plugin @typescript-eslint/parser eslint : typescript-eslint 적용하기 위한 typescript, eslint, typescript-eslint 관련 패키지eslint-config-prettier eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks prettier : typescript-eslint + prettier 적용 관련 패키지vsCode 에서 tsconfig.json을 참고해서 타입스크립트 문법을 검사한다. 뿐만 아니라 웹팩에서 설정할 ts-loader가 이 파일을 참고해서 트랜스파일 작업을 하기 때문에 tsconfig.json 파일은 먼저 생성해야 한다.
{
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["es6", "dom"],
"rootDir": "src",
"module": "CommonJS",
"esModuleInterop": true,
"target": "es5",
"sourceMap": true,
"moduleResolution": "node",
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"allowJs": true
},
"include": ["./src"],
"exclude": ["node_modules", "build"]
}
jsx: .tsx 확장자의 컴파일 결과 JSX 코드를 컴파일할 방법 결정
lib: 컴파일에 필요한 JavaScript 내장 라이브러리 지정
rootDir: 루트 디렉토리 기준 변경하는 것으로 js 아웃풋 경로에 영향
module: 프로그램에서 사용할 모듈 시스템 (target 프로퍼티가 es5로 지정되었을 때 CommonJS 로 지정)
esModuleInterop: 모든 가져오기에 대한 네임스페이스 객체 생성을 통해 CommonJS와 ES 모듈 간의 상호 운용성 제공
target: 컴파일할 JavaScript 버전 지정
sourceMap: 컴파일된 파일 디렉터리에 .js.map 파일 생성 설정
moduleResolution : module 프로퍼티에 따라 결정하는 것으로 모듈 분석 방법을 설정
allowJs: JavaScript 파일 컴파일 허용
// 엄격한 유형 검사 옵션
noImplicitReturns: 함수에서 return 없으면 에러
noImplicitThis: 명시적이지 않은 any 유형으로 this 표현식 사용시 에러
noImplicitAny: 명시적이지 않은 any 유형으로 표현식 및 선언 사용시 에러
strictNullChecks: 엄격한 null 검사 사용
ESLint: JavaScript 의 문법을 확인해주는 도구
module.exports = {
root: true,
env: {
browser: true,
node: true,
},
extends: [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
],
rules: {
"prettier/prettier": [
"error",
{
doubleQuote: true,
semi: true,
useTabs: false,
tabWidth: 4,
printWidth: 80,
bracketSpacing: true,
arrowParens: "avoid",
},
],
},
parserOptions: {
parser: "@typescript-eslint/parser",
},
};
vsCode 익스텐션에서 eslint 와 prettier 를 설치해준다.
이를 통해 오류를 검사할 수 있다.
설정을 다 마친 후, .js 를 .tsx 파일명을 변경해준다.
그러면 수많은 오류들이 터미널과 파일에 빨간줄을 통해 출력이 된다.
포팅하는 법은 타입 지정, props 들을 type이나 interface 로 설정해준다.
type TodoProps으로 생성해준다.TodoProps 의 프로퍼티로는 빨간줄로 표시된 오류에서 id, text, isComplete 를 찾아낼 수 있다. 각각의 타입을 number, string, boolean 으로 지정해준다.type TodoProps = {
id: number;
text: string;
isComplete: boolean;
}
useState로 todos 로 생성이 되어 있는데 여기서 타입을 추가해주는데 todos는 todo를 배열형식으로 만든 것이기 때문에 타입을 형식에 맞춰 작성한다.const [todos, setTodos] = useState<TodoProps[]>([]);
addTodo, removeTodo, completeTodo 에는 각각 todo, id, id 가 매개변수로 필요한 것을 알고 있고 이것을 타입만 지정해주면 된다.const addTodo = (todo: TodoProps) => {...}
const removeTodo = (id: number) => {...}
const completeTodo = (id: number) => {...}
todos, completeTodo, removeTodo 임을 알 수 있고 이 프로퍼티를 interface TodoListProps로 설정해준다.interface TodoListProps 안의 프로퍼티들은 각각의 타입을 지정해준다. 이때 App 에서 만들어준 TodoProps 를 사용한다.todos 는 todo를 가르키는 TodoProps를 배열화 한 것이다.completeTodo와 removeTodo 는 매개변수를 id 로 받는 void형 함수이다.interface TodoListProps{
todos: TodoProps[];
completeTodo(id:number):void;
removeTodo(id:number):void;
}
function Todo({props}:TodoListProps){ ... }
props로 받은 onSumbit 이벤트 함수를 interface TodoForm으로 지정해준다.

이때, 전달인자는 isComplete 프로퍼티가 빠진 TodoProps 타입을 알 수 있으며 void 형인 함수이다.
interface TodoFormProps {
onSubmit(todo: TodoProps):void;
}
const [input, setInput] = useState<string>("");
const [number, setNumber] = useState<number>(1);
TodoPropsTodoForm 에서 알 수 있듯이 onSubmit 함수의 전달인자는 todo인데, 그 중 isComplete 프로퍼티는 생략이 된 것을 알 수 있다. 따라서 todo를 나타내는 TodoProps type에서 isComplete 프로퍼티는 생략가능한 프로퍼티이므로 ? 키워드를 추가한다.TodoProps는 TodoForm과 Todo 컴포넌트에 사용되어 각각의 파일에서 import 를 해준다. import를 해주기 위해서는 해당 App 폴더에서 export를 해주어야만 다른 파일에서 사용가능하다.

e.target.value를 전달하기 위해 매개변수 e가 필요하다. 타입을 잘 모르겠어서 any로 설정해주었는데 기본값이 any형식으로 포함이 되기 때문에 굳이 설정해줄 필요가 없고 더 명확하게 타입을 명시해야 한다.onSumbit 은 React.FormEvent 관련 이벤트 타입으로 <HTMLFormElement> 요소 타입이다.
onChange 는 React.ChangeEvnet 관련 이벤트 타입으로 <HTMLInputElement> 요소 타입이다.
useRef 는 null로 초기화가 되어있고 타입지정을 무엇으로 해주어야 할 지 도무지 생각해도 잘 생각이 안났다.
그러다 useRef 가 사용되는 곳에서 마우스를 대 보니 <HTMLInputElement> 요소 타입인 것을 알아냈고, 위 이벤트 핸들러처럼 사용하였다.
하지만 타입을 지정해주어도 useEffect 내에서 inputRef.current 에 빨간줄 오류가 떴고, 이 값은 null 이 될 수도 있다는 뜻이었다.
null을 해결해주기 위해서 해당 분기를 해주었는데, 이보다 더 나은 방법은 없을까,,,?
? 키워드를 사용해서 선택적으로 null이 아닌 값만 자동적으로 받을 수 있게 수정하였다.
index.tsx 에서도 타입을 지정해주어야 했는데, document.getElementById()는 HTMLElement | null 형식이라고 도움을 주었다.
따라서 as 타입 단언을 이용해 HTMLElement 타입을 지정해주었다.
이론은 참 쉽게 느껴지고 포팅을 마치 후에는 그닥 어렵지 않은데 왜 시작이 항상 어려운지 모르겠다.
내 문제는 항상 어렵다고 회피하기 일수였지만, 이번 TIL 을 통해 조금이나마 포팅하는 방법을 외우는 방법이 아닌 이해하는 형식으로 배우게 되었다.
어렵게 생각하지말고 낯설다 라고 되뇌이며 노력해보자!