본문 바로가기

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

[2024 React.js 스터디] 정호용 #4주차 "React.JS 맛보기"

반응형

1장. 리액트 입문

1. 리액트는 어쩌다가 만들어 졌을까?

HTML/JS로 만들어진 카운터 코드를 예시로 보자면..

<h2 id="number">0</h2>
<div>
  <button id="increase">+1</button>
  <button id="decrease">-1</button>
</div>
const number = document.getElementById('number');
const increase = document.getElementById('increase');
const decrease = document.getElementById('decrease');

increase.onclick = () => {
  const current = parseInt(number.innerText, 10);
  number.innerText = current + 1;
};

decrease.onclick = () => {
  const current = parseInt(number.innerText, 10);
  number.innerText = current - 1;
};

이처럼 JS에서는 id를 이용해서 각 DOM을 선택하고, 원하는 이벤트 발생 시 DOM의 특정 속성을 바꿔준다.


DOM이란?

문서 객체 모델(The Document Object Model, 이하 DOM) 은 HTML, XML 문서의 프로그래밍 interface 이다. DOM은 문서의 구조화된 표현(structured representation)을 제공하며 프로그래밍 언어가 DOM 구조에 접근할 수 있는 방법을 제공하여 그들이 문서 구조, 스타일, 내용 등을 변경할 수 있게 돕는다. 

...중략...

자바스크립트 에서는....

문서의 모든 element (전체 문서, 헤드, 문서 안의 table, table header, table cell 안의 text)는 문서를 위한 document object model 의 한 부분이다. 때문에, 이러한 요소들을 DOM 과 JavaScript와 같은 스크립팅 언어를 통해 접근하고 조작할 수 있는 것이다.

 

출처:

https://developer.mozilla.org/ko/docs/Web/API/Document_Object_Model/Introduction

 

즉, 이벤트 발생 시, id를 사용해서 DOM이란 오브젝트를 선택 후, 필요에 따라 DOM의 속성을 바꾸는 것이다.

 

하지만 사용자의 인터렉션이 자주 발생하고, 동적으로 UI를 표현해야 한다면, 규칙이 많아질 것이고 관리도 힘들 것이다.

 

즉 이렇게 된다고...

그래서 이를 해결하기 위해 Ember, Backbone, AngularJS등의 프레임워크가 나왔다.

하지만 리액트는 발상이 다르다고 한다.

Ember, Backbone, AngularJS 등 기존 프레임워크 React.JS
어떤 상태가 바뀔때, 그에따라 DOM을 업데이트 하는 규칙 정의 어떤 상태가 바뀔 때, 처음부터 새로 만들기

처음부터 새로 만들면 개발이 쉬워지겠지만, 처음부터 만들어야 하기 때문에 속도가 느려지는 단점이 있다.

하지만 React.JS는 Virtual DOM이라는 것을 써서 이 문제를 해결했다.

Virtual DOM은 말 그대로 가상의 DOM인데, 브라우저 말고 메모리에 가상으로 존재하는 DOM이다.

이건 그냥 JS객체이기 때문에 작동 성능이 DOM을 업데이트 하는 것 보다 속도가 빠르다

 

리액트는 상태가 업데이트 되면,

1. 업데이트가 필요한 곳의 UI를 Virtual DOM을 통해 랜더링

2. 비교 알고리즘을 통해 실제 브라우저에 보여지는 DOM과 비교

3. 차이가 있는 곳을 감지하여 실제 DOM에 패치

2. 작업환경 준비

✅ 필요 프로그램Node.js - Webpack과 Babel같은 도구들이 Node.js기반이기에 설치 필요

 

 

Yarn - 개선된 버전의 npm. npm은 패키지 매니저 도구이고, 라이브러리 설치 및 버전관리에 사용된다.

코드 에디터 - IntelliJ 사용예정Git bash - 윈도우가 아니라 맥이라 미설치

 

✅ 새 프로젝트 만들어보기

