<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>s_ongt_aemin.log</title>
        <link>https://velog.io/</link>
        <description>안녕하세요!</description>
        <lastBuildDate>Sun, 24 Aug 2025 09:01:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>s_ongt_aemin.log</title>
            <url>https://images.velog.io/images/s_ongt_aemin/profile/3bcea0eb-5e51-4316-b298-f731e18df51b/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. s_ongt_aemin.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/s_ongt_aemin" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[github private 레포지토리를 EC2에 배포]]></title>
            <link>https://velog.io/@s_ongt_aemin/github-private-%EB%A0%88%ED%8F%AC%EC%A7%80%ED%86%A0%EB%A6%AC%EB%A5%BC-EC2%EC%97%90-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@s_ongt_aemin/github-private-%EB%A0%88%ED%8F%AC%EC%A7%80%ED%86%A0%EB%A6%AC%EB%A5%BC-EC2%EC%97%90-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Sun, 24 Aug 2025 09:01:49 GMT</pubDate>
            <description><![CDATA[<p>private 레포지토리에 접근하기 위해 설정을 해주어야한다.</p>
<h1 id="1">1.</h1>
<p>AWS EC2에서 ubuntu를 연결하고</p>
<pre><code>ssh-keygen -t ed25519 -C &quot;코멘트입력&quot; -f ~/.ssh/id_ed25519 -N &quot;&quot;</code></pre><p>위 코드를 입력한다. &quot;코멘트 입력&quot; 부분은 자유롭게 작성하면 된다.(따옴표 포함)
그러면 아래의 문구와 함께 키를 시각적으로 보여준다.</p>
<p>The key&#39;s randomart image is:
이상한 그림</p>
<p>실행하고 나면 id_ed25519 / id_ed25519.pub 두 파일이 ~/.ssh 폴더에 생긴다.</p>
<h1 id="2-다음-단계는-공개키pub-파일-내용을-확인해서-github에-등록">2. 다음 단계는 공개키(.pub 파일) 내용을 확인해서 GitHub에 등록</h1>
<pre><code>cat ~/.ssh/id_ed25519.pub</code></pre><p>위 명령어를 입력하면  &#39;ssh-ed25519 AAAAC3Nza...&#39; 로 시작하는 한 줄짜리 문자열이 출력</p>
<p>위 문자열을 GitHub 레포의 Settings → Deploy keys → Add deploy key 에 붙여넣기</p>
<p>title은 아무렇게나 짓고 key에 입력 그리고 Allow write access → 체크 안 함 (읽기 전용)</p>
<h1 id="3-github-ssh-연결-테스트">3. GitHub SSH 연결 테스트</h1>
<pre><code>ssh -T git@github.com</code></pre><p>위 코드 입력(GitHub에서 SSH 접속용 전용 계정)</p>
<p>Are you sure you want to continue connecting (yes/no/[fingerprint])?
위 질문에 yes</p>
<h1 id="4-레포지토리-연결">4. 레포지토리 연결</h1>
<pre><code>git clone git@github.com:아이디/레포지토리.git</code></pre><p>내 레포지토리의 ssh 부분을 복사해서 연결하기</p>
<h1 id="5-세팅하기">5. 세팅하기</h1>
<pre><code># 1) 패키지 업데이트
sudo apt-get update

# 2) 필수 빌드 도구 (native 모듈 대비)
sudo apt-get install -y build-essential python3 git

# 3) Node.js LTS 설치 (NodeSource 사용, NVM 불필요)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

# 4) 버전 확인
node -v
npm -v
</code></pre><h1 id="6-환경변수-세팅">6. 환경변수 세팅</h1>
<p>만약 레포지토리 내 특정 폴더만 필요하면 거기로 들어간다.</p>
<p>레포지토리 이동 후</p>
<pre><code>nano .env</code></pre><p>.env 파일 생성</p>
<p>환경변수 입력 후</p>
<p>Ctrl+O → Enter → Ctrl+X 로 저장하기</p>
<p>수정도 같은 방법으로 간으</p>
<h1 id="7-pm2-설치">7. pm2 설치</h1>
<p>Express 백엔드를 EC2에서 상시 돌리고 싶다면 pm2 같은 프로세스 매니저를 설치</p>
<pre><code>sudo npm install -g pm2</code></pre><p>설치 확인</p>
<pre><code>pm2 -v</code></pre><h1 id="8-실행">8. 실행</h1>
<p>npm 설치</p>
<pre><code>npm install</code></pre><p>실행</p>
<pre><code>npm run build</code></pre><h1 id="9-잘-연결됐는지-확인">9. 잘 연결됐는지 확인</h1>
<p>내부 : </p>
<pre><code>curl -i http://127.0.0.1:3001/healthz</code></pre><p>위 명령어를 입력하여 HTTP/1.1 200 OK 와 ok 가 뜨면 정상</p>
<p>외부 :</p>
<pre><code>http://&lt;EC2-퍼블릭IP&gt;:3001/healthz</code></pre><p>브라우저나 로컬 터미널에서 위 경로 입력</p>
<ul>
<li>이때 expres 백엔드 코드에 healthz 코드가 있어야한다.</li>
<li>외부에서 확인이 안된다면 보안그룹에서 규칙 추가</li>
</ul>
<p>규칙 추가(Add rule):</p>
<p>Type: Custom TCP
Port range: 3001
Source:</p>
<ul>
<li>개발/테스트 용: 0.0.0.0/0 (전 세계에서 접근 가능 → 나중엔 보안상 Nginx/80,443만 열어두는 게 좋아요)</li>
<li>안전하게 하고 싶으면 본인 IP만 입력 (My IP 선택)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] EC2 세팅하기]]></title>
            <link>https://velog.io/@s_ongt_aemin/AWS-EC2-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@s_ongt_aemin/AWS-EC2-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 17 Aug 2025 11:53:51 GMT</pubDate>
            <description><![CDATA[<h1 id="1-초기-세팅">1. 초기 세팅</h1>
<h2 id="1-지역region을-설정">1) 지역(region)을 설정</h2>
<pre><code>- 우리가 빌린 컴퓨터가 있는 곳(데이터 센터)
- 주요 사용자들의 위치와 데이터 센터가 가까울 수록 속도가 좋아진다.
 - 즉 내가 한국에 있다고 무조건 한국으로 선택할 필요없이 타겟의 위치로 설정</code></pre><h2 id="2-인스턴스-시작-클릭">2) 인스턴스 시작 클릭</h2>
<h2 id="3-정보-설정">3) 정보 설정</h2>
<ol>
<li><p>이름 설정</p>
<ul>
<li><p>원하는 이름 넣기</p>
<p><img src="https://velog.velcdn.com/images/s_ongt_aemin/post/37077870-7351-4ac2-828d-e8500ba4bb6c/image.png" alt=""></p>
</li>
</ul>
</li>
</ol>
<ol start="2">
<li><p>내가 살 OS 설정</p>
<ul>
<li><p>Ubuntu를 사용 : 최적의 성능을 뽑아내기 위해 가벼운 os 를 사용</p>
<p><img src="https://velog.velcdn.com/images/s_ongt_aemin/post/6666f7fd-951b-43b5-855f-9e79914eedc2/image.png" alt=""></p>
</li>
</ul>
</li>
</ol>
<ol start="3">
<li><p>인스턴스 유형</p>
<blockquote>
<p>AWS에서 빌리는 가상 컴퓨터 한대</p>
</blockquote>
<p> 유형 : 컴퓨터 사양 - 초기 서비스를 시작한다면 t2.micro를 사용해도 상관없다. 망하지 않고 규모가 커진다면 확장</p>
</li>
<li><p>키페어</p>
<blockquote>
<p>서버에 접근하기 위한 비밀번호</p>
</blockquote>
<p> 새 키페어 생성을 하고 이름을 짓고 그래도 생성하면 .pem 파일을 다운받는다. 이것을 잘 저장하기</p>
</li>
<li><p>네트워크 설정</p>
<p> 세팅하기 클릭</p>
<p> 1) vpc - AWS 안에 만드는 내 회사 전용 네트워크 공간. 그 안에 EC2, RDS 같은 자원을 넣고, IP·라우팅·방화벽을 내가 원하는대로 통제</p>
<p> 2) 보안그룹 - 빌린 인스턴스에서 모든 접근에 대해 검사. 방화벽으로 어떤 IP/서비스(포트)만 들어오거나 나가도 되는지 허용 목록(allow-list)으로 정한다.</p>
<p> <strong>보안 그룹 생성 방법</strong></p>
<p> a. 방화벽(보안그룹) 생성
 b. 보안그룹이름/설명 생성
 c. 인바운드 보안 그룹 규칙(ip와 포트 설정)</p>
