밍비
프로그램의 편린
밍비
전체 방문자
오늘
어제
  • 분류 전체보기 (64)
    • Spring (2)
    • TIL (23)
    • 프로그래머스 (12)
    • Udemy (16)
    • Typescript (2)
    • MERN (1)
    • AWS (7)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • Page Moving
  • 서비스아키텍처
  • useRef
  • 분산저장소
  • AWS Regions
  • useParams
  • 데이터 수정
  • 리액트 생애주기
  • react
  • 한입크기로잘라먹는리액트
  • overflow-wrap
  • 함수형 update
  • 리스트 조회
  • DOM
  • 리액트
  • 컴포넌트트리
  • state 관리
  • 리액트 프로젝트 만들기
  • Availability Zones
  • 한입 크기로 잘라먹는 리액트
  • State 합치기
  • 수평 스케일링
  • API 호출
  • useState
  • Points of Presence
  • 리액트 reducer
  • state 끌어올리기
  • 네이버커넥트
  • useNavigate
  • Edge Locations

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
밍비

프로그램의 편린

[React] React 입문
Udemy

[React] React 입문

2023. 4. 9. 18:21
728x90

이 블로그는 유데미 "한입 크기로 잘라먹는 리액트" 강의를 듣고 복습하고자 작성되었습니다!

목차

1. React가 필요한 이유

2. React App 만들기

3. JSX(Javascript Extension)

4. State

5. Props

 

 React가 필요한 이유 

저번 글에서 말했듯이 리액트가 브라우저에서 복잡하게 동작하는 JS 코드들을 단순화해준 프로그램입니다.

리액트를 사용한 JS 파일들은 마치 한 프로그램처럼 유기적으로 동작해 Web Application이라고도 부릅니다.

 

그렇다면 리액트가 왜 필요한지, 어떻게 유기적으로 동작하는지 알아봅시다.

 

1. 여러 페이지에서 중복되는 부분 재사용

웹사이트를 보다 보면, 네비게이션 바 등이 페이지가 바뀌어도 계속 보일 때가 있습니다.

이렇게 여러 페이지에서 중복되는 부분이 생길 때, 코드를 다 따로 쳐야하는 번거로운 상황이 생길 수 있습니다.

또한, 중복되는 부분에 변경사항이 생길 경우 모든 페이지를 반복해서 고쳐야 하는 일이 발생합니다.

이렇게 하나의 문제 때문에 여러 페이지를 반복해서 고치게 되는 것을 Shotgun Surgery(산탄총 수술)이라고 합니다.

이를 방지하기 위해 리액트는 반복되는 부분을 컴포넌트로 만들어, 해당 컴포넌트의 이름만 불러오면 재사용이 가능하게 하였습니다.

컴포넌트 기반 UI 라이브러리 덕분에 리액트에서 유기적인 동작이 가능한 것입니다.

 

2. 선언형 프로그래밍

jQuery와 같은 기존의 명령형 프로그래밍은 어떤 동작을 수행할 때, 그 절차를 하나하나 나열해야 했습니다.

이로 인해 코드가 너무 길어지고, 해석하기 힘들다는 단점이 있었습니다.

이를 해결한 것이 리액트와 같은 선언형 프로그래밍입니다.

선언형 프로그래밍은 현재 하려고 하는 일만 작성하면 되기 때문에 코드가 간결해지고 가독성이 좋아진다는 장점이 있습니다.

 

3. Virtual DOM

DOM(Document Object Model)이란 웹페이지를 이루는 태그들을 JS가 이용할 수 있게 만든 객체 모델입니다.

우리가 HTML코드를 왼쪽부분처럼 작성해도, 브라우저는 이를 트리 형태의 객체 모델로 인식하고 있다는 뜻입니다.

원래 페이지에 항목이 추가되는 등, 페이지 내용이 업데이트될 때마다 브라우저가 실제 DOM에 반영하기 위해 필요 이상의 많은 연산을 해야 했습니다. 작은 업데이트가 있어도 매번 새로 DOM에 반영한다면, 결국 성능 저하의 문제가 발생하게 됩니다.

 

리액트는 이를 해결하기 위해 Virtual DOM, 즉 가상 DOM에 업데이트된 상황을 모아놓은 후, 한 번에 렌더링합니다.

