StudyServer
web, server, java, spring 등.. 공부한 것을 기록하는 장소 입니다. 공부하면서 정리하였기 때문에 틀린 내용이 있을 수 있습니다. 이야기 해주시면 수정하겠습니다.

관리자 페이지

2019-04-30 00:00:00 +0000

관리자 추가

  • 상품 페이지를 만들면서 오로지 관리자만이 상품을 등록할 수 있게 만들어야 한다
  • 하나의 필드를 추가하여 0일 땐 일반유저, 1일 땐 관리자로 설정할 수 있지만 고객과 관리자의 데이터는 많이 다르므로 분리해야한다(예를들어 고객은 구매횟수나 구매금액이 필요하지만 관리자는 필요 없다)
  • 유저, 관리자의 기능을 정리하여 필요한 데이터를 뽑아낸다
  고객 쇼핑몰 관리자
정의 상품을 구매하는 고객을 의미한다 쇼핑몰 운영자를 의미한다(개발자 x) QA와 테스터와도 구분된다
기능1 상품 조회 상품 조회
기능2 상품 구매 및 장바구니 상품 관리
기능3 상품 결제 고객 관리
기능4 - 주문 조회 및 관리
기능5 - 게시판 관리
  • 표를 바탕으로 유저와 관리자의 데이터를 정의한다
  • 너무 기능이 많으므로 장바구니, 결제 기능은 제외, 관리자도 주문, 게시판 기능은 제외하고 정의
  • 유저는 총 구매 금액 , 한달 구매 금액 , 구매 등급 , 가입일 , 마지막 구매일
  • 관리는 상품 보기/관리 권한, 고객 보기/관리 권한
  • 정리된 데이터를 바탕으로 테이블을 정의한다

  • 고객 테이블(Customer)
필드명 데이터형 설명
user_seq bigint 유저 id
total_price bigint 총 구매 금액
total_month_price bigint 이번달 구매 금액
price_grade tinyint 구매 등급
join_date timestamp 가입일
last_buy_date timestamp 마지막 구매일
  • 관리자 테이블(Admin)
필드명 데이터형 설명
user_seq bigint 유저 id
product_view_author tinyint(1) 상품 조회 권한
product_update_author tinyint(1) 상품 관리 권한
customer_view_author tinyint(1) 고객 조회 권한
customer_update_author tinyint(1) 고객 관리 권한
  • 데이터를 바탕으로 테이블 생성 쿼리를 작성한다
  • 고객 테이블
