Salesforce/Lightning Component

[Aura Component] Aura Components Basics (아우라 컴포넌트 기초 과정) (작성중)

RHBY 2022. 4. 6. 12:58

※ 본 글은 https://trailhead.salesforce.com/content/learn/modules/lex_dev_lc_basics?trailmix_creator_id=leicsonbp&trailmix_slug=development-with-aura-components 의 일부 내용을 번역한 글 입니다.

 

라이트닝 컴포넌트 프레임워크란?

모바일 및 데스크톱 장치용 웹 또는 앱을 개발하기 위한 UI 프레임워크로, 라이트닝 플랫폼 기반의 앱을 역동적이고 응답성이 뛰어난 사용자 인터페이스로 구성하여 SPA 기반의 애플리케이션으로 구축하기 위한 현대적 프레임워크이다. 클라이언트는 JavaScript를 사용하고 서버측에서는 Apex를 사용한다.

 

앱 프레임워크는 모든 코드를 직접 작성하지 않고도 사용자 지정 앱을 쉽게 만들 수 있는 코드 및 서비스 모음이다. 이러한 프레임워크에는 Ruby on Rails(RPG메이커에서 사용하던..), Grails, AngularJS(프론트 프레임워크 3대장), Django, CakePHP 등 다양한 웹 앱 프레임워크가 있다. Salesforce 플랫폼에서는 Lightning Component 프레임워크를 사용한다. 기반은 모바일 어플리케이션이며 모바일 및 데스크톱 장치 모두에서 작동하는 앱(WPA)을 개발하는 것이 다른 프레임워크에 비해 간편하다.

 

MVC 모델의 Controller 부분을 프론트에서는 JavaScript로, 서버에서는 Apex로 처리한다.

라이트닝 컴포넌트 예제

<aura:component>
    <aura:attribute name="expense" type="Expense__c"/>
    <aura:registerEvent name="updateExpense" type="c:expensesItemUpdate"/>
    <!-- Color the item green if the expense is reimbursed -->
    <lightning:card title="{!v.expense.Name}" iconName="standard:scan_card"
                    class="{!v.expense.Reimbursed__c ?
                           'slds-theme_success' : ''}">
        <aura:set attribute="footer">
            <p>Date: <lightning:formattedDateTime value="{!v.formatdate}"/></p>
            <p class="slds-text-title"><lightning:relativeDateTime value="{!v.formatdate}"/></p>
        </aura:set>
        <p class="slds-text-heading_medium slds-p-horizontal_small">
            Amount: <lightning:formattedNumber value="{!v.expense.Amount__c}" style="currency"/>
        </p>
        <p class="slds-p-horizontal_small">
           Client: {!v.expense.Client__c}
        </p>
        <p>
            <lightning:input type="toggle"
                             label="Reimbursed?"
                             name="reimbursed"
                             class="slds-p-around_small"
                             checked="{!v.expense.Reimbursed__c}"
                             messageToggleActive="Yes"
                             messageToggleInactive="No"
                             onchange="{!c.clickReimbursed}"/>
        </p>
    </lightning:card>
</aura:component>

XML 마크업의 구조이며, <aura:component> 태그와 같은 사용자 지정 Aura 구성 요소 태그(XML 형태)와 HTML 태그를 혼용하여 작성한다. Visualforce에서 사용한 <네임스페이스:태그명> 구조와 유사하며, 기본적으로 제공되는 네임스페이스 구성 요소는 aura, force, lightning, ui와 같은 다양한 네임스페이스가 있다.

 

토글 스위치의 onchange 특성의 경우, 선택 및 선택 취소된 값을 나타내기 위해 오른쪽과 왼쪽으로 슬라이드 하는 체크박스이다. 해당 이벤트(토글)가 실행되면 JavaScript의 함수가 호출된다.

({
    clickReimbursed: function(component, event, helper) {
        let expense = component.get("v.expense");
        let updateEvent = component.getEvent("updateExpense");
        updateEvent.setParams({ "expense": expense });
        updateEvent.fire();
    }
})

