리액트

최상민·2023년 9월 23일

천재IT

목록 보기
10/10

초기 설정

  • yarn create react-app 명령어를 통해 리액트 앱 폴더를 생성할 수 있다.
  • yarn start 명령어를 사용하면 해당 폴더의 리액트 웹 앱을 구동시켜준다.
# yarn create react-app 이름
yarn create react-app ch01
yarn start

VScode에서 실행 오류 디버깅

💡 보안 상의 이유로 vscode에서 powershell로 yarn start 명령어가 불가능하다. cmd 창으로 바꾸어 yarn start를 실행하자

vscode 확장 프로그램

  • 리액트와 관련된 여러 확장 프로그램을 필요로 한다.
    • ESLint (VS Marketplace 🔗) : 문법 검사 도구
    • Reactjs code snippets (VS Marketplace 🔗)
    • Prettier - Code formatter (VS Marketplace 🔗) : 코드 스타일 자동 정리 도구
    • ES7+ React/Redux/React-Native snippets (VS Marketplace 🔗)

스니펫

  • 기본 템플릿 스니펫
//rafce
import React from 'react'

const Test2 = () => {
  return (
    <div>Test2</div>
  )
}

export default Test2
// rfc
import React from 'react'

export default function Test2() {
  return (
    <div>Test2</div>
  )
}
// rcc
import React, { Component } from 'react'

export default class Test6 extends Component {
  render() {
    return (
      <div>Test6</div>
    )
  }
}

JSX

리액트는 html이 아닌 js를 핵심으로 작성한다. 그러나 파일 확장자명이 js이더라도, 실제 파일은 jsx이다. jsx란 자바스크립트의 확장 문법으로 바벨을 사용하여 일반 자바스크립트 형태의 코드로 변환된다. jsx는 js와 html을 혼용하여 사용할 수 있다. 하지만 jsx 안에서 html 문법이 완전히 같지는 않다.

jsx 문법

  • 반드시 html 요소는 최상위의 부모 요소 하나로 감싸져있어야 한다.
    • 최상위 부모 요소로는
      , , <></> 등을 사용할 수 있다.
// 잘못된 예시
<h1>Title1</h1>
<h2>Title2</h2>

// 옳은 예시
<div>
	<h1>Title1</h1>
	<h2>Title2</h2>
</div>
  • html 요소 안에서도 자바스크립트 표현식을 {중괄호}로 감싸 사용할 수 있다.
const name = "Kim";
return (
	<>
		<h1>Name is {name}</h1>
	</>
)
  • if문은 사용불가하다. 대신 삼항 연산자를 사용해야 한다.
  • &&연산으로 참조건문을 사용할 수 있다.
  • undefined만 반환하여 렌더링하는 경우, 오류가 발생한다. 따라서 undefined의 리스크가 있을 때는 || 연산으로 기본 값을 지정하는 것이 좋다.
  • 기존의 html 태그의 속성 값에서 바(-)를 없애고 카멜 표기로 작성해야 한다.
const style = {
	backgroundColor: "red"
}

<h1 style={style}>backgroundColor</h1>
<h2 style={{backgroundColor: "blue"}}>inline style</h2>
  • html 태그에 class 속성을 className으로 표기한다.
  • html의 단일 태그였던 것도 반드시 닫아주어야 오류가 발생하지 앟ㄴ는다.
// html에서
<input type="text">

// jsx에서
<input type="text"></input>
<input type="text" /> // 이러한 방식을 self-closing 태그라 한다.
  • 주석은 {/중괄호/} 안의 주석으로 작성된다.
  • 일반적인 변수(let, var)의 경우 렌더링하면서 값이 초기화된다. 반면 state로 할당된 변수의 경우 렌더링되더라도 값이 초기화되지는 않는다.

컴포넌트

💡 클래스는 ES6 이후의 자바스크립트 문법이다.

컴포넌트 이름은 대문자로 시작한다.

클래스형 컴포넌트

  • render() { html 내용 } 함수가 반드시 필요하다.
  • state 기능 및 라이프 사이클 기능을 사용할 수 있고, 임의 매서드를 정의할 수 있다.
  • Component 클래스를 상속한다.
// rce
import React, { Component } from 'react'

export class Test6 extends Component {
  render() {
    return (
      <div>Test6</div>
    )
  }
}

export default Test6

함수형 컴포넌트

  • React v16.8 이후 Hooks 기능이 도입되어, state와 라이프 사이클 API를 사용할 수 있게 되었다.
// rfce
function Test6() {
  return (
    <div>Test6</div>
  )
}
export default Test6

props (properties)

컴포넌트의 속성을 설정할 때 사용하는 요소로 기본값을 지정하거나, 부모 컴포넌트에서 호출할 때 값을 설정해 줄 수 있다.

  • 부모 컴포넌트에서 값을 넘겨줄때 props의 이름만 넘겨주면 true로 값을 설정한 것으로 간주한다.
  • props.children: 컴포넌트 태그 사이의 내용을 보여준다.
// rafcp
const Child = props => {
  return (
    <div>
			<h1>{props.name}</h1>
			<div>{props.children}</div>
		</div>
  )
}
Test6.defaultProps = {
	name: "기본이름"
}

export default Child

-----

