😊 26.1 ν•¨μˆ˜μ˜ ꡬ뢄

ES6 μ΄μ „μ˜ λͺ¨λ“  ν•¨μˆ˜λŠ” 일반 ν•¨μˆ˜λ‘œμ„œ ν˜ΈμΆœν•  수 μžˆλŠ” 것은 λ¬Όλ‘  μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ ν˜ΈμΆœν•  수 μžˆλ‹€. λ‹€μ‹œ 말해, ES6 μ΄μ „μ˜ λͺ¨λ“  ν•¨μˆ˜λŠ” callable μ΄λ©΄μ„œ constructor이닀. μ΄λŠ” ν•¨μˆ˜μ˜ μ‚¬μš© λͺ©μ μ— 따라 λͺ…ν™•ν•œ ꡬ뢄이 μ—†μœΌλ―€λ‘œ 호좜 방식에 ν‹€λ³„ν•œ μ œμ•½μ΄ μ—†κ³  μƒμ„±μž ν•¨μˆ˜λ‘œ ν˜ΈμΆœλ˜μ§€ μ•Šμ•„λ„ ν”„λ‘œν† νƒ€μž… 객체λ₯Ό μƒμ„±ν•˜κΈ° λ•Œλ¬Έμ— ν˜Όλž€μŠ€λŸ¬μš°λ©° μ‹€μˆ˜λ₯Ό μœ λ°œν•  κ°€λŠ₯성이 있고 μ„±λŠ₯에도 μ’‹μ§€μ•Šλ‹€.

λ”°λΌμ„œ ES6μ—μ„œλŠ” ν•¨μˆ˜λ₯Ό μ‚¬μš© λͺ©μ μ— 따라 μ„Έ 가지 μ’…λ₯˜λ‘œ λͺ…ν™•νžˆ κ΅¬λΆ„ν•œλ‹€.


😊 26.2 λ©”μ„œλ“œ

ES6 μ‚¬μ–‘μ—μ„œ λ©”μ„œλ“œλŠ” λ©”μ„œλ“œ μΆ•μ•½ ν‘œν˜„μœΌλ‘œ μ •μ˜λœ ν•¨μˆ˜λ§Œμ„ μ˜λ―Έν•œλ‹€.

const obj = {
  x: 1,
  // fooλŠ” λ©”μ„œλ“œμ΄λ‹€.
  foo() { return this.x; },
  // bar에 λ°”μΈλ”©λœ ν•¨μˆ˜λŠ” λ©”μ„œλ“œκ°€ μ•„λ‹Œ 일반 ν•¨μˆ˜μ΄λ‹€.
  bar: function() { return this.x; }
};

console.log(obj.foo()); // 1
console.log(obj.bar()); // 1

ES6 μ‚¬μ–‘μ—μ„œ μ •μ˜ν•œ λ©”μ„œλ“œλŠ” μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 μ—†λŠ” non-constructorλ‹€. λ”°λΌμ„œ ES6 λ©”μ„œλ“œλŠ” μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ ν˜ΈμΆœν•  수 μ—†λ‹€. ES6 λ©”μ„œλ“œλŠ” μžμ‹ μ„ λ°”μΈλ”©ν•œ 객체λ₯Ό κ°€λ¦¬ν‚€λŠ” λ‚΄λΆ€ 슬둯[[HomeObject]] λ₯Ό κ°–λŠ”λ‹€. 즉 ES6 λ©”μ„œλ“œλŠ” super ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

const base = {
  name: 'Lee',
  sayHi() {
    return `Hi! ${this.name}`;
  }
};

const derived = {
  __proto__: base,
  // sayHiλŠ” ES6 λ©”μ„œλ“œλ‹€. ES6 λ©”μ„œλ“œλŠ” [[HomeObject]]λ₯Ό κ°–λŠ”λ‹€.
  // sayHi의 [[HomeObject]]λŠ” sayHiκ°€ λ°”μΈλ”©λœ 객체인 derivedλ₯Ό 가리킀고
  // superλŠ” sayHi의 [[HomeObject]]의 ν”„λ‘œν† νƒ€μž…μΈ baseλ₯Ό 가리킨닀.
  sayHi() {
    return `${super.sayHi()}. how are you doing?`;
  }
};

console.log(derived.sayHi()); // Hi! Lee. how are you doing?

