(SEB_FE) Section4 Unit5 JavaScript를 TypeScript로 포팅하기

PYM·2023년 6월 1일
0

(SEB_FE) SECTION4

목록 보기
20/24

💚환경 설정

🦖.eslintrc.js 파일

 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",
    },
};

🦖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"]
}

💚타입 구현 구조

  • types/types.tsx 파일에 필요한 타입을 정의해서 export 시켜서,
    각 파일에서 필요한 타입을 import 해서 사용했다.

💚types.tsx

// 각 개별 투두 데이터의 타입 정의 
type TodoType = {
    id: number;
    text: string;
    isComplete?: boolean;
};

// 각 투두 항목 컴포넌트가 받는 props의 타입 정의 
type TodoProps = {
    todos: TodoType[];
    completeTodo: (id: number) => void;
    removeTodo: (id: number) => void;
};

// 투두를 작성하는 TodoForm 컴포넌트가 받는 props의 타입 정의 
type TodoFormProps = {
    onSubmit: (todo: TodoType) => void;
};

export { TodoType, TodoProps, TodoFormProps };

💚index.tsx

const root = ReactDOM.createRoot(
    document.getElementById("root") as HTMLElement
);
  • index.tsx 파일에서 유념해야 할 점은 한 가지! 바로 root 변수에서 뒤에 as HTMLElement를 달아주는 것!

💚App.tsx

import TodoForm from "./component/TodoForm";
import Todo from "./component/Todo";
import { useState } from "react";
import "./todos.css";

import { TodoType } from "./types/types";

function Todos() {
    const [todos, setTodos] = useState<TodoType[]>([]);

    const addTodo = (todo: TodoType) => {
        if (!todo.text || /^\s*$/.test(todo.text)) {
            return;
        }

        const newTodos: TodoType[] = [todo, ...todos];

        setTodos(newTodos);
    };

    const removeTodo = (id: number) => {
        const removeArr = [...todos].filter((todo: TodoType) => todo.id !== id);

        setTodos(removeArr);
    };

    const completeTodo = (id: number) => {
        const completedTodo = todos.map((todo: TodoType) => {
            if (todo.id === id) {
                todo.isComplete = !todo.isComplete;
            }

            return todo;
        });

        setTodos(completedTodo);
    };

    return (
        <div>
            <div className="todo-app">
                <h1>To Do List</h1>
                <h2>오늘은 무슨 일을 계획하나요?</h2>
                <TodoForm onSubmit={addTodo} />
                <Todo
                    todos={todos}
                    completeTodo={completeTodo}
                    removeTodo={removeTodo}
                />
            </div>
        </div>
    );
}

export default Todos;
  • App.js의 구조는 다음과 같다.

    • 큰 제목 <h1> & 작은 제목 <h2>
    • 새로운 Todo항목을 입력하는 TodoForm 컴포넌트
      • props로 onSubmit={addTodo} 함수를 넘겨주고 있다.
    • 작성된 Todo 목록을 나타내는 Todo 컴포넌트
      • props로 전체 투두 목록인 todos, 투두를 클리어하는 함수 completeTodo, 투두를 삭제하는 removeTodo 함수를 전달하고 있다.
  • props로 넘겨주는 함수와 데이터들은 모두 App.js에서 정의하고 있다.

  • 전체 투두 목록을 담고있는 변수 todos의 타입은 types.tsx에서 importTodoType 을 적용.

  • addTodo 함수의 매개변수는 변수 todos에 새로이 추가되는 todo 항목으로, TodoType의 타입을 가진다.

  • todos에서 특정 todo 항목을 삭제하는 함수인 deleteTodo 함수의 매개변수는 지우고자하는 특정 todo의 idnumber 타입이다.

  • 특정 todo의 isComplete 값에 변경을 주는 completeTodo 함수의 매개변수 역시 변화를 주고자 하는 특정 todo의 id이므로, number 타입을 준다.

💚Todo.tsx

import { TodoProps } from "../types/types";

function Todo({ todos, completeTodo, removeTodo }: TodoProps) {
    return (
        <div className="wrapper-todo">
            {todos.map((todo, index) => {
                const todoClass = todo.isComplete
                    ? "todo-row complete"
                    : "todo-row";

                return (
                    <div className={todoClass} key={index}>
                        <div
                            key={todo.id}
                            onClick={() => completeTodo(todo.id)}
                        >
                            {todo.text}
                        </div>
                        <div className="icons">
                            <i
                                className="fas fa-times delete-icon"
                                onClick={() => removeTodo(todo.id)}
                            ></i>
                        </div>
                    </div>
                );
            })}
        </div>
    );
}

export default Todo;

  • 개별 todo를 보여주는 컴포넌트
  • 매개변수 {todos, completeTodo, deleteTodo}는 types.tsx에서 정의한 Todoprops 타입을 import해서 적용
    // 각 투두 항목 컴포넌트가 받는 props의 타입 정의 
    type TodoProps = {
       todos: TodoType[];
       completeTodo: (id: number) => void;
       removeTodo: (id: number) => void;
    };

💚TodoForm.tsx

import { useState, useEffect, useRef } from "react";
import { TodoFormProps } from "../types/types";

function TodoForm(props: TodoFormProps) {
    const [input, setInput] = useState<string>("");
    const [number, setNumber] = useState<number>(1);
    const inputRef = useRef<HTMLInputElement>(null);

    useEffect(() => {
        if (inputRef.current) {
            inputRef.current.focus();
        }
    });

    const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
        setInput(e.target.value);
    };

    const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
        e.preventDefault();

        setNumber(number + 1);

        props.onSubmit({
            id: number,
            text: input,
        });

        setInput("");
    };

    return (
        <form id="todoForm" className="todo-form" onSubmit={handleSubmit}>
            <input
                type="text"
                placeholder="Add a todo"
                value={input}
                name="text"
                className="todo-input"
                onChange={handleChange}
                ref={inputRef}
            />
            <button className="todo-button">Add todo</button>
        </form>
    );
}

export default TodoForm;

  • 새로 추가할 todo를 입력하는 form 컴포넌트
  • 매개변수 props의 타입은 types.tsx 파일에 정의한 TodoFormPropsimport 해서 적용

    type TodoFormProps = {
       onSubmit: (todo: TodoType) => void;
    };
    • 실제로 TodoForm 컴포넌트는 App.tsx에서 onSubmit={addTodo}를 전달인자로 받고 있다.
  • useState에 모두 타입을 지정해준다. inputstring, numbernumber 타입, useRef의 경우 <HTMLInputElement> 을 타입으로 지정해주면 된다.

  • handleChangehandleSubmit과 같은 이벤트 핸들러 함수의 경우 넘겨받는 매개변수의 타입을 e: React.ChangeEvent<HTMLInputElement>로 설정해주면 된다.

    • React.ChangeEvent<HTMLInputElement> 대신, 최상단에
      import { ChangeEvent, FormEvent } from "react"; 를 작성해주면
      e: ChangeEvent<HTMLInputElement> 와 같이 작성해도 된다.
profile
목표는 "함께 일하고 싶은, 함께 일해서 좋은" Front-end 개발자

0개의 댓글