[Spring] Spring MVC 패턴


스프링이 아닌 개발자 입장에서(?) 개발하는 Spring MVC 패턴은?



1. Spring MVC란?

Model-View-Controller의 약자이다.

  • Model: 어플리케이션 계층의 정보, 데이터 의미
  • View: 화면 출력 로직을 담당
  • Controller: Model과 View의 연결하는 제어 로직을 담당



2. Spring MVC 흐름

mvc-dev-s1

  • 최대한 짧게 포스팅할려고 해도 노력했다. 세부적인 내용이나 설정은 빼고 전체적인 흐름에 대한 포스팅이다. 해당 글을 읽기 전에 Spring에서 말하는 Spring MVC 이란? 포스팅도 읽어 볼 만 하다.
  • 전체적인 흐름은 JPA이 방식이 아닌 많이들 접하는(?) SQL mapper로 DB/쿼리 위주 개발 방식인 Spring MVC + DAO + MyBatis 형태이다. 우선 스프링에서 해주는 MVC 처리가 아닌 개발자가 웹 프로젝트에 있어서 대체적으로 비즈니스로직 개발 및 SQL문 작성하는 부분이다.


Controller


@Controller
@RequestMapping("/board")
public class BoardController {

	@Autowired
	private BoardService boardService;
	
	@RequestMapping("boardList")
	public String boardList(HttpServletRequest request, BoardDto boardDto) throws Exception {
        
		List<BoardDto> list = this.boardService.selectBoardList(request, boardDto);
        model.put("list", list);

		return "/boardList";
	}
	
	@RequestMapping("boardList2")
	public ModelAndView boardList2(HttpServletRequest request, BoardDto boardDto) throws Exception {
		ModelAndView mv = new ModelAndView();

		List<BoardDto> list = this.boardService.selectBoardList(request, boardDto);
        model.put("list", list);

		// mv.setViewName("/fo/board/boardList2");
		return mv;
	}
}

@Controller

  • 선언 시 해당 클래스에서 HTTP 요청을 처리하는 Controller로 명시
  • Dispatcher에서 @Controller 맵핑 된 클래스에게 위임
  • @RequestMapping 을 통하여 요청을 처리할 메서드에 호출
  • 필요한 비즈니스 로직을 호출(Service)


@RequestMapping

  • @Controller 선언 된 클래스에서만 사용 가능
  • 요청URI을 컨트롤러의 메서드와 매핑할 때 사용
  • 메서드 내 View Name이 없을 경우 path로 설정한 URI가 그대로 View Name이 된다.
  • Method 를 선언해서 특정 요청만 받을 수 있다.(여러 기능이 있음)
  • 리턴타입이 String일 경우 핸들러에서 ModelAndView인스턴스에 View Name를 담음



Service


BoardService

public interface BoardService {

	public List<BoardDto> selectNoticeBoardList(HttpServletRequest request, BoardDto boardDto) throws Exception;
}

BoardServiceImpl (구현체)

@Service
public class BoardServiceImpl implements BoardService {

	@Autowired //@Resource
	private BoardDao boardDao;

	public List<BoardDto> selectNoticeBoardList(HttpServletRequest request, BoardDto boardDto) throws Exception {
		return boardDao.selectBoardList(boardDto);
	}
}


@Service

  • 스프링에서 비즈니스로직을 처리하는 Service를 의미
  • Service interface 생성
  • ServiceImpl 구현체에 비즈니스 로직 구현
  • @Service(“id”) 형식으로 해당 Service의 이름을 줄 수 있다.
@Service("BoardService")
public class BoardServiceImpl implements BoardService {



DAO (Data Access Object)


  • 실제로 커넥션을 통해 DB에 접근하는 객체
  • Connection Pool로 DB에 연결한 Connection을 통해서 사용자의 여러 요청 처리
  • 사용자의 요청 마다 커넥션을 생성하지 않고, 모든 DB와의 연결을 DAO객체로 효율적으로 관리
  • 커넥션 풀이란?


@Repository
public class BoardDao {
	
