css 심화 with react
용어 정리
파싱: 코드를 분석하여 이해하기 좋은 구조로 만드는 것
렌더링: HTML, CSS, 자바스크립트로 작성된 문서를 파싱하여 브라우저에 시각적으로 출력
----------------------------------------------------------------------------------------
랜더 트리를 그리기 위해선
dom을 완성하고 cssom 을 그려야 한다.
하지만 우리에겐 js 또한 있다
한줄씩 실행하다 보면
html 읽으면서 dom 만들고 css 만나면 cssom 만들고
스크립트 만나면 자바스크립트 코드를 실행하기 위해 렌더링 엔진에서 자바스크립트 엔진으로 제어권을 넘겨주고 종료시 이어서 dom 을 생성해 준다.
자바스크립트 엔진은 DOM API dom, CSSOM 을 변경해줌 이때 DOM과 CSSOM은 다시 렌더 트리로 결합되고 변경된 렌더 트리를 기반으로 레이아웃과 페인트 과정을 거쳐 브라우저의 화면에 렌더링
우리가 react 파일을 build 하면 나오는 html
이렇기 때문에 react 는 js 라이브러리 이다.
여기서 과거에 배웠던
CSS 태그를 상단에 위치시키는 이유는 사용자에게 우리가 원하는 화면을 빠르게 보여주기 위함입니다. CSSOM이 구성되어야 브라우저가 렌더링을 하기 때문에 CSSOM 트리를 빠르게 구성할수록 사용자가 흰 화면을 보는 시간이 줄어들게 됩니다. 또한 외부 스타일 시트를 다운받아 사용하는 경우가 많기 때문에 스타일 시트를 최대한 빠르게 다운받기 위해 CSS 태그를 상단에 위치시킵니다.
다음으로 script 태그를 하단에 위치시키는 이유는 먼저 HTML 파서가 script 태그를 만나면 파싱을 멈추고 스크립트를 읽기 때문에 웹페이지의 로딩이 그만큼 늦춰질 수 있습니다. 따라서 웹 페이지의 로딩을 빠르게 하기 위해 하단에 위치시킵니다. 또다른 중요한 이유는 미처 생성되지 않은 DOM 노드를 읽거나 조작하는 것이 불가능하기 때문에 예상치 못한 오류가 발생할 수 있기 때문에 이를 예방하기 위해서 하단에 위치 시킵니다.
script 태그를 중간에 삽입하게 되면 DOM 트리가 생성되기 전에 DOM 요소를 조회하게 됩니다. 그러나 DOM에 id가 greeting인 요소는 아직 생성되지 않았기 때문에 다음과 같이 정상적으로 조회하지 못함을 알 수 있습니다.
반면 script 태그를 body 태그 밑에 위치시키면 DOM 요소를 생성한 후 조회를 하기 때문에 정상적으로 조회가 가능한 것을 확인할 수 있습니다.
대충 이랬던 내용과 다르게 build 된 파일을 보니 head 안에 script 가 들어가 있다
<script type="module" crossorigin src="/assets/index-XXXX.js"></script>
type="module"을 사용하는 스크립트는 자동으로 defer 속성이 적용돼.
defer의 의미:
HTML 파싱은 그대로 쭉 진행하고,
스크립트는 파싱 끝난 후 실행됨 (DOMContentLoaded 전에 실행)
그래서 문제 없음:
- HTML 파싱 멈추지 않음 ✅
- DOM 생성 완료 후 JS 실행됨 ✅
- React가 #root를 조작할 때 DOM이 준비되어 있음 ✅
그럼 구지 head 에 둔 이유
- 브라우저가 병렬로 더 빨리 받아오기 시작함
→ <head>에 있으면 더 일찍 fetch 시작됨
→ 빌드된 JS/CSS 파일은 작지 않으니 이게 이점이 됨 - 모듈 스크립트는 defer가 자동이므로 파싱 방해 안 함
- 초기 렌더링 최적화 (Time To Interactive 단축)
즉, React 앱이 실행되는 시점에는 DOM이 이미 다 만들어져 있기 때문에,
#root를 찾고 마운트하는 것도 아무 문제 없음.
좋아보이지?
하지만 이로써 생기는 이슈
(이거때문에 정리 시작)
새로고침시 다크모드일때 배경이 하얀색으로 바뀌었다가 검점색으로 바뀌는 깜빡임이 생길수 바께 없는 구조
이걸 해결하기 위해 css 를 깊게 파보자
이런식으로 그려주는대 우리가 리액트 쓰는 이유
dom 자체는 빠르나 DOM에 변화가 생기면, CSS를 다시 연산하고 레이아웃을 구성하고, 페이지를 리페인트 하는 과정이 오래걸림
따라서 이건 많이본 가상 dom을 사용
그럼 css 를 react 에서 어떻게 다루는가
1. 전통적인 CSS / SCSS (CSS 파일 import) 전통이라기엔 덕지덕지 여러 기술이 붙은 키메라가 되어가는중
CSS 파일은 번들링 도구(Vite, Webpack 등)가 처리
여기 안에서도 여러가지 기술이 있다.
css 전처리기 를 통해 css를 편하게 만들고 ( Sass, LESS, Stylus ) 컴파일을 통해 css 로 만든다
그후 후처리기 PostCSS 를 사용, JavaScript 플러그인을 활용하여 CSS를 변환하는 도구로, 자동 접두사 추가, 최신 CSS 문법 변환 등의 기능을 제공합니다 최신 css는 변수도 지원한다고? 이런걸 사용해서 더 나오긴하는대 (tailwind) 뒤에서보자
이런식으로 그냥 css 를 만들어다가 넣을수도 있지만
CSS의 속성은 어디서든 선언 가능하며 모든 프로젝트에 영향을 줍니다.
따라서, 기존에 사용한 이름을 피해서 작성해야 합니다. < 유지보수시 이게 생각보다 빡친다
HTML에서 지정한 class 순서가 아니라 CSS의 순서에 의해 서식 우선순위가 결정됩니다.
>> 그래서 퍼블리셔는 하나의 globalcss 파일을 선호한다고 (밑에 무한 덮어씨우기...)
프로젝트가 커지다 보니 이름이 길어지고(bem), 어디에 중첩(scss)되면 바꾸기도 힘들고 해서 모듈화를 하기 시작
2. CSS Modules
파일마다 스코프가 분리됨 (클래스명이 해시됨)
하지만 모듈화된 CSS는
CSS 파일을 만들어 관리해야 하고, 렌더링 과정에서 자동 변환된 클래스명이 코드의 가독성을 저해하는 경우도 있습니다.
https://www.npmjs.com/package/classnames
classnames
A simple utility for conditionally joining classNames together. Latest version: 2.5.1, last published: a year ago. Start using classnames in your project by running `npm i classnames`. There are 45936 other projects in the npm registry using classnames.
www.npmjs.com
이런거 써서 보완해 주기도 한다 (퍼블리싱 반영할때 쓰면 편할듯)
하지만 여전히
CSS는 JS와 분리된 파일에서 관리해야 해서 불편하고
JS의 변수/상태를 직접 활용할 수 없음 (리액트가 나왔다니까)
클래스명이 자동으로 바뀌기 때문에 디버깅 어려움 (이걸로 만든 라이브러리를 수정 할려면,,,)
테마나 동적 스타일링이 어려움
그래서 그냥 CSS를 JS 안에서 쓰면 안 될까? 란 생각으로
3. CSS-in-JS
여기서 잠깐 react 안에서 쓰는 Inline Style 은 css in js 가 아닙니다!
CSS-in-JS의 핵심 개념인 "스타일의 컴포넌트화, 캡슐화, 동적 클래스 생성 을 따르 않음!
Inline Style 기왕이면 쓰지 맙시다
인라인 스타일은 정의된 모든 스타일을 재정의하기 때문에 좋지 않은 관행으로 간주됩니다.
애니메이션을 적용하거나 미디어 쿼리 가 힘듬
( styled-components, emotion, JSS )
- JS 파일 안에서 CSS를 작성하는 기법 또는 패턴.
- 주로 라이브러리를 통해 스타일을 컴포넌트 단위로 다루고, 동적 스타일링과 스코핑을 지원함.
- 클래스명이 없는 만큼, 컴포넌트 단위로 스타일을 캡슐화
예시
vue는 SFC(Single File Component)로 묶어 .vue 파일 내 <style> 태그를 두고 Sass 문법을 통해 CSS를 사용할 수 있는 형태를 취했다. .vue 파일은 vue-loader를 통해 JavaScript로 변환되기 때문에 마찬가지로 JavaScript에서 CSS 파일이 번들링 된다. 따라서 한 파일에서 컴포넌트를 정의하고, 해당 컴포넌트의 스타일은 컴포넌트 파일 하나에 속하도록 했다.
react는 라이브러리의 정체성을 가지고 있기 때문에 다양한 CSS 방법을 적용할 수 있었다. react와 함께 사용할 수 있는 대중적인 CSS-in-JS 라이브러리로는 styled-components 또는 Emotion이 존재한다. styled-components는 컴포넌트 자체를 스타일만 입힐 수 있는 컴포넌트로 만들어 적용하고자 하는 컴포넌트 상위에 감싸서 사용한다. 이는 한 파일 안에 컴포넌트를 정의해 적용할 수 있고, 스타일 컴포넌트만 따로 파일로 분리해 적용할 수도 있다.
개꿀이자나?
라고 생각할 수 있지만 성능 이슈 발생 시작
- 런타임 오버헤드
- 스타일을 동적으로 생성/적용하기 때문에 앱 실행 중(런타임)에 스타일이 처리됩니다.
- 이는 특히 초기 렌더링 시점에 성능 저하를 유발함.
- 번들 사이즈 증가
- 런타임에 필요한 코드가 포함되어 최종 번들 크기가 커짐.
- 스타일 디버깅 어려움
- 스타일이 동적으로 생성되기 때문에 디버깅이나 DevTools에서 확인하기 어려움.
- SSR(서버 사이드 렌더링) 복잡성
- 서버에서 스타일을 추출해야 하는 복잡한 과정이 필요함. (next 나왔다고)
만능처럼 보이지만 유명한 글 하나 추가하면 (번역) 우리가 CSS-in-JS와 헤어지는 이유
(번역) 우리가 CSS-in-JS와 헤어지는 이유
원문: https://dev.to/srmagura/why-were-breaking-up-wiht-css-in-js-4g9b
junghan92.medium.com
메인테이너는 런타임 스타일시트 방식의 런타임 환경에서의 오버헤드와 번들 크기의 증가 등의 성능 문제
가 있기도 하다
그래서 나온게
Zero Runtime CSS-in-JS
- Vanilla Extract
- Linaria
- Astro Scoped Styles
- Astroturf
CSS-in-JS 코드를 CSS 파일로 먼저 변환합니다. 그런 다음 브라우저가 해당 스타일을 읽고 웹 페이지에 적용
= 런타임에 아무 일도 하지 않고, 모든 스타일을 빌드 타임에 미리 처리
모듈이랑 비슷해 보이긴 하지만 js 에서 변수를 받아다 쓸수 있다는게 어마어마하게 크다
state나 props에 따라 color 값을 따로 주거나 다른 style을 적용해야할 때 taged template 문법을 쓰는 styled-components 같은 경우 ${props = > ~~~} 이런식으로 props를 공유 가능하고 타입 까지 지정할 수 있기 때문에 논리적인 에러의 위험도 적다 반면 CSS-Module을 쓰면 styles={state ? "active" : ''} 이런식으로 inline 방식의 분기 처리
빌드시점에서 CSS를 생성해 버려서 첫로드는 빠르지만, 첫 페인트는 느릴수 있다
심지어 Vanilla Extract 경우 타입스크립트에서 타입을 지원해주는 기능도
https://vanilla-extract.style/
vanilla-extract — Zero-runtime Stylesheets-in-TypeScript.
Zero-runtime Stylesheets-in-TypeScript.
vanilla-extract.style
그렇다면, 런타임 환경이 아닌 빌드 타임의 모든 값이 결정되는 zero-runtime에서 동적 변수는 어떻게 생성하는 것일까? 일반적으로는 CSS 변수를 이용해 해결한다.
CSS 변수명만 생성해 두고, 런타임에서 값을 계산한 후, inline으로 CSS 변수를 바로 등록해 스타일을 입힌다.
@vanilla-extract/dynamic 패키지는 1kb밖에 되지 않는 vanilla-extract monorepo 내부의 패키지이므로 성능 저하가 거의 없다. zero-runtime 라이브러리에도 엄밀히 말하면 런타임이 존재할 수 있다.
이쯤되면 이거 다하면 프로젝트 언제 시작하고
생산성이 좋은지도 의문일 시점
독자 노선을 가는 영웅이 있으니
호불호 최강
4. Utility-First CSS (tailwind)
이올시다 (다른 것도 많지만 tailwind 를 가장 많이 보았다 이거 기반 라이브러리도 많고)
Atomic CSS 철학에 기반해, 미리 정의된 utility 클래스를 조합해 UI를 만드는 방식.
클라스를 미리 다 정의해두고
build 시 정리 해서 사용
- 반복되는 스타일 추상화 없이 빠른 UI 개발 가능
- 스타일과 마크업을 한눈에 확인 가능 → 컴포넌트 이해도↑
- 별도의 CSS 파일 작성 불필요 → 파일 정리 및 관리 부담↓
- 디자인 시스템과도 쉽게 연동 가능 (ex: theme 설정, 커스텀 config)
💡 Tailwind CSS Build는 어떻게 이루어지는지 간단하게 알아볼까요
- 소스 코드 스캔 → 프로젝트에서 사용된 클래스를 식별.
- 필터링 → 사용된 클래스만 추출.
- CSS 생성 → Tailwind 설정에 따라 CSS를 동적으로 생성.
- 최적화 (옵션) → 불필요한 CSS 제거 및 압축.
증분 빌드 써서 개발 환경이 매우 빠르다
파일을 저장할 때마다 전체를 다시 빌드하지 않고, 변경 사항만 반영하므로 속도가 매우 빠릅니다.
Internet Explorer 11 지원 중단 이지만 이미 관짝 들어가 있는거 쓰는 사람 없었으면 진짜루,,공공기관 나쁜넘들아
4.0 과 3.0 은 큰 차이가 있으니 공홈 확인해 보시고
테세구! (테일윈드가 세상을 구한다)를 열심히 외쳤으나 지금은 살짝 갸우뚱 한 상태
module도 쓰지마 sass도쓰지마
딱 가볍고 빠르게 내껏만 써!
https://tailwindcss.com/docs/compatibility
Compatibility - Getting started
Learn about browser support and compatibility with other tooling.
tailwindcss.com
그럼 tailwind를 어케 고쳐써야 하지 ?
사실 이걸 몰랐을때는 처음에 외우느라 죽는줄 알았는대 조금만 쓰다 보니까
없으면 답답함
빠르긴 진짜 개발속도가 빠르다
여기서 더 심화로 가기 시작하면 성능 개선쪽을 볼 수도 있다
Critical CSS
Critical CSS란, 사용자에게 가능한 한 빠르게 콘텐츠를 렌더링하기 위해 스크롤 없이 볼 수 있는 콘텐츠에 대한 CSS를 추출하는 것을 말한다.
이런식으로 초기 페이지 css만 들고온다거나 하는식의 활용이 있다. 초창기에는 안보이는 걸 나중에 들고 왔다면
ssr 환경에서는
- SSR에서는, 웹페이지 전체의 가시적인 부분(visible)의 CSS를 생성한다.
- 동적 렌더링 또는 지연 로딩(lazy loaded)되는 element에 CSS를 생성하지 않는다.
이런식으로 쓰기도 한다
https://github.com/danielroe/beasties
GitHub - danielroe/beasties: A library to inline your app's critical CSS and lazy-load the rest.
A library to inline your app's critical CSS and lazy-load the rest. - danielroe/beasties
github.com
다시 원래 목적으로 돌아와서 이러한 이유때문에
새로고침시
HTML 로딩 시점에는
- Tailwind 클래스나 dark 클래스가 아직 적용되지 않음
- 브라우저는 기본 배경색 (white) 으로 화면을 그려버
JS 런타임 이후에
- React 앱 실행됨 → class="dark" 같은 설정 적용됨
- 이때서야 Tailwind가 스타일 적용
- 결과: 한순간 하얀 배경이 보였다가 → 다크 테마가 적용됨
👉 깜빡임(Flash of Unstyled Content, FOUC) 발생!
테일윈드에서 깜빡임을 잡기 위해선
class 를 미리 줘야한다
이걸 빌드하면
이런식으로 나오는대
1. 첫 html dom 만들시 dark 라는걸 추가해 추가해 줘야지
2. css 다운
3. 그려짐
이순으로 동작해서 깝빡임이 사라진다
사실 빌드랑 css 원리랑 크게 연관은 많이 없지만
어디를 어떻게 봐야할지 개념이 없었어서
한번에 쓱 정리해 보았다
출처:
https://yung-developer.tistory.com/75
브라우저의 렌더링 과정 이해하기 + CSS 태그와 script 태그 위치
이번 주제는 우리가 작성한 코드 혹은 서버에 요청한 코드가 어떻게 화면에 표시되는지에 대해 알아보겠습니다. 사용자가 볼 수 있는 화면을 구성하기 위해 브라우저는 HTML, CSS, Javascript로 작성
yung-developer.tistory.com
https://velog.io/@younoah/PostCSS-PostCSS%EB%9E%80
[PostCSS] PostCSS란?
PostCSS에 대해 알아보자.
velog.io
https://is-this-it.tistory.com/70
CSS 후처리기 - PostCSS
PostCSS PostCSS는 CSS 전처리기 중 하나로 알려져 있는데, 정확히는 동작 순서에 따라 CSS 후처리기(CSS Post-processor)가 맞다. 따라서, 한 프로젝트에 SCSS 같은 전처리기와 함께 사용할 수 있다. Sass, LESS,
is-this-it.tistory.com
What is the actual difference between using an inline-style and using a css-in-js library like styled components in React?
Writing an inline style in js, const styles = { background: 'blue', color: 'white' } and using it in the JSX as below <div style={styles}> I am a div </div> Writing the styles...
stackoverflow.com
https://www.speakeasy.com/post/sass-vs-css-modules-vs-css-in-js
SASS vs CSS Modules vs CSS-in-JS: How to Choose?
Struggling to decide between Sass & CSS-in-JS for your UI? Here's why we chose CSS-in-JS for effortless embedding & theming of UI components – critical.
www.speakeasy.com
What is the actual difference between using an inline-style and using a css-in-js library like styled components in React?
Writing an inline style in js, const styles = { background: 'blue', color: 'white' } and using it in the JSX as below <div style={styles}> I am a div </div> Writing the styles...
stackoverflow.com
https://itchallenger.tistory.com/619
Tailwind CSS와 CSS-in-JS 무엇을 사용할까?
언제 어떤 도구를 쓰는게 맞는지 생각해보자. TLDR : 생산성 : Css-in-js 유지보수성, 확장성 : tailwind CSS Styled-Components 베스트 프랙티스(모범 사례) 원문 보기 :https://www.joshwcomeau.com/css/styled-components/ Th
itchallenger.tistory.com
CSS-in-JS vs CSS-module vs Post CSS 차이 구분하기
리액트 기반의 프론트엔드를 공부하다 보면 다양한 스타일링 방식을 알게됩니다. 프로젝트를 진행하며 다양한 방식의 스타일링을 사용해보려 했고 CSS-in-JS 방식과 CSS-Module 방식을 주로 사용하
hoime.tistory.com
https://rendaritfactory.tistory.com/38
Vanilla Extract - 설치 및 구현
서론사실 나는 CSS-in-JS 라이브러리를 굉장히 좋아한다. (Emotion, styled-componets) 이유중 하나라면 다들 스파게티 코드라고 불리우는 JS 파일내에서 CSS를 처리할수 있으며 (colocation), 동적으로 CSS 처리
rendaritfactory.tistory.com
https://pozafly.github.io/css/explore-how-to-apply-modern-css/
모던 CSS 적용 방법 둘러보기(CSS-in-JS with zero-runtime)
전처리기부터 Atomic CSS 까지, 웹 어플리케이션 개발에 필요한 CSS 적용 방법 및 최적화에 관한 이야기
pozafly.github.io
https://github.com/andreipfeiffer/css-in-js/blob/main/README.md
css-in-js/README.md at main · andreipfeiffer/css-in-js
A thorough analysis of all the current CSS-in-JS solutions with SSR & TypeScript support for Next.js - andreipfeiffer/css-in-js
github.com
https://velog.io/@sy3783/React7.-styled-components
CSS-in-JS_1. 등장 배경
CSS-in-JS는 CSS의 문법을 이용해 자바스크립트, 혹은 컴포넌트 내부에서 스타일을 작성하도록 해주는 방식이다.
velog.io
[Tailwind CSS] Utility-First CSS를 써야하는 이유 - 1탄
Tailwind CSS를 왜 써야하는지 궁금하다면? 🤔
velog.io
tailwindcss 4.0 무엇이 달라졌나요?
Tailwind CSS v4.0은 성능과 유연성을 극대화한 새로운 버전으로, 최신 웹 표준과 개발 흐름에 맞춘 다양한 기능과 개선 사항을 제공합니다. 이 글에서는 업데이트된 주요 내용을 살펴봅니다.
velog.io
https://velog.io/@jjh099/CSS-%EC%84%B8-%EA%B0%80%EC%A7%80-CSS
[CSS] 세 가지 CSS
CSS Module, Styled Components, Tailwind
velog.io
https://dev-district.tistory.com/39
[CSS] CSS-in-JS의 종류와 장단점
🐝 정의CSS 파일을 별도로 작성하지 않고 자바스크립트 파일 안에서 CSS를 작성하는 방식입니다. 자바스크립트 코드가 실행되며 CSS 코드가 생성됩니다.CSS-in-JS 라이브러리의 종류는 다음과 같습
dev-district.tistory.com
https://velog.io/@dooreplay/classNamesCSS-Modules
리액트에서 조건부 스타일을 줄때 classNames 라이브러리를 활용해보자!
리액트에서 조건에 따라 여러개의 className을 설정해야 할 때, template literal로 고생했던 당신! 특히나 CSS-Modules를 쓰고 있다면 더더욱 클릭해야 할 이 글! classNames library로 골칫거리를 속시원하게
velog.io