import Child from './Child';
const Parent= () => {
	return (
		<Child name="react" />;
		<Child>this is prop.children </Child>;
	)
}
const Child = ({name, children}) => {
  return (
    <div>
			<h1>{props.name}</h1>
			<div>{props.children}</div>
		</div>
  )
}
Test6.defaultProps = {
	name: "기본이름"
}

export default Child

State

컴포넌트 내부에서 바뀔 수 있는 값으로, 클래스형 컴포넌트의 state와 함수 컴포넌트의 state, useState가 있다.

일종의 변수이다. let과 var같은 경우도 사용 가능하고 값도 변경가능하지만, 자동으로 렌더링을 갱신하지 않는다. 즉, 실제로는 값이 바뀔지라도 화면 상에서는 바뀌지 않는다. 반면 state나 props의 경우 리액트에서 자동으로 변경된 데이터를 감지하고 다시 렌더링해준다.

  • state의 값은 setState 혹은 useState의 setter 함수를 통해서만 값을 바꾸어야 한다.

클래스형 컴포넌트의 state

  • constructor(props){ } 생성자 메서드를 작성하여 state를 설정할 수 있다. 이때 반드시 super(props)를 호출해야 한다.
  • this.state로 클래스 컴포넌트의 state를 조회할 수 있다. state는 json 객체 타입이어야 한다.
  • this.setState()로 클래스 컴포넌트의 state 값을 변경할 수 있다.
import {Component} from'react';

// 클래스 형의 state(상태정보)
class Test8 extends Component{
   constructor(props){
      super(props);
      this.state = {
         increaseNumber: 0,
         decreaseNumber: 0,
     };
   }

   render(){
      const {increaseNumber, decreaseNumber} = this.state;

      return <div className="App">
         <h1>증가하는 값 : {increaseNumber}</h1>
         <h1>감소하는 값 : {decreaseNumber}</h1>
         <button style={{ padding: '30px' }}
            onClick={()=>{
               this.setState({
                    increaseNumber: increaseNumber + 1,
                    decreaseNumber: decreaseNumber - 1,
               });
            }}
         > button </button>
      </div>
   }
}
export default Test8
  • constructor를 사용하지 않고 state를 선언할 수도 있다.
class Test8 extends Component{
	state = {
     increaseNumber: 0,
     decreaseNumber: 0,
	}

   render(){
      const {increaseNumber, decreaseNumber} = this.state;

      return <div className="App">
         <h1>증가하는 값 : {increaseNumber}</h1>
         <h1>감소하는 값 : {decreaseNumber}</h1>
         <button style={{ padding: '30px' }}
            onClick={()=>{
               this.setState({
                    increaseNumber: increaseNumber + 1,
                    decreaseNumber: decreaseNumber - 1,
               });
            }}
         > button </button>
      </div>
   }
}
export default Test8
  • this.setState를 override할 수도 있다. 이때 인자로 오는 값은 state가 된다.
this.setState(prevState => {
	return {number: prevState.number + 1};
})
  • this.setState함수 호출 뒤 콜백 함수를 등록하여 후속 작업을 처리할 수 있다.
this.setState(
	{number: number+1},
	() => {
		console.log("number가 증가했습니다");
	}
)

함수형 컴포넌트의 state

함수형 컴포넌트에서는 useState라는 훅(Hooks) 사용해 state와 this.setState를 커스터마이징 할 수 있다. 클래스형 컴포넌트의 state와 달리 함수형 컴포넌트의 useState에서는 객체 외에도 다양한 자료형의 값을 넣을 수 있다.

  • 한 컴포넌트 내에서 useState는 여러번 사용 가능하다.
import React, {useState} from 'react'

const Test9 = () => {
   const [message, setMessage] = useState('');
   const onClickEnter = () => setMessage("환영");
   const onClickLeave = () => setMessage("바이");
   const [color, setColor] = useState("black");
  return (
    <div className="App">
      <h1>Test9</h1>
      <button onClick={onClickEnter}>입실</button>
      <button onClick={onClickLeave}>퇴실</button>
      <h1 style={{color}}>{message}</h1>
      <button onClick={() => setColor('gold')}></button>
    </div>
  )
}

export default Test9

함수

반복문

  • arr.map 함수를 사용하여 return문 내에서 반복문을 사용할 수 있다.
import React from 'react'

const Test3 = () => {
   const arr = [1, 2, 3, 4, 5, 6, 7];
   return (
   <div className="App">
      <ul>
         {
            arr.map((item, index) => {
               return <li>{item}</li>
            })
         }
      </ul>
      
   </div>
   )
}

export default Test3
  • return문 내에서는 for문을 자체를 사용할 수 없어, arrow함수로 선언하고, 호출해야한다.
function App() {
  const weekArr = ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"];
  
  const rendering = () => {
    const result = [];
    for (let i = 0; i < weekArr.length; i++) {
      result.push(<span key={i}>{weekArr[i] + " / "}</span>);
    }
    return result;
  };

  return <div>{rendering()}</div>;
}

이벤트 🔗

  • 이벤트(event) : 사용자가 특정 요소를 건드리는 사건
  • 이벤트의 종류 : Mouse, Keyboard, Form, Touch, File, Image, Video 등
    • Mouse: onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp
    • Keyboard: onKeyDown onKeyPress onKeyUp
// arrow function으로 정의 가능
<button onClick={() => { this.setState({message:''})}}>지우기</button>

