작가 등록 기능 구현을 위해서 '영속 계층(데이터 처리 계층)', '비즈니스 로직 계층', '도메인 모델 계층' 작업을 하고자 합니다.
도메인 모델 계층 => AuthorVO 작성
- 작가 관련 데이터 운반 역할
영속 계층 => AuthorMapper.xml, AuthorMapper.java
- 작가 등록 연산 수행
비즈니스 로직 => AuthorService.java, AuthorServiceImpl.java
- 작가 등록 로직 구현
제어 계층 => AuthorController.java 작성
- 뷰(View)로부터의 요청 처리하는 url맵핑 메서드 작성
프리젠테이션 계층 => authorEnroll.jsp 계층
- 작가 등록에 사용될 데이터를 입력 및 전송할 수 있도록 작성
test_author테이블에 regDate, updateDate 두개의 컬럼을 추가합니다. 행(row)을 추가한 날짜와 수정한 날짜를 자동으로 기록하기 위해서입니다. 수정을 위한 코드는 아래와 같습니다.
-- 작가 테이블 수정
alter table test_author add regDate timestamp default now();
alter table test_author add updateDate timestamp default now();
test_author테이블에 데이터를 전달하거나, 테이블로부터 반환받은 데이터를 담을 객체를 정의하는 클래스(AutorVO.java)를 com.test.model패키지에 생성하여 작성합니다.
test_author테이블에 있는 컬럼들을 기준으로 변수들을 작성하였고, 추후 test_nation에 있는 nationName컬럼의 데이터를 같이 호출할 경우 대비해 해당 변수도 같이 정의하였습니다.
private접근자를 붙인 변수를 작성한 후 해당 변수들을 수정하거나 읽을 수 있도록 getter/setter/toString 작업을 합니다.
@Data
public class AuthorVO {
/* 작가 아이디 */
private int authorId;
/* 작가 이름 */
private String authorName;
/* 국가 id */
private String nationId;
/* 작가 국가 */
private String nationName;
/* 작가 소개 */
private String authorIntro;
/*등록 날짜*/
private Date regDate;
/* 수정 날짜 */
private Date updateDate;
}
작가 정보 관련 쿼리 메서드만 분리하여 관리하기 위해서 com.test.mapper패키지에 AuthorMapper.java 인터페이스를 생성하였습니다. 해당 인터페이스에 작가 등록 쿼리를 실행하는 메서드를 작성합니다.
/* 작가 등록 */
public void authorEnroll(AuthorVO author);
src/main/resources -> com/test/mapper 경로에 AuthorMapper.java 인터페이스와 동일한 이름의 AuthorMapper.xml파일을 생성합니다.
<?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.test.mapper.AuthorMapper">
<!-- 작가 등록 -->
<insert id="authorEnroll">
insert into test_author(authorName, nationId, authorIntro) values(#{authorName}, #{nationId}, #{authorIntro})
</insert>
</mapper>
com.test.service패키지에 AuthorService.java 인터페이스를 생성하고 작가 등록 메서드를 작성합니다.
/* 작가 등록 */
public void authorEnroll(AuthorVO author) throws Exception;
com.test.service패키지에 AuthorService.java 인터페이스를 구현하는 클래스인 AuthorServiceImpl.java클래스를 생성 후 상속 키워드를 추가합니다. 그리고 @Service어노테이션을 추가합니다.
AuthorMapper.java인터페이스를 주입해주는 코드를 작성하고 Service에서 정의한 작가등록 메서드를 오버라이딩하여 AuthorMapper의 작가 등록 메서드를 호출하는 코드를 작성합니다.
@Service
public class AuthorServiceImpl implements AuthorService {
@Autowired
AuthorMapper authorMapper;
@Override
public void authorEnroll(AuthorVO author) throws Exception {
authorMapper.authorEnroll(author);
}
}
먼저 AuthorService.java 인터페이스를 의존성 주입해주는 코드를 추가합니다.
@Autowired
private AuthorService authorService;
url이 "authorEnroll.do"인 POST방식의 URL매핑 메서드를 추가합니다. 파라미터로 BoardVO타입 변수 RedirectAttributes타입 변수를 추가하였습니다.
/* 작가 등록 */
@RequestMapping(value="authorEnroll.do", method=RequestMethod.POST)
public String authorEnrollPOST(AuthorVO author, RedirectAttributes rttr) throws Exception {
logger.info("authorEnroll :" + author);
authorService.authorEnroll(author); // 작가 등록 쿼리 수행
rttr.addFlashAttribute("enroll_result", author.getAuthorName());
return "redirect:/admin/authorManage";
}
class속성 값 'admin_content_subject'인 <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">
</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>
</div>
</div>
<div class="form_section">
<div class="form_section_title">
<label>작가소개</label>
</div>
<div class="form_section_content">
<input name="authorIntro" type="text">
</div>
</div>
</form>
<div class="btn_section">
<button id="cancelBtn" class="btn">취 소</button>
<button id="enrollBtn" class="btn enroll_btn">등 록</button>
</div>
</div>
'작가 등록'버튼과 '취소'버튼 두개가 있습니다. 두 개의 버튼이 작동되도록 스크립트 코드를 작성하였습니다.
/* 등록 버튼 */
$("#enrollBtn").click(function(){
$("#enrollForm").submit();
});
/* 취소 버튼 */
$("#cancelBtn").click(function(){
location.href="/admin/authorManage"
});
작가 등록을 수행 후 '작가 관리'페이지에 이동함과 동시에 등록이 성공하였음을 알리는 데이터("enroll_result")를 전송하였습니다. 이 데이터를 활용하여 성공을 알리는 경고창을 띄우는 코드를 작성합니다.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<script>
/* 작가 등록 알림창 */
$(document).ready(function(){
let result = '<c:out value="${enroll_result}"/>';
checkResult(result);
function checkResult(result){
if(result === ''){
return;
}
alert("작가'${enroll_result}' 을 등록하였습니다.");
}
});
</script>
resources/css/admin 경로에 있는 main.css파일을 제외한 모든 css파일(authorEnroll.css, authorManage.css, goodsEnroll.css, goodsManage.css)에서 적용된 .admin_content_wrap 식별자의 height속성을 지우고 min-height속성을 작성합니다. height속성의 경우 고정된 높이지만, min-height속성의 경우는 속성 값으로 최소 높이를 설정하여 속성 값보다 높이가 작아지는 것을 방지하면서 그 이상의 높이는 유동적으로 변경될 수 있도록 해줍니다.
/* 변경 전 */
/* 관리자페이지 컨텐츠 영역 */
.admin_content_wrap{
width: 80%;
float:left;
height: 100%;
height: 700px;
}
/* 변경 후 */
/* 관리자페이지 컨텐츠 영역 */
.admin_content_wrap{
width: 80%;
float:left;
min-height: 700px;
}
새로 작성한 태그들의 css설정을 추가하였습니다.
/* 관리자 컨텐츠 메인 영역 */
.form_section{
width: 95%;
margin-left: 2%;
margin-top: 20px;
border: 1px solid #dbdde2;
background-color: #efefef;
}
.form_section_title{
padding: 20px 35px;
}
.form_section_title label{
display: block;
font-size: 20px;
font-weight: 800;
}
.form_section_content{
padding: 20px 35px;
border-top: 1px solid #dbdde2;
}
.form_section_content input{
width: 98%;
height: 25px;
font-size: 20px;
padding: 5px 1%;
}
.form_section_content select{
width: 98%;
height: 35px;
font-size: 20px;
text-align-last: center;
}
/* 입력란 공란 경고 태그 */
.form_section_content span{
display: none;
padding-top: 10px;
text-align: center;
color: #e05757;
font-weight: 300;
}
/* 버튼 영역 */
.btn_section{
text-align: center;
margin: 80px 0;
}
.btn{
min-width: 180px;
padding: 4px 30px;
font-size: 25px;
font-weight: 600;
line-height: 40px;
}
.enroll_btn{
background-color: #dbdde2;
margin-left:15px;
}
authorEnroll.css 전체
@charset "UTF-8";
*{
margin: 0;
padding:0;
}
a{
text-decoration: none;
}
ul{
list-style: none;
}
/* 화면 전체 렙 */
.wrapper{
width: 100%;
}
/* content 랩 */
.wrap{
width : 1080px;
margin: auto;
}
/* 홈페이지 기능 네비 */
.top_gnb_area{
width: 100%;
height: 50px;
background-color: #f0f0f1;
position:relative;
}
.top_gnb_area .list{
position: absolute;
top: 0px;
right: 0;
}
.top_gnb_area .list li{
list-style: none;
float : left;
padding: 13px 15px 0 10px;
font-weight: 900;
cursor: pointer;
}
/* 관리제 페이지 상단 현페이지 정보 */
.admin_top_wrap{
height:110px;
line-height: 110px;
background-color: #5080bd;
margin-bottom: 15px;
}
.admin_top_wrap>span{
margin-left: 30px;
display:inline-block;
color: white;
font-size: 50px;
font-weight: bolder;
}
/* 관리자 wrap(네비+컨텐츠) */
.admin_wrap{
}
/* 관리자페이지 네비 영역 */
.admin_navi_wrap{
width: 20%;
height: 300px;
float:left;
height: 100%;
}
.admin_navi_wrap li{
display: block;
height: 80px;
line-height: 80px;
text-align: center;
}
.admin_navi_wrap li a{
display: block;
height: 100%;
width: 95%;
margin: 0 auto;
cursor: pointer;
font-size: 30px;
font-weight: bolder;
}
.admin_navi_wrap li a:link {color: black;}
.admin_navi_wrap li a:visited {color: black;}
.admin_navi_wrap li a:active {color: black;}
.admin_navi_wrap li a:hover {color: black;}
.admin_list_03{
background-color: #c8c8c8;
}
/* 관리자페이지 컨텐츠 영역 */
.admin_content_wrap{
width: 80%;
float:left;
min-height: 700px;
}
/* 관리자 컨텐츠 제목 영역 */
.admin_content_subject{
font-size: 40px;
font-weight: bolder;
padding-left: 15px;
background-color: #6AAFE6;
height: 80px;
line-height: 80px;
color: white;
}
/* 관리자 컨텐츠 메인 영역 */
.form_section{
width: 95%;
margin-left: 2%;
margin-top: 20px;
border: 1px solid #dbdde2;
background-color: #efefef;
}
.form_section_title{
padding: 20px 35px;
}
.form_section_title label{
display: block;
font-size: 20px;
font-weight: 800;
}
.form_section_content{
padding: 20px 35px;
border-top: 1px solid #dbdde2;
}
.form_section_content input{
width: 98%;
height: 25px;
font-size: 20px;
padding: 5px 1%;
}
.form_section_content select{
width: 98%;
height: 35px;
font-size: 20px;
text-align-last: center;
}
/* 입력란 공란 경고 태그 */
.form_section_content span{
display: none;
padding-top: 10px;
text-align: center;
color: #e05757;
font-weight: 300;
}
/* 버튼 영역 */
.btn_section{
text-align: center;
margin: 80px 0;
}
.btn{
min-width: 180px;
padding: 4px 30px;
font-size: 25px;
font-weight: 600;
line-height: 40px;
}
.enroll_btn{
background-color: #dbdde2;
margin-left:15px;
}
/* footer navai 영역 */
.footer_nav{
width:100%;
height:50px;
}
.footer_nav_container{
width: 100%;
height: 100%;
background-color:#8EC0E4;
}
.footer_nav_container>ul{
font-weight : bold;
float:left;
list-style:none;
position:relative;
padding-top:10px;
line-height: 27px;
font-family: dotum;
margin-left: 10px;
}
.footer_nav_container>ul>li{
display:inline;
width: 45px;
height: 19px;
padding: 10px 9px 0 10px;
}
.footer_nav_container>ul>span{
margin: 0 4px;
}
/* footer 영역 */
.footer{
width:100%;
height:130px;
background-color:#D4DFE6;
padding-bottom : 50px;
}
.footer_container{
width: 100%;
height: 100%;
margin: auto;
}
.footer_left>img {
width: 150%;
height: 130px;
margin-left: -20px;
margin-top: -12px;
}
.footer_left{
float :left;
width: 170px;
margin-left: 20px;
margin-top : 30px;
}
.footer_right{
float :left;
width: 680px;
margin-left: 70px;
margin-top : 30px;
}
/* float 속성 해제 */
.clearfix{
clear: both;
}
먼저 경고 문구가 적힌 span태그를 추가하겠습니다. 아래 3개의 span태그를 class속성 값 "form_section_content"인 <div>태그에 내부 제일 하단에 각각 추가합니다.
css는 9번 과정에서 추가하였습니다.
<span id="warn_authorName">작가 이름을 입력 해주세요.</span>
<span id="warn_nationId">소속 국가를 선택해주세요.</span>
<span id="warn_authorIntro">작가 소개를 입력 해주세요.</span>
자바스크립트 작업을 합니다. 등록 버튼(enrollBtn)클릭 메서드 내부에 '검사 통과 유무 변수'를 추가합니다. 해당 변수들은 의도적으로 메서드 내부에 선언하였습니다. 해당 변수를 메서드 내부에 선언함으로써 지역변수 규칙(변수가 선언된 블록 내에서만 유효하며, 블록이 종료되면 메모리에서 사라짐)으로 인해 메서드가 실행될 때마다 변수가 새롭게 선언되어 기본적으로 값이 'false'인 상태가 되도록 하기 위함입니다.
/* 등록 버튼 */
$("#enrollBtn").click(function(){
/* 검사 통과 유무 변수 */
let nameCheck = false; // 작가 이름
let nationCheck = false; // 소속 국가
let introCheck = false; // 작가 소개
/* 입력값 변수 */
let authorName = $('input[name=authorName]').val(); // 작가 이름
let nationId = $('select[name=nationId]').val(); // 소속 국가
let authorIntro = $('input[name=authorIntro]').val(); // 작가 소개
/* 공란 경고 span태그 */
let wAuthorName = $('#warn_authorName');
let wNationId = $('#warn_nationId');
let wAuthorIntro = $('#warn_authorIntro');
/* 작기 이름 공란 체크 */
if(authorName ===''){
wAuthorName.css('display', 'block');
nameCheck = false;
} else{
wAuthorName.css('display', 'none');
nameCheck = true;
}
/* 소속 국가 공란 체크 */
if(nationId ==='none'){
wNationId.css('display', 'block');
nationCheck = false;
} else{
wNationId.css('display', 'none');
nationCheck = true;
}
/* 작가 소개 공란 체크 */
if(authorIntro ===''){
wAuthorIntro.css('display', 'block');
introCheck = false;
} else{
wAuthorIntro.css('display', 'none');
introCheck = true;
}
/* 최종 검사 */
if(nameCheck && nationCheck && introCheck){
$("#enrollForm").submit();
} else{
return;
}
});
- 작가 등록 테스트
- 작가 등록 스크립트 테스트