<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.4">Jekyll</generator><link href="https://www.palgle.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.palgle.com/" rel="alternate" type="text/html" /><updated>2026-03-09T09:57:48+00:00</updated><id>https://www.palgle.com/feed.xml</id><title type="html">팔글 인사이드 구글</title><subtitle>팔글 인사이드 구글은 구글의 국내 진출을 정확하게 전달합니다.</subtitle><author><name>Samgu Lee</name><email>cable8mm@gmail.com</email></author><entry><title type="html">Github 소스 레포지토리에 API 도큐먼트를 깃헙 페이지에 만들기</title><link href="https://www.palgle.com/2024/04/28/how-to-make-gh-pages-with-main-branch/" rel="alternate" type="text/html" title="Github 소스 레포지토리에 API 도큐먼트를 깃헙 페이지에 만들기" /><published>2024-04-28T13:54:00+00:00</published><updated>2024-04-28T13:54:00+00:00</updated><id>https://www.palgle.com/2024/04/28/how-to-make-gh-pages-with-main-branch</id><content type="html" xml:base="https://www.palgle.com/2024/04/28/how-to-make-gh-pages-with-main-branch/"><![CDATA[<p>소스를 작성하면 테스트 코드와 더불어 API 문서를 만들어 동료나 혹은 다른 개발자가 사용하기 쉽게 도큐먼트를 제공합니다.</p>

<p><img src="/assets/images/screenshot-doctum-api-documents.png" alt="Doctum API documents" /></p>

<p>여러가지 라이브러리나 솔루션이 있겠지만, PHP 에서 사용할 수 있는 가장 쉬운 방법과 깃헙 페이지에 올릴 수 있는 방법을 소개합니다.</p>

<h2 id="api-문서-만들기">API 문서 만들기</h2>

<p>PHP에서 가장 쉽게 사용할 수 있는 라이브러리는 라라벨에서도 사용되는 <a href="https://github.com/code-lts/doctum">doctum</a> 입니다. doctum 깃헙 레포지토리에서 설치 방법이 자세히 나와있는데요, 요약하자면,</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-O</span> https://doctum.long-term.support/releases/latest/doctum.phar

<span class="nb">mv </span>doctum.phar ~/bin/
</code></pre></div></div>

<p>이제 소스의 <code class="language-plaintext highlighter-rouge">composer.json</code> 파일에 아래의 내용을 추가합니다.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"apidoc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"rm -rf build; rm -rf cache; doctum.phar update doctum.php --output-format=github --no-ansi --no-progress -v;"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"opendoc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"open build/index.html"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>이제 <code class="language-plaintext highlighter-rouge">composer apidoc</code> 명령어로 api 문서를 만들 수 있고, <code class="language-plaintext highlighter-rouge">composer opendoc</code> 명령어로 api 를 브라우져로 띄울 수 있게 되었습니다.</p>

<h2 id="깃헙-페이지로-배포하기">깃헙 페이지로 배포하기</h2>

<p>소스코드의 브랜치는 일반적으로 <code class="language-plaintext highlighter-rouge">main</code> 혹은 <code class="language-plaintext highlighter-rouge">master</code> 입니다. 깃헙은 <code class="language-plaintext highlighter-rouge">gh-pages</code>라는 특별한 브랜치를 제공하는데요, 이 브랜치는 <code class="language-plaintext highlighter-rouge">orphan</code> 으로 사용됩니다. <code class="language-plaintext highlighter-rouge">orphan</code>은 다른 브랜치와는 완전히 별도로 관리됨을 의미입니다.</p>

<p>아래와 같은 명령어를 이용해서 깃헙에 <code class="language-plaintext highlighter-rouge">gh-pages</code> 브랜치를 만듭니다.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout <span class="nt">--orphan</span> gh-pages<span class="se">\</span>
git reset <span class="nt">--hard</span><span class="se">\</span>
git commit <span class="nt">--allow-empty</span> <span class="nt">-m</span> <span class="s2">"first commit to gh-pages branch"</span><span class="se">\</span>
git push origin gh-pages<span class="se">\</span>
git checkout main
</code></pre></div></div>

<p>깃헙 레포지토리를 방문해서 <code class="language-plaintext highlighter-rouge">gh-pages</code> 브랜치가 생성되었는지 확인해 보세요.</p>

<h2 id="깃헙-페이지에-api-문서-배포하기">깃헙 페이지에 api 문서 배포하기</h2>

<p>우선 깃헙 페이지에 필요한 api 문서는 로컬에서 만들어지는 것이 아닙니다. 깃헙 액션으로 만듭니다. 개발 머신에 <code class="language-plaintext highlighter-rouge">doctum</code>을 설치한 이유는 테스트를 하기 위함입니다.</p>

<p>우선 깃헙 레포지토리에서 깃헙 페이지를 활성화 합니다.</p>

<p>상단에서 <code class="language-plaintext highlighter-rouge">Settings</code>을 클릭하고, 좌측 메뉴에서 <code class="language-plaintext highlighter-rouge">Pages</code>를 클릭 후 <code class="language-plaintext highlighter-rouge">Build and deployment</code> <code class="language-plaintext highlighter-rouge">Source</code> 에서 <code class="language-plaintext highlighter-rouge">Github Actions</code>를 선택하세요.</p>

<p><img src="/assets/images/screenshot-of-github-page.png" alt="Github Pages" /></p>

<p>깃헙에서의 준비는 끝났습니다. 이제 깃헙 액션으로 doctum으로 만들어진 api 문서를 깃헙 페이지로 배포하는 액션을 <code class="language-plaintext highlighter-rouge">.github/workflows</code> 폴더에 넣습니다.</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">deploy-to-github-pages</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">release</span><span class="pi">:</span>
    <span class="na">types</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">released</span><span class="pi">]</span>

<span class="na">permissions</span><span class="pi">:</span> <span class="pi">{}</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">publish-pages</span><span class="pi">:</span>
    <span class="na">permissions</span><span class="pi">:</span>
      <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span>
    <span class="na">uses</span><span class="pi">:</span> <span class="s">cable8mm/.github/.github/workflows/deploy-to-github-pages.yml@main</span>
</code></pre></div></div>

<p>위의 스크립트는 Release를 만들면 깃헙 페이지에 api문서를 만들어서 배포하는 액션입니다.</p>

<p>이런 방법으로 저의 오픈소스 프로젝트인 <a href="https://github.com/cable8mm/xeed">The Xeed</a> 에서는 <a href="https://www.palgle.com/xeed/">API 문서</a> 도메인으로 api 문서를 제공하고 있으며, 레포지토리 우측 website 섹션에 api 주소를 표기해 놨습니다.</p>

<p>이 방법에 대한 코멘트는 <a href="https://github.com/cable8mm/cable8mm.github.io">https://github.com/cable8mm/cable8mm.github.io</a> 의 이슈를 통해 여러분의 의견을 공유 해 주세요.</p>]]></content><author><name>Samgu Lee</name></author><category term="development" /><category term="github" /><category term="pages" /><category term="gh-pages" /><summary type="html"><![CDATA[소스를 작성하면 테스트 코드와 더불어 API 문서를 만들어 동료나 혹은 다른 개발자가 사용하기 쉽게 도큐먼트를 제공합니다.]]></summary></entry><entry><title type="html">깃헙 액션(Github Action)을 로컬에서 실행하기</title><link href="https://www.palgle.com/2024/03/02/run-github-actions-locally/" rel="alternate" type="text/html" title="깃헙 액션(Github Action)을 로컬에서 실행하기" /><published>2024-03-02T13:54:00+00:00</published><updated>2024-03-02T13:54:00+00:00</updated><id>https://www.palgle.com/2024/03/02/run-github-actions-locally</id><content type="html" xml:base="https://www.palgle.com/2024/03/02/run-github-actions-locally/"><![CDATA[<p>깃헙이 개발에 차지하는 비중이 커지면서, 깃헙 액션을 이용해서 CI와 CD를 구현하는데, 깃헙 액션을 테스트 하기 위한 방법으로 로컬에서 깃헙 액션을 구동하는 방법을 알아봅니다.</p>

<p><img src="/assets/images/github-and-docker.png" alt="" /></p>

<h2 id="준비">준비</h2>

<p>글의 분량 상 맥을 기준으로 합니다. 윈도우라고 하더라도 순서는 같으며, 핵심이 되는 <code class="language-plaintext highlighter-rouge">act</code>는 윈도우에서도 구동이 되기 때문에 아무런 문제가 없습니다.</p>

<ul>
  <li>Homebrew</li>
  <li>Git</li>
  <li>도커(Docker)</li>
  <li>act</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/bin/bash <span class="nt">-c</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="si">)</span><span class="s2">"</span>
<span class="c"># Install Homebrew</span>

brew <span class="nb">install </span>git
<span class="c"># Install git</span>
</code></pre></div></div>

<p>그리고 도커를 구동하기 위해서 <a href="https://www.docker.com/products/docker-desktop/">Docker Desctop</a>을 홈페이지에서 다운 받은 후 설치한 후 설정을 체크합니다.</p>

<p><img src="/assets/images/docker-setting.png" alt="도커 설정" /></p>

<ul>
  <li>Allow the default Docker socket to be used (requires password)</li>
  <li>Allow privileged port mapping (requires password)</li>
  <li>Automatically check configuration</li>
</ul>

<p>터미널에서 <code class="language-plaintext highlighter-rouge">docker</code> 명령어가 작동하는지 확인한 후 작동하지 않으면 <code class="language-plaintext highlighter-rouge">.zshrc</code> 파일에서 아래를 추가해 줍니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:~/.docker/bin

<span class="nb">source</span> .zshrc
</code></pre></div></div>

<p>그 후 마지막으로 <a href="https://nektosact.com/installation/homebrew.html">act를 설치</a>합니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>act
<span class="c"># Install act</span>
</code></pre></div></div>

<h2 id="실습">실습</h2>

<p>깃헙 액션이 있는 레포지토리를 클로닝 합니다. 만약 그런 레포지토리가 없거나 알지 못한다면 아래와 같이 해 보세요.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/cable8mm/n-format

<span class="nb">cd </span>n-format
</code></pre></div></div>

<p>그 후 <code class="language-plaintext highlighter-rouge">act -l</code> 명령어를 실행하면 깃헙 액션 리스트가 보입니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>INFO[0000] Using docker host <span class="s1">'unix:///var/run/docker.sock'</span>, and daemon socket <span class="s1">'unix:///var/run/docker.sock'</span>
Stage  Job ID   Job name  Workflow name       Workflow file  Events
0      phplint  phplint   PHP Linting <span class="o">(</span>Pint<span class="o">)</span>  lint.yml       workflow_dispatch,push
0      build    build     Test                test.yml       push,pull_request
</code></pre></div></div>

<p>Job name이 <code class="language-plaintext highlighter-rouge">phplint</code> 와 <code class="language-plaintext highlighter-rouge">build</code> 가 보이는데요, 잡 이름으로 실행하세요.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>act <span class="nt">-j</span> phplint
act <span class="nt">-j</span> build
</code></pre></div></div>

<h2 id="마무리">마무리</h2>

