@PostMapping("/delete")
@ResponseBody
public String delete(@RequestBody int bno, HttpSession session) {
System.out.println("삭제 글 번호 : " + bno);
SnsBoardVO vo = service.getDetail(bno);
UserVO user = (UserVO) session.getAttribute("login");
//로그인을 하지 않았거나, 작성자와 현재 로그인한 id가 일치하지 않는다면
if(user == null || !user.getUserId().equals(vo.getWriter()) ) {
return "noAuth";
}
service.delete(bno);
우선 삭제할 글번호와 로그인한 세션의 정보를 가져온다.
그 후 vo 객체에서 get으로 불러 준 뒤
userVo의 세션 정보와 매핑시킨디ㅏ.
그 후 조건문으로 로그인을 하지않은 경우 에러코드를 ajax쪽으로 보낸다,
그후
//파일 객체를 생성해서 지워지고 있는 게시물의 파일을 지목
File file = new File(vo.getUploadpath()+"\\"+vo.getFilename());
System.out.println("파일 삭제완료");
return file.delete() ? "Success" : "fail"; // 파일 삭제 메소드
파일객체를 생성자로 불러와 지워 지고 있는 게시물의 파일을 지목한다.,
그후 리턴값으로 fail or success를 넣는다.
그 뒤 Service문을 작성한다.
게시물의 정보를 가져올 GetDetail() 과
삭제를 담당하게 될 getDelete를 작성한다.
//파일 객체를 생성해서 지워지고 있는 게시물의 파일을 지목
File file = new File(vo.getUploadpath()+"\\"+vo.getFilename());
System.out.println("파일 삭제완료");
return file.delete() ? "Success" : "fail"; // 파일 삭제 메소드
@Override
public void delete(int bno) {
mapper.delete(bno);
}
mapper.xml을 작성한다.
<delete id="delete">
delete from snsboard
where bno = #{bno}
</delete>
//삭제 처리
//삭제하기 링크를 클릭했을 때 이벤트를 발생 시켜서
//비동기 방식으로 삭제를 진행해 주세요. (삭제 버튼은 한 화면에 여러개 겠죠?)
//서버쪽에서 권한을 확인 해 주세요. (작성자와 로그인 중인 사용자의 id를 비교해서)
//일치하지 않으면 문자열 "noAuth" 리턴, 성공하면 "Success" 리턴.
// url: /snsBoard/delete, method: post
3) 두번째 매개변수(콜백함수)에서 JSON 파일을 이용하여 로드된 데이터를 처리한다. 콜백함수는 로드된 데이터를 인자로 넘겨받는다.(JSON 데이터를 참조하기 위해 data 변수를 사용하고 있다.)
참고하자
을 활용 하여
컨트롤러와 통신을 한다. url을 넣어주고
콜백으로
for문을 돌려 str에 html을 add 시켜준다.
그 후 컨트롤러와 통신을 하여 불러온 list의 값으로
작성자 파일이름, 파일경로 등등을 불러와 준다.
그 후 str이 완료가 되었으면
J-Qurry 함수 html()로 전달을 해준다.
그 후 reset 값이 true로 바뀌면 str의 값은 ''으로 초기화가 된다.
VO 제작
@Getter
@Setter
public class PageVO {
//사용자가 선택한 페이지 정보를 담을 변수.
private int pageNum;
private int countPerPage;
//검색에 필요한 데이터를 변수로 선언.
private String keyword;
private String condition;
public PageVO() {
this.pageNum = 1;
this.countPerPage = 10;
}
}
//게시글의 이미지 파일 전송 요청
//ResponseEntity: 응답으로 변환될 정보를 모두 담은 요소들을 객체로 만들어서 반환해 줍니다.
@GetMapping("/display")
public ResponseEntity<byte[]> getFile(String fileLoca, String fileName) {
System.out.println("fileName: " + fileName);
System.out.println("fileLoca: " + fileLoca);
File file = new File("C:\\Users\\mls00\\OneDrive\\바탕 화면\\upload\\" + fileLoca + "\\" + fileName);
System.out.println(file);
ResponseEntity<byte[]> result = null;
try {
HttpHeaders headers = new HttpHeaders();
//probeContentType: 파라미터로 전달받은 파일의 타입을 문자열로 변환해 주는 메서드.
//사용자에게 보여주고자 하는 데이터가 어떤 파일인지를 검사해서 응답 상태 코드를 다르게 리턴할 수도 있습니다.
headers.add("Content-Type", Files.probeContentType(file.toPath()));
// headers.add("merong", "hello");
//ResponseEntity<>(응답 객체에 담을 내용, 헤더에 담을 내용, 상태 메세지);
//FileCopyUtils: 파일 및 스트림 데이터 복사를 위한 간단한 유틸리티 메서드의 집합체.
//file객체 안에 있는 내용을 복사해서 byte배열로 변환해서 바디에 담아 화면에 전달.
result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file), headers, HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
$(function() {
//등록하기 버튼 클릭 이벤트
$('#uploadBtn').click(function() {
regist();
});
//등록을 담당하는 함수
function regist() {
//세션에서 현재 로그인 중인 사용자 정보(아이디)를 얻어오자
const user_id = '${sessionScope.login.userId}';
//자바스크립트의 파일 확장자 체크 검색.
let file = $('#file').val();
console.log(user_id);
console.log(file);
//.을 제거한 확장자만 얻어낸 후 그것을 소문자로 일괄 변경
file = file.slice(file.indexOf('.') + 1).toLowerCase();
console.log(file);
if(file !== 'jpg' && file !== 'png' && file !== 'jpeg' && file !== 'bmp') {
alert('이미지 파일(jpg, png, jpeg, bmp)만 등록이 가능합니다.');
$('#file').val('');
return;
} else if(user_id === '') { //세션 데이터가 없다 -> 로그인 x
alert('로그인이 필요한 서비스입니다.');
return;
}
세션데이터가 없을경우 글 등록을 하지 못하게 하였다.
그리고 얻어온 세션 아이디로 글을 올린 사람에 대한 정보를 쿼리문으로 넘겨 줄 것이다.
우선 file이라는 변수로 file의 값을 가져온다.
그 후 파일 업로드가 끝나면 slice 함수로 .png 등 확장자를 잘라내어
tolowerCase로 소문자로 변경해준다.
그 뒤 이미지 파일만 올릴 수 있게
if문 으로 검증 로직을 구현 해준다.
즉 로그인 안됨 = 파일을 올릴 수없음
로그인 함 = 이미지 파일만 올릴 수있음
이 두가지 관문을 거쳐야 파일을 올릴 수있게 처리를 하였다.
미리보기 기능
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader(); //비동기처리를 위한 파읽을 읽는 자바스크립트 객체
//readAsDataURL 메서드는 컨텐츠를 특정 Blob 이나 File에서 읽어 오는 역할 (MDN참조)
reader.readAsDataURL(input.files[0]);
//파일업로드시 화면에 숨겨져있는 클래스fileDiv를 보이게한다
$(".fileDiv").css("display", "block");
reader.onload = function(event) { //읽기 동작이 성공적으로 완료 되었을 때 실행되는 익명함수
$('#fileImg').attr("src", event.target.result);
console.log(event.target)//event.target은 이벤트로 선택된 요소를 의미
}
}
}
$("#file").change(function() {
readURL(this); //this는 #file자신 태그를 의미
});
1) 이미지가 1개인 경우
미리보기 이미지가 표시될 이미지 태그를 생성하고,input file태그를 생성하고, 자바스크립트의FileReader()를 통해 이미지가 로딩되면 이미지 태그의src속성이 교체되도록 합니다.
2. 컨트롤러 제작
@GetMapping("/snsList")
public void snsList() {}
@PostMapping("/upload")
@ResponseBody
public String upload(MultipartFile file, String content,
HttpSession session) {
try {
String writer = ((UserVO)session.getAttribute("login")).getUserId();
//날짜별로 폴더를 생성해서 파일을 관리
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Date date = new Date();
String fileLoca = sdf.format(date);
//저장할 폴더 경로
String uploadPath = "C:\\Users\\mls00\\OneDrive\\바탕 화면\\upload\\" + fileLoca;
File folder = new File(uploadPath);
if(!folder.exists()) {
folder.mkdir(); //폴더가 존재하지 않는다면 생성해라.
}
String fileRealName = file.getOriginalFilename();
//파일명을 고유한 랜덤 문자로 생성.
UUID uuid = UUID.randomUUID();
String uuids = uuid.toString().replaceAll("-", "");
//확장자를 추출합니다.
String fileExtension = fileRealName.substring(fileRealName.indexOf("."), fileRealName.length());
System.out.println("저장할 폴더 경로: " + uploadPath);
System.out.println("실제 파일명: " + fileRealName);
System.out.println("폴더명: " + fileLoca);
System.out.println("확장자: " + fileExtension);
System.out.println("고유랜덤문자: " + uuids);
String fileName = uuids + fileExtension;
System.out.println("변경해서 저장할 파일명: " + fileName);
//업로드한 파일을 서버 컴퓨터의 지정한 경로 내에 실제로 저장.
File saveFile = new File(uploadPath + "\\" + fileName);
file.transferTo(saveFile);
//DB에 insert 작업을 진행.
SnsBoardVO snsVO = new SnsBoardVO(0, writer, uploadPath, fileLoca, fileName, fileRealName, content, null);
service.insert(snsVO);
return "success";
} catch (Exception e) {
System.out.println("업로드 중 에러 발생: " + e.getMessage());
return "fail"; //에러가 났을 시에는 실패 키워드를 반환.
}
}
@GetMapping("/snsList") void 처리로 페이지 이동을 할 수 있게 처리
로그인 세션을 가져와 로그인한 아이디의 값을 가져온다.
날자별로 폴더를 생성해서 관리를 하려고한다.
자바 객체인 SimpleDateFormat을 활용하여
날자를 생성해주고
Date date = new Date(); 하면 오늘 날자가 생성된다.
fileloca라는 변수에 저장해 둔다 (20220215) 이런식으로 나옴
저장할 폴더의 경로를 선택해준다.
String uploadPath = 실제사용할경로 + fileloca
그 후 자바 FIle객체를 사용하여
exist() - File 객체가 참조하는 파일 또는 디렉토리가 실제로 존재하면 true를, 그렇지
않으면 false를 리턴한다.
메소드를 활용하여 파일이 한번 생성 된 뒤 중복 생성을 방지한다.
String fileRealName = file.getOriginalFilename();로 파일 고유의 이름을 추출
<!-- 위의 구현체 xml을 빈으로 등록하기 위해서는 타입이 필요하니까
인터페이스를 자동으로 스캔해서 빈으로 등록하기 위한 설정.
-->
<mybatis-spring:scan base-package="com.spring.myweb.freeboard.mapper"/>
<mybatis-spring:scan base-package="com.spring.myweb.reply.mapper"/>
<mybatis-spring:scan base-package="com.spring.myweb.user.mapper"/>
<mybatis-spring:scan base-package="com.spring.myweb.snsboard.mapper"/>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.spring.myweb.snsboard.mapper.ISnsBoardMapper">
<!-- 등록하기 -->
<insert id="insert">
INSERT INTO snsboard
(bno, writer, uploadpath, fileloca, filename, filerealname, content)
VALUES
(snsboard_seq.NEXTVAL, #{writer}, #{uploadpath}, #{fileloca}, #{filename}, #{filerealname}, #{content})
</insert>
<!-- 전체 목록
<select id="getList" resultType="com.spring.myweb.command.SnsBoardVO">
SELECT * FROM snsboard
ORDER BY bno DESC
</select>
-->
<!-- 상세보기 -->
<select id="getDetail" resultType="com.spring.myweb.command.SnsBoardVO">
select * from snsboard
where bno = #{bno}
</select>
<!-- 삭제 -->
<delete id="delete">
delete from snsboard
where bno = #{bno}
</delete>
<!-- 페이징 전체목록 -->
<!-- 전체 목록 -->
<select id="getList" resultType="com.spring.myweb.command.SnsBoardVO">
SELECT * FROM
(
SELECT ROWNUM AS rn, tbl.* FROM
(
SELECT * FROM snsboard
ORDER BY bno DESC
) tbl
)
<![CDATA[
WHERE rn > (#{pageNum} - 1) * #{countPerPage}
AND rn <= #{pageNum} * #{countPerPage}
]]>
</select>
</mapper>
원래 만들고 junit으로 테스트를 해주어야하지만 생략하려고 한다.
컨트롤러 제작
package com.spring.myweb.controller;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.spring.myweb.command.SnsBoardVO;
import com.spring.myweb.command.UserVO;
import com.spring.myweb.snsboard.service.ISnsBoardService;
import com.spring.myweb.util.PageVO;
@Controller
@RequestMapping("/snsBoard")
public class SnsBoardController {
@Autowired
private ISnsBoardService service;
@GetMapping("/snsList")
public void snsList() {}
@PostMapping("/upload")
@ResponseBody
public String upload(MultipartFile file, String content,
HttpSession session) {
try {
String writer = ((UserVO)session.getAttribute("login")).getUserId();
//날짜별로 폴더를 생성해서 파일을 관리
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Date date = new Date();
String fileLoca = sdf.format(date);
//저장할 폴더 경로
String uploadPath = "C:\\Users\\mls00\\OneDrive\\바탕 화면\\upload\\" + fileLoca;
File folder = new File(uploadPath);
if(!folder.exists()) {
folder.mkdir(); //폴더가 존재하지 않는다면 생성해라.
}
String fileRealName = file.getOriginalFilename();
//파일명을 고유한 랜덤 문자로 생성.
UUID uuid = UUID.randomUUID();
String uuids = uuid.toString().replaceAll("-", "");
//확장자를 추출합니다.
String fileExtension = fileRealName.substring(fileRealName.indexOf("."), fileRealName.length());
System.out.println("저장할 폴더 경로: " + uploadPath);
System.out.println("실제 파일명: " + fileRealName);
System.out.println("폴더명: " + fileLoca);
System.out.println("확장자: " + fileExtension);
System.out.println("고유랜덤문자: " + uuids);
String fileName = uuids + fileExtension;
System.out.println("변경해서 저장할 파일명: " + fileName);
//업로드한 파일을 서버 컴퓨터의 지정한 경로 내에 실제로 저장.
File saveFile = new File(uploadPath + "\\" + fileName);
file.transferTo(saveFile);
//DB에 insert 작업을 진행.
SnsBoardVO snsVO = new SnsBoardVO(0, writer, uploadPath, fileLoca, fileName, fileRealName, content, null);
service.insert(snsVO);
return "success";
} catch (Exception e) {
System.out.println("업로드 중 에러 발생: " + e.getMessage());
return "fail"; //에러가 났을 시에는 실패 키워드를 반환.
}
}
//비동기 통신 후 가져올 목록
@GetMapping("/getList")
@ResponseBody
public List<SnsBoardVO> getList(PageVO paging) {
paging.setCountPerPage(3);
return service.getList(paging);
}
//게시글의 이미지 파일 전송 요청
//ResponseEntity: 응답으로 변환될 정보를 모두 담은 요소들을 객체로 만들어서 반환해 줍니다.
@GetMapping("/display")
public ResponseEntity<byte[]> getFile(String fileLoca, String fileName) {
System.out.println("fileName: " + fileName);
System.out.println("fileLoca: " + fileLoca);
File file = new File("C:\\Users\\mls00\\OneDrive\\바탕 화면\\upload\\" + fileLoca + "\\" + fileName);
System.out.println(file);
ResponseEntity<byte[]> result = null;
try {
HttpHeaders headers = new HttpHeaders();
//probeContentType: 파라미터로 전달받은 파일의 타입을 문자열로 변환해 주는 메서드.
//사용자에게 보여주고자 하는 데이터가 어떤 파일인지를 검사해서 응답 상태 코드를 다르게 리턴할 수도 있습니다.
headers.add("Content-Type", Files.probeContentType(file.toPath()));
// headers.add("merong", "hello");
//ResponseEntity<>(응답 객체에 담을 내용, 헤더에 담을 내용, 상태 메세지);
//FileCopyUtils: 파일 및 스트림 데이터 복사를 위한 간단한 유틸리티 메서드의 집합체.
//file객체 안에 있는 내용을 복사해서 byte배열로 변환해서 바디에 담아 화면에 전달.
result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file), headers, HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
// 상세보기 처리
@GetMapping("/getDetail/{bno}")
@ResponseBody
public SnsBoardVO getDetail(@PathVariable int bno) {
return service.getDetail(bno);
}
//삭제처리
@PostMapping("/delete")
@ResponseBody
public String delete(@RequestBody int bno, HttpSession session) {
System.out.println("삭제 글 번호 : " + bno);
SnsBoardVO vo = service.getDetail(bno);
UserVO user = (UserVO) session.getAttribute("login");
//로그인을 하지 않았거나, 작성자와 현재 로그인한 id가 일치하지 않는다면
if(user == null || !user.getUserId().equals(vo.getWriter()) ) {
return "noAuth";
}
service.delete(bno);
//파일 객체를 생성해서 지워지고 있는 게시물의 파일을 지목
File file = new File(vo.getUploadpath()+"\\"+vo.getFilename());
System.out.println("파일 삭제완료");
return file.delete() ? "Success" : "fail"; // 파일 삭제 메소드
}
//다운로드 비동기처리(화면에서 a태그를 클릭시download요청이 들어올 수 있도록 처리)
@GetMapping("/download")
@ResponseBody
public ResponseEntity<byte[]> download(String fileLoca, String fileName ){
System.out.println("fileName: " + fileName);
System.out.println("fileLoca: " + fileLoca);
File file = new File("C:\\Users\\mls00\\OneDrive\\바탕 화면\\upload\\" + fileLoca + "\\" + fileName);
ResponseEntity<byte[]> result = null;
try {
// 응답하는 본문을 브라우저가 어떻게 표시해야 할 지 알려주는 헤더 정보를 추가합니다.
// inline이라느 헤더 정보를 줄 경우 웹 페이지 화면에 포시가 되고, attachment인 경우 다운로드를 제공한다.
//request객체의 getHeader("User-Agent") -> 단어를 뽑아서 확인
//ie: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
//chrome: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
//파일명한글처리(Chrome browser) 크롬
//header.add("Content-Disposition", "attachment; filename=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1") );
//파일명한글처리(Edge) 엣지
//header.add("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
//파일명한글처리(Trident) IE
//Header.add("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", " "));
HttpHeaders header = new HttpHeaders();
header.add("content-Disposition", "attachment; filename="+ fileName);
result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file), header,HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
최대 업로드 가능한 바이트 크기(바이트 단위), -1은 제한이 없음을 의미 - <beans:property name="maxUploadSize" value="10485760" />
업로드 요청을 변환할 때 사용할 문자 인코딩 방식 <beans:property name="defaultEncoding" value="utf-8" />
이 두가지는 꼭 기억해주자
2. 본격적인 코딩😋
2-1 JSP
<!-- 파일 업로드에서는 enctype(인코딩타입)을 multipart/form-data로 반드시 설정 -->
<form action="upload_ok" method="post" enctype="multipart/form-data">
파일 선택 : <input type="file" name="file">
<input type="submit" value="전송">
</form>
<br><hr><br>
<!-- 파일 두개이상 붙히는거 -->
<form action="upload_ok2" method="post" enctype="multipart/form-data">
파일 선택 : <input type="file" multiple="multiple" name="files">
<input type="submit" value="전송">
</form>
연습이기 떄문에 디자인은 생략한다
이때
🤓
중요한게 form태그안에
enctype="multipart/form-data를 입력 해주어야한다.
2-1 Class VO 등 제작
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class UploadVO {
private String nmae;
private MultipartFile file;
}
@Getter
@Setter
@ToString
public class MultiUploadVO {
private List<UploadVO> list;
}
파일을 올릴때 한가지면 VO만 작성해도 되지만
2개 이상일 시는 LIst 타입으로 받아준다.
2-2 ConTroller 제작
//업로드로 가는 메소드
@GetMapping("/upload")
public void form() {}
@PostMapping("/upload_ok")
public String upload(@RequestParam("file") MultipartFile file) {
String fileRealName = file.getOriginalFilename(); //파일명을 얻어낼 수 있는 메서드!
long size = file.getSize(); //파일 사이즈
System.out.println("파일명 : " + fileRealName);
System.out.println("용량크기(byte) : " + size);
//서버에 저장할 파일이름 fileextension으로 .jsp이런식의 확장자 명을 구함
String fileExtension = fileRealName.substring(fileRealName.lastIndexOf("."),fileRealName.length());
String uploadFolder = "C:\\test\\upload";
/*
파일 업로드시 파일명이 동일한 파일이 이미 존재할 수도 있고 사용자가
업로드 하는 파일명이 언어 이외의 언어로 되어있을 수 있습니다.
타인어를 지원하지 않는 환경에서는 정산 동작이 되지 않습니다.(리눅스가 대표적인 예시)
고유한 랜던 문자를 통해 db와 서버에 저장할 파일명을 새롭게 만들어 준다.
*/
UUID uuid = UUID.randomUUID();
System.out.println(uuid.toString());
String[] uuids = uuid.toString().split("-");
String uniqueName = uuids[0];
System.out.println("생성된 고유문자열" + uniqueName);
System.out.println("확장자명" + fileExtension);
// File saveFile = new File(uploadFolder+"\\"+fileRealName); uuid 적용 전
File saveFile = new File(uploadFolder+"\\"+uniqueName + fileExtension); // 적용 후
try {
file.transferTo(saveFile); // 실제 파일 저장메서드(filewriter 작업을 손쉽게 한방에 처리해준다.)
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "fileupload/upload_ok";
}
우선 Jsp에서 파일의 name = file로 지정해주었기 떄문에
@RequestParam("file")로 가져온다.
다음에 getOriginalFilename()로 파일을 업로드하면서 업로드한 파일명을 얻어낸다.
-- 게시판 테이블 생성
CREATE TABLE mvc_board (
board_no NUMBER PRIMARY KEY,
title VARCHAR2(100) NOT NULL,
content VARCHAR2(300) NOT NULL,
writer VARCHAR2(50) NOT NULL,
reg_date DATE DEFAULT sysdate,
view_cnt NUMBER DEFAULT 0
);
-- board_no에 대한 시퀀스 설정
CREATE SEQUENCE board_seq
START WITH 1
INCREMENT BY 1
MAXVALUE 1000
NOCYCLE
NOCACHE;
우선 게시판을 만들 수있는 뼈대 즉 테이블 부터 생성해준다!
2. BoardVO 생성 🤪
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class BoardVO {
private int boardNo;
private String title;
private String content;
private String writer;
private Timestamp regDate;
private int viewCnt;
//new 마크 부착 여부 논리타입 필드
private boolean newMark;
}
@Controller
@RequestMapping("/board")
public class BoardController {
@GetMapping("/list")
public String list(Model model) {
System.out.println("/board/list: GET");
model.addAttribute("articles", service.getArticleList());
return "board/list";
}
}
4-2 인터페이스 제작
package com.spring.mvc.board.service;
import java.util.List;
import com.spring.mvc.board.commons.SearchVO;
import com.spring.mvc.board.commons.pageVO;
import com.spring.mvc.board.model.BoardVO;
public interface IBoardService {
//게시글 등록 기능
void insert(BoardVO article);
//게시글 전체 목록 기능
List<BoardVO> getArticleList(SearchVO search);
//게시글 상세 조회 기능
BoardVO getArticle(int boardNo);
//게시글 수정 기능
void update(BoardVO article);
//게시글 삭제 기능
void delete(int boardNo);
//게시물 수 조회 기능
int countArticles(SearchVO search);
//날자 한글로 변경
}
IboardService를 만든다. 기능은 단순하게 세팅해준다.
그 다음은 IBoardMapper를 생성해준다.
package com.spring.mvc.board.repository;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
import com.spring.mvc.board.commons.SearchVO;
import com.spring.mvc.board.commons.pageVO;
import com.spring.mvc.board.model.BoardVO;
public interface IBoardMapper {
//게시글 등록 기능
void insert(BoardVO article);
//검색 결과와 페이지 정보까지 가지고 있는 하나의 객체를 매개값으로 받는 방식
List<BoardVO> getArticleList(SearchVO search);
//게시글 상세 조회 기능
BoardVO getArticle(int boardNo);
//게시글 수정 기능
void update(BoardVO article);
//게시글 삭제 기능
void delete(int boardNo);
//게시물 수 조회 기능
int countArticles(SearchVO search);
}
여기서 주의 할 점!
MYBatis로 db연동을 진행할 떄 파라미터 값이 2개 이상이라면 1. @param으로 작성하는법
$('#signIn-btn').click(function() {
if(chk1 && chk2){
// ajax를 이용한 비동기 방식으로 로그인을 처리할 예정이다.
/*
아이디, 비밀번호를 가져오셔서 객체로 포장하세요.
비동기 통신을 진행하여 서버로 객체를 json형태로 전송하세요.
그리고, console.log()로 서버가 보내온 데이터를 확인하여
아이디가 없습니다, 비밀번호가 틀렸습니다, 로그인 성공이라는
메세지를 브라우저의 console창에서 확인하세요.
서버에서 클라이언트로 데이터 전송은 text로 이루어 질 것이며
idFail, pwFail, loginSuccess라는 문자열을 리턴할 것입니다.
전송방식: POST, url: /user/loginCheck
*/
const id = $('#signInId').val();
const pw = $('#signInPw').val();
//자동로그인 체크박스가 체크가 되었는지의 여부
//제이쿼리 is 상태여부를 확인할 수 있는 함수 논리값을 판단하여 논리값을 리턴
const autoLogin = $('#auto-login').is(':checked');
console.log("id" + id);
console.log("pw" + pw);
const userInfo ={
"account" : id,
"password" : pw,
//오토로그인 추가 체크의 여부를 확인하여 자동로그인을 구현할지 말지를 정한다.
"autoLogin" : autoLogin
};
// ajax 시작!
$.ajax({
type : "post",
url : "/user/loginCheck",
contentType : "application/json",
dataType : "text",
data : JSON.stringify(userInfo),
success : function(result) {
console.log("통신 성공" + result);
if(result === 'idFail'){
//console.log('아이디가 없습니다.');
$('#idCheck').css('background-color', 'pink');
$('#idCheck').html('<b style="font-size: 14px; color: red">[아이디가 없습니다.]</b>');
$('#signInPw').val('');
$("#singInPw").focus(); // 커서 이동 및 스크롤도 해당 위치로 이동시키는 함수
chk2 = false;
}else if (result === 'pwFail') {
//console.log('비밀번호가 틀렸습니다.');
$('#pwCheck').css('background-color', 'pink');
$('#pwCheck').html('<b style="font-size: 14px; color: red">[비밀번호가 틀렷습니다.]</b>');
$('#signInPw').val('');
$("#singInId").focus(); // 커서 이동 및 스크롤도 해당 위치로 이동시키는 함수
chk1 = false; chk2 = false;
}else{
console.log('로그인 성공');
location.href = '/';
}
},
error : function() {
alert('로그인 실패!');
}
}); // ajax 종료!
}else{
alert('입력값을 다시 확인하세요');
}
}); /
if(chk1 && chk2){ 의값으로
아이디 및 비밀번호 입력값이 모두가 true값이 진행되면 로그인 이벤트 처리
ajax(비동기 통신으로 진행) 이제 컨트롤러 작성하러 가보자! 😀
3. 스프링 작성 🤩
자 이제 컨트롤러 및 서비스 > 매퍼 및 xml을 작성 해보자!!
이번에도 비동기 통신임으로
@RestController @RequestMapping("/user")
를 진행해준다. 그리고 user라는 url로 진행하였다.
ajax에서 loginCheck라는 url으로 컨트롤러에 요청을 보내니 loginCheck와 같은 이름으로 작성한다.
@PostMapping("/loginCheck")
public String loginCheck(@RequestBody UserVO vo, HttpSession session , HttpServletResponse respones ) {//HttpServletRequest request) {
System.out.println("/user/logincheck : post");
System.out.println("param : " + vo);
// 매개값으로 httpsession 객체 받아서 사용
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
UserVO dbData = serivce.selectOne(vo.getAccount());
// mybatis는 조회된 데이터가 없을경우 null이 온다.
if(dbData != null) {
if(encoder.matches(vo.getPassword(), dbData.getPassword())) {
//로그인 성공 히원을 대상으로 세션 정보를 생성
session.setAttribute("login", dbData);
serivce.keepLogin(session.getId(), limitDate, vo.getAccount());
}
return "loginSuccess";
}else {
return "pwFail";
}
}else {
return "idFail";
}
}
이때 session.setAttribute("login", dbData); 세션을 생성해준다.
스프링에서는 간단하게 세션을 만들수 있다. 라는 login이라는 이름으로 dbDate의 값을 만들었다 . 이곳에는 userVO의 객체의 값이 들어있다.
이때 selectOne의 서비스와 keeplogin의 코드를 작성한다.
@Override
public UserVO selectOne(String account) {
return mapper.selectOne(account);
}
@Override
public void keepLogin(String session, Date limitTime, String account) {
Map<String, Object> datas = new HashMap<String, Object>();
datas.put("sessionId", session);
datas.put("limitTime", limitTime);
datas.put("account", account);
mapper.keepLogin(datas);
}
이때 SeletOne은 로그인하는 아이디의 검증을 위해 사용된다.
keeplogin은 세션의 유지 등에 사용된다.
package com.spring.mvc.user.repository;
import java.util.Map;
import com.spring.mvc.user.model.UserVO;
public interface IUserMapper {
//아이디 중복체크 기능
int checkId(String account);
//회원 가입기능
void register(UserVO vo);
//회원 정보 조회 기능
UserVO selectOne(String account);
//회원 탈퇴 기능
void delete(String account);
//자동 로그인 쿠키값 db 저장 처리
//sql > update 문으로 작성
void keepLogin(Map<String, Object> datas);
//세션아이디를 통한 회원 정보조회기능
/*
- 자동로그인을 하고 싶다는 사람한테 뭘 만들어 줬죠? > 쿠키(세션Id)
그리고 나서 그 사람이 나중에 우리사이트에 다시 방문했다고 칩시다.
단연히 우리 서버에 요청을 보낼 거고 , 요청과 함게 쿠키도 같이 전달이 되겠죠?
우리는 쿠키 안에 들어있는 세션 id로 회원 정보를 조회해서 마치 이사람이 로그인 중인 것 처럼
세션 데이터를 만들어 주자는 겁니다. (login이라는 세션 데이터 -> 로그인 중이라는 징표)
*/
UserVO getUserWithSessionId(String sessionId);
}
매퍼 작성 후 xml을 작성해준다.
<!-- 자동로그인을 희망하는 경우 쿠키값(세션아이디)와 유효시간을 갱신 -->
<update id="keepLogin">
update mvc_user set session_id = #{sessionId}, LIMIT_TIME = #{limitTime}
where account = #{account}
</update>
<select id="selectOne" resultMap="UserMap">
SELECT * FROM mvc_user
WHERE account=#{account}
</select>