[Project] 카운터 앱01

올챙이·2023년 5월 29일

프로젝트

목록 보기
1/3
post-thumbnail

한 입 크기로 잘아 먹는 리액트의 Project1 카운터 앱을 만들어보겠습니다.
이번달(23.05)안에 리액트 관련 프로젝트를 한개 만들겠다고 계획을 세워놨었는데 지금 27일인데 가능할까요?
목차는 다음과 같습니다.

목차

프로젝트 준비

  • 요구사항 분석하기
  • 컴포넌트 단위로 생각하기
  • 리액트 앱 만들기

UI 구현하기

  • Viewer 컴포넌프 만들기
  • Controller 컴포넌트 만들기
  • 컴포넌트 스타일링하기

기능 구현하기

  • State를 이용해 카운터 기능 구현하기
  • State는 어떤 컴포넌트에 만들까?
  • 리액트답게 설계하기

프로젝트 준비하기

요구사항 분석하기

  • 'Sim ple Counter'라고 적힌 제목과 두 개의 영역으로 나누어져 있습니다.
  • 첫 번째 영역은 현재의 카운트를 표시하며 뷰어(Viewer)라고 합니다.
  • 두 번째 영역은 카운트를 늘리거나 줄일 수 있는 6개의 버튼이 가지런히 놓여 있으며 컨트롤러(Controller)라고 합니다.

컴포넌트 단위로 생각하기

  • 리액트에서 앱을 구현할 때는 컴포넌트 단위로 생각하는 게 필요합니다.
  • App 컴포넌트: Viewer와 Controller 컴포넌트를 감싸는 템플릿입니다.
  • Viewer 컴포넌트: 현재의 카운트를 표시합니다.
  • Controller 컴포넌트: 카운트를 제어할 수 있는 기능을 제공합니다.
  • 하나의 페이지를 하나의 컴포넌트로 구성해도 문제는 없지만 하나의 컴포넌트가 여러 기능을 갖게 되면 코드가 복잡해져 프로젝트를 관리하기가 어려워지기 때문에 최대한 잘게 쪼개어 개발하는 게 필요합니다.
  • 하나의 컴포넌트는 단 하나의 역할만 수행하는 게 좋습니다.

리액트 앱 만들기

  • 폴더 만들어주기
    리액트를 사용하기 위해서는 폴더명의 맨 앞이 소문자이어야 합니다.
  • Visual Studio Code에서 열기
    파일에 들어가서 생성했던 폴더를 찾아 넣어주면 다음과 같은 화면이 나옵니다.
  • 리액트 앱 생성하기
    Ctrl+J 단축키를 활용하여 터미널 창을 활성화 시켜준 뒤에 npx create-react-app .명령어를 입력하여 리액트 앱을 생성합니다.

    카운트 앱에 필요없는 파일을 삭제해줍니다.
    src/App.test.js
    src/logo.svg
    src/reportWebVitals.js
    src/setupTest.js

    카운트 앱에 필요없는 코드를 삭제해줍니다.

1.src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

2.src/App.js

import './App.css';
import Viewer from "./component/Viewer";

function App() {
 return (div className="App"></div>;
}
export default App;

UI 구현하기

UI(User Interface) : 사용자 인터페이스라는 뜻입니다. 쉽게 말해서 껍데기?

Viewer 컴포넌프 만들기

  • src 내부에 component 폴더 생성
  • component 내부에 Viewer.js 파일 생성
  • Viewer.js 파일 내용 작성
    카운트를 표시하기 위한 컴포넌트라 '현재 카운트: '라는 글을 넣어줍니다.
    일단 카운트 값은 0으로 고정해두겠습니다.
const Viewer = () => {
  return (
    <div>
      <div>현재 카운트: </div>
      <h1>0</h1>
    </div>
  );
};
export default Viewer;
  • App.js 수정하기
    Viewer 컴포넌트를 페이지에 렌더링하기 위해서는 App의 자식으로 배치해야합니다.
    하는김에 App 컴포넌트에서 페이지의 제목도 함께 추가해보겠습니다.
import './App.css';
import Viewer from "./component/Viewer";

function App() {
  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <Viewer />
      </section>
    </div>
  );
}
export default App;

2행에서 Viewer 컴포넌트를 App의 자식으로 배치해주었습니다.
7행에서 h1태그로 'Simple Counter'이라는 제목을 지정해주었습니다.
8~10행에서 section태그에 묶여있는 Viewer는 Viewer.js에서 작성했던 내용을 말합니다.

  • 실행 결과
    터미널 창에 npm run start 명령어를 입력하여 리액트를 브라우저에 실행시켜줍니다.

