Next.js Docs

Next.js: 현대적인 웹 애플리케이션 개발을 위한 React 프레임워크

정의

Next.js는 사용자 경험(User Experience)과 개발자 경험(Developer Experience)을 극대화하기 위해 설계된 React 기반의 풀스택 웹 프레임워크입니다. 서버 사이드 렌더링(SSR), 정적 사이트 생성(SSG), 클라이언트 사이드 렌더링(CSR) 등 다양한 렌더링 방식을 지원하며, 간편한 라우팅, API 엔드포인트 생성, 최적화된 빌드 및 배포 기능을 제공하여 현대적인 웹 애플리케이션 개발을 효율적으로 만들어줍니다.


핵심 개념

  1. 파일 시스템 기반 라우팅 (File-system Routing):
    • pages 디렉터리 내의 파일 구조를 기반으로 자동으로 라우트가 생성됩니다.
    • 예시: pages/about.js/about 경로에 매핑됩니다.
    • 동적 라우팅 (pages/posts/[id].js) 및 Catch-all 라우팅 (pages/blog/[...slug].js)을 지원합니다.
  2. 다양한 렌더링 방식 (Rendering Strategies):
    • 서버 사이드 렌더링 (SSR - Server-Side Rendering): 각 요청마다 서버에서 페이지를 렌더링하여 초기 로딩 속도를 개선하고 SEO에 유리합니다. (getServerSideProps)
    • 정적 사이트 생성 (SSG - Static-Site Generation): 빌드 시점에 페이지를 미리 렌더링하여 매우 빠른 로딩 속도와 높은 보안성을 제공합니다. (getStaticProps, getStaticPaths)
    • 클라이언트 사이드 렌더링 (CSR - Client-Side Rendering): 초기 HTML은 최소한으로 제공하고, JavaScript를 통해 클라이언트 측에서 페이지를 렌더링합니다. (기본 동작)
    • Incremental Static Regeneration (ISR): 정적으로 생성된 페이지를 백그라운드에서 업데이트하여 정적 사이트의 장점과 데이터 업데이트의 유연성을 결합합니다.
  3. 데이터 가져오기 (Data Fetching):
    • getServerSideProps: 서버 사이드 렌더링을 위한 데이터 fetching 함수입니다. 매 요청 시 서버에서 실행됩니다.
    • getStaticProps: 정적 사이트 생성을 위한 데이터 fetching 함수입니다. 빌드 시점에 한 번 실행됩니다.
    • getStaticPaths: 동적 라우팅 페이지의 가능한 모든 경로를 미리 생성하기 위해 getStaticProps와 함께 사용됩니다.
    • 클라이언트 사이드 데이터 fetching: useEffectfetch 또는 써드파티 라이브러리 (swr, react-query)를 사용하여 클라이언트 측에서 데이터를 가져올 수 있습니다.
  4. API 라우팅 (API Routes):
    • pages/api 디렉터리 내에 JavaScript 파일을 생성하여 손쉽게 백엔드 API 엔드포인트를 구축할 수 있습니다.
    • Node.js 환경에서 실행되며, 요청 및 응답 객체를 제공합니다.
  5. 스타일링 (Styling):
    • CSS Modules: 컴포넌트 스코프 CSS를 제공하여 스타일 충돌을 방지합니다. (.module.css 파일)
    • Styled JSX: JavaScript 내에서 CSS를 작성할 수 있도록 지원합니다.
    • CSS-in-JS 라이브러리: Emotion, styled-components 등 다양한 CSS-in-JS 라이브러리를 통합하여 사용할 수 있습니다.
    • Tailwind CSS: 유틸리티 클래스 기반의 CSS 프레임워크를 손쉽게 통합할 수 있습니다.

주요 기능

  1. Hot Module Replacement (HMR): 코드 변경 사항을 저장하면 전체 페이지 새로고침 없이 변경된 모듈만 업데이트하여 개발 생산성을 향상시킵니다.
  2. 자동 코드 분할 (Automatic Code Splitting): 각 페이지별로 필요한 코드만 번들링하여 초기 로딩 시간을 최적화합니다.
  3. 최적화된 이미지 처리 (next/image): 이미지 포맷 최적화, 반응형 이미지, 레이아웃 시프트 방지 등 이미지 관련 성능을 자동으로 관리해주는 컴포넌트를 제공합니다.
  4. 폰트 최적화 (next/font): 웹 폰트 로딩 성능을 개선하는 기능을 제공합니다.
  5. 국제화 (i18n) 라우팅: 다국어 지원 웹 애플리케이션 개발을 위한 편리한 라우팅 및 로케일 관리를 제공합니다.
  6. 미들웨어 (Middleware): 요청이 처리되기 전에 코드를 실행하여 리다이렉션, 인증, 로깅 등 다양한 작업을 수행할 수 있습니다. (_middleware.js 파일)
  7. 정적 내보내기 (Static Export): Node.js 서버 없이 정적인 HTML, CSS, JavaScript 파일로 애플리케이션을 빌드하여 CDN과 같은 환경에 배포할 수 있습니다.
  8. TypeScript 지원: 별도의 설정 없이 TypeScript를 기본적으로 지원하여 개발 안정성을 높입니다.
  9. Vercel 플랫폼과의 완벽한 통합: Next.js 개발사인 Vercel에서 제공하는 호스팅 플랫폼과의 최적화된 연동을 통해 간편하고 효율적인 배포 및 관리가 가능합니다.

이 문서는 Next.js의 핵심적인 정의, 개념, 기능을 간결하게 정리한 자료입니다. 더 자세한 내용은 Next.js 공식 문서를 참고하시는 것을 추천드립니다.

 

