๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
  • ์žฅ์›์ต ๊ธฐ์ˆ ๋ธ”๋กœ๊ทธ
๐Ÿ“บ Front End/- React, Next.js

[styled-components] ThemeProvider์—์„œ MediaQuery๋ฅผ ์ ์šฉํ•˜๋Š” 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•

by Wonit 2021. 1. 2.

์ด ๊ธ€์€ Styled-components์˜ ThemeProvider์— ๋Œ€ํ•œ ๊ธฐ๋ณธ์ ์ธ ์„ ํ–‰ ์ง€์‹์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.


ํ˜น์‹œ ThemeProvider์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ํ™•์ธํ•ด๋ณด๊ณ  ์‹ถ๋‹ค๋ฉด ThemeProvider๋กœ ๊ณตํ†ต ์Šคํƒ€์ผ ์†์„ฑ ๊ด€๋ฆฌํ•˜๊ธฐ๋ฅผ ๋ฐฉ๋ฌธํ•ด์„œ ๊ฐ€๋ณ๊ฒŒ ์ฝ๊ณ  ์™€๋ณด์„ธ์š”. ๊ทธ๋Ÿผ ๋” ๋„์›€์ด ๋ ๊ฑฐ์—์š” :)


์–ด๋–ค ์ข…๋ฅ˜์˜ ์–ธ์–ด๋‚˜ ๋„๊ตฌ๊ฐ€ ๋˜์—ˆ๋˜ ์›น ํ”„๋ก ํŠธ์˜ ์Šคํƒ€์ผ๋ง์„ ํ•˜๋Š” ๊ณผ์ •์—๋Š” ๋งŽ์€ ๋…ธ๋ ฅ๊ณผ ํž˜์ด ๋“ค์–ด๊ฐ„๋‹ค.


๊ทธ ์ค‘์—์„œ๋„ ๋ชจ๋ฐ”์ผ๊ณผ ์›น์˜ ์Šคํƒ€์ผ์„ ๋”ฐ๋กœ ๋‚˜๋ˆ„๋Š” ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ ๋ถ€๋ถ„์—์„œ ๊ต‰์žฅํžˆ ๋ฐ˜๋ณต๋˜๋Š” ์ž‘์—…์„ ๋งŽ์ด ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ์ž‘์—…์„ ์šฐ๋ฆฌ๋Š” ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ๋‹ค.


๋ฐ”๋กœ ThemeProvider.


์˜ค๋Š˜์€ ์ด ThemeProvider๋ฅผ ํ†ตํ•ด์„œ ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ๋ฅผ ์ข€ ๋” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณผ ๊ฒƒ์ด๋‹ค.

๊ทธ ์ „์— ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ๋ฅผ ๋จผ์ € ์•Œ์•„๋ณด์ž.

๋ฏธ๋””์–ด ์ฟผ๋ฆฌ๋Š” CSS3์—์„œ ๋‚˜์˜จ ๊ธฐ์ˆ ๋กœ ํŠน์ • ์กฐ๊ฑด์ด true ์ผ ๋•Œ๋งŒ css ์†์„ฑ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ฌธ๋ฒ•์ด๋‹ค.


์—ฌ๊ธฐ์„œ ํŠน์ • ์กฐ๊ฑด์— ๋ณดํ†ต ์šฐ๋ฆฌ๋Š” ํ™”๋ฉด์˜ ๋„“์ด๋‚˜ ๋†’์ด์™€ ๊ฐ™์ด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์–ด๋–ค ๊ธฐ๊ธฐ์—์„œ ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š”์ง€๋ฅผ ํŒŒ์•…ํ•ด์„œ ๊ธฐ๊ธฐ๋ณ„๋กœ ์„œ๋กœ ๋‹ค๋ฅธ ์Šคํƒ€์ผ์„ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜๋Š”๋ฐ, ์ด๊ฒƒ์„ ๋ฐ”๋กœ ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด๋ผ๊ณ  ํ•œ๋‹ค.

@media screen and (max-width: 768px) {
  font-size: 3rem;
  font-weight: bold;
}

 

์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

 

๊ทธ๋Ÿผ screen ์˜์—ญ์—์„œ 768px ์ „ ๊นŒ์ง€์˜ width์—์„œ๋Š” font-size: 3rem๊ณผ font-weight: bold ์†์„ฑ์„ ์ ์šฉ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

ThemeProvider๋ฅผ ์ด์šฉํ•œ 2๊ฐ€์ง€ ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ ์‚ฌ์šฉ๊ธฐ

