필요 지식
#javascript #jquery #pignose-calendar #moment

피그노즈 캘린더에는 특정일 비활성화 기능은 매우 유용합니다. 예약 시스템에서 과거일자나 특정일자를 비활성화 시키거나 공휴일을 선택 못하게 하는 기능을 만들수가 있습니다.

그런데 피그노즈 캘린더의 특정일 비활성화는 캘린더를 만드는 시점에 세팅을 해야 합니다.(API상 캘린더 만든 이후에도 가능할것 같은데 적용이 안되네요.) 그리고 캘린더는 보통 화면이 처음 조회될때 만들게 됩니다. 하지만 특정일은 그 이후에 조회될수도 있죠. 이런 상황에서 어떻게 처리할 수 있을지 알아보겠습니다.

상황 정리

아래와 같은 소스가 있습니다.

  1. <script>
  2. //스크립터 소스
  3. (function(){
  4. $('input.calendar').pignoseCalendar({
  5. format: 'YYYY.MM.DD',
  6. theme: 'blue',
  7. lang: 'ko',
  8. disabledDates: [
  9. ],
  10. minDate: moment().add(1, 'd').format("YYYY-MM-DD") //과거 날짜는 예약 못하게
  11. });
  12. })();
  13. //개발자 소스
  14. (function() {
  15. })();
  16. </script>

편의상 하나의 스크립트 영역에 작성했지만 ‘스크립터 소스’ 부분은 실제로는 별도의 파일이나 다른 영역에 있을 것입니다. 저대로 호출을 하면 캘린더 기능은 동작하지만 개발자 소스 부분에서 특정일은 비활성화 되도록 세팅하고 싶은데 캘린더 생성 시점과 맞지 않습니다.(생성 시점에만 특정일을 할 수 있습니다.)

소스 위치 옮기기

스크립터 소스에 있는 캘린더를 개발자 소스 영역으로 옮기는 방법입니다. 캘린더의 적용 유무를 개발자가 하는 것이죠. 이 방법은 설명을 따로 드리지 않아도 이미 그렇게 하셨을것 같으니 넘어가겠습니다.

달력 생성 시점 바꾸기

먼저 개발자쪽 소스에서 아래와 같은 형태로 특정일을 비활성화 하고 싶습니다.

  1. //개발자 소스
  2. (function() {
  3. uijs.Calendar.setHoliday(['2018-10-05', '2019-03-10']);
  4. })();

물론 소스에 있는 uijs.Calendar는 스크립터 소스쪽에 있는 객체일 것입니다. 하지만 지금은 아무런 이름도 없죠. 소스를 고쳐야 합니다.

  1. //스크립터 소스
  2. var uijs = (function(){
  3. $('input.calendar').pignoseCalendar({
  4. format: 'YYYY.MM.DD',
  5. theme: 'blue',
  6. lang: 'ko',
  7. disabledDates: [
  8. ],
  9. minDate: moment().add(1, 'd').format("YYYY-MM-DD") //과거 날짜는 예약 못하게
  10. });
  11. return {
  12. Calendar : {
  13. setHoliday : function(days) {
  14. }
  15. }
  16. };
  17. })();

간단하게 보이기 위해 위와 같이 수정했지만 실제로 만들때는 AMD/CommonJS를 지원하는 UMD 모듈로 만드는 것이 좋습니다. 하지만 여전히 setHoliday() 함수가 호출되기 전에 캘린더가 생성됩니다.

그런데 캘린더가 꼭 화면이 열리면서 바로 만들어져야 할까요? 화면이 열린후에 사용자가 캘린더를 열고 날짜를 선택할지는 알수가 없습니다. 바로 뒤로 갈수도 있고 다른 기능만 클릭할 수도 있죠. 실제로 사용자가 달력을 열려고 할때 만들어도 되지 않을까요?

  1. $('input.calendar').one("click", function() {
  2. var $this = $(this);
  3. $this.pignoseCalendar({
  4. format: 'YYYY.MM.DD',
  5. theme: 'blue',
  6. lang: 'ko',
  7. disabledDates: [
  8. ],
  9. minDate: moment().add(1, 'd').format("YYYY-MM-DD") //과거 날짜는 예약 못하게
  10. });
  11. $this.trigger('click');
  12. });