<p>로컬에서 모든 깃헙 액션이 실행되는 것은 아닙니다. 예를 들어서, 제가 즐겨 쓰는 <code class="language-plaintext highlighter-rouge">stefanzweifel/git-auto-commit-action@v4</code> 액션은 코딩 스타일을 검사해서 수정한 후 자동으로 커밋해 주는 액션인데요, act로 실행할 경우 오토커밋이 작동하지 않기 때문에 이 단계에서 멈춥니다.</p>

<p>물론 깃헙과의 인증 프로세스를 만든다면 이런 문제가 없겠지만, 우리가 하려는 것은 로컬에서 구동하는 것이지 깃헙과의 연동이 아니겠죠.</p>

<p>다른 사람이 만들어 놓은 액션을 그대로 사용한다면 로컬에서 깃헙 액션을 구동하는 것이 의미가 없겠지만, 깃헙 액션으로 CICD를 구현하는 등 직접 액션을 만든다면 깃헙에 코드를 푸쉬하고 액션을 구동하고 수정하고 다시 푸쉬하고, 이런 작업을 몇일 동안 반복하는데 로컬 구동은 많은 도움이 됩니다.</p>

<p>도커는 코딩할 때에는 특히 맥 사용자라면 그다지 도움이 되지 않을 수도 있으나, 배포나 테스팅의 경우에는 워크플로(workflow)를 만드는데 도움이 됩니다.</p>

<p>도커의 전문가가 될 필요는 없지만, 도커 사용 방법은 알아두는걸 추천합니다.</p>]]></content><author><name>Samgu Lee</name></author><category term="development" /><category term="github" /><category term="action" /><category term="local" /><summary type="html"><![CDATA[깃헙이 개발에 차지하는 비중이 커지면서, 깃헙 액션을 이용해서 CI와 CD를 구현하는데, 깃헙 액션을 테스트 하기 위한 방법으로 로컬에서 깃헙 액션을 구동하는 방법을 알아봅니다.]]></summary></entry><entry><title type="html">라라벨 Herd 설치 후 Xdebug 에러 수정하는 법</title><link href="https://www.palgle.com/2024/03/01/fix-error-to-install-xdebug-for-herd/" rel="alternate" type="text/html" title="라라벨 Herd 설치 후 Xdebug 에러 수정하는 법" /><published>2024-03-01T14:42:00+00:00</published><updated>2024-03-01T14:42:00+00:00</updated><id>https://www.palgle.com/2024/03/01/fix-error-to-install-xdebug-for-herd</id><content type="html" xml:base="https://www.palgle.com/2024/03/01/fix-error-to-install-xdebug-for-herd/"><![CDATA[<p>라라벨 Valet을 대신할 수 있는 Laravel Herd를 사용하면 편하게 개발 환경을 만들 수 있지만, XDebug 에러가 날 경우가 있습니다. 에러를 해결할 수 있는 방법을 알려 드립니다.</p>

<p><img src="/assets/images/herdxdebug.png" alt="라라벨 Herd와 Xdebug" /></p>

<p>라라벨 Valet은 커맨드 만으로 mysql, dnsmasq, php, nginx, 그리고 https와 NAT 외부에서 접속할 수 있게 하는 등 다른 프레임워크에 비해서 굉장히 편한 개발환경을 만들어 줍니다. 10분도 안되서 개발 환경을 만들 수 있죠.</p>

<p>반면 라라벨 Herd는 라라벨 Valet을 건들이지 않고도 더 편하고 빠른 개발환경을 만들 수 있으며, <a href="https://herd.laravel.com/docs/1/advanced-usage/php-versions">php나 node 버전 업데이트 등도 쉽게 관리</a>할 수 있고 맥 네이티브 UI 를 제공하므로 사용하지 않을 이유가 없습니다.</p>

<p>다만, 에러가 생길 경우 Herd는 생긴지 얼마 되지 않기 때문에 조금 까다로우며, 이 글에서 다루는 에러도 해결을 하는데 8시간이나 걸렸습니다.</p>

<h2 id="xdebug-설정">Xdebug 설정</h2>

<p>라라벨 Herd는 유료인 Pro 버전을 제공하는데요, 그 경우 <a href="https://herd.laravel.com/docs/1/advanced-usage/xdebug">XDebug를 자동으로 관리</a> 해 줍니다. 무료 버전의 경우 아래와 같이 XDebug를 설정할 수 있죠.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">zend_extension</span><span class="o">=</span>/Applications/Herd.app/Contents/Resources/xdebug/xdebug-83-arm64.so
xdebug.mode<span class="o">=</span>debug,develop
xdebug.start_with_request<span class="o">=</span><span class="nb">yes
</span>xdebug.start_upon_error<span class="o">=</span><span class="nb">yes</span>
</code></pre></div></div>

<p>공식 문서에 따르면 위의 코드를 <code class="language-plaintext highlighter-rouge">php.ini</code> 파일 안에 넣습니다. <code class="language-plaintext highlighter-rouge">php.ini</code>는 화면 최 상단 Herd 아이콘을 클릭 후 나오는 <code class="language-plaintext highlighter-rouge">Show php.ini</code> 을 클릭하면 finder가 바로 뜹니다.</p>

<p><img src="/assets/images/laravel-herd-icon.png" alt="라라벨 Herd에서 php.ini 바로가기 버튼" /></p>

<h2 id="xdebug-에러">Xdebug 에러</h2>

<p>그런데, 위와 같이 하더라도 Xdebug와 php가 연결이 되지 않을 때가 있습니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ herd restart
Xdebug: <span class="o">[</span>Step Debug] Could not connect to debugging client. Tried: localhost:9003 <span class="o">(</span>through xdebug.client_host/xdebug.client_port<span class="o">)</span><span class="nb">.</span>
Xdebug: <span class="o">[</span>Step Debug] Could not connect to debugging client. Tried: localhost:9003 <span class="o">(</span>through xdebug.client_host/xdebug.client_port<span class="o">)</span><span class="nb">.</span>
Restarting DNSMasq...
Restarting PHP 8.2...
Restarting Nginx...
Herd services have been restarted.
</code></pre></div></div>

<p>Herd 재시작을 하면 DNSMasq, PHP, Nginx 모두 리스타트가 되지만, Xdebug는 작동하지 않습니다. 이 경우 어떻게 해결할까요?</p>

<h2 id="xdebug-에러-수정하는-법">Xdebug 에러 수정하는 법</h2>

<p>라라벨 Herd의 설명대로 <code class="language-plaintext highlighter-rouge">php.ini</code>에 넣고 아래 한 줄을 더 추가 해 줍니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xdebug.log_level<span class="o">=</span>0
</code></pre></div></div>

<p>이 명령어는 실행 로그를 멈추라는 뜻입니다.</p>

<p>이후 라라벨 Herd를 재시작하면 Xdebug가 웹이던 cli던 제대로 작동됨을 확인할 수 있습니다.</p>

<h2 id="도움글">도움글</h2>

<p>전 라라벨 Herd가 라라벨 valet에 맥 UI를 올린 것이라고 생각했습니다만, 라라벨 Herd는 valet과는 완전히 다르게 개발 환경을 관리 해 줍니다. 물론 Herd는 valet과 충돌되지 않는다고 이야기하지만, 전 이번 에러를 해결하면서 valet을 완전히 삭제했으며, 기존에 설치된 php 및 pecl 모두 삭제를 했습니다.</p>

<p>동일한 일을 두가지 프로그램이 할 경우 특정한 상황에서 문제를 발생할 수 있으니까요.</p>

<p>만약 맥에서 php 개발이 처음이라면 valet을 설치하지 말고 Herd를 설치하는 것이 더 쾌적한 개발 환경을 만들 수 있습니다.</p>]]></content><author><name>Samgu Lee</name></author><category term="development" /><category term="tips" /><category term="laravel" /><category term="herd" /><category term="xdebug" /><summary type="html"><![CDATA[라라벨 Valet을 대신할 수 있는 Laravel Herd를 사용하면 편하게 개발 환경을 만들 수 있지만, XDebug 에러가 날 경우가 있습니다. 에러를 해결할 수 있는 방법을 알려 드립니다.]]></summary></entry><entry><title type="html">내가 선택한 최고의 블로깅 플랫폼 Jekyll</title><link href="https://www.palgle.com/2024/02/26/my-best-blogging-tool/" rel="alternate" type="text/html" title="내가 선택한 최고의 블로깅 플랫폼 Jekyll" /><published>2024-02-26T14:32:00+00:00</published><updated>2024-02-26T14:32:00+00:00</updated><id>https://www.palgle.com/2024/02/26/my-best-blogging-tool</id><content type="html" xml:base="https://www.palgle.com/2024/02/26/my-best-blogging-tool/"><![CDATA[<p>여러가지 블로깅 플랫폼을 사용해 본 후 Jekyll를 선택한 이유를 설명해 보겠습니다.</p>

<p><img src="/assets/images/jekyll-og.png" alt="JamStack 블로깅 플랫폼 Jekyll" /></p>

<p>Jekyll(/ˌdʒek.əl/)은 한국 발음으로 제컬이라고 하며, 지킬박사와 하이드에서의 그 지킬이 Jekyll 입니다. <a href="https://jekyllrb.com">Jekyll</a>은 데이터베이스를 사용하지 않고, 블로거가 작성한 마크다운 타입의 문서를 HTML로 변환해서 보내는 역할을 하는 일종의 사이트 빌더입니다.</p>

<blockquote>
  <p>Transform your plain text into static websites and blogs.</p>

  <p>- Jykyll</p>
</blockquote>

<h2 id="jekyll의-장점-서버비까지-100-무료">Jekyll의 장점, 서버비까지 100% 무료</h2>

<p>깃헙이 공식적으로 지원하고 있으며, Jekyll을 사용하면 무료로 <a href="https://pages.github.com">깃헙 페이지</a>에 나의 블로그를 만들고, 도메인을 연결할 수 있습니다.</p>

<p><img src="/assets/images/jekull-showcase.png" alt="Jekyll의 쇼케이스" /></p>

<p>내가 블로깅 툴을 운영하면서 필요했던 것은 세가지 입니다.</p>

<ol>
  <li>내가 만든 문서에 HTML코드나 디자인, 혹은 광고 관련 코드가 저장되서 저장되면 안된다.</li>
  <li>나의 도메인과 연결할 수 있어야 하며, 사용제약이 있으면 안된다.</li>
  <li>테마가 있어야 하며, 테마를 커스텀할 수도 있어야 한다.</li>
</ol>

<p>내가 사용해 본 블로깅 플랫폼은 <code class="language-plaintext highlighter-rouge">wordpress</code>, <code class="language-plaintext highlighter-rouge">tattertools</code>, <code class="language-plaintext highlighter-rouge">tistory</code>, <code class="language-plaintext highlighter-rouge">naver blog</code>, <code class="language-plaintext highlighter-rouge">naver post</code>, <code class="language-plaintext highlighter-rouge">google blogger</code>, <code class="language-plaintext highlighter-rouge">gitbooks</code>, <code class="language-plaintext highlighter-rouge">Wix</code> 등 입니다.</p>

