Spring 스케줄러 — Quartz 예제로 따라하기
예제: 정해진 시간에 로그 한 줄을 찍는
SampleBatch(업무 내용은 신경 쓰지 않아도 됨)
0. 한 줄 요약
스케줄러 = "정해진 시간에 자동으로 돌아가는 알람 시계"
Job = 알람이 울릴 때 실행되는 코드
이 글에서는 "무슨 일을 하느냐"는 중요하지 않습니다. 언제, 어떻게 자동 실행되느냐만 다룹니다.
1. 스케줄러가 왜 필요한가?
어떤 작업이든 사람이 매번 버튼을 누르기 어렵습니다.
- 매일 새벽 2시
- 30분마다
- 월요일 오전 9시
이럴 때 시간만 맞으면 알아서 실행해 주는 게 스케줄러입니다.
[Cron: 매분 0초] ──► [Quartz] ──► [SampleBatchJob] ──► [Service] ──► 로그 출력2. Spring에서 스케줄링 방법 2가지
| 방식 | 어디에 쓰나 | 장점 | 단점 |
|---|---|---|---|
@Scheduled |
간단한 작업, 학습용 | 코드 5줄로 가능 | Job이 많아지면 관리 어려움 |
| Quartz + Spring | 실무 (우리 예제) | Cron·여러 Job·동시 실행 제어 | 설정 파일이 조금 더 많음 |
아래 예제는 Quartz 기준입니다.
3. 우리가 만들 프로젝트 구조
독립 실행 가능한 미니 Spring Boot 프로젝트입니다.
batch-scheduler-demo/
├── pom.xml
└── src/main/
├── java/com/example/batchdemo/
│ ├── BatchDemoApplication.java ← 시작점
│ └── batch/
│ ├── config/
│ │ ├── BatchSchedulerConfig.java ← Quartz 엔진 ON
│ │ └── SampleBatchConfig.java ← Cron(실행 시각) 정의
│ ├── job/
│ │ └── SampleBatchJob.java ← 알람이 울리면 Service 호출
│ └── service/
│ ├── SampleBatchService.java
│ └── SampleBatchServiceImpl.java ← 실제 실행할 코드
└── resources/
└── application.yml역할 한 줄 정리
| 파일 | 비유 |
|---|---|
SampleBatchConfig |
"몇 시에 울릴지" 알람 설정 |
SampleBatchJob |
알람이 울리면 딱 한 번 Service를 부르는 전화기 |
SampleBatchServiceImpl |
실제로 실행할 코드 (여기는 로그만 찍음) |
BatchSchedulerConfig |
Quartz 스케줄러 전원 스위치 |
4. 사전 준비
- JDK 17
- Maven 3.8+
- IDE (IntelliJ, VS Code 등)
빈 폴더 batch-scheduler-demo를 만들고 아래 파일을 경로 그대로 생성하세요.
5. pom.xml — 의존성
경로: batch-scheduler-demo/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>batch-scheduler-demo</artifactId>
<version>1.0.0</version>
<name>batch-scheduler-demo</name>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Quartz 스케줄러 (Spring 연동) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
6. application.yml — Cron 설정
경로: batch-scheduler-demo/src/main/resources/application.yml
# 학습용: 매분 0초에 실행 (눈으로 확인하기 쉬움)
app:
batch:
sample-cron: "0 * * * * ?"
# 운영 예시: "0 0 2 * * ?" → 매일 02:00 KST
Tip
처음엔 매분 돌리면 스케줄러가 도는지 바로 확인할 수 있습니다.
익숙해지면0 0 2 * * ?(매일 새벽 2시)로 바꾸세요.
7. BatchDemoApplication.java — 시작 클래스
경로: batch-scheduler-demo/src/main/java/com/example/batchdemo/BatchDemoApplication.java
package com.example.batchdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BatchDemoApplication {
public static void main(String[] args) {
SpringApplication.run(BatchDemoApplication.class, args);
}
}
8. SampleBatchService / SampleBatchServiceImpl
경로: batch-scheduler-demo/src/main/java/com/example/batchdemo/batch/service/SampleBatchService.java
package com.example.batchdemo.batch.service;
public interface SampleBatchService {
/** Quartz Job이 호출하는 진입점 (중복 실행 방지 포함) */
void executeScheduledSampleBatchTask();
/** 실제로 실행할 작업 — 예제에서는 로그만 출력 */
void executeSampleBatchTask();
}
경로: batch-scheduler-demo/src/main/java/com/example/batchdemo/batch/service/SampleBatchServiceImpl.java
package com.example.batchdemo.batch.service;
import java.time.LocalDateTime;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class SampleBatchServiceImpl implements SampleBatchService {
private static final Logger log = LoggerFactory.getLogger(SampleBatchServiceImpl.class);
/** 같은 Job이 겹쳐 돌지 않게 막는 플래그 */
private final AtomicBoolean running = new AtomicBoolean(false);
@Override
public void executeScheduledSampleBatchTask() {
if (!running.compareAndSet(false, true)) {
log.warn("sampleBatch skipped. already running.");
return;
}
try {
executeSampleBatchTask();
} finally {
running.set(false);
}
}
@Override
public void executeSampleBatchTask() {
String runId = UUID.randomUUID().toString();
log.info("sampleBatch executed. run_id={} at={}", runId, LocalDateTime.now());
// ↑ 실무에서는 여기에 "하고 싶은 일"을 넣으면 됩니다.
// (DB 처리, 파일 생성, API 호출 등 — 이 글 범위 밖)
}
}
왜 메서드를 2개로 나눴나?
| 메서드 | 역할 |
|---|---|
executeScheduledSampleBatchTask() |
스케줄러 전용 문지기 (중복 실행 방지) |
executeSampleBatchTask() |
실제 작업 (Job이 여러 개면 executeXxxBatchTask처럼 이름 구분) |
9. SampleBatchJob.java — Quartz가 부르는 Job
경로: batch-scheduler-demo/src/main/java/com/example/batchdemo/batch/job/SampleBatchJob.java
package com.example.batchdemo.batch.job;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import com.example.batchdemo.batch.service.SampleBatchService;
/**
* @DisallowConcurrentExecution
* = "이 Job은 동시에 2개 돌리지 마" (Quartz 레벨 보호)
*/
@DisallowConcurrentExecution
public class SampleBatchJob extends QuartzJobBean {
@Autowired
private SampleBatchService sampleBatchService;
@Override
protected void executeInternal(JobExecutionContext context) {
sampleBatchService.executeScheduledSampleBatchTask();
}
}
Job에는 비즈니스 로직을 넣지 않습니다. Service만 호출하는 얇은 껍데기로 유지하세요.
10. SampleBatchConfig.java — Cron(실행 시각) 정의
경로: batch-scheduler-demo/src/main/java/com/example/batchdemo/batch/config/SampleBatchConfig.java
package com.example.batchdemo.batch.config;
import java.util.TimeZone;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.example.batchdemo.batch.job.SampleBatchJob;
@Configuration
public class SampleBatchConfig {
public static final String JOB_NAME = "sampleBatchJob";
public static final String TRIGGER_NAME = "sampleBatchTrigger";
@Value("${app.batch.sample-cron}")
private String cronExpression;
// public static final String CRON_EXPRESSION = "0 0 3 * * ?";
/** Job 정의: "무엇을 실행할지" */
@Bean
public JobDetail sampleBatchJobDetail() {
return JobBuilder.newJob(SampleBatchJob.class)
.withIdentity(JOB_NAME)
.storeDurably()
.build();
}
/** Trigger 정의: "언제 실행할지" */
@Bean
public Trigger sampleBatchTrigger(JobDetail sampleBatchJobDetail) {
return TriggerBuilder.newTrigger()
.forJob(sampleBatchJobDetail)
.withIdentity(TRIGGER_NAME)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)
.inTimeZone(TimeZone.getTimeZone("Asia/Seoul"))
.withMisfireHandlingInstructionFireAndProceed())
.build();
}
}
Cron 표현식 치트시트 (Quartz 6~7필드)
초 분 시 일 월 요일 [연도]
0 0 2 * * ? → 매일 02:00:00
0 * * * * ? → 매분 0초 (학습용)
0 0/30 * * * ? → 30분마다| 필드 | 허용 값 | * 의미 |
|---|---|---|
| 초 | 0~59 | 매 초 |
| 분 | 0~59 | 매 분 |
| 시 | 0~23 | 매 시 |
| 일 | 1~31 | 매일 |
| 월 | 1~12 | 매월 |
| 요일 | SUN~SAT, ? |
요일 무관 |
11. BatchSchedulerConfig.java — Quartz 엔진 켜기
경로: batch-scheduler-demo/src/main/java/com/example/batchdemo/batch/config/BatchSchedulerConfig.java
package com.example.batchdemo.batch.config;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
@Configuration
public class BatchSchedulerConfig {
/**
* SchedulerFactoryBean = Quartz 스케줄러 본체.
* JobDetail + Trigger를 등록해야 실제로 돌아간다.
*/
@Bean
public SchedulerFactoryBean batchSchedulerFactoryBean(
ApplicationContext applicationContext,
JobDetail sampleBatchJobDetail, Trigger sampleBatchTrigger
// JobDetail sampleBatchJobDetail2, Trigger sampleBatchTrigger2
) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
// Job 안에서 @Autowired가 되도록 Spring과 연결
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
factory.setJobFactory(jobFactory);
factory.setJobDetails(sampleBatchJobDetail); // JobDetails 여러개 등록 가능.
factory.setTriggers(sampleBatchTrigger); // Triggers 여러개 등록 가능.
factory.setAutoStartup(true);
return factory;
}
/**
* Quartz가 Job을 new 할 때 Spring 빈 주입을 해 주는 팩토리
*/
static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
super.setApplicationContext(applicationContext);
this.applicationContext = applicationContext;
}
@Override
protected Object createJobInstance(org.quartz.spi.TriggerFiredBundle bundle) throws Exception {
Object job = super.createJobInstance(bundle);
applicationContext.getAutowireCapableBeanFactory().autowireBean(job);
return job;
}
}
}
12. 실행 방법
터미널에서 프로젝트 루트로 이동 후:
cd batch-scheduler-demo
mvn spring-boot:run
성공 로그 예시
1분 이내(테스트 Cron 기준) 콘솔에 아래와 비슷한 로그가 보이면 스케줄러가 정상 동작하는 것입니다.
sampleBatch executed. run_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx at=2026-06-26T14:01:0013. 전체 호출 흐름
[Quartz Scheduler]
│ Cron 시각 도래 (예: 매분 0초)
▼
[SampleBatchJob.executeInternal()]
│
▼
[SampleBatchServiceImpl.executeScheduledSampleBatchTask()]
│ running 플래그 검사 (이미 돌고 있으면 스킵)
▼
[SampleBatchServiceImpl.executeSampleBatchTask()]
│
└─ log.info("sampleBatch executed ...")Job 2개 이상일 때
BatchSchedulerConfig에서 Job·Trigger를 배열로 추가하면 됩니다.
factory.setJobDetails(sampleBatchJobDetail, anotherBatchJobDetail);
factory.setTriggers(sampleBatchTrigger, anotherBatchTrigger);
각 Job마다 XxxBatchConfig를 하나씩 두는 패턴이 일반적입니다.
14. 실무에서 스케줄러에 자주 붙이는 것
| 항목 | 스케줄러 관점에서 하는 일 |
|---|---|
| 배치 서버 1대만 등록 | WAS 여러 대여도 Job이 한 곳에서만 돌게 |
| 수동 실행 API | Cron 기다리지 않고 즉시 1회 실행 |
@DisallowConcurrentExecution |
같은 Job 중복 기동 방지 (Quartz) |
AtomicBoolean running |
Service 레벨 중복 기동 방지 (이중 안전장치) |
15. 자주 하는 실수 TOP 5
| # | 증상 | 원인 | 해결 |
|---|---|---|---|
| 1 | Job이 안 돈다 | Config가 @SpringBootApplication 패키지 밖 |
com.example.batchdemo 하위에 두기 |
| 2 | Job에서 @Autowired null |
AutowiringSpringBeanJobFactory 미설정 |
BatchSchedulerConfig 확인 |
| 3 | Cron이 이상한 시간에 돈다 | 타임존 미지정 | .inTimeZone(Asia/Seoul) |
| 4 | Job이 2번 돈다 | WAS 2대 모두 Scheduler 기동 | 배치 서버 1대만 setAutoStartup(true) |
| 5 | Cron 바꿨는데 반영 안 됨 | 앱 재기동 안 함 | spring-boot:run 재시작 |
16. @Scheduled로 30초 만에 맛보기 (보너스)
Quartz 전에 가장 단순한 형태도 알아 두면 좋습니다.
BatchDemoApplication.java에 @EnableScheduling 추가:
@SpringBootApplication
@EnableScheduling
public class BatchDemoApplication { ... }
HelloScheduler.java:
package com.example.batchdemo.batch;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class HelloScheduler {
@Scheduled(fixedRate = 5000) // 5초마다
public void tick() {
System.out.println("tick: " + System.currentTimeMillis());
}
}
- 장점: 바로 이해됨
- 한계: Cron·Job 관리·동시 실행 제어가 약함 → Job이 늘어나면 Quartz로 갑니다.
17. 정리 — 새 Job 추가 체크리스트
- Service —
executeXxxBatchTask()(실제 할 일) - Job — Service 호출만 (
QuartzJobBean) - XxxBatchConfig —
JobDetail+Trigger(Cron) - BatchSchedulerConfig —
setJobDetails/setTriggers에 등록 - application.yml — Cron 값
- 앱 실행 → 로그로 실행 시각 확인
SampleBatch 하나만 이해하면, 두 번째·세 번째 Job도 같은 뼈대에 Cron과 Service 이름만 바꿔 끼우면 됩니다.
18. (선택) 수동 실행 — Controller·스크립트 원리
Cron만으로도 배치는 돌아갑니다. Controller는 “지금 당장 한 번 돌려줘” 를 HTTP로 받기 위한 선택 구성입니다.
[자동] Cron → SampleBatchJob → executeScheduledSampleBatchTask() → executeSampleBatchTask()
↑ Controller 없음
[수동] curl / 스크립트 / Postman
→ SampleBatchController (POST /batch/sample/run.json)
→ acceptManualRun()
→ (비동기) executeSampleBatchTask()| 구분 | Controller 필요? |
|---|---|
| Quartz 스케줄만 사용 | 불필요 |
| 운영자가 즉시 1회 실행 | 필요 (또는 동일 API를 curl로 직접 호출) |
스크립트는 필수가 아닙니다. 스크립트는 POST + Cookie 헤더를 대신 보내 주는 편의 도구일 뿐입니다.
18.1 응답 DTO
경로: batch-scheduler-demo/src/main/java/com/example/batchdemo/batch/dto/SampleBatchManualRunResponse.java
package com.example.batchdemo.batch.dto;
public class SampleBatchManualRunResponse {
private boolean accepted;
private String reason;
private String run_id;
private String accepted_at;
private String trigger_type;
public boolean isAccepted() { return accepted; }
public void setAccepted(boolean accepted) { this.accepted = accepted; }
public String getReason() { return reason; }
public void setReason(String reason) { this.reason = reason; }
public String getRun_id() { return run_id; }
public void setRun_id(String run_id) { this.run_id = run_id; }
public String getAccepted_at() { return accepted_at; }
public void setAccepted_at(String accepted_at) { this.accepted_at = accepted_at; }
public String getTrigger_type() { return trigger_type; }
public void setTrigger_type(String trigger_type) { this.trigger_type = trigger_type; }
}
18.2 Service에 수동 접수 메서드 추가
SampleBatchService 인터페이스에 추가:
SampleBatchManualRunResponse acceptManualRun();
SampleBatchServiceImpl 예시 (기존 executeSampleBatchTask 재사용):
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import java.util.concurrent.Executor;
import org.springframework.beans.factory.annotation.Qualifier;
import com.example.batchdemo.batch.dto.SampleBatchManualRunResponse;
// 필드 추가
@Qualifier("sampleBatchExecutor")
private final Executor sampleBatchExecutor;
public SampleBatchServiceImpl(@Qualifier("sampleBatchExecutor") Executor sampleBatchExecutor) {
this.sampleBatchExecutor = sampleBatchExecutor;
}
private static final DateTimeFormatter ACCEPTED_AT =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
@Override
public SampleBatchManualRunResponse acceptManualRun() {
SampleBatchManualRunResponse response = new SampleBatchManualRunResponse();
response.setAccepted_at(OffsetDateTime.now(ZoneId.of("Asia/Seoul")).format(ACCEPTED_AT));
response.setTrigger_type("MANUAL");
if (!running.compareAndSet(false, true)) {
response.setAccepted(false);
response.setReason("ALREADY_RUNNING");
return response;
}
String runId = UUID.randomUUID().toString();
response.setAccepted(true);
response.setRun_id(runId);
// HTTP는 여기서 바로 끝(202). 실제 작업은 백그라운드 스레드에서 실행
sampleBatchExecutor.execute(() -> {
try {
executeSampleBatchTask();
} finally {
running.set(false);
}
});
return response;
}
SampleBatchConfig에 Executor 빈 추가 (수동 실행용, core=1):
@Bean(name = "sampleBatchExecutor")
public Executor sampleBatchExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("sample-batch-");
executor.initialize();
return executor;
}
18.3 Controller — HTTP 진입점
경로: batch-scheduler-demo/src/main/java/com/example/batchdemo/batch/controller/SampleBatchController.java
package com.example.batchdemo.batch.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.RestController;
import com.example.batchdemo.batch.dto.SampleBatchManualRunResponse;
import com.example.batchdemo.batch.service.SampleBatchService;
@RestController
@RequestMapping("/batch/sample")
public class SampleBatchController {
private final SampleBatchService sampleBatchService;
public SampleBatchController(SampleBatchService sampleBatchService) {
this.sampleBatchService = sampleBatchService;
}
@PostMapping("/run.json")
public ResponseEntity<SampleBatchManualRunResponse> runManual(
@RequestBody(required = false) Object body) {
SampleBatchManualRunResponse response = sampleBatchService.acceptManualRun();
return ResponseEntity.status(HttpStatus.ACCEPTED).body(response);
}
}
- HTTP 202 Accepted = “접수됨” (배치 완료가 아님)
- 완료 여부는 로그
sampleBatch executed로 확인
18.4 스크립트 없이 수동 호출 — 가능합니다
스크립트는 curl을 예쁘게 감싼 것입니다. 아래만으로 동일합니다.
curl (Git Bash / macOS / Linux)
curl -k -X POST "https://localhost:8080/batch/sample/run.json" \
-H "Content-Type: application/json" \
-d '{}'
PowerShell
Invoke-RestMethod -Method Post `
-Uri "https://localhost:8080/batch/sample/run.json" `
-ContentType "application/json" `
-Body "{}" `
-SkipCertificateCheck
Postman / Insomnia
| 항목 | 값 |
|---|---|
| Method | POST |
| URL | https://localhost:8080/batch/sample/run.json |
| Body | {} (raw JSON) |
실무 포털처럼 로그인 세션이 필요하면 Header에 Cookie 추가:
Cookie: JSESSIONID=로그인_후_복사한_값18.5 수동 스크립트 원리 (PowerShell 예시)
경로: docs/apps/batch/scripts/run-sample-batch.ps1
param(
[string]$BaseUrl = $env:PORTAL_BASE_URL,
[string]$SessionCookie = $env:PORTAL_SESSION_COOKIE
)
$Uri = "$($BaseUrl.TrimEnd('/'))/batch/sample/run.json"
$response = Invoke-RestMethod `
-Method Post -Uri $Uri `
-ContentType "application/json" `
-Body "{}" `
-Headers @{ Cookie = $SessionCookie } `
-SkipCertificateCheck
$response | ConvertTo-Json
동작 순서:
- 환경 변수에서 URL·쿠키 읽기
POST /batch/sample/run.json호출- JSON에서
accepted확인 true이면 서버 로그에서sampleBatch executed검색
스크립트에 배치 로직은 없습니다. 전부 서버 Controller → Service가 처리합니다.
18.6 응답 예시
접수 성공
{
"accepted": true,
"run_id": "a1b2c3d4-....",
"accepted_at": "2026-06-26T14:00:00+09:00",
"trigger_type": "MANUAL"
}
이미 실행 중
{
"accepted": false,
"reason": "ALREADY_RUNNING",
"run_id": null,
"trigger_type": "MANUAL"
}
18.7 수동 실행 체크리스트 (선택 기능)
- Service —
acceptManualRun()+Executor로 비동기 실행 - DTO —
SampleBatchManualRunResponse - Controller —
POST /batch/sample/run.json→ HTTP 202 - (선택) 스크립트 — curl 래퍼
- curl 또는 Postman으로
accepted: true확인 → 로그 확인
Cron만 쓸 거면 1~4번 전부 생략해도 §9~§13 스케줄러 예제는 그대로 동작합니다.
끝.batch-scheduler-demo 폴더를 만들고 §5~§13 파일을 붙여 넣은 뒤 mvn spring-boot:run 하세요.
수동 실행까지 붙이려면 §18을 추가합니다. 스크립트 없이 curl·Postman만으로도 Controller 수동 호출이 가능합니다.
'Backend > spring (Boot)' 카테고리의 다른 글
| Spring Boot DTO ↔ Entity 변환 방법 (0) | 2026.01.25 |
|---|---|
| DTO / VO / DAO / Entity / Model 비교 (0) | 2026.01.25 |
| Redis란? (Spring 연동) (0) | 2026.01.07 |
| Connection Pool과 Size 선정 기준 (with HikariCP) (0) | 2025.09.01 |
| [Spring, JSP] session 생성 차단 (0) | 2025.07.26 |