한 걸음씩

리액트 총정리 본문

React

리액트 총정리

winter17 2023. 7. 30. 21:39

1. 컴포넌트, JSX

 

  컴포넌트

  • UI를 구성하는 독립적이고 재사용 가능한 단위.
  • UI를 구성 구성하는 작은 부분들을 컴포넌트로 나누어 개발하면 코드의 가독성과 유지보수성이 향상된다

  JSX

  • JavaScript XML의 약자로, 리액트에서 UI를 표현하는 데 사용되는 문법
  • JavaScript 코드 안에 XML과 비슷한 문법을 사용하여 UI 컴포넌트를 작성할 수 있도록 도와준다
  • JSX를 사용하면 UI 구조를 더 직관적이고 가독성 좋게 작성할 수 있고, 동적인 값을 쉽게 포함할 수 있다

결과물

  • 컴포넌트 이름의 첫 글자는 대문자
  • css 스타일 사용법과 변수 사용법
import "./styles.css";

function App() {
  const name = "Tom";
  const naver = {
    name: "네이버",
    url: "https://naver.com"
  };
  return (
    <div>
      <h1
        style={{
          color: "white",
          backgroundColor: "green"
        }}
      >
        Hello {name}
      </h1>
      <a href={naver.url}>{naver.name}</a>
    </div>
  );
}
export default App;

 

  컴포넌트 만들기

  • 같은 컴포넌트를 어디서든 몇번이든 재사용가능하다

결과물
파일 구조
아래 컴포넌트 구조를 그림으로 표현했을 때

// App.js
import "./styles.css";
import Hello from "./component/Hello";
import Welcome from "./component/Welcome";

function App() {
  return (
    <div className="App">
      <Hello />
      <Welcome />
    </div>
  );
}
export default App;

 

// Hello.js
import World from "./World";
function Hello() {
  return (
    <div>
      <h1>Hello</h1>
      <World />
    </div>
  );
}
export default Hello;

 

// World.js
function World() {
  return (
    <div>
      <h1>world!</h1>
    </div>
  );
}
export default World;

 

// Welcome.js
function Hello() {
  return (
    <div>
      <p>Welcome</p>
    </div>
  );
}
export default Hello;

2. module css

  • CSS를 모듈화하여 컴포넌트별로 스타일을 캡슐화하는 기술
  • 각각의 컴포넌트가 자신만의 독립적인 스타일을 유지할 수 있으며, 전역 스타일 충돌을 방지하고 재사용 가능한 컴포넌트를 쉽게 작성할 수 있다
/* Button.module.css */

.button {
  padding: 10px 20px;
  background-color: blue;
  color: white;
  border: none;
  cursor: pointer;
}

/* 다른 컴포넌트의 CSS 파일 */

/* 다른 모듈 CSS와 클래스 이름이 겹치더라도 서로 영향을 주지 않습니다. */
.button {
  background-color: red;
}

[코드 설명]

Button.mudule.css는 button 클래스를 정의한다.

이 클래스는 해당 CSS 파일 내부에서만 유효하다

 

// Button.js

import React from 'react';
import styles from './Button.module.css';

function Button() {
  return <button className={styles.button}>Click me</button>;
}

export default Button;

[코드 설명]

Button 컴포넌트는 Button.module.css 파일을 import 하여 styles.button 클래스를 적용한다

이렇게 하면 해당 컴포넌트에서만 button 클래스의 스타일이 적용되며, 다른 컴포넌트와의 스타일 충돌을 방지할 수 있다


3. Handling Events

  • 사용자의 상호작용에 대응하여 UI를 업데이트하는 기능을 구현하는 것을 의미
  • 리액트에서 이벤트 핸들링은 기본적으로 JavaScript의 이벤트 핸들링과 유사하지만, JSX 문법과 함께 사용되어 좀 더 간편하고 선언적인 방식으로 구현할 수 있다
  • onClick, onChange, onSubmit 등 일반적인 JavaScript 함수와 동일한 방식으로 정의된다
import React, { useState } from 'react';

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

  // 클릭 이벤트 핸들러 함수
  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      {/* 버튼을 클릭할 때 handleClick 함수가 호출됨 */}
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

export default Counter;

[코드 설명]

Increment버튼을 클릭할 때마다 handleClick 이벤트 핸들러 함수가 호출되어 setCount함수를 사용하여 count 상태를 업데이트한다

handleClick함수를 버튼의 onClick 이벤트에 전달하면, 해당 버튼을 클릭할 때마다 카운터가 증가하게 된다


