«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

장미의 개발일기

모델2 방식 웹 개발하기 (MVC 패턴) : 회원가입 파트 제작해보기 본문

개발일기/JSP 프로그래밍 (Java 웹프로그래밍)

모델2 방식 웹 개발하기 (MVC 패턴) : 회원가입 파트 제작해보기

민장미 2023. 8. 12. 17:01

 - 코드 결합도 낮추기 : 액션 팩토리 패턴 +팩토리 서비스

 

 

-셋팅: 이클립스 EE + 톰캣 + 마이바티스  +오라클 + SQL gate  사용 

 

 

 

 

-모델1방식과 모델2 방식+mvc패턴 비교 포스팅:

JSP 개발방식 모델1 과 모델2 방식 비교 + MVC 패턴 (tistory.com)

 

JSP 개발방식 모델1 과 모델2 방식 비교 + MVC 패턴

*MVC 패턴 (스프링 프레임워크)* Model : 비즈니스 로직과 데이터를 처리 View : 사용자 인터페이스 (웹 브라우저에 보이는 페이지) Controller : 요청과 응답을 처리하며, model과 view 사이의 상호작용을

jangmicoding.tistory.com

 

- 마이바티스 셋팅 포스팅:

Mybatis 마이바티스 다운로드 및 환경 설정 (tistory.com)

 

Mybatis 마이바티스 다운로드 및 환경 설정

** 아래 포스팅의 예제 => model 1 타입의 게시판 사이트를 제작한 후, 그걸 복사해와서 마이바티스를 테스트 해보는 포스팅 입니다. ** 아래 모델1방식으로 웹 게시판 만들기 JSP 프로그래밍: 모델1

jangmicoding.tistory.com

 

 

 

 

controller 패키지 생성 후 , 

1. ActionForward class 생성  -> setter/getter 적용하기

 

-모델의 인터페이스에 들어갈 ActionForward :

ActionForward: 모델이 수행하고 난 후 이동할 View의 정보와 이동 방식을 저장한 후,
모델이 수행한 후 반환되는 객체다. (원래는 컨트롤러로 지정된 클래스에서 뷰를 반환하도록

설계하지만, 이해를 돕기 위해 분리해서 제작)

쉽게 말해서 ActionForward는 (뷰 = 클라이언트 화면에 보이는 화면 )~.jsp 파일들을 보여주기 위한 수단으로 생각하면 된다.

package controller;

public class ActionForward {
	private String url; // 모델이 실행한 후 이동할 viewName
	private boolean method; // 모델이 실행한 후 이동할 이동 방식

	public ActionForward() {

	}

	public ActionForward(String url, boolean method) {
		this.url = url;
		this.method = method;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public boolean isMethod() {
		return method;
	}

	public void setMethod(boolean method) {
		this.method = method;
	}

}

 

 

 

action 패키지 생성 후

2. Action Interface 생성 (다형성을 적용하기 위함)

:모델이 수행하고 난 후 ActionForward에 해당 정보를 저장한 후, 
Controller에 의해서 사용될 값을 반환하는 추상메서드를 정의한다.

 

*팩토리 메서드 패턴 (Factory Method Pattern): 이 패턴에서는 추상 클래스를 사용하여 객체 생성을 정의하는 인터페이스를 제공합니다. 서브 클래스에서 이 추상 클래스를 상속받아 실제 객체 생성을 구현합니다. 각 서브 클래스마다 객체를 생성하는 방식을 조정할 수 있으며, 클라이언트 코드는 추상 클래스를 통해 객체를 생성합니다.

package action;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import controller.ActionForward;

public interface Action {
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws IOException;
}

 

 

action패키지 안에 

3. 방금 만든 Action Interface를 구현하는 HelloAction클래스를 생성  (테스트 모델)

  => 인터페이스를 구현한 모델을 정의한다.

이때 Forward 방식으로 진행을 하는 것이 일반적이다. 때에 따라서는 redirect 방식도 사용한다.

(Forward방식은 다음페이지로 넘어갈 때, 데이터를 유지한 채로, redirect는 데이터를 유지하지 X)

*로그인을 한 후 : Forward 방식으로 다음페이지로 가기,   회원가입을 한 후: Redirect 로 main페이지로 간다.* 

 

package action;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import controller.ActionForward;

public class HelloAction implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
		request.setAttribute("msg", "안녕하세요 loopy");
		// false -> forward, true -> redirect
		return new ActionForward("hello", false);
	}

}

 

 

 

