JAVA
JSP: FrontController 직접 구현해보기
docc
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>