Skip to content

Latest commit

ย 

History

History
402 lines (238 loc) ยท 12.6 KB

README.md

File metadata and controls

402 lines (238 loc) ยท 12.6 KB

NomadCoders todo-list contest

Todo list ์ปจํ…Œ์ŠคํŠธ์— ์ฐธ๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด ์‹œ์ž‘ํ•œ ๋ฐ”๋‹๋ผ ์ž๋ฐ”์Šคํ‚™ํŠธ ํ”„๋กœ์ ํŠธ

image

(21๋…„ ๋‹น์‹œ ๊นƒํ—ˆ๋ธŒ)


๋ฌด๋ ค ๐Ÿ†์ธ๊ธฐ์ƒ์— ๋‹น์ฒจ์ด ๋˜์—ˆ๋‹ค! (์น˜ํ‚จ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!)

์ด ํ”„๋กœ์ ํŠธ์— ํˆฌํ‘œํ•ด์ฃผ์‹  ๋ชจ๋“  ๋ถ„๋“ค๊ป˜ ์ด ์ž๋ฆฌ๋ฅผ ๋นŒ๋ ค ๊ฐ์‚ฌ์˜ ๋ง์”€์„ ๋“œ๋ฆฐ๋‹ค.



ํ”„๋กœ์ ํŠธ ๋งํฌ



1. ์Šคํƒ

Only Javascript

์„œ๋“œ ํŒŒํ‹ฐ๋Š” ์ผ์ฒด ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค. (๋…ธ๋“œ ์ฒจ๊ฐ€์œจ 0%)


2. ๊ธฐ๋Šฅ

์ดํ•˜๋Š” ํ˜„์žฌ ๋ชจ๋‘ ๊ตฌํ˜„๋œ ๊ธฐ๋Šฅ๋“ค์ด๋‹ค.

๋ชจ๋“  ๋ฐ์ดํ„ฐ๋Š” ์ž๋™์œผ๋กœ LocalStorage์— ์ €์žฅ๋œ๋‹ค.

ํŽผ์น˜๊ธฐ/์ ‘๊ธฐ

2 - 1. ์œ ์ € ๐Ÿ™โ€โ™‚๏ธ

  • Display name ์„ค์ •(์ตœ์ดˆ 1ํšŒ) ย  ๋ฏธ๋ฆฌ๋ณด๊ธฐ

  • ๋กœ๊ทธ์•„์›ƒ(๋ฐ์ดํ„ฐ ์ดˆ๊ธฐํ™”)

2 - 2. ์นด๋“œ ๐Ÿ“

2 - 3. ํ•ด์‹œํƒœ๊ทธ ๐Ÿท

2 - 4. ํ”„๋กœํ•„ ๐Ÿ˜Ž

  • ๊ฐ์ข… ๋ฉ”๋‰ด

  • Todo ์ง„์ฒ™๋„ ย  ๋ฏธ๋ฆฌ๋ณด๊ธฐ

  • ํ”„๋กœํ•„ ์ˆจ๊ธฐ๊ธฐ ๊ฐ€๋Šฅ ย  ๋ฏธ๋ฆฌ๋ณด๊ธฐ

    • ๋””๋ฐ”์ด์Šค ๋†’์ด๋ฅผ ๊ณ ๋ คํ•˜์—ฌ ์ž๋™์œผ๋กœ ํ”„๋กœํ•„์ด ์ˆจ๊ฒจ์ง(๋””๋ฐ”์ด์Šค ๋†’์ด 1000px ๋ฏธ๋งŒ ์‹œ)

2 - 5. ๊ธฐํƒ€ ๐ŸŽธ

  • ์–ธ์–ด ๋ณ€๊ฒฝ ์ง€์›(ํ•œ๊ตญ์–ด, ์˜์–ด)

3. ๊ฐœ๋ฐœ ๋น„ํ•˜์ธ๋“œ

ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„ ๐Ÿ“…

  • ํ”„๋กœ์ ํŠธ ์‹œ์ž‘ ๐Ÿ‘‰ 2021๋…„ 2์›” 2์ผ

  • ํ”„๋กœ์ ํŠธ ์ข…๋ฃŒ ๐Ÿ‘ 2021๋…„ 2์›” 10์ผ

