기존의 게시판 CRUD 👉🏻 2021.03.21 - [java/spring] - [JPA] 게시판 CRUD
[JPA] 게시판 CRUD
가장 먼저 간단한 게시판 기능을 구현하기로 하였다. 카테고리를 선택하고 글의 제목과 내용을 입력하여 게시물을 등록하고 조회, 수정, 삭제까지 할 수 있도록 하였다. 그 외의 이미지, 동영상
juran-devblog.tistory.com
* 일단 게시판 제목과 내용은 Board에, 이미지 관련은 Images에 따로 저장하도록 하였는데 연관관계 매핑을 신경써서 좀 더 수정해야할 것 같다.
1. Images 엔티티 작성
📦 Images
@NoArgsConstructor
@Entity
@Getter
@Setter
public class Images extends DateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long imageNo;
@Column(length = 500, nullable = false)
private String originImageName;
@Column(length = 500, nullable = false)
private String imageName;
@Column(length = 1000, nullable = false)
private String imagePath;
@Builder
public Images(String originImageName, String imageName, String imagePath) {
this.originImageName = originImageName;
this.imageName = imageName;
this.imagePath = imagePath;
}
}
2. 기존의 Request 객체 수정
📄 BoardRequest (게시판 작성 form을 받아오는 객체)
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class BoardRequest {
@NotNull
private String boardTitle;
@NotNull
private String boardContent;
@NotNull
private Long categoryNo;
@NotNull
private MultipartFile image; // 새로 추가
}
3. 레퍼지토리 작성
📄 ImageRepository
public interface ImageRepository extends JpaRepository<Images, Long> {
}
JpaRepository 상속
4. 서비스 작성
📄 ImageService
@Service
@RequiredArgsConstructor
public class ImageService {
private final ImageRepository imageRepository;
@Transactional
public void saveImage(MultipartFile mtf) {
LocalDateTime now = LocalDateTime.now();
int year = now.getYear();
int month = now.getMonthValue();
int day = now.getDayOfMonth();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
int millis = now.get(ChronoField.MILLI_OF_SECOND);
String absolutePath = new File("/Users/juran/Desktop").getAbsolutePath() + "/";
String newFileName = "image" + hour + minute + second + millis;
String fileExtension = '.' + mtf.getOriginalFilename().replaceAll("^.*\\.(.*)$", "$1"); // 정규식 이용하여 확장자만 추출
String path = "images/local/" + year + "/" + month + "/" + day;
try {
File file = new File(absolutePath + path);
if(!file.exists()) {
file.mkdirs();
}
file = new File(absolutePath + path + "/" + newFileName + fileExtension);
mtf.transferTo(file);
Images images = Images.builder()
.originImageName(mtf.getOriginalFilename())
.imageName(newFileName + fileExtension)
.imagePath(path)
.build();
imageRepository.save(images);
} catch (Exception e) {
e.printStackTrace();
}
}
}
5. 기존의 컨트롤러 수정
📄 BoardApiController
@PostMapping(path = "register", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> register(@ModelAttribute BoardRequest boardRequest, Authentication authentication) throws Exception {
Member member = memberService.loadUserByUsername(authentication.getPrincipal().toString());
Category category = categoryService.findByCategoryNo(boardRequest.getCategoryNo());
boardService.registerBoard(boardRequest.getBoardTitle(), boardRequest.getBoardContent(), member, category);
imageService.saveImage(boardRequest.getImage()); // 이미지 저장 부분 새로 추가
ApiResponse apiResponse = new ApiResponse(true, "게시물 등록");
return ResponseEntity.ok(apiResponse);
}
6. JSP 수정
📄 register.jsp
<form name="form" method="post" enctype="multipart/form-data" id="frmData">
<input class="form-control" type="file" name="image" multiple="multiple"/>
</form>
파일 선택하는 부분 추가
7. 스크립트 수정
$('#btnBoarddRegister').on({ // 등록 버튼 눌렀을 때
click: function () {
// 폼 데이터 생성
let formData = new FormData($("#frmData")[0]);
formData.append("boardTitle", $('#boardTitle').val());
formData.append("boardContent", $('#boardContent').val());
formData.append("categoryNo", $('#selectCategory option:selected').val());
let boardRegConfirm = confirm('등록하시겠습니까?');
if(boardRegConfirm == true) {
$.ajax({
url: baseUrl + '/api/board/register',
type: 'POST',
enctype: 'multipart/form-data',
contentType: false,
processData: false,
data: formData,
beforeSend: function (xhr) {
xhr.setRequestHeader("${_csrf.headerName}", "${_csrf.token}");
},
success: function (result) {
location.href = '/';
}, error: function (error) {
console.log(error)
}
});
}
}
});
* FormData
ajax로 폼데이터 전송을 가능하게 해주는 객체로 form 필드와 그 값을 나타내는 일련의 key/value 쌍을 생성
업로드한 이미지 미리보기는 추가할 예정.. 곧..
자꾸 보이는 허점들.. 저장 시간도 추가해야함.. 어쨌든 저장 성공..!!!
처음에 그냥 multipartfile만 request 객체에 추가하고 스크립트에는 formData으로만 수정해줬을 때 발생한 오류1
data: formData
Uncaught TypeError: Illegal invocation
ajax로 데이터를 보낼 때 파라미터 값이 정상적으로 들어가지 않았을 때 발생하는 오류라고 한다.
json으로 보내지 않아서 생긴 오류인줄 알고
data: JSON.stringfy(formData)
로 수정하였다.
그랬더니 이번에는 백단에서 발생한 오류2
java.lang.NullPointerException
BoardRequest로 받은 객체에서 get으로 값을 조회하려 하니 저렇게 널포인터에러가 뜬다.
이상하다.. Request로 바로 못 받는거 같아서 이리저리 찾다 발견한 것이 MultipartHttpServletRequest
* 참고로 Multipart는 HTTP 요청 시 DTO로 받을 수 있다고 함
public ResponseEntity<?> register(@RequestBody MultipartHttpServletRequest mtfRequest, Authentication authentication) throws Exception {
// FormData 사용시 한글이 깨지는 것을 방지
String[] boardTitle = mtfRequest.getParameterValues("boardTitle");
String[] boardContent = mtfRequest.getParameterValues("boardContent");
String[] categoryNo = mtfRequest.getParameterValues("categoryNo");
String convertToBoardTitle = new String(boardTitle[0].getBytes("8859_1"),"utf-8");
String convertToBoardContent = new String(boardContent[0].getBytes("8859_1"),"utf-8");
Iterator<String> its = mtfRequest.getFileNames();
while (its.hasNext()) {
String fileNm = (String) its.next();
MultipartFile mtf = mtfRequest.getFile(fileNm);
imageService.saveImage(mtf);
}
....
바로 get으로 하나의 MultipartFile을 못꺼내오고 이름을 먼저 추출하여 그걸로 file을 추출해야 한다기에 코드를 이렇게 수정해주었고
한글이 깨지는 문제가 발생하다고 하여 위와 같이 "utf-8"로 변환해주었다.
이 방법도 안되서 다시 이전에 Request 객체로 받는 방법으로 다시 수정했다.
뭐 어찌저찌 머리를 싸매다 마주친 오류3(는 아니고 그냥 경고 로그)
org.springframework.web.HttpMediaTypeNotSupportedException
이거 보고 백단이 문제가 아니라 요청 보낼 때, 스크립트에서 ajax로 요청이 잘 못가는거 같아 multipartfile ajax 하는 스크립트를 열심히 찾아보았다.
enctype: 'multipart/form-data',
contentType: false,
processData: false,
data: formData,
그리하여 성공!! 이미지 미리보기랑 조회, 수정, Board테이블이랑 매핑 등등,, 아직 해야할 게 많군요
[SPRING BOOT] 2521 과몰입러의 스프링부트 웹소켓 채팅 서비스 만들기(2) 화면구성과 JS (0) | 2022.03.15 |
---|---|
[SPRING BOOT] 2521 과몰입러의 스프링부트 웹소켓 채팅 서비스 만들기(1) 자바소스 (0) | 2022.03.14 |
[SPRING] MultipartFile 게시판 이미지 업로드(1) 간단한 파일 업로드 예제 (1) | 2022.01.10 |
[SPRING] Model, ModelMap, ModelAndView 차이점 (0) | 2021.11.14 |
[ERROR] @Builder와 @NoArgsConstructor 동시에 사용할 때 (0) | 2021.08.24 |
댓글 영역