본문 바로가기

Back-End/Spring boot

[JAVA][Spring]관리자 페이지 만들기

~ 목차 ~

1-1. User.java

  - UserRole 만들기

1- 2. 회원가입 시 관리자 역할 부여 하기

  - signup.html : checkbox이용

  - UserController.java : @PostMapping("/signup")

 

2. AdminController.java 만들기

  - 사용자 정보 가져오기

  - 사용자 역할 확인하여 isAdmin 변수 설정

 

3. Admin.html 만들기

 - user수, 구독자수, 총 매출, 고객 문의  수

 - QnA 리스트 (승인/삭제)

 - Coupon 리스트

 

4. nav에 관리자 역할인 경우에만 Admin 버튼 보이게 설정하기

 


1-1. User.java

  1) User.java에 UserRole 만들기

 

@Entity
@Data
public class User implements Serializable {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	private String email;
	private String pwd;
	private String name;
	private Integer phone;
	private Date creDate;
	private String carname;
	@Column(nullable = true)
	private Integer count;
	private UserRole role;

	public enum UserRole {
		CUSTOMER, // 일반 고객0
		ADMIN // 관리자1
	}

	public String getRole() {
		return role.name(); // 열거형 값의 이름을 문자열로 반환
	}
}

 

public enum UserRole : 열거형(Enumeration)을 정의 , 관련된 상수 값(CUSTOMER, ADMIN)을 그룹화하는 자료형 -> 각 상수는 0부터 정수 값으로 자동으로 할당됨.

public String getRole() : UserRole 열거형 내부에서 현재 사용자 역할을 나타내는 role 변수의 이름을 문자열로 반환하는 함수

 

※ implements Serializable  쓰는 이유 

 

<SpringBoot 서버 재구동 시 세션의 값이 제거되는 현상>
 * 로그인 자꾸 풀리는 문제(session초기화)

    1. 자바에서 제공되는 자료형은 자동으로 제거되지 않음
    2. 별도로 작성한 클래스는 자동으로 제거됨

* 해결방법
    : 별도로 작성한 클래스에 Serializable 인터페이스를 구현하면 자동으로 제거되지 않음

 

 

2) UserRepository.java

public interface UserRepository extends JpaRepository<User, Long> {
  public User findByEmailAndPwd(String email, String pwd);
  public User findByEmail(String email);
  public User findById(long id);
  List<User> findByEmailAndNameAndPhone(String email,String name, Integer phone);
}

1- 2. 회원가입 시 관리자 역할 부여 하기

 

  1) signup.html

      - input의 checkbox 이용하기

<div class="mb-3 form-check">
     <input type="checkbox" class="form-check-input" id="isAdmin" name="isAdmin">
     <label class="form-check-label" for="isAdmin">관리자</label>
</div>

  2) UserController.java

@PostMapping("/signup")
	public String signupPost(@ModelAttribute User user, @RequestParam(value = "isAdmin", required = false) boolean isAdmin) 
    {
		String userPwd = user.getPwd();
		String encodePwd = passwordEncoder.encode(userPwd);
		user.setPwd(encodePwd);
		user.setCreDate(new Date());

		// 사용자 역할 설정(삼항연산자)
		User.UserRole userRole = isAdmin ? User.UserRole.ADMIN : User.UserRole.CUSTOMER;

		// 사용자 정보 저장
		user.setRole(userRole);

		userRepository.save(user);

		// User 수 증가
		List<User> users = userRepository.findAll();
		int userCount = users.size();
		userCount++;
		for (User u : users) {
			u.setCount(userCount);
		}
		userRepository.saveAll(users);

		return "redirect:/";
	}

※ @RequestParam 어노테이션의 required 속성을 false로 설정하는 이유

 : 해당 요청 매개변수(parameter)가 반드시 필수가 아니게 함, required=false를 사용하여 요청 매개변수가 누락되더라도       예외가 발생하지 않도록 설정


2. AdminController.java 만들기

  - 사용자 정보 가져오기

  - 사용자 역할 확인하여 isAdmin 변수 설정

 

@Controller
public class AdminController {

    @Autowired 
	UserRepository userRepository;

	@Autowired
	HttpSession session;

  @GetMapping("/admin")
	public String admin(Model model) {
		

	// 사용자 정보 가져오기
    User user = (User) session.getAttribute("user_info");

    // 사용자의 역할을 확인하여 isAdmin 변수 설정
    boolean isAdmin = user != null && "ADMIN".equals(user.getRole());
    model.addAttribute("isAdmin", isAdmin);
		
	
		return "/admin";
	}
}

