React + 1

LEE EUI JOO·2023년 2월 6일
0

Web Programming

목록 보기
10/17


리액트를 위해 HTML, CSS, JavaScript, TypeScript 가 선수 과목, TypeScript는 필수는 아님

  • SPA(Single Page Application) : 하나의 화면 (Index.html)을 가지고 애플리케이션의 모든 요청을 처리하는 방식

  • MPA(Multi Page Applicaion) : 여러 개이 파일을 번갈아가면서 애플리케이션이 요청을 처리하는 방식

  • react 는 싱글 페이지 애플리케이션 개발을 위한 Front End JavaScript Framework


Template Engine (템플릿 엔진)

  • HTML 문서를 서버에서 처리한 결과 데이터와 결합해서 출력해주는 소프트웨어

    • 동적인 HTML 문서를 만들기 위해서 필요

    • 서버 측, 클라이언트 측에 모두 필요하다

    • 서버 측 Template Engine 은 HTML을 생성해 내고, Front End 측은 DOM 객체의 조합을 만들어 낸다

    • angular, react, vue 는 Front End 에서 동작하는 템플릿 엔진


React

  • 페이스 북에서 만든 오픈 소스 자바스크립트 프레임 워크

  • 유저 인터페이스를 생성하는 것이 목적

  • Virtual DOM 을 이용해서 랜더링을 수행하고 JSX 문법을 이용해서 작성

  • Virtual DOM 은 게임 엔진에서 랜더링 할 때 사용하는 방식으로 화면을 다시 출력해야 할 때 전체를 다시 출력하는 것이 아닌, 변경된 부분만 재출력하는 방식을 사용

  • Virtual DOM 이 HTML 의 실제 DOM 보다 빠른 이유는 DOM 자체를 출력하는 속도는 HTML의 실제 DOM 이 빠르지만,
    실제 DOM은 변화가 생기면 DOM을 생성하고 CSS를 다시 적용해서 출력하는 형태로 동작하기 때문에 이전과 동일한 CSS 내용이나 레이아웃을 읽어서 출력하지만 Virtual DOM 을 사용하게 되면 디자인은 그대로 두고 데이터만 변경하는 형태로 동작을 하기 때문에 빠름

  • React 는 오로지 '화면 출력' 만 담당

  • 데이터를 가져오는 부분이나 상태 관리에 대한 부분은 별도의 라이브러리를 사용

    • react를 할 때는 ajax 나 fetch API, axios 라이브러리 등의 데이터를 가져오는 자바스크립트 코드를 작성을 할 수 있어야 하고, redux 나 context API 와 같은 상태 관리 라이브러리를 사용할 수 있어야 함

개발 환경 구성 및 프로젝트 생성 및 실행

개발환경 구성

  • node.js 설치 - react에서 사용하는 webpack 이나 babel 이 자바스크립트 런타임인 node.js를 사용

    • 짝수 버전(안정화) vs 홀수 버전 (개발 중인 버전)

  • 노드 설치 확인 : terminal에서 node -v
  • 선택적으로 yarn 설치 : npm install --loaction=global yarn
    • node 에는 npm 이라는 패키지 관리자가 존재하는 데 더 나은 속도 와 더 나은 캐싱 시스템을 사용하기 위함
    • yarn 설치확인 : yarn --version

  • webpack : 프로젝트에서 사용된 파일을 분석해서 웹 문서 파일로 변환하는 Module Bundler

    • react 프로젝트를 만들어서 실행할 때 사용
    • react 는 js,css,jpg 와 웹 문서 파일을 사용하지 않고 sass 같은 파일들을 이용하는데 이 파일들을 해석해서 웹 브라우저에 표시를 해줘야 하는데 사용되는 프로그램이 webpack 이다
  • babel : ECMA 2015(ES6) 이상의 코드를 javascript 엔진에서 사용할 수 있도록 번역해주는 javascript 트랜스 컴파일러

  • IDE - Visual Studio

    • 플러그 인
      • prettier (코드 스타일링)
      • esling(문법 확인)
      • elative path(경로작성 쉽도록)
      • guides(들여 쓰기 가이드라인)
      • react code snippets(자주 사용하는 코드를 단축어로 등록)
      • tailwind css, headwind, post css 등
  • typescript 컴파일러 설치 및 확인

    • npm i -g typescript ts-node
    • 확인
      • tsc -v
      • ts-node -v
  • scoop : 윈도우 사용자가 brew 나 apt 같은 프로그램을 사용하고자 하는 경우


🔎 자바 스크립트는 자료형을 작성하지 않음 - 컴파일 시 에러를 잡아내지 못함

let a = 10

🔎 타입 스크립트는 자료형을 명시

a:number = 10

