์์ ์ฝ๋๋ฅผ ํตํด ๊ธฐ๋ณธ์ ์ธ Job์ ์์ฑํ๊ณ ์คํํด๋ณธ๋ค.
์คํ๋ง ๋ฐฐ์น๋ฅผ ๋ณธ๊ฒฉ์ ์ผ๋ก ์์๋ณด๊ธฐ ์ ์ ๊ธฐ๋ณธ์ ์ธ job ๊ตฌ์ฑ์ ํ์
ํ๋ค.
Spring Batch๋ Job์ด๋ผ๋ ๋จ์๋ก ์์
์ ๋ถ๋ฅํ๋ค.
์์ ๋งํ๋ Batch Job์ ์๋ฏธํ๋๋ฐ ์ด Job์ ์ผ๋ จ์ ์์
์ ์ํํ๋ค. ์๋ฅผ ๋ค์ด, "ํ ๊ธฐ๋ฅ์ ๋ํ ๋ฐฐ์น๋ฅผ ์์ฑํด์ผ๋ผ"๋ผ๊ณ ํ๋ค๋ฉด, ํ๋์ Batch Job์ ๋ง๋ ๋ค๋ ์๋ฏธ๋ค.
์ด Job์ Step์ด๋ผ๋ ๋
์์ ํ ๊ฐ ์ด์ ๊ฐ์ง๋ค.
์ด Step์ ์ค์ ๋ฐฐ์น ์์
์ ์ํํ๋ ์ญํ ์ ๊ฐ์ง๋ฉฐ, ๋ฐ์ดํฐ ์กฐํ๋ ๋ฐฐ์น ๋ก์ง ์คํ ๋ฑ๊ณผ ๊ฐ์ด Job์ ์ค์ ๋ก ์ํํ๋ ๋
์์ด๋ค. ์ฆ, ์ด Step์ ์ฌ๋ฌ๊ฐ์ง ์ญํ ์ ๊ฐ์ง ์ ์๋ค.
๋ง์ฝ ์๋์ ๊ฐ์ด Step์ด ์ญํ ๋ณ(e.g. ๋ฐ์ดํฐ ์กฐํ ์ํ, ์ค์ ๋ฐฐ์น ๋ก์ง ์ํ, ๋ฐ์ดํฐ ์ ์ฅ ์ํ ๋ฑ)๋ก ๋ถ๋ฆฌ๋๋ค๋ฉด, ๊ฐ ์ญํ ๋ณ Step์ด ํ๋์ Job ์์ ๋ฌถ์ด๋ ์
์ด๋ค.
ํ๋์ Job์์๋ ์ฌ๋ฌ Step์ ์ ์ํ ์ ์์ผ๋ฉฐ, ์ด Step๋ค์ด ๋ชจ์ฌ์ 1.1์ Job์์ ์ด์ผ๊ธฐํ ์ผ๋ จ์ ์์
์ด ๋๋ ๊ฒ์ด๋ค.
๋ํ, Job์ด ์ฌ๋ฌ ๊ฐ์ Step์ ๊ฐ์ง๋ฏ, Step๋ ์ด๋ป๊ฒ ๊ตฌ์ฑํ๋๋์ ๋ฐ๋ผ์ ๋ด๋ถ ๊ตฌ์ฑ์ด ๋ฌ๋ผ์ง ์ ์๋๋ฐ ์ด๋ Step๊ณผ ๊ด๋ จ๋ ๊ธ์์ ์์ธํ ๋ค๋ฃฌ๋ค.
์ค์ ๋ก Job์ ์ ์ํ๊ณ , Step์ ํตํด ๋ฐฐ์น ๋ก์ง์ ๊ตฌํํด๋ณด์. [์ฝ๋ ์ฐธ๊ณ ]
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public List<User> getUsersRegisteredYesterday() {
final LocalDateTime start = LocalDateTime.now().minusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
final LocalDateTime end = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
return userRepository.findByRegisteredAtBetween(start, end);
}
}- ์์ ๊ธฐ์ค(00:00:00.000)์ผ๋ก ์ด์ ํ๋ฃจ๋์ ์ ๊ท ๊ฐ์ ๋ ์ ์ ๋ฅผ ์กฐํํ๊ธฐ ์ํด getUsersRegisteredYesterday() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค.
@Slf4j
@Configuration
@RequiredArgsConstructor
public class TransferNewUserJobConfiguration {
private final JobRepository jobRepository;
private final PlatformTransactionManager platformTransactionManager;
private static final String JOB_NAME = "TRANSFER_NEW_USER_JOB";
private static final String STEP_1_NAME = "TRANSFER_NEW_USER_STEP";
private final UserService userService;
@Bean
public Job transferNewUserJob() {
return new JobBuilder(JOB_NAME, jobRepository) // JobBuilder๋ฅผ ํตํด Job์ ์ ์ํ๋ค.
.start(transferNewUserStep()) // ์๋ ์ ์ํ step์ Job์ ์ํ๋๋ก ์ง์ด ๋ฃ๋๋ค.
.build();
}
@Bean
public Step transferNewUserStep() {
return new StepBuilder(STEP_1_NAME, jobRepository) // StepBuilder๋ฅผ ํตํด Step์ ์ ์ํ๋ค.
.tasklet((contribution, chunkContext) -> { // Step ๋ก์ง์ ์ ์
final List<User> users = userService.getUsersRegisteredYesterday();
log.info("{} ๋ช
์ ์ ์ ์ ๋ณด๋ฅผ AML ๋ฑ์ ์๋น์ค๋ก ์ ์ก", users.size());
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build();
}
} ์ฌ๊ธฐ์ ๋ด์ผํ ๋ด์ฉ์ ๋ฑ ๋๊ฐ์ง๋ค. transferNewUserJob()์ transferNewUserStep()์ด๋ค.
์ ํด๋์ค๋ฅผ ์ธ๋ป ๋ณด๋ฉด, Spring์์ ์ค์ ํ์ผ์ Bean์ ๋ฑ๋กํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋ค.
๋ง๋ค. Spring Batch์์๋ @Configuration์ ํตํด, Job์ Bean์ผ๋ก ๋ฑ๋กํ๋ค.
์ง๊ธ ์ฌ๊ธฐ์ ์ค์ํ ๊ฒ์ StepBuilder๋ฅผ ํตํด Step์ ์ ์ํ๊ณ , ์ ์ํ Step์ JobBuilder๋ฅผ ํตํด Job์ ๋ผ์ ๋ฃ์๋ค๋ ์ ์ด๋ค.
ํ
์คํธ๋ฅผ ํตํด ์ ์ฝ๋๋ฅผ ์คํ ์์ผ๋ณด์.
[์ฝ๋์ฐธ๊ณ ]
@Testcontainers
@SpringBootTest
@SpringBatchTest
class TransferNewUserJobConfigurationTest extends TestTemplate {
@Autowired
private Job transferNewUserJob;
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@Autowired
private UserRepository userRepository;
@Autowired
private DatabaseTemplate databaseTemplate;
@BeforeEach
void setup() {
jobLauncherTestUtils.setJob(transferNewUserJob);
databaseTemplate.truncate();
}
@Test
@SneakyThrows
void run() throws Exception{
// given
final User user = UserFixture.create(LocalDateTime.now().minusDays(1));
userRepository.save(user);
// when
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
// then
assertThat(jobExecution.getStatus()).isEqualTo(BatchStatus.COMPLETED);
}
}@SpringBatchTest๋ ๋ฐฐ์น ํ ์คํธ๋ฅผ ์ํ Component ๋ฑ์ ์๋์ผ๋ก Bean์ผ๋ก ์ ๊ณตํด์ฃผ๋ ์ญํ ์ ํ๋ค.TestTemplate๋ MySQL ํ๊ฒฝ์์ ๋ฐฐ์น ํ ์คํธ๋ฅผ ํ๊ธฐ ์ํด, ์ ์ํด๋์ ์ค์ ํ์ผ์ด๋ค.(TestContainer๋ฅผ ํตํด ๋์)
์ ํ
์คํธ๋ฅผ ํตํด TransferNewUserJobConfiguration#transferNewUserJob()์ ์คํํ๋ค.
๊ฒฐ๊ณผ๋ฅผ ํ์ธํ๊ธฐ์ ์์ ์์๋๋ Job ์คํ ์์๋ ์๋์ ๊ฐ๋ค.
- Job ์คํ ์์
- ์ ์ํ Step ์คํ
- Job ์ข ๋ฃ (๊ฒฐ๊ณผ ๋ฐํ)
์ค์ ๋ก๊ทธ๋ ์๋์ ๊ฐ๋ค.
-
์ฒซ๋ฒ์งธ ๋ฐ์ค
TRANSFER_NEW_USER_JOB์ด๋ ์ด๋ฆ์ ๊ฐ์ง JOB์ด ์คํ๋์๋ค. ๊ทธ๋ฐ๋ฐ, ๋ฐ๋ผ์ค๋ parameters ๊ฐ์ด ์๋ค. ์์ธํ ๋ด์ฉ์ Job ์ ๋ฆฌ ๊ธ์์ ๋ค๋ฃจ์ง๋ง, ์ฐ์ ์ Job ์คํ์ ๊ณ ์ ํ๊ฒ ์๋ณํ๊ธฐ ์ํ ๊ฐ์ด๋ผ๊ณ ์ดํดํ๋ฉด ๋๋ค.
random์ด๋ผ๋ ํค๋ก ํ์ฌ๊ธ Long ํ์ ์ value๊ฐ์ด ์๋ค. ์ด๋JobLauncherTestUtils์์ ์ ๊ณตํด์ฃผ๋ ๊ฐ์ผ๋ก ์ค๋ณต Job ์คํ์ ๋ง๊ธฐ ์ํ ๊ฐ์ด๋ค. ์ฐธ๊ณ ๋ก ์ค๋ณต Job ์ฌ๋ถ์ ๊ธฐ์ค์ JobParameter ๊ฐ์ด๋ค. ๋๋ฌธ์, ํ ์คํธ ํ๊ฒฝ์์ ์ค๋ณต Job์ ์ํ ์ ์ฝ์ ๋ง๊ธฐ์ํด ์ ๊ณตํ๊ณ ์๋ค.(Job์ ์ค๋ณต์คํ์ ํ์ฉํ์ง ์์) -
๋๋ฒ์งธ ๋ฐ์ค
Job ๋ด๋ถ์ ์ ์ํ Step์ด ์คํ๋๋ค. Step ๋ด๋ถ์ ์ ์๋ ๋ฐ์ดํฐ ์กฐํ ์ฟผ๋ฆฌ(select ~~)์ ๋ก๊ทธ("1 ๋ช ์ ์ ์ ์ ๋ณด๋ฅผ AML ๋ฑ์ ์๋น์ค๋ก ์ ์ก")๊ฐ ์ฐํ ๊ฒ์ ํ์ธํ ์ ์๋ค. -
์ธ๋ฒ์งธ ๋ฐ์ค Job์ด ์๋ฃ๋์๊ณ , ์ํ๋
COMPLETED๋ก ์ ์์ฒ๋ฆฌ ๋์์์ ์๋ฏธํ๋ค.

