CSS
#one{
border: 1px solid red;
display: inline;
margin:10px; /*좌우 margin만 적용*/
padding:10px; /*좌우 패딩만 효과가 있다*/
}
#two{
border: 1px solid blue;
display: inline-block;
margin:10px;
padding:10px;
}
position : relative, absolute, fixed, sticky 중 하나가 설정된 요소에서만 적용#one{
position:relative;
top: 100px;
left: 100px;
z-index: 10;
}
#two{
top: 150px;
left: 150px;
z-index: 9;
}
#three{
top: 200px;
left: 200px;
z-index: 8;
}
normal
italic : 기울림꼴
Times New Roman | Consolas | NanumGothic | Gothic | serif | sans-serif
normal | bold
16px#one{
font-size:20px;
}
none | underline | overline | line-through
➜ line :글자를 통과하는 선
#one{
color:red;
}
none
uppercase : 대문자
lowercase : 소문자
capitalize : 첫 글자를 대문자
#one{
letter-spacing:5px;
}
#one{
line-height:30px;
}
#one{
text-indent:100px;
}
left | center | right
justify : 늘리기 = 줄의 양 끝에 맞추가 위해 단어 간격을 자동 조절
#one{
text-align:left;
}
#two{
text-align: center;
}
#three{
text-align: right;
}
⭐ inline or inline-block 요소의 가운데 정렬에도 활용 가능
.wrapper{
text-align:center;
}
.box{
width: 100px;
height: 100px;
background-color: yellow;
display: inline-block;
}
p{
background-color: red;
}
p{
background-color: yellow;
}
➜ 배경색 : yellow
가중치 비교
li{ background-color: yellow; } ➜ 1점
ul li{ background-color: blue; } ➜ 2점
.active{ background-color: green; } ➜ 10점
li.active{ background-color: red; } ➜ 11점
#one{ background-color: pink; } ➜ 100점
#one{ background-color: white !important; } ➜ 10000점
⭐ 구체적일수록 가중치가 크다
visibility : hidden : 요소가 보이지 않지만 공간 차지
display : none : 요소가 화면에서 사라지고 공간 차지 X
1.0 : 완전 불투명 (기본값)
0.5 : 반투명 (배경이 살짝 비침)
0.0 : 완전 투명 (안 보인다)
visible | hidden | scroll
auto : 넘치면 스크롤바를 보이게 한다
방향 설정
overflow-x : x 방향으로 넘친 걸 처리
overflow-y : y 방향으로 넘친 걸 처리
overflow : 양쪽 모두 처리
jsp
String userName = (String)session.getAttribute("userName");
➜ session.getAttribute() 는 object type 을 반환하기에 String type 으로 casting 필요
<li><a href="${pageContext.request.contextPath}/user/loginform.jsp">로그인</a></li>
<li><a href="${pageContext.request.contextPath}/user/logout.jsp">로그아웃</a></li>
<%
// 세션에 저장된 값을 삭제하면 로그아웃
// "userName" 이라는 키값으로 저장된 값을 삭제
session.removeAttribute("userName");
// 응답하기
%>
<script>
alert("로그아웃 완료");
location.href = "${pageContext.request.contextPath}/";
</script>
➜ / : 최상위 경로 요청

