<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>seeyong_0.log</title>
        <link>https://velog.io/</link>
        <description>룰루랄라 개발일지🎶❤️‍🔥🧑‍💻❤️‍🔥🎵</description>
        <lastBuildDate>Tue, 26 Mar 2024 09:24:59 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>seeyong_0.log</title>
            <url>https://velog.velcdn.com/images/seeyong_0/profile/29996bf4-1dd9-4760-809d-efb668132a69/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. seeyong_0.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/seeyong_0" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[브라우저 좌표]]></title>
            <link>https://velog.io/@seeyong_0/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%A2%8C%ED%91%9C</link>
            <guid>https://velog.io/@seeyong_0/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%A2%8C%ED%91%9C</guid>
            <pubDate>Tue, 26 Mar 2024 09:24:59 GMT</pubDate>
            <description><![CDATA[<h1 id="브라우저-좌표란">브라우저 좌표란?</h1>
<p>coordinates
브라우저에는 x축과 y축이 있다.
x는 수평축 y는 수직축이다.
좌표는 브라우저창의 왼쪽 맨 위에서부터 시작(0,0) 한다.
x는 오른쪽으로 갈수록 숫자가 커지고
y는 아래로 갈수록 숫자가 커진다.</p>
<h1 id="elementgetboundingclientrect">Element.getBoundingClientRect()</h1>
<p>Element 오브젝트 안에 들어 있는 api 즉 함수이다.
Element는 브라우저에 올라가 있는 DOM에 있는 모든 요소들 (이미지 태그, 디브 태그 등등 모든 태그들)이다.
element에는 모두 getBoundingClientRect() 함수가 있다.
이미지를 화면에 보여주는 이미지 태그가 있다고해보자
imgElement.getBoundingClientRect()를하면 
이미지에 대한 width, height, top, left, right, bottom 등과 같은 다양한 정보를 제공한다.</p>
<h2 id="top-left">top, left</h2>
<p>top, left는 요소의 position에 대한 정보이다.
top, left는 요소의 왼쪽 위의 맨끝 지점을 기준으로한다.
left는 오른쪽으로 얼마나 갔는지를 나타내기 때문에 x좌표와 같고
top은 위에서 아래로 얼마나 갔는지를 나타내기 때문에 y좌표와 같다.</p>
<h2 id="bottom-right">bottom, right</h2>
<p>bottom, right도 요소의 position에 대한 정보이다.
bottom, right은 요소의 오른쪽 아래 맨끝 지점을 기준으로한다.
따라서 bottom은 요소가 브라우저의 위에서 아래까지 가장 먼 지점의 거리를 나타내고
right은 브라우저의 왼쪽에서 가장 먼 지점의 거리을 나타낸다.</p>
<p>주의할 점은 css에서 사용하는 bottom, right이랑은 개념이 다르다는 것이다.
css의 bottom은 브라우저 맨 아래에서 지점까지의 거리,
right은 맨 오른쪽에서 지점까지의 거리를 나타낸다.</p>
<h1 id="client-xy-vs-page-xy">Client x,y vs Page x,y</h1>
<p>페이지의 내용이 많아서 스크롤이 생겼고 사용자가 스크롤을 어느정도 내린 상태라고 했을때
브라우저에 보이는 내용보다 실제 페이지의 내용이 위, 아래에 더 있을 것이다.
이때 사용자가 클릭을 하면 Client x,y 와 Page x,y가 달라진다.
즉, 사용자가 클릭을 했을때 발생하는 클릭 이벤트 안의 x,y값과 페이지의 x,y값이 다르다.</p>
<h2 id="client-xy">Client x,y</h2>
<p>사용사자 클릭을하면 event라는 오브젝트가 개발자가 등록한 listner에 등록이된다.
event에는 client x,y라는 값이 들어있다.</p>
<p>client x,y는 페이지와 상관 없이 브라우저 창을 기준으로
클릭한 지점이 어디인지 나타내는 것이다. 
즉, x는 브라우저 창의 맨왼쪽에서 지점까지의 거리
y는 브라우저 창의 맨위쪽에서 지점까지의 거리를 나타낸다.</p>
<h2 id="page-xy">Page x,y</h2>
<p>Page x,y 는 페이지(문서) 자체를 기준으로 클릭한 지점을 나타낸다.
즉 x는 페이지 맨오른쪽에서 지점까지의 거리
y는 페이지 맨위쪽에서 지점까지의 거리를 나타낸다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[브라우저 구조분석]]></title>
            <link>https://velog.io/@seeyong_0/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EA%B5%AC%EC%A1%B0%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@seeyong_0/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EA%B5%AC%EC%A1%B0%EB%B6%84%EC%84%9D</guid>
            <pubDate>Mon, 18 Mar 2024 21:58:35 GMT</pubDate>
            <description><![CDATA[<h1 id="브라우저-구조">브라우저 구조</h1>
<h3 id="window">Window</h3>
<p>브라우저에서 웹페이지를 열개 되면
window라는 전체적인 오브젝트가 존재하고
window는 현재 열고 있는 페이지 뿐만 아니라 브라우저 전체 창을 의미한다.</p>
<h3 id="document">Document</h3>
<p>window 안에 페이지가 표기되는 부분이 바로 Document Object(DOM)이다.
Document는 우리가 HTML로 작성한 것이 보이는 부분이라고 생각하면 된다.</p>
<h3 id="navigator">Navigator</h3>
<p>사용자의 눈에는 보이지 않지만 
브라우저 자체의 정보들이 담겨있는 Navigator 라는 오브젝트도 있다.
BOM(Browser Object Model)의 일부</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Web APIs]]></title>
            <link>https://velog.io/@seeyong_0/Web-APIs</link>
            <guid>https://velog.io/@seeyong_0/Web-APIs</guid>
            <pubDate>Wed, 01 Nov 2023 11:05:53 GMT</pubDate>
            <description><![CDATA[<h2 id="apis란">APIs란?</h2>
<p>APIs는 Application Programming Interfaces의 약자이다.
우리는 자판기가 어떤 원리로 작동하는지 몰라도 동전 투입구에 금액을 넣고 음료수를 고르는 버튼을 누르면 원하는 음료수를 살 수 있다.</p>
<p>이처럼 윈도우스에서 동작하는 애플리케이션을 만들고싶다면 윈도우즈에서 제공하는 API를 이용해서 간단하게 애플리케이션을 만들 수 있고 YouTube의 백엔드 서비스에서 제공하는 API를 이용해서 Youtube에 있는 데이터들을 받아올 수 도있다.</p>
<p>이런 OS나 플랫폼에서 제공하는 API 이외에도 만약 우리가 작성하는 프로젝트에서 userStorage라는 class에서 로그인, 로그아웃을 할 수 있는 함수들을 만들었다면 이것도 우리가 작성한 즉 userStorage에서 제공하는 API이다. 그래서 user들은 로그인 로그아웃이 어떤 원리로 작동하는지 몰라도 아이디/비밀번호만 있으면 우리가 준비한 API를 통해 로그인/로그아웃을 할 수 있는 것이다.</p>
<h2 id="web-apis란">WEB APIs란?</h2>
<p>WEB APIs는 브라우저 자체에서 제공하는 API들이다.
브라우저마다 공통적으로 제공하기로 약속한 APIs가 굉장히 많다.</p>
<p><strong>DOM APIs</strong> 웹페이지에 존재하는 요소들을 생성하거나 삭제 또는 스타일 변경 등 조작하는 기능을 하는 APIs
<strong>Network APIs</strong> 서버와 통신하는 기능들을 제공 
<strong>Graphics APIs</strong> 캔버스나 webgl 같은 그래픽 관련 
<strong>Audio/Video APIs</strong> 오디오나 비디오를 재생, 중지하는 등 멀티미디어 관련
<strong>Device APIs</strong> 사용자가 온라인인지 오프라인인지 디바이스의 상태정보를 받아오는 
<strong>File APIs</strong> 사용자의 파일을 가져오거나 저장하는 
<strong>Storage APIs</strong> 사용자의 정보를 저장하는 </p>
<p>정말 다양한 APIs들이 있기 때문에 하나하나 다 설명할 수 는 없다. 
mdn사이트에 가면 다양한 web APIs들에 대해 알아볼 수 있으니 참고하자.</p>
<p>👉 mdn web APIs<a href="https://developer.mozilla.org/ko/docs/Web/API">링크텍스트</a></p>
<h2 id="https란">HTTPs란?</h2>
<p>어떤 apis들은 보안상의 이유로 사용자의 권한 요청이나, HTTPs를 요구할 수 있다.
HTTP는 Hypertext Transfer Protocal의 약자로, 웹 클라이언트와 서버가 어떻게 통신할지 정해놓은 통신규약이다. 클라이언트가 서버에게 정보를 요청(request)하고 다시 서버에서 정보를 받아오는(response) 방식으로 이루어져있다.
HTTPS는 HTTP에 Secure가 추가된것으로 즉 정보를 주고받을 때 보안처리가 잘 되었다는 의미이다.</p>
<p>예를 들어 HTTP에서 유저가 로그인할 때 비밀번호를 서버에 전송하면 보안처리가 되지 않아 입력한 그대로가 전송되기 때문에 해커에게 바로 노출 될 수 있는 반면, HTTPs에서 전송하면 암호화되어 전송되기 때문에 노출 가능성이 훨씬 줄어든다. </p>
<h2 id="external-apis">external APIs</h2>
<p>위에서 언급한 유튜브뿐만 아니라 트위터, 핀터레스트 등 다양한 회사에서 백엔드 APIs를 제공하고 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 문제 - 변수]]></title>
            <link>https://velog.io/@seeyong_0/JavaScript-%EB%AC%B8%EC%A0%9C-%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@seeyong_0/JavaScript-%EB%AC%B8%EC%A0%9C-%EB%B3%80%EC%88%98</guid>
            <pubDate>Tue, 24 Oct 2023 12:30:33 GMT</pubDate>
            <description><![CDATA[<h3 id="왜-아래의-변수는-동작하지-않을까">왜 아래의 변수는 동작하지 않을까?</h3>
<pre><code class="language-javascript">var name = &#39;park&#39;;
var id = 0;

function showName(){
  var name = &#39;kim&#39;;
  var id = 1;
  console.log(name);
}

showName();
console.log(id);
</code></pre>
<p>다음 코드를 실행했을 때 콘솔창에 무엇이 출력될까?</p>
<p>console.log(name) 부분에서는 &#39;kim&#39;이 출력되고
console.log(id) 부분에서는 0이 출력된다.</p>
<p>var name의 경우 함수 안에서 재선언 + 재할당을 하고 
출력했기 때문에 &#39;kim&#39;이 출력된다.</p>
<p>var id의 경우 함수 안에 있던 변수는 그 범위가 함수 안에서 
벗어날 수 없기 때문에 함수 바깥에서 console.log(id)를 하면
함수 밖에서 할당된 값인 0이 출력된다.</p>
<hr>
<h3 id="이자율-계산하기">이자율 계산하기</h3>
<p>철수는 은행에 예금을 하러 갔는데 예금 금액에 따라 이율이 달라지는 것을 보고 크게 당황했다.
첫 예금액이 5만원 미만이면 이율이 연 15퍼센트,
첫 예금액이 5만원 이상이면 이율이 연 20퍼센트라고 한다.
그래서 민준이는</p>
<p>1) 변수에 예금액을 넣으면
2) 2년 후의 총 예금액을 자동으로 콘솔창에 출력해주는 기능을 자바스크립트로 만들려고하는데
어떻게 코드를 짜면 될까?</p>
<pre><code class="language-javascript">var 예금액 = 60000;
var 미래예금액 = 0;

if(예금액 =&lt; 50000){
  미래예금액 = 예금액 * 1.15 * 1.15
} else {
  미래예금액 = 예금액 * 1.2 * 1.2    
}

console.log(미래예금액)</code></pre>
<p>예금액이라는 변수에 60000을 집어넣으면 86400 이 콘솔창에 출력됨. (이자 20%가 2번 붙음)</p>
<p>예금액이라는 변수에 10000을 집어넣으면 13225 이 콘솔창에 출력됨. (이자 15%가 2번 붙음)</p>
<hr>
<h3 id="커피-리필을-이상하게-해주는-곳이-있다-최대한-마실-수-있는-커피양을-계산해보자">커피 리필을 이상하게 해주는 곳이 있다. 최대한 마실 수 있는 커피양을 계산해보자.</h3>
<p>방금 마신 커피의 3분의 2만큼 총 2번 리필해주는 카페가 있다.
예를 들면 처음 커피를 90ml 주문하면 첫 리필은 60ml를 해주고,
그 다음 리필은 40ml를 해준다.
그럼 처음 주문한 커피 양에 따라서 최대한 마실 수 있는 커피양을 계산해주는 코드를 작성해보자.</p>
<pre><code class="language-javascript">var first = 360;
var second = first * (2/3)
var third = second * (2/3)
var all = first + second + third

console.log(all)</code></pre>
<p>var first에 360을 집어넣으면 위 코드를 실행했을 때 콘솔창에 760이 출력된다. (360 + 240 + 160)</p>
<p>이렇게 여러개의 변수를 선언하지 않고도 계산할 수 있는 방법이 있습니다.</p>
<pre><code class="language-javascript">var first = 360;
var total = 0;

total = first + first * 2/3 + first * 2/3 * 2/3

console.log(total)</code></pre>
<hr>
<h3 id="정답-제출-ui-퀴즈">정답 제출 UI 퀴즈</h3>
<p>유저가 <code>&lt;input&gt;</code>에 답을 적고 제출버튼을 누를 수 있는 퀴즈 UI</p>
<ol>
<li>유저가 답을 맞추면 alert(&#39;성공&#39;);</li>
<li>유저가 답을 3번 찍어서 못맞추면 alert(&#39;땡!&#39;) 을 띄워보자.
(위 문제의 답은 1335이다.)</li>
</ol>
<pre><code class="language-javascript">&lt;p&gt;태조 이성계가 태어난 년도는?&lt;/p&gt;
&lt;input type=&quot;text&quot; id=&quot;answer&quot;&gt;
&lt;button id=&quot;send-answer&quot;&gt;제출&lt;/button&gt;

&lt;script&gt;
  let count = 0

  document.getElementById(&quot;send-answer&quot;).addEventListner(&#39;click&#39;, function() {
    count++;
    let userAnswer = document.getElementById(&quot;answer&quot;).value 
    if(userAnswer === &#39;1335&#39;){
      alert(&#39;성공&#39;)
    } else if(userAnswer !==&#39;1335&#39; &amp;&amp; count &gt;= 3){
     alert(&#39;땡!&#39;)
    }
  })
&lt;/script&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next Blog] 캐러셀 포스트 보여주기]]></title>
            <link>https://velog.io/@seeyong_0/Next-Blog-%EC%BA%90%EB%9F%AC%EC%85%80-%ED%8F%AC%EC%8A%A4%ED%8A%B8-%EB%B3%B4%EC%97%AC%EC%A3%BC%EA%B8%B0</link>
            <guid>https://velog.io/@seeyong_0/Next-Blog-%EC%BA%90%EB%9F%AC%EC%85%80-%ED%8F%AC%EC%8A%A4%ED%8A%B8-%EB%B3%B4%EC%97%AC%EC%A3%BC%EA%B8%B0</guid>
            <pubDate>Tue, 12 Sep 2023 03:41:21 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/37d054b0-3506-4fc8-9150-fd38c3df1447/image.gif" alt=""></p>
<blockquote>
<p>이번에는 non featured Posts를 carousel로 보여주도록 만들었다. react로 만들어본 경험은 있는데 NextJS를 사용하여 만드는 것은 처음이라 기록해보았다.</p>
</blockquote>
<h1 id="non-featured-posts-데이터-받아오기">non featured posts 데이터 받아오기</h1>
<h2 id="비즈니스-로직">비즈니스 로직</h2>
<p>캐러셀로는 featured posts를 제외한 non featured posts를 보여줄 것이기 때문에 따로 non featured posts 데이터를 받아와야한다.</p>
<p><strong>src/service/posts.ts</strong></p>
<pre><code class="language-javascript">export const getAllPosts = cache(async () =&gt; {
  console.log(&quot;getAllPosts&quot;);

  const filePath = path.join(process.cwd(), &quot;data&quot;, &quot;posts.json&quot;);
  return readFile(filePath, &quot;utf-8&quot;)
    .then&lt;Post[]&gt;(JSON.parse)
    .then((posts) =&gt; posts.sort((a, d) =&gt; (a.date &gt; d.date ? -1 : 1)));
});

export async function getFeaturedPosts(): Promise&lt;Post[]&gt; {
  const posts = await getAllPosts();
  return posts.filter((post) =&gt; post.featured);
}

export async function getNonFeaturedPosts(): Promise&lt;Post[]&gt; {
  const posts = await getAllPosts();
  return posts.filter((post) =&gt; !post.featured);
}</code></pre>
<p>먼저 <code>getAllPosts</code>라는 함수를 통해 전체 posts 데이터를 받아온 후</p>
<p><code>getNonFeaturedPosts</code>함수에서 filter메서드를 통해 featured에 false 값이 할당된, 즉 non featured Posts만 필터 되도록 했다.</p>
<h2 id="컴포넌트에서-읽어-오기">컴포넌트에서 읽어 오기</h2>
<p>components 폴더에 PostsCarousel.tsx 컴포넌트를 생성해 캐러셀을 컴포넌트화 했다.</p>
<p>그리고 비즈니스 로직에서 작성한 <code>getNonFeaturedPosts</code>함수를 가져와 데이터를 읽어오도록 했다.</p>
<p><strong>src/components/PostsCarousel.tsx</strong></p>
<pre><code class="language-javascript">import { getNonFeaturedPosts } from &quot;@/service/posts&quot;;
import PostCard from &quot;./PostCard&quot;;

export default async function PostsCarousel() {
  const posts = await getNonFeaturedPosts();

  return (
    &lt;section className=&quot;my-4 p-7&quot;&gt;
      &lt;h2 className=&quot;text-2xl font-bold my-2&quot;&gt;You May Like&lt;/h2&gt;  
       {posts.map((post) =&gt; {
          return &lt;PostCard key={post.path} post={post} /&gt;;
       })}
    &lt;/section&gt;
  );
}
</code></pre>
<hr>
<h1 id="react-multi-carousel-사용">react-multi-carousel 사용</h1>
<p>공식 문서의 예제는 다음과 같다</p>
<pre><code class="language-javascript">import Carousel from &quot;react-multi-carousel&quot;;
// 라이브러리 import
import &quot;react-multi-carousel/lib/styles.css&quot;;
// css import
const responsive = {
  // 반응형 설정 
  superLargeDesktop: {
    // 이름은 마음대로 
    breakpoint: { max: 4000, min: 3000 },
    items: 5
  },
  desktop: {
    breakpoint: { max: 3000, min: 1024 },
    items: 3
  },
  tablet: {
    breakpoint: { max: 1024, min: 464 },
    items: 2
  },
  mobile: {
    breakpoint: { max: 464, min: 0 },
    items: 1
  }
};
&lt;Carousel responsive={responsive}&gt;
  &lt;div&gt;Item 1&lt;/div&gt;
  &lt;div&gt;Item 2&lt;/div&gt;
  &lt;div&gt;Item 3&lt;/div&gt;
  &lt;div&gt;Item 4&lt;/div&gt;
&lt;/Carousel&gt;;</code></pre>
<p>라이브러리를 import 한 후 
필요하면 반응형을 설정하고
Carousel이라는 컴포넌트로 carousel 안에 보여줄 아이템들을 감싸주면 된다.</p>
<p>원래는 swiper라는 라이브러리를 사용했었는데 업데이트가 되면서 
용량이 너무 커져서 이 라이브러리를 선택했다.</p>
<h2 id="multicarouseltsx-컴포넌트">MultiCarousel.tsx 컴포넌트</h2>
<p>재사용성과 클린 코드를 고려해 라이브러리 사용하는 부분도 따로 컴포넌트화했다.</p>
<p><strong>src/components/MultiCarousel.tsx</strong></p>
<pre><code class="language-javascript">&#39;use client&#39;
// 브라우저에서 자동으로 스크롤링 되는 부분
// 버튼을 눌렀을 때 이동하는 부분
// 이 있기 때문에 서버컴포넌트가 아니라 클라이언트 컴포넌트로 만들어야함

import Carousel from &quot;react-multi-carousel&quot;;
import &quot;react-multi-carousel/lib/styles.css&quot;;

const responsive = {
  superLargeDesktop: {
    breakpoint: { max: 4000, min: 3000 },
    items: 4
  },
  desktop: {
    breakpoint: { max: 3000, min: 1024 },
    items: 4
  },
  tablet: {
    breakpoint: { max: 1024, min: 464 },
    items: 3
  },
  mobile: {
    breakpoint: { max: 464, min: 0 },
    items: 2
  }
};

type Props = {
  children: React.ReactNode;
  // prop으로 받아오는 children의 타입을 지정할 때 
  // React의 ReactNode라고 지정해주면 된다.
};

export default function MultiCarousel({ children }: Props) {
  return (
    &lt;Carousel 
    // 내가 지정하고 싶은 option들을 prop으로 지정
    infinite // 무한정
    autoPlay // 자동 play
    responsive={responsive} // 위에서 정의한 responsive
    itemClass=&quot;m-2&quot; // 각 아이템 별로 class 지정 
    &gt;
      {children}
    &lt;/Carousel&gt;
  );
}
</code></pre>
<h2 id="carouselpoststsx에서-multicarouseltsx-사용">CarouselPosts.tsx에서 MultiCarousel.tsx 사용</h2>
<p>CarouselPosts는 기본적으로 서버 컴포넌트이다. 
따라서 클라이언트 컴포넌트가 아닌 부분들은 서버에서 처리를 하고
실제로 캐러셀을 보여주는 부분은 클라이언트 컴포넌트로 브라우저에서 처리하도록 되어있다.</p>
<p>이처럼 클라이언트 컴포넌트를 필요로 하는 부분이 있다면
전체 페이지는 클라이언트 컴포넌트로 만드는 것이 아니라
그 부분만 최소한의 단위로 만들어서 따로 만들어 사용하는 것이 중요하다.</p>
<p><strong>src/components/PostsCarousel.tsx</strong></p>
<pre><code class="language-javascript">import { getNonFeaturedPosts } from &quot;@/service/posts&quot;;
import MultiCarousel from &quot;./MultiCarousel&quot;;
import PostCard from &quot;./PostCard&quot;;

export default async function PostsCarousel() {
  const posts = await getNonFeaturedPosts();

  return (
    &lt;section className=&quot;my-4 p-7&quot;&gt;
      &lt;h2 className=&quot;text-2xl font-bold my-2&quot;&gt;You May Like&lt;/h2&gt;
      &lt;MultiCarousel&gt;
        // 캐러셀 컴포넌트를 가져와 아래 posts들을 감싸준다
        {posts.map((post) =&gt; {
          return &lt;PostCard key={post.path} post={post} /&gt;;
        })}
      &lt;/MultiCarousel&gt;
    &lt;/section&gt;
  );
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next Blog] Featured posts 보여주기]]></title>
            <link>https://velog.io/@seeyong_0/Next-Blog-Featured-posts-%EB%B3%B4%EC%97%AC%EC%A3%BC%EA%B8%B0</link>
            <guid>https://velog.io/@seeyong_0/Next-Blog-Featured-posts-%EB%B3%B4%EC%97%AC%EC%A3%BC%EA%B8%B0</guid>
            <pubDate>Fri, 25 Aug 2023 06:49:24 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/e4ca6b88-57a7-4dcf-aa1f-def6de237860/image.gif" alt=""></p>
