๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

ยท ์•ฝ 2๋ถ„

๊ฐ์ฒด#

๊ฐ์ฒด๋Š” ๋ฌธ์žํ˜•์œผ๋กœ๋œ ํ‚ค(key)์™€ ๋ชจ๋“  ์ž๋ฃŒํ˜•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’(value) ์Œ์œผ๋กœ ๊ตฌ์„ฑ๋œ ์—ฌ๋Ÿฌ ํ”„๋กœํผํ‹ฐ(property)์˜ ์ง‘ํ•ฉ์ด๋‹ค. ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์œผ๋กœ ํ•จ์ˆ˜๊ฐ€ ๋ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด ๊ฒฝ์šฐ ๋ฉ”์†Œ๋“œ(method)๋ผ๊ณ  ๋ถˆ๋ฆฐ๋‹ค.

๊ฐ์ฒด๋Š” ๊ณ ์œ ์˜ ํŒจํ‚ค์ง€์— ์ •๋ณด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณดํ˜ธํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

JavaScript์—์„œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์—๋Š” ํฌ๊ฒŒ 2๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. (์ถ”๊ฐ€์ ์œผ๋กœ Object.create()๋ฅผ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.)

let obj1 = new Object(); // '๊ฐ์ฒด ์ƒ์„ฑ์ž' ๋ฌธ๋ฒ•let obj2 = {}; // '๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด' ๋ฌธ๋ฒ•

๋‘ ๋ฐฉ๋ฒ•์€ ์ฐจ์ด๊ฐ€ ์—†๋‹ค.

Object๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋‚˜ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ƒ์„ฑํ•  ๋•Œ, ์ฐจ์ด๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด ์ฐจ์ด์˜ ํ•ต์‹ฌ์€ ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด([[Prototype]])์— ์žˆ๋‹ค.

๊ฐ์ฒด ์ •๋ ฌ ๋ฐฉ์‹#

"ํ”„๋กœํผํ‹ฐ์—” ์ˆœ์„œ๊ฐ€ ์žˆ์„๊นŒ?"

์ •์ˆ˜ ํ”„๋กœํผํ‹ฐ๋Š” ์ž๋™์œผ๋กœ ์ •๋ ฌ๋˜๊ณ , ๊ทธ ์™ธ์˜ ํ”„๋กœํผํ‹ฐ๋Š” ๊ฐ์ฒด์— ์ถ”๊ฐ€ํ•œ ์ˆœ์„œ๋Œ€๋กœ ์ •๋ ฌ๋œ๋‹ค. ์ฐธ๊ณ 

์ฐธ๊ณ ์ž๋ฃŒ#

ยท ์•ฝ 4๋ถ„

ํ”„๋กœํผํ‹ฐ ํ”Œ๋ž˜๊ทธ#

๊ฐ์ฒด ํ”„๋กœํผํ‹ฐ๋Š” ๊ฐ’(value) ์™€ ํ•จ๊ป˜ ํ”Œ๋ž˜๊ทธ(flag) ๋ผ ๋ถˆ๋ฆฌ๋Š” ํŠน๋ณ„ํ•œ ์†์„ฑ ์„ธ๊ฐ€์ง€๋ฅผ ๊ฐ–๋Š”๋‹ค.

  • writable : true ์ด๋ฉด ๊ฐ’์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ฝ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • enumerable : true ์ด๋ฉด for ... in ๋ฃจํ”„์—์„œ ์—ด๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด์—ด๊ฑฐํ•  ์ˆ˜ ์—†๋‹ค.
  • configurable : true ์ด๋ฉด ํ”„๋กœํผํ‹ฐ ์‚ญ์ œ๋‚˜ ํ”Œ๋ž˜๊ทธ ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ทธ๋ ‡์ง€์•Š์œผ๋ฉด ํ”„๋กœํผํ‹ฐ ์‚ญ์ œ์™€ ํ”Œ๋ž˜๊ทธ ์ˆ˜์ •์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

enumerable ํ”Œ๋ž˜๊ทธ#

์—ด๊ฑฐ ๊ฐ€๋Šฅ(enumerable) ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.

๋ฐ˜๋ณต ๊ฐ€๋Šฅ(iterable)์€๋น„์Šทํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ์˜๋ฏธ์ด๋‹ค.

configurable ํ”Œ๋ž˜๊ทธ#

configurable ํ”Œ๋ž˜๊ทธ๋ฅผ false ๋กœ ์„ค์ •ํ•˜๋ฉด ๋Œ์ดํ‚ฌ ๋ฐฉ๋ฒ•์ด ์—†๋‹ค.

defineProperty ๋ฅผ ์จ๋„ ๊ฐ’์„ true ๋กœ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†๋‹ค.

configurable: false ๊ฐ€ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๊ตฌ์ฒด์ ์ธ ์ œ์•ฝ์‚ฌํ•ญ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • configurable ํ”Œ๋ž˜๊ทธ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์Œ
  • enumerable ํ”Œ๋ž˜๊ทธ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์Œ.
  • writable: false ์˜ ๊ฐ’์„ true ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์Œ(true ๋ฅผ false ๋กœ ๋ณ€๊ฒฝํ•˜๋Š”๊ฒƒ์€ ๊ฐ€๋Šฅํ•จ).
  • getter / setter ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Œ(์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ๊ฐ€๋Šฅํ•จ).

์ด๋Ÿฐ ํŠน์ง•์„ ์ด์šฉํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด โ€œ์˜์›ํžˆ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š”โ€ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

