Skip to content

minjidev/react-ts-diet-frontend

Repository files navigation

ko en

📒 NutriNotes



프로젝트 소개

프로젝트 개요

  • 기획 배경: 컴퓨터와 함께 오랜 시간 자리에 앉아서 매일 햄버거(🍔)를 먹고 있는 저를 발견하고, 일자별로 식단을 기록하고 관리하는 서비스에 대한 필요성을 느꼈습니다.

  • 한 줄 설명: 식단을 검색/기록/분석하여 효과적으로 식습관을 관리할 수 있도록 하는 서비스입니다.

  • 대상: 식단 관리가 어려운 사람들

  • 개인 프로젝트

  • 개발 기간: 2023/07 ~ 진행 중

  • 배포 주소: https://nutrinotes.net


주요 기능

🥗 식단 둘러보기

카테고리별 식단을 캐러셀로 둘러볼 수 있고, 각 식단 카드를 선택하면 상세 영양 성분을 확인할 수 있습니다. recipes

🔎 식단 검색

외부 API(EDAMAM)에서 가져온 식단을 검색하고, 상세 영양 정보를 확인할 수 있습니다. search


✏️ 식단 기록

캘린더에서 일자를 지정해 식단을 저장하여 대시보드에서 해당 일자에 저장된 식단과 영양 성분을 분석해 시각화한 차트를 확인할 수 있습니다. dashboard


📃 회원 가입 & 로그인

JWT를 활용한 회원가입 및 로그인을 구현했습니다. React Hook Form과 Zod를 사용해 타입 안정성을 보장하는 스키마를 기반으로 폼을 관리하고, debounce를 사용해 이벤트 중복 호출을 방지하였습니다. register login

고민했던 지점들

📒 (노션에서 보기)

1. 웹 사이트 성능 최적화

프론트엔드 성능이 사용자 경험에 영향을 미칠 수 있다는 것을 알고, 개선점을 찾고자 했습니다. (블로그)

  • 초기 번들 사이즈 감소를 통한 초기 로딩 속도 향상

    • 불필요한 패키지 정리. 큰 부분을 차지하던 Lodash의 경우, 트리쉐이킹을 위해 default import로 필요한 함수만 받아오도록 수정.

    • 페이지 기반 dynamic import를 통해 필요한 시점에 자바스크립트를 로드하여 초기에 로드하는 자바스크립트 번들 사이즈를 감소.

  • 이미지 레이지 로딩을 통한 이미지 로딩 최적화

    • loading=lazy를 활용하고, 이를 지원하지 않는 브라우저를 위해 IntersectionObserver를 사용한 LazyImg 컴포넌트를 만들어 사용. useObserver 훅에서 옵저버를 생성하여 공유하고, 뷰포트와 intersecting하는 경우, 저해상도 이미지를 고해상도 이미지로 변경.

    • 이때, 유의할 점은 초기 화면에 나오는 이미지를 레이지 로딩하지 않아야한다는 것. 불필요한 리소스 로드 지연을 방지하기 위해 캐러셀의 첫 페이지에는 EagerLoad 이미지를, 이후에는 LazyLoad 이미지를 사용.

  • 데이터 Prefetch 및 Preload를 통한 render-as-you-fetch

    • React Router의 loader를 활용해 라우트 진입 전 필요한 데이터를 prefetch하고, 이후 TanStack Query의 useQuery를 활용해 캐싱 및 무효화.

    • prefetchQuery를 통해 유저 인터랙션(e.g. 검색 키워드 hover) 발생 시 데이터를 prefetch하여 최대한 빠르게 로딩을 시작하고, useQuery에서 캐싱된 데이터를 사용

💭 웹 성능 개선을 통해 FCP 0.6초, LCP 1.5초 개선하여 초기 로딩 속도를 단축했습니다. 이 과정에서 네트워크 요청 시 로딩을 최적화하기 위해 고려해야 할 요소들을 알고, 상황에 따른 적절한 최적화의 중요성을 깨달았습니다.

아쉬웠던 점은 현재 third-party api를 사용하고 있기 때문에 성능에 큰 영향을 미칠 수 있는 이미지 압축, WebP, AVIF와 같은 웹 페이지에 최적화된 형식을 사용하지 못했던 점입니다. 추후 백엔드에서 간단한 작업 뿐만 아니라 이미지를 직접 관리한다면 더 효율적인 개선이 가능할 것으로 보입니다.

2. 접근성 및 SEO 최적화

