본문 바로가기
Develop/React

"생활코딩 | React 2022년 개정판" 을 공부하며.. 2탄

by bellsilver7 2022. 3. 27.
728x90

컴포넌트 만들기

만약 1억줄의 복잡한 Html 태그가 존재한다고 가정하면 그것을 정리하고 싶어질 것이다. 이 정리의 핵심은 연관된 것들을 그룹핑 해서 이름을 붙이는 것이고 앞으로 그 이름만 기억하면 됩니다.

 

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div>
      <header>
        <h1><a href="/">Web</a></h1>
      </header>
      <nav>
        <ol>
          <li><a href="/read/1">Html</a></li>
          <li><a href="/read/2">Css</a></li>
          <li><a href="/read/3">Javascript</a></li>
        </ol>
      </nav>
      <article>
        <h2>Welcome</h2>
        Hello React!
      </article>
    </div>
  );
}

export default App;

html 코드를 복잡한 코드로 가정해 보았을 때 연관 있는 것끼리 그룹핑하여 사용자 정의 태그를 만들어 봅니다.

 

.. 생략 ..
function Header() {
  return <header>
    <h1><a href="/">Web</a></h1>
  </header>
}

function App() {
  return (
    <div>
      <Header></Header>
      <nav>
        <ol>
          <li><a href="/read/1">Html</a></li>
          <li><a href="/read/2">Css</a></li>
          <li><a href="/read/3">Javascript</a></li>
        </ol>
      </nav>
      <article>
        <h2>Welcome</h2>
        Hello React!
      </article>
    </div>
  );
}
.. 생략 ..

먼저 header 태그를 사용자 정의 태그로 정의하기 위해선 함수를 만들고 이때 이 함수는 대문자로 시작해야합니다. 아래와 같이 사용자 정의 태그를 만들어 봤고 React에서는 이것을 Component(컴포넌트) 라고 부릅니다. 

 

import logo from './logo.svg';
import './App.css';

function Header(props) {
  return <header>
    <h1><a href="/">Web</a></h1>
  </header>
}

function Nav()
{
  return <nav>
    <ol>
      <li><a href="/read/1">Html</a></li>
      <li><a href="/read/2">Css</a></li>
      <li><a href="/read/3">Javascript</a></li>
    </ol>
  </nav>
}

function Article()
{
  return <article>
    <h2>Welcome</h2>
    Hello React!
  </article>
}

function App() {
  return (
    <div>
      <Header></Header>
      <Nav></Nav>
      <Article></Article>
    </div>
  );
}

export default App;

각각의 태그를 그룹핑하여 Header, Nav, Article 컴포넌트를 만들어 봤습니다. 이처럼 컴포넌트 단위로 나눌 수 있는 React는 복잡한 코드를 간결하게 하여 독립된 부품으로 만들 수 있고 그것을 이용해 더 적은 복잡도로 소프트웨어를 만들 수 있게 됩니다. 동시에 컴포넌트를 서로 공유하여 사용할 수 있게 됨으로 생산성을 획기적으로 끌어올리는데 중요한 역할을 합니다.

 

PROP

React 에서는 속성을 prop 이라고 부른다.

 

function Header(props) { => 2. 파라미터 설정
  return <header>
    <h1><a href="/">{props.title}</a></h1> => 3. 호출
  </header>
}

function App() {
  return (
    <div>
      <Header title="REACT"></Header> => 1. 속성 부여
      <Nav></Nav>
      <Article></Article>
    </div>
  );
}

<Header> 태그에 title 이라는 속성과 "REACT" 라는 값을 부여했다. 그리고 컴포넌트의 첫 번째 파라미터는 props 라 명칭해 주었다. 대부분의 사람들이 props 로 사용하기에 굳이 다른 이름을 생각해낼 필요 없다. 속성의 값을 출력하기 위해서는 {}를 감싸주어야 한다.

 

컴포넌트 안에서 props 를 콘솔로 찍어보면 객체가 들어있는 것을 확인 할 수 있다. 여기서 title 의 값을 출력하기 위해선 {props.title} 와같이 입력하면 된다. 앞서 말했듯이 속성 값을 출력하기 위해서는 {}(중괄호)로 감싸주어야 하며 이는 일반 문자열로 인식되는 것이 아닌 표현식으로 취급되기 때문에 중괄호 안의 내용이 해석되서 반영된다.

 

function Article(props)
{
  return <article>
    <h2>{props.title}</h2>
    {props.body}
  </article>
}

