<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ord.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 26 Jan 2025 08:49:01 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ord.log</title>
            <url>https://velog.velcdn.com/images/eujin-shin/profile/9be6d6ac-909f-434a-90b2-4b1549696c4e/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ord.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/eujin-shin" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[자바스크립트 실행 컨텍스트에 대해]]></title>
            <link>https://velog.io/@eujin-shin/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EC%97%90-%EB%8C%80%ED%95%B4</link>
            <guid>https://velog.io/@eujin-shin/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EC%97%90-%EB%8C%80%ED%95%B4</guid>
            <pubDate>Sun, 26 Jan 2025 08:49:01 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@eujin-shin/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84%EA%B3%BC-%EB%9F%B0%ED%83%80%EC%9E%84-%ED%99%98%EA%B2%BD">지난 글</a>에서 자바스크립트가 어떻게 동작하는지 알아보기 위해, 자바스크립트를 실행시키는 엔진과 실행 환경인 런타임 환경에 대한 내용을 정리해 봤다. </p>
<blockquote>
<p>💡 <strong>실행 컨텍스트?</strong>
코드가 실행될 때 제공할 환경 정보를 갖고 있는 객체이다. 예를 들어 특정 코드 블록에서 사용 가능한 식별자 정보 등이 포함된다.</p>
</blockquote>
<p>그 글에서 위와 같이 실행 컨텍스트를 언급했었는데, 이번 스터디에서 마침 실행 컨텍스트 주제로 발표를 하게 돼서 관련 내용을 좀 더 자세히 공부해 봤다. 🧐</p>
<hr>
<h2 id="실행-컨텍스트란">실행 컨텍스트란?</h2>
<p>실행 컨텍스트는 코드가 실행되기 위해 필요한 <strong>환경 정보</strong>를 담고 있는 개념으로, 자바스크립트 엔진이 이를 객체 형태로 관리한다. 실행 컨텍스트는 다음과 같은 환경 정보를 포함한다.</p>
<ul>
<li>변수(전역변수, 지역변수, 매개변수, 객체 프로퍼티)</li>
<li>함수 선언</li>
<li>변수의 유효 범위(Scope)</li>
<li><code>this</code> 바인딩 정보</li>
</ul>
<h3 id="실행-컨텍스트의-생성">실행 컨텍스트의 생성</h3>
<p>자바스크립트 코드에서 실행 컨텍스트가 생성되는 유형은 다음 세 가지로 구분된다.</p>
<ul>
<li><strong>전역 코드</strong>: 처음 실행될 때 자동으로 전역 컨텍스트가 생성됨</li>
<li><strong>함수 코드</strong>: 함수 실행 시 해당 함수에 대한 실행 컨텍스트가 생성됨</li>
<li><strong><code>eval()</code> 함수</strong>: 단, 안전성과 성능 문제로 사용이 권장되지 않음</li>
</ul>
<p>따라서 <code>eval()</code> 함수는 제외하고 봤을 때, 자바스크립트 코드의 실행 흐름을 실행 컨텍스트 중심으로 생각해 보면 다음과 같이 정리할 수 있다.</p>
<ol>
<li><strong>전역 컨텍스트 생성</strong>: 코드 실행 시작 시 전역 실행 컨텍스트가 생성됨. 
(브라우저 환경에서 <code>window</code> 객체에 접근하는 것도 이러한 전역 컨텍스트를 통해 가능하다.)</li>
<li><strong>함수 실행 시 컨텍스트 생성</strong>: 특정 함수가 실행될 때 해당 함수에 대한 실행 컨텍스트가 생성됨.</li>
<li><strong>콜 스택 관리</strong>:<ul>
<li>생성된 실행 컨텍스트는 콜 스택(Call Stack)에 쌓인다.</li>
<li>코드 실행이 끝나면 실행 컨텍스트가 콜 스택에서 제거된다.</li>
</ul>
</li>
</ol>
<p>현재 실행 중인 코드에 대한 환경 정보를 실행 컨텍스트에 담고, 코드를 실행하는 동안 콜 스택에 넣어 두고, 코드 실행이 끝나면 사용 중이던 실행 컨텍스트를 콜 스택에서 제거한다고 이해할 수 있을 것 같다. 이를 통해 실행 컨텍스트는 <strong>코드의 실행 환경과 실행 순서를 보장</strong>해 준다.</p>
<h2 id="실행-컨텍스트의-구성-요소">실행 컨텍스트의 구성 요소</h2>
<p>이제 코드가 필요로 하는 환경 정보가 실행 컨텍스트 내부에 어떻게 담겨 있는지 좀 더 자세히 알아보자. 실행 컨텍스트는 다음 세 가지 요소로 구분된다.</p>
<ol>
<li>VariableEnvironment</li>
<li>LexicalEnvironment</li>
</ol>
<p><em>3. ThisBinding</em></p>
<blockquote>
<p><strong>‼️ ThisBinding에 대해</strong>
ThisBinding은 현재 코드에서 <code>this</code> 키워드가 어떤 객체를 가리키는지에 대한 정보를 담는다. ES6에서는 VariableEnvironment, LexicalEnvironment 안에 포함되었다는 글을 보기도 했는데 해당 내용은 공식적인 자료를 통해 확인하지는 못했다. 
이번 글에서는 렉시컬 환경을 통한 변수 참조에 좀 더 초점을 맞췄기 때문에, 아래로는 VariableEnvironment와 LexicalEnvironment에 대해서만 살펴본다. </p>
</blockquote>
<h3 id="렉시컬-환경">렉시컬 환경</h3>
<p>렉시컬 환경은 특정 변수, 함수와 식별자를 연결해 준다. 렉시컬 환경은 다시 environment record와 outer environment reference로 구성된다.</p>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/10b659d9-46f2-4a96-a6d7-6d0e9e94ed1d/image.png" alt=""></p>
<p>environment record에는 식별자 바인딩이 기록되어 있고, outer environment reference는 중첩된 구조의 렉시컬 환경을 만들어 준다. 그림의 맨 위 전역 렉시컬 환경부터 하위로 내려가면서 확인하면, outer environment reference가 링크드 리스트처럼 상위 렉시컬 환경을 가리키고 있는 걸 알 수 있다. </p>
<p>이를 통해서 스코프 체인을 사용할 수 있게 된다. 특정 식별자를 참조하고자 할 때 우선 현재 실행 컨텍스트의 렉시컬 환경 안에 있는 environment record를 탐색하고, 만약 존재하지 않는다면 outer environment reference를 통해 상위의 렉시컬 환경에 있는 environment record를 탐색하는 것이다. </p>
<p>그림에서 최상위에 있는 전역 컨텍스트의 렉시컬 환경은 outer environment reference가 <code>null</code>임을 확인할 수 있는데, 실제로도 전역 컨텍스트까지 탐색한 뒤에도 식별자가 발견되지 않으면 참조 에러가 발생할 것이다.</p>
<h3 id="variableenvironment와-lexicalenvironment의-차이">VariableEnvironment와 LexicalEnvironment의 차이</h3>
<p>&quot;렉시컬 환경&quot;이라는 용어가 중복해서 등장하다 보니 헷갈렸는데 🙄 나는 위에서 언급한 &quot;렉시컬 환경&quot;은 개념으로, 지금 언급하는 <code>VariableEnvironment</code>와 <code>LexicalEnvironment</code>는 이 개념에 해당하는 실행 컨텍스트의 실제 구성 요소로 이해했다. (추상 클래스와 자식 클래스의 관계 같은 느낌...?)</p>
<p>그럼 왜 이 둘은 따로 존재할까? 같은 렉시컬 환경이면 하나만 있으면 되지 않을까? <del>뭐 하러 사람 헷갈리게 비슷한 걸 여러 개 만들었을까</del></p>
<p><code>VariableEnvironment</code>의 environment record에는 <code>var</code>로 생성한 변수의 초기값을 저장하고, 이를 복사해 <code>LexicalEnvironment</code>의 environment record를 초기화한다. 그리고 <code>let</code>, <code>const</code>로 생성한 변수를 <code>LexicalEnvironment</code>의 environment record에 저장한 뒤, 코드 실행 과정에서 생기는 변경 사항을 반영하여 최신 상태로 업데이트 한다.</p>
<p>이 내용을 통해 예상할 수 있듯, 두 구성 요소는 <code>var</code>과 <code>let</code>, <code>const</code>를 통한 변수 선언의 차이에 의해 분리된 것으로 보인다.</p>
<h4 id="var과-let-const의-차이-1-변수-생성-방식"><code>var</code>과 <code>let</code>, <code>const</code>의 차이 (1): 변수 생성 방식</h4>
<p>변수 생성 방식은 <strong>(1) 선언</strong>(식별자를 등록한다), <strong>(2) 초기화</strong>(식별자를 위한 메모리를 할당한다), <strong>(3) 할당</strong>(실제로 값을 할당한다)의 세 단계로 나눠볼 수 있다. </p>
<p>이때 <code>var</code>로 생성한 변수는 선언과 초기화가 함께 이루어진다. 이로 인해서 변수를 실제 선언하는 코드 이전에 변수를 참조해도 <code>undefined</code>의 초기값이 반환되는 호이스팅이 발생하는 것이기도 한다.</p>
<p>반면 <code>let</code>, <code>const</code>로 생성한 변수는 선언만 먼저 이루어지고, &#39;초기화되지 않은 상태&#39;의 식별자로 유지된다. 따라서 실제 선언문 이전에 접근한다면 참조 에러가 발생하는데, 이 구간을 TDZ(Temporal Dead Zone)이라고 부른다.</p>
<h4 id="var과-let-const의-차이-2-블록-스코프-지원"><code>var</code>과 <code>let</code>, <code>const</code>의 차이 (2): 블록 스코프 지원</h4>
<p><code>var</code>로 생성한 변수는 블록 스코프를 지원하지 않는다. 조건문이나 반복문의 코드 블록 단위로 변수를 생성할 수 없는 것이다. 따라서 <code>var</code>로 선언한 변수의 스코프는 전역, 혹은 함수 단위로 생성되는 특정 실행 컨텍스트와 대응시킬 수 있다.</p>
<p>반면 <code>let</code>, <code>const</code>로 생성한 변수는 블록 스코프를 지원한다. 아래 코드를 확인해 보자.</p>
<pre><code class="language-js">function foo(x) {
  var a = &quot;a&quot;;
  if(x) {
    let b = &quot;true&quot;;
  } else {
    let b = &quot;false&quot;;
  }
  return a;
}</code></pre>
<ul>
<li>함수 <code>foo</code>는 실행 컨텍스트를 가진다. </li>
<li><code>var</code>로 생성된 <code>a</code>는 함수 <code>foo</code>안에서 유효하다.</li>
<li><code>let</code>으로 생성된 <code>b</code>는 조건문의 코드블록 안에서 유효하다.</li>
</ul>
<p>따라서 <code>b</code>의 경우, 함수 영역이 아닌 코드블록 단위에서 환경 정보로써 관리되어야 할 필요가 생긴다.</p>
<p>이렇게 변수 선언에 있어 서로 상이한 방식이 지원되고 있다 보니, 렉시컬 환경의 동작 방식에 있어서도 통일이 불가능했고 결과적으로 <code>VariableEnvironment</code>와 <code>LexicalEnvironment</code>가 구분된 것으로 보인다.</p>
<h3 id="렉시컬-환경의-생성과-관리">렉시컬 환경의 생성과 관리</h3>
<p>알아본 내용을 바탕으로, <strong>렉시컬 환경이 어떻게 구성되고 사용되는지를</strong> 정리하면 다음과 같다.</p>
<ol>
<li><p><strong>컨텍스트 생성 시점</strong></p>
<ul>
<li><code>VariableEnvironment</code>에서 <code>var</code> 변수를 선언 및 초기화 (<code>undefined</code> 할당)</li>
<li><code>outerEnvironmentReference</code>가 상위 컨텍스트의 <code>LexicalEnvironment</code>를 참조</li>
<li><code>VariableEnvironment</code>의 초기 정보를 <code>LexicalEnvironment</code>로 복사</li>
<li><code>LexicalEnvironment</code>에 <code>let</code>, <code>const</code> 변수 선언 (초기화되지 않음 → TDZ 적용)</li>
</ul>
</li>
<li><p><strong>코드 실행 중</strong></p>
<ul>
<li><code>LexicalEnvironment</code>에서 변수 및 함수 정보를 최신 상태로 관리</li>
<li>식별자를 참조할 때, 현재 실행 컨텍스트의 <code>LexicalEnvironment</code>를 먼저 확인하고, 없으면 <code>outerEnvironmentReference</code>를 따라 상위 컨텍스트를 탐색</li>
</ul>
</li>
</ol>
<hr>
<p>이렇게 전역 컨텍스트에 대한 글을 정리해 봤다. 사실 엔진의 구체적인 내부 동작이나, 스펙의 변경 의도 같은 건 찾아보는 게 좀 어려웠는데 😅 그래도 이번 내용을 공부하면서 변수 생성과 참조가 실제로 어떻게 동작하는지 이해하는 데 도움이 된 것 같다! ✏️</p>
<h2 id="참조">참조</h2>
<p><a href="https://es5.github.io/#x10.2">https://es5.github.io/#x10.2</a>
<a href="https://m.blog.naver.com/dlaxodud2388/222655214381">https://m.blog.naver.com/dlaxodud2388/222655214381</a>
<a href="https://iamsjy17.github.io/javascript/2019/06/10/js33_execution_context.html">https://iamsjy17.github.io/javascript/2019/06/10/js33_execution_context.html</a> (이미지 출처)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PS] 백준 2533: 사회망 서비스]]></title>
            <link>https://velog.io/@eujin-shin/PS-%EB%B0%B1%EC%A4%80-2533-%EC%82%AC%ED%9A%8C%EB%A7%9D-%EC%84%9C%EB%B9%84%EC%8A%A4</link>
            <guid>https://velog.io/@eujin-shin/PS-%EB%B0%B1%EC%A4%80-2533-%EC%82%AC%ED%9A%8C%EB%A7%9D-%EC%84%9C%EB%B9%84%EC%8A%A4</guid>
            <pubDate>Tue, 21 Jan 2025 11:00:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/eujin-shin/post/ed1b3df3-df33-4eb2-9af7-82f77f439777/image.png" alt="">
<img src="https://velog.velcdn.com/images/eujin-shin/post/380f7f9b-2980-4fa4-a5f3-d69ae6318518/image.png" alt=""></p>
<p><del>문제 참 길다</del></p>
<h3 id="풀이-과정-🔎">풀이 과정 🔎</h3>
<h4 id="해석해-보기-🤔">해석해 보기 🤔</h4>
<p>a. 하나의 노드를 (얼리 어답터가 아닌 사람으로) 선택했을 때, 해당 노드와 인접한 모든 노드는 무조건 얼리 어답터가 되어야 한다.
b. (a)의 조건을 만족하면서 최대한 많은 노드를 선택했을 때, 선택되지 않은 노드의 수가 정답이다.
c. 최대한 적은 노드를 얼리 어답터로 선택해서 모든 간선에 대해 간선이 하나 이상의 얼리 어답터 노드를 갖게 한다.</p>
<h4 id="풀이-구상하기">풀이 구상하기</h4>
<ol>
<li><p>처음에는 단순하게 접근해서, 트리에서 level 0 - level 2 - level 4...를 선택하는 경우나, level 1 - level 3 - level 5...를 선택하는 경우를 비교하면 되지 않을까 생각했다. (= 임의의 노드로부터 거리를 구했을 때 짝수인 노드들 / 홀수인 노드들로 구분. 어떤 노드를 루트로 선택하든 결과는 같다.) 
그러나 예제에서 <strong>서로 인접한</strong> 노드를 둘 다 얼리 어답터로 했을 때 해가 나오는 경우가 있어서 기각.</p>
</li>
<li><p>(c)로부터 출발해서, 간선이 <strong>&#39;연결하는 두 정점 중 하나 이상이 얼리 어답터&#39;</strong> 라는 조건을 만족하는지를 저장하고, 우선순위 큐를 사용해 <strong>조건을 만족하지 않는 간선과 가장 많이 연결되어 있는 노드</strong>를 하나씩 얼리 어답터로 빼는 방식도 생각했다.
이 방식은 가능할 것 같아서 구현도 하고 예제까지는 통과했는데 제출했더니 틀렸다 🥲 아마 다음 두 부분이 문제가 됐지 않을까 의심스럽다...</p>
</li>
</ol>
<ul>
<li>조건을 만족하는 간선이 변경됐을 때, 우선순위 큐에 들어가 있는 가중치를 바로 업데이트하지 못하고 일단 우선순위 큐에서 노드를 하나 꺼내서 가중치를 수정 &gt; 다음 노드와 비교해가며 가중치가 가장 높은 노드를 찾는 방식으로 구현했다.</li>
<li>가중치가 같은 노드가 있을 때 우선순위 판단을 할 기준이 없다.</li>
</ul>
<ol start="3">
<li>결과적으로 dp 태그를 보고 나서 정답 로직을 구상할 수 있었다 😂</li>
</ol>
<ul>
<li>dp로 접근하니 각 노드가 &#39;얼리 어답터일 때&#39;와 &#39;얼리 어답터가 아닐 때&#39; 필요한 최소 비용 (= 얼리 어답터의 수)를 각각 저장하고 활용하는 방식이 떠올랐다.</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><p><strong>노드가 얼리 어답터라면?</strong> 
인접한 각각의 노드는 무엇이든 상관 없으므로 최소값을 사용</p>
</li>
<li><p><strong>노드가 얼리 어답터가 아니라면?</strong> 
인접한 각각의 노드는 모두 얼리 어답터여야 하므로 해당 값을 사용</p>
<blockquote>
</blockquote>
</li>
<li><p>특정 노드에서 이렇게 비용을 구하려면 인접한 노드의 비용이 모두 구해진 상태여야 하므로, 트리의 리프 노드부터 출발해서 서브 트리 범위에서 비용을 구하고, 자식 노드의 비용이 모두 구해진 노드를 다음 순서로 처리했다.</p>
</li>
<li><p>이렇게 루트까지 반복하면 루트 노드가 얼리 어답터일 경우 / 아닐 경우의 비용이 구해지므로 해당 값 중 더 작은 값이 정답이다.</p>
</li>
</ul>
<p>트리에서의 dp를 아직 많이 풀어 보지 않아서 dp로 접근할 생각을 바로 하지 못한 게 조금 아쉬웠던 문제.</p>
<h3 id="정답-코드-✅">정답 코드 ✅</h3>
<pre><code class="language-cpp">// C++ 사용

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;
#define pnt pair&lt;int, int&gt; 
#define MAX 1000001
using namespace std;