명령어를 차례차례 입력하면 된다. 최초 리액트 프로젝트 생성 시, 시간이 약간 소요된다.

 

yarn start까지 입력 후 엔터를 치면...

 

 

위처럼 자동으로 열린다.

 

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

이 상태로 IntelliJ에 들어가면 당연히 변화가 없다. 왜냐? 우리는 그저 리액트 프로젝트를 터미널을 통해 만들기만 했을 뿐.

그래서 이를 찾아서 열어주면 된다.

 

begin-react프로젝트 폴더를 찾아서 열어준다.

 

 

✅ Hello.js

 

위처럼 Hello.js를 만들어 코드를 작성해 준다.

리액트 컴포넌트를 만들 때 import React from 'react';를 써줘야 한다.

리액트 컴포넌트는 함수형으로, 클래스형으로도 작성할 수 있다.

리액트 컴포넌트에서는 XML형태의 값을 리턴할 수 있는데, 이를 JSX라 부른다.

Hello라는 컴포넌트를 내보내려면 export default Hello;를 써줘야 한다. 이렇게 하면 다른 컴포넌트에서 불러와 사용할 수 있다.

 

 

 App.js

 

Hello.js때와 동일하게 react를 불러오고,

Hello라는 컴포넌트를 불러온다.

 

 

아직까지는 할 만 합니다!

추가로, 컴포넌트는 일종의 UI조각이라, 쉽게 재사용 할 수도 있다.

이제 index.js를 열면 이렇게 보인다

 

 

여기서 ReactDOM.render의 역할은 브라우저에 있는 DOM 내부의 리액트의 컴포넌트를 랜더링하겠다는 뜻.

여기서 id가 root인 DOM을 선택하고 있다.

 

이는 public/index.html을 열면 

root가 지정되어있다!

 

4. JSX

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

HTML같지만 사실 JS이다.

리액트 컴포넌트 파일에서 XML형태로 코드 작성 > babelJSXJS로 변환해준다.

*Babel은 JS의 문법을 확장해주는 도구이다.

 

 

✅ 태그는 꼭 닫혀 있어야 한다.

(이런거 안 된다.)

(이렇게됨)

 

즉, 태그를 <div>이렇게 열었으면, </div>이런 식으로 닫아줘야 한다.

 

 

이렇게 태그 안에 들어갈 내용이 없을 때, /를 이용해 셀프 클로징을 해줘야 한다.

 

 

 두 개 이상의 태그는 하나의 태그로 감싸져야 한다.

 

 

 

 

✅ 하지만, 단순히 감싸는 용도로 <div></div>를 쓰는 것은 별로 좋지 않을 수도 있다.

 그럴 땐 리액트의 fragment를 쓰면 된다.

태그 작성 시 <></>처럼 아무것도 쓰지 않으면  Fragment가 만들어진다.

이는 브라우저 상에서 별도의 element로 나오지 않는다.

 

JSX 안에 JS코드 사용하기

자바스크립트 변수 사용 시, {}로 감싸준다.

 

style과 className

JSX에서 태그에 style과 CSS class를 설정하는 방법은 조금 다르다.

인라인 스타일 객체 형태로 작성
-로 구분된 이름 camelCase 형태로 작성

 

 

CSS class 설정 시 class= 가 아닌 className= 으로 해줘야 한다.

 

 

✅ 주석 작성

주석은 {/* 주석 */} 으로 작성한다.

// 로 쓴 주석 역시 보이지 않는다.

 

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

✅ props의 기본 사용법

name이라는 값을 전달하고 싶다!

이렇게 써주면 된다.

 

Hello 컴포넌트에서 name 값을 쓰고 싶다면 {props.name}을 써주자

 

✅ 여러 개의 props, 비구조화 할당

 

우선 App.js에 props를 써주고 싶은 만큼 써주자.

 

 

그 뒤에 Hello.js에서 폰트 색상을 바꾸면 된다.

 

