주식과 지수 정보 조회 기능

728x90
반응형

 

현재 주식 정보와 지수(코스피, 코스닥) 정보 조회 기능을 개발하고 있다. 일단 지수 조회의 경우 얼추 마무리 단계에 있는데 개발하면서 어려웠던 점들을 작성하고자 한다(바이브 코딩을 했는데도 해결이 안되니 참 어려웠다...).

 

초기 버전

 

맨 처음 생각한 방법은 한국투자증권 API를 사용해서 코스피와 코스닥 정보를 조회하는 방법이었다.

 

index-chart-url: "/uapi/domestic-stock/v1/quotations/inquire-index-daily-price"

 

먼저 application.yml에 해당 코드를 추가해준다.

 

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;

@Data
@NoArgsConstructor
public class KisIndexDailyPriceDto {
    @JsonProperty("stck_bsop_date")
    private String date;

    @JsonProperty("bstp_nmix_prpr")
    private String price;

    @JsonProperty("bstp_nmix_oprc")
    private String openPrice;

    @JsonProperty("bstp_nmix_hgpr")
    private String highPrice;

    @JsonProperty("bstp_nmix_lwpr")
    private String lowPrice;
}
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class KisIndexDailyInfoDto {
    @JsonProperty("bstp_nmix_prpr")
    private String currentIndices;

    @JsonProperty("bstp_nmix_prdy_vrss")
    private String priceChange;

    @JsonProperty("bstp_nmix_prdy_ctrt")
    private String changeRate;
}

 

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
@NoArgsConstructor
public class KisIndexChartApiResponse {

    @JsonProperty("rt_cd")
    private String rtCd;

    @JsonProperty("msg_cd")
    private String msgCd;

    @JsonProperty("msg1")
    private String msg1;

    @JsonProperty("output1")
    private KisIndexDailyInfoDto todayInfo;

    @JsonProperty("output2")
    private List<KisIndexDailyPriceDto> dateInfoList;

}

 

주식 조회와 마찬가지로 한국투자증권 API와 매핑할 필드들을 담은 DTO를 생성한다. 

 

    public IndexChartResponseDto getIndexChart(String marketCode, String date, String dateType) {
        // 1. 토큰 처리 (Bearer가 이미 붙어있는지 확인 필요)
        String accessToken = kisAuthService.getAccessToken();
        String authorizationHeader = accessToken.startsWith("Bearer ") ? accessToken : "Bearer " + accessToken;

        String fidInputIscd = switch (marketCode.toUpperCase()) {
            case "KOSPI" -> "0001";
            case "KOSDAQ" -> "1001";
            default -> throw new BusinessException(KisApiErrorCode.INVALID_MARKET_CODE);
        };

        KisIndexChartApiResponse apiResponse = webClient.get()
                .uri(uriBuilder -> uriBuilder
                        .path(kisApiProperties.getIndexChartUrl()) // url 확인: /uapi/domestic-stock/v1/quotations/inquire-index-daily-price
                        .queryParam("FID_PERIOD_DIV_CODE", dateType) // D : 일별, W : 주별, M : 월별
                        .queryParam("FID_COND_MRKT_DIV_CODE", "U")
                        .queryParam("FID_INPUT_ISCD", fidInputIscd) // KOSPI
                        .queryParam("FID_INPUT_DATE_1", date)
                        .build())
                .header("authorization", authorizationHeader) // [수정] 명확한 변수명 사용 및 Bearer 접두사 한번만 붙도록 보장
                .header("appkey", kisApiProperties.getAppkey())
                .header("appsecret", kisApiProperties.getAppsecret())
                .header("tr_id", "FHPUP02120000")
                .header("custtype", "P") // [중요] 고객 타입 추가 (P: 개인)
                .retrieve()
                .bodyToMono(KisIndexChartApiResponse.class)
                .onErrorMap(error -> {
                    // 로깅을 통해 실제 API가 뱉는 에러 메시지를 확인하는 것이 좋습니다.
                    log.error("KIS API Error for {}: {}", marketCode, error.getMessage());
                    return new BusinessException(KisApiErrorCode.INDEX_FETCH_FAILED);
                })
                .block();

        // 응답 검증 강화 (output1이 null이거나 비어있으면 실패로 간주)
        if (apiResponse == null || !"0".equals(apiResponse.getRtCd())) { // Check rtCd for success
            log.error("API Response is valid but list is empty. Msg: {}",
                    apiResponse != null ? apiResponse.getMsg1() : "null response");
            throw new BusinessException(KisApiErrorCode.INDEX_FETCH_FAILED);
        }

        IndexChartInfoDto chartInfoDto = IndexChartInfoDto.builder()
                .marketName(marketCode.toUpperCase())
                .currentIndices(apiResponse.getTodayInfo().getCurrentIndices())
                .priceChange(apiResponse.getTodayInfo().getPriceChange())
                .changeRate(apiResponse.getTodayInfo().getChangeRate())
                .build();

        List<IndexChartPriceDto> chartPriceList = apiResponse.getDateInfoList().stream()
                .map(apiPrice -> IndexChartPriceDto.builder()
                        .date(apiPrice.getDate())
                        .price(apiPrice.getPrice())
                        .openPrice(apiPrice.getOpenPrice())
                        .highPrice(apiPrice.getHighPrice())
                        .lowPrice(apiPrice.getLowPrice())
                        .build())
                .collect(Collectors.toList());

        return IndexChartResponseDto.builder()
                .info(chartInfoDto)
                .priceList(chartPriceList)
                .build();

    }

 

