관리 메뉴

거니의 velog

(2) 일정 관리 애플리케이션 만들기 2 본문

React/React_일정 관리 앱 만들기

(2) 일정 관리 애플리케이션 만들기 2

Unlimited00 2023. 12. 5. 17:28

2. UI 구성하기

* 앞으로 만들 컴포넌트를 하나하나 용도별로 소개하겠다!


1. TodoTemplate : 화면을 가운데에 정렬시켜 주며, 앱 타이틀(일정 관리)을 보여 준다. children으로 내부 JSX를 props로 받아 와서 렌더링해 준다.

2. TodoInsert : 새로운 항목을 입력하고 추가할 수 있는 컴포넌트이다. state를 통해 인풋의 상태를 관리한다.

3. TodoListItem : 각 할 일 항목에 대한 정보를 보여 주는 컴포넌트이다. todo 객체를 props로 받아 와서 상태에 따라 다른 스타일의 UI를 보여 준다.

4. TodoList : todos 배열을 props로 받아 온 후, 이를 배열 내장 함수 map을 사용해서 여러 개의 TodoListItem 컴포넌트로 변환하여 보여 준다.


* 이렇게 총 네 개의 컴포넌트를 만든다. 이 컴포넌트들은 src 디렉터리에 components라는 디렉터리를 생성하여 그 안에 저장할 것이다. 컴포넌트 파일을 components 디렉터리에 넣는 이유는 기능이나 구조상 필요하기 때문이 아니라 자주 사용되는 관습이기 때문이다.

* 지금 단계에서는 컴포넌트의 기능에 대해 신경 쓰기보다는 모양새를 갖추는 데 집중할 것이다.


(1) TodoTemplate 만들기

* src 디렉터리에 components 디렉터리를 생성한 뒤 그 안에 TodoTemplate.js 와 TodoTemplate.scss 파일을 생성하자. 그 다음에는 자바스크립트 파일을 다음과 같이 작성하자.

import React from 'react';
import './TodoTemplate.scss';

const TodoTemplate = ({ children }) => {
  return (
    <div className="TodoTemplate">
      <div className="app-title">일정 관리</div>
      <div className="content">{children}</div>
    </div>
  );
};

export default TodoTemplate;

* 다음으로 이 컴포넌트를 App.js에서 불러와 렌더링하자.

import React from 'react';
import TodoTemplate from './components/TodoTemplate';

const App = () => {
  return <TodoTemplate>Todo App을 만들자!</TodoTemplate>;
};

export default App;

* 이 컴포넌트를 작성하는 과정에서 다음과 같이 상단에 import를 넣지 않고 바로 컴포넌트를 사용하려고 하면, VS Code 에디터에서 자동 완성 기능이 나타날 것이다.

* 그러나, TodoTemplate.js 컴포넌트가 VS Code 에서 다른 탭으로 열려 있지 않으면 자동 완성이 작동하지 않는다. 닫혀 있는 파일에도 자동 완성이 제대로 작동하려면 프로젝트 최상위 디렉터리에 jsconfig.json 파일을 만들어 주어야 한다.

* jsconfig.json 파일을 만들고 해당 파일을 열어서 Ctrl + Space를 눌러 보자.

* 자동 완성 박스가 나타나면 Enter를 눌러 보자. 그러면 다음과 같은 코드가 자동 완성 된다.

{
    "compilerOptions": {
        "target": "es2020"
    }
}

* [ Ctrl + Space ] 는 VS Code 에디터의 자동 완성 단축키이다. 컴포넌트 이름을 입력할 때도 이 키를 입력해서 VS Code 에디터의 자동 완성 인텔리센스를 열 수 있다. jsconfig.json 파일을 저장하고 나면, 불러오려는 컴포넌트 파일이 열려 있지 않아도 자동 완성을 통해 컴포넌트를 불러와서 사용할 수 있다.

스타일링되지 않은 TodoTemplate

