구현 기능
- 피드 목록
- 피드 클릭해 접속해 내용 읽기 (READ)
- 피드 생성 (CREATE)
- 피드 수정 (UPDATE)
- 피드 삭제 (DELETE)
프런트 컨트롤러 : DispaterServlet
프런트 컨트롤러는 클라이언트에게 직접 요청을 받아 그 요청을 일부 처리하고, 페이지 컨트롤러에게 넘긴다. 이때 처리하는 '일부'의 요청은 모든 컨트롤러가 공통으로 처리하는 것들이다.
프런트 컨트롤러는 Request 요청을 받아 model 객체에 담아서 페이지 컨트롤러에게 넘긴다. 또한 페이지 컨트롤러가 작업을 수행하고, 띄울 뷰(.jsp)의 경로(viewUrl)를 반환하면 일단 model 객체의 담겨진 데이터를 response 객체의 attribute로 전달하고, 해당 jsp 파일을 인클루딩 한다.
@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {
@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html; charset=UTF-8");
request.setCharacterEncoding("UTF-8");
String servletPath = request.getServletPath();
HashMap<String, Object> model = new HashMap<>();
HttpSession session = request.getSession();
ServletContext sc = request.getServletContext();
MySqlFeedDao feedDao = (MySqlFeedDao) sc.getAttribute("peedDao");
try {
Controller pageController;
if (servletPath.equals("/auth/login.do")) {
pageController = new LogInController();
} else if (servletPath.equals("/feed/main.do")) {
pageController = new FeedListController().setFeedDao(feedDao);
} else if (servletPath.equals("/feed/content.do")) {
if (request.getParameter("no") != null) {
model.put("no", Integer.parseInt(request.getParameter("no")));
pageController = new FeedContentController().setFeedDao(feedDao);
} else {
throw new ServletException();
}
} else if (servletPath.equals("/feed/add.do")) {
User user;
if(session.getAttribute("user") != null) {
user = (User) session.getAttribute("user");
} else {
user = new User().setName("anonymous");
}
if (request.getParameter("title") != null) {
Feed feed = new Feed()
.setTitle(request.getParameter("title"))
.setContent(request.getParameter("content"))
.setWriter(user.getName());
model.put("feed", feed);
}
pageController = new FeedAddController().setFeedDao(feedDao);
} else if (servletPath.equals("/feed/edit.do")) {
int no = Integer.parseInt(request.getParameter("no"));
if (request.getParameter("title") == null) {
model.put("no", no);
} else {
Feed feed = new Feed()
.setNo(no)
.setTitle(request.getParameter("title"))
.setContent(request.getParameter("content"));
model.put("editFeed", feed);
}
pageController = new FeedEditController().setFeedDao(feedDao);
} else if (servletPath.equals("/feed/delete.do")) {
if (request.getParameter("no") != null) {
model.put("no", Integer.parseInt(request.getParameter("no")));
pageController = new FeedDeleteController().setFeedDao(feedDao);
} else {
throw new ServletException();
}
}
else {
// TODO: error handling
pageController = new LogInController();
}
String viewUrl = pageController.execute(model);
for (String key : model.keySet()) {
request.setAttribute(key, model.get(key));
}
if (viewUrl.startsWith("redirect:")) {
response.sendRedirect(viewUrl.substring(9));
} else {
RequestDispatcher rd = request.getRequestDispatcher(viewUrl);
rd.include(request, response);
}
} catch (Exception e) {
}
}
}
ServletPath는 ...do 형식으로 해당 형식의 경로는 모두 DispaterServlet이 처리한다.
ServletPath를 전달 받은 DispatcherServlet은 세부 경로에 따라 if/else 문을 통해 각 페이지별로 필요한 데이터를 만들어 model 객체에 담는다. 그리고 페이지 컨트롤러의 execute() 메서드를 호출한다. execute() 메서드가 실행되면 결과 값으로 화면의 띄울 뷰(.jsp)의 경로를 리턴하고, DispatcherServlet은 뷰를 인크루딩한다. 만약 경로의 형식이 "ridrect:..." 인경우, 리다이랙션을 한다.
피드 목록
위 사진은 피드 목록 화면과 피드 목록 구현 다이아그램이다. ServletPath가 "feed/main.do"인 경우 프런트 컨트롤러는 FeedListController를 생성하고, execute() 메서드를 실행한다. FeedListController는 DAO를 필드로 받고, selectList() 메서드를 호출해 DB에서 피드목록을 List 타입으로 가져온다. 피드목록을 모델에 저장 후 FeedList.jsp의 경로를 프런트 컨트롤러에 리턴한다. 프런트 컨트롤러는 경로를 통해 해당 뷰를 인클루딩하고, Model에 담겨있던 피드 목록을 Request의 attribute에 전달한다. FeedList는 request 객체의 값을 이용해 피드 목록을 화면에 띄운다.
DispatcherServlet.java
else if (servletPath.equals("/feed/main.do")) {
pageController = new FeedListController().setFeedDao(feedDao);
}
FeedListController.java
public class FeedListController implements Controller {
MySqlFeedDao feedDao;
public FeedListController setFeedDao(MySqlFeedDao feedDao) {
this.feedDao = feedDao;
return this;
}
@Override
public String execute(Map<String, Object> model) throws Exception {
model.put("feeds", feedDao.selectList());
return "/feed/FeedList.jsp";
}
}
피드 생성 (CREATE)
피드 생성과 관련하여 클라이언트는 두가지 요청을 할 수 있다.
- 피드 생성 폼 요청 (GET 요청)
- 피드 생성 폼 작성을 완료 후 피드 생성 요청 (POST요청)
DistpathcerServlet은 service() 메서드를 통해 클라이언트의 요청을 처리하므로 GET 요청과 POST 요청을 구분할 수 없다. 그러므로 클라이언트가 보낸 request 객체에 피드 생성에 필요한 데이터가 있는지 여부에 따라, GET 요청 / POST 요청을 처리한다.
먼저 GET 요청 처리를 위한 컨트롤러의 작업 처리 다이어그램이다.
생성 폼을 요청하는 경우, 추가적인 파라미터는 없으므로 request 객체의 데이터를 model 객체로 옮겨줄 필요는 없다. DispatcherServlet은 FeedAddController를 생성해 execute()를 호출하고, FeedAddController는 view의 url을 리턴하고, DispatcherServlet은 해당 뷰를 인클루딩한다.
클라이언트가 생성 폼을 작성완료 후 '등록' 버튼을 클릭할 경우, POST 요청을 프런트 컨트롤러에게 보낸다. 이경우의 작업 처리 다이어그램은 다음과 같다.
피드 생성을 요청할 경우 request 객체에는 피드 생성을 위한 피드 제목, 피드 내용 데이터가 담겨있다. 이 데이터를 이용해 Feed 객체를 생성하고 model 객체에 담는다. 그 후 FeedAddController를 생성해 execute()를 호출한다. FeedAddController는 Model객체에서 Feed 객체를 가져와 FeedDao의 insert() 메서드를 호출해 DB에 해당 피드를 추가한다. 그후 main.do(피드 리시트 창)을 리다이랙션 한다.
DispatcherServlet.java
else if (servletPath.equals("/feed/add.do")) {
User user;
if(session.getAttribute("user") != null) {
user = (User) session.getAttribute("user");
} else {
user = new User().setName("anonymous");
}
if (request.getParameter("title") != null) {
Feed feed = new Feed()
.setTitle(request.getParameter("title"))
.setContent(request.getParameter("content"))
.setWriter(user.getName());
model.put("feed", feed);
}
pageController = new FeedAddController().setFeedDao(feedDao);
}
실제 코드에서는 request 객체안에 key 값이 "title" (피드 제목) 인 파라미터가 있는 경우, model 객체에 해당 피드를 생성해, 추가한다.
FeedAddController.java
public class FeedAddController implements Controller {
MySqlFeedDao feedDao;
public FeedAddController setFeedDao(MySqlFeedDao feedDao) {
this.feedDao = feedDao; return this;
}
@Override
public String execute(Map<String, Object> model) throws Exception {
if (model.get("feed") == null) {
return "/feed/FeedAddForm.jsp";
} else {
Feed feed = (Feed) model.get("feed");
feedDao.insert(feed);
return "redirect:main.do";
}
}
}
실제 코드에서는, model 객체에 feed 키의 값이 있는지 여부에 따라 피드 생성 폼을 요청하거나, Dao의 메서드를 호출하여 피드를 추가한다.
피드 읽기 (READ)
피드 목록에서 피드의 제목을 클릭하면 해당 피드의 내용이 화면에 나온다.
다음은 피드 컨텐츠 읽기를 클라이언트가 요청했을때의 프로세스이다.
클라이언트는 GET요청을 통해 피드 컨텐츠 읽기 작업을 요청한다. 프런트 컨트롤러(DispatcherServlet)은 Request 객체에 담겨있던 피드 고유 번호(no)를 Model 객체로 옮긴다. 그후 FeedContentController를 생성 후 execute 메서드를 호출한다. FeedContentController는 모델 객체의 담겨있던 no를 받아와 FeedDao의 SeletOne() 메서드를 호출 피드 고유 번호에 해당하는 피드 객체를 리턴 받는다. 리턴 받은 피드 객체를 다시 model 객체에 저장한 후, viewUrl(/feed/FeedContent.jsp)을 리턴한다. 다시 DispatcherServlet은 model 객체에 있던 데이터를 Response 객체의attribute로 전달하고, 뷰를 인클루딩 한다. 뷰는 response 객체에서 피드 객체를 받아와 피드 내용을 보여준다.
DispatcherServlet.java
else if (servletPath.equals("/feed/content.do")) {
if (request.getParameter("no") != null) {
model.put("no", Integer.parseInt(request.getParameter("no")));
pageController = new FeedContentController().setFeedDao(feedDao);
} else {
throw new ServletException();
}
DispatcherServlet에서 클라이언트가 컨텐트 읽기를 요청했을때의 처리하는 코드이다. request 객체의 키값이 "no"인 파라미터가 있는 경우, 해당 데이터를 Integer로 파싱 후 model 객체에 담는다. 없는 경우, 예외를 발생시킨다.
FeedContentController.java
public class FeedContentController implements Controller {
MySqlFeedDao feedDao;
public FeedContentController setFeedDao(MySqlFeedDao feedDao) {
this.feedDao = feedDao;
return this;
}
@Override
public String execute(Map<String, Object> model) throws Exception {
if (model.get("no") != null) {
Integer no = (Integer) model.get("no");
model.put("feed", feedDao.selectOne(no));
return "/feed/FeedContent.jsp";
} else {
return "error.jsp";
}
}
}
피드 수정 (UPDATE)
피드 내용 화면에서 '수정' 버튼을 클릭하면 피드 수정이 가능하다.
피드 수정 과정에서 클라이언트는 크게 두가지 요청을 하게 된다.
- 피드 수정 화면을 띄울 것을 요청 => GET /feed/edit.do?no=...
- 피드 수정 후 피드를 등록할 것을 요청 => POST /feed/edit.do (no=... title=... content=...)
프런트 컨틀롤러에서는 request 객체에 키값이 'title'인 데이터가 있는지 여부, 페이지 컨트롤러에서는 model 객체에 키값이 'title'인 데이터가 있는 지 여부에 따라 두 요청을 나눠 처리한다.
(1. 요청의 경우 피드 고유 번호(no)만 필요하므로 피드 제목(title) 데이터를 request/model 객체에 없다.)
먼저 1. 요청의 처리 프로세스이다.
피드 읽기 작업 처리 과정과 사실상 동일하다.
다음으로 2. 요청의 처리 프로세스이다.
클라이언트가 POST 요청과 함께 피드 고유번호(no)/제목(title)/내용(content)를 보내면 프런트 컨트롤러(DispatcherServlet)은 해당 데이터를 통해 Feed 객체를 생성하여 Model 객체에 담는다. 프런트 컨트롤러는 FeedEditController를 생성 후 execute(model) 메서드를 호출한다. 페이지 컨트롤러(FeedEditController)는 FeedDao의 update() 메서드를 통해 피드 내용을 수정한다. 그후 리다이랙션을 위한 메세지를 프런트 컨트롤러에게 리턴하고, 프런트 컨트롤러는 해당 URL로 리다이랙션한다.
DispatcherServlet.java
else if (servletPath.equals("/feed/edit.do")) {
int no = Integer.parseInt(request.getParameter("no"));
if (request.getParameter("title") == null) {
model.put("no", no);
} else {
Feed feed = new Feed()
.setNo(no)
.setTitle(request.getParameter("title"))
.setContent(request.getParameter("content"));
model.put("editFeed", feed);
}
pageController = new FeedEditController().setFeedDao(feedDao);
}
FeedEditController.java
public class FeedEditController implements Controller {
MySqlFeedDao peedDao;
public FeedEditController setFeedDao(MySqlFeedDao peedDao) {
this.peedDao = peedDao;
return this;
}
@Override
public String execute(Map<String, Object> model) throws Exception {
if (model.get("editFeed") == null) {
int no = (Integer) model.get("no");
model.put("editFeed", peedDao.selectOne(no));
return "/feed/FeedEditForm.jsp";
} else {
Feed editFeed = (Feed) model.get("editFeed");
peedDao.update(editFeed);
return "redirect:content.do?no="+ editFeed.getNo();
}
}
}
피드 삭제 (DELETE)
피드 수정 화면에서 '삭제' 버튼을 클릭하면 피드가 삭제된다.
클라이언트가 피드 삭제를 요청했을때의 처리 과정이다.
클라이언트는 GET 요청을 통해 삭제할 피드의 고유번호를 프런트 컨트롤러(DispatcherServlet)에게 보낸다. 프런트 컨트롤러는 피드 고유 번호를 model 객체에 담은후 FeedDeleteController를 생성 후 execute() 메서드를 호출한다. 페이지 컨트롤러(FeedDeleteController)는 FeedDao의 delete(no) 메서드를 호출해 해당 피드를 DB에서 삭제 후, main.do 경로로의 리다이랙션을 위한 메세지를 프런트 컨트롤러에게 보낸다. 그후, 프런트 컨트롤러는 해당 경로의 페이지로 리다이랙션 한다.
DispatcherServlet.java
else if (servletPath.equals("/feed/delete.do")) {
if (request.getParameter("no") != null) {
model.put("no", Integer.parseInt(request.getParameter("no")));
pageController = new FeedDeleteController().setFeedDao(feedDao);
} else {
throw new ServletException();
}
}
FeedDeleteController.java
public class FeedDeleteController implements Controller {
MySqlFeedDao feedDao;
public FeedDeleteController setFeedDao(MySqlFeedDao feedDao) {
this.feedDao = feedDao;
return this;
}
@Override
public String execute(Map<String, Object> model) throws Exception {
int no = (Integer) model.get("no");
feedDao.delete(no);
return "redirect:main.do";
}
}
'프로젝트 이야기 > CRUD 미니 게시판' 카테고리의 다른 글
220322 (5) : 객체 생성 자동화 (0) | 2022.03.22 |
---|---|
220319 (4) : 로그인/회원가입 입력폼 예외처리 (0) | 2022.03.19 |
220311 (3) : 인스턴스 생성 자동화하기 (feat. Reflection API) (0) | 2022.03.12 |
220310 (2) : 유저 기능 구현 (0) | 2022.03.10 |
20220303 (0) : 개요 (0) | 2022.03.03 |