<p>이 모든 툴들은 데이터베이스를 이용하며, 디자인 코드가 나의 글에 포함되기 때문에 콘텐츠에 집중하기가 쉽지 않습니다. 이 문제를 해결하기 위해서 해외에서는 <code class="language-plaintext highlighter-rouge">Medium</code>이나 <code class="language-plaintext highlighter-rouge">dev.to</code> 등이 나오기도 했습니다.</p>

<h2 id="jamstack-블로깅-플랫폼">JamStack 블로깅 플랫폼</h2>

<p>내가 필요한 두가지 기능을 지원하는 블로깅 플랫폼은 없었는데요, 순수 HTML 개발 환경을 추구하는 <code class="language-plaintext highlighter-rouge">JamStack</code>을 공부하면서 알게 된 웹사이트 제네레이터 중 몇가지가 눈에 띄었습니다.</p>

<ol>
  <li><a href="https://jekyllrb.com">Jekyll</a></li>
  <li><a href="https://docusaurus.io">Docusaurus</a></li>
  <li><a href="https://astro.build">Astro</a></li>
</ol>

<p>이 세가지 플랫폼은 모두 깃헙 페이지에 내 글을 올리고 도메인을 연결할 수 있습니다. 또한 마크다운으로 글을 쓰면 대부분의 것들이 자동으로 이루어 집니다.</p>

<blockquote>
  <p>Jekyll is one of the most mature static site generators around and has been a great tool to use — in fact, before Docusaurus, most of Facebook’s Open Source websites are/were built on Jekyll! It is extremely simple to get started. We want to bring a similar developer experience as building a static site with Jekyll.</p>

  <p>- docusaurus 도움말 중 <a href="https://docusaurus.io/docs#design-principles">디자인 철학 섹션</a> 일부 인용</p>
</blockquote>

<p>특히 Docusaurus는 메타(구 페이스북)가 Jekyll로 사이트를 제작하다가 그 이상의 기능이 필요해서 직접 만들어서 배포하는 것으로 웹사이트 빌더로 손색이 없습니다.</p>

<p>전 너무 많은 기능을 좋아하지 않습니다. 제가 원하는 것은 마크다운으로 문서만 만들면 나머지는 자동으로 배포가 되어야 하며, 서버 관리 포함해서 아무런 행위도 필요하지 않아야 한다는 것입니다.</p>

<h2 id="그래서-선택한-jekyll">그래서 선택한 Jekyll</h2>

<p>Jekyll을 선택한 이유는 <a href="https://docs.github.com/ko/pages/setting-up-a-github-pages-site-with-jekyll/about-github-pages-and-jekyll">깃헙이 공식적으로 지원하는 유일한 플랫폼</a>이라는 점이지만, 제작 철학도 저와 잘 맞았습니다. Jekyll의 개발 커뮤니티를 보면 사용하는 개발자들이 여러가지 제안을 하지만, 글쓰기 작업에 부가적인 행위가 필요할 경우 거부됩니다.</p>

<p><img src="/assets/images/github-page-screenshot.png" alt="글의 이력을 관리할 수 있는 깃헙 PR" /></p>

<p>즉, 오랜 기간이 지났지만 여전히 Jekyll는 <a href="https://docs.github.com/ko/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax">마크다운</a>을 만드는 것으로 모든 작업이 마무리 됩니다.</p>

<p>전 문서를 작성하는 도구로 <a href="https://code.visualstudio.com">Visual Studio Code</a> + Markdown Preview 를 사용하며, 자동화 툴로 아래의 것들을 이용합니다.</p>

<ol>
  <li>문서 포멧 맞춤기</li>
  <li>깨진 링크 리포팅</li>
  <li>나의 깃헙 저장소에서 글 작성이 마무리 후 배포</li>
  <li>배포 전에 나의 컴퓨터에서 미리보기</li>
</ol>

<h2 id="마크다운과-jekyll-제약이-주는-편안함">마크다운과 Jekyll, 제약이 주는 편안함</h2>

<p>블로깅을 하게 되면 디자인과 기능에 상당히 신경이 쓰입니다. 그래서 내 글에 기능과 디자인 코드가 포함됩니다.</p>

<p>임계점이 넘어가면 내 글은 이미 글이 아니라 브로셔가 되어 가며, 블로그 도구를 이전할 수도 없게 되며, 최종적으로는 글을 작성하지 않게 됩니다.</p>

<p><img src="/assets/images/my-writing-screenshot.png" alt="글쓰기에 집중할 수 있는 마크다운 편집기" /></p>

<p>마크다운은 글의 색깔도 바꿀 수 없습니다. 타이틀이 아니면 폰트 크기도 키울 수 없죠. 이런 제약은 모바일이던 컴퓨터던 보는 사람이 편하게 볼 수 있는 기능을 플랫폼에서 추가할 수 있게 해 줍니다.</p>

<p>그리고, 웹 편집기에 비해서 비교도 안되게 편한 마크다운 편집기를 컴퓨터에 설치해서 글을 쓰는 것은 최고의 경험을 제공합니다.</p>

<p><img src="/assets/images/jekyll-sample-blog.png" alt="테마 기능을 제공하는 Jekyll" /></p>

<p>이 것이 제가 돌아돌아 Jekyll로 온 이유입니다.</p>

<p>Jekyll가 어떻게 관리되고 있는지는 <a href="https://github.com/cable8mm/cable8mm.github.io">레포지토리를 오픈</a>해 놨으니 누구나 볼 수 있습니다.</p>]]></content><author><name>Samgu Lee</name></author><category term="blog" /><category term="opinion" /><summary type="html"><![CDATA[여러가지 블로깅 플랫폼을 사용해 본 후 Jekyll를 선택한 이유를 설명해 보겠습니다.]]></summary></entry><entry><title type="html">Gradle의 버전</title><link href="https://www.palgle.com/2024/02/25/talk-about-gradle-versions/" rel="alternate" type="text/html" title="Gradle의 버전" /><published>2024-02-25T16:54:00+00:00</published><updated>2024-02-25T16:54:00+00:00</updated><id>https://www.palgle.com/2024/02/25/talk-about-gradle-versions</id><content type="html" xml:base="https://www.palgle.com/2024/02/25/talk-about-gradle-versions/"><![CDATA[<p>자바 커뮤니티에서 패키지매니저는 구글이 안드로이드에서 <code class="language-plaintext highlighter-rouge">Gradle</code>의 손을 들어주면서 <code class="language-plaintext highlighter-rouge">Ant</code>와 <code class="language-plaintext highlighter-rouge">Maven</code> 보다 점유율을 높이고 있습니다. <code class="language-plaintext highlighter-rouge">Gradle</code>에서 버전과 그 밖의 것들을 이야기 해 보겠습니다.</p>

<p><img src="/assets/images/dependency-management-configurations.png" alt="Gradle 의존성과 빌드" /></p>

<p>편의상 패키지매니저라고 언급했으나, Gradle은 빌드 매니저입니다. 패키지 관리는 <a href="https://mvnrepository.com/">Maven의 것</a>을 이용하고 있습니다. 하지만, 우리가 패키지를 사용할 때 Gradle을 통해서 선언하기 때문에 빌드로 Gradle을 사용한다면, 패키지 선언도 Gradle을 사용해야 하므로, 사용하는 입장에서 패키지 의존성을 Gradle이 해결하는 느낌을 받습니다.</p>

<p>반면 패키지 자체를 개발할 경우에는 Gradle의 <a href="https://docs.gradle.org/current/userguide/publishing_maven.html">Maven Publish Plugin</a>을 이용해서 배포할 수 있습니다.</p>

<h2 id="gradle-버전-표기법">Gradle 버전 표기법</h2>

<p>먼저 아래의 선언(declaration)을 보시죠.</p>

