9Cells

Laravel Blade에서 Vue Component 사용하기

Vue는 컴파일을 하지않고 HTML의 script 태그에 포함하여 사용할 수 있습니다. 이 문서에서는 자바스크립트 코드를 컴파일하지 않고 재사용 가능한 Vue 컴포넌트를 사용하는 방법을 설명합니다. 여러 HTML 조각을 결합하는 것에는 Laravel Blade를 사용합니다.

카카오 우편번호 서비스 예제의 주소검색을 재사용 해봅시다. 주소검색 창은 서비스의 여러곳에서 사용될 수 있습니다. 쇼핑몰을 예로 들어, 구매 페이지에서 받는 사람 정보 입력에서 주소검색을 사용할 수 있고 마이 페이지 등에서 내 주소를 변경하기 위해 주소검색을 사용할 수 있습니다.

코드

app.blade.php

<!doctype html>
<html>
<head>
    <script src="{{ asset('js/app.js') }}" defer></script>
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    @stack('head')
</head>
<body>
<div id="app">
    <main class="py-4">
        @yield('content')
    </main>
</div>
</body>
@stack('script')
</html>

레이아웃을 구성합니다. head와 script를 @stack으로 만들어 하위 뷰에서 여러번 코드를 추가할 수 있게 했습니다.

address.blade.php

@once
@push('head')
    <script src="https://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
@endpush
@endonce

<search-address-modal ref="{{ $ref }}" @address-selected="{{ $addressSelected }}" inline-template>
    <div class="modal" tabindex="-1">
        <div class="modal-dialog modal-lg">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">주소검색</h5>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div ref="searchAddress" style="width: 100%; height: 450px;"></div>
                </div>
            </div>
        </div>
    </div>
</search-address-modal>

@once
@push('script')
    <script>
        Vue.component('search-address-modal', {
            data: function () {
                return {
                };
            },
            methods: {
                open: function () {
                    var self = this;
                    $(self.$el).modal();
                    new daum.Postcode({
                        oncomplete: function (data) {
                            $(self.$el).modal('hide');
                            self.$emit('address-selected', data);
                        },
                        width: '100%', height: '100%', maxSuggestItems: 5
                    }).embed(this.$refs.searchAddress);
                }
            }
        });
    </script>
@endpush
@endonce

주소검색 모달을 위와같이 구현합니다. 이 blade 파일을 다른 페이지에서 include 하여 재사용 가능합니다. inline-template을 사용하므로 최종적으로 합쳐진 하나의 HTML 안에서 위 HTML 블록은 별도의 컴포넌트 scope를 가집니다.

ref, @address-selected 같은 컴포넌트 외부에서 이 컴포넌트를 제어하기 위한 속성들을 지정할 수 있도록 했습니다. 주소가 선택되면 $emit('address-selected', data);를 호출하여 외부에서 주소 data를 받을 수 있게 했습니다.

Blade의 @once를 사용하여 이 컴포넌트가 한 번에 여러번 호출돼도 스크립트는 한 번만 선언되도록 했습니다.

page.blade.php

@extends('app')

@push('head')
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
@endpush

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <p><button @click="clickBtn1">버튼1</button></p>
                <div class="alert alert-success" role="alert">@{{ addr1 }}</div>

                <p><button @click="clickBtn2">버튼2</button></p>
                <div class="alert alert-success" role="alert">@{{ addr2 }}</div>
            </div>
        </div>
    </div>

    @include('address', ['ref' => 'address1', 'addressSelected' => 'addressSelected1'])
    @include('address', ['ref' => 'address2', 'addressSelected' => 'addressSelected2'])
@endsection

@push('script')
    <script>
        var vm = new Vue({
            el: '#app',
            data: function () {
                return {
                    'addr1': '선택주소1',
                    'addr2': '선택주소2',
                };
            },
            methods: {
                clickBtn1: function (e) {
                    this.$refs.address1.open();
                },
                clickBtn2: function (e) {
                    this.$refs.address2.open();
                },
                addressSelected1: function (data) {
                    this.addr1 = data.roadAddress;
                },
                addressSelected2: function (data) {
                    this.addr2 = data.roadAddress;
                }
            }
        });
    </script>
@endpush

address를 사용하는 페이지입니다. @include를 사용하여 address 컴포넌트를 두 번 포함하는 것을 볼 수 있습니다.

이 예제는 한 페이지 안에서 컴포넌트를 여러번 사용하는 예제입니다. 다른 페이지에서 컴포넌트를 재사용하는 것 또한 가능합니다.

마치며

재사용 가능한 주소검색 컴포넌트를 html에 코딩하는 형태로 만들어봤습니다. Vue는 inline-template를 사용하여 html 일부를 template 처럼 다룰 수 있습니다. 이를 사용하여 blade에서 vue 코드 조각을 조립하여 재사용 가능했습니다.