23) 상품 조회 (관리자용) 설계
Client 에서 "일반 회원" 과 "관리자" 구분하여 API 호출 필요
프론트 개발자와 작업 방법 논의
서버에서 역할을 프론트에 내려줄 수 있는 방법
로그인 성공 시 Response 에 회원 Role 추가
쿠키 사용

index.html 에 admin 데이터 추가
관리자인 경우만 → "admin" id 를 가진
<div id="admin"></div>
프론트 개발자 작업
24) 상품 조회 (관리자용) 구현
"관리자"인 경우에만 model 값 ("admin_role": true) 추가
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";
}
}타임리프 문법에 맞춰, model 에 "admin_role" 값이 있으면
→ "admin" id 값을 가진
<div th:if="${admin_role}" id="admin"></div>
<!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>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();
}
}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();
}
}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();
}
})
}