React

 

 

소스 :

https://github.dev/braverokmc79/react-ts-ex30

 

563.TypeScript 프로젝트의 양식 제출

 

타입스크립트는 이벤트 타입을 명확하게 지정해줍니다. 예를 들어, submitHandler 함수에서 이벤트 타입을

React.FormEvent로 지정했습니다. 이는 폼 제출 이벤트를 처리할 때 필요한 타입입니다.

 

NewTodo.tsx

import React from "react";

const NewTodo =()=>{
    const submitHandler =(event:React.FormEvent)=>{
        event.preventDefault();

    }

    return(
        <form onSubmit={submitHandler}>
            <label htmlFor="text">Todo text</label>
            <input type="text"  id="text" />
            <button >추가</button>
        </form>
    );
}


export default NewTodo;

 

이렇게 하면, event.preventDefault()를 호출할 때 자동 완성 기능을 사용할 수 있으며, 타입스크립트가 올바른 타입을 추론하여 코드의 안정성을 높일 수 있습니다.

 

 

 

 

 

 

564.refs 및 useRef 작업하

 

레퍼런스 생성:

const todoTextInputRef=useRef<HTMLInputElement>(null);

 

todoTextInputRef라는 레퍼런스를 생성합니다. 이 레퍼런스는 HTML <input> 요소와 연결되어, 입력 필드의 값을 직접 참조할 수 있게 합니다.

초기 값은 null로 설정됩니다.

 

입력된 텍스트 가져오기:

const enteredText=todoTextInputRef.current!.value;

todoTextInputRef를 통해 입력 필드의 현재 값을 가져옵니다. current 프로퍼티는 레퍼런스가 가리키는 DOM 요소를 참조합니다. 여기서 느낌표(!)는 TypeScript의

non-null assertion operator로, 이 시점에서는 current가 null이 아님을 보장합니다.

 

입력 검증:

if(enteredText.trim().length===0){
    //throw an errr 
    return;
}

입력된 텍스트가 공백을 제외하고도 빈 문자열이면, 에러를 발생시키거나(주석 처리된 부분) 함수 실행을 중단하고 반환합니다.

이 부분은 사용자가 빈 텍스트를 제출하는 것을 방지합니다.

 

NewTodo.tsx

import {useRef} from 'react';



const NewTodo =()=>{
    const todoTextInputRef=useRef<HTMLInputElement>(null);

    const submitHandler =(event:React.FormEvent)=>{
        event.preventDefault();

        const enteredText=todoTextInputRef.current!.value;

        if(enteredText.trim().length===0){
            //throw an errr 
            return;
        }

        
    }



    return(
        <form onSubmit={submitHandler}>
            <label htmlFor="text">Todo text</label>
            <input type="text"  id="text" ref={todoTextInputRef}   />
            <button >추가</button>
        </form>
    );
}


export default NewTodo;

 

 

 

 

 

 

 

 

565."Function Props"로 작업하기

 

1. NewTodo 컴포넌트 정의

NewTodo 컴포넌트는 사용자가 새로운 Todo 항목을 입력할 수 있도록 폼을 제공합니다. 이 컴포넌트는 onAddTodo 함수를 props로 받아 폼 제출 시 호출합니다.

NewTodo.tsx

import { useRef } from 'react';

const NewTodo: React.FC<{ onAddTodo: (text: string) => void }> = (props) => {
  const todoTextInputRef = useRef<HTMLInputElement>(null);

  const submitHandler = (event: React.FormEvent) => {
    event.preventDefault();
    const enteredText = todoTextInputRef.current!.value;

    if (enteredText.trim().length === 0) {
      // 입력 값이 없을 경우 처리
      return;
    }

    props.onAddTodo(enteredText);
    todoTextInputRef.current!.value = ''; // 입력 필드 초기화
  };

  return (
    <form onSubmit={submitHandler}>
      <label htmlFor="text">Todo text</label>
      <input type="text" id="text" ref={todoTextInputRef} />
      <button type="submit">추가</button>
    </form>
  );
};

export default NewTodo;

 

2. App 컴포넌트 수정

App 컴포넌트는 Todo 항목을 상태로 관리하며, NewTodo 컴포넌트로부터 새로운 Todo 항목을 받아 상태를 업데이트합니다.

App.tsx

 

