본문 바로가기
Spring & Spring Boot 실무 가이드

Spring Batch란? 개념부터 메타테이블, 코드 예제로 끝내는 입문 가이드

by devcomet 2025. 9. 16.
728x90
반응형

Spring Batch architecture diagram showing Job, Step, and Chunk processing flow for enterprise batch processing
Spring Batch란? 개념부터 메타테이블, 코드 예제로 끝내는 입문 가이드

 

Spring Batch 개념부터 Job/Step 구조, 메타테이블, Tasklet/Chunk 방식까지 완벽 정리. 대용량 데이터 처리를 위한 실무 예제와 성능 최적화 팁을 제공합니다.


Spring Batch란 무엇인가

Spring Batch란 무엇인가 섹션 이미지 - 서버 이미지

 

Spring Batch는 Spring Framework 기반의 배치 처리 프레임워크입니다.

대용량 데이터를 효율적으로 처리하기 위해 설계되었으며, 엔터프라이즈 환경에서 안정적인 배치 작업을 수행할 수 있도록 다양한 기능을 제공합니다.

Spring Batch 개념의 핵심은 '일괄 처리'에 있습니다.

실시간으로 하나씩 처리하기에는 너무 많은 데이터나, 특정 시간에 몰아서 처리해야 하는 작업들을 효율적으로 관리합니다.

 

Spring Batch의 주요 특징

Spring Batch의 주요 특징 정리 이미지

  • 대용량 데이터 처리: 수백만 건의 레코드도 메모리 효율적으로 처리
  • 견고성과 안정성: 실패 지점에서 재시작, 스킵, 재시도 등 다양한 복구 메커니즘
  • 트랜잭션 관리: 청크 단위의 트랜잭션으로 데이터 일관성 보장
  • 모니터링과 추적: 실행 상태, 진행률, 오류 정보 등 상세한 배치 로깅 추적
  • 확장성: 멀티스레드, 병렬 처리, 파티셔닝 지원

배치와 스케줄러 차이점 이해하기

많은 개발자들이 배치스케줄러를 혼동하는 경우가 있습니다.

 

이 둘의 차이점을 명확히 구분해보겠습니다.

구분 배치(Batch) 스케줄러(Scheduler)
역할 데이터 일괄 처리 방식 작업 실행 시점 관리
목적 대용량 데이터 효율적 처리 언제 실행할지 결정
예시 Spring Batch, ETL 도구 Quartz 스케줄러, Cron
관심사 How to Process When to Execute

배치 처리의 핵심

배치는 "어떻게 처리할 것인가"에 집중합니다.

  • 대용량 데이터를 청크 단위로 나누어 처리
  • 메모리 사용량 최적화
  • 트랜잭션 경계 설정
  • 오류 발생 시 복구 전략

스케줄러의 역할

스케줄러는 "언제 실행할 것인가"를 결정합니다.

// Quartz 스케줄러 예시
@Scheduled(cron = "0 0 2 * * ?")  // 매일 새벽 2시
public void runBatchJob() {
    // Spring Batch Job 실행
    jobLauncher.run(job, jobParameters);
}

Spring Batch 스케줄러 차이를 이해한다면, Spring Batch는 배치 처리를 담당하고, 실제 실행 시점은 별도의 스케줄러가 관리한다는 점입니다.


Spring Batch 핵심 아키텍처

Spring Batch 아키텍처 구조도

 

┌─────────────────┐    ┌─────────────────┐
│   JobLauncher   │────│   JobRepository │
└─────────────────┘    └─────────────────┘
         │                       │
         ▼                       │
┌─────────────────┐              │
│       Job       │──────────────┘
│  ┌───────────┐  │
│  │   Step    │  │
│  │ ┌───────┐ │  │
│  │ │Tasklet│ │  │  또는
│  │ └───────┘ │  │
│  │    또는    │  │
│  │ ┌───────┐ │  │
│  │ │ Chunk │ │  │
│  │ │R→P→W  │ │  │
│  │ └───────┘ │  │
│  └───────────┘  │
└─────────────────┘

Job과 Step의 관계

Spring Batch Job Step 차이는 계층적 구조로 이해할 수 있습니다.

  • Job: 배치 작업의 전체 단위 (예: 일매출 집계 배치)
  • Step: Job을 구성하는 개별 처리 단계 (예: 데이터 읽기 → 가공 → 저장)
