D+65::Shop 프로젝트 관리자 기능 구현

Am.Vinch·2022년 9월 28일
0

20220928_tue

어제 내용 정리
1. login_result.html 에서 권한여부(ROLE)에 따라 관리자(MANAGER),일반회원(MEMBER)으로 로그인시 어떻게 이동할지 sec문을 이용하여 alert창을 띄운 후 이동하도록 한다.
2. AdminController 를 만들어서 관리자컨트롤러로 페이지 이동하도록 만든다.
3. 관리자페이지의 가장 첫 화면은 '상품등록화면' 페이지이다.그래서 reg_item.html 파일을 만들어 준다.
4. 위 페이지는 layout으로 총 3단화면(top,side,content)으로 구성되어야한다.
: admin_layout으로 위 구성대로 구현한다.
단, th:replace를 사용할때 지정한 파일이름을 불러올때 일치하여 사용해야한다.

  1. side.html파일에서 사이드메뉴를 구현한다. (부트스트랩 이용)

오늘 실습 내용

  • 관리자 페이지 구현
  • 상품등록 화면구성기능
  • 카테고리목록조회
  • 카테고리 등록기능
  • ajax 적용하여 사용여부 변경

SHOP 관리자 페이지 기능구현

DB

SHOP_USER 테이블 생성

  • 파일명 : shop_user.sql (shop_user 계정 생성)
DROP TABLE ITEM_CATECGORY;
DROP TABLE SHOP_ITEM
-- 상품 카테고리 테이블
CREATE TABLE ITEM_CATECGORY (
    CATE_CODE VARCHAR2(50) CONSTRAINT ITEM_CATECGORY_PK PRIMARY KEY
    , CATE_NAME VARCHAR2(100) NOT NULL  
    , CATE_STATUS VARCHAR2(20) DEFAULT 'USE' NOT NULL
);
-- 상품 카테고리 테이블 조회
SELECT * FROM ITEM_CATECGORY;
-- 상품정보 테이블
CREATE TABLE SHOP_ITEM (
    ITEM_CODE VARCHAR2(50) CONSTRAINT SHOP_ITEM_PK PRIMARY KEY
    , ITEM_NAME VARCHAR2(100) NOT NULL
    , ITEM_PRICE NUMBER NOT NULL
    , ITEM_COMMENT VARCHAR2(200) 
    , REG_DATE DATE DEFAULT SYSDATE
    , ITEM_STOCK NUMBER NOT NULL
    , CATE_CODE VARCHAR2(50) NOT NULL CONSTRAINT SHOP_ITEM_FK  REFERENCES ITEM_CATECGORY (CATE_CODE) ON DELETE CASCADE 
    -- ON DELETE CASCADE 
    --부모 테이블에서 row 를 삭제할 경우 연결된 자식 테이블의 row 가 함께 삭제
    --연결된 데이터를 한 번에 지울 수 있어 데이터의 관리가 편리해지고 일관성을 유지
);

-- 상품정보 테이블 조회
SELECT * FROM SHOP_ITEM;

(관리자 관련)패키지생성

  • Kh.study.shop
    • admin
      - Controller
      - AdminController.java
  • AdminController.java
package Kh.study.shop.admin.controller;
@Controller
@RequestMapping("/admin")
public class AdminController {
	@Resource(name = "adminService")
	private AdminService adminService;
///////////////////////////////////////////////////////////////////	
	
	//관리자_첫화면 :상품등록 및 상세페이지 동시
	@GetMapping("/regItem")
	public String admin(Model model) {
		//상품목록조회
		model.addAttribute("cateList",adminService.cateList());
		return "content/admin/reg_item";
	}
	
	//카테고리 등록
	@PostMapping("/regCate")
	public String regCate(CategoryVO categoryVO) {
		adminService.regCate(categoryVO);
		// 카테고리 등록 후, 다시 첫화면 페이지로 이동
		return "redirect:/admin/regItem";
	}
	
	//카테고리상태 변경(ajax실행)
	//ajax사용하는 이유! 페이지이동없이하려고!!
	// -> 그래서 페이지이동 리턴값이 없다!
	@ResponseBody
	@PostMapping("/changeStatus")
	public void changeStatus(CategoryVO categoryVO) {
		//상태변경하기
		adminService.changeStatus(categoryVO);
	}	
}
  • AdminService
