ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

Promise

 

fetch๋Š” Promise๋ฅผ return ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

 

๐Ÿ“fetch๋ž€? ์™ธ๋ถ€์—์„œ ๋ฐ์ดํ„ฐ(์ •๋ณด)๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜

 

๐Ÿ“Promise๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ์™€ ๋น„๊ตํ•˜๊ธฐ (document.querySelector("์„ ํƒ์ž") vs fetch("์ฃผ์†Œ"))

 

document.querySelector()

const ์š”์†Œ = document.querySelector(".ํด๋ž˜์Šค์ด๋ฆ„");
console.log(์š”์†Œ) // html ์š”์†Œ๊ฐ€ ๋‚˜์˜จ๋‹ค. 
// ex) <div class="ํด๋ž˜์Šค์ด๋ฆ„"></div>

๋ณ€์ˆ˜์— ๋‹ด์œผ๋ฉด ์›ํ•˜๋Š” ๊ฒƒ์ด ๋‚˜์˜จ๋‹ค.

 

fetch()

const response = fetch("https://jsonplaceholder.typicode.com/todos/1");
console.log(response);

์ด์ƒํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚˜์˜จ๋‹ค. ์ด๊ฒŒ ๋ฐ”๋กœ promise ์ด๋‹ค.

์ด๋Ÿฐ promise๋ฅผ ๋งŒ๋‚˜๋ฉด ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„์•ผ ํ•œ๋‹ค.

 

๐Ÿ‘‡๐Ÿป๊ธ€๋กœ ์ •๋ฆฌํ•œ ๋‚ด์šฉ

1. fetch ํ•จ์ˆ˜๋ฅผ ์ผ๋ฐ˜ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์‹œ, ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ์˜ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์—†๋‹ค. 
2. JS์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” json์„ ์ด์šฉํ•œ ๋ณ€ํ™˜์ด ํ•„์š”ํ•˜๋‹ค. ๊ทธ ๋‹จ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
3. fetch ํ•จ์ˆ˜๋Š” promise๋ผ๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
4. promise ๊ฐ์ฒด๋Š” then์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค.
5. then ๋ฉ”์„œ๋“œ๋Š” ์ธํ’‹์œผ๋กœ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ๋ฐ›๋Š”๋‹ค.
6. ๊ทธ ์ฝœ๋ฐฑํ•จ์ˆ˜์˜ ์ธ์ž์—๋Š” promise ๊ฐ์ฒด์˜ return ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค.
    ์ฝœ๋ฐฑํ•จ์ˆ˜ ์•ˆ์—์„œ๋Š” json ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด JS์—์„œ ํ™œ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค.
7. json๋„ promise๋ฅผ return ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋˜ then์œผ๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
8. ๊ฒฐ๊ตญ JS์—์„œ ํ™œ์šฉ ๊ฐ€๋Šฅํ•œ array ํ˜•ํƒœ๋กœ ๊ฒฐ๊ณผ๊ฐ’์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

 

๐Ÿ‘‡๐Ÿป์ด๋Ÿฐ ๋ณต์žกํ•œ ๋ฐฉ๋ฒ•์„ ์™œ ์‚ฌ์šฉํ• ๊นŒ?

๋”๋ณด๊ธฐ
  • ๋งŒ์•ฝ ์„ธํƒ๊ธฐ๋ฅผ ๋Œ๋ฆฌ๊ณ , ์„ธํƒ๊ธฐ๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ์ž‘์—…์„ ๋ชปํ•œ๋‹ค๋ฉด ๊ต‰์žฅํžˆ ๋น„ํšจ์œจ์ ์ผ ๊ฒƒ์ด๋‹ค.
  • ์ผ๋ฐ˜์ ์œผ๋กœ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์„ธํƒ๊ธฐ๋ฅผ ์ž‘๋™ํ•œ ํ›„ ๋‹ค๋ฅธ ์ผ์„ ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์„ธํƒ๊ธฐ๋Š” ์•Œ์•„์„œ ๋Œ์•„๊ฐ€๋‹ค ๋๋‚ธ๋‹ค.
  • fetch๋Š” ์™ธ๋ถ€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ผ์ด๋‹ค. ์ฆ‰, ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค. ๋งˆ์น˜ ์„ธํƒ๊ธฐ์ฒ˜๋Ÿผ
  • ์ฝ”๋“œ๋Š” ์›๋ž˜ ์œ„์—์„œ ์•„๋ž˜๋กœ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰๋œ๋‹ค.
    ๊ทธ๋Ÿฐ๋ฐ ์œ„์— ์žˆ๋Š” ์ฝ”๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๋Š”๋ฐ ๋„ˆ๋ฌด ๋А๋ฆฌ๋ฉด ์•„๋ž˜ ์ฝ”๋“œ๋“ค์€ ์‹คํ–‰๋˜์ง€ ๋ชปํ•œ๋‹ค.
  • ์˜ค๋ž˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋Š” ๋น„๋™๊ธฐ๋กœ ์‹คํ–‰ํ•˜์—ฌ ๋‹ค์Œ ์ฝ”๋“œ๋Š” ๊ณ„์† ์ง„ํ–‰๋˜๋„๋ก ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ๋” ์ข‹๋‹ค.
  • ๋Œ€์‹  .then์„ ํ†ตํ•ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์ด ๋๋‚˜๋ฉด .then() ํ•จ์ˆ˜๋กœ ๊ฒฐ๊ณผ๊ฐ€ ์ „๋‹ฌ๋˜๊ฒŒ ๋งŒ๋“  ๊ฒƒ์ด๋‹ค.

 