아우라 컴포넌트는 코드의 묶음으로써, 기존에 작성한 마크업 기반의 소스를 .cmp 리소스에 포함할 수 있고, JavaScript 코드를 수많은 관련 리소스에 포함할 수도 있다. 관련된 리소스들은 서로 자동으로 배치되며 컴포넌트 번들을 사용할 수 있다. Visualforce 기반으로 작성 된 애플리케이션은 계속해서 Salesforce에서 실행되므로 라이트닝 컴포넌트에 맞게 재구성할 필요는 없지만, 모바일 환경을 고려하지 않은 언어이므로 모바일 장치에 적합한 애플리케이션을 구성하기 위해서는 라이트닝 컴포넌트를 사용하는 것이 좋다.

 

사용자 개체 Expense 만들기

해당 과정을 진행하려면 사용자 개체를 만들어 사용해야 한다. 필요한 사용자 개체 요소는 다음과 같다.

 

Aura Component 만들고 편집하기

직접 Hello World를 출력하는 아우라 컴포넌트 기반 애플리케이션을 만들어보자.

좌측은 라이트닝 컴포넌트, 우측은 라이트닝 앱이다. 우선 컴포넌트에 문단 태그를 사용해 Hello World를 작성했다. 그 후, 라이트닝 앱에 탑재하여 앱을 실행하는 것으로 결과를 확인할 수 있다.

실행 결과
도전 과제

1. campingList 라이트닝 컴포넌트를 만들고 Bug Spray, Bear Repellant, Goat Food를 리스트로 갖는 <ol> 목록 만들기

2. campingHeader 라이트닝 컴포넌트를 만들고 폰트 크기가 18px인 <h1> 태그에 Camping List를 작성하기

3. camping 라이트닝 컴포넌트를 만들고, campingHeader, campingList를 포함시키기

campingList
campingHeader
camping

속성과 표현식 다루기

위에서 다룬 컴포넌트들은 모두 정적인 컴포넌트들이다. 이제 속성과 표현식을 통해 동적인 컴포넌트를 설정하는 방법에 대해 알아보자.

 

컴포넌트에 컴포넌트를 주입할 때(<c:componentName/>), 사용자 정의 속성에 값을 부여해 컴포넌트를 동적으로 관리할 수 있다. 말로는 설명하기가 굉장히 어렵다. 아래의 소스를 확인하자.

<aura:component>
    <aura:attribute name="message" type="String"/>
    <p>Hello! {!v.message}</p>
</aura:component>
<aura:component>
    <c:helloMessage message="You look nice today."/>
</aura:component>

아래의 컴포넌트는 helloMessage 컴포넌트를 포함시키는 태그를 사용하고 있다. 이 때, message라는 사용자 정의 속성에 문자열 값을 부여했고, helloMessage 컴포넌트에서는 이를 <aura:attribute>로 받아 문단에서 표현식으로 출력될 수 있도록 작성했다. 이 처럼 부모 컴포넌트에서 사용자 정의 속성을 통해 자식 컴포넌트에게 값을 부여할 수 있다.

 

도전 과제

1. campingListItem 컴포넌트 만들기

2. item으로 호출할 수 있고 필수 값을 가지는 Camping_Item__c 개체를 속성으로 선언하기

3. 표현식을 사용해 Name, Price__c, Quantity__c, Packed__c 필드를 출력하기

4. <lightning:formattedNumber> 태그를 사용해 Price__c, Quantity__c 필드를 출력하기

5. <lightning:input type="toggle"> 태그를 사용해 Packed__c 필드를 출력하기

campingListItem

Controller로 작업 처리하기

이번에는 컴포넌트의 마크업을 변경해서 동적 컴포넌트를 구성하는 것이 아닌, 사용자의 입력에 반응하는 동적 컴포넌트를 구성하는 과정을 알아보자. <lightning:button> 태그를 통해 라이트닝 컴포넌트 버튼을 만들 수 있으며, HTML의 button 태그와 마찬가지로 onclick 속성을 통해 클릭 시 이벤트 JavaScript 함수를 지정하여 호출할 수 있다.

 

