티스토리 뷰

08. 스프링(Spring) 개발 - 프로젝트 계층 구조 설계



이전글 : http://marshmello.tistory.com/9


이전 글에서는 프로젝트를 생성하고 기본 UI 셋팅을 하였습니다.


이번글에서는 구현 작업중에 가장 먼저 처리하는 작업인 등록기능과 그 중에서도 영속 계층에 대한 처리 방법을 작성하겠습니다.


개발 경험이 충분하다면 컨트롤러를 먼저 작성하는 것도 좋은 방법이지만 간단한 테이블 구성인 경우와 개발 경험이 적은 경우에는 쉽게 결과를 파악 할 수 있는 영속 계층을 구현하는 것을 권장합니다. 만일 테이블의 구성이 복잡하다면 오히려 화면을 먼저 볼 수 있는 개발방식이 나은 경우가 많습니다.


데이터베이스와 관련된 작업을 먼저해서 좋은 점 중 하나는 그 사이에 변경되는 화면의 설계를 미룰 수 있다는 점과 WAS없이 테스트를 진행 할 수 있는 점입니다.



8.1 테이블 관련 클래스 생성

가장 먼저 하는 일은 테이블의 구조를 객체화시킬 때 사용하는 BoardVO 클래스를 작성하는 것으로 시작힙니다.

'com.marshmello.vo' 패키지 내에 BoardVO를 생성합니다. 


package com.marshmello.vo;

public class BoardVO {
	private Integer bNo;
	private String title;
	private String content;
	private String writer;
	private String regDate;
	private int viewCnt;

	public Integer getbNo() {
		return bNo;
	}

	public void setbNo(Integer bNo) {
		this.bNo = bNo;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public String getWriter() {
		return writer;
	}

	public void setWriter(String writer) {
		this.writer = writer;
	}

	public String getRegDate() {
		return regDate;
	}

	public void setRegDate(String regDate) {
		this.regDate = regDate;
	}

	public int getViewCnt() {
		return viewCnt;
	}

	public void setViewCnt(int viewCnt) {
		this.viewCnt = viewCnt;
	}

	@Override
	public String toString() {
		return "BoardVO [bNo=" + bNo + ", title=" + title + ", content=" + content + ", writer=" + writer + ", regDate="
				+ regDate + ", viewCnt=" + viewCnt + "]";
	}

}


8.2 DAO의 생성과 XML Mapper 작업

VO는 전체 영역에서 파라미터와 리턴 타입으로 사용되므로 가장 먼저 작업되고 이후에는 MyBatis의 DAO, XML Mapper를 설정해 주고 테스트하는 과정을 거치도록 합니다.


root-context.xml 의 설정을 위해 xml 파일 내부에서 사용하는 태그들에 대한 설정을 위한 XML 네임스페이스를 추가합니다. root-context.xml 파일을 선택하고 , 아래쪽의 탭 메뉴 중에서 NameSpaces를 선택합니다.



네임스페이스 beans, context, mybatis-spring(옵션)을 선택하고 저장합니다.


저장 후 source 탭을 눌러 MyBatis의 SqlSessionFactory, SqlSessionTemplate을 등록합니다.


<?xml version="1.0" encoding="UTF-8"?>


	<!-- Root Context: defines shared resources visible to all other web components -->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />
		<property name="url" value="jdbc:log4jdbc:mysql://localhost:3306/sample?autoReconnect=true&useSSL=false" />
		<property name="username" value="root" />
		<property name="password" value="1234" />
	</bean>
	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation"  value="classpath:/mybatis-config.xml" />
		<property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml" />
	</bean>
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"
		destroy-method="clearCache">
		<constructor-arg ref="sqlSessionFactory" name="sqlSessionFactory" />
	</bean>
	