console.log("์‹œ์ž‘")
fetch("https://jsonplaceholder.typicode.com/todos/1")
  .then((response) => {
    return response.json();
  })
  .then((data) => {
    console.log(data); // 2. ์–˜๋Š” ๋‚˜์ค‘์— ์ถœ๋ ฅ๋จ 
  });

console.log("fetch ๋๋‚ฌ๋‹ˆ?"); // 1. ์–˜๊ฐ€ ๋จผ์ € ์ถœ๋ ฅ๋˜๊ณ 

 

๐Ÿ“fetch ์ž‘์„ฑ๋ฒ•

1. fetch์˜ ์ธ์ž๋กœ ์ฃผ์†Œ ์ž‘์„ฑ

fetch(์ฃผ์†Œ)

 

2. ๋’ค์— .then(ํ•จ์ˆ˜) ์ž‘์„ฑ 

fetch(์ฃผ์†Œ).then(() => { })

 

3. then์˜ ์ฝœ๋ฐฑํ•จ์ˆ˜์— ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ return ํŒŒ๋ผ๋ฏธํ„ฐ.json() ์ž‘์„ฑ

fetch(์ฃผ์†Œ).then((ํŒŒ๋ผ๋ฏธํ„ฐ์ด๋ฆ„) => { 
	return ํŒŒ๋ผ๋ฏธํ„ฐ์ด๋ฆ„.json();
})

 

4. .then์„ ํ•œ ๋ฒˆ ๋” ์ž‘์„ฑ ๋ฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์‚ฌ์šฉ

fetch(์ฃผ์†Œ).then((ํŒŒ๋ผ๋ฏธํ„ฐ์ด๋ฆ„) => { 
	return ํŒŒ๋ผ๋ฏธํ„ฐ์ด๋ฆ„.json();
}).then((ํ•„์š”ํ•œ๋ฐ์ดํ„ฐ) => {
		// ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ
		console.log(ํ•„์š”ํ•œ๋ฐ์ดํ„ฐ)
})

 

 

options

๐Ÿ“options๋ž€?

const options = {
  method: "GET",
  headers: {
    accept: "application/json",
    Authorization:
      "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmOGE3YjI2ZmRmZmFiYmVjMjNhZWE0NjZkMzFkODM2NSIsIm5iZiI6MTcyOTU3OTAwMi43Nzc1MzgsInN1YiI6IjY0NzFhM2NmYTE5OWE2MDBmOTQxZjlhYyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.H2-DM_YlyvlwTr4KI-xacY2oeCrSVxybY2tRcPFRhwU",
  },
};

fetch(
  "https://api.themoviedb.org/3/movie/upcoming?language=en-US&page=1",
  options
)
  .then((res) => res.json())
  .then((res) => console.log(res))
  .catch((err) => console.error(err));

๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ์š”์ฒญํ•œ ๊ฒฝ์šฐ์— ๋„ฃ๋Š”๋‹ค.

  • method
    - GET, POST, PUT, PATCH, DELETE
  • headers
    - accept: "application/json": ์„œ๋ฒ„์—๊ฒŒ JSON ํ˜•์‹์˜ ์‘๋‹ต์„ ์š”์ฒญํ•œ๋‹ค.
    - Authorization: ์ธ์ฆ ์ •๋ณด ๋„ฃ๋Š” ๊ณณ (์—†์„ ๋•Œ๋„ ์žˆ์Œ)

์ž‘์„ฑ๋ฒ•: fetch์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋„ฃ๋Š”๋‹ค.

fetch(์ฃผ์†Œ, { method: "GET", headers: { ... } })

 

 

 

method

  • method์˜ ์ข…๋ฅ˜
    1. GET: ์กฐํšŒ
    2. POST: ์ถ”๊ฐ€, ๋“ฑ๋ก
    3. PUT: ์ „์ฒด ์ˆ˜์ •
    4. PATCH: ์ผ๋ถ€ ์ˆ˜์ •
    5. DELETE: ์‚ญ์ œ