사용자가 어떤 브라우저나 기기를 사용하더라도 화면을 동일하게 보고, 신체적, 환경적 조건에 관계 없이 정보에 접근할 수 있는 서비스를 만들고자 하였습니다.

  • 시맨틱 마크업 적용 (블로그)

    header, h, section, article, button과 같은 역할을 명시적으로 드러내는 시맨틱 태그를 사용한 마크업.

  • aria 및 image alt 태그 사용

    section과 article 태그의 경우 보여지는 제목이 있는 경우 aria-labelledby를, 그렇지 않은 경우 aria-label을 사용해 역할을 명시.

  • Open Graph 및 메타 태그 활용

    title, description, author, keywords 등의 메타 태그 및 OpenGraph를 활용하여 SEO를 최적화.

웹 표준 및 웹 접근성을 고려한 마크업 및 SEO 개선 작업을 통해 웹 접근성과 SEO의 중요성을 이해하고, LightHouse 기준 평균 지표 19.76% 향상이라는 성과를 도출하였습니다.

3. AWS 배포

  • 네트워크 요청 문제

    • CORS 에러
      • 개발 환경에서는 프록시를 사용해 요청을 redirect했지만, 배포 환경에서는 서버에서 cors 미들웨어를 사용해 CORS 헤더 설정하여 해결.
    • Mixed Content:
      • 프론트엔드 서버 요청을 CloudFront를 사용해 HTTPS로 redirect했을 때 문제가 발생하여 백엔드 서버 요청도 LoadBalancer에 SSL/TLS를 설정하여 해결.
  • S3 버킷 업데이트와 배포 환경 불일치

    • 동적 import한 코드를 배포했을 때, Failed to fetch dynamically loaded module 에러가 발생.

    • 버킷의 내용을 삭제하고 내용을 업데이트했지만, 배포 환경에 반영되지 않는 문제가 발생했습니다. CloudFront의 로그를 확인하여 캐싱 문제인 것을 확인했고, 브라우저 캐시 삭제 및 강제 새로고침을 했지만 해결되지 않았음.

    • 이를 해결하기 위해 코드 분할이 되지 않은 이전 버전을 먼저 업로드하고 CloudFront에서 캐시를 전체 무효화한 후 코드 분할한 코드를 업로드하여 해결.

  • 프론트엔드 및 백엔드 HTTPS 구성

    • Route53에서 사용하는 하나의 도메인으로 서버 구조를 구성의 어려움.

    • [프론트] Route53의 도메인은 S3로 연결하고, CloudFront에서 https로 redirect.

    • [백엔드] 사용하고 있는 도메인의 서브 도메인을 만들어 LoadBalancer에서 https로 redirect하고, 이를 target group에 들어온 요청을 처리할 EC2 포트를 명시해 연결.

SSL/TLS를 적용한 안전한 연결을 보장하고, 쾌적한 사용자 경험을 위한 기본적인 클라우드 환경 구성을 할 수 있게 되었습니다. 배포 과정에서 캐시 및 버전 관리의 중요성을 이해하고, 로그 분석을 통한 문제 해결 능력을 키웠습니다.


설치 가이드

NutriNotes를 로컬에서 실행하기:

요구사항

  • Node 20.5.1
  • Npm 10.0.0

프론트엔드

  1. 클론 git clone https://github.com/minjidev/react-ts-diet-frontend.git

  2. 디렉토리로 이동 cd react-ts-diet-frontend

  3. 의존성 설치 npm install

  4. .env 파일을 생성하세요.

    VITE_EDAMAM_APP_ID = YOUR_EDAMAM_APP_ID;
    VITE_EDAMAM_APP_KEY = YOUR_EDAMAM_APP_KEY;
    VITE_API_BASE_URL = BACKEND_ENDPONT;
  5. 개발 서버 시작: npm run dev

백엔드

  1. 클론 git clone https://github.com/minjidev/react-ts-diet-backend.git

  2. 디렉토리로 이동 cd react-ts-diet-backend

  3. 의존성 설치 npm install

  4. .env 파일을 생성

    PORT = BACKEND_PORT;
    JWT_SECRET_KEY = YOUR_JWT_SECRET_KEY;
  5. nodemon 실행: npm start


기술 스택

환경

Visual Studio Code Git GitHub

프론트엔드

Vite React TypeScript

React Router React Query Recoil Axios styled-components React Hook Form Zod Chart.js

백엔드

Node.js Express JSON Web Tokens MongoDB Mongoose

배포

Amazon S3 Amazon EC2 Amazon Route 53


기타

커밋 컨벤션

Type 설명
Feat 새로운 기능 추가
Fix 버그 수정
Refactor 코드 리팩토링, 파일 혹은 폴더명을 수정하거나 옮기는 작업만인 경우, 파일을 삭제하는 작업만 수행한 경우
Style CSS 및 레이아웃 작업수정
Docs 문서 수정
Chore 유지보수

About

A simple, visualized guide to plan and review one's diet

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors