티스토리 뷰

반응형

최근에 사내 관리자에서 Full Calendar 를 활용하여 데이터를 보여주어야 하는 작업이 별도로 필요했습니다. Semantic UI 를 찾아보던 중 호환성이 좋은것 같아서 적용하게 되었는데, 삽질기를 시작해보려 합니다.

 

1. 레퍼런스가 부족함 (혹은 내가 못찾은 것일수도)

가장 먼저 기능적으로 찾고자 할 때 Github 를 많이 찾아보지 않는 제 기준에서는 스택오버플로우나 구글링을 위주로 진행하고 있어서 달력을 렌더링 하는데 어려움이 있었습니다. 그래서 달력의 타이틀을 지정하거나(아래 사진에서 2022년 12월)이나 옆의 month,week,day 등과 같은 버튼 이벤트를 따로 만들어주는 줄 알았습니다. 

근데 찾아보니 다 방법이 있었습니다. 깃헙 주소는 아래 링크를 참조하는게 맞았고, 그리고 다른 분들이 구현하는 방법을 좀 더 참고해봤습니다. 꾸준히 업데이트하시는지 2022년 10월 기준인 지금은 v5 까지 나와있더군요. 

https://github.com/fullcalendar/fullcalendar

 

GitHub - fullcalendar/fullcalendar: Full-sized drag & drop event calendar

Full-sized drag & drop event calendar. Contribute to fullcalendar/fullcalendar development by creating an account on GitHub.

github.com

 

CDN 으로 추가는 아래와 같이 스크립트를 넣어주면 됩니다.

<!-- Full Calendar -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.1.0/fullcalendar.print.min.css" rel="stylesheet" media="print">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.1.0/fullcalendar.min.css">

<!-- fullcalendar 언어 CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.1.0/fullcalendar.min.js"></script>

 

 

2. 본격 캘린더 그리기 

기본적인 캘린더의 포맷을 그리는건 아래 사이트에서 참조했습니다. codepen에 누가 올려둔 라이브러리가 있어서 구현하기는 편했습니다만 여기 옵션으로는 제가 구현하고자 하는 뷰는 충분하지는 않았습니다 ㅠㅠ

https://codepen.io/nijin39/pen/JbQBXM

 

Semantic UI - Calendar

Semantic UI Example...

codepen.io

 

 

3. 구현 시작 및 자바스크립트 

이제 위 내용에 들어가는 것들을 변환하는 공통 함수를 만들 차례였습니다. 풀 소스는 아래에 크게 남겨두겠지만 세세하게 하나씩 설명하고자 합니다. 

// 날짜에 해당하는 내용의 String Format 으로 리턴해줍니다.
// Usage : new Date().toDateFormat(); // '2022-10-19'
// return String
Date.prototype.toDateFormat = function(){
  return this.toISOString().substring(0, 10);
}

// 날짜 기준으로 10월 19일인 오늘 기준으로 3일을 더할 수 있습니다. 
// Usage : new Date().addDays(3); // '2022-10-22'
// return Date
Date.prototype.addDays = function(days) {
  let date = new Date(this.valueOf());
  date.setDate(date.getDate() + days);
  return date;
}

// 위의 toDateFormat()에서 월까지만 나오게 됩니다. 
// Usage : new Date().toDateFormatUntilMonth();  // '2022-10'
// return String
Date.prototype.toDateFormatUntilMonth = function(){
  return this.toISOString().substring(0, 7);
}

// 한글로 변환해서 월까지 뿌려줍니다. 
// Usage : new Date().toDateFormat().toKoreanDateFormatter(); // '2022년 10월'
// return String
String.prototype.toKoreanDateFormatter = function() {
  return `${this.substring(0, 4)}년 ${this.substring(5, 7)}월`;
}