결국 연산의 수가 훨씬 줄어들게 되고, 높은 성능을 유지할 수 있게 됩니다.

 

 React App 만들기 

리액트 App을 만들기 전, 리액트와 같이 쓰이는 라이브러리에 대해 먼저 알아봅시다.

대표적으로 Webpack과 Babel이 있는데,

Webpack은 여러 JS 파일을 한 파일로 합쳐주는 모듈 번들 라이브러리이고,

Babel은 JSX 등의 쉽고 직관적인 JS 문법을 쓸 수 있도록 해주는 라이브러리입니다.

즉 Webpack은 분리된 컴포넌트들을 하나의 JS로 변환해주고, Babel은 리액트에서 HTML 문법을 쓸 수 있게 해주는 것입니다.

 

강의에서는 리액트, Webpack, Babel이 합쳐진 CRA(Create-React-App) 패키지를 다운로드했습니다.

이렇게 여러 필요한 라이브러리를 같이 설치해주는 것을 Boiler Plate라고 합니다.

 

CRA를 통해 리액트 앱을 처음 만들 때는 아래와 같이 명령어를 입력합니다

npx create-react-app [프로젝트 이름]

npm이 아니고 npx인 이유는

npm 패키지를 딱 한 번만 쓰고 싶을 때, 패키지를 임시 설치해서 실행만 시키는 명령어가 npx이기 때문입니다.

이후 npm start를 입력하면 localhost:3000으로 웹서버가 열립니다.

브라우저가 내 PC 주소에 요청해서, 내 PC가 리액트 앱을 반환한 것입니다.

그리고, App.js에서 App함수의 반환값이 HTML 속 root div에 자식 요소로 들어가있는 것을 확인할 수 있습니다.

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

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

index.js 파일을 보면, root.render 속에 <App />가 있어서 App의 내용이 root 안에 렌더됨을 알 수 있습니다.

 

 참고로, 깃허브 등에 내 프로젝트를 통째로 올릴 때는 node_modules가 너무 커서 오래걸릴 것입니다.

그래서 node_modules는 빼고 올리는 게 좋습니다.

파일을 다운받을 때는 package.json을 확인해 사용하는 모듈을 파악한 후 해당 모듈만 아래의 명령어를 써서 다운받으면 됩니다.

npm i [모듈 이름]

 

 JSX(Javascript Extension) 

JSX(Javascript Extension)은 JS에 HTML 요소를 섞은 문법입니다.

리액트는 App이라는 함수에서 JS extension을 시켜 컴포넌트를 만들 수 있습니다.

 

1. 모듈 import, export

 

여기서 모듈을 내보내고 불러올 때는 module.exports를 쓰지 않고, export default [컴포넌트 이름]을 사용합니다.

이렇게 내보낸 후, 다른 파일에서는 import [컴포넌트 이름] from [컴포넌트 경로]로 받아옵니다.

예를 들어, index.js에서 App.js의 App 컴포넌트를 불러온다고 합시다.

// App.js
import './App.css';
 
function App() {
  let name = 'mingbee';
  return (
    <div className='App'>
      <header className='App-header'>
        <h2>안녕 리액트 {name}</h2>
      </header>
    </div>
  );
}
 
export default App;

이렇게 App 컴포넌트를 export하고 나서, index에서 다음과 같이 불러옵니다.

import App from './App';

2. 태그 규칙

 

닫힘 규칙: 모든 태그는 닫아줘야 한다.

<h2></h2>처럼 여는 태그가 있으면, 닫는 태그로 닫아주어야 합니다.

<img />같은 태그는 여는 태그가 곧 닫는 태그입니다. 이런 태그를 Self-Closing Tag라고 합니다.

 

최상위 태그: jsx에서 컴포넌트를 반환하려면 반드시 하나의 최상위 태그로 다른 태그들을 묶어야 합니다.

이렇게 한 태그로 묶기 싫을 경우, React.fragment를 이용할 수 있습니다.

아래는 React.fragment의 예제 코드입니다.

import React from 'react';
 
// App함수 반환값
 
return (
    <React.Fragment>
      <MyHeader />
      <header className='App-header'>
        <h2>안녕 리액트</h2>
      </header>
      <MyFooter />
    </React.Fragment>
  );

