https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=251552545

 

모던 자바스크립트 Deep Dive

자바스크립트를 둘러싼 기본 개념을 정확하고 구체적으로 설명하고, 자바스크립트 코드의 동작 원리를 집요하게 파헤친다. 작성한 코드가 컴퓨터 내부에서 어떻게 동작할 것인지 예측하고, 명

www.aladin.co.kr

모던 자바스크립트 Deep Dive 교재에 나온 내용을 바탕으로 정리하였다.

 

 

1. 브라우저 랜더링 전체 과정

모던 자바스크립트 Deep Dive 자료

1. 브라우저가 HTML, CSS, JavaScript, 이미지 등의 리소스를 서버측에 요청하고 응답받는다.

2. 브라우저 랜더링 엔진에서 HTML과 CSS를 파싱하여 각각 DOM과 CSSOM을 생성한다.

3. 브라우저 자바스크립트 엔진은 Javascript를 파싱하여 AST(Abstract Syntax Tree)를 생성한다.

4. AST는 실행 후 DOM API를 통하여 DOM과 CSSOM을 변경한다.

5. 변경된 DOM과 CSSOM은 다시 Render Tree로 결합된다.

6. Render Tree를 기반으로 브라우저 화면에 페인팅한다.

 

※ 파싱?

프로그래밍 언어에 맞게 작성된 텍스트 문서를 읽어서 트리구조로 만드는 과정이다.

 

 

 

2. 랜더링 엔진이 HTML을 DOM으로 바꾸는 과정

모던 자바스크립트 Deep Dive 자료

1. 브라우저는 서버가 응답한 html 문서를 바이트 형태로 응답 받는다.

2. 응답된 바이트 형태의 html은 meta 태그에 선언된 인코딩 방식(UTF-8)에 따라 문자열로 변환된다.

3. 문자열로 변환된 html은 토큰들로 분해된다. 

4. 각 토큰들을 객체로 변환하여 노드(node)를 생성한다.

5. 노드들은 각 요소간 관계를 반영하여 트리 자료구조로 구성된다. 이 트리 자료구조를 DOM이라 한다. 

 

※토큰?

의미를 가지는 최소 단위

 

※DOM?

Document Object Model의 약자

 

 

3. CSS 파싱과 CSSOM 생성 과정

기본적으로 HTML 파싱과정과 동일하다.

  • 랜더링 엔진이 HTML을 처음부터 한 줄씩 순서대로 파싱하다가 style 태그 또는 link 태그를 만나면 DOM 생성을 일시 중단하고 css 파일을 서버에 요청한다.
  • 요청한 css파일은 바이트 -> 문자열 -> 토큰 -> 노드 -> CSSOM 의 과정을 거친다.
  • css 파싱이 완료되면 html 파싱 중단 지점부터 다시 html 파싱을 시작한다.

 

 

4. 자바스크립트 파싱과 실행

  • DOM은 html요소와 스타일을 변경할 수 있는 DOM API를 제공한다. 이 DOM API는 자바스크립트 실행 후 DOM과 CSSOM을 변경하도록 도와준다.
  • css 파싱과정과 마찬가지로 랜더링 엔진은 html을 한 줄씩 파싱하다가 script 태그를 만나면 DOM 생성을 일시 중단하고 자바스크립트 엔진이 자바스크립트 코드를 파싱하기 시작한다.

모던 자바스크립트 Deep Dive 자료

1. 자바스크립트 코드를 토큰들로 분해한다. (토크나이징 tokenizing)

2. 토큰들을 구분 분석(파싱)하여 AST(추상적 구문 트리)를 생성한다.

3. AST는 바이트코드로 변환되고 인터프레터에 의해 실행된다.

 

5. 랜더트리 생성 및 리플로우, 리랜더링 과정