// 그냥 function은 불가능
<button onClick={function() {this.setState({message: ''})}}}>지우기</button>

// arrow 함수 변수 사용 가능
const onDelete = () => { this.setState({message:''}); };
<button onClick={onDelete}>지우기</button>

// arrow 함수 변수를 호출하는 것을 불가능. 렌더링되는 시점에 호출되기 때문에 비정상 작동한다.
const onDelete = () => { this.setState({message:''}); };
<button onClick={onDelete()}>지우기</button>

// 그냥 함수 변수 사용 불가능
const onDelete2 = function () { this.setState({ message: "" }); };
<button onClick={onDelete2}>지우기</button>

Hook(훅) 🔗

Hook은 함수 컴포넌트에서 React state와 생명주기 기능(lifecycle features)을 “연동(hook into)“할 수 있게 해주는 함수입니다. Hook은 class 안에서는 동작하지 않습니다. 대신 class 없이 React를 사용할 수 있게 해주는 것입니다.

useState

  • useState(초기값) : 렌더링되는 변수 state와 그 state를 세팅하는 setState가 반환된다.
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}
💡 리액트의 훅은 값이 갱신되는 것이 아니라 대체되는 것이다. 즉 새로 생성되는 거나 마찬가지고, 따라서 배열이나 객체의 값을 수정할 때는 바로 해당 배열이나 객체에 값을 넣는 것이 아니라, 기존 값을 가져와서 수정 후 다시 대입해주어야 한다.
  • state 배열의 값을 추가할 때는 배열의 함수인 .concat(새로운 값)을 사용한다. 제거할 때는 .filter(제거할 조건)을 사용한다.
import React, { useState } from "react";
//useState를 이용한 객체의 배열 추가/제거
const Test2 = () => {
  const [names, setNames] = useState([
    { id: 1, text: "박진권" },
    { id: 2, text: "신승원" },
    { id: 3, text: "백준철" },
    { id: 4, text: "구예진" }
  ]);
  const [inputText, setInputText] = useState("");
  const [nextId, setNextId] = useState(5);
  const onChange = (e) => {
    setInputText(e.target.value);
  };
  const onClick = (e) => {
    const nextNames = names.concat({
      //하나의 객체 생성
      id: nextId,
      text: inputText
    });
    setNextId(nextId + 1); //아이디를 하나 증가
    setNames(nextNames); //객체를 배열에 추가
    setInputText("");
  };
  const onRemove = (id) => {
    const nextNames = names.filter((data) => data.id !== id);
    setNames(nextNames);
  };
  const namesList = names.map((data) => (
    <li key={data.id} onDoubleClick={() => onRemove(data.id)}>
      {" "}
      {data.id}: {data.text}
    </li>
  ));
  return (
    <div className="App">
      <input value={inputText} onChange={onChange} />
      <button onClick={onClick}>추가</button>
      <ul className="list">{namesList}</ul>
    </div>
  );
};
export default Test2;
  • state 객체의 값을 수정할 때도 객체를 복사본을 만든 후 값을 업데이트한다.
import React, { useState } from "react";

function ObjectExample() {
  const [person, setPerson] = useState({ name: "John", age: 30 });

  const updateAge = () => {
    // 객체의 복사본을 만들어서 업데이트합니다.
    const updatedPerson = { ...person, age: person.age + 1 };
    setPerson(updatedPerson);
  };

  return (
    <div>
      <p>Name: {person.name}</p>
      <p>Age: {person.age}</p>
      <button onClick={updateAge}>Update Age</button>
    </div>
  );
}

export default ObjectExample;
  • state 객체의 배열 값을 수정할 때도 객체의 배열의 복사본을 만들어서 수정해야한다. push, pop 안 된다.
import React, { useState } from "react";

function ObjectExample() {
  const [student, setStudent] = useState({
    id: [1, 2],
    name: ["kim1", "kim2"]
  });

  const updateStudent = () => {
    let updatedStudent = {
      id: [...student.id, 3],
      name: [...student.name, "kim3"]
    };
    setStudent(updatedStudent);
  };

  return (
    <div>
      <p>
        {student.id} {student.name}
      </p>
      <button onClick={updateStudent}>Update Student</button>
    </div>
  );
}

export default ObjectExample;

useRef

javascript에서는 특정 DOM 을 선택해야 하는 상황에 getElementById, querySelector 같은 DOM Selector 함수를 사용해서 DOM 을 선택한다. 이와 같이 리액트에서 특정 DOM 요소를 선택하여 변수를 붙여줄 때 사용하는 것이 ref(reference)이다.

  • 클래스형 컴포넌트의 React.createRef()와 같다.
import React, { Component } from 'react'
import './Test4.css';
class Test4 extends Component {
  input = React.createRef();    //ref 생성  
  render() {
    return (
      <div className='App'>
        <input type='password' ref={this.input} />
      </div>
    )
  }
}