๊ด€๋ จ Object ๋ฉ”์†Œ๋“œ#

  • Object.defineProperty : ๊ฐ์ฒด์— ์ƒˆ๋กœ์šด ์†์„ฑ์„ ์ •์˜ํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๊ณ  ๊ทธ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • Object.defineProperties : ๊ฐ์ฒด์— ํ•˜๋‚˜ ๋˜๋Š” ๊ทธ ์ด์ƒ์˜ ์ƒˆ๋กœ์šด ์†์„ฑ์„ ์ •์˜ํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๊ณ  ๊ทธ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • Object.getOwnPropertyDescriptor : ์ฃผ์–ด์ง„ ๊ฐ์ฒด ์ž์‹ ์˜ ์†์„ฑ์— ๋Œ€ํ•œ ์†์„ฑ ์„ค๋ช…์ž(descriptor)๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • Object.getOwnPropertyDescriptors : ์ฃผ์–ด์ง„ ๊ฐ์ฒด ์ž์‹ ์˜ ๋ชจ๋“  ์†์„ฑ๋“ค์˜ ์„ค๋ช…์ž(descriptor)๋“ค์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • Object.preventExtensions(obj) : ๊ฐ์ฒด์— ์ƒˆ๋กœ์šด ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†๊ฒŒ ํ•œ๋‹ค.

  • Object.seal(obj) : ์ƒˆ๋กœ์šด ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€๋‚˜ ๊ธฐ์กด ํ”„๋กœํผํ‹ฐ ์‚ญ์ œ๋ฅผ ๋ง‰์•„์ค€๋‹ค. ํ”„๋กœํผํ‹ฐ ์ „์ฒด์— configurable: false๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ํšจ๊ณผ์ด๋‹ค.

  • Object.freeze(obj) : ์ƒˆ๋กœ์šด ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€๋‚˜ ๊ธฐ์กด ํ”„๋กœํผํ‹ฐ ์‚ญ์ œ, ์ˆ˜์ •์„ ๋ง‰์•„์ค€๋‹ค. ํ”„๋กœํผํ‹ฐ ์ „์ฒด์— configurable: false, writable: false๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ํšจ๊ณผ์ด๋‹ค.

  • Object.isExtensible(obj) : ์ƒˆ๋กœ์šด ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒŒ ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ false๋ฅผ, ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ true๋ฅผ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • Object.isSealed(obj) : ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€, ์‚ญ์ œ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ  ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ๊ฐ€ configurable: false์ด๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • Object.isFrozen(obj) : ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€, ์‚ญ์ œ, ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ  ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ๊ฐ€ configurable: false, writable: false์ด๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์ฐธ๊ณ ์ž๋ฃŒ#

ยท ์•ฝ 9๋ถ„

class ์ž‘์„ฑ ์‹œ์— TypeScript์˜ ํด๋ž˜์Šค ๋ฌธ๋ฒ•๊ณผ JavaScript์˜ ๋ฌธ๋ฒ•์„ ๋น„๊ตํ•ด๋ณด์ž.

JS ํด๋ž˜์Šค ๋ฌธ๋ฒ•#

JS์—์„œ ํด๋ž˜์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.

