본문 바로가기
05_Server (04. JSP 프로그래밍 구현)

[1-4] 회원서비스 회원가입 페이지로 이동, 회원가입 요청

by moca7 2024. 9. 8.



ㅁ 회원서비스

- 회원서비스 개발 세팅         

- 회원서비스_로그인          
- 회원서비스_로그아웃           <- 여기까지 했음.
- 회원서비스_회원가입페이지로이동 

 

- 회원서비스_회원가입요청
- 회원서비스_마이페이지요청
- 회원서비스_정보변경요청
- 회원서비스_비번변경요청
- 회원서비스_회원탈퇴요청

 

 

 

 

 

ㅁ 회원가입페이지로 이동

 

 

- 아래는 header.jsp다.

 
<%@ page import="com.br.web.member.model.vo.Member" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<!-- Bootstrap 사용을 위한 CDN -->
<!-- ------------------------- -->
   
<style>
  header{height: 150px}
  header a{color:black;}
</style>
 

<%
  String contextPath = request.getContextPath(); // "/web"
 
  Member loginUser = (Member)session.getAttribute("loginUser");
  // 해당 구문이 실행되는 시점
  // 로그인 요청 전 페이지 로드시 : null
  // 로그인 성공 후 페이지 로드시 : 조회된 데이터가 담겨있는 Member객체
%>
 

<header class="row m-3">
  <div class="col-3 d-flex justify-content-center align-items-center">
    <a href="<%=contextPath%>"><img src="<%= contextPath %>/assets/image/goodee_logo.png"></a>
  </div>
  <div class="col-6"></div>
  <div class="col-3 d-flex justify-content-center align-items-center">

    <% if(loginUser == null) { %>
    <!-- case1. 로그인전 -->
    <form action="<%= contextPath %>/login.me" method="post">
      <table>
        <tr>
          <th>ID</th>
          <td><input type="text" name="userId" class="form-control form-control-sm" placeholder="Enter Your ID" required></td>
        </tr>
        <tr>
          <th>PWD</th>
          <td><input type="password" name="userPwd" class="form-control form-control-sm" placeholder="Enter Your PWD" required></td>
        </tr>
        <tr>
          <td colspan="2" align="center">
            <button type="submit" class="btn btn-secondary btn-sm">로그인</button>
            <button type="button" class="btn btn-secondary btn-sm">회원가입</button>
          </td>
        </tr>
      </table>
    </form>
    <% }else { %>
    <!-- case2. 로그인후 -->
    <div>
      <b><%= loginUser.getUserName() %></b>님 환영합니다. <br><br>

      <a href="<%=contextPath%>/myinfo.me">마이페이지</a>
      <a href="<%=contextPath%>/logout.me">로그아웃</a>
    </div>
    <% } %>

  </div>
</header>
<nav class="navbar m-3 navbar-expand-sm bg-dark navbar-dark d-flex justify-content-center">
  <ul class="navbar-nav">
    <li class="nav-item">
      <a class="nav-link" href="<%=contextPath%>">Home</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="<%= contextPath %>/list.no">공지사항</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">일반게시판</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">사진게시판</a>
    </li>
  </ul>
</nav>
 

 

- header.jsp에는 변수가 둘 선언되어 있다.

 

 

 

 

 

 

ㅁ src/main/webapp/views/member에 "회원가입페이지.html"(화면 구현한 것)이 있다.

- 이걸 jsp 파일로 만들어야 한다.

- src/main/webapp/views/member 우클릭 - new - jsp에서 signUp.jsp 파일을 만든다.

 

 

- 회원가입 페이지를 화면 구현한 "회원가입페이지.html"를 열어서 <body> 태그 안의 구문들을

"signUp.jsp" 파일의 <body> 태그에 전체 복사해서 붙여넣기 한다.

 

 

 

 

