ํฐ์คํ ๋ฆฌ ๋ทฐ
[JavaScript] ํ์ด๋จธ
๊ฐ๋ฐ๊ฐ๊ตด๐ธ 2022. 8. 13. 23:38ํธ์ถ ์ค์ผ์ค๋ง
ํจ์๋ฅผ ๋ช ์์ ์ผ๋ก ์ฆ์ ํธ์ถํ์ง ์๊ณ ์ผ์ ์๊ฐ์ด ๊ฒฝ๊ณผ๋ ์ดํ์ ํธ์ถ๋๋๋ก ํจ์ ํธ์ถ์ ์์ฝํ๋ ค๋ฉด ํ์ด๋จธ ํจ์๋ฅผ ์ด์ฉํด์ผ ํฉ๋๋ค. ์ด๋ฅผ ํธ์ถ ์ค์ผ์ค๋ง์ด๋ผ ํฉ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ๋ ํ์ด๋จธ๋ฅผ ์์ฑ/์ ๊ฑฐํ ์ ์๋ ํ์ด๋จธ ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด๋ ECMAScrip ์ฌ์์ ์ ์๋ ๋นํธ์ธ ํจ์๊ฐ ์๋๋ฉฐ ๋ธ๋ผ์ฐ์ /Node.js ํ๊ฒฝ์์ ์ ์ญ ๊ฐ์ฒด์ ๋ฉ์๋๋ก์ ์ ๊ณตํ๋ ํธ์คํธ ๊ฐ์ฒด์ ๋๋ค. ํ์ด๋จธ๋ฅผ ์์ฑํ๋ setTimeout๊ณผ setInterval ํ์ด๋จธ ํจ์๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค.
ํ์ด๋จธ ํจ์
setTimeout / clearTimout
setTimeout ํจ์๋ ๋๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ๋ฐ์ ์๊ฐ(ms, 1/1000์ด)์ผ๋ก ๋จ ํ ๋ฒ ๋์ํ๋ ํ์ด๋จธ๋ฅผ ์์ฑํ๊ณ ์ดํ ํ์ด๋จธ๊ฐ ๋ง๋ฃ๋๋ฉด ์ฒซ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ๋ฐ์ ์ฝ๋ฐฑ ํจ์๊ฐ ํธ์ถ๋ฉ๋๋ค. ์ฆ, setTimeout ํจ์์ ์ฝ๋ฐฑ ํจ์๋ ๋ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ๋ฐ์ ์๊ฐ ์ดํ ๋จ ํ ๋ฒ ์คํ๋๋๋ก ํธ์ถ ์ค์ผ์ค๋ง๋ฉ๋๋ค.
const timeoutId = setTimeout(func|code[, delay, param1, param2, ...]);
๋งค๊ฐ๋ณ์ | ์ค๋ช |
func | ํ์ด๋จธ๊ฐ ๋ง๋ฃ๋ ๋ค ํธ์ถ๋ ์ฝ๋ฐฑ ํจ์ |
delay | ํ์ด๋จธ ๋ง๋ฃ ์๊ฐ (์ธ์ ์ ๋ฌ์ ์๋ตํ๋ฉด ๊ธฐ๋ณธ๊ฐ 0์ด ์ง์ ) |
param1, param2, ... |
ํธ์ถ ์ค์ผ์ค๋ง๋ ์ฝ๋ฐฑ ํจ์์ ์ ๋ฌํด์ผ ํ ์ธ์๊ฐ ์กด์ฌํ๋ ๊ฒฝ์ฐ ์ธ ๋ฒ์งธ ์ดํ์ ์ธ์๋ก ์ ๋ฌํ ์ ์์ *IE9 ์ดํ์์๋ ์ฝ๋ฐฑ ํจ์์ ์ธ์๋ฅผ ์ ๋ฌํ ์ ์์ |
// 1์ด(1000ms) ํ ํ์ด๋จธ๊ฐ ๋ง๋ฃ๋๋ฉด ์ฝ๋ฐฑ ํจ์๊ฐ ํธ์ถ๋๋ค.
setTimeout(() => console.log('Hi!'), 1000);
// 1์ด(1000ms) ํ ํ์ด๋จธ๊ฐ ๋ง๋ฃ๋๋ฉด ์ฝ๋ฐฑ ํจ์๊ฐ ํธ์ถ๋๋ค.
// ์ด๋ ์ฝ๋ฐฑ ํจ์์ 'Lee'๊ฐ ์ธ์๋ก ์ ๋ฌ๋๋ค.
setTimeout(name => console.log(`Hi! ${name}.`), 1000, 'Lee');
// ๋ ๋ฒ์งธ ์ธ์(delay)๋ฅผ ์๋ตํ๋ฉด ๊ธฐ๋ณธ๊ฐ 0์ด ์ง์ ๋๋ค.
setTimeout(() => console.log('Hello!'));
setTimeout ํจ์๋ ์์ฑ๋ ํ์ด๋จธ๋ฅผ ์๋ณํ ์ ์๋ ๊ณ ์ ํ ํ์ด๋จธ id๋ฅผ ๋ฐํํ๋ฉฐ ์ด๋ฅผ ํธ์ถ ์ค์ผ์ค๋ง์ ์ทจ์ํ๋ clearTimeout ํจ์์ ์ธ์๋ก ์ ๋ฌํ์ฌ ํ์ด๋จธ๋ฅผ ์ทจ์ํ ์ ์์ต๋๋ค.
// 1์ด(1000ms) ํ ํ์ด๋จธ๊ฐ ๋ง๋ฃ๋๋ฉด ์ฝ๋ฐฑ ํจ์๊ฐ ํธ์ถ๋๋ค.
// setTimeout ํจ์๋ ์์ฑ๋ ํ์ด๋จธ๋ฅผ ์๋ณํ ์ ์๋ ๊ณ ์ ํ ํ์ด๋จธ id๋ฅผ ๋ฐํํ๋ค.
const timerId = setTimeout(() => console.log('Hi!'), 1000);
// setTimeout ํจ์๊ฐ ๋ฐํํ ํ์ด๋จธ id๋ฅผ clearTimeout ํจ์์ ์ธ์๋ก ์ ๋ฌํ์ฌ ํ์ด๋จธ๋ฅผ
// ์ทจ์ํ๋ค. ํ์ด๋จธ๊ฐ ์ทจ์๋๋ฉด setTimeout ํจ์์ ์ฝ๋ฐฑ ํจ์๊ฐ ์คํ๋์ง ์๋๋ค.
clearTimeout(timerId);
*id๋ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์ธ ๊ฒฝ์ฐ ์ซ์, Node.js ํ๊ฒฝ์ธ ๊ฒฝ์ฐ ๊ฐ์ฒด
setInterval / clearInterval
setInterval ํจ์๋ ๋๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ๋ฐ์ ์๊ฐ(ms, 1/1000์ด)์ผ๋ก ๋ฐ๋ณต ๋์ํ๋ ํ์ด๋จธ๋ฅผ ์์ฑํ๊ณ ์ดํ ํ์ด๋จธ๊ฐ ๋ง๋ฃ๋ ๋๋ง๋ค ์ฒซ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ๋ฐ์ ์ฝ๋ฐฑ ํจ์๊ฐ ๋ฐ๋ณต ํธ์ถ๋ฉ๋๋ค. ์ฆ, setInterval ํจ์์ ์ฝ๋ฐฑ ํจ์๋ ๋ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ๋ฐ์ ์๊ฐ์ด ๊ฒฝ๊ณผํ ๋๋ง๋ค ๋ฐ๋ณต ์คํ๋๋๋ก ํธ์ถ ์ค์ผ์ค๋ง๋ฉ๋๋ค.
const timeoutId = setInterval(func|code[, delay, param1, param2, ...]);
setInterval ํจ์์ ์ ๋ฌํ ์ธ์๋ setTimeout๊ณผ ๋์ผํฉ๋๋ค. ๋ํ setInterval ํจ์๋ ์์ฑ๋ ํ์ด๋จธ๋ฅผ ์๋ณํ ์ ์๋ ๊ณ ์ ํ id๋ฅผ ๋ฐํํ๋ฉฐ ์ด๋ฅผ clearInterval ํจ์์ ์ธ์๋ก ์ ๋ฌํ์ฌ ํธ์ถ ์ค์ผ์ค๋ง์ ์ทจ์ํ ์ ์์ต๋๋ค.
let count = 1;
// 1์ด(1000ms) ํ ํ์ด๋จธ๊ฐ ๋ง๋ฃ๋ ๋๋ง๋ค ์ฝ๋ฐฑ ํจ์๊ฐ ํธ์ถ๋๋ค.
// setInterval ํจ์๋ ์์ฑ๋ ํ์ด๋จธ๋ฅผ ์๋ณํ ์ ์๋ ๊ณ ์ ํ ํ์ด๋จธ id๋ฅผ ๋ฐํํ๋ค.
const timeoutId = setInterval(() => {
console.log(count); // 1 2 3 4 5
// count๊ฐ 5์ด๋ฉด setInterval ํจ์๊ฐ ๋ฐํํ ํ์ด๋จธ id๋ฅผ clearInterval ํจ์์
// ์ธ์๋ก ์ ๋ฌํ์ฌ ํ์ด๋จธ๋ฅผ ์ทจ์ํ๋ค. ํ์ด๋จธ๊ฐ ์ทจ์๋๋ฉด setInterval ํจ์์ ์ฝ๋ฐฑ ํจ์๊ฐ
// ์คํ๋์ง ์๋๋ค.
if (count++ === 5) clearInterval(timeoutId);
}, 1000);
๋๋ฐ์ด์ค์ ์ค๋กํ
scroll, resize, input, mousemove ๊ฐ์ด ์งง์ ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ์ฐ์ํด์ ๋ฐ์ํ๋ ์ด๋ฒคํธ์ ๋ฐ์ธ๋ฉํ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ๊ณผ๋ํ๊ฒ ํธ์ถ๋์ด ์ฑ๋ฅ์ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค. ๋๋ฐ์ด์ค์ ์ค๋กํ์ ์ด๋ฌํ ์ด๋ฒคํธ๋ฅผ ๊ทธ๋ฃนํํด์ ๊ณผ๋ํ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ํธ์ถ์ ๋ฐฉ์งํ๋ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ ๋๋ค.
์ด๋ฌํ ๋๋ฐ์ด์ค์ ์คํธ๋กค์ ๊ตฌํ์๋ ํ์ด๋จธ ํจ์๊ฐ ์ฌ์ฉ๋๋ฉฐ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๋ ๋งค์ฐ ์ ์ฉํฉ๋๋ค.
๋๋ฐ์ด์ค(debounce)
๋๋ฐ์ด์ค๋ ์งง์ ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ์ด๋ฒคํธ๊ฐ ์ฐ์ํด์ ๋ฐ์ํ๋ฉด ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ํธ์ถํ์ง ์๋ค๊ฐ ์ผ์ ์๊ฐ์ด ๊ฒฝ๊ณผํ ์ดํ์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ํ ๋ฒ๋ง ํธ์ถ๋๋๋ก ํฉ๋๋ค. ์ฆ, ๋๋ฐ์ด์ค๋ ์ด๋ฒคํธ๋ฅผ ๊ทธ๋ฃนํํด์ ๋ง์ง๋ง์ ํ ๋ฒ๋ง ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ํธ์ถ๋๋๋ก ํฉ๋๋ค.
์๋ฅผ ๋ค์ด ํ ์คํธ ์ ๋ ฅ ํ๋์์ input ์ด๋ฒคํธ๊ฐ ์งง์ ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ์ฐ์ํด์ ๋ฐ์ํ๋ ๊ฒฝ์ฐ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
<!DOCTYPE html>
<html>
<body>
<input type="text">
<div class="msg"></div>
<script>
const $input = document.querySelector('input');
const $msg = document.querySelector('.msg');
const debounce = (callback, delay) => {
let timerId;
// debounce ํจ์๋ timerId๋ฅผ ๊ธฐ์ตํ๋ ํด๋ก์ ๋ฅผ ๋ฐํํ๋ค.
return event => {
// delay๊ฐ ๊ฒฝ๊ณผํ๊ธฐ ์ด์ ์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ์ด์ ํ์ด๋จธ๋ฅผ ์ทจ์ํ๊ณ
// ์๋ก์ด ํ์ด๋จธ๋ฅผ ์ฌ์ค์ ํ๋ค.
// ๋ฐ๋ผ์ delay๋ณด๋ค ์งง์ ๊ฐ๊ฒฉ์ผ๋ก ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด callback์ ํธ์ถ๋์ง ์๋๋ค.
if (timerId) clearTimeout(timerId);
timerId = setTimeout(callback, delay, event);
};
};
// debounce ํจ์๊ฐ ๋ฐํํ๋ ํด๋ก์ ๊ฐ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ๋ฑ๋ก๋๋ค.
// 300ms๋ณด๋ค ์งง์ ๊ฐ๊ฒฉ์ผ๋ก input ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด debounce ํจ์์ ์ฝ๋ฐฑ ํจ์๋
// ํธ์ถ๋์ง ์๋ค๊ฐ 300ms ๋์ input ์ด๋ฒคํธ๊ฐ ๋ ์ด์ ๋ฐ์ํ๋ฉด ํ ๋ฒ๋ง ํธ์ถ๋๋ค.
$input.oninput = debounce(e => {
$msg.textContent = e.target.value;
}, 300);
</script>
</body>
</html>
- ์ฌ์ฉ์๊ฐ ์ผ์ ์๊ฐ ๋์ ํ ์คํธ ์ ๋ ฅ ํ๋์ ๊ฐ์ ์ ๋ ฅํ์ง ์์ผ๋ฉด ์ ๋ ฅ์ด ์๋ฃ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ
- debounce์ delay ์๊ฐ ๋์ input ์ด๋ฒคํธ๊ฐ ๋ ์ด์ ๋ฐ์ํ์ง ์์ผ๋ฉด ํ ๋ฒ๋ง ํธ์ถ
์ด์ฒ๋ผ ๋๋ฐ์ด์ค๋ resize ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ input ์์์ ์ ๋ ฅ๋ ๊ฐ์ผ๋ก ajax ์์ฒญํ๋ ์ ๋ ฅ ํ๋ ์๋์์ UI ๊ตฌํ, ๋ฒํผ ์ค๋ณต ํด๋ฆญ ๋ฐฉ์ง ์ฒ๋ฆฌ ๋์ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋ฉ๋๋ค. Ajax ์์ฒญ๊ณผ ๊ฐ์ ๋ฌด๊ฑฐ์ด ์ฒ๋ฆฌ๋ฅผ ์ํํ ๋๋ ์๋ฒ์ ๋ถ๋ด์ ์ฃผ์ง ์๊ธฐ ์ํด ์ ๋ ฅ์ ์๋ฃํ์ ๋ ํ ๋ฒ๋ง Ajax ์์ฒญ์ ์ ์กํ๋ ๊ฒ์ด ๋ฐ๋์งํฉ๋๋ค. (์ค๋ฌด์์๋ Underscore์ debounce ํจ์๋ Lodash์ debounce ํจ์๋ฅผ ์ฃผ๋ก ์ฌ์ฉ)
์ค๋กํ(throttle)
์ค๋กํ์ ์งง์ ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ์ด๋ฒคํธ๊ฐ ์ฐ์ํด์ ๋ฐ์ํ๋๋ผ๋ ์ผ์ ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ์ต๋ ํ ๋ฒ๋ง ํธ์ถ๋๋๋ก ํฉ๋๋ค. ์ฆ, ์ค๋กํ์ ์ด๋ฒคํธ๋ฅผ ๊ทธ๋ฃนํํด์ ์ผ์ ์๊ฐ ๋จ์๋ก ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ํธ์ถ๋๋๋ก ํธ์ถ ์ฃผ๊ธฐ๋ฅผ ๋ง๋ญ๋๋ค.
์๋ฅผ ๋ค์ด scroll ์ด๋ฒคํธ๊ฐ ์งง์ ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ์ฐ์ํด์ ๋ฐ์ํ๋ ๊ฒฝ์ฐ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
<!DOCTYPE html>
<html>
<head>
<style>
.container {
width: 300px;
height: 300px;
background-color: rebeccapurple;
overflow: scroll;
}
.content {
width: 300px;
height: 1000vh;
}
</style>
</head>
<body>
<div class="container">
<div class="content"></div>
</div>
<div>
์ผ๋ฐ ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ scroll ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ํ์:
<span class="normal-count">0</span>
</div>
<div>
์ค๋กํ ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ scroll ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ํ์:
<span class="throttle-count">0</span>
</div>
<script>
const $container = document.querySelector('.container');
const $normalCount = document.querySelector('.normal-count');
const $throttleCount = document.querySelector('.throttle-count');
const throttle = (callback, delay) => {
let timerId;
// throttle ํจ์๋ timerId๋ฅผ ๊ธฐ์ตํ๋ ํด๋ก์ ๋ฅผ ๋ฐํํ๋ค.
return event => {
// delay๊ฐ ๊ฒฝ๊ณผํ๊ธฐ ์ด์ ์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ์๋ฌด๊ฒ๋ ํ์ง ์๋ค๊ฐ
// delay๊ฐ ๊ฒฝ๊ณผํ์ ๋ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ์๋ก์ด ํ์ด๋จธ๋ฅผ ์ฌ์ค์ ํ๋ค.
// ๋ฐ๋ผ์ delay ๊ฐ๊ฒฉ์ผ๋ก callback์ด ํธ์ถ๋๋ค.
if (timerId) return;
timerId = setTimeout(() => {
callback(event);
timerId = null;
}, delay, event);
};
};
let normalCount = 0;
$container.addEventListener('scroll', () => {
$normalCount.textContent = ++normalCount;
});
let throttleCount = 0;
// throttle ํจ์๊ฐ ๋ฐํํ๋ ํด๋ก์ ๊ฐ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ๋ฑ๋ก๋๋ค.
$container.addEventListener('scroll', throttle(() => {
$throttleCount.textContent = ++throttleCount;
}, 100));
</script>
</body>
</html>
- throttle ํจ์๋ ์ด๋ฒคํธ๋ฅผ ๊ทธ๋ฃนํํด์ ์ผ์ ์๊ฐ ๋จ์๋ก ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ํธ์ถ๋๋๋ก ํธ์ถ ์ฃผ๊ธฐ๋ฅผ ๋ง๋ฆ
- throttle ํจ์์ ๋ ๋ฒ์งธ ์ธ์ delay๊ฐ ๊ฒฝ๊ณผํ์ ๋ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ์ฝ๋ฐฑ ํจ์๋ฅผ ํธ์ถํ๊ณ ์๋ก์ด ํ์ด๋จธ๋ฅผ ์ฌ์ค์
- delay ์๊ฐ ๊ฐ๊ฒฉ์ผ๋ก ์ฝ๋ฐฑ ํจ์๊ฐ ํธ์ถ
์ด์ฒ๋ผ ์คํธ๋กค์ scroll ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ ๋ฌดํ ์คํฌ๋กค UI ๊ตฌํ ๋ฑ์ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋ฉ๋๋ค. (์ค๋ฌด์์๋ Underscore์ throttle ํจ์๋ Lodash์ throttle ํจ์๋ฅผ ์ฃผ๋ก ์ฌ์ฉ)
[์ถ์ฒ] ๋ชจ๋ ์๋ฐ์คํฌ๋ฆฝํธ Deep Dive
'JavaScript > ๋ชจ๋ ์๋ฐ์คํฌ๋ฆฝํธ Deep Dive' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JavaScript] REST API (0) | 2022.08.16 |
---|---|
[JavaScript] Ajax (0) | 2022.08.15 |
[JavaScript] ์ด๋ฒคํธ (0) | 2022.08.13 |
[JavaScript] DOM (0) | 2022.08.12 |
[JavaScript] ๋ธ๋ผ์ฐ์ ์ ๋ ๋๋ง ๊ณผ์ (0) | 2022.08.09 |
- Total
- Today
- Yesterday
- ์๋ฐ์คํฌ๋ฆฝํธ
- ์ด์์ฒด์
- ํ๋กํ ์ฝ
- ๋์์ธ ํจํด
- ๋ฐฑ์ค node.js
- ์นด์นด์ค ์ธํด
- ์ฝ๋ฉํ ์คํธ
- JavaScript
- http
- ํฌํฌ์ธํฐ
- ๋ ์์ปฌ ํ๊ฒฝ
- ๋ชจ๋ ์๋ฐ์คํฌ๋ฆฝํธ deep dive
- ๋ฐฑ์ค javascript
- ์ด๋ถํ์
- ํ๋กํผํฐ
- ํจ์ํ ํ๋ก๊ทธ๋๋ฐ
- ๊ฐ์ฒด์งํฅ ํ๋ก๊ทธ๋๋ฐ
- ์ ์ญ ๋ณ์
- TDD
- 2019 ์นด์นด์ค ๊ฐ๋ฐ์ ๊ฒจ์ธ ์ธํด
- ๋คํธ์ํฌ
- ๋ฐฑ์ค
- fp
- Baekjoon
- git
- map
- ํ๋ก๊ทธ๋๋จธ์ค
- ์๋ฐ
- ์๊ณ ๋ฆฌ์ฆ
- ๋ค์ด๋๋ฏน ํ๋ก๊ทธ๋๋ฐ
์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |