회고

[수습일지] - 29

Happy Programmer 2023. 4. 24. 23:14

1.버튼을 클릭할 경우 값을 받아와 뿌려주는 기능은 wire를 사용할 필요 없이

버튼을 누르면 해당 값을 넣어주기만 하면 된다.

//html
<template>
    <lightning-card title="Wired with Function" icon-name="utility:animal_and_nature">
        <div>
            <lightning-button label="Get Accounts" onclick={buttonClick}>

            </lightning-button>
        </div>
        <template if:true={accounts}>
            <div>
                <template for:each={accounts} for:item='acc'>
                    <lightning-layout key={acc.Id}>
                        <lightning-layout-item flexibility="grow">
                            {acc.Name}
                        </lightning-layout-item>
                    </lightning-layout>
                </template>
            </div>
            <div>
                <br />
                {error}
            </div>
        </template>
    </lightning-card>
</template>

//js
import { LightningElement } from "lwc";
import getAccountList from "@salesforce/apex/AccountController.getAccList";

export default class BindImperativewithParam extends LightningElement {
  accounts;
  error;

  buttonClick() {
    getAccountList()
      .then((result) => {
        this.accounts = result;
        this.error = undefined;
      })
      .catch((error) => {
        this.accounts = undefined;
        this.error = error;
      });
  }
}

 

 

2.실시간으로 값을 입력받아 해당 값을 필터링하며 요청하는 방법은

변수를 담은 class를 생성한 다음 해당 class를 wire로 연결하고

매개 변수를 전달하는 방식으로 진행하며

해당 매개 변수는 onchange로 변경하는 방식으로 사용할 수 있다.

//class 여기서 쓰는건 아래의 findAccList
public with sharing class AccountController {
  @AuraEnabled(cacheable=true)
  public static List<Account> getAccList() {
    return [SELECT Id, Name FROM ACCOUNT ORDER BY CreatedDate DESC LIMIT 10];
  }

  @AuraEnabled(cacheable=true)
  public static List<Account> findAccList(String keyword) {
    String key = '%' + keyword + '%';
    return [
      SELECT Id, Name, Phone
      FROM ACCOUNT
      WHERE Name LIKE :key
      ORDER BY CreatedDate DESC
    ];
  }
}

//html
<template>
    <lightning-card title="Wired with Param" icon-name="utility:animal_and_nature">
        <div>
            <lightning-input onchange={handleonchange} label="Search Account" value={searchKey}>

            </lightning-input>
        </div>
        <template if:true={accounts.data}>
            <div>
                <template for:each={accounts.data} for:item='acc'>
                    <lightning-layout key={acc.Id}>
                        <lightning-layout-item flexibility="grow">
                            {acc.Name}
                        </lightning-layout-item>
                    </lightning-layout>
                </template>
            </div>
            <div>
                <br />
                {error}
            </div>
        </template>
    </lightning-card>
</template>

//js
import { LightningElement, wire } from "lwc";
import findAccountList from "@salesforce/apex/AccountController.findAccList";

export default class BindWirewithParam extends LightningElement {
  searchKey = "";

  @wire(findAccountList, { keyword: "$searchKey" })
  accounts;

  handleonchange(event) {
    this.searchKey = event.target.value;
  }
}

 

 

3.필요한 값에 들어가는 단어를 입력해 검색하는 기능을 위해서

wire를 사용할 필요 없이 해당 클래스에 바로 요청한 다음 이벤트 작동을 통해 실행할 수 있다.

 

위에서 진행한 필터링과의 차이를 찾아보자면

위에서 진행한 wire를 통한 필터링은 입력값에 따른 실시간 값 변동을 볼 수 있지만

지금은 직접 버튼 등의 클릭을 통해 동작을 지시할 때만 값을 받아오는 방식이다.

 

기존 wire 방식은 검색어 추천 등의 기능에 어울리고

현재의 코드는 조회를 하기 위해 키워드를 넣을 때 어울린다.

 

거의 대부분의 상황에서 당연히 wire 방식이 더 편의적일 수는 있지만

실시간으로 글자를 적은 모든 내용을 쿼리 요청을 하며

초반에 공백에 가까운 검색 명령을 수백만개 이상의 데이터에 요청할 경우

지연될 가능성도 높기 때문에 작은 양의 리스트 등에 사용하거나

