๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๋…ธ๋ ฅ์„ ์ด๊ธฐ๋Š” ์žฌ๋Šฅ์€ ์—†๊ณ 
๋…ธ๋ ฅ์„ ์™ธ๋ฉดํ•˜๋Š” ๊ฒฐ๊ณผ๋„ ์—†๋‹ค.
- ์ด์ฐฝํ˜ธ 9๋‹จ

OPEN SOURCE/FullCalendar

[FullCalendar] ํ’€์บ˜๋ฆฐ๋”(FullCalendar) ์†์„ฑ - 6, ์ƒํ˜ธ์ž‘์šฉ (Interaction Options)

  Junesker   2025. 2. 11.
๋ฐ˜์‘ํ˜•

 

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋ฐ˜์˜ ์˜คํ”ˆ์†Œ์Šค FullCalendar

 

 

 

 

 

ํ’€์บ˜๋ฆฐ๋”(FullCalendar) ์†์„ฑ - 6

 

 

 

 

 

FullCalendar JS๋ฅผ ํ™œ์šฉํ•˜๋‹ค ๋ณด๋ฉด ๊ต‰์žฅํžˆ ๋‹ค์–‘ํ•œ ์†์„ฑ๊ณผ ์ด๋ฒคํŠธ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์—ฌ๋Ÿฌ ์†์„ฑ๋“ค์ด ์–ด๋–ค ํŠน์ง•๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€, ํ’€์บ˜๋ฆฐ๋”๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ด๋ฒคํŠธ์—๋Š” ์–ด๋–ค ๊ฒƒ๋“ค์ด ์žˆ์œผ๋ฉฐ ํ•ด๋‹น ์ด๋ฒคํŠธ๋Š” ์–ด๋–ค ๋™์ž‘๋“ค์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š”์ง€ ๋“ฑ๋“ฑ์— ๋Œ€ํ•ด์„œ ์ž˜ ๋ชจ๋ฅผ ๋•Œ๊ฐ€ ๋งŽ๋‹ค. FullCalendar JS ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์—์„œ ์ œ๊ณตํ•˜๋Š” Documents ํŒŒ์ผ์„ ๋ณด๊ธด ํ•˜์ง€๋งŒ Docs ๋ฌธ์„œ๋“ค์„ ํ™•์ธํ•˜๋Š” ๊ฒŒ ์•„์ง ์ต์ˆ™์ง€ ์•Š์€ ์‚ฌ๋žŒ์€ ๋ฌธ์„œ ๋ณด๋Š” ๋ฐฉ๋ฒ•์กฐ์ฐจ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ FullCalendar JS์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์†์„ฑ๋“ค์„ ํ•˜๋‚˜์‹ ์‚ดํŽด๋ณด๊ณ ์ž ํ•œ๋‹ค. ๋ฌผ๋ก  100% ๋ชจ๋“  ์†์„ฑ๋“ค์„ ์ „๋ถ€ ๋ณด๋ฉด ์ข‹๊ฒ ์ง€๋งŒ, ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์†์„ฑ๋“ค ์œ„์ฃผ๋กœ ํ™•์ธํ•ด ๋ณด๊ณ  ๊ทธ ์™ธ์— ์†์„ฑ๋“ค์€ ์ง์ ‘ Documents๋ฅผ ํ™•์ธํ•ด ๋ณด๊ธธ ๋ฐ”๋ž€๋‹ค.

 

 

 

 

 

๐Ÿ“† 6. ์ƒํ˜ธ์ž‘์šฉ (Interaction Options)


 

 

 

 

 

droppable

' ๋‹ค๋ฅธ ๋‹ฌ๋ ฅ์˜ ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅํ•œ ์™ธ๋ถ€ ์š”์†Œ๋‚˜ ์ด๋ฒคํŠธ๋ฅผ ๋‹ฌ๋ ฅ์— ๋†“์„ ์ˆ˜ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€ '


