반응형

안녕하세요.

이번 포스트에서는 스프링 배치에서 비즈니스 로직을 담당하고 있는 processor를 학습 하겠습니다.

read 부분은 앞에서의 포스트 부분과 동일합니다.

https://junlab.tistory.com/274

 

[Spring Batch] 스프링 배치를 활용 하여 파일 읽기 (reader)

이번 포스트에서는 스프링 배치를 활용하여 파일을 읽어 보겠습니다. 1. sample 파일을 하나 만들어주세요. ID,lastName,firstName,position,birthYear,debutYear AbduKa00,Abdul-Jabbar,Karim,rb,1974,1996 AbduR..

junlab.tistory.com

 

processor는 생략이 가능한데, job에 추가를 하도록 하겠습니다.

1. processor 추가

@JobScope
@Bean
public Step flatFileStep(FlatFileItemReader<PlayerDto> playerFileItemReader,
                         ItemProcessor<PlayerDto, PlayerSalaryDto> playerSalaryItemProcessor
) {
    return stepBuilderFactory.get("flatFileStep")
            .<PlayerDto, PlayerSalaryDto>chunk(5)
            .reader(playerFileItemReader)
            .processor(playerSalaryItemProcessor)
            .writer(new ItemWriter<>() {
                @Override
                public void write(List<? extends PlayerSalaryDto> items) throws Exception {
                    items.forEach(System.out::println);
                }
            })
            .build();
}

playerSalaryItemProcessor는 아직 만들지 않았습니다. 일단은 processor를 추가하였는데요. 읽을 때는 PlayerDto로 읽고, 쓸 때는 PlayerSalaryDto로 쓰게 되겠습니다. 프로세스 단에서 작업을 처리하고 PlayerSalaryDto로 변경을 해주고 write로 넘겨 주시면 되겠습니다.

2. playerSalaryItemProcessor 작성

@StepScope
@Bean
public ItemProcessor<PlayerDto, PlayerSalaryDto> playerSalaryItemProcessor(
	PlayerSalaryService playerSalaryService) {
    
    return new ItemProcessor<PlayerDto, PlayerSalaryDto>() {
        @Override
        public PlayerSalaryDto process(PlayerDto item) throws Exception {
            return playerSalaryService.calcSalary(item);
        }
    };
}

ItemProcessor에서는 ItemProcessor를 반환해주면 되고, ItemReader에서는 ItemReader를 반환 해주시면 되겠습니다. 특정 기능이 추가 되는 내용이 있다면 각각의 스텝 안에서 ItemReader나 ItemProcessor를 구현한 객체를 사용해서 반환해주면 되고요. 스프링 배치는 큰 틀을 익히게 되면 필요로 하는 기능은 문서들을 보고 찾아서 사용을 해주시면 되겠습니다.

ItemProcessor에서는 연봉을 추가 하였고 서비스단을 만들어서 PlayerSalaryDto를 반환 하도록 하였습니다.

 

3. 서비스단 로직 작성

@Service
public class PlayerSalaryService {

    public PlayerSalaryDto calcSalary(PlayerDto player) {
        int salary = (Year.now().getValue() - player.getBirthYear()) * 1000000;
        return PlayerSalaryDto.of(player, salary);
    }
}

서비스 로직을 분리 하였습니다. 서비스 로직을 분리 후에 호출은 배치 내의 프로세스에서 호출만 하도록 하였습니다.

salary(연봉) 추가

 

호출을 하게 되면 연봉 부분이 추가 된 모습을 보실 수 있습니다.

 

프로세스에는 프로세스를 작성해서 사용할 수도 있지만 adapter를 사용할 수도 있습니다.

@StepScope
@Bean
public ItemProcessorAdapter playerSalaryItemProcessorAdapter(
	PlayerSalaryService playerSalaryService) {
    
    ItemProcessorAdapter<PlayerDto, PlayerSalaryDto> adapter = new ItemProcessorAdapter<>();
    adapter.setTargetObject(playerSalaryService);
    adapter.setTargetMethod("calcSalary");      // playerSalaryService의 메소드명을 명시
    return adapter;

}

Adapter를 사용하면 구현이 되어 있는 서비스 로직을 adapter를 부착 시켜주시고 step에서 호출을 해주시면 되겠습니다.

반응형
반응형

이번 포스트에서는 스프링 배치를 활용하여 파일을 읽어 보겠습니다.

 

1. sample 파일을 하나 만들어주세요.

ID,lastName,firstName,position,birthYear,debutYear
AbduKa00,Abdul-Jabbar,Karim,rb,1974,1996
AbduRa00,Abdullah,Rabih,rb,1975,1999
AberWa00,Abercrombie,Walter,rb,1959,1982
AbraDa00,Abramowicz,Danny,wr,1945,1967
AdamBo00,Adams,Bob,te,1946,1969
AdamCh00,Adams,Charlie,wr,1979,2003

