[강의] React 개발 기초

김하은·2023년 11월 21일
0

코드잇 강의 정리

목록 보기
46/60

인덱스 파일에서 하는 일

  • index.html: 웹 브라우저에서 가장 먼저 실행되는 파일
  • index.js: index.html 파일이 열리고 나서 실행되는 파일
    • 리액트 코드들 중에서 가장 먼저 실행되는 파일
    • react-dom이라는 패키지에서 디폴트로 export하는 객체를 ReactDOM이라는 이름으로 import함
    • render: 화면을 그린다
  • 정리
    • 리액트 프로젝트가 실행되면 브라우저는 index.html 파일을 염
    • index.js 파일의 코드를 실행함
    • ReactDOM.render 메소드는 첫 번째 아규먼트 값을 활용해서 HTML 요소를 만들고 두 번째 아규먼트 값에 그 요소를 넣어 주는 역할을 함

JSX

  • 자바스크립트와 html을 섞어서 쓸 수 있는 자바스크립트의 확장 문법
    • html과 완전히 같지는 않음
    • class 속성은 className이라고 해줘야 함
    • for 속성은 반복문과 이름이 겹쳐서 htmlFor라고 해줘야 함
    • 이벤트 핸들러들은 camel case로 써줘야 함
      • onBlur="", onFocus="", onMoueDown=""
root.render(
  <h1 className = "hello">안녕 리액트!</h1>,
  document.getElementById('root')
);

프래그먼트

  • jsx로 html을 작성할 때는 반드시 하나로 감싸진 태그를 작성해야 함

  • div 태그를 사용하고 싶지 않다면 프래그먼트를 이용하면 됨
    • 빈 태그로 축약형으로도 사용할 수 있음



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

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

JSX 문법

  • JSX는 자바스크립트의 확장 문법으로 리액트로 코드를 작성할 때 HTML 문법과 비슷한 이 JSX 문법을 활용하면 훨씬 더 편리하게 화면에 나타낼 코드를 작성할 수가 있음
  • 하지만 주의할 점이 몇가지 있음!

HTML과 다른 속성명

  • 속성명은 카멜 케이스로 작성하기!
import ReactDOM from 'react-dom';

ReactDOM.render(
  <button onClick= ... >클릭!</button>,
  document.getElementById('root')
);
  • 예외적으로 HTML에서 비표준 속성을 다룰 때 활용하는 data-* 속성은 카멜 케이스(Camel Case)가 아니라 기존의 HTML 문법 그대로 작성해야 함
import ReactDOM from 'react-dom';

ReactDOM.render(
  <div>
    상태 변경: 
    <button className="btn" data-status="대기중">대기중</button>
    <button className="btn" data-status="진행중">진행중</button>
    <button className="btn" data-status="완료">완료</button>
  </div>,
  document.getElementById('root')
);
  • 자바스크립트 예약어와 같은 속성명은 사용할 수 없음
import ReactDOM from 'react-dom';

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

반드시 하나의 요소로 감싸기 - Fragment

자바스크립트 표현식 넣기

  • 중괄호{}를 활용하면 자바스크립트 표현식을 넣을 수 있음
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('곧 도착합니다!');
}

ReactDOM.render(
  <>
    <h1>{product + ' ' + model} 주문하기</h1>
    <img src={imageUrl} alt="제품 사진" />
    <button onClick={handleClick}>확인</button>
  </>,
  document.getElementById('root')
);
  • 중괄호 안에서 for, if문 등의 문장은 다룰 수 없음
  • 조건문이 꼭 필요하다면 조건 연산자를, 반복문이 꼭 필요하다면 배열의 반복 메소드를 활용하면 됨

컴포넌트

  • 리액트 엘리먼트: JSX 문법으로 작성한 요소는 결과적으로 자바스크립트 객체가 됨
    • 리액트 엘리먼트를 ReactDOM.render 함수의 아규먼트로 전달하게 되면, 리액트가 객체 형태의 값을 해석해서 HTML 형태로 브라우저에 띄워줌
    • 리액트 엘리먼트는 리액트로 화면을 그려내는데 가장 기본적인 요소임
    • 함수 형태로 만들면 커스텀 태그처럼 활용할 수 있음
import ReactDOM from 'react-dom';