Introduction | Next.js

Welcome to the Next.js Documentation.

nextjs.org

관계형 데이터베이스 설계를 위한 핵심: 데이터베이스 정규화

데이터베이스 정규화는 관계형 데이터베이스를 설계하는 데 있어 데이터 중복을 최소화하고 데이터 무결성을 확보하기 위한 필수적인 과정입니다. 이 체계적인 구조화 과정을 통해 데이터베이스는 더욱 효율적이고 안정적으로 운영될 수 있습니다.

왜 데이터베이스 정규화가 중요할까요?

정규화의 궁극적인 목표는 다음과 같습니다.

  • 데이터 중복 제거: 불필요한 데이터 중복을 줄여 데이터베이스의 저장 공간 활용도를 높입니다.
  • 데이터 무결성 유지: 데이터의 일관성과 정확성을 확보하여 데이터에 대한 신뢰도를 향상시킵니다.
  • 이상 현상 방지: 데이터의 삽입, 삭제, 수정 시 발생할 수 있는 예기치 않은 문제점들을 예방합니다.

정규화의 단계별 이해

정규화는 여러 단계를 거치며, 각 단계마다 특정 규칙을 준수해야 합니다. 일반적인 정규화 단계와 그 특징은 다음과 같습니다.


1. 제1정규형 (1NF - First Normal Form)

  • 규칙: 테이블의 모든 속성(열)은 원자적인 값을 가져야 합니다. 즉, 하나의 속성에 여러 개의 값을 나열하여 저장할 수 없습니다.
  • 예시:
    주문 ID 상품명 수량
    1 '노트북, 마우스' 1
    2 키보드 2
    위의 예시에서 '주문 ID' 1의 '상품명' 속성은 '노트북'과 '마우스'라는 두 개의 값을 포함하고 있어 제1정규형을 위반합니다. 이를 제1정규형으로 만들기 위해서는 각 상품을 별도의 행으로 분리해야 합니다.
    주문 ID 상품명 수량
    1 노트북 1
    1 마우스 1
    2 키보드 2

2. 제2정규형 (2NF - Second Normal Form)

  • 규칙: 제1정규형을 만족하고, 기본 키가 아닌 모든 속성은 기본 키에 완전 함수 종속되어야 합니다. 이는 기본 키의 일부에만 종속되는 속성이 존재해서는 안 된다는 의미입니다.
  • 설명: 복합 키(두 개 이상의 속성으로 이루어진 기본 키)를 사용하는 테이블에서 발생할 수 있는 문제입니다. 기본 키의 일부 속성에만 종속되는 속성을 별도의 테이블로 분리해야 합니다.
  • 예시:
    주문 ID 상품 ID 상품명 가격
    1 P1 노트북 100만
    1 P2 마우스 3만
    2 P2 마우스 3만
    위 테이블에서 기본 키는 (주문 ID, 상품 ID)입니다. '상품명'과 '가격'은 '상품 ID'에만 종속되고 '주문 ID'에는 종속되지 않습니다. 따라서 제2정규형을 만족하기 위해 '상품' 테이블을 별도로 분리해야 합니다.
    주문 ID 상품 ID
    1 P1
    1 P2
    2 P2
    상품 테이블:
    상품 ID 상품명 가격
    P1 노트북 100만
    P2 마우스 3만
  • 주문 테이블:

3. 제3정규형 (3NF - Third Normal Form)

  • 규칙: 제2정규형을 만족하고, 기본 키가 아닌 모든 속성은 기본 키에만 종속되어야 합니다. 즉, 기본 키가 아닌 속성 간의 종속성이 존재해서는 안 됩니다. 이를 이행적 함수 종속이라고 하며, 이러한 종속성을 제거해야 합니다.
  • 예시:
    고객 ID 주소 우편번호 도시
    C1 서울시 강남구 123-456 서울
    C2 경기도 성남시 789-012 성남
    위 테이블에서 '고객 ID'가 기본 키입니다. '도시' 속성은 '우편번호'에 종속되고, '우편번호'는 '고객 ID'에 종속됩니다. 이는 기본 키가 아닌 속성 간의 이행적 함수 종속을 보여줍니다. 제3정규형을 만족하기 위해 '우편번호'와 '도시' 정보를 별도의 테이블로 분리합니다.
    고객 ID 주소 우편번호 도시
    C1 서울시 강남구 123-456 서울
    C2 경기도 성남시 789-012 성남
    위 테이블에서 '고객 ID'가 기본 키입니다. '도시' 속성은 '우편번호'에 종속되고, '우편번호'는 '고객 ID'에 종속됩니다. 이는 기본 키가 아닌 속성 간의 이행적 함수 종속을 보여줍니다. 제3정규형을 만족하기 위해 '우편번호'와 '도시' 정보를 별도의 테이블로 분리합니다.
    고객 ID 주소 우편번호
    C1 서울시 강남구 123-456
    C2 경기도 성남시 789-012
    우편번호 테이블:
    우편번호 도시
    123-456 서울
    789-012 성남
  • 고객 테이블:

4. BCNF (Boyce-Codd Normal Form)

  • 규칙: 제3정규형을 강화한 형태로, 테이블의 모든 결정자후보 키여야 합니다. 결정자란 다른 속성을 결정하는 속성을 의미합니다.
  • 설명: 일반적으로 제3정규형을 만족하는 테이블은 BCNF도 만족하지만, 여러 개의 후보 키가 존재하는 경우 BCNF를 위반할 수 있습니다.

