devpia.net

๐Ÿš€ Bootstrap 5.3 + Spring Boot๋กœ ๊ตฌ์ถ•ํ•˜๋Š” ๋ชจ๋˜ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜

๋‹คํฌ ๋ชจ๋“œ ์™„๋ฒฝ ์ง€์›๋ถ€ํ„ฐ Thymeleaf ๋ ˆ์ด์•„์›ƒ ์ตœ์ ํ™”, REST ์—ฐ๋™๊นŒ์ง€ ๋‹จ์ˆจ์— ๋๋‚ด๋Š” ํ”„๋กœ๋•์…˜๊ธ‰ ์›น ์•„ํ‚คํ…์ฒ˜

๋•์ฒ ์ด
2026๋…„ 8์›” 13์ผ ยท 5๋ถ„ ์ฝ๊ธฐ
์กฐํšŒ์ˆ˜ 7
๐Ÿš€ Bootstrap 5.3 + Spring Boot๋กœ ๊ตฌ์ถ•ํ•˜๋Š” ๋ชจ๋˜ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
โฑ๏ธ ์ฝ๋Š” ์‹œ๊ฐ„: ์•ฝ 9๋ถ„ยท์•ฝ 4,542์ž

"์„ ๋ฐฐ๋‹˜, ์š”์ฆ˜ ํ’€์Šคํƒ ํ† ์ด ํ”„๋กœ์ ํŠธ๋‚˜ ์‚ฌ๋‚ด ๋ฐฑ์˜คํ”ผ์Šค ๋งŒ๋“ค ๋•Œ React/Vue ๋นŒ๋“œ ํ™˜๊ฒฝ ์„ธํŒ…ํ•˜๋‹ค๊ฐ€ ๊ธฐ์šด ๋‹ค ๋น ์ง€๋Š”๋ฐ... ๋น ๋ฅด๊ณ  ์„ธ๋ จ๋˜๊ฒŒ ๋ฐฑ์—”๋“œ๋ž‘ ํ”„๋ก ํŠธ ํ•œ ๋ฐฉ์— ๋๋‚ด๋Š” ๋ฒ• ์—†๋‚˜์š”?" ํ›„๋ฐฐ๋“ค์˜ ์ด ๋‹จ๊ณจ ์งˆ๋ฌธ์— 15๋…„ ์ฐจ ๋Š™์€(?) ๊ฐœ๋ฐœ์ž์ธ ์ €๋Š” ๋Š˜ ๋ง์„ค์ž„ ์—†์ด ๋‹ตํ•ฉ๋‹ˆ๋‹ค. "Bootstrap 5.3๊ณผ Spring Boot์˜ ์ตœ์‹  ์กฐํ•ฉ์„ ์•„์ง๋„ ๋ฌด์‹œํ•œ๋‹ค๋ฉด ์ง„์งœ ์†ํ•ด๋‹ค!"๋ผ๊ณ  ๋ง์ด์ฃ .

Bootstrap 5.3๊ณผ Spring Boot ์ฝ”๋”ฉ ํ™˜๊ฒฝ
โ–ฒ ์ƒ์‚ฐ์„ฑ๊ณผ ์™„์„ฑ๋„๋ฅผ ๋™์‹œ์— ์žก๋Š” Bootstrap 5.3 & Spring Boot ๋ชจ๋˜ ํ’€์Šคํƒ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ

1. ์™œ 2026๋…„์—๋„ Bootstrap 5.3 + Spring Boot์ธ๊ฐ€?

