본문 바로가기

카테고리 없음

[React] CSS 상자 모델 이해하기

HTML의 각각의 요소정보

 

 CSS의 박스 모델

  Box-model (상자 모델)이란?  모든 HTML요소는 박스(box) 모양으로 구성되며, 이것을 박스 모델이라고 한다.

 박스 모델은 HTML 구성 요소를 패딩(padding), 테두리(border), 마진(margin), 컨텐츠(content)로 구분한다.

 

  각각의 요소는 width, height 속성을 가진다.  CSS 상자모델 표준은 레벨 1, 2, 3이 있으며 CSS1, CSS2,   CSS3 등의 이름으로 레벨에 따른 CSS를 구분한다.

 

 width와 height 스타일 속성

  CSS는 HTML 요소의 넓이와 높이를 의미하는 width와 height라는 스타일 속성을 제공한다.  이 두 속성은 auto, inherit, initial, unset 등의 CSS 키워드 값을 설정하거나 숫자 뒤에 px, %, em, rem 등의 단위를 붙여 사용할 수 있으며 기본 단위를 생략할 경우 px 단위로 간주한다.

 

 Div 컴포넌트 구현하기

 div 컴포넌트를 구성한다.

import {FC, DetailedHTMLProps, HTMLAttributes, PropsWithChildren} from 'react'
import {WidthHeight} from './WidthHeight'

export type ReactDivProps = DetailedHTMLProps<
  HTMLAttributes<HTMLDivElement>,
  HTMLDivElement
>

// PropsWithChildren : children prop을 컴포넌트의 props에 자동으로 추가해 준다.
// 이 타입은 children을 받아들이는 컴포넌트에 유용하게 사용된다.
export type DivProps = ReactDivProps & PropsWithChildren<WidthHeight>

// prettier-ignore
export const Div: FC<DivProps> = ({
  width, height, style: _style, ...props
}) => {
  const style = {..._style, width, height}
  return <div {...props} style={style} />
}

 

 구성한 Div 컴포넌트를 토대로 컴포넌트를 실제로 사용한다.

// height 속성을 사용하는 예를 확인할 수 있다.
import {Title, SubTitle, Div, Icon} from '../components'

