css임시

Jlee7362·2025년 8월 14일

JSP

detail.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="pageTitle" value="Article Detail" />
<%@ include file="/WEB-INF/jsp/usr/common/head.jspf"%>

<div class="card bg-base-100 shadow">
  <div class="card-body space-y-4">
    <div class="flex items-center justify-between">
      <h2 class="card-title text-2xl">${article.title}</h2>
      <div class="badge badge-outline">#${article.id}</div>
    </div>
    <p class="text-sm text-base-content/70">등록: ${article.regDate} · 수정: ${article.updateDate}</p>
    <div class="prose max-w-none whitespace-pre-wrap">${article.body}</div>

    <div class="divider"></div>
    <div class="flex gap-2 justify-end">
    <c:if test="${article.userCanModify}">
      <a href="/usr/article/modify?id=${article.id}" class="btn btn-warning">수정</a>
      <button class="btn btn-error"
              onclick="onDelete(${article.id})">삭제</button>
              </c:if>
      <a href="/usr/article/list" class="btn">목록</a>
    </div>
  </div>
</div>

<script>
 async function onDelete(id){
   const ok = await confirmAsync("정말 삭제하시겠어요?");
   if(!ok) return;
   location.href = "/usr/article/doDelete?id=" + id;
 }
</script>

<%@ include file="/WEB-INF/jsp/usr/common/foot.jspf"%>

list.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="pageTitle" value="Article List" />
<%@ include file="/WEB-INF/jsp/usr/common/head.jspf"%>

<div class="card bg-base-100 shadow">
  <div class="card-body gap-4">
    <!-- 상단 액션 -->
    <div class="flex flex-col md:flex-row md:items-center gap-3 justify-between">
      <div class="join">
        <input id="searchInput" type="text" class="input input-bordered join-item" placeholder="제목 검색…" />
        <button id="searchBtn" class="btn btn-primary join-item">검색</button>
      </div>
      <c:if test="${rq.isLogined() }">
      <a href="/usr/article/write" class="btn btn-secondary">새 글 작성</a>
      </c:if>
    </div>

    <!-- 테이블 -->
    <div class="overflow-x-auto">
      <table class="table table-zebra">
        <thead>
          <tr>
            <th>ID</th>
            <th>등록일</th>
            <th>제목</th>
            <th>작성자ID</th>
          </tr>
        </thead>
        <tbody id="articleTbody">
          <c:forEach var="article" items="${articles}">
            <tr class="hover cursor-pointer"
                onclick="location.href='/usr/article/detail?id=${article.id}'">
              <td>${article.id}</td>
              <td>${article.regDate}</td>
              <td class="text-primary">${article.title}</td>
              <td>${article.memberId}</td>
            </tr>
          </c:forEach>
        </tbody>
      </table>
    </div>

    <!-- (옵션) 페이징 자리 -->
    <div class="join self-end">
      <button class="btn join-item">&laquo;</button>
      <button class="btn join-item btn-active">1</button>
      <button class="btn join-item">2</button>
      <button class="btn join-item">&raquo;</button>
    </div>
  </div>
</div>

<script>
  $("#searchBtn").on("click", function(){
    const q = $("#searchInput").val().trim();
    if(!q) return showToast("검색어를 입력하세요", "warning");
    // 서버 검색으로 연결하려면 아래 URL 규칙을 컨트롤러에 맞춰 변경
    location.href = "/usr/article/list?searchKeyword=" + encodeURIComponent(q);
  });
</script>

<%@ include file="/WEB-INF/jsp/usr/common/foot.jspf"%>

modify.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
<c:set var="pageTitle" value="Modify Article" />
<%@ include file="/WEB-INF/jsp/usr/common/head.jspf"%>

<div class="card bg-base-100 shadow">
  <div class="card-body">
    <form action="/usr/article/doModify" method="post" data-safe-submit>
      <input type="hidden" name="id" value="${article.id}" />
      <div class="form-control mb-4">
        <label class="label"><span class="label-text">제목</span></label>
        <input name="title" type="text" class="input input-bordered" value="${article.title}" required />
      </div>

      <div class="form-control mb-6">
        <label class="label"><span class="label-text">내용</span></label>
        <textarea name="body" class="textarea textarea-bordered h-40" required>${article.body}</textarea>
      </div>

      <div class="flex gap-2 justify-end">
        <a href="/usr/article/detail?id=${article.id}" class="btn">취소</a>
        <button type="submit" class="btn btn-primary">수정</button>
      </div>
    </form>
  </div>
</div>

<%@ include file="/WEB-INF/jsp/usr/common/foot.jspf"%>

write.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ include file="/WEB-INF/jsp/usr/common/head.jspf"%>

