[Framework] TIL 054 - 23.10.04

유진·2023년 10월 4일
0

07_Framework

main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>프로젝트</title>
	
	<!-- fontaswesom 아이콘 사용할 수 있는 스크립트 연결-->
	<script src="https://kit.fontawesome.com/f821b57119.js" crossorigin="anonymous"></script>
</head>
<body>
	<main>
        
        
        <%-- header.jsp 추가 --%>
        
        <%--
        	<jsp:include page="jsp파일경로" />
        	
        	- jsp 파일 경로는 'webapp 폴더 기준'으로 작성
        	- JSP 액션 태그(jsp에 기본 내장됨)
        	- 다른 jsp 파일의 코드를 현재 위치에 추가
        
         --%>
         
        <jsp:include page="/WEB-INF/views/common/header.jsp" />
		

        <section class="content">
            <section class="content-1">
            	<h3>로그인된 회원 정보</h3>
            
            
            </section>
            

            <!-- 아이디/비밀번호/로그인버튼 영역 -->
            <section class="content-2">
            
            	<c:choose>
            		<%-- 로그인이 안되었을때 --%>
            		<%-- EL empty : 비어있거나 null이면 true --%>
            		<c:when test="${empty sessionScope.loginMember}">
            		
	            		<form action="/member/login" method="post" name="login-form" id="loginFrm">
	
		                    <fieldset class="id-pw-area">
		                        <section>
		                            <input type="text" name="memberEmail" placeholder="이메일">
		                            <input type="password" name="memberPw" placeholder="비밀번호">
		                        </section>
		
		                        <section>
		                            <button>로그인</button>
		                        </section>
		                    </fieldset>
		
		                    <label>
		                        <input type="checkbox" name="saveId"> 아이디 저장
		                    </label>
		
		                    <!-- 회원가입/ Id/pw 찾기 영역 -->
		                    <section class="signup-find-area">
		                        <a href="#">회원가입</a>
		                        <span>|</span>
		                        <a href="#">ID/PW 찾기</a>
		                    </section>
	
	                	</form>
            		</c:when>
            		
            		<%-- 로그인이 되었을때 --%>
            		<c:otherwise>
            			<article class="login-area">
            				
            				<a href="#">
            					<img src="/resources/images/user.png" id="memberProfile">
            				</a>
            				
            				<div class="my-info">
            					<div>
            						<a href="#" id="nickname">${sessionScope.loginMember.memberNickname}</a>
            						<a href="/member/logout" id="logoutBtn">로그아웃</a>
            					</div>
            					
            					<p></p>
            				</div>
            				
            			</article>
            			
            			
            		</c:otherwise>
            	</c:choose>
            
            

            </section>

        </section>
       

    </main>
    
    <jsp:include page="/WEB-INF/views/common/footer.jsp" />
</body>
</html>

MemberController.java

package edu.kh.project.member.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;

import edu.kh.project.member.model.dto.Member;
import edu.kh.project.member.model.service.MemberService;

@Controller // 요청/응답 클래스 + bean 등록(Spring 관리하는 객체)
@RequestMapping("/member") // 공통된 주소 앞부분 작성, member로 시작하는 요청은 해당 컨트롤러에서 처리
@SessionAttributes({"loginMember"}) // Model의 이름(key)를 적으면 session으로 추가
									// 올리고 싶은게 여러 개일 수 있기 때문에 나열하고 싶으면 {} 사용하여 적기
public class MemberController {
	
	// 등록된 Bean 중에서 필드와 타입이 일치하는 Bean 주입
	// -> MemberService를 구현한 MemberServiceImpl의 Bean 주입
	@Autowired
	private MemberService service;
	
	

	// 로그인 : /member/login
	// 로그아웃 : /member/logout
	
	// /member/login, post 처리방식
	
	
	// @RequestMapping : 요청 주소에 맞는 클래스/메서드 연결
	// @RequestMapping("요청주소") : -> GET / POST 구분 X , 주소만 맞으면 연결하지만 GET요청시 사용
	// @RequestMapping(value="/login", method=RequestMethod.GET/POST) -> GET/POST 방식을 구분
	