😊 26.3 ν™”μ‚΄ν‘œ ν•¨μˆ˜

ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” function ν‚€μ›Œλ“œ λŒ€μ‹  ν™”μ‚΄ν‘œ(β‡’, fat arrow)λ₯Ό μ‚¬μš©ν•˜μ—¬ 기쑴의 ν•¨μˆ˜ μ •μ˜λ³΄λ‹€ κ°„λž΅ν•˜κ²Œ ν•¨μˆ˜λ₯Ό μ •μ˜ν•  수 μžˆλ‹€. λ˜ν•œ μ½œλ°±ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ thisκ°€ μ „μ—­ 객체λ₯Ό κ°€λ¦¬ν‚€λŠ” 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•œ λŒ€μ•ˆμœΌλ‘œ μœ μš©ν•˜λ‹€.

βœ” 26.3.1 ν™”μ‚΄ν‘œ ν•¨μˆ˜ μ •μ˜

  • ν•¨μˆ˜ μ •μ˜
    ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” ν•¨μˆ˜ μ„ μ–Έλ¬ΈμœΌλ‘œ μ •μ˜ν•  수 μ—†κ³  ν•¨μˆ˜ ν‘œν˜„μ‹μœΌλ‘œ μ •μ˜ν•΄μ•Ό ν•œλ‹€. 호좜 방식은 κΈ°μ‘΄ ν•¨μˆ˜μ™€ λ™μΌν•˜λ‹€.
    const multiply = (x, y) => x * y;
    multiply(2, 3); // -> 6
  • λ§€κ°œλ³€μˆ˜ μ„ μ–Έ
    λ§€κ°œλ³€μˆ˜κ°€ κ°œμˆ˜μ— 따라 λ‹€μŒκ³Ό 같이 μ„ μ–Έν•  수 μžˆλ‹€.
    // λ§€κ°œλ³€μˆ˜κ°€ μ—¬λŸ¬κ°œμΈ 경우
    const arrow = (x, y) => { ... };
    
    // λ§€κ°œλ³€μˆ˜κ°€ ν•˜λ‚˜μΈ 경우 () μƒλž΅ κ°€λŠ₯
    const arrow = x => { ... };
    
    // λ§€κ°œλ³€μˆ˜κ°€ μ—†λŠ” 경우 ()λ₯Ό μƒλž΅ λΆˆκ°€
    const arrow = () => { ... };
  • ν•¨μˆ˜ λͺΈμ²΄ μ •μ˜
    ν•¨μˆ˜ λͺΈμ²΄κ°€ ν•˜λ‚˜μ˜ 문으둜 κ΅¬μ„±λœλ‹€λ©΄ ν•¨μˆ˜ λͺΈμ²΄λ₯Ό κ°μ‹ΈλŠ” μ€‘κ΄„ν˜Έ {}λ₯Ό μƒλž΅ν•  수 μžˆλ‹€. 이 λ•Œ ν•¨μˆ˜ λͺΈμ²΄ λ‚΄λΆ€μ˜ 문이 κ°’μœΌλ‘œ 평가될 수 μžˆλŠ” ν‘œν˜„μ‹μΈ 문이라면 μ•”λ¬΅μ μœΌλ‘œ λ°˜ν™˜λœλ‹€. ν•¨μˆ˜ λͺΈμ²΄μ˜ 문이 ν‘œν˜„μ‹μ΄ μ•„λ‹Œ 문이라면 μ€‘κ΄„ν˜Έλ₯Ό μƒλž΅ν•  수 μ—†λ‹€.
    // concise body
    const power = x => x ** 2;
    power(2); // -> 4
    
    // μœ„ ν‘œν˜„μ€ λ‹€μŒκ³Ό λ™μΌν•˜λ‹€.
    // block body
    const power = x => { return x ** 2; };
    객체 λ¦¬ν„°λŸ΄μ„ λ°˜ν™˜ν•˜λŠ” 경우 객체 λ¦¬ν„°λŸ΄μ„ μ†Œκ΄„ν˜Έ ()둜 감싸주어야 ν•œλ‹€.
    const create = (id, content) => ({ id, content });
    create(1, 'JavaScript'); // -> {id: 1, content: "JavaScript"}
    
    // μœ„ ν‘œν˜„μ€ λ‹€μŒκ³Ό λ™μΌν•˜λ‹€.
    const create = (id, content) => { return { id, content }; };
    ν™”μ‚΄ν‘œ ν•¨μˆ˜λ„ μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜(IFE)둜 μ‚¬μš©ν•  수 μžˆλ‹€.
    const person = (name => ({
      sayHi() { return `Hi? My name is ${name}.`; }
    }))('Lee');
    
    console.log(person.sayHi()); // Hi? My name is Lee.
    ν™”μ‚΄ν‘œ ν•¨μˆ˜λ„ 일급 κ°μ²΄μ΄λ―€λ‘œ Array.prototype.map, Array.prototype.filter, Array.prototype.reduce 같은 κ³ μ°¨ν•¨μˆ˜μ— 인수둜 전달할 수 μžˆλ‹€.
    // ES5
    [1, 2, 3].map(function (v) {
      return v * 2;
    });
    
    // ES6
    [1, 2, 3].map(v => v * 2); // -> [ 2, 4, 6 ]

