본문 바로가기

react

[코드잇] 리액트 웹 개발 시작하기 (5)

1. 컴포넌트 재사용 

 

app.js

import { useState } from 'react';
import Board from './Board';
import Button from './Button';

function random(n) {
  return Math.ceil(Math.random()*n);
}

function App () {
  const [myHistory, setMyHistory] = useState([]);
  const [otherHistory, setOtherHistory] = useState([]);

  const handleRollClick = () => {
    const nextMyNum = random(6);
    const nextOtherNum = random(6);
    setMyHistory([...myHistory, nextMyNum]);
    setOtherHistory([...otherHistory, nextOtherNum]);
  };

  const handleClearClick = () => {
    setMyHistory([]);
    setOtherHistory([]);
  };

  return ( 
    <div>
      <Button onClick={handleRollClick}>던지기</Button>
      <Button onClick={handleClearClick}>처음부터</Button>
      <div>
        <Board name="나" color="blue" gameHistory={myHistory} />
        <Board name="상대" color="red" gameHistory={otherHistory}/>
      </div>
    </div>
  );
}

export default App;

 

board.js

import Dice from './Dice';

function Board({ name, color, gameHistory}) {
  const num = gameHistory[gameHistory.length -1] || 1;
  const sum = gameHistory.reduce((a,b) => a+b,0);
  return (
    <div>
      <h1>{name}</h1>
      <Dice color={color} num={num} />
      <h2>총점</h2>
      <p>{sum}</p>
      <h2>기록</h2>
      <p>{gameHistory.join(',')}</p>
    </div>
  );
}

export default Board;

 

2. 리액트가 렌더링하는 방식

 

- 리액트는 내부적으로 Virtual DOM을 사용

- Virtual DOM은 웹페이지와 실제 DOM 사이에서 중간 매개체 역할을 하는 것

- 기존의 방식으로 화면을 업데이트 하려면 DOM을 직접 수정해야 하는데, 이것은 성능과 비용에 굉장히 많은 영향을 미침

 -> 수정할 부분을 DOM의 데이터에서 모두 찾아야 하기 때문

- 리액트는 DOM을 직접 수정하는 것이 아니라 업데이트해야 할 최소한의 부분만을 찾아서 업데이트 

 

 

3. 인라인 스타일

 

Button.js

const baseButtonStyle = {
  padding: '14px 27px',
  outline: 'none',
  cursor: 'pointer',
  borderRadius: '9999px',
  fontSize: '17px',
};

const blueButtonStyle = {
  ...baseButtonStyle,
  border:'solid 1px #7090ff',
  color: '#7090ff',
  backgroundColor: 'rgba(0, 89, 255 ,0.2)',
};

const redButtonStyle = {
  ...baseButtonStyle,
  border: 'solid 1px #ff4664',
  color: '#ff4664',
  backgroundColor: 'rgba(255,78,78,0.2)',
};

function Button({ color,children,onClick }) {
  const style = color === 'red'? redButtonStyle : blueButtonStyle;
  return (
  <button style ={style} onClick={onClick}>
    {children}
  </button>
  );
}

export default Button;

 

 

4. CSS 클래스 네임

 

App.css

.App .App-button {
  margin:6px;
}

App.js

import { useState } from 'react';
import Board from './Board';
import Button from './Button';
import './App.css';

function random(n) {
  return Math.ceil(Math.random()*n);
}

function App () {
  const [myHistory, setMyHistory] = useState([]);
  const [otherHistory, setOtherHistory] = useState([]);

  const handleRollClick = () => {
    const nextMyNum = random(6);
    const nextOtherNum = random(6);
    setMyHistory([...myHistory, nextMyNum]);
    setOtherHistory([...otherHistory, nextOtherNum]);
  };

  const handleClearClick = () => {
    setMyHistory([]);
    setOtherHistory([]);
  };

  return ( 
    <div className="App">
      <Button className="App-button" color="blue" onClick={handleRollClick}>던지기</Button>
      <Button className="App-button" color="red" onClick={handleClearClick}>처음부터</Button>
      <div>
        <Board name="나" color="blue" gameHistory={myHistory} />
        <Board name="상대" color="red" gameHistory={otherHistory}/>
      </div>
    </div>
  );
}

export default App;

 

Button.css

.Button {
  padding: 14px 27px;
  border-radius: 9999px;
  outline: none;
  font-size: 17px;
  cursor: pointer;
}

.Button.blue {
  border: solid 1px #7090ff;
  color: #7090ff;
  background-color: rgba(0, 89, 255, 0.2);
}

.Button.red {
  border: solid 1px #ff4664;
  color: #ff4664;
  background-color: rgba(255, 78, 78, 0.2);
}

 

Button.js

import './Button.css';

function Button({ className = '', color = 'blue', children, onClick }) {
  const classNames = `Button ${color} ${className}`;
  return (
    <button className={classNames} onClick={onClick}>
      {children}
    </button>
  );
}

export default Button;

 

 

5. 디자인을 적용하는 방법 

 

1) 이미지 불러오기

 

- 이미지 파일은 import 구문을 통해 불러오고, 불러온 이미지 주소를 src 속성으로 사용

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

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

export default App;

 

2) 인라인 스타일

 

- 리액트에서 인라인 스타일은 문자열이 아닌 객체형으로 사용 

- 프로퍼티 이름은 CSS 속성 이름으로, 프로퍼티 값은 CSS 속성 값으로 씀

- 대시 기호 없이 카멜 케이스로 써야 함

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;

 

3) CSS 파일 불러오기 

 

- import 구문으로 from 키워드 없이 파일을 불러올 수 있음

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

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

export default App;

 

4) 클래스네임 사용하기 

 

- CSS 파일에 정의된 클래스명을 className prop에 문자열로 넣어주면 됨

- 재사용성을 위해 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;

 

6. 편리하게 클래스네임을 쓰는 방법

 

- classnames 라이브러리 사용 

 - classnames는 npm을 통해 설치 가능 

 - npm install classnames을 입력하고 설치한 다음에 import로 불러와서 쓰면 됨

import classNames from 'classnames';

function Button({ isPending, color, size, invert, children }) {
  return (
    <button
      className={classNames(
        'Button',
        isPending && 'pending',
        color,
        size,
        invert && 'invert',
      )}>
     { children }
   </button >
  );
}