- 아래는 signUp.jsp 파일이다.

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

  <div class="container p-3">

    <!-- Header, Nav start -->
      <!-- header.jsp include로 표현할 예정 -->
    <!-- Header, Nav end -->

    <!-- Section start -->
    <section class="row m-3" style="min-height: 500px">

      <div class="container border p-5 m-4 rounded">
        <h2 class="m-4">회원가입</h2>
       
        <form action="" method="" class="m-4">
          <table class="table">
            <tr>
              <th>* 아이디</th>
              <td><input type="text" class="form-control" placeholder="Enter Your ID" required></td>
              <td><button type="button" class="btn btn-secondary btn-sm">중복확인</button></td>
            </tr>
            <tr>
              <th>* 비밀번호</th>
              <td><input type="password" class="form-control" placeholder="Enter Your Password" required></td>
              <td></td>
            </tr>
            <tr>
              <th>* 비밀번호 확인</th>
              <td><input type="password" class="form-control" placeholder="Enter Your Password Check" required></td>
              <td></td>
            </tr>
            <tr>
              <th>* 이름</th>
              <td><input type="text" class="form-control" placeholder="Enter Your Name" required></td>
              <td></td>
            </tr>
            <tr>
              <th>&nbsp;&nbsp;전화번호</th>
              <td><input type="text" class="form-control" placeholder="Enter Your Phone (- include)"></td>
              <td></td>
            </tr>
            <tr>
              <th>&nbsp;&nbsp;이메일</th>
              <td><input type="text" class="form-control" placeholder="Enter Your Email (@ include)"></td>
              <td></td>
            </tr>
            <tr>
              <th>&nbsp;&nbsp;주소</th>
              <td><input type="text" class="form-control" placeholder="Enter Your Address"></td>
              <td></td>
            </tr>
            <tr>
              <th>&nbsp;&nbsp;관심분야</th>
              <td>
                <input type="checkbox" name="interest" value="운동" id="sports">
                <label for="sports">운동</label>
                <input type="checkbox" name="interest" value="등산" id="climibing">
                <label for="climibing">등산</label>
                <input type="checkbox" name="interest" value="낚시" id="fishing">
                <label for="fishing">낚시</label>
                <input type="checkbox" name="interest" value="요리" id="cooking">
                <label for="cooking">요리</label>
                <input type="checkbox" name="interest" value="게임" id="game">
                <label for="game">게임</label>
                <input type="checkbox" name="interest" value="영화" id="movie">
                <label for="movie">영화</label>
              </td>
              <td></td>
            </tr>
          </table>

          <br>

          <div align="center">
            <button type="submit" class="btn btn-primary btn-sm">회원가입</button>
            <button type="reset" class="btn btn-danger btn-sm">초기화</button>
          </div>

        </form>

     
      </div>

    </section>
    <!-- Section end -->

    <!-- Footer start -->
      <!-- footer.jsp include 할 예정 -->
    <!-- Footer end -->

  </div>

 
</body>
</html>
 

 

 

- header와 footer를 include 지시어를 사용해서 include 한다.

 

- header에 있는 '회원가입' 버튼 클릭시 'signUp.jsp' 페이지로 이동되어야 한다.

 

 

 

 

 

ㅁ header.jsp의 '회원가입' 버튼 클릭시 'signUp.jsp'로 이동되게끔 한다.

 

- 아래는 header.jsp다.

 
<%@ page import="com.br.web.member.model.vo.Member" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<!-- Bootstrap 사용을 위한 CDN -->
<!-- ------------------------- -->
   
<style>
  header{height: 150px}
  header a{color:black;}
</style>
 

<%
  String contextPath = request.getContextPath(); // "/web"
 
  Member loginUser = (Member)session.getAttribute("loginUser");
  // 해당 구문이 실행되는 시점
  // 로그인 요청 전 페이지 로드시 : null
  // 로그인 성공 후 페이지 로드시 : 조회된 데이터가 담겨있는 Member객체
%>
 

<header class="row m-3">
  <div class="col-3 d-flex justify-content-center align-items-center">
    <a href="<%=contextPath%>"><img src="<%= contextPath %>/assets/image/goodee_logo.png"></a>
  </div>
  <div class="col-6"></div>
  <div class="col-3 d-flex justify-content-center align-items-center">

    <% if(loginUser == null) { %>
    <!-- case1. 로그인전 -->
    <form action="<%= contextPath %>/login.me" method="post">
      <table>
        <tr>
          <th>ID</th>
          <td><input type="text" name="userId" class="form-control form-control-sm" placeholder="Enter Your ID" required></td>
        </tr>
        <tr>
          <th>PWD</th>
          <td><input type="password" name="userPwd" class="form-control form-control-sm" placeholder="Enter Your PWD" required></td>
        </tr>
        <tr>
          <td colspan="2" align="center">
            <button type="submit" class="btn btn-secondary btn-sm">로그인</button>
            <button type="button" class="btn btn-secondary btn-sm" id="signup-btn">회원가입</button>
            <script>
              document.getElementById("signup-btn").addEventListener("click", () => {
                location.href = "<%=contextPath%>/signup.me";
              })
            </script>
          </td>
        </tr>
      </table>
    </form>
    <% }else { %>
    <!-- case2. 로그인후 -->
    <div>
      <b><%= loginUser.getUserName() %></b>님 환영합니다. <br><br>

      <a href="<%=contextPath%>/myinfo.me">마이페이지</a>
      <a href="<%=contextPath%>/logout.me">로그아웃</a>
    </div>
    <% } %>

  </div>