controller 패키지 안에 
4. ActionFactory 클래스를 생성하기 
ActionFactory => 컨트럴 서블릿으로부터 요청을 받아서 각 Action들(action패키지에 있는 클래스들)을 관리하는 역할
다형성을 기준으로 관리하고 싱글톤 처리를 한다. 

 

이미 성생한 HelloAction을 getAction()메서드에 등록해준다. 이후에도 다른 action을 생성하면, 이 메소드에 등록해준다. 

 

*싱글톤이란?

싱글톤(Singleton)은 소프트웨어 디자인 패턴 중 하나로, 클래스의 인스턴스를 딱 하나만 생성하고 이 인스턴스에 대한 전역적인 접근점을 제공하는 패턴이다.

자바에서는 new 연산자를 이용하여 인스턴스를 생성하는데, 매번 new를 사용하면 메모리를 많이 사용해야하므로, 하나의 인스턴스를 만들고, 필요할 때마다 그 인스턴스 하나를 공유해서 사용하는 개념이다. 

 

package controller;

import action.Action;
import action.HelloAction;

public class ActionFactory {
	private static ActionFactory factory;

	private ActionFactory() {
	}

	
	// synchronized : 스레드 동기화 처리 
	// return factory; => 하나의 ActionFactory 인스턴스를 생성 후, 이 인스턴스를 공유하여 사용하도록 한다. 
	public static synchronized ActionFactory getFactory() {
		if (factory == null) {
			factory = new ActionFactory();
		}
		return factory;
	}

	
	
	// CotrollServlet이 사용할 메서드
	// cmd는 서블릿으로부터 받는 요청 값
	
	public Action getAction(String cmd) {
		// 요청값을 비교해서 다형성 적용
		Action action = null;
        if (cmd.equals("hello")) {
			action = new HelloAction();
		}

		
		return action;
	}
	
	

}

 

 

controller 패키지에

5. ControllServlet을 생성 

-annotaion으로 서블릿으로 등록 => @WebServlet("*.loopy")

: urlpattern에 loopy가 들어가게 하되,  .loopy 앞에 모든 문자를 허용

Ex)   www.kkkkk.co.kr/pro.loopy    , www.kkkkk.co.kr/main.loopy 

이런식으로 가능 

 

*직접 jsp파일(뷰)을 호출 x => 요새 트렌드는 controller를 통해서 뷰를 호출한다.

package controller;

import java.io.IOException;
import java.io.PrintWriter;

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 action.Action;

/**
 * Servlet implementation class ControllServlet
 */
@WebServlet("*.loopy")
public class ControllServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doProcess(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		doProcess(request, response);
	}

	protected void doProcess(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("euc-kr");
		// pro.loopy?cmd=hello
		// pro.loopy?cmd=movielist
		String cmd = request.getParameter("cmd");
		// 사용자의 요청을 ActionFactory에 전달한 후 해당 모델을 Action인터페이스에 반환받는다.(다형성)
		Action action = null;
		if (cmd != null) {
			ActionFactory factory = ActionFactory.getFactory();
			action = factory.getAction(cmd);
			ActionForward af = action.execute(request, response);
			// 모델에서 결정된 이동방식과 URL을 실제 적용하는 단계
			if (af.isMethod()) {
				response.sendRedirect(af.getUrl());
			} else {
				// forward
				// /WEB-INF/ 보안폴더이기 때문에 반드시 forward에서만 접근이 가능하다.
				String path = "/WEB-INF/views";
				RequestDispatcher rd = request.getRequestDispatcher(path + "/" + af.getUrl() + ".jsp");
				rd.forward(request, response);
			}
		} else {
			// 오류코드
			response.setContentType("text/html;charset=euc-kr");
			PrintWriter out = response.getWriter();
			out.print("<h2>요청하신 페이지는 존재하지 않습니다.</h2>");
			out.close();
		}
	}

}

 

 