import React, { useState } from 'react';
import Todos from './components/Todos';
import NewTodo from './components/NewTodo';
import Todo from './models/todo';

function App() {
  const [todos, setTodos] = useState<Todo[]>([
    new Todo('Learn React'),
    new Todo('Learn TypeScript'),
  ]);

  const addTodoHandler = (todoText: string) => {
    const newTodo = new Todo(todoText);
    setTodos((prevTodos) => prevTodos.concat(newTodo));
  };

  return (
    <div>
      <NewTodo onAddTodo={addTodoHandler} />
      <Todos items={todos} />
    </div>
  );
}

export default App;

 

 

요약

  1. NewTodo 컴포넌트는 사용자 입력을 받아 onAddTodo 함수를 호출하여 새로운 Todo 항목을 상위 컴포넌트로 전달합니다.
  2. App 컴포넌트는 상태를 사용하여 Todo 목록을 관리하며, addTodoHandler 함수를 통해 새로운 Todo 항목을 추가합니다.
  3. App 컴포넌트는 NewTodo와 Todos 컴포넌트를 포함하여 새로운 Todo 항목을 추가하고 이를 화면에 렌더링합니다.

 

이 과정을 통해 Todo 항목을 동적으로 관리하고 화면에 반영할 수 있습니다.

 

 

 

 

 

 

566.State 및 TypeScript 관리하기

 

1. App 컴포넌트 수정

App 컴포넌트는 useState를 사용하여 Todo 항목을 상태로 관리합니다. 상태가 변경되면 컴포넌트가 다시 렌더링됩니다.

import { useState } from 'react';
import NewTodo from './components/NewTodo';
import Todos from './components/Todos';
import Todo from './models/todo';

function App() {
  const [todos, setTodos] = useState<Todo[]>([]); // 빈 배열을 초기 상태로 설정

  const addTodoHandler = (todoText: string) => {
    const newTodo = new Todo(todoText);

    setTodos((prevTodos) => {
      return prevTodos.concat(newTodo);
    });
  };

  return (
    <div>
      <NewTodo onAddTodo={addTodoHandler} />
      <Todos items={todos} />
    </div>
  );
}

export default App;

 

요약:

  • useState 훅을 사용하여 todos 상태를 관리합니다.
  • addTodoHandler 함수는 새로운 Todo 항목을 생성하고 기존의 Todo 목록에 추가합니다.
  • setTodos 함수는 이전 상태를 기반으로 새로운 상태를 설정합니다.

 

2. NewTodo 컴포넌트 정의

NewTodo 컴포넌트는 사용자로부터 새로운 Todo 항목을 입력받아 상위 컴포넌트로 전달합니다.

NewTodo.tsx

 

import { useRef } from 'react';

const NewTodo: React.FC<{ onAddTodo: (text: string) => void }> = (props) => {
  const todoTextInputRef = useRef<HTMLInputElement>(null);

  const submitHandler = (event: React.FormEvent) => {
    event.preventDefault();

    const enteredText = todoTextInputRef.current!.value;

    if (enteredText.trim().length === 0) {
      // 입력 값이 없을 경우 처리
      return;
    }

    props.onAddTodo(enteredText);
    todoTextInputRef.current!.value = ''; // 입력 필드 초기화
  };

  return (
    <form onSubmit={submitHandler}>
      <label htmlFor="text">Todo text</label>
      <input type="text" id="text" ref={todoTextInputRef} />
      <button type="submit">추가</button>
    </form>
  );
};

export default NewTodo;

 

요약:

  • useRef 훅을 사용하여 입력 필드의 참조를 가져옵니다.
  • 폼 제출 시 submitHandler 함수가 호출되어 입력된 텍스트를 onAddTodo 함수로 전달합니다.
  • 입력된 텍스트가 유효하지 않으면 함수를 종료합니다.
  • 입력 필드를 초기화합니다.

 

요약

  1. 상태 관리: useState 훅을 사용하여 Todo 항목을 상태로 관리하고, 상태가 변경되면 컴포넌트가 다시 렌더링됩니다.
  2. 상위 컴포넌트와의 통신: NewTodo 컴포넌트는 사용자 입력을 받아 상위 컴포넌트로 전달하고, 상위 컴포넌트는 전달받은 데이터를 기반으로 상태를 업데이트합니다.
  3. 동적 렌더링: 상태가 변경되면 Todos 컴포넌트가 업데이트되어 새로운 Todo 항목이 화면에 반영됩니다.