</header>
<nav class="navbar m-3 navbar-expand-sm bg-dark navbar-dark d-flex justify-content-center">
  <ul class="navbar-nav">
    <li class="nav-item">
      <a class="nav-link" href="<%=contextPath%>">Home</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="<%= contextPath %>/list.no">공지사항</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">일반게시판</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">사진게시판</a>
    </li>
  </ul>
</nav>
 

 

 

- 로그인 전에는 회원가입할 수 있는 버튼이 보여진다.

- a 태그가 아니라 button 태그이기 때문에 href 속성에 요청할 url을 바로 작성할 수는 없다.

자바스크립트 방식으로 url을 요청한다.

- 회원가입 버튼에 id를 주고 <script> 태그로 동적인 효과를 부여한다.

자바스크립트 방식으로 '회원가입' 버튼 요소를 선택하고 이벤트를 부여한다.

 

- location.href="요청할url";

url을 요청하고 싶으면 location 객체의 href 속성을 사용한다. 

- '회원가입' 버튼 클릭시 '회원가입' 페이지로 이동시킨다.

이 페이지에는 db로부터 조회해올 데이터는 없다. 그냥 정적인 페이지다.

단순한 정적페이지 이동은 그 페이지에 대한 경로를 곧바로 url로 요청해서 띄워줄 수도 있지만,

그러면 이 프로젝트의 디렉토리 구조가 url에 노출되므로 forward 한다.

- 단순한 페이지 이동도 무조건 서블릿을 호출시켜서 그 서블릿에서 forwarding 방식으로 이동한다.

 

- 자바스크립트 내에서도 표현식 사용이 가능하다.

"/signup.me"라는 url을 요청한다. 서블릿 호출이다.

 

 

 

 

 

ㅁ src/main/java/com.br.web.member.controller에 MemberSignUpController라는 서블릿 클래스를 만든다.

 

 

- 아래는 MemberSignUpController.java이다.

 
package com.br.web.member.controller;

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


@WebServlet("/signup.me")
public class MemberSignUpController extends HttpServlet {

    private static final long serialVersionUID = 1L;
       
    public MemberSignUpController() {
        super();
    }


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

        // 단순 페이지 이동 요청
        // 응답페이지 : /web/views/member/signUp.jsp
        request.getRequestDispatcher("/views/member/signUp.jsp").forward(request, response);
   
    }


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

}
 

 

 

- 회원가입 페이지로 이동시켜주는 "/signup.me"라는 url mapping 값을 가지는 서블릿 클래스를 만든다.

- doGet() 메소드 내부를 지우고, 단순한 페이지 이동 요청이므로 요청처리할 것 없이 바로 응답페이지에 대해 생각하면 된다.

- 요청시 전달값도 없고 요청처리할 것도 없다. 그냥 페이지 이동만 되게끔 하면 된다.

만약에 응답 페이지에서 필요한 데이터가 있다면 이동하기 전에 조회해서 request 객체에 담아서 전달하면 된다.

- forward로 이동할 경로에는 context path를 제외하고 쓴다.

 

 

 

 

 

- '회원서비스'의 '회원가입페이지 이동 요청' 기능은 '/signup.me'라는 url로 요청했고 get방식이었다.

요청시 전달값은 없었고 실행할 쿼리도 없었다. forward 방식으로 이동해서 응답페이지를 보여줬다.

- 다른 페이지에서도 이제 '회원가입페이지로 이동 요청' 하고 싶으면 이제부터 '/signup.me'라는 url을 redirect하면 된다.

 

 

 

 

 

 

ㅁ signUp.jsp로 돌아온다.

 

 

- 아래는 signUp.jsp 파일이다.

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

  <div class="container p-3">

    <!-- Header, Nav start -->
    <%@ include file="/views/common/header.jsp" %>
    <!-- Header, Nav end -->

    <!-- Section start -->
    <section class="row m-3" style="min-height: 500px">

      <div class="container border p-5 m-4 rounded">
        <h2 class="m-4">회원가입</h2>
       
        <form action="<%= contextPath %>/insert.me" method="post" class="m-4">
          <table class="table">
            <tr>
              <th>* 아이디</th>
              <td><input type="text" name="userId" class="form-control" placeholder="Enter Your ID" required></td>
              <td><button type="button" class="btn btn-secondary btn-sm">중복확인</button></td>
            </tr>
            <tr>
              <th>* 비밀번호</th>
              <td><input type="password" name="userPwd" class="form-control" placeholder="Enter Your Password" required></td>
              <td></td>
            </tr>
            <tr>
              <th>* 비밀번호 확인</th>
              <td><input type="password" class="form-control" placeholder="Enter Your Password Check" required></td>
              <td></td>
            </tr>
            <tr>
              <th>* 이름</th>
              <td><input type="text" name="userName" class="form-control" placeholder="Enter Your Name" required></td>
              <td></td>
            </tr>
            <tr>
              <th>&nbsp;&nbsp;전화번호</th>
              <td><input type="text" name="phone" class="form-control" placeholder="Enter Your Phone (- include)"></td>
              <td></td>
            </tr>
            <tr>
              <th>&nbsp;&nbsp;이메일</th>
              <td><input type="text" name="email" class="form-control" placeholder="Enter Your Email (@ include)"></td>
              <td></td>
            </tr>
            <tr>
              <th>&nbsp;&nbsp;주소</th>
              <td><input type="text" name="address" class="form-control" placeholder="Enter Your Address"></td>
              <td></td>
            </tr>
            <tr>
              <th>&nbsp;&nbsp;관심분야</th>
              <td>
                <input type="checkbox" name="interest" value="운동" id="sports">
                <label for="sports">운동</label>
                <input type="checkbox" name="interest" value="등산" id="climibing">
                <label for="climibing">등산</label>
                <input type="checkbox" name="interest" value="낚시" id="fishing">
                <label for="fishing">낚시</label>
                <input type="checkbox" name="interest" value="요리" id="cooking">
                <label for="cooking">요리</label>
                <input type="checkbox" name="interest" value="게임" id="game">
                <label for="game">게임</label>
                <input type="checkbox" name="interest" value="영화" id="movie">
                <label for="movie">영화</label>
              </td>
              <td></td>
            </tr>
          </table>

          <br>

          <div align="center">
            <button type="submit" class="btn btn-primary btn-sm">회원가입</button>
            <button type="reset" class="btn btn-danger btn-sm">초기화</button>
          </div>

        </form>

     
      </div>

    </section>
    <!-- Section end -->

    <!-- Footer start -->
    <%@ include file="/views/common/footer.jsp" %>
    <!-- Footer end -->

  </div>
 

</body>
</html>
 

 

 

- 각각의 input 요소 텍스트 상자에 name 속성을 추가해서 key값을 부여해준다. 그래야 데이터가 넘어간다.

- 안의 내용은 form 요소로 구성되어 있다.

개인정보니까 post 방식으로 요청하고, url은 context path를 동적으로 따와서 작성하고 서블릿을 호출한다.

 

- signUp.jsp에는 contextPath라는 변수가 선언되어 있지 않지만, header.jsp를 include하고 있기 때문에 사용 가능하다.

header.jsp를 include하는 include 지시어 아래쪽이어서 사용 가능한거고,

include 지시어 위쪽에서는 contextPath라는 변수 사용이 불가능하다.

 

 

- 비밀번호 확인은 name 속성을 추가하지 않아도 된다.

그냥 화면단에서 이 두 개가 일치하는지 비교하기 위해서 둔거기 때문에 굳이 데이터를 넘길 필요는 없다.

일치하는지 유효성 체크하는거 자바스크립트 때 했으니까 한번 적용을 해보시고,

잘 입력했다는 가정 하에 데이터 넘겨서 insert하는 것만 진행한다.

 

 

- (참고) 아이디 옆의 '중복확인' 버튼은 나중에 AJAX 배워서 적용시킨다.

 

 

 

 

ㅁ src/main/java/com.br.web.member.controller에 MemberInsertController라는 서블릿 클래스를 만든다.

 

- 아래는 MemberInsertController.java다.

 
package com.br.web.member.controller;

import java.io.IOException;

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 com.br.web.member.model.service.MemberService;
import com.br.web.member.model.vo.Member;


@WebServlet("/insert.me")
public class MemberInsertController extends HttpServlet {

    private static final long serialVersionUID = 1L;
       