5. 제4정규형 (4NF - Fourth Normal Form)

  • 규칙: BCNF를 만족하고, 테이블이 다치 종속성을 포함하지 않아야 합니다. 다치 종속성은 하나의 속성이 여러 개의 독립적인 값을 가질 때 발생합니다.

6. 제5정규형 (5NF - Fifth Normal Form)

  • 규칙: 제4정규형을 만족하고, 테이블이 조인 종속성을 포함하지 않아야 합니다. 조인 종속성은 테이블의 정보를 더 작은 테이블로 분해한 후 다시 조인했을 때 원래의 정보를 완전히 복원할 수 없는 경우 발생합니다.

정규화의 빛과 그림자

■ 정규화의 장점

  • 데이터 중복 최소화: 데이터 저장 공간을 효율적으로 사용하고, 데이터 변경 시 일관성을 유지하는 데 유리합니다.
  • 데이터 무결성 유지: 데이터의 정확성과 신뢰성을 높여 데이터베이스 운영의 안정성을 확보합니다.
  • 데이터 관리 용이성: 데이터 구조가 명확해져 데이터의 삽입, 삭제, 수정 작업을 보다 효율적으로 수행하고, 이상 현상의 발생 가능성을 줄입니다.

■ 정규화의 단점

  • 조인 연산 증가: 여러 테이블로 분리된 데이터를 조회하기 위해 조인 연산이 빈번하게 발생하여 쿼리 성능이 저하될 수 있습니다.
  • 테이블 수 증가: 정규화 단계가 높아질수록 테이블 수가 늘어나 데이터베이스 구조가 복잡해질 수 있습니다. 이는 개발 및 유지보수의 어려움을 야기할 수 있습니다.

균형 잡힌 설계: 비정규화 고려

때로는 성능 향상을 위해 의도적으로 정규화 원칙을 위배하는 비정규화를 고려하기도 합니다. 이는 특정 상황에서 조인 연산의 부담을 줄이고 데이터 접근 속도를 높이기 위한 전략입니다. 하지만 비정규화는 데이터 중복과 이상 현상의 위험을 증가시키므로 신중하게 결정해야 합니다.


결론

데이터베이스 정규화는 효율적이고 안정적인 데이터베이스 설계를 위한 핵심 원칙입니다. 각 정규화 단계를 이해하고, 데이터의 특성과 요구 사항을 고려하여 적절한 수준의 정규화를 적용하는 것이 중요합니다. 무조건 높은 수준의 정규화가 항상 최선은 아니며, 때로는 비정규화와의 균형을 통해 최적의 데이터베이스 성능을 확보해야 합니다.


참고 자료:

 

데이터베이스 정규화 설명 - Microsoft 365 Apps

데이터베이스를 정규화하는 방법과, 형식을 정규화하는 대신 사용할 수 있는 여러 가지 방법을 설명합니다. 데이터베이스 원칙을 이해하려면 데이터베이스 원칙을 마스터하거나 문서에 나와

learn.microsoft.com

 

Next.js 환경 변수 보안 가이드

Next.js 애플리케이션에서 환경 변수를 안전하게 관리하는 것은 매우 중요합니다. 특히 클라이언트 측 코드에 노출되어서는 안 되는 민감한 정보의 경우, 주의 깊은 처리가 필요합니다. Next.js 공식 문서를 기반으로 환경 변수 사용과 보안에 대한 지침을 정리했습니다.


1. 환경 변수 로드 방식

Next.js는 .env, .env.local, .env.[environment], .env.[environment].local 등의 파일을 통해 환경 변수를 로드합니다. 로드된 환경 변수는 process.env 객체를 통해 접근할 수 있습니다.


2. NEXT_PUBLIC_ 프리픽스의 중요성

Next.js에서 환경 변수명 앞에 NEXT_PUBLIC_ 프리픽스를 붙이는 것은 해당 변수를 클라이언트 측 코드에서 사용할 수 있도록 명시하는 것입니다.

  • NEXT_PUBLIC_ 프리픽스가 있는 변수:
    • 빌드 시점에 클라이언트 측 JavaScript 번들에 포함됩니다.
    • 브라우저 환경에서 실행되는 코드 (useEffect, 이벤트 핸들러 등)에서 process.env.NEXT_PUBLIC_MY_VARIABLE 형태로 접근할 수 있습니다.
    • 보안상 주의: 민감한 정보 (API 키, 데이터베이스 URL, 비밀 키 등)를 절대 포함해서는 안 됩니다. 클라이언트 측 코드는 사용자에게 노출될 수 있으므로 정보 유출 위험이 높습니다.
    • 사용 사례: 공개적으로 사용해도 안전한 설정 값 (Google Analytics 추적 ID, 공개 API 엔드포인트, 테마 설정 등)에 사용합니다.
  • NEXT_PUBLIC_ 프리픽스가 없는 변수:
    • 서버 환경에서만 접근 가능합니다. 빌드 시 클라이언트 측 번들에 포함되지 않습니다.
    • API 라우트 (pages/api), getServerSideProps, getStaticProps, 미들웨어 등 서버 측 코드에서 process.env.DB_URL과 같이 접근할 수 있습니다.
    • 보안: 데이터베이스 연결 정보, 백엔드 API 키, 서버 전용 비밀 키 등 민감한 정보를 안전하게 관리할 수 있습니다. 클라이언트 측으로 정보가 노출될 위험이 없습니다.
    • 사용 사례: 데이터베이스 연결 정보, 서버 측 API 인증 키, 내부 서비스 URL 등에 사용합니다.

