Project/javachip

[2nd Project] 아이디, 비밀번호 찾기/재설정(이메일 인증, JSP, STS)

ParkYeseul 2024. 10. 22. 17:06

JSP와 스프링프레임워크로

로그인 창에서 아이디, 비밀번호 찾기 구현하기
아이디를 찾고, 비밀번호는 이메일 인증을 통해서 인증된 사용자가 비밀번호를 재설정할 수 있다.
이 기능은 이메일 회원가입을 위한 기능!

(메론빵과 소보로와 마라탕이랑 오레오랑 계란과자의 효과인가 오늘 무슨 일로 개발이 척척 되는거지? 더 많이 먹어야겠다.)

 

뽈뽈뽈 로그인 창

 

 

 

 

 

 

🍼아이디 찾기

//회원정보 찾기
@PostMapping("/findIdProcess")
public String findIdProcess(@RequestParam("m_registration_type") String registrationType,
                            @RequestParam("m_nickname") String nickname, 
                            Model model) {

    //서비스 호출하여 아이디 찾기
    String foundId = m_memberServiceImpl.findIdByRegistrationAndNickname(registrationType, nickname);

    //찾은 아이디를 JSP 페이지에 전달
    if(foundId != null) {
        model.addAttribute("foundId",foundId);
    }else {
        model.addAttribute("errorMessage", "해당 정보로 등록된 아이디가 없습니다.");
    }

    //아이디 찾기 결과 페이지로 이동
    return "redirect:/Member/login";
}

}

@RequestParam("m_registration_type") String registrationType
m_registration_type이라는 요청 파라미터의 값을 받아서 registrationType 변수에 저장
m_registration_type" 부분은 **클라이언트(즉, JSP 폼이나 URL)**에서 전송되는 파라미터 이름과 일치해야 한다. 

 

<form action="/findIdProcess" method="post">
    <select name="m_registration_type">
        <option value="google">구글</option>
        <option value="facebook">페이스북</option>
    </select>
</form>

name="m_registration_type
컨트롤러에서 **@RequestParam("m_registration_type")**으로 해당 값을 받는다.

 

DB 테이블의 컬럼명이나 VO 필드명과는 관련이 없고, 이 값은 사용자가 폼이나 URL로 전달한 파라미터 이름이다.


@RequestParam("m_nickname") String nickname
마찬가지로 닉네임 파라미터를 받아서 nickname 변수에 값을 저장

 

파라미터 전달
사용자가 findId URL로 GET 또는 POST 요청을 보낼 때,

해당 파라미터 값들이 @RequestParam으로 매핑되어 컨트롤러 메서드에 전달

 

 

m_memberServiceImpl 객체의 findIdByRegistrationAndNickname

메서드를 호출하여 DB에서 회원의 아이디를 조회하는 작업을 수행

 

foundId

아이디 타입을 반환하고, 여기에 반환된 값이 저장된다.

 

 

Model 객체는 서버에서 JSP로 데이터를 전달하는데 사용

즉, 서버에서 처리된 데이터를 JSP 파일에서 출력할 수 있도록 데이터를 담아주는 역할을 한다.
예를 들어, 컨트롤러에서 특정 값(여기서는 찾은 아이디)을 찾았다면,

그 값을 Model 객체에 담아 JSP로 보내고, JSP에서 그 값을 **표현 언어(EL)**를 통해 출력


찾은 아이디를 모델에 추가 = 서버에서 찾은 아이디를 JSP 페이지로 전달하여

그 페이지에서 아이디를 출력할 수 있도록 하는 작업

 



컨트롤러에서 메소드를 생성하면 자연스럽게 service -> serviceImpl로 가기 때문에 큰 어려움은 없으나

DAO에서 적을 때 2개를 가져와야 하기 때문에 MAP함수를 사용했다.

public String findIdByRegistrationAndNickname(String registrationType, String nickname) {
    // 파라미터를 Map으로 묶기
    Map<String, Object> params = new HashMap<>();
    params.put("registrationType", registrationType);
    params.put("nickname", nickname);

    // sqlSession을 통해 파라미터를 Map으로 전달
    return sqlSession.selectOne(MAPPER + ".findIdByRegistrationAndNickname", params);
}

Map<String, Object> params = new HashMap<>()

Map은 키와 값을 쌍으로 저장하는 자료 구조이고 HashMap은 키와 값을 해시 테이블을 사용하여 저장

그래서 값을 빠르게 검색하고 추가 및 삭제 작업을 할 수 있다.

 

