[20240129 TIL] React 기초 - A to Z

Haizel·2024년 1월 29일
1
post-thumbnail

01. React 기초


✷ React란?

React는 UI(사용자 인터페이스) 제작을 도와주는 자바스크립트 라이브러리로, cdn등을 활용해 간단히 사용할 수 있다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>리액트 맛보기</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script> // 👈 react
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script> // 👈 react-dom
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>  
    // 🖕 babel : JSX문법을 → JavaScript로 변환해주는 "Babel"  
  </head>
  <body>
    <div id="app"></div>
    
    // 👇 babel을 연결했기 때문에 → HTML 문서에서도 "JSX" 문법을 사용해 작성할 수 있다!
    <script type="text/babel"> 
      const appEl = document.getElementById("app");
      const root = ReactDOM.createRoot(appEl);

      const Greeting = () => <h1>Hello World</h1>;

      root.render(<Greeting />);
    </script>
  </body>
</html>

✷ React는 왜 탄생하게 되었나?

① HTML, CSS, JavaScript를 사용한 웹 서비스 개발의 한계

전통적인 웹 개발에서 HTML, CSS, JavaScript는 각각 구조, 스타일, 동작을 담당하는 역할을 해왔다. 하지만 몇가지 한계점을 맞닿드리며 "REACT"가 탄생하게 되었다.

  1. 유지보수의 어려움: 웹 애플리케이션이 커질수록 HTML, CSS, JavaScript 코드 간의 상호작용이 복잡해지고 유지보수가 어려워졌다.
  2. DOM 조작의 비효율성: JavaScript를 사용한 직접적인 DOM 조작은 성능 저하를 가져올 수 있다.
    • ❓ DOM은 웹 페이지의 구조를 나타내므로, 자주 변경될 경우 브라우저의 렌더링 성능이 크게 저하될 수 있기 때문이다.
  3. 재사용성 부족: 전통적인 방식에서는 코드의 재사용성이 낮고, 동일한 기능을 다른 부분에서 사용하기 위해 코드를 반복 작성해야 하는 경우가 많았다.

② 기존의 한계 극복을 위한 React의 탄생

React는 Facebook에서 대규모 애플리케이션의 복잡한 UI를 효율적으로 구현하고 관리하기 위해 개발되었다.

  1. 컴포넌트 기반 접근: React는 컴포넌트 기반 아키텍처를 도입하여 재사용 가능한 UI 단위를 생성하여 개발의 효율성을 높이고 유지보수를 용이하게 해준다.
  2. 가상 DOM 사용: React는 가상 DOM을 도입을 통해 실제 DOM의 변경을 최소화하여 브라우저의 불필요한 렌더링 과정을 줄이고 성능을 향상시킨다.
  3. 선언적 프로그래밍: React는 선언적 프로그래밍 방식을 채택하여, 복잡한 상태 관리와 UI 업데이트를 간결하고 예측 가능한 방식으로 처리할 수 있다.

✷ 선언적 프로그래밍 방식의 React

React는 선언형 프로그래밍 방식을 따라, 웹 페이지의 모습을 선언한다.

예를 들면 '로그인했을 때만 사용자 이름을 보여줘' 👈 처럼!
로그인 상태에 따라 사용자의 이름을 어떻게 보여줄지는 React가 결정한다.

✔️ 선언적 프로그래밍이란?

선언적 프로그래밍은 컴퓨터 프로그래밍의 한 종류로, 컴퓨터에게 무엇을(What) 해야 하는지 알려주는 방식이다.

쉽게 말하면 컴퓨터에게 "무엇을 해야 하는지"에 대한 목표만 정해주고, 해당 과정은 프로그래밍 언어나 프레임워크가 처리하도록 하는 프로그래밍이다.

  • 명령형 프로그래밍(HOW): "아침에 일어나서, 양치를 하고, 조깅을 한 다음에, 아침 식사를 해." 👈 단계별로 구체적인 지시를 내린다.
  • 선언적 프로그래밍(What): "오늘 아침엔 건강하게 시작해야 해." 👈 여최종 목표만 명시하고, 그 과정은 자유롭게 선택할 수 있다.

