또자의 코딩교실

[스마트인재개발원]-1차 프로젝트를 진행하며-(4-1) 본문

코딩공부/프로젝트 진행

[스마트인재개발원]-1차 프로젝트를 진행하며-(4-1)

또자자 2021. 11. 25. 13:42

이번 포스팅에서는 실제 개발했던 프로젝트 사이트의 이동경로에 맞춘 Back-end Process중

첫화면 - 회원가입 - 로그인 - MBTI추천 메인사이트를 유즈케이스와 화면설계서를 동반하여 설명하고자 한다.

 

사이트의 서비스 흐름도는 다음과 같다.

사이트 이용 흐름도

 

이제 각 사이트들의 최종 결과 화면을 보며 백엔드적인 요소들을 살펴보자.


화면설계서 - 첫화면

위는 첫화면의 화면설계서이다. 코딩이 진행된 View에서 중요한 body 부분만 발췌하겠다.

<!-- JSP/Servlet 임을 표시합니다. -->
<%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>
<% String cpath = request.getContextPath(); %>

    <body id="page-top">
        <!-- Navigation-->
        <nav class="navbar navbar-expand-lg navbar-light fixed-top py-3" id="mainNav">
            <div class="container px-4 px-lg-5">
                <button class="navbar-toggler navbar-toggler-right" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
                <div class="collapse navbar-collapse" id="navbarResponsive">
                    <ul class="navbar-nav ms-auto my-2 my-lg-0">
                        <li class="nav-item"><a class="nav-link" href="#about"></a></li>
                        <li class="nav-item"><a class="nav-link" href="#about"></a></li>
                        <li class="nav-item"><a class="nav-link" href="<%=cpath%>/movieLogin.do">로그인</a></li>
                    </ul>
                </div>
            </div>
        </nav>
        
        <!-- Masthead-->
        <header class="masthead">
            <div class="container px-4 px-lg-5 h-100">
                <div class="row gx-4 gx-lg-5 h-100 align-items-center justify-content-center text-center">
                <div class="col-lg-8 align-self-end">
                        <h1 class="text-black font-weight-bolder">당신만의 영화를 찾으세요<br>여기,&nbsp BESPOKE CINEMA에서</h1>
                        <hr class="divider" />
                    </div>
                    <div class="col-lg-8 align-self-baseline">
                        <a class="btn btn-primary btn-xl" href="<%=cpath%>/memberForm.do">회원가입</a>
                    </div>
                    
                </div>
            </div>
        </header>
    </body>
</html>

a태그에 herf 속성을 주고 사이트 내 context path뒤, Handler Mapping이 이동할 경로인 memberForm.do(회원가입) 로 이동하도록 설정하였다. 같은 방식으로 로그인또한 설정하였다.

 

해당 사이트에 사용된 Controller는 아직 없다.

 

회원가입과 로그인 버튼을 누르면 해당 기능을 제공하는 페이지로 이동하는 간단한 기능만이 존재한다. 

 


 

화면설계서 - 회원가입

위는 회원가입 화면의 화면설계서이다. 코딩이 진행된 View에서 중요한 body 부분만 발췌하겠다.

<body>
<nav class="navbar navbar-default">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar">
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>                        
      </button>
      <a class="navbar-brand" href="<%=cpath%>/moviemain.do">BESPOKE CINEMA</a>
    </div>
    <div class="wrap">
        
     </div>
     <ul class="nav navbar-nav navbar-right">
     <li><a href="<%=cpath%>/movieLogin.do">로그인</a></li>
      <li><a href="<%=cpath%>/moviemycal.do">캘린더</a></li>
    </ul>
    </div>
