스프링부트에서 타임리프로 이미지를 CRUD한다는 것은 매우 복잡한 일이다.
이유는, DB가 이미지를 저장할 수 없기 때문이다.
이미지는 많은 화소로 만들어져 있어서 단일한 데이터로 ORACLE DB에 저장이 안된다.
**저장하기
1. DTO에서 MultiFile imgae를 만드는 것이 핵심
long sid;
String sname;
String simage; //화일명만 저장용도
MultipartFile simage1;//폼에서 받아온 이미지명
LocalDate sdate;
int sreadcount;//
2. 엔티티 역시 image_name만 저장함
public class SangEntity {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,generator = "koko"
)
@Column
long sid;
@Column
String sname;
@Column
String simage;
@Column
LocalDate sdate;
@Column
int sreadcount;
}
3. Input 에서 enctype="Multipart/form-data" 해주기
<div class="container">
<form action="/inputsave" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="sname">상품명:</label>
<input type="text" class="form-control" id="sname" name="sname">
</div>
<div class="form-group">
<label for="simage1">상품이미지</label>
<input type="file" class="form-control" id="simage1" name="simage1">
</div>
4. Controller에서 MultipartFile 에 이름만 추출하고 UUID 붙여서 DB에 저장
@PostMapping("/inputsave")
public String kk3(SangDTO dto) throws IOException {
MultipartFile mf= dto.getSimage1();
String fname=mf.getOriginalFilename();
/// /화일고유번호 지정
UUID ud =UUID.randomUUID();
fname=ud.toString()+"-"+fname;
//// 디비에 저장
dto.setSimage(fname);
SangEntity sentity =dto.toentity();
sangService.insertp(sentity);
5. 이미지는 폴더에 저장하기
/// 이미지 폴더에 저장
mf.transferTo(new File(path+"\\"+fname));
String path="C:\\sundo\\springboot\\day10_image\\src\\main\\resources\\static\\image";
**출력하기
1. Entity형식으로 저장소 DB로 가서 데이터 모두 가져오기
@GetMapping("/outgo")
public String kk4(Model mo){
List<SangEntity> list= sangService.allot();
mo.addAttribute("list",list);
log.info("자료의 갯수 : "+list.size());
return "out";
2. 이미지 출력하기 out.tempalate <파일 경로 이름이 image임>
<td>
<img th:src="@{./image/}+${aa.simage}" width="50px" height="40px">
</td>
**삭제하기
1. 삭제클래스 컨트롤러로 가기
<td>
<a th:href="@{/delete(number=${aa.sid},delimage=${aa.simage})}">삭제</a>
</td>
2. 해당 테이블 번호와 이미지를 모두 가져와야 함.
@GetMapping("/delete")
public String kk5(@RequestParam("number") long number,
@RequestParam("delimage") String delimage ){
sangService.deletea(number); //디비자료 삭제
//이미지폴더삭제
File ff = new File(path+"\\"+delimage);
ff.delete();
return "redirect:/outgo";
3. 구현부에서 삭제 메서드 쓰면 됨
@Override
public void deletea(long number) {
sangRepository.deleteById(number);
}
**수정하기
1. 수정하기 컨트롤러로 특정 테이블 번호 가져가기
<td>
<a th:href="@{/modify(number=${aa.sid},delimage=${aa.simage})}">수정</a>
</td>
2. 컨트롤러에서 가져온 숫자에 해당하는 모든 데이터 가져오기
@GetMapping("/modify")
public String kk7(@RequestParam("number")long number,Model mo){
SangEntity dto= sangService.modify1(number);
mo.addAttribute("dto",dto);
return "modifyview";
}
3. 인터페이스 구현부로 가서 저장소로 이동
@Override
public SangEntity modify1(long number) {
SangEntity dto = sangRepository.modify2(number);
return dto;
}
4. 저장소DB로 가서 번호에 해당하는 정보 모두 가져오기
@Query(value = """
select * from sang1212 where sid =:number
""",nativeQuery = true)
SangEntity modify2(long number);
5. 가져온 데이터를 수정뷰에 보여주고 수정하기
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2 th:text="${dto.sid}+${'의 자료수정 확인해주세요'}"></h2>
<div th:replace="~{top::menu}"></div>
<div class="container">
<form action="/modifysave" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="sid">상품번호:</label>
<input type="text" class="form-control" id="sid" name="sid" th:value="${dto.sid}" readonly>
</div>
<div class="form-group">
<label for="sname">상품명:</label>
<input type="text" class="form-control" id="sname" name="sname" th:value="${dto.sname}">
</div>
<div class="form-group">
<label for="simage1">상품이미지</label>
<input type="hidden" class="form-control" name="simagego" th:value="${dto.simage}">
<img th:src="@{./image/}+${dto.simage}" width="50px" height="40px">
<input type="file" class="form-control" id="simage1" name="simage1" >
</div>
<div class="form-group">
<label for="sdate">생산날짜</label>
<input type="date" class="form-control" id="sdate" name="sdate" th:value="${dto.sdate}" >
</div>
<button type="submit" class="btn btn-primary">전송</button>
</form>
</div>
</body>
</html>
6. 수정한 정보를 다시 DB에 저장할거임. 이 과정에서 이미지를 가져온 다음 해당 이미지 이름을 다시 추출해야 함.
@PostMapping("/modifysave")
public String kk8(SangDTO dto,
@RequestParam("simagego")String simagego) throws IOException {
String fname="";
MultipartFile mf= dto.getSimage1();
UUID ud=UUID.randomUUID();
if(mf.isEmpty()) {
log.info("이미지지정 안할경우 화일명"+simagego);
fname=simagego;
}else{
mf = dto.getSimage1();
fname = mf.getOriginalFilename();
/// /화일고유번호 지정
fname = ud.toString() + "-" + fname;
//// 디비에 저장
log.info("이미지화일명 : "+fname);
7. 이미지 파일에서도 해당 이미지 저장시 기존 이미지 지워야 함
log.info("이미지화일명 : "+fname);
mf.transferTo(new File(path+"\\"+fname));
File ff=new File(path+"\\"+simagego);
ff.delete();
}
dto.setSimage(fname);
SangEntity sentity =dto.toentity();
sangService.insertp(sentity);
/// 이미지 폴더에 저장
return "redirect:/";
**자세히보기
1. 자세히 보려는 해당 테이블의 번호 가지고 컨트롤러로 이동
<tr th:each="aa:${list}">
<td th:text="${aa.sid}"></td>
<td>
<a th:href="@{/detail(number=${aa.sid})}" th:text="${aa.sname}"> </a>
</td>
2. 컨트롤러에서 가져온 번호에 해당하는 정보를 DB에서 가져오면 됨. 이 과정에서 조회수 세기 코드 같이 추가
@GetMapping("/detail")
public String kk6(@RequestParam("number")long number,Model mo) {
//상품번호에 해당되는 자료들을 가지고 옴
//조회수 증가
sangService.readcount(number);
/// //////////////////////
SangEntity dto =sangService.detail(number);
mo.addAttribute("dto",dto);
return "detailview";
}
3. 구현부에서의 해당번호 가져오기
@Override
public SangEntity detail(long number) {
return sangRepository.findById(number).orElse(null);
}
4. 자세히보기뷰에서는 모든 테이블의 정보를 반복하여 보여주는 list 필요 없으므로 dto. 사용
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div th:replace="~{top::menu}"></div>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="container">
<table class="table table-hover">
<thead>
<tr>
<th>상품이미지</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<img th:src="@{/image/}+${dto.simage}" width="400px" height="300px">
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
** 조회수 올리기
1. 조회수 DB에 꼭 선언할 것
int sreadcount;//
2. 컨트롤러 내에서의 조회수 증가하기
@GetMapping("/detail")
public String kk6(@RequestParam("number")long number,Model mo) {
//상품번호에 해당되는 자료들을 가지고 옴
//조회수 증가
sangService.readcount(number);
/// //////////////////////
SangEntity dto =sangService.detail(number);
mo.addAttribute("dto",dto);
return "detailview";
}
3. readcount 매서드
@Override
public void readcount(long number) {
sangRepository.readcount(number);
}
4. 저장소에서 주의할 점은 @Transactional @Modifying 잊지말기
@Transactional
@Modifying
@Query(value = "update sang1212 set sreadcount=sreadcount+1 "+
" where sid =:number",nativeQuery = true)
void readcount(@Param("number") long number);
5. outtemplate에서 구현
<td th:text="${aa.sreadcount }"></td>
+ 다양한 표현법 (빨간색으로 표시)
<td>
<a th:href="@{/output(bnum=${my.bnum})}" th:text="${my.bnum}" ></a>
<span th:if="${my.rcount > 0}"
th:text="' [' + ${my.rcount} + ']'"
style="color: red; font-weight: bold;"></span>
</td>

끝.
'AI딥러닝 > SporingBoot' 카테고리의 다른 글
| Query문을 잘만 쓰면 개꿀, 기본문제풀이 4개 (0) | 2026.01.12 |
|---|---|
| Query과 Interface를 이용하여 두 개 이상의 테이블 조인하기 (0) | 2026.01.12 |
| 스프링부트 엔티티와 인터페이스로 페이징 처리기술 (0) | 2026.01.10 |
| Springboot : Thymeleaf 레이아웃 코딩 시 발생할 수 있는 오류 (0) | 2026.01.09 |
| Spring Boot, 초보개발자 API 정복하기! (0) | 2026.01.09 |