// 간선 정보 저장
vector &lt;int&gt; v[MAX];
// 리프 노드부터 시작하는 처리 순서를 구하기 위해, 각 노드의 부모 노드와 자식 노드의 개수 저장
int parents[MAX] = {0, }, children[MAX] = {0, };
// 각 노드에서 얼리 어답터일 때, 아닐 때 비용 저장
int dp[MAX][2] = {0, };

int main() {
  ios_base::sync_with_stdio(false);
  int n;
  cin&gt;&gt;n;

  // 간선 정보를 입력 받는다
  int x, y;
  for(int i = 1; i &lt; n; i++) {
    cin&gt;&gt;x&gt;&gt;y;
    v[x].push_back(y);
    v[y].push_back(x);
  }

  // 부모-자식 정보를 저장하기 위해 1번 노드를 루트로 정하고 트리 탐색
  queue &lt;int&gt; q, leaf;
  int cur;
  q.push(1);
  while(!q.empty()) {
    cur = q.front();
    q.pop();
    children[cur] = v[cur].size() - (cur == 1 ? 0 : 1);
    // 자식이 없을 경우 리프 노드이므로 큐에 저장
    if(children[cur] == 0) {
      leaf.push(cur);
    }
    for(int i = 0; i &lt; v[cur].size(); i++) {
      if(v[cur][i] != parents[cur]) {
        q.push(v[cur][i]);
        parents[v[cur][i]] = cur;
      }
    }
  }

  // 모든 자식의 비용 계산이 끝난 노드를 차례로 순회하며 자신의 비용을 부모에 추가
  int parent;
  while(!leaf.empty()) {
    cur = leaf.front();
    parent = parents[cur];
    leaf.pop();

    // cur이 얼리 어답터인 경우 자기 자신도 세어야 한다
    dp[cur][1]++;
    if(cur == 1) {
    // 루트 노드는 부모에 업데이트할 필요 x, 종료
      break;
    }

    // parent가 얼리 어답터가 아닐 경우, cur은 무조건 얼리 어답터여야 한다
       dp[parent][0] += dp[cur][1];
    // parent가 얼리 어답터일 경우, cur은 무엇이든 무관하므로 최솟값을 사용한다
    dp[parent][1] += (dp[cur][1] &lt; dp[cur][0] ? dp[cur][1] : dp[cur][0]);

    // 부모에게 계산이 남아 있는 자식 노드가 줄어든다
    // 계산해야 할 자식 노드가 더이상 없다면 해당 노드도 큐에 추가한다
    if(--children[parent] == 0) {
      leaf.push(parent);
    }
  }

  // 루트 노드가 얼리 어답터인 경우, 아닐 경우를 비교
  cout&lt;&lt;(dp[1][0] &lt; dp[1][1] ? dp[1][0] : dp[1][1])&lt;&lt;&#39;\n&#39;;
  return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 엔진과 런타임 환경]]></title>
            <link>https://velog.io/@eujin-shin/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84%EA%B3%BC-%EB%9F%B0%ED%83%80%EC%9E%84-%ED%99%98%EA%B2%BD</link>
            <guid>https://velog.io/@eujin-shin/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84%EA%B3%BC-%EB%9F%B0%ED%83%80%EC%9E%84-%ED%99%98%EA%B2%BD</guid>
            <pubDate>Thu, 09 Jan 2025 07:56:31 GMT</pubDate>
            <description><![CDATA[<p>최근 자바스크립트의 비동기 처리와 동시성에 대해 공부를 하고 있는데, 관련 자료를 읽다 보니 “자바스크립트 런타임 환경”과 “자바스크립트 엔진”에 대한 언급이 자주 등장했다. 마침 자바스크립트 딥다이브를 공부하면서 <strong>자바스크립트 코드가 어떻게 동작하는 건지</strong>에 대한 궁금증이 생겨서, 이 둘이 자바스크립트의 동작에서 각각 무슨 역할을 하고 있는 건지 정리해 보려고 한다! 🧐</p>
<hr>
<h2 id="자바스크립트-엔진">자바스크립트 엔진</h2>
<p>자바스크립트 엔진은 자바스크립트 코드를 해석하고 실행하는 소프트웨어를 의미한다. 브라우저나 Node.js 같은 <strong>런타임 환경 안에서</strong> 사용된다.</p>
<h3 id="주요-엔진들">주요 엔진들</h3>
<ul>
<li><strong>V8</strong>: Google에서 개발했고, Google Chrome과 Node.js에서 동작한다.</li>
<li><strong>SpiderMonkey</strong>: Mozilla에서 개발한 최초의 자바스크립트 엔진으로, Firefox에서 동작한다.</li>
<li><strong>JavaScriptCore</strong>: iOS나 MacOS 기기의 Safari에서 동작한다.</li>
<li><strong>Chakra</strong>: Microsoft Edge(구 버전)에서 동작한다. 현재의 Edge는 Chromium 기반 브라우저로 V8 엔진을 사용한다.</li>
</ul>
<blockquote>
<p>브라우저가 서로 다른 엔진으로 자바스크립트 코드를 실행하더라도, 같은 코드에 대해 같은 결과를 가질 수 있도록 자바스크립트 스펙을 표준화해서 ECMAScript를 정하게 되었다. 
하지만 내부적인 실행 방식과 최적화 여부는 서로 다르기 때문에 브라우저마다 엔진에 따라 성능 차이가 발생할 수 있다!</p>
</blockquote>
<h3 id="엔진의-동작에-대해">엔진의 동작에 대해</h3>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/f2947c5d-d381-45b9-a930-57ce7de3992d/image.png" alt=""></p>
<p>엔진의 주요 구성요소로는 메모리 힙과 콜 스택이 있는데, 이 구조 안에서 코드 실행과 데이터가 관리된다.</p>
<ul>
<li><strong>메모리 힙(Memory Heap)</strong><ul>
<li>메모리 할당을 통해 참조 타입의 데이터를 저장한다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>💡 <strong>자바스크립트의 변수 식별자</strong>
우리가 변수에 값을 할당할 때, 할당하려는 값은 메모리 힙 안의 특정 위치에 저장된다. 그리고 변수는 이 값이 저장된 주소값을 가짐으로써 변수를 참조할 때 할당된 값을 반환하게 된다.</p>
</blockquote>
<ul>
<li><strong>콜 스택(Call Stack)</strong><ul>
<li>스택 형태의 자료구조로서, 현재 실행 중인 함수의 정보를 저장해서 함수의 동작을 관리한다.</li>
<li>함수가 호출되는 순서대로 스택에 <strong>실행 컨텍스트</strong>가 쌓이게 되고, 함수가 종료되면 스택에서 제거된다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>💡 <strong>실행 컨텍스트?</strong>
코드가 실행될 때 제공할 환경 정보를 갖고 있는 객체이다. 예를 들어 특정 코드 블록에서 사용 가능한 식별자 정보 등이 포함된다.</p>
</blockquote>
<p>자바스크립트는 하나의 콜 스택을 갖고 있기 때문에, 한 번에 하나의 일만 할 수 있는 <strong>싱글스레드 언어</strong>인 것이다.</p>
<h2 id="그럼-런타임-환경은">그럼 런타임 환경은?</h2>
<p>한 번에 하나만 할 수 있는데 자바스크립트는 어떻게 동시성을 가질 수 있을까? 런타임 환경까지 이해하고 나니 이 질문이 해소됐다. </p>
<p>위에서 자바스크립트 엔진이 런타임 환경 안에서 사용된다고 언급했다. 예시로 든 모든 엔진 역시 브라우저나 Node.js 안에서 동작하고 있다. 여기서 브라우저, Node.js가 런타임 환경에 해당한다.</p>
<p>런타임 환경은 자바스크립트 엔진이 코드를 실행할 수 있도록 추가적인 환경을 제공한다. 여기에는 <strong>이벤트 루프, 콜백 큐, (브라우저 환경의 경우) Web API</strong> 등이 포함된다.</p>
<h3 id="런타임-환경-자세히-살펴보기">런타임 환경 자세히 살펴보기</h3>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/9ee94e70-c44a-4f8e-b11d-8249e185e5d1/image.png" alt=""></p>
<ul>
<li><strong>이벤트 루프(Event Loop)</strong>: 비동기 작업을 관리하며, 콜백 함수가 콜스택으로 들어가는 시점을 조율.</li>
<li><strong>콜백 큐(Callback Queue)</strong>: 비동기 작업의 결과를 대기시키는 공간.</li>
<li><strong>Web APIs</strong> (브라우저 런타임): DOM 조작, HTTP 요청 등 브라우저가 제공하는 기능.</li>
<li><strong>Node.js APIs</strong> (Node.js 런타임): 파일 시스템, 네트워크 요청 등의 기능.</li>
</ul>
<p>런타임 환경에서 제공하는 이벤트 루프, 콜백 큐를 통해 자바스크립트는 비동기적으로 작업을 처리할 수 있게 된다. 비동기적으로 실행된 콜백 함수들이 콜백 큐에 들어 있다가, 선입선출 방식에 따라 콜 스택이 비어 있을 때(= 현재 다른 작업이 없을 때) 콜 스택으로 이동해서 실행되게 된다.</p>
<p>콜백 큐는 작업의 성격에 따라 다른 종류로 나눠져 있고(Promise, setTimeOut 등), 각자 우선순위가 다르다. 이때 콜 스택이 비어 있는지 확인하고, 현재 우선순위가 가장 높은 작업을 콜 스택으로 보내 주는 역할을 수행하는 게 <strong>이벤트 루프이다.</strong></p>
<p>또한 브라우저와 Node.js는 똑같이 자바스크립트 엔진을 통해 자바스크립트를 실행시킬 수 있지만, 제공하는 환경에는 차이가 있다.</p>
<ul>
<li>브라우저 런타임: HTML, CSS, 자바스크립트를 실행해 브라우저 화면에 웹 페이지를 렌더링하고자 한다.</li>
<li>Node.js 런타임: 브라우저 밖에서 자바스크립트를 실행하고자 한다.</li>
</ul>
<blockquote>
<p>브라우저 환경에서는 Web API를 제공하고, Node.js 환경에서는 이를 사용할 수 없다. 단 Node.js 환경에서도 libuv를 통해 비동기 처리가 가능하며, Node.js는 추가로 내장 파일 시스템을 제공한다.</p>
</blockquote>
<hr>
<p>자바스크립트 엔진이 코드를 실행하는 방식과 런타임 환경이 이를 보조하는 구조를 알아보면서 자바스크립트의 동작 원리를 조금 더 이해할 수 있었다. 이 내용을 바탕으로 다음에는 동시성에 대한 글도 작성해 봐야겠다!</p>
<p>이미지 출처 <a href="https://medium.com/sessionstack-blog/how-does-javascript-actually-work-part-1-b0bacc073cf">How JavaScript works</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트의 Date는 왜 이럴까?]]></title>
            <link>https://velog.io/@eujin-shin/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-Date%EB%8A%94-%EC%99%9C-%EC%9D%B4%EB%9F%B4%EA%B9%8C</link>
            <guid>https://velog.io/@eujin-shin/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-Date%EB%8A%94-%EC%99%9C-%EC%9D%B4%EB%9F%B4%EA%B9%8C</guid>
            <pubDate>Mon, 23 Dec 2024 06:11:46 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-javascript">const now = new Date();</code></pre>
