<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>onion_0.log</title>
        <link>https://velog.io/</link>
        <description>눈물이 나도 코드를 계속 까보는 양파</description>
        <lastBuildDate>Mon, 20 Oct 2025 08:37:51 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>onion_0.log</title>
            <url>https://velog.velcdn.com/images/onion_0/profile/dcd9aab6-3c90-4a7d-becb-18cac400e840/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. onion_0.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/onion_0" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Next.js로 API 연결하기 (App Route 기준)]]></title>
            <link>https://velog.io/@onion_0/Next.js%EB%A1%9C-API-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0-App-Route-%EA%B8%B0%EC%A4%80</link>
            <guid>https://velog.io/@onion_0/Next.js%EB%A1%9C-API-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0-App-Route-%EA%B8%B0%EC%A4%80</guid>
            <pubDate>Mon, 20 Oct 2025 08:37:51 GMT</pubDate>
            <description><![CDATA[<h1 id="nextjs로-api-연결하는-방법">Next.js로 API 연결하는 방법</h1>
<p>Next.js에서 개발하면서 API 연결을 처음으로 시도해보고 있다.
우선 APP Route를 기준으로 API 연결 방법을 시도해보려고 한다.</p>
<p>보통 API를 불러올 때 기본적으로 Next.js에서는 <code>fetch()</code>를 사용하기에 이 기능을 활용하여 시도해 볼 예정이다.</p>
<p>추가로 axios도 많이 사용된다고 하나, 우선 기본 기능부터 숙달한 후 사용해보고자 한다.</p>
<h1 id="api-연결하기">API 연결하기</h1>
<h2 id="1-백엔드에서-제작한-api-준비하기">1. 백엔드에서 제작한 API 준비하기</h2>
<h2 id="2-nextjs-앱-생성">2. Next.js 앱 생성</h2>
<h2 id="3-nextjs-앱-내부에-api를-불러오기-위한-파일폴더-생성">3. Next.js 앱 내부에 API를 불러오기 위한 파일/폴더 생성</h2>
<h3 id="0-전체-작동-흐름">0) 전체 작동 흐름</h3>
<ol>
<li>사용자가 필터링 조건 또는 검색어 입력</li>
<li>클라이언트가 서버(API)로 해당 필터링 조건 전달</li>
<li>서버(API)는 전달 받은 필터링 조건으로 DB에서 데이터 조회 후 조건에 맞는 데이터 제공</li>
<li>클라이언트는 서버가 전달한 조건에 맞는 데이터를 받아서 화면에 출력</li>
<li>(새로고침 하면) 입력한 파라미터는 사라지도록 = 새로고침 하면 검색값 초기화</li>
</ol>
<h3 id="1-환경변수-파일-생성">1) 환경변수 파일 생성</h3>
<p><code>.env.local</code> 파일 생성</p>
<ul>
<li><p>해당 파일은 최상위 경로에 생성</p>
</li>
<li><p><code>API_BASE_URL</code> : 백엔드 서버 주소 (브라우저에 노출되지 않을 주소)</p>
</li>
<li><p><code>NEXT_PUBLIC_API_BASE_URL</code> : API 엔드포인트 시작 경로 (클라이언트에 노출될 경로)</p>
</li>
<li><p>별도 설정을 할 필요 없이 npm run dev 하면 내부적으로 process.env에 .env 파일에 정의된 값이 들어가서 자동으로 인식됨</p>
</li>
<li><p>단, 변수명 이름 주의 (아래의 표 참고)</p>
<table>
<thead>
<tr>
<th>실행 위치</th>
<th>접근 가능한 변수</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td><strong>서버(Node.js)</strong></td>
<td>모든 <code>process.env.*</code> 변수</td>
<td>브라우저에 안 노출되기 때문</td>
</tr>
<tr>
<td><strong>클라이언트(React 브라우저 코드)</strong></td>
<td><strong><code>NEXT_PUBLIC_</code>으로 시작하는 변수만</strong></td>
<td>Next.js가 빌드 시 안전하게 삽입해줌</td>
</tr>
</tbody></table>
</li>
</ul>
<br>