그리고 서비스 단에서 한국투자증권 API를 통해 조회하려는 날짜의 지수와 그 이전 날짜들의 지수 정보들을 조회하도록 만들었었다.

 

과거 데이터 데이터베이스 저장 및 조회

 

그런데 문제가 있었다. 한국투자증권 API의 경우 한 번의 호출에 최대 100건 제한이 존재했던 것이다. 주봉이나 월봉 데이터 100개는 나쁘지 않지만 일봉 100개면 공휴일을 제외해도 4개월 정도의 데이터 밖에 불러올 수 없다는 의미였고, 해결해야할 문제라고 판단했다.

 

나는 과거 데이터를 내 DB에 저장하고 조회하는 방식으로 수정하기로 했다. 호출 제한을 내가 어떻게 할 수 있는 것도 아니고, 많은 데이터를 불러올 경우 외부 API를 호출해서 조회하는 방식은 유저 경험에 좋지 않을 것이라 판단했다.

또한 과거 데이터들은 어짜피 변경되지 않으니 DB에 저장해서 조회하고, 금일 실시간 지수 정보만 한국투자증권 API를 사용해서 호출하고 스케줄러를 사용해 4시 반에 DB에 저장하는 방식을 채택했다.

 

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.time.LocalDate;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "index_daily_data", uniqueConstraints = {
        @UniqueConstraint(columnNames = { "market_name", "date" })
})
public class IndexDailyData {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "market_name", nullable = false, length = 10)
    private String marketName; // KOSPI, KOSDAQ

    @Column(name = "date", nullable = false)
    private LocalDate date;

    @Column(name = "opening_price", nullable = false, precision = 20, scale = 2)
    private BigDecimal openingPrice;

    @Column(name = "closing_price", nullable = false, precision = 20, scale = 2)
    private BigDecimal closingPrice;

    @Column(name = "high_price", nullable = false, precision = 20, scale = 2)
    private BigDecimal highPrice;

    @Column(name = "low_price", nullable = false, precision = 20, scale = 2)
    private BigDecimal lowPrice;

    @Column(name = "price_change", nullable = false, precision = 20, scale = 2)
    private BigDecimal priceChange;

    @Column(name = "change_rate", nullable = false)
    private Double changeRate;

    @Column(name = "volume", precision = 20, scale = 0)
    private BigDecimal volume;

    @Builder
    public IndexDailyData(String marketName, LocalDate date, BigDecimal openingPrice, BigDecimal closingPrice,
            BigDecimal highPrice, BigDecimal lowPrice, BigDecimal priceChange, Double changeRate, BigDecimal volume) {
        this.marketName = marketName;
        this.date = date;
        this.openingPrice = openingPrice;
        this.closingPrice = closingPrice;
        this.highPrice = highPrice;
        this.lowPrice = lowPrice;
        this.priceChange = priceChange;
        this.changeRate = changeRate;
        this.volume = volume;
    }

    public void updateInfo(BigDecimal closingPrice, BigDecimal openingPrice, BigDecimal highPrice, BigDecimal lowPrice,
            BigDecimal priceChange, Double changeRate, BigDecimal volume) {
        this.closingPrice = closingPrice;
        this.openingPrice = openingPrice;
        this.highPrice = highPrice;
        this.lowPrice = lowPrice;
        this.priceChange = priceChange;
        this.changeRate = changeRate;
        this.volume = volume;
    }
}

 

