본문 바로가기
프로그래밍/java

[Srping Batch] 스프링 배치를 이용하여 예,적금 데이터 동기화하기 1편

by 뜨끔쓰 2022. 9. 7.
728x90
728x90

스프링 배치 예, 적금 데이터 동기화 시리즈

[현재글]2022.09.07 - [프로그래밍/java] - [Srping Batch] 스프링 배치를 이용하여 예,적금 데이터 동기화하기 1편

2022.09.09 - [프로그래밍/java] - [Srping Batch] 스프링 배치를 이용하여 예,적금 데이터 동기화하기 2편

2022.09.10 - [프로그래밍/java] - [Srping Batch] 스프링 배치를 이용하여 예,적금 데이터 동기화하기 3편

2022.09.12 - [프로그래밍/java] - [Srping Batch] 스프링 배치를 이용하여 예,적금 데이터 동기화하기 4편

 


안녕하세요, 이번에는 Spring Batch강의를 듣고 개인적인 프로젝트에 사용할 예적금 데이터를 매일 동기화하는 배치를 만들며 내용 정리 및 공유를 하려고 합니다.

 

이번 시리즈 글을 읽으시면서 Spring Batch를 한번 사용해보시는건 어떨까요

 

일단 동기화를 진행하기위해 금융감독원에서 제공해주는 OPEN API를 사용하도록 해봅시다!


 

금융감독원 OPEN API 사이트 바로가기

 

홈|오픈 API|오픈 API 개요|오픈 API 소개 | 금융상품 통합 비교공시 시스템

소개 오픈 API(Application Program Interface)란 정보 이용자들에게 정보를 공개하기 위해 제공하는 일정한 규약으로 외부 개발자가 금융상품 통합 비교공시 데이터를 타 시스템에서 활용할 수 있도록 XM

finlife.fss.or.kr

사용하려면 인증키가 필요하기 때문에 인증키 신청을 해봅시다! 한지 오래돼서 얼마나 걸렸는지는 기억이 안나는데 금방 됐던 것 같아요.

 

인증키 신청페이지

하고나면 작성하신 메일로 아래처럼 인증키가 옵니다. 이걸 잘 적어둡시다!

메일로 수신한 인증키

그럼 이제 기본적인 작업은 끝났으며, 일단 설계를 생각해보도록 하죠. 일단 기본적으로 API종류를 흝겨 보면

API 종류

 

이런식으로 여러가지가 있습니다. 많지는 않죠? 저희가 필요한건 예, 적금이기 때문에 두개만 필요할수도 있겠지만

 

저같은 경우 해당 예금이나, 적금의 은행정보도 가지고 오고 싶기 때문에 금융회사 API까지 3가지 API를 사용해보겠습니다.

 

API구조는 대략 1:N 일대다 형식으로 구성되어 있다고 생각하면됩니다.

 

하나의 은행에 여러가지 예금,적금 상품이 있기 때문이죠?! 우선 금융회사 API의 요청, 결과 값을 확인해봅시다.

 

금융회사 API 요청변수

금융회사 API 요청변수

서비스명은 그대로 사용하고 응답방식은 JSON을 사용하기 때문에 xml 대신 json을 쓰시고,

인증키는 발급받은 인증키를 넣어주도록 합시다!

권역코드의 경우 은행, 저축은행 두군데를 동기화 하기때문에 020000(은행), 030300(저축은행)

페이지의 경우 유동적으로 변경할것이기 떄문에 일단은 1로 셋팅하도록합시다.

 

이제 결과값으로 어떤값을 주는지 보시죠.

 

금융회사API 결과 변수

금융회사 API 결과 변수

이런 형태의 결과값을 보내주는데 설명은 따로 안해도 될 것 같아요. 결과값을 참고하여 결과 DTO를 작성해보도록합시다.

대략적인 프로젝트의 구조는 다음과 같습니다.

프로젝트 구조

여기서 BankDto클래스가 결과값을 담는 클래스입니다.

사이트의 예제요청결과(JSON)을 보시면 대충감이 오실거에요.

@Getter
@Setter
public class BankDto {

    private String calTel;
    private String hompUrl;
    private String dclsChrgMan;
    private String korCoNm;
    private String finCoNo;
    private String dclsMonth;
    private Bank.BankType bankType;

    private List<Option> options;

    public Bank toEntity(){
        return Bank.builder()
                .calTel(calTel)
                .hompUrl(hompUrl)
                .dclsChrgMan(dclsChrgMan)
                .korCoNm(korCoNm)
                .finCoNo(finCoNo)
                .dclsMonth(dclsMonth)
                .bankType(bankType)
                .enable(1)
                .build();
    }

    @Getter
    @Setter
    public static class Result {
        private List<DepositOptionDto> optionList;
        private List<Baselist> baseList;
        @JsonProperty("err_msg")
        private String errMsg;
        @JsonProperty("err_cd")
        private String errCd;
        @JsonProperty("now_page_no")
        private String nowPageNo;
        @JsonProperty("max_page_no")
        private String maxPageNo;
        @JsonProperty("total_count")
        private String totalCount;
        @JsonProperty("prdt_div")
        private String prdtDiv;
    }