5-2.  WEB-INF 폴더 안에 views 폴더를 만들고 거기에 hello.jsp 를 만들어서 테스트한다
-ControlServlet 서블렛에서 실행 한 후, 인터넷 창이 뜨면 url을 아래처럼 바꿔서 테스트 한다

  url 뒤에 추가=>     /pro.loopy?cmd=hello     

    (hello는 hello.jsp를 의미 

    (cmd는 getAction() 메서드의 인자, String cmd를 의미 

 

=> 메세지(msg)가 화면에 출력되면 성공! 

msg : HelloAction클래스의 execute()메소드에 설명해놓은 부분  =>  request.setAttribute("msg", "안녕하세요 loopy");

 

*   ${msg } => EL (Expression Language) 로, 시간이 되면 따로 포스팅 할 예정

<%@ page language="java" contentType="text/html; charset=EUC-KR"
	pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>hello.jsp</title>
</head>
<body>
	<h1>Hello.jsp</h1>
	<p>메세지: ${msg }</p>
</body>
</html>

 

 

성공화면 

 

 

-----테스트 및 환경 구축 끝

 

 

 

 

 

 

 

 

6. 새로운 모델을 생성 해보기 (로그인)

 

1) 뷰 생성

-views/login 폴더 생성  + loginForm.jsp 파일 생성하기

-loginForm.jsp 

<%@ page language="java" contentType="text/html; charset=EUC-KR"
	pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>login/loginForm.jsp</title>
</head>
<body>
	<form method="post" action="pro.loopy" autocomplete="off">
		<input type="hidden" name="cmd" value="login">
		<input type="hidden" name="sub" value="loginchk">
		<p>아이디: <input type="text" name="id" id="id"></p>
		<p>비밀번호: <input type="password" name="pwd" id="pwd"></p>
		<p><input type="submit" value="send"></p>
	</form>
</body>
</html>

 

 

 

2) action 패키지에 LoginAction 클래스를 생성한 후에(Action 인터페이스 구현), ActionFactory에 등록해준다.

- LoginAction

sub => 두 번째 파라미터

package action;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import controller.ActionForward;

//cmd=loginForm 찾아오면
//내부적으로 sub란 파라미터로 분류 -> 로그인폼, 로그인처리, 로그아웃 처리 등등
public class LoginAction implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String sub = request.getParameter("sub");
		System.out.println("sub => " + sub);
		ActionForward af = null;
		if (sub.equals("loginForm")) {
			af = new ActionForward("login/loginForm", false);
		}
		return af;
	}

}

 

 

-ActionFactory의 getAction 메서드

	public Action getAction(String cmd) {
		// 요청값을 비교해서 다형성 적용
		Action action = null;
		if (cmd.equals("hello")) {
			action = new HelloAction();
		} else if (cmd.equals("login")) {
			action = new LoginAction();
		}
		return action;
	}

 

테스트 url : http://localhost/0812_Model2_join/pro.loopy?cmd=login&sub=loginForm

0812_Model2_join는 프로젝트명 

 

 

성공화면:

 

 

 

 

 

7.  프로젝트를 실행하면, 가장 먼저 실행될 메인화면 생성 

6번꺼 반복하기

 

 

1)  views/main 에   index.jsp 생성  

footer2.jsp와 header2.jsp를 index.jsp에 include 해주기 (위치: /views )

index.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@include file="../header2.jsp" %>
	<h2>여기는 index</h2>
	<c:choose>

		<%-- 세션이 없는 경우 , 로그인 전 화면 --%>
		<c:when test="${sessionScope.id==null }">
			<a href="main.loopy?cmd=login&sub=loginForm">Login</a>
			<br>
			<a href="main.loopy?cmd=member&sub=memForm">Join</a>
			<br>
		</c:when>
		<c:otherwise>
			<%-- 세션이 있는 경우 , 로그인 성공 후 화면 --%>
			<span>${sessionScope.id }님 반갑습니다.</span>
			<a href="main.loopy?cmd=member&sub=myPage">MyPage</a>
			<br>
			<a href="main.loopy?cmd=login&sub=logout">Logout</a>
		</c:otherwise>
	</c:choose>

<%@include file="../footer2.jsp" %>

 

-footer2.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
	<footer>Page Footer </footer>
</body>
</html>

 

 

-header2.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
	pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>main/main1.jsp</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link
	href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
	rel="stylesheet">
<link rel="stylesheet"
	href="<%=application.getContextPath()%>/resources/css/mystyle.css">
<script
	src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>
<script
	src="<%=application.getContextPath()%>/resources/js/ajaxdemo1.js"></script>
