본문 바로가기

[내일배움캠프] 프로덕트 디자인 8기/TIL(Today I Learned)

[TIL] 25.01.02(목)

React 입문 2주차

 

2-8

State - 1

 

useState (+ on Click)

import { useState } from "react";

const App = () => {
  const [name, setName] = useState("르순이");
  const handleNameChange = () => {
    setName("최르순이");
  };

  return (
    <div>
      <h1>React State</h1>
      <button
      onClick={handleNameChange} >최르순이로!!!</button>
      {name}
    </div>
  ); 
 };


 export default App;

 

 

input 태그는 항상 state(value)와 onChange 부분을 쌍으로 엮어야 한다 (value를 엮었을 때: 제어 컴포넌트 / 안 엮었을 때: 비제어 컴포넌트) > 엮지 않아도 동작은 하긴 하지만, 리액트에서 text라는 state가 아래 코드처럼 위에서도 쓰이고 인풋창 내부에서도 보여짐

> 이 두가지를 동기화 시키기 위해서(늘 똑같다는 것을 보여주기 위해서)는 제어 컴포넌트를 사용하는것이 맞음

import { useState } from "react";

const App = () => {
  const [text, setText] = useState("");
  const handleInputChange = (event) => {
    // console.log(event.target.valye);
      setText(event.target.value);
    };

  return (
    <div>
      <input
      type="text"
      onChange = {handleInputChange} value={text} />
      <br />
      {text}
    </div>
   );
  };

  export default App;

 

 

 

2-9

State - 2

 

불변성이란? (+메모리에 저장되는 원리 / 불변성은 리액트의 state 개념을 이해하기 위해 꼭 필요한 개념)

불변성이란 메모리에 있는 값을 변경할 수 없는 것을 뜻함. 자바스크립트의 데이터 형태중에 원시데이터는 불변성이 있고, 원시 데이터가 아닌 객체, 배열, 함수는 불변성이 없음

 

변경이 가능한 방법 (Mutable way)

let numbers = [1, 2, 3];
numbers.push(4); // 배열에 직접 요소를 추가
console.log(numbers); // [1, 2, 3, 4]

위와 같은 방식이 문제가 되는 이유:

numbers.push(4)를 사용하는 방식이 문제가 되는 이유는 이 작업이 numbers의 불변성을 깨뜨리기 때문.

이 경우에는 원본 배열 numbers에 직접적으로 새로운 요소 '4'를 추가하고 있음.

 

불변성을 깨뜨리는 것이 왜 문제가 될까?

1. 예측 불가능한 코드

데이터 구조를 직접 변경하면, 프로그램의 다른 부분에서 해당 데이터 구조를 참조하고 있을 때 그 부분들도 예상치 못한 방식으로 변경될 수 있음. 따라서 알 수 없는 버그를 발생시킬 수 있고, 프로그램의 동작을 예측하기 어렵게 만듦

2. 버그 추적의 어려움

원본 데이터가 여러 곳에서 변경될 수 있다면, 어떤 부분의 코드가 데이터를 변경했는지 추적하기 어려워짐. 이는 특히 큰 코드베이스나 여러 개발자들이 협업하는 환경에서 문제가 될 수 있음

 

불변성을 유지하는 방법 (Immutable way)

let numbers = [1, 2, 3];
let newNumbers = [...numbers, 4]; // 새 배열을 생성하여 기존 배열을 변경하지 않음
console.log(numbers); // [1, 2, 3]
console.log(newNumbers); // [1, 2, 3, 4]

 

변수를 저장하면 메모리에 어떻게 저장이 될까?

만약 우리가 let number = 1 이라고 선언을 하면, 메모리에는 1 이라는 값이 저장됨. 그리고 number 라는 변수는 메모리에 있는 1을 참조함. 그리고 이어서 let secondNumber = 1 이라고 다른 변수를 선언을 했다고 가정해보면, 이때도 자바스크립트는 이미 메모리에 생성되어 있는 1이라는 값을 참조함. 즉, number와 secondNumber는 변수의 이름은 다르지만, 같은 메모리의 값을 바라보고 있는 것.

그래서 콘솔에 number === secondNumber 를 하면 true가 출력됨.

하지만 원시데이터가 아닌 값(객체, 배열, 함수)는 이렇지 않음. let obj_1 = {name: ‘kim’} 이라는 값을 선언하면 메모리에 obj_1이 저장이 됨. 그리고 이어서 let obj_2 = {name: ‘kim’} 이라고 같은 값을 선언하면 obj_2라는 메모리 공간에 새롭게 저장이 됨.