@Configuration
@EnableBatchProcessing
public class BatchConfiguration {

    @Bean
    public Job salesAggregationJob(JobBuilderFactory jobBuilderFactory,
                                  Step extractStep, Step transformStep, Step loadStep) {
        return jobBuilderFactory.get("salesAggregationJob")
                .start(extractStep)
                .next(transformStep)
                .next(loadStep)
                .build();
    }
}

Spring Batch 메타 테이블 완벽 가이드

Spring Batch는 배치 작업의 상태와 진행 상황을 추적하기 위해 6개의 메타 테이블을 자동으로 생성합니다.

주요 메타 테이블 구조

BATCH_JOB_INSTANCE
├── JOB_INSTANCE_ID (PK)
├── JOB_NAME
└── JOB_KEY

BATCH_JOB_EXECUTION
├── JOB_EXECUTION_ID (PK)
├── JOB_INSTANCE_ID (FK)
├── CREATE_TIME
├── START_TIME
├── END_TIME
├── STATUS
├── EXIT_CODE
└── EXIT_MESSAGE

BATCH_JOB_EXECUTION_PARAMS
├── JOB_EXECUTION_ID (FK)
├── TYPE_CD
├── KEY_NAME
├── STRING_VAL
├── DATE_VAL
├── LONG_VAL
└── DOUBLE_VAL

BATCH_STEP_EXECUTION
├── STEP_EXECUTION_ID (PK)
├── STEP_NAME
├── JOB_EXECUTION_ID (FK)
├── START_TIME
├── END_TIME
├── STATUS
├── COMMIT_COUNT
├── READ_COUNT
├── FILTER_COUNT
└── WRITE_COUNT

BATCH_JOB_EXECUTION_CONTEXT
├── JOB_EXECUTION_ID (PK/FK)
└── SERIALIZED_CONTEXT

BATCH_STEP_EXECUTION_CONTEXT
├── STEP_EXECUTION_ID (PK/FK)
└── SERIALIZED_CONTEXT

Spring Batch 메타 테이블 활용 방법

이러한 메타 테이블을 통해 다음과 같은 정보를 확인할 수 있습니다.

-- 실패한 배치 작업 조회
SELECT je.JOB_EXECUTION_ID, ji.JOB_NAME, je.STATUS, je.EXIT_MESSAGE
FROM BATCH_JOB_EXECUTION je
JOIN BATCH_JOB_INSTANCE ji ON je.JOB_INSTANCE_ID = ji.JOB_INSTANCE_ID
WHERE je.STATUS = 'FAILED';

-- Step별 처리 건수 확인
SELECT STEP_NAME, READ_COUNT, WRITE_COUNT, COMMIT_COUNT
FROM BATCH_STEP_EXECUTION
WHERE JOB_EXECUTION_ID = ?;

Spring Batch 메타데이터 스키마 문서에서 더 자세한 정보를 확인할 수 있습니다.


Tasklet vs Chunk 처리 방식

Spring Batch Tasklet Chunk 방식의 차이점 정리 인포그래픽 이미지

 

Spring Batch에서 Step을 구현하는 두 가지 주요 방식이 있습니다.

Spring Batch Tasklet Chunk 방식의 차이점을 알아보겠습니다.

Tasklet 방식

단일 작업을 처리하는데 적합한 방식입니다.

@Bean
public Step backupStep(StepBuilderFactory stepBuilderFactory) {
    return stepBuilderFactory.get("backupStep")
            .tasklet(new Tasklet() {
                @Override
                public RepeatStatus execute(StepContribution contribution, 
                                          ChunkContext chunkContext) throws Exception {

                    // 데이터 백업 배치 작업 수행
                    performDatabaseBackup();

                    return RepeatStatus.FINISHED;
                }
            })
            .build();
}

Chunk 방식

대량 데이터를 효율적으로 처리하는 방식입니다.

@Bean
public Step processingStep(StepBuilderFactory stepBuilderFactory,
                          ItemReader<Customer> reader,
                          ItemProcessor<Customer, CustomerDto> processor,
                          ItemWriter<CustomerDto> writer) {

    return stepBuilderFactory.get("processingStep")
            .<Customer, CustomerDto>chunk(100)  // Chunk size
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .build();
}

사용 시나리오 비교

