설문폼 프로젝트를 클론코딩해보며 이것저것 새로 알기도하고, 알던걸 또 되새기며 조금씩 깨우치고있다. 그중 style-components를 사용하며 이렇게도 쓰는구나를 배운것이 있다.(style-components를 사용하는 법이기도하지만 CSS-in-JS의 사용법? 편리함? 이라고 봐야할까)
하나의 버튼 컴포넌트에 위와 같이 다양한 타입 그리고 한타입에서 또 세부적으로 다른 스타일링의 무언가를 상황별로 그려내려 할때, 단순히 if 문이나 함수를 써가면서도 만들 수 있겠지만 스타일변수를 만들어두고 타입들을 props로 받아 스타일을 주면 코드도 간결해지고 편리하다는것을 알게되었다.
function ActionButtons({ questionsLength, step }) {
const isLast = questionsLength - 1 === step;
const navigate = useNavigate();
return (
<ActionButtonsWrapper>
{/* 스텝넘버가 0이라면 이전버튼은 노출되지않고 0 다음 숫자일 경우 이전버튼 그리기 */}
{step === 0 || (
<Button
type="TERTIARY"
onClick={() => {
navigate(`${step - 1}`);
}}
>
이전
</Button>
)}
{/* isLast에있는 questionsLength가 마지막번호가 아니라면 다음버튼이 보여지고 마지막번호라면 제출번호 그리기 */}
{isLast ? (
<Button
type="PRIMARY"
onClick={() => {
navigate('/done');
}}
>
제출
</Button>
) : (
<Button
type="PRIMARY"
onClick={() => {
navigate(`${step + 1}`);
}}
>
다음
</Button>
)}
</ActionButtonsWrapper>
);
}
위 코드는 저 버튼들이 어떤상황에서 노출될지, 아닐지 상황을 따로 그려놓은 코드. 저 코드에서 중요한건 버튼마다 type을 props로 버튼에게 내려주고 있는것이다.
export const PRIMARY = {
BUTTON: {
DEFAULT: {
COLOR: '#fff',
BACKGROUND: '#6542F1',
},
HOVER: {
COLOR: '#fff',
BACKGROUND: '#9982F4',
},
PRESSED: {
COLOR: '#fff',
BACKGROUND: '#4B31B0',
},
DISABLED: {
COLOR: '#D0CDCD',
BACKGROUND: '#EDEDED',
},
},
};
export const SECONDARY = {
BUTTON: {
DEFAULT: {
COLOR: '#3A3A3A',
BACKGROUND: '#DEDEDE',
},
HOVER: {
COLOR: '#3A3A3A',
BACKGROUND: '#F3F3F3',
},
PRESSED: {
COLOR: '#3A3A3A',
BACKGROUND: '#B8B7B9',
},
DISABLED: {
COLOR: '#D0CDCD',
BACKGROUND: '#EDEDED',
},
},
};
export const TERTIARY = {
BUTTON: {
DEFAULT: {
COLOR: '#6542F1',
BACKGROUND: '#fff',
BORDER: '#6542F1',
},
HOVER: {
COLOR: '#9982F4',
BACKGROUND: '#fff',
BORDER: '#9982F4',
},
PRESSED: {
COLOR: '#4B31B0',
BACKGROUND: '#F0EDFC',
BORDER: '#4B31B0',
},
DISABLED: {
COLOR: '#D0CDCD',
BACKGROUND: '#F6F6F6',
BORDER: '#EDEDED',
},
},
};
이것이 최상위 이미지에 있는 버튼 스타일들을 변수화시켜서 정리해둔것! 폴더와 페이지도 따로 빼서 관리하니 이런식의 변수는 이곳에서 하면 된다.
import styled from 'styled-components';
// 선언해둔 스타일변수 가져오기
import { PRIMARY, SECONDARY, TERTIARY } from '../../constants/color';
// 가져온 스타일변수 이곳에서 꺼내쓰기 쉽게 객체로 다시 그룹핑
const colorMap = {
PRIMARY,
SECONDARY,
TERTIARY,
};
const Button = styled.button`
width: 200px;
padding: 16px 24px;
border-radius: 4px;
// 순수 css가 아니므로 이곳에서 함수를 사용할 수 있다. ${}를 사용하자
// style-components도 props를 받을 수 있다.
// 부모 컴포넌트가 내려준 props를 사용해 버튼스타일링을 할 수 있다.
color: ${({ type }) => colorMap[type].BUTTON.DEFAULT.COLOR};
background: ${({ type }) => colorMap[type].BUTTON.DEFAULT.BACKGROUND};
font-weight: bold;
// 아래 타입은 경우에 따라 border가 생긴다. 삼항연산자로 처리
border: ${({ type }) =>
type === 'TERTIARY'
? `1px solid ${TERTIARY.BUTTON.DEFAULT.BORDER}`
: 'none'};
&:hover {
color: ${({ type }) => colorMap[type].BUTTON.HOVER.COLOR};
background: ${({ type }) => colorMap[type].BUTTON.HOVER.BACKGROUND};
border: ${({ type }) =>
type === 'TERTIARY'
? `1px solid ${TERTIARY.BUTTON.HOVER.BORDER}`
: 'none'};
}
&:active {
color: ${({ type }) => colorMap[type].BUTTON.PRESSED.COLOR};
background: ${({ type }) => colorMap[type].BUTTON.PRESSED.BACKGROUND};
border: ${({ type }) =>
type === 'TERTIARY'
? `1px solid ${TERTIARY.BUTTON.PRESSED.BORDER}`
: 'none'};
}
&:disabled {
color: ${({ type }) => colorMap[type].BUTTON.DISABLED.COLOR};
background: ${({ type }) => colorMap[type].BUTTON.DISABLED.BACKGROUND};
border: ${({ type }) =>
type === 'TERTIARY'
? `1px solid ${TERTIARY.BUTTON.DISABLED.BORDER}`
: 'none'};
}
`;
export default Button;
복잡해보이지만 나름 선언해둔 스타일변수로 인해 깔끔해진편이다. if문으로 분개했다면 엄청 길어졌을것이니..