내가 지금까지 만들었던 jsp 프로젝트 MyHome 은 Model1이다.
jsp 파일과 파일로 값을 옮길 때 page로만 값을 이동시키고, 한 번에 눈에 들어오는 디자인이 아니다. 이렇게 홈페이지를 구성하면 나중에 코드 복잡도가 높아져 코드 작성, 수정 및 유지보수 과정에서 매우매우매우 개고생하게 된다. (마치 나의 OzPcServer 처럼...)
그런데 만약 servlet 에서 모든 request 와 reponse 를 관리하고, 값을 보내주고 받아오는 기능을 다 해 준다면?
훨씬 쉽고 통일감 있는 구성이 가능하다.
MVC는 Model-View-Controller의 약자로, 소프트웨어 개발에서 사용되는 설계 패턴 중 하나이다. 이 패턴은 소프트웨어를 세 가지 주요 구성 요소로 나누어 개발하고 유지보수하는 데 도움이 된다.
MVC 패턴은 이러한 세 가지 구성 요소 간의 강한 결합을 피하고, 각각을 독립적으로 유지하여 소프트웨어의 확장성과 유지보수성을 향상시킨다. 이는 코드를 분리하여 각 부분이 서로 영향을 덜 주면서 독립적으로 변경될 수 있도록 해 준다.
이렇게 생각하면 너무 쉽다.
Model = dao
Controller = 서블릿
view = jsp, react, view, (프론트엔드)
그럼 이제부터 controller 에 대해 알아가 보자.
나는 Member.jsp 를 MVC 패턴대로 따라가며 구성해 보고, 그에 쓰이는 디자인 패턴과 자바 기능을 설명하고자 한다.
MemberServlet.java
package member;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MemberServlet extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("EUC-KR");
resp.setContentType("text/html; charset=EUC-KR");
// 클라이언트의 요청 URL을 가져옴
String url = req.getRequestURI();
// 현재 웹 어플리케이션의 컨텍스트(경로)를 가져옴
String path = req.getContextPath();
// 요청 URL에서 컨텍스트를 제외한 부분을 가져와서 cmd에 저장
String cmd = url.substring(path.length());
// substring 메서드는 문자열에서 특정 범위의 문자열을 추출하는 데 사용되는
// Java의 문자열 메서드 중 하나입니다.
// 이 메서드는 원본 문자열에서 시작 인덱스부터 끝
// 인덱스 전까지의 부분 문자열을 반환합니다.
MemberCommandFactory factory = MemberCommandFactory.getInstance();
CommandIf cmdIf = factory.createCommand(cmd);
String nextPage = (String)cmdIf.processCommand(req, resp);
RequestDispatcher view = req.getRequestDispatcher(nextPage);
view.forward(req, resp);
}
}
값이 들어오면 무조건 이쪽으로 오게 하는 서블릿 페이지이다. 이 서블릿 페이지를 어떻게 관리하느냐?
목적: 특정한 동작 또는 요청을 객체로 캡슐화하여 매개 변수화하고, 큐에 저장하거나 로깅하며, 실행 취소할 수 있는 연산을 지원한다.
구성 요소:
Command: 실제 요청을 나타내는 인터페이스를 정의하고, 이를 구현하는 구체적인 클래스가 있다.
Client: Command 객체를 생성하고 ConcreteCommand를 설정하여 Receiver와 연결한다.
Invoker: Command를 실행하는 객체로, ConcreteCommand에 대한 요청을 책임진다.
Receiver: 실제 동작을 수행하는 객체이다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>front</servlet-name>
<servlet-class>FrontAppServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>front</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>member</servlet-name>
<servlet-class>member.MemberServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>member</servlet-name>
<url-pattern>*.mem</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>board</servlet-name>
<servlet-class>board.BoardServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>board</servlet-name>
<url-pattern>*.board</url-pattern>
</servlet-mapping>
</web-app>
끝에 mem 값을 갖는 그 어떤 주소이든~ 바로 MemberServlet 으로 들어오게 된다. 그래서 서블릿이 값을 관리하기 편하게 해 준다.
MemberCommandFactory factory = MemberCommandFactory.getInstance();
목적
특정 클래스가 인스턴스화될 때 하나의 인스턴스만을 생성하고, 그 인스턴스에 접근할 수 있는 전역적인 점근점을 제공합니다.
특징
생성자가 private이므로 외부에서 직접 인스턴스를 생성할 수 없습니다.
정적 메서드나 메서드로 접근할 수 있는 정적 멤버를 통해 유일한 인스턴스에 접근할 수 있습니다.
전역 상태를 피하고 객체 간에 공유된 상태를 유지할 때 유용합니다.
public class MemberCommandFactory {
private MemberCommandFactory() {}
private static MemberCommandFactory instance = new MemberCommandFactory();
public static MemberCommandFactory getInstance() {
return instance;
}
package member;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MemberInputCommand implements CommandIf {
@Override
public Object processCommand(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
return "WEB-INF/member/member_input.jsp";
}
}
이렇게 해서 어떤 command 든 받을 수 있게 준비 완료 해 놓기
CommandIf 의 의도는 여러 사람이 같이 만들겠다는 뜻이다.
클래스 이름만 알면 밑에서 올려 보낼 수 있게 해 주는 것.
다형성으로다가 여기 밑에서 구성한 걸 CommandIf로 올려 보내준다. 다형성
목적
객체 생성을 캡슐화하여 클라이언트가 생성할 구체적인 클래스를 지정하지 않고도 객체를 만들 수 있게 합니다.
종류
Factory Method 패턴: 서브클래스에서 객체를 생성하는 메서드를 정의하고, 이를 통해 객체를 생성합니다.
Abstract Factory 패턴: 여러 관련 객체의 집합을 생성할 수 있는 인터페이스를 제공하며, 이를 구현하는 여러 팩토리 클래스를 생성합니다.
Simple Factory 패턴: 하나의 팩토리 클래스에서 객체의 생성을 담당하며, 클라이언트는 팩토리 클래스를 통해 객체를 생성합니다.
package member;
public class MemberCommandFactory {
private MemberCommandFactory() {}
private static MemberCommandFactory instance = new MemberCommandFactory();
public static MemberCommandFactory getInstance() {
return instance;
}
public CommandIf createCommand(String cmd) {
CommandIf cmdIf = null;
if (cmd.equals("/index.mem")) {
cmdIf = new IndexMemberCommand();
}else if (cmd.equals("/memberSsn.mem")) {
cmdIf = new MemberSsnCommand();
}else if (cmd.equals("/member_check.mem")) {
cmdIf = new MemberCheckCommand();
}else if (cmd.equals("/member_input.mem")) {
cmdIf = new MemberInputCommand();
}else if (cmd.equals("/member_input_ok.mem")) {
cmdIf = new MemberInputOkCommand();
}
return cmdIf;
}
}
이러면 너무 쉬운 MVC 패턴 이해 완료.
이렇게 하고 나서 적용만 하면 된다.
정보 감사합니다.