Map<String, Object>
String = 키 타입

Object = 값 타입

 

params.put("registrationType", registrationType);
put("키", 값): Map에 키-값 쌍을 추가하는 메서드


"registrationType": 키로 사용할 값이며, SQL 쿼리에서 참조할 이름
registrationType: 메서드의 인자로 받은 가입 경로 값이 저장

 

이 값은 나중에 MyBatis 쿼리에서 #{registrationType}로 사용된다

 

<select id="findIdByRegistrationAndNickname" resultType="String">
    SELECT m_id
    FROM member
    WHERE m_registration_type = #{registrationType}
    AND m_nickname = #{nickname}
</select>

 

#{ } 안의 값은 DAO에서 전달된 파라미터의 키 또는 VO 객체의 필드 이름과 일치


Map을 사용하는 경우: #{} 안의 값은 Map의 키 이름과 일치

 

VO 객체를 사용하는 경우: #{} 안의 값은 VO 객체의 필드 이름과 일치


#{registrationType}와 #{nickname}은 DAO나 서비스 계층에서 전달되는 Map 또는 VO 객체의 필드 이름에 따라 결정된다.

 

 

 

 


🍉비밀번호 찾기

찾기라고 읽고 재설정이라고 쓴다.

 

비밀번호는 이메일 인증을 통해서 인증 후 비밀번호 재설정이 가능하도록 한다!

이메일 인증을 하면 
비밀번호 재설정 창이 생긴다. 

신기술이다!

 

 

 

<!-- 라디오 버튼으로 폼 전환 -->
<label><input type="radio" name="findType" value="id" onclick="toggleForm()" checked> 아이디 찾기</label>
<label><input type="radio" name="findType" value="password" onclick="toggleForm()"> 비밀번호 찾기</label>

toggleForm 함수: 라디오 버튼이 클릭되면, 해당 값에 따라 아이디 찾기 폼 또는 비밀번호 찾기 폼을 표시하거나 숨김

 

 

🍵m_findId.jsp

<script type="text/javascript">
        // 라디오 버튼 선택에 따라 다른 폼을 보여줌
        
        function toggleForm() {
            var type = document.querySelector('input[name="findType"]:checked').value;
            document.getElementById("idForm").style.display = (type === 'id') ? 'block' : 'none';
            document.getElementById("passwordForm").style.display = (type === 'password') ? 'block' : 'none';
        }

        // 이메일 인증번호 전송 함수
        function email_ok(email) {
            var contextPath = "${pageContext.request.contextPath}";
            
            if (!email || email === "") {
                alert("이메일을 입력하세요.");
                return;
            }

            $.ajax({
                type: "POST",
                url: contextPath + "/mailSend",  // 이메일 인증번호 전송 URL 수정
                data: { receiver: email },
                dataType: "text",
                success: function(response) {
                    if (response === "이메일 전송 성공") {
                        alert("이메일이 성공적으로 전송되었습니다.");
                    } else {
                        alert("이메일 전송에 실패했습니다.");
                    }
                },
                error: function(xhr, status, error) {
                    console.log("AJAX 오류:", error);
                }
            });
        }

        // 인증번호 확인 함수
        function verifyCode() {
            var contextPath = "${pageContext.request.contextPath}";
            var inputCode = document.getElementById("authCode").value;
            
            $.ajax({
                type: "POST",
                url: contextPath + "/verifyCode",  // 인증번호 확인 URL 수정
                data: { authCode: inputCode, 
                },
                success: function(response) {
                    if (response === "success") {
                        alert("인증이 완료되었습니다.");
                        document.getElementById("submitBtn").disabled = false; // 비밀번호 재설정 버튼 활성화
                        document.getElementById("newPasswordSection").style.display = "block"; // 비밀번호 재설정 폼 보이기
                    } else {
                        alert("인증번호가 일치하지 않습니다.");
                    }
                },
                error: function(xhr, status, error) {
                    alert("인증번호 확인 중 오류가 발생했습니다.");
                }
            });
        }
    </script>

email_ok(email): 사용자가 이메일을 입력하고, 인증번호를 이메일로 전송

AJAX 요청을 통해 서버에서 해당 이메일로 인증번호를 발송

 

verifyCode(): 사용자가 입력한 인증번호가 맞는지 서버에서 확인

AJAX 요청을 통해 사용자가 입력한 인증번호를 서버에 보내고,

서버에서 확인된 결과에 따라 인증 성공 또는 실패 메시지를 보여준다.

 

 

