๐Ÿ“Œ FullCalendar + Google Calendar ์—ฐ๋™ ๋”ฐ๋ผํ•˜๊ธฐ ๊ฐ€์ด๋“œ (Spring Boot)

My Pale Blue Dotยท2025๋…„ 5์›” 15์ผ
0

SPRING BOOT

๋ชฉ๋ก ๋ณด๊ธฐ
31/40
post-thumbnail

1๏ธโƒฃ ๋ชฉํ‘œ

  • FullCalendar์—์„œ ๋‚ ์งœ๋ฅผ ํด๋ฆญ โ†’ ๋ชจ๋‹ฌ์ฐฝ ์ž…๋ ฅ โ†’ ์ œ์ถœ โ†’ ๐Ÿ“† Google Calendar์— ์ผ์ • ์ž๋™ ๋“ฑ๋ก

2๏ธโƒฃ GCP ์ค€๋น„: OAuth 2.0 ์„ค์ •

โœ… Google Cloud Console์—์„œ

  1. ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
  2. API ๋ฐ ์„œ๋น„์Šค โ†’ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ โ†’ "Google Calendar API" ํ™œ์„ฑํ™”
  3. ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด > OAuth 2.0 ํด๋ผ์ด์–ธํŠธ ID ์ƒ์„ฑ
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์œ ํ˜•: ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
    • ์Šน์ธ๋œ ๋ฆฌ๋””๋ ‰์…˜ URI ์ถ”๊ฐ€:
      http://localhost:8888/google/cal/callback
      
  4. client_secret.json ๋‹ค์šด๋กœ๋“œ
    • src/main/resources/ ํด๋”์— ์ €์žฅ
    • .gitignore์— ๋“ฑ๋กํ•ด์•ผ ํ•จ

3๏ธโƒฃ HTML ๊ตฌ์„ฑ (FullCalendar + Bootstrap ๋ชจ๋‹ฌ)

โœ… ์บ˜๋ฆฐ๋” ๋ Œ๋”๋ง + ํด๋ฆญ ์‹œ ๋ชจ๋‹ฌ ๋„์šฐ๊ธฐ

<script>
document.addEventListener('DOMContentLoaded', function() {
  var calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
    initialView: 'dayGridMonth',
    googleCalendarApiKey: '๋ฐœ๊ธ‰๋ฐ›์€ API ํ‚ค',
    events: {
      googleCalendarId: '๋‚ด ๊ตฌ๊ธ€ ์บ˜๋ฆฐ๋” ID'
    },
    dateClick: function(info) {
      const form = document.dateForm;
      form.date.value = info.dateStr; // ํด๋ฆญํ•œ ๋‚ ์งœ ์ž๋™ ์ž…๋ ฅ
      document.querySelector('.date-event-modal').click();
    }
  });
  calendar.render();
});
</script>

โœ… ์ž…๋ ฅ ๋ชจ๋‹ฌ ํผ (name="dateForm" ํ•„์ˆ˜)

<form name="dateForm" action="/google/cal/post">
  <input name="date">
  <input name="title">
  <textarea name="desc"></textarea>
  <button>์ผ์ • ์ถ”๊ฐ€</button>
</form>

4๏ธโƒฃ ๋ฐฑ์—”๋“œ: ์ผ์ • ๋“ฑ๋ก ์ปจํŠธ๋กค๋Ÿฌ

@GetMapping("/google/cal/post")
public RedirectView post(@RequestParam String title, @RequestParam String desc, @RequestParam String date) {
    // ๋‚ ์งœ ํŒŒ์‹ฑ ๋ฐ Google Event ๊ฐ์ฒด ์ƒ์„ฑ
    // 9:00 ~ 10:00 ์ผ์ • ๊ณ ์ • + ์•Œ๋ฆผ ์ถ”๊ฐ€
    // GoogleCalendar.addEvent() ํ˜ธ์ถœ
    return new RedirectView("/google/cal");
}

