JSP서블릿 8일차

하파타카·2022년 1월 21일
0

JSP서블릿 수업

목록 보기
6/10
post-thumbnail

한 일

  • DOA
  • Todo App 만들기 -1-
  • demoDB 만들기
  • user 클래스 만들기
  • JDBCUtils 클래스로 DB연결 확인
  • UserDao 클래스 만들기
  • UserController서블릿으로 doPost메서드 작성
  • 회원가입용 본문을 만들어 heder, register, footer jsp페이지로 나누기
  • 유저 등록해보기
  • LoginBean 클래스 만들기
  • LoginDao 클래스 만들기
  • LoginController서블릿으로 doPost메서드 작성
  • 로그인용 본문을 만들기
  • 로그인 해보기

DOA (Data Access Object)

  • 개념: DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트.
    실질적으로 DB에 접근하는 트랜잭션 객체이다.
    커넥션관리의 효율성과 보안성때문에 사용됨.

  • 상세 설명: 웹페이지는 DB와 연결하기 위해 매번 커넥션 객체를 생성해야하는 비효율을 해결하기 위해 커넥션풀을 사용함.
    커넥션풀은 커넥션 객체를 미리 만들어두고 필요할때 가져다 쓴 후 다시 반환해 두는 것을 말함.
    하지만 한번에 꼭 한명의 사용자가 하나의 커넥션만 일으키는것이 아니므로 DB에 접속하는 객체를 전용으로 하나 만들어 필요한 모든페이지에서 호출하여 사용하게한다.
    이렇게 커넥션을 하나만 가져오고 그 커넥션을 가져온 객체가 모든 DB와의 연결을 해주는 것이 DOA이다.


Todo App 만들기 -1-

  • 구조

유저 등록 기능 :

  1. Create a JavaBean - User.java
  2. JDBC Connection - JDBCUtils.java
    => DB에서 user의 정보를 저장해야하므로 필요. 순수하게 연결만을 지원하는 클래스.
  3. DAO - UserDao.java => 사용자의 정보를 DB에 저장
  4. Controller - UserController.java
  5. View - register.jsp

로그인 기능 :

  1. JavaBean - LoginBean.java
  2. DAO - LoginDao.java => 사용자의 정보를 DB에서 조회
  3. Controller - LoginController.java
  4. View - login.jsp

demoDB 만들기

todo.table

users.table

=> 넣어둔 데이터는 테스트용으로, 무시해도 됨.

user 클래스 만들기

유저의 정보를 저장할 클래스를 만듦.

todoApp.model/User.class

public class User {
	private String firstName;
	private String lastName;
	private String userName;
	private String password;
	
	public User() {	}	// 기본생성성자는 java bean용으로 필요
	