구분 Tasklet Chunk
용도 단순 작업, 초기화, 정리 대량 데이터 처리
처리 방식 한 번에 모든 작업 청크 단위로 분할 처리
메모리 효율 전체 데이터 로드 청크 크기만큼만 로드
트랜잭션 전체가 하나의 트랜잭션 청크별 트랜잭션
예시 파일 압축, 디렉토리 정리 고객 데이터 이관, 정산

Spring Batch 핵심 컴포넌트

JobParameters와 ExecutionContext

 

Spring Batch JobParameters는 배치 작업에 전달되는 매개변수입니다.

@Bean
@StepScope
public FlatFileItemReader<Customer> customerReader(@Value("#{jobParameters[inputFile]}") String inputFile) {
    return new FlatFileItemReaderBuilder<Customer>()
            .name("customerReader")
            .resource(new FileSystemResource(inputFile))
            .delimited()
            .names("id", "name", "email")
            .targetType(Customer.class)
            .build();
}

 

ExecutionContext는 Step 간 데이터 공유를 위한 저장소입니다.

@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
    ExecutionContext executionContext = chunkContext.getStepContext()
                                                   .getStepExecution()
                                                   .getJobExecution()
                                                   .getExecutionContext();

    // 데이터 저장
    executionContext.put("processedCount", 1000);

    return RepeatStatus.FINISHED;
}

JobRepository와 관리 도구

Spring Batch JobRepository는 배치 메타데이터를 관리하는 핵심 컴포넌트입니다.

@Configuration
public class BatchConfig {

    @Bean
    public JobRepository jobRepository(DataSource dataSource, 
                                     PlatformTransactionManager transactionManager) throws Exception {
        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        factory.setDataSource(dataSource);
        factory.setTransactionManager(transactionManager);
        factory.setIsolationLevelForCreate("ISOLATION_READ_COMMITTED");
        return factory.getObject();
    }
}

 

JobLauncher, JobExplorer, JobOperator는 각각 다른 역할을 수행합니다

  • JobLauncher: Job 실행
  • JobExplorer: Job 실행 이력 조회
  • JobOperator: Job 제어 (중지, 재시작 등)

실제 활용 사례별 구현 예제

일매출 집계 배치 구현

대표적인 일매출 집계 배치 예시를 살펴보겠습니다.

@Configuration
public class DailySalesJobConfig {

    @Bean
    public Job dailySalesJob(JobBuilderFactory jobBuilderFactory, Step salesAggregationStep) {
        return jobBuilderFactory.get("dailySalesJob")
                .start(salesAggregationStep)
                .build();
    }

    @Bean
    public Step salesAggregationStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory.get("salesAggregationStep")
                .<SalesRecord, DailySales>chunk(1000)
                .reader(salesRecordReader())
                .processor(salesAggregationProcessor())
                .writer(dailySalesWriter())
                .build();
    }

    @Bean
    @StepScope
    public JpaPagingItemReader<SalesRecord> salesRecordReader() {
        return new JpaPagingItemReaderBuilder<SalesRecord>()
                .name("salesRecordReader")
                .entityManagerFactory(entityManagerFactory)
                .queryString("SELECT s FROM SalesRecord s WHERE s.saleDate = :targetDate ORDER BY s.id")
                .parameterValues(Collections.singletonMap("targetDate", LocalDate.now().minusDays(1)))
                .pageSize(1000)
                .build();
    }
}

구독 메일 배치 시스템

구독 메일 배치 처리 예제입니다.

@Component
public class NewsletterItemProcessor implements ItemProcessor<Subscriber, EmailMessage> {

    @Override
    public EmailMessage process(Subscriber subscriber) throws Exception {
        // 구독자별 맞춤 컨텐츠 생성
        String personalizedContent = generatePersonalizedNewsletter(subscriber);

        return EmailMessage.builder()
                .to(subscriber.getEmail())
                .subject("주간 뉴스레터")
                .content(personalizedContent)
                .build();
    }
}

@Component  
public class EmailItemWriter implements ItemWriter<EmailMessage> {

    @Override
    public void write(List<? extends EmailMessage> emails) throws Exception {
        // 배치로 메일 발송
        emailService.sendBulkEmails(emails);

        // 발송 로그 기록
        emails.forEach(email -> 
            log.info("Email sent to: {}", email.getTo())
        );
    }
}

