티스토리 뷰

구조적인 CSS 작성 방법의 발전

CSS -> SASS -> BEM -> CSS Modules -> CSS-in-Js(Styled Components)

 

구조화된 CSS가 필요하게 된 이유

- 프로젝트의 규모와 복잡도가 커지며 함께 작업할 팀원들이 많아짐

- 모바일이나 태블릿 등 다양한 디스플레이 등장

 

CSS 전처리기(CSS Preprocessor)

  • 컴파일을 통해 CSS문서로 변환(각 CSS 전처리기에 맞는 Compiler를 사용해야 함)
  • CSS의 문제(유지관리 어려움)를 프로그래밍 개념(변수, 함수, 상속 등)을 활용해 해결
    • 반복적인 작업
    • 번거로운 작업(Color 찾기, Tag 닫기 등)
    • 클래스와 상속 등으로 인한 CSS 문서 양 증가

 

SASS(Syntactically Awesome Style Sheets)

CSS를 확장해주는 스크립팅 언어, 전처리기

SCSS(Sassy CSS) 코드를 전처리한 다음 컴파일해서 전역 CSS 번들 파일을 만들어줌

특정 속성(ex. color, margin, width 등)의 값(ex. #ffffff, 25rem, 100px)을 변수로 선언하여 여러 곳에서 재사용

 

$ 변수 사용 예시

// css
.alert {
  border: 1px solid rgba(198, 83, 140, 0.88);
}

.button {
  color: rgba(198, 83, 140, 0.88)
}

// SASS 변수 사용 예제
$base-color: rgba(198, 83, 140, 0.88)

.alert {
  border: 1px solid $base-color
}

.button {
  color: $base-color
}

 

SASS 단점

전처리기가 내부에서 하는 작업을 정확히 알 수 없음,

내부 작업을 알지 못한채 스타일이 겹치는 문제를 해결하기 위해 계층 구조를 만들어 내는데 의존하게 되었고,

그 결과 컴파일된 CSS의 용량이 커지게 됨

 

CSS 전처리기의 문제를 보완하기 위해 BEM, OOCSS, SMACSS 같은 CSS 방법론 제기

공통 지향점

- 코드의 재사용성

- 코드의 간결화(유지보수 용이)

- 코드의 확장성

- 코드의 예측성(클래스명으로 의미 예측)

 

팀원들과 협업 시 CSS 작성에 앞서 작성 방법들을 규칙으로 정해두는 것이 중요함

 

BEM

Block__Element--Modifier로 구분하여 클래스를 작성하는 방법

Block: 전체를 감싸고 있는 블럭 요소

Element: 블럭이 포함하고 있는 한 조각

Modifier: 블럭 또는 요소의 속성 (블록이나 엘리먼트의 외관이나 상태를 변화 가능하게 하는 부분)

 

장점

BEM 방식의 이름을 여러번 반복하여 재사용할 수 있도록 하며,

HTML/CSS/SASS 파일에서도 일관된 코딩 구조를 만들어줌

 

단점

클래스명 선택자가 장황해짐 -> 마크업이 불필요하게 커짐

재사용 때마다 모든 UI 컴포넌트를 명시적으로 확장해야만 함

캡슐화 개념이 없어 유일한 클래스명 선택에 의존해야 했음

(캡슐화: 객체의 속성과 행위를 하나로 묶고 실제 구현 내용 일부를 외부로부터 감추어 은닉)

 

애플리케이션으로 개발 방향이 진화하면서(= 컴포넌트 단위 개발) 캡슐화의 중요성을 불러옴

CSS도 컴포넌트 기반으로 작성하기 위해 CSS-in-Js가 탄생

대표적인 라이브러리: Styled-components

-> 기능적 혹은 상태를 가진 컴포넌트들로부터 UI를 완전히 분리하여 사용할 수 있는 패턴 제공

 

  CSS SASS BEM Styled-Component
특징 기본적인
스타일링 방법
프로그래밍 방법론을 도입하여
컴파일된 CSS를 만들어내는 전처리기
CSS 클래스명 작성에
일관된 패턴을 강제하는 방법론
컴포넌트 기반으로
CSS를 작성할 수 있게
도와주는 라이브러리
장점 - 변수/함수/상속 개념을 활용하여
재사용 가능,
CSS의 구조화
네이밍으로 문제 해결,
전처리 과정 불필요
CSS를 컴포넌트 안으로
캡슐화,
네이밍이나 최적화를
신경 쓸 필요 없음
단점 일관된 패턴을
갖기 어려움
!important*의 남용
전처리 과정이 필요,
디버깅이 어려움,
컴파일한 CSS 파일이 거대해짐
선택자의 이름이 장황하고,
클래스 목록이 너무 많아짐
빠른 페이지 로드에 불리
여러 라이브러리를
사용하게 되므로,
번들의 크기가 커짐

* !important

: css에서 같은 속성을 여러번 정의하면 나중에 정의한 값이 적용됨.

나중에 설정된 값이 적용되지 않게하려면 원하는 속성값 뒤에 !important를 붙인다

 

 

CDD(Component Driven Development)

부품 단위로 UI 컴포넌트를 만들어 나가는 개발(ex. BBC, UN 사이트)

조립, 재사용

React 등의 라이브러리가 나오면서 컴포넌트 단위 개발이 주류가 되었는데 CSS는 그러지 못했음

 

CSS 불편한 점

- class, id 이름을 짓느라 고민

- css 파일에서 원하는 부분을 찾기 힘듦

- css 파일이 너무 길어져서 파일을 쪼개어 관리하게 되는 경우

- 스타일 속성이 겹쳐서 원하는 결과가 나오지 않는 경우

-> CSS를 컴포넌트화 시킴으로써 해결할 수 있음

 

이에 CSS in JS라는 개념이 나오면서 하나의 JS 파일로 HTML, JS, CSS를 묶어 컴포넌트 단위로 개발할 수 있게 됨

그 중 유명한 라이브러리가 'Styled Components'

 

 

Styled-Components

설치

npm install --save styled-components
// 또는
yarn add styled-components

 

package.json에 추가 -> 여러 버전의 styled-components가 설치되어 생기는 문제를 줄여줌

{
  "resoulutions": {
    "styled-components": "^5"
  }
}

파일 내부 임포트

import styled from "styled-components"

vscode extentions: vscode-styled-components

styled-components 문자열을 css형식으로 보여줌

 

Styled Components 문법

Template Literals 문법 사용

- 백틱(`)으로 감싸주고 내부는 기존 css형식으로 사용

- 변수를 사용하려면 ${}

 

1. 컴포넌트 만들기

const 컴포넌트이름 = styled.태그종류 `
  css속성1: 속성값;
  css속성2: 속성값;
`;

 

2. 이미 만들어진 컴포넌트를 활용하여 새 컴포넌트 만들기

const 새컴포넌트이름 = styled(기존컴포넌트) `
  추가할css속성1: 속성값;
  추가할css속성2: 속성값;
`;

3. props 활용하기

const 컴포넌트이름 = styled.태그종류 `
  css속성: ${(props) => JS코드}
`

// JS코드 예시
// 삼항연산자
props? props.color : "white"
// ||
props.color || "white"

4. (컴포넌트 말고) 전역 스타일 설정하기

최상위 컴포넌트에서 사용

import {createGlobalStyle} from "styled-components"

const GlobalStyle = createGlobalStyle `
  button {
    속성: 속성값;
  }
`

function App() {
  return (
    <GlobalStyle />
    // 생략
  )
}

5. 셀렉터, & 사용

const ToggleContainer = styled.div`
  position: relative;
  margin-top: 8rem;
  left: 47%;
  cursor: pointer;

  // div 자식 중 toggle-container 클래스를 가진 요소에 적용
  > .toggle-container {
    width: 50px;
    height: 24px;
    border-radius: 30px;
    background-color: #8b8b8b;
  // div 자식 중 toggle-container 클래스를 가지면서 toggle--checked 클래스도 가진 요소에 적용
    &.toggle--checked {
      background-color: var(--coz-purple-600);
    }
  }
`

 

6. 속성 추가하기(attrs)

export const ModalView = styled.div.attrs((props) => ({
  // attrs 메소드를 이용해서 div 엘리먼트에 속성을 추가하기
  role: 'dialog',
}))`
  background-color: white;
  color: var(--coz-purple-600);
  height: 30%;
  width: 50%;
  padding: 20px;
  border-radius: 30px;
`;

// <div role={dialog}></div>

 

 

useRef(Hook)

React 애플리케이션을 만들 때 DOM을 직접 조작하는 것을 지양해야하지만

직접 건드려야하는 상황이 발생하기도 함(= DOM 엘리먼트의 주소값을 활용해야하는 경우)

- focus(커서), text selection, media playback(play, pause, remove)

더보기

useRef를 특정 요소에 지정하면 그 요소는 리렌더링이 되지 않기 때문에 값이 유지됨

(돔에서 주소값을 가져오면 상태를 만들어주지 않아도 될 때 사용)

 

세 input값을 입력받아 한번에 제출하는 경우 포커스의 위치를 상태로 관리한다면(상태 +1)

포커스 위치가 바뀔 때마다 상태가 변하게 되므로 리렌더링이 일어나서 이전 입력값이 없어짐

입력값이 없어지는 것을 막기 위해 인풋마다 상태를 줘야함(상태 +3, 총 4)

 

input 요소에 useRef를 사용하고 포커스의 위치를 상태로 관리해주면

리렌더링이 일어나지 않아 입력값이 바뀌지 않는다

(상태 1, useRef 3 => 상태 관리(setState)해줄 필요 없이 제출 시 event.target.value를 받으면 됨)

-----

 

동영상을 재생/일시정지를 상태로 관리한다면(상태 +1)

재생/일시정지 때마다 리렌더링이 일어나 항상 처음부터 재생됨

재생 중인 시간을 기억하기 위한 상태를 추가로 만들어줘야 함(상태 +1, 총 2)

 

video 요소에 useRef를 주면 상태값을 주지 않고도 재생/일시정지를 사용할 수 있음

 

 

- 애니메이션 적용

- d3.js, greenSock 등 DOM 기반 라이브러리 활용

 

 

useRef로 DOM 노드, 엘리먼트, 컴포넌트 주소값을 참조할 수 있으며,

useRef로 저장된 주소값은 컴포넌트가 리렌더링돼도 바뀌지 않음

-> 이 특징 때문에 렌더링 여부에 상관없이 값을 유지하고 싶을 때 사용하기도 함

(상태를 사용하는 것이 더 번거로운 경우)

const 주소값을담을변수 = useRef(초기값)
return (
  <div>
    <input ref={주소값을담을변수} type="text" />
  </div>
)

// 주소는 {current: 요소}로 저장되어서 아래처럼 접근
주소값을담을변수.current

 

제시된 상황 외에 useRef를 남용하는 것은 부적절하고,

React 특징인 선언형 프로그래밍 원칙과 배치되기 때문에 조심해서 사용해야 함

 

 

 

 

22.12.22

코스 S3U3 CDD 진행중

'코드스테이츠(SEB_FE_42)' 카테고리의 다른 글

[React] storybook(CDD)  (2) 2022.12.23
[JS] XOR 연산  (0) 2022.12.22
[사용자 친화 웹] Figma  (0) 2022.12.20
[사용자 친화 웹] UI, UX, Wireframe, Prototype  (0) 2022.12.19
[자료구조/ 알고리즘] JSON (+재귀)  (0) 2022.12.16
댓글
공지사항