CSS
div.box
<div class="box">
div .box
<div>
<p class="box">
</div>
: 로 표시 :hover 상태가 된다.box:hover{
background-color: yellow;
}
button:focus{
color: red;
}
button:active{
width: 100px;
height: 100px;
}
<button p-class="focus active">
button:focus{
color: red;
}
button:active{
width: 100px;
height: 100px;
}
a:link{
color: #000;
}
a:visited{
color: green;
}
:: 로 표시p:first-letter{
font-weight: bold;
color: red;
}
#one:first-line{
background-color: yellow;
}
.quote:before{
content:"'";
color: red;
}
.quote:after{
content:"'";
color: blue;
}
default position 속성의 값top, left, right, bottom 속성 무시⭐ 요소의 원래 위치는 그대로 유지 = 보이는 위치만 바뀌고 다른 요소에 영향이 없다
#one{
position: relative;
top: 150px; ➜ 위에서 떨어진 거리 = y 좌표
left: 150px; ➜ 왼쪽에서 떨어진 거리 = x 좌표
}
특정 요소 = 부모 요소position: relative, absolute, fixed, sticky가 처음 나오는 요소를 기준으로 배치 가능window 창 을 기준으로 절대 좌표에 배치 할 때 사용relative , 자식 요소 absolute<div class="wrapper">
<div class="box" id="one">one</div>
<div class="box" id="two">two</div>
<div class="box" id="three">three</div>
<div class="box" id="four">four</div>
<div class="box" id="center">center</div>
<div id="test">test</div>
</div>
.wrapper{
position: relative;
width: 500px;
height: 500px;
margin: 0 auto;
border: 1px solid red
}
.box{
position: absolute;
width: 50px;
height: 50px;
background-color: yellow;
}
어떤 영역을 덮어버리고 싶다면?
absolute 로 한 후 top, left, right, bottom 을 0 으로 설정#test{
background-color: #000;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
color: #fff;
opacity: 0.5;
display: none;
}
⭐ opacity = 투명도 (0 ~ 1.0)
#one{
float: left;
}
#two{
float: right;
}
⚠️ float 사용 후 레이아웃이 깨질 가능성이 존재
clear 속성이 필요할 수 있다
width, height 설정 가능width, height, margin, padding 모두 가능inline 처럼 줄 안에 배치되지만 block 처럼 크기 지정 가능BootStrap
data-bs-theme = dark
light, 어두운 배경에서는 dark로 지정해 흰색 글씨 사용JSP
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- /WEB-INF/include/resource.jsp -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-LN+7fdVzj6u52u30Kp6M/trliBMCMKTyK833zpbD+pXdCLuTusPj697FH4R/5mcr" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js" integrity="sha384-ndDqU0Gzau9qJ1lfW4pNLlhNTkCfHzAVBReH9diLvGRem5+R9g2FzA8ZGN954O5Q" crossorigin="anonymous"></script>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- /WEB-INF/include/footer.jsp -->
<footer class="bg-success text-white py-4 mt-5">
<div class="container d-flex justify-content-between align-items-center flex-wrap">
<div>
<h5 class="mb-0">MyBrand</h5>
</div>
<div>
<a href="#" class="text-white me-3">
<i class="bi bi-facebook"></i>
</a>
<a href="#" class="text-white me-3">
<i class="bi bi-twitter-x"></i>
</a>
<a href="#" class="text-white">
<i class="bi bi-instagram"></i>
</a>
</div>
</div>
</footer>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- /WEB-INF/include/navbar.jsp -->
<nav class="navbar navbar-expand-md bg-success" data-bs-theme="dark">
<div class="container">
<a class="navbar-brand" href="${pageContext.request.contextPath}/">Acorn</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="${pageContext.request.contextPath}/member/list.jsp">Member</a>
</li>
<li class="nav-item">
<a class="nav-link" href="${pageContext.request.contextPath}/book/list.jsp">Book</a>
</li>
</ul>
</div>
</div>
</nav>
<jsp:include page="/WEB-INF/include/navbar.jsp">
<jsp:param value="index" name="thisPage"/>
⤷ 파라미터 값 ⤷ 파라미터명
</jsp:include>
request.getParameter("thisPage")
navbar.jsp 페이지에서 이런식으로 파라미터를 읽어낼 수 있다
<%
// navbar.jsp 페이지가 어떤 페이지에 include 되었는지 파라미터 읽어오기
String thisPage = request.getParameter("thisPage");
// "index" or "member" or "book"
%>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link <%= thisPage.equals("member") ? "active" : "" %>"
href="${pageContext.request.contextPath}/member/list.jsp">Member</a>
</li>
<li class="nav-item">
<a class="nav-link <%= thisPage.equals("book") ? "active" : "" %>"
href="${pageContext.request.contextPath}/book/list.jsp">Book</a>
</li>
</ul>
</div>
<td><a href="javascript:" id="deleteLink" data-num="<%=tmp.getNum()%>">삭제</a></td>
<script>
document.querySelector("#deleteLink").addEventListener("click", (e)=>{});
</script>
⚠️ 오류 상태 : 현재는 가장 a 첫번째 요소에만 이벤트가 발생
<td><a href="javascript:" class="delete-link" data-num="<%=tmp.getNum()%>">삭제</a></td>
<script>
document.querySelectorAll(".delete-link").forEach(item=>{
// item 은 클린한 a 요소의 참조값
item.addEventListener("click", (e)=>{
// e.target 은 이벤트가 발생한 바로 그 요소의 참조값이다
// click 이벤트가 발생한 a 요소에 data-num 속성의 value 를 읽어오기
const num=e.target.getAttribute("data-num");
const isDelete=confirm(num+" 번 회원을 삭제 하시겠습니까?");
if(isDelete){
// delete.jsp 페이지로 이동하게 하면서 삭젷할 회원의 번호도 같이 전달되도록 한다
location.href="${pageContext.request.contextPath }/member/delete.jsp?num="+num;
// 결과적으로 delete.jsp?num=X
}
});
});
</script>
CREATE TABLE users(
num NUMBER PRIMARY KEY, -- 회원의 고유번호
userName VARCHAR2(20) UNIQUE, -- 아이디
password VARCHAR2(100) NOT NULL, -- 비밀번호
email VARCHAR2(50) UNIQUE, -- 이메일
profileImage VARCHAR2(100), -- 프로필 이미지 정보 (처음 가입 시 NULL)
role VARCHAR2(10) DEFAULT 'ROLE_USER', -- 역할 ROLE_USER(일반 사용자) | ROLE_STAFT(직원) | ROLE_ADMIN(최고 권한 관리자)
updatedAt DATE, -- 수정 날짜
createdAt DATE -- 가입 날짜
);
CREATE SEQUENCE users_seq;
<li><a href="${pageContext.request.contextPath}/user/signup-form.jsp">회원가입</a></li>
<div class="container">
<h1>회원가입 양식</h1>
<form action="signup.jsp" method="post">
<div>
<label for="userName">아이디</label>
<input type="text" name="userName" id="userName" />
</div>
<div>
<label for="password">비밀번호</label>
<input type="text" name="password" id="password" />
</div>
<div>
<label for="email">이메일</label>
<input type="text" name="email" id="email" />
</div>
<button type="submit">가입</button>
</form>
</div>
public class UserDto {
private long num;
private String userName;
private String password;
private String email;
private String profileImage;
private String role;
private String updatedAt;
private String createdAt;
// 생성자
public UserDto() {
}
// setter, getter 메소드
public long getNum() {
return num;
}
public void setNum(long num) {
this.num = num;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getProfileImage() {
return profileImage;
}
public void setProfileImage(String profileImage) {
this.profileImage = profileImage;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(String updateAt) {
this.updatedAt = updatedAt;
}
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
}
insert 생성 : 회원정보 추가public boolean insert(UserDto dto) {
Connection conn = null;
PreparedStatement pstmt = null;
int rowCount = 0;
try {
conn = new DbcpBean().getConn();
String sql = """
INSERT INTO users
(num, userName, password, email, updatedAt, createdAt)
VALUES(users_seq.NEXTVAL, ?, ?, ?, SYSDATE, SYSDATE)
""";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,dto.getUserName());
pstmt.setString(2, dto.getPassword());
pstmt.setString(3, dto.getEmail());
rowCount = pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (Exception e) {
}
}
if (rowCount > 0) {
return true;
} else {
return false;
}
}
<%
String userName = request.getParameter("userName");
String password = request.getParameter("password");
String email = request.getParameter("email");
// 사용자가 입력한 비밀번호를 암호화한 비밀번호를 얻어낸다
String hashed = BCrypt.hashpw(password, BCrypt.gensalt());
System.out.println("암호화된 비밀번호"+hashed);
// dto 에 담아서
UserDto dto = new UserDto();
dto.setUserName(userName);
dto.setPassword(hashed);
dto.setEmail(email);
// dao 객체를 이용해서 DB 에 저장하고
boolean isSuccess = new UserDao().insert(dto);
// 응답한다
%>
<div class="container">
<%if(isSuccess) { %>
<p>
<strong><%=userName %></strong> 님 회원가입 되었습니다
<a href="loginform.jsp">로그인 하러 가기</a>
</p>
<%} else{ %>
<p>
가입 실패
<a href="signup-form.jsp">다시 가입하기</a>
</p>
<%} %>
</div>
<a href="loginform.jsp">로그인 하러 가기</a>
<div class="container">
<h1>로그인 양식</h1>
<form action="login.jsp" method="post">
<div>
<label for="userName">아이디</label>
<input type="text" name="userName" id="userName" />
</div>
<div>
<label for="password">비밀번호</label>
<input type="password" name="password" id="password" />
</div>
<button type="submt">로그인</button>
</form>
</div>
// userName 을 이용해서 회원 한 명의 정보를 리턴해주는 메소드
public UserDto getByUserName(String userName) {
UserDto dto = null;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = new DbcpBean().getConn();
String sql = """
SELECT num, userName, password, email, profileImage, role, updatedAt, createdAt
FROM users
WHERE userName = ?
""";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, userName);
rs = pstmt.executeQuery();
// select 되는 userName은 unique 속성을 가지기에 if 로도 가능
if (rs.next()) {
// UserDto 객체를 생성해서
dto = new UserDto();
// select 된 정보를 담는다
dto.setNum(rs.getLong("num"));
dto.setUserName(userName);
dto.setPassword(rs.getString("password"));
dto.setEmail(rs.getString("email"));
dto.setProfileImage(rs.getString("profileImage"));
dto.setRole(rs.getString("role"));
dto.setUpdateAt(rs.getString("updatedAt"));
dto.setCreatedAt(rs.getString("createdAt"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (rs != null)
rs.close();
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (Exception e) {}
}
return dto;
}
<%
// 폼 전송되는 아이디와 비밀번호 추출하기
String userName = request.getParameter("userName");
String password = request.getParameter("password");
// 아이디 비밀번호가 유효한 정보인지 여부
boolean isValid = false;
// DB 에서 userName 을 이용해서 select 되는 정보가 있는지 select 해 본다
UserDto dto = new UserDao().getByUserName(userName);
// 만일 select 된 정보가 있다면 (최소한 userName 은 존재한다는 것)
if(dto != null){
// raw 비밀번호와 DB 에 저장된 암호화된 비밀번호를 비교해서 일치하는지 확인
// BCrypt.checkpw(입력한 비밀번호, 암호화된 비밀번호)
isValid = BCrypt.checkpw(password, dto.getPassword());
}
/* 만일 입력한 아이디와 비밀번호가 유효한 정보라면 로그인 처리를 한다
jsp 에서 기본 제공해주는 HttpSession 객체에 userName 을 저장
HttpSession 객체에 저장하면 응답을 한 이후에 다음번 요청에서 HttpSession 객체에 저장된 정보를 읽어올 수 있다
세션 객체에 담긴 정보는 어떤 요청도 하지 않고 일정 시간이 흐르면 자동 삭제된다
필요하다면 (로그아웃이 요청되면) 강제로 세션 객체에 담긴 정보를 삭제할 수도 있다
세션 객체에 담긴 로그인 정보를 삭제하는 것이 "로그아웃"
세션 객체에 로그인 정보(userName)를 저장하는 것이 "로그인"
*/
if(isValid){
// HttpSession 객체에 "userName" 이라는 키값으로 userName 을 저장
session.setAttribute("userName", userName);
// 세션 유지시간 설정 (초단위)
session.setMaxInactiveInterval(60*10); // 설정하지 않으면 기본 30분
}
%>
<div class="container">
<%if(isValid) {%>
<p>
<strong><%=userName%></strong> 회원님 로그인 되었습니다
</p>
<%} else {%>
<p>
아이디 혹은 비밀번호가 틀립니다
<a href="loginform.jsp">다시 로그인</a>
</p>
<%} %>
</div>
<%
// 세션에 "userName" 이라는 키값으로 저장된 값이 있는지 읽어와 본다
String userName = (String)session.getAttribute("userName");
%>
<%if(userName != null) {%>
<strong><%=userName%></strong> 님 로그인 중
<%} %>