    // @RequestMapping(value="/login", method=RequestMethod.POST)
	public String login(HttpServletRequest req) {
		
		// 파라미터 전달 방법 1 : HttpServletRequest를 이용하는 방법
		// -> Controller 메서드에 매개변수로 HttpServletRequest를 작성
		
		// 매개변수에 적으면 사용 가능한 이유
		// Spring 제공하는 Argument Resolver(매개변수해결사)가 자동으로
		// 대입해주고 해결해줌.
		
		String inputEmail = req.getParameter("inputEmail");
		String inputPw = req.getParameter("inputPw");
		
		System.out.println("inputEmail : " + inputEmail);
		System.out.println("inputPw : " + inputPw);
		
		
		// ******************* redirect 방법! *******************
		// "redirect:요청주소"
		
		return "redirect:/";
	}
	
	
	// @PostMapping : @RequestMapping 자식으로
	//				Post 방식 요청을 연결하는 어노테이션
	//@PostMapping("/login")
	public String login(/*@RequestParam("inputEmail")*/ String inputEmail,
						/*@RequestParam("inputPw")*/ String inputPw/*,
						@RequestParam(value="saveId", required="fasle", defaultValue="1")*/) {
		
		// 파라미터 전달 방법 2 : @RequestParam 어노테이션 이용(+생략방법)
		
		// @RequestParam 어노테이션
		// - request 객체를 이용한 파라미터 전달 어노테이션
		// - 매개변수 앞에 해당 어노테이션 작성하면, 매개변수에 값이 주입됨.
		
		// ** 파라미터의 name 속성값과
		//	매개변수명이 같으면 @RequestParam 생략 가능! **
		
		
		// @RequestParam(value="name", required="fasle", defaultValue="1")
		// [속성]
		// value : 전달 받은 input 태그의 name 속성값
		
		// required : 입력된 name 속성값 파라미터 필수 여부 지정(기본값 true)
		// -> required = true인 파라미터가 존재하지 않는다면 400 Bad Request 에러 발생
		// -> required = true인 파라미터가 null인 경우에도 400 Bad Request

		// defaultValue : 파라미터 중 일치하는 name 속성 값이 없을 경우에 대입할 값 지정.
		// -> required = false인 경우 사용
		
		
		System.out.println("inputEmail : " + inputEmail);
		System.out.println("inputPw : " + inputPw);

		
		return "redirect:/";
	}
	
	
	//@PostMapping("/login")
	public String login(/*@ModelAttribute*/ Member inputMember) {
		
		// 파라미터 전달 방법 3 : @ModelAttribute 이용한 방법
		
		// - DTO(또는 VO)와 같이 사용하는 어노테이션
		
		// - 전달 받은 파라미터의 name 속성 값이
		//   같이 사용되는 DTO의 필드명과 같다면
		//   자동으로 setter를 호출해서 필드에 값을 세팅
		
		System.out.println(inputMember);
		
		// ** @ModelAttribute 사용 시 주의 사항 **
		// - DTO에 기본 생성자가 필수로 존재해야 한다
		// - DTO에 setter가 필수로 존재해야 한다
		
		// ** ModelAttribute 어노테이션은 생략이 가능하다 ! ** 
		
		// ** '@ModelAttribute'를 이용해 값이 필드에 세팅된 객체를
		//	'커맨드 객체' 라고 부른다.
		
		
		// Member [ memberEmail = user123, memberPw = pass123.. ]
		
		
		return "redirect:/";
	}
	
	
	/* 찐 로그인 메서드 */
	
	@PostMapping("/login")
	public String login(Member inputMember, Model model,
						@RequestHeader("referer") String referer
			) {
		
		// @RequestHeader(value="referer") String referer
		// -> 요청 HTTP header에서 "referer" (이전 주소) 값을 얻어와
		//  매개 변수 String referer에 저장
		
		
		// Model : 데이터 전달용 객체
		// -> 데이터를 K : V 형식으로 담아 전달
		// -> 기본적으로 request scope
		// -> @SessionAttributes 어노테이션과 함께 사용 시 session scope
		

		// 로그인 서비스 호출
		Member loginMember = service.login(inputMember);
		
		// DB 조회 결과 확인
		//System.out.println(loginMember);
		
		// 로그인 결과에 따라 리다이렉트 경로를 다르게 지정
		String path = "redirect:";
		
		if(loginMember != null) {  // 로그인 성공시
			path += "/";  // 메인페이지로 리다이렉트
			
			// session loginMember 추가
			
			// Session에 로그인한 회원 정보 추가
			// Servlet : HttpSession.setAttribute(key, value)
			// Spring  : Model + @SessionAttributes
			
			// 1) model에 로그인한 회원 정보 추가
			model.addAttribute("loginMember", loginMember);
			// -> 현재는 request scope
			
			// 2) 클래스 위에 @SessionAttributes 추가
			// -> 이제 session scope
			
			// --------------------------------------
			
			// 아이디 저장 (Cookie)
			
			
			
		} else {  // 로그인 실패
			path += referer;
			
			// message 추가 (아이디 또는 비밀번호 불일치)
			
			
		}
		
		return path;
	}
	
}

MemberService.java

package edu.kh.project.member.model.service;

import edu.kh.project.member.model.dto.Member;

// Service Interface 사용 이유

// 1. 프로젝트에 '규칙성'을 부여하기 위해서 == 모든 클래스가 같은 형태를 띔(협업 위함)

// 2. 클래스간의 '결합도를 약화' 시키기 위해서 (객체 지향적 설계) == MemberService service = new MemberServiceImpl();
// -> 유지보수성 향상								   MemberService service = new MemberServiceImpl2();
//												   MemberService service = new MemberServiceImpl3();

// 3. Spring AOP 사용을 위해서
// -> AOP는 spring-proxy를 이용해서 동작하는데
//  이 때 Service 인터페이스가 필요하다!

public interface MemberService {
	