	<context:component-scan base-package="com.marshmello.dao" />
	


설정의 마지막에 com.marshmello.dao 패키지를 자동으로 인식하기 위한 <context:component-scan>이 사용된 것을 주의합니다.


com.marshmello.dao 패키지에 BoardDAO 인터페이스를 아래와 같이 작성합니다.


package com.marshmello.dao;

import java.util.List;

import com.marshmello.vo.BoardVO;

public interface BoardDAO {
	
	public void create(BoardVO boardVO) throws Exception;
	
	public BoardVO read(Integer bNo) throws Exception;
	
	public void update(BoardVO boardVO) throws Exception;
	
	public void delete(Integer bNo) throws Exception;
	
	public List<boardvo> listAll() throws Exception;
}




'src/main/resources'에 mappers 폴더를 작성하고 폴더에 boardMapper.xml을 작성하고 insert,update,delete,select SQL문을 추가합니다.

<?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.marshmello.mapper.boardMapper">
	<insert id="create" parameterType="com.marshmello.vo.BoardVO">
		insert into tbl_board(title,content,writer) values
		(#{title},#{content},#{writer})
	</insert>
	<select id="read" resultType="com.marshmello.vo.BoardVO">
		select
		bno,title,content,writer,regdate, viewcnt from tbl_board where bno =
		#{bNo}
	</select>
	<update id="update">
		update tbl_board set title=#{title},
		content=#{content} where bno= #{bNo}
	</update>
	<delete id="delete">
		delete from tbl_board where bno = #{bNo}
	</delete>
	<select id="listAll" resultType="com.marshmello.vo.BoardVO">
	<![CDATA[
	select bno, title, content, writer, regdate,viewcnt from tbl_board
	]]>
	</select>
</mapper>


BoardDAO 인터페이스를 구현한 BoardDAOImpl을 다음과 같이 작성합니다.

package com.marshmello.dao; import java.util.List; import javax.inject.Inject; import org.apache.ibatis.session.SqlSession; import org.springframework.stereotype.Repository; import com.marshmello.vo.BoardVO; @Repository public class BoardDAOImpl implements BoardDAO { @Inject private SqlSession sqlSession; private static String namespace ="com.marshmello.mapper.boardMapper"; @Override public void create(BoardVO boardVO) throws Exception { sqlSession.insert(namespace+".create", boardVO); } @Override public BoardVO read(Integer bNo) throws Exception { return sqlSession.selectOne(namespace+".read",bNo); } @Override public void update(BoardVO boardVO) throws Exception { sqlSession.update(namespace+".update",boardVO); } @Override public void delete(Integer bNo) throws Exception { sqlSession.delete(namespace+".delete",bNo); } @Override public List<boardvo> listAll() throws Exception { return sqlSession.selectList(namespace+".listAll"); } }


테스트 순서는 위의 테스트 코드부터 순차적으로 진행해주면 되고, 각 테스트 후에 반드시 데이터베이스 상에서 올바르게 처리되었는지 확인을 합니다. 


8.2.1 <typeAliases>의 적용

XML Mapper를 이용하는데 있어 매번 parameterType이나 resultType을 패키지까지 포함된 클래스명을 작성하는 일이 번거롭다면 MyBatis의 설정파일인 myBatis-config.xml을 사용해서 <typeAliases>를 작성하면 됩니다.


mybatis-config.,xml 다음과 같이 작성합니다.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<typeAliases>
		<package name="com.marshmello.vo" />
	</typeAliases>
</configuration>

<typeAliases>는 하위요소로 <package>와 <typeAlias>요소를 가질 수 있습니다. 이 요소들을 이용하면 매번 parameterType이나 resultType에 사용하는 클래스의 이름을 'com.marshmello.vo'은 생략한 채로 표현하는 것이 가능합니다. 


8.3 비즈니스 계층 구현

비즈니스 계층은 고객의 요구사항이 반영되는 영역입니다. 비즈니스 영역에 만들어지는 클래스나 인터페이스는 반드시 요구사항과 일치하도록 설계돼야합니다.


스프링에서 비즈니스 영역은 일반적으로 서비스(Service)라는 이름을 칭합니다. 일반적인 개발순서는 요구사항을 메소드로 정리해서 xxxService인터페이스를 정의하고 xxxServiceImpl이라는 구현 객체를 만들어주는 순서로 진행됩니다.

이후의 모든 예제에서 Service 객체라고 하면 구현 객체를 의미합니다.


비즈니스 계층은 쉽게 말해 컨트롤러와 DAO 사이의 접착제 역할을 합니다. 중간에 비즈니스 영역을 구분해서 개발하면 개발 양이 늘어나는 것이 사실입니다. 그럼에도 몇가지 이유로 인해 굳이 계층을 분리해서 개발합니다.

  • 비즈니스 계층은 고객마다 다른 부분을 처리 할 수 있는 완충장치 역할을 합니다.
  • 각 회사마다 다른 로직이나 규칙을 데이터베이스에 무관하게 처리할 수 있는 완충 영역으로 존재할 필요가 있습니다.
  • 컨트롤러와 같은 외부 호출이 영속 계층에 종속적인 상황을 막아줍니다.
  • 만일 컨트롤러가 직접 영속 계층의 데이터베이스를 이용해하게 되면 트랜잭션의 처리나 예외의 처리 등 모든 로직이 컨트롤러로 집중됩니다. 비즈니스 계층은 컨트롤러로 하여금 처리해야 하는 일을 분업하게 만들어 줍니다.


비즈니스 계층의 구현은 로직에 필요한 데이터베이스 관련 객체들을 모아서 자신이 원하는 일을 처리하는 용도입니다.


com.marshmello.service 패키지 아래 BoardService.java 코드를 아래와 같이 작성합니다.

package com.marshmello.service;

import java.util.List;

import com.marshmello.vo.BoardVO;

public interface BoardService {

	public void regist(BoardVO board) throws Exception;

	public BoardVO read(Integer bNo) throws Exception;

	public void modify(BoardVO board) throws Exception;

	public void remove(Integer bNo) throws Exception;

	public List<boardvo>  listAll() throws Exception;
}


인터페이스를 구현한 구현 클래스는 아래와 같이 작성합니다.


package com.marshmello.service;

import java.util.List;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import com.marshmello.dao.BoardDAO;
import com.marshmello.vo.BoardVO;

@Service
public class BoardServiceImpl implements BoardService {

	@Inject
	private BoardDAO boardDao;

	@Override
	public void regist(BoardVO board) throws Exception {
		boardDao.create(board);
	}

	@Override
	public BoardVO read(Integer bNo) throws Exception {
		return boardDao.read(bNo);
	}

	@Override
	public void modify(BoardVO board) throws Exception {
		boardDao.update(board);
	}

	@Override
	public void remove(Integer bNo) throws Exception {
		boardDao.delete(bNo);
	}

	@Override
	public List<boardvo>  listAll() throws Exception {
		return boardDao.listAll();
	}

}


클래스 선언부에는 스프링의 빈으로 인식되기 위해서 @Service 애노테이션이 적용된 것을 볼 수 있습니다.

코드를 보면 간단한 CRUD 작업을 하기 때문에 별다른 것이 없고, DAO를 이용해서 작업을 실행하는 정도입니다. 


비즈니스의 계층의 서비스는 현재의 경우 별다른 중요한 작업이 없기 때문에 원한다면 테스트 코드를 작성하지 않아도 괜찮다고 생각합니다. 하지만 뒤에 나오는 트랜잭션이나 AOP 기능을 테스트 하기 위해서는 미리 테스트 코드를 가지고 있는 것이 좋습니다. 


이제 프로젝트의 기본 구조 설계는 완료되었습니다. 다음글에서는 게시판 등록 기능과 조회 기능 구현 작업을 해보도록 하겠습니다.


ex01.zip


수고하셨습니다.!


댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크