1. 랜더트리 생성

  • 랜더링 엔진은 DOM과 CSSOM을 결합하여 랜더 트리를 생성한다.
  • 브라우저 화면에 랜더링 되지 않는 노드는 제외된다. (meta 태그, script 태그, css display:none 등)
  • 랜더 트리는 html 요소의 레이아웃(위치&크기) 계산에 사용되고 브라우저 화면에 페인트된다.

2. 리플로우 & 리페인트

  • 자바스크립트 코드에 DOM API가 사용되면 DOM과 CSSOM이 변경된다.
  • 변경된 DOM과 CSSOM은 다시 렌더 트리를 생성하고 레이아웃 계산을 다시 한다. (리플로우)
  • 레이아웃 된 결과를 바탕으로 다시 페인트 한다. (리페인트)
  • 레이아웃에 영향이 없는 변경은 리플로우 없이 리페인트만 실행된다.

 

 

6. Script 태그의 위치

: 브라우저는 위에서 아래방향으로 순차적으로(동기적으로) 파싱하고 실행한다. 따라서 script 태그의 위치는 body 요소의 가장 아래에 위치시키는 것이 좋다. DOM API 사용시 DOM과 CSSOM이 이미 생성되어 있어야 하기 때문이다.

  • DOM이 완성되지 않은 상태에서 DOM API 사용시 작동이 안될 수 있다.
  • body 요소 가장 아래에 자바스크립트를 위치시키면 html 요소들의 랜더링에 지장받는 일이 없기에 페이지 로딩 시간이 단축된다.

 

추가내용)

HTML5부터 script 태그에 async와 defer 어트리뷰트가 추가되었다. async와 defer 어트리뷰트를 사용하면 자바스크립트 파일의 파싱와 로드가 비동기적으로 진행된다.

 

 

이번 글을 각 이벤트에 tooltip 추가하는 방법이다.

원래 목적은 tooltip 추가가 아니라 custom popover를 만드는 거였는데 찾아보던 중 tooltip 자료가 있어 적용해보았다.

물론 우리 프로젝트에는 안쓰일거지만 나중에 필요할 수도 있으니...

 

 

step 1. 우선 tippy.js 사용을 위해 script 추가해준다.

<script src="https://unpkg.com/@popperjs/core@2"></script><!-- tippy 사용 위찬 연결-->
<script src="https://unpkg.com/tippy.js@6"></script><!-- tippy 사용 위찬 연결-->

tippy.js라는 것을 사용할건데 나중에 가져다 쓸거면 다운받아야 할듯.

 

 

step 2. tooltip 코드 추가하기

eventDidMount: function (info) {
  tippy(info.el, {
    content: info.event._def.title,
    placement: "bottom",
    offset: [0, 0],
    interactive: true,
  })
},

사실 찾아보니 fullcalendar v4 이하를 사용한 글들이 많은데..

난 fullcalendar v6를 쓰고 있어서 v4 이하 버전에서만 작동하는 코드들이 많았다. 

 

 

결과

각 이벤트 밑에 mouseover 시 tooltip이 생성되는 것을 볼 수 있다. 

 

이 기능이 좀 좋았던게 more을 클릭해서 나오는 popover의 이벤트에도 알아서 똑같이 적용된다.

사실 이 기능을 위해 html을 고쳐서 popover를 좀 만들어볼까했었는데

content에 html 코드가 안먹는게 문제라...그 위에 따른 custom popover 만들 수가 없었다...ㅎㅎ

 

 

이 건은 다시 찾아보고 연구해봐야지...

fullcalendar 사용중 2가지 또 다른 문제점을 발견했다.

문제점 및 해결방법을 공유한다.

 

[문제점 1] 이전달, 다음달의 날짜도 한 주로 쳐서 보여지는 문제

위 이미지를 보면 5~11일까지는 다음달에 해당하는 날짜라 보여지면 안되는데(?) 보여지고 있다.

기본 셋팅값인것 같다.

 

 

이 문제는 이외로 쉽게 해결했다.

fixedWeekCount: false,

한 줄 추가해주면 해결 완료!