<p>만약 <code>src/app/api</code>를 통해 프록시를 사용하지 않고 <code>lib/api</code>로 바로 API를 불러온다면 <code>NEXT_PUBLIC_API_BASE_URL</code>에서 모두 정의해야 함</p>
<h3 id="2-프록시를-사용한다면-게이트웨이-폴더파일-생성">2) (프록시를 사용한다면) 게이트웨이 폴더/파일 생성</h3>
<p><code>src/app/api</code> 폴더 생성</p>
<ul>
<li>비밀/보안: 서버 전용 env(API_BASE_URL, 토큰 등)를 사용해 백엔드 호출 / 토큰은 여기서만 붙임</li>
<li>CORS 우회: 브라우저는 같은 오리진(/api/...)으로 호출 → CORS 문제 없음</li>
<li>요청 검증/정규화: 쿼리·바디를 검사(필수값, 타입)하고 백엔드 요구 포맷으로 변환</li>
<li>캐싱/리밋/로그/트레이싱: 공통으로 처리(Next revalidate, rate limit, 에러 로깅)</li>
<li>에러 매핑: 백엔드 에러를 프론트가 쓰기 쉬운 JSON 형태로 통일</li>
<li>응답 매핑: 백엔드 응답 스키마를 프론트 표준 스키마로 변환(키 이름, 필드 누락 보정 등)</li>
<li>인증 연동: 쿠키/세션에서 유저 인증을 읽어 백엔드로 전달(헤더/쿠키)</li>
</ul>
<h3 id="3-api-호출을-위한-폴더파일-생성">3) API 호출을 위한 폴더/파일 생성</h3>
<p><code>src/lib/api</code> 폴더 생성</p>
<ul>
<li>API에 파라미터를 전달하고 데이터를 받을 수 있는 함수를 정의</li>
<li>이때 API에 전달하는 정보와 받아오는 정보 모두에 대한 타입 및 인터페이스 정의<ul>
<li>인터페이스 이름은 상관이 없지만 필드 이름과 타입은 백엔드 API에서 정의된 것과 동일해야 함</li>
<li>만약 입력 받아야 하는 값이 아닌, 드롭다운과 같이 정해진 값 내에서 선택해야 하는 것들이면 해당 값들을 모은 type을 정의하는 것이 좋음</li>
</ul>
</li>
<li>API 호출 함수 정의<ul>
<li>전달 받는 파라미터를 통해 어떤 데이터를 받아와서 전달할지 정의</li>
<li>전달 받게 되는 파라미터 정의 (이때, 기본값이 정해져 있는 파라미터면 명시)</li>
<li><code>URLSearchParams()</code>를 통해 전달된 파라미터 값을 각각 할당</li>
<li>정의된 파라미터 값과 <code>.env.local</code> 파일에서 정의한 <code>BASE_URL</code>을 통해 <code>fetch()</code>로 API 요청</li>
<li>오류 감지 코드 포함<ul>
<li><code>fetch()</code>는 네트워크 오류(DNS 실패, 타임아웃 등)에서만 throw 하고
HTTP 4xx/5xx는 throw하지 않고 정상적으로 Response를 전달</li>
<li><code>res.ok</code>는 상태코드가 200–299이면 true, 그 외(400, 404, 500 등)는 false로 인식</li>
</ul>
</li>
<li>전달 받은 응답을 json 형식으로 변환하여 최종 반환</li>
</ul>
</li>
</ul>
<p>이때 API 호출을 위한 코드를 JS가 아닌 TS로 짜는 이유</p>
<ul>
<li>JS는 응답 데이터 구조, 파라미터 타입, 오타를 컴파일러가 잡아주지 못하기 때문에 나중에 데이터 형태가 바뀌면 런타임 에러로만 확인 가능</li>
<li>fetch() 자체는 JS의 내장 기능이니까 호출 방식은 동일하지만 TypeScript에서는 “타입만 추가”해서 더 안전하게 동작하도록 함</li>
</ul>
<h3 id="4-파라미터-전달--전달-받은-데이터-출력">4) 파라미터 전달 &amp; 전달 받은 데이터 출력</h3>
<p><code>page.tsx</code> 수정</p>
<ul>
<li>사용할 API 코드 import 하기</li>
<li>필요한 요소들을 useState()로 정의 (새로고침 시 사라지고 싶다면 초기화 값은 빈값으로 설정)<ul>
<li>서버에서 받아온 데이터</li>
<li>이어서 조회할 값 (커서 또는 페이지네이션 값)</li>
<li>에러 메시지 문자열 (추후 삭제 필요)</li>
<li>API 호출 중인지 아닌지 (로딩 출력 시 사용)</li>
</ul>
</li>
<li>API 호출 함수 정의</li>
<li>화면 접속 시 최초 1회 실행되도록 정의한 API 호출 함수 실행 (<strong>useEffect() 사용</strong>)</li>
<li>불러온 데이터를 <code>map()</code>을 통해 출력</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[카카오 코테 준비 (Python)]]></title>
            <link>https://velog.io/@onion_0/%EC%B9%B4%EC%B9%B4%EC%98%A4-%EC%BD%94%ED%85%8C-%EC%A4%80%EB%B9%84-Python</link>
            <guid>https://velog.io/@onion_0/%EC%B9%B4%EC%B9%B4%EC%98%A4-%EC%BD%94%ED%85%8C-%EC%A4%80%EB%B9%84-Python</guid>
            <pubDate>Wed, 01 Oct 2025 17:39:39 GMT</pubDate>
            <description><![CDATA[<h1 id="카카오-코테-준비">카카오 코테 준비...</h1>
<p>카카오 지원서를 제출했고 프로그래머스에서 코테를 본다고 연락을 받았다...
그나마 가장 자신 있는 언어인 Python으로 준비를 해보려고 한다</p>
<p>한동안 Python은 쳐다보지도 않았기 때문에 일단 기초부터 양치기로 풀어가면서 뇌를 좀 풀어보려고 한다</p>
<p>풀면서도 이걸 틀리네 싶어서 쪽팔리긴 하네...</p>
<h1 id="프로그래머스-코테">프로그래머스 코테</h1>
<h2 id="문제-풀이">문제 풀이</h2>
<p>너무 쉽거나 내가 절대 생각해낼 수 없는 풀이들은 제외하고 오답해두려는 문제들만 정리하려고 한다</p>
<hr>
<h3 id="코딩테스트-입문">코딩테스트 입문</h3>
<p><a href="https://school.programmers.co.kr/learn/challenges/beginner?order=acceptance_desc">https://school.programmers.co.kr/learn/challenges/beginner?order=acceptance_desc</a></p>
<h4 id="1-분수의-덧셈">1. 분수의 덧셈</h4>
<blockquote>
<p>첫 번째 분수의 분자와 분모를 뜻하는 numer1, denom1, 두 번째 분수의 분자와 분모를 뜻하는 numer2, denom2가 매개변수로 주어집니다. 두 분수를 더한 값을 기약 분수로 나타냈을 때 분자와 분모를 순서대로 담은 배열을 return 하도록 solution 함수를 완성해보세요.</p>
</blockquote>
<p>👇❌ 나의 오답</p>
<pre><code>def solution(numer1, denom1, numer2, denom2):

    a = numer1*denom2 + numer2*denom1
    b = denom1*denom2

    c = 1000
    while (c&gt;1):
        if (denom1 % c == 0) &amp; (denom2 % c == 0):
            a = a // c
            b = b // c
        c -= 1

    return [a, b]

import math</code></pre><p>👇⭕ 정답</p>
<pre><code>import math

def solution(numer1, denom1, numer2, denom2):
    b = denom1 * denom2
    a = (numer1 * denom2) + (numer2 * denom1)

    gcd=math.gcd(a,b)
    a = a // gcd
    b = b // gcd

    answer = [a, b]
    return answer</code></pre><h4 id="2-배열-두-배-만들기">2. 배열 두 배 만들기</h4>
<blockquote>
<p>정수 배열 numbers가 매개변수로 주어집니다. numbers의 각 원소에 두배한 원소를 가진 배열을 return하도록 solution 함수를 완성해주세요</p>
</blockquote>
<p>👇⭕ 나의 정답</p>
<pre><code>def solution(numbers):
    for i in range(len(numbers)):
        numbers[i] = numbers[i]*2
    return numbers</code></pre><p>👇⭕⭕⭕ 더 좋은 정답</p>
<pre><code>def solution(numbers):
    return [num*2 for num in numbers]</code></pre><ul>
<li>리스트 컴프리헨션: <code>[표현식 for 항목 in 반복가능객체 if 조건문]</code> 형태</li>
</ul>
<h4 id="3-중앙값-구하기">3. 중앙값 구하기</h4>
<blockquote>
<p>중앙값은 어떤 주어진 값들을 크기의 순서대로 정렬했을 때 가장 중앙에 위치하는 값을 의미합니다. 예를 들어 1, 2, 7, 10, 11의 중앙값은 7입니다. 정수 배열 array가 매개변수로 주어질 때, 중앙값을 return 하도록 solution 함수를 완성해보세요.</p>
</blockquote>
<p>👇⭕ 나의 정답</p>
<pre><code>def solution(array):
    array.sort()
    return array[(len(array)//2)]</code></pre><h4 id="4-최빈값-구하기">4. 최빈값 구하기</h4>
<blockquote>
<p>최빈값은 주어진 값 중에서 가장 자주 나오는 값을 의미합니다. 정수 배열 array가 매개변수로 주어질 때, 최빈값을 return 하도록 solution 함수를 완성해보세요. 최빈값이 여러 개면 -1을 return 합니다.</p>
</blockquote>
<p>👇⭕ 나의 정답</p>
<pre><code>def solution(array):
    if len(array) == 1:
        return array[0]

    else:
        array.sort()

        count_list = []
        count = 1
        for i in range(len(array)-1):
            if array[i] == array[i+1]:
                count += 1
            else:
                count_list.append(count)
                count = 1
        count_list.append(count)

        array_copy = list(set(array))
        array_copy.sort()

        a = array_copy[0]
        max = count_list[0]
        for i in range(1, len(count_list)):
            if max &lt; count_list[i]:
                max = count_list[i]
                a = array_copy[i]
            elif max == count_list[i]:
                a = -1    

        return a</code></pre><h4 id="5-짝수는-싫어요">5. 짝수는 싫어요</h4>
<blockquote>
<p>정수 n이 매개변수로 주어질 때, n 이하의 홀수가 오름차순으로 담긴 배열을 return하도록 solution 함수를 완성해주세요.</p>
</blockquote>
<p>👇⭕ 나의 정답</p>
<pre><code>def solution(n):

    list = []
    for i in range(1, n+1):
        if i % 2 == 1:
            list.append(i)
    list.sort()            
    return list</code></pre><p>👇⭕⭕⭕ 더 좋은 정답</p>
<pre><code>def solution(n):
    return [i for i in range(1, n+1, 2)]</code></pre><hr>
<h3 id="카카오-기출">카카오 기출</h3>
<p><a href="https://school.programmers.co.kr/learn/challenges?order=acceptance_desc&amp;languages=python3&amp;partIds=58464%2C37527%2C31236%2C25448%2C20069%2C17214%2C12286%2C9317%2C22586%2C18498%2C17931%2C300%2C301&amp;page=1">https://school.programmers.co.kr/learn/challenges?order=acceptance_desc&amp;languages=python3&amp;partIds=58464%2C37527%2C31236%2C25448%2C20069%2C17214%2C12286%2C9317%2C22586%2C18498%2C17931%2C300%2C301&amp;page=1</a></p>
<h4 id="1-숫자-문자열과-영단어">1. 숫자 문자열과 영단어</h4>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/81301">https://school.programmers.co.kr/learn/courses/30/lessons/81301</a></p>
<p>👇⭕ 나의 정답</p>
<pre><code>def solution(s):
    num = []
    for i in range(10):
        num.append(str(i))
    print(num)

    eng = [&quot;zero&quot;, &quot;one&quot;, &quot;two&quot;, &quot;three&quot;, &quot;four&quot;, &quot;five&quot;, &quot;six&quot;, &quot;seven&quot;, &quot;eight&quot;, &quot;nine&quot;]

    for i in range(10):
        if eng[i] in s:
            s = s.replace(eng[i], num[i])

    return int(s)</code></pre><h2 id="기억하기">기억하기</h2>
<h3 id="문법">문법</h3>
<h4 id="if문-문법">if문 문법</h4>
<p><code>if ~ elif ~ else</code></p>
<h4 id="if문-활용">if문 활용</h4>
<pre><code>if num1 == num2: 
    return 1 
else: 
    return -1</code></pre><p>위 아래 코드는 동일</p>
<pre><code>return 1 if num1==num2 else -1</code></pre><h4 id="배열">배열</h4>
<p><code>[1, 2, 3] * 2</code>의 결과는 <code>[1, 2, 3, 1, 2, 3]</code></p>
<h4 id="set형">set형</h4>
<p>중복을 제거하는 형은 set()</p>
<h4 id="sort">sort()</h4>
<p>return문이나 print문에 sort()를 쓰면 null값이 반환됨</p>
<h4 id="in">in</h4>
<ul>
<li><code>&quot;seven&quot; in &quot;seveneight&quot;</code> → 참</li>
<li><code>&quot;seveneight&quot; in &quot;seven&quot;</code> → 거짓</li>
</ul>
<h4 id="replace">replace()</h4>
<ul>
<li><code>&quot;ababab&quot;.replace(&quot;a&quot;, &quot;b&quot;)</code> &quot;ababab&quot; 문자열에서 &quot;a&quot;를 &quot;b&quot;로 변경</li>
<li>기본적으로 첫 번째 인자에 해당하는 전체 문자를 변경 (따로 옵션 지정하지 않는다면)</li>
<li>문자열에서 또다른 문자열로만 바꿀 수 있음
<code>&quot;ababab&quot;.replace(&quot;a&quot;, 1)</code> 불가능</li>
<li>변경 후 재할당 필요 <code>s = s.replace(&quot;a&quot;, &quot;b&quot;)</code></li>
</ul>
<h3 id="import">import</h3>
<h4 id="import-math">import math</h4>
<ul>
<li><code>math.gcd(a, b)</code> a, b 사이의 최대 공약수 구하기</li>
<li><code>math.lcm(a, b)</code> a, b 사이의 최소 공배수 구하기</li>
</ul>
<h3 id="최적화">최적화</h3>
<h4 id="함수의-변수">함수의 변수</h4>
<p>재사용 되는 함수가 아니라면 함수 내 변수는 최대한 지양할 것 (사용하지 말 것)</p>
<h4 id="리스트-컴프리헨션">리스트 컴프리헨션</h4>
<p><code>[표현식 for 항목 in 반복가능객체 if 조건문]</code> 형태</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[정보처리기사 2025년도 2회 실기]]></title>
            <link>https://velog.io/@onion_0/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-2025%EB%85%84%EB%8F%84-2%ED%9A%8C-%EC%8B%A4%EA%B8%B0</link>
            <guid>https://velog.io/@onion_0/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC-2025%EB%85%84%EB%8F%84-2%ED%9A%8C-%EC%8B%A4%EA%B8%B0</guid>
            <pubDate>Mon, 15 Sep 2025 08:39:10 GMT</pubDate>
            <description><![CDATA[<p>필기와 마찬가지로 살짝 늦은 실기 후기이지만...
실기에 합격하고 나서 신나는 기분으로 작성하고 싶었고 실기 시험을 응시하러 가는 분들께 생생한 정보 공유를 해드리고 기록을 위해 작성하게 되었다.</p>
<h2 id="2025년도-2회-실기-시험-결과">2025년도 2회 실기 시험 결과</h2>
<p>최종적으로 <strong>1트</strong>만에 합격!</p>
<p>정말 많이 힘들었고 고된 기간이었지만 한번에 합격이라는 결과를 만들어낼 수 있어서 뿌듯했다.</p>
<img src="https://velog.velcdn.com/images/onion_0/post/4667eb03-4fc3-41bb-9a39-bee5b2018b09/image.png">

<br>
실기 시험 점수는 70점이 나왔다!

<img src="https://velog.velcdn.com/images/onion_0/post/6b86ed63-e898-4abb-8b18-06a8b6514f2d/image.png">

<p>시험 보고 나온 당일에 올라온 답안들과 비교했을 때 60점대를 예상하고 있었는데 최종적으로 70점을 맞게 되어서 뿌듯 ㅎ0ㅎ</p>
<h2 id="응시-정보">응시 정보</h2>
<blockquote>
<p>응시 일자: 2025년 8월 19일(토) 시험 응시
응시 당시의 자격 요건: 서울 소재 4년제 대학에서 관련 과 졸업 예정
공부 기간: 약 4개월 (4<del>7월)
실제 빡세게 한 기간: 약 2개월 (6</del>7월)</p>
</blockquote>
<p>졸업 유예의 상태였으나 다행히 졸업 예정이라서 응시가 가능했다.</p>
<h2 id="응시를-위한-준비">응시를 위한 준비</h2>
<p>*나는 필기를 1회 때 응시했기 때문에 곧바로 공부를 했다면 1회 실기를 볼 수 있었지만...
지금 보면 차라리 실기 2회를 보길 잘했던 것 같다... *</p>
<h3 id="1-실기-신청-날짜와-실기-시험-날짜-확인하기">1. 실기 신청 날짜와 실기 시험 날짜 확인하기</h3>
<p>내 공부의 첫 시작은 시험 당일날까지 얼마 남았는지를 계산하는 것부터 시작한다.</p>
<p>실기는 응시 날짜가 다양했던 필기와 달리 <strong>모든 지역에서 단 하나의 날짜</strong>에만 응시한다.
때문에 본인이 원하는 곳에서 응시를 하고자 한다면 날짜 체크를 확실히 해두고 수강신청처럼 대기 타다가 우다다다 신청을 해야한다.
(당연하게도 당일에 큐넷에 접속하면 접속 대기가 뜨기 때문에 잘 맞춰 들어갈 것을 추천한다.)</p>
<p>나는 필기 1회차를 보고 나자마자 2회차의 실기 신청 날짜와 시험 응시 날짜를 확인했다.</p>
<p>3월 초에 일정을 확인했고 2회 실기를 보자고 다짐했기 때문에 확인 날 기준으로 시험까지 약 6개월 정도가 남아있었다.
필기 준비하느라 고생한 나를 위해 1개월 정도는 푹 쉬어주고 4월부터 다시 공부를 시작하자고 마음을 먹었다.</p>
<p>⭐ 참고로 실기는 필기 응시 전에 제출 서류를 다 해뒀다면 따로 제출할 서류가 없다! ⭐</p>
<h3 id="2-공부할-교재-선정하기">2. 공부할 교재 선정하기</h3>
<p>당시에 정보 공유의 목적으로 참여하고 있던 정보처리기사 오픈채팅방이 있었는데 마침 내가 교재를 고르고 있던 시기에 해당 채팅방에서 어떤 교재가 좋을지 투표를 하고 있었다. 
몇 백명이 참여한 투표 결과의 상위 2개가 흥달쌤, 수제비였다.
그래서 이 두 개의 교재 중에서 어떤 걸 해야할지 고민했는데, 보통 비전공자는 흥달쌤, 전공자는 수제비를 많이 이용하는 듯 했다.</p>
<p>큰 고민 없이 수제비를 바로 선택했고, 마침 수제비 사이트에서 수제비 개념서+수제비 파이널 강의가 묶어서 할인이 되고 있어서 바로 결제를 했다.
물론 강의를 구매한다고 해서 교재를 주진 않았고 교재는 따로 구매를 해야 했다.
(그리고 교재가 굉장히... 두꺼웠기 때문에 무조건 분철을 했다... 분철 없이는 못 살아~)</p>
<img src="https://velog.velcdn.com/images/onion_0/post/ddaaaf5a-9997-4532-ab94-9335b7183d6e/image.png">

<p><a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=356476589">https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=356476589</a></p>
<h2 id="시험-공부">시험 공부</h2>
<p>실기는 필기와 달리 엄청난 암기와 이해를 요구한다. 
그렇기 때문에 나는 내가 제일 잘하는 방식으로 암기와 이해를 파고들었다.</p>
<h3 id="1-강의-수강">1. 강의 수강</h3>
<blockquote>
<p>4~5월까지 개념 강의 수강</p>
</blockquote>
<p>위에 내가 실제 공부 기간은 7~8월이라고 한 이유가, 
4월부터 공부를 시작하긴 했으나 4월부터 5월까지는 개념 강의만 주구장창 듣기만 했기 때문이다.</p>
<p>수제비 개념 강의를 수강했는데 매일같이 들은 것도 아니었고 뜨문뜨문 들었기 때문에 거의 3개월 정도는 날렸다고 봐도 무방할 듯 하다.
그럼에도 기본 개념과 어느 정도의 내용이 있는지, 그리고 전공자에게 알맞은 적절한 설명을 통해서 완전 기초 이해 정도는 해두었다. </p>
<p>(수제비 개념 강의를 수강하면서 느꼈지만 사담이 싫다고 하는 사람들이 왜 있는지 이해하게 됐다. 그러나 나는 오히려 그런 것들이 암기 포인트로 적용하기 때문에 오히려 유용했다.)</p>
<h3 id="2-무지성-암기-시작">2. 무지성 암기 시작</h3>
<blockquote>
<p>6월 초 (2~3주)</p>
</blockquote>
<p>개념을 한번 훑었으니 제일 지옥같은 암기의 시작이다...</p>
<p><em>나는 오히려 어느 정도 이해하고 암기가 된 상태에서 더 자세하고 정확하게 외우는 건 오히려 쉬운데, 새로 암기하는 그 과정이 제일 고통스럽다... ㅜㅜ</em></p>
<p>우선 2~3주 정도는 개념서를 바탕으로 차근차근 암기와 정리를 병행했다.
정리가 없으면 공부가 어려운 성격 탓에;; 개념서의 목차를 기준으로 굿노트에 수기로 정리를 했으며 이렇게 정리해둔 건 추후에 키워드나 한 단원에 어떤 내용이 있는지 보기 확실히 편해서 잘 써먹었다.</p>
<img src="https://velog.velcdn.com/images/onion_0/post/c8c97b46-113e-4ee5-9462-b210db011df5/image.png">
▲ 굿노트로 직접 정리한 내용의 일부이다.

<p>개념서를 바탕으로 암기를 했다면 수제비 개념서 교재에서 각 단원마다 붙어있는 기출을 풀면서 암기를 어느 정도 했는지 파악했다.
이때에는 다 맞추는 걸 목표로 하지 않았고 시험에 대략 이렇게 나오는구나, 내가 이 개념은 아직 확실하지 않구나, 하는 것들을 확인했다.</p>
<h3 id="3-기출-풀어보기">3. 기출 풀어보기</h3>
<blockquote>
<p>6월 중순 (1주)</p>
</blockquote>
<p>개정 후 해당하는 기출의 범위가 2020년도~(현재)이다.
개념서를 한번 다 훑었으니 기출을 전부 풀어보았고 소요되는 시간, 틀린 문제 유형, 헷갈리는 개념들을 다시 한번 확인했다.</p>
<p>문제가 20문제밖에 되지 않기 때문에 시간이 부족하지는 않았으나, 외웠다고 생각했음에도 생각보다 많은 부분에서 개념이 흔들리고 있었다. </p>
<h3 id="4-소단원-기준-개념-암기">4. 소단원 기준 개념 암기</h3>
<blockquote>
<p>6월 말 (2주)</p>
</blockquote>
<p>이때부터는 시험이 진짜 더이상 미룰 수 없게도 1달 정도 남았던 기간이어서 거의 매일 공부하는 걸 목표로 했다.</p>
<p>우선 수제비 교재 기준, 하루에 아래의 양을 진행했다.</p>
<ul>
<li>소단원 1단원</li>
<li>소단원에 붙어있는 기출 다시 풀기 + 오답</li>
<li>기출 1개년치 풀기 + 오답</li>
</ul>
<p>사실 진짜 2주 정도 걸렸는지 정확히 기억은 안 나지만 아마 양이 적은 부분은 두 단원을 합쳐서 하거나 했어서 대략 2주 정도 걸렸을 거다.</p>
<p>이때는 키워드만 외우는 게 아니고 키워드와 그 정의까지 확실하게 외우는 걸 목표로 진짜 빡세게 암기했다. 그렇지 않으면 만약 시험에서 힌트가 나와도 못 알아볼 가능성이 높기 때문에 어쩔 수 없는 선택이었다...</p>
<h3 id="5-대단원-기준-개념-암기">5. 대단원 기준 개념 암기</h3>
<blockquote>
<p>7월 초 (2주)</p>
</blockquote>
<p>이때에는 기출에도 어느 정도 익숙해졌고 개념도 어느 수준 이상으로는 암기가 되었기 때문에 공부량을 확 늘렸다.</p>
<p>수제비 교재 기준, 하루에 아래의 양을 진행했다.</p>
<ul>
<li>대단원 1단원</li>
<li>대단원 내에 붙어있는 기출 중 틀린 문제만 풀기 + 오답</li>
<li>기출 2개년치 풀기 + 오답</li>
<li>수제비 파이널 모의고사 5개씩 풀기 + 오답</li>
</ul>
<p>사실 공부량이 정말 많았어서 사람도 거의 안 만나고 일어나면 자기 전까지 공부만 했던 것 같다.
(조금만 공부기간이 더 길었다면 정말 히키코모리가 됐을지도 몰라...)</p>
<p>그래도 필요하다고 느꼈기 때문에 하루에 저 양을 진행했는데, 확실히 암기를 꽉 잡고 가니까 어느 정도 스스로도 틀이 잡히기 시작했다.
이때에는 내가 이전에 정리해뒀던 굿노트 키워드 요약본을 같이 보면서 큰 틀까지 같이 잡아나갔다.</p>
<p>대단원 암기와 오답, 그리고 기출 2개년치 풀고 오답을 진행하는 것까지는 큰 무리가 없었다.
문제는 수제비 파이널... 문제가 정말정말 어렵거나 생각지도 못한 부분에서 더 깊은 이해와 암기를 필요로 하는 부분들이 많았다.
그래서 차라리 통과 점수를 맞는 것보다 내가 약했던 개념들을 잡아간다고 생각하고 풀고 오답을 확실히 했다.</p>
<p>이 시기에는 얼렁뚱땅 넘어갔거나 헷갈렸던 개념들을 확실히 암기하기 위해 수제비 강의와 정오표, 수제비 카페, 정보처리기사 오픈채팅방을 열심히 이용했다.
많은 도움과 조언, 그리고 헷갈려하던 개념들에 대해 설명을 자세히 들을 수 있었다..</p>
<blockquote>
<p>수제비 카페: <a href="https://cafe.naver.com/soojebi">https://cafe.naver.com/soojebi</a></p>
</blockquote>
<blockquote>
<p>정보처리기사 오픈채팅방: <a href="https://open.kakao.com/o/gQkcTpNh">https://open.kakao.com/o/gQkcTpNh</a></p>
</blockquote>
<h3 id="6-끝없는-복습">6. 끝없는 복습...</h3>
<blockquote>
<p>7월 중 (~시험 당일)</p>
</blockquote>
<p>이제는 끝없는 복습의 시작이다.</p>
<p>시험 3일 전부터는 </p>
<ul>
<li>기출 오답</li>
<li>잘 안 외워지는 개념 정리 (notion에 전체 개념과 헷갈리는 개념들 따로 정리) 및 무한 암기</li>
<li>수제비 파이널 모의고사 오답</li>
<li>25년도 1회차 실기 기출 풀어보기</li>
</ul>
<p>이렇게 진행했다. </p>
<p>그리고 정보처리기사 오픈채팅방에서도 주요한 개념들 위주의 퀴즈를 내주셔서 다같이 합심해서 맞추고 외우는 과정을 반복했다.
<em>실제로 2회차 실기 때 여기에서 내주셨던 퀴즈들 중 하나가 나왔다...!</em></p>
<p>그리고 notion에 정리하던 게 막판에 보기에 엄청 편했다.</p>
<img src="https://velog.velcdn.com/images/onion_0/post/bf4c1a22-ebad-437b-9bfb-f021219039a6/image.png">

<p>역시 정리가 좋아...</p>
<h2 id="시험-전날">시험 전날</h2>
<h3 id="마지막-공부">마지막 공부</h3>
<p>이제 시험 전날에는 이 이상 새로운 걸 할 수 없었기 때문에 복습만 했다.</p>
<ul>
<li>기출 오답</li>
<li>notion에 정리해둔 내용 암기</li>
<li>개념서 2회독</li>
<li>내일 시험장 가서 마지막까지 봐야 할 개념들 (안 외워지는 거 + 왕 중요한 거) 정리</li>
<li>준비물 정리</li>
</ul>
<h3 id="준비물">준비물</h3>
<p>준비물은 본인 수험번호가 적힌 수험표, 신분을 증빙할 수 있는 서류(민증 등), 검은색 볼펜, 샤프, 지우개, 화이트, 계산기, 물, 초콜릿이 있다.</p>
<ul>
<li>사실 수험표는 꼭 없어도 좋지만 그래도 수험번호가 적혀있는 건 한 부 챙겨가는 걸 추천한다. 물론 없더라도 감독관이 수험번호를 보여주기 때문에 귀찮다면 뭐...</li>
<li>신분 증빙 서류와 검은색 볼펜은 필수이다. 신분 증빙 서류가 어떤 게 가능한지는 수험표에도 명시되어 있기 때문에 사전에 확인하고 챙겨가길 바란다. 귀찮다면 민증이 최고. </li>
<li>검은색 볼펜으로 답을 기입해야만 올바른 방식으로 답을 기입했다고 인식하기 때문에, 만약 잘 풀어서 답도 맞게 도출했는데 샤프로 기입하거나 검은색 볼펜이 아닌 것으로 기입한다면 오답 처리가 된다.</li>
<li>단, 답을 기입하는 게 아니고 임의의 풀이를 한다거나 개인적으로 문제 풀이를 위해 샤프를 사용하는 것은 문제가 되지 않는다. 답만 검은색 볼펜으로 확실히 쓴다면 문제 없다. 만약 샤프와 검은색 볼펜이 헷갈릴 것 같아 문제가 될 것 같다면 제출 직전에 연필로 작성한 건 지워서 제출해도 된다. (그러나 지금까지 풀이과정 안 지우고 내서 문제가 된 적은 없었다고 한다.)</li>
<li>계산기는 큐넷 사이트에서 허용하는 기종이 맞는지 확인하고 가져가길 바란다. 기종이 맞지 않다면 사용이 불가하기 때문이다. 단순 계산기라면 괜찮기 때문에 다이소에서 구매해서 가져가도 문제가 없다. 계산기가 필요없다고 하는 사람들도 있지만 잘 풀어놓고 계산에서 실수하는 것만큼 슬픈 일이 없기에 나는 챙겨갔다.</li>
<li>계산기, 물, 초콜릿은 아예 반입도 안 된다고 하시는 감독관분들도 계시지만 우선은 챙겨가는 게 좋다. 시험장에서 꺼내놓았는데 큰 제재가 없다면 섭취가 가능하니 챙겨가자.</li>
<li>물, 초콜릿은 까먹기 편한 것으로 가져가자. 문제를 풀다보면 무조건 당이 딸린다. 나는 페레로로쉐를 구매해서 까면 바로 입에 넣을 수 있게 준비해뒀다.</li>
</ul>
<h2 id="시험-당일">시험 당일</h2>
<p>잠은 2시간 정도 잔 듯 하고 자기 직전까지 개념만 주구장창 확인했다.
일어나서 어제 챙겨둔 짐을 들고 바로 수험장으로 출발했다.</p>
<p>2시간 정도 일찍 출발해서 시험 1시간 40분 전에, 그러니까 8시까지 수험장에 도착했다.
도착해서 자리 배치 확인하고 자리 앉아서 세팅부터 했다.
물, 계산기, 신분증, 필기도구, 책, 아이패드를 꺼내고 그때부터 엄청 집중해서 마지막 암기를 시작했다.
확실히 애가 타고 긴장되니까 암기가 잘 되는지 끝까지 암기가 안 됐던 개념들이 갑자기 암기가 되기 시작했다. ㅋㅋㅋㅋㅋ </p>
<h3 id="떨리는-시험">떨리는 시험...</h3>
<p>시험 안내 방송이 시작됐을 때부터 이번에는 얼마나 어려울까에 대한 걱정이 엄청 컸다.
그래서 시험지를 배부받고 오탈자나 시험지 문제가 없는지 확인할 때 슬쩍 확인한 문제가 1회차에 비해 중구난방은 아닌 듯 해서 걱정을 살짝 덜었다. </p>
<p>나는 공부는 스스로에게도 이 이상은 할 수 없었을 거다 했을 정도로 열심히 임했다고 자신있게 말할 수 있다.
그렇기 때문에 스스로를 믿고 시험 문제를 차근차근 풀어나갔다. 헷갈리는 건 과감하게 재껴두고 할 수 있는 것들을 확실하게 풀어나갔다.</p>
<p>20문제이기 때문에 전체 다 푸는 데에는 오래 걸리지 않았으나 검수를 두세 번 했다.
내가 잘못 본 건 없는지, 실수한 계산은 없는지 계산기를 통해 확인했으며 당이 딸릴 때 준비해둔 초콜릿을 계속 먹으면서 집중해서 풀었다.</p>
<p>끝에 끝까지 풀이를 모르겠는 문제들은 과감하게 찍고 나왔다.</p>
<h2 id="후기">후기</h2>
<h3 id="1회차보다는-쉬웠다">1회차보다는 쉬웠다</h3>
<p>내가 이번 시험에 유독 긴장했던 이유가 몇 가지 있다.</p>
<ol>
<li>25년도 1회차가 정말 어려웠는데, 보통 기사 시험은 그 해의 1회차가 가장 쉽다는 말이 있다. 근데 1회차가 이전 년도에 비해 압도적으로 어려웠기 때문에... 그럼 2회차는 얼마나 어려울까 하는 걱정이 컸다.</li>
<li>내년에 약간의 개정이 생긴다는 얘기가 있었다. 그렇다면 올해 안에 무조건 따야하고... 이번에 떨어지면 3회차를 다시 준비해야 한다는 게 엄청 부담이었다.</li>
<li>처음 보는 기사 시험이었기에 어떻게 준비해야 하는지에 대한 고민이 컸다. 어떤 방식으로 준비하는 게 정답일지 모르겠어서 나 스스로에게 부끄럽지 않게 공부하고자 더 몰아붙히면서 공부한 것 같다.</li>
</ol>
<p>그럼에도 정말 다행히도 1회차보다 오히려 2회차가 쉬웠었다.
개념이 꼬아서 나오거나 그러지도 않았기에 그런 문제들은 확실히 보고 맞추고 가자 싶었다.
다만 여전히 C와 JAVA가 어렵게 나왔으나 1회차에 비하면 크게 어려운 수준도 아니었고 비슷하거나 살짝 쉬웠던 정도였던 듯 하다.</p>
<h3 id="시험이-끝나자마자">시험이 끝나자마자...</h3>
<p>다같이 정보처리기사 오픈채팅방에서 서로가 푼 문제와 답을 공유했고
그날 오후에는 각 강의 사이트에서 올려주는 사전답안을 보고 어느 정도의 점수가 나왔을지 확인했다.
나는 이때 62~64점 정도가 나와서 합격이겠거니 했지만... 그래도 확실히 합격을 받아야 안심할 수 있을 것 같았다.</p>
<h3 id="한-방에-합격">한 방에 합격</h3>
<p>그래도 결국 원트 합격이라는 스스로의 쾌거를 이루고 정말 뿌듯했다.
그치만 다시 한번 이렇게 공부하라고 하면 다시는 못할 정도로... ㅜㅜ 
길고 긴 과정이었지만 좋은 결과로 마무리하게 되어 스스로에게도 정말 뿌듯했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ethers v6를 사용해보기]]></title>
            <link>https://velog.io/@onion_0/Ethers-v6%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@onion_0/Ethers-v6%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Fri, 12 Sep 2025 08:38:44 GMT</pubDate>
            <description><![CDATA[<h1 id="시작하기에-앞서">시작하기에 앞서...</h1>
<p>이번 포스팅 내용은 Ethers 학습을 목적으로 간단히 진행한 프로젝트입니다.
2025 K-PaaS 공모전을 진행하면서 DID와 관련한 내용을 다루게 되었고 프론트 개발 과정 중에서 Ethers의 이해와 활용에 대한 이해도를 높이기 위해 진행하게 되었습니다.</p>
<p>DID의 직접적인 활용보다는 Ethers 사용과 학습에 중점을 두고 있습니다!</p>
<h1 id="ethers가-뭔데">Ethers가 뭔데?</h1>
<h2 id="이더리움과-상호작용하는-라이브러리">이더리움과 상호작용하는 라이브러리!</h2>
<blockquote>
<p>Ethers 공식 문서
<a href="https://docs.ethers.org/v6/">https://docs.ethers.org/v6/</a></p>
</blockquote>
<p>ethers.js 라이브러리는 Ethereum 블록체인 및 EVM(ecosystem)과 상호작용할 수 있게 해주는 완전하고 컴팩트한 라이브러리입니다. JavaScript/TypeScript에서 이더리움과 안전하게 상호작용할 수 있게 도와주는 기능을 합니다. 
다시 말해, ethers.js 라이브러리는 Ethereum 블록체인 및 EVM(ecosystem)과 상호작용할 수 있게 해주는 라이브러리입니다. </p>
<ul>
<li>지갑 연결</li>
<li>트랜잭션 전송</li>
<li>스마트 계약 실행</li>
<li>블록체인 데이터 조회 등</li>
</ul>
<p>때문에 DApp, 지갑, 기타 블록체인 읽기/쓰기 작업용으로 자주 쓰이고 있습니다.</p>
<h2 id="그럼-이더리움은-뭐야">그럼 이더리움은 뭐야?</h2>
<blockquote>
<p><a href="https://ethereum.org/ko/what-is-ethereum/">https://ethereum.org/ko/what-is-ethereum/</a></p>
</blockquote>
<p>이더리움은 탈중앙화된 스마트 계약 플랫폼이자 블록체인 네트워크입니다. 
간단하게는 개발 시 블록체인을 활용할 수 있도록 환경 제공을 비롯한 다양한 기능을 포함하는 &#39;플랫폼&#39;입니다.</p>
<h2 id="ethers는-어떻게-동작해">Ethers는 어떻게 동작해?</h2>
<p>Ethers v6의 공식 문서의 내용에 따르면 아래의 순서에 따라 동작합니다.</p>
<ol>
<li>Provider 연결</li>
<li>Signer 획득</li>
<li>Contract 연결(Optional)</li>
<li>읽기/쓰기 작업 실행</li>
</ol>
<p>이때 Ethers의 동작 과정에 사용되는 용어에 대한 설명은 다음과 같습니다.</p>
<ul>
<li>Provider: 블록체인과 연결, 읽기 작업 수행 (getBalance, getBlockNumber, etc.)</li>
<li>Signer: 사용자의 개인 키를 보유, 트랜잭션에 서명함</li>
<li>Contract: 스마트 계약 객체, ABI + 주소로 생성됨</li>
<li>Utils: formatEther, parseUnits, keccak256, toUtf8Bytes 등 유틸 함수</li>
<li>Wallet: 로컬 키페어를 가진 지갑 객체 (ex. new Wallet(privateKey))</li>
</ul>
<h1 id="그럼-ethers를-사용해-어떤-프로젝트를-할-수-있어">그럼 Ethers를 사용해 어떤 프로젝트를 할 수 있어?</h1>
<h2 id="구상한-프로젝트-주제">구상한 프로젝트 주제</h2>
<p>이번 주제에서 메인으로 고려해야 할 것은 아래의 두 가지였습니다.</p>
<ul>
<li>Ethers.js의 주요 기능: 스마트컨트랙트 연결, 지갑 연결, 송금, 블록/트랜잭션 조회 등</li>
<li>프론트엔드와의 상호작용: 버튼, 폼, 상태관리, 뷰 업데이트</li>
</ul>
<p>따라서 위 내용을 같이 고려했을 때 &#39;지갑 연결 + 잔액 조회 + 송금 DApp&#39;을 주제로 결정했습니다.</p>
<h2 id="개발-환경">개발 환경</h2>
<p>개발환경은 다음과 같습니다. </p>
<ul>
<li>Node: ≥ 18 (LTS 권장)</li>
<li>패키지 매니저: npm 또는 pnpm</li>
<li>프레임워크: Next.js 14 (App Router)</li>
<li>언어: TypeScript</li>
<li>UI: 기본 스타일</li>
<li>Web3: ethers@^6</li>
<li>지갑: MetaMask</li>
</ul>
<h2 id="메타마스크-설치">메타마스크 설치</h2>
<p>이번 프로젝트의 지갑으로는 메타마스크를 사용하기 때문에, 프로젝트 개발 전에 미리 설치를 해야 합니다.</p>
<h3 id="메타마스크가-뭐야">메타마스크가 뭐야?</h3>
<p>메타마스크는 사용자가 블록체인 애플리케이션뿐만 아니라 디지털 신원과 디지털 자산에 안전하게 접근할 수 있도록 지원하는 자기 수탁형 암호화폐 지갑 앱입니다. 
사용자는 MetaMask를 통해 Web3 상의 분산형 애플리케이션(디앱)과 상호작용하고, 암호화폐 포트폴리오를 관리하며, NFT를 민팅하는 등 다양한 기능을 이용할 수 있습니다. 또한, 안전한 로그인, 키 저장소, 토큰 지갑 등의 기능을 제공합니다.</p>
<blockquote>
<p>출처: <a href="https://metamask.io/ko/download">https://metamask.io/ko/download</a></p>
</blockquote>
<p>다시 말해, 이더리움(Ethereum) 및 <strong>이더리움 기반 블록체인 네트워크(예: Polygon, BNB Smart Chain 등)</strong>에 연결된 디지털 암호화폐 지갑을 말합니다.</p>
<h3 id="메타마스크-설치는-어떻게-해">메타마스크 설치는 어떻게 해?</h3>
<p>메타마스크는 웹 브라우저에서 설치해서 사용하거나 모바일 앱으로 사용 가능합니다. 
메타마스크를 사용할 수 있는 웹 브라우저는 Chrome, Firefox, Brave, Microsoft Edge이며, 저는 크롬에서 진행했습니다.</p>
<p>크롬에서 메타마스크 설치를 위해서는 크롬 확장프로그램으로 설치하면 되며, 아래 링크를 통해 설치가 가능합니다.
<a href="https://chromewebstore.google.com/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn?utm_source=chatgpt.com">https://chromewebstore.google.com/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn?utm_source=chatgpt.com</a>
설치 후 회원가입을 하면 되는데, 과정이 간단하므로 메타마스크에서 제시하는대로만 회원가입 하면 됩니다.</p>
<p>‼️추후 개발 서버를 열어서 테스트 할 때, 메타마스크가 설치된 크롬에서 열어야 테스트가 정상적으로 동작합니다.‼️</p>
<h2 id="프로젝트-설치">프로젝트 설치</h2>
<p>프로젝트 기본 환경 설치를 우선 진행했습니다.
저는 <code>/K-PaaS/</code> 폴더 아래에 이번 프로젝트의 폴더를 생성하고자 했습니다.</p>
<p>이를 위해 우선 <code>/K-Paas/</code> 폴더를 기준으로 cmd를 열고, 아래의 명령어를 순차적으로 진행했습니다.</p>
<p>1) &#39;dapp-wallet&#39;이라는 이름의 프로젝트 생성</p>
<pre><code># 프로젝트 생성
npx create-next-app@latest dapp-wallet --ts --eslint</code></pre><p>2) 생성한 프로젝트 폴더로 이동</p>
<pre><code>cd dapp-wallet</code></pre><p>3) ethers 라이브러리 다운로드</p>
<pre><code># web3 라이브러리
npm i ethers</code></pre><p>4) 환경 옵션 설정</p>
<img src="https://velog.velcdn.com/images/onion_0/post/5333f3b9-2b03-4bd8-8d91-91766ad8f005/image.png">

<ul>
<li>Tailwind CSS 사용 X</li>
<li><code>/src/</code> 디렉토리 사용</li>
<li>App Router 사용</li>
<li>Turbopack 사용 X → 기존의 webpack 사용</li>
<li>alias 사용 X</li>
</ul>
<h2 id="파일-구조">파일 구조</h2>
<p>이번 프로젝트의 디렉토리 구조는 다음과 같습니다.</p>
<pre><code>dapp-wallet/
├─ src/
│  ├─ app/
│  │  ├─ layout.tsx
│  │  └─ page.tsx
│  ├─ components/
│  │  ├─ ConnectButton.tsx
│  │  ├─ AccountPanel.tsx
│  │  └─ SendEthForm.tsx
│  ├─ lib/
│  │  └─ ethereum.ts
│  └─ styles/
│     └─ globals.css
├─ .env.local            # (옵션) 별도 RPC 사용 시
├─ package.json
└─ tsconfig.json</code></pre><p>※ 기본 동작은 MetaMask의 BrowserProvider 하나로 충분합니다. (읽기/쓰기 모두 가능)
퍼블릭 RPC를 따로 쓰고 싶다면 .env.local에 키를 넣고 JsonRpcProvider를 보조로 붙여도 됩니다.</p>
<h2 id="코드-작성">코드 작성</h2>
<h3 id="tsconfigjson-수정">tsconfig.json 수정</h3>
<p>앞서 디렉토리를 설치할 때 <code>/src/</code> 디렉토리를 사용한다고 설정을 했기 때문에, <code>tsconfig.json</code>에 아래의 코드를 추가해야 합니다.</p>
<pre><code>{
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;.&quot;,
    &quot;paths&quot;: {
      &quot;@/*&quot;: [&quot;./src/*&quot;]
    }
  }
}</code></pre><h3 id=""></h3>
<h1 id="깃허브-코드">깃허브 코드</h1>
<p><a href="https://github.com/onion-0/Wallet-DApp.git">https://github.com/onion-0/Wallet-DApp.git</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[강의] 한 입 크기로 잘라먹는 Next.js #10]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-10</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-10</guid>
            <pubDate>Mon, 01 Sep 2025 09:59:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="최적화와-배포">최적화와 배포</h1>
<h2 id="1-이미지-최적화">1. 이미지 최적화</h2>
<h3 id="1-이미지-최적화-1">1) 이미지 최적화...?</h3>
<p>웹페이지에서 평균적으로 58%의 용량을 차지하는 것은 이미지
= 용량의 관점에서만 보자면 웹페이지의 절반 이상이 거의 다 이미지로만 구성</p>
<p>Next.js에서는 대부분의 최적화 기법들이 자체적으로 다 제공이 되고 있음</p>
<ul>
<li><strong>이미지</strong>라는 컴포넌트를 활용하면 이미지 최적화 기법들이 모두 자동으로 적용</li>
</ul>
<img src="https://velog.velcdn.com/images/onion_0/post/21f42de3-f5c3-4d2a-87e0-6ef749ad46ce/image.png">

<p>이미지를 불러올 때의 문제점</p>
<ul>
<li>JPEG 형식으로 불러오기
: 나쁜 건 아지미나  되도록이면 WebP나 AVIF 등 JPEG보다 훨씬 더 경량화된 이미지 포맷으로 불러오는 것이 좋다</li>
<li>현재 화면에 렌더링 될 필요가 없는 이미지까지 한번에 불러오고 있음</li>
<li>불러오고 있는 이미지들의 사이즈가 실제로 화면에 나타날 사이즈보다 훨씬 더 큼</li>
</ul>
<h3 id="2-실습을-위한-환경-준비">2) 실습을 위한 환경 준비</h3>
<ol>
<li><p>vscode로 열기
vscode에서 <code>.../onebite-next/</code> 폴더로 열기</p>
</li>
<li><p>터미널에서 section08 폴더 생성
vscode에서 <code>/onebite-next/section09/</code> 폴더 생성</p>
</li>
<li><p>파일 옮기기
<code>/onebite-next/section08</code> 폴더에 있는 코드 중 아래 사진에 해당하는 코드만 선택하여 <code>/onebite-next/section09</code> 폴더로 옮기기</p>
</li>
</ol>
<img src="https://velog.velcdn.com/images/onion_0/post/bffd2abd-8727-45df-a439-40e1ee08ba18/image.png">

<ol start="4">
<li><p>필요한 모듈 다운로드
생성된 <code>/section09/</code> 폴더로 vscode에서 다시 열기
→ vscode 터미널을 열어서 <code>npm i</code> 명령어 실행</p>
</li>
<li><p>개발 모드로 넥스트 앱 실행
생성된 <code>/section09/</code> 폴더로 vscode에서 다시 열기
→ vscode의 터미널 열기
→ <code>npm run dev</code> 명령어 실행
→ <a href="http://localhost:3000/">http://localhost:3000/</a> 로 이동하면 확인 가능
→ (종료 시에는) <code>Ctrl+D</code>나 <code>Ctrl+C</code> 입력</p>
</li>
<li><p>백엔드 서버 열기
vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code> 명령어 실행</p>
</li>
</ol>
<h3 id="3-실습">3) 실습</h3>
<p>이미지 최적화 진행</p>
<ul>
<li><p><code>/src/components/book-item.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter01/src/components/book-item.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter01/src/components/book-item.tsx</a></p>
</li>
<li><p>원하는 도메인으로부터 불러오는 이미지는 안전하다는 설정 추가: <code>/next.config.ts</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter01/next.config.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter01/next.config.ts</a>
이때 추가하는 값은 <code>images</code> 안에 삽입되며, <code>book-item.tsx</code> 코드 수정 후 실행했을 때 콘솔에 나타나는 에러 메시지의 도메인을 삽입하면 됨</p>
</li>
<li><p><code>/src/app/book/[id]/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter01/src/app/book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter01/src/app/book/%5Bid%5D/page.tsx</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="2-검색-엔진-최적화-seo">2. 검색 엔진 최적화 (SEO)</h2>
<h3 id="1-검색-엔진-최적화">1) 검색 엔진 최적화...?</h3>
<p>검색엔진을 가지고 있는 포털 사이트들에서 우리가 제작한 서비스에 어떠한 페이지들이 있고 이 페이지에는 어떠한 정보들이 있는지 포털 사이트에서 더 잘 수집해 갈 수 있도록 설정하여, 결과적으로는 우리의 서비스가 검색 결과에 더 잘 노출이 되도록 설정하는 기술</p>
<p>= 우리의 서비스가 검색 결과에 더 잘 노출이 되도록 설정하는 기술</p>
<h3 id="2-검색-엔진-최적화-방법">2) 검색 엔진 최적화 방법</h3>
<ul>
<li>Sitemap 설정</li>
<li>RSS 발행</li>
<li>시멘틱 태그 설정</li>
<li>메타 데이터 설정</li>
</ul>
<p>등등... 많이 있음</p>
<p>현재 강의에서는 <strong>페이지별 메타 테이더 설정</strong>하는 방법에 대해 알아보기</p>
<h3 id="3-실습-진행을-위한-환경-세팅">3) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section09</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="4-실습">4) 실습</h3>
<ul>
<li><p>수업 자료로 올려주신 이미지 파일을 <code>/public/</code> 폴더 내로 이동</p>
</li>
<li><p>인덱스 페이지의 메타 데이터 설정하기: <code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter02/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter02/src/app/(with-searchbar)/page.tsx</a></p>
<pre><code>export const metadata: Metadata = {
  title: &quot;한입 북스&quot;,
  description: &quot;한입 북스에 등록된 도서를 만나보세요&quot;,
  openGraph: {
    title: &quot;한입 북스&quot;,
    description: &quot;한입 북스에 등록된 도서를 만나보세요&quot;,
    images: [&quot;/thumbnail.png&quot;],
  },
};</code></pre></li>
<li><p>메타 데이터 설정: <code>/src/app/(with-searchbar)/search/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter02/src/app/(with-searchbar)/search/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter02/src/app/(with-searchbar)/search/page.tsx</a>
▼ 동적 매개변수를 사용하여 메타 데이터 내보내기</p>
<pre><code>export async function generateMetadata({ searchParams }: Props): Promise&lt;Metadata&gt; {
  const { q } = await searchParams;
  return {
    title: `${q} : 한입북스 검색`,
    description: `${q} 검색 결과입니다`,
    openGraph: {
      title: `${q} : 한입북스 검색`,
      description: `${q} 검색 결과입니다`,
      images: [&quot;/thumbnail.png&quot;],
    },
  };
}</code></pre></li>
<li><p>메타 데이터 설정: <code>/src/app/book/[id]/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter02/src/app/book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter02/src/app/book/%5Bid%5D/page.tsx</a></p>
</li>
<li><p>파비콘 설정하기</p>
<ul>
<li>기존에 있던 <code>/src/app/favicon.ico</code> 파일 삭제하기</li>
<li>올려주신 자료에 있던 <code>favicon.ico</code> 파일(<code>/public/</code> 내에 있던 파일)을 <code>/src/app/</code> 하위로 이동</li>
</ul>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="3-배포하기">3. 배포하기</h2>
<h3 id="1-배포를-위한-기본-설정">1) 배포를 위한 기본 설정</h3>
<ul>
<li><p>Vercel 사이트(<a href="https://vercel.com/)%EC%97%90%EC%84%9C">https://vercel.com/)에서</a> 회원가입 하기
→ 나는 github으로 연동</p>
</li>
<li><p>vscode의 터미널에서 Vercel에 명령어 한번만으로 Next.js 앱을 빌드할 수 있는 도구 설치
<code>npm install -g vercel</code> 실행</p>
</li>
<li><p>vscode의 터미널에서 Vercel에 로그인
→ github에 갖다대서 enter 누르고 진행</p>
</li>
<li><p>배포를 위해, vscode의 터미널에서 <code>vercel</code> 명령어 입력
→ 현재 프로젝트 폴더를 배포할 거냐는 질문에 대해 <code>y</code> 입력
→ 어떤 scope를 선택해서 배포할 거냐는 질문에 뜨는 프로젝트를 선택하고 엔터 입력
→ 기존에 존재하는 프로젝트에 연결할 거냐는 질문에 <code>n</code> 입력
→ 새로 생성할 프로젝트 이름은 뭐냐는 질문에 <code>onebite-books-app</code> 으로 입력
→ 코드가 위치할 디렉토리 위치는 어디냐는 질문에 자동으로 설정된 기본값 그대로 하기 위해 바로 엔터 누르기
→ 자동으로 설정된 옵션들을 바꾸고 싶냐는 질문에 <code>n</code> 입력
→ 추가 프로젝트 옵션을 바꾸고 싶냐는 질문에 <code>n</code> 입력
→ 배포 진행중
→ 배포 진행 완료 후 뜨는 링크를 통해 들어가면 확인 가능</p>
</li>
<li><p>이때, 백엔드 서버는 안 열려 있기 때문에 DB에서 데이터를 불러오지 못하고 있음
→ <code>/onebite-books-server/</code>로 새 창을 하나 열어서 vscode 터미널을 새로 열기
→ 터미널에서 <code>vercel</code> 입력
→ 현재 프로젝트 폴더를 배포할 거냐는 질문에 대해 <code>y</code> 입력
→ 어떤 scope를 선택해서 배포할 거냐는 질문에 뜨는 프로젝트를 선택하고 엔터 입력
→ 기존에 존재하는 프로젝트에 연결할 거냐는 질문에 <code>n</code> 입력
→ 새로 생성할 프로젝트 이름은 뭐냐는 질문에 기본값으로 설정된 걸로 진행을 위해 엔터 입력
→ 코드가 위치할 디렉토리 위치는 어디냐는 질문에 자동으로 설정된 기본값 그대로 하기 위해 바로 엔터 누르기
→ 바로 배포 작업 진행
→ 배포 완료</p>
</li>
<li><p>백엔드 서버 도메인 확인 후 코드 수정 
→ vercel에서 배포한 백엔드 서버(onebite-books-server)에 들어가서 Production Deployment에서 &#39;Domains&#39; 값을 복사
→ vercel에서 배포한 onebite-books-app에 들어가서 settings 탭에서 Environment Variables 탭에서 새로운 환경변수 추가 
(Key는 아래 사진과 동일하게 설정, Value는 복사한 값 붙여넣고 https:// 추가) <img src="https://velog.velcdn.com/images/onion_0/post/d1ad5d04-bdf1-4dde-bf8a-b35654c593ce/image.png"></p>
</li>
<li><p>수정사항을 반영하기 위한 재배포 진행
→ vscode에서 <code>section2/</code>를 기준으로 폴더 열고 터미널 열기
→ 즉시 프로덕션 모드로 배포하기 위해 <code>vercel --prod</code> 명령어 실행
→ 배포 진행 후 완료</p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="4-배포-후-최적화">4. 배포 후 최적화</h2>
<h3 id="1-실습-진행을-위한-환경-세팅">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section09</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 서버 열기</p>
</li>
<li><p>vscode로 <code>onebite-books-server</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-실습">2) 실습</h3>
<ul>
<li><p>인덱스 페이지를 static 페이지로 재설정: <code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter04/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter04/src/app/(with-searchbar)/page.tsx</a></p>
</li>
<li><p><code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter04/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter04/src/app/(with-searchbar)/page.tsx</a></p>
</li>
<li><p><code>/src/app/book/[id]/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter04/src/app/book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section09/chapter04/src/app/book/%5Bid%5D/page.tsx</a></p>
</li>
<li><p>vercel에서 onebite-books-app 프로젝트의 Settings 탭에서 Functions 탭에 &#39;Function Region&#39; 메뉴에서의 위치를 아래와 같이 수정 <img src="https://velog.velcdn.com/images/onion_0/post/e9f3df28-b5ec-4d84-b463-038f111f85a5/image.png"></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[강의] 한 입 크기로 잘라먹는 Next.js #9]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-9</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-9</guid>
            <pubDate>Sat, 30 Aug 2025 13:49:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="고급-라우팅-패턴">고급 라우팅 패턴</h1>
<h2 id="1-패럴렐-라우트parallel-route">1. 패럴렐 라우트(Parallel Route)</h2>
<h3 id="1-패럴렐-라우트parallel-route-1">1) 패럴렐 라우트(Parallel Route)</h3>
<p>패럴렐 라우트(Parallel Route)
= 병렬 라우트</p>
<ul>
<li>하나의 화면 안에 여러 개의 페이지를 병렬로 함께 렌더링 시켜주는 패턴</li>
<li>하나의 화면 안에 Sidebar 페이지와 Feed 페이지를 함께 렌더링 시켜주는 패턴
(이때의 &#39;페이지&#39;는 <code>page.tsx</code> 파일에 작성되는 코드를 지칭)</li>
</ul>
<img src="https://velog.velcdn.com/images/onion_0/post/f80ad8dc-fa8b-4071-b940-adb26de72cc1/image.png">

<p>패럴렐 라우트를 위해서는 슬롯 생성 필요</p>
<ul>
<li>이때 슬롯이란 이름 앞에 골뱅이 기호가 붙은 폴더로, 병렬로 렌더링이 될 하나의 페이지 컴포넌트를 보관하는 역할</li>
<li>슬롯의 개수에는 제한 X</li>
<li>슬롯 안에 새로운 페이지 추가도 가능</li>
</ul>
<blockquote>
<p>패럴렐 라우트 공식문서
<a href="https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering">https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering</a></p>
</blockquote>
<h3 id="2-실습을-위한-환경-준비">2) 실습을 위한 환경 준비</h3>
<ol>
<li><p>vscode로 열기
vscode에서 <code>.../onebite-next/</code> 폴더로 열기</p>
</li>
<li><p>터미널에서 section08 폴더 생성
vscode에서 <code>/onebite-next/section08/</code> 폴더 생성</p>
</li>
<li><p>파일 옮기기
<code>/onebite-next/section07</code> 폴더에 있는 코드 중 아래 사진에 해당하는 코드만 선택하여 <code>/onebite-next/section08</code> 폴더로 옮기기</p>
</li>
</ol>
<img src="https://velog.velcdn.com/images/onion_0/post/bffd2abd-8727-45df-a439-40e1ee08ba18/image.png">

<ol start="4">
<li><p>필요한 모듈 다운로드
생성된 <code>/section08/</code> 폴더로 vscode에서 다시 열기
→ vscode 터미널을 열어서 <code>npm i</code> 명령어 실행</p>
</li>
<li><p>개발 모드로 넥스트 앱 실행
생성된 <code>/section08/</code> 폴더로 vscode에서 다시 열기
→ vscode의 터미널 열기
→ <code>npm run dev</code> 명령어 실행
→ <a href="http://localhost:3000/">http://localhost:3000/</a> 로 이동하면 확인 가능
→ (종료 시에는) <code>Ctrl+D</code>나 <code>Ctrl+C</code> 입력</p>
</li>
<li><p>백엔드 서버 열기
vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code> 명령어 실행</p>
</li>
</ol>
<h3 id="3-실습">3) 실습</h3>
<blockquote>
<p>개발 시 의도하지 않은 방색대로 동작한다면 개발 모드 중단 후 .next 폴더를 삭제하고 다시 가동해보기</p>
</blockquote>
<ul>
<li><p>새 폴더 생성: <code>/src/app/parallel/</code> 폴더 생성</p>
</li>
<li><p>새 파일 생성: <code>/src/app/parallel/page.tsx</code> 파일 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/page.tsx</a></p>
</li>
<li><p>parallel 라우트 생성을 위해서는 슬롯 생성 필요: <code>/src/app/parallel/%40sidebar/page.tsx</code> 폴더+파일 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/%40sidebar/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/%40sidebar/page.tsx</a></p>
</li>
<li><p>레이아웃 컴포넌트 수정: <code>/src/app/parallel/layout.tsx</code> 코드 생성</p>
</li>
<li><p>슬롯 새로 생성: <code>/src/app/parallel/@feed/page.tsx</code>
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/%40feed/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/%40feed/page.tsx</a></p>
</li>
<li><p>슬롯에 페이지 생성: <code>/src/app/parallel/@feed/setting/page.tsx</code>
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/%40feed/setting/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/%40feed/setting/page.tsx</a></p>
</li>
<li><p>레이아웃 컴포넌트 수정: <code>/src/app/parallel/layout.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/layout.tsx</a></p>
</li>
<li><p>슬롯별로 현재 렌더링할 페이지가 없을 때 대신 렌더링할 default 페이지 생성: <code>/src/app/parallel/@sidebar/default.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/%40sidebar/default.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/%40sidebar/default.tsx</a></p>
</li>
<li><p>슬롯별로 현재 렌더링할 페이지가 없을 때 대신 렌더링할 default 페이지 생성: <code>/src/app/parallel/default.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/default.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter01/src/app/parallel/default.tsx</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="2-인터셉팅-라우트inntercepting-route">2. 인터셉팅 라우트(Inntercepting Route)</h2>
<h3 id="1-인터셉팅-라우트inntercepting-route">1) 인터셉팅 라우트(Inntercepting Route)</h3>
<blockquote>
<p>인터셉팅 라우트 공식 문서
<a href="https://nextjs.org/docs/app/api-reference/file-conventions/intercepting-routes">https://nextjs.org/docs/app/api-reference/file-conventions/intercepting-routes</a></p>
</blockquote>
<ul>
<li>사용자가 특정 경로로 접속해서 새로운 페이지를 요청할 때 그 요청을 가로채서 원래 렌더링 돼야 하는 페이지가 아닌 우리가 원하는 어떠한 페이지를 대신 렌더링하도록 설정하는 라우팅 패턴</li>
<li>사용자가 동일한 경로에 접속하게 되더라도 특정 조건을 만족하게 되면 그때에는 원래 페이지가 아닌 다른 페이지를 렌더링 하도록 설정하는 기술</li>
</ul>
<p>인터셉팅 라우트를 동작시키는 조건</p>
<ul>
<li>Next.js에서 고정적으로 정해둠
: 초기 접속 요청이 아닐 때에만 인터셉팅 라우트가 동작하도록 설정
: 사용자가 초기 접속 요청이 아닌 클라이언트 사이드 렌더링 방식으로 접속을 하게 되었을 때에만 동작</li>
</ul>
<h3 id="2-실습-진행을-위한-환경-세팅">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section08</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="3-실습-1">3) 실습</h3>
<p>사용자가 도서 아이템을 클릭해서 북페이지로 이동하게 될 때 화면 내 모달 형태로 피드 위에 바로 나타나도록 생성</p>
<ul>
<li><p>북 폴더 아래에 page.tsx를 인터셉팅 할 수 있는 페이지 생성: <code>/src/app/(.)book/[id]/page.tsx</code> 폴더 + 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter02/src/app/(.)book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter02/src/app/(.)book/%5Bid%5D/page.tsx</a></p>
</li>
<li><p>모달 형태로 상세 페이지 렌더링을 위해 모달 컴포넌트 생성: <code>/src/components/modal.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter02/src/components/modal.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter02/src/components/modal.tsx</a></p>
</li>
<li><p>모달 컴포넌트를 브라우저에 배치: <code>/src/app/layout.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter02/src/app/layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter02/src/app/layout.tsx</a></p>
</li>
<li><p>모달 컴포넌트의 css 코드 생성: <code>/src/components/modal.module.css</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter02/src/components/modal.module.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter02/src/components/modal.module.css</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="3-패럴렐--인터셉팅-라우트">3. 패럴렐 &amp; 인터셉팅 라우트</h2>
<h3 id="1-실습-진행을-위한-환경-세팅">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section08</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-실습">2) 실습</h3>
<p><em>다시 수강</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[강의] 한 입 크기로 잘라먹는 Next.js #8]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-8</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-8</guid>
            <pubDate>Wed, 27 Aug 2025 17:31:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="서버-액션">서버 액션</h1>
<h2 id="1-서버-액션을-소개합니다">1. 서버 액션을 소개합니다</h2>
<h3 id="1-서버-액션server-action이란">1) 서버 액션(Server Action)이란?</h3>
<ul>
<li><p>브라우저에서 호출할 수 있는 서버에서 실행되는 비동기 함수</p>
</li>
<li><p>클라이언트인 브라우저에서 특정 폼의 제출 이벤트가 발생했을 때 서버에서만 실행되는 함수를 브라우저가 직접 호출하면서 데이터까지 폼 데이터 형식으로 전달할 수 있게 해주는 기능
= 클라이언트인 브라우저 측에서 특정 폼, 특정 양식의 제출이 발생했을 때 서버 측에서만 실행되는 어떠한 함수를 실행시켜주는 기능</p>
</li>
<li><p>함수의 최상단에 <code>useServer</code>라는 지시자를 붙임
→ 브라우저가 아닌 서버에서만 실행이 되는 서버 액션으로 설정</p>
</li>
</ul>
<img src="https://velog.velcdn.com/images/onion_0/post/c8d82c77-d5e1-4239-99f4-dd1d5d607c2f/image.png">

<h3 id="2-실습을-위한-환경-준비">2) 실습을 위한 환경 준비</h3>
<ol>
<li><p>vscode로 열기
vscode에서 <code>.../onebite-next/</code> 폴더로 열기</p>
</li>
<li><p>터미널에서 section07 폴더 생성
vscode에서 <code>/onebite-next/section07/</code> 폴더 생성</p>
</li>
<li><p>파일 옮기기
<code>/onebite-next/section06</code> 폴더에 있는 코드 중 아래 사진에 해당하는 코드만 선택하여 <code>/onebite-next/section07</code> 폴더로 옮기기</p>
</li>
</ol>
<img src="https://velog.velcdn.com/images/onion_0/post/bffd2abd-8727-45df-a439-40e1ee08ba18/image.png">

<ol start="4">
<li><p>필요한 모듈 다운로드
생성된 <code>/section07/</code> 폴더로 vscode에서 다시 열기
→ vscode 터미널을 열어서 <code>npm i</code> 명령어 실행</p>
</li>
<li><p>개발 모드로 넥스트 앱 실행
생성된 <code>/section07/</code> 폴더로 vscode에서 다시 열기
→ vscode의 터미널 열기
→ <code>npm run dev</code> 명령어 실행
→ <a href="http://localhost:3000/">http://localhost:3000/</a> 로 이동하면 확인 가능
→ (종료 시에는) <code>Ctrl+D</code>나 <code>Ctrl+C</code> 입력</p>
</li>
<li><p>백엔드 서버 열기
vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code> 명령어 실행</p>
</li>
</ol>
<h3 id="3-실습">3) 실습</h3>
<ul>
<li><p>서버 액션을 위한 브라우저의 폼 생성: <code>/src/app/book/[id]/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter01/src/app/book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter01/src/app/book/%5Bid%5D/page.tsx</a></p>
</li>
<li><p>서버 액션을 위해 생성한 브라우저의 폼의 디자인 수정: <code>/src/app/book/[id]/page.module.css</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter01/src/app/book/%5Bid%5D/page.module.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter01/src/app/book/%5Bid%5D/page.module.css</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="2-리뷰-추가-기능-구현하기-with-서버액션">2. 리뷰 추가 기능 구현하기 (with 서버액션)</h2>
<h3 id="1-실습-진행을-위한-환경-세팅">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section07</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-실습">2) 실습</h3>
<p>백엔드 서버에서 제공하는 리뷰와 관련된 API 기능들을 빠르게 이용해보기</p>
<ul>
<li><p>api 호출: <code>/src/app/book/[id]/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter02/src/app/book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter02/src/app/book/%5Bid%5D/page.tsx</a></p>
</li>
<li><p>가독성을 위해 서버 액션을 별도의 파일로 분리: <code>/src/actions/create-review.action.ts</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter02/src/actions/create-review.action.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter02/src/actions/create-review.action.ts</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="3-리뷰-조회-기능-구현하기-with-서버액션">3. 리뷰 조회 기능 구현하기 (with 서버액션)</h2>
<h3 id="1-실습-진행을-위한-환경-세팅-1">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section07</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-실습-1">2) 실습</h3>
<ul>
<li><p>도서의 리뷰들을 불러오는 기능 구현: <code>/src/app/book/[id]/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/app/book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/app/book/%5Bid%5D/page.tsx</a></p>
</li>
<li><p>하나의 리뷰를 렌더링 할 리뷰 아이템 컴포넌트 생성: <code>/src/components/review-item.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/components/review-item.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/components/review-item.tsx</a></p>
</li>
<li><p>리뷰 아이템 컴포넌트의 css 코드 생성: <code>/src/components/review-item.module.css</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/components/review-item.module.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/components/review-item.module.css</a></p>
</li>
<li><p>컴포넌트를 불러와서 렌더링 하도록 수정: <code>/src/app/book/[id]/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/app/book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/app/book/%5Bid%5D/page.tsx</a></p>
</li>
<li><p>ReviewEditor 컴포넌트를 따로 분리: <code>/src/components/review-editor.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/components/review-editor.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/components/review-editor.tsx</a></p>
</li>
<li><p>ReviewEditor 컴포넌트의 css 코드 생성: <code>/src/components/review-editor.module.css</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/components/review-editor.module.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter03/src/components/review-editor.module.css</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="4-리뷰-재검증-구현하기">4. 리뷰 재검증 구현하기</h2>
<h3 id="1-실습-진행을-위한-환경-세팅-2">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section07</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-실습-2">2) 실습</h3>
<p>지금까지는 사용자가 리뷰를 작성했을 때 브라우저를 한번 새로고침 해줬어야지만 자신이 작성한 리뷰 확인이 가능했음
→ 리뷰가 등록하자마자 바로 등록되도록 수정 필요</p>
<ul>
<li>리뷰가 등록하자마자 바로 등록되도록 수정: <code>/src/actions/create-review.action.ts</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter04/src/actions/create-review.action.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section08/chapter04/src/actions/create-review.action.ts</a>
→ 이때 <code>RevalidatePath</code> 메서드를 사용하면 데이터 캐시 + 풀 라우트 캐시까지 함께 삭제</li>
</ul>
<h3 id="3-동작-과정">3) 동작 과정</h3>
<img src="https://velog.velcdn.com/images/onion_0/post/555df2d9-a23c-4154-91bb-88fbb69add7b/image.png">


<p>&nbsp;</p>
<h2 id="5-다양한-재검증-방식-살펴보기">5. 다양한 재검증 방식 살펴보기</h2>
<h3 id="1-재검증-방식-5가지">1) 재검증 방식 5가지</h3>
<ul>
<li><p>(기존과 동일) 해당하는 페이지만 깔끔하게 재검등하도록 동작
: 두 번째 인수는 생략하고 첫 번째 인수로 특정 페이지의 주소만 명시</p>
</li>
<li><p>특정 경로의 모든 동적 페이지를 재검증
: RevalidatePath() 메서드 호출할 때, 첫 번째 인수로 폴더의 경로를 적고 두 번째 인수로 문자열로 <code>&quot;page&quot;</code>로 작성 
→ 첫 번째 경로와 같은 동적 페이지들이 전부 재검증</p>
</li>
<li><p>특정 레이아웃을 갖는 모든 페이지를 재검증
: RevalidatePath() 메서드 호출할 때, 첫 번째 인수로 폴더의 경로를 적고 두 번째 인수로 문자열로 <code>&quot;layout&quot;</code>로 작성 
→ 첫 번째 경로와 같은 레이아웃 페이지들이 전부 재검증</p>
</li>
<li><p>모든 페이지 재검증
: RevalidatePath() 메서드 호출할 때, 첫 번째 인수로 <code>/</code>를 적고 두 번째 인수로 문자열로 <code>&quot;layout&quot;</code>로 작성 
→ 모든 페이지들이 전부 재검증</p>
</li>
<li><p>태그 값을 기준으로 데이터 캐시 재검증</p>
</li>
</ul>
<h3 id="2-실습-진행을-위한-환경-세팅">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section07</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="3-실습-1">3) 실습</h3>
<ul>
<li><code>/src/actions/create-review.action.ts</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter05/src/actions/create-review.action.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter05/src/actions/create-review.action.ts</a></li>
</ul>
<p>&nbsp;</p>
<h2 id="6-클라이언트-컴포넌트에서의-서버액션">6. 클라이언트 컴포넌트에서의 서버액션</h2>
<p><em>다시 들어보기</em></p>
<h3 id="0-useactionstate">0) useActionState</h3>
<p>리액트 19버전부터 추가되는 완전 최신의 리액트 훅
폼 태그의 상태를 굉장히 쉽게 핸들링할 수 있도록 도와주는 여러가지 기능 보유</p>
<ul>
<li>첫 번째 인수: 핸들링하려는 폼에 액션 함수 삽입</li>
<li>두 번째 함수: 폼의 상태의 초기값</li>
</ul>
<h3 id="1-실습-진행을-위한-환경-세팅-3">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section07</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-실습-3">2) 실습</h3>
<p>중복 제출 방지 등을 위한 코드 수정</p>
<ul>
<li><code>src/components/review-editor.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter06/src/components/review-editor.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter06/src/components/review-editor.tsx</a></li>
</ul>
<p>&nbsp;</p>
<h2 id="7-리뷰-삭제-기능-구현하기">7. 리뷰 삭제 기능 구현하기</h2>
<p><em>다시 듣기</em></p>
<h3 id="1-실습-진행을-위한-환경-세팅-4">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section07</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-실습-4">2) 실습</h3>
<p>리뷰 아이템들에 있는 삭제하기 버튼을 클릭하면 하나의 리뷰를 데이터베이스로부터 삭제하는 기능 생성 ▶ 서버 액션을 통해 구현</p>
<ul>
<li><p><code>/src/components/review-item.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter07/src/components/review-item.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter07/src/components/review-item.tsx</a></p>
</li>
<li><p><code>/src/components/review-item-delete-button.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter07/src/components/review-item-delete-button.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter07/src/components/review-item-delete-button.tsx</a></p>
</li>
<li><p><code>/src/actions/delete-review.action.ts</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter07/src/actions/delete-review.action.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section07/chapter07/src/actions/delete-review.action.ts</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[강의] 한 입 크기로 잘라먹는 Next.js #7]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-7</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-7</guid>
            <pubDate>Sun, 24 Aug 2025 17:35:31 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="스트리밍과-에러핸들링">스트리밍과 에러핸들링</h1>
<h2 id="1-스트리밍이란">1. 스트리밍이란?</h2>
<h3 id="1-스트리밍streaming">1) 스트리밍(Streaming)</h3>
<p>스트리밍(Streaming)</p>
<ul>
<li>큰 용량의 동영상을 아주 빠른 속도로 시청할 수 있게 해줌</li>
<li>서버에서 클라이언트로 데이터를 넘겨줄 때 데이터의 크기가 너무 크거나 데이터를 준비하는데 걸리는 시간이 오래 걸리게 되어서 데이터를 빠르게 전송해주기 어려울 것 같을 때, 데이터를 여러 개의 조각으로 잘게 쪼갠 다음 쪼개진 작은 용량의 데이터들을 하나하나씩 클라이언트에게 전송하는 기술
= 데이터가 다 불러와지지 않은 상태에서도 현재까지 전달 받은 데이터에 접근은 가능하기 때문에 결과적으로 사용자에게는 긴 로딩 없이 서비스 제공 가능</li>
</ul>
<p>Next.js의 스트리밍</p>
<ul>
<li>Next.js는 스트리밍을 웹 서비스에서도 누릴 수 있게 HTMl 페이지를 스트리밍하는 기능을 자체적으로 제공</li>
<li>사용자가 특정 페이지에 접속했을 때 렌더링이 오래 걸리지 않는 컴포넌트들을 먼저 화면에 보여주고, 렌더링이 오래 걸릴 것으로 예상되는 컴포넌트들은 대체 UI를 화면에 보여주고 있다가 서버 측에서 컴포넌트의 렌더링이 완료가 되면 그때 뒤늦게 렌더링이 완료된 컴포넌트들만 따로 전달해서 컨텐츠를 보여주는 방식으로 제공</li>
</ul>
<h3 id="2-스트리밍은-언제-사용할까">2) 스트리밍은 언제 사용할까?</h3>
<p>Next.js의 스트리밍은 Dynamic 페이지에 자주 활용
Next.js의 Dynamic 페이지들은 Next.js 서버에서 빌드타임에 생성되지 않기 때문에 풀라우트 캐시에는 저장 X</p>
<p>▼ 이런 상황에서</p>
<img src="https://velog.velcdn.com/images/onion_0/post/67eea9d6-8d25-4289-9758-4cb2de3d4462/image.png">

<p>▼ 스트리밍을 통해 이렇게 제공 가능</p>
<img src="https://velog.velcdn.com/images/onion_0/post/d0cf9778-a391-4961-9dd8-7daf2646a343/image.png">


<p>&nbsp;</p>
<h2 id="2-스트리밍-1-페이지-스트리밍-적용하기">2. 스트리밍 (1) 페이지 스트리밍 적용하기</h2>
<h3 id="1-실습을-위한-환경-준비">1) 실습을 위한 환경 준비</h3>
<ol>
<li><p>vscode로 열기
vscode에서 <code>.../onebite-next/</code> 폴더로 열기</p>
</li>
<li><p>터미널에서 section06 폴더 생성
vscode에서 <code>/onebite-next/section06/</code> 폴더 생성</p>
</li>
<li><p>파일 옮기기
<code>/onebite-next/section05</code> 폴더에 있는 코드 중 아래 사진에 해당하는 코드만 선택하여 <code>/onebite-next/section06</code> 폴더로 옮기기</p>
</li>
</ol>
<img src="https://velog.velcdn.com/images/onion_0/post/bffd2abd-8727-45df-a439-40e1ee08ba18/image.png">

<ol start="4">
<li><p>필요한 모듈 다운로드
생성된 <code>/section06/</code> 폴더로 vscode에서 다시 열기
→ vscode 터미널을 열어서 <code>npm i</code> 명령어 실행</p>
</li>
<li><p>개발 모드로 넥스트 앱 실행
생성된 <code>/section06/</code> 폴더로 vscode에서 다시 열기
→ vscode의 터미널 열기
→ <code>npm run dev</code> 명령어 실행
→ <a href="http://localhost:3000/">http://localhost:3000/</a> 로 이동하면 확인 가능
→ (종료 시에는) <code>Ctrl+D</code>나 <code>Ctrl+C</code> 입력</p>
</li>
<li><p>백엔드 서버 열기
vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code> 명령어 실행</p>
</li>
</ol>
<h3 id="2-실습">2) 실습</h3>
<ul>
<li><p>스트리밍 기능 구현: <code>/src/app/(with-searchbar)/search/loading.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter02/src/app/(with-searchbar)/search/loading.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter02/src/app/(with-searchbar)/search/loading.tsx</a></p>
</li>
<li><p>딜레이 구현을 위해: <code>/src/util/delay.ts</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter02/src/util/delay.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter02/src/util/delay.ts</a></p>
</li>
<li><p>딜레이 구현을 위해: <code>/src/app/(with-searchbar)/search/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter02/src/app/(with-searchbar)/search/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter02/src/app/(with-searchbar)/search/page.tsx</a></p>
</li>
</ul>
<h3 id="3-스트리밍-코드">3) 스트리밍 코드</h3>
<ul>
<li><code>loading.tsx</code>로 설정한 로딩 시 실행되는 코드는 동일한 경로에 있는 컴포넌트만 스트리밍이 되도록 설정하는 게 아니라, 경로 아래에 있는 모든 페이지 컴포넌트들이 다 스트리밍이 되도록 설정</li>
<li><code>loading.tsx</code> 파일이 스트리밍하도록 설정하는 페이지 컴포넌트는 모든 페이지 컴포넌트는 아니고 <code>async</code>라는 키워드가 붙어서 비동기로 작동하도록 설정된 페이지 컴포넌트에만 스트리밍을 제공</li>
<li><code>loading.tsx</code> 파일은 무조건 <code>page.tsx</code> 코드, 즉 페이지 컴포넌트에만 스트리밍을 적용할 수 있음</li>
<li><code>loading.tsx</code> 파일로 설정된 스트리밍은 브라우저에서 쿼리 스트링이 변경될 때에는 틜거링 되지 않는다</li>
</ul>
<p>&nbsp;</p>
<h2 id="3-스트리밍-2-컴포넌트-스트리밍">3. 스트리밍 (2) 컴포넌트 스트리밍</h2>
<h3 id="1-loading-vs-suspense">1) loading vs. Suspense</h3>
<ul>
<li><code>loading.tsx</code>
: 로딩 컴포넌트는 무조건 페이지 단위로만 스트리밍 적용 가능 → 일반적인 컴포넌트들에는 스트리밍 설정 X</li>
<li><code>Suspense</code>
: 컴포넌트별로 세밀하게 스트리밍 적용
: <code>Suspense</code> 컴포넌트로 비동기 컴포넌트를 감싸주면 해당 컴포넌트를 스트리밍하도록 설정해서 미완성 상태, 즉 로딩 상태로 남겨둠<pre><code>    &lt;Suspense key={q || &quot;&quot;} fallback={&lt;div&gt;Loading ...&lt;/div&gt;}&gt;
      &lt;SearchResult q={q || &quot;&quot;} /&gt;
    &lt;/Suspense&gt;</code></pre>: 쿼리 스트링만 변경되었을 경우 로딩 상태가 다시 표시되지 않는 문제가 발생하지만, 해결도 가능
: 페이지 내에서 여러 개의 비동기 컴포넌트들을 동시다발적으로 스트리밍할 때 굉장히 유용 ⭐⭐⭐⭐⭐</li>
</ul>
<h3 id="2-실습-진행을-위한-환경-세팅">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section06</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="3-실습">3) 실습</h3>
<p>Suspense 맛보기</p>
<ul>
<li><code>loading.tsx</code> 코드 삭제</li>
<li>페이지 컴포넌트 분리 + <code>Suspense</code> 사용 : <code>/src/app/(with-searchbar)/search/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter03/src/app/(with-searchbar)/search/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter03/src/app/(with-searchbar)/search/page.tsx</a></li>
</ul>
<p>Suspense로 하나의 페이지 내에서 여러 개의 비동기 컴포넌트들을 동시다발적으로 스트리밍 시 동작 확인</p>
<ul>
<li><code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter03/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter03/src/app/(with-searchbar)/page.tsx</a></li>
</ul>
<p>&nbsp;</p>
<h2 id="4-스켈레톤skeleton-ui-적용하기">4. 스켈레톤(Skeleton) UI 적용하기</h2>
<h3 id="1-스켈레톤skeleton-ui">1) 스켈레톤(Skeleton) UI</h3>
<p>스켈레톤 = 뼈대
스켈레톤 UI = 뼈대 역할을 하는 UI</p>
<ul>
<li>페이지에 일부 컨텐츠가 로딩이 되는 동안 실제 컨텐츠 대신에 컨텐츠와 비슷하게 생긴 박스 형태의 실루엣 정도만 미리 보여주는 것</li>
<li>필수적인 기능은 아니나, 사용자의 입장에서는 완성도 측면에서 필요할 수 있음</li>
<li>React Loading Skeleton이라는 자동으로 스켈레촌 UI를 만들어주는 라이브러리 존재하니 필요하면 사용 가능</li>
</ul>
<p>▼ Ex. 유튜브의 스켈레톤 UI</p>
<img src="https://velog.velcdn.com/images/onion_0/post/73e44c7e-1080-48b7-9083-d8ddc39741bd/image.png">

<h3 id="2-실습-진행을-위한-환경-세팅-1">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section06</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="3-실습-1">3) 실습</h3>
<ul>
<li><p>스켈레톤 컴포넌트 생성: <code>src/components/skeleton/book-item-skeleton.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter04/src/components/skeleton/book-item-skeleton.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter04/src/components/skeleton/book-item-skeleton.tsx</a></p>
</li>
<li><p>스켈레톤 컴포넌트 적용: <code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter04/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter04/src/app/(with-searchbar)/page.tsx</a></p>
</li>
<li><p>스켈레톤 컴포넌트의 스타일 파일 생성: <code>src/components/skeleton/book-item-skeleton.module.css</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter04/src/components/skeleton/book-item-skeleton.module.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter04/src/components/skeleton/book-item-skeleton.module.css</a></p>
</li>
<li><p>스켈레톤 컴포넌트들을 리스트로 렌더링 해주는 컴포넌트 생성: <code>src/components/skeleton/book-list-skeleton.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter04/src/components/skeleton/book-list-skeleton.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter04/src/components/skeleton/book-list-skeleton.tsx</a></p>
</li>
<li><p>생성한 스켈레톤을 검색 페이지에도 적용: <code>/src/app/(with-searchbar)/search/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter04/src/app/(with-searchbar)/search/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter04/src/app/(with-searchbar)/search/page.tsx</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="5-에러-핸들링-errortsx">5. 에러 핸들링 (error.tsx)</h2>
<h3 id="1-에러-핸들링-errortsx">1) 에러 핸들링 (error.tsx)</h3>
<p>Next.js는 특정 경로에서 발생한 모든 오류들을 한꺼번에 처리할 수 있는 아주 편리한 에러 핸들링 기능을 제공</p>
<ul>
<li><p>에러문 출력을 위해 try-catch문을 사용하는 대신, 에러를 처리하고 싶은 페이지 컴포넌트의 파일과 같은 위치에 에러를 처리할 파일을 하나 생성: <code>error.tsx</code> 코드 생성
→ 이때 에러 파일은 <code>useClient</code>라는 지시자를 통해 클라이언트 컴포넌트로 설정하는 것이 필수 ⭐
→ 해당 에러 파일과 같은 경로에 있거나 하위 경로에 있는 페이지에서 오류가 발생하게 되면 에러 컴포넌트가 페이지 컴포넌트 대신 화면에 출력</p>
</li>
<li><p>에러 컴포넌트에서 제공하는 props</p>
<ul>
<li><code>error</code>
: 에러 객체 확인</li>
<li><code>reset</code>
: 함수
: 에러가 발생한 페이지를 복구하기 위해서 다시 한번 컴포넌트들을 렌더링 시켜보는 기능을 가지고 있는 함수
: 어떠한 매개변수도 전달받지 않으며 어떠한 값도 반환하지 않은 void 타입의 리턴을 하는 함수
: 브라우저(클라이언트) 측에서 현재 서버로부터 전달받은 데이터를 이용해서 화면을 다시 한번 렌더링 해보기만 하는 메서드 = 서버 컴포넌트를 재실행하는 것은 아님
: 에러 상태를 초기화하고 컴포넌트들을 다시 렌더링 → <code>refresh()</code> 메서드로 서버 컴포넌트를 다시 불러와서 리렌더링 한다고 해도 클라이언트 컴포넌트인 에러 상태가 사라지지 않기 때문에, <code>reset()</code>도 그 이후에 필수적으로 사용 필요</li>
</ul>
</li>
<li><p><code>error.tsx</code> 파일은 동일한 경로에 있는 인덱스 페이지 + 하위 경로에 있는 모든 페이지에 다 동일하게 적용</p>
</li>
</ul>
<h3 id="2-실습-진행을-위한-환경-세팅-2">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section06</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>(에러 확인을 위해) 백엔드 서버는 구동 X</p>
</li>
</ul>
<h3 id="3-실습-2">3) 실습</h3>
<ul>
<li><p>에러를 처리하는 파일 생성: <code>/src/app/(with-searchbar)/error.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter05/src/app/(with-searchbar)/error.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section06/chapter05/src/app/(with-searchbar)/error.tsx</a></p>
</li>
<li><p>백엔드 서버 가동: vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
<li><p>여전히 발생하는 오류 제거를 위해 <code>error.tsx</code> 코드 수정 가능</p>
<ul>
<li>강제 전체 렌더링</li>
<li>오류가 발생한 부분만 리렌더링: <code>refresh()</code>로 현재 페이지에 필요한 서버 컴포넌트들을 다시 불러옴</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[강의] 한 입 크기로 잘라먹는 Next.js #6]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-6</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-6</guid>
            <pubDate>Sun, 17 Aug 2025 09:05:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="페이지-캐싱">페이지 캐싱</h1>
<h2 id="1-풀-라우트-캐시-1">1. 풀 라우트 캐시 (1)</h2>
<h3 id="1-풀-라우트-캐시full-route-cache">1) 풀 라우트 캐시(Full Route Cache)</h3>
<p>Next 서버 측에서 빌드 타임에 특정 페이지의 렌더링 결과를 캐싱하는 기능
= 빌드 타임에 정적으로 미리 생성이 되어서 풀 라우트 캐시에 보관</p>
<img src="https://velog.velcdn.com/images/onion_0/post/5b9a263d-f479-47a8-b293-d421f5892c83/image.png">

<h3 id="2-풀-라우트-캐시가-시행되는-페이지는">2) 풀 라우트 캐시가 시행되는 페이지는?</h3>
<p>Next.js 앱에서 만들어 둔 모든 페이지는 자동으로 나뉨</p>
<ul>
<li>정적인 static 페이지 ← 여기에 풀 라우트 캐시 wjrdyd</li>
<li>동적인 dynamic 페이지 </li>
</ul>
<img src="https://velog.velcdn.com/images/onion_0/post/9375a71d-8e7d-4238-bd9c-9d7cb81d40fc/image.png">

<br>

<p>▼ 그렇다면 Next.js 앱에서는 static 페이지와 dynamic 페이지를 어떻게 구분할까?</p>
<img src="https://velog.velcdn.com/images/onion_0/post/a2b36614-1b09-44dd-876c-3eaf0f8bf23b/image.png">

<ul>
<li>Dynamic 페이지를 구분하는 기준<ul>
<li>특정 페이지가 접속 요청을 받을 때마다 매번 변화가 생기거나 데이터가 달라질 경우
→ (서버 컴포넌트에서) 캐시되지 않는 Data Fetching을 사용할 경우
→ (서버 컴포넌트에서) 동적 함수(쿠키, 헤더, 쿼리스트링)을 사용하는 컴포넌트가 있을 때<img src="https://velog.velcdn.com/images/onion_0/post/686f9bea-38c2-4643-bee6-e5c1a33d9272/image.png"></li>
</ul>
</li>
<li>Static 페이지를 구분하는 기준<ul>
<li>Dynamic 페이지가 아니면 모두 Static 페이지가 된다 (기본값)<img src="https://velog.velcdn.com/images/onion_0/post/73b23a66-a66a-4be6-96f0-6400832ff871/image.png">


</li>
</ul>
</li>
</ul>
<h3 id="3-풀-라우트-캐시도-revalidate-가능">3) 풀 라우트 캐시도 Revalidate 가능</h3>
<p>빌드타임에 생성해서 보관해둔 페이지를 특정 시간을 주기로 재생성, 즉 갱신하는 것도 가능</p>
<img src="https://velog.velcdn.com/images/onion_0/post/95126fb3-d20e-49ca-8d26-789f8de5a943/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/1b79a85e-9df1-4109-88e8-7e09be3c17cd/image.png">


<p>&nbsp;</p>
<h2 id="2-풀-라우트-캐시-2">2. 풀 라우트 캐시 (2)</h2>
<h3 id="1-실습을-위한-환경-준비">1) 실습을 위한 환경 준비</h3>
<ol>
<li><p>vscode로 열기
vscode에서 <code>.../onebite-next/</code> 폴더로 열기</p>
</li>
<li><p>터미널에서 section05 폴더 생성
vscode에서 <code>/onebite-next/section05/</code> 폴더 생성</p>
</li>
<li><p>파일 옮기기
<code>/onebite-next/section04</code> 폴더에 있는 코드 중 아래 사진에 해당하는 코드만 선택하여 <code>/onebite-next/section05</code> 폴더로 옮기기</p>
</li>
</ol>
<img src="https://velog.velcdn.com/images/onion_0/post/bffd2abd-8727-45df-a439-40e1ee08ba18/image.png">

<ol start="4">
<li><p>필요한 모듈 다운로드
생성된 <code>/section05/</code> 폴더로 vscode에서 다시 열기
→ vscode 터미널을 열어서 <code>npm i</code> 명령어 실행</p>
</li>
<li><p>개발 모드로 넥스트 앱 실행
생성된 <code>/section05/</code> 폴더로 vscode에서 다시 열기
→ vscode의 터미널 열기
→ <code>npm run dev</code> 명령어 실행
→ <a href="http://localhost:3000/">http://localhost:3000/</a> 로 이동하면 확인 가능
→ (종료 시에는) <code>Ctrl+D</code>나 <code>Ctrl+C</code> 입력</p>
</li>
<li><p>백엔드 서버 열기
vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code> 명령어 실행</p>
</li>
</ol>
<h3 id="2-실습">2) 실습</h3>
<ul>
<li><p>개발 서버 구동할 때 생성되는 오류 해결: <code>/src/app/(with-component)/layout.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter02/src/app/(with-searchbar)/layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter02/src/app/(with-searchbar)/layout.tsx</a></p>
</li>
<li><p>static 페이지로 수정 가능한 컴포넌트들 수정</p>
<ul>
<li><code>/src/app/layout.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter02/src/app/layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter02/src/app/layout.tsx</a></li>
<li><code>/src/app/(with-searchbar)/layout.tsx</code> 코드는 수정할 거 없음</li>
<li><code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter02/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter02/src/app/(with-searchbar)/page.tsx</a></li>
</ul>
</li>
</ul>
<p>=&gt; index 페이지가 static 페이지로 잘 설정됐는지 확인</p>
<ul>
<li>vscode 터미널에 <code>npm run build</code> 명령어로 실행해서 </li>
<li>생성된 <code>/section05/.next/server/app/index.html</code>의 코드를 통해 확인 가능</li>
</ul>
<p>&nbsp;</p>
<h2 id="3-풀-라우트-캐시-3-동적-경로에-적용하기">3. 풀 라우트 캐시 (3): 동적 경로에 적용하기</h2>
<h3 id="1-실습-진행을-위한-환경-세팅">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section05</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-실습-1">2) 실습</h3>
<ul>
<li>static 페이지로 수정 가능한 컴포넌트들 수정<ul>
<li><code>/src/app/(with-searchbar)/search/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter03/src/app/(with-searchbar)/search/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter03/src/app/(with-searchbar)/search/page.tsx</a>
→ 원래 해당 페이지는 동적으로 동작하기 때문에 static 페이지로 수정하지 않아도 되지만, 풀 라우트 캐시 적용을 위해 수정하고 싶다면 위 코드와 같이 수정 진행</li>
<li><code>/src/app/book/[id]/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter03/src/app/book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter03/src/app/book/%5Bid%5D/page.tsx</a>
→ URl 파라미터에 값을 명시할 때는 문자열만!!</li>
</ul>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="4-라우트-세그먼트-옵션">4. 라우트 세그먼트 옵션</h2>
<blockquote>
<p>라우트 세그먼트 옵션 관련 공식 문서
<a href="https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering">https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering</a></p>
</blockquote>
<ul>
<li><code>export const dynamic</code> 옵션: 특정 페이지의 유형을 강제로 static 또는 dynamic 페이지로 설정해주는 옵션<ul>
<li><code>export const dynamic = &quot;auto&quot;;</code> 아무것도 강제하지 않는 옵션 / 코드에 따라 페이지의 유형을 자동으로 설정</li>
<li><code>export const dynamic = &quot;force-dynamic&quot;;</code> 페이지를 강제로 dynamic 페이지로 강제하는 옵션<ul>
<li>특별한 상황이 아니면 권장되지 않음</li>
</ul>
</li>
<li><code>export const dynamic = &quot;force-static&quot;;</code> 페이지를 강제로 static 페이지로 강제하는 옵션<ul>
<li>해당 옵션은 dynamic 페이지에 적용할 경우 기능이 제대로 동작하지 않음</li>
</ul>
</li>
<li><code>export const dynamic = &quot;error&quot;;</code> 페이지를 강제로 static 페이지로 설정하되, static으로 설정하면 안되는 기능이 있다면 빌드 오류를 발생시키는 옵션</li>
</ul>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="5-클라이언트-라우터-캐시">5. 클라이언트 라우터 캐시</h2>
<h3 id="1-클라이언트-라우터-캐시">1) 클라이언트 라우터 캐시</h3>
<p>클라이언트인 브라우저에 저장되는 캐시
페이지 이동을 효율적으로 진행하기 위해 페이지의 일부 데이터를 보관
중복된 레이아웃은 브라우저 측의 캐시에 저장, 중복되지 않은 페이지 등만 따로 불러오게 설정 가능
새로고침이 되거나 아예 탭을 껐다가 다시 접속하는 경우에는 동작하지 않음</p>
<p>▼ 풀 라우트 캐시 진행 과정</p>
<img src="https://velog.velcdn.com/images/onion_0/post/a9c4f15e-881a-42d7-8ccb-9d1547a9f639/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/c377aa6e-8fb9-4582-b95d-04acb60194c2/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/c5fc89a6-1c79-4893-b3ae-4d64945fff3a/image.png">

<p>▼ 이때 중복 발생</p>
<img src="https://velog.velcdn.com/images/onion_0/post/ef390c5c-9789-4305-9a76-363cccbae245/image.png">

<p>▼ 클라이언트 라우터 캐시 전체 진행 과정</p>
<img src="https://velog.velcdn.com/images/onion_0/post/238344c4-ac7a-4327-aa8b-865e0a640dab/image.png">

<h3 id="2-실습-진행을-위한-환경-세팅">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section05</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="3-실습">3) 실습</h3>
<ul>
<li><code>/src/app/(with-searchbar)/layout.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter05/src/app/(with-searchbar)/layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section05/chapter05/src/app/(with-searchbar)/layout.tsx</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[강의] 한 입 크기로 잘라먹는 Next.js #5]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-5</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-5</guid>
            <pubDate>Fri, 15 Aug 2025 07:38:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="데이터-페칭">데이터 페칭</h1>
<p>App Router의 데이터 페칭</p>
<h2 id="1-앱-라우터의-데이터-페칭">1. 앱 라우터의 데이터 페칭</h2>
<h3 id="1-page-router의-데이터-페칭">1) Page Router의 데이터 페칭</h3>
<p>서버 컴포넌트라는 개념이 없고, 모든 컴포넌트가 전부 클라이언트 컴포넌트로써 동작함</p>
<img src="https://velog.velcdn.com/images/onion_0/post/cabc6369-56f4-4200-96b9-d336a4f7f63b/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/8ab680e9-c768-4176-9b59-68fcf77c9dd9/image.png">

<p>▼ 개발자가 직접 아래와 같이 경우를 나누어 개발해야 했음</p>
<img src="https://velog.velcdn.com/images/onion_0/post/2d21869d-a634-4d7e-9dfc-7e0b37c00434/image.png">

<h3 id="2-app-router의-데이터-페칭">2) App Router의 데이터 페칭</h3>
<p>▼ App Router의 서버 컴포넌트 구조</p>
<img src="https://velog.velcdn.com/images/onion_0/post/f95d94a9-47c9-47b3-8663-276a6bfaebe5/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/a67333f3-5e25-41aa-8356-a7e8db83de44/image.png">

<h3 id="3-실습을-위한-환경-준비">3) 실습을 위한 환경 준비</h3>
<ol>
<li><p>vscode로 열기
vscode에서 <code>.../onebite-next/</code> 폴더로 열기</p>
</li>
<li><p>터미널에서 section04 폴더 생성
vscode에서 <code>/onebite-next/section04</code> 폴더 생성</p>
</li>
<li><p>파일 옮기기
<code>/onebite-next/section03</code> 폴더에 있는 코드 중 아래 사진에 해당하는 코드만 선택하여 <code>/onebite-next/section04</code> 폴더로 옮기기</p>
</li>
</ol>
<img src="https://velog.velcdn.com/images/onion_0/post/bffd2abd-8727-45df-a439-40e1ee08ba18/image.png">

<ol start="4">
<li><p>필요한 모듈 다운로드
생성된 <code>/section04/</code> 폴더로 vscode에서 다시 열기
→ vscode 터미널을 열어서 <code>npm i</code> 명령어 실행</p>
</li>
<li><p>개발 모드로 넥스트 앱 실행
생성된 <code>/section04/</code> 폴더로 vscode에서 다시 열기
→ vscode의 터미널 열기
→ <code>npm run dev</code> 명령어 실행
→ <a href="http://localhost:3000/">http://localhost:3000/</a> 로 이동하면 확인 가능
→ (종료 시에는) <code>Ctrl+D</code>나 <code>Ctrl+C</code> 입력</p>
</li>
<li><p>백엔드 서버 열기
vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code> 명령어 실행</p>
</li>
</ol>
<h3 id="4-실습">4) 실습</h3>
<ul>
<li><p><code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter01/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter01/src/app/(with-searchbar)/page.tsx</a></p>
<ul>
<li>로직에 따라 컴포넌트를 분리해서 생성</li>
</ul>
</li>
<li><p>API 주소를 환경 변수로써 등록한 후 필요한 파일에서 불러다가 쓰도록 수정: <code>/section04/.env</code> 코드 생성</p>
<pre><code>NEXT_PUBLIC_API_SERVER_URL=http://localhost:12345  //NEXT_PUBLIC_ 접두사를 붙이지 않으면 Next.js가 자동으로 해당 환경 변수를 서버 측에서만 활용할 수 있게 프라이빗으로 설정해버림</code></pre></li>
<li><p><code>/src/app/(with-searchbar)/search/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter01/src/app/(with-searchbar)/search/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter01/src/app/(with-searchbar)/search/page.tsx</a></p>
</li>
<li><p><code>/src/app/book/[id]/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter01/src/book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter01/src/book/[id]/page.tsx</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="2-데이터-캐시">2. 데이터 캐시</h2>
<h3 id="1-데이터-캐시">1) 데이터 캐시</h3>
<p>fetch 메서드를 활용해 불러온 데이터를 next 서버에 보관하는 기능
영구적으로 데이터를 보관하거나 특정 시간을 주기로 갱신 시키는 것도 가능
불필요한 데이터 요청의 수를 줄여서 웹 서비스의 성능을 크게 개선할 수 있음</p>
<p>▼ 예시</p>
<img src="https://velog.velcdn.com/images/onion_0/post/459252ed-1d2f-4fcf-a1ca-cedf4d634825/image.png">

<ul>
<li><code>fetch()</code> 메서드를 필수로 사용해야만 가능함</li>
</ul>
<h3 id="2-데이터-캐시-옵션">2) 데이터 캐시 옵션</h3>
<ul>
<li><code>no-store</code> 옵션
<img src="https://velog.velcdn.com/images/onion_0/post/643c6148-df22-43aa-ad56-a1e84e07bd9d/image.png"><img src="https://velog.velcdn.com/images/onion_0/post/fe6f2637-d7aa-43ef-8ac0-a0b81dca100d/image.png"> - next.js 15 버전 이상부터는 따로 설정해주지 않아도 기본값으로 <code>no-store</code> 옵션이 설정됨</li>
</ul>
<ul>
<li><code>force-cache</code> 옵션
<img src="https://velog.velcdn.com/images/onion_0/post/e8f46881-cca6-4866-8ee3-226e59602298/image.png"><img src="https://velog.velcdn.com/images/onion_0/post/1020a89c-66d8-4a36-b02c-9dec5a2dddac/image.png"></li>
</ul>
<ul>
<li><code>revalidate</code> 옵션
<img src="https://velog.velcdn.com/images/onion_0/post/1cb9d9a1-522d-4cd3-95dc-92378b524eb5/image.png"><img src="https://velog.velcdn.com/images/onion_0/post/63a74214-45d0-4ea7-9972-2939c207d273/image.png"><img src="https://velog.velcdn.com/images/onion_0/post/a598bcf2-a899-4a28-9eac-37f45657d68a/image.png"></li>
</ul>
<ul>
<li><code>next tag</code> 옵션
<img src="https://velog.velcdn.com/images/onion_0/post/dc2024b1-5916-42ac-9e79-cc431de8b8fb/image.png">    - On-Demand ISR과 같은 방식</li>
</ul>
<h3 id="3-실습-진행을-위한-환경-세팅">3) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section04</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="4-실습-1">4) 실습</h3>
<h4 id="no-store-옵션-설정해보기"><code>no-store</code> 옵션 설정해보기</h4>
<ul>
<li><p><code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter02/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter02/src/app/(with-searchbar)/page.tsx</a></p>
</li>
<li><p>데이터 패칭이 발생할 때마다 로그 출력하도록 설정: <code>/section04/next.config.mjs</code> 코드 수정</p>
<pre><code>import type { NextConfig } from &quot;next&quot;;
</code></pre></li>
</ul>
<p>const nextConfig: NextConfig = {
  logging: {fetches: {fullUrl: true,},},
};</p>
<p>export default nextConfig;</p>
<p>```</p>
<h4 id="force-cache-옵션-설정해보기"><code>force-cache</code> 옵션 설정해보기</h4>
<ul>
<li><code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter02/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter02/src/app/(with-searchbar)/page.tsx</a></li>
</ul>
<h4 id="revalidate-옵션-설정해보기"><code>revalidate</code> 옵션 설정해보기</h4>
<ul>
<li><code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter02/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter02/src/app/(with-searchbar)/page.tsx</a></li>
</ul>
<p>&nbsp;</p>
<h2 id="3-리퀘스트-메모이제이션request-memoization">3. 리퀘스트 메모이제이션(Request Memoization)</h2>
<h3 id="1-리퀘스트-메모이제이션request-memoization">1) 리퀘스트 메모이제이션(Request Memoization)</h3>
<p>&quot;요청을 기억한다&quot;
하나의 페이지를 서버 측에서 렌더링하는 과정에서, 레이아웃 컴포넌트나 페이지 컴포넌트처럼 하나의 페이지를 이루고 있는 여러 개의 컴포넌트에서 발생하는 다양한 API 요청들 중에 중복적으로 발생하는 요청들을 캐싱해서 단 한 번만 요청할 수 있도록 (중복된 요청을 다시 발송하지 않을 수 있도록) 자동으로 데이터 패칭을 최적화 해주는 기능</p>
<img src="https://velog.velcdn.com/images/onion_0/post/3b25f53c-844b-4ed5-a4c1-aa95897bd771/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/034d921e-1ef5-4c71-9fd1-f64694d63c91/image.png">

<br>

<p>▼ 리퀘스트 메모이제이션 vs. 데이터 캐시</p>
<img src="https://velog.velcdn.com/images/onion_0/post/6c413f89-3df5-40dd-93c8-4108e10a2e6a/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/02454981-6f2e-4a59-b046-a2b12474ecc9/image.png">

<br>

<p>중복된 API 요청은 안 보내면 그만인데, 굳이 리퀘스트 메모이제이션 기능을 제공하는 이유는 뭘까?
→ App Router에서 React Server Component의 도입으로 데이터를 패칭하는 패턴이 변화했기 때문 = 서로 다른 컴포넌트에서 동일한 데이터를 필요로 하는 예외적인 경우가 생길 수밖에 없음</p>
<h3 id="2-실습-진행을-위한-환경-세팅">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section04</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p>vscode에서 새 창을 열고 <code>/onebite-books-server/</code> 폴더로 열기
→ vscode 터미널을 열고 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="3-실습">3) 실습</h3>
<ul>
<li><code>/src/app/layout.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter03/src/app/layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section04/chapter03/src/app/layout.tsx</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[강의] 한 입 크기로 잘라먹는 Next.js #4]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-4</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-4</guid>
            <pubDate>Thu, 14 Aug 2025 07:40:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="app-router-시작하기">App Router 시작하기</h1>
<h2 id="1-app-router-시작하기">1. App Router 시작하기</h2>
<h3 id="1-app-router">1) App Router</h3>
<p>Next.js 13 버전에 새롭게 추가된 라우터
기존의 Page Router를 완전히 대체</p>
<p>변경된 사항</p>
<ul>
<li>레이아웃 설정 방식 변경</li>
<li>페이지 라우팅 설정 방식 변경</li>
<li>데이터 페칭 방법 변경</li>
<li>React 18 신규 기능 추가</li>
</ul>
<p>변경되지 않는 사항</p>
<ul>
<li>네비게이팅</li>
<li>프리페칭</li>
<li>사전 렌더링</li>
</ul>
<h3 id="2-실습을-위한-환경-준비">2) 실습을 위한 환경 준비</h3>
<ol>
<li><p>vscode로 열기
vscode에서 <code>.../onebite-next/</code> 폴더로 열기</p>
</li>
<li><p>터미널에서 section3 폴더 생성
vscode에서 터미널을 열어서 <code>npx create-next-app@14 section03</code> 명령어 실행
→ <code>Ok to proceed</code>에서 <code>y</code> 입력
→ 타입스크립트를 사용할 거냐는 질문에 <code>Yes</code> 선택 후 엔터
→ ESLint를 사용할 거냐는 질문에 <code>Yes</code> 선택 후 엔터
→ Tailwind CSS를 사용할 거냐는 질문에 <code>No</code> 선택 후 엔터
→ src/ 디렉토리를 사용할 거냐는 질문에 <code>Yes</code> 선택 후 엔터
→ App Router를 사용할 거냐는 질문에 <code>Yes</code> 선택 후 엔터
→ import alias를 사용할 거냐는 질문에 <code>No</code> 선택 후 엔터</p>
</li>
</ol>
<blockquote>
<p>import alias: 임포트 문을 사용할 때 절대 경로를 사용해서 값을 불러올 수 있게 하는 그러한 기능</p>
</blockquote>
<ul>
<li><code>npx</code> 명령어<ul>
<li>node-package-execute</li>
<li>npmjs.com에 실제로 올라가 있는 등록되어 있는 라이브러리들의 최신 버전의 노드 패키지를 다운로드 없이 바로 실행</li>
</ul>
</li>
<li><code>create-next-app</code> 명령어<ul>
<li>Next.js 공식 문서에서 안내하고 있는 새로운 Next.App을 생성하는 노드.js 패키지</li>
</ul>
</li>
<li><code>@14</code> 명렁어<ul>
<li>현재 설치하려는 Next App을 14 버전으로 설치하겠다는 뜻</li>
<li>현재 가장 최신 버전과는 약간 차이가 있지만, 페이지 라우터에 존재하는 모든 기능들은 14 버전에서 안정적으로 모두 다 사용 가능</li>
</ul>
</li>
</ul>
<ol start="3">
<li><p>개발 모드로 넥스트 앱 실행
생성된 <code>/section03/</code> 폴더로 vscode에서 다시 열기
→ vscode의 터미널 열기
→ <code>npm run dev</code> 명령어 실행
→ <a href="http://localhost:3000/">http://localhost:3000/</a> 로 이동하면 확인 가능
→ (종료 시에는) <code>Ctrl+D</code>나 <code>Ctrl+C</code> 입력</p>
</li>
<li><p><code>.eslintrc.json</code> 파일명을 <code>eslint.config.mjs</code>로 바꾸고 코드는 아래와 같이 수정하기</p>
<pre><code>import { dirname } from &quot;path&quot;;
import { fileURLToPath } from &quot;url&quot;;
import { FlatCompat } from &quot;@eslint/eslintrc&quot;;
</code></pre></li>
</ol>
<p>const <strong>filename = fileURLToPath(import.meta.url);
const __dirname = dirname(</strong>filename);</p>
<p>const compat = new FlatCompat({
  baseDirectory: __dirname,
});</p>
<p>const eslintConfig = [
  ...compat.extends(&quot;next/core-web-vitals&quot;, &quot;next/typescript&quot;),
  {
    // 이 부분이 추가됩니다!
    rules: { 
      &quot;@typescript-eslint/no-unused-vars&quot;: &quot;warn&quot;,
      &quot;@typescript-eslint/no-explicit-any&quot;: &quot;off&quot;,
    },
  },
];</p>
<p>export default eslintConfig;</p>
<p>```</p>
<p>&nbsp;</p>
<h2 id="2-페이지-라우팅-설정하기">2. 페이지 라우팅 설정하기</h2>
<h3 id="1-app-router-버전의-페이지-라우팅">1) App Router 버전의 페이지 라우팅</h3>
<p>Page Router 버전의 페이지 라우팅과 방식이 크게 다르지 않음
→ 라우팅이 자동 설정된다는 점은 두 방식 모두 동일</p>
<p>Page Router 버전의 페이지 라우터에서는...
→ <code>/src/pages/</code> 아래에 있는 파일명이나 폴더명이 페이지의 이름이 되었음</p>
<p>App Router 버전의 페이지 라우터에서는...
→ <code>/src/app/</code> 아래에서 <code>page.tsx</code>라는 파일명을 갖는 파일만 페이지가 됨</p>
<img src="https://velog.velcdn.com/images/onion_0/post/5b4d060f-6007-4603-974d-d71503559c06/image.png">

<p>→ 동적 경로는...</p>
<img src="https://velog.velcdn.com/images/onion_0/post/909af7bc-f3d6-4fdf-8475-e0c94b85683a/image.png">

<h3 id="2-진행할-프로젝트">2) 진행할 프로젝트</h3>
<p>page 라우터에서 진행했던 &#39;한입 북스 프로젝트&#39;를 그대로 진행</p>
<h3 id="3-실습-진행을-위한-환경-세팅">3) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section03</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</p>
</li>
<li><p><code>src/app/global.css</code> 코드 전체 삭제</p>
</li>
<li><p><code>src/app/page.module.css</code> 코드 전체 삭제</p>
</li>
<li><p><code>src/app/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter02/src/app/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter02/src/app/page.tsx</a></p>
</li>
</ul>
<h3 id="4-실습-진행">4) 실습 진행</h3>
<ul>
<li><p>검색 페이지 생성: <code>/src/app/search/page.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter02/src/app/search/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter02/src/app/search/page.tsx</a></p>
<ul>
<li>쿼리 스트링을 위한 코드 추가</li>
<li>페이지에서 내보낼 내용 작성</li>
</ul>
</li>
<li><p>하나의 도서 아이템을 상세히 조회하는 북페이지 생성: <code>src/app/book/[id]/page.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter02/src/app/book/%5Bid%5D/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter02/src/app/book/%5Bid%5D/page.tsx</a></p>
<ul>
<li>동적 경로 라우팅을 설정할 때에는 이와 같이 설정하면 됨</li>
<li>url 파라미터 값을 해당 페이지에서 활용하는 코드 추가</li>
<li>만약 url 파라미터 값을 여러 개 하고 싶다면 page router에서 실행한 것과 동일하게 <code>/[id]/</code> 폴더명을 수정하면 됨</li>
</ul>
</li>
</ul>
<h4 id="추가-사항">추가 사항</h4>
<p>App Router에서는 쿼리 스트링이나 URL 파라미터와 같은 경로상에 포함되는 값들은 페이지 컴포넌트에게 모두 props로써 전달</p>
<p>서버 컴포넌트의 경우에는 서버 측에서 사전 렌더링을 위해 딱 한 번 실행이 되기 때문에 <code>async</code> 키워드를 붙일 수 있음</p>
<p>&nbsp;</p>
<h2 id="3-레이아웃-설정하기">3. 레이아웃 설정하기</h2>
<h3 id="1-레이아웃-설정">1) 레이아웃 설정</h3>
<img src="https://velog.velcdn.com/images/onion_0/post/ee2fc95c-43bd-4ca4-8fb1-8d3dff51709f/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/bbf70b35-5982-4cd6-ac09-23dafbee7d1e/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/1144a6e5-b15b-4158-aee0-74596b081486/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/5ecf7519-e3c0-49ec-bbd6-8a2ea5248b2d/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/de9959cf-ecd0-44a6-ba6e-1782ab916cec/image.png">

<h3 id="2-실습-진행을-위한-환경-세팅">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li>vscode로 <code>section03</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</li>
</ul>
<h3 id="3-실습">3) 실습</h3>
<ul>
<li><p><code>/src/app/layout.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter03/src/app/layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter03/src/app/layout.tsx</a></p>
</li>
<li><p><code>/src/app/(with-searchbar)/layout.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter03/src/app/(with-searchbar)/layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter03/src/app/(with-searchbar)/layout.tsx</a></p>
</li>
<li><p><code>/src/app/page.module.css</code> 파일과 <code>/src/app/page.tsx</code> 파일을  <code>/src/app/(with-searchbar)/</code> 폴더 내로 이동</p>
</li>
</ul>
<h4 id="추가-사항-1">추가 사항</h4>
<p><code>/src/app/layout.tsx</code></p>
<ul>
<li>모든 페이지에 다 동일하게 적용이 되는 가장 기초적인 레이아웃</li>
<li>HTML 페이지의 기초 틀을 유지하도록 만들어줌</li>
</ul>
<p><code>layout.tsx</code> 코드에서는 <code>page/tsx</code>가 어디 위치에 배치할 지 명시해줘야 함!!
→ <code>layout.tsx</code> 코드에서 <code>{children}</code>이라는 프롭스를 꼭 전달 받아야 함</p>
<p>특정 페이지들에만 적용되는 공통 레이아웃 적용을 위해서는 <strong>라우트 그룹(Route Group)</strong> 기능 사용 가능
→ <code>/src/app/</code> 폴더 밑에 새로운 폴더를 생성하는데, 이때 폴더의 이름은 <code>()</code>로 감싸주기
→ 라우트 그룹은 경로 상에는 아무런 영향을 미치지 않음</p>
<p>&nbsp;</p>
<h2 id="4-리액트-서버-컴포넌트-이해하기">4. 리액트 서버 컴포넌트 이해하기</h2>
<h3 id="1-리액트-서버-컴포넌트-등장-배경">1) 리액트 서버 컴포넌트 등장 배경</h3>
<p>상호작용이 있는 컴포넌트와 상호작용이 없는 컴포넌트가 섞여 있을 때, 상호작용이 있는 컴포넌트만 수화(Hydration)하면 되는데...
Page Router에서는 한 페이지에 속해있기만 한다면 상호작용 여부와 상관 없이 Hydration을 전부 진행해서 TTI까지 늦어지게 됨</p>
<p><strong>그렇다면?</strong></p>
<p>상호작용이 없는 컴포넌트들은 Hydration이 필요가 없기 때문에, 상호작용이 있는 컴포넌트만 구분해서 Hydration을 진행하면 됨
상호작용이 없는 컴포넌트들은 사전 렌더링 때 한 번만 불러오면 됨</p>
<h3 id="2-리액트-서버-컴포넌트react-server-component">2) 리액트 서버 컴포넌트(React Server Component)</h3>
<ul>
<li>React 18v부터 새롭게 추가된, 새로운 유형의 컴포넌트</li>
<li>서버측에서만 실행되는 컴포넌트 (브라우저에서는 실행 X)</li>
</ul>
<p>▼ 사전 렌더링 시에는 모든 컴포넌트를 불러옴</p>
<img src="https://velog.velcdn.com/images/onion_0/post/5d93db52-5ec1-4566-a352-4296b3d4b419/image.png">

<p>▼ 수화를 위해 컴포넌트를 불러올 때에는, 상호작용이 있는 컴포넌트들만 구분해서 불러옴</p>
<img src="https://velog.velcdn.com/images/onion_0/post/30e20c11-9826-401a-a0eb-fc38de723b44/image.png">

<ul>
<li>서버 컴포넌트 = 서버에서만 실행되는 컴포넌트 (상호작용이 없는 컴포넌트)</li>
<li>클라이언트 컴포넌트 = 일반적인 리액트 컴포넌트 (상호작용이 없는 컴포넌트)</li>
</ul>
<img src="https://velog.velcdn.com/images/onion_0/post/c39ab5fc-aaba-484c-bdbb-1dec3d91b458/image.png">

<p>=&gt; Next.js의 공식 문서에서는 페이지의 대부분은 서버 컴포넌트로 구성할 것을 권장하며, 클라이언트 컴포넌트는 꼭 필요한 경우에만 사용할 것</p>
<h3 id="2-실습-진행을-위한-환경-세팅-1">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li>vscode로 <code>section03</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</li>
</ul>
<h3 id="3-실습-1">3) 실습</h3>
<ul>
<li><code>/src/app/(with-searchbar)/searchbar.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter04/app/(with-searchbar)/searchbar.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter04/app/(with-searchbar)/searchbar.tsx</a><ul>
<li>search bar만 클라이언트 컴포넌트로 별개로 작성을 위해 파일 새로 생성</li>
<li>클라이언트 컴포넌트로 제작 (상태값 보관, 이벤트 핸들러 필요)</li>
</ul>
</li>
</ul>
<h4 id="추가-사항-2">추가 사항</h4>
<p>App Router에서는 기본적으로 모든 컴포넌트가 서버 컴포넌트로 동작</p>
<p>서버 컴포넌트에서는 원래 서버에서 할 수 있었던 보안에 민감한 작업들이나 데이터를 패칭해 오는 기능 등의 다양한 작업들을 진행할 수 있게 됨 
= 클라이언트 컴포넌트에서는 해당 동작이 불가능
= 클라이언트 컴포넌트에서는 가능한 동작을 서버 컴포넌트에서는 동작 불가능 (Ex. <code>useEffect()</code>)</p>
<p>링크는 HTMl의 고유 기능이기 때문에 JS 기능을 활용하는 상호작용은 아니어서, 링크로 이동하는 기능만 포함되어 있다면 해당 컴포넌트는 서버 컴포넌트!</p>
<p>&nbsp;</p>
<h2 id="5-리액트-서버-컴포넌트-주의사항">5. 리액트 서버 컴포넌트 주의사항</h2>
<img src="https://velog.velcdn.com/images/onion_0/post/2edff11f-999b-4e14-aed0-d0f5758c95f6/image.png">

<h3 id="1-서버-컴포넌트에는-브라우저에서-실행될-코드가-포함되면-안된다">1) 서버 컴포넌트에는 브라우저에서 실행될 코드가 포함되면 안된다</h3>
<p>▼ 불가능!</p>
<img src="https://velog.velcdn.com/images/onion_0/post/7e96f178-93a3-4f35-a215-fd90d2cff08f/image.png">

<p><img src="https://velog.velcdn.com/images/onion_0/post/559b920c-3af6-4b74-9667-d8aa2ac910ca/image.png" alt=""></p>
<h3 id="2-클라이언트-컴포넌트에는-클라이언트에서만-실행되지-않는다">2) 클라이언트 컴포넌트에는 클라이언트에서만 실행되지 않는다</h3>
<ul>
<li>서버 컴포넌트 = 서버 측에서만 실행되는 것</li>
<li>클라이언트 컴포넌트 = 클라이언트 + 서버 측에서 모두 실행되는 것</li>
</ul>
<h3 id="3-클라이언트-컴포넌트에서-서버-컴포넌트를-import-할-수-없다">3) 클라이언트 컴포넌트에서 서버 컴포넌트를 import 할 수 없다</h3>
<p>▼ 불가능!</p>
<img src="https://velog.velcdn.com/images/onion_0/post/b156b0fa-28aa-4a1a-a4c9-820213214ec7/image.png">

<p>▼ 왜 불가능하지?</p>
<img src="https://velog.velcdn.com/images/onion_0/post/b88e857b-cb7f-4566-a827-daf88cedeb57/image.png">

<p>▼ 그럼 이런 경우에서 Next.js는 어떻게 동작하지?</p>
<img src="https://velog.velcdn.com/images/onion_0/post/3c3c11df-eb6a-498b-8474-62865c347f33/image.png">

<h4 id="실습으로-확인해보기">실습으로 확인해보기</h4>
<ul>
<li><p><code>src/app/(with-searchbar)/client-component.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter05/app/(with-searchbar)/client-component.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter05/app/(with-searchbar)/client-component.tsx</a></p>
</li>
<li><p><code>src/app/(with-searchbar)/server-component.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter05/app/(with-searchbar)/server-component.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter05/app/(with-searchbar)/server-component.tsx</a></p>
</li>
<li><p><code>src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter05/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter05/app/(with-searchbar)/page.tsx</a></p>
</li>
</ul>
<h4 id="추가-사항-3">추가 사항</h4>
<p>만약 어쩔 수 없이 클라이언트 컴포넌트가 서버 컴포넌트를 반드시 자식으로 둬야 하는 경우가 된다면, 그럴 때에는 서버 컴포넌트를 클라이언트 컴포넌트에서 children props로 받아서 렌더링을 시켜주기</p>
<h3 id="4-서버-컴포넌트에서-클라이언트-컴포넌트에게-직렬화-되지-않는-props는-전달-불가능하다">4) 서버 컴포넌트에서 클라이언트 컴포넌트에게 직렬화 되지 않는 Props는 전달 불가능하다</h3>
<p>직렬화(Serialization)</p>
<ul>
<li>객체, 배열, 클래스 등의 복잡한 구조의 데이터를 네트워크 상으로 전송하기 위해 아주 단순한 형태(문자열, Byte)로 변환하는 것</li>
<li>예시<img src="https://velog.velcdn.com/images/onion_0/post/735a7867-cd55-4e73-9e88-4d569fb6b9ab/image.png">

</li>
</ul>
<p>그런데, JS 함수는 직렬화가 불가능
= 서버 컴포넌트에서 클라이언트 컴포넌트로 향하는 Props가 될 수 없음</p>
<img src="https://velog.velcdn.com/images/onion_0/post/79ac2bab-1b0e-4717-a112-e06d6b9c2b1f/image.png">

<br>

<p>사전 렌더링 과정 진행 중에...</p>
<img src="https://velog.velcdn.com/images/onion_0/post/1de5dae3-c9cf-4ad8-8a2e-c013204c07dc/image.png">

<ul>
<li>서버 컴포넌트가 실행되면 바로 HTML 태그가 실행되는 것이 아니라, RSC 페이로드라는 JSON과 비슷한 형태의 문자열이 생성</li>
<li>특정 서버 컴포넌트가 자신의 자식인 클라이언트 컴포넌트에게 함수 형태의 값을 props로 전달하고 있다면, 함수값 또한 함께 직렬화가 되어서 RSC 페이로드에 포함이 되어야 하는데 그게 불가능!
=&gt; 런타임 에러 발생!!</li>
</ul>
<blockquote>
<p>RSC Payload</p>
</blockquote>
<ul>
<li>React Server Component의 순수한 데이터(결과물)</li>
<li>React Server Component를 직렬화 한 결과</li>
<li>RSC Payload에는 서버 컴포넌트의 모든 데이터가 포함<ul>
<li>서버 컴포넌트의 렌더링 결과</li>
<li>연결된 클라이언트 컴포넌트의 위치</li>
<li>클라이언트 컴포넌트에게 전달하는 Props 값</li>
</ul>
</li>
<li>예시
<img src="https://velog.velcdn.com/images/onion_0/post/473ab7d5-515e-47bb-9fa1-9c3dc288d16c/image.png" alt=""></li>
</ul>
<p>&nbsp;</p>
<h2 id="6-네비게이팅">6. 네비게이팅</h2>
<p><em>실습 부분만 다시 듣기</em></p>
<h3 id="1-네비게이팅">1) 네비게이팅</h3>
<p>네비게이팅(Navigating) 
= 페이지 이동을 의미</p>
<p>페이지 이동은 Client Side Rendering 방식으로 처리
= Page Router 버전과 동일한 방식</p>
<p>App Router에서는 <strong>Server Component</strong>가 추가되어 방식이 살짝 변경됨 (비슷하긴 하지만...)</p>
<img src="https://velog.velcdn.com/images/onion_0/post/cfe11721-acf5-4519-8961-2b0238b8fc11/image.png">

<ul>
<li>기존에는 JS Bundle(클라이언트 컴포넌트)만 미리 전달되었지만, App Router에서는 서버 컴포넌트까지 같이 전달</li>
</ul>
<h3 id="2-실습-진행을-위한-환경-세팅-2">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li>vscode로 <code>section03</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 모드 서버 열기</li>
</ul>
<h3 id="3-실습-2">3) 실습</h3>
<p>모든 페이지에 다 똑같이 렌더링 되는 글로벌 내비게이션 바 생성
→ 루트 레이아웃에 생성하면 됨</p>
<ul>
<li><p><code>/src/app/layout.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter06/src/app/layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter06/src/app/layout.tsx</a></p>
</li>
<li><p><code>/src/app/(with-searchbar)/search/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter06/src/app/(with-searchbar)/search/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter06/src/app/(with-searchbar)/search/page.tsx</a></p>
</li>
<li><p><code>/src/app/(with-searchbar)/page.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter06/src/app/(with-searchbar)/page.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter06/src/app/(with-searchbar)/page.tsx</a></p>
</li>
<li><p><code>/src/omponents/</code> 폴더 생성 후 <code>client-component.tsx</code>, <code>server-components.tsx</code>, <code>serachbar.tsx</code> 코드를 해당 폴더로 이동</p>
</li>
<li><p><code>/src/components/searchbar.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter06/src/components/searchbar.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section03/chapter06/src/components/searchbar.tsx</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="7-한입북스-ui-구현하기">7. 한입북스 UI 구현하기</h2>
<p>수업 자료로 올려주신 <code>/src/</code> 코드를 바탕으로 코드 리뷰 진행</p>
<p><em>코드 리뷰 필요하면 다시 듣기</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[강의] 한 입 크기로 잘라먹는 Next.js #3. (2)]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-3.-2</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-3.-2</guid>
            <pubDate>Wed, 13 Aug 2025 11:22:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="page-router-핵심-정리">Page Router 핵심 정리</h1>
<h2 id="10-사전-렌더링과-데이터-페칭">10. 사전 렌더링과 데이터 페칭</h2>
<h3 id="1-데이터-패칭">1) 데이터 패칭...?</h3>
<p>Next.js 앱에서 백엔드 서버로부터 데이터를 불러오는 것</p>
<p>리액트 앱에서는...</p>
<ol>
<li>불러온 데이터를 보관할 State 생성</li>
<li>데이터 페칭 함수 생성</li>
<li>컴포넌트 마운트 시점에 fetchData 호출</li>
<li>데이터 로딩중일 때의 예외 처리</li>
</ol>
<img src="https://velog.velcdn.com/images/onion_0/post/f997f2a5-5491-4baa-af5f-3dee497a25f8/image.png">

<ul>
<li>이 과정은 초기 접속 요청부터 데이터 로딩까지 오랜 시간이 소요</li>
</ul>
<br>

<p>▼ 백엔드 서버로부터 현재 페이지에 필요한 데이터를 미리 불러오도록 설정 가능</p>
<img src="https://velog.velcdn.com/images/onion_0/post/bc15cc94-752c-4017-bbbd-e413936d3945/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/df8b6c8d-65d6-4e90-a36d-f80fbe268920/image.png">

<br>

<p>▼ React App의 데이터 페칭 vs. Next App의 데이터 페칭</p>
<img src=" https://velog.velcdn.com/images/onion_0/post/da4392fd-6aaa-4dfa-b0e6-ed7cfd54d562/image.png">

<br>

<p>Next.js에서 제공하는 사전 렌더링 방식</p>
<ul>
<li>서버사이드 렌더링(SSR)<ul>
<li>가장 기본적인 사전 렌더링 방식</li>
<li>요청이 들어올 때마다 사전 렌더링 진행</li>
</ul>
</li>
<li>정적 사이트 생성(SSG)<ul>
<li>바로 위에서  살펴본 사전 렌더링 방식</li>
<li>빌드 타임에 미리 페이지를 사전 렌더링 해 둠</li>
</ul>
</li>
<li>증분 정적 재생성(ISR)<ul>
<li>(향후 다룰 예정...)</li>
</ul>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="11-ssr-1-소개-및-실습">11. SSR #1. 소개 및 실습</h2>
<h3 id="1-ssrserver-side-rendering">1) SSR(Server Side Rendering)</h3>
<p>가장 기본적인 사전 렌더링 방식
요청이 들어올 때마다 사전 렌더링 진행 → 페이지 내부의 데이터를 항상 최신으로 유지 가능
데이터 요청이 늦어질 경우 그 뒤에 동작하는 모든 과정도 같이 느려짐</p>
<img src="https://velog.velcdn.com/images/onion_0/post/19a0ae5c-5263-44e9-b872-3129cb4e9cd9/image.png">

<blockquote>
<p>SSR 공식 문서
<a href="https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering">https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering</a></p>
</blockquote>
<h3 id="2-실습-진행을-위한-환경-세팅">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 서버 열기</li>
</ul>
<h3 id="3-ssr-방식으로-동작하도록-수정">3) SSR 방식으로 동작하도록 수정</h3>
<p><code>/src/pages/index.tsx</code> 페이지가 SSR 방식으로 동작하도록 코드 수정</p>
<ul>
<li><code>index.tsx</code>의 <code>Home()</code> 컴포넌트보다 먼저 실행되어 컴포넌트에 필요한 데이터를 불러오는 함수 정의
‼️해당 함수의 리턴값은 <code>props</code>라는 객체 프로퍼티를 포함하는 단 하나의 객체여야만 함
‼️서버 측에서만 실행되는 함수! → <code>console.log()</code> 함수가 있어도 vscode의 터미널에서만 확인 가능</li>
<li>해당 함수로부터 <code>props</code> 데이터를 전달 받는 페이지 컴포넌트의 <code>props</code> 타입은 어떻게 정의해야 하지?
<code>InferGetServerSidePropsType</code>이라는 Next.js에서 제공하는 내장 타입
: props를 전달해주는 함수의 반환값 타입을 자동으로 추론해주는 기능을 보유</li>
<li>코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter11/src/pages/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter11/src/pages/index.tsx</a></li>
</ul>
<p>서버에서 <code>window.location</code>을 실행하면?</p>
<ul>
<li><code>window</code>가 <code>Undefined</code>가 되어버려서, 결국 <code>Undefined.location</code>을 호출하는 게 되어버림</li>
</ul>
<p>브라우저 측에서만 실행되는 코드 작성을 하고 싶다면?</p>
<ul>
<li>(여러 방법이 있음)</li>
<li>가장 쉬운 방법은? <code>useEffect</code>를 이용하는 것</li>
</ul>
<p>&nbsp;</p>
<h2 id="12-ssr-2-실습">12. SSR #2. 실습</h2>
<p><em>다시 듣기</em></p>
<h3 id="1-실습-진행을-위한-환경-세팅">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 서버 열기</p>
</li>
<li><p>vscode로 <code>onebite-books-server</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-api-호출을-담당할-함수-생성">2) API 호출을 담당할 함수 생성</h3>
<ul>
<li><code>/src/lib/</code> 폴더 새로 생성</li>
<li>모든 도서를 불러오는 함수 생성: <code>/src/lib/fetch-books.ts</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter12/src/lib/fetch-books.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter12/src/lib/fetch-books.ts</a></li>
<li>추천도서 목록 생성: <code>src/lib/fetch-random-books.ts</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter12/src/lib/fetch-random-books.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter12/src/lib/fetch-random-books.ts</a></li>
<li><code>/src/pages/index.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter12/src/pages/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter12/src/pages/index.tsx</a></li>
<li><code>/src/lib/fetch-one-books.ts</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter12/src/lib/fetch-one-book.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter12/src/lib/fetch-one-book.ts</a></li>
</ul>
<p>&nbsp;</p>
<h2 id="13-ssg-1-소개">13. SSG #1. 소개</h2>
<h3 id="ssg">SSG</h3>
<p>SSR 방식의 단점을 해결하는 새로운 사전 렌더링 방식
명령어로 빌드할 때, 즉 빌드 타임에 미리 페이지를 사전 렌더링 → 페이지를 딱 한 번만 생성
사전 렌더링에 많은 시간이 소요되는 페이지이더라도 사용자의 요청에는 매우 빠른 속도로 응답 가능
빌드 타임 이후에는 페이지를 새롭게 사전 렌더링 하지 않기 때문에, 최신 데이터를 반영하기에는 어려움 → 정적 페이지에 적합</p>
<img src="https://velog.velcdn.com/images/onion_0/post/9c1d5b82-a7ec-4c50-9b5b-2b8d09135245/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/32267f5f-30f5-4960-9d58-99edd63a780f/image.png">


<p>&nbsp;</p>
<h2 id="14-ssg-2-정적-경로에-적용하기">14. SSG #2. 정적 경로에 적용하기</h2>
<h3 id="1-실습-진행을-위한-환경-세팅-1">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 서버 열기</p>
</li>
<li><p>vscode로 <code>onebite-books-server</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-ssg-동작하도록-수정">2) SSG 동작하도록 수정</h3>
<ul>
<li><p><code>/src/pages/index.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter14/src/pages/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter14/src/pages/index.tsx</a></p>
</li>
<li><p>개발 서버를 열었을 경우, 수정이 있을 때마다 사전 렌더링 방식에 상관 없이 바로바로 다시 사전 렌더링 진행
→ 프로덕션 서버에서 열어서 확인 가능
→ 따로 사전 렌더링 방식을 설정하지 않았을 경우 SSG 방식으로 자동으로 실행</p>
</li>
<li><p><code>/src/pages/search/index.tsx</code> 코드 수정
SSG 방식에는 쿼리 스트링 값을 꺼내오는 방법은 사용 불가능 (= 검색 결과를 서버로부터 불러오는 동작은 수행 불가)
→ <code>useEffect()</code>를 통해 백엔드에 직접 요청해서 사용 가능
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter14/src/pages/search/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter14/src/pages/search/index.tsx</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="15-ssg-3-동적-경로에-적용하기">15. SSG #3. 동적 경로에 적용하기</h2>
<h3 id="1-실습-진행을-위한-환경-세팅-2">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 서버 열기</p>
</li>
<li><p>vscode로 <code>onebite-books-server</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="2-ssg-동작하도록-수정-1">2) SSG 동작하도록 수정</h3>
<ul>
<li><p><code>/src/pages/book/[id].tsx</code> 코드 수정</p>
</li>
<li><p>동적 경로를 가진 페이지를 Next.js 서버 측에서 빌드타임에 SSG 방식으로 사전 렌더링하여 미리 생성해두기 위해서는... 
선수 과정으로 해당 페이지에 어떠한 URL 파라미터들이 존재할 수 있는지, 그러므로 어떤 경로들이 존재할 수 있는지 설명하는 과정이 필수적으로 요구됨</p>
<img src="https://velog.velcdn.com/images/onion_0/post/8f179741-15e1-4093-9e46-e2dc8442d5b9/image.png">


</li>
</ul>
<p>&nbsp;</p>
<h2 id="16-ssg-4-폴백옵션-설정하기">16. SSG #4. 폴백옵션 설정하기</h2>
<p><em>강의 다시 듣고 정리</em></p>
<h3 id="1">1)</h3>
<p>&nbsp;</p>
<h2 id="17-isr-1-소개-및-실습">17. ISR #1. 소개 및 실습</h2>
<h3 id="1-isrincremental-static-regeneration">1) ISR(Incremental Static Regeneration)</h3>
<p>증분 정적 재생성 방식</p>
<ul>
<li>SSG 방식으로 생성된 정적 페이지를 일정 시간을 주기로 다시 생성하는 기술</li>
<li>기존 SSG의 장점(매우 빠른 속도로 응답 가능) + 기존 SSR 장점(최신 데이터 반영 가능)</li>
</ul>
<img src="https://velog.velcdn.com/images/onion_0/post/bcc17ccd-0f2e-4411-80db-0f477837d28c/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/112ecef0-6b17-4738-9198-8a459d3616b7/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/a0019bba-2fe0-4778-a838-38f58397c5b2/image.png">

<blockquote>
<p>ISR 공식 문서
<a href="https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering">https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering</a></p>
</blockquote>
<h3 id="2-실습-진행을-위한-환경-세팅-1">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run start</code>로 프로덕션 서버 열기</p>
</li>
<li><p>vscode로 <code>onebite-books-server</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="3-isr-동작하도록-수정">3) ISR 동작하도록 수정</h3>
<p><code>/src/pages/index.tsx</code> 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter17/src/pages/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter17/src/pages/index.tsx</a></p>
<p>&nbsp;</p>
<h2 id="18-isr-2-주문형-재-검증on-demand-isr">18. ISR #2. 주문형 재 검증(On-Demand-ISR)</h2>
<h3 id="1-주문형-재-검증on-demand-isr">1) 주문형 재 검증(On-Demand-ISR)</h3>
<p>ISR의 한계</p>
<ul>
<li>만약 시간과 관계 없이 사용자의 행동에 따라 데이터가 업데이트 되는 페이지가 있다면?
→ 기존의 ISR 방식은 사용하기가 어려움</li>
<li>만약 사용자의 수정은 하루 뒤에 일어났는데 웹사이트는 60초 단위로 갱신된다면?
→ 갱신될 필요가 없는 상황에서도 갱신이 이루어짐</li>
</ul>
<p>주문형 재 검증</p>
<ul>
<li>요청을 받을 때마다 페이지를 다시 생성하는 ISR 방식<img src="https://velog.velcdn.com/images/onion_0/post/9a14c024-2704-46eb-b473-a834d5c31cb8/image.png">

</li>
</ul>
<h3 id="2-실습-진행을-위한-환경-세팅-2">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run start</code>로 프로덕션 서버 열기</p>
</li>
<li><p>vscode로 <code>onebite-books-server</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run start</code>로 백엔드 서버 열기</p>
</li>
</ul>
<h3 id="3-isr-동작하도록-수정-1">3) ISR 동작하도록 수정</h3>
<p><code>/src/pages/index.tsx</code> 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter18/src/pages/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter18/src/pages/index.tsx</a></p>
<p>&nbsp;</p>
<h2 id="19-seo-설정하기">19. SEO 설정하기</h2>
<p>지금까지 제작한 한입북스 서비스를 실제로 배포하기 위해, 사전 준비 단계로 페이지 별로 다양한 메타 태그들을 설정해서 아주 기본적인 SEO를 설정하기
= 아주 기본적인 검색엔진 최적화</p>
<h3 id="1-실습-진행을-위한-환경-세팅-3">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>수업 자료로 올려주신 파일 다운로드 후 압축 해제
→ 썸네일 파일과 파비콘 파일을 <code>/public/</code> 폴더 아래에 삽입
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/tree/main/section02/chapter19/public">https://github.com/onebite-nextjs/vod__onebite-next-codes/tree/main/section02/chapter19/public</a></p>
</li>
<li><p>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 개발 서버 열기</p>
</li>
</ul>
<h3 id="2-페이지별-메타-태그를-설정해서-페이지별로-seo-설정">2) 페이지별 메타 태그를 설정해서 페이지별로 SEO 설정</h3>
<ul>
<li><p><code>/src/pages/index.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter19/src/pages/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter19/src/pages/index.tsx</a></p>
</li>
<li><p><code>/src/pages/search/index.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter19/src/pages/search/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter19/src/pages/search/index.tsx</a></p>
</li>
<li><p><code>/src/pages/book/[id].tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter19/src/pages/book/%5Bid%5D.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter19/src/pages/book/[id].tsx</a>
페이지가 로딩 중인 상황이라도(fallback 상태일 지라도) 기본적인 메타 태그들은 설정되도록 코드를 짜야 함</p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="20-배포하기">20. 배포하기</h2>
<p><em>나는 코드 다시 수정해서 재시도 해보기...</em></p>
<h3 id="1-배포">1) 배포</h3>
<p>프론트엔드 어플리케이션을 배포할 때는 클라우드 서비스를 통해 배포하는 경우가 많음
→ 파이어베이스, 넷니파이 등등...</p>
<p>Next.js는 보통 Vercel을 통해 배포함</p>
<h3 id="2-배포를-위한-기본-설정">2) 배포를 위한 기본 설정</h3>
<ul>
<li>Vercel 사이트(<a href="https://vercel.com/)%EC%97%90%EC%84%9C">https://vercel.com/)에서</a> 회원가입 하기
→ 나는 github으로 연동</li>
<li>vscode의 터미널에서 Vercel에 명령어 한번만으로 Next.js 앱을 빌드할 수 있는 도구 설치
<code>npm install -g vercel</code> 실행</li>
<li>vscode의 터미널에서 Vercel에 로그인
→ github에 갖다대서 enter 누르고 진행</li>
<li>배포를 위해, vscode의 터미널에서 <code>vercel</code> 명령어 입력
→ 현재 프로젝트 폴더를 배포할 거냐는 질문에 대해 <code>y</code> 입력
→ 어떤 scope를 선택해서 배포할 거냐는 질문에 뜨는 프로젝트를 선택하고 엔터 입력
→ 기존에 존재하는 프로젝트에 연결할 거냐는 질문에 <code>n</code> 입력
→ 새로 생성할 프로젝트 이름은 뭐냐는 질문에 <code>onebite-books-page</code>로 입력
→ 코드가 위치할 디렉토리 위치는 어디냐는 질문에 자동으로 설정된 기본값 그대로 하기 위해 바로 엔터 누르기
→ 자동으로 설정된 옵션들을 바꾸고 싶냐는 질문에 <code>n</code> 입력
→ 추가 프로젝트 옵션을 바꾸고 싶냐는 질문에 <code>n</code> 입력
→ 배포 진행중
→ 배포 진행 완료 후 뜨는 링크를 통해 들어가면 확인 가능</li>
<li>이때, 백엔드 서버는 안 열려 있기 때문에 DB에서 데이터를 불러오지 못하고 있음
→ <code>/onebite-books-server/</code>로 새 창을 하나 열어서 vscode 터미널을 새로 열기
→ 터미널에서 <code>vercel</code> 입력
→ 현재 프로젝트 폴더를 배포할 거냐는 질문에 대해 <code>y</code> 입력
→ 어떤 scope를 선택해서 배포할 거냐는 질문에 뜨는 프로젝트를 선택하고 엔터 입력
→ 기존에 존재하는 프로젝트에 연결할 거냐는 질문에 <code>n</code> 입력
→ 새로 생성할 프로젝트 이름은 뭐냐는 질문에 기본값으로 설정된 걸로 진행을 위해 엔터 입력
→ 코드가 위치할 디렉토리 위치는 어디냐는 질문에 자동으로 설정된 기본값 그대로 하기 위해 바로 엔터 누르기
→ 바로 배포 작업 진행
→ 배포 완료</li>
<li>백엔드 서버 도메인 확인 후 코드 수정 
→ vercel에서 배포한 백엔드 서버(onebite-books-server)에 들어가서 Production Deployment에서 &#39;Domains&#39; 값을 복사
→ <code>src/lib/</code> 아래에 있는 fetch 파일에 각각 백엔드 서버로부터 데이터를 불러오는 함수의 url을 수정</li>
<li>수정사항을 반영하기 위한 재배포 진행
→ vscode에서 <code>section2/</code>를 기준으로 폴더 열고 터미널 열기
→ 즉시 프로덕션 모드로 배포하기 위해 <code>vercel --prod</code> 명령어 실행
→ 배포 진행 후 완료</li>
</ul>
<p>&nbsp;</p>
<h2 id="21-page-router-정리">21. Page Router 정리</h2>
<h3 id="1-page-router의-장점">1) Page Router의 장점</h3>
<ol>
<li><p>파일 시스템 기반의 간편한 페이지 라우팅 제공</p>
<ul>
<li>정적 경로와 동적 경로 모두 손쉽게 라우팅 가능<img src="https://velog.velcdn.com/images/onion_0/post/73335789-0a63-4fbf-95c9-708ae00b8d35/image.png">
</li>
</ul>
</li>
<li><p>다양한 방식의 사전 렌더링 제공</p>
<ul>
<li>기존의 리액트 앱이 갖던 느린 FCP라는 단점을 효과적으로 해결</li>
<li>방식: SSR, SSG, ISR</li>
</ul>
</li>
</ol>
<h3 id="2-page-router의-단점">2) Page Router의 단점</h3>
<ol>
<li><p>페이지별 레이아웃 설정이 번거롭다</p>
<ul>
<li>추후 App Router 버전에서는 파일 하나만 배치하는 것만으로도 페이지별 레이아웃 설정 가능</li>
</ul>
</li>
<li><p>데이터 페칭이 데이터 컴포넌트에 집중된다</p>
</li>
<li><p>불필요한 컴포넌트들도 JS Bundle에 포함된다</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[강의] 한 입 크기로 잘라먹는 Next.js #3. (1)]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-3</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-3</guid>
            <pubDate>Tue, 12 Aug 2025 03:40:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="page-router-핵심-정리">Page Router 핵심 정리</h1>
<h2 id="1-page-router를-소개합니다">1. Page Router를 소개합니다</h2>
<h3 id="1-page-router">1) Page Router</h3>
<p>현재 많은 기업에서 사용되고 있는 안정적인 라우터
React Router처럼 페이지 라우팅 기능 제공</p>
<p><code>Pages</code>라는 이름의 폴더를 기준으로 페이지를 라우팅</p>
<ul>
<li><code>Pages</code> 이름의 폴더 아래에 JS 파일이 있다면 파일의 경로와 이름에 따라 페이지 라우팅 제공</li>
<li>파일명 기반의 페이지 라우팅 가능<img src="https://velog.velcdn.com/images/onion_0/post/b0dc62e5-93bd-4d61-a20a-522b4e186aba/image.png"></li>
<li>폴더명 기반의 페이지 라우팅도 가능<img src="https://velog.velcdn.com/images/onion_0/post/32bdafdf-5026-49a4-ad4a-a6d8681643fa/image.png"></li>
<li>동적 경로를 갖는 페이지의 라우팅도 간단히 설정 가능
: 파일명을 대괄호([])로 묵어주게 되면 자동으로 동적 경로로 인식하여 매핑 가능<img src="https://velog.velcdn.com/images/onion_0/post/48f4d13c-e808-4a3f-926e-a65cf94730ec/image.png">
> 동적 경로(Dynamic Routes): 경로 상에 어떠한 가변적인 값을 포함하고 있는 경로


</li>
</ul>
<h3 id="2-page-router-버전의-next-app-생성하기">2) Page Router 버전의 Next App 생성하기</h3>
<h4 id="a-디렉토리-생성-및-실행">a. 디렉토리 생성 및 실행</h4>
<ol>
<li><p>원하는 위치에 프로젝트 폴더 생성
나의 폴더 위치: <code>.../onebite-next/</code></p>
</li>
<li><p>vscode로 열기
vscode를 열어서 생성한 폴더 열기</p>
</li>
<li><p>터미널에서 section2 폴더 생성
vscode에서 터미널을 열어서 <code>npx create-next-app@14 section02</code> 명령어 실행
→ <code>Ok to proceed</code>에서 <code>y</code> 입력
→ 타입스크립트를 사용할 거냐는 질문에 <code>Yes</code> 선택 후 엔터
→ ESLint를 사용할 거냐는 질문에 <code>Yes</code> 선택 후 엔터
→ Tailwind CSS를 사용할 거냐는 질문에 <code>No</code> 선택 후 엔터
→ src/ 디렉토리를 사용할 거냐는 질문에 <code>Yes</code> 선택 후 엔터
→ App Router를 사용할 거냐는 질문에 <code>No</code> 선택 후 엔터
→ import alias를 사용할 거냐는 질문에 <code>No</code> 선택 후 엔터</p>
</li>
</ol>
<blockquote>
<p>import alias: 임포트 문을 사용할 때 절대 경로를 사용해서 값을 불러올 수 있게 하는 그러한 기능</p>
</blockquote>
<ul>
<li><code>npx</code> 명령어<ul>
<li>node-package-execute</li>
<li>npmjs.com에 실제로 올라가 있는 등록되어 있는 라이브러리들의 최신 버전의 노드 패키지를 다운로드 없이 바로 실행</li>
</ul>
</li>
<li><code>create-next-app</code> 명령어<ul>
<li>Next.js 공식 문서에서 안내하고 있는 새로운 Next.App을 생성하는 노드.js 패키지</li>
</ul>
</li>
<li><code>@14</code> 명렁어<ul>
<li>현재 설치하려는 Next App을 14 버전으로 설치하겠다는 뜻</li>
<li>현재 가장 최신 버전과는 약간 차이가 있지만, 페이지 라우터에 존재하는 모든 기능들은 14 버전에서 안정적으로 모두 다 사용 가능</li>
</ul>
</li>
</ul>
<ol start="4">
<li>개발 모드로 넥스트 앱 실행
생성된 section02 폴더로 vscode에서 다시 열기
→ vscode의 터미널 열기
→ <code>npm run dev</code> 명령어 실행
→ <a href="http://localhost:3000/">http://localhost:3000/</a> 로 이동하면 확인 가능
→ (종료 시에는) <code>Ctrl+D</code>나 <code>Ctrl+C</code> 입력</li>
</ol>
<h4 id="b-pages-폴더-살펴보기">b. pages 폴더 살펴보기</h4>
<ol>
<li><p>Pages 폴더의 <code>index.tsx</code> 확인
Home 컴포넌트가 사용되고 있는 것을 확인 가능</p>
</li>
<li><p><code>/src/pages/test.tsx</code> 파일 새로 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter01/src/pages/test.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter01/src/pages/test.tsx</a>
→ <a href="http://localhost:3000/test">http://localhost:3000/test</a> 로 이동하면 화면에 TEST가 출력되는 것을 확인 가능</p>
</li>
<li><p>Pages 폴더의 <code>_app.tsx</code>, <code>_document.tsx</code> 확인
Pages 폴더 밑에 있긴 하지만 페이지 역할을 하는 파일은 아님
Next.js 앱의 모든 페이지에 공통적으로 적용될 로직이나 공통적으로 적용될 레이아웃 또는 데이터를 다루기 위해 필요한 파일</p>
</li>
</ol>
<ul>
<li><code>_app.tsx</code><ul>
<li>리액트에서의 App 컴포넌트와 동일한 역할</li>
<li>모든 컴포넌트들의 부모 컴포넌트 역할</li>
<li>모든 페이지 역할을 하는 컴포넌트들의 부모 컴포넌트 역할 수행</li>
<li>아래 코드에서
<code>Component</code> 매개변수: 현재 페이지 역할을 할 컴포넌트를 받음
<code>pageProps</code> 매개변수: <code>Component</code> 매개변수에게 전달될 페이지의 프롭스들을 모두 객체로 보관한 것<pre><code>export default function App({ Component, pageProps }: AppProps) {
return (
  &lt;&gt;
    &lt;header&gt;글로벌 헤더&lt;/header&gt;
    &lt;Component {...pageProps} /&gt;
  &lt;/&gt;
);
}</code></pre></li>
</ul>
</li>
<li><code>_document.tsx</code><ul>
<li>모든 페이지에 공통적으로 적용되어야 하는 Next.js 앱의 HTML 코드를 설정하는 컴포넌트</li>
<li>리액트 앱의 index.html과 비슷한 역할</li>
</ul>
</li>
</ul>
<ol start="4">
<li><p><code>next.config.mjs</code> 파일
넥스트 앱의 설정을 관리하는 파일
기본적으로 <code>reatStrictMode</code>가 <code>true</code>로 켜져 있는데, <code>false</code>로 수정 (실습시 디버깅 용이함을 위해)</p>
<pre><code>/** @type {import(&#39;next&#39;).NextConfig} */
const nextConfig = {
 reactStrictMode: false,
};

export default nextConfig;</code></pre></li>
</ol>
<p>&nbsp;</p>
<h2 id="2-페이지-라우팅-설정하기">2. 페이지 라우팅 설정하기</h2>
<h3 id="1-앞으로-구현할-프로젝트">1) 앞으로 구현할 프로젝트</h3>
<p>한입 북스(Onebite Books) 프로젝트</p>
<ul>
<li>Index 페이지<ul>
<li>인덱스 경로(/)에 해당하는 페이지</li>
<li>등록된 도서의 리스트를 볼 수 있음</li>
</ul>
</li>
<li>Search 페이지<ul>
<li>서치 경로(/search)에 해당하는 페이지</li>
<li>제목, 저자 별로 검색 결과를 볼 수 있음</li>
</ul>
</li>
<li>Book 페이지<ul>
<li>/book/{book_id} 경로의 페이지</li>
<li>특정 도서의 상세 정보 확인 가능</li>
</ul>
</li>
</ul>
<h3 id="2-실습-진행을-위한-환경-세팅">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 서버 열기</p>
</li>
<li><p><code>src/pages/index.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter02/src/pages/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter02/src/pages/index.tsx</a></p>
</li>
<li><p><code>src/styles/global.css</code> 코드 수정
전부 삭제</p>
</li>
<li><p><code>src/styles/Home.module.css</code> 코드 파일 삭제</p>
</li>
</ul>
<h3 id="3-검색을-위한-페이지-생성">3) 검색을 위한 페이지 생성</h3>
<ul>
<li><code>src/pages/search/index.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter02/src/pages/search/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter02/src/pages/search/index.tsx</a></li>
</ul>
<h3 id="-쿼리-스트링-설정">+ 쿼리 스트링 설정</h3>
<ul>
<li><p>경로의 끝에 물음표와 함께 명시되는 값이므로 페이지 경로 자체에는 영향을 주지 않음
= 폴더 구조를 따로 변경해야 하지는 않음</p>
</li>
<li><p>단, 사용을 위해서는 파일의 최상단에 useRouter라는 훅을 Next Router라는 패키지로부터 불러오면 됨
<code>import { useRouter } from &quot;next/router&quot;;</code></p>
</li>
<li><p>Next.js는 전달한 쿼리 스트링을 읽는 과정 중에서 컴포넌트를 한 번 더 렌더링 함
→ 한 번 더 렌더링 할 때 쿼리 스트링을 읽어옴</p>
</li>
<li><p>쿼리 스트링을 읽는 방법
<code>const { q } = router.query;</code>
<code>const { id } = router.query;</code></p>
</li>
</ul>
<h3 id="4-상세한-정보-조회를-위한-페이지-생성">4) 상세한 정보 조회를 위한 페이지 생성</h3>
<ul>
<li><p>북 페이지는 가변적인 값을 포함하는 동적 경로를 사용
→ url 파라미터를 사용해서 동적 경로를 갖는 페이지 생성</p>
</li>
<li><p><code>src/pages/book/[[...id]].tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter02/src/pages/book/%5B%5B...id%5D%5D.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter02/src/pages/book/%5B%5B...id%5D%5D.tsx</a></p>
</li>
<li><ul>
<li>이때 코드를 <code>[...idx].tsx</code>라고 설정해두면 실제 웹에서 <a href="http://localhost:3000/book/123/123/123/123">http://localhost:3000/book/123/123/123/123</a> 과 같이 접속하게 될 경우, 모든 id에 다 대응할 수 있음
= 이런 코드를 <strong>Catch All Segment</strong>라고 부름</li>
<li>이때 파일명을 <code>[[...idx]]tsx</code>라고 설정해두면 실제 웹에서 <a href="http://localhost:3000/book">http://localhost:3000/book</a> 과 같이 접속하게 될 경우에도 404 에러가 뜨지 않고 접속 가능
= 이런 코드를 <strong>Optional Catch All Segment</strong>라고 부름</li>
</ul>
</li>
</ul>
<h3 id="5-존재하지-않는-경로로-접속했을-때-트는-페이지-생성">5) 존재하지 않는 경로로 접속했을 때 트는 페이지 생성</h3>
<ul>
<li><code>src/pages/404.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter02/src/pages/404.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter02/src/pages/404.tsx</a></li>
</ul>
<p>&nbsp;</p>
<h2 id="3-네비게이팅">3. 네비게이팅</h2>
<p>Key Point: 어떻게 하면 클라이언트 사이드 렌더링 방식으로 페이지를 이동시킬 수 있는가?</p>
<h3 id="1--실습-진행을-위한-환경-세팅">1)  실습 진행을 위한 환경 세팅</h3>
<ul>
<li>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 서버 열기</li>
</ul>
<h3 id="2-네비게이션-바-생성">2) 네비게이션 바 생성</h3>
<ul>
<li><p><code>src/pages/_app.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter03/src/pages/_app.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter03/src/pages/_app.tsx</a></p>
</li>
<li><p>Next.js 앱에서는 링크를 만들 때 <code>&lt;a&gt;</code> 태그보다 자체적으로 제공하는 링크 컴포넌트를 이용하는 것이 훨씬 좋음</p>
<ul>
<li><code>&lt;a&gt;</code> 태그는 클라이언트 사이드 렌더링 방식
= 페이지를 이동시키는 게 아닌 일반적인 방식으로 서버에게 새로운 페이지를 매번 다시 요청 
= 비교적 느린 방식</li>
</ul>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="4-프리페칭pre-fetching">4. 프리페칭(Pre-Fetching)</h2>
<h3 id="1-pre-fetching">1) Pre-Fetching...?</h3>
<p>&quot;사전에 미리 불러온다&quot;</p>
<p>Next.js의 프리페칭</p>
<img src="https://velog.velcdn.com/images/onion_0/post/dd58bf07-bf65-47b5-ba9e-4f023669b2e2/image.png">

