ㅁ 회원서비스
- 회원서비스 개발 세팅 <- 이전에 여기까지 했음.
- 회원서비스_로그인 <- 여기부터 시작해서
- 회원서비스_로그아웃 <- 여기까지 내용이다.
- 회원서비스_회원가입페이지로이동
- 회원서비스_회원가입요청
- 회원서비스_마이페이지요청
- 회원서비스_정보변경요청
- 회원서비스_비번변경요청
- 회원서비스_회원탈퇴요청
ㅁ http://localhost:8888/web/
- 메인페이지를 들어가본다.
- 그런데 404 에러 뜸. 보니까 프로젝트 세팅에서는 context root가 web으로 변경되어 있는데,
이클립스 하단 Servers에서 서버 더블클릭하고 module 탭에서 보면 root가 webApp으로 되어 있음.
그래서 http://localhost:8888/webApp을 입력해야 들어가진다.
- edit 눌러서 /web으로 수정하고 서버 재시작 했는데도 여전히 안 됨.
- 서버 멈추고, project - clean 하고, 서버 재시작하고 하니까 됨.
- 메인페이지는 index.jsp가 로드되고 있는 것이다.
이 index.jsp에 header.jsp와 footer.jsp를 include해놔서 이렇게 요소들이 보여진다.
- 주로 header.jsp에서 기능을 계속 요청할 것이다.
header에 로그인 폼도 있고 다른 메뉴로 이동할 수 있는 메뉴바도 있다.
- 우상단 로그인 폼쪽은 로그인 성공 후에는 누가 로그인이 됐는지, 그리고 마이페이지로 이동을 요청할 수 있는 요소로 바뀌어야 한다.
이 자리에 보여질 두 가지 경우를 다 화면구현해 놨었다.
ㅁ 회원서비스 기능구현 진행을 위해 패키지와 클래스를 만들어놨었다.
- src/main/java는 소스폴더다.
- 여기에 com.br.web이라는 패키지가 있고, 그 뒤부터 네번째 레벨로 member, notice, board로 나뉜다.
- db 패키지도 따로 뒀고 db 패키지 안에는 config와 mappers 패키지를 뒀었다.
- JDBCTemplate 클래스도 구성을 했고, db.config 패키지에 driver.properties 파일도 만들어 놨었다.
어떤 db에 접속할건지, 그 환경설정 관련한 내용이었다.
- JDBCTeplate 클래스에서 db 연결해주는 메소드로 getConnection() 메소드를 뒀었다.
이 때 webapp/WEB-INF/classes/db/config 안에 동기화된 driver.properties 파일을 읽어들여야 한다.
- dao 클래스에 프로퍼티스 객체를 인스턴스 변수(인스턴스 필드)로 선언한다.
- src/main/java의 db.mappers 패키지에 member-mapper.xml 파일을 만들어 놨고
이 안에 key-value 세트로 실행할 쿼리문들을 작성할 예정이다.
- dao 측에서 쿼리문이 담긴 xml 파일을 읽어들여야 하기 때문에 마찬가지로 실제 읽어들이는 파일은
webapp/WEB-INF/classes/db/mappers 안에 동기화된 member-mapper.xml 파일을 읽어들여야 한다.
- prop이라는 인스턴스 변수에 key-value 세트들이 담기게 되고, 그걸 이제 앞으로 만들 메소드 측에서 사용하면 된다.
- dao의 메소드는 service에서 호출한다. 그래서 MemberService 클래스도 만들었다.
MemberService에 이제 각각의 기능별 메소드가 만들어질 예정이다.
- 각 기능에 맞는 쿼리를 service에서 dao 메소드를 호출시켜서 실행시킬 예정이다.
- 한 회원에 대한 데이터를 담을 VO 객체인 Member 클래스도 만들었다.
ㅁ 기능별 메소드 구현 + sql 쿼리문 작성
- 왼쪽에 MemberService 클래스를 띄우고, 오른쪽에 쿼리문을 작성할 member-mapper.xml을 띄운다.
- 그 외에 header.jsp와 MemberDao.java도 켜놓는다.
- 원래 개발할 때 기능 분석 끝나고, 화면 설계 끝나고, db도 다 구축되었으면
본격적인 기능구현 하기전에 화면 설계된 거 or 화면 구현된 걸 보면서
각각의 페이지상에 필요한 쿼리같은 걸 미리 다 작성해 둘 수가 있다.
- 앞으로 실행시켜야할 쿼리를 쭉 작성 해두고 쿼리에 문제가 없는지 쿼리 테스트도 진행을 해야만 한다.
- 그런데 아직은 이 페이지에서 요청별로 어떤 쿼리가 필요한지 예상이 불가능할 테니,
지금은 기능구현을 하면서 필요한 쿼리를 작성한다.
나중에 익숙해지면 미리 쿼리도 생각해보세요.
- MemberService 클래스에서는 빈번하게 멤버 dao 객체가 필요하기 때문에
그때마다 new MemberDao(); 하지 않고 인스턴스 변수로 MemberDao 객체를 생성한다.
ㅁ header.jsp를 살펴본다.
- 앞으로 현재 url 요청시, 절대경로 방식으로 포트번호 바로 뒤에 붙는 url을 작성할 예정이다.
그때마다 contextPath를 알아와야 한다.
- "/web"이라는 것을 알고 있지만 이 context path는 언제든지 변경될 수 있기 때문에
현재 context path를 동적으로 알아내는 request.getContextPath() 메소드를 사용해야 한다.
- 정적인 요소에 대한 경로를 작성할 때(로고 이미지)도 항상 context path를 제시해서 절대 경로 방식으로 작성해야 파일을 찾기가 훨씬 수월하다.
- contextPath가 곧 webapp 폴더를 가리킨다.
- header 태그는 지금 크게 3가지 영역으로 되어 있다.
로고 이미지가 있는 div가 있고, 가운데 div는 비워놨고, 세번째 div는 로그인 폼을 구성해놨다.
- 어떤 페이지에 있든 로고 이미지를 클릭하면 메인 페이지로 올 수 있게끔 하려면,
a 태그에 href로 요청할 url을 context path로 작성하면 된다.
- context path로 작성하면, "/web"이 오고, 이러면 index.jsp 페이지가 로드된다.
- 앞으로 요청 처리 이후에 index.jsp 페이지가 띄워지게끔 하려면 url로 위와 같이 context path를 요청하면 된다.
- 현재 이 페이지가 로드되는 시점이 로그인 전일 수도 있고, 로그인 후일 수도 있다.
로그인 전 상태라면 case1에 작성한 로그인 form 요소가 보여지게 하고,
로그인 후 상태라면 case2에 작성한 로그인 후 요소가 보여지게 할 예정이다.
- 로그인 후에는 현재 로그인한 회원의 이름이 보여져야 한다.
ㅁ header.jsp에 로그인 기능을 구현한다.
- case1에 로그인 요청을 할 수 있는 form 요소가 있고, form 요소 내에 submit 타입의 버튼으로 로그인 버튼이 있다.
- 아이디와 비밀번호 입력란에 required 속성을 추가한다.
- 아이디와 비밀번호 텍스트상자에 입력된 값을 로그인 요청시 서버에 전송시켜야 한다.
웹과 통신을 할 때는 항상 key-value 세트로 데이터가 움직인다.
- key 값을 부여해야 value 값이 넘어가므로 input 요소에 name 속성도 추가한다.
- 비밀번호가 노출되면 안되므로 이런 보안을 중시하는 요청은 post 방식으로 요청한다.
method="post"로 하면 데이터가 url에 노출되지 않는다.
- 요청할 url은 action=""에 작성하면 된다. 항상 서블릿을 요청한다고 보면 된다.
단순한 페이지 요청도 다 서블릿으로 요청한다.
- 서블릿의 url mapping 값을 context path 뒤에 작성한다.
항상 절대경로 방식으로 요청할 url을 작성하는 것이 좋다.
- 변수 contextPath에 "/web"이 담겨 있다.
- 회원서비스는 url mapping값의 패턴을 항상 뒤에 ".me"로 끝나게 작성하기로 한다.
서비스별로 url 패턴을 구분지어주는 것이 좋다.
보통 . 하고 뒤에 패턴을 붙인다.
- <form action="<%= contextPath %>/login.me" method="post">
/login.me 라는 url mapping 값을 가지는 서블릿을 호출하기로 한다.
ㅁ com.br.web.member.controller에 "/login.me"라는 url mapping 값을 가지는 서블릿 클래스를 만든다.
- 서블릿 클래스는 MVC 패턴 중 컨트롤러 역할이다.
- src/main/java의 com.br.web.member.controller 패키지 우클릭 - new - Servlet으로 서블릿을 만든다.
- 서블릿 클래스명의 시작은 대문자로 작성한다.
파일들이 많은데 어떤게 클래스고 어떤게 jsp 파일인지 구분할 수 있어야 한다.
- jsp 파일명은 소문자로 시작하게 작성한다.
- 바로 finish 하고 나중에 url mapping 값을 수정해도 되지만, next 하고 url mapping 값을 수정한다.
- 클래스명을 따서 "/MemberLoginController"로 되어 있는 url mapping 값을 "/login.me"로 바꾼다.
- 슬래시가 누락되면 안 된다.
슬래시 없이 url mapping 값을 쓰면 서버 스타트시 오류가 난다.
- 이렇게 "/login.me"라는 url mapping 값을 가지는 서블릿 클래스가 만들어진다.
- 위의 탭들을 MVC 흐름대로 정리한다.
header.jsp의 로그인 form에서 로그인 요청시, "/login.me"라는 url이 요청되고,
그때 실행되는 서블릿 클래스가 MemberLoginController이다.
- controller 클래스에서 service => dao로 데이터를 넘겨서 db에 쿼리문을 실행하고 결과를 돌려받는다.
- 요청이 get 방식이면 doGet() 메소드가, post 방식이면 doPost() 메소드가 서블릿에서 자동으로 실행된다.
- 그런데 어차피 doPost() 메소드에서도 doGet() 메소드를 호출하고 있다.
이클립스에서는 개발자 편의를 위해서 get 방식이든 post 방식이든 doGet() 메소드에서 작업할 수 있게 해놨다.
- doPost() 메소드에 작성된 구문은 그대로 두고,
doGet() 메소드에 작성된 구문을 다 지우고 post 방식임에도 그냥 doGet() 메소드에 작성한다.
- 컨트롤러에서는 항상 두 가지만 기억하면 된다.
요청에 관한 것과 응답에 관한 것을 작성하면 된다.
- 요청에 관한 것은 요청하면서 전달한 값을 뽑아내고, JDBC 과정을 통해서 쿼리를 실행 후 결과를 받으면 된다.
- 요청에 전달값이 없다면 굳이 값을 뽑을 필요 없다.
- 만약 단순한 페이지 이동이라면 그때는 쿼리를 실행할 필요도 없다.
- 지금은 아이디와 비밀번호 값이 전달된다.
요청시 전달값이 있다는 소리는 그 값을 뽑아서 어딘가에 사용을 한다는 소리다.
- request.getParameter(String) 혹은 request.getPrameterValues(String) 메소드로 값을 뽑으면 된다.
key 값을 제시해서 value 값을 뽑을 수 있다. 이때 무조건 문자열로 반환된다.
그래서 이 메소드 호출시 반환된 값을 String 타입의 변수에 담아둔다.
- 이때 변수명은 무조건 key값과 동일할 필요는 없고 임의로 의미를 부여해서 만들면 된다.
getParameter(Stirng)메소드에 제시하는 key 값은 input 요소의 name 속성값과 같아야 한다.
- post 방식 요청일 경우, 한글 데이터면 setCharacterEncoding("UTF-8") 메소드를 이용해서 utf-8로 인코딩 해야 한다.
여기서는 아이디와 비밀번호에 한글이 없어서 생략했다.
- 이제 이 아이디와 비밀번호가 유효한 회원이 맞는지를 확인해야 한다.
회원 data는 db에 저장되어 있다. 그러면 db에 이 아이디와 비밀번호를 가지고 있는 회원이 있는지 조회해야 한다.
여기서 실행할 쿼리는 select이다.
- service로 아이디와 비밀번호를 넘기면서 조회하러 간다.
MemberService() 객체를 생성해서 loginMember()라는 메소드를 호출한다. (아직은 이런 메소드가 없다)
ㅁ MemberService 클래스에 loginMember라는 메소드를 만든다.
- com/br/web/member/model/service 패키지의 MemberService 클래스에 loginMember라는 메소드를 만든다.
두 개의 String 타입의 변수를 받을 매개변수도 만든다.
- 뭘 반환할지는 아직 모르겠으면 반환형은 void로 일단 둬 놓는다.
- 이제 select문을 실행하기 위해 dao로 넘어가야 한다.
그런데 dao측에 jdbc 전체 구문을 작성하기에는 너무 길어지니 JDBCTemplate 클래스에 static 메소드로 선언해 두었다.
- db와 접속된 Connection 객체를 JDBCTemplate 클래스의 메소드를 호출해서 얻어내고 변수에 담는다.
- 그런데 매번 "JDBCTemplate."을 작성하기 귀찮아서 import static문으로
앞으로 MemberService 클래스에서는 JDBCTemplate 클래스 안에 있는 모든 static 메소드를 호출한다고 선언해둘 수 있다.
- import static com.br.web.common.template.JDBCTemplate.getConnection; 을 선언해 두면
앞으로 JDBCTemplate.getConnection();이 아닌 getConnection();이 가능하다.
- 그러면 전에 쓴 import com.br.web.common.template.JDBCTemplate; 는 필요가 없으니 지운다..
- 이제 select 문을 실행하러 넘어간다.
dao측 메소드를 호출할 건데 Connection 객체와 userId, userPwd를 다 전달한다.
- 이때 아직 어떤 값이 넘어올지 모르겠으면 그냥 이대로 둬 놓는다.
ㅁ MemberDao 클래스에 loginMember 메소드를 만든다.
- Connection 객체, userId, userPwd를 전달받기 위해 매개변수 3개를 둔다.
- 전달받은 아이디와 비밀번호를 가지고 select문을 실행시킨다.
결과는 ResultSet에 담겨서 돌아오는데 아이디는 중복이 안되므로 조회 결과는 하나(한 행, 한 회원)다.
아니면 아이디와 비밀번호를 잘못 입력한 경우 조회가 안 되니 rset은 비어있고 m도 null일 것이다.
- 최종 결과는 Member 객체의 각 필드에 담는다.
vo객체의 용도는 데이터를 담아서 전송시키는 것이다.
ㅁ member-mapper.xml에 쿼리를 작성한다.
- entry의 key값은 그냥 메소드명과 동일하게 준다. 괜히 오타날 수 있으니 붙여넣기 한다.
- 쿼리문은 대문자든 소문자든 상관없다.
- 참고로 sql developer를 안 켜놔도 db는 현재 이 pc에 다 구축이 되어있기 때문에 작동 잘 된다.
- sql developer를 켜서 Server 계정의 Member 테이블의 열 정보 탭에서 COLUMN_NAME을 싹 복사해 와서 컴마만 붙여준다.
- 다른사람도 잘 알아볼 수 있게 들여쓰기 중구난방으로 하지 말고, 컴마도 뒤가 아닌 앞에 쓴다.
보통 이렇게 많이 쓴다. 한 줄 단위 주석처리하기도 쉽다.
- FROM 다음 테이블명은 붙여써도 되는데 여러 테이블을 조인하는 경우 테이블들도 컬럼처럼 작성하는 경우가 있어서 한 줄 단위로 주석처리하기 쉽게끔 이렇게 작성하기도 한다.
- 이렇게 작성하면 SELECT 절, FORM 절, WHERE 절을 정확히 구분해서 볼 수 있다.
- USER_ID와 USER_PWD에는 뭐가 들어올지 모르므로 "?"를 써 놓는다.
- 조건을 여러개 쓸 때도 한 줄씩 내려서 표현해주는 것이 좋다.
- 탈퇴회원은 조회안되게끔 한다. STATUS 컬럼이 'R'이면 탈퇴회원이다.
탈퇴회원도 일정 기간 동안은 데이터를 가지고 있는다.
- 쿼리 마지막에는 절대 세미콜론(;)이 찍혀있어서는 안 된다.
- 이렇게 이 쿼리가 어떤 패키지의 어떤 클래스의 어떤 메소드 측에서 실행되는 쿼리인지 주석을 쓰기도 한다.
- INSERT, UPDATE, DELETE도 다 마찬가지다.
내가 어떤 명령어를 쓸지 작성하고 그 다음줄부터 작성해주시는게 좋다.
ㅁ MemberDao 클래스에 loginMember 메소드로 돌아온다.
- 최종 조회 결과는 Member 객체에 담아서 반환한다.
- 최종 조회 결과를 담을 변수를 null로 선언만 해둬야지 new로 생성해 놓으면 안 된다.
조회결과가 있는지 없는지를 비교할 때 null인지 null이 아닌지로 비교해야 하기 때문이다.
조회결과가 있을 때 vo 객체를 생성해야 한다.
- import할 때 Member 클래스가 많은데 우리가 만든 Member 객체로 한다.
- 미완성된 쿼리를 실행할 때는 PreparedStatement를 사용해야 한다.
- select 문은 조회 결과를 ResultSet으로 받는다.
- MemberDao 클래스가 생성되면서 생성자로 "/db/mappers/member-mapper.xml" 파일에 기술되어 있는
key-value 세트가 prop이라는 전역 변수에 읽어들여져 있다.
- 실행할 sql문을 prop에서 꺼내어 String 타입의 변수에 담는다.
- 필요한 변수들과 sql문 세팅이 끝났으면 pstmt 생성부터 진행하면 된다.
conn.prepareStatement(sql) 메소드 호출시 내가 실행할 sql문을 담은 채로 pstmt 객체를 생성한다.
- sql문이 물음표가 2개 있는 미완성된 sql문이다.
그러면 실행하기 전에 pstmt.setString(1, userId); 등으로 완성을 시켜야 한다.
- 그리고 pstmt.executeQuery() 메소드로 sql문을 실행한다.
조회 결과는 ResultSet으로 반환된다.
- 조회가 됐을 수도 있고 안됐을 수도 있는데 뭐가 됐든 커서를 움직여야만 한다.
움직였을 때 데이터가 있다면 생성을 진행한다. 한번만 진행하면 돼서 while은 필요 없다.
- rset에는 각각의 컬럼에 대한 값이 담겨있다.
그걸 우리는 뽑아서 Member 객체의 각 필드에 담는다.
m이라는 Member 타입 변수는 선언만 되어있고 현재 생성되어 있지 않다.
그래서 Member 객체에 데이터를 담고 싶으면 생성을 먼저 해야한다. 매개변수가 다 있는 생성자로 생성한다.
- rset.getXXX() 메소드에 인자로 주는 값은 db의 컬럼명이다.
대문자로 쓰든 소문자로 쓰든 상관없다.
- 이렇게 한 행에 대한 각각의 컬럼값들을 뽑아서 Member 객체의 각 필드에 담았다.
물론 조회 결과가 있을 때만 진행되는 내용이다.
조회 결과가 없으면 m은 null인 상태 그대로다.
- 그리도 다 쓴 객체들을 생성된 역순으로 반납한다.
import static com.br.web.common.template.JDBCTemplate.close; 문을 여기에도 상단에 써준다.
이러면 이제 close() 메소드 호출시 JDBCTemplate 클래스의 close() 메소드가 실행된다.
- Connection 객체는 service에 돌아가서 또 사용될 수도 있으니 service에서 반납을 진행한다.
ㅁ MemberService 클래스의 loginMember 메소드를 마저 완성시킨다.
- mDao.loginMember(conn, userId, userPwd) 메소드를 호출하면 조회된 데이터가 담겨있는 Member 객체가 반환된다.
- 조회를 한번 실행하고 왔다. 그런데 여기서 또 다른 쿼리가 실행될 수도 있다.
그러면 여기서 또 dao 메소드가 호출된다. 그때는 또 Connection 객체를 사용해야하니 service에서 반납을 진행하는 것이다.
- 더이상 실행할 쿼리가 없으면 Connection 객체를 반납하면 된다.
import static com.br.web.common.template.JDBCTemplate.close; 문을 여기에도 써준다.
그럼 close();만으로 메소드 호출이 가능하다.
- service에서도 최종적으로 loginUser라는 Member 객체를 반환한다.
ㅁ MemberLoginController 클래스의 doGet() 메소드로 돌아온다.
- loginUser라는 Member 객체 타입의 변수를 만들어서 결과값을 담는다.
null이냐 아니냐로 로그인 성공인지 실패인지를 판별할 수 있다. 각각의 경우에 대한 응답을 해주면 된다.
※ 요청 처리 후 특정 데이터를 담을 수 있는 객체
1. 이 객체들은 "JSP 내장객체"로 JSP에서 바로 사용 가능하다.
2. 주로 응답페이지에서 사용할 데이터를 담음
3. 어떤 객체에 응답 데이터를 담냐에 따라 담은 데이터를 꺼내서 사용할 수 있는 범위가 다르다.
4. 종류
- 사실 session, request 이 두 객체만 거의 쓴다.
(1) ServletContext application
ㄴ 컨텍스트 - 애플리케이션 하나당 한 개 존재 (즉, 애플리케이션에 유지할 데이터 담기)
ㄴ 애플리케이션 종료 전까지 데이터 사용 가능함 (범위가 가장 넓음)
(2) HttpSession session *
ㄴ 세션 - 브라우저 하나당 한 개 존재 (즉, 브라우저에 유지할 데이터 담기)
ㄴ 브라우저 종료 및 서버 종료 전까지 데이터 사용 가능함
(3) HttpServletRequest request *
ㄴ 요청 - 하나의 요청당 한 개 존재. 이 객체는 서블릿이 실행될 때 생성돼서 넘어온다. 서블릿 당 1개의 request 객체가 존재한다.
ㄴ (즉, 해당 요청에 대한 응답페이지에 유지할 데이터 담기)
ㄴ 응답페이지에서만 필요한 데이터는 request에 담는다.
ㄴ forward하면서 같이 전달된 request는 이동한 Servlet 및 JSP에서만 사용 가능함.
(4) PageContext page
ㄴ 페이지 - 한 페이지당 한 개 존재 (즉, 해당 페이지에 유지시킬 데이터 담기)
ㄴ jsp와 연관있다. 사실 서블릿 쪽에서는 접근할 수 없다.
ㄴ 해당 페이지에서 유지시킬 데이터를 그 페이지에서 담는 객체다.
ㄴ 해당 jsp에서 담고 해당 jsp에서만 사용 가능함
5. 공통 메소드
- key-value 세트로 담고, key값을 제시해서 그에 해당하는 value값을 꺼낸다.
1) 데이터 담기 : .setAttribute(String, Object)
2) 데이터 꺼내기 : .getAttribute(String ) => Object타입으로 반환 (캐스팅 필요)
3) 데이터 제거하기 : .removeAttribute(String )
- 아래는 MemberLoginController 클래스이다.
- 응답은 돌려받은 결과를 가지고 판단하면 된다.
- 로그인 실패시 에러 페이지를 띄운다.
에러 페이지는 로그인 실패뿐 아니라, 회원가입 실패, 회원 정보변경 실패시에도 재사용한다.
- 에러페이지는 아직 제작을 하진 않았지만 /web/views/common/errorPage.jsp 로 만들 예정이다.
- 에러 페이지에 어떤 서비스 실패인지 출력할 예정이다.
이 페이지에서 필요한 응답 데이터는 "로그인 실패" 메세지이다. "로그인 실패"라는 응답 데이터가 필요하다.
- 응답데이터가 어느 범위에서 필요한지를 생각해야 한다.
"로그인 실패" 메세지는 딱 이 해당 응답 페이지에서만 필요하다. request에 응답 데이터를 담으면 된다.
- "msg"라는 key 값으로 "로그인 실패"라는 value 값을 담았다.(String, Object로 담았다)
- 로그인 성공시 응답페이지는 메인페이지("/web/index.jsp")이다.
로그인 성공시 응답할 데이터는, 로그인에 성공한 순간 로그인한 회원 데이터가 쭉 사용된다.
로그인 성공 후 다른 페이지를 가도 현재 로그인한 회원 정보가 계속 필요하다.
- 로그인 성공시 필요한 응답데이터는 조회된 데이터들이 담겨있는 Member 객체다.
이 페이지 뿐만 아니라 다른 페이지 넘어가서도 쭉 이 로그인한 회원의 데이터가 계속 필요하다.
이 브라우저를 통해서 이 사이트를 이용하는 과정 중에 계속 필요하다.
- 응답 데이터들을 어딘가에 담아 놓아야 응답 페이지에서 쓰거나 브라우저 내에서 계속 사용할 수 있다.
request에 담았던 적이 있긴 한데 다른 객체에도 담을 수가 있다.
- session에 담긴 데이터는 브라우저가 종료되기 전까지 혹은 서버가 종료되기 전까지 쭉 유지가 된다.
그래서 각 페이지들에서 조건문 등에 쓸 수 있다.
- 로그인 성공시 응답데이터로 조회된 데이터들이 담겨있는 Member 객체가 넘어간다.
session으로 Member 객체를 넘기면 응답 페이지뿐만 아니라 다른 페이지에서도 쭉 사용이 가능하다.
- 로그인 성공시 메인페이지를 띄우고 싶으면 /web/index.jsp할 필요 없이 /web만 해도 된다.
그냥 contextPath로 url 재요청(redirect)하면 메인페이지가 띄워진다.
그런데 "/web"이라는 context path도 언제 바뀔지 모르므로 "/web" 이렇게 정적으로 작성하면 안되고,
request.getContextPath() 메소드로 동적으로 알아내야 한다.
- redirect하는 경우(url 재요청)에는 request 객체가 전달되지 않는다.
그래서 request에 데이터를 담아봤자 소용이 없다. 여기선 session에 담았다.
- jsp에서는 session이 내장객체라서 바로 쓸 수 있는데, 서블릿에서는 request.getSession();으로 얻어서 써야 한다.
- 이제부터 요청에 있어서 응답할 때 응답페이지를 뭐로 띄울 거고, 그때 필요한 응답데이터는 뭔지를 생각해야 한다.
그게 어느 범위까지 사용될 데이터인지 잘 판단해서 그에 맞는 객체에 담아야 한다.
- 응답페이지를 띄우는 방법이 두 가지가 있는 것이다. forward와 redirect.
forward로 이동해서 띄워주거나 아니면 이미 이 페이지를 요청하는 url이 존재한다면 그 url을 재요청하는 redirect 방식이 있다.
ㅁ webApp/src/main/webapp/views/common 폴더에 errorPage.jsp 만들기
- 에러 페이지도 header와 footer가 있었으면 해서 header와 footer를 include 했다. index.jsp에 가서 복사해왔다.
- 이 에러 페이지에서 쓸 데이터로 request에 "msg"라는 key값으로 "로그인 실패"가 담긴채로 이동했다.
- request로부터 데이터를 꺼내 쓰려면 자바코드를 실행해야 한다. 표현식을 사용한다.
- request는 jsp 내장객체이기 때문에 jsp에서 request 객체 생성 없이 곧바로 사용가능하다.
- JSP의 <%= ... %> 표현식은 내부적으로 toString() 메서드를 호출하여 값을 문자열로 변환합니다.
그래서 Object로 value값이 담겼지만 꺼낼 때 (String)으로 형변환하는 과정이 없었다.
ㅁ header.jsp 수정하기
- session 객체로부터 getAttribute로 응답데이터를 꺼낸다.
Object 타입으로 반환되므로 (Member)로 형변환하고 Member 타입 변수에 담는다.
- 서블릿과 달리 jsp에서는 session이 jsp 내장객체이므로 바로 접근 가능하다.
- Member는 타 패키지에 있는 클래스다보니 import해야 한다. import 페이지 지시어를 최상단에 작성한다.
- "<b>홍길동</b>님 환영합니다."를 위와 같이 표현식을 사용하여 수정했다.
화면 구현 단계에서는 정적으로 아무렇게나 써놓고, 기능 구현 단계에서 실제 데이터가 보여지게끔 바꾸면 된다.
ㅁ 이렇게 로그인 기능을 완성시켰고 테스트를 하기 전에, db와 연동이 되기 때문에 ojdbc6 라이브러리가 연동되어 있어야만 한다.
- ojdbc6 라이브러리를 src/main/webapp/WEB-INF/lib 폴더에 둔다.
- jar 파일은 자바 클래스들이 모여 있는 압축 형식이다. zip같은 압축 파일이다.
아이콘은 다르게 보일 수 있는데 상관없다. 무시. pc에 압축 프로그램(알집, 반디집 등)이 있으면 그럼.
- 프로젝트 우클릭 - 자바 빌드 패스에서 등록은 안했음. 이렇게 lib 폴더에 가져다만 두었다.
ㅁ 로그인 기능 테스트
- 로그인에 실패하면 forward하므로 최초 로그인 요청을 하면서 호출했던 servlet의 url mapping값인 /login.me가 보여진다.
이동한 에러페이지의 url이 보여지지 않는다.
- 로그인 성공시에는 /web으로 redirect해서 index.jsp(메인페이지)를 띄우므로 url도 변경된다.
- 만약 오류가 중첩해서 발생한다면, 최초 오류를 봐야 한다.
ㅁ 로그아웃 기능
- 로그인 후에 보여질 요소에 로그아웃 링크가 있다. 얘도 서블릿을 요청한다.
- "/logout.me"라는 url mapping 값을 가지는 서블릿 클래스를 호출한다.
ㅁ src/main/java의 com.br.web.member.controller 패키지에 MemberLogoutController 서블릿 클래스를 만든다.
- 로그아웃 요청은 요청과 함께 전달되는 데이터가 없다.
- 로그아웃 로직은 별도로 실행할 쿼리가 없다. service, dao갈 필요가 없다.
- 응답페이지는 바로 메인페이지로 돌아갈 건데 그때, 로그인 form이 보여지고자 한다면 loginUser라는 변수가 null이어야 한다.
- session.removeAttribute("loginUser");
이렇게 "loginUser"라는 key 값에 대한 Member 객체를 날려도 된다.
- 그런데 로그인한 회원 정보가 loginUser 말고 다른 곳에도 더 담겨있을 수 있다. session에 다른 정보도 더 담겨있을 수 있다.
깔끔하게 session을 싹 비우는게 좋다.
- session을 싹 비우려면 session 무효화를 하면 된다. session 무효화는 session을 만료시키는 개념이다.
- session.invalidate();
하면 session 안에 담겨있는 게 깔끔히 비워진다.
- 그 이후에 다시 메인페이지가 뜨게끔 redirect하면 된다.
- 자바 코드를 수정했으니 서버를 리스타트하고 다시 메인페이지를 띄운다.
서버가 종료됐다가 재실행되면 session이 무효화돼서 로그인이 풀린 상태다.
- 기존에 담긴 회원 객체는 날라갔으니 다시 로그인부터 하고, 로그아웃 기능을 테스트한다.
- 로그아웃을 클릭하는 순간 session이 무효화되고 다시 index.jsp가 로드된다.
loginUser == null이기 때문에 로그인 요청을 할 수 있는 form이 보여진다.
'05_Server (04. JSP 프로그래밍 구현)' 카테고리의 다른 글
[1-4] 회원서비스 회원가입 페이지로 이동, 회원가입 요청 (4) | 2024.09.08 |
---|---|
작업 현황 공유용 스프레드시트 (0) | 2024.09.08 |
[1-2] 회원서비스 개발 세팅 (이어서) (6) | 2024.09.07 |
[1-1] 웹 어플리케이션 제작 (0) | 2024.09.02 |
DB설계 및 구축 (+ 엑셀로 DB에 한번에 넣기) (5) | 2024.09.02 |