βœ” 26.3.2 ν™”μ‚΄ν‘œ ν•¨μˆ˜μ™€ 일반 ν•¨μˆ˜μ˜ 차이

  1. ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 μ—†λŠ” non-constructorλ‹€. κ·ΈλŸ¬λ―€λ‘œ prototype ν”„λ‘œνΌν‹°κ°€ μ—†κ³  ν”„λ‘œν† νƒ€μž…λ„ μƒμ„±ν•˜μ§€ μ•ŠλŠ”λ‹€.

    const Foo = () => {};
    // ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ ν˜ΈμΆœν•  수 μ—†λ‹€.
    new Foo(); // TypeError: Foo is not a constructor
  2. μ€‘λ³΅λœ λ§€κ°œλ³€μˆ˜ 이름을 μ„ μ–Έν•  수 μ—†λ‹€.

    function normal(a, a) { return a + a; }
    console.log(normal(1, 2)); // 4
    
    const arrow = (a, a) => a + a;
    // SyntaxError: Duplicate parameter name not allowed in this context
  3. ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” ν•¨μˆ˜ 자체의 this, arguments, super,new.target 바인딩을 κ°–μ§€μ•ŠλŠ”λ‹€.

βœ” 26.3.3 this

ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” ν•¨μˆ˜ 자체의 this 바인딩을 갖지 μ•ŠλŠ”λ‹€. λ”°λΌμ„œ ν™”μ‚΄ν‘œ ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ thisλ₯Ό μ°Έμ‘°ν•˜λ©΄μƒμœ„ μŠ€μ½”ν”„μ˜ thisλ₯Ό κ·ΈλŒ€λ‘œ μ°Έμ‘°ν•œλ‹€. 이λ₯Ό lexical this라고 ν•œλ‹€.

class Prefixer {
  constructor(prefix) {
    this.prefix = prefix;
  }

  add(arr) {
    // add λ©”μ„œλ“œλŠ” 인수둜 μ „λ‹¬λœ λ°°μ—΄ arr을 μˆœνšŒν•˜λ©° λ°°μ—΄μ˜ λͺ¨λ“  μš”μ†Œμ— prefixλ₯Ό μΆ”κ°€ν•œλ‹€.
    // β‘ 
    return arr.map(function (item) {
      return this.prefix + item; // β‘‘
      // -> TypeError: Cannot read property 'prefix' of undefined
    });
  }
}

const prefixer = new Prefixer('-webkit-');
console.log(prefixer.add(['transition', 'user-select']));

βœ” 26.3.4 super

ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” ν•¨μˆ˜ 자체의 super 바인딩을 갖지 μ•ŠλŠ”λ‹€. λ”°λΌμ„œ ν™”μ‚΄ν‘œ ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ superλ₯Ό μ°Έμ‘°ν•˜λ©΄ this와 λ§ˆμ°¬κ°€μ§€λ‘œ μƒμœ„ μŠ€μ½”ν”„μ˜ superλ₯Ό μ°Έμ‘°ν•œλ‹€.

class Base {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return `Hi! ${this.name}`;
  }
}

class Derived extends Base {
  // ν™”μ‚΄ν‘œ ν•¨μˆ˜μ˜ superλŠ” μƒμœ„ μŠ€μ½”ν”„μΈ constructor의 superλ₯Ό 가리킨닀.
  sayHi = () => `${super.sayHi()} how are you doing?`;
}

const derived = new Derived('Lee');
console.log(derived.sayHi()); // Hi! Lee how are you doing?

클래슀 ν•„λ“œμ— ν• λ‹Ήν•œ ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” ν”„λ‘œν† νƒ€μž… λ©”μ„œλ“œκ°€ μ•„λ‹ˆλΌ μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œκ°€ λœλ‹€. λ”°λΌμ„œ λ©”μ„œλ“œλ₯Ό μ •μ˜ν•  λ•ŒλŠ” ES6λ©”μ„œλ“œ μΆ•μ•½ ν‘œν˜„μœΌλ‘œ μ •μ˜ν•œ ES6λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€.

// Good
class Person {
  // 클래슀 ν•„λ“œ μ •μ˜
  name = 'Lee';