<ul>
<li>현재 사용자가 보고 있는 페이지 내에서 이동할 가능성이 있는 모든 페이지들을 사전에 미리 불러놓는 기능</li>
<li>사용자들이 다른 페이지로 이동할 때, 페이지 이동을 매운 빠른 속도로 지체 없이 처리해주기 위한 것</li>
</ul>
<br>

<p>▼ 아래의 동작 과정은 현재 페이지에 대해서만 동작하는 것</p>
<img src="https://velog.velcdn.com/images/onion_0/post/777a68c8-fc91-4d32-a5c7-f880c835382f/image.png">


<p>▼ 최종 동작 과정</p>
<img src="https://velog.velcdn.com/images/onion_0/post/130163a6-9987-4e0f-b786-c27a0261d2b7/image.png">

<br>

<p>웹에서 프리패칭 확인 방법</p>
<ul>
<li><code>F12 - 네트워크</code>를 통해 페이지를 이동시키면 불러오는 JS 코드들을 확인 가능</li>
</ul>
<h3 id="2-실습-진행을-위한-환경-세팅-1">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li><p>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run build</code>로 서버 열기</p>
</li>
<li><p><code>npm run dev</code>로 개발 모드를 가동해놓고 있을 때에는 프리패칭 동작 X
→ 프리패칭 동작을 확인하려면 빌드에서 실행하는 프로덕션 모드로 실행 시켜야 함</p>
</li>
</ul>
<h3 id="3-링크-컴포넌트가-아니어도-프리패칭-가능하도록-수정">3) 링크 컴포넌트가 아니어도 프리패칭 가능하도록 수정</h3>
<p><code>/src/pages/_app.tsx</code> 코드에서와 같이 <code>&lt;Link&gt;</code> 컴포넌트로 명시된 경로가 아니라면 프리패칭이 이루어지지 않음</p>
<p>그렇다면 어떻게 해야 하는가?
→ 앱 컴포넌트가 화면에 마운트 되었을 때, 라우터 객체의 특정 메서드를 통해 프로그래메틱하게 테스트 페이지를 프리매칭 하도록 코드 작성하면 됨</p>
<ul>
<li><code>/src/pages/_app.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter04/src/pages/_app.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter04/src/pages/_app.tsx</a></li>
</ul>
<h3 id="4-프리패칭이-자동으로-가능하더라도-기능하지-않도록-수정">4) 프리패칭이 자동으로 가능하더라도 기능하지 않도록 수정</h3>
<p>▼ 아래와 같이 수정하면 됨</p>
<pre><code>&lt;Link href={&quot;/search&quot;} prefetch={false}&gt;
    search
