07 / 14
CSS 기본
이제 옷을 입혀요. 두 가지를 해야 해요. 서버가 static 폴더를 열어 주기, HTML이 CSS 파일을 불러오기.
app.py에 두 줄 추가해요. import 한 줄, mount 한 줄.
app.py
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")app.mount("/static", ...) /static/... 주소로 오면 static 폴더의 파일을 그대로 보내라는 뜻이에요. 이미지도 CSS도 JS도 이 폴더에 둬요.
templates/index.html의 <head> 안에 한 줄 추가해요.
templates/index.html
<title>crack.log</title>
<link rel="stylesheet" href="/static/style.css">실행하면 이미지부터 떠요. 이제 static/style.css 탭을 채워요.
static/style.css
:root {
--bg: #f8f9fa;
--panel: #ffffff;
--text: #212529;
--muted: #868e96;
--line: #e9ecef;
--mint: #12b886;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", "Malgun Gothic", sans-serif;
background: var(--bg);
color: var(--text);
}
header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 32px;
background: var(--panel);
border-bottom: 1px solid var(--line);
}
header a {
font-size: 22px;
font-weight: 800;
color: var(--mint);
text-decoration: none;
}
main {
max-width: 1000px;
margin: 0 auto;
padding: 32px;
}
h1 { font-size: 22px; }
.card {
max-width: 320px;
background: var(--panel);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
}
.card a { color: inherit; text-decoration: none; }
.card .thumb { display: block; width: 100%; height: 160px; object-fit: cover; }
.card .body { padding: 16px; }
.card h2 { margin: 0 0 8px; font-size: 16px; }
.card p { margin: 0 0 12px; font-size: 14px; line-height: 1.5; color: var(--muted); }
.card .date { font-size: 12px; color: var(--muted); }
.card .foot {
display: flex;
justify-content: space-between;
padding: 10px 16px;
border-top: 1px solid var(--line);
font-size: 12px;
color: var(--muted);
}길어 보이지만 구조는 하나예요. 누구를(선택자) 어떻게(속성: 값;).
body태그 이름으로 고르기. 모든<body>..card점으로 시작하면 class 이름으로 고르기.class="card"인 것들..card h2띄어쓰기는 "안에 있는". card 안의 h2.:root에--mint같은 변수를 모아 두고var(--mint)로 꺼내 써요. 색을 한 곳에서 바꾸려고요. 9단계 다크모드에서 빛을 봐요.header의display: flex는 안의 것들을 가로로 나란히.justify-content: space-between은 양 끝으로 밀기.
해보기
--mint 값을 다른 색으로 바꿔 보세요. #f783ac 같은 거요. 카드의 border-radius를 24px로 바꿔 보세요.
이 단계 전체 코드
app.py
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
def home(request: Request):
return templates.TemplateResponse(request, "index.html")templates/index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>crack.log</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<header>
<a href="/">crack.log</a>
</header>
<main>
<h1>내 블로그</h1>
<article class="card">
<a href="/posts/1">
<img class="thumb" src="/static/thumb1.svg" alt="">
<div class="body">
<h2>FastAPI 첫 서버 띄우기</h2>
<p>uvicorn 한 줄이면 끝. 흰 화면에 Hello World가 뜨는 순간부터 시작.</p>
<span class="date">2026년 10월 10일</span>
</div>
</a>
<div class="foot">
<span>by <b>sfadfe</b></span>
<span>♥ 518</span>
</div>
</article>
</main>
</body>
</html>static/style.css
:root {
--bg: #f8f9fa;
--panel: #ffffff;
--text: #212529;
--muted: #868e96;
--line: #e9ecef;
--mint: #12b886;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", "Malgun Gothic", sans-serif;
background: var(--bg);
color: var(--text);
}
header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 32px;
background: var(--panel);
border-bottom: 1px solid var(--line);
}
header a {
font-size: 22px;
font-weight: 800;
color: var(--mint);
text-decoration: none;
}
main {
max-width: 1000px;
margin: 0 auto;
padding: 32px;
}
h1 { font-size: 22px; }
.card {
max-width: 320px;
background: var(--panel);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
}
.card a { color: inherit; text-decoration: none; }
.card .thumb { display: block; width: 100%; height: 160px; object-fit: cover; }
.card .body { padding: 16px; }
.card h2 { margin: 0 0 8px; font-size: 16px; }
.card p { margin: 0 0 12px; font-size: 14px; line-height: 1.5; color: var(--muted); }
.card .date { font-size: 12px; color: var(--muted); }
.card .foot {
display: flex;
justify-content: space-between;
padding: 10px 16px;
border-top: 1px solid var(--line);
font-size: 12px;
color: var(--muted);
}