✅ 매 번 props를 안 쓰는 방법이 있는데,

Hello.js 에서 함수의 파라미터에서 비구조화 할당 문법을 쓰면 된다.

({color, name}) 요렇게 써 주면 된다.

 

훨씬 간결해 졌다.

 

✅ DefaultProps로 기본값 설정

 

props를 지정하지 않았을 때, 기본값이 뜨게 할 수 있다.

 

Hello.js

 

App.js

 

이렇게 이름에 대한 값을 주지 않으면 기본 값으로 '이름없음' 이 뜨게 된다.

 

✅ props.children

 

컴포넌트 태그 사이에 넣은 값을 조회하고 싶을 때 쓴다.

 

Wrapper.js 새로 작성

 

App.js의 리턴값을 Wrapper로 감싸주면..

 

화면처럼 기존 App 안에 있던 요소들이 없어졌다. 내부 내용이 보여지게 하기 위해선 Wrapper에서 props.children을 렌더링 해줘야 한다.

 

 

6. 조건부 렌더링

✅ 조건부 렌더링이란... 특정 조건에 따라 다른 결과물을 렌더링 하는 것.

 

App.js

true는 자바스크립트 값이므로 {}로 감싸준다.

 

조건에 따라 true면 *를 , false면 null을 표시하게 하므로, 3항 연산자를 이용한다.

*JSX에서 null, undefined, false를 쓰면 아무것도 나오지 않는다.

 

 

근데 조건이 참일때만 보여주고 아닐땐 숨기고 싶다? && 연산자를 쓰자.

 

isSpecial && <b>*</b>의 결과는

isSpecial 이 true일 때 : <b>*</b>

isSpecial 이 false일 때 : false

가 됨.

 

props값 설정을 생략하면 ...? ={true}

 

App.js

여기서 isSpecial 뒤의 값을 생략하면 참으로 간주한다.

즉 isSpecial 은 isSpecial = {true} 과 의미상 동일.

 

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

컴포넌트에서 동적인 변화가 발생할 때는 어떻게 처리해야 할까?리액트 16.8 이전에는 함수형 컴포넌트에서 상태를 관리할 수 없었다.리액트 16.8 에서 Hooks라는 기능이 추가되어 함수형 컴포넌트에서도 상태를 관리할 수 있다.이번에 쓸 useState가 Hooks 중 하나이다.

 

 

Counter.js

 

App.js

여기서 Counter 컴포넌트를 렌더링 해 준다.

 

숫자와 버튼이 구현되었다. 이제, 버튼 클릭 시 이벤트가 발생하게끔 설정해 보자

 

Counter.js

onIncrease와 onDecrease는 화살표 함수를 통해 구현했다.

함수형태로 구현하면 안된다!!!

이벤트를 설정할 때는 함수타입의 값을 넣어줘야 한다.

 

정상적으로 값이 프린트된다.

 

✅ 동적인 값 끼얹기, useState()

컴포넌트에서 동적인 상태를 state라 한다. 리액트의 useState를 사용하면 컴포넌트에서 상태를 관리할 수 있다.

{useState} 를 리액트 패키지로부터 import해 준다

setter함수는 전달 받은 값을 업데이트 시켜준다.

<h1></h1> 태그 안에는 0 이 아닌 바뀌는 값, 즉 {number}가 들어가야 한다.

값의 증감이 잘 이루어졌다.

 

✅ 함수형 업데이트

 

값 업데이트는 두 가지 방법이 있다.

1. Setter함수를 사용해서 업데이트 하고 싶은 값을 파라미터로 넣어준다.

2. 기존 값을 어떻게 업데이트 할 지에 대한 함수를 등록한다.

 

2번 방법으로 코드를 고쳐보자

 

prevNumber => prevNumber +1을 해도

number +1 과 동일하다

 

8. input 상태 관리하기

 사용자가 입력할 수 있는 input태그에 대해 알아보자.

