<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ithank73.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 08 Dec 2023 10:44:53 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ithank73.log</title>
            <url>https://velog.velcdn.com/images/i_thank/profile/c41795be-a0ce-4186-aec9-b6ce9cc08c8f/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ithank73.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/i_thank" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[nginx의 root html은 도대체 왜 저기에 있는가]]></title>
            <link>https://velog.io/@i_thank/nginx%EC%9D%98-root-html%EC%9D%80-%EB%8F%84%EB%8C%80%EC%B2%B4-%EC%99%9C-%EC%A0%80%EA%B8%B0%EC%97%90-%EC%9E%88%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@i_thank/nginx%EC%9D%98-root-html%EC%9D%80-%EB%8F%84%EB%8C%80%EC%B2%B4-%EC%99%9C-%EC%A0%80%EA%B8%B0%EC%97%90-%EC%9E%88%EB%8A%94%EA%B0%80</guid>
            <pubDate>Fri, 08 Dec 2023 10:44:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/i_thank/post/4e8e6157-5a32-4648-a0d2-c17edbe47abc/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_thank/post/e8e5f536-b091-4adb-b9ff-c43f971505dd/image.png" alt="">
<img src="https://velog.velcdn.com/images/i_thank/post/07f3ca8f-d1fb-40c7-b44b-1f0c6497be53/image.png" alt=""></p>
<ol>
<li>처음 nginx를 설치.</li>
<li><strong>root에</strong> <strong>html이</strong> 기본값으로 설정되어있다.</li>
<li><strong>localhost:8080</strong>실행시 <strong>Welcome nginx</strong> 가 잘 뜨게된다.<pre><code>/opt/homebrew/etc/nginx/nginx.conf</code></pre><pre><code class="language-nginx">server {
 listen       8080;
 server_name  localhost;
 location / {
     root   html;
     index  index.html index.htm;
}</code></pre>
<h3 id="근데-저-html은-어디-있는것인가">근데 저 html은 어디 있는것인가?</h3>
</li>
</ol>
<hr>
<pre><code class="language-php">// 해당 명령을 내리면,
nginx -V </code></pre>
<pre><code class="language-php">// 이런 결과물이 있다.
...
--prefix=/opt/homebrew/Cellar/nginx/1.25.3
...</code></pre>
<pre><code class="language-php">// 들어가보자
cd /opt/homebrew/Cellar/nginx/1.25.3

// 그리고 목록을 보자
ls -al</code></pre>
<pre><code class="language-php">// 그러면, 이처럼 link가 걸려 있다.
html -&gt; ../../../var/www</code></pre>
<p>바로 저기 있는것이다.<img src="https://velog.velcdn.com/images/i_thank/post/c07f5d78-fb44-4efd-b469-073d6fa5bb00/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[전략 패턴(strategy pattern)]]></title>
            <link>https://velog.io/@i_thank/%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4strategy-pattern</link>
            <guid>https://velog.io/@i_thank/%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4strategy-pattern</guid>
            <pubDate>Mon, 05 Sep 2022 07:57:33 GMT</pubDate>
            <description><![CDATA[<p><del>오늘 제가 2시간동안 슬램덩크 요약본을 너무 재밋게 봤습니다.</del> 
농구 게임에 <code>캐릭터</code>가 있습니다. 기본적으로 <code>슛</code>이 있고, <code>드리블</code>이 있죠.</p>
<h4 id="슛">슛</h4>
<ol>
<li>레이업 슛</li>
<li>슬램 덩크 슛</li>
<li>3점 슛<h4 id="드리블">드리블</h4>
</li>
<li>달리기 드리블</li>
<li>풰이크 드리블</li>
<li>회전 드리블</li>
</ol>
<p>먼저 이런식으로 기본기능들을 가진 <code>슛</code>과 <code>드리블</code>은 <code>행동</code>이라고 표현할 수 있습니다.</p>
<p>우리회사의 열정적인 기획팀장님의 요구사항이 추가되다 보면 </p>
<ol>
<li><code>슛</code>에 <code>레이업슛</code>의 로직이 살짝 <code>수정</code>된다거나,</li>
<li><code>슛</code>을 누르면<code>레이업슛</code>만 했는데 이게<code>슬램덩크 슛</code>으로 <code>변경</code>될수도 있고,</li>
<li><code>드리블</code>에 <code>성큼성큼 드리블</code>같은 새로운 놈이 <code>추가</code> 될 수도 있습니다.</li>
</ol>
<h3 id="이처럼-전략-패턴은">이처럼 전략 패턴은</h3>
<p><code>어떤객체(캐릭터)</code>의 <code>어떤행동(슛 or 드리블)</code>을 쉽게 <code>수정</code>하고 <code>변경</code>할 수 있게 하는 패턴입니다.
<img src="https://velog.velcdn.com/images/i_thank/post/7856f5b7-0fe9-45f3-b1fd-bd224bc834a2/image.png" alt=""></p>
<h3 id="기획-및-설계">기획 및 설계</h3>
<p>기본적으로 이 농구게임에는 <code>슛</code>과 <code>드리블</code>이라는 기능이 들어가야 한다고 합니다.
그렇다면 <code>슛</code>과 <code>드리블</code>을 <code>행동집합</code>으로 볼 수 있겠군요. 이 행동집합에는 같은 <code>슛</code>이지만 여러 종류의 <code>슛</code>으로 나뉘니까요.
각 <code>슛</code>과 <code>드리블</code>에는 3개씩 <code>다른 행동</code>들이 있습니다. 캐릭터가 <code>슛</code>을 하거나 <code>드리블</code>을 할 때 동작하는 행동들이지요.
즉, 어떤 행동을 자유롭게 바꾸고자 할때 사용하면 좋다는 것입니다.
<del>의식의 흐름대로 상속이나 단순 인터페이스를 사용해서 구현 할 수 있지만, 이건 나중에 시간이 되면 다루도록 하겠습니다.</del></p>
<p>각 행동들을 부모 클래스에 정의하지 않고, 클래스 집합으로 만들어서 분리함으로(인터페이스로 분리후 그 인터페이스를 구현하는 방식으로) 쉽게 <code>행동변경</code>을 할 수 있습니다. 그래서 <code>setShoot()</code>, <code>setDribble()</code>같은 함수를 부모클래스에 정의해주었습니다.</p>
<pre><code class="language-php">// 메인 클래스
//이런식으로 말이죠.
BasketBall 강백호 = new 강백호();
강백호.shoot(); // 슬램덩크 슛
강백호.setShoot(new Point3Shoot()); 
강백호.shoot(); // 3점 슛</code></pre>
<h3 id="부모-클래스-정의">부모 클래스 정의</h3>
<pre><code class="language-php">public class BasketBall{

    ShootInterface shootInterface;
    DribbleInterface dribbleInterface;

    public function shoot(){
        shootInterce.shoot();
    }

    public function dribble(){
        dribbleInterface.dribble();
    }

    public function setShoot(ShootInterface si){
        shootInterface = si;
    }

    public function setDribble(DribbleInterface di){
        dribbleInterface = di;
    }
}</code></pre>
<ol>
<li><p>행동 집합을 정의한 인터페이스를 선언해 줍니다. 이 부모 클래스를 상속받은 인스턴스에서는 여기 있는 인터페이스 타입으로 행동을 할당해줍니다.</p>
<pre><code class="language-php">// 인스턴스 클래스
public class 강백호 extends BasketBall{
   __constructor(){
       shootInterface = new SlamdunkShoot();
       dribbleInterface = new Dribble();
   }
}</code></pre>
</li>
<li><p>각 행동들의 <code>실제 구현은 외부 클래스로 위임</code>해주었으므로, 이 행동들을 호출할 수 있는 함수를 선언해 주었습니다(<code>shoot()</code>, <code>dribble()</code>).</p>
<pre><code class="language-php">// 메인 클래스
BasketBall 강백호 = new 강백호(); 
// 위 강백호 클래스에서 
// shootInterface에 SlamdunkShoot(); 을 할당한 게 보입니다.
// SlamdunkShoot클래스의 shoot(); 함수에서 실제 구현을 맡고 있는거죠.
강백호.shoot(); // 슬램덩크 슛</code></pre>
<h3 id="인스턴스-클래스-정의">인스턴스 클래스 정의</h3>
<pre><code class="language-php">public class 강백호 extends BasketBall{
 __constructor{
     shootInterface = new SlamdunkShoot();
     dribbleInterface = new RunDribble();
 }
}</code></pre>
<pre><code class="language-php">public class 정대만 extends BasketBall{
 __constructor{
     shootInterface = new Point3Shoot();
     dribbleInterface = new RunDribble();
 }
}</code></pre>
<pre><code class="language-php">public class 송태섭 extends BasketBall{
 __constructor{
     shootInterface = new LayupShoot();
     dribbleInterfade = new RunDribble();
 }
}</code></pre>
<h3 id="메인-클래스">메인 클래스</h3>
<pre><code class="language-php">public class PlayBasketBall{
 BasketBall 강백호 = new 강백호();
 강백호.shoot(); // 슬램덩크 슛
 강백호.dribble(); // 달리기 드리블

 BasketBall 정대만 = new 정대만();
 정대만.shoot(); // 3점 슛
 정대만.dribble(); // 달리기 드리블

 BasketBall 송태섭 = new 송태섭();
 송태섭.shoot(); // 레이업 슛
 송태섭.dribble() // 풰이크 드리블

 // 기능 변경
 강백호.setShoot(new Point3Shoot());
 강백호.shoot(); // 3점슛
}</code></pre>
<h3 id="정리">정리</h3>
<p>전략패턴이란 무언가(어떤 객체) 어떤 기능(행동)이 있을 때, 그 기능을 쉽게 수정하기 위해서, 해당 알고리즘을 직접 구현하지 않고 외부 클래스로 빼내서 그 일을 위임시키는 것이다.
ex) 네비게이션 경로 검색 </p>
</li>
<li><p>걷기 도로</p>
</li>
<li><p>대중교통</p>
</li>
<li><p>자전거 도로
이처럼 네비게이션은 경로라는 기능을 검색하는데 이 중 어느하나를 선택한다.
이 때, 각각의 로직이 바뀔수도 있고, 추가될수도 있기 떄문에, 이걸 편히 하기위한 패턴이다. </p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redis Command]]></title>
            <link>https://velog.io/@i_thank/Redis-Command</link>
            <guid>https://velog.io/@i_thank/Redis-Command</guid>
            <pubDate>Wed, 10 Aug 2022 06:43:14 GMT</pubDate>
            <description><![CDATA[<h3 id="값-설정">값 설정</h3>
<h4 id="set-get">SET, GET</h4>
<p><code>SET hello &quot;world&quot;</code> =&gt; OK
<code>GET hello</code> =&gt; <code>world</code></p>
<h4 id="append">APPEND</h4>
<p><code>APPEND animals &quot;lion&quot;</code> =&gt; 4
<code>GET animals</code> =&gt; <code>lion</code>
<code>APPEND animals &quot;tiger&quot;</code> =&gt; 9
<code>GET animals</code> =&gt; <code>liontiger</code></p>
<h3 id="redis-command-document">redis command document</h3>
<p><a href="https://redis.io/commands">https://redis.io/commands</a> 여기서 참고해주면 아주 좋습니다.</p>
<p>사실 command에는 많은 옵션과 속성들을 지정할 수 있는데, 이 참에 영어공부도 같이 해줍니다.</p>
<ul>
<li><img src="https://velog.velcdn.com/images/i_thank/post/2203377d-377d-4d73-a714-211cffb302f2/image.png" alt=""></li>
</ul>
<h3 id="옵션-사용해보기">옵션 사용해보기</h3>
<h3 id="set">SET</h3>
<p><code>SET order_id 2 EX 2</code> -&gt; 2초 후에 사라짐.
<code>SET order_id 7 NX</code> -&gt; order_id가 없을때만 설정
<code>SET order_id 8 GET</code> -&gt; 이전에 저장되어있던 값 return.</p>
<h4 id="setex">SETEX</h4>
<p><code>SETEX color 2 &quot;red&quot;</code> -&gt; <code>SET + EX</code> 2초후에 사라짐</p>
<h4 id="setnx">SETNX</h4>
<p><code>SET color red</code> =&gt; <code>SET</code> + <code>NX</code></p>
<h4 id="mset">MSET</h4>
<p>여러개 세팅할때 사용.
<code>MSET first test1 second test2</code>
<code>GET first</code> =&gt; &quot;test1&quot;
<code>GET second</code> =&gt; &quot;test2&quot;</p>
<h4 id="msetnx">MSETNX</h4>
<p>추측가능...</p>
<h3 id="get">GET</h3>
<h4 id="mget">MGET</h4>
<p>여러개 가져옵니다.
<code>MSET first test1 second test2</code>
<code>MGET first second</code> =&gt; <code>[&quot;test1&quot;,&quot;test2&quot;]</code></p>
<h3 id="range">RANGE</h3>
<p> <code>SET model &quot;audi&quot;</code> -&gt; &quot;audi&quot;
 <code>GETRANGE model 0 2</code> -&gt; &quot;aud&quot;, programming적 index가 적용됩니다.(0이 첫번째.)
 <code>SETRANGE model 2 &quot;to&quot;</code> -&gt; 4, replace를 합니다.
 <code>GET model</code> -&gt; &quot;auto&quot;</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Event, Queue, Email]]></title>
            <link>https://velog.io/@i_thank/Event-Queue-Email</link>
            <guid>https://velog.io/@i_thank/Event-Queue-Email</guid>
            <pubDate>Sat, 06 Aug 2022 15:58:36 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/i_thank/post/c6e67c78-f692-4037-b4e0-736828143ee5/image.png" alt="">