어느 정도 시간 지연을 주는 쓰로틀링 같은 기법을 적용하며 사용해야 할 것 같고

지금의 버튼 클릭식 작동은 사용자가 원하는 키워드를 단 한번의 쿼리로 가져오기 때문에

데이터 낭비도 없고 서버에 부담도 없는 효율적인 방식이라고 볼 수 있다.

(효율 vs 편의)

//html
<template>
    <lightning-card title="Bind Imperative Method with Params" icon-name="utility:animal_and_nature">
        <div>
            <lightning-input onchange={handleonchange} label="Search Account" value={searchKey}>

            </lightning-input>
        </div>
        <div>
            <lightning-button label="Get Accounts" onclick={buttonClick}>

            </lightning-button>
        </div>
        <template if:true={accounts}>
            <div>
                <template for:each={accounts} for:item='acc'>
                    <lightning-layout key={acc.Id}>
                        <lightning-layout-item flexibility="grow">
                            {acc.Name}
                        </lightning-layout-item>
                    </lightning-layout>
                </template>
            </div>
            <div>
                <br />
                {error}
            </div>
        </template>
    </lightning-card>
</template>

//js
import { LightningElement } from "lwc";
import findAccountList from "@salesforce/apex/AccountController.findAccList";

export default class BindImperativewithParam extends LightningElement {
  searchKey = "";
  accounts;
  error;

  handleonchange(event) {
    this.searchKey = event.target.value;
  }

  buttonClick() {
    findAccountList({ keyword: this.searchKey })
      .then((result) => {
        this.accounts = result;
        this.error = undefined;
      })
      .catch((error) => {
        this.accounts = undefined;
        this.error = error;
      });
  }
}

 

 

4.Apex에서 제공하는 Schema를 통해서도 값에 접근할 수 있는데

‘@salesforce/schema/Account.Name’ 등으로 schema에서 원하는 필드명을 가져온 다음

해당 필드를 넣는 방식이다.

 

굳이 data.account.data.name 등으로 접근하지 않는 이유는 모르겠지만

혹시 쓸 일이 있을 수 있어 정리는 해둔다.

//class
public with sharing class AccountController {
  @AuraEnabled(cacheable=true)
  public static List<Account> getAccList() {
    return [SELECT Id, Name FROM ACCOUNT ORDER BY CreatedDate DESC LIMIT 10];
  }

  @AuraEnabled(cacheable=true)
  public static List<Account> findAccList(String keyword) {
    String key = '%' + keyword + '%';
    return [
      SELECT Id, Name, Phone
      FROM ACCOUNT
      WHERE Name LIKE :key
      ORDER BY CreatedDate DESC
    ];
  }
  @AuraEnabled(cacheable=true)
  public static Account getSingleAccount() {
    return [
      SELECT Id, Name, Phone
      FROM ACCOUNT
      ORDER BY CreatedDate DESC
      LIMIT 1
    ];
  }
}

//html
<template>
    <lightning-card title="Bind Using APEX Schema" icon-name="utility:animal_and_nature">
        <template if:true={account.data}>
            <div>
                <b>Name : {name}</b>
                <br>
                Phone : {phone}
            </div>
        </template>
        <template if:false={account.data}>
            <div>
                account.data = false
            </div>
        </template>
    </lightning-card>
</template>

//js
import { LightningElement, wire } from "lwc";
import { getSObjectValue } from "@salesforce/apex";
import getSingleAcc from "@salesforce/apex/AccountController.getSingleAccount";
import NAME_FIELD from "@salesforce/schema/Account.Name";
import PHONE_FIELD from "@salesforce/schema/Account.Phone";

export default class BindUsingApexSchema extends LightningElement {
  @wire(getSingleAcc) account;
  get name() {
    return this.account.data
      ? getSObjectValue(this.account.data, NAME_FIELD)
      : "";
  }
  get phone() {
    return this.account.data
      ? getSObjectValue(this.account.data, PHONE_FIELD)
      : "";
  }
}

 

 

5.부모 객체의 값을 자식이 가져오려는 경우 @api를 사용해서 받아와 사용할 수 있다.

다만 params 느낌으로 내려가기 때문에 내려준 이름이 자식 입장에서는 공식 용어가 된다.