&lt;/Link&gt;</code></pre><p>&nbsp;</p>
<h2 id="5-api-routes">5. API Routes</h2>
<h3 id="1-api-routes">1) API Routes...?</h3>
<p>Next.js에서 API를 구축할 수 있게 해주는 기능</p>
<blockquote>
<p>API Routes 공식 문서
<a href="https://nextjs.org/docs/pages/building-your-application/routing/api-routes">https://nextjs.org/docs/pages/building-your-application/routing/api-routes</a></p>
</blockquote>
<p>사실상 자주 사용되는 기능은 아니기 때문에... 우선 있다 정도로만 알고 있기</p>
<h3 id="2-실습-진행을-위한-환경-세팅-2">2) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 서버 열기</li>
</ul>
<h3 id="3-api-동작-폴더-확인">3) API 동작 폴더 확인</h3>
<ul>
<li><code>/src/pages/api/</code> 아래에서 동작하는 파일은...<ul>
<li>웹페이지가 아닌 API 라우터로써, 즉 API 응답을 정의하는 파일로써 자동으로 설정</li>
<li>API의 경로는 폴더 구조에 맞춰서 갖게 됨</li>
</ul>
</li>
</ul>
<h3 id="4-자체-api-제작">4) 자체 API 제작</h3>
<ul>
<li><code>/src/pages/api/time.ts</code>  코드 생성<ul>
<li>현재 시간을 반환하는 API</li>
<li><a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter05/src/pages/api/time.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter05/src/pages/api/time.ts</a></li>
</ul>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="6-스타일링">6. 스타일링</h2>
<h3 id="1-실습-진행을-위한-환경-세팅">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 서버 열기</li>
</ul>
<h3 id="2-nextjs-앱의-스타일링">2) Next.js 앱의 스타일링</h3>
<ul>
<li><p>리액트의 컴포넌트 스타일을 설정하는 과정과 동일</p>
</li>
<li><p>앱 컴포넌트가 아니라면 css 파일을 그대로 불러오는 것은 제한</p>
</li>
<li><p><em>WHY?*</em> css 충돌 방지를 위해</p>
</li>
<li><p>그렇다면 css는 어떻게 불러와야 할까?
→ Next.js가 기본적으로 제공하는 <strong>CSS 모듈</strong> 기능 활용 가능 
: 기존의 css 파일을 마치 모듈처럼 사용할 수 있도록 돕는 기능
: CSS 파일에 작성해둔 클래스 네임들이 다른 css 파일과 중복되지 않도록 자동으로 클래스 네임들을 유니크한 이름으로 파일마다 변환시켜주는 기능</p>
</li>
</ul>
<h3 id="3-스타일링-설정">3) 스타일링 설정</h3>
<ul>
<li><p><code>/src/pages/index.tsx</code> 파일의 컴포넌트 스타일 지정</p>
<ul>
<li>인라인 스타일로 지정
: <code>index.tsx</code>에서 직접 <code>&lt;style&gt;</code> 태그를 통해 설정하기</li>
<li>css 파일을 별도로 만들어서 분리하여 지정
: css 파일 생성
: 생성한 css 파일은 <code>index.tsx</code>에서 불러오기</li>
</ul>
</li>
<li><p><code>/src/pages/index.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter06/src/pages/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter06/src/pages/index.tsx</a></p>
</li>
<li><p><code>/src/pages/index.module.css</code> 파일 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter06/src/pages/index.module.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter06/src/pages/index.module.css</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="7-글로벌-레이아웃-설정하기">7. 글로벌 레이아웃 설정하기</h2>
<h3 id="1-실습-진행을-위한-환경-세팅-1">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 서버 열기</li>
</ul>
<h3 id="2-완성본-데모-살펴보기">2) 완성본 데모 살펴보기</h3>
<blockquote>
<p>완성본 데모 사이트
<a href="https://onebite-books-page-woad.vercel.app/">https://onebite-books-page-woad.vercel.app/</a></p>
</blockquote>
<h3 id="3-글로벌-레이아웃-설정">3) 글로벌 레이아웃 설정</h3>
<p>글로벌 레이아웃 설정을 위해서는 Next.js 앱의 모든 페이지의 부모 컴포넌트 역할을 하는 루트 컴포넌트인 <code>_app.tsx</code> 컴포넌트의 레이아웃을 적용시켜주면 됨</p>
<ul>
<li><p>글로벌 레이아웃을 설정하는 컴포넌트 추가해서 코드 분리</p>
<ul>
<li><code>/src/components/global-layout.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter07/src/components/global-layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter07/src/components/global-layout.tsx</a></li>
<li><code>/src/components/global-layout.css</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter07/src/components/global-layout.module.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter07/src/components/global-layout.module.css</a></li>
</ul>
</li>
<li><p><code>/src/styles/globals.css</code> 코드 수정 -  백그라운드 스타일 지정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter07/src/styles/globals.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter07/src/styles/globals.css</a></p>
</li>
<li><p><code>/src/pages/_app.tsx</code> 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter07/src/pages/_app.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter07/src/pages/_app.tsx</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="8-페이지별-레이아웃-설정하기">8. 페이지별 레이아웃 설정하기</h2>
<h3 id="1-실습-진행을-위한-환경-세팅-2">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 서버 열기</li>
</ul>
<h3 id="2-완성본-데모-살펴보기-1">2) 완성본 데모 살펴보기</h3>
<blockquote>
<p>완성본 데모 사이트
<a href="https://onebite-books-page-woad.vercel.app/">https://onebite-books-page-woad.vercel.app/</a></p>
</blockquote>
<h3 id="3-페이지별-레이아웃-설정">3) 페이지별 레이아웃 설정</h3>
<p>일부 페이지에만 존재하는 레이아웃을 설정하는 방법 알아보기</p>
<p>당연하게도 일부 페이지에만 존재하려면 <code>/src/pages/_app.tsx</code>에 컴포넌트를 추가하면 안 됨
→ 제작한 컴포넌트가 추가되길 원하는 페이지의 파일에 (자체 제작한) <code>getLayout()</code> 메서드를 추가해서 활용</p>
<blockquote>
<p>함수와 메서드 관련 보충 자료
<a href="https://reactjs.winterlood.com/0f33b159-6b19-433b-8db4-68d6b4a122e0">https://reactjs.winterlood.com/0f33b159-6b19-433b-8db4-68d6b4a122e0</a></p>
</blockquote>
<h4 id="a-검색-레이아웃">a. 검색 레이아웃</h4>
<ul>
<li><p>검색 레이아웃 틀 생성: <code>src/components/searchable-layout.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter08/src/components/searchable-layout.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter08/src/components/searchable-layout.tsx</a></p>
</li>
<li><p>검색 레이아웃 디자인 생성: <code>src/components/searchable-layout.css</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter08/src/components/searchable-layout.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter08/src/components/searchable-layout.css</a></p>
</li>
<li><p>생성한 검색 레이아웃 틀을 원하는 페이지에 적용: <code>/src/pages/index.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter08/src/pages/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter08/src/pages/index.tsx</a></p>
</li>
<li><p>생성한 검색 레이아웃 틀을 원하는 페이지에 적용: <code>/src/pages/search/index.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter08/src/pages/search/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter08/src/pages/search/index.tsx</a></p>
</li>
<li><p>루트 컴포넌트 수정: <code>src/pages/_app.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter08/src/pages/_app.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter08/src/pages/_app.tsx</a></p>
</li>
</ul>
<p>&nbsp;</p>
<h2 id="9-한입북스-ui-구현하기">9. 한입북스 UI 구현하기</h2>
<p><em>다시 듣기</em></p>
<h3 id="1-실습-진행을-위한-환경-세팅-3">1) 실습 진행을 위한 환경 세팅</h3>
<ul>
<li>vscode로 <code>section02</code> 폴더 열기
→ vscode 터미널을 열어서 <code>npm run dev</code>로 서버 열기</li>
</ul>
<h3 id="2-인덱스-페이지-구현하기">2) 인덱스 페이지 구현하기</h3>
<ul>
<li><p><code>/src/pages/index.tsx</code> 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/pages/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/pages/index.tsx</a></p>
</li>
<li><p><code>/src/pages/index.module.css</code> 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/pages/index.module.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/pages/index.module.css</a></p>
</li>
<li><p>도서 아이템 리스팅을 위한 코드 생성: <code>src/components/book-item.tsx</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/components/book-item.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/components/book-item.tsx</a></p>
</li>
<li><p><code>src/components/book-item.tsx</code>에 필요한 데이터 타입을 별도로 생성: <code>/src/types.ts</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/types.ts">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/types.ts</a></p>
</li>
<li><p>도서 아이템 리스팅의 디자인 코드 생성: <code>src/components/book-item.module.css</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/components/book-item.module.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/components/book-item.module.css</a></p>
</li>
<li><p>mock 데이터 생성: <code>src/mock/books.json</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/mock/books.json">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/mock/books.json</a></p>
</li>
<li><p>검색 기능 동작 구현: <code>src/pages/search/index.tsx</code> 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/pages/search/index.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/pages/search/index.tsx</a></p>
</li>
<li><p>북 페이지 수정: <code>src/pages/book/[id].tsx</code> 파일명 수정 + 코드 수정
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/pages/book/%5Bid%5D.tsx">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/pages/book/[id].tsx</a></p>
</li>
<li><p>북 페이지의 디자인 구현: <code>src/pages/book/[id].module.css</code> 코드 생성
<a href="https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/pages/book/%5Bid%5D.module.css">https://github.com/onebite-nextjs/vod__onebite-next-codes/blob/main/section02/chapter09/src/pages/book/[id].module.css</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[강의] 한 입 크기로 잘라먹는 Next.js #1~2.]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-12</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-Next.js-12</guid>
            <pubDate>Sun, 10 Aug 2025 18:31:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs?srsltid=AfmBOop98SToIvyP4ZLiwetJfX_FMn0dbu097kRsUWsuX9BOUxCY1Ihi</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="1-시작하며">1. 시작하며</h1>