</nav>
<div class="wrap1">
    <div class="form-wrap">

        <form id="login" action="<%=cpath%>/memberInsert.do" method="post" class="input-group">
            <h1 style="font-weight: bold; text-align: center; color: white;">회원가입</h1>
            
            <input name="id" type="id" class="input-field" placeholder="아이디" required style="color: white;">
            <input name="name" type="name" class="input-field" placeholder="닉네임" required style="color: white;">
            <input name="pwd" type="password" class="input-field" placeholder="비밀번호" required style="color: white;">
            <input name="email" type="email" class="input-field" placeholder="이메일" required style="color: white;">
            <input name="admin" type="admin" class="input-field" placeholder="남/녀" required style="color: white;">
            <p class="input-field" style="color: gray; font-size: 15px; font-weight:lighter; ">나의 MBTI : <span class="MBTI">
            </span>
             &nbsp
              &nbsp
               &nbsp
            <select name="mbti" id="mbtilist" style="height:100%;">
              <option value="INTJ">INTJ</option>
              <option value="INTP">INTP</option>
              <option value="ENTJ">ENTJ</option>
              <option value="ENTP">ENTP</option>
              <option value="INFJ">INFJ</option>
              <option value="INFP">INFP</option>
              <option value="ENFJ">ENFJ</option>
              <option value="ENFP">ENFP</option>
              <option value="ISTJ">ISTJ</option>
              <option value="ISFJ">ISFJ</option>
              <option value="ESTJ">ESTJ</option>
              <option value="ESFJ">ESFJ</option>
              <option value="ISTP">ISTP</option>
              <option value="ISFP">ISFP</option>
              <option value="ESTP">ESTP</option>
              <option value="ESFP">ESFP</option>
          </select></p>
          <p style="color: white; text-align: center;">MBTI를 모르신다면 &nbsp <a style="font-weight : bold;" href="javascript:openPop()">여기로!</a></p>
            <button class="submit">가입완료</button>
            <br>
            <br>
        </form>
    </div>
</div>
<script type="text/javascript">
function openPop()
{
window.open("https://www.visualdive.com/wp-content/uploads/2020/07/mbti-%ED%85%8C%EC%8A%A4%ED%8A%B8-819x1024.jpg", "startpop", "top=0, left=0, width=1024, height=819, scrollbars=no, resizable=no ,status=no ,toolbar=no");
}
</script>
</body>

차례로 사용자는 달라지는 input타입에 따라 정보를 입력하고, mbti를 모를경우 간단하게 검사할 수 있는 p태그에 herf속성(javascript 사용)을 넣어 해당 p태그를 클릭할 시 가져다 놓은 사진 정보가 뜨게 하였다. 

 

회원가입 버튼을 누를 경우 그동안 form 태그에 담겨진 정보가 post의 방법으로 submit되어 db에 등록되게 된다. 

db에 submit된 정보가 등록되는 process는 다음과 같다. 


1. 서버(View)에서 입력된 정보가 controller로 getAttribute를 통해 흘러들어온다.

		String id=request.getParameter("id");
		String name=request.getParameter("name");
		String pwd=request.getParameter("pwd");
		String email=request.getParameter("email");
		String admin=request.getParameter("admin");
		String mbti=request.getParameter("mbti");

2. 요청을 받은 MainController는 Client가 하달한 지시에 자신이 동작을 수행할 수 있는 하위 controller가 있는지 요청사항을 HandlerMapping을 통해 판별한다. 

if(view!=null) {
			if(view.indexOf("redirect:")!=-1) {
			   //                    "/m02/boardList.do"
				//indexOf의 역할 : 뒤에 오는 글자가 있는지 없는지 찾는다. 글자가 없으면 -1을 넘겨준다.
				//리턴값에 redirect가 들어가서 sendRedirect를 해야하는 경우
			   response.sendRedirect(cpath+view.split(":")[1]);
			   // redirect:/boardList.do
			 //cpath : view를 split으로 나눈것의 [0]번째 
				//view로 나온 경로를 :을 기준으로 잘라서 [0]번째가 아닌 [1]번째에 있는 /boardList.do를 가져온다.
			}else {
				//리턴값에 redirect가 없어서 forwarding을 해야하는 경우
			   //                           "boardList"->"/WEB-INF/board/boardList.jsp"	
			   RequestDispatcher rd=request.getRequestDispatcher(ViewResolver.makeViewUrl(view));
			   rd.forward(request, response); // View(JSP)
			}//View(JSP)	
			//"/WEB-INF/board/boardContent.jsp"
			//물리적인경로------/view의논리적인이름/jsp
			//접두사(prefix)--/------------/접미사(suffix)
		}//


