프론트 앤드(Front-End)/React

[React Class] 메모 앱 실습

RyanSin 2021. 1. 15. 19:00
반응형

- 지난 시간

안녕하세요. 지난 시간에는 Class Component Life Cycle(생명주기)에 대해서 알아봤습니다.

 

혹시 이해 못 하신 분들이나 놓치신 분들은, 아래 링크를 통해 지난 시간 글을 학습하고 오시는 걸 추천드리겠습니다. 

any-ting.tistory.com/10

 

[React Class] Class Component Life Cycle(생명주기)

- 지난 시간 지난 시간에는 하위(자식) -> 상위(부모) Comonent에게 값을 전달 방법에 대해서 알아봤습니다. 혹시 이해 못 하신 분들이나 놓치신 분들은, 아래 링크를 통해 지난 시간 글을 학습하고

any-ting.tistory.com

 

- 개요

이번 시간에는 지난 시간까지 알아본 React 기술들을 활용해서 간단한 메모 앱을 만들어 보도록 하겠습니다.

 

DB Server를 사용하지 않고 순수 React로만 만들어 보겠습니다.

 

저희가 만들 프로젝트는 아래와 프로젝트입니다.

 

결과 화면

간단하게 설명하면 오른쪽에 메모를 등록하는 컴포넌트와 왼쪽에는 등록한 메모를 보여주는 컴포넌트가 있습니다.

 

메모 데이터는 브라우저에 Local Storage를 사용했으며, 디자인 프레임워크로 react-bootstrap을 사용했습니다.

 

소스 코드는 하단에 링크를 기재해놨습니다. 실제 프로젝트를 구동해보시는 것도 좋을 것 같습니다.

 

- 프로젝트 구조 및 Component 생성

프로젝트 구조

입력을 받는 Input Component와 목록을 보여주는 List Component를 만들겠습니다.

 

1. 메모를 등록하는 Component (Input Component)

import React, { Component } from 'react';
import InputGroup from 'react-bootstrap/InputGroup'
import FormControl from 'react-bootstrap/FormControl'
import Button from 'react-bootstrap/Button'
import Card from 'react-bootstrap/Card'

const style = {
    Container: {
        display: "flex",
        flexDirection: "column",
    },
    RightContainer: {
        width: "100%",
        margin: "0 0 0 5px",
        fontSize: "14px",
        textAlign: "center",
        border: "1px solid gray",
    }
}

/**
 * @author Ryan
 * @description 메모를 등록하는 Component
 */
class Input extends Component {
    render() {
        return (
            <Card border="dark" style={style.RightContainer} >
                <Card.Header>메모 등록</Card.Header>
                <Card.Body>
              
                <InputGroup className="mb-3">
                <FormControl
                    placeholder="메모 내용"
                    aria-label="메모 내용"
                    aria-describedby="basic-addon2"
                    name="contents"
                    onChange={e => this.props.contentChange(e)}
                />
                <InputGroup.Append>
                    <Button variant="success" size="sm" style={style.Button} onClick={e => this.props.addItem(e)}>등록</Button>
                </InputGroup.Append>
                </InputGroup>
                </Card.Body>
            </Card>
        );
    }
}

export default Input;

2. 등록한 메모를 보여주는 Component

import React, { Component } from 'react';
import ListGroup from 'react-bootstrap/ListGroup'
import Card from 'react-bootstrap/Card'

const style = {
    LeftContainer: {
        width: "100%",
        margin: "0 5px 0 0",
        fontSize: "14px",
        textAlign: "center",
        border: "1px solid gray",
    },

    ListContainer:{
        overflow: "scroll",
        padding: "2px",
        height: "510px",
        borderTop: "1px solid #efefef",
    },

    ListItmes:{
        textAlign: "left",
    }
}

/**
 * @author Ryan
 * @description 등록한 메모 보여주는 Component
 */

class List extends Component {
    render() {
        return (
            <Card style={style.LeftContainer}>
                <Card.Header>메모 목록</Card.Header>
                <ListGroup style={style.ListContainer}>
                    {this.props.itemList.map(data => 
                        <ListGroup.Item key={data} style={style.ListItmes} onClick={e => this.props.removeItem(data)}>
                            {data}
                        </ListGroup.Item>
                    )}
                </ListGroup>
            </Card>
        );
    }
}

export default List;

 

실제 App.js 파일에서 두 Component를 불러와 적용하겠습니다.

 import List from './components/List';
 import Input from './components/Input';
 
 render() {
    return (
         <Container style={{marginTop: '30px'}}>
           <Row className="justify-content-center">
            <div style={style.BodyContainer}>
              <h1 style={style.Title}>React Class Memo Project</h1>
                <div style={style.Body}>

                  {/* 등록한 데이터를 목록으로 보여주는 Component */}
                    <List itemList={this.state.itemList} removeItem={this.removeItem}/>
                  
                  {/* 메모를 등록하는 Component  */}
                    <Input contentChange={this.contentChange} addItem={this.addItem} />
                </div>
            </div>
          </Row>
         </Container>
    );
  }