	// 이 User생성자는 UserController에서 User객체를 사용할 때 4개의 매개변수가 모두 필요하므로 미리 만들어둠
	public User(String firstName, String lastName, String usertName, String password) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
		this.userName = usertName;
		this.password = password;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getUsertName() {
		return userName;
	}
	public void setUsertName(String usertName) {
		this.userName = usertName;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

JDBCUtils 클래스로 DB연결 확인

JDBCUtils 클래스를 만들어 DB와 연결이 되는지 확인한다.
연결만을 확인하는 과정이므로 따로 작업은 없음.
=> 등록기능은 UserDao.class에서

todoApp.utils/JDBCUtils.class

// 간단히 연결을 사용하기위해 모두 static으로 선언
// demo 데이터베이스 사용, useSSL=false : SSL인증을 사용하지 않음
private static String jdbcURL = "jdbc:mysql://localhost:3306/demo?useSSL=false";
private static String jdbcUsername = "root";
private static String jdbcPassword = "1234";

public static void main(String[] args) {
	Connection conn = getConnection();
}

// DB연결을 위한 객체 선언
public static Connection getConnection() {
	
	Connection conn = null;
	
	try {
		Class.forName("com.mysql.jdbc.Driver");	// 0번. 드라이버 로드
		conn = DriverManager.getConnection(jdbcURL, jdbcUsername, jdbcPassword);	// 1번. 
		// jdbcURL, jdbcUsername, jdbcPassword을 매개변수로 커넥션함 (이 세가지는 위에 private static으로 선언해둠)
		
	} catch (ClassNotFoundException e) {
		System.out.println("드라이버 클래스를 찾지못함...");
	} catch (SQLException e) {
		System.out.println("SQL문 에러발생!");
	}
	// DB연결 성공
	System.out.println("DB연결 테스트 성공!");	// DB와 '연결'만 성공되었는지 확인. 작업은 안했음.
	return null;	// DB에 연결하여 커넥션을 받아옴
}


=> 다른 문구는 나오면 안됨!
=> 연결확인이 완료되면 main메서드는 삭제하고, return값도 1로 변경해준다.

UserDao 클래스 만들기

DAO는 DB의 데이터를 직접 조회, 조작하는 기능을 함.
이 클래스는 유저정보를 등록하는 기능을 하도록 만듦.

todoApp.dao/UserDao.class

// 유저 입력 => DB에 유저데이터를 입력
// ↓여기서 User는 todoApp.model에 내가 만들어둔 User임
public int registerUser(User user) {	// 쿼리문 insert의 결과는 1행이므로, 1이 리턴됨. 아니면 0 이하가 리턴되나, 0이하는 비정상값으로 에러발생.
	String INSERT_USER_SQL = "insert into users(firstName,lastName,userName,password) "
			+ "values (?, ?, ?, ?)";
	int result = 0;
	
	try {
		Connection conn = JDBCUtils.getConnection(); // 우변은 JDBCUtils클래스에 내가 만들어둔 getConnection()메서드를 말함
		PreparedStatement pstmt = conn.prepareStatement(INSERT_USER_SQL);
		pstmt.setString(1, user.getFirstName());	// 첫번째 ?에 user에서 FirstName속성값을 가져와 넣음
		pstmt.setString(2, user.getLastName());
		pstmt.setString(3, user.getUsertName());
		pstmt.setString(4, user.getPassword());		// pstmt 준비완료 (모든 ?를 채운 상태)
		
		result = pstmt.executeUpdate();	// 결과가 없는 업데이트, 삭제, 입력 등은 쿼리 업데이트를 해줬을때 영향을 받은 행의 갯수가 리턴됨(결과가 있는 select문은 약간 형태가 다름)
		
	} catch (SQLException e) {
		System.out.println("SQL 입력 에러 발생...");
	}
	return 1;
}

UserController서블릿에 doPost()메서드 만들기

가입하기 페이지에서 doPost방식으로 유저정보를 받아왔을 때 처리할 메서드 작성.
UserController서블릿의 주소는 /register 로 변경.

todoApp.controller/UserController.java(servlet)

@WebServlet("/register")
public class UserController extends HttpServlet {
	private static final long serialVersionUID = 1L;

	private UserDao userDao; // 아래서 계속 사용되므로 전역객체를 선언해줌
	
	@Override
	public void init() throws ServletException {
		// 이 메서드는 서블릿이 만들어질 때 한 번만 실행됨
		userDao = new UserDao();	// UserDao클래스를 메서드로 실행해 userDao에 결과를 리턴받아옴
	}

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

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 유저 입력시 post로 입력 데이터를 전달함
		request.setCharacterEncoding("UTF-8"); // 한글설정
		// parameter로 성, 이름, 유저, 비밀번호를 입력받음 
		String firstName = request.getParameter("firstName");
		String lastName = request.getParameter("lastName");
		String userName = request.getParameter("userName");
		String password = request.getParameter("password");
		
		User user = new User(firstName, lastName, userName, password);
		// DB에 위의 user를 입력한다.
		
		int result = userDao.registerUser(user);	// 1이 리턴되면 성공, 0이 리턴되면 에러
		if(result == 1) {
			System.out.println("회원등록 완료!");
		}
		// 화면을 보여주기 (register.html 페이지를 보여주기)
//		request.getRequestDispatcher("register.jsp").forward(request, response);  한 줄로 작성했을때
		RequestDispatcher dispatcher = request.getRequestDispatcher("register/Register.jsp");
		dispatcher.forward(request, response); 	// 위의 한 줄코드를 두줄로 작성했을 때
	}
}
  • init메서드내부에 userDao = new UserDao(); 를 작성한 이유는 사용은 doPost메서드에서 하고있지만 doPost메서드에 작성해두면 정보를 받아올 때마다 매번 생성하게 되는데 이게 비효율적이기 때문.
  • init메서드는 서블릿이 생성될 때 한번만 실행되는데, 서블릿이 생성되는것은 서블릿 생성주기를 참고한다.

서블릿 생명주기
1. 서블릿 초기화 init()
서블릿 실행 시, 초기에 한 번만 실행되는 메서드.
2. 요청, 응답 service()
사용자의 요청에 따라 스레드 단위로 실행되는 메서드로, 여러번 실행된다.
service()메서드를 통해 doGet(), doPost()메서드가 호출됨.
3. 서블릿 종료 destroy()
컨테이너로부터 종료요청이 왔을 때 한 번만 실행되는 메서드.
서블릿이 종료될 때 처리할 작업이 있으면 destroy()메서드를 오버라이딩해서 구현한다.

header와 footer는 가입하기 페이지가 아닌 다른페이지에서도 반복될 내용이므로 파일을 나누어 본문만 바꿔 페이지를 작성할 수 있도록 한다.

register/Register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      rel="stylesheet"
      href="<%=request.getContextPath()%>/css/bootstrap.min.css"
    />
    <link rel="stylesheet" href="<%=request.getContextPath()%>/css/style.css" />
    <title>유저 등록 페이지</title>
  </head>
  <body>
    <jsp:include page="../common/header.jsp" />
    <!-- nav bar 끝 -->
    <!-- 본문 시작 -->
    <div class="container">
      <h2>유저 등록</h2>
      <div class="col-md-6">
        <div class="alert alert-success center" role="alert">
          <p>${MESSAGE}</p>
        </div>
        <!-- input태그에 작성한 정보를 /register로 post방식으로 전송 -->
        <form action="<%=request.getContextPath()%>/register" method="post">
          <div class="form-group">
            <label for="firstName">성 : </label>
            <input type="text" class="form-control" name="firstName" required />
          </div>
          <div class="form-group">
            <label for="lastName">이름 : </label>
            <input type="text" class="form-control" name="lastName" required />
          </div>
          <div class="form-group">
            <label for="userName">id : </label>
            <input type="text" class="form-control" name="userName" required />
          </div>
          <div class="form-group">
            <label for="password">password : </label>
            <input type="text" class="form-control" name="password" required />
          </div>
          <div class="form-group mt-3">
            <button type="submit" class="btn btn-outline-success">
              가입하기
            </button>
          </div>
        </form>
      </div>
    </div>
    <!-- 본문 끝 -->
    <jsp:include page="../common/footer.jsp" />

