[ Styled Components ]
▶ css-in-js
JS 코드로 CSS 코드를 작성하여 컴포넌트를 꾸미는 방식
순수한 CSS 코드가 아니라 JS를 이용해서 CSS코드를 만들어내는 것
▶ styled-components
styled-components는 리액트에서 CSS-in-JS 방식으로 컴포넌트를 꾸밀 수 있게 도와주는 패키지
다양한 패키지가 있지만 styled-components 예전부터 꾸준히 많은 개발자들에게 선택받은 패키지
패키지란?
React에는 없는 기능이지만 우리가 추가로 가져와서 사용할 수 있는 써드파티 프로그램
패키지들은 누군가에 의해 만들어진 것으로 npm에 모여 있음
우리는 사용하고자 하는 패키지를 npm install 또는 yarn add 를 통해서 설치해서 사용할 수 있음
▶ styled-components 준비하기
1. VScode 플러그인 설치
플러그인 설치해야만 styled-components 안에서 스타일 코드를 편하게 작성할 수 있음
2. yarn에서 styled-components 설치하기
터미널에서 패키지 설치
yarn add styled-components
▶ styled-components 사용하기
기본적인 사용법 알아보기
styled. 뒤에는 html의 태그가 옴. ex) div ➡️ styled.div
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;
조건부 스타일링 구현해보기
classname을 사용해서 구현하기는 조금 까다로운 조건부 스타일링을 styled-components를 이용하면 간편하게 할 수 있음. CSS-in-JS 방식의 강점이 바로 스타일 코드를 JS코드 작성하듯이 스타일 코드를 작성할 수 있다는 점임
styled-components도 말 그대로 컴포넌트임. 즉, prosp를 통해서 정보를 전달해줄 수 있다는 말
import React from "react";
import styled from "styled-components";
const StBox = styled.div`
width: 100px;
height: 100px;
border: 1px solid ${(props) => props.borderColor}; // 부모 컴포넌트에서 보낸 props를 받아 사용
margin: 20px;
`;
const App = () => {
return (
<div>
{/* props를 통해 borderColor라는 값을 전달 */}
<StBox borderColor="red">빨간 박스</StBox>
<StBox borderColor="green">초록 박스</StBox>
<StBox borderColor="blue">파랑 박스</StBox>
</div>
);
};
export default App;
- styled-component를 생성함
- styled-component를 부모 컴포넌트에서 사용함
- 원하는 값을 props로 자식 컴포넌트(styled-component)로 전달
- 부모 컴포넌트에서 보낸 props를 받음
- JS코드를 사용하기 위해 ${ }를 열어줌(템플릿 리터럴 → 백틱안에 JS를 쓰기 위한)
- 비어있는 화살표 함수를 열어줌 ${( ) => { }}
- 함수의 인자에서 props를 받아오고 props 안에는 부모 컴포넌트에서 보낸것을 return 함
- ${(props)⇒{ return props.borderColor }} 리턴하면 (props)⇒{ return props.borderColor }의 값이 === 'red'가 됨. 브라우저에서는 border: 1px solid red라는 코드로 인식하여 스타일링 되는 것
위에 예시를 switcp문과 map을 사용하여 리팩토링 해보기
// src/App.js
import React from "react";
import styled from "styled-components";
const StContainer = styled.div`
display: flex;
`;
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 (
<StContainer>
{/* map을 이용해서 StBox를 반복하여 화면에 그림 */}
{boxList.map((box) => (
<StBox borderColor={box}>{getBoxName(box)}</StBox>
))}
</StContainer>
);
};
export default App;
▶ 정리
- CSS-in-JS란, 자바스크립트로 CSS코드를 작성하는 방식을 말함
- props를 통해서 부모 컴포넌트로부터 값을 전달받고 조건문을 이용해서 조건부 스타일링을 할 수 있음
[ GlobalStyles, Sass 소개, css reset ]
▶ GlobalStyle(전역 스타일링)
전역 스타일링
프로젝트를 아우르는 공통적으로 들어가야 할 스타일을 적용해야 할 필요도 있음
그럴 경우 ‘전역적으로(globally)’ 스타일을 지정한다라고 표현함. 그럴 때 적용하는 방법이 바로 전역 스타일링
컴포넌트 단위 스타일링
import styled from "styled-components";
function TestPage(props) {
return (
<Wrapper>
<Title>{props.title}</Title>
<Contents>{props.contents}</Contents>
</Wrapper>
);
}
const Title = styled.h1`
font-family: "Helvetica", "Arial", sans-serif;
line-height: 1.5;
font-size: 1.5rem;
margin: 0;
margin-bottom: 8px;
`;
const Contents = styled.p`
margin: 0;
font-family: "Helvetica", "Arial", sans-serif;
line-height: 1.5;
font-size: 1rem;
`;
const Wrapper = styled.div`
border: 1px solid black;
border-radius: 8px;
padding: 20px;
margin: 16px auto;
max-width: 400px;
`;
export default TestPage;
규모가 좀 큰 프로젝트라고 한다면 공통적으로 적용되는 스타일링 부분은 빼줄 필요가 있음
예시의 경우 글꼴(font)와 line-height를 공통 요소라 가정하고 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;
▶ Sass 소개, Nesting
Sass(Syntactically Awesome Style Sheets)
CSS를 전통적인 방법보다 효율적으로 사용하기 위해 만들어진 언어
CSS는 웹 프로젝트 규모가 커지면 커질수록 코드가 복잡해지고 유지보수도 불편해짐
계속해서 동일한 코드를 복사하고 붙여넣는 과정을 반복해야 하기 때문에 Human Error를 줄이려는 노력은 중요함
그래서 코드의 재사용성을 높이고 가독성 또한 향상시켜줄 수 있는 방법이 바로 Sass
Sass 특징 및 예시 소개
- 변수를 사용할 수 있음
$color: #4287f5;
$borderRadius: 10rem;
div {
background: $color;
border-radius: $borderRadius;
}
- 중첩할 수 있음(Nesting)
label {
padding: 3% 0px;
width: 100%;
cursor: pointer;
color: $colorPoint;
&:hover {
color: white;
background-color: $color;
}
}
- 다른 style 파일을 import 할 수 있음
// colors
$color1: #ed5b46;
$color2: #f26671;
$color3: #f28585;
$color4: #feac97;
//style.scss
@import "common.scss";
.box {
background-color: $color3;
}
▶ css reset
css reset
브라우저는 기본적으로 default style을 제공함. 다양한 웹브라우저들은 저마다 조금씩은 다른 default style을 제공하고 있기 때문에 이를 초기화하고 정하는대로만 표현되는 것이 중요함
CSS 초기화하기
초기화하는 방법은 여러가지가 있지만 그 중 코드를 사용하는 방법이 있음
//reset.css
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;
}
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./reset.css" />
</head>
<body>
<span>Default Style을 테스트</span>
<h1>이건 h1 태그</h1>
<p>이건 p 태그</p>
</body>
</html>
[ React Hooks - useState ]
▶ useState 복습
정의
useState는 가장 기본적인 hook이며 함수 컴포넌트에서 가변적인 상태를 가지게 해줌
복습
const [state, setState] = useState(initialState);
useState 라는 함수가 배열을 반환하고 이것을 구조 분해 문법으로 꺼내놓은 모습일 뿐임
state를 변수로 사용했고 setState를 이용해서 state 값을 수정할 수 있었음
그리고 만약 state가 원시 데이터타입이 아닌 객체 데이터 타입인 경우에는 불변성을 유지해주어야 함
▶ 함수형 업데이트
함수형 업데이트
setState를 사용하는 방식에는 우리가 알고 있는 방식이 아닌 또 다른 방식이 있음. 바로 함수형 업데이트 방식
// 기존에 우리가 사용하던 방식
setState(number + 1);
// 함수형 업데이트
setState(() => {});
setState의 ( )안에 수정할 값이 아니라 함수를 넣을 수 있음
함수의 인자에서는 현재의 state을 가져올 수 있고 { } 안에서는 이 값을 변경하는 코드를 작성할 수 있음
// 현재 number의 값을 가져와서 그 값에 +1을 더하여 반환한 것
setState((currentNumber)=>{ return currentNumber + 1 });
두 방식의 차이점
일반 업데이트 방식
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0);
return (
<div>
{/* 버튼을 누르면 1씩 플러스됨*/}
<div>{number}</div>
<button
onClick={() => {
setNumber(number + 1); // 첫번째 줄
setNumber(number + 1); // 두번쨰 줄
setNumber(number + 1); // 세번째 줄
}}
>
버튼
</button>
</div>
);
}
export default App;
setNumber을 세번 호출했지만 number이 1씩 증가
함수 업데이트 방식
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0);
return (
<div>
{/* 버튼을 누르면 3씩 플러스*/}
<div>{number}</div>
<button
onClick={() => {
setNumber((previousState) => previousState + 1);
setNumber((previousState) => previousState + 1);
setNumber((previousState) => previousState + 1);
}}
>
버튼
</button>
</div>
);
}
export default App;
setNumber을 세번 호출했지만 number이 1씩 증가
일반 업데이트 방식은 버튼을 클릭했을 때 첫번째 줄 ~ 세번째 줄의 있는 setNumber가 각각 실행되는 것이 아니라 배치(batch)로 처리함. 즉 onClick을 했을 때 setNumber 라는 명령을 세번 내리지만 리액트는 그 명령을 하나로 모아 최종적으로 한번만 실행을 시킴. 그래서 setNumber을 3번 명령하던 100번 명령하던 1번만 실행됨
함수형 업데이트 방식은 3번을 동시에 명령을 내리면 그 명령을 모아 순차적으로 각각 1번씩 실행시킴
0에 1더하고 그 다음 1에 1을 더하고, 2에 1을 더해서 3이라는 결과가 나옴
▶ 리액트팀이 일반 업데이트 방식과 함수형 업데이트 방식을 나눠서 설계한 이유
공식문서의 설명
📢 리액트는 성능을 위해 setState()를 단일 업데이트(batch update)로 한꺼번에 처리할 수 있습니다.
불필요한 리-렌더링을 방지(렌더링 최적화)하기 위해 즉, 리액트의 성능을 위해 한꺼번에 state를 업데이트 한다는 것
쉽게 생각하면 왔다갔다 왔다갔다 하는것이 아닌 다 듣고 한번에 가는것
▶ 정리
useState의 업데이트 방식은 2가지 방식이 있으며 각각 다르게 동작
useState 로 원시데이터가 아닌 데이터를 변경할때는 불변성을 유지해야 함
[ React Hooks - useEffect ]
▶ useEffect
useEffect 사용
useEffect는 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook
쉽게 말해 어떤 컴포넌트가 화면에 보여졌을 때 내가 무언가를 실행하고 싶다거나 어떤 컴포넌트가 화면에서 사라졌을 때 무언가를 실행하고 싶다면 useEffect를 사용하면 됨. React에서 제공하는 훅(기능) 이므로 import 해서 사용
코드로 보는 useEffect 기초
브라우저에서 컴포넌트를 눈으로 보는 순간 즉 '컴포넌트가 렌더링 될 때 실행된다'라는 것이 useEffect 핵심 기능
import React, { useEffect } from "react";
const App = () => {
useEffect(() => {
// App컴포넌트가 화면에 렌더링 될때 이 부분이 실행됨
console.log("hello useEffect");
});
return <div>Home</div>;
}
export default App;
useEffect와 리렌더링(re-rendering)
useEffect의 특징에 의해 우리가 의도치않은 동작을 경험할수도 있음
import React, { useEffect, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
useEffect(() => {
console.log("hello useEffect");
});
return (
<div>
<input
type="text"
value={value}
onChange={(event) => {
setValue(event.target.value);
}}
/>
</div>
);
}
export default App;
input이 있고 value 라는 state를 생성하여 input과 연결. 브라우저에 input에 어떤 값을 입력하면 useEffect가 계속 실행되는 것을 볼 수 있음. 여기서 input값을 입력한것뿐인데 useEffect가 계속 실행되는 점을 유의해야함
전체흐름을 보면
- input에 값을 입력
- value, 즉 state가 변경됨
- state가 변경되었기 때문에 App 컴포넌트가 리렌더링 됨
- 리렌더링이 되었기 때문에 useEffect가 다시 실행됨
- 1번 → 5번 과정이 계속 순환함
▶ 의존성 배열
의존성 배열(dependency array)
useEffect에는 의존성 배열이라는 것이 있음
쉽게 풀어 얘기하면 “이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행할게” 라는 것
// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳
useEffect(()=>{
// 실행하고 싶은 함수
}, [의존성배열])
- 의존성 배열이 없을때
- 렌더링될때마다 useEffect 실행
- 의존성 배열이 빈 배열인 경우
- 처음에 딱 한번만 useEffect 실행
- 의존성 배열에 값이 있는 경우
- 배열에 들어있는 값(value)가 변경될때마다 useEffect 실행
▶ clean up
클린업
컴포넌트가 사라졌을 때 useEffect가 실행되는 과정을 클린 업(clean up)이라고 표현
클린업 하는 방법
useEffect 안에서 return을 해주고 이 부분에 실행되길 원하는 함수를 넣으면 됨
useEffect(()=>{
// 화면에 컴포넌트가 나타났을(mount) 때 실행하고자 하는 함수
return ()=>{
// 화면에서 컴포넌트가 사라졌을(unmount) 때 실행하고자 하는 함수
}
}, [])
클린업 활용해보기
import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";
const 속세 = () => {
const nav = useNavigate();
useEffect(() => {
return () => {
console.log(
"안녕히 계세요 여러분! 전 이 세상의 모든 굴레와 속박을 벗어 던지고 제 행복을 찾아 떠납니다! 여러분도 행복하세요~~!"
);
};
}, []);
return (
<button
onClick={() => {
nav("/todos");
}}
>
속세를 벗어나는 버튼
</button>
);
};
export default 속세;
속세를 벗어나는 버튼을 만들었고 버튼을 누르면 useNavigate에 의해서 /todos로 이동하면서 속세 컴포넌트를 떠날 것임그러면서 화면에서 속세 컴포넌트가 사라질 것이고 useEffect의 return 부분이 실행될 것
▶ 정리
- useEffect는 화면에 컴포넌트가 mount 또는 unmount 됐을 때 실행하고자 하는 함수를 제어하게 해주는 훅
- 의존성 배열을 통해 함수의 실행 조건을 제어할 수 있음
- useEffect에서 함수를 1번만 실행시키고자 할때는 의존성 배열을 빈 배열로 둠
[ React Hooks - useRef ]
▶ useRef
useRef hook
DOM 요소에 접근할 수 있도록 하는 React Hook
// HTML과 JS에서 특정 DOM 선택할때
// getElementById 이용
const divTag = document.getElementById('#myDiv');
// querySelector 이용
const divTag2 = document.querySelector('#myDiv');
리액트에서도 DOM을 선택해야 할 상황이 생기기 마련. 예를 들면 화면이 렌더링 되자마자 특정 input태그가 focusing이 돼야 하는 경우 등. 그럴 경우에 useRef hook을 사용
사용방법
import "./App.css";
import { useRef } from "react";
function App() {
const ref = useRef("초기값");
console.log("ref", ref);
return (
<div>
<p>useRef에 대한 이야기에요.</p>
</div>
);
}
export default App;
변경도 가능함
import "./App.css";
import { useRef } from "react";
function App() {
const ref = useRef("초기값");
console.log("ref 1", ref);
ref.current = "바꾼 값";
console.log("ref 1", ref);
return (
<div>
<p>useRef에 대한 이야기</p>
</div>
);
}
export default App;
설정된 ref 값은 컴포넌트가 계속해서 렌더링 되어도 unmount 전까지 값을 유지함
이러한 특징 때문에 useRef는 다음 2가지 용도로 사용됨
- 저장공간
- state와 비슷한 역할을 함. 다만 state는 변화가 일어나면 다시 렌더링이 되어 내부 변수들은 초기화가 됨
- ref에 저장한 값은 렌더링을 일으키지 않음. 즉, ref의 값 변화가 일어나도 렌더링으로 인해 내부 변수들이 초기화 되는 것을 막을 수 있음(컴포넌트가 10번 렌더링되도 ref에 저장한 값은 유지됨)
- 정리하면
- state는 리렌더링이 꼭 필요한 값을 다룰 때 쓰면 됨
- ref는 리렌더링을 발생시키지 않는 값을 저장할 때 사용
- DOM
- 렌더링 되자마자 특정 input이 focusing 돼야 한다면 useRef를 사용하면 됨
▶ 예제 코드로 특징 살펴보기
state와 ref의 차이점
import "./App.css";
import { useRef, useState } from "react";
function App() {
const [count, setCount] = useState(0);
const countRef = useRef(0);
const plusStateCountButtonHandler = () => {
setCount(count + 1);
};
const plusRefCountButtonHandler = () => {
countRef.current++;
};
return (
<>
<div>
state 영역입니다. {count} <br />
<button onClick={plusStateCountButtonHandler}>state 증가</button>
</div>
<div>
ref 영역입니다. {countRef.current} <br />
<button onClick={plusRefCountButtonHandler}>ref 증가</button>
</div>
</>
);
}
export default App;
내부 변수를 let 키워드로 선언해서 변수 사용하면 안되는 이유
let 키워드를 사용하게 되면 렌더링 시 다시 변수가 초기화 됨
리렌더링이 된다 = 함수가 다시 호출된다 = 내부 변수가 다시초기화된다
DOM 접근
<input /> 태그에는 ref라는 속성이 있음. 이걸 통해 해당 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;
화면이 렌더링 되자마자 바로 focus가 들어감
이런점을 이용하여 활용한 예시
아이디어가 10자리 입력되면 자동으로 비밀번호 필드로 이동됨
import { useEffect, useRef, useState } from "react";
import "./App.css";
function App() {
const idRef = useRef("");
const pwRef = useRef("");
const [id, setId] = useState("");
const onIdChangeHandler = (event) => {
setId(event.target.value);
};
// 렌더링이 될 때
useEffect(() => {
idRef.current.focus();
}, []);
useEffect(() => {
if (id.length >= 10) {
pwRef.current.focus();
}
}, [id]);
return (
<>
<div>
아이디 :
<input
type="text"
ref={idRef}
value={id}
onChange={onIdChangeHandler}
/>
</div>
<div>
비밀번호 : <input type="password" ref={pwRef} />
</div>
</>
);
}
export default App;
'항해99' 카테고리의 다른 글
리액트 숙련주차 3 (0) | 2023.06.27 |
---|---|
리액트 숙련주차 2 (1) | 2023.06.27 |
리액트 입문주차 3 (0) | 2023.06.23 |
리액트 입문주차 2 (0) | 2023.06.23 |
리액트 입문주차 1 (0) | 2023.06.23 |
댓글