서비스 학습
SOURCE · fe/src/index.css · 2026-09-06

화면 전체가 공유하는
일곱 줄의 바닥

색과 글꼴을 이름으로 정의하고, 브라우저마다 다른 기본값을 없앤다. App.css는 여기서 정한 이름을 가져다 쓴다.

이 파일을 만든 이유

CSS 파일이 둘로 나뉜 이유는 적용 범위가 다르기 때문이다. 이 파일은 문서 전체에 걸리는 규칙 — 글꼴, 배경, 상자 계산 방식, 색 이름 — 을 정한다. App.css는 채팅 화면의 개별 요소를 다룬다.

가장 중요한 역할은 색을 이름으로 만드는 것이다. #2f6d5f를 여러 파일에 흩어 적으면 색을 바꿀 때 전부 찾아야 한다. --accent라는 이름으로 한 번 정의하면 한 곳만 고치면 된다.

전체 코드

:root { font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: #20201e; background: #f8f8f6; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; --ink: #20201e; --muted: #777772; --line: #e8e7e2; --paper: #fff; --sidebar: #f2f1ed; --accent: #2f6d5f; }
* { box-sizing: border-box; }
html, body, #root { width: 100%; min-width: 320px; min-height: 100%; margin: 0; }
body { min-height: 100svh; overflow: hidden; }
button, textarea { font: inherit; }
button { color: inherit; }
button:focus-visible, textarea:focus-visible { outline: 2px solid rgba(47,109,95,.55); outline-offset: 2px; }

main.tsximport './index.css'로 가져오고, App.tsximport './App.css'를 가져온다. import 순서가 곧 CSS 우선순위이므로, 전역 기본값이 먼저 깔리고 화면별 규칙이 나중에 덮는다.

구간별 해설

:root { … } — 문서의 뿌리

:root<html> 요소를 가리킨다. 여기에 정의한 값은 모든 하위 요소가 상속하므로 글꼴과 기본 색을 두기에 알맞다.

font-family: Inter, ui-sans-serif, -apple-system, …

앞에서부터 있는 글꼴을 찾아 쓴다. Inter는 이 프로젝트가 따로 내려받지 않으므로 설치돼 있지 않으면 건너뛴다. 다음 후보인 ui-sans-serif·-apple-system이 macOS·iPadOS에서 시스템 글꼴로 이어진다. 외부 글꼴을 받지 않으므로 오프라인에서도 화면이 깨지지 않는다. 한글은 시스템 글꼴이 담당한다.

--ink · --muted · --line · --paper · --sidebar · --accent

CSS 사용자 정의 속성이다. 이름이 색 자체가 아니라 역할을 가리킨다는 점이 중요하다. --green이 아니라 --accent이므로 나중에 강조색을 바꿔도 이름이 거짓말이 되지 않는다. App.css에서 var(--accent)로 꺼내 쓴다.

다크 모드를 넣는다면 @media (prefers-color-scheme: dark) 안에서 이 여섯 값만 다시 정의하면 된다. 개별 규칙은 손대지 않아도 된다.

font-synthesis: none · text-rendering · -webkit-font-smoothing

글자 렌더링 세부 설정이다. font-synthesis: none은 굵은 글꼴이 없을 때 브라우저가 인위적으로 굵게 만드는 것을 막는다. 나머지 둘은 자간 처리와 안티앨리어싱 방식으로, 주로 macOS·Safari에서 차이가 보인다.

* { box-sizing: border-box; }

CSS에서 가장 널리 쓰이는 한 줄이다. 기본값에서는 width: 300px에 padding과 border가 더해져 실제 폭이 커진다. border-box로 바꾸면 padding과 border가 포함되어 300px이 그대로 유지된다. 레이아웃 계산이 예측 가능해진다.

html, body, #root { width: 100%; min-width: 320px; min-height: 100%; margin: 0; }

세 요소를 함께 지정한 이유가 있다. #root가 빠지면 높이가 이어지지 않는다. React가 그리는 .app-shell이 화면 전체 높이를 쓰려면 그 위의 #root·body·html이 모두 높이를 가지고 있어야 한다. margin: 0은 브라우저 기본 여백을 없앤다.

min-width: 320px는 아주 좁은 화면에서 레이아웃이 무너지지 않게 하는 하한선이다.

body { min-height: 100svh; overflow: hidden; }

svhsmall viewport height로, 모바일 브라우저의 주소창이 보이는 상태의 높이다. 예전에 쓰던 100vh는 주소창을 제외하지 않아 iOS Safari에서 화면 아래가 잘리는 문제가 유명하다. iPad에서 쓰려는 이 프로젝트에 직접 관계된 선택이다.

overflow: hidden은 문서 전체가 스크롤되지 않게 한다. 스크롤은 메시지 목록 안에서만 일어나야 입력창이 늘 화면 아래에 고정된다. 채팅 앱의 전형적인 구조다.

button, textarea { font: inherit; }

form 요소는 부모의 글꼴을 물려받지 않는다. 브라우저가 자체 기본 글꼴을 쓰기 때문에, 이 줄이 없으면 입력창과 버튼만 다른 글꼴로 보인다. button { color: inherit; }도 같은 이유다.

:focus-visible { outline: 2px solid … }

키보드로 이동할 때만 테두리를 보여 준다. :focus와 달리 마우스 클릭에는 나타나지 않는다. 접근성을 위해 초점 표시는 반드시 필요하지만, 클릭할 때마다 테두리가 보이는 것은 원치 않기 때문에 나온 선택자다. 초점 표시를 아예 없애는 outline: none은 키보드 사용자가 현재 위치를 잃게 만든다.

실제로 확인하는 방법

확인방법보이는 것
변수가 적용된다개발자 도구에서 html 선택 → Computed--accent 등 여섯 개가 보인다
색을 한 번에 바꾼다Styles 패널에서 --accent 값 수정버튼·초점 테두리 등이 동시에 바뀐다
100svh의 효과iPad Safari에서 주소창을 보였다 감춘다입력창이 화면 밖으로 밀려나지 않는다
overflow: hidden페이지 전체를 스크롤해 본다문서는 움직이지 않고 메시지 목록만 스크롤된다

자주 발생하는 오류

증상원인진단
화면 높이가 안 채워짐#root에 높이 지정 누락html·body·#root 세 계단이 모두 필요하다
모바일에서 입력창이 잘림100vh 사용100svh로 바꾼다
var(--accent)가 안 먹힘이름 오타 또는 정의보다 먼저 사용정의는 :root에 있고 import 순서상 먼저 와야 한다
버튼 글꼴만 다름font: inherit 누락form 요소는 상속하지 않는다
스타일이 전혀 없음import './index.css' 누락부작용 import라 지워도 type 오류가 나지 않는다

설계 선택과 대안

왜 Tailwind 같은 도구를 쓰지 않는가?

학습이 1차 목적이므로 브라우저가 실제로 무엇을 하는지가 보이는 편이 낫다. 유틸리티 클래스는 빠르지만 box-sizing이나 svh 같은 결정이 도구 안에 감춰진다. 화면 수가 늘어나면 다시 판단할 문제다.

CSS 변수 대신 전처리기 변수는?

Sass 변수는 build 때 값으로 바뀌어 실행 중에 바꿀 수 없다. CSS 변수는 브라우저에 남아 있어 다크 모드 전환이나 개발자 도구에서의 실시간 수정이 가능하다. 지금 필요한 것은 후자다.

전역 * 선택자는 느리지 않은가?

box-sizing 정도는 현대 브라우저에서 측정 가능한 비용이 아니다. 얻는 예측 가능성이 훨씬 크다.

이전 단계와 다음 파일

여기서 정한 이름을 실제로 사용하는 곳은 App.css다. 이 파일을 불러오는 코드는 main.tsx, 클래스 이름을 붙이는 곳은 App.tsx에 있다.

← 이전채팅 화면과 state다음 →채팅 화면 스타일