본문 바로가기
JavaScript/React

React) 컴포넌트 스타일링④ - styled-components

by 박채니 2022. 11. 19.
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[리액트를 다루는 기술]의 책을 참고하여 포스팅한 개인 공부 내용입니다.

 

컴포넌트 스타일링

 

styled-components

- 자바스크립트 파일 안에 스타일을 선언하는 방식 (CSS-in-JS)

 

사용을 위해 설치해보겠습니다.

$ yarn add styled-components

 

StyledComponent.js

import styled, { css } from 'styled-components'

const Box = styled.div`
    /* props로 넣어준 값을 직접 전달 가능 */
    background: ${props => props.color || 'blue'};
    padding: 1rem;
    display: flex;
`;

const Button = styled.button`
    background: white;
    color: black;
    border-radius: 4px;
    padding: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    font-size: 1rem;
    font-weight: 600;

    /* & 문자를 사용하여 Sass처럼 자기 자신 선택 가능 */
    &:hover {
        background: rgba(255, 255, 255, 0.9);
    }

    /* inverted 값이 true일 때 특정 스타일 부여 */
    ${props =>
        props.inverted &&
        css`
            background: none;
            border: 2px solid white;
            color: white;
            &:hover {
                background: white;
                color: black;
            }
        `};
        & + button {
            margin-left: 1rem;
        }
    `;

    const StyledComponent = () => (
        <Box color='black'>
            <Button>안녕하세요</Button>
            <Button inverted={true}>테두리만</Button>
        </Box>
    );

    export default StyledComponent;

 

App.js

import StyledComponent from './StyledComponent';

class App extends Component {
  render() {
    return (
      <div>
        <StyledComponent />
      </div>
    );
  }
}

export default App;

※ 컴포넌트 내부에 작성한 CSS 코드의 신택스 하이라이팅 적용을 원한다면 vscode-style-components 설치

위 작동원리를 이해하기 위해 원리와 사용법을 알아보도록 하겠습니다.

 

Tagged 템플릿 리터럴

 

스타일 작성 시 `(백틱)을 사용하여 문자열에 스타일 정보를 넣어준 것을 확인할 수 있습니다.

해당 문법을 'Tagged 템플릿 리터럴' 이라고 합니다.

일반 템플릿 리터럴과 다른 점템플릿 내의 js 객체나 함수를 전달할 때 온전히 추출할 수 있습니다.

 

일반 템플릿 리터럴 예

객체 혹은 함수를 넣으면 객체[object Object] 형태, 함수문자열 그대로 나타나는 것을 확인할 수 있습니다.

 

Tagged 템플릿 리터럴 예

이처럼 Tagged 템플릿 리터러를 사용하면 js 객체나 함수를 그대로 추출할 수 있습니다.

따라서 이러한 속성을 이용해 styled-components로 만든 컴포넌트의 props를 스타일쪽에서 쉽게 조회할 수 있는 것!

 

스타일링된 엘리먼트 만들기

 

styled-components를 사용하여 스타일링된 엘리먼트를 만들 땐 컴포넌트 파일 상단에서 styled를 불러오고,

styled.태그명을 사용하여 구현합니다.

 

import styled from 'styled-components';

const MyComponent = styled.div`
	font-size: 2rem;
`;

이처럼 styled.div 뒤에 Tagged 템플릿 리터럴 문법으로 스타일을 지정해주면, 해당 스타일이 적용된 div로 이루어진 리액트 컴포넌트가 생성됩니다.

따라서 나중에 <MyComponent>Hello</MyComponent> 형태로 사용할 수 있는 것이죠.

(button 혹은 input에 스타일링을 하고 싶다면, styled.button / style.button 형태로 사용)

 

하지만 사용해야 할 태그가 유동적이거나 특정 컴포넌트 자체에 스타일링을 해주고 싶다면 아래와 같이 해주면 됩니다.

// 태그 타입을 styled 함수의 인자로 전달
const MyInput = styled('input')`
	background: gray;
`

// 아예 컴포넌트 형식의 값을 넣어줌
const StyledLink = styled(Link)`
	color: blue;