프로젝트 생성

  • npm 또는 yarn create react-app 앱이름 (--template typescript 옵션을 추가하면 타입스크립트용 프로젝트
# vscode 의 터미널에서

$ npm create react-app testapp 


프로젝트 구조

  • node_modules 디렉토리 : 노드 패키지 디렉토리로 설치한 노드 패키지들이 저장되는 디렉토리
    • 소스 코드를 이동할 때는 불필요한 디렉토리
    • 이 디렉토리를 삭제한 후 다른 곳에서 npm install 명령만 수행하면 다시 생성될 것임
cd testapp
npm istall

  • Public 디렉토리 : 정적인 파일을 저장하기 위한 디렉토리

  • manifest.json : 앱에 대한 정보

  • robots.txt : 검색 로봇에게 사이트 및 웹 페이지를 수집할 수 있도록 허용하거나 제한하는 국제 권고안

  • src 디렉토리

    • App.js(Apps.tsx - 타입스크립트 프로젝트) : 첫 화면에 보여질 컴포넌트
    • APP.css
    • APP.test.js
    • index.css
    • index.js : Entry Point
    • reportWebVitals.js : 성능 축적을 위한 파일
    • setupTests.js : 테스트 준비 파일
  • package.json : 노드 애플리케이션의 설정 파일

  • package-lock.json : 라이브러리의 의존성을 자세히 출력하는 파일 - 없어도 됨

  • README.md : 마크 다운 파일


실행

  • 개발 모드로 실행 : 디렉토리로 이동하여 npm run start(yarn 으로 생성한 경우 yarn start 가능)

  • localhost:3000 으로 확인 가능 - react 로고가 보일 것

  • 빌드 모드 실행
    • npm run build (번들 파일이 생성)


리액트 프레임워크 구성 요소

virtual DOM

  • 메모리에 만들어진 가상의 DOM
  • 실제 화면에 출력이 되는 것은 물리적인 DOM인데 react에서는 virtual DOM을 메모리 만들어두고 새로운 출력이 요청되면 virtual DOM과 물리적인 DOM의 변경된 부분만 랜더링을 하는 방식으로 출력

Javascript XML(JSX)

  • eact에서 componenet를 작성하기 위한 문법

componenet

  • 재사용이 가능한 부품, react에서는 재사용을 효율적으로 하기 위한 몇 가지 기능이 추가되어 있음

리액트 프레임워크의 패키지

react

  • 앱이 동작하는 환경과 무관하게 공통으로 사용하는 기능을 제공하는 패키지

react-dom 패키지

  • 앱이 동작하는 환경에 종속적인 패키지
    • react-dom/client : CSR(Client Side Rendering)을 위한 패키지
    • react-dom/server : SSR(Server Side Rendering)을 위한 패키지
    • react-native : android나 iOS에서 동작하는 앱을 만들기 위한 패키지

Virtual DOM

Markup Language

  • 사람이 쉽게 작성하고 컴퓨터가 이해할 수 있도록 만든 텍스트
  • HTML(브라우저가 해석)이나 XML(사용자가 해석) 등이 존재

DOM(Document Object Model)

  • 웹 브라우저는 HTML 형식의 문자열을 화면에 출력할 때 메모리 할당을 위해서 자바스크립트 객체로 변경을 하게 되는데 이러한 자바스크립트 객체를 DOM이라고 한다.
  • 태그 생성
document.createElement(태그이름[, 옵션])
  • 예시) index.js 파일에서 사용됨
index.js 파일에서 사용됨

