|
오늘의 할일을 등록하고 관리하는 앱 만들기 by React
1. 할일모음 앱 & 작업관리 앱 만들어보기
리액트로 무언가를 만들어보는 두번째 예제로 뭘 해볼까 하다가 많이들 하는 Todo App을 만들어 보는걸 함께 할 수 있도록 하는게 어떨까 싶어서, 글 남깁니다.
#todo #bucket list #todo app
2. 환경설정
리액트 개발환경을 자동으로 구성해주는 CRA(Create React App) 명령어를 잊지 않으셨죠?
npx create-reat-app todo-app
※ 위 명령어는 Node.js가 설치된 이후 Visual Studio Code를 이용해 작업폴더를 하나 생성한 다음에 실행해야 합니다. 지난번 단위환산 앱과 같은곳에서 시작해보세요!
3. 불필요한 파일과 코드 정리하기
※ 필수 작업은 아닌데, CRA로 생성할 경우 사용하지 않아도 되는 것들도 있어서 간단히 학습을 위해 정리하는 편입니다.
위와같이 기본 CRA가 구성하는 프로젝트 설정중 당장 불필요한 것들을 일부 걷어내고, [터미널 > 새 터미널] 또는 단축키 [Ctrl + `]를 눌러서 터미널 창이 열리면, npm start 명령을 입력해 개발 서버를 구동하면 3000번 포트에서 기본 리액트 앱이 미리보기 됩니다.
기본 CRA가 제공하는 로고 이미지나 컨텐츠를 코드에서 지워버렸기 때문에 아무것도 없는 상태에서 시작합니다.
※ title 변경은 public/index.html 에서 변경하세요!
4. 앱 UI 구상하기
4-1. 검색하기
여러가지 todo app 에 대해서 검색해봅시다. 무슨 앱인건지, 어떤 UI를 갖고 있는지 또는 어떤 기능을 갖추고 있는지 등등..
4-2. 스케치하기
스케치는 Figjam, UI 제작은 Figma !
5. 구현하기
이렇게 바로 시작한다고요? 네, 간단한 앱이니까요..
5-1. 컴포넌트로 관리용 폴더 생성하고 각 파일 컴포넌트 만들기
처음부터 컴포넌트 구성은 안해도 됩니다. 나중에 컴포넌트로 분리하거나 합성하는 사례가 있을듯 하니까요!
※ 타이틀 부분은 고정으로 사용할까 하다가...일단 컴포넌트로 구성해서 할 일을 만들어 줍시다. 할일이 몇개 있는지 표시하는것? 처럼요..
5-2. src/App.js 편집하기
[code]
import React from "react";
import './App.css';
function App() {
return (
<div className="App">
<h1>todo app</h1>
<div className="input-field">
<form>
<input type="text" placeholder='오늘의 할일을 등록해보세요' />
<button>등록</button>
</form>
</div>
<div className="output-field">
{ /* 여기에 할일들 목록이 출력됩니다 */ }
</div>
</div>
);
}
export default App;
[/code]
5-3. src/App.css 편집하기
[code]
h1 {
text-align: center;
border-bottom: 1px solid #ccc;
padding-bottom: 1rem
}
.input-field {
display: flex;
justify-content: center;
}
[/code]
5-4, state 사용하기
리액트에서 바뀌는 부분, 변화되는 데이터를 관리하는 state가 가장 중요한 부분 중 하나인데요, "할일"을 입력하는 입력폼에서 입력된 데이터로 할일(들)이 늘어나게 되고, 이때마다 등록(또는 +) 버튼을 눌러서 데이터를 조작하게 되겠죠? 할일(들)은 1개도 없을수도 있고 2개이상, 여러개일 수 있습니다. 즉, 배열 형테의 데이터를 갖는게 좋지 않을까요?
※ 리액트 sate는 '리액트 컴포넌트의 상태'를 의미합니다. 상태라고 하면 어려우니 '데이터' 라고 생각하시는게 좋고요, state를 정의할때 유의해야 하는게 렌더링이나 데이터 흐름에 사용되는 값만 state에 포함시키는 것입니다. 왜냐하면, 리액트는 state가 변경되는 UI를 재렌더링 합니다. 물론 이것도 제어할 수 있지만...
[code]
import React, { useState } from "react"; // useState 모듈 불러오기
import "./App.css";
function App() {
const [todo, setTodo] = useState([]); // todo 라는 state, setTodo라는 modifiter 함수
const xxxxonChange = (e) => setTodo(e.target.value); // 입력폼 데이터를 state에 등록
const xxxxonSubmit = (e) => {
e.preventDefault(); // form 새로고침 방지
console.log(todo);
setTodo("");
};
return (
<div className='App'>
<h1>todo app</h1>
<div className='input-field'>
<form xxxxonSubmit={xxxxonSubmit}>
<input
type='text'
placeholder='오늘의 할일을 등록해보세요'
xxxxonChange={xxxxonChange}
value={todo}
autoFocus
/>
<button>등록</button>
</form>
</div>
<div className='output-field'>
{/* 여기에 할일들 목록이 출력됩니다 */}
</div>
</div>
);
}
export default App;
[/code]
5-5. state 한번 더 사용하기
할일1, 할일2, 할일3, ... 우리는 이것을 할일들 이라는 state 변수로 관리할 계획입니다. 따라서, 별도의 state를 하나 더 준비해야하구요,
여기서 자바스크립트 ES6 문법인 Destructuring(=구조 분해 할당)에 대해서 잠깐 짚어보고 넘어가는게 좋습니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
또 하나의 중요한 개념인 스프레드 문법도 읽어보세요
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax
[code]
import React, { useState } from "react"; // useState 모듈 불러오기
import "./App.css";
function App() {
const [todo, setTodo] = useState([]); // todo 라는 state, setTodo라는 modifiter 함수
const [todos, setTodos] = useState([]); //todos 라는 state(=할일모음), setTodos라는 modifier 함수
const xxxxonChange = (e) => setTodo(e.target.value); // 입력폼 데이터를 state에 등록
const xxxxonSubmit = (e) => {
e.preventDefault(); // form 새로고침 방지
setTodo(""); // 할일 입력폼 초기화
setTodos(todos => [todo, ...todos]); // state는 새로운 값으로만 변경 가능
};
return (
<div className='App'>
<h1>todo app</h1>
<div className='input-field'>
<form xxxxonSubmit={xxxxonSubmit}>
<input
type='text'
placeholder='오늘의 할일을 등록해보세요'
xxxxonChange={xxxxonChange}
value={todo}
autoFocus
/>
<button>등록</button>
</form>
</div>
<div className='output-field'>
{ todos }
</div>
</div>
);
}
export default App;
[/code]
보기도 좋고 관리하기도 쉬운 목록(=list) 형태로 UI를 바꾸어 줍시다.
5-6. UI 변경하기
여기서는 할일들 state에 있는 데이터를 꺼내는 문법을 알아야 합니다. 보통 배열은 반복문과 많이 사용하잖아요? 그래서 관련된 메소드가 있다는걸 말씀드릴게요
- for , for ~ of, for ~ in 구문
- map()
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Map
- filter()
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
- reduce()
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
※ 세부 관련 링크는 찾아서 수정해드릴게요! 일단 리액트에서는 map, filter, reduce 라는 세가지 메소드를 자주 사용합니다.
[code]
import React, { useState } from "react"; // useState 모듈 불러오기
import "./App.css";
function App() {
const [todo, setTodo] = useState([]); // todo 라는 state, setTodo라는 modifiter 함수
const [todos, setTodos] = useState([]); //todos 라는 state(=할일모음), setTodos라는 modifier 함수
const xxxxonChange = (e) => setTodo(e.target.value); // 입력폼 데이터를 state에 등록
const xxxxonSubmit = (e) => {
e.preventDefault(); // form 새로고침 방지
setTodo(""); // 할일 입력폼 초기화
setTodos(todos => [todo, ...todos]); // state는 새로운 값으로만 변경 가능, ES6 구조분해할당 문법
};
return (
<div className='App'>
<h1>todo app</h1>
<div className='input-field'>
<form xxxxonSubmit={xxxxonSubmit}>
<input
type='text'
placeholder='오늘의 할일을 등록해보세요'
xxxxonChange={xxxxonChange}
value={todo}
autoFocus
/>
<button>등록</button>
</form>
</div>
<div className='output-field'>
<ul>
{todos.map((todo) => <li>{todo}</li>)}
</ul>
</div>
</div>
);
}
export default App;
[/code]
[code]
{ /*...기존과 동일한 코드 ... */}
<div className='output-field'>
<ul>
{todos.map((todo, i) => <li key={i}>{todo}</li>)}
</ul>
</div>
{ /*...기존과 동일한 코드 ... */}
[/code]
이제, 오류도 없고 할일을 입력하면 할일들 목록이 업데이트 되어 순서대로 리스트 됩니다.
5.7 끝! 잘 만들어졌네요..기본 기능에 충실하게..
여기서 CSS 추가하고 이것저것 정리하면 잘 되는거죠, 그런데 추가도 했으니 삭제를 해보고 싶지 않으신가요?
각 항목에 삭제용 버튼을 추가하고, 이벤트를 xxxxonClick시 removeTodo 함수와 바인딩 해줍니다. 또한, .map() 을 이용해 todo와 i (=index)를 넘겨받을 수 있는데 이중에서 특정 배열항목을 삭제하려면 index를 알아야 하니, i를 파라미터로 입력받아 삭제(X) 버튼을 누른 할일 항목의 인덱스와 할일들에 입력된 할일의 인덱스를 비교해서 나머지 할일들 모음을 반환하게 해서 처리를 해봤습니다.
[code]
import React, { useState } from "react"; // useState 모듈 불러오기
import "./App.css";
function App() {
const [todo, setTodo] = useState([]); // todo 라는 state, setTodo라는 modifiter 함수
const [todos, setTodos] = useState([]); //todos 라는 state(=할일모음), setTodos라는 modifier 함수
const xxxxonChange = (e) => setTodo(e.target.value); // 입력폼 데이터를 state에 등록
const xxxxonSubmit = (e) => {
e.preventDefault(); // form 새로고침 방지
setTodo(""); // 할일 입력폼 초기화
setTodos(todos => [...todos, todo]); // state는 새로운 값으로만 변경 가능, ES6 구조분해할당 문법
};
const removeTodo = (id) => setTodos(todos.filter((todo, i) => i !== id));
return (
<div className='App'>
<h1>todo app</h1>
<div className='input-field'>
<form xxxxonSubmit={xxxxonSubmit}>
<input
type='text'
placeholder='오늘의 할일을 등록해보세요'
xxxxonChange={xxxxonChange}
value={todo}
autoFocus
/>
<button>등록</button>
</form>
</div>
<div className='output-field'>
<ul>
{todos.map((todo, i) => <li key={i}>{todo} <button xxxxonClick={() => removeTodo(i)}>×</button></li>)}
</ul>
</div>
</div>
);
}
export default App;
[/code]
6. 각자 그럼, CSS를 조금 더 추가해서 UI를 보기좋게 만들어 보시고 todo-app을 본인의 깃헙에 배포해보시기 바랍니다.
UI를 세련되게 바꾸어 보는게 어떨까요?!
※ 시간날때 보세요!
이번 배포는 Heroku 라는 PaaS를 사용해보도록 할텐데, 별도로 자료 만들어서 드려볼게요! 기본은 깃헙에 배포이고요, 그다음으로 Heroku나 Netlify 등을 사용하기도 합니다.
※ 90% 완료인데, 일단 깃헙 Clone 해서 확인해보시길 바랍니다.
https://github.com/seonyeonghun/todo-app
|
첫댓글 이벤트 핸들러 바인딩할때 xxxonChange 라는 부분들은 다음카페 업로드시 자동으로 네이밍이 치환된 부분입니다. onChange로 작성하셔야겠네요,
※ 클론해서 시작해보실 분들은, 위 링크를 참고하세요
1) VSC > 파일 > 폴더열기 ... [작업폴더 지정]
2) 터미널 > 새 터미널, $> git clone https://github.com/seonyeonghun/todo-app
3) cd todo-app && code .
4) (새 작업창 열리면) 새 터미널 열기 > $ npm install 또는 npm i
5) npm start
※ $는 bash 기호입니다. cmd나 powershell이면 다른 기호나 주소가 나타날 수 있어요!