📝 커뮤니티 로그인, 로그아웃 기능 공부 후 자습
#230425
💻 작업파일
[src/main/java] - [edu.kh.comm.member.dao] - MemberDAO.java
[src/main/java] - [edu.kh.comm.member.controller] - MemberController.java
[src/main/java] - [edu.kh.comm.main.controller] - MainController.java (신규)
[src/main/resources] - [mappers] - member-mapper.xml
[src/main/resources] - mybatis-config.xml
[src]-[main]-[webapp]-[WEB-INF]-[views]-[common] - footer.jsp (수정)
[src]-[main]-[webapp]-[WEB-INF]-[views]-[common] - main.jsp (기존 index.jsp)
[src]-[main]-[webapp]- index.jsp (신규)
📌 앞에 1.2번은 230424 스프링 공부내용정리 하단 참고
1) 패키지명 + 클래스명을 모두 작성해야 한다.
2) mapper에서 VO에 저장된 값을 얻어와 사용할 경우 #{필드명}을 사용한다.
단, VO에 반드시 Getter가 작성되어 있어야 한다.
<< memberDAO >>
// 1행 조회 (파라미터가 VO인 경우)
String memberTel = sqlSession.selectOne("memberMapper.test3", inputMember);
inputMember에 저장된 값 : memberEmail, memberPw
디비버가서 테이블 복제 후 설정해주기
mapper 가서 sql문 써주기
<< member-mapper.xml >>
<!--
조회되는 행의 개수 : 1행
파라미터 타입 : edu.kh.comm.member.model.vo.Member (패키지명+클래스명)
조회되는 타입 : java.lang.String -> string
-->
<!--sql문 조건 :
아이디, 비밀번호가 일치하는 회원의 전화번호 조회 / 단, 정상회원만-->
<select id="test3" parameterType="edu.kh.comm.member.model.vo.Member" resultType="string">
SELECT MEMBER_TEL FROM MEMBER_S
WHERE MEMBER_EMAIL = #{memberEmail}
AND MEMBER_PW = #{memberPw}
AND SECESSION_FL = 'N'
</select>
mybatis-config.xml내에서
VO클래스의 패키지명 + 클래스명 모두 작성하는 것이 불편하기 때문에 짧은 별칭 부여
➡️ vo로 객체 불러 올 때마다 매번 parameterType을 길게 쓰기엔 너무 비효율적이여서...
<< mybatis-config.xml >>
<typeAliases>
<typeAlias type="edu.kh.comm.member.model.vo.Member" alias="member"/>
</typeAliases>
--------------------------------------------------------------------------------------
<< member-mappter.xml >>
<!-- <select id="test3" parameterType="edu.kh.comm.member.model.vo.Member"
resultType="string"> -->
<select id="test3" parameterType="member" resultType="string">
SELECT MEMBER_TEL FROM MEMBER_S
WHERE MEMBER_EMAIL = #{memberEmail}
AND MEMBER_PW = #{memberPw}
AND SECESSION_FL = 'N'
</select>
사용
VO의 필드명과 조회되는 컬럼명이 일치하면 자동으로 VO객체에 값이 세팅된다.
하지만 java와 DB의 표기법 차이로 인해서 위 특징이 적용되는 경우가 많지 않다
그래서 필드명, 컬럼명이 달라도 이를 연결시켜줄 수 있는 resultMap 태그를 제공
resultMap은 최상단에 입력해준다!
ex)
java표기법 / db표기법
memberNo / MEMBER_NO
<< Member.DAO >>
Member loginMember = sqlSession.selectOne("memberMapper.login", inputMember);
return loginMember;
<< member-mappter.xml >>
<!-- 로그인 -->
<!--
조회되는 행의 개수 : 1행
파라미터 타입 : edu.kh.comm.member.model.vo.Member -> member (사용자 정의)
조회되는 타입 : edu.kh.comm.member.model.vo.Member -> member (사용자 정의)
resultMap="member_rm" -> 조회 결과를 member_rm을 이용하여 비교해서 VO에 세팅
-->
<select id="login" parameterType="member" resultMap="member_rm">
SELECT MEMBER_NO,MEMBER_EMAIL,MEMBER_NICK ,MEMBER_TEL,
MEMBER_ADDR, PROFILE_IMG,
TO_CHAR( ENROLL_DT, 'YYYY-MM-DD HH24:MI:SS') AS ENROLL_DT
FROM MEMBER_S
WHERE MEMBER_EMAIL = #{memberEmail}
AND MEMBER_PW = #{memberPw}
AND SECESSION_FL = 'N'
</select>
SELECT 조회 결과(ResultSet)의 컬럼과
조회결과를 옮겨 담을 VO의 필드명이 일치하지 않을 때
이를 매핑시켜 조회결과가 필드에 세팅되게 하는 역할
[resultMap 속성]
1) type : 조회결과를 담을 VO의 타입 또는 별칭
2) id : 만들어진 resultMap 태그를 지칭할 이름(식별명)
[resultMap 내부 작성 태그]
: PK 역할의 컬럼 - 필드 연결
: PK을 제외한 나머지 컬럼 - 필드 연결
💻 태그 및 속성 자동완성 설정값 :
ctrl
+space
<< member-mappter.xml >>
<mapper namespace="memberMapper">
resultMap은 최상단에 입력하기 때문에 namespace 바로 아래 입력해준다!
<resultMap type="member" id="member_rm">
<id property="memberNo" column="MEMBER_NO"/>
<result property="memberEmail" column="MEMBER_EMAIL"/>
<result property="memberPw" column="MEMBER_PW"/>
<result property="memberNickname" column="MEMBER_NICK"/>
<result property="memberTel" column="MEMBER_TEL"/>
<result property="memberAddress" column="MEMBER_ADDR"/>
<result property="profileImage" column="PROFILE_IMG"/>
<result property="enrollDate" column="ENROLL_DT"/>
<result property="secessionFlag" column="SECESSION_FL"/>
</resultMap>
디버그로 서버 돌린 후, 로그인 하면 아래처럼 뜸!!!!
그럼 성공😊😊😊
현재의 로그인 기능 흐름
📌📌📌📌📌📌
생략된 상태에서 파라미터가 필드에 세팅된 객체
데이터를 맵 형식(K:V) 형태로 담아 전달하는 용도의 객체
➡️ request, session을 대체하는 객체
@SessionAttributes 미작성 ➡️ request scope
@SessionAttributes({"loginMember"}) // 자바 배열형태 {}로 넣기
public class MemberControler {
...... 중략
----------------------------------------------------------------
@PostMapping("/login")
public String login( /*@ModelAttribute */ Member inputMember, Model model) {
logger.info("로그인 기능 수행됨")
// 아이디, 비밀번호가 일치하는 회원정보를 조회하는 Service 호출 후 결과 반환 받기
Member loginMember = service.login(inputMember);
// ==req.setAttribute("loginMember, loginMember);
model.addAttribute("loginMember", loginMember);
return "redirect:/";
서버돌리면 이렇게 세션 저장되면서 로그인됨
@GetMapping("/logout")
public String logout( /* HttpSession session */
SessionStatus status ) {
// 로그아웃 == 세션을 없애는 것
// * @SessionAttributes을 이용해서 session scope에 배치된 데이터는
// SessionStatus라는 별도 객체를 이용해야만 없앨 수 있다.
logger.info("로그아웃 수행됨");
//session.invalidate(); // 기존 세션 무효화 방식으로는 안된다!
status.setComplete(); // 세션이 할 일이 완료됨 -> 없앰
return "redirect:/";
}
index.jsp 내용 복사해서 main.jsp 만들기
index.jsp 내용수정하기
<< index.jsp >>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<jsp:forward page="main" />
<< MainController >>
@Controller
public class MainController {
@RequestMapping("/main")
public String mainForward() {
return "common/main";
}
flashAttribute 사용하려면 해당 request내용을 redirect에 담아서
MainController를 통해 main으로 갔다가 main 하단에 footer로 가서
request에 message 속성에서 잠깐 머물렀다가 사라짐
// 로그인
@PostMapping("/login")
public String login( /*@ModelAttribute */ Member inputMember,
Model model,
RedirectAttributes ra,
HttpServletResponse resp,
HttpServletRequest req,
@RequestParam(value="saveId", required=false) String saveId ) {
// 커맨드 객체
// @ModelAttribute 생략된 상태에서 파라미터가 필드에 세팅된 객체
logger.info("로그인 기능 수행됨");
// 아이디, 비밀번호가 일치하는 회원 정보를 조회하는 Service 호출 후 결과 반환 받기
Member loginMember = service.login(inputMember);
/* Model : 데이터를 맵 형식(K:V) 형태로 담아 전달하는 용도의 객체
* -> request, session을 대체하는 객체
*
* - 기본 scope : request
* - session scope로 변환하고 싶은 경우
* 클래스 레벨로 @SessionAttributes를 작성하면 된다.
*
* @SessionAttributes 미작성 -> request scope
*
* */
if ( loginMember != null) { // 로그인 성공시
model.addAttribute("loginMember", loginMember);
// ==req.setAttribute("loginMember", loginMember);
// 로그인 성공시 무조건 쿠키 생성
// 단, 아이디 저장 체크 여부에 따라서 쿠키의 유지시간을 조정
Cookie cookie = new Cookie("saveId", loginMember.getMemberEmail());
if (saveId != null ) { // 아이디 저장이 체크되어있을 때
cookie.setMaxAge(60 * 60 * 24 * 365); // 초단위 지정(1년
} else { // 체크되지 않았을 때
cookie.setMaxAge(0); // 0초 -> 생성되자마자 사라짐 == 쿠키삭제
}
// 쿠키가 적용될 범위(경로) 지정
cookie.setPath(req.getContextPath()); // /comm 안에서 모두 쿠키 적용하겠다는 말
// 쿠키를 응답 시 클라이언트에게 전달
resp.addCookie(cookie);
} else {
// model.addAttribute("message", "아이디 또는 비밀번호가 일치하지 않습니다.");
ra.addFlashAttribute("message", "아이디 또는 비밀번호가 일치하지 않습니다.");
// redirect 시에도 request scope로 세팅된 데이터가 유지될 수 있도록 하는 방법을
// Spring 에서 제공해줌
// RedirectAttributes객체 (컨트롤러 매개변수에 작성하면 사용 가능)
}
return "redirect:/";
}
//로그아웃
@GetMapping("/logout")
public String logout(/*HttpSession session*/
SessionStatus status ) {
// 로그아웃 == 세션을 없애는 것
// * @SessionAttributes을 이용해서 session scope에 배치된 데이터는
// SessionStatus라는 별도 객체를 이용해야만 없앨 수 있다.
logger.info("로그아웃 수행됨");
// session.invalidate(); 기존 세션 무효화 방식으로는 X
status.setComplete(); // 세션이 할 일이 완료됨 -> 없앰
return "redirect:/"; // 메인페이지 재요청
}