<aura:component>
    <aura:attribute name="message" type="String"/>
    <p>Message of the day: {!v.message}</p>
    <div>
        <lightning:button label="You look nice today."
            onclick="{!c.handleClick}"/>
        <lightning:button label="Today is going to be a great day!"
            onclick="{!c.handleClick}"/>
    </div>
</aura:component>

위의 소스에서 c는 컨트롤러에 대한 값 제공자이며, 버튼을 클릭하면 JavaScript에서 handleClick 이라는 함수를 찾아 실행한다는 뜻이다. 해당 함수의 컨트롤러 소스는 다음과 같다.

({
    handleClick: function(component, event, helper) {
    /*
        component: 이벤트가 실행 된 컴포넌트 (component)
        event: 핸들러를 호출한 이벤트 (button)
        helper: 또 다른 JavaScript 리소스 (Function)
    */
        let btnClicked = event.getSource();         // the button
        let btnMessage = btnClicked.get("v.label"); // the button's label
        component.set("v.message", btnMessage);     // update our message
    }
})

해당 컨트롤러와 컴포넌트 간 이벤트 실행 순서도

해당 이벤트가 발생한 버튼 컴포넌트를 변수화 하여 저장한 다음, 해당 컴포넌트의 라벨 값을 가져와 변수에 저장한다. 그리고 뷰의 message 속성 컴포넌트의 값을 라벨 값으로 치환한다.

도전 과제

1. Packed! 라는 라벨을 붙인 button을 생성하고, 클릭 시 packItem 컨트롤러 함수를 호출하도록 구성하기

2. 해당 컨트롤러 함수를 통해 item 속성을 true 값을 갖는 Packed__c로 치환되도록 하기

3. 해당 버튼을 누르면 disabled로 변경되도록 구성하기

campingListItem 에 해당 태그 추가
컨트롤러 함수 추가

양식을 사용해 데이터 입력하기

지금까지 사용한 아우라 컴포넌트는 Visualforce에서 사용한 컴포넌트들 처럼 아름답지 못하거나, 정돈되지 못한 형태의 컴포넌트처럼 보였다. 그렇다면 Salesforce 플랫폼의 기본적인 컴포넌트들처럼 디자인되게 구성하려면 어떻게 해야할까? 정답은 바로 Salesforce Lightning Design System(SLDS)에 있다.

 

SLDS는 Salesforce 기반의 애플리케이션에서 실행되면 자동으로 사용할 수 있다. 하지만 독립 실행형 앱이나 Lightning Out, Lightning Components for Visualforce에서 컴포넌트를 사용하는 경우에는 기본적으로는 사용할 수 없다. 하지만 우리는 이러한 환경에서도 이를 사용해 더 나은 환경의 UI 기반 애플리케이션을 제공해야 한다. 이럴 때에는 어떻게 해야할까?

 

방법은 애플리케이션에 SLDS를 상속받아 추가하는 것이다. 직접 소스코드로 추가하는 방법을 알아보자.

<aura:application extends="force:slds">
        <!-- This component is the real "app" -->
        <!-- c:expenses/ -->
</aura:application>

<aura:application> 태그에 extends 속성을 달아 force:slds를 추가했다. 해당 속성은 Lightning Experience 및 Salesforce 앱에서 제공하는 동일한 SLDS 스타일을 포함하여 해당 애플리케이션에서 SLDS를 활성화하는 속성이다.

 

이제 해당 애플리케이션에서는 SLDS를 이용할 수 있게 되었다. expense 컴포넌트를 다음과 같이 수정하여 Lightning Experience 기반의 UI를 구성할 수 있다.

<aura:component>
    <aura:attribute name="newExpense" type="Expense__c"
     default="{ 'sobjectType': 'Expense__c',
                    'Name': '',
                    'Amount__c': 0,
                    'Client__c': '',
                    'Date__c': '',
                    'Reimbursed__c': false }"/>
    <aura:attribute name="expenses" type="Expense__c[]"/>
    <!-- PAGE HEADER -->
    <lightning:layout class="slds-page-header slds-page-header_object-home">
        <lightning:layoutItem>
            <lightning:icon iconName="standard:scan_card" alternativeText="My Expenses"/>
        </lightning:layoutItem>
        <lightning:layoutItem padding="horizontal-small">
            <div class="page-section page-header">
                <h1 class="slds-text-heading_label">Expenses</h1>
                <h2 class="slds-text-heading_medium">My Expenses</h2>
            </div>
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / PAGE HEADER -->
    <!-- NEW EXPENSE FORM -->
    <lightning:layout>
        <lightning:layoutItem padding="around-small" size="6">
        	<!-- CREATE NEW EXPENSE -->
            <div aria-labelledby="newexpenseform">
                <!-- BOXED AREA -->
                <fieldset class="slds-box slds-theme_default slds-container_small">
                <legend id="newexpenseform" class="slds-text-heading_small
                  slds-p-vertical_medium">
                  Add Expense
                </legend>
                <!-- CREATE NEW EXPENSE FORM -->
                <form class="slds-form_stacked">
                    <lightning:input aura:id="expenseform" label="Expense Name"
                                     name="expensename"
                                     value="{!v.newExpense.Name}"
                                     required="true"/>
                    <lightning:input type="number" aura:id="expenseform" label="Amount"
                                     name="expenseamount"
                                     min="0.1"
                                     formatter="currency"
                                     step="0.01"
                                     value="{!v.newExpense.Amount__c}"
                                     messageWhenRangeUnderflow="Enter an amount that's at least $0.10."/>
                    <lightning:input aura:id="expenseform" label="Client"
                                     name="expenseclient"
                                     value="{!v.newExpense.Client__c}"
                                     placeholder="ABC Co."/>
                    <lightning:input type="date" aura:id="expenseform" label="Expense Date"
                                     name="expensedate"
                                     value="{!v.newExpense.Date__c}"/>
                    <lightning:input type="checkbox" aura:id="expenseform" label="Reimbursed?"
                                     name="expreimbursed"
                                     checked="{!v.newExpense.Reimbursed__c}"/>
                    <lightning:button label="Create Expense"
                                      class="slds-m-top_medium"
                                      variant="brand"
                                      onclick="{!c.clickCreate}"/>
                </form>
                <!-- / CREATE NEW EXPENSE FORM -->
              </fieldset>
              <!-- / BOXED AREA -->
            </div>
            <!-- / CREATE NEW EXPENSE -->
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / NEW EXPENSE FORM -->
</aura:component>

실행 결과

특정 데이터 유형을 사용해 날짜 필드에 type="date" 속성을 부여 하는 등, 여러 개의 <lightning:input> 컴포넌트 인스턴스를 만들고 있다. 다양한 유형들이 있으며, 컴포넌트 유형을 데이터 유형과 일치시키는 것이 중요하다. 유형은 지정하지 않을 경우 기본적으로 텍스트 유형이 되며, 유형 별 컴포넌트는 폼 입력에 가장 적합한 입력 위젯을 제공한다.

Expense Date 입력 시, 다음과 같이 입력에 용이한 달력 위젯이 출력된다.

<lightning:input> 태그의 속성으로 지정할 수 있는 내용들은 required, placeholder, type, min, step 처럼 HTML에서 사용하는 속성들과 유사하며, 속성 별 사용처는 라이트닝 컴포넌트 라이브러리를 통해 찾아볼 수 있다.

https://developer.salesforce.com/docs/component-library

 

Components - Salesforce Lightning Component Library

General Information We use three kinds of cookies on our websites: required, functional, and advertising. You can choose whether functional and advertising cookies apply. Click on the different cookie categories to find out more about each category and to

developer.salesforce.com