function App() {
  return (
    <div>
      <Header title="REACT"></Header>
      <Nav></Nav>
      <Article title="Welcome" body="Hello React!"></Article>
    </div>
  );
}

 이렇게 Article 컴포넌트도 반영했다.

 

function Nav(props)
{
  // 2. 
  const lis = [
    <li><a href="/read/1">Html</a></li>,
    <li><a href="/read/2">Css</a></li>,
    <li><a href="/read/3">Javascript</a></li>
  ];
  return <nav>
    <ol>
      {lis}
    </ol>
  </nav>
}

function App() {
  // 1. 변수 선언
  // 변하지 않는 변수는 const로 선언하여 코드를 튼튼하게 할 수 있다.
  const topics = [
    {id: 1, title: "html", body: "html is ..."},
    {id: 2, title: "css", body: "css is ..."},
    {id: 3, title: "javascript", body: "javascript is ..."}
  ];
  return (
    <div>
      <Header title="REACT"></Header>
      <Nav topics={topics}></Nav>
      <Article title="Welcome" body="Hello React!"></Article>
    </div>
  );
}

  Nav 컴포넌트는 li 태그가 반복되고 있는데 이것도 props 로 정리할 수 있도록 만들어 보겠다. 먼저 topics 라는 변수를 const 로 선언했다. 변하지 않는 변수는 const 로 선언하여 코드를 튼튼하게 할 수 있다. 그리고 topics 변수를 컴포넌트에 넘기기 위해서는 props 값을 출력할 때와 마찬가지로 topics={topics} 이렇게 중괄호로 감싸 속성에 값을 부여하면 topics 가 그대로 전달 된다.

  다음으로 Nav 컴포넌트에 lis 라는 배열을 만들고 담으려고 하는 각각의 태그를 ,(콤마) 로 구분해 넣어주었다. 이제 배열에 있는 내용을 출력하기 위해서 {lis} 로 넣는다. 이제 props 에 topics 로 전달된 값을 받아서 동적으로 태그를 만들어 배열에 담아줄건데 이 것을 강의에서는 for 문을 사용했지만 map 을 사용해 해봤다. 

 

function Nav(props)
{
  const lis = [];
  props.topics.map((t) => {
    lis.push(<li><a href={'/read/' + t.id}>{t.title}</a></li>);
  });
  return <nav>
    <ol>
      {lis}
    </ol>
  </nav>
}

이렇게 하면 아래와 같이 출력이 되지만 개발자 도구를 열어보면 Warning: Each child in a list should have a unique "key" prop.
발생한 것을 볼 수 있다. 이것은 우리가 동적으로 만들어주는 태그(list)는 "key"라는 prop을 갖고 있어야 하고 그 값은 list 안에서 고유해야 한다.

 

function Nav(props)
{
  const lis = [];
  props.topics.map((t) => {
    lis.push(<li key={t.id}><a href={'/read/' + t.id}>{t.title}</a></li>);
  });
  return <nav>
    <ol>
      {lis}
    </ol>
  </nav>
}

  React는 자동으로 생성한 태그들에 대해서 추적하기 위한 근거로써 key 라고 하는 약속된 속성을 부여함으로 React가 성능을 높이고 정확한 동작을 하는데 우리가 협조한다고 생각하면 된다.

 

이벤트

컴포넌트에 이벤트 기능이 있어서 컴포넌트에 어떤 일이 발생했을 때 사용자가 추가적인 어떤 작업을 처리할 수 있는 방법에 대해 살펴본다.

 

function App() {
  // 변하지 않는 변수는 const로 선언하여 코드를 튼튼하게 할 수 있다.
  const topics = [
    {id: 1, title: 'html', body: 'html is ...'},
    {id: 2, title: 'css', body: 'css is ...'},
    {id: 3, title: 'javascript', body: 'javascript is ...'}
  ];
  return (
    <div>
      <Header title="REACT" onChangeMode={function(event) {
        alert('Header');
      }}></Header>
      <Nav topics={topics}></Nav>
      <Article title="Welcome" body="Hello React!"></Article>
    </div>
  );
}

Header 태그에 prop의 값으로 함수를 전달하기 위해 onChangeMode 라는 prop과 값으로 function(event){} 을 주고 이 함수를 호출해서 클릭했을때 alert('Header') 경고창이 뜨도록 했다.

 

function Header(props) {
  return <header>
    <h1><a href="/" onClick={function(event) {
      event.preventDefault(); // a 태그의 기본 동작을 방지
      props.onChangeMode();
    }}>{props.title}</a></h1>
  </header>
}

컴포넌트에는 onClick 이라고 이벤트를 걸어주고 값으로 함수를 작성했다.(문법 주의)