<code>클라이언트</code>가 <code>이메일</code>을 입력하면 <code>서버</code>에서는 <code>queue</code>를 통해 <code>비동기</code>로 <code>이메일</code>을 전송합니다.</p>
<h3 id="이벤트">이벤트</h3>
<ul>
<li>When to use Event
<a href="https://stackoverflow.com/questions/48198189/when-to-use-events-in-laravel">https://stackoverflow.com/questions/48198189/when-to-use-events-in-laravel</a>
<img src="https://velog.velcdn.com/images/i_thank/post/74300d9f-d10d-4805-afaa-a51a7445fdd7/image.png" alt=""></li>
<li><code>특정 이벤트 객체</code> 는 다수의 <code>이벤트리스너 객체</code> 와 연관된다고 생각하면 됩니다. </li>
<li><code>특정 이벤트가 발생</code>하면 이 이벤트와 관련된 <code>이벤트리스너</code> 에서 원하는 로직들이 실행됩니다.</li>
</ul>
<ol>
<li>이벤트 객체를 생성하고
<code>php artisan make:event NewsLetterEvent</code></li>
<li>위 이벤트발생시 실행할 이벤트 리스너를 생성하고
<code>php artisan make:listen NewsLetterEventListener --event=NewsLetterEvent</code></li>
<li>EventServiceProvider에 등록을 해주면 됩니다.<pre><code class="language-php">// EventServiceProvider.php
...
protected $listen = [
 ...
 NewsletterEvent::class =&gt; [
         NewsletterEventListener::class,
         ... // 등록할 이벤트 리스너들 추가 목록
 ]
...</code></pre>
</li>
<li>실행은 event 헬퍼 함수를 통해 할 수 있습니다.
<code>event(new NewsletterEvent($email));</code></li>
</ol>
<h3 id="이메일">이메일</h3>
<ol>
<li>email 객체를 생성해 줍니다.
<code>php artisan make:mail Newsletter</code><pre><code class="language-php">// Mail/Newsletter.php
public function __construct($user)
{
  $this-&gt;user = $user;
}
</code></pre>
</li>
</ol>
<p>public function build()
{
    $public_path = public_path(&#39;images/golang.png&#39;);</p>
<pre><code>return $this-&gt;subject($this-&gt;user[&#39;email&#39;] . &#39;의 회원가입을 축하합니다.&#39;)
    -&gt;attach($public_path)
    -&gt;view(&#39;mail.newsletter&#39;)
    -&gt;with([&#39;custom&#39; =&gt; &#39;thank you thank you!&#39;]);</code></pre><p>}</p>
<pre><code>2. 메일 템플릿을 작성해 줍니다.
```php
// mail.newsletter.blade.php
&lt;p&gt;{{$user[&#39;email&#39;]}}님 안녕하세요!
&lt;/p&gt;
&lt;p&gt;{{ $custom }}&lt;/p&gt;
&lt;img src=&quot;{{ $message-&gt;embed(public_path(&#39;images/docker_whale.png&#39;)) }}&quot; alt=&quot;&quot;&gt;
</code></pre><ol start="3">
<li>실행합니다.<pre><code class="language-php">$user = [
 &#39;email&#39; =&gt; $this-&gt;email
];
</code></pre>
</li>
</ol>
<p>$mail = new Newsletter($user);</p>
<p>Mail::to($this-&gt;email)-&gt;send($mail);</p>
<pre><code>
### 큐
이메일 같이 경우 처리되는데 시간이 걸리는 작업들은 비동기로 처리하는게 유리합니다.
![](https://velog.velcdn.com/images/i_thank/post/d6fd96ab-49a2-4ff2-9287-0d6e64e63264/image.png)
1) 비동기 처리를 위해서는 `queue를 실행`해야합니다. 그러지 않으면 `job`들이 쌓이기만 하고 처리는 되지 않습니다.
`php artisan queue:work`

2) 먼저 `queue connection(queue driver)`을 설정해줍니다.</code></pre><p>// .env
QUEUE_CONNECTION=database</p>
<pre><code>3) job들을 쌓을 `queue table`을 생성해 줍니다.
`php artisan queue:table`
`php artisan migrate`
![](https://velog.velcdn.com/images/i_thank/post/71b4bf31-6fda-4cf8-a1ed-17d8310df89c/image.png)
job을 쌓을 jobs 테이블과, 실패시 쌓이는 failed_jobs 테이블이 생성되었습니다.

4) 처리할 Job을 생성해 줍니다.
`php artisan make:job NewsletterJob`
```php
// NewsletterJob.php
public function __construct($email)
{
    $this-&gt;email = $email;
}

public function handle()
{
    $user = [
        &#39;email&#39; =&gt; $this-&gt;email
    ];

    $mail = new Newsletter($user);
    Mail::to($this-&gt;email)-&gt;send($mail);
}</code></pre><p>이메일 보내는 처리를 적어 주었습니다.</p>
<h3 id="로직-순서-정리">로직 순서 정리</h3>
<p><img src="https://velog.velcdn.com/images/i_thank/post/1530686a-eb10-49a7-a975-99deaff14ca5/image.png" alt=""></p>
<h3 id="github-source">github source</h3>
<p><a href="https://github.com/kaelch/laravel8/tree/event">https://github.com/kaelch/laravel8/tree/event</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter Navigator]]></title>
            <link>https://velog.io/@i_thank/Flutter-Navigator</link>
            <guid>https://velog.io/@i_thank/Flutter-Navigator</guid>
            <pubDate>Mon, 25 Jul 2022 06:06:39 GMT</pubDate>
            <description><![CDATA[<h3 id="1-기본-navigator">1. 기본 Navigator</h3>
<pre><code class="language-dart">TextButton(
    child: Text(&#39;push navigate&#39;),
    onPressed: (){
        Navigator.push(context,
            MaterialPageRoute(
                builder: (_) =&gt; SecondPage()
            )
        )
    }
)</code></pre>
<p><code>Navigator.push(context, builder: (_) =&gt; SecondPage())</code> 부분이 핵심입니다.
여기서 잘못된 <code>context</code> 전달로 제대로 작동하지 않을 수 있습니다.</p>
<pre><code class="language-dart">class SecondPage extends StatelessWidget {
  ...
  Widget build(BuildContext ctx) {
    return Scaffold(
        body: Center(
          child: ElevatedButton(
            child: Text(&#39;Back&#39;),
            onPressed: (){
              Navigator.pop(ctx);
            },
          ),
        )
    );
  }
}</code></pre>
<p>다시 되돌아갈때는, <code>Navigator.pop(context)</code>를 통해 구현할 수 있습니다.</p>
<h3 id="2-materialapp-navigator">2. MaterialApp Navigator</h3>
<p><code>MaterialApp()</code> 내부에서 라우트를 지정할 수 있습니다.</p>
<pre><code class="language-dart">MaterialApp(
    initialRoute: &#39;/&#39;,
    routes: {
        &#39;/a&#39;: (context) =&gt; ScreenA(),
        &#39;/b&#39;: (context) =&gt; ScreenB(),
        &#39;/c&#39;: (context) =&gt; ScreenC()
    },
    home: ScreenA()
)</code></pre>
<pre><code class="language-dart">class ScreenA extends StatelessWidget {
  ...
  ElevatedButton(
      child: Text(&#39;Go to B&#39;)
      onPressed: (){
          Navigator.pushNamed(context, &#39;/b&#39;);
      }, 
  ),
}</code></pre>
<p><code>Navigator.pushNamed()</code>를 통해 <code>MaterialApp()</code>에서 지정한 라우트로 이동할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redis Test Setting]]></title>
            <link>https://velog.io/@i_thank/Redis-Basic</link>
            <guid>https://velog.io/@i_thank/Redis-Basic</guid>
            <pubDate>Mon, 25 Jul 2022 00:28:06 GMT</pubDate>
            <description><![CDATA[<h3 id="목표">목표</h3>
<p>redis db를 cloud로 만들고, test tool을통해 redis 테스트 환경을 간편히 조성합니다.</p>
<h3 id="1-redis-console">1. redis console</h3>
<h4 id="1-httpsrediscom-로-접속합니다">1. <a href="https://redis.com/">https://redis.com/</a> 로 접속합니다.</h4>
<h4 id="2-로그인을-하게되면-redis-console로-접속하게-됩니다">2. 로그인을 하게되면 redis console로 접속하게 됩니다.</h4>
<ul>
<li><img src="https://velog.velcdn.com/images/i_thank/post/d589af6b-277d-4673-976e-d149c7d860a7/image.png" alt=""><h4 id="3-원하는-plan을-선택하고">3. 원하는 plan을 선택하고</h4>
</li>
<li><img src="https://velog.velcdn.com/images/i_thank/post/bd503401-f2c0-4a92-85fb-7dcc195d4e4b/image.png" alt=""></li>
<li><img src="https://velog.velcdn.com/images/i_thank/post/32936d24-e510-4ab6-804d-2e984a6cddde/image.png" alt=""></li>
</ul>
<p><code>Create subscription</code>으로 생성합니다.</p>
<h4 id="4-database를-생성해-줍니다">4. database를 생성해 줍니다.</h4>
<ul>
<li><img src="https://velog.velcdn.com/images/i_thank/post/8714bad7-449a-4dab-8f8b-53c53faf7781/image.png" alt=""></li>
<li><img src="https://velog.velcdn.com/images/i_thank/post/78075c4a-c368-4cdf-b488-8e4c6b481669/image.png" alt=""></li>
</ul>
<p><code>Database name</code>을 설정해주고 <code>Activate database</code>로 생성해 줍니다.</p>
<h3 id="2-rbook-console">2. rbook console</h3>
<p>쿼리를 날려볼 도구입니다.</p>
<h4 id="1-httpsrbookcloud-로-접속합니다">1. <a href="https://rbook.cloud/">https://rbook.cloud/</a> 로 접속합니다.</h4>
<ul>
<li><img src="https://velog.velcdn.com/images/i_thank/post/9b88d1dc-15e4-43cb-85b7-20665ed560cc/image.png" alt=""></li>
</ul>
<p>초라해 보이지만 강력하다고 합니다. <code>New Notebook</code>을 클릭합니다.</p>
<ul>
<li><img src="https://velog.velcdn.com/images/i_thank/post/70c5afc3-cbad-44c9-8778-f1ca09a43da8/image.png" alt=""></li>
</ul>
<p>이전에 만들었던 redis database와 연결해야하니 <code>Connect</code>을 클릭해 줍니다.</p>
<ul>
<li><img src="https://velog.velcdn.com/images/i_thank/post/b793d38e-dd60-4cef-9ffe-accc0cd398d4/image.png" alt=""> </li>
</ul>
<p>이전에 생성했던 redis db의 정보를 적어줍니다.</p>
<ul>
<li>Host -&gt; Public endpoint host(ex. redis-99999.c999.ap-south-1-1.ec2.cloud.redislabs.com) </li>
<li>Port -&gt; Public endpoint port(ex. 12345)</li>
<li>Password -&gt; Security부분 Default user password</li>
</ul>
<p><code>Test</code> 후 <code>Save</code>를 해줍니다.</p>
<ul>
<li><img src="https://velog.velcdn.com/images/i_thank/post/a44ff95b-4b0e-4410-bb3b-9fad5f9496ec/image.png" alt=""></li>
</ul>
<p>완료 되었군요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Laravel Slack Notification]]></title>
            <link>https://velog.io/@i_thank/Laravel-Slack-Notification</link>
            <guid>https://velog.io/@i_thank/Laravel-Slack-Notification</guid>
            <pubDate>Tue, 12 Jul 2022 06:20:23 GMT</pubDate>
            <description><![CDATA[<h3 id="목표">목표</h3>
<p><img src="https://velog.velcdn.com/images/i_thank/post/6af6e05a-7127-45c2-a06e-e4f1c73bffae/image.png" alt=""></p>
<ol>
<li>비즈니스 로직에서 Laravel 함수인 <code>notify(new SlackNotification())</code>를 호출합니다.</li>
<li><code>notify()</code>를 호출하면, <code>SlackNotification</code>에서 전처리 작업을 합니다.</li>
<li><code>Slack</code>에 메세지가 나타납니다.</li>
</ol>
<h3 id="과정">과정</h3>
<ol>
<li><h4 id="slack-notification-패키지를-설치합니다">Slack Notification 패키지를 설치합니다.</h4>
<p><code>composer require laravel/slack-notification-channel</code></p>
</li>
<li><h4 id="slack-notification을-만들어-줍니다">Slack Notification을 만들어 줍니다.</h4>
<p>여기서 slack 알림 전처리 작업을 합니다.</p>
<p><code>php artisan make:notification SlackNotification</code></p>
</li>
</ol>
<pre><code class="language-php">  &lt;?php
  namespace App\Notifications;
  use ...

  class SendNotification extends Notification
  {
      use Queueable;
      private $message;

      public function __construct($message)
      {
          $this-&gt;message = $message;
      }

      public function via($notifiable)
      {
          return [&#39;slack&#39;];
      }

      public function toSlack($notifiable): SlackMessage
     {
         return (new SlackMessage)
             -&gt;from(&#39;Laravel&#39;)
             -&gt;image(&#39;https://laravel.com/img/favicon/favicon.ico&#39;)
             -&gt;content($this-&gt;message);
     }
  }</code></pre>
<p>   2-1. <code>__constructor</code> 에서 보낼 메세지를 설정했습니다.
   2-2. <code>via()</code>에서<code>default</code>값인 <code>mail</code> 에서 <code>slack</code>으로 변경해줍니다.
   2-3. 위에서 설치한 <code>slack notification</code> 패키지가 잘 설치 되면 <code>toSlack</code>을 사용 할 수있습니다. 
   <code>toMail</code> 또한 <code>toSlack</code>으로 변경해 줍니다.
   2-4. 여기서 매개변수인 <code>$notifiable</code>은 이후 알림을 보내기 위해 <code>use Notifiable</code>한 <code>Model</code>의 값을 가져옵니다.</p>
<pre><code class="language-php">   // Models/User.php
   class User extends Authenticatable
    {
        use Notifiable;
    }

   // NotificationController.php
   class NotificationController{
       public function slackNotification(){
           //Notifiable을 use한 Model인 user에서 notify를 호출할 수 있습니다.

           // 1. 한번 호출
           $user = User::first();
           $message = &#39;라라벨이 말합니다.&#39;;
           $user-&gt;notify(new SlackNotification($message));

           // 2. 전부다 호출 
           $users = User::all();
           Notification::send($users, new SlackNotification($message));

           // 위에서 말한 매개변수인 $notifiable은 여기의 각 $user가 됩니다.
           // $message는 생성자의 인자로 사용 되겠지요.
       }
   }</code></pre>
<ol start="3">
<li><h4 id="slack-설정파일을-설정합니다">Slack 설정파일을 설정합니다.</h4>
<p>3-1. 2022-7월 기준 <code>Slack 찾아보기</code> 에서 <code>앱</code>을 클릭해 줍니다.
<img src="https://velog.velcdn.com/images/i_thank/post/1c0e2bc8-29a8-4bac-b50e-4c7d12a3c4f0/image.png" alt="">
3-2. incoming-webhook을 찾아 클릭해 줍니다.
<img src="https://velog.velcdn.com/images/i_thank/post/4088079d-2cfb-4461-9adc-14849bd35ff7/image.png" alt="">
3-3. Slack에 추가합니다.
<img src="https://velog.velcdn.com/images/i_thank/post/f5d75ca4-f71b-4aad-97a1-f0b3cc8dbaeb/image.png" alt="">
3-4. 원하는 채널을 선택해 줍니다.
<img src="https://velog.velcdn.com/images/i_thank/post/c5af23da-f54c-4e83-9b38-2906dac1e9e8/image.png" alt="">
3-5. 웹 후크 URL의 모든 URL을 복사해 둡니다.
<img src="https://velog.velcdn.com/images/i_thank/post/9ef3ff86-8f38-4f31-ae06-7767a9f9cbb3/image.png" alt=""></p>
<pre><code class="language-php">&lt;?php
class User extends Authenticatable
{
    use Notifiable;

    public function routeNotificationForSlack(): string
    {
        return config(&#39;services.notification.slack_webhook_url&#39;);
    }
}

// config/services.php
// config에 추가
&#39;notification&#39; =&gt; [
   &#39;slack_webhook_url&#39; =&gt; env(&#39;SLACK_WEBHOOK_URL&#39;),
]
//.env 에 추가
SLACK_WEBHOOK_URL=https://hooks.slack.com/services...</code></pre>
</li>
<li><p>####라우트 등록 및 실행</p>
<pre><code class="language-php">// web.php
Route::get(&#39;/slack-notification&#39;, [NotificationController::class, &#39;slackNotification&#39;]);</code></pre>
<p><img src="https://velog.velcdn.com/images/i_thank/post/a234ee18-a656-4ce3-b49c-f8130d21176b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/i_thank/post/f4e1c6fa-2c8b-491f-aeea-de4631269c30/image.png" alt=""></p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[laravel Controller - Middleware 동작 순서 정리]]></title>
            <link>https://velog.io/@i_thank/laravel-Controller-Middleware-%EB%8F%99%EC%9E%91-%EC%88%9C%EC%84%9C-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@i_thank/laravel-Controller-Middleware-%EB%8F%99%EC%9E%91-%EC%88%9C%EC%84%9C-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 30 Jun 2022 04:39:27 GMT</pubDate>
            <description><![CDATA[<p><code>Laravel middleware</code>를 적용하던 중, <code>Middleware</code>내부 로직에 따라서 각 <code>Controller(Business Logic)</code>와 <code>Middleware</code>에서 <code>\Log::Debug</code> 호출 순서가 다르다는걸 발견했습니다.</p>
<p>원래 <code>middleware</code>는 <code>요청 라우트의 비즈니스 로직(컨트롤러나 서비스) 들어가기 전에</code> <code>무조건 먼저 전부다 실행</code>되는 줄 알았는데 말이죠.</p>
<h3 id="기본-미들웨어">기본 미들웨어</h3>
<pre><code class="language-php">public function handle(Request $request, Closure $next)
{
    return $next($request);
}</code></pre>
<p>처음 <code>middleware</code>를 <code>make</code> 했을 때, 기본로직입니다. </p>
<h3 id="변형-미들웨어">변형 미들웨어</h3>
<p>하지만 아래처럼 로직을 바꾸게 되면, <code>Controller 내부 비즈니스 로직 실행 후에</code> <code>middleware 로직을 실행</code> 할 수 있습니다.</p>
<pre><code class="language-php">public function handle(Request $request, Closure $next)
{
//    return $next($request);
    $response = $next($request);
    return $response;
}</code></pre>
<h3 id="1-response변수에-할당-후-반환">1. $response변수에 할당 후 반환.</h3>
<pre><code class="language-php">// ExampleMiddleware.php

\Log::Debug(&#39;Middleware init&#39;); // 1
\Log::Debug(&#39;Middleware before allocate response&#39;); // 2
$response = $next($request);
\Log::Debug(&#39;Middleware after allocate response&#39;); // 4
return $response;</code></pre>
<pre><code class="language-php">// ExampleController.php

public function login($request): array
{
    \Log::Debug(&#39;in Controller&#39;); //3
}</code></pre>
<p><img src="https://velog.velcdn.com/images/i_thank/post/0fd514b7-d139-4c69-9300-b31850e49806/image.png" alt=""></p>
<h3 id="2-그냥-바로-request-반환">2. 그냥 바로 $request 반환.</h3>
<pre><code class="language-php">// ExampleMiddleware.php

\Log::Debug(&#39;Middleware init&#39;); // 1
\Log::Debug(&#39;Middleware before allocate response&#39;); // 2
\Log::Debug(&#39;Middleware after allocate response&#39;); // 3
return $next($request);</code></pre>
<pre><code class="language-php">// ExampleController.php

public function login($request): array
{
    \Log::Debug(&#39;in Controller&#39;); //4
}</code></pre>
<p><img src="https://velog.velcdn.com/images/i_thank/post/2d0d7037-481b-4312-a76b-42981883746f/image.png" alt=""></p>
<p>매일 삽질만 하는 줄 알았는데, 개인적으로 신기한 걸 발견해서 기분이 좋았습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ACL(laravel-permission)]]></title>
            <link>https://velog.io/@i_thank/ACLlaravel-permission</link>
            <guid>https://velog.io/@i_thank/ACLlaravel-permission</guid>
            <pubDate>Mon, 27 Jun 2022 01:06:26 GMT</pubDate>
            <description><![CDATA[<p><a href="https://spatie.be/docs/laravel-permission/v5/introduction">https://spatie.be/docs/laravel-permission/v5/introduction</a>
<code>ACL</code>를 위한 <code>laravel-permission</code>을 정리한 글입니다.
실제로 <code>role</code>과 <code>permission</code>을 할당하는 코드는 배제하고 테이블의 값들을 통해 어떻게 동작하는지 정리했습니다.</p>
<h2 id="설계">설계</h2>
<h3 id="1-첫번째-acl-분류">1. 첫번째 ACL 분류</h3>
<p><img src="https://velog.velcdn.com/images/i_thank/post/5e44a1fa-6d83-4b65-9f5d-27c590bbf4d1/image.png" alt=""></p>
<ol>
<li><code>인사과</code>은 <code>/user</code>에만 접근할수 있고,</li>
<li><code>총무과</code>는 <code>/system</code>,</li>
<li><code>마케팅과</code>는 <code>/product</code>에만 접속할 수 있습니다.</li>
</ol>
<h3 id="2-두번째-acl-세분화-분류">2. 두번째 ACL 세분화 분류</h3>
<p>역할(<code>role</code>)과 접근권한(<code>permission</code>)으로 좀 더 세분화를 해보겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/i_thank/post/37332664-a81a-46c3-a240-454731fafc79/image.png" alt=""></p>
<p>각 부서는 3가지의 권한이 있는데</p>
<ol>
<li><code>leader</code>는 <code>CRUD</code>,</li>
<li><code>member</code>는 <code>CR</code>,</li>
<li><code>intern</code>은 <code>R</code>만 가능합니다.</li>
</ol>
<p>즉, 유저가 <strong>권한이 없을 때는</strong>, <strong>해당 접근이 불가능</strong>하게 해야 합니다.</p>
<h3 id="3-table-관계도">3. table 관계도</h3>
<p><img src="https://velog.velcdn.com/images/i_thank/post/efa4714b-4e94-46ec-9b7e-c3be82ad3e77/image.png" alt=""></p>
<h4 id="31-role-permission">3.1 role, permission</h4>
<p><code>leader, member, intern</code>은 <code>role</code>이고, <code>CRUD</code>에 관한건 <code>permission</code>입니다.</p>
<p>1개의 <code>role</code>은 다수의 <code>permission</code>을 가질 수 있고, 1개의 <code>permission</code>또한 다수의 <code>role</code>을 가질 수 있기 때문에 
중간에 <code>role_has_permission</code> 테이블을 놓아서 <code>다대다 관계</code>를 형성하고 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/i_thank/post/0f048a62-7288-49f2-b455-2ccd355b40f7/image.png" alt=""></p>
<h4 id="32-model-has-roles">3.2 model has roles</h4>
<p>각 유저가 어떤 역할을 가지고 있는지는 <code>model_has_roles table</code>로 관리합니다.
이러면 기존<code>users table</code>에 <code>column</code>을 추가할 필요없이(기존 테이블 수정없이) 적용이 가능합니다.</p>
<p><img src="https://velog.velcdn.com/images/i_thank/post/db06b120-34ca-4445-95d1-f6f9a86fd97c/image.png" alt=""></p>
<h2 id="laravel-pemission-설정">laravel-pemission 설정</h2>
<h3 id="1-설치">1. 설치</h3>
<p><code>composer require spatie/laravel-permission</code></p>
<h3 id="2-프로바이더-설정">2. 프로바이더 설정</h3>
<p>선택사항입니다. 아래 providers 배열에 추가하지 않아도 자동으로 연결되지만,</p>
<p>현재 설치되어있는 패키지들을 명시적으로 표현해주고 싶어서 작성했습니다.</p>
<pre><code class="language-php">// config/app.php
&#39;providers&#39; =&gt; [
    // ...
    Spatie\Permission\PermissionServiceProvider::class,
];</code></pre>
<h3 id="3-마이그레이션-파일-가져오기">3. 마이그레이션 파일 가져오기</h3>
<p>새로운 테이블들을 생성하기 위한 마이그레이션 파일들을 로컬로 가져옵니다.</p>
<p><code>php artisan vendor:publish --provider=&quot;Spatie\Permission\PermissionServiceProvider&quot;</code></p>
<p>그러면 <code>config/permission.php</code>파일을 볼 수 있는데, 여기서 각 테이블에 대한 설정을 변경할 수 있습니다.</p>
<h3 id="4-laravel-permission-테이블-생성">4. laravel-permission 테이블 생성</h3>
<p><code>php artisan migrate</code>
<code>config/permission.php</code>를 토대로 <code>table</code>들이 생성됩니다.</p>
<ol>
<li>roles</li>
<li>permissions</li>
<li>role_has_permissions</li>
<li>model_has_roles</li>
<li>model_has_permissions(여기에선 다루지 않습니다.)</li>
</ol>
<h2 id="3-코드-작성">3. 코드 작성</h2>
<p><img src="https://velog.velcdn.com/images/i_thank/post/1bd3760e-c051-4ecc-b711-46c1aa303814/image.png" alt=""></p>
<p>각 컨트롤러에 <code>CRUD</code>관련 <code>function</code>이 정의되어 있습니다.</p>
<p>각각의 <code>role</code>에 따라 <code>function</code>들의 접근을 막아야 합니다.</p>
<h3 id="3-1-laravel-permission-table-값들">3-1. laravel-permission table 값들</h3>
<p><img src="https://velog.velcdn.com/images/i_thank/post/1399d1f2-7082-4c38-88ee-36ca95404ff3/image.png" alt="">
현재 <code>laravel-permission</code>관련 <code>table</code>들의 값들입니다.</p>
<ol>
<li><code>roles</code>테이블에는 각 부서당 3가지의 <code>role</code>이 있고, 총 9개의 <code>role</code>이 있습니다.</li>
<li><code>permissions</code> 테이블에는 현재 정의되어있는 기능들에 대한 <code>permission</code>이 있습니다.</li>
<li><code>role_has_permissions</code>테이블에는 각 <code>roles</code>와 <code>permissions</code>를 연결해 주는 값들이 있습니다.</li>
<li><code>model_has_roles</code>테이블에는 각 <code>user</code>가 가지고 있는 <code>role</code>을 정해주고 있습니다.</li>
</ol>
<h3 id="3-2-구현">3-2 구현</h3>
<p><code>인사과</code>와 관련있는 1개만 예를 들겠습니다.(나머지 과도 다 똑같습니다.)</p>
<p><code>web.php</code>에서 각 <code>route</code>에 <code>middleware</code>를 적용할 수도 있지만,</p>
<p>만약 기존 <code>route</code>가 <code>resource</code>로 되어있을 경우, 변경하기가 힘들어서</p>
<p><code>Controller</code>내부의 <code>constructor</code>에서 <code>middleware</code>를 적용하였습니다.</p>
<pre><code class="language-php">class UserController extends Controller
{
    public function __construct()
    {
        $this-&gt;middleware(&#39;permission:/user r,web&#39;, [&#39;only&#39; =&gt; [&#39;index&#39;]]);
        $this-&gt;middleware(&#39;permission:/user c,web&#39;, [&#39;only&#39; =&gt; [&#39;store&#39;]]);
        $this-&gt;middleware(&#39;permission:/user u,web&#39;, [&#39;only&#39; =&gt; [&#39;update&#39;]]);
        $this-&gt;middleware(&#39;permission:/user d,web&#39;, [&#39;only&#39; =&gt; [&#39;destroy&#39;]]);
    }</code></pre>
<p>각 <code>role</code>에 대한 <code>middleware</code>를 지정한 것이 아닌, <code>permission</code>에 대한 <code>middlware</code>를 지정한 것이 보입니다.</p>
<p><code>permission</code>이 아닌 <code>role</code>에 집중을 하게 되면, 접근제한을 원활하게 할 수 없게됩니다. 위 공식문서에서도 <code>permission</code>으로 접근제한을 두라 말하고 있습니다.</p>
<p><a href="https://spatie.be/docs/laravel-permission/v5/best-practices/roles-vs-permissions">https://spatie.be/docs/laravel-permission/v5/best-practices/roles-vs-permissions</a>
<img src="https://velog.velcdn.com/images/i_thank/post/30303e4f-8428-4cde-b9b6-2c69dc29149a/image.png" alt=""></p>
<p><code>permission middleware</code>를 사용하기 위해 <code>Kernel.php</code>의 <code>$routeMiddleware</code>에 <code>permission</code>을 추가합니다.</p>
<pre><code class="language-php">protected $routeMiddleware = [
        ...
        &#39;permission&#39; =&gt; \Spatie\Permission\Middlewares\PermissionMiddleware::class,
    ];</code></pre>
<h3 id="3-3-테스트-코드-작성">3-3 테스트 코드 작성</h3>
<p>auth가 구현되어 있지 않은 관계로 테스트코드를 작성하여 테스트했습니다.</p>
<pre><code class="language-php">&lt;?php
namespace Tests\Feature;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

class PermissionTest extends TestCase
{
    public function masterDataProvider() {
        return array(
            array(&quot;personal_master&quot;,&quot;/user&quot;),
            array(&quot;marketing_master&quot;,&quot;/product&quot;),
            array(&quot;affairs_master&quot;,&quot;/system&quot;),
        );
    }

    public function memberDataProvider() {
        return array(
            array(&quot;personal_member&quot;,&quot;/user&quot;),
            array(&quot;marketing_member&quot;,&quot;/product&quot;),
            array(&quot;affairs_member&quot;,&quot;/system&quot;),
        );
    }

    public function internDataProvider() {
        return array(
            array(&quot;personal_intern&quot;,&quot;/user&quot;),
            array(&quot;marketing_intern&quot;,&quot;/product&quot;),
            array(&quot;affairs_intern&quot;,&quot;/system&quot;),
        );
    }

    private function getUser($role)
    {
        $user = User::whereHas(&#39;roles&#39;, function($query) use($role){
            return $query-&gt;where(&#39;name&#39;, $role);
        })-&gt;with(&#39;roles&#39;)-&gt;first();
        $this-&gt;actingAs($user);
    }


    /**
     * @dataProvider masterDataProvider
     */
    public function test_role_master($role, $route)
    {
        $this-&gt;getUser($role);

        $this-&gt;get($route)-&gt;assertOk();
        $this-&gt;post($route)-&gt;assertOk();
        $this-&gt;patch($route.&#39;/1&#39;)-&gt;assertOk();
        $this-&gt;delete($route.&#39;/1&#39;)-&gt;assertOk();
    }

    /**
     * @dataProvider memberDataProvider
     */
    public function test_role_member($role, $route)
    {
        $this-&gt;getUser($role);

        $this-&gt;get($route)-&gt;assertOk();
        $this-&gt;post($route)-&gt;assertOk();
        $this-&gt;patch($route.&#39;/1&#39;)-&gt;assertStatus(403);
        $this-&gt;delete($route.&#39;/1&#39;)-&gt;assertStatus(403);
    }

    /**
     * @dataProvider internDataProvider
     */
    public function test_role_intern($role, $route)
    {
        $this-&gt;getUser($role);

        $this-&gt;get($route)-&gt;assertOk();
        $this-&gt;post($route)-&gt;assertStatus(403);
        $this-&gt;patch($route.&#39;/1&#39;)-&gt;assertStatus(403);
        $this-&gt;delete($route.&#39;/1&#39;)-&gt;assertStatus(403);
    }

}</code></pre>
<h2 id="4-소스코드">4. 소스코드</h2>
<p><a href="https://github.com/kaelch/laravel8.git">https://github.com/kaelch/laravel8.git</a></p>
<ol>
<li><code>php artisan db:seed</code>
테이블 시딩</li>
<li><code>php artisan test</code>
테스트</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[라라벨 특정 마이그레이션 롤백]]></title>
            <link>https://velog.io/@i_thank/%EB%9D%BC%EB%9D%BC%EB%B2%A8-%ED%8A%B9%EC%A0%95-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%A1%A4%EB%B0%B1</link>
            <guid>https://velog.io/@i_thank/%EB%9D%BC%EB%9D%BC%EB%B2%A8-%ED%8A%B9%EC%A0%95-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%A1%A4%EB%B0%B1</guid>
            <pubDate>Thu, 23 Jun 2022 02:07:15 GMT</pubDate>
            <description><![CDATA[<h4 id="1-마이그레이션-파일을-엽니다">1. 마이그레이션 파일을 엽니다.<img src="https://velog.velcdn.com/images/i_thank/post/d45842d3-0218-4233-ba5d-5aa74738f1d2/image.png" alt=""></h4>
<p>처음에 했던 <code>id: 75</code>인 <code>2014_10_12_000000_create_users_table</code>을 롤백하려 합니다.</p>
<h4 id="2-batch-컬럼을-수정해줍니다">2. <code>batch</code> 컬럼을 수정해줍니다.<img src="https://velog.velcdn.com/images/i_thank/post/41cbf26a-88ca-4f69-b5cf-629c2ae215b2/image.png" alt=""></h4>
<p><code>batch</code>값을 가장큰 <code>3</code>으로 변경해 주었습니다.</p>
<h4 id="3-php-artisan-migraterollback---step1-으로-롤백-해줍니다">3. <code>php artisan migrate:rollback --step=1</code> 으로 롤백 해줍니다.<img src="https://velog.velcdn.com/images/i_thank/post/ebf0befc-c7c1-4f8b-97ff-b58ba593f9f2/image.png" alt=""></h4>
<p><del>잘되는군요.</del></p>
<h4 id="4-php-artisan-migrate-으로-변경하고자-했던-테이블을-마이그레이션-합니다">4. <code>php artisan migrate</code> 으로 변경하고자 했던 테이블을 마이그레이션 합니다.<img src="https://velog.velcdn.com/images/i_thank/post/fd416d83-365c-40f0-ada8-d6b951a54899/image.png" alt=""></h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Context API with Hook]]></title>
            <link>https://velog.io/@i_thank/React-Context-API</link>
            <guid>https://velog.io/@i_thank/React-Context-API</guid>
            <pubDate>Thu, 24 Oct 2019 23:42:04 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기-전에">들어가기 전에</h2>
<h3 id="사전-요구사항">사전 요구사항</h3>
<ul>
<li><strong>React 기본</strong></li>
<li><strong>Hooks</strong></li>
</ul>
<h3 id="context-api의-이유">Context API의 이유</h3>
<ul>
<li><strong>Context API는 <code>property</code> 들을 매우 자식(손자, 증손자급)에게 전달할때 <code>props</code> 로 전달하는 번거로움을 제거할 수 있습니다.</strong></li>
</ul>
<h3 id="목표">목표</h3>
<ul>
<li><strong>Context API 사용하기 전과 후를 살펴보고 이걸 왜 써야하는지 몸과 마음으로 느끼기</strong></li>
</ul>
<hr>
<h4 id="--context-api를-사용하기-전-props를-전달-예시">- Context API를 사용하기 전 props를 전달 예시</h4>
<ul>
<li>App<ul>
<li>Header<ul>
<li>Nav</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="--구조는-이렇습니다">- 구조는 이렇습니다.</h4>
<p><strong>가장 하위인 <code>&lt;Nav&gt;</code> 에서 &quot;Draven&quot;을 출력하는데 이 값은 할아버지격 인 <code>&lt;App&gt;</code>에서 가져옵니다.
 하지만 <code>&lt;Nav&gt;</code>는 <code>&lt;Header&gt;</code>하위에 있으므로, <code>&lt;Header&gt;</code> 에도 <code>props</code>를 받아와야 합니다.
 즉, 쓸데없는 <code>props={props}</code> 때문에 코드가 아주 지저분 해집니다.</strong></p>
<pre><code class="language-javascript"># src/App.js
const App = () =&gt; {
  const champion = {
    name: &quot;Draven&quot;,
    characteristic: &quot;dirty&quot;
  };

  return (
    &lt;&gt;
      &lt;Header user={champion} /&gt;
    &lt;/&gt;
  );
};</code></pre>
<pre><code class="language-javascript"># src/Header.js
src/Header.js
const Header = ({ user }) =&gt; {
  return (
    &lt;&gt;
      &lt;Nav user={user} /&gt;
    &lt;/&gt;
  );
};</code></pre>
<pre><code class="language-javascript"># src/Nav.js
const Nav = ({ user }) =&gt; {
  return &lt;&gt;{user.name}&lt;/&gt;;
};
</code></pre>
<p><code>결과 = Draven</code></p>
<p><strong>즉, 손자인 <code>Nav</code>에서 할아버지의 <code>property</code>를 가져오려면 엄마의 <code>props</code>로 가져오는 와야합니다. 근대 만약 97개라면? 네 97대의 자손 모두에게 <code>props</code>를 가져와야 겠죠. 그럼 96번의 <code>user={user}</code>를...</strong> </p>
<h3 id="근데-context-api를-통해-props를-여러번-가져오지-않고도-property들을-가져-올-수-있습니다">근데 Context API를 통해 <code>props</code>를 여러번 가져오지 않고도 <code>property</code>들을 가져 올 수 있습니다.</h3>
<hr>
<h2 id="문제해결">문제해결</h2>
<p><img src="https://images.velog.io/post-images/i_thank/502d5590-f647-11e9-a480-2b0ca0c22d62/image.png" alt="image.png"></p>
<p>  <strong>inc를 누르면 증가하고 dec를 누르면 감소합니다.
    <a href="https://codesandbox.io/s/contextapihooks-mumli">https://codesandbox.io/s/contextapihooks-mumli</a>(결과확인)
  컴포넌트 구조는 Context API와 관련 된 것을 제외하고 아래와 같습니다.</strong></p>
<ul>
<li>App<ul>
<li>Red<ul>
<li>Blue<ul>
<li>Green</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>기존대로 라면 Blue영역과 Green영역에 사용할 함수와 데이터를 props로 넘겨줘야 합니다.</strong></p>
<pre><code>&lt;App&gt;
    &lt;Red&gt;
        &lt;Blue buttonClick={buttonClick}&gt;
            &lt;Green data={data}&gt;&lt;/Green&gt;
        &lt;/Blue&gt;
    &lt;/Red&gt;
&lt;/App&gt;
&lt;!--물론 코딩은 저렇게 하지않고 각 컴포넌트에서 하위 컴포넌트를 호출 했습니다.
(위 링크에서 확인하시면 좋습니다.)--&gt;</code></pre><ul>
<li><strong>디렉터리 구조</strong></li>
</ul>
<p><img src="https://images.velog.io/post-images/i_thank/56e067e0-f64e-11e9-8f01-3d50d1d0b1ce/image.png" alt="image.png"></p>
<ul>
<li><p><strong>src/context : state를 저장하는 <Provider>컴포넌트를 만들기위해 <Provider>의 상위인 createContext를 만들 파일입니다</strong></p>
</li>
<li><p><strong>src/components/ColorProvider : 위에서 만든 createContext를 호출하여 create.Provider를 만들고 state와 setState를 정의합니다</strong></p>
</li>
<li><p><strong>src/components/그외 것들 : View 컴포넌트들입니다.</strong></p>
</li>
</ul>
<h2 id="1-createcontext만들기">1. createContext만들기</h2>
<p><strong>/src/context/Color.context.js</strong></p>
<h4 id="about-colorcontextjs">About Color.context.js</h4>
<ul>
<li><h4 id="store의-저장소인-provider-즉-contextprovider를-만들기-위해-context를-만듭니다">store의 저장소인 Provider, 즉 <code>&lt;Context.Provider&gt;</code>를 만들기 위해 Context를 만듭니다.</h4>
</li>
</ul>
<pre><code class="language-javascript">//createContext를 import 해줍니다.
import { createContext } from &quot;react&quot;;

//직접 우리가 
const ColorContext = createContext({
//사실 여기 안에있는 데이터는 삭제해도 됩니다.
//왜냐면 ColorContext.Provider에서 state를 관리 할 거라서요.
//하지만 관리할 state가 무엇이 있는지 확인하기 좋습니다.
//그러기에 지우지 않는것을 추천합니다.

//우리가 관리할 state입니다.
  number: 0,
  increase: () =&gt; {},
  decrease: () =&gt; {}
});

export default ColorContext;
</code></pre>
<h2 id="2-provider만들기">2. Provider만들기</h2>
<h4 id="state를-모아둔-것을--providerstore라-생각하면-됩니다">state를 모아둔 것을  Provider(store)라 생각하면 됩니다.</h4>
<p><strong>/src/component/ColorProvider.component.js</strong></p>
<h4 id="about-colorprovidercomponentjs">About &quot;ColorProvider.component.js&quot;</h4>
<ul>
<li><h4 id="export-되는-colorprovider가-하는일"><code>export</code> 되는 <code>&lt;ColorProvider&gt;</code>가 하는일</h4>
<strong>1. 하위에서 사용 할 <code>state</code>와 <code>setState</code>를 정의합니다.</strong>
<strong>2. <code>state</code>를 관리할 <code>store</code>를 만들기 위해 <code>creactContext</code> 를 받아와서 <code>ColorContext.Provider</code> 를 <code>return</code> 합니다. .</strong></li>
</ul>
<pre><code class="language-javascript">import React, { useState } from &quot;react&quot;;
import ColorContext from &quot;../../context/Color.context&quot;;

//이 페이지의 특징은 함수같은 것들을 먼저 정의하고 
//useState를 통한 state 및 setState를 나중에 했다는 것입니다.
//여기서 Hook에 관한 이해가 필요합니다.
//생각보다 바로 이해하기 어려우니 심사숙고하며 보는걸 추천합니다.

//사용하고자 하는 컴포넌트 최상위에 지정할 Provider컴포넌트 입니다.  
const ColorProvider = ({ children }) =&gt; {

//우리가 Blue에서 사용할 함수입니다.
  const increase = () =&gt; {
//그 함수안에서 Hooks의 setNumber()를 사용 했습니다.
//prevState를 받아서 return을 통해 state를 업데이트 합니다.  
    setNumber(prevState =&gt; {
      return {
        ...prevState,
        number: prevState.number + 1
      };
    });
  };

  const decrease = () =&gt; {
    setNumber(prevState =&gt; {
      return {
        ...prevState,
        number: prevState.number - 1
      };
    });
  };

//state초기화 객체 입니다.
  const initialState = {
    number: 0,
    increase,
    decrease
  };
//Hook을 통한 state, setState를 정의합니다.
  const [number, setNumber] = useState(initialState);

  return (  
//ColorProvider에 state를 사용할 컴포넌트들을 호출하려면
//{children}이 있어야 합니다
//그래서 마지막 return에서 {children}을 리턴해줍니다.
    &lt;ColorContext.Provider value={number}&gt;
      {children}
      &lt;/ColorContext.Provider&gt;
  );
};

export default ColorProvider;
</code></pre>
<h2 id="3-하위-컴포넌트에서-usecontext를-통해-provider의-state사용하기">3. 하위 컴포넌트에서 useContext()를 통해 Provider의 state사용하기.</h2>
<ol>
<li><strong>src/components/App.js</strong></li>
</ol>
<pre><code class="language-javascript">import React from &quot;react&quot;;
//state를 사용하기 위해 앞에서 만든 Provider를 가져옵니다.  
import ColorProvider from &quot;./ColorProvider/ColorProvider.component&quot;;
import Red from &quot;./Red&quot;;
const App = () =&gt; {
  return (
//사용하고자 하는 자식들 컴포넌트들 가장 밖에서 &lt;Provider&gt;로 감싸줍니다.
    &lt;ColorProvider&gt;
      &lt;Red /&gt;
    &lt;/ColorProvider&gt;
  );
};
export default App;</code></pre>
<ol start="2">
<li>src/components.Red.js </li>
</ol>
<ul>
<li><strong><code>return</code>으로 <code>&lt;Blue&gt;</code>컴포넌트를 호출한게 다 입니다.</strong>  </li>
</ul>
<pre><code class="language-javascript">import React from &quot;react&quot;;
import Blue from &quot;./Blue&quot;;
import &quot;./App.css&quot;;

const Red = () =&gt; {
  return (
    &lt;div className=&quot;red&quot;&gt;
      &lt;Blue&gt;Blue&lt;/Blue&gt;
    &lt;/div&gt;
  );
};

export default Red;
</code></pre>
<ol start="3">
<li>src/components/Blue.js</li>
</ol>
<ul>
<li><strong>여기서 increase와 decrease를 사용하기 위해 가장 상위에서 state의 함수를 가져옵니다.</strong>  </li>
</ul>
<pre><code class="language-javascript">//저장소를 가져오기위해 useContext를 import합니다.  
import React, { useContext } from &quot;react&quot;;
import ColorContext from &quot;../context/Color.context&quot;;
import Green from &quot;./Green&quot;;
import &quot;./App.css&quot;;

const Blue = () =&gt; {
//원하는 함수를 useContext(Context)로 불러옵니다.  
  const { increase, decrease } = useContext(ColorContext);
  return (
    &lt;div className=&quot;blue&quot;&gt;
      //그리고 아래와 같이 사용하면 됩니다.
      &lt;button className=&quot;increment&quot; onClick={increase}&gt;
        inc
      &lt;/button&gt;
      &lt;button className=&quot;decrement&quot; onClick={() =&gt; decrease()}&gt;
        dec
      &lt;/button&gt;
      &lt;Green&gt;green&lt;/Green&gt;
    &lt;/div&gt;
  );
};

export default Blue;
</code></pre>
<ol start="4">
<li><strong>src/components/Green.js</strong></li>
</ol>
<pre><code class="language-javascript">//마찬가지로 useContext를 import합니다.  
import React, { useContext } from &quot;react&quot;;
import ColorContext from &quot;../context/Color.context&quot;;
import &quot;./App.css&quot;;
const Green = () =&gt; {
//useContext(Context)를 사용하여 사용할 준비를 합니다.  
  const value = useContext(ColorContext);
  return (
    &lt;div className=&quot;green&quot;&gt;
      //정의했던 state.변수명을 사용해 주면 됩니다.
      &lt;span className=&quot;number&quot;&gt;{value.number}&lt;/span&gt;
    &lt;/div&gt;
  );
};
export default Green;
</code></pre>
<hr>
<h2 id="정리">정리</h2>
<ol>
<li><p>React Context API는 <code>props</code>로 여러차례 전달 해야하는 번거러옴을 줄일수 있습니다.</p>
</li>
<li><p>그러면 결국 유지보수에 큰 이점이 됩니다.</p>
</li>
<li><p>그렇다는건 정신건강에 또한 큰 이점이 된다는 것입니다.   </p>
</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>