공식 문서에서 찾다가 안보여서 구글링했는데 성공적이었다. 내가 공식 문서 읽기가 아직 잘 안되는건가..?

 

 

[문제점 2] 이전달, 다음달의 날짜가 선택되는 문제

난 이전달의 날짜와 다음달의 날짜는 선택을 막고 싶은데...선택 되는게 문제였다.

그래서 여러가지 찾아보다가 힌트(?)를 얻어서 내가 그냥 직접 짰다.

조금 단순하게 무식한 방법으로 짜긴 했지만 그래도 결과는 나왔으니 만족!

 

step1. selectAllow 사용한다.

selectAllow: function (selectInfo) {
	var click_start = selectInfo.start.getMonth();
	var click_end = selectInfo.end.getMonth();
	if (click_start != month_int) {
	  return false;
	} else if (click_end != month_int && selectInfo.end.getDate() != 1) {
	  return false;
	} else {
	  return true;
	}
},

선택한 날짜의 시작날짜의 달과 끝날짜의 달을 받아와서 현재 달력의 달과 비교!

맞지 않으면 선택이 안되도록 했다.

 

step2. 현재 보여지는 달을 받아온다. (month_int)

calendar.render();
      
// rendering 후 month 불러오기
month_int = calendar.getDate().getMonth();

// 이전달 이동 후 month 불러오기
$('.fc-prev-button').click(function () {
	month_int = calendar.getDate().getMonth();
});

// 다음달 이동 후 month 불러오기
$('.fc-next-button').click(function () {
	month_int = calendar.getDate().getMonth();
});

calendar.rendar() 밑에 코드를 작성해주었다.

랜더링 시 현재 날짜의 달을 가져와주었고,

밑에는 이전달, 다음달로 이동하는 버튼 클릭시 보여지는 화면의 달을 가져오도록 하였다. 

 

 

더 좋은 방법이 있을 것 같기는 한데...

다음에 시간 나면 더 찾아보든지 해야겠다.

이번에는 정말 정말 오전 내내 시간을 보냈던 내용을 정리하려고 한다.

 

문제점

위 그림과 같이 날짜에 1일, 2일, 3일...이렇게 '일'이 붙은걸 볼 수 있다.

원하는 건 다른 달력과 마찬가지로 1, 2, 3...이런 식으로 '일'을 삭제하는 것이었다.

맡은 프로젝트에서 한국어,영어,일본어 버전 3개가 필요했는데...영어는 문제없이 나오지만 한국어와 일본어는 각각 '일'과  '日'이라는 글자가 붙어서 곤란했다. 문제 해결이 간단할거라 생각했는데...오전 내내 이 걸로 씨름했다.

 

 

해결방법

1. 첫번째 시도

 dayCellContent: function(e) {
    e.dayNumberText = e.dayNumberText.replace('日', '');
},

일본어로 검색하니까 해당 코드가 나왔다. 너무 너무 반가웠지만 내 코드에 적용하니까 날짜 자체가 안 나왔다.

다시 구글링 시작...

 

 

 

2. 두번째 시도

dayCellContent: function (info) {
    var number = document.createElement("a");
    number.classList.add("fc-daygrid-day-number");
    number.innerHTML = info.dayNumberText.replace("일", '').replace("日","");
    if (info.view.type === "dayGridMonth") {
      return {
        html: number.outerHTML
      };
    }
    return {
      domNodes: []
    };
},

결과는 너무나 성공적. 원래 찾은 코드는 버튼을 추가하는 코드였는데 버튼을 지우고 위 코드를 조금 활용했다.

어쨌거나 해결해서 너무 좋다!

 

 

해결 완료

사용 배경

회사 업무중 fullcalendar 라이브러리를 사용하게 되었다. 

처음에는 순수 자바스크립트로 구글링해가면서 달력을 구현했었는데 기능들이 자꾸 추가가 되면서 fullcalendar를 사용하게 되었다. 이 것 저 것 여러가지 구현해보면서 알아낸 것을 기록하고자 한다. 