<div class="language-gradle highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">dependencies</span> <span class="o">{</span>
    <span class="n">implementation</span><span class="o">(</span><span class="s2">"org.slf4j:slf4j-api:1.7.15"</span><span class="o">)</span>
<span class="o">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">org.slf4j:slf4j-api</code>는 패키지 이름이고, <code class="language-plaintext highlighter-rouge">1.7.15</code>이 버전입니다. 정확히 1.7.15버전을 설치하라는 의미인데요, 보통은 이렇게 쓰지 않고 1.7.x 패키지 중 높은 버전으로 설치하라고 선언합니다.</p>

<div class="language-gradle highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">implementation</span><span class="o">(</span><span class="s2">"org.slf4j:slf4j-api:1.7.+"</span><span class="o">)</span> <span class="c1">// 1.7.x 중 높은 버전</span>
<span class="n">implementation</span><span class="o">(</span><span class="s2">"org.slf4j:slf4j-api:1.+"</span><span class="o">)</span> <span class="c1">// 1.x.x 중 높은 버전</span>
<span class="n">implementation</span><span class="o">(</span><span class="s2">"org.slf4j:slf4j-api:[1.1, 2.0)"</span><span class="o">)</span> <span class="c1">// 1.1.x 중 높은 버전 혹은 2.x 중 2.0이 아니며 높은 버전</span>
<span class="n">implementation</span><span class="o">(</span><span class="s2">"org.slf4j:slf4j-api:[1.1, [2.1"</span><span class="o">)</span> <span class="c1">// 1.1.x 중 높은 버전 혹은 2.1.x 중 높은 버전</span>
</code></pre></div></div>

<p>괄호를 이용한 버전 범위 선언은 Maven에서 사용하는 스타일인데요, 여전히 Gradle에서도 사용할 수 있습니다.</p>

<p>만약 버전에 관계없이 최신 버전을 선언할 경우 <code class="language-plaintext highlighter-rouge">latest.integration</code> 혹은 <code class="language-plaintext highlighter-rouge">latest.release</code> 을 사용할 수 있습니다.</p>

<h2 id="gradle-버전-표기가-없는-경우">Gradle 버전 표기가 없는 경우</h2>

<p><a href="https://start.spring.io/">Spring initializr</a>에서 스켈레톤을 만들 경우 <code class="language-plaintext highlighter-rouge">build.gradle</code> 파일을 보면 버전 표기가 되어 있지 않습니다.</p>

<div class="language-gradle highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">dependencies</span> <span class="o">{</span>
  <span class="n">implementation</span> <span class="s1">'org.springframework.boot:spring-boot-starter'</span>
  <span class="n">testImplementation</span> <span class="s1">'org.springframework.boot:spring-boot-starter-test'</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Gradle 에서는 매우 큰 프로젝트의 경우 버전을 표기하지 말고 <code class="language-plaintext highlighter-rouge">constraints</code> 즉 의존성을 이용하라고 권장합니다.</p>

<div class="language-gradle highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">dependencies</span> <span class="o">{</span>
    <span class="n">implementation</span><span class="o">(</span><span class="s2">"org.springframework:spring-web"</span><span class="o">)</span>
<span class="o">}</span>

<span class="k">dependencies</span> <span class="o">{</span>
    <span class="n">constraints</span> <span class="o">{</span>
        <span class="n">implementation</span><span class="o">(</span><span class="s2">"org.springframework:spring-web:5.0.2.RELEASE"</span><span class="o">)</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>단순히 버전을 표기하지 않으면 저장소에서 가장 최신의 버전을 다운로드 합니다. 그리고, Spring Boot의 경우 버전에 관한 내용은 <a href="https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-starter-parent/2.5.2/spring-boot-starter-parent-2.5.2.pom">spring-boot-starter-parent</a> 의 <a href="https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-dependencies/2.5.2/spring-boot-dependencies-2.5.2.pom">spring-boot-dependencies</a>에 정의되어 있습니다.</p>

<h2 id="버전-업데이트하기">버전 업데이트하기</h2>

<p>Gradle에서 버전을 업데이트하는 명령어는 아래와 같습니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./gradlew refreshVersions
</code></pre></div></div>

<p>의존성이 해석된 다운로드 된 버전은 <code class="language-plaintext highlighter-rouge">Binary Reposity</code>라는 곳에 저장되며, 이 폴더는 보통 프로젝트가 아닌 컴퓨터에 저장되며, <code class="language-plaintext highlighter-rouge">~/.gradle/caches</code> 폴더에 저장됩니다.</p>

<p><img src="/assets/images/gradle-init.png" alt="Gradle init" /></p>

<p>gradle 프로젝트를 만들 경우 <code class="language-plaintext highlighter-rouge">gradle init</code> 명령어로 프로젝트에 필요한 폴더와 파일들을 만들 수 있습니다.</p>

<p>참고로 Gradle은 빌드매니저인 만큼 <code class="language-plaintext highlighter-rouge">tasks</code>라는 명령어도 관리를 해야 하는데요, 명령어의 집합을 plugins 라는 이름으로 관리합니다.</p>

<div class="language-gradle highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plugins</span> <span class="o">{</span>
	<span class="n">java</span>
	<span class="nf">id</span><span class="o">(</span><span class="s2">"org.springframework.boot"</span><span class="o">)</span> <span class="n">version</span> <span class="s2">"3.2.3"</span>
	<span class="n">id</span><span class="o">(</span><span class="s2">"io.spring.dependency-management"</span><span class="o">)</span> <span class="n">version</span> <span class="s2">"1.1.4"</span>
<span class="o">}</span>
</code></pre></div></div>

<p>스프링 부트를 설치하면 <code class="language-plaintext highlighter-rouge">build.gradle</code> 파일 가장 위에 <code class="language-plaintext highlighter-rouge">plugins</code>가 선언되어 있는데요, 각 패키지에서 만든 태스크(일종의 명령어)가 들어있습니다. 스프링 부트를 실행하는 <code class="language-plaintext highlighter-rouge">bootRun</code>도 여기에 정의되어 있기 때문에 <code class="language-plaintext highlighter-rouge">gradlew bootRun</code> 으로 스프링 부트를 구동할 수 있게 됩니다.</p>

<p>PHP의 composer나 Node의 npm, yarn 은 스크립트 언어이기 때문에 빌드를 할 필요가 없지만, 자바는 컴파일 언어이기 때문에 패키지 제작자가 빌드 시스템에 맞는 빌드 스크립트를 제작해 줄 필요가 있습니다.</p>

<p>쉽게 이야기하자면 하나의 프로그램으로 맥과 윈도우 양쪽에 배포한다면 코드는 그대로지만, 빌드와 아티펙트 배포 등의 빌드 스크립트는 두개를 작성해야 하는 것과 비슷합니다.</p>

<p>자바는 전세계 모든 디바이스에서 구동을 하기 위해 만들어 졌는데요, 그것을 구현하기 위해서 프로그래머는 프로그램을 제작하는 것 이상으로 수 많은 빌드 시스템에서 빌드가 제대로 되는 것을 보장해야 하기 때문에 자바 생태계는 Node나 PHP, Python 등의 스크립트 언어에 비해서 생태계가 빠르게 확장되지는 않으며, 커뮤니티 보다는 기업 위주의 생태계가 만들어 지는 이유이기도 합니다.</p>

<p>물론 이 것은 자바에만 있는 것이 아니라 C, C++, C# 등 빌드가 필요한 모든 언어들에 생기는 공통의 문제입니다. 이 문제를 해결한 golang은 빌드 문제를 배포시스템에서 완전히 해결하는 구조이기 때문에 컴파일 언어임에도 불구하고 매우 빠르게 생태계가 만들어 지고 있습니다.</p>]]></content><author><name>Samgu Lee</name></author><category term="development" /><category term="gradle" /><summary type="html"><![CDATA[자바 커뮤니티에서 패키지매니저는 구글이 안드로이드에서 Gradle의 손을 들어주면서 Ant와 Maven 보다 점유율을 높이고 있습니다. Gradle에서 버전과 그 밖의 것들을 이야기 해 보겠습니다.]]></summary></entry><entry><title type="html">스프링 부트를 위한 최고의 IDE 선택하기</title><link href="https://www.palgle.com/2024/02/24/which-ide-is-the-best-for-you/" rel="alternate" type="text/html" title="스프링 부트를 위한 최고의 IDE 선택하기" /><published>2024-02-24T14:25:00+00:00</published><updated>2024-02-24T14:25:00+00:00</updated><id>https://www.palgle.com/2024/02/24/which-ide-is-the-best-for-you</id><content type="html" xml:base="https://www.palgle.com/2024/02/24/which-ide-is-the-best-for-you/"><![CDATA[<p>스프링 부트의 공식 IDE는 다섯가지를 소개하고 있고, 주력으로 네가지를 제안합니다. 그 중 어떤 도구가 나에게 최고의 IDE일까요? 알아봅시다.</p>

<p><img src="/assets/images/best_ide_for_spring_boot_for_you.jpg" alt="스프링 부트 개발에서 사용할 수 있는 IDE 다섯가지" /></p>

<p>스프링의 공식 홈페이지에서는 특정 IDE를 다루지는 않지만, 주로 소개하는 IDE는 아래 네가지 입니다.</p>

<ul>
  <li>IntelliJ IDEA from JetBrains</li>
  <li>Visual Studio Code with Spring Boot Extension Pack from Microsoft</li>
  <li>Spring Tools 4 for Eclipse</li>
  <li>Spring Tools 4 for Theia</li>
</ul>

<p>이 중 <a href="https://theia-ide.org/">Theia</a> 는 점유율이 낮고, 공식문서에서도 제외되는 경우가 많기 때문에 앞의 세가지 IDE만을 다루겠습니다.</p>

<h2 id="intellij-idea-from-jetbrains">IntelliJ IDEA from JetBrains</h2>

<p>Java 관련 개발의 최고의 도구라고 할 수 있습니다. 기업용 도구인 <a href="https://netbeans.apache.org/front/main/index.html">넷빈스(NetBeans)</a>가 있었지만, 넷빈스를 보유했던 썬 마이크로시스템즈(Sun microsystems)가 오라클에게 인수된 후 넷빈스는 2016년에 아파치에 기부되었고, 아파치 인큐베이터에서 관리되고 있습니다. 아파치는 최고의 백엔드 시스템을 론칭한 이력이 있으나, 컨슈머용이나 개발자용 UI 도구를 운영하기에는 다소 보수적인 면이 있습니다.</p>

<p><img src="/assets/images/screenshot_intellij_ide.png" alt="IntelliJ 홈페이지 스크린샷" /></p>

<p>반면 IntelliJ를 만들고 판매하는 JetBrains는 과거 <a href="https://www.embarcadero.com/products/delphi">델파이가 누렸던 영광</a>을 재현하는데 성공하는 것으로 보이며, Apple에는 XCode, Microsoft에는 Visual Studio가 있었지만, 다른 나머지 기업들이 사용했던 Eclipse의 불편함을 각 개발 플랫폼 별로 제품으로 만들어 판매하고 있다는 점입니다.</p>

<p>지금까지 자바 개발 IDE에 있어서 최고의 툴은 단연 IntelliJ 라고 할 수 있습니다. 한가지 단점은 <a href="https://www.jetbrains.com/idea/buy/?section=personal&amp;billing=yearly">개인이 사용하기에는 라이센스 비용이 있습니다</a>.</p>

<table>
  <thead>
    <tr>
      <th>IDE</th>
      <th>1년 구독 금액(개인)</th>
      <th>1년 구독 금액(기업)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://www.jetbrains.com/idea/buy/?section=commercial&amp;billing=yearly">IntelliJ</a></td>
      <td>US $169.00</td>
      <td>US $599.00</td>
    </tr>
    <tr>
      <td><a href="https://visualstudio.microsoft.com/vs/pricing/?tab=business">Visual Studio</a></td>
      <td>Free(Community vesion)</td>
      <td>US $250.00</td>
    </tr>
  </tbody>
</table>

<p>개인에 한해서라면 <em>Unity</em>, <em>Android Studio</em>, <em>XCode</em>, <em>Visual Studio Code</em> 등 대부분의 IDE가 무료라는 것을 생각한다면 <em>IntelliJ</em> 의 유료 정책은 다소 아쉽지만, 자바 개발에 있어서 압도적인 효율과 새로운 기술을 가장 빠르게 적용해 준다는 것을 고려한다면 충분한 가치가 있습니다.</p>

<p>자바나 스프링을 주력 개발 언어와 플랫폼으로 사용한다면 최고의 선택입니다.</p>

<h2 id="visual-studio-code-with-spring-boot-extension-pack-from-microsoft">Visual Studio Code with Spring Boot Extension Pack from Microsoft</h2>

<p>스프링 공식 홈페이지에서는 예전에는 Eclipse나 STS(Spring Tool Shite)를 우선했습니다. 예를 들어, 가이드가 도큐먼트에서 IDE를 설명할 때 Eclipse나 STS가 첫번째로 묘사가 되었는데요, 이 부분이 미묘하게 바뀌고 있습니다.</p>

<p><img src="/assets/images/home-screenshot-mac-lg-2x.png" alt="VSCode screenshot" /></p>

<p>이미 STS라는 Eclipse를 기반으로 한 단독 IDE는 소개하고 있지 않습니다. 그리고, 도큐먼트에서도 이 자리를 Visual Studio Code(VSCode)가 대치하고 있습니다.</p>

<p>따라서, 현재 VSCode를 이용해서 스프링 부트 개발환경을 만드는데는 심각한 문제는 없습니다. 오히려 Eclipse 기반의 툴이 UI에서 모든 기능을 구현하기 때문에 IDE에서 지원하지 않는 기능은 사용할 수 없지만, VSCode는 일반적으로 Extension과 커맨드를 동시에 사용하기 때문에 이득이 있는 경우도 있습니다.</p>

<p>만약 여러분의 개발 스택이 자바에 국한되지 않고, IntelliJ에 매월 혹은 매년 지불할 가치가 적거나 없다고 생각한다면, VSCode가 최고의 선택입니다.</p>

<h2 id="spring-tools-4-for-eclipse">Spring Tools 4 for Eclipse</h2>

<p>IntelliJ와 마찬가지로 Spring Tools 4는 Eclipse 기반으로 만들어 졌지만, 느립니다. 그런데 더 큰 문제가 있습니다.</p>

<p><img src="/assets/images/eclipse-shot.jpg" alt="Spring Tools 4 for Eclipse 스크린샷" /></p>

<p>이클립스라는 도구는 자바로 만드는 모든 개발을 할 수 있도록 설계되었고, 그 부분은 아직까지 유지되고 있는데, 문제가 되는 부분이 바로 <strong>workspace</strong>라는 개념입니다.</p>

<p>VSCode의 경우 사실 <a href="https://code.visualstudio.com/docs/editor/workspaces">workspace</a>, project라는 개념이 없습니다. 편의성을 위해서 폴더를 project라고 부르고 있으며, project로써 하는 모든 것은 확장에서 처리합니다.</p>

<blockquote>
  <p>A Visual Studio Code “workspace” is the collection of one or more folders that are opened in a VS Code window (instance).</p>
</blockquote>

<p>반면 이클립스의 workspace가 하는 일은 의외로 많은데요, 이클립스 자체가 필요한 모든 정보를 workspace의 <code class="language-plaintext highlighter-rouge">.metadata</code> 폴더에 넣고, 이클립스를 실행할 때 마다 어떤 <strong>workspace</strong>를 사용할지에 대한 선택을 강요합니다. 그리고, 동일한 <strong>workspace</strong> 내의 projects는 서로 Linking이 되죠.</p>

<p>이 부분이 문제가 되는 이유는 최근의 CICD 환경에서는 소스에서 프로그램을 만들기 위한 <strong>build(빌드))</strong>라는 절차가 자동화가 되고, 빌드에 필요한 모든 정보가 project 내에 설정파일로 넣는데, <strong>workspace</strong>라는 존재가 이를 방해합니다.</p>

<p>IntelliJ는 이런 문제 때문에 더이상 <strong>workspace</strong>를 개발자에게 강요하지 않습니다. 다른 대부분의 IDE도 강제하고 있지 않습니다. 반면 Spring Tools 4 는 <strong>workspace</strong>를 git에 넣을 방법이 모든 프로젝트를 단일 레포지토리에 넣는 것이므로 소스 저장소 관리가 매우 복잡해 집니다.</p>

<p>이런 이유로 학습으로서의 목적이 아니면 Spring Tools 4 for Eclipse는 추천하지 않습니다. VSCode라는 대체제도 있으니까요.</p>

<h2 id="세줄-요약">세줄 요약</h2>

<ul>
  <li>구독이 가능하면 <a href="https://www.jetbrains.com/idea/">IntelliJ</a></li>
  <li>구독이 힘들면 <a href="https://code.visualstudio.com/">VSCode</a></li>
  <li><a href="https://spring.io/tools">Spring Tools 4 for Eclipse</a>는 쓰지 마세요.</li>
</ul>

<p>만약 기존의 프로젝트가 Eclipse과 강력히 결합되어 있다면, 빌드를 자동화하기 위해서 별도의 담당자를 배정해야 합니다. 그 만큼 Eclipse의 workspace는 협업과 소스코드 관리 및 배포에 있어서 위험할 수 있습니다.</p>]]></content><author><name>Samgu Lee</name></author><category term="development" /><category term="spring" /><category term="ide" /><summary type="html"><![CDATA[스프링 부트의 공식 IDE는 다섯가지를 소개하고 있고, 주력으로 네가지를 제안합니다. 그 중 어떤 도구가 나에게 최고의 IDE일까요? 알아봅시다.]]></summary></entry><entry><title type="html">스프링 부트 프로젝트 개발에 커스텀 도메인 연결하기</title><link href="https://www.palgle.com/2024/02/23/spring-boot-with-custom-domain/" rel="alternate" type="text/html" title="스프링 부트 프로젝트 개발에 커스텀 도메인 연결하기" /><published>2024-02-23T22:20:00+00:00</published><updated>2024-02-23T22:20:00+00:00</updated><id>https://www.palgle.com/2024/02/23/spring-boot-with-custom-domain</id><content type="html" xml:base="https://www.palgle.com/2024/02/23/spring-boot-with-custom-domain/"><![CDATA[<p>스프링 부트 환경에서 <code class="language-plaintext highlighter-rouge">http://localhost:8080</code> 이 아닌 <code class="language-plaintext highlighter-rouge">https://spring-boot.test</code> 와 같은 도메인으로 개발할 수 있는 방법을 소개합니다.</p>