3. 보안 고려 사항

  • 민감한 정보는 NEXT_PUBLIC_ 없이 저장: 데이터베이스 URL, API 비밀 키, 서비스 계정 정보 등 클라이언트 측에 노출되어서는 안 되는 중요한 정보는 반드시 NEXT_PUBLIC_ 프리픽스 없이 .env 파일에 저장해야 합니다.
  • 클라이언트 측 코드에서 민감한 정보 사용 금지: NEXT_PUBLIC_ 프리픽스가 없는 환경 변수는 클라이언트 측에서 접근할 수 없으므로, 필요한 경우 서버 측 API를 통해 데이터를 요청하는 방식으로 우회해야 합니다.
  • .env.local 파일 관리: .env.local 파일은 개발 환경에서만 사용되며 Git으로 추적되지 않는 것이 일반적입니다. 여기에 개발 환경에 특화된 설정을 저장할 수 있습니다.
  • 프로덕션 환경 변수 설정: 프로덕션 환경에서는 .env.production 또는 서버 환경 변수 설정을 통해 실제 서비스에 필요한 환경 변수를 설정해야 합니다.

4. 공식 문서 링크

더 자세한 내용은 Next.js 공식 문서를 참고하시기 바랍니다.

 

Configuring: Environment Variables | Next.js

Learn to add and access environment variables in your Next.js application.

nextjs.org

 

결론적으로, 데이터베이스 URL과 같이 클라이언트 환경에 유출되면 보안상 심각한 문제를 야기할 수 있는 정보는 반드시 NEXT_PUBLIC_ 프리픽스 없이 .env 파일에 저장하고, 서버 측 코드에서만 접근해야 합니다. NEXT_PUBLIC_ 프리픽스는 공개적으로 사용해도 안전한 정보에만 사용해야 합니다.

Webpack과 Vite의 특징과 개념

웹팩(Webpack) 모듈 번들러

웹팩은 자바스크립트 애플리케이션을 위한 정적 모듈 번들러입니다. 웹팩은 애플리케이션을 구성하는 JavaScript, CSS, 이미지, 폰트 등 다양한 자원들을 모듈로 취급하고, 이 모듈들 간의 의존성 관계를 분석하여 브라우저에서 효율적으로 로딩할 수 있는 하나 또는 여러 개의 번들(묶음)로 만들어줍니다.

핵심 개념:

  • 엔트리(Entry): 웹팩이 번들링을 시작하는 지점입니다. 보통 애플리케이션의 메인 JavaScript 파일이 됩니다.
  • 아웃풋(Output): 웹팩이 생성한 번들된 파일들의 출력 경로와 이름을 설정합니다.
  • 로더(Loader): JavaScript 파일이 아닌 다른 타입의 파일(CSS, 이미지, 폰트 등)을 웹팩이 이해하고 처리할 수 있도록 변환하는 도구입니다. 예를 들어, css-loader는 CSS 파일을 JavaScript 모듈로 변환하고, style-loader는 변환된 CSS를 DOM에 삽입합니다.
  • 플러그인(Plugin): 번들링 과정 전반에 걸쳐 다양한 작업을 수행하는 확장 기능입니다. 코드 최적화, 환경 변수 설정, HTML 파일 생성 등 다양한 용도로 사용됩니다.
  • 모듈(Module): 웹 애플리케이션을 구성하는 모든 파일 (JavaScript, CSS, 이미지, 폰트 등)을 웹팩의 관점에서 모듈이라고 부릅니다. 웹팩은 이 모듈들 간의 의존성을 파악합니다.

특징:

  • 다양한 자원 처리: JavaScript뿐만 아니라 CSS, 이미지, 폰트 등 다양한 유형의 자원을 처리할 수 있습니다.
  • 강력한 확장성: 로더와 플러그인 시스템을 통해 다양한 기능을 추가하고 사용자 정의 구성을 할 수 있습니다.
  • 코드 분할(Code Splitting): 번들을 여러 개로 분리하여 초기 로딩 속도를 개선하고 필요할 때만 특정 코드를 로딩할 수 있도록 합니다.
  • 최적화: 코드 압축(Minification), 난독화(Obfuscation) 등 다양한 최적화 기능을 제공합니다.
  • 넓은 생태계: 오랜 기간 사용되어 온 만큼 방대한 커뮤니티와 다양한 써드파티 라이브러리 및 도구를 지원합니다.

참고 공식 문서 (레퍼런스):


Vite 모듈 번들러

Vite는 더 빠르고 가벼운 최신 프론트엔드 개발 경험을 제공하기 위해 탄생한 빌드 도구입니다. 특히 개발 단계에서 네이티브 ES Modules를 활용하여 매우 빠른 서버 시작 시간과 즉각적인 Hot Module Replacement (HMR)를 제공하는 것이 특징입니다.

핵심 개념:

  • 네이티브 ES Modules (Native ESM): 개발 단계에서 브라우저의 네이티브 ES Modules 기능을 활용하여 코드를 번들링하지 않고 필요한 모듈만 요청 시 제공합니다.
  • Rollup 기반 번들링: 프로덕션 빌드 시에는 최적화된 번들 생성을 위해 Rollup을 사용합니다.
  • 플러그인: Vite 역시 플러그인 시스템을 통해 다양한 기능을 확장할 수 있습니다. Rollup 플러그인과 호환되거나 Vite 고유의 플러그인을 사용할 수 있습니다.
  • CSS 및 정적 자산 처리: CSS, 이미지, 폰트 등 정적 자산을 내장된 방식으로 효율적으로 처리합니다.
  • Hot Module Replacement (HMR): 코드 변경 시 전체 페이지를 새로고침하지 않고 변경된 모듈만 빠르게 업데이트하여 개발 생산성을 향상시킵니다.