4. state, useState

  • state는 리액트 컴포넌트에서 동적인 데이터를 관리하는 데 사용된다
  • useState는 리액트의 훅(Hook)중 하나로, 함수형 컴포넌트에서 state를 사용할 수 있게 해 준다
  • useState를 통해 컴포넌트의 상태를 선언적으로 업데이트할 수 있어서 코드의 가독성과 유지보수성을 향상할 수 있다
import React, { useState } from 'react';

function Counter() {
  // useState 훅을 사용하여 count 변수와 setCount 함수를 생성하여 초기값을 0으로 설정
  const [count, setCount] = useState(0);

  // 클릭 이벤트 핸들러 함수
  const handleClick = () => {
    // setCount 함수를 사용하여 count 상태를 업데이트
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      {/* 버튼을 클릭할 때 handleClick 함수가 호출됨 */}
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

export default Counter;

[코드 설명]

useState 훅을 사용하여 count 변수와 setCount 함수를 생성하여 state를 관리한다.

useState 훅은 배열을 반환한다

배열의 첫 번째 요소는 현재 state값(count)이고, 두 번째 요소는 state를 업데이트하는 함수(setCount)

useState함수의 인자로는 초기 state의 값을 전달한다. 위의 코드에서 useState(0)으로 초기값을 0으로 설정했다

 

handleClick이라는 이벤트 핸들러 함수는 버튼이 클릭되었을 때 호출되며, setCount함수를 사용하여 count상태를 업데이트한다

버튼을 클릭할 때마다 count값이 증가하게 된다


5. props

  • 리액트 컴포넌트에서 데이터를 전달하는 데 사용되는 속성(properties)이다
  • 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 주로 props를 사용한다
  • props는 부모 컴포넌트에서 자식 컴포넌트로 읽기 전용으로 전달되며, 자식 컴포넌트에서는 받은 props를 사용하여 해당 데이터를 렌더링 하거나 기능을 구현할 수 있다
  • 컴포넌트 간에 흐름이 명확하고 독립성을 유지할 수 있다
  • 재사용 가능하다
// Parent.js
import React from 'react';
import Child from './Child';

function Parent() {
  const name = 'John';
  const age = 30;

  return (
    <div>
      {/* Child 컴포넌트에 name과 age를 props로 전달 */}
      <Child name={name} age={age} />
    </div>
  );
}

export default Parent;

 

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

function Child(props) {
  // props로 전달받은 데이터를 사용하여 렌더링
  return (
    <div>
      <p>Name: {props.name}</p>
      <p>Age: {props.age}</p>
    </div>
  );
}

export default Child;

[코드 설명]

Parent 컴포넌트는 name과 age라는 데이터를 가지고 있다. 이 데이터를 Child 컴포넌트로 props를 사용하여 전달한다.

Child 컴포넌트에서는 props를 인자로 받아 해당 데이터를 사용하여 렌더링 한다. 

props는 Child 컴포넌트의 인자로 전달되며, props.name, props.age와 같은 방식으로 Parent 컴포넌트에서 전달된 데이터를 사용할 수 있다.


6. 더미 데이터 구현, map() 반복문

 

  더미 데이터(dummy data)

  • 실제 데이터를 대체하여 개발 중인 애플리케이션을 테스트하거나 디자인을 확인하는 데 사용되는 가상의 데이터
  • 주로 개발자가 수동으로 작성하여 사용하며, 실제 데이터를 받아오기 전에 임시로 사용된다
  • 더미 데이터를 사용하여 애플리케이션의 UI를 구성하고 테스트하는 것이 효율적이다

 

  map()

  • 배열의 각 요소를 변환하여 새로운 배열을 반환하는 배열 메서드
  • map() 메서드를 사용하여 동적인 리스트를 렌더링 할 때, 각 리스트 아이템에 고유한 key속성을 지정하는 것이 중요하다. 이를 통해 리액트가 리스트 변경을 추적하여 성능을 최적화할 수 있다. map()과 key 속성은 동적인 리스트를 렌더링 하는데 필수적으로 사용되어야 한다.

 

  key 속성

  • key 속성은 각각의 리스트 아이템에 고유한 식별자를 지정하는 역할을 한다
  • 리액트가 가상 DOM에서 리스트 아이템의 변경을 추적할 때 사용되며, 리스트 아이템이 추가, 삭제, 수정될 때 성능을 최적화하는데 도움을 준다
  • key 속성을 사용하지 않을 경우, 리액트는 경고를 표시하며 리스트 아이템을 렌더링 하지만, 리스트의 변경이 발생했을 때 성능 저하와 오류가 발생할 수 있다

 

// DummyData.js
const dummyData = [
  { id: 1, name: 'John', age: 30 },
  { id: 2, name: 'Jane', age: 25 },
  { id: 3, name: 'Tom', age: 28 },
  { id: 4, name: 'Alice', age: 35 },
];

export default dummyData;

[코드 설명]

dummyData라는 더미 데이터를 배열로 정의한 것이다. 

각 객체는 id, name, age라는 프로퍼티를 가지고 있으며, 각 객체는 개별적인 데이터를 나타낸다.

 

// DummyList.js
import React from 'react';
import dummyData from './DummyData';

function DummyList() {
  return (
    <div>
      <h2>Dummy Data List</h2>
      <ul>
        {dummyData.map((item) => (
          // 더미 데이터를 기반으로 리스트 아이템 렌더링
          <li key={item.id}>
            <p>Name: {item.name}</p>
            <p>Age: {item.age}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default DummyList;

 

[코드 설명]

DummyList 컴포넌트에서 dummyData를 사용하여 리스트 아이템을 렌더링 하는 것이다

map() 메서드를 사용하여 dummyData 배열을 순회하며 각각의 아이템에 대해 JSX 요소를 생성한다.

각 아이템의 id, name, age 프로퍼티를 사용하여 리스트 아이템의 내용을 동적으로 생성한다.

 

// 결과
Dummy Data List
- Name: John
  Age: 30
- Name: Jane
  Age: 25
- Name: Tom
  Age: 28
- Name: Alice
  Age: 35

7. react-router-dom

  • 리액트 프로젝트에서 브라우저의 주소를 통해 페이지를 라우팅 하고 관리하는 데 사용되는 라이브러리
  • 싱글 페이지 애플리케이션(SPA)을 만들 수 있으며, 페이지 간의 전환과 URL에 따른 컴포넌트 렌더링을 간단하게 처리할 수 있다

 

// 설치
npm install react-router-dom

 

 

// App.js
import React from 'react';
import { BrowserRouter, Route, Switch, Link } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Contact from './Contact';

function App() {
  return (
    <BrowserRouter>
      <div>
        <nav>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/contact">Contact</Link></li>
          </ul>
        </nav>

        <Switch>
          {/* exact: 정확한 경로 설정 */}
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route path="/contact" component={Contact} />
        </Switch>
      </div>
    </BrowserRouter>
  );
}

export default App;

[코드 설명]

react-router-dom을 사용하여 페이지를 라우팅 하려면, 프로젝트의 최상위 컴포넌트(일반적으로 App.js)를 BrowserRouter 컴포넌트로 감싸야한다. 이렇게 함으로써 브라우저의 주소와 라우팅을 관리할 수 있다

 

위의 코드에서 BrowserRouter 컴포넌트로 감싸고, 라우트를 정의하기 위해 Route 컴포넌트를 사용한다.

Link컴포넌트를 사용하여 내비게이션 메뉴를 생성하고, to 속성에 URL 경로를 설정하여 해당 페이지로 이동할 수 있다

 

Switch 컴포넌트는 여러 라우트 중에서 첫 번째로 매칭되는 라우트만 렌더링 하도록 한다. 이렇게 함으로써 중복된 라우트가 렌더링 되는 것을 방지할 수 있다

 

Link 컴포넌트 1. react-router-dom 라이브러리에서 제공하는 컴포넌트로, 리액트의 라우터와와 함께 사용된다
2. 브라우저의 주소를 변경하면서 페이지 이동을 처리한다
3. 전통적인 페이지 이동이 아닌 리액트의 SPA(Single Page Application)에서는 페이지 전체를 새로 로드하지 않고, 가상 DOM을 사용하여 렌더링을 처리한다.
4. HTML에 직접 렌더링되지 않으며, 리액트의 라우터와 함께 동작하는 기능을 제공한다. 주로 네비게이션 메뉴나 링크를 생성할 때 사용된다.
a 태그 1. HTML에서 제공하는 앵커(anchor)요소로, 기본적으로 웹 페이지에서 다른 웹 페이지로 이동하는데 사용된다. 
2. 클릭 시 페이지 전체를 새로 로드하며, 브라우저의 주소가 변경된다.
3. a 태그를 사용하여 페이지 이동을 할 수 있지만, 일반적으로 SPA를 구축하는 경우에는 Link 컴포넌트를 사용하는 것이 권장된다. a 태그를 사용할 경우 페이지가 전체적으로 새로 로드되므로, 리액트의 SPA의 장점인 빠른 렌더링과 페이지 전환의 부드러움을 활용할 수 없다
a 태그는 다른 사이트로 이동할 때 사용하는 앵커요소이며, 홈페이지 내에서 이동을 할 때는 주로 Link 컴포넌트를 사용하는 것이 적합하다

8. json-server, Rest API

 

json-server

  • 개발 단계에서 간단하게 RESTful API를 구축하는 데 사용되는 도구이다
  • 실제 서버를 구축하지 않고도 JSON 파일을 데이터베이스처럼 다루어 API 엔드포인트를 만들 수 있다
  • 이를 통해 프론트엔드 개발자들은 서버 없이도 클라이언트와 상호작용할 수 있게 된다
// 설치
npm install -g json-server

 

// db.json 파일 생성 후 아래 데이터 작성
{
  "posts": [
    { "id": 1, "title": "Post 1", "body": "This is post 1" },
    { "id": 2, "title": "Post 2", "body": "This is post 2" },
    { "id": 3, "title": "Post 3", "body": "This is post 3" }
  ],
  "users": [
    { "id": 1, "name": "John Doe" },
    { "id": 2, "name": "Jane Smith" }
  ]
}

 

// 터미널에서 json-server 실행
json-server --watch db.json --port 5000

위의 명령으로 서버가 실행되고, 'http://localhost:5000'에서 데이터를 조회하거나 추가, 수정, 삭제할 수 있다

 

  Rest API

  • Representation State Transfer
  • 웹 아키덱처의 한 형식으로, 자원을 URL로 표현하고 HTTP 메서드(GET: 데이터 조회, POST: 데이터 생성, PUT: 데이터 업데이트, DELETE: 데이터 삭제 등)를 사용하여 해당 자원을 조작하는 아키텍처 스타일
  • RESTful API는 REST 아키텍처를 따르는 API를 의미하여, HTTP 메서드를 사용하여 데이터를 조작한다

9. useEffect, fetch()로 API 호출

 

  useEffect

  • 리액트 함수형 컴포넌트에서 부수 효과(side effect)를 처리하는 데 사용된다
  • 부수 효과는 컴포넌트의 렌더링과는 직접적으로 관련이 없는 작업들을 말한다
  • 예를 들어, API호출, 구독과 같은 작업들이 부수 효과에 해당한다
  • 컴포넌트가 렌더링 될 때마다 호출되며, 기본적으로 매 렌더링마다 실행된다.
  • 두 번째 매개변수로 의존성 배열(dependency array)을 제공하여 특정 상태나 프로퍼티가 변경될 때만 useEffect를 실행하도록 설정할 수 있다
import React, { useEffect, useState } from 'react';

function MyComponent() {
  const [data, setData] = useState([]);

  useEffect(() => {
    // API 호출 등의 작업을 처리
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data))
      .catch(error => console.error(error));
  }, []);

  return (
    <div>
      {/* data를 사용하여 UI 렌더링 */}
      {data.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}

 

  fetch()

  • 브라우저에서 제공하는 비동기 네트워크 요청을 위한 메서드
  • HTTML 요청을 보내고 응답을 처리하는 데 사용된다
  • fetch()를 사용하여 API를 호출하면 JSON 데이터 등의 비동기 응답을 받아서 처리할 수 있다
  • fetch()는 Promise를 반환하므로,. then(),. catch()를 사용하여 비동기적으로 데이터를 처리할 수 있다
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => {
    // API로부터 받아온 데이터를 처리
    console.log(data);
  })
  .catch(error => {
    // 에러 처리
    console.error(error);
  });

[코드 설명]

fetch()를 사용하여 API로부터 데이터를 받아와서 response.json()을 사용하여 JSON 데이터로 변환하고, .then()을 사용하여 데이터를 처리하고, .catch()를 사용하여 에러를 처리하고 있다

 

useEffect와 fetch()를 함께 사용하여 리액트 함수형 컴포넌트에서 API 호출을 처리할 수 있다. useEffect를 사용하여 컴포넌트가 마운트 되었을 때 API호출을 처리하고, fetch()를 사용하여 비동기적으로 데이터를 받아와서 처리할 수 있다


10. Custom Hooks

  • 재사용 가능한 로직을 추출하여, 컴포넌트 간에 공유할 수 있도록 도와주는 기능
  • 상태 관리, 데이터 가져오기, 이벤트 처리 등과 같은 여러 작업들을 추상화하여 컴포넌트로부터 분리할 수 있다
  • 2023.07.25 - [React] - [React] Hooks 10개

11. PUT(수정), DELETE(삭제)

 

  PUT(수정)

  • JSON 서버의 데이터를 수정하는 PUT 요청을 보내는 예시
  • PUT 요청은 해당 리소스를 업데이트한다
// PUT(수정) 예시

// 데이터베이스를 모방하는 JSON 서버 URL
const apiURL = 'https://jsonplaceholder.typicode.com/posts';

// 수정할 데이터 정보
const dataToUpdate = {
  id: 1,
  title: 'Updated Title',
  body: 'Updated Body Content',
};

// PUT 요청
fetch(`${apiURL}/${dataToUpdate.id}`, {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(dataToUpdate),
})
  .then(response => response.json())
  .then(updatedData => console.log('Updated Data:', updatedData))
  .catch(error => console.error('Error:', error));

 

  DELETE(삭제)

  • JSON 서버의 데이터를 삭제하는 DELETE 요청을 보내는 예시
  • DELETE 요청은 해당 리소스를 삭제한다
// DELETE(삭제) 예시

// 데이터베이스를 모방하는 JSON 서버 URL
const apiURL = 'https://jsonplaceholder.typicode.com/posts';

// 삭제할 데이터의 ID
const dataToDeleteId = 1;

// DELETE 요청
fetch(`${apiURL}/${dataToDeleteId}`, {
  method: 'DELETE',
})
  .then(response => {
    if (response.ok) {
      console.log('Data deleted successfully.');
    } else {
      console.error('Failed to delete data.');
    }
  })
  .catch(error => console.error('Error:', error));

[코드 설명]

위의 코드에서 JSON 서버의 데이터를 수정하거나 삭제하기 위해 fetch() 함수를 사용했다

데이터를 수정할 때는 PUT 요청을 보내고, 데이터를 삭제할 때는 DELETE 요청을 보냈다

요청을 성공적으로 보내면 콘솔에 성공 메시지가 출력되고, 실패하면 에러 메시지가 출력된다

 

실제 서버에서는 해당 리소스가 존재하는지 확인하고, 권한 등의 검증 과정이 필요하며, 위의 예시는 간단한 시뮬레이션을 위한 것이므로 실제 서버 구축 시에는 더 많은 처리 과정과 보안 고려가 필요하다.


12. POST(생성), useHistory()

 

  POST(생성)

  • JSON 서버의 데이터를 생성하는 POST 요청을 보낸다
  • POST 요청은 새로운 리소스를 생성한다
// POST(생성) 예시

// 데이터베이스를 모방하는 JSON 서버 URL
const apiURL = 'https://jsonplaceholder.typicode.com/posts';

// 생성할 새 데이터 정보
const newData = {
  title: 'New Post Title',
  body: 'New Post Body Content',
};

// POST 요청
fetch(apiURL, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(newData),
})
  .then(response => response.json())
  .then(createdData => console.log('Created Data:', createdData))
  .catch(error => console.error('Error:', error));

 

  useHistory()

  • 리액트 라우터의 훅으로, 브라우저의 history 객체를 접근할 수 있게 해 준다
  • 이를 사용하여 리액트 컴포넌트에서 브라우저의 기록을 조작하고 페이지를 이동할 수 있다
import React from 'react';
import { useHistory } from 'react-router-dom';

const NewPostForm = () => {
  const history = useHistory();

  const handleSubmit = (event) => {
    event.preventDefault();

    // 새 데이터를 생성하는 POST 요청 코드 (이전 POST 예시 코드와 유사)

    // 요청이 성공적으로 완료되면 새로운 데이터의 상세 페이지로 이동
    fetch(apiURL, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(newData),
    })
      .then(response => response.json())
      .then(createdData => {
        console.log('Created Data:', createdData);
        // 새로 생성된 데이터의 상세 페이지로 이동
        history.push(`/posts/${createdData.id}`);
      })
      .catch(error => console.error('Error:', error));
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* 폼 요소들 */}
      <input type="text" name="title" />
      <textarea name="body" />

      <button type="submit">Submit</button>
    </form>
  );
};

export default NewPostForm;

[코드 설명]

위의 코드에서 useHistory()를 사용하여 history 객체를 가져왔다

폼을 제출하면 새로운 데이터를 생성하는 POST 요청을 보내고, 요청이 성공적으로 완료되면 history.push()를 사용하여 새로 생성된 데이터의 상세 페이지로 이동하게 된다.

history.push()는 리액트 라우터의 Link 컴포넌트와 유사하게 동작하여 페이지를 이동시킨다.

 

'React' 카테고리의 다른 글

React Router V6  (0) 2023.08.02
TypeScript props, state, forms, themes  (0) 2023.08.01
[React] Hooks 10개  (0) 2023.07.25
React + TypeScript 설치 방법  (0) 2023.07.20
[React] Styled Components  (0) 2023.07.20