Skip to content

Latest commit

ย 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
ย 
ย 
ย 
ย 
ย 
ย 

README.md

1. ๋ฐฐ์น˜ ๊ธฐ๋ณธ ๊ตฌ์„ฑ(Job & Step)

์˜ˆ์‹œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๊ธฐ๋ณธ์ ์ธ Job์„ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•ด๋ณธ๋‹ค.
์Šคํ”„๋ง ๋ฐฐ์น˜๋ฅผ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์•Œ์•„๋ณด๊ธฐ ์ „์— ๊ธฐ๋ณธ์ ์ธ job ๊ตฌ์„ฑ์„ ํŒŒ์•…ํ•œ๋‹ค.

1.1 Job

Spring Batch๋Š” Job์ด๋ผ๋Š” ๋‹จ์œ„๋กœ ์ž‘์—…์„ ๋ถ„๋ฅ˜ํ•œ๋‹ค.
์†Œ์œ„ ๋งํ•˜๋Š” Batch Job์„ ์˜๋ฏธํ•˜๋Š”๋ฐ ์ด Job์€ ์ผ๋ จ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, "ํ•œ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๋ฐฐ์น˜๋ฅผ ์ž‘์„ฑํ•ด์•ผ๋ผ"๋ผ๊ณ  ํ•œ๋‹ค๋ฉด, ํ•˜๋‚˜์˜ Batch Job์„ ๋งŒ๋“ ๋‹ค๋Š” ์˜๋ฏธ๋‹ค.


1.2 Step

์ด Job์€ Step์ด๋ผ๋Š” ๋…€์„์„ ํ•œ ๊ฐœ ์ด์ƒ ๊ฐ€์ง„๋‹ค. ์ด Step์€ ์‹ค์ œ ๋ฐฐ์น˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์—ญํ• ์„ ๊ฐ€์ง€๋ฉฐ, ๋ฐ์ดํ„ฐ ์กฐํšŒ๋‚˜ ๋ฐฐ์น˜ ๋กœ์ง ์‹คํ–‰ ๋“ฑ๊ณผ ๊ฐ™์ด Job์„ ์‹ค์ œ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๋…€์„์ด๋‹ค. ์ฆ‰, ์ด Step์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์—ญํ• ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.
๋งŒ์•ฝ ์•„๋ž˜์™€ ๊ฐ™์ด Step์ด ์—ญํ• ๋ณ„(e.g. ๋ฐ์ดํ„ฐ ์กฐํšŒ ์ˆ˜ํ–‰, ์‹ค์ œ ๋ฐฐ์น˜ ๋กœ์ง ์ˆ˜ํ–‰, ๋ฐ์ดํ„ฐ ์ €์žฅ ์ˆ˜ํ–‰ ๋“ฑ)๋กœ ๋ถ„๋ฆฌ๋œ๋‹ค๋ฉด, ๊ฐ ์—ญํ• ๋ณ„ Step์ด ํ•˜๋‚˜์˜ Job ์•ˆ์— ๋ฌถ์ด๋Š” ์…ˆ์ด๋‹ค.

job๊ณผ step์˜ ๊ด€๊ณ„

ํ•˜๋‚˜์˜ Job์•ˆ์—๋Š” ์—ฌ๋Ÿฌ Step์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด Step๋“ค์ด ๋ชจ์—ฌ์„œ 1.1์˜ Job์—์„œ ์ด์•ผ๊ธฐํ•œ ์ผ๋ จ์˜ ์ž‘์—…์ด ๋˜๋Š” ๊ฒƒ์ด๋‹ค.
๋˜ํ•œ, Job์ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ Step์„ ๊ฐ€์ง€๋“ฏ, Step๋„ ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑํ•˜๋А๋ƒ์— ๋”ฐ๋ผ์„œ ๋‚ด๋ถ€ ๊ตฌ์„ฑ์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๋Š” Step๊ณผ ๊ด€๋ จ๋œ ๊ธ€์—์„œ ์ž์„ธํžˆ ๋‹ค๋ฃฌ๋‹ค.