//parent html
<template>
    <lightning-card title="I am Parent" icon-name="utility:animal_and_nature">
        <div class="slds-var-m-around_medium">
            <c-child-Component-Basic mydata={myData}>
            </c-child-Component-Basic>
        </div>
    </lightning-card>
</template>

//parent js
import { LightningElement } from "lwc";

export default class ParentComponentBasic extends LightningElement {
  myData = { Name: "Yohan", Company: "windmillSoft" };
}

//child html
<template>
    <lightning-card title="I am Child" icon-name="utility:animal_and_nature">
        <template if:true={mydata}>
            <div class="slds-var-m-around_medium">
                <b>{mydata.Name}
                    <br>
                    {mydata.Company}
                </b>
            </div>
        </template>
        <template if:false={mydata}>
            No Data found.
        </template>
    </lightning-card>
</template>

//child js
import { LightningElement, api } from "lwc";

export default class ChildComponentBasic extends LightningElement {
  @api mydata;
}

 

 

6.부모 객체가 실시간으로 값을 받아 필터링 된 객체들을 iterate을 통해 자식에게 넘겨주고

데이터의 수에 따라 자식 요소가 생겨나게 할 수 있다.

 

다른 컴포넌트의 호출은 이전에도 말했지만 앞에 c-를 붙이고 카멜케이스의 구분을-를 붙여

“c-component-Name-With-Camel-Case” 형식으로 가져올 수 있다.

 

또한 iterate를 사용해줄 때는 key값 사용이 권장된다.

//parent html
<template>
    <lightning-card title="I am Parent with Nesting" icon-name="utility:animal_and_nature">
        <div class="slds-var-m-around_medium">
            <lightning-input onchange={handleOnChange} label="Search"></lightning-input>
        </div>
        <div class="slds-var-m-around_medium">
            <template if:true={myData}>
                <template for:each={myData} for:item="acc">
                    <c-child-Component-Basic mydata={acc} key={acc.Id}>
                    </c-child-Component-Basic>
                </template>
            </template>
        </div>
    </lightning-card>
</template>

//parent js
import { LightningElement } from "lwc";
import findAccList from "@salesforce/apex/AccountController.findAccList";

export default class ParentComponentwithChildNestingIteration extends LightningElement {
  myData;
  error;
  handleOnChange(event) {
    const keyword = event.target.value;
    if (keyword != null && keyword.length > 0) {
      findAccList({ keyword })
        .then((result) => {
          this.myData = result;
          this.error = undefined;
        })
        .catch((error) => {
          this.myData = undefined;
          this.error = error;
        });
    } else {
      this.myData = undefined;
    }
  }
}

//child html
<template>
    <lightning-card title="I am Child" icon-name="utility:animal_and_nature">
        <template if:true={mydata}>
            <div class="slds-var-m-around_medium">
                <b>{mydata.Name}
                    <br>
                    {mydata.Phone}
                </b>
            </div>
        </template>
        <template if:false={mydata}>
            No Data found.
        </template>
    </lightning-card>
</template>

//child js
import { LightningElement, api } from "lwc";

export default class ChildComponentBasic extends LightningElement {
  @api mydata;
}

 

 

7.자식에서 부모로 이벤트를 넘겨주기 위해서는 사실 이벤트를 넘겨주는 것이아니라

부모의 이벤트 핸들러를 자식에게 넘겨줘서 누르는 방식으로 진행된다.

 

특이하게도 해당 함수를 바로 실행하는 것이 아니라

on’xxx’ 형태로 내려보내고 자식 컴포넌트에서 xxx 형태의 이벤트를 발생시킬 경우

부모 컴포넌트에서 감지하는 방식이다.

//parent html
<template>
    <lightning-card title="I am Listening" icon-name="utility:animal_and_nature">
        <div>
            Count of Clicks : {count}
            <c-child-Component-Communication onincreasecount={handleEventChange}>
                Child-Component-Communication
            </c-child-Component-Communication>
        </div>
    </lightning-card>
</template>

//parent js
import { LightningElement } from "lwc";

export default class ParentComponentCommunication extends LightningElement {
  count = 1;
  handleEventChange() {
    this.count = this.count + 1;
  }
}

//child html
<template>
    <lightning-card title="I am communicationg" icon-name="utility:animal_and_nature">
        <div>
            <lightning-button label="Count++" onclick={handleOnClick}>

            </lightning-button>
        </div>
    </lightning-card>