<blockquote>
<p>포스트 내용을 간략하게 보여주는 포스트 카드를 만들고 카드들을 정렬해서 보여주도록 구현</p>
</blockquote>
<h1 id="구현-순서">구현 순서</h1>
<ol>
<li>코드 구조화</li>
<li>모든 데이터 불러오기</li>
<li>모든 포스트 데이터를 보여줌</li>
</ol>
<h1 id="코드구조">코드구조</h1>
<h2 id="featuredposts-컴포넌트">FeaturedPosts 컴포넌트</h2>
<p><strong>src/page.tsx</strong></p>
<pre><code class="language-javascript">import FeaturedPosts from &quot;@/components/FeaturedPosts&quot;;
import Hero from &quot;@/components/Hero&quot;;
import PostsCarousel from &quot;@/components/PostsCarousel&quot;;

export default async function HomePage() {
  return (
    &lt;section&gt;
      &lt;Hero /&gt;
      {/* @ts-expect-error Server Component */}
      &lt;FeaturedPosts /&gt;
    &lt;/section&gt;
  );
}</code></pre>
<p><strong>src/components</strong>에 FeaturedPosts 컴포넌트를 생성후 Home page에 Hero 컴포넌트 밑에 위치</p>
<h2 id="postsgrid-컴포넌트">PostsGrid 컴포넌트</h2>
<p>포스트 카드를 그리드 형식으로 보여주는 컴포넌트
그리드로 보여주는 것을 재사용할 수 있도록 따로 컴포넌트로 만들었다.</p>
<p><strong>src/components/FeaturedPosts.ts</strong></p>
<pre><code class="language-javascript">import { getFeaturedPosts } from &quot;@/service/posts&quot;;
import PostsCard from &quot;./PostCard&quot;;
import PostsGrid from &quot;./PostsGrid&quot;;

