본문 바로가기

WINK-(Web & App)/React.js 스터디

[2024 여름방학 React.js 스터디] 김태일 #3주차 "리액트 입문"

반응형

01. 리액트가 만들어진 배경

- 처리해야 할 이벤트, 상태값, DOM이 다양해질때 코드가 복잡해지는 것을 해결하기 위함

- 어떠한 상태가 바뀔 때, 업데이트를 작업하는것이 아닌 모든걸 새로 만든다는 발상에서 출발

- Virtual DOM을 통해 실현 (업데이트에 대한 고민해결 + 빠른 성능)

 

01-1 Virtual DOM

- 브라우저에 실제로 보여지지 않는 가상의 DOM

- 업데이트가 필요한 곳의 UI를 Virtual DOM 을 통해서 렌더링

- 실제 브라우저에 보여지고 있는 DOM 과 비교를 한 후, 차이가 있는 곳을 감지하여 이를 실제 DOM 에 패치

 

02. 작업환경 준비

- Node.js : 자바스크립트 런타임 (Webpack, Babel 등의 도구들을 사용하기위해 설치)

- Yarn : 개선된 버전의 npm (라이브러리 설치 및 해당 라이브러리들의 버전 관리를 하게 될 때 사용)

- 코드 에디터 : VSCode, Atom, WebStorm, Sublime 등등

 

02-1 새 프로젝트 만들기

1) begin-react 디렉터리 안에 리액트 프로젝트 생성

$ npx create-react-app begin-react

 

2) begin-react 디렉터리에서 yarn start 명령어 입력

$ cd begin-react
$ yarn start

 

3. http://localhost:3000/ 브라우저 열림

 Git Bash 에서의 기본 경로 : pwd명령어를 통해 확인

이외의 명령어 https://taei1.tistory.com/4 참고

 

03. 나의 첫번째 리액트 컴포넌트

- 리액트 컴포넌트는 함수형태, 클래스형태 모두 작성 가능

 

1) src 디렉터리에 Hello.js 파일 작성

//Hello.js
import React from 'react';

const Hello = () => {
  return <div>안녕하세요</div>;
}

export default Hello;  //Hello라는 컴포넌트를 내보내겠다는 의미(다른 컴포넌트를 불러와 사용 가능)

 

2) 리액트 호출

import React from 'react';

 

3) App.js에서 Hello 컴포넌트 불러오기

(Hello 컴포넌트를 div안에서 여러번 재사용 가능)

//App.js
import React from 'react';
import Hello from './Hello';

const App = () => {
  return (
    <div>
      <Hello /> 
    </div>
  );
}

export default App;

 

4) Index.js 코드 살펴보기

- root.render : 실제 DOM 내부에 리액트 컴포넌트를 렌더링 

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

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

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
<!-- public폴더 안 index.html 내부 -->
<div id="root"></div>

Index.js에서 렌더링 된 결과물이 index.html의 div 내부에 렌더링 됨

 

04. JSX

- 리액트에서 생김새를 정의할 때 사용하는 문법

- HTML의 모습을 하고 있지만 실제로는 JavaScript임

 

04-1 Babel

- 자바스크립트의 문법을 확장해주는 도구

- 리액트 컴포넌트 파일에서 XML 형태로 코드를 작성하면 babel 이 JSX 를 JavaScript 로 변환

 

04-2 JSX가 JavaScript로 변환하기 위한 규칙

1) 태그가 닫혀야 함

- 아래 코드는 태그를 닫는 </div>가 부족하므로 오류가 뜸

- Self Closing 태그 : 열리고 바로 닫히는 태그를 의미 ( ex. <Hello />)

//App.js
import React from 'react';
import Hello from './Hello';

const App = () => {
  return (
    <div>
      <Hello />
      <Hello />
      <Hello />
      <div> 
    </div>
  );
}

export default App;

 

2) 태그가 감싸져야 함

- 두개 이상의 태그는 무조건 하나의 태그로 감싸져있어야 함

- 아래 코드는 Hello태그와 div태그를 다른 태그로 감싸주어야 오류 해결

※ 불필요한 태그를 만들어 감싸는 것은 비효율 --> 리액트의 Fragment (태그를 이름없이 작성) 사용

(Fragment : 브라우저 상에서 별도의 엘리먼트로 나타나지 않음 (작성예시. <> </>) )  