<p><img src="/assets/images/spring_boot_and_laravel_valet.png" alt="Spring boot and laravel valet" /></p>

<p>웹사이트를 개발할 때 <code class="language-plaintext highlighter-rouge">localhost</code>나 <code class="language-plaintext highlighter-rouge">127.0.0.1</code>로 테스트를 하게 되면 쿠키와 세션, 그리고 카카오 로그인과 같은 외부 SDK와의 연동 문제 등이 생길 수 있습니다. 따라서, ssl과 커스텀 도메인은 라라벨 에코시스템에서는 필수이지만, 다른 프레임워크에서는 <a href="https://docs.spring.io/spring-boot/docs/current/reference/html/using.html">공식 문서에서도 이 부분을 다루지 않습니다</a>.</p>

<p>이 글에서는 <a href="https://laravel.kr/docs/9.x/valet">라라벨 발렛</a>을 이용해서 <a href="https://spring.io/projects/spring-boot">스프링 부트</a>를 로컬에서 띄우는 방법을 설명합니다.</p>

<p><a href="https://laravel.com/docs/10.x/valet">Laravel Valet</a>을 설치한 후 아래의 순서대로 따라 하세요.</p>

<h2 id="1-valet-secure를-실행합니다">1. <code class="language-plaintext highlighter-rouge">valet secure</code>를 실행합니다</h2>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>spring-boot

valet secure
</code></pre></div></div>

<p>Valet은 간단한 커맨드 만으로 브라우져에서 <a href="https://spring-boot.test">https://spring-boot.test</a> 로 접속을 할 수 있게 만들어 줍니다.</p>

<h2 id="2-nginx-설정을-수정합니다">2. Nginx 설정을 수정합니다</h2>

<p>Nginx 설정파일을 에디터로 열어주세요.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>code /Users/[MAC_NAME]/.config/valet/Nginx/spring-boot.test
</code></pre></div></div>

<p>이제 Valet의 Nginx 설정파일인 <code class="language-plaintext highlighter-rouge">spring-boot.test</code> 파일에서 가장 상단에 다음을 추가합니다.</p>

<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">map</span> <span class="nv">$sent_http_content_type</span> <span class="nv">$expires</span> <span class="p">{</span>
    <span class="kn">"text/html"</span>                 <span class="s">epoch</span><span class="p">;</span>
    <span class="kn">"text/html</span><span class="p">;</span> <span class="kn">charset=utf-8"</span>  <span class="s">epoch</span><span class="p">;</span>
    <span class="kn">default</span>                     <span class="no">off</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>그리고, 아래의 코드를 찾아서</p>

<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">location</span> <span class="n">/</span> <span class="p">{</span>
    <span class="kn">rewrite</span> <span class="s">^</span> <span class="s">"/Users/[MAC_NAME]/.composer/vendor/laravel/valet/server.php"</span> <span class="s">last</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>이렇게 수정합니다.</p>

<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">location</span> <span class="n">/</span> <span class="p">{</span>
    <span class="kn">expires</span> <span class="nv">$expires</span><span class="p">;</span>

    <span class="kn">proxy_redirect</span>                      <span class="no">off</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">Host</span>               <span class="nv">$host</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Real-IP</span>          <span class="nv">$remote_addr</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span>    <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Proto</span>  <span class="nv">$scheme</span><span class="p">;</span>
    <span class="kn">proxy_read_timeout</span>          <span class="mi">1m</span><span class="p">;</span>
    <span class="kn">proxy_connect_timeout</span>       <span class="mi">1m</span><span class="p">;</span>
    <span class="kn">proxy_pass</span>                          <span class="s">http://127.0.0.1:8080</span><span class="p">;</span> <span class="c1"># set the adress of the Spring Boot instance here</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="3-커스텀-도메인으로-접속합니다">3. 커스텀 도메인으로 접속합니다</h2>

<p>설정을 변경한 후 <code class="language-plaintext highlighter-rouge">valet restart</code> 실행 후 <a href="https://spring-boot.test">https://spring-boot.test</a> 로 접속을 하면 <a href="http://127.0.0.1:8080">http://127.0.0.1:8080</a> 으로 접속됩니다.(proxy_pass)</p>

<p>자, 이제 스프링 부트 프로젝트를 실행합니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./gradlew bootRun
</code></pre></div></div>

<p>브라우져에 <a href="https://spring-boot.test">https://spring-boot.test</a> 를 입력하면 스프링 부트 웹사이트를 볼 수 있습니다!</p>

<p><img src="/assets/images/screenshot_custom_domain.png" alt="https와 더불어 커스텀 도메인이 적용되었다" /></p>

<p>개발할 때 도메인을 이용해야 하는 이유는 인증 관련 작업을 할 경우 IP나 localhost의 경우 개발을 하기가 매우 어렵기 때문입니다. 특히 <a href="https://developers.kakao.com/docs/latest/en/kakaologin/common">카카오 로그인</a> 등의 경우는 개발 자체가 불가능할 때도 있습니다.</p>

<p>부가적으로 Valet에는 <a href="https://ngrok.com/"><code class="language-plaintext highlighter-rouge">ngrok</code></a>나 <a href="https://expose.dev/"><code class="language-plaintext highlighter-rouge">expose</code></a>를 이용해서 <a href="https://laravel.com/docs/10.x/valet#sharing-sites">외부에서도 접속할 수 있거나 하는 기능</a>이 있기 때문에 개발 편의성이 높아지는 장점도 있습니다.</p>

<p>라라벨 발렛을 사용하지 않아도 Ngnix나 Apache를 이용해서 <a href="https://medium.com/@steelcityamir/using-apache-as-a-reverse-proxy-for-spring-boot-embedded-tomcat-f704da73e7c8">리버스 프록시를 구성할 수 있습니다</a>.</p>

<p>다음은 <code class="language-plaintext highlighter-rouge">spring-boot.test</code> 설정파일 전문입니다.</p>

<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">map</span> <span class="nv">$sent_http_content_type</span> <span class="nv">$expires</span> <span class="p">{</span>
    <span class="kn">"text/html"</span>                 <span class="s">epoch</span><span class="p">;</span>
    <span class="kn">"text/html</span><span class="p">;</span> <span class="kn">charset=utf-8"</span>  <span class="s">epoch</span><span class="p">;</span>
    <span class="kn">default</span>                     <span class="no">off</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">server</span> <span class="p">{</span>
    <span class="kn">listen</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">80</span><span class="p">;</span>
    <span class="c1">#listen 127.0.0.1:80; # valet loopback</span>
    <span class="kn">server_name</span> <span class="s">demo-for-spring-boot.test</span> <span class="s">www.demo-for-spring-boot.test</span> <span class="s">*.demo-for-spring-boot.test</span><span class="p">;</span>
    <span class="kn">return</span> <span class="mi">301</span> <span class="s">https://</span><span class="nv">$host$request_uri</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">server</span> <span class="p">{</span>
    <span class="kn">listen</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">443</span> <span class="s">ssl</span><span class="p">;</span>
    <span class="c1">#listen VALET_LOOPBACK:443 ssl; # valet loopback</span>
    <span class="kn">server_name</span> <span class="s">demo-for-spring-boot.test</span> <span class="s">www.demo-for-spring-boot.test</span> <span class="s">*.demo-for-spring-boot.test</span><span class="p">;</span>
    <span class="kn">root</span> <span class="n">/</span><span class="p">;</span>
    <span class="kn">charset</span> <span class="s">utf-8</span><span class="p">;</span>
    <span class="kn">client_max_body_size</span> <span class="mi">512M</span><span class="p">;</span>
    <span class="kn">http2</span>  <span class="no">on</span><span class="p">;</span>

    <span class="kn">location</span> <span class="n">/41c270e4-5535-4daa-b23e-c269744c2f45/</span> <span class="p">{</span>
        <span class="kn">internal</span><span class="p">;</span>
        <span class="kn">alias</span> <span class="n">/</span><span class="p">;</span>
        <span class="kn">try_files</span> <span class="nv">$uri</span> <span class="nv">$uri</span><span class="n">/</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kn">ssl_certificate</span> <span class="s">"/Users/cable8mm/.config/valet/Certificates/spring-boot.test.crt"</span><span class="p">;</span>
    <span class="kn">ssl_certificate_key</span> <span class="s">"/Users/cable8mm/.config/valet/Certificates/spring-boot.test.key"</span><span class="p">;</span>

    <span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
        <span class="kn">expires</span> <span class="nv">$expires</span><span class="p">;</span>

        <span class="kn">proxy_redirect</span>                      <span class="no">off</span><span class="p">;</span>
        <span class="kn">proxy_set_header</span> <span class="s">Host</span>               <span class="nv">$host</span><span class="p">;</span>
        <span class="kn">proxy_set_header</span> <span class="s">X-Real-IP</span>          <span class="nv">$remote_addr</span><span class="p">;</span>
        <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span>    <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
        <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Proto</span>  <span class="nv">$scheme</span><span class="p">;</span>
        <span class="kn">proxy_read_timeout</span>          <span class="mi">1m</span><span class="p">;</span>
        <span class="kn">proxy_connect_timeout</span>       <span class="mi">1m</span><span class="p">;</span>
        <span class="kn">proxy_pass</span>                          <span class="s">http://127.0.0.1:8080</span><span class="p">;</span> <span class="c1"># set the adress of the Node.js instance here</span>
    <span class="p">}</span>

    <span class="kn">location</span> <span class="p">=</span> <span class="n">/favicon.ico</span> <span class="p">{</span> <span class="kn">access_log</span> <span class="no">off</span><span class="p">;</span> <span class="kn">log_not_found</span> <span class="no">off</span><span class="p">;</span> <span class="p">}</span>
    <span class="kn">location</span> <span class="p">=</span> <span class="n">/robots.txt</span>  <span class="p">{</span> <span class="kn">access_log</span> <span class="no">off</span><span class="p">;</span> <span class="kn">log_not_found</span> <span class="no">off</span><span class="p">;</span> <span class="p">}</span>

    <span class="kn">access_log</span> <span class="no">off</span><span class="p">;</span>
    <span class="kn">error_log</span> <span class="s">"/Users/cable8mm/.config/valet/Log/nginx-error.log"</span><span class="p">;</span>

    <span class="kn">error_page</span> <span class="mi">404</span> <span class="s">"/Users/cable8mm/.composer/vendor/laravel/valet/server.php"</span><span class="p">;</span>

    <span class="kn">location</span> <span class="p">~</span> <span class="sr">[^/]\.php(/|$)</span> <span class="p">{</span>
        <span class="kn">fastcgi_split_path_info</span> <span class="s">^(.+</span><span class="err">\</span><span class="s">.php)(/.+)</span>$<span class="p">;</span>
        <span class="kn">fastcgi_pass</span> <span class="s">"unix:/Users/cable8mm/.config/valet/valet.sock"</span><span class="p">;</span>
        <span class="kn">fastcgi_index</span> <span class="s">"/Users/cable8mm/.composer/vendor/laravel/valet/server.php"</span><span class="p">;</span>
        <span class="kn">include</span> <span class="s">fastcgi_params</span><span class="p">;</span>
        <span class="kn">fastcgi_param</span> <span class="s">SCRIPT_FILENAME</span> <span class="s">"/Users/cable8mm/.composer/vendor/laravel/valet/server.php"</span><span class="p">;</span>
        <span class="kn">fastcgi_param</span> <span class="s">PATH_INFO</span> <span class="nv">$fastcgi_path_info</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kn">location</span> <span class="p">~</span> <span class="sr">/\.ht</span> <span class="p">{</span>
        <span class="kn">deny</span> <span class="s">all</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">server</span> <span class="p">{</span>
    <span class="kn">listen</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">60</span><span class="p">;</span>
    <span class="c1">#listen 127.0.0.1:60; # valet loopback</span>
    <span class="kn">server_name</span> <span class="s">demo-for-spring-boot.test</span> <span class="s">www.demo-for-spring-boot.test</span> <span class="s">*.demo-for-spring-boot.test</span><span class="p">;</span>
    <span class="kn">root</span> <span class="n">/</span><span class="p">;</span>
    <span class="kn">charset</span> <span class="s">utf-8</span><span class="p">;</span>
    <span class="kn">client_max_body_size</span> <span class="mi">128M</span><span class="p">;</span>

    <span class="kn">add_header</span> <span class="s">X-Robots-Tag</span> <span class="s">'noindex,</span> <span class="s">nofollow,</span> <span class="s">nosnippet,</span> <span class="s">noarchive'</span><span class="p">;</span>

    <span class="kn">location</span> <span class="n">/41c270e4-5535-4daa-b23e-c269744c2f45/</span> <span class="p">{</span>
        <span class="kn">internal</span><span class="p">;</span>
        <span class="kn">alias</span> <span class="n">/</span><span class="p">;</span>
        <span class="kn">try_files</span> <span class="nv">$uri</span> <span class="nv">$uri</span><span class="n">/</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
        <span class="kn">rewrite</span> <span class="s">^</span> <span class="s">"/Users/cable8mm/.composer/vendor/laravel/valet/server.php"</span> <span class="s">last</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kn">location</span> <span class="p">=</span> <span class="n">/favicon.ico</span> <span class="p">{</span> <span class="kn">access_log</span> <span class="no">off</span><span class="p">;</span> <span class="kn">log_not_found</span> <span class="no">off</span><span class="p">;</span> <span class="p">}</span>
    <span class="kn">location</span> <span class="p">=</span> <span class="n">/robots.txt</span>  <span class="p">{</span> <span class="kn">access_log</span> <span class="no">off</span><span class="p">;</span> <span class="kn">log_not_found</span> <span class="no">off</span><span class="p">;</span> <span class="p">}</span>

    <span class="kn">access_log</span> <span class="no">off</span><span class="p">;</span>
    <span class="kn">error_log</span> <span class="s">"/Users/cable8mm/.config/valet/Log/nginx-error.log"</span><span class="p">;</span>

    <span class="kn">error_page</span> <span class="mi">404</span> <span class="s">"/Users/cable8mm/.composer/vendor/laravel/valet/server.php"</span><span class="p">;</span>

    <span class="kn">location</span> <span class="p">~</span> <span class="sr">[^/]\.php(/|$)</span> <span class="p">{</span>
        <span class="kn">fastcgi_split_path_info</span> <span class="s">^(.+</span><span class="err">\</span><span class="s">.php)(/.+)</span>$<span class="p">;</span>
        <span class="kn">fastcgi_pass</span> <span class="s">"unix:/Users/cable8mm/.config/valet/valet.sock"</span><span class="p">;</span>
        <span class="kn">fastcgi_index</span> <span class="s">"/Users/cable8mm/.composer/vendor/laravel/valet/server.php"</span><span class="p">;</span>
        <span class="kn">include</span> <span class="s">fastcgi_params</span><span class="p">;</span>
        <span class="kn">fastcgi_param</span> <span class="s">SCRIPT_FILENAME</span> <span class="s">"/Users/cable8mm/.composer/vendor/laravel/valet/server.php"</span><span class="p">;</span>
        <span class="kn">fastcgi_param</span> <span class="s">PATH_INFO</span> <span class="nv">$fastcgi_path_info</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kn">location</span> <span class="p">~</span> <span class="sr">/\.ht</span> <span class="p">{</span>
        <span class="kn">deny</span> <span class="s">all</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Samgu Lee</name></author><category term="development" /><category term="spring" /><category term="laravel" /><category term="valet" /><summary type="html"><![CDATA[스프링 부트 환경에서 http://localhost:8080 이 아닌 https://spring-boot.test 와 같은 도메인으로 개발할 수 있는 방법을 소개합니다.]]></summary></entry><entry><title type="html">개발자가 알아야 하는 기본(Essentials) 지식들</title><link href="https://www.palgle.com/2024/02/22/knowledges-essentials-for-developers/" rel="alternate" type="text/html" title="개발자가 알아야 하는 기본(Essentials) 지식들" /><published>2024-02-22T10:07:00+00:00</published><updated>2024-02-22T10:07:00+00:00</updated><id>https://www.palgle.com/2024/02/22/knowledges-essentials-for-developers</id><content type="html" xml:base="https://www.palgle.com/2024/02/22/knowledges-essentials-for-developers/"><![CDATA[<p>많은 회사들이 특유의 개발 문화와 원칙을 만들어 갑니다. 사내 혹은 그룹도 마찬가지이며, 개발자로서 꼭 가지고 있어야 하는 기술 소양을 설명합니다.</p>

<p><img src="/assets/images/untitlsssssed.png" alt="Various Stacks" /></p>

<blockquote>
  <p>{tip} 기술 소양이란 단어는 수학의 Axios(<a href="https://namu.wiki/w/%EA%B3%B5%EB%A6%AC">공리-무조건 옳다고 가정하고, 증명하지 않는다.</a>)의 느낌으로, 개발을 진행할 때 개발자가 당연히 알고 있다고 가정하는 기술들을 말합니다. 편의상 직무별로 분류하지는 않습니다.</p>
</blockquote>

<h2 id="언어">언어</h2>

<h3 id="패키지-매니저">패키지 매니저</h3>

<p><a href="https://getcomposer.org/">Composer</a>, <a href="https://www.npmjs.com/">NPM</a> 혹은 <a href="https://cocoapods.org/">Cocoapods</a>와 같이 가장 많이 사용하는 패키지매니져를 이용하는 방법과 만드는 방법을 알고 경험을 해 봐야 합니다. <a href="https://developer.apple.com/documentation/xcode/swift-packages">Apple</a>과 <a href="https://learn.microsoft.com/en-us/windows/package-manager/">Microsoft</a>는 공식 패키지매니저를 제공하고 있고, 저장소로 깃헙을 사용합니다. 패키지를 만든 후 패키지매니저 레퍼지토리에 올리는 방법까지 알아야 합니다.</p>

<h3 id="오퍼레이터">오퍼레이터</h3>

<p>integer 이외의 타입에서 오퍼레이터가 어떻게 작동하는지 알아야 합니다. 특히 오퍼레이터 오버로드를 지원하는 언어들에서는 보통 어떻게 개발되는지 깃헙을 통해서 익혀야 할 필요가 있습니다.</p>

<h3 id="디버거">디버거</h3>

<p>디버깅을 하는 방법은 크게 세가지가 있습니다.</p>

<ol>
  <li>프린트</li>
  <li>디버거</li>
  <li>로그파일</li>
  <li>테스트 프레임워크</li>
</ol>

<p>1번 방법이 가장 편하고 가볍게 이용할 수 있습니다. 2번의 경우 headless 개발, 말하자면 화면이 없거나 로직이 복잡할 경우 break point를 이용하기 위해서 사용합니다. 다만, 이벤트를 이용할 경우에는 제대로 된 값이 나오지 않는 경우가 있습니다.</p>

<p>3번은 stream이나 이벤트 코딩을 할 경우 많이 사용하는데요, 로그파일에 <code class="language-plaintext highlighter-rouge">tail -f</code> 명령어를 이용해서 화면에 띄워놓고 코딩을 합니다. 로그파일을 중심으로 하기 때문에 <a href="https://www.scaler.com/topics/pipe-command-in-linux/">리눅스 pipe 커맨드</a>를 과감하게 사용할 수 있어야 원하는 디버깅을 자유롭게 할 수 있습니다.</p>

<p>4번은 <a href="https://github.blog/2022-02-02-build-ci-cd-pipeline-github-actions-four-steps/">CICD</a>가 널리 보급된 후 필수가 되고 있는데요, 현대 언어들은 예외없이 주력 테스트 프레임워크를 지원합니다. 테스트 프레임워크로 만들어진 테스트 코드는 CICD단계에서 매번 실행되기 때문에 매우 편하고 제가 추천하는 방법입니다.</p>

<p>물론 네가지 모두 모국어를 사용하듯이 편하게 사용할 수 있어야 합니다.</p>

<h3 id="테스트">테스트</h3>

<p>프레임워크는 라이프사이클을 이해하는 것이 무엇보다 중요합니다. 이 경우 프레임워크에서 제공하는 테스트 코드가 어떤 원리로 작동되는지 코드레벨에서 이해해야 합니다.</p>

<h3 id="구현">구현</h3>

<p>내가 제안하는 구조를 다른 개발자에게 설명하는 방법은 크게 두가지가 있습니다. pseudocode와 다이어그램입니다.</p>

<p>두가지 방법 모두를 이용할 수 있어야 하며, 특히 시퀀스 다이어그램은 툴을 이용해야 하므로 익숙해 지면 질 수록 나의 디자인을 설명하기가 수월해 집니다. 전 markdown 문서와 잘 맞는 <a href="https://mermaid.js.org/">mermaid 다이아그램</a>을 이용하고 있습니다.</p>

<h2 id="테크닉">테크닉</h2>

<h3 id="정규표현식">정규표현식</h3>

<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Regular_expressions">정규표현식</a>은 언어마다 차이가 거의 없습니다. 즉, 한번 익혀 놓으면 어떤 언어를 사용하던지 문자열 처리를 어렵지 않게 처리할 수 있습니다.</p>

<p>특히 웹을 개발하면 문자열 처리가 중요해 지기 때문에 반드시 익혀야 합니다. 사람은 죽어서 이름을 남기고, <a href="https://perldoc.perl.org/perlre">펄 언어는 죽어서 정규표현식을 남겼습니다</a>.</p>

<h3 id="빌드">빌드</h3>

<p>자신의 코드가 배포될 때 까지의 과정이 어떻게 진행되는지 알아야 합니다. 현대 개발은 컴파일 언어 뿐만이 아니라 자바스크립트와 같은 스크립트 언어들도 빌드 후 배포됩니다.</p>

<p>컴파일 언어에 비해 스크립트의 빌드는 훨씬 복잡하며, 문제가 생길 가능성이 높습니다. 따라서, 빌드를 하는 전과정을 이해하지 않으면 빌드할 때 발생하는 문제를 해결하기가 어렵습니다.</p>

<p><a href="https://webpack.js.org/">Webpack</a>이나 <a href="https://vitejs.dev/">Vite</a> 와 같은 번들링과 빌드를 지원하는 툴의 소스코드를 이해하는 것이 가장 빠른 방법입니다.</p>

<h3 id="깃과-깃헙">깃과 깃헙</h3>

<p>깃은 현대 개발에 있어서 이미 가장 중요한 중심이 되고 있습니다. CICD 때문인데요, 따라서 레포지토리를 만들고, 브랜치를 관리하고 PR과 Conflict를 처리할 수 있어야 합니다. 의외로 까다로운데요, 코딩 스타일과 깃헙 액션 등을 과감히 이용하지 않으면 여러명의 개발자들이 동일 레포지토리에서 개발하기가 불가능합니다.</p>

<p>보통 유명한 레포지토리들은 10개 이상의 깃헙액션이 사용되며, <a href="https://resources.github.com/ci-cd/">문서와 시스템 전체가 CICD로 자동 배포</a>되고 있습니다. 심지어 여러가지 버전이 동시에 개발될 때에도 깃과 깃헙만으로 운영되고 있을 정도입니다.</p>

<h3 id="마크다운">마크다운</h3>

<p><a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax">마크다운</a>은 문서를 깃으로 만들고 운영하기에 최고의 문서 포멧이며, 별도의 프로그램을 사용하지 않아도 문서를 이해할 수 있을 정도로 매우 단순한 구조입니다. 지금 이 글도 마크다운으로 쓰고 있습니다.</p>

<p>Word나 엑셀을 이용해서 문서를 만들고 있다면 지금부터 마크다운으로 만들어 보세요. 마크다운으로 문서를 만들면 <a href="https://github.com/simonhaenisch/md-to-pdf"><code class="language-plaintext highlighter-rouge">md-to-pdf</code></a> 패키지 등을 이용해서 커맨드로 pdf로 변환할 수 있습니다. 그리고, 그 문서를 깃헙으로 관리하면 매번 필요한 문서를 제작할 필요가 없어집니다.</p>

<h3 id="개발-용어">개발 용어</h3>

<p>컨벤션, 패턴 용어는 별다른 설명 없이 대화할 수 있어야 합니다. 단, 공식 문서에 용어 그대로 사용해야 하며, 편의상 용어를 바꾸지 않습니다.</p>

<blockquote>
  <p>{tip} 테크닉은 꼭 외우고 있어야 할 필요는 없지만, 공식 문서를 보고 할 수 있어야 합니다.</p>
</blockquote>

<h2 id="레퍼런스--가이드">레퍼런스 &amp; 가이드</h2>

<ol>
  <li>공식 레퍼런스를 영문으로 읽을 수 있거나 읽어야 한다는 강한 욕망이 있어야 합니다.</li>
  <li>어떤 언어, 프레임워크, 시스템 등의 자료를 찾을 때 공식 레퍼런스나 가이드를 우선 확인할 수 있어야 합니다.</li>
  <li>W3C drift, PHP.net document, Laravel Document, Apple Developer Document, Android Developer Document 등 자신의 주력 언어나 프레임워크의 공식 문서는 반복적으로 읽어야 합니다.</li>
</ol>

<p>개발자가 익혀야 하는 기본을 적어봤습니다. 그리고, <a href="https://velog.io/@riwonkim/%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%8A%94-%EC%96%B4%EB%94%94%EA%B9%8C%EC%A7%80-%EA%B3%B5%EB%B6%80%EB%A5%BC-%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80-%EC%98%81%EC%96%B4%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%98%EA%B2%AC">의외로 영어 실력이 중요</a>하며, 다른 훌륭한 개발자의 코드를 주기적으로 리뷰하는 것도 도움이 됩니다. <a href="https://github.com/">깃헙</a>과 <a href="https://stackoverflow.com/">스택오버플로우</a>는 개발에 있어서 너무나도 중요해서 언급할 필요도 없을 정도입니다.</p>]]></content><author><name>Samgu Lee</name></author><category term="opinion" /><category term="development" /><summary type="html"><![CDATA[많은 회사들이 특유의 개발 문화와 원칙을 만들어 갑니다. 사내 혹은 그룹도 마찬가지이며, 개발자로서 꼭 가지고 있어야 하는 기술 소양을 설명합니다.]]></summary></entry><entry><title type="html">스타트업 초기에 유용한 이메일만으로 협업하기</title><link href="https://www.palgle.com/2024/02/18/how-to-email-in-your-company/" rel="alternate" type="text/html" title="스타트업 초기에 유용한 이메일만으로 협업하기" /><published>2024-02-18T10:40:00+00:00</published><updated>2024-02-18T10:40:00+00:00</updated><id>https://www.palgle.com/2024/02/18/how-to-email-in-your-company</id><content type="html" xml:base="https://www.palgle.com/2024/02/18/how-to-email-in-your-company/"><![CDATA[<p>스타트업을 만들고 운영하면 처음 만나는 문제가 바로 정보 관리입니다. 프로젝트와 관련된 문서를 어떻게 관리하고 업데이트하고 소통할 것인가의 문제는 20년 전 부터 해결되지 않는 문제 중 하나입니다.</p>

<p><img src="/assets/images/various_emails.png" alt="Various Emails" /></p>

<p>정보 관리 혹은 프로젝트 매니저가 단독으로 운용되기 전 단계 까지라면 이메일 만으로 모든 정보를 유통하는 것이 효율이 좋을 수 있습니다. 본 글에서는 이메일 정보 유통과 소통의 하나의 시나리오를 제안합니다.</p>

<p>단 하나의 룰은 “메신저를 사용하지 않는다” 입니다.</p>

<h2 id="같은-팀-소속끼리-요청하기">같은 팀 소속끼리 요청하기</h2>

<p>같은 팀에 소속된 매니저님들은 예외없이 이메일로 요청을 하고, 이메일로 회신을 합니다. 물론 내용 파악이 필요할 때에는 전화나 카톡, 미팅을 할 수도 있지만, 그렇다고 하더라도 그 결과를 다시 이메일에 반영을 하죠.</p>

<p>조금 더 자세히 Case Study를 살펴보도록 합니다.</p>

<h3 id="case-1-이것-좀-해-주세요-메일-x-나-혼자-처리-가능">Case 1. “이것 좀 해 주세요” 메일 x 나 혼자 처리 가능</h3>

<table>
  <thead>
    <tr>
      <th>조건</th>
      <th>처리 방법</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1시간 이내 처리가 가능</td>
      <td>처리 후 완료 회신.</td>
    </tr>
    <tr>
      <td>1시간 이내 처리가 불가능</td>
      <td>(1) 완료 예정일을 이메일로 알림 (2) 처리 후 완료 회신.</td>
    </tr>
  </tbody>
</table>

<h3 id="case-2-이것-좀-해-주세요-메일-x-나-혼자-처리-불가능">Case 2. “이것 좀 해 주세요” 메일 x 나 혼자 처리 불가능</h3>

<p>실제 처리자에게 요청 자체를 넘겨서 처리자가 직접 요청자에게 처리 결과를 Case 1 형식으로 회신하도록 합니다.</p>

<p>만약 요청자의 컨텍포인트가 별도로 정해져 있어서 처리자가 직접 요청자에게 결과를 알릴 수 없는 상황이라면 아래와 같이 Case 1을 중계합니다.</p>

<table>
  <thead>
    <tr>
      <th>조건</th>
      <th>처리 방법</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>실제 처리자가 1시간 이내 처리가 가능</td>
      <td>처리 후 완료 회신.</td>
    </tr>
    <tr>
      <td>실제 처리자가 1시간 이내 처리가 불가능</td>
      <td>(1) 완료 예정일을 이메일로 알림 (2) 처리 후 완료 회신.</td>
    </tr>
  </tbody>
</table>

<h2 id="다른-팀-소속에서-요청받기">다른 팀 소속에서 요청받기</h2>

<p>다른 팀이라도 이메일로 요청을 받는 것이 기본입니다. 하지만, 부득이하게 그렇지 않을 경우는 아래와 같이 Case 3으로 처리합니다.</p>

<h3 id="case-3-이메일이-아닌-방법으로-요청이-왔을-때">Case 3. 이메일이 아닌 방법으로 요청이 왔을 때</h3>

<table>
  <thead>
    <tr>
      <th>조건</th>
      <th>처리 방법</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>카톡, 전화(구두)로 요청 옴</td>
      <td>(1) 일단 내용을 정리해서 요청자에게 이메일을 발송. (2) Case 1, Case 2 방식으로 처리.</td>
    </tr>
    <tr>
      <td>구두로 급하게 요청하는데 30분 안에 처리 가능</td>
      <td>(1) 요청자와 같은 공간에 있으므로, 처리를 그 자리에서 완료합니다. (2) 처리 결과를 메일로 작성해서 요청자에게 발송.</td>
    </tr>
    <tr>
      <td>구두로 급하게 요청하는데 30분 안에 처리 불가능</td>
      <td>(1) 요청 내용을 이메일로 보내달라고 요청 (2) Case 1, Case 2 형식으로 처리합니다.</td>
    </tr>
  </tbody>
</table>

<p>새로운 협업 방식을 도입할 때에는 베스트 상황만을 고려하지 않고, 협업 중간에 안 좋은 상황이 발생할 때 어떻게 해야 되는지가 정의되어야 합니다.</p>]]></content><author><name>Samgu Lee</name></author><category term="opinion" /><category term="email" /><category term="team" /><summary type="html"><![CDATA[스타트업을 만들고 운영하면 처음 만나는 문제가 바로 정보 관리입니다. 프로젝트와 관련된 문서를 어떻게 관리하고 업데이트하고 소통할 것인가의 문제는 20년 전 부터 해결되지 않는 문제 중 하나입니다.]]></summary></entry><entry><title type="html">협업의 필수 도구가 되어 가는 Git, 베스트 깃(Git) 커맨드 몇가지</title><link href="https://www.palgle.com/2024/02/08/best-git-commands/" rel="alternate" type="text/html" title="협업의 필수 도구가 되어 가는 Git, 베스트 깃(Git) 커맨드 몇가지" /><published>2024-02-08T12:32:00+00:00</published><updated>2024-02-08T12:32:00+00:00</updated><id>https://www.palgle.com/2024/02/08/best-git-commands</id><content type="html" xml:base="https://www.palgle.com/2024/02/08/best-git-commands/"><![CDATA[<p>Github이 Git의 생태계를 넒혀가고 있습니다. CI/CD가 보편화 되면서 소스 저장소의 역할이 버전 관리에만 국한되지 않고, 코드의 표준화와 각종 코드의 빌드 및 배포의 핵심이 되었습니다.</p>

<p><img src="/assets/images/github_homepage.png" alt="Github homepage" /></p>

<p>비단 개발자 뿐만이 아니라 각종 프로젝트 문서나 심지어 소설, 그림도 깃으로 관리하는 프로젝트가 생기고 있습니다. 마이크로소프트 오피스와 마찬가지로 협업을 하는 사람이라면 점점 깃에 익숙해 져야 할 필요가 있습니다.</p>

<p>따라서, 몇 명이 개발을 하던 소기업이던 대기업이던 Git을 이용하고 Github을 이용해서 협업을 하는 경우가 빠르게 늘고 있습니다. Github의 주인인 마이크로 소프트는 자사의 오픈소스 에디터인 VSCode에서 공식 확장을 이용해서 Github과의 협업을 하나의 툴로 만들어서 제공하고 있습니다.</p>

<p>워낙 자주 사용하는 툴이기 때문에 Git과 Github 을 통해 어떻게 개발을 하는게 베스트인지를 생각 해 봅니다.</p>

<h2 id="github에-코드를-올리는-두가지-방법-commit-and-pr">Github에 코드를 올리는 두가지 방법, Commit and PR</h2>

<p>오픈소스 프로젝트의 경우 운용상의 이유로 Commit을 이용한 저장소 코드 올리기는 허용하지 않는 경우가 많습니다. 대신, PR을 만들면 각종 테스트와 리뷰, 테스트가 이루어 지고 리뷰어가 승인을 할 경우 main 브랜치에 머지 됩니다.</p>

<p>개발자 입장에서는 PR을 이용한 개발에 두가지 상황이 있을 수 있습니다.</p>

<ol>
  <li>내가 브랜치를 만들어서 PR을 생성하는 경우</li>
  <li>PR을 Pull 해서 개발을 한 후 PR에 다시 Commit 하는 경우</li>
</ol>

<p>한가지 중요한 점은 현대 개발을 단일 Commit이 작을 수록 관리가 편해지는 경향이 생겼습니다. 그 이유는 CI/CD로 인해 빌드에 대한 모든 것이 자동화 되었기 때문입니다. 과거에는 빌드 매니저가 빌드를 하기까지 몇 주가 걸리는 경우도 있던 시절과는 전혀 다른 양상입니다.</p>

<p>따라서, 이 두가지 상황 모두 하루에도 몇번 씩 발생하고 있습니다. 이 경우 아래 커맨드가 도움을 줄 수 있습니다.</p>

<h2 id="커맨드를-활용하자">커맨드를 활용하자</h2>

<h3 id="내가-브랜치를-만들어서-pr을-생성하는-경우">내가 브랜치를 만들어서 PR을 생성하는 경우</h3>

<p>브랜치를 생성하고 이동할 때 아래 세개의 커맨드를 사용합니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span><span class="nv">gc</span><span class="o">=</span><span class="s2">"git checkout"</span>
<span class="nb">alias </span><span class="nv">gcb</span><span class="o">=</span><span class="s2">"git checkout -b"</span>
<span class="nb">alias </span><span class="nv">gb</span><span class="o">=</span><span class="s2">"git branch"</span>
<span class="nb">alias </span><span class="nv">gbclean</span><span class="o">=</span><span class="s2">"git branch | grep -v "</span>main<span class="s2">" | xargs git branch -D"</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">hello</code>라는 브랜치로 예를 들어 보겠습니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># hello 브랜치 생성 후 이동</span>
gcb hello

<span class="c"># hello 브랜치 만들기</span>
gb hello

<span class="c"># main 브랜치로 이동</span>
gc main

<span class="c"># PR이 머지된 후 로컬에서 main 브랜치 이외의 모든 브랜치를 삭제</span>
gbclean
</code></pre></div></div>

<p>전 머지는 Github에서 이루어 지기 때문에 로컬에서는 위 네가지 명령어만으로 개발을 합니다.</p>

<h3 id="pr을-pull-해서-개발을-한-후-pr에-다시-commit-하는-경우">PR을 Pull 해서 개발을 한 후 PR에 다시 Commit 하는 경우</h3>

<p>PR은 번호가 강조되고 브랜치 이름은 상대적으로 중요하지 않습니다. 따라서, 아래와 같은 명령어를 이용합니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#pull request clean</span>
<span class="k">function </span>prc<span class="o">()</span>
<span class="o">{</span>
    git <span class="k">for</span><span class="nt">-each-ref</span> refs/heads/pr/ <span class="nt">--format</span><span class="o">=</span><span class="s1">'%(refname)'</span> | <span class="k">while </span><span class="nb">read </span>ref <span class="p">;</span> <span class="k">do </span><span class="nv">branch</span><span class="o">=</span><span class="k">${</span><span class="nv">ref</span><span class="p">#refs/heads/</span><span class="k">}</span> <span class="p">;</span> git branch <span class="nt">-D</span> <span class="nv">$branch</span> <span class="p">;</span> <span class="k">done</span>
<span class="o">}</span>

<span class="c">#pull request pull</span>
<span class="k">function </span>prp<span class="o">()</span>
<span class="o">{</span>
    <span class="k">if</span> <span class="o">[</span> <span class="nv">$# </span><span class="nt">-eq</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">then
        </span><span class="nb">echo</span> <span class="s1">'Pull request number need.'</span>
    <span class="k">else
        </span>git fetch <span class="nt">-fu</span> <span class="k">${</span><span class="nv">2</span><span class="k">:-</span><span class="nv">origin</span><span class="k">}</span> refs/pull/<span class="nv">$1</span>/head:pr/<span class="nv">$1</span> <span class="o">&amp;&amp;</span> git checkout <span class="nb">pr</span>/<span class="nv">$1</span>
    <span class="k">fi</span>
<span class="o">}</span>

<span class="c">#delete branch</span>
<span class="k">function </span>brc<span class="o">()</span>
<span class="o">{</span>
    <span class="k">if</span> <span class="o">[</span> <span class="nv">$# </span><span class="nt">-eq</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">then
        </span><span class="nb">echo</span> <span class="s1">'Branch name need.'</span>
    <span class="k">else
        </span>git <span class="k">for</span><span class="nt">-each-ref</span> <span class="s2">"refs/heads/**/*</span><span class="nv">$1</span><span class="s2">*"</span> <span class="nt">--format</span><span class="o">=</span><span class="s1">'%(refname)'</span> | <span class="k">while </span><span class="nb">read </span>ref <span class="p">;</span> <span class="k">do </span><span class="nv">branch</span><span class="o">=</span><span class="k">${</span><span class="nv">ref</span><span class="p">#refs/heads/</span><span class="k">}</span> <span class="p">;</span> git branch <span class="nt">-D</span> <span class="nv">$branch</span> <span class="p">;</span> <span class="k">done
    fi</span>
<span class="o">}</span>
</code></pre></div></div>

<p>이 명령어도 예를 들어 봅니다.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 3번 PR을 pull</span>
prp 3

<span class="c"># 3번 PR의 브랜치를 삭제</span>
prc 3

<span class="c"># PR 브랜치에서 다시 브랜치를 만들었을 경우 브랜치 이름 매칭이 된 브랜치 전체를 삭제</span>
brc &lt;브랜치 이름 중 일부&gt;
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">git push</code> 이나 <code class="language-plaintext highlighter-rouge">git merge main</code>과 같은 경우는 별도의 명령어를 만들지 않고 git 명령어 그대로 사용합니다.</p>

<p>다음 번에는 Github에서 제공하는 여러가지 머지 방법에 대해서 이야기 해 보도록 하겠습니다.</p>]]></content><author><name>Samgu Lee</name></author><category term="opinion" /><category term="email" /><category term="team" /><summary type="html"><![CDATA[Github이 Git의 생태계를 넒혀가고 있습니다. CI/CD가 보편화 되면서 소스 저장소의 역할이 버전 관리에만 국한되지 않고, 코드의 표준화와 각종 코드의 빌드 및 배포의 핵심이 되었습니다.]]></summary></entry></feed>