Google Calendar API연동 with fullcalendar - java (1)

기명서·2025년 1월 6일
0

API

목록 보기
2/4
post-thumbnail

저번엔 자바스크립트로 API연동을 해봤으니 이번엔 JAVA로 데이터 불러오는걸 해보자.

구글API에서 프로젝트 생성 및 OAuth설정이 끝났다는 가정하에 진행

나는 maven프로젝트라 API연동을 하려면 구글 API 라이브러리, oauth 인증을 위한 라이브러리, calendar 서비스를 위한 라이브러리를 pom.xml에 추가해준다.

		<dependency>                                             
	        <groupId>com.google.api-client</groupId>             
	        <artifactId>google-api-client</artifactId>           
	        <version>1.22.0</version>                            
	    </dependency>                                            
	    <dependency>                                             
	        <groupId>com.google.oauth-client</groupId>           
	        <artifactId>google-oauth-client-jetty</artifactId>   
	        <version>1.22.0</version>                            
	    </dependency>                                            
	    <dependency>                                             
		    <groupId>com.google.http-client</groupId>            
		    <artifactId>google-http-client-jackson2</artifactId> 
		    <version>1.41.0</version>                            
		</dependency>                                            
		<dependency>                                             
   			<groupId>com.google.apis</groupId>                   
		    <artifactId>google-api-services-calendar</artifactId>
		    <version>v3-rev235-1.22.0</version>                  
		</dependency>                                            

그 다음 구글 Oauth 인증을 통해 Calendar서비스를 생성하는 코드를 짜준다. 나는 GoogleCalendarService라는 파일 만든 후 작업진행.

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.CalendarScopes;
import com.google.api.services.calendar.model.CalendarList;
import com.google.api.services.calendar.model.CalendarListEntry;

public class GoogleCalendarServiceImpl {
	private static final String APPLICATION_NAME = "Google Calendar API Java Quickstart";
	// 데이터 저장 경로 설정
    private static final java.io.File DATA_STORE_DIR = new java.io.File(
            System.getProperty("user.home"),
            ".credentials/calendar-java-quickstart");

    // 데이터 저장소 팩토리
    private static FileDataStoreFactory DATA_STORE_FACTORY;

    // JSON 팩토리
    private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    
    // HTTP 통신을 위한 Transport
    private static HttpTransport HTTP_TRANSPORT;

    // 필요한 Google Calendar API 범위 설정
    private static final List<String> SCOPES = Arrays.asList(CalendarScopes.CALENDAR);
    
    static {
        try {
            // HTTP 전송을 위한 트러스트된 Transport 생성
            HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
            // 파일 저장소 팩토리 설정
            DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
        } catch (Throwable t) {
            t.printStackTrace();
            System.exit(1);
        }
    }
	
    public static Credential authorize() throws IOException, GeneralSecurityException {
    	// client_secret.json 파일을 클래스 경로에서 읽기
        try (InputStream in = GoogleCalendarService.class.getResourceAsStream("/client_secret.json")) {
            if (in == null) {
                throw new FileNotFoundException("Resource '/client_secret.json' not found. Ensure the file exists in the classpath.");
            }

            // GoogleClientSecrets 객체 생성
            GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));

            // GoogleAuthorizationCodeFlow 객체 생성
            GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
                    HTTP_TRANSPORT,
                    JSON_FACTORY,
                    clientSecrets,
                    SCOPES
            )
                    .setDataStoreFactory(DATA_STORE_FACTORY)
                    .setAccessType("offline")
                    .build();
            
            // Credential 객체 생성
            Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
            System.out.println("Credentials saved to " + DATA_STORE_DIR.getAbsolutePath());
            
            return credential;
        } catch (IOException e) {
            // JSON 파일을 읽거나 GoogleClientSecrets 로드 중 문제가 발생했을 때 예외 처리
            System.err.println("Error reading client_secret.json: " + e.getMessage());
            throw e;
        }
    }
    
    /**
     * Google Calendar API 사용을 위한 Service 객체 생성
     */
    public static Calendar getCalendarService() throws IOException, GeneralSecurityException {
        Credential credential = authorize();
        return new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
                .setApplicationName(APPLICATION_NAME)
                .build();
    }
}