import React from 'react';
import Hello from './Hello';

const App = () => {
  return (
    <Hello />
    <div>안녕히계세요.</div>
  );
}

export default App;

 

04-3 JSX안에 자바스크립트 값 사용하기

- JSX 내부에 자바스크립트 변수를 보여줄 때 {}로 감싸서 표현

import React from 'react';
import Hello from './Hello';

const = App() => {
  const name = 'react';
  return (
    <>
      <Hello />
      <div>{name}</div>
    </>
  );
}

export default App;

 

04-4 style과 className

1) style 설정 방법

- 인라인 스타일 : 객체 형태로 작성, camelCase형태로 네이밍

import React from 'react';
import Hello from './Hello';

const App = () => {
  const name = 'react';
  const style = {
    backgroundColor: 'black',
    color: 'aqua',
    fontSize: 24, // 기본 단위 px
    padding: '1rem' // 다른 단위 사용 시 문자열로 설정
  }

  return (
    <>
      <Hello />
      <div style={style}>{name}</div>
    </>
  );
}

export default App;

 

2) className 설정 방법

- class= 이 아닌 className= 으로 설정 해주어야 함

- 첫번째 css파일을 두번째 js파일에서 className="gray-box"로 불러옴

.gray-box {
  background: gray;
  width: 64px;
  height: 64px;
}
import React from 'react';
import Hello from './Hello';
import './App.css';


const App = () => {
  const name = 'react';
  const style = {
    backgroundColor: 'black',
    color: 'aqua',
    fontSize: 24, // 기본 단위 px
    padding: '1rem' // 다른 단위 사용 시 문자열로 설정
  }

  return (
    <>
      <Hello />
      <div style={style}>{name}</div>
      <div className="gray-box"></div>
    </>
  );
}

export default App;

 

04-5 주석

- 내부의 주석 : {/* 이런 형태로 */} 작성

- 태그 내부에서는 // 형태로도 주석 작성이 가능

import React from 'react';
import Hello from './Hello';
import './App.css';


const App = () => {
  const name = 'react';
  const style = {
    backgroundColor: 'black',
    color: 'aqua',
    fontSize: 24, // 기본 단위 px
    padding: '1rem' // 다른 단위 사용 시 문자열로 설정
  }

  return (
    <>
      {/* 주석은 화면에 보이지 않습니다 */}
      /* 중괄호로 감싸지 않으면 화면에 보입니다 */
      <Hello 
        // 열리는 태그 내부에서는 이렇게 주석을 작성 할 수 있습니다.
      />
      <div style={style}>{name}</div>
      <div className="gray-box"></div>
    </>
  );
}

export default App;

 

05. props를 통해 컴포넌트에게 값 전달하기

- props : 어떠한 값을 컴포넌트에게 전달해야 할때 사용 (properties의 줄임말)

 

05-1 props 기본 사용법

1) App 컴포넌트에서 Hello 컴포넌트 사용할 때 name이라는 값 전달하기

//App.js
import React from 'react';
import Hello from './Hello';

function App() {
  return (
    <Hello name="react" />
  );
}

export default App;

 

2) Hello 컴포넌트에서 name값 사용하기

- 파라미터를 통해 조회 가능

- 객체 형태로 전달

- props.name을 통해 name값 조회 가능

//Hello.js
import React from 'react';

const Hello = (props) => {
  return <div>안녕하세요 {props.name}</div>
}

export default Hello;

 

05-2 여러개의 props, 비구조화 할당

1) color라는 값 전달

//App.js
import React from 'react';
import Hello from './Hello';

const App = () => {
  return (
    <Hello name="react" color="red"/>
  );
}

export default App;

 

2) Hello 컴포넌트에서 color값 조회, 폰트의 색상으로 설정

//Hello.js
import React from 'react';

const Hello = (props) => {
  return <div style={{ color: props.color }}>안녕하세요 {props.name}</div>
}

export default Hello;

 

 

 

3) 비구조화 할당 문법을 사용하여 코드 수정

- props. 대신에 파라미터에서 props 내부의 값 조회

import React from 'react';

const Hello = ({ color, name }) => {
  return <div style={{ color }}>안녕하세요 {name}</div>
}

export default Hello;

 

05-3 defaultProps로 기본값 설정

- props를 지정하지 않고 기본적으로 사용할 값을 설정하고 싶을 때 사용

