티스토리 뷰

스프링의 기본적인 실행 구조를 간단한 게시판 서비스를 구성하며 순차적으로 알아보자.


1.

우선 프로젝트를 만들고 나서 요청에 따른 controller를 자동 호출하기 위한 DispatcherServlet을 web.xml 문서에 추가해줘야 한다.

 

◎WEB-INF.web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
  <display-name>spring_simple_board</display-name>
  
  <!--  DispatcherServlet 등록 -->
  <servlet>
  	<servlet-name>board</servlet-name> 
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>board</servlet-name>
  	<url-pattern>*.do</url-pattern> <!--  모든 요청사항 뒤에 .do가 붙으면 DispatcherServlet을 사용해! -->
  </servlet-mapping>
  
  
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

 이때, servlet의 이름은 dispatcher가 자동으로 찾기 때문에 위와 같이 이름을 작성했다면, servlet.xml은 반드시 

board-servlet.xml 과 같이 생성해야 한다.


2. 

board-servlet.xml 파일을 생성하고, 해당 문서가 HandlerMapping과 ViewResolver 기능을 수행할 수 있도록 각각 등록해줘야 한다.

 

◎WEB-INF.board-servlet.xml

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

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- 요청에 따른 컨트롤러 반환 담당 : /list.do  -->
	<bean id="defaultHandlerMapping"  class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
	
	<!-- viewResolver(위치, 이동할 페이지 지정) : "list" -->
	<bean id="viewResolover"  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	    <property name="viewClass"   value="org.springframework.web.servlet.view.InternalResourceView"/>
	    <property name="prefix" value="/"/> <!--  "/list" -->
	    <property name="suffix" value=".jsp"/> <!--  "/list.jsp" -->
	</bean>
	
</beans>

3. 

JNDI를 이용해서 DB를 연동해야 하기 때문에, 해당 DB의 정보를 DAO에서 사용할 수 있도록 Context.xml 파일을 작성해야 한다.

 

◎META-INF.Context.xml

<?xml version="1.0" encoding="UTF-8"?>
<Context>
	<Resource name="jdbc/orcl" 
                   auth="container"
                   type="javax.sql.DataSource"
                   username="SPRING_EX"
                   password="1234"
                   driverClassName="oracle.jdbc.driver.OracleDriver"
                   factory="org.apache.commons.dbcp.BasicDataSourceFactory"
                   url="jdbc:oracle:thin:@localhost:1521:XE"
                   maxActive="20"
                   maxIdle="10">
    </Resource>
</Context>

4. 

이제 생성한 Context.xml 파일을 이용하여 Connection 객체를 생성하여 이용하기 위한 DAO를 작성한다.

 

전체적인 흐름을 알아보는 예제이기 때문에 상세 메서드 & DTO 는 기술하지 않겠다.

 

중요한 점은 DB를 제어하는 각각의 메서드에서 Connection 객체를 생성자에서 선언한 ds 맴버 변수를 이용하여 생성한다는 점이다.

 

◎BoardDAO.java

package com.choonham.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;

import javax.naming.InitialContext; // InitialContext implements Context
import javax.naming.NamingException;
import javax.sql.DataSource;

import com.choonham.command.BoardCommand;
import com.choonham.dto.BoardDTO;

public class BoardDAO {

	DataSource ds;
	//DataSource 객체는 getConnection() 과 동일한 역할을 담당한다.
	public BoardDAO() {
		// Context 객체 얻기 : Context.xml 문서에서 name = "jdbc/orcl" 속성 값을 추출
		// 방법 : InitialContext 객체를 이용(해당 객체가 가지고 있는 lookUp() 을 통해 name 속성 값을 추출할 수 있다.
		//  단, jdbc/orcl을 찾기 위해서는 java:comp/evn/jdbc/orcl 와 같이 적어야 한다.
		try {
			InitialContext ctv = new InitialContext();
			
			ds = (DataSource)ctv.lookup("java:comp/env/jdbc/orcl");
		} catch (NamingException e) {
			e.printStackTrace();
		}
	
	}
	
	/** 글 목록 조회 **/
	public ArrayList<BoardDTO> list() {
		//생략
	}
	
	/** 새 글에 대한 글 번호를 위한 메서드 **/
	public int getNewNum() {
		//생략
	}
	
	/** 글 저장 메서드 **/
	public void write(BoardCommand data) {
		//생략
	}
	

}

5. 