</template>

//child js
import { LightningElement } from "lwc";

export default class ChildComponentCommunication extends LightningElement {
  handleOnClick() {
    this.dispatchEvent(new CustomEvent("increasecount"));
  }
}

 

 

8.파라미터를 여러개 보내는 것 또한 이전의 작업과 다를게 거의 없는데

그래서 그런지 굳이 if문으로 숫자가 5까지밖에 안올라가게 제한까지 걸었는데

출력 메세지가 겨우 string인게 많이 아쉽다.

//parent html
<template>
    <lightning-card title="I am Listening" icon-name="utility:animal_and_nature">
        <div>
            Default Message : {msg}
            <br>
            Count of Clicks : {count}
            <c-child-Component-Custom-Eventwith-Data onincreasecount={handleEventChange}>
            </c-child-Component-Custom-Eventwith-Data>
        </div>
    </lightning-card>
</template>

//parent js
import { LightningElement } from "lwc";

export default class ParentComponentCustomEventwithData extends LightningElement {
  endValue = 0;
  count = 1;
  msg = "Default Message";
  handleEventChange(event) {
    this.endValue = event.detail.endValue;
    this.msg = event.detail.msg;
    if (this.count < this.endValue) this.count = this.count + 1;
  }
}

//child html
<template>
    <lightning-card title="I am communicationg" icon-name="utility:animal_and_nature">
        <div>
            <lightning-button label="Count++" onclick={handleOnClick}>

            </lightning-button>
        </div>
    </lightning-card>
</template>

//child js
import { LightningElement } from "lwc";

export default class ChildComponentCustomEventwithData extends LightningElement {
  endValue = 5;
  handleOnClick() {
    const myEventwithValue = new CustomEvent("increasecount", {
      detail: {
        endValue: this.endValue,
        msg: "I am String"
      }
    });
    this.dispatchEvent(myEventwithValue);
  }
}

 

 

9.api를 통해 실시간으로 값을 입력받는 것은 이미 진행했지만

css를 변경하는 특이한 반응이라서 굳이 넣은 것 같다.

//parent html
<template>
    <lightning-card title="I am Parent with API property" icon-name="utility:animal_and_nature">
        <div>
            <div style="padding: 10px">
                <lightning-input type="number" value={percentage} onchange={handleonchange}>

                </lightning-input>
                <br>
                <c-childofp2cusingapi percentage={percentage}></c-childofp2cusingapi>
            </div>
        </div>
    </lightning-card>
</template>

//parent js
import { LightningElement } from "lwc";

export default class Parentofp2cusingapi extends LightningElement {
  percentage = 20;
  handleonchange(event) {
    this.percentage = event.target.value;
  }
}

//child html
<template>
    <lightning-card title="I am child with API property" icon-name="utility:animal_and_nature">
        <div>
            <div style="padding: 10px">
                <p>{percentage}%</p>
                <br>
                <div style={style}>

                </div>
            </div>
        </div>
    </lightning-card>
</template>

//child js
import { LightningElement, api } from "lwc";

export default class Childofp2cusingapi extends LightningElement {
  @api percentage;
  get style() {
    return `background-color:red; height:50px; width:${this.percentage}%`;
  }
}

 

 

10.부모의 api만 내려받는 것이 아니라 자식이 api로 선언한 요소를

부모가 querySelector로 접근해 기능을 작동시키는 방식으로 자주 사용할 수 있을 것 같다.

//parent html
<template>
    <lightning-card title="I am Parent with API function" icon-name="utility:animal_and_nature">
        <div>
            <div style="padding: 10px">
                <lightning-button label="Refresh" onclick={handleClick}>
                </lightning-button>
                <br>
                <c-childofp2cusingfuncion></c-childofp2cusingfuncion>
            </div>
        </div>
    </lightning-card>
</template>

//parent js
import { LightningElement } from "lwc";

export default class Parentofp2cusingfunction extends LightningElement {
  handleClick() {
    this.template.querySelector("c-childofp2cusingfuncion").refresh();
  }
}

//child html
<template>
    <lightning-card title="I am child with API function" icon-name="utility:animal_and_nature">
        <div>
            <div style="padding: 10px">
                <p>{timestamp}</p>
                <br>
            </div>
        </div>
    </lightning-card>
</template>

//child js
import { LightningElement, api } from "lwc";