//Hello.js
import React from 'react';

function Hello({ color, name }) {
  return <div style={{ color }}>안녕하세요 {name}</div>
}

Hello.defaultProps = {
  name: '이름없음'
}

export default Hello;

 

- name이 없는 Hello 컴포넌트 렌더링

//App.js
import React from 'react';
import Hello from './Hello';

const App = () => {
  return (
    <>
      <Hello name="react" color="red"/>
      <Hello color="pink"/>
    </>
  );
}

export default App;

 

 

05-4 props.children

- 컴포넌트 태그 사이에 넣은 값을 조회할 때 사용

 

1) src 디렉터리에 Wrapper.js 생성

//Wrapper.js
import React from 'react';

const Wrapper = () => {
  const style = {
    border: '2px solid black',
    padding: '16px',
  };
  return (
    <div style={style}>

    </div>
  )
}

export default Wrapper;

 

2) Wrapper 컴포넌트 App에서 사용

- Wrapper태그 내부에 Hello 컴포넌트를 넣음

//App.js
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';

function App() {
  return (
    <Wrapper>
      <Hello name="react" color="red"/>
      <Hello color="pink"/>
    </Wrapper>
  );
}

export default App;

 

- Hello 컴포넌트들이 보여지지 않음 --> Wrapper에서 props.chidren 렌더링 필요

 

- props.children 렌더링

//Wrapper.js
import React from 'react';

function Wrapper({ children }) {
  const style = {
    border: '2px solid black',
    padding: '16px',
  };
  return (
    <div style={style}>
      {children}
    </div>
  )
}

export default Wrapper;

 

- 결과물

 

06. 조건부 렌더링

- 특정 조건에 따라 다른 결과물을 렌더링 하는 것을 의미

 

1) App 컴포넌트에서 Hello 컴포넌트를 사용할 때 isSpecial 이라는 props설정

//App.js
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';


const App = () => {
  return (
    <Wrapper>
      <Hello name="react" color="red" isSpecial={true}/>
      <Hello color="pink" />
    </Wrapper>
  )
}

export default App;

 

2) Hello 컴포넌트에서 isSpcial이 true 일때 <b>*</b> 를, 그렇지 않다면 null 을 보여주도록 작성 (삼항연산자 사용)

//Hello.js
import React from 'react';

const Hello = ({ color, name, isSpecial }) => {
  return (
    <div style={{ color }}>
      { isSpecial ? <b>*</b> : null }
      안녕하세요 {name}
    </div>
  );
}

Hello.defaultProps = {
  name: '이름없음'
}

export default Hello;

06-1 && 연산자

- 특정 조건이 true이면 보여주고, 그렇지 않다면 false (단축 평가 논리 계산법 학습 필요)

<div style={{ color }}>
    {isSpecial && <b>*</b>}
    안녕하세요 {name}
</div>

 

06-2 props값 설정을 생략하면 = {true}

- props 이름만 작성하고 값 설정을 생략하면 true로 설정한 것으로 간주

- 아래 코드에서는 isSpecial = {true}와 동일

<Wrapper>
    <Hello name="react" color="red" isSpecial />
    <Hello color="pink"/>
</Wrapper>

 

07. useState를 통해 컴포넌트에서 바뀌는 값 관리하기

- 사용자에 인터랙션에 따라 내용이 바뀌는 것을 구현해야 함

 

1) src 디렉터리에 Counter.js 코드 작성

//Counter.js
import React from 'react';

const Counter = () => {
  return (
    <div>
      <h1>0</h1>
      <button>+1</button>
      <button>-1</button>
    </div>
  );
}

export default Counter;

 

2) App에서 Counter 렌더링

//App.js
import React from 'react';
import Counter from './Counter';

function App() {
  return (
    <Counter />
  );
}

export default App;

 

3) 이벤트 설정 (Counter.js 수정)

- 클릭 이벤트가 발생 했을 때 특정 함수가 호출되도록 설정

- button에 onClick으로 각 함수를 연결

import React from 'react';