<p>자바스크립트의 <code>Date</code> 객체를 생성할 수 있는 코드이다. 위 코드를 사용하면 현재 시간을 표현하는 값을 <code>now</code>에 저장할 수 있다. 외에도 Date 객체는 특정 시간에 대한 값을 저장하거나, 비교하는 등의 메서드를 제공해 자바스크립트 코드로 시간을 다룰 수 있게 해 준다.</p>
<h2 id="그런데-왜-🤔">그런데 왜... 🤔</h2>
<p>예약 서비스를 만들며 &#39;요일별 운영 시간&#39;을 설정하는 기능을 구현하다가, 이 기능에 Date 객체를 활용하기에는 근본적인 한계가 있음을 마주쳤다. 우리가 설정하고자 하는 건 <strong>특정 날짜에 종속되지 않는 시간</strong>이었지만, 자바스크립트의 Date 객체는 날짜 없이 시간만을 독립적으로 설정할 수 없다.</p>
<p>위 문제는 라이브러리를 통해 해결했다. 예약 서비스 내의 다른 기능에서 시간 연산이나 비교가 더 필요해질 여지가 있다고 판단되었기 때문에 날짜/시간 관련 라이브러리를 체크하고, 내가 원했던 대로 시간 데이터만 독립적으로 조작할 수 있는 <a href="https://moment.github.io/luxon/#/">luxon</a>을 도입했다. (luxon 사용에 대한 얘기는 다음 기회에 써 봐야겠다... ✏️)</p>
<p>하지만 의문이 들었다. 자바를 사용하는 백엔드 쪽에서는 LocalTime이라는 클래스를 사용해 아주 간단한 코드로!! 시간 정보만을 객체로 만들고 있는 걸 봐서 더 그랬다.</p>
<pre><code class="language-java">LocalTime lt = LocalTime.of(15, 0);
// 15:00</code></pre>
<p>그럼 자바스크립트는 왜 이런 걸까? 보다 유연하고 편리하게 시간을 다룰 수 있지 않았을까? 이 글에서는 이런 궁금증에서 출발해 Date 객체에 대해 더 알아보고, 그 과정에서 알게 된 Temporal에 대한 내용을 다룬다.</p>
<h2 id="date">Date</h2>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Date">mdn web docs - Date</a>
자세한 사용법과 예제는 위 문서에서 확인할 수 있다.</p>
<p><strong>객체 생성하기</strong>
문자열이나 숫자 값 등을 활용해 특정 시점을 지정하거나, 현재 시점을 객체로 저장할 수 있다.</p>
<ul>
<li><code>new Date()</code>: 인수 없이 호출하면 현재 날짜와 시간이 저장된 객체가 반환된다.</li>
<li><code>new Date(value)</code>: UNIX 타임스탬프 값(1970년 1월 1일 00:00:00 UTC를 기준으로 밀리초 단위의 시간)을 사용해 호출할 수 있다.</li>
<li><code>new Date(dateString)</code>: 파싱 가능한 형태의 문자열로 표현한 날짜/시간 정보를 사용해 호출할 수 있다. 즉 <code>&quot;December 23, 2024 10:05:00&quot;</code> 등을 전달할 수 있다. 단, <strong>해당 파싱은 브라우저 간 일관성이 보장되지 않기 때문에 사용하지 않을 것을</strong> 권장하고 있다.</li>
<li><code>new Date(year, monthIndex, day, hours, minutes, seconds, miliseconds)</code>: 각각의 구성 요소를 정해진 범위의 숫자로 전달할 수 있다. 이떄 최소한 연/월은 포함되어야 하며, 누락된 요소의 경우 <strong>기본값이 저장된다.</strong></li>
</ul>
<p><strong>getter / setter 사용하기</strong></p>
<ul>
<li><code>getFullYear(), getMonth(), getDate(), getDay()</code>등의 메서드를 통해 객체의 구성 요소를 가져올 수 있다. 이때 date는 날짜, day는 요일에 해당한다. 또한 month, day와 같은 값은 인덱스로 반환된다.</li>
<li>마찬가지로 <code>setFullYear(), setMonth(), setDate(), setTime()</code> 등을 사용하면 객체의 구성요소를 변경할 수도 있다.</li>
</ul>
<p><strong>시간 비교하기</strong>
Date 객체는 숫자형의 타임스탬프 값으로 변환될 수 있기 때문에, 이를 이용해 시간을 비교하는 것이 가능하다. 단 연, 월, 일의 길이는 타임스탬프에서 항상 일정하지 않기 때문에 (윤년, 월별 날짜수 등의 영향을 받는다) 이를 사람이 인식하는 기간으로 표현하는 데는 어려움이 생긴다. 문서의 예제에서도 주로 특정 동작을 수행하는 데 걸리는 시간을 측정하는 상황을 다루고 있다.</p>
<h3 id="한계점">한계점</h3>
<p><a href="https://maggiepint.com/2017/04/09/fixing-javascript-date-getting-started/">Fixing JavaScript Date - Getting Start</a></p>
<p>위 글은 자바스크립트의 또 다른 날짜/시간 라이브러리인 <a href="https://momentjs.com/">moment.js</a>, 그리고 앞으로 얘기할 Temporal에 참여한 Maggie Pint의 블로그다. 글에서는 Date 객체가 갖는 문제를 (더 심각한 순으로) 다음과 같이 나열하고 있다.</p>
<ol>
<li>사용자의 로컬 타임(시스템 설정 시간), UTC 표준 시간대 외의 다른 시간대를 지원하지 않는다.</li>
<li>Parser 동작이 일관되지 않아 사용할 수 없다.</li>
<li>Date 객체가 _가변성_을 갖는다.</li>
<li><em>DST</em> 적용을 예측하지 못한다. </li>
<li>연산 API가 사용하기 어렵다.</li>
<li>_그레고리력_만을 지원한다.</li>
</ol>
<blockquote>
<p>보충 💡</p>
</blockquote>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Glossary/Immutable">불변성 immutable</a>과 <a href="https://developer.mozilla.org/ko/docs/Glossary/Mutable">가변성 mutable</a> 개념이 익숙하지 않다면 문서를 확인해 보자.</li>
<li>DST는 Daylight Saving Time, 서머타임을 의미한다. 한국에서는 서머타임을 적용하지 않아서 낯설지만 간략히 소개하면 낮이 긴 하절기에 시간대를 원래보다 한 시간 당기는 제도라고 한다.</li>
<li>그레고리력은 현재 세계 표준으로 사용되는 달력으로, 400년에 윤년이 97번 돌아온다.</li>
</ul>
<h3 id="왜-발생했을까-그리고-왜-해결되지-않았을까">왜 발생했을까? 그리고 왜 해결되지 않았을까?</h3>
<p>자바스크립트는 초기 설계 당시 <strong>열흘 만에</strong> 만들어졌다. 이렇듯 시간이 부족한 상황에서 날짜 데이터에 대한 요구사항을 해결하기 위해 기존에 나와 있던 Java의 Date 클래스를 참조했고, 자연스레 해당 클래스가 갖던 문제점까지 공유하게 되었다. </p>
<p>실제로 자바의 경우 해당 문제 해결을 위해 자바 8 버전부터 java.time이라는 새로운 패키지를 도입하고, 위에서 언급된 시간대 처리, 가변성 보유 등의 문제를 해결하면서 기존의 Date 클래스를 글 초반에 언급한 LocalDate, LocalTime 클래스 등으로 대체하게 되었다고 한다.</p>
<p>자바스크립트에서는 어땠을까? 위 문제 중 일부는 <strong>Date 객체를 수정하는 선에서</strong> 해결 가능했다. </p>
<ul>
<li>시간대 지원이나 그레고리력 외의 역법 지원은 새로운 factory 함수(= 객체를 반환하는 함수)를 추가해 해결할 수 있을 것이다.</li>
<li><code>setDate()</code>같은 기존의 api보다 직관적인 api (예를 들면 <code>addDate()</code>)를 추가하면 연산 api의 사용이 어렵다는 문제도 해결할 수 있을 것이다.</li>
<li>DST(서머타임) 버그의 경우 ECMAScript 2018에서 해결되었다고 한다.</li>
</ul>
<p>반면 해결할 수 없는 문제가 있었다. <a href="https://maggiepint.com/2017/04/11/fixing-javascript-date-web-compatibility-and-reality/">Maggie Pint의 다음 블로그 글</a>이 이를 설명하고 있다.</p>
<p><strong>Date 객체의 가변성</strong></p>
<p>Date 객체는 setter를 통해 값을 변경할 수 있다. setter가 기존 인스턴스를 수정하는 대신 새로운 인스턴스를 반환하게 하는 등 불변성을 보장하면 에러에 덜 취약해지겠지만, 이러한 변경은 기존에 가변성을 활용해 작성되어 있던 코드를 망가트릴 것이다.</p>
<p><strong>일관되지 않은 파싱</strong></p>
<p>자바스크립트에서 날짜/시간 문자열의 형식은 <a href="https://ko.wikipedia.org/wiki/ISO_8601">ISO 8601</a>을 따른다. 이는 국제적으로 통용되는 날짜와 시간에 대한 표준이기도 하다. </p>
<p>ISO 8601에서는 날짜 정보만을 갖는 <code>date-only form</code>과 여기에 시간 정보가 결합된 <code>date-time form</code>을 정의하고 있다. 자바스크립트에서는 이 포맷을 해석할 수 있다. 그리고 <strong>시간대가 정의되지 않았다면,</strong> <code>date-only form</code>은 표준 시간대로, <code>date-time form</code>은 로컬 시간대로 추론된다.</p>
<p>예를 들어 시스템이 한국 시간대를 따르고 있을 때 다음과 같이 Date 객체를 생성한다고 해 보겠다.</p>
<pre><code class="language-javascript">const dateTime = new Date(&quot;2024-12-23T00:00&quot;);
const date = new Date(&quot;2024-12-23&quot;);</code></pre>
<p>여기서 <code>dateTime</code>은 한국 시간 00시 00분에 해당하는 값을 갖는다. 반면 날짜만 입력한 <code>date</code>는 표준 시간 00시 00분, UTC+9인 한국 시간으로는 09시 00분에 해당하는 값을 갖게 된다.</p>
<p>원래 ISO 8601 표준에서는, 시간대가 정의되지 않았을 경우 로컬 시간대로 해석되어야 한다고 정했다. 위의 예시에서 두 변수는 모두 한국 시간대를 기준으로 추론되어야 하는 것이다. 하지만 ES5에서 이와 반대로 시간대가 정의되지 않았을 때 <strong>표준 시간대로</strong> 추론하게 되었고, 이 오류를 ISO 8601에 맞춰 로컬 시간대로 수정하고자 시도했을 때 <strong>시간 정보가 이전과 다르게 해석된다</strong>는 사용자들의 버그 리포트가 발생하면서 현재처럼 포맷에 따라 다른 시간대 추론이 정착하게 되었다고 한다.</p>
<blockquote>
<p>A blessing and curse of JavaScript is that it is currently the world’s most popular programming language.</p>
</blockquote>
<p>원문에서는 위와 같은 설명과 함께, 이 문제들을 두 개의 원칙 아래에 두고 있다.</p>
<p><strong>1. Web compatibility</strong>: ECMAScript는 현재 표준과 충돌하는 수정을 적용할 수 없다. 
<strong>2. Web Reality</strong>: 코드가 현재 동작하고 있다면, 이후의 표준도 이 동작이 유지될 것을 보장해야 한다.</p>
<p>결론적으로 이미 만들어진 코드가 계속 작동할 수 있어야 하기 때문에, 이 문제들을 고칠 수 없다는 것이다. 그럼 자바스크립트에서도 java.time 패키지처럼 새로운 걸 만들면 안 될까? 🧐</p>
<h2 id="temporal">Temporal</h2>
<p>이러한 문제를 해결하기 위해 새로운 객체 <a href="https://tc39.es/proposal-temporal/docs/index.html">Temporal</a>이 제안되었다. Temporal은 다음 해결책을 적용해, 기존 Date 객체가 갖던 문제점들을 해소하고자 한다.</p>
<ol>
<li>직관적인 시간과 날짜 연산 api를 제공한다.</li>
<li>DST 반영을 포함해 모든 시간대를 지원한다.</li>
<li>고정된 날짜와 시간을 다룸으로써 불변성을 보장한다.</li>
<li>엄격하게 구체화된 포맷에 따라 파싱한다.</li>
<li>그레고리력 외의 달력을 지원한다.</li>
</ol>
<p>또한 위에서 소개한 자바의 LocalTime, LocalDate처럼 날짜 정보와 시간 정보를 독립적으로 다룰 수 있도록 지원한다. (처음에 Date 객체에 대해 느꼈던 불편함도 Temporal을 통해 해소할 수 있을 것이다! 😄)</p>
<h3 id="date에서-temporal로">Date에서 Temporal로?</h3>
<p>사실 Date 공식 문서에서도 Temporal을 소개하고 있지만, &quot;아직 프로덕션에는 적합하지 않다&quot;고 설명한다. 현재 Temporal은 Stage 3에 올라와 있는데, 완성된 명세를 바탕으로 구현 주체나 사용자들에게 피드백을 받는 단계라고 한다.</p>
<blockquote>
<p>보충 💡</p>
</blockquote>
<ul>
<li>Stage 3은 TC39 프로세스의 절차로, ECMAScript 기술 위원회인 TC39와 제안 프로세스에 대해서는 아래 글에서 읽어 볼 수 있다.
<a href="https://ahnheejong.name/articles/ecmascript-tc39/">ECMAScript와 TC39</a></li>
</ul>
<p>개발 중인 서비스에서 직접 적용해 볼 수 없는 건 아쉽지만, Temporal을 통해 Date보다 더 발전된 형태로 시간을 다뤄볼 날이 기대된다. ✨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PS] 백준 1507: 궁금한 민호]]></title>
            <link>https://velog.io/@eujin-shin/PS-%EB%B0%B1%EC%A4%80-1507-%EA%B6%81%EA%B8%88%ED%95%9C-%EB%AF%BC%ED%98%B8</link>
            <guid>https://velog.io/@eujin-shin/PS-%EB%B0%B1%EC%A4%80-1507-%EA%B6%81%EA%B8%88%ED%95%9C-%EB%AF%BC%ED%98%B8</guid>
            <pubDate>Thu, 19 Dec 2024 07:42:35 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/eujin-shin/post/394f50e2-279b-4598-93b7-032b1a9c9326/image.png" alt=""></p>
<h3 id="풀이-과정-🔎">풀이 과정 🔎</h3>
<ul>
<li><p>가장 가까운 경우부터 체크하면서 도로를 추가해야 하는지, 다른 도로로 최소 이동 시간을 달성 가능한지 확인하면 어떨까 (주어진 게 최소 이동 시간이라고 해서 이런 생각이 들었던 듯)</p>
</li>
<li><p>이때, 확인되는 이동 시간에 따라 도로를 추가할지 결정하자.</p>
<blockquote>
<ul>
<li><strong>주어진 최소 이동 시간보다 작다면?</strong> 이미 갱신된 최솟값이 늘어날 수는 없음. 성립 불가능</li>
<li><strong>주어진 최소 이동 시간과 같다면?</strong> 지금까지 추가한 도로만으로 조건 만족됨. 추가 처리 불필요</li>
<li><strong>주어진 최소 이동 시간보다 크다면?</strong> 처음에는 이때 경우의 수가 다양해지지 않을까 고민했는데, 이미 추가한 도로를 경로에 포함시키고 새로운 도로를 추가한다면 그 도로가 연결하는 두 도시는 최소 이동 시간이 갱신되어 버리기 때문에 불가능하다는 생각이 들었다. 이 경우에는 무조건 최소 이동 시간을 비용으로 가지는 하나의 도로를 추가하면 된다고 결론내렸다.</li>
</ul>
</blockquote>
</li>
<li><p>그렇다면 현재까지 추가한 도로를 통해 모든 도시에서 모든 도시 사이의 최소 이동 시간을 가지고 있어야 한다.</p>
</li>
<li><p>플로이드-워셜로 도로를 추가할 때마다 새 도로를 경유해서 갱신할 수 있는 최소 이동 시간을 갱신하면 되겠다!</p>
</li>
<li><p>로직은 다음과 같이 정리했다.</p>
<blockquote>
<p>최소 이동 시간이 짧은 순으로 두 도시 x, y 사이의 이동 시간을 확인
(1) 현재 이동 시간 &lt; 주어진 최소 이동 시간이라면 불가능한 것으로 플래그 표시하고 종료
(2) 현재 이동 시간 &gt; 주어진 최소 이동 시간이라면 x, y 사이에 도로를 추가
(2)-1. 서로 다른 모든 a, b 쌍에 대해 x, y 사이의 도로를 경유해서 이동하는 시간을 체크한 후 최솟값 갱신</p>
</blockquote>
</li>
</ul>
<h3 id="정답-코드-✅">정답 코드 ✅</h3>
<pre><code>// C++ 사용

#include &lt;iostream&gt;
#include &lt;queue&gt;
#define INF 50000
#define pnt pair&lt;int, int&gt;
#define ppnt pair&lt;int, pnt&gt;
using namespace std;

// 도시 간의 거리가 가까운 순서대로 체크
// 풀고 난 뒤 생각해 보니 우선순위 큐까지 쓸 필요 없이 벡터나 배열에 입력받아서 정렬하고 써도 됐겠다는 생각이 들었다.
priority_queue &lt;ppnt, vector&lt;ppnt&gt;, greater&lt;ppnt&gt; &gt; pq;
int map[20][20] = {0, }, dist[20][20] = {0, };

int main() {
  int n;
  cin&gt;&gt;n;

  // 주어진 거리 입력 &amp; 탐색된 거리 초기화
  for(int i = 0; i &lt; n; i++) {
    for(int j = 0; j &lt; n; j++) {
      cin&gt;&gt;map[i][j];
      if(i != j) {
        dist[i][j] = INF;
      }
      if(j &gt; i) {
        pq.push(make_pair(map[i][j], make_pair(i, j)));
      }
    }
  }

  bool isAble = true;
  int sum = 0, distance, update, x, y;

  // 가까운 순으로 주어진 모든 거리 정보 체크
  while(!pq.empty() &amp;&amp; isAble) {
    distance = pq.top().first;
    x = pq.top().second.first;
    y = pq.top().second.second;
    pq.pop();

    if(dist[x][y] &lt; distance) {
      // 현재 두 도시 사이의 최소 거리가 주어진 거리보다 가까운 경우: 성립 불가능
      isAble = false;
    } else if(dist[x][y] &gt; distance) {
      // 현재 두 도시 사이의 최소 거리가 주어진 거리보다 먼 경우: 도로를 추가
      dist[x][y] = dist[y][x] = distance;
      sum += distance;

      // 해당 도로를 경유할 때의 최소 거리 갱신
      for(int start = 0; start &lt; n; start++) {
        for(int end = start + 1; end &lt; n; end++) {
          update = INF;
          if(dist[x][start] != INF &amp;&amp; dist[y][end] != INF) {
            update = dist[x][start] + dist[y][end];
          }
          if(dist[x][end] != INF &amp;&amp; dist[y][start] != INF &amp;&amp; update &gt; dist[x][end] + dist[y][start]) {
            update = dist[x][end] + dist[y][start];
          }

          if(update != INF &amp;&amp; update + distance &lt; dist[start][end]) {
            dist[start][end] = dist[end][start] = update + distance;
          }
        }
      }
    }
  } 

  if(isAble) {
    cout&lt;&lt;sum&lt;&lt;&#39;\n&#39;;
  } else {
    cout&lt;&lt;&quot;-1\n&quot;;
  }
  return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[PS] 백준 32624: 신기한 루트 개수 찾기]]></title>
            <link>https://velog.io/@eujin-shin/PS-%EB%B0%B1%EC%A4%80-32624-%EC%8B%A0%EA%B8%B0%ED%95%9C-%EB%A3%A8%ED%8A%B8-%EA%B0%9C%EC%88%98-%EC%B0%BE%EA%B8%B0</link>
            <guid>https://velog.io/@eujin-shin/PS-%EB%B0%B1%EC%A4%80-32624-%EC%8B%A0%EA%B8%B0%ED%95%9C-%EB%A3%A8%ED%8A%B8-%EA%B0%9C%EC%88%98-%EC%B0%BE%EA%B8%B0</guid>
            <pubDate>Wed, 04 Dec 2024 08:15:06 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/eujin-shin/post/978c03de-8a01-496c-aa73-ec08f2e383e8/image.png" alt=""></p>
<h3 id="풀이-과정-🔎">풀이 과정 🔎</h3>
<ul>
<li><p>우선 조건을 만족하는 <strong>&quot;K번 정점&quot;</strong>에 대해 조금 더 이해해 보려고 했다. 문제에서는 이 정점이 루트가 되었을 때, 주어진 두 정점 A와 B의 &quot;가장 가까운 공통 조상이 A이거나 B가 되지 않아야 한다&quot;고 설명했다. 조금은 꼬아둔 설명이었던 것 같은데... 😌 </p>
</li>
<li><p>다르게 이해하면 <strong>정점 K에서 정점 A로 가는 경로에 정점 B가 포함되거나, 그 반대의 경우(정점 B로 가는 경로에 정점 A가 포함)가 되어서는 안 된다</strong>, 즉 정점 K에서 두 정점으로 가는 경로는 겹치지 않아야 한다고 해석할 수 있었다.</p>
</li>
<li><p>여기까지 찾았을 때 <strong>그래프 탐색</strong>을 사용해야 할 것 같다고 생각했는데, 정점의 최대 범위가 300,000이므로 각 정점에 대해 이 정점이 조건을 만족하는지 판단하려고 하면 시간초과가 발생할 것 같았다. 따라서 해석한 조건을 반대로 접근해 봤다.</p>
</li>
</ul>
<blockquote>
<p>(1) 정점 A에서 정점 B로 가는 경로에 포함되는 정점
(2) (1)에 해당하는 정점으로부터 A와 B를 거치지 않고 도달할 수 있는 정점</p>
</blockquote>
<ul>
<li>이렇게 하면 답이 될 수 있는 모든 정점에서부터 시작하는 대신, 주어진 두 개의 정점으로부터 시작해서 탐색이 가능했다. 로직은 다음과 같이 구상했다.</li>
</ul>
<blockquote>
<p>(1) <code>A</code>에서 시작하고, <code>B</code>를 찾아야 할 타겟 정점으로 둔다.
(2) <code>A</code>에 인접해 있는 각각의 정점들(<code>a1</code>, <code>a2</code>, <code>a3</code>...)에서 차례대로 BFS를 수행한다.
(3) 탐색 중 <code>A</code>, <code>B</code>는 방문할 수 있는 정점에서 제외하고, 각 BFS 시행마다 방문한 정점의 개수를 센다. 
(4) 탐색 중 <code>B</code>를 발견할 경우, 해당 시행에서 방문한 정점의 개수가 정답이다.</p>
</blockquote>
<h3 id="정답-코드-✅">정답 코드 ✅</h3>
<pre><code>// C++ 사용

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;queue&gt;
using namespace std;