export default class Childofp2cusingfuncion extends LightningElement {
  timestamp = new Date();

  @api
  refresh() {
    this.timestamp = new Date();
  }
}

 

 

11.이전에도 유사한 작업을 해서 굳이 이걸 왜 또 하는지는 모르겠지만

유저 데이터를 가져와서 출력하는 내용이었다.

 

wire와 getFieldValue를 사용해 값을 가져왔는데

이전에도 그렇고 굳이 getFieldValue를 사용하는 이유를 정확하게는 모르겠다.

(user.data.name 등으로도 접근이 가능하다)

//html
<template>
    <lightning-card title="Get User Data using Wire" icon-name="utility:animal_and_nature"></lightning-card>
    <template if:true={user.data}>
        <div style="padding:10px; background-color:white">
            <p>Id : {userId}</p>
            <p>Name : {name}</p>
        </div>
    </template>
</template>

//js
import { LightningElement, wire } from "lwc";
import { getRecord, getFieldValue } from "lightning/uiRecordApi";
import Id from "@salesforce/user/Id";
import NAME_FIELD from "@salesforce/schema/User.Name";

const fields = [NAME_FIELD];
export default class GetUserDetails extends LightningElement {
  userId = Id;
  @wire(getRecord, { recordId: "$userId", fields })
  user;

  get name() {
    return getFieldValue(this.user.data, NAME_FIELD);
  }
}

 

 

12.갑자기 wire를 왜 사용하는지 궁금해서 검색해보니

$기호로 전달하고 있는 값이 변경될 경우 다시 해당 값을 가져오기 때문에 유용하다고 한다.

 

생각해보면 필터링이나 입력 시 실시간 변경되는 것들은 대부분 wire를 사용했던 것 같다.

 

하지만 wire를 사용해서 받아오는 데이터는 거의 읽기전용 느낌으로만 사용되기 때문에

업데이트, 삭제 등의 작업이 필요한 경우에는 JS Api를 사용하는 것을 권장한다.

 

또한 JSON.stringify를 통해 문자열로 변경해 주는 것도 잊지는 말아야 하는데

자동으로 JSON이 처리되는 axios를 사용하다보니 문자열이 제대로 출력되지 않는 부분을 잊을 뻔 했다.

(물론 localStorage 사용 시 다시 사용해서 금방 기억은 났을 것 같다.)

 

하단의 코드는 wire를 통해 현재 입력한 이름의 객체 정보를 불러오는 것으로

getObjectInfo와 wire 정도가 중요 키워드로 보인다.

//html
<template>
    <lightning-card title="Get Object Infousing Wire" icon-name="utility:animal_and_nature">
        <div>
            <div style="padding:10px">
                <lightning-input placeholder="Enter API Name" label="Object Name">

                </lightning-input>
                <br>
                <lightning-button label="Fetch Metadata" onclick={handleClick}>

                </lightning-button>
                <br>
                <p>{objectMetadata}</p>
            </div>
        </div>
    </lightning-card>
</template>

//js
import { LightningElement, wire } from "lwc";
import { getObjectInfo } from "lightning/uiObjectInfoApi";

export default class GetObjectDetails extends LightningElement {
  objectApiName;

  @wire(getObjectInfo, { objectApiName: "$objectApiName" })
  objectInfo;

  handleClick() {
    this.objectApiName = this.template.querySelector("lightning-input").value;
  }

  get objectMetadata() {
    return this.objectInfo ? JSON.stringify(this.objectInfo.data) : "";
  }
}

 

 

13.이번에는 조금 showToastEvent를 제대로 다룬 것 같은데

toast 기능이 중간에 뜨는 알림 같은 것이라 과제에 쓰기에도 괜찮을 것 같은 느낌이 들었다.

 

예를 들어 메일발송 후 성공을 띄우던가 대부분의 에러 핸들링에 error toast를 사용해도 좋을 것 같았다.

 

또한 사용자를 고려해서 만든다면 각각 에러핸들링마다 에러코드 부여 또는 사유를 간단히 적어

어떤 부분에서 문제가 발생했는지를 더 쉽게 알려서 빠르게 대처받을 수 있게 하는 것도 가능할 것 같다.(생각해보면 toast는 너무 빨리 지나가서 팝업이 더 좋을 수는 있겠다..)