	/** 로그인 서비스
	 * @param inputMember (email, pw)
	 * @return email, pw가 일치하는 회원정보 또는 null
	 */
	Member login(Member inputMember);

}

MemberServiceImpl.java

package edu.kh.project.member.model.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import edu.kh.project.member.model.dao.MemberDAO;
import edu.kh.project.member.model.dto.Member;

@Service // 비즈니스 로직(데이터 가공, DAO 호출, 트랜잭션 제어)처리하는 클래스 명시
		// + Bean으로 등록하는 어노테이션
public class MemberServiceImpl implements MemberService{
	
	// @Autowired : 작성된 필드와
	// Bean으로 등록된 객체 중 타입이 일치하는 Bean을
	// 해당 필드에 자동 주입(Injection) 하는 어노테이션.
	// == DI(Dependency Injection, 의존성 주입)
	// -> 객체를 직접 만들지 않고, Spring이 만든걸 주입함(Spring에 의존)
	
	@Autowired
	private MemberDAO dao;

	@Override
	public Member login(Member inputMember) {
		
		// dao 메서드 호출
		Member loginMember = dao.login(inputMember);
		
		return loginMember;
	}
	
	
}

MemberDAO.java

package edu.kh.project.member.model.dao;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import edu.kh.project.member.model.dto.Member;

@Repository // Persistence Layer, 영속성 관련 클래스 // 영속성 == 영원히 지속되는 성질
			// (DB관련 클래스) + Bean(== Spring 객체화) 등록
public class MemberDAO { // IOC == 제어의 역전
	// DAO는 DB랑 연결하기 위한 Connection이 공통적으로 필요하다
	// + Mybatis 영속성 프레임워크를 이용하려면 Connection을 이용해서 만들어진 객체
	// SqlSessionTemplate을 사용
	@Autowired
	private SqlSessionTemplate sqlSession;
	
	

	/** DAO
	 * @param inputMember
	 * @return 회원 정보 또는 null
	 */
	public Member login(Member inputMember) {
		
		// 마이바티스를 이용해서 1행 조회(selectOne)
		
		// sqlSession.selectOne("namespace값.id값", 전달할 값);
		// -> namespace가 일치하는 Mapper에서
		// id가 일치하는 SQL 구문을 수행 후
		// 결과를 1행 반환
		
		return sqlSession.selectOne("memberMapper.login", inputMember);
	}

}

member-mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="memberMapper">
	<!-- namespace : 해당 공간의 이름 지정 -->

	<!-- mapper 파일 생성 시 아래 태그 반드시 삭제 -->
	<!--   <cache-ref namespace=""/>   -->
	
	<!-- 작성예시 -->
	<!-- <select id=""></select>  -->
	<!-- <insert id=""></insert>  -->
	
	<!-- 
		resultMap  
		- SELECT 조회 결과(ResultSet) 컬럼명과
		  컬럼 값을 옮겨 담을 DTO의 필드명이 같지 않을 때
		  이를 매핑 시켜 SELECT시 자동으로 담기게하는 역할
	 
	 
	 	-속성
	 	type : 연결할 DTO (패키지명 + 클래스명 또는 별칭)
	 	id : 만들어진 resultMap을 지칭할 식별명(이름)
	 	
	 	<id> 태그 : PK 역할 컬럼 - 필드 매핑
	 	<result> 태그 : <id>제외 나머지
	 -->
	 
	<resultMap type="Member" id="member_rm">
	
	<!-- property가 java, column이 db라고 생각하면 됨. -->
	
		<!-- DB의 기본 키(복합키면 여러 개 작성) -->
		<id property="memberNo" column="MEMBER_NO" />

		<!-- DB의 일반 컬럼들 -->
		<result property="memberEmail" column="MEMBER_EMAIL" />
		<result property="memberPw" column="MEMBER_PW" />
		<result property="memberNickname" column="MEMBER_NICKNAME" />
		<result property="memberTel" column="MEMBER_TEL" />
		
		<result property="memberAddress" column="MEMBER_ADDR" />
		
		<result property="profileImage" column="PROFILE_IMG" />
		<result property="enrollDate" column="ENROLL_DATE" />
		<result property="memberDeleteFlag" column="MEMBER_DEL_FL" />
		<result property="authority" column="AUTHORITY" />
	</resultMap>
	
	<!--  
  		SQL 관련 태그의 속성
  		
  		- parameterType : 전달 받은 값의 자료형
  						기본 : 패키지명 + 클래스명
  						별칭 : Mybatis 별칭 또는 사용자 지정 별칭
  						
		- parameterMap : (사용 안함)
		
		- resultType : select 결과를 담아서 반환할 자료형
					단, DTO를 작성할 경우 필드명 = 컬럼명 인 경우만 가능
										memberNo   MEMBER_NO
		
		- resultMap : select 결과의 컬럼명과 
					 결과를 저장할 DTO 필드명이 다를 경우
					 이를 알맞게 매핑(연결)시켜주는 <resultMap> id 작성
  	-->
  	
  	
  	<!-- 
  		** 마이바티스에서 전달 받은 값을 SQL에 작성하는 방법 **
  		
  		#{변수명|필드명} : PreparedStatement -> placeholder 대체하여 사용
  						 : SQL에 값 대입 시 양쪽에 '' 붙여서 대입
  		
  		${변수명|필드명} : Statement -> '숫자' 쓸 때 많이 사용
  						 : SQL에 값 대입 시 양쪽에 아무것도 붙이지 않음
 						 
		사용 예시)
		test1 = "user01"
		test2 = MEMBER_EMAIL
  	
  		- MEMBER_EMAIL이 'user01'인 회원 조회
  		SELECT * FROM MEMBER WHERE ${test2} = #{test1}
  								 MEMBER_EMAIL = 'user01'
  	 -->
	
	<!-- 
		<select></select>
		<insert></insert>
		<update></update>
		<delete></delete>
		사용 가능한 속성도 조금씩 다름
	 -->
	<select id="login" parameterType="Member" resultMap="member_rm">
  		SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICKNAME, MEMBER_PW,
			MEMBER_TEL, MEMBER_ADDR, PROFILE_IMG, AUTHORITY,
			TO_CHAR(ENROLL_DATE, 'YYYY"년" MM"월" DD"일" HH24"시" MI"분" SS"초"') AS ENROLL_DATE 
		FROM "MEMBER"
		WHERE MEMBER_DEL_FL = 'N'
		AND MEMBER_EMAIL = #{memberEmail}
  	</select>
	