ItemReader, ItemProcessor, ItemWriter 심화

ItemReader, ItemProcessor, ItemWriter 심화 정리 체이닝 이미지

 

Chunk 기반 처리의 3대 핵심 컴포넌트를 자세히 알아보겠습니다.

ItemReader 구현

JpaPagingItemReader를 활용한 대용량 데이터 읽기

@Bean
@StepScope
public JpaPagingItemReader<Customer> customerReader(@Value("#{jobParameters[status]}") String status) {
    return new JpaPagingItemReaderBuilder<Customer>()
            .name("customerReader")
            .entityManagerFactory(entityManagerFactory)
            .queryString("SELECT c FROM Customer c WHERE c.status = :status ORDER BY c.id")
            .parameterValues(Collections.singletonMap("status", status))
            .pageSize(100)  // Paging size
            .build();
}

ItemProcessor 패턴

데이터 변환과 비즈니스 로직 처리

@Component
public class CustomerValidationProcessor implements ItemProcessor<Customer, ValidatedCustomer> {

    @Override
    public ValidatedCustomer process(Customer customer) throws Exception {
        // 유효성 검증
        if (!isValidEmail(customer.getEmail())) {
            return null;  // null 반환 시 해당 아이템 필터링
        }

        // 데이터 변환
        return ValidatedCustomer.builder()
                .customerId(customer.getId())
                .email(customer.getEmail().toLowerCase())
                .status("VALIDATED")
                .processedAt(LocalDateTime.now())
                .build();
    }
}

ItemWriter 최적화

배치 처리로 성능 향상

@Component
public class JpaItemWriter<T> implements ItemWriter<T> {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void write(List<? extends T> items) throws Exception {
        for (T item : items) {
            entityManager.merge(item);
        }
        // 청크 단위로 flush
        entityManager.flush();
        entityManager.clear();
    }
}

성능 최적화 핵심 전략

Chunk size와 Paging size 설정

Chunk size Paging size 관계는 성능에 직접적인 영향을 미칩니다.

@Bean
public Step optimizedStep(StepBuilderFactory stepBuilderFactory) {
    return stepBuilderFactory.get("optimizedStep")
            .<Customer, CustomerDto>chunk(1000)  // Chunk size
            .reader(customerReader())  // Page size: 1000
            .processor(customerProcessor())
            .writer(customerWriter())
            .taskExecutor(taskExecutor())  // 병렬 처리
            .build();
}

 

권장사항

  • Chunk size == Paging size: 메모리 사용량 최적화
  • 정렬 보장: ORDER BY 절 필수 (데이터 일관성)
  • 트랜잭션 크기: 너무 크면 롤백 비용 증가, 너무 작으면 오버헤드 증가

배치 로깅 추적 트랜잭션 관리

@Configuration
public class BatchConfig {

    @Bean
    public PlatformTransactionManager batchTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
        transactionManager.setDefaultTimeout(300);  // 5분 타임아웃
        return transactionManager;
    }

    @Bean
    public Step transactionalStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory.get("transactionalStep")
                .<Customer, Customer>chunk(100)
                .reader(customerReader())
                .writer(customerWriter())
                .transactionManager(batchTransactionManager(null))
                .build();
    }
}

JobExecution과 StepExecution 모니터링

 

JobExecution StepExecution을 활용한 배치 작업 추적

@Component
public class BatchJobExecutionListener implements JobExecutionListener {

    @Override
    public void beforeJob(JobExecution jobExecution) {
        log.info("Job {} started at {}", 
                jobExecution.getJobInstance().getJobName(),
                jobExecution.getCreateTime());
    }

    @Override
    public void afterJob(JobExecution jobExecution) {
        if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
            log.info("Job {} completed successfully", 
                    jobExecution.getJobInstance().getJobName());
        } else {
            log.error("Job {} failed with status {}", 
                    jobExecution.getJobInstance().getJobName(),
                    jobExecution.getStatus());
        }

        // Step별 실행 결과 확인
        jobExecution.getStepExecutions().forEach(stepExecution -> {
            log.info("Step: {}, Read: {}, Write: {}, Skip: {}", 
                    stepExecution.getStepName(),
                    stepExecution.getReadCount(),
                    stepExecution.getWriteCount(),
                    stepExecution.getSkipCount());
        });
    }
}