</head>
<body>
	<%-- header시작 --%>
	<%-- p-5 : padding 5
	text-white : text 흰색
	text-center : text-align center
	 --%>
	<header class="p-5 bg-primary text-white text-center">
		<hgroup>
			<h1>연습용 WebPage!</h1>
			<p>[연습용 WebPage! 입니다.]</p>
		</hgroup>
	</header>
	<%-- header끝 --%>
	<%-- 메뉴 시작 --%>
	<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
		<%-- 메뉴를 만들 때 검색까지 배치를 적용 --%>
		<div class="container-fluid">
			<%-- ul => navbar-nav --%>
			<ul class="navbar-nav">
				<li class="nav-item"><a
					href="<%=application.getContextPath()%>/main/main1.jsp"
					class="nav-link active">Home</a></li>
				<li class="nav-item"><a
					href="<%=application.getContextPath()%>/pororo/gallery.jsp"
					class="nav-link ">갤러리</a></li>
				<li class="nav-item"><a
					href="<%=application.getContextPath()%>/tboard/list.jsp"
					class="nav-link ">자유게시판</a></li>
				<li class="nav-item"><a href="" class="nav-link ">프로필</a></li>
				<li class="nav-item dropdown"><a
					class="nav-link dropdown-toggle" href="#" id="navbarDropdown"
					role="button" data-bs-toggle="dropdown" aria-expanded="false">
						Dropdown </a>
					<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
						<li><a class="dropdown-item" href="#">Action</a></li>
						<li><a class="dropdown-item" href="#">Another action</a></li>
						<li><hr class="dropdown-divider"></li>
						<li><a class="dropdown-item" href="#">Something else here</a></li>
					</ul></li>
				<li class="nav-item dropdown"><a
					class="nav-link dropdown-toggle" href="#" id="navbarDropdown"
					role="button" data-bs-toggle="dropdown" aria-expanded="false">
						멤버쉽 </a>
					<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
						<li><a class="dropdown-item"
							href="<%=application.getContextPath()%>/member/join.jsp">회원가입</a></li>
						<li><a class="dropdown-item" href="#">로그인</a></li>
						<li><a class="dropdown-item" 
						href="pro.kosmo?cmd=member&sub=pageMemList">페이지처리예제</a></li>
					</ul>
				</li>
			</ul>
			<form class="d-flex">
				<input class="form-control me-2" type="search" placeholder="Search"
					aria-label="Search">
				<button class="btn btn-primary" type="submit">Search</button>
			</form>
		</div>
	</nav>

 

 

(파일 경로 사진 참고 

 

 

-jstl 다운로드 받은 후 => WEB-INF/lib에 넣기

jstl-1.2.jar
0.40MB

index.jsp에서 아래 3 번째 줄 코드를 반드시 적어줘야  jstl 사용이 가능하다. 

 

 

 

2) IndexAction class 생성 및 Action 인터페이스 오버라이딩

package action;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import controller.ActionForward;

public class IndexAction implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws IOException {

		return new ActionForward("main/index", false);
	}

}

 

 

2)ActionFactory - getAction 메소드에 등록해주기 

	public Action getAction(String cmd) {
		// 요청값을 비교해서 다형성 적용
		Action action = null;
		if (cmd.equals("hello")) {
			action = new HelloAction();
		} else if (cmd.equals("login")) {
			action = new LoginAction();
		}else if (cmd.equals("index")) {
			action = new IndexAction();
		}
		return action;
	}

 

테스트 url : http://localhost/0812_Model2_join/pro.loopy?cmd=index

성공화면

 

 

 

 

 

8.  LoginAction 메서드 구현하기

--Session ( scope obj) : 인터넷 서치를 하다보면 세션과 쿠키라는 단어를 종종 들어보았을 것이다. 

둘 다 사용자의 상태를 저장하는 공간으로 Cookie는 클라이언트 측에, Session은 서버에 저장이 된다.

그래서 Cookie는 보안에 매우 취약하다. Session은 서버가 털리지 않는 이상 안전하다.

 

-LoginAction 

아까 만들어둔 로그인 폼에 이어서 로그인 체크 로직 및 로그아웃 로직을 구현한다. 

VO 및 Dao 부분은 아직 DB작업을 안했으니 생략. 

package action;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import controller.ActionForward;

//cmd=loginForm 찾아오면
//내부적으로 sub란 파라미터로 분류 -> 로그인폼, 로그인처리, 로그아웃 처리 등등
public class LoginAction implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String sub = request.getParameter("sub");
		System.out.println("sub => " + sub);
		ActionForward af = null;
		if (sub.equals("loginForm")) {
			af = new ActionForward("login/loginForm", false);
		} else if (sub.equals("loginchk")) {

		
			
		
			// redirect로 보내기 (test용)
			af = new ActionForward("pro.loopy?cmd=index", true);

		} else if (sub.equals("logout")) {
			// 세션을 삭제 - false는 새로운 세션을 생성하지 않는다.(기존값)
			HttpSession session = request.getSession(false);
			session.removeAttribute("id");
			// session.invalidate(); //모든 세션을 삭제
			System.out.println("logout");
			af = new ActionForward("pro.loopy?cmd=index", true);
		}
		return af;
	}

}

 

 

 