먼저 DB에 저장하기 위해 엔티티를 하나 생성한다. 저장해야 할 필드가 많으므로 @Builder 어노테이션을 사용했다. 많은 사람들이 class위에 @Builder 어노테이션을 사용하는 데, id처럼 내가 직접 설정해서는 안 되는 값도 빌더에 포함될 수 있기 때문에 굉장히 위험할 수 있다고 판단해서 생성자 위에 붙였다.

 

또한 이전 코드들을 보면 대부분의 타입이 String으로 되어있는 것을 볼 수 있는데, 이건 한국투자증권 API에서 String으로 건내주기 때문에 어쩔 수가 없다. 하지만 내 DB에는 해당 값에 맞는 타입을 설정해서 넣어주었다.

 

즉, 한국투자증권 API에 요청을 보내고 받을 때는 String으로 받고, DB에 저장하고 불러올 때에는 BigDecimal, Double, LocalDate로 받게 설정했다.

 

@Transactional
public void fetchAndSaveHistoricalData(String marketCode, String startDateStr) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        LocalDate targetStartDate = LocalDate.parse(startDateStr, formatter);
        LocalDate currentDate = LocalDate.now();

        log.info("Starting historical data fetch for {} from {} to {}", marketCode, currentDate,
                        targetStartDate);

        while (currentDate.isAfter(targetStartDate)) {
                String currentDateStr = currentDate.format(formatter);
                log.info("Fetching data for date: {}", currentDateStr);

                try {
                        // 1. API 호출 및 데이터 가져오기
                        IndexChartResponseDto response = fetchIndexChartFromApi(marketCode, currentDateStr,
                                        "D");

                        if (response.getPriceList() == null || response.getPriceList().isEmpty()) {
                                log.warn("No data returned for date: {}. Stopping.", currentDateStr);
                                break;
                        }

                        // 2. 데이터 저장
                        for (IndexChartPriceDto priceDto : response.getPriceList()) {
                                LocalDate parsedDate = LocalDate.parse(priceDto.getDate(), formatter);

                                // 이미 저장된 데이터가 있으면 건너뛰기 (옵션)
                                if (indexDailyDataRepository.findByMarketNameAndDate(marketCode, parsedDate)
                                                .isPresent()) {
                                        continue;
                                }

                                IndexDailyData entity = IndexDailyData.builder()
                                                .marketName(marketCode)
                                                .date(parsedDate)
                                                .closingPrice(new BigDecimal(priceDto.getPrice()))
                                                .openingPrice(new BigDecimal(priceDto.getOpenPrice()))
                                                .highPrice(new BigDecimal(priceDto.getHighPrice()))
                                                .lowPrice(new BigDecimal(priceDto.getLowPrice()))
                                                .priceChange(BigDecimal.ZERO)
                                                .changeRate(0.0)
                                                .volume(BigDecimal.ZERO)
                                                .build();

                                indexDailyDataRepository.save(entity);
                        }

                        // 3. 다음 호출을 위해 날짜 갱신 (가장 과거 날짜 - 1일)
                        String oldestDateStr = response.getPriceList().get(response.getPriceList().size() - 1)
                                        .getDate();
                        LocalDate oldestDate = LocalDate.parse(oldestDateStr, formatter);

                        if (!oldestDate.isBefore(currentDate)) {
                                // 무한 루프 방지: API가 계속 같은 날짜를 반환하거나 이상할 경우
                                log.warn("Oldest date {} is not before current date {}. Stopping to prevent infinite loop.",
                                                oldestDate, currentDate);
                                break;
                        }

                        currentDate = oldestDate.minusDays(1);

                        // API 호출 제한 고려 (0.1초 대기)
                        Thread.sleep(100);

                } catch (Exception e) {
                        log.error("Error fetching historical data for {}: {}", currentDateStr, e.getMessage());
                        break; // 에러 발생 시 중단
                }
        }
        log.info("Finished historical data fetch for {}", marketCode);
}
@PostMapping("/indices/{marketCode}/init-history")
@Operation(summary = "초기 지수 데이터 구축", description = "현재부터 특정 과거 시점까지의 데이터를 반복적으로 수집하여 DB에 저장합니다.")
public ResponseEntity<SuccessResponse<Void>> initHistoricalData(
        @PathVariable String marketCode,
        @RequestParam String startDate) {
    kisStockService.fetchAndSaveHistoricalData(marketCode, startDate);
    return ResponseEntity.ok(new SuccessResponse<>(true, "초기 데이터 구축 시작", null));
}

 

