REACT 웹 개발 시작하기

LEE GYUHO·2023년 10월 18일
0

📌 REACT 개발 기초

  • 📘명령어

    • 프로젝트 생성 명령어
      npm init react-app <폴더 이름>
      혹은 폴더를 VS Code로 열고 터미널에서 npm init react-app .

    • 프로젝트 실행 명령어(개발 모드 실행)
      수정을 하고 새로고침을 하지 않아도 바로 반영된다.
      npm run start

    • 개발 모드 종료: Ctrl + c
      페이지 동작이 멈춘다.

  • 📘인덱스 파일에서 하는 일

import ReactDOM from 'react-dom/client';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<h1>안녕 리액트!</h1>);
//<div id="root">
//<h1>안녕 리액트!</h1>
//</div>
//이렇게 만든다는 것이다.
  • 📘JSX
    자바스크립트 파일 안에 HTML태그를 사용하는 문법
    자바스크립트와 HTML을 섞어서 쓸 수 있는 자바스크립트의 확장된 문법

    • class
      class="hello" 이런 식으로 class를 사용하고 싶으면 className="hello" 이렇게 className으로 바꿔서 사용해야 한다.

    • for
      for 속성도 사용이 불가능하다. 자바스크립트에서 반복문에 사용되기 때문이다.
      htmlFor="name" 이런식으로 바꿔서 사용해야 한다.

    • 이벤트 핸들러

      <input id="name" type="text" onblur="" onfocus="" onmousedown=""/>
      //이렇게 작성했던 이벤트 핸들러는
        
      <input id="name" type="text" onBlur="" onFocus="" onMouseDown=""/>
      //이렇게 카멜케이스로 작성해줘야 한다.
  • 📘프래그먼트
    Fragments는 DOM에 별도의 노드를 추가하지 않고 여러 자식을 그룹화할 수 있다.
    JSX문법으로 HTML태그를 작성할 때에는 하나로 감싸진 태그를 작성해야 한다.

import ReactDOM from 'react-dom';

ReactDOM.render(
  <p>안녕</p>
  <p>리액트!</p>,
  document.getElementById('root')
);
//이렇게 두개의 태그를 연달아서 작성하면 안된다(에러가 나온다)

import ReactDOM from 'react-dom';

ReactDOM.render(
  <Fragment>
    <p>안녕</p>
    <p>리액트!</p>
  </Fragment>,
  document.getElementById('root')
);
//이렇게 하나의 태그로 감싸줘야 한다.


import { Fragment } from 'react';//Fragment 태그를 만들면 자동으로 생성된다.
import ReactDOM from 'react-dom';

ReactDOM.render(
  <Fragment>
    <p>안녕</p>
    <p>리액트!</p>
  </Fragment>,
  document.getElementById('root')
);
//div태그를 사용하고 싶지 않다면 리액트에서 제공하는 프래그먼트를 사용하면 된다.


import ReactDOM from 'react-dom';

ReactDOM.render(
  <>
    <p>안녕</p>
    <p>리액트!</p>
  </>,
  document.getElementById('root')
);
//Fragment는 <>이렇게 이름없는 태그로 축약형 문법을 주로 사용하게 된다.
  • 📘JSX에서 자바스크립트 사용하기
    JSX는 실제로 사용될 때 자바스크립트 코드로 변환되어 실행된다.

    {}중괄호 안에는 자바스크립트의 표현식만 사용이 가능하기 때문에 if문, for문, 함수 선언과 같은 자바스크립트의 문장은 사용이 불가능하다.

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() {
  alert('곧 도착합니다!');
}

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

//<h1>{product.toUpperCase()} 주문하기</h1> 이런식으로도 가능하다.
  • 📘컴포넌트
import ReactDOM from 'react-dom';

function Hello(){
  return <h1>안녕 리액트!</h1>;
}

const element = (
  <>
    <Hello/>
    <Hello/>
    <Hello/>
  </>
);

ReactDOM.render(element, document.getElementById('root'));
//Hello라는 함수를 만들면 element변수에서 JSX코드를 작성할 때
//Hello라는 함수 이름을 가진 태그를 사용할 수 있다.
//Hello 태그는 Hello 함수가 리턴하는 리액트 엘리먼트를 의미한다.

//ex) const element = <h1>안녕 리액트!</h1>;
//console.log(element)를 하면 자바스크립트 객체가 출력된다.
//리액트에서는 이 객체를 리액트 엘리먼트라고 한다.

//여기서 Hello라는 함수는 리액트 컴포넌트라고 한다.
//함수 이름의 첫글자는 대문자로 해야 하고
//반드시 JSX문법으로 만든 리액트 엘리먼트를 리턴해 줘야 한다.