`

 

스타일에서 props 조회하기

 

위에서 생성하였던 코드를 살펴보면, 스타일 쪽에서 컴포넌트에게 전달된 props 값을 참조할 수 있었습니다.

 

StyledComponent.js - Box 컴포넌트

const Box = styled.div`
    /* props로 넣어준 값을 직접 전달 가능 */
    background: ${props => props.color || 'blue'};
    padding: 1rem;
    display: flex;
`;

...
...

<Box color='black'> ... </Box>

background 값에 props를 조회해 props.color 값이 주어지지 않는다면 'blue'로 기본 색상을 설정해주었습니다.

JSX에서 color값을 props로 넣어준 것도 확인할 수 있습니다.

 

props에 따른 조건부 스타일링

 

StyledComponent.js - Button

import styled, { css } from 'styled-components'
/* 
    단순 변수의 형태가 아니라 여러 줄의 스타일 구문을 조건부로 설정해야 하는 경우엔
    css를 불러와야 함
*/

const Button = styled.button`
    background: white;
    color: black;
    border-radius: 4px;
    padding: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    font-size: 1rem;
    font-weight: 600;

    /* & 문자를 사용하여 Sass처럼 자기 자신 선택 가능 */
    &:hover {
        background: rgba(255, 255, 255, 0.9);
    }

    /* inverted 값이 true일 때 특정 스타일 부여 */
    ${props =>
        props.inverted &&
        css`
            background: none;
            border: 2px solid white;
            color: white;
            &:hover {
                background: white;
                color: black;
            }
        `};
        & + button {
            margin-left: 1rem;
        }
    `;

 

<Button>안녕하세요</Button>
<Button inverted={true}>테두리만</Button>

위처럼 생성한 컴포넌트는 props를 사용하여 다른 스타일을 적용할 수 있습니다.

 

또한, 스타일 코드 여러 줄을 props에 따라 넣어 주어야 할 때는 CSS를 불러와야 합니다.

CSS를 불러오지 않고도 문자열을 넣어도 작동하긴 하지만 문자열 그대로 취급 되므로, 함수를 받아 사용하진 못하여 props 값을 사용할 수 없습니다.

// css 사용하지 않은 버전

	${props =>
        props.inverted &&
        `
            background: none;
            border: 2px solid white;
            color: white;
            &:hover {
                background: white;
                color: black;
            }
        `};

Tagged 템플릿 리터럴이 아니기 때문에 props를 참조하지 않는다면 css 불러와서 사용하지 않고 위처럼 사용해도 되지만,

만일 props를 참조한다면 반드시 CSS로 감싸준 후 Tagged 템플릿 리터럴을 사용해줘야 합니다.

 

반응형 디자인

 

브라우저 가로 크기에 따라 다른 스타일 적용하기 위해 media 쿼리를 사용해주겠습니다.

 

StyledComponent.js - Box

const Box = styled.div`
    /* props로 넣어준 값을 직접 전달 가능 */
    background: ${props => props.color || 'blue'};
    padding: 1rem;
    display: flex;
    /*
        기본적으로 가로 크기 1024px에 가운데 정렬을 하고,
        가로 크기가 작아짐에 따라 크기를 줄이고
        768px 미만이 되면 꽉 채움
    */
    width: 1024px;
    margin: 0 auto;
    @media (max-width: 1024px) {
        width: 768px;
    }
    @media (max-width: 768px) {
        width: 100%;
    }
`;

위처럼 일반 CSS와 별다른 차이가 없습니다.

해당 작업을 함수화하여 간편하게 사용해보겠습니다. (styled-components 매뉴얼에서 제공하는 유틸 함수 참고)

 

const sizes = {
    desktop: 1024,
    tablet: 768
}

// 위에 있는  size 객체에 따라 자동으로 media 쿼리 함수 생성
const media = Object.keys(sizes).reduce((acc, label) => {
    acc[label] = (...args) => {
        css`
            @media (max-width: ${sizes[label] / 16}em) {
                ${css(...args)};
            }
        `
    }
    return acc;
}, {});

const Box = styled.div`
    /* props로 넣어준 값을 직접 전달 가능 */
    background: ${props => props.color || 'blue'};
    padding: 1rem;
    display: flex;
    width: 1024px;
    margin: 0 auto;
    ${media.desktop`width: 768px`}
    ${media.tablet`width: 100%`}
`;

이처럼 다른 파일로 모듈화하여 필요에 따라 불러와 간편하게 사용할 수 있습니다.