JSX

  • 자바스크립트에 XML을 추가한 확장형 문법

  • XML이나 HTML은 동적인 데이터를 가지고 DOM을 생성하지 못함

  • 동적인 데이터를 가지고 DOM을 생성하고자 할 때는 JavaScript의 도움을 받아야 함

  • JavaScript와 XML을 혼용해서 사용할 수 있는 문법으로 코드가 번들링될 때 babel-loader를 사용해서 자바스크립트 구문으로 변경해서 실행

  • 장점

    • 자바스크립트만드로 작성하는 것보다 알아보기 쉬움
    • 오류가 있으면 바벨이 코드를 변환하는 과정에서 이를 감지하기 때문에 디버깅이 수월, 최근에는 TypeScript로 작성하는 것을 권장
  • 규칙

    • 주석

      주석
      
      {/* 주석 내용 /}
    • 만들거나 태그 안에서는 // 나 / 주석 /을 이용해서 작성이 가능한데 이 경우 닫는 것은 다음 줄에 입력이 되어야 함

      <img // 주석
      />
    • 모든 태그는 반드시 닫아야 하고 순서대로 닫아야 함

    • 모든 요소는 반드시 하나의 태그 안에 배치가 되어야 함

    • 하나의 컴포넌트에서 root는 한 개


JavaScript 표현식을 사용할 때는 {} 안에 기재

  • /testapp/src/App.js

import logo from './logo.svg';
import './App.css';

import React, {Fragment} from 'react';

function App() {
  const message = "자바스크립트의 표현식";
  return (
    <>
      <h1>React App</h1>
      <h2>{message}</h2>
    </>
  );
}

export default App;

JavaScript 표현식에 if를 사용할 수 없으므로 삼항 연산자를 이용

import logo from './logo.svg';
import './App.css';
import React, {Fragment} from 'react';

function App() {
  const message = "자바스크립트의 표현식";
  let condition = false;
  let data = null;

  return (
  <>
      <h1>React app</h1>
      <h2>{message}</h2>
      <p>{condition ? "참" : "거짓"}</p>
      <p>{condition && "참"}</p>
      <p>{data}</p>
  </>
    
  );
}

export default App;

태그 내부에 스타일을 설정할 때 스타일은 자바스크립트 객체 형태로 작성해서 사용

  • class 대신에 className을 이용 - 경고 발생(for 속성도 그대로 사용하면 경고)
  • 스타일의 속성에 _가 있으면 제거하고 사용해야 하고 camel case 표기법을 사용한다.

Camel Case

클래스는 대문자로 시작하고 속성과 메서드는 소문자로 시작하는데,
두 번째 단어의 시작은 대문자로 명명하는 방식

Snake Case
상수의 이름은 모두 대문자로 표기

헝거리언 표기법
접두어를 붙이는 방식으로 데이터의 범위와 자료형에 대한 접두어를 사용하는 방식
g_자료형
m_
_
파이썬에서는 _를 사용하면 안됨

import logo from './logo.svg';
import './App.css';

import React, {Fragment} from 'react';

function App() {
  const style = {
    backgroundColor:"black",
    color:"aqua",
    fontSize:"48px",
    fontWeight:"bold",
    padding:16
  }
  return (
    <>
      <h1 style={style}>React App</h1>
      <h2 className='react'>React App</h2>
    </>
  );
}

export default App;


Component

개념

  • MVC(Model View Controller) : 기능 별로 나누어서 구현

    • Model : 데이터베이스에 작업을 수행하는 Persistance 계층 과 Bussiness Logic을 수행하는 Service 계층을 합친 것

    • View : 화면 출력 부분

    • Controller : Model 과 View 사이를 연결

  • MVC 구조의 View를 독립적으로 구성한 개체

  • Component를 여러 개 조합해서 하나의 화면을 생성

  • 데이터의 변경이나 이벤트가 발생하면 화면을 리랜더링 하지 않고 Component를 리랜더링 하는 개념

  • 여러가지 기능을 소유하고 있기 때문에 단순한 View 와는 다르다

  • 서버 사이드 템플릿 엔진은 데이터를 출력하는 용도 외에는 사용할 수 없는데 Component 는 여러 가지 기능을 내장한다


종류

  • 함수형 컴포넌트 : 함수를 가지고 생성

  • 클래스형 컴포넌트 : 클래스를 가지고 생성

  • ❓ 함수가 클래스의 인스턴스 보다는 가볍고, 클래스가 함수보다는 안전하다

  • 최근에는 속도 때문에 함수형 컴포넌트를 권장함 - Life Cycle(수명주기) 를 이용하는 경우가 아니라면 함수형 컴포넌트를 사용


클래스형 컴포넌트 생성 및 사용

  • 생성
    • 컴포넌트 클래스를 상속해서 생성
    • render 함수를 구현해서 출력할 JSX를 리턴
  • 파일의 확장자는 js 나 jsx를 사용(TypeScript 를 사용하는 경우는 ts 나 tsx로 작성)
  • src 디렉토리에 jsx 파일을 생성하고 작성

기본 틀 - MyComponent.jsx


import React, {Component} from "react";

class MyComponent extends Component{
    render(){
        return(
            <div>
                컴포넌트 첫번째
            </div>

        )
    }

}

export default MyComponent
  • 대부분의 경우 index.js를 수정하지 않고 App.js 파일을 수정해서 MyComponent를 사용 - app.js 수정

App.js

import logo from './logo.svg';
import './App.css';

import React, {Fragment} from 'react';

import MyComponent from './MyComponent';

function App() {
  
  return (

    <MyComponent />
  );
}

export default App;

  • 자바스크립트 로직을 추가할 수 있음

MyComponent.jsx

import React, {Component} from "react";

class MyComponent extends Component{
    render(){

        const isLoading = true;
        if(isLoading){
            return <p>Loading...</p>
        }
        
        return(
            <div>
                컴포넌트 첫번째
            </div>

        )
    }

}

export default MyComponent


props - properties 의 약자로 태그의 속정

  • 컴포넌트를 만들 때 이름 = 값의 형태로 설정

  • 클래스 형 컴포넌트에서는 this.props.name 이라는 속성으로 내부에서 사용이 가능

    • 사용하는 이유는 상위 컴포넌트에서 하위 컴포넌트에게 데이터를 전달할 목적

    • 컴포넌트 내부에서 수정할 수 없다.

    • props 값이 수정되면 자동으로 리렌더링 된다.

  • App.js 에서 MyComponent 에서 출력하는 부분을 수정

import logo from './logo.svg';
import './App.css';

import React, {Fragment} from 'react';

import MyComponent from './MyComponent';

function App() {
  
  return (

    <MyComponent name ="lee" />
  );
}

export default App;
  • MyComponent.jsx 파일 수정
import React, {Component} from "react";

class MyComponent extends Component{
    render(){

        console.log(this.props.name);

        
        return(
            <div>
                컴포넌트 첫번째 : {this.props.name}
            </div>

        )
    }

}

export default MyComponent

  • 변수를 넣어서 작성도 가능

MyComponent.jsx - data 변수

import React, {Component} from "react";

class MyComponent extends Component{
    render(){

        console.log(this.props.name);

        let data = this.props.name;

        
        return(
            <div>
                컴포넌트 첫번째 : {data}
            </div>

        )
    }

}

export default MyComponent

  • props 의 값을 변경하고자 하면 다른 변수를 생성해서 수행해야 한다

MyComponent.jsx

class MyComponent extends Component{
    render(){

        console.log(this.props.name);
        //props 의 값을 컴포넌트 내부에서는 변경할 수 없음
        //this.props.name = "steven"

        //props 의 값을 변경하고자 하면 다른 변수를 생성해서 수행해야 한다

        let data = this.props.name;
        data = "steven";

        
        return(
            <div>
                컴포넌트 첫번째 : {data}
            </div>

        )
    }

}

export default MyComponent


함수형 컴포넌트 - 함수를 만들어서 출력할 내용을 리턴

  • props로 전달된 데이터는 함수의 매개변수가 된다.

  • MyComponent.jsx 파일을 수정해서 함수형 컴포넌트로 변경

MyComponent.jsx

import React, {Component} from "react";

const MyComponent = () => {
    return(
        <div>함수형  컴포넌트 </div>
    )
}

export default MyComponent

  • props 를 쓰고자 한다면 매개변수에 props

MyComponent.jsx

import React, {Component} from "react";

const MyComponent = (props) => { //매개변수로 props 입력
    return(
        <div>함수형  컴포넌트 : {props.name} </div>
    )
}

export default MyComponent

App.js

import logo from './logo.svg';
import './App.css';

import React, {Fragment} from 'react';

import MyComponent from './MyComponent';

function App() {
  
  return (

    <MyComponent name ="lee" />
  );
}

export default App;

  • 함수형 컨포넌트에서 props 기본값 설정

App.js

import logo from './logo.svg';
import './App.css';

import React, {Fragment} from 'react';

import MyComponent from './MyComponent';

function App() {
  
  return (

    <MyComponent/>
  );
}

export default App;

MyComponent.jsx

import React, {Component} from "react";

const MyComponent = (props) => {
    return(
        <div>함수형  컴포넌트 : {props.name} </div>
    )
}

MyComponent.defaultProps = {
    name : "기본값 설정"
}

export default MyComponent


태그 안의 내용 활용

  • 태그 안의 내용은 props.children으로 접근이 가능

App.js

import logo from './logo.svg';
import './App.css';

import React, {Fragment} from 'react';

import MyComponent from './MyComponent';

function App() {
  
  return (
    <>
    <MyComponent>태그 안의 내용1</MyComponent>
    <MyComponent>태그 안의 내용2</MyComponent>
    </>
  );
}

export default App;

MyComponent.jsx

import React, {Component} from "react";

const MyComponent = (props) => {
    return(
        <>
        <div>함수형  컴포넌트 : {props.name} </div>
        <div>태그 안의 내용 : {props.children}</div>
        </>
    )   
}

MyComponent.defaultProps = {
    name : "기본값 설정"
}

export default MyComponent

  • 구조 분해 할당

App.js

import logo from './logo.svg';
import './App.css';

import React, {Fragment} from 'react';

import MyComponent from './MyComponent';

function App() {
  
  return (
    <>
    <MyComponent>태그 안의 내용1</MyComponent>
    <MyComponent>태그 안의 내용2</MyComponent>
    </>
  );
}

export default App;

MyComponent.jsx

import React, {Component} from "react";

const MyComponent = (props) => {
    const {name, children} = props; //구조 분해 할당 내용
    return(
        <>
        <div>함수형  컴포넌트 : {props.name} </div>
        <div>태그 안의 내용 : {props.children}</div>
        </>
    )   
}


MyComponent.defaultProps = {
    name : "기본값 설정"
}

export default MyComponent
  • 다른 방법의 구조 분해 할당

MyComponent.jsx

import React, {Component} from "react";

const MyComponent = ({name,children}) => {
    //const {name, children} = props;
    return(
        <>
        <div>함수형  컴포넌트 : {name} </div>
        <div>태그 안의 내용 : {children}</div>
        </>
    )   
}


MyComponent.defaultProps = {
    name : "기본값 설정"
}

export default MyComponent

유효성 검사

  • 필수 나 자료형을 설정해두는 것이 가능
  • prop-types 를 import 해서 설정
    • 컴포넌트이름.propTypes = {속성이름 : prop-types.자료형}
  • props 의 값이 문자열이 아닌 경우는 {} 안에 작성해야 한다

MyComponent.jsx


import React from 'react';
import PropTypes from 'prop-types';

const MyComponent = ({name, year, children}) => {
    return(
        <>
            <div>함수형 컴포넌트: {name}</div>
            <div>태그 안의 내용: {children}</div>
            <div>년도: {year}</div>
        </>
    )
}

MyComponent.propTypes = {
    name:PropTypes.string
}

MyComponent.defaultProps = {
    name:"기본값",
    year:PropTypes.number.isRequired
}

export default MyComponent;

App.js

import './App.css';

import React from 'react';

import MyComponent from './MyComponent';

function App() {
  return (
    <MyComponent name='adam' year={2023}>태그 안의 내용</ MyComponent>
  );
}

export default App;

자료형 Check

  • array : 배열

  • bool : boolean

  • func : 함수 - 자바스크립트에서는 함수도 하나의 자료형

  • number : 숫자

  • object : 객체

  • string : 문자열

  • node : 화면에 출력될 수 있는 모든 것

  • element : react 요소

  • any : 모든 자료형

  • instanceOf(클래스 이름) : 클래스로부터 파생된 인스턴스
  • OneOf([데이터 나열]) : 나열된 데이터 중 하나 - 데이터를 나열하기 때문에 enum에 가까움
  • oneOfType([자료형 나열]) : 나열된 자료형 중 하나

클래스형 component 에서 props 사용

App.js

import MyComponent from './MyComponent';

function App() {
  return (
    <>
      <MyComponent name="아담" favoriteNumber={3} children="my son"/>
    </>
  )
}
export default App;

MyComponent.jsx

import React, {Component} from 'react';
import PropTypes from 'prop-types';

class MyComponent extends Component {
    render() {
      const { name, favoriteNumber, children } = this.props; // 비구조화 할당
      return (
        <div>
          안녕하세요, 제 이름은 {name} 입니다. <br />
          children 값은 {children}
          입니다.
          <br />
          제가 좋아하는 숫자는 {favoriteNumber}입니다.
        </div>
      );
    }
  }

  MyComponent.defaultProps = {
    name:'기본 이름'
  }

  MyComponent.propTypes = {
    name:PropTypes.string,
    favoriteNumber:PropTypes.number.isRequired
  }

  export default MyComponent;


Component 의 Props - react 공식 문서

  • All React Component must act like pure functions with respect to their props

  • 모든 react component는 그들의 props에 관해서는 Pure 함수 같은 역할

  • pure function(순수 함수)
    • 입력으로 받은 매개변수의 데이터를 수정하지 않는다
    • 외부의 데이터에 영향을 주지 않는 함수
  • 동일한 입력이 주어지면 동일한 결과를 리턴해야 한다

state

  • props 는 상위 컴포넌트가 하위 컴포넌트에게 넘겨주는 데이터 - 데이터를 상위 컴포넌트에서 만들기 때문에 하위 컴포넌트에서 변경하지 못하도록 함

  • state 는 컴포넌트 내부에서 만드는 데이터로 읽기와 수정 모두 가능

  • 초기화

    • class 형 컴포넌트의 경우는 생성자나 render 메서드 외부에서 수행
    • this.state = {상태이름:값, 상태이름:값...} 형태로 초기화
  • get

    • this.state.상태이름
  • set

    • setState({상태이름:값, 상태이름:값...}) 을 이용해서 변경

화면에 출력될 Component 를 생성 - ClassComponent.jsx

App.js

import MyComponent from './MyComponent';

import ClassComponent from './ClassComponent';
function App() {
  return (
    <>
      <ClassComponent/>
      <MyComponent name="아담" favoriteNumber={3} children="my son"/>
    </>
  )
}
export default App;

ClassComponent.jsx

import React, { Component } from "react";

class ClassComponent extends Component{

    //생성자

    constructor(props){
        super(props);

        this.state = {
            number:0
        }
    }
    render(){
        return(
            <>
                <p>number:{this.state.number}</p>
                <button onClick={()=>{
                    this.setState({
                        number: this.state.number + 1
                    })
                }}>증가</button>
            
            </>

        )
    }
}
export default ClassComponent;

  • 실행을 해서 버튼을 누르면 숫자가 증가하면서 출력된다

    • react 에서는 props 나 state 값이 변경이 되면 Component 가 다시 출력된다
  • react 에서는 props 나 state 값을 직접 변경하는 것이 허용되지 않는다

    • props 나 state 는 변경할 수 없는 데이터로 만들어 진 것(Virtual DOM)

    • 변경하고자 하면 다른 곳에 복제를 해서 변경을 해야한다.

  • 생성자가 아닌 메서드 외부애서 생성해도 인스턴스가 소유하기 때문에 성자에서 생성한 것 과 같은 효과를 가짐

ClassComponent.jsx

import React, { Component } from "react";

class ClassComponent extends Component{

    //생성자
    /*

    constructor(props){
        super(props);

        this.state = {
            number:0
        }
    }
    */

    // 생성자가 아닌 메서드 외부애서 생성해도 인스턴스가 소유하기 때문에
    // 생성자에서 생성한 것 과 같은 효과를 가짐
    state = {
        number:1
    }

    render(){
        return(
            <>
                <p>number:{this.state.number}</p>
                <button onClick={()=>{
                    this.setState({
                        number: this.state.number + 1
                    })
                }}>증가</button>
            
            </>

        )
    }
}
export default ClassComponent;

  • setState 에는 state 수정 구문을 삽입하는데 수정 구문 대신에 함수를 대입해도 가능

  • 함수는 state 를 매개변수로 받고 state 의 수정 구문을 return 해주면 된다.

  • ClassComponent.jsx 파일의 button 생성 코드 수정

