소스 :
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;
요약
- NewTodo 컴포넌트는 사용자 입력을 받아 onAddTodo 함수를 호출하여 새로운 Todo 항목을 상위 컴포넌트로 전달합니다.
- App 컴포넌트는 상태를 사용하여 Todo 목록을 관리하며, addTodoHandler 함수를 통해 새로운 Todo 항목을 추가합니다.
- 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 함수로 전달합니다.
- 입력된 텍스트가 유효하지 않으면 함수를 종료합니다.
- 입력 필드를 초기화합니다.
요약
- 상태 관리: useState 훅을 사용하여 Todo 항목을 상태로 관리하고, 상태가 변경되면 컴포넌트가 다시 렌더링됩니다.
- 상위 컴포넌트와의 통신: NewTodo 컴포넌트는 사용자 입력을 받아 상위 컴포넌트로 전달하고, 상위 컴포넌트는 전달받은 데이터를 기반으로 상태를 업데이트합니다.
- 동적 렌더링: 상태가 변경되면 Todos 컴포넌트가 업데이트되어 새로운 Todo 항목이 화면에 반영됩니다.
이 과정을 통해 사용자가 입력한 Todo 항목을 동적으로 관리하고 화면에 반영할 수 있습니다.
568.연습하기:Todo 제거하기
이제 Todo를 클릭했을 때 해당 항목이 삭제되도록 구현해보겠습니다. 이를 위해 각 Todo 항목에 클릭 이벤트를 추가하고, 클릭 시 Todo가 삭제되도록 하겠습니다.
Step-by-Step Implementation
- TodoItem 컴포넌트에서 onClick 이벤트 추가
- Todos 컴포넌트에서 onRemoveTodo 함수 전달
- 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:
TodoItem.tsx:
- onClick 이벤트를 li 요소에 추가하여 항목이 클릭될 때 onRemoveTodo 함수가 호출되도록 했습니다.
- 버튼 클릭 시에도 Todo를 삭제하되, 이벤트 전파를 막기 위해 event.stopPropagation()을 사용했습니다.
Todos.tsx:
- TodoItem 컴포넌트에 onRemoveTodo 함수와 필요한 props를 전달합니다.
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"] }
주요 옵션 설명
target:
- target 옵션은 타입스크립트 코드를 어떤 자바스크립트 버전으로 컴파일할지 결정합니다. 예: es5, es6, es2015 등.
- 예: "target": "es5"
lib:
- 프로젝트에서 사용할 라이브러리를 정의합니다. 예: dom, es2015, esnext 등.
- 예: "lib": ["dom", "es2015"]
allowJs:
- 타입스크립트 프로젝트에 자바스크립트 파일을 포함할지 여부를 결정합니다.
- 예: "allowJs": true
strict:
- 엄격 모드 설정으로, 모든 엄격한 타입 체크 옵션을 활성화합니다.
- 예: "strict": true
jsx:
- JSX를 어떻게 처리할지 결정합니다. 예: preserve, react, react-native.
- 예: "jsx": "react"
module:
- 모듈 시스템을 설정합니다. 예: commonjs, esnext.
- 예: "module": "commonjs"
outDir:
- 컴파일된 자바스크립트 파일의 출력 디렉토리를 설정합니다.
- 예: "outDir": "./dist"
rootDir:
- 소스 파일의 루트 디렉토리를 설정합니다.
- 예: "rootDir": "./src"
esModuleInterop:
- ES 모듈 호환성을 위해 설정합니다.
- 예: "esModuleInterop": true"
forceConsistentCasingInFileNames:
- 파일 이름의 대소문자 일관성을 강제합니다.
- 예: "forceConsistentCasingInFileNames": true"
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 파일은 타입스크립트 프로젝트의 컴파일러 옵션과 설정을 정의합니다. 위에서 설명한 주요 옵션들을 통해 프로젝트의 동작 방식을 세부적으로 제어할 수 있습니다. 프로젝트 요구사항에 따라 적절한 옵션을 설정하여 타입스크립트를 효율적으로 사용할 수 있습니다.
댓글 ( 0)
댓글 남기기