정적 리소스 관리

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;
    // 처리
});


전역 네임스페이스 — window.App

모든 전역 공유 상태는 window.App 단일 오브젝트 아래에 관리한다. 페이지마다 각자 전역 변수를 선언하면 이름 충돌과 디버깅 어려움이 생긴다.

window.App
├── config    # 서버 주입 런타임 설정 (Thymeleaf inline으로 주입)
└── utils     # 공통 유틸 함수 (main.js에서 정의)
// common-script.html — 서버 설정 주입 (Thymeleaf inline)
window.App = window.App || {};
App.config = {
    apiUrl:  '/*서버에서 주입*/',
    version: '/*서버에서 주입*/'
};

// main.js — App 초기화 및 공통 유틸 확장
window.App = window.App || {};
// App.utils.slugify = function(text) { ... };
  • window.App = window.App || {} 패턴으로 로드 순서에 무관하게 안전하게 초기화
  • 페이지 전용 데이터는 App에 추가하지 않음 — IIFE 내부 지역 변수로 처리

스크립트 실행 패턴 선택 기준

jQuery를 사용하지 않는 프로젝트에서는 $(document).ready()를 사용하지 않는다.

패턴사용 위치이유
IIFE (function(){...})()페이지 전용 인라인 스크립트 (body 끝)body 끝에 위치하므로 실행 시점에 DOM이 이미 파싱됨
DOMContentLoaded 이벤트외부 .js 파일파일 로드 위치가 달라져도 안전하게 동작
jQuery $(document).ready()사용 안 함jQuery 미도입 프로젝트에서 불필요
// 페이지 인라인 스크립트 — IIFE
(function () {
    'use strict';
    document.getElementById('btn').addEventListener('click', handler);
    function handler() { ... }
})();

// 외부 .js 파일 — DOMContentLoaded
document.addEventListener('DOMContentLoaded', function () {
    document.getElementById('btn').addEventListener('click', handler);
    function handler() { ... }
});

IIFE 규칙:

  • 모든 인라인 page-script는 IIFE로 감싸기 (전역 오염 방지)
  • 함수를 전역으로 선언하지 않기 — function openModal(){} 방식 금지
  • 인라인 스크립트가 100줄 초과 시 외부 .js 파일로 분리

이미지 관리

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

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