boardProject
( 디자인적인 부분들 조금씩 수정 )
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<header>
<!-- 클릭 시 메인페이지로 이동하는 로고 -->
<section>
<a href="/">
<img src="/resources/images/logo.jpg" id="homeLogo">
</a>
</section>
<!-- 검색창 부분 -->
<section>
<section class="search-area">
<!-- form 내부 input 태그 값을 서버 또는 페이지로 전달 -->
<form action="#" name="search-form">
<!-- fieldset : form 내부에서 input을 종류별로 묶는 용도로 자주 사용 -->
<fieldset>
<!-- search : 텍스트 타입과 기능적으로는 똑같으나,
브라우저에 의해 다르게 표현될 수 있음. -->
<!-- autocomplete : HTML 기본 자동완성 사용 X -->
<input type="search" id="query" name="query"
autocomplete="off" placeholder="검색어를 입력해주세요."
>
<button id="searchBtn" class="fa-solid fa-magnifying-glass"></button>
</fieldset>
</form>
</section>
</section>
<section></section>
</header>
<!-- 보통은 header안에 작성하나 사이드에 nav가 있는 경우도 있기 때문에 따로 작성해본다! -->
<nav>
<ul>
<li><a href="#">공지사항</a></li>
<li><a href="#">자유게시판</a></li>
<li><a href="#">질문게시판</a></li>
<li><a href="#">FAQ</a></li>
<li><a href="#">1:1문의</a></li>
</ul>
</nav>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- footer는 주요 콘텐츠가 아니라서 보통 main 태그 안에 작성하지 않음. -->
<footer>
<p>Copyright © 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>
<%@ 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>Insert title here</title>
<link rel="stylesheet" href="/resources/css/main-style.css">
<!-- 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>
<%-- 로그인이 안되었을때 --%>
<c:when test="${true}">
<form action="/member/login" method="post" name="login-form" id="loginFrm">
<fieldset class="id-pw-area">
<section>
<input type="text" name="inputEmail" placeholder="이메일">
<input type="password" name="inputPw" 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">
</a>
<div class="my-info">
<div>
<a href="#" id="nickname"></a>
<a href="#" 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>
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
/* div, main, section, header, footer, article, nav, aside, form
{ border : 1px solid black; } */
/* 다 만들고 삭제 */
main{
width: 1140px;
margin : auto;
/* min-height : 요소의 최소 높이
-> 내부 요소가 없어도 최소 높이 유지
-> 내부 요소가 지정된 크기를 초과하면
그에 맞에 늘어남
*/
min-height: 500px;
}
/* header */
header{
height: 200px;
display: flex;
flex-direction: row;
}
header > section:nth-child(1){ width: 15%;}
header > section:nth-child(2){ width: 70%;}
header > section:nth-child(3){ width: 15%; }
/* 로고 */
header > section:first-child{
display: flex;
justify-content: center;
align-items: center;
}
#homeLogo{
width: 120px;
height: auto; /* 이미지 기본 비율에 맞게 자동 지정 */
}
/* 검색 영역 */
header > section:nth-child(2){
display: flex;
justify-content: center;
align-items: center;
}
.search-area{
width: 500px;
}
.search-area fieldset{
border: 2px solid #455ba8;
padding : 2px;
border-radius: 5px;
display: flex;
}
/* 검색 창 */
#query{
border : none; /* 테두리 없애기 */
/* outline :
포커스가 맞춰진 input의 테두리 */
/* outline : none; */
outline : 0;
font-size: 18px;
font-weight: bold;
padding : 10px;
width: 92%;
}
/* 검색 버튼 */
#searchBtn{
width: 8%;
cursor: pointer;
border : none;
font-size: 1.2em;
color : #455ba8;
background-color: white;
}
/* nav */
nav{
height: 50px;
position: sticky;
/* 화면 밖으로 벗어났을 때
지정된 위치에 고정*/
top : 0; /* 최상단에 붙임 */
background-color: white;
border-bottom: 2px solid black;
z-index: 1000;
}
nav > ul{
display: flex;
/* li 태그 앞에 기호 삭제 */
list-style: none;
height: 100%;
}
nav li{
flex : 0 1 150px;
/* grow shrink basis */
}
nav a{
text-decoration: none;
color : black;
font-size: 18px;
font-weight: bold;
padding : 11px 0;
display: block;
text-align: center;
border-radius: 5px;
transition-duration: 0.1s;
height: 100%;
}
nav a:hover{
color : white;
background-color: #455ba8;
}
/* content */
.content{
height: 800px;
display: flex;
/* flex 지정 시 기본값 */
flex-direction: row;
justify-content: flex-start;
/* flex-start : flex요소에만 사용 가능한 정렬 방식 */
/* start : block요소, flex, grid 등에서도 적용 가능한 정렬 방식 */
align-items: stretch;
}
.content-1{
flex-basis: 70%;
padding : 20px;
}
.content-2{ flex-basis: 30%;}
.content-1 > *{
margin-bottom: 20px;
}
.content-1 > pre{
font-size: 1rem;
font-family: '고딕';
}
/* 로그인 영역 */
.content-2 > form{
height: 150px;
padding : 10px;
margin-top : 10px;
display: flex;
flex-direction: column;
justify-content: center;
}
.id-pw-area{
border: 1px solid #ccc;
display: flex;
/* 중심축 방향 크기 지정 */
flex-basis: 60%;
}
/* id/pw 입력 영역*/
.id-pw-area > section:first-child{
width: 75%;
display: flex;
flex-direction: column;
}
.id-pw-area > section:first-child > input{
border : 0;
border-right : 1px solid #ccc;
flex-basis: 50%;
outline: none;
padding: 5px;
}
/* id input 태그 */
input[name='inputEmail']{
border-bottom : 1px solid #ccc !important;
}
/* id/pw에 초점이 맞춰졌을 때 */
.id-pw-area > section:first-child > input:focus{
border: 2px solid #455ba8 !important;
}
/* 로그인 버튼 영역 */
.id-pw-area > section:last-child{
width: 25%;
display: flex;
justify-content: center;
}
/* 로그인 버튼 */
.id-pw-area > section:last-child > button{
width: 100%;
background-color: white;
cursor: pointer;
border : none;
}
/* 로그인 버튼에 마우스가 올라갔을 때 */
.id-pw-area > section:last-child > button:hover{
background-color: #455ba8;
color: white;
}
/* 아이디 저장 label */
.content-2 label{
margin-top: 5px;
font-size: 14px;
display: flex;
padding: 5px 0;
}
input[name='saveId']{
margin-right: 5px;
}
/* 회원가입, ID/PW 찾기 영역 */
.signup-find-area{
margin-top: 10px;
padding-left: 5px;
}
.signup-find-area > a{
color : black;
text-decoration: none;
font-size: 14px;
}
.signup-find-area > span{
padding: 0 10px;
}
/* footer */
footer{
height: 200px;
background-color: #a3add342;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
footer > p{
font-weight: bold;
margin : 16px 0;
}
footer > article > *{
font-size: 14px;
}
footer a{
color:black;
text-decoration: none;
}
footer span{
padding : 0 10px;
}
/* ---------------------------------------------------------------------------- */
/* 로그인 성공 시 화면 */
.login-area{
height: 120px;
padding: 30px 10px;
margin: 10px 0;
border: 1px solid black;
border-radius: 10px;
display: flex;
align-items: center;
}
/* 프로필 이미지 */
#memberProfile{
width: 64px;
height: 64px;
border-radius: 50%;
}
/* 프로필 이미지를 감싸고 있는 a태그 */
.login-area > a{
display: block;
width: 70px;
height: 70px;
border: 3px solid #ddd;
border-radius: 50%;
}
/* 회원 정보 + 로그아웃 버튼 영역 설정 */
.my-info{
width: 100%;
height: 100%;
margin-left: 20px;
}
.my-info > div{
display: flex;
justify-content: space-between;
}
a{
text-decoration: none;
color : black;
}
/* 닉네임 */
#nickname{
color : black;
font-size: 20px;
font-weight: bold;
letter-spacing: -3px;
}
/* 로그아웃 버튼 */
#logoutBtn{
color : gray;
font-size: 14px;
font-weight: bold;
border: 2px solid gray;
padding : 3px 5px;
border-radius: 10px;
}
#logoutBtn:hover{
color : white;
background-color: gray;
}
#logoutBtn:active{
transform: scale(0.8);
}
/* 내정보 - 이메일 */
.my-info > p{
margin: 0;
color : gray;
}
/**********************************/
/* 검색결과 */
.search-page {
padding : 50px;
min-height: 1000px;
}
.search-div {
margin-top: 20px;
border: 1px solid black;
border-radius: 20px;
padding : 20px;
}
.sign-form {
margin-top: 20px;
}
.sign-form input {
margin-bottom: 20px;
}
-> 로그인창 클릭하면 화면이 변경되게끔 만들 예정.
lib > lombok, ojdbc, taglibs 넣어주기
1 ) 위 화면 창 shift + 빈 곳 우클릭 = 파워쉘창열기
2 )
3 )
4 )
5 )
6 )
7 ) 창 닫은 후 이클립스 껐다 켜기
** LOMBOK은 SPRING때도 사용하므로 잘 기억하기!
<%@ 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>Insert title here</title>
<link rel="stylesheet" href="/resources/css/main-style.css">
<!-- 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="inputEmail" placeholder="이메일">
<input type="password" name="inputPw" 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">
</a>
<div class="my-info">
<div>
<a href="#" id="nickname"></a>
<a href="#" 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>
package edu.kh.project.member.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import edu.kh.project.member.model.dto.Member;
import edu.kh.project.member.model.service.MemberService;
@WebServlet("/member/login")
public class LoginController extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
// 인코딩 처리
req.setCharacterEncoding("UTF-8");
// 파라미터 얻어오기
String inputEmail = req.getParameter("inputEmail");
String inputPw = req.getParameter("inputPw");
// 서비스 객체 생성
MemberService service = new MemberService();
// 로그인 서비스 호출 후 결과 반환받기
Member loginMember = service.login(inputEmail, inputPw);
System.out.println(loginMember);
// Session 객체 생성
HttpSession session = req.getSession();
if(loginMember != null) { // 로그인 성공
// session에 로그인한 회원 정보를 추가
session.setAttribute("loginMember", loginMember);
// session 만료 시간 지정 (초단위 지정)
session.setMaxInactiveInterval(60*60);
// forward : 요청 처리 후 자체적인 화면이 존재하여
// 이를 이용해서 응답
// redirect : 요청 처리 후 자체적인 화면이 없어서
// 화면이 있는 다른 요청을 다시 호출(요청)
resp.sendRedirect("/"); // 메인 페이지 재요청
} else { // 실패
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
package edu.kh.project.common;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JDBCTemplate {
/* Template : 주형, 양식, 본뜨기 위한 틀
*
* JDBCTemplate : JDBC 관련 작업을 위한 코드를 제공하는 클래스
*
* - DB 연결을 위한 Connection 생성 구문
* - JDBC 객체 자원 반환 구문(close)
* - commit, rollback 구문
* - auto commit 기능 off
*
*
* * 어디서든 클래스명.메서드명 으로 호출 가능하도록
* public static으로 작성 *
* */
// 필드
private static Connection conn = null;
// 왜 static 필드?
// - static 메서드가 참조 가능한 필드는 static 필드 밖에 없기 때문에
public static Connection getConnection() {
try {
// 커넥션 객체가 없거나 닫혀있는 경우
// -> 새로운 연결(커넥션 다시 얻어오기)
if(conn == null || conn.isClosed()) {
// conn.isClosed() : 커넥션이 close 상태이면 true 반환
Properties prop = new Properties();
// Map<String, String> 형태, XML 파일 입출력 특화
String filePath
= JDBCTemplate.class.getResource("/edu/kh/project/sql/driver.xml").getPath();
// file:/C:/server_06/...
// /C:/server_06/...
System.out.println(filePath);
prop.loadFromXML( new FileInputStream(filePath) );
// 스트림을 이용해서 driver.xml 파일을 읽어와 prop에 저장
// prop에 저장된 값을 변수로 따로 저장
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String user = prop.getProperty("user");
String pw = prop.getProperty("pw");
// Oracle JDBC Driver 객체 메모리 로드
Class.forName(driver);
// DriverManager를 이용해 Connection 얻어오기
conn = DriverManager.getConnection(url, user, pw);
// ** 자동 커밋 비활성화 ** //
// -> 왜? 개발자가 직접 트랜잭션을 제어하기 위해서
conn.setAutoCommit(false);
}
}catch (Exception e) {
e.printStackTrace();
}
return conn;
}
//--------- close() 구문 ---------
/** Connection close() 메서드
* @param conn
*/
public static void close(Connection conn) {
try {
if(conn != null && !conn.isClosed()) conn.close();
}catch (SQLException e) {
e.printStackTrace();
}
}
/** Statement close() 메서드
* @param stmt
*/
public static void close(Statement stmt) {
try {
if(stmt != null && !stmt.isClosed()) stmt.close();
}catch (SQLException e) {
e.printStackTrace();
}
}
/** ResultSet close() 메서드
* @param rs
*/
public static void close(ResultSet rs) {
try {
if(rs != null && !rs.isClosed()) rs.close();
}catch (SQLException e) {
e.printStackTrace();
}
}
/** 트랜잭션 Commit 메서드
* @param conn
*/
public static void commit(Connection conn) {
try {
if(conn != null && !conn.isClosed()) conn.commit();
}catch (SQLException e) {
e.printStackTrace();
}
}
/** 트랜잭션 Rollback 메서드
* @param conn
*/
public static void rollback(Connection conn) {
try {
if(conn != null && !conn.isClosed()) conn.rollback();
}catch (SQLException e) {
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>driver.xml file 입니다.</comment>
<entry key="driver">oracle.jdbc.driver.OracleDriver</entry>
<!-- 각자 컴퓨터에 설치된 DB(18c) -->
<entry key="url">jdbc:oracle:thin:@localhost:1521:XE</entry>
<!-- 학원 DB 서버 -->
<!-- <entry key="url">jdbc:oracle:thin:@115.90.212.22:10000:XE</entry> -->
<!-- project 계정 -->
<entry key="user">project</entry>
<entry key="pw">project1234</entry>
</properties>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="login">
SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICKNAME, 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 = ?
AND MEMBER_PW = ?
</entry>
</properties>
package edu.kh.project.member.model.service;
import static edu.kh.project.common.JDBCTemplate.*;
import java.sql.Connection;
import edu.kh.project.member.model.dao.MemberDAO;
import edu.kh.project.member.model.dto.Member;
public class MemberService {
private MemberDAO dao = new MemberDAO();
// 주석 단축키 : 메소드 커서 -> alt + shift + J
/** 로그인 서비스
* @param inputEmail
* @param inputPw
* @return
*/
public Member login(String inputEmail, String inputPw) throws Exception{
Connection conn = getConnection();
Member loginMember = dao.login(conn, inputEmail, inputPw);
close(conn);
return loginMember;
}
}
package edu.kh.project.member.model.dao;
import static edu.kh.project.common.JDBCTemplate.*;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import edu.kh.project.member.model.dto.Member;
public class MemberDAO {
private Statement stmt;
private PreparedStatement pstmt;
private ResultSet rs;
private Properties prop;
public MemberDAO() {
try {
prop = new Properties();
String filePath
= MemberDAO.class.getResource("/edu/kh/project/sql/member-sql.xml").getPath();
prop.loadFromXML(new FileInputStream(filePath));
} catch(Exception e) {
e.printStackTrace();
}
}
/** 로그인 DAO
* @param conn
* @param inputEmail
* @param inputPw
* @return
*/
public Member login(Connection conn, String inputEmail, String inputPw) throws Exception{
Member loginMember = null;
try {
String sql = prop.getProperty("login");
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, inputEmail);
pstmt.setString(2, inputPw);
rs = pstmt.executeQuery();
if(rs.next()) {
loginMember = new Member();
loginMember.setMemberNo( rs.getInt(1) );
loginMember.setMemberEmail( rs.getString(2) );
loginMember.setMemberNickname( rs.getString(3) );
loginMember.setMemberTel( rs.getString(4) );
loginMember.setMemberAddress( rs.getString(5) );
loginMember.setProfileImage( rs.getString(6) );
loginMember.setAuthority( rs.getInt(7) );
loginMember.setEnrollDate( rs.getString(8) );
}
} finally {
close(rs);
close(pstmt);
}
return loginMember;
}
}
package edu.kh.project.member.model.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
// lombok 다운로드 및 lib > lombok.jar 파일 넣기 -> 두 단계 모두 진행해야 import 가능!
@Getter
@Setter
@ToString
@NoArgsConstructor // 기본생성자
@AllArgsConstructor // 모든 필드 매개변수 생성자
public class Member {
// lombok 라이브러리 : getter/setter, 생성자, toString() 자동완성 라이브러리
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;
}
로그인 버튼 클릭 시,
cf )