// YYYY-MM-dd 형식에서 연월일단위로 데이터를 뿌려줍니다.
// Usage : '2022-10-19'.toKoreanDateFormatterYYYYMMDD(); // '2022년 10월 19일'
// return String
String.prototype.toKoreanDateFormatterYYYYMMDD = function() {
  return `${this.substring(0, 4)}년 ${this.substring(5, 7)}월 ${this.substring(8, 10)}일`;
}

// 기간값을 받았을 때 {start: '2022-10-19', end: '2022-10-25'}의 형태로 반환해줍니다.
// Usage: toDateFormatPeriod('Oct 19-25, 2022') 
// return JSON { start: 시작일자, end: 종료일자 }
function toDateFormatPeriod(text) {
  let year = text.split(', ')[1];
  let split = text.split(' ');
  let firstDate = new Date(split[0] + ' ' + split[1] + ' ' + year).addDays(1);
  let secondDate = firstDate.addDays(6);

  return {
    "start" : firstDate.toDateFormat(),
    "end" : secondDate.toDateFormat(),
  }
}

// 문자열과 영문 월 축약형인지 아닌지의 플래그를 받아서 연월일 값으로 반환해줍니다. 
// Usage : toDateFormatMMDDYYYY('Oct 19, 2022', true);
// return String
function toDateFormatMMDDYYYYY(text, short) {
  let split = text.split(' ');
  let month = short ? getMonthFromShortEng(split[0]) : getMonthFromEng(split[0]);
  let dayOfMonth = split[1].replaceAll(',', '');
  let year = split[2];
  let day = ("0" + dayOfMonth).slice(-2);
  return `${year}-${month}-${day}`;
}

// 연도와 영문 월을 받아서 1일 값을 가진 연월일 값으로 반환해줍니다. 
// Usage : toDateFormatter('2022', 'October'); // '2022-10-01'
// return String
function toDateFormatter(year, enMonth) {
  let month = getMonthFromEng(enMonth);
  return year + '-' + month + '-01';
}

// 영문 월 이름을 받아 숫자 월로 반환해줍니다.
// Usage: getMonthFromShortEng('October'); // '10'
// return String
function getMonthFromEng(enMonth) {
  let month = '01';
  if(enMonth == 'January') {
    month = '01';
  } else if(enMonth == 'Febrary') {
    month = '02';
  } else if(enMonth == 'March') {
    month = '03';
  } else if(enMonth == 'April') {
    month = '04';
  } else if(enMonth == 'May') {
    month = '05';
  } else if(enMonth == 'June') {
    month = '06';
  } else if(enMonth == 'July') {
    month = '07';
  } else if(enMonth == 'August') {
    month = '08';
  } else if(enMonth == 'September') {
    month = '09';
  } else if(enMonth == 'October') {
    month = '10';
  } else if(enMonth == 'November') {
    month = '11';
  } else {
    month = '12';
  }
  return month;
}

// 영문 짧은 월 이름을 받아 숫자 월로 반환해줍니다.
// Usage: getMonthFromShortEng('Oct'); // '10'
// return String
function getMonthFromShortEng(enMonth) {
  let month = '01';
  if(enMonth == 'Jan') {
    month = '01';
  } else if(enMonth == 'Feb') {
    month = '02';
  } else if(enMonth == 'Mar') {
    month = '03';
  } else if(enMonth == 'Apr') {
    month = '04';
  } else if(enMonth == 'May') {
    month = '05';
  } else if(enMonth == 'Jun') {
    month = '06';
  } else if(enMonth == 'Jul') {
    month = '07';
  } else if(enMonth == 'Aug') {
    month = '08';
  } else if(enMonth == 'Sep') {
    month = '09';
  } else if(enMonth == 'Oct') {
    month = '10';
  } else if(enMonth == 'Nov') {
    month = '11';
  } else {
    month = '12';
  }
  return month;

}

이렇게 구현을 하고 서버로 파라미터를 넘기는게 더 깔끔하다는 생각에 클라이언트에서 이와 같이 타입을 문자열로 변환하여 적용하는 스크립트를 짜고 있었습니다. 하지만 이런 기능들은 제가 고민하지 않아도 이미 제공이 되고 있었습니다. 옵션을 추가하면 되는 것이었고, 옵션 추가 방법은 생각보다 쉬웠습니다. 

