[Vue] 컴포넌트 - 기초

youngseo·2022년 5월 2일
0
post-thumbnail

한 번에 끝내는 프론트엔드 개발 초격차 패키지 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>

컴포넌트의 특징

1. 재활용

한 번 만들어놓은 컴포넌트는 얼마든지 재활용해서 사용을 할 수 있습니다.

아래와 같이 <MyBtn>이라는 컴포넌트를 여러 개 작성을 하면 화면에 해당 수만큼 컴포넌트가 출력된 것을 확인할 수 있습니다.

<template>
  <MyBtn />
  <MyBtn />
  <MyBtn />
  <MyBtn />
</template>

2. Props

컴포넌트에 스타일 지정등을 통해 조금 더 확장해서 만들 수 있도록 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>

3. Props로 클래스 추가

세번째 버튼에 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" />

4. text Props

이번에는 각각의 버튼에 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>

5. Slot

이러한 부분들을 조금 더 최적화시킬 수 있는 방법이 있습니다.

마치 <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이라는 태그부분으로 대체해서 넣어준다는 개념으로 이해할 수 있습니다.

0개의 댓글