--DB작업 

 

*마이바티스 셋팅 

마이바티스 다운로드 후 => WEB-INF/lib에 넣기

mybatis-3.5.13.jar
1.71MB

 

 

마이바티스 자세한 셋팅의 위에 링크 달아둔 포스팅 참고 

 

 

context.xml  => DB 관련 정보 담는 파일 , 위치 중요 

 

 

-config/config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 데이터베이스 접속 -->
	<environments default="">
		<environment id="">
			<transactionManager type="JDBC" />
			<dataSource type="JNDI">
				<property name="data_source"
					value="java:comp/env/jdbc/myora" />
			</dataSource>
		</environment>
	</environments>
	<!-- 작업을 진행 할 mapper는 꼭 등록해줘야 한다. -->
	<mappers>
		<mapper resource="mapper/member.xml" />
	</mappers>
</configuration>

 

 

-- mapper/member.xml  ( mapper)

아래 코드는 로그인 로직 관련 전부 다 구현되어 있음 *  mapper에 관한 설명은 생략 

 

-가입시 아이디 중복체크, 로그인시 아이디 및 비번체크 ,가입하기 등 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- member.xml -->
<mapper namespace="mem">
	<select id="idcheck" parameterType="String" resultType="int">
		SELECT
		COUNT(*) cnt FROM MEM1 WHERE id=#{id}
	</select>

	<!-- loginCheck -> select count(*) cnt from mem1 where id=#{id} and pwd=#{pwd} -->
	<select id="loginchk" parameterType="vo.Mem1VO" resultType="int">
		select count(*) cnt from mem1 where id=#{id} and pwd=#{pwd}
	</select>

	<!-- MyPage -> select num, id, name, tel, email from mem1 where id=#{id} -->

	<select id="mypage" parameterType="String"
		resultType="vo.Mem1VO">
		select num, id, name, tel, email from mem1 where id=#{id}
	</select>

	<!-- Join -> insert into mem1 values (id, pwd, name, email, tel) -->
	<insert id="join" parameterType="vo.Mem1VO">
		insert into mem1 values
		(mem1_seq.nextval, #{id}, #{pwd}, #{name}, #{email}, #{tel},
		#{reip},sysdate)
	</insert>
	
	
	<!-- 회원전체 데이터 샘플 
		파라미터 : stype - 검색구분, svalue -검색값 
		where 엘리멘트의 특징 => 동적으로 구문적인 역할을 수행한다. sql의 where절 ㅇㅇ 
	-->
	<!-- XML문법 PC DATA : 영역 안에서 < >를 사용할 수가 없기 때문에 그 목적으로 사용하기 위해서 탈출!
	 <![CDATA[문장]]>   -->
	<select id="listBack1" resultType="vo.Mem1VO" parameterType="map">
		<![CDATA[
		SELECT NUM, ID, NAME, TEL, EMAIL, REIP, MDATE FROM MEM1 WHERE NUM < 35
		]]>
	</select>
	
	<select id="listBack2" resultType="vo.Mem1VO" parameterType="map">
		SELECT NUM, ID, NAME, TEL, EMAIL, REIP, MDATE FROM MEM1 
		
		
		<!-- xml 영역으로 봐라 , 영역안은 pc data다  -->
		<if test="stype != null and svalue != null">
			<where>
				<choose>
					<when test="stype == 1">
						NAME LIKE '%'||#{svalue} ||'%' AND NUM > 10
					</when>				
					<when test="stype == 2">
						ID = #{svalue}
					</when>					
					<when test="stype == 3">
						EMAIL LIKE '%'||#{svalue}||'%'	
					</when>				
				</choose>			
			</where>
		</if>
		
		ORDER BY 1 DESC
	</select>
	<select id="list" resultType="vo.Mem1VO" parameterType="map">
		SELECT
		num, id, name, tel, email, reip, mdate FROM(
		SELECT ROWNUM r_num, a.*
		FROM(
		SELECT * FROM mem1 ORDER BY 1 desc
		) a
		)WHERE r_num BETWEEN
		#{begin} AND #{end}
	</select>
	<select id="totalCount" resultType="int">
		select count(*) cnt from mem1
	</select>
</mapper>

 

 

 

 

9.  DB작업 

-1) 테이블 및 시퀀스 생성  (

 SQL gate에서 테이블 및 시퀀스 생성 (MEM1, MEM1_ID_UQ) 

CREATE TABLE MEM1(
	NUM NUMBER CONSTRAINT MEM1_NUM_PK PRIMARY KEY,
	ID VARCHAR2(20) NOT NULL,
	PWD VARCHAR2(10),
	NAME VARCHAR2(45),
	EMAIL VARCHAR2(45),
	TEL VARCHAR2(30),
	REIP VARCHAR2(30),
	MDATE DATE,
	CONSTRAINT MEM1_ID_UQ UNIQUE(ID)
);

CREATE SEQUENCE MEM1_SEQ
INCREMENT BY 1
START WITH 1;

 

 

-2) 이클립스에서 위의 sql문을 보면서 VO만들기     