※ 로그인 하면 session에 user_info 값이 들어가도록 설정해 놓았다. 

    그 값으로 사용자 정보를 가져오는 코드를 넣었다.(관리자인지 고객인지 알기 위한 코드) 

※ isAdmin 변수를 usernull이 아니고, 사용자의 역할이 "ADMIN"인 경우에만 true로 설정합니다.

※ model.addAttribute("isAdmin", isAdmin);  << Model model을 넣어야함

  • 모델 객체(model)에 isAdmin 변수를 추가
  • 이렇게 하면 해당 모델 속성을 뷰(예: HTML 템플릿)에서 사용할 수 있게 됨. 예를 들어, 뷰에서 isAdmin 속성을 이용하여 사용자에게 특정 기능이나 페이지 접근 권한을 부여할 수 있다.

3. Admin.html 만들기

  - container 만들기

  - content-section 만들기

  - 추가 기능들 만들기(관련 정보들 가져오기)

 

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">

<head th:replace="common/head">
</head>

<body>

  <div th:replace="common/header"></div>

  <nav th:replace="common/nav"></nav>

  <div class="container">
    <div class="card">
      <div id="main-content">
        <!-- panel -->
        <div id='wrapper'>
          <!-- Content -->
          <!-- 통계 -->
          <div id='content-section'>
            <div class='panel panel-default'>
              <div class='panel-heading'>
                <div class="admintitle">
                  <h4>PIKA ADMIN</h4>
                </div>
              </div>
              <div class='panel-body'>
                <div class='panel-count'>
                  <div class="box1">
                    <div class="count-name">User 수</div>
                    <p th:text="${userCount}"></p>
                  </div>
                  <div class="box2">
                    <div class="count-name">구독자 수</div>
                    <p th:text="${membershipCount}"></p>
                  </div>
                  <div class="box3">
                    <div class="count-name">총 매출</div>
                    <p>$<span th:text="${membershipCount * 3900}"></span></p>
                  </div>
                  <div class="box4">
                    <div class="count-name">고객 문의</div>
                    <p th:text="${totalQnaCount}"></p>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <!-- 충전소 정보 등록 -->
          <div id='content-section'>
            <div class='panel panel-default'>
              <div class='panel-heading'>
                <div class="admintitle">
                  <h4>Charging QnA</h4>
                </div>
              </div>
              <div class='panel-body'>
                <table class="table table-striped table-hover">
                  <thead>
                    <tr>
                      <th scope="col">Id</th>
                      <th scope="col">City</th>
                      <th scope="col">Location</th>
                      <th scope="col">Address</th>
                      <th scope="col">급속충전기 (대)</th>
                      <th scope="col">완속충전기 (대)</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr th:each="b:${chargings}">
                      <td th:text="${b.id}"></td>
                      <td th:text="${b.city}"></td>
                      <td th:text="${b.location}"></td>
                      <td th:text="${b.address}"></td>
                      <td th:text="${b.fastch}"></td>
                      <td th:text="${b.slowch}"></td>
                      <td>
                        <a class="btn btn-secondary btn-sm" th:href="@{/chargingsDetail/{id}(id=${b.id})}">View
                          Details</a>
                        <button th:onclick="'chargingsRm(' + ${b.id} +')'">삭제</button>
                      </td>
                    </tr>
                  </tbody>
                </table>
                <div class="plus-charging">
                  <button type="button" class="btn btn-block" id="charging-btn"> 충전소 정보 등록하기</button>
                </div>
              </div>
            </div>
          </div>
          <!-- 차정보 등록 -->
          <div id='content-section'>
            <div class='panel panel-default'>
              <div class='panel-heading'>
                <div class="admintitle">
                  <h4>Car QnA</h4>
                </div>
              </div>
              <div class='panel-body'>
                <table class="table table-striped table-hover">
                  <thead>
                    <tr>
                      <th scope="col">Id</th>
                      <th scope="col">회사</th>
                      <th scope="col">차 이름</th>
                      <th scope="col"></th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr th:each="car : ${carQnas}">
                      <td th:text="${car.id}"></td>
                      <td th:text="${car.qnacompany}"></td>
                      <td th:text="${car.qnacarname}"></td>
                      <td><button th:onclick="'approve(' + ${car.id} + ')'">승인</button>&nbsp;
                        <button th:onclick="'carqnaRm(' + ${car.id} +')'">삭제</button>
                      </td>
                    </tr>
                  </tbody>
                </table>
                <div class="plus-car">
                  <button type="button" class="btn btn-block" id="plus-btn"> 차 정보 등록하기</button>
                </div>
              </div>
            </div>
          </div>
          <!-- 쿠폰 삭제 -->
          <div id='content-section'>
            <div class='panel panel-default'>
              <div class='panel-heading'>
                <div class="admintitle">
                  <h4>Coupon</h4>
                </div>
              </div>
              <div class='panel-body'>
                <table class="table table-striped table-hover">
                  <thead>
                    <tr>
                      <th scope="col">유저</th>
                      <th scope="col">쿠폰 이름</th>
                      <th scope="col">쿠폰 유효기간</th>
                      <th scope="col">쿠폰 번호</th>
                      <th scope="col">쿠폰 사용</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr th:each="coupon : ${coupons}">
                      <td th:text="${coupon.user.email}"></td>
                      <td th:text="${coupon.name}"></td>
                      <td>
                        <span th:text="${coupon.startDate}"></span>
                        ~
                        <span th:text="${coupon.endDate}"></span>
                      </td>
                      <td th:text="${coupon.code}"></td>
                      <td th:text="${coupon.used}"></td>
                      <td>
                        <button th:onclick="'couponRm(' + ${coupon.id} +')'">삭제</button>
                      </td>
                    </tr>
                  </tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>
    //충전소등록버튼
    document.querySelector('#charging-btn').addEventListener('click', () => {
      location = '/chlregister';
    });
    //충전소 삭제 버튼
    function chargingsRm(id) {
      const isOk = confirm('삭제하시겠습니까?');
      if (isOk) {
        location = `/chargingslist/remove?id=${id}`;
      }
    }
    //차 등록 버튼
    document.querySelector('#plus-btn').addEventListener('click', () => {
      location = '/addCar';
    });
    //차 삭제 버튼
    function carqnaRm(id) {
      const isOk = confirm('삭제하시겠습니까?');
      if (isOk) {
        location = `/admin/carQna/remove?id=${id}`;
      }
    }
    //승인 버튼
    function approve(id) {
      const isOk = confirm('승인하시겠습니까?');
      if (isOk) {
        location = `/admin/carQna/approve?id=${id}`;
      }
    }
    //쿠폰 삭제 버튼
    function couponRm(id) {
      const isOk = confirm('삭제하시겠습니까?');
      if (isOk) {
        location = `/coupon/remove?id=${id}`;
      }
    }
  </script>