이 과정을 통해 사용자가 입력한 Todo 항목을 동적으로 관리하고 화면에 반영할 수 있습니다.

 

 

 

 

 

 

 

 

 

568.연습하기:Todo  제거하기

이제 Todo를 클릭했을 때 해당 항목이 삭제되도록 구현해보겠습니다. 이를 위해 각 Todo 항목에 클릭 이벤트를 추가하고, 클릭 시 Todo가 삭제되도록 하겠습니다.

Step-by-Step Implementation

  1. TodoItem 컴포넌트에서 onClick 이벤트 추가
  2. Todos 컴포넌트에서 onRemoveTodo 함수 전달
  3. App 컴포넌트에서 removeTodoHandler 함수 구현

 

1. TodoItem 컴포넌트에서 onClick 이벤트 추가

import React from "react";
import classes from "./TodoItem.module.css";

interface TodoItemProps {
  text: string;
  id: string;
  onRemoveTodo: (id: string) => void;
}

const TodoItem: React.FC<TodoItemProps> = (props) => {
  
  const onRemoveTodo = () => {
    const cf = window.confirm("정말 삭제 하시겠습니까?");
    if (cf) {
      props.onRemoveTodo(props.id);
    }
  };

  return (
    <li className={classes.item} onClick={onRemoveTodo}>
      {props.text}
      <button onClick={(event) => {
        event.stopPropagation();
        onRemoveTodo();
      }}>삭제</button>
    </li>
  );
};

export default TodoItem;

 

 

2. Todos 컴포넌트에서 onRemoveTodo 함수 전달

import React from "react";
import Todo from "../models/todo";
import TodoItem from "./TodoItem";
import classes from "./Todos.module.css";

interface TodosProps {
  items: Todo[];
  onRemoveTodo: (id: string) => void;
}

const Todos: React.FC<TodosProps> = (props) => {
  return (
    <ul className={classes.todos}>
      {props.items.map((item) => (
        <TodoItem 
          key={item.id}
          id={item.id}
          text={item.text}
          onRemoveTodo={props.onRemoveTodo} 
        />
      ))}
    </ul>
  );
};

export default Todos;

 

 

3. App 컴포넌트에서 removeTodoHandler 함수 구현

import React, { useState } from 'react';
import NewTodo from './components/NewTodo';
import Todos from './components/Todos';
import Todo from './models/todo';

function App() {
  const [todos, setTodos] = useState<Todo[]>([]);

  const addTodoHandler = (todoText: string) => {
    const newTodo = new Todo(todoText);
    setTodos((prevTodos) => prevTodos.concat(newTodo));
  };

  const removeTodoHandler = (todoId: string) => {
    setTodos((prevTodos) => prevTodos.filter(todo => todo.id !== todoId));
  };

  return (
    <div>
      <NewTodo onAddTodo={addTodoHandler} />
      <Todos items={todos} onRemoveTodo={removeTodoHandler} />
    </div>
  );
}

export default App;

 

Explanation:

  1. TodoItem.tsx:

    • onClick 이벤트를 li 요소에 추가하여 항목이 클릭될 때 onRemoveTodo 함수가 호출되도록 했습니다.
    • 버튼 클릭 시에도 Todo를 삭제하되, 이벤트 전파를 막기 위해 event.stopPropagation()을 사용했습니다.
  2. Todos.tsx:

    • TodoItem 컴포넌트에 onRemoveTodo 함수와 필요한 props를 전달합니다.
  3. App.tsx:

    • removeTodoHandler 함수는 Todo의 id를 받아서 해당 Todo를 todos 상태에서 제거합니다.
    • Todos 컴포넌트에 removeTodoHandler를 onRemoveTodo props로 전달합니다.

 

 

이제 Todo 항목을 클릭하거나 버튼을 클릭하면 해당 항목이 삭제되도록 구현되었습니다.

 

 

 

 

 

 

 

 

569.컨텍스트 API 및 TypeScript

 

타입스크립트와 컨텍스트 API를 이용해 Todo 리스트를 관리하는 방법을 단계별로 설명드리겠습니다.