vo/Mem1VO.java

VO(DTO): DB의 정보를 담는 객체 

package vo;

public class Mem1VO {
	private int num;
	private String id;
	private String pwd;
	private String name;
	private String email;
	private String tel;
	private String reip;
	private String mdate;

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getTel() {
		return tel;
	}

	public void setTel(String tel) {
		this.tel = tel;
	}

	public String getReip() {
		return reip;
	}

	public void setReip(String reip) {
		this.reip = reip;
	}

	public String getMdate() {
		return mdate;
	}

	public void setMdate(String mdate) {
		this.mdate = mdate;
	}

}

 

 

-3) factory 패키지에 FactoryService 클래스 생성

FactoryService  =>  SqlSessionFactory 객체를 반환 => SqlSessionFactory객체는 SqlSession을 생성한다.  

*SqlSession :  DB에 대한 SQL 문을 실행하고, 그 결과를 반환해주는 객체. (mapper 파일과 연결되어 있다)

 

MemberDao (Dao는 DB에 접근하는 객체다.)

MemberDao의 모든 메서드에는 아래코드(SqlSession생성)가 첫줄에 들어간다. 

SqlSession ss = FactoryService.getFactory().openSession();
package factory;

import java.io.IOException;
import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class FactoryService {
	private static SqlSessionFactory factory;
	// config.xml이 mapper 즉 sql문까지 포함되고 있기 때문에 불러와서 사용 가능한 객체
	// SqlSession 반환
	static {
		try (Reader reader = Resources.getResourceAsReader("config/config.xml");) {
			factory = new SqlSessionFactoryBuilder().build(reader);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static SqlSessionFactory getFactory() {
		return factory;
	}

}

 

 

 

-4) DAO 만들기

Dao: DB에 접근을 하는 객체 

dao/MemberDao.java

package dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;

import factory.FactoryService;
import vo.Mem1VO;

public class MemberDao {
	private static MemberDao dao;

	// 싱글톤
	private MemberDao() {

	}

	// 동기화
	public synchronized static MemberDao getDao() {
		if (dao == null) {
			dao = new MemberDao();
		}
		return dao;
	}

	// idcheck를 하기 위한 메서드 정의
//	<mapper namespace="mem">
//	<select id="idcheck" parameterType="String" resultType="int">
//	SELECT COUNT(*) cnt FROM MEM1 WHERE id=#{id}
	public int idCheck(String id) {
		SqlSession ss = FactoryService.getFactory().openSession();
		int cnt = ss.selectOne("mem.idcheck", id);
		ss.close();
		return cnt;
	}

	// loginCheck
//	<select id="loginchk" parameterType="vo.Mem1VO" resultType="int">
//	select count(*) cnt from mem1 where id=#{id} and pwd=#{pwd}
	public int loginCheck(Mem1VO v) {
		SqlSession ss = FactoryService.getFactory().openSession();
		int cnt = ss.selectOne("mem.loginchk", v);
		ss.close();
		return cnt;
	}

	// mypage
//	<select id="mypage">
//	select num, id, name, tel, email from mem1 where id=#{id}
	public Mem1VO myPage(String id) {
		SqlSession ss = FactoryService.getFactory().openSession();
		Mem1VO v = ss.selectOne("mem.mypage", id);
		ss.close();
		return v;
	}

	// join
//	<insert id="join" parameterType="vo.Mem1VO">
//	insert into mem1 values (mem1_seq.nextval, #{id}, #{pwd}, #{name}, #{email}, #{tel})
	public void join(Mem1VO v) {
		SqlSession ss = FactoryService.getFactory().openSession();
		ss.insert("mem.join", v);
		ss.commit();
		ss.close();
	}
	
	
	//1.회원관리 시스템 
	//SELECT NUM, ID, NAME, TEL, EMAIL, REIP, MDATE FROM MEM1 ORDER BY 1 DESC
	
	/*
	 * public List<Mem1VO> memList() { SqlSession ss =
	 * FactoryService.getFactory().openSession();
	 *
	 * 2.검색기능 업데이트 member xml 수정 후에 아래 메소드
	 *  List<Mem1VO> list = ss.selectList("mem.list"); //다중행 쿼리니까 selectlist()
	 * ss.close(); return list; } 
	 * 3. 페이지처리 -begin, end  
	 * <select id="list" resultType="vo.Mem1VO" parameterType="map">
	 */  
	public List<Mem1VO> memList(Map<String, String> map) {
		SqlSession ss = FactoryService.getFactory().openSession();
		List<Mem1VO> list = ss.selectList("mem.list",map); //다중행 쿼리니까 selectlist() 
		ss.close();
		return list; 
	}
	//3. total함께 처리
	//<select id="totalCount" resultType="int">
	public int getCnt() {
		SqlSession ss = FactoryService.getFactory().openSession();
		int cnt = ss.selectOne("mem.totalCount");
		ss.close();
		return cnt;
	} // Dao 의 위로 ㄱㄱ 
	
	
	
}

 

 

-5) LoginAction 코드 마무리 및 완성하기