๋Œ€๋žต ์ผ์ฃผ์ผ ๋‚จ์ง“ ๊ฑธ๋ ธ๋‹ค.

๋Š๋‚€ ๊ฒƒ

  • ๊ตฌํ˜„ํ•˜๋ฉด์„œ ๊ฐ€์žฅ ํž˜๋“ค์—ˆ๋˜ ์ˆœ์„œ๋Š” ์ด๋ฒคํŠธ > ํ•ด์‹œํƒœ๊ทธ ๊ตฌํ˜„ > ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ „ํ™˜ > ๋‚˜๋จธ์ง€ ์˜€๋‹ค.

2๋…„ ํ›„ ๋Š๋‚€ ์ 

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

4. ๋ฌธ์ œ ๋ฐ ํ•ด๊ฒฐ

๐Ÿ™‰ ์นด๋“œ ๊ฐ๊ฐ์— ๊ณ ์œ ํ•œ ID๋ฅผ ๋ถ€์—ฌํ•˜๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•?

LocalHost์— ์ €์žฅ๋œ ์นด๋“œ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ์‹๋ณ„์ž๋กœ ๊ฐ ์นด๋“œ ๋ฐ์ดํ„ฐ์— ID๋ฅผ ๋ถ€์—ฌํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ID๋ฅผ ์–ด๋–ค ๊ธฐ์ค€์œผ๋กœ ๋งŒ๋“ค์ง€ ๊ณ ๋ฏผ์ด ๋˜์—ˆ๋‹ค.

๋‹จ์ˆœํ•˜๊ฒŒ ๋– ์˜ฌ๋ฆฐ ์ƒ๊ฐ์œผ๋กœ๋Š”

1. ID๋ฅผ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” 1, 2, 3, 4... ๋กœ ๋ถ€์—ฌํ•˜์ž.
2. ํ•ด์‹œํ•จ์ˆ˜์— ์นด๋“œ์˜ ํ…์ŠคํŠธ๋ฅผ ๋„ฃ์–ด์„œ ๋‚˜์˜จ ๊ฒฐ๊ณผ๋ฅผ ID๋กœ ์‚ฌ์šฉํ•˜์ž.

๊ฐ€ ์žˆ์—ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ด ๋‘ ๋ฐฉ๋ฒ•์€ ์ „๋ถ€ ์น˜๋ช…์ ์ธ(?) ๋‹จ์ ์ด ์กด์žฌํ–ˆ๋‹ค.

  • 1๋ฒˆ์€ ID๋ฅผ ๋ถ€์—ฌํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์งœ๊ฒŒ ๋˜๋ฉด ๋กœ์ง์ด ๋งค์šฐ ๋ณต์žกํ•ด์ง„๋‹ค๋Š” ๊ฒƒ(+ ๋ฉ”๋ชจ๋ฆฌ๋„ ์‚ฌ์šฉํ•ด์•ผ ํ•จ), ๋‹จ์ˆœํ•˜๊ฒŒ ์ง ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ ์นด๋“œ๋ฅผ ๋งŽ์ด ์ƒ์„ฑํ•œ๋‹ค๋ฉด ID์˜ ํฌ๊ธฐ๊ฐ€ ๋ฌดํ•œํžˆ ์ปค์ง„๋‹ค๋Š” ๊ฒƒ.
  • 2๋ฒˆ์€ ๊ฐ™์€ ํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ง„ ์นด๋“œ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด, ์œ ์ผํ•œ ID๊ฐ€ ๋ณด์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ .

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ •๋ง ์ข‹์€ ์•„์ด๋””์–ด๋ฅผ ๋– ์˜ฌ๋ ธ๋‹ค.

โœ” Salt๋ฅผ ์ด์šฉํ•ด์„œ ID๋ฅผ ์ƒ์„ฑํ–ˆ๋‹ค.

  1. 10๊ธ€์ž์˜ ๋žœ๋คํ•œ Salt๋ฅผ ์ƒ์„ฑํ–ˆ๋‹ค.