웹 어플리케이션이 클라이언트의 요청을 가로채서 전후 처리를 할 수 있게하는 기능
/member/* 경로로 들어오는 모든 요청을 필터링
@WebFilter("/member/*") // "/member/" 회원의 모든 요청에 대해서 필터링하겠다
public class MyFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("My Filter 동작");
// 요청의 흐름 이어가기
chain.doFilter(request, response);
}
}

// 들어오는 모든 요청에 대해서 필터링 하겠다는 의미
@WebFilter("/*")
public class SecurityFilter implements Filter{
// 로그인 없이 접근 가능한 경로 목록
Set<String> whiteList = Set.of(
"/index.jsp",
"/user/loginform.jsp", "/user/login.jsp",
"/user/signup-form.jsp", "/user/signup.jsp",
"/images/"
);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("필터 수행됨");
// 로그인을 했는지 확인 작업
// 부모 type 을 자식 type 으로 casting
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
// HttpSession 객체의 참조값 얻어내기
HttpSession session = req.getSession();
// context path
String cPath = req.getContextPath();
// 클라이언트의 요청 경로 얻어내기
String uri = req.getRequestURI();
// uri 에서 context path 를 제거한 순수 경로를 얻어낸다
String path = uri.substring(cPath.length());
System.out.println(path);
// 로그인 없이 접근 가능한 요청 경로면 필터링을 하지 않는다
if(isWhiteList(path)) {
chain.doFilter(request, response);
return; // 메소드를 여기서 종료하기
}
// 로그인 여부 확인
String userName = (String)session.getAttribute("userName");
// 만일 로그인을 하지 않았다면
if(userName == null) {
// 로그인 페이지로 redirect (새로운 경로로 요청을 다시 하라고 응답) 이동시킨다
// query 문자열이 있으면 읽어와서
String query = req.getQueryString();
// 인코딩을 한 다음
String encodedUrl = query == null ? URLEncoder.encode(uri, "UTF-8")
: URLEncoder.encode(uri + "?" + query, "UTF-8");
// Redirect 되는 경로 뒤에 url 이라는 파라미터명으로 전달
res.sendRedirect(req.getContextPath() + "/user/loginform.jsp?url=" + encodedUrl);
return; // 메소드를 여기서 끝내기
}
chain.doFilter(request, response);
}
// 화이트리스트 검사
private boolean isWhiteList(String path) {
// 만일 최상위 경로 요청이면 허용
if ("/".equals(path)) return true;
// 반복문 돌면서 모든 WhiteList 를 불러내서
for (String prefix : whiteList) {
// 현재 요청경로와 대조한다
if (path.startsWith(prefix)) {
System.out.println(path);
return true;
}
}
return false;
}
}
⭐ Redirect 다시 요청하라고 새로운 경로를 준다

// 로그인 여부 확인
String userName = (String)session.getAttribute("userName");
// 만일 로그인을 하지 않았다면
if(userName == null) {
// 로그인 페이지로 redirect (새로운 경로로 요청을 다시 하라고 응답) 이동시킨다
// query 문자열이 있으면 읽어와서
String query = req.getQueryString();
// 인코딩을 한 다음
String encodedUrl = query == null ? URLEncoder.encode(uri, "UTF-8")
: URLEncoder.encode(uri + "?" + query, "UTF-8");
// Redirect 되는 경로 뒤에 url 이라는 파라미터명으로 전달
res.sendRedirect(req.getContextPath() + "/user/loginform.jsp?url=" + encodedUrl);
}
chain.doFilter(request, response);
}
⭐ url = /shop/buy.jsp?num=1&amount=2&xxx...
<%
// GET 방식 파라미터 url 이라는 이름으로 전달되는 값이 있는지 읽어와 본다
String url = request.getParameter("url");
// 만일 넘어오는 값이 없다면
if(url == null){
// 로그인 후에 인덱스 페이지로 갈 수 있도록 한다
String cPath = request.getContextPath();
url = cPath+"/index.jsp";
}
%>
<%-- 로그인 성공 후에 이동할 url 정보를 추가로 form 전송되도록 한다 --%>
<input type="hidden" name="url" value="<%=url%>"/>
➜ 로그인 후 이동할 원래 요청 경로가 hidden input 으로 form에 담긴다
<input type="hidden" name="url" value="/Step02DataBase/book/list.jsp"/>
// 로그인 후에 가야 할 목적지 정보
String url = request.getParameter("url");
// 로그인 실패를 대비해서 목적지 정보를 인코딩한 결과도 준비한다
String encodedUrl = URLEncoder.encode(url, "UTF-8");
<div class="container">
<%if(isValid) {%>
<p>
<strong><%=userName%></strong> 회원님 로그인 되었습니다
<a href="<%=url %>">확인</a>
</p>
<%} else {%>
<p>
아이디 혹은 비밀번호가 틀립니다
<a href="loginform.jsp?url=<%=encodedUrl %>">다시 로그인</a>
</p>
<%} %>
</div>
<%if(userName != null) {%>
<a href="${pageContext.request.contextPath}/user/info.jsp"><%=userName %></a> 님 로그인 중
<%} %>
<%
// 세션에 저장된 userName 을 읽어온다
String userName = (String)session.getAttribute("userName");
// DB 에서 사용자 정보를 읽어온다
UserDto dto = new UserDao().getByUserName(userName);
%>
<div class="container">
<h1>회원 가입 정보</h1>
<table class="table table-bordered table-striped">
<tr>
<th>아이디</th>
<td><%=dto.getUserName()%></td>
</tr>
<tr>
<th>비밀번호</th>
<td>
<a href="edit-password.jsp">수정하기</a>
</td>
</tr>
<tr>
<th>이메일</th>
<td><%=dto.getEmail()%></td>
</tr>
<tr>
<th>프로필 이미지</th>
<td>
<i style="font-size:50px;" class="bi bi-person-circle"></i>
</td>
</tr>
<tr>
<th>최종 수정 날짜</th>
<td><%=dto.getUpdateAt()%></td>
</tr>
<tr>
<th>가입 날짜</th>
<td><%=dto.getCreatedAt()%></td>
</tr>
</table>
<a href="edit.jsp">개인 정보 수정(이메일, 프로필 사진)</a>
</div>
<div class="container">
<h1>비밀번호 수정 양식</h1>
<form action="update-password.jsp" method="post" id="editForm">
<div>
<label for="password">기존 비밀번호</label>
<input type="text" name="password" id="password" />
</div>
<div>
<label for="newPassword">새 비밀번호</label>
<input type="text" name="newPassword" id="newPassword" />
</div>
<div>
<label for="newPassword2">새 비밀번호 확인</label>
<input type="text" id="newPassword2" />
</div>
<button type="submit">수정하기</button>
</form>
</div>
<script>
// id 가 editForm 인 요소에 "submit" 이벤트가 일어났을 때 실행할 함수 등록
// form 안에 있는 submit 버튼을 누르면 해당 form 에는 "submit" 이벤트가 발생
document.querySelector("#editForm").addEventListener("submit", (e)=>{
/*
여기서 해야할 일
- 폼에 입력한 내용의 유효성을 검증한다
- 검증해서 유효하다면(잘 입력했다면) 관여하지 않는다(자동으로 폼이 제출된다)
- 유효하지 않다면 e.preventDefault(); 를 실행해서 폼 제출을 막아준다
*/
// 기존 비밀번호
const pwd = document.querySelector("#password").value;
// 새 비밀번호
const newPwd = document.querySelector("#newPassword").value;
// 새 비밀번호 확인
const newPwd2 = document.querySelector("#newPassword2").value;
if(pwd.trim() == ""){ // 문자열에서 공백 제거 (좌우 공백) 해서 비교
alert("기존 비밀번호를 입력하세요");
e.preventDefault();
} else if(newPwd.trim() == ""){
alert("새 비밀번호를 입력하세요");
e.preventDefault();
} else if(newPwd.trim() != newPwd2.trim()){
alert("새 비밀번호를 확인란과 동일하게 입력해주세요");
e.preventDefault();
}
})
</script>
<%
// 1. 폼 전송되는 기존 비밀번호와 새 비밀번호를 읽어온다
String password = request.getParameter("password");
String newPassword = request.getParameter("newPassword");
// 2. 세션에 저장된 userName 을 이용해서 가입정보를 DB 에서 불러온다
String userName = (String)session.getAttribute("userName");
UserDto dto = new UserDao().getByUserName(userName);
// 3. 기존 비밀번호와 DB 에 저장된 비밀번호가 일치하는지 확인해서
boolean isValid = BCrypt.checkpw(password, dto.getPassword());
// 4. 일치한다면 새 비밀번호를 DB 에 수정 반영하고 로그아웃한다
if(isValid){
// 새 비밀번호를 암호화 한다
String encodedPwd = BCrypt.hashpw(newPassword, BCrypt.gensalt());
// dto 에 담고
dto.setPassword(encodedPwd);
// DB 에 수정 반영
new UserDao().updatePassword(dto);
// 로그아웃
session.removeAttribute("userName");
}
// 5. 일치하지 않는다면 에러 정보를 응답하고 다시 입력할 수 있도록 한다
%>
<div class="container">
<%if(isValid) {%>
<p>
<strong><%=userName%></strong> 님의 비밀번호가 수정되고 로그아웃 되었습니다
<a href="loginform.jsp?url=${pageContext.request.contextPath}/user/info.jsp">다시 로그인</a>
</p>
<%} else {%>
<p>
기존 비밀 번호가 일치하지 않습니다. 다시 입력해 주세요.
<a href="edit-password.jsp">확인</a>
</p>
<%} %>
</div>
update 메소드 추가// 비밀번호를 수정 반영하는 메소드
public boolean updatePassword(UserDto dto) {
Connection conn = null;
PreparedStatement pstmt = null;
int rowCount = 0;
try {
conn = new DbcpBean().getConn();
String sql = """
UPDATE users
SET password = ?, updatedAt = SYSDATE
WHERE userName = ?
""";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, dto.getPassword());
pstmt.setString(2, dto.getUserName());
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 = (String)session.getAttribute("userName");
UserDto dto = new UserDao().getByUserName(userName);
%>
<div class="container">
<h1>가입정보 수정 양식</h1>
<%-- input type = "file" 이 있는 form 의 전송 방식은 다르다
따라서 enctype = "multipart/form-data" 속성을 form 에 추가한다
서버에서 해당 요청을 처리하는 방법도 다르기 때문에 jsp 가 아닌 서블릿에서 처리를 하자
--%>
<form action="${pageContext.request.contextPath}/user/update" method="post"
enctype="multipart/form-data">
<div>
<label for="userName">아이디</label>
<input type="text" name="userName" value="<%=dto.getUserName()%>" readonly/>
</div>
<div>
<label for="email">이메일</label>
<input type="text" name="email" value="<%=dto.getEmail()%>"/>
</div>
<div>
<label>프로필 이미지</label>
<div>
<%if(dto.getProfileImage() == null) {%>
<i style="font-size:50px;" class="bi bi-person-circle"></i>
<%} else {%>
<%-- 다음 시간에! --%>
<%} %>
</div>
<input type="file" name="profileImage" accept="image/*" />
</div>
<button type="submit">수정 확인</button>
<button type="reset">취소</button>
</form>
</div>
⚠️ ProfileImage 부분은 새로운 개념이 나오기에 다음 시간에..