[Spring] session으로 로그인 유지하기
안녕하세요 코북입니다. 오늘은 session을 사용해 로그인 기능을 구현해보겠습니다. 들어가기에 앞서 간단하게 session에 대해서 정리해보자면, session은 우선 cookie의 트래픽 문제와 보안적인 문제를 해결하기 위해 등장했습니다. 사용자의 로컬 컴퓨터에 정보를 저장하는 cookie와 다르게 session은 해당 서버(웹)와 사용자의 메모리(브라우저)에 정보를 저장합니다. 정보를 서버에 저장하기 때문에 관리하기가 편하고 효율적입니다.
session의 동작 절차는 다음과 같습니다.
1. 사용자가 서버에 접속을 요청하면 서버에서는 HTTP Request를 통해 쿠키에서 session id를 확인한 후, session id가 없다면 set-cookie값을 통해 새로 발행한 session id를 보냅니다.
2. 사용자가 HTTP Request header에 session id를 포함하여 원하는 정보를 요청합니다. 서버는 session id를 통해 해당 세션을 찾아 클라이언트 상태 정보를 유지하며 적절한 응답을 합니다.
3. session id는 해당 서버와 사용자 브라우저에 저장되고, 이 때 사용되는 cookie타입은 세션 종료 시 같이 소멸되는 memory cookie가 사용됩니다.
4. 사용자가 접속 종료를 하면 서버에 저장된 session id는 소멸됩니다.
session을 로그인과 연결시켜보면 다음과 같습니다.
1. 사용자가 서버에 로그인을 요청합니다.
2. 로그인 기능을 수행하는 메소드에 의해 setAttribute()메소드를 호출합니다.
3. 해당 속성 값이 session 안에 저장됩니다.
4. 결괏값을 응답해주면 로그인 완료입니다.
5. 속성값을 반환해서 사용하려면 getAttribute()메소드를 호출합니다.
6. 사용자가 반환된 속성값을 이용할 수 있습니다.
7. session을 삭제해 로그아웃을 하기 전까지 정보를 유지합니다.
이제는 코드를 통해 어떻게 구현했는지 살펴보겠습니다. 진행 순서는 다음과 같습니다. 지난번에 controller를 먼저 만들었더니 수정되는 mapper에 따라 값을 계속 바꿔줘야 했기 때문에 오늘은 mapper를 먼저 만드는 방식으로 진행했습니다.
1. mapper.xml
2. mapper
3. controller
4. jsp
1. Mapper.xml
<mapper namespace="city.turtle.mapper.MembersMapper">
<!-- 로그인 -->
<select id="signIn" resultType="city.turtle.mapper.MembersVO">
select * from members where mb_id = #{mb_id} and mb_pwd = #{mb_pwd}
</select>
</mapper>
parameter로 id와 pw값을 받습니다. 조회한 결괏값은 resultType에서 설정하는데 MembersVO로 설정했습니다. parameterType은 sql 실행에 필요한 데이터를 외부로부터 받을 때 사용하는 속성이고, resultType은 검색 관련 sql문이 실행되면 ResultSet이 반환되는데, ResultSet에 저장된 검색 결과를 mapping할 자바 객체를 지정할 때 사용하는 속성입니다. parameterType은 생략할 수 있고, resultType은 <select> 엘리먼트에서는 생략할 수 없는 속성입니다.
2. Mapper
@Mapper
public interface MembersMapper {
public MembersVO signIn(MembersVO vo);
}
xml의 반환 값은 VO에 담기위해 MembersVO를 리턴 타입으로 설정해줬고, 로그인할 때 넘어오는 정보도 마찬가지로 VO입니다.
3. Controller
@Autowired
private MembersMapper mapper;
// 로그인페이지
@RequestMapping("/login.do")
public String login() {
return "login";
}
// 로그인
@RequestMapping("/signIn.do")
public String signIn(MembersVO vo, HttpServletRequest request) {
MembersVO signIn = mapper.signIn(vo);
HttpSession session =request.getSession();
if (signIn != null) {
session.setAttribute("signIn", signIn);
return "redirect:/index.do";
} else {
session.setAttribute("signIn", null);
return "redirect:/login.do";
}
}
// 로그아웃
@RequestMapping("/logout.do")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/login.do";
}
로그인 버튼을 누르면 signIn.do 요청이 오고 controller가 요청을 처리합니다. 사용자에게 받아온 값들은 vo를 통해 mapper로 넘겨주고 반환 값을 다시 vo에 담습니다. request.getSession()을 통해 세션도 생성했습니다. 로그인 성공과 실패 여부를 if문을 통해 구분해줬습니다. signIn에 담긴 값이 null이 아닐 경우 세션 값을 생성해주고 메인화면으로 보내고, null인 경우 세션에 빈 값을 넣고 다시 로그인 화면으로 보냅니다. 로그아웃 버튼을 누르면 session.invalidate()를 통해 세션의 모든 속성 값을 제거하고, 다시 로그인 화면으로 보내도록 설정해줬습니다.
4. JSP
<section class="bg-light">
<div class="container py-4">
<div class="row align-items-center justify-content-between">
<a class="navbar-brand h1 text-center" href="index.do">
<span class="text-dark h4">도시</span> <span class="text-primary h4">거북</span>
</a>
</div>
<c:if test="${signIn == null}">
<form action="${cpath}/signIn.do" method="post">
<div class="form-group">
<input type="text" class="form-control" name="mb_id" placeholder="아이디">
</div>
<div class="form-group">
<input type="password" class="form-control" name="mb_pwd" placeholder="비밀번호">
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault">
<label class="form-check-label text-secondary" for="flexCheckDefault">
로그인 상태 유지
</label>
</div>
<div class="d-grid gap-2">
<button class="btn btn-primary btn-lg" type="submit">로그인</button>
</div>
</form>
<div class="otherButton text-center">
<span class="text-secondary">다른 계정으로 로그인</span>
<button type ="button" class = "btn"><img src='./resources/img/logoNaver4.png'></button>
<button type ="button" class = "btn"><img src='./resources/img/logoKakao4.png'></button>
<button type ="button" class = "btn"><img src='./resources/img/logoGoogle4.png'></button>
</div>
<div class="row">
<div class="col-lg-6 col-sm-12 text-lg-start text-center">
<button type="button" class="btn text-secondary" onclick="location.href='signUp.do'">회원가입</button>
</div>
<div class="col-lg-6 col-sm-12 text-lg-end text-center">
<button type="button" class="btn text-secondary">아이디/비밀번호 찾기</button>
</div>
</div>
</c:if>
</div>
</section>
로그인 페이지입니다. form태그의 action값을 signIn.do로 설정해서 로그인 버튼(submit)을 누르면 input태그에 입력한 정보가 signIn.do로 넘어갑니다. 넘길 input태그의 값들은 name에 설정해주시면 됩니다.
<div class="navbar align-self-center d-flex">
<a class="nav-link" href="#">다운로드 <i class='bx bx-downvote bx-sm bx-fade-down-hover text-primary'></i></a>
<c:if test="${signIn == null}">
<a class="nav-link" href="login.do"><i class='bx bx-user-circle bx-sm text-primary'></i></a>
</c:if>
<c:if test="${signIn != null}">
<a class="nav-link" href="logout.do">${signIn.mb_id} 로그아웃</a>
</c:if>
</div>
로그인을 하게되면 Header에 로그아웃 버튼이 보이도록 설정해줍니다. JSTL if문을 사용했고 test는 필수 속성 값으로 평가할 조건입니다. 저는 따로 설정하지 않았지만, scope는 조건 결과를 저장할 변수의 범위이고 var 속성을 사용하면 조건 결과를 저장할 변수명을 설정할 수 있습니다.
+
${signIn.mb_id} 로 값을 불러와 아이디를 제대로 가져왔는지 확인할 수 있습니다.
다음은 실행화면입니다.
배운 점
session과 cookie의 차이점을 간략하게 알아볼 수 있었고, session에 특징과 동작 과정에 대해서 공부할 수 있는 시간이었습니다. 지난번과 다르게 작업순서를 바꿔봤는데 Mapper-Controller-JSP 순서로 하니까 수정해야 할 내용들이 줄어들어서 보다 빠르고 효율적으로 작업할 수 있었습니다. EL태그를 사용해 객체를 불러오는 것을 구현해 볼 수 있었습니다.