Loading the catalog…
Loading the catalog…
시작 얼마 전 성시경이 나오는 유튜브 쇼츠를 봤다 언어를 독학하시면서 "어제 배웠던 내용을 오늘이면 까먹는다"고 하셨다 그래도 계속 공부한다고 하시면서 "계속하면 된다는 것을 안다" 라는 걸 느꼈다고 하셨다 나도 지금 이 공부를 하면서 당장은 어제 배웠던 내용을 기억하지 못하기도 하지만 계속하다보면 되겠지..라는 생각으로 일단 공부를 한다 key 리액트에서 배열을 이용해 여러 컴포넌트를 렌더링할 때는 각 요소에 key를 지정해야 한다 const users = [ { id: 1, name: "Lee" }, { id: 2, name: "Kim" }, { id: 3, name: "Park" }, ]; function UserList() { return ( <ul> {users.map((user) => ( <li key={user.id}> {user.name} </li> ))} </ul> ); } 리액트가 이전 렌더링 결과와 새로운 렌더링 결과를 비교할 때 각 요소가 이전의 어떤 요소와 같은 요소인지 판단하는 기준 으로 사용한다 재조정 리액트에서는 상태가 props가 변경되면 새로운 ui를 계산한다 이전 UI ↓ 상태 변경 ↓ 새로운 UI 계산 ↓ 이전 UI와 새로운 UI 비교 ↓ 필요한 부분만 변경 이렇게 이전 렌더링 결과와 새로운 렌더링 결과를 비교하여 실제로 무엇을 변경해야 하는지 결정하는 과정을 재조정 이라고 한다 예를 들어 다음과 같은 목록이 있다고 해보자 Lee Kim Park 새로운 사용자가 중간에 추가되었다 Lee Choi Kim Park 리액트는 새로운 목록을 보면서 Lee는 기존 Lee인가? Choi는 새로운 요소인가? Kim은 기존 Kim인가? Park는 기존 Park인가? 를 판단해야 한다 이때 key가 사용된다 <li key={1}>Lee</li> <li key={4}>Choi</li> <li key={2}>Kim</li> <li key={3}>Park</li> 리액트는 key를 이용해 기존 요소와 새 요소의 관계를 파악할 수 있다 key 1 → 기존 Lee key 4 → 새롭게 추가 key 2 → 기존 Kim key 3 → 기존 Park 따라서 목록의 순서가 변경되더라도 요소의 정체성을 유지할 수 있다 key가 중요한 이유 리액트는 같은 위치에 있는 요소만 보는 것이 아니라 key를 이용해 어떤 요소가 이전 요소와 같은 것인지 판단한다 예를 들어 const users = [ { id: 1, name: "Lee" }, { id: 2, name: "Kim" }, ]; 이 목록이 const users = [ { id: 2, name: "Kim" }, { id: 1, name: "Lee" }, ]; 처럼 순서가 바뀌었다고 해보자 적절한 key가 있다면 리액트는 key 1 → Lee가 이동 key 2 → Kim이 이동 이라고 판단할 수 있다 즉 새로운 요소 두 개가 생겼다고 생각하는 것이 아니라 기존 요소가 위치를 변경한 것으로 판단할 수 있다 key와 상태 key는 단순히 DOM 변경 최적화에만 영향을 주는 것이 아니다 리액트는 컴포넌트의 상태를 렌더 트리에서의 위치와 key를 기준으로 연결한다 예를 들어 function App({ user }) { return ( <Profile key={user.id} user={user} /> ); } 사용자가 변경되어 key가 달라지면 리액트는 기존 Profile 과 같은 컴포넌트라고 보지 않는다 Profile key=1 ↓ 사용자 변경 ↓ Profile key=2 리액트는 기존 컴포넌트를 제거하고 새로운 컴포넌트를 만든 것으로 취급할 수 있다 따라서 내부 state도 초기화된다 이 특성을 이용해 폼 상태 등을 의도적으로 초기화할 수도 있다 <Chat key={selectedUser.id} user={selectedUser} /> 사용자가 변경될 때마다 채팅 엽력 상태를 새롭게 초기화하는 식으로 사용할 수 있다 리액트는 state를 컴포넌트 자체가 아니라 렌더 트리의 위치와 연결해서 관리하며 key는 같은 위치에서도 컴포넌트의 정체성을 구분하는 데 사용할 수 있다 배열의 index를 key로 사용함녀 안 되는 이유 다음과 같이 작성할 수도 있다 users.map((user, index) => ( <User key={index} user={user} /> )); 목록이 절대 변경되지 않는다면 문제가 드러나지 않을 수도 있다 하지만 요소가 추가되거나 삭제되거나 순서가 변경되면 문제가 발생할 수 있다 예를 들어 index 0 → Lee index 1 → Kim index 2 → Park 여기에서 맨 앞에 Choi 가 추가되면 index 0 → Choi index 1 → Lee index 2 → Kim index 3 → Park 이전에는 index 0 이 Lee 였지만 이제는 Choi 가 되었다 리액트 입장에서는 key 0 이전 → Lee 현재 → Choi 인데 key는 동일하다 따라서 실제 데이터의 정체성과 리액트가 판단하는 요소의 정체성이 어긋날 수 있다 특히 리스트 아이템 내부에 state가 있다면 잘못된 항목에 기존 state가 유지되는 문제가 발생할 수 있다 따라서 일반적으로 데이터 자체를 식별할 수 있는 안정적인 값을 사용하는 것이 좋다 key={user.id} key는 전역적으로 유일해야 할까? key가 애플레케이션 전체에서 유일할 필요는 없다 같은 부모 아래에 있는 형제 요소들 사이에서만 구분할 수 있으면 된다 리액트 공식 문서에서도 key는 전역적으로 유일할 필요가 없고 부모 안에서의 위치를 구분한다고 설명한다 key의 핵심 key는 단순한 식별자처럼 보이지만 리액트 재조정 과정에서 중요한 역할을 한다 이전 렌더 트리 ↓ key 비교 ↓ 새로운 렌더 트리 같은 key → 같은 요소로 판단 가능 새로운 key → 새로운 요소로 판단 사라진 key → 제거된 요소로 판단 따라서 key는 목록에서 요소의 정체성을 리액트에게 알려주는 값 이라고 이해할 수 있다 왜? 1. key가 바뀌면 왜 state가 초기화가 되는 거지? 리액트의 state는 컴포넌트 함수 자체에 저장되는 것이 아니라 렌더 트리에서 해당 컴포넌트의 정체성과 연결되어 관리된다 재조정 과정에서 같은 위치에 같은 컴포넌트가 존재하고 key도 동일하다면 리액트는 기존 컴포넌트라고 판단하여 state를 유지할 수 있다 반대로 key가 변경되면 리액트는 이전 컴포넌트와 다른 컴포넌트라고 판단한다 따라서 기존 컴포넌트가 제거되고 새로운 컴포넌트가 생성되면서 기존 state도 함께 사라지고 새로운 state가 초기값부터 만들어진다 즉 key가 state를 직접 초기화하는 것이 아니라 key 변경으로 컴포넌트의 정체성이 달라지고 새로운 컴포넌트로 처리되기 때문에 state가 초기화 되는 것 이다 나만의 정리 리액트에서는 반복적인 컴포넌트를 구성하는 경우에는 key 속성을 사용합니다. key 속성은 해당 컴포넌트의 정체성을 의미하며 이 정체성을 기준으로 리액트는 재조정 과정에서 요소를 재사용하게 됩니다. 따라서 불필요한 DOM 생성이나 삭제를 피하고 실제로 변경된 부분만 최소한으로 업데이트해 성능 최적화가 가능합니다. 이런 정체성을 유지시키기 위해서 key에는 고유한 값을 설정해야 합니다. Next.js 리액트는 ui를 만들기 위한 라이브러리이다 리액트만으로도 애플리케이션을 만들 수는 있지만 실제 서비스를 만들기 위해서는 ui 이외에도 여러 기능이 필요하다 예를 들어 라우팅 데이터 패칭 코드 분할 서버 렌더링 정적 렌더링 서버 코드 이미지 최적화 메타데이터 빌드 설정 등을 함께 구성해야 한다 Next.js는 이런 기능을 리액트 위에 제공하는 리액트 프레임워크 다 현재 Next.js 공식 문서에서도 full-stack web application을 만들기 위한 리액트 프레임워크로 설명하고 있으며 리액트 컴포넌트를 ui에 사용하면서 Next.js가 추가 기능과 최적화를 제공한다고 설명한다 리액트와의 관계 둘의 관계는 다음처럼 생각할 수 있다 React → UI를 구성하는 라이브러리 Next.js → React를 기반으로 실제 웹 애플리케이션을 구성하기 위한 여러 기능을 제공하는 프레임워크 Next.js를 사용하는 이유 1. 라우팅 리액트 자체에는 애플리케이션 페이지를 관리하는 라우터가 포함도어 있지 않다 Next.js는 파일 구조를 기반으로 라우팅을 제공한다 App Router에서는 app/ ├─ page.tsx ├─ login/ │ └─ page.tsx └─ posts/ └─ page.tsx 가 / ↓ app/page.tsx /login ↓ app/login/page.tsx /posts ↓ app/posts/page.tsx 처럼 URL과 연결된다 라우팅을 별도의 라이브러리와 설정으로 직접 구성하지 않아도 된다 2. 다양한 렌더링 방식 리액트를 브라우저에서만 실행하면 전통적인 CSR 형태가 된다 Next.js에서는 서버에서 ui를 렌더링하거나 미리 정적으로 생성하는 방식 등을 함께 사용할 수 있다 Client Rendering Server Rendering Static Rendering Streaming 등을 페이지와 데이터 특성에 따라 조합할 수 있다 현재 App Router에서는 정적 렌더링과 동적 렌더링을 지원하며 정적 렌더링은 빌드 시점이나 재검증 시 서버에서 생성되고 동적 렌더링은 요청 시점에 서버에서 렌더링된다 3. 서버에서 데이터 가져오기 Next.js App Router에서는 서버 컴포넌트를 이용해 서버에서 직접 데이터를 가져올 수 있다 export default async function Page() { const posts = await getPosts(); return ( <PostList posts={posts} /> ); } 이 코드는 브라우제어서 직접 실행할 필요가 없다 Server ↓ 데이터 조회 ↓ UI 계산 ↓ 결과 전달 따라서 데이터베이스나 내부 API처럼 서버에 접근해야 하는 리소스를 다루기 편리하다 4. 서버 컴포넌트 App Router에서는 리액트 서버 컴포넌트를 사용할 수 있다 서버 컴포넌트에서는 서버에서 실행되기 때문에 해당 컴포넌트의 자바스크립트를 브라우저에서 실행할 필요가 없는 경우 클라이언트 자바스크립트 양을 줄이는 데 도움이 될 수 있다 Server Component → 서버에서 실행 → 데이터 조회 가능 → 클라이언트 JS 부담 감소 가능 Client Component → 브라우저에서 실행 → 상태 / 이벤트 / 브라우저 API 사용 가능 따라서 모든 컴포넌트를 클라이언트에서 실행하는 대신 필요한 부분만 클라이언트 컴포넌트로 만들 수 있다 5. 코드 분할 큰 애플리케이션의 모든 자바스크립트를 한 번에 내려받으면 초기 로딩 비용이 증가할 수 있다 Next.js는 라우트를 기준으로 필요한 자바스크립트를 나누어 제공하는 기능을 기본적으로 제공한다 전체 애플리케이션 코드 ↓ 한 번에 모두 다운로드 X 현재 페이지에 필요한 코드 ↓ 우선 다운로드 리액트만 이용해 애플리케이션을 구성한다면 번들링과 코드 분할 같은 설정을 별도로 고려해야 할 수 있지만 Next.js는 이러한 하위 도구들을 기본적으로 구성한다 6. Streaming 서버에서 페이지를 렌더링한다고 해서 모든 데이터를 기다린 다음 한 번에 응답할 필요는 없다 Next.js는 리액트의 Suspense를 이용한 스트리밍을 지원한다 Page Header → 준비 완료 Sidebar → 준비 완료 Dashboard → 느린 데이터 스트리밍을 이용하면 Header + Sidebar ↓ 먼저 전달 Dashboard ↓ 준비되면 나중에 전달 처럼 페이지를 여러 부분으로 나누어 전달할 수 있다 Next.js 공식 문서에서도 스트리밍은 route를 작은 chunk로 나누어 준비된 부분부터 클라이언트에 전달하여 느린 데이터가 전체 화면을 막지 않게 할 수 있다고 설명한다 7. 개발 환경과 최적화 기능 Next.js는 웹 애플리케이션을 만들 때 자주 필요한 여러 기능을 기존 제공한다 예를 들어 Image 최적화 Font 최적화 Metadata 관리 Route Handler Server Actions Caching Prefetching 등을 프레임워크 안에서 사용할 수 있다 따라서 개발자가 애플리케이션 구조를 처음부터 모두 직접 구성해야 하는 부담을 줄일 수 있다 Next.js의 장점 장점을 정리하면 React 기반의 통합된 개발 구조 파일 기반 라우팅 서버 렌더링과 정적 렌더링 지원 Server Component 사용 가능 Streaming 지원 코드 분할과 여러 최적화 기능 기본 제공 서버 코드와 클라이언트 코드를 하나의 프로젝트에서 구성 가능 등이 있다 특히 페이지마다 하나의 렌더링 방식만 강제하는 것이 아니라 요구사항에 따라 서버와 클라이언트 렌더링을 조합할 수 있다는 점이 큰 특징이다 Next.js의 단점 1. 리액트보다 학습해야 할 개념이 많다 리액트만 사용할 때는 주로 컴포넌트와 상태 관리에 집중할 수 있다 Next.js에서는 추가적으로 Server Component Client Component Static Rendering Dynamic Rendering Caching Revalidation Streaming Server Actions Route Handler 등을 이해해야 한다 특히 서버와 클라이언트의 경계를 구분해야 하기 때문에 처음에는 복잡하게 느껴질 수도 있다 2. 캐싱과 렌더링 구조가 복잡해질 수 있다 Next.js에서는 데이터나 페이지의 특성에 따라 캐싱과 렌더링 방식이 달라질 수 있다 이 데이터는 언제 갱신되는가? 서버에서 처리되는가? 클라이언트에서 다시 요청하는가? 정적으로 생성되는가? 요청마다 새로 렌더링되는가? 를 고려해야 한다 단순한 리액트 CSR 애플리케이션보다 데이터 흐름을 이해해야 할 범위가 넓어진다 3. 서버 운영을 고려해야 할 수도 있다 모든 페이지가 정적 파일로만 만들어지는 것이 아니라 SSR이나 Server Action, Route Handler 등을 사용한다면 서버 실행 환경이 필요하다 Static Site → CDN 중심으로 제공 가능 Dynamic Next.js → 서버 실행 환경 필요 따라서 배포 구조와 서버 비용도 고려해야 할 수도 있다 4. 프레임워크에 대한 의존성이 커진다 Next.js는 라우팅부터 데이터 처리, 캐싱, 빌드까지 많은 부분을 담당한다 편리한 만큼 애플리케이션 구조가 Next.js의 규칙과 기능에 크게 의존할 수 있다 따라서 단순히 React 문법만 아는 것보다 Next.js의 렌더링 및 캐싱 모델까지 이해해야 한다 Next.js는 언제 사용하면 좋을까 Next.js는 다음과 같은 요구사항이 있을 때 특히 유용하다 검색 노출이 중요한 페이지 초기 콘텐츠를 서버에서 제공하고 싶은 경우 정적 페이지와 동적 페이지가 함께 존재하는 서비스 서버와 클라이언트 코드를 하나의 React 프로젝트에서 관리하고 싶은 경우 페이지 단위로 다양한 렌더링 전략이 필요한 서비스 반대로 매우 단순한 내부 도구나 SEO가 필요 없는 작은 CSR 애플리케이션이라면 Next.js의 모든 기능이 반드시 필요한 것은 아니다 결국 Next.js를 사용하는 이유는 단순히 리액트보다 성능이 좋기 때문이 아니라 웹 애플리케이션에 필요한 여러 기능과 렌더링 전략을 리액트 위에서 통합적으로 제공하기 때문 이라고 이해할 수 있다 왜? 1. 스트리밍 방식과 클라이언트 패칭으로 인한 로딩이 사용자에게는 무슨 차이가 있는 거지? 두 방식 모두 사용자에게 로딩 ui를 보여줄 수 있지만 데이터 요청과 ui 생성이 시작되는 시점에 차이가 있다 클라이언트 패칭은 브라우저에서 자바스크립트와 리액트가 실행된 이후 에 데이터 요청을 시작하는 경우가 많이 초기 로딩에서 추가적인 네트워크 워터폴이 발생할 수 있다 반면 서버 스트리밍은 서버에서 데이터 요청과 렌더링을 진행 하면서 먼저 준비된 ui를 클라이언트로 전달하고 느린 영역은 Suspense의 fallback을 먼저 보여준 뒤 준비되는 대로 추가 ui를 전달할 수 있다 따라서 사용자는 느린 데이터 하나 때문에 전체 페이지를 기다리지 않고 준비된 부분부터 확인할 수 있다 다만 사용자 상호작용 이후 데이터를 갱신하는 경우에는 클라이언트 패칭이 더 자연스러운 경우도 있기 때문에 두 방식은 상황에 따라 함께 사용할 수 있다 2. API Route가 뭐고 서버리스 함수는 뭐지? API Route는 Next.js 애플리케이션 내부에서 HTTP API endpoint를 만드는 기능이다 Pages Router에서는 API Route라고 부르며 현재 App Router에서는 비슷한 역할을 Route Handler가 담당한다 반면 Serverless Function은 API를 작성하는 방법이 아니라 서버를 직접 관리하지 않고 요청이 발생할 때 서버 코드를 실행하도록 하는 클라우드 실행 방식이다 따라서 API Route나 Route Handler가 배포 환경에 따라 Serverless Function으로 실행될 수는 있지만 두 개념이 같은 것은 아니다 3. Server Actions는 뭐지? API Route와의 차이는? 서버 액션은 서버에서 실행되는 비동기 함수를 리액트 컴포넌트에서 함수처럼 호출할 수 있도록 하는 기능이다 내부적으로 클라이언트와 서버 사이의 요청은 여전히 발생하지만 개발자가 별도의 API endpoint와 fetch 요청을 직접 작성하지 않아도 된다 Route Handler는 GET, POST와 같은 HTTP endpoint 자체를 제공하기 때문에 외부 서비스, 다른 클라이언트, 웹훅 등에서도 사용할 수 있다 반면 서버 액션은 현재 리액트 ui에서 발생하는 데이터 생성, 수정, 삭제와 같은 작업을 서버 로직과 연결할 때 사용하기 자연스럽고 Next.js의 캐시 재검증 기능과도 쉽게 연동할 수 있다 일반적으로 스프링과 같이 별도의 백엔드 서버가 이미 구성되어 있고 해당 서버가 CRUD API를 제공한다면 단순한 데이터 조회나 생성, 수정, 삭제를 위해 Route Handler나 서버 액션을 반드시 추가할 필요는 없다 클라이언트에서는 백엔드 API를 직접 호출하고 서버 컴포넌트에서는 서버에서 백엔드 API를 직접 호출할 수 있다 다만 인증 처리, BFF 역할, 여러 API 응답의 조합, 외부에 노출할 HTTP endpoint, Next.js의 캐시 재검증과 Mutation을 연결해야 하는 경우처럼 Next.js 서버 계층이 해결해야 할 명확한 목적이 있다면 Route Handler나 서버 액션을 추가로 사용할 수 있다 따라서 외부에서도 사용할 명시적인 HTTP API가 필요하다면 Route Handler를 사용하고 현재 Next.js ui에서 발생하는 서버 Mutation을 처리한다면 서버 액션을 고려할 수 있으며 별도의 백엔드가 존재하는 구조에서는 필요한 이유가 있을 때만 이러한 Next.js 서버 계층을 추가하는 것이 좋다 나만의 정리 Next.js는 리액트에 추가로 실제 서비스에 필요한 부분들을 기본 기능으로 제공하는 프레임워크입니다. 현재 next.js는 app router를 기본으로 하여 파일 구조를 기반으로 라우팅을 제공하기 때문에 별도의 라우팅 설정을 하지 않아도 됩니다. 또한 다양한 렌더링 방식을 지원하여 사용자 경혐을 개선할 수 있습니다. next.js는 풀스택 프레임워크로 서버에서 실행되는코드와 API도 함께 작성할 수 있기 때문에 서비스에 따라 별도의 백엔드 구성 없이 하나의 next.js 애플리케이션으로 개발이 가능합니다. 이외에도 이미지 최적화, 스트리밍, 코드 분할 등 웹 애플리케이션 개발에 필요한 여러 최적화 기능을 제공합니다. 렌더링 방식 웹 페이지를 사용자에게 보여주기 위해서는 HTML이 만들어져야 한다 CSR, SSR, SSG의 가장 큰 차이는 이 HTML을 언제 어디서 만들어 사용자에게 전달하는가 에 있다 CSR → 브라우저에서 렌더링 SSR → 요청이 들어올 때 서버에서 렌더링 SSG → 요청 이전에 서버에서 미리 렌더링 CSR CSR(Client Side Rendering)은 브라우저에서 자바스크립트를 실행해 ui를 만드는 방식이다 서버에서 기본 HTML과 자바스크립트를 전달한다 Browser ↓ 페이지 요청 Server ↓ HTML + JavaScript 전달 Browser ↓ JavaScript 다운로드 ↓ JavaScript 실행 ↓ UI 렌더링 전통적인 리액트 SPA에서 많이 사용되는 방식이다 예를 들어 서버에서 다음과 같은 HTML을 내려줄 수 있다 <div id="r
What RADAR observed and classified to build this opportunity. It is what the source published, not a verification that the offer is still active.
22일차. key, Next.js, 렌더링 방식. 시작 얼마 전 성시경이 나오는 유튜브 쇼츠를 봤다 언어를 독학하시면서 "어제 배웠던 내용을 오늘이면 까먹는다"고 하셨다 그래도 계속 공부한다고 하시면서 "계속하면 된다는 것을 안다" 라는 걸 느꼈다고 하셨다 나도 지금 이 공부를 하면서 당장은 어제 배웠던 내용을 기억하지 못하기도 하지만 계속하다보면 되겠지..라는 생각으로 일단 공부를 한다 key 리액트에서 배열을 이용해 여러 컴포넌트를 렌더링할 때는 각 요소에 key를 지정해야 한다 const users = [ { id: 1, name: "Lee" }, { id: 2, name: "Kim" }, { id: 3, name: "Park" }, ]; function UserList() { return (…
Open source