3. HandlerMapping을 통해 판별되어 일할 하위 POJO(Controller)가 있다면 DAO객체가 생성된 controller에서 DAO 중 id가 동일한 DAO method를 실행하라 명령한다. 

public HandlerMapping() {
		mappings=new HashMap<String, Controller>();
        mappings.put("/memberInsert.do", new MemberInsertController());
        }
public Controller getController(String key) {
		return mappings.get(key);
		}
BoardDAO dao = new BoardDAO();


3-1. DAO에서는 SQL Session을 연다. 그리고 세션은 해당하는 리턴타입에 따라 DataSet인 VO를 골라와 객체를 생성하기도 하고 int 타입의 필요한 데이터들을 반환한다. 대개 이 단계에서 오류가 많아 반드시 System.out.println 으로 불러와진 데이터들을 확인해야 한다. 

public int memberInsert(UserVO vo) {
	        SqlSession session = sqlSessionFactory.openSession();
	        int cnt = session.insert("memberInsert", vo);
	        System.out.println(cnt);
	        session.commit();
	        session.close();
	        return cnt;
	 }
UserVO vo = new UserVO();
		vo.setMb_id(id);
		vo.setMb_name(name);
		vo.setMb_pwd(pwd);
		vo.setMb_email(email);
		vo.setAdmin_yn(admin);
		vo.setMb_mbti(mbti);
		System.out.println(mbti);
        
int cnt = dao.memberInsert(vo);
	
		String nextPage = null;
		String cpath = request.getContextPath();
		System.out.println();
		if(cnt>0) {
			nextPage = "redirect:/movieLogin.do";
		} else {
			throw new ServletException("error");
		}
		return nextPage;


3-2. DAO 명령문 내에서 동일한 id를 가진 MyBatis로 분리한 SQL Query문이 실행되는 실질적인 Mapper.xml에 직접적인 SQL Query문을 실행하라 명령한다.