export default async function FeaturedPosts() {

  return (
    &lt;section&gt;
      &lt;h2&gt;Featured Posts&lt;/h2&gt;
      &lt;PostsGrid/&gt;
    &lt;/section&gt;
  );
}</code></pre>
<p>PostsGrid 컴포넌트를 FeaturedPosts에 import하여 보여지도록 함</p>
<p><strong>src/components/PostsGrid.ts</strong></p>
<pre><code class="language-javascript">import { Post } from &quot;@/service/posts&quot;;
import PostCard from &quot;./PostCard&quot;;

export default function PostsGrid({ posts }: { posts: Post[] }) {
  return (
    &lt;ul className=&quot;grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4&quot;&gt;
      {posts.map((el) =&gt; {
        return (
          &lt;li key={el.title}&gt;
            &lt;PostCard post={el} /&gt;
          &lt;/li&gt;
        );
      })}
    &lt;/ul&gt;
  );
}</code></pre>
<h3 id="postsgrid-코드-설명">PostsGrid 코드 설명</h3>
<p><strong>css grid</strong>를 사용하여 post card들을 배치하는 틀을 만들었다.
<strong>컴포넌트화</strong>하여 이 틀을 다른 곳에서도 재사용할 수 있도록했다.
화면 크기에따라 열의 개수가 달라지는 <strong>반응형</strong>으로 만들었다.</p>
<h2 id="postcard-컴포넌트">PostCard 컴포넌트</h2>
<p>위의 코드에서 확인할 수 있듯이 PostsGrid 컴포넌트 안에 import 하여 사용
정리하자면 </p>
<p><strong>homepage &gt; FeaturedPosts &gt; PostsGrid &gt; PostCard</strong>
구조로 구성되어있다.</p>
<p><strong>src/components/PostCard.ts</strong></p>
<pre><code class="language-javascript">import { Post } from &quot;@/service/posts&quot;;
import Image from &quot;next/image&quot;;
import Link from &quot;next/link&quot;;

type Props = { post: Post };

export default function PostCard({
  post: { title, description, date, category, path },
}: Props) {
  return (
    &lt;Link href={`/posts/${path}`}&gt;
      &lt;article className=&quot;w-full pb-5 rounded-md overflow-hidden bg-white shadow-md hover:shadow-xl&quot;&gt;
        &lt;div className=&quot;w-full h-40 &quot;&gt;
          &lt;Image
            src={`/image/posts/${path}.png`}
            alt={title}
            width={300}
            height={200}
            className=&quot;w-full h-full object-cover&quot;
          /&gt;
        &lt;/div&gt;
        &lt;div className=&quot; flex flex-col justify-center items-center&quot;&gt;
          &lt;time className=&quot; self-end mb-2 mr-2 text-slate-400&quot;&gt;
            {date.toString()}
          &lt;/time&gt;
          &lt;h1 className=&quot; font-bold text-xl&quot;&gt;{title}&lt;/h1&gt;
          &lt;p className=&quot;mb-3 px-5 w-full truncate text-center&quot;&gt;{description}&lt;/p&gt;
          &lt;span className=&quot;bg-sky-200    px-3 py-1 rounded-md text-sm&quot;&gt;
            {category}
          &lt;/span&gt;
        &lt;/div&gt;
      &lt;/article&gt;
    &lt;/Link&gt;
  );
}</code></pre>
<h1 id="비즈니스-로직">비즈니스 로직</h1>
<p>비즈니스 로직은 다량의 데이터를 읽어오는 것 같은 복잡한 로직을 의미한다.
이런 복잡한 로직은 컴포넌트 내에 담고 있는 것이 아니라 
복잡한 로직을 담당하는 모듈에게 전가해야한다.</p>
<p>따라서 프로젝트의 source 폴더 안에 <code>service</code> 폴더를 생성하고 (api, manager 등이라고 해도 좋다) 그 안에 비즈니스 로직들만 담당하는 파일들을 생성해 작업하는 것이 바람직하다.</p>
<p>따라서 featured posts에 필요한 posts 데이터들을 읽어오기 위해 
<strong>src/service/posts.ts</strong> 모듈을 생성해 비즈니스 로직을 작성했다.</p>
<pre><code class="language-javascript">import path from &#39;path&#39;;
import { readFile } from &quot;fs/promises&quot;;

export type Post = {
  title: string;
  description: string;
  date: Date;
  category: string;
  path: string;
  featured: boolean;
}

export async function getAllPosts():Promise&lt;Post[]&gt; {
    // 비동기 함수
    // Promise&lt;Post[]&gt; 호출하면 Post의 배열 Promise를 반환한다는 의미
    const filePath = path.join(process.cwd(), &quot;data&quot;, &quot;posts.json&quot;);
    // process.cwd()는 process가 현재 동작하고 있는 현재 경로를 받아오는 것을 의미
    // public/data/posts.json을 받아오도록 설정함
     return (
        readFile(filePath, &quot;utf-8&quot;)
          // 받아온 파일을 읽기 위해 readFile 사용
          // readFile은 promise에 있는걸로 가져와야함
          // 그래야 프로미스를 반환하는 readFile을 사용할 수 있음
        .then&lt;Post[]&gt;(JSON.parse)
          // data를 가지고 와서 JSON에 있는 parse에 그대로 전달 
          // .then(data =&gt; JSON.parse(data))의 축약버전임
          // 전달하는 것과 인자가 같을 때 생략 가능
          // &lt;Post[]&gt;제네릭으로 타입을 지정해줘야 
          //아래 sort(a, d) 에서 a, d에 대한 타입 에러가 안남
        .then((posts) =&gt; posts.sort((a, d) =&gt; (a.date &gt; d.date ? -1 : 1)))
  );
  // 최신순으로 정렬
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next blog] 페이지 레이아웃 만들기]]></title>
            <link>https://velog.io/@seeyong_0/Next-blog-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@seeyong_0/Next-blog-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Tue, 08 Aug 2023 10:18:12 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>NextJS를 활용한 개발자를 위한 blog 프로젝트를 시작했다.
