Next.js 면접 준비
작성일 : 2025.01.19
1. "Next.js가 무엇이며, React와의 주요 차이점은 무엇인가요?"
Next.js는 React를 기반으로 한 풀스택 웹 프레임워크로, React의 기능을 확장하여 더욱 강력한 웹 애플리케이션 개발 환경을 제공합니다. React가 UI 라이브러리로서의 역할에 중점을 둔다면, Next.js는 이를 포함하면서도 더 포괄적인 개발 솔루션을 제공합니다.
가장 두드러진 차이점은 Next.js가 서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG)을 기본적으로 지원한다는 점입니다. 이를 통해 초기 페이지 로 딩 속도를 개선하고 검색 엔진 최적화(SEO)를 강화할 수 있습니다. 또한, Next.js는 파일 시스템을 기반으로 한 직관적인 라우팅 시스템을 제공하여, 개발자가 복잡한 라우팅 설정 없이도 페이지를 쉽게 구성할 수 있게 해줍니다.
Next.js의 또 다른 강점은 자동 코드 스플리팅 기능입니다. 이는 각 페이지에 필요한 JavaScript 코드만을 로드하여 초기 번들 크기를 줄이고 애플리케이션의 성능을 최적화합니다. 추가로, API 라우트를 내장하고 있어 별도의 백엔드 서버 없이도 API 엔드포인트를 쉽게 생성하고 관리할 수 있습니다.
마지막으로, Next.js는 이미지, 폰트, 스크립트와 같은 정적 자원들을 자동으로 최적화하는 기능을 제공합니다. 이미지 컴포넌트는 자동 크기 조정, 포맷 변환, lazy loading을 지원하며, 폰트와 스크립트도 최적화된 로딩 전략을 통해 더 나은 성능을 제공합니다. 이러한 기능들은 개발자가 성능 최적화에 들이는 시간과 노력을 크게 줄여줍니다.
2. "Next.js의 렌더링 방식들을 설명해주세요."
Next.js는 웹 애플리케이션의 다양한 요구사항을 충족시키기 위해 네 가지의 핵심적인 렌더링 방식을 제공합니다.
먼저, Server Side Rendering(SSR)은 사용자의 요청이 있을 때마다 서버에서 페이지를 동적으로 생성하는 방식입니다. getServerSideProps 함수를 통해 구현되며, 실시간 데이터가 필요하거나 사용자별 맞춤 콘텐츠를 제 공해야 하는 페이지에 적합합니다. 매 요청마다 최신 데이터로 페이지를 생성할 수 있다는 장점이 있지만, 서버 부하가 상대적으로 크다는 특징이 있습니다.
Static Site Generation(SSG)은 빌드 시점에 페이지를 미리 생성하여 제공하는 방식입니다. getStaticProps를 사용하여 구현하며, 모든 사용자에게 동일한 콘텐츠를 제공하는 블로그 포스트나 제품 소개 페이지와 같이 자주 변경되지 않는 콘텐츠에 이상적입니다. 한번 생성된 페이지를 재사용할 수 있어 매우 빠른 응답 속도를 제공합니다.
Incremental Static Regeneration(ISR)은 SSG의 확장된 형태로, 정적 페이지를 주기적으로 재생성할 수 있게 해줍니다. getStaticProps 함수에 revalidate 옵션을 설정하여 구현하며, 데이터의 실시간성과 정적 페이지의 성능 이점을 모두 얻을 수 있습니다. 예를 들어, 주기적으로 업데이트되는 뉴스 기사나 상품 정보 페이지에 적합합니다.
마지막으로, Client Side Rendering(CSR)은 브라우저에서 JavaScript를 실행하여 페이지를 렌더링하는 전통적인 React의 방식입니다. useEffect 훅이나 SWR, React Query와 같은 데이터 페칭 라이브러리를 사용하여 구현합니다. 사용자 대시보드나 실시간 채팅과 같이 빈번한 데이터 업데이트가 필요한 동적인 기능에 적합합니다. 초기 로딩 시간이 다소 길 수 있지만, 이후의 상호작용은 매우 빠르게 이루어진다는 특징이 있습니다.
이러한 다양한 렌더링 방식을 제공함으로써, Next.js는 개발자가 각 페이지의 특성과 요구사항에 맞는 최적의 렌더링 전략을 선택할 수 있게 해줍니다.
3. "Next.js의 파일 기반 라우팅에 대해 설명해주세요."
Next.js는 직관적이고 편리한 파일 시스템 기반의 라우팅 시스템을 제공합니다. 이는 pages 디렉토리의 파일 구조를 웹 애플리케이션의 URL 구조로 자동 변환하는 방식으로 작동합니다.
가장 기본적인 형태로, 루트 경로('/')는 pages 디렉토리의 index.js 파일에 의해 처리됩니다. 예를 들어 홈페이지의 경우 pages/index.js 파일이 담당하게 됩니다. 일반적인 정적 경로는 파일 이름을 그대로 URL 경로로 사용합니다. pages/about.js 파일은 '/about' URL로 자동 매핑되며, pages/contact.js는 '/contact' 경로로 연결됩니다.
동적 라우팅이 필요한 경우, 대괄호([])를 사용하여 처리할 수 있습니다. 예를 들어, pages/posts/[id].js 파일은 '/posts/1', '/posts/2' 등과 같은 동적 URL을 처리할 수 있으며, 컴포넌트 내에서 useRouter 훅을 통해 id 값을 쉽게 접근할 수 있습니다. 이는 블로그 포스트나 제품 상세 페이지와 같이 동적 콘텐츠를 표시해야 하는 경우에 유용합니다.
더 복잡한 동적 라우팅이 필요한 경우, Catch-all 라우팅을 사용할 수 있습니다. pages/posts/[...slug].js와 같은 형식으로 파일을 생성하면, '/posts/a/b/c'와 같은 여러 단계의 경로를 하나의 컴포넌트에서 처리할 수 있습니다. 이는 복잡한 URL 구조나 다단계 카테고리 시스템을 구현할 때 특히 유용합니다.
이러한 파일 기반 라우팅 시스템은 별도의 라우팅 설정 파일이 필요 없어 프로젝트 구조를 더욱 명확하게 만들어주며, 개발 자가 직관적으로 라우팅 구조를 파악하고 관리할 수 있게 해줍니다. 또한 자동으로 코드 스플리팅이 적용되어 각 페이지에 필요한 코드만 로드되므로 성능 최적화에도 도움이 됩니다.
4. "Next.js에서 상태 관리는 어떻게 하나요?"
Next.js에서의 상태 관리는 애플리케이션의 규모와 요구사항에 따라 다양한 방식으로 구현할 수 있습니다.
가장 기본적인 방법은 React의 내장 훅을 사용한 로컬 상태 관리입니다. useState는 컴포넌트 내부에서 간단한 상태를 관리할 때 사용되며, useReducer는 더 복잡한 상태 로직이 필요한 경우에 활용됩니다. 이러한 로컬 상태 관리는 폼 데이터나 UI 상태와 같이 특정 컴포넌트에 국한된 상태를 다룰 때 적합합니다.
애플리케이션의 규모가 커지고 여러 컴포넌트 간에 상태를 공유해야 하는 경우, 전역 상태 관리 라이브러리를 사용할 수 있습니다. Redux는 예측 가능한 상태 컨테이너로서 복잡한 상태 로직을 체계적으로 관리할 수 있게 해주며, Recoil은 React에 최적화된 상태 관리 라이브러리로 더 간편한 API를 제공합니다. Zustand는 최소한의 보일러플레이트로 직관적인 상태 관리를 가능하게 합니다.
서버 데이터를 다루는 경우, SWR이나 React Query와 같은 데이터 페칭 라이브러리를 사용할 수 있습니다. 이러한 라이브러리들은 서버 상태의 캐싱, 재검증, 낙관적 업데이트 등을 효율적으로 처리할 수 있게 해주며, 실시간 데이터 동기화가 필요한 경우에 특히 유용합니다.
작은 규모의 애플리케이션이나 특정 컴포넌트 트리 내에서만 상태를 공유해야 하는 경우, React의 Context API를 활용할 수 있습니다. Context API는 별도의 라이브러리 설치 없이 React 내장 기능만으로 전역 상태를 관리할 수 있게 해주며, 프로퍼티 드릴링 문제를 해결하는 데 효과적입니다.
5. "Next.js의 성능 최적화 방법들을 설명해주세요."
Next.js는 웹 애플리케이션의 성능을 최적화하기 위한 다양한 내장 기능을 제공합니다. 이러한 기능들은 개발자가 별도의 복잡한 설정 없이도 최적화된 웹 애플리케이션을 구축할 수 있도록 도와줍니다.
Next.js의 Image Component는 이미지 최적화를 자동으로 처리합니다. 사용자의 디바이스와 뷰포트에 맞춰 이미지 크기를 자동으로 조정하고, WebP와 같은 최신 이미지 포맷을 지원하며, lazy loading을 통해 필요한 시점에만 이미지를 로드합니다. 이를 통해 페이지 로딩 속도를 개선하고 대역폭 사용을 최적화할 수 있습니다.
Script Component는 JavaScript 파일의 로딩을 최적화합니다. 스크립트의 로딩 전략을 'beforeInteractive', 'afterInteractive', 'lazyOnload' 등으로 세밀하게 제어할 수 있어, 페이지의 초기 로딩 성능을 향상시킬 수 있습니다.
자동 코드 스플리팅은 각 페이지에 필요한 JavaScript 코드만 을 따로 번들링하여 제공합니다. 이를 통해 초기 페이지 로드 시 불필요한 코드 다운로드를 방지하고, 필요한 코드만 로드함으로써 애플리케이션의 성능을 최적화합니다.
Incremental Static Regeneration(ISR)은 정적 페이지를 필요에 따라 점진적으로 재생성할 수 있게 해줍니다. 이를 통해 서버 부하를 줄이면서도 데이터의 신선도를 유지할 수 있으며, 사용자에게는 항상 빠른 응답 속도를 제공할 수 있습니다.
마지막으로, Edge Runtime을 통해 사용자와 가까운 위치의 서버에서 페이지를 제공할 수 있습니다. 이는 전 세계 사용자에게 일관된 빠른 응답 속도를 제공하며, 특히 동적 콘텐츠를 다루는 경우에 효과적입니다.
이러한 최적화 기능들은 Next.js 애플리케이션의 성능을 자동으로 향상시키며, 개발자는 비즈니스 로직 구현에 더 집중할 수 있게 됩니다.
6. "getStaticProps와 getServerSideProps의 차이점은 무엇인가요?"
Next.js에서 데이터 페칭을 위한 두 가지 핵심적인 함수인 getStaticProps와 getServerSideProps는 각각 다른 사용 사례와 장단점을 가지고 있습니다.
getStaticProps는 빌드 시점에 데이터를 가져와서 정적 페이지를 생성하는 함수입니다. 이 방식은 한 번 생성된 페이지를 캐시하여 모든 사용자에게 동일한 콘텐츠를 제공하므로, 매우 빠른 페이지 로딩 속도를 제공합니다. 블로 그 포스트, 제품 카탈로그, 문서화 페이지와 같이 데이터가 자주 변경되지 않는 페이지에 이상적입니다. 또한 CDN에서 캐시될 수 있어 전 세계 사용자에게 일관된 빠른 속도를 제공할 수 있습니다.
반면 getServerSideProps는 매 사용자 요청마다 서버에서 데이터를 새로 가져와 페이지를 생성합니다. 이는 실시간 데이터나 사용자별 맞춤 콘텐츠가 필요한 경우에 적합합니다. 예를 들어, 사용자 대시보드, 실시간 가격 정보, 개인화된 추천 시스템 등에서 활용될 수 있습니다. 하지만 매 요청마다 서버에서 페이지를 생성해야 하므로, getStaticProps에 비해 상대적으로 응답 속도가 느리고 서버 부하가 높을 수 있다는 특징이 있습니다.
이 두 함수는 개발자가 페이지의 특성과 요구사항에 따라 적절한 데이터 페칭 전략을 선택할 수 있게 해주며, 이를 통해 최적의 사용자 경험을 제공할 수 있습니다.
7. "Next.js에서 API 라우트를 어떻게 구현하나요?"
Next.js는 백엔드 API를 구현하기 위한 내장 기능으로 API 라우트를 제공합니다. 이는 별도의 백엔드 서버 설정 없이도 풀스택 애플리케이션을 구축할 수 있게 해주는 강력한 기능입니다.
API 라우트의 기본 구현은 pages/api 디렉토리 내에 JavaScript 파일을 생성하는 것으로 시작됩니다. 예를 들어, pages/api/users.js 파일에서는 handler 함수를 export하여 API 엔드포인트를 생성할 수 있습니 다. 이 handler 함수는 요청(req)과 응답(res) 객체를 매개변수로 받아 HTTP 요청을 처리합니다.
HTTP 메소드에 따른 다른 동작을 구현하기 위해서는 req.method를 사용하여 요청 메소드를 확인하고 적절한 처리를 할 수 있습니다. 예를 들어, GET 요청에서는 데이터를 조회하고, POST 요청에서는 새로운 데이터를 생성하는 등의 로직을 구현할 수 있습니다.
동적 API 라우트는 pages/api 디렉토리 내에서 대괄호([])를 사용하여 구현할 수 있습니다. 예를 들어, pages/api/users/[id].js 파일을 생성하면 '/api/users/1'과 같은 동적 경로를 처리할 수 있으며, req.query를 통해 동적 파라미터에 접근할 수 있습니다.
또한, Next.js는 API 라우트에 미들웨어를 적용할 수 있는 기능을 제공합니다. 이를 통해 인증, 로깅, 에러 처리 등의 공통 기능을 여러 API 라우트에 걸쳐 재사용할 수 있습니다. 미들웨어는 요청이 처리되기 전에 실행되어 요청을 수정하거나 응답을 조작할 수 있으며, 이는 코드의 재사용성과 유지보수성을 높여줍니다.