1. 계기

뱅크샐러드를 모티브로 시작한 프로젝트라 뱅크샐러드처럼 달력 형태로 daily 지출내역을 관리할 수 있으면 좋을 것 같다고 생각했다. 그래서 일단 달마다 시작일과 요일, 전체 날짜가 다르니 동적으로 만들어야할 것 같은데 잘 몰라서 역시 구글링을 했다. javascript로 동적인 날짜 정보를 가져오고, 그 정보를 우리가 아는 달력의 형태처럼 만들기 위해 css를 사용했다. 

 

* '예산 알림 받기' 버튼은 해당 포스트와는 관련 없는 내용이므로 설명을 하지 않았다.*

 

2. 기본 구조 - HTML

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="layout/default_layout">
<th:block layout:fragment="content">

<div id="top" class="container text-center">
    <div class="row">
        <div class="col-2"><a href="/index">AccountBook</a></div>
        <div class="col-8">My AccountBook</div>
        <div class="col-2">마이 페이지</div>
    </div>
</div>

<div id="middle" class="container text-center" style="padding-top:50px;">
    <div class="row">
        <div class="col align-self-start">
            <div class="row">
                <div class="col-auto"><button class="btn btn-light nav-btn-prev" onclick="goPrev();"><</button></div>
                <div id="year-month" class="col-auto"></div>
                <div class="col-auto"><button class="btn btn-light nav-btn-next" onclick="goNext();">></button></div>
            </div>
        </div>
            <div class="col align-self-end"><button type="button" class="btn btn-success" id="budgetToastBtn" onclick="getAlarm();">예산 알림 받기</button></div>
    </div>
</div>

<div class="calendar">
    <div class="days">
        <div class="day">MON</div>
        <div class="day">TUE</div>
        <div class="day">WED</div>
        <div class="day">THU</div>
        <div class="day">FRI</div>
        <div class="day">SAT</div>
        <div class="day">SUN</div>
    </div>
    <div class="dates"></div>
</div>

</th:block>
</html>

 

2-1. 코드 톺아보기

- 레이아웃 코드

thymeleaf로 레이아웃을 만들어 적용했기에 윗부분에 다음과 같은 코드가 있다. 이는 레이아웃을 적용하지 않았다면 필요없다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="layout/default_layout">
<th:block layout:fragment="content">

 

- 달력 넘기는 코드

<div id="middle" class="container text-center" style="padding-top:50px;">
    <div class="row">
        <div class="col align-self-start">
            <div class="row">
                <div class="col-auto"><button class="btn btn-light nav-btn-prev" onclick="goPrev();"><</button></div>
                <div id="year-month" class="col-auto"></div>
                <div class="col-auto"><button class="btn btn-light nav-btn-next" onclick="goNext();">></button></div>
            </div>
        </div>
            <div class="col align-self-end"><button type="button" class="btn btn-success" id="budgetToastBtn" onclick="getAlarm();">예산 알림 받기</button></div>
    </div>
</div>

왼쪽 상단에 현재 날짜를 보여주고, 이전달과 다음달로 이동할 수 있는 코드이고, goPrev(), goNext()로 동작한다.

 

- 캘린더 코드

<div class="calendar">
    <div class="days">
        <div class="day">MON</div>
        <div class="day">TUE</div>
        <div class="day">WED</div>
        <div class="day">THU</div>
        <div class="day">FRI</div>
        <div class="day">SAT</div>
        <div class="day">SUN</div>
    </div>
    <div class="dates"></div>
</div>

기본 캘린더 틀이고, 요일을 div로 만들고, 각 날짜를 아래 date 클래스의 div 태그에 동적으로 할당할 예정이다.

 

 

3. Javascript로 날짜 정보 동적으로 가져오기

 

3-1. Javascript 전체 코드

