๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ‘จ‍๐Ÿ’ป web.dev/log

Mixin์„ ์‚ฌ์šฉํ•ด๋ณด๋ฉฐ ๋Š๋‚€์ ๋“ค

by HandHand 2022. 7. 2.

๐Ÿ“Œ Mixin ์ด๋ž€?

๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ mixin ์€ ํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ๋“ค์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•ด์„œ

์ƒ์†์—†์ด ๋‹ค๋ฅธ ํด๋ž˜์Šค์— ํ–‰๋™์„ ๋”ํ•ด์ฃผ๋Š” ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
์ฃผ๋กœ ์„œ๋กœ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ์„ ๊ณต์œ ํ•˜๊ณ ์ž ํ• ๋•Œ ์‚ฌ์šฉํ•˜๋ฉฐ

์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

 

๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„์ธ๋งŒํผ React ์™€ Vue ๋“ฑ์˜ ๋‹ค์–‘ํ•œ FE ์•„ํ‚คํ…์ณ์—์„œ๋„

์ด ๊ฐœ๋…์„ ์ด์šฉํ•ด์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ œ์‹œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

1๏ธโƒฃ ES6 ์˜ class ๋ฅผ ์ด์šฉํ•œ mixin

Object.assign ์„ ์ด์šฉํ•œ ๋ฉ”์„œ๋“œ ๋ณต์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ mixin ํŒจํ„ด์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const GreetingMixin = {
    sayHi() {
        alert('Hello')
    }
    sayBye() {
        alert('Bye')
    }
}

class User {
    constructor(name) {
        this.name = name
    }
}

Object.assign(User.prototype, GreetingMixin);

new User('JeongHyeon').sayHi();

2๏ธโƒฃ Vue ์—์„œ mixin ์„ ์ด์šฉํ•œ ์ปดํฌ๋„ŒํŠธ ๊ตฌ์„ฑ

Vue ์—์„œ๋Š” mixin ์„ ์ด์šฉํ•œ ์ปดํฌ๋„ŒํŠธ ๋กœ์ง ์žฌ์‚ฌ์šฉ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

Vue 3 ์—์„œ๋„ ์—ฌ์ „ํžˆ mixin ์„ ์ง€์›ํ•˜์ง€๋งŒ ๋ช‡๊ฐ€์ง€ ๋ฌธ์ œ์ ๋“ค ๋•Œ๋ฌธ์—

Composition API ๋ผ๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.


Vue Mixin ์ปดํฌ๋„ŒํŠธ ์ •์˜

import { Vue, Component } from 'vue-property-decorator'

@Component({})
export default class GreetingMixin extends Vue {
    // ... APIs
}

์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค ๐Ÿ˜€

import { Mixins } from 'vue-property-decorator'

import GreetingMixin from './GreetingMixin.ts'

export default class Example extends Mixins(GreetingMixin) {
    // ...
}

3๏ธโƒฃ React 15 ์—์„œ mixin ์„ ์ด์šฉํ•œ ์ปดํฌ๋„ŒํŠธ ๊ตฌ์„ฑ

ํ˜„์žฌ๋Š” deprecated ๋œ ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ํ•œ๋•Œ React ์—์„œ๋Š” mixin ์„ ์ด์šฉํ•ด์„œ

์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

import React from 'react';

const GreetingMixin = {
  sayHi() {
        /** */
  }
};

const MyComponent = React.createClass({
  mixins: [GreetingMixin],
  handleClick() {
    this.sayHi();
  },
  render() {
    return (
      <button onClick={this.handleClick}>Say Hi!</button>
    );
  }
});

export default MyComponent;

 

๐Ÿ“Œ Mixin ์€ ๋งŒ๋Šฅ์ผ๊นŒ?

์–ด๋Š ํŒจํ„ด์ด๋‚˜ ๊ทธ๋ ‡๋“ฏ์ด, ๋ชจ๋“  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋‹จ ํ•˜๋‚˜์˜ ์€์ƒ‰ ํƒ„ํ™˜์€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

mixin ๋„ ๊ฒฐ๊ตญ์—๋Š” ํ•œ๊ณ„์ ์ด ์žˆ๊ณ  ์ด๋ฅผ ์ž˜ ์•Œ๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์ œ๊ฐ€ ์‹ค์ œ ์—…๋ฌด์—์„œ mixin ์„ ์‚ฌ์šฉํ•ด๋ณด๋ฉฐ ๋Š๋‚€ ํ•œ๊ณ„์ ๋“ค๊ณผ React ๋ธ”๋กœ๊ทธ ์— ๊ฒŒ์žฌ๋œ ๊ธ€์„ ์ฝ์–ด๋ณด๋ฉฐ

๊ณต๊ฐํ•œ ๋ถ€๋ถ„์„ ์ •๋ฆฌํ•œ ๋‚ด์šฉ๋“ค์ž…๋‹ˆ๋‹ค.

 