가장 먼저 페이지 레이아웃을 만들어보았다.</p>
</blockquote>
<h1 id="header-footer-컴포넌트-만들기">Header, Footer 컴포넌트 만들기</h1>
<p>Header와 Footer를 컴포넌트로 만들지 않고 
app/layout.tsx에 바로 만들어도 되지만
layout에 모든 코드와 로직을 작성하면 너무 뚱뚱해진다. 
한 layout에 모든 것을 정의하기 보다는 
개별적이고 독립적인 작은 단위의 컴포넌트로 만들어 나가는 것이 중요 하다.</p>
<p>NextJS라는 프레임 워크도 어찌됐든 react를 베이스로 하고 있기 때문에 
react의 기본 원칙을 따를 것이 중요하다.</p>
<p>따라서 src 폴더에 components 폴더를 만들고 그 안에 Header와 Footer 컴포넌트를 만들었다.</p>
<h2 id="componentsheadertsx">components/Header.tsx</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/9dc9164e-04b4-4e5d-b2c2-4359ce3814b1/image.png" alt=""></p>
<p>블로그 타이틀을 클릭했을 때도 home으로 이동할 수 있도록 
Link 태그 안에 h1 태그를 넣어 경로를 부여했다.</p>
<h2 id="componentsfootertsx">components/Footer.tsx</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/8a11546b-ade4-4a4a-a666-e5519a6e8211/image.png" alt=""></p>
<h2 id="applayouttsx">app/layout.tsx</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/3c347a05-5174-4a28-9e9a-12ded7bd5076/image.png" alt=""></p>
<p>layout에서 Header 와 Footer를 import해서 사용했다.
Header와 Footer 사이에 main 태그를 추가하고 그 안에 children을 넣었다.</p>
<p>layout에서 main 태그로 감싸주고 있기 때문에 children에 해당하는 app/page.tsx 에서 main 태그 대신 section 태그로 변경해주었다.</p>
<hr>
<h1 id="about-posts-contact-페이지-만들기">about, posts, contact 페이지 만들기</h1>
<p>navigation을 클릭했을 때 해당 경로로 이동할 수 있도록 page들을 만들었다.
nextJs의 파일 기반 라우팅을 활용해서 
<img src="https://velog.velcdn.com/images/seeyong_0/post/d6f92c9f-79d7-4cfc-a925-2402dc088924/image.png" alt=""></p>
<p>위와 같이 파일을 구성했다.</p>
<hr>
<h1 id="tailwind로-스타일링하기">tailwind로 스타일링하기</h1>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/3ea93abb-8304-4d3f-868c-e220658892ad/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js 새로운 프로젝트 기본 세팅, ts module 에러 해결]]></title>
            <link>https://velog.io/@seeyong_0/Next.js-%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%EB%B3%B8-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@seeyong_0/Next.js-%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%EB%B3%B8-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Fri, 04 Aug 2023 06:59:20 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Next로 프로젝트를 진행할 때 기초 세팅하는 방법에 대해 정리해보았다.</p>
</blockquote>
<h1 id="nextjs-설치">NextJs 설치</h1>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/2f862f37-dd85-415b-8b21-765ac2a3116c/image.png" alt=""></p>
<p>터미널에서 만들고자하는 폴더로 이동후 </p>
<p><code>npx create-next-app@latest</code> 명령어를 통해 next를 설치할 수 있다.</p>
<p><a href="https://nextjs.org/docs/getting-started/installation">https://nextjs.org/docs/getting-started/installation</a>
공식문서에도 자세히 나와있으니 참고해보자</p>
<hr>
<h1 id="tailwindcss-세팅">TailwindCss 세팅</h1>
<h2 id="설치">설치</h2>
<p>Next 설치할 때 Tailwind설치 여부를 물어보는데 그때 설치하면 
Tailwind가 같이 설치된다.<img src="https://velog.velcdn.com/images/seeyong_0/post/31fe559b-b687-47f7-8cea-a424ea3c1109/image.png" alt="">
package.json에 dependencies를 보면
tailwindcss 3.3.3 버전이 설치되어있는 것을 확인 할 수 있다.</p>
<h2 id="tailwindconfigjs">tailwind.config.js</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/0b490322-2847-41bb-8113-5dfc3e012399/image.png" alt="">
그리고 tailwind.config 파일도 기본적으로 세팅이 되어있는데
content 배열 안에 있는 내용은 각각
pages 안에, components 안에, app 폴더 안에서 tailwind를 사용하겠다고
명시한 것이다.</p>
<p>여기서 만약 src directory를 사용한다면 
src directory 안에 있는 모든 것들에 tailwind를 적용시키도록 설정할 수 있다.</p>
<p>나는 프로젝트에서 src directory를 사용할 것이기 때문에 
기존 내용들을 지우고 <code>&#39;./src/**/*.{js,ts,jsx,tsx,mdx}&#39;,</code>만 기입했다.</p>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/32b05e51-9d67-48cf-83e1-0f05ab894381/image.png" alt=""></p>
<h2 id="global-css">global css</h2>
<p>스타일을 import하기 위해서 
app 폴더에 있는 global.css 파일에</p>
<p>@tailwind base;
@tailwind components;
@tailwind utilities;</p>
<p>3가지를 제일 위에다 기입하면 된다.</p>
<p>그리고 app/layout.tsx에 global.css를 import하면된다.</p>
<hr>
<h1 id="폰트-설정">폰트 설정</h1>
<p>app/layout.tsx 에서</p>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/db453c0d-c735-4bfd-86cc-44854f0aab80/image.png" alt=""></p>
<p>Open_Sans를 import 하고 
html 태그에 className을 sans.className으로 설정하면 된다.</p>
<p>page가 아니라 layout에서 해야하는 이유는 
layout html 전반적으로 font를 설정해줄 수 있기 때문이다.</p>
<hr>
<h1 id="tsconfig-설정">tsconfig 설정</h1>
<h2 id="target">target</h2>
<p>수정하기 전에는 target이 es5라고 되어있다.
하지만 최근에는 웬만한 브라우저는 모두 es6 이상을 지원해주기 때문에
es6로 변경하는 것이 좋다.</p>
<p>예전 버전으로 설정하면 예전 버전을 지원하기 위해 코드가 길어지고 
커질 수 있기 때문에 불필요하게 예전 버전으로 설정하는 것은 지양해야한다.</p>
<hr>
<h1 id="ts-module-에러-해결">ts module 에러 해결</h1>
<p>next 13.4.13버전은 처음 실행을 할 때 부터 ts module에러가 발생한다.
이를 해결하기 위해서는 tsconfig 파일을 수정하면 된다.</p>
<pre><code>&quot;module&quot;: &quot;esnext&quot;,
&quot;moduleResolution&quot;: &quot;node&quot;,</code></pre><p>module과 moduleResolution을 위와 같이 변경하면 오류가 해결된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 하이브리드 렌더링]]></title>
            <link>https://velog.io/@seeyong_0/Next.js-%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C-%EB%A0%8C%EB%8D%94%EB%A7%81</link>
            <guid>https://velog.io/@seeyong_0/Next.js-%ED%95%98%EC%9D%B4%EB%B8%8C%EB%A6%AC%EB%93%9C-%EB%A0%8C%EB%8D%94%EB%A7%81</guid>
            <pubDate>Fri, 14 Jul 2023 07:10:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/f8c62051-7918-441d-bfca-f961387bf6d1/image.png" alt="">
<img src="https://velog.velcdn.com/images/seeyong_0/post/574330b7-46e6-4023-85a0-70752766ec1f/image.webp" alt=""></p>
<h1 id="hybrid-web-app-이란">Hybrid Web App 이란?</h1>
<p>성능 좋은 강력한 Web App을 만들기 위해서 두개 이상의 렌더링 방법을 사용하는 것을 의미한다.</p>
<p>Next.js는 두가지 이상의 렌더링 방식을 사용할 수 있다.</p>
<p>예를 들어</p>
<p>홈페이지는 ISR로 
about 페이지는 잘 변경되지 않기 때문에 SSG로
사용자의 profile페이지는 CSR/SSR을 하이브리드해서 만들 수 있다.</p>
<p>이처럼 한 애플리케이션에서 페이지의 특성에 따라 적절한 렌더링 방식을 체택해 만들 수 있고
심지어는 하나의 페이지 내에서도 하이브리드가 가능하다.</p>
<hr>
<h1 id="hydration이란">Hydration이란?</h1>
<p>Hydrate는 수화시키다. 즉, 물로 가득 채우다라는 의미이다.</p>
<p>컵라면은 건조되어있어서 가지고 다니기 편하지만 바로 먹을 수는 없다. 뜨거운 물을 부어 끓여 먹어야한다. 이런 이미지를 상상하면서 Next.js가 어떻게 작동하는지 연상해보자.</p>
<h2 id="nextjs에서-hydration">Next.js에서 Hydration</h2>
<p>클라이언트가 서버에 요청을하면
Next.js는 필요한 것들을 받아와서 페이지를 생성한다.
여기서 만들어진 페이지는 정적인 HTML페이지이다. 사용자에게 보여줘야하는 정보들을 담아 HTML로 만든 정적페이를 빠르게 보여준다.
이를 pre-rendering이라고도 한다. </p>
<p>그러나 이 페이지에는 아직 자바스크립트 코드가 포함되어있지 않기 때문에 사용자가 여기 저기 클릭을 해도 아무 반응을 하지 않는다.</p>
<p>pre-rendering후에 react라이브러리 자체와 우리가 작성한 소스코드들을 보내준다.</p>
<p>클라이언트에서 리액트와 소스코드를 다운 받은 후에는 react로 가득 채우며 <strong>Hydration</strong>이 된다.
한마디로, 컴포넌트로 렌더링이된다. 이전까지는 정적인 페이지였다면 실제 컴포넌트들을 렌더링해서 사용자가 클릭하면 컴포넌트 내부의 로직이 실행되면서 클릭을 처리할 수 있게된다.</p>
<hr>
<h1 id="web-app-개발시-중요한-포인트">Web App 개발시 중요한 포인트</h1>
<h2 id="ttvtime-to-view를-줄이는-것">TTV(Time To View)를 줄이는 것</h2>
<p>사용자가 빠르게 웹페이지의 첫 화면을 볼 수 있도록 해야함</p>
<h2 id="ttitime-to-interact를-줄이는-것">TTI(Time To Interact)를 줄이는 것</h2>
<p>정적인 HTML페이지와 Hydration된 페이지의 간극을 줄이는 것이 매우 중요하다!</p>
<hr>
<h1 id="언제-어떤-렌더링-방식을-사용하는-것이-좋은가">언제 어떤 렌더링 방식을 사용하는 것이 좋은가</h1>
<p>언제 어떤 렌더링 방식을 쓰는지에 대한 정답은 없다.
내가 생각했을 때 적절한 방법을 정리해보았다.
<img src="https://velog.velcdn.com/images/seeyong_0/post/7cb43baa-b31f-4a79-ad82-1d8058f01807/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] SSR의 특징, 장/단점]]></title>
            <link>https://velog.io/@seeyong_0/Next.js-SSR%EC%9D%98-%ED%8A%B9%EC%A7%95-%EC%9E%A5%EB%8B%A8%EC%A0%90</link>
            <guid>https://velog.io/@seeyong_0/Next.js-SSR%EC%9D%98-%ED%8A%B9%EC%A7%95-%EC%9E%A5%EB%8B%A8%EC%A0%90</guid>
            <pubDate>Fri, 14 Jul 2023 07:02:19 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/1c0309f4-b47f-401c-a5f0-d688d39f2d76/image.webp" alt=""></p>