export default Test4;
  • 함수형 컴포넌트에서는 useRef(초기값)를 사용한다.
    • 초기값: useRef 훅은 초기값을 인자로 받는데, 이 인자는 useRef가 생성한 ref 객체의 .current 프로퍼티의 초기값으로 사용된다. 초기값을 전달하지 않아도 무방하며, 필요한 경우 초기값을 지정할 수 있다. useRef의 초기값을 사용하는 경우에는 해당 초기값이 처음 렌더링 시에만 사용되며, 이후에는 업데이트되지 않는다. 따라서 useRef를 통해 참조한 값은 컴포넌트의 렌더링과 관계없이 일관되게 유지된다. 즉 useRef의 .current의 값을 바꾸더라도 렌더링하기 전에는 화면 상으로 값이 바뀌지 않는다.
  • DOM 선택자로써 useRef 사용: useRef() 를 사용하여 Ref 객체를 만들고, 이 객체를 우리가 선택하고 싶은 DOM 에 ref 값으로 설정해주어야 한다. 그러면, Ref 객체의 .current 값은 우리가 원하는 DOM 을 가르키게 된다.
import React, { useRef, useEffect } from "react";

function InitialDOMReference() {
  // 초기값으로 DOM 요소를 설정하려면 useRef의 인자로 DOM 요소를 전달합니다.
  const divRef = useRef(1);

  useEffect(() => {
    // 컴포넌트가 마운트되면 초기값으로 설정된 div 요소에 스타일을 적용합니다.
    if (divRef.current) {
      divRef.current.style.backgroundColor = "lightblue";
    }
  }, []);

  return (
    <div>
      {/* 초기값으로 설정할 div 요소 */}
      <div>{divRef.current}</div>
      <div ref={divRef}>This is a div element</div>
    </div>
  );
}

export default InitialDOMReference;
  • 이전 값 저장 및 추적 용도로써 useRef 사용: useState는 setState를 통해서 값을 변경하며 자체적으로 렌더링된다. 하지만 useRef의 경우 .current에 직접적으로 값을 변경하며, 자체적으로 렌더링되지 않기 때문에 컴포넌트의 렌더링 사이클 간에 값의 변경 이력을 추적하거나 이전 값을 비교할 수 있다. (와 어렵다)
import React, { useState, useEffect, useRef } from 'react';

function ValueComparisonExample() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();

  useEffect(() => {
    // 컴포넌트가 업데이트될 때 이전 count 값을 저장합니다.
    prevCountRef.current = count;
  }, [count]);

  const handleIncrement = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Current Count: {count}</p>
      <p>Previous Count: {prevCountRef.current}</p>
      <button onClick={handleIncrement}>Increment</button>
    </div>
  );
}

export default ValueComparisonExample;

useEffect 🔗

  • 컴포넌트가 렌더링 될 때 특정 작업을 실행하는 훅.
    • React의 함수형 컴포넌트에서 부수 효과(side effect)를 수행할 때 사용되는 훅(Hook) 중 하나이다. 부수 효과란 컴포넌트의 렌더링과 관련이 없는 작업으로, 주로 데이터 가져오기, DOM 조작, 구독 설정 및 해제 등을 포함한다.
    • useEffect는 컴포넌트가 렌더링될 때, 업데이트될 때, 또는 컴포넌트가 언마운트될 때 실행될 콜백 함수를 설정할 수 있다. 이 콜백 함수는 컴포넌트 렌더링 이후 비동기로 실행되며, 이러한 비동기 작업을 통해 부수 효과를 수행할 수 있다.
  • useEffet의 기본 구조
    • dependecies: 의존성 배열(dependency array)로, 배열 내의 값이 변경될 때에만 부수 효과 함수가 재실행된다. 이 배열을 생략하면 컴포넌트가 렌더링될 때마다 부수 효과 함수가 실행된다.
useEffect(() => {
  // 부수 효과를 수행할 코드
}, [dependencies]);
  • 의존성 배열이 있는 경우
import React, { useState, useEffect } from 'react';

function WithDependencies() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]); // count 값이 변경될 때만 useEffect 실행

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default WithDependencies;
  • 의존성 배열이 없는 경우
import React, { useState, useEffect } from 'react';

function WithoutDependencies() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("Effect ran");
  }, []); // 빈 배열이므로 컴포넌트가 마운트될 때만 실행

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default WithoutDependencies;

useContext

  • 계층구조에서 상위와 하위 컴포넌트 사이에 데이터 교환하는 훅.
    • prop는 부모에서 자식으로 데이터를 제공하지만, Context는 그 이상 즉, 조상에서 후손 컴포넌트로 데이터를 전달한다.
  • Provider: 상위 계층에서 하위계층에 value를 제공. 두 개 이상의 value는 객체나 배열로 전달해야 한다.
    import React from 'react'
    import {AgeContext} from './AgeContext'
    import {NameContext} from './NameContext'
    import Header from './Header'
    
    const Test5 = () => {
    
       return (
          <div>
             <h1>Test 5</h1>
             <AgeContext.Provider value="age of provider">
                <NameContext.Provider value="this is name of provider">
                   <Header />
                </NameContext.Provider>
             </AgeContext.Provider>
             
          </div>
       )
    }
    
    export default Test5
  • 데이터를 전달하는 곳에서 createContext 사용
    import React, {createContext} from 'react'
    
    export const AgeContext = createContext(null);
  • 데이터를 전달받는 곳에서 useContext를 사용
    import React, {useContext} from 'react'
    import { AgeContext} from './AgeContext'
    import { NameContext} from './NameContext'
    
    const Header = () => {
       const age = useContext(AgeContext);
       const user = useContext(NameContext);
    
      return (
        <div>
          <p>Hello, <strong>{user}</strong>!</p>
          <p>You are <strong>{age}</strong> years old.</p>
        </div>
      )
    }
    
    export default Header