CREATE TABLE `customer` (
  `user_seq` bigint NOT NULL,
  `total_price` bigint DEFAULT 0,
  `total_month_price` bigint DEFAULT 0,
  `price_grade` tinyint DEFAULT 0,
  `join_date` timestamp NULL DEFAULT NOW(),
  `last_buy_date` timestamp,
  PRIMARY KEY (`user_seq`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • 관리자 테이블
CREATE TABLE `admin`(
  `user_seq` bigint NOT NULL,
  `product_view_author` tinyint(1) DEFAULT 0,
  `product_update_author` tinyint(1) DEFAULT 0,
  `customer_view_author` tinyint(1) DEFAULT 0,
  `customer_update_author` tinyint(1) DEFAULT 0,
  PRIMARY KEY (`user_seq`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • 일반 고객과 관리자를 구분짓는 플래그를 user에 추가한다
ALTER TABLE USER ADD ADMIN TINYINT(1) DEFAULT 0;
  • 관리자로 만들 유저는 ADMIN 필드 값을 1로 준다
  • ADMIN에 해당 유저 정보를 추가한다
  • 유저의 정보를 가져오는 쿼리에 admin 필드를 가져오도록 한다
    INSERT INTO admin(user_seq, product_view_author, product_update_author, customer_view_author, customer_update_author)
    VALUES(12, 1, 1, 1, 1);
    

페이지 정의

  • 관리자의 기능을 바탕으로 페이지를 정의 한다
  • 페이지는 2개. 상품 보기,관리 페이지, 회원 보기, 관리 페이지
  • 상품은 조회, 추가, 삭제, 수정 기능이 기본적으로 들어가야 한다(페이징, 검색은 나중에)
  • 회원은 조회, 추가, 수정 기능이 기본적으로 들어간다(페이징, 검색은 나중에)
  • 아래와 같은 url을 정의 한다(이전에 정의했지만 재정의 해야한다. 관리자 페이지를 따로 만들므로)
url 설명 Method
/admin/product/list 상품 리스트 보기 GET
/admin/product/{seq} 상품이 있는지 체크 POST
/admin/product/{seq} 상품 상세 보기 GET
/admin/product/post 작성할 수 있는 권한이 있는지 체크 POST
/admin/product/post 새로운 상품 등록 페이지 이동 GET
/admin/product/post 새로운 상품 등록 POST
/admin/product/post/{seq} 작성할 수 있는 권한이 있는지 체크 GET
/admin/product/post/{seq} 기존에 있는 상품 페이지 이동 POST
/admin/product/post/{seq} 기존에 있는 상품 수정 PUT
/admin/product/delete 상품 삭제 GET
url 설명 Method
/admin/user/list 고객 조회 GET
/admin/user/{seq} 고객이 있는지 체크 POST
/admin/user/{seq} 고객 상세 보기 GET
/admin/user/post 고객 추가 체크 POST
/admin/user/post 고객 추가 이동 GET
/admin/user/post 고객 추가 PUT
/admin/user/post/{seq} 고객 정보 수정 체크 POST
/admin/user/post/{seq} 고객 정보 수정 페이지 이동 GET
/admin/user/post/{seq} 고객 정보 수정 PUT
/admin/user/delete 고객 정보 삭제 GET

고객, 관리자 VO, Service, DAO, xml 생성

  • 고객. 관리자 페이지의 고객 정보와 관련 있다.
  • Service, DAO, xml 파일도 생성한다
import java.util.Date;
import lombok.Data;
@Data
public class CustomerVO{
     private Long userSeq;
     private Long totalPrice;
     private Long totalMonthPrice;
     private    Byte priceGrade;
     private Date joinDate;
     private Date lastBuyDate;
}
  • 관리자. 관리자의 권한과 관련 있다
import lombok.Data;
@Data
public class AdminVO{
     private Long userSeq;
     private boolean productViewAuthor;
     private boolean productUpdateAuthor;
     private boolean customerViewAuthor;
     private boolean customerUpdateAuthor;
}

관리자 화면 추가

  • 일반 고객과 관리자가 보는 화면이 다르다.
  • userVO객체의 admin 값이 true일 때 관리자 메뉴가 보이도록 수정한다
<c:choose>
    <c:when test="${true eq userVO.admin}">
        <div class="col-md-3 col-xs-3">
            <a href="#">상품 관리</a>
        </div>
        <div class="col-md-3 col-xs-3">
            <a href="#">고객 관리</a>
        </div>
        <div class="dropdown col-md-3 col-xs-3">주문 관리</div>
        <div class="col-md-3 col-xs-3">게시판 관리</div>
    </c:when>
    <c:otherwise>
        <div class="col-md-3 col-xs-3">
            <a href="/main">메인화면</a>
        </div>
        <div class="col-md-3 col-xs-3">공지사항</div>
        <div class="dropdown col-md-3 col-xs-3">
            <div class="dropbtn">전체상품</div>
            <div class="dropdown-content">
                <a href="/product/list">디저트</a> <a href="/product/list">이유식</a>
            </div>
        </div>
        <div class="col-md-3 col-xs-3">
            <a href="/board/list?page=1&count=10">자유게시판</a>
        </div>
    </c:otherwise>
</c:choose>

상품 관리

  • 상품 리스트 가져오기
  • 상품 재고 필드 추가 및 VO, 쿼리 수정, 제품 pic 필드 -> img로 변경
alter table product add stock Long Default 0;
alter table product change pic img varchar(50)
  • 메뉴에서 관리자 상품관리 페이지로 이동되도록 수정(관리자 상품관리, 상세페이지, 추가, 수정 페이지를 만든다)

MappingJackson2JsonView

2019-04-30 00:00:00 +0000

MappingJackson2JsonView

View Resolver

  • 클라이언트의 요청을 받은 결과값으로 View이름을 전달한다. 이 View이름으로 ViewObject를 찾아서 전달할 View 내용을 생성한다
  • View 이름을 전달하면서 데이터도 같이 전달하게 된다. 이 때 ajax를 사용 중이라면 값을 전달할 때 마다 일일이 json으로 바꿔야 한다
  • MappingJaxkson2JsonView를 사용하면 간단하게 전달할 수 있다.

사용법

  • pom.xml에서 jackson 라이브러리 사용
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.9.13</version>
</dependency>
  • ServletContext에 MappingJackson2JsonView 객체를 jsonView라는 이름으로 bean 등록
  • MappingJackson2JsonView를 사용하면 ModelAndView가 json형식으로 변한다 => 일일이 json 변환하지 않아도 됨
@Bean(name="jsonView")
public View mappingJacksonJsonView(){
    return new MappingJackson2JsonView();
}
  • 사용 방법
@RequestMapping(value = "/post/{seq}", method = RequestMethod.DELETE)
public String delete(@PathVariable("seq") Long seq, Model model, HttpSession session) throws Exception {

    BoardVO boardVO = service.getBoardVO(seq);
    UserVO userVO = (UserVO) session.getAttribute("userVO");
    checkUserNull(userVO);

    ResultMap resultMap = service.deleteVO(seq, userVO, boardVO);
    model.addAllAttributes(resultMap);

    return"jsonView"
}

Spring Javaconfig

2019-04-29 00:00:00 +0000

Java Configuration

  • xml 대신 java로 root, servlet 설정을 함(xml 대신 java로 설정이 느는 추세)
  • 아래 예제는 ‘코드로 배우는 스프링 웹 프로젝트’ 책 예제를 정리한 내용

Java Configuration 방법

  • 프로젝트를 생성한다. 임의로 ex.config.test로 지정하였다
  • pom.xml 설정 ~~~
4.0.0 org.zerock controller jex00 war 1.0.0-BUILD-SNAPSHOT 1.6 5.1.5.RELEASE 1.6.10 1.6.6 org.springframework spring-context ${org.springframework-version} commons-logging commons-logging org.springframework spring-webmvc ${org.springframework-version} org.springframework spring-test ${org.springframework-version} org.aspectj aspectjrt ${org.aspectj-version} org.slf4j slf4j-api ${org.slf4j-version} org.slf4j jcl-over-slf4j ${org.slf4j-version} runtime org.slf4j slf4j-log4j12 ${org.slf4j-version} runtime log4j log4j 1.2.17 org.projectlombok lombok 1.18.6 provided javax.inject javax.inject 1 javax.servlet servlet-api 2.5 provided javax.servlet.jsp jsp-api 2.1 provided javax.servlet jstl 1.2 junit junit 4.12 test maven-eclipse-plugin 2.9 org.springframework.ide.eclipse.core.springnature org.springframework.ide.eclipse.core.springbuilder true true org.apache.maven.plugins maven-compiler-plugin 2.5.1 1.8</source> 1.8 -Xlint:all true true org.codehaus.mojo exec-maven-plugin 1.2.1 org.test.int1.Main org.apache.maven.plugins maven-war-plugin 3.2.2 false
- webapp/resources/spring 아래에 있는 xml 설정파일을 삭제한다
- ex.config.config 패키지를 생성한다. 그리고 RootConfig.java 를 만들고 아래와 같이 코드를 작성
- xml 설정 파일 대신  ex.config.sample 에 있는 클래스를 bean으로 등록

package ex.config.config;

import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration;

@Configuration @ComponentScan(basePackages= {“ex.config.sample”}) public class RootConfig {

}

- Webconfig.xml

package ex.config.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer{ @Override protected Class[] getRootConfigClasses() { return new Class[]{RootConfig.class}; } @Override protected Class[] getServletConfigClasses() { // TODO Auto-generated method stub return null; } @Override protected String[] getServletMappings() { // TODO Auto-generated method stub return null; } }


- ex.config.sample 패키지를 만들고 bean 등록할 class를 생성한다
- Chef.java

package ex.config.sample;

import org.springframework.stereotype.Component;

import lombok.Data;

@Component @Data public class Chef {

}

- Restaurant.java

package ex.config.sample;

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;

import lombok.Data; import lombok.Setter;

@Component @Data public class Restaurant {

@Setter(onMethod_ = @Autowired)
private Chef chef;

}

- test 폴더에 ex.config.test 패키지 생성. 그리고 junit으로 Test할 JUnitCase 생성

package ex.config.test; import static org.junit.Assert.assertNotNull; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import ex.config.config.RootConfig; import ex.config.sample.Restaurant; import lombok.Setter; import lombok.extern.log4j.Log4j; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {RootConfig.class}) @Log4j public class SampleTests {

   @Setter(onMethod_ = @Autowired)
   private Restaurant restaurant;
   
   
   @Test
   public void testExist() {
          
          assertNotNull(restaurant);
          log.info(restaurant);
          
          log.info("-----------------------------------------");
          
          log.info(restaurant.getChef());
          
   } } ~~~

게시판 만들기-제품 등록

2019-04-29 00:00:00 +0000

상품 등록2

  • 임의의 데이터를 출력하는 것 까지 완료
  • 이제 상품 등록을 페이지에서 하는 기능을 추가 필요
  • 이미지는 upload기능이 들어가므로 나중에 구현한다
  • 유저, 관리자가 나뉘어져 있지 않도록 권한 체크와 관리자 페이지는 등록 다음으로 구현한다

Controller, Service, Query 처리

  • 상품 생성 날짜가 필요하여서 생성 날짜 필드를 추가하였다
Alter table product  Add create_date Timestamp  DEFAULT NOW() NOT NULL;
  • 상품을 등록하는 쿼리문을 만든다
<insert id="createProductVO" parameterType="ProductVO">
           INSERT INTO product
           (                    
                   category
                  , pic
                  , title
                  , price
                  , text
                  , user_seq
           )
           VALUES
           (
                #{category}
                ,#{pic}
                ,#{title}
                ,#{price}
                ,#{text}
                ,#{userSeq}
                ,#{userSeq}
           )
     </insert>
  • Service에서는 상품의 값이 올바르게 들어갔는지 체크하고 쿼리문을 호출한다
@Override
public ResultMap createProduct(ProductVO productVO) throws Exception{
    int category = productVO.getCategory();
    String picSrc = productVO.getPic();
    String title = productVO.getTitle();
    Long price = productVO.getPrice();
    String text = productVO.getText();
    
    if(title == null || text == null || picSrc == null) {
        throw new LogicException("990", "알 수 없는 에러가 발생했습니다");
    }
    
    if(title.isEmpty()) {
        throw new LogicException("1000", "상품명을 입력해주세요");    
    }
    
    if(TITLE_SIZE_MAX < title.length()) {
        throw new LogicException("1001", "상품명이 너무 깁니다");
    }
    
    // 이미지 체크
    
    if(0 >= price) {
        throw new LogicException("1004", "가격은 1원 이상이여야 합니다");
    }
    
    if(PRICE_SIZE_MAX <= price) {
        throw new LogicException("1006", "가격은 1,000,000,000원 이하만 입력 가능합니다");
    }
    
    if(text.isEmpty()) {
        throw new LogicException("1007", "설명을 입력해주세요");                
    }
    
    if(TEXT_SIZE_MAX <= text.length()) {
        throw new LogicException("1008", "설명은 100자 이하여야 합니다");
    }
    
    int result = dao.createProduct(productVO);
    if(0 == result) {
        throw new LogicException("990", "알 수 없는 에러가 발생했습니다");
    }
    
    ResultMap resultMap = new ResultMap();
    resultMap.setStatus("200");
    resultMap.setMsg("");
    
    return resultMap;
}
  • Contorller에서는 /post url 을 POST, GET 방식 2가지의 방식으로 받는다
  • POST 방식은 상품 등록이 가능한 권한이 있는지 체크한다
  • GET 방식은 상품 등록 페이지로 이동한다
  • 상품 등록 요청 url은 method를 PUT으로 받는다
  • json 데이터와 ProductVO가 매핑이 되지 않는 상황 발생 => @RequestBody를 추가하니 매핑되었음
@RequestMapping(value = "/post", method = RequestMethod.POST)
public String checkPost(HttpSession session, Model model) throws Exception {
    ResultMap resultMap = service.getCheckPost();
    model.addAllAttributes(resultMap);
    
    return JSON_VIEW;    
}

@RequestMapping(value = "/post", method = RequestMethod.PUT)
public String postProduct(@RequestBody ProductVO productVO, HttpSession session, Model model) throws Exception {
    ResultMap resultMap = service.createProduct(productVO);
    model.addAllAttributes(resultMap);
    
    return JSON_VIEW;
}

View

  • ‘등록’ 버튼을 클릭하면 해당 데이터를 PUT method로 전달하고 성공할 시 list로 페이지 이동되도록 만듬
// 페이지 이동
$('#product-new-product-create').click(function(){
     console.log('product-new-product-create');
     ajaxRequest("/product/post", "", "POST",  "location.href='/product/post'");
});

// 상품 등록
$("#product-new-add-product").click(function(){
     var category = $('#product-add-select-input').val();
     var title = $('#product-title-input-text').val();
     var price = $('#product-price-input-text').val();
     var text = $('#product-text-input-text').val();
     
     if(category == undefined || title == undefined || price ==  undefined || text == undefined){
           return;
     }
     price = price.replace(",", "");
     
     var data ={
                "category":category,
                "title":title,
                "price":price,
                "text":text
     };
     
     ajaxRequest("/product/post", JSON.stringify(data), "PUT",  "location.href='/product/list'");    
});

게시판 만들기-제품 페이지 제작

2019-04-29 00:00:00 +0000

제품 페이지 제작

  • 마켓컬리, 쿠팡, 웹쇼핑몰 처럼 상품의 목록이 나온는 상품 페이지 제작
  • 제품에 대한 정보를 뽑아내고 DB 생성및 쿼리, VO 등을 작성한다
  • 카테고리 디저트는 1005, 이유식은 1006
필드명 데이터형 설명
product_seq bigint(20) 상품 고유 시퀀스
category bigint(20) 상품 카테고리
pic varchar(30) 상품 사진
title varchar(20) 상품 이름
price int 상품 가격
text varchar(100) 상품 설명
count bigint(20) 조회수
product_del tinyint(1) 삭제 여부
user_seq bigint(20) 작성자 시퀀스
  • 상품 DB 생성 쿼리문
CREATE TABLE `product` (
  `PRODUCT_SEQ` bigint(20) NOT NULL AUTO_INCREMENT,
  `CATEGORY` int NOT NULL,
  `PIC` text DEFAULT 'img/1.jpg',
  `TITLE` varchar(20) NOT NULL,
  `PRICE` bigint NOT NULL DEFAULT 0,
  `TEXT` varchar(100) DEFAULT '',
  `COUNT` bigint(20) DEFAULT 0,
  `PRODUCT_DEL` bigint(20) DEFAULT 0,
  `USER_SEQ` bigint(20),
  PRIMARY KEY (`PRODUCT_SEQ`)
  
) ENGINE=InnoDB AUTO_INCREMENT=97 DEFAULT CHARSET=utf8
  • 임시 데이터 넣기
INSERT INTO PRODUCT(CATEGORY, TITLE, PRICE, TEXT)
VALUES(1005, '달콤유자케익', 20000, '유자를 흠뻑 넣은 케익');
INSERT INTO PRODUCT(CATEGORY, TITLE, PRICE, TEXT)
VALUES(1005, '자몽이슬톡톡', 3000, '이슬 톡톡');
INSERT INTO PRODUCT(CATEGORY, TITLE, PRICE, TEXT)
VALUES(1005, '머랭쿠키', 2000, '머랭~머랭~');

URL 결정

  • 마켓컬리에서 소분류를 선택 시 카테고리가 달라진다
  • 즉 하나의 테이블에서 대분류, 소분류에 따라 데이터를 다르게 가져온다 https://www.kurly.com/shop/goods/goods_list.php?category=908003
  • 베스트 상품은 기존 카테고리가 있고 그 중에서 베스트 상품 선별
  • 상품에 관한 기능은 상품 조회, 상품 등록, 상품 수정, 상품 삭제가 있다(상세페이지 X)
url 설명 Method
/product/list 상품 리스트 보기 GET
/product/post 작성할 수 있는 권한이 있는지 체크 GET
/product/post 새로운 상품 등록 [POST
/product/post/{seq} 작성할 수 있는 권한이 있는지 체크 GET
/product/post/{seq} 기존에 있는 상품 수정 [POST
/product/delete 상품 삭제 GET

Controller, Service , VO

  • product 테이블의 데이터를 불러오는 쿼리
<select id="getProductVOList" resultType="ProductVO">
           SELECT product_seq as productSeq
                   , category
                   , pic
                   , title
                   , price
                   , text
                   , count
                   , product_del as productDel
                   , user_seq as userSeq
           FROM product         
     </select>
  • Service
public ResultMap getProductVOList(){
    List<ProductVO> list = dao.getProductVOList();
    ResultMap resultMap = new ResultMap();
    resultMap.setStatus("200");
    resultMap.put("list", list);
    return     resultMap;
}
  • Conroller
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(Model model) throws Exception {
    ResultMap resultMap = service.getProductVOList();
    model.addAllAttributes(resultMap);
    
    return "product/list";    
}

View

  • list.jsp 를 생성하고 상품 리스트를 뿌려주는 화면 구성
<h2>상세 메뉴</h2>
<div><button id="product-new-product-create" class="btn  btn-info">새 상품 추가</button></div>
<div id="product-list" >
<c:forEach var = "product" items = "${list}">
<div class="row col-md-4 col-xs-12 product-elements">
     <input type="hidden" value="${product.productSeq}"/>
     <input type="hidden" value="${product.category}"/>
     <div><img src="/resources/${product.pic}" /></div>
     <div class="product-in-text">${product.title}</div>
     <div class="product-in-text">${product.price}원</div>
     <div class="product-in-text">${product.text}</div>
</div>
</c:forEach>
</div><!-- product-list -->
  • 상품 리스트 페이지로 이동하는 url 추가
<div class="dropbtn">전체상품</div>
    <div class="dropdown-content">
        <a href="/product/list">디저트</a>
        <a href="/product/list">이유식</a>
    </div>
</div>

Posts

subscribe via RSS