- React.js
- 새로운 프로젝트를 만들어보겠다.
C:\data\react>yarn create react-app ex02
- 프로젝트 폴더로 이동
C:\data\react\ex02
- 프로젝트 시작
C:\data\react\ex02\yarn start
- C:\data\react\ex02\public\index.html -> html 시작 부분
- C:\data\react\ex02\src\index.js -> root 연결부 / App component render 연결부
- C:\data\react\ex02\src\App.css -> 웹 CSS부분
- C:\data\react\ex02\src\App.js -> 웹 선언부분
import './App.css';
import Foods from './component/Foods';
// 함수형 컴포넌트(화살표형으로 전환)
const App = () => {
return (
<div className="App">
<Foods/>
</div>
);
}
export default App;
- C:\data\react\ex02\src\(new)component
- C:\data\react\ex02\src\component\(new)Foods.js
const Foods = () =>{
return(
<div>
<h1>음식목록</h1>
</div>
)
}
export default Foods;
- 음식목록에 들어갈 배열을 만들어 출력해보자.
- C:\data\react\ex02\src\component\Foods.js
import { useState } from "react";
const Foods = () =>{
// 변수 변경은 setfoods로
const [foods, setfoods] = useState([
{id:1,name:'족발'},
{id:2,name:'보쌈'},
{id:3,name:'탕슉'},
{id:4,name:'수육'}
]);
return(
<div>
<h1>음식목록</h1>
<div>
{foods.map((food)=><h3 key={food.id}>{food.name}</h3>)}
</div>
</div>
)
}
export default Foods;
- input tag로 배열에 추가해보자.
- C:\data\react\ex02\src\component\Foods.js
import { useState } from "react";
const Foods = () =>{
// 변수 변경은 setfoods로
const [foods, setfoods] = useState([
{id:1,name:'족발'},
{id:2,name:'보쌈'},
{id:3,name:'탕슉'},
{id:4,name:'수육'}
]);
const [name,setName] = useState('만두');
const [nextId, setNextId] = useState(5);
const onClickAdd = () =>{
// concat 연결하겠다.
const newFoods = foods.concat({
id: nextId, name: name
});
setfoods(newFoods);
setNextId(nextId+1);
setName('');
}
const onKeyPressName = (e) =>{
if(e.key==='Enter'){
onClickAdd();
}
}
return(
<div>
<h1>음식목록</h1>
{/* onchage할때마다 setName한다. */}
<input type="text" value={name} onChange={(e)=>setName(e.target.value)}
onKeyPress={onKeyPressName}/>
<button onClick={onClickAdd}>추가</button>
{name}
<div>
{foods.map((food)=><h3 key={food.id}>{food.id}:{food.name}</h3>)}
</div>
</div>
)
}
export default Foods;
- <h3> 더블클릭하면 삭제하는 메서드를 만들어보도록 하겠다.
const onRemove = (id,name) => {
if(!window.confirm(`${name}을(를) 삭제하시겠습니까?`)) return;
const newFoods = foods.filter(food => food.id !== id);
setfoods(newFoods);
}
<div>
{foods.map((food)=><h3 key={food.id}
onDoubleClick={()=>onRemove(food.id, food.name)}>{food.id}:{food.name}</h3>)}
</div>
- 이름, 전화번호, 주소 입력받아 추가하는 컴포넌트를 만들어보겠다.
- C:\data\react\ex02\src\component\(new)Address.js -> parent
import React, { useState } from 'react';
import Person from './Person';
const Address = () => {
const [persons, setPersons] = useState([
{id:1,name:'김철수',tel:'032-123-4567',address:'인천시 미추홀구 학익동'},
{id:2,name:'이영희',tel:'032-890-1234',address:'인천시 부평구 청천동'},
{id:3,name:'박대준',tel:'032-567-8901',address:'인천시 중구 신포동'},
])
return (
<div>
<h1>주소록</h1>
<table>
{persons.map((p)=><Person key={p.id} id={p.id} name={p.name} tel={p.tel} address={p.address}/>)}
</table>
</div>
);
};
export default Address;
- C:\data\react\ex02\src\component\(new)person.js -> child
const Person = (props) => {
const{id,name,tel,address} = props;
return (
<tr>
<td><h3>{id}</h3></td>
<td><h3>{name}</h3></td>
<td><h3>{tel}</h3></td>
<td><h3>{address}</h3></td>
</tr>
);
};
export default Person;
- 입력추가하는 input tag를 만들고 값을 받아보겠다.
- C:\data\react\ex02\src\component\Address.js
import React, { useState } from 'react';
import Person from './Person';
const Address = () => {
// 변수 배열
const [persons, setPersons] = useState([
{id:1,name:'김철수',tel:'032-123-4567',address:'인천시 미추홀구 학익동'},
{id:2,name:'이영희',tel:'032-890-1234',address:'인천시 부평구 청천동'},
{id:3,name:'박대준',tel:'032-567-8901',address:'인천시 중구 신포동'},
])
// 변수 오브젝트
const [form, setForm] = useState({
name:'이주성',
tel:'032-870-7720',
address:'인천시 서구 검암동'
})
const {name,tel,address} = form;
const onChange = (e) =>{
const newForm ={
...form,
[e.target.name]:e.target.value
}
setForm(newForm);
}
return (
<div>
<h1>주소록</h1>
<div className="divInput">
<input type="text" name="name" value={name} onChange={onChange}/><br/>
<input type="text" name="tel" value={tel} onChange={onChange}/><br/>
<input type="text" name="address" value={address} onChange={onChange} size={50}/><br/>
<button>주소추가</button>
</div>
<table>
{persons.map((p)=><Person key={p.id} id={p.id} name={p.name} tel={p.tel} address={p.address}/>)}
</table>
</div>
);
};
export default Address;
const [nextId,setNextId] = useState(4);
const onInsert = () =>{
if(name===''){
alert("이름을 입력하세요.")
return;
}
if(!window.confirm("내용을 추가하시겠습니까?")) return;
const newPersons=persons.concat({
...persons,
id:nextId,
name:name,
tel:tel,
address:address
});
setPersons(newPersons);
setNextId(nextId+1);
setForm({
name:'',
tel:'',
address:''
});
}
return (
...
<button onClick={onInsert}>주소추가</button>
...
);
- 삭제 버튼을 추가하겠다.
- 값뿐만아니라 메서드나 이벤트도 넘겨줄 수 있다.
C:\data\react\ex02\src\component\Person.js
const Person = (props) => {
const{id,name,tel,address,onRemove} = props;
return (
<tr>
...
<button onClick={onRemove}>삭제</button>
</tr>
);
};
export default Person;
- C:\data\react\ex02\src\component\Address.js
import React, { useState } from 'react';
import Person from './Person';
const Address = () => {
...
const onRemove =(id) =>{
if(!window.confirm(`${id}을(를) 삭제하시겠습니까?`)) return;
const newPersons = persons.filter(p => p.id !== id);
setPersons(newPersons);
}
return (
<div>
<h1>주소록</h1>
<div className="divInput">
...
</div>
<table>
{persons.map((p)=><Person key={p.id} id={p.id} name={p.name} tel={p.tel} address={p.address}
onRemove={()=>onRemove(p.id)}/>)}
</table>
</div>
);
};
export default Address;
- 컴포넌트의 라이프사이클 메서드
- 마운트: DOM이 생성되고 웹 브라우저상에 나타나는 것을 마운트라고 한다. 이때 호출되는 메서드는 다음과 같다. 1) constructor: 컴포넌트를 새로 만들 때마다 호출되는 클래스 생성자 메서드이다.
2) render: 우리가 준비한 UI를 렌더링하는 메서드이다.
3) componentDidMount: 컴포넌트가 웹 브라우저상에 나타난 후 호출하는 메서드이다. - 업데이트: pops가 바뀔 때, state가 바뀔 때, 부모 컴포넌트가 리렌더링될 때 다음 메서드를 호출한다.
1) shouldComponentUpdate: props 또는 state를 변경했을 때 컴포넌트가 리렌더링을 해야 할지 말아야 할지를 결정하는 메서드이다.
2) render: 컴포넌트를 리렌더링 한다.
3) componentDidUpdate: 컴포넌트의 업데이트 작업이 끝난 후 호출하는 메서드이다. - 언마운트: 마운트의 반대 과정, 즉 컴포넌트를 DOM에서 제거하는 것을 언마운트라고 한다. componentWillUnmount: 컴포넌트가 웹 브라우저상에서 사라지기 전에 호출하는 메서드이다.
- class 함수를 이용해 jsonplaceholder의 todos 데이터를 가지고와 특정 아이디를 가지는 데이터를 불러와보도록 하겠다.
https://jsonplaceholder.typicode.com/todos
- C:\data\react\ex02\src\component\(new)Api.js
import React, { Component } from 'react';
class Api extends Component {
// 생성자에 변수 정의
constructor(props){
super(props);
this.state ={
data:''
}
}
// 데이터 불러와 변수에 넣기
callAPI = ()=>{
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(res => res.json())
.then(json => {
this.setState({
data:json.title
})
});
}
// render 함수가 실행된 후 싱행
componentDidMount(){
this.callAPI();
}
render() {
return (
<div>
<h1>오늘의 할일</h1>
<hr/>
<h3>{this.state.data ? this.state.data:'데이터를 불러오는 중입니다.'}</h3>
</div>
);
}
}
export default Api;
- 함수형을 이용해 위와 마찬가지로 데이터를 불러와 출력해보도록 하겠다. 다만 검색란을 이용해 특정 데이터를 웹에서 입력하여 찾을 수 있도록 하겠다.
- C:\data\react\ex02\src\component\(new)Api1.js
import { useEffect, useState } from "react";
const Api1 = () => {
const [data,setData] = useState('');
const [number,setNumber] = useState(1);
const callAPI = ()=>{
fetch(`https://jsonplaceholder.typicode.com/todos/${number}`)
.then(res => res.json())
.then(json => {
setData(json.title);
});
}
useEffect(()=>{
callAPI();
},[number]);
return(
<div>
<h1>오늘의 할일(함수형)</h1>
<input type="text" value={number} size={3} onChange={(e)=>setNumber(e.target.value)}/>
<button>검색</button>
<h3>{data ? data:'데이터를 불러오는 중입니다.'}</h3>
</div>
)
};
export default Api1;
- 컴포넌트 스타일링
리엑트에서 DOM 요소에 스타일을 적용할 때는 문자열 형태로 넣는 것이 아니라 객체 형태로 넣어 주어야 한다. 스타일 이름 중에 - 문자가 포함되 는 있으면 - 문자를 없애고 카멜 표기법으로 작성해야 한다. 따라서 background-color는 backgroundColor로 작성한다.
- style 다이렉트로 표현하는 방법 2가지
- C:\data\react\ex02\src\component\Hello.js
const Hello = () => {
return(
//direct CSS속성은 {{}}에 넣어야 하고 오브젝트 속성이라
//아래와 같이 한다.
//padding-top을 paddingTop로 camelCase로 줘야한다. 이와 같이 조금씩 다르다.
<div>
<h1 style={{background:'black', color:'white',paddingTop:'50px', paddingBottom:'50px'}}>Hello World!</h1>
</div>
)
}
export default Hello;
or
const Hello = () => {
const style= {background:'black', color:'white',paddingTop:'50px', paddingBottom:'50px'}
return(
//direct CSS속성은 {{}}에 넣어야 하고 오브젝트 속성이라
//아래와 같이 한다.
//padding-top을 paddingTop로 camelCase로 줘야한다. 이와 같이 조금씩 다르다.
<div>
<h1 style={style}>Hello World!</h1>
</div>
)
}
export default Hello;
- 일정관리 웹 애플리케이션 만들기
- TodoTemplate
- C:\data\react\ex02\src\component\(new)TodoTemplate.js
import React from 'react';
const TodoTemplate = ({children}) => {
return (
<div className="TodoTemplate">
<div className="app-title">일정관리</div>
<div className="content">{children}</div>
</div>
);
};
export default TodoTemplate;
- C:\data\react\ex02\src\App.js
import './App.css';
import TodoTemplate from './component/TodoTemplate';
const App = () => {
return (
<div className="App">
<TodoTemplate>
{/* children 부분 */}
<h1>일정관리</h1>
<h2>이주성</h2>
</TodoTemplate>
</div>
);
}
export default App;
- C:\data\react\ex02\src\component\(new)css\(new)TodoTemplate.css
body {
margin: 0px;
padding: 0px;
background: #e9ecef;
}
.TodoTemplate {
width: 512px;
/*width가 주어진 상태에서 좌우 중앙 정렬*/
margin: 0px auto;
margin-top: 6rem;
border-radius: 4px;
overflow: hidden;
}
.app-title {
background: #22b8cf;
color: white;
height: 4rem;
font-size: 1.5rem;
/*내용물의 세로 가운데 정렬*/
align-items: center;
/*내용물의 가로 가운데 정렬*/
justify-content: center;
display: flex;
}
.content {
background: white;
}
- TodoInsert
- C:\data\react\ex02\src\component\(new)TodoInsert.js
import React from 'react';
import {MdAdd} from 'react-icons/md';
const TodoInsert = () => {
return (
<form className="TodoInsert">
<input placeholder="할일을 입력하세요." />
<button type="submit"><MdAdd/></button>
</form>
);
};
export default TodoInsert;
- TodoInsert css를 추가해준다.
- C:\data\react\ex02\src\component\css\TodoTemplate.css
.TodoInsert {
display: flex;
background: #495057;
}
/*기본 스타일 초기화*/
input {
background: none;
outline: none;
border: none;
padding: 0.5rem;
font-size: 1.125rem;
line-height: 1.5;
color: white;
/*버튼을 제외한 영역을 모두 차지하기*/
flex: 1;
}
input::placeholder {
color: #dee2e6;
}
/*기본 스타일 초기화*/
button {
background: none;
outline: none;
border: none;
background: #868e96;
color: white;
padding-left: 1rem;
padding-right: 1rem;
font-size: 1.5rem;
align-items: center;
cursor: pointer;
}
- 터미널을 중지(ctrl + c)시키고 다음과 같이 입력한다.
C:\data\react\ex02>yarn add react-icons
- 설치가 완료 후 다시 시작한다.
yarn start
- TodoList와 TodoListItem을 만들자.
- C:\data\react\ex02\src\component\(new)TodoListItem.js
import React from 'react';
const TodoListItem = () => {
return (
<div>
할일
</div>
);
};
export default TodoListItem;
- C:\data\react\ex02\src\component\(new)TodoList.js
import React from 'react';
import TodoListItem from './TodoListItem';
const TodoList = () => {
return (
<div>
<TodoListItem/>
<TodoListItem/>
<TodoListItem/>
</div>
);
};
export default TodoList;
- C:\data\react\ex02\src\App.js
...
const App = () => {
return (
<div className="App">
<TodoTemplate>
{/* children 부분 */}
<TodoInsert/>
<TodoList/>
</TodoTemplate>
</div>
);
}
...
- app.js에서 지정한 변수값들을 todolistitem으로 보내도록 하겠다.
- C:\data\react\ex02\src\App.js
...
const App = () => {
const[todos,setTodos] = useState([
...
{id:4,text:'API 호출',checked: false} // 추가
]);
return (
<div className="App">
...
<TodoList todos={todos}/>
...
</div>
);
}
export default App;
- C:\data\react\ex02\src\component\TodoList.js
...
const TodoList = ({todos}) => {
return (
<div>
{todos.map(todo => <TodoListItem key={todo.id} todo={todo}/>)}
</div>
);
};
...
- C:\data\react\ex02\src\component\TodoListItem.js
import React from 'react';
import { MdCheckBox, MdCheckBoxOutlineBlank, MdRemoveCircleOutline } from 'react-icons/md';
const TodoListItem = ({todo}) => {
const {text,checked} =todo;
return (
<div className="TodoListItem">
<div className={checked? "checkbox_checked":"checkbox"}>
{checked ? <MdCheckBox/>:<MdCheckBoxOutlineBlank/>}
<div className="text">{text}</div>
</div>
<div className="remove">
<MdRemoveCircleOutline/>
</div>
</div>
);
};
export default TodoListItem;
- C:\data\react\ex02\src\component\css\TodoTemplate.css
/* 리스트 CSS */
.TodoList {
min-height: 320px;
max-height: 513px;
overflow: auto;
}
.TodoListItem {
padding: 1rem;
display: flex;
align-items: center;
/*엘리먼트 사이사이에 테두리를 넣어 줌*/
border-top: 1px solid #dee2e6;
}
.TodoListItem:nth-child(even) {
background: #f8f9fa;
}
svg {
/*아이콘*/
font-size: 1.5rem;
margin-right: 0.5rem;
}
.checkbox,
.checkbox_checked {
cursor: pointer;
/*차지할 수 있는 영역 모두 차지*/
flex: 1;
display: flex;
align-items: center;
}
.checkbox_checked svg {
color: #22b8cf;
}
.checkbox_checked .text {
color: #adb5bd;
text-decoration: line-through;
}
.remove {
cursor: pointer;
color: #ff6b6b;
}
- 값을 입력받아 list를 추가해보겠다.
- todoinsert에 있던 변수와 form을 app.js로 옮기겠다. useCallback은 아직 안배워서 나중에 해보도록 하겠다.
import './App.css';
import { useRef, useState } from 'react';
...
import {MdAdd} from 'react-icons/md';
...
// 변수 선언 리퍼런스
const [nextId,setNextId] = useState(5);
...
const onClickSubmit = (e) =>{
e.preventDefault();
if(!window.confirm('추가하시겠습니까?')) return;
const newTodos = todos.concat({
...todos,
id:nextId,
text:value,
checked:false
});
setTodos(newTodos);
setNextId(nextId+1);
setValue('');
}
return (
<div className="App">
<TodoTemplate>
<form className="TodoInsert">
<input placeholder="할일을 입력하세요." value={value} onChange={onChange}/>
<button onClick={onClickSubmit}><MdAdd/></button>
</form>
<TodoList todos={todos}/>
</TodoTemplate>
</div>
);
};
export default App;
- 엔터로 입력을 받고 싶다면
...
const onKeyPress=(e)=>{
if(e.key==='Enter'){
onClickSubmit();
}
}
return (
<div className="App">
<TodoTemplate>
<form className="TodoInsert">
<input placeholder="할일을 입력하세요." value={value} onChange={onChange}
onKeyPress={onKeyPress}/>
<button onClick={onClickSubmit}><MdAdd/></button>
</form>
<TodoList todos={todos}/>
</TodoTemplate>
</div>
);
};
export default App;
- 다시
- CSS 빼고 전부 삭제
- C:\data\react\ex02\src\App.js
import './App.css';
import TodoList from './component/TodoList';
const App = () => {
return (
<div className="App">
<TodoList>
</TodoList>
</div>
);
};
export default App;
- C:\data\react\ex02\src\component\(new)TodoList.js
import React, { useState } from 'react';
import './css/TodoTemplate.css'
import {MdAdd} from "react-icons/md"
import TodoListItem from './TodoListItem';
const TodoList = () => {
const [todos,setTodos] = useState([
{id:1, text:'리액트 기초',checked:true},
{id:2, text:'리액트 중급',checked:true},
{id:3, text:'리액트 실전',checked:false},
{id:4, text:'리액트 UI',checked:false},
]);
return (
<div className="TodoTemplate">
<div className="app-title">일정관리</div>
<div className="content">
<div className="TodoInsert">
<input placeholder="일정을 입력하세요."/>
<button><MdAdd/></button>
</div>
{todos.map(todo=><TodoListItem key={todo.id} todo={todo}/>)}
</div>
</div>
);
};
export default TodoList;
- C:\data\react\ex02\src\component\TodoListItem.js
import React from 'react';
import { MdCheckBox, MdCheckBoxOutlineBlank, MdRemoveCircleOutline } from 'react-icons/md';
const TodoListItem = ({todo}) => {
const{text,checked}=todo;
return (
<div className="TodoListItem">
<div className={ checked ? "checkbox_checked": "checkbox" }>
{ checked ? <MdCheckBox/> :<MdCheckBoxOutlineBlank/> }
<div className="text">{ text }</div>
</div>
<div className="remove">
<MdRemoveCircleOutline/>
</div>
</div>
);
};
export default TodoListItem;
- C:\data\react\ex02\src\component\TodoList.js
import React, { useState } from 'react';
import './css/TodoTemplate.css'
import {MdAdd} from "react-icons/md"
import TodoListItem from './TodoListItem';
const TodoList = () => {
const [todos,setTodos] = useState([
{id:1, text:'리액트 기초',checked:true},
{id:2, text:'리액트 중급',checked:true},
{id:3, text:'리액트 실전',checked:false},
{id:4, text:'리액트 UI',checked:false},
]);
const [value,setValue] = useState('');
const [nextId,setNextId] = useState(5);
const onInsert = () => {
const newTodos = todos.concat({
...todos,
id:nextId,
text:value,
checked:false
});
setTodos(newTodos);
setNextId(nextId+1);
setValue('');
}
const onKeyPress = (e) =>{
if(e.key==='Enter'){
onInsert();
}
}
return (
<div className="TodoTemplate">
<div className="app-title">일정관리</div>
<div className="content">
<div className="TodoInsert">
<input placeholder="일정을 입력하세요." value={value}
onChange={(e)=>setValue(e.target.value)}
onKeyPress={onKeyPress}/>
<button onClick={onInsert}><MdAdd/></button>
</div>
{todos.map(todo=><TodoListItem key={todo.id} todo={todo}/>)}
</div>
</div>
);
};
export default TodoList;
- 이제 삭제해보도록 하겠다.
- C:\data\react\ex02\src\component\TodoList.js
...
const onRemove =(id) =>{
if(!window.confirm(`${id}번 일정을 삭제하시겠습니까?`)) return;
// 같지 않은 것만 걸러내겠다. 같은건 안걸러내겠다.
const newTodos = todos.filter((todo)=>todo.id!==id);
setTodos(newTodos);
}
return (
<div className="TodoTemplate">
<div className="app-title">일정관리</div>
<div className="content">
...
{todos.map(todo=><TodoListItem key={todo.id} todo={todo}
onRemove={()=>onRemove(todo.id)}/>)}
</div>
</div>
);
};
export default TodoList;
- C:\data\react\ex02\src\component\TodoListItem.js
...
return (
<div className="TodoListItem">
...
<div className="remove">
<MdRemoveCircleOutline onClick={onRemove}/>
</div>
</div>
);
};
export default TodoListItem;
- 체크박스 토글
- C:\data\react\ex02\src\component\TodoList.js
...
const onToggle = (id) => {
setTodos(todos.map(todo => todo.id === id ? {...todo, checked:!todo.checked}:todo))
}
return (
<div className="TodoTemplate">
<div className="app-title">일정관리</div>
<div className="content">
...
{todos.map(todo=><TodoListItem key={todo.id} todo={todo}
onRemove={()=>onRemove(todo.id)} onToggle={()=>onToggle(todo.id)} />)}
</div>
</div>
);
};
export default TodoList;
- C:\data\react\ex02\src\component\TodoListItem.js
import React from 'react';
import { MdCheckBox, MdCheckBoxOutlineBlank, MdRemoveCircleOutline } from 'react-icons/md';
const TodoListItem = ({todo,onRemove,onToggle}) => {
const{text,checked}=todo;
return (
<div className="TodoListItem">
<div className={ checked ? "checkbox_checked": "checkbox" } onClick={onToggle}>
{ checked ? <MdCheckBox/> :<MdCheckBoxOutlineBlank/> }
<div className="text">{ text }</div>
</div>
<div className="remove">
<MdRemoveCircleOutline onClick={onRemove}/>
</div>
</div>
);
};
export default TodoListItem;
'ICIA 수업일지' 카테고리의 다른 글
2021.09.14 수업일지(React.js) (0) | 2021.09.14 |
---|---|
2021.09.13 수업일지(React.js) (0) | 2021.09.13 |
2021.09.09 수업일지(간이발표, VSC, React) (0) | 2021.09.09 |
2021.09.08 수업일지(Spring, Servlet, JSTL) (0) | 2021.09.08 |
2021.09.07 수업일지(Servlet, Mysql, jstl) (0) | 2021.09.07 |