정적 리소스 관리
디렉토리 구조
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>