ThemeProvider๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ๋ฐฉ๋ฒ•์€ 2๊ฐ€์ง€ ์ด๋‹ค.

  1. MediaQuery ์†์„ฑ์„ ๋ชจ๋“ˆํ™” ํ•˜๊ธฐ.
  2. MediaQuery ์ž์ฒด๋ฅผ ๋ชจ๋“ˆํ™” ํ•˜๊ธฐ.

์–ด๋–ค ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜๋˜ ๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ ์ด ์กด์žฌํ•œ๋‹ค.

 

๋ฐฉ๋ฒ•์„ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ๋’ค์—์„œ ๋ฌด์—‡์ด ๋ณธ์ธ์—๊ฒŒ ๋” ๋งž์„์ง€ ๋น„๊ตํ•ด๋ณด์ž.

MediaQuery ์†์„ฑ์„ ๋ชจ๋“ˆํ™” ํ•˜๊ธฐ.

MediaQuery ์†์„ฑ์„ ๊ฐ์ฒดํ™” ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ง€๋‚œ ์‹œ๊ฐ„์— ๋ดค๋˜ ํ”„๋กœ์ ํŠธ์—์„œ ThemeProvider ์‚ฌ์šฉํ•˜๊ธฐ์˜ ๋ฐฉ๋ฒ•๊ณผ ์ •ํ™•ํžˆ ๋™์ผํ•˜๋‹ค.

 

์šฐ์„  theme.js ํŒŒ์ผ์„ ์ƒ์„ฑํ•œ๋‹ค.

theme.js

๊ทธ๋ฆฌ๊ณ  2๊ฐœ์˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

  1. size ๊ฐ์ฒด : ๊ธฐ๊ธฐ์˜ ์‚ฌ์ด์ฆˆ์˜ ๊ฐ’์ด ๋“ค์–ด์žˆ๋Š” ๊ฐ์ฒด
  2. device ๊ฐ์ฒด : media query์˜ ์†์„ฑ๊ฐ’์ด ๋“ค์–ด์žˆ๋Š” ๊ฐ์ฒด
const deviceSizes = {
  mobile: "375px",
  tablet: "768px",
  laptop: "1024px",
};

const device = {
  mobile: `screen and (max-width: ${deviceSizes.mobile})`,
  tablet: `screen and (max-width: ${deviceSizes.tablet})`,
  laptop: `screen and (max-width: ${deviceSizes.laptop})`,
};

const theme = {
  device
};

export default theme;

 

์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ๋•Œ๋Š” @media๋ฅผ ๋จผ์ € ์„ ์–ธํ•ด์ฃผ๊ณ  ๋’ค์— ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด๋œ๋‹ค.

 

@media ${({ theme }) => theme.device.tablet} {
  flex-direction: column;
  font-size: ${({ theme }) => theme.fontSizes.paragraph};
}

 

์ด์ œ ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด์ž.


ThemeProvider์˜ theme props๋กœ ์ „๋‹ฌํ•ด์ค€๋‹ค.

 

import React from "react";
import styled, { ThemeProvider } from "styled-components";
import theme from "./theme";

const Div = styled.div`
  @media ${({ theme }) => theme.device.tablet} {
    flex-direction: column;
  }

  width: 100vw;
  height: 100vh;
  background: wheat;
  display: flex;
  align-items: center;
  justify-content: center;
`;

const Div2 = styled.div`
  width: 100px;
  height: 100px;
  background: white;
  margin: 5px;
`;

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Div>
        <Div2>์ •๋ง๋กœ</Div2>
        <Div2>๋ฆฌ์•กํŠธ</Div2>
        <Div2>์žฌ๋ฐŒ์–ด์š”</Div2>
        <Div2>์ง„์งœ๋กœ</Div2>
      </Div>
    </ThemeProvider>

  );
};

export default App;

MediaQuery ์ž์ฒด๋ฅผ ๋ชจ๋“ˆํ™” ํ•˜๊ธฐ.

์ด ๋ฐฉ๋ฒ•์€ ์œ„์˜ ๋ฐฉ๋ฒ•์—์„œ ์กฐ๊ธˆ ๋ฐœ์ „ํ•œ ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์œ„์—์„œ๋Š” screen and (max-width: ${deviceSizes.mobile}) ์„ ์ƒ์ˆ˜ํ™” ์‹œ์ผœ exportํ•œ ๊ฒƒ์„ ์‚ฌ์šฉํ–ˆ๋‹ค.