특이사항으로 각 태그에 aura:id 속성이 설정되어있는데, 이 속성은 태그에 고유한 ID를 설정하여 해당 필드 값을 읽기 위한 수단이다. 동일한 ID를 공유하는 경우 필드 유효성 검사를 위한 과정에서 개별 필드가 아닌 필드 배열로 접근할 수 있다.

 

sObject 개체의 필드 값을 페이지에 표현식으로 사용할 때, 기본 값을 지정할 수 있다. default 속성으로 기본 값을 지정할 수 있으며, NullPoint 예외를 JavaScript 또는 마크 업 방식으로 처리할 수 있는 효율적인 방법이다.

 

이제 Create Expense 버튼의 동작 이벤트를 구성하는 컨트롤러 함수를 확인해보자.

({
    clickCreate: function(component, event, helper) {
        let validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
            // Displays error messages for invalid fields
            inputCmp.showHelpMessageIfInvalid();
            return validSoFar && inputCmp.get('v.validity').valid;
        }, true);
        // If we pass error checking, do some real work
        if(validExpense){
            // Create the new expense
            let newExpense = component.get("v.newExpense");
            console.log("Create expense: " + JSON.stringify(newExpense));
            helper.createExpense(component, newExpense);
        }
    }
})

<lightning:button> 태그를 통해 지정된 onclick 속성에는 clickCreate 함수를 호출하는 이벤트가 설정되어있다. clickCreate 컨트롤러 함수의 구조를 확인해보자. 우선 validExpense 변수에 컴포넌트의 expenseform을 ID값으로 가지는 모든 필드 값을 가져와 함수를 실행한다. 함수에는 필수 값 입력 여부를 검사하며, 입력이 모두 정상적으로 완료 되었다면 true 값을 반환한다. 

 

하단의 조건문에서는 상기한 필수 값 입력 여부 검사가 정상적으로 완료 된 경우, 컴포넌트 내에 newExpense로 선언된 Expense__c 개체를 가져와 콘솔로 출력하고, 파라미터로 해당 개체를 포함시켜 helper 리소스의 createExpense 함수를 호출하고 종료된다.

({
    createExpense: function(component, expense) {
        let theExpenses = component.get("v.expenses");
        // Copy the expense to a new object
        // THIS IS A DISGUSTING, TEMPORARY HACK
        let newExpense = JSON.parse(JSON.stringify(expense));
        theExpenses.push(newExpense);
        component.set("v.expenses", theExpenses);
    }
})

helper 리소스의 createExpense 함수에서는 expense__c[] 배열 개체에 새로 입력 된 expense__c 개체를 입력하는 과정이 포함되어있다. 

 

도전 과제

1. <aura:attribute> 태그를 통해 name 속성을 items로 갖는 배열 형태의 Capming_Item__c 생성하기

2. <aura:attribute> 태그를 통해 name 속성을 newItem 으로, Quantity__c 필드와 Price__c 필드의 기본값을 0으로 갖는 Camping_Item__c 생성하기

3. 적절한 Input 구성 요소를 사용하여 newItem 속성의 값을 출력하기

4. Quantity__c 필드는 1 이상의 숫자를 입력하도록 설정하기 (step 속성)

5. form submit 처리를 하면 clickCreateItem 컨트롤러 함수를 실행시킬 수 있도록 구성한다.

6. 폼 양식에 유효한 값이 입력되었다면 JavsScript 컨트롤러 함수는 새로 입력 된 폼 양식 값들을 sObject 배열에 push하고 이에 대한 알림 트리거를 구성한다. (console.log) 그 후 입력 된 값들은 모두 지우고 새 값들을 넣어 초기 상태로 만들어준다.

 

camping
campingList
campingListItem
campingListController
결과 적용

서버 Controller를 사용해 Salesforce에 연결하기

지금까지의 과정은 완전하게 클라이언트 영역에서만 작업할 수 있는 내용들이었다. 이전 파트의 도전과제에서도 지금까지 입력한 캠핑 용품 개체의 배열 또한 단순 출력 목적으로만 작성되었다. 이번 과정을 통해 앞서 작성했던 개체의 배열을 Salesforce 개체 데이터화 하여 사용할 수 있도록 서버 사이드 컨트롤러와 연결해보자.

 

