1️⃣ Init() 메소드 구현
NewsController 서블릿 클래스를 생성해주자.
package ch10;
import javax.servlet.*;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/news.nhn")
@MultipartConfig(maxFileSize=1024*1024*2, location="c:/Temp/img")
public class NewsController extends HttpServlet {
private static final long serialVersionUID = 1L;
private NewsDAO dao;
private ServletContext ctx;
//웹리소스 기본경로
private final String START_PAGE = "ch10/newsList.jsp";
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
dao = new NewsDAO();
ctx = getServletContext();
}
}
뉴스 이미지 파일 업로드 처리를 위해 @MultipartConfig를 추가해줬다.
최대 파일크기와 저장위치지를 지정하였다.
저장될 위치의 폴더는 미리 만들어둬야한다.
뷰페이지가 2개뿐이기 때문에 실효성은 없지만
여러기능과 뷰가 포함된 서비스 같은 경우 중간에 시작페이지로 이동할 필요가 있다.
따라서 시작페이지 이동을 상수로 지정해둬야한다.
여기서는 START_PAGE로 지정해놨다.
NewsDAO 사용을 위한 선언과 서버로그메시지 생성을 위해 ServletContext객체를 선언해놨고
init() 메소드에서 초기화를 한다. super.init(config)는 빼먹지 않아야한다.
init() 메소드는 서블릿이 생성되어 컨테이너에 등록될때 한번만 호출한다.
2️⃣ service() 메소드 구현
service() 메소드의 기본구조
service()는 HttpServlet 클래스에 있는 메소드를 오버라이딩한것으로
서블릿이 호출될때마다 실행되는 메소드다.
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String action = req.getParameter("action");
dao = new NewsDAO();
//자바 리플렉션을 사용해 if, switch없이 요청에 따라 구현메소드가 실행.
Method m;
String view = null;
//action 파라미터가 없을시
if(action == null) {
action = "listNews";
}
}
자바 리플렉션 API를 사용해 action에 지정된 이름과 동일한 메소드를 호출하는 구조를 사용했다.
자바 리플렉션으로 메소드 호출
action으로 전달된 이름의 메소드를 자동으로 호출하는 부분이다.
try {
//현재 클래스에서 action이름과 HttpServletRequest를 파라미터로 하는 메소드를 찾음.
m = this.getClass().getMethod(action, HttpServletRequest.class);
//메소드 실행후 리턴값
view = (String)m.invoke(this, req);
}
catch (NoSuchMethodException e) {
e.printStackTrace();
ctx.log("요청 action 없음!");
req.setAttribute("error", "action 파라미터가 잘못되었습니다!");
view = START_PAGE;
}
catch (Exception e) {
e.printStackTrace();
}
this.getClass()는 현재 클래스객체에 대한 참조이다.
getMethod()는 두번째 인자타입을 메소드 인자로 가지는 메소드를 찾아 해당 메소드 객체를 가져온다.
각 메소드는 뷰 페이지를 문자열 형태로 리턴하므로
리턴된 뷰를 view 변수에 넣는다.
메소드가 구현되지않은경우 catch 부분에서 에러 처리된다.
ctx.log()는 톰캣의 콘솔로그 메시지를 남기는 메소드이다.
클라이언트에 전달할 에러메시지는 error 라는 이름으로 request scope에 넣어 시작페이지로 이동하게 했다.
뷰 이동
요청 처리 메소드를 호출한 후 리턴된 뷰페이지로 이동하면된다.
앞에서 했던 것처럼 forward를 쓰면되긴하는데
post 요청을 forward 처리하면 웹브라우저를 새로고침할경우 이전 요청이 한번더 실행되는 문제가 있다 . . .
따라서 컨트롤러에서 페이지 이동시 포워딩과 리디렉션 모두 지원해야한다.
페이지 리디렉션은 response.sendRedirect()를 사용하면 된다.
하지만 요청 처리 메소드는 일괄적으로 뷰 페이지 파일경로를 리턴하게 되어있다.
우리는 일관된 방법으로 리디렉션을 지원하기 위한 규격화된 접근이 필요하다.
스프링에서는 return "redirect:/뷰페이지 경로"와 같이 리턴하면 리디렉션이 이루어지는 구조를 제공한다.
따라서 우리가 만들 컨트롤러에서도 동일한 규격을 지원하도록 아래와 같이
리턴에 따라 포워딩과 리디렉션이 구분되도록 한다.
if(view.startsWith("redirect:/")) {
String rview = view.substring("redirect:/".length()); //redirect:/문자열이후 경로
resp.sendRedirect(rview);
}
else {
RequestDispatcher dispatcher = req.getRequestDispatcher(view);
dispatcher.forward(req, resp); //지정된 뷰로 포워딩. 포워딩시 콘텍스트 경로는 필요X
}
3️⃣ 세부 메소드 구현
addNews() 메소드
뉴스 기사를 등록하기 위한 요청을 처리하는 메소드다.
이미지 파일 저장을 위해 request로부터 Part 객체를 참조하고 getFilename()메소드를 호출해
파일이름을 가져온 다음 서버의 디스크에 저장할것이다.
다음으로 HTML폼으로 입력된 데이터를 acache commons 라이브러리의 BeanUtils를 이용해
매핑한다음 이미지 파일이름을 경로와 함께 저장하고 dao의 addNews() 메소드를 호출해 등록할것이다.
예외가 발생할경우 로그메시지를 출력하고 뷰 페이지에 전달된 에러메시지를 request에 저장할것이다.
그리고 return listNews(request)와 같이 목록을 처리하는 메소드를 호출한 후
전달된 뷰로 이동해야 에러메시지가 목록과 함께 표시된다.
마지막으로 정상 처리된 뉴스 목록으로 이동할수있도록 뷰페이지를 리턴해야 한다.
새로운 뉴스를 post 방식으로 등록요청을 하기 때문에 리턴은 리디렉션 방식으로 처리할것이다.
"redirect:/"이후에 오는 경로부분은 프로젝트 이름 다음에 오는 경로만 작성해야 하니 유의할것.
public String addNews(HttpServletRequest request) {
News n = new News();
try {
//이미지 파일 저장
Part part = request.getPart("file");
String fileName = getFilename(part);
if(fileName != null && !fileName.isEmpty()) {
part.write(fileName);
}
//입력값을 News객체로 매핑
BeanUtils.populate(n, request.getParameterMap());
//이미지 파일 이름을 News 객체에도 매핑
n.setImg("/img/"+fileName);
dao.addNews(n);
}
catch (Exception e) {
e.printStackTrace();
ctx.log("뉴스 추가 과정에서 문제 발생!");
request.setAttribute("error", "뉴스가 정상적으로 등록되지않았습니다!");
return listNews(request);
}
return "redirect:/news.nhn?action=listNews";
}
getAll() 메소드
newsList.jsp에서 뉴스 목록을 보여주기 위한 요청을 처리하는 메소드다.
dao.getAll() 메소드를 호출해 목록을 가져온 후 request에 저장하고 newsList.jsp로
포워딩 되도록 리턴해줄것이다. 그외 예외처리는 위와 동일하다.
public String listNews(HttpServletRequest request) {
List<News> list;
try {
list = dao.getAll();
request.setAttribute("newslist", list);
}
catch (Exception e) {
e.printStackTrace();
ctx.log("뉴스 목록 생성 과정에서 문제 발생!");
request.setAttribute("error", "뉴스 목록이 정상적으로 처리되지 않았습니다!");
}
return "ch10/newsList.jsp";
}
getNews() 메소드
뉴스기사를 클릭했을때 호출하기 위한 요청을 처리하는 메소드이다.
기본 구조는 다른 요청메소드와 동일하다.
선택한 뉴스의 aid값을 파라미터에서 읽어와 dao.getNews()에 전달할것이다.
에러메시지 처리 부분은 addNews()와 같다.
public String getNews(HttpServletRequest request) {
int aid = Integer.parseInt(request.getParameter("aid"));
try {
News n = dao.getNews(aid);
request.setAttribute("news", n);
}
catch (SQLException e) {
e.printStackTrace();
ctx.log("뉴스를 가져오는 과정에서 문제 발생!!!");
request.setAttribute("error", "뉴스를 정상적으로 가져오지 못했습니다!!");
}
return "ch10/newsView.jsp";
}
deleteNews() 메소드
뉴스를 삭제하기 위한 메소드다. getNews()와 동일하게 aid값을 읽어오고
dao.delNews() 메소드에 전달한다.
삭제의 경우에도 리디렉션으로 이동할 수 있도록 할것이다.
public String deleteNews(HttpServletRequest request) {
int aid = Integer.parseInt(request.getParameter("aid"));
try {
dao.delNews(aid);
}
catch (SQLException e) {
e.printStackTrace();
ctx.log("뉴스 삭제 과정에서 문제 발생!!!");
request.setAttribute("error", "뉴스가 정상적으로 삭제되지 않았습니다!");
return listNews(request);
}
return "redirect:/news.nhn?action=listNews";
}
getFileName() 메소드
Path 객체로 전달된 이미지로부터 파일 이름을 추출하기 위한 메소드이다.
여기서는 헤더정보에서 단순히 파일 이름만 가져오지만
경우에 따라 공백처리, 중복파일 이름처리등 추가적인 작업이 필요하다.
private String getFilename(Part part) {
String fileName = null;
//파일 이름이 들어있는 헤더 영역을 가져옴.
String header = part.getHeader("content-disposition");
System.out.println("Header = > "+header);
//파일 이름이 들어있는 속성 부분의 시작위치를 가져와 " " 사이의 값만 가져옴.
int start = header.indexOf("filename=");
fileName = header.substring(start+10, header.length()-1);
ctx.log("파일명 :"+fileName);
return fileName;
}
NewsController 전체 코드
package ch10;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.*;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.List;
@WebServlet("/news.nhn")
@MultipartConfig(maxFileSize=1024*1024*2, location="c:/Temp/img")
public class NewsController extends HttpServlet {
private static final long serialVersionUID = 1L;
private NewsDAO dao;
private ServletContext ctx;
//웹리소스 기본경로
private final String START_PAGE = "ch10/newsList.jsp";
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
dao = new NewsDAO();
ctx = getServletContext();
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String action = req.getParameter("action");
dao = new NewsDAO();
//자바 리플렉션을 사용해 if, switch없이 요청에 따라 구현메소드가 실행.
Method m;
String view = null;
//action 파라미터가 없을시
if(action == null) {
action = "listNews";
}
//리플렉션
try {
//현재 클래스에서 action이름과 HttpServletRequest를 파라미터로 하는 메소드를 찾음.
m = this.getClass().getMethod(action, HttpServletRequest.class);
//메소드 실행후 리턴값
view = (String)m.invoke(this, req);
}
catch (NoSuchMethodException e) {
e.printStackTrace();
ctx.log("요청 action 없음!");
req.setAttribute("error", "action 파라미터가 잘못되었습니다!");
view = START_PAGE;
}
catch (Exception e) {
e.printStackTrace();
}
//Post요청에선 리디렉션으로 이동
if(view.startsWith("redirect:/")) {
String rview = view.substring("redirect:/".length()); //redirect:/문자열이후 경로
resp.sendRedirect(rview);
}
else {
RequestDispatcher dispatcher = req.getRequestDispatcher(view);
dispatcher.forward(req, resp); //지정된 뷰로 포워딩. 포워딩시 콘텍스트 경로는 필요X
}
}
//뉴스 추가
public String addNews(HttpServletRequest request) {
News n = new News();
try {
//이미지 파일 저장
Part part = request.getPart("file");
String fileName = getFilename(part);
if(fileName != null && !fileName.isEmpty()) {
part.write(fileName);
}
//입력값을 News객체로 매핑
BeanUtils.populate(n, request.getParameterMap());
//이미지 파일 이름을 News 객체에도 매핑
n.setImg("/img/"+fileName);
dao.addNews(n);
}
catch (Exception e) {
e.printStackTrace();
ctx.log("뉴스 추가 과정에서 문제 발생!");
request.setAttribute("error", "뉴스가 정상적으로 등록되지않았습니다!");
return listNews(request);
}
return "redirect:/news.nhn?action=listNews";
}
//getAll 메소드
public String listNews(HttpServletRequest request) {
List<News> list;
try {
list = dao.getAll();
request.setAttribute("newslist", list);
}
catch (Exception e) {
e.printStackTrace();
ctx.log("뉴스 목록 생성 과정에서 문제 발생!");
request.setAttribute("error", "뉴스 목록이 정상적으로 처리되지 않았습니다!");
}
return "ch10/newsList.jsp";
}
//뉴스 구하기
public String getNews(HttpServletRequest request) {
int aid = Integer.parseInt(request.getParameter("aid"));
try {
News n = dao.getNews(aid);
request.setAttribute("news", n);
}
catch (SQLException e) {
e.printStackTrace();
ctx.log("뉴스를 가져오는 과정에서 문제 발생!!!");
request.setAttribute("error", "뉴스를 정상적으로 가져오지 못했습니다!!");
}
return "ch10/newsView.jsp";
}
//뉴스 삭제
public String deleteNews(HttpServletRequest request) {
int aid = Integer.parseInt(request.getParameter("aid"));
try {
dao.delNews(aid);
}
catch (SQLException e) {
e.printStackTrace();
ctx.log("뉴스 삭제 과정에서 문제 발생!!!");
request.setAttribute("error", "뉴스가 정상적으로 삭제되지 않았습니다!");
return listNews(request);
}
return "redirect:/news.nhn?action=listNews";
}
//getFilename
private String getFilename(Part part) {
String fileName = null;
//파일 이름이 들어있는 헤더 영역을 가져옴.
String header = part.getHeader("content-disposition");
System.out.println("Header = > "+header);
//파일 이름이 들어있는 속성 부분의 시작위치를 가져와 " " 사이의 값만 가져옴.
int start = header.indexOf("filename=");
fileName = header.substring(start+10, header.length()-1);
ctx.log("파일명 :"+fileName);
return fileName;
}
}
[메인으로 돌아가기]
'Java Spring > 책공부 1 (JSP와 스프링)' 카테고리의 다른 글
21. 리스너 (0) | 2022.07.15 |
---|---|
20. 뷰 구현 (0) | 2022.07.15 |
18. 모델 구현 (0) | 2022.07.14 |
17. 뉴스기사 관리 서비스 설계 (0) | 2022.07.14 |
16. JDBC 종합 : 학생정보 조회와 등록 (0) | 2022.07.13 |