작성한 DAO를 board-servlet.xml 에 등록해야지만 사용할 수 있기 때문에 등록해준다.

 

◎WEB-INF.board-servlet.xml

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

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- 요청에 따른 컨트롤러 반환 담당 : /list.do  -->
	<bean id="defaultHandlerMapping"  class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
	
	<!--  DB 접속 : JNDI 방식 -->
	<bean id = "boardDAO" class = "com.choonham.dao.BoardDAO"></bean> 
    
	<!-- viewResolver(위치, 이동할 페이지 지정) : "list" -->
	<bean id="viewResolover"  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	    <property name="viewClass"   value="org.springframework.web.servlet.view.InternalResourceView"/>
	    <property name="prefix" value="/"/> <!--  "/list" -->
	    <property name="suffix" value=".jsp"/> <!--  "/list.jsp" -->
	</bean>
	
</beans>

6.

이후 실제 기능을 구현할 컨트롤러를 작성해줘야 한다.

 

Spring 프레임워크에서 컨트롤러가 요청을 받아 자동적으로 어떤 메서드를 수행하게 하기 위해서는 반드시 

ModelAndView() 메서드를 사용해야하는데, 이는 org.springframework.web.servlet.mvc.Controller를 구현 받거나 

spring프레임워크에서 제공하는 다른 컨트롤러를 상속받아야 한다.

 

◎ListActionController.java 는 아래와 같이 따로 인터페이스를 구현받아 작성했다.

◎ListActionController.java

package com.choonham.controller;

import java.util.ArrayList;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import com.choonham.dao.BoardDAO;
import com.choonham.dto.BoardDTO;

public class ListActionController implements Controller {
	
	private BoardDAO dao;

	public void setDao(BoardDAO dao) {
		this.dao = dao;
		System.out.println("setDao() 호출 : " + dao);
	}
	
	public ListActionController() {
		// TODO Auto-generated constructor stub
	}
	
	/**  요청사항이 전달되면 자동 호출되는 메서드 **/
	@Override
	public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
		System.out.println("ListActionController의 handleRequest() 자동호출 ");
		
		ArrayList<BoardDTO> list = dao.list();
		ModelAndView mav = new ModelAndView();
		mav.setViewName("list"); // list.jsp 를 의미
		mav.addObject("list", list); // request.setAttribute("list", list); 개념
		
		return mav;
	}

}

 

또한 글 쓰기 기능을 담당하는 ◎WriteActionController.java 의 경우, 아래와 같이 AbstractCommandController 를 상속 받았다.

◎WriteActionController.java

package com.choonham.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractCommandController;

import com.choonham.command.BoardCommand;
import com.choonham.dao.BoardDAO;

/**  AbstractCommondController를 상속 받은 이유
 *  사용자가 입력한 값을 외부로부터 자동으로 전달받기 위해
 *  요청시 자동으로 호출되는 handle()의 매개 변수의 역할
 *   HttpServletRequest : request 객체 (요청 객체)
 *   HttpServletResponse : response 객체 (응답 객체)
 *   Object : 입력받은 값을 저장하는 객체
 *   BindException : 전달 받을 값에 대한 오류를 예외처리
 * 
 *   따라서 이 컨트롤러 등록시, 입력 받은 값을 저장할 수 있는 객체가 필요하고, 
 *   객체는 commandClass 변수를 통해 자동 전달한다.
 * **/

public class WriteActionController extends AbstractCommandController {

	private BoardDAO dao;
	
	public void setDao(BoardDAO dao) {
		this.dao = dao;
		System.out.print("dao 호출" + dao);
	}

	@Override
	protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object command, BindException error)
			throws Exception {
		BoardCommand data = (BoardCommand) command;
		// BoardDAO의 write() 메서드에게 data 객체를 전달
		
		dao.write(data);
		
		// 기존 방식
		// String t = request.getParameter("t");
		
		// Spring 방식 : ModelAndView객체 사용
		ModelAndView mav = new ModelAndView();
		mav.setViewName("redirect:/list.do");
		return mav;
	}

}

7. 

마지막으로 생성한 각각의 컨트롤러들을 board-servlet.xml에 등록해주면 해당 요청이 들어왔을 때, 자동으로 파라미터를 맵핑하고, 기능을 수행하는 구조가 완성이 된다.

 

