해외 직구나 해외 결제를 자주 하다 보면 유로 환율이 신경 쓰일 때가 많습니다. 유리한 환율일 때 결제하고 싶지만 실시간으로 보고 있을 수는 없습니다. 그래서 환율이 일정 폭 이상 움직이면 카카오톡으로 알림을 보내주는 시스템을 만들었습니다.
시스템 구성
- 데이터 수집: 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 관리 글에 따로 정리했습니다.