<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>public_danuel.log</title>
        <link>https://velog.io/</link>
        <description>다뉴하는 코딩</description>
        <lastBuildDate>Fri, 11 Dec 2020 16:27:06 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>public_danuel.log</title>
            <url>https://images.velog.io/profiles/public_danuel/thumbnails/1554562714.032.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. public_danuel.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/public_danuel" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[SQL JOIN은 벤 다이어그램? 이제는 그만!]]></title>
            <link>https://velog.io/@public_danuel/sql-join-is-not-venn-diagram</link>
            <guid>https://velog.io/@public_danuel/sql-join-is-not-venn-diagram</guid>
            <pubDate>Fri, 11 Dec 2020 16:27:06 GMT</pubDate>
            <description><![CDATA[<h1 id="간단요약">간단요약</h1>
<ul>
<li>벤 다이어그램을 통해 SQL JOIN을 설명하는 것은 대체적으로 정확하지 않다.</li>
<li>SQL JOIN은 연산 전후의 연결관계가 드러나게 설명하는 것이 정확하다.</li>
</ul>
<h1 id="면책조항">면책조항</h1>
<p>우선! 자극적으로 제목을 작성한 것에 양해를 구합니다.
이 포스트는 그저 제 의견이며 무조건 옳다는 것은 아니라는 점을 밝힙니다.
(레퍼런스는 포스트 마지막 부분에 있습니다.)</p>
<p>본 포스트를 읽고 이해한 이후 &quot;SQL JOIN을 벤 다이어그램으로 설명&quot; 하는 것을 보면 어디인지 모르게 불편한 마음이 드는 현상에 대해 책임지지 않습니다!</p>
<h1 id="sql-join은-벤-다이어그램">SQL JOIN은 벤 다이어그램?</h1>
<p><img src="https://images.velog.io/images/public_danuel/post/37e29df6-b3e1-434e-8308-6266157e0c2c/image.png" alt="구글에 SQL JOIN을 검색한 결과; 벤 다이어그램 이미지가 잔뜩 보인다"></p>
<p>SQL JOIN을 설명할 때에는 벤 다이어그램을 자주 사용합니다.
구글에 검색을 해보아도, 책에서도, 온라인 포스트에서도 벤 다이어그램을 통해 설명합니다.
심지어는 누군가에게 배우거나 누군가를 가르칠 때에도 벤 다이어그램의 도움을 받기도 합니다.
SQL JOIN 외에도 벤 다이어그램을 통해 설명하는 방식은 널리 쓰이다 보니 누구에게나 굉장히 익숙하지 않은가 하는 생각도 듭니다.</p>
<p>그래서... &quot;모두&quot; 가 그렇게 사용하고 있으니 정확한 방식으로 표현하는 것일까요?
그것을 뒷받침하듯이 구글 검색 결과도 이렇게나 벤 다이어그램이 많이 나오니까 말이에요.</p>
<p>결론적으로, SQL JOIN을 벤 다이어그램으로 설명하는 것은 대체적으로 정확한 방식이 아닙니다.</p>
<h1 id="벤-다이어그램">벤 다이어그램</h1>
<p>SQL JOIN을 설명할 때에 벤 다이어그램을 사용하는 것이 정확하지 않은 이유를 이해하기 위해서는 벤 다이어그램이 어떤 개념인지 살펴 볼 필요가 있습니다.
(아래의 인용 내용은 그 아래에서 추가적으로 설명하니 스윽 훑고 내리셔도 괜찮습니다.)</p>
<blockquote>
<p>벤 다이어그램(Venn diagram)은 서로 다른 집합들 사이의 관계를 표현하는 다이어그램이다.</p>
<p><a href="https://ko.wikipedia.org/wiki/%EB%B2%A4_%EB%8B%A4%EC%9D%B4%EC%96%B4%EA%B7%B8%EB%9E%A8">Wikipedia 벤 다이어그램</a></p>
</blockquote>
<blockquote>
<p><img src="https://images.velog.io/images/public_danuel/post/018d427e-e9ec-41ad-8f1b-97d1b8a6a561/image.png" alt="벤 다이어그램 이미지; 원 2개가 일부 겹친 형태이며, 왼쪽 원에는 A가, 오른쪽 원에는 B가 쓰여 있다"></p>
<p>주황색 원(집합 A)은 다리가 두 개인 모든 생물들, 푸른 원(집합 B)은 날 수 있는 모든 생물들을 나타낸다. 각종 생물은 곧 그림의 어느 곳에 위치한 점과 같다. 예를 들어 비둘기처럼 두 다리를 가진 날 수 있는 생물은, 두 원이 겹치는 부분에 위치하는 점이다.</p>
<p>인간과 펭귄은 다리가 두 개여서 주황색 원에 위치하나, 날지는 못하므로 푸른 원과 겹치지 않은 곳에 위치한다. 날 수 있지만 다리가 여섯 개인 모기는, 푸른 원 중 주황색 원과 겹치지 않은 부분에 있다. 고래나 거미처럼, 두 다리를 갖지도 않고 날지도 못하는 생물은 두 원의 바깥에 있을 것이다.</p>
<p>A와 B를 합친 영역을 A와 B의 합집합이라고 한다. 이는 곧 두 다리를 가졌거나, 날 수 있거나, 혹은 양쪽 다에 해당하는 생물들을 모은 것이다.</p>
<p>A와 B가 겹쳐진 영역을 A와 B의 교집합이라고 한다. 이는 곧 두 다리와 날 수 있는 능력을 동시에 갖춘 생물들을 모은 것이다.</p>
<p><a href="https://ko.wikipedia.org/wiki/%EB%B2%A4_%EB%8B%A4%EC%9D%B4%EC%96%B4%EA%B7%B8%EB%9E%A8#.EC.98.88">Wikipedia 벤 다이어그램 - 예시</a></p>
</blockquote>
<p>널리 쓰이고 나름대로의 레퍼런스를 요구하는 위키피디아에서의 정의와 예시입니다.
설명하라고 하면 막상 명확하게 설명하지는 못 하더라도 머릿속에서는 한창 떠올리고 있는 그것입니다.</p>
<p>이 벤 다이어그램은 집합과 집합간의 관계를 표현하기 위한 개념입니다.
다리가 2개인 생물과 날 수 있는 생물에서의 대표적인 교집합은 &quot;다리가 2개이고 날 수 있는 생물&quot; 일 것입니다.
중요하니 한 번 더 표현하자면 &quot;다리가 2개이고 날 수 있는 생물&quot; 입니다.
다리가 2개인 생물과 날 수 있는 생물을 결합(JOIN)한 그 결과가 아니라, 애초부터 이미 &quot;다리가 2개이고 날 수 있는 생물&quot; 이라는 것이 포인트입니다.</p>
<p>어떤 말을 하고 싶은지 눈치챈 분이 혹시 있을까요?
(벌써 눈치 채셨다면 축하드립니다! 설마 INTP인가요!?)</p>
<p>그렇습니다.
벤 다이어그램으로 표현하는 집합 연산 결과는 어느 집합에 있던 것이든 각각의 원소에 어떠한 조작도 가하지 않는다는 것입니다.</p>
<p>아직 이해가 되지 않은 분을 위해 다르게 표현하자면, SQL JOIN은 양쪽의 원소를 결합(JOIN)하는 연산이고, 벤 다이어그램은 결합이 아니라 분류를 하는 연산입니다.
<del>(엄밀한 표현은 아니지만 말이 그렇다는 것입니다.)</del></p>
<p>그래도 이해하기가 아직 어려울 수 있을 것 같습니다.
눈을 감거나 뒤로가기를 하지 말고, 아래 섹션을 마저 살펴 보면 이해를 도울 수 있을 것입니다.</p>
<h1 id="그러면-어떻게-설명해">그러면 어떻게 설명해?</h1>
<p>SQL JOIN을 벤 다이어그램 보다 정확하게 설명하는 것은 의외로 어렵지 않습니다.
그저 결과를 요약해 표현하면 됩니다.</p>
<p>마치 아래 이미지처럼 말이죠.</p>
<p><img src="https://images.velog.io/images/public_danuel/post/b9e7d931-bdc3-4aab-99c2-779b7614eff6/image.png" alt="대표적인 4가지 연산인 INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN이 순서대로 보이는 그림"></p>
<p>(<a href="https://blog.jooq.org/2016/07/05/say-no-to-venn-diagrams-when-explaining-joins/">레퍼런스</a>를 기반으로 직접 다시 그렸음을 밝힙니다.)</p>
<p>대표적인 4가지 JOIN 연산인 INNER, LEFT, RIGHT, FULL 순서대로 보이는 그림입니다.
순서대로, 왼쪽의 숫자 집합과 오른쪽의 알파벳 집합이 연산 정의에 따라 같은 위치의 있는 레코드가 결합(JOIN)해 결과를 나타냅니다.</p>
<p>INNER JOIN으로 보자면, 정확히 같은 위치에 있는 레코드끼리만 결합(JOIN)하는 형태입니다.
나머지 JOIN 연산도 비슷한 원리입니다.</p>
<p>벤 다이어그램과의 차이점은 결합결과까지도 표현한다는 것입니다.</p>
<h1 id="결론">결론</h1>
<p>살펴 보았듯이 SQL JOIN을 설명함에 있어 벤 다이어그램은 적절한 방식이 아닙니다.
언뜻 생각해보면 그래도 SELF JOIN은 사정이 다르지 않겠느냐 할 수 있지만, JOIN은 레코드를 결합하는 연산이니 여전히 적절한 방식이 아닙니다.</p>
<p>이제 여러분은 &quot;SQL JOIN을 벤 다이어그램으로 설명&quot; 하는 것을 보면 어디인지 모르게 불편한 마음이 드는 현상이 발생할 것입니다!
이것을 위해 이 포스트를 작성한... 것은 아니고, 이러한 의견도 있다는 것을 포스트로 작성해보고 싶었습니다.</p>
<p>여기까지 읽어 주셔서 감사합니다.</p>
<h1 id="레퍼런스">레퍼런스</h1>
<ul>
<li><a href="https://blog.jooq.org/2016/07/05/say-no-to-venn-diagrams-when-explaining-joins/">Say NO to Venn Diagrams When Explaining JOINs</a></li>
</ul>
<h1 id="리소스">리소스</h1>
<ul>
<li><a href="https://ko.wikipedia.org/wiki/%EB%B2%A4_%EB%8B%A4%EC%9D%B4%EC%96%B4%EA%B7%B8%EB%9E%A8">Wikipedia 벤 다이어그램</a></li>
<li><a href="http://colorsafe.co/">Color Safe</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift4] JSON Codable - encode편]]></title>
            <link>https://velog.io/@public_danuel/Swift4-JSON-Codable-encode</link>
            <guid>https://velog.io/@public_danuel/Swift4-JSON-Codable-encode</guid>
            <pubDate>Sat, 02 May 2020 07:49:10 GMT</pubDate>
            <description><![CDATA[<p>본 포스트는 Swift4에서 JSON을 Swift스럽게 변환하는 방법을 소개한 후 다양한 경우를 알아 봅니다.
겪어 본 여러 경우를 최대한 모두 작성했습니다.</p>
<p>각 섹션에서 중복적으로 작성한 부분이 있으나, 이는 필요한 부분만 빠르게 찾아 읽을 수 있게 하기 위함입니다.
(입문자/초보자도 처음부터 끝까지 읽기 보다는 <a href="#%EA%B8%B0%EB%B3%B8">기본</a> 섹션을 익힌 후 필요한 부분만 찾아 읽는 것을 권장합니다.)</p>
<p>본 포스트의 코드는 쉬운 이해를 위해 에러 핸들링을 과감히 생략했습니다.
이는 Best Practice가 아니므로 각자의 상황에 맞게 에러핸들링 하는 것을 권장합니다.</p>
<h1 id="용어-정의">용어 정의</h1>
<h2 id="경우-케이스">경우, 케이스</h2>
<p>본 포스트에서는 switch/enum 문법에서만 케이스라고 표현하고 나머지는 경우라고 표현하고 있습니다.</p>
<h2 id="nullable">Nullable</h2>
<p>본 포스트에서는 null 값일 수 있는 경우를 nullable하다고 표현합니다.</p>
<h2 id="optional">Optional</h2>
<p>본 포스트에서는 필드가 없을 수 있는 경우를 optional하다고 표현합니다.</p>
<h1 id="codable">Codable</h1>
<p>JSON을 다룰 수 있는 방법을 Swift4부터 <a href="https://developer.apple.com/documentation/swift/codable/">Codable</a>이라는 Protocol로 공식적으로 지원합니다.</p>
<pre><code class="language-swift">import Foundation</code></pre>
<p>조금 더 정확히, Codable은 Foundation 프레임워크에서 제공하는 Protocol이므로 파일 최상단에서 Foundation을 꼭 import 해줘야 합니다.
(Xcode에서 파일을 생성하면 기본적으로 작성해주지만, 저는 VS Code에서 주로 작성하는 탓에 잠깐 헤맸습니다.)</p>
<pre><code class="language-swift">typealias Codable = Encodable &amp; Decodable</code></pre>
<p>이 Codable은 Protocol 2개로 구성되어 있습니다.
<code>Encodable</code> 은 JSON 포맷의 String으로 변환하는 역할을,
<code>Decodable</code> 은 JSON 포맷의 String에서 Swift에서 다룰 수 있는 값으로 변환하는 역할을 합니다.</p>
<h1 id="encodable">Encodable</h1>
<h2 id="기본">기본</h2>
<pre><code class="language-swift">import Foundation

let data0 = try! JSONEncoder().encode(false)
let result0 = String(data: data0, encoding: .utf8)! // false

let data1 = try! JSONEncoder().encode(123)
let result1 = String(data: data1, encoding: .utf8)! // 123

let data2 = try! JSONEncoder().encode(&quot;Danuel&quot;)
let result2 = String(data: data2, encoding: .utf8)! // &quot;Danuel&quot;</code></pre>
<p>JSONEncoder().encode 메서드는 Encodable을 상속한 타입의 값을 입력으로 받으며, Data 타입을 출력합니다.
이렇게 출력받은 값은 JSON 포맷의 문자열을 byte 형태로 가지고 있으며, 만약 String 값을 원한다면 추가적으로 직접 변환을 해주어야 합니다.</p>
<p>Encodable을 기본적으로 상속하고 있는 타입은 아래와 같습니다.</p>
<ul>
<li>Bool</li>
<li>Int</li>
<li>UInt</li>
<li>Double</li>
<li>Float</li>
<li>String</li>
<li><a href="https://developer.apple.com/documentation/swift/encodable">etc(링크 참조)</a></li>
</ul>
<h2 id="struct-class-enum">Struct, Class, Enum</h2>
<pre><code class="language-swift">import Foundation

struct Post: Codable {
  let id: String
  let title: String
}

let post = Post(id: &quot;1&quot;, title: &quot;first post&quot;)
let data0 = try! JSONEncoder().encode(post)
let result0 = String(data: data0, encoding: .utf8)! // {&quot;id&quot;:&quot;1&quot;,&quot;title&quot;:&quot;first post&quot;}

class Account: Codable {
  let id: String
  let displayName: String

  init (id: String, displayName: String) {
    self.id = id
    self.displayName = displayName
  }
}

let account = Account(id: &quot;1&quot;, displayName: &quot;Danuel&quot;)
let data1 = try! JSONEncoder().encode(account)
let result1 = String(data: data1, encoding: .utf8)! // {&quot;id&quot;:&quot;1&quot;,&quot;displayName&quot;:&quot;Danuel&quot;}

enum BoardType: String, Codable {
  case common
  case notice
}

let boardType: BoardType = .common
let data2 = try! JSONEncoder().encode(boardType)
let result2 = String(data: data2, encoding: .utf8)! // &quot;common&quot;</code></pre>
<p>Encodable을 상속시키는 것만으로 간단히 JSON 포맷으로 변환할 준비가 끝났습니다.</p>
<h1 id="다양한-경우">다양한 경우</h1>
<p>모든 것이 이전의 내용처럼 간결하면 마음이 편안하겠지만 현실은 그렇지 않은 경우가 더 많습니다.
이 섹션에서는 다양한 경우를 소개하고, 이에 대한 해결책은 알아 봅니다.
(입문자/초보자라면 끝까지 정독해 이해하고자 하기 보다는, &quot;이런 경우도 있구나&quot; 하는 정도로 이해하고 넘겨도 괜찮습니다.)</p>
<p>추가적으로, 이 섹션에서는 정의하는 방법만 소개하며 JSON 포맷으로 변환하는 코드는 작성하지 않았습니다. (<code>JSONEncoder().encode(value)</code> 는 대부분 동일하거든요.)</p>
<h2 id="배열-값으로-오는-경우">배열 값으로 오는 경우</h2>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;123&quot;,
  &quot;tags&quot;: [&quot;swift&quot;, &quot;codable&quot;, &quot;json&quot;]
}</code></pre>
<p>위 예시처럼 값이 배열로 오는 경우가 있을 수 있습니다.
이 경우는 아래처럼 Codable을 상속한 타입의 배열로 지정하면 알아서 변환합니다.</p>
<pre><code class="language-swift">import Foundation

struct Post: Codable {
  let id: String
  let tags: [String]
}</code></pre>
<h2 id="null-값일-수-있는-경우">null 값일 수 있는 경우</h2>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;123&quot;,
  &quot;displayName&quot;: &quot;Danuel&quot;,
  &quot;email&quot;: null
}</code></pre>
<p>때로는 위처럼 특정 키의 값이 null로 오는 경우가 있으며, 이를 <a href="#nullable">nullable</a>하다고 표현합니다.</p>
<p>이 경우는 간단히 아래처럼 작성하면 됩니다.</p>
<pre><code class="language-swift">import Foundation

