JPA로 질문게시판 만들기 -2 ( 글작성)

Elly·2024년 2월 15일
0

안녕하세요. 오늘은 글 작성을 해보도록하겠습니다.
제가 혼자 공부하고 만들다보니 부족한 점이 많습니다. 지적과 조언 부탁드리겠습니다
궁금하신 분은 이전 글에서 api설계와 프로젝트 구조를 확인해주세요.

요구사항

2. 질문게시글 작성 페이지
-이미지 및 파일 업로드 가능
-공백 유효성 검사


QuestionSaveRequestDto

사..실 저는 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 = "제목은 필수입니다.") 를 붙여줬습니다.

글쓰기창

Controller

  // 글쓰기창
  @GetMapping("/questions/save")
  public String questionsWrite() {
    return "board/question/write";
  }

글쓰기 창은 이게 다입니다. (참고로 제 프로젝트는 Spring Security를 해놓았기 때문에, 게시판 조회와 리스트 말고는 로그인 페이지로 가도록 되어있습니다.)

저는 글쓰기 에디터로 서머노트를 사용합니다. 적용 방법은 여기를 클릭해주세요.

글작성

Controller

  // 글쓰기저장
  @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를 생성하여 관리하는 방법을 쓰겠습니다.

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;
    }

Service

  // 글작성
  @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가 되도록 합니다.

write.html

<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를 상속받아서 사용한다면 문제 없이 잘 될 것입니당.

JS

// 저장 버튼 클릭 여부
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이여서 엥 하시죠,, 조회수 기능도 다음에 글을 작성하도록 하겠습니다 헤헤

profile
반성적 사고하며 성장하는 개발자

0개의 댓글

관련 채용 정보