  sayHi() { console.log(`Hi ${this.name}`); }
}
const person = new Person();
person.sayHi(); // Hi Lee

βœ” 26.3.5 arguments

ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” ν•¨μˆ˜ 자체의 arguments 바인딩을 갖지 μ•ŠλŠ”λ‹€. λ”°λΌμ„œ ν™”μ‚΄ν‘œ ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ argumentsλ₯Ό μ°Έμ‘°ν•˜λ©΄ this와 λ§ˆμ°¬κ°€μ§€λ‘œ μƒμœ„ μŠ€μ½”ν”„μ˜ argumentsλ₯Ό μ°Έμ‘°ν•œλ‹€. ν•˜μ§€λ§Œ ν™”μ‚΄ν‘œ ν•¨μˆ˜μ—μ„œλŠ” arguments 객체λ₯Ό μ°Έμ‘°ν•  μˆ˜λŠ” μžˆμ§€λ§Œ ν™”μ‚΄ν‘œ ν•¨μˆ˜ μžμ‹ μ—κ²Œ μ „λ‹¬λœ 인수 λͺ©λ‘μ„ 확인할 수 μ—†κ³  μƒμœ„ ν•¨μˆ˜μ—κ²Œ μ „λ‹¬λœ 인수 λͺ©λ‘μ„ μ°Έμ‘°ν•˜λ―€λ‘œ 그닀지 도움이 λ˜μ§€ μ•ŠλŠ”λ‹€. λ”°λΌμ„œ ν™”μ‚΄ν‘œ ν•¨μˆ˜λ‘œ κ°€λ³€ 인자 ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•  λ•ŒλŠ” λ°˜λ“œμ‹œ Rest νŒŒλΌλ―Έν„°λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

(function () {
  // ν™”μ‚΄ν‘œ ν•¨μˆ˜ foo의 argumentsλŠ” μƒμœ„ μŠ€μ½”ν”„μΈ μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜μ˜ argumentsλ₯Ό 가리킨닀.
  const foo = () => console.log(arguments); // [Arguments] { '0': 1, '1': 2 }
  foo(3, 4);
}(1, 2));

// ν™”μ‚΄ν‘œ ν•¨μˆ˜ foo의 argumentsλŠ” μƒμœ„ μŠ€μ½”ν”„μΈ μ „μ—­μ˜ argumentsλ₯Ό 가리킨닀.
// ν•˜μ§€λ§Œ μ „μ—­μ—λŠ” arguments 객체가 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€. arguments κ°μ²΄λŠ” ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œλ§Œ μœ νš¨ν•˜λ‹€.
const foo = () => console.log(arguments);
foo(1, 2); // ReferenceError: arguments is not defined

😊 26.4 Rest νŒŒλΌλ―Έν„°

βœ” 26.4.1 κΈ°λ³Έ 문법

Rest νŒŒλΌλ―Έν„°(λ‚˜λ¨Έμ§€ λ§€κ°œλ³€μˆ˜)λŠ” λ§€κ°œλ³€μˆ˜ 이름 μ•žμ— μ„Έκ°œμ˜ 점 ...을 λΆ™μ—¬μ„œ μ •μ˜ν•œ λ§€κ°œλ³€μˆ˜λ₯Ό μ˜λ―Έν•œλ‹€. Rest νŒŒλΌλ―Έν„°λŠ” ν•¨μˆ˜μ— μ „λ‹¬λœ μΈμˆ˜λ“€μ˜ λͺ©λ‘μ„ λ°°μ—΄λ‘œ μ „λ‹¬λ°›λŠ”λ‹€.

function foo(...rest) {
  // λ§€κ°œλ³€μˆ˜ restλŠ” μΈμˆ˜λ“€μ˜ λͺ©λ‘μ„ λ°°μ—΄λ‘œ μ „λ‹¬λ°›λŠ” Rest νŒŒλΌλ―Έν„°λ‹€.
  console.log(rest); // [ 1, 2, 3, 4, 5 ]
}

foo(1, 2, 3, 4, 5);

일반 λ§€κ°œλ³€μˆ˜μ™€ Rest νŒŒλΌλ―Έν„°λŠ” ν•¨κ»˜ μ‚¬μš©ν•  수 있으며 Rest νŒŒλΌλ―Έν„°λŠ” λ°˜λ“œμ‹œ λ§ˆμ§€λ§‰ νŒŒλΌλ―Έν„°μ΄μ–΄μ•Ό ν•œλ‹€.

function bar(param1, param2, ...rest) {
  console.log(param1); // 1
  console.log(param2); // 2
  console.log(rest);   // [ 3, 4, 5 ]
}
bar(1, 2, 3, 4, 5);