🌳M_MemberController

//회원정보 찾기에서 비밀번호 재설정
@PostMapping("/resetPassword")
public String resetPassword(@RequestParam("newPassword")String newPassword,
                        @RequestParam("confirmPassword") String confirmPassword,
                        HttpSession session, Model model){

 // 세션에서 이메일 가져오기 전에 로그 출력
System.out.println("세션에서 이메일 가져오기 시도...");

//세션에서 인증된 이메일 가져오기
String m_email = (String) session.getAttribute("m_email");
System.out.println("세션에 저장된 이메일: " +m_email);

if(m_email == null) {
    model.addAttribute("errorMessage", "인증 정보가 만료되었습니다. 다시 시도해주세요");
    return "redirect:/Member/m_findId";
}



//1. 비밀번호 일치 여부확인 
if(!newPassword.equals(confirmPassword)) {
    model.addAttribute("errorMessage", "비밀번호와 비밀번호 확인이 일치하지 않습니다.");
    return "Member/m_findId";
}

//2. 비밀번호 유효성 검사
if(!m_memberServiceImpl.isValidPassword(newPassword)) {
    model.addAttribute("passwordValidationError", "비밀번호는 최소 8자 이상이어야 하며 숫자와 특수문자를 포함해야 합니다.");
    return "Member/m_findId";
}

//3. 비밀번호 업데이트 로직

try {
    boolean isUpdated = m_memberServiceImpl.updatePassword(m_email, newPassword);

    if (isUpdated) {
        return "redirect:/Member/login";  // 비밀번호 변경 성공 시 로그인 페이지로 리다이렉트
    } else {
        model.addAttribute("errorMessage", "비밀번호 변경에 실패했습니다. 다시 시도해 주세요.");
        return "Member/m_findId";  // 업데이트 실패 시 재설정 페이지로 이동
    }
} catch (Exception e) {
    e.printStackTrace();  // 에러 로그 출력
    model.addAttribute("errorMessage", "비밀번호 변경 중 오류가 발생했습니다.");
    return "Member/m_findId";  // 예외 발생 시 비밀번호 재설정 페이지로 이동
}
}

HttpSession session

서버와 클라이언트 간의 세션 정보를 관리하는 객체

= 사용자의 로그인 정보나 기타 상세 정보를 세션에 저장하고, 페이지 간에 사용자 상태를 유지할 수 있음

 

Model model

model객체는 뷰(JSP)로 데이터를 전달하기 위해 사용

= 컨트롤러에서 처리한 결과를 JSP 뷰로 전달할 때, 메세지 전달을 model에 담아 JSP에 전달할 수 있음

 

 

🚕MailSend

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

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	response.setContentType("text/html; charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        System.out.println("MailSend 서블릿 요청 수신");

        // 이메일 파라미터를 받아옴
        String email = request.getParameter("receiver");
        System.out.println("받은 이메일: " + email);  // 이메일 값을 서버 로그에 출력
        
        // 이메일 값이 null이거나 비어있는지 확인
        if (email == null || email.isEmpty()) {
            System.out.println("이메일 파라미터가 비어있습니다.");
            response.getWriter().write("이메일 파라미터가 비어있습니다.");
            return;
        }

        // 세션에 이메일 저장
        HttpSession httpSession = request.getSession();
        httpSession.setAttribute("m_email", email);  // 세션에 이메일 저장
        System.out.println("세션에 이메일 저장됨: " + email);

mailsend는 이메일 회원가입할 때 만들어 놓은 파일
일부분인데 여기서 세션에 이메일을 저장한다. 

여기서 저장된 이메일 값을 가지고 가서 이 이메일을 기준으로 비밀번호를 바꾼다.

 

 

🧵VerifyCode

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


  @Override 
  protected void service(HttpServletRequest request,
  HttpServletResponse response) throws ServletException, IOException { 
      String inputCode = request.getParameter("authCode"); // 사용자가 입력한 인증번호 
      HttpSession session = request.getSession(); 
      String sessionCode = (String) session.getAttribute("authCode"); // 세션에 저장된 인증번호

      if (inputCode != null && inputCode.equals(sessionCode)) {
      String m_email = (String) session.getAttribute("m_email");  // 세션에서 이메일 가져오기
      System.out.println("세션에 저장된 이메일: " + m_email);
      response.getWriter().write("success"); // 인증번호 일치 
      } else {
      response.getWriter().write("fail"); // 인증번호 불일치 
      } 

  }

}