* 이 작업을 마치면 위와 같은 화면이 나타날 것이다. 이제 스타일을 작성할 차례인데, CSS가 익숙하지 않다면, 가급적 브라우저를 한쪽 화면에 띄워 놓고 각 스타일 코드가 실제로 어떠한 변화를 주는지 확인하면서 작성해 보는 것을 추천한다.

[TodoTemplate.scss]

@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;500;700&display=swap');

body {
    font-family: 'Noto Sans KR', sans-serif;
}

.TodoTemplate {
    width: 512px;
    // width가 주어진 상태에서 좌우 중앙 정렬
    margin: 6rem auto 0;
    border-radius: 4px;
    overflow: hidden;

    .app-title {
        background-color: #22b8cf;
        color: white;
        height: 4rem;
        font-size: 1.5rem;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .content {
        background-color: white;
    }
}

* 여기서는 레이아웃을 작성할 때 flex라는 display 속성을 자주 쓸 텐데, 코드에 있는 주석을 자세히 읽으며 작성해 보면 각 코드가 어떤 역할을 하는지 충분히 학습할 수 있지만, flex를 더 자세히 알고 싶다면 Flexbox Froggy라는 사이트를 추천한다. flex를 학습하는 데 큰 도움이 될 것이다.

https://flexboxfroggy.com/#ko

 

Flexbox Froggy

A game for learning CSS flexbox

flexboxfroggy.com


(2) TodoInsert 만들기

* 이번에는 TodoInsert 를 만들 차례이다. components 디렉터리에 TodoInsert.js 파일과 TodoInsert.scss 파일을 생성해 보자.

import React from 'react';
import { MdAdd } from 'react-icons/md';
import './TodoInsert.scss';

const TodoInsert = () => {
  return (
    <form className="TodoInsert">
      <input type="text" placeholder="할 일을 입력하세요." />
      <button type="submit">
        <MdAdd />
      </button>
    </form>
  );
};

export default TodoInsert;

* 여기서 처음으로 react-icons의 아이콘을 사용했다.

https://react-icons.github.io/react-icons/icons/md/

 

React icons preview for md

#fff/#000#000/#fff#ddd/#333#333/#ddd#ff0/#fff#000/#ff0#0ff/#fff#000/#0ff#f0f/#fff#000/#f0f Codes import { null } from "react-icons/md"; mdmd/nullimport { null } from "react-icons/md";

react-icons.github.io

* 이 페이지에 들어가면 다음과 같은 수 많은 아이콘과 이름이 함께 나타나는 모습을 볼 수 있다.

* 여기서 사용하고 싶은 아이콘을 고른 다음, import 구문을 사용하여 불러온 후 컴포넌트처럼 사용하면 된다.

import { 아이콘 이름 } from 'react-icons/md';

* 이제 이 컴포넌트를 App에서 불러와 렌더링해 보자.

import React from 'react';
import TodoTemplate from './components/TodoTemplate';
import TodoInsert from './components/TodoInsert';

const App = () => {
  return (
    <TodoTemplate>
      <TodoInsert />
    </TodoTemplate>
  );
};

export default App;

스타일링 되지 않은 TodoInsert

* 이제 이 컴포넌트를 스타일링해 보자!

[TodoInsert.scss]

.TodoInsert {
    display: flex;
    background-color: #495057;
    input {
        // 기본 스타일 초기화
        background: none;
        outline: none;
        border: none;
        padding: 0.5rem;
        font-size: 1.125rem;
        line-height: 1.5;
        color: white;
        &:placeholder {
            color: #dee2e6;
        }
        // 버튼을 제외한 영역을 모두 차지하기
        flex: 1;
    }
    button {
        // 기본 스타일 초기화
        background: none;
        outline: none;
        border: none;
        background-color: #868e96;
        color: white;
        padding-left: 1rem;
        padding-right: 1rem;
        font-size: 1.5rem;
        display: flex;
        align-items: center;
        cursor: pointer;
        transition: background-color 0.1s ease-in;
        &:hover {
            background-color: #adb5bd;
        }
    }
}

TodoInsert

* 스타일링을 하니 TodoInsert 컴포넌트가 훨씬 아름다워졌다. 마우스를 + 버튼에 올려 보면 버튼의 배경색도 잘 바뀌는 모습을 볼 수 있다.


(3) TodoListItem과 TodoList 만들기

* 이제 일정 관리 항목이 보일 TodoListItem과 TodoList를 만들 차례이다.

* 먼저 TodoListItem 컴포넌트부터 만들어 보자. components 디렉터리에 TodoListItem.js와 TodoListItem.scss를 생성해 보자.

* 그 다음에는 TodoListItem.js를 다음과 같이 작성해 보자.

import React from 'react';
import {
  MdCheckBoxOutlineBlank,
  MdCheckBox,
  MdRemoveCircleOutline,
} from 'react-icons/md';
import './TodoListItem.scss';

const TodoListItem = () => {
  return (
    <div className="TodoListItem">
      <div className="checkbox">
        <MdCheckBoxOutlineBlank />
        <div className="text">할 일</div>
      </div>
      <div className="remove">
        <MdRemoveCircleOutline />
      </div>
    </div>
  );
};

export default TodoListItem;

* 여기서는 다양한 아이콘을 불러와 사용했다. 아직 MdCheckBox 아이콘 컴포넌트는 사용하지 않은 상태인데, 이 아이콘은 나중에 할 일이 완료되었을 때 체크된 상태를 보여 주기 위해 사용할 아이콘이다.

* 이 컴포넌트를 다 작성했으면 TodoList.js 파일과 TodoList.scss 파일을 생성하고, TodoList.js 컴포넌트를 다음과 같이 작성해 보자.

import React from 'react';
import TodoListItem from './TodoListItem';
import './TodoList.scss';

const TodoList = () => {
  return (
    <div className="TodoList">
      <TodoListItem />
      <TodoListItem />
      <TodoListItem />
    </div>
  );
};

export default TodoList;

* 지금은 이 컴포넌트에 TodoListItem 을 불러와서 별도의 props 전달 없이 그대로 여러 번 보여주고 있다. 나중에는 여기에 기능을 추가하고 다양한 데이터를 전달할 것이다.

* 컴포넌트를 다 작성했으면 App에서 렌더링해 보자.

import React from 'react';
import TodoTemplate from './components/TodoTemplate';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';

const App = () => {
  return (
    <TodoTemplate>
      <TodoInsert />
      <TodoList />
    </TodoTemplate>
  );
};

export default App;

스타일링되지 않은 TodoList

* 이제 여기에 스타일링을 하자!

* 스타일링을 할 첫 번째 컴포넌트는 TodoList인데, 필요한 스타일이 그다지 많지 않다.

[TodoList.scss]

.TodoList {
    min-height: 320px;
    max-height: 513px;
    overflow-y: auto;
}

* 그 다음에는 TodoItemList을 스타일링하자.

[TodoListItem.scss]

.TodoListItem {
    padding: 1rem;
    display: flex;
    align-items: center;
    &:nth-of-type(even) {
        background-color: #f8f9fa;
    }
    .checkbox {
        cursor: pointer;
        flex: 1; // 차지할 수 있는 영역 모두 차지
        display: flex;
        align-items: center; // 세로 중앙 정렬
        svg {
            // 아이콘 스타일
            font-size: 1.5rem;
        }
        .text {
            margin-left: 0.5rem;
            flex: 1; // 차지할 수 있는 영역 모두 차지
        }
        // 체크되었을 때 보여줄 스타일
        &.checked {
            svg {
                color: #22b8cf;
            }
            .text {
                color: #abd5bd;
                text-decoration: line-through;
            }
        }
    }
    .remove {
        display: flex;
        align-items: center;
        font-size: 1.5rem;
        color: #ff6b6b;
        cursor: pointer;
        &:hover {
            color: #ff8787;
        }
    }

    // 엘리먼트 사이사이에 테두리를 넣어줌
    & + & {
        border-top: 1px solid #dee2e6;
    }
}

* 저장하고 화면을 확인해 보자.

스타일링 끝

* 컴포넌트의 스타일링이 모두 끝났다!