카테고리 없음

JSP와 MVC패턴 TODO 프로젝트

개발자공부 2024. 7. 9. 18:34

알아둬야 하는 지식 복습

 

1. 롬북 어노테이션

https://devnote0203.tistory.com/100

 

lombok annotation

🔔 lombok 라이브러리를 사용하는 이유Getter, Setter, Constructor 등을 자동으로 생성해준다. 그러나 무한루프에 빠지거나 불필요한 getter 및 setter도 포함하기 때문에 사용하지 않는 경우도 있다. ■ @G

devnote0203.tistory.com

2. 빌드 패스에 라이브러리 추가하는 방법


 

사전 기반 지식

1. MVC 패턴

MVC 패턴과 모델구조 + 히카리cp 필요하다

 


시나리오 코드

 

더보기

TestController

package com.tenco.controller;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

import com.tenco.model.UserDAO;
import com.tenco.model.UserDAOImpl;
import com.tenco.model.UserDTO;

@WebServlet("/test/*")
public class TestController extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private UserDAO userDAO;
       
    public TestController() {
        super();
    }

    @Override
    public void init() throws ServletException {
    	userDAO = new UserDAOImpl();
    }
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String action = request.getPathInfo();
		
		switch (action) {
        // 해당 url 요청이 들어온다면
		case "/byId":
			
			// http://localhost:8080/mvc/test/byId
			// userDAO.getUserById(1);
			// userDAO.getUserByUsername("홍길동");
			// userDAO.getAllUsers();
			
			UserDTO dto = UserDTO.builder().password("888").email("a@nate.com").build();
			int count = userDAO.updateUser(dto, 3);
			System.out.println(count);
			
			break;

		default:
			break;
		}
	
	}

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

}

 

TodoController

package com.tenco.controller;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

public class TodoController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    public TodoController() {
        super();
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().append("Served at : ").append(request.getContextPath());
		// getContextPath() --> 프로젝트 경로만 가져온다. 파일경로X
	}

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

}

 

UserController

package com.tenco.controller;

import java.io.IOException;

import com.tenco.model.UserDAO;
import com.tenco.model.UserDAOImpl;
import com.tenco.model.UserDTO;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

// 주소 설계
// http://ip:포트/rootContext/urlmapping
// http://localhost:8080/mvc/user
// /user/* --> http://localhost:8080/mvc/user/xxx 가능
@WebServlet("/user/*")
public class UserController extends HttpServlet {
	private static final long serialVersionUID = 1L;
    private UserDAO userDAO;   
	
    public UserController() {
        super();
    }

    @Override
    public void init() throws ServletException {
    	userDAO = new UserDAOImpl();
    }
    
    // GET 방식으로 들어올 때
    // http://localhost:8080/mvc/user/signUp
    // http://localhost:8080/mvc/user/signIn
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// user/signIn --> 로그인 페이지
		// user/signUp --> 회원가입 페이지
		
		String action = request.getPathInfo();
		// getPathInfo() --> 클라이언트가 요청 시 보낸 URL과 관련된 추가 경로 정보를 반환한다.
		switch (action) {
		case "/signIn":
			// 로그인 페이지로 보내는 동작 처리
			request.getRequestDispatcher("/WEB-INF/views/signIn.jsp").forward(request, response);
			// getRequestDispatcher() --> 클라이언트로부터 최초에 들어온 요청을 JSP/Servlet 내에서 
			// 원하는 자원으로 요청을 넘기는 역할을 수행하거나 특정 자원에 처리를 요청하고 처리 결과를 얻어오는 기능을 수행하는 클래스이다.
			break;
		case "/signUp":
			// 회원 가입 페이지로 보내는 동작 처리
			request.getRequestDispatcher("/WEB-INF/views/signUp.jsp").forward(request, response);
			break;
		default:
			response.sendError(HttpServletResponse.SC_NOT_FOUND);
			break;
		}
	}

	// http://localhost:8080/mvc/views/todoForm.jsp
	// --> 서블릿을 통해 요청한 것이 아니라 해당 경로로 바로 이동했다.
	// 그래서 보안 폴더인 web-inf에 views폴더(jsp포함)를 옮겨준다.
	
	// 로그인 기능 요청(자원에 요청 -- GET 방식 예외적인 처리 _ 보안)
	// POST 요청시 - 로그인 기능 구현, 회원가입 기능 구현
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String action = request.getPathInfo();
		switch (action) {
		case "/signIn":
			
			break;
		case "/signUp":
			signUp(request, response);
			break;
		default:
			response.sendError(HttpServletResponse.SC_NOT_FOUND);
			break;
		}
	
	}

	/**
	 * 회원가입 기능
	 * @param request
	 * @param response
	 * @throws IOException 
	 * @throws ServletException 
	 */
	private void signUp(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		// 임시로 인증 검사 필요 없는 기능
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		String email = request.getParameter("email");
		
		// 방어적 코드 작성(username 확인한 것)
		if(username == null || username.trim().isEmpty()) {
			request.setAttribute("errorMessage", "사용자 이름을 입력하시오");
			request.getRequestDispatcher("/WEB-INF/views/signUp.jsp").forward(request, response);
			return;
		}
		
		
		// 방어적 코드 작성 (password 확인)
		
		// 방어적 코드 작성 (email 확인)
		UserDTO userDTO = UserDTO.builder().username(username).password(password).email(email).build();
		
		int resultRowCount = userDAO.addUser(userDTO);
		
		System.out.println(resultRowCount);
		
		if(resultRowCount == 1) {
			response.sendRedirect("user/signIn?message=success");
		}else {
			response.sendRedirect("user/signUp?message=error");
		}
	}

}

 

