- QnA 글작성


오른쪽이 네이버의 QnA글작성 페이지 왼쪽에 클론코딩으로 만든 페이지
스마트에디터 api를 사용하고 싶었지만 사용방법도 모를 뿐더러
네이버 api사이트를 들어가보니 개발자 등록을 해야하더라...
그래서 쓸만한 에디터 없나 찾아보다가 ckeditor 에디터를 사용했다
- boardSave.jsp - script
ckeditor를 cdn주소로 가져와 사용했기 때문에
placeholder를 적용하는 방법도 몰라 고생이 많았습니다
ck에디터 플러그인
혹시나 ckeditor를 처음 사용한다면 한번 읽어보시길 추천드립니다.
(cdn으로 사용할 시 적용이 안되는 부분도 있음! 예 : placeholder )
Class Config (CKEDITOR.config) - CKEditor 4 API docs
CKEditor 4 API Documentation. The Class Config. Stores default configuration settings. Changes to this object are reflected in all editor instances, if not specified otherwise for a particular instance. Read more about setting CKEditor configuration in the
ckeditor.com
<script>
let boardPointPut = document.getElementById('boardPointPut');
사용자가 입력한 point(내공)의 값을 가져오기 위한 document객체
const memberPointPut = parseInt('${memberDTO.memberPoint}');
사용자의 보유 포인트 정보를 담은 변수
Long타입으로 필드를 설정했기 때문에 int로 형변환을 진행해야했습니다.
CKEDITOR.replace('editor1', {
height: '350px',
language: 'ko',
enterMode: CKEDITOR.ENTER_BR
});
CKDEITOR를 적용하기 위해서는 textarea에 id로 ckeditor를 적용해주어야 합니다
하지만 설정 당시에는 사용방법을 몰랐기 디폴트 값을 몰랐기 때문에
editor1로 이름을 변경하여 적용하엿습니다
안에는 따로 stlye을 적용한 모습입니다
enterMode의 경우 사용자가 엔터(줄바꿈)를 입력했을때 자동으로 뒤에 br을 적용해줍니다
CKEDITOR.instances['editor1'].on('contentDom', function () {
this.document.on('click', function (event) {
CKEDITOR.instances.editor1.setData('<br>');
});
});
CKEDITOR에디터에 클릭이벤트를 적용하는 방법입니다 instances를 사용하여
따로 주입을해주어야합니다 cdn으로 사용했기 떄문에 placeholder을 적용할 수 없어
내용을 작성하기위해 ckeditor영역을 클릭하는 경우,
초기에 작성되어있는 정보를 지워줍니다.
const back = () => {
location.href = "/";
}
const boardCheck = () => { 게시글 작성의 유효성을 체크해주는 함수
const boardTitle = document.getElementById('boardTitle');
글 제목을 입력받는 input태그의 객체
const checkResult = CKEDITOR.instances.editor1.getData();
사용자가 editor에 입력한 값을 가져오기 위해 getDate메소드를 사용
const betPointInfo = document.getElementById('betPointInfo');
사용자가 입력한 내공이 보유내공보다 많은 경우
문제가 있음을 공지하기 위한 result태그
const questionContent = `
궁금한 내용을 질문해 주세요.
<br>
<br>
질문 시 이런 점을 주의해 주세요!
<br>
- 답변이 등록되면 질문 수정/삭제가 불가합니다.
<br>
- 질문 내용에 개인정보(실명, 전화번호, 메신저 및 네이버 아이디)가 포함되지 않게 해 주세요.
<br>
- 의료 질문 관련하여 의료인은 SNS 상담을 하지 않고, 신체 사진/동영상을 요구하지 않습니다.
<br>
악의적인 범죄로 이어질 수 있으니 유의해 주세요.
<br>
피해를 입으셨다면, 서비스에 신고, 112 또는 사이버경찰청(www.police.go.kr)으로 신고 부탁드립니다.
`;
placeholder을적용할 수 없기 때문에 미리 안애 내용을 적어두고 클릭하면
비우는 형식으로 사용했습니다 그렇기 때문에 사용자가 제목만 적고 등록하는 상황을
방지하고자 처음에 작성되어 있는 내용을 그대로 등록하는 경우에 대한 유효성를 위한
문자열 변수를 따로 만들어 주었습니다.
const cleanCheckResult = checkResult.replace(/<\/?[^>]+(>|$)/g, '').replace(/\s+/g, '');
ckeditor를 사용한 경우 정확한 유효성 체크를 진행하기 위해서는
띄어쓰기를 모드 지우고 진행해야합니다 그렇기 때문에 ckeditor의 벨류를 담은 객체변수를
repalce를 사용하여 띄어쓰기를 모두 지우고 다른 변수에 담아줍니다.
const cleanQuestionContent = questionContent.replace(/<\/?[^>]+(>|$)/g, '').replace(/\s+/g, '');
위의 초기내용도 마찬가지로 띄어쓰기를 모두 지우고 유효성 체크를 진행하기 위해
replace를 사용하여 문자열을 변수에 담아줍니다.
const regex = /^\d+$/; 제목의 유효성 체크에 사용된 정규식입니다.
if (boardTitle.value.length == 0) {
alert("제목을 입력해주세요");
boardTitle.focus();
return false;
} else if (boardTitle.value.length < 5) {
alert("제목이 너무 짧습니다\n최소 5자 이상으로 제목 내용을 알 수 있게 작성해 주세요");
boardTitle.focus();
return false;
} else if (boardTitle.value.match(regex)) {
alert("질문 제목을 상세하게 입력해주세요.");
boardTitle.focus();
return false;
} else if (cleanCheckResult === cleanQuestionContent) {
alert("내용입력해주세요");
CKEDITOR.instances['editor1'].focus();
CKEDITOR.instances.editor1.setData('<br>');
return false;
} else if (cleanCheckResult.length == 0) {
alert("내용입력해주세요");
CKEDITOR.instances['editor1'].focus();
return false;
} else if (cleanCheckResult.length < 5) {
alert("제목이 너무 짧습니다\n최소 5자 이상으로 제목 내용을 알 수 있게 작성해 주세요");
CKEDITOR.instances['editor1'].focus();
return false;
} else if (cleanCheckResult.match(regex)) {
alert("질문 내용을 상세하게 입력해주세요.");
CKEDITOR.instances['editor1'].focus();
return false;
} else if(boardPointPut.value > memberPointPut) {
console.log("boardPointPut,memberPointPut"+boardPointPut.value,memberPointPut);
alert("보유 내공을 확인해주세요.")
betPointInfo.innerHTML = "현재 보유 내공은 "+memberPointPut+"입니다.";
return false;
}
else {
return true;
}
이제 유효성 체크를 진행하고 모두 통과하는 경우 true를 리턴하여
submit을 진행합니다.
}
</script>
- Controller
@GetMapping("/board/save")
public String boardSave(HttpSession session, Model model) {
QnA작성 공간으로 이동시 사용되는 Mapping 입니다.
MemberDTO memberDTO = boardService.findById(session.getAttribute("memberId"));
model.addAttribute("memberDTO", memberDTO);
QnA를 작성하는 공간에서도 Layout을 사용가능하게 만들었기 때문에
사용자의 정보를 함께 넘겨줍니다.
return "/boardPage/boardSave";
}
@PostMapping("/board/save")
public String saveBoard(@ModelAttribute BoardDTO boardDTO, HttpSession session, Model model) throws IOException {
QnA작성을 완료하고 넘어오는 값을 DB로 전달하기 위한 PostMapping입니다.
MemberDTO memberDTO = boardService.findById(session.getAttribute("memberId"));
JSP에서 넘어오는 Board의 정보는 제목 , 내용 , 내공 3가지 뿐이기 때문에
로그인한 사용자의 정보를 가져와 BoardDTO에 정보를 추가합니다.
if(boardDTO.getBoardPoint() != 0){
memberDTO.setMemberPoint(memberDTO.getMemberPoint()-boardDTO.getBoardPoint());
memberService.memberPointUpdate(memberDTO); // 여기부터
}
사용자가 게시글에 Point(내공)을 걸었을 경우 사용된 Point만큼
사용자의 보유 point를 차감하고 사용자의 정보를 업데이트합니다.
boardDTO.setBoardWriter(memberDTO.getMemberEmail());
boardDTO.setMemberId(memberDTO.getId());
작성인 , 작성인의 ID를 BoardDTO에 추가해줍니다.
boardService.saveBoard(boardDTO);
이렇게 모든 정보를 조합하여 DB까지 이동합니다.
model.addAttribute("memberDTO", memberDTO);
사용자의 화면을 인덱스로 돌리기 전 Layout에 필요한 사용자의 정보를 model에 담아
함께 이동합니다.
return "redirect:/";
redirect를 사용하여 사용자가 방금 올린 게시글을 출력해줍니다.
}
- Service
이미지 게시에 대한 부분은 ckeditor를 사용하였기 때문에
따로 알아보는 시간을 가져야 했지만 프로젝트 시간에 쫓겨 수정하지 못하였습니다.ㅠ
public void saveBoard(BoardDTO boardDTO) throws IOException {
if (boardDTO.getBoardFile().get(0).isEmpty()) {
boardDTO.setFileAttached(0);
boardRepository.saveBoard(boardDTO);
}
원래는 사진이 등록 가능한 구조로 설계를 했기 때문에
이부분을 만들 당시 약간의 틀을 잡아 놨습니다
boardDTO의 BoardFile의 0번째 칸이 비었다면
FileAttached를 0으로 수정하고 BoardDTO를 save합니다
}
- Repository
public BoardDTO saveBoard(BoardDTO boardDTO) {
sql.insert("naverBoard.saveBoard", boardDTO);
return boardDTO;
Repository에서는 Mapper까지 받아온 값을 전달합니다
이부분의 리턴DTO도 파일 업로드를 위해 만들어둔 뼈대입니다.
}
- Mapper
<insert id="saveBoard" parameterType="board" useGeneratedKeys="true" keyProperty="id">
insert into board_table(boardTitle,boardWriter,boardContents,fileAttached,memberId,boardPoint)
value (#{boardTitle},#{boardWriter},#{boardContents},#{fileAttached},#{memberId},#{boardPoint})
쿼리문을 작성하고 keyPrperty id로 파일이 있을 시 사용될 DTO를 반환하게 하였습니다.
결국 사용하지 못했습니다.
</insert>
'나의 수업일지' 카테고리의 다른 글
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (8) (0) | 2023.06.11 |
---|---|
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (7) (0) | 2023.06.11 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (5) (0) | 2023.06.09 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (4) (0) | 2023.06.07 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (3) (0) | 2023.06.07 |
- QnA 글작성


오른쪽이 네이버의 QnA글작성 페이지 왼쪽에 클론코딩으로 만든 페이지
스마트에디터 api를 사용하고 싶었지만 사용방법도 모를 뿐더러
네이버 api사이트를 들어가보니 개발자 등록을 해야하더라...
그래서 쓸만한 에디터 없나 찾아보다가 ckeditor 에디터를 사용했다
- boardSave.jsp - script
ckeditor를 cdn주소로 가져와 사용했기 때문에
placeholder를 적용하는 방법도 몰라 고생이 많았습니다
ck에디터 플러그인
혹시나 ckeditor를 처음 사용한다면 한번 읽어보시길 추천드립니다.
(cdn으로 사용할 시 적용이 안되는 부분도 있음! 예 : placeholder )
Class Config (CKEDITOR.config) - CKEditor 4 API docs
CKEditor 4 API Documentation. The Class Config. Stores default configuration settings. Changes to this object are reflected in all editor instances, if not specified otherwise for a particular instance. Read more about setting CKEditor configuration in the
ckeditor.com
<script>
let boardPointPut = document.getElementById('boardPointPut');
사용자가 입력한 point(내공)의 값을 가져오기 위한 document객체
const memberPointPut = parseInt('${memberDTO.memberPoint}');
사용자의 보유 포인트 정보를 담은 변수
Long타입으로 필드를 설정했기 때문에 int로 형변환을 진행해야했습니다.
CKEDITOR.replace('editor1', {
height: '350px',
language: 'ko',
enterMode: CKEDITOR.ENTER_BR
});
CKDEITOR를 적용하기 위해서는 textarea에 id로 ckeditor를 적용해주어야 합니다
하지만 설정 당시에는 사용방법을 몰랐기 디폴트 값을 몰랐기 때문에
editor1로 이름을 변경하여 적용하엿습니다
안에는 따로 stlye을 적용한 모습입니다
enterMode의 경우 사용자가 엔터(줄바꿈)를 입력했을때 자동으로 뒤에 br을 적용해줍니다
CKEDITOR.instances['editor1'].on('contentDom', function () {
this.document.on('click', function (event) {
CKEDITOR.instances.editor1.setData('<br>');
});
});
CKEDITOR에디터에 클릭이벤트를 적용하는 방법입니다 instances를 사용하여
따로 주입을해주어야합니다 cdn으로 사용했기 떄문에 placeholder을 적용할 수 없어
내용을 작성하기위해 ckeditor영역을 클릭하는 경우,
초기에 작성되어있는 정보를 지워줍니다.
const back = () => {
location.href = "/";
}
const boardCheck = () => { 게시글 작성의 유효성을 체크해주는 함수
const boardTitle = document.getElementById('boardTitle');
글 제목을 입력받는 input태그의 객체
const checkResult = CKEDITOR.instances.editor1.getData();
사용자가 editor에 입력한 값을 가져오기 위해 getDate메소드를 사용
const betPointInfo = document.getElementById('betPointInfo');
사용자가 입력한 내공이 보유내공보다 많은 경우
문제가 있음을 공지하기 위한 result태그
const questionContent = `
궁금한 내용을 질문해 주세요.
<br>
<br>
질문 시 이런 점을 주의해 주세요!
<br>
- 답변이 등록되면 질문 수정/삭제가 불가합니다.
<br>
- 질문 내용에 개인정보(실명, 전화번호, 메신저 및 네이버 아이디)가 포함되지 않게 해 주세요.
<br>
- 의료 질문 관련하여 의료인은 SNS 상담을 하지 않고, 신체 사진/동영상을 요구하지 않습니다.
<br>
악의적인 범죄로 이어질 수 있으니 유의해 주세요.
<br>
피해를 입으셨다면, 서비스에 신고, 112 또는 사이버경찰청(www.police.go.kr)으로 신고 부탁드립니다.
`;
placeholder을적용할 수 없기 때문에 미리 안애 내용을 적어두고 클릭하면
비우는 형식으로 사용했습니다 그렇기 때문에 사용자가 제목만 적고 등록하는 상황을
방지하고자 처음에 작성되어 있는 내용을 그대로 등록하는 경우에 대한 유효성를 위한
문자열 변수를 따로 만들어 주었습니다.
const cleanCheckResult = checkResult.replace(/<\/?[^>]+(>|$)/g, '').replace(/\s+/g, '');
ckeditor를 사용한 경우 정확한 유효성 체크를 진행하기 위해서는
띄어쓰기를 모드 지우고 진행해야합니다 그렇기 때문에 ckeditor의 벨류를 담은 객체변수를
repalce를 사용하여 띄어쓰기를 모두 지우고 다른 변수에 담아줍니다.
const cleanQuestionContent = questionContent.replace(/<\/?[^>]+(>|$)/g, '').replace(/\s+/g, '');
위의 초기내용도 마찬가지로 띄어쓰기를 모두 지우고 유효성 체크를 진행하기 위해
replace를 사용하여 문자열을 변수에 담아줍니다.
const regex = /^\d+$/; 제목의 유효성 체크에 사용된 정규식입니다.
if (boardTitle.value.length == 0) {
alert("제목을 입력해주세요");
boardTitle.focus();
return false;
} else if (boardTitle.value.length < 5) {
alert("제목이 너무 짧습니다\n최소 5자 이상으로 제목 내용을 알 수 있게 작성해 주세요");
boardTitle.focus();
return false;
} else if (boardTitle.value.match(regex)) {
alert("질문 제목을 상세하게 입력해주세요.");
boardTitle.focus();
return false;
} else if (cleanCheckResult === cleanQuestionContent) {
alert("내용입력해주세요");
CKEDITOR.instances['editor1'].focus();
CKEDITOR.instances.editor1.setData('<br>');
return false;
} else if (cleanCheckResult.length == 0) {
alert("내용입력해주세요");
CKEDITOR.instances['editor1'].focus();
return false;
} else if (cleanCheckResult.length < 5) {
alert("제목이 너무 짧습니다\n최소 5자 이상으로 제목 내용을 알 수 있게 작성해 주세요");
CKEDITOR.instances['editor1'].focus();
return false;
} else if (cleanCheckResult.match(regex)) {
alert("질문 내용을 상세하게 입력해주세요.");
CKEDITOR.instances['editor1'].focus();
return false;
} else if(boardPointPut.value > memberPointPut) {
console.log("boardPointPut,memberPointPut"+boardPointPut.value,memberPointPut);
alert("보유 내공을 확인해주세요.")
betPointInfo.innerHTML = "현재 보유 내공은 "+memberPointPut+"입니다.";
return false;
}
else {
return true;
}
이제 유효성 체크를 진행하고 모두 통과하는 경우 true를 리턴하여
submit을 진행합니다.
}
</script>
- Controller
@GetMapping("/board/save")
public String boardSave(HttpSession session, Model model) {
QnA작성 공간으로 이동시 사용되는 Mapping 입니다.
MemberDTO memberDTO = boardService.findById(session.getAttribute("memberId"));
model.addAttribute("memberDTO", memberDTO);
QnA를 작성하는 공간에서도 Layout을 사용가능하게 만들었기 때문에
사용자의 정보를 함께 넘겨줍니다.
return "/boardPage/boardSave";
}
@PostMapping("/board/save")
public String saveBoard(@ModelAttribute BoardDTO boardDTO, HttpSession session, Model model) throws IOException {
QnA작성을 완료하고 넘어오는 값을 DB로 전달하기 위한 PostMapping입니다.
MemberDTO memberDTO = boardService.findById(session.getAttribute("memberId"));
JSP에서 넘어오는 Board의 정보는 제목 , 내용 , 내공 3가지 뿐이기 때문에
로그인한 사용자의 정보를 가져와 BoardDTO에 정보를 추가합니다.
if(boardDTO.getBoardPoint() != 0){
memberDTO.setMemberPoint(memberDTO.getMemberPoint()-boardDTO.getBoardPoint());
memberService.memberPointUpdate(memberDTO); // 여기부터
}
사용자가 게시글에 Point(내공)을 걸었을 경우 사용된 Point만큼
사용자의 보유 point를 차감하고 사용자의 정보를 업데이트합니다.
boardDTO.setBoardWriter(memberDTO.getMemberEmail());
boardDTO.setMemberId(memberDTO.getId());
작성인 , 작성인의 ID를 BoardDTO에 추가해줍니다.
boardService.saveBoard(boardDTO);
이렇게 모든 정보를 조합하여 DB까지 이동합니다.
model.addAttribute("memberDTO", memberDTO);
사용자의 화면을 인덱스로 돌리기 전 Layout에 필요한 사용자의 정보를 model에 담아
함께 이동합니다.
return "redirect:/";
redirect를 사용하여 사용자가 방금 올린 게시글을 출력해줍니다.
}
- Service
이미지 게시에 대한 부분은 ckeditor를 사용하였기 때문에
따로 알아보는 시간을 가져야 했지만 프로젝트 시간에 쫓겨 수정하지 못하였습니다.ㅠ
public void saveBoard(BoardDTO boardDTO) throws IOException {
if (boardDTO.getBoardFile().get(0).isEmpty()) {
boardDTO.setFileAttached(0);
boardRepository.saveBoard(boardDTO);
}
원래는 사진이 등록 가능한 구조로 설계를 했기 때문에
이부분을 만들 당시 약간의 틀을 잡아 놨습니다
boardDTO의 BoardFile의 0번째 칸이 비었다면
FileAttached를 0으로 수정하고 BoardDTO를 save합니다
}
- Repository
public BoardDTO saveBoard(BoardDTO boardDTO) {
sql.insert("naverBoard.saveBoard", boardDTO);
return boardDTO;
Repository에서는 Mapper까지 받아온 값을 전달합니다
이부분의 리턴DTO도 파일 업로드를 위해 만들어둔 뼈대입니다.
}
- Mapper
<insert id="saveBoard" parameterType="board" useGeneratedKeys="true" keyProperty="id">
insert into board_table(boardTitle,boardWriter,boardContents,fileAttached,memberId,boardPoint)
value (#{boardTitle},#{boardWriter},#{boardContents},#{fileAttached},#{memberId},#{boardPoint})
쿼리문을 작성하고 keyPrperty id로 파일이 있을 시 사용될 DTO를 반환하게 하였습니다.
결국 사용하지 못했습니다.
</insert>
'나의 수업일지' 카테고리의 다른 글
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (8) (0) | 2023.06.11 |
---|---|
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (7) (0) | 2023.06.11 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (5) (0) | 2023.06.09 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (4) (0) | 2023.06.07 |
인천 일보 아카데미 58~67일 차 개인 프로젝트 - NAVER 지식in 클론 코딩 (3) (0) | 2023.06.07 |