<h2 id="1-강의-소개">1) 강의 소개</h2>
<p>Next.js에서 제공하는 라우터</p>
<ul>
<li>페이지 라우터: Next 초창기부터 제공되어 오던 구 버전의 라우터</li>
<li>앱 라우터: Next 13버전과 함께 처음으로 공개된 신규 라우터 (다양한 신규 기능 제공, 페이지라우터의 단점 개선)</li>
</ul>
<p>&nbsp;</p>
<h1 id="2-nextjs를-소개합니다">2. Next.js를 소개합니다</h1>
<h2 id="1-nextjs는-어떤-목적과-특징을-가지고-있는가">1) Next.js는 어떤 목적과 특징을 가지고 있는가?</h2>
<p>Next.js</p>
<ul>
<li>리액트 전용의 웹 개발 프레임워크</li>
<li>리액트를 기존보다 훨씬 더 강력하고 편하게 더 넓은 범위의 개발에 활용할 수 있도록 다양하고 강력한 기능들을 제공
= React의 확장판</li>
<li>리액트의 확장판</li>
<li>Library가 아닌 Framework → Next.js가 제공하는 라우터를 통해 페이지 라우팅 기능을 구현해야 함</li>
</ul>
<h2 id="2-nextjs-사전렌더링-이해하기">2) Next.js 사전렌더링 이해하기</h2>
<h3 id="a-csrclient-side-rendering">a. CSR(Client Side Rendering)</h3>
<p>React.js 앱의 기본적인 렌더링 방식</p>
<p>장점</p>
<ul>
<li>클라이언트(브라우저)에서 직접 화면을 렌더링 하는 방식</li>
<li>페이지의 이동이 매우 빠르고 쾌적함
= 초기 접속 이후의 페이지 이동이 빠름</li>
</ul>
<p>단점</p>
<ul>
<li>초기 접속 속도가 느리다: 실제 화면이 렌더링 될 때까지 꽤 오랜 시간 소요
= FCP(초기 접속 속도)가 꽤나 느리다</li>
</ul>
<br>

