이 글은 Today I Learn 시리즈의 17번째 기록입니다. (총 40개)
  • 카테고리를 받아서 _cat에 넣고 post가 해당 categories를 포함하는지 검사한다. 포함되면 graph_posts에 devlog에서 해당되는 posts를 넣고, 포함되는게 없으면 devlog 전체 출력
{% if include.category %} 

  {% assign _cat = include.category %}
  
  {% assign graph_posts = site.devlog | where_exp: "p", "p.categories contains _cat" %}
  
{% else %}

  {% assign graph_posts = site.devlog %}
  
{% endif %}
  • graph의 id 생성. 카테고리를 받아서 graph_id에 넣고 기본값은 “all”로 지정. ex) category가 “ue5”면 id는 “graph-ue5”, 없으면 “graph-all”
{% assign graph_id = include.category | default: "all" %}

<div class="til-graph" id="graph-{{ graph_id }}"></div>
  • graph_posts에서 날짜만 추출해서 JS 배열로 만든다. Jekyll이 빌드 시점에 렌더링 → 결과는 ["2026-03-28", "2026-03-27", ...] 형태
(function () {
  var postDates = [{% for post in graph_posts %}"{{ post.date | date: "%Y-%m-%d" }}"{% unless forloop.last %},{% endunless %}{% endfor %}];
  • 날짜별 포스트 수를 counts 객체에 저장한다. ex) { "2026-03-28": 2, "2026-03-27": 1 }
  var counts = {};
  postDates.forEach(function (d) {
    counts[d] = (counts[d] || 0) + 1;
  });
  • 날짜만 필요하니까 시간을 00:00:00으로 초기화
  var today = new Date();

  today.setHours(0, 0, 0, 0);
  • 그래프 시작일 계산: 오늘이 속한 주의 일요일에서 51주 전 → 총 52주(364일)치 그래프
  var start = new Date(today);
  start.setDate(start.getDate() - start.getDay());
  start.setDate(start.getDate() - 51 * 7);
  • graph_id에 해당하는 div를 가져온다
  var container = document.getElementById('graph-{{ graph_id }}');
  • Date 객체를 “YYYY-MM-DD” 형식의 문자열로 변환. counts의 키와 형식을 맞추기 위해 사용
  function toLocalDateStr(dt) {
    var y = dt.getFullYear();
    var m = String(dt.getMonth() + 1).padStart(2, '0');
    var day = String(dt.getDate()).padStart(2, '0');
    return y + '-' + m + '-' + day;
  }
  • 52열(주) x 7행(요일) 그리드 생성
  for (var w = 0; w < 52; w++) {
    var col = document.createElement('div');
    col.className = 'til-graph-col';
    for (var d = 0; d < 7; d++) {
    
      // 해당 셀의 날짜 계산
      var date = new Date(start);
      date.setDate(start.getDate() + w * 7 + d);
      var dateStr = toLocalDateStr(date);

      // 해당 날짜의 포스트 수 (없으면 0)
      var count = counts[dateStr] || 0;

      // 셀 생성. count에 따라 클래스를 다르게 부여 (0~4) 
      // CSS에서 til-cell--0 ~ til-cell--4 로 색깔 정의
      var cell = document.createElement('div');
      cell.className = 'til-cell til-cell--' + Math.min(count, 4);

      // 마우스 호버 시 날짜와 포스트 수 표시
      cell.title = dateStr + (count > 0 ? ' (' + count + ')' : '');
      col.appendChild(cell);
    }
    container.appendChild(col);
  }


Series: Today I Learn

1 C++ 자료형(Data Type) 2 MD5 vs pHash 3 C++에서 함수의 선언과 정의 4 Tkinter padx, pady 5 메모리와 포인터 변수 6 Call by Value, Call by Reference, Call by Pointer 비교 7 const 8 Gemfile — Jekyll 프로젝트의 의존성 파일 9 kramdown-parser-gfm — Jekyll의 GFM 파서 10 파서(Parser) 11 AHU vs OHU 12 I might try it vs I'll try it 뉘앙스 차이 13 SESSION_EXPIRE_AT_BROWSER_CLOSE=True 14 configuration key 15 Git stash vs discard 16 subprocess.Popen으로 Windows 탐색기에 명령어를 전달 17 Post 잔디 분석하기 읽는 중 18 Google Sheets Sync 최적화 19 DSL (Domain Specific Language)과 GPL (General Purpose Language) 20 마크다운 표 그리는 방법 21 쿼리 파라미터(Query Parameter). 기존 QR코드 재활용 22 Django 보안 취약점 점검 및 수정 23 OOP Object-Oriented Programming 객체 지향 프로그래밍 24 Fernet 대칭 암호화 25 Jekyll 코드블록 안의 Liquid 태그 26 Flutter 앱 개발 — DB부터 상태관리까지 27 insertOnConflictUpdate vs DoUpdate(target) 28 세션 필터 29 아코디언(Accordiaon) UI를 펼친상태로 만들기 30 input의 step 31 Flutter 에서 퍼즐 셀 구현 32 Flutter 앱 개발 TIL 33 Word Cloud 34 Google Sheets를 데이터 버스로(with AppSheet) 35 Django 모델 텍스트 필드 자동 수집 패턴 36 localStorage로 섹션 토글 상태 유지 37 순차 ID 생성(`select_for_update()` + `max()` 조합) 38 역참조 검색과 distinct() 39 Android 파일 공유 MIME 타입 40 AssetManifest — Flutter 빌드 타임 asset 목록 런타임 조회