๋งŽ์€ ๋ถ„๋“ค์ด '๋ถ€ํŠธ์ŠคํŠธ๋žฉ' ํ•˜๋ฉด 10๋…„ ์ „ ๊ทธ ํˆฌ๋ฐ•ํ•˜๊ณ  ๋ป”ํ•œ ํŒŒ๋ž€์ƒ‰ ๋ฒ„ํŠผ ๋””์ž์ธ์„ ๋– ์˜ฌ๋ฆฌ๊ณค ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Bootstrap 5.3๋ถ€ํ„ฐ ์™„์ „ํžˆ ๋‹ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค. ์ˆœ์ˆ˜ CSS ๋ณ€์ˆ˜(CSS Variables) ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜, ๋นŒํŠธ์ธ ์ปฌ๋Ÿฌ ๋ชจ๋“œ(๋‹คํฌ ๋ชจ๋“œ/๋ผ์ดํŠธ ๋ชจ๋“œ) ํ† ๊ธ€, ์œ ํ‹ธ๋ฆฌํ‹ฐ API์˜ ํญ๋ฐœ์ ์ธ ์ง„ํ™”๋กœ ๋ณ„๋„์˜ ๋ฌด๊ฑฐ์šด ํ”„๋ก ํŠธ์—”๋“œ ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ(Webpack, Vite ๋“ฑ) ์—†์ด๋„ ์ตœ์‹  ํŠธ๋ Œ๋“œ์˜ UI๋ฅผ ์ˆœ์‹๊ฐ„์— ๋ฝ‘์•„๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์— ์ž๋ฐ” ์ง„์˜์˜ ์ ˆ๋Œ€๊ฐ•์ž Spring Boot 3.x+์™€ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง ํ…œํ”Œ๋ฆฟ ์—”์ง„์ธ Thymeleaf๋ฅผ ์–น์œผ๋ฉด? ๋ณต์žกํ•œ SPA(Single Page Application) ๋ฐฐํฌ/CORS ์„ค์ •/SEO ๊ณ ๋ฏผ ์—†์ด ํ˜ผ์ž์„œ๋„ 3์ผ ๋งŒ์— ์™„์„ฑ๋„ ๋†’์€ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ๊ธ‰ ์„œ๋น„์Šค๋ฅผ ๋š๋”ฑ ๋Ÿฐ์นญํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋น„๊ต ํ•ญ๋ชฉ Spring Boot + Bootstrap 5.3 (SSR) Spring Boot + React/Next.js (SPA/ํ•˜์ด๋ธŒ๋ฆฌ๋“œ)
์ดˆ๊ธฐ ๊ตฌ์ถ• ๋ฐ ํ•™์Šต ๋น„์šฉ ๋งค์šฐ ๋‚ฎ์Œ (๊ธฐ๋ณธ HTML/JS/CSS ์ง€์‹์œผ๋กœ ์ฆ‰์‹œ ๊ตฌ์ถ•) ๋†’์Œ (์ƒํƒœ ๊ด€๋ฆฌ, ๋ฒˆ๋“ค๋Ÿฌ, Node ํ™˜๊ฒฝ ํ•„์ˆ˜)
๋‹คํฌ ๋ชจ๋“œ ์ง€์› data-bs-theme ์†์„ฑ ํ•˜๋‚˜๋กœ ์ž๋™ ์ „ํ™˜ ๋ณ„๋„ ํ…Œ๋งˆ ํ”„๋กœ๋ฐ”์ด๋” ๋ฐ CSS-in-JS ์„ค์ • ํ•„์š”
๊ฒ€์ƒ‰์—”์ง„ ์ตœ์ ํ™” (SEO) ๊ธฐ๋ณธ ์™„๋ฒฝ ์ง€์› (์„œ๋ฒ„ ๋ Œ๋”๋ง) SSR(Next.js) ๊ตฌ์ถ• ๋˜๋Š” ํ”„๋ฆฌ๋ Œ๋”๋ง ์„ธํŒ… ํ•„์š”
์ ํ•ฉํ•œ ๋„๋ฉ”์ธ ์‚ฌ๋‚ด ์–ด๋“œ๋ฏผ/๋ฐฑ์˜คํ”ผ์Šค, MVP ํ”„๋กœ๋•ํŠธ, B2B SaaS, ํฌํ„ธ ์‹ค์‹œ๊ฐ„ ์ธํ„ฐ๋ž™์…˜์ด ๊ทน๋„๋กœ ๋งŽ์€ ๋ชจ๋ฐ”์ผ ์›น์•ฑ, SNS