    public MemberInsertController() {
        super();
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 
        // 1. 요청
        // 요청시 전달값 뽑기
        request.setCharacterEncoding("utf-8");
 
        String userId = request.getParameter("userId"); // "user03"
        String userPwd = request.getParameter("userPwd"); // "pass03"
        String userName = request.getParameter("userName"); // "강개똥"
        String phone = request.getParameter("phone"); // "010-1111-2222" | ""
        String email = request.getParameter("email"); // "ssss@sss.com" | ""
        String address = request.getParameter("address"); // "서울시" | ""
        String[] interestArr = request.getParameterValues("interest"); // ["운동", "등산"] | null
 
        String interest = "";
        if(interestArr != null) {
            interest = String.join(",", interestArr);
        }
       
        Member m = new Member(userId, userPwd, userName, phone, email, address, interest);
       
        // 서비스 호출 (쿼리실행)
        int result = new MemberService().insertMember(m);
       
       
        // 2. 응답
        if(result > 0) {
 
        }else {
 
        }
 
       
    }

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

}
 

 

 

- "/insert.me"라는 url mapping 값을 가지는 서블릿 클래스를 만든다. 

- "/insert.me" 요청시 다수의 데이터가 넘어온다.

항상 요청 관련한 내용, 응답 관련한 내용만 써주면 된다.

- 요청은 요청시 전달값 뽑아서 sql문을 실행한다.

요청시 전달값 뽑고 service 호출하기. 이게 jdbc 과정이다.

- insert 쿼리는 처리된 행 수가 돌아온다. 

0보다 크면 insert가 성공한 거고 0이면 실패다.

 

 

- post 방식 요청이므로 utf-8로 인코딩한다.

- 요청시 전달한 데이터가 다수이므로 vo 객체에 담아서 service로 넘긴다.

dao까지 넘겨서 db에 insert를 수행한다.

- 그런데 Member 객체의 매개변수 생성자로 저 7개의 매개변수를 받아주는 생성자가 없다. 없으면 만들면 된다. 

 

 

 

 

- /webApp/src/main/java/com/br/web/member/model/vo/Member.java 에서 alt shift s + o 눌러서 생성자를 만들어 준다.

- 내가 데이터를 new Member(~, ~, ...)로 전달하는 순서와 생성자의 매개변수 순서가 일치하는지도 확인한다.

 

 

 

 

 

- 그런데 db에 자바에서의 배열객체를 저장시킬 수는 없다. interestArr은 String형 배열이다.

관심분야의 경우 db에 '운동,등산' 이렇게 하나의 문자열로 만들어서 기록을 하기로 했다.

즉 String 타입의 배열을 하나의 문자열로 합쳐서 넘겨줘야 한다.

- 그런데 String 타입의 배열이 null일 수 있다.

null pointer exception을 방지하기 위해 조건문으로 String 배열이 null인지 체크한다.

 

- db에 기록할 데이터를 가공하는 과정이다. 데이터 가공 처리는 controller 단에서 한다.

 

 

 

 

 

ㅁ MemberService 클래스에 insertMember() 메소드를 만든다.

 

 

- 아래는 MemberService.java이다.

 
package com.br.web.member.model.service;

import static com.br.web.common.template.JDBCTemplate.close;
import static com.br.web.common.template.JDBCTemplate.commit;
import static com.br.web.common.template.JDBCTemplate.getConnection;
import static com.br.web.common.template.JDBCTemplate.rollback;

import java.sql.Connection;
import java.util.Map;

import com.br.web.member.model.dao.MemberDao;
import com.br.web.member.model.vo.Member;

public class MemberService {
   
    private MemberDao mDao = new MemberDao();
   
    public Member loginMember(String userId, String userPwd) {
 
        Connection conn = getConnection();
        Member loginUser = mDao.loginMember(conn, userId, userPwd);
        close(conn);
        return loginUser;
 
    }
 
   
    public int insertMember(Member m) {
 
        Connection conn = getConnection();
        int result = mDao.insertMember(conn, m);
       
        if(result > 0) {
            commit(conn);
        }else {
            rollback(conn);
        }
       
        close(conn);
       
        return result;
       
    }
   

}
 

 

 

- MemberService 클래스에서는 MemberInsertController 클래스에서 보낸 Member 객체를

매개변수로 전달받을 수 있는 insertMember()메소드가 필요하다.

- JDBCTemplate 클래스의 commit()과 rollback() 메소드를 그냥 이름만으로 호출하고 싶다면 import static 해줘야 한다.

 

 

 

 

 

ㅁ MemberDao 클래스에 insertMember() 메소드를 만든다.

 

 
package com.br.web.member.model.dao;