<h1 id="ssr이란">SSR이란?</h1>
<p>Server Side Rendering
렌더링하는 주체가 <strong>서버</strong>이고, <strong>요청시 렌더링</strong>된다.</p>
<p>미리 렌더링해두는 것이 아니라 클라이언트 측에서 요청을하면 렌더링을 해서 클라이언트에게 보내준다.</p>
<p>서버에 우리가 배포한 웹 애플리케이션이 있고
클라이언트에서 웹페이지 정보를 달라고 요청하면
서버에서 코드를 읽고 fetching해야할 데이터들 fetching한 후
HTML 파일을 만들어서 클라이언트에게 보내준다.</p>
<p>미리 렌더링을 해두는 SSG나 ISR과는 다르게 클라이언트가 요청할 때 렌더링을 하는 것이다.</p>
<hr>
<h1 id="ssr의-장점">SSR의 장점</h1>
<p>SSG와 ISR의 장점 +</p>
<h3 id="실시간-데이터를-사용">실시간 데이터를 사용!</h3>
<p>항상 최신의 데이터를 반영한 html을 받을 수 있음</p>
<h3 id="사용자별-데이터-제공-가능">사용자별 데이터 제공 가능</h3>
<p>요청할 때마다 렌더링하기 때문에 사용자마다 다른 데이터를 보여줄 수 있음</p>
<h1 id="ssr의-단점">SSR의 단점</h1>
<h3 id="비교적-느릴-수-있음">비교적 느릴 수 있음</h3>
<p>요청을 할 때마다 렌더링을 해야하기 때문에 SSG와 ISR에 비해 느릴 수 있다.</p>
<h3 id="서버의-과부하가-걸릴-수-있음">서버의 과부하가 걸릴 수 있음</h3>
<p>서버의 overhead가 높아진다고도 함
매요청마다 페이지를 다시 만들어야하기 때문에 사용자가 많아지면 과부하가 오기 쉬움</p>
<h3 id="cdn에-캐시가-안됨">CDN에 캐시가 안됨</h3>
<p>요청할 때 마다 만들기 때문에 캐시할 수 없음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] ISR 특징, 장/단점]]></title>
            <link>https://velog.io/@seeyong_0/Next.js-ISR-%ED%8A%B9%EC%A7%95-%EC%9E%A5%EB%8B%A8%EC%A0%90</link>
            <guid>https://velog.io/@seeyong_0/Next.js-ISR-%ED%8A%B9%EC%A7%95-%EC%9E%A5%EB%8B%A8%EC%A0%90</guid>
            <pubDate>Fri, 14 Jul 2023 06:32:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/6bbb0ef7-fdc6-4185-82b1-50aea501adcc/image.webp" alt=""></p>
<h1 id="isr이란">ISR이란?</h1>
<p>Incremental Static Regeneration
SSG를 보완하기 위해 나온 것으로 주기적으로 다시 만들 수 있는 방식이다.</p>
<p>렌더링의 주체자는 <strong>서버</strong>이고, <strong>주기적으로 렌더링</strong>을한다.
기본적으로는 SSG와 동일한 원리이지만 우리가 설정한 주기만큼 페이지를 계속해서 다시 만들어준다.</p>
<hr>
<h1 id="isr의-장점">ISR의 장점</h1>
<p>SSG의 장점들 +</p>
<h3 id="데이터가-주기적으로-업데이트-됨">데이터가 주기적으로 업데이트 됨</h3>
<h1 id="isr의-단점">ISR의 단점</h1>
<h3 id="실시간-데이터가-아님">실시간 데이터가 아님</h3>
<p>주기적으로 업데이트가 되기는하지만 여전히 실시간으로 변하는 데이터를 반영할 수 없음</p>
<h3 id="여전히-사용자별-정보-제공이-어려움">여전히 사용자별 정보 제공이 어려움</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] SSG 특징, 장/단점]]></title>
            <link>https://velog.io/@seeyong_0/Next.js-SSG-%ED%8A%B9%EC%A7%95-%EC%9E%A5%EB%8B%A8%EC%A0%90</link>
            <guid>https://velog.io/@seeyong_0/Next.js-SSG-%ED%8A%B9%EC%A7%95-%EC%9E%A5%EB%8B%A8%EC%A0%90</guid>
            <pubDate>Fri, 14 Jul 2023 06:06:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/d1b3f866-6bc6-46fa-9424-5d358e2e3266/image.webp" alt=""></p>
<h1 id="ssg란">SSG란?</h1>
<p>Static Site Generation
CSR의 단점들을 보완하기 위해 코딩할 때는 편하게 하지만
배포할 때는 옛날처럼 정적으로 배포하고자 나온것이 SSG이다.</p>
<p>SSG는 렌더링하는 <strong>주체자가 서버</strong>이고 어플리케이션이 <strong>빌드될때 렌더링</strong>한다. (언제 렌더링이 되냐에 따라 ISR, SSR, SSG로 나뉜다.)</p>
<p>서버에 Next.js 어플리케이션을 배포해서 웹애플리케이션을 실행하기 전에 먼저 빌드를한다.
빌드할때 우리가 작성한 소스코드들을 서버에서 실행하면서 
리액트 코드를 HTML로 변환한다. </p>
<p>코드에서 서버에 있는 데이터베이스를 읽거나 또는 클라우드에서 데이터를 읽어와서 페이지를 만드는 것도 빌드할 때 실행이된다.</p>
<p>필요한 데이터들을 다 읽어오고 브라우저 상에서 어떻게 표기될건지 태그들을 다 하나씩 만들어서 실제 HTML 파일들을 빌드할 때 만들어 둔다.</p>
<p>따라서 브라우저에서 페이지를 요청하면 서버에서 미리 만들어둔 HTML 파일을 보내준다. 
그럼 클라이언트 측에서는 받아온 데이터를 바로 표기만하면 되는 것이다.</p>
<hr>
<h1 id="ssg의-장점">SSG의 장점</h1>
<h3 id="페이지-로딩-시간-ttv이-빠르다">페이지 로딩 시간 (TTV)이 빠르다</h3>
<p>서버에서 만들어둔 HTML파일만 받아서 표기하면 되기 때문에 리액트 라이브러리나 자바스크립트 소스코드들을 클라이언트 측에서 받을 필요가 없기 때문에 페이지 로딩 시간이 빠르다.</p>
<h3 id="자바스크립트가-필요-없음">자바스크립트가 필요 없음</h3>
<p>위의 이유로 자바스크립트를 활성화 하지 않아도 웹 애플리케이션을 사용할 수 있다.</p>
<h3 id="seo-최적화가-좋음">SEO 최적화가 좋음</h3>
<p>웹 크롤러가 우리 페이지를 확인할 수 있음</p>
<h3 id="보안이-뛰어나다">보안이 뛰어나다</h3>
<p>불필요하게 소스코드들을 클라이언트 측에 보낼 필요가 없기 때문에 보안에 뛰어나다.</p>
<h3 id="cdn에-캐시가-된다">CDN에 캐시가 된다.</h3>
<p>HTML파일만 주고 받으면 되기 때문에 CDN에 캐시가 가능하다.</p>
<hr>
<h1 id="ssg의-단점">SSG의 단점</h1>
<h3 id="데이터가-정적임">데이터가 정적임</h3>
<p>빌드할때 렌더링하기 때문에 데이터가 정적이다.
데이터가 가변적으로 변하는 웹사이트에는 적합하지 않음</p>
<h3 id="사용자별-정보-제공이-어려움">사용자별 정보 제공이 어려움</h3>
<p>사용자별로 장바구니에 담은 데이터가 달른경우 사용자별로 정보를 제공해야하는데 빌드할 때 렌더링하기 때문에 이런 정보를 제공하기가 어렵다.</p>
<p>즉, 사용자들이 모두 같은 내용의 정보만 봐도 괜찮은 웹애플리케이션에는 적합하나 그렇지 않으면 적합하지 않다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] CSR 특징, 장/단점]]></title>
            <link>https://velog.io/@seeyong_0/Next.js-CSR-%ED%8A%B9%EC%A7%95-%EC%9E%A5%EB%8B%A8%EC%A0%90</link>
            <guid>https://velog.io/@seeyong_0/Next.js-CSR-%ED%8A%B9%EC%A7%95-%EC%9E%A5%EB%8B%A8%EC%A0%90</guid>
            <pubDate>Fri, 14 Jul 2023 05:24:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/1fc76ae2-abdc-4877-91e4-4f07150c8ce7/image.webp" alt=""></p>
