React 숙련주차 강의

박감자·2024년 11월 5일

TIL

목록 보기
34/34

숙련주차 강의 시작

  1. styled-components
  2. React hook: useState, useeffect, useRef, useContext
  3. react-router-dom

styled-components

CSS in JS 개념

css 파일을 따로 만드는 것이 아닌 말 그대로 JS에서 CSS를 작성하여 컴포넌트를 꾸미는 방식을 의미한다. JS에서 작성하게 되므로 style 적용시 조건문과 변수 등의 로직도 이용이 가능하다.

패키지 설치

이런 방식으로 컴포넌트를 꾸밀 수 있게 도와주는 것이 styled-components라는 패키지이다

VSCode로 설치시

yarn add styled-components

사용법

강의에서 사용한 예제

// src/App.js

import React from "react";
// styled-components에서 styled 라는 키워드를 import 합니다.
import styled from "styled-components";

// styled키워드를 사용해서 styled-components 방식대로 컴포넌트를 만듭니다. 
const StBox = styled.div`
	// 그리고 이 안에 스타일 코드를 작성합니다. 스타일 코드는 우리가 알고 있는 css와 동일합니다.
  width: 100px;
  height: 100px;
  border: 1px solid red;
  margin: 20px;
`;

const App = () => {
	// 그리고 우리가 만든 styled-components를 JSX에서 html 태그를 사용하듯이 사용합니다.
  return <StBox>박스</StBox>;
};

export default App;

const 변수명 = styled.div``; 으로 백틱을 사용하여 안에 CSS 스타일링을 적어주면 된다. 아래의 예시와 같이 JS 로직과 결합이 가능하다.

// src/App.js

import React from "react";
import styled from "styled-components";

const StBox = styled.div`
  width: 100px;
  height: 100px;
  border: 1px solid ${(props) => props.borderColor};
  margin: 20px;
`;
// 박스의 색을 배열에 담습니다.
const boxList = ["red", "green", "blue"];

// 색을 넣으면, 이름을 반환해주는 함수를 만듭니다.
const getBoxName = (color) => {
  switch (color) {
    case "red":
      return "빨간 박스";
    case "green":
      return "초록 박스";
    case "blue":
      return "파란 박스";
    default:
      return "검정 박스";
   }
};
const App = () => {
  return (
    <>
      {/* 빨간, 초록, 파랑 박스가 차례대로 생성 */}
      {boxList.map((box) => (
        <StBox borderColor={box}>{getBoxName(box)}</StBox>
      ))}
    </>
  );
};

export default App;

GlobalStyles (전역)

css 파일의 모든 요소에 적용하는 방식이 있었듯이, GlobalStyles를 사용하여 전역에 스타일링을 적용할 수 있다. (강의에서 사용한 예제)

// GlobalStyle.jsx
import { createGlobalStyle } from "styled-components";

const GlobalStyle = createGlobalStyle`
  body {
    font-family: "Helvetica", "Arial", sans-serif;
    line-height: 1.5;
  }
`;

export default GlobalStyle;

// App.jsx
import GlobalStyle from "./GlobalStyle";
import BlogPost from "./BlogPost";

function App() {
	const title = '전역 스타일링 제목입니다.';
	const contents = '전역 스타일링 내용입니다.';
  return (
    <>
      {/* 컴포넌트처럼 적용 */}
      <GlobalStyle />
      <BlogPost title={title} contents={contents} />
    </>
  );
}

export default App;

CSS reset

브라우저마다 각각 지원하는 기본 스타일이 있다. 예를 들면 <h1> 태그도 글씨를 조금 더 키우고 볼드 처리가 되듯이 이에 대한 스타일이 브라우저마다 하나씩 존재한다.

따라서 프로그래머가 원하는 방식으로만 스타일링을 하고 싶을 때, 완전히 새로운 스타일을 적용하고 싶을 때 css reset 코드를 적용 시켜줄 수 있다.

