<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>pangs_boy.log</title>
        <link>https://velog.io/</link>
        <description>점진적 과부하</description>
        <lastBuildDate>Tue, 30 Jul 2024 06:16:09 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>pangs_boy.log</title>
            <url>https://velog.velcdn.com/images/pangs_boy/profile/9ada05b5-059f-4d9e-ba4b-77c596b65b59/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. pangs_boy.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/pangs_boy" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[TIL]99클럽 코테 스터디 9일차 TIL(더 맵게)]]></title>
            <link>https://velog.io/@pangs_boy/TIL99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-9%EC%9D%BC%EC%B0%A8-TIL%EB%8D%94-%EB%A7%B5%EA%B2%8C</link>
            <guid>https://velog.io/@pangs_boy/TIL99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-9%EC%9D%BC%EC%B0%A8-TIL%EB%8D%94-%EB%A7%B5%EA%B2%8C</guid>
            <pubDate>Tue, 30 Jul 2024 06:16:09 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/pangs_boy/post/fde17567-9f8c-471b-a546-a5109bde80d7/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/5813fc7b-778f-4a5e-96c7-683851870587/image.png" alt=""></p>
<h1 id="풀이">풀이</h1>
<pre><code class="language-python">import heapq

def solution(scoville, K):
    heapq.heapify(scoville)
    mix = 0

    while scoville[0] &lt; K:  # 가장 작은 값이 K 이상이 될 때까지 반복
        # 예외처리
        if len(scoville) &lt; 2:
            return -1  # 더 이상 섞을 수 없으면 -1 반환

        mix +=1
        one = heapq.heappop(scoville)
        two = heapq.heappop(scoville)
        plus = one + (2*two)

        heapq.heappush(scoville, plus)

    return mix
</code></pre>
<p>heapq를 사용하여 풀면 간단하게 풀 수 있었다. scoville 배열을 heapq로 만들어주고 scoville배열의 맨 첫 번쨰 즉, 가장 작은 수가 K보다 작으면 계속 배열을 돌고 크거나 같다면 탈출하는 방식이다.</p>
<p>이때 one에서 첫번째 [0]을 pop한 후 새로 heapq가 재배열된다. 그 후 가장 작은 수인 two도 삭제하고 plus에 one + 2*two값을 넣어준다. 그 후 scoville에 push해준다.
만약 반복문을 탈출했다면 mix를 반환한다.</p>
<h1 id="회고">회고</h1>
<p>heapq만 알고 있다면 파이썬에서는 정말 쉬운 문제라 생각한다. 역시 적폐 파이썬..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]99클럽 코테 스터디 5일차 TIL(전화번호 목록)]]></title>
            <link>https://velog.io/@pangs_boy/TIL99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-5%EC%9D%BC%EC%B0%A8-TIL%EC%A0%84%ED%99%94%EB%B2%88%ED%98%B8-%EB%AA%A9%EB%A1%9D</link>
            <guid>https://velog.io/@pangs_boy/TIL99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-5%EC%9D%BC%EC%B0%A8-TIL%EC%A0%84%ED%99%94%EB%B2%88%ED%98%B8-%EB%AA%A9%EB%A1%9D</guid>
            <pubDate>Fri, 26 Jul 2024 10:38:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/pangs_boy/post/062e6c32-b54c-42f0-9908-5d2dfb749d88/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/423592b2-72e8-4304-af2a-cea82b226342/image.png" alt=""></p>
<h1 id="풀이">풀이</h1>
<h2 id="첫-번쨰-풀이">첫 번쨰 풀이</h2>
<pre><code class="language-python">def solution(phone_book):
    phone_book.sort()

     for i in range(len(phone_book)):
         for j in range(i + 1, len(phone_book)):
             if phone_book[j].startswith(phone_book[i]):
                 return False</code></pre>
<p>첫 번쨰 풀이는 생각보다 오래 걸리지 않았다. 우선 사전적으로 <strong>phone_book</strong>을 정렬해준 뒤 이중 for문을 돌며 모든 두 쌍을 비교하는 방법이다. 이떄 startswith함수를 사용하여 접두사가 겹치는지 확인하고 겹치면 False, 그렇지 않음 True를 출력한다.
그러나 이 방법은 $O(n^2)$의 시간 복잡도를 가지기 떄문에 효율성 체크에서 막혀버렸다 ㅠㅠㅠ</p>
<h2 id="두-번쨰-풀이">두 번쨰 풀이</h2>
<pre><code class="language-python">def solution(phone_book):
    phone_book.sort()

    for i in range(len(phone_book)-1):
        if phone_book[i+1].startswith(phone_book[i]):
            return False

    return True</code></pre>
<p>sort는 그대로 유지하며 sort의 장점을 활용하기로 했다. 어차피 문자열 배열이 정렬되면서 비슷한 문자열이 같이 배치되었을 것이고 겹치는 문자열의 갯수가 아닌 True, False값만 뱉어내면 되기 때문에 굳이 이중 for문을 돌지 않고 양 옆 문자열만 비교하는 방법을 채택했다. 이렇게 되면 <strong>nlog(n)</strong> 의 시간 복잡도를 갖는다!</p>
<h1 id="회고">회고</h1>
<p>우선 어려운 문제는 아니었다. 딱히 예외 처리를 할 필요도 없어서 한 번 생각나면 쭉 풀수 있는 문제였던 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]99클럽 코테 스터디 4일차 TIL(JadenCase  문자열 만들기)]]></title>
            <link>https://velog.io/@pangs_boy/TIL99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-4%EC%9D%BC%EC%B0%A8-TILJadenCase-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@pangs_boy/TIL99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-4%EC%9D%BC%EC%B0%A8-TILJadenCase-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 25 Jul 2024 02:41:02 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/pangs_boy/post/dc074928-c2e0-453f-b09a-27700a36cbca/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/6295119f-b83d-42c0-bf14-3da92f89b7fa/image.png" alt="">
그래도 이번 문제는 크게 어려움은 없었다. 물론 15분 정도 걸렸지만...
아이디어 자체는 어려움이 없었고 예외처리를 생각하지 못해 시간이 오래걸렸던 것 같다.</p>
<h1 id="풀이">풀이</h1>
<pre><code class="language-python">def solution(s):
    answer =[]
    s_arr = s.split(&quot; &quot;)
    for i in s_arr:
        if len(i) ==0:
            answer.append(i)
        elif i[0].isdigit():
            answer.append(i.lower())
        else:
            answer.append(i[0].upper()+i[1:].lower())

    return &#39; &#39;.join(answer)
</code></pre>
<p>풀이는 간단하다. 공백으로 문자열을 쪼개어 배열로 분할해주고 맨 앞이 숫자이면 전부 소문자로 배열에 추가한다. 그게 아니라면 첫번쨰 문자만 대문자 + 나머지는 소문자로 처리한다. 그런데 이렇게만 하면 런타임에러가 계속 발생해서 절반밖에 통과가 되지 않았다 ㅠㅠ...
무엇이 문제인가 --&gt; 빈배열일떄를 처리해주어야 한다고 한다. 이거 추가하니까 런타임 없이 돌아간다! 또 빈배열일때 elif를 사용하지 않고 if문으로 따로 빼내었는데 이러면 또 런타임에러가 뜸.
어렵진 않았다! isdigit(), title()같은 함수에 대해 공부할 수 있는 시간이었따.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]99클럽 코테 스터디 3일차 TIL(문자열 내 마음대로 정렬하기)]]></title>
            <link>https://velog.io/@pangs_boy/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-3%EC%9D%BC%EC%B0%A8-TIL%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%82%B4-%EB%A7%88%EC%9D%8C%EB%8C%80%EB%A1%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@pangs_boy/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-3%EC%9D%BC%EC%B0%A8-TIL%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%82%B4-%EB%A7%88%EC%9D%8C%EB%8C%80%EB%A1%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 24 Jul 2024 09:34:49 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/pangs_boy/post/a57f3267-20d6-4060-92f1-95c39e810d7d/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/d50e227d-b735-4a0b-8398-f603c288964b/image.png" alt=""></p>
<p>오랜만에 코테 복귀를 하려니 꽤나 막막했다. 쉬워보이는 문제도 고민을 해야할 정도로 실력이 형편없다는 것을 느꼈다 흑흑. 문제를 다 읽고 나서 가장 먼저 든 생각은 문자열의 n번째 알파벳으로 정렬하기 위해서는 일단 앞으로 뺴내자였고 그 다음 &#39;사전순&#39;은 그냥 정렬하면 되겠구나 싶었다.</p>
<h1 id="풀이">풀이</h1>
<pre><code class="language-python">def solution(strings, n):
    # 우선 for문을 돌면서 sort로 n번쨰를 뽑아내 섞는다.
    answer = [] # 빈배열
    for i in strings: # strings배열 만큼 돈다.
        answer.append(i[n]+i) # n번째 알파벳을 뽑은 후 원래 string과 합친다. 
    answer.sort() # 그리고 알파벳 순서대로 sort를 하면 n번째 알파벳 순서로 정렬됨과 동시에 같은 값은 사전순이 된다.
    sorted_Answer = [i[1:] for i in answer] # 맨 앞 알파벳을 빼고
    return sorted_Answer #출력한다.</code></pre>
<p>그래서 우선 밖으로 맨 앞으로 n번째 문자를 빼내었다. 그리고 그냥 섞어버림 끝이다. 하하
그 뒤에 맨 앞 문자는 포함하면 안되므로 1: 두번째 문자부터 출력해주면 정답이다. 매번 코테 준비를 했다 안했다를 반복하니까 풀때마다 문법이 헷갈린다... 그나마 젤 쉬운게 파이썬이긴 하지만 문법에 관한 공부를 더 해야겠다는 생각
이 심정을 대변하듯 다른 분들의 풀이를 보니 <strong>단 한줄</strong>에 코드가 끝나더라~</p>
<h2 id="대표적인풀이">대표적인(?)풀이</h2>
<pre><code class="language-python">return sorted(strings, key=lambda x: (x[n], x))</code></pre>
<h1 id="결론">결론</h1>
<p>무작정 문제를 풀 것이 아닌 문법 공부도 같이하자!(lambda 포함)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] Solid 원칙으로 React Hook 작성하기(ISP)]]></title>
            <link>https://velog.io/@pangs_boy/CS-Solid-%EC%9B%90%EC%B9%99%EC%9C%BC%EB%A1%9C-React-Hook-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0ISP</link>
            <guid>https://velog.io/@pangs_boy/CS-Solid-%EC%9B%90%EC%B9%99%EC%9C%BC%EB%A1%9C-React-Hook-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0ISP</guid>
            <pubDate>Wed, 10 Jul 2024 13:48:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>사용하지 않는 메서드에 의존하도록 코드를 강제해서는 안된다.</p>