Spring Batch 테스트 코드 작성

배치 작업의 안정성을 보장하기 위한 Spring Batch 테스트 코드 예제입니다.

@SpringBatchTest
@SpringBootTest
@Transactional
class CustomerProcessingJobTest {

    @Autowired
    private TestJobLauncher testJobLauncher;

    @Autowired
    private Job customerProcessingJob;

    @Autowired
    private JobRepository jobRepository;

    @Test
    public void testCustomerProcessingJob() throws Exception {
        // Given
        JobParameters jobParameters = new JobParametersBuilder()
                .addString("inputFile", "test-customers.csv")
                .addDate("date", new Date())
                .toJobParameters();

        // When
        JobExecution jobExecution = testJobLauncher.run(customerProcessingJob, jobParameters);

        // Then
        assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
        assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());

        // Step별 검증
        StepExecution stepExecution = jobExecution.getStepExecutions().iterator().next();
        assertEquals(100, stepExecution.getReadCount());
        assertEquals(90, stepExecution.getWriteCount());  // 10개 필터링
        assertEquals(0, stepExecution.getSkipCount());
    }

    @Test
    public void testJobWithInvalidData() throws Exception {
        // Given
        JobParameters jobParameters = new JobParametersBuilder()
                .addString("inputFile", "invalid-customers.csv")
                .addDate("date", new Date())
                .toJobParameters();

        // When
        JobExecution jobExecution = testJobLauncher.run(customerProcessingJob, jobParameters);

        // Then - 오류 처리 검증
        assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());

        StepExecution stepExecution = jobExecution.getStepExecutions().iterator().next();
        assertTrue(stepExecution.getSkipCount() > 0);  // 스킵된 레코드 존재
    }
}

테스트 유틸리티 활용

@TestConfiguration
public class BatchTestConfig {

    @Bean
    @Primary
    public JobLauncherTestUtils jobLauncherTestUtils() {
        return new JobLauncherTestUtils();
    }

    @Bean
    @Primary  
    public JobRepositoryTestUtils jobRepositoryTestUtils() {
        return new JobRepositoryTestUtils();
    }
}

Spring Batch 테스트 가이드 문서를 참조하여 더 다양한 테스트 시나리오를 구현할 수 있습니다.


실무 적용 시 고려사항

재시작 및 장애 복구

@Bean
public Job restartableJob(JobBuilderFactory jobBuilderFactory, Step processingStep) {
    return jobBuilderFactory.get("restartableJob")
            .start(processingStep)
            .incrementer(new RunIdIncrementer())  // 재실행 가능
            .listener(jobExecutionListener())
            .build();
}

@Bean  
public Step faultTolerantStep(StepBuilderFactory stepBuilderFactory) {
    return stepBuilderFactory.get("faultTolerantStep")
            .<Customer, Customer>chunk(100)
            .reader(customerReader())
            .processor(customerProcessor()) 
            .writer(customerWriter())
            .faultTolerant()
            .skip(ValidationException.class)  // 특정 예외 스킵
            .skipLimit(10)  // 최대 스킵 횟수
            .retry(TransientDataException.class)  // 재시도 대상 예외
            .retryLimit(3)  // 재시도 횟수
            .build();
}

배치 모니터링 구축

Spring Boot Actuator와 연동한 배치 상태 모니터링

@Component
public class BatchMetricsCollector {

    private final MeterRegistry meterRegistry;
    private final JobExplorer jobExplorer;

    @EventListener
    public void handleJobExecution(JobExecutionEvent event) {
        JobExecution jobExecution = event.getJobExecution();

        // 메트릭 수집
        Timer.Sample sample = Timer.start(meterRegistry);
        sample.stop(Timer.builder("batch.job.duration")
                .tag("job.name", jobExecution.getJobInstance().getJobName())
                .tag("status", jobExecution.getStatus().toString())
                .register(meterRegistry));

        // 처리량 메트릭
        jobExecution.getStepExecutions().forEach(stepExecution -> {
            Gauge.builder("batch.step.read.count")
                    .tag("job.name", jobExecution.getJobInstance().getJobName())
                    .tag("step.name", stepExecution.getStepName())
                    .register(meterRegistry, stepExecution, StepExecution::getReadCount);
        });
    }
}

분산 처리와 스케일링