// 각 정점에서 간선으로 이어지는 다른 정점들을 저장
vector &lt;int&gt; v[300001];

// DFS 탐색에서 특정 정점의 방문 여부를 저장
int visited[300001] = {0, };

int main() {
  ios_base::sync_with_stdio(false);
  int n, start, end, x, y;

  // start = 주어진 두 정수 A, B 중 하나
  // end = A, B 중 나머지 하나
  cin&gt;&gt;n&gt;&gt;start&gt;&gt;end;

  // 트리 입력
  for(int i = 1; i &lt; n; i++) {
    cin&gt;&gt;x&gt;&gt;y;
    v[x].push_back(y);
    v[y].push_back(x);
  }

  queue &lt;int&gt; q;
  int count, cur, next;
  bool correct = false;
  visited[start] = 1;

  // start에 인접한 각각의 정점으로부터 DFS 탐색 시작
  for(int i = 0; i &lt; v[start].size(); i++) {
    q.push(v[start][i]);
    visited[v[start][i]] = 1;
    count = 0;
    while(!q.empty()) {
      cur = q.front();
      q.pop();

      // 탐색 중 end가 발견될 경우 현재 탐색에서 도달한 모든 정점은 조건 만족
      if(cur == end) {
        correct = true;
        continue;
      }
      count++;
      for(int j = 0; j &lt; v[cur].size(); j++) {
        next = v[cur][j];
        if(visited[next] == 0) {
          visited[next] = 1;
          q.push(next);
        }
      }
    }
    if(correct) {
      break;
    }
  }

  // 조건을 만족한 탐색에서 방문한 정점의 개수
  cout&lt;&lt;count&lt;&lt;&#39;\n&#39;;
  return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스크럼과 스프린트 기반으로 프로젝트하기 🔄]]></title>
            <link>https://velog.io/@eujin-shin/%EC%95%A0%EC%9E%90%EC%9D%BC%ED%95%98%EA%B2%8C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@eujin-shin/%EC%95%A0%EC%9E%90%EC%9D%BC%ED%95%98%EA%B2%8C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 26 Nov 2024 14:14:45 GMT</pubDate>
            <description><![CDATA[<h2 id="스크럼-스프린트-애자일">스크럼, 스프린트, 애자일?</h2>
<blockquote>
<p><strong>애자일(Agile)</strong> 소프트웨어 개발을 비롯한 다양한 프로젝트에서 유연하고 반복적인 작업 방식을 지향하는 방법론. 변화하는 요구사항에 빠르게 적응하며, 고객 가치를 최우선으로 두고 결과물을 점진적으로 발전시키는 데 초점을 맞춘다.
<strong>스크럼(Scrum)</strong> 애자일 철학을 기반으로 한 프레임워크.
<strong>스프린트(Sprint)</strong> 한 달 이내의 짧은 주기로 이루어진 작업 기간. 이 기간 동안 팀은 스프린트 목표를 설정하고, 목표를 달성하기 위한 작업을 집중적으로 수행한다. 스프린트가 끝난 후에는 결과물을 점검하고, 다음 단계의 계획을 세우기 위해 회고와 리뷰를 진행한다.</p>
</blockquote>
<p>내가 이 개념들을 처음 접한 건 소마 프로젝트를 시작하면서였다. 우리 팀의 전담 멘토님 중 한 분께서 (멘토님에 대한 이야기는 밑에서 좀 더 자세히 쓰겠다! 🫢) 예비 기간의 특강을 통해 <strong>애자일 개발 방법론</strong>을 소개해 주셨고, 멘토님께 멘토링을 받으며 우리 팀은 자연스럽게?! <strong>지라와 컨플루언스, 스크럼, 스프린트</strong>를 프로젝트에 적용해 볼 수 있었다.</p>
<p>이렇게 프로젝트 관리를 해 본 건 처음이었는데, 시간이 갈수록 <strong>&quot;지난 프로젝트에서도 이렇게 했더라면 더 효율적이지 않았을까?&quot;</strong> 생각하게 될 때가 종종 있었다. 과정을 마무리하면서 돌이켜 보면 6개월의 기간 동안, 지금까지의 프로젝트 중 가장 큰 볼륨으로 진행했던 소마 프로젝트를 기간 끝까지 꾸준히 할 수 있었던 데는 프로젝트 관리를 성실히한 덕이 컸다는 생각이 든다. 👏 </p>
<p>그래서 이 글을 적어 보게 되었다! <del>아직 나도 좀 헷갈리는</del> 이론과 개념에 대한 이야기보다는, 이런 도구와 개념을 바탕으로 <strong>어떻게 프로젝트를 진행했는지, 그렇게 했을 때 어떤 효과를 느꼈는지</strong>를 중심으로 기록해 보려고 한다. </p>
<h2 id="소마에서의-프로젝트-관리-feat-jira">소마에서의 프로젝트 관리 (feat. Jira)</h2>
<h3 id="jira--confluence">Jira &amp; Confluence</h3>
<blockquote>
<p>지라 / 컨플루언스 모르는 사람에게 설명하기: 노션 비슷한 것, 근데 이제 스프린트 개발에 최적화된...</p>
</blockquote>
<p>우선 지라는 <strong>프로젝트 및 이슈 관리 도구</strong>로 스크럼, 칸반 같은 형태의 작업 관리를 지원한다. 전체적인 타임라인 설계, 스프린트 진행, 작업 내역 및 예정 작업 관리, 진행 상황 시각화 같은 기능을 갖고 있다. 깃허브와도 연동되어서 실제 개발과 밀접하게 관리할 수 있다.
컨플루언스는 <strong>협업용 문서 관리 도구</strong>다. (이쪽은 진짜 노션이랑 비슷하다) 지라와 같은 Atlassian에서 서비스하기 때문에 연동이 가능하다.</p>
<p>우리 팀에서는 이번에 지라와 컨플루언스에서 프로젝트를 관리했는데, 2주 단위로 스프린트를 진행하면서 <strong>칸반 보드와 백로그</strong>를 중심으로 사용했다.</p>
<h3 id="스프린트-플래닝">스프린트 플래닝</h3>
<p>2주 스프린트는 플래닝으로 시작됐다. 😌</p>
<ul>
<li>이번 스프린트의 목표</li>
<li>스프린트 기간 동안의 이슈 사항, 개인 일정</li>
<li>일정을 바탕으로 한 예상 작업 시간 산출</li>
<li>스프린트 내에 해결해야 할 작업 티켓별로 시간 추정치 작성</li>
<li>팀원 각자에게 할당할 작업 티켓 결정</li>
</ul>
<p>플래닝 시간에는 주로 이런 얘기를 나누고 문서에 기록했다. 가장 좋았던 건 이렇게 내게 할당된 작업을 바탕으로 <strong>스프린트 기간 동안의 작업을 계획할 수 있다</strong>는 점, 그리고 장기 프로젝트 동안 해이해지지 않고 <strong>전체적인 타임라인에서의 프로젝트 진행 현황을 트래킹할 수 있다</strong>는 점! 또 &#39;작업이 가능한 시간&#39;을 미리 계산하고 &#39;각 작업에 걸릴 시간&#39;을 예상한 다음 작업을 할당하면 2주의 기간 안에 어느 정도 도달 가능한 작업량을 설정하게 되는 만큼, 꾸준히 효능감을 느낄 수 있게 해 주는 장치이기도 하다.</p>
<p>물론 이런저런 시행착오를 겪기도 한다. 개발 중 예상 못한 에러를 잡느라 예상 시간을 훌쩍 넘기거나, 일정과 무관하게 해야 할 일이 넘쳐서 일단 스프린트 안에 작업 티켓을 가득 채우고 본다거나... 😂 특히 초반에는 시간 추정치와 실제 작업 시간에 차이가 발생하는 경우가 많았다. 우리 팀은 각자가 자신의 작업 내역을 회고하면서 점차 해소된 문제이긴 했지만, 더 신중하게 시간 추정치를 계산하려면 멘토님께서 가르쳐 주신 <a href="https://brunch.co.kr/@workingus/38">플래닝 포커</a>를 활용해 볼 수도 있을 것 같다. 프로젝트 후반부에는 플래닝에서 정한 작업 범위의 달성률이 조금씩 올라가기도 했는데, 이때도 성취감을 느낄 수 있었다 👍</p>
<h3 id="데일리-스크럼">데일리 스크럼</h3>
<p>매일 아침에는 데일리 스크럼을 진행한다. 스프린트 플래닝에 따라 &#39;오늘 하루 동안&#39; 할 일을 정하고, 공유할 이슈가 있다면 간단히 얘기 나눈다. <em>그날의 기분이나 컨디션, TMI를 얘기하기도 한다는데 우리는 거기까진 안 했다... ㅎㅎ ;</em> 데일리 스크럼은 할 일을 시작하기 전 짧게 진행한다. 빠른 진행을 위해 서서 이야기하는 스탠드업 미팅도 있다고 한다! 우리 팀의 경우 <strong>10분~15분</strong> 정도로 시간을 잡았는데, 익숙해지기 전까지는 조금 오래 걸렸다가 익숙해진 뒤로는 그날의 이슈가 얼마나 많은가... 에 따라 달라졌던 것 같다... 😅</p>
<h3 id="티켓으로-개발-관리하기">티켓으로 개발 관리하기</h3>
<p>매일 스크럼을 반복하면서, 스프린트 도중의 진행 상황은 지라의 <strong>칸반 보드</strong>를 통해 관리한다.</p>
<ul>
<li>티켓</li>
</ul>
<p>우리 팀에서는 티켓이 개발 단위였고, 티켓마다 <strong>하나의 브랜치와 하나의 풀 리퀘스트</strong>가 나왔다. 적절한 크기로 티켓을 나누는 것도 처음엔 조금 어려웠는데, 후반부로 가면서는 하나의 피쳐 당 하나의 백엔드 티켓, 하나의 프론트엔드 티켓이 기본으로 나왔던 것 같다. 피쳐에서 티켓을 만들고, 티켓 본문에 상세한 개발 요건이나 개발 후 작성한 PR 내용을 남겼다. 관련해서 기록할 이슈가 발생했다면 댓글을 쓰거나, 컨플루언스 문서를 만든 뒤 티켓에 기록했다.</p>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/e9b347e0-005d-4e41-ad9c-65f8e10dd143/image.png" alt=""></p>
<p>속성도 설정할 수 있는데, 기본적으로는 <strong>담당자</strong>와 <strong>작업 시간</strong>을 설정해서 썼고 필요에 따라 우선순위를 사용하기도 했다. 덤으로 깃허브와 연결하면 티켓으로부터 바로 브랜치를 생성하고, 해당 브랜치의 풀 리퀘스트나 빌드 내역까지 지라에서 확인할 수 있다! 😎</p>
<ul>
<li>워크플로우</li>
</ul>
<p>칸반 보드에 기본으로 <code>할 일</code>, <code>진행 중</code>, <code>완료</code>가 있었던 것 같다. 우리는 조금씩 조정을 거치며 <code>할 일</code> - <code>진행 중</code> - <code>리뷰 요청</code> (PR 진행 중) - <code>완료</code>의 흐름에 정착했고, 추가로 우선순위가 떨어졌거나 당장 작업하지 못하는 티켓은 <code>보류</code>로 넘겼다. 스프린트를 진행하며 작업을 할 때마다 티켓에 시간을 기록하고, 워크플로우에 따라 티켓 상태를 바꾸면 칸반 보드를 통해 이번 스프린트의 현황을 한눈에 파악할 수 있어 좋았다.</p>
<ul>
<li>백로그</li>
</ul>
<p>백로그에는 &quot;나중에 할 일&quot;을 티켓화해서 모아둔다. 기능 명세에 따라 피쳐별로 티켓을 만들어 놓기도 했고, 프로젝트 진행 도중 <strong>&quot;당장 급한 건 아니지만 나중에 해야겠다&quot;</strong>고 떠오른 일들을 백로그에 넣어 두면 플래닝 때 백로그를 보면서 티켓을 할당할 수 있다. 이런 작업들은 대체로 시간 지나면 까먹기 십상이라 백로그에 모아 두면 잊지 않고 가져갈 수 있어서 좋았다. (물론 잊지 않는 것과 그 일을 정말 하는 건 다른 문제지만... 🙄)</p>
<h3 id="스프린트-회고">스프린트 회고</h3>
<p>스프린트가 끝날 때는 리뷰 / 회고를 진행한다. 우리는 멘토님과 함께 진행해서 해당 스프린트의 성과나 결과물 등을 공유하고, 완료되지 않은 할 일들에 대해 이유를 파악하고, 지난 스프린트의 목표 달성 여부를 체크한 뒤 KPT 회고를 진행했다. </p>
<p>스프린트 회고는 특히 <strong>계속해서 팀원들과 터놓고 이야기할 시간을 만들어 줬다</strong>는 점에서 좋았다. KPT 회고를 하면서 이번 스프린트의 좋았던 점, 팀원들에게 감사했던 점, 힘들었던 점이나 고치고 싶은 점 등을 이야기 하는데 우선 나 스스로도 계속해서 성찰할 수 있는 계기가 됐고, 팀원들에게 솔직하게 고마운 마음을 전하거나 스스로 부족했던 부분을 반성하는 시간을 가질 수 있었다. 🤍</p>
<h3 id="with-이광헌-멘토님">with 이광헌 멘토님</h3>
<p>우리 팀이 이렇게 프로젝트 관리를 할 수 있었던 데는 소마의 이광헌 멘토님이 아주 아주 큰 기여를 해 주셨다. 🥹 초반 애자일에 대한 이해나 지라 세팅부터 도와주시기도 했고, 우리의 스프린트 주기에 맞춰 2주마다 한 번씩 규칙적으로 멘토링을 진행하면서 회고를 같이해 주셨다. 멘토님과 같이 회고를 하면 우리의 이야기나 고민에 많이 조언을 해 주시는 것도, 그때그때 우리가 성장할 수 있는 도전 과제를 제시해 주시는 것도 모두 좋았지만 거기 앞서서 <strong>꾸준히 이 과정을 반복할 수 있다</strong>는 큰 원동력을 얻게 된다. </p>
<p>사실 이런 프로젝트 관리는 누구도 강제로 시키지 않기 때문에, 팀에서 관심을 갖고 시작하더라도 누군가 한 명이라도 의지를 갖고 이끌어가지 않는 이상 흐지부지될 위험이 있다. 😢 실제로 지라 자체도 우리에게는 낯선 도구였어서, 소마에서 함께 활동한 다른 팀들은 지라나 스프린트 기반 개발을 끝까지 가져가지 않는 경우도 많이 있었던 것 같다. (물론 애자일이 무조건 정답인 것도 아니니 팀에게 맞는 방법을 찾으면 되는 것!) 하지만 우리 팀에게는 멘토님이 계셨으므로...! 프로젝트 관리도, 프로젝트 자체도 소마 최종 발표까지 텐션을 유지할 수 있었다. 이렇게 하나의 프로젝트를 완료해 본 건 정말 값진 경험으로 남을 것 같다. </p>
<p>혹시나 만약에 소마 16기(나 그 이후)에 지원한 분께서 이 글을 보고 애자일에 관심을 갖게 되신다면 이광헌 멘토님을 모시는 걸 적극 추천한다! 👍👍 단순히 애자일에 한정되지 않고, 프로젝트를 발전시키는 것은 물론 &quot;일 잘하는 방법&quot;에 대해서도 많은 것을 가르쳐 주시는 멘토님이라고 생각한다 😌 (인기가 많으시니까 빠르게 어필하시길...!)</p>
<h2 id="다음-프로젝트를-시작한다면">다음 프로젝트를 시작한다면</h2>
<p>내 다음 프로젝트가 언제, 어디서, 무엇이 될지는 모르겠지만 🤔 이번 프로젝트를 진행하면서 인원과 프로젝트의 목적에 무관하게 이런 관리 방법을 적용하는 게 도움이 될 것 같다는 생각을 여러 번 했다. 혼자 토이 프로젝트를 하더라도 위 시스템 중 간소화한 방식으로나마 채택해서 적용해 보고 싶은 마음이다. </p>
<p>예를 들어 지라나 컨플루언스를 새로 익히기 부담스럽다면, (사용은 나름 직관적인 듯했지만 소소한 기능이 많아서 잘 활용하기까지는 시간이 좀 걸릴 수 있을 것 같다) 아마 많은 사람들이 이미 익숙하게 쓰고 있을 노션을 활용할 수도 있을 것이다. 쓰면서 내가 느낀 장단점이라면</p>
<ul>
<li>노션 vs 지라: 노션이 보편적이고 친숙하지만, 협업 플랫폼으로 사용할 경우 인원수에 따라 무료 플랜 이용이 불가능하다는 단점. 지라의 경우 처음이라면 조금 낯설 수 있으나 무료로 팀에서 함께 사용할 수 있고, 아무래도 스프린트 기반의 작업 관리에 최적화된 기능들이 많다.</li>
<li>노션 vs 컨플루언스: 여기에선 사실 지라를 사용하기로 결정한 게 아닌 이상 노션의 손을 들 것 같다... 문서 작성 면에서는 약간 노션 + 구글 독스 같은 느낌이고, 지라와 연동이 수월하지만 컨플루언스의 표나 데이터베이스 기능을 사용하다 보면 노션의 데이터베이스가 아쉬워지는 순간이 종종 있었고 🥲 우리 팀도 프로젝트 후반부에는 노션을 일부 병행해서 사용했다.</li>
</ul>
<p>이 정도! 내 기준으로는 개인 프로젝트라면 아직 지라에서 새 워크스페이스를 세팅하기보다 노션에서 시작하는 게 가벼울 것 같고, 팀 프로젝트라면 팀 내의 지라 사용 경험에 따라 도입을 고민해 볼 것 같다.</p>
<p>데일리 스크럼은 일단 풀타임으로 프로젝트를 진행해야 적용할 것 같은데 😅 팀 프로젝트라면 팀이 만나는 주기에 맞춰 현재까지의 작업 현황이나 이슈 상황을 공유하는 방식으로 가져가는 것도 좋겠다. 또 프로젝트 기간이 아주 짧은 게 아닌 이상, 스프린트 기반으로 플래닝과 회고를 진행하는 건 꾸준히 텐션을 유지하고 팀 내 소통을 도모하는 데 도움이 많이 될 것 같다!</p>
<hr>
<p>마지막으로 이 모든 일들에 앞서서, 팀 프로젝트를 잘 진행하기 위해서는 당연하게도 <strong>팀원들과의 소통</strong>이 0순위 아닐까 생각한다. 위의 내용은 한창 프로젝트가 달려나가는 도중의 이야기이다. 관리 방식이 잘 잡혀 있다면 안정적으로 뛰는 데 도움이 되지만, 서로 목표를 공유하고 이해하는 팀원들의 존재도 그 못지 않게 강력할 것이다! 이번 프로젝트의 경우에는 시작하기 전 <strong>각자가 이 프로젝트에서 이루고 싶은 목표</strong>에 대해 이야기하고 함께 그라운드 룰을 만드는 과정을 거쳤는데, 여기에서 우리 팀만의 시스템이 시작된다고 느꼈다. 좋은 팀원들을 잘 만난 덕도 컸지만, 이렇게 시작한 팀이기에 열심히 달리는 과정이 즐거웠던 것 같다. 👟 </p>
<p>애자일 입문자의 소소한 기록이었지만... 이 글을 읽는 분들의 플젝과 협업에 작게나마 도움이 되었으면 한다 🍀🪽</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024 팀 네이버 컨퍼런스: DAN 24 방문기 ⚡️]]></title>
            <link>https://velog.io/@eujin-shin/2024-%ED%8C%80-%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%BB%A8%ED%8D%BC%EB%9F%B0%EC%8A%A4-DAN-24-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@eujin-shin/2024-%ED%8C%80-%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%BB%A8%ED%8D%BC%EB%9F%B0%EC%8A%A4-DAN-24-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sat, 23 Nov 2024 14:01:31 GMT</pubDate>
            <description><![CDATA[<h3 id="첫-컨퍼런스-방문-🎉">첫 컨퍼런스 방문 🎉</h3>
<p>네이버 컨퍼런스인 DAN 24: day 1에 우연히 참가 신청을 성공해서 다녀왔다! ✌️ 신청하는 날 마침 몸이 좀 안 좋아서 늦게 출근했는데 팀원이 알려 줘서 선릉역 나가다가 후다닥 참가 신청... 🙄 그간 추첨제로 운영하는 컨퍼런스들을 간간이 신청해 본 적은 있지만 한 번도 당첨된 적이 없었는데, 이번 네이버 컨퍼런스는 선착순으로 당첨되어서 방문할 수 있게 되었다 ㅎ_ㅎ</p>
<p>사실 컨퍼런스 당일은(11/11) 최종 평가 앞두고 바쁜 시기이기도 했고, 마침 또 코엑스에서 하다 보니 지하철 한 정거장 거리라! 당일에도 오전엔 정신없이 이것저것 하다가 진짜 후다닥 갔다온 것 같긴 하다... ㅋㅋㅋㅋ</p>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/21e962ff-73f6-4c07-a9e3-94e1b9ead620/image.jpeg" alt=""></p>
<p>한창 세션 진행 중이던 세 시쯤 도착! 네이버페이 어플 월렛으로 NFT 티켓을 발급받아 입장했다. </p>
<h3 id="세션-듣고-난-뒤의-생각들-💭">세션 듣고 난 뒤의 생각들... 💭</h3>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/a2105d5a-39b8-4355-9cf9-8b863c07a266/image.jpeg" alt=""></p>
<p>여유가 있었다면 이것저것 들어 봐도 재밌었을 것 같지만... 👀 네이버 검색 FE 개발 시스템, 지표 기반 웹 성능 최적화 두 개 듣고 왔다! <em>(부스는 중간 쉬는 시간에 잽싸게 구경했다 ^\</em>^)_</p>
<p>인상적이었던 내용은 <strong>&quot;성능 지표는 데이터 기반 의사 결정의 근거가 되어 준다&quot;</strong>던 이야기, 그리고 <strong>&quot;대규모 조직의 일하는 방법&quot;</strong>. </p>
<p>마침 최근에 이미지 관련 최적화를 할 일이 있었는데, 현재는 케이스 자체가 그렇게 많지 않기도 하고 명확한 기준은 없이 그냥 &#39;로드 시간이 줄어들면 좋은 거지...!&#39; 정도의 느낌만 갖고 있다가 세션으로 네이버 검색팀의 주요 지표나 기준점이 언급되어서 더 재밌게 들었던 것 같다. </p>
<blockquote>
<p><strong>기억에 남는 내용</strong> ⚡️</p>
</blockquote>
<ul>
<li>성능 지표의 기준을 설정하고, 거기 맞춘 서비스를 안정적으로 제공하기 위한 노력</li>
<li>위와 같은 기준점과 지표 확보가 잘 되고 있을 때, 의사결정 상황에서 각 방안의 &#39;성능 비용&#39;을 고려해 결정을 내릴 수 있다</li>
</ul>
<p>또 지금은 매우 소규모 조직에서 프로젝트를 진행하면서, 나 한 사람이 프론트엔드 레포의 거의 모든 코드를 작성했고 전체 운영도 셋이서 나눠 하고 있는데... 이런 상황에서 본 큰 조직의 시스템이 신기하고 새롭기도 했다. 큰 규모로 큰 서비스를 만드는 조직의 고민들을 조금이나마 들여다본 느낌?!</p>
<blockquote>
<p><strong>기억에 남는 내용</strong> ⚡️</p>
</blockquote>
<ul>
<li>대규모 조직에서 구성원들이 어떻게 조직에 잘 녹아들고 각자의 자리에서 책임을 다할 수 있게 하려면 어떤 시스템을 구축해야 할까</li>
</ul>
<p>지금의 환경에서는 지금의 즐거움이 있지만, 이 환경 속에서도 특히 우리 팀이 갖춘 &quot;일하는 시스템&quot;이 안정감을 줬던 만큼 저렇게 체계화된 (체계화를 위해 노력하는) 팀에서 협업하는 것은 또 새로운 즐거움으로 다가올 것 같다는 생각도 들었다.</p>
<p>Google Solutions Challenge 참가 당시 데모 데이에 방문했을 때 (그 당시에는 엄청 실력이 부족했던 상태라 다른 팀들 플젝 보면서 기도 많이 죽었지만... 🤣) 늘 내가 하는 방식이 정답이 아닐 텐데, 이대로 고착화되어 가는 것에 대한 걱정을 알게 모르게 하다가 그런 네트워킹과 공유의 기회가 환기가 된다고 느꼈었다. 이번 DAN 24에서도 비슷한 느낌을 받은 듯!</p>
<p>시스템은 안정적인 조직을 만들어 주기도 하지만 또 지속적인 노력이 없다면 하던 틀을 벗어나기 쉽지 않게 만들기도 할 것 같은데, 다른 시스템과 다른 환경에 대해 계속해서 눈이 틔여 있는 게 참 좋은 자극이 되고 발전의 기회를 열어 주는 것 같다.</p>
<p>첫 컨퍼런스였는데 좋은 행사에 방문하게 되어 이런저런 배움의 기회가 됐던 것 같다!</p>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/c8f81219-721d-430d-b09b-65c2005f63d4/image.jpeg" alt=""></p>
<p>(+ 참가 선물로 담요 받았다 😆)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 서비스에서 튜토리얼 기능 구현하고, 진짜 사용자 확보하기 🧩]]></title>
            <link>https://velog.io/@eujin-shin/%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4%EC%97%90%EC%84%9C-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B3%A0-%EC%A7%84%EC%A7%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%ED%99%95%EB%B3%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@eujin-shin/%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4%EC%97%90%EC%84%9C-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B3%A0-%EC%A7%84%EC%A7%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%ED%99%95%EB%B3%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 08 Nov 2024 15:01:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/eujin-shin/post/0ea1462d-7bef-470a-8703-07a8293ab1f3/image.png" alt=""></p>