특징:

  • 압도적으로 빠른 개발 서버 시작: 네이티브 ESM을 활용하여 콜드 스타트 시에도 매우 빠르게 개발 서버를 실행할 수 있습니다.
  • 즉각적인 HMR: 변경된 코드만 빠르게 브라우저에 반영하여 개발 경험을 크게 향상시킵니다.
  • 최적화된 프로덕션 빌드: Rollup을 사용하여 작고 효율적인 프로덕션 번들을 생성합니다.
  • 간단한 설정: 대부분의 일반적인 설정이 내장되어 있어 별도의 복잡한 설정 없이 빠르게 프로젝트를 시작할 수 있습니다.
  • 현대적인 프레임워크 지원: Vue.js, React, Preact, Svelte 등 주요 프론트엔드 프레임워크를 위한 템플릿과 최적화된 설정을 제공합니다.

참고 공식 문서 (레퍼런스):

 

Vite

Next Generation Frontend Tooling

vite.dev

React와 Next.js의 특징들


Next.js란 무엇인가요?

 

Next.js는 풀스택 웹 애플리케이션을 빌드하기 위한 React 프레임워크입니다.

React Components를 사용하여 사용자 인터페이스를 빌드하고 Next.js를 사용하여 추가 기능과 최적화를 구현합니다.

후드 아래에서 Next.js는 번들링, 컴파일 등과 같이 React에 필요한 툴링을 추상화하고 자동으로 구성합니다. 이를 통해 구성에 시간을 들이는 대신 애플리케이션 빌드에 집중할 수 있습니다.

개인 개발자이든 대규모 팀에 속한 사람이든 Next.js는 대화형, 동적인, 빠른 React 애플리케이션을 구축하는 데 도움을 줄 수 있습니다.

 

https://nextjs.org/docs

 

Introduction | Next.js

Welcome to the Next.js Documentation.

nextjs.org


Next.js의 주요 기능은?

 

  • Routing : 레이아웃, 중첩 라우팅, 로딩 상태, 오류 처리 등을 지원하는 서버 구성 요소를 기반으로 구축된 파일 시스템 기반 라우터입니다.
  • Rendering : 클라이언트 및 서버 구성 요소를 사용한 클라이언트 측 및 서버 측 렌더링.
  • Data Fetching : 서버 구성 요소에서 async/await를 사용하여 간소화된 데이터 가져오기와 `fetch` 요청 메모 생성, 데이터 캐싱 및 재검증을 위환 확장된 API가 제공됩니다.
  • Styling : CSS 모듈, Tailwind CSS, CSS-in-JS를 포함한 선호하는 스타일링 방법 지원
  • Optimizations : 이미지, 글꼴, 스크립트를 최적화하여 애플리케이션의 Core Web Vitals와 사용자 경험을 개선합니다.
  • TypeScript : 더 나은 유형 검사와 더 효율적인 컴파일, 그리고 사용자 정의 TypeScript 플러그인과 유형 검사기를 통해 TypeScript에 대한 지원이 개선되었습니다.

React만 사용할 때와 비교하여 Next.js를 사용하는 이유

 

성능, SEO, 개발 생산성 측면에서 Next.js가 다양한 개발 도구와 기능을 제공하기 때문에, React만 사용할 때 보다 효율적인 웹 개발을 할 수 있습니다.


1. 성능 향상

- 서버 사이드 렌더링 (SSR) 및 정적 사이트 생성 (SSG)

  • Next.js는 SSR과 SSG를 통해 초기 로딩 속도를 향상시키고 사용자 경험을 개선합니다. React는 기본적으로 클라이언트 사이드 렌더링 (CSR)을 사용하므로 초기 로딩 시 빈 페이지가 표시될 수 있습니다.
  • SSR은 서버에서 미리 HTML을 생성하여 클라이언트에 전송하므로 초기 로딩 속도가 빠릅니다. SSG는 빌드 시점에 HTML을 생성하여 더욱 빠른 로딩 속도를 제공합니다.
  • Next.js 공식 문서 - Rendering : https://nextjs.org/docs/pages/building-your-application/rendering

- 코드 분할 및 최적화

  • Next.js는 자동으로 코드 분할 및 최적화를 수행하여 불필요한 코드 로딩을 줄입니다. 이를 통해 페이지 로딩 속도를 더욱 향상시키고 사용자 경험을 개선합니다.
  • Next.js 공식 문서 - Optimizing : https://nextjs.org/docs/pages/building-your-application/optimizing

- 이미지 최적화 

  • Next.js는 내장된 이미지 최적화 기능을 제공하여 이미지 로딩 속도를 향상시킵니다. 이를 통해 웹사이트의 성능을 최적화하고 사용자 경험을 개선합니다.
  • Next.js 공식 문서 - Images : https://nextjs.org/docs/pages/api-reference/components/image 

2. SEO 최적화

- 검색 엔진 친화적인 구조

  • Next.js는 SSR을 통해 검색 엔진 크롤러가 웹사이트의 콘텐츠를 쉽게 수집할 수 있도록 합니다. 이를 통해 검색 엔진 최적화 (SEO)를 향상시키고 검색 결과 상위 노출을 가능하게 합니다.
  • Next.js 공식 문서 - SEO : https://nextjs.org/learn/seo

- 메타 태그 관리


3. 개발 생산성 향상