$('#calendar').fullCalendar({
    monthNames: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
    monthNamesShort: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"],
    dayNames: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"],
    dayNamesShort: ["일", "월", "화", "수", "목", "금", "토"],
});

 

4. 버튼 이벤트 붙이기 

이것도 사실 좀 어려웠습니다. 기본적으로 이벤트를 붙이는것이 전역으로 선언을 하게 되어버리면 ajax 호출 후 캘린더를 렌더링 하기 때문에 이벤트가 잘 붙지 않았기에 (쉽게 말해서 콜백 이벤트이기 때문에) 시행착오를 좀 거쳤습니다. 기본적으로 나와 있는 옵션은 구현하기에 부족한 부분이 많았어서 몇개 더 찾아봤습니다. 

$('#calendar').fullCalendar({
    height: 700,
    header: {
        //center: 'prev,title,next', 
        left: null, 
        center: 'title',
        right: null
    },
    defaultDate: 'YYYYMMDD', 
    editable: true,
    eventLimit: true,
    buttonText: {
        today: "오늘",
        month: "월별",
        week: "주별",
        day: "일별",
    }
   
});

 

그리고 아래에 추가되는 버튼 이벤트 클릭은 Ajax 안에 이렇게 넣어줬습니다.

$.ajax({
    type: "GET",
    url: url
})
.done(res => {
	$('#calendar').fullCalendar({
        ...options
    });
    
    $('.fc-next-button').on('click', function() {
        callPrevNextButtonEvent();
      });

      $('.fc-prev-button').on('click', function() {
        callPrevNextButtonEvent();
      });

      $('.fc-month-button').on('click', function() {
        view = 'month';
        viewFlags = [true, false, false];
        callPrevNextButtonEvent();
      });

      $('.fc-basicWeek-button').on('click', function() {
        view = 'week';
        viewFlags = [false, true, false];
        callPrevNextButtonEvent();
      });

      $('.fc-basicDay-button').on('click', function() {
        view = 'day';
        viewFlags = [false, false, true];
        callPrevNextButtonEvent();
      });

      $('.fc-day-number').on('click', function() {
        view = 'day';
        viewFlags = [false, false, true];
        callPrevNextButtonEvent();
      })

      $('.fc-today-button').on('click', function() {
        callPrevNextButtonEvent();
      })
}

 

그리고 위에서 사용하는 callPrevNextButtonEvent 함수는요..

function callPrevNextButtonEvent() {
  let month = viewFlags[0];
  let week = viewFlags[1];
  if(month) {
    getCalendarByMonth();
  } else if(week) {
    getCalendarByWeek()
  } else {
    getCalendarByEachDay()
  }
  onChangeTitle();
}

// 타이틀을 바꿔주는 함수 (내부 함수들은 위에 자바스크립트에서 선언)
function onChangeTitle() {
  let month = viewFlags[0];
  let week = viewFlags[1];
  let title = $('.fc-center h2').text();
  if(month) {
    let val = toDateFormatText(title).toKoreanDateFormatter();
    $('.fc-center h2').text(val);
  } else if(week) {
    let val = toDateFormatPeriod(title);
    let text = val.start.toKoreanDateFormatterYYYYMMDD() + ' - ' + val.end.toKoreanDateFormatterYYYYMMDD();
    $('.fc-center h2').text(text);
  } else {
    let val = toDateFormatMMDDYYYYY(title).toKoreanDateFormatterYYYYMMDD();
    $('.fc-center h2').text(val);
  }
}

이렇게 함수를 지정하고 일일히 다 호출해줬네요. 어떻게 보면 삽질한거죠^^

더 추가되는 내용은 이어서 작성할 예정입니다..!

반응형
댓글
공지사항