<h2 id="미션-방금-가입한-사용자가-계속-서비스를-사용하게-하자">미션: 방금 가입한 사용자가 계속 서비스를 사용하게 하자!</h2>
<p>현재 개발하고 있는 <a href="https://www.freebe.co.kr/">프리비</a>는 프리랜서 사진 작가를 위한 예약 관리 도구다. 사용자가 이 서비스를 정말 자신의 업무에서 활용하려면, 우선 우리가 기획 단계에서 설계한 대로 자신의 촬영 상품을 등록하고 프로필을 설정하는 등의 세팅을 해 줘야 했다. 사실상 서비스를 통해 편리함을 얻기 전에 <strong>해야 할 일</strong>부터 생기는 셈이다. </p>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/ffa3a540-0012-4776-8b14-698e01a9a02d/image.png" alt="">
<em>(초기 메인 페이지 UI는 위와 같았다. 처음 가입한 사용자가 이런 페이지를 제일 먼저 마주치면 과연 계속해서 서비스를 사용할 수 있을까 🥹)</em></p>
<p>사용자가 우리의 의도대로 움직이게 하려면 어떻게 할 수 있을까? 처음 가입한 이후 무엇부터 해야 할지를 알려 줄 필요도 있고, 서비스를 통해 얻게 될 기대 효과를 상기시켜 줄 수도 있을 것이다. 우선 전자가 존재하지 않는 건 가입 이후의 사용자 유지를 어렵게 만들 요인으로 보였기 때문에, 서비스 튜토리얼을 통해 세팅 방법을 좀 더 쉽게 소개하기로 했다.</p>
<h2 id="튜토리얼로-더-친절한-서비스를-만들어-보자">튜토리얼로 더 친절한 서비스를 만들어 보자</h2>
<h3 id="0-메인-페이지에서-기능까지의-접근성-높이기">0. 메인 페이지에서 기능까지의 접근성 높이기</h3>
<p>우선 튜토리얼이 진행될 메인 페이지의 UI를 개선했다. 메인 페이지에서 접근할 수 있는 기능을 한눈에 보여주고, 튜토리얼을 통해 각각의 기능을 어떤 순서로, 어떻게 사용해야 할지 설명하고자 했다. </p>
<p>기존에 메인 페이지에서 기능에 접근하려면 헤더의 메뉴를 열어야 했고, 마이페이지에 들어갔을 때만 사이드 메뉴로 각각의 탭을 보여주고 있었다. (이렇다 보니 메인 페이지가 더 허전해 보이기도 했다 😂) 모든 페이지에서 사이드 메뉴를 통해 이동할 수 있고, 필요할 때는 메뉴를 닫아 작업 영역을 넓힐 수 있는 방식으로 수정했다. 
<img src="https://velog.velcdn.com/images/eujin-shin/post/15f0633f-7d77-4b2c-96df-8fd12413248a/image.png" alt="">
<em>(수정된 현재 UI. 아직 예약이 없어도 조금은 덜 비어 보인다!)</em></p>
<h3 id="1-튜토리얼-진행-상태-관리하기">1. 튜토리얼 진행 상태 관리하기</h3>
<blockquote>
<p>각 컴포넌트에서 지금 튜토리얼을 띄워 줘야 하는지, 튜토리얼의 어느 단계에 위치해 있는지를 어떻게 공유할 수 있을까?</p>
</blockquote>
<p>전역 상태 관리가 가장 먼저 떠올랐지만, 현재 프로젝트에서 tanstack query와 react-hook-form의 useFormContext를 쓰다 보니 전역 상태의 필요성이 없었어서 이걸 위해 도입하기에는 고민이 됐다. 구현하다 보니 튜토리얼 단계는 하나의 컴포넌트에서 관리해도 문제가 없어서, url에 search parameter를 추가해 튜토리얼 진행 여부만 구별하기로 했다. 클라이언트 컴포넌트일 경우 Next.js의 <code>useSearchParam</code>을 사용해 확인이 가능하다. 이 경우 가입 직후 이동하는 메인 페이지 url에 파라미터를 추가해 주기만 하면 되고, 사용자가 직접 &#39;튜토리얼 보기&#39; 버튼을 눌러 튜토리얼을 시작했을 때도 동일한 url로 이동시켜 주기만 하면 된다.</p>
<p>처음에는 진행 여부를 알아야 하는 각 컴포넌트에서(크게는 튜토리얼 진행시 열려 있어야 하는 사이드 메뉴와, 실제 데이터 대신 더미 데이터를 보여 줘야 하는 신청 목록이 있었다) 개별적으로 파라미터를 확인해 진행 상태를 추적하려 했지만, 관리 포인트가 지나치게 늘어나는 문제가 있었다. 또 기존에 이용하던 다른 파라미터에 대한 로직과 충돌이 생길 수도 있는 상황이었다.</p>
<p>이 문제를 해결하기 위해서는 다음과 같이 접근 방식을 수정했다.</p>
<ul>
<li>우선, <strong>사이드 메뉴 레이아웃</strong>을 메인 페이지를 제외한 다른 페이지에만 적용하고 메인 페이지에서는 레이아웃 대신 페이지에서 직접 하위 컴포넌트로 삽입했다.</li>
<li>메인 페이지에서 각각의 파라미터를 확인하는 로직을 하나로 만들고, 여기에서 확인된 튜토리얼 진행 여부는 props로 하위 컴포넌트에 전달한다.</li>
</ul>
<blockquote>
<p>이 방식은 <strong>쿼리 파라미터를 통한 상태 관리</strong>와 <strong>상태 전달 방식</strong>에 대해 고려할 때 유용하다. 특히, 페이지 간 상태 공유가 필요한 경우, 중앙에서 관리하여 상태 변경의 일관성을 유지하는 것이 관리 효율성을 높인다.</p>
</blockquote>
<h3 id="2-특정-요소-하이라이트하기">2. 특정 요소 하이라이트하기</h3>
<p>튜토리얼 과정에서 단순히 텍스트나 이미지를 보여 줘서 기능을 소개할 수도 있겠지만, 기왕이면 실제로 페이지 위에 각 영역을 강조해서 보여 주면 더 좋을 것이다. 나는 튜토리얼에서 원래 쓰고 있던 <a href="https://mantine.dev/">@Mantine</a>의 모달을 사용했는데, 이렇게 하면 페이지 위에 오버레이, 오버레이 위에 모달이 올라가는 UI가 만들어진다. 여기서 특정 영역만 강조하는 방법은 두 가지가 떠올랐다.</p>
<ul>
<li><strong>특정 요소를 오버레이 위로 올리기</strong>: z-index를 활용해 특정 요소가 오버레이보다 앞에 오도록 설정하는 방법</li>
<li><strong>오버레이를 특정 요소를 제외하고 덮기</strong>: <a href="https://developer.mozilla.org/ko/docs/Web/CSS/mask">mask</a> 속성을 사용해 오버레이 영역을 일부분만 적용하는 방법</li>
</ul>
<p>사실 처음에는 z-index를 사용하면 쉽게 구현할 수 있을 것으로 예상했지만, 모달이 포탈(portal)에서 렌더링되어 기존 DOM 계층을 벗어나기 때문에 이 방법은 불가능했다. 🥲 </p>
<blockquote>
<p>포탈은 React에서 모달을 구현할 때 자주 사용된다. 포탈은 DOM 구조 외부에 위치하여 모달이 다른 요소에 의해 가려지지 않도록 도와주지만, 계층 구조의 제약이 발생할 수 있다.</p>
</blockquote>
<p>그 외에도 z-index는 상위 컴포넌트에서부터 위계적으로 적용되니, 첫 번째 방법으로 구현하려면 강조해야 할 각각의 컴포넌트와 오버레이 컴포넌트가 모두 형제 컴포넌트 관계에 있어야만 할 것 같다. 결국 두 번째 방법을 통해 제외시켜야 할 영역을 오버레이 컴포넌트에 직접 지정했다. <code>mask-composite</code> 속성을 <code>add</code>로 두고, <code>mask-image</code>를 다음과 같이 설정하면 원하는 위치에 사각형으로 오버레이를 뚫을 수 있었다.</p>
<pre><code>linear-gradient(to right, 
black ${maskX.start}px, 
transparent ${maskX.start}px ${maskX.end}px, 
black ${maskX.end}px), 
linear-gradient(black ${maskY.start}px, 
transparent ${maskY.start}px ${maskY.end}px, 
black ${maskY.end}px)</code></pre><p>오버레이에 전달할 CSS 속성 하나만 수정하면 되고, 영역을 완전히 원하는 대로 조정할 수 있어서 좋기도 했지만...! 직접 컴포넌트를 기준으로 하는 게 아니라, 각 컴포넌트가 위치하게 될 좌표를 기준점으로 삼으면서 한계를 동반하기도 했다. 바로 이걸로 인한 추가적인 문제가 다음에... 👇</p>
<h3 id="3-튜토리얼-시-스크롤-조정하기">3. 튜토리얼 시 스크롤 조정하기</h3>
<p>강조할 요소가 오버레이에서 제외하는 위치에 알맞게 보이게 하기 위해서는 튜토리얼 시작 시 무조건 스크롤을 최상단으로 조정해야 하는 이슈도 발생했다. 튜토리얼이 트리거될 때 <code>scrollTop</code>을 활용하여 스크롤을 최상단으로 이동시키려고 했지만, 일부 상황에서는 정확히 최상단으로 이동하지 않는 문제가 있었다. 고정된 헤더가 있어 위치가 맞지 않았던 것이 원인이었다.</p>
<p>이 문제는 CSS의 <code>scroll-margin-top</code> 속성을 활용해 해결할 수 있었다. 고정된 헤더가 있을 때 적절한 마진을 설정해 줌으로써, 튜토리얼에 따라 스크롤 위치를 조정해도 요소가 제대로 강조되는 화면 구성을 만들 수 있었다.</p>
<blockquote>
<p><strong>scroll-margin-top</strong>은 특히 고정 헤더나 상단에 공간이 필요한 페이지 디자인에서 유용한 속성이다. 이 속성은 스크롤로 특정 요소가 스크롤 위치에 맞춰 노출될 때 추가적인 여백을 줘서 정확한 위치 조정을 가능하게 한다.</p>
</blockquote>
<hr>
<p>요런 시행착오를 거쳐 결과적으로는 다음과 같이 튜토리얼이 완성됐다! 👏</p>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/22098c86-16a3-44e6-95fa-aaa1ccbe7934/image.gif" alt=""></p>
<p>구현하는 동안 많이 헤매기도 했지만 완성해 놓고 보니 마음에 들게 잘 나오기도 했고 나름 재미있었다 😆 이제 이 튜토리얼이 우리 서비스를 더 많이 사용하게 해 줬으면... 🙏</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js로 프로젝트 시작하기 ⚡️]]></title>
            <link>https://velog.io/@eujin-shin/Next.js-14%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-App-router</link>
            <guid>https://velog.io/@eujin-shin/Next.js-14%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-App-router</guid>
            <pubDate>Sat, 27 Jul 2024 15:12:58 GMT</pubDate>
            <description><![CDATA[<p>이번 프로젝트에서 Next.js를 사용하게 되면서 세팅을 위해 관련 개념을 이것저것 찾아보고 이해하기 위해 노력했다. 처음 사용해 보는 것은 아니지만 그동안 이렇게 제대로 써 보려고 시도한 적이 없기도 했고, 그 사이에 달라진 것도 많아서 공부할 주제가 많았다.</p>
<p>공부한 내용을 정리하며, Next.js가 무엇이고 왜 쓰는지에 대한 이야기를 담아 보려고 한다 😆</p>
<h3 id="nextjs란"><a href="https://nextjs.org/">Next.js</a>란?</h3>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/10f44d2f-bbe2-4612-bddd-cd30af3b658f/image.png" alt=""></p>
<p>Next.js는 React 기반의 웹 어플리케이션 프레임워크이다. 이 말을 이해하기 위해서는 우선 &#39;프레임워크&#39;가 무엇인지 알 필요가 있다. </p>
<blockquote>
<p>💡 <strong>프레임워크와 라이브러리 구분하기</strong></p>
</blockquote>
<p>이 둘은 <strong>제어를 어디서 주도하느냐</strong>에 따라 구분할 수 있다. </p>
<p>라이브러리를 사용할 경우 <strong>개발자가</strong> 자신의 코드 내에서 라이브러리를 호출해 그 안에 정의된 요소를 활용한다. React는 JavaScript로 UI 컴포넌트를 구축할 수 있게 해 주는 라이브러리이다. 나의 의도대로 가져다 사용하는 공구상자 같은 느낌이다.</p>
<p>프레임워크를 사용할 경우 <strong>프레임워크가</strong> 정해진 뼈대를 제공함으로써 제어를 주도하고, 개발자는 프레임워크에 맞춰 자신의 코드를 작성한다. 이쪽은 프로젝트를 위한 가이드, 또는 설계도에 빗댈 수 있다.</p>
<p>즉, 개발자는 React라는 라이브러리를 내 코드에서 호출해 그 라이브러리에 구현되어 있는 요소를 컴포넌트 구현에 활용할 수 있다. 그리고 여기에 Next.js라는 프레임워크를 적용한다면, Next.js에서 제시하는 설계도에 맞추어 코드를 구현하게 될 것이다. </p>
<p>그럼 우리가 Next.js의 설계도를 따를 때 얻을 수 있는 이점은 뭘까? 개발자는 이 설계도에 맞춰 자신의 코드를 작성함으로써, Next.js에서 제공하는 기능을 사용할 수 있게 된다. 예를 들어 초기에 Next.js의 정체성은 SSR (Server Side Rendering) 중심이었는데...</p>
<blockquote>
<p>⚙️ <strong>Next.js</strong>: 이 설계도대로 만들면 요 부분은 내가 알아서 서버 측에서 렌더링되게 할게~
🧑‍💻 <strong>개발자</strong>: ㅇㅋ 그렇게 만들게~</p>
</blockquote>
<p>프레임워크를 사용하는 건 (잘 정리된 설계도는 그 자체로도 도움이 되지만) 이런 느낌이라고 생각해 볼 수 있겠다.</p>
<h3 id="nextjs-최신-버전의-특징">Next.js 최신 버전의 특징</h3>
<p>사실 현 시점에서 React 프로젝트를 구성하고자 하면 거의 필수적으로 Next.js를 거치게 될 것이다. 이전에는 create-react-app을 사용해 별도의 프레임워크를 끼우지 않은 프로젝트를 생성하는 방식을 사용하기도 했지만, 이제는 React 공식문서에서 프레임워크를 사용하는 걸 권장하고 있다. 그리고 그 프레임워크로 가장 먼저 제시되는 것이 Next.js이다.  </p>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/858d248f-573a-4b5b-b5cc-b3fc7722d37a/image.png" alt=""></p>
<p>현재 Next.js 기반 프로젝트가 갖는 대표적인 특징은 다음과 같다.</p>
<ul>
<li>라우팅: 개발자가 직접 라우터를 구성하지 않아도 <strong>프로젝트의 폴더 구조에 기반한 라우팅 시스템</strong>을 제공한다.</li>
<li>렌더링: Client Side Rendering, Server Side Rendering, Static Site Generation을 필요에 따라 구분해서 사용 가능하다.</li>
<li>최적화: 이미지, 폰트와 같은 리소스를 최적화해 성능을 향상시킨다.</li>
<li>데이터 호출: fetch API를 확장해 자체적으로 데이터 캐싱과 같은 기능을 포함한다.</li>
<li>API Routes: API 엔드포인트를 구성해 프론트엔드뿐 아니라 간단한 백엔드 기능까지는 풀스택으로 구현할 수 있다.</li>
</ul>
<p>이제 Next.js는 단지 SSR을 사용하기 위한 프레임워크가 아닌 셈이다. 다음 글에서는 이 중에서도 Next.js가 제공하는 라우팅 시스템에 대해 더 정리해 볼 예정이다.</p>
<hr>
<p><em>읽어 주셔서 감사합니다. 내용에 부족한 부분이 있다면 댓글로 조언해 주세요! 🤍</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 9월 2주 ] 주간 회고 〰]]></title>
            <link>https://velog.io/@eujin-shin/9%EC%9B%94-2%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0-%E3%80%B0</link>
            <guid>https://velog.io/@eujin-shin/9%EC%9B%94-2%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0-%E3%80%B0</guid>
            <pubDate>Tue, 19 Sep 2023 07:00:55 GMT</pubDate>
            <description><![CDATA[<h3 id="이번-주에는">이번 주에는?</h3>
<ul>
<li>데이터 로딩 중 🌀</li>
</ul>
<p>원래는 무한 스크롤을 적용해 보고 싶었지만 정말 사용자 입장에서만 알고 개발자 입장에서는 처음 공부해 본 거라 😅 (페이지네이션 / 무한 스크롤이 별도의 개념이라는 것도 처음 알았다) 이걸 하려면 프론트엔드뿐 아니라 백엔드 측에서도 구현을 해 줘야 한다는 점을 확인하고 일단은 해당 사실 회의에서 공유한 뒤 보류. </p>
<p>당장은 페칭하는 동안 스피너 애니메이션이 돌아가도록 해 두었다! gif 사용하는 방법도 봤는데, html으로 모양 만든 뒤 CSS 애니메이션으로 회전시키는 방법 선택.</p>
<ul>
<li>JS Date 객체 사용하기: <code>new Date()</code></li>
</ul>
<p>날짜를 사용해야 하는 기능(랭킹 집계 기간 표시, 플래너 날짜 선택)이 좀 있는데, 일단은 라이브러리 없이 Date 객체로 구현해 뒀다. </p>
<p>→ 기간 표시하기: 현재 날짜 정보를 가져오고, 사용자가 선택한 기간(월간, 주간, 전체)에 따라 표시해야 할 값을 계산하기로 했다. </p>
<p>월간은 <code>getMonth()</code> 쓰면 간단하지만 주간을 어떻게 <code>월요일 날짜 ~ 일요일 날짜</code>로 표시해야 하나 싶었는데, <code>getDay()</code>를 쓰면 요일 값을 숫자로 받아올 수 있고 현재 날짜와 요일 값을 기반으로 <code>setDate()</code>를 사용해 월요일 날짜와 일요일 날짜를 구할 수 있었음! 심지어 계산할 때 지난 달 / 다음 달로 넘어가면 자동으로 넘겨 준다...!!! 👍</p>
<p>→ 날짜 선택 옵션(다음 날 / 전날 선택하기): 이건 현재 시점의 날짜가 아니라 현재 선택된 날짜에서 전날, 다음 날을 골라야 하는 거라 날짜 정보를 <code>useState</code> 사용해서 state로 관리했다. </p>
<p>위의 방식을 참고해서 setState 써 주면 될 줄 알았는데... 예상 밖의 복병이 있었음. 일단 날짜 객체를 그대로 state에 넣어 두니 <code>setDate()</code> 메소드를 사용해도 state 변경을 인식하지 못하는 것 같았다. (리렌더링 안 됨) 다른 자료를 참고해 <code>{date: &lt;Date 객체&gt;]}</code> 같은 식으로... 객체로 한 번 더 감싸 주어서 이 부분은 해결. </p>
<p>그런데 setState에서 prev 값을 가져와 +1 / -1 로 setDate를 해 줬더니... 왜인지 두 번째로 setState가 일어날 때부터 날짜가 2일씩 움직이는 현상 발생 🥺 이건 아직 해결을 못했다. 방법 찾는 중.</p>
<p>→ 날짜 선택 옵션(자유 선택): 캘린더 모달을 사용해 원하는 날짜로 바로 이동하는 기능도 넣으려고 하는데, 이 부분은 라이브러리를 사용할 예정.</p>
<ul>
<li>리액트 네이티브 적응기</li>
</ul>
<p>stack navigator 공부해서 여차저차 집어 넣... 은 줄 알았는데 화면이 안 보이는 문제가 생겼다. 기존에 되어 있는 구조가 <code>탭 &gt; 폼 작성 버튼 누르면 모달 on &gt; 모달에서 옵션 선택해서 폼 작성 탭으로 이동</code> 이런 형태였고, 지금 폼 작성을 단계별 화면으로 쪼개는 게 내가 담당한 파트였음. 그렇다 보니 여기서 스택을 넣으면 탭 &gt; 모달 &gt; 탭 &gt; 스택... 이런 식이 되는 거라 ㅠㅠㅋㅋ 결국 스택을 빼고, 단계별 화면을 모두 각각의 탭으로 작성해서 사용하기로 했음.</p>
<p>화면 하나씩 퍼블리싱하다가 보니 <code>&lt;View&gt;</code> 태그를 제대로 안 써서 위의 문제가 생겼던 건가 싶기도 하고....</p>
<ul>
<li>Postman</li>
</ul>
<p>프론트 측에서 api를 테스트 해 볼 때 사용 가능.</p>
<br>

<h3 id="셀프-피드백">셀프 피드백</h3>
<p>→ 리액트 네이티브는 완전 초반 적응기는 넘긴 듯! 리액트랑 다른 개념이 은근히 있는 듯해서 앞으로도 이게 왜 안 돼?! 하면서 열심히 찾아봐야 할 것 같긴 하다... ㅋㅋㅋ
<br></p>
<h3 id="다음-주-목표-💡">다음 주 목표 💡</h3>
<ul>
<li><code>~ 수요일</code>
두 번째 form 화면 구현
업데이트 내역 pull 받고 나도 pull request 보내기</li>
<li><code>~ 주말</code>
플래너 기능 완성 (+ 오류 수정 포함)
회원가입 추가 필드 정리
홈 화면, 위치 인증 파트 넘겨받아 진행</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 9월 1주 ] 주간 회고 👟]]></title>
            <link>https://velog.io/@eujin-shin/9%EC%9B%94-1%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@eujin-shin/9%EC%9B%94-1%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 11 Sep 2023 03:13:46 GMT</pubDate>
            <description><![CDATA[<h3 id="이번-주에는">이번 주에는?</h3>
<ul>
<li>React Native 환경 설정</li>
</ul>
<p>MacOS 기준 안드로이드 개발에 안드로이드 스튜디오, iOS 개발에 Xcode 등이 필요하다. 공유받은 자료 참조해서 따라하는데 용량 문제로 전에 밀어 버렸던 Xcode를 다시 설치해야 돼서 ㅠㅠㅋㅋ 디스크 용량 자체가 부족한 탓에 좀 골치가 아팠다. (SSD 교체까지 알아봄... M1 맥북은 불가하다고 🫠) 결과적으로는 재부팅 이후 시스템 데이터 줄어들었을 때 설치하여 해... 결? </p>
<ul>
<li>React Native Navigator/Stack</li>
</ul>
<p>어플에서 연속된 화면 이동에 사용 가능한 네비게이터. 파라미터 전달이 가능함. </p>
<ul>
<li>페이지 옵션(기간 / 장소) 선택에 따른 리렌더링 로직 구현</li>
</ul>
<p>페이지 디자인상 <code>&lt;select&gt;</code> 태그를 사용하기에는 한계가 있어서,</p>
<blockquote>
<p>옵션을 배열 구조로 관리
→ 옵션 array 사용해 컴포넌트 생성 
→ 옵션에 대응되는 값을 파라미터로 담아 클릭 이벤트 핸들러 연결 
→ 옵션 선택시 파라미터를 사용해 setState 발생하게 함</p>
</blockquote>
<p>이런 식으로 구현해 봤다. 이제 api만 연결하면 동작하게 할 수 있을 듯...!</p>
<br>

<h3 id="셀프-피드백">셀프 피드백</h3>
<p>→ 기존 프로젝트 타임라인을 맞추기 위해 열심히 하고... 는 있는데 조금 빠듯할 것 같기도 하다 ㅋㅋ ㅠㅠ 백엔드 정리된 이후에 api 작업 들어가는 게 편할 것 같아서,,, 좀 미루고 있었는데 테스트는 미뤄두더라도 일단 코드 작성이라도 해 봐야겠다.</p>
<p>→ 리액트 네이티브를 시작하게 돼서 공부할 것도, 작업할 것도 새로 생겼으니 시간 배분에 좀 더 신경을 써야겠음.
<br></p>
<h3 id="다음-주-솔루션-💡">다음 주 솔루션 💡</h3>
<ul>
<li><code>~ 월요일 회의</code> 
날짜 데이터 형식 확인
회원가입, 랭킹 조회 api 작업</li>
<li><code>~ 수요일 세션</code>
stack navigator container (전체적 구조?) 구현</li>
<li><code>~ 주말</code>
하단 메뉴 마무리
플래너: 디자인 불확실한 부분은 임시로라도 구현...? 회의에서 논의해 보기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 8월 5주 ] 주간 회고 🍀]]></title>
            <link>https://velog.io/@eujin-shin/8%EC%9B%94-5%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@eujin-shin/8%EC%9B%94-5%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 05 Sep 2023 12:34:44 GMT</pubDate>
            <description><![CDATA[<h3 id="이번-주에는">이번 주에는?</h3>
<ul>
<li>기존 프로젝트 퍼블리싱 작업 막바지</li>
<li>github 더 깊이 알아가기 💥
(커밋 메시지 컨벤션, 커밋 단위, 이슈, 커밋 로그 정리 + vim 에디터 등)</li>
<li>신규 프로젝트 합류, React Native 환경 설정</li>
</ul>
<br>

<h3 id="셀프-피드백">셀프 피드백</h3>
<p>→ 스타일링에 많이 익숙해진 걸 느꼈다. flex 사용, footer 배치 등도 이번 주에 완료함!</p>
<p>→ 개강했더니 체력이 딸려서 (...) 주간 회고에 공을 들일 자신이 없어 배운 내용 정리는 패스 🥹 그나마 노션 TIL 작성에 조금 익숙해져서 그쪽에 작업 내용, 새롭게 찾아 본 것 등을 기록하고 있다.
<br></p>
<h3 id="다음-주-솔루션-💡">다음 주 솔루션 💡</h3>
<ul>
<li><p>학기 중에는 최대한 공강 시간을 활용해야겠다. 🔥 그래도 금요일을 제외하고는 낮 / 오후 수업 ~ 저녁 사이에 시간이 좀 뜨고...! 오히려 집에서 늘어지느라 흐지부지 보내는 날이 있던 방학에 비하면 조금 더 알차게 쓸 기회가 있을지도 (과제 많아지면 과연 . . .?)</p>
</li>
<li><p>기존 진행하던 프로젝트 팀의 전체적 일정을 재정비했고 가능하면 10월 초에 1차 배포를 목표로 하기로 했다. 다음 주에는 api 연결 좀 더 공부하고 FE 쪽 내부 로직부터 차근차근 구현해 봅시다!</p>
</li>
<li><p>새로운 팀에 합류하게 되어 개인 프로젝트는 잠시만 보류 . . .</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 8월 4주 ] 주간 회고 ⚙️]]></title>
            <link>https://velog.io/@eujin-shin/8%EC%9B%94-4%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@eujin-shin/8%EC%9B%94-4%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 29 Aug 2023 07:40:32 GMT</pubDate>
            <description><![CDATA[<h3 id="이번-주에는">이번 주에는?</h3>
<ul>
<li>브랜치 merge 이후 생긴 문제 해결...</li>
<li>백엔드 연동 &amp; 배포 관련 공부</li>
<li>Tailwind CSS 개인 프로젝트</li>
</ul>
<br>

<h3 id="새롭게-배운-것--배워야-할-것">새롭게 배운 것 &amp; 배워야 할 것</h3>
<ul>
<li>Tailwind CSS 사용하기 </li>
</ul>
<p>지난 주에 계획한 대로 Tailwind를 사용해서 개인 프로젝트를 시작했다. 공식 문서 살펴가면서 클래스 이름 익히는 중인데, 아직 사용자 정의 값으로 템플릿을 만드는 게 어색해서 임의 값 지정을 너무 많이 쓰는 게 조금 비효율적으로 느껴지기도... 🤔 또 기본적으로 inline 방식을 사용하게 되면서 스타일링 수정을 진행할 때 가독성이 떨어진다는 단점도 느꼈다. 처음 Tailwind를 접할 때는 styled component보다 더 나은 방식이 될 거라는 막연한 기대가 조금 있었던 것 같은데, 사용해 보니 확실히 장단이 있구나 싶음.</p>
<ul>
<li>React 빌드 후 화면 안 보임 문제 해결</li>
</ul>
<p>이 문제 때문에 (핑계.) 이번 주 개발이 많이 밀렸다 🥲🥲 버전 정리를 위해 다른 develop branch와 main에서 merge를 진행했는데, 그 이후 정리된 버전으로 pull 받고 나니 빌드했을 때 localhost:3000에서 작성한 컴포넌트가 보이지 않기 시작했다...!!!!</p>
<p>구글링하면서 &quot;리액트 안 보임&quot; &quot;리액트 흰 화면&quot; &quot;리액트 빌드 에러&quot; 등등으로 정말 많은 글을 찾아본 것 같은데... 내가 겪은 문제랑은 다 유형이 달라서 너무 막막하고 답답하게 느껴졌다. 내 경우에는 
→ 화면은 안 보이지만
→ 콘솔에 에러가 표시되지 않고 
→ 개발자 도구를 열면 코드는 정상적으로 보이고
→ 컴포넌트를 선택했을 때 화면에 컴포넌트 영역이 제대로 표시되기까지 하는
상태였음...</p>
<p>브랜치 병합 이후 생긴 문제이다 보니 처음에는 라이브러리 버전 문제 같은 걸 의심하다가, 코드가 제대로 개발자 도구에서 확인되고 테스트해 봤을 때 렌더링 자체는 문제가 없다는 생각이 들어 그제서야 스타일링 문제를 의심하기 시작했다. 결국 이런저런 시도 끝에 페이지 컴포넌트의 가장 상위 요소에서 <code>position: absolute</code>를 빼고 나니 해결되었음 😂 조금 허무...</p>
<p>이 부분 코드는 변경되지도 않았는데 왜 문제가 생긴 건지는 <del>그동안 밀린 태스크 따라잡아야 해서</del> 아직 정확히 파악하지 못했다. 더 상위 요소에서 뭔가 스타일링 요소가 변경되었던 걸까...?</p>
<ul>
<li>백엔드 api에 대해</li>
</ul>
<p>개발 못하는 동안 (ㅋㅋ ㅠㅠ) 이론적인 부분을 좀 공부했었다. 테스트 - 배포에 대한 지식이 아직도 많이 부족하기도 하고, 프로젝트 초~중반쯤 백엔드 태스크를 작은 것이나마 받아서 수행해 봤을 때 프론트엔드이더라도 백엔드에 대한 기본적 이해가 있는 게 유리하다는 생각이 많이 들었음.</p>
<p>→ RESTful = REpresentational State Transfer
→ API = Application Programming Interface</p>
<p>api는 &#39;프론트엔드(웹 브라우저) - 백엔드(데이터베이스) 간 통신을 가능하게 하는 인터페이스&#39;로 이해했다. 데이터베이스에는 필요한 데이터가 저장되어 있고, api를 통해 그 상태를 전달하거나 수정, 삭제할 수 있는 것.</p>
<p>api를 정의하는 요소에는
→ URI (what. 어떤 리소스에 대한 통신인가?)
→ HTTP 메소드 (how. 리소스를 어떻게 할 것인가?)
가 포함.</p>
<ul>
<li>ps) 세그먼트 트리</li>
</ul>
<p>백준에서 세그먼트 트리 문제를 하나 풀면서 복습도 좀 했다. 세그먼트 트리의 각 노드는 배열의 특정 범위에 대응됨. 루트에서 멀리 떨어진 (더 깊은) 노드일수록 배열에서 더 좁은 범위에 대응되며, 배열의 특정 부분에 대한 정보를 구하거나 수정할 때 log n의 시간복잡도를 가진다.</p>
<br>

<h3 id="셀프-피드백">셀프 피드백</h3>
<p>→ 문제 생기고 나서 구글링만 계속 하며 막막해 했었는데 진작 코드 수정해가면서 테스트해 볼 생각을 했으면 훨씬 빨리 해결했을 것 같다. 회피하지 말자... 마주하고 해결하자...... 🫠</p>
<p>→ 지난 주 피드백에 따라 TIL 작성을 꾸준히 한 것은 좋았다. 매일 작성하니 이날 뭘 했는지 요약이 되고, 동기부여도 되는 느낌!</p>
<br>

<h3 id="다음-주-솔루션-💡">다음 주 솔루션 💡</h3>
<ul>
<li><p>일정 없는 날은 되도록 낮 이전에 학교 갔다가 저녁에 돌아오는 루틴으로 작업해 보기</p>
</li>
<li><p>다음 주 목표!
1) (지난 주에 마치지 못한) 새롭게 퍼블리싱한 페이지 완성, 로직 구현
2) api / 퍼블리싱 분리되어 있는 부분 정리하기
3) React + Tailwind 개인 프로젝트 퍼블리싱까지 완성해 보기 (어렵다면 반응형 빼고라도!)</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 8월 3주 ] 주간 회고 💭]]></title>
            <link>https://velog.io/@eujin-shin/8%EC%9B%94-3%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@eujin-shin/8%EC%9B%94-3%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 21 Aug 2023 07:02:20 GMT</pubDate>
            <description><![CDATA[<h3 id="이번-주에는">이번 주에는?</h3>
<ul>
<li>랭킹보드, 공간 정보 페이지 퍼블리싱</li>
<li>프론트엔드 파트 브랜치 merge</li>
<li>Tailwind CSS 공부 </li>
</ul>
<br>

<h3 id="새롭게-배운-것--배워야-할-것">새롭게 배운 것 &amp; 배워야 할 것</h3>
<ul>
<li>Tailwind CSS 사용하기 </li>
</ul>
<p>→ &quot;Utility-first CSS Framework&quot;. 각 속성에 대응되는 클래스명을 사용함으로써 HTML 요소를 인라인에서 스타일링할 수 있음.</p>
<p>컨셉은 어렵지 않은데, 제공되는 클래스를 쏙쏙 뽑아서 사용하는 타입이다 보니 기존 CSS 속성에 대응하는 클래스를 파악해야 해서 오히려 초반에는 어렵게 느껴지는 감이 있다 😬 얕고 (제법) 넓은 지식이 필요한 느낌...?!</p>
<p>공식 홈페이지에서 사용 가능한 <a href="https://play.tailwindcss.com/">tailwind play</a>가 tailwind에 익숙해지는 데 도움이 되는 것 같다. 관련 자료를 찾아보면서도 느꼈지만 다양한 클래스를 능숙하게 사용하는 레벨에 도달하려면 일단은 문서 뒤져가면서 직접 써 보는 게 도움이 될 것 같아서, 다음에 tailwind를 사용해 작은 개인 프로젝트를 해 보기로!</p>
<ul>
<li>추가로 공부해야 할 부분</li>
</ul>
<p>React 동적 라우팅 (내가 담당하는 부분에서는 라우팅 경로에서 parameter를 가져와 거기 맞는 정보값 / api를 사용하도록 해야 한다!)</p>
<p>프로젝트 배포에 관해 (프로젝트 초반에 백엔드 부분에서 작은 태스크를 담당했어서 나는 프론트 전문 - 백엔드 전문 사이의 연결 고리 (?) 같은 위치가 되었다. 그렇다 보니 프론트, 백 둘 다 내 로컬에 가져와 테스트를 하게 되었는데 여기서 버전 관리의 어려움이 생기는 바람에... 솔루션으로 백엔드에서 완료된 부분까지 먼저 배포를 하고 프론트에서 배포된 상태의 api를 가져다가 쓰는 방식이 언급되었는데, 내가 배포 경험도 없고 배포 방식에 대한 지식도 없다 보니 깔끔히 해결하기가 어렵다. 한 차례 더 확인해 보고 재논의!)</p>
<br>

<h3 id="셀프-피드백">셀프 피드백</h3>
<p>→ CSS 복습, 다른 프로젝트에서의 작업 방식 경험으로 조금 더 효율적이고 깔끔한 방식으로 개발을 하게 된 것 같다. (일단 퍼블리싱 할 때 CSS 레이아웃으로 인한 괴로움,,, 이 줄어들었고 컴포넌트도 구분해 가면서 구조를 잡을 수 있게 됨)</p>
<p>→ 학교 나가서 확실히 효율이 올라가는 것도 느꼈다... 🔥 사실 주 후반부에는 다른 일정이 많아서 아예 작업을 못 한 날도 있는데, (이번 주의 반성 포인트) 이번 주 태스크의 대부분은 학교 나갔던 며칠 안에 호로록 끝내고 왔다. </p>
<p>→ 이번 주에는 중간 기록이 소홀했고 상기했듯이 몰아서 작업 - 아예 휴일 &lt; 이런 식으로 중간에 텀이 뜨다 보니 태스크 복기하는 것 자체가 어려웠다. 원래는 노션에서 주간 회고 양식에 맞춰 생각날 때마다 메모하는 식으로 중간 기록을 만들었는데, 개발하고 나면 노션에 TIL을 짧게 작성하는 방식도 효과적일 듯.</p>
<br>

<h3 id="다음-주-솔루션-💡">다음 주 솔루션 💡</h3>
<ul>
<li><p>일정 없는 날은 되도록 낮 이전에 학교 갔다가 저녁에 돌아오는 루틴으로 작업해 보기</p>
</li>
<li><p>다음 주 목표!
1) 팀 회의 통해서 버전 관리, 배포/테스트 방식 다시 정리
2) 이후 새롭게 퍼블리싱한 페이지 완성, 로직 구현
3) 개강 전에 React + Tailwind 조합으로 가벼운 개인 프로젝트 (단어장 or 코드잇) 진행하기</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 8월 2주 ] 주간 회고 🍃]]></title>
            <link>https://velog.io/@eujin-shin/8%EC%9B%94-2%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@eujin-shin/8%EC%9B%94-2%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 13 Aug 2023 09:12:19 GMT</pubDate>
            <description><![CDATA[<h3 id="이번-주에는">이번 주에는?</h3>
<ul>
<li>프로젝트 진행
A: 백엔드 api 테스트, CSS 레이아웃 공부
B: Next.js 공부, 컴포넌트 구현</li>
</ul>
<br>

<h3 id="새롭게-배운-것--배워야-할-것">새롭게 배운 것 &amp; 배워야 할 것</h3>
<ul>
<li>Next.JS로 개발하기 </li>
</ul>
<p>→ 리액트와의 차이점?
프레임워크 vs 라이브러리. 보다 자율성이 강하고 직접 설정해 줘야 하는 부분이 더 많은 React에 비해 프레임워크인 Next.JS에서는 적절한 곳에 코드를 배치하면 프레임워크가 웹 페이지를 구성해 준다고 볼 수 있음</p>
<p>(ex. 라우팅의 경우, React는 별도 패키지를 이용해 라우팅을 개발자가 구현해야 하지만 Next.JS에서는 pages 폴더 안에 파일을 작성하기만 하면 해당 디렉토리 경로에 대응되는 페이지 경로로 접근이 가능해짐 
⇒ 단 Next.JS 최근 버전에서는 방식이 변경되었다고 함. 이번 프로젝트에서는 업데이트 시점에 걸쳐서 개발을 시작하게 되어서 우선 이전 방식에 맞춰 개발하기로)</p>
<ul>
<li>api 테스트</li>
</ul>
<p>내 로컬에서 db, 백엔드 서버, 프론트엔드 서버를 모두 돌려서 테스트를 진행해 봤다. 아직 예외 사항을 전부 커버하지는 못하지만... 일단 회원가입 페이지의 &quot;기본 기능&quot;은 프론트-백 모두 포함해서 제대로 구현된 것을 확인할 수 있었음 ✨</p>
<ul>
<li>CSS 레이아웃: 박스 모델 복습</li>
</ul>
<p>→ 키 포인트 ✔️</p>
<p>박스 모델은 display 속성을 기준으로 inline, block으로 구분 가능. 크게는 행 안에 존재하는 요소(+ width, height, padding 적용 안 됨)와 하나의 행을 차지하는 요소로 나뉜다. 또한 행 안에 위치하지만 block처럼 다루어지는 inline-block도 존재</p>
<p>박스 모델은 content - padding - border - margin으로 구성. 디폴트로는 박스 사이즈가 content부터 border를 포함하지만, box-sizing 속성을 content-box로 설정할 경우 높이와 너비는 content 사이즈에 적용.</p>
<p>외부 요소와의 간격을 결정하는 margin이 중첩될 경우 더 큰 값이 적용.</p>
<p>사실 예전에 한 번씩은 다 봤던 내용인데 나는 CSS가 왜 이렇게 머리에 안 들어오는지... </p>
<ul>
<li>컴포넌트 계층 만들기</li>
</ul>
<p>원래 진행 중인 프로젝트에서 컴포넌트를 쪼개지 못하고 하염없이 코드가 길고 더러워지는 게 마음의 짐이었는데, 새로 들어간 프로젝트에서는 다른 분들이 컴포넌트 쪼개는 걸 보면서 흉내내 볼 수 있었다. </p>
<p>중복되는 컴포넌트는 쪼개고, 하위 컴포넌트에서 매번 변경되어야 하는 내용은 props로 전달받고, 상위 컴포넌트에서 하위 컴포넌트를 불러와 사용하기! 이것도 머리로는 모르지 않았지만 실제적 스킬이 부족했는데 확실히 좋은 사례를 보고 따라갈 수 있으니 좋은 경험이 되는 것 같다는 생각도 해 봄. (물론 구글링하면 다 나오지만 팀 내에 참고할 코드가 있는 건 확실히 달랐다,,,)</p>
<br>

<h3 id="셀프-피드백">셀프 피드백</h3>
<p>→ 이번 주는 뭔가 좋았던 것도 나빴던 것도 딱히 없는 느낌... 무난하게 하려던 걸 한 정도</p>
<p>+ 이 글 쓰다 보니 같은 태스크를 너무 오래 붙잡고 있나 (너무 쉬엄쉬엄하나) 싶기도... 🥲 조급해하려면 끝도 없고 정신건강에도 안 좋긴 하지만 적정선에서 고민해 봐야겠다 💪</p>
<br>

<h3 id="다음-주-솔루션-💡">다음 주 솔루션 💡</h3>
<ul>
<li><p>본가에서 돌아왔으니 다음 주부터는 학교에 나가서 일과를 해결하고 오기로!</p>
</li>
<li><p>다음 주 목표!
원래 잡고 있던 태스크가 거의 정리된 상태라 팀 내에서 역할 분담 한 차례 더 해야 파악할 수 있을 것 같다...
일단은 회원가입 기능 + 테스트 조금 더 보완하기?</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 8월 1주 ] 주간 회고 🫧]]></title>
            <link>https://velog.io/@eujin-shin/8%EC%9B%94-1%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@eujin-shin/8%EC%9B%94-1%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 06 Aug 2023 11:26:36 GMT</pubDate>
            <description><![CDATA[<h3 id="이번-주에는">이번 주에는?</h3>
<ul>
<li>프로젝트 진행
A: 백엔드 api 연결, CSS 레이아웃 공부
B: 타입스크립트, Next.js 공부</li>
</ul>
<br>

<h3 id="새롭게-배운-것--배워야-할-것">새롭게 배운 것 &amp; 배워야 할 것</h3>
<ul>
<li>Next.js, TS 사용하기</li>
</ul>
<p>→ 타입스크립트의 &#39;타입 안정성&#39;
브라우저에서 이해하는 것은 자바스크립트임. 따라서 타입스크립트 코드는 컴파일시 자바스크립트 코드로 변환하게 된다. 이때, 작성된 코드에 문제가 없는지 확인해서 문제가 있을 경우 변환되지 않는다.</p>
<p>⇒ 즉, “보호장치”의 역할. 컴파일 이후 런타임 도중 타입에 대한 제약 부재로 발생 가능한 &#39;런타임 에러&#39;를 방지하게 된다.
ex) 함수의 경우 입력값의 데이터 타입과 개수에 대해 보호장치를 만들 수 있음</p>
<ul>
<li>하단 메뉴바 만들기</li>
</ul>
<blockquote>
<p>❗ 전체 container의 width가 정해진 상태에서 메뉴바를 자식 컴포넌트로 넣으면서 width를 100%로 설정했더니 부모 영역 안에 맞게 들어가지 않음</p>
</blockquote>
<ul>
<li>react-router-dom: <code>&lt;Link&gt;</code>, <code>&lt;useNavigator&gt;</code></li>
</ul>
<p>→ 둘의 차이는?
useNavigator는 이벤트 핸들러 함수를 통해 실행되기 때문에 보다 복잡한 로직 구현 가능</p>
<ul>
<li>프론트의 API 호출 (feat. react-hook-form)</li>
</ul>
<p>→ 프론트엔드와 백엔드가 연결되려면?
(로컬에서 진행하는 테스트 단계 기준) 프론트엔드에서는 package.json에 proxy 설정이 되어야 하고 백엔드에서 CORS 세팅이 되어야 함.</p>
<p>→ react-hook-form과의 연계
기존에 react-hook-form을 사용해 실시간으로 유효성 검사를 진행하도록 구현했는데, input 컴포넌트 자체에 에러 조건과 에러 메시지를 설정해 두기만 하면 됐기 때문에 별개의 버튼을 통해 작동하는 &#39;중복 ID/닉네임 검사&#39;를 이 로직에 어떻게 끼워야 할지 고민이었음.</p>
<p>1) 버튼을 눌렀을 때, input field에 달린 조건이 검증되고 만약 유효하지 않은 값이라면 작동하지 않아야 함</p>
<p>2) 아니라면 input field에 입력 중인 값을 가져와 (라이브러리에서 제공하는 <code>getValues</code>를 활용할 수 있었다) api 호출 후 response 받기</p>
<p>(* 여기부터는 미완...)
3) 응답으로 받은 값에 따라 
사용 가능한 ID/닉네임임을 알리거나, 
사용 불가한 ID/닉네임임을 알리고 다시 입력을 요청하거나 (라이브러리에서 제공하는 <code>setError</code>를 활용 가능할 듯)
예외 처리</p>
<p>4) 사용 가능한 값으로 인증된 이후 값이 변화하면 재인증 필요. 제출시 인증이 필요한 field는 모두 인증된 상태인지 확인되어야 함
(* 여기는 어떻게 구현해야 할지 아직 고민 중이다. ID, 닉네임, 메일 이렇게 세 개 필드라 state를 사용할까 생각 중...)</p>
<blockquote>
<p>❗ 비동기 함수? await? then?</p>
</blockquote>
<ul>
<li>JS로 백준 풀기</li>
</ul>
<p>→ 백준에서는 node.js 사용, 입력값을 받기 위해 readline 가져와 별도로 인터페이스 정의해야 함</p>
<br>