Client - Server 간 프로세스 진행 구조

클라이언트 - 서버 간 통신의 기본적인 구조는 비동기 통신, 그리고 콜백 함수를 통한 실행 유지이다.

 

클라이언트 측 컨트롤러는 서버 Request를 실행한 직후, 서버 Response를 기다리지않고 클라이언트 컨트롤러의 남은 처리를 계속한다. 서버 Response가 돌아오면 Callback 함수를 통해 패키지 코드를 실행하고 클라이언트 데이터, UI 업데이트를 포함한 응답 처리를 진행한다.

 

더보기

Callback 함수

 

우리가 익히 알고 있는 프로그래밍의 진행 방식은 소스 코드의 최상단부터 최하단까지 순차적으로 실행 순서를 갖는 동기 방식이다. 이러한 동기 방식의 특징은 이전 소스 코드가 처리 진행 중일 때 이후의 소스 코드를 실행하지 않는 점이다.

 

비동기 방식은 이와는 반대로 소스 코드의 순서는 그대로 유지하되, 이전 소스 코드가 처리 중임에도 불구하고 다음 소스 코드를 실행한다는 것이다. 상기한 프로세스 진행 구조도를 확인해보자. 만약 3번처럼 클라이언트 컨트롤러가 서버 컨트롤러에 이벤트 요청을 진행했을 때, 서버에서는 3번의 요청을 처리하는 동안 클라이언트 컨트롤러는 4번을 요청할 것이고, 3번에 대한 요청이 4번과 연관되어 있을 경우 (예를 들어, 3번의 처리 결과를 4번의 요청에 대한 파라미터로 제공하거나 하는 경우) 처리에 문제가 발생하게 될 것이다.

 

이러한 현상을 방지하기 위해, 비동기 방식 프로그래밍에서는 실행 시점을 개발자가 지정하여 수행할 수 있는 함수를 제공하는데 이를 Callback 함수라고 한다.

이제 간단한 Apex 컨트롤러를 만들어 구조를 확인해보자.

public with sharing class ExpensesController {
    // STERN LECTURE ABOUT WHAT'S MISSING HERE COMING SOON
    @AuraEnabled
    public static List<Expense__c> getExpenses() {
        return [SELECT Id, Name, Amount__c, Client__c, Date__c,
                       Reimbursed__c, CreatedDate
                FROM Expense__c];
    }
}

확인하기에 앞서, @AuraEnabled 어노테이션을 알아보자. @AuraEnabled 어노테이션은 라이트닝 컴포넌트의 핵심 프레임워크로 static 컨트롤러 메서드에 적용 가능하고 공용, 전역 범위여야 설정할 수 있다. 해당 메서드를 호출하게 되면 Expense__c 개체의 필드를 조회하여 List 컬렉션 형태로 제공한다.

 

아우라 컴포넌트에 서버 컨트롤러를 참조하는 방법은 다음과 같다.

<aura:component controller="ExpensesController">

이제 첫 컴포넌트 실행 시, 서버 컨트롤러를 통해 기존의 입력 된 Expense 개체들을 가져와 출력하는 과정을 만들어보자.

<aura:handler name="init" action="{!c.doInit}" value="{!this}"/>

우선 컴포넌트 태그 내부에 이벤트 핸들러를 생성한다. 페이지가 처음으로 실행되면 실행된 컴포넌트를 대상으로 클라이언트 컨트롤러에 있는 doInit 컨트롤러 메서드를 호출하는 이벤트 핸들러이다.

// Load expenses from Salesforce
    doInit: function(component, event, helper) {
        // Create the action
        let action = component.get("c.getExpenses");
        // Add callback behavior for when response is received
        action.setCallback(this, function(response) {
            let state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.expenses", response.getReturnValue());
            }
            else {
                console.log("Failed with state: " + state);
            }
        });
        // Send action off to be executed
        $A.enqueueAction(action);
    },