import static com.br.web.common.template.JDBCTemplate.close;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;

import com.br.web.member.model.vo.Member;

public class MemberDao {
 
   
    private Properties prop = new Properties();
 
   
    public MemberDao() {
       
        String filePath = MemberDao.class.getResource("/db/mappers/member-mapper.xml").getPath();
       
        try {
            prop.loadFromXML(new FileInputStream(filePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
       
    }
 
   
    public Member loginMember(Connection conn, String userId, String userPwd) {
 
        // select문 => ResultSet (한 행, 한 회원) => Member객체
        Member m = null;
        PreparedStatement pstmt = null;
        ResultSet rset = null;
 
        String sql = prop.getProperty("loginMember");
       
        try {
            pstmt = conn.prepareStatement(sql); // 미완성된 sql문
            pstmt.setString(1, userId);
            pstmt.setString(2, userPwd);
            rset = pstmt.executeQuery();
           
            if(rset.next()) {
                m = new Member(rset.getInt("USER_NO")
                             , rset.getString("user_id")
                             , rset.getString("user_pwd")
                             , rset.getString("user_name")
                             , rset.getString("phone")
                             , rset.getString("email")
                             , rset.getString("address")
                             , rset.getString("interest")
                             , rset.getDate("enroll_date")
                             , rset.getDate("modify_date")
                             , rset.getString("status"));
            }
           
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(rset);
            close(pstmt);
        }
       
        return m;
    }
 
   
    public int insertMember(Connection conn, Member m) {
 
        // insert => 처리된 행 수
        int result = 0;
        PreparedStatement pstmt = null;
 
        String sql = prop.getProperty("insertMember");
       
        try {
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, m.getUserId());
            pstmt.setString(2, m.getUserPwd());
            pstmt.setString(3, m.getUserName());
            pstmt.setString(4, m.getPhone());
            pstmt.setString(5, m.getEmail());
            pstmt.setString(6, m.getAddress());
            pstmt.setString(7, m.getInterest());
           
            result = pstmt.executeUpdate();
           
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(pstmt);
        }
       
        return result;
    }


}
 

 

 

- insert 쿼리를 실행하면 처리된 행 수가 반환된다.

- 최종적으로 반환된 행 수가 1이라면 성공적으로 insert된 거고, 아니면(0이라면) insert에 실패한 것이다.

그러면 문법문제일 수도 있기 때문에 절대 오류가 나선 안 된다.

제약 조건에 위배될 수도 있지만 그 제약 조건에 위배가 안되게 애초에 화면단에서부터 유효성 체크를 해줬어야 한다.

(지금 유효성 체크 과정은 스킵했지만)

 

 

 

 

 

ㅁ /webApp/src/main/java/db/mappers/member-mapper.xml에 insert 쿼리문을 작성한다.

 

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>

    <entry key="loginMember">
        SELECT
                      USER_NO
                    , USER_ID
                    , USER_PWD
                    , USER_NAME
                    , PHONE
                    , EMAIL
                    , ADDRESS
                    , INTEREST
                    , ENROLL_DATE
                    , MODIFY_DATE
                    , STATUS
          FROM
                    MEMBER
         WHERE
                     USER_ID = ?
             AND USER_PWD = ?
             AND STATUS IN ('A', 'U')
    </entry>
 
   
    <entry key="insertMember">
        INSERT
            INTO MEMBER
          (
            USER_NO
          , USER_ID
          , USER_PWD
          , USER_NAME
          , PHONE
          , EMAIL
          , ADDRESS
          , INTEREST
          )
          VALUES
          (
            SEQ_UNO.NEXTVAL
          , ?
          , ?
          , ?
          , ?
          , ?
          , ?
          , ?
          )
    </entry>

</properties>
 

 

 

- 메소드명과 동일하게 "insertMember"라는 key 값을 가지는 entry 객체를 만든다.

- 총 11개의 컬럼이 있는 테이블인데 8개의 컬럼만 insert 했다.

나머지 3개는 ENROLL_DATE, MODIFY_DATE, STATUS인데 다 default 값이 설정되어 있다.

걔네 들을 굳이 쓰지 않아도 알아서 값이 들어간다.

- 8개의 컬럼을 선택했으니 8개의 값을 쓰면 된다.

USER_NO 컬럼은 SEQ_UNO.NEXTVAL로 시퀀스로 새로운 번호를 발생시켜서 넣어야만 한다.

절대 겹치면 안되기 때문이다.

- 나머지 7개 값들은 ?로 두고 사용자가 입력한 값으로 INSERT 한다.

 

 

 

 

ㅁ /webApp/src/main/java/com/br/web/member/controller/MemberInsertController.java로 돌아온다.

 

 

- 아래는 MemberInsertController.java다.

 

 
package com.br.web.member.controller;

import java.io.IOException;

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 com.br.web.member.model.service.MemberService;
import com.br.web.member.model.vo.Member;


@WebServlet("/insert.me")
public class MemberInsertController extends HttpServlet {
 
    private static final long serialVersionUID = 1L;
       

    public MemberInsertController() {
        super();
    }


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 요청
        // 요청시 전달값 뽑기
        request.setCharacterEncoding("utf-8");
 
        String userId = request.getParameter("userId"); // "user03"
        String userPwd = request.getParameter("userPwd"); // "pass03"
        String userName = request.getParameter("userName"); // "강개똥"
        String phone = request.getParameter("phone"); // "010-1111-2222" | ""
        String email = request.getParameter("email"); // "ssss@sss.com" | ""
        String address = request.getParameter("address"); // "서울시" | ""
        String[] interestArr = request.getParameterValues("interest"); // ["운동", "등산"] | null
        String interest = "";
 
        if(interestArr != null) {
            interest = String.join(",", interestArr);
        }
       
        Member m = new Member(userId, userPwd, userName, phone, email, address, interest);
               
        // 서비스 호출 (쿼리실행)
        int result = new MemberService().insertMember(m);
       
       
        // 2. 응답
        if(result > 0) {
            /*
             * * 회원가입 성공
             *   ㄴ 응답페이지 : 메인페이지
             *   ㄴ 응답데이터 : "성공적으로 회원가입 되었습니다." alert 메세지
             */
           
            request.getSession().setAttribute("alertMsg", "성공적으로 회원가입 되었습니다.");
            response.sendRedirect(request.getContextPath());
        }else {
            /*
             * * 회원가입 실패
             *   ㄴ 응답페이지 : 에러페이지
             *   ㄴ 응답데이터 : "회원가입 실패" 메세지 (해당 응답페이지에서만 필요)
             */
           
            request.setAttribute("msg", "회원가입 실패");
            request.getRequestDispatcher("/views/common/errorPage.jsp").forward(request, response);
           
        }
       
    }


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

}
 

 

 

- 회원가입에 실패한 경우 이미 만들어 놓은 에러페이지가 응답페이지로 뜨게끔 한다.

포워딩으로 만들어 놓은 에러페이지로 이동한다. 포워딩은 context path를 제외하고 입력한다.

"회원가입 실패" 메세지는 응답 페이지에서만 필요하기 때문에 request에 담는다.

- 이때 아무 key 값으로 담으면 안된다. 

errorPage.jsp에서 <%=request.getAttribute("msg") %>로 에러메시지를 출력하고 있기 때문에 "msg"를 key로 한다.

- 이렇게 회원가입 실패에 대해 처리를 했는데,

코드만 잘 작성되어 있으면 절대 실패할 일이 없다. 에러페이지는 혹시 모르니 만드는 것이다.

 

 

- 회원가입에 성공한 경우 response.sendRedirect("/web");으로 메인페이지 url을 재요청하면 된다.

"/web"이라는 context path는 언제 바뀔지 모르기 때문에 request.getContextPath()로 동적으로 context path를 알아온다.

- 회원가입 성공시 응답데이터는 응답페이지인 메인페이지에서 한번 alert로 띄워주고 끝이다. 

그런데 request에 담긴 데이터는 포워딩으로 전달이 되어야만 이동한 페이지에서 쓸 수 있다.

url을 요청하는 방식(redirect)에서는 request에 담을 수가 없다. 그러면 session에 담으면 된다.

- 어떤 페이지를 응답하는 url이 존재하면 포워딩하면 안 되고 반드시 url을 재요청하는 방식으로 해야한다.

 

- redirect하는 경우에는 request에 데이터를 담아도 의미가 없습니다.

redirect는 클라이언트(브라우저)에게 새로운 URL로 이동하라는 명령을 보내는 것이기 때문에, 클라이언트는 서버로부터 받은 새로운 URL로 다시 요청을 보냅니다. 이때, 기존의 request 객체는 사라지고 새로운 요청이 생성되므로, 이전 요청에 담았던 데이터는 유지되지 않습니다.

 

 

 

 

ㅁ header.jsp로 돌아온다.

 

 
<%@ page import="com.br.web.member.model.vo.Member" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<!-- Bootstrap 사용을 위한 CDN -->
<!-- ------------------------- -->
   
<style>
  header{height: 150px}
  header a{color:black;}
</style>

<%
  String contextPath = request.getContextPath();    //    "/web"
 