const element = <h1>안녕 리액트!</h1>;
console.log(element);
ReactDOM.render(element, document.getElementById('root'));
  • 리액트 컴포넌트
    • 리액트 엘리먼트를 조금 더 자유롭게 다루기 위한 하나의 문법
    • 컴포넌트를 만드는 가장 간단한 방법은 자바스크립트의 함수를 활용하는 것임
    • 컴포넌트 함수 이름을 통해 하나의 태그처럼 활용할 수가 있음
    • 이런 특성을 모듈 문법으로 활용할 수 있음
    • 주의사항
      • JSX 문법으로 만든 리액트 엘리먼트를 리턴해야함
      • 리액트 컴포넌트의 이름은 반드시 첫 글자를 대문자로 작성해야 하며 컴포넌트 이름의 첫 글자가 소문자라면 오류가 발생함
import ReactDOM from 'react-dom';

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

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

ReactDOM.render(element, document.getElementById('root'));

Props

  • 컴포넌트에 지정한 속성들
  • Properties의 약자
  • 컴포넌트에 전달된 속성을 모두 가리키며 각각의 속성은 prop이라고 부름
  • 컴포넌트에 속성을 지정해주면 각 속성이 하나의 객체로 모여서 컴포넌트를 정의한 함수의 첫 번째 파라미터로 전달됨
// App.js
import Dice from './Dice';

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

export default App;
// Dice.js
import diceBlue01 from './assets/dice-blue-1.svg';
import diceBlue02 from './assets/dice-blue-2.svg';
// ...
import diceRed01 from './assets/dice-red-1.svg';
import diceRed02 from './assets/dice-red-2.svg';
// ...

const DICE_IMAGES = {
  blue: [diceBlue01, diceBlue02],
  red: [diceRed01, diceRed02],
};

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;

children

  • props에는 children이라는 조금 특별한 프로퍼티(prop, 프롭)가 있음
  • 컴포넌트를 작성할 때 컴포넌트를 단일 태그가 아니라 여는 태그와 닫는 태그의 형태로 작성하면, 그 안에 작성된 코드가 바로 이 children 값에 담기게 됨
  • 컴포넌트를 작성할 때 어떤 정보를 전달할 때는 일반적인 props의 속성값을 주로 활용하고, 화면에 보여질 모습을 조금 더 직관적인 코드로 작성하고자 할 때 children 값을 활용할 수가 있음
  • children을 활용하면 단순히 텍스트만 작성하는 걸 넘어서 컴포넌트 안에 컴포넌트를 작성할 수도 있고, 컴포넌트 안에 복잡한 태그들을 더 작성할 수도 있음
// 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;

State

  • 상태가 바뀔 때마다 화면을 새롭게 그려냄
  • state를 만들고, state를 바꾸기 위해서는 일단 useState라는 함수를 활용해야 함
import { useState } from 'react';

// ...

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

// ...
  • 보통 Destructuring 문법으로 작성함
  • useState 함수가 초깃값을 아규먼트로 받고 그에 따른 실행 결과로 요소 2개를 가진 배열의 형태로 리턴을 하기 때문임
  • 첫 번째 요소가 바로 state이고, 두 번째 요소가 이 state를 바꾸는 setter 함수임
  • 첫 번째 변수는 원하는 state의 이름(num)을 지어주고, 두 번째 변수에는 state 이름 앞에 set을 붙인 다음 카멜 케이스로 이름을 지어주는 것(setNum)이 일반적임
  • state는 변수에 새로운 값을 할당하는 방식으로 변경하는 것이 아니라 이 setter 함수를 활용해야 함
  • setter 함수는 호출할 때 전달하는 아규먼트 값으로 state 값을 변경해 줌
import { useState } from 'react';
import Button from './Button';
import Dice from './Dice';

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

  const handleRollClick = () => {
    setNum(3); // num state를 3으로 변경!
  };

  const handleClearClick = () => {
    setNum(1); // num state를 1로 변경!
  };

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

export default App;

참조형 State

  • 자바스크립트의 자료형은 크게 기본형(Primitive type)과 참조형(Reference type)로 나눌 수 있음
  • gameHistory state는 배열 값 자체를 가지고 있는 게 아니라 그 배열의 주솟값을 참조하고 있는 때문에 push 메소드로 배열 안에 요소를 변경했다고 하더라도 결과적으로 참조하는 배열의 주솟값은 변경된 것이 아니게 됨
  • 결과적으로 리액트 입장에서는 gameHistory state가 참조하는 주솟값은 여전히 똑같기 때문에 상태(state)가 바뀌었다고 판단하지 않음
