정적 리소스 관리

CSS/JS/이미지 정적 리소스 관리 규칙 및 디자인 토큰

마지막 수정: 2026-05

정적 리소스 관리

디렉토리 구조

static/
├── css/
│   └── guide.css         # 개발 가이드 전용 스타일 (단일 파일)
├── js/
│   └── guide.js          # 개발 가이드 JS (단일 파일)
└── images/               # 이미지 파일
    └── {이미지파일}

정적 리소스 참조 방법

Thymeleaf에서 정적 리소스는 th:href, th:src@{...} 표현식을 사용한다.

<!-- CSS 참조 -->
<link rel="stylesheet" th:href="@{/css/guide.css}">

<!-- JS 참조 -->
<script th:src="@{/js/guide.js}"></script>

<!-- 이미지 참조 -->
<img th:src="@{/images/logo.png}" alt="로고">

디자인 토큰 (CSS 변수)

:root {
  --sidebar-width: 260px;
  --primary: #2563eb;
  --primary-hover: #1d4ed8;
  --text: #111827;
  --text-secondary: #6b7280;
  --bg: #ffffff;
  --sidebar-bg: #f8fafc;
  --border: #e2e8f0;
  --code-bg: #f1f5f9;
  --active-bg: #eff6ff;
  --active-color: #2563eb;
  --active-border: #2563eb;
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: "JetBrains Mono", "Fira Code", Consolas, monospace;
}

하드코딩 금지

/* 잘못된 예 */
color: #111827;
background: #2563eb;

/* 올바른 예 */
color: var(--text);
background: var(--primary);

외부 CDN

현재 사용 중인 외부 CDN:

라이브러리용도비고
highlight.js 11.9.0코드 구문 강조cdnjs.cloudflare.com

그 외 외부 CDN은 최소화한다.

<!-- highlight.js CDN -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>

외부 CDN 사용 시 SRI(Subresource Integrity) 해시 적용을 권장한다:

<link rel="stylesheet"
      href="https://cdn.example.com/lib.css"
      integrity="sha384-..."
      crossorigin="anonymous">

SRI 해시 생성: srihash.org


CSS 작성 규칙

선택자 규칙

/* BEM-like 네이밍 */
.doc-article {}         /* 블록 */
.doc-article-title {}   /* 요소 */
.nav-link.active {}     /* 상태 */

반응형 브레이크포인트

/* 태블릿/소형 화면 */
@media (max-width: 1100px) {
    .toc-aside { display: none; }
}

/* 모바일 */
@media (max-width: 768px) {
    .mobile-header { display: flex; }
    .sidebar { transform: translateX(-100%); }
    .main-content { margin-left: 0; }
}

JavaScript 작성 규칙

// eval() 절대 금지
// innerHTML 직접 할당 금지 → textContent 또는 DOM API 사용

// 잘못된 예
element.innerHTML = userInput;

// 올바른 예
element.textContent = userInput;
// DOMContentLoaded 이후 초기화
document.addEventListener('DOMContentLoaded', () => {
    // 초기화 코드
});

// 이벤트 위임 패턴 (성능 최적화)
document.querySelector('.sidebar-nav').addEventListener('click', (e) => {
    const link = e.target.closest('.nav-link');
    if (!link) return;
    // 처리
});

이미지 관리

이미지 파일은 static/images/ 디렉토리에 위치한다.

<!-- WebP 우선, PNG 폴백 -->
<picture>
    <source srcset="/images/photo.webp" type="image/webp">
    <img th:src="@{/images/photo.png}" alt="설명" loading="lazy">
</picture>