이 예제에서는 Todo를 추가하고, 클릭하여 삭제하는 기능을 구현합니다. 컨텍스트 API를 통해 상태 관리를 수행하고, 이를 컴포넌트에 제공하여 효율적인 상태 관리를 도모합니다.

 

1. store 폴더 생성 및 todos-context.tsx 파일 추가

src
└── store
    └── todos-context.tsx

 

2. todos-context.tsx 파일 작성

2.1 TodosContext 생성

import React, { useState } from 'react';
import Todo from '../models/todo';

type TodosContextObj = {
  items: Todo[];
  addTodo: (todoText: string) => void;
  removeTodo: (id: string) => void;
};

export const TodosContext = React.createContext<TodosContextObj>({
  items: [],
  addTodo: (todoText: string) => {},
  removeTodo: (id: string) => {},
});

const TodosContextProvider: React.FC<{children?:React.ReactNode}> = (props) => {
  const [todos, setTodos] = useState<Todo[]>([]);

  const addTodoHandler = (todoText: string) => {
    const newTodo = new Todo(todoText);
    setTodos((prevTodos) => prevTodos.concat(newTodo));
  };

  const removeTodoHandler = (todoId: string) => {
    setTodos((prevTodos) => prevTodos.filter((todo) => todo.id !== todoId));
  };

  const contextValue: TodosContextObj = {
    items: todos,
    addTodo: addTodoHandler,
    removeTodo: removeTodoHandler,
  };

  return (
    <TodosContext.Provider value={contextValue}>
      {props.children}
    </TodosContext.Provider>
  );
};

export default TodosContextProvider;

 

 

3. App.tsx 파일 수정

3.1 TodosContextProvider 적용

import NewTodo from './components/NewTodo';
import Todos from './components/Todos';
import TodosContextProvider from './store/todos-context';

function App() {
  return (
    <TodosContextProvider>
      <NewTodo  />
      <Todos   /> 
    </TodosContextProvider>
  );
}

export default App;

 

4. Todos.tsx 파일 수정

4.1 컨텍스트 사용

import React, { useContext } from "react";
import TodoItem from "./TodoItem";
import classes from "./Todos.module.css";
import { TodosContext } from "../store/todos-context";

const Todos: React.FC = () => {
  const todosCtx = useContext(TodosContext);

  return (
    <ul className={classes.todos}>
      {todosCtx.items.map((item) => (
        <TodoItem
          key={item.id}
          text={item.text}
          id={item.id}
          onRemoveTodo={todosCtx.removeTodo.bind(null, item.id)}
        />
      ))}
    </ul>
  );
};

export default Todos;

 

5. TodoItem.tsx 파일 수정

5.1 onRemoveTodo 프로퍼티 추가

import React from "react";
import classes from "./TodoItem.module.css";

interface TodoItemProps {
  text: string;
  id: string;
  onRemoveTodo: () => void;
}

const TodoItem: React.FC<TodoItemProps> = (props) => {
  return (
    <li className={classes.item} onClick={props.onRemoveTodo}>
      {props.text}
    </li>
  );
};

export default TodoItem;

 

 

6. NewTodo.tsx 파일 수정

6.1 컨텍스트 사용

import { useContext, useRef } from "react";
import classes from "./NewTodo.module.css";
import { TodosContext } from "../store/todos-context";

const NewTodo: React.FC = () => {
  const todoTextInputRef = useRef<HTMLInputElement>(null);
  const todosCtx = useContext(TodosContext);

  const submitHandler = (event: React.FormEvent) => {
    event.preventDefault();

    const enteredText = todoTextInputRef.current!.value;

    if (enteredText.trim().length === 0) {
      //throw an errr
      return;
    }

    todosCtx.addTodo(enteredText);
  };

  return (
    <form onSubmit={submitHandler} className={classes.form}>
      <label htmlFor="text">Todo text</label>
      <input type="text" id="text" ref={todoTextInputRef} />
      <button>추가</button>
    </form>
  );
};

export default NewTodo;

 

 

위 단계를 통해 Todo 리스트를 관리하는 컨텍스트 API와 타입스크립트를 사용하여 효율적으로 상태를 관리할 수 있습니다.

이 과정에서는 TodosContextProvider를 사용해 상태를 관리하고, 각 컴포넌트에서 컨텍스트를 통해 상태에 접근 및 조작할 수 있도록 했습니다.

 

 

 

 

 

 

 

