2024년 10월 31일
클래스형 컴포넌트
리액트 초기 버전에서 주로 사용했음
예전에 구현 했던 레거시 시스템에 많이 사용했기 때문에 (지금도 여전히 일부에서는 사용) 알고 있어야함
// ClassCom.tsx
// 클래스형 컴포넌트 만들어보기
import { Componnet } from 'react';
class ClassCom extends Component{
render() {
return(
<div>
클래스형 컴포넌트
</div>
)
}
}
export default ClassCom;
함수형 컴포넌트
리액트 16버전 이후로 권장하는 컴포넌트 방식
state, lifecycle를 보완해서 최근에는 함수형 컴포넌트를 더 선호하고 잘 씀 → 가독성면에서 더 좋음
// FuncCom.tsx
import React from 'react';
function FuncCom() {
return(
<div>
함수형 컴포넌트
</div>
);
}
export default FuncCom;
함수형 컴포넌트는 sate와 lifecycle의 불편함이 있었는데, hook이라는 개념을 도입해 해결했음 → 현재 함수형 컴포넌트를 권장하고 있음
모던자바스크립트에서 화살표 함수를 이용해 함수형 컴포넌트를 만들 수도 있음
// FuncCom.tsx
import React from 'react';
const FuncCom = () => {
return(
<div>
함수형 컴포넌트
</div>
);
}
export default FuncCom;
// Todolist.tsx
import React from 'react';
const TodoList : React.FC = () => { // 리액트 컴포넌트의 타입 정의
// props의 타입을 명시하기 위해 사용
// const title = "오늘 할 일";
// title = 100;
// 문자열로 타입이 결정되었는데 숫자를 재할당하면 에러가 난다
// 따라서 타입을 적어줘야함
const title : string = "오늘 할 일"
// state 데이터의 상태를 나타냄
// 데이터가 변경되면 즉시 화면에 반영
// 일반 변수를 사용하면 상태 관리가 안되지만, state를 사용해 상태 관리할 수 이음
// useState() 훅을 사용해 state를 사용할 수 있음
// 그리고 todos가 여러 개이면 state를 하나씩 넣어주는 것은 불가능
// 따라서, 배열 사용해서 넣어준다 -> 배열을 사용할때 인덱스를 가져와서 나열해줌(대부분의 프로그래밍이 그럼)
const [todos] = useState(['공부하기', '잠자기', '놀기']);
return(
<div>
<h1>{title}</h1>
<p></p>
<div className='container'>
<ul>
<li>{todos[0]}</li>
<li>{todos[1]}</li>
<li>{todos[2]}</li>
</ul>
</div>
</div>
);
}
export default TodoList;
다량의 변수를 배열의 형태로 관리 할 수 있는 것을 알게 되었다.
그런데 state에 직접 접근해서 접근하는 것은 불가능하다 → 별도의 변경함수를 이용해서 바꿔야함
클래스 구성요소에는 멤버 변수, 멤버 함수가 있었다. 클래스의 캡슐화 철학을 기반으로 멤버 변수를 보호하고 멤버 함수로 멤버 변수를 접근하도록 만들었다. 이와 비슷하게 state도 외부에서 마음대로 바꾸지 못하고, setTodos(함수) 를 사용해 변경 할 수 있는 것과 같은 이치
// Todolist.tsx
import React, { useState } from 'react';
const TodoList : React.FC = () => {
const title : string = "오늘 할 일";
const [todos, setTodos] = useState<string[]>(['공부하기', '잠자기', '놀기']); // 각 데이터가 문자열이므로 타입을 추가함
// 원래는 우항의 데이터 전체(메모리 주소값)를 좌항에 주소값을 넘겨주는 형태로 사용 했는데,
// 이제 구조 분해 할당을 사용하면 그 안의 데이터들을 뽑아내서 바로 좌항에서 사용할 수 있음
/*
const person = {
name : 'lee',
age: 20,
cit : 'seoul'
}
const {name, age, city} = person;
console.log(name); // lee
console.log(age); // 20
console.log(city); // seoul
*/
return(
<div>
<h1>{title}</h1>
<p></p>
<div className='container'>
<div className='board'>
<ul>
<li>{todos[0]}</li>
<li>{todos[1]}</li>
<li>{todos[2]}</li>
</ul>
</div>
</div>
</div>
);
}
export default TodoList;
map을 통해 반복되는 데이터 보여주기
// MapTeste.tsx
import React, {} from 'react';
const MapTest = () => {
const fruits = ['apple', 'banana', 'orange'];
return(
<div>
<h2>과일</h2>
<ul>
{
fruits.map((fruit, idx)=>(<li key={idx}>{fruit}</li>))
}
</ul>
</div>
)
}
export default MapTest;
내가 타입을 만들어서 사용하기
// Todolist.tsx
import React, { useState } from 'react';
type Todo = {
id : number;
text : string;
isChecked : boolean;
};
// 인터페이스가 아닌 type을 사용한 이유
// 인터페이스는 주로 객체의 구조를 정의할때 사용
// type을 새로 지정하기 위해서는 type이 더 적합
// 그리고 데이터를 입력할 때 type 형식에 맞게 들어가야함
const TodoList : React.FC = () => {
const title : string = "오늘 할 일";
const [todos, setTodos] = useState<Todo[]>([
{id: 1, text: '공부하기', isChecked: false},
{id: 2, text: '잠자기', isChecked: false},
{id: 3, text: '놀기', isChecked: false}
]);
return(
<div>
<h1>{title}</h1>
<p></p>
<div className='container'>
<div className='board'>
<ul>
{
todos.map((todo, idx)=>(<li key={idx}>{todo.text}</li>))
}
</ul>
</div>
</div>
</div>
);
}
export default TodoList;
// Todolist.tsx
import React, { useState } from 'react';
type Todo = {
id : number;
text : string;
isChecked : boolean;
}
const TodoList : React.FC = () => {
const title : string = "오늘 할 일";
// 구조분해할당
const [todos, setTodos] = useState<Todo[]>([
{id: 1, text: '공부하기', isChecked: false},
{id: 2, text: '잠자기', isChecked: false},
{id: 3, text: '놀기', isChecked: false}
]);
const handleCheckedChange = (itemId : number) => {
setTodos((prevItems) =>
prevItems.map((item) => item.id === itemId ? {...item, isChecked : !item.isChecked} : item)
);
}
return(
<div>
<h1>{title}</h1>
<p></p>
<div className='container'>
<div className='board'>
<ul>
{
todos.map((todo, idx) => (
<li key={todo.id}>
<input type="checkbox" onChange={()=>{
handleCheckedChange(todo.id)
}}></input>
<span>
{
todo.isChecked ? <del>{todo.text}</del> : <span>{todo.text}</span>
}
</span>
</li>))
}
</ul>
</div>
</div>
</div>
);
}
export default TodoList;
부트스트랩 사용하기
코드 작성
// Todolist.tsx
import React, { useState } from 'react';
import { Button } from 'react-bootstrap';
type Todo = {
id : number;
text : string;
isChecked : boolean;
}
const TodoList : React.FC = () => {
const title : string = "오늘 할 일";
// todos
const [todos, setTodos] = useState<Todo[]>([
{id: 1, text: '공부하기', isChecked: false},
{id: 2, text: '잠자기', isChecked: false},
{id: 3, text: '놀기', isChecked: false}
]);
// newtodo
const [newTodo, setNewTodo] = useState<string>('');
const handleCheckedChange = (itemId : number) => {
setTodos((prevItems) =>
prevItems.map((item) => item.id === itemId ? {...item, isChecked : !item.isChecked} : item)
);
}
const addTodo = () => {
if(newTodo.trim() !== '') {
setTodos([...todos, {id : Date.now(), text : newTodo, isChecked : false}]);
setNewTodo('');
}
}
return(
<div>
<h1>{title}</h1>
<p></p>
<div className='container'>
<div>
<input type="text" placeholder='할 일 입력' style={{marginRight: '10px', writingMode : 'horizontal-tb'}} onChange={(e) => setNewTodo(e.target.value)}/>
<Button onClick={addTodo}>추가</Button>
</div>
<p></p>
<div className='board'>
<ul>
{
todos.map((todo, idx) => (
<li key={todo.id}>
<input type="checkbox" onChange={()=>{
handleCheckedChange(todo.id)
}}></input>
<span>
{
todo.isChecked ? <del>{todo.text}</del> : <span>{todo.text}</span>
}
</span>
</li>))
}
</ul>
</div>
</div>
</div>
);
}
export default TodoList;
스프레드 연산자 : 기존의 배열을 변경하지 않고, 새로운 요소를 이용해서 변경할 때 사용
원본은 유지하되, 새로운 것을 적용시켜줌
똑같은 내용이지만 주소값 다름
let a = [1, 2, 3];
let b = a;
console.log(a === b); // true -> 얕은 복사
let c = [...a];
console.log(a === c); // false -> 깊은 복사
타이머 컴포넌트 만들기
// Timer.tsx
import React, { useState } from 'react';
const Timer : React.FC = () => {
const [seconds, setSeconds] = useState<number>(0);
return(
<div>
<h2>타이머 : {seconds}초</h2>
<button onClick={()=>{
setInterval(()=>{
setSeconds(prev => prev + 1);
}, 1000) // 1초마다 타이머 동작
}}>시작</button>
</div>
);
}
export default Timer;
시계 컴포넌트 만들기
// Clock.tsx
import React, { useState } from 'react';
const Clock : React.FC = () => {
const [time, setTime] = useState(new Date());
setInterval(()=>{
setTime(new Date());
}, 1000);
return(
<div>
현재 시간 : {time.toLocaleTimeString()}
</div>
);
}
export default Clock;
// Todolist.tsx
import React, { useState } from 'react';
import { Button } from 'react-bootstrap';
type Todo = {
id : number;
text : string;
isChecked : boolean;
}
const TodoList : React.FC = () => {
const title : string = "오늘 할 일";
// todos
const [todos, setTodos] = useState<Todo[]>([
{id: 1, text: '공부하기', isChecked: false},
{id: 2, text: '잠자기', isChecked: false},
{id: 3, text: '놀기', isChecked: false}
]);
// newtodo
const [newTodo, setNewTodo] = useState<string>('');
const handleCheckedChange = (itemId : number) => {
setTodos((prevItems) =>
prevItems.map((item) => item.id === itemId ? {...item, isChecked : !item.isChecked} : item)
);
}
const addTodo = () => {
if(newTodo.trim() !== '') {
setTodos([...todos, {id : Date.now(), text : newTodo, isChecked : false}]);
setNewTodo('');
}
}
const deleteTodo = (id : number) => {
setTodos(todos.filter((todo)=> todo.id !== id));
}
return(
<div>
<h1>{title}</h1>
<p></p>
<div className='container'>
<div>
<input type="text" placeholder='할 일 입력' style={{marginRight: '10px', writingMode : 'horizontal-tb'}} onChange={(e) => setNewTodo(e.target.value)}/>
<Button onClick={addTodo}>추가</Button>
</div>
<p></p>
<div className='board'>
<ul>
{
todos.map((todo, idx) => (
<li key={todo.id}>
<input type="checkbox" onChange={()=>{
handleCheckedChange(todo.id)
}}></input>
<span>
{
todo.isChecked ? <del>{todo.text}</del> : <span>{todo.text}</span>
}
</span>
<button onClick={() => deleteTodo(todo.id)} className='delbutton'>삭제</button>
</li>))
}
</ul>
</div>
</div>
</div>
);
}
export default TodoList;