Spring boot 게시판 만들기(게시판 CRUD)
by 볼빵빵오춘기게시판을 만드는 이유
자바를 배우고 나서 다음 단계를 넘어가려 할 때, 책이나 인터넷에서 자료를 찾아보면 게시판 만들기 예제가 많이 등장하는 것을 알 수 있다.
국비 교육 과정에서도 최종 프로젝트로 게시판을 만들어 포트폴리오로 활용하는 경우가 흔하다.
처음에는 아무 생각 없이 그냥 공부하다 보면 자연스럽게 알게 되겠지 하고 지나쳤던 것 같다.
하지만 다시 생각해보니, 게시판 만들기가 자주 등장하는 데는 이유가 있지 않을까 싶다.
이제 그 이유와 더불어, CRUD의 중요성, 그리고 CRUD를 이용해 게시판을 만드는 이유와 게시판 구현은 어떻게 했는지 정리해본다.
CRUD(Create, Read, Update, Delete)가 중요한 이유
더보기
- 데이터 관리의 기본 기능 제공
대부분의 애플리케이션은 데이터를 다룬다.
예를 들어, 사용자 정보, 상품 목록, 게시물, 댓글 등 다양한 데이터를 다루는데, 이 데이터를 관리하기 위해서는 생성, 읽기, 수정, 삭제가 필요하다.
CRUD는 이러한 데이터 관리의 기본적인 요구사항을 충족시킨다. - 유연한 데이터 조작 가능
CRUD 기능을 통해 사용자는 데이터를 쉽게 추가, 조회, 수정, 삭제할 수 있다.
이는 데이터의 유연한 관리를 가능하게 하며, 사용자가 요구하는 다양한 데이터 조작 작업을 지원한다. - 애플리케이션의 기능 확장성
CRUD는 데이터베이스와의 상호작용을 위한 기본적인 인터페이스를 제공한다.
이를 기반으로 추가적인 기능을 구현하거나 시스템을 확장할 수 있다.
예를 들어, 필터링, 정렬, 검색 등의 기능도 CRUD 작업을 기반으로 쉽게 구현할 수 있다. - 표준화된 인터페이스 제공
대부분의 개발자들이 CRUD 개념에 익숙하기 때문에, 이를 사용하면 코드의 가독성과 유지보수성이 향상된다.
이는 개발팀 간의 협업을 원활하게 하고, 새로운 팀원이 프로젝트에 쉽게 적응할 수 있도록 도와준다. - 데이터 무결성 보장
CRUD를 통해 데이터의 생성부터 삭제까지 일관된 작업을 수행할 수 있다.
이를 통해 데이터의 무결성을 유지하고, 시스템의 신뢰성을 높일 수 있다.
CRUD를 이용해 게시판을 만드는 이유
더보기
- 실전 같은 학습 경험
- 기본적인 웹 애플리케이션 구조 이해
게시판은 대부분의 웹 애플리케이션에서 필요한 기능들을 작은 범위에서 경험할 수 있는 좋은 예시이다.
사용자는 게시판을 통해 데이터베이스와의 상호작용, 사용자 입력 처리, 화면 렌더링 등 다양한 기술을 종합적으로 학습할 수 있다. - 웹 개발 전반에 대한 이해: 게시판 프로젝트는 프론트엔드(HTML, CSS, JavaScript)와 백엔드(서버, 데이터베이스)의 연동을 포함하여, 전반적인 웹 개발의 흐름을 이해할 수 있게 도와준다.
- 기본적인 웹 애플리케이션 구조 이해
- CRUD 기능의 실습과 이해
- Create, Read, Update, Delete
게시판은 자연스럽게 CRUD 기능을 구현할 수 있는 주제이다.
사용자는 게시판에서 글을 작성(Create), 조회(Read), 수정(Update), 삭제(Delete)하며, 각 기능이 어떻게 작동하는지 직접 경험하게 된다.
이 과정에서 데이터베이스와의 상호작용, 입력 검증, 트랜잭션 관리 등을 실습할 수 있다.
- Create, Read, Update, Delete
- 프로젝트 완성도 높이기
- 포트폴리오에 적합
게시판은 작은 규모의 프로젝트이지만, 여러 가지 중요한 기능을 포함하고 있어 포트폴리오로 적합하다.
특히, 이를 통해 얻은 결과물은 취업 준비나 면접에서 유용하게 사용될 수 있다. - 확장 가능성
게시판은 기본적인 CRUD 기능에서 시작하여, 이후에 페이징, 검색, 권한 관리, 댓글 기능 등 다양한 기능을 추가하면서 확장할 수 있다.
이를 통해 프로젝트를 점진적으로 개선해 나갈 수 있는 경험을 얻게 된다.
- 포트폴리오에 적합
- 현실적인 문제 해결 경험
- 실제 웹 애플리케이션에서 발생할 수 있는 문제를 해결
게시판 프로젝트를 진행하면서 다양한 문제에 부딪히게 된다.
예를 들어, 입력 검증, 데이터 무결성 유지, 동시성 이슈 등의 문제를 해결하면서 현실적인 프로그래밍 문제를 경험하게 된다. - 보안과 성능 고려
게시판을 만들 때는 사용자 인증, 권한 관리, 데이터 보호 등 보안 문제도 고려해야 한다.
이를 통해 보안에 대한 기본적인 이해를 높일 수 있다.
- 실제 웹 애플리케이션에서 발생할 수 있는 문제를 해결
- 커뮤니티와 협업 경험
- 다른 개발자들과 코드 리뷰 및 협업: 게시판 프로젝트는 다양한 개발자들과 공유하기 쉽고, 코드 리뷰를 통해 피드백을 받을 수 있는 좋은 주제이다.
이를 통해 협업 능력도 키울 수 있다.
- 다른 개발자들과 코드 리뷰 및 협업: 게시판 프로젝트는 다양한 개발자들과 공유하기 쉽고, 코드 리뷰를 통해 피드백을 받을 수 있는 좋은 주제이다.
글쓰기(Create)
controller
// 글쓰기 Form
@GetMapping("/communityW")
public String writeForm(){
return "community/write";
}
write.html
- 기본 글쓰기 부분이라 제목과 내용정도만 입력하도록 하였고 제목과 내용은 required을 하여 필수 입력사항으로 했다.
- 파일은 파일추가 버튼과 파일삭제버튼을 눌러 갯수를 유동적으로 넣을 수 있도록 했다.(bbsAddFile.js 연결한 이유)
<div class="col-12 mb-15">
<form method="post" action="/communityW" enctype="multipart/form-data">
<input type="hidden" name="username" Id="username" placeholder="username" th:value="${#authentication.principal.username}" >
<div class="form-group">
<label for="title">제목</label><br>
<input type="text" name="title" Id="title" placeholder="제목을 입력해주세요." required class="width100p">
</div>
<div class="form-group">
<label for="content">내용</label>
<div>
<textarea name="content" id="content" style="width:100%;" rows="3" placeholder="내용을 입력해주세요." required></textarea>
</div>
</div>
<div class="form-group">
<label>파일 - 이미지 첨부(png, jpeg, jpg만 가능합니다.)</label> <span id="btn-addFile">+</span>
</div>
<div id="inputFile">
</div>
<button class="btn bg_03A3F1 color-fff" type="submit">글쓰기 완료</button>
</form>
</div>
bbsAddFile.js
html에서 + 버튼을 누르면 파일첨부할 수 있는 input type=’file’를 추가해주도록 만들었고, 추가된 + 버튼 옆에 x 버튼을 만들어 파일첨부를 안할시 삭제하도록 처리하도록 하였다.
let index = {
init: function() {
$("#btn-addFile").on("click", () => {
this.addFile();
});
// 이벤트 위임 적용
$("#inputFile").on("click", ".btn-removeFile", function() {
index.removeParentFormGroup($(this));
});
$("#inputFile").on("click", ".btn-hiddenFile", function() {
index.hiddenParentFormGroup($(this));
});
},
addFile: function() {
var addContent = "";
addContent += '<div class="form-group">'
+ '<input name="files" type="file" accept="image/png, image/jpeg, image/jpg" required>'
+ '<span class="btn-removeFile">x</span>'
+ '</div>';
$("#inputFile").append(addContent);
},
removeParentFormGroup: function(button) {
button.closest("div.form-group").remove();
},
hiddenParentFormGroup: function(button) {
button.closest("div.form-group").addClass("d-none");
}
}
index.init();
Controller
받아온 file들과 communityDto를 service에 save() 파라미터로 넘겨준다.
// 글 작성
@PostMapping("/communityW")
public String write(CommunityDto communityDto, List<MultipartFile> files,Model model) throws Exception{
commnunityService.save(communityDto,files);
model.addAttribute("message", "글 작성이 완료되었습니다.");
model.addAttribute("searchUrl", "/communityL");
return "message/message";
}
Service
파일을 유동적으로 첨부할 수 있도록 해놓았기 때문에 파일이 있는 경우와 없는 경우를 나누었다.
@Autowired
private CommunityReplyRepository communityReplyRepository;
public void save(CommunityDto communityDto, List<MultipartFile> files) throws Exception{
CommunityEntity communityEntity = CommunityEntity.toSaveCommnunityEntity(communityDto);
// 여러 개의 파일을 가져올 경우
if(files!=null){ // 파일이 있을 경우
communityEntity.setFileAttached(1);
CommunityEntity saveEntitiy = commnunityRepository.save(communityEntity);
// 파일 이름가져오기
for(MultipartFile file : files){
// 원본 이름
String originalFileName = file.getOriginalFilename();
// 확장자 가져오기
String[] extension = originalFileName.split("[.]");
int lastIdx = extension.length-1;
// 저장용 파일 이름 만들기
UUID uuid = UUID.randomUUID();
String storedFileName = System.currentTimeMillis()+"-"+uuid+"."+extension[lastIdx];
// boardFile DB에 넣을 BoardFileEntity 세팅
BoardFileEntity boardFileEntity = new BoardFileEntity();
boardFileEntity.setBoardId(saveEntitiy.getId());
boardFileEntity.setOriginalFileName(originalFileName);
boardFileEntity.setStoredFileName(storedFileName);
boardFileEntity.setBoard("community");
// 파일 저장용 폴더
String savePath = System.getProperty("user.dir")+"/src/main/resources/static/img/community";
// 파일 저장
File saveFile = new File(savePath,storedFileName);
file.transferTo(saveFile);
// boardFile DB에 넣음
boardFileRepository.save(boardFileEntity);
}
}else{ // 파일이 없을 경우
communityEntity.setFileAttached(0);
commnunityRepository.save(communityEntity);
}
}
글 상세페이지(Read)
Controller
글 상세페이지는 게시글과 그 게시물과 연관되어있는 댓글들을 보여준다.
(※ 댓글은 댓글 crud 참고)
// 글 상세
@GetMapping("/communityV/{id}")
public String view(Model model,@PathVariable int id){
// community에서 글 정보 가져오기
CommunityEntity communityEntity = commnunityService.view(id);
model.addAttribute("board", communityEntity);
// 저장 파일 가져오기
List<BoardFileEntity> boardFiles = commnunityService.getFileList(id); // Entity로 변경하기
model.addAttribute("boardFileList",boardFiles);
// 댓글 가져오기
// 댓글은 가져올 필요x
// communityEntity를 보면
// private List<CommunityReplyEntity> reply;
// 이 부분을 통해 reply는 board 검색 시 그냥 가지고 오게 된다.
// 그렇기 때문에 따로 가져오는 로직을 만들 필요가 없다.
// 여기서 내가 말한 로직이란 service -> repository로 가는 로직을 말한다.
List<CommunityReplyEntity> replies = communityEntity.getReply();
// 각 댓글 내용 출력 model에 담기
model.addAttribute("replies",replies);
return "community/view";
}
Service
게시글의 내용과 제목, 파일 등 정보를 가져오면서 해당 글을 작성한 이가 아니면 조회수를 올라가도록 처리했다.
@Transactional
public CommunityEntity view(int id){
CommunityEntity communityEntity = commnunityRepository.findById(id).get();
// 조회수 올리기
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated() && authentication.getPrincipal() instanceof UserDetails) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
String username = userDetails.getUsername();
// 로그인한 사용자의 정보를 이용하여 처리
if(username!=communityEntity.getUsername()){
commnunityRepository.updateConunt(id);
// 위의 코드에 @transation을 이용하여 바로 DB는 변경이 되나 view페이지에는 +1 된 숫자로 세팅이 되지않으므로 위에 가져온 communityEntity의 count를 +1해서 다시 세팅해준다.
// 사용자가 view페이지에 들어오는 순간 작성자가 아니라면 바로 +1해서 보여준다.
communityEntity.setCount(communityEntity.getCount()+1);
}
}else {
// 기획안에 첨은에는 로그인한 회원만을 하였기 떄문에 비로그인사용자는 따로 추가 처리하지는 않았다.
commnunityRepository.updateConunt(id);
communityEntity.setCount(communityEntity.getCount()+1);
}
return commnunityRepository.findById(id).get();
}
Repository
// 조회수 올리기
@Modifying
@Query(value=" update CommunityEntity c set c.count = c.count + 1 where c.id = :id ")
void updateConunt(@Param("id") int id);
글 수정(Update)
controller
// 글 수정 Form
@GetMapping("/communityU/{id}")
public String updateForm(@PathVariable int id,Model model){
CommunityEntity communityEntity = commnunityService.updateForm(id);
// 등록되어있던 글 정보 가져오기
model.addAttribute("board",communityEntity);
// 등록되어있던 글에 파일 정보 가져오기
if(communityEntity.getFileAttached()==1){
List<BoardFileEntity> boardFiles = commnunityService.getFileList(id); // Entity로 변경해서 받아오기
model.addAttribute("boardFileList",boardFiles);
}
return "community/update";
}
update.html
- write.html 과 동일하나 input value에 값을 가져오고 파일에도 첨부된 파일은 보여주도록 처리했다.
- 수정 시에도 파일은 유동적이로 첨부할 수 있기에 bbsAddFile.js 연결했다.
<form method="post" action="/communityU" enctype="multipart/form-data">
<input type="hidden" name="id" Id="id" placeholder="id" th:value="${board.id}" >
<input type="hidden" name="count" Id="count" placeholder="count" th:value="${board.count}" >
<input type="hidden" name="username" Id="username" placeholder="username" th:value="${#authentication.principal.username}" >
<div class="form-group">
<label for="title">제목</label><br>
<input type="text" name="title" Id="title" th:value="${board.title}" placeholder="제목을 입력해주세요." class="width100p" required>
</div>
<div class="form-group">
<label for="content">내용</label>
<div>
<textarea name="content" id="content" style="width:100%;" th:utext="${board.content}" rows="3" placeholder="내용을 입력해주세요." required></textarea>
</div>
</div>
<div class="form-group">
<label>파일 - 이미지 첨부(png, jpeg, jpg만 가능합니다.)</label> <span id="btn-addFile">+</span>
</div>
<div id="inputFile">
<div class="form-group" th:if="${board.fileAttached}==1" th:each="boardFile : ${boardFileList}">
<input name="files" type="file" accept="image/png, image/jpeg, image/jpg" ><br>
첨부 되어있는 파일 : <span th:text="${boardFile.originalFileName}"></span><br>
<input name="del" type="checkbox" th:value="${boardFile.id}">첨부 파일 삭제
</div>
</div>
<button class="btn bg_03A3F1 color-fff" type="submit">수정 완료</button>
</form>
controller
// 글 수정
@PostMapping("/communityU")
public String update(CommunityDto communityDto, List<MultipartFile> files, @RequestParam (required = false) List<Integer> del,Model model,@AuthenticationPrincipal PrincipalDetails principalDetails) throws Exception{
commnunityService.update(communityDto,files,del);
return "redirect:/communityL";
}
Service
- 파일 첨부와 첨부된 파일삭제조건에 따라 실행되도록 했다.
- 조건은 다음과 같다.
- 새로 들어온 파일이 없고 삭제도 없을 때 => 파일 변경 없음.
- 새로 들어온 파일이 없고 삭제만 있을 때
- 새로 들어온 파일만 있고 삭제는 없을 때
- 새로 들어온 파일이 있고 삭제도 있을 때
@Transactional
public void update(CommunityDto communityDto,List<MultipartFile> files,List<Integer> del)throws Exception{
CommunityEntity communityEntity = CommunityEntity.toUpdateCommnunityEntity(communityDto);
// 조건
// 새로 들어온 파일이 없고 삭제도 없을 때 => 파일 변경 없음.
// 새로 들어온 파일이 없고 삭제만 있을 때
// 새로 들어온 파일만 있고 삭제는 없을 때
// 새로 들어온 파일이 있고 삭제도 있을 때
// 조건을 정리하자면
// 1. del 삭제해야할 파일이 있는가? => 있다면 orginalName, storedName 을 공백으로 변경
if(del !=null){
for(Integer id : del){
boardFileRepository.gapUpdate(id);
}
}
// 2. 새로 들어온 파일이 있는가?
// 기존 파일 List
List<BoardFileEntity> boardFileEntities = boardFileRepository.findByBoardIdAndBoard(communityDto.getId(), "community");
// 2-1. 조건1. 기존파일 리스트 길이보다 작거나 같은경우 새로운 파일로 update
if(files != null){ // 새 파일이 있을 때
if(files.size()<=boardFileEntities.size()){
for(int i=0;i<boardFileEntities.size();i++){
// 기존 해당 인덱스 부분에 새로운 파일을 set해주기
MultipartFile file = files.get(i);
if (!file.getOriginalFilename().isEmpty()) {
// 원본 이름
String originalFileName = file.getOriginalFilename();
// 확장자 가져오기
String[] extension = originalFileName.split("[.]");
int lastIdx = extension.length-1;
// 저장용 파일 이름 만들기
UUID uuid = UUID.randomUUID();
String storedFileName = System.currentTimeMillis()+"-"+uuid+"."+extension[lastIdx];
BoardFileEntity newFile = new BoardFileEntity();
newFile.setId(boardFileEntities.get(i).getId());
newFile.setBoard(boardFileEntities.get(i).getBoard());
newFile.setBoardId(boardFileEntities.get(i).getBoardId());
newFile.setOriginalFileName(originalFileName);
newFile.setStoredFileName(storedFileName);
boardFileEntities.set(i,newFile);
// 파일 저장용 폴더
String savePath = System.getProperty("user.dir")+"/src/main/resources/static/img/community";
// 파일 저장
File saveFile = new File(savePath,storedFileName);
file.transferTo(saveFile);
}
}
for(BoardFileEntity file : boardFileEntities){
boardFileRepository.update(file.getId(),file.getOriginalFileName(),file.getStoredFileName());
}
}else{ // 2-2. 조건2. 기존파일 리스트 길이보다 크다. 큰 부분만 insert
for(int i=0;i<boardFileEntities.size();i++){
// 기존 해당 인덱스 부분에 새로운 파일을 set해주기
MultipartFile file = files.get(i);
if (!file.getOriginalFilename().isEmpty()) {
// 원본 이름
String originalFileName = file.getOriginalFilename();
// 확장자 가져오기
String[] extension = originalFileName.split("[.]");
int lastIdx = extension.length-1;
// 저장용 파일 이름 만들기
UUID uuid = UUID.randomUUID();
String storedFileName = System.currentTimeMillis()+"-"+uuid+"."+extension[lastIdx];
BoardFileEntity newFile = new BoardFileEntity();
newFile.setId(boardFileEntities.get(i).getId());
newFile.setBoard(boardFileEntities.get(i).getBoard());
newFile.setBoardId(boardFileEntities.get(i).getBoardId());
newFile.setOriginalFileName(originalFileName);
newFile.setStoredFileName(storedFileName);
boardFileEntities.set(i,newFile);
// 파일 저장용 폴더
String savePath = System.getProperty("user.dir")+"/src/main/resources/static/img/community";
// 파일 저장
File saveFile = new File(savePath,storedFileName);
file.transferTo(saveFile);
}
}
for(BoardFileEntity file : boardFileEntities){
boardFileRepository.update(file.getId(),file.getOriginalFileName(),file.getStoredFileName());
}
for(int i = boardFileEntities.size();i <files.size();i++){
MultipartFile file = files.get(i);
// 원본 이름
String originalFileName = file.getOriginalFilename();
// 확장자 가져오기
String[] extension = originalFileName.split("[.]");
int lastIdx = extension.length-1;
// 저장용 파일 이름 만들기
UUID uuid = UUID.randomUUID();
String storedFileName = System.currentTimeMillis()+"-"+uuid+"."+extension[lastIdx];
// boardFile DB에 넣을 BoardFileEntity 세팅
BoardFileEntity boardFileEntity = new BoardFileEntity();
boardFileEntity.setBoardId(communityDto.getId());
boardFileEntity.setOriginalFileName(originalFileName);
boardFileEntity.setStoredFileName(storedFileName);
boardFileEntity.setBoard("community");
// 파일 저장용 폴더
String savePath = System.getProperty("user.dir")+"/src/main/resources/static/img/community";
// 파일 저장
File saveFile = new File(savePath,storedFileName);
file.transferTo(saveFile);
// boardFile DB에 넣음
boardFileRepository.save(boardFileEntity);
}
}
}
// 3. DB에서 orginalName, storedName 을 공백인 컬럼은 삭제한다.
boardFileRepository.deleteFile(communityDto.getId(),"community");
// 4. 해당 게시물에 첨부파일이 있다면 community BBS table에 fileattached 를 1로 바꿔준다.
boardFileEntities = boardFileRepository.findByBoardIdAndBoard(communityDto.getId(), "community");
if(boardFileEntities == null || boardFileEntities.isEmpty()){
communityEntity.setFileAttached(0);
}else{
communityEntity.setFileAttached(1);
}
commnunityRepository.save(communityEntity);
}
Repository
// 파일 변경
// 첨부되어있는 파일 삭제에 체크박스에 체크 했을 경우
@Modifying
@Query(value=" update BoardFileEntity c set c.originalFileName = '', c.storedFileName = '' where c.id = :id ")
void gapUpdate(@Param("id") int id);
// 파일 변경
// 첨부되어있는 파일 삭제에 체크 했고 새 파일을 넣을 경우 or 해당 새 파일 넣는 경우 => 새 파일을 넣는 경우
@Modifying
@Query(value=" update BoardFileEntity c set c.originalFileName = :originalFileName, c.storedFileName = :storedFileName where c.id = :id ")
void update(@Param("id") int id,@Param("originalFileName") String originalFileName,@Param("storedFileName") String storedFileName);
// 첨부되어있는 파일 삭제에 체크박스에 체크 했고 새 파일이 들어오지않은경우 - c.originalFileName = '' and c.storedFileName = '' 빈칸으로 변경했고 불필요한 데이터이므로 삭제
// 두 번 나눠서 삭제이유는 기존 파일은 냅둬야하기때문에 구분하고자 '' 으로 변경 후 삭제함.
@Modifying
@Query(value=" delete BoardFileEntity c where c.boardId = :boardId and c.originalFileName = '' and c.storedFileName = '' and c.board = :board ")
void deleteFile(@Param("boardId") int boardId,@Param("board") String board);
// boardId 와 board를 통해 파일 정보 삭제
// boardId만 있을 경우 다른 테이블에 pk인 boardId도 삭제 될 수 있기 때문에 board(protect,shelter,community)인지 확인
@Modifying
@Query(value=" delete BoardFileEntity c where c.boardId = :boardId and c.board = :board")
void deleteBbsFile(@Param("boardId") int boardId,@Param("board") String board);
글 삭제(Delete)
Controller
//글 삭제
@GetMapping("/communityD/{id}")
public String delete(@PathVariable int id,Model model){
// community table에서 해당 글 삭제하기
// reply tabled에서 해당 글과 관련된 댓글 삭제하기
// entity만들 때 연관관계에 의해 reply table에서 댓글은 같이 삭제된다.
// file table에서 해당 게시글에 연관된 파일 삭제하기 file table에서 boardID 가 매개변수로 받은 id와 같으면 삭제하기
commnunityService.delete(id);
model.addAttribute("message", "글이 삭제가 되었습니다.");
model.addAttribute("searchUrl", "/communityL");
return "message/message";
}
Service
// 글 삭제
@Transactional
public void delete(int id){
commnunityRepository.deleteById(id);
boardFileRepository.deleteBbsFile(id,"community");
// 실질적 저장 파일 삭제하기
List<BoardFileEntity> boardFiles = getFileList(id);
if(boardFiles!=null){
for (BoardFileEntity s : boardFiles) {
String srcFileName = null;
String fileName = s.getStoredFileName();
String uploadPath = System.getProperty("user.dir")+"/src/main/resources/static/img/community";
try {
srcFileName = URLDecoder.decode(fileName,"UTF-8");
File file = new File(uploadPath + File.separator + srcFileName); // 매개변수 => 파일경로 이다.
boolean result = file.delete(); // true이면 지우기 성공, false면 지우기 실패
}catch (UnsupportedEncodingException e){
e.printStackTrace();
}
}
}
}
Repository
// boardId 와 board를 통해 파일 정보 삭제
// boardId만 있을 경우 다른 테이블에 pk인 boardId도 삭제 될 수 있기 때문에 board(protect,shelter,community)인지 확인
@Modifying
@Query(value=" delete BoardFileEntity c where c.boardId = :boardId and c.board = :board")
void deleteBbsFile(@Param("boardId") int boardId,@Param("board") String board);
'👩🏻💻 About 프로그래밍 > spring' 카테고리의 다른 글
Spring Data JPA Entity 연관관계 (0) | 2024.08.13 |
---|---|
Spring boot 게시판 댓글 CRUD (0) | 2024.08.13 |
회원가입 유효성 검사 (0) | 2024.08.13 |
Spring Security 로그인 실패 시 에러메세지 (0) | 2024.08.12 |
파일 업로드(하나 업로드 vs 여러 개 업로드) (0) | 2024.07.20 |
블로그의 정보
Hello 춘기's world
볼빵빵오춘기