    @Getter
    @Setter
    public static class Option {
        @JsonProperty("exis_yn")
        private String exisYn;
        @JsonProperty("area_nm")
        private String areaNm;
        @JsonProperty("area_cd")
        private String areaCd;
        @JsonProperty("fin_co_no")
        private String finCoNo;
        @JsonProperty("dcls_month")
        private String dclsMonth;
    }

    @Getter
    @Setter
    public static class Baselist {
        @JsonProperty("cal_tel")
        private String calTel;
        @JsonProperty("homp_url")
        private String hompUrl;
        @JsonProperty("dcls_chrg_man")
        private String dclsChrgMan;
        @JsonProperty("kor_co_nm")
        private String korCoNm;
        @JsonProperty("fin_co_no")
        private String finCoNo;
        @JsonProperty("dcls_month")
        private String dclsMonth;

    }

    @Data
    public static class ResponseBankApi {
        private Result result;


        public boolean requestSuccess(){
            if(result != null && result.getErrCd().equals("000")){
                return true;
            }

            return false;
        }

        public boolean isOverLastPage(){
            if(requestSuccess() && Integer.parseInt(result.getMaxPageNo()) < Integer.parseInt(result.getNowPageNo())){
                return true;
            }

            return false;
        }
    }

}

결과값이 카멜케이스가 아니라 스네이크 케이스이기 때문에 @JsonProperty어노테이션으로 변환하였습니다.

저는 하나하나 하긴했는데 @JsonNaming 어노테이션을 사용하셔도 무방할 것 같습니다.

 

프로젝트는 JPA를 사용하여 DB통신을 할 것이기 때문에 Bank Entity도 생성해봅시다!

 

Response로 받는값을 모두 저장할 필요는 없고 각자 저장해야할 컬럼들만 설정해서 entity를 만들어봅시다!

 

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "tb_bank")
public class Bank extends BaseTimeEntity {

    @RequiredArgsConstructor
    public enum BankType {
        BANK(020000, "은행"),
        SAVING_BANK(030300, "저축은행");

        private final int topFinGrpNo;
        private final String title;
    }

    @Id
    @Column(length = 20)
    private String finCoNo;

    @Column
    private String dclsMonth;
    @Column(length = 100)
    private String korCoNm;
    @Column
    private String dclsChrgMan;
    @Column
    private String hompUrl;
    @Column(length = 50)
    private String calTel;

    @Column
    @Enumerated(EnumType.STRING)
    private BankType bankType;

    @Column
    private int enable;


    @OneToMany(mappedBy = "bank", fetch = FetchType.LAZY)
    @Builder.Default
    private List<Deposit> deposits = new ArrayList<>();

    @OneToMany(mappedBy = "bank", fetch = FetchType.LAZY)
    @Builder.Default
    private List<Saving> savings = new ArrayList<>();

}

여기서 보이는 deposits과 savings 은 각각 적금, 예금 API에서 사용할 Entity를 일대다 형태로 작성하였고,

 

BankType을 Enum형태로 만들어 은행과 저축은행을 구분합니다.

 

그럼 이제 외부 API를 호출하기 위해 webclient를 사용하여 통신을 진행할 메서드를 작성해봅시다.

 

    public BankDto.ResponseBankApi getBankList(int currentPage, String topFinGrpNo) throws Exception {

        return webClient.get()
                    .uri(uriBuilder -> uriBuilder.scheme("https")
                            .host(fssHost)
                            .path(bankPath)
                            .queryParam("auth", authKey)
                            .queryParam("topFinGrpNo", topFinGrpNo)
                            .queryParam("pageNo", currentPage)
                            .build())
                    .retrieve()
                    .onStatus(HttpStatus::isError, clientResponse -> Mono.error(new Exception()))
                    .bodyToMono(BankDto.ResponseBankApi.class)
                    .flux()
                    .toStream()
                    .findFirst().orElse(null);


    }

currentPage와 topFinGrpNo을 인자로 받아 API를 호출하여 BankDto의 ResponseBankApi 객체에 담아주는 메서드입니다.

 

topFinGrpNo => 은행, 저축은행 변수

currentPage => 요청할 Page번호

 

그럼 이제 기본적인 셋팅은 완료되었으니 다음글에서는 스프링배치를 이용하여 은행정보 동기화를 구현해보도록합시다!

 

긴 글 읽어주셔서 감사합니다.

 

 더좋은 의견이나 질문사항이 있으시면 댓글로 남겨주시면 답변드리도록 하겠습니다!


 

해당 API를 이용하여 작성한 ToyProject 사이트

https://cash.sundry.ninja

 

[뜨끔한가계부] 메인

금융/재테크/잡다한것 가득가득한 저장소

cash.sundry.ninja

 

728x90
반응형

댓글