◎WEB-INF.board-servlet.xml

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

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- 요청에 따른 컨트롤러 반환 담당 : /list.do  -->
	<bean id="defaultHandlerMapping"  class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
	
	<!--  DB 접속 : JNDI 방식 -->
	<bean id = "boardDAO" class = "com.choonham.dao.BoardDAO"></bean> 
	
	<!-- 글 목록 보기 컨트롤러 등록 : private dao => setDao(boardDAO) -->
	<bean name = "/list.do" class = "com.choonham.controller.ListActionController">
		<property name = "dao">
			<ref bean = "boardDAO" />
		</property>
	</bean>
	
	<!-- 글 쓰기 화면 처리 컨트롤러 등록 : 스프링이 제공 -->
	<bean name = "/writeui.do" class="org.springframework.web.servlet.mvc.ParameterizableViewController"> <!--  파라미터 없이 뷰를 내보내주는 spring 클래스 -->
		<property name = "viewName" value = "write"></property>
	</bean>
	
	<!--  글 저장 처리 컨트롤러 등록  -->
	<bean name = "/write.do" class = "com.choonham.controller.WriteActionController">
		<property name = "dao">
			<ref bean = "boardDAO"/>
		</property>
		<property name = "commandClass" value = "com.choonham.command.BoardCommand"></property>
	</bean>
	
	
	<!-- viewResolver(위치, 이동할 페이지 지정) : "list" -->
	<bean id="viewResolover"  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	    <property name="viewClass"   value="org.springframework.web.servlet.view.InternalResourceView"/>
	    <property name="prefix" value="/"/> <!--  "/list" -->
	    <property name="suffix" value=".jsp"/> <!--  "/list.jsp" -->
	</bean>
	
</beans>

화면 구성은 다음과 같이 간단하게 구성했다.

 

◎index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
		response.sendRedirect("http://localhost:9000/spring_simple_board/list.do");
%>

◎list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import = "java.util.ArrayList, com.choonham.dto.BoardDTO" %>
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>list</title>
</head>
<body>
	<table border = "1">
		<tr>
			<td colspan = "5">게시판 목록 &nbsp;&nbsp;&nbsp;
				<a href = "writeui.do">글 쓰기</a>
			</td>
		</tr>
		
		<tr>
			<th>번호</th>
			<th>제목</th>
			<th>작성자</th>
			<th>작성일</th>
			<th>조회수</th>
		</tr>
		
		<!--  DispatcherServlet은 Controller 로부터 ModelAndView 객체를 받고 
				그 중 model 에 해당하는 객체를 list.jsp의 request에게 전달
				따라서 list.jsp 는 request의 attribute 영역에서 해당 객체를 추출하여 사용 가능
		 -->
		 
		 <%
		 	ArrayList<BoardDTO> list = (ArrayList)request.getAttribute("list");
		 	if(list != null) {
		 		for(BoardDTO dto:list){		
		 			int num = dto.getNum();
		 			String title = dto.getTitle();
		 			String author = dto.getAuthor();
		 			String writeday = dto.getDate();
		 			int readcnt = dto.getReadcnt();
		 %>
		 	<tr>
		 		<td><%= num %></td>
		 		<td><a href = "retrieve.do?num=<%= num %>"><%= title %></a></td>
		 		<td><%= author %></td>
		 		<td><%= writeday.substring(0, 10) %></td>
		 		<td><%= readcnt %></td>
		 	</tr>
		 <%
		 		}
			 } 
		 %>
		
		
		
	</table>
</body>
</html>

◎write.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>write.jsp</title>
</head>
<body>
	<form action = "write.do">
		<table>
			<tr>
				<td>제목</td>
				<td>
					<input type = "text" name = "title" />
				</td>
			</tr>
			<tr>
				<td>작성자</td>
				<td>
					<input type = "text" name = "author" />
				</td>
			</tr>
			
			<tr>
				<td>내용</td>
				<td>
					<textarea rows = "5" cols = "30" name = "content"></textarea>
				</td>
			</tr>
			
			<tr>
				<td colspan = "2">
					<input type = "submit" value = "저장" />
				</td>
			</tr>
		</table>
	</form>
</body>
</html>

결과 페이지는 다음과 같이 정상적으로 작동하는 걸 확인할 수 있다.

 

 

데이터의 흐름을 눈으로 따라가기가 지금은 좀 힘들지만, 대부분의 업무를 자동으로 맵핑하여 처리해주는 점이 익숙해지면 굉장히 편리할 거 같다. 

 

... 일단 디버깅이 굉장히 어려워진 거 같으니, 최대한 집중해서 코드를 작성해야할 거 같다. 

 

 

Comments