๊ทธ๋Ÿผ ์šฐ๋ฆฌ๋Š”

 

@media {}

 

๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ์ด๋ฒˆ์—๋Š” MediaQuery ๋ฌธ๋งฅ ์ž์ฒด๋ฅผ ์ง์ ‘ ๋งŒ๋“ ๋‹ค.

 

2๊ฐœ์˜ js ํŒŒ์ผ์„ ์ƒ์„ฑํ•œ๋‹ค.

 

  1. media.js : media ์ฟผ๋ฆฌ ์ž์ฒด๋ฅผ exportํ•  ํŒŒ์ผ
  2. theme.js : ํ†ต์ผํ™”๋ฅผ ์œ„ํ•ด ์ •์˜๋œ ๋‹ค๋ฅธ theme ์†์„ฑ (์ผ๋ฐ˜์ ์ธ ThemeProvider์˜ theme props ๊ฐ’)

media.js

import { css } from "styled-components";

const sizes = {
  mobile: 320,
  tablet: 768,
  laptop: 1024,
  tesktop: 2560,
};

export default Object.keys(sizes).reduce((acc, label) => {
  acc[label] = (...args) => css`
    @media screen and (max-width: ${sizes[label]}px) {
      ${css(...args)};
    }
  `;
  return acc;
}, {});

์—ฌ๊ธฐ ๋ฌธ๋ฒ•์„ ์กฐ๊ธˆ ์งš๊ณ  ๋„˜์–ด๊ฐ€์•ผ ํ•˜๋Š”๋ฐ,

Object.keys() ํ•จ์ˆ˜๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋„˜์–ด๊ฐ„ sizes ๊ฐ์ฒด์— ์žˆ๋Š” enumerable ์†์„ฑ๋ช…์„ ๊ฐ€์ง€๊ณ  reduce()์˜ ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜๋กœ size์— ์ •์˜๋œ ๊ฐ’์œผ๋กœ acc ์—ฐ์‚ฐ์œผ๋กœ ๊ฐ๊ฐ์˜ media query์˜ ์†์„ฑ๋“ค์„ styled-components์˜ css ์†์„ฑ์„ ์ด์šฉํ•ด์„œ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค.


๊ทธ๋ฆฌ๊ณ  ๊ทธ ์†์„ฑ ๊ฐ’๋“ค์„ ์œ„์—์„œ ์ •์˜ํ•œ sizes ๊ฐ์ฒด์— ์žˆ๋Š” ๊ฐ’๋“ค๋กœ media query์˜ ์†์„ฑ์„ ๋งŒ๋“œ๋Š”๋ฐ ...args ๋ฌธ๋ฒ•์œผ๋กœ ์ƒ์„ฑ๋œ ๋ชจ๋“  args๋ฅผ media query ๋‚ด์— ์ ์šฉ์‹œ์ผœ์ค€๋‹ค.

 

theme.js

import styled from "styled-components";

const colors = {
  black: "#000000",
  grey: "#999999",
};

// ... ์ƒ๋žต

const theme = {
  fontSizes,
  colors,
  common,
};

export default theme;

 

ThemeProvider์— ์ ์šฉํ•˜๊ธฐ

์ด ๋ฐฉ๋ฒ•์€ Theme ์†์„ฑ์— ๋“ค์–ด๊ฐˆ ํŒŒ์ผ์ด 2๊ฐœ๊ฐ€ ๋˜๋ฏ€๋กœ 2๊ฐœ์ญ ์†์„ฑ์„ ThemeProvider์˜ props๋กœ ๋„ฃ์–ด์ค˜์•ผํ•œ๋‹ค.

 

import React from "react";
import styled, { ThemeProvider } from "styled-components";
import theme from "./theme";
import media from "./media";

const Div = styled.div`
  font-size: ${({ theme }) => theme.fontSizes.subtitle};

  ${({ theme }) => theme.tablet` // theme props์˜ media ๊ฐ์ฒด ์‚ฌ์šฉํ•˜๊ธฐ
    flex-direction: column;
    font-size: ${({ theme }) => theme.fontSizes.paragraph}; // theme props์˜ theme.js ๊ฐ์ฒด ์‚ฌ์šฉํ•˜๊ธฐ
  `};

  width: 100vw;
  height: 100vh;
  background: wheat;
  display: flex;
  align-items: center;
  justify-content: center;
`;

const Div2 = styled.div`
  width: 100px;
  height: 100px;
  background: white;
  margin: 5px;
`;

