SASS, SCSS
css 코드의 재활용성과 코드 가독성을 높여 유지보수가 좋은 CSS 전처리기 중 하나
sass : ;
(세미콜론)과 {}
를 사용하지 않고 tab
을 이용해 스타일 정의
scss : 기존의 css파일과 비슷한 문법 사용
sass와 scss 모두 변수 선언 시 $
사용
npm i node-sass
// SassComponent.js
import './SassComponent.scss'
export default function SassComponent() {
return <div className="SassComponent">
<div className="box red"></div>
<div className="box orange"></div>
<div className="box yellow"></div>
<div className="box green"></div>
<div className="box blue"></div>
<div className="box navy"></div>
<div className="box purple"></div>
</div>
}
//SassComponent.scss
@import './utils.scss';
.SassComponent {
display: flex;
//중첩
.box {
@include box(100px);
//width: 100px;
//height: 100px;
border: 1px solid black;
//& : 중첩 안에서 키워드는 부모 선택자를 참조하여 치환
// 하나의 태그에 여러 클래스 같이 존재할 때
&.red {
background-color: $color-red;
}
&.orange {
background-color: $color-orange;
}
&.yellow {
background-color: $color-yellow
}
&.green {
background-color: $color-green;
}
&.blue {
background-color: $color-blue;
}
&.navy {
background-color : $color-navy;
}
&.purple {
background-color: $color-purple;
}
}
.btn {
padding : 10px;
margin : 10px;
border : 1px solid black;
}
.btn-primary {
@extend .btn;
background-color : blue;
}
//_utils.scss
$color-red : #ff0000;
$color-orange : orange;
$color-yellow : yellow;
$color-green : green;
$color-blue : blue;
$color-navy : navy;
$color-purple : #b734b7;
@mixin box($size) {
width: $size;
height: $size;
}
특정 태그 내부에 있는 태그의 css에 접근할 때, 중첩 사용
SCSS 파일명 앞에 _
붙이면 파일단위로 분리되어 컴파일되지 않음, 보통의 경우 css 변수나 코드블록을 미리 정의해놓고 사용(@import
로 정의된 파일 가져다 사용)
@mixin
으로 미리 css 코드블록을 정의 가능, 정의한 코드블록을 사용할 때 @include
사용
@extend
로 다른 css 선택자의 스타일을 그대로 가져다 사용 가능
Styled Component
css를 외부 파일에 의존하지 않고, 컴포넌트 내부에 직접 넣어 사용
각각의 컴포넌트 안에 css가 존재하기 때문에 스타일이 전역적으로 중첩되지 않음
npm i styled-components
(VSCode IDE 사용 시 vscode-styled-components
플러그인 설치)
import styled from 'styled-components';
const _Box = styled.div`
background-color: blue;
width: 100px;
height: 100px;
`
const _Box2 = styled.div`
background-color: ${props => props.color};
width: 200px;
height: 200px;
`
const _Circle = styled(_Box2)`
border-radius: 100px;
`
const _Btn = styled.button`
background-color: blue;
color: pink;
padding: 10px 15px;
border-radius: 4px;
`
const _Input = styled.input.attrs({ required : true })`
`
const _Box3 = styled.div`
width: 200px;
height: 100px;
background-color: aqua;
line-height: 100px;
text-align: center;
${_Input} {
background-color: purple;
}
.ptag {
color : indianred;
font-weight: bold;
&:hover {
font-size: 32px;
}
}
`
export default function StyledComponent() {
return (
<div>
<_Box3>
<p className="ptag">Hello Styled</p>
<_Input></_Input>
</_Box3>
<_Box></_Box>
<_Box2 color="green" as="header"></_Box2>
<_Circle color="red"></_Circle>
<_Btn as="a">클릭</_Btn>
<_Input></_Input>
</div>
)
}
import styled from 'styled-components'
로 스타일 컴포넌트 사용 준비
styled.태그명
에 백틱을 사용, 백틱 안에 스타일 정의
${}
로 props 이용해 스타일 정의 가능
styled(다른 컴포넌트명)
으로 다른 스타일 컴포넌트의 스타일을 상속 가능
styled.태그명.attrs()
로 태그의 속성값 접근, 정의 가능
중첩 css 사용 시, ${}
로 해당 컴포넌트 내부의 다른 컴포넌트 css를 지정하거나, 일반 css 문법으로 내부의 태그 css 작성 가능
기존에 작성한 컴포넌트 태그를 as
를 이용해 태그를 바꾸어 사용 가능
React Router
요청된 url에 따라 다른 컴포넌트를 보여주기 위해 사용하는 라이브러리
//버전 6 지정 설치
npm i react-router-dom@6
HTML5를 지원하는 브라우저의 주소 감지, 새로고침을 하지 않아도 새로운 컴포넌트를 렌더링
BrowserRouter
는 바뀌는 부분의 최상단에 위치해야 함
요청한 url 경로에 따라 일치하는 컴포넌트를 렌더링해주도록 하는 역할
Route
로 구체적인 컴포넌트 렌더링 결정
기존 HTML의 a
태그의 새로고침 렌더링과 달리, Link
는 컴포넌트 페이지 전환을 방지하며 경로를 변경
import {BrowserRouter, Routes, Route} from "react-router-dom";
import Header from "./Components/Header";
import Home from "./Components/Home";
import About from "./Components/About";
import NotFound from "./Components/NotFound";
import User from "./Components/User";
import Redirect from "./Components/Redirect";
import UserDetail from "./Components/UserDetail";
import App from "./App";
import Error from "./Components/Error";
import Comment from "./Components/Comment";
export default function Router() {
return (
<>
<BrowserRouter>
<Header></Header>
<Routes>
<Route path="/" element={<Home/>}></Route>
<Route path="/about" element={<About/>}></Route>
<Route path="/user" element={<User/>}></Route>
<Route path="/user/:id" element={<UserDetail/>}></Route>
<Route path="/redirect" element={<Redirect/>}></Route>
<Route path="*" element={<NotFound/>}></Route>
</Routes>
</BrowserRouter>
</>
)
}
Route
의 path
속성으로 url 경로 지정, element
로 렌더링할 컴포넌트 입력
Link
는 to
속성으로 url 경로 지정
지정한 url 이외에 다른 url 에러 요청 처리시 path
에 *
사용
react-router v6.4 이후부터 제공하는 컴포넌트 렌더링 라우터
부모 라우트 컴포넌트 내에서 사용되어, 부모 라우트의 url과 일치하는 자식 라우트 컴포넌트를 렌더링
context
속성을 통해 자식 라우트에 데이터 전송 가능, 자식 라우트에서는 useOutletContext()
로 접근 가능
url 경로에 :
로 설정해 요청시 받는 파라미터를 컴포넌트 내부에서 가져옴
url 경로에 쿼리스트링 형식으로 요청을 받는 API에 대해 받는 파라미터를 컴포넌트 내부에서 사용
Link
컴포넌트를 사용하지 않고 다른 페이지로 이동해야 하는 경우 사용
//PracRouter.js
import {createBrowserRouter} from "react-router-dom";
import Prac from "./App";
import NameKdt from "./PracComponents/NameKdt";
import New from "./PracComponents/New";
const router = createBrowserRouter([
{
path : '/',
element : <Prac/>,
},
{
path : 'student',
children : [
{
path : ':value',
element : <NameKdt/>,
},
{
path : 'new',
element : <New/>,
}
]
}
]);
export default router;
//index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import {RouterProvider} from "react-router-dom";
import Router from "./Router";
import PracRouter from "./PracRouter";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// <App />
<RouterProvider router={PracRouter}></RouterProvider>
);
//NameKdt.js
import {useNavigate, useParams, useSearchParams} from "react-router-dom";
import {useEffect} from "react";
export default function NameKdt() {
const { value } = useParams();
const navigate = useNavigate();
const goBack = () => {
navigate(-1);
}
return (
<>
<div>학생의 이름은 <span style={{ color : 'green' }}>{value}</span>입니다.</div>
<button onClick={goBack}>이전페이지로</button>
</>
)
}
//New.js
import {useNavigate, useSearchParams} from "react-router-dom";
export default function New() {
const [searchParams, setSearchParams] = useSearchParams();
console.log(searchParams.get('name'));
const navigate = useNavigate();
const goBack = () => {
navigate(-1);
}
return (
<>
<div>학생의 이름은 <span style={{ color : 'green' }}>new</span>입니다.</div>
<div>실제 이름은 <span style={{ color : 'red' }}>{searchParams.get('name')}</span></div>
<button onClick={goBack}>이전페이지로</button>
</>
)
}
CreateBrowserRouter
사용 시, BrowserRouter
로 감싸지 않고, index.js
에서 RouterProvider
를 사용해 라우터 제공, router
속성으로 라우터 가져와 사용
createBrowserRouter()
함수 사용하여 라우터 생성, path
는 요청할 url 을 적고, element
로 요청 url에 렌더링할 컴포넌트 추가
children
속성으로 url 중첩 라우팅 사용 가능, 이외에 errorElement
속성으로 잘못된 url 접근에 대한 컴포넌트 렌더링 가능
NameKdt
컴포넌트의 path
에 :
사용해 파라미터를 받으므로, 해당 컴포넌트에서 useParams()
사용해 값을 가져옴
navigate
사용 시, 변수에 useNavigate()
로 정의해 해당 변수 사용(-1 = 이전 페이지, 문자열로 직접 url 지정 가능)
New
컴포넌트에 대한 요청을 받을 때 쿼리스트링 방식 사용, useSearchParams()
로 값을 가져옴, 배열 값 중 setSearchParams
로 직접 파라미터 지정도 가능
searchParams.get()
함수로 요청 시 받은 파라미터 접근 가능
React Hook Form
React에서의 Form의 상태관리와 유효성 검사를 지원
npm i react-hook-form
import {useForm} from "react-hook-form";
export default function Form() {
const { register, handleSubmit, watch, formState : {errors} } = useForm();
const onValid = (data) => {
console.log(data);
};
// const onInvalid = (error) => {
// console.log('err', error);
// };
// console.log(watch('password'));
console.log('errors', errors);
return (
<>
<form onSubmit={handleSubmit(onValid)}>
<input type="text" {...register('username', {
required : '이름을 입력하세요',
minLength : {
message : '최소 2글자 이상', value : 2
}
})}/>
<div>{errors.username?.message}</div>
<input type="text" {...register('email', {
required : '이메일을 입력하세요',
validate : {
useGmail : (value)=> {
return value.includes('gmail.com') || 'gmail로만 가입 가능합니다.';
}
}
})}/>
<div>{errors.email?.message}</div>
<input type="text" {...register('password')}/>
<button type="submit">Submit</button>
</form>
</>
);
}
useForm()
함수로 react hook form 요소들을 가져와 사용
register
로 입력 요소에 연결, 유효성 검사 설정
폼 제출 시 handleSubmit
으로 처리, 두개의 콜백함수가 인자로 오는데, 첫번째 함수는 유효성 검사가 통과했을 때 실행되는 함수로 필수 작성해야 하고, 두번째 함수는 유효성 검사가 실패했을때 실행하는 함수로 선택적으로 작성 가능
watch
로 폼 필드의 값을 실시간으로 관찰 가능
formState
는 객체형식으로, 폼의 상태를 나타내는 여러 상태들을 가지고 있음(errors
, isValid
, isDirty
, isSubmitted
등)
import {useForm} from "react-hook-form";
export default function PracComponent() {
const { register, handleSubmit, watch, formState : {errors} } = useForm();
const onValid = (data) => {
console.log(data);
}
return (
<>
<form onSubmit={handleSubmit(onValid)}>
<input type="text" {...register('name', {
required : '이름은 필수항목입니다.'
})}/>
<div>{errors.name?.message}</div>
<input type="number" {...register('age', {
min : {
message : '0 이상의 숫자만 입력가능합니다.',
value : 0
}
})}/>
<div>{errors.age?.message}</div>
<button type="submit">제출</button>
</form>
</>
);
}
register()
함수의 첫번째 인자는 태그 요소의 이름, 두번째 인자는 객체형식으로 required
, min
, maxLength
등 여러 조건 설정 가능
조건 내부에 message
속성으로 유효성 검사 실패 시 나타날 문구와 같이 작성 가능
validate
속성으로 커스텀 유효성 검사 작성 가능