// ... 

  const [gameHistory, setGameHistory] = useState([]);

  const handleRollClick = () => {
    const nextNum = random(6);
    gameHistory.push(nextNum);
    setGameHistory(gameHistory); // state가 제대로 변경되지 않는다!
  };

// ...
  • 그래서 참조형 state를 활용할 때는 반드시 새로운 참조형 값을 만들어 state를 변경해야 함
  • 가장 간단한 방법은 Spread 문법(...) 을 활용하는 것임
// ... 

  const [gameHistory, setGameHistory] = useState([]);

  const handleRollClick = () => {
    const nextNum = random(6);
    setGameHistory([...gameHistory, nextNum]); // state가 제대로 변경된다!
  };

// ...

컴포넌트가 좋은 이유

  • Component: 부품
  • 반복적인 개발이 줄어듦
  • 오류를 고치기 쉬움
  • 일을 쉽게 나눌 수 있음

컴포넌트 재사용하기

  • 스테이트 리프팅: 자식 컴포넌트의 스테이트를 부모 컴포넌트로 올려주는 것

리액트가 렌더링하는 방식

  • 렌더링: 리액트에서 렌더링이란, 컴포넌트가 현재 props와 state의 상태에 기초하여 UI를 어떻게 구성할지 컴포넌트에게 요청하는 작업을 의미함
  • Virtual 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 파일을 임포트하게 되면 html의 head태그 안에 style태그가 자동으로 작성됨
  • 컴포넌트 재사용과 디자인에 도움이 됨

디자인 적용하는 방법과 팁

이미지 불러오기

  • 이미지 파일은 import 구문을 통해 불러오고, 불러온 이미지 주소를 src 속성으로 사용하면 됨
import diceImg from './assets/dice.png';

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

export default App;

인라인 스타일

  • 리액트에서 인라인 스타일은 문자열이 아닌 객체형으로 사용함
  • 프로퍼티 이름은 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;

CSS 파일 불러오기

  • import 구문으로 파일을 불러올 수 있으며 from 키워드 없이 쓰면 됨
import diceImg from './assets/dice.png';
import './Dice.css';

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

export default App;

클래스네임 사용하기

  • 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;

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

템플릿 문자열을 사용한 예

  • 템플릿 문자열을 사용하면 개수가 늘어날수록 아래처럼 알아보기 힘들어진다는 문제점이 있음
function Button({ isPending, color, size, invert, children }) {
  const classNames = `Button ${isPending ? 'pending' : ''} ${color} ${size} ${invert ? 'invert' : ''}`;
  return <button className={classNames}>{children}</button>;
}

export default Button;

배열을 사용한 예

  • 배열을 이용해도 지저분해 질 수 있음
function Button({ isPending, color, size, invert, children }) {
  const classNames = [
    'Button',
    isPending ? 'pending' : '',
    color,
    size,
    invert ? 'invert' : '',
  ].join(' ');
  return <button className={classNames}>{children}</button>;
}

export default Button;

classnames 라이브러리를 사용한 예

  • npm install classnames를 입력하면 설치할 수 있음
  • import 로 불러와서 사용하면 됨
  • Classnames
import classNames from 'classnames';

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

export default Button;

명령어 복습하기

프로젝트 생성하기

npm init react-app .

개발 모드 실행하기

npm start (npm run start)

실행 중인 서버 종료하기

ctrl + c

개발된 프로젝트 빌드하기

npm run build

빌드한 것 로컬에서 실행하기

npx serve build


브라우저는 어떻게 리액트를 알아들을까?

  • transpiling: jsx로 작성한 파일을 순수 자바스크립트로 번역함
  • bundling: 웹브라우저가 다운받기 좋도록 묶음으로 만들어짐
    • 웹사이트에 접속한 사람들은 번들파일이 실행된 모습을 보는 것임

참고 자료

리액트의 렌더링은 어떻게 일어나는가?

profile
아이디어와 구현을 좋아합니다!

0개의 댓글