const Counter = () => {
  const onIncrease = () => {
    console.log('+1')
  }
  const onDecrease = () => {
    console.log('-1');
  }
  return (
    <div>
      <h1>0</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

리액트에서 엘리먼트에 이벤트를 설정해 줄때 on이벤트이름={실행하고싶은함수} 형태로 설정해야함

 

4) 동적인 값 끼얹기, useState

- 상태(state) : 컴포넌트에서 동적인 값을 의미

- useState함수 : 컴포넌트에서 상태를 관리할 수 있음

//Counter.js
import React, { useState } from 'react';  //리액트 패키지에서 useState 함수 호출

const Counter = () => {
  const [number, setNumber] = useState(0);  //상태의 기본값을 파라미터에 넣어 호출
                                            // 첫번째 원소는 현재 상태, 두번째 원소는 Setter함수
  const onIncrease = () => {
    setNumber(number + 1);
  }

  const onDecrease = () => {
    setNumber(number - 1);                //배열 비구조화 할당을 통해 각 원소 추출
  }

  return (
    <div>
      <h1>{number}</h1>        //0이 아닌 number값을 보여줌
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

5) 함수형 업데이트

- Setter함수를 사용할 때  새로운 값을 파라미터로 넣어주는 대신, 기존값을 함수를 등록하는 방식으로 업데이트 가능

//Counter.js
import React, { useState } from 'react';

const Counter = () => {
  const [number, setNumber] = useState(0);

  const onIncrease = () => {
    setNumber(prevNumber => prevNumber + 1);
  }

  const onDecrease = () => {
    setNumber(prevNumber => prevNumber - 1);
  }

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

 

08. input 상태 관리하기

- input 태그 : 리액트에서 사용자가 입력 할 수 있는 태그

 

1) src 디렉터리에 InputSample.js 파일 생성

import React from 'react';

const InputSample = () => {
  return (
    <div>
      <input />
      <button>초기화</button>
      <div>
        <b>값: </b>
      </div>
    </div>
  );
}

export default InputSample;

 

2) App에서 렌더링

//App.js
import React from 'react';
import InputSample from './InputSample';

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

export default App;

 

3) input의 onChange 라는 이벤트 사용

//inputSample.js
import React, { useState } from 'react';

const InputSample = () => {
  const [text, setText] = useState('');

  const onChange = (e) => {    // e: 이벤트 객체
    setText(e.target.value);   // e.target : input DOM을 가리킴 
  };  //--> input에 입력한 값을 알 수 있음

  const onReset = () => {
    setText('');
  };

  return (
    <div>
      <input onChange={onChange} value={text}  />
      <button onClick={onReset}>초기화</button>
      <div>
        <b>값: {text}</b>
      </div>
    </div>
  );
}

export default InputSample;

 

09. 여러개의 input 상태 관리하기

- placeholder : input이 비어져있을 때 input에 대한 설명을 보여주는 값

 

1) input 여러개 만들기, onChange와 onReset 함수 비우기

//InputSample.js
import React, { useState } from 'react';

const InputSample = () => {
  const onChange = (e) => {
  };

  const onReset = () => {
  };


  return (
    <div>
      <input placeholder="이름" />
      <input placeholder="닉네임" />
      <button onClick={onReset}>초기화</button>
      <div>
        <b>값: </b>
        이름 (닉네임)
      </div>
    </div>
  );
}

export default InputSample;

 

2) input에 name을 설정하고 이벤트가 발생했을 때 name 값을 참조하도록 함

(useState에서는 문자열이 아닌 객체 형태의 상태를 관리해주어야 함)

//InputSample.js
import React, { useState } from 'react';

const InputSample = () => {
  const [inputs, setInputs] = useState({
    name: '',
    nickname: ''
  });

  const { name, nickname } = inputs; // 비구조화 할당을 통해 값 추출

  const onChange = (e) => {
    const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
    setInputs({
      ...inputs, // 기존의 input 객체를 복사한 뒤
      [name]: value // name 키를 가진 값을 value 로 설정
    });
  };

  const onReset = () => {
    setInputs({
      name: '',
      nickname: '',
    })
  };


  return (
    <div>
      <input name="name" placeholder="이름" onChange={onChange} value={name} />
      <input name="nickname" placeholder="닉네임" onChange={onChange} value={nickname}/>
      <button onClick={onReset}>초기화</button>
      <div>
        <b>값: </b>
        {name} ({nickname})
      </div>
    </div>
  );
}

export default InputSample;

 

 

 리액트 상태에서의 객체 수정법

- 새로운 객체를 만들어 변화를 주고, 이를 상태로 사용해야 함



반응형