	@Autowired //@Resource
	private SqlSessionTemplate sqlSession;

	public List<BoardDto> selectNoticeBoardList(BoardDto boardDto) {
		return sqlSession.selectList("noticeBoard.selectNoticeBoardList", boardDto);
	}
}

@Repository

  • 주로 DB와 직접 연결하는 클래스를 의미



MyBatis


  • 객체 지향 언어인 자바의 관계형 데이터베이스(RDBMS)을 좀 더 쉽게 사용할 수 있게 도와 주는 개발 프레임 워크
  • 프로그램 코드와 SQL 쿼리의 분리로 코드의 간결성 및 유지보수성 향상
  • 복잡한 쿼리나 동적쿼리에 강하다.


context-mapper.xml (Mybatis설정)

<!-- SqlSession setup for MyBatis Database Layer -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:/sqlmapper/${Globals.DbType}/sql-mapper-config.xml" />
    <property name="mapperLocations" value="classpath:/sqlmapper/${Globals.DbType}/mappers/**/*.xml" />
</bean>
    
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSession"/>
</bean>
  • SqlSessionTemplate은 SqlSession을 구현하고 코드에서 SqlSession를 대체하는 역할을 한다. SqlSessionTemplate은 쓰레드에 안전하고 여러개의 DAO나 매퍼에서 공유할수 있다.
  • SqlSession 인터페이스를 구현하는 Singleton형태의 SqlSessionTemplate을 xml방식으로 Spring IoC Container에 빈으로 등록한다.
  • SqlSession 개체가 스프링 빈에 주입될 수 있기 때문이다. DI를 적용할 수 있다.
  • mapperLocations 경로에 있는 SQL구문이 있는 mapper 파일을 등록해준다.


BoardDao.java

// MyBatis SqlSession개체를 사용하기 위해 @Autowired 선언
@Autowired
private SqlSessionTemplate sqlSession;
// SQL Mapper 파일의 namespace와 id값으로 SqlSession로 DB에 접근한다.
sqlSession.selectList("board.selectBoardList", boardDto);



SQL Mapper

  • SQL은 관계형 데이터베이스 관리 시스템(RDBMS)의 데이터를 관리하기 위해 설계된 특수 목적의 프로그래밍 언어이다.
  • MyBatis Mapper는 자바빈즈 객체를 PreparedStatement 파라미터와 ResultSets으로 대신 쉽게 맵핑해주는 역할이다.
  • 관계형 데이터베이스에 접근 할 떄 자바코드와 SQL구문을 나눠 작업하는 부분
  • SQL기반 데이터 매핑
  • 자바 코드에서 SQL 관련부분 제거


board_SQL.xml

<?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="board">

	<select id="selectBoardList" parameterType="neo.apps.board.dto.boardDto" resultType="neo.apps.board.dto.boardDto">
		SELECT
			IDX
			, TITLE
			, CONTENT
			, HIT_CNT
			, REG_ID
			, REG_DATE
		FROM
			T_BOARD
	</select>

</mapper>



DB


  • DataBase를 의미한다. 데이터를 저장하고 관리하는 역할
  • Oracle, MySql, MariaDB, MSSQL 등
  • 스프링 설정에서 Apache DBCP를 사용해서 커넥션 풀 사용
  • 커넥션 풀이란 웹 컨테이너(WAS)가 실행되면서 connection 객체를 미리 pool에 생성
  • MyBatis에서 SqlSession을 이용해 DataSource(데이터베이스 연결정보)에 접근


context-datasource.xml

<!-- Apache commons DBCP -->
<bean id="dataSource-mysql" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${Globals.DriverClassName}"/>
    <property name="url" value="${Globals.Url}" />
    <property name="username" value="${Globals.UserName}"/>
    <property name="password" value="${Globals.Password}"/>
</bean>



DTO (Data Transfer Object)