package Kh.study.shop.admin.service;

public interface AdminService {
	//카테고리 목록조회
	List<CategoryVO> cateList();
	
	//카테고리 등록
	void regCate(CategoryVO categoryVO);
	
	//카테 상태 변경
	void changeStatus(CategoryVO categoryVO);
}
  • AdminServiceImpl
package Kh.study.shop.admin.service;
@Service("adminService")
public class AdminServiceImpl implements AdminService {
	@Autowired//어노테이션으로 객체생성
	private SqlSessionTemplate sqlSession;
	
	//목록조회
	@Override
	public List<CategoryVO> cateList() {
		return 	sqlSession.selectList("adminMapper.cateList");

	}
	//카테고리 등록
	@Override
	public void regCate(CategoryVO categoryVO) {
		sqlSession.insert("adminMapper.regCate",categoryVO);
	}
	//카테 상태 변경
	@Override
	public void changeStatus(CategoryVO categoryVO) {
		sqlSession.update("adminMapper.changeStatus",categoryVO);
	}
}

VO 클래스 파일 생성

  • item
    • vo
      - ItemVO
      • CategoryVO
  • ItemVO
package Kh.study.shop.item.vo;
@Setter
@Getter
@ToString
public class ItemVO {
	private String	itemCode;
	private String	itemName;
	private int	itemPrice;
	private String	itemComment;
	private String	regDate;
	private int	itemStock;
	private String	cateCode;
}
  • CategoryVO
package Kh.study.shop.item.vo;
@Setter
@Getter
@ToString
public class CategoryVO {
	private String	cateCode;
	private String	cateName;
	private String	cateStatus;
}

mapper

  • src/main/resources
    • mappers
      • admin-mapper.xml
  • admin-mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--해당 파일에 모든 쿼리문을 작성 -->