    <script src="<%=request.getContextPath()%>/js/bootstrap.bundle.min.js"></script>
  </body>
</html>

header태그와 footer태그를 통째로 잘라 각각 jsp파일로 만든 후 Register.jsp에는 해당 태그가 들어갈 자리에 파일을 링크해준다.
css와 js파일 링크는 모든 경로를 상대경로(예- "../js/bootstrap.bundle.min.js")로 작성하면 경로에러가 발생할 수 있으므로 request.getContextPath()를 이용해 현재 경로를 입력하고 나머지 주소를 이어서 작성한다.

common/header.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<header>
	<nav class="navbar navbar-expand-lg navbar-dark"
		style="background-color: cornflowerblue">
		<div class="container-fluid">
			<a class="navbar-brand" href="#">TODO APP</a>
			<button class="navbar-toggler" type="button"
				data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
				aria-controls="navbarSupportedContent" aria-expanded="false"
				aria-label="Toggle navigation">
				<span class="navbar-toggler-icon"></span>
			</button>
			<div class="collapse navbar-collapse" id="navbarSupportedContent">
				<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
					<li class="nav-item"><a class="nav-link" href="#">로그인</a></li>
					<li class="nav-item"><a class="nav-link" href="#">가입하기</a></li>
				</ul>
			</div>
		</div>
	</nav>
</header>

common/footer.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<footer class="footer font-small black mt-5">
	<div class="footer-copyright text-center py-3">
		📢 2022 Copyright:
		<String> BusanIT</String>
	</div>
</footer>

유저 등록해보기

여기까지 작성한 후 Register.jsp페이지에서 유저데이터를 입력해 DB에 저장되는지 확인해본다.


=> 가입하기 페이지에 각 데이터를 입력한 후 가입하기버튼 클릭



=> DB에 유저데이터가 성공적으로 등록되면 eclipse콘솔창에는 완료 메시지가, DB에는 데이터가 입력된 것을 확인할 수 있다.

LoginBean 클래스 만들기

로그인 체크를 할때 입력한 유저정보(id, password)를 저장할 객체.
자바 빈 객체를 사용한다.

todoApp.model

private String username;
private String password;

public String getUsername() {
	return username;
}
public void setUsername(String username) {
	this.username = username;
}
public String getPassword() {
	return password;
}
public void setPassword(String password) {
	this.password = password;
}

LoginDao 클래스 만들기

입력받은 데이터를 DB에 쿼리를 보내 결과를 받아오는 메서드를 만든다.

todoApp.dao/LoginDao.class

// return을 boolean으로 받으며, DB에 계정이 있으면 ture 없으면 false
public boolean validate(LoginBean loginBean) {
	boolean status = false; // 체크해서 없으면 false
	
	Connection conn = JDBCUtils.getConnection();	// DB연결함
	String sql = "SELECT * FROM users WHERE userName = ? AND password = ?";
	
	try {
		PreparedStatement pstmt = conn.prepareStatement(sql);
		pstmt.setString(1, loginBean.getUsername());	// 첫번째 ?에 loginBean클래스의 getUsername()메서드로 리턴받은 값을 넣음
		pstmt.setString(2, loginBean.getPassword());	// sql문 준비완료
		
		ResultSet rs = pstmt.executeQuery();	// 쿼리문 실행한 값을 ResultSet의 객체 rs에 저장 
		status = rs.next(); // 결과 행이 있으면 true, 없으면 false(next()가 rs의 값을보고 둘중하나리턴해줌)
		
	} catch (SQLException e) {
		System.out.println("SQL 로그인 에러");
	}
	return status;
}

LoginController서블릿으로 doPost메서드 작성

todoApp.controller/LoginController.java(servlet)

@WebServlet("/login")
public class LoginController extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	private LoginDao loginDao;	// 로그인 체크 Dao객체
	