- 파일 시스템 기반 라우팅

  • Next.js는 파일 시스템 기반 라우팅을 제공하여 직관적인 페이지 관리를 가능하게 합니다. 이를 통해 개발자는 라우팅 설정을 간편하게 처리하고 개발 생산성을 향상시킬 수 있습니다.
  • Next.js 공식 문서 - Routing : https://nextjs.org/docs/pages/building-your-application/routing

- API 라우팅

  • Next.js는 API 라우팅 기능을 제공하여 백엔드 API를 손쉽게 구축할 수 있도록 합니다. 이를 통해 개발자는 프론트엔드와 백엔드를 통합하여 효율적인 개발 환경을 구축할 수 있습니다.
  • Next.js 공식 문서 - API routes : https://nextjs.org/docs/pages/building-your-application/routing/api-routes

- TypeScript 지원

  • Next.js는 기본적으로 TypeScript를 지원하여 안정적인 코드 작성을 돕고 유지보수성을 향상시킵니다. React도 TypeScript 사용이 가능하지만 추가적인 설정이 필요합니다.
  • Next.js 공식 문서 - TypeScript : https://nextjs.org/docs/pages/api-reference/config/typescript

리액트 컴포넌트의 이해: 함수형 vs 클래스형

리액트에서 컴포넌트는 UI를 구축하는 핵심적인 building block이며, 독립적이고 재사용 가능한 코드 단위입니다. 컴포넌트를 통해 복잡한 사용자 인터페이스를 작고 관리하기 쉬운 조각으로 분할하여 개발 효율성과 유지보수성을 크게 향상시킬 수 있습니다.

리액트 컴포넌트는 탄생 초기부터 존재했던 클래스 컴포넌트와 이후 등장하여 주류가 된 함수형 컴포넌트 두 가지 주요 유형으로 나뉩니다.


함수형 컴포넌트 (Functional Components)

정의:

함수형 컴포넌트는 JavaScript의 일반적인 함수로 작성됩니다. 이 함수는 props (properties) 라는 입력 객체를 인자로 받아, 리액트 엘리먼트를 기술하는 JSX (JavaScript XML) 를 반환합니다. ES6의 화살표 함수 문법을 활용하면 더욱 간결하고 직관적인 코드를 작성할 수 있습니다.

특징:

  • 상태 관리 및 생명주기: 초기 함수형 컴포넌트는 자체적인 상태(state)를 가질 수 없었고, 컴포넌트의 생명주기(mount, update, unmount 등)에 직접적으로 접근할 수 없었습니다. 이는 클래스 컴포넌트의 주요 장점이었습니다.
  • React Hooks의 등장과 변화: React Hooks가 React 16.8 버전에 도입되면서 함수형 컴포넌트의 이러한 제약은 혁신적으로 해결되었습니다. useState, useEffect 등의 Hooks를 통해 함수형 컴포넌트에서도 상태 관리, 생명주기 관련 작업, 그리고 다양한 부가 기능을 간결하고 효과적으로 처리할 수 있게 되었습니다.
  • 간결성과 가독성: 클래스 컴포넌트에 비해 문법이 훨씬 간결하여 코드의 양을 줄이고 가독성을 높이는 데 유리합니다. 특히 UI 로직이 단순한 컴포넌트의 경우, 함수형 컴포넌트의 장점이 더욱 두드러집니다.
  • 성능: 이론적으로 함수형 컴포넌트는 클래스 컴포넌트보다 약간의 성능 우위를 가질 수 있습니다. 이는 함수 호출 오버헤드가 클래스 인스턴스 생성 및 메서드 호출 오버헤드보다 일반적으로 작기 때문입니다. 또한, Hooks 최적화 기법을 통해 불필요한 리렌더링을 더욱 효과적으로 방지할 수 있습니다.
  • 재사용성: Hooks를 통해 컴포넌트 로직을 추출하고 재사용하기 용이하여, 함수형 컴포넌트의 재사용성을 더욱 높일 수 있습니다.

클래스 컴포넌트 (Class Components)

정의:

클래스 컴포넌트는 ES6의 클래스 문법을 사용하여 작성되며, 반드시 React.Component 를 상속받아야 합니다. 클래스 컴포넌트는 UI를 렌더링하는 역할을 하는 render() 메서드를 필수적으로 구현해야 하며, 이 메서드 내에서 리액트 엘리먼트를 기술하는 JSX를 반환합니다.

특징:

  • 상태(State) 관리: 클래스 컴포넌트는 this.state 속성을 통해 컴포넌트 내부의 동적인 데이터를 관리할 수 있습니다. this.setState() 메서드를 사용하여 상태를 업데이트하고, 상태가 변경되면 리액트는 해당 컴포넌트를 다시 렌더링합니다.
  • 생명주기 메서드: 클래스 컴포넌트는 컴포넌트의 생성(constructor), 렌더링(render), 업데이트(componentDidUpdate), 소멸(componentWillUnmount) 등 다양한 생명주기 시점에 특정 코드를 실행할 수 있는 메서드를 제공합니다. 이러한 메서드를 통해 API 호출, 이벤트 리스너 등록 및 해제 등 컴포넌트의 동작 방식을 세밀하게 제어할 수 있습니다.
  • 문법적 복잡성: 함수형 컴포넌트에 비해 클래스 문법을 사용하므로 코드의 구조가 더 복잡하고 장황해질 수 있습니다. 특히 상태 관리 로직이나 생명주기 관련 코드가 많아질수록 가독성이 떨어질 수 있습니다.
  • this 바인딩: 클래스 컴포넌트 내에서 이벤트 핸들러 함수 등을 사용할 때 this 바인딩에 주의해야 합니다. constructor 내에서 .bind(this)를 사용하거나 화살표 함수를 사용하여 this를 올바르게 바인딩해야 하는 번거로움이 있습니다.
  • React Hooks 등장 이후의 위상 변화: React Hooks가 등장하면서 함수형 컴포넌트에서도 상태 관리 및 생명주기 관련 작업을 훨씬 간결하게 처리할 수 있게 됨에 따라, 클래스 컴포넌트의 사용 빈도는 점차 줄어들고 있습니다. 새로운 리액트 프로젝트에서는 함수형 컴포넌트와 Hooks를 사용하는 것이 일반적인 추세입니다. 하지만 여전히 기존 코드베이스나 특정 라이브러리에서는 클래스 컴포넌트를 찾아볼 수 있습니다.

