logo









블로그

다양한 이야기를 공유합니다

이달의 블로그

더보기

App Router 로 변경하면서 - Route


next/router → next/navigationPage Router import { useRouter } from "next/router"; export default function PostPage() { const router = useRouter();...

App Router 로 변경하면서 - fetch 를 선택한 이유


axios를 버리고 fetch와 QueryClient를 선택한 이유프로젝트를 Pages Router에서 App Router로 마이그레이션하면서 가장 많이 고민한 부분은 "데이터를 어떻게 가져올 것인가" 였다.기존에는 axios로 모든 요청을 처리했고, 그게 당연하다고 생각했다. 하지만 App Router로 전환하면서 두 가지 큰 변화를 겪었다.서버 컴포넌트에서는 fetch를 써야 Next.js의 캐싱이 작동한다클라이언트 컴포넌트의 비동기 상태는 React Query(TanStack Query)의 QueryClient로 관리한다Pages Router + axiosPages Router 시절의 데이터 패칭은 단순했다.// pages/posts/index.jsx export async function getServerSideProps() { const res = await axios.get('https://api.example.com/posts')...

App Router 로 변경하면서


App Router를 이해할 때 가장 먼저 봐야 하는 것들Next.js에서 Page Router를 쓰다가 App Router로 넘어오면 처음에는 폴더 구조가 바뀐 것처럼 보인다.pages/index.tsx가 app/page.tsx가 되고,pages/blog/[id].tsx가 app/blog/[id]/page.tsx가 된다.겉으로 보면 단순한 라우팅 방식의 변화처럼 느껴진다.하지만 실제로 App Router를 써보면 핵심은 폴더 구조가 아니다.더 중요한 변화는 렌더링을 바라보는 관점이다.Page Router에서는 보통 페이지 단위로 생각했다.이 페이지는 SSR로 할까? 이 페이지는 SSG로 할까? 이 페이지는 클라이언트에서 데이터를 가져올까?반면 App Router에서는 조금 더 세밀하게 생각하게 된다.이 컴포넌트는 서버에서 실행돼도 되는가?...

CSS의 종류와 동작


CSS는 언제 동작하나브라우저 기준으로 CSS는 대략 이렇게 동작한다.HTML 다운로드 → CSS 다운로드 → CSSOM 생성...

useRef, forwardRef, useImperativeHandle


React를 처음 사용할 때는 대부분 상태를 useState로 관리한다.const [value, setValue] = useState(''); 값이 바뀌면 React가 다시 렌더링하고, UI는 그 상태를 기준으로 업데이트된다.이게 React의 기본 흐름이다.하지만 모든 값을 state로 관리하는 게 항상 좋은 선택은 아니다. input에 focus 주기DOM 크기 측정하기이전 값 기억하기부모 컴포넌트에서 자식 input을 제어하기같은 작업은 state보다 ref가 더 적합한 경우가 많았다.이때 사용하는 Hook이 useRef다.useRef란useRef는 렌더링 사이에서 값을 유지할 수 있는 객체를 만든다.const ref = useRef(initialValue);반환값은 이런 형태다.{ current: initialValue...

Critters


Critters를 공부하면서 가장 크게 바뀐 생각처음엔 CSS를 그냥 스타일 파일 정도로 생각했다.심지어 JS 최적화에 비해 덜 중요하다고 느꼈다.왜냐면 대부분 성능 이야기를 하면번들 크기hydrationReact 렌더링같은 게 더 많이 보였기 때문이다. 근데 브라우저 렌더링 과정을 공부하면서 생각이 바뀌었다.특히 느낀 건 CSS는 생각보다 굉장히 강한 render-blocking resource라는 점이었다.브라우저는 CSS가 없으면 쉽게 화면을 못 그린다처음엔HTML 먼저 보여주고 CSS는 나중에 적용하면 되는 거 아닌가? 싶었다.실제 흐름은 보통HTML 파싱...

무한스크롤과 중복호출 방지


useInfiniteQueryuseInfiniteQuery는 페이지 단위 데이터를 가져올 때 사용하는 TanStack Query 훅이다.즉 전체 데이터를 한 번에 가져오는 게 아니라현재 페이지 데이터 가져오기 → 다음 페이지 요청 → 이어붙이기방식으로 동작한다.예를 들어게시글 무한스크롤댓글 목록상품 리스트SNS 피드같은 UI에 적합하다.기본 구조const {...

ISR


모든 페이지를 미리 만드는 건 생각보다 비싸다예를 들어 상품 페이지가 있다고 하자.상품 3만 개가 있다고 가정하면3만 개 HTML 생성이 필요하다.처음엔 그냥3만 번 만들면 되는 거 아닌가?라고 생각했다.근데 실제론- 빌드 시간 증가- 메모리 사용 증가- 서버 리소스 증가- 배포 시간 증가전부 연결된다.특히 작은 서버 환경이나 EC2 프리티어 같은 곳에 직접 배포해보면 체감이 온다.next build 자체가 무거워지고, 메모리 부족으로 프로세스가 죽기도 한다.실제로는 모든 페이지가 동일한 트래픽을 받지 않는다.예를 들어인기 상품 → 조회수 높음오래된 상품 → 거의 안 봄인데도 SSG만 사용하면 안 보는 페이지까지 전부 빌드 해야한다. 여기서 ISR의 필요성이 보이기 시작했다.ISR은 “필요할 때만 정적 생성”ISR(Incremental Static Regeneration)은 SSG 기반인데, 필요할 때만 페이지를 다시 생성한다.처음엔 이름이 좀 추상적이었다. 핵심은“빌드를 점진적으로 한다”문제 상황예를 들어id 1 ~ 25 → 인기 페이지 id 26 ~ 30000...

DAL


DAL(Data Access Layer)프로젝트 규모가 작을 때는 보통 페이지나 컴포넌트 안에서 바로 인증 검사를 한다.const user = await getUser(); if (!user) { redirect('/login');...

useReportWebVitals


성능 최적화를 할 때 처음에는 Lighthouse 점수만 확인했다.하지만 실제 서비스에서는 Lighthouse 점수와 사용자 체감 성능이 항상 일치하지 않았다.개발 환경에서는 괜찮아 보여도 실제 사용자는 전혀 다른 조건에서 서비스를 사용한다.느린 네트워크저사양 디바이스모바일 브라우저캐시 상태 차이페이지 진입 경로 차이Lighthouse만으로 부족했던 이유Lighthouse는 성능 개선 방향을 잡는 데 유용하다. 하지만 기본적으로 정해진 테스트 환경에서 실행된다.반면 실제 서비스에서는 사용자마다 조건이 다르다.예를 들어 개발자 PC에서는 LCP가 1.5초로 나와도, 실제 모바일 사용자는 4초 이상 걸릴 수 있다.또 특정 페이지는 전체 평균은 괜찮아 보여도, 일부 저사양 디바이스 사용자에게만 CLS가 높게 발생할 수 있다.이런 문제는 Lighthouse만으로는 발견하기 어렵다.그래서 운영 단계에서는 RUM, 즉 Real User Monitoring이 필요하다.기본 사용 코드'use client'; import { useReportWebVitals } from 'next/web-vitals'; export function WebVitals() {...

Meta


Next.js Metadata 시스템App Router에서는 직접 를 조작하기보다,metadata 객체를 선언적으로 관리한다.import type { Metadata } from 'next'; export const metadata: Metadata = { title: 'My Blog',...

Image


단순히 의 wrapper처럼 보인다.하지만 실제로는이미지 최적화lazy loadingresponsive imageCLS 방지포맷 최적화같은 작업이 내부적으로 같이 처리된다.왜 대신 Image를 사용하는가초기에는 로도 충분하다고 생각했다.그러나 img는 원본 이미지 그대로 전송모바일에서도 초고해상도 이미지 다운로드화면 밖 이미지까지 모두 요청이미지 로딩 전 layout shift 발생같은 문제가 쉽게 생긴다.특히 콘텐츠 서비스나 커머스에서는 이미지 개수가 많아질수록네트워크 비용초기 렌더링 속도사용자 체감 성능차이가 꽤 커졌다.이미지 최적화를 브라우저에게 맡기지 않고 프레임워크 차원에서 처리하는 방식이 훨씬 안정적이었다.자동 최적화next/image는 브라우저와 디바이스 환경에 맞춰 이미지를 최적화한다.대표적으로이미지 리사이즈포맷 최적화(WebP/AVIF)lazy loadingCLS 방지responsive image 생성같은 작업을 자동으로 처리한다.중요한 건 “같은 이미지라도 사용자 환경마다 다른 이미지가 전달된다”는 점이다.예를 들어 모바일에서는 작은 이미지를, 고해상도 디스플레이에서는 더 큰 이미지를 내려준다.이 차이가 모바일 성능에서 꽤 크게 체감됐다.CLS 방지일반 는처럼 크기를 지정하지 않으면,이미지가 로드되기 전까지 브라우저가 공간 크기를 모른다.그 결과텍스트 렌더링 → 이미지 로드 → 레이아웃 밀림현상이 발생한다.이게 CLS(Cumulative Layout Shift)다. 특히 모바일에서는 체감이 꽤 심했다.Image 컴포넌트는 width와 height를 통해 렌더링 전에 미리 공간을 확보한다....

추천 블로그

더보기

컴퓨터 구조


웹도 컴퓨터 위에서 돌아간다처음 웹개발을 하면 cpu, ram 등 컴퓨터 기초에 대해서 자세히 공부를 해야하나? 라고 생각했지만프로젝트 규모가 커지고, 브라우저 성능 최적화를 공부하고, Next.js를 EC2에 배포하면서컴퓨터구조나 기초적인 부분들도 다 알아야하는구나 라는 생각이 자연스럽게 든것 같다. CPU는 생각보다 “직접적인 존재”JS 실행Layout 계산Paint이벤트 처리전부 CPU 작업이었다."브라우저가 느리네?" 라고 생각이들면 CPU가 레이아웃 계산을 엄청 많이 수행하기 때문일수 있다. 프로그램은 CPU가 직접 실행한다우리가 작성한 JavaScript 코드도 결국 CPU가 실행한다.예를 들어 이런 코드가 있다고 해보자.const result = 10 + 20; console.log(result);개발자는 이 코드를 JavaScript로 작성하지만, 컴퓨터 입장에서는 결국 명령어를 해석하고 계산해야 한다.이때 CPU가 실제 연산을 수행한다.단순하게 보면 흐름은 이렇다.1. 프로그램 파일은 디스크에 저장되어 있다. 2. 실행하면 필요한 데이터가 RAM에 올라간다....

많이 본 블로그

더보기

CSS의 종류와 동작


CSS는 언제 동작하나브라우저 기준으로 CSS는 대략 이렇게 동작한다.HTML 다운로드 → CSS 다운로드 → CSSOM 생성...

Persist, Rehydrate


Zustand persist처음에는 전역 상태는 페이지를 이동하거나 새로고침하면 초기화되는 것이 당연하다고 생각했다.그런데 실제 서비스를 만들다 보면 새로고침 후에도 유지되어야 하는 UI 상태가 꽤 많다.예를 들어 상품 목록 페이지에서 사용자가 이런 필터를 설정했다고 하자.카테고리: 노트북 정렬: 낮은 가격순 가격 범위: 50만원 ~ 150만원...

Browser 저장소


편리한 Cookie쿠키는 브라우저가 요청마다 자동으로 서버에 포함한다예를 들어Set-Cookie: token=abc123를 서버가 보내면 브라우저가 저장한다.그리고 이후 요청마다Cookie: token=abc123를 자동으로 포함한다. 개발자가 직접 넣지 않아도 된다.CSRF 조심로그인 구현할 때도쿠키 저장 → 자동 인증 유지라서 굉장히 편하다.하지만 “자동으로 전송된다”는 건 공격자 입장에서도 편한 것이기 때문에 항상 CSRF 조심! Cookie는 서버와 “함께 쓰는 저장소” 느낌쿠키는 localStorage.setItem() 처럼 프론트가 마음대로 쓰는 저장소라기보다,서버가 브라우저에게 “이 값을 저장하고, 앞으로 이런 규칙으로 보내라”고 지시하는 인증 장치에 가깝다.Set-Cookie: token=abc; HttpOnly; Secure; SameSite=Lax이 한 줄 안에 보안 정책이 들어 있다.1. Set-Cookie로그인 성공 후 서버가 응답 헤더로 보낸다.HTTP/1.1 200 OK Set-Cookie: token=abc123; HttpOnly; Secure; SameSite=Lax브라우저는 이걸 보고 쿠키 저장소에 저장한다.이후 같은 조건의 요청을 보낼 때 자동으로 붙인다.GET /mypage HTTP/1.1...

useReportWebVitals


성능 최적화를 할 때 처음에는 Lighthouse 점수만 확인했다.하지만 실제 서비스에서는 Lighthouse 점수와 사용자 체감 성능이 항상 일치하지 않았다.개발 환경에서는 괜찮아 보여도 실제 사용자는 전혀 다른 조건에서 서비스를 사용한다.느린 네트워크저사양 디바이스모바일 브라우저캐시 상태 차이페이지 진입 경로 차이Lighthouse만으로 부족했던 이유Lighthouse는 성능 개선 방향을 잡는 데 유용하다. 하지만 기본적으로 정해진 테스트 환경에서 실행된다.반면 실제 서비스에서는 사용자마다 조건이 다르다.예를 들어 개발자 PC에서는 LCP가 1.5초로 나와도, 실제 모바일 사용자는 4초 이상 걸릴 수 있다.또 특정 페이지는 전체 평균은 괜찮아 보여도, 일부 저사양 디바이스 사용자에게만 CLS가 높게 발생할 수 있다.이런 문제는 Lighthouse만으로는 발견하기 어렵다.그래서 운영 단계에서는 RUM, 즉 Real User Monitoring이 필요하다.기본 사용 코드'use client'; import { useReportWebVitals } from 'next/web-vitals'; export function WebVitals() {...

ISR


모든 페이지를 미리 만드는 건 생각보다 비싸다예를 들어 상품 페이지가 있다고 하자.상품 3만 개가 있다고 가정하면3만 개 HTML 생성이 필요하다.처음엔 그냥3만 번 만들면 되는 거 아닌가?라고 생각했다.근데 실제론- 빌드 시간 증가- 메모리 사용 증가- 서버 리소스 증가- 배포 시간 증가전부 연결된다.특히 작은 서버 환경이나 EC2 프리티어 같은 곳에 직접 배포해보면 체감이 온다.next build 자체가 무거워지고, 메모리 부족으로 프로세스가 죽기도 한다.실제로는 모든 페이지가 동일한 트래픽을 받지 않는다.예를 들어인기 상품 → 조회수 높음오래된 상품 → 거의 안 봄인데도 SSG만 사용하면 안 보는 페이지까지 전부 빌드 해야한다. 여기서 ISR의 필요성이 보이기 시작했다.ISR은 “필요할 때만 정적 생성”ISR(Incremental Static Regeneration)은 SSG 기반인데, 필요할 때만 페이지를 다시 생성한다.처음엔 이름이 좀 추상적이었다. 핵심은“빌드를 점진적으로 한다”문제 상황예를 들어id 1 ~ 25 → 인기 페이지 id 26 ~ 30000...

Error Boundary와 StartTransition


Error Boundary가 Event Handler 에러를 잡지 못하는 이유처음 Error Boundary를 사용했을 때 헷갈렸던 부분이 있었다.분명 Error Boundary를 만들었는데,버튼 클릭 중 발생한 에러는 잡히지 않았다.예를 들어const handleClick = () => { throw new Error('Exception'); };...

Lifecycle


React Lifecycle을 Fiber 기준으로 이해하기처음 React의 lifecycle을 배울 때는 보통 이렇게 이해한다.mount update unmount클래스 컴포넌트 기준으로는componentDidMount...

Critters


Critters를 공부하면서 가장 크게 바뀐 생각처음엔 CSS를 그냥 스타일 파일 정도로 생각했다.심지어 JS 최적화에 비해 덜 중요하다고 느꼈다.왜냐면 대부분 성능 이야기를 하면번들 크기hydrationReact 렌더링같은 게 더 많이 보였기 때문이다. 근데 브라우저 렌더링 과정을 공부하면서 생각이 바뀌었다.특히 느낀 건 CSS는 생각보다 굉장히 강한 render-blocking resource라는 점이었다.브라우저는 CSS가 없으면 쉽게 화면을 못 그린다처음엔HTML 먼저 보여주고 CSS는 나중에 적용하면 되는 거 아닌가? 싶었다.실제 흐름은 보통HTML 파싱...

Image


단순히 의 wrapper처럼 보인다.하지만 실제로는이미지 최적화lazy loadingresponsive imageCLS 방지포맷 최적화같은 작업이 내부적으로 같이 처리된다.왜 대신 Image를 사용하는가초기에는 로도 충분하다고 생각했다.그러나 img는 원본 이미지 그대로 전송모바일에서도 초고해상도 이미지 다운로드화면 밖 이미지까지 모두 요청이미지 로딩 전 layout shift 발생같은 문제가 쉽게 생긴다.특히 콘텐츠 서비스나 커머스에서는 이미지 개수가 많아질수록네트워크 비용초기 렌더링 속도사용자 체감 성능차이가 꽤 커졌다.이미지 최적화를 브라우저에게 맡기지 않고 프레임워크 차원에서 처리하는 방식이 훨씬 안정적이었다.자동 최적화next/image는 브라우저와 디바이스 환경에 맞춰 이미지를 최적화한다.대표적으로이미지 리사이즈포맷 최적화(WebP/AVIF)lazy loadingCLS 방지responsive image 생성같은 작업을 자동으로 처리한다.중요한 건 “같은 이미지라도 사용자 환경마다 다른 이미지가 전달된다”는 점이다.예를 들어 모바일에서는 작은 이미지를, 고해상도 디스플레이에서는 더 큰 이미지를 내려준다.이 차이가 모바일 성능에서 꽤 크게 체감됐다.CLS 방지일반 는처럼 크기를 지정하지 않으면,이미지가 로드되기 전까지 브라우저가 공간 크기를 모른다.그 결과텍스트 렌더링 → 이미지 로드 → 레이아웃 밀림현상이 발생한다.이게 CLS(Cumulative Layout Shift)다. 특히 모바일에서는 체감이 꽤 심했다.Image 컴포넌트는 width와 height를 통해 렌더링 전에 미리 공간을 확보한다....

Store


상태관리처음 React를 배울 때는 상태관리가 그렇게 중요하게 느껴지지 않았다.컴포넌트 안에서const [count, setCount] = useState(0);정도만 써도 대부분 동작했다.문제는 프로젝트 규모가 커지면서 발생했다.예를 들어 로그인한 사용자 정보를 여러 컴포넌트에서 같이 사용해야 하는 상황이 생긴다.App └── Layout └── Header...

Rendering 최적화


프론트엔드 성능 최적화를 공부하면서 가장 크게 바뀐 생각처음엔 성능 최적화라고 하면 단순히Lighthouse 점수 올리기이미지 압축하기React memo 쓰기정도로 생각했다.실제로 EC2 프리티어에 직접 배포해보면서 생각이 완전히 바뀌었다.이거 커지면 커질수록 다 돈이잖아? 번들 크기처음엔 JS 번들 크기를 너무 가볍게 봤다."요즘 인터넷 빠른데 몇 MB쯤이야"라고 생각했다.근데 AWS 프리티어 EC2에 Next.js 프로젝트를 올리는 순간 체감이 왔다.npm install 중 멈춤next build 도중 프로세스 종료메모리 부족명령어 자체가 버벅임같은 상황을 꽤 자주 경험했다.그때 처음“아 번들 크기는 단순 프론트 문제만이 아니구나”라는 걸 느꼈다.실제로는빌드 메모리서버 리소스배포 시간실행 비용까지 전부 연결되어 있었다.코드 스플리팅예전엔 dynamic import를"있으면 좋은 최적화"정도로 생각했다.근데 실제로는 초기 로딩 자체를 바꾼다.const Chart = dynamic(() => import('./Chart'));이렇게 하면 차트를 첫 화면에서 굳이 안 받아도 된다.즉초기 JS 감소초기 파싱 감소초기 실행 감소효과가 생긴다.특히 차트 라이브러리나 에디터처럼 무거운 패키지에서 체감이 크다.라이브러리 선택 처음엔 라이브러리 크기를 거의 신경 안 썼다.근데 번들 분석을 해보면 꽤 놀랍다.예를 들어moment lodash 전체 import chart library...

컴퓨터 구조


웹도 컴퓨터 위에서 돌아간다처음 웹개발을 하면 cpu, ram 등 컴퓨터 기초에 대해서 자세히 공부를 해야하나? 라고 생각했지만프로젝트 규모가 커지고, 브라우저 성능 최적화를 공부하고, Next.js를 EC2에 배포하면서컴퓨터구조나 기초적인 부분들도 다 알아야하는구나 라는 생각이 자연스럽게 든것 같다. CPU는 생각보다 “직접적인 존재”JS 실행Layout 계산Paint이벤트 처리전부 CPU 작업이었다."브라우저가 느리네?" 라고 생각이들면 CPU가 레이아웃 계산을 엄청 많이 수행하기 때문일수 있다. 프로그램은 CPU가 직접 실행한다우리가 작성한 JavaScript 코드도 결국 CPU가 실행한다.예를 들어 이런 코드가 있다고 해보자.const result = 10 + 20; console.log(result);개발자는 이 코드를 JavaScript로 작성하지만, 컴퓨터 입장에서는 결국 명령어를 해석하고 계산해야 한다.이때 CPU가 실제 연산을 수행한다.단순하게 보면 흐름은 이렇다.1. 프로그램 파일은 디스크에 저장되어 있다. 2. 실행하면 필요한 데이터가 RAM에 올라간다....