그리고 초기 지수 데이터를 DB에 저장하는 API를 개발했다. 이 API를 통해 내가 저장하려는 지수와 언제 데이터부터 저장할 것인지를 설정해서 내 DB에 저장하였다.

 

@Transactional
public void saveIndexDailyData(String marketCode, String date, String dateType) {
        IndexChartResponseDto response = fetchIndexChartFromApi(marketCode, date, dateType);

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");

        for (IndexChartPriceDto priceDto : response.getPriceList()) {
                LocalDate parsedDate = LocalDate.parse(priceDto.getDate(), formatter);

                if (indexDailyDataRepository.findByMarketNameAndDate(marketCode, parsedDate).isPresent()) {
                        continue;
                }

                IndexDailyData entity = IndexDailyData.builder()
                                .marketName(marketCode)
                                .date(parsedDate)
                                .closingPrice(new BigDecimal(priceDto.getPrice()))
                                .openingPrice(new BigDecimal(priceDto.getOpenPrice()))
                                .highPrice(new BigDecimal(priceDto.getHighPrice()))
                                .lowPrice(new BigDecimal(priceDto.getLowPrice()))
                                .priceChange(BigDecimal.ZERO) // DTO에 없으면 0 또는 계산
                                .changeRate(0.0) // DTO에 없으면 0 또는 계산
                                .volume(BigDecimal.ZERO) // DTO에 volume이 없다면 0 처리, 있다면 new
                                                         // BigDecimal(priceDto.getVolume())
                                .build();

                indexDailyDataRepository.save(entity);
        }
}
@Slf4j
@Component
@RequiredArgsConstructor
public class StockScheduler {

    private final KisStockService kisStockService;

    // 매일 오후 4시 30분(16:30)에 실행 (장 마감 후)
    @Scheduled(cron = "0 30 16 * * *")
    public void scheduleDailyIndexFetch() {
        log.info("Starting daily index data fetch...");

        String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));

        try {
            // KOSPI
            log.info("Fetching daily KOSPI data for {}", today);
            kisStockService.saveIndexDailyData("KOSPI", today, "D");

            // API 호출 제한 고려 (잠시 대기)
            Thread.sleep(1000);

            // KOSDAQ
            log.info("Fetching daily KOSDAQ data for {}", today);
            kisStockService.saveIndexDailyData("KOSDAQ", today, "D");

            log.info("Daily index data fetch completed successfully.");
        } catch (Exception e) {
            log.error("Failed to fetch daily index data: {}", e.getMessage());
        }
    }
}

 

그리고 스케줄러를 사용해서 4시 반(3시 반에 장 마감이니까 4시 반으로 설정했다)에 금일 날짜의 정보를 내 DB에 저장하도록 만들었다.

 