더보기

UserDAO

package com.tenco.model;

import java.util.List;

public interface UserDAO {

	// insert 쿼리는 단순해서 보통 리턴타입으로 void를 사용한다.
	int addUser(UserDTO userDTO);
	UserDTO getUserById(int id);
	UserDTO getUserByUsername(String username);
	List<UserDTO> getAllUsers();
	// 왜 파라미터 값으로 DTO와 id 값을 받는걸까?
	// 내 정보를 확인하기 위해서는 권한이 필요하다.
	// 그 권한을 인증하기 위해서는 세션 ID도 필요하다.
	int updateUser(UserDTO user, int principalId); 
	int deleteUser(int id); // 인증검사를 실시해야 한다.
}

 

UserDAOImpl

package com.tenco.model;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import javax.naming.InitialContext;
import javax.sql.DataSource;

public class UserDAOImpl implements UserDAO {

	private DataSource dataSource;

	public UserDAOImpl() {

		// jdbc 연결
		try {
			InitialContext ctx = new InitialContext();
			dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/MyDB");
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	@Override
	public int addUser(UserDTO userDTO) {
		// 실행 완료된 행 개수를 알고 싶다.
		int resultCount = 0;

		// 회원가입시 insert 쿼리문
		String sql = " INSERT INTO users(username, password, email) VALUES(? , ? , ?) ";

		// 데이터베이스 커넥션
		try (Connection conn = dataSource.getConnection()) {
			// 트랜잭션 시작
			conn.setAutoCommit(false);
			try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
				pstmt.setString(1, userDTO.getUsername());
				pstmt.setString(2, userDTO.getPassword());
				pstmt.setString(3, userDTO.getEmail());
				// executeUpdate() --> int 값으로 반환된다.
				resultCount = pstmt.executeUpdate();
				
				// 트랜잭션 커밋 - 물리적인 장치에 데이터 영구히 남기기
				conn.commit();
				
				System.out.println("resultCount : " + resultCount);
			} catch (Exception e) {
				conn.rollback();
				e.printStackTrace();
			} // end of PreparedStatement

		} catch (Exception e) {
			System.out.println("커넥션 개체 오류");
			e.printStackTrace();
		} // end of connection

		return resultCount;
	}

	/**
	 * 깊은 공부를 하지 않았다면
	 * SELECT에서는 일단 트랜잭션 처리를 하는 것을 이해하지 못한다.
	 * 그러니 일단 SELECT에서는 트랜잭션을 사용하지 말자.
	 * 
	 * 하지만 팬텀 리드 현상 때문이라면 사용하는 것이 좋다.
	 * (정합성을 위해서 처리하는 것도 옳은 방법이다.)
	 */
	
	@Override
	public UserDTO getUserById(int id) {
		String sql = " SELECT * FROM users WHERE id = ? ";
		UserDTO userDTO = null;
		
		try (Connection conn = dataSource.getConnection()) {
			try (PreparedStatement pstmt = conn.prepareStatement(sql)){
				pstmt.setInt(1, id);
				ResultSet rs = pstmt.executeQuery();
				// 특정 유저를 찾는 것이니 단일 행이다. --> if 사용
				if(rs.next()) {
					userDTO = new UserDTO();
					userDTO.setId(rs.getInt(id));
					userDTO.setUsername(rs.getString("username"));
					userDTO.setPassword(rs.getString("password"));
					userDTO.setEmail("email");
					userDTO.setCreatedAt("created_at");
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println(userDTO.toString());
		return userDTO;
	}
	

	@Override
	public UserDTO getUserByUsername(String username) {
		
		String sql = " SELECT * FROM users WHERE username = ? ";
		UserDTO userDTO = null;
		
		try (Connection conn = dataSource.getConnection()){
			try (PreparedStatement pstmt = conn.prepareStatement(sql)){
				pstmt.setString(1, username);
				ResultSet rs = pstmt.executeQuery();
				if(rs.next()) {
					userDTO = new UserDTO();
					userDTO.setId(rs.getInt("id"));
					userDTO.setUsername(rs.getString("username"));
					userDTO.setPassword(rs.getString("password"));
					userDTO.setCreatedAt(rs.getString("created_at"));
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println(userDTO.toString());
		return userDTO;
	}

	@Override
	public List<UserDTO> getAllUsers() {
		
		String sql = " SELECT * FROM users ";
		// 자료구조를 사용할 때 일단 생성시키자.
		// 메모리 낭비가 약간 있는 방법이지만 그리 크지 않으니 일단 사용한다.
		List<UserDTO> list = new ArrayList();
		
		try (Connection conn = dataSource.getConnection()){
			try (PreparedStatement pstmt = conn.prepareStatement(sql)){
				ResultSet rs = pstmt.executeQuery();
				
				// 모든 이용자를 검색하는 것이기 때문에 다중행이다.
				// --> while 반복문 사용
				while(rs.next()) {
					UserDTO userDTO = new UserDTO();
					userDTO.setId(rs.getInt("id"));
					userDTO.setUsername(rs.getString("username"));
					userDTO.setPassword(rs.getString("password"));
					userDTO.setEmail(rs.getString("email"));
					userDTO.setCreatedAt(rs.getString("created_at"));
					list.add(userDTO);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println(list.toString());
		return list;
	}

	@Override
	public int updateUser(UserDTO user, int principalId) {
		
		int rowCount = 0;
		String sql = " UPDATE users SET password = ?, email = ? WHERE id = ? ";
		
		try (Connection conn = dataSource.getConnection()){
			conn.setAutoCommit(false);
			try (PreparedStatement pstmt = conn.prepareStatement(sql)){
				pstmt.setString(1, user.getPassword());
				pstmt.setString(2, user.getEmail());
				pstmt.setInt(3, principalId);
				rowCount = pstmt.executeUpdate();
				conn.commit();
			} catch (Exception e) {
				conn.rollback();
				e.printStackTrace();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return rowCount;
	}

	@Override
	public int deleteUser(int id) {
		
		int rowCount = 0;
		String sql = " DELETE FROM users WHERE id = ? ";
		
		try (Connection conn = dataSource.getConnection()){
			conn.setAutoCommit(false);
			try (PreparedStatement pstmt = conn.prepareStatement(sql)){
				pstmt.setInt(1, id);
				pstmt.executeUpdate();
				conn.commit();
			} catch (Exception e) {
				conn.rollback();
				e.printStackTrace();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return rowCount;
	}

}

 

UserDTO

package com.tenco.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * DTO 클래스
 * 데이터를 변환하거나 담는 개념이며,
 * 메서드를 사용할 수 있다.
 */

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class UserDTO {

	private int id;
	private String username;
	private String password;
	private String email;
	private String createdAt;
	
	// 필요하다면 메서드 정의 가능
}

 

더보기
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource
name="jdbc/MyDB"
auth="Container"
type="javax.sql.DataSource"
factory="com.zaxxer.hikari.HikariJNDIFactory"
uniqueResourceName="MyDB"
minimumIdle="5"
maximumPoolSize="10"
connectionTimeout="30000"
idleTimeout="60000"
maxLifetime="180000"
jdbcUrl="jdbc:mysql://localhost:3306/db_todo2?serverTimezone=Asia/Seoul"
driverClassName="com.mysql.cj.jdbc.Driver"
username="root"
password="asd123"
/>
</Context>

 

더보기

views > signIn.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인</title>
</head>
<body>
	<h1>로그인 화면입니다.</h1>
</body>
</html>

 

views > signUp.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입</title>
</head>
<body>
	<h1>회원가입 화면입니다.</h1>
	<%
		String eMessage = (String)request.getAttribute("errorMessage");
        // getAttribute 
        // Object 타입이다. 모든 객체를 담을 수 있고, 클래스 객체를 받을 수 있다.
        // 해당 메소드를 이용하여 세션 변수를 검색할 수 있다.
        // setAttribute를 통해 값을 설정해주지 않으면 null값을 반환받는다.
        
        // getParameter()와 getAttribute() 차이
        // getParameter()는 클라이언트로부터 HTML 값을 받아올 때
        // getAttribute()는 JSP, Servlet과 값을 주고 받는다.
	%>
</body>
</html>

 

실행결과

더보기
http://localhost:8080/mvc/test/byId

 

크롬 talented api 사용
http://localhost:8080/mvc/user/signUp > talented api로 post 해보기