<h3 id="셀프-피드백">셀프 피드백</h3>
<p>→ 지난 주에 비해 좀 더 많은 걸 했지만 해야 할 태스크가 산발적이라 실질적으로 프로젝트 진도를 많이 나가지 못한 부분이 아쉽다. </p>
<p>→ 목표의 우선 순위를 결정하고 확실히 해결할 부분을 결정해야겠다.</p>
<br>

<h3 id="다음-주-솔루션-💡">다음 주 솔루션 💡</h3>
<ul>
<li><p><strong>매일 목표 시간을 3시간으로 설정하고, 개인 공부 시간을 최소 1시간으로 잡는다.</strong></p>
</li>
<li><p>다음 주 목표!
1) Next.JS 강의 수강 후 <strong>담당한 컴포넌트 완성하기</strong>
2) 회원가입 파트는 백엔드와 테스트 결과 공유한 이후 <strong>인증 부분 완성</strong>
3) CSS 레이아웃 공부해서 <strong>마이페이지 UI 재구성 + 완성</strong></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PS] 백준 14003: 가장 긴 증가하는 부분 수열 5]]></title>
            <link>https://velog.io/@eujin-shin/PS-%EB%B0%B1%EC%A4%80-14003-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-5</link>
            <guid>https://velog.io/@eujin-shin/PS-%EB%B0%B1%EC%A4%80-14003-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4-5</guid>
            <pubDate>Sun, 30 Jul 2023 03:37:56 GMT</pubDate>
            <description><![CDATA[<p><del>오랜만에 P5 푼 기념</del></p>
<p><img src="https://velog.velcdn.com/images/eujin-shin/post/a917c882-d1c0-4044-a7b4-787fdd1b01ac/image.png" alt=""></p>
<h3 id="풀이-과정-🔎">풀이 과정 🔎</h3>
<p>우선 주어지는 수열의 크기가 1,000,000이기 때문에 O(n^2)로는 안 된다는 건 금방 알 수 있다. 부분 수열 문제 자체도 좀 오랜만이긴 했지만 기억을 더듬어... O(n log n)으로 <strong>최장 증가 부분 수열의 길이</strong>를 구할 수 있는 알고리즘을 떠올렸다.</p>
<blockquote>
<p>배열 <code>len</code>을 정의해 <code>len[i]</code>가 증가 부분 수열의 i번째 원소가 될 수 있는 최솟값을 갖도록 한다. 즉 새로운 원소 <code>k</code>가 주어졌을 때, </p>
</blockquote>
<ul>
<li>배열이 비어 있거나 배열에 들어 있는 최댓값보다 클 경우 배열의 맨 뒤에 원소를 넣는다. </li>
<li>현재 배열에 들어 있는 최솟값보다 작거나 같을 경우 <code>len[0]</code>을 수정한다.</li>
<li>두 경우 다 아니라면 이분탐색을 통해 <code>len</code>에서 <code>k</code> 이상인 최솟값을 찾고, 그 값을 <code>k</code>로 수정한다.</li>
</ul>
<p>위 방식을 사용했을 때 <code>len</code>의 길이가 최장 증가 부분 수열의 길이이다. 다만 위 알고리즘으로는 <strong>최장 증가 부분 수열</strong>을 구할 수는 없다. </p>
<p>따라서 위의 과정에 더해 <strong>증가 부분 수열에서 해당 원소의 바로 앞에 있는 원소</strong>를 찾고자 했다. 위 알고리즘을 통해 주어진 배열에서 <code>k</code>보다 앞에 있으면서, <code>k</code>보다 작은 최댓값을 찾을 수 있다. 이 값을 또 다른 배열 <code>back</code>을 통해 관리하면 <code>len</code> 배열의 최댓값에서 시작해서 수열을 거슬러 올라가는 방식으로 전체 최장 증가 부분 수열을 찾을 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 7월 4주 ] 주간 회고 🧶]]></title>
            <link>https://velog.io/@eujin-shin/7%EC%9B%94-4%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@eujin-shin/7%EC%9B%94-4%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 29 Jul 2023 08:15:25 GMT</pubDate>
            <description><![CDATA[<h3 id="이번-주에는">이번 주에는?</h3>
<ul>
<li><p>프로젝트 진행
A: 회원가입 페이지 로직 구현, 마이페이지 컴포넌트
B: 프로필 페이지 담당</p>
</li>
<li><p>개인 공부
노마드코드 리액트 코스 수강 중</p>
</li>
</ul>
<br>

<h3 id="새롭게-배운-것--배워야-할-것">새롭게 배운 것 &amp; 배워야 할 것</h3>
<ul>
<li>입력 폼 유효성 검사 (feat. react-hook-form)</li>
</ul>
<p>→ useForm 호출 시 mode를 onChange로 설정해 실시간 유효성 검사 가능: errors가 실시간으로 업데이트 되며, 각 field의 error 값을 확인하여 그에 대한 리액션 가능
ex)  <code>error</code>를 styled component의 prop으로 전달해서 상호작용성 추가, 에러 메시지 출력
→ 비밀번호 정규식 regex
→ 모든 필드가 유효할 때 submit을 활성화할 수는 없을까?</p>
<ul>
<li>백엔드 api 호출: 스웨거 문서 읽기, axios 사용</li>
</ul>
<p>→ 백엔드 측에서 작성한 api 문서를 참고
→ POST 호출하고 “url”과 “데이터 객체”를 전달하는 게 기초적
→ 데이터베이스 수업에서 CRUD 배우고 php로나마 구현해 본 게 도움이 되는 듯</p>
<ul>
<li>FE - BE 연동</li>
</ul>
<p>→ 어느 정도 로직이 구현되면서 DB까지 포함해 테스트를 해 볼 필요가 생김. 백엔드와 논의해서 연결과 테스트 방법을 알아보고 있다</p>
<br>

<h3 id="셀프-피드백">셀프 피드백</h3>
<p>→ 이번 주는 전체적으로... 많이 나태했다 🫠 뽀모도로 타이머를 사용하면 &#39;하는 날에는&#39; 효율적으로 할 수가 있는데, 다른 일정이며 컨디션을 핑계로 한두 세션 겨우 하거나 아예 빼 먹은 날들이...
원래는 오후에 주로 책상에 앉았는데, 아침에 커피 한 잔 딱 마시고 오전부터 바로 할 일 시작해야 할 것 같다.</p>
<p>→ 회원가입 페이지 코드가 좀 비효율적이라는 생각이 많이 든다. 컴포넌트 활용을 제대로 못하지 않았나...... 지금 진행하는 다른 프로젝트는 비교적 규모가 크고 컴포넌트 구성을 논의해 볼 기회도 좀 더 있을 것 같아서 잘 활용해야겠음.</p>
<p>→ 리액트 코스의 경우 최근에 공부한 내용이랑 겹치는 것이 많아서 집중도가 떨어진다.</p>
<p>→ 덤으로... 프로젝트 B 태스크를 받으면서 Next.JS를 빠르게 공부해야 할 필요가 생김 😬</p>
<br>

<h3 id="다음-주-솔루션-💡">다음 주 솔루션 💡</h3>
<ul>
<li><p><strong>매일 목표 시간을 3시간으로 설정하고, 개인 공부 시간을 최소 1시간으로 잡는다.
→ 프로젝트 개발하면서 물음표가 생긴 부분은 노션에 기록해 뒀다가 공부 시간에 복습해 본다. 주간 회고에도 이 부분을 기록하면 더 좋을 듯.</strong>
→ 노션 사용은 그럭저럭 하고 있는데 아직 습관을 못 붙였다. 하루 전체의 스케줄과 집중력 관리 방법을 좀 더 고민해 봐야 함</p>
</li>
<li><p>다음 주 목표!
→ Next.JS 강의 진행을 우선으로, ReactJS는 (남은 코스가 연습 위주인 것 같으니) 서브로
→ 마이페이지 내 컴포넌트 짜기
→ 프로필 페이지 UI, 공통 컴포넌트
→ 프로젝트 내의 이슈 (상태관리 라이브러리, 코드 편집기, 프로젝트 테스트) 관련해서 공부하기</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 7월 3주 ] 주간 회고 🌱]]></title>
            <link>https://velog.io/@eujin-shin/7%EC%9B%94-3%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@eujin-shin/7%EC%9B%94-3%EC%A3%BC-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 22 Jul 2023 06:47:13 GMT</pubDate>
            <description><![CDATA[<h3 id="이번-주에는">이번 주에는?</h3>
<ul>
<li>프로젝트 A 진행 중</li>
</ul>
<p>태스크 조정으로 FE 리드에 가까운 역할(?)이 되었다. 이번 주엔 페이지 UI 마무리하고 스톱워치 로직 구현하는 게 중심이었다!</p>
<ul>
<li>노마드코더 ReactJS 코스 수강</li>
</ul>
<p>후술할 프로젝트 B 합류도 있고, 이래저래 기초를 다시 다지고 싶은 마음이 커서 바닐라 JS 코스 먼저 끝내고 ReactJS로 넘어왔다. 아직은 초반부.</p>
<ul>
<li>프로젝트 B 시작...?</li>
</ul>
<p>모집 공고보고 냉큼 합류하긴 했는데 아직 태스크 분담이 확실히 안 되어서 대기 중. 일단 시작되면 휘리릭 타임라인 짜여질 것 같기도 해서 분담 받으면 최대한 바로 쳐낼 수 있게 준비를 해야 할 것 같다.
<br></p>
<h3 id="새롭게-배운-것--배워야-할-것">새롭게 배운 것 &amp; 배워야 할 것</h3>
<ul>
<li><p>프로젝트를 하면서 axios, styled-components, react-hook-form 등의 라이브러리를 쓰게 됐다. 특히 axios, react-hook-form은 회원가입 구현하면서 접했는데 기말 끝나고 오는 새 많이 까먹어서... 복습이 좀 필요하다 🫠</p>
</li>
<li><p>styled-components: JS로 스타일링 된 페이지 요소를 생성할 수 있게 하는 CSS-in-JS 라이브러리. </p>
</li>
<li><blockquote>
<p>간단히 다룰 수 있게 되긴 했는데, 아직 선택자는 헷갈린다.  어떻게 써야 효율적인지에 대해서도 좀 더 공부가 필요할 듯.</p>
</blockquote>
</li>
<li><blockquote>
<p>그리고 프로젝트하면서 전반적으로 아직 CSS 무지렁이라는 것을 많이 느낀다 ㅋㅋ ㅠ</p>
</blockquote>
</li>
<li><p>스톱워치의 시작/일시정지/초기화 구현 로직.</p>
</li>
<li><blockquote>
<p>작동 여부(boolean)와 카운터를 사용했다. setInterval로 1초마다 카운터를 증가시키고, 카운터를 시간 포맷으로 변형해 띄워 주는 방식.</p>
</blockquote>
</li>
<li><blockquote>
<p>서치한 자료 참고해서 <code>useRef()</code>를 사용했는데 이게 정확히 무슨 역할인지 이해하지 못함.</p>
<br>
</blockquote>
</li>
</ul>
<h3 id="셀프-피드백">셀프 피드백</h3>
<p>➕ 프로젝트에서 받은 태스크는 밀리지 않고 해결하고 있다. 이건 어떻게 해야 하는지 여기저기 찾아가면서 하고 있는데, 확실히 전에 몰랐던 것들을 다양하게 알아가고 있다.</p>
<p>➕ 노마드코더 강의가 만족스러워서 적정 속도로 진도 빼고 있는 것 같다. 다음 주 안에 리액트 코스 완강하고 Next.JS로 넘어가야지!</p>
<p>➕ 회고 작성법을 고민 중이었는데 나름대로 구성을 짜서 적기 시작했다. 앞으로도 작성하면서 구조를 조금씩 조정해 나가면 좋을 듯.</p>
<p>➖ 공부가 지나치게 &#39;얕고 넓은&#39; 느낌이다. 어떻게 하긴 했지만 이게 왜 되는지 제대로 알지 못하는 게 너무 많다. (그야말로 이게 왜 돼? 의 연속) 하나씩 다 밑바닥부터 이해하면서 올라오려니 태스크가 밀릴 것 같고 (ㅜㅜ) 결국 당장 구현하는 것에만 우선순위가 집중되어서 프로젝트 경험이 양질의 학습으로 연결되지 못하고 있다.</p>
<p>➖ 구체적인 주간 목표, 일간 목표 설정이 부족하다. 공부 시간이 오락가락...!
<br></p>
<h3 id="다음-주-솔루션-💡">다음 주 솔루션 💡</h3>
<ul>
<li>매일 목표 시간을 3시간으로 설정하고, 개인 공부 시간을 최소 1시간으로 잡는다.</li>
<li><blockquote>
<p>프로젝트 개발하면서 물음표가 생긴 부분은 노션에 기록해 뒀다가 공부 시간에 복습해 본다. 주간 회고에도 이 부분을 기록하면 더 좋을 듯.</p>
</blockquote>
</li>
<li>주간 회고의 경우에도 너무 시간과 에너지를 소모하지 않고 지속할 수 있도록 자잘한 메모를 남긴다.</li>
<li>다음 주 목표!</li>
<li><blockquote>
<p>노마드코더 React 강의 완강 / Next.JS 강의 시작</p>
</blockquote>
</li>
<li><blockquote>
<p>백엔드 쪽 스웨거 확인하고 공부하기</p>
</blockquote>
</li>
<li><blockquote>
<p>회원가입 관련 라이브러리 복습하고 로직 구현</p>
</blockquote>
</li>
<li><blockquote>
<p>자잘한 페이지 UI 마저 완료</p>
</blockquote>
</li>
<li><blockquote>
<p>(만약 프로젝트 B 태스크가 여유로운 편이라면...) styled-components 효율적으로 쓰는 법 공부해 보기</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>