</blockquote>
<p>이름에서 알 수 있듯이 인터페이스와 관련이 있으며, 기본적으로 함수와 클래스는 명시적으로 사용하는 인터페이스만 구현해야 한다는 뜻이다. 이는 인터페이스를 깔끔하게 유지하고 클래스가 여러 메서드를 포함하는 하나의 큰 인터페이스를 구현하는 대신 몇 개의 인터페이스를 선택하여 구현하도록 하는 것이 가장 쉬운 방법이다.</p>
<p>예를 들어 웹사이트를 소유한 사람을 나타내는 클래스는 그 사람에 대한 세부 정보를 설명하는 Person이라는 인터페이스와 소유한 웹사이트에 대한 메타데이터가 있는 Website라는 인터페이스 두 개를 구현해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/d8586bfe-a606-44d1-95b9-d32185b563e7/image.png" alt=""></p>
<blockquote>
<p>만약 소유자와 웹사이트에 대한 정보를 모두 포함하는 단일 인터페이스 웹사이트를 만들었다면 이는 인터페이스 분리 원칙에 위배된다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/bc830161-13de-4023-8730-25a172767706/image.png" alt=""></p>
<blockquote>
<p>위 인터페이스의 문제점이 무엇일까?? 문제는 인터페이스의 사용성을 떨어뜨린다는 것이다. 만약 human이 아니라 company라면 company는 실제로 family name이 없다. 그렇다면 human과 company 모두 사용할 수 있도록 인터페이스를 수정헤야할까? 아니면 CompanyOwnedWebsite라는 새로운 인터페이스를 만들어야할까?
그러면 선택적 프로퍼티가 많은 인터페이스를 만들거나 각각 PersonWebsite 및 CompanyWebsite이라는 두 개의 인터페이스를 만들 수 있다. 이 두 가지 솔루션 중 어느 것도 최적은 아닐 것이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/c64bd56a-18c4-40ff-85d4-5854bbfbced5/image.png" alt=""></p>
<p>이때 ISP를 따르는 솔루션은 다음과 같다.
<img src="https://velog.velcdn.com/images/pangs_boy/post/b6524532-5034-47c9-baab-29110791dc29/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] Solid 원칙으로 React Hook 작성하기(LSP)]]></title>
            <link>https://velog.io/@pangs_boy/CS-Solid-%EC%9B%90%EC%B9%99%EC%9C%BC%EB%A1%9C-React-Hook-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0LSP</link>
            <guid>https://velog.io/@pangs_boy/CS-Solid-%EC%9B%90%EC%B9%99%EC%9C%BC%EB%A1%9C-React-Hook-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0LSP</guid>
            <pubDate>Mon, 08 Jul 2024 03:00:15 GMT</pubDate>
            <description><![CDATA[<h1 id="리스코프-치환-원칙lsp---liskov-substitution-principle">리스코프 치환 원칙(LSP - Liskov Substitution Principle)</h1>
<blockquote>
<p>Hook/컴포넌트가 일부 props를 받아들이는 경우, 해당 훅/컴포넌트를 확장하는 모든 Hook과 컴포넌트는 확장하는 Hook/컴포넌트가 받아들이는 모든 props를 받아들여야 한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/13a15dd6-dc1f-45a2-80e4-def3db5531c1/image.png" alt="">
위의 훅을 사용하면 useLocalAndRemoteStorage는 useLocalStorage와 동일한 작업(로컬 스토리지에 저장)을 수행하지만, 데이터를 추가 장소에 저장하여 useLocalStorage의 기능을 확장했기 때문에 useLocalStorage의 하위 유형으로 볼 수 있습니다.</p>
<p>두 Hook에는 일부 공유 props와 반환값이 있지만, useLocalAndRemoteStorage에는 useLocalStorage가 허용하는 onDataSaved 콜백 props가 누락되어 있다. 반환 프로퍼티의 이름도 다르게 지정되어 있으며, 로컬 데이터는 useLocalStorage에서는 data로 이름이 지정되지만 useLocalAndRemoteStorage에서는 localData로 이름이 지정된다.</p>
<p>리스코프는 이를 지원하기 위해 Hook을 업데이트할 것입니다. 또한, useLocalStorage 훅의 로컬 데이터 변수 명도 useLocalAndRemoteStorage의 로컬 데이터 변수 명과 일치하도록 업데이트할 것이다.
<img src="https://velog.velcdn.com/images/pangs_boy/post/78cdf6df-fa68-474f-9c7c-d29de18d6022/image.png" alt="">
Hook에 공통 인터페이스(들어오는 props, 나가는 반환값)가 있으면 교환이 매우 쉬워질 수 있다. 그리고 리스코프 치환 원칙을 따른다면, 다른 Hook/컴포넌트를 상속하는 Hook과 컴포넌트는 그 Hook이나 컴포넌트가 상속하는 Hook이나 컴포넌트로 치환할 수 있어야 한다.</p>
<hr>
<h1 id="react에서-lsp를-사용하는-이유는-무엇일까">React에서 LSP를 사용하는 이유는 무엇일까?</h1>
<p>React에서 상속은 그다지 눈에 띄지는 않지만, 내부적으로는 분명히 사용된다. 웹 애플리케이션에는 종종 비슷해 보이는 컴포넌트가 여러 개 있기도 한다. 텍스트, 제목, 링크, 아이콘 링크 등은 모두 비슷한 유형의 컴포넌트이며 상속을 통해 이점을 얻을 수 있다.</p>
<p>IconLink 컴포넌트는 Link 컴포넌트를 래핑할 수도 있고 그렇지 않을 수도 있다. 어느 쪽이든 동일한 인터페이스(동일한 props 사용)로 구현하면 이점을 얻을 수 있다. 이렇게 하면 추가 코드를 편집할 필요 없이 애플리케이션의 어느 곳에서든 언제든지 Link 컴포넌트를 IconLink 컴포넌트로 교체하는 것이 간단하다.</p>
<p>Hook도 마찬가지이다. 웹 애플리케이션은 서버에서 데이터를 가져온다. 로컬 스토리지나 상태 관리 시스템을 사용할 수도 있다. 이러한 경우 props을 공유하여 상호 교환할 수 있도록 하는 것이 바람직하다.</p>
<p>애플리케이션은 백엔드 서버에서 사용자, 작업, 제품 또는 기타 데이터를 가져올 수 있다. 이러한 함수는 인터페이스를 공유하여 코드와 테스트를 더 쉽게 재사용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] Solid 원칙으로 React Hook 작성하기(OCP)]]></title>
            <link>https://velog.io/@pangs_boy/CS-Solid-%EC%9B%90%EC%B9%99%EC%9C%BC%EB%A1%9C-React-Hook-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0OCP</link>
            <guid>https://velog.io/@pangs_boy/CS-Solid-%EC%9B%90%EC%B9%99%EC%9C%BC%EB%A1%9C-React-Hook-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0OCP</guid>
            <pubDate>Mon, 08 Jul 2024 02:36:27 GMT</pubDate>
            <description><![CDATA[<h1 id="개방폐쇄-원칙ocp---openclosed-principle">개방/폐쇄 원칙(OCP - Open/Closed Principle)</h1>
<blockquote>
<p>소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에는 개방적이어야 하지만, 수정에는 폐쇄적이어야 한다.</p>
</blockquote>
<p>즉 <span style="color:red">다시 건들지 않을 수 있도록 Hook과 컴포넌트를 작성하고, 다른 Hook과 컴포넌트에서는 재사용만 해야한다.</span>
<img src="https://velog.velcdn.com/images/pangs_boy/post/2e34f704-8c64-4df9-b82f-b5520b712d3f/image.png" alt="">
위 Hook은 user를 가져와 반환한다. 관리자는 이메일을 업데이트할 수 있고 일반 유저는 이메일 업데이트를 할 수 없다. 그러나 위 코드는 유지보수하기에 용이한 코드는 아니다.
<img src="https://velog.velcdn.com/images/pangs_boy/post/4307b156-9ec5-409d-a468-17f8dff5f352/image.png" alt="">
이처럼 일반 유저와 관리자의 코드를 분리했다. 이 경우 만약 일반 유저에 문제가 생겼을때 관리자 코드를 건드리지 않고 일반 유저에 대한 문제만 해결할 수 있다. 또한 새로운 user 유형이 추가되거나 useAdmin HOok이 업데이트되면 useUser를 건드릴 필요가 없어진다. <span style="color:red">즉, 새로운 유저 유형을 추가할 때 일반 유저에게 실수로 버그를 전송할 필요가 없어진다.</span></p>
<hr>
<h1 id="react에서-ocp를-사용하는-이유는-무엇일까">React에서 OCP를 사용하는 이유는 무엇일까?</h1>
<p>너무 많은 컴포넌트 분할에는 많은 렌더링 비용이 들어간다. 그러나 위의 예시에서는 Hook이 최소한으로 사용되었고, 그다지 많은 일을 실행하지 않았다.
추가 Hook이 필요하고 구현하는 데 시간이 더 걸릴 수 있지만, 확장성이 높아져 더 자주 재사용할 수 있다. 또한 테스트를 다시 작성하는 횟수가 줄어들어 Hook이 더 견고해진다. 그리고 가장 중요한 것은 <span style="color:red">이전 코드를 건드리지 않으면 버그를 만들지 않는다는 것이다.</span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] Solid 원칙으로 React Hook 작성하기(SRP)]]></title>
            <link>https://velog.io/@pangs_boy/CS-Solid-%EC%9B%90%EC%B9%99%EC%9C%BC%EB%A1%9C-React-Hook-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0SRP</link>
            <guid>https://velog.io/@pangs_boy/CS-Solid-%EC%9B%90%EC%B9%99%EC%9C%BC%EB%A1%9C-React-Hook-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0SRP</guid>
            <pubDate>Tue, 02 Jul 2024 14:06:46 GMT</pubDate>
            <description><![CDATA[<p>우선 React Hook 작성에 앞서 Solid 원칙이 무엇인지 간단하게 알아보고 가도록 한다.</p>
<h1 id="객체-지향-설계의-5원칙-solid">객체 지향 설계의 5원칙 S.O.L.I.D</h1>
<p>SOLID원칙이란 객체 지향 설계에서 지켜줘야 할 5개의 소프트웨어 개발 원칙을 말한다.</p>
<ul>
<li><span style="color: red">S</span>RP(Single Responsibility Principle) - 단일 책임 원칙</li>
<li><span style="color: red">O</span>CP(Open Closed Principle) - 개방 폐쇄 원칙</li>
<li><span style="color: red">L</span>SP(Listov Substitution Principle) - 리스코프 치환 원칙</li>
<li><span style="color: red">I</span>SP(Interface Segregation Principle) - 인터페이스 분리 원칙</li>
<li><span style="color: red">D</span>IP(Dependency Inversion Principle) - 의존 역전 원칙</li>
</ul>
<hr>
<p>이 중 먼저 <strong>SRP</strong>를 적용한 React Hook 예시를 살펴 보자.</p>
<h1 id="단일-책임-원칙single-responsibility-principle">단일 책임 원칙(Single Responsibility Principle)</h1>
<p><span style="color: red"><strong>모듈은 단 하나의 액터만 담당해야 한다.</strong></span>
<img src="https://velog.velcdn.com/images/pangs_boy/post/1e8dcf0d-4408-436a-a6ea-5657696220c6/image.png" alt=""></p>
<p>이 Hook은 단일 책임 원칙(SRP)을 준수하지 않으므로 SOLID 하지 않는다. user 데이터를 가져오는 책임과 todo 데이터를 가져오는 책임 두 가지를 모두 가지고 있기 때문이다.</p>
<p>대신 위의 코드를 user에 대한 데이터를 가져오는 훅과 todo 데이터를 가져오는 훅으로 분리해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/314c718f-f7dc-47c8-91e7-893864d674c2/image.png" alt="">
위 원칙은 모든 Hook과 컴포넌트에 적용되며 모두 각각 한 가지 일만 해야한다. 이 원칙을 적용할 때 고려해야할 사항은 다음과 같다.</p>
<h3 id="1-ui를-표시해야-하는-컴포넌트인가-아니면-로직을-처리해야하는-컴포넌트-인가">1. UI를 표시해야 하는 컴포넌트인가? 아니면 로직을 처리해야하는 컴포넌트 인가?</h3>
<h3 id="2-이-hook은-어떤-단일-유형의-데이터를-처리해야-하는가">2. 이 Hook은 어떤 단일 유형의 데이터를 처리해야 하는가?</h3>
<h3 id="3-이-hook-또는-컴포넌트는-어떤-layer에-속하는가">3. 이 Hook 또는 컴포넌트는 어떤 Layer에 속하는가?</h3>
<p>위 고려사항에 대해 같은 답변을 하지 못하는 Hook을 만들고 있다면 SRP 원칙을 위반하고 있다는 것이다!</p>
<hr>
<h1 id="react에서-srp를-사용하는-이유는-무엇일까">React에서 SRP를 사용하는 이유는 무엇일까?</h1>
<p>SRP는 실제로 React와 잘 어울린다. React는 컴포넌트 기반 아키텍처를 따르며, 이는 작은 컴포넌트들이 함꼐 모여 애플리케이션을 구성할 수 있도록 구성된 아키텍처를 의미한다. 컴포넌트의 크기가 작을수록 재사용할 수 있는 가능성이 높아진다.</p>
<p>이러한 이유 때문에 React는 SRP에 기반을 두고 있다. 이 원칙을 따르지 않으면 항상 새로운 Hook과 컴포넌트를 작성해야하고 재사용할 수 없다.</p>
<p>또한 SRP를 따르지 않으면 코드를 테스트하기 어려워진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] CSR, SSR의 차이와 장단점]]></title>
            <link>https://velog.io/@pangs_boy/CS-CSR-SSR%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%99%80-%EC%9E%A5%EB%8B%A8%EC%A0%90</link>
            <guid>https://velog.io/@pangs_boy/CS-CSR-SSR%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%99%80-%EC%9E%A5%EB%8B%A8%EC%A0%90</guid>
            <pubDate>Wed, 26 Jun 2024 10:06:50 GMT</pubDate>
            <description><![CDATA[<p>우선 제목과 같이 간단하게 CSR과 SSR에 대한 개념을 먼저 살펴보자.</p>
<h1 id="1-csr-ssr이란">1. CSR, SSR이란?</h1>
<h2 id="1-1-csr">1-1. CSR</h2>
<p>CSR의 동작과정을 살펴보자면 다음과 같다.
<img src="https://velog.velcdn.com/images/pangs_boy/post/0809c86e-4e1b-4d49-b120-1c4d8477f02a/image.png" alt=""></p>
<ol>
<li>유저가 웹 사이트에 방문하면, 브라우저가 서버에 콘텐츠를 요청한다.</li>
<li>이에 서버는 빈 뼈대만 있는 HTML을 응답으로 보내준다.</li>
<li>브라우저가 연결된 JS 링크를 통해 서버로부터 다시 JS 파일을 다운로드한다.</li>
<li>JS를 통해 동적으로 페이지를 만들어 브라우저에 띄운다.<h2 id="1-2-ssr">1-2. SSR</h2>
SSR의 동작과정을 살펴보자면 다음과 같다.
<img src="https://velog.velcdn.com/images/pangs_boy/post/29841814-29d9-483c-bd27-03d6ad1a0e11/image.png" alt=""></li>
<li>유저가 웹 사이트에 방문하면, 브라우저가 서버에 콘텐츠를 요청한다.</li>
<li>이에 서버는 페이지에 필요한 데이터를 즉시 얻어와 모두 삽입하고, CSS까지 적용하여 렌더링 준비를 마친 HTML과 JS 코드를 브라우저에 응답으로 전달한다.</li>
<li>브라우저는 JS 코드를 다운로드하고 HTML에 JS 로직을 연결한다.</li>
</ol>
<h1 id="2spa와-mpa">2.SPA와 MPA</h1>
<p>위에선 본 글의 주제인 CSR과 SSR의 동작원리에 대해 간단하게 살펴보았다. 그러나 더 깊게 CSR과 SSR에 대해 설명하기 위해서는 SPA와 MPA의 차이점을 알아야 한다.</p>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/e558f43e-0887-4088-9f19-81034a692fcc/image.png" alt=""></p>
<h2 id="2-1-spa">2-1. SPA</h2>
<p>SPA(Single Page Application)은 하나의 페이지로 구성된 웹 어플리케이션을 말한다.</p>
<h2 id="2-2-mpa">2-2. MPA</h2>
<p>MPA(Multi Page Application)은 이동할 때마다 서버로부터 새로운 HTML을 받아와 페이지 전체를 렌더링하는 웹 페이지 구성 방법이다.</p>
<h1 id="3-csr-ssr-깊이-들어가기">3. CSR, SSR 깊이 들어가기</h1>
<ul>
<li>SPA에서는 CSR로 렌더링하고, MPA에서는 SSR로 렌더링 한다.</li>
<li>SPA는 필요한 정적 리소스를 한 번에 모두 다운로드하고, 이후 새로운 페이지 요청이 왔을 떄 필요한 데이터만 전달받아 클라이언트에서 필요한 페이지를 갱신하기 때문에 CSR로 렌더링한다.</li>
<li>MAP는 새로운 요청이 있을 때마다 이미 렌더링 된 정적 리소스를 받아오기 때문에 SSR로 렌더링한다.</li>
</ul>
<h2 id="3-1-csr과-ssr의-차이점">3-1. CSR과 SSR의 차이점</h2>
<p>CSR(Client Server Rendering)는 클라이언트 측에서 렌더링하는 방식이고, SSR은 Server Side Rendering의 약자로, 서버 측에서 렌더링하는 방식이다.
즉 <strong>어디에서 렌더링을 준비하냐</strong>의 차이가 있다.</p>
<h2 id="3-2-ssr과-ssg의-차이점">3-2. SSR과 SSG의 차이점</h2>
<p>SSG(Static Site Generation)은 서버에서 HTMl을 보내주는 방식을 취하여 SSR과 유사하나 <strong>언제</strong> 만들어주는지에 차이가 있다.
SSR은 요청시 서버에서 즉시 HTML을 만들어 응답하기 때문에 데이터가 달라지거나 수정이 어려운 페이지에 적합하고, SSG는 페이지들을 모두 서버에 만들어 놓은 후 해당페이지를 응답하는 것으로 캐싱해두면 좋을 페이지에 사용하면 적합하다.</p>
<h1 id="4-csr의-장단점">4. CSR의 장,단점</h1>
<h2 id="4-1-장점">4-1. 장점</h2>
<ol>
<li>페이지 전화신 화면 깜빡임이 없다.</li>
<li>초기 로딩 이후 구동 속도가 빠르다.</li>
<li>TTV와 TTi의 시간차가 없다.</li>
<li>서버의 부하가 Client로 분산된다.<h2 id="4-2-단점">4-2. 단점</h2>
</li>
<li>초기 로딩 속도가 느리다.</li>
<li>검색엔진(SEO)에 불리하다.</li>
</ol>
<h1 id="5-ssr의-장단점">5. SSR의 장,단점</h1>
<h2 id="5-1-장점">5-1. 장점</h2>
<ol>
<li>초기 구동속도가 빠르다.</li>
<li>SEO에 능하다.<h2 id="5-2-단점">5-2. 단점</h2>
</li>
<li>페이지 전환시 깜빡임이 있다.</li>
<li>TTV와 TTI 사이 시간차가 있다.</li>
<li>서버 부하가 존재한다.</li>
</ol>
<h1 id="6-csr-단점-보완-방법">6. CSR 단점 보완 방법</h1>
<h2 id="6-1-초기-로딩-속도-문제-해결">6-1. 초기 로딩 속도 문제 해결</h2>
<p>Code Spliting, Tree-Shaking, Chunk 분리 등을 통해 JS 번들 크기를 줄여 초기 DOM 생성 속도를 줄여 초기 로딩 속도를 개선할 수 있다.</p>
<h2 id="6-2-검색엔진-개선">6-2. 검색엔진 개선</h2>
<p>Pre-rendering 방식을 통해 검색엔진을 개선할 수 있다.</p>
<h1 id="7-어떤-상황에-무엇을-써야할까">7. 어떤 상황에 무엇을 써야할까?</h1>
<table>
<thead>
<tr>
<th>Rendering Method</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><strong>CSR</strong></td>
<td>- 유저와 상호작용이 많다<br>- 대부분이 고객의 개인정보로 이루어진 페이지들이라 검색엔진에 노출될 필요는 없다</td>
</tr>
<tr>
<td><strong>SSR</strong></td>
<td>- 회사 홈페이지여서 홍보나 상위노출이 필요하다<br>- 누구에게나 항상 같은 내용을 보여준다<br>- 업데이트가 빈번해 해당 페이지 데이터가 자주 바뀐다</td>
</tr>
<tr>
<td><strong>SSG</strong></td>
<td>- 회사 홈페이지여서 홍보나 상위노출이 필요하다<br>- 누구에게나 항상 같은 내용을 보여준다<br>- 업데이트를 거의 하지 않는다</td>
</tr>
<tr>
<td><strong>Universal Rendering</strong></td>
<td>- 사용자에 따라 페이지 내용이 달라진다<br>- 빠른 interaction과 화면 깜빡임이 없어야 한다<br>- SEO를 포기할 수 없어 상위노출이 되면 좋겠다</td>
</tr>
</tbody></table>
<h1 id="8-대표적인-프레임워크">8. 대표적인 프레임워크</h1>
<table>
<thead>
<tr>
<th>Rendering Method</th>
<th>대표적인 프레임워크</th>
</tr>
</thead>
<tbody><tr>
<td><strong>CSR</strong></td>
<td>- React<br>- Vue.js<br>- Angular</td>
</tr>
<tr>
<td><strong>SSR</strong></td>
<td>- Next.js (React 기반)<br>- Nuxt.js (Vue.js 기반)<br>- Angular Universal</td>
</tr>
<tr>
<td><strong>SSG</strong></td>
<td>- Next.js (React 기반, SSG 지원)<br>- Nuxt.js (Vue.js 기반, SSG 지원)<br>- Gatsby (React 기반)</td>
</tr>
</tbody></table>
<p><a href="https://dev-ellachoi.tistory.com/28">참고1</a>, <a href="https://www.youtube.com/watch?v=YuqB8D6eCKE">참고2</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] - React에서 SMS로 로그인하기]]></title>
            <link>https://velog.io/@pangs_boy/TIL-React%EC%97%90%EC%84%9C-SMS%EB%A1%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@pangs_boy/TIL-React%EC%97%90%EC%84%9C-SMS%EB%A1%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 24 Jun 2024 04:17:32 GMT</pubDate>
            <description><![CDATA[<p>소개팅 웹 앱을 개발하던 도중 로그인 처리를 어떤 방식으로 해야할지 고민이었다. 다른 소개팅 앱의 레퍼런스를 확인하던 중 대부분의 서비스들이 핸드폰 번호를 사용한 SMS로그인 형식을 사용하고 있었다. 
사용자가 번호를 입력하면 인증번호를 보내주어 이를 PW처럼 사용하는 방식이었는데, 이 방식을 사용하면 미세하게나마 우리가 돈을 내야했다 ㅠㅠㅠ... 그래서 어떻게 돈을 안들이고 로그인을 처리할 수 있을까 고민하던 중 역으로 사용자가 우리한테 메시지를 보내면 어떨까? 라는 생각이 들어 이를 적용하기로 하였다.</p>
<h2 id="프로세스는-크게-다음과-같다">프로세스는 크게 다음과 같다.</h2>
<h3 id="1-사용자가-핸드폰-번호를-입력한-후-인증-버튼을-누른다">1. 사용자가 핸드폰 번호를 입력한 후 &#39;인증 버튼&#39;을 누른다.</h3>
<h3 id="2-핸드폰-번호가-db에-있는지-확인하고-있으면-로그인-없으면-회원가입으로-넘어간다">2. 핸드폰 번호가 DB에 있는지 확인하고 있으면 로그인, 없으면 회원가입으로 넘어간다.</h3>
<h3 id="3-모바일에서-sms로-바로-이동한-후-서버에서-보내준-랜덤한-verificationcode를-body에-담아-우리-측-이메일-주소로-바로-보낼-수-있도록-설정한다">3. 모바일에서 SMS로 바로 이동한 후 서버에서 보내준 랜덤한 verificationCode를 body에 담아 우리 측 이메일 주소로 바로 보낼 수 있도록 설정한다.</h3>
<h3 id="4-사용자가-verificationcode를-담은-sms를-보내면-서버에서-맞는지-확인하여-회원가입-또는-로그인을-승인시킨다">4. 사용자가 verificationCode를 담은 sms를 보내면 서버에서 맞는지 확인하여 회원가입 또는 로그인을 승인시킨다.</h3>
<hr>
<h1 id="1-사용자가-핸드폰-번호를-입력한-후-인증-버튼을-누른다-1">1. 사용자가 핸드폰 번호를 입력한 후 &#39;인증 버튼&#39;을 누른다.</h1>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/f60c14b5-65e5-49e4-a1a8-122c486a39ce/image.png" alt="">
위처럼 본인의 핸드폰 번호를 입력하고 버튼을 누르게 되면 임의의 verificationCode를 생성하는 API와 회원가입 유/무를 확인하는 API를 호출한다. 그 후 바로 sms로 이동한다.</p>
<pre><code class="language-Typescript">const onSubmit = async (data: z.infer&lt;typeof FormSchema&gt;) =&gt; {
    try {
      const authenticationCode = await getAuthenticationCode(data.pin);

      if (authenticationCode.data.code) {
        setUserData(&#39;phoneNumber&#39;, data.pin);
        setUserData(&#39;verificationCode&#39;, authenticationCode.data.code);
        ...</code></pre>
<h1 id="2-핸드폰-번호가-db에-있는지-확인하고-있으면-로그인-없으면-회원가입으로-넘어간다-1">2. 핸드폰 번호가 DB에 있는지 확인하고 있으면 로그인, 없으면 회원가입으로 넘어간다.</h1>
<p>onSubmit 함수에 회원가입 유/무를 판별하는 API를 함께 호출한다.</p>
<pre><code class="language-Typescript">if (authenticationCode.data.code) {
        setUserData(&#39;phoneNumber&#39;, data.pin);
        setUserData(&#39;verificationCode&#39;, authenticationCode.data.code);

        const memberStatus = await getExistMember(data.pin);
        setUserExist(memberStatus.data.exists);</code></pre>
<p>phoneNumber와 verificationCode는 추후에 로그인 및 회원가입시 필요하기에 데이터를 전역으로 관리한다.</p>
<h1 id="3-모바일에서-sms로-바로-이동한-후-서버에서-보내준-랜덤한-verificationcode를-body에-담아-우리-측-이메일-주소로-바로-보낼-수-있도록-설정한다-1">3. 모바일에서 SMS로 바로 이동한 후 서버에서 보내준 랜덤한 verificationCode를 body에 담아 우리 측 이메일 주소로 바로 보낼 수 있도록 설정한다.</h1>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/af5189a4-1f4a-4103-86fd-a71b997b2d4e/image.png" alt="">
버튼을 누르면 SMS로 이동하여 자동으로 메시지 내용에 서버에서 발급한 vericifationCode를 넣어놓고 email도 서버에서 관리하는 email로 자동으로 설정한다. 유저는 메시지를 보내기만 하면 되므로 불편한 점이 사라지고 우리도 메시지를 보낼 필요가 없어지기 떄문에 win-win작전이라고 볼 수 있다 ^^</p>
<pre><code class="language-Typescript">// 핸드폰 기종에 따라 분기
        const userAgent = navigator.userAgent.toLowerCase();
        let bodyPrefix = &#39;?body=&#39;;

        if (
          userAgent.indexOf(&#39;iphone&#39;) &gt; -1 ||
          userAgent.indexOf(&#39;ipad&#39;) &gt; -1 ||
          userAgent.indexOf(&#39;ipod&#39;) &gt; -1
        ) {
          bodyPrefix = &#39;&amp;body=&#39;;
        }

        const smsUrl = `sms:${import.meta.env.VITE_DUETT_EMAIL}${bodyPrefix}${encodeURIComponent(authenticationCode?.data?.code)}`;
        window.location.href = smsUrl;</code></pre>
<p>이때 안드로이드와 IOS는 각각 sms로 연결하는 방식이 다르다. 안드는 ?body로 IOS는 &amp;body로 붙여줘야 sms로 바로 넘어가는 현상을 확인할 수 있다. sms로 이동할때 sms: 뒤에 이메일을 붙여주면 바로 그 메일로 보낼 수 있는 환경을 제공하고 bodyPrefix 뒤에 내용물은 바로 sms body에 넣어준다. 이와 같은 세팅으로 우리는 버튼 하나로 코드를 보낼 수 있게 되었다! 이제 백엔드에서 이 코드와 phoneNumber의 유저의 코드가 일치하는지 확인한 후 요청을 승인해준다.</p>
<pre><code class="language-Typescript">const onSubmit = async (data: z.infer&lt;typeof FormSchema&gt;) =&gt; {
    try {
      const authenticationCode = await getAuthenticationCode(data.pin);

      if (authenticationCode.data.code) {
        setUserData(&#39;phoneNumber&#39;, data.pin);
        setUserData(&#39;verificationCode&#39;, authenticationCode.data.code);

        const memberStatus = await getExistMember(data.pin);
        setUserExist(memberStatus.data.exists);

        // 핸드폰 기종에 따라 분기
        const userAgent = navigator.userAgent.toLowerCase();
        let bodyPrefix = &#39;?body=&#39;;

        if (
          userAgent.indexOf(&#39;iphone&#39;) &gt; -1 ||
          userAgent.indexOf(&#39;ipad&#39;) &gt; -1 ||
          userAgent.indexOf(&#39;ipod&#39;) &gt; -1
        ) {
          bodyPrefix = &#39;&amp;body=&#39;;
        }

        const smsUrl = `sms:${import.meta.env.VITE_DUETT_EMAIL}${bodyPrefix}${encodeURIComponent(authenticationCode?.data?.code)}`;
        window.location.href = smsUrl;

        toast({
          title: &#39;인증 메시지가 전송되었습니다.&#39;,
        });

        setIsSubmitted(true);
      } else {
        throw new Error(&#39;reponse가 없나?&#39;);
      }
    } catch (error) {
      console.error(error);
      toast({
        title: `인증 에러: ${error}`,
        description: &#39;인증 메시지 전송에 실패했습니다. 다시 시도해주세요.&#39;,
      });
    }
  };</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 재사용성 & 유지보수성의 중요성]]></title>
            <link>https://velog.io/@pangs_boy/TIL-%EC%9E%AC%EC%82%AC%EC%9A%A9%EC%84%B1-%EC%9C%A0%EC%A7%80%EB%B3%B4%EC%88%98%EC%84%B1%EC%9D%98-%EC%A4%91%EC%9A%94%EC%84%B1</link>
            <guid>https://velog.io/@pangs_boy/TIL-%EC%9E%AC%EC%82%AC%EC%9A%A9%EC%84%B1-%EC%9C%A0%EC%A7%80%EB%B3%B4%EC%88%98%EC%84%B1%EC%9D%98-%EC%A4%91%EC%9A%94%EC%84%B1</guid>
            <pubDate>Mon, 08 Apr 2024 05:13:40 GMT</pubDate>
            <description><![CDATA[<h1 id="프론트엔드에서의-재사용성과-유지보수성이란">프론트엔드에서의 재사용성과 유지보수성이란?</h1>
<h2 id="재사용성reusability">재사용성(Reusability)</h2>
<ul>
<li>한번 작성된 <span style="color:red">공통 컴포넌트나 스타일, 함수 등</span>이 <span style="color:red">여러 페이지나 프로젝트</span>에서 재사용 될 수 있는 능력을 의미.</li>
</ul>
<h2 id="유지보수성maintainability">유지보수성(Maintainability)</h2>
<ul>
<li><span style="color:red">서비스의 기능, 화면</span>에 <span style="color:red">새로운 요구사항을 반영</span>하거나 <span style="color:red">수정</span>을 쉽고 효율적으로 이루어지는 정도를 의미</li>
</ul>
<h1 id="왜-중요한가">왜 중요한가?</h1>
<h2 id="소프트웨어-제품의-라이프-사이클">소프트웨어 제품의 라이프 사이클</h2>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/6fad7e48-7fa6-4964-a930-4e2558bacdae/image.png" alt=""></p>
<ul>
<li>서비스가 지속적으로 성장하면서 자연스레 규모가 커진다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/642e7851-52d8-4fb2-b859-d3a67145e776/image.png" alt=""></p>
<ul>
<li>매번 새로운 기능을 출시 할 때마다 개발자의 수는 지속적으로 증가했지만, 코드 생산성은 한 곳으로 수렴한다.
<img src="https://velog.velcdn.com/images/pangs_boy/post/dad38e72-3bcd-4fb9-bfd8-87d7c601861f/image.png" alt=""></li>
<li>처음 만들 떄에는 생산성이 100%이지만 출시할 때마다 급격하게 하락하게 된다.</li>
</ul>
<h3 id="사람은-늘었는데-왜-생산성은-떨어질까">사람은 늘었는데 왜 생산성은 떨어질까?</h3>
<ul>
<li>빠르게 서비스를 만들기 위해 지저분한 코드를 작성했기 때문</li>
</ul>
<h1 id="프론트엔드에서-재사용성을-위해-할-수-있는-것들">프론트엔드에서 재사용성을 위해 할 수 있는 것들</h1>
<h2 id="1-컴포넌트화">1. 컴포넌트화</h2>
<pre><code class="language-jsx">import React from &#39;react&#39;;

const Button = ({ onClick, children }) =&gt; {
  return &lt;button onClick={onClick}&gt;{children}&lt;/button&gt;;
};

export default Button;</code></pre>
<h2 id="2-공통-라이브러리-및-유틸리티-사용">2. 공통 라이브러리 및 유틸리티 사용</h2>
<pre><code class="language-jsx">import axios from &#39;axios&#39;;

const fetchData = async () =&gt; {
  try {
    const response = await axios.get(&#39;/example&#39;);
    return response.data;
  } catch (error) {
    console.error(&#39;Error : &#39;, error);
  }
};</code></pre>
<h2 id="3-디자인-시스템-구축">3. 디자인 시스템 구축</h2>
<ul>
<li>타이포그래피 스타일, 색상 등을 미리 지정<h2 id="4-api-추상화-코드-디자인-등">4. API 추상화, 코드 디자인 등</h2>
</li>
</ul>
<h1 id="프론트엔드에서-유지보수성을-위해-할-수-있는-것들">프론트엔드에서 유지보수성을 위해 할 수 있는 것들</h1>
<h2 id="1-코드의-가독성과-일관성을-향상eslint">1. 코드의 가독성과 일관성을 향상(eslint)</h2>
<pre><code class="language-js">module.exports = {
    env: {
      browser: true,
      es2021: true,
      node: true,
    },
  ...</code></pre>
<h2 id="2-테스트-자동화">2. 테스트 자동화</h2>
<pre><code class="language-js">// EX&gt; Jest
test(&#39;adds 1 + 2 to equal 3&#39;, () =&gt; {
  expect(1 + 2).toBe(3);
});</code></pre>
<h2 id="3-문서화">3. 문서화</h2>
<ul>
<li>주석을 달아 어떻게 돌아가는 코드인지 작성<h2 id="4-모듈화와-코드-분리-응집도-등">4. 모듈화와 코드 분리, 응집도 등</h2>
<pre><code>src/
├── components/
│   ├── Header.js
│   ├── Sidebar.js
│   └── ...
├── utils/
│   ├── api.js
│   ├── helpers.js
│   └── ...
└── ...</code></pre><h2 id="5-리팩토링기술-부채">5. 리팩토링(기술 부채)</h2>
<pre><code class="language-jsx">// 리팩토링 이전
const total = items.reduce((acc, item) =&gt; {
if (item.status === &#39;completed&#39;) {
  acc += item.price * item.quantity;
}
return acc;
}, 0);
</code></pre>
</li>
</ul>
<p>// 리팩토링 이후
const completedItems = items.filter(item =&gt; item.status === &#39;completed&#39;);
const total = completedItems.reduce((acc, item) =&gt; acc + item.price * item.quantity, 0);</p>
<p>```</p>
<p>[출처] 책 - &#39;클린 아키텍쳐&#39;</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스] - 3.20]]></title>
            <link>https://velog.io/@pangs_boy/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-3.20</link>
            <guid>https://velog.io/@pangs_boy/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-3.20</guid>
            <pubDate>Mon, 08 Apr 2024 05:03:08 GMT</pubDate>
            <description><![CDATA[<h1 id="11-소프트웨어-요구사항-명세서">1.1 소프트웨어 요구사항 명세서</h1>
<h2 id="소프트웨어의-요구사항-명세">소프트웨어의 요구사항 명세</h2>
<ul>
<li>프로젝트에 있어서 가장 중요한 것</li>
<li>개발자의 임무 : 요구사항을 만족하는 소프트웨어를 개발하는 것<h3 id="주의할-점">주의할 점</h3>
</li>
</ul>
<ol>
<li>요구사항은 여러 가지 이유로 프로젝트 수행 도중 변경될 수 있음<ul>
<li>요구사항 변경에 따라 세부 구현 사항, 때로는 전체 구조에 변경이 생기는 경우도 있음</li>
</ul>
</li>
<li>테스트 케이스의 작성은 요구사항을 반영해야 하며, 명세된 요구사항을 빠짐 없이 반영해야 함<ul>
<li>TDD : 요구사항 -&gt; 테스트 케이스 -&gt; 코드</li>
</ul>
</li>
<li>고객 요구에 따라 소프트웨어 요구사항을 도출하는 것이 프로젝트의 시작<ul>
<li>우리의 경우에는 이미 문서로 주어진 요구사항 명세를 분석하는 것으로 시작</li>
</ul>
</li>
</ol>
<h2 id="요구사항-명세서">요구사항 명세서</h2>
<h3 id="software-requirement-specificationsrs">Software Req;uirement Specification(SRS)</h3>
<ul>
<li><p>소프트웨어 구현물의 기능적/비기능적 요구사항을 기술한 문서</p>
<ul>
<li>기능적 요구사항 : 소프트웨어가 갖춰야 하는 기능</li>
<li>비기능적 요구사항 : 성능, 자원 사용량 등에 존재하는 여러 측면의 제약</li>
</ul>
</li>
<li><p>워터폴 모델의 소프트웨어 개발 프로세스에서 필수 산출물로 정의하는 것이지만, 애자일 방법론을 적용하는 경우 민첩성을 높이기 위해 산출을 생략하는 경우도 꽤 있음</p>
</li>
<li><p>Requirement-driven development 방식을 택하는 경우도 있음</p>
<ul>
<li>특히 mission critical system 등에서는 formal verification 방법과 조합하여 요구사항 만족을 엄격하게 검증하는 것을 중요시하고 있음</li>
<li>웹 개발 맥락에서는 흔히 적용하는 방법은 아님</li>
</ul>
</li>
<li><p>기능적 요구사항에 대한 개괄적 설명</p>
</li>
<li><p>적용 기술에 대한 지정</p>
</li>
<li><p>웹 개발 맥락에서는 흔히 적용하는 방법은 아님</p>
<ul>
<li>일반적으로는 요구사항 명세서의 요소가 아니지만, 여기에서는 동일한 기술 적용을 훈련하기 위해서 지정하고 있음</li>
</ul>
</li>
<li><p>각 요소 기능에 대한 명세</p>
</li>
<li><p>데이터베이스 스키마의 약식 표현</p>
<ul>
<li>이것도 일반적으로 요구사항 명세의 일부는 아니지만, 여기에서는 데이터베이스를 별도로 제공하므로 이의 스키마를 요구사항 명세서에 기술하였음</li>
</ul>
</li>
<li><p>백엔드 API 세트의 정의</p>
<ul>
<li>기능에 대한 설명은 생략하고 (이후 구조설계서에서 약간 상세화), 제공하는 리소스와 메서드를 나열</li>
</ul>
</li>
</ul>
<h1 id="12-개발-계획-수립">1.2 개발 계획 수립</h1>
<ul>
<li><p>프로젝트의 성공</p>
<ul>
<li>원하는 (계획한) 결과물이 산출되어야 함</li>
<li>또한, 이 결과물은 정해진 기한 내에 산출되어야 함</li>
</ul>
</li>
<li><p>계획을 수립해야 하는 항목들</p>
<ul>
<li>역할 분담 (우리의 경우에는 해당하지 않으나)</li>
<li>기획 / 디자인 / 코드 개발 / 테스트 / 운영 자원을 적기에 적재적소에 투입할 수 있어야 함</li>
<li>코드 개발만 떼어 놓고 보면 BE 와 FE 로 나누어 개발하는 것이 자연스러워 보임</li>
<li>개발 방법, 팀 사이의 인터페이스 🡨 결과물에 대한 (가능한 범위 내에서) 구체적인 예상으로부터</li>
<li>프로젝트 일정 및 지연 발생에 대한 대응책</li>
</ul>
</li>
<li><p>계획해야 하는 것들</p>
<ul>
<li>활용할 요소기술, 최종 서비스 실행 환경 등</li>
<li>개발 프로세스에 적용할 모델(Waterfall, Agile)</li>
<li>코드 및 아티팩트의 유지관리 정책 및 도구</li>
<li>코딩 스타일 규약</li>
<li>코드 리뷰 계획, 통합 주기와 방법</li>
<li>개발 환경과 통합/테스트 도구, 릴리스 정책과 방법</li>
<li>서비스 운영 계획, 유지보수 정책과 범위</li>
</ul>
</li>
<li><p>요소기술</p>
<ul>
<li>Express, React , MariaDB</li>
<li>JWT, Tiptap</li>
</ul>
</li>
<li><p>개발/통합/테스트 도구</p>
<ul>
<li>GitHub, Docker/Kubernetes</li>
<li>Jenkins, Terraform, Selenium</li>
</ul>
</li>
<li><p>인프라 환경</p>
<ul>
<li>AWS EC2 + Minikube, Nginx</li>
<li>AWS S3, AWS ECR</li>
</ul>
</li>
<li><p>TDD를 적용해 볼 것인지?</p>
<ul>
<li>습관이 되어 있지 않다면 다소 부담스러운 접근이 될 수 있음.</li>
<li>요구사항과 개발 범위, 결과물에 대한 예측이 명확</li>
</ul>
</li>
<li><p>CI(지속적 통합) 적용</p>
<ul>
<li>어느 단계에서부터 도입, 적용할 것인지? 어떤 환경/도구를 이용할 것인지?</li>
</ul>
</li>
<li><p>CD(지속적 배포/인도) 적용</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] - 프로젝트 튜토리얼 만들기(1)]]></title>
            <link>https://velog.io/@pangs_boy/TIL-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EB%A7%8C%EB%93%A4%EA%B8%B01</link>
            <guid>https://velog.io/@pangs_boy/TIL-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EB%A7%8C%EB%93%A4%EA%B8%B01</guid>
            <pubDate>Thu, 14 Mar 2024 09:38:42 GMT</pubDate>
            <description><![CDATA[<p>MyList 베타서비스 배포 전 QA 및 피드백을 받아본 결과 공통적으로 나온 결론은 &quot;신규 유저가 사용하기 어렵다!&quot;였다. 분명 내가 만질 때는 쉬웠는데... 이러한 점이 배포된 서비스와 개인 포트폴리오와의 큰 차이점이 아닌가한다. 내 눈에 예쁘고, 편한데... 이게 어려우면 다른건 어떻게 쓰지..?라는게 내 주관적인 생각이었지만, 피드백을 받은 이상 고쳐야지머..</p>
<h2 id="1-이미지로-튜토리얼-진행하기">1. 이미지로 튜토리얼 진행하기</h2>
<p>처음엔 디자이너분께 이미지 또는 GIF형식의 파일을 달라했다. 단순히 이미지를 넣고 State값을 관리하면서 이미지만 넘기면서 보여주면 되지않을까하는 안일한 실수였다. 이미지도 당연히 width, height만 조절하면될줄 알았는데 모바일에서 URL Navbar때문에 잘리고, width height비율이 맞지않아 이상하게 보인다거나 문제가 많았다. 가장 큰 문제는 이미지를 넘기는 버튼을 화면의 빈칸인 맨 아래에 absoulte로 넣었다는 것인데, 이가 스마트폰 기종에 따라 Navbar가 하단에 위치하는 경우가 있어 가차없이 짤려버렸다. 그렇다고 flex-column으로 넣어버리면 배경이 붕뜨는 이상한 UI가 완성되었다. 구글링을 한 결과에서도 보통은 Native로 개발한 분들뿐이고 참고할만한 데이터가 많지 않았다. 그래서 결과적으로 선택한 방법은 화면 전체적으로 투명도를 낮추고 강조할 부분만 보여주며 말풍선을 달자이다.</p>
<h2 id="2-튜토리얼-상태를-만들어-튜토리얼-모드를-관리하자">2. 튜토리얼 상태를 만들어 튜토리얼 모드를 관리하자</h2>
<p>총 튜토리얼은 7개의 화면을 보여줘야했고, 같은 페이지가 아닌 여러 페이지에서 튜토리얼을 진행해야했다. 떄문에 튜토리얼 상태라는 것을 알려주기 위해 전역적으로 관리가 필요했다. 간단한 상태값 관리기 떄문에 Recoil을 사용하여 </p>
<pre><code class="language-ts">TutorialStep.ts

import { atom } from &quot;recoil&quot;;

export type TutorialStep =
  | &quot;header&quot;
  | &quot;playlist&quot;
  | &quot;env&quot;
  | &quot;list1&quot;
  | &quot;list2&quot;
  | &quot;add1&quot;
  | &quot;add2&quot;
  | null;

export const tutorialStepState = atom&lt;TutorialStep&gt;({
  key: &quot;tutorialStepState&quot;,
  default: null, 
});
</code></pre>
<p>총 7개의 튜토리얼 상태를 만들어줬다. 또한 이를 편하게 사용하기 위해 hooks에 함수로 만들어 줬다.</p>
<pre><code class="language-ts">export function useTutorial() {
  const [tutorialStep, setTutorialStep] =
    useRecoilState&lt;TutorialStep&gt;(tutorialStepState);

  const toggleTutorialMode = () =&gt; {
    const stepOrder: TutorialStep[] = [
      &quot;header&quot;,
      &quot;playlist&quot;,
      &quot;env&quot;,
      &quot;list1&quot;,
      &quot;list2&quot;,
      &quot;add1&quot;,
      &quot;add2&quot;,
      null,
    ];
    const currentStepIndex = stepOrder.indexOf(tutorialStep);
    const nextStep = stepOrder[(currentStepIndex + 1) % stepOrder.length];
    setTutorialStep(nextStep);
  };

  return { tutorialStep, toggleTutorialMode, setTutorialStep };
}</code></pre>
<p>이로써 튜토리얼을 진행할 1차 관문을 넘었따. 세팅이 끝났으니 본격적으로 컴포넌트에 적용을 시켜보자!!</p>
<pre><code class="language-tsx">...
const { tutorialStep, toggleTutorialMode, setTutorialStep } = useTutorial();

  // 튜토리얼 모드를 표시하는 조건부 렌더링
  const isTutorialMode = tutorialStep !== null;
  const handlePageClick = (e: React.MouseEvent) =&gt; {
    if (isTutorialMode) {
      if (tutorialStep === &quot;playlist&quot;) {
        e.stopPropagation(); // 이벤트 전파 중단

        return;
      }
      toggleTutorialMode();
    }
  };

...
return (
    &lt;div
      className={`h-full scrollbar-hide overflow-scroll relative ${
        isTutorialMode ? &quot;bg-black bg-opacity-50&quot; : &quot;&quot;
      }`}
      onClick={handlePageClick}
    &gt;
      {isTutorialMode &amp;&amp; (
        &lt;div className=&quot;fixed inset-0 bg-black bg-opacity-70 z-10&quot;&gt;&lt;/div&gt;
      )}
      &lt;Tutorial
        username={userData?.username}
        setTutorialMode={setTutorialStep}
        length={playlistData?.length}
      /&gt;
</code></pre>
<p>간단히 코드를 살펴보자. 우선 화면을 클릭하여 튜토리얼이 넘어가도록 최상단 div에 onClick 이벤트를 넣어주었다. 여기서 문제가 무엇이냐. 두번째 튜토리얼인 &quot;playlist&quot;이후에는 새로운 플레이리스트를 보내는 로직(post)이 동작하여 페이지를 이동한 후 튜토리얼을 진행해야했다. 즉, 그대로 넘어가버린다면 아직 데이터가 생성되지도않은 페이지로 이동할 수 없기에 튜토리얼이 진행이 안된다는 것이다 ㅠㅠㅠ... 그래서 playlist일때는 페이지에서 튜토리얼을 넘어가는 방식을 멈추고</p>
<pre><code class="language-tsx">const handleAddPlaylistClick = async (e: React.MouseEvent) =&gt; {
    e.stopPropagation();

    if (tutorialStep === &quot;playlist&quot;) {
      handleAddPlaylist(title, titleImage, token); 
    }
  };</code></pre>
<p>추가하는 버튼을 눌러야만 튜토리얼도 같이 진행되며 페이지가 이동되도록 만들었다.
<img src="https://velog.velcdn.com/images/pangs_boy/post/b183146e-9950-4e6d-9827-d7afe043fb87/image.png" alt="">
또한 위 모달에서 튜토리얼을 시작해야하므로 시작 지점을 &quot;보러가기&quot;버튼으로 옮겨주었다. 또한 플레이리스트가 0개일떄, token이 있을떄라는 조건을 달아 첫 로그인한 유저가 아니라면 튜토리얼 진행을 할 수 없도록 구현하였다. 이제 추가적인 디테일부분은 2탄에서 작성하도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] - 방명록을 만들어보자!]]></title>
            <link>https://velog.io/@pangs_boy/React-%EB%B0%A9%EB%AA%85%EB%A1%9D%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@pangs_boy/React-%EB%B0%A9%EB%AA%85%EB%A1%9D%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Thu, 07 Mar 2024 04:59:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/pangs_boy/post/da1ec2d2-d4a6-4ff6-8b76-4ce6b6d808d9/image.png" alt="">
플레이리스트의 방명록을 만드려면 이러한 UI의 방명록이 필요했다. 헤더를 고정할 필요는 없지만 사용자가 스크롤을 내리지않고 항상 댓글을 입력해야하므로, Footer는 항상 고정해야했다. </p>
<hr>
<h2 id="1-댓글-영역과-작성-영역-고정하기feat-무한스크롤">1. 댓글 영역과 작성 영역 고정하기(Feat. 무한스크롤)</h2>
<p>댓글이 보여지는 부분은 무한스크롤을 구현하여 처리하고 Footer는 항상 고정해야 한다. 처음에는 헤더와 푸터의 높이를 각각 10%씩 지정하여 Main의 height를 80%로 하거나, Px단위로 바꾸어 적용도 해보았지만 height가 고정되는 순간 안의 데이터들이 짤려버리는 문제가 발생했다. 또한 댓글이 길어지면 Div의 height도 계속 바뀌기 떄문에 고정 height를 쓸 수 없었다. 끝없는 구글링결과 나는 두가지 라이브러리를 사용하기로 했다 ㅎ.. 바로 react-intersection-observer와 InfinityScroll이다!</p>
<pre><code>npm i react-intersection-observer
npm i react-infinite-scroller</code></pre><p>요렇게 두가지 라이브러리를 깔아주자.</p>
<pre><code>&lt;InfiniteScroll
          className=&quot;h-[85%] smartPhone:h-[70%] overflow-y-scroll scrollbar-hide&quot;
          pageStart={0}
          loadMore={loadMore}
        &gt;
          &lt;main&gt;
            &lt;div className=&quot;flex flex-col items-center justify-center h-full&quot;&gt;
              {visitorData &amp;&amp;
                visitorData.map((visitor: any) =&gt; (</code></pre><p>헤더와 푸터는 각각 5%, 10%의 height를 가지고 있어 InfiniteScroll을 85%로 선언해주었다. 이때 InfiniteScroll은 pageStart와 loadMore는 필수적으로 작성해야하는 props로 pageStart는 0번 page부터 데이터를 보여주므로 0으로 설정해주고 loadMore는 스크롤을 내릴떄 동작하는 함수인데 나는 굳이 필요없으므로 laodMore라는 빈 함수를 넣어주었다.
<img src="https://velog.velcdn.com/images/pangs_boy/post/13e26b41-a82b-4233-9b67-51305a35ef97/image.png" alt="">
그럼 요롷게 범위안에서 스크롤을 내리지 않는 무한스크롤이 되었다! 근데 이러면 끝난거아닌가?라는 생각이 들 수 있지만 그렇지 않다. 왜냐하면 15개씩 페이징이 되어있어서 버튼을 누르면 추가적인 페이지가 렌더링되도록 해야하기 떄문이다 ㅠㅠㅠㅠㅠ..</p>
<hr>
<h2 id="2-버튼으로-추가-페이지-렌더링">2. 버튼으로 추가 페이지 렌더링</h2>
<p><img src="blob:https://velog.io/f30df91b-659e-4a4a-b0b4-28573d9dd2eb" alt="업로드중..">
요렇게 댓글이 15개가 넘어가면 버튼으로 추가 렌더링을 하도록 해주자. 지금이야 댓글이 얼마 없지만 예를들어 300개라면 한번에 300개 이상의 데이터가 렌더링되어 성능이 저하될것이다. 또한 사용자들도 굳이 300개를 전부 보고싶어하지도 않을것이다! 그래서 백엔드에서 15개씩 페이징을 해주었다 ㅎㅎ</p>
<pre><code class="language-typescript">import { useInView } from &quot;react-intersection-observer&quot;;
const Visitor = () =&gt; {
  const [view] = useInView(); // view에 도달했을떄 새로운 페이지 렌더링

  const [isLast, setLast] = useState&lt;boolean&gt;(false); //
  const [page, setPage] = useState&lt;number&gt;(0);
  const [visitorData, setVisitorData] = useState&lt;VisitorData[]&gt;([]);


  const fetchVisitorData = async (pageNum: number) =&gt; {
    try {
      const visitor = await getVisitor(Number(playlistId), pageNum);
      setVisitorData((prevUsers) =&gt; [...prevUsers, ...visitor.data]);

      if (visitor.data.length &lt; 15) { // 15개 미만일떄
        setLast(true); // true로 바꿔주자.
      } else {
        setLast(false);
      }
    } catch (error) {
      console.error(error);
    }
  };

  const handleLoadMore = () =&gt; {
    const newPage = page + 1;
    setPage(newPage); // 페이지 번호를 증가시키고
    fetchVisitorData(newPage); // 새 페이지 번호를 fetchVisitorData에 전달
  };
  return(
    {!isLast &amp;&amp; (
            &lt;button
              onClick={handleLoadMore}
              className=&quot;flex justify-center items-center h-10 w-full&quot;
            &gt;
              &lt;FaCirclePlus size={28} color=&quot;&quot; /&gt;
            &lt;/button&gt;
          )}</code></pre>
<p>isLast가 false일때 버튼을 누르면 handleLoadMore 함수가 실행된다. 페이지를 증가시키고 새 페이지에대한 api를 요청하는 구조이다. 이로써 처음엔 0페이지에서 추후에 페이지를 증가시키며 데이터를 렌더링하는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] SweetAlert2를 사용해보자.]]></title>
            <link>https://velog.io/@pangs_boy/React-SweetAlert2%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@pangs_boy/React-SweetAlert2%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Thu, 07 Mar 2024 04:09:55 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 진행하며 alert창을 써야할 일이 많아졌다. 토큰이 없어 권한이 없다거나 삭제를 해야할때 경고창 등 사용해야할 경우가 늘어남에 따라 JS에서 지원하는 기본 alert창을 쓰기가 싫어졌다.<img src="https://velog.velcdn.com/images/pangs_boy/post/8bcf4395-f743-4a5c-ba48-bc6648249e0d/image.png" alt="">
<del>너무나 투박하다. ㅠㅠ</del></p>
<hr>
<p>여기서 우리는 <a href="https://sweetalert2.github.io/">Sweetalert</a>를 사용해보자. 우선 설치를 먼저하도록 하자.</p>
<pre><code>npm install sweetalert2</code></pre><p>위 명령어로 간단하게 설치할 수 있다. 그 후 </p>
<pre><code>import Swal from &quot;sweetalert2&quot;;</code></pre><p>원하는 페이지에 위와같이 라이브러리를 불러와 사용하면 된다. 공식페이지에서 확인해본 결과 타이머, 아이콘, 취소 버튼등 다양한 기능을 지원하고 API까지 연결할 수 있다!
<img src="https://velog.velcdn.com/images/pangs_boy/post/02f5299e-1bac-4ead-bdf7-511fe25bc09c/image.png" alt="">
&#39;A dialog with three buttons&#39;와 같이 코드를 작성하면 유저가 원하는 글구의 alert를 작성할 수 있고, 또한 &#39;result.isConfirmed&#39;와 같이 원하는 기능도 추가가 가능하다. </p>
<hr>
<h3 id="불편한-점-발생">불편한 점 발생</h3>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/957961c6-9f31-4495-b6fa-dc8feb836a09/image.png" alt="">
내가 구현해햐 하는 ALert는 사진과 같은데, 단순히 Swal에서 CSS를 설정해주기에는 한계가 많다. 바뀌지 않는 부분도 많고 세세한 설정이 먹히지 않기 떄문이었다 ㅠㅠ</p>
<hr>
<h3 id="해결방법">해결방법</h3>
<p>그래서 나는 그냥 CSS 파일을 만들어 불러오기로 했다.</p>
<pre><code class="language-css">.popup {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 150px;
  width: 252px;
  font-size: 10px;
  color: #c2c2c2;
  font-weight: bold;
  border-radius: 20px;
}

.confirmButton {
  color: black;
  background-color: white;
  border-width: 2px;
  padding: 8px 30px;
  border-color: black;
  border-radius: 50px;
  margin-right: 20px;
  font-weight: bold;
  font-size: 13px;
}
.cancelButton {
  padding: 8px 30px;
  border-radius: 50px;
  color: white;
  background-color: red;
  border-width: 2px;
  border-color: red;
  font-weight: bold;
  font-size: 13px;
}
.title {
  color: black;
  font-size: 15px;
  font-weight: bold;
}
.htmlContainer {
  font-size: 20px;
}
</code></pre>
<pre><code class="language-tsx">const swalButton = Swal.mixin({
    customClass: {
      popup: &quot;popup&quot;, // 전체
      confirmButton: &quot;confirmButton&quot;, // 취소
      cancelButton: &quot;cancelButton&quot;, // 삭제
      title: &quot;title&quot;, // 타이틀
      htmlContainer: &quot;htmlContainer&quot;, // 내용
    },
    buttonsStyling: false,
  });</code></pre>
<p>미리 swalButton이라는 함수에 custom할 Swal Alert를 선언해주고 위 css를 적용한다!</p>
<pre><code class="language-tsx">const handleSubmit = useCallback(
    async (event: React.FormEvent) =&gt; {
      event.preventDefault();
      if (!token) {
        swalButton
          .fire({
            title: &quot;로그인 필요한 서비스입니다.&quot;,
            text: &quot;로그인이 하시겠습니까?&quot;,
            showCancelButton: true,
            confirmButtonColor: &quot;blue&quot;,
            cancelButtonColor: &quot;#d33&quot;,
            confirmButtonText: &quot;취소&quot;,
            cancelButtonText: &quot;로그인&quot;,
          })
          .then((result) =&gt; {
            if (result.dismiss === Swal.DismissReason.cancel) {
              localStorage.setItem(&quot;prevUrl&quot;, window.location.href);
              navigate(&quot;/login&quot;);
            }
          });
        return;
      }
      try {
        ...</code></pre>
<p>그 다음 Alert가 필요한 함수에 넣어주면 끝이다! 다른 블로그들을 뒤져봐도 자세히 커스터마이징 하는 방법을 찾기가 어려웠는데, 이렇게 해주니 간단하게 해결할 수 있었다~ </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GraphQL이란?]]></title>
            <link>https://velog.io/@pangs_boy/GraphQL%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@pangs_boy/GraphQL%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Tue, 13 Feb 2024 08:19:14 GMT</pubDate>
            <description><![CDATA[<h1 id="graphql이란">GraphQL이란?</h1>
<p>GraphQL은 FaceBook에서 개발된 오픈소스 기술로 데이터 질의(Query + Schema)언어이다.
클라이언트는 GraphQL 서버로 쿼리를 전송하고, 서버는 해당 쿼리를 해석하고 데이터를 반환한다.
이 때, 클라이언트가 요청한 필드만 반환되므로 over fetching을 줄여 효율적이다. 
또한, GraphQL은 스키마를 사용하여 데이터 모델을 정의하기 때문에 클라이언트와 서버 간의 일관성 있는 데이터 통신을 보장한다.이를 통해 클라이언트가 서버가 제공하는 데이터 중 원하는 데이터를 가져오는 것이 가능해진다.</p>
<h1 id="graphql의-장단점">GraphQL의 장단점</h1>
<h2 id="장점">장점</h2>
<h3 id="1-오버페칭과-언더페칭을-해결할-수-있다">1. 오버페칭과 언더페칭을 해결할 수 있다.</h3>
<h3 id="2-엔드포인트와-요청-형식을-고민하지-않아도-된다">2. 엔드포인트와 요청 형식을 고민하지 않아도 된다.</h3>
<h3 id="3-클라이언트-로직이-간결해진다">3. 클라이언트 로직이 간결해진다.</h3>
<h2 id="단점">단점</h2>
<h3 id="1-http-캐싱을-활용하기-어렵다">1. HTTP 캐싱을 활용하기 어렵다.</h3>
<h3 id="2-에러-핸들링이-어렵다">2. 에러 핸들링이 어렵다.</h3>
<h1 id="예시">예시</h1>
<pre><code>// REST API request
GET, https://swapi.dev/api/people/1

// REST API response
{
    &quot;name&quot;: &quot;Luke Skywalker&quot;,
    &quot;height&quot;: &quot;172&quot;,
    &quot;mass&quot;: &quot;77&quot;,
    &quot;hair_color&quot;: &quot;blond&quot;,
    &quot;skin_color&quot;: &quot;fair&quot;,
    &quot;eye_color&quot;: &quot;blue&quot;,
    &quot;birth_year&quot;: &quot;19BBY&quot;,
    &quot;gender&quot;: &quot;male&quot;,
    &quot;homeworld&quot;: &quot;http://swapi.dev/api/planets/1/&quot;,
    &quot;films&quot;: [&quot;http://swapi.dev/api/films/1/&quot;, &quot;http://swapi.dev/api/films/2/&quot;, &quot;http://swapi.dev/api/films/3/&quot;, &quot;http://swapi.dev/api/films/6/&quot;],
    &quot;species&quot;: [],
    &quot;vehicles&quot;: [&quot;http://swapi.dev/api/vehicles/14/&quot;, &quot;http://swapi.dev/api/vehicles/30/&quot;],
    &quot;starships&quot;: [&quot;http://swapi.dev/api/starships/12/&quot;, &quot;http://swapi.dev/api/starships/22/&quot;],
    &quot;created&quot;: &quot;2014-12-09T13:50:51.644000Z&quot;,
    &quot;edited&quot;: &quot;2014-12-20T21:17:56.891000Z&quot;,
    &quot;url&quot;: &quot;http://swapi.dev/api/people/1/&quot;
}</code></pre><pre><code>// GraphQL request
query {
    person(personID: 1) {
        name
        height
        mass
    }
}

// GraphQL response
{
    &quot;data&quot;: {
        &quot;person&quot;: {
            &quot;name&quot;: &quot;Luke Skywalker&quot;,
            &quot;height&quot;: 172,
            &quot;mass&quot;: 77
        }
    }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Youtube] Youtube API 사용법]]></title>
            <link>https://velog.io/@pangs_boy/Youtube-Youtube-API-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@pangs_boy/Youtube-Youtube-API-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Mon, 05 Feb 2024 08:31:43 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 진행하면서 Youtube영상을 Embed로 띄워줘야하는 상황이 있어 YoutubeAPI를 사용하여 영상의 썸네일, 타이틀, ID값들을 가져와야했다.</p>
<h1 id="1-google-cloud-platform-프로젝트-등록">1. Google Cloud Platform 프로젝트 등록</h1>
<h3 id="1-1-처음엔-google-cloud-platform에-등록을-해줘야-한다">1-1. 처음엔 <a href="https://console.cloud.google.com/projectselector2/apis/dashboard">Google Cloud Platform</a>에 등록을 해줘야 한다.</h3>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/6dca3d98-a4db-45d0-b613-65afa51f17ce/image.png" alt="">
우선 <code>프로젝트 선택</code>에서 새로운 프로젝트를 생성해준다. </p>
<h3 id="1-2-그후-검색란에서-youtube를-검색해주면">1-2. 그후 검색란에서 Youtube를 검색해주면</h3>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/3df91850-1734-47b6-82a9-7458a2764816/image.png" alt="">
원하는 API들을 가져다 쓸 수 있다!
나는 썸네일과 영상ID값이 필요하기 때문에 YouTube Data API v3를 사용할 것이다.</p>
<h3 id="1-3">1-3.</h3>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/af631bfa-1d6e-4726-92ca-33020a5beb1b/image.png" alt="">
사용자 인증 정보에 들어가 <code>사용자 인증 정보 만들기</code>를 눌러준다.
<img src="https://velog.velcdn.com/images/pangs_boy/post/ee977676-e76b-4098-b868-473d28ce048b/image.png" alt="">
그 후 API 키를 생성해주고 이를 잘 보관해준다.</p>
<h1 id="2-테스트-해보기">2. <a href="https://developers.google.com/youtube/v3/docs/videos/list?hl=ko">테스트</a> 해보기</h1>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/9de4e3a2-ae19-4db2-9fbd-56bbbd6d1554/image.png" alt=""></p>
<p>제일 대표적인 매개변수를 확인해보자.
우선 내가 필요한 것은 영상의 대한 데이터이므로, part에서 snippet을 선택해준다. 또한 maxResults에 나는 너무 많은 데이터는 필요가 없으므로 10개로 제한한다. 그리고 q뒤에 검색할 쿼리값을 담는다.</p>
<blockquote>
<p><a href="https://www.googleapis.com/youtube/v3/search?part=snippet&amp;maxResults=10&amp;q=%EA%B2%80%EC%83%89%EC%BF%BC%EB%A6%AC&amp;type=video&amp;key=API%ED%82%A4%EA%B0%92">https://www.googleapis.com/youtube/v3/search?part=snippet&amp;maxResults=10&amp;q=검색쿼리&amp;type=video&amp;key=API키값</a></p>
</blockquote>
<p>그러면 위와같은 형식으로 검색을 하게되는것이다. 나는 여기서 maxResults와 검색쿼리는 내가 원할때 바꿔주고 싶으므로</p>
<pre><code class="language-typescript">import axios from &quot;axios&quot;;

export const youtubeAPIData = async (max: number, search: string) =&gt; {
  try {
    const response = await axios.get(
      `https://www.googleapis.com/youtube/v3/search?part=snippet&amp;maxResults=${max}&amp;q=${search}&amp;type=video&amp;key=${process.env.REACT_APP_YOUTUBE_API_KEY}`
    );
    return response.data;
  } catch (error) {
    console.log(error);
    throw error;
  }
};</code></pre>
<p>와 같이 API를 사용할 수 있도록 선언해주었다. 그전에 URL에서도 잘 데이터가 넘어오나 시험을 해보았다.</p>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/bc2b0f3e-55cd-4695-8add-6ace812a6eb3/image.png" alt="">
위와 같이 검색 데이터가 잘넘어오는 것을 확인할 수 있었다! 정렬방식은 최신, 인기순 등 여러가지 방안이 있는 듯하다. 기호에 맞게 설정해서 쓰면될거같음.</p>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/c316efa1-88e4-4e40-9da1-437539dfb88b/image.png" alt="">
최종적으로 완성할 디자인은 이랬다. 그러나 프로젝트를 진행하던 중 문제가 발생했다...
Youtube API는 하루에 사용할 수 있는 할당량이 존재한다는 것이었다..!
얘네는 하루에 10000 코스트의 할당량을 주는데 이가 정말 골 떄린다. <a href="https://developers.google.com/youtube/documentation?hl=ko">YOUTUBE API 공식문서</a>
<img src="https://velog.velcdn.com/images/pangs_boy/post/081641de-8335-47b1-9313-a6743940f5ed/image.png" alt="">
위 할당표를 보면 영상 하나에 1코스트가 아닌 내가 할 행위에 따라 다 다른 것이었다. 나는 검색(search)를 사용해야하는데 단 한번 검색에 100 + 보기 + 알파를 하면 터무니 없는 코스트였다. 물론 타 블로그에서 최대한의 효율을 뽑아먹는 방법들을 잘 정리해주시긴 했지만, 우리 프로젝트는 하루에 몇명이 들어올지도 모르는 상황이기때문에 사용이 불가능했다. 또, 수익화 모델이 존재하기 때문에 크롤링도 사용하지 못하고 위 유튜브 데이터 넣기는 꿈으로 남았다.. 하하 ㅠ</p>
<hr>
<p>그래서 대안으로 내세운 것이 있었으니 다음 편에 설명하도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스] 1월 회고록]]></title>
            <link>https://velog.io/@pangs_boy/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-1%EC%9B%94-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@pangs_boy/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-1%EC%9B%94-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Thu, 01 Feb 2024 04:06:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/pangs_boy/post/fc743ebc-61d3-44a0-8de7-e86472e280a9/image.png" alt=""></p>
<p>1월은 정말 정신 없이 흘러갔다. 백엔드 개발과 DB 및 ERD생성은 처음 해보았기 때문에 강의를 따라가는데 많은 어려움이 있었다. 또한 개인적인 객기로 모두 typescript로 마이그레이션한 결과 더더욱 강의를 따라가기가 벅찼다. 그러나 JS문법을 기존에 알고있었고, 새로운 것을 배우는 즐거움으로 어느정도 커버가 되었다. 협업하여 제대로 동작하는지는 알 수 없으나, 백엔드의 기초에 대해 배웠다는 점과 NodeJS를 배웠다는 것이 이번 스프린트의 큰 장점이었던 것 같다. 이제 스프린트3인 프론트엔드 파트로 넘어가면서, 이전보다는 아는 내용이 많이 섞여 있어 맘이 좀 편한 것 같다. 남은 약 3개월도 정진하여 데브코스가 끝났을때는 적어도 이전보다 성장한 모습이 되길 바라고 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[인프콘 2023] 커뮤니케이션 잘하는 개발자의 4가지 습관]]></title>
            <link>https://velog.io/@pangs_boy/%EC%9D%B8%ED%94%84%EC%BD%98-2023-%EC%BB%A4%EB%AE%A4%EB%8B%88%EC%BC%80%EC%9D%B4%EC%85%98-%EC%9E%98%ED%95%98%EB%8A%94-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-4%EA%B0%80%EC%A7%80-%EC%8A%B5%EA%B4%80</link>
            <guid>https://velog.io/@pangs_boy/%EC%9D%B8%ED%94%84%EC%BD%98-2023-%EC%BB%A4%EB%AE%A4%EB%8B%88%EC%BC%80%EC%9D%B4%EC%85%98-%EC%9E%98%ED%95%98%EB%8A%94-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-4%EA%B0%80%EC%A7%80-%EC%8A%B5%EA%B4%80</guid>
            <pubDate>Tue, 30 Jan 2024 02:47:47 GMT</pubDate>
            <description><![CDATA[<p>프론트엔드 개발자로서 프로젝트 개발을 하면서 <code>커뮤니케이션을 잘한다는 것이 무엇일까?</code> ,<code>타 직군과 어떻게 소통해야 상호이해가 잘 될수 있을까?</code> 등에 관한 문제를 항상 고민하고 궁금했었다. 우연히 인프콘에서 이를 명확하게 설명해주는 <code>송범근 개발자님</code>의 컨퍼런스를 찾게되었고 이를 요약하여 공유해보려 한다.</p>
<hr>
<h1 id="다른-직군과의-요구사항-커뮤니케이션">&#39;다른 직군과의 요구사항 커뮤니케이션&#39;</h1>
<h2 id="커뮤니케이션-잘-하는-개발자가-뭐지">&quot;커뮤니케이션 잘 하는 개발자가 뭐지?&quot;</h2>
<blockquote>
<p>Q. 어떤 사람이 커뮤니케이션을 잘하는 개발자인가요?
A. 커뮤니케이션을 잘한다는 유형은 사람마다 너무나 다르다. 그러나 공통적으로 우선 커뮤니케이션이 안되는 개발자는 =&gt; <code>그냥 안된다</code>고 말한다. 보통 개발자를 다음의 두 가지 유형으로 구분한다.</p>
</blockquote>
<h2 id="1-스펙-구현형-개발자">1. 스펙 구현형 개발자</h2>
<blockquote>
<p>위의 <code>그냥 안된다</code>라고 말하는 개발자들을 <code>스펙 구현형 개발자</code>라 이야기 한다.</p>
</blockquote>
<h3 id="특징">&lt;특징&gt;</h3>
<ul>
<li>스펙에 대한 용어를 본인의 기준(개발/전문용어)에 맞춰 설명한다. 때문에 이를 모르는 사람은 이해하기 어렵다.<blockquote>
<p>예를 들어, 디자이너나 마케터 등 다른 직군에게 개발용어를 설명없이 부탁, 설명한다면 이들은 당연히 알아들을수없다!</p>
</blockquote>
</li>
<li><code>스펙 구현형 개발자</code>의 생각은 개발자 == <code>스펙을 주면 잘 구현하는 사람</code></li>
<li>구현에 집중하면 <code>일의 시야</code>가 좁아진다.</li>
<li>커뮤니케이션 못하는 개발자가 &#39;안 된다&#39;고 하는 이유는 <code>스펙을 구현하는데 집중</code>하기 때문이다.</li>
</ul>
<h2 id="2-문제-해결형-개발자">2. 문제 해결형 개발자</h2>
<blockquote>
<p>이들은 <code>안 된다</code>라는 말을 그냥 하지 않는다 (↔️ 스펙 구현형 개발자)
그렇다고 &quot;에스맨&quot;이라는 것이 아니다!</p>
</blockquote>
<ul>
<li>어떤 문제인지 재차 물어보고, 어려운 이유가 무엇인지 설명하며 솔루션을 제공한다.</li>
<li>핵심은 <code>안 되는 그것</code>이 아니다.</li>
<li><code>문제 해결형 개발자</code>는 의도와 맥락을 이해해서, 더 좋은 스펙을 만들어내려고 하는 개발자이다.</li>
</ul>
<h3 id="커뮤니케이션-잘하는-개발자가-안된다라고-말하지-않는-이유는-문제-해결에-집중하기-때문이다"><strong>커뮤니케이션 잘하는 개발자가 &quot;안된다&quot;라고 말하지 않는 이유는 <code>문제 해결에 집중</code>하기 때문이다.</strong></h3>
<hr>
<h2 id="변화를-만들려면-습관이-필요하다">변화를 만들려면 습관이 필요하다.</h2>
<blockquote>
<p>누구나 <code>스펙 구현형 개발자</code>에서 시작한다. 그러나 최종적으로 문제 해결형 개발자가 되려면 어떻게 해야할까? <code>아는 것</code>만으로는 충분하지 않다. 실제로 행동하는 <code>습관</code>이 필요하다.</p>
</blockquote>
<h2 id="커뮤니케이션-잘하는-개발자의-4가지-습관">커뮤니케이션 잘하는 개발자의 4가지 습관</h2>
<h3 id="1-해결하려는-문제와-의도에-대해-묻는다">1. 해결하려는 문제와 의도에 대해 묻는다.</h3>
<blockquote>
<p>일하다가 개발 스펙에 관련된 <code>질문/요청을 들었을떄</code> 바로 된다/안 된다 <code>답을 하는 대신에</code> 해당 스펙이 해결하려는 문제, 상대방의 의도/상황을 <code>먼저 물어본다.</code></p>
</blockquote>
<ul>
<li>문제에 대해 생각을 해보지 않고 해결책으로 넘어가는 실수를 저지른다. 이를 해결하기 위해서 단지 물어보는 습관을 기르면 된다. 무조건 한 번더 문제가 무엇인지 물어보자<h3 id="2-상대방의-말을-듣고-내가-이해한-바를-공유한다">2. 상대방의 말을 듣고 내가 이해한 바를 공유한다.</h3>
<blockquote>
<p>상대방 말을 끝까지 <code>들었을 때,</code> 중간에 끊거나 바로 <code>내 답을 얘기하는 대신에</code> 내가 이해한 바를 <code>한번 더 공유하자.</code></p>
</blockquote>
</li>
</ul>
<h3 id="3-안-된다고-말할-때는-상대방의-관점에서-대안을-제시한다">3. 안 된다고 말할 때는 상대방의 관점에서 대안을 제시한다.</h3>
<blockquote>
<p> 문제가 있을 때, 안 되는 이유를 길게 <code>설명하는 대신에</code> 제약을 덜 받는 다른 방향성이나 <code>대안을 제시한다.</code></p>
</blockquote>
<ul>
<li>대부분 상대방이 궁극적으로 원하는 것은 <code>왜 기술적으로 어려운지</code> 이해하는 게 아니다.</li>
<li>이유를 납득 시키는 것보다 대안에 집중해야한다.</li>
</ul>
<h3 id="4-문제를-해결할-또-다른-방법은-없을지-고민한다">4. 문제를 해결할 <code>또 다른 방법</code>은 없을지 고민한다.</h3>
<blockquote>
<p>하느냐 안 하느냐 <code>딜레마 상황이라고 느껴질 때,</code> 혼자 안 된다고 <code>단정하는 대신에</code> 혹시 다른 방법은 없을까? <code>한번 더 질문/생각해본다.</code></p>
</blockquote>
<ul>
<li>&#39;된다/안된다&#39;의 흑백 논리에 갇히지 말자.</li>
<li>한번 더 물어보면, 다양한 사람들의 인풋을 받을 수 있다.</li>
</ul>
<h2 id="최종-정리">최종 정리</h2>
<ol>
<li>좋은 개발자가 되려면 <code>다른 직군과의 커뮤니케이션 능력</code>이 필수다.</li>
<li>커뮤니케이션 <code>못하는 개발자</code>는 <code>스펙 구현</code>에 집중하고, 커뮤니케이션 <code>잘하는 개발자</code>는 <code>문제 해결</code>에 집중한다.</li>
<li>커뮤니케이션 잘하는 개발자로 성장하려면 <code>실제로 행동에 옮길 수 있는 습관</code>을 만들어야 한다.</li>
</ol>
<p><a href="https://www.inflearn.com/course/lecture?courseSlug=%EC%9D%B8%ED%94%84%EC%BD%982023-%EB%8B%A4%EC%8B%9C%EB%B3%B4%EA%B8%B0&amp;unitId=177902">출처 - 커뮤니케이션 잘하는 개발자의 4가지 습관 | 인프콘2023</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] Redux란?]]></title>
            <link>https://velog.io/@pangs_boy/CS-Redux%EB%9E%80</link>
            <guid>https://velog.io/@pangs_boy/CS-Redux%EB%9E%80</guid>
            <pubDate>Sat, 27 Jan 2024 06:16:09 GMT</pubDate>
            <description><![CDATA[<h1 id="리덕스redux란">리덕스(Redux)란?</h1>
<h2 id="--redux란-js-상태관리-라이브러리이다">- Redux란 JS 상태관리 라이브러리이다.</h2>
<h2 id="💡그런데-상태관리도구는-왜-필요한-것일까">💡그런데 상태관리도구는 왜 필요한 것일까?</h2>
<ul>
<li>React에서 State 즉 데이터는 컴포넌트 안에서 관리된다. 자식 컴포넌트 간의 다이렉트 데이터 전달이 불가능한데, 이 떄 부모 컴포넌트를 통해 전달 받을 수 있다.</li>
<li>자식 컴포넌트가 많아질수록 상태관리가 매우 복잡해지는데 이를 해결하기 위해 Redux 등의 상태관리 라이브러리들이 탄생한 것이다.</li>
</ul>
<hr>
<h1 id="redux의-원칙">Redux의 원칙</h1>
<h2 id="1-single-source-of-truth">1. Single source of truth</h2>
<ul>
<li>동일한 데이터는 항상 같은 곳에서 가지고 온다.</li>
<li>Store라는 하나만의 데이터 공간이 존재한다.<h2 id="2-state-is-read-only">2. State is read-only</h2>
</li>
<li>Action이라는 객체를 통해서만 상태를 변경할 수 있다.(ex)useState)<h2 id="3-changes-are-made-with-pure-functions">3. Changes are made with pure functions</h2>
</li>
<li>변경은 순수함수로만 가능하다.</li>
<li>Reducer와 연관되는 개념이다.</li>
<li>Store - Action - Reducer
<img src="https://velog.velcdn.com/images/pangs_boy/post/f6ca23a3-b178-4a79-96d3-9b5a344d34df/image.png" alt=""></li>
</ul>
<hr>
<h1 id="redux-용어-설명">Redux 용어 설명</h1>
<h1 id="store">Store</h1>
<blockquote>
<p>Store는 상태가 관리되는 하나의 공간이다.(유일)</p>
</blockquote>
<ul>
<li>컴포넌트와 별개로 스토어라는 공간에 필요한 상태를 담는다.</li>
<li>컴포넌트에서 상태 정보가 필요할 떄 스토어에 접근한다.</li>
</ul>
<h1 id="action">Action</h1>
<blockquote>
<ul>
<li>Action은 앱에서 스토어에 운반할 데이터를 의미한다.</li>
</ul>
</blockquote>
<ul>
<li>객체 형식으로 되어있다.</li>
</ul>
<pre><code class="language-json">{
  type: &#39;ACTION_CHANGE_USER&#39;, // 필수
  payload: { // 옵션
    address: &#39;서웉륵별시&#39;,
    age: 100
  }
}</code></pre>
<h1 id="reducer">Reducer</h1>
<ul>
<li>Action을 Store에 바로 전달하는 것이 아니라 Reducer에 전달해야 한다.</li>
<li>Reducer가 주문을 보고 Store의 상태를 업데이트 한다.</li>
<li>Action을 Reducer에 전달하기 위해 dispatch() 메서드를 사용해야 한다.</li>
</ul>
<h1 id="dispatch">Dispatch</h1>
<ul>
<li>Dispatch는 Store의 내장 함수로 Action을 발생시킨다.</li>
<li>Action을 Parameter로 전달하고 Reducer를 호출한다.</li>
</ul>
<h1 id="subscribe">Subscribe</h1>
<ul>
<li>Subscribe는 Store의 내장 함수로 특정 함수를 전달해주면 Action이 Dispatch 되었을 때마다 전달된 함수가 호출된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/pangs_boy/post/feb3a796-fa14-49de-a020-c08f23a45d15/image.png" alt=""></p>
<blockquote>
<ol>
<li>Action 객체가 dispatch() 메서드에 전달 </li>
<li>dispatch()를 통해 Reducer를 호출</li>
<li>Reducer는 새로운 Store를 생성</li>
</ol>
</blockquote>
<hr>
<h1 id="redux의-장점">Redux의 장점</h1>
<blockquote>
<ol>
<li>순수 함수를 사용하여 상태를 예측 가능하게 한다.</li>
<li>유지보수가 편하다.</li>
<li>Redux dev tool이 있어 디버깅에 유리하다.</li>
<li>비동기를 지원하는 Redux Sage, Redux Thunk등 다양한 미들웨어가 존재한다.</li>
</ol>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>