<c:set var="pageTitle" value="Write Article" />
<div class="card bg-base-100 shadow">
  <div class="card-body">
    <form action="/usr/article/doWrite" method="post" data-safe-submit>
      <div class="form-control mb-4">
        <label class="label"><span class="label-text">제목</span></label>
        <input name="title" type="text" class="input input-bordered" placeholder="제목을 입력하세요" required />
      </div>

      <div class="form-control mb-6">
        <label class="label"><span class="label-text">내용</span></label>
        <textarea name="body" class="textarea textarea-bordered h-40" placeholder="내용을 입력하세요" required></textarea>
      </div>

      <div class="flex gap-2 justify-end">
        <a href="/usr/article/list" class="btn">취소</a>
        <button type="submit" class="btn btn-primary">등록</button>
      </div>
    </form>
  </div>
</div>

<%@ include file="/WEB-INF/jsp/usr/common/foot.jspf"%>

foot.jspf


  </main>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
  <!-- Toast 영역 -->
  <div id="toast" class="toast toast-end hidden z-50"></div>

  <!-- 공통 Confirm 모달 -->
  <dialog id="confirmModal" class="modal">
    <div class="modal-box">
      <h3 class="font-bold text-lg">확인</h3>
      <p id="confirmMessage" class="py-4">이 작업을 진행할까요?</p>
      <div class="modal-action">
        <form method="dialog" class="flex gap-2">
          <button class="btn btn-outline">취소</button>
          <button id="confirmOkBtn" class="btn btn-primary">확인</button>
        </form>
      </div>
    </div>
  </dialog>
</body>
</html>

head.jspf

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>${pageTitle != null ? pageTitle : 'App'}</title>

  <!-- Tailwind + DaisyUI (CDN) -->
  <script src="https://cdn.tailwindcss.com"></script>
  <link href="https://cdn.jsdelivr.net/npm/daisyui@4.12.10/dist/full.min.css" rel="stylesheet" />

  <!-- jQuery -->
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

  <!-- App Static -->
  <link rel="stylesheet" href="/resource/common.css" />
  <script defer src="/resource/common.js"></script>
</head>
<body class="min-h-screen bg-base-200 text-base-content">
  <!-- Navbar -->
  <header class="navbar bg-base-100 shadow-sm">
    <div class="flex-1">
      <a href="/home/main" class="btn btn-ghost text-xl">MyApp</a>
    </div>
    <div class="flex-none gap-2">
      <a href="/usr/article/list" class="btn btn-ghost">Articles</a>
      <a href="/usr/member/join" class="btn btn-ghost">Join</a>
      <c:if test="${!rq.isLogined() }">
      <a href="/usr/member/login" class="btn btn-primary">Login</a>
      </c:if>
      <c:if test="${rq.isLogined() }">
      <a href="/usr/member/doLogout" class="btn btn-primary">Logout</a>
      </c:if>
      <!-- 다크모드 토글 -->
      <label class="swap swap-rotate ml-2">
        <input id="themeToggle" type="checkbox" />
        <svg class="swap-on fill-current w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5.64 17l-.71.71a9 9 0 1012.72-12.72l-.71.71A7 7 0 115.64 17z"/></svg>
        <svg class="swap-off fill-current w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.76 4.84l-1.8-1.79-1.42 1.41 1.79 1.8 1.43-1.42zM1 13h3v-2H1v2zm10-9h2V1h-2v3zM4.96 19.78l1.8-1.79-1.42-1.41-1.79 1.8 1.41 1.4zM20 13h3v-2h-3v2zm-7 10h2v-3h-2v3zm6.24-4.22l1.41-1.4-1.79-1.8-1.41 1.41 1.79 1.79zM12 6a6 6 0 100 12A6 6 0 0012 6z"/></svg>
      </label>
    </div>
  </header>

  <!-- 페이지 컨테이너 -->
  <main class="container mx-auto px-4 py-8">
    <h1 class="text-2xl md:text-3xl font-bold mb-6">${pageTitle}</h1>

main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>


<c:set var="pageTitle" value="MAIN PAGE"></c:set>

<%@ include file="../common/head.jspf"%>

<h1>Spring Boot + JSP 정상 작동!</h1>

<div><div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nobis in eaque iure ut accusantium soluta pariatur dolorem consequatur ipsam ab. Optio et error aperiam quos eos possimus commodi aspernatur nemo.</div>
<div>Doloremque fugit eos veritatis fugiat explicabo minus dolore necessitatibus et eum voluptatibus autem ipsum assumenda atque numquam aspernatur quibusdam neque qui aut sit eligendi. Non magnam voluptate enim odit sapiente.</div>
</div>

<%@ include file="../common/foot.jspf"%>

join.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ include file="/WEB-INF/jsp/usr/common/head.jspf"%>
<c:set var="pageTitle" value="Join" />

