<14:30 5교시>
edit.html 내가 했던거는 다른거는 다 잘했는데 빨간줄 부분을 안고치고 있었음.
타임리프에서는 $가 해석될까봐 걱정할 필요는 없다.
쌤이 보내준거
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>/user/edit.html</title>
<style>
#profileImage{
width: 100px;
height: 100px;
border: 1px solid #cecece;
border-radius: 50%;
}
#profileFile{
display: none;
}
</style>
</head>
<body>
<div class="container">
<h3>회원 정보 수정 양식</h3>
<form th:action="@{/user/update}"
method="post" id="myForm" enctype="multipart/form-data">
<input type="file" name="profileFile" id="profileFile" accept="image/*"/>
<div>
<label for="id">아이디</label>
<input type="text" id="id" th:value="${dto.userName}" readonly/>
</div>
<div>
<label for="email">이메일</label>
<input type="text" id="email" name="email" th:value="${dto.email}"/>
</div>
<div>
<label>프로필 이미지</label>
<div>
<a href="javascript:" id="profileLink">
<th:block th:if="${dto.profileImage == null}">
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill="currentColor" class="bi bi-person-circle" viewBox="0 0 16 16">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
</svg>
</th:block>
<th:block th:unless="${dto.profileImage == null}">
<img id="profileImage" th:src="@{/upload/{name}(name=${dto.profileImage})}" alt="프로필 이미지" />
</th:block>
</a>
</div>
</div>
<button type="submit">수정확인</button>
<button type="reset">취소</button>
</form>
</div>
<script>
//프로필이미지 출력란에 원래 있었던 html 을 변수에 담아 두었다가
const saved=document.querySelector("#profileLink").innerHTML;
//취소 버튼을 누르면
document.querySelector("#myForm").addEventListener("reset", ()=>{
//변수에 담아둔 내용을 이용해서 원상 복구 시킨다.
document.querySelector("#profileLink").innerHTML=saved;
});
//링크를 클릭했을때
document.querySelector("#profileLink").addEventListener("click", ()=>{
// input type="file" 요소를 강제 클릭해서 파일 선택 창을 띄운다.
document.querySelector("#profileFile").click();
});
//새로운 이미지가 선택되었을때
document.querySelector("#profileFile").addEventListener("change", (e)=>{
console.log("hi")
//선택된 파일 배열 객체를 얻어낸다.
const files = e.target.files;
//만일 파일 데이터가 존재한다면
if(files.length > 0){
//파일로 부터 데이터를 읽어들일 객체 생성
const reader=new FileReader();
//로딩이 완료(파일데이터를 모드 읽었을때) 되었을때 실행할 함수 등록
reader.onload=(event)=>{
//읽은 파일 데이터 얻어내기
const data=event.target.result;
const img=`<img src="${data}" id="profileImage" alt="프로필이미지">`;
document.querySelector("#profileLink").innerHTML=img;
};
//파일을 DataURL 형식의 문자열로 읽어들이기
reader.readAsDataURL(files[0]);
}
});
</script>
</body>
</html>
custom.properties 에는 파일 업로드 위치 정보를 가지고 있는데, 이걸 메인메소드가 있는 클래스에 로딩시켜주기 위해 @PropertySource 어노테이션을 붙여준다.
동적 쿼리문 구성하기 & 내가 생각도 못한 updateAt 도 같이 update 시켜줘야 함!
글구 다시 한번 DB에서는 대소문자를 가리지 않는다. 보기 좋으라고 카멜케이스.
프로필 이미지도 수정됨. 메일도 수정됨.
<15:30 6교시>
글목록 만들기
밑작업
<16:30 7교시>
sql로 저장해두고
include로 불러다 쓴다.
이런 동적 sql 문 작성이 편해서 myBatis를 사용하는 것이다, 를 체감중.
어디서든, 글 목록의 페이징 처리를 할 수 있는 형태의 서비스. 반드시 PostController에만 의존된 것은 아니다.
@RequestParam 은 pageNum이 파라미터로 넘어오지 않을 때의 default 값을 1로 설정하기 위해서 작성한 내용이고,
검색한 키워드도 파라미터로 넘어오게 되면 PostDto에는 condition과 keyword가 null이 아니다.
검색한 키워드가 넘어오지 않으면 PostDto의 condition과 keyword가 null이다.
그 결과, 우리가 jsp에서 했던 글목록의 페이징 처리 로직이 컨트롤러와 서비스로 나뉘어 있다.
페이징 처리를 편하게 하기 위해 PostListDto를 새롭게 만듦.
PostService 에서도 getPost를 PostList를 리턴하도록 변경
<17:30 8교시>
쌤 코드
package com.example.spring10.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.spring10.dto.PostDto;
import com.example.spring10.dto.PostListDto;
import com.example.spring10.repository.PostDao;
@Service
public class PostServiceImpl implements PostService{
//한 페이지에 몇개씩 표시할 것인지
final int PAGE_ROW_COUNT=5;
//하단 페이지를 몇개씩 표시할 것인지
final int PAGE_DISPLAY_COUNT=5;
@Autowired private PostDao postDao;
/*
* pageNum 과 검색 조건, 키워드가 담겨 있을수 있는 PostDto 를 전달하면
* 해당 글 목록을 리턴하는 메소드
*/
@Override
public PostListDto getPosts(int pageNum, PostDto search) {
//보여줄 페이지의 시작 ROWNUM
int startRowNum=1+(pageNum-1)*PAGE_ROW_COUNT;
//보여줄 페이지의 끝 ROWNUM
int endRowNum=pageNum*PAGE_ROW_COUNT;
//하단 시작 페이지 번호
int startPageNum = 1 + ((pageNum-1)/PAGE_DISPLAY_COUNT)*PAGE_DISPLAY_COUNT;
//하단 끝 페이지 번호
int endPageNum=startPageNum+PAGE_DISPLAY_COUNT-1;
//전체 글의 갯수
int totalRow=postDao.getCount(search);
//전체 페이지의 갯수 구하기
int totalPageCount=(int)Math.ceil(totalRow/(double)PAGE_ROW_COUNT);
//끝 페이지 번호가 이미 전체 페이지 갯수보다 크게 계산되었다면 잘못된 값이다.
if(endPageNum > totalPageCount){
endPageNum=totalPageCount; //보정해 준다.
}
// startRowNum 과 endRowNum 을 PostDto 객체에 담아서
search.setStartRowNum(startRowNum);
search.setEndRowNum(endRowNum);
//글 목록 얻어오기
List<PostDto> list=postDao.getList(search);
String findQuery="";
if(search.getKeyword() != null) {
findQuery="&keyword="+search.getKeyword()+"&condition="+search.getCondition();
}
//글 목록 페이지에서 필요한 정보를 모두 PostListDto 에 담는다.
PostListDto dto=PostListDto.builder()
.list(list)
.startPageNum(startPageNum)
.endPageNum(endPageNum)
.totalPageCount(totalPageCount)
.pageNum(endPageNum)
.totalRow(totalRow)
.findQuery(findQuery)
.condition(search.getCondition())
.keyword(search.getKeyword())
.build();
return dto;
}
@Override
public void createPost(PostDto dto) {
}
}
이번에는 PostListDto를 사용해서 글 목록을 뿌려주기.
일단 내가 한데까지 백업하고 가려고
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="container">
<a th:href="@{/post/new}">새글 작성</a>
<h1>게시글 목록 입니다</h1>
<table class="table table-striped">
<thead class="table-dark">
<tr>
<th>번호</th>
<th>작성자</th>
<th>제목</th>
<th>조회수</th>
<th>등록일</th>
</tr>
</thead>
<tbody>
<tr th:each="tmp: ${dto.list}">
<td>${dto.list.num}</td>
<td>${dto.list.writer}</td>
<td>
<a th:href="#">${dto.list.title}</a>
</td>
<td>${dto.list.viewCount}</td>
<td>${dto.list.createdAt}</td>
</tr>
</tbody>
</table>
<nav>
<ul class="pagination">
<!-- Prev 버튼 -->
<li th:if="${dto.startPageNum} !=1" class="page-item">
<a class="page-link" th:href="@{/post/list{pageNum}(pageNum=${dto.startPageNum - 1}${dto.findQuery})}">Prev</a>
</li>
<!-- 페이지 번호 -->
<th:block th:each="${dto.startPageNum}, ${dto.endPageNum}, i">
<li class="page-item ${i==pageNum? 'active':''}">
<a th:href="@{/post/list}(pageNum=${i}${findQuery})">${i}</a>
</li>
</th:block>
<!-- Next 버튼 -->
<li th:if="${dto.endPageNum<dto.totalPageCount}" class="page-item">
<a class="page-link" th:href="@{/post/list{pageNum}(pageNum=${endPageNum+1}${findQuery})}">Next</a>
</li>
</ul>
</nav>
<form action="@{/post/list}" method="get">
<label for="condition">검색조건</label>
<select name="condition" id="condition">
<option value="title_content" th:selected="${dto.condition == 'title_content' ? 'selected' : ''}">제목 + 내용</option>
<option value="title" th:selected="${dto.condition == 'title' ? 'selected' : ''}">제목</option>
<option value="writer" th:selected="${dto.condition == 'writer' ? 'selected' : ''}">작성자</option>
</select>
<input type="text" name="keyword" placeholder="검색어..." value="${dto.keyword }"/>
<button class="btn btn-primary btn-sm" type="submit">검색</button>
<a class="btn btn-success btn-sm" th:href="@{/post/list}">새로고침</a>
</form>
<th:block th:if="dto.keyword !=null">
<p>
<strong>${dto.totalRow}</strong>개의 자료가 검색되었습니다.
</p>
</th:block>
</div>
</body>
</html>
샘이 참고하라고 코드 보내놔주셨는데 몇개만 봤고 나머진 아직 안봤음
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>/post/list.html</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<a th:href="@{/post/new}">새글 작성</a>
<h1>게시글 목록 입니다</h1>
<table class="table table-striped">
<thead class="table-dark">
<tr>
<th>번호</th>
<th>작성자</th>
<th>제목</th>
<th>조회수</th>
<th>등록일</th>
</tr>
</thead>
<tbody>
<tr th:each="post : ${dto.list}">
<td>[[${post.num}]]</td>
<td>[[${post.writer}]]</td>
<td>
<a th:href="@{|view?num=${post.num}${dto.findQuery}|}">[[${post.title}]]</a>
</td>
<td>[[${post.viewCount}]]</td>
<td>[[${post.createdAt}]]</td>
</tr>
</tbody>
</table>
<nav>
<ul class="pagination" th:if="${dto.totalPageCount ne 0}">
<li th:if="${dto.startPageNum ne 1}" class="page-item">
<a class="page-link" th:href="@{|/post/list?pageNum=${dto.startPageNum - 1}${dto.findQuery}|}">Prev</a>
</li>
<li th:each="i : ${#numbers.sequence(dto.startPageNum, dto.endPageNum)}" class="page-item" th:classappend="${i} == ${dto.pageNum} ? 'active'">
<a class="page-link" th:href="@{|/post/list?pageNum=${i}${dto.findQuery}|}">[[${i}]]</a>
</li>
<li th:if="${dto.endPageNum lt dto.totalPageCount}" class="page-item">
<a class="page-link" th:href="@{|/post/list?pageNum=${dto.endPageNum + 1}${dto.findQuery}|}">Next</a>
</li>
</ul>
</nav>
<!-- 검색 폼 -->
<form th:action="@{/post/list}" method="get">
<label for="condition">검색조건</label>
<select name="condition" id="condition">
<option value="title_content" th:selected="${dto.condition eq 'title_content'}">제목 + 내용</option>
<option value="title" th:selected="${dto.condition eq 'title'}">제목</option>
<option value="writer" th:selected="${dto.condition eq 'writer'}">작성자</option>
</select>
<input type="text" name="keyword" placeholder="검색어..." th:value="${dto.keyword}"/>
<button class="btn btn-primary btn-sm" type="submit">검색</button>
<a th:href="@{/post/list}" class="btn btn-success btn-sm">새로고침</a>
</form>
<p th:if="${not #strings.isEmpty(dto.keyword)}">
<strong th:text="${dto.totalRow}"></strong> 개의 글이 검색 되었습니다
</p>
</div>
</body>
</html>
'자바풀스택 과정 > 자바 풀 스택 : 수업내용정리' 카테고리의 다른 글
자바 풀 스택 2/25 오후 기록 062-2 (0) | 2025.02.25 |
---|---|
자바 풀 스택 2/25 오전 기록 062-1 (0) | 2025.02.25 |
자바 풀 스택 2/24 오전 기록 061-1 (0) | 2025.02.24 |
자바 풀 스택 2/21 오후 기록 060-2 (0) | 2025.02.21 |
자바 풀 스택 2/21 오전 기록 060-1 (0) | 2025.02.21 |