본문 바로가기

프론트 JS 뿌시기

프론트 js 처음부터 뿌시기 5월 - 1 <React 기초 정리>

1. JS 기본 문법

변수 선언

  • let, const → 블록 단위 스코프
  • var → 블록을 벗어나도 접근 가능 (함수 스코프) → 사용을 지양하는 추세

Rest 연산자 (...)

  • 함수 매개변수, 객체 또는 배열에서 남은 요소들을 하나의 변수로 그룹화
// 함수 매개변수로 사용
function sum(...numbers) {
  return numbers.reduce((acc, current) => acc + current, 0);
}

// 객체에서 특정 속성을 제외한 나머지 속성들 추출
const { password, ...sampleObj } = person;

Null 병합 연산자 (??)

  • 좌변이 null이나 undefined일 경우에만 우변 값 사용
  • || 연산자와의 차이: ||는 모든 falsy 값을 평가하지만, ??는 null과 undefined만 평가
const itemsPerPage = preferences.itemsPerPage ?? 10;

ES6 모듈 시스템

// 내보내기
export const add = (a, b) => a + b;
export default function multiply(x, y) { return x * y; }

// 가져오기
import multiply, { add } from './math.js';
import { conflictFunction as function1 } from './module1';
import * as MathFunctions from './math.js';

Promise와 Async/Await

  • Promise: 비동기 작업의 완료 또는 실패를 나타내는 객체
const myPromise = new Promise((resolve, reject) => {  
    // 비동기 작업 후 resolve 또는 reject 호출
});
  • Async/Await: 비동기 코드를 동기적으로 작성할 수 있게 해주는 문법
async function fetchData() {  
    try {    
        const response = await fetch('https://api.example.com/data');    
        const data = await response.json();
        return data;  
    } catch (error) {    
        console.error("오류 발생:", error);  
    }
}
  • Async 함수는 항상 Promise를 반환
  • await는 Promise가 해결될 때까지 함수 실행을 일시 중단

2. React 기초

SPA(Single Page Application)

  • SPA는 하나의 HTML 페이지로 이루어진 애플리케이션
  • React, Vue, Angular가 대표적인 SPA 구현 도구

MPA vs SPA

  • MPA(Multi Page Application):
    • 여러 페이지로 구성된 애플리케이션
    • 페이지 이동 시 서버에서 새 HTML 파일을 생성하여 전송
    • SSR(Server Side Rendering) 방식 사용
  • SPA(Single Page Application):
    • 단일 페이지로 구성된 애플리케이션
    • 최초 로딩 후 페이지 전환 없이 필요한 데이터만 서버에서 받아와 동적으로 화면 갱신
    • CSR(Client Side Rendering) 방식 사용

SPA 동작 원리

  1. 요청: 클라이언트가 특정 UI 요소를 클릭하면 서버에 데이터만 요청
  2. 응답: 서버는 HTML 대신 JSON 형태의 데이터만 전송
  3. 렌더링: 클라이언트의 JavaScript가 받은 데이터로 DOM을 업데이트하여 UI 변경

