Develop/SpringBoot

[Springboot] Google calendar API 이용해서 공휴일 데이터 받기

chea-young

일정 관리 앱을 만들어주는 프로젝트 진행 중, Google calendar API 연동 및 테스트 담당을 맡게 되었다. Google canledar API를 이용하여 공휴일 데이터를 받는 부분을 정리하고자 한다.


Springboot 버전
- Java 17
- Springboot 3.2.0

 

Gradle 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-web'
 
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
 
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
 
    runtimeOnly 'com.h2database:h2'
 
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
    testImplementation 'org.springframework.security:spring-security-test'
 
    // google api
    implementation 'com.google.apis:google-api-services-calendar:v3-rev20231123-2.0.0'
    implementation 'com.google.api-client:google-api-client:1.31.3'
cs


application.yml

1
2
3
4
5
6
7
8
spring:
  profiles:
    active: local
  api-key:
    google:
      google-calendar-api:
        key: [Your API Key]
 
cs

 

개발 순서

1. Google calendar API key 발급 및 설정 (추후 추가될 예정)
2. 공휴일 데이터 받는 부분 개발

3. 받은 공휴일 데이터의 날짜 정보를 LocalDataTime으로 변경

4. 받은 데이터 controller에 연결 및 테스트

 

개발 폴더 구조

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├── common
│   ├── dto
│   │   └── error
│   │       ├── request
│   │       └── response
│   ├── enumType
│   ├── exception
│   ├── handler
│   └── utils
├── config
└── develop
    ├── controller
    │   └── Google
    └── service
        └── Google
 
cs

 


 

2. 공휴일 데이터 받기

Google에서 제공하는 calendar 패키지를 이용하면 따로 Rest API 요청을 하지 않고서도 API 연동이 가능하다. 공휴일 데이터를 받는 API는 사용자 인증 정보가 선택 사항이기 때문에 구현 코드에서는 사용자 인증 정보를 보내주지 않고 있다.

 - 생성 위치: package com.lchy.develop.develop.service.Google;

- 코드 설명

  - createGoogleCalendarService: 공휴일 데이터를 받기 위해 Calendar API 를 사용하기 위한 Calendar 객체를 생성하는 함수. 함수 내부에서 CalendarRequestInitializer를 이용해 API key를 저장함.

  - findHolidayFromGoogleCalendar: Calendar API를 이용해 공휴일 데이터를 받는 함수. Calendar API의 event의 리스트를 받는 API를 사용. Events 의 형태로 response 를 받음

    - `service.events().list([calendarId]).execute();`: API를 요청하는 부분으로 [calendarId]는 한국의 공휴일 데이터를 요청함으로 구글에서 제공하고 있는 캘린더 Id를 넣어줌. 만약 다른 나라의 공휴일을 가져오고 싶은 경우 링크를 클릭하여 원하는 정보를 넣어주고, 사용자의 캘린더에 등록된 데이터를 받아오고 싶은 경우 사용자의 calendarId를 넣어주면 됨.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.CalendarRequestInitializer;
import com.google.api.services.calendar.model.Events;
import java.io.IOException;
import java.security.GeneralSecurityException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
 
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class GoogleCalendarService {
    @Value("${spring.api-key.google.google-calendar-api.key}")
    private String GOOGLE_CALENDAR_API_KEY;
 
    private final static String GOOGLE_KOREA_HOLIDAY_CALENDAR_ID = "en.south_korea#holiday@group.v.calendar.google.com";
    private final static JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
 
 
    /**
     * Google calendar API를 사용하기 위한 Calendar 서비스를 생성해서 반환하는 함수
     *
     * @return
     * @throws GeneralSecurityException
     * @throws IOException
     */
    private Calendar createGoogleCalendarService() throws GeneralSecurityException, IOException {
        NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
 
        return new Calendar.Builder(httpTransport, JSON_FACTORY, null)
                .setCalendarRequestInitializer(new CalendarRequestInitializer(GOOGLE_CALENDAR_API_KEY))
                .build();
    }
 
    /**
     * 공휴일 데이터를 가져오는 API
     *
     * @return
     * @throws IOException
     * @throws GeneralSecurityException
     */
    public Events findHolidayFromGoogleCalendar() throws IOException, GeneralSecurityException {
        Calendar service = createGoogleCalendarService();
 
        return service.events().list(GOOGLE_KOREA_HOLIDAY_CALENDAR_ID).execute();
    }
}
cs
 

 

 

 