이렇게 React 모듈을 import한 후 사용하면 됩니다.

import './App.css';
 
import React from 'react';
import MyHeader from './MyHeader';
import MyFooter from './MyFooter';
function App() {
  let name = 'mingbee';
  return (
    <>
      <MyHeader />
      <header className='App-header'>
        <h2>안녕 리액트 {name}</h2>
      </header>
      <MyFooter />
    </>
  );
}
 
export default App;

React.fragment는 꼭 쓰지 않고, 빈 괄호로 놔둬도 됩니다.

또 위 코드처럼 jsx는 <h2></h2> 태그 안에 { [변수명] } 을 쓸 경우, 문자열이나 숫자를 렌더링할 수 있습니다.

 

그렇다면, App 컴포넌트에서 CSS 파일 없이 인라인으로 스타일을 바꾸어보겠습니다.

import React from 'react';
import MyHeader from './MyHeader';
function App() {
  let name = 'mingbee';
  const style = {
    App: {
      backgroundColor: 'teal',
    },
    h2: {
      color: 'antiquewhite',
    },
    bold_text: {
      color: 'aliceblue',
    },
  };
  return (
    <div style={style.App} className='App'>
      <MyHeader />
      <h2 style={style.h2}>안녕 리액트 {name}</h2>
      <b style={style.bold_text} id='bold_text'>
        React.js
      </b>
    </div>
  );
}
 
export default App;

먼저 App 컴포넌트 안에 style 객체를 선언한 후, return 시에 태그에서 필요한 부분만 속성으로 가져오면 됩니다.

return 부분을 보면, div나 h2, b 안에 style로 불러오는 것을 볼 수 있습니다.

 

아래 코드는 중괄호와 삼항연산자를 합친 조건부 렌더링 실습 코드입니다.

import React from 'react';
import MyHeader from './MyHeader';
function App() {
  let name = 'mingbee';
  const style = {
    App: {
      backgroundColor: 'teal',
    },
    h2: {
      color: 'antiquewhite',
    },
    bold_text: {
      color: 'aliceblue',
    },
  };
 
  const number = 5;
  return (
    <div style={style.App} className='App'>
      <MyHeader />
      <h2 style={style.h2}>안녕 리액트 {name}</h2>
      <b style={style.bold_text} id='bold_text'>
        {number}는 {number % 2 === 0 ? '짝수' : '홀수'}
      </b>
    </div>
  );
}
 
export default App;

return 부분에서, 

<b style={style.bold_text} id='bold_text'> {number}는 {number % 2 === 0 ? '짝수' : '홀수'} </b>

이 부분은 number 변수에 숫자를 입력받아, 홀수인지 짝수인지 알려주는 부분입니다.

이렇게 중괄호 안에 삼항연산자를 사용해 number 값에 따라 다른 결과를 반환시킬 수 있습니다.

 

 State 

State란, 계속해서 변화하는 특정 상태를 말합니다. 웹사이트의 다크모드/라이트모드 상태처럼, 상태에 따라 각각 다른 동작을 합니다.

리액트에서는 useState라는 메소드를 사용해 state를 관리합니다.

useState는 배열을 반환하는데, 배열의 비구조화 할당을 통해 0번째에 state를, 1번째에 setState 함수를 받아옵니다.

아래의 예시 코드를 봅시다.

const [count, setCount] = useState(0);

여기서 배열 0번째인 count는 상태의 값이 되고, setCount는 상태를 변화시키는 함수가 됩니다.

useState(0)은 count의 초기상태를 0으로 해서 state를 만들겠다는 뜻입니다.

컴포넌트는 자신이 가진 state가 변화하면 함수를 다시 호출해 화면을 리렌더합니다.

위에서 작성한 count state를 이용해 버튼을 눌러 숫자를 세는 counter사이트를 만들어보겠습니다.

import React, { useState } from 'react';
const Counter = () => {
  const [count, setCount] = useState(0);
  const onIncrease = () => {
    setCount(count + 1);
  };
  const onDecrease = () => {
    setCount(count - 1);
  };
  return (
    <div>
      <h2>{count}</h2>
      <button onClick={onIncrease}>+</button>
      <button onClick={onDecrease}>-</button>
    </div>
  );
};
export default Counter;

 

