안녕하세요. 오늘은 글 작성을 해보도록하겠습니다.
제가 혼자 공부하고 만들다보니 부족한 점이 많습니다. 지적과 조언 부탁드리겠습니다
궁금하신 분은 이전 글에서 api설계와 프로젝트 구조를 확인해주세요.
2. 질문게시글 작성 페이지
-이미지 및 파일 업로드 가능
-공백 유효성 검사
사..실 저는 requestDto와 responseDto를 다 따로 뺐는데요. 저도 쓰면서 "이걸,,이렇게 dto를 많이 만들어도되나?" 생각이 들었습니다. 찾다보니 InnerClass로 관리하시는 분들이 많더라고요.
우선은 프로젝트를 끝내놓고, 다음에 유지보수를 하겠습니다.
https://song8420.tistory.com/383
정말 정리를 잘해주셔서 꼬옥 보고 공부하도록 하겠습니다..
@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class QuestionSaveRequestDto {
private Long qbNo;
@NotBlank(message = "제목은 필수입니다.")
private String qbTitle;
@NotBlank(message = "내용은 필수입니다.")
private String qbContent;
private Member member;
private String qbNickName;
private int qbViews;
private int commentCount;
private boolean hasThumbnail;
private LocalDateTime qbCreateDt;
private LocalDateTime qbModifyDt;
@Builder
public QuestionSaveRequestDto(Long qbNo, String qbTitle, String qbContent, LocalDateTime qbCreateDt,
LocalDateTime qbModifyDt, int qbViews, String qbNickName, String qbThuqbnail, boolean hasThumbnail) {
this.qbNo = qbNo;
this.qbTitle = qbTitle;
this.qbContent = qbContent;
this.qbCreateDt = qbCreateDt;
this.qbModifyDt = qbModifyDt;
this.qbViews = qbViews;
this.qbNickName = qbNickName;
this.hasThumbnail = hasThumbnail;
}
public QuestionBoard toEntity(Member member) {
return QuestionBoard.builder()
.member(member)
.qbNickName(member.getMemberNickname())
.qbTitle(qbTitle)
.qbContent(qbContent)
.qbViews(qbViews)
.hasThumbnail(hasThumbnail)
.build();
}
public boolean hasThumbnail(boolean hasThumnail) {
return this.hasThumbnail = hasThumnail;
}
저는 서버에서 @Vaild로 빈칸검사를 해야하기 때문에
@NotBlank(message = "제목은 필수입니다.") 를 붙여줬습니다.
// 글쓰기창
@GetMapping("/questions/save")
public String questionsWrite() {
return "board/question/write";
}
글쓰기 창은 이게 다입니다. (참고로 제 프로젝트는 Spring Security를 해놓았기 때문에, 게시판 조회와 리스트 말고는 로그인 페이지로 가도록 되어있습니다.)
저는 글쓰기 에디터로 서머노트를 사용합니다. 적용 방법은 여기를 클릭해주세요.
// 글쓰기저장
@ResponseBody
@PostMapping("/questions/save")
public ResultDto save(@RequestBody @Valid QuestionSaveRequestDto requestDto,
BindingResult bindingResult) {
ResultDto resultDto = new ResultDto();
try {
// 유효성빈칸검사 걸렸을 경우
if (bindingResult.hasErrors()) {
resultDto.setFail(bindingResult.getFieldError().getDefaultMessage());
return resultDto;
}
return questionBoardService.save(requestDto);
} catch (Exception e) {
resultDto.setFail("오류가 발생했습니다.");
return resultDto;
}
}
@Valid를 사용하면 에러 내용이 BindingResult 변수에 담기게 되는데, 저희 프로젝트는 resultDto를 생성하여 관리하는 방법을 쓰겠습니다.
public class ResultDto {
private boolean result;
private String message;
private String uri;
public void setFail(String message) {
this.result = false;
this.message = message;
}
public void setSuccess(String message) {
this.result = true;
this.message = message;
}
// 글작성
@Transactional
public ResultDto save(@Valid QuestionSaveRequestDto requestDto) {
Member member = CommonUtil.getMember();
ResultDto resultDto = new ResultDto();
// 섬네일이 있다면 이미지사진을 제목에 표시
String content = requestDto.getQbContent();
if (content.contains("<img src=")) {
requestDto.hasThumbnail(true);
}
try {
questionBoardRepository.save(requestDto.toEntity(member));
} catch (Exception e) {
resultDto.setFail("문제가 발생했습니다.");
}
return ResultDto.builder().result(true).build();
}
Member member = CommonUtil.getMember();는 현재 사용자의 정보를 가져옵니다 (Spring Security를 사용)
저는 서머노트를 사용해서 내용에 이미지가 있으면 Db에
이런식으로 저장이 되기 때문에
게시판 조회 시에 이미지 유무를 위하여 <img src=가 있다면 hasThumbnail이 true가 되도록 합니다.
<div layout:fragment="Content" class="item Content">
<article>
<div class="container" role="main">
<div class="form-group">
<label for="qbTitle">제목</label>
<input type="text" class="form-control" id="qbTitle" name="qbTitle" placeholder="제목을 입력해 주세요">
<div class="error_message text-start float-start" id="titleErrorMsg" style="display: none;">
제목을 입력해주세요.
</div>
</div>
<br>
<div class="form-group">
<label for="qbContent">내용</label>
<textarea class="form-control" rows="5" id="qbContent" name="qbContent" placeholder="내용을 입력해 주세요"></textarea>
<div class="error_message text-start float-start" id="contentErrorMsg" style="display: none;">
내용을 입력해주세요.
</div>
</div>
<div class="button-wrap">
<button onclick="location.href='/questions'" type="button" class="button_cancel btn-lg col-5"
id="button_cancel">취소</button>
<button type="button" class="button_save btn-lg col-5" id="button_save">저장</button>
</div>
</div>
</article>
</div>
save메소드는 jpa가 기본으로 제공하는 메소드이므로
public interface QuestionBoardRepository extends JpaRepository<QuestionBoard,Long> 이렇게 JpaRepository를 상속받아서 사용한다면 문제 없이 잘 될 것입니당.
// 저장 버튼 클릭 여부
let isButtonClicked = false;
// 공백확인
function isEmpty(value, $errorMsg) {
if (value.trim() === '') {
// 에러 메시지 표시
$errorMsg.show();
return true;
}
return false;
}
$(function() {
// 취소 버튼 클릭 시
$('.button_cancel').on('click', function() {
history.back();
});
// 저장 버튼 클릭 시
$('.button_save').on('click', function() {
// 에러 메시지 숨기기
$('.error_message').hide();
// 저장 버튼 중복 호출 막기
if (isButtonClicked) {
alert('처리중입니다.');
return;
} else {
isButtonClicked = true;
}
// 글 등록하기
let param = {};
param.qbTitle = $('[name=qbTitle]').val();
param.qbContent = $('[name=qbContent]').val();
// 제목이나 내용이 비어있는지 체크
if (!isEmpty(param.qbTitle, $('#titleErrorMsg')) && !isEmpty(param.qbContent, $('#contentErrorMsg'))) {
CustomUtils.sendPostAjax('/questions/save', param, function(res) {
if (res.result) {
alert('글이 정상적으로 작성되었습니다.');
window.location.href = '/questions';
} else {
alert(res.message);
// 처리 완료 후 false로 변경
isButtonClicked = false;
}
});
} else {
isButtonClicked = false;
}
});
}
isEmpty 함수로 클라이언트에서도 빈칸검사를 해주었습니다.
resultDto에 result가 true면 글작성 완료, false라면 setFail에 담긴 에러메세지값이 뜹니다.
유효성도 잘되고
글도 잘됩니다...!! 조회수가 1이여서 엥 하시죠,, 조회수 기능도 다음에 글을 작성하도록 하겠습니다 헤헤