VO 및 Dao 부분 구현 

package action;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import controller.ActionForward;
import dao.MemberDao;
import vo.Mem1VO;

//cmd=loginForm 찾아오면
//내부적으로 sub란 파라미터로 분류 -> 로그인폼, 로그인처리, 로그아웃 처리 등등
public class LoginAction implements Action {

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String sub = request.getParameter("sub");
		System.out.println("sub => " + sub);
		ActionForward af = null;
		if (sub.equals("loginForm")) {
			af = new ActionForward("login/loginForm", false);
		} else if (sub.equals("loginchk")) {

			String id = request.getParameter("id");
			String pwd = request.getParameter("pwd");
			System.out.println("id: " + id);
			System.out.println("pwd: " + pwd);
			// 로그인이 성공/실패
			// 성공시 세션 저장
			Mem1VO v = new Mem1VO();
			v.setId(id);
			v.setPwd(pwd);
			
			int cnt = MemberDao.getDao().loginCheck(v);
			if (cnt > 0) {
				HttpSession session = request.getSession();
				session.setAttribute("id", id);
			} else {
				PrintWriter out = response.getWriter();
				out.println("Login에러!");
				out.println("<a href='pro.loopy?cmd=index'>Home</a>");
				out.close();
			}

			// redirect로 보내기 (test용)
			af = new ActionForward("pro.loopy?cmd=index", true);

		} else if (sub.equals("logout")) {
			// 세션을 삭제 - false는 새로운 세션을 생성하지 않는다.(기존값)
			HttpSession session = request.getSession(false);
			session.removeAttribute("id");
			// session.invalidate(); //모든 세션을 삭제
			System.out.println("logout");
			af = new ActionForward("pro.loopy?cmd=index", true);
		}
		return af;
	}

}

 

 

 

로그인 테스트 : 

sql gate에서 수동으로 데이터를 넣거본다. 

insert into mem1 values(mem1_seq.nextval, 'soso6', '1', '노리1', 'buggl56e@naver.com', '010-222-3333','123.12.13',sysdate);

 

위의 아이디와 비번으로 로그인 테스트해보기

 

로그인 성공

 

 

로그아웃 성공

 

 

 

 10. 회원가입 로직 

6번에서 한 과정 다시 반복 

-1)  views/main/에 join.jsp 생성 (회원가입 폼)

<%@ page language="java" contentType="text/html; charset=EUC-KR"
	pageEncoding="EUC-KR"%>
<%@include file="../header2.jsp"%>
<div id="wrap" class="container mt-5">
	<div class="row">
		<h2>회원가입 폼</h2>
		<form method="post" action="pro.loopy" autocomplete="on">
		<input type="hidden" name="cmd" value="member">
		<input type="hidden" name="sub" value="memAdd">
			<p>
				아이디 : <input type="text" name="id" id="id">
				<button id="idChkBtn" >중복확인</button>
			</p>
			<p>
				<span id="target"></span>
			</p>
			<p>
				비밀번호 : <input type="password" name="pwd" id="pwd">
			</p>
			<p>
				이름 : <input type="text" name="name" id="name">
			</p>
			<p>
				이메일 : <input type="email" name="email" id="email">
			</p>
			<p>
				전화번호 : <input type="tel" name="tel" id="tel">
			</p>
			<p><input type="hidden" name="reip" value="<%=request.getRemoteAddr() %>"></p>
			<p style="text-align: right;">
				<input type="submit" value="가입">
			</p>
		</form>
	</div>
