개발 블로그
← 블로그 목록

유로 환율 카카오톡 알림 만들기 — Jsoup + Redis + 카카오 API


해외 직구나 해외 결제를 자주 하다 보면 유로 환율이 신경 쓰일 때가 많습니다. 유리한 환율일 때 결제하고 싶지만 실시간으로 보고 있을 수는 없습니다. 그래서 환율이 일정 폭 이상 움직이면 카카오톡으로 알림을 보내주는 시스템을 만들었습니다.

시스템 구성

  • 데이터 수집: Jsoup으로 네이버 환율 페이지 크롤링 (1분 간격)
  • 상태 저장: Redis에 직전 환율 값 보관
  • 변동 감지: 현재값과 직전값의 차이가 10원 이상이면 알림 발송
  • 알림 발송: 카카오 API로 카카오톡 메시지 전송

Jsoup으로 환율 크롤링

네이버에서 "유로 환율"을 검색한 결과 페이지에 환율 정보가 노출됩니다. Jsoup으로 해당 엘리먼트를 파싱합니다.

private BigDecimal getCurrencyValue(String url) {
    try {
        Document doc = Jsoup.connect(url)
            .userAgent("Mozilla/5.0")
            .get();
        Elements els = doc.select(".price_info .price");
        for (Element el : els) {
            return new BigDecimal(el.text().replace(",", ""));
        }
    } catch (IOException e) {
        log.error("환율 크롤링 실패: {}", e.getMessage());
    }
    return null;
}

Redis로 직전 환율 저장

1분마다 환율을 가져와서 Redis에 저장된 직전값과 비교합니다. 처음 실행 시에는 기준값만 저장하고 알림은 보내지 않습니다.

private Map checkChangeCurrencyValue(BigDecimal current) {
    final String REDIS_KEY = "exchange::euro";
    final BigDecimal THRESHOLD = new BigDecimal("10.00");

    BigDecimal prev = redisService.getCurrencyValue(REDIS_KEY);
    if (prev == null) {
        redisService.setValue(REDIS_KEY, current);
        return Collections.emptyMap();  // 첫 실행, 기준값만 저장
    }

    BigDecimal diff = current.subtract(prev);
    boolean isSignificant =
        (diff.compareTo(BigDecimal.ZERO) > 0 && diff.compareTo(THRESHOLD) >= 0) ||
        (diff.compareTo(BigDecimal.ZERO) < 0 && diff.abs().compareTo(THRESHOLD) >= 0);

    if (isSignificant) {
        redisService.setValue(REDIS_KEY, current);  // 기준값 갱신
        return buildChangeMap(current, prev, diff);
    }
    return Collections.emptyMap();
}
변동이 발생했을 때만 기준값을 갱신합니다. 10원 미만의 작은 변동은 누적되지 않고 무시됩니다. 즉, 1원씩 10번 올라도 알림이 안 가는 게 의도된 동작입니다.

카카오톡 메시지 발송

카카오 디벨로퍼스에서 앱을 등록하고, 카카오 로그인으로 access token을 발급받습니다. 메시지 템플릿을 미리 등록해두고 API로 호출합니다.

public void sendKakaoMsg(Map msgMap, int templateId) {
    String accessToken = redisService.getKakaoKey("kakaotoken::access");

    HttpHeaders headers = new HttpHeaders();
    headers.set("Authorization", "Bearer " + accessToken);
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
    body.add("template_id", String.valueOf(templateId));
    body.add("template_args", JsonUtil.object2String(msgMap));

    restTemplate.postForEntity(
        "https://kapi.kakao.com/v2/api/talk/memo/send",
        new HttpEntity<>(body, headers),
        String.class
    );
}

스케줄러 설정

Spring의 @Scheduled로 1분마다 환율을 체크합니다.

@Scheduled(fixedDelay = 60 * 1000, initialDelay = 1000)
public void checkExchange() throws Exception {
    exchangeService.procExchange();
}

보너스: macOS TTS 연동

알림이 올 때 카카오톡뿐 아니라 맥미니 스피커로 음성 알림도 나오도록 했습니다. macOS의 say 명령어를 활용했는데, 한국 여성 음성(Yuna)으로 "유로 환율 상승 1,400"처럼 읽어줍니다.

String cmd = "say -r 130 -v Yuna 유로 환율 " + increase + " " + value;
Runtime.getRuntime().exec(cmd);

운영 소감

이 시스템을 만든 뒤로 환율 앱을 별도로 켜보는 일이 확연히 줄었습니다. 알림이 오면 그때 확인하면 되니까요. 카카오 access token이 6시간마다 만료되는 게 처음엔 골치였는데, 스케줄러로 자동 갱신하도록 처리했습니다. 관련 내용은 카카오 OAuth 토큰 Redis 관리 글에 따로 정리했습니다.