ClassComponent.jsx

import React, { Component } from "react";

class ClassComponent extends Component{

    //생성자
    /*

    constructor(props){
        super(props);

        this.state = {
            number:0
        }
    }
    */

    // 생성자가 아닌 메서드 외부애서 생성해도 인스턴스가 소유하기 때문에
    // 생성자에서 생성한 것 과 같은 효과를 가짐
    state = {
        number:1
    }

    render(){
        return(
            <>
                <p>number:{this.state.number}</p>
                <button onClick={()=>{
                    this.setState(
                        (prevState) => {return {number:prevState.number + 1}}
                    )
                }}>증가</button>
            
            </>

        )
    }
}
export default ClassComponent;

CallBack : 이벤트가 발생하거나 상태 변화가 수행된 후 호출되는 함수

  • 대부분 비동기가 처리
<동기 VS 비동기 >

동기 (Synchronous)
 - 순차적으로 실행, 하나의 명령이 종료된 후 다른 명령을 수행
 - 앞의 작업이 끝나야 다음 작업이 수행되는 구조라서 앞의 작업이 끝나고 난 후 작업을 수행하거나 결과를 사용하고자 하는 것이 어렵지 않음(결과를 리턴)
                    
                    
비동기 (ASynchronous)
 - 하나의 명령이 수행 중인 동안에 다른 명령을 수행할 수 있는 방식 - 스레드
 - 현재 작업이 끝나고 난 후 수행되는 코드를 만들 때는 콜백을 사용하거나 Promise 또는 async & await 구문을 활용
 - 콜백을 사용할 때는 매개변수를 잘 파악해야 한다
 - 매개변수가 작업의 결과인 경우가 많음
  • setState 로 state 를 변경하고 난 후 호출되는 콜백을 사용하고자 할 때는 setState 의 두번째 매개변수로 함수를 대입하면 된다.

  • ClssComponent 에서 state 의 변화가 생기고 난 후 호출되는 코드 작성

ClassComponent.jsx

import React, { Component } from "react";

class ClassComponent extends Component{

    //생성자
    /*

    constructor(props){
        super(props);

        this.state = {
            number:0
        }
    }
    */

    // 생성자가 아닌 메서드 외부애서 생성해도 인스턴스가 소유하기 때문에
    // 생성자에서 생성한 것 과 같은 효과를 가짐
    state = {
        number:1
    }

    render(){
        return(
            <>
                <p>number:{this.state.number}</p>
                <button onClick={()=>{
                    this.setState(
                        (prevState) => {return {number:prevState.number + 1}},   //상태 변하는 문장 뒤에 , 입력 후 함수 작성
                         () =>{console.log("상태 변화 발생!!!!");}
                    )
                }}>증가</button>
            
            </>

        )
    }
}
export default ClassComponent;

  • state 는 setState 메서드로만 수정해야 하고 객체 나 배열을 수정하고자 하는 경우는 복사본을 만들고 복사본을 수정한 후 그 복사본을 setState에 대입해서 업데이트 해야한다

  • 배열이나 문자열의 경우 메서드가 원본에 작업을 수행하는지, 아니면 복사본에 수행한 후 리턴하는지 확인을 해볼 필요가 있다

import React, { Component } from "react";

class ClassComponent extends Component{

    //생성자
    /*

    constructor(props){
        super(props);

        this.state = {
            number:0
        }
    }
    */

    // 생성자가 아닌 메서드 외부애서 생성해도 인스턴스가 소유하기 때문에
    // 생성자에서 생성한 것 과 같은 효과를 가짐
    state = {
        number:1
    }

    render(){
        return(
            <>
                <p>number:{this.state.number}</p>
                <button onClick={()=>{
                    this.setState(
                        (prevState) => {return {number:prevState.number + 1}},   //상태 변하는 문장 뒤에 , 입력 후 함수 작성
                         () =>{
                             
                            
                            
                            console.log("상태 변화 발생!!!!");
                            let ar = ["a","b","c"];
                            //원본을 변경하는 메서드
                            ar.push("d");
                            console.log(ar);
                        
                        
                        
                        }
                    )
                }}>증가</button>
            
            </>

        )
    }
}
export default ClassComponent;

import React, { Component } from "react";

class ClassComponent extends Component{

    //생성자
    /*

    constructor(props){
        super(props);

        this.state = {
            number:0
        }
    }
    */

    // 생성자가 아닌 메서드 외부애서 생성해도 인스턴스가 소유하기 때문에
    // 생성자에서 생성한 것 과 같은 효과를 가짐
    state = {
        number:1
    }

    render(){
        return(
            <>
                <p>number:{this.state.number}</p>
                <button onClick={()=>{
                    this.setState(
                        (prevState) => {return {number:prevState.number + 1}},   //상태 변하는 문장 뒤에 , 입력 후 함수 작성
                         () =>{
                             
                            
                            
                            console.log("상태 변화 발생!!!!");
                            let ar = ["a","b","c"];
                            //원본을 변경하는 메서드
                            ar.push("d");
                            console.log(ar);
                            // 요소를 순회하면서 함수를 수행해서 그 결과를
                            // 가지고 새로운 배열을 만들어서 리턴하는 함수
                            
                            console.log(ar.map((e)=>{return e + "알파벳"}));
                        
                        
                        
                        }
                    )
                }}>증가</button>
            
            </>

        )
    }
}
export default ClassComponent;


Event Handling

용어

  • Event : 사용자 또는 시스템이 발생시키는 사건
  • Event Handler, Event Listener : 이벤트가 발생했을 때 호출되는 함수 또는 함수를 소유한 객체

자바스크립트 에서의 이벤트 처리

  • DOM 객체.addEventListener('이벤트 이름',(e) => { 이벤트가 발생했을 때 수행할 내용 }

이벤트 처리 함수에 전달되는 객체 - Event 타입

  • type : 이벤트 이름으로 대소문자를 구분하지 않음
  • isTrusted : 웹 브라우저에서 발생한 이벤트(true)인지 프로그래밍으로 발생한 것(false) 인지를 판단
  • target : 이벤트가 처음 발생한 객체
  • currentTarget : 이벤트 버블링이 발생한 경우 이벤트가 현재 위치한 객체
  • bubbles : 버블링 여부

리액트의 이벤트 종류 - https://reactjs.org/docs/events.html

  • 이벤트 속성은 on 으로 시작하고 camel case
  • 이벤트 속성에 연결하는 것은 자바스크립트 코드가 아니고 함수 형태의 값을 전달
  • Component에는 이벤트를 설정할 수 없고 DOM에만 이벤트를 연결
    • Component 에서 on이름 = 데이터를 넘기는 것은 이벤트 핸들링이 아니고 property를 넘기는 것

Event Bubbling

  • 하위 요소에서 발생한 이벤트가 상위 요소에게 전파 되는 것
  • 자바스크립트의 DOM은 기본적으로 Event Bubbling을 수행
<div class = 'outer'>
	<div class = 'inner'>
    </div>
</div>
  • outer와 inner 모두에 click에 대한 이벤트 처리 핸들러가 존재하는 경우 inner에서 click이 발생하면 inner의 click에 해당하는 event handler를 수행 한 후 outer의 click에 해당하는 event handler를 수행하는 것이 event bubbling

  • event bubbling 을 막고자 할 때는 Event 객체가 stopPropagation()을 호출하면 된다.


이벤트 처리 - Input 의 onChange : input의 내용이 변경되었을 때 발생하는 이벤트

  • Input 에 입력된 값은 value 속성에 저장

  • 이벤트 처리를 수행할 Component를 생성 - EventComponent.jsx
    EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{
    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type="text" placeholder="이름을 입력해주세요"/>
            
            </>
        )
    }
}

export default EventComponent;

App.js

import MyComponent from './MyComponent';

import ClassComponent from './ClassComponent';

import EventComponent from './EventComponent';
function App() {
  return (
    <>
      <EventComponent/>
      <ClassComponent/>
      <MyComponent name="아담" favoriteNumber={3} children="my son"/>
    </>
  )
}
export default App;

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{
    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type="text" placeholder="이름을 입력해주세요"
                onChange={(e)=>{
                    //입력하는 내용을 콘솔에 출력
                    console.log(e.target.value);
                }}/>

            
            </>
        )
    }
}

export default EventComponent;
  • 입력 내용을 콘솔에 출력하는 이벤트 설정

  • 입력한 내용을 state에 저장

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{

    constructor(props){
        super(props);
        this.state = {

            name:''
        }
    }
    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type="text" placeholder="이름을 입력해주세요"
                value={this.state.name}
                onChange={(e)=>{
                    //입력하는 내용을 콘솔에 출력
                    //console.log(e.target.value);

                    //입력된 값을 name 에 저장
                    this.setState({
                        name:e.target.value
                    })



                }}/>

            
            </>
        )
    }
}

export default EventComponent;
  • 콘솔에 출력되지 않음

현재화면에 button 을 추가해서 버튼의 클릭 이벤트 처리 - name의 내용을 대화상자로 출력하고 name 을 초기화

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{

    constructor(props){
        super(props);
        this.state = {

            name:''
        }
    }
    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type="text" placeholder="이름을 입력해주세요"
                value={this.state.name}
                onChange={(e)=>{
                    //입력하는 내용을 콘솔에 출력
                    //console.log(e.target.value);

                    //입력된 값을 name 에 저장
                    this.setState({
                        name:e.target.value
                    })



                }}/>
                <button onClick={(e)=>{
                    alert(this.state.name);
                    this.setState({
                        name:''
                    })
                }}>버튼</button>

                
                
                <input type='button' value = '버튼'/>
            
            </>
        )
    }
}

export default EventComponent;

  • 초기화

  • Component 내에 이벤트 처리 코드를 작성할 때는 이런 방식으로 많이 수행한다!!

별도의 함수로 이벤트 처리

  • 이벤트 처리의 내용이 많으면 DOM 생성 태그 안에 많은 양의 자바스크립트 코드를 작성하면 가독성이 떨어지게 되고, 공유 데이터를 핸들링하는 경우에 대부분의 경우 공유 데이터는 App.js 에 존재할 가능성이 높은데,
    이 경우 App.js 에 함수를 만들어 처리하는 것이 수월하므로 App.js나 상위 컴포넌트에서 이벤트 처리 함수를 만들어서 하위 컴포넌트에게 전달하는데 이 경우는 함수를 연결하는 형태로 이벤트를 처리하게 된다.

  • 동일한 내용을 별도의 함수로 만들어서 처리

클래스 내의 함수에서 this 를 사용하고자 하는 경우 화살표 함수를 만들면 안됨

let f = function(){
	내용
    this.
}

// 화살표 함수에서는 this 와 arguments를 사용할 수 없다

let f = () =>{
	내용
    this.
}

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{
    //input 의 내용이 변경될 때 호출될 함수

    handleChange(e){
        this.setState({
            name:e.target.value

        })
    }

    //버튼을 클릭했을 때 호출 될 함수

    handleClick(e){
        alert(this.state.name);
        this.setState({
            name:''
        })
    }


        constructor(props){
        super(props);
        this.state = {

            name:''
        }
// 같은 클래스에 속했다고 해서 아래에서 불러올 수 없다
// 클래스의 구조일분 인스턴스의 구조는 아니기 때문
        this.handleClick = this.handleClick.bind(this);
        this.handleChange = this.handleChange.bind(this);// 이 구문 매우 중요
    }
    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type="text" placeholder="이름을 입력해주세요"
                value={this.state.name}
                onChange={this.handleChange}/>
                <button onClick={this.handleClick}>버튼</button>
                <input type='button' value = '버튼'/>
            
            </>
        )
    }
}

export default EventComponent;


바벨의 transform-class-properties 문법

  • 리액트는 바벨이 크로스 컴파일링 (ECMA 2015+ 이상의 문법으로 작성된 스크립트 코드를 하위 버전에 호환될 수 있게 변환)을 수행해주는데 클래스 안에 만든 요소를 클래스 안에서 this를 이용해서 접근할 수 있도록 지원

  • 앞의 컴포넌트를 수정 - 이제 바인딩 작업이 필요없다

  • 생성자에서 변환하는 작업을 안할 수 있음

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{
    //input 의 내용이 변경될 때 호출될 함수

    state = {

        name: ''
    }

    handleChange = (e) => {
        this.setState({
            name:e.target.value

        })
    }

    //버튼을 클릭했을 때 호출 될 함수

    handleClick = (e) => {
        alert(this.state.name);
        this.setState({
            name:''
        })
    }


        constructor(props){
        super(props);
        this.state = {

            name:''
        }

        
    }
    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type="text" placeholder="이름을 입력해주세요"
                value={this.state.name}
                onChange={this.handleChange}/>
                <button onClick={this.handleClick}>버튼</button>
                <input type='button' value = '버튼'/>
            
            </>
        )
    }
}

export default EventComponent;


Event Routing

  • 하나의 여러 가지 이벤트 또는 여러 객체의 동일한 이벤트를 하나의 객체 또는 함수를 가지고 처리하는 것

  • 유사한 이벤트 처리를 위해서 함수를 여러 개 만들거나 객체를 여러 개 만드는 것도 자원의 낭비

  • Event Routing 을 하게 되면 이벤트가 발생한 객체를 구분할 수 있어야 하는데 자바스크립트 에서는 DOM객체를 찾아서 비교하지만, 리액트에서는 name 속성에 값을 부여한 후 e.target.name 속성을 가지고 구분한다 HTML 의 DOM 만 사용하는 것이 아니므로 id속성을 이용해서 DOM 을 찾는 것을 권장하지 않음

  • 리액트를 하다 보면 아래와 같은 코드는 보이지 않음

    • document.getElementByid("아이디")
    • document.querySelector("선택자")
  • 이전 컴포넌트를 수정 - input을 2개로 늘릴 것임

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{
    //input 의 내용이 변경될 때 호출될 함수

    state = {

        name: '',
        age: ''
    }

    handleNameChange = (e) =>{
        this.setState({
            name:e.target.value
        })
    }

    handleAgeChange = (e) =>{
        this.setState({
            age:e.target.value
        })
    }
        
    
    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type='text' value = {this.state.name} //전달 - value
                placeholder='이름 입력' onChange={this.handleNameChange}/>
                <input type='text' value={this.state.age}
                placeholder='나이 입력' onChange={this.handleAgeChange}/>
                <button>확인</button>

                
            
            </>
        )
    }
}

export default EventComponent;

  • alert 추가

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{
    //input 의 내용이 변경될 때 호출될 함수

    state = {

        name: '',
        age: ''
    }

    handleNameChange = (e) =>{
        this.setState({
            name:e.target.value
        })
    }

    handleAgeChange = (e) =>{
        this.setState({
            age:e.target.value
        })
    }

    handleClick = (e) => {
        alert(this.state.name + ':' + this.state.age);
        this.setState({
            name : '',
            age : ''
        })
    }
        
    
    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type='text' value = {this.state.name} //전달 - value
                placeholder='이름 입력' onChange={this.handleNameChange}/>
                <input type='text' value={this.state.age}
                placeholder='나이 입력' onChange={this.handleAgeChange}/>
                <button onClick={this.handleClick}>확인</button>

                
            
            </>
        )
    }
}

export default EventComponent;

  • 코드를 수정해서 2개의 input 의 change 이벤트를 하나의 함수로 처리

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{
    //input 의 내용이 변경될 때 호출될 함수

    state = {

        name: '',
        age: ''
    }

    handleChange = (e) =>{
        // 이벤트가 발생한 객체의 이름과 동일한 state 의 값을
        // 입력한 내용으로 수정
        this.setState({
            [e.target.name]:e.target.value
        })
    }


    handleClick = (e) => {
        alert(this.state.name + ':' + this.state.age);
        this.setState({
            name : '',
            age : ''
        })
    }
        
    
    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type='text' value = {this.state.name} name = 'name' //전달 - value
                placeholder='이름 입력' onChange={this.handleChange}/>
                <input type='text' value={this.state.age} name = 'age'
                placeholder='나이 입력' onChange={this.handleChange}/>
                <button onClick={this.handleClick}>확인</button>

                
            
            </>
        )
    }
}

export default EventComponent;
  • 동일한 결과

onKeyPress - 트리거(Trigger) 사용

  • 키보드를 눌렀을 때 발생하는 이벤트 객체의 key 속성을 확인하면 누른 keyboard의 값을 확인할 수 있음

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{
    //input 의 내용이 변경될 때 호출될 함수

    state = {

        name: '',
        age: ''
    }

    handleChange = (e) =>{
        // 이벤트가 발생한 객체의 이름과 동일한 state 의 값을
        // 입력한 내용으로 수정
        this.setState({
            [e.target.name]:e.target.value
        })
    }


    handleClick = (e) => {
        alert(this.state.name + ':' + this.state.age);
        this.setState({
            name : '',
            age : ''
        })
    }
    // input의 KeyPress 이벤트 연결할 함수
    handleKeyPress = (e) => {
      	
        //alert(e.key) //그냥 확인하는 방법
		//누른 키가 Enter 라면 handleClick 함수를 호출
      
      	//이벤트를 강제로 발생시키는 것을 Event - Trigger 라고 한다
        if(e.key === 'Enter'){
            this.handleClick();
        }
    }




    
    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type='text' value = {this.state.name} name = 'name' //전달 - value
                placeholder='이름 입력' onChange={this.handleChange}/>
                <input type='text' value={this.state.age} name = 'age'
                placeholder='나이 입력' onChange={this.handleChange} //나이 입력에 onKeyPress 적용
                onKeyPress={this.handleKeyPress}/>
                <button onClick={this.handleClick}>확인</button>

                
            
            </>
        )
    }
}

export default EventComponent;
  • Enter 누르면 입력


Input 태그의 기타 속성

  • input 의 defaultValue 나 defaultChecked 속성을 이용하면 기본값이나 기본 체크를 설정할 수 있다.

  • file 은 보안 문제 때문에 value 값을 가져오는 것은 가능하지만 value를 설정하는 것은 안된다.

  • checkbox의 체크 여부를 가져오고자 하는 경우에는 e.target.checked 로 가져오면 된다.

  • file

    • multiple 속성의 값이 false 이면 하나만 선택이 가능하고 true 이면 여러 개 선택이 가능.
    • accept 속성을 이용하면 파일의 종류를 제한하는 것이 가능 - images/* 로 설정하면 그림파일만 선택이 가능하고 text/plain 으로 설정하면 텍스트 파일로 제한
    • 선택한 파일의 목록은 e.target.files 로 가져올 수 있는데, 이 속성은 자바스크립트의 FileList

Event 객체의 preventDefault()

  • 이벤트가 발생했을 때 이 이벤트와 관련된 웹 브라우저의 기본 구현 내용을 실행하지 않도록 해주는 함수

  • form 의 submit 이벤트는 form의 데이터를 action으로 설정한 곳으로 전송하는 기능을 내장하고 있고 form 의 reset 이벤트는 form의 데이터를 삭제하는 기능을 내장하고 있으며 브라우저는 DragAndDrop 이벤트 발생 시 drop 을 막아버리는 기능을 내장하고 있음

  • 웹에서 입력할 수 있는 도구

 - input : text, passwd, checkbox, radio, 
	file, hidden(보이지는 않는 속성이지만 서버에게 데이터를 전달하고자 하는 경우 사용)
, image, button, submit, reset

<checkbox  radio  하나의 그룹이 되려면 name의 값이 같아야 하고,
value를 설정해주는 것이 좋다

value 설정이 안되어 있으면 on 이나 off  서버에게 전송되고 
value  설정되어 있으면 value가 넘어간다>


 - textarea : 여러 줄의 문자열, 태그 와 태그 사이에 내용이 들어가기 때문에 
 	설정을 할 때에는 value 가 아니고 innerHTML 이고 입력된 내용을 가져올 때는 value
    여는 태그 와 닫는 태그의 라인이 다르면 첫 커서가 중간에 들어간다
 

 - select : 목록 중에 하나를 선택
 		select 에 name 을 설정하고 option 으로 항목을 만드는데 
        value를 설정해줘야 함
  • 클라이언트 사이드 렌더링을 할 때는 submit 이벤트를 사용하지 않고 ajax, fetch api, axios 라이브러리 등을 이용해서 서버에 데이터를 전송하기 때문에 입력한 내용을 가져와, get 방식의 파라미터를 만드는 것, 그리고 json 형식의 파라미터를 만드는 것 과 file이 존재하는 파라미터를 만드는 것이 중요하다

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{
    state={
        name:'',
        content:'',
        image: []

    }

    
    handleChange = (e) => {
        this.setState({
            [e.target.name]:e.target.value
        })
    }

    handleClick = (e) => {
        alert(this.state.name + '+' + this.state.content);
        console.log(this.state.image);
    }


    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type='text' name = 'name' defaultValue='No Name'
                onChange={this.handleChange} /> 
                <br />
                <fieldset>
                    <legend>성별</legend>
                    <input type='radio' name = 'gender' value='man'defaultChecked/>남자
                    <input type='radio' name = 'gender' value='woman'/>여자
                </fieldset>
                <textarea name='content' onChange = {this.handleChange}></textarea>
                <select name='cnt'>
                    <option value = '1'>1</option>
                    <option value = '2'>2</option>
                    <option value = '3'>3</option>
                </select>
                <input type='file' name='image' multiple='multiple'
                accept='iamge/*' onChange={this.handleChange}/>
                <br/>
                <button onClick={this.handleClick}>전송</button>
                
            </>
        )
    }
}

export default EventComponent;

  • 파일 전송

EventComponent.jsx

import React, {Component} from "react";

class EventComponent extends Component{
    state={

        image: []

    }

    //파일의 경우는 files 속성을 state 에 연결해야 한다
    //file 의 경우는 e.target.value 를 호출하면 파일의 절대경로를 문자열로 리턴
    //이 데이터는 서버로 전송하면 문자열이 된다.
    
    handleChange = (e) => {
        this.setState({
            [e.target.name]:e.target.files
        })
    }

    handleClick = (e) => {
        
        console.log(this.state.image);
    }


    render(){
        return(
            <>
                <h1>이벤트 연습</h1>
                <input type='file' name='image' multiple='multiple'
                accept='iamge/*' onChange={this.handleChange}/>
                <br/>
                <button onClick={this.handleClick}>전송</button>
                
            </>
        )
    }
}

export default EventComponent;

  • 전송 button 클릭


Drag & Drop 이벤트 사용 시 주의사항

  • 브라우저는 Drag 이벤트가 발생할 때 Drop 이벤트를 중지시키는 기보 기능을 내장하고 있음

  • Drop 이벤트를 사용하고자 할 때는 첫 번째 문장이 e.preventDefault()가 되어야 한다.


profile
무럭무럭 자라볼까

0개의 댓글