<mapper namespace="adminMapper">
	<resultMap type="Kh.study.shop.item.vo.CategoryVO" id="category">
		<id column="CATE_CODE"  	 property="cateCode"/>
		<result column="CATE_NAME"   property="cateName"/>
		<result column="CATE_STATUS" property="cateStatus"/>
	</resultMap>
	
	<!-- 카테고리 목록조회 -->
	<select id="cateList" resultMap="category">
		SELECT 
			CATE_CODE
			,CATE_NAME
			,CATE_STATUS
		FROM ITEM_CATECGORY
		ORDER BY CATE_CODE
	</select>
	
	 <!-- 카테고리 등록 -->
	 <insert id="regCate">
		<selectKey resultType="String" keyProperty="cateCode" order="BEFORE">
		SELECT 'CATE_' ||LPAD(NVL(MAX(TO_NUMBER(SUBSTR(CATE_CODE,6))),0)+1,3,0)
 		FROM ITEM_CATECGORY
		</selectKey>
		INSERT INTO ITEM_CATECGORY 
			(CATE_CODE,CATE_NAME)
		VALUES 
			(#{cateCode},#{cateName})
	</insert>    
	
	<!-- 카테고리 상태 변경 -->
	<update id="changeStatus">
	UPDATE ITEM_CATECGORY
	SET CATE_STATUS = #{cateStatus}
	WHERE CATE_CODE = #{cateCode}
	</update>
</mapper> 

css,js

  • src/main/resources
    • static
      • css
        • admin.css
      • js
        • reg_item.js
  • top.html(수정)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

<div th:fragment="top">
<!-- 아이템컨트롤러에서 던져진 로그인여부값을 히든으로 가져와서 받는다. -->
	<input type="hidden" id="isLoginFail" th:value="${isLoginFail}">

	<div class="row">
		<div class="col text-end" sec:authorize="isAnonymous()">
			<span data-bs-toggle="modal" data-bs-target="#join_modal" style="color: #3D8361;">JOIN</span>
			<span data-bs-toggle="modal" data-bs-target="#login_modal" style="color: #3D8361;">LOGIN</span>
		</div>
		<div class="col text-end" sec:authorize="isAuthenticated()" >
			<!-- 아래 sec 데이터값은 아이디값을 추출되기때문에 span태그 사이에 다른 문자열을 넣을 수 없다. -->
			<!-- 그래서 이어서 문자열을 작성하려면 span태그 밖에서 작성하도록 한다.  -->
			<span sec:authentication="name"></span>	님 반갑습니다😊
			<!-- 로그아웃 컨트롤러는 시큐리티에서 자동으로 호출되기때문에 컨트롤러나 form태그 작성 불필요 -->
			<!-- 단순히 a태그나 버튼으로 경로이동만 시켜주면 된다! --> 
			<button th:onclick="location.href='@{/logout}';" sec:authorize="isAuthenticated()" type="submit"  class="btn btn-light">LOGOUT</button>
		</div>
	</div>
	<div class="row">
		<div class="col text-center" > 
			<span style=" color: #A1C298; font-weight: bold; font-size: 55px;">S H O P</span>
		</div>
	</div>
	<!--top MENU -->
	<div class="row">
		<div class="col">
			<nav class="navbar navbar-expand-lg bg-light">
			  <div class="container-fluid">
			    <a class="navbar-brand" href="#" style="color: #3D8361;">MENU</a>
			    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
			      <span class="navbar-toggler-icon"></span>
			    </button>
			    <div class="collapse navbar-collapse" id="navbarNav">
			      <ul class="navbar-nav">
			        <li class="nav-item">
			          <a class="nav-link active" aria-current="page" href="#">Home</a>
			        </li>
			        <li class="nav-item">
			          <a class="nav-link" href="#">Item</a>
			        </li>
			        <li class="nav-item">
			          <a class="nav-link" href="#">Order</a>
			        </li>
			        <li class="nav-item">
			          <a class="nav-link " href="#">Cart</a>
			        </li>
			      </ul>
			    </div>
			  </div>
			</nav>
		</div>
	</div>
	
	
<!-- login 클릭시 실행 Modal  -->
<div class="modal fade" id="login_modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
  
    <div class="modal-content">
    
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel" style="font-weight: bold;">L O G I N</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      
      <!-- 로그인창 modal  -->
      <div class="modal-body">
      <!-- 로그인 - 세션이용 데이터 form태그로 보내주기 -->
      <!-- 부트스트랩에서 가져왔기때문에 함부러 수정x 형식에 맞게 해야사용가능하다 -->
		<form class="row g-3" name="formLogin" method="post" action="/member/login" >
		  <div class="mb-3">
		  <!-- label은 input태그의 라벨(스티커)이다. 웬만하면, for값과 id값이 같아야한다 -->
		  <!-- html 파일 내에 memberId 라는 id값이 여러개 존재한다.(중복발생) -->
		    <label for="memberId" class="form-label" >ID</label>
		    <input id="memberId" type="text" class="form-control" name="memberId" aria-describedby="emailHelp" placeholder="Input your ID"  >
		  </div>
		  <div class="mb-3">
		    <label for="memberPw" class="form-label">Password</label>
		    <input type="password" class="form-control" name="memberPw"  id="memberPw" placeholder="Input your password">
		  </div>
	      <div class="modal-footer">
	        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
	        <button type="submit" class="btn btn-primary">Login</button>  <!--submit은 ajax에서 사용하는 것이 아니다!!! onclick이용해서 ajax? -->
	        <button type="button" class="btn btn-primary" onclick="goLogin();">AjaxLogin</button>
	      </div>
		</form>
      </div>

    </div>
  </div>
</div>

<!-- join 클릭시 실행 Modal -->
<div class="modal fade" id="join_modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
  
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel" style="font-weight: bold;">J O I N</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      
      <div class="modal-body">
      <!--  너비 조정-->
      <!-- col-12 / col-6 :  한 줄 너비 총 크기 12이기때문에 12분등 중 어느만큼 사용하는건지 -->
	  	<form class="row g-3" action="/member/join" method="post">
		  	<!-- class값은 고정 건드리면 안됨. 부트스트랩사용하기위한 의미 -->
		    <!-- for태그와 id태그 이름은 같게 하도록 -->
		  <div class="col-12">
		    <label for="memberId" class="form-label">ID</label>
		    <input type="text" class="form-control" id="memberId" placeholder="put your ID" name="memberId">
		  </div>
		  <div class="col-12">
		    <label for="memberPw" class="form-label">PASSWORD</label>
		    <input type="password" class="form-control" id="memberPw" name="memberPw" placeholder="put your Password">
		  </div>
		  <div class="col-12">
		    <label for="memberName" class="form-label">NAME</label>
		    <input type="text" class="form-control" id="memberName" name="memberName" placeholder="put your Name">
		  </div>

		 <!-- 주소(상세주소 + 검색버튼추가) -->
		 <!-- 줄을 맞추기위해 실제내용은 화면에 안보이도록 공백문자사용/ class에 form-control 붙여넣기 -->
		  <div class="col-9">
		    <label for="memberAddr" class="form-label"> ADDRESS </label>
		    <input type="text" class="form-control" id="memberAddr" name="memberAddr"  readonly onclick="searchAddr();"><!-- 값변경못하도록 읽기전용속성값부여하기 ( 데이터넘기기 가능) -->
		  </div>
		  <div class="col-3" >
		    <label for="" class="form-label">&nbsp;</label>
			<input type="button" class="btn btn-secondary form-control" onclick="searchAddr();" value="Search">
		  </div>
		  <div class="col-12" >
		    <input type="text" class="form-control" id="addrDetail" name="addrDetail">
		  </div>
		 
		  <div class="col-12">
		    <label for="memberEmail" class="form-label">EMAIL</label>
		    <input type="text" class="form-control" id="memberEmail" placeholder="put your Email" name="memberEmail">
		  </div>
		  
		  <div class="d-grid gap-2 col-12" >
		    <!-- 버튼클릭하나로 모든 데이터 가져가야하므로 무조건 submit!  -->
		    <button type="submit" class="btn btn-primary">JOIN</button>
		  </div>
		</form>
	  </div>      
    </div>
  </div>
</div>
<!-- 카카오API 사용위해 자바스크립트 사용 전, 미리 로드하기 -->
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<!-- js불러오는 방법_기본 (1) -->
<!-- 주의)html 파일 이외의 파일들은 static 폴더 기준이면서 맨앞은 '/'넣어줘야한다!!!(templates X) -->
<!-- src속성값을 넣어주면 해당 파일에 js찾아서 불러온다는 기능 -->
<!-- <script type="text/javascript" src="/js/layout/top.js"></script> -->