수정하기 전 소스에서는 바로 캘린더를 만들었지만 지금은 one() 함수를 이용해서 클릭 이벤트만 만들었을 뿐입니다. 달력을 열기 위해 클릭을 하면 캘린더가 만들어지고 trigger로 인해 캘린더가 열릴 것입니다. 캘린더가 만들어지면 기존 클릭 이벤트는 필요가 없기 때문에 one()함수를 사용하였습니다.

이제 캘린더 생성 시점이 개발자 소스의 특정일 호출보다 이후가 되었습니다. 특정일을 담은 변수를 캘린더를 생성하며 사용할 수 있게 되었습니다. 전체 샘플은 맨 밑에 있습니다.

  1. $('input.calendar').one("click", function() {
  2. var $this = $(this);
  3. $this.pignoseCalendar({
  4. format: 'YYYY.MM.DD',
  5. theme: 'blue',
  6. lang: 'ko',
  7. disabledDates: [
  8. ],
  9. minDate: moment().add(1, 'd').format("YYYY-MM-DD") //과거 날짜는 예약 못하게
  10. });
  11. $this.trigger('click');
  12. });

이벤트 재생성

그런데 위의 방식에서도 단점이 있습니다. 캘린더가 만들어진 이후에는 특정일을 변경하지 못한다는 것입니다. 캘린더가 생성된 이후에는 uijs.Calendar.setHoliday() 함수를 아무리 호출해도 캘린더에 반영되지 않습니다. 어떻게 하면 될까요?

  1. $('input.calendar').one("click", createCalendar);
  2. function createCalendar() {
  3. var $this = $(this);
  4. $this.pignoseCalendar({
  5. format: 'YYYY.MM.DD',
  6. theme: 'blue',
  7. lang: 'ko',
  8. disabledDates: holidays,
  9. minDate: moment().add(1, 'd').format("YYYY-MM-DD") //과거 날짜는 예약 못하게
  10. });
  11. $this.trigger('click');
  12. }
  13. return {
  14. Calendar : {
  15. setHoliday : function(days) {
  16. $('input.calendar').unbind('click');
  17. holidays = holidays.concat(days);
  18. $('input.calendar').one("click", createCalendar);
  19. }
  20. }
  21. };

이전 소스와 다른 부분이 많지는 않습니다. $('input.calendar') 에 클릭 이벤트로 있던 콜백함수를 createCalendar로 함수 이름을 분리했을뿐 내용은 완전히 동일합니다. 가장 다른 부분은 setHoliday() 함수에서 클릭 이벤트를 다시 만드는 부분입니다.

  1. setHoliday : function(days) {
  2. $('input.calendar').unbind('click'); //이전 클릭 이벤트 삭제
  3. holidays = holidays.concat(days);
  4. $('input.calendar').one("click", createCalendar); //캘린더 생성 이벤트 추가
  5. }

이전 소스에서는 캘린더가 만들어지면 캘린더를 만드는 클릭이벤트는 없어지기 때문에 캘린더 재생성이 불가능했습니다. 하지만 이번엔 setHoliday() 함수를 호출할 때마다 캘린더를 생성하는 클릭 이벤트를 만들기 때문에 캘린더 재생성이 가능합니다. 몇줄 안 바뀌었지만 원하는 기능이 완성 되었습니다.

정리