droppable ์†์„ฑ์€ ์™ธ๋ถ€ ์š”์†Œ๋ฅผ Drag & Drop์œผ๋กœ ์บ˜๋ฆฐ๋”์— ๋“œ๋กญํ•  ์ˆ˜ ์žˆ๋„๋ก ํ™œ์„ฑํ™” ํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ์†์„ฑ์„ ํ†ตํ•ด ์™ธ๋ถ€ ์ด๋ฒคํŠธ๋ฅผ ์ผ์ •์œผ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๊ณ  ์ธํ„ฐ๋ž™์…˜์„ ๊ฐ•ํ™”ํ•˜๊ฑฐ๋‚˜ jQuery UI ๋˜๋Š” Draggable ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ false์ž…๋‹ˆ๋‹ค.

 

<div id="external-events">
  <p><strong>์™ธ๋ถ€ ์ผ์ • ๋ชฉ๋ก</strong></p>
  <div class="fc-event" data-title="์ผ์ •1">์ผ์ •1</div>
  <div class="fc-event" data-title="์ผ์ •2">์ผ์ •2</div>
</div>

<div id="calendar"></div>

<script>
document.addEventListener('DOMContentLoaded', function() {
  // ์™ธ๋ถ€ ์š”์†Œ๋ฅผ ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅํ•˜๊ฒŒ ์„ค์ •
  new FullCalendar.Draggable(document.getElementById('external-events'), {
    itemSelector: '.fc-event',  // ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅํ•œ ์š”์†Œ
    eventData: function(eventEl) {
      return {
        title: eventEl.dataset.title  // ๋“œ๋กญ๋  ๋•Œ ์‚ฌ์šฉํ•  ์ œ๋ชฉ
      };
    }
  });

  // ์บ˜๋ฆฐ๋” ์„ค์ •
  var calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
    initialView: 'dayGridMonth',
    droppable: true,     // ๋“œ๋กญ ํ™œ์„ฑํ™”
    editable: true       // ์ผ์ • ์ˆ˜์ • ๊ฐ€๋Šฅ
  });
  calendar.render();
});
</script>

 

 

Draggable ํด๋ž˜์Šค๋Š” FullCalendar์˜ ๋‚ด์žฅ ๊ธฐ๋Šฅ์ด๋ฏ€๋กœ jQuery UI๊ฐ€ ๋ณ„๋„๋กœ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅํ•œ ์š”์†Œ์— CSS๋กœ ์Šคํƒ€์ผ์„ ์ถ”๊ฐ€ํ•˜๋ฉด UI๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

 

 

eventClick

' ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ํด๋ฆญํ•˜๋ฉด ๋ฐœ์ƒ '


eventClick ์†์„ฑ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์บ˜๋ฆฐ๋”์˜ ์ผ์ •(event)์„ ํด๋ฆญํ•  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์ž…๋‹ˆ๋‹ค. ์ผ์ •์„ ํด๋ฆญ ์‹œ ํŒ์—… ํ‘œ์‹œ, ์ƒ์„ธ ์ •๋ณด ๋ณด๊ธฐ, ์ˆ˜์ •, ์‚ญ์ œ ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ณ  event ๊ฐ์ฒด์™€ jsEvent, view ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. 

 

var calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
  initialView: 'dayGridMonth',
  events: [
    { title: '์ผ์ •1', start: '2025-02-15', description: 'ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ์ƒํ™ฉ ๋…ผ์˜' },
    { title: '์ผ์ •2', start: '2025-02-18' }
  ],
  eventClick: function(info) {
    alert('์ผ์ • ์ œ๋ชฉ: ' + info.event.title);  // ์ผ์ • ์ œ๋ชฉ ํ‘œ์‹œ
  }
});
calendar.render();

 

 

 

 

 

 

 

eventDrop