export default function DivTest() {
  return (
    <section className="mt-4">
      <Title>CopyMe</Title>
      <Div className="text-center text-blue-100 bg-blue-600" height="6rem">
        <Icon name="home" className="text-3xl" />
        <SubTitle>Home</SubTitle>
      </Div>
    </section>
  )

 

실행결과 - 개발자도구에 style 속성 중 height: 6rem;이 정상적으로 들어간 것을 확인할 수 있다.

 

  다음으로는 Div의 Height를 명시적으로 설정하지 않고 개발자 도구를 통해 높이를 확인해 보자.  상단 Div 속성 중 height="6rem"를 제거한다.

실행결과 : div영역의 높이가 103px로 변경된 것을 확인할 수있다. 즉 컨텐츠 자체가 갖고 있는 높이로 변경 되었음을 확인할 수 있다.

 

 

  이렇게 높이를 명시하지 않고 브라우저가 컨텐츠 영역의 크기만큼만을 할당하도록 구현하는 것이 더 바람직하다.

 

 컨테이너 콘텐츠, 그리고 box-sizing 스타일 속성

  HTML에서는 부모/자식 요소로 관계를 구분해 표현한다.  하지만 CSS는 렌더링 중심이기 때문에 부모요소를 컨테이너(container), 자식요소를 콘텐츠(content) 라고 한다.

컨테이너

  CSS에서는 테두리와 패딩을 무시한 콘텐츠 영역의 크기만을 컨테이너 크기로 생각햇다.  그러므로 CSS 표준은 box-sizing이란 스타일 속성을 제공하여 컨테이너 크기를 설정할 수 있다.  box-sizing 속성의 설정 값은 content-box, padding-box, border-box, inherit이 있으며 기본 값은 content-box이다.

 

  테일윈드 CSS에서는 설정 값에 대응하기 위해 box-border(border-box), box-content(content-box) 클래스를 제공한다.

 

 

 캐스케이딩 알아보기

  캐스캐이딩은 사전적인 의미로 "위에서 아래로 물이 계단을 따라 흘러내린다" 라는 의미이다.  즉 부모 요소에 설정한 스타일 속성 값은 자식 요소에서 명시적으로 속성 값이 다르게 설정되지 않았다면 부모 요소에서 설정한 속성 값을 따른다는 의미이다.

 

 뷰포트 알아보기

  뷰포트(viewport)란 웹 페이지에서 사용자가 볼 수 있는 영역이다.  뷰포트는 PC, 모바일, 테블릿 등의 장치의 화면 크기가 각각 다르기 때문에 생긴 개념이며 넓이는 'viewport width'라는 의미로 vw, 높이는 'viewport height'라는 의미로 vh라는 단위를 사용한다.  테일윈드CSS는 뷰포트의 크기를 지정하는 클래스 2개와 퍼센트 단위를 사용하는 클래스 2개를 제공한다.

클래스 이름 의미
w-screen width: 100vw;
h-screen height: 100vh;
w-full widht: 100%;
h-full height: 100%;
import {Title} from '../components'

export default function CopyMe() {
  return (
    <section className="w-screen h-screen mt-4 bg-indigo-900">
      <Title>ViewportTest</Title>
    </section>
  )
}

뷰포트 실행결과에 따른 width heigth사이즈 확인

 

 테일윈드CSS의 길이 관련 클래스

  테일윈드CSS는 width를 의미하는 'w', height를 의미하는 'h' 뒤에 숫자를 쓰는 표기법을 가진 클래스들도 제공한다.

 숫자의 단위는 rem이며 w-숫자, h-숫자의 기준은 4로서, w-4는 1rem('M'글자크기)이다.  즉 w-1은 0.25rem을 의미한다.

  w-1/2형태로 퍼센트 단위 길이를 표현할 수도 있다.  50%이며 w-1/2는 부모 요소의 width, height 50%값으로 설정 된다.

// 'w'는 문자의 넓이, 'h'는 문자의 높이
w-숫자, h-숫자
// 분모는 2~6까지, 분자는 1~분모-1까지
w-분자/분모, h-분자/분모,
import {Title, Div} from '../components'

export default function CopyMe() {
  return (
    <section className="mt-4">
      <Title>HeightTest</Title>
      <Div className="h-40 mt-4 text-center bg-blue-500">
        <Div className="bg-blue-500 h-1/2">
          <p className="text-center text-red-50">h-1/2</p>
        </Div>
        <Div className="bg-red-500 h-1/2">
          <p className="text-center text-red-50">h-1/2</p>
        </Div>
      </Div>
    </section>
  )
}

 

실행결과

 

 padding 스타일 속성

  패딩(padding) 속성은 콘텐츠(content)과 테두리(border) 사이의 간격을 패딩 영역의 크기로 설정할 수 있다.  CSS는 padding-left, padding-right, padding-top, padding-bottom 스타일 속성을 제공한다.

 

  padding은 padding-방향 형태의 속성들이 같은 값을 가질 때 더 쉽게 패딩 값을 설정할 수 있다.  padding: 1rem;은 padding-방향 형태의 속성값을 모두 'M'글자 1개의 크기(1rem)로 설정한다.

 

 테일윈드CSS에서는 패딩관련 클래스 padding을 의미하는 'p-'뒤에 숫자를 붙이며 분수는 허용하지 않는다.  padding은 수평을 의미하는x, 수직을 의미하는 y도 쓸수 있다.

p(x | y | t | l | b | r)-숫자
import {Title} from '../components'
import * as D from '../data'

export default function CopyMe() {
  const sentence = D.randomSentence()
  return (
    <section>
      <Title>PaddintgTest</Title>
      <div className="p-8">
        <div className="text-white bg-sky-600">
          <p>{sentence}</p>
        </div>
        <div className="p-8 text-white bg-orange-600">
          <p>{sentence}</p>
        </div>
      </div>
    </section>
  )
}

실행결과

 

 margin 스타일 속성

  margin 스타일 속성은 margin-left, margin-right, margin-top, margin-bottom 스타일 속성을 합쳐 놓은 단축 속성이다.  해당 속성은 HTML 요소와 인접한 요소간의 간격을 결정하는 스타일 속성이다.

 

  테일윈드CSS 마진 관련 클래스는 'm-' 뒤에 숫자를 붙이며 분수는 지원하지 않는다.

// width와 동일하게 m-4는 margin: 1rem; 을 의미한다.
m-숫자
// 수평방향 x, 수직방향 y를 쓸 수 있다.
m(x | y | t | l | b | r) - 숫자
import {Title} from '../components'
import * as D from '../data'

export default function MarginTest() {
  // react JSX
  const boxes = D.makeArray(10).map((notUsed, index) => (
    // React JSX에서는 JavaScript 코드를 삽입할 때 중괄호 {}를 사용하지만
    // JSX에서 반환하는 요소가 여러 줄인 경우 괄호 ()를 사용하는 것이 권장
    <div key={index} className="inline-block w-8 h-8 m-4 bg-pink-300" />
  ))
  return (
    <section className="mt-4">
      <Title>MarginTest</Title>
      <div className="mt-4 bg-blue-700">{boxes}</div>
    </section>
  )
}

실행 결과

 

 background-image 스타일 속성

  이미지 태그는 HTML 요소 중 유일하게 width, height 속성이 있어 이미지의 가로, 세로 화면 비율에 맞춰 표시를 한다.  이미지가 로딩되기 전에 width, height를 통해 이미지 크기를 확보 함으로서 화면의 높이가 변하는 현상을 극복할 수 있다.
  다만 이렇게 이미지를 특정 크기로 고정하는 것은 번거롭고 어렵기 때문에 background-image 속성을 선호한다.

import {Title} from '../components'
import * as D from '../data'

const src = D.randomImage(3000, 1600)
export default function ImageTest() {
  return (
    <section className="mt-4">
      <Title>ImageTest</Title>
      <img src={src} className="bg-gray-300" width="400" height="400" />
    </section>
  )
}

 

  Div 컴포넌트에 src 속성 추가

import {FC, DetailedHTMLProps, HTMLAttributes, PropsWithChildren} from 'react'
import {WidthHeight} from './WidthHeight'

export type ReactDivProps = DetailedHTMLProps<
  HTMLAttributes<HTMLDivElement>,
  HTMLDivElement
>
export type DivProps = ReactDivProps &
  PropsWithChildren<WidthHeight> & {
    src?: string
  }

// prettier-ignore
export const Div: FC<DivProps> = ({
  width, height, style: _style, src, className: _className, ...props
}) => {
  const style = {..._style, width, height, backgroundImage: src && `url(${src})`}
  // bg-gray-300는 이미지 로딩 실패를 알 수 있게 함
  const className = ['box-sizing', src && 'bg-gray-300', _className].join(' ')
  return <div {...props} className={className} style={style} />
}
import {Title, Div, SubTitle} from '../components'
import * as D from '../data'

const src = D.randomImage(1200, 400)
export default function backgroundImageTest() {
  return (
    <section className="mt-4">
      <Title>backgroundImageTest</Title>
      <Div className="mt-4 bg-gray-300 h-80" src={src}>
        <SubTitle className="text-gray-500">Some Test here</SubTitle>
      </Div>
    </section>
  )
}

실행결과는 div같은 컨테이너의 배경이미지로 쓰임을 짐작할 수 있다.

 

 Background-size 스타일 속성

  CSS는 전체 이미지를 볼 수 있게 background-size 스타일을 제공하며 테일윈드CSS는 밑의 클래스를 지원한다.

클래스 이름 의미
 bg-auto  background-size : auto;
 bg-cover  background-size : cover;
 bg-contain  background-size : bg-contain;

 

 border 스타일 속성

  css 상자 모델은 HTML 요소가 차지 하는 테두리(border)를 설정할 수 있다.  border 스타일 속성은 border-width, border-style, border-color의 단축 속성이며 사용하는 방법은 다음과 같다.

// 테두리가 4px이고 타입은 직선, 색상은 빨강으로 표시
border: 4px soild red;

border : border-width || border-style || border-color;

 

  테일윈드 CSS는 border, border-style, border-color와 관련해서 클래스를 제공한다.

 그리고 bg-red-500 형태처럼 bg 접두사만 border로 바꾼 패턴 클래스도 제공한다.

border-style의 표시 예시

클래스 이름 의미 비고
 border  border-width: 1px;  border-(0 | 2 | 4 | 8)
 border-t  border-top-width : 1px;  border-t-(0 | 2 | 4 | 8)
 border-r  border-right-width : 1px;  border-r-(0 | 2 | 4 | 8)
 border-b  border-bottom-width : 1px;  border-b-(0 | 2 | 4 | 8)
 border-l  border-left-width: 1px;  border-l-(0 | 2 | 4 | 8)
border-solid border-style: solid;  
border-dashed border-style: dashed;  
border-dotted border-style: dotted;  
border-double border-style: double;  
border-none border-style: none;  
border-transparent boarder-color: transparent;  
border-black --tw-border-opacity: 1;
border-color: rgba(0, 0, 0, var(--tw-border-opacity));
border-white --tw-border-opacity: 1;
border-color: rgba(255, 255, 255, var(--tw-border-opacity));
border-red-500 패턴 클래스 제공

 

 border-radius 스타일 속성

  css는 border의 모서리 부분을 둥글게 마드는 border-radius 스타일 속성을 제공한다.  이 스타일의 속성 값은 픽셀, 퍼센트, em, rem 등의 단위를 사용할 수 있다.  테일윈드CSS도 border-radius 스타일에 대응하는 클래스를 제공한다.

  또한 위쪽이나 아래쪽 모서리만 둥글게 하는 클래스 패턴과 한족 모서리만 둥글게 하는 클래스도 패턴으로 제공한다.

클래스 이름 의미
rounded border-radius : 0.25rem;
rounded-full border-radius : 9999px;
rounded-sm border-radius : 0.125rem;
rounded-md border-radius : 0.375rem;
rounded-lg border-radius : 0.5rem;
rounded-xl border-radius : 0.75rem;
rounded-2xl border-radius : 1rem;
rounded-(t | b)-(sm | md | lg | xl | 2xl ...) 위, 아래쪽 모서리만 둥글게 하는 클래스 패턴
rounded-(tl | tr | bl | br) -(sm | md | lg | xl | 2xl ...) 한 쪽 모서리만 둥글게 하는 클래스 패턴

 

 Avatar 컴포넌트 만들기 ( 다시 실습 필요)

  rouded-full 클래스를 적용해 동그란 원 모양의 이미지를 생성하는 컴포넌트를 만든다.

import type {FC} from 'react'
import {Div} from './Div'
import type {DivProps} from './Div'

export type AvatarProps = DivProps & {
  size?: string
}

// prettier-ignore
export const Avatar: FC<AvatarProps> = ({
  className: _className, style, src, size, ...props
}) => {
  const w_or_h = size ?? '3rem'
  const className = ['rounded-full bg-cover bg-gray-300', _className].join(' ') 
  return (
    <Div
      {...props}
      src={src}
      width={w_or_h}
      height={w_or_h}
      className={className}
      style={style} />
  )
}

 

  컴포넌트를 통한 화면을 표시할 tsx를 편집해 실행한다. ( 교재 소스와 비교해 보아도 틀린 부분이 없는데 라운딩처리가 안된다.... (확인필요)

import {Title, Div, Avatar} from '../components'
import * as D from '../data'

export default function AvatarTest() {
  const avatars = D.range(0, 10).map(index => (
    <Avatar
      className="inline-block -ml-6 border-4 border-white"
      key={index}
      src={D.randomAvatar()}
    />
  ))
  return (
    <section className="mt-4">
      <Title>AvatarTest</Title>
      <Div className="px-12 py-4 m-8 bg-blue-300">{avatars}</Div>
    </section>
  )
}

실행결과 : 실습실패 오류 찾는데 시간이 오래 걸려서 우선 학습 후 다시 확인하기로 함.

 

 display 스타일 속성

  css의 display 속성은 HTML 요소의 배치를 결정하는 중요한 요소이며 테일윈드CSS에서도 자주 사용하는 display요소를 클래스로 제공한다.

클래스 이름 의미 설명
 hidden  display : none;  화면에 표시되지 않으며 요소의 width, height 속성값이 적용되지 않는다.
 block  display : block;  수직으로 배치되는 HTML요소를 의미한다. 대표적으로 <div>요소이다.
width와 height 속성값을 명시적으로 설정할 수 있다.
 inline  display : inline;  요소를 수평으로 배치하며 더 이상 배치할 수 없을 때 줄을 바꿔 다시 배치한다.
즉 이 요소는 width와 height 스타일 속성값을 명시적으로 설정할 수 없다.
 inline-block  display : inline-block;  inline과 block을 결합한 것이다.  inline이므로 수평으로 배치되지만 block이므로
width와 height 속성값을 설정할 수 있다.
 flex  display : flex;  

 

 

 

- inline-block 요소는 width, height 스타일 속성값이 적용되고
  자식 요소가 수평인 것을 확인할 수 있다.

- inline 요소는 수평으로 배치되지만 width, height  스타일 속성이
  적용되지 않을 것을 확인할 수 있다.

- block 요소는 자식 요소가 수직으로 배치되고 width, height 스타일
  속성이 적용된 것을 확인할 수 있다.

 

 visibility 스타일 속성

  CSS의 visibility는 HTML요소를 화면에 나타나지 않게 할 때 사용하며 스타일 속성에는 visible, hidden이 존재한다.  앞서 나온 display:none과의 차이점은 요소의 width, height 요소값이 적용된다는 점이며 테일윈드CSS의 클래스는 다음과 같다.

클래스 이름 의미
visible  visivility : visible;
invisible  visivility : hidden;
import {Title} from "../components"

export default function DisplayNoneTest() {
  return (
    <section className="mt-4">
      <Title>DisplayNoneTest</Title>
      <div className="mt-4">
        <p className="visible">visibility: visible text</p>
        <p className="invisible">visibility: hidden text</p>
        <p className="hidden">display: none text</p>
      </div>
    </section>
  )
}

 

왼쪽 실행결과는 display:none이며 오른쪽 실행결과는 visibility:hidden의 실행결과이다.

 

 position left, top, right, bottom 스타일 속성

  CSS는 left, right, top, bottom 등 HTML 요소의 위치를 표현하는 스타일 속성이 있으며 해당 속성을 사용하기 위해서는 positon 스타일 속성에 absolute라는 값이 설정되어야 한다.  테일윈드도 해당 속성을 지원하는 클래스를 제공한다.

클래스 이름 의미 설명
 absolute  position : absolute; 좌표가 되는 기준이 존재해야 한다.  미 설정 시 <html> 태그를 기준으로 한다.
 relative  position : relative;  

 

  우선 position의 속성들을 props로 선언한다.

export type leftRightTopBottom = {
  left?: string
  right?: string
  top?: string
  bottom?: string
}

 

  해당 leftRightTopbottom를 DivProps에 추가하고 컴포넌트의 내용을 일부 수정한다.

import {FC, DetailedHTMLProps, HTMLAttributes, PropsWithChildren} from "react"
import {WidthHeight} from "./WidthHeight"
import {leftRightTopBottom} from "./LeftRightTopBottom"

export type ReactDivProps = DetailedHTMLProps<
  HTMLAttributes<HTMLDivElement>,
  HTMLDivElement
>
export type DivProps = ReactDivProps &
  PropsWithChildren<WidthHeight> &
  leftRightTopBottom & {
    src?: string
  }

// prettier-ignore
export const Div: FC<DivProps> = ({
  width, height, style: _style, src, className: _className,
  left, right, top, bottom, ...props
}) => {
  const style = {
    ..._style, width, height, backgroundImage: src && `url(${src})`,
    left, right, top, bottom
  }
  // bg-gray-300는 이미지 로딩 실패를 알 수 있게 함
  const className = ['box-sizing', src && 'bg-gray-300', _className].join(' ')
  return <div {...props} className={className} style={style} />
}

 

  마지막으로 Div 컴포넌트를 통해 positon를 화면에 표시할 소스를 추가한다.

import {Title, Div, Icon} from "../components"
import * as D from "../data"

const src = D.randomImage(800, 500)

// prettier-ignore
export default function PositionTest() {
  const icons = ['home', 'search', 'settings', 'favorite'].map(name=> (
    <Icon key={name} name={name} className="mr-2" />
  ))
  return (
    <section className="mt-4">
      <Title>PositionTest</Title>
      <Div className="relative border-2 border-gray-500" src={src} height="10rem">
        <Div className="absolute p-2 text-white bg-blue-500" left="1rem" top="1rem">{icons}</Div>
        <Div className="absolute p-2 text-white bg-blue-500" right="1rem" top="1rem">{icons}</Div>
        <Div className="absolute p-2 text-white bg-blue-500" left="1rem" bottom="1rem">{icons}</Div>
        <Div className="absolute p-2 text-white bg-blue-500" right="1rem" bottom="1rem">{icons}</Div>
      </Div>
    </section>
  )
}

 

실행 결과

 

 z- index 스타일 속성

  CSS에서 z-index는 HTML이 화면에 그려질 때 어떤 요소가 위에 올라가는지를 설정하는 속성이다.  테일윈드 CSS에서제공하는 클래스는 z-0~50, z-auto가 존재한다.

 

 Overlay 컴포넌트 만들기

  모달 대화상자가 나타날 때 웹 페이지의 다른 곳을 사용자가 임의로 클릭할 수 없게 하는 화면 UI를 오버레이라고 한다.

 z-index, position 등 요소들을 조합해 컴포넌트를 생성한다.

import type {FC} from "react"
import type {ReactDivProps} from "./Div"
import {Div} from "./Div"

export type OverlayProps = ReactDivProps & {
  opacityClass?: string
}
 // prettier-ignore
export const Overlay: FC<OverlayProps> = ({
  className: _className, opacityClass, children, ...props
}) => {
  const className = [
    _className, "absolute z-50 w-screen h-screen",
    opacityClass ?? "bg-black/70",
    "flex items-center justify-center"].join(" ")
  // prettier-ignore
  return (
    <Div {...props} className={className} top="0" left="0">{children}</Div>
  )
}

 

 생성한 컴포넌트를 통해 화면에 표시하기 위해 소스를 추가한다.

import {Title, Div, Icon, Overlay} from "../components"

export default function OverlayTest() {
  return (
    <section className="mt-4">
      <Title>OverlayTest</Title>
      <Overlay opacityClass="bg-black/70">
        <Div className="relative flex items-center justify-center p-8 bg-white h-1/2">
          <Div className="absolute" right="1rem" top="1rem">
            <Icon name="close" className="text-gray-500" />
          </Div>
          <p className="text-5xl">bodal dialog box</p>
        </Div>
      </Overlay>
    </section>
  )
}

실행 결과