그래서 obj_1 === obj2 는 false 가 되죠.

 

데이터를 수정하면 어떻게 될까?

다시 원시데이터로 돌아와서 만약에 기존에 1이던 number를 number = 2 라고 새로운 값을 할당하면 메모리에서는 어떻게 될까? 원시 데이터는 불변성이 있음. 즉, 기존 메모리에 저장이 되어 있는 1이라는 값이 변하지 않고, 새로운 메모리 저장공간에 2가 생기고 number라는 값을 새로운 메모리 공간에 저장된 2를 참조하게 됨. 그래서 secondNumber를 콘솔에 찍으면 여전히 1이라고 콘솔에 보임. number와 secondNumber는 각각 다른 메모리 저장공간을 참조하고 있기 때문.

obj_1를 수정해보면, obj_1.name = ‘park’ 이라고 새로운 값을 할당하면 어떻게 될까? 객체는 불변성이 없음. 그래서 기존 메모리 저장공간에 있는 {name: ‘kim’} 이라는 값이 {name : ‘park’} 으로 바뀌어 버리는 것.

원시데이터는 수정을 했을 때 메모리에 저장된 값 자체는 바꿀 수 없고, 새로운 메모리 저장공간에 새로운 값을 저장함.

원시데이터가 아닌 데이터는 수정했을 때 기존에 저장되어 있던 메모리 저장공간의 값 자체를 바꿔버림.

 

리액트에서 불변성이 가지는 의의

"리액트에서는 화면을 리렌더링 할지 말지 결정할 때 state의 변화를 확인함."

state가 변했으면 리렌더링 하는 것이고, state가 변하지 않았으면 리렌더링을 하지 않음.

그때, state가 변했는지 변하지 않았는지 확인하는 방법이 state의 변화 전, 후의 메모리 주소를 비교하는 것.

그래서 만약 리액트에서 원시데이터가 아닌 데이터를 수정할 때 불변성을 지켜주지 않고, 직접 수정을 가하면 값은 바뀌지만 메모리주소는 변함이 없게 되는것. 그래서 즉, 개발자가 값은 바꿨지만 리액트는 state가 변했다고 인지하지 못하게 되어 버림.

그래서 결국 마땅히 일어나야 할 리렌더링이 일어나지 않게됨.

 

리액트 불변성 지키기 예시

import React, { useState } from "react";

function App() {
  const [dogs, setDogs] = useState(["말티즈"]);

  function onClickHandler() {
		// spread operator(전개 연산자)를 이용해서 dogs를 복사합니다. 
	  // 그리고 나서 항목을 추가합니다.
    setDogs([...dogs, "시고르자브르종"]);
  }

  console.log(dogs);
  return (
    <div>
      <button onClick={onClickHandler}>버튼</button>
    </div>
  );
}

export default App;
import React, { useState } from 'react';