라우팅(Routing)

  • Hashed Routing:
    • URL의 해시(#) 부분을 활용한 라우팅 기법 http://localhost:3000/#about
    • 해시 이후 부분은 서버로 전송되지 않고 클라이언트에서만 처리
  • Browser Routing:
    • 깔끔한 URL 구조를 제공하는 방식 www.sample-homepage.com/about
    • React에서는 사용자 경험을 위해 주로 Browser Routing 방식을 채택

React 프로젝트 생성 방법

  1. CRA(Create React App):
    yarn create react-app [프로젝트명]
    
  2. Vite:
    • CRA보다 훨씬 빠른 속도
    yarn create vite [프로젝트명] --template react
    
    • Vite가 최근 트렌드

3. React 컴포넌트(Components)

컴포넌트 유형

  1. 함수형 컴포넌트
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
  1. 클래스형 컴포넌트
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

부모-자식 컴포넌트 관계

  • 부모 컴포넌트: 다른 컴포넌트를 포함하는 컴포넌트
  • 자식 컴포넌트: 다른 컴포넌트 안에 포함되는 컴포넌트
function Child() { //자식
  return <div>나는 자식입니다.</div>;
}

function App() { //부모
  return <Child />;
}

4. Props

Props 특성

  • 읽기 전용(Read-only)
  • 컴포넌트 간 데이터 전달

Props 사용법

function Mother() {
  const name = '홍부인';
  return <Child motherName={name} />; // motherName으로 props 전달
}

function Child(props) {
  return <div>{props.motherName}</div>;
}

Props Children

  • 컴포넌트 태그 사이에 넣은 내용을 props.children으로 접근 가능:
function User(props) {
  return <div>{props.children}</div>;
}

function App() {
  return <User>안녕하세요</User>;
}

Props 추출 방법

  • 구조 분해 할당(Destructuring) (보통 이 방식으로 많이 사용)
function Todo({ title, body, isDone, id }) {
  return <div>{title}</div>;
}
  • 기본값 설정(Default Arguments):
function Welcome({ name = "Guest" }) {
  return <h1>Welcome, {name}!</h1>;
}

5. State

State 사용법

  • useState Hook을 사용하여 state 생성
const [name, setName] = useState("김할아");
setName("박할아"); // 상태 업데이트

State 응용 예시

function App() {
  const [name, setName] = useState("길동이");
  
  function onClickHandler() {
    setName("누렁이");
  }
  
  return (
    <div>
      {name}
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

입력 필드와 State

const App = () => {
  const [inputValue, setInputValue] = useState("");

  const onChangeHandler = (event) => {
    setInputValue(event.target.value);
  };

  return (
    <div>
      <input type="text" onChange={onChangeHandler} value={inputValue} />
      <p>입력값: {inputValue}</p>
    </div>
  );
};

Props Drilling

  • 여러 계층의 컴포넌트를 거쳐 데이터를 전달하는 것
  • 너무 깊은 경우 상태 관리 라이브러리(Redux, Recoil 등) 사용 고려

6. 불변성(Immutability)

리액트에서 불변성 유지하기

  • 배열/객체 수정 시 직접 변경하지 않고 전개 연산자(...)로 복사 후 수정
// 올바른 방법
setDogs([...dogs, "시고르자브르종"]);

// 잘못된 방법 (불변성 위반)
items.push(items.length + 1);
setItems(items);

7. 컴포넌트와 렌더링

컴포넌트의 본질

  • 컴포넌트:
    • 리액트의 핵심 빌딩 블록
    • UI 요소를 표현하는 최소 단위
    • 선언적 프로그래밍 방식으로 UI 구성

명령형 vs 선언형 프로그래밍

  • 명령형(DOM 직접 조작): 절차를 일일이 코드로 작성
  • 선언형(React): UI를 선언하면 React가 내부적으로 절차 수행

렌더링 과정

  • 트리거(Triggering): 렌더링 요청
  • 렌더링(Rendering): 컴포넌트가 UI 생성
  • 커밋(Commit): 결과를 실제 DOM에 반영

렌더링 발생 조건

  • 첫 리액트 앱 실행 시
  • 상태(state) 변경 시
  • props 변경 시
  • 부모 컴포넌트 리렌더링 시

8. DOM과 Virtual DOM

DOM(Document Object Model)

  • 웹페이지의 요소들을 트리 형태로 표현

가상 DOM(Virtual DOM)

  • 실제 DOM의 복사본으로 메모리에 객체 형태로 저장

DOM 조작 과정

  • 변경 전/후 두 버전의 가상 DOM 생성
  • Diffing: 두 가상 DOM 비교하여 변경점 파악
  • 재조정(Reconciliation): 변경된 부분만 실제 DOM에 적용

Batch Update

  • 여러 변경사항을 모아서 한 번에 DOM에 적용

브라우저 렌더링(페인팅)

  • 스타일 계산: HTML/CSS 분석하여 스타일 결정
  • 레이아웃: 요소 위치와 크기 계산
  • 페인팅: 실제 화면에 그리기

9. 반복 컴포넌트 리팩토링

map() 함수 사용

  • JavaScript의 map() 메서드를 사용하여 반복되는 JSX 요소를 동적 렌더링으로 변환
  • 예시: 여러 채소 컴포넌트를 단일 매핑으로 변환:
const vegetables = ["감자", "고구마", "오이", "가지", "옥수수"];

return (
  <div className="app-style">
    {vegetables.map((vegetableName) => {
      return (
        <div className="square-style" key={vegetableName}>
          {vegetableName}
        </div>
      );
    })}
  </div>
);

복잡한 데이터 처리

  • 객체 배열(더 복잡한 데이터 구조) 다루기
  • 반복적인 UI 요소를 처리하기 위한 별도 컴포넌트 생성
  • 사용자 데이터 예시:
function User(props) {
  return (
    <div>{props.user.age}살 - {props.user.name}</div>
  );
}

const App = () => {
  const users = [
    { id: 1, age: 30, name: '송중기' },
    { id: 2, age: 24, name: '송강' },
    { id: 3, age: 21, name: '김유정' },
    { id: 4, age: 29, name: '구교환' },
  ];

  return (
    <div className="app-container">
      {users.map((user) => {
        return <User user={user} key={user.id} />;
      })}
    </div>
  );
};

10. 상태 관리 및 사용자 상호작용

useState를 활용한 CRUD 구현

const [users, setUsers] = useState([/* 초기 사용자 */]);
const [name, setName] = useState('');
const [age, setAge] = useState('');

// 사용자 추가
const addUserHandler = () => {
  const newUser = {
    id: users.length + 1,
    age: age,
    name: name,
  };
  setUsers([...users, newUser]);
};

// 사용자 삭제
const deleteUserHandler = (id) => {
  const newUserList = users.filter((user) => user.id !== id);
  setUsers(newUserList);
};

컴포넌트 추출

  • 중복을 줄이기 위한 커스텀 버튼 컴포넌트 생성:
function CustomButton(props) {
  return <button onClick={props.onClick}>{props.children}</button>;
}

키(Key) 사용

  • React가 리스트 항목을 효율적으로 관리하기 위한 식별자
{
  users.map((user) => {
    return <User user={user} key={user.id} handleDelete={deleteUserHandler}/>;
  })
}

학습하며 어려웠던 점과 배운 점

map vs forEach 차이점

  • 반환 값
    • map(): 원본 배열의 각 요소를 변환한 새로운 배열을 반환합니다.
    • forEach(): 반환 값이 없습니다 (undefined).
  • 목적
    • map(): 배열의 각 요소를 변환하여 새 배열을 만드는 데 사용합니다.
    • forEach(): 단순히 배열을 순회하며 각 요소에 대해 함수를 실행하는 데 사용합니다.
  • React에서의 사용
    • map()은 JSX에서 배열 데이터를 컴포넌트 리스트로 변환할 때 특히 유용합니다.
    • forEach()는 새 배열을 반환하지 않기 때문에 JSX 내에서 직접 사용하기에는 적합하지 않습니다.

DOM이 트리 구조인 이유

<html>
  <body>
    <div>
      <h1>Hello</h1>
      <p>World</p>
    </div>
  </body>
</html>

위 HTML 코드의 트리 구조:

html
└── body
    └── div
        ├── h1
        │   └── "Hello"
        └── p
            └── "World"

리액트의 개발 방식

  • 리액트 개발자는 선언형 방식으로 개발하지만, 내부적으로는 명령형으로 동작
  • 가상 DOM을 사용하여 렌더링 최적화

이론적 개념 이해

  • DOM, 컴포넌트, 렌더링과 같은 개념적인 부분은 코딩보다 이론적 이해가 필요
  • 이번 학습을 통해 이러한 개념을 더 명확히 확립할 수 있었음