getSalt: () => {
  let salt = "";

  for (let i = 0; i < 10; i++) {
    if (Math.random() > 0.5) {
      const ascii = Math.floor(Math.random() * 26) + 97;
      const c = String.fromCharCode(ascii);
      salt += c;
    } else {
      salt += Math.floor(Math.random() * 10);
    }
  }

  return salt;
},
  1. Hash ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. 32bit์˜ ์ •์ˆ˜๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.
createHash(str) {
  let hash = 0;
  let chr;

  for (let i = 0; i < str.length; i++) {
    chr = str.charCodeAt(i);
    hash = (hash << 5) - hash + chr;
    hash |= 0; // Convert to 32bit integer
  }

  return hash;
},
  1. ์นด๋“œ ํ…์ŠคํŠธ์™€ Salt๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ๊ณ ์œ ํ•œ ID๋ฅผ ์ƒ์„ฑํ–ˆ๋‹ค!
const toConvert = card.text + getSalt();
const id = createHash(toConvert);  // Unique ID! 

(2๋…„ ํ›„ ์—…๋ฐ์ดํŠธ) salt ๋งŒ๋“ค์–ด ์ฃผ๋Š”๊ฑฐ๋„ ์‚ฌ์น˜๋‹ค.

๊ทธ๋ƒฅ ํ˜„์žฌ ์‹œ๊ฐ„์„ ms๋กœ ๋ณ€ํ™˜ํ•ด์„œ ๊ทธ๊ฑฐ๋ฅผ ์•„์ด๋””๋กœ ์จ๋„ ๊ดœ์ฐฎ์•˜์„๋“ฏ ํ•˜๋‹ค..


๐Ÿ™‰ Modal์„ ํšจ์œจ์ ์œผ๋กœ ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค.

์นด๋“œ ์ƒ์„ฑ, ์นด๋“œ ์ˆ˜์ •, ํ•ด์‹œํƒœ๊ทธ ํ•„ํ„ฐ๋ง ๋“ฑ๋“ฑ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์— ๊ฐ™์€ Modal์„ ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค.
React๋ผ๋ฉด state๋ฅผ ์ด์šฉํ•ด ์†์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ–ˆ์ง€๋งŒ, ๋ฐ”๋‹๋ผ๋ผ ๊ทธ๋ ‡๊ธฐ๋Š” ํž˜๋“ค์—ˆ๋‹ค.

โœ” setState๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์—ˆ๋‹ค.

  1. modal Class์— setState ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค.
setState(nextData) {
  this.data = nextData;

  ... ์ค‘๋žต

  this.renderModal();
}
  1. renderModal ๋ฉ”์„œ๋“œ์—์„œ this.data๋ฅผ ์ด์šฉํ•ด modal์˜ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์žฌ๋ Œ๋”๋งํ•œ๋‹ค.
renderModal() {
  const {
    title,
    ...์ƒ๋žต,
  } = this.data;
  
  // modal์˜ title ์žฌ๋ Œ๋”๋ง
  const $modalTitle = this.$modalContainer.querySelector(
    ".modal-content__title"
  );
  $modalTitle.textContent = title;

  ... ์ค‘๋žต
}

๐Ÿ™‰ ๋žœ๋คํ•œ ์ƒ‰์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” ํƒœ๊ทธ์˜ ํ…์ŠคํŠธ๊ฐ€ ๋ณด์ด์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ƒ๊ฒผ๋‹ค.

๊ฐ€๋ น, ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ์€ ์ƒ‰์œผ๋กœ ํƒœ๊ทธ์˜ ์ƒ‰์ด ์„ค์ •๋˜์–ด ๋ฒ„๋ฆฌ๋ฉด ํƒœ๊ทธ์˜ ํ…์ŠคํŠธ๊ฐ€ ๋ณด์ด์ง€ ์•Š์•˜๋‹ค.

imageimage