<insert id="memberInsert" parameterType="kr.smhrd.model.UserVO">
		insert into members(mb_id,mb_name,mb_pwd,mb_email,admin_yn,mb_mbti) 
		values(#{mb_id},#{mb_name},#{mb_pwd},#{mb_email},#{admin_yn},#{mb_mbti})
	</insert>


3-3. Mapper.xml에서는 DAO method에서 실행한 실질적인 작업을 수행한 후 커밋한 후 세션을 닫는다. 
4. DB에서 수행한 정보들의 결과가 제대로 잘 들어갔는지 확인한다.


화면설계서 - 로그인

위는 로그인 화면의 화면설계서이다. 코딩이 진행된 View에서 중요한 body 부분만 발췌하겠다.

<body>
     <ul class="nav navbar-nav navbar-right">
     <li><a href="<%=cpath%>/memberForm.do">회원가입</a></li>
     
    </ul>
<div class="wrap1">
    <div class="form-wrap">
        <form id="login" action="<%=cpath%>/login.do" method="post"  class="input-group">
            <h1 style="font-weight: bold; text-align: center; color: white;">로그인</h1>
            <br>
            <input type="id" name="id" id="id" class="input-field" placeholder="아이디" required style="color: white;" >
            <input type="password" id="pwd" name="pwd" class="input-field" placeholder="비밀번호" required style="color: white;" >
            <br>
            <br>
            <button class="submit" >로그인</button>
        </form>
    </div>
</div>
</body>

사용자는 input태그에 해당하는 정보를 입력하고 submit버튼을 누르면 기존 DB에 있던 정보와 비교해 동일한 하나의 정보를 불러오는 식의 process는 다음과 같다. 


1. 서버(View)에서 입력된 정보가 controller로 getAttribute를 통해 흘러들어온다.

PrintWriter writer = response.getWriter();

	   
      String id=request.getParameter("id");
      String pwd=request.getParameter("pwd");
      String mb_mbti=request.getParameter("mbti");

2. 요청을 받은 MainController는 Client가 하달한 지시에 자신이 동작을 수행할 수 있는 하위 controller가 있는지 요청사항을 HandlerMapping을 통해 판별한다. 

private HashMap<String, Controller> mappings;
	public HandlerMapping() {
		mappings=new HashMap<String, Controller>();
        mappings.put("/login.do", new LoginController());
        }
       
public Controller getController(String key) {
		return mappings.get(key);
		}

3. HandlerMapping을 통해 판별되어 일할 하위 POJO(Controller)가 있다면 DAO객체가 생성된 controller에서 DAO 중 id가 동일한 DAO method를 실행하라 명령한다. 또한 로그인이 성공한다면 데이터값을 가진 vo바구니(succ)들을 setAttribute한다.

UserVO vo=new UserVO();
      vo.setMb_id(id);
      vo.setMb_pwd(pwd);
      vo.setMb_mbti(mb_mbti);
      
     
// UserDAO -> BoardDAO
BoardDAO dao=new BoardDAO();
      UserVO succ = dao.isLogin(vo);
      System.out.println(succ);

3-1. DAO 명령문 내에서 동일한 id를 가진 MyBatis로 분리한 SQL Query문이 실행되는 실질적인 Mapper.xml에 직접적인 SQL Query문을 실행하라 명령한다.

public UserVO isLogin(UserVO vo) {
		SqlSession session=sqlSessionFactory.openSession();
		vo=session.selectOne("Login",vo);
		session.close();
		return vo;
	}

3-2. Mapper.xml에서는 DAO method에서 실행한 실질적인 작업을 수행한 후 세션을 닫는다. 

<select id="Login" parameterType="kr.smhrd.model.UserVO" resultType="kr.smhrd.model.UserVO">
		select * from members where mb_id=#{mb_id} and mb_pwd=#{mb_pwd}
	</select>

 

4. Query문의 수행후 메인 boardList로 다시 제대로 Redirect되었는지 확인한다.

 

이때 주소창에서 ?(=쿼리스트링) 뒤로 성공했을때의 데이터값을 가진 vo바구니(succ)들을 붙여 함께 redirect한다.

실패했을경우에는 로그인실패와 함께 안내문구를 출력하고 다시 movielogin.do로 보내준다.

if(succ!=null) { // 로그인 성공 -> 로그인 성공했다는 표시를 메모리(세션)에 해두어야 한다.
         HttpSession session=request.getSession();
         session.setAttribute("vo", succ);
         return "redirect:/moviembti2.do?mb_mbti="+succ.getMb_mbti()+"&mb_id="+succ.getMb_id()+"&mb_pwd="+succ.getMb_pwd(); //redirect에서 같이 넘겨줌
      }else {
    	  System.out.println("로그인 실패");
    	  writer.println("<script>alert('아이디와 비밀번호를 확인해주세요!'); location.href='movieLogin.do' </script>");
    	  writer.close();
    	  return "redirect:/movieLogin.do";
      }

 


위는 로그인 후 처음 접속한 사람이 볼 수 있는 MBTI기반 영화 추천화면의 화면설계서이다.

코딩이 진행된 View에서 중요한 body 부분만 발췌하겠다.

 

<!-- cpath를 받아와서 각각 필요한 정보들을 사용하기 위해 vo들을 getAttribute로 불러오는 코드 -->
      <%
    String cpath = request.getContextPath(); // /m02
    UserVO user=(UserVO)session.getAttribute("vo");
    MovieVO vo1 =(MovieVO)request.getAttribute("vo1");
    MovieVO vo2 =(MovieVO)request.getAttribute("vo2");
    MovieVO vo3 =(MovieVO)request.getAttribute("vo3");
    MovieVO vo4 =(MovieVO)request.getAttribute("vo4");
    /* MovieVO list1 =(MovieVO)request.getAttribute("list1"); */
    
    /* 여러개의 movieVO를 사용하기 때문에 ArrayList형태안에 넣어서 JSP내에서 사용한다.  */
    ArrayList<MovieVO> list1=(ArrayList<MovieVO>)request.getAttribute("list1");
    
    %>
    
 /* Ajax활용해서 좋아요 기능 구현 */
#heart{
    font-size: 30px;
    margin-top: 10px;
}
#heart:active{
  color: red;
  transform: translateY(4px);
}
i.selected{
  color: red;
}


