ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JSP 피피티 11강 서비스레이어
    JSP 피피티 2020. 9. 5. 10:47

    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

    뭐 이런 식으로 코드가 작동 한다고 하셨다.

    결과 확인

     

전설의 개발자