์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์— ํ…์ŠคํŠธ์˜ ์ƒ‰์„ ์–ด๋‘ก๊ฒŒ ๋งŒ๋“œ๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ• ๊นŒ ๊ณ ๋ฏผํ–ˆ์ง€๋งŒ,
๋กœ์ง์ด ๋ณต์žกํ•ด์งˆ ๊ฒƒ ๊ฐ™์•˜๊ณ  ์ด๊ฒƒ์ด ํšจ์œจ์ ์ธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์•„๋‹Œ ๊ฒƒ ๊ฐ™์•˜๋‹ค.

๊ทธ๋Ÿฌ๋‹ค ์ข‹์€ ์•„์ด๋””์–ด๋ฅผ ๋ฐœ๊ฒฌํ–ˆ๋‹ค.

โœ” ํƒœ๊ทธ ์ƒ์„ฑ์‹œ, ํƒœ๊ทธ ๋ฐฐ๊ฒฝ์ƒ‰์˜ alpha ๊ฐ’์„ ์˜๋„์ ์œผ๋กœ ๋‚ฎ์€ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•œ๋‹ค.

์ด๊ฒƒ์ด ๋งค์šฐ ํšจ๊ณผ์ ์ด์—ˆ๋˜ ์ด์œ ๊ฐ€ ์žˆ๋‹ค.

1. ๊ธฐ๋ณธ์ ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋Š” ๋‹คํฌ๋ชจ๋“œ์ด๊ธฐ ๋•Œ๋ฌธ์—, alpha ๊ฐ’์„ ๋‚ฎ๊ฒŒ ์„ค์ •ํ•˜๋ฉด ๊ทธ๋งŒํผ ํƒœ๊ทธ์˜ ์ƒ‰๋„ ์–ด๋‘์›Œ์ง„๋‹ค.
2. ํƒœ๊ทธ์˜ ํ…์ŠคํŠธ๊ฐ€ ํƒœ๊ทธ ๋ฐฐ๊ฒฝ์ƒ‰์— ๋น„ํ•ด ์ƒ๋Œ€์ ์œผ๋กœ ์–ด๋‘์›Œ์ง„๋‹ค.

๋‹ค์Œ์€ ํƒœ๊ทธ ๋ฐฐ๊ฒฝ์ƒ‰์˜ alpha๊ฐ’์— ๋”ฐ๋ฅธ ํƒœ๊ทธ์˜ ๋ชจ์Šต์ด๋‹ค.

alpha=1 alpha=0.6 alpha=0.2
image image image

์ •๋ง ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค!

(2๋…„ ํ›„ ์—…๋ฐ์ดํŠธ) HSL ์ปฌ๋Ÿฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐ์€ ํ†ค์œผ๋กœ ๋ Œ๋”๋งํ•˜๋ฉด opacity๋ฅผ ๋‚ฎ์ถœ ํ•„์š”๊ฐ€ ์—†๋‹ค.

๋ฌธ์ œ๋Š” ๋ผ์ดํŠธ๋ชจ๋“œ์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•ด์•ผํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
๋ถ„๋ช… opacity๋งŒ์œผ๋กœ๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ ์ƒ๊ฐํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด RGB ๋Œ€์‹  HSL ์ปฌ๋Ÿฌ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

Hue ๊ฐ’์˜ ๋ฒ”์œ„๋ฅผ ์ „์ฒด๋กœ ๋‘๊ณ ,
Saturation์˜ ๋ฒ”์œ„๋ฅผ ๋Œ€๋žต 55 ~ 60%,
Lightness์˜ ๋ฒ”์œ„๋ฅผ ๋Œ€๋žต 75 ~ 85%

์ •๋„๋กœ ํ•ด๋‘๊ณ  ๋žœ๋ค์œผ๋กœ ๋ Œ๋”๋งํ•˜๋ฉด ๋ฐ์€ ํ†ค์˜ ํŒŒ์Šคํ…” ์ƒ‰์ƒ์ด ๋‚˜์˜จ๋‹ค.

์ด ๋ฐฉ๋ฒ•์„ ๋– ์˜ฌ๋ฆฌ๊ณ , ๊ฐ™์€ ๋ฐฉ์‹์˜ ํƒœ๊ทธ ์ƒ์„ฑ์„ ์ด ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜์˜€๋‹ค.


