https://devjunyeok.tistory.com/193
답변형 게시판 구현 (2)
https://devjunyeok.tistory.com/192 답변형 게시판 만들기 (1) 네이버 카페 등의 게시판을 주로 보면 질문에 대한 답글이 게시되어 있는 기능을 종종 볼 수 있습니다. 이번에는 답글을 달 수 있는 게시판을
devjunyeok.tistory.com
📝 실행결과

지난 글에 이어 이번에는 게시글 생성 기능구현을 해보겠습니다.
💻 프론트 작업
게시글 추가 페이지를 만들기 위해 AddReplyBoard.tsx 페이지를 생성합니다.
rfce 단축키를 이용해 함수를 생성하고, return 문 아래에 html 코드를 작성합니다.
// return문 아래에 작성
<div className="row">
{submitted ? (
<div className="col-6 mx-auto">
<h4>You submitted successfully!</h4>
<button className="btn btn-success" onClick={newReplyBoard}>
Add
</button>
</div>
) : (
<>
{/* 제목 start */}
<TitleCom title="Add Reply Board" />
{/* 제목 end */}
<div className="col-6 mx-auto">
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="boardTitle" className="col-form-label">
boardTitle
</label>
</div>
<div className="col-9">
<input
type="text"
id="boardTitle"
required
className="form-control"
value={replyBoard.boardTitle}
onChange={handleInputChange}
placeholder="boardTitle"
name="boardTitle"
/>
</div>
</div>
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="boardContent" className="col-form-label">
boardContent
</label>
</div>
<div className="col-9">
<input
type="text"
id="boardContent"
required
className="form-control"
value={replyBoard.boardContent}
onChange={handleInputChange}
placeholder="boardContent"
name="boardContent"
/>
</div>
</div>
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="boardWriter" className="col-form-label">
boardWriter
</label>
</div>
<div className="col-9">
<input
type="text"
id="boardWriter"
required
className="form-control"
value={replyBoard.boardWriter}
onChange={handleInputChange}
placeholder="boardWriter"
name="boardWriter"
/>
</div>
</div>
<div className="row g-3 mt-3 mb-3">
<button
onClick={saveReplyBoard}
className="btn btn-outline-primary ms-2 col"
>
Submit
</button>
</div>
</div>
</>
)}
</div>
변수 및 함수 정의
return 문 위쪽에 html에 사용된 변수와 함수들을 정의합니다.
// return 문 위에 작성
// todo: 변수 정의
// todo: 객체 초기화
const initialReplyBoard = {
bid: null,
boardTitle: "",
boardContent: "",
boardWriter: "",
viewCnt: 0,
boardGroup: null,
boardParent: 0,
};
// replyBoard 객체
const [replyBoard, setReplyBoard] = useState<IReplyBoard>(initialReplyBoard);
// 저장버튼 클릭후 submitted = true 변경됨
const [submitted, setSubmitted] = useState<boolean>(false);
// todo: 함수 정의
// input 태그에 수동 바인딩
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target; // 화면값
setReplyBoard({ ...replyBoard, [name]: value }); // 변수저장
};
// 저장 함수
const saveReplyBoard = () => {
// 임시 객체
var data = {
boardTitle: replyBoard.boardTitle,
boardContent: replyBoard.boardContent,
boardWriter: replyBoard.boardWriter,
viewCnt: replyBoard.viewCnt,
boardGroup: null, // 입력시 제외
boardParent: 0, // 입력시 제외
};
ReplyBoardService.createBoard(data) // 게시물 저장 요청
.then((response: any) => {
setSubmitted(true);
console.log(response.data);
})
.catch((e: Error) => {
console.log(e);
});
};
// 새폼 보여주기 함수 : 변수값 변경 -> 화면 자동 갱신(리액트 특징)
const newReplyBoard = () => {
setReplyBoard(initialReplyBoard); // replyBoard 초기화
setSubmitted(false); // submitted 변수 초기화
};
프론트 서버를 재시작 후 화면을 테스트합니다.