5๏ธโƒฃ GoogleCalendar ํด๋ž˜์Šค ๊ตฌํ˜„

โœ… ์ผ์ • ๋“ฑ๋ก + ์ธ์ฆ ์ฒ˜๋ฆฌ

public static Event addEvent(Event event) {
  Calendar service = new Calendar.Builder(...).build();
  return service.events().insert(CALENDAR_ID, event).execute();
}
private static Credential getCredentials(...) {
  InputStream in = GoogleCalendar.class.getResourceAsStream("/client_secret.json");
  GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(...)
      .setAccessType("offline").build();
  return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}

6๏ธโƒฃ ์ตœ์ดˆ ์‹คํ–‰ ์‹œ ์ธ์ฆ ํ•„์š” (์ฝ˜์†” ์•ˆ๋‚ด)

Please open the following address in your browser:
https://accounts.google.com/o/oauth2/auth...
  1. URL์„ ๋ธŒ๋ผ์šฐ์ €์— ๋ถ™์—ฌ๋„ฃ๊ณ  Google ๊ณ„์ • ๋กœ๊ทธ์ธ
  2. ์ธ์ฆ ํ›„ localhost:8888/google/cal/callback์œผ๋กœ ์ž๋™ ๋ฆฌ๋””๋ ‰์…˜
  3. credentials/StoredCredential์— ์ธ์ฆ ์ •๋ณด ์ €์žฅ๋จ

7๏ธโƒฃ ํ…Œ์ŠคํŠธ ํ๋ฆ„

  1. ์„œ๋ฒ„ ์‹คํ–‰
  2. ๋ธŒ๋ผ์šฐ์ €์—์„œ /google/cal ์ ‘์†
  3. ๋‚ ์งœ ํด๋ฆญ โ†’ ๋ชจ๋‹ฌ์—์„œ ์ œ๋ชฉ/๋‚ด์šฉ ์ž‘์„ฑ โ†’ ์ผ์ • ์ถ”๊ฐ€
  4. Google Calendar์—์„œ ์ผ์ • ๋“ฑ๋ก ํ™•์ธ

๐Ÿ”’ ๋ณด์•ˆ ์ฃผ์˜

  • client_secret.json ์ ˆ๋Œ€ ์™ธ๋ถ€ ๊ณต๊ฐœ ๊ธˆ์ง€!
  • .gitignore์— ๋ฐ˜๋“œ์‹œ ์ถ”๊ฐ€:
src/main/resources/client_secret.json
credentials/

โœ… ๋‚˜์ค‘์— ๋‹ค์‹œ ํ•  ๋•Œ๋„ ๊ธฐ์–ตํ•  ๊ฒƒ!

ํ•ด์•ผ ํ•  ๊ฒƒ์ด์œ 
OAuth ํด๋ผ์ด์–ธํŠธ ID ๋“ฑ๋ก์ธ์ฆ URL ๋ฆฌ๋””๋ ‰์…˜ ๊ฒฝ๋กœ ํ•„์ˆ˜ (/callback)
client_secret.json ์œ„์น˜resources/์— ๋‘๊ณ  .gitignore ๋“ฑ๋ก
์ตœ์ดˆ ์ธ์ฆ์ฝ˜์†”์—์„œ URL ์—ด๊ณ  ํ•œ ๋ฒˆ ๋กœ๊ทธ์ธํ•ด์•ผ ํ† ํฐ ์ €์žฅ๋จ
FullCalendar์™€ ๋ฐฑ์—”๋“œ ์บ˜๋ฆฐ๋” ID ์ผ์น˜ํ”„๋ก ํŠธ์™€ ๋ฐฑ์—”๋“œ๊ฐ€ ๊ฐ™์€ ์บ˜๋ฆฐ๋” ์กฐ์ž‘ํ•ด์•ผ ํ•จ

profile
Here, My Pale Blue.๐ŸŒ

0๊ฐœ์˜ ๋Œ“๊ธ€