' ๋“œ๋ž˜๊ทธ๊ฐ€ ์ค‘์ง€๋˜๊ณ  ์ด๋ฒคํŠธ๊ฐ€ ๋‹ค๋ฅธ ์š”์ผ/์‹œ๊ฐ„์œผ๋กœ ์ด๋™ํ•  ๋•Œ ๋ฐœ์ƒ '


eventDrop ์†์„ฑ์€ ์ผ์ •์„ ๋“œ๋ž˜๊ทธํ•˜์—ฌ ๋‚ ์งœ๋‚˜ ์‹œ๊ฐ„์„ ๋ณ€๊ฒฝํ•  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์ž…๋‹ˆ๋‹ค. ์ผ์ •์„ ์ƒˆ๋กœ์šด ๋‚ ์งœ ๋˜๋Š” ์‹œ๊ฐ„์œผ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๊ณ  ์ด๋™ ํ›„ ์„œ๋ฒ„์— DB ์—…๋ฐ์ดํŠธ ์š”์ฒญ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. 

 

var calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
  initialView: 'dayGridMonth',
  editable: true,   // ์ผ์ • ๋“œ๋ž˜๊ทธ/์ˆ˜์ • ๊ฐ€๋Šฅ
  events: [
    { title: '์ผ์ •1', start: '2025-02-15' },
    { title: '์ผ์ •2', start: '2025-02-18' }
  ],
  eventDrop: function(info) {
    alert('์ƒˆ๋กœ์šด ๋‚ ์งœ: ' + info.event.start.toISOString());  // ๋ณ€๊ฒฝ๋œ ๋‚ ์งœ ํ‘œ์‹œ
  }
});
calendar.render();

 

 

๋งค๊ฐœ๋ณ€์ˆ˜ ์„ค๋ช…
info.event ๋“œ๋ž˜๊ทธ ํ›„ ๋ณ€๊ฒฝ๋œ ์ผ์ • ๊ฐ์ฒด (title, start, end ๋“ฑ)
info.delta ์ผ์ •์ด ์ด๋™ํ•œ ์‹œ๊ฐ„ ์ฐจ์ด (์˜ˆ: 1์ผ, 2์‹œ๊ฐ„ ๋“ฑ)
info.revert() ์ผ์ • ์ด๋™์„ ์ทจ์†Œํ•˜๊ณ  ์›๋ž˜ ์œ„์น˜๋กœ ๋˜๋Œ๋ฆผ
info.jsEvent JavaScript ์ด๋ฒคํŠธ ๊ฐ์ฒด (๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ๋“ฑ)

 

 

 

 

 

eventResize

' ์ผ์ •์˜ ์‹œ๊ฐ„ ๋ฒ”์œ„๋ฅผ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ์ถ•์†Œํ•  ๋•Œ ๋ฐœ์ƒ '


eventResize ์†์„ฑ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ผ์ •(event)์˜ ์‹œ๊ฐ„ ๋ฒ”์œ„๋ฅผ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ์ถ•์†Œํ•  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์ž…๋‹ˆ๋‹ค. Drag๋กœ ์ผ์ •์˜ ์ข…๋ฃŒ ์‹œ๊ฐ„์„ ๋ณ€๊ฒฝ ํ•  ์ˆ˜ ์žˆ๊ณ  ์ผ์ • ๋ณ€๊ฒฝ ํ›„ DB ์—…๋ฐ์ดํŠธ ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

var calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
  initialView: 'timeGridWeek',  // ์ฃผ๊ฐ„ ๋ทฐ์—์„œ ๋” ์ง๊ด€์ ์œผ๋กœ ํ™•์ธ ๊ฐ€๋Šฅ
  editable: true,               // ์ผ์ • ๋“œ๋ž˜๊ทธ ๋ฐ ๋ฆฌ์‚ฌ์ด์ฆˆ ๊ฐ€๋Šฅ
  events: [
    { title: '์ผ์ •1', start: '2025-02-15T10:00:00', end: '2025-02-15T11:00:00' }
  ],
  eventResize: function(info) {
    alert('์ƒˆ๋กœ์šด ์ข…๋ฃŒ ์‹œ๊ฐ„: ' + info.event.end.toISOString());  // ๋ณ€๊ฒฝ๋œ ์ข…๋ฃŒ ์‹œ๊ฐ„ ํ‘œ์‹œ
  }
});
calendar.render();

 

 