<!-- js불러오는 방법_타임리프 (2) -->
<!-- 기본방법과 비슷하지만 @{}사용해야한다! -->
<script type="text/javascript" th:src="@{/js/layout/top.js}"></script>

<!-- 위 방법의 차이점은? -->
<!-- 디자이너와 협업시 용이하다.
이클립스를 사용하지 않는 디자이너분들은 타임리프를 사용해서 타임리프로 된 html파일은 데이터 던져주면,
데이터값을 확인할 수 있다. 타임리프를 사용하지않으면 디자이너분들이 사용하기 불편하기때문에 사용하는 것이다. -->
</div>

</html>
  • reg_item.js
    :사용여부(USE,UNUSE)라디오 버튼 클릭 시 진행
function changeStatus(cateCode, status){
   const result =  confirm('상품의 상태를 변경할까요?');
   if(result){
      //ajax start
      $.ajax({
         url: '/admin/changeStatus', //요청경로
         type: 'post',
         data: {'cateStatus':status,'cateCode':cateCode}, //필요한 데이터
         success: function(result) {
            alert('상태를 변경했습니다');
         },
         error: function() {
            alert('실패');
         }
      });
      //ajax end
   }
}
  • admin.css
/* 모든파일에 적용되는 공통 css  */
@charset "UTF-8";
@font-face {
    font-family: 'RIDIBatang';
    src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_twelve@1.0/RIDIBatang.woff') format('woff');
    font-weight: normal;
    font-style: normal;
}

body {
    font-family: 'RIDIBatang';
	
}
a{
	text-decoration:none;	
	color: #0F3D3E;
	font-size: 18px;
	font-weight: bold;
}
a:hover{
	color: #1C6758;
}
.layoutTable{
	width: 1000px;
	margin: 0 auto;
	border: 1px solid black;
	border-collapse: collapse;
	margin-top: 40px;
	text-align: center;
	
}
.layoutTable > tbody >tr, .layoutTable > tbody > tr >td{
	border: 1px solid black;
}
.layoutTable > tbody> tr{/* 자식 */
	height: 500px;
}
.layoutTable > tbody> tr > td{/* 자식 */
	vertical-align: top;
	padding: 20px;
}
.stuListTable, .stuDetailTable{
	width: 400px;
	border: 1px solid gray;
	border-collapse: collapse;
	margin: 0 auto;
	text-align: center;
}
.stuListTable tr,.stuListTable td{/* 자손 */
	border: 1px solid gray;
}
.stuDetailTable tr,.stuDetailTable td{/* 자손 */
	border: 1px solid gray;
}
span:hover {
	cursor: pointer;/* 손가락모양으로 커서변경 */
}

