To-Do App 만들기

dev bourgeois·2023년 10월 30일
0

React A-Z Study

목록 보기
2/10
post-thumbnail

리액트 기본 구조

Create React App 폴더와 파일 기본 구조

*이름 수정되면 안되는 파일
1. public/index.html ➡️ 페이지 템플릿
2. src/index.js ➡️ js 시작점

*src 폴더 : 대부분의 리액트 소스 코드들 이곳에 작성하면 된다. 번들러는 여기서만 작동한다.

*Package.json 파일
-해당 프로젝트에 대한 정보들이 들어있다.
-프로젝트 이름, 버전, 필요한 라이브러리, 라이브러리 버전들이 명시되어 있다.
앱을 시작할 때 사용할 스크립트, 앱을 빌드, 테스트할 때 사용할 스크립트 등이 명시되어 있다.

웹팩

웹팩은 오픈 소스 자바스크립트 모듈 번들러로써 여러 개로 나누어져 있는 파일들을 하나의 자바스크립트 코드로 압축하고 최적화하는 라이브러리이다.

장점

  • 여러 파일의 자바스크립트 코드를 압축하여 최적화할 수 있기 때문에 로딩에 대한 네트워크 비용을 줄일 수 있다.
  • 모듈 단위로 개발이 가능하여 가독성과 유지보수가 쉽다.

React App 실행

npm run start -> 이 명령어로 리액트 앱을 시작할 수 있다.

package.json
-> npm run start, npm run build, npm run test ,,

*소스 코드 수정으로 텍스트 변경하기
-> App.js 파일에서 텍스트 수정


SPA(Single Page Application)

현재 App.js 파일의 소스 코드를 변경하면 변경한 부분이 화면에 적용된다.
이게 어떠한 순서로 실행되는지 알아본다

public/index.html
html 템플릿 파일이고 div 엘리먼트의 id가 root이다.

src/index.js
자바스크립트의 시작점이다. 여기에서 위에 root id를 가진 div 엘리먼트를 잡아준다.
그렇기에 그 엘리먼트 안에서 화면을 꾸밀 수 있게 되는 것이다.

📍여기서 생기는 의문점
index.html 템플릿이 하나면 한 개의 페이지를 만들 때는 괜찮은데 두 개 이상의 페이지를 만들면❓❓

이와 같은 방식은 전통적인 웹 사이트를 만들 때 사용하는 Multi Page Application이다.
하지만 요즘에는 이러한 방식을 사용하지 않는다.

웹사이트 전체 페이지를 하나의 페이지에 담아 동적으로 화면을 바꿔가며 표현
이것을 SPA(Single Page Applicaton)이라고 한다.

*SPA에서 화면 변경은 어떻게

➡️ 자바스크립트 영역에서 HTML 5의 History API를 사용해서 페이지 전환(브라우징)
-현재 페이지 내에서 화면 이동이 일어난 것처럼 작동하게 해준다.


할 일 목록 앱 소개 및 JSX

App.js의 function App()

JSX(Javascript syntax extension)

자바스크립트의 확장 문법이다. 리액트에서는 JSX를 이용해서 화면에서 UI가 보이는 모습을 나타내 준다.
JSX를 이용하면 UI를 나타낼 때 자바스크립트(logic)와 HTML구조(markup)를 같이 사용할 수 있기 때문에 기본 UI에 데이터가 변하는 것들이나 이벤트들이 처리되는 부분을 더욱 쉽게 구현할 수 있다.
또한, createElement를 쉽게 사용하기 위해 사용한다.

이는 매우 편리하기 때문에 거의 모든 사람이 사용한다.

*원래 리액트에서 화면을 그리는 방식
React.createElement API를 사용해서 엘리먼트를 생성한 후(객체가 되낟) 이 엘리먼트를 In-Memory에 저장한다. 그리고 ReactDom.render 함수를 사용해서 실제 웹 브라우저에 그려준다.

*JSX는 createElement를 쉽게 사용하기 위해 사용

모든 UI를 만들때 마다 createElement를 사용해서 컴포넌트를 만들 수는 없다.
그렇기 때문에 JSX를 사용한 후 그걸 바벨(babel)이 다시 createElement로 바꿔서 사용한다.