2. ํ”„๋กœ์ ํŠธ ์•„ํ‚คํ…์ฒ˜์™€ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ๊ฟ€ํŒ

ํ˜„์—…์—์„œ ์ฝ”๋“œ ๊ผฌ์ด์ง€ ์•Š๊ณ  ์พŒ์ ํ•˜๊ฒŒ ์œ ์ง€๋ณด์ˆ˜ํ•˜๋ ค๋ฉด Thymeleaf์˜ ๋ ˆ์ด์•„์›ƒ ์กฐ๊ฐ(Layout Dialect ๋˜๋Š” Fragment) ์„ค๊ณ„๋ฅผ ๋˜‘๋˜‘ํ•˜๊ฒŒ ํ•ด๋‘ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ๊ตฌ์กฐ๋ฅผ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

src/main/resources/
โ”œโ”€โ”€ static/
โ”‚   โ”œโ”€โ”€ css/          # ์ปค์Šคํ…€ override ์Šคํƒ€์ผ
โ”‚   โ”œโ”€โ”€ js/           # ์ปฌ๋Ÿฌ๋ชจ๋“œ ํ† ๊ธ€ ์Šคํฌ๋ฆฝํŠธ & ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
โ”‚   โ””โ”€โ”€ images/
โ””โ”€โ”€ templates/
    โ”œโ”€โ”€ fragments/    # ํ—ค๋”, ํ‘ธํ„ฐ, ๋„ค๋น„๊ฒŒ์ด์…˜๋ฐ”, ๋‹คํฌ๋ชจ๋“œ ์Šค์œ„์ฒ˜
    โ”‚   โ”œโ”€โ”€ header.html
    โ”‚   โ””โ”€โ”€ navbar.html
    โ”œโ”€โ”€ layout/       # ๊ณตํ†ต ๋งˆ์Šคํ„ฐ ๋ ˆ์ด์•„์›ƒ
    โ”‚   โ””โ”€โ”€ default.html
    โ””โ”€โ”€ views/        # ์‹ค์ œ ์ฝ˜ํ…์ธ  ํŽ˜์ด์ง€๋“ค
        โ”œโ”€โ”€ dashboard.html
        โ””โ”€โ”€ users.html
๋ชจ๋˜ ๋Œ€์‹œ๋ณด๋“œ ์ธํ„ฐํŽ˜์ด์Šค ๋ฐ์ดํ„ฐ ๋ถ„์„
โ–ฒ ๊น”๋”ํ•œ ์ปดํฌ๋„ŒํŠธ ๊ตฌ์กฐํ™”๋Š” ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค

3. ์‹ค์ „ ์ฝ”๋“œ: Bootstrap 5.3 ๋‹คํฌ๋ชจ๋“œ ์™„๋ฒฝ ์—ฐ๋™ ๋ฒ ์ด์Šค ๋ ˆ์ด์•„์›ƒ

Bootstrap 5.3์˜ ๊ฐ€์žฅ ํฐ ์ถ•๋ณต์ธ ๋‹คํฌ ๋ชจ๋“œ ์‹œ์Šคํ…œ ๊ฐ์ง€ ๋ฐ ์Šค์œ„์นญ ๊ธฐ๋Šฅ์„ Thymeleaf ๋ ˆ์ด์•„์›ƒ์— ์ง์ ‘ ๋…น์—ฌ๋‚ธ ์‹ค์ „ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ๋ณต์‚ฌํ•ด์„œ ๋ฐ”๋กœ ํ™œ์šฉํ•ด ๋ณด์„ธ์š”!

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" data-bs-theme="auto">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title th:text="${pageTitle} ?: 'Modern Spring App'">๋ชจ๋˜ ์Šคํ”„๋ง ์•ฑ</title>
    <!-- Bootstrap 5.3.3 CSS & Icons -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
    <!-- ๋‹คํฌ๋ชจ๋“œ ๊นœ๋นก์ž„(FOUC) ๋ฐฉ์ง€ ์ธ๋ผ์ธ ์Šคํฌ๋ฆฝํŠธ -->
    <script>
      (() => {
        const storedTheme = localStorage.getItem('theme');
        const getPreferredTheme = () => {
          if (storedTheme) return storedTheme;
          return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
        };
        document.documentElement.setAttribute('data-bs-theme', getPreferredTheme());
      })();
    </script>
