※ 많은 레퍼런스들을 참고하여 고민하며 구현한 코드지만 저 스스로도 취업준비생인 초보 개발자이기 때문에 코드 상의 문제 혹은 더 바람직한 우수사례들이 있을 수 있습니다. 코드 작성 시 참고 정도로 활용해주시고 문제나 개선 가능한 부분 발견 시 공유해주신다면 감사하겠습니다.
CONTROLLER
@PostMapping("create")
public String createPost(MultipartHttpServletRequest mtfRequest, BoardVO boardVO) {
// 게시글 저장
boardService.create(boardVO);
// 업로드 파일이 존재할 때
if (mtfRequest != null) {
// 파일(들)을 지정된 경로에 저장하고 데이터베이스에 기록될 경로(들)을 ArrayList로 반환
List<String> fileDirs = UploadFileUtils.fileUpload(mtfRequest);
// file 데이터를 담을 VO 생성
FileVO fileVO = new FileVO();
// fileDirs 리스트를 순회하며 vo에 데이터를 담기
for (String fileDir : fileDirs) {
// board의 insert 수행 후 자동생성된 bno가 담긴 boardVO의 멤버변수 bno 가져오기
int bno = boardVO.getBno();
// fileVO 객체에 데이터 채우기
fileVO.setBno(bno);
fileVO.setFile(fileDir);
// 데이터베이스에 저장
fileService.save(fileVO);
}
} else {
System.out.println("첨부 파일이 없습니다.");
}
return "redirect:/board";
}
UploadFileUtils
public class UploadFileUtils {
public static List<String> fileUpload(MultipartHttpServletRequest mtfRequest) {
// multipartFile 리스트
List<MultipartFile> mtfs = mtfRequest.getFiles("image");
// 데이터베이스에 기록되는 경로를 담을 리스트
List<String> fileList = new ArrayList<>();
for (MultipartFile mtf : mtfs) {
// 파일 이름
String originalFileName = mtf.getOriginalFilename();
// 업로드 경로
String uploadPath = mtfRequest.getSession().getServletContext().getRealPath(File.separator + "uploadedImages" + File.separator);
// 조회시 과부하를 막기 위한 경로 구분용 현재 날짜를 yyyyMMdd 형태로 반환받기
String date = getTodayDate();
// 파일 이름 중복문제를 해결하기 위한 UUID
String uuid = UUID.randomUUID().toString();
// 데이터베이스에 기록될 경로 형태
// 현재날짜 + UUID + "_" + 파일
String dbFile = date + uuid + "_" + originalFileName;
// 파일 저장 경로
String saveFile = uploadPath + dbFile;
// 위에서 생성했던 리스트에 파일 이름 담기
fileList.add(dbFile);
try {
// 파일 경로에 저장하기
mtf.transferTo(new File(saveFile));
} catch (IOException e) {
e.printStackTrace();
}
}
return fileList; // 파일 리스트 반환
}
private static String getTodayDate() {
// Calendar 객체 생성
Calendar cal = Calendar.getInstance();
// 날짜 담을 변수
String datePath;
// 날짜 + 세퍼레이터 스트링
datePath = String.format("%04d%02d%02d%s", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), File.separator);
return datePath;
}
}
BoardMapper.xml (Insert 부분)
<insert id="insert" parameterType="BoardVO">
-- file 테이블의 외래키로 사용될 bno를 반환받아 BoardVO에 담김
<selectKey keyProperty="bno" resultType="int" order="BEFORE">
select QUE_SEQUENCE.NEXTVAL from dual
</selectKey>
INSERT INTO BOARD (bno, title, content, writer)
VALUES (#{bno}, #{title}, #{content}, #{writer})
</insert>
ORACLE을 사용중이기 때문에 별도의 SEQUENCE를 생성했으므로 selectKey 태그를 사용했습니다.
MySQL이나 MS_SQL을 사용할 경우 링크(https://roqkffhwk.tistory.com/180) 참고하셔서 작성하시길 바랍니다.