@PostMapping("/delete")@ResponseBodypublic String delete(@RequestBodyint 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"; // 파일 삭제 메소드@Overridepublicvoiddelete(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
//게시글의 이미지 파일 전송 요청//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;
} elseif(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")publicvoidsnsList(){}
@PostMapping("/upload")@ResponseBodypublic 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:scanbase-package="com.spring.myweb.freeboard.mapper"/><mybatis-spring:scanbase-package="com.spring.myweb.reply.mapper"/><mybatis-spring:scanbase-package="com.spring.myweb.user.mapper"/><mybatis-spring:scanbase-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">
<mappernamespace="com.spring.myweb.snsboard.mapper.ISnsBoardMapper"><!-- 등록하기 --><insertid="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>
--><!-- 상세보기 --><selectid="getDetail"resultType="com.spring.myweb.command.SnsBoardVO">
select * from snsboard
where bno = #{bno}
</select><!-- 삭제 --><deleteid="delete">
delete from snsboard
where bno = #{bno}
</delete><!-- 페이징 전체목록 --><!-- 전체 목록 --><selectid="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")publicclassSnsBoardController{
@Autowiredprivate ISnsBoardService service;
@GetMapping("/snsList")publicvoidsnsList(){}
@PostMapping("/upload")@ResponseBodypublic 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")@ResponseBodypublic 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}")@ResponseBodypublic SnsBoardVO getDetail(@PathVariableint bno){
return service.getDetail(bno);
}
//삭제처리@PostMapping("/delete")@ResponseBodypublic String delete(@RequestBodyint 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")@ResponseBodypublic 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;
}
}
Apache Commons FileUpload 는 서블릿 3.0이상 부터 사용이 가능하다.
<!-- 서블릿 3.0이상 사용 가능한 파일 업로드 api-->
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version></dependency>
<!-- commons-io -->
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.4</version></dependency>
레거시 프로젝트 pom.xml에서 넣어주고 꼭 메이븐 업데이트(alt + F5)
를 눌러서 꼬옥! 업데이트를 해주자
1-2 객체 등록 및 빈(Bean) 등록
Web-INF > config(Spring) > Servlet-config(이름다를 수 있음!)
@Getter
@Setter
@ToString
public classMultiUploadVO{
private List<UploadVO> list;
}
파일을 올릴때 한가지면 VO만 작성해도 되지만
2개 이상일 시는 LIst 타입으로 받아준다.
2-2 ConTroller 제작
//업로드로 가는 메소드
@GetMapping("/upload")
public voidform() {}
@PostMapping("/upload_ok")
public Stringupload(@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()로 파일을 업로드하면서 업로드한 파일명을 얻어낸다.
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";
}
그리고 saveFile의 이름으로 아까 생성한 경로에 파일이 들어갈 수 있도록 생성해 준뒤
file.transferTo(saveFile);로 파일을 전송해준다.
이때 파일전달에 예외를 던지고 있어 예외처리를 해준다.
파일이 2개 이상 업로드 될 시🤡
위의 경우는 1가지일 때이다.
VO는 동일하다.
Jsp 파일이 달라지게 되는데.
<form action="upload_ok2" method="post" enctype="multipart/form-data">
파일 선택 : <inputtype="file"multiple="multiple"name="files"><inputtype="submit"value="전송"></form>
multiple="multiple"을 써주게 되면
이렇게 바뀌게 된다🤓
컨트롤러에서 추가된 부분을 찾아보자!
@PostMapping("/upload_ok2")
public Stringupload2(MultipartHttpServletRequest files) {
//서버에서 저장 할 경로String uploadFolder = "C:\\test\\upload";
List<MultipartFile> list = files.getFiles("files");
for(int i = 0; i<list.size(); i++) {
String fileRealName = list.get(i).getOriginalFilename();
long size = list.get(i).getSize();
System.out.println("파일명 :" + fileRealName);
System.out.println("사이즈" + size);
File saveFile = new File(uploadFolder + "\\" + fileRealName);
try {
list.get(i).transferTo(saveFile);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return"fileupload/upload_ok";
}
기본적인 형식을 똑같으나.
MultipartHttpServletRequest : 다중파일을 받아 올 수 있다.
먼저 와 같이 서버에 저장할 경로를 지정해 준뒤
List로 파일을 받아오자
List<MultipartFile> list = files.getFiles("files") 파일 여러개라 리스트로받고 반복문을 돌린다. 하나씩 뺴줘야하니까.
GET방식으로 진행하며 기존에 있던 name 파라미터 값을 그대로 컨트롤러로 옮기려고한다.
3. SPRING 부분
//이메일 인증
@GetMapping("/mailCheck")
@ResponseBody
public StringmailCheck(String email) {
System.out.println("이메일 인증 요청이 들어옴!");
System.out.println("이메일 인증 이메일 : " + email);
}
Get방식으로 가져오며 이메일을 확인 할 수있게 간단하게 프린트로 찍어준다.
이때 변수의 이름이 동일하면 @붙는 아노테이션을 지정 안할수 있다.
굳 아주 잘 들어오는 것을 알 수있다.
컨트롤러에서 다 작성해주어도 되지만 컨트롤러가 너무 비대해지기 때문에 따로 MailSendService라는
CLASS를 하나 만들어서 진행하려고 한다.
클래스는 만들었다면 빈등록을 해주어야한다.
Spring 폴더에 있는 경로로 빈 xml 파일을 만들어 주어 설정값을 해주었다.
XML 빈등록
<context:property-placeholder location="classpath:/db-config/email.properties" />
<!-- 이메일 인증 관련 빈 등록 -->
<beanid="mailSender"class="org.springframework.mail.javamail.JavaMailSenderImpl"><propertyname="host"value="smtp.gmail.com" /><propertyname="port"value="587" /><propertyname="username"value="${email.account}" /><propertyname="password"value="${email.password}" /><propertyname="javaMailProperties"><props><propkey="mail.transport.protocol">smtp</prop><propkey="mail.smtp.auth">true</prop><!-- gmail의 경우 보안문제 업데이트로 인해 SSLSocketFactory를 추가해야 smtp 사용 가능. --><propkey="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop><propkey="mail.smtp.starttls.enable">true</prop><propkey="mail.debug">true</prop><propkey="mail.smtp.ssl.trust">smtp.gmail.com</prop><propkey="mail.smtp.ssl.protocols">TLSv1.2</prop></props></property></bean>
각 property의 값
host : 이메일 보낼 url을 입력
port : 포트 번호 입력
userName : 이메일을 보낼 아이디 입력
Password : 이메일 비번
아래 키값은 보안 업데이트로 따로 추가 해 주어야한다.
아이디 비밀번호가 노출되는게 싫은 경우 file을 따로 만들어서 빈 주입후 따로 사용이 가능하다.
다시 돌아와서
MailSendService.class 제작🥰
@Component
public classMailSendService{
@Autowired
private JavaMailSenderImpl mailSender;
private int authNumber;
// 난수 발생(여러분들 맘대러)
public voidmakeRandomNumber() {
// 난수의 범위 111111 ~ 999999 (6자리 난수)
Random r = new Random();
int checkNum = r.nextInt(888888) + 111111;
System.out.println("인증번호 : " + checkNum);
authNumber = checkNum;
}
//이메일 보낼 양식!
public StringjoinEmail(String email) {
makeRandomNumber();
String setFrom = ".com"; // email-config에 설정한 자신의 이메일 주소를 입력 String toMail = email;
String title = "회원 가입 인증 이메일 입니다."; // 이메일 제목 String content =
"홈페이지를 방문해주셔서 감사합니다." + //html 형식으로 작성 ! "<br><br>" +
"인증 번호는 " + authNumber + "입니다." +
"<br>" +
"해당 인증번호를 인증번호 확인란에 기입하여 주세요."; //이메일 내용 삽입
mailSend(setFrom, toMail, title, content);
return Integer.toString(authNumber);
}
//이메일 전송 메소드
public voidmailSend(String setFrom, String toMail, String title, String content) {
MimeMessage message = mailSender.createMimeMessage();
// true 매개값을 전달하면 multipart 형식의 메세지 전달이 가능.문자 인코딩 설정도 가능하다.try {
MimeMessageHelper helper = new MimeMessageHelper(message,true,"utf-8");
helper.setFrom(setFrom);
helper.setTo(toMail);
helper.setSubject(title);
// true 전달 > html 형식으로 전송 , 작성하지 않으면 단순 텍스트로 전달.
helper.setText(content,true);
mailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
-- 게시판 테이블 생성
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;
$('#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;
}elseif (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 StringloginCheck(@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 voidkeepLogin(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);
//회원 탈퇴 기능 voiddelete(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>