//이미지를 사용할 때 주의사항
import diceBlue01 from './assets/dice-blue-1.svg';

funtion Dice() {
  return <img src={diceBlue01} alt="주사위" />;
}
//이렇게 하면 오류가 뜬다.
import diceBlue01 from './assets/dice-blue-1.svg';

funtion Dice() {
  return <img src={./assets/dice-blue-1.svg} alt="주사위" />;
}
  • Props(Properties라고 생각하면 된다)
    컴포넌트의 속성은 Prop이라고 부른다
import HandIcon from './HandIcon';

function App() {
  return (
    <div>
      <HandIcon color="blue"/>
    </div>
  );
}

export default App;

//컴포넌트에 color Prop을 지정해 준 것이다.
//컴포넌트 태그에 지정해준 속성은 하나의 객체 형태로 컴포넌트 함수의 첫번째 파라미터로 전달된다.


import diceBlue01 from './assets/dice-blue-1.svg';

function Dice(props) {
  console.log(props);
  return <img src={diceBlue01} alt="주사위" />;
}

export default Dice;

//콘솔에 출력된 객체를 보면 color라는 프로퍼티가 있는 객체가 있다.


import diceBlue01 from './assets/dice-blue-1.svg';
import diceRed01 from './assets/dice-red-1.svg';

function Dice(props) {
  const diceImg = props.color === 'red' ? diceRed01 : diceBlue01;
  return <img src={diceImg} alt="주사위" />;
}

export default Dice;
//이렇게 props를 활용하면 컴포넌트에 전달하는 prop값에 따라 랜더되는 모습을 다양하게 변경할 수 있다.

//Dice.js
import diceBlue01 from './assets/dice-blue-1.svg';
import diceBlue02 from './assets/dice-blue-2.svg';
import diceBlue03 from './assets/dice-blue-3.svg';
import diceBlue04 from './assets/dice-blue-4.svg';
import diceBlue05 from './assets/dice-blue-5.svg';
import diceBlue06 from './assets/dice-blue-6.svg';
import diceRed01 from './assets/dice-red-1.svg';
import diceRed02 from './assets/dice-red-2.svg';
import diceRed03 from './assets/dice-red-3.svg';
import diceRed04 from './assets/dice-red-4.svg';
import diceRed05 from './assets/dice-red-5.svg';
import diceRed06 from './assets/dice-red-6.svg';

const DICE_IMAGES = {
  blue: [diceBlue01, diceBlue02, diceBlue03, diceBlue04, diceBlue05, diceBlue06],
  red: [diceRed01, diceRed02, diceRed03, diceRed04, diceRed05, diceRed06],
};

// function Dice(props) {
//   const src = DICE_IMAGES[props.color][props.num - 1];
//   const alt = `${props.color} ${props.num}`;
//   return <img src={src} alt={alt} />;
// } 구조분해를 이용하여 아래처럼 바꿀 수 있다.

function Dice({ color = 'blue', num = 1 }) {
  const src = DICE_IMAGES[color][num - 1];
  const alt = `${color} ${num}`;
  return <img src={src} alt={alt} />;
}

export default Dice;



//App.js
import Dice from './Dice';

function App() {
  return (
    <div>
      <Dice color="red" num={2} />
    </div>
  );
}

export default App;
//리액트 개발자 도구를 활용하면 props의 값을 바꿔줄 수 있다.
  • 📘children
//Button.js

function Button({ text }) {
  return <button>{text}</button>;
}

export default Button;


//App.js
import Button from './Button';
import Dice from './Dice';

function App() {
  return (
    <div>
      <div>
        <Button text="던지기" />
        <Button text="처음부터" />
      </div>
      <Dice color="red" num={2} />
    </div>
  );
}

export default App;

---------------------------------------------------------------------
//Button.js

function Button({ children }) {
  return <button>{children}</button>;
}

export default Button;


//App.js

import Button from './Button';
import Dice from './Dice';

function App() {
  return (
    <div>
      <div>
        <Button>던지기</Button>
        <Button>처음부터</Button>
      </div>
      <Dice color="red" num={2} />
    </div>
  );
}

export default App;

//children을 사용하면 이렇게 좀 더 직관적이게 바꿀 수 있다.
  • 📘state
    일단 state를 사용하려면 react 패키지에서 useState를 불러와야 한다.
    import { useState } from 'react';

    useState는 일반적으로 const [num, setNum] = useState(1);
    이런식으로 작성한다.
    (파라미터로 초기값을 전달받고 함수가 실행된 후에는 배열의 형태로 요소 2개를 리턴한다.)

    배열의 두 요소 중 첫번째 요소는 state값이다.(현재 변수의 값을 나타낸다.)
    두번째 요소는 세터함수이다. 이 함수를 호출할 때 파라미터로 전달하는 값으로 state값이 변경되는 것이다. state를 사용할 때는 변수에 새로운 값을 할당하면서 값을 변경하는 것이 아니라 세터함수를 통해서만 값을 변경해야 한다.
    그래서 일반적으로 첫번째 요소이름의 앞에 set을 붙여주는 형식으로 이름을 붙인다.

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

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

