관리자 상품 조회

송영재·2022년 10월 23일

Spring

목록 보기
12/45
  • 23) 상품 조회 (관리자용) 설계

    • Client 에서 "일반 회원""관리자" 구분하여 API 호출 필요

      1. 일반 회원: GET /api/products
      2. 관리자: GET /api/admin/products
    • 프론트 개발자와 작업 방법 논의

      • 서버에서 역할을 프론트에 내려줄 수 있는 방법

        1. 로그인 성공 시 Response 에 회원 Role 추가

        2. 쿠키 사용

          1. 세션 유지를 위해 쿠키 설정했던 것과 같은 방식

        3. index.html 에 admin 데이터 추가

          1. 관리자인 경우만 → "admin" id 를 가진

            추가 (동적 웹 페이지 사용)

            <div id="admin"></div>
          2. 프론트 개발자 작업

            1. "admin" id 가 내려오면 관리자로 판단
            2. 상품 조회 시 역할별로 분리하여 API 호출
              1. 일반 회원: GET /api/products
              2. 관리자: GET /api/admin/products
  • 24) 상품 조회 (관리자용) 구현

    1. "관리자" 회원에 대한 처리
      1. "관리자"인 경우에만 model 값 ("admin_role": true) 추가

        • [코드스니펫] controller > HomeController
          import com.sparta.springcore.model.UserRoleEnum;
          import com.sparta.springcore.security.UserDetailsImpl;
          import org.springframework.security.core.annotation.AuthenticationPrincipal;
          import org.springframework.stereotype.Controller;
          import org.springframework.ui.Model;
          import org.springframework.web.bind.annotation.GetMapping;
          
          @Controller
          public class HomeController {
              @GetMapping("/")
              public String home(Model model, @AuthenticationPrincipal UserDetailsImpl userDetails) {
                  model.addAttribute("username", userDetails.getUsername());
          
                  if (userDetails.getUser().getRole() == UserRoleEnum.ADMIN) {
                      model.addAttribute("admin_role", true);
                  }
          
                  return "index";
              }
          }
      2. 타임리프 문법에 맞춰, model 에 "admin_role" 값이 있으면

        "admin" id 값을 가진

        추가

        <div th:if="${admin_role}" id="admin"></div>
        • [코드스니펫] resources > templates > index.html
          <!doctype html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta name="viewport"
                    content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
              <meta http-equiv="X-UA-Compatible" content="ie=edge">
              <meta property="og:title" content="00만의 셀렉샵">
              <meta property="og:description" content="관심상품을 선택하고, 최저가 알림을 확인해보세요!">
              <meta property="og:image" content="images/og_selectshop.png">
              <link href="https://fonts.googleapis.com/css2?family=family=Nanum+Gothic&display=swap" rel="stylesheet">
              <link rel="stylesheet" href="css/style.css">
              <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
              <script src="basic.js"></script>
              <title>나만의 셀렉샵</title>
          </head>
          <body>
          <div class="header" style="position:relative;">
              <div id="header-title-login-user">
                  <span th:text="${username}"></span> 님의
              </div>
              <div id="header-title-select-shop">
                  Select Shop
              </div>
          
              <form id="my_form" method="post" action="/user/logout">
                  <a id="logout-text" href="javascript:{}" onclick="document.getElementById('my_form').submit();">로그아웃</a>
              </form>
          </div>
          <div class="nav">
              <div class="nav-see active">
                  모아보기
              </div>
              <div class="nav-search">
                  탐색하기
              </div>
          </div>
          <div id="see-area">
              <div id="product-container">
          
              </div>
          </div>
          <div id="search-area">
              <div>
                  <input type="text" id="query">
                  <!--    <img src="images/icon-search.png" alt="">-->
              </div>
              <div id="search-result-box">
          
              </div>
              <div id="container" class="popup-container">
                  <div class="popup">
                      <button id="close" class="close">
                          X
                      </button>
                      <h1>⏰최저가 설정하기</h1>
                      <p>최저가를 설정해두면 선택하신 상품의 최저가가 떴을 때<br/> 표시해드려요!</p>
                      <div>
                          <input type="text" id="myprice" placeholder="200,000"></div>
                      <button class="cta" onclick="setMyprice()">설정하기</button>
                  </div>
              </div>
          </div>
          <div th:if="${admin_role}" id="admin"></div>
          </body>
          </html>
    2. "관리자" 용 상품 목록 조회 API 구현
      1. GET /api/admin/products 에 대한 처리
        • [코드스니펫] controller > ProductController
          import com.sparta.springcore.dto.ProductMypriceRequestDto;
          import com.sparta.springcore.dto.ProductRequestDto;
          import com.sparta.springcore.model.Product;
          import com.sparta.springcore.security.UserDetailsImpl;
          import com.sparta.springcore.service.ProductService;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.security.core.annotation.AuthenticationPrincipal;
          import org.springframework.web.bind.annotation.*;
          
          import java.util.List;
          
          @RestController // JSON으로 데이터를 주고받음을 선언합니다.
          public class ProductController {
          
              private final ProductService productService;
          
              @Autowired
              public ProductController(ProductService productService) {
                  this.productService = productService;
              }
          
              // 신규 상품 등록
              @PostMapping("/api/products")
              public Product createProduct(@RequestBody ProductRequestDto requestDto,
                                           @AuthenticationPrincipal UserDetailsImpl userDetails) {
                  // 로그인 되어 있는 회원 테이블의 ID
                  Long userId = userDetails.getUser().getId();
          
                  Product product = productService.createProduct(requestDto, userId);
          
                  // 응답 보내기
                  return product;
              }
          
              // 설정 가격 변경
              @PutMapping("/api/products/{id}")
              public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) {
                  Product product = productService.updateProduct(id, requestDto);
          
                  // 응답 보내기 (업데이트된 상품 id)
                  return product.getId();
              }
          
              // 로그인한 회원이 등록한 관심 상품 조회
              @GetMapping("/api/products")
              public List<Product> getProducts(@AuthenticationPrincipal UserDetailsImpl userDetails) {
                  // 로그인 되어 있는 회원 테이블의 ID
                  Long userId = userDetails.getUser().getId();
          
                  return productService.getProducts(userId);
              }
          
              // (관리자용) 등록된 모든 상품 목록 조회
              @GetMapping("/api/admin/products")
              public List<Product> getAllProducts() {
                  return productService.getAllProducts();
              }
          }
        • [코드스니펫] controller > ProductService
          import com.sparta.springcore.dto.ProductMypriceRequestDto;
          import com.sparta.springcore.dto.ProductRequestDto;
          import com.sparta.springcore.model.Product;
          import com.sparta.springcore.repository.ProductRepository;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.stereotype.Service;
          
          import java.util.List;
          
          @Service
          public class ProductService {
          
              private final ProductRepository productRepository;
          
              @Autowired
              public ProductService(ProductRepository productRepository) {
                  this.productRepository = productRepository;
              }
          
              public Product createProduct(ProductRequestDto requestDto, Long userId ) {
                  // 요청받은 DTO 로 DB에 저장할 객체 만들기
                  Product product = new Product(requestDto, userId);
          
                  productRepository.save(product);
          
                  return product;
              }
          
              public Product updateProduct(Long id, ProductMypriceRequestDto requestDto) {
                  Product product = productRepository.findById(id)
                          .orElseThrow(() -> new NullPointerException("해당 아이디가 존재하지 않습니다."));
          
                  int myprice = requestDto.getMyprice();
                  product.setMyprice(myprice);
                  productRepository.save(product);
          
                  return product;
              }
          
              // 회원 ID 로 등록된 상품 조회
              public List<Product> getProducts(Long userId) {
                  return productRepository.findAllByUserId(userId);
              }
          
              // 모든 상품 조회 (관리자용)
              public List<Product> getAllProducts() {
                  return productRepository.findAll();
              }
          }
    3. 프론트엔드 개발자 작업 내용 적용
      • [코드스니펫] resources > static > basic.js
        let targetId;
        
        $(document).ready(function () {
            // id 가 query 인 녀석 위에서 엔터를 누르면 execSearch() 함수를 실행하라는 뜻입니다.
            $('#query').on('keypress', function (e) {
                if (e.key == 'Enter') {
                    execSearch();
                }
            });
            $('#close').on('click', function () {
                $('#container').removeClass('active');
            })
        
            $('.nav div.nav-see').on('click', function () {
                $('div.nav-see').addClass('active');
                $('div.nav-search').removeClass('active');
        
                $('#see-area').show();
                $('#search-area').hide();
            })
            $('.nav div.nav-search').on('click', function () {
                $('div.nav-see').removeClass('active');
                $('div.nav-search').addClass('active');
        
                $('#see-area').hide();
                $('#search-area').show();
            })
        
            $('#see-area').show();
            $('#search-area').hide();
        
            if ($('#admin').length === 1) {
                showProduct(true);
            } else {
                showProduct();
            }
        })
        
        function showProduct(isAdmin = false) {
            // 1. GET /api/products 요청
            // 2. #product-container(관심상품 목록), #search-result-box(검색결과 목록) 비우기
            // 3. for 문 마다 addProductItem 함수 실행시키고 HTML 만들어서 #product-container 에 붙이기
            $.ajax({
                type: 'GET',
                url: isAdmin ? '/api/admin/products' : '/api/products',
                success: function (response) {
                    $('#product-container').empty();
                    $('#search-result-box').empty();
                    for (let i = 0; i < response.length; i++) {
                        let product = response[i];
                        let tempHtml = addProductItem(product);
                        $('#product-container').append(tempHtml);
                    }
                }
            })
        }
        
        function addProductItem(product) {
            return `<div class="product-card"token interpolation">${product.link}'">
                        <div class="card-header">
                            <img src="${product.image}"
                                 alt="">
                        </div>
                        <div class="card-body">
                            <div class="title">
                                ${product.title}
                            </div>
                            <div class="lprice">
                                <span>${numberWithCommas(product.lprice)}</span>원
                            </div>
                            <div class="isgood ${product.lprice > product.myprice ? 'none' : ''}">
                                최저가
                            </div>
                        </div>
                    </div>`;
        }
        
        function numberWithCommas(x) {
            return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        }
        
        function execSearch() {
            let query = $('#query').val();
            if (query == '') {
                alert('검색어를 입력해주세요');
                $('#query').focus();
                return;
            }
            $.ajax({
                type: 'GET',
                url: `/api/search?query=${query}`,
                success: function (response) {
                    $('#search-result-box').empty();
                    for (let i = 0; i < response.length; i++) {
                        let itemDto = response[i];
                        let tempHtml = addHTML(itemDto);
                        $('#search-result-box').append(tempHtml);
                    }
                }
            })
        }
        
        function addHTML(itemDto) {
            return `<div class="search-itemDto">
                        <div class="search-itemDto-left">
                            <img src="${itemDto.image}" alt="">
                        </div>
                        <div class="search-itemDto-center">
                            <div>${itemDto.title}</div>
                            <div class="price">
                                ${numberWithCommas(itemDto.lprice)}
                                <span class="unit">원</span>
                            </div>
                        </div>
                        <div class="search-itemDto-right">
                            <img src="images/icon-save.png" alt="" onclick='addProduct(${JSON.stringify(itemDto)})'>
                        </div>
                    </div>`
        }
        
        function addProduct(itemDto) {
            $.ajax({
                type: "POST",
                url: '/api/products',
                contentType: "application/json",
                data: JSON.stringify(itemDto),
                success: function (response) {
                    $('#container').addClass('active');
                    targetId = response.id;
                }
            })
        }
        
        function setMyprice() {
            let myprice = $('#myprice').val();
            if (myprice == '') {
                alert('올바른 가격을 입력해주세요');
                return;
            }
            $.ajax({
                type: "PUT",
                url: `/api/products/${targetId}`,
                contentType: "application/json",
                data: JSON.stringify({myprice: myprice}),
                success: function (response) {
                    $('#container').removeClass('active');
                    alert('성공적으로 등록되었습니다.');
                    window.location.reload();
                }
            })
        }

0개의 댓글