<p>▼ React의 렌더링 방식
<img src="https://velog.velcdn.com/images/onion_0/post/26d281a9-9bf7-40be-8665-6721655bd6a5/image.png"></p>
<img src="https://velog.velcdn.com/images/onion_0/post/1a097684-eadd-4b82-ac7f-7c3fd5f67157/image.png">

<blockquote>
<p><strong>FCP(First Contentful Paint)</strong><img src="https://velog.velcdn.com/images/onion_0/post/bf677928-43ef-4cbe-aa8f-08c83d57e133/image.png"></p>
</blockquote>
<ul>
<li>3sec 이상일 경우: 이탈률 32% 증가</li>
<li>5sec 이상일 경우: 이탈률 90% 증가</li>
<li>6sec 이상일 경우: 이탈률 106% 증가</li>
<li>10sec 이상일 경우: 이탈률 123% 증가</li>
</ul>
<h3 id="b-사전-렌더링">b. 사전 렌더링</h3>
<p>Next.js를 대표하는 기능 중 하나
브라우저의 요청에 사전에 렌더링이 완료된 HTML을 응답하는 렌더링 방식
React.js의 렌더링 방식인 CSR(Client Side Rendering)의 단점을 효율적으로 해결하는 기술</p>
<img src="https://velog.velcdn.com/images/onion_0/post/3b4d1c3a-4cca-42c3-a4da-c162ced543e0/image.png">

