랩
이 절차는 New Relic 브라우저로 웹 앱의 문제를 해결하는 방법을 알려주는 랩의 일부입니다.
랩의 각 절차는 마지막 절차를 기반으로 하므로 이 절차를 시작하기 전에 마지막 절차인 브라우저 에이전트로 애플리케이션 계측을완료했는지 확인하십시오.
지금까지 애플리케이션이 제대로 작동했습니다. 사용자가 주문할 수 있었고 귀하의 서비스에 만족했습니다. 그러나 이제 애플리케이션에 약간의 통찰력이 있으므로 일부 JavaScript 오류가 표시되고 있음을 알 수 있습니다.
이 절차에서는 New Relic 브라우저를 사용하여 이러한 오류의 원인을 찾고 적시에 애플리케이션을 디버깅합니다.
프런트엔드 오류 디버그
안타까운 소식은 애플리케이션에 몇 가지 오류가 있음을 확인했다는 것입니다. 좋은 소식은 최근에 브라우저 에이전트로 애플리케이션을 계측했다는 것입니다! 아직 로그인하지 않은 경우 New Relic으로 이동하여 계정에 로그인합니다.
뉴렐릭 홈페이지에서 Browser 으로 이동하여 Relicstaurants 를 선택하세요.
여기에서는 Page views with JavaScript errors, Core web vitals, User time on the site, Initial page load and route changes 등을 포함하여 브라우저 애플리케이션과 관련된 모든 데이터를 볼 수 있습니다.
팁
데이터가 보이지 않습니까? 브라우저 모니터링을 활성화했고 부하 생성기가 실행 중인지 확인하십시오.
Page views with javascript errors 주목하세요.
여기에서 애플리케이션에 일부 Javascript 오류가 있음을 나타내는 스파이크가 표시됩니다.
Page views with javascript errors 을 클릭합니다.
그러면 총 오류 인스턴스와 함께 모든 JS 오류를 볼 수 있는 JS errors 페이지로 이동됩니다.
자세한 내용을 보려면 Cart cannot be empty 오류를 클릭하세요.
여기에서는 errorMessage, INSTANCES, INTERACTIONS AFFECTED 및 오류와 관련된 기타 세부정보를 볼 수 있습니다.
다음으로 Error Instances 탭으로 이동합니다.
여기에서는 Event Log, Stack trace 및 기타를 포함하여 특정 오류와 관련된 자세한 내용을 볼 수 있습니다.
Stack trace 을(를) 보려면 Error Instances 페이지에서 아래로 스크롤하세요.
여기에서 오류의 스택 추적을 볼 수 있습니다.
위의 오류 세부 정보를 보면 이제 서비스에 영향을 미치는 특정 오류를 알 수 있습니다. 그러나 여기에 표시된 스택 추적은 축소되었으며 이 오류의 원인을 이해하기 어렵습니다. 이를 이해하려면 소스 맵을 업로드하여 오류 축소를 해제해야 합니다.
JS 오류를 축소 해제하기 위해 소스 맵 업로드
축소된 JavaScript는 대부분 브라우저의 오류 페이지에서 이해하기 어렵고 쓸모 없는 스택 추적을 생성합니다. 소스 맵을 업로드하면 이러한 오류가 이해할 수 있는 스택 추적으로 변환됩니다. 또한 코드 라인에 대한 유용한 참조를 제공하고 디버깅을 더 쉽게 만듭니다. UI, API 또는 npm 모듈을 통해 소스 맵을 New Relic에 업로드할 수 있습니다.
여기서는 New Relic UI를 사용하여 소스 맵을 업로드하고 JS 오류를 축소 해제합니다.
JS 오류 페이지에서 오류의 스택 추적으로 이동하여 확장합니다.
여기에 소스 맵을 업로드하는 옵션이 표시됩니다.
find file 을 클릭합니다.
이렇게 하면 로컬 저장소에서 소스 맵을 업로드할 수 있는 파일 탐색기 창이 열립니다. 프로젝트의 build/static/js 디렉터리에서 소스 맵을 찾아 업로드합니다.
팁
소스 맵 파일의 파일 확장자는 .js.map
입니다. Relicstaurants는 소스 맵을 생성하도록 설정되어 있으며 build/static/js 디렉토리에서 찾을 수 있습니다. 프로젝트의 소스 맵을 생성하는 데 문제가 있는 경우 설명서에 따라 생성 방법을 알아보세요.
소스 맵이 성공적으로 업로드되면 오류가 축소되지 않은 것을 볼 수 있습니다.
여기서는 이 오류를 생성하는 특정 파일과 코드 줄을 볼 수 있습니다. 119행에서 Cart cannot be empty! 는 Components/layouts/app/app-컨테이너/header/app-컨테이너-header.js 파일의 onClick 이벤트와 연결되어 있으며 사용자에게 공지도 트리거하고 있습니다. 이 파일을 자세히 살펴보겠습니다!
선택한 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 브라우저로 웹 앱의 문제를 해결하는 방법을 알려주는 랩의 일부입니다. 다음으로 애플리케이션에서 프런트엔드 속도 저하를 디버깅해보십시오.