<body>
<nav class="navbar navbar-default">
  <div class="container">
    <div class="navbar-header">
    	
    	<!-- 툴바 -->
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar">
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>                        
      </button>
      <a class="navbar-brand" href="<%=cpath%>/moviemain.do">BESPOKE CINEMA</a>
    </div>
    <div class="wrap">
      <div class="search">
         
        </button>
      </div>
   </div>
   
      <ul class="nav navbar-nav navbar-right">
       <li><a href="<%=cpath%>/moviemain.do">Main</a></li>
       <li><a href="<%=cpath%>/movieContent.do?idx=<%=user.getIdx()%>">내정보</a></li>
       <li><a href="<%=cpath%>/logout.do">로그아웃</a><li>
      </ul>
     
    </div>
  </div>
</nav>

	<!-- 가입한 회원의 DB에 저장되어있는 mbti출력 -->
<div class="jumbotron text-center">
    <h1>YOUR MBTI</h1>
    <h2><%=user.getMb_mbti()%></h2>
    
  </div>
   
  	<!-- DB에 해당 MBTI로 저장된 영화 4개를 랜덤으로 출력. 이하 4개 -->
<div class="container-fluid text-center bg-grey">
    
    <div class="row text-center">
      <div class="col-sm-4">
      
      	<!-- 포스터 및 제목 출력 -->
        <div class="thumbnail" style="position: relative; left: 5%;">
          <a href="#" class="myButton1">1</a>
          <img src="<%=list1.get(0).getMovie_pos()%>" alt="Paris">
          <p style="color: white; font-size: 20px;"><strong><%=list1.get(0).getMovie_title()%></strong></p>
          <!-- 좋아요 버튼 -->
          <div class="h_container">
            <i id="heart" class="far fa-heart"></i>
          </div>
          <!-- 상세보기 페이지 이동버튼 -->    
          <a href="<%=cpath%>/reviewList.do?movie_seq=<%=list1.get(0).getMovie_seq()%>" class="myButton">상세보기</a>
          </div>
        </div> 
     
     <!-- 두번째 -->
      <div class="row text-center">
      <div class="col-sm-4">
        <div class="thumbnail" style="position: relative; left: 5%;">
          <a href="#" class="myButton1">2</a>
          <img src="<%=list1.get(1).getMovie_pos()%>" alt="Paris">
          <p style="color: white; font-size: 20px;"><strong><%=list1.get(1).getMovie_title()%></strong></p>
          <div class="h_container">
            <i id="heart" class="far fa-heart"></i>
          </div>
              <a href="<%=cpath%>/reviewList.do?movie_seq=<%=list1.get(1).getMovie_seq()%>" class="myButton">상세보기</a>
            </div>
        </div>
     
      <!-- 세 번째 -->
      <div class="row text-center">
      <div class="col-sm-4">
        <div class="thumbnail" style="position: relative; left: 5%;">
          <a href="#" class="myButton1">3</a>
          <img src="<%=list1.get(2).getMovie_pos()%>" alt="Paris">
          <p style="color: white; font-size: 20px;"><strong><%=list1.get(2).getMovie_title()%></strong></p>
          <div class="h_container">
            <i id="heart" class="far fa-heart"></i>
          </div>
              <a href="<%=cpath%>/reviewList.do?movie_seq=<%=list1.get(2).getMovie_seq()%>" class="myButton">상세보기</a>
            </div>
                </div> 
      
      <!-- 네 번째 -->
      <div class="row text-center">
      <div class="col-sm-4">
        <div class="thumbnail" style="position: relative; left: 5%; bottom: 8%;">
          <a href="#" class="myButton1">4</a>
          <img src="<%=list1.get(3).getMovie_pos()%>" alt="Paris">
          <p style="color: white; font-size: 20px;"><strong><%=list1.get(3).getMovie_title()%></strong></p>
          <div class="h_container">
            <i id="heart" class="far fa-heart"></i>
          </div>
              <a href="<%=cpath%>/reviewList.do?movie_seq=<%=list1.get(3).getMovie_seq()%>" class="myButton">상세보기</a>
            </div>
        </div>
      </div>
  </div>
  </div>
   
  <!-- 하단 바  -->
  <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
   <hr>
            <!-- 자바스크립트 ajax 이용한 좋아요버튼을 누르면 색깔이 변하면서 간단한 애니메이션이 나온다. -->
                <span style="color: white;">Bespoke cinema</span>
                <script>
					$('i').on('click', function(){
 					 $('i').removeClass('selected');
 					 $(this).addClass('selected');
	

						});