2. Job & Step ์ •์˜

์‹ค์ œ๋กœ Job์„ ์ •์˜ํ•˜๊ณ , Step์„ ํ†ตํ•ด ๋ฐฐ์น˜ ๋กœ์ง์„ ๊ตฌํ˜„ํ•ด๋ณด์ž. [์ฝ”๋“œ ์ฐธ๊ณ ]

- UserService.java

@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() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

- TransferNewUserJobConfiguration.java

@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์— ๋ผ์›Œ ๋„ฃ์—ˆ๋‹ค๋Š” ์ ์ด๋‹ค.
ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ ์‹œ์ผœ๋ณด์ž.


2.1 ํ…Œ์ŠคํŠธ ์ž‘์„ฑ

[์ฝ”๋“œ์ฐธ๊ณ ]

@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 ์‹คํ–‰ ์ˆœ์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  1. Job ์‹คํ–‰ ์‹œ์ž‘
  2. ์ •์˜ํ•œ Step ์‹คํ–‰
  3. Job ์ข…๋ฃŒ (๊ฒฐ๊ณผ ๋ฐ˜ํ™˜)

์‹ค์ œ ๋กœ๊ทธ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

job ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๊ฒฐ๊ณผ

  1. ์ฒซ๋ฒˆ์งธ ๋ฐ•์Šค
    TRANSFER_NEW_USER_JOB์ด๋ž€ ์ด๋ฆ„์„ ๊ฐ€์ง„ JOB์ด ์‹คํ–‰๋˜์—ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, ๋”ฐ๋ผ์˜ค๋Š” parameters ๊ฐ’์ด ์žˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Job ์ •๋ฆฌ ๊ธ€์—์„œ ๋‹ค๋ฃจ์ง€๋งŒ, ์šฐ์„ ์€ Job ์‹คํ–‰์„ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ’์ด๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.
    random์ด๋ผ๋Š” ํ‚ค๋กœ ํ•˜์—ฌ๊ธˆ Long ํƒ€์ž…์˜ value๊ฐ’์ด ์žˆ๋‹ค. ์ด๋Š” JobLauncherTestUtils์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” ๊ฐ’์œผ๋กœ ์ค‘๋ณต Job ์‹คํ–‰์„ ๋ง‰๊ธฐ ์œ„ํ•œ ๊ฐ’์ด๋‹ค. ์ฐธ๊ณ ๋กœ ์ค‘๋ณต Job ์—ฌ๋ถ€์˜ ๊ธฐ์ค€์€ JobParameter ๊ฐ’์ด๋‹ค. ๋•Œ๋ฌธ์—, ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ์ค‘๋ณต Job์— ์˜ํ•œ ์ œ์•ฝ์„ ๋ง‰๊ธฐ์œ„ํ•ด ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค.(Job์€ ์ค‘๋ณต์‹คํ–‰์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Œ)

  2. ๋‘๋ฒˆ์งธ ๋ฐ•์Šค
    Job ๋‚ด๋ถ€์— ์ •์˜ํ•œ Step์ด ์‹คํ–‰๋œ๋‹ค. Step ๋‚ด๋ถ€์— ์ •์˜๋œ ๋ฐ์ดํ„ฐ ์กฐํšŒ ์ฟผ๋ฆฌ(select ~~)์™€ ๋กœ๊ทธ("1 ๋ช…์˜ ์œ ์ € ์ •๋ณด๋ฅผ AML ๋“ฑ์˜ ์„œ๋น„์Šค๋กœ ์ „์†ก")๊ฐ€ ์ฐํžŒ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

  3. ์„ธ๋ฒˆ์งธ ๋ฐ•์Šค Job์ด ์™„๋ฃŒ๋˜์—ˆ๊ณ , ์ƒํƒœ๋Š” COMPLETED๋กœ ์ •์ƒ์ฒ˜๋ฆฌ ๋˜์—ˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค.