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;
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;
브라우저마다 각각 지원하는 기본 스타일이 있다. 예를 들면 <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의 기본적인 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이다.
컴포넌트가 렌더링 된 후마다 특정 작업을 수행하는 hook
사용법은 아래의 예시 코드와 같다
useEffect(() => {
// 이 부분이 실행된다.
console.log("hello useEffect");
});
렌더링이 될때마다 실행된다고 생각하면 펀하다. 단, useEffect를 사용한 컴포넌트의 상태가 변할때마다 실행이 되서, 실행이 불필요하게 많아질 수 있다. 추가로 컴포넌트에서 다루는 상태가 2개, 3개 늘어날 수록 useEffect가 더 많이 실행된다. 이를 방지하는 것이 의존성 배열이다.
특정 상태가 바뀔때만 useEffect를 실행할 수 있게끔 필요한 상태를 지정해줄 수 있다.
// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳 입니다.
useEffect(()=>{
// 실행하고 싶은 함수
}, [의존성배열])
배열이 비어있는 경우 최초 렌더링 이후에만 (딱 1번) 실행된다.
배열에 상태가 존재하는 경우, 해당 상태가 변경이 되었을대만 실행된다.
추가로 Strict mode 개발 환경에서는 최초 렌더링시, 이가 2번 렌더링 될 수도 있다. 버그는 아니고 개발할때 Strict mode를 주석처리 후 개발을 이어가면 되며, 프로젝트 완성시 다시 주석처리를 취소해주면 된다.
컴포넌트가 unmount 될때, 즉 사라졌을때 동작하는 함수를 뜻한다. 이도 useEffect를 사용하여 구현할 수 있다.
useEffect(()=>{
// 화면에 컴포넌트가 나타났을(mount) 때 실행하고자 하는 함수를 넣어주세요.
return ()=>{
// 화면에서 컴포넌트가 사라졌을(unmount) 때 실행하고자 하는 함수를 넣어주세요.
}
}, [])
특정 값을 지정하기 위해 사용하는 hook으로 useState와 유사하지만 렌더링과 상관없이 값을 저장하는 차이가 있다. 즉, 렌더링이 돼도 값이 유지된다.
const ref = useRef("초기값");
그러면 ref는 {current: "초기값"}이라는 객체가 생성되며 key 값은 current이다.
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를 지정해줄 수 있다. 이렇게 하면 새로고침시 포커스가 아이디 인풋으로 잡힌다.
부모 컴포넌트에서 자식 컴포넌트로 데이터를 전해줄 때 단방향으로 props를 통해 전달했다. 그러나 이 과정이 복잡해지고 깊이가 깊어지면 prop drilling 현상이 일어난다.

즉 prop이 어떤 컴포넌트로부터 전달받게 되었는지 파악이 어려워진다. 오류가 생길시 추적이 늦어지는 것이랑 같으므로 오류/버그 처리도 따라 느려지게 된다.
이를 보완하는 것이 context API이다. 그리고 이를 useContext를 통해 전역 데이터 관리를 할 수 있게 된다

페이지 이동을 구현할 수 있도록 해주는 패키로 여러 페이지를 가진 웹을 만들 수 있도록 도와준다.
설치는 아래 터미널에서 아래 코드를 실행
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만 해주면 사용할 수 있다.
router-dom hooks가 따로 있다고 한다... (머리 터질 예정...)
사용 방법과 코드는 모두 강의 예제로 쓰인 코드이다.
다른 페이지로 이동시 사용할 수 있는 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;
현재의 위치를 알려주는 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 부분만 바뀌는 것
function Layout({ children }) {
return (
<div>
<Header />
<div style={{...layoutStyles}}>
{children}
</div>
<Footer />
</div>
);
}
동적 루트라고도 하며 path에 유동적인 값을 넣어서 특정 페이지로 이동이 하게끔 구현을 돕는다.
<Route path="works/:id" element={<Works />} />
이렇게 지정해주면 "works/1", "works/2", "works/3", 등 이런 식으로 아이디 값으로 이동해도 모두 works 페이지로 이동할 수 있도록 해준다.
이제 아이디로 이동한 패스 값을 조회 할 수 있도록 도와주는 것이 useParams이다.
const params = useParams();
const targetWork = data.find(function (work) {
return work.id === Number(params.id);
});
works/1 | works/4 |
|---|
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 |
|---|
식곤증이 심해지는 계절인것 같다