useMemo

  • 컴포넌트에서 활용한 데이터를 메모리에 저장해두었다가 동일한 입력이 들어오면, 재활용할 수 있도록 해주는 훅. 구조는 다음과 같다.
    • dependencies: 메모이제이션하고자 하는 값 또는 연산이 어떤 의존성에 의존하는지를 나타낸다. 배열에 포함된 값들 중 하나라도 변경될 때에만 useMemo 내의 함수가 다시 실행되고, 그 결과가 업데이트된다.
const memoizedValue = useMemo(() => {
  // 계산하고 메모이제이션할 값 또는 연산
  return result;
}, [dependencies]);
  • useMemo를 사용하지 않았을 때는 다른 state를 바꾸어 렌더링이 될때마다 계산된다.
    import React, { useState } from 'react';
    
    function Fibonacci({ n }) {
      // 피보나치 수열을 계산하는 함수
      const calculateFibonacci = (num) => {
        console.log(`Calculating Fibonacci(${num})...`);
    
        if (num <= 1) {
          return num;
        }
    
        let prev = 0;
        let current = 1;
    
        for (let i = 2; i <= num; i++) {
          const next = prev + current;
          prev = current;
          current = next;
        }
    
        return current;
      };
    
      // 매 렌더링마다 중복 계산
      const fibonacciNumber = calculateFibonacci(n);
    
      return (
        <div>
          <p>Fibonacci({n}): {fibonacciNumber}</p>
        </div>
      );
    }
    
    function App() {
      const [fibNumber, setFibNumber] = useState(10);
      const [number, setNumber] = useState(0);
    
      return (
        <div>
          <input
            type="number"
            value={fibNumber}
            onChange={(e) => setFibNumber(Number(e.target.value))}
          />
          <Fibonacci n={fibNumber} />
          <p>{number}</p>
          <button onClick={()=>{setNumber(number+1)}}>increase Number</button>
        </div>
      );
    }
    
    export default App;
  • useMemo를 사용할 경우 depenedencis가 바뀔 때만 렌더링 된다.
    import React, { useState, useMemo } from "react";
    
    function Fibonacci({ n }) {
      // 피보나치 수열을 계산하고 메모이제이션
      const fibonacciNumber = useMemo(() => {
        console.log(`Calculating Fibonacci(${n})...`);
    
        if (n <= 1) {
          return n;
        }
    
        let prev = 0;
        let current = 1;
    
        for (let i = 2; i <= n; i++) {
          const next = prev + current;
          prev = current;
          current = next;
        }
    
        return current;
      }, [n]);
    
      return (
        <div>
          <p>
            Fibonacci({n}): {fibonacciNumber}
          </p>
        </div>
      );
    }
    
    function App() {
      const [fibNumber, setFibNumber] = useState(10);
      const [number, setNumber] = useState(6);
    
      return (
        <div>
          <h1>App</h1>
          <input
            type="number"
            value={fibNumber}
            onChange={(e) => setFibNumber(Number(e.target.value))}
          />
          <Fibonacci n={fibNumber} />
          <p>{number}</p>
          <button onClick={()=>{setNumber(number+1)}}>number increase</button>
        </div>
      );
    }
    
    export default App;

useReducer

현재 상태변수(state) 객체와 행동(action) 객체를 인자로 받아, 새로운 상태를 반환하는 훅. useState보다 더 복잡하고, 다양한 변수나 객체를 관리할 때 사용한다. useState와 비슷한 역할을 하지만 더 많은 제어와 구조화된 상태 관리를 가능하게 한다.

  • useReducer의 구조
    • state: 리듀서 함수를 통해 관리되는 현재 상태입니다.
    • dispatch: 액션을 디스패치하는 함수입니다. 이 함수를 호출하여 상태를 변경할 수 있습니다.
    • reducer: 리듀서 함수로, 현재 상태와 액션을 받아 새로운 상태를 반환하는 함수입니다.
    • initialState: 초기 상태로, 컴포넌트가 처음 렌더링될 때 사용됩니다.
const [state, dispatch] = useReducer(reducer, initialState);

import React, { useReducer } from 'react';

// 리듀서 함수
const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

function Counter() {
  // useReducer를 사용하여 상태와 디스패치 함수를 가져옴
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
}

export default Counter;

useCallback

함수를 재활용하기 하기 위한 훅(hook)으로 함수를 메모이제이션하고 필요할 때만 새로 생성되도록 도와준다.

💡 실제로 함수가 재생성되는지 확인하는 것이 어렵다. 크롬 웹스토어에서 React DevTools을 이용해서 렌더링되는 항목을 하이라이트해서 볼 수 있다고 한다.
  • useCallback의 구조
    • memoizedCallback: 메모이제이션된 콜백 함수입니다. 이 함수는 렌더링 시에 재사용됩니다.
    • callback: 메모이제이션하고자 하는 콜백 함수입니다.
    • dependencies (선택사항): 콜백 함수가 의존하는 값들의 배열입니다. 배열에 포함된 값 중 하나라도 변경되면, 메모이제이션된 함수가 새로 생성됩니다.
const memoizedCallback = useCallback(callback, dependencies);

React-router-dom 🔗

React-router-dom은 웹 애플리케이션의 경로 및 파라미터, 구조 등을 연결될 수 있도록 하는 훅(hook)의 모음체이다. BrowserRouter > Routes > Route 순의 계층을 이룬다.