class MyClass {  publicProp = 'value'; // public ํ”„๋กœํผํ‹ฐ  #privateProp = 'value'; // private ํ”„๋กœํผํ‹ฐ. ES2019 ๋ถ€ํ„ฐ ์ง€์›.  _protectedProp = 'value'; // protected ํ”„๋กœํผํ‹ฐ. ๊ธฐ๋Šฅ์ ์œผ๋กœ ์ง€์›ํ•˜์ง€ ์•Š์ง€๋งŒ, ๊ด€๋ก€์ ์œผ๋กœ _๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ‘œํ˜„ํ•˜๊ณ  ์•ฝ์†ํ•˜์—ฌ ์‚ฌ์šฉํ•จ.
  constructor(...) { // ์ƒ์„ฑ์ž ๋ฉ”์„œ๋“œ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ฐ’์„ ๋ฐ›์•„ ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ค์ •ํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.    // ...  }
  static staticMethod(...) {} // static ๋ฉ”์„œ๋“œ
  publicMethod(...) {} // public ๋ฉ”์„œ๋“œ  #privateMethod(...) {} // private ๋ฉ”์„œ๋“œ
  get publicProp(...) {} // getter ๋ฉ”์„œ๋“œ  set publicProp(...) {} // setter ๋ฉ”์„œ๋“œ
  get privateProp(...) {} // getter ๋ฉ”์„œ๋“œ  set privateProp(...) {} // setter ๋ฉ”์„œ๋“œ
  get protectedProp(...) {} // getter ๋ฉ”์„œ๋“œ  set protectedProp(...) {} // setter ๋ฉ”์„œ๋“œ}

ํด๋ž˜์Šค ์ƒ์†#

JS์—์„œ๋„ ํด๋ž˜์Šค ์ƒ์†์„ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ธฐ์กด ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜์—ฌ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ• ์ˆ˜์žˆ๋‹ค.

class Animal {    constructor(name) {        this.speed = 0;        this.name = name;    }    run(speed) {        this.speed = speed;        alert(`${this.name} ์€/๋Š” ์†๋„ ${this.speed}๋กœ ๋‹ฌ๋ฆฝ๋‹ˆ๋‹ค.`);    }    stop() {        this.speed = 0;        alert(`${this.name} ์ด/๊ฐ€ ๋ฉˆ์ท„์Šต๋‹ˆ๋‹ค.`);    }}
let animal = new Animal('๋™๋ฌผ');
class Rabbit extends Animal {    hide() {        alert(`${this.name} ์ด/๊ฐ€ ์ˆจ์—ˆ์Šต๋‹ˆ๋‹ค!`);    }}
let rabbit = new Rabbit('ํฐ ํ† ๋ผ');
rabbit.run(5); // ํฐ ํ† ๋ผ ์€/๋Š” ์†๋„ 5๋กœ ๋‹ฌ๋ฆฝ๋‹ˆ๋‹ค.rabbit.hide(); // ํฐ ํ† ๋ผ ์ด/๊ฐ€ ์ˆจ์—ˆ์Šต๋‹ˆ๋‹ค!

ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ด๋‹

ํด๋ž˜์Šค Rabbit์œผ๋กœ ๋งŒ๋“  ๊ฐ์ฒด๋Š” rabbit.hide()์™€ ๊ฐ™์€ Rabbit์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ์—๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ณ , rabbit.run()๊ณผ ๊ฐ™์€ Animal์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ์—๋„ ์ ‘๊ทผํ• ์ˆ˜์žˆ๋‹ค.

extends ํ‚ค์›Œ๋“œ๋Š” ์ด๋ ‡๊ฒŒ ํ”„๋กœํ† ํƒ€์ž… ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜์—ฌ, ๊ฐ์ฒด rabbit -> Rabbit.prototype -> Animal.prototype ์ˆœ์œผ๋กœ ๋ฉ”์†Œ๋“œ๋ฅผ ์ฐพ์•„์„œ ์‹คํ–‰ํ•œ๋‹ค.

์ ‘๊ทผ ์ œ์–ด์ž(Access Modifier)#

๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” ์ ‘๊ทผ ์ œ์–ด์ž๋ฅผ ํ™œ์šฉํ•ด ๋‚ด๋ถ€ ์ธํ„ฐํŽ˜์ด์Šค์™€ ์™ธ๋ถ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌ๋ถ„ํ•˜์—ฌ ์บก์Šํ™”ํ•œ๋‹ค.

  • public : ์–ด๋””์„œ๋“ ์ง€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์™ธ๋ถ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค.
  • protected : ํด๋ž˜์Šค ์ž์‹ ๊ณผ ์ž์† ํด๋ž˜์Šค์—์„œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋‚ด๋ถ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค.
  • private : ํด๋ž˜์Šค ์ž์‹ ์—์„œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋‚ด๋ถ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค.

JS์—์„œ๋Š” public, protected ์— ๋Œ€ํ•œ ๋ฌธ๋ฒ•์  ์ง€์›์ด ์—†์œผ๋ฉฐ, ๊ด€์Šต์ ์œผ๋กœ _ ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ protected ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

private ์†์„ฑ์€ ES2019๋ถ€ํ„ฐ ์ง€์›ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

static#

์ •์  ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์„œ๋“œ๋Š” ์–ด๋–ค ํŠน์ •ํ•œ ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ํด๋ž˜์Šค์— ์†ํ•œ ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ ์žํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

์ •์  ๋ฉค๋ฒ„๋Š” ์ƒ์†๋œ๋‹ค.

class Article {    constructor(title, date) {        this.title = title;        this.date = date;    }
    static createTodays() {        // this๋Š” Article์ž…๋‹ˆ๋‹ค.        return new this("Today's digest", new Date());    }}
let article = Article.createTodays();
alert(article.title); // Today's digest

TS ํด๋ž˜์Šค ๋ฌธ๋ฒ•#

TS๋Š” JS์˜ ๋ชจ๋“  ํด๋ž˜์Šค ๋ฌธ๋ฒ•์„ ์ง€์›ํ•˜๋ฉฐ, ๊ทธ์— ๋”ํ•ด์„œ ๋ช‡ ๊ฐ€์ง€ ๋ฌธ๋ฒ•์ ์ธ ์ง€์›์„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค. ํ•˜์ง€๋งŒ ๋ชจ๋“  ๋ฌธ๋ฒ•์€ ์ปดํŒŒ์ผ ํƒ€์ž„์— ๋Œ€ํ•ด์„œ๋งŒ ์ ์šฉ๋˜๊ณ , ๋Ÿฐํƒ€์ž„์—๋Š” ๊ฒฐ๊ตญ JS๊ฐ€์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ๋งŒ์„ ์‚ฌ์šฉํ•ด ๋™์ž‘ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์ž.

๋‹ค์Œ์€ JS์—์„œ ์ง€์›ํ•˜์ง€ ์•Š์ง€๋งŒ, TS์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Class์™€ ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ๋“ค์ด๋‹ค.

readonly#

ํ•„๋“œ์—๋Š” readonly ์ œ์–ด์ž๋ฅผ ์ ‘๋‘์‚ฌ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ƒ์„ฑ์ž ํ•จ์ˆ˜๊ฐ€์•„๋‹Œ ๊ณณ์—์„œ ํ• ๋‹น์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

class Greeter {    readonly name: string = 'world';
    constructor(otherName?: string) {        if (otherName !== undefined) {            this.name = otherName;        }    }
    err() {        this.name = 'not ok';        // Cannot assign to 'name' because it is a read-only property.    }}const g = new Greeter();g.name = 'also not ok';// Cannot assign to 'name' because it is a read-only property.

overload#

JS๋Š” ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐฏ์ˆ˜์™€ ์ž๋ฃŒํ˜•์ด ์ž์œ ๋กญ๊ธฐ ๋•Œ๋ฌธ์—, ์˜ค๋ฒ„๋กœ๋“œ์— ๋Œ€ํ•œ ๊ฐœ๋…์ด์‚ฌ์‹ค์ƒ ์—†๋‹ค. (๋‹จ์ง€, ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ์กฐ๊ฑด์— ๋”ฐ๋ฅธ ๋ถ„๊ธฐ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ •๋„)

TS์—์„œ๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜์— ๋Œ€ํ•ด ๊ตฌ์ฒด์ ์œผ๋กœ ์˜ค๋ฒ„๋กœ๋“œ signature๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

class Point {    // Overloads    constructor(x: number, y: string);    constructor(s: string);    constructor(xs: any, y?: any) {        // TBD    }}
class Util {    // Overloads    len(s: string): number;    len(arr: any[]): number;    len(x: any) {        return x.length;    }}

override#

TypeScript์—์„œ๋Š” ํŒŒ์ƒ ํด๋ž˜์Šค๊ฐ€ ํ•ญ์ƒ ๊ธฐ๋ณธ ํด๋ž˜์Šค์˜ ํ•˜์œ„ ์œ ํ˜•์ด ๋˜๋„๋ก ํ•œ๋‹ค.

class Base {    greet() {        console.log('Hello, world!');    }}
class Derived extends Base {    greet(name?: string) {        if (name === undefined) {            super.greet();        } else {            console.log(`Hello, ${name.toUpperCase()}`);        }    }}
const d = new Derived();d.greet();d.greet('reader');

ํŒŒ์ƒ ํด๋ž˜์Šค๊ฐ€ ๊ธฐ๋ณธ ํด๋ž˜์Šค์˜ ๊ทœ์น™์„ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•œ๋ฐ, ๊ธฐ๋ณธ ํด๋ž˜์Šค๊ฐ€ ํŒŒ์ƒ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

// Alias the derived instance through a base class referenceconst b: Base = d;// No problemb.greet();

๋งŒ์•ฝ ํŒŒ์ƒ ํด๋ž˜์Šค๊ฐ€ ๊ธฐ๋ณธ ํด๋ž˜์Šค๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๋Š”๋‹ค๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

class Base {    greet() {        console.log('Hello, world!');    }}
class Derived extends Base {    // Make this parameter required    greet(name: string) {        // Property 'greet' in type 'Derived' is not assignable to the same property in base type 'Base'.        // Type '(name: string) => void' is not assignable to type '() => void'.        console.log(`Hello, ${name.toUpperCase()}`);    }}
const b: Base = new Derived();// Crashes because "name" will be undefinedb.greet();

์ ‘๊ทผ ์ œ์–ด์ž(Access Modifier)#

Member Visibility

  • public : ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ๋ฉค๋ฒ„๋Š” public์œผ๋กœ ์–ด๋””์„œ๋‚˜ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
  • protected : ํด๋ž˜์Šค ์ž์‹ ๊ณผ ์ž์† ํด๋ž˜์Šค์—์„œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
  • private : protected์™€ ์œ ์‚ฌํ•˜๊ฒŒ ํด๋ž˜์Šค ์ž์‹ ์—์„œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ž์† ํด๋ž˜์Šค์—์„œ๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค.

Why No Static Classes?#

TS(JS)๋Š” Java๋‚˜ C#์—์„œ ์‚ฌ์šฉํ•˜๋Š” static class ๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. (static class๋Š” ์ธ์Šคํ„ด์Šคํ™” ํ•  ์ˆ˜ ์—†๋‹ค.)

๋”ฐ๋ผ์„œ, static class ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋ถˆํ•„์š”ํ•˜๋‹ค. ๋‹จ์ง€ ๋‹จ์ˆœ ๋ฆฌํ„ฐ๋Ÿด ๊ฐ์ฒด๋ฅผ์“ฐ๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•˜๋‹ค.

// Unnecessary "static" classclass MyStaticClass {    static doSomething() {}}
// Preferred (alternative 1)function doSomething() {}
// Preferred (alternative 2)const MyHelperObject = {    dosomething() {},};

abstract#

์•„์ง ๊ตฌํ˜„ํ•˜์ง€ ์•Š์€ ๋ฉ”์†Œ๋“œ์™€ ํ”„๋กœํผํ‹ฐ์— abstract ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. abstract ๋ฉค๋ฒ„๋ฅผ ๊ฐ€์ง„ ํด๋ž˜์Šค๋Š” ๋ฐ˜๋“œ์‹œ abstarct class ์—ฌ์•ผ ํ•œ๋‹ค.

abstract class์˜ ์—ญํ• ์€ abstract ๋ฉค๋ฒ„๋ฅผ ๊ตฌํ˜„ํ•  ์„œ๋ธŒ ํด๋ž˜์Šค์˜ ๊ธฐ์ดˆ ํด๋ž˜์Šค๊ฐ€๋˜๋Š” ๊ฒƒ์ด๋‹ค.

abstract class Base {    abstract getName(): string;
    printName() {        console.log('Hello, ' + this.getName());    }}
class Derived extends Base {    getName() {        return 'world';    }}
const d = new Derived();d.printName();

์ฐธ๊ณ ์ž๋ฃŒ#

ยท ์•ฝ 7๋ถ„

JavaScript ๊ฐ์ฒด ๊ธฐ๋ณธ#

๊ฐ์ฒด๋Š” ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ์™€ ํ•จ์ˆ˜์˜ ์ง‘ํ•ฉ์ด๋‹ค. (ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์†Œ๋“œ)

๊ฐ์ฒด๋Š” ๊ณ ์œ ์˜ ํŒจํ‚ค์ง€์— ์ •๋ณด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณดํ˜ธํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด ๋ฐฉ์‹#

var person = {    name: ['Bob', 'Smith'],    age: 32,    gender: 'male',    interests: ['music', 'skiing'],    bio: function () {        alert(            this.name[0] +                ' ' +                this.name[1] +                ' is ' +                this.age +                ' years old. He likes ' +                this.interests[0] +                ' and ' +                this.interests[1] +                '.',        );    },    greeting: function () {        alert("Hi! I'm " + this.name[0] + '.');    },};

๊ฐ์ฒด์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ์  ํ‘œ๊ธฐ๋ฒ•(person.name)๊ณผ ๊ด„ํ˜ธ ํ‘œ๊ธฐ๋ฒ•(person['age']) ์ด ์žˆ๋‹ค.

this๋Š” ๋ฌด์—‡์ธ๊ฐ€?#

this ํ‚ค์›Œ๋“œ๋Š” ์ง€๊ธˆ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.

Java์—์„œ์˜ this๋Š” ์ธ์Šคํ„ด์Šค ์ž์‹ (self)๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ฐธ์กฐ๋ณ€์ˆ˜์ด๋‹ค. ์ฃผ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜์™€๊ฐ์ฒด ์ž์‹ ์ด ๊ฐ–๊ณ  ์žˆ๋Š” ๋ฉค๋ฒ„๋ณ€์ˆ˜๋ช…์ด ๊ฐ™์„ ๊ฒฝ์šฐ ์ด๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.

public Class Person {
  private String name;
  public Person(String name) {    this.name = name;  }}

ํ•˜์ง€๋งŒ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๊ฒฝ์šฐ Java์™€ ๊ฐ™์ด this์— ๋ฐ”์ธ๋”ฉ๋˜๋Š” ๊ฐ์ฒด๋Š” ํ•œ๊ฐ€์ง€๊ฐ€ ์•„๋‹ˆ๋ผ, ํ•จ์ˆ˜ ํ˜ธ์ถœ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค.

  1. ํ•จ์ˆ˜ ํ˜ธ์ถœ : ๊ธฐ๋ณธ์ ์œผ๋กœ this๋Š” ์ „์—ญ๊ฐ์ฒด์— ๋ฐ”์ธ๋”ฉ ๋œ๋‹ค. ์‹ฌ์ง€์–ด ๋‚ด๋ถ€ํ•จ์ˆ˜, ๋ฉ”์†Œ๋“œ์˜ ๋‚ด๋ถ€ํ•จ์ˆ˜, ์ฝœ๋ฐฑํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ๋„ ์ „์—ญ๊ฐ์ฒด์— ๋ฐ”์ธ๋”ฉ๋œ๋‹ค. 1-1. apply/call/bind ํ˜ธ์ถœ : ์›ํ•˜๋Š” this์— ๋ฐ”์ธ๋”ฉํ•˜์—ฌ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  2. ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ : ๋ฉ”์†Œ๋“œ๋ฅผ ์†Œ์œ ํ•œ ๊ฐ์ฒด, ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ๊ฐ์ฒด์— ๋ฐ”์ธ๋”ฉ๋œ๋‹ค.
  3. ์ƒ์„ฑ์ž ํ•จ์ˆ˜ ํ˜ธ์ถœ :
    1. ๋นˆ ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ this ๋ฐ”์ธ๋”ฉ
    2. this๋ฅผ ํ†ตํ•œ ํ”„๋กœํผํ‹ฐ ์ƒ์„ฑ
    3. ์ƒ์„ฑ๋œ ๊ฐ์ฒด ๋ฐ˜ํ™˜
var foo = function () {    console.dir(this);};
// 1. ํ•จ์ˆ˜ ํ˜ธ์ถœfoo(); // window// window.foo();
// 2. ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœvar obj = {foo: foo};obj.foo(); // obj
// 3. ์ƒ์„ฑ์ž ํ•จ์ˆ˜ ํ˜ธ์ถœvar instance = new foo(); // instance
// 4. apply/call/bind ํ˜ธ์ถœvar bar = {name: 'bar'};foo.call(bar); // barfoo.apply(bar); // barfoo.bind(bar)(); // bar

OOP#

OOP์˜ ๊ธฐ๋ณธ ์ปจ์…‰์€ ์‹ค ์„ธ๊ณ„์˜ ์ผ๋“ค์„ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ชจ๋ธ๋งํ•˜๊ณ , ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์–ด๋ ค์šธ ์ผ๋“ค์„ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์šฉ์–ด ์ •๋ฆฌ

๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์‹ธ๋‹ค : encapsulate ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๊ธฐ ์œ„ํ•œ ์ด๋ฆ„ : namespace ์ถ”์ƒํ™” : ์ค‘์š”ํ•œ ๊ฒƒ๋“ค๋งŒ ๋ฝ‘์•„์„œ ๋‹จ์ˆœํ•œ ๋ชจ๋ธ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—…์ธ์Šคํ„ด์Šค : ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ๋งŒ๋“ค์–ด์ง„ ์‹ค์ œ ๊ฐ์ฒด (ํด๋ž˜์Šค๋Š” ์ฒญ์‚ฌ์ง„, ๋„๋ฉด, ํ‹€. ์ธ์Šคํ„ด์Šค๋Š” ์ฐ์–ด์ ธ ๋‚˜์˜จ ๊ฒฐ๊ณผ๋ฌผ) ๋‹คํ˜•์„ฑ : polymorphism. ์—ฌ๋Ÿฌ ๊ฐ์ฒด ํƒ€์ž…์— ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ

์ƒ์„ฑ์ž์™€ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค#

  1. ์ผ๋ฐ˜์ ์ธ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ์ฒด ์ƒ์„ฑ: ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๊ตณ์ด ๋นˆ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ ๋‚ด์šฉ์„ ์ฑ„์›Œ์„œ ๋ฆฌํ„ดํ•ด์•ผ ํ•˜๋Š”๊ฐ€?
function createNewPerson(name) {    var obj = {};    obj.name = name;    obj.greeting = function () {        alert("Hi! I'm " + this.name + '.');    };    return obj;}
var salva = createNewPerson('Salva');salva.name;salva.greeting();
  1. ์ƒ์„ฑ์ž ํ•จ์ˆ˜

new ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๋™์ž‘ํ•œ๋‹ค.

function Person(name) {    // this = {};  (๋นˆ ๊ฐ์ฒด๊ฐ€ ์•”์‹œ์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ง)
    // ์ƒˆ๋กœ์šด ํ”„๋กœํผํ‹ฐ๋ฅผ this์— ์ถ”๊ฐ€ํ•จ    this.name = name;    this.greeting = function () {        alert(`Hi! I'm ${this.name}.`);    };
    // return this;  (this๊ฐ€ ์•”์‹œ์ ์œผ๋กœ ๋ฐ˜ํ™˜๋จ)}
var salva = new Person('Salva');salva.name;salva.greeting();

๊ฐ๊ฐ์€ ์„œ๋กœ ๋‹ค๋ฅธ namespace์— ์ €์žฅ๋˜์–ด์žˆ๋‹ค. ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์†Œ๋“œ๋“ค์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, person1 ๋˜๋Š” person2๋กœ๋ถ€ํ„ฐ ํ˜ธ์ถœํ•˜์—ฌ์•ผ ํ•œ๋‹ค. ๋‘ ๊ฐ์ฒด์˜ ๊ธฐ๋Šฅ์€ ๋”ฐ๋กœ ํŒจํ‚ค์ง•๋˜์–ด์„œ๋กœ ์ถฉ๋Œํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‘ Person ๊ฐ์ฒด๋Š” ๊ฐ๊ฐ ๊ณ ์œ ์˜ name ํ”„๋กœํผํ‹ฐ์™€ greeting() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋‘˜์ด ์ƒ์„ฑ๋  ๋•Œ ๋ถ€์—ฌ๋ฐ›์€ ์ž์‹ ์˜ name ๊ฐ’์„์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์— ์ฃผ๋ชฉํ•˜์ž. ์ด๊ฒƒ์ด this๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋งค์šฐ ์ค‘์š”ํ•œ ์ด์œ  ์ค‘ ํ•˜๋‚˜์ด๋‹ค. ๊ฐ์ฒด๋“ค์€ ๋‹ค๋ฅธ ๊ฐ’์ด ์•„๋‹ˆ๋ผ, ๊ทธ๋“ค์ด ๊ฐ€์ง„ ๊ณ ์œ ์˜ ๊ฐ’์„ ์‚ฌ์šฉํ•œ๋‹ค.

๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด๊ณผ ์ƒ์„ฑ์ž ํ•จ์ˆ˜ ๋ฐฉ์‹์˜ ์ฐจ์ด#

// ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด ๋ฐฉ์‹var foo = {    name: 'foo',    gender: 'male',};
console.dir(foo);
// ์ƒ์„ฑ์ž ํ•จ์ˆ˜ ๋ฐฉ์‹function Person(name, gender) {    this.name = name;    this.gender = gender;}
var me = new Person('Lee', 'male');console.dir(me);
var you = new Person('Kim', 'female');console.dir(you);

๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด ๋ฐฉ์‹๊ณผ ์ƒ์„ฑ์ž ํ•จ์ˆ˜ ๋ฐฉ์‹์˜ ์ฐจ์ด๋Š” ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด([[Prototype]])์—์žˆ๋‹ค.

๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ, ์ƒ์„ฑ๋œ ๊ฐ์ฒด์˜ ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด๋Š” Object.prototype์ด๋‹ค.

์ƒ์„ฑ์ž ํ•จ์ˆ˜ ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ, ์ƒ์„ฑ๋œ ๊ฐ์ฒด์˜ ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด๋Š” Person.prototype์ด๋‹ค

์ฐธ๊ณ ์ž๋ฃŒ#

JavaScript ๊ฐ์ฒด ์†Œ๊ฐœ ํ•จ์ˆ˜ ํ˜ธ์ถœ ๋ฐฉ์‹์— ์˜ํ•ด ๊ฒฐ์ •๋˜๋Š” this JavaScript: What is the meaning of this?

ยท ์•ฝ 4๋ถ„

Nuxt.js์˜ View๋Š” app template๊ณผ Layout, Page๋กœ ๊ตฌ์„ฑ๋œ๋‹ค. SEO ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ดํŽ˜์ด์ง€ ๋ณ„๋กœ meta ํƒœ๊ทธ์™€ head ์„น์…˜์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

Composition of a View in Nuxt.js

Pages#

๋ชจ๋“  ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ทฐ ์ปดํฌ๋„ŒํŠธ์ด๋‹ค. ํ•˜์ง€๋งŒ Nuxt.js๋Š” ์‰ฝ๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋„๋กํŠน๋ณ„ํ•œ ์†์„ฑ๋“ค๊ณผ ํ•จ์ˆ˜๋“ค์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

๋ชจ๋“  ํŽ˜์ด์ง€ ์†์„ฑ์„ ๋ณด๋ ค๋ฉด Directory Structure๋ฅผ์ฐธ๊ณ 

Layouts#

๋ ˆ์ด์•„์›ƒ์€ Nuxt.js ์•ฑ์˜ Look & Feel์„ ๋ณ€๊ฒฝํ•  ๋•Œ ์œ ์šฉํ•˜๋‹ค. (์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์ด๋“œ๋ฐ”)

๋ ˆ์ด์•„์›ƒ์€ layouts ๋””๋ ‰ํ† ๋ฆฌ์— ์ถ”๊ฐ€ํ•œ๋‹ค.

default.vue ํŒŒ์ผ์€ ๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ์„ ์ •์˜ํ•œ๋‹ค.

layouts/default.vue
<template>    <Nuxt /></template>

๋ ˆ์ด์•„์›ƒ์—๋Š” <Nuxt /> ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐ˜๋“œ์‹œ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ ์™ธ์˜ ์ปค์Šคํ…€ ๋ ˆ์ด์•„์›ƒ์€ layouts ํด๋”์— ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์—ฌ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

layouts/blog.vue
<template>    <div>        <div>My blog navigation bar here</div>        <Nuxt />    </div></template>

์ด๋ ‡๊ฒŒ ์ƒ์„ฑํ•œ ๋ ˆ์ด์•„์›ƒ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, page ์ปดํฌ๋„ŒํŠธ์—์„œ layout ์†์„ฑ์— ํŒŒ์ผ ์ด๋ฆ„์„ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

pages/posts.vue
<template>    <!-- Your template --></template><script>    export default {        layout: 'blog',        // page component definitions    };</script>

Error Page#

์—๋Ÿฌ ํŽ˜์ด์ง€๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ(404, 500, ๋“ฑ๋“ฑ) ํ‘œ์‹œ๋˜๋Š” ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.

์—๋Ÿฌ ํŽ˜์ด์ง€๋Š” ๋ ˆ์ด์•„์›ƒ์ด์ง€๋งŒ, ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ํ‘œ์‹œ๋˜๋Š” ํŠน๋ณ„ํ•œ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ด์•ผํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ๋ ˆ์ด์•„์›ƒ ์ปดํฌ๋„ŒํŠธ์™€ ๋‹ฌ๋ฆฌ <Nuxt /> ์ปดํฌ๋„ŒํŠธ๋ฅผ ํฌํ•จํ•ด์„  ์•ˆ๋˜๊ณ  , layouts ๋””๋ ‰ํ† ๋ฆฌ์— ์œ„์น˜ํ•ด์•ผ ํ•œ๋‹ค. ๋‹ค๋ฅธ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์ฒ˜๋Ÿผ ๋‹ค๋ฅธ ๋ ˆ์ด์•„์›ƒ์„์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

layouts/error.vue
<template>    <div>        <h1 v-if="error.statusCode === 404">Page not found</h1>        <h1 v-else>An error occurred</h1>        <NuxtLink to="/">Home page</NuxtLink>    </div></template>
<script>    export default {        props: ['error'],        layout: 'error', // you can set a custom layout for the error page    };</script>

Document: App.html#

App ํ…œํ”Œ๋ฆฟ์€ head์™€ body์— ๋Œ€ํ•œ ์ปจํ…์ธ ์™€ ๋ณ€์ˆ˜๋ฅผ ์ฃผ์ž…ํ•˜๋Š” Nuxt.js ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜์‹ค์ œ HTML ํ”„๋ ˆ์ž„์„ ๋งŒ๋“œ๋Š”๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.

์ด ํŒŒ์ผ์€ ์ž๋™์ ์œผ๋กœ ์ƒ์„ฑ๋˜๊ณ , ์ผ๋ฐ˜์ ์œผ๋กœ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ๊ฑฐ์˜ ์—†๋‹ค.

ํ•œ ๊ฐ€์ง€ ์‚ฌ์šฉ ์˜ˆ์‹œ๋Š” IE์— ๋Œ€ํ•œ ์กฐ๊ฑด๋ถ€ CSS ๋Œ€์‘์ด๋‹ค.

app.html
<!DOCTYPE html><!--[if IE 9]><html class="lt-ie9 ie9" {{ HTML_ATTRS }}><![endif]--><!--[if (gt IE 9)|!(IE)]><!--><html {{ HTML_ATTRS }}><!--<![endif]-->    <head {{ HEAD_ATTRS }}>        {{ HEAD }}    </head>    <body {{ BODY_ATTRS }}>        {{ APP }}    </body></html>

์ฐธ๊ณ ์ž๋ฃŒ#

Nuxt.js - Concept/Views

ํƒœ๊ทธ:

ยท ์•ฝ 1๋ถ„

Nuxt.js ๊ณต์‹ ๋ฌธ์„œ์˜ Rendering๊ณผ Deployment Target ๋ถ€๋ถ„์„ ์ฝ์œผ๋ฉด์„œ nuxt.config.js์˜ ssr: 'true'์™€ target: 'server'์˜ ์ฐจ์ด๊ฐ€ ๋ฌด์—‡์ธ์ง€ ํ—ท๊ฐˆ๋ ธ๋‹ค.

static site๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” target: 'static'๊ณผ ssr: 'true'๋ฅผ ํ™œ์„ฑํ™” ํ•ด์•ผํ•œ๋‹ค. mode: 'universal'๋Š” ๊ฐ™์€ ๊ธฐ๋Šฅ์ด์ง€๋งŒ, deprecated ๋˜์—ˆ๋‹ค.

ssr: 'true'์ด ๊ธฐ๋ณธ๊ฐ’์ด๋‹ค.

ssr: 'false'๋Š” mode: 'spa'์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์ด์ง€๋งŒ deprecated ๋˜์—ˆ๋‹ค. ssr: 'false'๋Š” target: 'static'๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

Nuxt.js์—๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ช…๋ น์–ด๊ฐ€ ์žˆ๋Š”๋ฐ, target ๊ฐ’์— ๋”ฐ๋ผ์„œ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค.

์•„๋ž˜์˜ ํ‘œ๊ฐ€ ์†์„ฑ ๊ฐ’์— ๋”ฐ๋ฅธ ๋ช…๋ น์–ด์— ๋Œ€ํ•œ ๊ธฐ๋Œ€ ๋™์ž‘์„ ์ž˜ ์„ค๋ช…ํ•ด์ค€๋‹ค.

Available commands and rendering

์ฐธ๊ณ ์ž๋ฃŒ#

Demystify Nuxtสผs target, mode, and ssr properties Going Full Static

ยท ์•ฝ 2๋ถ„

git-config - conditional includes

gitconfig์˜ conditional includes๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋กœ์ปฌ ์ปดํ“จํ„ฐ์˜ ๋””๋ ‰ํ† ๋ฆฌ๋ณ„๋กœ gitconfig ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

working-directory  โ”œโ”€โ”€ company # ํšŒ์‚ฌ ํ”„๋กœ์ ํŠธ  โ””โ”€โ”€ github # ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ

ํšŒ์‚ฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ํšŒ์‚ฌ ๋ฉ”์ผ์ธ me@company.com ๋ฅผ git user.email๋กœ ์‚ฌ์šฉํ•˜๋ ค๊ณ ํ•˜๊ณ , ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ์—๋Š” me@gmail.com ์„ git user.email๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๊ณ  ํ•˜์ž.

์ „์—ญ ์„ค์ •์œผ๋กœ๋Š” ๊ฐœ์ธ ๊ณ„์ •์„ ์„ค์ •ํ•œ๋‹ค.

git config --global user.name megit config --global user.email me@gmail.com

๊ทธ๋ฆฌ๊ณ  ~/.gitconfig ์„ ์—ด์–ด์„œ user ์„น์…˜ ์•„๋ž˜์— includeIf ์„น์…˜์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

.gitconfig

[user]    name = me    email = me@gmail.com
[includeIf "gitdir:~/working-directory/company/"]    path = ~/working-directory/company/company.inc

gitdir: ์€ glob ํŒจํ„ด์„ ๋”ฐ๋ฅด๋Š” ๋ฌธ์ž์—ด๋กœ, git directory ๊ฐ€ ํ•ด๋‹น ํŒจํ„ด๊ณผ ์ผ์น˜ํ•˜๋ฉด, path์— ์žˆ๋Š” ํŒŒ์ผ์„ include ํ•œ๋‹ค.

gitconfig ํŒŒ์ผ์€ ์•„๋ž˜์˜ ์„ค์ •์ด ์œ„์˜ ์„ค์ •์„ ๋ฎ์–ด์“ฐ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๊ฒฝ๋กœ์˜ ํŒŒ์ผ์— user ์„ค์ •์„ ํ•ด๋‘๋ฉด, user ์„ค์ •์ด ๋ฎ์–ด์“ฐ์—ฌ ํ•ด๋‹น ์„ค์ •์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.

~/working-directory/company/company.inc

[user]    email = me@company.com

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•œ ๋’ค์— ~/working-directory/company/ ํ•˜์œ„์— ์ƒˆ๋กœ์šด git ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ์ƒ์„ฑํ•˜๋ฉด, git ์ด๋ฉ”์ผ์ด ํšŒ์‚ฌ ๊ณ„์ •์ธ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

conditional-include-gitconfig-example

์œ ์˜ํ•  ์ ์€ git์ด ์„ค์ •๋œ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

ยท ์•ฝ 1๋ถ„

yarn run vs npm run#

yarn run [script] [\<args>]#

  • scripts ์— ๋“ฑ๋ก๋œ ๋ช…๋ น์–ด ์‹คํ–‰
  • node_modules/.bin/์— ์œ„์น˜ํ•œ ์‹คํ–‰ ๊ฐ€๋Šฅ ํŒŒ์ผ(executable) ์‹คํ–‰
  • run ์€ ์ƒ๋žต ๊ฐ€๋Šฅํ•˜๋‹ค.

npm run-script \<command>#

  • npm run <command> ๋กœ alias
  • scripts ์— ๋“ฑ๋ก๋œ ๋ช…๋ น์–ด ์‹คํ–‰

npm exec (npx)#

  1. node_modules/.bin ์— ์œ„์น˜ํ•œ ์‹คํ–‰ ๊ฐ€๋Šฅ ํŒŒ์ผ(executable) ์‹คํ–‰
  2. remote npm package ์‹คํ–‰

nuxt ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ#

yarn create nuxt-app# npx create-nuxt-app

create-nuxt-app ์„ ์‚ฌ์šฉํ•˜๋ฉด cli๋ฅผ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ์— ๊ด€ํ•œ ์„ค์ •์— ๋Œ€ํ•œ ์งˆ์˜๋ฅผ ํ•œ๋‹ค .

image-20210707155912520

ยท ์•ฝ 4๋ถ„

git-flow#

git-flow ๋Š” Vincent Driessen์˜ ๋ธŒ๋žœ์นญ ๋ชจ๋ธ์„ ์ ์šฉํ•˜์—ฌ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ž‘์—…ํ•  ์ˆ˜์žˆ๋„๋ก ํ•˜๋Š” git์˜ ํ™•์žฅ์ด๋‹ค. ๋” ์•Œ์•„๋ณด๊ธฐ

git-flow vs git-flow-avh#

git-flow ์„ค์น˜ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ณด๋ฉด, git-flow-avh๋ฅผ ์„ค์น˜ํ•˜๋ผ๋Š” ๊ฐ€์ด๋“œ๊ฐ€ ๋‚˜์˜ค๋Š”๋ฐ, git-flow ์ €์žฅ์†Œ๊ฐ€ ์žˆ๊ณ , ์ด๊ฒŒ์›๋ณธ์ธ ๊ฒƒ ๊ฐ™์•„์„œ ์•ฝ๊ฐ„์˜ ํ˜ผ๋ž€์ด ์žˆ์—ˆ๋‹ค.

๋‹คํ–‰ํžˆ git-flow-avh์˜ ๊นƒํ—™ ์œ„ํ‚ค์˜ Home๊ณผ FAQ์— ๊ฐ„๋žตํ•œ ์„ค๋ช…์ด์žˆ๋‹ค.

git-flow์—์„œ hooks์™€ filter ๊ตฌํ˜„์ด ํ•„์š”ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์›๋ณธ git-flow์— ๋Œ€ํ•œ ํŒจ์น˜๋ฅผ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. 5๊ฐœ์›”์ด ์ง€๋‚˜๋„, ํŒจ์น˜๊ฐ€ ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜๊ณ  ์šฐ๋ฆฌ๋Š” AVH Edition์„ ๋งŒ๋“œ๋Š” ์ผ์— ์ง‘์ค‘ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

AVH๋Š” A Virtual Home์ด๋ผ๋Š” ์ž‘์„ฑ์ž์˜ ๊ฐœ์ธ ์›น์‚ฌ์ดํŠธ ์ด๋ฆ„์—์„œ ๋”ฐ์™”๋‹ค๊ณ  ํ•œ๋‹ค. ์ฐธ๊ณ 

AVH Edition์ด git-flow์˜ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ๋“ค์„ ํฌํ•จํ•˜๋ฉด์„œ hooks์™€ filter ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  , cheatsheet ๋ฅผ ํฌํ•จํ•ด์„œ AVH Edition์œผ๋กœ ๊ฐ€์ด๋“œํ•˜๋Š” ๋ฌธ์„œ๊ฐ€ ๋งŽ์•„์„œ AVH Edition์œผ๋กœ ์„ค์น˜๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค.

์„ค์น˜ํ•˜๊ธฐ#

Installation ์—์„œ ์šด์˜์ฒด์ œ ๋ณ„ ์„ค์น˜ ๊ฐ€์ด๋“œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

MacOS ๊ธฐ์ค€์œผ๋กœ๋Š” brew๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์น˜ํ•œ๋‹ค.

brew install git-flow-avh

1.7.0 ๋ฒ„์ „์—์„œ git flow init ์‹œ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”๋ฐ, (์ด์Šˆ) ~/.gitflow_export ์— FLAGS_GET_OPT_CMD ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฒƒ์ด ์›์ธ์ด์—ˆ๋‹ค. ๋ฒ„์ „์ด ์—…๋ฐ์ดํŠธ ๋˜๋ฉด์„œ ์ด๋ฅผ ์‚ญ์ œํ•ด์•ผ ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค. ์ฐธ๊ณ 

์‹œ์ž‘ํ•˜๊ธฐ#

๋จผ์ €, git ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

git init hello-git-flow

git ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋กœ ์ด๋™ํ•œ ํ›„, git flow๋ฅผ ์‹œ์ž‘ํ•œ๋‹ค.

cd hello-git-flowgit flow init

CLI์˜ ์งˆ์˜์— ๋”ฐ๋ผ ์ง„ํ–‰ํ•˜์—ฌ ๋ธŒ๋žœ์น˜ ์ปจ๋ฒค์…˜์„ ์ •ํ•œ๋‹ค.

๋ช…๋ น์–ด#

git-flow ๋ช…๋ น์–ด ํ•œ์งค ์š”์•ฝ

git-flow commands

๋Š๋‚€์ #

ํ‰์†Œ ํ•˜๋‚˜์˜ Origin ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๋‘๊ณ , main, develop, feature/, fix/ ... ์œผ๋กœ ๋ธŒ๋žœ์นญํ•œ ํ›„, Pull Request -> Merge -> Pull (develop) ๋ฐฉ์‹์˜ workflow๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ git flow์˜ finish ๋ช…๋ น์–ด๊ฐ€ ๋กœ์ปฌ์—์„œ ๋จธ์ง€๋˜๋Š” ๋ฐฉ์‹์ด๋ผ, Pull Request์™€ ํ•จ๊ป˜ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ ์ ˆํ•œ์ง€ ๊ณ ๋ฏผ์ด ๋“ค์—ˆ๋‹ค.

๋‹คํ–‰ํžˆ, Stack Overflow์— ๋น„์Šทํ•œ ๊ณ ๋ฏผ์ด์žˆ์—ˆ๋‹ค. ๊ธฐ์กด ์›Œํฌํ”Œ๋กœ์šฐ ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, finish ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , publish -> PR -> Merge -> Pull ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค. ์ถ”๊ฐ€์ ์œผ๋กœ git-flow-avh์˜ hooks ๊ธฐ๋Šฅ์„ํ™œ์šฉํ•ด PR ์ž‘์„ฑ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜๋Š”, ํ•˜๋‚˜์˜ Upstream ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๋‘๊ณ  forkํ•˜์—ฌ ํ˜‘์—… ๋Œ€์ƒ์ž์˜ ๊ฐœ์ธ Origin ๋ ˆํฌ์ง€ํ† ๋ฆฌ์—์„œ PRํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๋Š” ๋ฐฉ์‹๋„ ์žˆ๋‹ค. ์ฐธ๊ณ 

์ด๋Ÿฌํ•œ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด, ๊ฐ์ž์˜ Origin repo์—์„œ ๋ถ€๋‹ด์—†์ด ๋‹ค์–‘ํ•œ ์‹คํ—˜๋“ค์„ ํ•ด๋ณผ ์ˆ˜์žˆ๋Š” ์žฅ์ ๋„ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.

github-flow-repository-structure

Reference#

git-flow cheatsheet

์šฐ๋ฆฐ Git-flow๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์š”

ํƒœ๊ทธ:

ยท ์•ฝ 2๋ถ„

๋นˆ ๋ธŒ๋žœ์น˜ ์ƒ์„ฑํ•˜๊ธฐ#

git checkout --orphan empty-branch
git rm -rf .
# ๋นˆ ์ปค๋ฐ‹ ์ƒ์„ฑํ•˜๊ธฐgit commit --allow-empty -m "Initial commit"

Git Pull ์ „๋žต#

git pull ์„ ๋ณ„๋„ ์˜ต์…˜ ์—†์ด ์‹คํ–‰ํ–ˆ์„ ๋•Œ ๊ฒฝ๊ณ  ๋ฌธ๊ตฌ๊ฐ€ ๋‚˜ํƒ€๋‚ฌ๋‹ค.

warning: Pulling without specifying how to reconcile divergent branches isdiscouraged. You can squelch this message by running one of the followingcommands sometime before your next pull:
git config pull.rebase false  # merge (the default strategy)git config pull.rebase true   # rebasegit config pull.ff only       # fast-forward only
You can replace "git config" with "git config --global" to set a defaultpreference for all repositories. You can also pass --rebase, --no-rebase,or --ff-only on the command line to override the configured default perinvocation.
  • default (merge) : ๋กœ์ปฌ ๋ธŒ๋žœ์น˜์™€ ๋ฆฌ๋ชจํŠธ ๋ธŒ๋žœ์น˜์˜ Head๊ฐ€ ๋‹ค๋ฅธ ์œ„์น˜์— ์žˆ์„ ๋•Œ, pull ๋ฐ›์œผ๋ฉด, Merge ์ปค๋ฐ‹์„ ์ƒ์„ฑํ•จ.

  • rebase : ๋กœ์ปฌ ๋ธŒ๋žœ์น˜์™€ ๋ฆฌ๋ชจํŠธ ๋ธŒ๋žœ์น˜์˜ Head๊ฐ€ ๋‹ค๋ฅธ ์œ„์น˜์— ์žˆ์„ ๋•Œ, pull ๋ฐ›์œผ๋ฉด, ๋ฆฌ๋ชจํŠธ ๋ธŒ๋žœ์น˜๋ฅผ rebase ํ•˜์—ฌ history๋ฅผ ์ •๋ฆฌํ•จ.

  • fast-forward only : fast-forward ๊ด€๊ณ„์— ์žˆ์„ ๋•Œ๋งŒ pull ์„ ํ—ˆ์šฉํ•จ.

2021-02-26-210226-image-0

Git - git-pull Documentation

[GIT] Git pull ์ „๋žต (default, --ff -only, --rebase)

GitHub ํ™˜๊ฒฝ์—์„œ์˜ ์‹ค์ „ Git ๋ ˆ์‹œํ”ผ : NHN Cloud Meetup

Conventional Commit#

Conventional Commits

Git commit ๋ฉ”์„ธ์ง€ ๊ทœ์•ฝโ€Š-โ€ŠConventional Commits