먼저 useState 사용 전 import를 해줍니다.

또, return 부분을 보면, + 버튼과 -버튼이 있고, +버튼을 누르면 OnIncrease, -버튼을 누르면 onDecrease 함수가 실행됩니다.

즉 count state는 0에서 출발해, +버튼을 누르면 1 증가되고, -버튼을 누르면 1 감소하게 됩니다.

 

리액트에서는 여러 state를 한 컴포넌트로 관리하는 것이 가능합니다.

아래 코드처럼 count state를 하나 더 만들어 두 개의 counter를 만들 수도 있습니다.

import React, { useState } from 'react';
const Counter = () => {
  const [count, setCount] = useState(0);
  const onIncrease = () => {
    setCount(count + 1);
  };
  const onDecrease = () => {
    setCount(count - 1);
  };
 
  const [count2, setCount2] = useState(0);
  const onIncrease2 = () => {
    setCount2(count2 + 1);
  };
  const onDecrease2 = () => {
    setCount2(count2 - 1);
  };
  return (
    <div>
      <h2>{count}</h2>
      <button onClick={onIncrease}>+</button>
      <button onClick={onDecrease}>-</button>
 
      <h2>{count2}</h2>
      <button onClick={onIncrease2}>+</button>
      <button onClick={onDecrease2}>-</button>
    </div>
  );
};
export default Counter;

이렇게 말입니다.

 

 

 Props 

1. Props 기초

count의 초기값을 App 컴포넌트가 전달하는 값으로 설정하려면,

App.js에서 Counter 컴포넌트를 추가할 때 아래와 같이 props를 추가하면 됩니다.

<Counter initialValue={5} a={10} />

이렇게 쓸 경우 Counter.js에서 Counter 컴포넌트의 인자로 props를 받게 됩니다.

props는 initialValue: 5, a: 1 를 가진 한 객체이므로, 점표기법을 통해 각 요소에 접근해 사용 가능합니다.

아래는 예시 코드입니다.

// App.js
import React from 'react';
import MyHeader from './MyHeader';
import Counter from './Counter';
 
function App() {
  let name = 'mingbee';
 
  const number = 5;
  return (
    <div>
      <MyHeader />
      <Counter initialValue={5} a={10} />
    </div>
  );
}
 
export default App;

App.js에서 Counter 컴포넌트를 불러올 때 props로 initialValue는 5, a는 10을 전달했습니다.

// Counter.js
import React, { useState } from 'react';
const Counter = (props) => {
  const [count, setCount] = useState(props.initialValue);
  const onIncrease = () => {
    setCount(count + 1);
  };
  const onDecrease = () => {
    setCount(count - 1);
  };
  return (
    <div>
      <h2>{count}</h2>
      <button onClick={onIncrease}>+</button>
      <button onClick={onDecrease}>-</button>
    </div>
  );
};
export default Counter;

이후 Counter.js는 props로 아까의 initialValue와 a를 받아, count state의 초기값으로 props.initialValue를 사용하였습니다.

 

2. 객체 props 전달

그런데 props로 줄 요소가 너무 많아지면 코드가 한 줄로 엄청 길어져 가독성이 떨어집니다.

이런 경우에는 아예 객체를 만들고, spread 연산자로 전달하는 것이 좋습니다.

const counterProps = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
  e: 5,
  initialValue: 5,
};

이렇게 객체를 생성한 후,

<Counter {...counterProps} />

return에서 이렇게 Counter 컴포넌트를 불러오는 것입니다.

이렇게 전달할 경우, Counter에서는 객체의 각 요소를 비구조화할당을 통해 원하는 것만 가져온 후 바로 사용 가능합니다.