router, routes, route

  • BrowserRouter는 React Router의 주요 컴포넌트 중 하나로, HTML5 History API를 사용하여 브라우저의 URL을 관리합니다. 이 컴포넌트를 앱의 최상위 레벨에서 사용하면 라우팅을 설정할 수 있습니다.
  • Routes 컴포넌트를 사용하면 라우트를 중첩 구조로 정의할 수 있으며, 각 라우트에 대한 조건을 기반으로 라우팅을 처리할 수 있습니다.
  • Route 컴포넌트는 특정 URL 경로와 일치하는 경우에 해당 컴포넌트를 렌더링하는 역할을 합니다. path 와 사용하여 일치시킬 경로를 지정하고, **element** 를 사용하여 해당 경로와 일치할 때 렌더링할 컴포넌트를 지정합니다.
  • Outlet을 이용해 Layout을 짤 수 있다. Layout이 될 상위 Route의 페이지에 Outlet을 넣어주면, Outlet 자리에 하위 Route 페이지의 내용이 들어간다.
import './App.css';
import React, { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Layout from './Layout';
import Home from './pages/Home';
import About from './pages/About';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="/about" element={<About />} />
        </Route>  
      </Routes>
    </Router>
  );
}

export default App;
  • Link 컴포넌트는 페이지 간의 내비게이션을 쉽게 구현할 수 있도록 도와주는 컴포넌트입니다. 주로 애플리케이션 내에서 다른 페이지로 이동하거나 특정 경로로 이동하기 위해 사용됩니다. to props를 설정해 Route로 설정된 특정 페이지로 이동
  • useNavigate: 자바스크립트와 같은 페이지 이동 또는 URL 제어와 관련한 rrd의 훅(hook)
  • Navigate: Link+replace. Route로 설정된 특정 페이지로 이동. 주로 이전 페이지, 다음 페이지로 이동하거나 특정 경로로 이동하고자 할 때 사용합니다.
import React from 'react'
import { Outlet,  useNavigate, Link } from 'react-router-dom';
const Layout = () => {
    const navigate = useNavigate();
    const goBack = () => { navigate(-1); };
    const goHome = () => { navigate('/', { replace : true }); }

		//const isLoggedIn = false;

		if (!isLoggedIn) {
    return <Navigate to="/login" replace={true} />;
	  }		

    return (
        <div>
            <header style={{ background:'#ececec', padding:20, fontSize:24 }}>
                <button onClick={goBack}>뒤로가기</button>
                <button onClick={goHome}>홈으로</button>
                <ul style={{ float:'right' }}>
                    <li><Link to='/articles'>게시판</Link></li>
                    <li><Link to='/login'>로그인</Link></li>
                    <li><Link to='/mypage'>마이페이지</Link></li>
                </ul>
            </header>
            <main>
                <Outlet />
            </main>
        </div>
    )
}
export default Layout

리액트 스타일

yarn add sass

yarn add open-color include-media

yarn add classname

yarn add styled-component

확장프로그램

  • Sass/Less/Stylus/Pug/Jade/Typescript/Javascript Compile Hero Pro (VS Marketplace 🔗)

sass

Sass(시스루 CSS)는 CSS의 확장된 버전으로, 스타일 시트를 보다 효율적으로 작성하고 관리할 수 있도록 도와주는 스타일 언어입니다. Sass는 CSS의 단점을 보완하고, 코드를 더 모듈화하고 가독성 있게 작성할 수 있도록 다양한 기능을 제공합니다.

Sass의 주요 특징과 기능은 다음과 같습니다:

  1. 변수 (Variables): Sass는 변수를 사용하여 스타일 속성의 값을 지정하고 재사용할 수 있습니다. 예를 들어, 색상 코드나 글꼴명을 변수로 정의하고 여러 규칙에서 사용할 수 있습니다.
  2. 중첩 (Nesting): Sass는 CSS 규칙을 중첩하여 가독성을 향상시킵니다. 부모 선택자 내에서 자식 규칙을 중첩하여 스타일 계층 구조를 명확하게 표현할 수 있습니다.
  3. 믹스인 (Mixins): 믹스인은 스타일 규칙을 재사용하기 위한 방법으로, 함수처럼 동작하며 여러 규칙에서 적용할 수 있는 스타일 집합을 정의할 수 있습니다.
  4. 확장 (Extend/Inheritance): 스타일 규칙을 다른 규칙으로 확장하거나 상속할 수 있으며, 코드의 중복을 줄이고 유지보수성을 향상시킵니다.
  5. 연산 (Operations): Sass는 산술 연산을 지원하여 스타일 속성 값을 계산하고 조작할 수 있습니다. 예를 들어, 여러 값을 더하거나 곱할 수 있습니다.
  6. 조건문과 반복문 (Control Directives): Sass는 조건문과 반복문을 사용하여 스타일 규칙을 동적으로 생성하고 조작할 수 있습니다.
  7. 파일 분할 (File Splitting): 큰 프로젝트의 스타일 시트를 여러 파일로 분할하여 개발 및 유지보수를 용이하게 합니다.

