비밀번호 변경 기능 개요
본 프로젝트에서는 사용자 비밀번호 변경 기능을 구현한다. 이 기능은 데이터베이스와 직접적으로 연동되어야 하며, 보안상 비밀번호를 평문으로 저장하지 않고 해시 처리하는 것이 일반적인做法이다. 그러나 본 튜토리얼에서는 기본적인密码 변경 기능의 구현 과정에 집중한다.
1. 프론트엔드 링크 설정
먼저 사용자가 비밀번호 변경 페이지에 접근할 수 있는 메뉴 링크를 추가해야 한다. JSP 페이지에서 다음과 같이 앵커 태그를 추가한다.
<li><a href="${pageContext.request.contextPath }/jsp/passwordModify.jsp">비밀번호 변경</a></li>
2. 아키텍처 설계 원칙
프로젝트 구현 시에는 일반적으로 하위 계층부터 상위 계층으로 진행한다. 즉, DAO(Data Access Object) Layer부터 시작하여 Service Layer, 그리고 마지막으로 Servlet Layer를 구현하는 순서로 진행한다. 이러한 방식은 각 계층 간의 의존성을 명확히 하고 코드 재사용성을 높인다.
3. DAO 인터페이스 정의
데이터베이스 접근을 위한 인터페이스를 먼저 선언한다. 이 인터페이스는 비밀번호 변경에 필요한 메서드를 정의한다.
// 현재 사용자의 비밀번호를 수정한다
public int modifyUserPassword(Connection connection, int userId, String newPassword) throws SQLException;
4. DAO 구현 클래스 작성
인터페이스를 구현하는 클래스를 작성한다. 이 클래스에서는 실제 SQL 문을 실행하여 데이터베이스의 비밀번호를 업데이트한다.
package com.hospital.dao.user;
import com.hospital.dao.BaseDao;
import com.hospital.pojo.User;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDaoImpl implements UserDao {
// 데이터베이스에서 사용자 정보 조회
public User getLoginUser(Connection connection, String loginId) throws SQLException {
PreparedStatement pstm = null;
ResultSet rs = null;
User user = null;
if (connection != null) {
String sql = "select * from smbms_user where userCode=?";
Object[] params = {loginId};
rs = BaseDao.execute(connection, pstm, rs, sql, params);
if (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setUserCode(rs.getString("userCode"));
user.setUserName(rs.getString("userName"));
user.setUserPassword(rs.getString("userPassword"));
user.setGender(rs.getInt("gender"));
user.setBirthday(rs.getDate("birthday"));
user.setPhone(rs.getString("phone"));
user.setAddress(rs.getString("address"));
user.setUserRole(rs.getInt("userRole"));
user.setCreatedBy(rs.getInt("createdBy"));
user.setCreationDate(rs.getTimestamp("creationDate"));
user.setModifyBy(rs.getInt("modifyBy"));
user.setModifyDate(rs.getTimestamp("modifyDate"));
}
BaseDao.closeResource(null, pstm, rs);
}
return user;
}
// 사용자 비밀번호 수정
public int modifyUserPassword(Connection connection, int userId, String newPassword) throws SQLException {
PreparedStatement pstm = null;
int resultCount = 0;
if (connection != null) {
String sql = "update smbms_user set userPassword = ? where id = ?";
Object params[] = {newPassword, userId};
resultCount = BaseDao.execute(connection, pstm, sql, params);
BaseDao.closeResource(null, pstm, null);
}
return resultCount;
}
}
5. Service 인터페이스 정의
비즈니스 로직을 처리하기 위한 Service 계층을 구성한다. Servlet에서 직접 DAO에 접근하지 않고 Service를 경유하여 처리한다.
// 사용자 ID를 기반으로 비밀번호를 변경한다
public boolean modifyPassword(int userId, String newPassword);
6. Service 구현 클래스 작성
Service 인터페이스를 구현하는 클래스를 작성한다. 여기서 트랜잭션 관리가 이루어진다.
public boolean modifyPassword(int userId, String newPassword) {
Connection connection = null;
boolean success = false;
try {
connection = BaseDao.getConnection();
if (userDao.modifyUserPassword(connection, userId, newPassword) > 0) {
success = true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
BaseDao.closeResource(connection, null, null);
}
return success;
}
7. Servlet 컨트롤러 구현
사용자의 요청을 처리하고 응답을 반환하는 Servlet을 구현한다. 여기서는 클라이언트로부터 새 비밀번호를 받아 Service 계층으로 전달한다.
package com.hospital.servlet.user;
import com.hospital.pojo.User;
import com.hospital.service.user.UserServiceImpl;
import com.hospital.util.Constants;
import com.mysql.jdbc.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("method");
if ("savepwd".equals(action) && action != null) {
this.changePassword(req, resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
public void changePassword(HttpServletRequest req, HttpServletResponse resp) {
Object userSession = req.getSession().getAttribute(Constants.USER_SESSION);
String newPassword = req.getParameter("newpassword");
boolean flag = false;
if (userSession != null && !StringUtils.isNullOrEmpty(newPassword)) {
UserServiceImpl userService = new UserServiceImpl();
flag = userService.modifyPassword(((User) userSession).getId(), newPassword);
if (flag) {
req.setAttribute("message", "비밀번호가 성공적으로 변경되었습니다. 새 비밀번호로 다시 로그인해 주세요.");
req.getSession().removeAttribute(Constants.USER_SESSION);
} else {
req.setAttribute("message", "비밀번호 변경에 실패했습니다.");
}
} else {
req.setAttribute("message", "새 비밀번호 값이 올바르지 않습니다.");
}
try {
req.getRequestDispatcher("pwdmodify.jsp").forward(req, resp);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
8. Servlet 매핑 등록
web.xml 파일 또는 어노테이션을 사용하여 Servlet을 등록하고 URL 패턴을 매핑한다. 이렇게 해야 클라이언트의 요청이 올바른 Servlet으로 전달된다.
9. 테스트 및 디버깅
구현完成后 테스트를 수행한다. 만약 비밀번호 변경이 실패한다면 다음과 같은 원인을 확인해야 한다:
- 데이터베이스 연결이 정상적으로 수립되었는지 확인
- SQL 문법이 올바른지 검증
- 파라미터 값이 정확히 전달되고 있는지 로그를 통해 확인
- 트랜잭션이 제대로 커밋되었는지 확인
가장 흔한 문제는 DAO 层에서 SQL 실행 시 문제가 발생하는 것이다.PreparedStatement의 파라미터 순서와 값이 제대로 매핑되었는지 반드시 확인해야 한다.