@Bean
public Step partitionedStep(StepBuilderFactory stepBuilderFactory,
                           Partitioner partitioner,
                           Step workerStep) {
    return stepBuilderFactory.get("partitionedStep")
            .partitioner("workerStep", partitioner)
            .step(workerStep)
            .gridSize(4)  // 파티션 수
            .taskExecutor(taskExecutor())
            .build();
}

@Component
public class DatePartitioner implements Partitioner {

    @Override
    public Map<String, ExecutionContext> partition(int gridSize) {
        Map<String, ExecutionContext> partitions = new HashMap<>();

        LocalDate startDate = LocalDate.now().minusDays(30);
        LocalDate endDate = LocalDate.now();

        long totalDays = ChronoUnit.DAYS.between(startDate, endDate);
        long daysPerPartition = totalDays / gridSize;

        for (int i = 0; i < gridSize; i++) {
            ExecutionContext context = new ExecutionContext();
            LocalDate partitionStart = startDate.plusDays(i * daysPerPartition);
            LocalDate partitionEnd = (i == gridSize - 1) ? endDate : 
                                   startDate.plusDays((i + 1) * daysPerPartition - 1);

            context.put("startDate", partitionStart);
            context.put("endDate", partitionEnd);
            partitions.put("partition" + i, context);
        }

        return partitions;
    }
}

마무리

Spring Batch는 엔터프라이즈급 배치 처리를 위한 강력한 프레임워크입니다.

Job과 Step 기반의 체계적인 아키텍처, 메타 테이블을 통한 실행 추적, Tasklet과 Chunk 방식의 유연한 구현 옵션을 제공합니다.

성공적인 배치 시스템 구축을 위해서는 적절한 청크 크기 설정, 트랜잭션 관리, 장애 복구 전략, 그리고 충분한 테스트가 필요합니다.

Spring Batch 공식 레퍼런스 가이드를 통해 더 자세한 내용을 학습하고, 실제 프로젝트에 적용해보시기 바랍니다.

대용량 데이터 처리가 필요한 현대의 애플리케이션에서 Spring Batch는 더욱 중요한 역할을 할 것입니다.

체계적인 학습과 실습을 통해 안정적이고 효율적인 배치 시스템을 구축해보세요.


같이 보면 좋은 글

 

Spring Boot 테스트 컨테이너 실전 가이드 - Docker 없이 통합 테스트 자동화

현대적인 마이크로서비스 아키텍처에서 통합 테스트의 중요성이 날로 증가하고 있습니다.특히 테스트컨테이너 스프링부트 환경에서는 실제 데이터베이스, 메시지 큐, 외부 서비스와의 연동을

notavoid.tistory.com

 

Spring Boot 3.0 Native Image 완벽 가이드 - GraalVM으로 초고속 애플리케이션 만들기

"우리 서비스가 시작되는데 왜 이렇게 오래 걸리지?"많은 개발자들이 한 번쯤 겪어본 고민입니다.전통적인 Spring Boot 애플리케이션은 강력한 기능을 제공하지만,JVM 특성상 시작 시간이 길고 메모

notavoid.tistory.com

 

Spring Cloud Stream으로 이벤트 드리븐 마이크로서비스 구축: 실무 완벽 가이드

서론: 왜 이벤트 드리븐 아키텍처가 필요한가?현대 소프트웨어 개발에서 마이크로서비스 아키텍처는 선택이 아닌 필수가 되었습니다.하지만 서비스 간 통신과 데이터 일관성을 유지하는 것은

notavoid.tistory.com

 

[Spring Security] Spring Security의 FilterChain 구조 완벽 이해

안녕하세요! 오늘은 Spring Security의 핵심 엔진인 FilterChain에 대해 실무 관점에서 깊이 있게 알아보겠습니다.대규모 운영 환경에서 실제로 마주하는 성능 이슈와 해결 방법을 중심으로 설명드리겠

notavoid.tistory.com

 

MSA 완벽 가이드: 마이크로서비스 아키텍처 개념·장점·도입 전략 총정리

MSA(마이크로서비스 아키텍처)는 대규모 애플리케이션을 작은 독립적인 서비스들로 분해하여 개발 효율성과 확장성을 극대화하는 현대적인 소프트웨어 아키텍처 패턴입니다. 현대 소프트웨어

notavoid.tistory.com

 

728x90
반응형