ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [3-ch9 비즈니스 계층] Service의 생성과 설정
    Back-End/Spring Legacy 2022. 8. 19. 13:22

     

    비즈니스 계층이란?

     

     

     

    비즈니스 계층은 고객의 요구사항을 반영하는 계층으로 프레젠테이션 계층(컨트롤러, view) 과 영속 계층(db)의 중간 다리 역할을 하게 된다. 영속 계층은 데이터베이스를 기준으로 해서 설계를 나눠 구현하지만, 비즈니스 계층은 로직을 기준으로 해서 처리한다. 

     

    예를들어, '쇼핑몰에서 상품을 구매한다'고 가정해볼때, 해당 쇼핑몰의 로직이' 물건을 구매하는 회원에게는 포인트를 올려준다'고 하면 영속 계층의 설계는 '상품'과 '회원'으로 나누어서 설계하게 된다. 반면에 비즈니스 계층은 상품 영역과 회원 영역을 동시에 사용해서 하나의 로직을 처리하게 되므로 다음과 같은 구조를 만들게 된다 .

     

     

     

    Business (service), Persistence tier( DB, DAO)

     

     

    오늘 포스팅할 예제는 단일 테이블을 이용하고 있기 때문에 위와 같은 구조는 아니지만, 설계를 할 때는 원칙적으로 영역을 구분해서 작성해야 한다. 일반적으로 비즈니스 영역에 있는 객체들은 '서비스(Service)'라는 용어를 많이 사용한다.

     

     

     

    비즈니스 계층의 설정

     

    비즈니스 계층을 위해 프로젝트 내 org.zerock.service라는 패키지를 작성하고 게시물은 BoardService 인터페이스와 인터페이스를 구현한 BoardServiceImpl 클래스를 선언한다. (이는 느슨한(loose) 연결(결합)을 한다.)

     

     

     

    BoardService 메서드를 설계할 때 메서드 이름은 현실적인 로직, 즉) 고객과 소통을 위한 이름을 붙이는 것이 관례이다. 명백하게 반환해야 할 데이터가 있는 'select'를 해야 하는 메서드는 리턴 타입을 지정할 수 있다. 하나씩 테스트를 하기 위해서나 코드를 작성할 때 BoardService에 미리 메서드를 작성해두면 BoardServiceImpl은 BoardService 인터페이스를 구현(inplements)하고 있기 때문에 BoardServiceImpl에도 같은 메서드를 @Override해서 작성해야 한다. 그러므로 하나씩 진행하는 게 편하다. 

     

     

    <BoardService 인터페이스>

    package org.zerock.service;
    
    import java.util.List;
    
    import org.zerock.domain.BoardVO;
    
    public interface BoardService {
    
    	public void register(BoardVO board);
    	
    	public BoardVO get(Long bno);
    
    	public boolean modify(BoardVO board);
    	
    	public boolean remove(Long bno);
    	
    	public List<BoardVO> getList();
    }

     

     

    BoardServiceImpl 클래스에 가장 중요한 부분은 @Service라는 어노테이션이다. @Service는 계층 구조상 주로 비즈니스 영역을 담당하는 객체임을 표시하기 위해 사용한다. 작성된 어노테이션은 패키지를 읽어 들이는 동안 처리된다. BoardServiceImpl 가 정상적으로 동작하기 위해서는 BoardMapper 객체가 필요하다.  

     

    이는 @Autowired와 같이 직접 설정해줄 수 있고, Setter를 이용해 처리할 수도 있다. 여기서는 @AllArgsConstructor를 사용해서 모든 파라미터를 이용하는 생성자를 만들고 있지만 @RequiredArgsConstructor를 사용하고 객체 생성 시 final 예약어를 작성해도 된다.

     

     

    <BoardServiceImpl 클래스>

    package org.zerock.service;
    
    import java.util.List;
    
    import org.springframework.stereotype.Service;
    import org.zerock.domain.BoardVO;
    import org.zerock.mapper.BoardMapper;
    
    import lombok.AllArgsConstructor;
    import lombok.extern.log4j.Log4j2;
    
    @Log4j2
    @Service
    @AllArgsConstructor
    
    public class BoardServiceImpl implements BoardService{
    	
    	private BoardMapper mapper;
    	
    	@Override
    	public void register(BoardVO board) {
    		
    		log.info("register ........." + board);
    		mapper.insertSelectKey(board);
    		
    	};
    	
    	@Override
    	public List<BoardVO> getList(){
    		
    		log.info("getList............");
    		return mapper.getList();
    	}
    	
    	@Override
    	public BoardVO get(Long bno) {
    		log.info("get............." + bno);
    		return mapper.read(bno);
    		
    	}
    	
    	@Override
    	public boolean modify(BoardVO board) {
    		
    		log.info("modify............." + board);
    		return mapper.update(board) == 1; 
    	}
    	
    	@Override
    	public boolean remove(Long bno) {
    		log.info("remove ........... "+bno);
    		return mapper.delete(bno) ==1;
    	}
    
    }

     

     

     

    스프링 서비스 객체 설정(root-context.xml)

     

    비지니스 계층의 인터페이스와 구현 클래스가 작성되었다면, 이를 스프링의 빈으로 인식하기 위해서 root-context.xml에 @Service 어노테이션이 있는 org.zerock.service 패키지를 스캔하도록 추가해야 한다. namespace에 context 항목을 체크한다.

     

     

     

    네임스페이스를 추가하면 해당 이름으로 시작하는 태그들을 활용할 수 있다. root-context.xml의 내부 내용을 추가한다.

     

    <context:component-scan base-package="org.zerock.service"></context:component-scan>

     

     

    비지니스 계층의 구현과 테스트 

     

    BoardMapper와 BoardService, BoardServiceImpl에 대한 구조 설정이 완료되었다면 테스트하기 위해 'src/test/java' 밑에 BoardServiceTests 클래스를 작성한다. 

     

    package org.zerock.service;
    
    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 org.zerock.domain.BoardVO;
    
    import lombok.Setter;
    import lombok.extern.log4j.Log4j2;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
    @Log4j2
    public class BoardServiceTests {
    	
    	@Setter(onMethod_ = @Autowired)
    	public BoardService service;
    	
    	@Test
    	public void testExist() {
    		log.info(service);
    		assertNotNull(service);
    	}
    	
    	@Test
    	public void testRegister() {
    		
    		BoardVO board = new BoardVO();
    		board.setTitle("서비스 이용 새로 작성");
    		board.setContent("서비스 이용 새로 작성 내용");
    		board.setWriter("soso");
    		service.register(board);
    		
    		log.info("생성된 게시물 번호 : " + board.getBno());
    		
    	}
    	
    	@Test
    	public void testGetList() {
    		service.getList().forEach(board -> log.info(board));;
    	}
    	
    	@Test
    	public void testGet() {
    		log.info(service.get(1L));
    	}
    	
    	@Test
    	public void testUpdate() {
    		BoardVO board = new BoardVO();
    		board = service.get(1L);
    		if(board == null){return;}
    		
    		board.setTitle("service 제목 수정");
    		log.info("modify result " + service.modify(board));
    		
    	}
    	
    	@Test
    	public void testDelete() {
    		log.info("remove result ...." + service.remove(2L));
    		
     	}
    
    }

     

     

     

     

    등록 작업 (register)

     

    <BoardServiceImpl> 에 register 메서드를 완성해주는데, 새로운 정보를 insert하기에 BoardVO 객체 타입을 매개변수로 받는다. 반환 타입은 void.

     

    @Override
    public void register(BoardVO board) {
    
        log.info("register ........." + board);
        mapper.insertSelectKey(board);
    
    };

     

    BoardService는 void 타입으로 설계되었으므로 mapper.insertSelectKey()의 반환 값인 int를 사용하지 않고 있지만, 필요하다면 예외 처리나 void 대신 int 타입을 이용해서 사용할 수 있다. 

     

     

    <BoardServiceTests>

    @Test
    public void testRegister() {
    
        BoardVO board = new BoardVO();
        board.setTitle("서비스 이용 새로 작성");
        board.setContent("서비스 이용 새로 작성 내용");
        board.setWriter("soso");
        service.register(board);
    
        log.info("생성된 게시물 번호 : " + board.getBno());
    
    }

     

     

     

     

    testRegister()의 테스트 결과는 다음과 같이 생성된 게시글의 번호를 확인할 수 있다. 

     

    db

     

     

    service가 없을때와 있을때의 Tests 코드의 차이

     

     

     

    목록(리스트) 작업 구현 테스트

     

    앞 포스팅에서 테스트 한 내용을 다뤘기에 service를 추가했다고 해서 결과에 달라질 건 없다. 이해를 위해 호출 과정을 볼 수 있는 사진을 첨부하겠다.

     

     

     

     

     

    반응형

    댓글

Designed by Tistory.