제가 지금까지 스크립터들의 코딩을 보면서 느낀점은 실제로 사용하는 시점과 상관없이 객체를 만드는 경우가 많다는 것이었습니다. 물론 화면에 처음부터 보이는 슬라이드 배너 같은 경우라면 처음부터 만들어야 할 것입니다. 하지만 스크롤을 내려야만 볼수 있는 객체, 클릭해야만 볼 수 있는 객체를 필요한 시점이 아닌데 만들어야 할까요?

위의 방식은 분명한 장점이 있습니다.

  1. 달력에서 제공하지 않는 기능을 다른 형태로 구현함
  2. 객체를 처음부터 만들지 않으므로 초기 로딩 속도가 아주 조금이라도 빠름

달력 라이브러리의 기본 기능에서 제공하지 못한것을 사용 시점을 이용하여 구현해보았습니다. 다른 라이브러리 들도 이러한 형태로 사용할 하는게 더 좋을지 생각해 보는 시간을 가지셨으면 합니다.

최종 샘플(달력 생성 시점 바꾸기)

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <link rel="stylesheet" href="pignose.calendar.min.css">
  8. <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  9. <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.min.js"></script>
  10. <script src="pignose.calendar.full.min.js"></script>
  11. <title>Document</title>
  12. </head>
  13. <body>
  14. <div class="wrap">
  15. <input type="text" id="text-calendar1" class="calendar" />
  16. <input type="text" id="text-calendar2" class="calendar" />
  17. <input type="text" id="text-calendar3" class="calendar" />
  18. </div>
  19. <script>
  20. //스크립터 소스
  21. var uijs = (function(){
  22. var holidays = [];
  23. $('input.calendar').one("click", function() {
  24. var $this = $(this);
  25. $this.pignoseCalendar({
  26. format: 'YYYY.MM.DD',
  27. theme: 'blue',
  28. lang: 'ko',
  29. disabledDates: holidays,
  30. minDate: moment().add(1, 'd').format("YYYY-MM-DD") //과거 날짜는 예약 못하게
  31. });
  32. $this.trigger('click');
  33. });
  34. return {
  35. Calendar : {
  36. setHoliday : function(days) {
  37. holidays = holidays.concat(days);
  38. }
  39. }
  40. };
  41. })();
  42. //개발자 소스
  43. (function() {
  44. uijs.Calendar.setHoliday(['2018-10-05', '2019-03-21']);
  45. })();
  46. </script>
  47. </body>
  48. </html>

최종 샘플(이벤트 재생성)

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <link rel="stylesheet" href="pignose.calendar.min.css">
  8. <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  9. <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.min.js"></script>
  10. <script src="pignose.calendar.full.min.js"></script>
  11. <title>Document</title>
  12. </head>
  13. <body>
  14. <div class="wrap">
  15. <input type="text" id="text-calendar1" class="calendar" />
  16. <input type="text" id="text-calendar2" class="calendar" />
  17. <input type="text" id="text-calendar3" class="calendar" />
  18. </div>
  19. <script>
  20. //스크립터 소스
  21. var uijs = (function(){
  22. var holidays = [];
  23. $('input.calendar').one("click", createCalendar);
  24. function createCalendar() {
  25. var $this = $(this);
  26. $this.pignoseCalendar({
  27. format: 'YYYY.MM.DD',
  28. theme: 'blue',
  29. lang: 'ko',
  30. disabledDates: holidays,
  31. minDate: moment().add(1, 'd').format("YYYY-MM-DD") //과거 날짜는 예약 못하게
  32. });
  33. $this.trigger('click');
  34. }
  35. return {
  36. Calendar : {
  37. setHoliday : function(days) {
  38. $('input.calendar').unbind('click');
  39. holidays = holidays.concat(days);
  40. $('input.calendar').one("click", createCalendar);
  41. }
  42. }
  43. };
  44. })();
  45. //개발자 소스
  46. (function() {
  47. uijs.Calendar.setHoliday(['2018-10-05', '2019-03-21']);
  48. })();
  49. </script>
  50. </body>
  51. </html>