<div class="card bg-base-100 shadow max-w-2xl mx-auto">
  <div class="card-body">
    <form action="/usr/member/doJoin" method="post" id="joinForm" data-safe-submit class="grid md:grid-cols-2 gap-4">
      <div class="form-control">
        <label class="label"><span class="label-text">아이디</span></label>
        <input name="loginId" class="input input-bordered" required />
      </div>
      <div class="form-control">
        <label class="label"><span class="label-text">비밀번호</span></label>
        <input name="loginPw" type="text" class="input input-bordered" required/>
      </div>
      <div class="form-control">
        <label class="label"><span class="label-text">이름</span></label>
        <input name="name" class="input input-bordered" required />
      </div>
      <div class="form-control">
        <label class="label"><span class="label-text">닉네임</span></label>
        <input name="nickname" class="input input-bordered" required />
      </div>
      <div class="form-control">
        <label class="label"><span class="label-text">휴대폰</span></label>
        <input name="cellphoneNum" type="text" class="input input-bordered"/>
      </div>
      <div class="form-control">
        <label class="label"><span class="label-text">이메일</span></label>
        <input name="email" type="text" class="input input-bordered"/>
      </div>

      <div class="md:col-span-2 flex justify-end gap-2 mt-4">
        <a href="/home/main" class="btn">취소</a>
        <button class="btn btn-primary" type="submit">회원가입</button>
      </div>
    </form>
  </div>
</div>

<script>
  $("#joinForm").on("submit", function(e){
    const email = $("input[name=email]").val().trim();
    if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)){
      e.preventDefault();
      showToast("이메일 형식을 확인해주세요.", "warning");
    }
  });
</script>

<%@ include file="/WEB-INF/jsp/usr/common/foot.jspf"%>

login.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ include file="/WEB-INF/jsp/usr/common/head.jspf"%>
<c:set var="pageTitle" value="Login" />

<div class="hero min-h-[60vh]">
  <div class="hero-content w-full max-w-md">
    <div class="card bg-base-100 w-full shadow">
      <div class="card-body">
        <form action="/usr/member/doLogin" method="post" data-safe-submit>
          <div class="form-control">
            <label class="label"><span class="label-text">아이디</span></label>
            <input name="loginId" class="input input-bordered" required />
          </div>
          <div class="form-control mt-3">
            <label class="label"><span class="label-text">비밀번호</span></label>
            <input name="loginPw" type="password" class="input input-bordered" required />
          </div>
          <div class="form-control mt-6">
            <button class="btn btn-primary" type="submit">로그인</button>
          </div>
          <div class="text-sm text-right mt-2">
            <a href="/usr/member/join" class="link">회원가입</a>
          </div>
        </form>
      </div>
    </div>
  </div>
</div>

<%@ include file="/WEB-INF/jsp/usr/common/foot.jspf"%>

common.css

@charset "UTF-8";
/* 공통 커스텀 */
:root {
  --app-max-width: 1100px;
}
.container {
  max-width: var(--app-max-width);
}
.table td, .table th {
  white-space: nowrap;
}

common.js

// 테마 토글
$(function () {
  const $toggle = $("#themeToggle");
  const initial = localStorage.getItem("theme") || "light";
  setTheme(initial);
  $toggle.prop("checked", initial !== "light");

  $toggle.on("change", function () {
    setTheme(this.checked ? "dark" : "light");
  });

  function setTheme(theme) {
    document.documentElement.setAttribute("data-theme", theme);
    localStorage.setItem("theme", theme);
  }
});

// Toast 헬퍼
window.showToast = function (message, type = "info", ms = 2500) {
  const $toast = $("#toast");
  const color = {
    info: "alert-info",
    success: "alert-success",
    warning: "alert-warning",
    error: "alert-error",
  }[type] || "alert-info";

  const html = `<div class="alert ${color} shadow">${message}</div>`;
  $toast.removeClass("hidden").append(html);
  setTimeout(() => $toast.addClass("hidden").empty(), ms);
};

// Confirm 모달 (Promise)
window.confirmAsync = function (message = "진행할까요?") {
  return new Promise((resolve) => {
    const modal = document.getElementById("confirmModal");
    $("#confirmMessage").text(message);
    modal.showModal();
    $("#confirmOkBtn").one("click", function () {
      resolve(true);
    });
    $(modal).one("close", function () {
      resolve(false);
    });
  });
};

// 폼 중복 전송 방지
$(document).on("submit", "form[data-safe-submit]", function () {
  const $btn = $(this).find("button[type=submit], input[type=submit]");
  $btn.prop("disabled", true).addClass("loading");
  setTimeout(() => $btn.prop("disabled", false).removeClass("loading"), 3000);
});

0개의 댓글