아래는 강의에서 사용한 예시 코드이며, 이는 구글링을 통해서도 다른 리셋 코드를 쉽게 찾아볼 수 있다.

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
	display: block;
}
body {
	line-height: 1;
}
ol, ul {
	list-style: none;
}
blockquote, q {
	quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
	content: '';
	content: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}

React Hooks

useState

React의 기본적인 hook으로 함수 컴포넌트에서 가변적인 상태를 가질 수 있도록 해주는 hook이다. 사용법은 구좀분해를 이용한다.

const [state, setState] = useState("초기값");

함수형 업데이트

setState() 안에서 직접 state+1처럼 업데이트 하는 것이 아닌 함수를 사용하여 {} 안에 로직을 넣을 수 있는 방식

// 함수형 업데이트 
setState(() => {});

기본적으로 React는 Batch update를 따르기 때문에 같은 상태에 대한 변경 사항이 각각 실행 되는 것이 아닌 한 번에 처리가 된다. 예를 들면 아래와 같은 코드는 setCount를 3번 사용함에도 브라우저에서는 한번만 실행된다

<button
onClick={() => {
  setNumber(number + 1); // 첫번째 줄 
  setNumber(number + 1); // 두번쨰 줄
  setNumber(number + 1); // 세번째 줄
}}
  >
    버튼
</button>

이를 3번 순차적으로 실행하고 싶을 경우 함수형 업데이트는 동시에 여러 명령을 내려도, 명령들이 모아져 순차적으로 1번씩 실행 되므로, 우리가 원하는 것처럼 3을 더할 수 있게 된다.

<button
onClick={() => {
  setNumber((previousState) => previousState + 1);
  setNumber((previousState) => previousState + 1);
  setNumber((previousState) => previousState + 1);
}}
  >
    버튼
</button>

Batch Update

배치 업데이트가 불편하게 보이지만 예를 들어 설명하면, 식당에서 여러 음식을 주문할 때 웨티어 새로운 메뉴를 받을때마다 주방에 가서 알려주는 것이 아닌, 손님의 모든 음식 주문을 받고 한 번에 주방에 전달하는 것이 효율적이다. 이를 따르는 것이 Batch Update이다.

useEffect

컴포넌트가 렌더링 된 후마다 특정 작업을 수행하는 hook
사용법은 아래의 예시 코드와 같다

useEffect(() => {
  // 이 부분이 실행된다.
  console.log("hello useEffect");
});

렌더링이 될때마다 실행된다고 생각하면 펀하다. 단, useEffect를 사용한 컴포넌트의 상태가 변할때마다 실행이 되서, 실행이 불필요하게 많아질 수 있다. 추가로 컴포넌트에서 다루는 상태가 2개, 3개 늘어날 수록 useEffect가 더 많이 실행된다. 이를 방지하는 것이 의존성 배열이다.

의존성 배열

특정 상태가 바뀔때만 useEffect를 실행할 수 있게끔 필요한 상태를 지정해줄 수 있다.

// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳 입니다.
useEffect(()=>{
	// 실행하고 싶은 함수
}, [의존성배열])

배열이 비어있는 경우 최초 렌더링 이후에만 (딱 1번) 실행된다.
배열에 상태가 존재하는 경우, 해당 상태가 변경이 되었을대만 실행된다.

추가로 Strict mode 개발 환경에서는 최초 렌더링시, 이가 2번 렌더링 될 수도 있다. 버그는 아니고 개발할때 Strict mode를 주석처리 후 개발을 이어가면 되며, 프로젝트 완성시 다시 주석처리를 취소해주면 된다.

+) clean up

컴포넌트가 unmount 될때, 즉 사라졌을때 동작하는 함수를 뜻한다. 이도 useEffect를 사용하여 구현할 수 있다.