1๏ธโƒฃ Mixin ๋‚ด๋ถ€ ๋ฉ”์„œ๋“œ์˜ ์ค‘๋ณต ์œ„ํ—˜์ด ์žˆ๋‹ค.

๊ทœ๋ชจ๊ฐ€ ์ž‘์€ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ํฐ ์˜ํ–ฅ์ด ์—†์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ์—ฌ๋Ÿฌ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•จ๊ป˜ ์ง„ํ–‰ํ•˜๋Š”

ํ”„๋กœ์ ํŠธ์—์„œ๋Š” mixin ๋‚ด๋ถ€์—์„œ ์ •์˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.


์˜ˆ๋ฅผ ๋“ค์–ด, ํŒ€๋งˆ๋‹ค ๋‹ค๋ฅด์ง€๋งŒ ์ผ๋ฐ˜์ ์œผ๋กœ ํŠน์ • ์•ก์…˜์— ๋Œ€์‘ํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋“ค ๊ฐ™์€ ๊ฒฝ์šฐ

handleClick ํ˜น์€ onClick ๊ณผ ๊ฐ™์ด ์ผ๋ฐ˜์ ์ธ ํ˜•ํƒœ์˜ ๋ฉ”์„œ๋“œ๋“ค์ด

๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ์ •์˜๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ์‹ฌ์‹ฌ์น˜ ์•Š๊ฒŒ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ,

๋งŒ์•ฝ mixin ๋‚ด๋ถ€์—์„œ ๋™์ผํ•œ ์ด๋ฆ„์„ ๊ฐ€์ง„ ํ•จ์ˆ˜๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•œ๋‹ค๋ฉด ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๋ฌผ๋ก  ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ mixin ๋„ค์ด๋ฐ ๊ทœ์น™์„ ํ†ตํ•ด ํ•ด๊ฒฐํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ,

๋ชจ๋“  ๊ตฌ์„ฑ์›์ด ํ•ด๋‹น ๋ฃฐ์„ ๋”ฐ๋ผ์•ผ ํ•˜๊ณ  ์™ธ๋ถ€ ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋„ ๋™์ผํ•œ ์ปจ๋ฒค์…˜์ด ์ ์šฉ๋˜์–ด์žˆ๋‹ค๋Š”
๋ณด์žฅ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— 100% ์‹ ๋ขฐ ๊ฐ€๋Šฅํ•œ ๋ฐฉ๋ฒ•์€ ์•„๋‹™๋‹ˆ๋‹ค.

โญ๏ธ Mixin naming convention ์˜ˆ์‹œ

์ด์ „์— Vue ๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœํ•  ๋•Œ์—๋Š” ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ ์‚ฌ์šฉ๋˜๋Š” mixin ์—์„œ

๊ณต๊ฐœ๋˜๋Š” API์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด prefix ๋ฅผ ๋ถ€์—ฌํ•˜๋Š” ๋„ค์ด๋ฐ ๊ทœ์น™์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

import { Vue, Component } from 'vue-property-decorator'

@Component({})
export default class GreetingMixin extends Vue {
    private name = 'owen'

    public GreetingMixin_sayHi() {
        console.log(`say hi to ${this.name}!`)
    }
}

2๏ธโƒฃ Mixin ๋ผ๋ฆฌ ์ƒํ˜ธ ๊ฐ„์˜ ํ™•์žฅ์ด ํ•„์š”ํ•  ๊ฒฝ์šฐ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค.

์ƒํ˜ธ ๋…๋ฆฝ์ ์ธ mixin ์„ ์„ค๊ณ„ํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์‹ค์ œ ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋‹ค ๋ณด๋ฉด

๊ต‰์žฅํžˆ ์ด์ƒ์ ์ด๊ณ  ๋งŽ์€ ๋…ธ๋ ฅ์„ ํ•„์š”๋กœ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋Š๋‚„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ€๋”์€ ์„œ๋น„์Šค ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ผ ์–ด์ฉ” ์ˆ˜ ์—†์ด ๊ธฐ์กด์— ์กด์žฌํ•˜๋Š” ํŠน์ • mixin ์— ์˜์กดํ•˜๋Š”

๋‹ค๋ฅธ mixin ์˜ ์„ค๊ณ„๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๋‘ mixin ์‚ฌ์ด์˜ ์˜์กด์„ฑ์ด ์ฝ”๋“œ์˜ ์ „๋ฐ˜์ ์ธ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋‚ฎ์ถ”๊ณ 

๋ณต์žกํ•œ mixin ๊ตฌ์กฐ ๋•Œ๋ฌธ์— ์ƒˆ๋กœ์šด ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•ด๋‹น ์ฝ”๋“œ๋กœ ์ž‘์—…ํ•  ๋•Œ ์–ด๋ ค์›€์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์–ด๋–ค ๋ณ€ํ™”๊ฐ€ side effect ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ์ง€ ์˜ˆ์ธก์ด ์•ˆ๋˜๊ธฐ ๋•Œ๋ฌธ์ด์ฃ .

3๏ธโƒฃ Mixin ์ด ๋งŽ์„์ˆ˜๋ก ๋ถ„์„์ด ์–ด๋ ต๊ณ  ์ฝ”๋“œ ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.