</mapper>

Member.java

package edu.kh.project.member.model.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@NoArgsConstructor
@Getter
@Setter
@ToString
public class Member {
	private int memberNo;
	private String memberEmail;
	private String memberPw;
	private String memberNickname;
	private String memberTel;
	private String memberAddress;
	private String profileImage;
	private String enrollDate;
	private String memberDeleteFlag;
	private int authority;
}

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>

	<!-- SqlSessionTemplate 관련 설정 -->
	<settings>
	
		<!-- insert, update 사용 값중 null 이 있을 경우 
			SQL 구문에 null 포함되어 있다는 오류 발생
			이 설정 후, 오류 발생 X, NULL 값을 컬럼에 대입
			단, NOT NULL 제약조건이 없는 컬럼에만 가능
		-->
		<setting name="jdbcTypeForNull" value="NULL"/>
	</settings>
	
	<!-- 별칭 작성 부분 -->
	<!-- VO/DTO 클래스의 패키지명+클래스명 작성하는게 불편하기 때문에 짧은 별칭을 부여 -->
	<typeAliases>
		<typeAlias type="edu.kh.project.member.model.dto.Member" alias="Member"/>
	
	</typeAliases>
	
	
	<!-- mapper파일(SQL 작성되는파일) 위치 등록 부분 -->
	<mappers>
		<mapper resource="/mappers/member-mapper.xml"/>
		<!-- 추후 board-mapper를 사용하고 싶다면 추가해야 함!
		<mapper resource="/mappers/board-mapper.xml"/>
		 -->
	</mappers>


</configuration>


이메일, 패스워드 입력 후 로그인 버튼 클릭 시, 화면 전환됨.


  • 로그인 실패 시 메세지 출력

MemberController.java

package edu.kh.project.member.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import edu.kh.project.member.model.dto.Member;
import edu.kh.project.member.model.service.MemberService;

@Controller // 요청/응답 클래스 + bean 등록(Spring 관리하는 객체)
@RequestMapping("/member") // 공통된 주소 앞부분 작성, member로 시작하는 요청은 해당 컨트롤러에서 처리
@SessionAttributes({"loginMember"}) // Model의 이름(key)를 적으면 session으로 추가
									// 올리고 싶은게 여러 개일 수 있기 때문에 나열하고 싶으면 {} 사용하여 적기
public class MemberController {
	
	// 등록된 Bean 중에서 필드와 타입이 일치하는 Bean 주입
	// -> MemberService를 구현한 MemberServiceImpl의 Bean 주입
	@Autowired
	private MemberService service;
	
	

	// 로그인 : /member/login
	// 로그아웃 : /member/logout
	
	// /member/login, post 처리방식
	
	
	// @RequestMapping : 요청 주소에 맞는 클래스/메서드 연결
	// @RequestMapping("요청주소") : -> GET / POST 구분 X , 주소만 맞으면 연결하지만 GET요청시 사용
	// @RequestMapping(value="/login", method=RequestMethod.GET/POST) -> GET/POST 방식을 구분
	
