오늘부터는 본격적으로 MVC2를 사용해서 하나의 게시판을 구현해나간다. 실제 수업도 실습-해설의 무한 반복이다. 따라서 이제부터 정리할 내용은 전체적인 로직을 설명하고자 한다.
- Controller
- 클라이언트에서 데이터를 전달하는 방법
- Front Controller
- 중복확인 기능 구현
MVC2 패턴에서 클라이언트로부터 오는 요청은 서블릿을 거치게 된다. 물론 JSP도 실제는 컴파일 시 서블릿으로 변환되지만, 디자인 패턴 자체에서 요청을 통제 역할은 ‘서블릿’에게 맞겨지는데 이를 ‘Controller’라고 한다.
작업 과정
요청 – 서버 - 컨트롤러 – 모델 - jsp - 응답
위와 같은 구조를 택하면 요청에 대한 로그를 확인할 수 있으며, 장기적으로 기능 추가 시 API인 컨트롤러에 추가되기 때문에 유지 보수면에서도 좋다.
일반적으로 클라이언트에서 입력된 정보를 전달하는 방법은 form – submit
을 사용해 영역 안의 입력 데이터를 GET, POST방식으로 컨트롤러로 전달한다.
하지만 이 방법은 두 가지 한계가 있다.
입력되는 값을 전달하기 때문에, 글 번호와 같이 이미 DB에서 넘겨받는 값을 유기적으로 활용할 수 없다.
form 영역 안에 있는 것만 전달 받기 때문에 디자인에 있어 제약이 있다.
하지만 기본적으로 URL을 통해 전달되기에 URL을 설정하는 방식으로 응용할 수 있다. 다음과 같이 사용할 수 있다.
?
와 변수의 값을 문자열로 연결하여 사용한다.$(‘#del’).on(“click”, function(){
let deli_Id = $(“#deli_Id”).val();
location.href = “delete_controller?sid=”+del_Id;
});
<tr>
<td class="seqtd"><div class="seq">${i.seq}</div>
<td><div class="title" id="title">${i.title}</div>
</tr?
<script>
$(function(){
$(".title").on("click",function(){
location.href = "/read.board?seq="+$(this).parent().siblings(".seqtd").children().text();
})
})
</script>
<a>
태그 사용href
속성값으로 데이터가 전달될 경로를 정해줄 수 있다.<a href="read.board?seq=${i.seq }"></a>
위 경우에는 controller에서 view로 데이터를 전달하여, 브라우저에 렌더링되는 타이밍에 값을 미리 설정해줄 수 있다. 그래서 반복적인 값의 출력에서 식별을 줄 수 있다.
MVC2의 컨트롤러는 요청과 응답의 사이에서 그 관계를 통제해주는 역할을 한다. 하지만 사용자로부터 들어오는 요청의 종류는 무수히 많은데, 이를 하나씩 컨트롤러로 통제해줄 필요가 없다.
즉, 컨트롤러의 역할을 요청 별로 만드는 것이 아니라 프로그램을 이루는 모든 요청을 종류 별로 묶어서 하나의 컨트롤러로 만들어줘야 하는데, 이를 ‘프론트 컨트롤러’라고 한다.
이런 프론트 컨트롤러는 실제 서블릿 하나지만, 내부에 여러 서블릿으로 나뉘어 있어서 들어오는 모든 요청을 내부의 다른 컨트롤러에게 분배하여 처리한다.
프론트 컨트롤러는 다음의 형태를 갖는다.
@WebServlet("*.member")
public class MemberController extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// uri를 통해 자원의 식별자를 가져온다.
String uri = request.getRequestURI();
joinDAO dao = new joinDAO();
MemberDAO mDao = new MemberDAO();
// DAO를 사용하기 때문에 전체에 대한 예외처리를 해준다.
try {
// 조건문을 통해 식별자 별로 로직을 만든다.
if(uri.equals("/join.member")) { // 회원가입 View
response.sendRedirect("/member/joinform.jsp");
} else if (uri.equals("/duplCheck.member")) { // ID중복체크
String id = request.getParameter("id");
boolean result = mDao.isIdExist(id);
request.setAttribute("result", result);
if (result) {
request.getRequestDispatcher("/idCheckView.jsp").forward(request, response);
} else {
request.getRequestDispatcher("/idCheckView.jsp").forward(request, response);
}
} else if (uri.equals("/logout.member")) { // 로그아웃
request.getSession().invalidate();
response.sendRedirect("/index.jsp");
}
} catch (Exception e) {
e.printStackTrace();
response.sendRedirect("error.html");
}
}
위 코드는 다음의 단계로 나뉜다.
*
앞에 어떤 단어가 올 수 있다는 뜻이다. 여기서 중요한 것은 그 뒤에 오는 .OOO
이다.
기존에 요청을 URL로 이동시켰다면, 이제는 해당 URI라는 식별자를 통해서 들어온다. 위에선 .member
로 끝나기 때문에 이동 경로의 뒤에 .member
로 끝나는 모든 요청은 위 서블릿으로 들어오게 된다.
/
를 붙여서 안되며, 폴더 내부에 있는 것은 상위 폴더의 주소까지 비교해야 한다. /member/*.member
이제 로직을 실행하기 위해 request에 담긴 URI를 추출해줘야 한다.
String uri = request.getRequestURI();
결과적으로 프론트 컨트롤러는 기능별로 생성했던 컨트롤러들을 조건문으로 구별하여 하나의 서블릿 안에 넣어둬서 처리하는 것이다.
RESTful 은 고유의 url 형식이 있다. 형태는 비슷한데, url패턴이 다르다.
중복 확인 기능의 맹점은 ‘비동기 통신’으로 요청-응답의 동기처리와 달리 요청을 보낸 후, 처리가 완료될 때 응답을 보내는 것이다. 그러나 이는 ajax를 사용하기 때문에 지금 시점에서는 불가능하고, 팝업을 띄워 다른 창에서 중복확인을 진행하는 것이다.
window.open(“url”, “이름”, “옵션”);
js를 통해 팝업창을 띄운다.opener.document.getElementById
false
, 아니면 true
를 반환한다.window.close();
로 팝업창을 닫는다.<script>
$("#idCheck").on('click', function(){
// 팝업을 띄우는 함수 : string 값 3개의 인자를 받는다.
// window.open("URL", "pop-up명", "OPTION");
// 공백 시 경고 및 이벤트 처리 막음
if($("#id").val()==""){
alert("ID를 먼저 입력하세요");
$("#id").focus();
return
}
window.open("/duplCheck.member?id="+$("#id").val(), "", "top=100,left=200,width=300, height=200");
});
</script>
<!--중복 확인 view : 조건에 따라 보여주는 화면이 다르다.-->
<div>
<c:choose>
<!--컨트롤러로부터 중복 결과를 전달 받는다.-->
<c:when test="${result}">
<p>사용할 수 있는 아이디입니다.</p>
<button id="ok">OK</button>
<button id="cancel">Cancel</button>
<script>
$("#ok").on("click", function(){
// openee를 닫는다.
window.close();
});
$("#cancel").on("click", function(){
// opener의 ID를 가져온다.
opener.document.getElementById("id").value="";
window.close();
});
</script>
</c:when>
<c:otherwise>
<p>사용할 수 없는 아이디입니다.</p>
<button id="ok">OK</button>
<script>
$("#ok").on("click", function(){
opener.document.getElementById("id").value="";
window.close();
});
</script>
</c:otherwise>
</c:choose>
</div>
else if (uri.equals("/duplCheck.member")) { // ID중복체크
String id = request.getParameter("id");
boolean result = mDao.isIdExist(id);
request.setAttribute("result", result);
if (result) {
request.getRequestDispatcher("/idCheckView.jsp").forward(request, response);
} else {
request.getRequestDispatcher("/idCheckView.jsp").forward(request, response);
}
}
// ID 중복 확인
public boolean isIdExist(String id) throws Exception {
String sql = "select id from member where id=?";
try(
Connection con = this.getConnection();
PreparedStatement pstat = con.prepareStatement(sql);
){
pstat.setString(1, id);
try(
ResultSet rs = pstat.executeQuery();
){
if (rs.next() == true) {
return false;
} else {
return true;
}
}
}
}