본문 바로가기

TypeScript

TypeScript 타입스크립트 + 리액트를 활용해 TodoList 만들기 #1

* 아래의 글은 JavaScript 프롤로그 및 목차를 먼저 읽으신 후 읽으시기를 권장합니다.

* 타입스크립트 기본 문법은 타입스크립트 핸드북 을 통해 학습하시는 것을 추천드립니다.


본 글에서 다룰 내용

  • React template을 활용한 타입스크립트 프로젝트 만들기
  • 기본 세팅하기
  • TodoList 컴포넌트와 디자인 완성하기

본 글에서 사용하는 기술

  • TypeScript
  • React-Hooks
  • Context-API
  • SCSS
  • Styled-Components

일단 제일 먼저 프로젝트를 생성해야합니다.

 

타입스크립트 + 리액트 프로젝트를 생성하는 방법은 아주 간단합니다.

yarn add create react-app [project-name] --template typescript

//or

npx create-react-app [project-name] --template typescript

타입스크립트 템플릿만 설정해주면 CRA로 타입스크립트 설정이 되어있는 리액트 프로젝트를 만들 수 있습니다.

 

이렇게 하면 CRA로 타입스크립트 템플릿을 적용한 프로젝트를 생성할 수 있습니다.


기본 세팅하기

 

저는 Styled-Components의 styled와 createGlobalStyle을 사용하여 기본 세팅을 했습니다.

 

App.tsx

import React from 'react';
import styled, { createGlobalStyle } from 'styled-components';

const Rootdiv = styled.div`
  display : flex;
  height : 100vh;
  width : 100%;
  justify-content : center;
  align-items : center;
  flex-direction : column;
`;

const GlobalStyle = createGlobalStyle`
  body{
    padding : 0;
    margin : 0;
  }
`;

const App = () => {
  return(
    <>
      <GlobalStyle />
        <Rootdiv>
        </Rootdiv>
    </>
  )
}

export default App;

GlobalStyle을 사용해 모든 페이지 body의 마진과 패딩을 없애고 시작합니다.

 

ToDoList에서는 App.tsx에서 모든 컴포넌트를 불러오기 때문에 RootDiv를 사용하여 Div의 크기와 디자인을 설정해주었습니다.


ToDoList를 제작하면서 세 가지 컴포넌트를 제작해야합니다.

  • TodoForm.tsx: 새 할 일을 등록할 때 사용하는 컴포넌트
  • TodoItem.tsx: 할 일에 대한 정보를 보여주는 컴포넌트
  • TodoList.tsx: 여러 TodoItem을 렌더링해주는 컴포넌트

src 디렉토리에 component 디렉토리를 만들고, 그 안에 위 세 개의 컴포넌트를 제작하시면 됩니다.


제일 먼저 TodoForm 먼저 코드를 작성해 보겠습니다.

 

TodoForm 컴포넌트는 이런 역할을 합니다.

  • ToDoList에 등록할 내용 작성을 위한 인풋
  • ToDoList에 등록하기 위한 버튼

input의 value값은 useState를 통해서 관리하도록 하겠습니다. submit 이벤트를 통해 새 항목을 생성하는 부분은 #2에서 구현하겠습니다.

 

src/components/TodoForm.tsx

import React, { useState } from 'react';

function TodoForm() {
  const [value, setValue] = useState('');

  const onSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    // TODO: 새 항목 생성하기
    setValue('');
  };

  return (
    <form onSubmit={onSubmit} className = "todoForm">
      <input
        value={value}
        placeholder="무엇을 하실 건가요?"
        onChange={e => setValue(e.target.value)}
      />
      <button>등록</button>
    </form>
  );
}

export default TodoForm;

저는 SCSS를 사용하여 반응형 웹 디자인을 했습니다.

 

src/styles/TodoForm.scss

@import './mixin';

@include mobile{
    .todoForm{
        display: flex;
        width: 100%;
        height: 10%;
        align-items: center;
        justify-content: center;
        input{
            display: flex;
            width: 70%;
            height: 34px;
            outline: none;
            padding-left: 10px;
            font-family: 'Noto Sans KR', sans-serif;
            font-weight: bold;
        };
        
        button{
            display: flex;
            width: 60px;
            height: 40px;
            outline: none;
            cursor: pointer;
            align-items: center;
            justify-content: center;
            font-family: 'Noto Sans KR', sans-serif;
            font-weight: bold;
        }
    }
}

@include tablet{
    .todoForm{
        display: flex;
        width: 100%;
        height: 10%;
        align-items: center;
        justify-content: center;
        input{
            display: flex;
            width: 50%;
            height: 34px;
            outline: none;
            padding-left: 10px;
            font-family: 'Noto Sans KR', sans-serif;
            font-weight: bold;
        };
        
        button{
            display: flex;
            width: 5%;
            height: 40px;
            outline: none;
            cursor: pointer;
            align-items: center;
            justify-content: center;
            font-family: 'Noto Sans KR', sans-serif;
            font-weight: bold;
        }
    }
}

@include desktop{
    .todoForm{
        display: flex;
        width: 100%;
        height: 10%;
        align-items: center;
        justify-content: center;
        input{
            display: flex;
            width: 30%;
            height: 34px;
            outline: none;
            padding-left: 10px;
            font-family: 'Noto Sans KR', sans-serif;
            font-weight: bold;
        };
        
        button{
            display: flex;
            width: 5%;
            height: 40px;
            outline: none;
            cursor: pointer;
            align-items: center;
            justify-content: center;
            font-family: 'Noto Sans KR', sans-serif;
            font-weight: bold;
        }
    }
}

