프로그램에서 실행하지 말고 주소를 쳐서 실행해야 한다.
그래야 뒤에 뭐가 안 붙는다.
예시 : http://localhost:9090/day06/
- 세션(session)의 개요
쿠키가 웹 브라우저에 사용자의 상태를 유지하기 위한 정보를 저장했다면, 세션(session)은 웹 서버 쪽의 웹 컨테이너에 상태를 유지하기 위한 정보를 저장한다.
세션은 사용자의 정보를 유지하기 위해 javax.servlet.http 패키지의 HttpSession 인터페이스를 구현해서 사용한다. 쿠키는 사용자의 상태 유지를 위한 정보를 웹 브라우저에 저장해서 웹 서버가 쿠키 정보를 읽어서 사용한다.
이것은 웹 브라우저에 저장된 쿠키는 웹 서버에서 열어볼 수 있다는 점에서 보안상 문제가 발생할 수 있다. 따라서 사용자의 정보를 유지하기 위해서는 쿠키를 사용하는 것보다 세션을 사용한 웹 브라우저와 웹 서버의 상태 유지가 훨씬 안정적이고, 보안상의 문제도 해결할 수 있다.
세션은 웹 브라우저 당 1개씩 생성되어 웹 컨테이너에 저장된다
웹 서버는 각각의 웹 브라우저로부터 발생한 요청에 대해서 특별한 식별자를 부여한다. 이후에 이 식별자를 웹 브라우저에서 발생한 요청들과 비교해서 같은 식별인지를 구별하게 된다. 이 특별한 식별자에 특정한 값을 넣을 수 있으며, 이것을 사용해서 세션을 유지하게 된다.
- 세션(Session) 메소드 리스트
- 세션(Session)의 속성
• index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>인덱스</title>
</head>
<body>
<a href="joinview.jsp">회원가입</a><br>
<a href="loginview.jsp">로그인</a>
</body>
</html>
• loginview.jsp
<%@page import="com.koreait.dto.UserDTO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인</title>
</head>
<style>
body{
background-color:rgb(245,246,247);
}
#wrap{
width:600px;
margin:0 auto;
}
#wrap > tr, #wrap > td{
padding:10px;
}
#wrap > tr{
width:200px;
}
#wrap > td{
width:350px;
}
input[type=text], input[type=password]{
padding:10px;
width:200px;
margin-left:20px;
border:1px solid #ccc;
outline:none;
}
input[type=submit]{
margin-top:30px;
padding:10px 20px;
width:100px;
border:none;
background-color:rgb(0,200,80);
border-radius:5px;
color:#fff;
font-weight:bold;
font-size:18px;
}
</style>
<body>
<%
// session : 세션(session)은 웹 서버 쪽의 웹 컨테이너에 상태를 유지하기 위한 정보를 저장한다.
// .getAttribute : 선택한 요소(element)의 특정 속성의 값을 가져옵니다.
UserDTO joinUser = (UserDTO)session.getAttribute("joinUser");
String userid = null;
if(joinUser != null){
userid = joinUser.getUserid();
session.removeAttribute("joinUser");
}
String check = request.getParameter("l");
if(check != null && check.equals("f")){
%>
<script>alert("로그인 실패!");</script>
<%
}
%>
<div id="wrap">
<form name="loginForm" action="login_db.jsp" method="post" onsubmit="return sendit();">
<table>
<tr>
<th>아이디</th>
<td>
<input type="text" name="userid" placeholder="아이디를 입력하세요"
value="<%=userid==null?"":userid%>">
</td>
</tr>
<tr>
<th>비밀번호</th>
<td>
<input type="password" name="userpw" placeholder="비밀번호를 입력하세요">
</td>
</tr>
<tr>
<th colspan="2"><input type="submit" value="로그인"></th>
</tr>
</table>
</form>
</div>
</body>
<script src="./user.js"></script>
</html>
• login_db.jsp
<%@page import="com.koreait.dao.UserDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
UserDAO udao = new UserDAO();
String userid = request.getParameter("userid");
String userpw = request.getParameter("userpw");
if(udao.login(userid,userpw)){
session.setAttribute("loginUser", userid);
response.sendRedirect("mainview.jsp");
}
else{
response.sendRedirect("loginview.jsp?l=f");
}
%>
• UserDAO.java
package com.koreait.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import com.koreait.dto.UserDTO;
public class UserDAO {
Context context;
DataSource ds;
Connection conn;
PreparedStatement ps;
ResultSet rs;
public boolean join(UserDTO newUser) {
int result = 0;
try {
// context : 설정하면서 name을 적었는데 name을 찾아올 객체
context = new InitialContext(null);
// lookup이 항상 DataSource type만 return하는게 아니다.
// 다른 type을 만들 수 있기 때문인데 그렇다보니까 return할 때
// object되어 있다. 즉, 업캐스팅된 객체를 돌려주기 때문에 다운캐스팅을 해줘야 한다.
ds = (DataSource)context.lookup("java:comp/env/jdbc/mysql");
conn = ds.getConnection();
String sql = "insert into test_user values(?,?,?,?,?,?,?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, newUser.getUserid());
ps.setString(2, newUser.getUserpw());
ps.setString(3, newUser.getUsername());
ps.setString(4, newUser.getUsergender());
ps.setString(5, newUser.getZipcode());
ps.setString(6, newUser.getAddr());
ps.setString(7, newUser.getAddrdetail());
ps.setString(8, newUser.getAddretc());
String[] hobbies = newUser.getUserhobby(); // [게임, 운동, 코딩]
// hobbies[0]을 하는 이유는 무조건 하나는 체크를 하기 위해서
String hobbyStr = hobbies[0]; // "게임"
// length가 1개면 반복문을 한번도 안돈다.
// 즉, 0번방 한개만 존재하면 for문을 돌지 않는것이다.
for (int i = 1; i < hobbies.length; i++) { // ← 1~2니까 운동, 코딩이 포함된다.
hobbyStr += "," + hobbies[i]; // 운동, 코딩 앞에 ,을 붙여서 추가해 준다.
}
ps.setString(9, hobbyStr);
result = ps.executeUpdate();
} catch(NamingException ne) {
System.out.println(ne);
} catch (SQLException sqle) {
System.out.println(sqle);
}
return result == 1;
}
public boolean checkId(String userid) {
try {
// context : 설정하면서 name을 적었는데 name을 찾아올 객체
context = new InitialContext(null);
// lookup이 항상 DataSource type만 return하는게 아니다.
// 다른 type을 만들 수 있기 때문인데 그렇다보니까 return할 때
// object되어 있다. 즉, 업캐스팅된 객체를 돌려주기 때문에 다운캐스팅을 해줘야 한다.
ds = (DataSource)context.lookup("java:comp/env/jdbc/mysql");
conn = ds.getConnection();
String sql = "select * from test_user where userid=?";
ps = conn.prepareStatement(sql);
ps.setString(1, userid);
rs = ps.executeQuery();
// 없어야 true
return !rs.next();
} catch(NamingException ne) {
System.out.println(ne);
} catch (SQLException sqle) {
System.out.println(sqle);
}
return false;
}
public boolean login(String userid,String userpw) {
try {
context = new InitialContext(null);
ds = (DataSource)context.lookup("java:comp/env/jdbc/mysql");
conn = ds.getConnection();
String sql = "select * from test_uesr where userid=? and userpw=?";
ps = conn.prepareStatement(sql);
ps.setString(1, userid);
rs= ps.executeQuery();
return rs.next();
} catch(NamingException ne) {
System.out.println(ne);
} catch(SQLException sqle) {
System.out.println(sqle);
}
return false;
}
}
• UserDTO.java
package com.koreait.dto;
public class UserDTO {
private String userid;
private String userpw;
private String username;
private String usergender;
private String zipcode;
private String addr;
private String addrdetail;
private String addretc;
private String[] userhobby;
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getUserpw() {
return userpw;
}
public void setUserpw(String userpw) {
this.userpw = userpw;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUsergender() {
return usergender;
}
public void setUsergender(String usergender) {
this.usergender = usergender;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public String getAddrdetail() {
return addrdetail;
}
public void setAddrdetail(String addrdetail) {
this.addrdetail = addrdetail;
}
public String getAddretc() {
return addretc;
}
public void setAddretc(String addretc) {
this.addretc = addretc;
}
public String[] getUserhobby() {
return userhobby;
}
public void setUserhobby(String[] userhobby) {
this.userhobby = userhobby;
}
}
• login_db.jsp
<%@page import="com.koreait.dao.UserDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
UserDAO udao = new UserDAO();
String userid = request.getParameter("userid");
String userpw = request.getParameter("userpw");
if(udao.login(userid,userpw)){
session.setAttribute("loginUser", userid);
response.sendRedirect("mainview.jsp");
}
else{
// "loginview.jsp?login=false" : 로그인이 실패했니?
response.sendRedirect("loginview.jsp?l=f");
}
%>
• mainview.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>mainview</title>
</head>
<body>
<%
String loginUser = null;
loginUser = (String)session.getAttribute("loginUser");
if(loginUser == null){
%>
<script>
alert("로그인 후 이용하세요!");
location.href = "loginview.jsp"
</script>
<%
}
else{
%>
<table>
<tr>
<td><%=loginUser%> 님 방문을 환영합니다~!</td>
<td><a href="logout_session.jsp">로그아웃</a></td>
</tr>
</table>
<%
}
%>
</body>
</html>
• logout_session.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
if(session.getAttribute("loginUser") != null){
// session.invalidate() : 로그아웃하면 정보를 남겨둘 필요가 없으니 전부 날려주는 것
session.invalidate();
response.sendRedirect("index.jsp");
}
%>
• join_db.jsp
<%@page import="com.koreait.dao.UserDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<% request.setCharacterEncoding("UTF-8"); %>
<jsp:useBean id="newUser" class="com.koreait.dto.UserDTO"/>
<jsp:setProperty property="*" name="newUser"/>
<%
//String[] hobbies = newUser.getUserhobby();
//for(int i=0;i<hobbies.length;i++){
// System.out.println(hobbies[i]);
//}
UserDAO udao = new UserDAO();
if(udao.join(newUser)){
session.setAttribute("joinUser", newUser);
%>
<script>
alert("회원가입을 축하합니다!");
location.href = "loginview.jsp";
</script>
<%
}
else{
%>
<script>
alert("잠시 후에 다시 시도해주세요.")
location.href = "joinview.jsp";
</script>
<%
}
%>
• joinView.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입</title>
<style>
body{
background-color:rgb(245,246,247);
}
input{
box-sizing: border-box;
cursor:pointer;
}
table{
border-collapse: collapse;
}
th{
text-align: left;
}
th::after{
content:"";
display:inline-block;
box-sizing:border-box;
width:1px;
height:14px;
}
th,td{
padding:5px;
}
td{
padding-left:20px;
width:400px;
}
input[type=text], input[type=password]{
padding:10px 15px 10px 10px;
border:1px solid #ccc;
width:250px;
}
input:focus{
outline:none;
border:1px solid rgb(0,200,80);
}
td > input[type=text]+input[type=button]{
margin-left:10px;
padding:8px 10px;
background-color:rgb(0,200,80);
color:#fff;
font-size:14px;
font-weight:bold;
border:none;
border-radius:5px;
width:80px;
}
.gender_area > td{
font-size:16px;
}
.zipcode_area > td > input[type=text]{
width:200px;
}
.zipcode_area > td > input[type=button]{
width:130px !important;
}
.addr_area > td > input[type=text], .addr_area+tr > td > input[type=text], .addr_area+tr+tr > td > input[type=text]{
width:340px;
}
.hobby_area > td > div{
display: flex;
width:360px;
flex-wrap: wrap;
}
.hobby_area > td > div > div{
padding:10px;
flex:1 1 40%;
}
.hobby_area > td > div > div:nth-child(2n){
border-left:1px solid #ccc;
}
input[type=submit]{
margin:0 auto;
padding:10px 10px;
margin-left:40px;
background-color:rgb(0,200,80);
color:#fff;
font-size:20px;
font-weight:bold;
border:none;
border-radius:5px;
width:400px;
}
</style>
</head>
<body>
<form name="joinForm" method="post" action="join_db.jsp" onsubmit="return join();">
<table>
<tr>
<td id="result" colspan="2"></td>
</tr>
<tr>
<th><label for="userid">아이디</label></th>
<td><input type="text" name="userid" id="userid"><input type="button" value="중복검사" onclick="checkId()"></td>
</tr>
<tr>
<th><label for="userpw">비밀번호</label></th>
<td><input type="password" name="userpw" id="userpw"></td>
</tr>
<tr>
<th><label for="userpw_re">비밀번호 확인</label></th>
<td><input type="password" name="userpw_re" id="userpw_re"></td>
</tr>
<tr>
<th><label for="username">이름</label></th>
<td><input type="text" name="username" id="username"></td>
</tr>
<tr class="gender_area">
<th>성별</th>
<td>
<label>남자 <input type="radio" name="usergender" value="M" checked></label>
<label>여자 <input type="radio" name="usergender" value="W"></label>
</td>
</tr>
<tr class="zipcode_area">
<th>우편번호</th>
<td>
<!-- readonly는 수정이 안되게 막는 것이다. -->
<input readonly name="zipcode" type="text" id="sample6_postcode" placeholder="우편번호"><input type="button" onclick="sample6_execDaumPostcode()" value="우편번호 찾기">
</td>
</tr>
<tr class="addr_area">
<th>주소</th>
<!-- readonly는 수정이 안되게 막는 것이다. -->
<td><input readonly name="addr" type="text" id="sample6_address" placeholder="주소"></td>
</tr>
<tr>
<th>상세주소</th>
<td><input name="addrdetail" type="text" id="sample6_detailAddress" placeholder="상세주소"></td>
</tr>
<tr>
<th>참고항목</th>
<!-- readonly는 수정이 안되게 막는 것이다. -->
<td><input readonly name="addretc" type="text" id="sample6_extraAddress" placeholder="참고항목"></td>
</tr>
<tr class="hobby_area">
<th>취미</th>
<td>
<div>
<div>
<label><input type="checkbox" name="userhobby" value="게임"> 게임하기</label>
</div>
<div>
<label><input type="checkbox" name="userhobby" value="그림"> 그림그리기</label>
</div>
<div>
<label><input type="checkbox" name="userhobby" value="영화"> 영화보기</label><br>
</div>
<div>
<label><input type="checkbox" name="userhobby" value="운동"> 운동하기</label>
</div>
<div>
<label><input type="checkbox" name="userhobby" value="노래"> 노래부르기</label>
</div>
<div>
<label><input type="checkbox" name="userhobby" value="코딩"> 코딩하기</label>
</div>
</div>
</td>
</tr>
<tr>
<th colspan="2">
<input type="submit" value="가입 완료">
</th>
</tr>
</table>
</form>
</body>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script src="./user.js"></script>
</html>
• WebContent에다가 javascriptSourceFile로 생성
• user.js
→ 여기서는 javascript 문법을 바로 사용 가능하다.
function sample6_execDaumPostcode() {
new daum.Postcode({
oncomplete: function(data) {
// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
// 각 주소의 노출 규칙에 따라 주소를 조합한다.
// 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
var addr = ''; // 주소 변수
var extraAddr = ''; // 참고항목 변수
//사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
if (data.userSelectedType === 'R') { // 사용자가 도로명 주소를 선택했을 경우
addr = data.roadAddress;
} else { // 사용자가 지번 주소를 선택했을 경우(J)
addr = data.jibunAddress;
}
// 사용자가 선택한 주소가 도로명 타입일때 참고항목을 조합한다.
if(data.userSelectedType === 'R'){
// 법정동명이 있을 경우 추가한다. (법정리는 제외)
// 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
if(data.bname !== '' && /[동|로|가]$/g.test(data.bname)){
extraAddr += data.bname;
}
// 건물명이 있고, 공동주택일 경우 추가한다.
if(data.buildingName !== '' && data.apartment === 'Y'){
extraAddr += (extraAddr !== '' ? ', ' + data.buildingName : data.buildingName);
}
// 표시할 참고항목이 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
if(extraAddr !== ''){
extraAddr = ' (' + extraAddr + ')';
}
// 조합된 참고항목을 해당 필드에 넣는다.
document.getElementById("sample6_extraAddress").value = extraAddr;
} else {
document.getElementById("sample6_extraAddress").value = '';
}
// 우편번호와 주소 정보를 해당 필드에 넣는다.
document.getElementById('sample6_postcode').value = data.zonecode;
document.getElementById("sample6_address").value = addr;
// 커서를 상세주소 필드로 이동한다.
document.getElementById("sample6_detailAddress").focus();
}
}).open();
}
function checkId() {
const userid = document.joinForm.userid;
if(userid.value.length<5 || userid.value.length>12) {
alert("아이디는 5자 이상 12자 이하로 입력해주세요!");
userid.focus();
return false;
}
const result = document.getElementById("result");
const xhr = new XMLHttpRequest();
xhr.open("GET","checkId_db.jsp?userid="+userid.value,true);
// 서버에서 응답이 도착하면 특정한 자바스크립트 함수를 호출
xhr.onreadystatechange = function() {
// readyState == 4란 의미는 데이터를 전부 받은 상태, 완료된 상태를 의미한다.
if(xhr.readyState == 4) {
// status == 200은 서버로 부터 응답상태가 요청에 성공하였다는 의미다.
if(xhr.status == 200) {
// 문자열로 응답 데이터를 얻음
let txt = xhr.responseText;
txt = txt.trim();
if(txt == "O") {
result.innerHTML = "사용할 수 있는 아이디입니다!";
} else {
result.innerHTML = "이미 존재하는 아이디입니다!";
}
}
}
}
xhr.send();
}
// 회원가입 유효성 검사
function join() {
const joinForm = document.joinForm;
const result = document.getElementById("result");
const userid = joinForm.userid;
if(userid.value == ""){
alert("아이디를 입력하세요!");
userid.focus();
//userid.style.borderColor = "red";
return false;
}
if(userid.value.length < 5 || userid.value.length > 12){
alert("아이디는 5자 이상 12자 이하로 작성해주세요!");
userid.focus();
return false;
}
//userid.style.borderColor = "black";
if(result.innerHTML == ""){
alert("아이디 중복검사를 해주세요!")
return false;
}
if(result.innerHTML != "사용할 수 있는 아이디입니다!"){
alert("아이디가 중복되었습니다!");
userid.focus();
return false;
}
const userpw = joinForm.userpw;
const userpw_re = joinForm.userpw_re;
if(userpw.value == ""){
alert("비밀번호를 입력하세요!");
userpw.focus();
return false;
}
// 정규식 - https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Regular_Expressions
// /^(?=.*?)/ : 맨 뒤 ? 뒤에 문자를 쓰면 그 문자가 있는지 확인하는 정규식
let reg = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[~?!@#$%^&*_-]).{8,}$/
if(!reg.test(userpw.value)) {
alert("비밀번호는 8자 이상, 숫자, 대문자, 소문자, 특수문자를 모두 포함해야 합니다!");
userpw.focus();
return false;
}
// ↓의미 \w : 어떤문자가 있고 \1\1\1 : 그 문자가 연속으로 3번 반복되는지
// \w\1\1\1 : 어떤문자가 4번 연속으로 써지는지 확인
if(/(\w\1\1\1/.test(userpw.value)) {
alert("같은 문자를 4번 이상 연속해서 사용하실 수 없습니다!");
userpw.focus();
return false;
}
// 띄어쓰기가 포함되어 있는지 확인
/*if(/\s/)*/
// -1이면 띄어쓰기가 포함 x
// -1이 아니면 띄어쓰기 포함
if(userpw.value.search(" ") != -1) {
alert("비밀번호는 공백을 포함할 수 없습니다!");
userpw.focus();
return false;
}
if(userpw_re.value == "") {
alert("비밀번호를 확인을 해주세요!");
userpw_re.focus();
return false;
}
if(userpw.value != userpw_re.value ) {
alert("비밀번호 확인을 다시 해주세요!");
userpw.focus();
return false;
}
const username = joinForm.username;
if(username.value == ""){
alert("이름을 입력하세요!");
username.focus();
return false;
}
const zipcode = joinForm.zipcode;
if(zipcode.value == "") {
alert("주소찾기를 진행해주세요!");
sample6_execDaumPostcode();
return false;
}
const addrdetail = joinForm.addrdetail;
if(addrdetail.value =="") {
alert("주소를 마저 입력해주세요!");
addrdetail.focus();
return false;
}
// userhobby를 찾으면 배열로 받아 올 수 있다.
// for(let hobby of hobbies) : userhobby라고 되어 있는 name들을 전부 꺼내오면서 hobby에 넣어준다.
const hobbies = joinForm.userhobby;
let check = false;
for(let hobby of hobbies) {
// hobby.checked : 취미가 채크가 되어있는지 확인
if(hobby.checked) {
check = true;
break;
}
}
if(!check) {
alert("취미를 하나 이상 선택하세요!");
return false;
}
return true;
}
// 로그인 유효성 검사
function sendit() {
const userid = document.loginForm.userid;
const userpw = document.loginForm.userpw;
if(userid.value=="") {
alert("아이디를 입력하세요!")
userid.focus();
return false;
}
if(userpw.value=="") {
alert("비밀번호를 입력하세요!")
userpw.focus();
return false;
}
return true;
}