<ul>
<li><p>기본 세팅은 모든 컴퓨터에 접근가능하도록 되어있음
  <img src="https://velog.velcdn.com/images/s_ongt_aemin/post/055f6d96-f1ad-4be1-99a4-23d4b89c54e1/image.png" alt=""></p>
</li>
<li><p>특정 ip를 지정하고 싶으면 소스유형 - 사용자지정 - ip직접입력</p>
</li>
</ul>
<ol start="6">
<li>스토리지 구성<blockquote>
<p>EBS : EC2에 붙이는 저장소(스토리지), 인스턴스를 꺼도 데이터는 남아있다.</p>
</blockquote>
</li>
</ol>
</li>
</ol>
<h2 id="4-인스턴스-시작">4) 인스턴스 시작</h2>
<p>인스턴스 시작 버튼 클릭</p>
<p>대시보스 - 인스턴스 실행중에서 관련 내용확인 - 연결 버튼 클릭해서 연결하기</p>
<h3 id="탄력적-ip-연결하기">탄력적 ip 연결하기</h3>
<blockquote>
<p>인스턴스를 생성하고 받는 ip는 임시 ip이기때문에 ip는 바뀐다. 고정 ip를 할당받는 것이 탄력적 ip</p>
</blockquote>
<p>네트워크 및 보안 - 탄력적ip </p>
<ol>
<li>탄력적 ip 주소 할당 클릭 후 기본값으로 할당</li>
<li>할당된 ip를 선택하고 작업 - 탄력적 ip 주소연결</li>
<li>인스턴스 선택에서 연결할 인스턴스를 선택하고 연결 클릭</li>
</ol>
<p>그러면 이제 ip가 고정된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] zustand 사용]]></title>
            <link>https://velog.io/@s_ongt_aemin/React-zustand-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@s_ongt_aemin/React-zustand-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Sat, 10 May 2025 14:52:04 GMT</pubDate>
            <description><![CDATA[<h1 id="1-설치하기">1. 설치하기</h1>
<p>zustand를 설치한다.</p>
<pre><code># npm을 쓰는 경우
npm install zustand

# yarn을 쓰는 경우
yarn add zustand

# pnpm을 쓰는 경우
pnpm add zustand
</code></pre><h1 id="2-저장소-파일-만들고-상태-정의">2. 저장소 파일 만들고 상태 정의</h1>
<p>src/store/useStore.ts</p>
<pre><code>import { create } from &#39;zustand&#39;

// (1) 스토어가 가질 상태와 액션 타입 정의 (TypeScript 사용 시)
interface CounterState {
  count: number
  increase: () =&gt; void
  reset: () =&gt; void
}

// (2) create로 스토어 생성
export const useCounterStore = create&lt;CounterState&gt;((set) =&gt; ({
  count: 0,                             // 초기 상태
  increase: () =&gt; set((state) =&gt; ({     // 액션: count++
    count: state.count + 1
  })),
  reset: () =&gt; set({                    // 액션: 초기화
    count: 0
  }),
}))
</code></pre><h2 id="set의-역할">set의 역할</h2>
<blockquote>
<p>create 함수가 호출될 때, 내부적으로 set(업데이트 함수)과 get(현재 상태를 읽는 함수)을 자동으로 주입해 준다.</p>
</blockquote>
<ol>
<li><p>객체로 업데이트</p>
<ul>
<li><p>단순 대입</p>
</li>
<li><p>얕은 병합</p>
<pre><code>// 스토어에 { a: 1, b: 2, c: 3 }가 있을 때…
set({ b: 42 })
// 결과: { a: 1, b: 42, c: 3 }</code></pre></li>
</ul>
</li>
<li><p>함수 콜백으로 업데이트</p>
<ul>
<li><p>이전 상태(state)를 참고해서 새 값을 계산</p>
</li>
<li><p>콜백 인자인 state는 “업데이트 직전의 상태”</p>
<pre><code>// count 값을 이전 값에 +1 해서 업데이트
set((state) =&gt; ({ count: state.count + 1 }))</code></pre></li>
</ul>
</li>
</ol>
<h2 id="get의-역할">get의 역할</h2>
<blockquote>
<p>현재 스토어의 전체 상태를 즉시 조회할 수 있는 기능, 스토어 내에서만 사용할 수 있다.</p>
</blockquote>
<pre><code>create((set, get) =&gt; ({
  count: 0,
  doubleIfOdd: () =&gt; {
    const { count } = get()            // 현재 count 읽기
    if (count % 2 === 1) {
      set({ count: count * 2 })        // 조건부로 업데이트
    }
  }
}))
</code></pre><p>위 코드에서 doubleIfOdd는 count라는 변수가 존재하지 않아 get을 이용하여 count를 불러와줘야한다. 
왜냐하면 count는 자바스크립트의 변수가 아니라 &#39;스토어 객체의 프로퍼티&#39;이기 때문에 doubleIfOdd에서 get으로 호출해주어야 값을 가져올 수 있다.</p>
<h1 id="3-컴포넌트에-가져와-사용하기">3. 컴포넌트에 가져와 사용하기</h1>
<pre><code>import React from &#39;react&#39;
import { useCounterStore } from &#39;@/store/useStore&#39;

export default function Counter() {
  const { count, increase, reset } = useCounterStore()
  return (
    &lt;div&gt;
      &lt;p&gt;현재 카운트: {count}&lt;/p&gt;
      &lt;button onClick={increase}&gt;+1&lt;/button&gt;
      &lt;button onClick={reset}&gt;리셋&lt;/button&gt;
    &lt;/div&gt;
  )
}
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] app router에서 context api 사용]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-app-router%EC%97%90%EC%84%9C-context-api-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-app-router%EC%97%90%EC%84%9C-context-api-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Mon, 24 Mar 2025 13:36:04 GMT</pubDate>
            <description><![CDATA[<h1 id="1-소개">1. 소개</h1>
<blockquote>
<p>next.js app router에서 context api를 사용하는 법은 typescript와 같이 소개한다.</p>
</blockquote>
<h1 id="2-코드-소개">2. 코드 소개</h1>
<h2 id="1-context-파일-생성">1) context 파일 생성</h2>
<ul>
<li>MyContext.tsx</li>
</ul>
<pre><code>import React, { createContext, useContext, useState, ReactNode } from &#39;react&#39;;

interface MyContextType {
  data: string;
  setData: React.Dispatch&lt;React.SetStateAction&lt;string&gt;&gt;;
}

const MyContext = createContext&lt;MyContextType | undefined&gt;(undefined);

export const MyProvider: React.FC&lt;{ children: ReactNode }&gt; = ({ children }) =&gt; {
  const [data, setData] = useState&lt;string&gt;(&#39;초기값&#39;); // 기본값 설정

  return (
    &lt;MyContext.Provider value={{ data, setData }}&gt;
      {children}
    &lt;/MyContext.Provider&gt;
  );
};

export const useMyContext = (): MyContextType =&gt; {
  const context = useContext(MyContext);
  if (!context) {
    throw new Error(&#39;useMyContext는 MyProvider 내부에서 사용되어야 합니다.&#39;);
  }
  return context;
};</code></pre><h3 id="인터페이스타입-정의">인터페이스(타입) 정의</h3>
<pre><code>interface MyContextType {
  data: string;
  setData: React.Dispatch&lt;React.SetStateAction&lt;string&gt;&gt;;
}</code></pre><ul>
<li><p>data :  데이터의 타입을 명시</p>
<pre><code>React.Dispatch&lt;React.SetStateAction&lt;string&gt;&gt;;</code></pre></li>
<li><p>React.Dispatch는 인자로 React.SetStateAction<string>를 받는다.
```</p>
</li>
<li><p>useState의 setter함수에 사용하는 함수로 ts에서 useState 자체에서는 setter 함수의 타입을 자동으로 추론하기 때문에 별도로 타입을 명시하지 않아도 작동하지만 context에서는 명시하는 것을 추천</p>
<ul>
<li>React.SetStateAction<string> : 상태를 새값으로 바꾸거나 이전 상태를 기반으로 새 값을 계산</li>
<li>React.Dispatch : 상태 업데이트 함수의 타입. React.SetStateAction<string>를 인자로 받아 상태를 업데이트</li>
</ul>
</li>
</ul>
<h3 id="context-생성">context 생성</h3>
<pre><code>const MyContext = createContext&lt;MyContextType | undefined&gt;(undefined);</code></pre><ul>
<li><p>createContext:
React의 createContext 함수를 사용하여 Context 객체를 생성합니다.</p>
</li>
<li><p>초기값:
초기값으로 undefined를 지정함으로써, 만약 Provider 없이 Context를 사용하려고 하면 에러를 발생시킬 수 있도록 설계되었습니다.</p>
</li>
</ul>
<h3 id="provider-컴포넌트">Provider 컴포넌트</h3>
<pre><code>export const MyProvider: React.FC&lt;{ children: ReactNode }&gt; = ({ children }) =&gt; {
  const [data, setData] = useState&lt;string&gt;(&#39;초기값&#39;); // 기본값 설정

  return (
    &lt;MyContext.Provider value={{ data, setData }}&gt;
      {children}
    &lt;/MyContext.Provider&gt;
  );
};
</code></pre><ul>
<li><p>상태 관리:
useState 훅을 사용해 data라는 상태와 이를 업데이트하는 setData 함수를 생성합니다. 초기값은 &#39;초기값&#39;으로 설정되어 있습니다.</p>
</li>
<li><p>Provider 역할:
생성된 MyContext.Provider에 { data, setData }를 값으로 전달하여, 이 Provider 하위의 모든 컴포넌트가 해당 상태와 업데이트 함수를 사용할 수 있게 만듭니다.</p>
</li>
<li><p>Next.js 앱 라우터와의 통합:
Next.js의 새로운 App Router 구조에서는 이 Provider를 최상위 레이아웃 파일에 래핑하여 페이지 전체에서 전역 상태에 접근할 수 있도록 합니다.</p>
</li>
</ul>
<h3 id="커스텀-훅">커스텀 훅</h3>
<pre><code>export const useMyContext = (): MyContextType =&gt; {
  const context = useContext(MyContext);
  if (!context) {
    throw new Error(&#39;useMyContext는 MyProvider 내부에서 사용되어야 합니다.&#39;);
  }
  return context;
};
</code></pre><ul>
<li><p>useContext 사용:
React의 useContext 훅을 사용해 현재 Context 값을 가져옵니다.</p>
</li>
<li><p>안전성 체크:
만약 Provider 밖에서 이 훅을 호출할 경우 context가 undefined가 되므로, 이를 감지하고 명확한 에러 메시지를 던져 개발자가 올바르게 Provider 내부에서 사용하도록 유도합니다.</p>
</li>
<li><p>편의성:
이 커스텀 훅을 사용하면 각 컴포넌트에서 useContext(MyContext)를 직접 호출할 필요 없이, 간단히 useMyContext()로 Context 값에 접근할 수 있습니다.</p>
</li>
</ul>
<hr>
<h2 id="2-전역-레이아웃에-provider-적용-applayouttsx">2) 전역 레이아웃에 Provider 적용 (app/layout.tsx)</h2>
<blockquote>
<p>리액트, 페이지 라우트 next.js는 가장 상위 파일인 app.tsx에 적용하지만 app router는 가장 상위 layout.tsx에 적용한다.</p>
</blockquote>
<pre><code>import &#39;./globals.css&#39;;
import { MyProvider } from &#39;../contexts/MyContext&#39;;

export const metadata = {
  title: &#39;My Next.js App&#39;,
  description: &#39;App Router에서 Context API 사용 예시&#39;,
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang=&quot;ko&quot;&gt;
      &lt;body&gt;
        {/* 앱 전체에 MyProvider 적용 */}
        &lt;MyProvider&gt;
          {children}
        &lt;/MyProvider&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  );
}
</code></pre><p>MyProvider를 불러와 상위 구조에 감싼다.</p>
<hr>
<h2 id="3-데이터-저장업데이트-페이지">3. 데이터 저장(업데이트) 페이지</h2>
<pre><code>&#39;use client&#39;;

import React from &#39;react&#39;;
import { useMyContext } from &#39;../../contexts/MyContext&#39;;
import Link from &#39;next/link&#39;;

export default function SavePage() {
  const { setData } = useMyContext();

  const handleSave = () =&gt; {
    // 원하는 데이터를 저장(업데이트)합니다.
    setData(&#39;저장된 데이터입니다!&#39;);
  };

  return (
    &lt;div&gt;
      &lt;h1&gt;데이터 저장 페이지&lt;/h1&gt;
      &lt;button onClick={handleSave}&gt;데이터 저장&lt;/button&gt;
      &lt;p&gt;
        &lt;Link href=&quot;/read&quot;&gt;데이터 조회 페이지로 이동&lt;/Link&gt;
      &lt;/p&gt;
    &lt;/div&gt;
  );
}
</code></pre><p>useMyContext를 사용하여 setData 함수를 가져온다.
setData 함수에 저장할 함수를 만들고 함수를 적용하면 저장된다.</p>
<hr>
<h2 id="4-데이터-조회꺼내쓰기-페이지-appreadpagetsx">4. 데이터 조회(꺼내쓰기) 페이지 (app/read/page.tsx)</h2>
<pre><code>// app/read/page.tsx
&#39;use client&#39;;

import React from &#39;react&#39;;
import { useMyContext } from &#39;../../contexts/MyContext&#39;;
import Link from &#39;next/link&#39;;

export default function ReadPage() {
  const { data } = useMyContext();

  return (
    &lt;div&gt;
      &lt;h1&gt;데이터 조회 페이지&lt;/h1&gt;
      &lt;p&gt;저장된 데이터: {data}&lt;/p&gt;
      &lt;p&gt;
        &lt;Link href=&quot;/save&quot;&gt;데이터 저장 페이지로 이동&lt;/Link&gt;
      &lt;/p&gt;
    &lt;/div&gt;
  );
}
</code></pre><p>useMyContext를 가져와 data를 불러온다.
불러온 data를 원하는 곳에서 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 배포하기]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-trcdnur2</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-trcdnur2</guid>
            <pubDate>Thu, 16 Jan 2025 13:37:12 GMT</pubDate>
            <description><![CDATA[<h1 id="1-vercel-활용하기">1. vercel 활용하기</h1>
<h3 id="1-vercel-설치-명령어를-프로젝트에-입력">1) vercel 설치 명령어를 프로젝트에 입력</h3>
<p>window</p>
<blockquote>
<p> npm i -g vercel</p>
</blockquote>
<p>mac os</p>
<blockquote>
<p>sudo  npm i -g vercel</p>
</blockquote>
<h3 id="2-vercel-로그인">2) vercel 로그인</h3>
<p>명령어</p>
<blockquote>
<p>vercel login</p>
</blockquote>
<p>vercel 로그인 수단을 선택하여 로그인</p>
<h3 id="3-백엔드-서버-vercel에-배포선택">3) 백엔드 서버 vercel에 배포(선택)</h3>
<p>명령어</p>
<blockquote>
<p>vercel</p>
</blockquote>
<p>아래 질문들이 나온다.</p>
<ul>
<li>계정확인</li>
<li>어떤 계정에 배포할지 (내가 가입한 vercel 선택)</li>
<li>이미 존재하는 프로젝트와 연결할 것인지?</li>
<li>배포할 프로젝트의 이름</li>
<li>어떤 디렉토리에 코드가 위치해있는 지 - 기본값으로 설정</li>
</ul>
<h3 id="4-next-앱-배포">4) next 앱 배포</h3>
<p>명령어</p>
<blockquote>
<p>vercel</p>
</blockquote>
<p>같은 질문들이 나온다.</p>
<p>아래 질문들이 나온다.</p>
<ul>
<li>계정확인</li>
<li>어떤 계정에 배포할지 (내가 가입한 vercel 선택)</li>
<li>이미 존재하는 프로젝트와 연결할 것인지?</li>
<li>배포할 프로젝트의 이름</li>
<li>어떤 디렉토리에 코드가 위치해있는 지 - 기본값으로 설정</li>
</ul>
<h3 id="5-vercel에서-동작하는-서버는-vercel에서-배포한-서버의-주소를-입력해야한다">5) vercel에서 동작하는 서버는 vercel에서 배포한 서버의 주소를 입력해야한다.</h3>
<ul>
<li><p>배포한 next 앱은 로컬에서 작동되는 서버와 연결된다. vercel에서 배포한 서버로 바꾸기 위해 설정이 필요하다.</p>
</li>
<li><p>vercel에 배포한 백엔드의 url를 가져와 vercel에 배포한 next 앱에서 setting으로 들어가 Enviornment Variables(환경변수) 탭에서 &#39;Create New&#39;로 들어가 key와 value를 입력한다.</p>
</li>
<li><p>그리고 다시 빌드(redeploy)를 하면 배포한 서버에 연결된다.</p>
</li>
</ul>
<h3 id="6-터미널에서-재배포">6) 터미널에서 재배포</h3>
<blockquote>
<p>vercel --prod</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 검색엔진최적화(SEO)]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EA%B2%80%EC%83%89%EC%97%94%EC%A7%84%EC%B5%9C%EC%A0%81%ED%99%94SEO</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EA%B2%80%EC%83%89%EC%97%94%EC%A7%84%EC%B5%9C%EC%A0%81%ED%99%94SEO</guid>
            <pubDate>Wed, 15 Jan 2025 12:53:19 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>검색엔진 결과 페이지(SERP)에서 더 높은 순위에 표시되도록 최적화하는 과정</p>
</blockquote>
<h1 id="방법">방법</h1>
<h2 id="1-메타-태그-설정">1) 메타 태그 설정</h2>
<ol>
<li>Metadata를 설치해준다.</li>
</ol>
<pre><code>import { Metadata } from &quot;next&quot;;</code></pre><ol start="2">
<li>index 컴포넌트(page) 위에 metadata를 아래와 같이 작성해준다.<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>그러면 메타 태그를 내용에 맞게 채워넣어준다. 여기서 openGraph는 sns에 들어가는 메타태그도 포함시킨다.</li>
</ol>
<h3 id="동적인-값을-통해-메타데이터-설정">동적인 값을 통해 메타데이터 설정</h3>
<p>동적인 페이지를 데이터가 계속 바뀌기 때문에 그에 맞게 설정해주어야한다.</p>
<pre><code>export async function generateMetadata({
  searchParams,
}: {
  searchParams: Promise&lt;{q?:string}&gt;;
}): Promise&lt;Metadata&gt; {
  //현재 페이지의 메타 데이이터를 동적으로 생성하는 역할
  const { q } = await searchParams;

  return {
    title: `${q} : 한입북스`,
    description : `${q}의 검색결과입니다.`,
    openGraph: {
      title: `${q} : 한입북스`,
      description : `${q}의 검색결과입니다.`,
      images : [&quot;/thumbnail.png&quot;]
    },
  }
}

export default function Page({
  searchParams,
}: {
  searchParams: {
    q?: string;
  };
}) {
  return (
    &lt;Suspense
      key={searchParams.q || &quot;&quot;}
      fallback={&lt;BookListSkeleton count={3} /&gt;}
    &gt;
      &lt;SearchResult q={searchParams.q || &quot;&quot;} /&gt;
    &lt;/Suspense&gt;
  );
}</code></pre><p>기존 페이지의 데이터를 받아 오는 것과 같이 함수를 작성하고 데이터를 같이 받아와 return에 작성한다. </p>
<ul>
<li>generateMetadata라는 함수를 사용해야한다.</li>
<li>Promise&amp;ltMetadata&amp;gt 로 타입정의</li>
<li>api를 불러 올 수 도 있다.</li>
</ul>
<h2 id="2-favicon-설정">2) favicon 설정</h2>
<blockquote>
<p>app 폴더 내 favicon.ico 파일을 바꿔준다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js]  이미지 최적화]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Wed, 15 Jan 2025 12:01:55 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>모든 웹 용량에서 이미지가 가장 많은 용량을 차지하는 이미지를 최적화</p>
</blockquote>
<h1 id="2-방법">2. 방법</h1>
<blockquote>
<p>이미지 컴포넌트 활용</p>
</blockquote>
<h2 id="문제점">문제점</h2>
<ol>
<li>이미지는 webP나 AVIF 등 경량화된 포맷을 활용하는 것이 아니라 jpeg나 png를 사용한다.</li>
<li>필요한 이미지부터 보여주고 추가적으로 랜더링하는 것이 아닌 당장 필요없는 이미지까지 가져온다.</li>
<li>필요한 이미지 사이즈보다 더 큰 사이즈로 가져온다.</li>
</ol>
<h2 id="사용법">사용법</h2>
<p>img 태그 대신 Image 컴포넌트 사용</p>
<ol>
<li><p>Image 컴포넌트를 import 한다.</p>
<pre><code>import Image from &quot;next/image&quot;;</code></pre></li>
<li><p>이미지 컴포넌트 내 경로, 크기 그리고 alt 값을 지정해준다.</p>
<pre><code>&lt;Image src={coverImgUrl} width={80} height={105} alt={title}/&gt;</code></pre></li>
<li><p>이미지가 저장된 이미지가 아니라 외부 서버에서 가져온 이미지면 보안때문에 자동으로 차단되어 오류가 난다.</p>
</li>
</ol>
<p>이때 Next.config에 어떤 서버에서 가져온 이미지는 안전하다고 알려줘야한다.</p>
<pre><code>images : {
    domains: [
      &#39;도메인 주소&#39;
    ]
  }</code></pre><p>images안에 domain을 넣고 허용할 주소를 입력해준다.</p>
<h3 id="더-많은-방식">더 많은 방식</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 인터셉팅 라우트(Intercepting Route)]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EC%9D%B8%ED%84%B0%EC%85%89%ED%8C%85-%EB%9D%BC%EC%9A%B0%ED%8A%B8Intercepting-Route</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EC%9D%B8%ED%84%B0%EC%85%89%ED%8C%85-%EB%9D%BC%EC%9A%B0%ED%8A%B8Intercepting-Route</guid>
            <pubDate>Sun, 12 Jan 2025 14:03:40 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>특정 조건일 때 원래 페이지가 아닌 다른 페이지를 랜더링하게 설정하는 기술</p>
</blockquote>
<p>사용자가 특정 경로로 접속해 새로운 페이지를 요청할때 페이지를 가로채서 다른 페이지를 대신 랜더링한다.</p>
<p>초기접속요청이 아닐때만 인터셉팅이 된다.
즉, 사용자가 초기 접속이 아닌 클라이언트 방식(link, router push)으로 접근했을 때만 인터셉팅 라우트가 동작</p>
<p>예를 들어 인스타그램에서 피드를 클릭하면 상세 피드가 나오고 뒤로 가기를 하면 다시 피드화면이 나오게 된다. 그리고 상세피드에서 새로고침(클라이언트 방식으로 접근)을 하면 완전히 이동하게 된다.</p>
<h1 id="2-사용법">2. 사용법</h1>
<h2 id="1-세팅">1) 세팅</h2>
<ol>
<li>가로챌 폴더와 똑같은 이름의 폴더를 만들고 앞에 (.)또는 (..)을 붙여준다. ex) (.)book/[id]<ul>
<li>점이 하나라면 상대경로로 동일한 경로 상에 있다는 것을 알려준다. (.)</li>
<li>인터셉팅하려는 경로가 한뎁스 더 있다면 점을 두개 붙여주면 상위 경로의 폴더를 인터셉팅한다는 뜻이다. (..)</li>
<li>두 단계 위에 있다면 두번 적어준다. (..)(..)</li>
<li>점이 세개라면 app폴더 바로 밑의 폴더를 인터셉팅한다는 뜻이다. (...)</li>
</ul>
</li>
<li>만든 폴더 내 page.tsx를 만들어준다.</li>
<li>그러면 해당 페이지는 인터셉팅이 된다.</li>
<li>인터셉팅할 페이지에 원래 랜더링 될 페이지를 import 해주면 같은 내용으로 랜더링된다.
 이때 모달창으로 뜨게 하던지 다른 방식으로 나타나게 활용할 수 있다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 패럴랠 라우트(Parallel Route)]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%ED%8C%A8%EB%9F%B4%EB%9E%A0-%EB%9D%BC%EC%9A%B0%ED%8A%B8Parallel-Route</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%ED%8C%A8%EB%9F%B4%EB%9E%A0-%EB%9D%BC%EC%9A%B0%ED%8A%B8Parallel-Route</guid>
            <pubDate>Sun, 12 Jan 2025 12:53:01 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>하나의 화면에 여러개의 페이지를 병렬로 랜더링 시켜주는 패턴</p>
</blockquote>
<p>고급 라우팅 패턴 중 하나로 sidebar 나 feed 등 여러 페이지(page.tsx에 작성되는 페이지 컴포넌트)를 병렬로 랜더링 시켜준다.</p>
<p>소셜네트워크나 대시보드 등에 구축</p>
<h1 id="2-사용법">2. 사용법</h1>
<h2 id="1-세팅하기">1) 세팅하기</h2>
<ol>
<li>parallel 폴더를 만들어준다.</li>
<li>폴더 내 layout과 page를 만들어준다.</li>
<li>parallel 폴더 아래 슬롯을 만들어줘야한다.<ul>
<li>슬롯은 &#39;@이름&#39;으로 만들어준다.</li>
<li>병렬로 랜더링될 페이지 컴포넌트를 보관하는 폴더</li>
<li>슬롯 내 페이지 컴포넌트는 부모의 레이아웃 컴포넌트에 props로 자동 전달</li>
</ul>
</li>
<li>부모의 layout 컴포넌트에는 children과 같이 설정한 슬롯의 이름을 받아올 수 있다.
layout.tsx<pre><code>import { ReactNode } from &#39;react&#39;
</code></pre></li>
</ol>
<p>export default function Layout({
    children, sidebar
}:{
    children:ReactNode;
    sidebar:ReactNode;
}) {
    return (
        <div>
            {sidebar}
            {children}
        </div>
    )
}</p>
<p>```
sidebar와 children이 병렬로 연결되어있다.
슬롯의 제한은 없다.</p>
<p>*<em>폴더 구조(예시) *</em></p>
<p>parallel(폴더)
ㄴ @sidebar - page.tsx</p>
<ul>
<li>layout.tsx</li>
<li>page.tsx</li>
</ul>
<h2 id="2-슬롯-내-새로운-페이지-추가">2) 슬롯 내 새로운 페이지 추가</h2>
<p><strong>폴더구조(예시)</strong></p>
<p>parallel(폴더)
ㄴ @sidebar
    ㄴ @setting
        - page.tsx
    - page.tsx
ㄴ @feed
    - page.tsx
    - default.tsx</p>
<ul>
<li>layout.tsx</li>
<li>page.tsx</li>
<li>default.tsx</li>
</ul>
<ol>
<li>@setting 폴더를 슬롯 안에 만든다.</li>
<li>setting 폴더 내 page.tsx 컴포넌트를 만들어준다.</li>
<li>/parallel/setting 경로로 이동한다면 setting 폴더 내의 page.tsx가 랜더링된다.<ul>
<li>setting 폴더가 없는 슬롯은 404 not found가 되어야하지만 next에서는 이전의 페이지를 유지한다.</li>
</ul>
</li>
<li>link 컴포넌트를 이용해 클라이언트 사이드 랜더링 방식으로 이동할 때만 가능<ul>
<li>초기접속에서 주소로 바로 /parallel/setting 접속해서 들어가면 슬롯의 이전값을 찾을 수 없기 때문에 404 페이지가 나타난다.</li>
</ul>
</li>
<li>위 문제를 해결하기 위해 default 페이지를 만든다. (children 페이지에도 추가)</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 서버 액션]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EC%84%9C%EB%B2%84-%EC%95%A1%EC%85%98</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EC%84%9C%EB%B2%84-%EC%95%A1%EC%85%98</guid>
            <pubDate>Thu, 09 Jan 2025 12:31:15 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>브라우저에서 호출 할 수 있는 서버에서 실행되는 비동기 함수</p>
</blockquote>
<p>서버액션을 활용하면 api를 만들필요없이 함수 하나만으로 브라우저에서 Next 서버측에서 실행되는 함수를 직접 호출할 수 있다.</p>
<h1 id="2-활용">2. 활용</h1>
<p>특정 폼 특정 양식에서 사용</p>
<h2 id="예시">예시</h2>
<pre><code>export default function Page() {
    const saveName = async (formData: FormData) =&gt; {
        &quot;use server&quot;;

        console.log(formData)
        const name = formData.get(&quot;name&quot;);
        await sql`INSERT INTO Names (name) VALUES (${name})`; //sql문
        await saveDB({ name:name}); // DB에 저장
    }

    return (

        &lt;form action={saveName}&gt;
            &lt;input name=&quot;name&quot; placeholder=&quot;이름을 입력해주세요&quot; /&gt;
            &lt;button type=&quot;submit&quot;&gt;제출하기&lt;/button&gt;
        &lt;/form&gt;
    )
}</code></pre><ol>
<li>input에 이름을 적고 제출 버튼을 누르면 form 태그의 action의 saveName 함수가 실행된다.</li>
<li>함수 내 &quot;use server&quot;가 있으면 서버 액셜으로 설정된다.</li>
<li>input 태그의 값이 formData의 매개변수로 전달이 된다.</li>
<li>save name 함수의 formData에서 값을 꺼내와서 DB에 값을 직접 저장한다거나 sql문에서 활용할 수 있다.</li>
</ol>
<p>서버에서만 실행되는 함수를 브라우저가 직접 호출하면서 데이터까지 formData로 전달할 수 있게 해주는 기능 - 기존에서 api를 사용했어야했는데 js만으로도 가능해졌다.</p>
<h2 id="사용-이유">사용 이유</h2>
<p>api를 사용하려면 경로, 예외처리 등등 손이 많이간다.
단순한 기능을 사용할 때 코드가 간결해진다.
브라우저가 아닌 오직 서버만 실행되어 보안상으로 민감한거나 중요한 데이터를 다룰때 유용</p>
<h2 id="재검증-구현">재검증 구현</h2>
<p>입력 내용이 화면에 바로 반영되어야 할 경우 보통 input을 통해 데이터를 전달하면 바로 적용이 되지 않고 새로고침을 해야 반영이 된다. 이를 해결하기 위해 재검증 구현이 필요하다.</p>
<p>데이터가 입력이 되면 재랜더링을 통해 재검증이 되어야한다.</p>
<ul>
<li>revaliatePath() 사용하기</li>
</ul>
<pre><code>import { revalidatePath } from &quot;next/cache&quot;;

try {
    ...
    revaliatePath(&#39;경로&#39;) 
}
</code></pre><p>next 캐시에서 revalidatePath를 불러오고 불러온 경로에 페이지를 인수로 전달한다.
그러면 인수로 전달한 페이지를 재검증한다.</p>
<p>revalidatePath는 next 서버에게 인수로 전달한 경로의 페이지를 다시 생성(재검증)해달라는 메소드</p>
<h3 id="주의점">주의점</h3>
<ol>
<li>revalidatePath 메소드는 서버측에서만 호출할 수 있어 서버 컴포넌트에서만 사용이 가능하고 클라이언트 컴포넌트에서는 사용이 불가능하다.</li>
<li>revalidatePath 메소드는 인수로 전달한 경로를 전부 재검증하기 때문에 해당 페이지의 모든 캐시도 무효화한다. force-cache를 설정하더라도 캐시가 다 날라간다.</li>
<li>데이터 캐시 뿐아니라 페이지를 캐싱하는 풀라우트 캐시도 삭제된다. 다시 풀라우트 캐시를 저장해주지 않는다.</li>
</ol>
<h3 id="다양한-재검증-방식">다양한 재검증 방식</h3>
<ol>
<li>특정 주소에 해당하는 페이지만 재검증</li>
</ol>
<pre><code>revaliatePath(&#39;경로&#39;) </code></pre><ol start="2">
<li><p>특정 경로의 모든 동적 페이지를 재검증</p>
<pre><code>revaliatePath(&quot;경로/폴더경로&quot;, &quot;page&quot;) </code></pre><p>폴더경로나 파일의 경로를 명시</p>
</li>
<li><p>특정 레이아웃을 갖는 모든 페이지 재검증</p>
<pre><code>revaliatePath(&#39;/경로&#39;, &quot;layout&quot;) </code></pre><p>레이아웃을 재검증</p>
</li>
<li><p>모든 데이터 재검증</p>
<pre><code>revaliatePath(&#39;/&#39;, &quot;layout&quot;) </code></pre></li>
<li><p>태그기준, 데이터 캐시 재검증</p>
</li>
</ol>
<pre><code>import { revalidateTag } from &quot;next/cache&quot;;

revaliateTag(&#39;태그명&#39;) </code></pre><p>태그 값을 갖는 데이터 캐시 재검증
태그는 데이터 페칭에 적용되는 옵션에 태그를 지정해준다.
지정해준 태그를 활용하여 재검증을 한다.
import 해주어야함</p>
<ul>
<li>태그 설정<pre><code></code></pre></li>
</ul>
<p>const response = await fetch(
    <code>api 주소</code>,
    { next: { tags : [&#39;태그명&#39;]} }
)</p>
<pre><code>
태그 방식이 불필요한 캐시를 삭제하지 않고 가능하다.


## 클라이언트 컴포넌트에서 서버액션
</code></pre><p>import { useActionState } from &quot;react&quot;;</p>
<p>export default funtcion Page () {
    const [state, formAction, isPending] = useActionState(
        createReviewAction,
        null
      );
}</p>
<pre><code>
useActionState는 리액트 19 버전에서 출시
폼 태그의 상태를 쉽게 다룰 수 있는 기능이 있다.

- 기본적으로 2개의 인수를 전달하며 호출
1. 다룰 form의 액션 함수를 넣어준다.
2. 두번째 인수는 form 상태의 초기값을 넣어준다. (null 등등)

- const 배열 형태로 3개의 값을 반환
1. form의 state값 반환
2. form의 액션을 의미하는 formAction 반환
3. 로딩 상태를 의미하는 isPending 반환

그러면 전달받을 form의 액션 값을 formAction으로 설정해 실행시켜주면 useActionState 훅이 인수로 전달한 createReviewAction을 실행. 자동으로 state와 pending 값을 관리


createReviewAction.ts</code></pre><p>export default funtcion createReviewAction () {
    if (!bookId || !content || !author) {
    return {
      status: false,
      error: &quot;내용을 입력해주세요&quot;,
    };
  }
}</p>
<pre><code>특정 서버액션 조건에서 반환된 값을 사용할 수 있다.

특정 조건으로 action이 실패했다면
status로 액션이 실패했음을 알려주고 error 프로퍼티로 실패 이유를 알려줄 수 있다.

추가로 try catch문에서도 실패 시 반환값을 전달할 수 있다.
</code></pre><input disabled={isPending}/>

<p>useEffect(() =&gt; {
    if (state &amp;&amp; !state.status) {
      alert(state.error);
    }
  }, [state]);</p>
<pre><code>
위 처럼 input의 제어할 수 있고 아래처럼 state 값으로 상황에 맞는 에러메세지를 추가할 수도 있다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 에러 핸들링]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EC%97%90%EB%9F%AC-%ED%95%B8%EB%93%A4%EB%A7%81</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EC%97%90%EB%9F%AC-%ED%95%B8%EB%93%A4%EB%A7%81</guid>
            <pubDate>Tue, 17 Dec 2024 13:37:36 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>오류 발생 시 미리 만들어둔 오류페이지를 보여주는 것</p>
</blockquote>
<h1 id="2-사용법">2. 사용법</h1>
<p>에러가 발생한 대상과 같은 위치에 에러가 발생했을 때 보여줄 페이지 컴포넌트인 error.tsx(jsx)를 만들어준다. (하위 폴더까지 적용)</p>
<ul>
<li>error.tsx<pre><code>&quot;use client&quot;
</code></pre></li>
</ul>
<p>import { useEffect } from &quot;react&quot;
import { useRouter } from &quot;next/navigation&quot;;</p>
<p>export default function Error({error, reset,} : {error:Error; reset:()=&gt; void;}) {
    useEffect(() =&gt; {
        console.error(error.message)
    },[error])</p>
<pre><code>const router = useRouter();

return (
    &lt;div&gt;
        &lt;h3&gt;오류가 발생했습니다.&lt;/h3&gt;
        &lt;button onClick={()=&gt; reset()}&gt;다시 시도&lt;/button&gt; // 해결안될 수도 있음
        &lt;button onClick={()=&gt; {
            router.refresh(); //현재 페이지에 필요한 서버 컴포넌트들을 다시 불러옴
            reset(); // 에러 상태를 초기화, 컴포넌트들을 다시 랜더링
            }}&gt;다시 시도&lt;/button&gt;
    &lt;/div&gt;
)</code></pre><p>}</p>
<p>```</p>
<ol>
<li>use client로 클라이언트 페이지로 설정(서버나 클라이언트 환경 둘다 대응 가능)</li>
<li>error props로 에러 메세지 출력가능 (이때 type은 error로 설정)</li>
<li>reset props가 제공 , 에러가 발생한 페이지를 복구하기 위해 다시 한번 컴포넌트를 랜더링시켜본다. - 클라이언트 측에서만 리랜더링이 실행되어 서버측 오류이면 해결이 되지않는다.(router함수를 사용하여 필요한 컴포넌트만 다시 불러오기)</li>
<li>router.refresh() 함수를 실행시키고 reset() 함수를 실행시키기 위해 startTransition 함수를 사용하면 된다.</li>
</ol>
<h2 id="starttransition">startTransition</h2>
<blockquote>
<p>상태 업데이트나 UI 전환이 비동기적이고, 덜 중요한 작업으로 처리되도록 만들어 주는 API</p>
</blockquote>
<p>콜백 함수를 인수로 전달받아 콜백함수 안에 ui를 변경시키는 작업을 일괄적으로 실행</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 스트리밍]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D</guid>
            <pubDate>Sat, 14 Dec 2024 13:31:52 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<p>스트리밍 : 서버에서 클라이언트로 데이터를 넘겨야할 때 보내야할 데이터가 크거나 준비시간이 오래걸릴 때 데이터를 여러개의 조각으로 나눈 후 하나씩 전송한다.</p>
<blockquote>
<p> 데이터가 모두 불러와지지 않은 상태에서도 먼저 불러올 수 있는 컴포넌트를 불러오고 나중에 랜더링이 오래걸리는 페이지를 불러온다.</p>
</blockquote>
<p>dynamic page에서 자주 사용한다. 
static page에 비해 미리 페이지를 그릴 수 없어 페이지 응답이 오래 걸릴 때 스트리밍을 사용해 빠르게 랜더링이 되는 부분만 먼저 보여주고 오래 걸리는 컴포넌트는 후속으로 가져온다.</p>
<h1 id="2-사용법">2. 사용법</h1>
<h2 id="1-페이지-스트리밍-적용">1) 페이지 스트리밍 적용</h2>
<p>폴더 내 loading.js나 loading.tsx 파일을 만든다. 그러면 같은 폴더와 하위 폴더의 dynamic page 파일이 로딩중일 때 만든 loading 파일이 대체 ui로 스트리밍된다.</p>
<p><strong>주의사항/특징</strong></p>
<ul>
<li>해당 폴더와 하위 폴더 모두 적용이 된다.</li>
<li>모든 페이지에 스트리밍 되는 것이 아니라 async가 붙어 비동기로 작동하는 페이지 컴포넌트에만 제공한다. 동기적인 컴포넌트라면 적용되지 않는다.</li>
<li>loading 파일은 페이지 컴포넌트에만 적용된다. 일반적인 컴포넌트는 적용불가능</li>
<li>loading 파일은 쿼리스트링이 변경될때는 트리거 되지 않는다. - 쿼리스트링의 값이 바뀌면 적용되지 않는다.</li>
</ul>
<h2 id="2-컴포넌트-스트리밍">2) 컴포넌트 스트리밍</h2>
<p>컴포넌트와 쿼리스트링에서 스트리밍을 사용하는 방법이다.</p>
<pre><code>import { Suspense } from &quot;react&quot;;


 &lt;Suspense key={searchParams.q || &#39;&#39;}  fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
      &lt;SearchResult q={searchParams.q || &quot;&quot;}/&gt;
    &lt;/Suspense&gt;</code></pre><ol>
<li>suspense를 import해온다.</li>
<li>원하는 컴포넌트를 suspens로 감싼다.</li>
<li>fallback 함수를 통해 대체 ui를 설정한다.</li>
<li>query string이 있을 때는 바뀌는 key값을 적용해준다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 클라이언트 라우터 캐시]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EC%BA%90%EC%8B%9C</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-%EC%BA%90%EC%8B%9C</guid>
            <pubDate>Sat, 14 Dec 2024 08:09:49 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>브라우저에 저장되는 캐시로 페이지 이동의 효율성을 위해 페이지의 일부 데이터를 캐싱</p>
</blockquote>
<p>여러 개의 페이지가 공통된 레이아웃을 사용하면 브라우저에서 여러번 같은 내용을 요청하는 비효율 성이 있다.
이를 해결하기 위해 클라이언트 라우터 캐시를 만들어 페이지에 접속하려할 때 RSC payload의 값 중 레이아웃의 값만 따로 모아 저장한다.
새로운 페이지를 요청할때 중복되는 값은 클라이언트 라우터 캐시에서 가져와서 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 라우트 세그먼트 옵션]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EB%9D%BC%EC%9A%B0%ED%8A%B8-%EC%84%B8%EA%B7%B8%EB%A8%BC%ED%8A%B8-%EC%98%B5%EC%85%98</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EB%9D%BC%EC%9A%B0%ED%8A%B8-%EC%84%B8%EA%B7%B8%EB%A8%BC%ED%8A%B8-%EC%98%B5%EC%85%98</guid>
            <pubDate>Sat, 14 Dec 2024 07:48:20 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>강제로 특정 페이지를 static 또는 dynamic 페이지로 설정하는 등 페이지의 동작을 강제로 설정</p>
</blockquote>
<h1 id="2-사용법">2. 사용법</h1>
<p>약속된 이름의 변수를 설정하고 값을 설정해서 내보내면 페이지의 설정을 강제로 조정할 수 있는 기능 - 라우트 세그먼트 옵션</p>
<pre><code>export const dynamic = &quot;옵션&quot;;</code></pre><p>특정 페이지의 유형을 강제로 static, dynamic 페이지로 설정</p>
<p>옵션
    1. auto : 기본값, 아무것도 강제하지 않음
    2. force-dynamic : 페이지를 강제로 dynamic 페이지로 설정
    3. force-static : 페이지를 강제로 static 페이지로 설정 - 모든 dynamic 요소를 static으로 작동되게 함
    4. error : 페이지를 강제로 static 페이지로 설정 (dynamic 요소가 있다면 오류를 반환)</p>
<p><strong>특별한 상황이 아니라면 권장되지 않음. 자연스럽게 가자</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 풀라우트 캐시 (Full Route Cache) - 최적화]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%ED%92%80%EB%9D%BC%EC%9A%B0%ED%8A%B8-%EC%BA%90%EC%8B%9C-Full-Route-Cache</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%ED%92%80%EB%9D%BC%EC%9A%B0%ED%8A%B8-%EC%BA%90%EC%8B%9C-Full-Route-Cache</guid>
            <pubDate>Thu, 12 Dec 2024 14:08:25 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>Next 서버측에서 빌드타임에 특정 페이지의 랜더링 결과를 캐싱하는 기능</p>
</blockquote>
<p>캐싱된 특정 페이지를 다시 요청하면 캐시에 저장된 내용을 전송한다.</p>
<h1 id="2-특징">2. 특징</h1>
<p><strong>Static Page에만 full route cache가 적용된다.</strong></p>
<h2 id="dynamic-page-vs-static-page">Dynamic page vs Static page</h2>
<h3 id="dynamic-page">Dynamic Page</h3>
<blockquote>
<p>특정 페이지가 접속 요청을 받을 때 마다 변화가 생기거나 데이터가 달라질 경우</p>
</blockquote>
<ol>
<li>캐시 되지 않는 Data Fetching을 사용할 경우 ex) {cache:no-store}</li>
<li>동적함수(쿠키, 헤더, 쿼리스트링)을 사용하는 컴포넌트가 있을 때</li>
</ol>
<h3 id="static-page">Static Page</h3>
<blockquote>
<p>Dynamic Page가 아니면 모두 Static Page - default</p>
</blockquote>
<p>빌드 타임에 풀 라우트 캐시로 저장된다.</p>
<h2 id="revalidate-갱신">Revalidate (갱신)</h2>
<p>설정한 시간이 지나면 갱신이 되도록 할 수 있다.</p>
<h2 id="최적화하기">최적화하기</h2>
<ul>
<li><p>Dynamic Page를 Static Page로 변경해주면 최적화를 할 수 있다.
동적함수가 없는 컴포넌트에서 캐시를 저장할 수 있는 항목을 캐싱하도록 설정 (revaliate 옵션도 static으로 반영됨)</p>
</li>
<li><p>동적함수가 있는 페이지에서 캐시를 저장하도록 설정하면 조금 더 빠르게 최적화가 가능하다. ({cache:&quot;force-cache&quot;}</p>
</li>
<li><p>동적 경로를 가지는 페이지(쿼리스트링 등등)를 Static page로 설정하려면 generateStaticParams라는 약속된 이름의 함수를 통해 return으로 어떤 url 파라미터가 빌드타임에 존재하는지 설정</p>
<pre><code>  export function generateStaticParams () {
    return [{id:&quot;1&quot;}, {id:&quot;2&quot;}, {id:&quot;3&quot;}]
  }</code></pre><ul>
<li><p>위 함수가 존재하는 페이지는 데이터 캐싱이 되지 않는 페이지라도 강제로 static이 된다.</p>
</li>
<li><p>return에는 문자열로만 데이터 설정</p>
</li>
<li><p>page router의 getStaticPaths와 비슷</p>
<p>  generateStaticParams값으로 명시하지 않은 값을 404페이지로 보내려면</p>
<pre><code>    export const dynamicParams = false;
    를 generateStaticParams 위에 입력</code></pre></li>
</ul>
</li>
</ul>
<h4 id="번외">번외</h4>
<p>빌드타임에 클라이언트 동작을 하는 함수를 불러오려면 오류가 생긴다.
이때 suspense(컴포넌트 스트리밍)로 클라이언트 동작을 하는 컴포넌트를 묶어주면 해결</p>
<pre><code>import {  Suspense } from &quot;react&quot;;

&lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
  &lt;Searchbar /&gt; // 쿼리스트링이 있는 컴포넌트
&lt;/Suspense&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 리퀘스트 메모이제이션(Request Memoization)]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EB%A6%AC%ED%80%98%EC%8A%A4%ED%8A%B8-%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98Request-Memoization</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EB%A6%AC%ED%80%98%EC%8A%A4%ED%8A%B8-%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98Request-Memoization</guid>
            <pubDate>Thu, 12 Dec 2024 11:49:59 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>여러 api 요청 중 중복으로 발생하는 요청을 캐싱해서 한반만 요청 수 있게 자동으로 데이터 페칭을 최적화</p>
</blockquote>
<p>동일한 api호출이 반복되면 리퀘스트 메모이제이션으로 캐싱하여 동일한 요청에 대응한다.</p>
<ul>
<li><p>데이터 캐시와는 다르다
여러 요청에 대한 중복에 대해서만 대응하기 때문에 랜더링이 종료되면 모든 캐시가 소멸된다.
만약 접속이 여러번 일어나면 그때마다 리퀘스트 메모이제이션이 작동
&lt;-&gt; 데이터 캐시는 서버가 중단되기 전까지 저장된다.</p>
</li>
<li><p>앱라우터 방식에서는 api를 필요한 컴포넌트에서 요청하기 때문에 중복된 요청이 있을 수 있다. 그래서 해당 기능이 존재한다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 데이터 캐시]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%BA%90%EC%8B%9C</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%BA%90%EC%8B%9C</guid>
            <pubDate>Tue, 10 Dec 2024 12:57:08 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>fetch 메서드를 활용해 불러온 데이터를 Next 서버에 보관하는 기능(캐싱)</p>
</blockquote>
<h1 id="2-사용법">2. 사용법</h1>
<p>fetch메서드의 두번째 인수에 객체 형태로 설정하여 사용가능하다. (axois 등에서는 불가)</p>
<pre><code>const response = await fetch(`~/api`, {옵션});</code></pre><h2 id="옵션">옵션</h2>
<h3 id="1-cache-force-cache">1) {cache: &quot;force-cache&quot;}</h3>
<pre><code>- 요청 결과는 무조건 캐싱한다. 
- 최초 한번만 호출됨
- 14버전에서 기본값으로 지정</code></pre><h3 id="2-cache-no-store">2) {cache: &quot;no-store&quot;}</h3>
<pre><code>- 데이터 페칭의 결과를 저장하지 않는 옵션
- 캐싱을 아예 하지 않는다.
- 15버전에서 기본값으로 지정</code></pre><h3 id="3-next-revalidate--시간-">3) {next: {revalidate : 시간 }}</h3>
<pre><code>- 특정 시간을 주기로 캐시를 업데이트
- page router의 ISR 방식과 유사</code></pre><h3 id="4-next-tags--a-">4) {next: {tags : [&#39;a&#39;] }}</h3>
<pre><code>- 요청이 들어왔을 때 데이터를 최신화 (on-demand Revalidate)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 데이터 페칭]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8E%98%EC%B9%AD</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8E%98%EC%B9%AD</guid>
            <pubDate>Sun, 08 Dec 2024 12:36:23 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>앱 라우터에서 데이터를 가져오는 방법</p>
</blockquote>
<h1 id="2-사용법">2. 사용법</h1>
<h4 id="페이지-라우터에서의-방법">페이지 라우터에서의 방법</h4>
<p>getServerSideProps, getStaticProps 등등 특수한 함수를 사용</p>
<ul>
<li>데이터 드릴링으로 props를 넘겨줘야하는다는 문제점이 있는데 앱라우터에서 개선</li>
</ul>
<h2 id="앱라우터">앱라우터</h2>
<blockquote>
<p>데이터가 필요한 컴포넌트에서 직접 데이터를 페칭한다.</p>
</blockquote>
<h3 id="사용법">사용법</h3>
<ol>
<li>데이터를 불러올 컴포넌트에 async를 붙여 비동기로 불러올 수 있게 한다.</li>
<li>```
const response = await fetch(<code>서버주소</code>);
if(!response.ok) {
 return <div>Error...</div>
}
const 이름 = await response.json();</li>
<li>2번과 같이 await와 함께 데이터를 불러오는 함수를 컴포넌트 안에서 작성가능</li>
<li>만약 한페이지에 여러개의 api를 사용한다고 하면 컴포넌트를 분리해 각각 api에 맞는 컴포넌트를 작성 후 해당 컴포넌트를 넣어준다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 네비게이팅]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EB%84%A4%EB%B9%84%EA%B2%8C%EC%9D%B4%ED%8C%85-7uue82mx</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EB%84%A4%EB%B9%84%EA%B2%8C%EC%9D%B4%ED%8C%85-7uue82mx</guid>
            <pubDate>Wed, 04 Dec 2024 13:40:56 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<p>기존 페이지 라우터 방식과 비슷하지만 서버 컴포넌트라는 개념이 추가되어 방식이 변경된다.</p>
<p>앱 라우터 js 번들에는 클라이언 컴포넌트만 포함되어 서버 컴포넌트는 포함이 되지않아 서버 컴포넌트로 구성된 RSC Payload도 함께 전달해주어야한다.</p>
<h1 id="2-사용법">2. 사용법</h1>
<h2 id="기본">기본</h2>
<pre><code>import Link from &quot;next/link&quot;;

 &lt;Link href={&#39;/&#39;}&gt;index&lt;/Link&gt;</code></pre><p>link를 import 해주고 link 태그를 사용하여 연결해준다.</p>
<h2 id="프로그래매틱한-이동법">프로그래매틱한 이동법</h2>
<p>함수 등을 사용하여 페이지를 이동시키는 법</p>
<pre><code>import { useRouter } from &quot;next/navigation&quot;;

export default function Searchbar() {
    const router = useRouter();

    const onSubmit = () =&gt;{
        router.push(`/search?q=${search}`);
    }

    return (
        &lt;div&gt;
            &lt;input value={search} onChange={onChangeSearch}/&gt;
            &lt;button onClick={onSubmit}&gt;검색&lt;/button&gt;
        &lt;/div&gt;
    )
}
</code></pre><ol>
<li>앱 라우터에서는 페이지 라우터와 달리 next/navigation에서 useRouter를 불러와야한다.</li>
<li>함수 내 router를 사용하여 이동할 페이지를 연결해준다.</li>
<li>연결하고 싶은 버튼에 함수를 달아준다.</li>
</ol>
<h2 id="앱라우터의-프리패칭pre-fetching">앱라우터의 프리패칭(pre-fetching)</h2>
<blockquote>
<p>링크들이 존재해 이동할 가능성이 있는 모든 페이지의 데이터를 미리 불러오는 동작</p>
</blockquote>
<ul>
<li>개발모드에서 확인할 수 없고 npm run build -&gt; npm run start로 프로덕션 모드로 확인가능</li>
</ul>
<p>앱 라우터의 모든 페이지는 static 하거나 dynamic 페이지로 나뉜다.
staic 페이지 : 빌드 타임이 미리 생성된 정적인 페이지 -rsc와 js 번들 둘다 불러옴
dynamic 페이지 : 브라우저가 요청할 때 마다 생성되는 동적인 페이지 - rsc만 프리 패칭 후 js 번들은 페이지 이동이 발생했을 때만 불러온다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 리액트 서버 컴포넌트]]></title>
            <link>https://velog.io/@s_ongt_aemin/Next.js-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%84%9C%EB%B2%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@s_ongt_aemin/Next.js-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%84%9C%EB%B2%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Sat, 30 Nov 2024 16:22:36 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<blockquote>
<p>오직 서버측에서만 실행되는 컴포넌트</p>
</blockquote>
<p>기존 next.js는 js bundle을 브라우저로 넘길 때 &#39;react hook, 이벤트 핸들러&#39; 등 상호작용이 있는 컴포넌트와 상호작용이 없는 컴포넌트 둘 다 넘긴다.</p>
<p>상호작용이 없는 컴포넌트는 굳이 브라우저에 여러번 전송(hydration)할 필요가 없다.</p>
<p>페이지 라우터는 모든 컴포넌트를 묶어 브라우저에 전송하기 때문에 js bundle의 용량이 커지게 된다.</p>
<p>그래서 이런 문제를 해결하기 위해 서버 컴포넌트를 사용한다.</p>
<h1 id="2-서버-컴포넌트">2. 서버 컴포넌트</h1>
<p>딱 한번만 실행되고 여러번 실행될 필요가 없다.</p>
<p>기본적으로 모든 컴포넌트가 서버 컴포넌트이기 때문에 클라이언 컴포넌트(기본 상호작용이 있는 컴포넌트)만 따로 설정해주면 된다.  - console.log가 찍히지 않음</p>
<h2 id="사용">사용</h2>
<blockquote>
<p>&quot;use client&quot;</p>
</blockquote>
<p>클라이언트 컴포넌트로 만들고자 하는 파일 맨위에 디렉티브(지시자- 상단의 문구)를 입력해준다. (따옴표까지)</p>
<h2 id="서버-컴포넌트와-클라이언트-컴포넌트의-구분">서버 컴포넌트와 클라이언트 컴포넌트의 구분</h2>
<ul>
<li><p>서버 컴포넌트
HTML의 고유의 기능인 link 등은 서버</p>
</li>
<li><p>클라이언트 컴포넌트
useState, useEffect 등 상호작용이 일어나는 페이지</p>
</li>
</ul>
<h2 id="주의사항">주의사항</h2>
<p><strong>1. 서버 컴포넌트에는 브라우저에서 실행될 코드가 포함되어서는 안된다.</strong></p>
<ul>
<li>상호작용이 일어나면 안된다. (useEffect, useState, onChange, onClick, 라이브러리 ...)</li>
</ul>
<p><strong>2. 클라이언트 컴포넌트는 클라이언트만 실행되지 않는다.</strong></p>
<ul>
<li>처음에 서버 클라이언트와 같이 실행이 된다.</li>
</ul>
<p><strong>3. 클라이언트 컴포넌트에서 서버 컴포넌트를 import 할 수 없다.</strong></p>
<ul>
<li>초반에는 두 컴포넌트가 같이 실행이 되어 동작하지만 브라우저에서 하이드레이션을 위해 한번 더 실행이 될 때는 클라이언트 컴포넌트의 코드는 존재하지만 서버컴포넌트는 존재하지 않아 없는 코드를 import하게 되어 가능하지 않다고 한다.</li>
<li>컴포넌트 양이 많아지게 되면 불편함이 따른다 이때 next.js는 서버 컴포넌트를 자동으로 클라이언트 컴포넌트로 바꿔준다. 최대한 서버 컴포넌트로 사용하기</li>
<li>그래서 클라이언트 컴포넌트에 children을 사용하여 서버 컴포넌트를 넘겨주는 형식으로 사용</li>
</ul>
<p><strong>4. 서버 컴포넌트에서 클라이언트 컴포넌트에게 직렬화 되지 않는 Props는 전달 불가하다.</strong></p>
<ul>
<li>직렬화 : 객체, 배열, 클래스 등의 복잡한 구조의 데이터를 네트워크 상으로 전송하기 위해 아주 단순한 형태(문자열, byte)로 변환하는 것</li>
<li>서버 컴포넌트에서 클라이언트 컴포넌트로 함수같은 값들은 props로 전달할 수 없다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>