๐Ÿ™‰ 'element๋“ค์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋๋‚ฌ์„ ๋•Œ' ๋ฅผ setTimeout์œผ๋กœ ์บ์น˜ํ–ˆ๋‹ค.

์ด๋ฅผ ์ ์šฉํ•˜๊ธฐ ์ „์—, ๊ตฌ๊ธ€๋ง์„ ํ•ด๋ณด๋‹ˆ ๋Œ€๋ถ€๋ถ„์˜ Stackoverflow, ๋ธ”๋กœ๊ทธ, ํฌ์ŠคํŠธ ๋“ฑ์—์„œ setTimeout์„ ์‚ฌ์šฉํ•จ์„ ์•Œ๊ฒŒ๋˜์—ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด,

/* CSS */

.element {
  ... ์ค‘๋žต
  opacity: 0;
  animation: customAnimation 0.5s linear forwards;
}

@keyframes customAnimation {
  from {
    opacity: 0;
  }
  
  to {
    opacity: 1;
  }
}
// JS

const element = document.querySelector(".element");

// ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ข…๋ฃŒ (css์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ 0.5์ดˆ๋กœ ์„ค์ •)
setTimeout(whenAnimationEnd, 500);

์ฒ˜๋Ÿผ ๋ง์ด๋‹ค.

๊ทธ๋ž˜์„œ ๋‹น์—ฐํžˆ ์ด๊ฒƒ์ด ์ •์„์ ์ธ ๋ฐฉ๋ฒ•์ธ์ค„ ์•Œ๊ณ ์žˆ์—ˆ๋‹ค.

โœ” ๊ทธ๋Ÿฌ๋‚˜, animationend ์ด๋ฒคํŠธ๊ฐ€ ์กด์žฌํ•œ๋‹ค.

// JS

const element = document.querySelector(".element");

// ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ข…๋ฃŒ์ด๋ฒคํŠธ
element.addEventListener("animationend", whenAnimationEnd);

์ด๊ฒƒ์„ ์•Œ๊ฒŒ๋œ ํ›„ ๋ฉ˜ํƒˆ์ด ๋‚˜๊ฐˆ ๋ป”ํ–ˆ๋‹ค. ๐Ÿ˜ญ๐Ÿ˜ญ
๋ถ€์กฑํ•œ ๋‚ด ๊ฒ€์ƒ‰๋Šฅ๋ ฅ์„ ํƒ“ํ–ˆ๋‹ค.






๋ถ€๋ก. ๊ธฐ๋Šฅ ๋ฏธ๋ฆฌ๋ณด๊ธฐ

์œ ์ € ๐Ÿ™โ€โ™‚๏ธ

Display name ์„ค์ •

init1


์นด๋“œ ๐Ÿ“

์นด๋“œ ์ƒ์„ฑ & ํƒœ๊ทธ ์ƒ์„ฑ

create1

์นด๋“œ ์ˆ˜์ •

edit1

์นด๋“œ ์‚ญ์ œ

delete1

์นด๋“œ ์ƒํƒœ๋ณ€๊ฒฝ && Todo ์ง„์ฒ™๋„

statechange1

์นด๋“œ ์ตœ์ƒ๋‹จ ๊ณ ์ •

fixcard1


ํ•ด์‹œํƒœ๊ทธ ๐Ÿท

ํ•ด์‹œํƒœ๊ทธ ํ•„ํ„ฐ๋ง

filter1

ํƒœ๊ทธ ์ƒ‰ ์ˆ˜์ •

coloredit1

ํƒœ๊ทธ ์‚ญ์ œ

deletetag1


ํ”„๋กœํ•„ ๐Ÿ˜Ž

ํ”„๋กœํ•„ ์ˆจ๊ธฐ๊ธฐ ๊ธฐ๋Šฅ + ๋””๋ฐ”์ด์Šค ๋†’์ด ๊ณ ๋ คํ•ด ์ž๋™ ์ˆจ๊ธฐ๊ธฐ

hide1