html

layout

  • src/main/resources
    • templates
      • content
        • admin
          • reg_item.html
    • fragment
      • side.html
    • layout
      • admin_layout.html
  • side.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div th:fragment="side">
<div style="color: #367E18;" >
   <ul><a th:href="@{/admin/setItem}">상품등록</a> </ul>
   <ul><a th:href="@{/admin/setItem}">상품관리</a></ul>
   <ul><a th:href="@{/admin/setRole}">회원권한설정</a></ul>
   <ul><a th:href="@{/admin/setMenu}">메뉴관리</a></ul>
</div>

</div>
</html>
  • reg_item.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout" 
	xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
	layout:decorate="~{layout/admin_layout}"><!-- 물결표시와 중괄호 문법은 선택적으로 사용가능하다. -->
<link href="/css/reg_item.css" rel="stylesheet">

<div layout:fragment="content">
	<!-- 전체화면 -->
	<div class="row">
		<!-- 좌측화면 -->
		<div class="col-5">
				<div class="row">
					<div class="col-12 mb-3"><!-- mb : margin-bottom / ms : margin-side -->
						<h6>CATEGORY MANAGE</h6>
					</div>
					<div class="col-12 mb-3">
            	 		<form class="row g-3" th:action="@{/admin/regCate}" method="post">
		                  <div class="col-8">
		                     <input name="cateName" type="text" class="form-control" id="" placeholder="카테고리를 입력하세요">
		                  </div>
		                  <div class="col-4">
		                     <button type="submit" class="btn btn-primary"><!-- 등록버튼 안에 이미지 들고오기 단, 버튼태그만 가능하며 인풋태그는 불가능 -->
			                     <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cart-check" viewBox="0 0 16 16">
								  <path d="M11.354 6.354a.5.5 0 0 0-.708-.708L8 8.293 6.854 7.146a.5.5 0 1 0-.708.708l1.5 1.5a.5.5 0 0 0 .708 0l3-3z"/>
								  <path d="M.5 1a.5.5 0 0 0 0 1h1.11l.401 1.607 1.498 7.985A.5.5 0 0 0 4 12h1a2 2 0 1 0 0 4 2 2 0 0 0 0-4h7a2 2 0 1 0 0 4 2 2 0 0 0 0-4h1a.5.5 0 0 0 .491-.408l1.5-8A.5.5 0 0 0 14.5 3H2.89l-.405-1.621A.5.5 0 0 0 2 1H.5zm3.915 10L3.102 4h10.796l-1.313 7h-8.17zM6 14a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm7 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
								</svg>
		                     등록</button>
		                  </div>
		         	    </form> 
					</div>
					
			<div class="col-12 mb-3">
					<table class="table table-striped table-hover text-center">
		                 <thead>
		                   <tr>
		                     <th scope="col">No.</th>
		                     <th scope="col">카테코드</th>
		                     <th scope="col">상품명</th>
		                     <th scope="col">사용여부</th>
		                   </tr>
		                 </thead>
		                 <!-- 목록조회 테이블  -->
		                  <tbody>
		                 	 <th:block th:if="${#lists.size(cateList) == 0}">
		                 		<tr>
		                 			<td colspan="4">게시글 목록이 없습니다.</td>
		                 		</tr>
		                 	</th:block> 
		                 	<!-- if문을 그대로 사용하면서 문자열 부정문은 맨앞에 느낌표를 사용한다! -->
		                 	<!-- <th:block th:if="${!(#lists.size(cateList) eq 0)}"> -->
		                 	 <th:block th:unless="${#lists.size(cateList) == 0}"> <!-- 등호대신 eq 도 가능하다 -->
			                   <th:block th:each="cateList, status : ${cateList}"><!-- 행번호추출하는 방법 status추가하기 -->
			                   <tr> 
			                     <td th:text="${status.count}"></td><!-- 행번호추출 -->
			                     <td th:text="${cateList.cateCode}" ></td>
			                     <td th:text="${cateList.cateName}" ></td>
			                     <td>
				                     <!-- 라디오값을 타임리프로 데이터 넘기는 방법?? -->
				                     <!-- 1. 라디오값을 중복이아닌 단 하나만 선택되도록하려면 name값을 동일하게 부여한다 -->
				                     <!-- 2. 각 선택한 라디오값마다 다른 값을 부여해서 다음 페이지에 데이터를 갖고가려한다. -->
				                     <!-- 3. 타임리프로 라디오값 넘기는 방법은 status를 사용해서 행번호를 각각 매겨준다.그래야 각각 다른 값을 갖기때문-->
				                     <!-- 4. 각각의 사용여부의 값이 check된 값인지 확인하기위해서, 새로고침해도 진짜 본인의값인지(사용/미사용)알려면 
				                             카테리스트의 status값이 use인지 unuse인지에 따라 체크되도록 만들어 확인해볼수있다. -->
				                             
									<!-- ajax 함수로 클릭시 이동할 때 변수명 가져가는 방법 -->
									<!-- 함수명 괄호안에 변수명은 항상 대괄호 두개 안에 기존방식대로 넣어준다!! -->
			                         <div class="form-check form-check-inline">
				                       <input class="form-check-input" type="radio" th:name="|cateStatus_${status.count}|" 
				                       	id="use" value="USE" th:checked="${cateList.cateStatus eq 'USE'}"
				                       	th:onclick="changeStatus([[${cateList.cateCode}]], 'USE');" >                      
				                       <label class="form-check-label" for="inlineRadio1">사용</label>
				                     </div>
				                     <div class="form-check form-check-inline">
				                       <input class="form-check-input" type="radio" th:name="|cateStatus_${status.count}|" 
				                      	 id="unuse" value="UNUSE" th:checked="${cateList.cateStatus eq 'UNUSE'}"
				                      	 th:onclick="changeStatus([[${cateList.cateCode}]], 'UNUSE');" >
				                       <label class="form-check-label" for="inlineRadio2">미사용</label>
				                     </div>
			                     </td>
			                   </tr>
                              </th:block>
			                </th:block> 
		                </tbody>
		                
		              </table>
					</div>
				</div>
		</div>
		<!--  우측화면 -->
		<div class="col-7">상품등록화면</div>
	</div>