function foo(...rest, param1, param2) { }
foo(1, 2, 3, 4, 5);
// SyntaxError: Rest parameter must be last formal parameter

Rest νŒŒλ¦¬λ―Έν„°λŠ” 단 ν•˜λ‚˜λ§Œ μ„ μ–Έν•  수 있고, ν•¨μˆ˜ 객체의 length ν”„λ‘œνΌν‹°μ— 영ν–₯을 주지 μ•ŠλŠ”λ‹€.

function foo(...rest1, ...rest2) { }

foo(1, 2, 3, 4, 5);
// SyntaxError: Rest parameter must be last formal parameter

function foo(...rest) {}
console.log(foo.length); // 0

function bar(x, ...rest) {}
console.log(bar.length); // 1

function baz(x, y, ...rest) {}
console.log(baz.length); // 2

βœ” 26.4.2 Rest νŒŒλΌλ―Έν„°μ™€ arguments 객체

ES5μ—μ„œλŠ” λ§€κ°œλ³€μˆ˜μ˜ 개수λ₯Ό 사전에 μ•Œ 수 μ—†λŠ” κ°€λ³€ 인자 ν•¨μˆ˜ ν˜ΈμΆœμ‹œ arguments λΌλŠ” μœ μ‚¬ λ°°μ—΄ 객체λ₯Ό μ‚¬μš©ν•΄ 지역 λ³€μˆ˜μ²˜λŸΌ μ‚¬μš©ν•  수 μžˆλ‹€. μ΄λ•Œ argumentλŠ” 배열이 μ•„λ‹Œ μœ μ‚¬ λ°°μ—΄ 객체 μ΄λ―€λ‘œ λ°°μ—΄ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ €λ©΄ Function.prototype.call μ΄λ‚˜ Function.prototype.apply λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄ arguments 객체λ₯Ό λ°°μ—΄λ‘œ λ³€ν™˜ν•΄μ•Όν•˜λŠ” λ²ˆκ±°λ‘œμ›€μ΄ μžˆμ—ˆλ‹€.

function sum() {
  // μœ μ‚¬ λ°°μ—΄ 객체인 arguments 객체λ₯Ό λ°°μ—΄λ‘œ λ³€ν™˜ν•œλ‹€.
  var array = Array.prototype.slice.call(arguments);

  return array.reduce(function (pre, cur) {
    return pre + cur;
  }, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // 15

ES6μ—μ„œλŠ” rest νŒŒλΌλ―Έν„°λ₯Ό μ‚¬μš©ν•˜μ—¬ κ°€λ³€ 인자 ν•¨μˆ˜μ˜ 인수 λͺ©λ‘μ„ λ°°μ—΄λ‘œ 직접 μ „λ‹¬λ°›μ„μˆ˜ μžˆλ‹€. ν™”μ‚΄ν‘œ ν•¨μˆ˜λ‘œ κ°€λ³€ 인자 ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•  λ•ŒλŠ” λ°˜λ“œμ‹œ Rest νŒŒλΌλ―Έν„°λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

function sum(...args) {
  // Rest νŒŒλΌλ―Έν„° argsμ—λŠ” λ°°μ—΄ [1, 2, 3, 4, 5]κ°€ ν• λ‹Ήλœλ‹€.
  return args.reduce((pre, cur) => pre + cur, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15

😊 26.5 λ§€κ°œλ³€μˆ˜ κΈ°λ³Έκ°’

μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 λ§€κ°œλ³€μˆ˜μ˜ κ°œμˆ˜μ™€ 인수의 개수λ₯Ό μ²΄ν¬ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— λ°©μ–΄ μ½”λ“œκ°€ ν•„μš”ν•˜λ‹€. ES6μ—μ„œ λ„μž…λœ λ§€κ°œλ³€μˆ˜ 기본값을 μ‚¬μš©ν•΄ 인수 체크 및 μ΄ˆκΈ°ν™”λ₯Ό ν•  수 μžˆλ‹€.

function sum(x = 0, y = 0) {
  return x + y;
}

console.log(sum(1, 2)); // 3
console.log(sum(1));    // 1

rest νŒŒλΌλ―Έν„°μ—λŠ” 기본값을 지정할 수 μ—†λ‹€.

function foo(...rest = []) {
  console.log(rest);
}
// SyntaxError: Rest parameter may not have a default initializer
profile
μ΄μ‚¬μ€‘μž…λ‹ˆλ‹€!🌟https://velog.io/@devkyoung2

0개의 λŒ“κΈ€

κ΄€λ ¨ μ±„μš© 정보