<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>2_2wisdom.log</title>
        <link>https://velog.io/</link>
        <description>응애 나 애기 개발자</description>
        <lastBuildDate>Mon, 13 Feb 2023 17:11:11 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>2_2wisdom.log</title>
            <url>https://velog.velcdn.com/images/2_2wisdom/profile/ef405120-f136-4710-b2c7-ce2985201094/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 2_2wisdom.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/2_2wisdom" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[React + Typescript 로 google-FontAwesome 사용해서 아이콘 넣기]]></title>
            <link>https://velog.io/@2_2wisdom/Next.js-Typescript-%EB%A1%9C-google-FontAwesome-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%EC%95%84%EC%9D%B4%EC%BD%98-%EB%84%A3%EA%B8%B0</link>
            <guid>https://velog.io/@2_2wisdom/Next.js-Typescript-%EB%A1%9C-google-FontAwesome-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%EC%95%84%EC%9D%B4%EC%BD%98-%EB%84%A3%EA%B8%B0</guid>
            <pubDate>Mon, 13 Feb 2023 17:11:11 GMT</pubDate>
            <description><![CDATA[<p>웹페이지를 만들다보면 아이콘이 정말정말 많이 필요한데, 그 때 잘 쓰게되는 페이지가 바로 <a href="https://fontawesome.com/">Google-FontAwesome</a> 이다!
하지만 HTML로 코딩할때는 그냥 <code>i</code> 태그 넣어서 간편하게 썼던것이 React로 넘어오면서... typescript로 쓰면서... 조금 복잡해진다... (나만 그런가?)</p>
<p>나에게는 기록이 되고, 누군가에게는 도움이 될 수 있도록 여기에 차근차근 기록해볼테니 함께 해보자 !</p>
<h2 id="fontawesome-설치-">FontAwesome 설치 !</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/bc02d31b-cb19-442f-95d9-f5b3ab71efd6/image.png" alt="fontawesome">
난 공식문서만 보고 해결할 때 짜릿함을 느끼는데, 그래서 이렇게 친절한 공식문서를 너무 사랑한다 ... 폰트어썸에는 이렇게 프레임워크마다 사용방법이 잘 나와있는데, 내가 사용할 것은 React 기반의 next.js 니까 <a href="https://fontawesome.com/docs/web/use-with/react/">React</a>로 보겠다 !</p>
<ol>
<li><p>SVG 코어 설치
<code>yarn add @fortawesome/fontawesome-svg-core</code>
폰트어썸의 핵심 패키지이다.</p>
</li>
<li><p>아이콘 패키지 설치
<code>yarn add @fortawesome/free-solid-svg-icons</code>
<code>yarn add @fortawesome/free-regular-svg-icons</code>
폰트어썸에는 pro가 있고 free가 있는데, 나는 무료 아이콘만 쓸것이기 때문에 free로 설치해주었다. solid랑 regular 두가지가 있는데, 본인이 사용할 것만 다운받으면 될 것 !
보니까 solid는 속이 꽉 찬 아이콘이고, reqular는 선으로만 그려진 아이콘인듯 하다 !</p>
</li>
<li><p>React 구성요소 설치
<code>yarn add @fortawesome/react-fontawesome@latest</code>
React에서 사용할 것이기 때문에 React 구성요소 설치까지 하면 필요한 설치는 끝 !</p>
</li>
</ol>
<h2 id="필요한-코드-추가">필요한 코드 추가</h2>
<hr>
<p>폰트어썸을 next.js, 그리고 typescript로 쓰려면 몇가지 추가해야할 코드가 있는데, 하나씩 해보도록 하자</p>
<pre><code class="language-ts">// pages &gt; _app.tsx

import { config } from &#39;@fortawesome/fontawesome-svg-core&#39;
import &#39;@fortawesome/fontawesome-svg-core/styles.css&#39;
config.autoAddCss = false</code></pre>
<p>공식문서에 따르면
<em>&quot;폰트어썸은 next.js랑 잘 어울리지만 그냥쓰면 왕큰 아이콘을 보게되니까 이 코드를 추가해서 쓰세연&quot;</em>
이라고 써있다. (아마도?)</p>
<h2 id="아이콘-추가해보기-">아이콘 추가해보기 !</h2>
<hr>
<pre><code class="language-ts">import { FontAwesomeIcon } from &#39;@fortawesome/react-fontawesome&#39;
...
&lt;FontAwesomeIcon icon={faCalendar} /&gt;
  ...</code></pre>
<p>React에서 아이콘을 사용하기위해서는 위와같은 식으로 사용하면 된다 !
<code>&lt;FontAwesomeIcon icon={사용할 아이콘 이름} /&gt;</code></p>
<h2 id="with-typescript-">with typescript ?</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/547bad01-10ae-4818-ab2c-661e8aef79f1/image.png" alt="error">
시키는대로 했는데 또 에러가 났다 ...!
<code>index.d.ts(24, 3): The expected type comes from property &#39;icon&#39; which is declared here on type &#39;IntrinsicAttributes &amp; FontAwesomeIconProps&#39;</code></p>
<p>찬찬히 읽어보니 <code>IntrinsicAttributes</code> <code>FontAwesomeIconProps</code> 둘 중 타입 하나 지정해줘 ~ 라는 뜻 같은데 ...</p>
<p>에러를 구글에 쳐보니 <a href="https://stackoverflow.com/questions/67708429/typescript-facing-problem-trying-to-pass-font-awsome-icon-as-object-to-child-c">이런 해답</a>을 주셨다.</p>
<pre><code class="language-ts">import { IconProp } from &quot;@fortawesome/fontawesome-svg-core&quot;;
...
&lt;FontAwesomeIcon icon={faCalendar as IconProp} /&gt;
  ...</code></pre>
<p>간단하다 ! IconProp을 불러와서 지정해주면 끝 !</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/356db564-7a14-4dd7-8dfe-c8cee0fec008/image.png" alt="calendar icon">
이렇게 뽀짝한 아이콘 불러오기 완료 !</p>
<hr>
<p>typescript 에러는 해결하는 방법이 여러가지인데, 내가 아직 잘 못쓰다보니까 공식문서에있는 방법으로는 해결하지 못했다 ㅠ 더 좋은 방법이 있다면 다음에 또 수정해볼 것 !</p>
<p>조금 아쉬운점은 github 아이콘같은 브랜드 아이콘의 경우 pro로, 유료 서비스인 것 같았다. 그런애들은 svg파일을 다운로드해서 사용하게 되었다.</p>
<p>아무튼 이제 아이콘들 잘 불러올 수 있으니까 다시 힘내서 꾸며보자 !</p>
<p><strong>Github</strong>
<a href="https://github.com/2wisdom/dear-diary">https://github.com/2wisdom/dear-diary</a></p>
<p><strong>Reference</strong>
<a href="https://fontawesome.com/docs/web/use-with/react/">fontawesome | with react</a>
<a href="https://fontawesome.com/docs/web/use-with/react/use-with#next-js">fontawesome | with next-js</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[next-auth로 kakao 로그인 구현하기]]></title>
            <link>https://velog.io/@2_2wisdom/next-auth%EB%A1%9C-kakao-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@2_2wisdom/next-auth%EB%A1%9C-kakao-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 11 Feb 2023 14:17:55 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@2_2wisdom/Next.js%EC%99%80-Prisma-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0">지난_이야기</a></p>
<p>저번시간에는 Next.js랑 Prisma를 연결했다 ! 이번에는 next-auth로 kakao 소셜로그인을 구현해보도록 하자. 레쓰기리잇 !</p>
<h2 id="next-auth-설치하기">Next-Auth 설치하기</h2>
<hr>
<p><code>yarn add next-auth</code></p>
<p>일단 next-auth를 설치 !</p>
<p>그런 다음 pages &gt; api &gt; auth &gt; [...nextauth].ts 를 만들어서 <code>providers</code>에 <code>kakaoProvider</code>를 추가해준다 !</p>
<pre><code class="language-ts">// pages &gt; api &gt; auth &gt; [...nextauth].ts

import NextAuth from &quot;next-auth&quot;;
import KakaoProvider from &quot;next-auth/providers/kakao&quot;;

export const authOptions = {
  providers: [
    KakaoProvider({
      clientId: process.env.KAKAO_CLIENT_ID!,
      clientSecret: process.env.KAKAO_CLIENT_SECRET!,
    }),
  ],
};
export default NextAuth(authOptions);</code></pre>
<blockquote>
<p>❗ 처음에 공식문서대로 작성했더니</p>
</blockquote>
<pre><code class="language-ts">    KakaoProvider({
      clientId: process.env.KAKAO_CLIENT_ID,
      clientSecret: process.env.KAKAO_CLIENT_SECRET,
    }),</code></pre>
<p><code>clientId</code>, <code>clientSecret</code> 이부분에 해당 오류가 떴다.
<code>Type &#39;string | undefined&#39; is not assignable to type &#39;string&#39;.
  Type &#39;undefined&#39; is not assignable to type &#39;string&#39;.ts(2322)</code>
 번역기를 돌려보면
 <code>&#39;string | defined&#39; 형식은 &#39;string&#39; 형식에 할당할 수 없습니다.
&#39;undefined&#39; 유형을 &#39;string&#39;.ts(2322)&#39; 유형에 할당할 수 없습니다</code>
대충 이런 말인데, undefined 할 수 없기때문에 반드시 보장해주어야한다는 말 !
그래서 반드시 있어야한다는 뜻의 <code>process.env.KAKAO_CLIENT_ID || &#39;&#39;</code> 또는 <code>process.env.KAKAO_CLIENT_ID!</code> 를 넣어주면 해결 !</p>
<h2 id="kakao-연결">kakao 연결</h2>
<hr>
<p>자, 그럼 저 Provider 안에 있는  <code>process.env.KAKAO_CLIENT_ID</code> 와, <code>process.env.KAKAO_CLIENT_SECRET</code>를 넣어줘야하지 않겠는가 ?</p>
<p>일단 <a href="https://developers.kakao.com/console/app">kakao_developers</a>에서 앱을 등록해주자 !</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/98637264-73b1-4917-bf10-dad048a9cb13/image.png" alt="애플리케이션_추가하기">
추가해줍니다 ! 뭔가 진짜 개발자가 된 기분 ... (코쓱</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/c81ae44c-27f5-492e-90f4-f1e26f11a05b/image.png" alt="카카오로그인_활성화">
제품설정 &gt; 카카오로그인에 활성화를 ON 해주고, Redirect URI에 해당 주소를 넣어준다.
<code>http://localhost:3000/api/auth/callback/kakao</code>
나중에 배포하게 되면 <code>localhost:3000</code> 쪽에 배포한 URL를 넣어주면 될 것 !</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/c5168b2a-9ec2-44c1-a733-a4f4761cf507/image.png" alt="동의항목">
다음은 동의항목에서 카카오로그인 할 때 나오는 동의목록을 설정해준다.</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/414c1909-82c8-4ac6-941e-a2c95fa7cd0f/image.png" alt="로그인예시"></p>
<p>체크하고 나면 이런식으로 예시를 보여준다 !
난 일단... 닉네임만 쓸것이기 때문에 닉네임만 체크를 해주었다 !</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/226d301c-0ab1-49fa-865a-4a26535c5381/image.png" alt="ClientId"></p>
<p>그리고 다른 메뉴에 요약 정보가 있는데, 이곳에 있는 <code>REST API키</code> 가 <code>KAKAO_CLIENT_ID</code> 가 되는것이다 ! 확인해서 넣어주면 된다.</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/402f5b76-493b-4ece-8cad-9ad922de70c0/image.png" alt="ClientSecret"></p>
<p>다음은 제품설정 &gt; 보안 메뉴에 가면 카카오로그인 ON을 할 수 있는데, 거기에 나오는 코드를 <code>KAKAO_CLIENT_SECRET</code> 에 넣어주면 된다.</p>
<pre><code class="language-jsx">// .env

# next-auth
NEXTAUTH_SECRET = &quot;REST API 키&quot;
KAKAO_CLIENT_ID = &quot;Client Secret 코드&quot;</code></pre>
<p>대충 이런식으로 <code>.env</code> 에 넣어주면 끝 !</p>
<h2 id="버튼-만들기">버튼 만들기</h2>
<hr>
<p>모든 소셜로그인 버튼에는 해당 회사에서 정해준 디자인가이드가 있다는것을 알고 있었는가 ? 난 이번에 처음 알았다.</p>
<p><a href="https://developers.kakao.com/docs/latest/ko/kakaologin/design-guide">카카오 로그인 | 디자인 가이드</a></p>
<p>위 문서에 가면 어떻게 만들어야한다는 디자인 가이드가 나와있다. 해당 메뉴얼대로 만들어줘야 한다 !</p>
<pre><code class="language-ts">import { useSession, signIn } from &quot;next-auth/react&quot;;
import Image from &quot;next/image&quot;;
import { KakaoBtn } from &quot;../styles/AuthStyle&quot;;
export default function KakaoLogin() {
  const { data: session } = useSession();

  return (
    &lt;&gt;
      &lt;KakaoBtn onClick={() =&gt; signIn(&quot;kakao&quot;)}&gt;
        &lt;Image
          src=&quot;/images/kakao_logo.png&quot;
          alt=&quot;kakao-login&quot;
          width={20}
          height={20}
        /&gt;
        &lt;a&gt;카카오 로그인&lt;/a&gt;
      &lt;/KakaoBtn&gt;
    &lt;/&gt;
  );
}
</code></pre>
<p>나는 <code>styed-component</code>로 디자인을 해주었다. 만들어준 버튼에 <code>onClick={() =&gt; signIn(&quot;kakao&quot;)</code>  이벤트를 넣어주면 로그인 만들기 완료 !</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/bb35ce2d-aa57-4364-8b14-41de461ebdd3/image.gif" alt="카카오로그인_버튼">
제법 깔쌈한 로그인버튼이 만들어졌다 ! 이걸로 kakao 로그인 완료 !</p>
<h2 id="callback-url">callback URL</h2>
<hr>
<p>내가 기획했던것은
로그인 -&gt; 유저페이지
로그아웃 -&gt; 메인페이지
였는데, React에서는 userNavigate로 했던것을 Next auth에서는 더욱 쉽게 할 수 있다 !</p>
<pre><code class="language-ts">onClick={() =&gt; signIn(&quot;kakao&quot;, { callbackUrl: &quot;/user&quot; })}</code></pre>
<pre><code class="language-ts">onClick={() =&gt; signOut({ callbackUrl: &quot;/&quot; })}</code></pre>
<p>signIn과 signOut 모두 <code>callbackUrl</code> 로 간단하게 리다이렉트 할 수 있다 !</p>
<h2 id="배포-후-서버-오류">배포 후 서버 오류?</h2>
<hr>
<p>지금 만들고있는 페이지를 vercel를 통해 배포하고있었는데, 로컬에서는 잘 되던 로그인이 배포한 페이지에서는 되지 않는것을 확인했다.
이세상에 정말 쉬운게 하나도 없다 ...</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/4b2fcf38-9814-46f6-8af1-8a3fafc969f7/image.png" alt="server_error"></p>
<p>찾아보니 해당 서버에러는 <a href="https://next-auth.js.org/configuration/options#secret">next_auth | secret</a> 문서로 해결할 수 있다 !</p>
<p>vercel 환경변수에 암호화하기 위한 키를 넣어줘야 하는데,</p>
<p><code>openssl rand -base64 32</code>
터미널에 해당 명령어를 쳐주면 암호화 키를 준다.
<img src="https://velog.velcdn.com/images/2_2wisdom/post/79f353a4-7773-4034-b6d1-684518b122fd/image.png" alt="Project_Settings">
해당 암호화 키를 vercel 환경변수에 <code>NEXTAUTH_SECRET</code> 의 Value로 넣어주면 해결 !</p>
<hr>
<p>이걸로 카카오 로그인이 완료되었다 !
그치만 아직 디테일한 부분이 더 들어가야한다...
로그인 후 마이페이지로 페이지 이동도 넣어줘야하고,
가장 중요한건 세션만료 exp 기간이 너무 길어서 로그아웃을 하지않으면 컴퓨터를 껐다켜도 로그인이 풀리지 않는다...</p>
<p>일단 로그인으로 너무 힘을 빼서 재미있는 기능구현부터 해볼 예정 !</p>
<p>다시 빠이팅해부장 🔥🔥🔥</p>
<p><strong>Github</strong>
<a href="https://github.com/2wisdom/dear-diary">https://github.com/2wisdom/dear-diary</a></p>
<p><strong>Reference</strong>
<a href="https://next-auth.js.org/">next-auth 공식문서</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js와 Prisma 연결하기]]></title>
            <link>https://velog.io/@2_2wisdom/Next.js%EC%99%80-Prisma-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@2_2wisdom/Next.js%EC%99%80-Prisma-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 03 Feb 2023 18:25:48 GMT</pubDate>
            <description><![CDATA[<p>이번에 다이어리 웹을 하기로 마음을 먹고서 백엔드를 어떤걸로 할지 고민을 많이했다.
보니까 Next.js에는 Prisma를 많이 사용하길래 Prisma를 사용하기로 결정 !</p>
<p>공식문서를 보고 찬찬히 따라해보기로 했다 레쓰기릿</p>
<h2 id="prisma-설치--db연결">prisma 설치 &amp; DB연결</h2>
<hr>
<p><code>npm install prisma --save-dev</code>
<code>npx prisma init</code></p>
<p>위 명령어로 prisma를 설치해주자 !</p>
<p>설치를 했다면 이제 <code>.env</code> 파일에 DB주소를 넣어줘야하는데,</p>
<blockquote>
<p><code>.env</code>파일은 꼭 루트 디렉토리에 넣어주는것과 <code>gitignore</code> 안에 넣어서 내 중요한 DB정보를 깃헙에 올리지않도록 주의하자 !</p>
</blockquote>
<pre><code>// .env
DATABASE_URL=&quot;postgresql://postgres:[YOUR-PASSWORD]@db.ObubJTKrJYcPSkdsWqms.supabase.co:5432/postgres&quot;</code></pre><p>공식문서에 <em>&quot;이렇게 작성하세요~&quot;</em> 하고 아주 친절하게 나와있는데도 나같은 새머리는 조금 헤메었기에 ... 자세히 적어보자면</p>
<pre><code>DATABASE_URL=&quot;DB종류://username:password@DB주소/database&quot;</code></pre><p>나는 <code>mysql</code>을 쓸것이기 때문에 DB종류에 <code>mysql</code>을 넣었다.</p>
<p>이렇게 <code>.env</code> 파일을 만들었으면 연결 완료 !</p>
<h2 id="schema-만들기">Schema 만들기</h2>
<hr>
<p><code>schema.prisma</code> 라는 파일을 만들어주자!</p>
<p>사람마다 <code>prisma</code> 라는 폴더를 만들어서 그 안에 해당 파일을 넣는 사람도 있던데 나는 일단 그냥 루트디렉토리에 넣었다.</p>
<pre><code class="language-jsx">datasource db {
  provider = &quot;mysql&quot;
  url      = env(&quot;DATABASE_URL&quot;)
}

generator client {
  provider = &quot;prisma-client-js&quot;
}

model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  password  String
  posts     Post[]
}

model Post {
  id        Int        @id @default(autoincrement())
  title     String
  image     String?
  content   String?
  date      DateTime
  author    User       @relation(fields: [authorId], references: [id])
  authorId  Int
}</code></pre>
<p><a href="https://www.prisma.io/docs/concepts/components/prisma-schema/data-model">Prisma | Data model</a></p>
<p>백엔드는 아직 너무너무 어렵기때문에... 일단 위에 공식문서를 보면서 간단하게 나한테 필요한 정보들로 스키마를 짜보았다 !</p>
<p><code>npx prisma db push</code></p>
<p>스키마를 다 짰다면 위 명령어로 prisma db에 푸시를 해보자 !
뭔가 질문하기도 하는데 읽어보고 <code>y</code> 눌러서 넘어가면 된다.</p>
<p><code>npx prisma studio</code></p>
<p>위 명령어를 치면 <a href="http://localhost:5555">http://localhost:5555</a> 에 studio가 켜진다!
<img src="https://velog.velcdn.com/images/2_2wisdom/post/8cc00509-7a82-422a-8a38-2fb5d820adc1/image.png" alt="Prisma-Studio-User"> 보면 내가 짠대로 잘 들어와있는것을 확인 !
<img src="https://velog.velcdn.com/images/2_2wisdom/post/4d8c48e8-5034-4901-bc07-267599790f3d/image.png" alt="HeidiSQL"> DB에도 잘 들어와있다 !</p>
<h2 id="prisma-client-설치-및-생성">Prisma Client 설치 및 생성</h2>
<hr>
<p>Prisma를 사용해서 Next.js 에서 데이터베이스를 엑세스하려면 Prisma Client를 설치해야 한다고 써있다.</p>
<p><code>yarn add @prisma/client</code></p>
<p>일단 Prisma Client를 설치해주고 (난 yarn을 쓰기때문에 yarn으로 설치했다)</p>
<p><code>npx prisma generate</code></p>
<p>해당 명령어로 스키마 파일이 변경될 때마다 업데이트를 해줘야 한다.</p>
<p><code>mkdir lib &amp;&amp; touch lib/prisma.ts</code></p>
<p>공식문서 너무 친절... <code>lib</code> 폴더를 만들고 그 안에 <code>prisma.ts</code> 를 만들어주는 명령어다...</p>
<pre><code class="language-ts">import { PrismaClient } from &#39;@prisma/client&#39;;

let prisma: PrismaClient;

if (process.env.NODE_ENV === &#39;production&#39;) {
  prisma = new PrismaClient();
} else {
  if (!global.prisma) {
    global.prisma = new PrismaClient();
  }
  prisma = global.prisma;
}

export default prisma;</code></pre>
<p>그리고 그 안에 이렇게 코드를 짜주면 끝...인데?</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/8546c09d-2523-4aaf-ab5c-bf1b9dde6f34/image.png" alt="">아니 분명 공식문서에는 저렇게만 써주라고 되어있었다구요 ;ㅅ;</p>
<p><code>Element implicitly has an &#39;any&#39; type because type &#39;typeof globalThis&#39; has no index signature.ts(7017)</code>
보니까 빌어먹을 타입스크립트의 타입오류였는데,</p>
<p><del>깃헙의 개발자들이 이미 <a href="https://github.com/prisma/prisma/discussions/10037">멋진 답안</a>을주신게 있어서 쉽게 해결할 수 있었다.</del>
타입스크립트가 global을 몰라서 생긴 오류였다.</p>
<p><strong>23.02.05. 수정!</strong>
저대로 수정했었는데 아는 개발자분께서 <a href="https://github.com/2wisdom/dear-diary/issues/10">이슈</a>를 남겨주셨다.
전역변수를 나타내는 global 은 Condition 에 제약을 받으면 안돼서 if절 밖으로 빼야한다고 알려주셔서 다시 수정 !</p>
<pre><code class="language-ts">import { PrismaClient } from &quot;@prisma/client&quot;;

let globalWithPrisma = global as typeof globalThis &amp; {
  prisma: PrismaClient;
};

let prisma: PrismaClient;

if (process.env.NODE_ENV === &quot;production&quot;) {
  prisma = new PrismaClient();
} else {
  if (!globalWithPrisma.prisma) {
    globalWithPrisma.prisma = new PrismaClient();
  }
  prisma = globalWithPrisma.prisma;
}

export default prisma;

</code></pre>
<p>진짜 끝 !</p>
<hr>
<p>이제 Prisma를 연결했으니 다음시간에는 카카오톡으로 소셜 로그인을 만들어볼것이다 !</p>
<p>누가 내 블로그를 볼지는 잘 모르겠지만 잘못된 정보나 더 좋은 방법이 있다면 꼭 알려주길 바란다 !</p>
<p><strong>Github</strong>
<a href="https://github.com/2wisdom/dear-diary">https://github.com/2wisdom/dear-diary</a></p>
<p><strong>Reference</strong>
<a href="https://vercel.com/guides/nextjs-prisma-postgres">How to Build a Fullstack App with Next.js, Prisma, and PostgreSQL</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[실험실 | Next.js와 SSR, CSR]]></title>
            <link>https://velog.io/@2_2wisdom/Next.js%EC%99%80-SSR-CSR</link>
            <guid>https://velog.io/@2_2wisdom/Next.js%EC%99%80-SSR-CSR</guid>
            <pubDate>Fri, 03 Feb 2023 16:28:22 GMT</pubDate>
            <description><![CDATA[<p>Next.js를 쓰면서 <strong>Next.js가 제일 잘하는건 SSR이다 !</strong> </p>
<p>라고 … 듣기는 했는데, SSR이란 뭘까? SSR과 같이 이야기되는 CSR은 또 뭘까?</p>
<p><a href="https://d2.naver.com/helloworld/7804182">naverD2 어서와,SSR은 처음이지?</a></p>
<p>요새는 참 똑똑한분들이 정리를 잘 해놓으시는 것 같다.
위의 글이 너무 잘 정리해놔서 내가 적을 건 없다. 너무 좋으니 한번씩 읽어봐보시길.</p>
<p>그렇다면 나는 눈으로 직접 보면서 둘의 차이점을 알아보자.</p>
<p>일단 두개의 비교를 위해 똑같이 Hello, world를 띄우는 두개의 페이지를 준비했다.</p>
<p>하나는 <code>CSR</code> 방식인 일반 <code>React.js</code>로 만들고, (나는 vite로 만들었다.)
다른 하나는 <code>SSR</code> 방식인 <code>Next.js</code> 로 만들었다.</p>
<h2 id="속도-차이">속도 차이</h2>
<hr>
<p>일단 두 페이지의 속도차이를 보자.</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/5063a12e-4c96-4eec-bd89-6c735d291b73/image.png" alt="SSR network"></p>
<p>SSR은 모든 데이터를 매핑해서 가져온다는 특징으로, 초반 로딩이 느리다고했다.</p>
<p>네트워크창을 보면 <strong>로드: 610밀리초</strong>로 나오는데,</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/84961698-9ef9-481e-b657-6f3d829c1804/image.png" alt=""></p>
<p>CSR network</p>
<p>CSR은 확실히 <strong>로드:125밀로초</strong>로 첫 렌더링은 확실히 빠르다.</p>
<h2 id="document-">document ?</h2>
<hr>
<p>next.js와 react.js 에 각각 <code>document.body</code> 를 치면 어떤 차이가 있을까 ?</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/0be6d6f6-867d-4e79-ac09-855e8c59096f/image.png" alt=""><em>왼쪽: SSR(next.js) / 오른쪽: CSR(react.js)</em></p>
<p>CSR방식인 react에서는 document.body가 정상적으로 호출되지만,
SSR방식인 next에서는 오류가 발생한다 !</p>
<p>오류를 읽어보면 <code>document is mot defined</code> 가 뜨는데 그 이유는,
사실 <code>document</code>는 <code>window.document</code> 이다…! (당연한가?)</p>
<p>그래서 CSR인 React에서는 <code>window.document</code> 를 해도 브라우저에서 호출이 된것이기 때문에 호출이 되는 반면,
SSR 방식인 Next에서는 서버에서 매핑이 된다하지않았나 ? 서버에서 만들기때문에 window가 존재하지 않는다…!</p>
<p>그래서 <code>(window.)document.body</code>가 오류가 뜨는것이다 !</p>
<p>그럼 next에서는 document를 쓰지 못하는걸까 ? <code>document.</code> 이 필요할 땐 어쩌지?</p>
<p>document가 필요한 시점은 dom이 화면에 나왔을때부터이기 때문에 <code>useEffect</code>로 컴포넌트가 마운트 되고 난 후에 쓰면 호출이 가능하다 !</p>
<pre><code class="language-jsx">import { useEffect } from &quot;react&quot;;

export default function Home() {
  useEffect(() =&gt; {
    console.log(document.body);
  });
  return (
    &lt;div&gt;
      &lt;h1&gt;
        SSR &lt;br /&gt; hello, world!
      &lt;/h1&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>대충 이런식 ?</p>
<h2 id="seo">SEO</h2>
<hr>
<p>Next.js는 SSR로, SEO가 잘된다는 장점이 있다 !</p>
<p>그렇다면 왜? SEO가 잘되는것일까 ?</p>
<p><img src="https://velog.velcdn.com/images/2_2wisdom/post/ff05430f-1ff2-4b32-93d2-d69c600c5dbc/image.png" alt=""><em>왼쪽: next.js / 오른쪽: react.js</em></p>
<p>아까 내가 각각 next.js, react.js 로 <code>hello, world!</code> 를 띄웠다.</p>
<p>그렇다면 각각 두 페이지를 Postman에다가 띄우면 어떤게 나올까?</p>
<p>(페이지 소스보기 <code>Ctrl + U</code> 로도 확인 가능하다.)</p>
<p><strong>Next.js (SSR)</strong></p>
<pre><code class="language-jsx">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;

&lt;head&gt;
    &lt;style data-next-hide-fouc=&quot;true&quot;&gt;
        body {
            display: none
        }
    &lt;/style&gt;&lt;noscript data-next-hide-fouc=&quot;true&quot;&gt;
        &lt;style&gt;
            body {
                display: block
            }
        &lt;/style&gt;
    &lt;/noscript&gt;
    &lt;meta charSet=&quot;utf-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot; /&gt;
    &lt;meta name=&quot;next-head-count&quot; content=&quot;2&quot; /&gt;&lt;noscript data-n-css=&quot;&quot;&gt;&lt;/noscript&gt;
    &lt;script defer=&quot;&quot; nomodule=&quot;&quot; src=&quot;/_next/static/chunks/polyfills.js?ts=1672325386908&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/_next/static/chunks/webpack.js?ts=1672325386908&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/_next/static/chunks/main.js?ts=1672325386908&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/_next/static/chunks/pages/_app.js?ts=1672325386908&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/_next/static/chunks/pages/index.js?ts=1672325386908&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/_next/static/development/_buildManifest.js?ts=1672325386908&quot; defer=&quot;&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;/_next/static/development/_ssgManifest.js?ts=1672325386908&quot; defer=&quot;&quot;&gt;&lt;/script&gt;&lt;noscript
        id=&quot;__next_css__DO_NOT_USE__&quot;&gt;&lt;/noscript&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;div id=&quot;__next&quot;&gt;
        &lt;div&gt;
            &lt;h1&gt;SSR &lt;br/&gt; hello, world!&lt;/h1&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;script src=&quot;/_next/static/chunks/react-refresh.js?ts=1672325386908&quot;&gt;&lt;/script&gt;
    &lt;script id=&quot;__NEXT_DATA__&quot; type=&quot;application/json&quot;&gt;
        {&quot;props&quot;:{&quot;pageProps&quot;:{}},&quot;page&quot;:&quot;/&quot;,&quot;query&quot;:{},&quot;buildId&quot;:&quot;development&quot;,&quot;nextExport&quot;:true,&quot;autoExport&quot;:true,&quot;isFallback&quot;:false,&quot;scriptLoader&quot;:[]}
    &lt;/script&gt;
&lt;/body&gt;

&lt;/html&gt;</code></pre>
<p><strong>React.js (CSR)</strong></p>
<pre><code class="language-jsx">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;

&lt;head&gt;
    &lt;script type=&quot;module&quot;&gt;
        import RefreshRuntime from &quot;/@react-refresh&quot;
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () =&gt; {}
window.$RefreshSig$ = () =&gt; (type) =&gt; type
window.__vite_plugin_react_preamble_installed__ = true
&lt;/script&gt;

    &lt;script type=&quot;module&quot; src=&quot;/@vite/client&quot;&gt;&lt;/script&gt;

    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;link rel=&quot;icon&quot; type=&quot;image/svg+xml&quot; href=&quot;/vite.svg&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Vite + React + TS&lt;/title&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
    &lt;script type=&quot;module&quot; src=&quot;/src/main.tsx?t=1672322031531&quot;&gt;&lt;/script&gt;
&lt;/body&gt;

&lt;/html&gt;</code></pre>
<p>두개를 보면 각 페이지의 html을 반환하는데,</p>
<p>SSR에는 body안에 hello,world가 뜨는 반면, CSR의 html파일안에는 눈씻고 찾아봐도 hello,world는 찾아볼 수가 없다...</p>
<p>이게 왜 중요하냐 !</p>
<p>이게바로 <strong>크롤링</strong>이기 때문 …!</p>
<p>검색엔진이 크롤링으로 페이지를 긁어갈 때, 이 <strong>html 파일을 읽기 때문</strong>이다 !</p>
<p>** 내가 이 두 페이지를 웹에 올렸다고 했을 때, 사용자가 hello, world 를 검색하면 SSR로 만든 페이지는 뜨겠지만, CSR로 만든 페이지는 뜨지 않을것이다 !**</p>
<p>그건 곧 서비스의 수익과도 연결되기 때문에 아주 중요하다 !</p>
<hr>
<p><strong>마무리</strong></p>
<p>뭔가 CS 지식으로 그냥 SSR이랑 CSR은 이런 차이가 있고, SSR은 SEO가 더 잘되고… 이런걸 아는것보다 직접 실험하고 눈으로 확인하니까 뭔가 더욱 흥미가 생기고 잘 이해가 되는 것 같다 !</p>
<p>앞으로 이런 실험들을 많이 해나가면 좋을 것 같다  !</p>
]]></description>
        </item>
    </channel>
</rss>