키값을 부여하지 않는다면 아래와 같은 코드가 콘솔에 출력된다.

의미를 조금 해석해 보자면 PokemonList의 key값이 정해지지 않아서 오류가 발생할 수 있다고 한다. 하지만 화면 렌더링은 정상적으로 이루어지고 있다. 그러면 무슨 문제가 있기에 리액트는 이것을 경고하는 것일까?

 

리액트 공식 문서에 따르면

"배열을 렌더링할 때 key값이 고유하지 않으면 렌더링이 잘못될 수 있다"로 결론을 내릴 수 있다.

 

key값을 만들때 주의점

  • 리스트를 만들 때 포함되어야 하는 특수한 문자열 속성이다. 
  • key값은 어떤 고유한 항목을 변경, 삭제, 추가 혹은 식별할지에 도움을 주는 프롭입니다.
  • key값은 배열 내부의 엘리먼트로 고유성을 부여해야 합니다.
  • key값은 배열의 index 값으로 사용하면 안 됩니다.

일단 이 정도로 정리할 수 있다. 즉 key값은 어떤 element의 변동이 일어났는지를 식별할 수 있게 도와주는 프롭인 것이다.

 

보통 배열을 화면에 렌더링 할 때 추가 삭제 등의 행동은 빈번하게 일어날 수 있다.

이때 key값이 없다면 다음 문제가 발생할 것이다.

  1. 요소의 값이 변경되었는지 삭제되었는지 불분명함
  2. React가 state가 변경되지 않았다고 판정하고 렌더링을 안 해줌

첫 번째부터 살펴보자면, 예를 들어 우리가 [index0 사과, index1 바나나, index2 자몽]이라는 배열을 렌더링 한다고 생각해 보자. 고유한 key값이 없어 index값을 key값으로 설정했다.

 

이제 바나나를 삭제하려고 한다.

그럼 결괏값은 [0 사과, 1 자몽]이 된다.

 

이번엔 자몽을 제거하고 바나나를 자몽으로 교체하려고 한다.

그럼 결괏값은 [0 사과, 1 자몽]이 된다.

 

그럼 두 가지 경우의 결괏값은 같아지는데 key값(index)을 비교해도 결과만 봐서는 어떻게 바뀌었는지 알 수 없다.

 

만약 [1: 사과, 2: 바나나, 3: 자몽]의 id값을 key로 지정한다고 가정하고 바나나를 삭제해 보면 [1: 사과, 3: 자몽]으로 결과만으로 어떤 과정이 있었는지 한눈에 보이는 것을 알 수 있다.

 

두 번째 경우는 VirtualDOM(가상돔 VDOM이라고도 한다) 이란걸 알아야 한다.

리액트나 다른 라이브러리에서 VirtualDOM을 보통은 사용한다고 들었다.

필자는 리액트에 대한 공부 밖에 아직 하지 않아서 잘 모른다

 

일단 내가 공부한 선에서 얘기를 해보자면, 브라우저는 렌더링을 할 때 DOM을 사용하게 되는데,

리액트의 VirtualDOM은 리액트의 컴포넌트가 변화할 때 이를 감지하고 저장해서 변화가 일어난 VirtualDOM과 RealDOM을 비교해서 새로운 DOM을 새로 생성하고 VirtualDOM을 업데이트한다.

근데 이때 사용되는 리스트의 비교값이 key값이다.

 

그럼 생각해 보자. 만약 index를 키값으로 설정하고 [0: 사과, 1: 바나나, 2: 자몽]이런 배열에서 바나나멜론으로 교체했다. [0:사과, 1:멜론, 2: 자몽] 여기서 key값이 바뀌었는가? index값은 그대 로고 안의 내용만 바뀌었기 때문에 리액트는 이를 변경되지 않았다고 판정하고 리렌더링을 해주지 않는다

 

 

마무리

key값은 고유한 값을 지정해 주어야 한다.

key값이 없으면 리렌더링 할 때 오류가 발생할 수 있다.

 

Reference

React에서 키의 역할 - 네이버 블로그 (리액트 내부의 컴포넌트 생성에 대해서도 추가로 다루고 있다.)

https://junior-datalist.tistory.com/184 - 리액트에서 키값이 필요한 이유

'React' 카테고리의 다른 글

[React] JSON 배열 렌더링하기  (2) 2023.04.18
[React] Props 정리  (0) 2023.04.17
[React] CSS 사용하기  (0) 2023.04.12
[React] JSX 문법  (0) 2023.04.08
[React] 개발자 도구 설치  (0) 2023.04.08

아래 JSON 데이터 배열을 랜더링 해보려고 한다.

[
  {
    "id": 1,
    "name": "이상해씨",
    "types": [
      "풀",
      "독"
    ]
  },
  {
    "id": 2,
    "name": "이상해풀",
    "types": [
      "풀",
      "독"
    ]
  },
...
//기본 index.js 코드
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.js";
import items from "./pokemons.json";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <>
    <App items={items} />
  </>
);

map메서드 이용하기

배열 메소드 map에서 콜백함수의 리턴 값을 리액트 엘리먼트로 리턴한다.

//Pokemon.js
function Pokemon({ item }) { // PokemonList에서 추출한 item 값을 이용한다.
  return (
    <div>
      No.{item.id} {item.name}
    </div>
  );
}

function PokemonList({ items }) { // App.js에서 items prop을 받아온다.
  return (
    <ul>
      {items.map((item) => { //map 메소드를 이용해 새로운 배열을 만든다
        return (
          <li key={item.id}>
            <Pokemon item={item} />
          </li>
        );
      })}
    </ul>
  );
}

export default PokemonList;
//App.js
import PokemonList from "./Pokemon";
import items from "./pokemons.json"; //json파일 불러오기

function App() {
  return <PokemonList items={items} />; 
}

export default App;

Sort로 정렬하기

아이디가 양수일 경우에는 오름차순

아이디가 음수일 경우에는 내림차순

Pokemon.js는 위와 동일

//App.js
import { useState } from "react";
import PokemonList from "./Pokemon";
import items from "./pokemons.json";

function App() {
  const [direction, setDirection] = useState(1);

  //onClick속성에 들어갈 값들
  const handleAscClick = () => setDirection(1); //추가된 함수 => 양수일경우 오름차순
  const handleDesClick = () => setDirection(-1); //추가된 함수 => 음수일경우 내림차순

  //id값을 기준으로 정렬
  const sortedItems = items.sort((a, b) => direction * (a.id - b.id)); // 추가된 sort

  return (
    <div>
      <div>
        <button onClick={handleAscClick}>번호 순서대로</button> //button으로 확인
        <button onClick={handleDesClick}>번호 반대로</button> //button으로 확인
      </div>
      <PokemonList items={sortedItems} /> //items에서 sortedItems로 변경
    </div>
  );
}

export default App;

filter함수로 삭제하기

filter함수를 이용해 클릭한 item id값과 현재 id값중 다른값을 리턴하여 새로운 배열을 만든다.

//Pokemon.js
function Pokemon({ item, onDelete }) { // 삭제 프롭 추가 PokemonList로 부터 받아옴
  const handleDeleteClick = () => onDelete(item.id); // 삭제 함수 추가

  return (
    <div>
      No.{item.id} {item.name}
      <button onClick={handleDeleteClick}>삭제</button> //삭제 버튼 추가
    </div>
  );
}

function PokemonList({ items, onDelete }) { //삭제 프롭 추가 App.js로 부터 받아옴
  return (
    <ul>
      {items.map((item) => {
        return (
          <li key={item.id}>
            <Pokemon item={item} onDelete={onDelete} /> //삭제 프롭 추가
          </li>
        );
      })}
    </ul>
  );
}

export default PokemonList;

 

//App.js
import { useState } from "react";
import PokemonList from "./Pokemon";

//Items => mockItems로 변경
import mockItems from "./pokemons.json";