어쨌건 this.dispatchEvent를 통해서 new ShowToastEvent를 보내면 toast가 뜨는 것이 재미있었다.

//html
<template>
    <lightning-card title="Toast Events" icon-name="utility:animal_and_nature">
        <div>
            <div style="padding: 10px">
                <lightning-button label="Error" onclick={showError}></lightning-button>
                <lightning-button label="Warning" onclick={showWarning}></lightning-button>
                <lightning-button label="Success" onclick={showSuccess}></lightning-button>
                <lightning-button label="Info" onclick={showInfo}></lightning-button>
            </div>
        </div>
    </lightning-card>
</template>

//js
import { LightningElement } from "lwc";
import { ShowToastEvent } from "lightning/platformShowToastEvent";

export default class ShowToastEvents extends LightningElement {
  showError() {
    const evt = new ShowToastEvent({
      title: "Salesforce Toast",
      message: "Salesforce Yohan LWC Stack learning",
      variant: "error"
    });
    this.dispatchEvent(evt);
  }
  showWarning() {
    const evt = new ShowToastEvent({
      title: "Salesforce Toast",
      message: "Salesforce Yohan LWC Stack learning",
      variant: "warning"
    });
    this.dispatchEvent(evt);
  }

  showSuccess() {
    const evt = new ShowToastEvent({
      title: "Salesforce Toast",
      message: "Salesforce Yohan LWC Stack learning",
      variant: "success"
    });
    this.dispatchEvent(evt);
  }

  showInfo() {
    const evt = new ShowToastEvent({
      title: "Salesforce Toast",
      message: "Salesforce Yohan LWC Stack learning",
      variant: "info"
    });
    this.dispatchEvent(evt);
  }
}

 

 

14.이전에 진행했던 코드지만 다시 한번 복습하는 듯한 강의였는데

toast에 대해서 조금 더 알고 wire에 대해서 알고 보니 조금 더 이것저것 많이 보였다.

 

여기서 refreshApex(this.wiredAccountsResult)에 대한 호기심이 생겨서 다른 코드는 왜 되지 않는지 보고 싶었는데

분명 error, accounts도 내부에서 같이 값을 할당받는데 if문 내부에 있는 값들은 넣어도 refresh가 되지 않았다.

 

한참 고생해서 테스트해도 되지 않아서 이사님에게 도움을 요청했는데

첫 번째로 값을 if문 밖으로 꺼내도 되지 않는 문제는 다른 타입인데

동일한 조건을 만들겠다고 result.data인 값에 result를 할당해서 되지 않았던 것이고

result.data를 넣고 꺼낸 값을 refresh하니 정상 작동했다.

 

그래도 if문 내부에 있는 것은 되지 않아서 다시 질문을 드렸는데

그것 또한 console이 내부에선 찍히지 않아서 되지 않는 것이라고 넘어가긴 했는데

생각해보면 초기 동작 자체는 되고 그 뒤로 refreshApex가 작동하는 것인데

refreshApex는 if문 내부는 들여다보지를 못한다고 인식해야 할 것 같다.

//html
<!-- sldsValidatorIgnore -->
<template>
    <lightning-card title="Delete Records without server-side script" icon-name="utility:animal_and_nature">
        <template if:true={accounts}>
            <div>
                <template for:each={accounts} for:item="acc">
                    <lightning-layout key={acc.Id}>
                        <lightning-layout-item flexibility="grow">
                            {acc.Name}
                        </lightning-layout-item>
                        <lightning-layout-item flexibility="grow">
                            <lightning-button-icon icon-name="utility:delete" onclick={deleteAccount}
                                data-record={acc.Id}>
                            </lightning-button-icon>
                        </lightning-layout-item>
                    </lightning-layout>
                </template>
            </div>
        </template>
    </lightning-card>
</template>

//js
import { LightningElement, wire } from "lwc";
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import { refreshApex } from "@salesforce/apex";
import { deleteRecord } from "lightning/uiRecordApi";
import getAccList from "@salesforce/apex/AccountController.getAccList";

export default class DeleteAccounts extends LightningElement {
  accounts;
  error;
  wiredAccountsResult;

  @wire(getAccList)
  wiredAccounts(result) {
    this.wiredAccountsResult = result;
    if (result.data) {
      this.accounts = result.data;
      this.error = undefined;
    } else if (result.error) {
      this.error = result.error;
      this.accounts = undefined;
    }
  }