3 EventDateTime을 LocalDateTime으로 변경하기

Google calendar API는 모든 날짜 데이터를 EventDateTime으로 전달해준다. 그래서 DB에 저장하거나 해당 값을 이용하기 위해서 LocalDateTime으로 변경하주는 과정이 필요하였다. 만약, 해당 과정이 필요한 경우에만 사용하면 된다.

 

- 생성 위치: package com.lchy.develop.common.utils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import com.google.api.services.calendar.model.EventDateTime;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
 
@Component
public class DateUtils {
 
    /**
     * EventDateTime 에서 LocalDateTime으로 변환하는 함수
     *
     * @param eventDateTime
     * @return
     */
    public static LocalDateTime convertEventDateTimeToLocalDateTime(EventDateTime eventDateTime) {
        if (eventDateTime.getDateTime() != null) {
            return LocalDateTime.ofInstant(
                    Instant.ofEpochMilli(eventDateTime.getDateTime().getValue()),
                    ZoneId.systemDefault()
            );
        } else if (eventDateTime.getDate() != null) {
            return LocalDateTime.ofInstant(
                    Instant.ofEpochMilli(eventDateTime.getDate().getValue()),
                    ZoneId.systemDefault()
            ).withHour(0).withMinute(0).withSecond(0).withNano(0);
        } else {
            throw new CustomException(HttpStatus.BAD_REQUEST, ErrorCode.INVALID_EVENTDATETIME);
        }
    }
}
cs

3. API test

위에서 작성한 코드에서 API 요청 시, Event 값을 받아오는지 확인한다.

1.Controlle 생성
- 생성 위치: package com.lchy.develop.controller

- 코드 설명

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@RequiredArgsConstructor
@RequestMapping("api/develop/")
public class GoogleCalendarController {
    private final GoogleCalendarService googleCalendarService;
 
    @GetMapping("v1/holiday")
    public ResponseEntity<Object> getHoliday() {
        try {
            Events events = googleCalendarService.findHolidayFromGoogleCalendar();
 
            return new ResponseEntity<>(events, HttpStatus.OK);
        } catch (Exception ex) {
            throw new CustomException(ex);
        }
    }
}
cs
2. Postman 테스트

의도한 Response가 전달된 것을 확인 가능

- 전체 내용: GetHoliday.json

GetHoliday.json
0.00MB

 

3. Response 설명

  • Google calendar API 사용 시, 시간에 대한 데이터는 EventDateTime으로 전달 (EventDateTiem을 LocalDateTime으로 변경하는 DateUtils.convertEventDateTimeToLocalDateTime 함수 추가)
  • Google calendar API - Event list 설명
    • updated: 한국 공휴일 캘린더 수정 최종 수정 시간 (RFC 3339 타임 스탬프)
    • item: 요청한 캘린더의 일정 목록
    • nextSyncToken: 결과가 반환된 이후 변경된 항목만 검색하는 데 나중에 사용되는 토큰
  • Google calendar API - Event list 설명
    • attachments: 일정 첨부파일 파일 리스트 (일정당 최대 25개 첨부파일 포함 가능)
      • fileUrl: 첨부파일 대한 URL 링크
    • attendees: 이벤트 참석자 리스트
    • colorId: 이벤트의 색상
    • created: 이벤트 생성 시간 (RFC 3339 타임 스탬프)
    • creator: 이벤트 만든 사람 정보
    • end: 이벤트 종료 시간
    • location: 지리적 위치
    • summary: 이벤트 제목
    • visibility: 이벤트 공개 상태
    • start: 이벤트 시작 시간


받은 공휴일 데이터를 가지고 각 국가별 공휴일 데이터를 저장하는 Table을 생성하여 일정 주기마다 update가 현재를 넘어갔다면 Table을 업데이트 해주는 기능을 개발하여 프로젝트에서 공휴일 데이터를 관리할 수 있도록 구현할 예정이다.