💻 백엔드 작업
ReplyBoardRepository.java 파일에 게시물 생성(수정) 함수 작성
// 게시물 저장함수 : 최초 생성(board_group(그룹번호)), board_parent(부모번호))
// => board_group(부모번호 == 자식번호(bid)), board_parent(최초생성시 0, 댓글이 달리면 부모 번호가 들어감)
// todo : JPA insert문 직접 작성(DML : 테이블 데이터 변경, 트랜잭션을 동반)
// => @Transactional, @Modifying
// => 예 ) 변수 전달 : :#{#replyBoard.boardTitle}
@Transactional
@Modifying
@Query(value = "INSERT INTO TB_REPLY_BOARD " +
"VALUES(sq_reply_board.nextval, :#{#replyBoard.boardTitle}, " +
":#{#replyBoard.boardContent}, " +
":#{#replyBoard.boardWriter}, " +
"0, sq_reply_board.CURRVAL, 0, 'N', " +
" TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL, NULL)", nativeQuery = true)
int insertByBoard(@Param("replyBoard") ReplyBoard replyBoard);
JPA insert 문을 직접 작성하기 위해서는 @Transactional, @Modifying 어노테이션을 함수위에 붙여줍니다.
그리고 쿼리문을 작성해야 하는데 먼저 SQL Developer에서 테스트 후 정상적으로 데이터가 나오는 것을 확인하여 쿼리문을 복사하여 붙여넣기 합니다.
INSERT INTO TB_REPLY_BOARD
VALUES(sq_reply_board.nextval, '제목', '내용', '홍길동', 0, sq_reply_board.CURRVAL, 0, 'N',
TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL, NULL);

정상적으로 쿼리문을 작성하였다면 위 처럼 메세지가 나옵니다. 테스트용으로 데이터를 insert 하였으므로 ROLLBACK을 시켜줍니다.
ROLLBACK;


SQL Developer의 쿼리문을 ;을 제외한 모든 문장을 복사하여 @Query(value = " ")의 " " 사이에 붙혀넣기합니다.
// 게시물 저장함수 : 최초 생성(board_group(그룹번호)), board_parent(부모번호))
// => board_group(부모번호 == 자식번호(bid)), board_parent(최초생성시 0, 댓글이 달리면 부모 번호가 들어감)
// todo : JPA insert문 직접 작성(DML : 테이블 데이터 변경, 트랜잭션을 동반)
// => @Transactional, @Modifying
// => 예 ) 변수 전달 : :#{#replyBoard.boardTitle}
@Transactional
@Modifying
@Query(value = "INSERT INTO TB_REPLY_BOARD " +
"VALUES(sq_reply_board.nextval, :#{#replyBoard.boardTitle}, " +
":#{#replyBoard.boardContent}, " +
":#{#replyBoard.boardWriter}, " +
"0, sq_reply_board.CURRVAL, 0, 'N', " +
" TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL, NULL)", nativeQuery = true)
int insertByBoard(@Param("replyBoard") ReplyBoard replyBoard);
변수를 전달하기 위해 '제목', '내용', '홍길동' 부분을 아래와 같이 수정합니다.

ReplyBoardService에 함수 추가
// 게시물 저장
public int saveBoard(ReplyBoard replyBoard) {
int insertCount = replyBoardRepository.insertByBoard(replyBoard);
return insertCount;
}
ReplyBoardController에 함수추가
// 게시글 저장
@PostMapping("/reply-board")
public ResponseEntity<Object> createBoard(@RequestBody ReplyBoard replyBoard) {
try {
int insertCount = replyBoardService.saveBoard(replyBoard); // db 저장
return new ResponseEntity<>(insertCount, HttpStatus.OK);
} catch (Exception e) {
// DB 에러가 났을경우 : INTERNAL_SERVER_ERROR 프론트엔드로 전송
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
💻 프론트 + 백엔드 테스트
'Spring Boot > 스프링부트 예제' 카테고리의 다른 글
[Spring Security] BCryptPasswordEncoder를 사용한 안전한 비밀번호 처리 방법 (0) | 2025.03.16 |
---|---|
답변형 게시판 구현 (2) (1) | 2023.10.27 |
답변형 게시판 만들기 (1) (1) | 2023.10.26 |
QnA 다양한 검색 게시판 CRUD (1) (1) | 2023.10.24 |
게시판 페이징 처리 (1) | 2023.10.23 |