파일 구성은 csv 형태로 되어 있으며 첫줄 은 제목이 작성이 되어 있습니다.

 

2. 잡을 생성 하겠습니다.

private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;

@Bean
public Job flatFileJob(Step flatFileStep) {
    return jobBuilderFactory.get("flatFileJob")
            .incrementer(new RunIdIncrementer())
            .start(flatFileStep)
            .build();
}

잡을 만드는 방법은 앞에서의 학습과 동잉ㄹ합니다. 잡 / 스텝 빌더 팩도리를 주입 받고, flatFileJob이라는 잡을 생성하였습니다.

start 부분에는 스텝을 생성하였는데, 아직 스텝은 만들지 않았으니 스텝을 생성하겠습니다.

3. 스텝 코드 작성

@JobScope
@Bean
public Step flatFileStep(FlatFileItemReader<PlayerDto> playerFileItemReader) {
    return stepBuilderFactory.get("flatFileStep")
            .<PlayerDto, PlayerDto>chunk(5)
            .reader(playerFileItemReader)
            .writer(new ItemWriter<>() {
                @Override
                public void write(List<? extends PlayerDto> items) throws Exception {
                    items.forEach(System.out::println);
                }
            })
            .build();
}

chunk는 작업을 처리하는 개수입니다. 스텝에서는 chunk단위로 작업을 처리하게 되는데 현재 5개 단위로 작업을 처리 하겠습니다. 즉, 5개 아이템을 읽고, 이렇게 읽은 5개 아이템을 처리 한 다음에 5개 아이템을 기록 하겠습니다. 5개 작업 단위로 커밋이 쳐지는 걸 알 수 있습니다.

<PlayerDto, PlyerDto>는  PlayerDto 타입으로 읽어 드리고, PlayerDto 타입으로 쓰여지겠습니다.

reader()에는 파일을 읽어 들일 수 있는 FlatFileItemReader을 등록 하였고, writer()에는 이렇게 읽은 파일 내용을 콘솔에 출력하는 모습입니다.

이번 시간에는 파일을 읽어 들이는 학습을 진행하기 때문에 출력은 단순 출력을 진행합니다.

 

4. playerFileItemReader작성

@StepScope
@Bean
public FlatFileItemReader<PlayerDto> playerFileItemReader() {
    return new FlatFileItemReaderBuilder<PlayerDto>()
            .name("playerFileItemReader")
            .lineTokenizer(new DelimitedLineTokenizer())
            .linesToSkip(1)
            .fieldSetMapper(new PlayerFieldSetMapper())
            .resource(new FileSystemResource("player-list.txt"))
            .build();
}

DelimitedLineTokenizer을 작성하면 디폴트로 콤마로 파일을 나눌 수 있습니다. 첫번째 줄은 제목임으로 스킵을 하고 읽어들인 데이터를 매퍼를 통해서 PlayerDto로 만들게 됩니다.

 

DTO)

@Data
public class PlayerDto {
    private String ID;
    private String lastName;
    private String firstName;
    private String position;
    private int birthYear;
    private int debutYear;
}

 

5. 매퍼 함수 작성

public class PlayerFieldSetMapper implements FieldSetMapper<PlayerDto> {
    @Override
    public PlayerDto mapFieldSet(FieldSet fieldSet) throws BindException {
        PlayerDto dto = new PlayerDto();
        dto.setID(fieldSet.readString(0));
        dto.setLastName(fieldSet.readString(1));
        dto.setFirstName(fieldSet.readString(2));
        dto.setPosition(fieldSet.readString(3));
        dto.setBirthYear(fieldSet.readInt(4));
        dto.setDebutYear(fieldSet.readInt(5));

        return dto;
    }
}

읽어들인 파일 내용을 객체로 변경하기 위해서 FieldSetMapper를 구현한 매퍼를 작성하였습니다.

 

스프링 배치 파일 읽기

 

스프링 배치를 사용하지 않고 파일을 읽어들이는 것은 A부터 Z까지 개발을 해야 되지만, 스프링 배치를 사용하면 주어진 기능을 사용하면 되겠습니다. 또한 형식이 정형화가 되어 있기 때문에 장기적으로 본다면 유지보수에도 유리 하겠습니다.

 

공식문서)

https://docs.spring.io/spring-batch/docs/current/reference/html/readersAndWriters.html#flatFileItemReader

 

ItemReaders and ItemWriters

So far, only delimited files have been discussed in much detail. However, they represent only half of the file reading picture. Many organizations that use flat files use fixed length formats. An example fixed length file follows: The following example sho

docs.spring.io

반응형
반응형

안녕하세요.

이번 시간에는 스프링 배치(spring batch)의 job 과 step이 실행 되었을 때, 상태 확인 및 무언가 작업을 추가하기 위한 리스너 사용법을 학습하겠습니다.

 

잡 리스너는 JobExecutionListener를 반환하는 메소드를 만들고 Job에 listener로 등록을 해주시면 되는데요.