doInit 컨트롤러 메서드는 action 변수를 선언하고 서버 컨트롤러 메서드인 c.getExpenses를 초기 값으로 지정한다. 그 후, action 변수에 setCallback 콜백 함수를 선언해 응답을 받아 다음 과정을 진행하는 함수를 생성한다. 응답 결과가 성공인 경우 컴포넌트의 expenses 개체에 결과 값을 반환하고 컨트롤러 메서드를 종료한다.

더보기

참고로 마크업 기반 컴포넌트에서 사용하는 컨트롤러 호출 식별자와 클라이언트 컨트롤러 내에서 사용하는 컨트롤러 호출 식별자는 서로 다른 의미를 갖는다. 위 사진의 예시처럼 마크업 기반 컴포넌트에서의 c. 식별자는 클라이언트 컨트롤러를 의미하고, 클라이언트 컨트롤러에서 c. 식별자는 서버 컨트롤러를 의미한다.

특이사항으로, $A.enqueueAction(action); 코드에 사용된 $A 식별자는 아우라 컴포넌트에 대한 식별자이다. 해당 코드는 아우라 컴포넌트 프레임워크의 Request queue에 해당 응답 처리 과정을 서버 호출에 추가한다는 의미로, 다른 보류 중인 서버 Request의 다음 요소로 추가된다.

도전 과제

1. getItems, saveItem 서버 컨트롤러 메서드를 갖는 Apex 클래스 CampingListController.cls 생성하기

2. 컴포넌트 실행 시 DB에서 입력되어있는 개체들을 불러오는 doInit 이벤트 핸들러 작성하기

3. 자바스크립트 컨트롤러 helper에 createItem 메서드를 작성하고 입력 한 캠핑 Item을 DB에 저장하는 과정 작성하기

더보기

여담으로, 해당 Trailhead에 대한 정답을 제공하는 유튜브에서 전혀 맞지 않는 소스 코드를 배포해 아직도 외국인 개발자들이 댓글에서 정답을 구걸(?) 하는 광경을 봤다. 다행히 해결하기 어려운 문제는 아니었기에 정답 소스를 댓글로 배포했다.

 

정답을 원하는 외국인들 (작성자가 답글로 새로운 소스 코드를 배포했지만 그 마저도 클라이언트 컨트롤러 메서드명이 다르고 컴포넌트 모듈화 때문에 정답 처리가 안되는 상황이었다.)

 

답변 전문은 해당 유튜브 댓글에 작성하였다. https://www.youtube.com/watch?v=ov0lQbtRu24

campingList.cmp
campingListController.cls
campingListController.js
campingListHelper.js

이벤트로 컴포넌트 연결하기

<lightning:input type="toggle"
            label="Reimbursed?"
            name="reimbursed"
            class="slds-p-around_small"
            checked="{!v.expense.Reimbursed__c}"
            messageToggleActive="Yes"
            messageToggleInactive="No"
            onchange="{!c.clickReimbursed}"/>

우선 이벤트를 보내려면 이벤트를 실행시킬 수 있는 컴포넌트를 지정해야한다. Expense 앱에서는 toggle 형태의 input 태그를 사용한다. 체크 여부는 expense 개체의 Reimbursed__c 필드 값을 참조하고 onchange 이벤트로 clickReimbursed 클라이언트 컨트롤러 메서드를 호출한다.

 

({
    clickReimbursed: function(component, event, helper) {
        let expense = component.get("v.expense");
        let updateEvent = component.getEvent("updateExpense");
        updateEvent.setParams({ "expense": expense });
        updateEvent.fire();
    }
})

clickReimbursed 클라이언트 컨트롤러 메서드에서는 입력 된 비용 개체 정보와 지정된 이벤트를 가져온다. 이벤트에 비용 개체 정보를 파라미터로 넘겨 실행한다. 이벤트 구조는 다음과 같다.

<aura:event type="COMPONENT">
    <aura:attribute name="expense" type="Expense__c"/>
</aura:event>