  • 어플리케이션 계층 간에 데이터를 전달하는 객체이다.
  • DTO는 로직을 갖고 있지 않는 순수한 데이터 객체이다.
  • 일반적으로 getter, setter 메소드만 가진 클래스를 말합니다


BoardDto.java

package neo.apps.board.dto;

public class BoardDto {
	
	public int idx;
	
	public String title;
	
        // ... 생략 ...

	public String getReg_date() {
		return reg_date;
	}

	public void setReg_date(String reg_date) {
		this.reg_date = reg_date;
	}

	@Override
	public String toString() {
		return "NoticeBoardDto [idx=" + idx + ", title=" + title + ", content=" + content + ", hit_cnt=" + hit_cnt
				+ ", reg_id=" + reg_id + ", reg_date=" + reg_date + "]";
	}
}



여기까지가 스프링 MVC 흐름 및 어플리케이션 계층 구조 설명이다. Controller 클래스에서 세부적인 기능이 많다. 그리고 DAO에서 Mapper 찾는 방식도 여러방식이 있고 @Mapper을 사용하는 방식도 있는데 이러한 설명은 추후 단계별로 포스팅 할 예정이다.

하여튼 이번 포스팅에 개인적인 목적은 스프링 MVC패턴 설명에서 접할 수 있는 키워드들과 싱글톤 개념에 접근하는거 이기에 이어서 설명하면




3. Spring MVC 에서 싱글톤

@Controller, @Service, @Repository


  • @Component는 @Controller, @Service, @Repository의 상위 개념이다. 나누는 이유는 스프링에서 프리젠테이션, 서비스, 퍼시스턴스 계층을 명시적으로 구분하기 위함이다.
  • <context:component-scan /> 설정으로 애플리케이션이 구동될 때, @Component, @Controller, @Service, @Repository 선언 된 클래스들을 스캔하여 Spring IoC Container(ApplicationContext)에 자동으로 빈(Bean)으로 등록하게 된다.
  • 의존성 주입(DI)이 가능하기 때문에 하나의 클래스에 다른 클래스가 의존성을 갖지 않게 됩니다.


@Autowired


  • @Autowired를 사용하여 동일한 객체(Singleton)를 사용
  • 스프링이 의존하는 객체를 찾아서 주입한다.(의존성 주입(Injection))
  • 스프링에서 자동적으로 객체를 만들어 사용한다.
  • 생성자, 필드, 메서드 세 곳에 적용이 가능
  • java진영의 @Resource로도 사용가능(둘의 차이는 있음)


bean

  • 스프링에 등록 된 객체
  • Spring IoC Container 의해 등록, 생성, 조회 등 관리 한다.
  • new생성자와 다르게 쓰레드(사용자의 요청)에 따라 객체를 만드는 게 아니라 애플리케이션 구동 시 스프링에서 서버의 힙 메모리에 Singleton으로 생성한다.


Singleton

  • 스프링은 기본적으로 모든 bean을 Singleton으로 생성하여 관리한다.
  • 애플리케이션 구동 시 Spring IoC Container에 bean마다 하나의 객체를 생성하는 것을 의미한다.
  • 스프링에서 하나의 객체를 사용하기 위해 @Autowired사용



위와 같이 스프링 MVC패턴으로 개발 시 사용되는 애너테이션이나 스프링 설정 등이 사용자의 요청에 의해 자주 사용되는 클래스나 DB에 접근하기 위해 커넥션 정보를 매번 new 생성자로 만드는게 아닌 스프링에서 싱글톤으로 만들어 단 하나의 동일한 객체를 공유하여 사용하고 있다는게 포인트이다.

그래서 DAO객체나 MyBatis를 스프링에서 설정으로 싱글톤으로 만들어 @Autowired로 DI해서 사용하고 있다.

스프링 MVC 패턴으로 개발하고 있다면 다 알고 있을 수도 모르고 있을 수도 있는 이번 포스팅은 이렇게 마무리하고 다음 스프링 포스팅은 싱글톤에 대해 포스팅 할 예정이다.



참고