struct Account: Codable {
  let id: String
  let displayName: String
  let email: String?
}</code></pre>
<p>이렇게 타입 끝에 물음표(?)를 추가하면 알아서 null을 nil로 변환합니다.</p>
<h2 id="키가-없을-수-있는-경우">키가 없을 수 있는 경우</h2>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;123&quot;,
  &quot;displayName&quot;: &quot;Danuel&quot;
}</code></pre>
<p>현실에서는 어떠한 이유로 특정 키가 없을 수도 있는 경우가 있으며, 이를 <a href="#optional">optional</a>하다고 표현합니다.</p>
<p>위 예시가 대표적이며, 계정 정보에 email을 등록하지 않은 경우 해당 필드를 포함하지 않는다고 가정하겠습니다.</p>
<pre><code class="language-swift">import Foundation

struct Account: Codable {
  let id: String
  let displayName: String
  let email: String?
}</code></pre>
<p>이렇게 타입 끝에 물음표(?)를 추가하면, email 필드가 없는 경우 알아서 null로 변환합니다.</p>
<h2 id="특정-필드가-없거나-특정-필드의-값이-null이면-기본값을-넣고-싶은-경우">특정 필드가 없거나 특정 필드의 값이 null이면 기본값을 넣고 싶은 경우</h2>
<pre><code class="language-json">// 특정 필드가 없는 경우
{
  &quot;id&quot;: &quot;123&quot;,
  &quot;displayName&quot;: &quot;Danuel&quot;
}

// 특정 필드의 값이 null인 경우
{
  &quot;id&quot;: &quot;123&quot;,
  &quot;displayName&quot;: &quot;Danuel&quot;,
  &quot;email&quot;: null
}</code></pre>
<p>변환작업을 하다 보면 특정 필드가 없거나 특정 필드의 값이 null인 경우 기본값을 할당하고 싶을 수 있습니다.
그런 경우에는 아래처럼 작성하면 알아서 기본값으로 변환합니다.</p>
<pre><code class="language-swift">import Foundation

struct Account: Codable {
  let id: String
  let displayName: String
  let email: String = &quot;unknown@unknown.unknown&quot;
}</code></pre>
<h2 id="키가-서로-다른-경우">키가 서로 다른 경우</h2>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;123&quot;,
  &quot;title&quot;: &quot;first post&quot;,
  &quot;like_count&quot;: 17
}</code></pre>
<pre><code class="language-swift">import Foundation

struct Post: Codable {
  let id: String
  let title: String
  let likeCount: Int
}</code></pre>
<p>포스트의 좋아요 수를 JSON에서는 <code>like_count</code> 로 표시하는데, 코드에서는 <code>likeCount</code> 로 사용하고 있습니다.
이 경우 서로 키가 다르다는 것을 아래와 같이 명시하면 알아서 변환합니다.</p>
<pre><code class="language-swift">import Foundation

struct Post: Codable {
  enum CodingKeys: String, CodingKey {
    case id
    case title
    case likeCount = &quot;like_count&quot;
  }

  let id: String
  let title: String
  let likeCount: Int
}</code></pre>
<p>안심하세요! 갑자기 코드가 확 늘어났다고 겁 먹을 필요 없어요.</p>
<p>키가 서로 다를 뿐이라면 CodingKeys라는 enum을 추가적으로 정의하기만 하면 됩니다.
이 CodingKeys에는 해당 타입의 Property를 모두 명시해 주세요.
(다른 이름으로는 작동하지 않으니 꼭 CodingKeys로 이름을 지어야 합니다.)</p>
<p>어느 버전부터는 이런 코드를 작성하지 않아도 camelCase, snake_case, kebab-case도 알아서 변환한다고 하는데, 일단 저는 확인하지 못 했습니다.</p>
<p><a href="#%EB%8B%A8%EC%88%9C-enum%EC%9D%B4-%EC%95%84%EB%8B%8C-%EA%B2%BD%EC%9A%B0">단순 enum이 아닌 경우</a>는 섹션을 따로 준비했습니다.</p>
<h2 id="직접-정의한-타입으로-지정하고-싶은-경우">직접 정의한 타입으로 지정하고 싶은 경우</h2>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;123&quot;,
  &quot;title&quot;: &quot;first post&quot;,
  &quot;author&quot;: {
    &quot;id&quot;: &quot;456&quot;,
    &quot;displayName&quot;: &quot;Danuel&quot;
  }
}</code></pre>
<pre><code class="language-swift">import Foundation

struct Post: Codable {
  let id: String
  let title: String
  let author: Account
}

struct Account: Codable {
  let id: String
  let displayName: String
}</code></pre>
<p>대표적으로 위 예시처럼, 직접 정의한 타입으로 지정해야 하는 경우가 있을 수 있습니다.
이 경우는 Codable을 상속하도록 정의한 후 타입을 지정해주면 알아서 변환합니다.</p>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;123&quot;,
  &quot;title&quot;: &quot;first post&quot;,
  &quot;kind&quot;: &quot;notice&quot;
}</code></pre>
<pre><code class="language-swift">import Foundation

struct Post: Codable {
  let id: String
  let title: String
  let kind: PostKind
}

enum PostKind: String, Codable {
  case notice
  case common
}</code></pre>
<p>enum도 비슷한 원리로 작성할 수 있습니다.</p>
<p>이 때, enum에서 정의한 케이스 외의 값이 오는 경우는 <a href="#enum%EC%97%90%EC%84%9C-%EC%A0%95%EC%9D%98%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EC%BC%80%EC%9D%B4%EC%8A%A4%EA%B0%80-%EC%9E%88%EB%8A%94-%EA%B2%BD%EC%9A%B0">Enum에서 정의하지 않은 케이스가 있는 경우</a> 에서 알아보겠습니다.</p>
<h2 id="enum의-각-케이스와-json-필드의-값이-다른-경우">Enum의 각 케이스와 JSON 필드의 값이 다른 경우</h2>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;123&quot;,
  &quot;role&quot;: &quot;a&quot;, // admin
  &quot;displayName&quot;: &quot;Danuel&quot;
}</code></pre>
<pre><code class="language-swift">import Foundation

struct Account: Codable {
  let id: String
  let role: AccountRole
  let displayName: String
}

enum AccountRole: String, Codable {
  case admin
  case common
}</code></pre>
<p>enum의 각 케이스와 JSON 필드의 값이 다른 경우가 있을 수 있습니다.
위 예시가 대표적인 경우이며, JSON의 role 필드에 있는 a는 AccountRole enum의 admin 케이스를 의미합니다.
이 경우는 아래처럼 작성할 수 있습니다.</p>
<pre><code class="language-swift">import Foundation

struct Account: Codable {
  let id: String
  let role: AccountRole
  let displayName: String
}

enum AccountRole: String, Codable {
  case admin = &quot;a&quot;
  case common = &quot;c&quot;
}</code></pre>
<h2 id="enum에서-정의하지-않은-케이스가-있는-경우">Enum에서 정의하지 않은 케이스가 있는 경우</h2>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;123&quot;,
  &quot;role&quot;: &quot;unknown role&quot;,
  &quot;displayName&quot;: &quot;Danuel&quot;
}</code></pre>
<pre><code class="language-swift">import Foundation

struct Account: Codable {
  let id: String
  let role: AccountRole
  let displayName: String
}

enum AccountRole: String, Codable {
  case admin
  case common
}</code></pre>
<p>이 경우는 Swift4의 Codable에서 조금 특별합니다. 제가 아는 것이 맞다면, 현실적으로 종종 발생하지만 딱히 이렇다 할만하게 널리 쓰이는 방법이 없거든요.</p>
<p>여기서는 제가 사용하는 방법을 소개하겠습니다.
(읽는 분에 따라 이해하기 어려울 수 있으니 마음의 준비를 해주세요.)</p>
<pre><code class="language-swift">import Foundation

protocol UnknownCaseRepresentable: RawRepresentable, CaseIterable where RawValue: Equatable {
  static var unknownCase: Self { get }
}

extension UnknownCaseRepresentable {
  init (rawValue: RawValue) {
    let value = Self.allCases.first(where: { $0.rawValue == rawValue })
    self = value ?? Self.unknownCase
  }
}

struct Account: Codable {
  let id: String
  let role: AccountRole
  let displayName: String
}

enum AccountRole: String, Codable, UnknownCaseRepresentable {
  static var unknownCase = .unknown

  case admin
  case common
  case unknown
}</code></pre>
<p>UnknownCaseRepresentable이라는 Protocol을 정의한 후 원하는 enum에 상속시키는 구조입니다.
일치하는 값이 없는 경우 unknownCase 값으로 초기화하며, 이를 위해 unknown이라는 케이스를 추가합니다.
(보다 자세한 원리는 Codable의 동작을 이해해야 하므로 생략하겠습니다.)</p>
<p>이렇게 작성하면 해당 enum에서 정의하지 않는 케이스는 unknownCase에 할당한 케이스로 변환합니다.</p>
<h2 id="단순-enum이-아닌-경우">단순 Enum이 아닌 경우</h2>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;123&quot;,
  &quot;title&quot;: &quot;first post&quot;,
  &quot;attachments&quot;: [
    {
      &quot;kind&quot;: &quot;image&quot;,
      &quot;url&quot;: &quot;/images/123.jpg&quot;,
      &quot;size&quot;: &quot;large&quot;
    },
    {
      &quot;kind&quot;: &quot;audio&quot;,
      &quot;url&quot;: &quot;/audios/123.wav&quot;
    }
  ]
}</code></pre>
<pre><code class="language-swift">import Foundation

struct Post: Codable {
  let id: String
  let title: String
  let attachments: [PostAttachment]
}

enum PostAttachment: Codable {
  case image(url: String, size: String)
  case audio(url: String)
}</code></pre>
<p>코드를 작성하다 보면 위와 같은 경우도 종종 있을 수 있습니다.
enum은 enum인데 각 케이스 마다 적절한 값을 들고 있는 경우이죠.</p>
<p>이 경우는 이전과 많이 다르니 마음의 준비를 해주세요.</p>
<pre><code class="language-swift">import Foundation

struct Post: Codable {
  let id: String
  let title: String
  let attachments: [PostAttachment]
}

enum PostAttachment {
  case image(url: String, size: String)
  case audio(url: String)
  case unknown
}