</head>
<body class="bg-body-tertiary">
    <!-- ๊ณตํ†ต ๋„ค๋น„๊ฒŒ์ด์…˜๋ฐ” -->
    <nav class="navbar navbar-expand-lg bg-body border-bottom sticky-top shadow-sm">
      <div class="container">
        <a class="navbar-brand fw-bold text-primary" href="/"><i class="bi bi-rocket-takeoff-fill me-2"></i>DevStudio 2026</a>
        <div class="d-flex align-items-center gap-3">
          <!-- ํ…Œ๋งˆ ๋ณ€๊ฒฝ ํ† ๊ธ€ ๋ฒ„ํŠผ -->
          <button class="btn btn-outline-secondary btn-sm rounded-pill" id="themeToggleBtn">
            <i class="bi bi-moon-stars-fill"></i>
          </button>
          <span class="badge bg-primary-subtle text-primary border border-primary-subtle">Spring Boot 3.x</span>
        </div>
      </div>
    </nav>

    <!-- ๋ณธ๋ฌธ ์ฃผ์ž… ์˜์—ญ -->
    <main class="container my-5" th:replace="~{::content}">
      <p>๊ธฐ๋ณธ ์ปจํ…์ธ  ์˜์—ญ์ž…๋‹ˆ๋‹ค.</p>
    </main>

    <script src="/uploads/2026/08/bootstrap-53-spring-boot๋กœ-๊ตฌ์ถ•ํ•˜๋Š”-๋ชจ๋˜-์›น-์• ํ”Œ๋ฆฌ์ผ€์ด์…˜-img3-f1219749.jpg"></script>
</body>
</html>
์‹œ๋‹ˆ์–ด 15๋…„ ์ฐจ๊ฐ€ ์ „ํ•˜๋Š” ์‹ค๋ฌด ์ตœ์ ํ™” ํŒ

1. FOUC(ํ™”๋ฉด ๊นœ๋นก์ž„) ๋ฐฉ์ง€: ๋‹คํฌ ๋ชจ๋“œ ์ ์šฉ ์Šคํฌ๋ฆฝํŠธ๋Š” ๋ฒˆ๋“ค ํŒŒ์ผ์— ๋„ฃ์ง€ ๋ง๊ณ  <head> ํƒœ๊ทธ ์ƒ๋‹จ์— ์ธ๋ผ์ธ JS๋กœ ์ฆ‰์‹œ ์‹คํ–‰์‹œ์ผœ์•ผ ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์‹œ ํฐ์ƒ‰ ํ™”๋ฉด์ด ๋ฒˆ์ฉ์ด๋Š” ์ฐธ์‚ฌ๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
2. WebJars ํ™œ์šฉ vs CDN: ํ์‡„๋ง ํ™˜๊ฒฝ์ด๋‚˜ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ํ™˜๊ฒฝ์—์„œ๋Š” org.webjars:bootstrap ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ Gradle/Maven์— ์ง์ ‘ ์ž„ํฌํŠธํ•ด ์™ธ๋ถ€ ์˜์กด์„ฑ์„ ์—†์• ๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