✔️ 선언적 프로그래밍의 장점

  1. 코드의 가독성이 좋다: 무엇을 하는 코드인지 한눈에 파악하기 쉽다.
  2. 변경이 간편하다: 목표만 바꾸면 되니까, 업데이트가 필요할 때 쉽게 바꿀 수 있다.

✷ React Component와 React Element

① 리액트 컴포넌트 (React Component)

  • 리액트 컴포넌트는 UI의 한 부분을 캡슐화한 코드블록을 말한다.
  • 과거에는 클래스를 사용해 컴포넌트를 만들었지만, 현재는 함수를 사용해 만드는 것이 일반적이다. (객체 지향 → 함수 지향)
function Greeting() {
	return <div>Hello World</div>
}

② 리액트 엘리먼트 (React Element)

  • 리액트 엘리먼트는 컴포넌트의 인스턴스로, 화면에 표시할 내용을 기술한 객체를 말한다.
  • JSX문법을 사용해 생성한다.
<Greeting />

02. DOM/Virtual DOM과 SPA의 개념


✷ DOM

  • DOM(Document Object Model)은 웹 페이지의 내용과 구조를 나타내는 방식이다.
  • HTML 문서에 있는 각 요소들(태그, 텍스트 등)을 브라우저가 이해할 수 있는 구조로 바꿔주는 역할을 한다.
  • 웹 페이지에 있는 모든 요소를 객체로 다룰 수 있어서, JavaScript를 통해 이 요소들을 변경하거나, 이벤트를 처리할 수 있다.

✷ Virtual DOM

  • 가상 DOM은 실제 DOM을 흉내 낸 JavaScript 객체이다.
  • React는 가상 DOM을 사용해 실제 DOM보다 빠르게 UI 변경사항을 관리한다.
    - 가상 DOM은 변경이 필요한 부분만 체크하여 실제 DOM에 반영해주기 때문에 페이지 전체를 새로 불러오는 것보다 훨씬 효율적이다.
  • React를 통해 가상 DOM을 이용해 필요한 부분만 자동으로 업데이트할 수 있어 훨씬 편리하게 그리고 높은 생산성으로 서비스를 만들 수 있다.

✷ SPA(Single Page Application)

  • SPA는 한 번의 페이지 로드로 전체 웹서비스에 필요한 모든 콘텐츠를 동적으로 렌더링하는 웹 애플리케이션 방식이다.
  • 사용자와의 상호작용에 따라 필요한 부분만 JavaScript를 통해 갱신한다. 즉 전체 페이지를 새로 불러오는 대신, 필요한 데이터만 주고받는 방식이다.
  • 빠른 페이지 반응성과 부드러운 사용자 경험을 제공이 필요할 때 주로 사용된다.

03. JSX


✷ JSX란

  • JavaSript를 확장한 문법으로, React에서 UI 구조를 표현하는데 사용된다.
  • HTML 태그와 유사하지만, 실제로는 JavaScript에서 확장된 문법임을 주의하자!
  • JSX는 React 라이브러리의 createElement 함수 호출을 보다 직관적으로 표현해주는 문법적 편의를 제공한다.
// 컴포넌트 선언
function SomeComponent() { 
	return <h1>{3 + 5}</h1> 
} 

// 엘리먼트 생성 
const someElement = <SomeComponent /> 
			
// 컴포넌트의 재사용 
function AnotherComponent() {
	return ( 
		<div>
		 <SomeComponent /> 
		 <SomeComponent /> 
	 </div>
	)
 }

✷ JSX 특징

❶ HTML과 유사한 문법

HTML Tag를 비슷하게 사용할 수 있어, 웹 개발자에게 친숙하고 읽기 쉽다.

❷ Javascript 표현식

JSX 내에서 JavaScript 표현식을 중괄호 {} 안에 사용할 수 있다.
이를 통해 Data Binding이나 반복문 처리 등이 가능하다.

function App() {
	const name = "Young"

	return <div>Hello~! My name is {name}.</div>
}

❸ 컴포넌트 기반