    // @RequestMapping(value="/login", method=RequestMethod.POST)
	public String login(HttpServletRequest req) {
		
		// 파라미터 전달 방법 1 : HttpServletRequest를 이용하는 방법
		// -> Controller 메서드에 매개변수로 HttpServletRequest를 작성
		
		// 매개변수에 적으면 사용 가능한 이유
		// Spring 제공하는 Argument Resolver(매개변수해결사)가 자동으로
		// 대입해주고 해결해줌.
		
		String inputEmail = req.getParameter("inputEmail");
		String inputPw = req.getParameter("inputPw");
		
		System.out.println("inputEmail : " + inputEmail);
		System.out.println("inputPw : " + inputPw);
		
		
		// ******************* redirect 방법! *******************
		// "redirect:요청주소"
		
		return "redirect:/";
	}
	
	
	// @PostMapping : @RequestMapping 자식으로
	//				Post 방식 요청을 연결하는 어노테이션
	//@PostMapping("/login")
	public String login(/*@RequestParam("inputEmail")*/ String inputEmail,
						/*@RequestParam("inputPw")*/ String inputPw/*,
						@RequestParam(value="saveId", required="fasle", defaultValue="1")*/) {
		
		// 파라미터 전달 방법 2 : @RequestParam 어노테이션 이용(+생략방법)
		
		// @RequestParam 어노테이션
		// - request 객체를 이용한 파라미터 전달 어노테이션
		// - 매개변수 앞에 해당 어노테이션 작성하면, 매개변수에 값이 주입됨.
		
		// ** 파라미터의 name 속성값과
		//	매개변수명이 같으면 @RequestParam 생략 가능! **
		
		
		// @RequestParam(value="name", required="fasle", defaultValue="1")
		// [속성]
		// value : 전달 받은 input 태그의 name 속성값
		
		// required : 입력된 name 속성값 파라미터 필수 여부 지정(기본값 true)
		// -> required = true인 파라미터가 존재하지 않는다면 400 Bad Request 에러 발생
		// -> required = true인 파라미터가 null인 경우에도 400 Bad Request

		// defaultValue : 파라미터 중 일치하는 name 속성 값이 없을 경우에 대입할 값 지정.
		// -> required = false인 경우 사용
		
		
		System.out.println("inputEmail : " + inputEmail);
		System.out.println("inputPw : " + inputPw);

		
		return "redirect:/";
	}
	
	
	//@PostMapping("/login")
	public String login(/*@ModelAttribute*/ Member inputMember) {
		
		// 파라미터 전달 방법 3 : @ModelAttribute 이용한 방법
		
		// - DTO(또는 VO)와 같이 사용하는 어노테이션
		
		// - 전달 받은 파라미터의 name 속성 값이
		//   같이 사용되는 DTO의 필드명과 같다면
		//   자동으로 setter를 호출해서 필드에 값을 세팅
		
		System.out.println(inputMember);
		
		// ** @ModelAttribute 사용 시 주의 사항 **
		// - DTO에 기본 생성자가 필수로 존재해야 한다
		// - DTO에 setter가 필수로 존재해야 한다
		
		// ** ModelAttribute 어노테이션은 생략이 가능하다 ! ** 
		
		// ** '@ModelAttribute'를 이용해 값이 필드에 세팅된 객체를
		//	'커맨드 객체' 라고 부른다.
		
		
		// Member [ memberEmail = user123, memberPw = pass123.. ]
		
		
		return "redirect:/";
	}
	
	
	/* 찐 로그인 메서드 */
	
	@PostMapping("/login")
	public String login(Member inputMember, Model model,
						@RequestHeader("referer") String referer,
						RedirectAttributes ra
			) {
		
		// @RequestHeader(value="referer") String referer
		// -> 요청 HTTP header에서 "referer" (이전 주소) 값을 얻어와
		//  매개 변수 String referer에 저장
		
		
		// Model : 데이터 전달용 객체
		// -> 데이터를 K : V 형식으로 담아 전달
		// -> 기본적으로 request scope
		// -> @SessionAttributes 어노테이션과 함께 사용 시 session scope
		

		// 로그인 서비스 호출
		Member loginMember = service.login(inputMember);
		
		// DB 조회 결과 확인
		//System.out.println(loginMember);
		
		// 로그인 결과에 따라 리다이렉트 경로를 다르게 지정
		String path = "redirect:";
		
		if(loginMember != null) {  // 로그인 성공시
			path += "/";  // 메인페이지로 리다이렉트
			
			// session loginMember 추가
			
			// Session에 로그인한 회원 정보 추가
			// Servlet : HttpSession.setAttribute(key, value)
			// Spring  : Model + @SessionAttributes
			
			// 1) model에 로그인한 회원 정보 추가
			model.addAttribute("loginMember", loginMember);
			// -> 현재는 request scope
			
			// 2) 클래스 위에 @SessionAttributes 추가
			// -> 이제 session scope
			
			// --------------------------------------
			
			// 아이디 저장 (Cookie)
			
			
			
		} else {  // 로그인 실패
			path += referer;
			
			// message 추가 (아이디 또는 비밀번호 불일치)
			
			/* redirect(재요청) 시
			 * 기존 요청(request)이 사라지고
			 * 새로운 요청(request)을 만들게 되어
			 * redirect된 페이지에서는 이전 요청이 유지 되지 않는다!
			 * -> 유지 하고 싶으면 어쩔 수 없이 session scope를 이용
			 * 
			 * RedirectAttributes를 스프링에서 제공
			 * - 리다이렉트 시 데이터를 request scope로 전달할 수 있게하는 객체
			 * 
			 * 응답 전 : request scope
			 * 
			 * 응답 중 : session scope로 잠시 이동
			 * 
			 * 응답 후 : request scope로 복귀
			 * 
			 */
			
			// addFlashAttribute : 잠시 session 에 추가
			ra.addFlashAttribute("message", "아이디 또는 비밀번호 불일치");
		}
		
		return path;
	}
	
}