이벤트는 컴포넌트와 응용 프로그램 두 가지 유형이 있다. 이번 과정에서는 부모 컴포넌트가 이벤트를 찾아 처리하는 구성이기에 컴포넌트 방식을 사용한다. 이벤트를 정의할 때에는 간결해야하며, 속성은 다른 요소에 전달할 수 있는 네임스페이스를 포함하여야한다.

 

해당 아우라 이벤트는 클라이언트 컨트롤러 메서드를 통해 비용 개체 정보를 받아와 속성으로 저장한다.

<aura:registerEvent name="updateExpense" type="c:expensesItemUpdate"/>

이벤트 내에 클라이언트 컨트롤러 메서드 expensesItemUpdate 타입의 registerEvent 태그를 선언한다. 이는 패키지 형태의 정의 방식이며 컴포넌트에서 이벤트 전송을 등록하면 이벤트가 사용하는 형식을 선언한다.

<aura:handler name="updateExpense" event="c:expensesItemUpdate"
        action="{!c.handleUpdateExpense}"/>

이제 컴포넌트에 이벤트 핸들러를 선언한다. 이 때 속성으로 expensesItemUpdate 타입의 이벤트를 선언하고 handleUpdateExpense 클라이언트 컨트롤러 메서드를 실행하는 액션을 지정한다. 

    handleUpdateExpense: function(component, event, helper) {
        let updatedExp = event.getParam("expense");
        helper.updateExpense(component, updatedExp);
    }

handleUpdateExpense 클라이언트 컨트롤러 메서드 액션은 이벤트를 통해 파라미터에 저장 된 비용 개체 정보를 가져와 helper의 updateExpense 메서드를 실행한다.

updateExpense: function(component, expense) {
        let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                // do nothing!
            }
        });
        $A.enqueueAction(action);
    },

helper의 updateExpense 메서드는 서버 컨트롤러의 saveExpense 메서드에 비용 개체 정보를 파라미터로 전달하고 비동기 방식으로 프로세스를 실행시킨다.

 

전체적으로 실행 프로세스를 정리하면 다음과 같다.

더보기
1.이벤트 대상이 되는 input 컴포넌트가 실행되면
이벤트 시작 전에 필요한 액션을 담은 클라이언트 컨트롤러 메서드가 실행된다. (파라미터 가져오기 등)
2.이벤트에 1번의 처리 결과를 포함시키고, registerEvent동작시킨다.
3.2번에 의해 컴포넌트의 핸들러가 동작하여 이벤트 액션을 담은 클라이언트 컨트롤러 메서드가 실행된다.
4.3번에 의해 비동기 방식의 updateExpense 메서드가 동작한다.
5.성공 시 별도의 동작을 구성하지 않았으므로 이벤트 실행이 종료된다.

이벤트 실행 알고리즘
도전 과제

1. HTML form을 campingListForm 으로 분리하여 리팩터링하고, 해당 컴포넌트에서 체크박스 선택 시 clickCreateItem 클라이언트 컨트롤러 메서드가 작동할 수 있도록 구성하기

2. campingList 컴포넌트에서 addItemEvent 이벤트가 실행될 수 있도록 구성하고, handleAddItem 메서드를 클라이언트 컨트롤러 메서드에 구성하기

3. handleAddItem 메서드는 데이터베이스에 데이터를 추가하고, items 개체에 더하도록 구성하기

4. addItemEvent 이벤트는 Camping_Item__c 타입의 속성을 갖고 item으로 명명된 컴포넌트로 구성하기

5. campingListForm 에 addItemEvent 타입의 addItem으로 명명된 registerEvent 추가하기

6. campingListForm의 클라이언트 컨트롤러에 helper.js를 통해 createItem 메서드를 실행하는 구간 추가하기

7. campingListForm의 helper.js에 item 데이터를 데이터베이스에 추가하고 기존에 입력 된 값을 초기화하는 Camping_Item__c 타입의 newItem 변수를 생성하여 적용하는 addItem 이벤트를 구성하기