Vue Code Style
๐ ๋ค์ด๊ฐ๊ธฐ
Vue.js ํ๋ ์์ํฌ๋ฅผ ์กฐ๊ธ๋ ๊น๋ํ๊ณ ์ ๋๋ก ์ฌ์ฉํ๊ธฐ ์ํด์ Vue API์์ ์ ๊ณตํ๋ ์ฝ๋ ์คํ์ผ์ ํด์ํด์ ๊ธฐ๋กํ๋ฉด ๊ณต๋ถํด๋ณผ ์์ ์ด๋ค.
ํ์์ ์ผ๋ก ์ง์ผ์ค์ผํ๋ ๊ฒ๋ค๊ณผ ๋งค์ฐ์ถ์ฒ, ์ถ์ฒ, ์ฃผ์ ์ ๋์ ๊ฐ๋๋ก ์๊ตฌ๋๋ค.
ํ์ ๊ท์น
์ปดํฌ๋ํธ ์ด๋ฆ์ ํฉ์ฑ์ด ์ฌ์ฉ
Vue์์ ์ ๊ณต๋๋ ๋นํธ์ธ ์ปดํฌ๋ํธ๋ฅผ ์ ์ธํ๊ณ ์ปดํฌ๋ํธ ์ด๋ฆ์ ํญ์ ํฉ์ฑ์ด๋ฅผ ์ฌ์ฉํด์ผํ๋ค.
- ๋ชจ๋ HTML ์๋ฆฌ๋จผํธ์ ์ด๋ฆ์ ํ ๋จ์ด์ด๊ธฐ ๋๋ฌธ์ ํฉ์ฑ์ด๋ฅผ ์ฌ์ฉํด์ ์ถฉ๋์ ๋ฐฉ์งํ๋ค.
์ปดํฌ๋ํธ ๋ฐ์ดํฐ
์ปดํฌ๋ํธ์
data
๋ ๋ฐ๋์ ํจ์์ฌ์ผ ํ๋ค.
data
์ ๊ฐ์ด ์ค๋ธ์ ํธ์ผ ๊ฒฝ์ฐ, ์ปดํฌ๋ํธ์ ๋ชจ๋ ์ธ์คํด์ค๊ฐ ๊ณต์ ๋๋ค. ์ด๋ง์์ฆ์จ ๋ค๋ฅธ ๊ณณ์์ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฌ์ฉํ๋ค๋ฉด data๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๋ฏ๋ก ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋๋ค๋ ๋ป์ด๋ค.
๊ฐ ์ปดํฌ๋ํธ๋ ์์ฒด data๋ง์ ๊ด๋ฆฌํ๊ธฐ๋ฅผ ์ํ๊ธฐ ๋๋ฌธ์ ์ด๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๊ธฐ ์ํด์๋ ๊ณ ์ ํ data ๊ฐ์ฒด๋ฅผ ์์ฑํด์ผ ํ๋ค.
Props ์ ์
Prop์ ๊ฐ๋ฅํ ์์ธํ๊ฒ ์ ์๋์ด์ผํ๋ค.
- ์ปดํฌ๋ํธ์ API๋ฅผ ๋ฌธ์ํํ๋ฏ๋ก ์ปดํฌ๋ํธ์ ์ฌ์ฉ ๋ฐฉ๋ฒ์ ์ฝ๊ฒ ์ ์ ์๋ ์ฅ์ ์ด ์๋ค.
- Vue๋ ํ์ ์ ์ ์ํ ์ ์๋ ๊ธฐ๋ฅ์ด ๋ด์ฅ๋์ด ์๋๋ฐ ์ด๋ฅผ ์ด๊ธฐ๋ฉด ๊ฒฝ๊ณ ๋ฉ์์ง๋ฅผ ํ์ํ์ฌ ์ ์ฌ์ ์ค๋ฅ๋ฅผ ๋ง์ ์ ์๋๋ก ๋์์ค๋ค.
v-for
์ key
์ง์
v-for๋ key์ ํญ์ ํจ๊ป ์ฌ์ฉ
- ์๋๋ฉ์ด์
์ ๊ฐ์ฒด ๋ถ๋ณ์ฑ๊ณผ ๊ฐ์ ์์ธก ๊ฐ๋ฅํ ํ๋์ ์ ์งํ๋๊ฒ
Vue๋ ๊ฐ์ DOM ๋ฐฉ์์ ์ฒดํํ๊ณ ์์ผ๋ฉฐ ๊ฐ๋ฅํ ์ ์ DOM์ ์์ง์ฌ์ ๋ ๋๋ง์ ์ต์ ํํ๋๋ฐ key๊ฐ์ ์ค์ ํ๋ฏ๋ก์จ Vue์ ์์ง์์ ์์ธก ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
v-if
์ v-for
๋ฅผ ๋์์ ์ฌ์ฉํ์ง๋ง์
v-for๊ฐ ์ฌ์ฉ๋ ์๋ฆฌ๋จผํธ์ ์ ๋ v-if๋ฅผ ์ฌ์ฉํ์ง ๋ง์.
Bad
- ๋ฆฌ์คํธ ๋ชฉ๋ก์ ํํฐ๋งํ๊ธฐ ์ํด์ ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ค.
<div v-for="user in users" v-if="user.isActive"></div>
์ด ๊ฒฝ์ฐ computed ์์ฑ์ ์ฌ์ฉํด์ ํํฐ๋ง๋ ๋ชฉ๋ก์ผ๋ก ๋์ฒดํด์ ์ฌ์ฉํด์ผํ๋ค.
- ๋ฆฌ์คํธ์ ๋ด์ฉ์ ์จ๊ธฐ๊ธฐ ์ํด์๋ ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ค.
<div v-for="user in users" v-if="shouldShowUsers"></div>
์ด ๊ฒฝ์ฐ v-if
๋ฅผ ์ปจํ
์ด๋ ์๋ฆฌ๋จผํธ๋ก ์ฎ๊ฒจ ์ฌ์ฉํ๋ค.
Good
- ํํฐ๋ง๋ ๋ชฉ๋ก์ ๋ณ๊ฒฝ ์ฌํญ์ด ์๋ ๊ฒฝ์ฐ์๋ง ๊ณ์ฐ๋๋ฏ๋ก ํํฐ๋ง ํจ์จ์ฑ์ ๋์ผ ์ ์๋ค.
v-for="user in activeUsers"
๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ๋๋ง ์ค์ ํ์ฑ ์ฌ์ฉ์๋ง ๋ฐ๋ณต๋๋ฏ๋ก ๋ ๋๋ง ํจ์จ์ฑ์ด ์ฆ๊ฐํ๋ค.- ๋ก์ง์ด ๊ณ์ธต์์ ๋ถ๋ฆฌ๋๋ฏ๋ก ์ ์ง ๊ด๋ฆฌ(ํ์ฅ์ฑ)์ด ์ฌ์์ง๋ค.
์ปดํฌ๋ํธ ์คํ์ผ ์ค์ฝํ
์ต์์ APP ๊ตฌ์ฑ ์์์ ๋ ์ด์์ ๊ตฌ์ฑ ์์์ ์คํ์ผ์ด ์ ์ญ์ ์ผ ์ ์์ง๋ง ๋ค๋ฅธ ๊ตฌ์ฑ์ ์ปดํฌ๋ํธ์ ๊ฒฝ์ฐ ๋ฒ์๋ ํญ์ ์ง์ ๋์ด์ผ ํ๋ค.
์ฑ๊ธ ํ์ผ ์ปดํฌ๋ํธ์ ๊ฒฝ์ฐ style์์ญ์ scoped
๋ฅผ ์์ฑ์ ํตํด ํด๋น ์ปดํฌ๋ํธ์์๋ง ์ฌ์ฉ๋๋๋ก ์คํ์ผ์ ์ง์ ํ ์ ์๋ค.
- ๋ํ ํ๋ก์ ํธ๋ ๋ค๋ฅธ ๊ฐ๋ฐ์์ ํจ๊ป ์์ ํ ๋ scoped ์ค์ ์ ์คํ์ผ์ ์ถฉ๋์ ์ต์ํ ํ ์ ์๋ค.
Private ์์ฑ ์ด๋ฆ
ํ๋ฌ๊ทธ์ธ, mixin ๋ฑ์์ ์ปค์คํ ์ฌ์ฉ์ private ํ๋กํผํฐ๋ ํญ์ ์ ๋์ฌ
$_
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅ.
๋ค๋ฅธ ์ฌ๋์ ์ฝ๋์ ์ถฉ๋์ ํผํ๋ ค๋ฉด named scope๋ฅผ ์ฌ์ฉ.
Vue ์์๋ ์ ๋์ฌ๋ก _
์ $
๋ฅผ ์ฌ์ฉ์ค์ด๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ํผํฉํ $_
๋ฅผ ์ฌ์ฉํ์ฌ private ์์ฑ์ ๊ตฌ๋ถํ๋ ์ฌ์ฉํ ์ ์๋ค.
var myGreatMixin = {
// ...
methods: {
$_myGreatMixin_update: function () {
// ...
},
},
}
// Even better!
var myGreatMixin = {
// ...
methods: {
publicMethod() {
// ...
myPrivateFunction()
},
},
}
function myPrivateFunction() {
// ...
}
export default myGreatMixin
๋งค์ฐ ์ถ์ฒํจ (๊ฐ๋ ์ฑ ํฅ์์ ์ํจ)
์ปดํฌ๋ํธ ํ์ผ
์ปดํฌ๋ํธ ํ์ผ์ ์ด์ฉํ์ฌ ํ์ผ ์์คํ ํ๊ฒฝ์ ์ฐ๊ฒฐ์ ์ฝ๊ฒํ๊ณ ๋น ๋ฅด๊ฒ ์ฐพ์ ์ ์๊ฒ ํ๋ค.
Bad
Vue.component('TodoList', {
// ...
})
Vue.component('TodoItem', {
// ...
})
Good
components/
|- TodoList.vue
|- TodoItem.vue
์ฑ๊ธ ํ์ผ ์ปดํฌ๋ํธ ์ด๋ฆ ๊ท์น ์ง์ (casing)
์ฑ๊ธ ํ์ผ ์ปดํฌ๋ํธ์ ์ด๋ฆ์ ์ง์ ํ ๋ PascalCase ๋๋ kebab-case์ ์ฌ์ฉํ๋ค.
๋ฒ ์ด์ค ์ปดํฌ๋ํธ ์ด๋ฆ
๊ธฐ๋ณธ ์ปดํฌ๋ํธ์ ์ปจ๋ฒค์ ์ผ๋ก prefix
Base
,App
,V
๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌ๋ถํ๋ค. ์ปจ๋ฒค์ ์ ์ด์ฉํ์ฌ ์ ์ญ ์ปดํฌ๋ํธ๋ก ๋ฑ๋กํ์ฌ ์ฌ์ฉ๊ฐ๋ฅํ๋ค.
Example
components\
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
components\
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
๋ฒ ์ด์ค ์ปดํฌ๋ํธ ์ ์ญ ์ปดํฌ๋ํธ๋ก ๋ฑ๋ก
// main.js
let requireComponent = require.context('./src/components', false, /^Base[A-Z].(vue|js|ts)$/)
requireComponent.keys().forEach(fileName => {
let baseComponentConfig = requireComponent(fileName)
baseComponentConfig = baseComponentConfig.default || baseComponentConfig
let baseComponentName = baseComponentConfig.name || fileName.replace(/^.+\//, '').replace(/\.\w+$/, '')
Vue.component(baseComponentName, baseComponentConfig)
})
์ฑ๊ธ ์ธ์คํด์ค ์ปดํฌ๋ํธ ์ด๋ฆ
์ธ์คํด์ค๊ฐ ํ๋๋ง ์์ด์ผํ๋ ๊ตฌ์ฑ ์์์ ์ ๋์ฌ
The
๋ก ์์ํ๋ค. ํ๋๋ง ์์์ ๋ํ๋ธ๋ค.
- ํํ์ด์ง๋น ํ๋ฒ๋ง ์ฌ์ฉ๋๋ ์์
- props๋ฅผ ํ์ฉํ์ง ์๋ ์์
- props๋ฅผ ์ถ๊ฐํด์ผํ์ง๋ง ํ๋ฒ๋ง ์ฌ์ฉ๋๋ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์์
Bad
components/
|- Heading.vue
|- MySidebar.vue
Good
components/
|- TheHeading.vue
|- TheSidebar.vue
๊ฐํ ์ฐ๊ด์ฑ์ ๊ฐ์ง ์ปดํฌ๋ํธ ์ด๋ฆ
์์ ๊ตฌ์ฑ ์์์ ๊ธด๋ฐํ๊ฒ ์ฐ๊ฒฐ๋ ํ์ ๊ตฌ์ฑ ์์๋ ์์ ๊ตฌ์ฑ ์์ ์ด๋ฆ์ ์ ๋์ฌ๋ก ํฌํจํด์ผํ๋ค.
์์๋ฅผํตํด ์๋ง์ ํ์ด๋ณด๋ฉด..
๋ณดํต์ ํจํค์ง ๊ด๋ฆฌ๋ฅผ ํ ๋ ์๋์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์์ ๊ตฌ์ฑ ์์์ ์ด๋ฆ์ ๋ด ๋๋ ํ ๋ฆฌ์ ํ์ ๊ตฌ์ฑ์์๋ฅผ ์ค์ฒฉํด์ ์ฌ์ฉํ ๊ฒ์ด๋ค.
components/
|- TodoList/
|- Item/
|- index.vue
|- Button.vue
|- index.vue
or
components/
|- TodoList/
|- Item/
|- Button.vue
|- Item.vue
|- TodoList.vue
ํ์ง๋ง ์ด๋ฐ ๋ฐฉ๋ฒ์ ์ถ์ฒํ์ง ์๋๋ค.
- ๋น์ทํ ์ด๋ฆ์ ๊ฐ์ง ํ์ผ๋ค์ด ๋ง์์ง๋ฉฐ, ํธ์ง๊ธฐ์์ ์ ์ํ ํ์ผ์ ํ์ด ๋ถํธํ๋ค.
- ์ค์ฒฉ๋ ์ฌ๋ฌ ํ์ ๋๋ ํ ๋ฆฌ๋ ํธ์ง๊ธฐ์ ์ฌ์ด๋๋ฐ์ ๋์ค๋ฅผ ๋๋ฆฌ๊ณ ๊ฒ์์ ๋ถ๋ฆฌํ๋ค๋ ๊ฒ์ด๋ค.
์ด๋ฌํ ์ด์ ๋ก ๊ตฌ์ ฉ์์์ ์ด๋ฆ์ ๊ด๊ณ๋ฅผ ๋ช ํํ๊ฒ ๋ํ๋ด๋ ๋ฐฉ๋ฒ์ ์ถ์ฒํ๋๋ฐ, ํธ์ง๊ธฐ์์๋ ์ผ๋ฐ์ ์ผ๋ก ํ์ผ์ ์ํ๋ฒณ ์์ผ๋ก ๊ตฌ์ฑํ๋ฏ๋ก ๊ด๋ จ ํ์ผ๋ ๋ณด๊ดํ๊ธฐ๋ ํธ๋ฆฌํ๋ค.
Bad
component/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
Good
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
์ฌ์ค ์ด ๋ถ๋ถ์ ์ทจํฅ์ฐจ์ด๊ฐ ์๊ธธ ์ ์๋ค. ์ฌ์ฉํ๊ณ ์๋ IDE๊ฐ ์ผ๋ง๋ ์ง์ํ๋๊ฐ์ ๋ฐ๋ผ์ ๊ฐ๋ ์ฑ์ด๋ ์ปดํฌ๋ํธ๋ฅผ ์ฐพ๋ ์ฌ์ ์ ๋๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ด๋ค. ํ์ง๋ง ๋๋ ๊ฐ์ ์ด์ ๋ก ๋ถํธํจ์ ๋๋ผ๋์ฐจ๋ผ ์ถ์ฒํด์ฃผ๋ ๋ฐฉ์์ ์ด์ฉํด์ ๊ฐ๋ฐ์ ์ ์ฉํด๋ณผ๊นํ๋ค.
์ปดํฌ๋ํธ ์ด๋ฆ์ ๋จ์ด ์์ ์ ๋ ฌ
๊ตฌ์ฑ์์ ์ด๋ฆ์ ์ต์์ ์์ค ๋จ์ด๋ก ์์ํ๊ณ ์ค๋ช ๋จ์ด๋ก ๋๋ด์ผํ๋ค.
์์์ ์ธ๊ธํ๋ ๊ฐํ ์ฐ๊ด์ฑ์ ๊ฐ์ง ์ปดํฌ๋ํธ ์ด๋ฆ๊ณผ ๊ฐ์ ์ด์ ๋ก ์ถ์ฒ๋ฉ๋๋ค.
Bad
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
Good
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingCheckboxLaunchOnStartup.vue
์ ํ ํด๋ก์ง ์ปดํฌ๋ํธ
์ปจํ ์ธ ๊ฐ ์๋ ๊ตฌ์ฑ ์์๋ ๋จ์ผ ํ์ผ ๊ตฌ์ฑ ์์, ๋ฌธ์์ด ํ ํ๋ฆฟ ๋ฐ JSX์์ ์์ฒด ๋ซ์์ผํ๋ค. - ๊ทธ๋ฌ๋ DOM ํ ํ๋ฆฟ์ ๋ซ์ง ์๋๋ค.
์ถ๊ฐ์ ์ผ๋ก html ํ
ํ๋ฆฟ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ PascalCase๋ ์ง์ํ์ง๋ง kebab-case๋ก๋ ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค. html ํ๊ทธ๋ ๋์๋ฌธ์ ๊ตฌ๋ถ์ด ์๊ธฐ๋๋ฌธ์ ๊ฐ์ธ์ ์ผ๋ก๋ kebab-case
๋ฅผ ์ ํธํ๋ค.
Good
<!-- In single-file components, string template, and JSX -->
<MyComponent />
<!-- In DOM templates -->
<my-component></my-component>
Prop ์ด๋ฆ ๊ท์น ์ง์ (casing)
prop ์ด๋ฆ์ ์ ์ธ ์ค์์ ํญ์ camelCase๋ฅผ ์ฌ์ฉํด์ผํ๋ค. ๋ HTML ํ ํ๋ฆฟ ๋ฐ JSX์์๋ kebab-case๋ฅผ ์ฌ์ฉํ๋ค.
Good
props: {
greetingText: String
}
<WelcomeMessage greeting-text="hi" />
๋ค์ค ์์ฑ ์๋ฆฌ๋จผํธ
์ฌ๋ฌ ์์ฑ์ ๊ฐ์ง ์์๋ ํ์ค์ ํ๋์ ์์ฑ์ ๊ฐ์ง ์ฌ๋ฌ ์ค๋ก ํ์ฅ๋์ด์ผ ํ๋ค.
Good
<img src="https://..." alt="Vue Logo" />
ํ ํ๋ฆฟ์์ ๋จ์ํ ํํ์
๊ตฌ์ฑ ์์ ํ ํ๋ฆฟ์๋ ๋ ๋ณต์กํ ํํ์์ด ๊ณ์ฐ๋ ์์ฑ(computed) ๋๋ ๋ฉ์๋(method)๋ก ๋ฆฌํํฐ๋ง๋๋จ์ํ ํํ์๋ง ํฌํจํด์ผํ๋ค.
๋จ์ํ ๊ณ์ฐ๋ ์์ฑ
์ฐธ๊ณ