소스 :
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)
댓글 남기기