๋งค๊ฐœ๋ณ€์ˆ˜ ์„ค๋ช…
info.event ๋ฆฌ์‚ฌ์ด์ฆˆ ํ›„ ๋ณ€๊ฒฝ๋œ ์ผ์ • ๊ฐ์ฒด (title, start, end ๋“ฑ)
info.delta ์ผ์ •์ด ํ™•์žฅ/์ถ•์†Œ๋œ ์‹œ๊ฐ„ ์ฐจ์ด (Duration ๊ฐ์ฒด)
info.revert() ์ผ์ • ๋ณ€๊ฒฝ ์ทจ์†Œ ๋ฐ ์›๋ž˜ ์ƒํƒœ๋กœ ๋ณต๊ตฌ
info.jsEvent JavaScript ์ด๋ฒคํŠธ ๊ฐ์ฒด (๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ๋“ฑ)

 

 

 

 

 

dateClick

' ์‚ฌ์šฉ์ž๊ฐ€ ๋‚ ์งœ๋‚˜ ์‹œ๊ฐ„์„ ํด๋ฆญํ•˜๋ฉด ๋ฐœ์ƒ '


dateClick ์†์„ฑ์€ ์บ˜๋ฆฐ๋”์˜ ํŠน์ • ๋‚ ์งœ๋‚˜ ์‹œ๊ฐ„์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์ž…๋‹ˆ๋‹ค. ๋‚ ์งœ ๋˜๋Š” ์‹œ๊ฐ„์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ์ฆ‰๊ฐ์ ์ธ ์•ก์…˜์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ์ด๋ฒคํŠธ๋ฅผ ์ด์šฉํ•˜๋ฉด ์ƒˆ๋กœ์šด ์ผ์ • ์ถ”๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

var calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
  initialView: 'dayGridMonth',
  dateClick: function(info) {
    alert('ํด๋ฆญํ•œ ๋‚ ์งœ: ' + info.dateStr);  // ํด๋ฆญํ•œ ๋‚ ์งœ ํ‘œ์‹œ
  }
});
calendar.render();

// ํด๋ฆญํ•œ ๋‚ ์ž์— ์ƒˆ๋กœ์šด ์ผ์ • ์ถ”๊ฐ€ํ•˜๊ธฐ
var calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
  initialView: 'dayGridMonth',
  events: [],  // ์ดˆ๊ธฐ์—๋Š” ์ผ์ • ์—†์Œ
  dateClick: function(info) {
    calendar.addEvent({
      title: '์ƒˆ๋กœ์šด ์ผ์ •',
      start: info.dateStr,
      allDay: true
    });
  }
});
calendar.render();

 

 

๋งค๊ฐœ๋ณ€์ˆ˜ ์„ค๋ช…
info.date ํด๋ฆญํ•œ ๋‚ ์งœ์˜ Date ๊ฐ์ฒด
info.dateStr YYYY-MM-DD ํ˜•์‹์˜ ๋ฌธ์ž์—ด ๋‚ ์งœ
info.allDay ํด๋ฆญํ•œ ๊ณณ์ด ์ข…์ผ(all-day) ์˜์—ญ์ธ์ง€ ์—ฌ๋ถ€
info.jsEvent JavaScript์˜ ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ๊ฐ์ฒด (ํด๋ฆญ ์œ„์น˜ ๋“ฑ)
info.view ํ˜„์žฌ ์บ˜๋ฆฐ๋”์˜ ๋ทฐ ์ •๋ณด (์˜ˆ: dayGridMonth, timeGridWeek)

 

 

 

 

 

@Junesker


 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€