오늘은 드디어 웹개발의 꽃이라 감히 불러보는 MVC 패턴에 대한 정의, 그리고 개념을 설명들었다.
MVC 패턴은 Client와 Server 그리고 Database간에 요청(Request)과 응답(Response)를 각각 역활에 따라 분류하고 그에 따라 코드를 작성하는 Pattern이라고 설명할수 있을것 같다.
Model, View, Controller로 이루어져있는데.
Model은 어플리케이션에서 사용되는 데이터와, 그 데이터를 처리하는 부분이고
View는 User(사용자)에게 보여지는 부분, 즉 웹 페이지의 화면을 구성하고 있는 부분이며
Controller는 User의 Request를 받아 수행하고 처리하며, 그 결과를 Response 해주는 과정이라고 설명할수 있을것 같다.
음... 정확한 설명인지는 잘 모르겠지만 현재로써는 이렇게 이해하고 있다.
따라서 Model에 관한건 Model 관련 코드로, View에 관련된건 View 코드로, Controller에 관련된건 Controller 코드로 각각 나뉘어 코드를 작성하며 그에 따른 동작들도 각각 구현해서 서로가 서로를 호출하고 응답하는 방식으로 구현한다.
그럼 왜 그렇게들 MVC MVC 하는것인가?
이 패턴을 잘 사용할줄 알면 User의 어플리케이션에서 비즈니스 로직을 분리해 애플리케이션의 시각적인 요소나, 비즈니스 로직을 서로 영향없이 유지/보수가 가능하다.
우리가 수업시간에 배운것들로 표현해보자면
Model - DAO, DTO 같은 쿼리를 실행하는 소스코드
View - HTML, Jquery, CSS로 이루어진 코드로 Web Page에 영향을 미치는 소스코드
Controller - Model과 View 사이에서 User의 요청이 들어오면 이를 어떻게 처리할지 전달하는 역활. Java 파일
이라고 할수 있겠다. 사실 이런 이론적인 부분은 더 정확하게 열거되어있는게 Google에 아주 다양하게 있으니 그것을 참고하는것도 좋은 방법일수 있다. 물론 필자 또한 그런식으로 이해해 나가고 있다.
이런식으로 구조를 설계해주었다. 확실히 MVC 패턴 방식으로 코드를 작성하게 되면 많은 파일(?)을 생성하게 된다. 각각의 기능과 역활에 따라 파일을 각각 만들어줘야 하기 때문이다.
현재 내 수준에 MVC 패턴을 100% 이론적으로 이해하기는 어렵다고 생각된다.
하지만 "흐름"을 알아야 한다. 어디서 어떤녀석이 어떤일을 하는거며, 어떤 메소드들이 호출되면 어떤 파일에서 수행되고 어떤 값을 가지고 오고 가고 하는지 그 흐름을 파악하는게 첫번째 일이라고 생각된다.
예를들어보자면 WEB-INF 폴더 안에 commandHandlerURI.properties 파일을 살펴보겠다.
이런식으로 현재 작성이 되어있는데 음? 이게 뭐지? 라고 생각이 들것이다.
이건 URL값이 /registerForm.do로 끝나면 member.command 패키지 안의 MyMemberFormContoller 클래스를 실행해준다. 라는 뜻이다.
그럼 MyMemberFormContoller라는 클래스를 보러 가보자.
package member.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import mvc.command.CommandHandler;
//mvc.command의 commentHandler의 구현클래스
//controller 클래스, URL registerForm.do로 요청되면 호출되는 controller 클래스
public class MyMemberFormController implements CommandHandler {
@Override
public String process(HttpServletRequest request, HttpServletResponse response) throws Exception {
/*
* 컨트롤러가 해야할일
* 1. 요청과 함께 넘어온 parameter 얻기
* 2. 비즈니스 로직 수행 ->Service<->DOA<->DB
* 3. 수행결과 를 모델화
* 4. 어떤 view를 보여줄지 지정
*/
System.out.println("MyMemberForm 호출 성공");
return "view/member/myJoinForm.jsp";
}
}
이 클래스는 CommandHandler라는 Interface를 상속받은 Class이다. 이 CommandHandler라는 Interface는 수업시간에 직접 작성한 Interface이다.
package mvc.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface CommandHandler {
//String 타입의 view를 리턴해주는 메서드
public String process(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
이것이 CommandHandler 인터페이스이다. 원래 interface는 상수와 추상메서드만 가질수 있고 여기에서도 Process라는 추상메서드만 선언이 되어있다. abstract 가 선언이 되어야하지만 생략도 가능하기 때문에, 이 코드에서는 생략되어있다.
그리고 process라는 Method는 HttpServletRequest와 HttpServletResponse라는 Java에서 제공하는 클래스를 매개변수로 제공한다.
이를 상속받으면 상속받은 구현 Class는 무조건 저 process라는 추상메서드를 구현해서 Override 해야한다.
따라서 Controller에서 CommandHandler interface를 상속받는다면 process 메서드를 구현하면서 그 안에서 응답과 요청이 번갈아 이루어질수 있게, 그리고 그 요청값과 응답값을 다시 어디론가 보내는 역활을 해주게 된다.
그럼 Controller가 Return으로 가리키고 있는 myJoinForm.jsp를 살펴보자.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css">
<!-- cdn 방식으로 jquery 연동 -->
<!-- <script src="https://code.jquery.com/jquery-2.2.4.js"></script> -->
<!-- download방식으로 jquery 연동 -->
<!-- <script src="./js/jquery-3.6.3.js"></script> -->
<!-- google cdn 방식 -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<meta charset="utf-8">
<title>My회원가입</title>
<style>
.c1 {
width:90px;
}
#memberFRM td, memberFRM th{
border: 1px solid #ddd;
padding: 4px;
}
#memberFRM th{
text-align: left;
width:100px;
background-color: aqua;
}
</style>
<script>
$(function(){
//폼의 유효성 검사를 담당하는 function
//memberFRM 폼객체의 onsubmit에 해당하는 submit
$('form#memberFRM').submit(function(){ // = form객체의 submit이 발생되면
let obj = $(this);
//alert(obj.attr('id'));
//회원가입 form의 유효성 검사를 하기위한 함수
//console.log(obj); //object HTMLFormElements
let id = $('#mid').val(); //각 요소의 value를 꺼내옴
let pwd = $('#mpwd').val();
let re_pwd = $('#re_mpwd').val();
let name = $('#mname').val();
let phone = $('#tel').val();
if(id == null || id=="") { //id의 값이 비었을때
alert('아이디를 입력해주세요');
$('#mid').focus(); //포커스 위치 지정
return false; //return문만 있으면 하단에 코드가 더 있더라도 함수를 더이상 진행하지않는다
}
if(pwd == null || pwd=="") { //pwd의 값이 비었을때
alert('비밀번호를 입력해주세요');
$('#mpwd').focus();
return false;
}
if(pwd.length < 4 || pwd.length > 12) { //글자수 4~12사이로 제한
alert('비밀번호4~12글자 이내로 작성하세요');
$('#mpwd').val(""); //비밀번호 입력된 값을 초기화
$('#mpwd').focus();
return false;
}
if(re_pwd == null || re_pwd == "") { //비밀번호 확인 필수입력 유효성검사
alert('비밀번호 확인을 입력해주세요');
$('#re_mpwd').focus();
return false;
}
if(pwd != re_pwd) { //비밀번호와 비밀번호 확인이 일치하지 않을때
alert('비밀번호가 일치하지 않습니다');
$('#re_mpwd').val(""); //비밀번호 확인란 초기화
$('#mpwd').val(""); //비밀번호란 초기화
$('#mpwd').focus(); //비밀번호란에 다시 포커스 지정
return false;
}
if(name == null || name=="") { //이름 작성 여부 검증
alert('이름을 입력해주세요');
$('#mname').focus();
return false;
}
//약관동의여부
/* let c = $('input[type=checkbox]:checked').length;
if(c===0) { //체크된 상태가 아닐때
alert('동의해주세요');
$('#agree').focus();
return false;
} */
return true;
/* submit 메소드를 이용하여 submit 버튼이 아니더라도 동일한 결과를 수행하게 만들어준다
obj.submit(); */
/* 폼객체의 메소드와 액션을 별도로 지정할수도있다
obj.method="post";
obj.action="j_ok.jsp";*/
});
//email 선택부분 jquery로 변환
$('#email_dd').change(function(op){
//alert($('select[id=email_dd] option:selected').val());
let text = $('select[id=email_dd] option:selected').val();
$('#email_d').val(text);
});
let openwindow;
$('#searchId').click(function(){
openwindow = window.open('view/member/idDuplicate.jsp','searchId','width=400px,height=200px','');
});
}); //jquery의 끝
/*
let obj = document.getElementById('memberFRM');
//alert(frmObj);
//회원가입 form의 유효성 검사를 하기위한 함수
//function validChk(obj){
//console.log(obj); //object HTMLFormElements
let id = obj.mid.value; //각 요소의 value를 꺼내옴
let pwd = obj.mpwd.value;
let re_pwd = obj.re_mpwd.value;
let name = obj.mname.value;
let phone = obj.tel.value;
if(id == null || id=="") { //id의 값이 비었을때
alert('아이디를 입력해주세요');
obj.mid.focus(); //포커스 위치 지정
return false; //return문만 있으면 하단에 코드가 더 있더라도 함수를 더이상 진행하지않는다
}
if(pwd == null || pwd=="") { //pwd의 값이 비었을때
alert('비밀번호를 입력해주세요');
obj.mpwd.focus();
return false;
}
if(pwd.length < 4 || pwd.length > 12) { //글자수 4~12사이로 제한
alert('비밀번호4~12글자 이내로 작성하세요');
obj.mpwd.value=""; //비밀번호 입력된 값을 초기화
obj.mpwd.focus();
return false;
}
if(re_pwd == null || re_pwd == "") { //비밀번호 확인 필수입력 유효성검사
alert('비밀번호 확인을 입력해주세요');
obj.re_mpwd.focus();
return false;
}
if(pwd != re_pwd) { //비밀번호와 비밀번호 확인이 일치하지 않을때
alert('비밀번호가 일치하지 않습니다');
obj.re_mpwd.value=""; //비밀번호 확인란 초기화
obj.mpwd.value=""; //비밀번호란 초기화
obj.mpwd.focus(); //비밀번호란에 다시 포커스 지정
return false;
}
if(name == null || name=="") { //이름 작성 여부 검증
alert('이름을 입력해주세요');
obj.mname.focus();
return false;
}
//약관동의여부
if(!obj.agree.checked) { //체크된 상태가 아닐때
alert('동의해주세요');
obj.agree.focus();
return false;
}
let genderObj = obj.gender;
if(!genderObj[0].checked && !genderObj[1].checked) { //성별여부 체크검증
alert('성별을 체크해주세요');
return false;
}
//정규식 let reg = /^[0-9]+/g;
//정규표현식.test(대상문자열) => 맞으면 true 리턴
if(phone==null || phone=="") {
alert('전화번호 필수입력사항입니다');
obj.tel.focus();
return false;
} else if(phone != "") {
let phonenumber = phone.replaceAll('-',"");
alert(phonenumber);
}
return true;
/* submit 메소드를 이용하여 submit 버튼이 아니더라도 동일한 결과를 수행하게 만들어준다
obj.submit(); */
/* 폼객체의 메소드와 액션을 별도로 지정할수도있다
obj.method="post";
obj.action="j_ok.jsp";
*/
/* function selectEmail(op) { //select
let text ="";
let index = op.selectedIndex; //select의 인덱스 번호 가져오기
text = op[index].value; //해당 인덱스번호의 value를 가져와 text 변수에 대입
//alert(text);
//document.getElementById('email_d').value = text; //text를 해당 email_d에 뿌려주기
document.memberFRM.email_d.value = text; //text를 해당 email_d에 뿌려주기 form으로 접근해 대입함
if(op[index].value == null || op[index].value == "") {
//alert('직접입력');
document.getElementById('email_d').focus();
}
} */
</script>
</head>
<body>
<!-- onsubmit은 submit 버튼을 눌렀을때만 수행된다 여기서 this는 submit 이 아닌 이벤트가 발생하는 form을 의미 -->
<form action="/register.do" name="memberFRM" id="memberFRM" method="get"> <!-- return을 부여하면서 함수가 false를 리턴하면 넘기지않는다 -->
<table>
<caption>회원가입</caption>
<tbody>
<tr>
<th class='c1'>아이디</th>
<td><input type="text" name="memberid" id="mid"/><input type="button" name="searchId" id="searchId" value="ID찾기"/></td>
<tr/>
<tr>
<th class='c1'>비밀번호</th>
<td><input type="password" name="memberpwd" id="mpwd"/></td>
</tr>
<tr>
<th class='c1'>비밀번호확인</th>
<td><input type="password" name="re_memberpwd" id="re_mpwd"/></td>
<tr/>
<tr>
<th class='c1'>이름</th>
<td><input type="text" name="membername" id="mname"/></td>
</tr>
<tr>
<th class='c1'>이메일</th>
<td><input type="text" name="email_id" id="email_id" size="12">@<input type="text" name="email_d" id="email_d" size="10"/>
<select name="email_dd" id="email_dd">
<option value="">직접입력</option>
<option value="daum.net">daum.net</option>
<option value="naver.com">naver.com</option>
<option value="nate.com">nate.com</option>
<option value="kakao.com">kakao.com</option>
<option value="gmail.com">gmail.com</option>
</select>
</td>
</tr>
<!-- <tr>
<td colspan="2" style="text-align: center;"><input type="checkbox" name="agree" id="agree"/><label for="agree">약관동의</label></td>
</tr> -->
<tr>
<td colspan="2" style="text-align: center;"><input type="submit" value="가입"/><input type="reset" value="취소"/></td>
</tr>
</tbody>
</table>
</form>
</body>
</html>
Jquery Style로 작성해본 View단이다. View기 때문에 주로 시각적인 요소를 표현해주는 HTML, CSS가 주된 구현 코드이다.
이런식으로 각각의 파일이 User의 요청을 분석하고, 그 분석 내용에 따라 서로 다른 Page를 보여주고,
서로 다른 Response값으로 응답하는것이다.
중요한 부분이다. MVC 패턴은 앞으로에 있을 Spring뿐만 아니라, 이후 실무에서도 사용되기 때문에 지금 시점을 최대한 내것으로 잘 만들어서 손에 익혀야한다.
'Study > 중앙정보처리학원과정' 카테고리의 다른 글
55일차. <JSTL>의 사용 방법 (0) | 2023.01.09 |
---|---|
54일차. MVC Pattern에서 Controller의 역활 그리고 Connection Pool을 이용한 DB 접근 (0) | 2023.01.06 |
52일차. 51일차의 유효성검사를 Jquery 스타일로 변경하기 (1) | 2023.01.04 |
51일차. Javascript 회원가입 폼(Form) 유효성 검사하기 (2) | 2023.01.03 |
49일차. Jquery Keyup() 이벤트와 Textarea을 이용한 예제 (1) | 2023.01.01 |