function App() {
  const [num, setNum] = useState(1);

  const handleRollClick = () => {
    const nextNum = random(6);
    setNum(nextNum);
  };

  const handleClearClick = () => {
    setNum(1);
  };

  return (
    <div>
      <div>
        <Button onClick={handleRollClick}>던지기</Button>
        <Button onClick={handleClearClick}>처음부터</Button>
      </div>
      <Dice color='red' num={num} />
    </div>
  );
}

export default App;
  • 📘참조형 state

    • 자바스크립트의 자료형에는 기본형과 참조형으로 나눌 수 있다.

    • 참조형은 특성상 변수로 다룰 때 주의해야할 부분이 있는데, state를 활용할 때도 같다.

    • ushift나 push 메소드로 배열 안에 요소를 변경했다고 하더라도, 리액트 입장에서는 state가 참조하는 주솟값이 여전히 똑같기 때문에 state가 바뀌었다고 생각하지 않는다.

    • 참조형 state를 사용할 때는 반드시 새로운 참조형 값을 만들어서 state를 변경하도록 한다.
      (가장 간단한 방법은 Spread 문법 '...' 사용)
import { useState } from 'react';
import Button from './Button';
import Dice from './Dice';

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

function App() {
  const [num, setNum] = useState(1);
  const [sum, setSum] = useState(0);
  const [gameHistory, setGameHistory] = useState([]);

  const handleRollClick = () => {
    const nextNum = random(6);
    setNum(nextNum);
    setSum(sum + nextNum);
    // const prevHistory = gameHistory;
    // gameHistory.push(nextNum);
    // console.log(prevHistory === gameHistory); //true
    // setGameHistory(gameHistory);
    setGameHistory([...gameHistory, nextNum]);
  };

  const handleClearClick = () => {
    setNum(1);
    setSum(0);
    setGameHistory([]);
  };

  return (
    <div>
      <div>
        <Button onClick={handleRollClick}>던지기</Button>
        <Button onClick={handleClearClick}>처음부터</Button>
      </div>
      <div>
        <h2></h2>
        <Dice color='blue' num={num} />
        <h2>총점</h2>
        <p>{sum}</p>
        <h2>기록</h2>
        {gameHistory.join(', ')}
      </div>
    </div>
  );
}

export default App;
  • 📘컴포넌트가 좋은 이유

    • 반복적인 개발이 줄어든다.

    • 오류를 고치기 쉽다.

    • 일을 쉽게 나눌 수 있다.(협업하기 좋다)

  • 📘리액트가 렌더링하는 방식
    state가 바뀔 때 리액트가 렌더링하는 방식

    virtual DOM을 사용한다.(효율적으로 화면을 처리할 수 있다.)
    virtual DOM에 새롭게 렌더링한 것을 실제 DOM tree에 바로 반영하지 않고
    state 변경 전과 후의 virtual DOM을 비교하여 바뀐 부분만 찾아내고 각각에 해당하는 실제 DOM 노드를 변경한다.

    이런 방식으로 렌더링을 하면 크게 2가지 좋은 점이 있다.

  1. 개발자가 직접 DOM 노드를 신경 쓸 필요가 없어서 단순하고 깔끔한 코드를 작성할 수 있다. (리액트 컴포넌트를 작성할 때는 무슨 데이터를 어떻게 보여줄지만 신경쓰면 된다.)
  2. 변경사항들을 효율적으로 처리할 수 있다.
    (변경사항들을 리액트가 적당히 모아서 처리할 수 있다.)
    (virtual DOM이 바뀔때마다 DOM에 바로 전달하지 않고 모아서 전달한다.)
  • 📘컴포넌트 디자인 입히는 방법 - 인라인 스타일
    객체형태로 스타일 속성값을 지정해줘야 한다.
    카멜케이스로 작성해줘야 한다.
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;
  • 📘컴포넌트 디자인 입히는 방법 - CSS 클래스네임
    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;

js파일에서 css 불러오는 방법: import '파일명';(import './index.css';)

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;

margin과 같이 요소의 외부적으로 영향을 끼칠만한 스타일 속성은 내부적으로 말고 외부에서 관리하는 것이 좋다.

  • 📘빌드하기

    • 개발된 프로젝트 빌드하기: npm run build

    • 빌드한 것 로컬에서 실행하기: npx serve build

profile
누구나 같은 팀으로 되길 바라는 개발자가 되자

0개의 댓글