extension PostAttachment: Codable {
  enum CodingKeys: String, CodingKey {
    case kind: String
    case url: String
    case size: String
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let kind = try container.decode(String.self, forKey: .kind)
    switch kind {
      case &quot;image&quot;:
        let url = try container.decode(String.self, forKey: .url)
        let size = try container.decode(String.self, forKey: .size)
        self = .image(url: url, size: size)
      case &quot;audio&quot;:
        let url = try container.decode(String.self, forKey: .url)
        self = .audio(url: url, size: size)
      default:
        self = .unknown
    }
  }

  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    switch self {
      case let .image(url, size):
        try encoder.encode(&quot;image&quot;, forKey: .kind)
        try encoder.encode(url, forKey: .url)
        try encoder.encode(size, forKey: .size)
      case let .audio(url):
        try encoder.encode(&quot;audio&quot;, forKey: .kind)
        try encoder.encode(url, forKey: .url)
      case .unknown:
        try encoder.encode(&quot;unknown&quot;, forKey: .kind)
    }
  }
}</code></pre>
<p>놀라지 마세요! 원리 자체는 간단해요!</p>
<p>찬찬히 살펴 보면 extension 문법을 통해 Codable을 상속하도록 하고 init과 decode 메서드를 구현하는 구조입니다.
init은 decode 로직이고, encode는 encode 로직입니다.</p>
<p>init을 살펴 보면 container를 생성한 후 kind 필드를 꺼내 적절한 타입으로 직접 변환을 합니다.
encode 메서드도 마찬가지로, container를 생성한 후 타입 마다 직접 JSON 포맷으로 변환을 하고 있습니다.</p>
<p>보면 알 수 있지만, 이 경우는 사람이 직접 변환 로직을 작성해줘야 하기 때문에 실수가 발생할 여지가 많으므로 각별한 주의가 필요합니다.
또한, 잘 이해한다면 보다 복잡한 경우도 대응할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Router 제한적 경로 접근]]></title>
            <link>https://velog.io/@public_danuel/Restrict-Route</link>
            <guid>https://velog.io/@public_danuel/Restrict-Route</guid>
            <pubDate>Thu, 26 Mar 2020 17:56:40 GMT</pubDate>
            <description><![CDATA[<p>웹사이트를 작성하다 보면 &#39;로그인 한&#39; 혹은 &#39;관리자만&#39; 혹은 그 외의 제한적으로 접근 가능한 페이지를 구현할 필요가 생깁니다.
여러 시행착오를 겪으며 제가 현재 사용중인 방법을 소개하고자 합니다.</p>
<pre><code class="language-tsx">// RestrictRoute.tsx
import React, { FC } from &quot;react&quot;;

interface Props {
  path: string | string[];
  component: FC;
  fallback: FC;
  exact?: boolean;
  isAllow: (user: User) =&gt; boolean;
}

export const RestrictRoute = ({
  component: Component,
  fallback: Fallback,
  isAllow
}: Props) =&gt; {
  const user = useUser();
  return isAllow(user) ? &lt;Component /&gt; : &lt;Fallback /&gt;;
};</code></pre>
<p><code>RestrictRoute</code> 컴포넌트는 해당 페이지에 접근하면 인증 여부에 따라 컴포넌트이며, 기본 4개의 Props와 1개의 추가적인 Props를 받습니다.</p>
<p><code>path</code> 는 경로를 의미하며 React Router 기반으로 사용하던 경로를 그대로 사용할 수 있습니다.
<code>component</code> 는 인증 성공시의 컴포넌트입니다.
<code>fallback</code> 은 인증 실패시의 컴포넌트입니다.
<code>exact</code> 는 정확히 일치하는 경로일 때에만 컴포넌트를 렌더링 할지 결정합니다.
<code>isAllow</code> 함수는 인증 여부를 판정하는 역할을 합니다.</p>
<p>useUser는 예시일 뿐이니, User 뿐만 아니라 더 많은 데이터를 가진 무엇인가를 isAllow 함수의 파라미터로 전달해도 괜찮습니다.</p>
<p>이 컴포넌트에는 path와 exact를 사용하는 부분이 없는데, 이 부분은 잠시 후 소개하겠습니다.</p>
<pre><code class="language-tsx">// App.tsx
import React from &quot;react&quot;;
import { BrowserRouter, Switch, Route, Redirect } from &quot;react-router-dom&quot;;
import { RestrictRoute } from &quot;./RestrictRoute&quot;;

const GoToMainPage = () =&gt; &lt;Redirect to=&quot;/&quot; /&gt;;

const App = () =&gt; (
  &lt;BrowserRouter&gt;
    &lt;Switch&gt;
      &lt;Route path=&quot;/&quot; component={MainPage} /&gt;
      &lt;RestrictRoute
        path=&quot;/sign-in&quot;
        component={SignInPage}
        fallback={GoToMainPage}
        exact
        isAllow={user =&gt; user.isSignedIn}
      /&gt;
      &lt;RestrictRoute
        path=&quot;/sign-up&quot;
        component={SignUpPage}
        fallback={GoToMainPage}
        exact
        isAllow={user =&gt; user.isSignedIn}
      /&gt;
      {/* ... */}
      {/* ... */}
      {/* ... */}
      &lt;Redirect to=&quot;/&quot; /&gt;
    &lt;/Switch&gt;
  &lt;/BrowserRouter&gt;
);</code></pre>
<p>실제 사용은 위처럼 할 수 있습니다.</p>
<p>이미 로그인을 한 유저가 로그인(<code>/sign-in</code>) 페이지 혹은 회원가입(<code>/sign-up</code>) 페이지에 접근할 필요는 없을테니, 로그인 여부를 확인한 후 로그인을 이미 했다면 메인(<code>/</code>) 페이지로 이동시킵니다.</p>
<p><code>React Router</code> 의 <code>Switch</code> 컴포넌트는 바로 하위 컴포넌트를 순회하면서 <code>path</code> 와 <code>exact</code> 조건을 만족하면 전달 받은 component를 바로 렌더링 합니다.
이것을 이용하기 위해 <code>RestrictRoute</code> 컴포넌트는 <code>path</code> 와 <code>exact</code> 를 받는 것입니다.</p>
<h1 id="활용">활용</h1>
<pre><code class="language-tsx">// App.tsx
import React from &quot;react&quot;;
import { BrowserRouter, Switch, Route, Redirect } from &quot;react-router-dom&quot;;
import { RestrictRoute } from &quot;./RestrictRoute&quot;;

const GoToMainPage = () =&gt; {
  alert(&quot;관리자만 접근 가능한 페이지입니다.&quot;);
  return &lt;Redirect to=&quot;/&quot; /&gt;;
};

const App = () =&gt; (
  &lt;BrowserRouter&gt;
    &lt;Switch&gt;
      {/* ... */}
      {/* ... */}
      {/* ... */}
      &lt;RestrictRoute
        path=&quot;/admin&quot;
        fallback={GoToMainPage}
        component={AdminPage}
        isAllow={user =&gt; user.isAdmin}
      /&gt;
      {/* ... */}
      {/* ... */}
      {/* ... */}
    &lt;/Switch&gt;
  &lt;/BrowserRouter&gt;
);</code></pre>
<p>관리자만 접속 가능한 페이지인 경우도 간단하게 구현할 수 있습니다.
관리자가 아니면 alert(Toast 등도 가능!)로 관리자만 접근 가능한 페이지라고 알릴 수도 있습니다.</p>
<h1 id="결론">결론</h1>
<p>interface 선언을 제외하면 몇 라인 되지 않는 이 간단한 컴포넌트는 다양한 요구사항을 만족시킬 수 있습니다.
응용하기에 따라 더 많은 유연성을 확보할 수 있기도 합니다.</p>
<p>이 아이디어가 여러분을 도울 수 있기를 바랍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Node.js 기반에서 환경변수 사용하기 (dotenv, cross-env)]]></title>
            <link>https://velog.io/@public_danuel/process-env-on-node-js</link>
            <guid>https://velog.io/@public_danuel/process-env-on-node-js</guid>
            <pubDate>Sat, 07 Dec 2019 16:37:20 GMT</pubDate>
            <description><![CDATA[<h1 id="환경변수가-뭐죠">환경변수가 뭐죠?</h1>
<p>엄밀한 정의는 아니지만, 환경변수는 <code>특정 process를 위한 key-value 형태의 변수</code>라고 할 수 있습니다.
예를 들면, Java 기반으로 개발을 하기 위해서 JDK를 설치할 때에 환경변수 경로를 설정해줘야 하는 경우가 있겠네요.</p>
<p>Node.js 기반이라면 <code>process.env[key]</code> 형태로 사용할 수 있습니다.
<code>process.env.NODE_ENV</code> 를 떠올리셨다면 맞아요. 이미 익숙하게 사용해오던, Node.js 기반에서의 대표적인 환경변수 중 하나이죠.</p>
<h1 id="크로스플랫폼-환경변수-설정">크로스플랫폼 환경변수 설정</h1>
<p>프로젝트 참여자 각각이 MacOS, Windows, Linux 등 다양한 OS를 사용하고 있다면 여러 난감한 상황이 발생합니다.
OS 마다 환경변수를 설정하는 방법이 다르다는 것이 그 중 하나입니다.</p>
<p>이에 대해 Node.js 커뮤니티에서 내놓은 몇몇 대책을 소개하겠습니다.</p>
<h2 id="dotenv-라이브러리"><code>dotenv</code> 라이브러리</h2>
<pre><code class="language-cmd"># npm
npm i -s dotenv

# Yarn
yarn add dotenv</code></pre>
<p>1번째로는 <code>dotenv</code> 라이브러리를 설치해서 사용하는 것입니다.
이 라이브러리는 미리 작성해놓은 <code>.env</code> 파일을 환경변수에 대신 설정해주는 기능을 가지고 있습니다.</p>
<pre><code class="language-typescript">import dotenv from &#39;dotenv&#39;

dotenv.config()</code></pre>
<p>위처럼 작성하면 현재 디렉토리의 <code>.env</code> 파일을 자동으로 인식해서 환경변수를 설정해줍니다.</p>
<pre><code class="language-typescript">import path from &#39;path&#39;
import dotenv from &#39;dotenv&#39;

dotenv.config({ path: path.join(__dirname, &#39;path/to/.env&#39;) })</code></pre>
<p>위처럼 작성하면 원하는 <code>.env</code> 파일의 위치를 직접 지정할 수 있습니다.
이렇게 하면 production, develop, local, test 등 상황에 맞게 환경변수를 작성한 후 적절하게 사용할 수 있겠죠.</p>
<p>이 정도만 해도 충분히 괜찮다고 생각할 수 있지만, 이 방법은 아쉬운 점이 있습니다.</p>
<pre><code class="language-cmd">+ src
  - index.develop.js
  - index.local.js
  - index.production.js
  - index.test.js
  - ...
- .env.develop
- .env.local
- .env.production
- .env.test
- package.json
- ...</code></pre>
<p>원하는 상황의 개수 만큼 진입점 파일을 따로 작성을 해줘야 한다는 것입니다.
물론, 이미 각 상황에 따라 진입점이 다르다면 괜찮지만 번거로운 것은 여전합니다.</p>
<h2 id="cross-env-라이브러리"><code>cross-env</code> 라이브러리</h2>
<pre><code class="language-cmd"># npm
npm i -s cross-env

# Yarn
yarn add cross-env</code></pre>
<p>2번째로는 <code>cross-env</code> 라이브러리를 설치해서 사용하는 것입니다.
이 라이브러리는 프로그램을 CLI 환경에서 실행시킬 때에 환경변수를 설정하는 기능을 가지고 있습니다.</p>
<pre><code class="language-cmd">cross-env NODE_ENV=production node src/index.js</code></pre>
<p>위처럼 사용할 수 있습니다. MacOS/Linux와 Windows는 환경변수를 설정하는 방법이 다른데, OS의 이러한 상황과 관계 없이 설정하기 위해 사용합니다.</p>
<pre><code class="language-typescript">import path from &#39;path&#39;
import dotenv from &#39;dotenv&#39;

if (process.env.NODE_ENV === &#39;production&#39;) {
  dotenv.config({ path: path.join(__dirname, &#39;path/to/.env.production&#39;) })
} else if (process.env.NODE_ENV === &#39;develop&#39;) {
  dotenv.config({ path: path.join(__dirname, &#39;path/to/.env.develop&#39;) })
} else {
  throw new Error(&#39;process.env.NODE_ENV를 설정하지 않았습니다!&#39;)
}</code></pre>
<p>이 라이브러리를 이용하면 1개의 진입점으로도 상황에 맞게 환경변수를 사용할 수 있겠죠.</p>
<h1 id="환경변수-관리">환경변수 관리</h1>
<p><code>환경변수는</code> Git/SVN과 같은 <code>버전컨트롤시스템에서 관리하지 않는 것이 좋습니다</code> .
GitHub 등 Git 호스팅 서비스를 이용한다면 Private Repository가 아닌 경우 그대로 노출이 되는 등 <code>보안적</code> 으로 여러 <code>문제가 있기 때문</code> 입니다.</p>
<p>그러나, 버전컨트롤시스템의 제어를 벗어난 다른 방법으로 환경변수를 관리하다 보면 꼭 설정해줘야 하는 환경변수를 누락하기도 합니다.
이는 잠재적으로, 의도하지 않은 동작을 일으키기도 하고, 트러블 슈팅 난이도를 올리는 원인으로 작용하기도 합니다.</p>
<pre><code class="language-typescript">import Sequelize from &#39;sequelize&#39;

const sequelize = new Sequelize({
  database: process.env.DATABASE,
  port: process.env.PORT,
  username: process.env.USERNAME,
  password: process.env.PASSWORD,
  dialect: process.env.DIALECT,
  dialectOptions: {
    connectTimeout: Number(process.env.CONNECT_TIMEOUT)
  }
})</code></pre>
<p>또한, 환경변수는 <code>value가 항상 string</code> 이라는 단점 아닌 단점이 있습니다.
Node.js 기반 프로젝트를 둘러보면 위 예시처럼 필요한 곳에서 직접 타입을 변환해 사용하기도 하죠.</p>
<p>환경변수는 어디에서이든 접근할 수 있다는 특성 때문에, 잘 관리하는 것이 어렵기도 합니다.</p>
<p>다음 섹션부터는 제가 참여중인 프로젝트에서 환경변수를 관리하는 방법을 소개하고자 합니다.
(본인만의 방법 혹은 이 보다 더 좋은 방법이 있다면 소개 부탁드립니다!)</p>
<h2 id="설정모음">설정모음</h2>
<p>환경변수는 어찌 보면 설정모음이라는 성격을 가지고 있습니다.
이에 착안해 configs라는 변수에 담고, 환경변수가 필요한 곳에서는 이 configs 변수를 불러와 사용을 합니다.</p>
<pre><code class="language-typescript">// configs 변수
export const configs = {
  database: process.env.DATABASE || &#39;localhost&#39;,
  port: process.env.PORT || 3306,
  username: process.env.USERNAME || &#39;root&#39;,
  password: process.env.PASSWORD || &#39;admin&#39;,
  dialect: process.env.DIALECT || &#39;mysql&#39;,
  connectTimeout: Number(process.env.CONNECT_TIMEOUT || 1000)
}

// 환경변수가 필요한 곳
import Sequelize from &#39;sequelize&#39;
import { configs } from &#39;path/to/configs&#39;

const sequelize = new Sequelize({
  database: configs.database,
  port: configs.port,
  username: configs.username,
  password: configs.password,
  dialect: configs.dialect,
  dialectOptions: {
    connectTimeout: configs.connectTimeout
  }
})</code></pre>
<p>환경변수가 필요한 곳에서는 configs 변수를 불러와서 사용한다는 규칙을 지키면 프로젝트에서 쓰고 있는 환경변수가 얼마나 있는지 파악하기도 편리합니다.
사용하지 않는 환경변수를 추적하기에도 용이하구요.</p>
<p>사용하는 환경변수의 개수 만큼 작성해야 한다는 단점이 있지만, 환경변수를 추가할 때에 1번 작성한 이후에는 수정할 일이 거의 없으므로 이 정도는 감수할 만합니다.
또한, 다음 섹션에서 소개할 cast 헬퍼 함수를 이용하면 환경변수 누락까지도 한 곳에서 방지할 수 있습니다.</p>
<h2 id="cast-헬퍼-함수">cast 헬퍼 함수</h2>
<pre><code class="language-javascript">const number = (value: string) =&gt; {
  const result = Number(value)
  if (!Number.isNaN(result)) {
    return result
  }
}

const string = (value: string) =&gt; value

const typeConverter = { number, string }

const cast = (key, type, defaultValue) =&gt; {
  const value = process.env[key]
  if (value !== undefined) {
    const result = typeConverter[type](value)
    if (result !== undefined) {
      return result
    }
    throw new Error(`process.env.${key}에 적절한 값을 설정하지 않았습니다`)
  }
  if (defaultValue !== undefined) {
    return defaultValue
  }
  throw new Error(`process.env.${key}에 할당할 값이 없습니다`)
}</code></pre>
<p>key와 type을 지정하면 환경변수에서 해당 key의 value를 읽어와 지정한 타입으로 변환을 해주는 기능을 하는 헬퍼 함수입니다.</p>
<p>key가 없거나 적절한 변환 결과가 없으면(즉, 값이 undefined이면) 해당 key에 대한 적절한 에러를 던집니다. (만약 defaultValue가 있다면 그것을 변환 결과로 return 합니다.)</p>
<pre><code class="language-typescript">export const configs = {
  database: cast(&#39;DATABASE&#39;, &#39;string&#39;, &#39;localhost&#39;),
  port: cast(&#39;PORT&#39;, &#39;number&#39;, 3306),
  username: cast(&#39;USERNAME&#39;, &#39;string&#39;, &#39;root&#39;),
  password: cast(&#39;PASSOWRD&#39;, &#39;string&#39;, &#39;admin&#39;),
  dialect: cast(&#39;DIALECT&#39;, &#39;string&#39;, &#39;mysql&#39;),
  connectTimeout: cast(&#39;CONNECT_TIMEOUT&#39;, &#39;number&#39;, 1000)
}</code></pre>
<p>앞서 작성한 cast 헬퍼 함수를 이용해서 다시 작성하면 이렇게 할 수 있습니다.</p>
<p>위 예시에서 알 수 있듯이, 프로그램을 실행시킬 때에 모든 환경변수를 적절히 설정한 경우에만 에러 없이 동작하므로, 에러가 없다면 누락한 환경변수가 없다는 것을 보장할 수 있습니다.</p>
<pre><code class="language-typescript">const boolean = (value: string) =&gt; {
  switch (value) {
    case &#39;true&#39;: {
      return true
    }
    case &#39;false&#39;: {
      return false
    }
  }
}

// ...

const typeConverter = { boolean, number, string }

// ...

const configs = {
  isLogging: cast(&#39;IS_LOGGING&#39;, &#39;boolean&#39;, false),
  // ...
}</code></pre>
<p>typeConverter는 유연해서, 위 예시처럼 원하는 변환 함수를 추가하면 원하는 만큼 유연하게 타입을 변환할 수 있습니다.</p>
<p>간단하게 소개를 하기 위해 기능을 많이 축소시켰지만, cast 헬퍼 함수를 조금 더 확장하면 number 타입인 경우에는 min, max 체크를 하는 등의 기능도 추가할 수 있습니다.</p>
<h1 id="하니팁">하니팁</h1>
<ul>
<li>Node.js 기반이라면 프로덕션 환경에서는 <code>process.env.NODE_ENV</code> 를 <code>production</code> 으로 설정해주세요. 어떤 라이브러리는 프로덕션 모드에 대한 최적화코드를 따로 작성해놔서 퍼포먼스 향상이 있을 수 있거든요.</li>
<li>React 등 Babel/webpack 기반 개발환경이라면 <code>process.env.NODE_ENV</code> 는 직접 사용하는 것을 추천해요. production용 빌드라면 <code>process.env.NODE_ENV</code> 조건이 걸린 코드를 날려주기도 하거든요.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 6-0. 후기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-epilogue</link>
            <guid>https://velog.io/@public_danuel/trendy-react-epilogue</guid>
            <pubDate>Tue, 14 May 2019 07:59:29 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 강좌를 작성하면서 중점으로 둔 부분과 후기를 작성했습니다.</p>
<h1 id="강좌-중점">강좌 중점</h1>
<p>이 강좌에서는 의도적으로 생략한 것이 많습니다. 이는 강좌의 무게감을 줄이고, React 라이브러리 자체를 익히는 것에 집중하기 위해서입니다. 이미 React를 익힌 분에게는 새로이 익힐 내용 없이 가볍기만 하다는 느낌이 들 수 있지만, 전체적인 흐름을 익히는 것에 중점을 두었습니다.</p>
<h1 id="react-라이브러리-섹션에-대해">React 라이브러리 섹션에 대해</h1>
<ul>
<li>Lazy Loading</li>
<li>AJAX</li>
<li>양방향 네트워킹</li>
<li>Graphics</li>
<li>음성/영상 처리</li>
<li>국제화I18N</li>
<li>현지화L10N</li>
<li>etc</li>
</ul>
<p>직전 편은 React와 관련 있는 이슈 중 큰 분류만 작성한 것이므로, 이 외에도 다양한 이슈가 있으며 웹프론트엔드에는 더 많은 이슈가 있습니다.</p>
<p>해당 이슈를 만나거나 해당 이슈가 나타날 것으로 예상할 때에 알아보는 것이 바람직합니다.</p>
<h1 id="후기">후기</h1>
<p>포스트는 커녕 블로그를 제대로 관리해본 적도 없는데 의도한 분량 만큼의 강좌를 다행히도 작성했습니다. 개인적으로는 다소 무모하다고 생각했는데 끝까지 해냈습니다.</p>
<p>강좌를 작성하고 나니 포스트를 꾸준히 작성하고 싶다는 욕심이 생깁니다. React 라이브러리 섹션에서 소개한 라이브러리를 주제로 하나씩 포스트를 작성해볼까 하는 생각이 듭니다. 무모함은 끝이 없는듯 합니다</p>
<p>강좌를 보면서 의문점, 부족한 점, 보완점 등 댓글로 달아주시면 적극적으로 반영하겠습니다. 댓글은 부담스럽거나 강좌와는 별개의 질문이 있거나 개인적으로 연락을 하고 싶은 분은 <a href="mailto:public.danuel@gmail.com">public.danuel@gmail.com</a>로 메일을 보내주세요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 5-0. React 라이브러리]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-libraries</link>
            <guid>https://velog.io/@public_danuel/trendy-react-libraries</guid>
            <pubDate>Tue, 14 May 2019 07:59:26 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 React를 하면서 만나는 여러 이슈와 그 때에 고려할 수 있는 라이브러리를 간단하게 소개하겠습니다.</p>
<p>목차 간소화를 위해 하위에 넣은 분류도 있지만, 각각을 하나의 독립적인 분류로 보는 것이 좋습니다.</p>
<h1 id="css-스타일링">CSS 스타일링</h1>
<p>React에서 CSS 스타일링은 CSS를 직접 사용하는 방법과 <a href="http://lesscss.org/">LESS</a>, <a href="https://sass-lang.com/">SASS</a>가 대표적인 <code>CSS Module</code>, <a href="https://www.styled-components.com/">styled-components</a>, <a href="https://emotion.sh/docs/introduction">emotion</a>이 대표적인 <code>CSS in JS</code> 등 굉장히 다양한 방법이 있습니다.</p>
<p>최근에는 <a href="https://www.styled-components.com/">styled-components</a>가 각광을 받고 있으며, 이전에는 SASS가 주로 쓰였습니다.</p>
<h2 id="css-프레임워크">CSS 프레임워크</h2>
<p>빠른 구현 혹은 편의성 등 위해서 CSS 프레임워크를 도입하고자 할 수 있습니다. 그런 때에는 <a href="https://ant.design/">Ant Design</a>, <a href="https://material-ui.com/">Material UI</a>, <a href="https://react.semantic-ui.com/">Semantic UI</a> 등 라이브러리를 알아볼 수 있습니다.</p>
<h2 id="컴포넌트-문서화">컴포넌트 문서화</h2>
<p>화면을 구성할 때에는 크게 하강식top-down 방식과 상향식bottom-up 방식이 있습니다. 하강식은 큰 그림을 먼저 그린 후 세부적으로 작은 그림을 그리고, 상향식은 작은 그림을 먼저 그린 후 그것을 조합하는 방식이라고 할 수 있습니다.</p>
<p>여러 컴포넌트를 작성하다 보면 확인 버튼과 같이 비슷비슷한 컴포넌트가 존재합니다. 그런 컴포넌트는 미리 작성해 어떻게 사용하는지 문서화를 해둔다면 편리할 것입니다. 이런 목적을 위한 것 중에는 <a href="https://storybook.js.org/">storybook</a>이 있습니다.</p>
<h1 id="상태-관리state-management">상태 관리State Management</h1>
<p>상태 관리는 React 뿐만 아니라 어떤 프로젝트를 하더라도 어려운 이슈입니다.</p>
<p>React Hooks 등장 이전부터 널리 쓰이는 라이브러리로는 <a href="https://react-redux.js.org/">react-redux</a>와 MobX가 있습니다. <a href="https://react-redux.js.org/">react-redux</a>는 기본적으로 작성해야 할 코드가 많지만 보다 고수준의 상태 관리 코드를 작성할 수 있고 추가적인 다양한 최적화를 할 수 있으며, <a href="https://mobx.js.org/">MobX</a>는 라이브러리에게 상태 관리를 맡기지만 Decorator라는 확장문법을 이용해 적은 코드로 상태 관리를 할 수 있습니다.</p>
<ul>
<li><a href="https://react-redux.js.org/">react-redux</a>는 <a href="https://redux.js.org/">Redux</a>를 React에서 적절히 사용할 수 있게 감싼 형태의 라이브러리입니다.</li>
</ul>
<p>이 강좌의 중심인 React v16.8부터는 React Context와 React Hooks를 조합해 상태 관리를 유연하게 할 수 있습니다. 이를 기반으로 하면 대부분의 React에서의 상태관리 이슈는 해결할 수 있지만, React 사용경험에 따라 코드 퀄리티 격차가 클 수 있습니다.</p>
<h2 id="라우팅routing">라우팅Routing</h2>
<p>여러 페이지를 가진 웹어플리케이션을 구현하려면 브라우저의 URL을 직접 다루어야 한다는 큰 어려움이 있습니다. 이와 관련해 가장 널리 쓰이는 라이브러리는 <a href="https://reacttraining.com/react-router/">react-router-dom</a>입니다. 사실상 표준 정도로 쓰이고 있습니다.</p>
<h2 id="불변성immutablity">불변성Immutablity</h2>
<p>불변성은 데이터를 변경할 수 없는 것이라고 할 수 있습니다. 게임으로 보면, 캐릭터가 데미지를 받아서 체력이 깎여야 한다면 해당 캐릭터의 체력을 깎는 것이 아니라 &#39;체력이 깎인 새로운 캐릭터&#39; 로 대체하는 느낌으로 볼 수 있습니다. 비효율적이라는 느낌이 들 수 있지만 React는 이렇게 해도 충분한 퍼포먼스를 낼 수 있으며 버그를 쉽게 잡거나 새로운 기능을 추가하는 등 여러 이점이 있다는 것을 증명한 라이브러리입니다.</p>
<p>JavaScript는 이러한 불변성을 문법적으로 지원을 하지는 않기 때문에 직접 혹은 라이브러리를 통해 개념적으로 사용해야 합니다. 널리 쓰이는 라이브러리로는 <a href="https://immutable-js.github.io/immutable-js/">immutable.js</a>와 <a href="https://github.com/immerjs/immer">immer.js</a>가 있습니다. 하위호환성 이슈가 없다면 <a href="https://github.com/immerjs/immer">immer.js</a>를 우선적으로 고려하는 것을 추천합니다.</p>
<h1 id="서버측-렌더링server-side-rendering">서버측 렌더링Server-Side Rendering</h1>
<p>React는 기본적으로 클라이언트측 렌더링Client-Side Rendering입니다. 게임으로 보면 화면을 그리는 프로그램(게임 클라이언트)을 만들기 위한 라이브러리라고 할 수 있습니다.</p>
<p>서버측 렌더링은 클라이언트측 렌더링과 반대로 화면을 그리는 방법을 서버측에서 제공하는 것입니다. 게임으로 보면 게임서버가 화면을 보내주는 것입니다.</p>
<p>프로젝트의 요구사항에 <a href="https://ko.wikipedia.org/wiki/%EA%B2%80%EC%83%89_%EC%97%94%EC%A7%84_%EC%B5%9C%EC%A0%81%ED%99%94">검색엔진최적화SEO</a>, 초기화 시점 퍼포먼스 등 이슈가 있다면 서버측 렌더링을 고려해볼 수 있습니다.</p>
<p>대표적으로 <a href="https://nextjs.org/">Next.js</a>는 서버측 렌더링을 중심으로 하는 React 기반 프레임워크입니다. 몇몇 규칙을 따라 프로젝트를 작성하면 알아서 서버측 렌더링을 할 수 있지만, <a href="https://nextjs.org/">Next.js</a>를 기반으로 해도 해결하기 어려운 부분이 어려울 때에는 <a href="https://reactjs.org/docs/react-dom.html#hydrate">hydration</a> 등의 방법을 고려해야 합니다.</p>
<h2 id="코드-분할code-splitting"><a href="https://reactjs.org/docs/code-splitting.html">코드 분할Code-Splitting</a></h2>
<p>웹어플리케이션을 작성하다 보면 코드가 점점 늘어납니다. React를 기반으로 여러 컴포넌트와 파일로 분리해서 작성하기는 하지만 클라이언트(유저)에게는 결과적으로 1개의 스크립트 파일로 전달합니다. 프로젝트가 작다면 충분히 만족스러운 퍼포먼스를 낼 수 있겠지만 어느 순간부터는 퍼포먼스가 부족하다는 느낌이 들기 시작합니다.</p>
<p>이와 관련해서 React에서는 <a href="https://reactjs.org/docs/code-splitting.html#reactlazy">lazy</a>라는 함수로 코드 분할을 지원합니다. 해당 모듈과 그 모듈이 사용하는 것을 적절히 분리해서 유저에게 전달하는 방식입니다. 현재로서는 서버측 렌더링에서는 사용할 수 없으므로 서버측 렌더링도 고려를 해야 한다면 <a href="https://www.smooth-code.com/open-source/loadable-components/">loadable-components</a>도 추가적으로 알아보는 것이 좋습니다.</p>
<ul>
<li>정확히 <a href="https://reactjs.org/docs/code-splitting.html#reactlazy">lazy</a> 함수는 webpack을 통해 코드 분할을 지원합니다.</li>
</ul>
<h1 id="테스트test">테스트Test</h1>
<p>TDD, Unit Test, BDD, etc. 웹프론트엔드에도 테스트 요구가 늘고 있습니다.</p>
<p>테스트는 중요합니다. 테스트 코드를 작성하면 매번 인간이 직접 확인해야 하던 사항을 문서화 및 자동으로 체크할 수 있으며 이 외에도 여러 장점이 있지만, 웹프론트엔드는 데이터 구조 뿐만 아니라 컴포넌트가 화면에 어떻게 보이는지도 테스트를 해야 하기 때문에 테스트를 어떻게 해야 하는지 난해합니다.</p>
<p>React 기반 프로젝트는 주로 <a href="https://jestjs.io/">Jest</a>, Enzyme 등 라이브러리를 이용해 테스트를 합니다. <a href="https://jestjs.io/">Jest</a>는 React를 작성한 Facebook에서, <a href="https://airbnb.io/enzyme/">Enzyme</a>은 Airbnb에서 발표했습니다.</p>
<h2 id="타입-시스템type-system">타입 시스템Type System</h2>
<p>JavaScript는 타입을 명시하지 않는 언어입니다. 그러다 보니 프로젝트가 크다면 가독성, 유지보수성, 협업용이성 등 어려움을 느끼는 순간이 오며, 그런 때에는 타입을 명시하는 언어의 필요성을 검토해야 합니다.</p>
<p>React는 <a href="https://flow.org/">Flow(JavaScript에 타입을 명시하는 언어 중 하나)</a>로 작성한 라이브러리입니다. 따라서 <a href="https://flow.org/">Flow</a>를 지원하며 <a href="https://www.typescriptlang.org/">TypeScript</a>, <a href="https://reasonml.github.io/">ReasonML</a> 등 여러 언어를 지원합니다. React 초기에는 <a href="https://flow.org/">Flow</a>와 <a href="https://www.typescriptlang.org/">TypeScript</a>가 서로 경합을 펼쳤으나, 최근에는 <a href="https://www.typescriptlang.org/">TypeScript</a>의 강력한 React 지원에 힘 입어 <a href="https://www.typescriptlang.org/">TypeScript</a>를 기반으로 하는 프로젝트가 많습니다.</p>
<ul>
<li><a href="https://flow.org/">Flow</a>는 타입 체커라고 하는 것이 조금 더 정확하지만, 이해편의상 언어로 표현했습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 4-0. React Portals 알아보기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-portals</link>
            <guid>https://velog.io/@public_danuel/trendy-react-portals</guid>
            <pubDate>Sat, 04 May 2019 16:46:43 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 React Portals을 소개하고 작성하는 방법을 알아보겠습니다.</p>
<p>React Portals는 React에서의 특수한 개념이므로 다른 기술 혹은 분야에서의 Portal과는 의미가 다를 수 있습니다.</p>
<p>React의 고급 기능이므로 React를 익히는 목적이라면 어떤 것을 위한 기능인지 목적만 확인하는 정도로 충분합니다.</p>
<h1 id="react-진입점">React 진입점</h1>
<pre><code class="language-html">&lt;!-- /public/index.html --&gt;
&lt;body&gt;
  &lt;div id=&#39;root&#39;&gt;&lt;/div&gt;
&lt;/body&gt;</code></pre>
<pre><code class="language-jsx">import React from &#39;react&#39;
import ReactDOM from &#39;react-dom&#39;

const App = () =&gt; (
  &lt;div&gt;App&lt;/div&gt;
)

ReactDOM.render(&lt;App /&gt;, document.getElementById(&#39;root&#39;))</code></pre>
<p>지금까지 소개한 내용으로 볼 떄에 React는 위 예시와 같이 최상위 태그 아래에 모든 컴포넌트를 &#39;집어넣는&#39; 구조입니다.</p>
<p>로그인창, 결제창 등을 위한 다이얼로그와 같은 컴포넌트를 작성하다 보면 이러한 구조를 벗어나 또 다른 최상위 태그에 위치하게 하고 싶을 때가 있습니다.</p>
<pre><code class="language-html">&lt;!-- /public/index.html --&gt;
&lt;body&gt;
  &lt;div id=&#39;root&#39;&gt;&lt;/div&gt;
  &lt;div id=&#39;other&#39;&gt;&lt;/div&gt;
&lt;/body&gt;</code></pre>
<pre><code class="language-jsx">import React from &#39;react&#39;
import ReactDOM from &#39;react-dom&#39;

const App = () =&gt; (
  &lt;div&gt;App&lt;/div&gt;
)

ReactDOM.render(&lt;App /&gt;, document.getElementById(&#39;root&#39;))

const Other = () =&gt; (
  &lt;div&gt;Other&lt;/div&gt;
)

ReactDOM.render(&lt;Other /&gt;, document.getElementById(&#39;other&#39;))</code></pre>
<p>이런 형태를 상상해볼 수 있지만 App 컴포넌트와 Other 컴포넌트 사이에 State를 공유하는 등 복잡한 동작이 늘어날수록 점점 구조가 이상하게 변합니다.</p>
<p>더 나은 방법이 필요한 순간입니다.</p>
<h1 id="react-portals">React Portals</h1>
<pre><code class="language-html">&lt;!-- /public/index.html --&gt;
&lt;body&gt;
  &lt;div id=&#39;root&#39;&gt;&lt;/div&gt;
  &lt;div id=&#39;other&#39;&gt;&lt;/div&gt;
&lt;/body&gt;</code></pre>
<pre><code class="language-jsx">import React from &#39;react&#39;
import ReactDOM, { createPortal } from &#39;react-dom&#39;

const App = () =&gt; (
  &lt;&gt;
    &lt;div&gt;App&lt;/div&gt;
    &lt;Other /&gt;
  &lt;/&gt;
)

const Other = () =&gt; {
  return createPortal(&lt;div&gt;Other&lt;/div&gt;, document.getElementById(&#39;other&#39;))
}

ReactDOM.render(&lt;App /&gt;, document.getElementById(&#39;root&#39;))</code></pre>
<p>React Portals를 이용하면 위에서 언급한 요구사항을 우회책 없이도 해결할 수 있습니다.</p>
<p>익숙하지 않으므로 하나씩 살펴보겠습니다.</p>
<ul>
<li><p>React 라이브러리에서 createPortal을 불러오기.</p>
</li>
<li><p>App 컴포넌트에 Other 컴포넌트 포함하기.</p>
</li>
<li><p>Other 컴포넌트 안에서 createPortal 함수를 실행한 값을 return 하기.</p>
<ul>
<li>createPortal 함수의 1번째 파라미터는 화면에 나타낼 JSX, 2번째 파라미터는 1번째 파라미터가 위치할 태그입니다.</li>
</ul>
</li>
</ul>
<p>해당 컴포넌트의 아래가 아니라 다른 태그의 하위에 위치한다는 것이 처음에는 헷갈릴 수 있지만 금방 적응할 수 있습니다.</p>
<pre><code class="language-jsx">import React, { useMemo } from &#39;react&#39;
import ReactDOM, { createPortal } from &#39;react-dom&#39;

const App = () =&gt; (
  &lt;&gt;
    &lt;div&gt;App&lt;/div&gt;
    &lt;Other /&gt;
  &lt;/&gt;
)

const Other = () =&gt; {
  const rootElement = useMemo(() =&gt; document.getElementById(&#39;other&#39;))

  return createPortal(&lt;div&gt;Other&lt;/div&gt;, rootElement)
}

ReactDOM.render(&lt;App /&gt;, document.getElementById(&#39;root&#39;))</code></pre>
<p>상태가 바뀌면 컴포넌트를 다시 그리는데, 그 때 마다 document.getElementById 메서드를 사용하는 것은 효율이 좋지 않으므로 React Hooks의 useMemo 함수를 이용하여 간단하나마 최적화를 해주는 것이 좋습니다.</p>
<h1 id="추상화">추상화</h1>
<pre><code class="language-html">&lt;!-- /public/index.html --&gt;
&lt;body&gt;
  &lt;div id=&#39;root&#39;&gt;&lt;/div&gt;
  &lt;div id=&#39;other&#39;&gt;&lt;/div&gt;
&lt;/body&gt;</code></pre>
<pre><code class="language-jsx">import React, { useMemo } from &#39;react&#39;
import ReactDOM, { createPortal } from &#39;react-dom&#39;

const App = () =&gt; (
  &lt;&gt;
    &lt;div&gt;App&lt;/div&gt;
    &lt;Portal&gt;
      &lt;Other /&gt;
    &lt;/Portal&gt;
  &lt;/&gt;
)

const Portal = ({ children }) =&gt; {
  const rootElement = useMemo(() =&gt; document.getElementById(&#39;other&#39;))

  return createPortal(children, rootElement)
}

const Other = () =&gt; (
  &lt;div&gt;Other&lt;/div&gt;
)

ReactDOM.render(&lt;App /&gt;, document.getElementById(&#39;root&#39;))</code></pre>
<p>위 예시처럼 Portal 컴포넌트로 분리하면 보다 더 읽기 편하게 작성할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 4-1. React Portals 활용]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-portals-in-use</link>
            <guid>https://velog.io/@public_danuel/trendy-react-portals-in-use</guid>
            <pubDate>Sat, 04 May 2019 16:46:39 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 React Portals를 활용하는 개인적인 방법을 소개하겠습니다.</p>
<p>React Portals를 어떻게 활용할 수 있는지 참고자료로써 쓰이기를 희망합니다.</p>
<h1 id="소스코드">소스코드</h1>
<pre><code class="language-javascript">import React, { useEffect, useMemo } from &#39;react&#39;
import { createPortal } from &#39;react-dom&#39;

const Portal = ({ id, children }) =&gt; {
  const [parentElement, cleanupParentElement] = useMemo(() =&gt; createParentElement(id), [])

  useEffect(() =&gt; () =&gt; cleanupParentElement(), [])

  return createPortal(children, parentElement)
}

const createParentElement = id =&gt; {
  const rootElement = getRootElement(id)
  const parentElement = document.createElement(&#39;div&#39;)

  rootElement.appendChild(parentElement)

  const cleanupParentElement = () =&gt; {
    rootElement.removeChild(parentElement)
  }

  return [parentElement, cleanupParentElement]
}

const getRootElement = id =&gt; {
  const rootElement = document.getElementById(id)
  if (rootElement !== null) {
    return rootElement
  }

  const nextRootElement = document.createElement(&#39;div&#39;)
  nextRootElement.setAttribute(&#39;id&#39;, id)
  document.body.appendChild(nextRootElement)

  return nextRootElement
}</code></pre>
<p>크게 3단계 구성입니다.</p>
<ul>
<li>getRootElement: 해당하는 id 엘리먼트를 불러오며, 없으면 body 태그 안에 추가한다.</li>
<li>createParentElement: 위에서 만든 Root 엘리먼트 안에 새로운 div 엘리먼트를 추가한다.</li>
<li>createPortal: 위에서 만든 Parent 엘리먼트에 Portal을 생성한다.</li>
</ul>
<p>추가적으로 메모리 누수를 막기 위하여 Portal을 종료하면 해당 Parent 엘리먼트를 제거하는 로직이 있습니다.</p>
<p>같은 id이면 그 엘리먼트 안에 새로운 Parent 엘리먼트를 추가하는 원리로써 아래의 장점을 가집니다.</p>
<ul>
<li>createPortal 함수를 그대로 사용하는 것 보다 편리하다.</li>
<li>경고창, 결제창, 설정창 등 여러 대화상자 컴포넌트의 논리적 우선순위를 정리하기 용이하다.</li>
<li><code>/public/index.html</code> 를 수정할 필요가 없다</li>
</ul>
<h1 id="활용-방법">활용 방법</h1>
<pre><code class="language-jsx">const App = () =&gt; (
  &lt;&gt;
    &lt;div&gt;App&lt;/div&gt;
    &lt;Dialog&gt;
      &lt;User /&gt;
    &lt;/Dialog&gt;
  &lt;/&gt;
)

const Dialog = ({ children }) =&gt; (
  &lt;Portal id=&#39;dialog&#39;&gt;{children}&lt;/Portal&gt;
)

const User = () =&gt; (
  &lt;div&gt;User&lt;/div&gt;
)</code></pre>
<h1 id="심화">심화</h1>
<p>위에서 작성한 Portal 컴포넌트를 응용하면 다양한 기능을 유연하게 추가할 수 있습니다.</p>
<pre><code class="language-jsx">const EscapeablePortal = ({ id, isOpen, close, children }) =&gt; {
  const closePortalToClickEscapeKey = () =&gt; {
    const closePortal = event =&gt; {
      if (event.key === &#39;Escape&#39;) {
        close()
      }
    }

    document.addEventListener(&#39;keydown&#39;, closePortal)

    return () =&gt; {
      document.addEventListener(&#39;keydown&#39;, closePortal)
    }
  }
  useEffect(closePortalToClickEscapeKey, [close])

  if (!isOpen) {
    return null
  }

  return (
    &lt;Portal id={id}&gt;{children}&lt;/Portal&gt;
  )
}</code></pre>
<p>키보드의 ESC 키를 클릭하면 Portal을 종료하는 컴포넌트입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 3-1. useContext 알아보기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-usecontext</link>
            <guid>https://velog.io/@public_danuel/trendy-react-usecontext</guid>
            <pubDate>Mon, 29 Apr 2019 12:01:49 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 React Context를 조금 더 편하게 사용할 수 있는 useContext를 알아보겠습니다.</p>
<ul>
<li>useContext는 React가 제공하는 React Hooks이지만, React Context를 먼저 이해해야 하기 때문에 미처 소개하지 못 헀습니다.</li>
</ul>
<h1 id="react-context-중첩">React Context 중첩</h1>
<pre><code class="language-jsx">import React, { createContext } from &#39;react&#39;

// 0. AppContext 생성
const AppContext = createContext()

const App = () =&gt; {
  const user = {
    nickname: &#39;danuel&#39;,
    isAdmin: true
  }

  return (
    &lt;AppContext.Provider value={user}&gt;
      &lt;div&gt;
        &lt;Posts /&gt;
      &lt;/div&gt;
    &lt;/AppContext.Provider&gt;
  )
}

// 1. PostsContext 생성
const PostsContext = createContext()

const Posts = () =&gt; {
  const posts = [
    {
      title: &#39;useContext 알아보기&#39;,
      content: &#39;이번 편에서는 React Context를 ...&#39;
    }
  ]

  return (
    &lt;PostsContext.Provider value={posts}&gt;
      &lt;Children /&gt;
    &lt;/PostsContext.Provider&gt;
  )
}

// 2. user와 posts를 가져와 화면에 보여주기
const Children = () =&gt; (
  &lt;AppContext.Consumer&gt;
    {user =&gt; (
      &lt;PostsContext.Consumer&gt;
        {posts =&gt; {
          let label = &#39;user&#39;
          if (user.isAdmin) {
            label = &#39;admin&#39;
          }

          return (
            &lt;div&gt;
              &lt;div&gt;{label}&lt;/div&gt;
              &lt;div&gt;{user.nickname}&lt;/div&gt;
              &lt;div&gt;{posts.map((post, index) =&gt; (
                &lt;div key={index}&gt;
                  &lt;div&gt;{post.title}&lt;/div&gt;
                  &lt;div&gt;{post.content}&lt;/div&gt;
                &lt;/div&gt;
              ))}&lt;/div&gt;
            &lt;/div&gt;
          )
        }}
      &lt;/PostsContext.Consumer&gt;
    )}
  &lt;/AppContext.Consumer&gt;
)</code></pre>
<p>Children 컴포넌트가 user와 post를 모두 보여주는 억지스러운 예시이지만, 쉽게 이해할 수 있도록 간단하게 표현했습니다.</p>
<p>React Context는 Props Drilling 패턴을 떨칠 수 있고 유연한 컴포넌트를 작성할 수 있다는 부분에서 좋은 기능인 것은 분명하지만, 여러 Context를 중첩해서 사용하다 보면 간단 기능을 담당하는 컴포넌트가 점점 이상하게 변하기 시작합니다. 더 나은 방법이 필요하다는 생각이 드는 순간입니다.</p>
<h1 id="usecontext">useContext</h1>
<pre><code class="language-jsx">import React, { createContext, useContext } from &#39;react&#39;

// 0. AppContext 생성
const AppContext = createContext()

const App = () =&gt; {
  const user = {
    nickname: &#39;danuel&#39;,
    isAdmin: true
  }

  return (
    &lt;AppContext.Provider value={user}&gt;
      &lt;div&gt;
        &lt;Posts /&gt;
      &lt;/div&gt;
    &lt;/AppContext.Provider&gt;
  )
}

// 1. PostsContext 생성
const PostsContext = createContext()

const Posts = () =&gt; {
  const posts = [
    {
      title: &#39;useContext 알아보기&#39;,
      content: &#39;이번 편에서는 React Context를 ...&#39;
    }
  ]

  return (
    &lt;PostsContext.Provider value={posts}&gt;
      &lt;Children /&gt;
    &lt;/PostsContext.Provider&gt;
  )
}

// 2. user와 posts를 가져와 화면에 보여주기
const Children = () =&gt; {
  const user = useContext(AppContext)
  const posts = useContext(PostsContext)

  let label = &#39;user&#39;
  if (user.isAdmin) {
    label = &#39;admin&#39;
  }

  return (
    &lt;div&gt;
      &lt;div&gt;{label}&lt;/div&gt;
      &lt;div&gt;{user.nickname}&lt;/div&gt;
      &lt;div&gt;{posts.map((post, index) =&gt; (
        &lt;div key={index}&gt;
          &lt;div&gt;{post.title}&lt;/div&gt;
          &lt;div&gt;{post.content}&lt;/div&gt;
        &lt;/div&gt;
      ))}&lt;/div&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>useContext를 이용하면 Context.Consumer로 컴포넌트를 작성할 때 보다 더 쉽고 편하고 직관적으로 작성할 수 있습니다. useState, useEffect 등 여러 React Hooks와 조합해서 사용하기에도 용이하다는 장점도 얻을 수 있습니다.</p>
<h2 id="주의">주의</h2>
<pre><code class="language-jsx">import React, { createContext, useContext } from &#39;react&#39;

const Context = createContext()

// ...

const Children = () =&gt; {
  const context0 = useContext(Context.Provider) // ERROR!!!
  const context1 = useContext(Context.Consumer) // ERROR!!!
  const context2 = useContext(Context) // OK

  // ...
}</code></pre>
<p>useContext는 createContext 함수를 실행한 결과를 그대로 파라미터로 넘겨줘야 합니다.</p>
<h1 id="팁">팁</h1>
<pre><code class="language-jsx">import React, { createContext } from &#39;react&#39;

const AppContext = createContext()

// ...

const User = () =&gt; {
  const appContext = useContext(AppContext)

  // ...
}</code></pre>
<p>이 예시와 같이 사용하다 보면 여러 Context를 모두 인지하고 있어야 하고, Context를 전달하기 전에 추가적인 처리를 해주고자 한다면 유연성이 부족하다는 이슈가 있습니다.</p>
<pre><code class="language-jsx">import React, { createContext, useContext, useMemo } from &#39;react&#39;

const AppContext = createContext()

const useAppContext = () =&gt; useContext(AppContext)

// ...

const User = () =&gt; {
  const appContext = useAppContext()

  // ...
}</code></pre>
<p>이 예시와 같이 유의미한 이름을 지정하고 사용하면 Context를 인지할 필요 없이 Custom Hooks를 사용하는 것처럼 작성할 수 있으며, 코드를 읽기도 한결 더 수월합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 3-0. React Context 알아보기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-context</link>
            <guid>https://velog.io/@public_danuel/trendy-react-context</guid>
            <pubDate>Mon, 29 Apr 2019 12:01:46 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 React Context를 소개하고 작성하는 방법을 알아보겠습니다.</p>
<ul>
<li>아래에서 나오는 Context.Provider는 다음 편인 useContext를 이용해 더 쉽고 편하게 작성할 수 있기 때문에, React Context 개념과 createContext, Context.Provider 컴포넌트의 작성방법만 익혀도 괜찮습니다.</li>
</ul>
<h1 id="props-drilling">Props Drilling</h1>
<p>React Context를 소개하기 전에 Props Drilling 패턴을 살펴보겠습니다.</p>
<pre><code class="language-jsx">import React from &#39;react&#39;

const App = () =&gt; {
  const user = {
    nickname: &#39;Danuel&#39;,
    isAdmin: true
  }

  return (
    &lt;div&gt;
      &lt;Main user={user} /&gt;
    &lt;/div&gt;
  )
}

const Main = ({ user }) =&gt; (
  &lt;main&gt;
    &lt;Avatar user={user} /&gt;
  &lt;/main&gt;
)

const Avatar = ({ user }) =&gt; (
  &lt;div&gt;
    &lt;User user={user} /&gt;
  &lt;/div&gt;
)

const User = ({ user }) =&gt; {
  let label = &#39;user&#39;
  if (user.isAdmin) {
    label = &#39;admin&#39;
  }

  return (
    &lt;div&gt;
      &lt;div&gt;{label}&lt;/div&gt;
      &lt;div&gt;{user.nickname}&lt;/div&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>컴포넌트를 작성하고 사용하다 보면 해당 컴포넌트에게는 필요가 없지만 하위 컴포넌트에게 전달하기 위해 Props를 받아야 할 때가 있습니다. 이 예시로 보면 Main 컴포넌트와 Avatar 컴포넌트는 user에 대한 상태를 알 필요가 없지만, User 컴포넌트에게 user 상태를 전달하기 위해서 상위 컴포넌트로부터 전달을 받습니다.</p>
<p>이러한 패턴을 Props Drilling이라고 합니다.</p>
<p>Props Drilling을 이용해 작성한 컴포넌트는 서로가 서로에게 의존을 하는 형태로 발전하기 시작하고, 재활용이 불가능하지만 분리만 해놓은 컴포넌트를 작성하게 만드는 좋지 않은 패턴입니다. 재활용 가능한 컴포넌트를 작성하기 위해서 무엇인가 대책이 필요합니다.</p>
<h1 id="react-context">React Context</h1>
<p><img src="https://images.velog.io/post-images/public_danuel/b73ac230-6a75-11e9-81cf-1308fb80ddac/PropsDrillingvsReactContext16x.png" alt="Props_Drilling_vs_React_Context@16x.png"></p>
<p>이미지에서 알 수 있듯이 React Context를 이용하면 Main 컴포넌트와 Avatar 컴포넌트를 건너뛰고 User 컴포넌트에 Props를 바로 전달할 수 있습니다. 이를 위해서는 약간의 추상화를 해야 합니다.</p>
<pre><code class="language-jsx">import React, { createContext } from &#39;react&#39;

// 0. Context를 생성합니다.
const AppContext = createContext()

// 1. 상위 컴포넌트 안에서 AppContext.Provider 컴포넌트로 user를 전달합니다.
// 하위 컴포넌트는 AppContext.Provider 태그 안에 위치하게 합니다.
const App = () =&gt; {
  const user = {
    nickname: &#39;Danuel&#39;,
    isAdmin: true
  }

  return (
    &lt;AppContext.Provider value={user}&gt;
      &lt;div&gt;
        &lt;Main /&gt;
      &lt;/div&gt;
    &lt;/AppContext.Provider&gt;
  )
}

const Main = () =&gt; (
  &lt;main&gt;
    &lt;Avatar /&gt;
  &lt;/main&gt;
)

const Avatar = () =&gt; (
  &lt;div&gt;
    &lt;User /&gt;
  &lt;/div&gt;
)

// 2. AppContext.Consumer 컴포넌트를 이용해 Context에서 전달한 user를 사용합니다.
const User = () =&gt; (
  &lt;AppContext.Consumer&gt;
    {user =&gt; {
      let label = &#39;user&#39;
      if (user.isAdmin) {
        label = &#39;admin&#39;
      }

      return (
        &lt;div&gt;
          &lt;div&gt;{label}&lt;/div&gt;
          &lt;div&gt;{user.nickname}&lt;/div&gt;
        &lt;/div&gt;
      )
    }}
  &lt;/AppContext.Consumer&gt;
)</code></pre>
<p>React Hooks까지와는 조금씩 다른 부분이 있어서 복잡하게 보일 수 있지만,  크게 3단계로 나누어 살펴보면 쉽게 이해할 수 있습니다.</p>
<ol start="0">
<li><p>React 라이브러리에서 제공하는 createContext 함수를 불러와서 Context라는 이름의 State를 만들어준다.</p>
</li>
<li><p>해당 Context가 가지고 있는 Context.Provider 컴포넌트를 상위 컴포넌트를 감싸는 형태로 작성한다.</p>
</li>
<li><p>해당 Context를 사용하고자 하는 하위 컴포넌트에서 Context.Consumer로 감싸는 형태로 작성한다.</p>
<ul>
<li>Context.Consumer 태그 안에는 JSX를 return 하는 함수를 작성한다.</li>
</ul>
</li>
</ol>
<p>React Context를 이용하면 재활용 가능한 컴포넌트를 작성할 수 있다고 소개했습니다. 그 소개처럼 하위 컴포넌트에게 전달하기 위해서 상위 컴포넌트에게 Props를 받아들이는 부분이 없는 형태로 바뀌었습니다. 즉, Props Drilling 패턴을 떨쳐냈습니다.</p>
<pre><code class="language-jsx">import React, { createContext } from &#39;react&#39;

// ...

const Avatar = () =&gt; (
  &lt;div&gt;
    &lt;AppContext.Consumer&gt;{User}&lt;/AppContext.Consumer&gt;
  &lt;/div&gt;
)

const User = user =&gt; {
  let label = &#39;user&#39;
  if (user.isAdmin) {
    label = &#39;admin&#39;
  }

  return (
    &lt;div&gt;
      &lt;div&gt;{label}&lt;/div&gt;
      &lt;div&gt;{user.nickname}&lt;/div&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>추가적으로 개선한 부분만을 확인하기 위해 Avatar 컴포넌트와 User 컴포넌트만 따로 보여주는 예시 코드입니다.</p>
<p>이전 예시 코드와 다른 점은 Avatar 컴포넌트 안에서 Context.Consumer 컴포넌트를 사용했고, 그 컴포넌트 안에는 User 컴포넌트를 JSX 형태가 아니라 문자열, 숫자를 표시할 때와 같이 함수 형태로 바로 위치하게 작성했습니다. 컴포넌트는 함수의 형태를 하고 있기에 가능한 기법입니다.</p>
<p>이렇게 작성하는 경우는 많지 않지만, &#39;범용적으로 사용하고자 하는 컴포넌트라면 이렇게도 작성할 수 있다&#39; 는 것을 이해한다면 더욱 유연하고 깔끔한 컴포넌트를 작성할 수 있습니다.</p>
<h1 id="팁">팁</h1>
<pre><code class="language-jsx">import React, { createContext } from &#39;react&#39;

const Context = createContext()

const App = () =&gt; {
  const user = {
    nickname: &#39;Danuel&#39;,
    isAdmin: true
  }

  return (
    &lt;Context.Provider value={user}&gt;
      {/* ... */}
    &lt;/Context.Provider&gt;
  )
}</code></pre>
<p>App 컴포넌트가 지금은 user 데이터만 전달하는 로직을 가지고 있지만, Header 컴포넌트, Main 컴포넌트, Navigation 컴포넌트 등 하위 컴포넌트가 하나씩 늘어나다 보면 무엇을 하는 컴포넌트인지 알기 어려울 정도로 복잡하게 변합니다.</p>
<pre><code class="language-jsx">import React, { createContext } from &#39;react&#39;

const AppContext = createContext()

const AppProvider = ({ children }) =&gt; {
  const user = {
    nickname: &#39;Danuel&#39;,
    isAdmin: true
  }

  return (
    &lt;AppContext.Provider value={user}&gt;
      {children}
    &lt;/AppContext.Provider&gt;
  )
}

const App = () =&gt; (
  &lt;AppProvider&gt;
    {/* ... */}
  &lt;/AppProvider&gt;
)</code></pre>
<p>이 예시와 같이 AppProvider라는 별도의 컴포넌트를 작성해서 사용하면 보다 더 읽기 쉬운 컴포넌트를 작성할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 2-5. useReducer 알아보기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-usereducer</link>
            <guid>https://velog.io/@public_danuel/trendy-react-usereducer</guid>
            <pubDate>Thu, 18 Apr 2019 08:39:15 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 useReducer를 알아보겠습니다.</p>
<ul>
<li>useReducer는 State를 다루는 관점과 방법 정도의 차이 정도이고, useState로 작성할 때에 비해 작성해야 하는 코드의 양이 많으므로 어떻게 활용하면 좋을지 감을 잡기 어렵습니다.</li>
<li>이번 편은 이렇게 작성하는 방법이 있다는 정도만 이해해도 충분합니다.</li>
</ul>
<h1 id="usestate">useState</h1>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const useUser = () =&gt; {
  const [isAdmin, setBeAdmin] = useState(false)
  const [nickname, setNickname] = useState(&#39;&#39;)
  const [email, setEmail] = useState(&#39;&#39;)

  const reset = () =&gt; {
    setBeAdmin(false)
    setNickname(&#39;&#39;)
    setEmail(&#39;&#39;)
  }
  const toggleToBeAdmin = () =&gt; setBeAdmin(!admin)
  const updateNickname = event =&gt; {
    const nickname = event.target.value

    setNickname(nickname)
  }
  const updateEmail = event =&gt; {
    const email = event.target.value

    setEmail(email)
  }

  return {
    isAdmin,
    nickname,
    email,
    reset,
    toggleToBeAdmin,
    updateNickname,
    updateEmail
  }
}

const User = () =&gt; {
  const user = useUser()

  let label = &#39;user&#39;
  if (user.isAdmin) {
    label = &#39;admin&#39;
  }

  return (
    &lt;div&gt;
      &lt;label&gt;{label}&lt;/label&gt;
      &lt;h1&gt;{user.name}&lt;/h1&gt;
      &lt;h3&gt;{user.email}&lt;/h3&gt;
      &lt;button onClick={user.reset}&gt;RESET&lt;/button&gt;
      &lt;button onClick={user.toggleToBeAdmin}&gt;toggle admin mode&lt;/button&gt;
      &lt;input type=&#39;text&#39; onChange={user.updateNickname} /&gt;
      &lt;input type=&#39;text&#39; onChange={user.updateEmail} /&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>React는 State를 변경하면 바뀐 부분을 새로 그리기 위해 해당 하는 모든 컴포넌트를 다시 실행합니다.</p>
<p>간단한 프로젝트일 때에는 충분히 잘 작동하지만, 프로젝트의 스케일이 클수록 퍼포먼스가 중요하기 때문에 최적화에 신경을 써야 합니다.</p>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const useUser = () =&gt; {
  const [user, setUser] = useState({
    isAdmin: false,
    nickname: &#39;&#39;,
    email: &#39;&#39;
  })

  const reset = () =&gt; setUser({
    isAdmin: false,
    nickname: &#39;&#39;,
    email: &#39;&#39;
  })
  const toggleToBeAdmin = () =&gt; setUser(user =&gt; ({ ...user, isAdmin: !user.isAdmin }))
  const updateNickname = event =&gt; setUser(user =&gt; ({ ...user, nickname: event.target.value }))
  const updateEmail = event =&gt; setUser(user =&gt; ({ ...user, email: event.target.value }))

  return {
    isAdmin,
    nickname,
    email,
    reset,
    toggleToBeAdmin,
    updateNickname,
    updateEmail
  }
}

const User = () =&gt; {
  const user = useUser()

  let label = &#39;user&#39;
  if (user.isAdmin) {
    label = &#39;admin&#39;
  }

  return (
    &lt;div&gt;
      &lt;label&gt;{label}&lt;/label&gt;
      &lt;h1&gt;{user.name}&lt;/h1&gt;
      &lt;h3&gt;{user.email}&lt;/h3&gt;
      &lt;button onClick={user.reset}&gt;RESET&lt;/button&gt;
      &lt;button onClick={user.toggleToBeAdmin}&gt;toggle admin mode&lt;/button&gt;
      &lt;input type=&#39;text&#39; onChange={user.updateNickname} /&gt;
      &lt;input type=&#39;text&#39; onChange={user.updateEmail} /&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>React Hooks를 사용한다면 isAdmin, nickname, email와 같은 여러 State를 1개의 State로 줄이는 것이 가장 먼저 시도해볼 수 있는 최적화입니다.</p>
<p>1개의 State로 줄이고 보니 괜히 더 번잡하게 변해버린 느낌이 듭니다. 보다 더 일관적인 방법으로 작성할 수는 없을까요?</p>
<h1 id="usereducer">useReducer</h1>
<pre><code class="language-jsx">import React, { useReducer } from &#39;react&#39;

const initialUserState = {
  isAdmin: false,
  nickname: &#39;&#39;,
  email: &#39;&#39;
}

const userReducer = (state, action) =&gt; {
  switch (action.type) {
    case &#39;reset&#39;: {
      return initialUserState
    }
    case &#39;toggleToBeAdmin&#39;: {
      return { ...state, isAdmin: !state.isAdmin }
    }
    case &#39;updateNickname&#39;: {
      return { ...state, nickname: action.nickname }
    }
    case &#39;updateEmail&#39;: {
      return { ...state, email: action.email }
    }
    default: {
      throw new Error(`unexpected action.type: ${action.type}`)
    }
  }
}

const User = () =&gt; {
  const [user, dispatchUser] = useReducer(userReducer, initialUserState)

  let label = &#39;user&#39;
  if (user.isAdmin) {
    label = &#39;admin&#39;
  }

  const reset = () =&gt; dispatchUser({ type: &#39;reset&#39; })
  const toggleToBeAdmin = () =&gt; dispatchUser({ type: &#39;toggleToBeAdmin&#39; })
  const updateNickname = event =&gt; dispatchUser({ type: &#39;updateNickname&#39;, nickname: event.target.value })
  const updateEmail = event =&gt; dispatchUser({ type: &#39;updateEmail&#39;, email: event.target.value })

  return (
    &lt;div&gt;
      &lt;label&gt;{label}&lt;/label&gt;
      &lt;h1&gt;{user.name}&lt;/h1&gt;
      &lt;h3&gt;{user.email}&lt;/h3&gt;
      &lt;button onClick={reset}&gt;RESET&lt;/button&gt;
      &lt;button onClick={toggleToBeAdmin}&gt;toggle admin mode&lt;/button&gt;
      &lt;input type=&#39;text&#39; onChange={updateNickname} /&gt;
      &lt;input type=&#39;text&#39; onChange={updateEmail} /&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>useReducer를 이용하면 이렇게 작성할 수 있습니다.</p>
<p>useState와 비슷하게 return 배열의 1번째에는 State가, 2번째에는 State를 변경하는 함수가 있습니다.</p>
<p>useState의 State를 변경하는 함수는 넘긴 값을 그대로 다음 State로 사용하지만, useReducer의 State를 변경하는 함수는 reducer를 거치면서 추가적으로 가공한 State로 사용할 수 있습니다.</p>
<p>살펴보았듯이, 일관적인 구조를 가지고 있기 때문에 코드의 양이 늘어났음에도 useState를 사용하는 것 보다 로직을 파악하기가 쉽고 보다 더 체계적이라는 느낌을 받을 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 2-4. useMemo 알아보기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-usememo</link>
            <guid>https://velog.io/@public_danuel/trendy-react-usememo</guid>
            <pubDate>Thu, 18 Apr 2019 08:37:57 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 퍼포먼스 최적화와 가독성에 도움을 주는 useMemo를 알아보겠습니다.</p>
<h1 id="usememo">useMemo</h1>
<pre><code class="language-jsx">import React, { useEffect, useState } from &#39;react&#39;

const User = () =&gt; {
  const [nickname, setNickname] = useState(&#39;&#39;)
  const [nicknameLength, setNicknameLength] = useState(0)

  const updateNicknameLength = () =&gt; {
    setNicknameLength(nickname.length)
  }
  useEffect(updateNicknameLength, [nickname])

  const updateNickname = event =&gt; {
    const nickname = event.target.value

    setNickname(nickname)
  }

  return (
    &lt;div&gt;
      &lt;input onChange={updateNickname} /&gt;
      &lt;label&gt;{nicknameLength}&lt;/label&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>nickname을 변경하면 그것을 자동으로 nicknameLength를 업데이트하는 컴포넌트입니다.</p>
<p>이와 비슷한 형태로 작성하는 경우는 꽤 많지만, 기능에 비해서 코드가 번잡한 느낌입니다. useEffect는 업데이트가 끝난 이후에 작동하기 때문에 한 번 더 업데이트를 한다는 이슈도 있으므로 퍼포먼스가 중요하다면 더 나은 방법이 필요합니다.</p>
<pre><code class="language-jsx">import React, { useMemo, useState } from &#39;react&#39;

const User = () =&gt; {
  const [nickname, setNickname] = useState(&#39;&#39;)
  const nicknameLength = useMemo(() =&gt; nickname.length, [nickname])

  const updateNickname = event =&gt; {
    const nickname = event.target.value

    setNickname(nickname)
  }

  return (
    &lt;div&gt;
      &lt;input onChange={updateNickname} /&gt;
      &lt;label&gt;{nicknameLength}&lt;/label&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>useMemo 함수를 사용하면 깔끔하게 작성할 수 있습니다. </p>
<p>useEffect처럼 2번째 파라미터에 전달한 배열이 이전의 배열과 다르면 1번쨰 파라미터가 발동하며, useState와는 다르게 해당 State를 변경하는 함수를 제공하지 않습니다.</p>
<ul>
<li>2번째 파라미터를 전달하지 않으면 State가 바뀔 때 마다 1번째 파라미터로 전달한 함수를 항상 실행하기 때문에 주의해야 합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 2-3. useRef 알아보기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-useref</link>
            <guid>https://velog.io/@public_danuel/trendy-react-useref</guid>
            <pubDate>Thu, 18 Apr 2019 08:36:20 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 Ref와 React Hooks의 기본 API 중 하나인 useRef를 알아보겠습니다.</p>
<h1 id="documentgetelementbyid">document.getElementById</h1>
<pre><code class="language-jsx">import React from &#39;react&#39;

const User = () =&gt; {
  const requestToLogin = event =&gt; {
    event.preventDefault()

    const idReference = document.getElementById(&#39;id&#39;)
    const passwordReferenece = document.getElementById(&#39;password&#39;)

    const id = idReference.target.value
    const password = passwordReferenece.target.value

    // a AJAX logic
  }

  return (
    &lt;form onSubmit={requestToLogin}&gt;
      &lt;label&gt;
        id:
        &lt;input id=&#39;id&#39; type=&#39;text&#39; /&gt;
      &lt;/label&gt;
      &lt;label&gt;
        password:
        &lt;input id=&#39;password&#39; type=&#39;password&#39; /&gt;
      &lt;/label&gt;
      &lt;button type=&#39;submit&#39;&gt;로그인!&lt;/button&gt;
    &lt;/form&gt;
  )
}</code></pre>
<p>컴포넌트를 작성하다 보면 태그를 직접 다루어야 하는 경우도 있습니다.</p>
<p>해당 프로젝트 안에서 유일한 id를 지정한다면 <code>document.getElementById</code> 메서드로 직접 Element를 불러와서 사용할 수 있습니다.</p>
<h1 id="ref">Ref</h1>
<p>간단한 컴포넌트라면 <code>document.getElementById</code> 메서드를 이용할 수도 있곘지만, 테이블처럼 반복 컴포넌트인 경우에는 아무래도 한계와 React스럽지 않다는 찜찜함도 남습니다.</p>
<p>다행히도 React는 해결할 수 있는 방법을 제공합니다.</p>
<pre><code class="language-jsx">import React, { createRef, useState } from &#39;react&#39;

const User = () =&gt; {
  const [idReference, setIdReference] = useState(() =&gt; createRef())
  const [passwordReferenece, setPasswordReference] = useState(() =&gt; createRef())

  const requestToLogin = event =&gt; {
    event.preventDefault()

    const id = idReference.current.target.value
    const password = passwordReferenece.current.target.value

    // a AJAX logic
  }

  return (
    &lt;form onSubmit={requestToLogin}&gt;
      &lt;label&gt;
        id:
        &lt;input ref={idReference} type=&#39;text&#39; /&gt;
      &lt;/label&gt;
      &lt;label&gt;
        password:
        &lt;input ref={passwordReference} type=&#39;password&#39; /&gt;
      &lt;/label&gt;
      &lt;button type=&#39;submit&#39;&gt;로그인!&lt;/button&gt;
    &lt;/form&gt;
  )
}</code></pre>
<p>useState를 이용하여 createRef 함수를 State로 생성하고 해당 State를 ref 속성에 지정하면 해당 State의 <code>.current</code> 를 통해서 사용할 수 있습니다.</p>
<h1 id="custom-hooks로-감싸기">Custom Hooks로 감싸기</h1>
<p>createRef 함수로 생성한 State는 직접 Element를 지정해줄 일이 없기 때문에 useState의 2번째 return 값이 필요가 없으므로, Custom Hooks로 분리하면 깔끔하게 작성할 수 있습니다.</p>
<pre><code class="language-jsx">import React, { createRef, useState } from &#39;react&#39;

const useReference = () =&gt; {
  const [reference, setReference] = useState(() =&gt; createRef())

  return reference
}

const User = () =&gt; {
  const idReferenece = useReference()
  const passwordReference = useReference()

  // ...

  return (
    &lt;form&gt;
      &lt;label&gt;
        id:
        &lt;input ref={idReference} type=&#39;text&#39; /&gt;
      &lt;/label&gt;
      &lt;label&gt;
        password:
        &lt;input ref={passwordReference} type=&#39;password&#39; /&gt;
      &lt;/label&gt;
      &lt;button type=&#39;submit&#39;&gt;로그인!&lt;/button&gt;
    &lt;/form&gt;
  )
}</code></pre>
<h1 id="useref">useRef</h1>
<p>Custom Hooks로 작성하면 보다 깔끔하게 사용할 수 있지만, useReference는 자주 사용할 Custom Hooks인데 모든 프로젝트 마다 추가하는 것은 아무래도 번거로운 작업입니다.</p>
<p>React는 고맙게도 useRef 라는 이러한 기능을 지원합니다.</p>
<pre><code class="language-jsx">import React, { useRef } from &#39;react&#39;

const User = () =&gt; {
  const idReferenece = useRef()
  const passwordReference = useRef()

  // ...

  return (
    &lt;form&gt;
      &lt;label&gt;
        id:
        &lt;input ref={idReference} type=&#39;text&#39; /&gt;
      &lt;/label&gt;
      &lt;label&gt;
        password:
        &lt;input ref={passwordReference} type=&#39;password&#39; /&gt;
      &lt;/label&gt;
      &lt;button type=&#39;submit&#39;&gt;로그인!&lt;/button&gt;
    &lt;/form&gt;
  )
}</code></pre>
<h1 id="forwardref">forwardRef</h1>
<pre><code class="language-jsx">import React, { useRef } from &#39;react&#39;

const User = () =&gt; {
  const idReferenece = useRef()
  const passwordReference = useRef()

  // ...

  return (
    &lt;form&gt;
      &lt;LabelInput text=&#39;id:&#39; type=&#39;text&#39; ref={idReference} /&gt;
      &lt;LabelInput text=&#39;password:&#39; type=&#39;password&#39; ref={passwordReference} /&gt;
    &lt;/form&gt;
  )
}

const LabeledInput = ({ text, type, ref }) =&gt; (
  &lt;label&gt;
    {text}
    &lt;input type={type} ref={ref} /&gt;
  &lt;/label&gt;
)</code></pre>
<p>컴포넌트를 작성하다 보면 하위 컴포넌트의 Element가 필요한 경우도 있지만, ref 속성은 React에서 특별한 속성이기 때문에 위처럼 작성하면 원하는 동작을 하지 않습니다.</p>
<pre><code class="language-jsx">import React, { forwardRef, useRef } from &#39;react&#39;

const User = () =&gt; {
  const idReferenece = useRef()
  const passwordReference = useRef()

  // ...

  return (
    &lt;form&gt;
      &lt;LabelInput text=&#39;id:&#39; type=&#39;text&#39; ref={idReference} /&gt;
      &lt;LabelInput text=&#39;password:&#39; type=&#39;password&#39; ref={passwordReference} /&gt;
    &lt;/form&gt;
  )
}

const LabeledInput = forwardRef(({ text, type }, ref) =&gt; (
    &lt;label&gt;
    {text}
    &lt;input type={type} ref={ref} /&gt;
  &lt;/label&gt;
))</code></pre>
<p>여러 방법이 있지만 React가 지원하는 forwardRef 함수를 이용해서 해결하는 방법이 가장 깔끔합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 2-2. Custom Hooks 알아보기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-custom-hooks</link>
            <guid>https://velog.io/@public_danuel/trendy-react-custom-hooks</guid>
            <pubDate>Thu, 18 Apr 2019 08:35:04 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 React Hooks를 조금 더 유연하게 사용할 수 있는 방법인 Custom Hooks를 알아보겠습니다.</p>
<h1 id="custom-hooks-기본-형태">Custom Hooks 기본 형태</h1>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const User = () =&gt; {
  const [nickname, setNickname] = useState(&#39;&#39;)

  const updateNickname = event =&gt; {
    const nickname = event.target.value

    setNickname(nickname)
  }

  return (
    &lt;div&gt;
      &lt;label&gt;{nickname}&lt;/label&gt;
      &lt;input value={nickname} onChange={updateNickname} /&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>이번에는 nickname을 변경하면 label에도 반영하는 컴포넌트입니다. React Hooks를 이용하여 State를 만들고, State를 변경하는 함수를 만들고, 그것을 태그에 할당하는 형태입니다.</p>
<p>지금은 간단한지만, 길이 체크, 알파벳과 숫자 체크, API 요청 등 로직을 하나씩 추가하기 시작하면 복잡하게 변하는 것은 금방입니다.</p>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const useUser = () =&gt; {
  const [nickname, setNickname] = useState(&#39;&#39;)
  const [isAdmin, setBeAdmin] = useState(false)

  const updateNickname = event =&gt; {
    const nickname = event.target.value

    setNickname(nickname)
  }

  return [nickname, updateNickname]
}

const User = () =&gt; {
  const [nickname, setNickname] = useUser()

  return (
    &lt;div&gt;
      &lt;label&gt;{nickname}&lt;/label&gt;
      &lt;input value={nickname} onChange={setNickname} /&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>이 예시와 같이 별도의 함수로 분리해서 작성할 수 있으며, 이것을 Custom Hooks라고 합니다.</p>
<p>이 패턴은 당장에 작성해야 할 코드는 많지만 장기적으로 아래의 장점을 얻을 수 있습니다.</p>
<ul>
<li>컴포넌트와 로직을 분리할 수 있습니다.</li>
<li>컴포넌트와 로직을 조합하는 형태로 작성할 수 있습니다.</li>
<li>여러 컴포넌트에서 재활용이 가능하기 때문에 중복 코드를 줄일 수 있습니다.</li>
<li>여러 React 프로젝트를 진행한다면 효용성 좋은 Custom Hooks를 공유하며 기술적 경험을 축적할 수 있습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 2-1. React Hooks 기본-하]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-hooks-basic-1</link>
            <guid>https://velog.io/@public_danuel/trendy-react-hooks-basic-1</guid>
            <pubDate>Thu, 18 Apr 2019 08:33:48 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 useState 함수와 useEffect 함수를 조금 더 유연하게 사용하는 방법을 알아보겠습니다.</p>
<h1 id="usestate">useState</h1>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const ExpensiveComponent = () =&gt; {
  const [someValue, setSomeValue] = useState(() =&gt; {
    let value = 0
    for (let index = 0; index &lt; 100_0000; index += 1) {
      value += 1
    }
    return value
  })

  return &lt;div&gt;{someValue}&lt;/div&gt;
}</code></pre>
<p>useState는 초기화 할 때에만 함수를 1번 실행하는 방식을 지원합니다.</p>
<p>처음 1번만 함수를 실행을 하며, 그 함수의 return 값을 State로 사용합니다.</p>
<h1 id="useeffect">useEffect</h1>
<p><img src="https://images.velog.io/post-images/public_danuel/70ab8fd0-61b4-11e9-874c-3d964a464aaa/CounterTitleToggle.gif" alt="CounterTitle_Toggle.gif"></p>
<pre><code class="language-jsx">import React, { useEffect, useState } from &#39;react&#39;

const Counter = () =&gt; {
  const [nickname, setNickname] = useState(&#39;Danuel&#39;)
  const [count, setCount] = useState(0)

  useEffect(() =&gt; {
    document.title = nickname
  })

  const toggleNickname = () =&gt; {
    if (nickname === &#39;Danuel&#39;) {
      setNickname(&#39;Other&#39;)
    } else {
      setNickname(&#39;Danuel&#39;)
    }
  }

  const decreaseCount = () =&gt; setCount(count - 1)
  const increaseCount = () =&gt; setCount(count + 1)

  return (
    &lt;div&gt;
      &lt;h1&gt;{nickname}&lt;/h1&gt;
      &lt;button onClick={toggleNickname}&gt;toggleNickname&lt;/button&gt;
      &lt;h3&gt;{count}&lt;/h3&gt;
      &lt;button onClick={decreaseCount}&gt;- 1&lt;/button&gt;
      &lt;button onClick={increaseCount}&gt;+ 1&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>이번에는 카운트 기능과 타이틀을 바꾸는 기능을 가진 컴포넌트입니다.</p>
<p>이 컴포넌트는 원하는대로 작동은 잘 하지만, 찬찬히 살펴보면 카운트를 변경할 때에도 title을 변경하는 비효율적인 부분이 있습니다.</p>
<p>잠시 생각해보면 이전의 State와 현재의 State가 다를 때에만 useEffect 함수가 발동하는 것이 가장 효율적입니다.</p>
<pre><code class="language-jsx">...
useEffect(() =&gt; {
  document.title = nickname
}, [nickname])
...</code></pre>
<p>nickname을 배열에 담아 2번째 파라미터에 추가하면 이전의 State와 현재의 State가 다를 때에만 발동합니다.</p>
<pre><code class="language-jsx">...
useEffect(() =&gt; {
  document.title = nickname
}, [])
...</code></pre>
<p>빈 배열을 2번째 파라미터에 추가하면 처음 딱 1번만 발동합니다.</p>
<h1 id="비동기적인-state-변경">비동기적인 State 변경</h1>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const SECOND = 1000

const AsynchronousCounter = () =&gt; {
  const [nickname, setNickname] = useState(&#39;Danuel&#39;)
  const [count, setCount] = useState(0)

  const decreaseCount = () =&gt; {
    window.setTimeout(() =&gt; setCount(count - 1), 3 * SECOND)
  }
  const increaseCount = () =&gt; {
    window.setTimeout(() =&gt; setCount(count + 1), 3 * SECOND)
  }

  return (
    &lt;div&gt;
      &lt;p&gt;{nickname}&lt;/p&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button onClick={decreaseCount}&gt;- 1&lt;/button&gt;
      &lt;button onClick={increaseCount}&gt;+ 1&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>이번에는 비동기적으로 State를 변경해보겠습니다.</p>
<p><code>- 1</code> 버튼 혹은 <code>+ 1</code> 버튼을 클릭하면 3초 후에 State를 바꾸는 컴포넌트입니다.</p>
<p><img src="https://images.velog.io/post-images/public_danuel/7821a830-61b4-11e9-bf0a-7575a906d2e9/AsyncCountBad.gif" alt="AsyncCount_Bad.gif"></p>
<p>연속적으로 클릭한 후에 값을 살펴보면 예상과는 다른 작동을 합니다. 이것은 비동기와 
관련이 있습니다.</p>
<p>버튼을 클릭하면 3초 후에 State를 변경하는 함수를 실행하는데, 이 순간에 들어가는 값은 버튼을 클릭한 시점의 State로 이미 결정해놓았기 때문입니다.</p>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const SECOND = 1000

const AsynchronousCounter = () =&gt; {
  // ...

  const decreaseCount = () =&gt; {
    window.setTimeout(() =&gt; setCount(previousCount =&gt; previousCount - 1), 3 * SECOND)
  }
  const increaseCount = () =&gt; {
    window.setTimeout(() =&gt; setCount(previousCount =&gt; previousCount + 1), 3 * SECOND)
  }

  // ...
}</code></pre>
<p>이런 이슈를 해결하기 위해서 React에서는 State를 변경하는 함수의 파라미터에 함수를 넘기는 방법도 지원합니다. 언뜻 보면 조금 복잡하게 보이지만, 바뀐 코드만 보면 setCount 함수에 함수를 넘겨준 것 뿐입니다.</p>
<p>코드에서 보듯이, 함수로 넘겨줄 때에는 &#39;직전&#39;의 State를 입력으로 받고 다음 State를 return 하는 형태입니다.</p>
<p><img src="https://images.velog.io/post-images/public_danuel/7be4cab0-61b4-11e9-bf0a-7575a906d2e9/AsyncCountGood.gif" alt="AsyncCount_Good.gif"></p>
<p>간단한 변경이지만 이렇게 잘 작동합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 2-0. React Hooks 기본-상]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-hooks-basic-0</link>
            <guid>https://velog.io/@public_danuel/trendy-react-hooks-basic-0</guid>
            <pubDate>Thu, 18 Apr 2019 08:27:12 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 상태State 개념과 React Hooks를 소개하고, useState와 useEffect를 작성하는 방법을 알아보겠습니다.</p>
<h1 id="상태state">상태State</h1>
<p>State는 Props와 함께 React의 핵심 개념입니다. Props는 상위 컴포넌트에서 하위 컴포넌트에게 데이터를 전달하는 것이라 하면, State는 컴포넌트 자체적으로 값을 가지고 있는 데이터입니다.</p>
<p>직접적으로 State를 변경하는 것은 많은 복잡성이 발생하기 때문에 React 자체적으로 변경하는 방법을 제공합니다.</p>
<h1 id="react-hooks">React Hooks</h1>
<p>React v16.8 이전까지는 State를 가질 수 없었던 Function 컴포넌트에도 State를 가질 수 있게 해주는 개념 및 React가 지원하는 함수 중 use로 시작하는 것을 통칭하는 명칭입니다.</p>
<ul>
<li>useState</li>
<li>useEffect</li>
<li>useRef</li>
<li>useMemo</li>
<li>useReducer</li>
</ul>
<p>이 외에도 더 많은 함수가 있지만, 이 정도만 해도 충분히 많은 기능을 구현할 수 있으므로 React Hooks 섹션에서는 위 5개만 소개하겠습니다.</p>
<h1 id="usestate">useState</h1>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const Counter = () =&gt; {
  const [count, setCount] = useState(0)

  return (
    &lt;div&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button onClick={() =&gt; setCount(count - 1)}&gt;- 1&lt;/button&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;+ 1&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>useState를 불러와서 컴포넌트 안에서 사용하는 형태입니다.</p>
<p>useState 함수 return 값은 배열이며, 1번째에는 useState에 넘겨준 값이, 2번째에는 값을 변경할 수 있는 함수가 있습니다.</p>
<p><img src="https://images.velog.io/post-images/public_danuel/93dd1290-61b3-11e9-bc0c-05c13371725b/Count.gif" alt="Count.gif"></p>
<p>실제로 작성을 해서 확인을 해보면 처음에는 0이 보고, <code>- 1</code> 버튼을 클릭하면 클릭한 횟수 만큼 수가 내려가고, <code>+ 1</code> 버튼을 클릭하면 클릭한 횟수 만큼 수가 올라갑니다.</p>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const Counter = () =&gt; {
  const [nickname, setNickname] = useState(&#39;Danuel&#39;)
  const [count, setCount] = useState(0)

  return (
    &lt;div&gt;
      &lt;p&gt;{nickname}&lt;/p&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button onClick={() =&gt; setCount(count - 1)}&gt;- 1&lt;/button&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;+ 1&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>컴포넌트 안에서 useState는 여러 번 사용할 수 있으며, 이는 여러 State를 가질 수 있다는 것을 의미한다고 할 수 있습니다.</p>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const Counter = () =&gt; {
  const [nickname, setNickname] = useState(&#39;Danuel&#39;)
  const [count, setCount] = useState(0)

  const decreaseCount = () =&gt; setCount(count - 1)
  const increaseCount = () =&gt; setCount(count + 1)

  return (
    &lt;div&gt;
      &lt;p&gt;{nickname}&lt;/p&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button onClick={decreaseCount}&gt;- 1&lt;/button&gt;
      &lt;button onClick={increaseCount}&gt;+ 1&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>간단한 함수인 경우에는 속성property에 직접 함수를 작성해도 큰 무리 없이 읽을 수 있지만, 함수의 개수가 늘어나고 구현부가 길수록 읽기가 난해하므로 예시처럼 유의미한 이름을 가진 변수로 작성하여 사용하는 것을 권장합니다.</p>
<ul>
<li>이 예시로 보면, &quot;<code>- 1</code> 버튼을 클릭했을 때에는 decreaseCount를 하는구나!&quot; 하고 코드를 쉽게 읽을 수 있습니다.</li>
</ul>
<h1 id="useeffect">useEffect</h1>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;

const Counter = () =&gt; {
  const [nickname, setNickname] = useState(&#39;Danuel&#39;)
  const [count, setCount] = useState(0)

  const decreaseCount = () =&gt; {
    setCount(count - 1)
    document.title = count - 1
  }
  const increaseCount = () =&gt; {
    setCount(count + 1)
    document.title = count + 1
  }

  return (
    &lt;div&gt;
      &lt;p&gt;{nickname}&lt;/p&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button onClick={decreaseCount}&gt;- 1&lt;/button&gt;
      &lt;button onClick={increaseCount}&gt;+ 1&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>이번에는 카운트를 변경할 때 마다 title을 변경하는 기능을 추가해보겠습니다.</p>
<p>decreaseCount 함수와 increaseCount 함수에 title을 변경하는 코드를 작성했습니다.</p>
<p><img src="https://images.velog.io/post-images/public_danuel/98d5a7d0-61b3-11e9-bf0a-7575a906d2e9/CounterTitleBad.gif" alt="CounterTitle_Bad.gif"></p>
<p>이미지에서 알 수 있듯이, 첫화면에서는 title이 count가 아니라 기본으로 설정해놓은 값이 보이는 이슈가 있습니다.</p>
<p>일부만 다른 중복 코드도 탐탁치 않습니다. 결국 보면 count로 변경하는 것인데 변경하는 시점에 값을 결정하기 때문에 일부만 바꾸어서 작성을 해야 합니다.</p>
<p>확장성 부분에서도 만족스러운 구조는 아닙니다. 지금은 1씩 빼거나 더하는 기능이지만, 10씩 빼거나 더하는 기능, 100씩 빼거나 더하는 기능 등 기능이 늘어나면 그에 일부만 다른 중복 코드를 여기저기에 작성해줘야 합니다.</p>
<p>React가 이러한 이슈를 모를리 없습니다.</p>
<pre><code class="language-jsx">import React, { useEffect, useState } from &#39;react&#39;

const Counter = () =&gt; {
  const [nickname, setNickname] = useState(&#39;Danuel&#39;)
  const [count, setCount] = useState(0)

  const decreaseCount = () =&gt; {
    setCount(count - 1)
  }
  const increaseCount = () =&gt; {
    setCount(count + 1)
  }

  useEffect(() =&gt; {
    document.title = count
  })

  return (
    &lt;div&gt;
      &lt;p&gt;{nickname}&lt;/p&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button onClick={decreaseCount}&gt;- 1&lt;/button&gt;
      &lt;button onClick={increaseCount}&gt;+ 1&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>useEffect 함수를 이용하면 위에서 언급한 여러 이슈를 깔끔하게 해결할 수 있습니다.</p>
<p>useEffect는 해당 컴포넌트의 연산이 끝난 이후 함수를 실행합니다. 거칠게 표현하자면 화면에 그리는 작업이 끝난 이후에 useEffect 함수가 발동한다고 할 수 있습니다.</p>
<p>예시 코드에는 useEffect 함수 안에 title을 변경하는 코드가 있습니다. 이 코드는 Counter 컴포넌트를 화면에 그린 이후 작동합니다.</p>
<p><img src="https://images.velog.io/post-images/public_danuel/9cdc8a10-61b3-11e9-bc0c-05c13371725b/CounterTitleGood.gif" alt="CounterTitle_Good.gif"></p>
<p>이미지에서 보듯이, 첫화면에는 title이 0이고 버튼을 누를 때 마다 title도 그에 맞게 바뀌는 동작을 합니다.</p>
<p>버튼을 클릭하면 count가 바뀌고, 조금 뒤늦게 title도 따라 바뀝니다. 이 부분이 바로 &#39;해당 컴포넌트의 연산이 끝난 이후 함수를 실행한다&#39;는 의미입니다.</p>
<h1 id="사용-규칙">사용 규칙</h1>
<p>이렇게 직관적이고 편리한 React Hooks이지만 꼭 지켜야 하는 규칙이 있습니다.</p>
<ol>
<li><p>선택적 실행 금지only call hooks at the top level</p>
<pre><code class="language-jsx">import React, { useEffect, useState } from &#39;react&#39;

const User = () =&gt; {
  const [nickname, setNickname] = useState(&#39;Danuel&#39;)

  if (nickname === &#39;Unknown&#39;) {
    useEffect(() =&gt; {
      // ...
    })
  }

  return &lt;h1&gt;{nickname}&lt;/h1&gt;
}</code></pre>
<p>위처럼 어떤 상태에 의해 React Hooks 함수를 실행하면 예상과는 다른 동작을 하는 등 &#39;정상적인 작동&#39;을 보장하지 않습니다. React Hooks는 useState, useEffect 등을 실행하는 순서와 깊은 연관이 있기 때문입니다.</p>
</li>
<li><p>Function 컴포넌트에서만 사용 가능</p>
<p>React의 컴포넌트는 2종류가 있습니다. 그 중 Class 컴포넌트는 React Hooks와는 별개로 가지고 있는 상태관리 방법이 있으므로, 복잡성 등의 이유로 사용할 수 없습니다.</p>
<p>실제로 Class 컴포넌트에서 사용을 해보고자 해도 작동을 하지 않거나 예상과는 다른 동작을 합니다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 1-5. 이벤트 알아보기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-event</link>
            <guid>https://velog.io/@public_danuel/trendy-react-event</guid>
            <pubDate>Tue, 16 Apr 2019 17:37:35 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 HTML에서 이벤트를 작성하는 방법을 잠시 살펴본 후, React에서 이벤트를 작성하는 방법을 알아보겠습니다.</p>
<h1 id="html에서의-이벤트">HTML에서의 이벤트</h1>
<pre><code class="language-html">&lt;button onclick=&quot;console.log(&#39;clicked!&#39;)&quot;&gt;click!&lt;/button&gt;</code></pre>
<p>HTML에서는 이벤트를 위와 같은 형태로 작성합니다. 특징으로는 이벤트의 이름은 항상 소문자만 있습니다.</p>
<p>해당 태그를 클릭하면 발동하는 onclick 이벤트 외에 onmouseenter, onmouseleave 등 무수히 많은 다양한 이벤트가 있으며, input 태그는 onchange, onfocus, onblur 등 텍스트 입력와 관련한 이벤트를 추가적으로 사용할 수 있습니다.</p>
<p>이러한 이벤트는 유저와 상호작용하는 웹페이지, 더 나아가서는 웹앱을 구현할 때에 굉장히 중요하므로 필수적인 기능이자 개념이라 할 수 있습니다.</p>
<h1 id="react에서의-이벤트">React에서의 이벤트</h1>
<pre><code class="language-jsx">import React from &#39;react&#39;

const App = () =&gt; (
  &lt;button onClick={event =&gt; console.log(&#39;clicked!&#39;)}&gt;click!&lt;/button&gt;
)</code></pre>
<p>React에서의 이벤트는 앞서 알아본 HTML에서의 이벤트와 비슷한 형태로 작성할 수 있습니다.</p>
<p>HTML에서의 이벤트와 크게 다른 부분은 <code>onClick</code> , <code>onMouseEnter</code> , <code>onMouseLeave</code> , <code>onChange</code> 등과 같이 단어의 첫부분은 대문자이고, <code>함수</code> 를 전달해야 한다는 것입니다.</p>
<p>Chrome에서 버튼을 클릭하면 <code>clicked!</code> 라는 메시지가의 DevTools의 콘솔console창에 나타나는 것을 볼 수 있습니다.</p>
<p>위 코드에서는 event라는 파라미터는 사용하지 않았지만, 이벤트가 발동하면 받을 수 있는 파라미터입니다. 해당 이벤트에 대한 데이터를 가지고 있는 객체입니다.</p>
<pre><code class="language-jsx">import React from &#39;react&#39;

const App = () =&gt; (
  &lt;input type=&#39;text&#39; onChange={event =&gt; console.log(event.target.value)} /&gt;
)</code></pre>
<p>위 예시처럼 input 태그의 onChange 이벤트에 함수를 전달하면 유저가 텍스트를 입력할 때 마다 바뀐 텍스트를 받을 수 있습니다.</p>
<h1 id="작성-가이드">작성 가이드</h1>
<pre><code class="language-jsx">import React from &#39;react&#39;

const App = () =&gt; (
  &lt;input type=&#39;text&#39; onChange={event =&gt; {
    const nickname = event.target.value
    console.log(nickname)
    console.log(&#39;nickname length: &#39;, nickname.length)
  }} /&gt;
)</code></pre>
<p>이벤트에 직접 함수를 작성하면 그 당시에는 편하지만, 다른 사람과 협업을 하거나 나중에 다시 읽을 때에는 의미를 이해하기 어렵다는 이슈가 있습니다. 무엇인가 나은 방법이 필요합니다.</p>
<pre><code class="language-jsx">import React from &#39;react&#39;

const App = () =&gt; {
  const logNickname = event =&gt; {
    const nickname = event.target.value
    console.log(nickname)
    console.log(&#39;nickname length: &#39;, nickname.length)
  }

  return (
    &lt;input type=&#39;text&#39; onChange={logNickname} /&gt;
  )
}</code></pre>
<p>이 예시처럼 함수에 이름을 지어 이벤트에 전달하면 구현부를 보지 않아도 어떤 역할을 하는지 가늠할 수 있습니다.</p>
<h1 id="못다한-이야기">못다한 이야기</h1>
<ol>
<li><p>이번 편에서 살펴본 이벤트는 사실 DOM Event이며, 디자인 패턴 중 일부인 Event와는 다른 부분이 있습니다. 조금 더 자세히 이해하고 싶다면 DOM에 대해 조금 더 알아보는 것을 권장합니다.</p>
</li>
<li><p>onClick, onMouseEnter와 같이 단어의 첫글자를 대문자로 표기하는 방식을 camelCase 표기법이라고 합니다. 낙타 등의 혹이 솟은 것과 비슷하다고 해서 붙은 이름입니다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 1-4. 반복 컴포넌트 작성하기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-multiple-components</link>
            <guid>https://velog.io/@public_danuel/trendy-react-multiple-components</guid>
            <pubDate>Tue, 16 Apr 2019 17:35:02 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 반복 컴포넌트multiple components 작성하는 방법을 알아보겠습니다.</p>
<h1 id="기본-형태">기본 형태</h1>
<pre><code class="language-jsx">import React from &#39;react&#39;

const Animals = () =&gt; {
  const animalList = [&#39;dog&#39;, &#39;cat&#39;, &#39;tiger&#39;]

  return (
    &lt;ol&gt;
      &lt;li&gt;
        &lt;span&gt;{animalList[0]}&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;span&gt;{animalList[1]}&lt;/span&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;span&gt;{animalList[2]}&lt;/span&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  )
}</code></pre>
<p>동물의 이름을 순차적으로 보여주는 컴포넌트입니다.</p>
<p>이 예시처럼 배열의 길이가 작고 항상 고정적이라면 이렇게 작성하는 것도 괜찮겠지만, 배열의 길이가 크거나 비고정적이라면 이렇게 작성하는 것으로는 분명히 한계가 있습니다.</p>
<p>반복적으로 나타나는 부분은 li 태그이니 이 부분을 배열의 map 메서드를 이용하면 유연하게 작성을 하면 좋을듯 합니다.</p>
<pre><code class="language-jsx">import React from &#39;react&#39;

const Animals = () =&gt; {
  const animalList = [&#39;dog&#39;, &#39;cat&#39;, &#39;tiger&#39;]

  return (
    &lt;ol&gt;
      {animalList.map((animal, index) =&gt; (
        &lt;li key={index}&gt;
          &lt;span&gt;{animal}&lt;/span&gt;
        &lt;/li&gt;
      ))}
    &lt;/ol&gt;
  )
}</code></pre>
<p>배열의 길이에 상관 없이 반복 컴포넌트로 바뀌었습니다.</p>
<p>그러고 보니 li 태그에 key 속성이 있는데, React에서 배열의 map 메서드를 이용해 반복 컴포넌트를 작성하는 경우 필수적으로 추가해줘야 하는 특별한 속성입니다.</p>
<pre><code class="language-jsx">import React from &#39;react&#39;

const Animal = ({ name }) =&gt; (
  &lt;li&gt;
    &lt;span&gt;{name}&lt;/span&gt;
  &lt;/li&gt;
)

const Animals = () =&gt; {
  const animalList = [&#39;dog&#39;, &#39;cat&#39;, &#39;tiger&#39;]

  return (
    &lt;ol&gt;
      {animalList.map((animal, index) =&gt; (
        &lt;Animal key={index} name={animal} /&gt;
      ))}
    &lt;/ol&gt;
  )
}</code></pre>
<p>커스텀 컴포넌트를 사용할 때에도 작성을 해줘야 하며, 내부가 아니라 외부에 작성을 해줘야 합니다.</p>
<h1 id="key가-필요한-이유">key가 필요한 이유</h1>
<p><a href="https://velog.io/@public_danuel/trendy-react-0-0">0-0. 개요#간단한_원리</a>에서 소개했듯이 React는 상태가 바뀌면 바뀐 부분만 새로 그리는데, 반복 컴포넌트인 경우는 key 속성에 지정한 값을 기준으로 기존의 컴포넌트인지 판단합니다.</p>
<ul>
<li>즉, 이전의 key 값과 새로운 key 값이 같으면 기존의 컴포넌트이니 새로 그리지 않습니다.</li>
</ul>
<p>따라서, 배열 안에서 key 값은 중복이 없는 유일한 값이어야 합니다</p>
<ul>
<li>중복이 있으면 이전에 그린 것인지 새로 그려야 하는 것인지 알 수 없기 때문입니다.</li>
</ul>
<p>또한, 문자열string 혹은 숫자number만 지정할 수 있으며, 그 이외에 진리값boolean, 배열array, 오브젝트object 등 값을 지정할 경우 DevTools의 console 창에 경고메시지가 발생합니다.</p>
<p>팁으로, 배열의 map 메서드는 2번째 파라미터가 배열의 순서에 해당하는 숫자값이니 가장 무난하게 사용할 수 있습니다.</p>
<ul>
<li>key는 최적화와 관련이 있으므로, React에 익숙한 분이라면 이번 편에서 소개하는 내용 보다 더 자세하게 설명한 포스트를 통해 더 깊이 이해하는 것을 권장합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 강좌) trendy React 1-3. Props 알아보기]]></title>
            <link>https://velog.io/@public_danuel/trendy-react-props</link>
            <guid>https://velog.io/@public_danuel/trendy-react-props</guid>
            <pubDate>Tue, 16 Apr 2019 17:28:19 GMT</pubDate>
            <description><![CDATA[<p>이번 편에서는 우선 JSX 문법을 확장하고, 동적으로 값을 전달하는 방법인 Props 개념을 알아보겠습니다.</p>
<h1 id="선수지식">선수지식</h1>
<p>이번 편 이후부터는 <a href="https://velog.io/@public_danuel/destructuring-assignment">비구조화 할당</a>을 이용해 설명할 예정이므로 어떤 문법인지 모른다면 <a href="https://velog.io/@public_danuel/destructuring-assignment">비구조화 할당</a>을 먼저 익히고 보는 것을 권장합니다.</p>
<h1 id="jsx-문법-확장">JSX 문법 확장</h1>
<pre><code class="language-jsx">const User = () =&gt; {
  return &lt;h1&gt;Danuel&lt;/h1&gt;
}</code></pre>
<p>JSX 문법과 컴포넌트는 굉장히 직관적이지만 이것만으로는 아직 무엇인가 부족합니다. 태그 사이의 값이 항상 고정적이기 때문입니다! JSX 문법의 확장이 필요한 순간입니다.</p>
<pre><code class="language-jsx">const User = () =&gt; {
  const nickname = &#39;Danuel&#39;

  return &lt;h1&gt;{nickname}&lt;/h1&gt;
}</code></pre>
<p>태그 안에서라면 어디든지 중괄호 <code>{}</code> 로 감싼 형태로 변수를 사용할 수 있습니다. 요소를 표현하는 방식과 같습니다.</p>
<p>이 간단한 확장으로 보다 더 유연한 컴포넌트를 작성할 준비가 끝났습니다.</p>
<h1 id="props-소개">Props 소개</h1>
<p>Props는 상위 컴포넌트에서 하위 컴포넌트로만 전달하는 개념이며, 하위 컴포넌트는 상위 컴포넌트에 값을 전달할 수가 없습니다.</p>
<ul>
<li>하위 컴포넌트에서 상위 컴포넌트로 값을 전달하고 방법은 React에서 Props 개념 만큼 중요한 State 개념과 작성하는 방법을 알아야 하므로, 일단 Props를 먼저 알아보겠습니다.</li>
</ul>
<pre><code class="language-jsx">const User = props =&gt; (
  &lt;h1&gt;{props.nickname}&lt;/h1&gt;
)

const App = () =&gt; (
  &lt;div&gt;
    &lt;User nickname=&#39;Danuel&#39; /&gt;
  &lt;/div&gt;
)</code></pre>
<p>상위 컴포넌트에서 하위 컴포넌트에게 데이터를 내려보내는 방식입니다. 즉, 위에서 아래로 흐릅니다.</p>
<pre><code class="language-jsx">const User = ({ nickname }) =&gt; (
  &lt;h1&gt;{nickname}&lt;/h1&gt;
)

const App = () =&gt; (
  &lt;div&gt;
    &lt;User nickname=&#39;Danuel&#39; /&gt;
  &lt;/div&gt;
)</code></pre>
<p><a href="https://velog.io/@public_danuel/destructuring-assignment">비구조화 할당destructuring assignment</a>을 이용하면 조금 더 간소하게 작성할 수 있습니다.</p>
<h1 id="defaultprops">defaultProps</h1>
<pre><code class="language-jsx">   const User = ({ isAdmin, nickname }) =&gt; {
     let label = &#39;user&#39;
     if (isAdmin) {
       label = &#39;admin&#39;
     }

     return (
       &lt;div&gt;
         &lt;h3&gt;{label}&lt;/h3&gt;
         &lt;h1&gt;{nickname}&lt;/h1&gt;
       &lt;/div&gt;
     )
   }

   User.defaultProps = {
     isAdmin: false
   }

   const Users = () =&gt; (
     &lt;div&gt;
       &lt;User nickname=&#39;Danuel&#39; isAdmin={true} /&gt;
       &lt;User nickname=&#39;Other&#39; /&gt;
     &lt;/div&gt;
   )</code></pre>
<p>   React에는 defaultProps라고 하는 Props의 일부 속성에 기본값을 지정하는 특별한 기능이 있습니다.</p>
<p>   익혀두면 상당히 편한 기능이지만, 함수에 속성property를 할당하기 때문에 JavaScript 문법에 익숙하지 않은 분은 이 방식이 상당히 어색할 수 있습니다.</p>
<h1 id="못-다한-이야기">못 다한 이야기</h1>
<ol>
<li><p>propTypes</p>
<pre><code class="language-jsx">import React from &#39;react&#39;
import PropTypes from &#39;prop-types&#39;

const User = ({ nickname }) =&gt; (
  &lt;h1&gt;{nickname}&lt;/h1&gt;
)

User.propTypes = {
  nickname: PropTypes.string
}</code></pre>
<p>JavaScript는 타입 변환에 대해 굉장히 관대한 언어이기 때문에 실수가 자주 발생합니다. 그러한 실수를 방지하기 위해서 React는 propTypes라는 기능을 통해 타입체크를 지원합니다.</p>
<p>처음에는 이 기능을 React 자체적으로 지원했지만, 여러 변화를 따라 prop-types라는 별도의 라이브러리로 분리하고 이전에 작성한 코드와의 하위호환을 위해 남아있습니다. 이 강좌를 끝낸 후 여유가 있다면 TypeScript로 React를 해보는 것을 추천합니다. React는 Flow라는 언어로 작성을 했고 활발한 커뮤니티 지원 덕분에 TypeScript에 대한 타입 지원이 굉장히 좋은데, 이것은 propTypes를 별도의 라이브러리로 분리한 이유 중 하나입니다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript ) 비구조화 할당 알아보기]]></title>
            <link>https://velog.io/@public_danuel/destructuring-assignment</link>
            <guid>https://velog.io/@public_danuel/destructuring-assignment</guid>
            <pubDate>Mon, 15 Apr 2019 07:36:36 GMT</pubDate>
            <description><![CDATA[<p>이 포스트는 ECMAScript 2015에 들어온 새로운 문법인 비구조화 할당destructuring assignment을 배열과 오브젝트 섹션으로 나누어 소개합니다.</p>
<h1 id="배열array">배열array</h1>
<pre><code class="language-javascript">const animalList = [&quot;CAT&quot;, &quot;DOG&quot;, &quot;TIGER&quot;];
const cat = animalList[0];
const dog = animalList[1];
const tiger = animalList[2];
console.log(cat); // CAT
console.log(dog); // DOG
console.log(tiger); // TIGER</code></pre>
<p>animalList라는 변수는 순서대로 &#39;cat&#39;, &#39;dog&#39;, &#39;tiger&#39;를 가지고 있는 배열입니다.</p>
<p>이 변수가 가진 값을 각각 변수에 꺼내어 쓰려고 하면 이렇게 직접 하나하나 지정을 해주어야 합니다.</p>
<p>이렇게 작성하는 것은 아무래도 귀찮은 작업이고, 코드를 읽기에도 괜히 복잡하게 보이는 단점이 있습니다.</p>
<pre><code class="language-javascript">const [cat, dog, tiger] = [&quot;CAT&quot;, &quot;DOG&quot;, &quot;TIGER&quot;];
console.log(cat); // CAT
console.log(dog); // DOG
console.log(tiger); // TIGER</code></pre>
<p>비구조화 할당을 이용하면 위처럼 간단하게 작성할 수 있습니다.</p>
<h2 id="나머지-패턴">나머지 패턴</h2>
<pre><code class="language-javascript">const animalList = [&quot;CAT&quot;, &quot;DOG&quot;, &quot;TIGER&quot;];
const [cat, ...restAnimalList] = animalList;
console.log(cat); // CAT
console.log(restAnimalList); // [&quot;DOG&quot;, &quot;TIGER&quot;]</code></pre>
<p>더 나아가, 앞의 3개 요소와 나머지 요소를 분리하고 싶을 때에는 위 예시코드처럼 작성할 수 있습니다</p>
<h2 id="기본값">기본값</h2>
<pre><code class="language-javascript">const [cat, dog, tiger, animal = &quot;MONKEY&quot;] = [&quot;CAT&quot;, &quot;DOG&quot;, &quot;TIGER&quot;];
console.log(cat); // CAT
console.log(dog); // DOG
console.log(tiger); // TIGER
console.log(animal); // MONKEY</code></pre>
<p>비구조화 할당을 할 때에는 기본값을 지정할 수 있습니다.</p>
<h1 id="오브젝트object">오브젝트object</h1>
<pre><code class="language-javascript">const animals = {
  cat: &quot;CAT&quot;,
  dog: &quot;DOG&quot;,
  tiger: &quot;TIGER&quot;
};
const cat = animals.cat;
const dog = animals.dog;
const tiger = animals.tiger;
console.log(cat); // CAT
console.log(dog); // DOG
console.log(tiger); // TIGER</code></pre>
<p>위와 같이 오브젝트에서 요소를 꺼내어 변수에 할당할 때에도 가능합니다.</p>
<pre><code class="language-javascript">const { cat, dog, tiger } = {
  cat: &quot;CAT&quot;,
  dog: &quot;DOG&quot;,
  tiger: &quot;TIGER&quot;
};
console.log(cat); // CAT
console.log(dog); // DOG
console.log(tiger); // TIGER</code></pre>
<p>위와 같이 작성하면 비구조화 할당을 수행하며, 변수의 이름과 같은 key에 있는 값이 담깁니다.</p>
<h2 id="나머지-패턴-1">나머지 패턴</h2>
<pre><code class="language-javascript">const { cat, ...animals } = {
  cat: &quot;CAT&quot;,
  dog: &quot;DOG&quot;,
  tiger: &quot;TIGER&quot;
};
console.log(cat); // CAT
console.log(animals); // { dog: DOG, tiger: TIGER }</code></pre>
<p>배열에서의 비구조화 할당에 나머지 패턴이 있듯이, 오브젝트에서의 비구조화 할당에도 나머지 패턴이 있습니다.</p>
<h2 id="기본값-1">기본값</h2>
<pre><code class="language-javascript">const { cat, dog, tiger, monkey = &quot;monkey&quot; } = {
  cat: &quot;CAT&quot;,
  dog: &quot;DOG&quot;,
  tiger: &quot;TIGER&quot;
};
console.log(cat); // CAT
console.log(dog); // DOG
console.log(tiger); // TIGER
console.log(monkey); // MONKEY</code></pre>
<p>배열에서의 비구조화 할당처럼 오브젝트에서도 기본값을 지원합니다.</p>
]]></description>
        </item>
    </channel>
</rss>