결론적으로, React Hooks의 등장 이후 함수형 컴포넌트는 상태 관리, 생명주기 처리 등 클래스 컴포넌트의 모든 기능을 더 간결하고 효율적으로 수행할 수 있게 되면서 리액트 개발의 중심축으로 자리매김했습니다. 새로운 프로젝트에서는 함수형 컴포넌트와 Hooks를 적극적으로 활용하는 것이 권장됩니다. 하지만 클래스 컴포넌트 역시 여전히 중요한 개념이며, 기존 코드 이해나 특정 상황에서는 여전히 사용될 수 있습니다.

 

 

첫 번째 컴포넌트 – React

The library for web and native user interfaces

ko.react.dev

https://ko.react.dev/learn/your-first-component

React에서 배열을 렌더링할 때 Key를 설정해야 하는 이유

리액트에서 배열을 렌더링할 때 key prop을 설정하는 것은 매우 중요합니다. 이는 리액트가 어떤 항목이 변경, 추가 또는 삭제되었는지 식별하고 가상 DOM을 효율적으로 업데이트하기 위한 핵심적인 메커니즘이기 때문입니다.

key prop은 리액트가 배열 내의 각 엘리먼트에 대한 고유한 식별자를 알 수 있도록 해줍니다. 이 고유한 식별자를 통해 리액트는 다음과 같은 작업을 최적화하여 애플리케이션의 성능과 안정성을 향상시킵니다.

■ Key 설정의 필요성 (Why Keys are Necessary)

  • 효율적인 업데이트 (Efficient Updates):
    • key가 없다면 리액트는 배열의 변경 사항을 감지하기 위해 각 요소를 이전 렌더링 결과와 순서대로 비교해야 합니다. 배열의 순서가 바뀌거나 중간에 요소가 삽입/삭제되면, 실제로는 내용이 변경되지 않은 요소까지 불필요하게 업데이트될 수 있습니다. 이는 성능 저하의 주요 원인이 됩니다.
    • key를 사용하면 리액트는 각 요소의 고유한 식별자를 통해 변경된 요소, 새로 추가된 요소, 삭제된 요소를 정확하게 파악할 수 있습니다. 따라서 변경된 요소에 대해서만 실제 DOM 업데이트를 수행하여 애플리케이션의 성능을 최적화할 수 있습니다.
  • 예측 가능한 컴포넌트 동작 (Predictable Component Behavior):
    • key가 없을 경우, 리액트는 컴포넌트의 상태를 제대로 유지하지 못할 수 있습니다. 예를 들어, 입력 필드를 가진 목록에서 중간에 항목이 추가되면, 리액트는 기존 컴포넌트 인스턴스를 재사용하려고 시도하면서 예상치 못한 방식으로 상태가 섞이거나 초기화될 수 있습니다.
    • key를 사용하면 리액트는 각 컴포넌트 인스턴스를 해당 데이터 항목과 안정적으로 연결합니다. 따라서 배열이 변경되어도 각 컴포넌트는 올바른 데이터와 상태를 유지하며 예측 가능한 방식으로 동작합니다.
  • 컴포넌트 상태 유지 (Maintaining Component State):
    • 리액트는 key를 사용하여 렌더링된 컴포넌트 인스턴스와 실제 DOM 엘리먼트를 연결합니다. 배열 내의 요소 순서가 변경되거나 새로운 요소가 삽입/삭제될 때, key가 없다면 리액트는 기존 컴포넌트 인스턴스를 재사용하려고 시도할 수 있습니다. 이 과정에서 컴포넌트의 내부 상태 (예: 입력 필드의 값, 체크박스 상태, 스크롤 위치 등)가 예기치 않게 손실될 수 있습니다.
    • 고유한 key를 제공하면 리액트는 각 데이터 항목에 해당하는 컴포넌트 인스턴스를 정확하게 식별하고 유지하므로, 배열이 변경되어도 컴포넌트의 상태가 보존됩니다.

