DAO 없이 쇼핑몰 첨부파일 처리하기

기여·2024년 7월 28일
0

소소한 개발팁

목록 보기
61/103

예전에 eclipse 교육 받을 때 백쪽에 항상 Vo, Dao, DaoImpl, Service(★), ServiceImpl, Controller(★★) 이렇게 최소 6종세트가 필요했다.
(★, ★★: 이하부터는 Svc, Ctrl로 표시할 예정)

sts4로 넘어오면서 DaoImpl가 생략되었다.
chat gpt에게 물어보니 Dao까지도 생략 가능하더라~ 즉: Vo, Svc, SvcImpl, Ctrl 4종세트로 줄여짐!
그래서 개인 플젝에 바로 적용해봤다.

진행하려는 건 sts4에서 war/thymeleaf/lombok/mariaDB/mybatis, 첨부파일 포함된 쇼핑몰이다.

1, Vo

private String pimgStr; // 첨부파일명. db에는 varchar로 저장
private MultipartFile pimg; // 첨부파일 자체. db엔 없고 백-프론트에만 사용

2, Svc
ㄴ Dao 대신 mapper 역할 맡기기

@Mapper
public interface PrdSvc {
	List<PrdVo> prdList(PrdVo vo); // 상품 리스트 관리
	void addPrd(PrdVo vo); //상품 추가
}

3, SvcImpl
ㄴ Svc에서 매핑하기

@Service
public class PrdSvcImpl {

	@Autowired
	PrdSvc prdMapper; //mapper
	
	public List<PrdVo> prdList(PrdVo vo) {
		return prdMapper.prdList(vo);
	}
	
	public void addPrd(PrdVo vo) {
		prdMapper.addPrd(vo);
	}
}

4.1, application.properties
ㄴ 저장경로 선언을 위해 아래 문구 추가:

file.upload-dir=src/main/resources/static/img

4.2, FileStorageService
ㄴ 파일 저장경로 처리를 위한 별도의 class
선언한 저장경로 ${file.upload-dir} 을 @Value에 대입

@Service
public class FileStorageService {
	
	@Value("${file.upload-dir}")
    private String uploadDir;

    @PostConstruct
    public void init() {
        File uploads = new File(uploadDir);

        //프론트에선 파일 첨부를 필수로 지정했어도, 백에서도 첨부파일 없는 경우 대비
        if (!uploads.exists()) {
            uploads.mkdirs();
        }
    }

    public String getUploadDir() {
        return uploadDir;
    }
}

4.3, Ctrl
ㄴ SvcImpl에서 매핑하기

@RequestMapping("/prd/")
@Controller
public class PrdCtrl {
	
	PrdCtrl(){
		System.out.println("PrdCtrl 생성자");
	}
	
	@Autowired
	PrdSvcImpl prdSvcImpl; //mapper

	@GetMapping("prdList") // 상품 리스트 조회
	public String prdList(Model model) {
		System.out.println("prdList");
		
		PrdVo vo=new PrdVo(); // 필요한 경우 값 설정
		List<PrdVo> li=prdSvcImpl.prdList(vo);
		
		model.addAttribute("li", li);
		model.addAttribute("lisize", li.size());
		
		return "prd/prdList";
	}
	
	@GetMapping("addPrdForm") //상품 추가 시작
	public String addPrdForm() {
		System.out.println("addPrdForm");
		
		return "prd/addPrdForm";
	}
	
	@PostMapping("addPrd") //상품정보 (첨부 포함) 제출
	public String addPrd(MultipartHttpServletRequest request, HttpServletResponse response) throws Exception {
	    System.out.println("addPrd");

	    PrdVo vo = new PrdVo();

	    // 첨부파일 처리
	    Part filePart = request.getPart("pimg");
        String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString();
        
        //application, FileStorageService에서 정의한 경로(resources/static/img)로 저장하도록 설정
        String realFolder = fileStorageService.getUploadDir();
        
        //파일 경로 생성        
	    File file = new File(realFolder, fileName);
        filePart.write(file.getAbsolutePath());

        // 콘솔에 저장된 파일 경로 출력
        System.out.println("저장 경로: " + file.getAbsolutePath());

	    // 기타 값 처리
	    vo.setPimgStr(fileName);
	    vo.setPname(request.getParameter("pname"));
	    vo.setPdesc(request.getParameter("pdesc"));
	    vo.setPprice(Integer.parseInt(request.getParameter("pprice")));
	    vo.setPstock(Integer.parseInt(request.getParameter("pstock")));
	    vo.setPdelifee(Integer.parseInt(request.getParameter("pdelifee")));

	    // db에 추가
	    prdSvcImpl.addPrd(vo);    

	    return "redirect:prdList";
	}	
}

mapper.xml

<mapper namespace="com.mall.prd.PrdSvc"> <!-- Dao 대신 Svc 씀 -->

<select id="prdList" resultType="com.mall.prd.PrdVo">
SELECT * FROM products order by product_code desc
</select> 

<insert id="addPrd" parameterType="com.mall.prd.PrdVo">
INSERT INTO products (
            pimgStr,
            ...
        ) VALUES (
            #{pimgStr},
            ...
        )
</insert>
 </mapper>

상품 추가 양식

<form action="/prd/addPrd" 
method="post" enctype="multipart/form-data">

<table class="addPrdTable">		
	<tr>	
		<th>pname</th>
		<td><input type="text"  name="pname"></td>
	</tr>
	
	<tr>	
		<th>pimg</th>
		<td><input type="file" name="pimg" required="required"></td>
	</tr>	
	...	
</table>
<br>

<input type=submit  value=등록 class=button>
&emsp;
<input type="reset"  value="다시 입력" class=button>

</form>

상품 리스트

<h3 th:text="'상품건수: ' + ${lisize} + '건'"></h3>

<table>
	<tr class="tr_color">
		<th>no</th> 
		<th>product_code</th> 		
	    <th>pimg</th>	    	    
	    ...
	</tr>
		
	<tr th:each="m, stat : ${li}" class="tr_color">	
		
		<td th:text="${stat.count}"></td> <!-- db와 관계없는 자동증가하는 순번 -->
		<td th:text="${m.product_code}"/>
		
		<td>
		<img th:src="@{/img/}+${m.pimgStr}" width="100" />		
		</td>		
		...
	</tr>
	
</table>

유의할 점은 파일 저장경로 설정 시,
db 통해 직접 레코드 추가했을 때에 저장된 경로와 동일하게
'resources/static/img'에 저장되도록 하는 것이다.
해당 img 폴더는 플젝 생성할 때부터 미리 추가해두는 게 좋겠지.

그렇지 않으면, db가 아닌 프론트에서 이미지 올렸을 때,
하단 webapp 폴더에 'static/img' 폴더가 자동으로 생성되고, 이미지는 이쪽에 저정된다!
큰 문제는 아니라지만 개발ㆍ배포 간에 통일성이 떨어지고,
직접 겪은 바로는 이미지가 출력 안 되는 현상이 있었다.

처음엔 당황했지만 저장경로 재설정하니까 이미지가 제대로 출력됐다.

ps: 명탐정 코난 주30년 축하한당.

profile
기기 좋아하는 여자

0개의 댓글