function App() {
  const [items, setItems] = useState([1, 2, 3]);

  const addItem = () => {
    // 새 배열을 생성하고 기존 항목을 복사한 후 새 항목 추가
    setItems([...items, items.length + 1]); // 불변성 유지
  };

  return (
    <div>
      <button onClick={addItem}>Add Item</button>
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

 

 

 

2-10

컴포넌트 및 렌더링

 

컴포넌트

컴포넌트는 리액트의 핵심 빌딩 블록 중 하나. 즉 리액트에서 개발할 모든 애플리케이션은 컴포넌트라는 조각으로 구성됨. 컴포넌트는 UI 구축 작업을 훨씬 쉽게 만들어줌. UI 요소를 표현하는 최소한의 단위이며, 화면의 특정 부분이 어떻게 생길지 정하는 '선언체'.

컴포넌트를 생성하고 보여지고자 하는 UI 요소를 컴포넌트 내부에서 JSX를 통해 선언하면 이를 리액트가 화면에 그려줌.

리액트의 컴포넌트 기반 개발 이전에는 브라우저에서 동적으로 변하는 UI를 표현하기 위해 직접 DOM 객체를 조작하는 명령형 프로그래밍 방식으로 구현했음. 

 

그렇다면 기존 명령형 프로그래밍과 리액트 컴포넌트의 선언적 프로그래밍은 어떻게 다를까?

 

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

1. DOM(명령형 프로그래밍)

명령형으로 작성된 코드의 경우 Hello, World!를 출력하기 위해 컴퓨터가 수행하는 절차를 일일히 코드로 작성해주어야 함.

// Hello, World! 화면에 출력하기
// 순수 javaScript 명령형 코드
const root = document.getElementById('root'); 
const header = document.createElement('h1'); 
const headerContent = document.createTextNode(
	'Hello, World!'
);

header.appendChild(headerContent); 
root.appendChild(header);

 

2. 리액트(선언형 프로그래밍)

React 코드의 경우 내가 UI을 선언하고 render 함수를 호출하면 React가 알아서 절차를 수행해 화면에 출력해줌. 즉, 화면에 어떻게 그려야할지는 React 내부에 잘 숨겨져 추상화되어 있음.

// React 코드 (선언적인)
const header = <h1>Hello World</h1>; // jsx
ReactDOM.render(header, document.getElementById('root'));

DOM을 직접 조작하여 명령형 프로그래밍 방식으로 작성하던 코드가 나쁘다는 것은 아님!

카운터 예시와 같이 격리된 예제에서는 차라리 리액트와 같은 UI 라이브러리를 사용하지 않고 만드는게 더 빠르고 전체적인 번들 사이즈 측면에서도 더 효율적인 방법일수 있음.

하지만, 더 복잡한 UI 시스템에서는 관리하기가 기하급수적으로 어려워지게 됨.

 

 

렌더링

리액트에서 렌더링이란, 컴포넌트가 현재 props와 state의 상태에 기초하여 UI를 어떻게 구성할지 컴포넌트에게 요청하는 작업을 의미함.

렌더링을 쉽게 설명하기 위해서 HTTP 통신을 배울 때 많이 보는 주방장과 웨이터 예시를 사용해 설명해보면,

이제부터 컴포넌트를 주방에서 요리를 준비하는 주방장, 그리고 리액트는 손님으로부터 주문받아 주방에 전달하고 완성된 요리를 손님에게 서빙하는 웨이터로 가정. 브라우저를 요리를 주문하는 손님으로 가정, 그리고 손님이 주문하고 주방장이 만드는것이 요리가 아닌 UI라고 가정.

  • UI - 음식
  • 컴포넌트 - 음식을 만드는 주방장
  • 리액트 - 웨이터
  • 브라우저 - 손님

렌더링이 일어나는 프로세스를 아래와 같이 설명할 수 있음.

  1. 렌더링 일으키는 것은 (triggering)- UI를 주문하고 주방으로 전달하는 것
  2. 렌더링한다는 것은 (rendering)- 주방에서 컴포넌트가 UI를 만들고 준비하는 것
  3. 렌더링 결과는 실제 DOM에 커밋한다는 것은 (commit) - 리액트가 준비된 UI를 손님 테이블에 올려놓는 것

렌더링 프로세스 예시

 

렌더링 트리거

1. 첫 리액트 앱을 실행했을 때

2. 현재 리액트 내부에 어떤 상태(state)에 변경이 발생했을 때

- 컴포넌트 내부 state가 변경되었을 때,

- 컴포넌트에 새로운 props가 들어올 때,

- 상위 부모 컴포넌트에서 위의 이유로 렌더링이 발생했을 때. (부모~자식 순으로 위에서 아래로 리렌더링이 실행됨)

 

리액트 앱이 실행되고 첫 렌더링이 일어나면 리액트는 컴포넌트의 루트에서 시작하여 아래쪽으로 쭉 훑으며 컴포넌트가 반환하는 JSX 결과물을 DOM 요소에 반영함.

 

 

리렌더링

첫 렌더링은 자동으로 일어난 것이었음. 리액트 앱이 실행되면 리액트는 전체 컴포넌트를 렌더링하고 결과물을 DOM에 반영해 브라우저상에 보여줌. 첫 렌더링을 끝난 이후에 추가로 렌더링을 트리거 하려면 상태를 변경해주면 됨. (setState 함수 외에도 몇 가지가 더 있음)

 

컴포넌트 상태에 변화가 생기면 리렌더링이 발생함. 이 때, 여러 상태가 변경됐다면 리액트는 이를 큐 자료 구조에 넣어 순서를 관리함.

1. 주방 예시를 다시 들어보면 리렌더링은 음식점 손님(브라우저)이 첫 주문 이후에 갈증이 생겨 추가로 음료를 주문하거나 처음 받은 음식이 마음에 들지 않아 새로운 메뉴를 주문하는 것과 같음.

2. 새로운 UI 주문(리렌더링)이 일어나면 리액트가 변경된 내용을 주방에 있는 요리사인 컴포넌트에 전달하고 컴포넌트는 새로운 변경된 주문을 토대로 새로운 요리(UI)를 만듦

3. 새롭게 만들어진 요리(렌더링 결과)는 리액트에 의해 다시 손님 테이블에 올려짐(DOM에 반영 - commit phase)

리렌더링 프로세스 예시

 

 

브라우저 렌더링

브라우저의 렌더링과 리액트의 렌더링은 엄연히 다른 독립적인 프로세스.

렌더링이 완료되고 React가 DOM을 업데이트한 후 브라우저는 화면을 그림. 이 프로세스를 "브라우저 렌더링"이라고 하지만 혼동을 피하기 위해 "페인팅"이라고도 함. (다음 시간에 DOM과 VirtualDOM을 배우면서 좀 더 자세히 살펴볼 예정)

 

 

2-11

DOM과 Virtual DOM

 

브라우저가 렌더링 되는 원리

리액트(react.js)나, 뷰(Vue.js)는 가상돔(Virtual DOM)을 사용해서 원하는 화면을 브라우저에 그려줌. 자체적으로 상당히 효율적인 알고리즘을 사용해서 그려주기 때문에 그 속도가 어마어마함.

가상돔의 동작 원리를 아는것은 리액트로 프론트엔드를 개발하는 사람이라면 기본소양 정도로 가볍게라고 한번 듣고 넘어가는 것은 중요함.

 

DOM

DOM이란: 가상돔(Virtual DOM)을 이해하기 위해서는 먼저 DOM(Document Object Model)을 이해할 필요가 있음.

브라우저를 돌아다니다 보면 아래와 같은 수 많은 컴포넌트로 구성된 웹페이지를 볼 수 있음. 이러한 페이지를 문서(Document)라고 함. 페이지를 이루는 컴포넌트는 엘리먼트(Element)라고 함.

DOM은 이 엘리먼트를 tree형태(= DOM TREE)로 표현한 것 (아래 사진 참고)

브라우저 웹페이지 예시
DOM TREE 예시

 

트리의 요소 하나하나를 '노드'라고 부름. 각각의 '노드'는 해당 노드에 접근과 제어(DOM 조작)를 할 수 있는 API를 제공함.

* API: 단순히 HTML 요소에 접근해서 수정할 수 있는 함수 정도로 이해하면 됨(p태그를 지우거나 다시 더하거나 내용을 변경하거나..)

 

DOM API 사용 예시

// id가 demo인 녀석을 찾아, 'Hello World!'를 대입해줘.
document.getElementById("demo").innerHTML = "Hello World!";

// p 태그들을 모두 가져와서 element 변수에 저장해줘
const element = document.getElementsByTagName("p");

// 클래스 이름이 intro인 모든 요소를 가져와서 x 변수에 저장해줘
const x = document.getElementsByClassName("intro");

 

가상 DOM(Virtual DOM)

가상 DOM이란?: 리액트는 가상 DOM을 이용해서 실제 DOM을 변경하는 작업을 상당히 효율적으로 수행함.

가상 DOM

가상 DOM은 실제DOM과 구조가 완벽히 동일한 복사본 형태라고 이해하면 됨. 실제 DOM은 아니지만, 객체(object) 형태로 메모리에 저장되기 때문에 실제 DOM을 조작하는 것 보다 훨씬 더 빠르게 조작을 수행할 수 있음. (실제 DOM을 조작하는 것 보다, 메모리상에 올라와있는 javascript 객체를 변경하는 작업이 훨씬 더 가벼움)

 

가상 DOM이 실제 DOM을 변경하는 것이 아니라면, 도대체 어떻게 화면이 바뀌게 되는 것일까?

 

DOM 조작 과정

만약 인스타그램의 좋아요 버튼을 누른다면 

화면이 바뀌어야 함. 저 빨간색 하트에 해당되는 엘리먼트 엘리먼트 DOM 요소가 갱신되어야 한다는 것!

(=DOM을 조작해야 한다는 의미)

 

STEP 1

이 과정에서 리액트는 항상 아래와 같은 2가지 버전의 가상 DOM을 가지고 있음

1. 화면이 갱신되기 전 구조가 담겨있는 가상 DOM 객체

2. 화면 갱신 후 보여야 할 가상 DOM 객체

리액트는 state가 변경되어야만 리렌더링이 됨. 그 때, 바로 2번에 해당되는 가상DOM을 만드는 것.

 

STEP 2 : diffing

State가 변경되면 2번에서 생성된 가상돔과 1번에서 이미 갖고있었던 가상돔을 비교해서 어느 부분(엘리먼트)에서 변화가 일어났는지를 상당히 빠르게 파악해냄.

 

STEP 3 : 재조정(reconciliation)

파악이 다 끝나면, 변경이 일어난 그 부분만 실제 DOM에 적용시켜줌. 적용시킬 때는, 한건 한건 적용시키는 것이 아니라, 변경사항을 모두 모아 한 번만 적용을 시킴(Batch Update)

*Batch Update: 앞서 useState 시간에 리액트가 state를 batch 방법으로 update한다는 것을 배웠음. 변경된 모든 엘리먼트를 한꺼번에 반영할 수 있는 방법이기도 함.

 

<클릭 한 번으로 화면에 있는 5개의 엘리먼트가 바뀌어야 한다면>

  • 실제 DOM : 5번의 화면 갱신 필요
  • 가상 DOM : Batch Update로 인해 단 한번만 갱신 필요

 

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

위 내용에서 다음 3가지 주요 React 렌더링 순서에 대해 배웠음

  1. 가상 DOM 생성: React는 컴포넌트의 렌더 함수를 실행하여 가상 DOM을 새로 생성함.
  2. 차이 비교 (Diffing): 새로운 가상 DOM과 이전 가상 DOM을 비교하여 실제 DOM을 업데이트해야 할 부분을 찾음.
  3. 배치 업데이트 (Reconciliation): 변경된 부분만을 효율적으로 실제 DOM에 반영함.

React가 실제 DOM을 업데이트하면, 이 변경 사항은 브라우저의 렌더링 엔진에 의해 화면에 반영됨.

브라우저 렌더링 과정은 다음과 같음:

  1. 스타일 계산: HTML과 CSS를 분석하여 각 요소의 스타일을 계산함.
  2. 레이아웃: 각 요소의 위치와 크기를 계산함.
  3. 페인팅: 레이아웃 단계에서 계산된 정보를 바탕으로 요소를 화면에 그림.

브라우저의 렌더링과 리액트의 렌더링은 엄연히 다른 독립적인 프로세스!!

 

 

 

 

2-12

카운터 앱 실습

 

App.jsx에 코드를 작성

+ 1 버튼을 누를 때마다 숫자가 + 1 증가

- 1 버튼을 누를 때마다 숫자가 - 1 감소

import React, { useState } from 'react';

const App = () => {
  const [number, setNumber] = useState(0);

  const onMinusButtonClick = () => {
    setNumber(number -1);
  }

  const onPlusButtonClick = () => {
    setNumber(number +1);
  }

  return (
  <div style={{
    display: "flex",
    flexDirection: "column",
    justifyContent: "center",
    alignItems: "center",
  }}
  > 
    <div>{number}</div>
    <div>
    <button onClick={onMinusButtonClick}>-1</button>
    <button onClick={onPlusButtonClick}>+1</button>
    </div>
  </div>
  );
};


export default App;

 

 

 

2-13, 2-14

스타일링 및 refactoring1, 2

 

컴포넌트 스타일링

import React from 'react';

const App = () => {
  const style = {
    display: "flex",
    gap: "12px",
    padding: "50px",
  };

  const users = [ 
    { id: 1, age: 30, name: "송중기" },
    { id: 2, age: 22, name: "송강" },
    { id: 3, age: 24, name: "김유정" },
    { id: 4, age: 28, name: "구교환" },
  ];

  return (
    <div style={style}>
      {users.map(function (user) {
        return <User key={user.id} user={user} />;
      })}
    </div>
  );
};

export default App;

const User = ({ user }) => {
  const squareStyle = {
    width: "100px",
    height: "100px",
    border: "1px solid green",
    borderRadius: "10px",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
  };

  return (
    <div style={squareStyle}>
      {user.age}살 - {user.name}
    </div>
  );
};

 

 

 불변성을 유지시키며 새로운 user 추가하기

import React, { useState } from 'react';

const App = () => {
  const style = {
    display: "flex",
    gap: "12px",
    padding: "50px",
  };
  
  const [users, setUsers] = useState([ 
    { id: new Date().getTime(), age: 30, name: "송중기" },
    { id: new Date().getTime() +1, age: 22, name: "송강" },
    { id: new Date().getTime() +2, age: 24, name: "김유정" },
    { id: new Date().getTime() +3, age: 28, name: "구교환" },
  ]);

  const [age, setAge] = useState(0);;
  const [name, setName] = useState("");

  return (
    <>
    <input type="number" value={age} onChange={(e)=>{
      setAge(e.target.value)
    }}
    />
    <input
    type="text"
    value={name}
    onChange={(e) => {
      setName(e.target.value);
    }}
    />
    <button onClick={()=>{
      // user state에 한 객체가 추가되면 됨!
      const newUser = {
        id: new Date().getTime(),
        age: age,
        name: name
      }
      // console.log("newUser", newUser);
      setUsers([...users, newUser]);
    }}>추가</button>
    <div style={style}>
      {users.map(function (user) {
        return <User key={user.id} user={user} />;
      })}
    </div>
    </>
  );
};

export default App;

const User = ({ user }) => {
  const squareStyle = {
    width: "100px",
    height: "100px",
    border: "1px solid green",
    borderRadius: "10px",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
  };

  return (
    <div style={squareStyle}>
      {user.age}살 - {user.name}
    </div>
  );
};

 

 

 App.jsx

import { useState } from 'react';
import Button from './components/Button';
import User from './components/User';

const App = () => {
  const style = {
    display: "flex",
    gap: "12px",
    padding: "50px",
  };
  
  const [users, setUsers] = useState([ 
    { id: new Date().getTime(), age: 30, name: "송중기" },
    { id: new Date().getTime() +1, age: 22, name: "송강" },
    { id: new Date().getTime() +2, age: 24, name: "김유정" },
    { id: new Date().getTime() +3, age: 28, name: "구교환" },
  ]);

  const [age, setAge] = useState(0);;
  const [name, setName] = useState("");
  const addUserHandler = ()=> {
    const newUser = {
      id: new Date().getTime(),
      age: Number(age),
      name: name
    };
    setUsers([...users, newUser]);
   }

   const deleteUserHandler = (id) => {
     //삭제할 대상 id
     const deletedUsers = users.filter(function(user) {
       return user.id != id
     });

   setUsers(deletedUsers);
   };

  return (
    <>
    <input type="number" value={age} onChange={(e)=>{
      setAge(e.target.value)
    }}
    />
    <input
    type="text"
    value={name}
    onChange={(e) => {
      setName(e.target.value);
    }}
    />
    <Button
    color="green"
    onClick={addUserHandler}>추가</Button>
    <div style={style}>
      {users.filter(function(u) {
        return u.age <25;
      }).map(function (user) {
        return <User
        key={user.id}
        user={user}
        deleteUserHandler={deleteUserHandler}
        />;
      })}
    </div>
    </>
  );
};

export default App;

 

 

User.jsx

import Button from "./Button";

const User = ({ user, deleteUserHandler }) => {
    const squareStyle = {
      width: "100px",
      height: "100px",
      border: "1px solid green",
      borderRadius: "10px",
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      flexDirection: "column",
    };
  
    const {age, name, id} = user;
  
    return (
      <div style={squareStyle}>
        <div>
        {user.age}살 - {user.name}
      </div>
      <div>
        <Button color="red"
        onClick={()=>deleteUserHandler(id)}>삭제</Button>
      </div>
      </div>
    );
  };

export default User;

 

 

Button.jsx

import React from 'react';

const Button = ({ children, onClick, color }) => {

    if (color){
      return (
        <button
        style={{
          backgroundColor: color,
          color: "white"
        }}
        onClick={onClick}>{children}</button>
      )
    }
  
    return <button onClick={onClick}>{children}</button>;
  };
  

export default Button;

'[내일배움캠프] 프로덕트 디자인 8기 > TIL(Today I Learned)' 카테고리의 다른 글

[TIL] 25.01.07(화)  (0) 2025.01.07
[TIL] 25.01.06(월)  (0) 2025.01.06
[TIL] 24.12.31(화)  (1) 2024.12.31
[TIL] 24.12.30(월)  (0) 2024.12.30
[TIL] 24.12.26(목)  (1) 2024.12.26