스프링으로 쇼핑몰을 만들어보자20 - 작가목록 기능 구현 1
기능구현
- 회원가입(주소API연동, 이메일 인증, ajax를 사용하여 아이디 중복검사)
- 관리자 페이지(인터셉터 적용, 상품관리, 회원관리)
- 업로드(이미지 등록, 수정, 삭제)
- 검색
- 페이징
- 구매(장바구니, 포인트사용)
- 댓글(등록,수정,삭제)
- 중간에 막혔거나 에러 해결 못하겠으면 ybnr92@gmail.com 으로 문의
목표
- 작가 목록 출력 쿼리 구현
순서
- 수정사항
- Criteria 작성
- AuthorMapper.java
- AuthorMapper.xml
- AuthorMapper 메서드 테스트
- 정해진 갯수와 페이지의 작가 정보를 출력을 구현한 뒤 사용자가 마우스를 통해 페이지 이동을 할 수 있도록 페이지네이션을 구현할 예정이다.
- 이번편은 지정한 수만큼 작가 데이터를 출력시키는데 필요로 한 쿼리 작성과 해당 쿼리를 호출하는 Mapper 작성을 목표로 한다.
수정사항
- /resource/admin 폴더에 있는 css 모든 파일에
.admin_content_wrap
식별자가 가지고 있는 height 속성을 지우고 min-height 속성을 작성한다. height 속성의 경우 고정된 높이지만, min-height 속성의 경우는 속성 값으로 최소 높이를 설정하여 속성 값보다 높이가 작아지는 것을 방지하면서 그 이상의 높이는 유동적으로 변경될 수 있도록 해준다. - 적용 대상 파일
- authorEnroll.css
- authorManage.css
- goodsEnroll.css
- goodsManage.css
- main.css
/* 기존 코드 */
.admin_content_wrap{
width: 80%;
float:left;
height: 100%;
height:700px;
}
/* 수정 코드 */
.admin_content_wrap{
width: 80%;
float:left;
min-height:700px;
}
admin 폴더 jsp 파일 include 태그 적용
- admin 폴더에서 중복이자 공통적으로 적용되는 코드를 include태그를 적용을 한다. 각 jsp파일마다 변경될 코드에 집중할 수 있도록 하기 위함이다. 더불어 공통되는 코드들을 하나의 파일로 관리하는 것이기 때문에 유지보수에도 용이하다.
../WEB-INF/views
경로에includes/admin
폴더를 생성 후 header.jsp, footer.jsp 파일을 생성한다. 각 파일에 아래의 코드만 남기고 모든 태그 코드를 삭제한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
- admin 폴더에 있는 jsp 파일 중 한 파일을 선택해서 body태그 다음의 코드부터 class 속성값이
admin_content_wrap
인 div 태그 이전 코드까지 복사하여head.jsp
파일에 붙여 넣는다.
<div class="wrapper">
<div class="wrap">
<!-- gnv_area -->
<div class="top_gnb_area">
<ul class="list">
<li><a href="/main">메인 페이지</a></li>
<li><a href="/member/logout.do">로그아웃</a></li>
<li>고객센터</li>
</ul>
</div>
<!-- top_subject_area -->
<div class="admin_top_wrap">
<span>관리자 페이지</span>
</div>
<!-- contents-area -->
<div class="admin_wrap">
<!-- 네비영역 -->
<div class="admin_navi_wrap">
<ul>
<li >
<a class="admin_list_01" href="/admin/goodsEnroll">상품 등록</a>
</li>
<li>
<a class="admin_list_02" href="/admin/goodsManage">상품 관리</a>
</li>
<lI>
<a class="admin_list_03" href="/admin/authorEnroll">작가 등록</a>
</lI>
<lI>
<a class="admin_list_04" href="/admin/authorManage">작가 관리</a>
</lI>
<lI>
<a class="admin_list_05">회원 관리</a>
</lI>
</ul>
</div>
- class 속성 값이
admin_content_wrap
인 div 태그 다음 코드부터 /body 혹은 script 태그 이전 코드들을 복사 하여footer.jsp
파일에 붙여 넣는다.
<div class="clearfix"></div>
</div>
<!-- Footer 영역 -->
<div class="footer_nav">
<div class="footer_nav_container">
<ul>
<li>회사소개</li>
<span class="line">|</span>
<li>이용약관</li>
<span class="line">|</span>
<li>고객센터</li>
<span class="line">|</span>
<li>광고문의</li>
<span class="line">|</span>
<li>채용정보</li>
<span class="line">|</span>
</ul>
</div>
</div> <!-- class="footer_nav" -->
<div class="footer">
<div class="footer_container">
<div class="footer_left">
<img src="../resources/img/civically-1.jpg">
</div>
<div class="footer_right">
(주) 이름없는 회사 대표이사 : OOO
<br>
사업자등록번호 : ooo-oo-ooooo
<br>
대표전화 : oooo-oooo(발신자 부담전화)
<br>
<br>
COPYRIGHT(C) <strong>yoonbitnara.github.io</strong> ALL RIGHTS RESERVED.
</div>
<div class="clearfix"></div>
</div>
</div> <!-- class="footer" -->
</div> <!-- class="wrap" -->
</div> <!-- class="wrapper" -->
- 아래의 코드를 view/admin 폴더에 있는 모든 파일에 위에서 복사한 코드들과 동일한 코드들을 아래의 태그로 변경해준다.
<%@include file="../includes/admin/header.jsp" %>
<%@include file="../includes/admin/footer.jsp" %>
- 대략 아래와 같이 header, footer 부분을 include한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="../resources/css/admin/authorEnroll.css">
<script
src="https://code.jquery.com/jquery-3.4.1.js"
integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
crossorigin="anonymous"></script>
</head>
</head>
<body>
<%@include file="../includes/admin/header.jsp" %>
<div class="admin_content_wrap">
<div class="admin_content_subject"><span>작가 등록</span></div>
<!-- 작가 정보 작성 및 전송 코드 추가 -->
<div class="admin_content_main">
<form action="/admin/authorEnroll.do" method="post" id="enrollForm">
<div class="form_section">
<div class="form_section_title">
<label>작가 이름</label>
</div>
<div class="form_section_content">
<input name="authorName">
<span id="warn_authorName">작가 이름을 입력 해주세요.</span>
</div>
</div>
<div class="form_section">
<div class="form_section_title">
<label>소속 국가</label>
</div>
<div class="form_section_content">
<select name="nationId">
<option value="none" selected>=== 선택 ===</option>
<option value="01">국내</option>
<option value="02">국외</option>
</select>
<span id="warn_nationId">소속 국가를 선택해주세요.</span>
</div>
</div>
<div class="form_section">
<div class="form_section_title">
<label>작가소개</label>
</div>
<div class="form_section_content">
<input name="authorIntro" type="text">
<span id="warn_authorIntro">작가 소개를 입력 해주세요.</span>
</div>
</div>
</form>
<div class="btn_section">
<button id="cancelBtn" class="btn">취 소</button>
<button id="enrollBtn" class="btn enroll_btn">등 록</button>
</div>
</div>
<!-- 작가 정보 작성 및 전송 코드 추가 -->
</div>
<div class="clearfix"></div>
</div>
<%@include file="../includes/admin/footer.jsp" %>
- jsp 파일이 함축되어 가독성이 좋아졌다.
Criteria 클래스 작성
com.store.model
패키지에Criteria.java
클래스를 생성 후 아래의 코드를 작성한다.- 해당 클래스는 지정한 개수와 검색 조건에 따라서 작가 데이터를 출력하는 쿼리를 실행하는데 필요로 한 데이터들의 모임이다.
- 참고로 Criteria는 기준이라는 의미다.
- 아래의 코드를 추가한다.
public class Criteria {
/* 현재 페이지 번호 */
private int pageNum;
/* 페이지 표시 개수 */
private int amount;
/* 페이지 skip */
private int skip;
/* 검색 타입 */
private String type;
/* 검색 키워드 */
private String keyword;
/* Criteria 생성자 */
public Criteria(int pageNum, int amount) {
this.pageNum = pageNum;
this.amount = amount;
this.skip = (pageNum -1) * amount;
}
/* Criteria 기본 생성자 */
public Criteria() {
this(1,10);
}
/* 검색 타입 데이터 배열 변환 */
public String[] getTypeArr() {
return type == null ? new String[] {}:type.split("");
}
- getter,setter,toString 메서드를 추가한다.
package com.store.model;
public class Criteria {
/* 현재 페이지 번호 */
private int pageNum;
/* 페이지 표시 개수 */
private int amount;
/* 페이지 skip */
private int skip;
/* 검색 타입 */
private String type;
/* 검색 키워드 */
private String keyword;
/* Criteria 생성자 */
public Criteria(int pageNum, int amount) {
this.pageNum = pageNum;
this.amount = amount;
this.skip = (pageNum -1) * amount;
}
/* Criteria 기본 생성자 */
public Criteria() {
this(1,10);
}
/* 검색 타입 데이터 배열 변환 */
public String[] getTypeArr() {
return type == null ? new String[] {}:type.split("");
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public int getSkip() {
return skip;
}
public void setSkip(int skip) {
this.skip = skip;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
@Override
public String toString() {
return "Criteria [pageNum=" + pageNum + ", amount=" + amount + ", skip=" + skip + ", type=" + type
+ ", keyword=" + keyword + "]";
}
}
- amount와 pageNum 변수의 값이 변경될때 skip변수의 값도 변경되도록 setAmount(), setPageNum() 메서드를 일부 수정해준다.
- Criteria 클래스의 데이터를 사용할 때 매번 새로운 생성자를 호출하여 사용하기 때문에 해당 과정이 필요 없지만 혹시 모를 경우를 위해 작업했다.
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
this.skip = (pageNum - 1) * this.amount;
}
public void setAmount(int amount) {
this.amount = amount;
this.skip = (this.pageNum - 1) * this.amount;
}
AuthorMapper.java 인터페이스
- 작가 데이터 목록 쿼리를 실행하는 메서드를 선언한다.
- 여러 작가의 데이터를 반환받아야 하기 때문에 리턴 타입으로 List 자료구조를 지정하였다.
- List는 배열과 비슷한 자료구조이다. 배열과는 다르게 삽입되는 데이터에 따라 크기가 동적으로 변한다.
- List에 저장될 데이터가 AuthorVO(작가정보)임을 명시하기 위해 AuthorVO 제네릭을 선언하였다.
- List는 java.util.List를 import 해야 한다.
package com.store.mapper;
import java.util.List;
import com.store.model.AuthorVO;
import com.store.model.Criteria;
public interface AuthorMapper {
/* 작가 등록 */
public void authorEnroll(AuthorVO author);
/* 작가 목록 */
public List<AuthorVO> authorGetList(Criteria cri);
}
AuthorMapper.xml
- AuthorMapper.java 인터페이스에서 선언한 메서드가 실행할 쿼리 코드를 작성한다.
- select 태그의 resultType속성 값은
com.store.model.AuthorVO
를 부여한다. - resultType을 클래스로 지정하였지만 메서드의 리턴 타입을 List로 선언하였기 때문에 MyBatis가 지정된 클래스 인스턴스 요소를 가지는 List로 자동 변환해준다.
<?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="com.store.mapper.AuthorMapper">
<!-- 작가 등록 -->
<insert id="authorEnroll">
insert into author(authorName, nationId, authorIntro) values(#{authorName}, #{nationId}, #{authorIntro});
</insert>
<!-- 작가 목록 -->
<select id="authorGetList" resultType="com.store.model.AuthorVO">
select authorid, authorname, nationid, regdate, updatedate
from author
<if test="keyword != null">
where authorname like concat('%',#{keyword}, '%')
</if>
order by authorid desc
limit #{skip}, #{amount}
</select>
</mapper>
AuthorMapper 메서드 테스트
- 새로 작성한 authorGetList() 메서드가 정상적으로 동작하는지 확인하기 위해 테스트를 진행한다.
- 테스트하기 앞서 페이징 적용이 되는지 확인하기 위해 재귀 복사를 통해 여러 행을 추가해주었다.
insert into author(authorname, nationid)(select authorname, nationid from author);
AuthorMapperTests.java
클래스에 기존 테스트 코드는 주석 처리를 한 후 아래의 코드를 추가한다.- Junit테스트를 하여 정상적으로 동작하는지를 확인한다.
/* 작가 목록 테스트 */
@Test
public void authorGetListTest() throws Exception {
Criteria cri = new Criteria(3,10); // 3page & 10개 행 표시
List<AuthorVO> list = mapper.authorGetList(cri);
for(int i = 0; i < list.size(); i++) {
System.out.println("list" + i + ".............." + list.get(i));
}
}
- 검색어를 입력한 경우에도 쿼리가 정상적으로 동작하는지 확인하기 위해 keyword 변수에 데이터를 넣은 후 테스트를 해본다.
/* 작가 목록 테스트 */
@Test
public void authorGetListTest() throws Exception {
Criteria cri = new Criteria(3,10); // 3page & 10개 행 표시
cri.setKeyword("핏불");
List<AuthorVO> list = mapper.authorGetList(cri);
for(int i = 0; i < list.size(); i++) {
System.out.println("list" + i + ".............." + list.get(i));
}
}
- 잘된다.
Leave a comment