public IndexChartResponseDto getIndexChart(String marketCode, String startDate, String endDate,
                        String dateType) {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
                LocalDate targetEndDate = LocalDate.parse(endDate, formatter);
                LocalDate targetStartDate = LocalDate.parse(startDate, formatter);

                try {
                        // 1. 실시간 데이터(API) 시도 (endDate 기준 100건)
                        IndexChartResponseDto apiResponse = fetchIndexChartFromApi(marketCode, endDate, dateType);

                        // API 데이터가 없으면 DB만 조회
                        if (apiResponse.getPriceList() == null || apiResponse.getPriceList().isEmpty()) {
                                throw new BusinessException(KisApiErrorCode.INDEX_FETCH_FAILED);
                        }

                        // 2. API 데이터 필터링 (startDate보다 이후인 데이터만 사용)
                        List<IndexChartPriceDto> filteredApiList = apiResponse.getPriceList().stream()
                                        .filter(dto -> {
                                                LocalDate dtoDate = LocalDate.parse(dto.getDate(), formatter);
                                                return !dtoDate.isBefore(targetStartDate)
                                                                && !dtoDate.isAfter(targetEndDate);
                                        })
                                        .toList();

                        // 3. 부족한 과거 데이터 DB 조회
                        List<IndexChartPriceDto> dbPriceList = new ArrayList<>();

                        // API 데이터가 있고, 가장 오래된 날짜가 startDate보다 아직 미래라면 -> 그 사이 공백을 DB로 채움
                        if (!filteredApiList.isEmpty()) {
                                String oldestApiDateStr = filteredApiList.get(filteredApiList.size() - 1).getDate();
                                LocalDate oldestApiDate = LocalDate.parse(oldestApiDateStr, formatter);

                                if (oldestApiDate.isAfter(targetStartDate)) {
                                        List<IndexDailyData> pastDataList = indexDailyDataRepository
                                                        .findAllByMarketNameAndDateBetweenOrderByDateDesc(
                                                                        marketCode, targetStartDate,
                                                                        oldestApiDate.minusDays(1));

                                        dbPriceList = pastDataList.stream()
                                                        .map(this::convertToDto)
                                                        collect(Collectors.toList());
                                }
                        } else {
                                // API 데이터가 startDate 범위에 하나도 없으면 (API가 너무 미래 데이터만 줬거나 등)
                                // DB에서 전체 범위 조회 시도 (API가 준 데이터의 가장 오래된 날짜보다 더 과거를 요청했을 수 있음)
                                // 하지만 fetchIndexChartFromApi는 endDate 기준 100개를 주므로,
                                // endDate가 startDate보다 훨씬 미래라면 API 데이터가 startDate를 커버하지 못할 수 있음.
                                // 이 경우 API 데이터 전체(100개) + 그 이전 DB 데이터를 합쳐야 하는데,
                                // 여기서는 단순하게 "API가 커버하지 못하는 영역"을 DB에서 가져오는 로직으로 처리

                                // API가 반환한 가장 오래된 날짜 확인
                                String apiOldestRaw = apiResponse.getPriceList()
                                                .get(apiResponse.getPriceList().size() - 1).getDate();
                                LocalDate apiOldestDate = LocalDate.parse(apiOldestRaw, formatter);

                                if (apiOldestDate.isAfter(targetStartDate)) {
                                        List<IndexDailyData> pastDataList = indexDailyDataRepository
                                                        .findAllByMarketNameAndDateBetweenOrderByDateDesc(
                                                                        marketCode, targetStartDate,
                                                                        apiOldestDate.minusDays(1));
                                        dbPriceList = pastDataList.stream()
                                                        .map(this::convertToDto)
                                                        collect(Collectors.toList());
                                }
                        }

                        // 4. 데이터 병합
                        List<IndexChartPriceDto> mergedList = new ArrayList<>(filteredApiList);
                        mergedList.addAll(dbPriceList);

                        return IndexChartResponseDto.builder()
                                        .info(apiResponse.getInfo())
                                        .priceList(mergedList)
                                        .build();

                } catch (Exception e) {
                        log.warn("Failed to fetch from API, falling back to DB: {}", e.getMessage());

                        // API 실패 시 DB에서 전체 범위 조회
                        List<IndexDailyData> entityList = indexDailyDataRepository
                                        .findAllByMarketNameAndDateBetweenOrderByDateDesc(marketCode, targetStartDate,
                                                        targetEndDate);

                        if (entityList.isEmpty()) {
                                throw new BusinessException(KisApiErrorCode.INDEX_FETCH_FAILED);
                        }

                        IndexDailyData todayData = entityList.get(0);

                        IndexChartInfoDto chartInfoDto = IndexChartInfoDto.builder()
                                        .marketName(marketCode.toUpperCase())
                                        .currentIndices(todayData.getClosingPrice().toString())
                                        .priceChange(todayData.getPriceChange().toString())
                                        .changeRate(todayData.getChangeRate().toString())
                                        .build();

                        List<IndexChartPriceDto> chartPriceList = entityList.stream()
                                        .map(this::convertToDto)
                                        .collect(Collectors.toList());

                        return IndexChartResponseDto.builder()
                                        .info(chartInfoDto)
                                        .priceList(chartPriceList)
                                        .build();
                }
        }

 