</div>

<script>
	window.onload = function() {
		document.querySelector("#idChkBtn").onclick = function(e) {

			//폼전송을 막는 기능
			e.preventDefault();
			let param = "cmd=member&sub=idChk&id="+ document.getElementById("id").value;
			sendRequest("pro.loopy", param, res, "get");
		};
		function res() {
			
			if (xhr.readyState === 4) {
				if (xhr.status === 200) {
					let idCnt = parseInt(xhr.responseText);
					console.log(idCnt + ":" + typeof (idCnt));
					let msg = "";
					if (idCnt === 0) {
						msg = "사용 가능한 아이디 입니다!";
						document.getElementById("target").style.color = 'blue';
					} else {
						msg = "이미 사용중인 아이디 입니다!";
						document.getElementById("target").style.color = 'red';
					}
					document.getElementById("target").innerHTML = msg;
				}
			}
		}
	};
</script>

<%@include file="../footer2.jsp"%>

 

 

-2)  action에 Mem1Action 생성 

package action;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import controller.ActionForward;
import dao.MemberDao;
import vo.Mem1VO;

//가입, 마이페이지
// cmd = member, sub = memForm, memAdd, myPage
public class Mem1Action implements Action {
	

	@Override
	public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String sub = request.getParameter("sub");
		ActionForward af = null;
		if (sub.equals("memForm")) { // 회원가입 폼
			af = new ActionForward("main/join", false);
		} else if (sub.equals("memAdd")) { /// 회원가입 처리
			
			String id = request.getParameter("id");
			String pwd = request.getParameter("pwd");
			String name = request.getParameter("name");
			String email = request.getParameter("email");
			String tel = request.getParameter("tel");
			String reip = request.getParameter("reip");
			Mem1VO v = new Mem1VO();
			
			v.setId(id);
			v.setPwd(pwd);
			v.setName(name);
			v.setEmail(email);
			v.setTel(tel);
			v.setReip(reip);
			MemberDao.getDao().join(v);
			
			//af = new ActionForward("pro.loopy?cmd=member&sub=myPage", true);
			af = new ActionForward("pro.loopy?cmd=index", true);
		} else if (sub.equals("myPage")) {
			// false는 기존의 세션
			HttpSession session = request.getSession(false);
			System.out.println("Session: " + session.getAttribute("id"));
			// 세션이 null이면 로그인하지 않고 접근 한 경로
			if (session.getAttribute("id") == null) { // login하지 않고 접속했을 경우
				// 로그인 폼으로 강제 이동
				af = new ActionForward("pro.loopy?cmd=login&sub=loginForm", true);
			} else { // 정상적인 경우
				// select num, id, name, tel, email from mem1 where id=로그인한 세션아이디 값
				Mem1VO v = MemberDao.getDao().myPage((String) session.getAttribute("id"));
				// 상세 정보값을 myPage로 전송한다.
				request.setAttribute("v", v);
				af = new ActionForward("main/mypage", false);
			}
			// pro.loopy?cmd=member&sub=idChk&id=div
		} else if (sub.equals("idChk")) { // 아이디 중복확인 , Ajax로 요청
			String id = request.getParameter("id");
			System.out.println("Param: " + id);
			int idres = MemberDao.getDao().idCheck(id);
			System.out.println("Param:2 " + idres);
			request.setAttribute("idres", idres);
			af = new ActionForward("main/idchk", false);
		//pro.loopy?cmd=member&sub=adminMemList
		}
		return af;
	}

}

 

-3) ActionFactory에 등록해주기

	public Action getAction(String cmd) {
		// 요청값을 비교해서 다형성 적용
		Action action = null;
		if (cmd.equals("hello")) {
			action = new HelloAction();
		} else if (cmd.equals("login")) {
			action = new LoginAction();
		}else if (cmd.equals("index")) {
			action = new IndexAction();
		}else if (cmd.equals("member")) {
			action = new Mem1Action();
		}
		return action;
	}

 

회원가입 테스트 하기 

 

 

가입 후 성공 화면 (index페이지로 리다이렉트)

 

DB에 데이터 들어온 거 확인 

 

마이페이지 등등 다른 로직들도 차례대로 구현해주면 된다.