[๋ฌธ์„œ]

method์™€ ์ฃผ์†Œ๊ฐ€ ์žˆ๋‹ค.

์ฃผ๋กœ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋Š” method, ์ฃผ์†Œ๋ฅผ ์ œ๊ณตํ•ด์ฃผ๋ฉฐ ์šฐ๋ฆฌ๋Š” ์‚ฌ์šฉ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

๋”๋ณด๊ธฐ
  • method: GET
  • ์ฃผ์†Œ: /posts
    • ์˜๋ฏธ: ๊ฒŒ์‹œ๋ฌผ ์—ฌ๋Ÿฌ ๊ฐœ(posts)๋ฅผ ์กฐํšŒ(GET)ํ•œ๋‹ค.
    • ํ•˜์ง€๋งŒ GET ๋ฉ”์†Œ๋“œ๋Š” ์ƒ๋žตํ•˜๋ฉด ๊ทธ๋ƒฅ fetch๊ฐ€ ์•Œ์•„์„œ GET์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.
fetch("https://jsonplaceholder.typicode.com/posts", { method: "GET" })
// method ์ƒ๋žต ๊ฐ€๋Šฅ

fetch("https://jsonplaceholder.typicode.com/posts")

 

  • method: GET
  • ์ฃผ์†Œ: /posts/1
    • ์˜๋ฏธ: id๊ฐ€ 1์ธ ๊ฒŒ์‹œ๋ฌผ(/posts/1)์„ ์กฐํšŒ(GET)ํ•œ๋‹ค.
fetch("https://jsonplaceholder.typicode.com/posts/1", { method: "GET" })

 

  • method: GET
  • ์ฃผ์†Œ: /comments?postId=1 vs /post/1/comments
    • ๋ชจ๋“  ๋Œ“๊ธ€ ์ค‘ id๊ฐ€ 1์ธ ๊ฒŒ์‹œ๋ฌผ(/comments?postId=1)์„ ํ•„ํ„ฐ๋งํ•ด์„œ ์กฐํšŒ(GET)์˜จ๋‹ค.
fetch("https://jsonplaceholder.typicode.com/comments?postId=1", { method: "GET" })

 

  • method: POST
  • ์ฃผ์†Œ: /posts
    • ๊ฒŒ์‹œ๋ฌผ๋“ค(/posts)์— ๋ฐ์ดํ„ฐ๋ฅผ **์ถ”๊ฐ€(POST)**ํ•œ๋‹ค
    • POST ๋ฉ”์†Œ๋“œ๋Š” body๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ์Œ
    • body์— ์ถ”๊ฐ€ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๋Š”๋‹ค.
fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',
  body: JSON.stringify({
    title: 'foo',
    body: 'bar',
    userId: 1,
  }),
  headers: {
    'Content-type': 'application/json; charset=UTF-8',
  },
})
  .then((response) => response.json())
  .then((json) => console.log(json));

 

 

  • method: PUT
  • ์ฃผ์†Œ: /posts/1
    • id๊ฐ€ 1์ธ ๊ฒŒ์‹œ๋ฌผ(/posts/1)์„ **์ˆ˜์ •(PUT)**ํ•œ๋‹ค.
    • PUT ๋ฉ”์†Œ๋“œ๋Š” body๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ์Œ
    • body์— ์ˆ˜์ •ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๋Š”๋‹ค.
fetch('https://jsonplaceholder.typicode.com/posts/1', {
  method: 'PUT',
  body: JSON.stringify({
    id: 1,
    title: 'foo',
    body: 'bar',
    userId: 1,
  }),
  headers: {
    'Content-type': 'application/json; charset=UTF-8',
  },
})
  .then((response) => response.json())
  .then((json) => console.log(json));

 

  • method: PATCH
  • ์ฃผ์†Œ: /posts/1
    • id๊ฐ€ 1์ธ ๊ฒŒ์‹œ๋ฌผ(/posts/1)์„ **์ผ๋ถ€ ์ˆ˜์ •(PATCH)**ํ•œ๋‹ค.
    • PATCH ๋ฉ”์†Œ๋“œ๋Š” body๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ์Œ
    • title๋งŒ ์ˆ˜์ •
fetch('https://jsonplaceholder.typicode.com/posts/1', {
  method: 'PATCH',
  body: JSON.stringify({
    title: 'foo',
  }),
  headers: {
    'Content-type': 'application/json; charset=UTF-8',
  },
})
  .then((response) => response.json())
  .then((json) => console.log(json));

 

  • method: DELETE
  • ์ฃผ์†Œ: /posts/1
    • id๊ฐ€ 1์ธ ๊ฒŒ์‹œ๋ฌผ(/posts/1)์„ ์‚ญ์ œ(DELETE)ํ•œ๋‹ค.