그리고 함수를 호출하기 위해 props.onChanageMode(); 라고 작성하고 이제 아래와 같이 header 태그를 클릭하면 경고창을 볼 수 있다.

 

컴포넌트에 이벤트를 부여하는 방법에 대해 배웠고 이번에는 nav 안의 태그를 클릭할 때 태그마다 다른 값을 출력해보도록 하겠다.

 

function App() {
  // 변하지 않는 변수는 const로 선언하여 코드를 튼튼하게 할 수 있다.
  const topics = [
    {id: 1, title: 'html', body: 'html is ...'},
    {id: 2, title: 'css', body: 'css is ...'},
    {id: 3, title: 'javascript', body: 'javascript is ...'}
  ];
  return (
    <div>
      <Header title="REACT" onChangeMode={(event) => {
        alert('Header');
      }}></Header>
      <Nav topics={topics} onChangeMode={(id) => {
        alert(id);
      }}></Nav>
      <Article title="Welcome" body="Hello React!"></Article>
    </div>
  );
}

Header 태그와 마찬가지로 onChangeMode prop에 함수로 값을 주고 파라미터로 id 라는 변수를 받도록 설정한 다음 경고창에 아이디 값이 출력되도록 만들었다.

 

function Nav(props)
{
  const lis = [];
  props.topics.map((t) => {
    // 동적으로 만들어주는 태그는 key라는 prop을 가져야 한다.
    // event => {} 파라미터가 하나일 경우 괄호를 생략할 수 있다.
    lis.push(<li key={t.id}>
      <a id={t.id} href={'/read/' + t.id} onClick={event => {
        event.preventDefault();
        props.onChangeMode(event.target.id); // id 값을 넘겨주기 위해 target 인 a 태그의 id 값을 가져오도록 했다.
      }}>{t.title}</a>
      </li>);
  });
  return <nav>
    <ol>
      {lis}
    </ol>
  </nav>
}

컴포넌트에서도 마찬가지로 onClick에 함수를 부여하고 onChangeMode 함수에 id 값을 전달하기 위해 a 태그에 id 속성을 만들어 가져와 보았다. 여기까지 하고 네비게이션 태그를 클릭해보면 태그마다 경고창에 다른 값이 출력되는 것을 볼 수 있다.

 

 

STATE

 

컴포넌트는 prop 이라는 입력을 통해서 return 이라는 ui 값을 만들게 되고, 이 컴포넌트 함수를 다시 실행해서 새로운 값을 만들기 위한 또 하나의 데이터가 바로 state 다. prop 과 state 모두 값이 변경되면 새로운 값을 return 하지만 prop은 컴포넌트를 사용하는 외부자를 위한 데이터라고 하면 컴포넌트를 만드는 내부자를 위한 데이터라고 할 수 있는 차이점이 있다.

 

function App() {
  // 변하지 않는 변수는 const로 선언하여 코드를 튼튼하게 할 수 있다.
  const mode = 'WELCOME';
  const topics = [
    {id: 1, title: 'html', body: 'html is ...'},
    {id: 2, title: 'css', body: 'css is ...'},
    {id: 3, title: 'javascript', body: 'javascript is ...'}
  ];

  let content = null;
  if (mode === 'WELCOME') {
    content = <Article title="Welcome" body="Hello WEB!"></Article>;
  } else if (mode === 'READ') {
    content = <Article title="Welcome" body="Hello READ!"></Article>;
  }
  return (
    <div>
      <Header title="REACT" onChangeMode={(event) => {
      }}></Header>
      <Nav topics={topics} onChangeMode={(id) => {
      }}></Nav>
      {content}
    </div>
  );
}

먼저 mode 라는 상수를 선언하고 mode의 값에 따라 content 에 Article 을 다르게 하여 출력할 수 있도록 했다. 이제 이벤트에 따라 mode의 값이 변경될 수 있도록 해보겠다.

 

// react 에서 제공하는 함수인 useState 라는 훅을 import 
import {useState} from 'react';

// 상태를 만든다
const _mode = useState('WELCOME');

console.log(_mode);

state를 사용하기 위해서는 useState 를 import 해야 하고 위에서 앞에서 선언했던 mode 를 _mode 로 변경해 useState 로 상태값을 부여했다. 그리고 console 에 찍어보면 0번째에는 상태의 값을 읽을때 쓰는 데이터가 들어있고 1번째에는 그 상태의 값을 변경할 때 사용하는 함수가 들어있다.

 

// 1. 
const _mode = useState('WELCOME');
const mode = _mode[0];
const setMode = _mode[1];