적용할 때 헷갈리시는 분들은 아래 풀 소스코드를 확인해주세요 :)

 

다음으로는 기능에 대해서 알아보겠습니다.

- 기능

/**
 * App.js 
 */
import React, { Component } from 'react';
import List from './components/List';
import Input from './components/Input';
import Container from 'react-bootstrap/Container'
import Row from 'react-bootstrap/Row'

const style = {

  MainContainer: {
    display: "flex",
    justifyContent: "center",
    marginTop: '30px',
    overflow: 'hidden',
    minHeight: "100vh",
  },

  BodyContainer: {
    width:"80%",
  },

  Title: {
    textAlign: "center",
    border: "1px solid gray",
  },

  Body: {
    display: "flex",
    height: "100%"
  },

}

class App extends Component {
  
  constructor(props){
    super(props)
    this.state = {
        itemList: [], //메모 아이템을 담을 배열
        contents: "", // 메모 내용
    }
    //이벤트 함수 등록
    this.contentChange = this.contentChange.bind(this)    
    this.addItem = this.addItem.bind(this)
    this.removeItem = this.removeItem.bind(this)
  }

  /**
   * @author Ryan
   * @description Component 최초 랜더링 시 데이터 불러와 적용
   */
  componentDidMount(){
    try {
      const items = localStorage.getItem("item")
      
      // items가 있다면 itemList state 업데이트
      if(items){
        this.setState({itemList: JSON.parse(items)})
      }     
    } catch (error) {
      console.log(error);
    }
  }

  /**
   * @author Ryan
   * @description 최종 state 값 localStorage 저장
   */  
  componentDidUpdate(nextProps, nextState, snapshot){

    //기존 state 값과 변경된 state 값이 다르면 변경 state 값 저장
    if(nextState.itemList.length !== this.state.itemList.length){
      localStorage.setItem("item", JSON.stringify(this.state.itemList))
    }
  }

  /**
   * @author Ryan
   * @descirption Input 입력 값을 받는 함수
   */
  contentChange = (e) =>{
    this.setState({
      contents : e.target.value
    })
  }

  /**
   * @author Ryan
   * @descirption 등록 버튼 클릭 함수
   */
  addItem = (e) => {
    e.preventDefault();

    this.setState( state => {
      return {
       itemList: state.itemList.concat(state.contents) // 배열에 데이터를 추가한다.
      }
    })
  }

  /**
   * @author Ryan
   * @description 메모 아이템 클릭시 삭제 함수
   */
  removeItem = (data) =>{
    this.setState(state => {
      return {
       itemList: state.itemList.filter(item => item !== data) //특정 아이템을 제외한 나머지 아이템을 새로운 배열을 만들어 반환 한다.
      }
    })
  }

  render() {
    return (
         <Container style={{marginTop: '30px'}}>
           <Row className="justify-content-center">
            <div style={style.BodyContainer}>
              <h1 style={style.Title}>React Class Memo Project</h1>
                <div style={style.Body}>

                  {/* 등록한 데이터를 목록으로 보여주는 Component */}
                    <List itemList={this.state.itemList} removeItem={this.removeItem}/>
                  
                  {/* 메모를 등록하는 Component  */}
                    <Input contentChange={this.contentChange} addItem={this.addItem} />
                </div>
            </div>
          </Row>
         </Container>
    );
  }
}

export default App;

 

App.js 파일 전체 소스코드입니다.

- Constructor : Component 생성자

  1. this.state : itemList 메모를 아이템을 등록하는 배열, contents 사용자가 입력한 데이터를 담아 놓는 문자열 변수
  2. this.contentChange & this.addItem & this.removeItem : 이벤트 함수를 등록합니다. (this를 참조하기 위해)

- Event Function : 사용자 이벤트 함수

  1. contentChange : 사용자가 입력한 값을 받아서 contents 변수에 추가
  2. addItem: 등록 버튼 클릭 이벤트 (itemList 배열에 아이템을 추가)
  3. removeItem : 목록 데이터를 클릭 시 데이터를 삭제 

기본적인 사용자가 입력한 값을 받고, 그 값을 등록하고, 그리고 삭제를 하는 함수입니다.

- Life Cycle  : React Class 생명주기

  1. componentDidMount() : Component가 최초 랜더링 시 데이터를 불러온다.
  2. componentDidUpdate() : 최종적으로 사용자가 입력한 값인 state 값을 Local Storage에 저장

- Props & State

List Component와 Input Component에 Props로 이벤트 함수와 State 값을 전달해줍니다.

각 Component에서 입력과 출력 이벤트가 발생하면 App.js Component에서 State값을 변경해줍니다.

 

이번 시간에는 지난 시간까지 배워 온 기술들을 활용해서 작은 프로젝트를 만들어 봤습니다.

 

 

- 소스 저장소

GitHub : github.com/Ryan-Sin/React-Class-Memo

 

Ryan-Sin/React-Class-Memo

Contribute to Ryan-Sin/React-Class-Memo development by creating an account on GitHub.

github.com