footer.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

    
<!-- footer는 주요 콘텐츠가 아니라서 보통 main 태그 안에 작성하지 않음. -->
<footer>
   <p>Copyright &copy; KH Information Educational Institute E-Class</p>

   <section>
       <a href="#">프로젝트 소개</a>
       <span>|</span>
       <a href="#">이용약관</a>
       <span>|</span>
       <a href="#">개인정보처리방침</a>
       <span>|</span>
       <a href="#">고객센터</a>
   </section>
</footer>

<%-- request에 message가 존재할 경우 --%>
<c:if test="${not empty message}">
	
	<script>
		// EL/JSTL 구문이 먼저 해석
		// 문자열의 경우 따옴표가 없는 상태이니 옆에 붙여줘야함.
		<%-- alert 안에는 문자열만 들어와야 함 --%>
		alert('${message}') // ${message}
	</script>
	
	<%--
		session에 message를 추가하면
		브라우저 종료 또는 만료 전까지 계속 메세지가 출력된다
	
		-> 1회 출력 후 session에서 message 삭제
	 
	 <c:remove var="message" scope="session"/>
	
	--%>
	
</c:if>


이메일, 패스워드 틀리게 입력 후 로그인 버튼 클릭 시,
팝업창 출력되고 기존 로그인 화면으로 돌아감


  • 아이디 저장 (Cookie)

MemberController.java

package edu.kh.project.member.controller;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import edu.kh.project.member.model.dto.Member;
import edu.kh.project.member.model.service.MemberService;

@Controller // 요청/응답 클래스 + bean 등록(Spring 관리하는 객체)
@RequestMapping("/member") // 공통된 주소 앞부분 작성, member로 시작하는 요청은 해당 컨트롤러에서 처리
@SessionAttributes({"loginMember"}) // Model의 이름(key)를 적으면 session으로 추가
									// 올리고 싶은게 여러 개일 수 있기 때문에 나열하고 싶으면 {} 사용하여 적기
public class MemberController {
	
	// 등록된 Bean 중에서 필드와 타입이 일치하는 Bean 주입
	// -> MemberService를 구현한 MemberServiceImpl의 Bean 주입
	@Autowired
	private MemberService service;
	
	

	// 로그인 : /member/login
	// 로그아웃 : /member/logout
	
	// /member/login, post 처리방식
	
	
	// @RequestMapping : 요청 주소에 맞는 클래스/메서드 연결
	// @RequestMapping("요청주소") : -> GET / POST 구분 X , 주소만 맞으면 연결하지만 GET요청시 사용
	// @RequestMapping(value="/login", method=RequestMethod.GET/POST) -> GET/POST 방식을 구분
	
    // @RequestMapping(value="/login", method=RequestMethod.POST)
	public String login(HttpServletRequest req) {
		
		// 파라미터 전달 방법 1 : HttpServletRequest를 이용하는 방법
		// -> Controller 메서드에 매개변수로 HttpServletRequest를 작성
		
		// 매개변수에 적으면 사용 가능한 이유
		// Spring 제공하는 Argument Resolver(매개변수해결사)가 자동으로
		// 대입해주고 해결해줌.
		
		String inputEmail = req.getParameter("inputEmail");
		String inputPw = req.getParameter("inputPw");
		
		System.out.println("inputEmail : " + inputEmail);
		System.out.println("inputPw : " + inputPw);
		
		
		// ******************* redirect 방법! *******************
		// "redirect:요청주소"
		
		return "redirect:/";
	}
	
	
	// @PostMapping : @RequestMapping 자식으로
	//				Post 방식 요청을 연결하는 어노테이션
	//@PostMapping("/login")
	public String login(/*@RequestParam("inputEmail")*/ String inputEmail,
						/*@RequestParam("inputPw")*/ String inputPw/*,
						@RequestParam(value="saveId", required="fasle", defaultValue="1")*/) {
		
		// 파라미터 전달 방법 2 : @RequestParam 어노테이션 이용(+생략방법)
		
		// @RequestParam 어노테이션
		// - request 객체를 이용한 파라미터 전달 어노테이션
		// - 매개변수 앞에 해당 어노테이션 작성하면, 매개변수에 값이 주입됨.
		
		// ** 파라미터의 name 속성값과
		//	매개변수명이 같으면 @RequestParam 생략 가능! **
		
		
		// @RequestParam(value="name", required="fasle", defaultValue="1")
		// [속성]
		// value : 전달 받은 input 태그의 name 속성값
		
		// required : 입력된 name 속성값 파라미터 필수 여부 지정(기본값 true)
		// -> required = true인 파라미터가 존재하지 않는다면 400 Bad Request 에러 발생
		// -> required = true인 파라미터가 null인 경우에도 400 Bad Request

		// defaultValue : 파라미터 중 일치하는 name 속성 값이 없을 경우에 대입할 값 지정.
		// -> required = false인 경우 사용
		
		
		System.out.println("inputEmail : " + inputEmail);
		System.out.println("inputPw : " + inputPw);

		
		return "redirect:/";
	}
	
	
	//@PostMapping("/login")
	public String login(/*@ModelAttribute*/ Member inputMember) {
		
		// 파라미터 전달 방법 3 : @ModelAttribute 이용한 방법
		
		// - DTO(또는 VO)와 같이 사용하는 어노테이션
		
		// - 전달 받은 파라미터의 name 속성 값이
		//   같이 사용되는 DTO의 필드명과 같다면
		//   자동으로 setter를 호출해서 필드에 값을 세팅
		
		System.out.println(inputMember);
		
		// ** @ModelAttribute 사용 시 주의 사항 **
		// - DTO에 기본 생성자가 필수로 존재해야 한다
		// - DTO에 setter가 필수로 존재해야 한다
		
		// ** ModelAttribute 어노테이션은 생략이 가능하다 ! ** 
		
		// ** '@ModelAttribute'를 이용해 값이 필드에 세팅된 객체를
		//	'커맨드 객체' 라고 부른다.
		
		
		// Member [ memberEmail = user123, memberPw = pass123.. ]
		
		
		return "redirect:/";
	}
	
	
	/* 찐 로그인 메서드 */
	