아래 코드를 보도록 하겠습니다.

@Bean
public Job advancedJob(JobExecutionListener jobExecutionListener,
                       Step advancedStep) {
    return jobBuilderFactory.get("advancedJob")
            .incrementer(new RunIdIncrementer())
            .validator(new LocalDateParameterValidator("targetDate"))
            .listener(jobExecutionListener)
            .start(advancedStep)
            .build();
}

@JobScope
@Bean
public JobExecutionListener jobExecutionListener() {
    return new JobExecutionListener() {
        @Override
        public void beforeJob(JobExecution jobExecution) {
            log.info("[JobExecutionListener#beforeJob] jobExecution is " + jobExecution.getStatus());
        }

        @Override
        public void afterJob(JobExecution jobExecution) {
            if (jobExecution.getStatus() == BatchStatus.FAILED) {
                log.error("[JobExecutionListener#afterJob] jobExecution is " + jobExecution.getStatus());
            }

            log.info("[JobExecutionListener#afterJob] jobExecution is " + jobExecution.getStatus());
        }
    };
}

jobExecutionListener() 메소드를 만들고 JobExecutionListener를 반환합니다. 리스너를 통해서 필요로 하는 작업을 구현 해주셔야 되는데요. job이 실행되기 전에는 beforeJob이 실행이 되고 job이 끝난 이후에는 aftetJob이 실행이 되겠습니다.

데이터베이스에 실행 결과를 쌓아 줄 수도 있고, 잡이 끝난 후에 배치 상태가 오류가 발생하였다면 텔레그램이나 슬렉같은 메신저로 알람을 받아 볼 수 있도록 구현을 해주 실 수도 있겠습니다.

 

step의 작업의 리스턴도 job과 유사합니다.

@JobScope
@Bean
public Step advancedStep(StepExecutionListener stepExecutionListener,
                         Tasklet advancedTasklet) {
    return stepBuilderFactory.get("advancedStep")
            .listener(stepExecutionListener)
            .tasklet(advancedTasklet)
            .build();
}

@StepScope
@Bean
public StepExecutionListener stepExecutionListener() {
    return new StepExecutionListener() {
        @Override
        public void beforeStep(StepExecution stepExecution) {
            log.info("[StepExecutionListener#beforeStep] stepExecution is " + stepExecution.getStatus());
        }

        @Override
        public ExitStatus afterStep(StepExecution stepExecution) {
            log.info("[StepExecutionListener#afterStep] stepExecution is " + stepExecution.getStatus());
            return stepExecution.getExitStatus();
        }
    };
}

StepExecutionListener를 반환하하는 메소드를 만들고 step설정에 listener를 등록 해주시면 되겠습니다.

#beforeJog이 실행이 되고 step 작업 후에 마지막으로 #afterJob이 실행이 되는 모습을 보실 수 있겠습니다.

반응형
반응형

간략하게 css의 단위에 대해서 정리를 하겠습니다.

 

px: 픽셀이라고 부릅니다. 보통 화면의 크기는 1920 * 1080을 많이 사용하는데 px은 가로 1920개의 px이 있고  세로는 1080개의 px이 있겠습니다. 즉, 하나의 점을 의미합니다.

 

%: 상대적으로 나타내는 백분율을 나타냅니다.

 

em: 요소의 글꼴의 크기(font-size)가 되겠습니다. 글씨 크기가 10px이면 1em은 10px을 의미하겠습니다.

 

rem: r의 의미는 루트인 최상위 글씨 크기를 의미하겠습니다.

 

vw: 뷰포트의 세로 너비의 백분율을 의미 합니다. 뷰포트라 함은 브라우저, 즉 페이지 전체를 의미하겠습니다. 1vw라 함은 가로 너비의 백분의 1만큼의 너비입니다. 50vw를 주면 부모가 어떻든지 간에 절반을 차지하겠습니다.

50vw 값

 

vh: vw와 내용은 동일하며 세로 너비를 의미합니다.

 

See the Pen 단위 by Myeongjun Go (@junngo) on CodePen.

반응형
반응형

안녕하세요.

VSCode를 우분투 환경에서 사용을 하고 있는데요. 글자 입력은 되는데 특정 글자들이 톡톡 안되는 문제가 있습니다.

예를 들면 다음과 같은 문제입니다.

같이 코딩해요를 입력하면 "같" 이라는 글자는 "가ㅌ"로 입력되는 문제가 있습니다.

이외에도 특정 글자들에 대해서 이런 문제가 발생하는데요.

다음 문제를 해결 하겠습니다.

 

1.  Crtl + Shift + p를 누른신 다음에 setting을 입력하고 세팅 화면으로가주세요.

 

2. 세팅 화면에서 font family를 입력해주세요.

 

3. 그리고 'Droid Sans Fallback' 을 제거 해주시면 되겠습니다.

 

그리고 나면 정상적으로 한글 입력이 되겠습니다.

감사합니다 :)

반응형

+ Recent posts