const ThemeProviderPrac = () => {
  return (
    <ThemeProvider theme={{ ...theme, ...media }}>
      <Div>
        <Div2>์ •๋ง๋กœ</Div2>
        <Div2>๋ฆฌ์•กํŠธ</Div2>
        <Div2>์žฌ๋ฐŒ์–ด์š”</Div2>
        <Div2>์ง„์งœ๋กœ</Div2>
      </Div>
    </ThemeProvider>
  );
};

export default ThemeProviderPrac;

๋งŒ์•ฝ ๊ธฐ์กด์— ์ •์˜๋œ theme์ด ์žˆ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด theme={ media} ์„ theme={{ ...theme, ...media }}>๊ณผ ๊ฐ™์ด theme๊ณผ media๋ฅผ ๋ชจ๋‘ spread ์‹œ์ผœ์ฃผ๋ฉด ๋œ๋‹ค.

 

์ „๊ฐœ ์—ฐ์‚ฐ์œผ๋กœ theme์˜ ์†์„ฑ๊ณผ media ์†์„ฑ์„ spread ์‹œ์ผœ์ฃผ๋ฉด ์ •์˜๋œ theme ์†์„ฑ์„ ํ†ต์ผ์„ฑ ์žˆ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  media๋กœ ์ •์˜๋œ Media Query ์†์„ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋‘ ๋ฐฉ๋ฒ•์˜ ์ฐจ์ด

๋‘ ๋ฐฉ๋ฒ•์—๋Š” ์ฐจ์ด๊ฐ€ ํ•˜๋‚˜ ์žˆ๋‹ค.

 

/* ์ฒซ ๋ฒˆ์งธ medie ์†์„ฑ์„ ๋ชจ๋“ˆํ™” ํ•˜๊ธฐ */
const Box = styled.div`
  @media ${({ theme }) => theme.device.tablet} {
    flex-direction: column;
    font-size: ${({ theme }) => theme.fontSizes.paragraph};
  }
`;


/* ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ• media ์ฟผ๋ฆฌ ์ž์ฒด๋ฅผ ๋ชจ๋“ˆํ™” ํ•˜๊ธฐ*/
const Box = styled.div`
  ${({ theme }) => theme.tablet`
    flex-direction: column;
    font-size: ${({ theme }) => theme.fontSizes.paragraph};
  `};
`;

์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ ๋ฌธ๋ฒ• ์ž์ฒด๋ฅผ ๋ชจ๋“ˆํ™” ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— @media๋งŒ ์—†๋‹ค ๋ฟ์ด์ง€ ์‚ฌ์šฉ๋ฒ• ์ž์ฒด๋Š” ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ์™€ ๋™์ผํ•˜๋‹ค.

 

๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ๋ฌธ๋งฅ์˜ ์‹œ์ž‘์ด @media๋กœ ์‹œ์ž‘ํ•˜๋ฏ€๋กœ ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ๊ฐ€ ์‹œ์ž‘๋œ๋‹ค๊ณ  ๋ˆ„๊ฐ€ ๋ณด๋”๋ผ๋„ ์•Œ ์ˆ˜ ์žˆ๊ณ  ์ง๊ด€์ ์ด๋‹ค.

 

๋˜ํ•œ ๋ฏธ๋””์–ด ์†์„ฑ์ด ๋งŽ์•„์ง€๋Š” ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ๊ฒฝ์šฐ๋Š” ํ•œ ํŒŒ์ผ์— ๋งŽ์€ ์†์„ฑ๋“ค์ด ๋“ค์–ด๊ฐ€ ์žˆ์–ด์„œ ํ—ท๊ฐˆ๋ฆด ์ˆ˜ ์žˆ๋Š” ์š”์†Œ๋“ค์ด ๋งŽ์€ ๋ฐ˜๋ฉด ๋‘ ๋ฒˆ์งธ๊ฒฝ์šฐ๋Š” ํ•˜๋‚˜์˜ media.js ํŒŒ์ผ์—์„œ ๋ฏธ๋””์–ด ์†์„ฑ์„ ๋”ฐ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ์˜ ๋ถ„๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.


์„ ํƒ์€ ๋ณธ์ธ์—๊ฒŒ ๋งž๋Š” ๋ฐฉ๋ฒ•์„ ํ•˜๋ฉด ๋˜์ง€๋งŒ ํ•„์ž๋ผ๋ฉด 2๋ฒˆ์งธ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค!

๋Œ“๊ธ€