Sass는 .scss 확장자를 가진 파일로 작성되며, 이 파일들은 Sass 컴파일러를 통해 일반 CSS 파일로 변환됩니다. Sass를 사용하려면 먼저 Sass 컴파일러를 설치하고 설정해야 합니다. 이러한 작업을 통해 Sass 코드를 CSS로 변환하고 웹 애플리케이션에서 사용할 수 있습니다.

Sass는 프론트엔드 웹 개발에서 널리 사용되며, 스타일 시트의 유지보수와 확장성을 향상시키는 데 도움을 줍니다. CSS 작업을 보다 효율적으로 수행하고 가독성 있는 코드를 작성할 수 있도록 도와주는 강력한 도구입니다.

open-color

open-color은 색상 팔레트를 관리하고 선택할 수 있게 해주는 오픈 소스 색상 팔레트 라이브러리입니다. 이 라이브러리는 웹 개발자들이 프로젝트에서 사용할 색상을 쉽게 선택하고 일관된 디자인을 유지하는 데 도움을 줍니다.

open-color 팔레트는 다양한 색상과 톤을 제공하며, 각 색상은 이름과 함께 기본 색상 (red, blue, green 등)과 더 어두운/더 밝은 버전 (red[0], red[1], red[2], ..., blue[0], blue[1], blue[2], ...)으로 구성됩니다. 이러한 다양한 색상을 사용하여 웹 애플리케이션 또는 웹사이트의 디자인을 만들 수 있습니다.

include-media

include-media는 미디어 쿼리를 편리하게 관리하고 사용할 수 있게 해주는 CSS 미디어 쿼리 라이브러리입니다. 이 라이브러리는 반응형 웹 디자인을 구현하는 데 도움을 주며, 다양한 화면 크기와 장치에 대한 스타일을 쉽게 정의하고 적용할 수 있도록 해줍니다.

include-media를 사용하면 CSS 미디어 쿼리를 JavaScript 함수와 유사한 방식으로 작성할 수 있습니다. 이를 통해 코드를 더 읽기 쉽게 만들고 유지보수를 용이하게 할 수 있습니다.

주요 특징과 사용법은 다음과 같습니다:

  1. 사용자 정의 미디어 쿼리 정의: include-media를 사용하여 원하는 미디어 쿼리를 정의하고 이름을 부여할 수 있습니다. 이를 통해 미디어 쿼리를 재사용할 수 있습니다.
  2. 읽기 쉬운 구문: JavaScript 함수와 유사한 구문을 사용하여 미디어 쿼리를 작성합니다. 예를 들어, media.between('400px', '800px')와 같이 사용할 수 있습니다.
  3. 미디어 쿼리 조건 만족 여부 확인: include-media를 사용하여 특정 미디어 쿼리가 현재 브라우저 환경에서 만족되는지 확인할 수 있습니다.
  4. 미디어 쿼리 범위 지정: 특정 범위 내에 있는 미디어 쿼리를 정의하고 사용할 수 있습니다. 예를 들어, media.between('400px', '800px')를 사용하여 400px에서 800px 사이의 미디어 쿼리를 만들 수 있습니다.

include-media는 CSS 미디어 쿼리를 관리하고 사용하기 위한 도구로, 반응형 웹 디자인을 개발하고 구현하는 데 도움을 주며 코드를 더 구조적이고 읽기 쉽게 만듭니다. 이 라이브러리는 JavaScript 환경에서 CSS 미디어 쿼리를 더욱 효과적으로 다루는 데 유용합니다.

styled-components

styled-components는 JavaScript에서 CSS 스타일을 관리하고 생성하는 데 사용되는 JavaScript 라이브러리입니다. 이 라이브러리는 React 애플리케이션을 개발할 때 매우 인기가 있으며, 컴포넌트 기반 스타일링을 지원하여 컴포넌트와 스타일을 쉽게 결합할 수 있습니다.

styled-components를 사용하면 다음과 같은 주요 기능과 이점을 얻을 수 있습니다:

  1. 컴포넌트 기반 스타일링: styled-components를 사용하면 각 React 컴포넌트에 스타일을 연결할 수 있습니다. 스타일은 해당 컴포넌트에 로컬하게 적용되므로 스타일 간의 충돌을 방지하고 유지보수를 쉽게 만듭니다.
  2. JavaScript를 사용한 스타일 작성: 스타일을 JavaScript 템플릿 리터럴을 사용하여 작성합니다. 이는 동적인 스타일 작업을 수행하거나 프롭스에 따라 스타일을 변경하는 데 매우 편리합니다.
  3. 스타일 컴포넌트 생성: styled-components를 사용하여 스타일이 적용된 새로운 컴포넌트를 생성할 수 있습니다. 이를 통해 스타일을 재사용하고 컴포넌트를 더욱 모듈화할 수 있습니다.
  4. 자동 벤더 프리픽싱: styled-components는 자동으로 벤더 프리픽스를 추가하여 크로스 브라우징을 지원합니다. 따라서 CSS 속성을 작성할 때 벤더별 접두사를 신경 쓸 필요가 없습니다.
  5. 서버 사이드 렌더링(SSR) 지원: styled-components는 서버 사이드 렌더링을 지원하여 초기 로드 시에도 올바른 스타일이 적용되도록 합니다.