๋ฐ”๋กœ ์œ„ 2๋ฒˆ ์—์„œ ์–ธ๊ธ‰ํ•œ ๋ฌธ์ œ์™€ ์—ฐ๊ด€๋˜๋Š” ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

mixin ์—์„œ ์ •์˜ํ•œ ๋ฉ”์„œ๋“œ๋“ค์€ ํ•ด๋‹น mixin ์„ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ์˜ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๋ฉฐ ์ˆ˜๋งŽ์€ ๊ณตํ†ต ๋กœ์ง๋“ค์ด mixin ์œผ๋กœ ๊ด€๋ฆฌ๋˜๋ฉด

์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ์–ด๋””์„œ๋ถ€ํ„ฐ ์œ ๋ž˜๋œ ๊ฒƒ์ด๊ณ  ๋งฅ๋ฝ ํŒŒ์•…์ด ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

mixin ๋„ค์ด๋ฐ ๊ทœ์น™์„ ํ†ตํ•ด ๋Œ€๋žต์ ์ธ ๋งฅ๋ฝ์€ ํŒŒ์•… ๊ฐ€๋Šฅํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ

์„œ๋กœ ๋ณต์žกํ•˜๊ฒŒ ์–ฝํ˜€์žˆ๋Š” mixin ๋“ค์€ ์˜์กด์„ฑ์— ์˜ํ•ด ๋ฆฌํŒฉํ† ๋ง์„ ์–ด๋ ต๊ฒŒ ํ•˜๊ณ 

์„œ๋น„์Šค ๋กœ์ง ๋ถ„์„์ด ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.

 

๐Ÿ“Œ ๋” ์ž‘์€ ๋‹จ์œ„๋กœ ์ชผ๊ฐœ์ž!

โœ… ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ์™€ ์ปดํฌ๋„ŒํŠธ ํ•ฉ์„ฑ

React ์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๋ฌธ์ œ์ ์„ ์•Œ๊ณ  mixin ์„ ์ด์šฉํ•œ ์ปดํฌ๋„ŒํŠธ ํ™•์žฅ ์ง€์›์„ ์ค‘์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋Œ€์•ˆ์œผ๋กœ ์ƒํƒœ ๊ฐ’์„ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ(HOC) ๋‚˜ render prop ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜

์ปดํฌ๋„ŒํŠธ ํ•ฉ์„ฑ ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•๋“ค์„ ํ†ตํ•ด ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๋Š” ๊ฒƒ์„ ์•ˆ๋‚ดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

โœ… React Hook ์˜ ๋“ฑ์žฅ

React 16.8 ๋ถ€ํ„ฐ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์œผ๋กœ ๋“ฑ์žฅํ•œ hook ์€ ๊ธฐ์กด์— ํด๋ž˜์Šค ๊ธฐ๋ฐ˜์œผ๋กœ

์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌ์„ฑํ•˜๋˜ ๋ฐฉ์‹์—์„œ ๋ฒ—์–ด๋‚˜ ํ•จ์ˆ˜ํ˜•์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ •์˜ํ•˜๊ณ  ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ

์—ฌ๋Ÿฌ React ์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

useState, useEffect , useRef ์™€ ๊ฐ™์ด ๋นŒํŠธ์ธ์œผ๋กœ ์ œ๊ณต๋˜๋Š” hook ์ด์™ธ์—๋„

๊ฐœ๋ฐœ์ž๋Š” ์ž์‹ ๋งŒ์˜ hook ์„ ์ •์˜ํ•˜๊ณ  ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

โœ… Vue composition API

Vue 3.0 ์— ์ด๋ฅด๋Ÿฌ์„œ Vue ์ง„์˜์— ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ ํŒจ๋Ÿฌ๋‹ค์ž„์ด ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.

Composition API ๋Š” ๊ทœ๋ชจ๊ฐ€ ํฐ Vue ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋…ผ๋ฆฌ์  ๊ด€์‹ฌ์‚ฌ์˜ ๋‹จํŽธํ™”๋กœ ์ธํ•œ

๋ณต์žกํ•œ ์ปดํฌ๋„ŒํŠธ ๋ฐ ์œ ์ง€๋ณด์ˆ˜์„ฑ ์ €ํ•˜๋ผ๋Š” ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ œ์•ˆ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ์„œ ๋‹ค๋ฃจ์ง€ ์•Š์ง€๋งŒ, React Hook ๊ณผ ์œ ์‚ฌํ•œ ํ˜•ํƒœ๋กœ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋กœ์ง์„

ํ•จ์ˆ˜ ๋‹จ์œ„๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๐Ÿ“Œ ์ฐธ๊ณ ์ž๋ฃŒ

Mixins Are Dead. Long Live Composition
๋ฏน์Šค์ธ
Mixins Considered Harmful - React Blog
์†Œ๊ฐœ | Vue.js

๋ฐ˜์‘ํ˜•

๐Ÿ’ฌ ๋Œ“๊ธ€