</body>

</html>

4. nav.html에 관리자 역할인 경우에만 Admin 버튼 보이게 설정하기

 

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <div class="container">
    <div class="sidebar" style="width: 300px; height: 100%;">
      <button class="button">⚡
      </button>
      <div class="content">
        <!-- <nav class="nav">
          <div class="nav-1">
            <div class="head_logo">
              <a href="/">
                <img class="logo_text" src="car.png" alt="로고" />
              </a>
            </div>
          </div> -->
        <div class="nav-2">
          <div class="menubar">
            <a class="word" th:href="@{/}">홈</a>
          </div>
          <div class="menubar">
            <a class="word" th:href="@{/map}">충전소 찾기</a>
            <!-- <div class="submenu">
              <a class="word" th:href="@{/map}">지도</a>
              <a class="word" th:href="@{/map}">목록 추가</a>
            </div> -->
          </div>
          <div class="menubar">
            <a class="word">게시판</a>
            <div class="submenu">
              <a class="word" th:href="@{/board/list}">공지사항</a>
              <a class="word" th:href="@{/qna/list}">Q&A</a>
            </div>
          </div>
          <div class="menubar">
            <a class="word">정보마당</a>
            <div class="submenu">
              <a class="word" th:href="@{/statistic/chargerStatus}">통계정보</a>
              <a class="word" th:href="@{/statistic/ChargerExpense}">서비스안내</a>
            </div>
          </div>
          <div class="menubar">
            <a class="word">미디어</a>
            <div class="submenu">
              <a class="word" th:href="@{/media/news?searchText=전기차}">전기차 뉴스</a>
              <a class="word" th:href="@{/media/reels}">전기차 영상</a>
              <a class="word" th:href="@{/media/gamepage}">전기차 게임</a>
            </div>
          </div>
          <div class="menubar">
            <a class="word">멤버십</a>
            <div class="submenu">
              <a class="word" th:href="@{/exchange}">코인교환</a>
              <a class="word" th:href="@{/coupon}">쿠폰함</a>
              <br>
              <a class="word" th:href="@{/membership}">가입하기</a>
              <a th:if="${session != null and session.user_info != null and session.has_membership}" class="word" th:href="@{/mymembership}">나의 멤버십</a>
            </div>
          </div>
        </div>
        <br>
        <br>
        <div th:if="${session.user_info == null}" class="menubar">
          <a th:href="@{/signin}" class="word">로그인</a>
          <a th:href="@{/signup}" class="word">회원가입</a>
        </div>
          <div th:unless="${session.user_info == null}" class="menubar">
            <span class="word">[[ ${session.user_info.name} + '님 반갑습니다.' ]]</span>
            <br>
              <a class="word" th:href="@{/exchange}">나의 찌리릿: <span th:text="${userCoin}"></span>개</a>
            <br>
            <a th:href="@{/mypage(email=${session.user_info.email})}" class="word">Mypage</a>
            <br>
            <!-- 관리자 권한으로 로그인하면 관리자 페이지 링크 보이게 설정 -->
            <a th:if="${session != null and session.user_info != null and session.user_info.role == 'ADMIN'}" th:href="@{/admin}" class="word">
              Admin
            </a>
            <br>
            <a th:href="@{/signout}" class="word">로그아웃</a>
          </div>
        </div>
      </div>
    </div>

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

  <script>
    const button = document.querySelector(".button");
    const sidebar = document.querySelector(".sidebar");
    let isSidebarOpen = false;
    let timeoutId;

    sidebar.style.width = "0";

    button.addEventListener("click", () => {
      if (!isSidebarOpen) {
        sidebar.style.width = "300px";
        isSidebarOpen = true;
      } else {
        sidebar.style.width = "0";
        button.style.right = "80px";
        closeSubmenus();
        isSidebarOpen = false;
      }
    });

    function closeSubmenus() {
      const submenus = document.querySelectorAll(".submenu");
      submenus.forEach((submenu) => {
        submenu.style.display = "none";
      });

      // If there's no submenu open, move the button back to right.
      if (!document.querySelector('.submenu[style*="block"]')) {
        button.style.right = "80px";
      }
    }

    const menuItems = document.querySelectorAll(".menubar");

    menuItems.forEach((menuItem) => {

      menuItem.addEventListener("mouseover", () => {

        if (isSidebarOpen) {
          closeSubmenus();
          const submenu = menuItem.querySelector(".submenu");

          if (submenu) {
            submenu.style.display = "block";
            button.style.right = "310px"; // If a submenu opens, move the button right.
          }
        }
      });

      menuItem.addEventListener("mouseout", () => {

        if (isSidebarOpen) {
          const submenu = menuItem.querySelector(".submenu");

          if (submenu && submenu.style.display === "block") {
            timeoutId = setTimeout(() => {
              submenu.style.display = "none";
              if (!document.querySelector('.submenu[style*="block"]')) { // check if there's no other opened sub-menu
                button.style.right = "80px"; // If a submenu closes, move the button back to right.
              }
            }, 1000); // wait for one second before closing the submenu
          }
        }
      });

      menuItem.addEventListener("mouseover", () => clearTimeout(timeoutId)); // if mouse comes back on item before timeout ends, clear the timeout

    });
  </script>
</body>

</html>

 

Thymeleaf (타임리프) 사용하여 html에서 버튼 보이는 조건 생성해주기 ( th:if )

<a th:if="${session != null and session.user_info != null and session.user_info.role == 'ADMIN'}" th:href="@{/admin}" class="word">
              Admin
            </a>

조건 1. 세션이 null이 아닐경우

조건 2. 세션에 저장된 user_info가 null이 아닐경우 (로그인되어야 함)

조건 3. 세션에 저장된 user_info에 저장된 role이 ADMIN 문자열인 경우 (관리자로 회원가입되어야함)

이 세가지 조건이 모두 만족(and)일 경우에 Admin a링크가 보이고 admin.html으로 이동!

 


결과

 

관리자로 회원가입하고 로그인했을때 nav 상태
Admin페이지

  감사합니다 :)

728x90