AI딥러닝/SporingBoot

SpringBoot Thymleaf Oracle' Image CRUD+S 처리하는 법

Ystory96 2026. 1. 10. 15:18

스프링부트에서 타임리프로 이미지를 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>

 

 

끝.