■ Key 설정 시 주의사항 (Important Considerations for Setting Keys)

  • 고유하고 안정적인 값 사용 (Use Unique and Stable Values):
    • key prop은 형제 요소들 사이에서 반드시 고유해야 합니다. 동일한 key 값을 가진 형제 요소가 있으면 리액트는 어떤 요소를 업데이트해야 할지 혼동하여 성능 저하 및 예기치 않은 동작이 발생할 수 있습니다.
    • 데이터베이스의 ID, UUID, 또는 서버에서 제공하는 고유한 식별자와 같이 안정적이고 예측 가능한 고유 값을 사용하는 것이 가장 좋습니다. 렌더링 과정에서 동적으로 생성되는 값 (예: Math.random())은 매번 렌더링 시마다 변경되므로 key로서 적합하지 않습니다.
  • 인덱스를 Key로 사용하는 것은 최후의 수단 (Avoid Using Index as Key Unless Necessary):
    • 배열의 인덱스를 key로 사용하는 것은 일반적으로 권장되지 않습니다. 배열의 순서가 변경되거나 중간에 요소가 삽입/삭제되면, 인덱스는 더 이상 해당 요소의 고유한 식별자가 되지 못합니다. 이로 인해 리액트는 엘리먼트를 제대로 식별하지 못하고 불필요한 재렌더링을 수행하거나 컴포넌트의 상태가 제대로 유지되지 않을 수 있습니다.
    • 배열의 내용이 변경되지 않거나, 배열의 순서가 바뀌지 않는 정적인 목록을 렌더링하는 경우에 한해서만 인덱스를 key로 사용하는 것을 고려해볼 수 있습니다. 하지만 이 경우에도 잠재적인 문제를 완전히 배제할 수는 없습니다.
    // 일반적으로 피해야 하는 패턴
    {items.map((item, index) => (
      <li key={index}>{item.name}</li>
    ))}
    
    // 더 나은 패턴 (item에 고유한 id가 있다고 가정)
    {items.map((item) => (
      <li key={item.id}>{item.name}</li>
    ))}
  • 동적으로 Key 생성 지양 (Avoid Dynamically Generating Keys):
    • key 값은 렌더링 과정에서 동적으로 생성되어서는 안 됩니다. 매번 렌더링될 때마다 새로운 key가 생성되면 리액트는 모든 요소가 변경되었다고 인식하여 비효율적인 업데이트를 수행하게 됩니다.
    • key안정적이고 예측 가능해야 리액트가 요소의 동일성을 제대로 파악할 수 있습니다.
  • 형제 요소 간 고유성 유지 (Uniqueness Among Siblings):
    • key 값은 전역적으로 고유할 필요는 없습니다. key동일한 부모 아래 있는 형제 요소들 사이에서만 고유하면 됩니다. 서로 다른 배열을 렌더링하는 컴포넌트에서는 동일한 key 값을 사용해도 충돌이 발생하지 않습니다.

배열을 렌더링할 때 key prop을 올바르게 설정하는 것은 리액트 애플리케이션의 성능 최적화와 예측 가능한 UI 동작을 보장하는 기본적인 동시에 매우 중요한 개발 습관입니다. 항상 데이터의 고유한 식별자를 key로 제공하도록 노력해야 합니다.

 

 

리스트 렌더링 – React

The library for web and native user interfaces

ko.react.dev

https://ko.react.dev/learn/rendering-lists

State and Lifecycle – React


리액트 컴포넌트 생명주기는 컴포넌트가 생성되고 화면에 렌더링되어 업데이트되고 제거될 때까지의 일련의 과정을 의미합니다.

 

리액트 컴포넌트는 다음과 같은 세 가지 주요 생명주기 단계를 거칩니다.

 

1. 마운팅(Mounting): 컴포넌트 생성 및 렌더링

  • constructor(): 컴포넌트가 생성될 때 호출됩니다. 초기 state 설정 및 메서드 바인딩에 사용됩니다.
  • static getDerivedStateFromProps(): props에 따라 state를 업데이트할 때 사용됩니다.
  • render(): 컴포넌트를 렌더링하는 메서드입니다. JSX를 반환합니다.
  • componentDidMount(): 컴포넌트가 DOM에 삽입된 후 호출됩니다. 외부 API 호출, 이벤트 리스너 설정 등에 사용됩니다.

2. 업데이트(Updating): 컴포넌트 업데이트 및 재렌더링

  • static getDerivedStateFromProps(): props에 따라 state를 업데이트할 때 사용됩니다.
  • shouldComponentUpdate(): 컴포넌트가 업데이트되어야 하는지 여부를 결정합니다. 성능 최적화에 사용됩니다.
  • render(): 컴포넌트를 렌더링하는 메서드입니다. JSX를 반환합니다.
  • getSnapshotBeforeUpdate(): DOM 업데이트 직전에 호출됩니다. DOM 변경 전 정보를 캡처할 때 사용됩니다.
  • componentDidUpdate(): 컴포넌트 업데이트 후 호출됩니다. DOM 업데이트 후 작업을 수행할 때 사용됩니다.

3. 언마운팅(Unmounting): 컴포넌트 제거

  • componentWillUnmount(): 컴포넌트가 DOM에서 제거되기 직전에 호출됩니다. 이벤트 리스너 제거, 타이머 제거 등에 사용됩니다.

[ 함수형 컴포넌트와 Hooks ]

 

클래스형 컴포넌트 외에도 함수형 컴포넌트와 Hooks를 사용하여 리액트 컴포넌트를 만들 수 있습니다. 함수형 컴포넌트에서는 다음과 같은 Hooks를 사용하여 생명주기 관련 작업을 수행합니다.

  • useEffect(): componentDidMount, componentDidUpdate, componentWillUnmount와 유사한 기능을 제공합니다.
  • useState(): state를 관리합니다.
  • useContext(): context를 사용합니다.
  • useReducer(): 복잡한 state 관리를 위한 reducer를 사용합니다.
  • useMemo(): 연산 결과를 메모이제이션하여 성능을 최적화합니다.
  • useCallback(): 함수를 메모이제이션하여 성능을 최적화합니다.
  • useRef(): DOM 요소 또는 컴포넌트 인스턴스에 접근합니다.

 

State: A Component's Memory – React

The library for web and native user interfaces

react.dev

상태: 컴포넌트의 메모리 – React

 

Synchronizing with Effects – React

The library for web and native user interfaces

react.dev

Synchronizing with Effects – React

+ Recent posts