주의해야 할 JSX 문법 규칙

  • 컴포넌트에 여러 엘리먼트 요소가 있다면 반드시 부모 요소 하나로 감싸기
  • JSX Key 속성 → 요소의 리스트 나열할 때 Key 넣어주기 → 가상 돔/바뀐 부분 찾을 때 사용
  • key는 유니크한 값(index (x))

*중괄호 두 번 사용하는 것
{{display: 'flex'}}
-> 이는 React에서 Javascript 표현식을 JSX 속성에 전달하는 방식이다.
외부 중괄호는 JSX에서 Javascript 표현식을 포함시키고,
내부 중괄호는 Javascript 객체를 만든다.

예를 들어, <form style={{display: 'flex'}}>...</form>에서

  • 외부 중괄호는 JSX에서 Javascript 표현식을 사용하겠다는 것을 나타내고,
  • 내부 중괄호 {display: 'flex'}는 Javascript 객체를 만든다. 이 객체는 style 속성에 전달되고 해당 객체의 'display' 속성이 'flex'로 설정된다.

    이중 중괄호를 사용하는 것은 React에서 스타일을 동적으로 설정하거나 Javascript값을 JSX에 삽입하는 일반적인 방법으로 이렇게 함으로써 Javascript 코드를 JSX에 통합할 수 있으며, 동적으로 스타일과 데이터를 처리할 수 있다.

할 일 목록 앱 만들기 시작

  1. 원래 있던 부분들 지우기 -> App.js App.css, index.css
  2. 바탕 색 바꾸기
  3. 컨테이너 만들기


  4. 할 일 목록 텍스트 넣기

할 일 목록 UI 만들기(JSX, CSS 작성)


1. 하나의 목록 JSX(HTML) 추가하기


2. 하나의 목록 스타일링하기(CSS)
(1) 버튼 스타일링

(2) 리스트 스타일링
체크 박스에 체크 표시를 하면 줄이 가게 만들기 위한 스타일링
=> 동적으로 만들어야 하기 때문에 함수로 만든다

  1. 목록 하나 더 추가하기(하드코딩 -> ❌)

map() 메소드를 사용한 할 일 목록 나열

할 일 목록을 하드코딩 X ➡️ map() 메소드 사용

*map 메소드

const array1 = [1, 4, 9, 16];
const map1 = array1.map(x => x*2);
console.log(map1);
// expected ouptput : Array [2, 8, 18, 32]
  1. 할 일 목록 데이터 만들기
  1. map 메소드 사용해서 데이터 나열하기

JSX Key 속성

map 메소드를 사용해서 데이터를 나열할 때 key 속성을 넣어주었다.

리스트에 key 속성을 넣지 않는다면❓
error 발생

리액트에서 요소의 리스트를 나열할 때에는 Key를 넣어줘야 한다.
키는 React가 변경, 추가 또는 제거된 항목을 식별하는 데 도움이 된다.
(가상돔에서 바뀐 부분을 찾을 때 이용)
요소에 안정적인 ID를 부여하려면 배열 내부의 요소에 키를 제공해야 한다.

Key에는 Unique한 값을 넣어줘야 된다.
index 비추천 -> index도 0부터 시작해서 유니크한 값을 가지지만 리스트가 추가되거나 제거되면 key값도 바뀌게 된다.


filter 메소드를 사용해서 할 일 목록 지우기

*filter 메소드

const words = {'spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'};
const result = words.filter(word => word.length > 6);
console.log(result);
// expected out.put: Array ['exuberant', 'destruction', 'present']


1. 클릭 이벤트 발생 시 함수 호출하기

  1. 함수 안에서 할 일은 클릭한 목록 지우기

    id -> 파라미터로 넘겨진, 삭제하기 위해 버튼을 누른 데이터 목록에 해당된다

✅ 로그에서 보면 newTodoDate 클릭한 목록이 지워지긴 했는데 화면에서는 아무런 변화x


React State

state, value

리액트에서 데이터가 변할 때 화면을 다시 렌더링 해주기 위해서 사용해야 한다.
React State란, 컴포넌트의 렌더링 결과물에 영향을 주는 데이터를 갖고 있는 객체이다.
State가 변경되면 컴포넌트는 Re-rendering 된다. 또한, State는 컴포넌트 안에서 관리된다.

  1. 리액트 State 생성하기
  1. State를 이용한 할 일 목록 삭제하기