<h1 id="csr이란">CSR이란?</h1>
<p>Client Side Rendering
렌더링 하는 주체자가 클라이언트(브라우저)라는 의미이다.</p>
<p>클라이언트에서 서버로 요청하면
서버에서 HTML, 리액트 라이브러리 자체, 우리가 작성한 소스코드를 보내준다.
보내준 것들을 클라이언트 측에서 다운로드한 뒤
소스코드로 DOM트리를 만든 후에 클라이언트가 볼 수 있는 페이지가 나타나는 것이다.</p>
<hr>
<h1 id="csr의-장점">CSR의 장점</h1>
<h3 id="처음-한번만-로딩이-되면-그-이후에는-빠른-ux를-제공할-수-있다">처음 한번만 로딩이 되면, 그 이후에는 빠른 UX를 제공할 수 있다.</h3>
<p>어플리케이션에서 링크를 클리가하거나 하는 요청을 보내면
전체적으로 다시 렌더링할 필요 없이 
필요한 부분만 업데이트할 수 있기 때문이다.</p>
<h3 id="서버의-부하가-적음">서버의 부하가 적음</h3>
<p>앞선 장점과 이어지는 부분
필요한 부분만 업데이트하면 되기 때문에 서버에 무리가 가지 않는다</p>
<h1 id="csr의-단점">CSR의 단점</h1>
<h3 id="페이지-로딩-시간이-길다">페이지 로딩 시간이 길다</h3>
<p>TTV(time to view) 또는 FCP(First Contentful Paint)가 길다고도 한다.</p>
<p>처음에 리액트 라이브러리와 모든 소스코드를 다 다운로드하고 compute(계산)해야하기 때문이다.</p>
<h3 id="자바스크립트-활성화가-필수이다">자바스크립트 활성화가 필수이다</h3>
<p>HTML이 비어있기 때문에 리액트 라이브러리와 자바스크립트 코드가 필수이다.</p>
<p>만약 사용자가 브라우저에서 자바스크립트를 비활성화시키면 우리가 만들 어플리케이션을 사용할 수 없다.</p>
<h3 id="seo-최적화가-힘들다">SEO 최적화가 힘들다</h3>
<p>이 또한 HTML이 비어있기 때문에 발생하는 문제이다.
웹크롤러가 어떤 정보가 있는지 확인하기 어렵기 때문에 최적화가 어렵다.</p>
<h3 id="보안에-취약하다">보안에 취약하다</h3>
<p>모든 소스코드를 브라우저에서 다운받아야하기 때문에 
이 페이지를 실행하기 위해 어떤 서버에서 어떤 데이터를 받아오는지 
서버에서 데이터를 접근하기 위해 획득하는 키라던지 
이런 모든 코드 요소가 다 클라이언트에 가야하기 때문에 보안에 취약할 수 밖에 없다.</p>
<h3 id="cdn에-캐시가-안됨">CDN에 캐시가 안됨</h3>
<p>CDN이란 Content Delivery Network의 약자로
한국에서 미국사이트에 접속하는 경우 미국 서버에 데이터를 요청하고 받아오는 과정이 오래 걸리기 때문에 두 국가의 중간 지점에 있는 국가의 서버에 접속하고자하는 사이트의 데이터가 있는지 물어본다.</p>
<p>만약 데이터가 없으면 미국 서버에 데이터를 요청해 받은 후 캐싱해두고 이후에 다른 사용자가 또 같은 데이터를 요청하면 미국 서버에 요청할 필요 없이 캐싱해둔 데이터를 보내줄 수 있는 것이다. </p>
<p>CSR의 경우 HTML이 비어 있어서 소스코드가 필요하기 때문에 CDN에 캐싱하기가 어렵다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Figma 필수 단축키]]></title>
            <link>https://velog.io/@seeyong_0/Figma-%ED%95%84%EC%88%98-%EB%8B%A8%EC%B6%95%ED%82%A4</link>
            <guid>https://velog.io/@seeyong_0/Figma-%ED%95%84%EC%88%98-%EB%8B%A8%EC%B6%95%ED%82%A4</guid>
            <pubDate>Fri, 07 Jul 2023 03:31:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/839cce9a-4cc6-4a89-ad68-dc29c8d82466/image.png" alt=""></p>
<blockquote>
<p>프론트엔드 개발자로서 디자인 능력을 갖추면 더욱 경쟁력 있는 개발자가 될 수 있을 것 같아서 피그마를 배우기 시작했다.</p>
</blockquote>
<h1 id="피그마-필수-단축키">피그마 필수 단축키</h1>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/b7028e52-31b4-455d-b339-a93d61bdf45c/image.png" alt=""></p>
<p>작업의 효율을 높이기 위해 기본적인 단축키들을 정리해봤다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NextJS 라우팅 연습하기]]></title>
            <link>https://velog.io/@seeyong_0/NextJS%EB%A1%9C-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EC%97%B0%EC%8A%B5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@seeyong_0/NextJS%EB%A1%9C-%EB%9D%BC%EC%9A%B0%ED%8C%85-%EC%97%B0%EC%8A%B5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 05 Jul 2023 03:10:34 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/30bd5e96-dcef-4afe-b878-e5e4ce3d4fe1/image.gif" alt=""></p>
<blockquote>
<p>NextJs를 사용해 간단한 이벤트 관리 프로젝트를 만들면서 라우팅과 필터링을 연습했습니다.</p>
</blockquote>
<hr>
<h1 id="이벤트-관리-프로젝트-소개">이벤트 관리 프로젝트 소개</h1>
<p>사용자가 어떤 이벤트들이 있는지 둘러 볼 수 있는 간단한 웹페이지 입니다.</p>
<h1 id="프로젝트-routes-계획">프로젝트 Routes 계획</h1>
<p>NextJS는 파일 기반 라우팅을 제공하기 때문에 처음부터 프로젝트의 구조를 체계적으로 계획하고 파일 구조를 만들면 좋습니다.</p>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/2c2373c7-ecbd-42a0-a71f-09038d1ce701/image.png" alt=""></p>
<p>** 메인 페이지: ** 진행중인 주요 이벤트 목록을 볼 수 있음
** 이벤트 페이지: ** 모든 이벤트 목록을 볼 수 있음
** 이벤트 디테일 페이지: ** 선택한 이벤트의 상세 정보를 볼 수 있음
** 필터링된 이벤트 페이지: ** 선택한 연도와 월에 진행되는 이벤트만 필터링해서 보여주는 페이지</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Next.js ] 생성된 프로젝트 분석하기]]></title>
            <link>https://velog.io/@seeyong_0/Next.js-%EC%83%9D%EC%84%B1%EB%90%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@seeyong_0/Next.js-%EC%83%9D%EC%84%B1%EB%90%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 20 Jun 2023 02:26:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/0a2d0932-089f-4623-9c2f-5d6e7b507261/image.webp" alt=""></p>
<p>NextJS의 프로젝트 구조를 분석하고 React 프로젝트와는 어떤점이 다른지 알아보자.</p>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/e6a73e25-e835-4739-94b2-5df4bcb0848b/image.png" alt=""></p>
<p>NextJS의 중요한 세 개의 폴더 pages, public, styles를 중심으로 분석해보았다.</p>
<h1 id="1-public">1. public</h1>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/d4e9d2c0-a210-48a7-96e3-73f889ab50b9/image.png" alt=""></p>
<p>public 폴더는 page에서 사용할 공공 리소스 (이미지 파일 등)을 포함하는 파일이다.</p>
<h3 id="️-주의할-점-️">‼️ 주의할 점 ‼️</h3>
<p>create-react-app으로 생성한 일반적인 표준 React 앱의 내에서는 public 폴더 내에 index.html 파일이 있지만 NextJS 앱 내에는 그 파일이 없다.</p>
<p>이는 NextJS에는 사전 렌더링 기능이 있기 때문인데, 요청이 서버로 도달할 때에 싱글 페이지 애플리케이션의 단일 페이지가 동적으로 사전 렌더링이 되어 콘텐츠를 포함한 초기 페이지를 반환해서 index.html이 필요 없다.(정확히는 NextJS로 페이지가 언제 사전 렌더링될지 결정할 수 있게 된다. 추후에 더 자세히 알아보자) </p>
<h1 id="2-pages">2. pages</h1>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/2772afc5-ef8a-489c-90af-acb86b9bd984/image.png" alt=""></p>
<p>pages가 가장 중요한 폴더이다.</p>
<p>이 폴더에 파일 기반의 라우팅을 설정할 수 있기 때문이다.
애플리케이션을 구성하는 다양한 페이지들을 정의할 때 pages가 가장 중요하다.</p>
<h1 id="3-styles">3. styles</h1>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/26556a21-72c8-4e40-b902-74a509a31a00/image.png" alt="">
styles 폴더에서는 스타일링 관련한 작업이 진행된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Next.js ] 왜 Next.js를 사용하는가?]]></title>
            <link>https://velog.io/@seeyong_0/Next.js-%EC%99%9C-Next.js%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@seeyong_0/Next.js-%EC%99%9C-Next.js%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80</guid>
            <pubDate>Fri, 16 Jun 2023 08:15:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seeyong_0/post/7e454276-023f-48a6-8563-45a5f3a4b6dd/image.png" alt=""></p>
