Google Calendar 연동

Google Calendar + Server to Server 연동 + G Suite

Google Calendar API 활성화

https://console.developers.google.com/ 에 접속해서, ‘Google Calendar API’ 검색 후 사용 설정

https://console.cloud.google.com/apis/credentials/serviceaccountkey 를 통해 서비스 계정 추가(서버 to 서버 연동을 위함)

json 파일을 다운받아 두고, 해당 계정의 고유 ID 를 기억해 둔다.

https://admin.google.com/ 에 접속해서 G Suite 의 관리자 페이지로 접근한다.

보안 – 고급 설정에 진입한다.

도메인 전체 위임 아래의 도메인 전체 위임 관리에 접근한다.

새로 추가를 눌러서

클라이어트 ID 와 범위(https://www.googleapis.com/auth/calendar) 를 입력하고 승인을 누른다.

Node.js 예제는 아래와 같다.

$ npm install googleapis --save
const {google} = require('googleapis');

const key = require('./[YOUR_SERVICE_ACCOUNT_FILE].json');
const SCOPES = 'https://www.googleapis.com/auth/calendar';

const getList = async () => {
	var auth = new google.auth.JWT(
			key.client_email,
			null,
			key.private_key,
			SCOPES,
			'YOUR_PRIMARY_EMAIL_ID'
	);

	const calendar = google.calendar({
		version: 'v3',
		auth
	});
	
	const res = await calendar.events.list({
		calendarId: 'YOUR_CALENDAR_ID',
		timeMin: (new Date()).toISOString(),
		maxResults: 10,
		singleEvents: true,
		orderBy: 'startTime',
	});

	const events = res.data.items;

	if (events.length) {
		events.map((event, i) => {
			const start = event.start.dateTime || event.start.date;
			console.log(`${start} - ${event.summary}`);
		});
	}
}

getList();

YOUR_SERVICE_ACCOUNT_FILE : 위에서 다운 받은 서비스 계정(json 파일)

YOUR_PRIMARY_EMAIL_ID : G Suite 에 속한 계정의 email ID

YOUR_CALENDAR_ID : API로 사용하고자 하는 Calendar ID(캘린더 – 설정 및 공유 – 캘린더 통합 – 캘린더 ID)

로봇청소기와 그리움

설에 가족 모두 울산에 같이 내려갔다가, 설 연휴 마지막 날에 아내와 첫째, 둘째를 놔두고 회사일로 나만 올라오게 되었다. 일주일 동안 회사일도 바쁘고, 감기 기운도 조금 있어서 일주일이 상당히 빠르게 흘렀다. 오늘 올라오고 있다는 전화 통화를 하고, 불현듯 일주일 동안 로봇청소기의 먼지통을 비우지 않았다는 생각이 들었다.
전기밥을 먹어야 하는 곳에서 열심히 충전하고 있는 로봇청소기의 뚜껑을 열고, 먼지통을 꺼내서, 다용도실로 걸어가면서 먼지통의 뚜껑을 열었는데, 먼지가 거의 모이지 않았다는 것을 알게 되었다. 비우려고 걸어가던 발걸음이 허탈하기도 했는데, 가장 먼저 든 생각은…

‘거봐, 우리 딸들이 그동안 먼지를 다 만드셨군!!’

조금 모인 먼지라도 쓰레기봉투에 버리고 청소솔로 내부를 깨끗하게 쓸어내면서, 다시 이런 생각이…

‘그래, 딸들이 독립을 하면, 로봇청소기 비울 일도 잘 없겠군!’

비운 먼지통을 들고 다시 로봇청소기에게 가서 먼지통을 넣으면서는 이런 생각이…

‘그런데, 딸들이 독립을 하고, 집에 아내와 둘 밖에 없으면, 먼지통의 먼지가 참 그립겠구나… ‘

Spring boot JPA

source code 는 아래 github 에서 받을 수 있습니다.
https://github.com/nanbean/springReact 의 spring-jpa branch해당 source code 받지 말고 아래대로 차근 차근 scratch 부터 만들어 보는 것을 추천합니다.

# 해당 source code 를 통해서 동작성을 빠르게 확인하려면,
$ git clone -b spring-jpa https://github.com/nanbean/springReact.git lcms
$ cd lcms
$ mvn spring-boot:run

이후 REST API 동작성 확인 으로 이동하시면 됩니다.

이전 spriing react 최신 기준으로 시작합니다.(Spring boot React)

spring-boot-devtools, h2, JPA, hsqldb, lombok 을 Dependency 에 추가

1. 이전 project 에서 pom.xml 의 dependency 에 spring-boot-devtools, h2, jpa, hsqldb, lombok 을 추가한다. dependencies 하위에 추가합니다.

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>


		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

Content.java 추가

1. src/main/java/com/lge/lcms/content 디렉토리를 만들고 Content.java 파일을 하위에 생성합니다.

title 과 genre 만 가진 간단한 Entity 입니다.

package com.lge.lcms.content;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Column;

@NoArgsConstructor
@Table
@Getter
@Entity
public class Content {

	@Id
	@GeneratedValue
	private Long id;

	@Column
	private String title;

	@Column
	private String genre;

	@Builder
	public Content(String title, String genre) {
		this.title = title;
		this.genre = genre;
	}
}

ContentRepository.java 파일 추가

1. src/main/java/com/lge/lcms/content  하위에 ContentRepository.java 파일을 추가합니다.

package com.lge.lcms.content;

import org.springframework.data.jpa.repository.JpaRepository;

public interface ContentRepository extends JpaRepository<Content, Long> {

}

ContentRepositoryTest.java 파일 추가

1. src/test/java/com/lge/lcms/content  하위에 ContentRepository.java 파일을 추가합니다.

package com.lge.lcms.content;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.Test;
import org.junit.After;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ContentRepositoryTest {

		@Autowired
		ContentRepository contentRepository;

		@After
		public void cleanup() {
			contentRepository.deleteAll();
		}

		@Test
		public void getTest() {
			contentRepository.save(Content.builder()
				.title("Incredible")
				.genre("Action")
				.build());

			List<Content> contentList = contentRepository.findAll();

			Content content = contentList.get(0);
			assertThat(content.getTitle(), is("Incredible"));
			assertThat(content.getGenre(), is("Action"));
		}
}

Test 실행

1. Test 를 통해서 Build 가 정상적으로 되는지 Test Result 가 정상인지 확인 합니다.

$ mvn test
...
...
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 29.654 s
[INFO] Finished at: 2019-01-30T13:36:29+09:00
[INFO] Final Memory: 42M/576M
[INFO] ------------------------------------------------------------------------

Rest API 추가

1. src/main/java/com/lge/lcms/web 디렉토리를 만들고 ContentRestController.java 파일을 하위에 생성합니다.

rest/content REST API 를 만듭니다.

package com.lge.lcms.web;

import com.lge.lcms.dto.ContentSaveRequestDto;
import com.lge.lcms.content.ContentRepository;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@AllArgsConstructor
@RequestMapping("/rest")
public class ContentRestController {

	private ContentRepository contentRepository;

	@PostMapping("/content")
	public void savePosts(@RequestBody ContentSaveRequestDto dto){
		contentRepository.save(dto.toEntity());
	}
}

DTO 추가

1. src/main/java/com/lge/lcms/dto 디렉토리를 만들고 ContentSaveRequestDto.java 파일을 하위에 생성합니다.

View 를 생성합니다. View 와 Entity 는 구분하는 것이 좋습니다.

package com.lge.lcms.dto;

import com.lge.lcms.content.Content;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.Getter;

@Getter
@Setter
@NoArgsConstructor
public class ContentSaveRequestDto {

		private String title;
		private String genre;

		public Content toEntity(){
			return Content.builder()
				.title(title)
				.genre(genre)
				.build();
		}
}

REST API 동작성 확인

1. H2 활성화, src/main/resources/application.properties 에 아래 내용을 추가합니다.

H2 DB 를 웹브라우저를 통해서 접근하기 위해서입니다.

spring:
	h2:
		console:
			enabled: true

2. spring boot 실행

$ mvn spring-boot:run

3. post 수행

$ curl --header "Content-Type: application/json" --request POST --data '{"title": "Incredible","genre": "Action"}' http://localhost:8080/rest/content

4. 입베디드 H2 consle 진입

브라우저 주소창에 http://localhost:8080/h2-console 을 입력합니다.

5. connect

JDBc URL 에 jdbc:h2:mem:testdb 을 입력하고 connect 를 눌러서 진입합니다.

7. query 를 통해서 POST API 동작 확인합니다.

아래 query 를 입력하고 Run 버튼을 누릅니다.

SELECT * FROM CONTENT;