	@Override
	public void init() {
		// 서블릿이 생성될 때 실행되는 init()메서드를 오버라이드
		loginDao = new LoginDao();
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8"); // 입력받을 때 한글설정
		response.setContentType("text/html;charset=UTF-8");	// 출력할 때 한글설정
		// id, password를 parameter로 입력받기
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		LoginBean loginBean = new LoginBean();
		loginBean.setUsername(username);
		loginBean.setPassword(password);

		// 로그인이 성공했는지 체크
		if (loginDao.validate(loginBean)) {	// true면 계정있음. 로그인됨 => 할일 페이지로 forward
			System.out.println("로그인 성공~!");
			RequestDispatcher dispatcher = request.getRequestDispatcher("todo/todo-list.jsp");
			dispatcher.forward(request, response);
		}else { // false면 계정없음. 로그인 실패 => 
			System.out.println("로그인 실패...");
			HttpSession session = request.getSession();
			session.setAttribute("user", username);		// username은 session에 저장해 다시 보내줌 (비밀번호만 틀렸을때 재입력이 편하게)
			session.setAttribute("message", "Login Fail 로그인에 실패했습니다...");
			response.sendRedirect("login/login.jsp");	// send는 페이지 새로열기이므로 모든 입력데이터가 사라짐
		}
	}
}

init()메서드를 사용하는 이유는 위의 UserController서블릿과 동일함.

로그인용 본문을 만들기

header와 footer는 링크처리, 로그인은 id와 password만 받으면 되므로 회원가입 페이지에서 그 부분만 수정한다.

login/login.jsp

<!-- 본문 시작 -->
<div class="container">
  <div class="row mt-3">
    <div class="col-md-6 mx-auto">
      <h2>유저 등록</h2>
      <div class="alert alert-success center" role="alert">
        <p>${MESSAGE}</p>
      </div>
      <!-- input태그에 작성한 정보를 /register로 post방식으로 전송 -->
      <form action="<%=request.getContextPath()%>/register" method="post">
        <div class="form-group mt-2">
          <label for="firstName">성 : </label>
          <input type="text" class="form-control" name="firstName" required />
        </div>
        <div class="form-group mt-2">
          <label for="lastName">이름 : </label>
          <input type="text" class="form-control" name="lastName" required />
        </div>
        <div class="form-group mt-2">
          <label for="userName">id : </label>
          <input type="text" class="form-control" name="userName" required />
        </div>
        <div class="form-group mt-2">
          <label for="password">password : </label>
          <input type="text" class="form-control" name="password" required />
        </div>
        <div class="form-group mt-3">
          <button type="submit" class="btn btn-outline-success">가입하기</button>
        </div>
      </form>
    </div>
  </div>
</div>
<!-- 본문 끝 -->

로그인 해보기

  • 로그인 성공
    todo/todo-list.jsp페이지로 forward한다. (아직 만들지 않은 페이지이므로 404에러발생)

  • 로그인 실패

    로그인에 실패하면 LoginController서블릿의 doPost메서드에 의해 login.jsp페이지로 이동하는데, 이때 forward가 아니라 send로 데이터를 가지고 이동하는게 아닌 단순이동으로 처리된다.
    메시지와 id는 send로 이동할때 소실되므로 따로 세션에 저장하여 로그인에 실패해 다시 login.jsp페이지로 이동해도 그대로 값이 저장되어있도록 해주었다.(로그인에 실패했음을 알리고 비밀번호만 틀렸을 때 재입력이 편리하도록)

profile
천 리 길도 가나다라부터

0개의 댓글