useEffect(()=>{
  // 화면에 컴포넌트가 나타났을(mount) 때 실행하고자 하는 함수를 넣어주세요.

  return ()=>{
    // 화면에서 컴포넌트가 사라졌을(unmount) 때 실행하고자 하는 함수를 넣어주세요.
  }
}, [])

useRef

특정 값을 지정하기 위해 사용하는 hook으로 useState와 유사하지만 렌더링과 상관없이 값을 저장하는 차이가 있다. 즉, 렌더링이 돼도 값이 유지된다.

const ref = useRef("초기값");

그러면 ref는 {current: "초기값"}이라는 객체가 생성되며 key 값은 current이다.

용도

  1. 저장공간 : useState와 비슷하지만 렌더링 되어도 값이 유지 되기 때문에 렌더링을 발생시키지 않는 값을 저장할 때 유용
  2. DOM : 렌더링 되자마자 특정 input이 focusing 돼야 하는 등, DOM 요소를 핸들링해야 하는 순간이라면 useRef를 사용하는 것이 좋은 선택지가 될 수 있다.

DOM 접근 예제

import { useEffect, useRef } from "react";
import "./App.css";

function App() {
  const idRef = useRef("");

  // 렌더링이 될 때
  useEffect(() => {
    idRef.current.focus();
  }, []);

  return (
    <>
      <div>
        아이디 : <input type="text" ref={idRef} />
      </div>
      <div>
        비밀번호 : <input type="password" />
      </div>
    </>
  );
}

export default App;

위의 예시처럼 해당하는 DOM 요소로 접근할 수 있도록 ref를 지정해줄 수 있다. 이렇게 하면 새로고침시 포커스가 아이디 인풋으로 잡힌다.

useContext

부모 컴포넌트에서 자식 컴포넌트로 데이터를 전해줄 때 단방향으로 props를 통해 전달했다. 그러나 이 과정이 복잡해지고 깊이가 깊어지면 prop drilling 현상이 일어난다.

즉 prop이 어떤 컴포넌트로부터 전달받게 되었는지 파악이 어려워진다. 오류가 생길시 추적이 늦어지는 것이랑 같으므로 오류/버그 처리도 따라 느려지게 된다.

이를 보완하는 것이 context API이다. 그리고 이를 useContext를 통해 전역 데이터 관리를 할 수 있게 된다

context API

  • createContext : context 생성
  • useContext : context를 구독하고 해당 context의 현재값을 읽는다
  • Provider : context를 하위 컴포넌트에게 전달한다

React-router-dom

패키지 설명

페이지 이동을 구현할 수 있도록 해주는 패키로 여러 페이지를 가진 웹을 만들 수 있도록 도와준다.

설치는 아래 터미널에서 아래 코드를 실행

yarn add react-router-dom 

사용 순서는 아래와 같다

후에 여러 페이지 파일을 만들어 Router.js를 생성하고 route 코드를 작성해주면, 원하는 페이지 컴포넌트로 이동할 수 있게끔 만들어 줄 수 있다. 아래는 강의에서 사용한 에시 코드

import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
import Contact from "../pages/Contact";
import Works from "../pages/Works";

const Router = () => {
  return (
    <BrowserRouter>
      <Routes>
				{/* 
						Routes안에 이렇게 작성합니다. 
						Route에는 react-router-dom에서 지원하는 props들이 있습니다.

						path는 우리가 흔히 말하는 사용하고싶은 "주소"를 넣어주면 됩니다.
						element는 해당 주소로 이동했을 때 보여주고자 하는 컴포넌트를 넣어줍니다.
				 */}
        <Route path="/" element={<Home />} />
        <Route path="about" element={<About />} />
        <Route path="contact" element={<Contact />} />
        <Route path="works" element={<Works />} />
      </Routes>
    </BrowserRouter>
  );
};

export default Router;

후에 App.js에서 import만 해주면 사용할 수 있다.

react-router-dom hooks

router-dom hooks가 따로 있다고 한다... (머리 터질 예정...)
사용 방법과 코드는 모두 강의 예제로 쓰인 코드이다.