4. ์„ฑ๋Šฅ๊ณผ ์ƒ์‚ฐ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•˜๋Š” ๋ชจ๋ฒ” ์‚ฌ๋ก€ (Best Practices)

  • HTMX์™€์˜ ๊ฒฐํ•ฉ ๊ฒ€ํ† : ์ˆœ์ˆ˜ ํ™”๋ฉด ๋ Œ๋”๋ง์— ๋งŒ์กฑํ•˜์ง€ ๋ชปํ•˜๊ณ  SPA๊ธ‰์˜ ๋น„๋™๊ธฐ ๋ฐ˜์‘์„ฑ์„ ์›ํ•œ๋‹ค๋ฉด React๋ฅผ ๋„์ž…ํ•˜๊ธฐ ์ „์— HTMX๋ฅผ ๋ถ™์—ฌ๋ณด์„ธ์š”. Bootstrap ์ปดํฌ๋„ŒํŠธ์™€ ๊ถํ•ฉ์ด ํ™˜์ƒ์ ์ž…๋‹ˆ๋‹ค.
  • SCSS ์ปค์Šคํ…€ ๋นŒ๋“œ: ๊ธฐ๋ณธ CDN๋„ ํ›Œ๋ฅญํ•˜์ง€๋งŒ, ๊ธฐ์—… ๋ธŒ๋žœ๋“œ ์ปฌ๋Ÿฌ๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ง€์ •ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด _variables.scss๋ฅผ ์ปค์Šคํ…€ํ•˜์—ฌ ์ „์—ญ ์ปฌ๋Ÿฌ ๋งต์„ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜์„ธ์š”.
  • Spring Boot Caching & Gzip: application.yml์—์„œ ์ •์  ๋ฆฌ์†Œ์Šค(CSS/JS) ์บ์‹œ ํ—ค๋”(Cache-Control)์™€ ์••์ถ•(server.compression.enabled=true)์„ ์ผœ๋‘๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ํŠธ๋ž˜ํ”ฝ์„ 60% ์ด์ƒ ์ ˆ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ๋ฌด๋ฆฌ ์š”์•ฝ: ๋ง์„ค์ด์ง€ ๋ง๊ณ  ์ง€๊ธˆ ๋‹น์žฅ ์‹œ์ž‘ํ•˜์„ธ์š”!

๊ฐœ๋ฐœ์ž์—๊ฒŒ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์ž์›์€ '์‹œ๊ฐ„'์ž…๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ๋นŒ๋“œ ๋„๊ตฌ์™€ ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋ฒ„์ „ ์—…๊ทธ๋ ˆ์ด๋“œ ํ”ผ๋กœ๊ฐ์— ์ง€์น˜์…จ๋‹ค๋ฉด, Bootstrap 5.3 + Spring Boot ์กฐํ•ฉ์œผ๋กœ ๋ณธ์งˆ์ ์ธ ๋ฐฑ์—”๋“œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋น ๋ฅธ ๊ฐ€์น˜ ๊ฒ€์ฆ์— ์ง‘์ค‘ํ•ด๋ณด์„ธ์š”. ๋‹จ์ˆœํ•จ ์†์— ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ์—”์ง€๋‹ˆ์–ด๋ง์˜ ๋ฏธํ•™์ด ์ˆจ์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธ€์ด ๋งˆ์Œ์— ๋“œ์…จ๋‚˜์š”? ๋ฐ˜์‘์„ ๋‚จ๊ฒจ์ฃผ์„ธ์š”!
๐Ÿ’ก ๋ณธ ๊ฒŒ์‹œ๋ฌผ์˜ ๋ชจ๋“  ์ €์ž‘๊ถŒ์€ ๋ธ”๋กœ๊ทธ ์ž‘์„ฑ์ž์—๊ฒŒ ์žˆ์œผ๋ฉฐ ๋ฌด๋‹จ ์ „์žฌ ๋ฐ ์žฌ๋ฐฐํฌ๋ฅผ ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

๋Œ“๊ธ€ 0

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค. ์ฒซ ๋Œ“๊ธ€์„ ๋‚จ๊ฒจ๋ณด์„ธ์š”!

๋ณธ ์‚ฌ์ดํŠธ๋Š” ์›ํ™œํ•œ ์„œ๋น„์Šค ์ œ๊ณต์„ ์œ„ํ•ด ์ฟ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ธฐ