<script>
    var currentYear = ''; // 현재 페이지에 있는 연도
    var currentMonth = ''; // 현재 페이지에 있는 월
    var currentDate = ''; // 현재 페이지에 있는 일
    $(document).ready(function() {
        // 한국 표준시간 가져오기
        var date = new Date();
        var utc = date.getTime() + (date.getTimezoneOffset() * 60 * 1000);
        var kstGap = 9 * 60 * 60 * 1000;
        var today = new Date(utc + kstGap);
        var koreaDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());

        // 연, 월, 일 가져오기
        currentYear = koreaDate.getFullYear();
        currentMonth = koreaDate.getMonth() + 1;
        currentDate = koreaDate.getDate();

        // 페이지 로딩 초기에 달력 가져오기
        renderCalendar(currentYear, currentMonth, currentDate);
    });

    function renderCalendar(currentYear, currentMonth, currentDate) {
    	// zeroFormat()은 따로 정의한 함수, 2024-5를 2024-05로 만들어주는 역할
        $('#year-month').text(currentYear + '.' + zeroFormat(currentMonth));

        // 지난달 마지막 날짜, 요일 구하기
        var startDay = new Date(currentYear, currentMonth - 1, 0);
        var prevDate = startDay.getDate();
        var prevDay = startDay.getDay();

        // 이번 달의 마지막 날짜, 요일 구하기
        var endDay = new Date(currentYear, currentMonth, 0);
        var nextDate = endDay.getDate();
        var nextDay = endDay.getDay();

        calendar = document.querySelector('.dates')
        calendar.innerHTML = '';

		// 이번달의 1일 위치를 맞추기 위해 이전달의 날짜들 위치시키기
        for (var i = prevDate - prevDay + 1; i <= prevDate; i++) {
            calendar.innerHTML = calendar.innerHTML + '<div class="day prev disable"></div>'
        }

		// 이번달의 날짜 위치시키기
        for (var i = 1; i <= nextDate; i++) {
            calendar.innerHTML = calendar.innerHTML + '<button type="button" id=' + i + ' class="day current btn-light" data-bs-toggle="modal" data-bs-target="#dateModal" onclick="modal(this.id);">' + i + '</button> ';
        }
    }
    
    // 이전달로 이동
    function goPrev() {
        currentMonth -= 1
        if (currentMonth == 0) {
            currentYear -= 1;
            currentMonth = 12;
        }
        renderCalendar(currentYear, currentMonth, 1);
    }

    // 다음달로 이동
    function goNext() {
        currentMonth += 1
        if (currentMonth == 13) {
            currentYear += 1;
            currentMonth = 1;
        }
        renderCalendar(currentYear, currentMonth, 1);
    }
    
    function zeroFormat(value) {
        if (value / 10 < 1) {
            return '0' + value;
        } else {
            return value;
        }
    }
</script>

 

3-2. 코드 톺아보기 - 한국 표준시간 데이터 가져오기 

    var currentYear = ''; // 현재 페이지에 있는 연도
    var currentMonth = ''; // 현재 페이지에 있는 월
    var currentDate = ''; // 현재 페이지에 있는 일
    $(document).ready(function() {
        // 한국 표준시간 가져오기
        var date = new Date();
        var utc = date.getTime() + (date.getTimezoneOffset() * 60 * 1000);
        var kstGap = 9 * 60 * 60 * 1000;
        var today = new Date(utc + kstGap);
        var koreaDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());

        // 연, 월, 일 가져오기
        currentYear = koreaDate.getFullYear();
        currentMonth = koreaDate.getMonth() + 1;
        currentDate = koreaDate.getDate();

        // 페이지 로딩 초기에 달력 가져오기
        renderCalendar(currentYear, currentMonth, currentDate);
    });
  • currentYear, currentMonth, currentDate는 현재, 이전달, 다음달에도 사용할 것이기 때문에 전역변수로 설정했다.
  • 한국 표준시간의 달력이 필요하므로 한국 시간으로 계산해 Date 객체를 생성한다. 
  • 주의할 점은 month가 0부터 시작하기 때문에 숫자를 맞추기 위해서 month 값에 +1을 꼭 해주어야 한다.

 

필요한 year, month, date를 renderCalendar()에 넘겨 달력을 만든다.

 

