[React] 투두리스트 웹 만들기
안녕하세요 오랜만에 돌아왔습니다.
방학한지 3주정도 지났는데 이제야 공부 시작하네요.
소프트웨어 공학이라는 전공에서 팀 프로젝트로 투두리스트 어플을 만들었었는데
저는 디자인 담당이라 기능 구현을 하지 못했습니다.
이번 기회에 혼자서 따라가면서 만들어 보려고 합니다.
이번 프로젝트는 벨로퍼트 님의 게시물을 참고하였습니다.
벨로퍼트와 함께하는 모던 리액트 · GitBook (vlpt.us)
벨로퍼트와 함께하는 모던 리액트 · GitBook
벨로퍼트와 함께하는 모던 리액트 본 강의자료는 패스트캠퍼스 온라인 강의에서 제공하는 리액트 강의에서 사용되는 강의 문서입니다. 이 튜토리얼은 여러분들이 JavaScript 의 기초를 잘 알고있
react.vlpt.us
먼저 프로젝트를 하나 만들어 줄게요.
터미널에 npx create-react-app todo를 입력하여 만들어 주었습니다.
npm install react-icons --save
npm install --save styled-components
react-icons와 styled-components 라이브러리를 설치해 줍니다.
npm run start로 잘 구동되는지 확인해주고
import React from 'react';
import { createGlobalStyle } from 'styled-components';
const GlobalStyle = createGlobalStyle`
body {
background: #e9ecef;
}
`;
function App() {
return (
<>
<GlobalStyle />
<div>안녕하세요</div>
</>
);
}
export default App;
#e9ecef라는 색상을 이용해서 배경색을 설정해 줍니다.
새롭게 알게 된 것이 createGlobalStyle인데
styled-components에 있는 함수로, 글로벌 스타일을 적용해 준다고 합니다.
그 다음 src 경로 안에 컴포넌트 들을 저장할 Components 폴더를 하나 만들어 줍니다.
그리고 가장 먼저 만들어야 할 것은 템플릿 컴포넌트입니다.
TodoTemplate.js라는 파일을 하나 만들어 줍니다.
components/TodoTemplate.js
import React from 'react';
import styled from 'styled-components';
const TodoTemplateBlock = styled.div`
width: 512px;
height: 768px;
position: relative; /* 추후 박스 하단에 추가 버튼을 위치시키기 위한 설정 */
background: white;
border-radius: 16px;
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.04);
margin: 0 auto; /* 페이지 중앙에 나타나도록 설정 */
margin-top: 96px;
margin-bottom: 32px;
display: flex;
flex-direction: column;
`;
function TodoTemplate({ children }) {
return <TodoTemplateBlock>{children}</TodoTemplateBlock>;
}
export default TodoTemplate;
position : relative - 원래 위치 기준으로 상대적으로 배치
border-radius - 테두리 둥글게 만들어줌
box - shadow - 박스에 그림자를 만들어줌
display : flex - 기본 row (왼쪽 -> 오른쪽)
flex-direction : column - 위에서 아래로 정렬
이제 app.js에서 적용시켜 봅니다.
import를 해주고 TodoTemplate 태그로 감싸주면 되겠네요.
투두리스트가 보여질 하얀색 박스를 생성했습니다.
다음은 app.head라는 머리글에 올 컴포넌트를 만들어 보겠습니다.
import React from 'react';
import styled from 'styled-components';
const TodoHeadBlock = styled.div`
padding-top: 48px;
padding-left: 32px;
padding-right: 32px;
padding-bottom: 24px;
border-bottom: 1px solid #e9ecef;
h1 {
margin: 0;
font-size: 36px;
color: #343a40;
}
.day {
margin-top: 4px;
color: #868e96;
font-size: 21px;
}
.tasks-left {
color: #20c997;
font-size: 18px;
margin-top: 40px;
font-weight: bold;
}
`;
function TodoHead() {
return (
<TodoHeadBlock>
<h1>2019년 7월 10일</h1>
<div className="day">수요일</div>
<div className="tasks-left">할 일 2개 남음</div>
</TodoHeadBlock>
);
}
export default TodoHead;
padding - 방향 : 간격 생성
border - bottom : 1px solid : 박스 라인 아래쪽 생성
새롭게 알게 된 css - font-weight : bold 는 글자를 굵게 해주는 css라네요.
(normal로 해주면 보통 굵기)
잘 적용된 모습입니다.
이번에는 할 일 리스트를 보여주는 TodoList 컴포넌트를 만들어 줍시다.
import React from 'react';
import styled from 'styled-components';
const TodoListBlock = styled.div`
flex: 1;
padding: 20px 32px;
padding-bottom: 48px;
overflow-y: auto;
background: gray; /* 사이즈 조정이 잘 되고 있는지 확인하기 위한 임시 스타일 */
`;
function TodoList() {
return <TodoListBlock>TodoList</TodoListBlock>;
}
export default TodoList;
새로 알게 된 내용인 overflow-y는 세로 축으로 내용이 넘칠 때 어떻게 처리할 것인지 도와주는 함수입니다.
가로 축은 overflow-x라고 합니다.
overflow-y의 속성 ▽
visible (기본값) : 특정 요소가 박스를 넘어 가더라도, 그대로 보여준다.
hidden : 부모요소의 범위를 넘어가는 자식 요소의 부분은 보이지 않도록 처리한다.
(세로 스크롤바가 나타나지 않을 뿐 브라우저에 따라 가로 스크롤바는 나타남)
scroll : 부모요소의 범위를 넘어가는 자식요소의 부분은 보이지 않지만, 사용자가 확인 할 수 있도록 스크롤바를 표시한다.
(세로 스크롤바 항상 표시)
auto : 부모요소의 범위를 넘어가는 자식요소의 부분이 있을 경우 해당 부분을 보이지 않도록 처리하고, 사용자가 해당 부분을 확인 할 수 있도록 스크롤바를 표시 한다.
(내용이 넘칠때만 세로 스크롤바 표시)
보통은 auto를 사용하는 것이 적절해 보입니다.
flex : 1도 저에게는 생소한데 화면의 크기에 따라 유연하게 조정해주는 속성이라고 합니다.
app.js에 적용해 주면 될 것 같습니다.
템플릿 태그 안에 <TodoHead> 와 <TodoList>를 넣어주면 됩니다.
TodoList가 보여질 부분이 그레이 색상으로 보입니다.
적용된 것이 확인 되었기 때문에 그레이 색상은 없애줍니다.
다음은 TodoList 내용이 보여질 TodoItem을 만들어 줍니다.
import React from 'react';
import styled, { css } from 'styled-components';
import { MdDone, MdDelete } from 'react-icons/md';
const Remove = styled.div`
display: flex;
align-items: center;
justify-content: center;
color: #dee2e6;
font-size: 24px;
cursor: pointer;
&:hover {
color: #ff6b6b;
}
display: none;
`;
const TodoItemBlock = styled.div`
display: flex;
align-items: center;
padding-top: 12px;
padding-bottom: 12px;
&:hover {
${Remove} {
display: initial;
}
}
`;
const CheckCircle = styled.div`
width: 32px;
height: 32px;
border-radius: 16px;
border: 1px solid #ced4da;
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
cursor: pointer;
${props =>
props.done &&
css`
border: 1px solid #38d9a9;
color: #38d9a9;
`}
`;
const Text = styled.div`
flex: 1;
font-size: 21px;
color: #495057;
${props =>
props.done &&
css`
color: #ced4da;
`}
`;
function TodoItem({ id, done, text }) {
return (
<TodoItemBlock>
<CheckCircle done={done}>{done && <MdDone />}</CheckCircle>
<Text done={done}>{text}</Text>
<Remove>
<MdDelete />
</Remove>
</TodoItemBlock>
);
}
export default TodoItem;
여기서 코드를 이해하는데 시간이 좀 걸렸습니다.
const TodoItemBlock = styled.div`
display: flex;
align-items: center;
padding-top: 12px;
padding-bottom: 12px;
&:hover {
${Remove} {
display: initial;
}
}
`;
이 부분이 ComponentSelector라고 해서 TodoItemBlock 위에 커서가 있으면 Remove 컴포넌트를 보여주라는 의미 입니다.
${props =>
props.done &&
css`
border: 1px solid #38d9a9;
color: #38d9a9;
`}
&&연산자로 props로 받은 상태가 done이면 css부분을 보여주라는 의미입니다.
할 일 요소들에 커서를 갖다되면 쓰레기통 모양의 Remove 컴포넌트를 보여주는 모습입니다.
다음은 새로운 할 일을 등록할 수 있는 TodoCreate를 만들어 보겠습니다.
import React, { useState } from 'react';
import styled, { css } from 'styled-components';
import { MdAdd } from 'react-icons/md';
const CircleButton = styled.button`
background: #38d9a9;
&:hover {
background: #63e6be;
}
&:active {
background: #20c997;
}
z-index: 5;
cursor: pointer;
width: 80px;
height: 80px;
display: block;
align-items: center;
justify-content: center;
font-size: 60px;
position: absolute;
left: 50%;
bottom: 0px;
transform: translate(-50%, 50%);
color: white;
border-radius: 50%;
border: none;
outline: none;
display: flex;
align-items: center;
justify-content: center;
transition: 0.125s all ease-in;
${props =>
props.open &&
css`
background: #ff6b6b;
&:hover {
background: #ff8787;
}
&:active {
background: #fa5252;
}
transform: translate(-50%, 50%) rotate(45deg);
`}
`;
const InsertFormPositioner = styled.div`
width: 100%;
bottom: 0;
left: 0;
position: absolute;
`;
const InsertForm = styled.form`
background: #f8f9fa;
padding-left: 32px;
padding-top: 32px;
padding-right: 32px;
padding-bottom: 72px;
border-bottom-left-radius: 16px;
border-bottom-right-radius: 16px;
border-top: 1px solid #e9ecef;
`;
const Input = styled.input`
padding: 12px;
border-radius: 4px;
border: 1px solid #dee2e6;
width: 100%;
outline: none;
font-size: 18px;
box-sizing: border-box;
`;
function TodoCreate() {
const [open, setOpen] = useState(false);
const onToggle = () => setOpen(!open);
return (
<>
{open && (
<InsertFormPositioner>
<InsertForm>
<Input autoFocus placeholder="할 일을 입력 후, Enter 를 누르세요" />
</InsertForm>
</InsertFormPositioner>
)}
<CircleButton onClick={onToggle} open={open}>
<MdAdd />
</CircleButton>
</>
);
}
export default TodoCreate;
주의해야 할 것은 ComponentSelector에는 hover과 active 그리고 focus 속성이 있는데
hover : 마우스가 해당 요소 위에 있을 때 함수를 실행
active : 마우스로 해당 요소를 클릭하는 순간부터 때는 순간 까지 함수를 실행
focus : 마우스로 해당 요소를 클리하면 함수를 실행
UI면에서 유용한 ComponentSelector를 알게 되어서 좋았던 것 같습니다.
사용자 측에서 전부 다 동적으로 바뀌는 UI를 만들 수 있다는 점에서 ComponentSelector가 강점이 있는 것 같습니다.
여기까지가 UI 부분이었고
다음 시간에는 기능 구현을 해보도록 하겠습니다.
감사합니다!
