티스토리 뷰
지금까지는 Spring 게시판 예제를 맛보기로 구성해보며, 전체적인 구초를 한 번 훑었다. 그 마무리 단계로, 완성한 게시판 예제를 한번 더 단계별로 확인해보면서 Spring 구동의 순서와 구성 순서 등을 확인하려고 한다.
그럼 바로 한번 리뷰해보자.
1. 요청을 처리할 DispatcherServlet을 사용하기 위해 우선적으로 web.xml 문서에 Servlet 파일들을 등록
◎web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>spring_simple_board</display-name>
<!-- 컨트롤러역할을 하는 서블릿의 이름 및 요청경로를 지정 -->
<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>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
이때, 주의할 점은 톰캣 서버가 구동되면서 WEB-INF => web.xml => board-servlet.xml의 순서로 App의 정보를 읽기 때문에,
<servlet><servlet-name>board</servlet-name>~~~ </servlet>
등록해 놓은 서블릿 이름이 board-servlet.xml 파일명과 다를 경우 HandlerMapping 불가능해 오류가 난다.
2. board-servlet.xml 문서 작성
컨트롤러의 맵핑, DAO 등록 등의 역할을 수행해주는 servlet.xml 문서르 작성해야한다.
그 방법은 이전 포스팅에서 자세하게 다루었으니 생략하겠다.
servlet(spring).xml 작성: https://til-choonham.tistory.com/168
3. Connection 객체 생성을 위한 Context.xml 작성
META-INF => Context.xml : JNDI 방식으로 데이터베이스에 접근하기 위해 만든 xml 문서
- JDBC 드라이버를 이용 : 접속할 때마다 Connection 객체를 생성 및 해제...
따라서 동시 접속자 수가 많을 경우, 퍼포먼스 저하!!!!
- 스프링이 제공하는 DataSource 를 이용한 접근 방식 : 미리 Connection 객체를 여러 개 생성 후, 풀에 담아 놓고 사용
예) 100명이 접속할 경우,
JDBC 드라이버 : 100번 요청이 있으면 Connection 객체를 100번 생성.. 이후 재활용 불가능하기 때문에 DS를 사용한 다.
DataSource : 미리 100개를 생성 . 1000명이 요청 시, 미리 생성된 100 개의 Connection 객체 get!!!
사용자가 일을 마치면 해당 Connection 객체를 다시 pool 에 반환..
자동으로 관리해줌... (Connection 객체 꺼내고 / 반환 )
◎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. DAO -> 생성자 구성
DAO를 구성하면서 가장 먼저 해줘야 하는 것은 3번에서 작성한 Context.xml을 맵핑하여 DS 객체로 Connection 객체를 생성하는 것이다.
◎BoardDAO.java
package board.dao;
/** Connection, PreparedStatement, 쿼리 실행관련**/
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
//-------------------------------------------------------
/** Context(Interface이다.), InitialContext 객체 **/
// lookup(찾고자하는 이름(JNDI명)) -> 탐색기에서 검색하는 것과 같은 느낌
import javax.naming.Context;
import javax.naming.InitialContext;
//---------------------------------------------------
/** 추가 (JNDI 방식) **/
// DataSource 객체 -> getConnection()
import javax.sql.DataSource;
import board.command.BoardCommand;
import board.dto.BoardDTO;
public class BoardDAO {
DataSource ds;
// 생성자 : DataSource 얻기 : InitialContext와 JNDI명
public BoardDAO() {
try {
// InitialContext ctx=new InitialContext(); 이것도 가능.
Context ctx = new InitialContext();
// lookup("java:comp/env/찾고자하는 JNDI이름")
ds = (DataSource) ctx.lookup("java:comp/env/jdbc/orcl");
System.out.println("ds : " + ds);
} catch (Exception e) {
e.printStackTrace();
}
}
}
5. VO 구성
이번 예제에서 DTO는 다음과 같이 구성되어 있다.
board.command => BoardCommand.java (글쓰기 / 수정 .. 사용자 입력값 => 컨트롤러에게 전달 전용 VO)
board.dto => BoardDTO.java (데이터베이스와 컨트롤러 전달 전용 VO)
◎BoardCommand.java
package board.command;
//사용자로부터 순수 입력받는 값만 처리해주는 Class
public class BoardCommand {
private String author, title, content;
public BoardCommand() {
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
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;
}
}
◎BoardDTO.java
package board.dto;
public class BoardDTO {
private int num;
private String author, title, content, date;
private int readcnt;
public BoardDTO() {
// TODO Auto-generated constructor stub
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
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 getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public int getReadcnt() {
return readcnt;
}
public void setReadcnt(int readcnt) {
this.readcnt = readcnt;
}
}
VO 역할 구분 이유 :
사용자 입력값은 5개 / 테이블의 필드 10 개중 사용자 입력 값은 5개, 기본값 설정이 되어 있는 필드 5개 일 경우, 기존 VO는 테이블의 전체 필드에 대한 멤버변수(private)를 선언. 그에 대한 getter /setter 가 필요
만약에 동시접속자가 100,000일 경우 불필요한 5개 변수에 대한 메모리가 낭비된다.
따라서 사용자 입력값을 저장할 VO 객체와 데이터베이스의 결과 값을 저장할 VO 객체를 분리하여 사용하는 것이 합리적이다.
6. DAO에 각 기능에 대한 메서드 정의
DAO의 생성자를 제외한 메서드는 기존에 사용하던 방식과 마찬가지로 구성하면 된다. 다만, Connection 객체는 ds 맴버 변수를 이용하여 얻어야 한다.
◎BoardDAO.java
/* 글 목록 조회 메서드 */
public ArrayList<BoardDTO> list() {
ArrayList<BoardDTO> list = new ArrayList<BoardDTO>();
try {
String sql = "SELECT * FROM springboard ORDER BY num desc";
Connection con = ds.getConnection();
// Connection con = pool.getConnection();
PreparedStatement stmt = con.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
BoardDTO data = new BoardDTO();
data.setNum(rs.getInt("num"));
data.setAuthor(rs.getString("author"));
data.setTitle(rs.getString("title"));
data.setContent(rs.getString("content"));
data.setDate(rs.getString("writeday"));
data.setReadcnt(rs.getInt("readcnt"));
list.add(data);
} // end while
rs.close();
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
7. 각 기능에 대한 컨트롤러 정의 & 등록
각 컨트롤러를 다음과 같이 정의하고, servlet.xml에 등록해야 한다.
비지니스 로직을 처리하는 각 컨트롤러 : board.controller
1. 목록 처리 컨트롤러 : ListActionController implements Controller
2. 쓰기화면 처리 컨트롤러 : 스프링이 제공하는 ParameterizableViewController (화면만 응답처리할 경우)
@Override
public ModelAndView handleRequestInternal(~~~~) ~~~~{ }
handleRequestInternal( ) 메서드의 역할 :
viewName 속성값을 이용하여
ModelAndView mav = new ModelAndView();
mav.setViewName("viewName 속성값");
mav 객체를 반환
3. ParameterizableViewController 등록 시에는 반드시 viewName 속성에 출혁화면 파일명만 설정
◎board-servlet.xml
<!-- 1. 글 목록 보기 -->
<bean name="/list.do" class="board.controller.ListActionController">
<property name="dao">
<ref bean="boardDAO"/>
</property>
</bean>
controller)
◎ListActionController.java
package board.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 board.dao.BoardDAO;
import board.dto.BoardDTO;
//Controller를 상속받는 이유 : 요청을 받아서 처리하기 위함.
public class ListActionController implements Controller {
BoardDAO dao; // BoardDAO dao = new BoardDAO();
public void setDao(BoardDAO dao) {
this.dao = dao;
System.out.println("setDao()호출됨(dao) : "+dao);
}
@Override
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
System.out.println("ListActionController 실행됨!");
ArrayList<BoardDTO> list = dao.list();
ModelAndView mav = new ModelAndView();
mav.setViewName("list"); // list.jsp
//request.setAttribute("list",list);
mav.addObject("list", list);
return mav;
}
}
==============================================================
3. 저장 처리 컨트롤러 : WriteActionController extends AbstractCommandController
1. implements Controller : 브라우저로부터 데이터가 파라미터로 전달될 경우
@Override
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) {
}
2. extends AbstractCommandController : 브라우저의 입력 파라미터 명과 해당 값을 VO 객체와 자동 매핑 시킨후
컨트롤러에게 자동으로 전할 경우,,,
@Override
protected ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object command, <=== 자동 매핑된 VO 객체가 전달 되는 매개변수
BindException error)
자동 매핑된 VO 객체를 Object command에 자동 전달 하려면
해당 컨트롤러를 등록할 때 미리 설정해야 한다..
◎board-servlet.xml
<!-- 3. 글쓰기(글 수정하기와 거의 유사) DB연결해서-->
<bean name="/write.do" class="board.controller.WriteActionController">
<property name="dao">
<ref bean="boardDAO"/>
</property>
<property name="commandClass" value="board.command.BoardCommand"/>
</bean>
controller)
◎WriteActionController.java
package board.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 board.command.BoardCommand;
import board.dao.BoardDAO;
// AbstractCommandController 를 상속받은 이유 :
// ModelAndView handle~~() 의 매개 변수가 Controller interface와 다르기 때문
/*
AbstractCommandController의 콜백메서드인 handle() 가 전달받는 객체
handle() 콜백메서드의 매개변수와 설정 값
1. Request : 요청객체
2. Response : 응답객체
3. Object : 입력받은 값을 저장하는 객체
4. BindException : 사용자로부터 값을 입력 시, 에러가 발생하면 처리해주는 class
*/
public class WriteActionController extends AbstractCommandController {
BoardDAO dao; // BoardDAO dao = new BoardDAO();
public void setDao(BoardDAO dao) {
this.dao = dao;
System.out.println("WriteActionController setDao()호출됨(dao) : " + dao);
}
// 매개변수를 알기 쉽게 변환
@Override
protected ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException error) throws Exception {
request.setCharacterEncoding("UTF-8");
// spring 방식
// BoardCommnad 는 상속받은 부모 class(AbstractCommandController)의
// "commandClass" 속성에 의해 자동 전달(주입).
BoardCommand data = (BoardCommand)command;
dao.write(data);
/*
// 기존 Model2(MVC) 방식
String author = request.getParameter("author");
String content =request.getParameter("content");
String title = request.getParameter("title");
dao.write(author, title, content);
*/
// 기존 Model2(MVC) 방식
// response.sendRedirect("list.jsp");
// spring 방식
return new ModelAndView("redirect:/list.do");
}
}
/*
System.out.println("spring 방식");
//ModelAndView mav = new ModelAndView();
//mav.setViewName("list");
// 또는
ModelAndView mav = new ModelAndView("list");
// 위에 ModelAndView() 안에 넣음으로서
// mav.setViewName("list"); 생략이 가능하다.
System.out.println("spring 방식 return mav");
return mav;
//
// 또는
//위의 주석 문을 한줄로 처리 가능하다
*/
8. 프론트 구성
jsp파일은 기존 request 파라미터를 사용하여 구성한 것과 동일하게 구성하면 된다.
예제의 GitHub:
'[JAVA] > Spring' 카테고리의 다른 글
[Spring] Spring DI 인자 전달 (0) | 2021.06.14 |
---|---|
[Spring] 스프링의 DI(Dependency Injection) 의존성 주입 (0) | 2021.06.14 |
[Spring] 게시판 예제를 통한 Spring 구조 정리 (0) | 2021.06.10 |
[Spring] JNDI 을 이용한 DB 연결하기 (0) | 2021.06.10 |
[Spring] spring.xml 문서 작성 (0) | 2021.06.10 |
- react
- 인천 구월동 맛집
- react-native
- 인천 구월동 이탈리안 맛집
- AsyncStorage
- 맛집
- await
- redux
- Promise
- 정보보안기사 #실기 #정리
- Async
- 이탈리안 레스토랑
- 파니노구스토
- javascript
- redux-thunk
- Total
- Today
- Yesterday