const Counter = ({ initialValue }) => {

이렇게 counterProps의 속성들 중 원하는 요소(initialValue)만 가져온 후 아래처럼 사용 가능합니다.

const [count, setCount] = useState(initialValue);

 

그런데 만약 App.js에 있던 props 객체에서 initialValue가 사라지거나 이름이 수정될 경우 값이 나오지 않는 버그가 발생합니다.

이를 대비하고자, Counter 컴포넌트에 대해 defaultProps를 설정할 수 있습니다.

Counter.defaultProps = {
  initialValue: 0,
};

컴포넌트 선언 이후 위와같이 작성하면, initialValue를 받지 못할 경우 기본값으로 0이 설정됩니다.

 

3. Props로 동적데이터(ex: state) 전달

props로 state같은 동적데이터도 전달이 가능합니다.

Counter 컴포넌트가 갖고있는 count state를 props로 가져와 양수인지 음수인지 출력해보겠습니다.

먼저, count를 props로 불러와 홀짝을 구분하는 프로그램을 만듭니다.

// OddEvenResult.js
const OddEvenResult = ({ count }) => {
  console.log(count);
  return <>{count % 2 === 0 ? '짝수' : '홀수'}</>;
};
 
export default OddEvenResult;

그 다음, 이 컴포넌트를 Counter.js에서 import하고, return에 자식요소로 배치 후 count props를 전달합니다.

// Counter.js
import React, { useState } from 'react';
import OddEvenResult from './OddEvenResult';
const Counter = ({ initialValue }) => {
  const [count, setCount] = useState(initialValue);
 
  const onIncrease = () => {
    setCount(count + 1);
  };
  const onDecrease = () => {
    setCount(count - 1);
  };
  return (
    <div>
      <h2>{count}</h2>
      <button onClick={onIncrease}>+</button>
      <button onClick={onDecrease}>-</button>
      <br/>
      <OddEvenResult count={count} />
    </div>
  );
};
 
Counter.defaultProps = {
  initialValue: 0,
};
export default Counter;

마지막으로, App.js에서 Counter 컴포넌트를 import해 배치합니다.

// App.js
import React from 'react';
import Counter from './Counter';
 
function App() {
  const number = 5;
  return (
    <div>
      <Counter />
    </div>
  );
}
 
export default App;

이렇게 하면 Counter와 함께 count state가 홀수인지 짝수인지 알려주는 프로그램을 만들 수 있습니다.

3. Props로 HTML 요소 전달

그럼 마지막으로 이 모든 요소들을 감쌀 Container 컴포넌트를 만들어서 App 컴포넌트에 넣어보겠습니다.

Container는 현재까지 만든 요소들을 감쌀 style입니다.

 

먼저 Container 컴포넌트를 작성합니다.

// Container.js
const Container = ({ children }) => {
  return (
    <div style={{ margin: 20, padding: 20, border: '1px solid gray' }}>
      {children}
    </div>
  );
};
export default Container;

Container 컴포넌트는 children을 props로 받습니다. Container는 style을 적용한 div 안에 props를 다 넣어버리는 컴포넌트입니다.

 

그 다음, App.js를 작성합니다. 이때, App이 return하는 모든 요소가 Container의 props로 들어가야 합니다.

// App.js
import React from 'react';
import Counter from './Counter';
import Container from './Container';
 
function App() {
  const number = 5;
  return (
    <Container>
      <div>
        <Counter />
      </div>
    </Container>
  );
}
 
export default App;

이렇게 최상위 div를 Container 태그 안에 넣으면,

태그 안의 요소들이 모두 children props로 들어가, Container의 style을 적용받습니다.

결과적으로, 이렇게 Container 박스 안에 다른 요소들이 들어간 것을 확인할 수 있습니다!

 

오늘은 React의 기초적이지만 필수 개념인 CRA(Create React App), JSX 문법, State, Props에 대해 다뤄보았습니다.

다음 글부터는 본격적인 웹사이트 제작 포스팅이 될 것인데, 실제로 배포할 사이트를 만드는 과정이라 더 흥미로울 것 같습니다!

앞으로도 열심히 작성해보겠습니다. 감사합니다ㅎㅎ

 

728x90

'Udemy' 카테고리의 다른 글

[React] React에서 DOM 조작, 데이터 조회 기능 (리스트 렌더링)  (0) 2023.04.15
[React] 리액트로 프로젝트 만들기  (0) 2023.04.15
[Node.js] Node.js 기초, 실행환경 구성  (0) 2023.04.09
[React] Javascript 응용  (0) 2023.04.08
[React] Javascript 기본  (0) 2023.04.04
    밍비
    밍비

    티스토리툴바