function App() {
  const [direction, setDirection] = useState(1);
  
  //items 선언 => 초깃값은 똑같아서 items 그대로 쓰면됨
  const [items, setItems] = useState(mockItems);

  //onClick속성에 들어갈 값들
  const handleAscClick = () => setDirection(1); //양수일경우 오름차순
  const handleDesClick = () => setDirection(-1); //음수일경우 내림차순

  //id값을 기준으로 정렬
  const sortedItems = items.sort((a, b) => direction * (a.id - b.id));

  //추가된 함수 => 클릭한 item의 아이디값과 다른값을 모두 출력
  const handleDelete = (id) => setItems(items.filter((item) => item.id !== id));
 

  return (
    <div>
      <div>
        <button onClick={handleAscClick}>번호 순서대로</button>
        <button onClick={handleDesClick}>번호 반대로</button>
      </div>
      <PokemonList items={sortedItems} onDelete={handleDelete} /> // onDelete 프롭 추가
    </div>
  );
}

export default App;

렌더링 값:

마무리

CSS는 사용하지 않아서 출력값을 보기가 좀 어렵긴 하지만 읽고 정렬하고 삭제하는 기능은 정상적으로 작동하는 것을 확인할 수 있었다. 위에서 사용된 메소드들이 생소하다면 링크를 타고 배열의 메소드를 확인해보는것을 추천한다.

 

Reference

https://www.codeit.kr/learn/5035 - codeit 배열렌더링하기

'React' 카테고리의 다른 글

[React] 배열의 key값이 중요한 이유  (0) 2023.04.18
[React] Props 정리  (0) 2023.04.17
[React] CSS 사용하기  (0) 2023.04.12
[React] JSX 문법  (0) 2023.04.08
[React] 개발자 도구 설치  (0) 2023.04.08

컴포넌트에 파라미터를 전달할 때 HTML에서의 속성값을 쓰는 것처럼 전달한다. 이때 이 컴포넌트에 지정한 속성을 리액트에서 Props(프롭)라고 부른다. Props는 properties(속성)의 약자다.

컴포넌트에 속성을 지정하면 각 속성이 하나의 객체로 모여서 컴포넌트를 정의한 함수의 첫 번째 파라미터로 전달된다.

쉽게 생각하면 컴포넌트의 prop들은 하나의 객체 안에 들어가는대 이때 prop의 이름은 객체의 key가 되고 속성의 값은 key에 할당된 값이 된다.

//App.js
import React from 'react';
import Hello from './Hello';

function App() {
  return (
    <Hello name="react" /> //name이 prop이다.
  );
}

export default App;
//Hello.js
import React from 'react';

function Hello(props) { //console에 props를 출력하면 객체가 출력된다.
  return <div>안녕하세요 {props.name}</div> //객체 값에 접근하는것과같이 key를 이용해 접근한다.
}

export default Hello;

위 코드를 보자면 name이 props의 키값이 되고 react가 props name의 키값이 된다.

let props = { // props객체 표현
 name: "react"
}

구조분해 문법과 같이 사용하면 좀더 간결하게 코드를 작성할 수 있다.

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

function Hello({name}) { //name값을 구조분해
  return <div>안녕하세요 {name}</div> //name 변수로 바로 사용가능
}

export default Hello;

한 가지 특별한 prop이 존재하는데 바로 children 프롭이다. 보통 컴포넌트는 <컴포넌트 /> 이런식으로 작성한다 그런데 버튼 같은 태그를 사용할 때는 여는 태그(<button>)와 닫는 태그(</button>)의 형태로 작성할 때가 있다. 이 때 그 이 태그들 사이에 작성된 코드가 바로 이 children 값에 담기게 된다.

// App.js

import React from 'react';
import ChildComponent from './ChildComponent';

function App() {
  return (
    <div>
      <ChildComponent>Hello, world!</ChildComponent>
    </div>
  );
}

export default App;
// ChildComponent.js

import React from 'react';

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

export default ChildComponent;

Reference

https://www.codeit.kr/learn/4652 - codeit props 정리하기

https://react.vlpt.us/basic/05-props.html - props 정리한 블로그

'React' 카테고리의 다른 글

[React] 배열의 key값이 중요한 이유  (0) 2023.04.18
[React] JSON 배열 렌더링하기  (2) 2023.04.18
[React] CSS 사용하기  (0) 2023.04.12
[React] JSX 문법  (0) 2023.04.08
[React] 개발자 도구 설치  (0) 2023.04.08