</script>




</body>

사용된 controller는 다음과 같다.

public class MbtiMainController implements Controller{
	@Override
	public String requestHandler(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		UserVO vo = new UserVO();
        MovieDAO dao1=new MovieDAO();
        
        //movie DB에 저장된 모든 리스트들을 DAO에서 allmovieList라는 아이디를 가진 애 중에 mapper와 연결하여 해당 기능을 불러온다.
        //mapper실행결과 모든 movie데이터가 불러져 온다.(select *)
		ArrayList<MovieVO> list = (ArrayList<MovieVO>)dao1.allMovieList();
		System.out.println(vo);
		 
	    //추천해줄영화 중  회원의 mbti장르와 불러온 mbti장르가 같은 영화를 불러오는 코드이다.  
		Random rd = new Random();
		//방이 4개인 배열을 만든다. (사이트에서 4개의 배열을 보여주니까)
		int suggest[] = new int[4];
		
	      //4개의 랜덤한 영화를 뽑는 이중 for문.
	      for(int i=0; i<suggest.length; i++) {
	    	  //추천한 영화의 seq를 뽑는다.시퀀스는 1부터 시작하므로 1을 더해준다.
	         suggest[i] = rd.nextInt(list.size())+1;
	         //만약 뽑아온 seq가 기존의 것과 동일할 경우 한번 줄여 다시 random seq를 뽑게 하여 동일한 영화를 두번 뽑게 하는 일이 없도록 한다.
	         for(int j=0; j<i; j++) {
	            if(suggest[i] == suggest[j]) {
	               i--;
	            }
	         }
	      }
	      
	      //뽑힌 추천영화 4개를 vo1~4까지 담아 setAttribute로 보낼준비를 하자.
	      MovieVO vo1=dao1.movieList1(suggest[0]);
	      request.setAttribute("vo1", vo1);
	     
	      MovieVO vo2=dao1.movieList1(suggest[1]);
	      request.setAttribute("vo2", vo2);
	      
	      
	      MovieVO vo3=dao1.movieList1(suggest[2]);
	      request.setAttribute("vo3", vo3);
	      
	      MovieVO vo4=dao1.movieList1(suggest[3]);
	      request.setAttribute("vo4", vo4);
     
         return "mbtimain";
         
	}

}

 

Comments