신기하게도 바로 어제 포트폴리오 사용자 경험을 올리기 위해 Lighthouse를 사용해보았는데 오늘 수업에서 알려주셔서 스터디노트에서는 간단히 정리하고 자세한 성능 향상을 위해 수정할 내용은 따로 포스팅하려한다.
🔸 Lighthouse: 웹 성능 최적화 도구
- Google에서 개발한 오픈소스 웹 성능 분석 도구
- 웹사이트의 품질을 측정하고 개선할 수 있는 방법을 제공
- 측정 영역: 성능, 접근성, PWA, SEO, 모범 사례
- 점수 범위: 0~100점
- 사용 목적: 웹사이트 품질 진단 및 개선
주요 측정 영역
- 성능 (Performance)
- 로딩 속도, 상호작용 지연 시간, 시각적 안정성 등
- 접근성 (Accessibility)
- 모범 사례 (Best Practices)
- 보안, 모바일 대응 등 웹 개발 표준 준수 여부
- SEO (검색 엔진 최적화)
- PWA (Progressive Web App)
- 오프라인 지원, 설치 가능 등 PWA 요구사항 충족 여부
Lighthouse 활용 방법
1️⃣ 성능 최적화
지표 | 개선 방법 |
---|
First Contentful Paint(FCP) | 서버 응답 시간 단축, 렌더링 차단 리소스 제거 |
Largest Contentful Paint(LCP) | 이미지 최적화, 중요 리소스 우선 로드 |
Total Blocking Time(TBT) | JS 실행 시간 단축, 코드 분할 |
Cumulative Layout Shift(CLS) | 이미지·광고 크기 명시, 레이아웃 안정화 |
2️⃣ 접근성 개선
- 이미지에
alt
속성 추가 - 텍스트 색상 대비 향상
- 키보드만으로 탐색 가능하게
- ARIA 속성 정확히 사용
3️⃣ SEO 최적화
<title>
, <meta>
등 메타 태그 작성- 의미 있는 URL 구조
- 모바일 대응
robots.txt
, sitemap.xml
구현
⚠️ 주의사항
- 점수는 테스트 환경(네트워크, 디바이스 등)에 따라 달라질 수 있음
- 모든 항목을 무조건 따를 필요는 없음 (비즈니스 우선순위 고려)
- 100점이 목표가 아니라 → 사용자 경험 개선이 진짜 목표!
🔸 JSON Server
- JSON 파일을 기반으로 빠르게 RESTful API를 생성할 수 있는 도구
- 프론트엔드 개발 시 빠른 프로토타입 구축 가능
- 주요 특징:
- JSON 파일을 데이터베이스로 사용
- CRUD (GET, POST, PUT, PATCH, DELETE)
- 필터링, 정렬, 페이지네이션 등의 기능 제공
설치 및 설정
- JSON Server와 개발 서버를 동시에 실행하기 위한 concurrently 패키지를 설치
1
| npm install -D json-server concurrently
|
package.json
스크립트 추가
1
2
3
4
5
| "scripts": {
"watch:json-server": "json-server db.json --port 3000",
"watch:react": "react-scripts start",
"dev": "concurrently npm:watch:*"
}
|
npm run dev
: React + JSON Server 동시 실행
프로젝트 루트에 data 구성 (db.json
)
1
2
3
4
| {
"products": [ /* 주얼리 상품들 */ ],
"cart": []
}
|
데이터 조회
📌 조건 필터링 (쿼리 파라미터)
1
2
3
| const response = await axios.get('http://localhost:3000/products', {
params: { price_gte: 100000, category: 'top' }
});
|
조건 | 의미 |
---|
_lt | 미만 < |
_lte | 이하 <= |
_gt | 초과 > |
_gte | 이상 >= |
_ne | 같지 않음 != |
field_like=value | 포함 검색 |
📌 범위 지정
1
2
| // 0번부터 2개 조회
fetchProducts('_start=0&_limit=2')
|
키 | 의미 |
---|
_start | 시작 인덱스 |
_end | 종료 인덱스 |
_limit | 개수 제한 |
📌 페이지네이션
1
| fetchProducts('_page=2&_per_page=2')
|
응답 정보 예:
1
2
3
4
5
| {
"pages": 3,
"items": 6,
"data": [ ... ]
}
|
📌 정렬
1
2
| fetchProducts('_sort=discount') // 오름차순
fetchProducts('_sort=-discount') // 내림차순
|
🔸 비동기 통신
Axios를 사용하는 이유 (fetch
vs axios
)
항목 | fetch | axios |
---|
응답 형태 | JSON으로 변환 필요 (res.json() ) | 자동으로 객체 형태로 반환 |
요청 방식 | GET , POST 만 자주 쓰고, 다른 메서드는 다소 불편 | PUT , PATCH , DELETE 등 다양한 요청 쉽게 가능 |
설정 | 옵션을 매번 지정해야 함 | 기본 설정과 인터셉터 등 유용한 기능 제공 |
📌 fetch는 응답을 받은 뒤 .json()
으로 다시 파싱해야 하기 때문에 비동기 처리를 두 번 해야 함.
반면 axios는 한 번에 객체 형태로 받아오므로 간편함!
비동기 처리란?
- 네트워크 요청 같은 시간이 오래 걸리는 작업을 백그라운드에서 실행하고
- 그동안 다른 작업은 먼저 실행
- 작업이 끝나면 나중에 결과를 가져오는 방식
ex) 백엔드에 데이터 요청 → 기다리는 동안 화면 렌더링 계속 → 응답 오면 그때 처리
Promise vs Async/Await
- 예전엔
.then()
체이닝으로 처리 (Promise
) - 지금은
async/await
로 동기처럼 코드 작성 가능
await
이 하는 일?
1
| const data = await axios.get('/api')
|
결과가 올 때까지 기다린 후 다음 줄 실행
이전처럼 콜백 지옥이나 .then()
연속 사용 안 해도 됨!
자바스크립트 비동기 처리 흐름
자바스크립트는 싱글 스레드 언어임 -> 한 번에 한 가지 작업만
- 하지만 비동기를 통해 시간이 오래 걸리는 작업동안 다른 작업을 계속할 수 있도록 만들어둠
✅ Call Stack (콜 스택)
- 현재 실행 중인 함수들이 쌓이는 공간
- LIFO 구조 (Last In First Out, 후입선출)
- 함수가 호출되면 스택에 들어가고, 실행이 끝나면 빠져나가
동기적인 작업은 여기서 모두 처리됨
ex) 함수1 → 함수2 → 함수3
이런 식으로 스택에 쌓였다 빠짐
✅ Web APIs
setTimeout
, fetch
, eventListener
등은 브라우저가 따로 처리- JavaScript는 “이 작업 좀 해줘!” 하고 브라우저한테 일을 던짐
- 이건 비동기로 처리됨 → 결과가 준비될 때까지 기다릴 필요 X
ex) setTimeout(() => console.log('Hi'), 1000)
→ 브라우저가 1초 세고 나서 callback을 Task Queue에 넣어줘
⭐️⭐️⭐️
“비동기 함수는 Web API에서 따로 처리되고, 끝나면 Task Queue에 콜백이 등록된다. 콜스택이 비는 순간, 이벤트 루프가 Task Queue에서 콜백을 꺼내 실행시킨다.”
axios를 사용한 예시
❌ await
안 썼을 경우
1
2
3
4
5
6
7
8
| useEffect(() => {
const getBannerData = () => {
const data = axios.get('http://localhost:3000/banners/')
console.log(data) // ❗ Promise 객체만 출력됨
setBanner(data.data) // ❗ 아직 데이터가 안 들어와서 에러남
}
getBannerData()
}, [])
|
await/async 처리를 안하면 비동기로 돌기 때문에 콘솔에 Promise 객체만 찍힘
✅ await
을 썼을 경우
1
2
3
4
5
6
7
8
| useEffect(() => {
const getBannerData = async () => {
const data = await axios.get('http://localhost:3000/banners/')
console.log(data)
setBanner(data.data)
}
getBannerData()
}, [])
|
콘솔에 실제 데이터 출력
예외 처리 (try-catch)
네트워크 요청은 실패할 수 있기 때문에 에러를 안전하게 처리해야 함
1
2
3
4
5
6
7
8
| const getBannerData = async () => {
try {
const data = await axios.get('http://localhost:3000/banners/')
setBanner(data.data)
} catch (err) {
console.log(err)
}
}
|
try
블록에서 요청 시도해라 ~- 실패하면
catch
로 넘어가 에러를 잡아라 ! - 앱이 강제로 중단되지 않도록 도와주면서 무슨 에러인지 확인할 수 있음
API 분리 구조 (폴더 구조 예시)
1
2
3
4
5
6
7
8
9
10
11
12
| src/
├── api/ <----------------------- API 호출 함수를 위한 디렉토리
│ ├── bannerApi.js <---------- 배너 관련 API 함수
│ └── productApi.js <------------ 상품 관련 API 함수
├── organism/
│ ├── Header.jsx
│ ├── Footer.jsx
├── layout/
│ ├── Default.jsx
├── pages/
│ ├── MainPage.jsx
│ └── ...
|
bannerApi.js
1
2
3
4
5
6
7
8
9
10
11
| import axios from 'axios'
const BASE_URL = 'http://localhost:3000/banners/'
export const getBannerData = async () => {
try {
const res = await axios.get(`${BASE_URL}`)
return res.data
} catch (err) {
console.log('err----', err)
}
}
|
컴포넌트에서 사용 예시 (HeroSlider.jsx)
1
2
3
4
5
6
7
8
9
10
11
| useEffect(() => {
const fetchBanner = async () => {
try {
const data = await getBannerData()
setBanner(data)
} catch (err) {
console.log('에러 발생:', err)
}
}
fetchBanner()
}, [])
|
END