InputSample.js

 

 

App.js

 

렌더링 해 준다.

 

 

이제는 useState를 이용해서 입력한 상태를 관리해 보자.

InputSample.js

 

setter함수로 입력받은 값을 text에 대입하거나, 초기화하는 함수 onChange, onReset을 만든다.

 

입력을 하면 하는 대로 값: 뒤에 입력값이 뜨고, 초기화를 누르면 입력창이 비워진다.

*이 때, input 태그의 상태관리를 위해 입력값을 저장하는 value값을 설정하는 것이 중요하다.

 

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

✅ input이 여러개일 때는 어떻게 해야 할까?

 

input개수가 여러개일때는

useState를 여러 번 사용하고 onChange를 여러 개 만들 수 있으나...

좋은 방법은 아니다.

 

더 좋은 방법은 input에 name을 설정하고, 이벤트가 발생했을 때 이 값을 참조하는 것이다.

그리고, useState에서는 문자열이 아니라 객체 형태의 상태를 관리해 주어야 한다.

 

음... 근데 문제는....

 

onChange 이벤트 함수 부분이 한번에 이해가 가질 않는다ㅜㅜ

하나하나 뜯어보기로 한다..

    const onChange = (e) => {
        const {value, name} = e.target;
        setInputs({
            ...inputs,
            [name] : value
        });
    };

...inputs에서 왜 ...를 쓰는고 하니...

우리 교재 2장-7번에 자세히 나와있었다!

https://learnjs.vlpt.us/useful/07-spread-and-rest.html

 

07. spread 와 rest 문법 · GitBook

07. spread 와 rest 이번에는 ES6 에서 도입된 spread 와 rest 문법에 대해서 알아보겠습니다. 서로 완전히 다른 문법인데요, 은근히 좀 비슷합니다. spread 일단 spread 문법부터 알아봅시다. spread 라는 단어

learnjs.vlpt.us

... 연산자의 정체는 스프레드 연산자 (자바스크립트에도 있었네..?)

아무튼 복습하자면 기존의 객체를 건들지 않고도 복사가 가능한 연산자이다.

즉 inputs의 객체 (name과 nickname이 들어있는 객체)를 복사하고,

사용자로부터 받은 value값을 name에 넣는 건데...

name이 아니라 [name] 이다?!?

주석을 보니 조금 이해된 것 같다. name은 키 , value는 값이다. 즉, 키-값을 쌍으로 관리함을 유추해볼 수 있다.

여기서 name은 추측해보건데 input 태그의 속성의 키인 것 같다. 즉, 상황에 따라 name의 키와 값이 바뀌게 되는 것이다.

 

이해하고 넘어가려는 찰나 이 문장이 거슬린다. 왜 쓴거지?

const { name, nickname } = inputs;

주석을 보면 "비구조화 할당을 통해 값 추출"이라 써 있는데..

 

앞에서 봤던 비구조화 문법을 다시 보자

Hello 안에 파라미터로 {color, name}을 줬기 때문에, 굳이 prop.color 나 prop.name을 쓰지 않아도 됐었다.

이를 여기 적용해 보면..

결국에는 파라미터로 들어갈 {name, nickname}을 한 번에 inputs로 통칭해 버린 것이고,

setInputs() 안 파라미터로 {...inputs, [name]: value} 결국 이렇게 들어가는 것이다.

오호라 그럼...음...

의문점이 더 생겼다... inputs 객체는 왜 복사하는 거지??

어차피 값을 덮어씌울 건데...

 

 

아무튼 이 내용은 질문하든, 정보를 찾던 해서 해결해야 할 것 같다.

어쨌든 ... 문법, 즉 spread 문법을 써서 복사하는 작업을 "불변성을 지킨다" 라고 부른다.

그래야만 리액트 컴포넌트에서 상대가 업데이트 됐음을 감지하고 리렌더링이 진행된다.

 

암튼 이번 주차 공부 끝!

반응형