  deleteAccount(event) {
    const recordId = event.target.dataset.record;
    deleteRecord(recordId)
      .then(() => {
        this.dispatchEvent(
          new ShowToastEvent({
            title: "Success",
            message: "Account Deleted",
            variant: "success"
          })
        );
        return refreshApex(this.wiredAccountsResult);
      })
      .catch((error) => {
        this.dispatchEvent(
          new ShowToastEvent({
            title: "Error",
            message: "Error Deleting record",
            variant: "error"
          })
        );
      });
  }
}

 

 

15.apex class에서 soql을 작성했을 때 limit이 1인 경우

[0]을 붙이던 안붙이던 lwc에서 불러올 때는 {data:xxxx}형태로 들어오는데

왜 그런지 궁금하다.

 

해결을 위해 다양한 시도를 했는데

  1. [0]을 붙이고 제거하며 시도 = 그대로
  2. limit을 2로 바꾸고 실행 = 작동 안됨(console도 되지 않음)
  3. limit을 바꿨기 때문에 return type을 수정해야 함을 인식하고 List<Account>로 변경하니 데이터가 넘어가는 것을 볼 수 있었지만 undefined로 넘어갔다. 추가적 확인을 위해 하단의 콘솔을 시도했지만 Error가 발생하는 것을 보고
console.log(
      this.account,
      this.account[0],
      this.account.data,
      this.account[0].data
    );
//Error "caught (in promise)"

다른 데이터의 경우 어떻게 출력되는지를 확인하니 List의 경우 {data:[]}형태로 반환되는 것을 보고

객체에 [0]을 시도해 에러가 발생했음을 인지하고 다시 this.account, this.account.data로 시도했다.

 

결과적으로 받은 데이터는 {data:[a,b]}형태였고 .data의 경우 [a,b] 형태의 배열에 담긴 계정 정보였다.

 

이를 검증하기 위해 1개로 제한하니 List<Account>일 때는 하나의 값이라도 data:[{a}]의 형태였고

controller 부분의 쿼리 리턴 타입을 Account로 변경하고 하나의 값이 반환되자 data:{a} 형태로

배열을 벗겨낼 필요 없이 바로 값에 접근할 수 있다는 것을 볼 수 있었다.

 

최종적인 결론으로는 리턴 타입이 객체 하나일 경우

배열 안에 들어 있는 상태라도 강제로 벗겨준다는 것을 알 수 있었다.

 

아래는 문제의 코드

//controller inside
@AuraEnabled(cacheable=true)
  public static Account getSingleAccount() {
    return [
      SELECT Id, Name, Phone
      FROM ACCOUNT
      ORDER BY CreatedDate DESC
      LIMIT 1
    ];
  }

//js
export default class BindUsingApexSchema extends LightningElement {
  @wire(getSingleAcc) account;
  get name() {
    console.log(this.account, this.account.data);
    return this.account.data
      ? getSObjectValue(this.account.data, NAME_FIELD)
      : "";
  }
  get phone() {
    return this.account.data
      ? getSObjectValue(this.account.data, PHONE_FIELD)
      : "";
  }
}

결론적으로 apex 자체적으로 데이터가 하나고 객체 형태 반환인 경우 배열을 벗겨준다고 이해하고 넘어갔다.

 

 

 

 

 

(1).백준 16431번 베시와 데이지는 베시, 데이지, 존의 위치를 받은 다음

누가 더 빨리 존에게 도착할 수 있는지를 묻는 문제였다.

 

다만 베시는 한 칸씩 이동할 수 있지만

데이지는 대각선 이동도 가능하기 때문에 이동 방식을 고려하다가

어차피 가장 먼 축(x,y)의 길이와 같다는 사실을 알아내고 Math.max로 축의 길이를 비교했다.

const input = `4 5
5 4
4 4`.split('\n').map(el => el.split(' ').map(Number))

const bessie = Math.max(Math.abs(input[0][0]-input[2][0]), Math.abs(input[0][1]-input[2][1]))
const daisy = Math.abs(input[1][0]-input[2][0]) + Math.abs(input[1][1]-input[2][1])
if(bessie > daisy){
    console.log('daisy')
}
else if(bessie < daisy){
    console.log('bessie')
}
else{
    console.log('tie')
}