JSX를 사용해 React 컴포넌트를 → React 엘리먼트로 만들 수 있다.
이를 통해 UI를 구조화하고 재사용할 수 있다.


✷ JSX 장점 및 주의사항

✔️ JSX의 장점

  1. 읽기 쉽고 작성하기 편리하다.
    • UI 코드가 시각적으로 이해하기 쉬워 → 개발 효율성이 높아진다.
  2. 컴포넌트 구조 명확화
    • 컴포넌트의 구조를 한눈에 파악하기 쉬워 → 프로젝트 유지보수성이 향상된다.

✔ JSX 사용 시 주의사항

  • 브라우저는 JavaScript의 확장 문법인 JSX 문법을 이해하지 못하기 때문에 Babel과 같은 트랜스파일러(컴파일러)를 통해 JSX 문법 → JavaScript로 변환하는 과정이 필요하다.

04. React Product Poject 구조


❶ node_modules

  • npm으로 설치한 패키지들이 저장되어 있는 디렉토리

❷ public

  • 웹 서비스의 루트 경로(/)에서 바로 접근 가능한 Asset들을 담아두는 디렉토리

❸ src

  • 웹 서비스의 주요 소스 코드들을 저장하고 관리하는 디렉토리
    • App.js : 프로젝트 루트 컴포넌트
    • App.css : 루트 컴포넌트에서 사용할 스타일들을 담아 둔 스타일시트 파일
    • index.js : 루트 컴포넌트를 HTML document에 연결시켜주는 코드가 작성된 JS파일
    • index.css : 프로젝트 전반에 걸쳐 사용할 스타일들 담아 둔 스타일시트 파일
    • 그 외 : 그 외 파일들은 테스트와 디버깅을 위한 파일로, 대부분 사용하지 않는다.

❹ .gitignore

  • git을 사용한 소프트웨어 버전 관리에서 제외할 파일과 폴더를 적어두는 파일

❺ package.json

  • 프로젝트의 주요 정보들(이름, 설치된 패키지, 스크립트 등)을 담고 있는 파일

❻ package-lock.json

  • package-json을 통해 설치된 패키지들의 상세 정보를 담고 있는 파일

❼ README.md

  • 해당 프로젝트의 소스 코드에 대한 설명 등 다양한 정보를 기재해두는 파일

05. React 자세히 알아보기


✷ Props, Children

✔️ Props

  • 부모 컴포넌트로부터 자식 컴포넌트에 전달하는 데이터를 props이라고 한다.
  • props는 읽기 전용으로, 수정이 불가하다.
  • props를 잘 사용하면 컴포넌트의 재사용성과 유연성이 크게 증가한다.
  • 부모가 전달헤주는 props값이 변경되면 자식 컴포넌트는 리렌더링된다.
function Greeting(props) {
  return <h1>안녕하세요, {props.name}!</h1>;
}

function App() {
	const someName = "철수"
  return (
    <div>
      <Greeting name="지수" />
      <Greeting name={someName} />
    </div>
	);
}

✔️ Children

  • React에는 children 이라고 하는 특별한 props가 존재한다.
  • 컴포넌트에 값을 전달하면, 해당 컴포넌트에 children으로 props가 전달된다.
function App() {
	return <Card>안녕하세요?</Card>
}

function Card(props) {
	return <div>{props.children}</div>
}

✷ useState와 useEffect

✔️ State

리액트 공식문서에 따르면 state는 'A Component's Momory'라고 한다.

  • State는 React 컴포넌트 내부의 동적인 데이터를 관리하는 데 사용되는 데이터 구조로, 일반적으로 시간에 따라 변하는 값이나 사용자의 상호작용 또는 네트워크 응답 등에 의해 변경되는 값을 관리하는 역할을 한다.
  • State가 바뀌면 컴포넌트는 리렌더링되는데, 이때 리렌더링은 '함수가 재실행됨'을 의미하며 그 결과로 화면이 다시 그려지게 된다.
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // 초기값 0

  const increment = () => {
    setCount(count + 1); // count를 1 증가시키는 함수
  };

  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={increment}>증가</button>
    </div>
  );
}

✔️ useEffect

