Scheduler
- Spring Boot Scheduler는 별도의 라이브러리나 설정없이 사용 가능
- 사용할 클래스에 @EnabledScheduling을 추가하고 사용할 메서드에 @Scheduled 메서드를 추가해 동작
- 스케줄러는 프로그램과 함께 생명주기를 가져감(프로그램이 켜지면 실행되고 꺼지면 종료됨)
- @Scheduled()을 사용하는 메서드는 독립적으로 움직임
@Scheduled(fixedDelay=1000) | 이전 작업이 종료된 후 설정 시간 이후에 다시 시작 |
@Scheduled(fixedRate=1000) | 설정된 시간마다 시작을 한다. 즉 이전 작업이 종료되지 않아도 시작 |
@Scheduled(cron = "* * * * * *") | 초(0-59) 분(0-59) 시간(0-23) 일(1-31) 월(1-12) 요일(0-7) - 각 자리에 입력 / 없으면 *로 표시 |
- SchedulerModule
package kr.co.gudi.schedule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@EnableScheduling
@Component
public class SchedulerModule {
Logger logger = LoggerFactory.getLogger(getClass());
@Scheduled(fixedDelay = 1000)
public void fixedDelay() {
logger.info("작업 종료 후 1초 후 실행");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Scheduled(fixedDelay = 3000)
public void fixedRate() {
logger.info("3초 마다 실행");
}
// cron은 인터넷에 사용법이 많이 나와있음
// 초,분,시,일,월,요일,년도(생략가능)
//5초마다 : 5 <- 매분 5초(x) / 0/5 : 5초마다(O)
@Scheduled(cron = "0/5 * * * * MON-FRI")
public void cron() {
logger.info("5초 마다 실행");
}
}
File Upload / Download
- File Upload를 위해서 commons-fileupload & commons-io 라이브러리가 필요함
- Boot에서 기본으로 해당 라이브러리를 가지고 있으므로 설정만 해주면 됨
# File Upload
- application.properties
server.port=80
spring.mvc.view.prefix=/views/
spring.mvc.view.suffix=.jsp
// 업로드 가능한 하나의 파일 크기
spring.servlet.multipart.max-file-size=50MB
// 업로드 가능한 총 파일 크기
spring.servlet.multipart.max-request-size=500MB
// 파일 저장 경로
spring.servlet.multipart.location=C:/upload
- home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<style></style>
</head>
<body>
<a href="fileList.do">파일 리스트 보기</a>
<h3>파일 업로드</h3>
<form action="upload.do" method="post" enctype="multipart/form-data">
<input type="file" name="uploadFile"/>
<button>전송</button>
</form>
<hr/>
<h3>멀티파일 업로드</h3>
<form action="multiUpload.do" method="post" enctype="multipart/form-data">
<input type="file" name="files" multiple="multiple"/>
<button>전송</button>
</form>
</body>
<script></script>
</html>
- result.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<style></style>
</head>
<body>
<c:if test="${list.size()==0}"><p>업로드된 파일이 없습니다.</p></c:if>
<c:if test="${list.size()>0}">
<c:forEach items="${list}" var="file">
<img src="photo.do?path=${file}" width="250">
<a href="download.do?path=${file}">다운로드</a>
<a href="delete.do?path=${file}">삭제</a>
<br/>
</c:forEach>
</c:if>
<p><a href="/">돌아가기</a></p>
</body>
<script></script>
</html>
- FileController
package kr.co.gudi.controller;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import kr.co.gudi.service.FileService;
@Controller
public class FileController {
private final FileService service;
@Value("${spring.servlet.multipart.location}") private String root;
public FileController(FileService service) {
this.service = service;
}
Logger logger = LoggerFactory.getLogger(getClass());
@GetMapping(value = "/")
public String home() {
return "home";
}
@PostMapping(value = "/upload.do")
public String upload(MultipartFile uploadFile) {
service.upload(uploadFile);
return "redirect:/fileList.do";
}
@PostMapping(value = "/multiUpload.do")
public String multiUpload(MultipartFile[] files) {
service.multiUpload(files);
return "redirect:/fileList.do";
}
@GetMapping(value = "/fileList.do")
public String fileList(Model model) {
ArrayList<String> list = service.filelist();
model.addAttribute("list",list);
return "result";
}
@GetMapping(value = "/photo.do")
public ResponseEntity<Resource> showImg(String path) {
logger.info("show file : "+root+"/"+path);
//body
Resource body = new FileSystemResource(root+"/"+path);
//header
HttpHeaders header = new HttpHeaders();
String type;
try {
type = Files.probeContentType(Paths.get(root+"/"+path));
logger.info("type : "+type);
header.add("Content-type", type);
} catch (IOException e) {
e.printStackTrace();
}
//body, headers, status
return new ResponseEntity<Resource>(body, header, HttpStatus.OK);
}
@GetMapping(value = "/download.do")
public ResponseEntity<Resource> download(String path) {
//body
Resource body = new FileSystemResource(root+"/"+path);
//header
HttpHeaders header = new HttpHeaders();
try {
String fileName = "이미지"+path.substring(path.lastIndexOf("."));
//한글 파일명은 깨질 수 있으므로 인코딩을 해줘야함
fileName = URLEncoder.encode(fileName,"UTF-8");
// text/...은 문자열, image/... 이미지, application/octet-stream은 바이너리 데이터
header.add("Content-type", "application/octet-stream");
//content-Disposition : 내려보낼 데이터가 문자열(inline)인지 파일(attatchment)인지 알려줌
header.add("content-Disposition", "attatchment;fileName=\""+fileName+"\"");
} catch (IOException e) {
e.printStackTrace();
}
//body, headers, status
return new ResponseEntity<Resource>(body, header, HttpStatus.OK);
}
@GetMapping(value = "delete.do")
public String delete(String path) {
service.delete(path);
return "redirect:/fileList.do";
}
}
- FileService
package kr.co.gudi.service;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
public class FileService {
Logger logger = LoggerFactory.getLogger(getClass());
@Value("${spring.servlet.multipart.location}") private String root;
public void upload(MultipartFile uploadFile) {
//1.파일명 추출
String fileName = uploadFile.getOriginalFilename();
//2.새파일 생성(현재시간+확장자)
String ext = fileName.substring(fileName.lastIndexOf("."));
String newFileName = System.currentTimeMillis()+ext;
logger.info(fileName+" >> "+newFileName);
//3.파일 저장
try {
byte[] bytes = uploadFile.getBytes();
Path path = Paths.get(root+"/"+newFileName);
Files.write(path, bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
public void multiUpload(MultipartFile[] files) {
for (MultipartFile file : files) {
upload(file);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public ArrayList<String> filelist() {
ArrayList<String> list = new ArrayList<String>();
File[] files = new File(root+"/").listFiles();
logger.info("get file list : "+files);
for(File file : files) {
list.add(file.getName());
logger.info("get file : "+file);
}
return list;
}
public void delete(String path) {
File f = new File(root+"/"+path);
if(f.exists()) {
f.delete();
}
}
}
Tomcat
- Spring Boot에서 제공하는 내장 Tomcat을 사용할 수도 있고 실제 Tomcat을 설치해서 사용도 가능
Internal Tomcat | External Tomcat |
Tomcat 을 따로 설치 할 필요가 없음 | 더 많은 트래픽 처리를 할 수 있음 |
Run AS > Spring Boot App | Run As > Run On Server |
별도의 설정이 필요 없음(대신 단순한 형태로 이용) | Sever.xml이나 web.xml로 여러 설정이 가능 |
수정이 거의 일어나지 않는 운영에 유용 | 수정이 자주 일어나는 운영에 유용 |
Jar 형태로 빌드 할 때 주로 사용 | War 형태로 빌드 할 때 주로 사용 |
Build
- 개발한 소스가 실행 가능한 프로그램으로 변환 하는 작업
- Eclipse에서는 plugin을 통해 복사된 Tomcat에 소스를 동작시켜 줌(실제 빌드가 되었다고 보기 어려움)
- 실제 동작할 수 있는 WAR 파일을 사용해 실제 Tomcat에 적용하여 사용 가능
- Build 전 확인 사항
- Window > Preferences 선택
- java > Installed JREs - JRE로 되어있다면 JDK 를 추가해야함
- 기존 JRE는 삭제 해도 됨
- 에러로 인해 빌드되지 않는 경우 Build 전 Test 진행 문제 발생 시에도 무시하도록 아래 내용을 pom.xml에 추가
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
WAR & JAR
WAR(Web Archive) | JAR(Java Archive) |
프로젝트가 Tomcat 안에서 동작 | 프로젝트가 하나의 자바 프로그램으로 동작 |
가장 일반적인 형태의 빌드 | 솔루션 형태가 아닌 일반 서비스에서는 사용하지 않는 편이 좋음 (수용 한계가 적음) |
외부의 Tomcat webapp 폴더에 파일을 넣어야 함 | Tomcat이 내장되어 있기 때문에 Tomcat에 파일을 넣을 필요가 없음 |
프로젝트 생성 시 packaging을 war로 선택 | 프로젝트 생성 시 packaging을 jar로 선택해야 함 (보통 jar 빌드의 경우 view page가 없는 것이 좋음) |
# WarBuild
- 프로젝트 생성 후 Run As >> Maven install >> target 폴더의 war 파일을 tomcat의 webapp 폴더에 ROOT로 저장
- apache-tomcat-9.0.71 >> bin 에서 cmd 켜서 start up으로 프로젝트 실행
# JarBuild
- 프로젝트 생성 후 Run As >> Maven install >> target 폴더의 jar 파일을 원하는 폴더에 ROOT로 저장
- jar 파일이 저장된 폴더에서 cmd 켜서 java -jar ROOT.jar 로 프로젝트 실행
'코딩도전기 > Spring Boot' 카테고리의 다른 글
Spring Boot - Properties / AOP(Interceptor) (0) | 2023.06.02 |
---|---|
Spring Boot - REST API (0) | 2023.05.31 |
String Boot - Restful Service (0) | 2023.05.30 |
Spring Boot - Transaction / Connection Pool / CSS 일괄적용 (0) | 2023.05.26 |
Spring Boot - 쿼리문 로고 찍기 / 동적쿼리 (0) | 2023.05.25 |