  Member loginUser = (Member)session.getAttribute("loginUser");
  // 해당 구문이 실행되는 시점
  // 로그인 요청 전 페이지 로드시 : null
  // 로그인 성공 후 페이지 로드시 : 조회된 데이터가 담겨있는 Member객체
 
  String alertMsg = (String)session.getAttribute("alertMsg");
  // 해당 구문이 실행되는 시점
  // 특정 서비스 요청 전 페이지 로드시 : null
  // 특정 서비스 요청 성공 후 페이지 로드시 : alert로 띄워줄 메세지
%>
 

<% if(alertMsg != null) { %>
<script>
  alert('<%=alertMsg%>');
</script>
<% session.removeAttribute("alertMsg"); } %>
 

<header class="row m-3">
  <div class="col-3 d-flex justify-content-center align-items-center">
    <a href="<%=contextPath%>"><img src="<%= contextPath %>/assets/image/goodee_logo.png"></a>
  </div>
  <div class="col-6"></div>
  <div class="col-3 d-flex justify-content-center align-items-center">

    <% if(loginUser == null) { %>
    <!-- case1. 로그인전 -->
    <form action="<%= contextPath %>/login.me" method="post">
      <table>
        <tr>
          <th>ID</th>
          <td><input type="text" name="userId" class="form-control form-control-sm" placeholder="Enter Your ID" required></td>
        </tr>
        <tr>
          <th>PWD</th>
          <td><input type="password" name="userPwd" class="form-control form-control-sm" placeholder="Enter Your PWD" required></td>
        </tr>
        <tr>
          <td colspan="2" align="center">
            <button type="submit" class="btn btn-secondary btn-sm">로그인</button>
            <button type="button" class="btn btn-secondary btn-sm" id="signup-btn">회원가입</button>
            <script>
              document.getElementById("signup-btn").addEventListener("click", () => {
                location.href = "<%=contextPath%>/signup.me";
              })
            </script>
          </td>
        </tr>
      </table>
    </form>
    <% }else { %>
    <!-- case2. 로그인후 -->
    <div>
      <b><%= loginUser.getUserName() %></b>님 환영합니다. <br><br>

      <a href="<%=contextPath%>/myinfo.me">마이페이지</a>
      <a href="<%=contextPath%>/logout.me">로그아웃</a>
    </div>
    <% } %>

