ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JSP: FrontController 직접 구현해보기
    JAVA 2021. 7. 26. 11:02

    Spring 의 FrontController 이해하기

    // FronctController.java
    
    package com.stone.mvc.control;
    
    import java.io.IOException;
    import java.util.Map.Entry;
    import java.util.Set;
    
    import javax.servlet.RequestDispatcher;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.stone.mvc.util.ViewResolver;
    
    
    //FrontController
    @WebServlet("*.do")
    public class FrontController extends HttpServlet {
        
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 1. uri path 만 제거
    		String uri= request.getRequestURI();
    		String 요청작업DotDo=uri.substring(5);//"/mvc/"5글자 다음 인덱스인 6째부터(zero base면 5가 6번째임)
    		while(true) {
    			// 2. 요청작업 구하기
    			int lastIdx=요청작업DotDo.indexOf(".do");//".do" 시작인덱스
    			String 요청작업=요청작업DotDo.substring(0,lastIdx);//".do"문자열 제거
    			
    			// 3. 요청과 컨트롤 객체 매핑
    	        IControl control= ControlProvider.getControlProvider().getControl(요청작업);
    			//해당 커트롤러 요청처리
    			ModelAndView 모델과지정된뷰명 = control.process(request, response);
    			Set<Entry<String,Object> >  entry들= 모델과지정된뷰명.model.entrySet();
    			for(Entry<String,Object> entry  : entry들 ){
    				request.setAttribute(entry.getKey(), entry.getValue());
    			}
    			
    			// 4. 경로에 따른 제어			
    			if(모델과지정된뷰명.viewName.contains("redirect:/")) {
    				요청작업DotDo = 모델과지정된뷰명.viewName.substring("redirect:/".length());//"redirect:/"문자열 제거
    				response.sendRedirect(요청작업DotDo);
    				break;//이미 제어권이 넘어가므로 큰 의미는 없다.
    			}
    			if(모델과지정된뷰명.viewName.contains("forward:/")) {
    				요청작업DotDo = 모델과지정된뷰명.viewName.substring("forward:/".length());//"forward:/"문자열 제거
    				System.out.println(요청작업DotDo);
    				continue;//요청작업 구하기부터 다시함
    			}
    			// 5. view로 forward
    			RequestDispatcher rd =request.getRequestDispatcher(ViewResolver.getJspViewName(모델과지정된뷰명.viewName));
    			rd.forward(request, response);		
    			break;//포워드시 이미 제어권이 넘어가므로 큰 의미는 없다.
    		}		
    	}
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		doGet(request,response );
    	}
    
    }
    // ControlProvider.java
    
    class BasicControlProvider {
    	
    	HashMap<String, IControl> controls = new HashMap<String, IControl>();
    	
    	BasicControlProvider(){
    		controls.put("prepare_board", new 게시물등록준비());
    		controls.put("add_board", new 게시물등록());
            // 추가사항
    		controls.put("prepare_member", new 회원등록준비());
    		controls.put("add_member", new 회원등록());
    		controls.put("info_member", new 회원정보준비());
    		controls.put("inquery_address", new 우편번호조회준비());
    		controls.put("inquery_id", new 아이디중복검사준비());
    	}
    	
    	public IControl getControl(String work) {
    		return controls.get(work);
    	}
    }
    
    public class ControlProvider {
    	
    	static BasicControlProvider controlProvider = new BasicControlProvider();
    	
    	public static BasicControlProvider getControlProvider() {
    		return controlProvider;
    	}
    }

    회원등록

    >> 회원등록준비.java

    public class 회원등록준비 implements IControl{
    
    	public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
    		ModelAndView mv = new ModelAndView();
    		
    		HttpSession session = request.getSession();
    		// 로그인해서 없는 카테고리지만 어떻게 주소를 찾아 들어올 경우 돌려보내기
    		if(!(session.getAttribute("no") == null)) {
    			mv.setViewName("index");
    		}
    				
    		mv.setViewName("회원등록창");
    		return mv;
    	}
    }

    >> 회원등록창.jsp

    <body>
    
    <form action="add_member" onsubmit="return 회원등록하다()" method="post" enctype="multipart/form-data">
    <h1>회원등록</h1>
    <!--기초내용 -->
    성명 <input type = "text" name = "name" id="name"><br>
    // 추가사항
    프로필<img id="myimg"/><br>
    	<input type="file" id="profile" name="profile"/>
    
    우편번호 <input type = "text" name = "post" id="post" readonly="readonly"><input type="button" onclick="조회창을띄우다()" value="조회"><br>
    주소 <input type = "text" name = "address" id="address" readonly="readonly"> <br>
    상세주소 <input type = "text" name = "detailaddress"> <br>
    전화번호 <input type="text" name="tel"/><br>
    이메일 <input type="text" name="email1"/>@
     <select name="email2">
     	<option value="google.com">google.com</option>	
     	<option value="naver.com">naver.com</option>	
     	<option value="daum.com">daum.com</option>	
     </select>
    <br>
    아이디 <input type="text" name="id" id="id" readonly="readonly"> <input type="button" onclick="아이디중복검사창을띄우다()" value="조회"><br>
    패스워드 <input type="password" name="password" id="password1"/><br>
    패스워드재입력 <input type="password" id="password2"/><br>
    <input type="submit" value="등록" />
    </form>
    
    </body>
    
    <script>
    /* 우편번호,주소 */
    function 조회창을띄우다(){
    	window.open("inquery_address","","width=450,height=300");
    }
    
    function 주소를받다(우편번호, 주소){
    	let txt우편번호 = document.querySelector("#post");
    	txt우편번호.value = 우편번호;
    	
    	let txt주소 = document.querySelector("#address");
    	txt주소.value = 주소;
    }
    
    /* 아이디 */
    function 아이디중복검사창을띄우다(){
    	window.open("inquery_id","","width=450,height=300");
    }
    
    function 아이디를받다(id){
    	let txtID = document.querySelector("#id");
    	txtID.value = id;
    }
    
    /* 필수입력, 비밀번호확인 */
    function 회원등록하다(){
    	// 필수 입력확인
    	var txt성명 = document.querySelector("#name");
    	if(txt성명.value == ""){
    		alert("성명은 필수 입력입니다.");
    		return false;
    	}
    	
    	var txt아이디 = document.querySelector("#id");
    	if(txt아이디.value == ""){
    		alert("아이디은 필수 입력입니다.");
    		return false;
    	}
    	
    	var txt패스워드 = document.querySelector("#password1");
    	if(txt패스워드.value == ""){
    		alert("패스워드은 필수 입력입니다.");
    		return false;
    	}
    	
    	// 비밀번호 동일 확인
    	var txt패스워드2 = document.querySelector("#password2");
    	if(txt패스워드.value != txt패스워드2.value){
    		alert("패스워드가 동일하지 않습니다.");
    		return false;
    	}
    	
    	return true;
    }
        // 추가사항
    	function 그림파일읽어출력하기(event){
    		var 그림파일 = event.target.files[0];
    		
    		if(!그림파일.type.match('image.*')){
    			alert("그림파일을 선택해주세요.");
    		}
    		
    		var 파일리더 = new FileReader();
    		파일리더.onload = function(event2){
    			var myimg = document.querySelector("#myimg");
    			img.src = event2.currentTarget.result;
    		}
    		파일리더.readAsDataURL(그림파일);
    	}
    	
    	document.querySelector("#myfile").addEventListener("change",그림파일읽어출력하기);
    </script>

    >> 회원등록.java

    public class 회원등록 implements IControl{
    
    	public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
    		Member member = null;
    		
    		String folder = "D:\\dojihye\\testFolder";
    		File file = new File(folder);
    
    		if(!file.exists()) {
    			try {
    				file.mkdir();
    			} catch (Exception e) {
    				System.err.println("이미 존재하는 폴더입니다.");
    			}
    		}
    		// try 문안에서와 밖에서 사용해야하니 위에서 선언
    		ModelAndView mv = new ModelAndView();
    		MemberDAO dao = new MemberDAO();
    		
    		
    		try {
    			// MultipartRequest 가 null 일때 String folder 즉, 폴더지정을 잘했는지 확인
    			MultipartRequest mr = new MultipartRequest(request, folder, 1024*1204*5,"utf-8",new DefaultFileRenamePolicy());
    			member = new Member();
    			member.setName(mr.getParameter("name"));
    			member.setPost(mr.getParameter("post"));
    			member.setAddress(mr.getParameter("address"));
    			member.setDetailaddress(mr.getParameter("detailaddress"));
                // 이메일 주의
    			member.setEmail(mr.getParameter("email1")+"@"+mr.getParameter("email2"));
    			member.setTel(mr.getParameter("tel"));
    			member.setId(mr.getParameter("id"));
    			member.setPassword(mr.getParameter("password"));
    			member.setState(mr.getParameter("state"));
    			member.setSize(mr.getParameter("size"));
    			
    			File pro = mr.getFile("profile");
    			// input 태그에 name 이 profile 인지 확인. 아니면 가져오는 것도 없어서 길이도 없으니 오류
    			byte[] profile = new byte[(int) pro.length()];
    			FileInputStream is = new FileInputStream(pro);
    			is.read(profile);
    			member.setProfile(profile);
    			
    		} catch (Exception e) {
    			// profile 에 이미지 미등록시 오류가 나니 없어도 save 하도록 똑같이 코드작성
    //			e.printStackTrace();
    			mv.addObject("member", member);
    			mv.setViewName("회원등록결과통보");
    			dao.save(member);
    			
    			return mv;
    		}
    
    		dao.save(member);
    		
    		mv.addObject("member", member);
    		mv.setViewName("회원등록결과통보");
    		
    		return mv;
    
      }
    }

    >> 회원등록결과통보.jsp

    <body>
    ${member.name} 님 회원등록 감사합니다! <br />
    <a href="index?no=-1">index</a>
    </body>

    >> 인덱스화면가기.java

    public class 인덱스화면가기 implements IControl{
    
    	public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
    		ModelAndView mv = new ModelAndView();
    		HttpSession session = request.getSession();
    		
    		int no = 0;
    		if(!(session.getAttribute("no") == null)){
    			no = Integer.valueOf((String)session.getAttribute("no"));
    		}
    
    		mv.addObject("no", no);
    		mv.setViewName("index");
    		return mv;
    	}
    
    }

    >> index.jsp

    <body>
    	index 입니다.
    	<br />
    	<%if(request.getParameter("msg") != null){%>
    	${msg}
    	<% 	}%>
    	<%if(login){%>
    	<a href="info_member">회원정보보기</a>
    	<% 	}%>
    </body>

    회원정보

    >> 회원정보준비.java

    	public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
    		ModelAndView mv = new ModelAndView();
    		HttpSession session = request.getSession();
    		
    		int no = (Integer)session.getAttribute("no");
    		
    		if(no>0) {
    			MemberDAO dao = new MemberDAO();
    			Member member = dao.findByNo(no);
    			
    			mv.addObject("member", member);
    			mv.setViewName("회원정보보기");	
    		}else {
    			mv.addObject("msg", "로그인이 필요합니다.");
    			mv.setViewName("index");
    		}
    		
    		return mv;
    	}

    >> 회원정보보기.jsp

    <body>
    name: ${member.name} <br />
    profile: <img src="profile?no=${member.no}"> <br />
    post: ${member.post} <br />
    address: ${member.address} <br />
    detailaddress: ${member.detailaddress} <br />
    tel: ${member.tel} <br />
    email: ${member.email} <br />
    id: ${member.id} <br />
    password: ${member.password} <br />
    rdate: ${member.rdate} <br />
    state: ${member.state} <br /> 
    size: ${member.size} <br />
    </body>

    >> 프로필보기.java

    public class 프로필보기 implements IControl{
    
    	public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
    		int no = Integer.valueOf(request.getParameter("no"));
    		
    		ModelAndView mv = new ModelAndView();
    		
    		mv.addObject("no", no);
    		mv.setViewName("profile");
    		
    		return mv;
    	}
    
    }

    >> profile.jsp

    <%@ page trimDirectiveWhitespaces="true" %>
    <%
    	// 바이너리로 응답 (html 응답) 모든 파일은 다 바이너리(byte)규격 이다.
    	int no = Integer.valueOf(request.getParameter("no"));
    	MemberDAO dao = new MemberDAO();
    	Member member = dao.findByNo(no);
    	
    	response.setContentType("image/jpeg");
    	ServletOutputStream os = response.getOutputStream();
    	try{
    		byte[] profile = member.getProfile();
    
    		os.write(profile);
    		
    	}catch(Exception e){
    		// 회원 이미지가 없어 null 예외 뜰 경우 images 폴더에 저장해둔 기본 유저이미지를 읽어 write한다.
    		String path = request.getRealPath("images");
    		File file = new File(path + "\\hoteIindexImg.png");
    		byte[] image = new byte[(int)file.length()];	//일단 file 크기만큼의 byte 배열을 만든다.
    		FileInputStream 입력스트림 = new FileInputStream(file);
    		입력스트림.read(image);
    
    		os.write(image);
    	}
    %>

    우편번호 API (다음 무료 API)

    https://postcode.map.daum.net/guide

    원하는 폼에 예제파일보기 누르고 코드 그대로 복사해 넣으면 끝!

    >> 조회창 예시

    <h1>우편번호조회</h1>
    <input type="text" id="sample4_postcode" placeholder="우편번호">
    <input type="button" onclick="sample4_execDaumPostcode()" value="우편번호 찾기"><br>
    <input type="text" id="sample4_roadAddress" placeholder="도로명주소">
    <input type="text" id="sample4_jibunAddress" placeholder="지번주소">
    <span id="guide" style="color:#999;display:none"></span>
    <input type="text" id="sample4_detailAddress" placeholder="상세주소">
    <input type="text" id="sample4_extraAddress" placeholder="참고항목">
    <input type="button" onclick="주소값주기()" value="확인"/>
    
    <script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
    <script>
        //본 예제에서는 도로명 주소 표기 방식에 대한 법령에 따라, 내려오는 데이터를 조합하여 올바른 주소를 구성하는 방법을 설명합니다.
        function sample4_execDaumPostcode() {
            new daum.Postcode({
                oncomplete: function(data) {
                    // 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
    
                    // 도로명 주소의 노출 규칙에 따라 주소를 표시한다.
                    // 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
                    var roadAddr = data.roadAddress; // 도로명 주소 변수
                    var extraRoadAddr = ''; // 참고 항목 변수
    
                    // 법정동명이 있을 경우 추가한다. (법정리는 제외)
                    // 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
                    if(data.bname !== '' && /[동|로|가]$/g.test(data.bname)){
                        extraRoadAddr += data.bname;
                    }
                    // 건물명이 있고, 공동주택일 경우 추가한다.
                    if(data.buildingName !== '' && data.apartment === 'Y'){
                       extraRoadAddr += (extraRoadAddr !== '' ? ', ' + data.buildingName : data.buildingName);
                    }
                    // 표시할 참고항목이 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
                    if(extraRoadAddr !== ''){
                        extraRoadAddr = ' (' + extraRoadAddr + ')';
                    }
    
                    // 우편번호와 주소 정보를 해당 필드에 넣는다.
                    document.getElementById('sample4_postcode').value = data.zonecode;
                    document.getElementById("sample4_roadAddress").value = roadAddr;
                    document.getElementById("sample4_jibunAddress").value = data.jibunAddress;
                    
                    // 참고항목 문자열이 있을 경우 해당 필드에 넣는다.
                    if(roadAddr !== ''){
                        document.getElementById("sample4_extraAddress").value = extraRoadAddr;
                    } else {
                        document.getElementById("sample4_extraAddress").value = '';
                    }
    
                    var guideTextBox = document.getElementById("guide");
                    // 사용자가 '선택 안함'을 클릭한 경우, 예상 주소라는 표시를 해준다.
                    if(data.autoRoadAddress) {
                        var expRoadAddr = data.autoRoadAddress + extraRoadAddr;
                        guideTextBox.innerHTML = '(예상 도로명 주소 : ' + expRoadAddr + ')';
                        guideTextBox.style.display = 'block';
    
                    } else if(data.autoJibunAddress) {
                        var expJibunAddr = data.autoJibunAddress;
                        guideTextBox.innerHTML = '(예상 지번 주소 : ' + expJibunAddr + ')';
                        guideTextBox.style.display = 'block';
                    } else {
                        guideTextBox.innerHTML = '';
                        guideTextBox.style.display = 'none';
                    }
                }
            }).open();
        }
        
        function 주소값주기(){
        	var 우편번호 = document.querySelector("#sample4_postcode").value;
        	var 도로명주소 = document.querySelector("#sample4_roadAddress").value + document.querySelector("#sample4_extraAddress").value;
        	var 지번주소 = document.querySelector("#sample4_jibunAddress").value;
        	var 상세주소 = document.querySelector("#sample4_detailAddress").value;
        	
        	opener.주소를받다(우편번호, 도로명주소, 지번주소, 상세주소);
        	close();
        }
    </script>

    댓글

Designed by Tistory.