랩
이 절차는 New Relic 브라우저로 웹 앱의 문제를 해결하는 방법을 알려주는 랩의 일부입니다.
랩의 각 절차는 마지막 절차를 기반으로 하므로 이 절차를 시작하기 전에 마지막 절차인 브라우저 에이전트로 애플리케이션 계측을완료했는지 확인하십시오.
지금까지 애플리케이션이 제대로 작동했습니다. 사용자가 주문할 수 있었고 귀하의 서비스에 만족했습니다. 그러나 이제 애플리케이션에 약간의 통찰력이 있으므로 일부 JavaScript 오류가 표시되고 있음을 알 수 있습니다.
이 절차에서는 New Relic 브라우저를 사용하여 이러한 오류의 원인을 찾고 적시에 애플리케이션을 디버깅합니다.
프런트엔드 오류 디버그
안타까운 소식은 애플리케이션에 몇 가지 오류가 있음을 확인했다는 것입니다. 좋은 소식은 최근에 브라우저 에이전트로 애플리케이션을 계측했다는 것입니다! 아직 로그인하지 않은 경우 New Relic으로 이동하여 계정에 로그인합니다.
팁
데이터가 보이지 않습니까? 브라우저 모니터링을 활성화했고 부하 생성기가 실행 중인지 확인하십시오.
위의 오류 세부 정보를 보면 이제 서비스에 영향을 미치는 특정 오류를 알 수 있습니다. 그러나 여기에 표시된 스택 추적은 축소되었으며 이 오류의 원인을 이해하기 어렵습니다. 이를 이해하려면 소스 맵을 업로드하여 오류 축소를 해제해야 합니다.
JS 오류를 축소 해제하기 위해 소스 맵 업로드
축소된 JavaScript는 대부분 브라우저의 오류 페이지에서 이해하기 어렵고 쓸모 없는 스택 추적을 생성합니다. 소스 맵을 업로드하면 이러한 오류가 이해할 수 있는 스택 추적으로 변환됩니다. 또한 코드 라인에 대한 유용한 참조를 제공하고 디버깅을 더 쉽게 만듭니다. UI, API 또는 npm 모듈을 통해 소스 맵을 New Relic에 업로드할 수 있습니다.
여기서는 New Relic UI를 사용하여 소스 맵을 업로드하고 JS 오류를 축소 해제합니다.
find file 을 클릭합니다.
이렇게 하면 로컬 저장소에서 소스 맵을 업로드할 수 있는 파일 탐색기 창이 열립니다. 프로젝트의 build/static/js 디렉터리에서 소스 맵을 찾아 업로드합니다.
팁
소스 맵 파일의 파일 확장자는 .js.map
입니다. Relicstaurants는 소스 맵을 생성하도록 설정되어 있으며 build/static/js 디렉토리에서 찾을 수 있습니다. 프로젝트의 소스 맵을 생성하는 데 문제가 있는 경우 설명서에 따라 생성 방법을 알아보세요.
선택한 IDE에서 애플리케이션을 열고 src/components/layouts/app/app-container/header/app-container-header.js 파일로 이동합니다. 표시된 코드를 자세히 살펴보십시오.
import { Button, Drawer, Table } from 'antd';import Text from 'antd/lib/typography/Text';import { orderList } from 'atoms/order-list.atom';import { useState } from 'react';import { Link } from 'react-router-dom';import { useRecoilState } from 'recoil';import { Logo, StyledHeader } from './app-header-styled';import Navi from './navi-items';import { useNavigate } from 'react-router';
const Header = () => { const [isSidebarVisible, setIsSidebarVisible] = useState(false); const [orderListState, setOrderList] = useRecoilState(orderList); const navigate = useNavigate();
const onClose = () => { setIsSidebarVisible(false); }; const handleSidebarOpen = () => { setIsSidebarVisible(true); };
const itemQuantity = (list) => { let totalItemQuantity = 0; list.forEach((item) => (totalItemQuantity += item.count));
return totalItemQuantity; };
const handleDeleteItem = (clickedRow) => { const reducedData = orderListState.filter((item) => item.name === clickedRow.name ? false : true ); setOrderList(reducedData); };
const columns = [ { title: 'Name', dataIndex: 'name', key: 'name', }, { title: 'Count', dataIndex: 'count', key: 'count', }, { title: 'Price', dataIndex: 'price', key: 'price', }, { title: 'Delete', render: (clickedRow) => ( <Button onClick={() => handleDeleteItem(clickedRow)}>-</Button> ), }, ];
return ( <StyledHeader> <Link to="/"> <Logo> <div>Relicstaurants</div> <p>by New Relic</p> </Logo> </Link> <Navi sidebarVisible={handleSidebarOpen} orderListLength={itemQuantity(orderListState)} /> <Drawer size="large" title="Cart" placement="right" onClose={onClose} visible={isSidebarVisible} > <Table dataSource={orderListState} columns={columns} pagination={false} summary={(pageData) => { let totalPrice = 0;
pageData.forEach( ({ price, count }) => (totalPrice += price * count) );
return ( <> <Table.Summary.Row> <Table.Summary.Cell colSpan={2}>Total</Table.Summary.Cell> <Table.Summary.Cell> <Text type="danger">{totalPrice.toFixed(2)}</Text> </Table.Summary.Cell> </Table.Summary.Row> <Table.Summary.Row> <Table.Summary.Cell colSpan={3}> <Button disabled={totalPrice > 0 ? false : true} primary onClick={() => { setOrderList([]); setIsSidebarVisible(false); }} > Clear Cart </Button> </Table.Summary.Cell> <Table.Summary.Cell> <Button id="pay" primary onClick={() => { if (!(totalPrice > 0)) { var err = new Error('Cart cannot be empty!'); newrelic.noticeError(err); alert(err) navigate('/') setIsSidebarVisible(false); } else { navigate(`/payment`, { state: totalPrice }); setIsSidebarVisible(false); } }} > PAY </Button> </Table.Summary.Cell> </Table.Summary.Row> </> ); }} /> </Drawer> </StyledHeader> );};
export default Header;
여기서는 사용자가 실수로 빈 카트를 사용하여 결제하려고 할 때마다 Cart cannot be empty! 오류가 발생한다는 점에 유의하세요. 공지 최종 사용자에게 빈 장바구니로는 결제를 진행할 수 없다는 기능이 코딩되어 있습니다. 이제 이 오류가 서비스에 영향을 미치지 않는다는 것을 알게 되었습니다. 그러나 이러한 극단적인 경우를 처리하고 오류를 방지하는 더 나은 방법이 있습니다.
애플리케이션 제공을 중지하려면 애플리케이션을 실행 중인 터미널에서 Ctrl+C
누르세요. 다음과 같이 src/components/layouts/app/app-container/header/app-container-header.js를 업데이트합니다.
import { Button, Drawer, Table } from 'antd';import Text from 'antd/lib/typography/Text';import { orderList } from 'atoms/order-list.atom';import { Message } from 'components/common';import { useState } from 'react';import { Link } from 'react-router-dom';import { useRecoilState } from 'recoil';import { Logo, StyledHeader } from './app-header-styled';import Navi from './navi-items';import { useNavigate } from 'react-router';
const Header = () => { const [isSidebarVisible, setIsSidebarVisible] = useState(false); const [orderListState, setOrderList] = useRecoilState(orderList); const navigate = useNavigate();
const onClose = () => { setIsSidebarVisible(false); }; const handleSidebarOpen = () => { setIsSidebarVisible(true); };
const itemQuantity = (list) => { let totalItemQuantity = 0; list.forEach((item) => (totalItemQuantity += item.count));
return totalItemQuantity; };
const handleDeleteItem = (clickedRow) => { const reducedData = orderListState.filter((item) => item.name === clickedRow.name ? false : true ); setOrderList(reducedData); };
const columns = [ { title: 'Name', dataIndex: 'name', key: 'name', }, { title: 'Count', dataIndex: 'count', key: 'count', }, { title: 'Price', dataIndex: 'price', key: 'price', }, { title: 'Delete', render: (clickedRow) => ( <Button onClick={() => handleDeleteItem(clickedRow)}>-</Button> ), }, ];
return ( <StyledHeader> <Link to="/"> <Logo> <div>Relicstaurants</div> <p>by New Relic</p> </Logo> </Link> <Navi sidebarVisible={handleSidebarOpen} orderListLength={itemQuantity(orderListState)} /> <Drawer size="large" title="Cart" placement="right" onClose={onClose} visible={isSidebarVisible} > {orderListState.length > 0 ? ( <Table dataSource={orderListState} columns={columns} pagination={false} summary={(pageData) => { let totalPrice = 0;
pageData.forEach( ({ price, count }) => (totalPrice += price * count) );
return ( <> <Table.Summary.Row> <Table.Summary.Cell colSpan={2}>Total</Table.Summary.Cell> <Table.Summary.Cell> <Text type="danger">{totalPrice.toFixed(2)}</Text> </Table.Summary.Cell> </Table.Summary.Row> <Table.Summary.Row> <Table.Summary.Cell colSpan={3}> <Button disabled={totalPrice > 0 ? false : true} primary onClick={() => { setOrderList([]); setIsSidebarVisible(false); }} > Clear Cart </Button> </Table.Summary.Cell> <Table.Summary.Cell> <Button id="pay" disabled={totalPrice > 0 ? false : true} primary onClick={() => { navigate(`/payment`, { state: totalPrice }); setIsSidebarVisible(false); }} > PAY </Button> </Table.Summary.Cell> </Table.Summary.Row> </> ); }} /> ) : ( <Message>Nothing in cart</Message> )} </Drawer> </StyledHeader> );};
export default Header;
여기서는 장바구니가 비어 있을 때 오류 대신 Nothing in cart 메시지를 표시하도록 파일을 수정했습니다. 최종 사용자가 장바구니에 항목을 담을 때까지 PAY 버튼은 비활성화된 상태로 유지됩니다.
애플리케이션 다시 시작
애플리케이션을 수정했으므로 이제 로컬 서버를 다시 시작할 차례입니다.
$npm run build$npm run newstart
부하 생성기도 다시 시작하십시오.
$python3 simulator.py
중요
올바른 터미널 창에서 이러한 명령을 실행하고 있는지 확인하십시오. 해당 창이 더 이상 없으면 설정 절차의 단계를 따르십시오.
부하 생성기가 New Relic으로 데이터를 전송하기 시작하면 애플리케이션에서 더 이상 JavaScript 오류를 보고하지 않는다는 점에 유의하십시오.
요약
요약하자면, 애플리케이션에서 오류를 발견하고 New Relic 브라우저를 사용하여 다음을 수행했습니다.
- 오류율 검토
- 애플리케이션의 JS 오류 분석
- 오류 인스턴스 이해
- 소스 맵을 업로드하여 JS 오류 디버그
랩
이 절차는 New Relic 브라우저로 웹 앱의 문제를 해결하는 방법을 알려주는 랩의 일부입니다. 다음으로 애플리케이션에서 프런트엔드 속도 저하를 디버깅해보십시오.