[Spring] Spring MVC 패턴
in Spring on Spring 포스팅
스프링이 아닌 개발자 입장에서(?) 개발하는 Spring MVC 패턴은?
1. Spring MVC란?
Model-View-Controller의 약자이다.
- Model: 어플리케이션 계층의 정보, 데이터 의미
- View: 화면 출력 로직을 담당
- Controller: Model과 View의 연결하는 제어 로직을 담당
2. Spring MVC 흐름
- 최대한 짧게 포스팅할려고 해도 노력했다. 세부적인 내용이나 설정은 빼고 전체적인 흐름에 대한 포스팅이다. 해당 글을 읽기 전에 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 패턴으로 개발하고 있다면 다 알고 있을 수도 모르고 있을 수도 있는 이번 포스팅은 이렇게 마무리하고 다음 스프링 포스팅은 싱글톤에 대해 포스팅 할 예정이다.
참고
- http://www.mybatis.org/mybatis-3/ko/index.html
- http://www.mybatis.org/spring/ko/sqlsession.html
- https://nachwon.github.io/sql-1-intro/