4주차에도 새로운 개념들을 많이 배웠다. 나만의 셀렉샵이라는 이름의 미니 프로젝트를 만들어보는 시간이었는데, 간단한 프로젝트지만 스프링의 중요한 요소들을 많이 다뤄주신 것 같아 정리 꼼꼼히 해둘 생각.
1. 3계층 아키텍처
어플리케이션을 3개의 논리적·물리적 컴퓨팅 계층으로 분리 구성하는 잘 정립된 소프트웨어 어플리케이션! (출처 IBM)
이 3개의 계층이란 프리젠테이션(클라이언트) 계층(JS,HTML,GUI 등 프론트엔드), 애플리케이션 계층(미들웨어, API), 데이터 계층(DBMS)으로 나눠지는데, 프리젠테이션 계층에서는 데이터 계층과 직접적으로 통신할 수 없고, 모든 통신은 애플리케이션 계층을 통과한다. 즉, 아래와 같은 형식으로 프로그램이 작동하는 모습인데, 적절한 분업을 통해 각각의 계층들이 각자의 역할을 잘 수행해내기만 하면, 어플리케이션은 원활하게 동작한다.
장점은 어떤 것이 있을까? (출처 IBM Cloud Education)
보다 신속한 개발: 각 계층이 서로 다른 팀에서 동시에 개발될 수 있으므로, 기업은 애플리케이션을 보다 빠르게 시장에 출시할 수 있으며 프로그래머는 각 계층에 대해 최신 및 최상의 언어와 툴을 사용할 수 있습니다.
확장성 개선: 필요에 따라 임의의 계층을 다른 계층과 독립적으로 확장할 수 있습니다.
안정성 향상: 한 계층의 가동 중단은 다른 계층의 가용성 또는 성능에 별로 영향을 미치지 않습니다.
보안성 강화: 프리젠테이션 계층과 데이터 계층이 직접 통신할 수 없으므로, 잘 설계된 애플리케이션 계층은 내부 방화벽의 일종으로 작동하여 SQL 인젝션 및 기타 악의적 행위를 방지할 수 있습니다.
더 파고들자면 복잡할 것 같고, 3계층 (및 4계층) 아키텍쳐는 단일 혹은 2계층 아키텍쳐와 비교해 개발팀의 각 구성원이 동시에 분업하는 구조라 빠르고, 안정적이다. 또 클라이언트 측에서는 api만 터치할 뿐 데이터를 직접적으로 관리하지 않기 때문에 안전하다! 지금 배우고 있는 스프링은 그 중 어플리케이션 계층을 중점적으로 개발하는 거라고 생각하면 될 것 같다. 물론 단점도 있다!! 서버 비용 등 개발의 비용도 늘어난다는것...ㅎㅎ
2. "나만의 셀렉샵"의 기능
일단 4주차에서 구현을 목표로 하는 기능들은,
- "탐색하기" 탭에서 상품 검색 (=>네이버 쇼핑검색 API 이용)
- 관심 상품을 DB에 등록 및 "모아보기" 탭에서 조회
- 관심 상품 등록 시 희망 가격을 입력해, 현재 가격이 희망가보다 낮을 경우 "최저가" 표시.
- 유저가 계속 확인하지 않아도 매일 동일한 시각에 현재 가격 업데이트
이렇게 네가지를 잡았다.
일단 상품 검색 기능을 구현하면서, 네이버 쇼핑검색 오픈 API를 발급받아 사용했는데, 이 부분이 흥미로웠다. 네이버에서 공개한 open API를 사용하는데, 그것을 직접적으로 클라이언트 계층에서 요청을 보내는 것이 아니라, 어플리케이션 계층에서 API 요청을 보내고 그 응답을 필요한 데이터만 뽑아 하나의 인스턴스로 만든다. 그리고 그 인스턴스들은 데이터베이스에 차곡차곡 쌓인다.
이 부분이 굉장히 매력적으로 느껴졌다. 외부의 API 만을 직접적으로 사용하려고 하면 코드가 지저분해지고, 어려워지는데, 내 서버에 특정 api에 요청이 들어오면 그 요청의 쿼리 값을 받아 외부 api에 요청을 보내는 것은 좀 더 복잡하고 복합적인 api의 모음을 하나의 api로 압축할 수 있다는 뜻이기도 하니까.
(지금 스프링 공부가 끝나면 다시 재개하려고 하는 사료 앱에 어떻게 써먹으면 좋을지도 떠올랐다!)
네이버에 openAPI 신청을 하고, ARC를 통해 다뤄보고, 거기다 보너스로 Java코드로 generate까지 해서 받아 그대로 활용했다. (ARC... 요물이군.. 근데 왜 나한테는 DATA TABLE을 보여주지 않는거지?)
또 이 과정에서 응답으로 받은 json 문자열을 JSONObject, JSONArray로 변환해 사용하는 것을 배웠는데, 역시 json은 자바스크립트에서 다루는게 제일 편하군... 자바스크립트는 시키지 않아도 마음대로 오브젝트로 만들어 읽는 녀석인데... 이쪽은 getJSONArray 같은 메소드를 활용해야 한다니.. 하암..
3. 엉뚱한 곳에서 만난 에러코드
네이버 API를 사용해보고 서버를 run 하자마자 에러코드가 나타났다.
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
Process finished with exit code 0
- 임베디드 데이터베이스(H2, HSQL or Derby)를 사용할 경우, 이를 클래스 경로에 넣어라.
- 특정 프로파일에서 데이터베이스 설정을 로드하고자 하면 이를 활성화해야한다. (현재 활성화된 프로파일이 없다.)
라는 내용의 제안을 함께 보여주는 에러코드였는데, 이 부분을 지난 3주차까지의 강의 내용들과 비교해 생각해보니, application.properties 파일에 그 원인이 있을 것 같았다. (강의 내용 중엔 이 부분을 편집하는 내용이 없..었다 아마도) 임베디드 데이터 베이스 설정을 활성화하라는 건데 h2를 활성화하는 부분이 내 기억으로는 거기서 밖에 없었던 것 같았으니까. 그래서 3주차의 강의에서 삽입했던 코드를 그대로 가져와 붙여봤다. (구글링은 많이 해봤는데, 대부분의 검색결과가 mySQL을 사용하는 환경에서 application.properties에 url과 username, password 등을 입력하는 솔루션이었기에, h2를 사용하는 나한테는 도움이 안됐다. 구글링결과링크)
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.jpa.show-sql=true
아무튼 다시 재가동. 오류의 일부분만이라도 해결이 됐다면 오류코드의 양이 좀 줄어야 하는데, 오히려 에러 발생 부분만 잘라도 1600자가 넘는 막대한 양의 에러코드가 쏟아졌다.
(길이 제한 때문에 앞 부분의 오류 없는 로그 부분 생략합니다.)
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-08-17 02:41:03.301 ERROR 17288 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.RuntimeException: Failed to load driver class org.h2.Driver in either of HikariConfig class loader or Thread context classloader
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:541) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1334) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154) ~[spring-context-5.3.9.jar:5.3.9]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908) ~[spring-context-5.3.9.jar:5.3.9]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.9.jar:5.3.9]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.3.jar:2.5.3]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) [spring-boot-2.5.3.jar:2.5.3]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) [spring-boot-2.5.3.jar:2.5.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) [spring-boot-2.5.3.jar:2.5.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) [spring-boot-2.5.3.jar:2.5.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332) [spring-boot-2.5.3.jar:2.5.3]
at com.sparta.week04.Week04Application.main(Week04Application.java:11) [main/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_291]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_291]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_291]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_291]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.5.3.jar:2.5.3]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.RuntimeException: Failed to load driver class org.h2.Driver in either of HikariConfig class loader or Thread context classloader
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1334) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) ~[spring-beans-5.3.9.jar:5.3.9]
... 26 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.RuntimeException: Failed to load driver class org.h2.Driver in either of HikariConfig class loader or Thread context classloader
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.9.jar:5.3.9]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.9.jar:5.3.9]
... 40 common frames omitted
Caused by: java.lang.RuntimeException: Failed to load driver class org.h2.Driver in either of HikariConfig class loader or Thread context classloader
at com.zaxxer.hikari.HikariConfig.setDriverClassName(HikariConfig.java:491) ~[HikariCP-4.0.3.jar:na]
at org.springframework.boot.jdbc.DataSourceBuilder$MappedDataSourceProperty.set(DataSourceBuilder.java:460) ~[spring-boot-2.5.3.jar:2.5.3]
at org.springframework.boot.jdbc.DataSourceBuilder$MappedDataSourceProperties.set(DataSourceBuilder.java:355) ~[spring-boot-2.5.3.jar:2.5.3]
at org.springframework.boot.jdbc.DataSourceBuilder.build(DataSourceBuilder.java:181) ~[spring-boot-2.5.3.jar:2.5.3]
at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.createDataSource(DataSourceConfiguration.java:48) ~[spring-boot-autoconfigure-2.5.3.jar:2.5.3]
at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari.dataSource(DataSourceConfiguration.java:90) ~[spring-boot-autoconfigure-2.5.3.jar:2.5.3]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_291]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_291]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_291]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_291]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.9.jar:5.3.9]
... 41 common frames omitted
Process finished with exit code 0
나한테 왜 그러는데... 제대로 설명을 해줘야 알거아냐...
뭘 원하는지 몰라서 이것저것 구글링해서 나오는 건 다해봤다. Maven Repo에서 h2 gradle도 가져와보고, 프로젝트 코드 전체를 다시 살펴봤고, mySQL을 써야하나 싶어 그쪽도 알아봤다. 암튼 다 안되더라 그래서 새벽2시에 슬랙에 질문을 올리고 겨우 잠들었다.
아침이 되자 홀린듯이 컴퓨터를 켜고 생각난 부분을 구글에서 찾아, build.gradle 파일에 코드 두 줄을 추가했다.
생각이 어떻게 떠오른지는 기억이 안난다. 감사..!!!
implementation 'com.h2database:h2'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
그리고 코드가 정상적으로 동작했다. (얻어걸린거라 교훈도 없다.)
스프링부트는 build.gradle 파일의 dependencies에 데이터베이스 관련 의존성을 추가하고, application.properties를 비워두면 알아서 해당 데이터베이스를 기본 데이터소스로 사용한다고 한다. 여튼 혼자 북치고 장구치고 하다가 뭐 하나 배웠다. 늘 그랬듯이.
4. 결과물은?
프로젝트의 설계는 api 설계부터 시작해, 스니펫으로 제공해주신 HTML/CSS 파일을 그대로 사용했다. JS 부분은 강의 영상을 보며 작성했다. 결과물은 다음과 같다.
간단한 UI지만 모달, 탭 네비게이션 등이 있는 코드라 쪼끔은 복잡한 코드였다. 시간을 두고 찬찬히 씹어봐야겠다.
5. 스케줄링
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor // final 멤버 변수를 자동으로 생성합니다.
@Component // 스프링이 필요 시 자동으로 생성하는 클래스 목록에 추가합니다.
public class Scheduler {
private final ProductRepository productRepository;
private final ProductService productService;
private final NaverShopSearch naverShopSearch;
// 초, 분, 시, 일, 월, 주 순서
@Scheduled(cron = "0 0 1 * * *")
public void updatePrice() throws InterruptedException {
System.out.println("가격 업데이트 실행");
// 저장된 모든 관심상품을 조회합니다.
List<Product> productList = productRepository.findAll();
for (int i=0; i<productList.size(); i++) {
// 1초에 한 상품 씩 조회합니다 (Naver 제한)
TimeUnit.SECONDS.sleep(1);
// i 번째 관심 상품을 꺼냅니다.
Product p = productList.get(i);
// i 번째 관심 상품의 제목으로 검색을 실행합니다.
String title = p.getTitle();
String resultString = naverShopSearch.search(title);
// i 번째 관심 상품의 검색 결과 목록 중에서 첫 번째 결과를 꺼냅니다.
List<ItemDto> itemDtoList = naverShopSearch.fromJSONtoItems(resultString);
ItemDto itemDto = itemDtoList.get(0);
// i 번째 관심 상품 정보를 업데이트합니다.
Long id = p.getId();
productService.updateBySearch(id, itemDto);
}
}
}
@Scheduled라는 어노테이션을 처음 사용해봤는데 내장 기능으로 이런 것까지 할 수 있다니 짱이잖아..?!
+ 프로젝트 메인 함수에 @Enable Scheduling을 써줘야함 (스프링 3.1 이상버전에서 지원)
단순히 어노테이션으로 클래스 위에 @Scheduled를 기입하고 cron 값을 삽입한다.
CRON 표현식은 어려워보이지만 직관적이라 금방 이해할 수 있다.
```초(0-59) | 분(0-59) | 시(0-23) | 일(1-31) | 월(1-12) | 요일(0-6)```
순서대로,
> 초 0-59 , - * /
> -분 0-59 , - * /
> -시 0-23 , - * /
> -일 1-31 , - * ? / L W
> -월 1-12 or JAN-DEC , - * /
> -요일 1-7 or SUN-SAT , - * ? / L # (1:일, 2:월, 3:화, 4:수, 5:목, 6:금, 7:토)
> -년(옵셔널 파라미터) 1970-2099 , - * /
> * : 모든 값
> ? : 특정 값 없음
> - : 범위 지정에 사용
> , : 여러 값 지정 구분에 사용
> / : 초기값과 증가치 설정에 사용
> L : 지정할 수 있는 범위의 마지막 값 (일: 해당 월의 마지막날, 요일: 토요일)
> W : 월~금요일 또는 가장 가까운 월/금요일
> # : 몇 번째 무슨 요일 2#1 => 첫 번째 월요일
* 기호는 모든 조건에서 참이 되고("any"),
? 기호는 "none"이라고 생각하면 된다.
(요일은 0이 일, 6이 토요일인데 헷갈리면 단어 형태로 입력가능)
요일 뒤에 #과 1-5까지의 숫자를 입력하면
n번째 주의 해당 요일에 실행되도록 예약할 수 있고,
(예: 3#2 => 수요일of둘째주)
시,분,초를 나타내는 칸에 (초기값)/(시간 간격)을 입력하면 주기적으로 실행되는 코드를 짤 수 있다.
(예: 0/10 => 0분부터 10분마다)
또 (시작범위)-(끝범위)를 입력하면 범위 설정을 할 수 있다.
(예: "0 0 9-18 * * * =>오전 9시부터 오후 6시까지 정각마다 실행)
cron을 제일 많이 사용할 것 같지만 다른 것들도 있긴 하다.
// fixedDelay : milliseconds 단위로, 이전 작업이 끝난 시점으로 부터 고정된 시간을 설정한다.
@Scheduled(fixedDelay=5000)
public void testScheduler(){
System.out.println("스케줄러");
}
// fixedDelayString : fixedDelay와 같은데 property의 value만 문자열로 넣는 것이다.
@Scheduled(fixedDelay="5000")
public void testScheduler(){
System.out.println("스케줄러");
}
// fixedRate : milliseconds 단위로, 이전 작업이 수행되기 시작한 시점으로 부터 고정된 시간을 설정한다.
@Scheduled(fixedRate = 3000)
public void testScheduler(){
System.out.println("스케줄러");
}
// fixedRateString : fixedDelay와 같은데 property의 value만 문자열로 넣는 것이다.
@Scheduled(fixedRate = "3000")
public void testScheduler(){
System.out.println("스케줄러");
}
// initialDelay : 스케줄러에서 메서드가 등록되자마자 수행하는 것이 아닌 초기 지연시간을 설정하는 것이다.
@Scheduled(fixedDelay = 3000, initialDelay = 1000)
public void testScheduler(){
System.out.println("스케줄러");
}
// initialDelayString : 위와 마찬가지로 문자열로 값을 표현하겠다는 의미다.
@Scheduled(fixedDelay = 3000, initialDelayString = "1000")
public void testScheduler(){
System.out.println("스케줄러");
}
5주차에는 지금까지 만든 웹서비스의 데이터베이스를 테스트용인 h2에서 MySQL로 갈아끼우고 배포까지 해보는 내용이다. 재미있다재미있다!!
'Java' 카테고리의 다른 글
<JAVA> DTO가 뭐예요? 꼭 써야 하나요? (0) | 2021.11.08 |
---|---|
웹개발의 봄, 스프링 5주차 (최저가셀렉샵) [스파르타코딩클럽] (0) | 2021.08.19 |
웹개발의 봄, 스프링 3주차 (24시간뉴스피드) [스파르타코딩클럽] (0) | 2021.08.16 |
웹개발의 봄, 스프링 2주차 (내머리터짐) [스파르타코딩클럽] (0) | 2021.08.15 |
웹개발의 봄, 스프링 1주차 (+자바) [스파르타코딩클럽] (0) | 2021.08.12 |
댓글