Controller 컴포넌트 만들기

  • component 내부에 Controller.js 파일 생성
  • Controller.js 파일 내용 작성
const Controller = () => {
  return (
    <div>
      <button>-1</button>
      <button>-10</button>
      <button>-100</button>
      <button>+100</button>
      <button>+10</button>
      <button>+1</button>
    </div>
  );
};
export default Controller;
  • App.js 수정하기
    Controller 컴포넌트를 페이지에 렌더링하려면 Viewer처럼 App의 자식으로 배치해야하기 때문에 App 컴포넌트를 수정해줍니다.
import './App.css';
import Controller from './component/Controller';
import Viewer from "./component/Viewer";

function App() {
  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <Viewer />
      </section>
      <section>
        <Controller />
      </section>
    </div>
  );
}
export default App;
  • 실행 결과

컴포넌트 스타일링하기

  • App.css 수정하기
body{
  padding:20px;
}

.App{
  margin: 0 auto;
  width: 500px;
}

//.App > section은 className=App 요소의 section 태그에만 적용됩니다.
.App > section{
  padding:20px;
  background-color:rgb(245,245,245);
  border:1px solid rgb(240,240,240);
  border-radius:5px;
  margin-bottom:10px;
}

기능 구현하기

State를 이용해 카운터 기능 구현하기

  • 아이디어
    "Controller 컴포넌트에 있는 버튼을 클릭하면, Viewer 컴포넌트에 있는 카운트가 증가하거나 감소해야 한다."

State는 어떤 컴포넌트에 만들까?

State는 반드시 컴포넌트 함수 안에 만들어야 합니다.
그리고 지금까지 만든 [카운터] 앱에는 App, Viewer, Controller 3개의 컴포넌트가 있습니다.
이 3개의 컴포넌트 중에 가장 적절한 경우가 무엇인지 살펴보겠습니다.

  • Viewer 컴포넌트
    Viewer 컴포넌트가 Controller 컴포넌트에 setCount를 전달할 방법이 없습니다.
    리액트에서 컴포넌트가 다른 컴포넌트에 데이터를 전달할 때는 Props를 사용합니다.
    Props는 부모만이 자식에게 전달할 수 있는데 Viewer와 Controller 컴포넌트는 부모-자식 관계가 아니므로 어떠한 값도 전달 할 수 없습니다.
import { useState } from "react";
const Viewer = () => {
  const [count, serCount]=useState(0);
  return (
    <div>
      <div>현재 카운트: </div>
      <h1>{count}</h1>
    </div>
  );
};
export default Viewer;
  • Controller 컴포넌트
    Viewer 컴포넌트와 같은 이유로 탈락입니다.
  • App 컴포넌트
    App.js를 다음과 같이 수정합니다.
import './App.css';
import Controller from './component/Controller';
import Viewer from "./component/Viewer";
import {useState} from "react";

function App() {
  const [count, setCount]=useState(0);
  const handleSetCount=(value)=>{
    setCount(count + value);
  };

  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <Viewer count={count} />
      </section>
      <section>
        <Controller handleSetCount={handleSetCount} />
      </section>
    </div>
  );
}
export default App;

Viewer.js를 다음과 같이 수정합니다.

const Viewer = ({ count }) => {
  return (
    <div>
      <div>현재 카운트: </div>
      <h1>{count}</h1>
    </div>
  );
};
export default Viewer;

Controller.js를 다음과 같이 수정합니다.

const Controller = ({handleSetCount}) => {
  return (
    <div>
      <button onClick={() => handleSetCount(-1)}>-1</button>
      <button onClick={() => handleSetCount(-10)}>-10</button>
      <button onClick={() => handleSetCount(-100)}>-100</button>
      <button onClick={() => handleSetCount(+100)}>+100</button>
      <button onClick={() => handleSetCount(+10)}>+10</button>
      <button onClick={() => handleSetCount(+1)}>+1</button>
    </div>
  );
};
export default Controller;

리액트답게 설계하기

  • 리액트에서 컴포넌트 간에 데이터를 전달할 때는 Props를 사용하는데, 전달 방향은 언제나 부모로부터 자식에게 전달하는 방식입니다.
    -이러한 데이터 전달 특징을 '단방향 데이터 흐름'이라고 합니다.

0개의 댓글