다음에 달력 구현할 일이 있으면 여러므로 편하게 사용할 수 있도록 하는 것이 목표이다.

 

 

참고 사이트

https://fullcalendar.io/

 

FullCalendar - JavaScript Event Calendar

Open Source... With over 10 years of open source and over 120 contributors, FullCalendar will always have a free and open source core. Learn more

fullcalendar.io

 

 

기본 코드

1. fullcalendar 다운받기 (2023.04.06 기준 6.1.5 버전 다운받았음)

script 파일 추가해준다.

<script src='../dist/index.global.js'></script>

실제 사용할 때는 상대경로를 절대경로로 바꿔서 사용했다.

 

 

2. html 코드 추가

<body>
  <div id='calendar'></div>
</body>

html에도 당연히 코드 추가해준다. calendar id 잡아주기

 

 

3. 전체 기본 코드.

밑에서 하나씩 설명 달아야지

document.addEventListener('DOMContentLoaded', function() {
    var calendarEl = document.getElementById('calendar');

    var calendar = new FullCalendar.Calendar(calendarEl, {
      headerToolbar: {
        left: 'prevYear,prev,next,nextYear today',
        center: 'title',
        right: 'dayGridMonth,dayGridWeek,dayGridDay'
      },
      initialDate: '2023-01-12',
      navLinks: true, // can click day/week names to navigate views
      editable: true,
      dayMaxEvents: true, // allow "more" link when too many events
      events: [
        {
          title: 'All Day Event',
          start: '2023-01-01'
        },
        {
          title: 'Long Event',
          start: '2023-01-07',
          end: '2023-01-10'
        },
        {
          groupId: 999,
          title: 'Repeating Event',
          start: '2023-01-09T16:00:00'
        },
        {
          groupId: 999,
          title: 'Repeating Event',
          start: '2023-01-16T16:00:00'
        },
        {
          title: 'Conference',
          start: '2023-01-11',
          end: '2023-01-13'
        },
        {
          title: 'Meeting',
          start: '2023-01-12T10:30:00',
          end: '2023-01-12T12:30:00'
        },
        {
          title: 'Lunch',
          start: '2023-01-12T12:00:00'
        },
        {
          title: 'Meeting',
          start: '2023-01-12T14:30:00'
        },
        {
          title: 'Happy Hour',
          start: '2023-01-12T17:30:00'
        },
        {
          title: 'Dinner',
          start: '2023-01-12T20:00:00'
        },
        {
          title: 'Birthday Party',
          start: '2023-01-13T07:00:00'
        },
        {
          title: 'Click for Google',
          url: 'http://google.com/',
          start: '2023-01-28'
        }
      ]
    });

    calendar.render();
  });

 

 

1) 달력 헤더 부분 수정

headerToolbar: {
    left: 'prevYear,prev,next,nextYear today',
    center: 'title',
    right: 'dayGridMonth,dayGridWeek,dayGridDay'
},

기본 설정이다.

 

난 dayGridMonth만 사용할거라 다 지웠다. 달력 이전과 이후만 남겨두고 다 날림.

headerToolbar: {
    left: 'prev',
    center: 'title',
    right: 'next'
  },

 

 

2) 나머지 기본 설정 설명

initialDate: '2023-04-06', // 달력 처음 로드될 때 표시되는 날짜. default는 현재 날짜
navLinks: false, // 요일이랑 날짜 클릭 시 일이나 주단위 보여주는 화면으로 넘어감
editable: true, // 드래그해서 수정 가능한지. 길게 확장도 가능
dayMaxEvents: true, // +more 표시 전 최대 이벤트 갯수. true는 셀 높이에 의해 결정

 

 

정말 정말 기본적인 것만 설명하였다.

더 알아야 하는 내용은 차근 차근 계속 추가 예정이다.

+ Recent posts