비즈니스 계층
- 고객의 요구사항을 반영하는 계층
- 업무의 단위로 설계
- 트랜잭션의 단위
- 여러 개의 Mapper나 DAO를 사용하는 경우가 존재함
- xxxService의 형태로 작성
비지니스 계층의 설정
비즈니스 계층을 위해서 프로젝트 내 org.codehows.service라는 패키지를 작성한다.
설계를 할 때 각 계층 간의 연결은 인터페이스를 이용해서 느슨한 연결을 한다.
게시물은 BoardService 인터페이스와 인터페이스를 구현한 BoardServiceImpl클래스를 선언한다.
BoardService 인터페이스
// BoardService.java
package org.codehows.service;
import java.util.List;
import org.codehows.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();
}
BoardService 메서드를 설계할 때 메서드 이름은 현실적인 로직의 이름을 붙이는 것이 관례이다.
명백하게 반환해야 할 데이터가 있는 select를 해야하는 메서드는 리턴타입을 지정할 수 있다.
게시물은 특정한 게시물을 가져오는 get() 메서드와 전체리스트를 구하는 getList()의 경우 처음부터 메서드의 리턴 타입을 결정해서 진행 할 수 있다.
BoardService 인터페이스를 구현하는 구현체는 BoardServiceImpl이라는 클레스로 작성한다.
약간의 로그를 기록할 수 있는 정도의 코드를 작성한다.
BoardServiceImpl 클래스
// BoardServiceImpl.java
package org.codehows.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.codehows.domain.BoardVO;
import org.codehows.mapper.BoardMapper;
import lombok.AllArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@Log4j
@Service
@AllArgsConstructor
public class BoardServiceImpl implements BoardService {
private BoardMapper mapper;
@Override
public void register(BoardVO board) {}
@Override
public BoardVO get(Long bno) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean modify(BoardVO board) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean remove(Long bno) {
// TODO Auto-generated method stub
return false;
}
@Override
public List<BoardVO> getList() {
// TODO Auto-generated method stub
return null;
}
}
스프링의 서비스 객체 설정 (root-context.xml)
비즈니스 계층의 인터페이스와 구현 클래스가 작성되었다면, 이를 스프링의 빈으로 인식하기 위해서 root-context.xml에 @Service 어노테이션이 있는 org.codehows.service 패키지를 스캔 하도록 추가
프로젝트 생성 시 만들어진 root-context.xml의 네임스페이스 탭에서 context 항목을 추가
네임 스페이스를 추가했으면 해당 이름으로 시작하는 태그들을 활용 할 수 있다.
root-context.xml
// root-context.xml
... 생략 ...
<context:component-scan base-package="org.codehows.service"></context:component-scan>
</beans>
비즈니스 계층의 구현과 테스트
BoardMapper와 BoardService, BoardServiceImpl에 대한 구조 설정이 완료되었으므로 'src/test/java' 밑에 org.codehows.service.BoardServiceTests 클레스를 작성 한뒤 테스트 작업을 진행
테스트 환경의 BoardServiceTests 클래스
// BoardServiceTest.java
package org.codehows.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 lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardServiceTests {
@Setter(onMethod_ = {@Autowired})
private BoardService service;
@Test
public void testExist() {
log.info(service);
assertNotNull(service);
}
}
BoardServiceTests의 첫 테스트는 BoardService 객체가 제대로 주입이 가능한지 확인하는작업으로 시작한다.
정상적으로 BoardService 객체가 생성되고 BoardMapper가 주입되었다면 아래와 같이 BoardService객체와 데이터베이스 관련 로그가 같이 출력된다.
등록 작업의 구현과 테스트
등록 작업은 BoardServiceImpl에서 파라미터로 전달되는 BoardVO 타입의 객체를 BoardMapper를 통해서 처리한다.
// BoardServiceImpl.java
package org.codehows.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.codehows.domain.BoardVO;
import org.codehows.mapper.BoardMapper;
import lombok.AllArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@Log4j
@Service
@AllArgsConstructor
public class BoardServiceImpl implements BoardService {
private BoardMapper mapper;
@Override
public void register(BoardVO board) {
log.info("register......" + board); << 추가
mapper.insertSelectKey(board); << 추가
}
... (생략) ...
BoardServiceTests 클래스
// BoardServiceTests.java
... (생략) ...
@Test
public void testRegister() {
BoardVO board = new BoardVO();
board.setTitle("새로 작성하는 글");
board.setContent("새로 작성하는 내용");
board.setWriter("newbie");
service.register(board);
log.info("생성된 게시물의 번호: " + board.getBno());
}
목록(리스트) 작업의 구현과 테스트
BoardServiceImpl 클레스에서 현재 테이블에 저장된 모든 데이터를 가져오는 getList()는 아래와 같이 구현된다.
// BoardServiceImpl.java
... (생략) ...
@Override
public List<BoardVO> getList() {
log.info("getList........");
return mapper.getList();
}
조회 작업의 구현과 테스트
조회는 게시물의 번호가 파라미터이고 BoardVO의 인스턴스가 리턴이 된다.
BoardServiceImpl 클래스
// BoardServiceImpl.java
... 생략 ...
@Override
public BoardVO get(Long bno) {
log.info("get......" + bno);
return mapper.read(bno);
}
... 생략 ...
테스트 코드
BoardServiceTests 클래스
// BoardServiceTests.java
... 생략 ...
@Test
public void testGet() {
log.info(service.get(1L));
}
}
삭제/수정 구현과 테스트
삭제/수정은 메서드의 리턴 타입을 void로 설계할 수도 있지만 엄격하게 처리하기 위해서 Boolean 타입으로 처리한다.
// BoardServiceImpl.java
... 생략 ...
@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;
}
정상적으로 수정과 삭제가 이루어지면 1이라는 값이 반환되기 떄문에 == 연산자를 이용해서 true/false를 처리할 수 있다.
BoardServiceTests 클래스
// BoardServiceTests.java
... 생략 ...
@Test
public void testDelete() {
// 게시물 번호의 존재 여부를 확인하고 테스트 할 것
log.info("REMOVE RESULT: " + service.remove(2L));
}
@Test
public void testUpdate() {
BoardVO board = service.get(1L);
if (board == null) {
return;
}
board.setTitle("제목 수정합니다.");
log.info("MODIFY RESULT: " + service.modify(board));
}
}
testDelete() 의 경우에는 해당 게시물이 존재할 때 true를 반환하는 것을 확인할 수 있고, testUpdate()의 경우에는 특정한 게시물을 먼저 조회하고, title 값을 수정한 후 이를 업데이트 한다.
'Spring' 카테고리의 다른 글
list.jsp (0) | 2023.03.23 |
---|---|
스프링 - Part 3 - 기본적인 웹 게시물 관리 04 (프레젠테이션(웹) 계층의 CRUD 구현) (0) | 2023.03.22 |
스프링 - Part 3 - 기본적인 웹 게시물 관리 02 (영속/비즈니스 계층의 CRUD 구현) (0) | 2023.03.22 |
스프링 - Part 3 - 기본적인 웹 게시물 관리 01 (스프링 MVC 프로젝트의 기본 구성) (0) | 2023.03.22 |
스프링 - Part 2 - 스프링 MVC 설정 02 (2) | 2023.03.21 |