모바일, 태블릿, 데스크톱 크기 별로 반응형 디자인을 했습니다.


TodoItem 코드를 작성해보겠습니다.

 

TodoItem은 이런 역할을 합니다.

  • ToDoList에 저장되어있는 내역을 보여줌(id, content)
  • ToDoItem을 삭제할 수 있는 버튼

src/components/TodoItem.tsx

import React from 'react';
import './TodoItem.css';

export type TodoItemProps = {
  todo: {
    id: number;
    text: string;
    done: boolean;
  };
}

function TodoItem({ todo } : TodoItemProps){
    return(
        <div className = "todoItem">
            <div className = "ItemIndex">
                <span>{todo.id}</span>
            </div>
            <div className = {`ItemContent ${todo.done ? 'done' : ''}`}>
                <span>{todo.text}</span>
            </div>
            <div className = "ItemBtn">
                <span>삭제</span>
            </div>
        </div>
    );
}

export default TodoItem;

props로는 todo 객체를 받아옵니다.

만약 todo.done 값이 참이라면 done SCSS 클래스를 적용합니다.

 

src/styles/TodoItem.scss

@import './mixin';

@include mobile{
  .todoItem{
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    width: 90%;
    height : 45px;
    font-family: 'Noto Sans KR', sans-serif;
    border-bottom-style: solid;
    border-bottom-width: 1px;
    margin-bottom: 5px;
  }.done{
    color: #999999;
    text-decoration: line-through;
  };
}

@include tablet{
  .todoItem{
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    width: 50%;
    height : 45px;
    font-family: 'Noto Sans KR', sans-serif;
    border-bottom-style: solid;
    border-bottom-width: 1px;
    margin-bottom: 5px;
  }.done{
    color: #999999;
    text-decoration: line-through;
  };
}

@include desktop{
  .todoItem{
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    width: 35%;
    height : 45px;
    font-family: 'Noto Sans KR', sans-serif;
    border-bottom-style: solid;
    border-bottom-width: 1px;
    margin-bottom: 5px;
  }.done{
    color: #999999;
    text-decoration: line-through;
  };
}

.ItemIndex{
  display: flex;
  width: 5%;
  height: 100%;
  align-items: center;
};

.ItemContent{
  display: flex;
  width: 85%;
  height: 100%;
  align-items: center;
  span{
    cursor: pointer;
  };
}

.ItemBtn{
  display: flex;
  width: 10%;
  height: 100%;
  align-items: center;
  justify-content: center;
  color : red;
  cursor : pointer;
}

TodoList 코드를 작성해보겠습니다.

 

TodoList는 이런 역할을 합니다.

  • TodoItems 컴포넌트를 렌더링 해주는 역할

TodoList에서는 todos라는 배열을 사용하여 여러개의 TodoItems 컴포넌트를 렌더링합니다.

아직은 이 배열에 대한 상태가 존재하지 않으므로 이 배열을 임시적으로 TodoList 컴포넌트 내부에서 선언하겠습니다.

 

src/components/TodoList.tsx

import React from 'react';
import TodoItem from './TodoItem';
import '../styles/TodoList.scss';

function TodoList(){
	const todos = [
    	{
      		id: 1,
      		text: 'Context API 배우기',
      		done: true
    	},
    	{
      		id: 2,
      		text: 'TypeScript 배우기',
      		done: true
    	},
    	{
      		id: 3,
      		text: 'TypeScript 와 Context API 함께 사용하기',
      		done: false
    	}
  	];
    return(
        <div className = "todoList">
            {
                todos.length === 0 ? <span>오늘 할 일을 등록하세요!</span>
                : todos.map(todo => (
                    <TodoItem todo = {todo} key = {todo.id} />
                ))
            }
        </div>
    )
}

export default TodoList;

저는 todoList div 내에서 삼항 연산자를 사용해 todos배열의 길이에 따라 다른 모습을 보여주도록 했습니다.

 

src/styles/TodoList.scss

.todoList{
    display: flex;
    width: 100%;
    flex-direction: column;
    align-items: center;
    span{
        font-family: 'Noto Sans KR', sans-serif;
        font-size: large;
        font-weight: bold;
    }
}

마지막으로 앞에서 코딩한 컴포넌트를 App에서 렌더링 해봅시다.

 

src/App.tsx

import React from 'react';
import styled, { createGlobalStyle } from 'styled-components';

const Rootdiv = styled.div`
  display : flex;
  height : 100vh;
  width : 100%;
  justify-content : center;
  align-items : center;
  flex-direction : column;
`;

const GlobalStyle = createGlobalStyle`
  body{
    padding : 0;
    margin : 0;
  }
`;

const App = () => {
  return(
    <>
      <GlobalStyle />
        <Rootdiv>
          <TodoForm />
          <TodoList />
        </Rootdiv>
    </>
  )
}

export default App;

위의 코드를 따라 하시면 예쁜 ToDoList가 나올 것 입니다.

ToDoList

 

다음 #2 글에서는 ContextApi를 사용하여 실제로 작동하는 ToDoList를 구현해보겠습니다.

 

혹시나 오류가 나거나, 조금 더 효율적인 코드가 있다면 댓글 남겨주세요!🥰

 

긴 글 읽어주셔서 감사합니다😁


참고 문헌 및 사이트

전체 코드는 제 깃헙 링크에서 확인하실 수 있습니다.

TS-ToDoList - https://github.com/Bigstar1108/TS-ToDoList

 

긴 글 읽어주셔서 감사합니다😀