서비스 로직도 수정했다. 로직은 아래와 같다.

 

1. API 호출 : 먼저 한국투자증권 API를 호출하여 실시간 현재가와 최근 100일치 데이터를 가져온다.

2. DB 조회 : API가 준 데이터 중 가장 오래된 날짜보다 더 과거의 데이터를 DB에서 꺼낸다.

3. 병합 : [API 데이터(최근)] + [DB 데이터(과거)] 를 합쳐서 리턴해준다.

 

이 방법은 채택하면 장점이 몇 가지가 있는데, 먼저 API를 먼저 사용하기 때문에 현재가가 즉시 반영된다. 또한 DB의 과거 데이터 덕분에 100일 제한 없이 1년, 10년치 차트도 그릴 수 있게 된다.

 

 

지수 현재가 조회와 과거 데이터 조회 API 분리

 

마지막으로 지수의 현재 데이터와 과거 데이터를 분리하였다.

 

현재는 output1과 output2가 붙어있어 하나의 API로 2개의 정보를 같이 조회하도록 설정되어있는데, 단순히 지수만 확인하고 싶을 때에도 불필요하게 많은 데이터를 불러와야 될 수도 있고, 추후 캐싱을 사용할 때에도 과거 데이터는 길게 캐싱하고, 현재가는 짧게 캐싱하는 전략을 사용할 때에도 용이할 것이라 판단했다.

 

@GetMapping("/indices/{marketCode}/status")
    @Operation(summary = "지수 현재 상태 조회", description = "코스피(kospi) / 코스닥(kosdaq)의 실시간 지수 정보를 조회합니다.")
    public ResponseEntity<SuccessResponse<IndexChartInfoDto>> getIndexStatus(@PathVariable String marketCode) {
        IndexChartInfoDto statusData = kisStockService.getIndexStatus(marketCode);
        return ResponseEntity.ok(new SuccessResponse<>(true, "지수 현재 상태 조회 성공", statusData));
    }

@GetMapping("/indices/{marketCode}/chart")
@Operation(summary = "지수 조회", description = "코스피(kospi) / 코스닥(kosdaq)의 날짜별 정보를 조회합니다.")
public ResponseEntity<SuccessResponse<IndexChartResponseDto>> getIndexChart(
        @PathVariable String marketCode,
        @RequestParam String startDate,
        @RequestParam String endDate,
        @RequestParam(defaultValue = "D") String dateType) {
    IndexChartResponseDto chartData = kisStockService.getIndexChart(marketCode, startDate, endDate, dateType);
    return ResponseEntity.ok(new SuccessResponse<>(true, "기간별 지수 정보 조회 성공", chartData));
}

 

public IndexChartInfoDto getIndexStatus(String marketCode) {
                // 오늘 날짜 기준으로 API 호출하여 최신 상태 정보만 가져옴
                String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
                // API 호출 시 dateType은 'D'로 고정
                IndexChartResponseDto response = fetchIndexChartFromApi(marketCode, today, "D");
                return response.getInfo();
        }

 

 

주식 파트의 경우 상당히 어려워서 바이브 코딩을 많이 사용했다. 하지만 그냥 AI만 돌리고 끝내버리면 정작 내 실력은 늘지 않으니 이렇게 정리의 시간을 가지는 것이 매우 좋다고 생각한다.

 

 

 

 

 

 

728x90
반응형