[코딩온] 프론트엔드 입문 Day 45 (React 파라미터&쿼리, styling)
오늘은 React에서 파라미터와 쿼리를 다루는 법, 스타일링하는 법, SASS를 배웠다!👏
사실 이번 교육과정 두번째 파트에서 JS 학습 초기에, 리더님이 $ 기호에 대해 설명하며 "React를 배울 때 SASS도 배우게 될 예정"이라고 하셔서 SASS가 뭔지 궁금했었는데, 직접 배워보니 굉장히 편리한 CSS 라이브러리였다.
오늘 배운 내용을 여기에 전부 적으면 글이 너무 길어질 것 같아서, 일단 파라미터&쿼리와 스타일링부터 정리해보겠다.

React - Parameter & Query
파라미터(Parameter)는 주소 뒤에 슬래시로 구분되는 부분, 쿼리(Query)는 주소 뒤에 물음표부터 시작되는 부분이다. JS의 DOM 요소에 대해 배우던 당시에 쿼리스트링도 배웠기 때문에, 둘의 차이에 대해서는 이해하기 쉬웠다.😉
JS에서는 URLSearchParams()로 특정 파라미터(key)와 값(value)을 확인/추가/삭제할 수 있었는데, React에서는 파라미터는 useParams(), 쿼리는 useSearchParams()를 이용하여 접근한다.
수업 시간에 실험했던 코드를 살펴보자.
Parameter - useParams()
/* Parameter at React */
// productDetail.jsx
import { useNavigate, useParams } from 'react-router-dom';
export default function ProductDetail() {
const tempProductsData = [
{
id: 1,
name: '다이슨 슈퍼소닉',
email: 'Eliseo@gardner.biz',
body: '다이슨 슈퍼소닉 헤어드라이어',
},
{
id: 2,
name: 'SSD 1TB',
email: 'Jayne_Kuhic@sydney.com',
body: '삼성전자 삼성 외장SSD T7 1TB',
},
];
const navigate = useNavigate();
const params = useParams();
console.log(params.productId);
const { productId } = useParams();
const [result] = tempProductsData.filter(
(prod) => prod.id === Number(productId)
);
console.log(result);
if (!result) {
return (
<>
<h1>존재하지 않는 상품입니다!</h1>
<button onClick={() => navigate('/')}>홈으로</button>
<button onClick={() => navigate('/products')}>상품 목록 페이지</button>
</>
);
}
return (
<>
<h1>상품 상세 페이지</h1>
<button onClick={() => navigate('/')}>홈으로</button>
<button onClick={() => navigate('/products')}>상품 목록</button>
<button onClick={() => navigate(-1)}>뒤로 가기</button>
<div>상품명: {result.name}</div>
<div>판매자 정보: {result.email}</div>
<div>상세정보: {result.body}</div>
</>
);
}
// app.js
<Routes>
<Route path="/products/:productId" element={<ProductDetail />} />
</Routes>
- 일단 productDetail 컴포넌트에서 useNavigate, useParams 기능을 사용하기 위해 react-router-dom으로부터 import를 해온다.
- 함수형 컴포넌트 선언문 내부에 객체의 배열로 이루어진 데이터부터 변수에 저장했다.
- navigate 변수에는 useNavigate()를, params 변수에는 useParams()를 저장했다.
- 콘솔로 params.productId를 찍어보면 객체의 배열에서 가져온 id값(숫자)가 찍힌다.
- productId를 구조분해하여 useParams()에 담는다.
- 이후에 만약 아래 사진처럼, 배열로 받은 데이터에 filter()를 사용해서 prod.id와 productId가 같을 때 result라는 변수에 담는다고 선언하면, result는 파라미터로 받은 id의 상품 정보가 담긴 객체의 배열이 되어, 'result[0].id' 로 접근해야 한다.
- 이렇게 작성하면 접근하기 불편하니까, result를 바로 구조분해하여 객체를 변수에 저장한다. 예시 코드처럼 작성하면 필터링한 결과의 배열에서 0번째 index 요소의 값이 result 변수에 저장된다. 그래서 콘솔로 result를 찍으면 객체의 배열에서 index 하나만 뽑아서 출력하게 된다.
- 그리고 if문으로 만약 result가 없다면 존재하지 않는 상품이라는 메시지가 보여지도록 코드를 작성했다.
- return문 내부에는 브라우저에서 보여주고 싶은 내용을 작성하는데, 버튼의 onClick에 navigate를 연결하여 각자 어디로 이동할지를 설정했고, 상품 리스트는 점 접근법으로 불러왔다.
- 마지막으로 app.js에서 라우팅 경로에 "/products/:productId"라고 작성하면, 각각의 id값에 맞는 상품이 브라우저에 보여지게 된다.
Query - useSearchParams()
다크/라이트 모드가 변경되면 쿼리스트링으로 주소에 표시되도록 하는 코드
/* Query for React */
import { useState } from 'react';
import { useSearchParams } from 'react-router-dom';
export default function Home() {
const [queryParams, setQueryParams] = useSearchParams();
const [isDark, setIsDark] = useState(false);
console.log(queryParams.get('mode'));
/*
/ -> null
/?mode=dark -> dark
*/
const toggleMode = () => {
// isDark가 true이면 다크모드니까 일반모드로 변경 -> 루트 주소
// isDark가 false면 일반모드니까 다크로 변경 -> /?mode=dark
setQueryParams(isDark ? {} : { mode: 'dark' });
setIsDark(!isDark);
};
return (
<>
<h1>Welcome, Home 컴포넌트</h1>
<div style={isDark ? { backgroundColor: '#000', color: '#fff' } : {}}>
현재 모드: {isDark ? '다크 모드' : '라이트 모드'}
</div>
<button onClick={toggleMode}>모드 변경</button>
</>
);
}
- 일단 기능을 제대로 사용하기 위해 useState, useSearchParams를 import한다.
useState는 다크/라이트 모드 상태관리를 위해 사용된다. - 함수형 컴포넌트 선언문 내부에서 가장 먼저 useState, useSearchParams부터 구조분해할당한다.
- queryParams.get()으로 쿼리 'mode'를 가져와서 콘솔에 찍어보면
→ 쿼리가 없으면 null이 나오고, '?mode=dark'로 입력된 경우에는 dark가 출력된다. - toggleMode라는 함수를 만들어서, setQueryParams()의 인자에 삼항연산자로 토글될 때마다 쿼리가 바뀔 수 있도록 작성하고, setIsDark state의 인자로는 isDark를 부정하는 값을 넣어서 라이트 모드에 대한 설정을 한다.
- return문 내부에 브라우저에서 보여질 부분을 작성한다.
삼항연산자를 사용하여 현재 모드에 따라 배경색과 텍스트가 제대로 출력될 수 있도록 했고,
버튼에는 앞서 만들어둔 toggleMode 함수를 onClick으로 연결했다.
React - Styling
React 컴포넌트의 스타일링 방식은 크게 3가지가 있다.
- CSS - 일반 CSS 확장자를 가진 파일을 의미한다. 소규모 프로젝트에 적합하다.
- Styled Components - JS 안에서 CSS를 작성할 수 있게 도와주는 CSS-in-JS 라이브러리. 컴포넌트 기반의 설계 방식을 갖고 있다.
- SASS - Syntactically Awesome Style Sheets. 'CSS 전처리기'를 이용해 쉽게 작업할 수 있게 해준다.
💡 여기서 잠깐!
일반 CSS 파일의 경우, 코드가 길어지면 어떤 스타일링을 어디에 했는지 찾기 어렵고, 선택자가 다양해져서 코드 자체가 복잡해진다. 이런 CSS의 단점을 보완하기 위해 등장한 것이, 자신만의 문법을 가지고 CSS를 만들어주는 'CSS 전처리기'이다.
→ CSS 전처리기는 변수, 함수 등 개념을 사용할 수 있어 재활용성과 가독성이 좋다.
React에서 스타일 적용방법
/* 1. inline style */
export default function BasicCss() {
const divStyle = {
backgroundColor: 'navy',
color: 'pink',
textAlign: 'center',
fontWeight: 700,
};
return (
<>
<h1>일반 CSS 사용하기</h1>
<div style={{
backgroundColor: 'pink',
color: 'navy',
fontSize: 25,
textAlign: 'right',
}}>
인라인으로 스타일 적용(태그에 직접 작성)
</div>
<div style={divStyle}>인라인으로 스타일 적용(객체 변수 전달)</div>
</>
);
}
1. inline style 방법
HTML에서 했던 것과 비슷한데, 객체 형태로 만들어서 대입하는 방법이다.
React 컴포넌트 파일에 JS 문법으로 작성하기 때문에, 속성의 이름은 camelCase로 작성한다.
불편한 부분이 많아서 실제로는 잘 쓰이지 않는다고 한다.
/* 2. 별도의 css 파일 사용 */
import '../styles/basicCss.css';
export default function BasicCss() {
return (
<>
<h3>CSS 파일 사용해서 스타일링</h3>
<div className="box">.box</div>
<div className="container">
<div className="item item1">.item.item1</div>
<div className="item">.item</div>
</div>
</>
);
}
2. 별도의 CSS 파일을 만들어서 import 하는 방법
CSS 파일은 반드시 React 애플리케이션의 src 폴더 내부에 styles라는 하위 폴더에 생성한다.
컴포넌트별로 관리하기 때문에, CSS 파일의 이름도 컴포넌트와 같은 이름으로 만드는게 일반적이다.
CSS 선택자가 컴포넌트별로 중복되지 않도록 유의해서 지정해야 한다.
/* 3. CSS module */
import className from 'classname'; // classname module
import style from '../styles/moduleCss.module.css';
export default function ModuleCss() {
return (
<>
<h2>module css 사용</h2>
<div className={style.container}>moduleCss.module.css 사용</div>
<div>여러 클래스 지정하는 방법</div>
<div className={`${style.first} ${style.second}`}>
방법 1. 템플릿 리터럴 사용
</div>
<div className={[style.first, style.second].join(' ')}>
방법 2. join() 사용 (배열을 하나의 문자열로 합쳐주는 메서드)
</div>
<div className={className(style.first, style.second)}>
방법 3. className 모듈 사용
</div>
</>
);
}
3. CSS module 이라는 React 내장 기능 이용하기
'이름.module.css' 형식으로 파일을 생성하여, 사용할 컴포넌트에서 CSS 파일을 import한다.
컴포넌트에서 선택자로 들어올 값은 점 접근법으로 가져와서 사용한다.
CSS module 형식을 사용하면, 아래 사진처럼 선택자가 자동으로 생성된다.
다수의 선택자 지정은 → 템플릿 리터럴, join() 메서드, className 모듈을 설치하여 사용하는 방법이 있다.
4. Styled Components
개별 컴포넌트 중심으로 스타일의 적용을 돕는 방법인데, React의 기본 기능이 아니라서 아래 명령어로 설치가 필요하다.
// 터미널에서 styled comps 설치하기
$ npm install styled-components
// 설치후 사용을 위해 import
import styled from "styled-components";
/* 4. Styled Components */
import styled, { keyframes } from 'styled-components';
export default function StyledComp() {
const rotate = keyframes`
0%{
transform: rotate(0);
}
50%{
transform: rotate(180deg);
width: 100px;
height: 100px;
}
100%{
transform: rotate(360deg);
}
`;
const MyDiv = styled.div`
border: 1px solid gray;
padding: 10px;
margin: 10px 0;
`;
const Child = styled.span`
background-color: pink;
color: purple;
&:hover {
color: blue;
animation: ${rotate} 1s infinite linear;
}
@media screen and (min-width: 720px) {
background-color: lightblue;
}
`;
const RotateDiv = styled.div`
width: 50px;
height: 50px;
background-color: ${(props) => (props.bgcolor ? props.bgcolor : 'orange')};
animation: ${rotate} 30s infinite linear;
`;
return (
<>
<div>일반 div</div>
<MyDiv>styled-component 사용한 div</MyDiv>
<Child>child 컴포넌트</Child>
<RotateDiv>rotate</RotateDiv>
<RotateDiv bgcolor="green">rotate</RotateDiv>
</>
);
}
- 기능을 사용하려면 import를 해야한다. 위 코드에서는 애니메이션 기능도 사용하려고 keyframes도 import했다.
- 백틱으로 감싼 CSS 스타일링을 변수에 담고, 변수를 컴포넌트처럼 불러와서 사용한다.
예를 들어, MyDiv라는 컴포넌트는 div 태그에 border, padding, margin을 스타일링했다는 의미이다. - 가상클래스, 가상요소 선택자는 스스로를 의미하는 & 기호로 백틱 내부에 선언한다. 미디어쿼리도 마찬가지.
위 코드에서는 span 태그로 만든 Child라는 컴포넌트에 호버되었을 경우에 대한 스타일링과, 720px 이상일 때의 미디어쿼리가 설정되었다. 애니메이션은 앞에 rotate라는 이름으로 선언한 변수를 가져왔다.
💡 지금까지의 파일 구조
나중에 3차 프로젝트를 진행할 때도 참고하면 좋을 것 같아서, 지금까지 React를 학습하면서 만들었던 파일 구조를 전부 가져왔다.
- components - jsx 확장자로 만든 컴포넌트가 들어있다.
- hooks - js 확장자로 개발자가 직접 만든 custom hooks가 들어있다.
- provider - useContext 기능을 사용하기 위한 provider.jsx 파일이 들어있다. 컴포넌트 폴더 내부에 만드는 폴더.
- contexts - createContext 키워드로 만든 context 파일이 들어있다.
- pages - 말 그대로 jsx 확장자로 만든 메인, 상품 목록, 상품 상세 등 각종 페이지가 들어있다.
- styles - React 스타일링을 위한 파일들이 들어있다.
React에서 스타일링하는 방법까지 배웠으니, 이제 그럴듯한 웹 페이지 하나 정도는 만들 수 있게 되지 않았을까 싶다. 물론 짧은 시간에 뚝딱 만들어낼 정도로 잘하는건 아니지만..!🙂
대망의 SASS에 대한 설명은 다음 포스팅에서!