styled-components는 React 생태계에서 널리 사용되며, 많은 개발자와 프로젝트에서 선호되는 스타일링 도구 중 하나입니다. React 컴포넌트와 스타일을 쉽게 통합하고 관리할 수 있으므로, 복잡한 웹 애플리케이션의 스타일링 작업을 단순화하는 데 도움이 됩니다.

  • 예시 styled-components를 사용한 간단한 예시를 보여드리겠습니다. 이 예시에서는 React 컴포넌트에 스타일을 적용하고 커스텀 버튼을 생성하는 방법을 보여줍니다. 먼저, styled-components 라이브러리를 설치합니다:
    npm install styled-components
    # 또는
    yarn add styled-components
    
    다음은 예시 코드입니다:
    import React from 'react';
    import styled from 'styled-components';
    
    // 스타일을 정의한 컴포넌트
    const StyledButton = styled.button`
      background-color: #007bff;
      color: #fff;
      font-size: 16px;
      padding: 10px 20px;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    
      &:hover {
        background-color: #0056b3;
      }
    `;
    
    // 스타일이 적용된 버튼을 갖는 컴포넌트
    function MyComponent() {
      return (
        <div>
          <h1>Styled Button Example</h1>
          <StyledButton>Click Me</StyledButton>
        </div>
      );
    }
    
    export default MyComponent;
    
    이 코드에서는 다음과 같은 작업을 수행하고 있습니다:
    1. styled-components에서 styled 함수를 사용하여 스타일이 적용된 StyledButton 컴포넌트를 생성합니다. 이 컴포넌트의 스타일은 템플릿 리터럴을 사용하여 정의되며, 일반적인 CSS와 유사한 구문을 사용합니다.

    2. StyledButton 컴포넌트를 사용하여 버튼을 렌더링합니다. 이 버튼은 StyledButton 컴포넌트의 스타일이 적용됩니다.

    3. 버튼에 대한 스타일은 StyledButton 컴포넌트의 속성으로 정의되어 있으므로, 컴포넌트를 재사용하거나 동적으로 스타일을 변경할 수 있습니다.

      styled-components를 사용하면 React 컴포넌트와 스타일을 쉽게 통합할 수 있으며, 각 컴포넌트의 스타일을 독립적으로 관리할 수 있습니다. 이를 통해 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.

classname

classname 또는 classnames는 JavaScript 환경에서 CSS 클래스 이름을 조작하고 조합하는 유용한 도구 중 하나입니다. 주로 React 컴포넌트를 개발하거나 다른 JavaScript 환경에서 CSS 클래스를 동적으로 처리할 때 사용됩니다.

주요 용도와 기능은 다음과 같습니다:

  1. CSS 클래스 결합: 여러 CSS 클래스 이름을 하나의 문자열로 결합하여 동적으로 클래스를 추가할 수 있습니다.
  2. 조건부 클래스: 조건에 따라 특정 클래스를 동적으로 추가하거나 제거할 때 유용합니다. 예를 들어, 특정 조건이 충족되면 "active" 클래스를 추가하고 그렇지 않으면 추가하지 않을 수 있습니다.
  3. 반복적 클래스: 반복되는 패턴의 클래스 이름을 동적으로 생성할 때 사용됩니다. 예를 들어, 리스트 아이템을 매핑하면서 각 아이템에 서로 다른 클래스를 지정할 수 있습니다.

React와 함께 사용할 때, 동적으로 클래스를 관리하고 조작하는 데 특히 유용합니다. React 컴포넌트에서 상태나 프롭스에 따라 클래스를 추가하거나 제거하고, 조건부로 스타일을 변경하는 데 활용할 수 있습니다.

  • classnames 라이브러리를 사용한 몇 가지 예제를 더 제공하겠습니다.
    1. 조건부 클래스 추가/제거:

      import classNames from 'classnames';
      
      const isActive = true;
      const isDisabled = false;
      
      const buttonClasses = classNames('button', {
        'active': isActive,
        'disabled': isDisabled,
      });
      
      // 결과: 'button active'
      

      위의 예제에서는 isActive 변수가 true이므로 'active' 클래스가 추가되고, isDisabled 변수가 false이므로 'disabled' 클래스는 추가되지 않습니다.

    2. 다중 클래스 결합:

      import classNames from 'classnames';
      
      const primaryColor = 'primary';
      const additionalClasses = 'extra-class';
      
      const elementClasses = classNames(primaryColor, additionalClasses);
      
      // 결과: 'primary extra-class'
      

      이 예제에서는 두 개의 클래스 이름을 결합하여 하나의 문자열로 만들고 있습니다.

    3. 조건부 클래스와 반복 요소:

      ```jsx
      import classNames from 'classnames';
      
      const items = ['item1', 'item2', 'item3'];
      const selectedItem = 'item2';
      
      const listClasses = classNames('list', {
        'active': selectedItem === 'item2',
      });
      
      const itemClasses = items.map((item) =>
        classNames('item', {
          'selected': item === selectedItem,
        })
      );
      
      // 결과: list active, item, item selected, item
      
      ```
      
      이 예제에서는 반복 요소 목록을 매핑하면서 조건부 클래스를 추가하고 있습니다. 선택된 항목은 'selected' 클래스가 추가되고, 특정 항목은 'active' 클래스가 추가됩니다.

      classnames 라이브러리를 사용하면 동적인 클래스 이름 관리를 간편하게 할 수 있으며, React 컴포넌트에서 클래스를 조작하거나 조건부로 스타일을 변경하는 데 유용합니다.

profile
상민

0개의 댓글