	// Session : '서버' -> 보안에 더 유리
	// Cookie : '클라이언트'(브라우저) -> 속도는 조금 더 빠름
	
	@PostMapping("/login")
	public String login(Member inputMember, Model model, // 파라미터 추가할때마다 , 적어주기!
						@RequestHeader("referer") String referer,
						RedirectAttributes ra,
						@RequestParam(value="saveId", required=false) String saveId,
						HttpServletResponse resp
						) {
		
		// @RequestHeader(value="referer") String referer
		// -> 요청 HTTP header에서 "referer" (이전 주소) 값을 얻어와
		//  매개 변수 String referer에 저장
		
		
		// Model : 데이터 전달용 객체
		// -> 데이터를 K : V 형식으로 담아 전달
		// -> 기본적으로 request scope
		// -> @SessionAttributes 어노테이션과 함께 사용 시 session scope
		
		// @RequestParam(value="saveId", required=false) String saveId
		// -> name 속성값이 saveId인 파라미터를 전달받아서 저장
		// required 미작성 시 기본 값 true
		// required = false : 필수 아님(null 허용) 
		
		

		// 로그인 서비스 호출
		Member loginMember = service.login(inputMember);
		
		// DB 조회 결과 확인
		//System.out.println(loginMember);
		
		// 로그인 결과에 따라 리다이렉트 경로를 다르게 지정
		String path = "redirect:";
		
		if(loginMember != null) {  // 로그인 성공시
			path += "/";  // 메인페이지로 리다이렉트
			
			// session loginMember 추가
			
			// Session에 로그인한 회원 정보 추가
			// Servlet : HttpSession.setAttribute(key, value)
			// Spring  : Model + @SessionAttributes
			
			// 1) model에 로그인한 회원 정보 추가
			model.addAttribute("loginMember", loginMember);
			// -> 현재는 request scope
			
			// 2) 클래스 위에 @SessionAttributes 추가
			// -> 이제 session scope
			
			// --------------------------------------
			
			// 아이디 저장 (Cookie)
			
			/* Cookie란?
			 * - 클라이언트 측(브라우저)에서 관리하는 파일
			 * 
			 * - 쿠키파일에 등록된 주소 요청 시 마다
			 * 	 자동으로 요청에 첨부되어 서버로 전달됨.
			 * 
			 * - 서버로 전달된 쿠키에
			 * 	 값 추가, 수정, 삭제 등을 진행한 후
			 * 	 다시 클라이언트에게 반환
			 * 
			 */
			
			// 쿠키 생성(해당 쿠키에 담을 데이터를 k:v 로 지정)
			Cookie cookie = new Cookie("saveId", loginMember.getMemberEmail());
			
			if(saveId != null) { // 체크가 되었을 때
				
				// 한 달(30일) 동안 유지되는 쿠키 생성
				cookie.setMaxAge(60*60*24*30); // 초단위 지정
				
				
			} else { // 체크가 안되었을 때
				
				// 0초 동안 유지되는 쿠키 생성
				// -> 기존에 쿠키가 지정되어 있었다면 해당 쿠키를 삭제
				cookie.setMaxAge(0);
				
			}
			
			// 클라이언트가 어떤 요청을 할 때 쿠키가 첨부될지 경로(주소)를 지정
			cookie.setPath("/"); // localhost:/ 이하 모든 주소
								// ex) / , /member/login , /member/logout 등
								// 모든 요청에 쿠키를 첨부
			
			// 응답 객체(HttpServletResponse) 를 이용해서
			// 만들어진 쿠키를 클라이언트에게 전달
			resp.addCookie(cookie);
			
			
		} else {  // 로그인 실패
			path += referer;
			
			// message 추가 (아이디 또는 비밀번호 불일치)
			
			/* redirect(재요청) 시
			 * 기존 요청(request)이 사라지고
			 * 새로운 요청(request)을 만들게 되어
			 * redirect된 페이지에서는 이전 요청이 유지 되지 않는다!
			 * -> 유지 하고 싶으면 어쩔 수 없이 session scope를 이용
			 * 
			 * RedirectAttributes를 스프링에서 제공
			 * - 리다이렉트 시 데이터를 request scope로 전달할 수 있게하는 객체
			 * 
			 * 응답 전 : request scope
			 * 
			 * 응답 중 : session scope로 잠시 이동
			 * 
			 * 응답 후 : request scope로 복귀
			 * 
			 */
			
			// addFlashAttribute : 잠시 session 에 추가
			ra.addFlashAttribute("message", "아이디 또는 비밀번호 불일치");
		}
		
		return path;
	}
	
	
	@GetMapping("/logout")
	public String logout(SessionStatus status/*HttpSession session*/) { // 두 방법 중 하나 사용하면 됨!
		
		// SessionStatus : 세션 상태를 관리하는 객체
		
		//session.invalidate(); // 세션 무효화
		status.setComplete();
		
		return "redirect:/";
	}
	
}