<br>

<img src="https://velog.velcdn.com/images/onion_0/post/bf56c1df-8754-4e0f-abac-a340090eb641/image.png">
▲ 이때에는 인터렉션(상호작용)은 불가

<img src="https://velog.velcdn.com/images/onion_0/post/a7ee7363-bf2c-428b-a12c-d989466a1165/image.png">

<img src="https://velog.velcdn.com/images/onion_0/post/dde1f009-d0f8-4bcd-9771-0857dd2c2120/image.png">

<br>

<p>▼ 전체 실행 과정</p>
<img src="https://velog.velcdn.com/images/onion_0/post/cac97407-ccf5-4b27-9d4e-622cad1b5e91/image.png">

<blockquote>
<p>수화(Hydration): HTML과 JavaScript를 연결하는 과정</p>
</blockquote>
<blockquote>
<p>TTI(Time to Interactive): 상호작용까지 가능해진 시점</p>
</blockquote>
<h3 id="c-사전-렌더링-이후의-페이지-이동-요청-과정">c. 사전 렌더링 이후의 페이지 이동 요청 과정</h3>
<img src="https://velog.velcdn.com/images/onion_0/post/4d89f49b-d9f1-4eb0-ae4b-f82f583ca213/image.png">


<h2 id="3-실습용-백엔드-서버-세팅하기">3) 실습용 백엔드 서버 세팅하기</h2>
<h3 id="a-백엔드-서버-코드-다운로드">a. 백엔드 서버 코드 다운로드</h3>
<p><a href="https://github.com/winterlood/onebite-books-server">https://github.com/winterlood/onebite-books-server</a>
해당 링크에서 download zip으로 다운로드
→ 원하는 폴더의 위치에 압축 해제 (<code>/문서/</code> 폴더 추천)
→ vscode에서 <code>onebite-books-server</code> 폴더 열기</p>
<h3 id="b-백엔드-서버가-사용할-데이터베이스-설정">b. 백엔드 서버가 사용할 데이터베이스 설정</h3>
<p>Supabase 클라우드 서비스를 통해 설정 진행</p>
<p><a href="https://supabase.com/">https://supabase.com/</a> 접속 후 회원가입
→ <code>onebite-books</code> 이름을 가진 프로젝트 생성
→ 생성된 프로젝트 페이지의 <code>connect</code> 버튼을 눌러서 <code>ORMs</code> 메뉴 탭 들어가기
→ Tool이 <code>Prisma</code>로 되어있는지 확인 후, <code>.env.local</code>에 있는 코드 전문 복사
→ vscode에서 <code>onebite-books-server</code> 폴더를 열고 <code>.env</code> 파일을 생성해서 복사한 코드 삽입 후 저장 (이때 코드 내에 <code>[YOUR-PASSWORD]</code>에 DB PW 입력하기)  <strong>공개적으로 노출 금지</strong></p>
<h3 id="c-백엔드-서버-가동을-위한-사전-작업-진행">c. 백엔드 서버 가동을 위한 사전 작업 진행</h3>
<ul>
<li><p>의존성 설치
→ vscode의 <code>onebite-books-server</code> 폴더를 연 상태에서 터미널을 열어서 <code>npm i</code>로 설치</p>
</li>
<li><p>Supabase DB 초기화
→ vscode의 <code>onebite-books-server</code> 폴더를 연 상태에서 터미널을 열어서 <code>npx prisma db push</code> 명령어 실행</p>
</li>
<li><p>생성된 DB 확인
→ Supabase의 프로젝트 페이지에서 테이블 에디터 탭으로 이동
→ 아래 사진과 같이 <code>Book</code>과 <code>Review</code> 테이블 설치 확인 가능</p>
<img src="https://velog.velcdn.com/images/onion_0/post/53960b81-c1cf-4565-a0fe-051f6855752c/image.png"></li>
<li><p>생성된 DB에 랜덤한 초기데이터 삽입
→ vscode의 <code>onebite-books-server</code> 폴더를 연 상태에서 터미널을 열어서 <code>npm run seed</code> 명령어 실행
→ Supabase의 프로젝트 페이지에서 테이블 에디터 탭으로 들어가 2개의 DB를 각각 확인해보면 랜덤한 데이터 삽입된 것을 확인 가능</p>
</li>
<li><p>백엔드 서버 빌드
→ vscode의 <code>onebite-books-server</code> 폴더를 연 상태에서 터미널을 열어서 <code>npm run build</code> 명령어 실행</p>
</li>
<li><p>백엔드 서버 가동
→ vscode의 <code>onebite-books-server</code> 폴더를 연 상태에서 터미널을 열어서 <code>npm run start</code> 명령어 실행
→ <a href="http://localhost:12345/">http://localhost:12345/</a> 로 접근 가능
→ <a href="http://localhost:12345/api">http://localhost:12345/api</a> 에서 API 문서 확인 가능</p>
</li>
<li><p>백엔드 서버 종료
→ vscode의 <code>onebite-books-server</code> 폴더를 연 상태에서 터미널을 열어서 <code>ctrl+C</code> 또는 <code>ctrl+D</code> 실행</p>
</li>
<li><p>백엔드 서버 재가동
→ vscode의 <code>onebite-books-server</code> 폴더를 연 상태에서 터미널을 열어서 <code>npm run start</code> 명령어 실행</p>
</li>
<li><p>백엔드 서버의 데이터 확인 방법
→ vscode의 <code>onebite-books-server</code> 폴더를 연 상태에서 터미널을 열어서 스플릿 아이콘 눌러서 새로운 터미널 생성
→ <code>npx prisma studio</code> 명령어 실행
→ <a href="http://localhost:5555/">http://localhost:5555/</a> 로 접속: 현재 서버가 사용 중인 데이터들을 조회할 수 있는 별도의 페이지로 접속</p>
</li>
</ul>
<h3 id="d-유의사항">d. 유의사항</h3>
<p>Supabase에 일주일 이상 접속을 하지 않는다면 데이터베이스가 중단됨
대시보드 들어가서 <code>Restore project</code> 누르면 10분에서 1시간 이내에 복구 가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[한 입 크기로 잘라먹는 리액트(React.js) #12.]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-12-7rourf3y</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-12-7rourf3y</guid>
            <pubDate>Wed, 06 Aug 2025 11:32:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://reactjs.winterlood.com/">https://reactjs.winterlood.com/</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="context">Context</h1>
<h2 id="1-context란">1. Context란</h2>
<h3 id="1-context">1) Context?</h3>
<p>React Context</p>
<ul>
<li>컴포넌트 간의 데이터를 전달하는 또 다른 방법</li>
<li>기존의 Props가 가지고 있던 단점을 해결할 수 있음 = Props Drilling 해결 가능</li>
</ul>
<p>Props가 가지고 있는 단점이 뭔데?</p>
<ul>
<li>부모에서 자식으로만 값 전달 가능
→ 바로 한 단계 아래의 자식으로만 값 전달이 가능해서, 2단계 아래에 있는 자식 요소에게 값을 다이렉트로 전달하는 것이 불가능 = <strong>Props Drilling</strong></li>
<li><strong>Props Drilling</strong><img src="https://velog.velcdn.com/images/onion_0/post/4d6ff11b-66af-4378-8fad-ec75d28502d0/image.png">

</li>
</ul>
<p>그럼 React Context는 Props Drilling을 어떻게 해결하는데?</p>
<ul>
<li>context에 보관해뒀다가 다이렉트로 전달 가능<img src="https://velog.velcdn.com/images/onion_0/post/52fc1510-8fbe-4be1-8fbf-8275b73bbd09/image.png"></li>
<li>이러한 Context는 여러 개 생성도 가능<img src="https://velog.velcdn.com/images/onion_0/post/979d623b-5957-4906-9344-c782c6ec79d6/image.png">


</li>
</ul>
<h3 id="2-실습-환경-구성">2) 실습 환경 구성</h3>
<p><code>section11/</code> 폴더 생성
→ <code>section10</code>의 <code>node_modules</code>만 제외하고 전부 복붙
→ <code>section11</code>로 vscode에서 다시 열기
→ vscode 터미널 열어서 <code>npm i</code>로 필요한 패키지 전부 설치
→ vscode 터미널에서 <code>npm run dev</code>로 vite 열기</p>
<p>&nbsp;</p>
<h2 id="2-context-사용하기">2. Context 사용하기</h2>
<h3 id="1-context-사용">1) Context 사용</h3>
<p>보통 컴포넌트의 외부에 선언</p>
<ul>
<li>컴포넌트 내부에 선언하게 되면 리렌더링이 될 때마다 새로운 컨텍스트를 생성하게 되는데, 굳이 그럴 필요가 없음</li>
<li>특수한 경우가 아니라면 컨텍스트의 생성은 컴포넌트 외부에 선언</li>
</ul>
<p>Context의 프로퍼티 중...</p>
<ul>
<li><code>Provider</code>만 알아둬도 충분</li>
<li><code>Provider</code><ul>
<li>컨텍스트가 공급할 데이터를 설정하거나, 이 컨텍스트의 데이터를 공급 받을 컴포넌트들을 설정하기 위해 사용하는 props</li>
<li>사실은 컴포넌트... 
→ <code>App.jsx</code>에서 <code>&lt;&gt;</code>로 선언해서 프로바이더 컴포넌트를 사용하고 그 하위에 컴포넌트를 둔다면, 하위에 있는 모든 컴포넌트들은 해당 프로바이더의 데이터를 공급받을 수 있음<pre><code>&lt;TodoContext.Provider
value={{
    todos,
    onCreate,
    onUpdate,
    onDelete,
}}
&gt;</code></pre></li>
</ul>
</li>
</ul>
<h3 id="2-props-drilling-없애기">2) Props Drilling 없애기</h3>
<ul>
<li><code>/src/App.jsx</code> 수정
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section11/chapter02/src/App.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section11/chapter02/src/App.jsx</a></li>
<li><code>src/component/</code> 수정
<a href="https://github.com/winterlood/onebite-react-v2/tree/main/section11/chapter02/src/components">https://github.com/winterlood/onebite-react-v2/tree/main/section11/chapter02/src/components</a></li>
</ul>
<h3 id="3-현재-react-app의-계층-구조">3) 현재 React App의 계층 구조</h3>
<p><img src="https://velog.velcdn.com/images/onion_0/post/a6b451b7-5116-4671-b7ed-2d8479d272e0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/onion_0/post/56245c7d-69cb-4a49-987b-d185e212cabb/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/onion_0/post/73e419d9-dd75-4b99-bc19-c38b949a6c53/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/onion_0/post/9cc6020b-e05e-452b-848d-cfde9f0af3a0/image.png" alt=""></p>
<p>&nbsp;</p>
<h2 id="3-context-분리하기">3. Context 분리하기</h2>
<p><em>마지막 설명 부분 다시 들어보기...</em></p>
<h3 id="1-현재-문제--최적화가-풀려-있음">1) 현재 문제 : 최적화가 풀려 있음</h3>
<ul>
<li>현재 구조<img src="https://velog.velcdn.com/images/onion_0/post/4ae9acf4-2d5d-48bc-a0b7-333323be7671/image.png">


</li>
</ul>
<ul>
<li>TodoContext.Provider도 컴포넌트인데, props가 바뀌게 되면 결국 매번 새롭게 생성하게 되는 것<img src="https://velog.velcdn.com/images/onion_0/post/60c9ca1c-9eb7-4184-8c94-cbe7fe1e7178/image.png">


</li>
</ul>
<ul>
<li>상세 원인
<img src="https://velog.velcdn.com/images/onion_0/post/568b5c6a-12e2-4834-81ea-c0214132563e/image.png"><img src="https://velog.velcdn.com/images/onion_0/post/e0392028-8826-4912-80ec-da0cb90711d2/image.png"></li>
</ul>
<h3 id="2-해결-방법">2) 해결 방법</h3>
<ul>
<li>Context를 분할<img src="https://velog.velcdn.com/images/onion_0/post/e2a41211-91e2-4a5c-b470-31b1b17fd7fd/image.png">


</li>
</ul>
<ul>
<li>상세 분할 구조
<img src="https://velog.velcdn.com/images/onion_0/post/95140470-c5ec-44a1-b02b-32cf9d6dbfad/image.png"><img src="https://velog.velcdn.com/images/onion_0/post/cea639b2-b80a-46e4-b748-9c606d21c756/image.png"></li>
</ul>
<ul>
<li>리렌더링이 된다면?<img src="https://velog.velcdn.com/images/onion_0/post/20b58d19-09ab-4faf-b462-b82e527d34f5/image.png">


</li>
</ul>
<h3 id="3-실습">3) 실습</h3>
<ul>
<li><p>Context 분리: <code>src/App.jsx</code> 수정
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section11/chapter03/src/App.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section11/chapter03/src/App.jsx</a></p>
</li>
<li><p>각 컴포넌트들이 전달 받는 Context 값 수정: <code>src/component/</code> 하위 파일 수정
<a href="https://github.com/winterlood/onebite-react-v2/tree/main/section11/chapter03/src/components">https://github.com/winterlood/onebite-react-v2/tree/main/section11/chapter03/src/components</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[한 입 크기로 잘라먹는 리액트(React.js) #11.]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-11</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-11</guid>
            <pubDate>Wed, 06 Aug 2025 09:26:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://reactjs.winterlood.com/">https://reactjs.winterlood.com/</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="최적화">최적화</h1>
<h2 id="1-최적화란">1. 최적화란</h2>
<p>웹 서비스의 성능을 개선하는 모든 행위
아주 단순한 것부터 아주 어려운 것까지 모두 존재</p>
<h3 id="0-실습-환경-구성">0) 실습 환경 구성</h3>
<p><code>section10/</code> 폴더 생성
→ <code>section09</code>의 <code>node_modules</code>만 제외하고 전부 복붙
→ <code>section10</code>로 vscode에서 다시 열기
→ vscode 터미널 열어서 <code>npm i</code>로 필요한 패키지 전부 설치
→ vscode 터미널에서 <code>npm run dev</code>로 vite 열기</p>
<p>&nbsp;</p>
<h2 id="2-usememo와-연산-최적화">2. useMemo와 연산 최적화</h2>
<h3 id="1-usememo">1) useMemo</h3>
<p><strong>메모이제이션 기법</strong>을 기반으로 불필요한 연산을 최적화 하는 React Hook</p>
<p><strong>메모이제이션 기법</strong>이란</p>
<ul>
<li>기억해두기, 메모해두기</li>
<li>동일한 연산을 반복적으로 수행해야 될 때, 계산 후 결과값을 메모리에 보관해둔 다음 해당 연산이 필요해지면 저장되어 있던 결과값을 바로 돌려주는 기법<img src="https://velog.velcdn.com/images/onion_0/post/1d66ab38-f2c1-45bc-bad7-904d420c9c55/image.png">


</li>
</ul>
<h3 id="2-실습">2) 실습</h3>
<p>최적화 실습을 위해 현재 결과를 분석하는 함수 추가: <code>src/components/List.jsx</code> 수정
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section10/chapter02/src/components/List.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section10/chapter02/src/components/List.jsx</a></p>
<ul>
<li><code>useMemo()</code> 내부에는 메모리에 저장하고 싶은 연산을 넣으면 됨</li>
</ul>
<p>&nbsp;</p>
<h2 id="3-reactmemo와-컴포넌트-렌더링-최적화">3. React.memo와 컴포넌트 렌더링 최적화</h2>
<h3 id="1-reactmemo">1) React.memo</h3>
<ul>
<li>컴포넌트를 인수로 받아 최적화된 컴포넌트로 만들어 반환<img src="https://velog.velcdn.com/images/onion_0/post/493940c4-d6f1-4b4e-9d26-3e5474bd6713/image.png">


</li>
</ul>
<ul>
<li>부모 컴포넌트가 리렌더링 되더라도 자신이 받는 props가 바뀌지 않으면 다시는 리렌더링이 발생하지 않도록 메모이제이션 됨<img src="https://velog.velcdn.com/images/onion_0/post/b9e2d332-1379-4a1d-b010-6570ff50bd92/image.png">


</li>
</ul>
<h3 id="2-실습-1">2) 실습</h3>
<p>불필요한 리렌더링 방지</p>
<ul>
<li><code>/src/components/Header.jsx</code> 수정
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section10/chapter03/src/components/Header.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section10/chapter03/src/components/Header.jsx</a></li>
<li><code>/src/components/TodoItem.jsx</code> 수정
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section10/chapter03/src/components/TodoItem.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section10/chapter03/src/components/TodoItem.jsx</a></li>
</ul>
<p>객체 타입에 해당하는 함수는 컴포넌트가 리렌더링 되면서 새롭게 생성이 되면 주소값도 같이 바뀜
= 매번 다른 값으로 생성된는 것 (이전과 동일한 함수로 취급되지 않음)</p>
<p>memo 메서드는 현재 props와 이전 props를 기준으로 얕은 비교를 하기 때문에 주소값이 바껴서 서로 다른 값이라고 판단하게 되는 것
→ 해결방법</p>
<ul>
<li><p>앱 컴포넌트에서 함수들 자체를 메모이제이션 해서 리렌더링이 되더라도 다시 생성되지 않게 방지 (다음 시간에)</p>
</li>
<li><p>TodoItem 컴포넌트의 메모 함수 안에 두 번째 인수로 콜백 함수를 추가로 전달해서 최적화 기능을 커스터마이징</p>
<pre><code>// 고차 컴포넌트 (HOC)
export default memo(TodoItem, (prevProps, nextProps) =&gt; {
  // 반환값에 따라, Props가 바뀌었는지 안바뀌었지 판단
  // T -&gt; Props 바뀌지 않음 -&gt; 리렌더링 X
  // F -&gt; Props 바뀜 -&gt; 리렌더링 O

  if (prevProps.id !== nextProps.id) return false;
  if (prevProps.isDone !== nextProps.isDone) return false;
  if (prevProps.content !== nextProps.content) return false;
  if (prevProps.date !== nextProps.date) return false;

  return true;
});</code></pre></li>
</ul>
<blockquote>
<p><strong>고차 컴포넌트(Higher Order Component; HOC)</strong>
컴포넌트를 인수로 받아서 해당 컴포넌트에 최적화나 메모이제이션 같은 추가적인 기능을 덧붙여서 기능이 추가된 새로운 컴포넌트를 반환해주는 메모와 같은 메서드들</p>
</blockquote>
<p>&nbsp;</p>
<h2 id="4-usecallback과-함수-재생성-방지">4. useCallback과 함수 재생성 방지</h2>
<h3 id="1-usecallback">1) useCallback</h3>
<p>React.memo를 사용할 때 최적화 커스터마이징을 하는 것보다 실제로 함수가 재생성 되는 것을 방지하는 것이 더 효율적
→ useCallback은 함수가 재생성 되는 것을 막는 기능</p>
<p>useCallback 사용</p>
<ul>
<li>첫번째 인수: 최적화 하고 싶은 함수 (= 불필요하게 재생성되지 않도록 방지하고 싶은 함수)</li>
<li>두번째 인수: depth</li>
</ul>
<p>useCallback 동작 과정</p>
<ul>
<li>기본적으로 첫 번째 인수로 전달한 콜백 함수를 그대로 생성해서 반환</li>
<li>이때 이렇게 생성되는 함수가 depth가 변경되었을 때만 다시 생성
= 함수를 메모이제이션</li>
<li>depth를 빈 배열로 설정한다면 해당 컴포넌트가 최초로 한번 렌더링 될 때에만 함수를 딱 한 번 생성하고 그 뒤에는 리렌더링이 아무리 많이 일어나도 함수를 새롭게 생성하지 않음</li>
</ul>
<h3 id="2-실습-2">2) 실습</h3>
<p><code>/src/App.jsx</code> 수정
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section10/chapter04/src/App.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section10/chapter04/src/App.jsx</a></p>
<h3 id="3-최적화에-대해">3) 최적화에 대해...</h3>
<p>최적화는</p>
<ul>
<li>언제 해야 하는지</li>
<li>어떤 것을 해야 하는지를 고려</li>
</ul>
<p>발생 가능한 문제</p>
<ul>
<li>너무 이른 타이밍에 해도 문제</li>
<li>너무 많은 것을 최적화 하려고 해도 문제</li>
</ul>
<br>