<!-- 반드시 해당되는 div 태그 안에 있어야 실행이 된다. -->	
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" th:src="@{/js/layout/reg_item.js}"></script>
</div>
</html>
  • admin_layout.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
		xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
<link href="/css/admin.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
</head>

<body>
<div class="container">
	<!-- 고정된 top.html 화면 -->
	<div style="margin-bottom: 50px;" class="row">
		<div class="col">
			<div th:replace="fragment/top::top"></div>
		</div>
	</div>
	<!-- 고정된 side.html 화면 -->
	<!-- col의 12등분을 이용하여 너비조정이 쉽게 가능하다. -->
	<div  style="position: fixed; width: 200px;" class="row">
		<div class="col">
			<div th:replace="fragment/side::side"></div>
		</div>
	</div>
	<!-- 계속바뀌는 베이스화면 -->
	<div style="position: relative; left: 250px;" class="row">
		<div class="col">
			<div layout:fragment="content"></div>
		</div>
	</div>
</div>
</body>

</html>

결과

  • 관리자(admin)계정으로 로그인시, 관리자 페이지 첫화면은 상품등록 페이지로 띄운다.
    단, 좌측은 카테고리 등록기능과 카테고리 목록리스트가 보이는 페이지, 우측은 해당 카테고리별 상품등록 화면으로 분리하여 구현한다.
  • 목록페이지 기능 구현시, 카테고리 데이터가 없으면 "조회된 데이터가 없습니다."를 띄운다.
  • 목록조회 순서는 카테코드의 내림차순으로 진행된다.
  • 등록할때마다 사용여부의 기본값은 사용('USE')값으로 나타내야한다.
  • 'No.'값은 html에서 status 라는 값을 cateList에서 뽑아서 진행한다. -> 행번호 데이터 추출
  • 등록예시1)
  • 등록예시2)
  • 등록결과
profile
Dev.Vinch

0개의 댓글