한 번에 끝내는 프론트엔드 개발 초격차 패키지 Online를 들으며 정리한 내용입니다.
App.vue
<template>
<MyBtn />
</template>
<script>
import MyBtn from '~/components/MyBtn'
export default {
components: {
MyBtn
}
}
</script>
MyBtn.vue
<template>
<div class="btn">
Apple
</div>
</template>
<style scoped>
.btn {
display: inline-block;
margin: 4px;
padding: 6px 12px;
border-radius: 4px;
background-color: gray;
color: white;
cursor: pointer;
}
</style>
한 번 만들어놓은 컴포넌트는 얼마든지 재활용해서 사용을 할 수 있습니다.
아래와 같이 <MyBtn>
이라는 컴포넌트를 여러 개 작성을 하면 화면에 해당 수만큼 컴포넌트가 출력된 것을 확인할 수 있습니다.
<template>
<MyBtn />
<MyBtn />
<MyBtn />
<MyBtn />
</template>
⭐컴포넌트에 스타일 지정등을 통해 조금 더 확장해서 만들 수 있도록 props라는 기능을 통해서 내용을 정리해줄 수 있습니다. 이것을 부모컴포넌트에서 자식컴포넌트로 특정한 데이터를 전달할 때 쓴다고 해서 '부모-자식 간의 데이터 통신 방법'이라고도 합니다.
App.vue
<template>
<MyBtn />
<MyBtn color="#000"/>
<MyBtn />
<MyBtn />
</template>
<script>
import MyBtn from '~/components/MyBtn'
export default {
components: {
MyBtn
}
}
</script>
MyBtn.vue
<template>
<div
:style="{ backgroundColor: color }" //props에 해당하는 내용을 실제로 연결
class="btn">
Apple
</div>
</template>
<script>
export default {
props: {
color: {
type: String,
default: 'gray' // 기본값 지정
}
}
}
</script>
Props
는 컴포넌트가 실행이 될 때 마치 속성처럼 받아내는 내용(대표적으로 color라는 내용)을 정의해주는 옵션입니다. 여기서 color라는 것은 MyBtn컴포넌트가 실행될 때 외부(App.vue)에서 내용을 받아서 쓸 수 있는 구조를 만들어주는 것입니다.
이는 MyBtn컴포넌트의 type속성 부분에 String으로 받아야된다고 되어져 있기 때문에 color값으로 숫자데이터는 사용할 수 없으며, 문자데이터가 들어갈 수 있도록 만들어줘야합니다. 대표적으로 "#000"을 넣어서 화면에 출력할 수도 있습니다.
✅보충: 숫자데이터와 문자데이터 구분
<MyBtn color ="123"/>
에서 123은 숫자데이터가 아니라 123이라는 글자가 적혀있는 문자데이터입니다.
만약, 숫자로 전달하고 싶다면<MyByn :color="123" />
과 같이color
의 앞에다가:
를 통해 데이터 바인딩을 해서 실제로 숫자데이터로 인식될 수 있도록 만들어줘야합니다.
아래와 같이 부모컴포넌트에 data속성을 이용해 조금 더 직접적으로 부모컴포넌트가 가지고 있는 데이터를 자식컴포넌트로 전달하는 코드를 작성할 수도 있습니다.
App.vue
<template>
<MyBtn />
<MyBtn :color="color" />//문자이기 때문에 데이터처럼 읽힐 수 있도록 v-bind: 를 사용해야합니다.
<MyBtn />
<MyBtn />
</template>
<script>
import MyBtn from '~/components/MyBtn'
export default {
components: {
MyBtn
},
data() {
return {
color: '#000'
}
}
}
</script>
세번째 버튼에 large라는 속성을 별도의 값이 없도록 작성을 해주었습니다.
App.vue
<template>
<MyBtn />
<MyBtn :color="color" />
<MyBtn large />
<MyBtn />
</template>
MyBtn.vue
<template>
<div
:class=" { large }"//class바인딩을 해줍니다.
//객체데이터 내부에서 속성과 내용이 같으면 데이터 내용부분을 생략해줄 수 있습니다.
:style="{ backgroundColor: color }"
class="btn">
Apple
</div>
<script>
export default {
props: {
color: {
type: String,
default: 'gray'
},
large: {
type: Boolean, //large속성이 있거나 없는 개념
default: false //large속성이 없는 것으로 default
}
}
}
</script>
이제 이 large라는 속성이 있는 경우 조금 더 크게 보일 수 있도록 기본적인 스타일을 정의해주도록 하겠습니다.
<style scoped lang="scss">
.btn {
display: inline-block;
margin: 4px;
padding: 6px 12px;
border-radius: 4px;
background-color: gray;
color: white;
cursor: pointer;
&.large{
font-size: 20px;
padding: 10px 20px;
}
}
</style>
이렇게 해당하는 컴포넌트에 마치 html의 속성처럼 내용이 붙어있을 때 혹은 그내용의 어떤 값이 연결되어 있을 때 그것을 컴포넌트 내부에서 어떻게 처리할지를 정의하는 개념으로 props라는 옵션을 사용할 수 있습니다.
또한 이 props옵션은 하나만 사용하는 것이 아니라 여러개를 사용할수도 있습니다.
<MyBtn //large속성과 color속성을 함께 사용
large
color="roaylblue" />
이번에는 각각의 버튼에 Apple이라는 공통된 내용이 아닌 각각 다르게 출력될 수 있도록 만들어보도록 하겠습니다.
App.vue
<template>
<MyBtn text="Banana" /> <!-- 1. text라는props추가-->
<MyBtn :color="color" />
<MyBtn
large
color="royalblue" />
<MyBtn />
</template>
MyBtn.vue
<template>
<div
:class=" { large: large }"
:style="{ backgroundColor: color }"
class="btn">
{{ text }} // 3. 이중중괄호 구문으로 사용
</div>
</template>
<script>
export default {
props: {
color: {
type: String,
default: 'gray'
},
large: {
type: Boolean,
default: false
},
text: { // 2. text라는 props 정의
type: String,
default: '',
}
}
}
</script>
첫번 째 banana가 정상적으로 출력되고 있지만 다른 버튼들에는 약간의 문제가 생겼습니다. 나머지 버튼들에도 아래와 같이 직접적으로 text를 제공하면 정상적으로 동작하는 것을 확인할 수 있습니다.
<template>
<MyBtn text="Banana" />
<MyBtn
:color="color"
text="Banana" />
<MyBtn
large
color="royalblue"
text="Banana" />
<MyBtn text="Banana" />
</template>
이러한 부분들을 조금 더 최적화시킬 수 있는 방법이 있습니다.
마치 <button>Button</button>
과 같이 이름이 별도의 속성으로 들어가는 것이 아니라 컨텐츠로 들어가게금 컴포넌트를 제어해줄 수 있습니다.
App.vue
<template>
<MyBtn>Banana</MyBtn> //열리고 닫히는 구조로 변경
</template>
MyBtn.vue
<template>
<div>
...
<slot></slot> // {{text}}지우고 작성
</div>
</template>
컴포넌트도 일반적인 요소처럼 열리고 닫히는 태그로 만들어 그 사이에 어떠한 내용을 적어줄 수 있으며, 그렇게 적힌 내용을 어디에 출력할 것인지 slot이라는 태그로 그 위치를 정의해줄 수 있습니다.
또한 이렇게 slot을 제공하게 되면 개별적으로 text라는 속성을 script에 정의를 해줄 필요가 없어집니다.
App.vue
<template>
<MyBtn>Banana</MyBtn>
<MyBtn
:color="color">
Cherry
</MyBtn>
<MyBtn
large
color="royalblue">
Apple
</MyBtn>
<MyBtn>Banana</MyBtn>
</template>
MyBtn.vue
<template>
<div
:class=" {large: large}"
:style="{backgroundColor: color}"
class="btn">
<slot></slot>
</div>
</template>
<script>
export default {
props: {
color: {
type: String,
default: 'gray'
},
large: {
type: Boolean,
default: false
}
}
}
</script>
그리고 이 slot에는 단순히 텍스트만 작성하는 개념은 아닙니다. 아래와 같이 span태그로 감싸고 style을 정의해줄 수도 있습니다.
<template>
<MyBtn>Banana</MyBtn>
<MyBtn
:color="color">
<span style="color: red">Cherry</span>
</MyBtn>
<MyBtn
large
color="royalblue">
Apple
</MyBtn>
<MyBtn>Banana</MyBtn>
</template>
결국 slot이라는 개념을 통해서 단순히 텍스트만 받아서 slot태그부분에 출력하는 것이 아니고 MyBtn이라는 컴포넌트가 실행되는 부분에서 열리고 닫히는 태그 사이의 모든 내용을 slot이라는 태그부분으로 대체해서 넣어준다는 개념으로 이해할 수 있습니다.