JSP 피피티 11강 서비스레이어
Service Layer의 이해
-프로그램의 요구사항(비지니스 로직)을 담당하는 부분
하나의 웹 페이지에서 구현해야 하는 모든 기능을 JSP페이지에서 모두 구현할 경우, 한 페이지에서 구현해야 하는 내용이 지나치게 많아지기 때문에 효율적이지 못하며, 코드의 유지보수에도 좋지 않다.
Service Layer는 하나의 페이지가 구현해야 하는 기능 중 저수준의 데이터 처리로직(SQL 수행)과 웹 고유의 기능(요청에 대한 응답, UI구현)을 분리하고, 이를 연결해 주는 역할을 수행한다.
Service Layer의 구현 과정
-요구사항 정의
프로그램 개발시에 구현하고자 하는 기능을 미리 산정하는 과정
이 단계에서 도출된 기능들을 구체화 하는 것이 프로그래머의 역할
-요구사항을 하나의 기능 단위로 준비하기
*도출된 요구사항을 구현하기 위한 일련의 처리 로직을 비지니스 로직이라 한다.
*비지니스 로직을 표현하기 위한 기능 단위는 프로젝트의 요구사항에 부합해야 하므로, 요구사항 명세서가 명시하고 있는 기능들에 대해 인터페이스로 표현한다.
*Service Layer에서 정의되는 인터페이스의 역할은 데이터베이스 설계에 부합되는 데이터 처리로직을 수행하는 것이다.
만약에 기능구현까지 3단계로 나뉜다면 패키지 > 인터페이스 > 메서드 라고 하셨던거 같음
서비스 레이어 시작하기
servlet-context.xml에 서비스 구현체가 포함된 패키지 명시
<context:component-scan base-package="study.spring.simplespring.service.impl" />
구현할 기능을 정의한 인터페이스
/src/main/java/study.spring.simplespring.service/DepartmentService.java
package study.spring.practicespring.service;
import java.util.List;
import study.spring.practicespring.model.Department;
/*학과 데이터 관리 기능을 제공하기 위한 Service 계층*/
public interface DepartmentService {
/**
* 학과 데이터 상세 조회
* @param Department 조회할 학과의 일련번호를 담고 있는 beans
* @return 조회된 데이터가 저장된 Beans
* @throws Exception
*/
public Department getDepartmentItem(Department input) throws Exception;
/*
* 학과데이터 목록 조회
* @return 조회 결과에 대한 컬렉션
* @throw Exception
*/
public List<Department> getDepartmentList(Department input) throws Exception;
/**
* 학과 데이터가 저장되어 있는 갯수 조회
* @return int
* @throws Exception
*/
public int getDepartmentCount(Department input) throws Exception;
/**
* 학과 데이터 등록하기
* @param Department 저장할 정보를 담고 있는 Beans
* @return int
* @throws Exception
*/
public int addDepartment(Department input) throws Exception;
/**
* 학과 데이터 수정하기
* @param Department 수정할 정보를 담고 있는 Beans
* @return int
* @throws Exception
*/
public int editDepartment(Department input) throws Exception;
/**
* 학과 데이터 삭제하기
* @param Department 수정할 정보를 담고 있는 Beans
* @return int
* @throws Exception
*/
public int deleteDepartment(Department input) throws Exception;
}
구현체 클래스 작성
인터페이스를 상속받고 부모가 정의하고 있는 메서드를 오버라이드 한다.
/src/main/java/study.spring.simplespring.service.impl/DepartmentServiceImpl.java
package study.spring.practicespring.service.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import study.spring.practicespring.model.Department;
import study.spring.practicespring.service.DepartmentService;
/*학과 데이터 관리 기능을 제공하기 위한 Service 계층에 대한 구현체*/
@Slf4j
@Service
public class DepartmentServiceImpl implements DepartmentService{
/* MyBatis 세션 객체 주입 설정 */
//구현체에서 MyBatis를 호출하기 위해서는 sqlSession이 필요하다.
@Autowired
SqlSession sqlSession;
/**
* 학과 데이터 상세 조회
* @param Department 조회할 학과의 일련번호를 담고 있는 Beans
*/
@Override
public Department getDepartmentItem(Department input) throws Exception {
Department result = null;
try {
result = sqlSession.selectOne("DepartmentMapper.selectItem", input);
if(result == null) {
throw new NullPointerException("result=null");
}
}catch(NullPointerException e) {
log.error(e.getLocalizedMessage());
throw new Exception("조회된 데이터가 없습니다.");
}catch(Exception e) {
log.error(e.getLocalizedMessage());
throw new Exception("데이터 조회에 실패했습니다.");
}
return result;
}
/**
* 학과 데이터 목록 조회
* @return 조회 결과에 대한 컬렉션
* @throws Exception
*/
@Override
public List<Department> getDepartmentList(Department input) throws Exception {
List<Department> result = null;
try{
result = sqlSession.selectList("DepartmentMapper.selectList", input);
if(result == null){
throw new NullPointerException("result=null");
}
}catch(NullPointerException e) {
log.error(e.getLocalizedMessage());
throw new Exception("조회된 데이터가 없습니다.");
}catch(Exception e) {
log.error(e.getLocalizedMessage());
throw new Exception("데이터 조회에 실패했습니다.");
}
return result;
}
/**
* 학과 데이터가 저장되어 있는 갯수 조회
* @return int
* @throws Exception
*/
@Override
public int getDepartmentCount(Department input) throws Exception {
int result = 0;
try {
result = sqlSession.selectOne("DepartmentMapper.selectCountAll", input);
}catch(Exception e) {
log.error(e.getLocalizedMessage());
throw new Exception("데이터 조회에 실패했습니다.");
}
return result;
}
/**
* 학과데이터 등록하기
* @param Department 저장할 정보를 담고 있는 Beans
* @return int
* @throws Exception
*/
@Override
public int addDepartment(Department input) throws Exception{
int result = 0;
try {
result = sqlSession.insert("DepartmentMapper.insertItem", input);
if(result == 0) {
throw new NullPointerException("result=0");
}
}catch(NullPointerException e) {
log.error(e.getLocalizedMessage());
throw new Exception("저장된 데이터가 없습니다.");
}catch(Exception e) {
log.error(e.getLocalizedMessage());
throw new Exception("데이터 저장에 실패했습니다.");
}
return result;
}
/**
* 학과데이터 수정하기
* @param Department 수정할 정보를 담고 있는 Beans
* @return int
* @throws Exception
*/
@Override
public int editDepartment(Department input) throws Exception {
int result = 0;
try {
result = sqlSession.update("DepartmentMapper.updateItem", input);
if(result ==0) {
throw new NullPointerException("result=0");
}
}catch(NullPointerException e) {
log.error(e.getLocalizedMessage());
throw new Exception("수정된 데이터가 없습니다.");
}catch(Exception e) {
log.error(e.getLocalizedMessage());
throw new Exception("데이터 수정에 실패했습니다.");
}
return result;
}
/**
* 학과 데이터 삭제하기
* @param Department 수정할 정보를 담고 있는 Beans
* @return int
* @throws Exception
*/
@Override
public int deleteDepartment(Department input) throws Exception {
int result = 0;
try {
result = sqlSession.delete("DepartmentMapper.deleteItem", input);
if(result ==0) {
throw new NullPointerException("result=0");
}
}catch(NullPointerException e) {
log.error(e.getLocalizedMessage());
throw new Exception("삭제된 데이터가 없습니다.");
}catch(Exception e) {
log.error(e.getLocalizedMessage());
throw new Exception("데이터 삭제에 실패했습니다.");
}
return result;
}
}
서비스 레이어를 작성했다면 잘 작성이 되었는지 단위테스트를 진행한다.
/src/test/java/study.spring.practicespring.service/DepartmentServiceTest.java
package study.spring.practicespring.service;
import java.util.List;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import lombok.extern.slf4j.Slf4j;
import study.spring.practicespring.model.Department;
/*Lombok의 log4j 객체*/
//import lombok.extern.slf4j.Slf4j;
@Slf4j
/* JUnit에 의한 테스트 클래스로 정의 */
//import org.junit.runner.RunWith;
//import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
/*Spring의 설정 파일들을 읽어들이도록 설정(**은`모든` 이라는 의미)*/
//import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/**/*-context.xml"})
/* 웹 어플리케이션임을 명시 */
//import org.springframework.test.context.web.WebAppConfiguration;
@WebAppConfiguration
/* 메서드 이름순서로 실행하도록 설정(설정하지 않을 경우 무작위 순서로 실행됨) */
//import org.junit.FixMethodOrder;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class DepartmentServiceTest {
/* Service객체 주입 설정 */
//import org.springframework.beans.factory.annotation.Autowired;
@Autowired
private DepartmentService departmentService;
/* 단일행 조회 테스트 */
@Test
public void testA() {
//검색조건으로 사용될 POJO클래스 객체
Department input = new Department();
input.setDeptno(101);
Department output = null;
try {
output = departmentService.getDepartmentItem(input);
log.debug(output.toString());
}catch(Exception e) {
log.error(e.getLocalizedMessage());
e.printStackTrace();
}
}
/* 다중행 조회 테스트 */
@Test
public void testB() {
//검색조건으로 사용도리 POJO클래스 객체
Department input = new Department();
input.setDname("공학");
List<Department> output = null;
try {
output = departmentService.getDepartmentList(input);
for(Department item:output) {
log.debug(item.toString());
}
}catch(Exception e) {
log.error(e.getLocalizedMessage());
e.printStackTrace();
}
}
/* 전체 데이터 수 조회 */
@Test
public void testC() {
int count = 0;
try {
count = departmentService.getDepartmentCount(null);
log.debug("전체 데이터 수: " + count);
}catch(Exception e) {
log.error(e.getLocalizedMessage());
e.printStackTrace();
}
}
/* 조건에 따른 데이터 수 조회 */
@Test
public void testD() {
int count = 0;
Department input = new Department();
input.setDname("공학");
try {
count = departmentService.getDepartmentCount(input);
log.debug("공학을 포함하는 학과이름을 갖는 데이터 수: " + count);
}catch(Exception e) {
log.error(e.getLocalizedMessage());
e.printStackTrace();
}
}
/* 데이터 저장 테스트 */
@Test
public void testE() {
Department input = new Department();
input.setDname("신규학과");
int output = 0;
try {
output = departmentService.addDepartment(input);
log.debug("저장된 데이터 수: " + output);
//[중요] 생성된 Pk값을 MyBatis에 의해 입력 파라미터의 해당 멤버변수에 셋팅된다.
log.debug("생성된 PK 갑 : " + input.getDeptno());
}catch(Exception e) {
log.error(e.getLocalizedMessage());
e.printStackTrace();
}
}
/* 데이트 수정 테스트 */
@Test
public void testF() {
Department input = new Department();
input.setDeptno(203);
input.setDname("수정학과");
input.setLoc("5호관");
int output = 0;
try {
output = departmentService.editDepartment(input);
log.debug("수정된 데이터 수: " + output);
}catch(Exception e) {
log.error(e.getLocalizedMessage());
e.printStackTrace();
}
}
/* 데이트 삭제 테스트 */
@Test
public void testG() {
Department input = new Department();
input.setDeptno(203);
int output = 0;
try {
output = departmentService.deleteDepartment(input);
log.debug("삭제된 데이터 수: " + output);
}catch(Exception e) {
log.error(e.getLocalizedMessage());
e.printStackTrace();
}
}
}
단위 테스트에서 testA()메서 드를 실행하면
*참고용 Mapper
뭐 이런 식으로 코드가 작동 한다고 하셨다.
결과 확인