Jekyll2017-01-15T21:43:30+09:00http://blog.xogus.io//Taehyun KimSoftware Developer @PuzzleData & CS Senior @UNISTTaehyun Kimkgyoo8232@gmail.comSVG에서 getBBox 활용하기2017-01-15T00:00:00+09:002017-01-15T00:00:00+09:00http://blog.xogus.io/2017/01/15/SVG%EC%97%90%EC%84%9C-getBBox-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0<p>SVG DOM 객체에 포함된 getBBox 메소드에 대해 알아봅시다!</p>
<h2 id="getbbox">getBBox란?</h2>
<p>SVG 1.1 스펙에서 getBBox에 대한 설명을 보면 아래와 같다.</p>
<blockquote>
<p>Returns the tight bounding box in current user space (i.e., after application of the ‘transform’ attribute, if any) on the geometry of all contained graphics elements, exclusive of stroking, clipping, masking and filter effects). Note that getBBox must return the actual bounding box at the time the method was called, even in case the element has not yet been rendered.<br />
<cite><a href="https://www.w3.org/TR/SVG11/types.html#__svg__SVGLocatable__getBBox">www.w3.org</a></cite></p>
</blockquote>
<p>대략 해석해보면 getBBox 함수가 호출되는 순간의 해당 엘리멘트가 차지하는 bounding box의 정보를 리턴한다는 얘기다.</p>
<p>getBBox를 호출하면 SVGRect객체를 리턴하는데 SVGRect는 위치정보와 너비, 높이 정보를 담고 있다. 이를 타입스크립트의 interface로 표현하자면 다음과 같다</p>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">interface</span> <span class="nx">SVGRect</span> <span class="p">{</span>
<span class="nl">x</span><span class="p">:</span> <span class="nx">number</span><span class="p">;</span>
<span class="nl">y</span><span class="p">:</span> <span class="nx">number</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="nx">number</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="nx">number</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>호출 된 순간의 해당 객체의 위치와 크기 정보를 알려준다는 점에서 jQuery의 <code class="highlighter-rouge">offset</code>, <code class="highlighter-rouge">width</code>, <code class="highlighter-rouge">height</code> 메소드를 합친 것이라고도 볼 수 있을 것 같다.</p>
<h2 id="getbbox-">getBBox 활용하기</h2>
<p>텍스트를 포함하는 요소나 하위요소를 포함하는 요소의 경우, 브라우저가 렌더링하기 전까지는 정확한 크기와 위치 등을 알아내기가 쉽지 않다. 이때, getBBox를 이용한다면 이를 쉽게 알 수 있다.<br />
SVG 평면 위에 텍스트와 이를 rect로 감싸는 노드 몇개를 그려보는 예제를 이용해 getBBox를 활용해보자</p>
<p>먼저 SVG에 그려질 노드 정보를 담는 data를 정의한다. 총 3개의 노드를 그릴 것이고, 보다시피 name에 이름, x, y 에 각각 x, y좌표 정보를 담고 있다.</p>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// Data</span>
<span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">[{</span>
<span class="na">name</span><span class="p">:</span> <span class="s1">'NODE1'</span><span class="p">,</span>
<span class="na">x</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
<span class="na">y</span><span class="p">:</span> <span class="mi">100</span>
<span class="p">},</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="s1">'NODE2'</span><span class="p">,</span>
<span class="na">x</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
<span class="na">y</span><span class="p">:</span> <span class="mi">300</span>
<span class="p">},</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="s1">'NODE3'</span><span class="p">,</span>
<span class="na">x</span><span class="p">:</span> <span class="mi">400</span><span class="p">,</span>
<span class="na">y</span><span class="p">:</span> <span class="mi">200</span>
<span class="p">}];</span></code></pre></figure>
<p>HTML 파일에 노드들이 그려질 svg 평면과 이를 위한 스타일을 정의해준다.</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"><style></span>
<span class="nf">#svg</span> <span class="p">{</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.node</span> <span class="nt">rect</span> <span class="p">{</span>
<span class="py">stroke</span><span class="p">:</span> <span class="no">skyblue</span><span class="p">;</span>
<span class="py">stroke-width</span><span class="p">:</span> <span class="m">2px</span><span class="p">;</span>
<span class="py">fill</span><span class="p">:</span> <span class="m">#fff</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.node</span> <span class="nt">text</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="m">#333</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt"></style></span>
<span class="nt"><svg</span> <span class="na">id=</span><span class="s">"svg"</span> <span class="na">xmlns=</span><span class="s">"http://www.w3.org/2000/svg"</span> <span class="na">version=</span><span class="s">"1.1"</span><span class="nt">></svg></span></code></pre></figure>
<p>각각의 노드 데이터를 이용해 svg평면에 노드를 그려준다.</p>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">svg</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">'#svg'</span><span class="p">);</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">d</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// node group (g는 group의 약자로 하위 요소들을 묶는 용도로 쓰임)</span>
<span class="c1">// namespace를 이용하여 생성하여야 svg오브젝트를 얻을 수있음!</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="s1">'http://www.w3.org/2000/svg'</span><span class="p">,</span> <span class="s1">'g'</span><span class="p">);</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">className</span><span class="p">.</span><span class="nx">baseVal</span> <span class="o">=</span> <span class="s1">'node'</span><span class="p">;</span>
<span class="c1">// node 그룹의 기준 좌표 설정</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'transform'</span><span class="p">,</span> <span class="s1">'translate('</span> <span class="o">+</span> <span class="p">[</span><span class="nx">d</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">d</span><span class="p">.</span><span class="nx">y</span><span class="p">]</span> <span class="o">+</span> <span class="s1">')'</span><span class="p">);</span>
<span class="nx">svg</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>
<span class="c1">// node label</span>
<span class="kd">var</span> <span class="nx">text</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="s1">'http://www.w3.org/2000/svg'</span><span class="p">,</span> <span class="s1">'text'</span><span class="p">);</span>
<span class="nx">text</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">d</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">text</span><span class="p">);</span>
<span class="c1">// node rect</span>
<span class="kd">var</span> <span class="nx">rect</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="s1">'http://www.w3.org/2000/svg'</span><span class="p">,</span> <span class="s1">'rect'</span><span class="p">);</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'rx'</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'ry'</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">insertBefore</span><span class="p">(</span><span class="nx">rect</span><span class="p">,</span> <span class="nx">text</span><span class="p">);</span>
<span class="p">});</span></code></pre></figure>
<p>이 코드를 추가해주고 실행시켜주면 아래와 같이 나온다.</p>
<p><img src="http://blog.xogus.io/assets/images/posts/SVG에서-getBBox-활용하기/screenshot-1.png" alt="" /></p>
<p>요소검사를 해보면 rect가 코드상으로는 존재하지만 rect가 가져야 할 하늘색 외곽선이 보이지 않는다. rect의 너비와 높이가 지정되지 않아 기본값인 0으로 지정되어있기 때문이다. 이를 지정해주는 코드를 추가해주면 되는데 형제 요소가 text인게 문제다.<br />
폰트에 따라, 글자 수에 따라 text요소의 길이는 천차만별이기 때문이다. 이때 getBBox를 이용해보자</p>
<figure class="highlight"><pre><code class="language-js" data-lang="js"> <span class="c1">// node label</span>
<span class="kd">var</span> <span class="nx">text</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="s1">'http://www.w3.org/2000/svg'</span><span class="p">,</span> <span class="s1">'text'</span><span class="p">);</span>
<span class="nx">text</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">d</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="c1">// 여기서 getBBox를 호출할 경우, text.getBBox() => { x:0, y:0, width:0, height:0 }</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">text</span><span class="p">);</span> <span class="c1">// 여기서 렌더링이 이뤄지므로 렌더링 한 이후에 getBBox를 호출해야함!</span>
<span class="kd">var</span> <span class="nx">bbox</span> <span class="o">=</span> <span class="nx">text</span><span class="p">.</span><span class="nx">getBBox</span><span class="p">();</span>
<span class="c1">// node rect</span>
<span class="kd">var</span> <span class="nx">rect</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="s1">'http://www.w3.org/2000/svg'</span><span class="p">,</span> <span class="s1">'rect'</span><span class="p">);</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'rx'</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'ry'</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">insertBefore</span><span class="p">(</span><span class="nx">rect</span><span class="p">,</span> <span class="nx">text</span><span class="p">);</span>
<span class="c1">// rect의 크기 설정</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'width'</span><span class="p">,</span> <span class="nx">bbox</span><span class="p">.</span><span class="nx">width</span> <span class="o">+</span> <span class="nx">textMargin</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'height'</span><span class="p">,</span> <span class="nx">bbox</span><span class="p">.</span><span class="nx">height</span> <span class="o">+</span> <span class="nx">textMargin</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span></code></pre></figure>
<p>이렇게 getBBox호출 코드와 이를 이용해 rect의 크기를 설정해주는 코드를 추가해주면,</p>
<p><img src="http://blog.xogus.io/assets/images/posts/SVG에서-getBBox-활용하기/screenshot-2.png" alt="" /></p>
<p>위와 같아지는데, 기본적으로 text 요소의 포지셔닝이 다른 요소들과 다른 것 같다. getBBox를 이용해 이를 보정하는 코드를 삽입해보자.</p>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// node label</span>
<span class="kd">var</span> <span class="nx">text</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="s1">'http://www.w3.org/2000/svg'</span><span class="p">,</span> <span class="s1">'text'</span><span class="p">);</span>
<span class="nx">text</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">d</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="c1">// 여기서 getBBox를 호출할 경우, text.getBBox() => { x:0, y:0, width:0, height:0 }</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">text</span><span class="p">);</span> <span class="c1">// 여기서 렌더링이 이뤄지므로 렌더링 한 이후에 getBBox를 호출해야함!</span>
<span class="kd">var</span> <span class="nx">bbox</span> <span class="o">=</span> <span class="nx">text</span><span class="p">.</span><span class="nx">getBBox</span><span class="p">();</span>
<span class="c1">// text의 x, y 좌표</span>
<span class="nx">text</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'x'</span><span class="p">,</span> <span class="o">-</span> <span class="nx">bbox</span><span class="p">.</span><span class="nx">width</span> <span class="o">/</span> <span class="mi">2</span><span class="p">);</span>
<span class="nx">text</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'y'</span><span class="p">,</span> <span class="nx">bbox</span><span class="p">.</span><span class="nx">height</span> <span class="o">/</span> <span class="mi">2</span><span class="p">);</span>
<span class="c1">// node rect</span>
<span class="kd">var</span> <span class="nx">rect</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="s1">'http://www.w3.org/2000/svg'</span><span class="p">,</span> <span class="s1">'rect'</span><span class="p">);</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'rx'</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'ry'</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">insertBefore</span><span class="p">(</span><span class="nx">rect</span><span class="p">,</span> <span class="nx">text</span><span class="p">);</span>
<span class="c1">// rect의 x, y 좌표 및 크기 설정</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'x'</span><span class="p">,</span> <span class="nx">bbox</span><span class="p">.</span><span class="nx">x</span> <span class="o">-</span> <span class="nx">bbox</span><span class="p">.</span><span class="nx">width</span> <span class="o">/</span> <span class="mi">2</span> <span class="o">-</span> <span class="nx">textMargin</span><span class="p">);</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'y'</span><span class="p">,</span> <span class="nx">bbox</span><span class="p">.</span><span class="nx">y</span> <span class="o">+</span> <span class="nx">bbox</span><span class="p">.</span><span class="nx">height</span> <span class="o">/</span> <span class="mi">2</span> <span class="o">-</span> <span class="nx">textMargin</span><span class="p">);</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'width'</span><span class="p">,</span> <span class="nx">bbox</span><span class="p">.</span><span class="nx">width</span> <span class="o">+</span> <span class="nx">textMargin</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
<span class="nx">rect</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'height'</span><span class="p">,</span> <span class="nx">bbox</span><span class="p">.</span><span class="nx">height</span> <span class="o">+</span> <span class="nx">textMargin</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span></code></pre></figure>
<p>위와 같이 수정한 후 이를 브라우저에서 열어보면 이렇게 이뻐진다!</p>
<p><img src="http://blog.xogus.io/assets/images/posts/SVG에서-getBBox-활용하기/screenshot-3.png" alt="" /></p>
<p>드디어 볼만한 노드가 그려지게 되었다. 이름이 긴 노드를 추가해서 다른 길이에서도 제대로 작동하는지 테스트해보자.</p>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// Data</span>
<span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">[{</span>
<span class="na">name</span><span class="p">:</span> <span class="s1">'NODE1'</span><span class="p">,</span>
<span class="na">x</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
<span class="na">y</span><span class="p">:</span> <span class="mi">100</span>
<span class="p">},</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="s1">'NODE2'</span><span class="p">,</span>
<span class="na">x</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
<span class="na">y</span><span class="p">:</span> <span class="mi">300</span>
<span class="p">},</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="s1">'NODE3'</span><span class="p">,</span>
<span class="na">x</span><span class="p">:</span> <span class="mi">400</span><span class="p">,</span>
<span class="na">y</span><span class="p">:</span> <span class="mi">200</span>
<span class="p">},</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="s1">'이름이 기이이이이이인 NODE4'</span><span class="p">,</span>
<span class="na">x</span><span class="p">:</span> <span class="mi">400</span><span class="p">,</span>
<span class="na">y</span><span class="p">:</span> <span class="mi">400</span>
<span class="p">}];</span></code></pre></figure>
<p><img src="http://blog.xogus.io/assets/images/posts/SVG에서-getBBox-활용하기/screenshot-4.png" alt="" /></p>
<p>길이가 긴 노드에서도 노드가 제대로 위치하는 것을 볼 수 있다.<br />
마지막으로 노드의 중심점이 데이터에 지정된 x, y좌표에 제대로 위치하는지 확인해보기 위해 circle을 넣어보면,</p>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// 중앙 위치 확인용 circle</span>
<span class="kd">var</span> <span class="nx">circle</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElementNS</span><span class="p">(</span><span class="s1">'http://www.w3.org/2000/svg'</span><span class="p">,</span> <span class="s1">'circle'</span><span class="p">);</span>
<span class="nx">circle</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'r'</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
<span class="nx">circle</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'fill'</span><span class="p">,</span> <span class="s1">'red'</span><span class="p">);</span>
<span class="c1">// 중심점의 x, y좌표</span>
<span class="nx">circle</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'cx'</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="nx">circle</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'cy'</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">circle</span><span class="p">);</span></code></pre></figure>
<p><img src="http://blog.xogus.io/assets/images/posts/SVG에서-getBBox-활용하기/screenshot-5.png" alt="" /></p>
<p>중앙에서 약간 위에 위치하고 있어 어느정도의 오차가 있다고 볼 수 있지만, 이정도면 봐줄만 한 것 같다.</p>
<p>예제 코드 및 실행 결과는 blocks를 통해 확인하실 수 있습니다.</p>
<ul>
<li><a href="http://bl.ocks.org/kimxogus/dd38738b4d1e73257016040e175a8b0b">D3js 버전</a></li>
<li><a href="http://bl.ocks.org/kimxogus/fcdaae653f4a4fa351de04235ff7863f">Vanila Script 버전</a></li>
</ul>Taehyun Kimkgyoo8232@gmail.comSVG DOM 객체에 포함된 getBBox 메소드에 대해 알아봅시다!react-native-version-check2016-12-17T00:00:00+09:002016-12-17T00:00:00+09:00http://blog.xogus.io/2016/12/17/react-native-version-check<p>그간 작업하던 React Native 버전 체크 라이브러리인 <code class="highlighter-rouge">react-native-version-check</code>의 1.0버전이 릴리즈 되었습니다!</p>
<div style="text-align:center;">
<figure style="display:inline-block;width:100px;margin-right: 30px;">
<a href="https://github.com/kimxogus/react-native-version-check">
<img src="http://blog.xogus.io/assets/images/github/PNG/GitHub-Mark-120px-plus.png" alt="" />
<figcaption>GitHub</figcaption>
</a>
</figure>
<figure style="display:inline-block;width:100px;">
<a href="https://www.npmjs.com/package/react-native-version-check">
<img src="http://blog.xogus.io/assets/images/npm/simple-logo/n-large.png" alt="" />
<figcaption>NPM</figcaption>
</a>
</figure>
</div>
<h2 id="section">제작 배경</h2>
<p>앱이 지속적으로 업데이트 되는 와중에도 적지 않은 유저들이 업데이트를 하지 않고 구 버전에 머무르는데요. 메이저 업데이트가 이뤄지고, API가 바뀌는 등 구 버전에 대한 지원이 중단되는 경우 유저들에게 이를 알리고 업데이트 하도록 유도할 필요가 있습니다.</p>
<p>단순히 구버전 유저들에게 업데이트 여부를 알려주기만 하는건 문제가 없지만, 이를 알리는 시점을 언제로 할지가 애매해지게 됩니다. 제출한 업데이트가 스토어에 반영되기까지는 최소 몇시간에서 며칠까지도 걸리는데 스토어에 반영되지 않은 업데이트를 안내하는 것은 유저들에게 혼란을 줄 수 있고 업데이트가 스토어에 반영되는 것을 직접 확인해서 안내하는 것은 아무래도 귀찮은 작업이니까요.</p>
<p>이를 해결할 수 있는 방법 중 하나가 스토어에 올라가있는 앱의 버전을 직접 확인하는 것입니다. 해당 앱의 앱스토어 혹은 마켓 페이지를 파싱하여 최신 버전 정보를 가져오는 것이죠. 이럴 경우 위의 두 문제를 모두 해결할 수 있습니다. 이를 이미 구현하여 코드를 공유하신 분 도 있습니다. (<a href="http://itmir.tistory.com/524">Android Market Version Checker - 안드로이드 마켓 버전 확인하기 - 미르의 IT 정복기</a>) 다행히 플레이 마켓과 앱스토어 모두 버전 정보를 담고 있는 태그가 모두 <code class="highlighter-rouge">itemprop="softwareVersion"</code> 를 포함하고있기 때문에 위의 코드로 모두 파싱이 가능합니다. 그래서 이 코드를 이용하여 Android, iOS 모두를 지원하는 버전 체크 라이브러리인 <code class="highlighter-rouge">react-native-version-check</code>을 만들게 되었습니다.</p>
<h2 id="section-1">설치</h2>
<p><strong>react-native-version-check</strong>은 버전 정보, 국가 정보(iOS AppStore 경로) 등을 가져오기 위해 네이티브 API를 사용하고 있습니다. 때문에 이를 추가하는 작업이 필요합니다. (제 깃헙의 README에 있는 설치법을 한글로 옮겨 적었습니다.)</p>
<h3 id="npm--">NPM 라이브러리 설치</h3>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">npm install --save react-native-version-check</code></pre></figure>
<h3 id="ios">iOS</h3>
<ol>
<li>프로젝트 폴더의 ios폴더를 XCode 프로젝트로 연다.</li>
<li>프로젝트 네비게이터에서 <em>Libraries</em> 우클릭</li>
<li><code class="highlighter-rouge">Add Files to [PROJECT_NAME]</code></li>
<li><code class="highlighter-rouge">node_modules/react-native-version-check/ios/RNVersionCheck.xcodeproj</code> 추가</li>
<li>프로젝트의 <em>Build Phases</em> > <em>Link Binary With Libraries</em> 내 <code class="highlighter-rouge">libRNVersionCheck.a</code> 추가</li>
</ol>
<h3 id="android">Android</h3>
<ol>
<li><em>android/settings.gradle</em> 내 아래 구문 추가</li>
</ol>
<figure class="highlight"><pre><code class="language-gradle" data-lang="gradle"><span class="o">...</span>
<span class="n">include</span> <span class="s1">':react-native-version-check'</span>
<span class="n">project</span><span class="o">(</span><span class="s1">':react-native-version-check'</span><span class="o">).</span><span class="na">projectDir</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="n">rootProject</span><span class="o">.</span><span class="na">projectDir</span><span class="o">,</span> <span class="s1">'../node_modules/react-native-version-check/android'</span><span class="o">)</span></code></pre></figure>
<ol>
<li><em>android/app/build.gradle</em> 파일 내 <em>dependencies_에 _react-native-version-check</em> 추가</li>
</ol>
<figure class="highlight"><pre><code class="language-gradle" data-lang="gradle"><span class="k">dependencies</span> <span class="o">{</span>
<span class="o">...</span>
<span class="n">compile</span> <span class="nf">project</span><span class="o">(</span><span class="s1">':react-native-version-check'</span><span class="o">)</span>
<span class="o">}</span></code></pre></figure>
<ol>
<li><em>android/app/src/main/java/[…]/MainApplication.java</em> 내 <em>React Package</em> 등록</li>
</ol>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">io.xogus.reactnative.versioncheck.RNVersionCheckPackage</span><span class="o">;</span> <span class="c1">// <--- HERE</span>
<span class="o">......</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="n">List</span><span class="o"><</span><span class="n">ReactPackage</span><span class="o">></span> <span class="nf">getPackages</span><span class="o">()</span> <span class="o">{</span>
<span class="o">......</span>
<span class="k">new</span> <span class="nf">RNVersionCheckPackage</span><span class="o">()</span> <span class="c1">// <------ HERE</span>
<span class="o">......</span>
<span class="o">}</span></code></pre></figure>
<h2 id="section-2">사용법</h2>
<p>자세한 Reference는 GitHub 저장소의 <a href="https://github.com/kimxogus/react-native-version-check#methods">README</a>를 참고해주세요 :)</p>
<ul>
<li>라이브러리 임포트</li>
</ul>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">import</span> <span class="nx">VersionCheck</span> <span class="nx">from</span> <span class="s2">"react-native-version-check"</span><span class="p">;</span></code></pre></figure>
<ul>
<li>패키지 확인하기</li>
</ul>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">VersionCheck</span><span class="p">.</span><span class="nx">getPackageName</span><span class="p">());</span>
<span class="c1">// com.your.app</span></code></pre></figure>
<ul>
<li>현재 버전 확인하기</li>
</ul>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">VersionCheck</span><span class="p">.</span><span class="nx">getCurrentVersion</span><span class="p">());</span>
<span class="c1">// 1.0.0</span></code></pre></figure>
<ul>
<li>현재 빌드 번호 확인하기</li>
</ul>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">VersionCheck</span><span class="p">.</span><span class="nx">getCurrentBuildNumber</span><span class="p">());</span>
<span class="c1">// 10</span></code></pre></figure>
<ul>
<li>[iOS일 경우] App Store의 앱 이름, 앱 ID 등록하기
<ul>
<li>스토어의 최신버전을 확인 하기 위해 스토어의 url을 생성해야하는데 플레이 마켓의 경우 패키지명만 있으면 되지만 앱스토어의 경우 국가, 앱 이름, 앱 ID가 필요합니다. 때문에 스토어 기준 최신버전 확인을 위해 이 값들을 지정해주셔야 합니다. (앱 내에서 1번만 등록)</li>
<li>카카오톡의 App Store URL의 경우로 보자면 https://itunes.apple.com/KR/app/kakaotog-kakaotalk/id362057947 에서 <em>KR</em> 이 국가 코드, <em>kakaotog-kakaotalk</em> 이 앱 이름, <em>362057949</em> 이 앱 ID입니다.</li>
<li>제가 iOS 개발 경험이 없어 이 값들을 입력 없이 구하는 방법을 모르겠네요. 혹시 아시는 분 있다면 알려주시길 부탁드립니다!</li>
</ul>
</li>
</ul>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">VersionCheck</span><span class="p">.</span><span class="nx">setAppID</span><span class="p">(</span><span class="nx">APP_ID</span><span class="p">);</span>
<span class="nx">VersionCheck</span><span class="p">.</span><span class="nx">setAppName</span><span class="p">(</span><span class="nx">APP_NAME</span><span class="p">);</span></code></pre></figure>
<ul>
<li>최신 버전 확인하기 (앱스토어, 마켓 기준)</li>
</ul>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">VersionCheck</span><span class="p">.</span><span class="nx">getLatestVersion</span><span class="p">()</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">latestVersion</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">latestVersion</span><span class="p">);</span>
<span class="c1">// 2.0.0</span>
<span class="p">})</span></code></pre></figure>
<ul>
<li>자체 URL이용하여 최신 버전 확인하기
<ul>
<li><em>URL</em> 과 <em>isomorphic-fetch</em> 옵션을 이용하여 자체 API를 이용하실 수도 있습니다.</li>
<li>iOS에서 자체 URL을 이용하여 최신버전 체크를 하는 경우, <code class="highlighter-rouge">setAppID</code>와 <code class="highlighter-rouge">setAppName</code>을 지정하지 않으셔도 됩니다.</li>
</ul>
</li>
</ul>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">VersionCheck</span><span class="p">.</span><span class="nx">getLatestVersion</span><span class="p">({</span>
<span class="na">url</span><span class="p">:</span> <span class="s2">"http://url.of/custom/api"</span><span class="p">,</span>
<span class="na">fetchOptions</span><span class="p">:</span> <span class="p">{</span>
<span class="na">method</span><span class="p">:</span> <span class="s2">"GET"</span><span class="p">,</span>
<span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">"x-custom-param"</span><span class="p">:</span> <span class="s2">"value"</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="nx">latestVersion</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">latestVersion</span><span class="p">);</span>
<span class="c1">// 2.0.0</span>
<span class="p">})</span></code></pre></figure>
<ul>
<li>업데이트 필요 여부 확인하기
<ul>
<li>버전 정보를 이용하여 업데이트 여부를 알 수 있습니다.</li>
</ul>
</li>
</ul>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">VersionCheck</span><span class="p">.</span><span class="nx">needUpdate</span><span class="p">()</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">res</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">res</span><span class="p">);</span>
<span class="c1">// { isNeeded: true, currentVersion: "1.0.0", latestVersion: "1.1.0" }</span>
<span class="p">});</span></code></pre></figure>
<ul>
<li>메이저 버전만 체크하기
<ul>
<li><em>depth</em> 를 이용하여 메이저 버전만을 이용하여 업데이트 여부를 판단할 수도 있습니다.</li>
</ul>
</li>
</ul>
<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">VersionCheck</span><span class="p">.</span><span class="nx">needUpdate</span><span class="p">({</span>
<span class="na">depth</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="na">delimiter</span><span class="p">:</span> <span class="s2">"."</span> <span class="c1">// 기본값이므로 입력하지 않으셔도 됩니다.</span>
<span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="nx">res</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">res</span><span class="p">);</span>
<span class="c1">// { isNeeded: false, currentVersion: "1.0.0", latestVersion: "1.1.0" }</span>
<span class="c1">// 버전의 첫 필드인 1이 같으므로 업데이트 할 필요가 없습니다.</span>
<span class="p">});</span></code></pre></figure>Taehyun Kimkgyoo8232@gmail.com그간 작업하던 React Native 버전 체크 라이브러리인 react-native-version-check의 1.0버전이 릴리즈 되었습니다!첫 포스팅2016-12-13T00:00:00+09:002016-12-13T00:00:00+09:00http://blog.xogus.io/2016/12/13/%EC%B2%AB-%ED%8F%AC%EC%8A%A4%ED%8C%85<p>길고 긴 삽질 끝에 블로그를 열게 되었다!</p>
<p>원래는 첫 포스팅으로 블로그를 열기 까지의 삽질 과정을 정리해보려 했으나 주말이 지난 관계로<del><em>피곤해</em></del> 다음으로 미루도록 하고 텅빈 블로그에 뭐라도 채워넣고자 이렇게 끄적거려본다.</p>
<p>블로그를 열게 된 이유를 꼽자면 내가 해 온 일들에 대한 자취를 남기기 위해서랄까? 그간 공들여서 만든 자료나 코드들이 시간이 지나면 버려지다시피 하는게 아까워서 나름대로 보존하고 공유하기 위해 페이스북, 깃헙 등을 이용했지만 어디까지나 SNS, 코드 저장소일 뿐이라 아무래도 한계점이 많았다. 페이스북은 뉴스피드 내려가면 그만이고 깃헙도 링크를 뿌리지 않는 한 접근성이 좋은편은 아니니까.</p>
<p>처음 공유할만한 자료라고 만든 <em>React 튜토리얼</em>(<a href="https://github.com/kimxogus/react-tutorial">GitHub</a>, <a href="http://www.slideshare.net/ssuser555dd7/react-1">SlideShare</a>)은 생활코딩 페이지에서도 공유되고 나름 흥했으나 그 뒤로는 기껏 만들어놓고도 아무도 보지 않고 주인에게서마저 잊혀지는 자료들 뿐이니… 이왕이면 애써 만든 자료들 다같이 보고 공유하는게<del><em>따봉도 많이 받고</em></del> 좋지 않은가. 안그래도 React-Native 라이브러리(<a href="https://github.com/kimxogus/react-native-version-check">react-native-version-check</a>)를 제작하면서 나만의 패키징을 만들자는 생각을 하다 내친김에 xogus.io 도메인을 구입하게 되었는데, 이걸 썩히는 것도 아까워 큰맘먹고 블로그를 열게 되었다.</p>
<p>블로그 하나 여는게 뭐 그리 큰맘이겠냐마는 메모하거나 일기도 귀찮아서 안쓰고 그나마 쓴 것들도 까먹고 버리는 나 같은 게으름뱅이에게 블로그 관리라는건 분명 쉽지 않은 일이 될 듯 하다. 그래도 가끔 한번 삘받고 열심히 쓰면 뭐라도 나오니 어떻게든 관리는 되지 않을까</p>Taehyun Kimkgyoo8232@gmail.com길고 긴 삽질 끝에 블로그를 열게 되었다!