사실 여기서 인증된 이메일을 가지고 세션에 저장하는게 보안상 이점이 크다.

디버깅을 보면 알겠지만, 여기서 세션에 저장을 하려고 했으나 실패했다. 계속 null값을 받아와 흑흑

원인은 지금부터 찾아봐야겠지만, 시간관계상 mailsend에서 이메일을 저장했다. 

 

 

🤾‍♀️M_MemberServiceImpl/M_MemberDAO

// M_MemberServiceImpl - 비밀번호 업데이트
@Override
public boolean updatePassword(String m_email, String newPassword) {
    int result = dao.updatePassword(m_email, newPassword); 
    return result > 0;  // 업데이트 성공 여부 반환
}

DAO
public int updatePassword(String m_email, String newPassword) {
 Map<String, Object> params = new HashMap<>();
 params.put("m_email", m_email);   
 params.put("newPassword", newPassword);  // 새 비밀번호

    // SQL Mapper에 파라미터 전달하여 비밀번호 업데이트 실행
    return sqlSession.update(MAPPER+".updatePassword", params);
}

 

위 아이디 찾기랑 동일하다!

 

 

🥶M_MemberMapper

<!--회원정보 찾기(Password) -->
	<update id="updatePassword" parameterType="map">
	 UPDATE m_member 
	 SET m_password = #{newPassword}
	 WHERE m_email = #{m_email}
	
	</update>

파라미터 타입이 Map인 이유

여러 개의  파라미터를 쉽게 전달하기 위해서! 

 

 

 

 

👑m_findId.jsp

<!-- 비밀번호 찾기 폼 -->
<div id="passwordForm" style="display:none;">
    <form action="${pageContext.request.contextPath}/Member/sendVerificationCode" method="post">
        <!-- 이메일 입력 -->
        <input type="email" id="email" name="m_email" placeholder="이메일을 입력하세요">
        <button type="button" onclick="email_ok(document.getElementById('email').value)">인증번호 전송</button>
    </form>

    <!-- 인증번호 입력 및 확인 -->
    <div id="verificationSection" style="margin-top: 20px;">
        <input type="text" id="authCode" placeholder="인증번호를 입력하세요">
        <button type="button" onclick="verifyCode()">인증번호 확인</button>
        <p id="verificationStatus"></p> <!-- 인증 상태를 표시할 영역 -->
    </div>

    <!-- 비밀번호 재설정 폼 (인증 성공 시에만 보임) -->
    <div id="newPasswordSection" style="display:none;">
        <form action="${pageContext.request.contextPath}/Member/resetPassword" method="post">
            <input type="password" name="newPassword" placeholder="새로운 비밀번호를 입력하세요" required>
            <input type="password" name="confirmPassword" placeholder="비밀번호 확인" required>
            <button type="submit" id="submitBtn" disabled>비밀번호 재설정</button>
        </form>
    </div>
</div>

 

 

우와 진짜 하루에 이렇게 오류한테 고통 안받고 공부하면서 하는거 처음이야.........

알찬 8시간이었다. 

 

진짜 기능 개발하면 할 수록 재밌는데 css걱정에 .. 눈이 흐려진다...


 

 


🛴개발에서 자주 사용하는 기초 용어들 정리하기

 

파라미터 (Parameter)

메서드(함수)를 정의할 때, 그 메서드가 필요로 하는 값을 받는 변수.
메서드에 어떤 값을 받을지 미리 정해놓은 자리.

public int sum(int a, int b) {  // a와 b가 '파라미터'
    return a + b;
}

 

인수 (Argument)
메서드를 호출할 때 전달하는 실제 값.


int result = sum(3, 5);  // 3과 5가 '인수'

 

변수 (Variable)
데이터를 저장하기 위해 이름을 붙인 저장 공간.


int number = 10;  // number는 '변수', 10은 그 변수에 저장된 값

 

 

키-값 (Key-Value)
키는 데이터에 접근할 때 사용하는 이름이고, 값은 그 키에 저장된 실제 데이터

Map<String, String> person = new HashMap<>();
person.put("name", "John");  // 'name'이 키, 'John'이 값

 

 

 



정청산기 실기 끝나고 너무 컨디션이 안 좋았는데 

그냥 또 앉아서 하니까 시간이 후룩 가버렸다.

머리가 초금 아푸지만

머리에 정리가 잘 된 하루~😋

 

이제 11월이 다가오고 학원도 끝이 보인다.

제발~ 얼른 끝나랏!