Make Your Components Easier to Think About (์ปดํฌ๋ํธ์ ๋ํด ๋ ์ฝ๊ฒ ์๊ฐํ๊ธฐ)
๐ก์๋ณธ๊ธ : https://michaelnthiessen.com/make-your-components-easier-to-think-about
Make Your Components Easier to Think About
I hate thinking. Well, actually, I *love* thinking, but only when Iโm able to solve problems or make progress with it. But often our code gets in the way of this.
michaelnthiessen.com
๋๋ ์๊ฐํ๋๊ฑธ ์ซ์ดํ๋ค.
์ฌ์ค ์ ๋ ์๊ฐํ๋ ๊ฒ์ ์ข์ํ์ง๋ง, ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ฑฐ๋ ์ง์ ์ ์ด๋ฃฐ์ ์์๋๋ง ์๊ฐ์ ํฉ๋๋ค. ํ์ง๋ง ์ข ์ข ์ฝ๋๊ฐ ์ด๋ฅผ ๋ฐฉํดํ ๋๊ฐ ์์ต๋๋ค. ํ ์ํฌ์ ์ฐธ์์๊ฐ ์ฝ๋ ์ฝ๊ธฐ์ ๋ํด ๋งํ๋ฏ์ด โ๋ง์ฝ ํผ๋์ค๋ฝ๋ค๋ฉด ๋์ ์๋ชป์ด ์๋๋๋ค.โ ๋ค์์ ์ฝ๋๋ฅผ ๋ ์ฝ๊ฒ ์๊ฐํ์ฌ ํผ๋์ ์ค์ด๊ณ ์ค์ ์์ ์ ์๋ฃํ ์ ์๋ ๋ช ๊ฐ์ง ๋ฐฉ๋ฒ์ ๋๋ค.
๊ฐ๋ ์ฑ์ ์ํ ์ปดํฌ๋ํธ๋ฅผ ์ถ์ถํ๊ธฐ
์ฌ๋์ด ์ดํดํ๊ธฐ ์ฝ๋๋ก ์ปดํฌ๋ํธ๋ฅผ ์ต์ ํํ๋ ํ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์๋(intention)์ ๊ตฌํ(implementation)์ ๋ถ๋ฆฌํ๋ ๊ฒ์ ๋๋ค. ์ด๋ ์๋ก์ด ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด์ผ ํ๋ ๊ฐ์ฅ ์ค์ํ ์ด์ ์ค ํ๋์ด๋ฉฐ, ์ฝ๋์ ์ผ๋ถ๋ถ์ ํด๋น ์ฝ๋์ ๊ธฐ๋ฅ์ ์ค๋ช ํ๋ ์ฝ๋๋ก ๋์ฒดํ ์ ์์ต๋๋ค.
๋ค์์ ์ค์ ๊ธฐ๋ณธ์ ์ธ ์ปดํฌ๋ํธ์ ๋๋ค.
<template>
<div>
<p>{{ user.name }} ({{ user.age }})</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
const user = ref({
name: 'John Doe',
age: 28,
});
</script>
UserInfo ์ปดํฌ๋ํธ๋ฅผ ์ถ์ถํ๋ฉด ์ด ํ ์ค์ด ํ๋ ์ผ์ธ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ํ์ํ๋ ๊ฒ์ด ๋งค์ฐ ๋ช ํํด์ง๋๋ค.
<template>
<div>
<UserInfo :user="user" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import UserInfo from './UserInfo.vue';
const user = ref({
name: 'John Doe',
age: 28,
});
</script>
์ด ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค๊ธฐ ์ํด์๋ ๋ช ๊ฐ์ง ๋ณด์ผ๋ฌ ํ๋ ์ดํธ๋ฅผ ๊ตฌ์ฑํด์ผ ํฉ๋๋ค.
<template>
<p>{{ user.name }} ({{ user.age }})</p>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
user: Object,
});
</script>
๋ฆฌํฉํ ๋งํ ์ฝ๋์์๋ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ UserInfo ์ปดํฌ๋ํธ๋ก ์ถ์ถํ์ต๋๋ค. ์ด๋ ๊ฒ ๋ถ๋ฆฌํ๋ฉด ์ฝ๋๊ฐ ๋ชจ๋ํ๊ฐ ๋ฉ๋๋ค. ์ด์ ์ฐ๋ฆฌ ์ฝ๋๋ ์ข๋ ์ฝ๊ธฐ ์ฌ์์ง๋๋ค. ์ด๊ฒ์ ๊ฐ๋จํ ์์์ด๋ฉฐ, ์ด์ ์ ์ ์ต๋๋ค. ๋ ๊ธธ๊ณ ๋ณต์กํ ์ปดํฌ๋ํธ์์๋ ์ด๋ฌํ ๋ฐฉ์์ผ๋ก ์ปดํฌ๋ํธ๋ฅผ ์ถ์ถํ๋ฉด ํจ์ฌ ๋ ๋ง์ ์ด์ ์ ์ป์ ์ ์์ต๋๋ค. ์ด๊ฒ์ ๋ํ self-documenting code(์ค์ค๋ก ๋ฌธ์ํ๋ ์ฝ๋)๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ํ ์์์ด๊ธฐ๋ ํฉ๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก Clean Components Toolkit์ ํต์ฌ ๋ถ๋ถ์ ๋๋ค. ๋ชจ๋ ํจํด, ๊ธฐ์ ๋ฐ ์์น์ โ์ด๋ป๊ฒ ํ๋ฉด ์ฝ๋๋ฅผ ๋ ์ค๋ช ์ ์ผ๋ก ๋ง๋ค์ ์์๊น?โ๋ผ๋ ์ง๋ฌธ์ ๋์ง๋๋ค. ๋ง์ฝ ์ฝ๋๋ฅผ ๋ ์ค๋ช ์ ์ผ๋ก ๋ง๋๋ ํจํด์ ๋ฐ๋ฅด๊ณ ์๋ค๋ฉด ์ข์ ํจํด์ด ์๋ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค. ๊ทธ๋ฌ๋ self-documenting code(์ค์ค๋ก ๋ฌธ์ํ๋ ์ฝ๋)๋ ์ปดํฌ๋ํธ ์ด๋ฆ๋ฟ๋ง ์๋๋ผ ๋ ์ธ๋ถํํ ์ ์์ต๋๋ค.
์ข ๋ self-documenting code(์ค์ค๋ก ๋ฌธ์ํ๋ ์ฝ๋)๋ฅผ ์์ฑํ๊ธฐ
Vue ์ปดํฌ๋ํธ์๋ props, data, computed ์์ฑ, methods, ๋ผ์ด๋ธ์ฌ์ดํด hooks ๋ฑ๋ฑ์ ๋ํด ๋ช ํํ๊ณ ์๋ฏธ์๋ ์ด๋ฆ์ด ์์ด์ผ ํฉ๋๋ค. ๋ํ ํ ํ๋ฆฟ ์ฝ๋๊ฐ ์ ์ ํ ๋ค์ฌ์ฐ๊ธฐ, ์์(formatting), ๋๋ฌธ ์ฃผ์์ ์ฌ์ฉํ์ฌ ์ฒด๊ณ์ ์ด๊ณ ๊ฐ๋ ์ฑ์ด ์ข์์ผ ํฉ๋๋ค. ๋์ ์ฝ๋๊ฐ self-documenting(์ค์ค๋ก ๋ฌธ์ํ๋ ์ฝ๋) ๋์ด ์๋ค๋ฉด ํด๋น ์ฝ๋๊ฐ ์ํํ๋ ์์ ์ ์๋๋ฅผ ์๋ฆฌ๋๋ฐ ๋์๋ฉ๋๋ค. ํฐ๋ฌด๋์๋ ์์์ผ ์๋ ์์ง๋ง(๋๊ฐ ์ด๋ฐ ์ฝ๋๋ฅผ ์์ฑํ๊ฒ ์ด์?) ์ฝ๋์์ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๊ณ ์๋์ง ํ์ ํ๋ ค๋ฉด ์ฝ๊ฐ์ ๋ ธ๋ ฅ์ด ํ์ํ์ฃ .
<template>
<div>
<button @click="c">Increment</button>
<p>{{ a }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const a = ref(0);
function c() {
a.value++;
}
</script>
๋ง์ต๋๋ค. counter ์ ๋๋ค. ํ์ง๋ง ๋ค์ ์์๋ฅผ ๋ณด๋ฉด ๋ณด์๋ง์ ๋ฌด์์ธ์ง ์ฆ์ ์ ์ ์์ต๋๋ค.
<template>
<div>
<button @click="incrementCounter">Increment</button>
<p>{{ counterValue }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const counterValue = ref(0);
function incrementCounter() {
counterValue.value++;
}
</script>
๋ฆฌํฉํ ๋ง๋ ์ฝ๋์์๋ ๋ณ์์ ๋ฉ์๋์ ์ด๋ฆ์ ๋ ์ ์ค๋ช ํ ์ ์๋๋ก ๊ฐ์ ํ์ต๋๋ค. ์ด์ ์ด ์ปดํฌ๋ํธ๋ก ๋์์ฌ ๋๋ง๋ค ์๊ฐ๊ณผ ์ ์ ์ ์๋์ง๋ฅผ ๋ญ๋นํ ํ์๊ฐ ์์ต๋๋ค.
๋๋ฌด ๊ธด ์ปดํฌ๋ํธ ๋ถํ ํ๊ธฐ
์ปดํฌ๋ํธ๊ฐ ๋๋ฌด ๋ง์ ์ผ์ ํ๊ณ ๋๋ฌด ๊ธธ์ด์ง๋ฉด ์ดํดํ๊ณ ์ ์ง ๋ณด์ ๊ด๋ฆฌํ๊ธฐ๊ฐ ์ด๋ ค์ธ ์ ์์ต๋๋ค. ๊ธด ์ปดํฌ๋ํธ ์์น์ ๋ฐ๋ฅด๋ฉด ๊ธด ์ปดํฌ๋ํธ๋ฅผ ๋ ์๊ณ ์ง์ค๋ ์ปดํฌ๋ํธ๋ก ๋๋๋ฉด ๊ฐ๋ ์ฑ, ์ฌ์ฌ์ฉ์ฑ ๋ฐ ํ ์คํธ ๊ฐ๋ฅ์ฑ์ ๊ฐ์ ํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค. ๋ค์์ ์ด ๋ชจ๋ ๊ฒ์ ์๋ํ๋ ์ ์์๊ฑฐ๋ ์์ ์ ์ปดํฌ๋ํธ์ ๋๋ค.
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }} - {{ item.price }}
<button @click="addToCart(item)">Add to cart</button>
</li>
</ul>
<div>
<h2>Cart</h2>
<ul>
<li v-for="item in cart" :key="item.id">
{{ item.name }} - {{ item.price }}
<button @click="removeFromCart(item)">Remove</button>
</li>
</ul>
<p>Total: {{ totalPrice }}</p>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const title = ref('My Store');
const description = ref('Welcome to my store!');
const items = ref([
{ id: 1, name: 'Item A', price: 10 },
{ id: 2, name: 'Item B', price: 20 },
]);
const cart = ref([]);
function addToCart(item) {
cart.value.push(item);
}
function removeFromCart(item) {
const index = cart.value.indexOf(item);
if (index !== -1) {
cart.value.splice(index, 1);
}
}
const totalPrice = computed(() => {
return cart.value.reduce((sum, item) => sum + item.price, 0);
});
</script>
์ด๋ฅผ ๋ถํ ํ๋ ๋ฐ ๋์์ด ๋๋ Clean Components Toolkit์ ์ปดํฌ๋ํธ ๊ฒฝ๊ณ ํจํด์ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค. ์ด ํจํด์ ์ผ๋ฐ์ ์ผ๋ก ์ฝ๋์ ์ด๋ฏธ ์ ์๋ ๊ฒฝ๊ณ๊ฐ ์์ด ์ปดํฌ๋ํธ๋ฅผ ๋ถํ ํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์์ ์๋ ค์ค๋๋ค. ์ด๋ฌํ ๊ฒฝ๊ณ๋ฅผ ์ฐพ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ํ ํ๋ฆฟ์ ์ดํด๋ณด๋ ๊ฒ์ ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ถ์ถํ ์ ์๋ ์ธ ๊ฐ์ง ๊ฐ๋ณ ๊ตฌ์ฑ ์์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
- StoreHeader
- ItemList
- Cart
<template>
<div>
<!-- StoreHeader -->
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<!-- ItemList -->
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }} - {{ item.price }}
<button @click="addToCart(item)">Add to cart</button>
</li>
</ul>
<!-- Cart -->
<div>
<h2>Cart</h2>
<ul>
<li v-for="item in cart" :key="item.id">
{{ item.name }} - {{ item.price }}
<button @click="removeFromCart(item)">Remove</button>
</li>
</ul>
<p>Total: {{ totalPrice }}</p>
</div>
</div>
</template>
์๋กญ๊ณ ๊ฐ์ํ๋ ๊ตฌ์ฑ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ์ด์ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๊ณ ์๋์ง ํจ์ฌ ๋ ์ฝ๊ฒ ์ดํดํ ์ ์์ต๋๋ค.
<template>
<div>
<StoreHeader :title="title" :description="description" />
<ItemList :items="items" @addToCart="addToCart" />
<Cart :cart="cart" @removeFromCart="removeFromCart" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import StoreHeader from './StoreHeader.vue';
import ItemList from './ItemList.vue';
import Cart from './Cart.vue';
const title = ref('My Store');
const description = ref('Welcome to my store!');
const items = ref([
{ id: 1, name: 'Item A', price: 10 },
{ id: 2, name: 'Item B', price: 20 },
]);
const cart = ref([]);
function addToCart(item) {
cart.value.push(item);
}
function removeFromCart(item) {
const index = cart.value.indexOf(item);
if (index !== -1) {
cart.value.splice(index, 1);
}
}
</script>
StoreHeader ์ปดํฌ๋ํธ์ ๋๋ค.
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ description }}</p>
</div>
</template>
<script setup>
const props = defineProps({
title: String,
description: String,
});
</script>
ItemList ์ปดํฌ๋ํธ์ ๋๋ค.
<template>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }} - {{ item.price }}
<button @click="$emit('addToCart', item)">Add to cart</button>
</li>
</ul>
</template>
<script setup>
const props = defineProps({
items: Array,
});
</script>
Cart ์ปดํฌ๋ํธ์ ๋๋ค.
<template>
<div>
<h2>Cart</h2>
<ul>
<li v-for="item in cart" :key="item.id">
{{ item.name }} - {{ item.price }}
<button @click="$emit('removeFromCart', item)">Remove</button>
</li>
</ul>
<p>Total: {{ totalPrice }}</p>
</div>
</template>
<script setup>
import { defineProps, computed } from 'vue';
const props = defineProps({
cart: Array,
});
const totalPrice = computed(() => {
return props.cart.reduce((sum, item) => sum + item.price, 0);
});
</script>
์ฌ๊ธฐ์ ํธ๋ ์ด๋์คํ๋ฅผ ๋ฐ๊ฒฌํ์์๋ ์์ต๋๋ค. ํ๋์ ๋ณต์กํ ์ปดํฌ๋ํธ๋ฅผ ์ฌ๋ฌ ๊ฐ์ ๊ฐ๋จํ ์ปดํฌ๋ํธ๋ก ๋ฐ๊พธ๋ ๊ฒ์ ๋๋ค. ์ฝ๋๊ฐ ์กฐ๊ธ ๋ ๋ง์์ง๊ธด ํ์ง๋ง ์ดํดํ๊ธฐ ํจ์ฌ ์ฝ๊ณ ์ ์ง ๊ด๋ฆฌ ๋ฐ ์์ ํ๊ธฐ ์ฌ์ด ์ฝ๋๊ฐ ๋ฉ๋๋ค. ์ ๊ฐ ๋ณด์ฌ๋๋ฆฐ ์์๋ค ์ฌ์ด์๋ ํต์ผ๋ ์์น์ด ์์ต๋๋ค.
๊ฐ์ฅ ๋ฉ์ฒญํ๊ณ , ๊ฐ์ฅ ์ข์ ํ๊ณ , ๊ฐ์ฅ ํผ๊ณคํ ์๊ธฐ์์ ์ ์ํด. ์ต์ ํ ํ์ธ์.
์ ๋ ์๋ฆฌํ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒฝํฅ์ด ์์ต๋๋ค.(์ ์ด๋ ์ ๊ฐ ๋๋ํ๋ค๊ณ ๋๊ปด์ง๋๋ก ๋ง๋ค์ฃ .) ํ์ง๋ง ๋ค์๋ ์์์ด ๋จ์ด์ง๊ฑฐ๋ ์๋์ง๊ฐ ์ค์ด๋ ์ํ๋ก ๋์์ค๋ฉด ๋ฌด์จ ์ผ์ด ์ผ์ด๋๊ณ ์๋์ง ํ์ ํ๊ธฐ ์ด๋ ต์ต๋๋ค. ์ฐ๋ฆฌ๋ ์ต์ ์ ๋ ์๋ ์ดํดํ ์ ์๊ณ ์์ฐ์ ์ผ๋ก ์์ ํ ์ ์๋ ๋ช ํํ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ์ถ์ต๋๋ค. ์ด๋ ์ข์ ์ด๋ฆ ์ง์ ๊ณผ ์ ๋ฆฌ, ๋ช ํํ๊ณ ๊ฐ๊ฒฐํ ๋ฌธ์์ ์์ ๋ฅผ ์กฐํฉํ์ฌ ๋ฌ์ฑํ ์ ์์ต๋๋ค. ๋ํ ์ผ๊ด๋ ํจํด๊ณผ ๊ท์น์ ์ฌ์ฉํ๋ฉด ์ธ์ง์ ๋ถํ๋ฅผ ์ค์ด๊ณ ํด์์ด๋ ์ค๋จ ํ์๋ ํ๋ก์ ํธ ์์ ์ ๊ณ์ํ๊ธฐ๊ฐ ์ฌ์์ง ์ ์์ต๋๋ค.
๊ฒฐ๋ก
์ฌ๋์ด ์ดํดํ ์ ์๋๋ก ์ปดํฌ๋ํธ๋ฅผ ์ต์ ํํ๋ ๊ฒ์ ์ ์ง๋ณด์์ฑ, ๊ฐ๋ ์ฑ ๋ฐ ์ ๋ฐ์ ์ธ ํ๋ก์ ํธ ์ฑ๊ณต์ ์ํด ๋งค์ฐ ์ค์ํฉ๋๋ค. ์ฌ๋ฐ๋ฅธ ๋ช ๋ช ๊ท์น์ ์ฌ์ฉํ๊ณ , ์ปดํฌ๋ํธ ๋ฉ์๋๋ฅผ ์ถ์ถํ๊ณ , ์ฝ๋๋ฅผ ์ ๋ฆฌํ๊ณ , ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ๋ฅด๋ฉด Vue ์ปดํฌ๋ํธ์ ๋ ์ฝ๊ฒ ์ ๊ทผํ๊ณ ์์ ํ๊ธฐ ์ฝ๊ฒ ๋ง๋ค์ ์์ต๋๋ค. ์ด ๋ฌธ์์๋ ๋ช ๊ฐ์ง ๋ฐฉ๋ฒ์ ์๊ฐํ์ง๋ง, ๊ทธ ์ธ์๋ ๋ค์ํ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. Clean Component Toolkit์๋ ๋ ๋์ Vue ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๋ ๋ฐ ๋์๋๋ ํจํด, ๊ธฐ์ ๋ฐ ๊ฐ๋ ๊ณผ ๊ฐ์ 21๊ฐ์ง ๋๊ตฌ๊ฐ ํฌํจ๋์ด ์์ต๋๋ค. ๊ฐ๊ฐ์ ์ค์ ์ฝ๋ ์์ ์ ๋๊ตฌ๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ฃผ๋ ๋จ๊ณ๋ณ ๋ฆฌํฉํฐ๋ง ์์ ๋ฅผ ํฌํจํ์ฌ ๋งค์ฐ ์์ธํ๊ฒ ๋ค๋ฃน๋๋ค. ๋ฐ๋ผ์ Vue ์ฝ๋๋ฅผ ๊ฐ์ ํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์ธํ ์์๋ณด๊ณ ์ถ๋ค๋ฉด Clean Components Toolkit์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.