오늘의 할일 : 게시물 리스트 가져오기 및 이미지 슬라이드 -> Controller 에서 가져와야 할것 (게시물 리스트 , 현재 로그인 유저 아이디 , 게시물을 작성한 유저의 정보, 각 게시물에 해당하는 이미지)
member = memberRepository.findByUserId(username) - 현재 로그인한 유저의 정보를 가져옴
List boards = boardRepository.findAll() - 현재 등록된 모든 게실물 가져오기
Map<Long,Member> boardMembers = new HashMap<>() - 게시물을 작성한 유저의 정보를 가져옴
Map<Long, List> boardImages = new HashMap<>() - 게시물에 해당하는 이미지 값을 가져옴
Map으로 원하는 정보를 put으로 내보내줘서 Model로 만들어준다
Member writer = memberRepository.findByIdx(useridx) 일단 member DB에서 board안에 저장된 useridx 값을 가져와서 비교 해서 있으면 -> boardMembers.put(board.getBoardIdx(), writer) 그 정보값을 put으로 model로 넣어준다
- 나머지 Map도 같은 형식으로 넣어줬다
- Repository에서 Long 값으로 하나더 선언 해준다 기존 findById는 Integer값이여서 새로 선언 해줬다 idx 값이 다르면 하나더 만들어주는게 좋다
// 메인페이지에서 게실물 작성하기
@GetMapping("/")
public String index(Model model,Principal principal) {
Member member = null;
if (principal != null) { // 사용자가 로그인한 경우
String username = principal.getName();
member = memberRepository.findByUserId(username)
.orElseThrow(() -> new RuntimeException("유저 정보를 찾을 수 없습니다"));
}
List<Board> boards = boardRepository.findAll();
// 게시물을 작성한 유저의 정보를 가져오기
Map<Long,Member> boardMembers = new HashMap<>();
Map<Long, List<String>> boardImages = new HashMap<>();
for (Board board : boards){
Long useridx = board.getBoardUseridx();
Member writer = memberRepository.findByIdx(useridx).orElseThrow(() -> new RuntimeException("게지물 작성자를 찾을 수 없습니다"));
boardMembers.put(board.getBoardIdx(), writer);
List<String> images = new ArrayList<>();
if (board.getBoardImages() != null && !board.getBoardImages().isEmpty()) {
images = Arrays.asList(board.getBoardImages().split(","));
}
boardImages.put(board.getBoardIdx(), images);
}
model.addAttribute("member", member); // 현재 로그인한 유저의 정보
model.addAttribute("boardList", boards); // 모든 게실물
model.addAttribute("writers", boardMembers); // 게시물 작성자의 정보
model.addAttribute("boardImages", boardImages);
model.addAttribute("board", new Board()); // 새 게시물 작서용 객체
return "index";
}
th:if="{#authorization.expression('isAuthenticated()')}" , isAnonymous() 로 로그인 유무를 체크 th:each ="boards : ${boardList}" 로 게시물 반복을 해준다 <br> "{writers[boards.boardIdx].userName" 으로 게시물을 작성한 유저의 정보를 가저온다
<div class="post-create">
<div th:if="${#authorization.expression('isAuthenticated()')}">
<form th:action="@{/board}" method="post" th:object="${board}" enctype="multipart/form-data">
<div style="display: flex; align-items: center;">
<img class="profile-img" th:src="@{${'/profile-images/' + member.userImage}}" alt="프로필">
</div>
<div>
<input type="text" th:field="*{boardContent}" placeholder="무엇을 작성하시겠습니까?">
</div>
<!-- 이미지 미리보기 슬라이드 -->
<div class="image-slider">
<button type="button" class="slide-btn left">❮</button>
<div class="preview-container" id="preview-container"></div>
<button type="button" class="slide-btn right">❯</button>
</div>
<div class="buttons">
<input type="file" class="upload-btn" name="files" onchange="preView(this);" multiple accept="image/*" />
<button class="file-btn" onclick="ImgUpload(event)">이미지 선택</button>
<button class="submit-btn">작성 완료</button>
</div>
</form>
</div>
<div th:if="${#authorization.expression('isAnonymous()')}" class="login-message">
<div style="display: flex; align-items: center;">
<img class="profile-img" th:src="@{'/profile-images/' + basic.png}" alt="프로필">
</div>
<div>
<input type="text" placeholder="로그인 후 이용이 가능합니다">
</div>
<a th:href="@{/login}">로그인 페이지로 이동</a>
</div>
</div>
<!-- 게시글 목록 리스트 -->
<div class="post" th:each="boards : ${boardList}">
<div class="post-header">
<img class="profile-img" th:src="@{${'/profile-images/' + writers[boards.boardIdx].userImage}}" alt="프로필">
<div class="user-info">
<span class="user-name" th:text="${writers[boards.boardIdx].userName}"></span>
<span class="user-id" th:text="${'@' +writers[boards.boardIdx].userId}"></span>
</div>
<span class="time" th:data-time="${boards.boardAt}"></span>
</div>
<div class="post-content" th:text="${boards.boardContent}">
</div>
<div class="board-img-list">
<div class="board-img-list-inner">
<button class="board-slide-left">❮</button>
<div class="img-list-inner" th:each="imgUrl : ${boardImages.get(boards.boardIdx)}">
<img th:src="@{${'/board-images/' + imgUrl}}" alt="게시물 이미지">
</div>
<button class="board-slide-right">❯</button>
</div>
</div>
<div class="post-actions">
<span class="toggle-comments">💬 <span th:text="${boards.boardComent}"></span></span>
<span>❤️ <span th:text="${boards.boardLike}"></span></span>
<span>👀 <span th:text="${boards.boardView}"></span></span>
</div>
<!-- 댓글 창 달기 -->
<div class="comment-section" th:attr="data-board-id=${boards.boardIdx}">
<div class="comment-input" th:if="${#authorization.expression('isAuthenticated()')}">
<img class="profile-img" th:src="@{${'/profile-images/' + member.userImage}}" alt="프로필">
</div>
<div class="comment-font">
<input type="text" placeholder="무엇을 작성하시겠습니까?">
</div>
<!-- 이미지 미리보기 슬라이드 -->
<div class="image-slider comment-slider">
<button type="button" class="slide-btn left comment-left" >❮</button>
<div class="preview-container comment-container" id="coment-container"></div>
<button type="button" class="slide-btn right comment-right">❯</button>
</div>
<div class="comment-btn">
<input type="file" class="upload-btn commentupload-btn" name="commentfiles" onchange="preView(this);" multiple accept="image/*" />
<button class="file-btn" th:attr="data-board-id=${boards.boardIdx}" onclick="CommentImgUpload(event)">이미지 선택</button>
<button class="comment-submit">작성</button>
</div>
</div>
</div>
댓글 이미지 선택 댓글 이미지 선택을 클릭하면 ( 버튼값이다)가실행된다 이후 fileInput에 강제로 th:attr="data-board-id=${boards.boardIdx}" 이값을 저장한다 댓글에서 이미지를 넣었을 경우에만 해당
function CommentImgUpload(event) {
event.preventDefault();
const button = event.currentTarget; // 이미지 선택 버튼을 클릭
const boardId = button.dataset.boardId; // 클릭한 버튼의 data 값을 가져옴
// 더 안전하게: 버튼 기준으로 가장 가까운 comment-section 찾기
const commentSection = button.closest(".comment-section");// 버튼 기준으로 가장 가까운 .comment-section 찾기
const fileInput = commentSection.querySelector(".commentupload-btn"); // 해당 섹션 안의 input[type="file"] 찾기
fileInput.dataset.boardId = boardId; // fileInput에 data 값을 강제로 넣어줌
fileInput.click(); // input 클릭해서 파일 선택창 열기
}
아래의 HTML DIV에서 댓글 preView 이미지 선택을 누르면
<input type="file" class="upload-btn commentupload-btn" name="commentfiles" onchange="preView(this);" multiple accept="image/*" />
<button class="file-btn" th:attr="data-board-id=${boards.boardIdx}" onclick="CommentImgUpload(event)">이미지 선택</button>
CommentImgUpload 에서 받아온 boardId 값을 -> 아래의 preView
input.dataset.boardId(여기다가 저장) 해주고
document.querySelector(`.comment-section[data-board-id="${boardId}"] 해당 하는 값을 찾아준다
let postImages = []; // 게시물 이미지 배열
let commentImagesMap = {}; // key: boardId, value: 이미지 배열
// 게시물 및 댓글 이미지 프리뷰
function preView(input) {
const isComment = input.classList.contains("commentupload-btn");
const boardId = input.dataset.boardId;
let previewContainer;
if (isComment) {
previewContainer = document.querySelector(`.comment-section[data-board-id="${boardId}"] .comment-container`);
if (!commentImagesMap[boardId]) {
commentImagesMap[boardId] = []; // 초기화
}
} else {
previewContainer = document.getElementById("preview-container");
}
let images = isComment ? commentImagesMap[boardId] : postImages;
if (input.files.length + images.length > 10) {
alert("최대 10장까지만 업로드할 수 있습니다.");
return;
} -> 이미지는 10장 까지만
Array.from(input.files).forEach((file) => {
if (!file.type.startsWith("image/")) return;
images.push(file);
const reader = new FileReader();
reader.onload = function (e) {
const imgWrapper = document.createElement("div");
imgWrapper.classList.add("preview-wrapper");
const img = document.createElement("img");
img.src = e.target.result;
img.classList.add("preview-img");
const closeBtn = document.createElement("span");
closeBtn.classList.add("preview-close");
closeBtn.textContent = "X";
closeBtn.onclick = function () {
const currentIndex = images.indexOf(file);
removeImage(currentIndex, isComment, boardId); // ✨ boardId 넘기기
};
imgWrapper.appendChild(img);
imgWrapper.appendChild(closeBtn);
previewContainer.appendChild(imgWrapper);
if(isComment){
initComentSlider();
}else{
initBoardSlider();
}
};
reader.readAsDataURL(file);
});
updateFileInput(isComment, boardId); // ✨ boardId 넘기기
}
그리고 파일을 하나씩 추가해도 전에 파일이미지가 남아있게 해준다
//선택한 이미지를 input에 넣어줌 기존 파일을 유지해줌
function updateFileInput(isComment, boardId = null) {
const dataTransfer = new DataTransfer();
let images;
if (isComment) {
images = commentImagesMap[boardId] || [];
const container = document.querySelector(`.comment-section[data-board-id="${boardId}"]`);
const fileInput = container.querySelector(".commentupload-btn");
images.forEach(file => dataTransfer.items.add(file));
fileInput.files = dataTransfer.files;
} else {
images = postImages;
const fileInput = document.querySelector(".upload-btn");
images.forEach(file => dataTransfer.items.add(file));
fileInput.files = dataTransfer.files;
}
}
전체적인 게시물 이미지 슬라이드에 대한 구조 설명
만약 이미지가 5장이라면 해당하는 이미지 하나씩 div 크기를 정해준다 , 그리고 그 위에 div를 하나더 감싸서 실제로 보여지는 container 구조를 작성한다 그리고 안에 있는 이미지는 inline-flef로 일렬로 정렬해준다 당연 크기는 400px 으로 정해준후 overflow를 줘서 넘어가면 안보이게 이후 아래 그림은 400px , 400px 이라면 버튼에 따라서 left를 누르면 해당 container의 translate +400만큼 증가시켜주고 , right -400만큼 시켜줘서 밀어주면서 overflow:hidden 이기 때문에 보여지는 이미지가 밀리면서 보인다
게시물 이미지 슬라이드
게시물은 각 해당하는 하는 값들만 이미지 슬라이드를 해야 하기때문에 forEach로
전체 div board-img-list-innder 안에 있는 값을 하나씩 img-list-inner을 하나씩 찾아서
각 해당하는 값에 div를 translateX - 275씩 만큼 움직여준다
나머지 슬라이드도 비슷하게 작성
document.addEventListener("DOMContentLoaded", function () {
const sliders = document.querySelectorAll(".board-img-list-inner");
sliders.forEach(slider => {
const imgContainer = slider.querySelectorAll(".img-list-inner");
const images = Array.from(imgContainer);
const leftBtn = slider.querySelector(".board-slide-left");
const rightBtn = slider.querySelector(".board-slide-right");
let currentIndex = 0;
const imgWidth = 275; // 이미지 크기 (275px + 여백 25px 고려)
const maxIndex = images.length - 2; // ✅ 최대 이동 가능 인덱스
// 슬라이드 이동 함수
function updateSlide() {
const offset = -(currentIndex * imgWidth);
imgContainer.forEach(img => img.style.transform = `translateX(${offset}px)`);
// 왼쪽 버튼 제어
leftBtn.style.display = currentIndex > 0 ? 'block' : 'none';
// 오른쪽 버튼 제어
rightBtn.style.display = currentIndex >= maxIndex ? 'none' : 'block';
}
// ⬅️ 왼쪽 이동
leftBtn.addEventListener("click", function () {
if (currentIndex > 0) {
currentIndex--;
updateSlide();
}
});
// ➡️ 오른쪽 이동
rightBtn.addEventListener("click", function () {
if (currentIndex < maxIndex) {
currentIndex++;
updateSlide();
}
});
// 초기 슬라이드 상태 업데이트
updateSlide();
});
});
- 기존 게시물 이미지 슬라이드
- 게시물 작성 이미지 슬라이드
전체적인 구조 및 html