2024. 1. 26. 21:30ㆍProject/ShoppingMall
🟢 Entity
▪️ ImageEntity
공지테이블과 조인해주기
여러 개의 이미지를 1개의 공지글에 첨부 가능 → @ManyToOne
외래키 설정 → noticeId
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table(name = "image")
@SequenceGenerator(
name = "image_SEQ",
sequenceName = "image_SEQ",
initialValue = 1,
allocationSize = 1
)
public class ImageEntity extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator ="image_SEQ")
private Integer imageId;
@Column(name = "imageFile")
private String imageFile;
//대표이미지=0, 서브이미지=1, 상세이미지=2
@Column(name = "imageType")
private Integer imageType;
//외래키 (productId) - 상품테이블과 조인 (여러 개의 이미지가 하나의 상품에 매핑)
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
@JoinColumn(name = "productId")
private ProductEntity productEntity;
//외래키 (noticeId) - 공지테이블과 조인 (여러 개의 이미지가 하나의 공지 글에 매핑)
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
@JoinColumn(name = "noticeId")
private NoticeEntity noticeEntity;
}
//외래키 (noticeId) - 공지테이블과 조인 (여러 개의 이미지가 하나의 공지 글에 매핑)
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
@JoinColumn(name = "noticeId")
private NoticeEntity noticeEntity;
▪️ NoticeEntity
이미지테이블과 조인해주기
1개의 공지글에 여러 개의 이미지 첨부 가능 → @OneToMany
mappedBy = "noticeEntity" : 양방향 매핑을 의미. 관계의 주인(Owner)이 되는 쪽을 지정
ImageEntity 클래스 내에서 noticeEntity 필드를 사용하여 매핑
cascade = CascadeType.ALL : 모든 작업에 대해 Cascade를 수행하도록 설정
공지사항 엔터티가 삭제되면 관련된 모든 이미지 엔터티도 삭제
private List<ImageEntity> noticeImages = new ArrayList<>()
공지사항 엔터티에 대한 여러 이미지 엔터티를 담는 컬렉션
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table(name = "notice")
@SequenceGenerator(
name = "notice_SEQ",
sequenceName = "notice_SEQ",
initialValue = 1,
allocationSize = 1)
public class NoticeEntity extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "notice_SEQ")
private Integer noticeId;
//공지테이블과 매핑
//공지 게시글 1개에 여러 개의 이미지
@OneToMany(mappedBy = "noticeEntity", cascade = CascadeType.ALL)
private List<ImageEntity> noticeImages = new ArrayList<>();
@Column(name="noticeRole")
@Enumerated(EnumType.STRING)
private NoticeRole noticeRole;
@Column(name="noticeTitle",nullable = false,length = 50)
private String noticeTitle; //제목
@Column(name="noticeWriter", nullable = false,length = 20)
private String noticeWriter; //작성자
@Column(name="noticeContent", nullable = false,length = 2000)
private String noticeContent; //공지내용
@Column(name = "noticeView")
private Integer noticeView;
}
//공지테이블과 매핑
//공지 게시글 1개에 여러 개의 이미지
@OneToMany(mappedBy = "noticeEntity", cascade = CascadeType.ALL)
private List<ImageEntity> noticeImages = new ArrayList<>();
CascadeType.ALL: 모든 작업에 대해 Cascade를 수행
저장, 업데이트, 삭제 등 모든 상태 변화가 관련된 엔터티에 전파
CascadeType.PERSIST: 영속성 컨텍스트에 엔터티를 추가할 때만 Cascade를 수행
저장(INSERT) 작업에 대해서만 전파
CascadeType.MERGE: 병합 작업에 대해서만 Cascade를 수행
엔터티의 상태를 업데이트할 때만 전파
CascadeType.REMOVE: 삭제 작업에 대해서만 Cascade를 수행
부모 엔터티를 삭제할 때 자식 엔터티도 함께 삭제
CascadeType.REFRESH: 엔터티를 새로고침할 때 Cascade를 수행
🟢 DTO
▪️ ImageDTO
공지사항 외래키 추가 → noticeId
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ImageDTO {
private Integer imageId; // 이미지 고유번호
private String imageFile; // 이미지 파일이름
private Integer imageType; // 이미지 종류 (대표이미지=0, 서브이미지=1, 상세이미지=2)
private Integer productId; // 상품번호(외래키)
private Integer noticeId; // 공지번호(외래키)
}
private Integer noticeId; // 공지번호(외래키)
▪️ ImageDTO
공지사항 외래키 추가 → noticeId
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ImageDTO {
private Integer imageId; // 이미지 고유번호
private String imageFile; // 이미지 파일이름
private Integer imageType; // 이미지 종류 (대표이미지=0, 서브이미지=1, 상세이미지=2)
private Integer productId; // 상품번호(외래키)
private Integer noticeId; // 공지번호(외래키)
}
private Integer noticeId; // 공지번호(외래키)
🟢 Service
▪️ Config → WebMvcConfig
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//application에서 사용자 변수 읽어오기
@Value(("${uploadPath}"))
String uploadPath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/images/**")
.addResourceLocations(uploadPath); //자원의 위치
}
}
▪️ FileService
@Service
@RequiredArgsConstructor
public class FileService {
//파일을 저장할 경로
@Value("${imgLocation}")
private String imgLocation;
//저장할 경로,파일명,데이터 값
public String uploadFile(String originalFileName, byte[] filedata) throws Exception {
UUID uuid = UUID.randomUUID(); //문자열 생성
String extendsion = originalFileName.substring(originalFileName.lastIndexOf(".")); //문자열 분리
String saveFileName = uuid.toString()+extendsion; //새로운 파일명
String uploadFullurl = imgLocation+saveFileName; //저장위치 및 파일명
//하드디스크에 파일 저장
FileOutputStream fos = new FileOutputStream(uploadFullurl);
fos.write(filedata);
fos.close();
return saveFileName; //데이터베이스에 저장할 파일명
}
// 파일 삭제 (상품을 수정시 기존 파일을 삭제하고 새로운 파일을 저장)
public void deleteFile(String fileName) throws Exception {
String deleteFileName = imgLocation + fileName;
File deleteFile = new File(deleteFileName);
if (deleteFile.exists()) {
deleteFile.delete();
}
}
}
▪️ ImageService
이미지 파일을 업로드하고, 해당 이미지 정보를 데이터베이스의 이미지 테이블(ImageEntity)에 저장
@Service
@RequiredArgsConstructor
@Transactional
public class ImageService {
//파일업로드
private final FileService fileService;
private final ModelMapper modelMapper = new ModelMapper();
private final ImageRepository imageRepository;
// 공지테이블에 이미지 삽입 메서드
public void noticeImage(ImageDTO imageDTO, NoticeEntity noticeEntity, MultipartFile imageFile) throws Exception {
// 원본 파일명과 새로운 파일명 초기화
String originalFileName = imageFile.getOriginalFilename(); // 저장 할 파일명
String newFileName = ""; // UUID로 생성된 새로운 파일명
// 파일이 존재할 경우에만 업로드 진행
if (originalFileName != null && originalFileName.length() > 0) {
// 파일 업로드
newFileName = fileService.uploadFile(originalFileName, imageFile.getBytes());
}
// 새로운 파일명으로 변경된 이미지 파일명 설정
imageDTO.setImageFile(newFileName);
// DTO를 Entity로 변환
ImageEntity imageEntity = modelMapper.map(imageDTO, ImageEntity.class);
// 공지사항 테이블과 이미지 테이블 간의 관계 설정
imageEntity.setNoticeEntity(noticeEntity);
try {
// 이미지 엔터티를 저장
imageRepository.save(imageEntity);
} catch (Exception e) {
// 예외 처리 (생략된 부분)
}
}
▪️ NoticeService
글 목록을 불러올 때 이미지 정보도 함께 로드되어야함
그렇지 않으면 ModelMapper가 MultipartFile에 대한 매핑 오류를 발생
//이미지 관리 DTO
private List<ImageDTO> imageDTOs;
@Service
@RequiredArgsConstructor
@Transactional
public class NoticeService {
private final FileService fileService;
private final ImageService imageService;
private final NoticeRepository noticeRepository;
private final CommentRepository commentRepository;
private final ModelMapper modelMapper = new ModelMapper();
//공지등록
public void insert(NoticeDTO noticeDTO, List<MultipartFile> imageFiles) throws Exception {
//이미지 저장
//데이터베이스에 저장 할 이미지, 업로드한 실제 이미지
List<ImageDTO> dataDTO = noticeDTO.getImageDTOs();
List<MultipartFile> images = noticeDTO.getImages();
//공지사항 등록
NoticeEntity data = modelMapper.map(noticeDTO, NoticeEntity.class);
NoticeEntity noticeEntity = noticeRepository.save(data);
//공지에 이미지 저장
int index = 0;
for(MultipartFile file : images) {
ImageDTO imageDTO = dataDTO.get(index);
try {
//이미지 등록
imageService.noticeImage(imageDTO, noticeEntity, file);
} catch (IOException e) {
//
}
index++;
}
}
//공지목록 (관리자)
public Page<NoticeDTO> findAll(String type, String keyword, String noticeRole, Pageable page) throws Exception {
int currentpage = page.getPageNumber()-1;
int blockLimit = 10;
Pageable pageable = PageRequest.of(currentpage, blockLimit, Sort.by(Sort.Direction.DESC, "noticeId"));
Page<NoticeEntity> noticeEntityPage;
if("tc".equals(type) && keyword != null && !keyword.isEmpty()) {
noticeEntityPage = noticeRepository.findByNoticeTitleOrNoticeContent(keyword, pageable);
} else if("r".equals(type) && noticeRole != null && !noticeRole.isEmpty()) {
noticeEntityPage = noticeRepository.findByNoticeRole(NoticeRole.valueOf(noticeRole), pageable);
} else {
noticeEntityPage = noticeRepository.findAll(pageable);
}
return noticeEntityPage.map(noticeEntity -> NoticeDTO.builder()
.noticeId(noticeEntity.getNoticeId())
.noticeTitle(noticeEntity.getNoticeTitle())
.noticeWriter(noticeEntity.getNoticeWriter())
.noticeRole(noticeEntity.getNoticeRole())
.noticeContent(noticeEntity.getNoticeContent())
.noticeView(noticeEntity.getNoticeView())
.imageDTOs(noticeEntity.getNoticeImages().stream()
.map(imageEntity -> modelMapper.map(imageEntity, ImageDTO.class))
.collect(Collectors.toList()))
.reDate(noticeEntity.getReDate())
.modDate(noticeEntity.getModDate())
.build()
);
}
}
▪️ 메소드 분리해서 작성하는 방법
@Service
@RequiredArgsConstructor
@Transactional
public class NoticeService {
private final FileService fileService;
private final ImageService imageService;
private final NoticeRepository noticeRepository;
private final CommentRepository commentRepository;
private final ModelMapper modelMapper = new ModelMapper();
//공지등록
public void insert(NoticeDTO noticeDTO, List<MultipartFile> imageFiles) throws Exception {
//이미지 저장
//데이터베이스에 저장 할 이미지, 업로드한 실제 이미지
List<ImageDTO> dataDTO = noticeDTO.getImageDTOs();
List<MultipartFile> images = noticeDTO.getImages();
//공지사항 등록
NoticeEntity data = modelMapper.map(noticeDTO, NoticeEntity.class);
NoticeEntity noticeEntity = noticeRepository.save(data);
//공지에 이미지 저장
int index = 0;
for(MultipartFile file : images) {
ImageDTO imageDTO = dataDTO.get(index);
try {
//이미지 등록
imageService.noticeImage(imageDTO, noticeEntity, file);
} catch (IOException e) {
//
}
index++;
}
}
//이미지테이블 전달
private List<ImageDTO> mapImagesToDTOs(List<ImageEntity> imagesEntities) {
//이미지가 없는 경우, 빈 리스트를 반환
if (imagesEntities == null) {
return Collections.emptyList();
}
//이미지 목록이 null이 아닌 경우
//이미지 목록을 스트림으로 변환, map 함수를 사용하여 각 ImageEntity를 ImageDTO로 매핑
//collect(Collectors.toList())를 사용하여 스트림의 결과를 리스트로 수집
return imagesEntities.stream()
.map(imageEntity -> modelMapper.map(imageEntity, ImageDTO.class))
.collect(Collectors.toList());
}
//공지목록 (관리자)
public Page<NoticeDTO> findAll(String type, String keyword, String noticeRole, Pageable page) throws Exception {
int currentpage = page.getPageNumber()-1;
int blockLimit = 10;
Pageable pageable = PageRequest.of(currentpage, blockLimit, Sort.by(Sort.Direction.DESC, "noticeId"));
Page<NoticeEntity> noticeEntityPage;
if("tc".equals(type) && keyword != null && !keyword.isEmpty()) {
noticeEntityPage = noticeRepository.findByNoticeTitleOrNoticeContent(keyword, pageable);
} else if("r".equals(type) && noticeRole != null && !noticeRole.isEmpty()) {
noticeEntityPage = noticeRepository.findByNoticeRole(NoticeRole.valueOf(noticeRole), pageable);
} else {
noticeEntityPage = noticeRepository.findAll(pageable);
}
List<NoticeDTO> noticeDTOList = new ArrayList<>();
for(NoticeEntity noticeEntity : noticeEntityPage) {
NoticeDTO noticeDTO = NoticeDTO.builder()
.noticeId(noticeEntity.getNoticeId())
.noticeTitle(noticeEntity.getNoticeTitle())
.noticeWriter(noticeEntity.getNoticeWriter())
.noticeRole(noticeEntity.getNoticeRole())
.noticeContent(noticeEntity.getNoticeContent())
.noticeView(noticeEntity.getNoticeView())
.imageDTOs(mapImagesToDTOs(noticeEntity.getNoticeImages()))
.reDate(noticeEntity.getReDate())
.modDate(noticeEntity.getModDate())
.build();
noticeDTOList.add(noticeDTO);
}
return new PageImpl<>(noticeDTOList, pageable, noticeEntityPage.getTotalElements());
}
}
🟢 Contoroller
▪️ NoticeController
@RequestParam : HTTP 요청에서 특정한 이름의 파라미터를 찾아 해당 파라미터 값을 메서드 파라미터에 매핑하는 역할
List<MultipartFile> imageFiles : 여러 개의 파일을 받기 위한 자료구조
클라이언트에서 전송한 파일들이 이 리스트에 담겨옴
@Controller
@Transactional
@RequiredArgsConstructor
public class NoticeController {
private final NoticeService noticeService;
private final CommentService commentService;
//관리자
//공지등록폼
@GetMapping("/admin_noticeinsert")
public String noticeForm(Model model) throws Exception {
NoticeDTO noticeDTO = new NoticeDTO();
model.addAttribute("noticeDTO", noticeDTO);
model.addAttribute("noticeRole", NoticeRole.values());
return "admin/notice_insert";
}
//공지등록처리 (이미지 등록 처리)
@PostMapping("/admin_noticeinsert")
public String noticeProc(@Valid NoticeDTO noticeDTO,
BindingResult bindingResult,
@RequestParam("images") List<MultipartFile> imageFiles,
Model model) throws Exception {
if(bindingResult.hasErrors()) {
model.addAttribute("noticeRole", NoticeRole.values());
return "admin/notice_insert";
}
try {
noticeService.insert(noticeDTO, imageFiles);
model.addAttribute("success", "공지 등록이 완료되었습니다.");
model.addAttribute("searchUrl", "/admin_noticelist");
return "message";
} catch (Exception e) {
model.addAttribute("error", "공지 등록 실패! 다시 등록해주세요.");
model.addAttribute("searchUrl", "/admin_noticeinsert");
return "message";
}
}
//공지목록
@GetMapping("/admin_noticelist")
public String noticeList(@RequestParam(value = "type", defaultValue = "") String type,
@RequestParam(value = "keyword", defaultValue = "") String keyword,
@RequestParam(value = "noticeRole", defaultValue = "") String noticeRole,
@PageableDefault(page = 1) Pageable pageable,
Model model) throws Exception {
Page<NoticeDTO> noticeDTOS = noticeService.findAll(type, keyword, noticeRole, pageable);
int blockLimit = 5;
int startPage = (((int)(Math.ceil((double) pageable.getPageNumber() / blockLimit))) - 1) * blockLimit + 1;
int endPage = Math.min(startPage + blockLimit - 1, noticeDTOS.getTotalPages());
int prevPage = noticeDTOS.getNumber();
int currentPage = noticeDTOS.getNumber() + 1;
int nextPage = noticeDTOS.getNumber() + 2;
int lastPage = noticeDTOS.getTotalPages();
model.addAttribute("noticeDTOS", noticeDTOS);
model.addAttribute("startPage", startPage);
model.addAttribute("endPage", endPage);
model.addAttribute("prevPage", prevPage);
model.addAttribute("currentPage", currentPage);
model.addAttribute("nextPage", nextPage);
model.addAttribute("lastPage", lastPage);
model.addAttribute("type", type);
model.addAttribute("keyword", keyword);
return "/admin/notice_list";
}
}
▪️ Alert창
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Alert</title>
</head>
<body>
<script th:inline="javascript">
var message = [[${message}]];
var error = [[${error}]];
var success = [[${success}]];
console.log("Message:", message);
console.log("Error:", error);
console.log("Success", success);
var confirmationMessage = message || error || success;
if (confirmationMessage) {
var userConfirmed = confirm(confirmationMessage);
if (userConfirmed) {
console.log("확인되었습니다.");
location.replace([[${searchUrl}]]);
} else {
console.log("취소되었습니다.");
location.replace(history.go(-1));
}
}
</script>
</body>
</html>
🟢 insert
▪️ admin - Insert
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/adminlayout}">
<head>
<meta charset="utf-8">
<title>ADMIN - Notice Insert</title>
</head>
<body>
<div layout:fragment="content">
<div class="main-wrapper wrapper-2">
<div class="breadcrumb-area breadcrumb-padding-8">
<div class="container">
<div class="breadcrumb-content text-center">
<div class="breadcrumb-title">
<h2>Notice</h2>
</div>
</div>
</div>
</div>
<div class="notice-area bg-white pb-130">
<div class="container">
<div class="notice-info-wrap">
<form th:action="@{/admin_noticeinsert}" method="post" enctype="multipart/form-data" th:object="${noticeDTO}">
<input type="hidden" name="noticeView" value="0">
<table class="table notice-table">
<tbody>
<tr>
<th scope="row" class="notice-padding">제목</th>
<td class="notice-info">
<input type="text" name="noticeTitle" id="noticeTitle">
<span class="text-danger" th:if="${#fields.hasErrors('noticeTitle')}" th:errors="*{noticeTitle}"></span>
</td>
</tr>
<tr>
<th scope="row" class="notice-padding">작성자</th>
<td class="notice-info">
<input type="text" name="noticeWriter" id="noticeWriter">
<span class="text-danger" th:if="${#fields.hasErrors('noticeWriter')}" th:errors="*{noticeWriter}"></span>
</td>
</tr>
<tr>
<th scope="row" class="notice-padding">공지유형</th>
<td>
<div class="sidebar-widget update">
<div class="sidebar-archive-wrap">
<select name="noticeRole" id="noticeRole">
<option value="" th:selected="${noticeRole == ''}"> == 선택 == </option>
<option value="DELIVERY" th:selected="${noticeRole=='DELIVERY'}">배송공지</option>
<option value="PRODUCT" th:selected="${noticeRole=='PRODUCT'}">상품공지</option>
<option value="TIP" th:selected="${noticeRole=='TIP'}">청소꿀팁</option>
<option value="EVENT" th:selected="${noticeRole=='EVENT'}">이벤트</option>
<option value="POINT" th:selected="${noticeRole=='POINT'}">적립금</option>
<option value="ETC" th:selected="${noticeRole=='ETC'}">기타</option>
</select>
<span class="text-danger" th:if="${#fields.hasErrors('noticeRole')}" th:errors="*{noticeRole}"></span>
</div>
</div>
</td>
</tr>
<tr>
<th scope="row" class="notice-padding">공지내용</th>
<td class="notice-info">
<textarea rows="15" name="noticeContent" id="noticeContent"></textarea>
<span class="text-danger" th:if="${#fields.hasErrors('noticeContent')}" th:errors="*{noticeContent}"></span>
</td>
</tr>
<tr>
<th scope="row" class="notice-padding" id="imageDTOs[3]">이미지 1</th>
<td class="notice-info">
<input type="hidden" name="imageDTOs[3].imageType" value="3">
<input type="file" name="images" onchange="previewImage(this, 'previewImg3')">
<img id="previewImg3" class="img-thumbnail" style="max-width: 100px; max-height: 100px;">
</td>
</tr>
<tr>
<th scope="row" class="notice-padding" id="imageDTOs[1]">이미지 2</th>
<td class="notice-info">
<input type="hidden" name="imageDTOs[1].imageType" value="1">
<input type="file" name="images" onchange="previewImage(this, 'previewImg1')">
<img id="previewImg1" class="img-thumbnail" style="max-width: 100px; max-height: 100px;">
</td>
</tr>
<script>
//매개변수 : 파일 입력란과 미리보기 이미지의 ID
function previewImage(input, previewId) {
var preview = document.getElementById(previewId);
//파일 입력란에서 선택된 파일 객체를 가져오기
var file = input.files[0];
//파일을 읽어오는데 사용되는 FileReader 객체를 생성
var reader = new FileReader();
//파일이 로드될 때 실행되는 이벤트 핸들러를 정의
reader.onload = function (e) {
preview.src = e.target.result;
};
//파일이 존재하는 경우에만 readAsDataURL 메서드를 사용하여
//파일을 읽어와 데이터 URL로 변환
if (file) {
reader.readAsDataURL(file);
}
}
//이벤트 핸들러를 사용하여 문서가 로드되면 아래의 코드 블록 실행
$(document).ready(function () {
//"images"로 시작하는 모든 파일 입력란에 대해 change 이벤트 핸들러를 추가
$('input[name^="images"]').change(function () {
//현재 파일 입력란의 ID에서 'fileInput'을 제거하여 인덱스를 추출
var index = $(this).attr('id').replace('fileInput', '');
//파일 입력란과 미리보기 이미지의 ID를 전달하여 previewImage 함수를 호출
previewImage(this, 'previewImg' + index);
});
});
</script>
</tbody>
</table>
<div class="row">
<div class="col d-flex justify-content-center">
<div class="notice-button-container">
<div class="notice-content">
<div class="notice-btn-date-wrap list-btn">
<div class="notice-btn">
<button type="submit" class="btn btn-lg">등록</button>
<button type="reset" class="btn btn-lg">다시</button>
<button type="button" class="btn btn-lg" th:onclick="|location.href='@{/admin_noticelist}'|">취소</button>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
▪️ 이미지 관리 DTO
private List<ImageDTO> imageDTOs;
▪️ 이미지 종류
private Integer imageType;
<tr>
<th scope="row" class="notice-padding" id="imageDTOs[3]">이미지 1</th>
<td class="notice-info">
<input type="hidden" name="imageDTOs[3].imageType" value="3">
<input type="file" name="images" onchange="previewImage(this, 'previewImg3')">
<img id="previewImg3" class="img-thumbnail" style="max-width: 100px; max-height: 100px;">
</td>
</tr>
▪️ 삽입된 이미지 미리보기
<script>
//매개변수 : 파일 입력란과 미리보기 이미지의 ID
function previewImage(input, previewId) {
var preview = document.getElementById(previewId);
//파일 입력란에서 선택된 파일 객체를 가져오기
var file = input.files[0];
//파일을 읽어오는데 사용되는 FileReader 객체를 생성
var reader = new FileReader();
//파일이 로드될 때 실행되는 이벤트 핸들러를 정의
reader.onload = function (e) {
preview.src = e.target.result;
};
//파일이 존재하는 경우에만 readAsDataURL 메서드를 사용하여
//파일을 읽어와 데이터 URL로 변환
if (file) {
reader.readAsDataURL(file);
}
}
//이벤트 핸들러를 사용하여 문서가 로드되면 아래의 코드 블록 실행
$(document).ready(function () {
//"images"로 시작하는 모든 파일 입력란에 대해 change 이벤트 핸들러를 추가
$('input[name^="images"]').change(function () {
//현재 파일 입력란의 ID에서 'fileInput'을 제거하여 인덱스를 추출
var index = $(this).attr('id').replace('fileInput', '');
//파일 입력란과 미리보기 이미지의 ID를 전달하여 previewImage 함수를 호출
previewImage(this, 'previewImg' + index);
});
});
</script>
'Project > ShoppingMall' 카테고리의 다른 글
[User_공지사항] 댓글 등록, 목록, 수정 / 댓글 좋아요 싫어요 (3) (0) | 2024.01.25 |
---|---|
[User_공지사항] 게시글 목록 / 상세보기 / 조회수 / 이전 다음 페이지 이동 (2) (0) | 2024.01.23 |
[Admin_공지사항] enum 공지사항 유형 / 조회수 증가 / 다중 검색 기능(1) (0) | 2024.01.20 |
[User_상품진열] enum 카테고리별로 진열 / 상품 상세페이지 / 할인율 계산 / 자동합산 / 연관상품 (0) | 2024.01.20 |
[Admin_상품등록] 다중 이미지 삽입 / 이미지-상품 테이블 조인 / enum 카테고리, 판매 상태(2) (0) | 2024.01.19 |