<h4 id="a-그렇다면-최적화는-언제-하는-것이-좋은가">a. 그렇다면 최적화는 언제 하는 것이 좋은가?</h4>
<p>리액트 앱을 최적화 할 때는 하나의 프로젝트가 거의 완성한 상태에서 최적화 진행
= 기능을 구현하는 것이 최우선, 기능이 완성이 되면 그 뒤에 최적화를 진행</p>
<h4 id="b-최적화의-대상은">b. 최적화의 대상은?</h4>
<p>꼭 최적화가 필요할 것 같은 연산들이나 함수들의 컴포넌트에만 최적화를 적용하는 것이 좋음
전부 최적화를 하는 것은 오히려 성능 저하 발생</p>
<p>유저의 행동에 따라서 개수가 굉장히 많아질 수 있는 컴포넌트(Ex. <code>TodoItem.jsx</code>), 함수들을 굉장히 많이 가지고 있어서 코드가 무거운 컴포넌트 등</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[한 입 크기로 잘라먹는 리액트(React.js) #10.]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-10</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-10</guid>
            <pubDate>Wed, 06 Aug 2025 08:19:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://reactjs.winterlood.com/">https://reactjs.winterlood.com/</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="usereducer">useReducer</h1>
<h2 id="1-usereducer를-소개합니다">1. useReducer를 소개합니다.</h2>
<h3 id="0-실습-환경-구성">0) 실습 환경 구성</h3>
<p><code>section09/</code> 폴더 생성
→ <code>section08</code>의 <code>node_modules</code>만 제외하고 전부 복붙
→ <code>section09</code>로 vscode에서 다시 열기
→ vscode 터미널 열어서 <code>npm i</code>로 필요한 패키지 전부 설치
→ vscode 터미널에서 <code>npm run dev</code>로 vite 열기</p>
<h3 id="1-usereducer">1) useReducer?</h3>
<p>컴포넌트 내부에 새로운 State를 생성하는 React Hook
모든 useState는 useReducer로 대체 가능
useState와 달리, 상태 관리 코드를 컴포넌트 외부로 분리 가능</p>
<ul>
<li><p><code>useState</code>는 컴포넌트 내부에서 상태 관리 코드 작성</p>
</li>
<li><p><code>useReducer</code>은 <code>reducer()</code>라는 함수를 통해 컴포넌트 외부에서 상태 관리 코드 분리 가능</p>
</li>
<li><p><code>reducer()</code> 함수는 상태를 실제로 변화시키는 변환기 역할</p>
</li>
<li><p>컴포넌트 내부에서 <code>useReducer()</code>로 정의 시, <code>[state, dispatch]</code> 사용</p>
<ul>
<li>예시 : <code>const [state, dispatch] = useReducer(reducer, 0);</code></li>
<li>이때 <code>dispatch</code>는 상태 변화가 있어야 한다는 사실을 알리는(발송하는) 함수</li>
<li>초기값은 <code>useReducer(reducer, 0)</code>과 같이 0 부분에 초기값을 설정</li>
</ul>
</li>
<li><p>인수로 전달된 요청의 내용을 담고 있는 객체 = <strong>Action 객체</strong></p>
<ul>
<li><p>예시 코드</p>
<pre><code>const onClickPlus = () =&gt; {
// 인수: 상태가 어떻게 변화되길 원하는지
// -&gt; 액션 객체
dispatch({
  type: &quot;INCREASE&quot;,
  data: 1,
});
};

const onClickMinus = () =&gt; {
dispatch({
  type: &quot;DECREASE&quot;,
  data: 1,
});
};</code></pre></li>
</ul>
</li>
</ul>
<h3 id="2-실습">2) 실습</h3>
<h4 id="a-예제-파일-생성">a. 예제 파일 생성</h4>
<p><code>/src/components/Exam.jsx</code> 생성
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section09/chapter01/src/components/Exam.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section09/chapter01/src/components/Exam.jsx</a></p>
<h4 id="b-appjsx-수정">b. App.jsx 수정</h4>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section09/chapter01/src">https://github.com/winterlood/onebite-react-v2/tree/main/section09/chapter01/src</a></p>
<p>&nbsp;</p>
<h2 id="2-투두리스트-업그레이드">2. 투두리스트 업그레이드</h2>
<h3 id="1-usestate를-usereducer로-바꾸기">1) useState()를 useReducer()로 바꾸기</h3>
<p><code>src/App.jsx</code> 수정하기
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section09/chapter02/src/App.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section09/chapter02/src/App.jsx</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[한 입 크기로 잘라먹는 리액트(React.js) #9.]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-9</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-9</guid>
            <pubDate>Wed, 06 Aug 2025 07:38:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://reactjs.winterlood.com/">https://reactjs.winterlood.com/</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="프로젝트-2-투두리스트">프로젝트 #2. 투두리스트</h1>
<h2 id="1-프로젝트-소개-및-준비">1. 프로젝트 소개 및 준비</h2>
<h3 id="0-데모-버전">0) 데모 버전</h3>
<p>강사님이 만드신 데모 버전: <a href="https://chapter7-dun.vercel.app/">https://chapter7-dun.vercel.app/</a></p>
<br>

<h3 id="1-실습-준비">1) 실습 준비</h3>
<ul>
<li><p>vscode에서 <code>/onebite-react/</code> 열기 
→ vscode 터미널 열기 
→ <code>npm create vite@latest</code> 입력 후 엔터 
→ (설치해도 되냐는 질문이 나오면 <code>y</code> 입력) 
→ 프로젝트 이름 설정: <code>section06</code> 누르고 엔터 
→ 프레임워크 선택: <code>React</code> 선택 후 엔터 
→ 어떤 버전의 리액트 앱을 만들 건지 선택: <code>JavaScript</code> 선택
→ 왼쪽에 <code>.../onebite-react/section06</code> 폴더 생성</p>
</li>
<li><p><code>section06</code> 폴더로 vscode에서 다시 열기
→ vscode 터미널 열기
→ <code>npm i</code>로 필요한 패키지 모두 다운로드
→ <code>npm run dev</code> 입력 후 엔터
→ 뜨는 localhost 주소로 이동</p>
</li>
<li><p><code>eslint.config.js</code> 수정</p>
<pre><code>import js from &quot;@eslint/js&quot;;
import globals from &quot;globals&quot;;
import reactHooks from &quot;eslint-plugin-react-hooks&quot;;
import reactRefresh from &quot;eslint-plugin-react-refresh&quot;;

export default [
  { ignores: [&quot;dist&quot;] },
  {
    files: [&quot;**/*.{js,jsx}&quot;],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
      parserOptions: {
        ecmaVersion: &quot;latest&quot;,
        ecmaFeatures: { jsx: true },
        sourceType: &quot;module&quot;,
      },
    },
    plugins: {
      &quot;react-hooks&quot;: reactHooks,
      &quot;react-refresh&quot;: reactRefresh,
    },
    rules: {
      ...js.configs.recommended.rules,
      ...reactHooks.configs.recommended.rules,
      &quot;no-unused-vars&quot;: &quot;off&quot;,
      &quot;react/prop-types&quot;: &quot;off&quot;,
      &quot;react-refresh/only-export-components&quot;: [
        &quot;warn&quot;,
        { allowConstantExport: true },
      ],
    },
  },
];</code></pre></li>
<li><p><code>public/vite.svg</code> 삭제</p>
</li>
<li><p><code>src/assets/react.svg</code> 삭제</p>
</li>
<li><p><code>/src/App.jsx</code> 수정</p>
<pre><code>import { useState } from &#39;react&#39;
import &#39;./App.css&#39;

function App() {
  return (
    &lt;&gt;
      TodoList
    &lt;/&gt;
  )
}

export default App</code></pre></li>
<li><p><code>/src/App.css</code> 전체 내용 삭제</p>
</li>
<li><p><code>/src/index.css</code> 전체 내용 삭제</p>
</li>
<li><p><code>src/assets/main.jsx</code> 코드 수정</p>
<pre><code>import { StrictMode } from &#39;react&#39;
import { createRoot } from &#39;react-dom/client&#39;
import &#39;./index.css&#39;
import App from &#39;./App.jsx&#39;

createRoot(document.getElementById(&#39;root&#39;)).render(
    &lt;App /&gt;
)</code></pre></li>
</ul>
<p>&nbsp;</p>
<h2 id="2-ui-구현하기">2. UI 구현하기</h2>
<h3 id="1-컴포넌트-분리">1) 컴포넌트 분리</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter2/src/components">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter2/src/components</a></p>
<h3 id="2-appjsx-수정">2) App.jsx 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/blob/main/section08/chapter2/src/App.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section08/chapter2/src/App.jsx</a></p>
<h3 id="3-appcss-수정">3) App.css 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/blob/main/section08/chapter2/src/App.css">https://github.com/winterlood/onebite-react-v2/blob/main/section08/chapter2/src/App.css</a></p>
<p>&nbsp;</p>
<h2 id="3-기능-구현-준비하기">3. 기능 구현 준비하기</h2>
<h3 id="1-appjsx-appcss-수정">1) App.jsx, App.css 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter3/src">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter3/src</a></p>
<h3 id="2-컴포넌트-수정">2) 컴포넌트 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter3/src/components">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter3/src/components</a></p>
<p>&nbsp;</p>
<h2 id="4-create---투두-추가하기">4. Create - 투두 추가하기</h2>
<h3 id="1-appjsx-appcss-수정-1">1) App.jsx, App.css 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter4/src">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter4/src</a></p>
<h3 id="2-컴포넌트-수정-1">2) 컴포넌트 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter4/src/components">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter4/src/components</a></p>
<ul>
<li><code>onKeydown</code>에서 <code>keyCode</code>가 13이라면 <code>엔터(Enter) 키</code>를 의미</li>
</ul>
<p>&nbsp;</p>
<h2 id="5-read---투두리스트-렌더링하기">5. Read - 투두리스트 렌더링하기</h2>
<h3 id="1-appjsx-appcss-수정-2">1) App.jsx, App.css 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter5/src">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter5/src</a></p>
<h3 id="2-컴포넌트-수정-2">2) 컴포넌트 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter5/src/components">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter5/src/components</a></p>
<ul>
<li>리액트에서는 내부적으로 리스트 형태로 렌더링 된 컴포넌트들이나 요소들을 서로 구분할 때, 각각의 요소를 Key라는 prop을 통해서 구분
=&gt; 리스트로 컴포넌트를 렌더링 할 때는 모든 아이템 컴포넌트에 반드시 key라는 prop을 고유한 값으로 전달하는 것이 필수!</li>
</ul>
<p>&nbsp;</p>
<h2 id="6-update---투두-수정하기">6. Update - 투두 수정하기</h2>
<h3 id="1-appjsx-appcss-수정-3">1) App.jsx, App.css 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter6/src">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter6/src</a></p>
<h3 id="2-컴포넌트-수정-3">2) 컴포넌트 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter6/src/components">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter6/src/components</a></p>
<p>&nbsp;</p>
<h2 id="7-delete---투두-삭제하기">7. Delete - 투두 삭제하기</h2>
<h3 id="1-appjsx-appcss-수정-4">1) App.jsx, App.css 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter7/src">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter7/src</a></p>
<h3 id="2-컴포넌트-수정-4">2) 컴포넌트 수정</h3>
<p><a href="https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter7/src/components">https://github.com/winterlood/onebite-react-v2/tree/main/section08/chapter7/src/components</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[한 입 크기로 잘라먹는 리액트(React.js) #8.]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-8</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-8</guid>
            <pubDate>Tue, 05 Aug 2025 13:16:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://reactjs.winterlood.com/">https://reactjs.winterlood.com/</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="라이프사이클lifecycle">라이프사이클(LifeCycle)</h1>
<h2 id="1-라이프사이클이란">1. 라이프사이클이란?</h2>
<p>생애 주기: 인간의 탄생부터 죽음까지의 타임라인을 단계별로 나누어 놓은 것</p>
<p>▼ 리액트 컴포넌트의 라이프사이클</p>
<img src="https://velog.velcdn.com/images/onion_0/post/99bcef73-48ad-4035-844b-5c5f55215130/image.png">

<ul>
<li>Mount<ul>
<li>컴포넌트가 탄생하는 순간</li>
<li>화면에 처음 렌더링 되는 순간</li>
<li>&quot;A 컴포넌트가 Mount 되었다!&quot; = &quot;A 컴포넌트가 화면에 처음으로 렌더링 되었다&quot;</li>
</ul>
</li>
<li>Update<ul>
<li>Mount 이후 컴포넌트가 다시 렌더링 되는 순간</li>
<li>리렌더링 될 때를 의미</li>
<li>&quot;A 컴포넌트가 Update 되었다!&quot; = &quot;A 컴포넌트가 화면에 리렌더링 되었다&quot;</li>
</ul>
</li>
<li>UnMount<ul>
<li>컴포넌트가 화면에서 사라지는 순간</li>
<li>렌더링에서 제외되는 순간</li>
<li>&quot;A 컴포넌트가 UnMount 되었다!&quot; = &quot;A 컴포넌트가 화면에서 사라졌다&quot;</li>
</ul>
</li>
</ul>
<p>컴포넌트의 라이프 사이클의 단계별로 컴포넌트들이 각각 다른 작업을 수행하도록 만드는 것 = <strong>라이프 사이클 제어</strong>
→ 라이프 사이클 제어는 useEffect를 이용하면 쉽게 구현 가능</p>
<p>&nbsp;</p>
<h2 id="2-useeffect-사용하기">2. useEffect 사용하기</h2>
<h3 id="1-useeffect란">1) useEffect란?</h3>
<p>useEffect</p>
<ul>
<li>컴포넌트의 사이드 이펙트를 제어하는 새로운 React Hook</li>
<li>Side Effect를 제어하거나 새롭게 만들 수 있음<img src="https://velog.velcdn.com/images/onion_0/post/cb71a7aa-bf5e-4e2a-888f-a5c91c3f4c39/image.png">

</li>
</ul>
<p>근데 사이드 이펙트가 뭐지?</p>
<ul>
<li>사이드 이펙트
: 부수적인 효과, 파생되는 효과
: 컴포넌트의 동작에 따라 파생되는 여러 효과<img src="https://velog.velcdn.com/images/onion_0/post/ce0e3349-9e21-4003-a297-56456aa352a3/image.png">

</li>
</ul>
<br>

<h3 id="2-vscode에서-실습-환경-열기">2) vscode에서 실습 환경 열기</h3>
<ul>
<li><p>vscode에서 <code>/onebite-react/</code> 열기 
→ vscode 터미널 열기 
→ <code>npm create vite@latest</code> 입력 후 엔터 
→ (설치해도 되냐는 질문이 나오면 <code>y</code> 입력) 
→ 프로젝트 이름 설정: <code>section07</code> 누르고 엔터 
→ 프레임워크 선택: <code>React</code> 선택 후 엔터 
→ 어떤 버전의 리액트 앱을 만들 건지 선택: <code>JavaScript</code> 선택
→ 왼쪽에 <code>.../onebite-react/section07</code> 폴더 생성</p>
</li>
<li><p><code>section07</code> 폴더로 vscode에서 다시 열기
→ vscode 터미널 열기
→ <code>npm i</code>로 필요한 패키지 모두 다운로드
→ <code>npm run dev</code> 입력 후 엔터
→ 뜨는 localhost 주소로 이동</p>
</li>
<li><p><code>eslint.config.js</code> 수정</p>
<pre><code>import js from &quot;@eslint/js&quot;;
import globals from &quot;globals&quot;;
import reactHooks from &quot;eslint-plugin-react-hooks&quot;;
import reactRefresh from &quot;eslint-plugin-react-refresh&quot;;

export default [
  { ignores: [&quot;dist&quot;] },
  {
    files: [&quot;**/*.{js,jsx}&quot;],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
      parserOptions: {
        ecmaVersion: &quot;latest&quot;,
        ecmaFeatures: { jsx: true },
        sourceType: &quot;module&quot;,
      },
    },
    plugins: {
      &quot;react-hooks&quot;: reactHooks,
      &quot;react-refresh&quot;: reactRefresh,
    },
    rules: {
      ...js.configs.recommended.rules,
      ...reactHooks.configs.recommended.rules,
      &quot;no-unused-vars&quot;: &quot;off&quot;,
      &quot;react/prop-types&quot;: &quot;off&quot;,
      &quot;react-refresh/only-export-components&quot;: [
        &quot;warn&quot;,
        { allowConstantExport: true },
      ],
    },
  },
];</code></pre></li>
<li><p><code>public/vite.svg</code> 삭제</p>
</li>
<li><p><code>src/assets/react.svg</code> 삭제</p>
</li>
<li><p>나머지 코드는 section06에서 그대로 복붙하기</p>
</li>
</ul>
<br>

<h3 id="3-useeffect-이용하기">3) useEffect 이용하기</h3>
<p><code>useEffect</code>는 두 번째 인수로 전달한 배열에 들어가 있는 값이 바뀌게 되면, SideEffect로서 첫 번째 인수로 전달한 콜백 함수를 실행</p>
<ul>
<li>이때 전달되는 배열을 <strong>의존성 배열(Dependency array; deps)</strong>이라고 부름</li>
<li>deps에는 값을 여러 개 전달할 수 있음</li>
</ul>
<ul>
<li><code>src/App.jsx</code> 파일의 코드 수정<pre><code>import &quot;./App.css&quot;;
import Viewer from &quot;./components/Viewer&quot;;
import Controller from &quot;./components/Controller&quot;;
import { useState, useEffect } from &quot;react&quot;;
</code></pre></li>
</ul>
<p>function App() {
  const [count, setCount] = useState(0);
  const [input, setInput] = useState(&quot;&quot;);</p>
<p>  useEffect(() =&gt;{
    console.log(<code>count: ${count} / input: ${input}</code>);
  }, [count, input]);</p>
<p>  const onClickButton = (value) =&gt; {
    setCount(count + value);
  };</p>
<p>  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        &lt;input value={input} onChange={(e)=&gt; {
          setInput(e.target.value)
        }} /&gt;
      </section>
      <section>
        <Viewer count={count} />
      </section>
      <section>
        <Controller onClickButton={onClickButton} />
      </section>
    </div>
  );
}</p>
<p>export default App;</p>
<pre><code>

- ```setCount()``` 함수는 비동기로 동작


&amp;nbsp;

## 3. useEffect로 라이프사이클 제어하기

### 1) 마운트를 useEffect를 통해 제어</code></pre><p>useEffect(()=&gt;{
  console.log(&quot;mount&quot;)
}, []);</p>
<pre><code>
### 2) 업데이트를 useEffect를 통해 제어</code></pre><p>useEffect(()=&gt; {
  console.log(&quot;update&quot;)
});</p>
<pre><code>
마운트 시점을 제외하고 업데이트 되는 순간에만 콜백함수를 실행하고 싶다면?</code></pre><p>  const isMount = useRef(false);</p>
<p>  useEffect(() =&gt;{
    console.log(<code>count: ${count} / input: ${input}</code>);
  }, [count, input]);</p>
<p>  // 1. 마운트
  useEffect(()=&gt;{
    console.log(&quot;mount&quot;)
  }, []);</p>
<p>  // 2. 업데이트
  useEffect(()=&gt; {
    if(!isMount.current){
      isMount.current = true;
      return;
    }
    console.log(&quot;update&quot;)
  });</p>
<pre><code>
### 3) 언마운트를 useEffect를 통해 제어
- ```src/components/Even.jsx``` 파일 생성</code></pre><p>const Even = () =&gt; {
    return <div>짝수입니다</div>;
};</p>
<p>export default Even;</p>
<pre><code>
- ```src/App.jsx``` 파일 생성</code></pre><p>import &quot;./App.css&quot;;
import Viewer from &quot;./components/Viewer&quot;;
import Controller from &quot;./components/Controller&quot;;
import Even from &#39;./components/Even&#39;;
import { useState, useEffect, useRef } from &quot;react&quot;;</p>
<p>function App() {
  const [count, setCount] = useState(0);
  const [input, setInput] = useState(&quot;&quot;);</p>
<p>  const isMount = useRef(false);</p>
<p>  useEffect(() =&gt;{
    console.log(<code>count: ${count} / input: ${input}</code>);
  }, [count, input]);</p>
<p>  // 1. 마운트
  useEffect(()=&gt;{
    console.log(&quot;mount&quot;)
  }, []);</p>
<p>  // 2. 업데이트
  useEffect(()=&gt; {
    if(!isMount.current){
      isMount.current = true;
      return;
    }
    console.log(&quot;update&quot;)
  });</p>
<p>  // 3. </p>
<p>  const onClickButton = (value) =&gt; {
    setCount(count + value);
  };</p>
<p>  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        &lt;input value={input} onChange={(e)=&gt; {
          setInput(e.target.value)
        }} /&gt;
      </section>
      <section>
        <Viewer count={count} />
        {count % 2 === 0 ? <Even /> : null}
      </section>
      <section>
        <Controller onClickButton={onClickButton} />
      </section>
    </div>
  );
}</p>
<p>export default App;</p>
<pre><code>
- - ```src/components/Even.jsx``` 파일 수정</code></pre><p>import { useEffect  } from &quot;react&quot;;</p>
<p>const Even = () =&gt; {
    useEffect(()=&gt;{
        // 클린업, 정리함수
        return () =&gt; {}
    }, []);
    return <div>짝수입니다</div>;
};</p>
<p>export default Even;</p>
<pre><code>
&amp;nbsp;

## 4. React 개발자 도구 사용하기

### 1) React 개발자 도구?
리액트 앱을 개발할 때 컴포넌트의 지금 상태가 어떤지, state의 값은 어떻고 props로 어떤 걸 받았는지 등의 정보를 쉽게 확인할 수 있도록 도와주는 도구

크롬에 설치해서 사용할 수 있는 무료 크롬 확장 프로그램

### 2) React 개발자 도구 설치하기
&gt; 설치 사이트: https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=ko
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[한 입 크기로 잘라먹는 리액트(React.js) #7.]]></title>
            <link>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-6-qn5z5vhw</link>
            <guid>https://velog.io/@onion_0/%ED%95%9C-%EC%9E%85-%ED%81%AC%EA%B8%B0%EB%A1%9C-%EC%9E%98%EB%9D%BC%EB%A8%B9%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8React.js-6-qn5z5vhw</guid>
            <pubDate>Tue, 05 Aug 2025 12:47:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://reactjs.winterlood.com/">https://reactjs.winterlood.com/</a>
위 내용을 바탕으로 강의를 수강하며 정리하였습니다.</p>
</blockquote>
<h1 id="프로젝트-1-카운터-앱">프로젝트 #1. 카운터 앱</h1>
<h2 id="1-프로젝트-소개-및-준비">1. 프로젝트 소개 및 준비</h2>
<h3 id="1-구조">1) 구조</h3>
<img src="https://velog.velcdn.com/images/onion_0/post/abbf70c2-3f3b-44a7-a3fe-f664efd96f54/image.png">

<br>

<h3 id="2-실습-환경-구성">2) 실습 환경 구성</h3>
<ul>
<li><p>vscode에서 <code>/onebite-react/</code> 열기 
→ vscode 터미널 열기 
→ <code>npm create vite@latest</code> 입력 후 엔터 
→ (설치해도 되냐는 질문이 나오면 <code>y</code> 입력) 
→ 프로젝트 이름 설정: <code>section06</code> 누르고 엔터 
→ 프레임워크 선택: <code>React</code> 선택 후 엔터 
→ 어떤 버전의 리액트 앱을 만들 건지 선택: <code>JavaScript</code> 선택
→ 왼쪽에 <code>.../onebite-react/section06</code> 폴더 생성</p>
</li>
<li><p><code>section06</code> 폴더로 vscode에서 다시 열기
→ vscode 터미널 열기
→ <code>npm i</code>로 필요한 패키지 모두 다운로드
→ <code>npm run dev</code> 입력 후 엔터
→ 뜨는 localhost 주소로 이동</p>
</li>
<li><p><code>eslint.config.js</code> 수정</p>
<pre><code>import js from &quot;@eslint/js&quot;;
import globals from &quot;globals&quot;;
import reactHooks from &quot;eslint-plugin-react-hooks&quot;;
import reactRefresh from &quot;eslint-plugin-react-refresh&quot;;

export default [
  { ignores: [&quot;dist&quot;] },
  {
    files: [&quot;**/*.{js,jsx}&quot;],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
      parserOptions: {
        ecmaVersion: &quot;latest&quot;,
        ecmaFeatures: { jsx: true },
        sourceType: &quot;module&quot;,
      },
    },
    plugins: {
      &quot;react-hooks&quot;: reactHooks,
      &quot;react-refresh&quot;: reactRefresh,
    },
    rules: {
      ...js.configs.recommended.rules,
      ...reactHooks.configs.recommended.rules,
      &quot;no-unused-vars&quot;: &quot;off&quot;,
      &quot;react/prop-types&quot;: &quot;off&quot;,
      &quot;react-refresh/only-export-components&quot;: [
        &quot;warn&quot;,
        { allowConstantExport: true },
      ],
    },
  },
];</code></pre></li>
<li><p><code>public/vite.svg</code> 삭제</p>
</li>
<li><p><code>src/assets/react.svg</code> 삭제</p>
</li>
<li><p><code>/src/App.jsx</code> 수정</p>
<pre><code>import { useState } from &#39;react&#39;
import &#39;./App.css&#39;

function App() {
  return (
    &lt;&gt;
      카운터 앱
    &lt;/&gt;
  )
}

export default App</code></pre></li>
<li><p><code>/src/App.css</code> 전체 내용 삭제</p>
</li>
<li><p><code>/src/index.css</code> 전체 내용 삭제</p>
</li>
<li><p><code>src/assets/main.jsx</code> 코드 수정</p>
<pre><code>import { StrictMode } from &#39;react&#39;
import { createRoot } from &#39;react-dom/client&#39;
import &#39;./index.css&#39;
import App from &#39;./App.jsx&#39;

createRoot(document.getElementById(&#39;root&#39;)).render(
    &lt;App /&gt;
)</code></pre></li>
</ul>
<p>&nbsp;</p>
<h2 id="2-ui-구현하기">2. UI 구현하기</h2>
<h3 id="0-vscode에서-실습-환경-열기">0) vscode에서 실습 환경 열기</h3>
<p><code>section06</code> 폴더로 vscode에서 열기
→ vscode 터미널 열기
→ <code>npm i</code>로 필요한 패키지 모두 다운로드
→ <code>npm run dev</code> 입력 후 엔터
→ 뜨는 localhost 주소로 이동</p>
<br>

<h3 id="1-컴포넌트-만들기">1) 컴포넌트 만들기</h3>
<h4 id="a-컴포넌트-폴더와-파일-만들기">a. 컴포넌트 폴더와 파일 만들기</h4>
<ul>
<li><code>src/components/</code> 폴더 생성</li>
<li><code>src/components/Viewer.jsx</code> 파일 생성</li>
<li><code>src/components/Controller.jsx</code> 파일 생성</li>
</ul>
<h4 id="b-코드-삽입">b. 코드 삽입</h4>
<ul>
<li><code>src/components/Viewer.jsx</code> 파일에 코드 생성
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter02/src/components/Viewer.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter02/src/components/Viewer.jsx</a></li>
<li><code>src/components/Controller.jsx</code> 파일에 코드 생성
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter02/src/components/Controller.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter02/src/components/Controller.jsx</a></li>
<li><code>src/App.jsx</code> 파일에 코드 생성
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter02/src/App.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter02/src/App.jsx</a></li>
<li><code>src/App.css</code> 파일에 코드 생성
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter02/src/App.css">https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter02/src/App.css</a></li>
</ul>
<p>&nbsp;</p>
<h2 id="3-기능-구현하기">3. 기능 구현하기</h2>
<h3 id="0-vscode에서-실습-환경-열기-1">0) vscode에서 실습 환경 열기</h3>
<p><code>section06</code> 폴더로 vscode에서 열기
→ vscode 터미널 열기
→ <code>npm i</code>로 필요한 패키지 모두 다운로드
→ <code>npm run dev</code> 입력 후 엔터
→ 뜨는 localhost 주소로 이동</p>
<br>

<h3 id="1-버튼-동작하도록-수정">1) 버튼 동작하도록 수정</h3>
<ul>
<li><p><code>src/App.jsx</code> 파일의 코드 수정
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter03/src/App.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter03/src/App.jsx</a></p>
</li>
<li><p><code>src/components/Viewer.jsx</code> 파일의 코드 수정
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter03/src/components/Viewer.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter03/src/components/Viewer.jsx</a></p>
</li>
<li><p><code>src/components/Controller.jsx</code> 파일의 코드 생성
<a href="https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter03/src/components/Controller.jsx">https://github.com/winterlood/onebite-react-v2/blob/main/section06/chapter03/src/components/Controller.jsx</a></p>
</li>
</ul>
<p>컴포넌트 간에 데이터를 주고 받으려면 props를 이용해야 하는데, props는 기본적으로 부모에서 자식으로만 전달 가능함 ▶ 컴포넌트 간의 데이터 전달은 불가능</p>
<h3 id="2-동작-과정">2) 동작 과정</h3>
<p>스테이트를 계층 구조상 위로 끌어올려서 그 아래에 있는 컴포넌트들이 모두 공유할 수 있도록 만드는 방법 = <strong>스테이트 리프팅(스테이트 끌어올리기)</strong></p>
<img src="https://velog.velcdn.com/images/onion_0/post/edc79190-9c11-43c0-a1a7-64ba1ecc8552/image.png">

<p>데이터들은 항상 위에서 아래로 하나의 방향으로만 흐르게 됨</p>
]]></description>
        </item>
    </channel>
</rss>