인라인 스타일

리액트에서 인라인 스타일은 객체형으로 쓰입니다. 프로퍼티 이름은 CSS 속성 이름으로, 프로퍼티의 CSS값은 CSS 속성 값으로 쓰입니다.

한가지 주의할 점은 -(대시)기호가 들어간 속성이름은 CamelCase로 작성해야합니다.

ex) boarder-radius -> boarderRadius

import diceImg from './assets/dice.png';

const style = {
  borderRadius: '50%',
  width: '120px',
  height: '120px',
};

function Dice() {
  return <img style={style} src={diceImg} alt="주사위 이미지" />;
}

export default App;

 

import해서 가져오기

import구문과 똑같이 쓰는대 from을 안쓰면 됩니다. 즉 전체파일을 불러오는 거죠.

import 'css파일경로'

 

클래스 네임 활용하기

CSS 파일에서 정의된 클레스 이름을 className prop에 넣어주면 됩니다. 이때 재사용성을 위해 부모로부터 받아오는 것이 좋습니다.

import diceImg from './assets/dice.png';
import './Dice.css';

function Dice({ className = '' }) {
  const classNames = `Dice ${className}`;
  return <img className={classNames} src={diceImg} alt="주사위 이미지" />;
}

export default App;

 

'React' 카테고리의 다른 글

[React] JSON 배열 렌더링하기  (2) 2023.04.18
[React] Props 정리  (0) 2023.04.17
[React] JSX 문법  (0) 2023.04.08
[React] 개발자 도구 설치  (0) 2023.04.08
[React]React 프로젝트 만들고 실행/종료/빌 해보기  (0) 2023.04.08

자바스크립트 확장 문법이다

 

사용 시 주의점

  • 속성명은 카멜케이스(Camel Case)로 적어야 한다. ex) onClick, onBlur, onFocus, onMouseDown, onMouseOver, tabIndex 단, 예외적으로 비표준 속성(data-* 속성)은 카멜케이스가 아니라 HTML 문법 그대로 작성해야 한다.
import ReactDOM from 'react-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <button onClick= ... >클릭!</button>
);
import ReactDOM from 'react-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <div>
    상태 변경: 
    <button className="btn" data-status="대기중">대기중</button>
    <button className="btn" data-status="진행중">진행중</button>
    <button className="btn" data-status="완료">완료</button>
  </div>
);

 

  • 자바스크립트 예약어와 같은 속성명으로 사용이 불가능하다 ex) for, class x -> htmlFor, className in JSX

React 공식문서 - 어트리뷰트차이

 

DOM 엘리먼트 – React

A JavaScript library for building user interfaces

ko.reactjs.org

import ReactDOM from 'react-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <form>
    <label htmlFor="name">이름</label>
    <input id="name" className="name-input" type="text" />
  </form>
);

 

  • 반드시 하나의 요소로 묶기 - Fragement or 빈 태그(<></>) (빈 태그를 더 많이 사용함)
import ReactDOM from 'react-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <> // 혹은 <Fragement> </>
    <p>안녕</p>
    <p>리액트!</p>
  </>
);
  • 자바스크립트 표현식을 넣을 땐 중괄호{} 사용
import ReactDOM from 'react-dom';

const product = 'MacBook';
const model = 'Air';
const imageUrl = 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/1e/MacBook_with_Retina_Display.png/500px-MacBook_with_Retina_Display.png'

function handleClick(e) {
  alert('곧 도착합니다!');
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <>
    <h1>{product + ' ' + model} 주문하기</h1>
    <img src={imageUrl} alt="제품 사진" />
    <button onClick={handleClick}>확인</button>
  </>
);

'React' 카테고리의 다른 글

[React] Props 정리  (0) 2023.04.17
[React] CSS 사용하기  (0) 2023.04.12
[React] 개발자 도구 설치  (0) 2023.04.08
[React]React 프로젝트 만들고 실행/종료/빌 해보기  (0) 2023.04.08
[React] 개발 환경 구축하기  (0) 2023.04.08

+ Recent posts