fetch('https://jsonplaceholder.typicode.com/posts/1', {
  method: 'DELETE',
});

 

 

 

catch

๐Ÿ“catch๋ž€?

์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ์กฐ์ž‘ํ•˜๋Š” ์šฉ๋„์ด๋‹ค.

const options = {
  method: "GET",
  headers: {
    accept: "application/json",
    Authorization:
      "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmOGE3YjI2ZmRmZmFiYmVjMjNhZWE0NjZkMzFkODM2NSIsIm5iZiI6MTcyOTU3OTAwMi43Nzc1MzgsInN1YiI6IjY0NzFhM2NmYTE5OWE2MDBmOTQxZjlhYyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.H2-DM_YlyvlwTr4KI-xacY2oeCrSVxybY2tRcPFRhwU",
  },
};

fetch(
  "https://api.themoviedb.org/3/movie/upcoming?language=en-US&page=1",
  options
)
  .then((res) => res.json())
  .then((res) => console.log(res))
  .catch((err) => console.error(err));

 

 

๋”๋ณด๊ธฐ

์—ฐ์Šต์šฉ ์„ธํŒ…

[index.html]

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>

<body>
	<div id="movie-container"></div>
	<script src="script.js"></script>
</body>

	</html>

 

[script.js]

const options = {
  method: "GET",
  headers: {
    accept: "application/json",
    Authorization:
      "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmOGE3YjI2ZmRmZmFiYmVjMjNhZWE0NjZkMzFkODM2NSIsIm5iZiI6MTcyOTU3OTAwMi43Nzc1MzgsInN1YiI6IjY0NzFhM2NmYTE5OWE2MDBmOTQxZjlhYyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.H2-DM_YlyvlwTr4KI-xacY2oeCrSVxybY2tRcPFRhwU",
  },
};

fetch(
  "https://api.themoviedb.org/3/movie/upcoming?language=en-US&page=1",
  options
)
  .then((res) => res.json())
  .then((res) => {
   // ๋งŒ์•ฝ ์—ฌ๊ธฐ ์•„๋ž˜ ์ฝ”๋“œ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด catch๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. 
    const movies = res.results;
    const movieContainer = document.getElementById("movie-container");
    movies.forEach((movie) => {
      movieContainer.innerHTML += `<div>${movie.title}</div>`;
    });
  })

 

์ผ๋ถ€๋Ÿฌ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ๋ณธ๋‹ค.

const options = {
  method: "GET",
  headers: {
    accept: "application/json",
    Authorization:
      "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmOGE3YjI2ZmRmZmFiYmVjMjNhZWE0NjZkMzFkODM2NSIsIm5iZiI6MTcyOTU3OTAwMi43Nzc1MzgsInN1YiI6IjY0NzFhM2NmYTE5OWE2MDBmOTQxZjlhYyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.H2-DM_YlyvlwTr4KI-xacY2oeCrSVxybY2tRcPFRhwU",
  },
};

fetch(
  "https://api.themoviedb.org/3/movie/upcoming?language=en-US&page=1",
  options
)
  .then((res) => res.json())
  .then((res) => {
    const movies = res.results;
    // ์ฝ”๋“œ ๋ง๊ฐ€๋œจ๋ฆผ(document.get ์ด๋ผ๋Š” ํ•จ์ˆ˜๋Š” ์—†์–ด์š”!)
    const movieContainer = document.get("movie-container");
    movies.forEach((movie) => {
      movieContainer.innerHTML += `<div>${movie.title}</div>`;
    });
  })

 

catch๋ฅผ ๋ถ™์—ฌ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

const options = {
  method: "GET",
  headers: {
    accept: "application/json",
    Authorization:
      "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmOGE3YjI2ZmRmZmFiYmVjMjNhZWE0NjZkMzFkODM2NSIsIm5iZiI6MTcyOTU3OTAwMi43Nzc1MzgsInN1YiI6IjY0NzFhM2NmYTE5OWE2MDBmOTQxZjlhYyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.H2-DM_YlyvlwTr4KI-xacY2oeCrSVxybY2tRcPFRhwU",
  },
};

fetch(
  "https://api.themoviedb.org/3/movie/upcoming?language=en-US&page=1",
  options
)
  .then((res) => res.json())
  .then((res) => {
    const movies = res.results;
    const movieContainer = document.get("movie-container");
    movies.forEach((movie) => {
      movieContainer.innerHTML += `<div>${movie.title}</div>`;
    });
  })
  .catch((err) => {
    const errorDiv = document.createElement("div");
    errorDiv.textContent = "์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜์ค‘์— ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.";
    document.body.appendChild(errorDiv);
  });
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
TAG
more
ยซ   2026/03   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
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
๊ธ€ ๋ณด๊ด€ํ•จ