571.보너스:tsconfig.json 탐색

tsconfig.json 파일은 타입스크립트 프로젝트의 핵심 구성 파일로, 컴파일러 옵션과 프로젝트 설정을 정의합니다. 이를 통해 타입스크립트 코드를 자바스크립트로 컴파일하는 과정을 세부적으로 제어할 수 있습니다. 주요 설정 옵션을 설명하고, 예제를 통해 어떻게 적용되는지 살펴보겠습니다.

기본 tsconfig.json 구조

아래는 기본적인 tsconfig.json 파일의 예제입니다:

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "es2015"],
    "allowJs": true,
    "strict": true,
    "jsx": "react",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

 

주요 옵션 설명

  1. target:

    • target 옵션은 타입스크립트 코드를 어떤 자바스크립트 버전으로 컴파일할지 결정합니다. 예: es5, es6, es2015 등.
    • 예: "target": "es5"
  2. lib:

    • 프로젝트에서 사용할 라이브러리를 정의합니다. 예: dom, es2015, esnext 등.
    • 예: "lib": ["dom", "es2015"]
  3. allowJs:

    • 타입스크립트 프로젝트에 자바스크립트 파일을 포함할지 여부를 결정합니다.
    • 예: "allowJs": true
  4. strict:

    • 엄격 모드 설정으로, 모든 엄격한 타입 체크 옵션을 활성화합니다.
    • 예: "strict": true
  5. jsx:

    • JSX를 어떻게 처리할지 결정합니다. 예: preserve, react, react-native.
    • 예: "jsx": "react"
  6. module:

    • 모듈 시스템을 설정합니다. 예: commonjs, esnext.
    • 예: "module": "commonjs"
  7. outDir:

    • 컴파일된 자바스크립트 파일의 출력 디렉토리를 설정합니다.
    • 예: "outDir": "./dist"
  8. rootDir:

    • 소스 파일의 루트 디렉토리를 설정합니다.
    • 예: "rootDir": "./src"
  9. esModuleInterop:

    • ES 모듈 호환성을 위해 설정합니다.
    • 예: "esModuleInterop": true"
  10. forceConsistentCasingInFileNames:

    • 파일 이름의 대소문자 일관성을 강제합니다.
    • 예: "forceConsistentCasingInFileNames": true"
  11. skipLibCheck:

    • 라이브러리 파일의 타입 검사를 건너뜁니다.
    • 예: "skipLibCheck": true"

예제: NewTodo.tsx 파일과 tsconfig.json 연계

tsconfig.json 파일에 포함된 라이브러리가 실제로 어떻게 적용되는지 예제를 통해 살펴보겠습니다.

 

// NewTodo.tsx
import React, { useRef, useContext } from 'react';
import { TodosContext } from '../store/todos-context';

const NewTodo: React.FC = () => {
  const todosCtx = useContext(TodosContext);
  const todoInputRef = useRef<HTMLInputElement>(null);

  const submitHandler = (event: React.FormEvent) => {
    event.preventDefault();
    const enteredText = todoInputRef.current!.value;
    if (enteredText.trim().length === 0) {
      return;
    }
    todosCtx.addTodo(enteredText);
  };

  return (
    <form onSubmit={submitHandler}>
      <label htmlFor="todo-text">Todo Text</label>
      <input type="text" id="todo-text" ref={todoInputRef} />
      <button type="submit">Add Todo</button>
    </form>
  );
};

export default NewTodo;

 

 

위 코드에서 HTMLInputElement 타입은 tsconfig.json 파일의 lib 옵션에 dom이 포함되어 있기 때문에 사용할 수 있습니다. 만약 dom 라이브러리가 제외된다면, 타입스크립트는 이 타입을 인식하지 못하고 오류를 발생시킬 것입니다.

 

결론

tsconfig.json 파일은 타입스크립트 프로젝트의 컴파일러 옵션과 설정을 정의합니다. 위에서 설명한 주요 옵션들을 통해 프로젝트의 동작 방식을 세부적으로 제어할 수 있습니다. 프로젝트 요구사항에 따라 적절한 옵션을 설정하여 타입스크립트를 효율적으로 사용할 수 있습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

In the multitude of counselors there is wisdom. (모사가 많으면 평안을 누리느니라.)

댓글 ( 0)

댓글 남기기

작성