위 코드를 간단하게 설명하자면 Google Calendar API 인증 과정에 필요한 코드이며 client_secret.json 파일을 읽고 Credential 객체를 생성하는 역할을 한다. 여기서 중요한 부분은 자바로 구글 API연동을 하게되면 처음 실행 시 웹 페이지가 호출이 되며 승인하라는 안내창이 나오는데 한번 생성이 되면 다음 실행 시에는 바로 API데이터를 불러오는게 가능하다.

참고사진

그다음 실행 코드를 작성한다. 나같은 경우 해당 메뉴이동 시 호출하게끔 만들었다. main메서드 만들어서해도 상관은 없다.

	// 캘린더 목록 출력
for (CalendarListEntry calendar : calendarList.getItems()) {
    System.out.printf("ID: %s | Summary: %s\n", calendar.getId(), calendar.getSummary());
    
    if(calendar.getSummary().equals("test") || calendar.getSummary().equals("연구소")) {
    	Events event2 = service.events().list(calendar.getId())
                .setOrderBy("startTime")          // 시작 시간 기준으로 정렬
                .setSingleEvents(true)           // 반복 이벤트를 개별적으로 표시
                .execute();
    	
    	for (Event event : event2.getItems()) {
            System.out.printf("Event Title: %s\n", event.getSummary());
            
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");

            // 이벤트 생성 시간 (UTC -> KST)
            DateTime createdTime = event.getCreated();
            if (createdTime != null) {
                Instant createdInstant = Instant.ofEpochMilli(createdTime.getValue());
                ZonedDateTime createdZonedTime = ZonedDateTime.ofInstant(
                        createdInstant,
                        ZoneId.of("Asia/Seoul")
                );
                System.out.printf("Created At (KST): %s\n", createdZonedTime.format(formatter));
            }

            // 이벤트 수정 시간 (UTC -> KST)
            DateTime updatedTime = event.getUpdated();
            if (updatedTime != null) {
                Instant updatedInstant = Instant.ofEpochMilli(updatedTime.getValue());
                ZonedDateTime updatedZonedTime = ZonedDateTime.ofInstant(
                        updatedInstant,
                        ZoneId.of("Asia/Seoul")
                );
                System.out.printf("Last Updated At (KST): %s\n", updatedZonedTime.format(formatter));
            }
            
            // 이벤트 시작 시간 (start)
            EventDateTime start = event.getStart();
            if (start.getDateTime() != null) {
                System.out.printf("Start: %s\n", start.getDateTime());
            } else if (start.getDate() != null) {
                System.out.printf("Start (All-day): %s\n", start.getDate());
            }

            // 이벤트 종료 시간 (end)
            EventDateTime end = event.getEnd();
            if (end.getDateTime() != null) {
                System.out.printf("End: %s\n", end.getDateTime());
            } else if (end.getDate() != null) {
                System.out.printf("End (All-day): %s\n", end.getDate());
            }

            System.out.println("-------------------------");
            }
        }
    }

} catch (IOException | GeneralSecurityException e) {
    System.err.println("An error occurred: " + e.getMessage());
    e.printStackTrace();
}

실행을 해보자.

첫 실행 시 이런 에러가 나올 것이다. 이건 따로 설정을 해주어야한다.

테스트 사용자를 넣어줘야 access_denied 에러가 안나올 것이다. 근데 이 에러가 아닌

redirect_uri_mismatch 에러가 나온다면 승인된 리디렉션 URI 설정을 해주어야한다.

나는 로컬에서 개발중이라 저렇게 넣어주었다. 저런 오류까지 잡아준 후 실행시켜보면

이렇게 데이터값을 받아올 수 있다. 이제 이 데이터로 DB에 넣어도 되고 다양하게 사용할 수 있다.

profile
개발 공간(치매 대비 저장소)

0개의 댓글