useEffect은 함수형 컴포넌트에서 부수효과(side effects)를 처리하기 위한 Hook이다.

useEffect(() => {
  // 부수 효과를 수행하는 코드를 작성한다.
  
  return () => {
    //  컴포넌트가 언마운트되기 전, 또는 업데이트되기 전에 실행되는 '정리(clean-up)' 코드를 작성한다.
  };
}, [dependencies]); //부수 효과를 유발할 의존성들을 추가한다.

그럼 어떤 부수효과(side effects)를 처리할까?

  • 데이터 fetching: API 호출과 같은 비동기 작업을 수행할 때 유용하다.
  • 이벤트 리스너 설정: 컴포넌트가 마운트될 때 이벤트 리스너를 설정하고, 언마운트될 때 정리된다.
  • DOM 업데이트: 외부 라이브러리를 사용하여 DOM을 직접 조작하는 경우에 사용할 수 있다.

👀 useEffect의 종속성 배열

❶ 종속성 배열 ❌

종속성(의존성) 배열을 제공하지 않으면, 컴포넌트가 마운트되거나 업데이트될때 마다 실행된다.

useEffect(() => {
  console.log('컴포넌트가 마운트되거나 업데이트될 때마다 실행됩니다.');
});

❷ 빈 배열 제공

빈 배열([])로 제공하면, 컴포넌트가 마운트될 때 한번만 실행된다.

useEffect(() => {
  console.log('컴포넌트가 마운트될 때 한 번만 실행됩니다.');
}, []);

❸ 특정 변수 할당

종속성 배열에 특정 값을 할당하면, 해당 특정 변수가 변경될 때만 실행된다.

useEffect(() => {
  console.log(`변수가 변경될 때만 실행됩니다: ${variable}`);
}, [variable]);

❹ 정리(Cleanup) 함수 사용

컴포넌트가 언마운트되거나 업데이트가 되기 전에 필요한 정리 작업용 함수로도 사용할 수 있다.

예를 들어 구독 해제나 타이머를 제거하는 작업을 들 수 있다.

useEffect(() => {
  const timer = setTimeout(() => {
    console.log('이 코드는 타이머에 의해 실행됩니다.');
  }, 1000);

  return () => {
    clearTimeout(timer); // 컴포넌트가 언마운트되거나 업데이트되기 전에 타이머를 제거합니다.
  };
}, []);

✷ React의 불변성 관리

🔥 불변성(Immutability) : 데이터가 생성된 후 수정되지 않는 성질

React에서 상태를 변경할 때는 반드시 항상 새로운 객체나 배열을 생성해 업데이트 해야한다.
👉 React는 상태 불변성을 유지함으로써 이전 상태와 새 상태를 효과적으로 비교하기 때문이다.

❶ 배열 상태의 불변성 관리

➕ 추가
새 요소를 배열에 추가할 때는 스프레드 연산자(...)를 사용해 기존 배열을 복사하고 새 요소를 추가한다.

const [items, setItems] = useState(['사과', '바나나']);

const addItem = (item) => {
  setItems([...items, item]);
};

➖ 제거
특정 요소를 제거할 때는 filter 함수를 사용해 해당 요소를 제외한 새 배열을 생성한다.

const [items, setItems] = useState(['사과', '바나나']);

const removeItem = (targetItem) => {
  setItems(items.filter(item => item !== targetItem));
};

✔️ 수정
특정 요소를 수정할 때는 map 함수를 사용해 해당 요소만 업데이트된 새 배열을 생성한다.

const [items, setItems] = useState(['사과', '바나나']);

const updateItem = (targetItem, newItem) => {
  setItems(items.map(item => item === targetItem ? newItem : item));
};

❷ 객체 상태의 불변성 관리

객체의 속성을 업데이트할 때는 객체 전개 연산자({...})를 사용해 객체를 복사하고, 특정 필드를 업데이트한다.

const [user, setUser] = useState({ name: '지수', age: 20 });

const updateUser = (newValues) => {
  setUser({ ...user, ...newValues });
};
profile
한입 크기로 베어먹는 개발지식 🍰

0개의 댓글