<h1 id="nextjs란">Next.js란?</h1>
<blockquote>
<p>Next.js는 프로덕션용 React 프레임워크이다.
Next.js는 React.js의 풀스택 프레임워크이다.</p>
</blockquote>
<p>Next.js는 React상에 구축된 프레임워크이다. 라이브러리와의 차이점으로는 프레임워크의 규모가 더욱 크다. 또한 더 많은 기능을 가지고 있으며 코드 작성법 파일 구조화 방법에 대한 보다 명확한 규칙과 가이드라인이 있다. </p>
<p>React 상에 구축된 프레임워크이기 때문에 React의 특성을 더 강화하는 면이 있다. 리액트에서는 직접 추가해야하는 기능들(라우팅 등)이 이미 추가 되어있기 때문이다. &#39;프로덕션용&#39;은 대규모 React 앱 구축을 더욱 쉽게 할 수 있는 점을 의미한다.</p>
<hr>
<h1 id="nextjs의-핵심기능">Next.js의 핵심기능</h1>
<h2 id="1-server-side-rendering">1. Server-side Rendering</h2>
<p>Next.js의 가장 큰 핵심 기능은 바로 <strong>서버사이드렌더링</strong>이다.
서버사이드렌더링이란 클라이언트 측이 아닌 서버 측에서 렌더링이되는 콘텐츠 처리방식이다. 
React의 경우 렌더링을 전부 클라이언트 측에서 처리한다(Client-side Rendering). 
즉 서버가 아니라, 사용자의 브라우저에서 처리가 된다. 
그 결과, 사용자가 페이지를 방문했을 때 서버에서 클라이언트 측으로 전송되는 HTML 코드에 별 내용이 없다.</p>
<h3 id="그게-문제가-되나-csr의-문제점">그게 문제가 되나? (CSR의 문제점)</h3>
<p>상황에 따라 문제가 될 수 있다.</p>
<h4 id="화면-깜빡임으로-사용자의-사용성-저하">화면 깜빡임으로 사용자의 사용성 저하</h4>
<p>일부 데이터를 서버로부터 페칭할 때 클라이언트 측에서 JavaScript코드가 실행된 뒤에 데이터 페칭이 시작되기 때문에 전송된 요청의 회신을 대기하느라 로딩이 생긴다. 
즉, 요청된 페이지가 아직 해당 데이터를 포함하지 않기 때문에 빈 페이지가 잠깐씩 보이는 것이다. </p>
<h4 id="seo에-불리함">SEO에 불리함</h4>
<p>검색엔진에 잘 노출되어야하고 많은 콘텐츠를 포함하고있는 대규모 이커머스 사이트의 경우 SEO가 굉장히 중요하다.
검색하는 사람들은 서버에서 전송하는 HTML만 볼 수 있는데, CSR는 서버에서 클라이언트 측으로 전송되는 HTML 코드에 내용이 없어서 검색에 노출되지가 않는다. 
검색 엔진 사용자들이 콘텐츠를 볼 수 없다면? 그래서 방문자가 없다면? 큰 문제가 될 수 있다.</p>
<h3 id="ssr의-좋은점">SSR의 좋은점</h3>
<p>이런 경우 서버측 렌더링이 훨씬 더 유리하다. 
해당 페이지가 서버에서 미리 렌더링이 되면 
즉, 요청이 서버로 도달할 때 데이터 페칭이 서버측에서 이루어지면 
모든 내용을 포함한 페이지가 사용자와 검색 엔진 사용자들에게 제공될 수 있기 때문이다.</p>
<h3 id="nextjs와-reactjs의-관계">Next.js와 React.js의 관계</h3>
<p>Next.js에는 내장 SSR이 있어 자동으로 페이지를 미리 렌더링하기 때문에 아무 추가 설정 없이도 React 페이지, 컴포넌트를 서버측에서 미리 렌더링 해준다. </p>
<p>유의해야할 점은 Next.js를 사용하면 초기 로딩 혹은 초기 요청의 이후에 브라우저에 표준 React 앱이 여전히 실행 중이라는 것이다. 여전히 표준 싱글 페이지 애플리케이션(SPA)이다. 
그 이후에 사용자가 페이지를 둘러보며 콘텐츠를 확인하는 이런 행동들은 브라우저 상에서 React가 모두 처리한다. 이를 통한 빠른 상호작용을 제공하는 것이 React의 역할인 것이다. 
React 기존의 특징은 유지하면서, 초기 로딩 때 미리 렌더링된 페이지를 갖게 된다.  </p>
<p>즉, 궁극적으로 Next.js를 사용하면 클라이언트측 및 서버 측의 코드가 융합된다고 볼 수 있다.</p>
<h2 id="2-file-based-routing">2. File-based Routing</h2>
<p>NextJS를 사용하면, 파일과 폴더에 페이지와 라우트를 정의하게 된다.
NextJS 앱에는, 이름이 pages라고 설정되어야만 하는 폴더가 있는데.
pages폴더의 구조에 따라 Route가 설정된다.</p>
<p>따라서 React-router를 사용해 추가적인 코드를 작성하지 않아도 된다. 
그러면서도 중첩 라우트, 다이나믹 라우트와 같은 기능들도 지원한다.</p>
<h2 id="3-fullstack-가능">3. Fullstack 가능</h2>
<p>NextJs를 사용하면 React에서 백엔드 코드를 쉽게 추가할 수 있다.
서버 측 사전 렌더링을 통한 클라이언트 측 코드뿐만 아니라
파일 시스템으로 작동을 하거나 데이터베이스로 전달이 되는 백엔드 코드도 
NodeJS 코드를 통해 손쉽게 백엔드 API를 React 프로젝트로 추가할 수 있다.
데이터베이스, 혹은 파일로 데이터를 저장하는 코드나 데이터를 가져오거나 인증을 추가하는 등의 코드를 추가하는 작업이 NextJS를 사용하면 아주 간편해진다. 
독립적인 REST API 프로젝트를 구축할 필요 없이 하나의 프로젝트에 머물면서 React 사용자 인터페이스인 클라이언트 측의 코드도 전부 추가할 수 있으며 백엔드 API 코드와도 융합이 가능하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[포트폴리오 사이트 회고록]]></title>
            <link>https://velog.io/@seeyong_0/%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%EC%82%AC%EC%9D%B4%ED%8A%B8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@seeyong_0/%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%EC%82%AC%EC%9D%B4%ED%8A%B8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Tue, 20 Dec 2022 09:05:19 GMT</pubDate>
            <description><![CDATA[<h1 id="1-프로젝트-소개">1. 프로젝트 소개</h1>
<h2 id="포트폴리오-사이트">포트폴리오 사이트</h2>
<blockquote>
<p>그동안 진행한 프로젝트들을 정리한 포트폴리오 사이트 입니다.
원래 포트폴리오를 노션에 정리했었는데 저의 개성을 잘 나타내지 못하는 것 같아서 직접 포트폴리오 사이트를 기획 및 제작했습니다.
모든 데이터들은 JSON형식으로 정리하여 적용했습니다. 타입스크립트를 사용하여 제작했습니다.</p>
</blockquote>
<h2 id="개발기간-및-인원">개발기간 및 인원</h2>
<ul>
<li>개발 기간: 2022/11/28 ~ 2022/12/13</li>
<li>개발 인원: 개인 프로젝트</li>
</ul>
<h2 id="github-repo">GitHub Repo</h2>
<ul>
<li><a href="https://github.com/seyoung97/seyoung-portfolio">Front end</a></li>
</ul>
<h2 id="시연영상">시연영상</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=GvEnj2rMITI">시연 영상 보러가기</a></li>
</ul>
<h2 id="기술스택">기술스택</h2>
<ul>
<li>Front End
<code>HTML</code> <code>TypeScrip</code> <code>React</code> <code>Vite</code>
<code>React-router</code> <code>react-scroll</code> <code>react-player</code> <code>styled-component</code></li>
</ul>
<hr>
<h1 id="3-담당한-기능">3. 담당한 기능</h1>
<h2 id="프리뷰">프리뷰</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/2f7d9536-e7fc-4a43-8d40-c91159e15091/image.gif" alt=""></p>
<h3 id="배경-동영상">배경 동영상</h3>
<p>video 태그와 position 속성을 활용해 배경에 동영상을 삽입했습니다.
동영상의 용량이 100MB가 넘어 git LFS를 사용해 github에 업로드했습니다.</p>
<h3 id="미리보기-carousel">미리보기 Carousel</h3>
<p>swiper 라이브러리를 사용해 그동안 진행한 프로젝트들을 간략하게 소개하는 미리보기를 구현했습니다.
첨부된 이미지들을 직접 디자인 및 제작했습니다.</p>
<h2 id="about-me">About Me</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/2ef802e1-a86a-450a-9894-0362af4c5ff2/image.gif" alt=""></p>
<h3 id="탭-ui">탭 UI</h3>
<p>파일 구조: components &gt; aboutme &gt; AboutMe.tsx &gt; Category.tsx, EducationSection.tsx
Category 컴포넌트에 &#39;category&#39;라는 state를 props로 넘겨주어 탭을 클릭할 때 마다 state 값이 변경되도록 했습니다.
state 값이 변경 되면 조건부 렌더링을 활용해 해당 state 값에 맞는 UI를 출력하도록 구현했습니다.</p>
<h2 id="프로젝트-리스트">프로젝트 리스트</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/956aa60f-5739-49e7-965c-ff8809d1944c/image.gif" alt=""></p>
<h3 id="프로젝트-리스트-데이터-구조">프로젝트 리스트 데이터 구조</h3>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/fb5a26f7-ae87-4d08-bd35-311e7fc77a9c/image.png" alt="">
JSON 형식으로 데이터를 작성했습니다.
projects_list 와 internship_list 두 배열로 구성되어있으며 배열의 요소에 각각 프로젝트에 나열될 정보와 인턴쉽에 나열될 정보들이 객체로 이루어져있습니다.</p>
<h3 id="projectcard-컴포넌트-재사용">ProjectCard 컴포넌트 재사용</h3>
<p>파일 구조: components &gt; projects &gt; Projects.tsx &gt; ProjectCard.tsx
Project와 Internship 구분되어 있었기 때문에 따로 map함수를 두번 작성했습니다.
컴포넌트를 재사용하기 위해 content라고 props 이름을 통일했습니다.</p>
<h3 id="상세페이지로-동적-라우팅">상세페이지로 동적 라우팅</h3>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/4ab7f895-f4bf-4a9a-be53-6f80bf2e85e1/image.png" alt="">
파일 구조: pages &gt; Detail.tsx</p>
<p>프로젝트 카드를 클릭하면 해당 프로젝트에 대한 상세 정보를 보여주는 페이지로 이동되도록 구현했습니다.
상세 정보 페이지도 하드 코딩하지 않고 content 컴포넌트를 재사용했기 때문에 content컴포넌트에 props로 클릭한 프로젝트에 대한 정보가 전달 될 수 있도록 구현해야했습니다.</p>
<p>처음에는 useParams를 사용해 url에 프로젝트의 id 값을 넘겨주고 id값을 인덱스로 사용해 데이터를 전달하면 될것이라고 생각했지만 자료 상의 id값과 인덱스가 달라서 엉뚱한 정보가 전달되었습니다.
project_list와 internship_list 각각 다른 배열에 있는게 문제였습니다. 따라서 ProjectCard 컴
포넌트에 type 과 index라는 props를 전달했고 프로젝트일경우 type에 P를 전달 인턴쉽일 경우 I를 전달했습니다. 그리고 url에 type과 index를 삽입했습니다.</p>
<p>Detail 페이지에서 url에서 type과 index를 추출하여 조건문을 작성해 content에 알맞은 정보가 props로 전달되도록 구현했습니다.</p>
<h2 id="프로젝트-상세-정보">프로젝트 상세 정보</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/252fbbe3-eb9e-47bd-a7d3-35aa7fb80f65/image.gif" alt=""></p>
<h3 id="상세-정보-데이터-구조">상세 정보 데이터 구조</h3>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/2f3b29f5-69e7-4635-98cf-b82cd0e1d65e/image.png" alt="">
projectsList.json 파일에 detail 키를 추가하여 이어서 작성했습니다.
페이지 내용을 하드 코딩 하지 않고 JSON으로 작성한 뒤 데이터를 뿌리는 방식으로 구현했습니다.</p>
<h3 id="목차">목차</h3>
<p>content와 같은 방식으로 데이터를 전달 받아서 프로젝트 별로 다른 목차가 나타나도록 구현했습니다.
react-scroll 라이브러리를 사용하여 목차를 클릭하면 해당 내용으로 스크롤이 이동하도록 구현했습니다.</p>
<hr>
<h1 id="6-성장-point">6. 성장 point</h1>
<p>video 태그와 iframe 태그로 영상을 첨부하는 방법을 배울 수 있었습니다.
배경에 영상을 추가하면서 position 속성들에 대해 더 깊이 이해할 수 있었습니다.
Type alias를 활용해 props에 타입을 지정하는 방식을 연습할 수 있었습니다.
TypeScript에서 undefined 와 null을 처리하는 방법에 대해 배울 수 있었습니다.
react-scroll을 활용해 스크롤 이동을 구현할 수 있게 되었습니다.
복잡한 구조의 데이터를 활용해 컴포넌트를 재사용하는 방법을 배울 수 있었습니다.
useMemo를 활용해 불필요한 렌더링을 줄여 성능을 향상시키는 방법을 배울 수 있었습니다.</p>
<hr>
<h1 id="7-느낀점">7. 느낀점</h1>
<h3 id="방대한-데이터를-다루는-방법">방대한 데이터를 다루는 방법</h3>
<p>그동안에는 이렇게 많은 양의 데이터를 직접 JSON으로 가공하고 적용한 경험이 없었습니다.</p>
<p>정보를 가장 효율적으로 구조화하는 방식에 대해 고민을 많이 해볼 수 있는 기회였습니다.
아직 많이 부족하지만 그래도 프로젝트 상세 정보를 모두 JSON으로 정리하고 적용하는데 성공해서 뿌듯했습니다.</p>
<h3 id="콜백함수의-단점을-보완하는-방법">콜백함수의 단점을 보완하는 방법?</h3>
<p>프로젝트 상세 정보 데이터를 적용할 때 프로젝트 마다 내용의 분량이 다르기 때문에 2중 3중 map을 돌려서 문단 갯수만큼 출력되도록 했습니다.
그러다보니 계속해서 콜백 함수를 작성해야했고 코드의 가독성이 떨어지는 부작용이 생겼습니다. 왜 콜백 지옥이 유명한지 직접 체험할 수 있었습니다.
이 문제는 앞으로 리액트를 더 깊이 공부하면서 리팩토링을 통해 개선하고자합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Pre-onboarding] lululab 병원예약 서비스 회고록]]></title>
            <link>https://velog.io/@seeyong_0/Pre-onboarding-lululab-%EB%B3%91%EC%9B%90%EC%98%88%EC%95%BD-%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@seeyong_0/Pre-onboarding-lululab-%EB%B3%91%EC%9B%90%EC%98%88%EC%95%BD-%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Tue, 20 Dec 2022 08:47:29 GMT</pubDate>
            <description><![CDATA[<h1 id="1-프로젝트-소개">1. 프로젝트 소개</h1>
<h2 id="lululab-병원-예약-서비스">lululab 병원 예약 서비스</h2>
<h2 id="개발기간-및-인원">개발기간 및 인원</h2>
<ul>
<li>개발 기간: 2022/10/17 ~ 2022/10/20</li>
<li>개발 인원: 개인 프로젝트</li>
</ul>
<h2 id="github-repo">GitHub Repo</h2>
<ul>
<li><a href="https://github.com/seyoung97/preonboarding-lululab">Front end</a></li>
</ul>
<h2 id="시연영상">시연영상</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=IKEK2qdGtqk">시연 영상 보러가기</a></li>
</ul>
<h2 id="기술스택">기술스택</h2>
<ul>
<li>Front End
<code>HTML</code> <code>styled-component``TypeScrip</code> <code>React</code> <code>Vite</code>
<code>React-router</code> <code>react-calendar</code> </li>
</ul>
<hr>
<h1 id="3-담당한-기능">3. 담당한 기능</h1>
<h2 id="예약-시간-선택-페이지">예약 시간 선택 페이지</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/3ecff5c0-7758-4d92-a45d-2aef2feacc05/image.gif" alt=""></p>
<h3 id="react-calendar-커스텀">react-calendar 커스텀</h3>
<p>react-calendar 라이브러리를 사용해 calendar UI를 구현했습니다.
공식문서와 개발자도구를 활용해 class name을 확인하면서 웹페이지에 잘 어울리도록 디자인을 변경했습니다.
지난 날짜는 선택하지 못하도록 minDate 속성에 newDate()를 부여했습니다.</p>
<h3 id="날짜별-예약-가능-시간-확인">날짜별 예약 가능 시간 확인</h3>
<p>reservation.json 파일에 날짜별로 예약가능 시간들을 정리했습니다.
getDate()함수를 활용해 limit 변수에 선택한 날짜를 담았습니다.
변수 limit을 reservation.json함수에 접근할 인덱스로 사용했습니다.
예약이 불가능한 시간은 버튼이 disabled가 되도록 구현했습니다.</p>
<h2 id="예약자-정보-입력-페이지">예약자 정보 입력 페이지</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/4b4827de-0d82-4d7a-bac3-994a1f79e965/image.gif" alt=""></p>
<h3 id="입력한-정보-예약자-리스트에-저장하기">입력한 정보 예약자 리스트에 저장하기</h3>
<p>reservatedList는 예약자 리스트 데이터를 담은 state입니다.
useLocation을 활용해 예약시간 선택 페이지에서 url로 넘겨준 예약일시를 reservatedTime 변수에 담았습니다. 변수를 JSX문법을 활용해 화면에 출력했습니다.
input 태그의 value값을 onChange로 감지해 state들에 담았습니다.
예약하기 버튼을 클릭하면 onClick이벤트가 발생해 AddList 함수가 실행되어 reservatedList에 입력한 정보들이 추가되도록 했습니다.</p>
<h2 id="no-show-방지-시스템-구축">no-show 방지 시스템 구축</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/20bf0af8-e1b4-4b83-94b9-777f0b9b3040/image.gif" alt=""></p>
<h3 id="no-show-고객은-온라인-예약-불가하도록-구현">no-show 고객은 온라인 예약 불가하도록 구현</h3>
<p>assets &gt; data &gt; blackList.json파일에 no-show 고객 리스트를 데이터화했습니다.
blackList.json을 ReservationForm에 import한 후 useMemo를 활용해 blackList라는 변수에 담았습니다.
반복문과 조건문을 활용해 사용자가 입력한 정보가 blackList의 연락처와 이름이 같은지 비교한 후 같다면 openError state가 true가 되어 Error 창이 뜨도록했습니다.
반면 입력한 내용이 blackList데이터에 없다면 reservatedList state에 입력한 정보가 추가되어 예약자 리스트에 저장되도록 구현했습니다.</p>
<h2 id="예약자-조회-페이지">예약자 조회 페이지</h2>
<p><img src="https://velog.velcdn.com/images/seeyong_0/post/ff75fd71-fffa-4ab8-bf66-55d104fcd0e5/image.gif" alt=""></p>
<h3 id="연락처로-예약-조회">연락처로 예약 조회</h3>
<p>파일 구조: pages &gt; ReservationInquiry.jsx
props로 받은 reservatedList(예약자 리스트 state)의 예약자 연락처(user_contact)중 조회한 연락처(inquiry)가 있는지 반복문을 사용해 확인했습니다.
같은 연락처가 있다면 isReservated라는 state에 예약 정보를 담아 Modal &gt; Modal.jsx에 props로 전달했습니다.
open state가 true가 되어 modal이 뜨도록했고 modal에 isReservated에 담긴 정보가 출력되도록 구현했습니다.
반면 연락처가 없다면 errorOpen state가 true가 되어 Modal &gt; Error.jsx가 뜨도록했습니다.</p>
<hr>
<h1 id="6-성장-point">6. 성장 point</h1>
<p>react-calendar로 달력 UI를 구현하는 방법을 배울 수 있었습니다.
UI 구현에 그치지 않고 적극적으로 활용하여 예약 시스템에 적용하는 방식을 터득했습니다.
반복문과 조건문 그리고 비교 연산자로 검색 기능을 구현하는 방법을 터득했습니다.
백엔드와 소통하여 offset &amp; limit을 설정, 무한 스크롤 구현
windows height를 활용하여 무한스크롤 구현</p>
<hr>
<h1 id="7-느낀점">7. 느낀점</h1>
<h3 id="재미있었던-예약-기능">재미있었던 예약 기능</h3>
<p>지난 프로젝트들에서 해본 적 없는 기능이라 새로운 기능을 구현하는게 재미있었습니다.
처음 구현해본 만큼 뿌듯함과 동시에 아쉬움이 남는 부분들도 있습니다.</p>
<h3 id="리팩토링-하고-싶은-부분">리팩토링 하고 싶은 부분</h3>
<p>예약자 정보 입력 페이지에 form 태그와 useRef를 사용하면 더 간결한 코드가 될것 같아서 리팩토링을 꼭 진행하고 싶습니다.
또한 리팩토링을 통해 예약자 정보 입력 페이지에 유효성 검사 기능을 추가하고 싶습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Redux] State Mutation]]></title>
            <link>https://velog.io/@seeyong_0/Redux-State-Mutation</link>
            <guid>https://velog.io/@seeyong_0/Redux-State-Mutation</guid>
            <pubDate>Tue, 08 Nov 2022 07:47:59 GMT</pubDate>
            <description><![CDATA[<h1 id="mutation">Mutation</h1>
<p>변수를 변경하는 행위를 mutation이라고 한다. </p>
<pre><code class="language-javascript">const friends =[&quot;dal&quot;]
friends.push(&quot;lynn&quot;)// =&gt; mutation</code></pre>
<h1 id="never-mutate-the-state">Never mutate the State!</h1>
<p>이는 매우 중요한 리덕스의 기본 원칙 중 하나이다.
그럼 어떻게 변경해??
mutate하는 게 아니라 <strong>새로운 objects를 리턴</strong>해야 한다!
상태를 수정하는 것이 아니라 새로운 것을 만들어야함!</p>
<h2 id="state를-mutate한-잘못된-방식">state를 mutate한 잘못된 방식</h2>
<pre><code class="language-javascript">//state를 mutate한 잘못된 방식
const reducer = (state = [], action) =&gt; {
  switch (action.type) {
    case ADD_TODO:
      return state.push(action.text);// NEVER DO THIS!!!!
    case DELETE_TODO:
      return [];
    default:
      return state;
  }
};</code></pre>
<h2 id="새로운-object를-리턴하는-방식">새로운 object를 리턴하는 방식</h2>
<pre><code class="language-javascript">//새로운 object를 리턴하는 방식
const reducer = (state = [], action) =&gt; {
  switch (action.type) {
    case ADD_TODO:
      return [...state, { text: action.text , id:Date.now()}];
    case DELETE_TODO:
      return [];
    default:
      return state;
  }
};

const store = createStore(reducer);
store.subscribe(() =&gt; console.log(store.getState()));

const onSubmit = (e) =&gt; {
  e.preventDefault();
  const toDo = input.value;
  input.value = &quot;&quot;;
  store.dispatch({ type: ADD_TODO, text: toDo });
};

form.addEventListener(&quot;submit&quot;, onSubmit);</code></pre>
]]></description>
        </item>
    </channel>
</rss>