// 2.
const [mode, setMode] = useState('WELCOME');

useState 의 배열 값을 각각 변수에 담기 위해 1번 처럼 할 수 있지만 2번과 같이 축약해서 사용할 수도 있다.

 

function App() {
  // 변하지 않는 변수는 const로 선언하여 코드를 튼튼하게 할 수 있다.
  const [mode, setMode] = useState('WELCOME');
  const topics = [
    {id: 1, title: 'html', body: 'html is ...'},
    {id: 2, title: 'css', body: 'css is ...'},
    {id: 3, title: 'javascript', body: 'javascript is ...'}
  ];

  let content = null;
  if (mode === 'WELCOME') {
    content = <Article title="Welcome" body="Hello WEB!"></Article>;
  } else if (mode === 'READ') {
    content = <Article title="Welcome" body="Hello READ!"></Article>;
  }
  return (
    <div>
      <Header title="REACT" onChangeMode={(event) => {
        setMode('WELCOM');
      }}></Header>
      <Nav topics={topics} onChangeMode={(id) => {
        setMode('READ');
      }}></Nav>
      {content}
    </div>
  );
}

이제 이벤트 안에 setMode 로 상태값을 변경할 수 있게 해 주어 header 를 클릭하면 "WELCOM", nav 를 클릭하면 "READ" 가 출력되게 되었다.

 

function App() {
  const [mode, setMode] = useState('WELCOME');
  const [id, setId] = useState(null); // 1. 새로운 state 추가
  const topics = [
    {id: 1, title: 'html', body: 'html is ...'},
    {id: 2, title: 'css', body: 'css is ...'},
    {id: 3, title: 'javascript', body: 'javascript is ...'}
  ];

  let content = null;
  if (mode === 'WELCOME') {
    content = <Article title="Welcome" body="Hello WEB!"></Article>;
  } else if (mode === 'READ') {
    // 3. 이벤트로 넘어온 id 상태값과 topics 의 id 값이 일치하는 경우의 title 과 body 값으로 속성값 설정
    let title, body;
    topics.map(x => {
      if (x.id === id) {
        title = x.title;
        body = x.body;
      }
    });
    content = <Article title={title} body={body}></Article>;
  }

  return (
    <div>
      <Header title="REACT" onChangeMode={(event) => {
        setMode('WELCOM');
      }}></Header>
      <Nav topics={topics} onChangeMode={(_id) => {
        setMode('READ');
        setId(_id); // 2. 이벤트가 일어날 때 값을 부여
      }}></Nav>
      {content}
    </div>
  );
}

이제 nav 태그를 클릭 했을 때 해당하는 body 값을 출력할 수 있도록 하기 위해 1. 새로운 state 를 추가하고 Nav 태그의 2. 이벤트가 일어날 때 값을 부여해 주기 위해 이벤트 함수 안에서 setId(_id) 로 값을 부여한다. 그리고 3. 이벤트로 넘어온 id 상태값과 topics 의 id 값이 일치하는 title 과 body의 값으로 속성값을 설정하면 된다. 하지만 이렇게 하고 실행해보면 아무것도 나오지 않게 되며 그 이유는 topics.map 안에서 console.log(x.id, id); 를 출력해 보면

 

 

하나는 숫자, 하나는 문자. 이렇게 둘의 데이터 타입이 다른 것을 볼 수 있다.

 

props.onChangeMode(Number(event.target.id));

둘의 데이터 타입을 일치시켜주기 위해 이벤트 함수로 넘겨주는 even.target.id 값을 Number 함수를 사용해 숫자 타입으로 변환해 주고 나면 원하는 바와 같이 Nav 태그의 이벤트에 따라 내용이 다르게 출력되는 것을 실행해 볼 수 있다.

 

이전 1탄 내용에서는 React 에 도입할 준비를 했다면 이번에는 도입해서 필수적인 주제들에 대해 배우게 된것 같다. 요약하면 React는 컴포넌트를 만들고 거기에 prop(속성)을 넣어 ui 를 return 하며 그 컴포넌트에 이벤트를 부여해 새로운 state(상태) 값으로 새로운 ui 를 return 해주는 것이다. 재사용에 용이한 도구라고 생각되고 onclick 과 onClick 모두 허용되는 것이 아닌 onClick 만 허용되는 것을 보고 정해진 규칙이 잘 되있는 부분에 React 에 대한 만족도가 올라갔다.

 

여기까지 글을 마치고 다음으로는 Create, Upate, Delete 에를 학습하고 마무리 해보겠다.

728x90

댓글