☑️ state를 바꾸려면


할 일 목록 추가하기


*UI 부분
1. 할 일 목록을 입력하는 부분

  1. 입력 버튼

*기능 부분
1. 글 입력시 state 변경

화면에 입력이 안됨 -> 렌더링이 안되고 있기 때문 -> state 사용!

  1. 입력 버튼 클릭 시 목록에 추가하기

  2. 입력란에 있던 글씨 지워주기


전개 연산자

특정 객체 또는 배열의 값을 다른 객체, 배열로 복제하거나 옮길 때 사용 (…)
배열 조합, 객체 조합, 기존 배열을 보존


마무리 된 일 표시하기(조건부 삼항 연산자)

*UI 부분
1. 글 중앙에 선 긋기

  1. completed true인 목록에만 선 긋기

  1. 체크 박스 클릭해서 완료 상태 바꾸기

*조건부 삼항 연산자

if (a) {
  a = "a";
}
else {
  a = "b";
} 
a ? a = "a" : a = "b";

*코드

import React, {Component} from "react"; // 라이브러리에서 컴포넌트 가져오기
import "./App.css";

// 클래스형 컴포넌트
export default class App extends Component { // 컴포넌트를 사용할 수 있게 상속받음

  state = {

    todoData : [
      /*{
        id: "1",
        title: "공부하기",
        completed: false
      },
      {
        id: "2",
        title: "청소하기",
        completed: false
      }*/
    ],
    value: "" 
    // 입력한 값 들어있음
  };

  btnStyle = {
    color: "#fff",
    border: "none",
    padding: "5px 9px",
    borderRadius: "50%",
    cursor: "pointer", // 마우스 커서 모양 
    float: "right" // 요소의 플로팅 설정 -> 값을 오른쪽으로 플로팅
  }
 
  // 동적 요소 함수로 설정
  getStyle = (completed) => {
    return {
      padding: "10px",
      borderBottom: "1px #ccc dotted",
      textDecoration: completed ? "line-through" : "none"
    }
  }

  handClick = (id) => {
    let newTodoData = this.state.todoData.filter(data => data.id !== id);
    console.log('newTodoData', newTodoData);
    this.setState({todoData: newTodoData});
  }

  handleChange = (e) => {
    console.log('e', e.target.value);
    this.setState({value: e.target.value});
  }

  handleSubmit = (e) => {
    // form 안에 input 전송할 때 페이지 리로드 막아줌
    e.preventDefault();

    // 새로운 할 일 데이터
    let newTodo = {
      id: Date.now(),
      title: this.state.value,
      completed: false
    }

    // 원래 있던 할 일에 새로운 할 일 더해주기
    // 입력하면 value 빈값으로
    this.setState({todoData:[...this.state.todoData, newTodo], value: "" });
  };

  handleComleteChange = (id) => {
    let newTodoData = this.state.todoData.map(data => {
      if (data.id === id) {
        data.completed = !data.completed;
      }
      return data;
    });
    this.setState({ todoData: newTodoData});
  };


 
  render() { // 컴포넌트 내 메소드, render() 내 UI 작성 / 함수형 컴포넌트에서는 RENDER 메소드 필요 없이 바로 UI 작성 가능
    // 일반 html과 달리 class 대신 className 사용
    return (
      <div className="container">
        <div className="todoBlock">
          <div className="title">
            <h1>할 일 목록</h1>
          </div>

      {this.state.todoData.map(data => (
         <div style={this.getStyle(data.completed)} key={data.id}>
         <input type="checkbox" defaultChecked={false} onChange={() => this.handleComleteChange(data.id)}  />
          {data.title}
         <button style={this.btnStyle} onClick={() => this.handClick(data.id)}>x</button>
       </div>
      ))}

      
      <form style={{display: 'flex'}} onSubmit={this.handleSubmit}>
        <input 
          type="text" 
          name="value" 
          style={{flex: '10', padding: '5px'}}
          placeholder="해야 할 일을 입력하세요."
          value={this.state.value}
          onChange={this.handleChange}
        />

         <input
          type="submit"
          value="입력"
          className="btn"
          style={{flex: '1'}}
        />
      </form>
      </div>
    </div>
    )
  }
}

0개의 댓글