3-3. 코드 톺아보기 - 캘린더 로드하기

    function renderCalendar(currentYear, currentMonth, currentDate) {
    	// zeroFormat()은 따로 정의한 함수, 2024-5를 2024-05로 만들어주는 역할
        $('#year-month').text(currentYear + '.' + zeroFormat(currentMonth));

        // 지난달 마지막 날짜, 요일 구하기
        var startDay = new Date(currentYear, currentMonth - 1, 0);
        var prevDate = startDay.getDate();
        var prevDay = startDay.getDay();

        // 이번 달의 마지막 날짜, 요일 구하기
        var endDay = new Date(currentYear, currentMonth, 0);
        var nextDate = endDay.getDate();
        var nextDay = endDay.getDay();

        calendar = document.querySelector('.dates')
        calendar.innerHTML = '';

		// 이번달의 1일 위치를 맞추기 위해 이전달의 날짜들 위치시키기
        for (var i = prevDate - prevDay + 1; i <= prevDate; i++) {
            calendar.innerHTML = calendar.innerHTML + '<div class="day prev disable"></div>'
        }

		// 이번달의 날짜 위치시키기
        for (var i = 1; i <= nextDate; i++) {
            calendar.innerHTML = calendar.innerHTML + '<button type="button" id=' + i + ' class="day current btn-light" data-bs-toggle="modal" data-bs-target="#dateModal" onclick="modal(this.id);">' + i + '</button> ';
        }
    }

이번달의 전체 날짜를 알게 되었으니 <div class="dates">를 가져와서 날짜를 입력하면 된다. 이때 고려해야할 점은 1일의 요일 위치인데, 이전달을 고려하지 않고 이번달 데이터만 html에 추가하면 1일의 요일이 맞지 않게 된다. 

 

따라서 이전달 정보를 가지고 와서 이번달 1일 전에 태그를 추가함으로써 이번달 1일의 요일을 맞출 수 있다.

// 지난달 마지막 날짜, 요일 구하기
var startDay = new Date(currentYear, currentMonth - 1, 0);
var prevDate = startDay.getDate();
var prevDay = startDay.getDay();
        
// 이번달의 1일 위치를 맞추기 위해 이전달의 날짜들 위치시키고 보이지 않게 disable하기
for (var i = prevDate - prevDay + 1; i <= prevDate; i++) {
    calendar.innerHTML = calendar.innerHTML + '<div class="day prev disable"></div>'
}

 

3-4. 이전달, 다음달로 이동하기

    // 이전달로 이동
    function goPrev() {
        currentMonth -= 1
        if (currentMonth == 0) {
            currentYear -= 1;
            currentMonth = 12;
        }
        renderCalendar(currentYear, currentMonth, 1);
    }

    // 다음달로 이동
    function goNext() {
        currentMonth += 1
        if (currentMonth == 13) {
            currentYear += 1;
            currentMonth = 1;
        }
        renderCalendar(currentYear, currentMonth, 1);
    }

이전달과 다음달로 캘린더 이동은 기본적으로 위에서 구현한 renderCalendar()에 필요한 날짜를 넘겨주면 쉽게 구현할 수 있다. 여기서 주의해야할 점은 날짜 계산을 어떻게 할 지인데, 1년은 1월부터 12월까지이고, 1월 이전에는 전년도의 12월이, 12월의 다음달은 다음년도의 1월이기 때문이다. 이는 먼저 월을 계산한 다음에 이전 년도나 다음년도로 넘어간다면 따로 currentYear, currentMonth를 재설정하는 방식으로 해결했다.

 

 

구현 화면

위 코드를 적용하면 날짜 정보는 동적으로 가져오지만, 아래처럼 우리가 아는 달력 형태가 아닌 데이터가 나열된 형태로만 확인할 수 있다.

 

4.  css로 달력 모양 만들기

/resources/static/css/index.css

.calendar {
    padding-top: 60px;
    position: relative;
    margin: 0 auto;
}

.calendar .days {
    display: flex;
    margin-bottom: 20px;
    padding-bottom: 20px;
    border-bottom: 1px solid #ddd;
}

.calendar .day {
    display:flex;
    align-items: center;
    justify-content: center;
    width: calc(100% / 7);
    text-align: left;
    color: #999;
    font-size: 12px;
    text-align: center;
    border-radius:5px
}

.calendar .dates {
    display: flex;
    flex-flow: wrap;
    height: 290px;
}

 

구현 화면

 

위 css 코드를 적용한 다음에 화면이다. 우리가 알고 있는 일반 달력화면이 되었다! 참고한 블로그에서는 월요일부터 시작하는 달력으로 만들었지만 일요일부터 시작하는 달력을 만들고 싶으면 커스터마이징하면 될 것 같다.

 

 

참고

자바스크립트로 달력 만들기

 

+ Recent posts