자바풀스택 과정/자바 풀 스택 : 수업내용정리

자바 풀 스택 2/24 오후 기록 061-2

파티피플지선 2025. 2. 24. 18:22

<14:30 5교시>

edit.html 내가 했던거는 다른거는 다 잘했는데 빨간줄 부분을 안고치고 있었음.

 

 

타임리프에서는 $가 해석될까봐 걱정할 필요는 없다.

원래 \${data}라고 했었음

 

더보기

쌤이 보내준거

<!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교시>

글목록 만들기

밑작업

 

 

 

 

 

간단해진 SQL 문

 

 

 

<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>