main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>프로젝트</title>
	
	<!-- fontaswesom 아이콘 사용할 수 있는 스크립트 연결-->
	<script src="https://kit.fontawesome.com/f821b57119.js" crossorigin="anonymous"></script>
</head>
<body>
	<main>
        
        
        <%-- header.jsp 추가 --%>
        
        <%--
        	<jsp:include page="jsp파일경로" />
        	
        	- jsp 파일 경로는 'webapp 폴더 기준'으로 작성
        	- JSP 액션 태그(jsp에 기본 내장됨)
        	- 다른 jsp 파일의 코드를 현재 위치에 추가
        
         --%>
         
        <jsp:include page="/WEB-INF/views/common/header.jsp" />
		

        <section class="content">
            <section class="content-1">
            	<h3>로그인된 회원 정보</h3>
            
            
            </section>
            

            <!-- 아이디/비밀번호/로그인버튼 영역 -->
            <section class="content-2">
            
            	<c:choose>
            		<%-- 로그인이 안되었을때 --%>
            		<%-- EL empty : 비어있거나 null이면 true --%>
            		<c:when test="${empty sessionScope.loginMember}">
            		
	            		<form action="/member/login" method="post" name="login-form" id="loginFrm">
	
		                    <fieldset class="id-pw-area">
		                        <section>
		                            <input type="text" name="memberEmail" placeholder="이메일"
		                            	autocomplete="off"
		                            	value="${cookie.saveId.value}"
		                            	>
		                            <input type="password" name="memberPw" placeholder="비밀번호">
		                        </section>
		
		                        <section>
		                            <button>로그인</button>
		                        </section>
		                    </fieldset>
		
		                    <label>
		                    
		                    	<c:if test="${not empty cookie.saveId.value}">
		                    		<%-- 쿠키에 저장된 이메일이 있으면 변수 선언 : save --%>
		                    		
		                    		<c:set var="save" value="checked"/>
		                    	</c:if>
		                    	
		                        <input type="checkbox" name="saveId" ${save}> 아이디 저장
		                        
		                    </label>
		
		                    <!-- 회원가입/ Id/pw 찾기 영역 -->
		                    <section class="signup-find-area">
		                        <a href="#">회원가입</a>
		                        <span>|</span>
		                        <a href="#">ID/PW 찾기</a>
		                    </section>
	
	                	</form>
            		</c:when>
            		
            		<%-- 로그인이 되었을때 --%>
            		<c:otherwise>
            			<article class="login-area">
            				
            				<a href="#">
            					<img src="/resources/images/user.png" id="memberProfile">
            				</a>
            				
            				<div class="my-info">
            					<div>
            						<a href="#" id="nickname">${sessionScope.loginMember.memberNickname}</a>
            						<a href="/member/logout" id="logoutBtn">로그아웃</a>
            					</div>
            					
            					<p></p>
            				</div>
            				
            			</article>
            			
            			
            		</c:otherwise>
            	</c:choose>
            
            

            </section>

        </section>
       

    </main>
    
    <jsp:include page="/WEB-INF/views/common/footer.jsp" />
</body>
</html>

1)


아이디 저장 체크 후 로그인 버튼 클릭 시,
로그아웃 버튼 클릭 시, 아이디 저장되어 있고 아이디 저장 체크되어 있음

2)


아이디 저장 체크하지 않고 로그인 버튼 클릭 시,
로그아웃 버튼 클릭 시, 아이디 저장되어 있지 않고 아이디 저장 체크되어 있지 않음


cf )
아이디 저장 체크 후, F12 개발자도구 켜서 확인해보면 saveId 들어와 있는 것을 볼 수 있음!

0개의 댓글