  </div>
</header>
<nav class="navbar m-3 navbar-expand-sm bg-dark navbar-dark d-flex justify-content-center">
  <ul class="navbar-nav">
    <li class="nav-item">
      <a class="nav-link" href="#">Home</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">공지사항</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">일반게시판</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">사진게시판</a>
    </li>
  </ul>
</nav>
 

 

 

- session으로부터 alert 메세지를 뽑아서 script 구문을 통해 출력되게 한다.

- 코드가 실행되는 시점을 생각해야 한다.

header.jsp가 로드될 때마다 alertMsg 데이터가 있는 것은 아니다. 

- script 구문은 alert로 띄워줄 메세지가 존재할 때만 script 요소가 만들어지게끔 조건문으로 작성한다.

- alert() 메소드 안에 표현식을 쓸 때도, ' '로 문자열로 취급되게끔 해야 한다.

 

- session에 담긴 데이터는 서버가 종료되기 전까지 쭉 유지가 된다.

필요없어졌다면 직접 지워줄 수도 있어야 한다.

회원가입 성공 메세지는 한 번만 출력되고 더 이상 출력이 되어서는 안 된다. 일회성 메세지다. 그래서 session을 비워준다.

 

- header.jsp의 변수가 3개가 되었다. contextPath, loginUser, alertMsg.

 

 

 

ㅁ 서버를 재시작하고, 메인페이지를 켜서 회원가입하고 회원가입 성공 메세지가 alert로 뜨는지 확인한다.

- 그리고 로그인도 해보고, sql developer를 켜서 데이터가 db에 잘 담겼는지도 확인한다. 컬럼에 맞게 잘 담겼는지.

 

 

ㅁ 회원가입 서비스뿐 아니라 정보변경 서비스 등 다른 서비스 요청에 성공했을 때도 alert 메세지를 활용할 예정이다.