useNavigate

다른 페이지로 이동시 사용할 수 있는 Hook

// src/pages/home.js
import { useNavigate } from "react-router-dom";

const Home = () => {
  const navigate = useNavigate();

  return (
    <button
      onClick={() => {
        navigate("/works");
      }}
    >
      works로 이동
    </button>
  );
};

export default Home;

useLocation

현재의 위치를 알려주는 hook

// src/pages/works.js
import { useLocation } from "react-router-dom";

const Works = () => {
  const location = useLocation();
  console.log("location :>> ", location);
  return (
    <div>
      <div>{`현재 페이지 : ${location.pathname.slice(1)}`}</div>
    </div>
  );
};

export default Works;

Link는 API로써 브라우저에서 기본으로 제공하는 a 태그 대신 사용할 수 있다. a 태크를 사용하게 되면 페이지가 새로고침을 하게 되는데 그러면 다시 돌아왔을 때 모든 컴포넌트가 새로 렌더링 되어야하고 이전의 상태들도 초기화 되기 때문에 반드시 필요하다

import { Link, useLocation } from 'react-router-dom';

const Works = () => {
  const location = useLocation();
  console.log('location :>> ', location);
  return (
    <div>
      <div>{`현재 페이지 : ${location.pathname.slice(1)}`}</div>
      <Link to="/contact">contact 페이지로 이동하기</Link>
    </div>
  );
};

export default Works;

children

레이아웃을 만들때 유용하며 범용적인 박스 같은 느낌으로 생가하면 편하다.

즉 사진에서 보이는 것처럼 children 부분만 바뀌는 것

function Layout({ children }) {
  return (
    <div>
      <Header />
      <div style={{...layoutStyles}}>
        {children}
      </div>
      <Footer />
    </div>
  );
}

Dynamic Routes

동적 루트라고도 하며 path에 유동적인 값을 넣어서 특정 페이지로 이동이 하게끔 구현을 돕는다.

<Route path="works/:id" element={<Works />} />

이렇게 지정해주면 "works/1", "works/2", "works/3", 등 이런 식으로 아이디 값으로 이동해도 모두 works 페이지로 이동할 수 있도록 해준다.

useParams

이제 아이디로 이동한 패스 값을 조회 할 수 있도록 도와주는 것이 useParams이다.

const params = useParams();

const targetWork = data.find(function (work) {
  return work.id === Number(params.id);
});
works/1 works/4

중첩 라우팅 (Outlet)

children 말고도 사용할 수 있는 라우팅 방법. Route 안에 Route를 추가하는 것이 가능하다

<Route path="/about" element={<About />}>
  <Route path="mypage" element={<MyPage />} />
  <Route path="myimg" element={<MyImg />} />
</Route>

이렇게 하면 About페이지에서 MyPage와 MyImg 페이지에 About 페이지의 공통 요소가 포함 되어 있는 것처럼 나온다

현재 About page가 아래와 같이 생겼을 경우

import { Outlet, useLocation, useNavigate } from "react-router-dom";

function About() {
  const location = useLocation();
  const navigate = useNavigate();
  return (
    <div>
      <header>
        <h1>About</h1>
        <p>현재 페이지 path : {location.pathname.slice(1)}</p>
      </header>

      <Outlet />

      <footer>
        <button
          onClick={() => {
            navigate("/about/mypage");
          }}
        >
          My Page로 이동
        </button>
        <button
          onClick={() => {
            navigate("/about/myimg");
          }}
        >
          My Img로 이동
        </button>
      </footer>
    </div>
  );
}

export default About;

여기서 Outlet을 사용하고 있기 때문에 children처럼 about/mypage 와 about/myimg로 라우팅이 가능하다

about about/mypage about/myimg

마치며...

식곤증이 심해지는 계절인것 같다

profile
코딩하는 감자

0개의 댓글