<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>gang_shik.log</title>
        <link>https://velog.io/</link>
        <description>측정할 수 없으면 관리할 수 없고, 관리할 수 없으면 개선시킬 수도 없다</description>
        <lastBuildDate>Fri, 25 Apr 2025 07:47:41 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. gang_shik.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/gang_shik" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[최단경로 알고리즘]]></title>
            <link>https://velog.io/@gang_shik/%EC%B5%9C%EB%8B%A8%EA%B2%BD%EB%A1%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@gang_shik/%EC%B5%9C%EB%8B%A8%EA%B2%BD%EB%A1%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Fri, 25 Apr 2025 07:47:41 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="최단-경로-알고리즘-정리-다익스트라-vs-플로이드-와샬">최단 경로 알고리즘 정리: 다익스트라 vs 플로이드-와샬</h1>
<hr>
<h2 id="1-최단-경로-알고리즘-개요">1. 최단 경로 알고리즘 개요</h2>
<p>최단 경로 알고리즘은 다음과 같이 분류</p>
<ul>
<li><strong>단일 출발점(Single-Source)</strong>: 하나의 노드에서 모든 노드까지의 최단 경로 계산 
→ 다익스트라, 벨만-포드(Bellman-Ford)</li>
<li><strong>전체 쌍(All-Pairs)</strong>: 모든 노드 쌍 사이의 최단 경로 계산<br>→ 플로이드-와샬, 존슨(Johnson)</li>
</ul>
<hr>
<h2 id="2-다익스트라-알고리즘">2. 다익스트라 알고리즘</h2>
<h3 id="기본-개념">기본 개념</h3>
<ul>
<li><strong>그리디(Greedy)</strong> 전략 기반</li>
<li><strong>음수가 아닌 가중치</strong>만 처리 가능</li>
<li>우선순위 큐를 사용해 최적화(O((V+E) log V))</li>
</ul>
<h3 id="동작-원리">동작 원리</h3>
<ol>
<li><p>출발 노드의 거리를 0, 나머지는 ∞로 초기화</p>
</li>
<li><p>미방문 노드 중 최소 거리 노드 선택</p>
</li>
<li><p>선택 노드의 인접 노드 거리 갱신</p>
</li>
<li><p>모든 노드 방문 시까지 반복</p>
<ul>
<li>그래프에서 출발점과 도착점 사이의 최단 거리를 구하는 알고리즘</li>
<li>보통 단일 정점 간 최단 경로 산출 시 사용, 도로 교통망이나 OSPF 등의 네트워크 라우팅 프로토콜에 널리 이용<pre><code class="language-javascript">// ShortestPath() : edge object 객체 저장을 위한 생성자
// key: vertex
// value: list 형태로 연결된 vertex를 표현하여 edge 연결 관계 표현
function ShortestPath() {
this.edges = {};
}
</code></pre>
</li>
</ul>
</li>
</ol>
<p>// addVertex() : 정점 추가(간선 비용 표시를 위해 key/value object 형태로 저장)
ShortestPath.prototype.addVertex = function (vertex) {
  this.edges[vertex] = {};
};</p>
<p>// addEdge() : 간선 추가
ShortestPath.prototype.addEdge = function (srcVertex, dstVertex, weight) {
  this.edges[srcVertex][dstVertex] = weight;
};</p>
<p>// _extractMin() : 최단 거리 노드 검색
ShortestPath.prototype._extractMin = function (queue, dist) {
  let minDistance = Number.POSITIVE_INFINITY;
  let minVertex = null;</p>
<p>  for (let vertex in queue) {
    if (dist[vertex] &lt;= minDistance) {
      minDistance = dist[vertex];
      minVertex = vertex;
    }
  }</p>
<p>  return minVertex;
};</p>
<p>// dijkstra() : 다익스트라 최단 경로 탐색
ShortestPath.prototype.dijkstra = function (start) {
  let queue = {};
  let dist = {};</p>
<p>  for (let vertex in this.edges) {
    dist[vertex] = Number.POSITIVE_INFINITY;
    queue[vertex] = this.edges[vertex];
  }</p>
<p>  dist[start] = 0;
  while (Object.keys(queue).length != 0) {
    let u = this._extractMin(queue, dist);</p>
<pre><code>delete queue[u];

for (let neighbor in this.edges[u]) {
  let alt = dist[u] + this.edges[u][neighbor];
  if (alt &lt; dist[neighbor]) dist[neighbor] = alt;
}</code></pre><p>  }</p>
<p>  for (let vertex in this.edges) {
    if (dist[vertex] === Number.POSITIVE_INFINITY)
      delete dist[vertex];</p>
<p>  return dist;
};</p>
<pre><code>
---

## 3. 플로이드-와샬 알고리즘

### 기본 개념
- **동적 계획법(DP)** 기반
- **음수 가중치** 허용(단, 음수 사이클 제외)
- 모든 노드 쌍의 최단 경로를 O(V³) 시간에 계산

### 동작 원리
1. 인접 행렬로 거리 초기화
2. 각 노드를 중간 경유지로 사용해 거리 갱신
3. 3중 반복문으로 모든 조합 검사

- 동적 계획법을 활용해, 그래프에서 가능한 모든 정점 쌍에 대해 최단 거리를 구하는 알고리즘
- 음의 가중치가 있어도 사용 가능하며, 많은 수의 간선으로 이루어져 있는 밀집 그래프(Dense Graph)에 사용 적합

```javascript
// floydWarshall() : 플로이드-워셜 최단 경로 탐색
ShortestPath.prototype.floydWarshall = function () {
  let dist = {};

  for (let srcVertex in this.edges) {
    dist[srcVertex] = {};
    for (let dstVertex in this.edges) {
      if (srcVertex === dstVertex) dist[srcVertex][dstVertex] = 0;
      else dist[srcVertex][dstVertex] = Number.POSITIVE_INFINITY;
    }
  }

  for (let srcVertex in this.edges) {
    for (let dstVertex in this.edges[srcVertex])
      dist[srcVertex][dstVertex] = this.edges[srcVertex][dstVertex];
  }

  for (let midVertex in this.edges) 
    for (let srcVertex in this.edges)
      for (let dstVertex in this.edges)
        dist[srcVertex][dstVertex] = Math.min(dist[srcVertex][dstVertex], dist[srcVertex][midVertex] + dist[midVertex][dstVertex]);

  for (let srcVertex in this.edges)
    for (let dstVertex in this.edges)
      if (dist[srcVertex][dstVertex] === Number.POSITIVE_INFINITY)
        delete dist[srcVertex][dstVertex];

  return dist;
};</code></pre><hr>
<h2 id="4-알고리즘-비교">4. 알고리즘 비교</h2>
<table>
<thead>
<tr>
<th>기준</th>
<th>다익스트라</th>
<th>플로이드-와샬</th>
</tr>
</thead>
<tbody><tr>
<td><strong>계산 범위</strong></td>
<td>단일 출발점</td>
<td>전체 노드 쌍</td>
</tr>
<tr>
<td><strong>시간 복잡도</strong></td>
<td>O((V+E) log V)</td>
<td>O(V³)</td>
</tr>
<tr>
<td><strong>공간 복잡도</strong></td>
<td>O(V)</td>
<td>O(V²)</td>
</tr>
<tr>
<td><strong>가중치 제약</strong></td>
<td>음수 불가</td>
<td>음수 허용(사이클 제외)</td>
</tr>
<tr>
<td><strong>최적화 기법</strong></td>
<td>우선순위 큐</td>
<td>동적 계획법</td>
</tr>
<tr>
<td><strong>적합 그래프</strong></td>
<td>희소 그래프</td>
<td>밀집 그래프</td>
</tr>
</tbody></table>
<hr>
<h2 id="5-선택-가이드">5. 선택 가이드</h2>
<ul>
<li><p><strong>다익스트라</strong>가 적합한 경우:  </p>
<ul>
<li>단일 출발점 문제  </li>
<li>그래프가 희소하고 가중치가 양수  </li>
<li>실시간 경로 계산이 필요할 때</li>
</ul>
</li>
<li><p><strong>플로이드-와샬</strong>이 적합한 경우:  </p>
<ul>
<li>전체 노드 쌍의 경로가 필요  </li>
<li>그래프가 밀집되어 있음  </li>
<li>음수 가중치 존재 시</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[그래프와 DFS & BFS]]></title>
            <link>https://velog.io/@gang_shik/%EA%B7%B8%EB%9E%98%ED%94%84%EC%99%80-DFS-BFS</link>
            <guid>https://velog.io/@gang_shik/%EA%B7%B8%EB%9E%98%ED%94%84%EC%99%80-DFS-BFS</guid>
            <pubDate>Thu, 24 Apr 2025 07:59:33 GMT</pubDate>
            <description><![CDATA[<h2 id="til">TIL</h2>
<h3 id="그래프graph">그래프(Graph)</h3>
<ul>
<li><p>정점과 간선으로 구성되어 네트워크 구조를 추상화한 비선형 자료 구조</p>
</li>
<li><p>그래프 특징</p>
<ul>
<li>정점(Vertex)과 간선(Edge)의 집합</li>
<li>다양한 그래프 종류를 혼합하여 표현 가능</li>
</ul>
</li>
<li><p>그래프 종류</p>
<ul>
<li>방향 그래프(Directed Graph) : 간선에 특정 방향이 존재하는 그래프(A -&gt; B로 표현, A에서 B로만 이동 가능)</li>
<li>무방향 그래프(Undirected Graph) : 간선에 특정 방향이 존재하지 않는 그래프(A - B로 표현, 양방향 이동 가능)</li>
<li>가중치 그래프(Weighted Graph) : 간선에 비용이나 가중치가 할당된 그래프</li>
</ul>
</li>
<li><p>그래프 종류 2</p>
<ul>
<li>연결 그래프(Connected Graph) : 무방향 그래프에 있는 모든 정점쌍에 대해 항상 경로가 존재하는 그래프</li>
<li>비연결 그래프(Disconnected Graph) : 무방향 그래프에서 특정 정점쌍 사이에 경로가 존재하지 않는 그래프</li>
<li>순환 그래프(Cycle) : 단순 경로의 시작 정점과 종료 지점이 동일하여 순환 지점이 존재하는 그래프</li>
<li>비순환 그래프(Acyclic Graph) : 순환 지점이 존재하지 않는 그래프</li>
<li>완전 그래프(Complete Graph) : 그래프에 속해 있는 모든 정점이 서로 연결되어 있는 그래프</li>
</ul>
</li>
<li><p>그래프 표현 방법</p>
<ul>
<li>인접 리스트(Adjacency List) : 정점에 연결된 다른 정점을 리스트로 표현</li>
<li>인접 행렬(Adjacency Matrix) : 정점에 연결된 다른 정점을 정점x정점 크기의 매트릭스로 표현</li>
</ul>
</li>
<li><p>그래프 구현</p>
<pre><code class="language-javascript">/* 방향 그래프 */
// Graph(): edge object 객체 저장을 위한 생성자
// key : vertex
// value : list 형태로 연결된 vertex를 표현하여 edge 연결 관계 표현
function Graph() {
this.edge = {};
}
</code></pre>
</li>
</ul>
<p>// addVertex() : 정점(vertex) 추가
Graph.prototype.addVertex = function (v) {
  this.edge[v] = [];
};</p>
<p>// addEdge() : 간선(edge) 추가
Graph.prototype.addEdge = function (v1, v2) {
  this.edge[v1].push(v2);
};</p>
<p>// removeEdge(): 간선(edge) 삭제
Graph.prototype.removeEdge = function (v1, v2) {
  if (this.edge[v1]) {
    let idx = this.edge[v1].indexOf(v2);</p>
<pre><code>if (idx != -1) {
  this.edge[v1].splice(idx, 1);
}

if (this.edge[v1].length === 0) {
  delete this.edge[v1];
}</code></pre><p>  }
};</p>
<p>// removeVertex(): 정점(vertex) 삭제
Graph.prototype.removeVertex = function (v) {
  if (this.edge[v] === undefined) return;</p>
<p>  let length = this.edge[v].length; // changed length
  let connectedVertex = [...this.edge[v]]; // object copy</p>
<p>  for (let i = 0; i &lt; length; i++) {
    this.removeEdge(v, connectedVertex[i]);
  }
}:</p>
<p>// sizeVertex(): vertex 개수 반환
Graph.prototype.sizeVertex = function () {
  return Object.keys(this.edge).length;
};</p>
<p>// sizeEdge() : edge 개수 반환
Graph.prototype.sizeEdge = function (vertex) {
  return this.edge[vertex] ? Object.keys(this.edge[vertex]).length : 0;
};</p>
<p>// print() : 현재 Graph 연결 상태 출력
Graph.prototype.print = function () {
  for (let vertex in this.edge) {
    let neighbors = this.edge[vertex];
    if (neighbors.length == 0) continue;</p>
<pre><code>process.stdout.write(`${vertex} -&gt; `);
for (let j = 0; j &lt; neighbors.length; j++) {
  process.stdout.write(`${neighbors[j]} `);
}

console.log(&quot;&quot;);</code></pre><p>  }
};</p>
<p>/* 무방향 그래프 */</p>
<p>// addEdge() : 간선(edge) 추가
Graph.prototype.addEdge = function (v1, v2) {
  this.edge[v1].push(v2);
  this.edge[v2].push(v1);
};</p>
<p>// removeEdge() : 간선(edge) 삭제
Graph.prototype.removeEdge = function (v1, v2) {
  if (this.edge[v2]) {
    let idx = this.edge[v2].indexOf(v1);</p>
<pre><code>if (idx != -1) {
  this.edge[v2].splice(idx, 1);
}

if (this.edge[v2].length === 0) {
  delete this.edge[v2];
}</code></pre><p>  }
};</p>
<pre><code>
### DFS(Depth First Search)
- 트리나 그래프 등에서 하나의 노드를 최대한 깊게 들어가면서 해를 찾는 탐색 기법
- 장/단점
  - 장점 : 인접한 후보 노드만 기억하면 되므로 적은 기억공간 소요, 노드가 깊은 단계에 있을 경우 빠르게 정답 산출
  - 단점 : 선택한 경로가 답이 아닐 경우 불필요한 탐색 가능, 최단 경로를 구할 시 찾은 해가 정답이 아닐 경우 발생
```javascript
// dfs() : DFS 탐색
Graph.prototype.dfs = function (startVertex) {
  this._dfsRecursiveVisit(startVertex);
};

// _dfsRecursiveVisit() : 재귀를 이용한 DFS 탐색
Graph.prototype._dfsRecursiveVisit = function (vertex) {
  if (this.visited[vertex]) {
    return;
  }

  this.visited[vertex] = true;
  console.log(`visit &quot; ${vertex}&quot;`);

  let neighbors = this.edge[vertex];
  for (let i = 0; i &lt; neighbors.length; i++) {
    this._dfsRecursiveVisit(neighbors[i]);
  }
};

// dfs() : DFS 탐색
Graph.prototype.dfs = function (startVertex) {
  this._dfsLoopVisit(startVertex);
};

// _dfsLoopVisit() : 스택을 이용한 DFS 탐색
Graph.prototype._dfsLoopVisit = function (vertex) {
  let stack = new Stack();
  stack.push(vertex);

  while (!stack.isEmpty()) {
    let vertex = stack.pop();
    if (this.visited[vertex]) {
      continue;
    }

    this.visited[vertex] = true;
    console.log(`visit &quot;${vertex}&quot;`);

    let neighbors = this.edge[vertex];
    for (let i = neighbors.length - 1; i &gt;= 0; i--) {
      stack.push(neighbors[i]);
    }
  }
};</code></pre><h3 id="bfsbreadth-first-search">BFS(Breadth First Search)</h3>
<ul>
<li>트리나 그래프 등에서 인접한 노드를 우선 방문하면서 넓게 움직이며 해를 찾는 탐색 기법</li>
<li>장/단점<ul>
<li>장점 : 최단 경로 탐색에서 구한 해가 정답임을 보장</li>
<li>단점 : 경로가 매우 길어질 경우, 탐색 범위가 증가하면서 DFS보다 많은 기억 공간이 필요<pre><code class="language-javascript">// bfs() : BFS 탐색
Graph.prototype.bfs = function (startVertex) {
this._bfsLoopVisit(startVertex);
};
</code></pre>
</li>
</ul>
</li>
</ul>
<p>// _bfsLoopVisit() : 큐를 이용한 BFS 탐색
Graph.prototype._bfsLoopVisit = function (vertex) {
  let queue = new Queue();
  queue.enqueue(vertex);</p>
<p>  while (!queue.isEmpty()) {
    let vertex = queue.dequeue();
    if (this.visited[vertex]) {
      continue;
    }</p>
<pre><code>this.visited[vertex] = true;
console.log(`visit &quot;${vertex}&quot;`);

let neighbors = this.edge[vertex];
for (let i = 0; i &lt; neighbors.length; i++) {
  queue.enqueue(neighbors[i]);
}</code></pre><p>  }
};</p>
<p>// _bfsShortestPath() : 다른 정점 간 최단 경로 비용 산출
Graph.prototype._bfsShortestPath = function (vertex) {
  let queue = new Queue();
  queue.enqueue(vertex);</p>
<p>  let distance = {};
  let pre_visit = {};
  for (let vertex in this.edge) {
    distance[vertex] = 0;
    pre_visit[vertex] = null;
  }</p>
<p>  while(!queue.isEmpty()) {
    let vertex = queue.dequeue();
    if (this.visited[vertex]) {
      continue;
    }</p>
<pre><code>this.visited[vertex] = true;
console.log(`visit &quot;${vertex}&quot;`);

let neighbors = this.edge[vertex];
for (let i = 0; i &lt; neighbors.length; i++) {
  distance[neighbors[i]] = distance[vertex] + 1;
  pre_visit[neighbors[i]] = vertex;
  queue.enqueue(neighbors[i]);
}</code></pre><p>  }</p>
<p>  return { distance, pre_visit };
};</p>
<p>// _from_to_path() : from 정점에서 to 정점으로 최단 경로 출력
Graph.prototype._from_to_path = function (pre_visit, from, to) {
  let stack = new Stack();</p>
<p>  for (let v = to; v !== from; v = pre_visit[v]) {
    stack.push(v);
  }
  stack.push(from);</p>
<p>  while (!stack.isEmpty()) {
    let v = stack.pop();
    process.stdout.write(<code>${v} -&gt;</code>);
  }
  console.log(&quot;end&quot;);
};</p>
<p>// shortestPath(): 다른 정점 간 최단 경로 탐색
Graph.prototype.shortestPath = function (startVertex) {
  let result = this._bfsShortestPath(startVertex);</p>
<p>  console.log(result.distance);
  console.log(result.pre_visit);</p>
<p>  for (let vertex in this.edge) {
    if (vertex === startVertex) continue;</p>
<pre><code>this._from_to_path(result.pre_visit, startVertex, vertex);</code></pre><p>  }
};
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[트리]]></title>
            <link>https://velog.io/@gang_shik/%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@gang_shik/%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Tue, 22 Apr 2025 10:06:01 GMT</pubDate>
            <description><![CDATA[<h3 id="트리tree">트리(Tree)</h3>
<ul>
<li><p>그래프의 일종으로 두 노드 사이의 하나의 간선만 연결되어 있는, 최소 연결과 계층 형태의 비선형 자료 구조</p>
</li>
<li><p>노드(node) : 하나 이상의 값을 갖는 객체 단위</p>
</li>
<li><p>간선(edge) : 두 노드를 연결하는 선</p>
</li>
<li><p>루트 노드(Root node) : 부모가 없는 최상위 노드</p>
</li>
<li><p>단말 노드(Leaf node) : 자식이 없는 노드</p>
</li>
<li><p>부모 노드(Parent node) : 특정 Sub-Tree 내에서의 상위 노드</p>
</li>
<li><p>자식 노드(Child node) : 특정 Sub-Tree 내에서의 하위 노드</p>
</li>
<li><p>형제(Sibling) : 부모가 없는 최상위 노드</p>
</li>
<li><p>트리 특징</p>
<ul>
<li>주요 특징 : &#39;최소 연결 트리&#39;로 불림, 계층 모델, 방향 비순환 그래프(DAG: Directed Acyclic Graph) 한 종류</li>
<li>트리 종류 : 이진 트리, 이진 탐색 트리, AVL 트리, 힙(Heap)</li>
<li>노드 크기(size) : 자신을 포함한 모든 자손 노드의 개수</li>
<li>노드 깊이(depth) : 루트에서 특정 노드에 도달하기 위한 간선의 개수</li>
<li>노드 레벨(level) : 트리의 특정 깊이를 가지는 노드의 집합</li>
<li>노드 차수(degree) : 노드가 지닌 가지의 수</li>
<li>트리 차수(tree degree) : 트리의 최대 차수</li>
<li>트리 높이(height) : 루트 노드에서 가장 깊숙이 있는 노드의 깊이</li>
</ul>
</li>
<li><p>트리 순회</p>
<ul>
<li>트리 구조에서 각각의 노드를 정확히 한 번씩 체계적인 방법으로 방문하는 과정</li>
<li>N(Node) : 해당 노드를 방문</li>
<li>L(Left) : 왼쪽 서브 트리로 이동</li>
<li>R(Right) : 오른쪽 서브 트리로 이동</li>
<li>순회 방식<ul>
<li>전위 순회(Pre-order) : N-L-R</li>
<li>중위 순회(In-order) : L-N-R</li>
<li>후위 순회(Post-order) : L-R-N</li>
<li>층별 순회(Level-order) : 낮은 Level부터 순차적으로 순회</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="이진-트리binary-tree">이진 트리(Binary Tree)</h3>
<ul>
<li><p>각각의 노드가 최대 두개의 자식 노드를 가지는 트리 자료 구조</p>
</li>
<li><p>활용 방식</p>
<ul>
<li>검색과 정렬 : 이진 탐색 트리와 이진 힙 구현에 활용</li>
<li>허프만 코딩 : 연관 분기 구조 위한 데이터 표현에 활용</li>
</ul>
</li>
<li><p>이진 트리의 종류</p>
<ul>
<li>포화 이진 트리(Perfect binary Tree)</li>
<li>완전 이진 트리(Complete binary Tree)</li>
<li>정 이진 트리(Full binary Tree)</li>
<li>편향 이진 트리(Skewed binary Tree)</li>
<li>균형 이진 트리(Balanced binary Tree)</li>
</ul>
</li>
<li><p>포화 이진 트리(Perfect binary Tree)</p>
<ul>
<li>모든 레벨의 노드가 가득 채워져 있는 트리</li>
<li>특징<ul>
<li>Leaf 노드를 제외한 모든 자식은 2개의 노드를 보유</li>
<li>노드의 개수 n = 2^h - 1</li>
</ul>
</li>
</ul>
</li>
<li><p>완전 이진 트리(Complete binary Tree)</p>
<ul>
<li>마지막 레벨 전까지 노드가 가득 채워져 있고, 마지막 레벨은 왼쪽부터 순차적으로 채워져 있는 트리</li>
<li>특징<ul>
<li>배열을 사용해 효율적인 표현 가능</li>
<li>노드의 개수 : n &lt; 2^h - 1</li>
</ul>
</li>
</ul>
</li>
<li><p>정 이진 트리(Full binary Tree)</p>
<ul>
<li>모든 노드가 0개 또는 2개의 자식 노드만 갖는 트리</li>
<li>특징<ul>
<li>proper 또는 plane 이진 트리라고도 불림</li>
<li>노드의 개수 : n &lt;= 2^h - 1</li>
</ul>
</li>
</ul>
</li>
<li><p>편향 이진 트리(Skewed binary Tree)</p>
<ul>
<li>왼쪽 혹은 오른쪽으로 편향되게 치우쳐 있는 트리</li>
<li>특징<ul>
<li>각각의 높이에 하나의 노드만 존재</li>
<li>노드의 개수 : h</li>
</ul>
</li>
</ul>
</li>
<li><p>균형 이진 트리(Balanced binary Tree)</p>
<ul>
<li>삽입/삭제가 이루어 질 때, 왼쪽 서브 트리와 오른쪽 서브 트리의 높이 차를 1 이하로 맞추는 이진 탐색 트리</li>
<li>특징<ul>
<li>서브 트리 높이 차이가 항상 1 이하로 유지</li>
<li>균형 트리 종류 : AVL 트리, Red-Black 트리, B트리, B+트리, B*트리</li>
</ul>
</li>
</ul>
</li>
<li><p>이진 트리 구현</p>
<pre><code class="language-javascript">// Node() : value와 left, right node 저장을 위한 생성자
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
</code></pre>
</li>
</ul>
<p>// BinaryTree() : 시작 노드인 root를 저장하기 위한 생성자
function BinaryTree() {
  this.root = null;
}</p>
<p>// _insertNode() : 재귀로 트리를 순회하면 노드 추가 (내부 사용)
BinaryTree.prototype._insertNode = function (node, value) {
  if (node === null) {
    node = new Node(value);
  } else if (value &lt; node.value) {
    node.left = this._insertNode(node.left, value);
  } else if (value &gt;= node.value) {
    node.right = this._insertNode(node.right, value);
  }
  return node;
};</p>
<p>// insert() : 노드 추가
BinaryTree.prototype.insert = function (value) {
  this.root = this._insertNode(this.root, value);
};</p>
<p>// _preOrderTraverseNode() : 재귀로 트리를 순회하며 전위 순회(내부 사용)
BinaryTree.prototype._preOrderTraverseNode = function (node, callback) {
  if (node === null) {
    return;
  }</p>
<p>  callback(node);
  this._preOrderTraverseNode(node.left, callback);
  this._preOrderTraverseNode(node.right, callback);
};</p>
<p>// preOrderTraverse() : 전위 순회하며 노드 출력
BinaryTree.prototype.preOrderTraverse = function (callback) {
  this._preOrderTraverseNode(this.root, callback);
};</p>
<p>// _inOrderTraverseNode() : 재귀로 트리를 순회하며 중위 순회(내부 사용)
BinaryTree.prototype._inorderTraverseNode = function (node, callback) {
  if (node === null) {
    return;
  }</p>
<p>  this._inOrderTraverseNode(node.left, callback);
  callback(node);
  this._inOrderTraverseNode(node.right, callback);
};</p>
<p>// inOrderTraverse() : 중위 순회하며 노드 출력
BinaryTree.prototype.inOrderTraverse = function (callback) {
  this._inOrderTraverseNode(this.root, callback);
};</p>
<p>// _postOrderTraverseNode() : 재귀로 트리를 순회하며 후위 순회(내부 사용)
BinaryTree.prototype._postOrderTraverseNode = function (node, callback) {
  if (node === null) {
    return;
  }</p>
<p>  this._postOrderTraverseNode(node.left, callback);
  this._postOrderTraverseNode(node.right, callback);
  callback(node);
};</p>
<p>// postOrderTraverse() : 후위 순회하며 노드 출력
BinaryTree.prototype.postOrderTraverse = function (callback) {
  this._postOrderTraverseNode(this.root, callback);
};</p>
<p>// Queue 객체 추가
function Queue(array) {
  this.array = array ? array : [];
}
Queue.prototype.isEmpty = function () {
  return this.array.length == 0;
};
Queue.prototype.enqueue = function (element) {
  return this.array.push(element);
};
Queue.prototype.dequeue = function () {
  return this.array.shift();
};</p>
<p>// levelOrderTraverse() : 층별 순회하며 노드 출력
BinaryTree.prototype.levelOrderTraverse = function (callback) {
  let q = new Queue();
  let node;
  q.enqueue(this.root);
  while (!q.isEmpty()) {
    node = q.dequeue();
    callback(node);
    if (node.left !== null) q.enqueue(node.left);
    if (node.right !== null) q.enqueue(node.right);
  }
};</p>
<pre><code>
### 이진 탐색 트리
- 현재 노드를 기준으로 왼쪽에는 작은 값, 오른쪽에는 큰 값으로 정렬해 놓는 이진 트리 기반 자료 구조
```javascript
// Node() : value와 left, right node 저장을 위한 생성자
function Node(value) {
  this.value = value;
  this.left = null;
  this.right = null;
}

// BinarySearchTree() : 시작 노드인 root를 저장하기 위한 생성자
function BinarySearchTree() {
  this.root = null;
}

// _inOrderTraverseNode() : 재귀로 트리를 순회하며 중위 순회(내부 사용)
BinarySearchTree.prototype._inorderTraverseNode = function (node, callback) {
  if (node === null) {
    return;
  }

  this._inOrderTraverseNode(node.left, callback);
  callback(node);
  this._inOrderTraverseNode(node.right, callback);
};

// inOrderTraverse() : 중위 순회하며 노드 출력
BinarySearchTree.prototype.inOrderTraverse = function (callback) {
  this._inOrderTraverseNode(this.root, callback);
};

// _insertNode() : 재귀로 트리를 순회하면 노드 추가 (내부 사용)
BinarySearchTree.prototype._insertNode = function (node, value) {
  if (node === null) {
    node = new Node(value);
  } else if (value &lt; node.value) {
    node.left = this._insertNode(node.left, value);
  } else if (value &gt;= node.value) {
    node.right = this._insertNode(node.right, value);
  }
  return node;
};

// insert() : 노드 추가
BinarySearchTree.prototype.insert = function (value) {
  this.root = this._insertNode(this.root, value);
};

// _minNode() : 반복문으로 트리를 순회하며 최솟값 노드 탐색
BinarySearchTree.prototype._minNode = function (node) {
  if (node === null) {
    return null;
  }

  while (node &amp;&amp; node.left !== null) {
    node = node.left;
  }

  return node.value;
};

// _maxNode() : 반복문으로 트리를 순회하며 최댓값 노드 탐색
BinarySearchTree.prototype._maxNode = function (node) {
  if (node === null) {
    return null;
  }

  while (node &amp;&amp; node.right !== null) {
    node = node.right;
  }

  return node.value;
};

// min() : 최솟값 노드 탐색
BinarySearchTree.prototype.min = function () {
  return this._minNode(this.root);
};

// max() : 최댓값 노드 탐색
BinarySearchTree.prototype.max = function () {
  return this._maxNode(this.root);
};

// _searchNode() : 재귀로 트리를 순회하며 값을 만족하는 노드 탐색
BinarySearchTree.prototype._searchNode = function (node, value) {
  if (node === null) {
    return false;
  }

  if (node.value === value) {
    return true;
  } else if (node.value &gt; value) {
    return this._searchNode(node.left, value);
  } else if (node.value &lt; value) {
    return this._searchNode(node.right, value);
  }
};

// search() : value 노드 탐색
BinarySearchTree.prototype.search = function (value) {
  return this._searchNode(this.root, value);
};

// _finMinNode() : 반복문으로 트리를 순회하며 최솟값을 보유한 노드 탐색
BinarySearchTree.prototype._findMinNode = function (node) {
  while (node &amp;&amp; node.left !== null)
    node = node.left;

  return node;
};

// _removeNode() : 재귀로 트리를 순회하며 값을 만족하는 노드를 찾고 삭제
BinarySearchTree.prototype._removeNode = function (node, value) {
  if (node === null)
    return null;
  if (node.value === value) {
    // case 1 : leaf node
    if (node.left === null &amp;&amp; node.right === null) {
      node = null;
    }
    // case 2 : 1 child node
    else if (node.left === null) {
      node = node.right;
    } else if (node.right === null) {
      node = node.left;
    }
    // case 3 : 2 child node
    else {
      let aux = this._findMinNode(node.right);
      node.value = aux.value;
      node.right = this._removeNode(node.right, aux.value);
    }
  } else if (node.value &gt; value) {
    node.left = this._removeNode(node.left, value);
  } else if (node.value &lt; value) {
    node.right = this._removeNode(node.right, value);
  }
  return node;
};

// remove() : 노드 삭제
BinarySearchTree.prototype.remove = function (value) {
  root = this._removeNode(this.root, value);
};

</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[해시 테이블]]></title>
            <link>https://velog.io/@gang_shik/%ED%95%B4%EC%8B%9C-%ED%85%8C%EC%9D%B4%EB%B8%94</link>
            <guid>https://velog.io/@gang_shik/%ED%95%B4%EC%8B%9C-%ED%85%8C%EC%9D%B4%EB%B8%94</guid>
            <pubDate>Tue, 22 Apr 2025 10:04:51 GMT</pubDate>
            <description><![CDATA[<h3 id="해시테이블">해시테이블</h3>
<ul>
<li><p>먼저 해시함수란?</p>
<ul>
<li>임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수</li>
<li>해시 함수 특성<ul>
<li>압축성 : 다양한 가변 길이의 입력에 대해 고정된 크기의 결과값을 반환하는 성질</li>
<li>효율성 : 어떤 입력 값에 대해서도 많은 자원과 시간이 소요되지 않고 처리되는 성질</li>
<li>저항성 : 결과값을 바탕으로 입력 값을 찾는 것이 불가능한 성질</li>
</ul>
</li>
</ul>
</li>
<li><p>해시테이블</p>
<ul>
<li>Hash 함수를 사용하여 평균 O(1) 시간 복잡도로 특정 값을 신속하게 찾는 자료 구조</li>
<li>충돌(Collision) 해결방법<ul>
<li>해시 함수 변경 : 더 큰 숫자의 공간과 Modular 산술 값을 이용해 충돌 최소화</li>
<li>자료구조 확장 : Open Addressing Method(선형 조사법, 이중 해시), Close Addressing Method(체이닝)<pre><code class="language-javascript">const HASH_SIZE = 37;
</code></pre>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>// Element() : key, value 저장을 위한 생성자
function Element(key, value) {
  this.key = key;
  this.value = value;
}</p>
<p>// HashTable() : 생성자
function HashTable() {
  this.table = new Array(HASH_SIZE);
  this.length = 0;
}</p>
<p>// hashCode() : 해시 함수
HashTable.prototype.hashCode = function (key) {
  let hash = 0;
  for (let i = 0; i &lt; key.length; i++) {
    hash += key.charCodeAt(i);
  }
  return hash % HASH_SIZE;
};</p>
<p>// put() : 데이터 추가
HashTable.prototype.put = function (key, value) {
  let index = this.hashCode(key);
  console.log(<code>key: ${key} -&gt; index: ${index}</code>);</p>
<p>  if (this.table[index] !== undefined) {
    return false;
  }</p>
<p>  this.table[index] = new Element(key, value);
  this.length++;</p>
<p>  return true;
};</p>
<p>// get() : 데이터 조회
HashTable.prototype.get = function (key) {
  return this.table[this.hashCode(key)];
};</p>
<p>// remove() : 데이터 삭제
HashTable.prototype.remove = function (key) {
  let element = this.table[this.hashCode(key)];</p>
<p>  if (element !== undefined) {
    delete this.table[this.hashCode(key)];
    this.length--;
  }</p>
<p>  return element;
};</p>
<p>// clear() : 초기화
HashTable.prototype.clear = function () {
  this.table = new Array(HASH_SIZE);
  this.length = 0;
};</p>
<p>// size() : 크기 반환
HashTable.prototype.size = function () {
  return this.length;
};</p>
<p>// getBuffer() : 데이터 셋 반환
HashTable.prototype.getBuffer = function () {
  let array = [];
  for (let i = 0; i&lt; this.table.length; i++) {
    if (this.table[i]) {
      array.push(this.table[i]);
    }
  }
  return array;
};</p>
<p>// print() : 데이터 셋 출력
HashTable.prototype.print = function () {
  for (let i = 0; i &lt; this.table.length; i++) {
    if (this.table[i]) {
      console.log(i + &quot; -&gt; &quot; + this.table[i].key + &quot;: &quot; + this.table[i].value);
    }
  }
};</p>
<pre><code>- 충돌해결, 해시함수 변경을 통해서 충돌을 최소화함
```javascript
// loselose
const HASH_SIZE = 37;

// hashCode() : 해시 함수
HashTable.prototype.hashCode = function (key) {
  let hash = 0;
  for (let i = 0; i &lt; key.length; i++) {
    hash += key.charCodeAt(i);
  }
  return hash % HASH_SIZE;
};

// use djb2 hash size
const HASH_SIZE = 1013;

// hashCode() : 해시 함수
HashTable.prototype.hashCode = function (key) {
  let hash = 5381;
  for (let i = 0; i &lt; key.length; i++) {
    hash = hash * 33 + key.charCodeAt(i);
  }
  return hash % HASH_SIZE;
};</code></pre><h3 id="선형-조사법-해시테이블">선형 조사법 해시테이블</h3>
<ul>
<li>Hash 충돌이 발생했을 때, 그 다음 주소를 확인하고 비어 있다면 그 자리에 대신 저장하는 해시테이블 기반 자료구조<pre><code class="language-javascript">cosnt HASH_SIZE = 5; // 충돌을 위해 변경
</code></pre>
</li>
</ul>
<p>// Element() : Key, value 저장을 위한 생성자
function Element(key, value) {
  this.key = key;
  this.value = value;
}</p>
<p>// LinearHashTable() : 생성자
function LinearHashTable() {
  this.table = new Array(HASH_SIZE);
  this.length = 0;
}</p>
<p>// hashCode() : 해시 함수
LinearHashTable.prototype.hashCode = function (key) {
  let hash = 0;
  for (let i = 0; i &lt; key.length; i++) {
    hash += key.charCodeAt(i);
  }
  return hash % HASH_SIZE;
};</p>
<p>// clear() : 초기화
LinearHashTable.prototype.clear = function () {
  this.table = new Array(HASH_SIZE);
  this.length = 0;
};</p>
<p>// size() : 크기 반환
LinearHashTable.prototype.size = function () {
  return this.length;
};</p>
<p>// getBuffer() : 데이터 셋 반환
LinearHashTable.prototype.getBuffer = function () {
  let array = [];
  for (let i = 0; i &lt; this.table.length; i++) {
    if (this.table[i]) {
      array.push(this.table[i]);
    }
  }
  return array;
};</p>
<p>// print() : 데이터 셋 출력
LinearHashTable.prototype.print = function () {
  for (let i = 0; i &lt; this.table.length; i++) {
    if (this.table[i]) {
      console.log(i + &quot; -&gt; &quot; + this.table[i].key + &quot;: &quot; + this.table[i].value);
    }
  }
};</p>
<p>// put() : 데이터 추가
LinearHashTable.prototype.put = function (key, value) {
  let index = this.hashCode(key);
  let startIndex = index;
  console.log(<code>key: ${key} -&gt; index: ${index}</code>);</p>
<p>  do {
    if (this.table[index] === undefined) {
      this.table[index] = new Element(key, value);
      this.length++;</p>
<pre><code>  return true;
}

index = (index + 1) % HASH_SIZE;</code></pre><p>  } while (index !== startIndex);</p>
<p>  return false;
};</p>
<p>// get() : 데이터 조회
LinearHashTable.prototype.get = function (key) {
  let index = this.hashCode(key);
  let startIndex = index;</p>
<p>  do {
    if (this.table[index] !== undefined &amp;&amp; this.table[index].key === key) {
      return this.table[index].value;
    }</p>
<pre><code>index = (index + 1) % HASH_SIZE;</code></pre><p>  } while (index !== startIndex);</p>
<p>  return undefined;
};</p>
<p>// remove() : 데이터 삭제
LinearHashTable.prototype.remove = function (key) {
  let index = this.hashCode(key);
  let startIndex = index;</p>
<p>  do {
    if (this.table[index] !== undefined &amp;&amp; this.table[index].key === key) {
      let element = this.table[index];
      delete this.table[index];
      this.length--;</p>
<pre><code>  return element;
}

index = (index + 1) % HASH_SIZE;</code></pre><p>  } while (index !== startIndex);</p>
<p>  return undefined;
};</p>
<pre><code>
### 체이닝 해시테이블
- 별도의 자료구조인 연결리스트를 병합 사용하여 Hash 충돌을 해결한 해시테이블 기반 자료구조
```javascript
// 앞서 LinkedList를 모듈로 가져와서 사용

const HASH_SIZE = 37;

// Element() : key, value 저장을 위한 생성자
function Element(key, value) {
  this.key = key;
  this.value = value;
}

// ChainingHashTable() : 생성자
function ChainingHashTable() {
  this.table = new Array(HASH_SIZE);
  this.length = 0;
}

// hashCode() : 해시 함수
ChainingHashTable.prototype.hashCode = function (key) {
  let hash = 0;
  for (let i = 0; i &lt; key.length; i++) {
    hash += key.charCodeAt(i);
  }
  return hash % HASH_SIZE;
};

// clear() : 초기화
ChainingHashTable.prototype.clear = function () {
  this.table = new Array(HASH_SIZE);
  this.length = 0;
};

// size() : 크기 반환
ChainingHashTable.prototype.size = function () {
  return this.length;
};

// put() : 데이터 추가
ChainingHashTable.prototype.put = function (key, value) {
  let index = this.hashCode(key);
  console.log(`key: ${key} -&gt; index: ${index}`);

  if (this.table[index] === undefined) {
    this.table[index] = new LinkedList();
  }

  this.table[index].append(new Element(key, value));
  this.length++;

  return true;
};

// getBuffer() : 데이터 셋 반환
ChainingHashTable.prototype.getBuffer = function () {
  let array = [];
  for (let i = 0; i &lt; this.table.length; i++) {
    if (this.table[i]) {
      let current = this.table[i].head;
      do {
        array.push(current.data);
        current = current.next;
      } while (current);
    }
  }
  return array;
};

// print() : 데이터 셋 출력
ChainingHashTable.prototype.print = function () {
  for (let i = 0; i &lt; this.table.length; i++) {
    if (this.table[i]) {
      let current = this.table[i].head;
      process.stdout.write(`#${i}`);
      do {
        process.stdout.write(` -&gt; ${current.data.key}: ${current.data.value}`);
        current = current.next;
      } while (current);
      console.log(&quot;&quot;);
    }
  }
};

// get() : 데이터 조회
ChainingHashTable.prototype.get = function (key) {
  let index = this.hashCode(key);

  if (this.table[index] !== undefined &amp;&amp; !this.table[index].isEmpty()) {
    let current = this.table[index].head;

    do {
      if (current.data.key === key) {
        return current.data.value;
      }
      current = current.next;
    } while (current);
  }

  return undefined;
};

// remove() : 데이터 삭제
ChainingHashTable.prototype.remove = function (key) {
  let index = this.hashCode(key);
  let element = undefined;

  if (this.table[index] !== undefined) {
    let current = this.table[index].head;

    do {
      if (current.data.key === key) {
        element = current.data;
        this.table[index].remove(current.data);
        if (this.table[index].isEmpty()) {
          delete this.table[index];
        }
      }
      current = current.next;
    } while (current);
  }

  this.length--;
  return element;
};</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[우선순위 큐]]></title>
            <link>https://velog.io/@gang_shik/%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%ED%81%90</link>
            <guid>https://velog.io/@gang_shik/%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%ED%81%90</guid>
            <pubDate>Mon, 21 Apr 2025 07:21:30 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="우선순위-큐priority-queue">우선순위 큐(Priority Queue)</h1>
<hr>
<h2 id="1-우선순위-큐란">1. 우선순위 큐란?</h2>
<p>우선순위 큐는 <strong>각 요소에 우선순위가 부여</strong>되고, <strong>우선순위가 높은 요소가 먼저 처리</strong>되는 추상 자료형(ADT)입니다. 일반 큐(FIFO)와 달리 요소의 추가/제거 순서가 <strong>우선순위에 의해 결정</strong>되며, 동일한 우선순위인 경우에는 삽입 순서(FIFO)를 따름</p>
<h3 id="핵심-특징">핵심 특징</h3>
<ul>
<li><strong>우선순위 기반 처리</strong>: 높은 우선순위 요소가 먼저 제거됨.</li>
<li><strong>동적 구조</strong>: 요소 추가 시마다 내부적으로 재정렬됨.</li>
<li><strong>유연한 구현</strong>: 배열, 연결리스트, 힙(Heap) 등 다양한 자료구조로 구현 가능</li>
</ul>
<hr>
<h2 id="2-우선순위-큐의-주요-연산">2. 우선순위 큐의 주요 연산</h2>
<pre><code class="language-javascript">// Element() : 데이터와 우선순위를 저장하기 위한 생성자 함수
function Element(data, priority) {
  this.data = data;
  this.priority = priority;
}

// PriorityQueue() : Element 관리를 위한 생성자 함수
function PriorityQueue() {
  this.array = [];
}

// getBuffer() : 객체 내 데이터 셋 반환
PriorityQueue.prototype.getBuffer = function () {
  return this.array.map((element) =&gt; element.data);
};

// isEmpty() : 객체 내 데이터 존재 여부 파악
PriorityQueue.prototype.isEmpty = function () {
  return this.array.length == 0;
};

// enqueue() : 데이터 추가
PriorityQueue.prototype.enqueue = function (data, priority) {
  let element = new Element(data, priority);
  let added = false;

  for (let i = 0; i &lt; this.array.length; i++) {
    if (element.priority &lt; this.array[i].priority) {
      this.array.splice(i, 0, element);
      added = true;
      break;
    }
  }

  if (!added) {
    this.array.push(element);
  }

  return this.array.length;
};

// dequeue() : 데이터 삭제
PriorityQueue.prototype.dequeue = function () {
  return this.array.shift();
};

// front() : 가장 첫 데이터 반환
PriorityQueue.prototype.front = function () {
  return this.array.length == 0 ? undefined : this.array[0].data;
};

// size() : 큐 내 데이터 개수 확인
PriorityQueue.prototype.size = function () {
  return this.array.length;
};

// clear() : 큐 초기화
PriorityQueue.prototype.clear = function () {
  this.array = [];
};</code></pre>
<hr>
<h2 id="3-구현-방식별-특징">3. 구현 방식별 특징</h2>
<table>
<thead>
<tr>
<th>구현 방식</th>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td><strong>정렬된 배열</strong></td>
<td><code>peek()</code>이 O(1)</td>
<td>삽입/삭제 시 O(n) 재정렬 필요</td>
</tr>
<tr>
<td><strong>연결리스트</strong></td>
<td>동적 크기 관리 용이</td>
<td>중간 삽입 시 탐비 시간 증가</td>
</tr>
<tr>
<td><strong>힙(Heap)</strong></td>
<td>삽입/삭제 모두 O(log n)</td>
<td>구현 복잡성</td>
</tr>
</tbody></table>
<p><strong>힙이 가장 효율적</strong>으로 널리 사용되며, Python의 <code>queue.PriorityQueue</code>와 Java의 <code>PriorityQueue</code>도 내부적으로 힙을 사용함.</p>
<hr>
<h2 id="4-실제-활용-사례">4. 실제 활용 사례</h2>
<h3 id="1-운영체제-작업-스케줄링">1) 운영체제 작업 스케줄링</h3>
<ul>
<li>고우선순위 시스템 이벤트(예: 하드웨어 인터럽트)를 먼저 처리</li>
</ul>
<h3 id="2-네트워크-패킷-관리">2) 네트워크 패킷 관리</h3>
<ul>
<li>VoIP 데이터(낮은 지연 요구)를 일반 데이터보다 우선 전송</li>
</ul>
<h3 id="3-알고리즘-최적화">3) 알고리즘 최적화</h3>
<ul>
<li><strong>다익스트라 알고리즘</strong>: 최소 거리 노드 우선 처리</li>
<li><strong>A* 경로 탐색</strong>: 예상 비용이 낮은 경로 우선 탐색</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[스택]]></title>
            <link>https://velog.io/@gang_shik/%EC%8A%A4%ED%83%9D</link>
            <guid>https://velog.io/@gang_shik/%EC%8A%A4%ED%83%9D</guid>
            <pubDate>Fri, 18 Apr 2025 06:39:13 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="스택stack">스택(Stack)</h1>
<h2 id="1-스택이란">1. 스택이란?</h2>
<p><strong>스택(Stack)</strong>은 데이터를 쌓아 올린 형태의 <strong>선형 자료구조</strong>로, <strong>LIFO(Last In, First Out)</strong> 원칙을 따릅니다. 즉, 가장 마지막에 추가된 데이터가 가장 먼저 제거되는 구조<br>실생활에서는 접시를 쌓아두는 모습, 프링글스 통에서 감자칩을 꺼내는 모습이 대표적인 예</p>
<hr>
<h2 id="2-스택의-주요-연산">2. 스택의 주요 연산</h2>
<p>스택에서는 오직 한 쪽(Top)에서만 데이터의 추가와 삭제가 일어납니다.<br>주요 연산은 다음과 같습니다.</p>
<ul>
<li><strong>Push</strong>: 스택의 맨 위(Top)에 요소를 추가  </li>
<li><strong>Pop</strong>: 스택의 맨 위(Top) 요소를 제거 및 반환  </li>
<li><strong>Peek(Top)</strong>: 스택의 맨 위 요소를 제거하지 않고 확인  </li>
<li><strong>isEmpty</strong>: 스택이 비어있는지 확인  </li>
<li><strong>Size</strong>: 스택에 들어있는 요소의 개수 반환</li>
</ul>
<pre><code class="language-javascript">// Stack() : 생성자 함수로 초기 데이터 설정
function Stack(array) {
  this.array = array ? array : [];
}

// getBuffer() : 객체 내 데이터 셋 반환
Stack.prototype.getBuffer = function () {
  return this.array.slice();
}

// isEmpty() : 객체 내 데이터 존재 여부 파악
Stack.prototype.isEmpty = function () {
  return this.array.length == 0;
};

// push() : 데이터 추가
Stack.prototype.push = function (element) {
  return this.array.push(element);
}

// pop() : 데이터 삭제
Stack.prototype.pop = function () {
  return this.array.pop();
}

// peek() : 가장 끝 데이터 반환
Stack.prototype.peek = function () {
  return this.array[this.array.length - 1];
}

// size() : 스택 내 데이터 개수 확인
Stack.prototype.size = function () {
  return this.array.length;
}

// indexOf() : 데이터 위치 값 조회
Stack.prototype.indexOf = function (element, position = 0) {
  /* case 1 */
  // return this.array.indexOf(element, position);
  /* case 2 */
  for (let i = position; i &lt; this.array.length; i++) {
    if (element == this.array[i]) return i;
  }

  return -1;
};

// includes() : 데이터 존재 여부 확인
Stack.prototype.includes = function (element, position = 0) {
  /* case 1 */
  // return this.array.includes(element);
  /* case 2 */
  for (let i = position; i &lt; this.array.length; i++) {
    if (element == this.array[i]) return true;
  }

  return false;
};</code></pre>
<p>각 연산은 일반적으로 시간 복잡도 $$O(1)$$로 매우 빠르게 수행됨.</p>
<hr>
<h2 id="3-스택의-구현-방식">3. 스택의 구현 방식</h2>
<p>스택은 <strong>배열(Array)</strong>과 <strong>연결리스트(Linked List)</strong> 두 가지 방식으로 구현할 수 있음</p>
<table>
<thead>
<tr>
<th align="left">구현 방식</th>
<th align="left">특징</th>
</tr>
</thead>
<tbody><tr>
<td align="left">배열 기반</td>
<td align="left">고정 크기(Static), 빠른 인덱스, 접근 크기 제한, 오버플로우 발생 가능</td>
</tr>
<tr>
<td align="left">연결리스트 기반</td>
<td align="left">동적 크기(Dynamic), 메모리 효율적, 포인터 관리 필요, 구현이 다소 복잡</td>
</tr>
</tbody></table>
<ul>
<li><strong>배열 기반 스택</strong>: 크기가 고정되어 있어 미리 최대 크기를 정해야 하며, 인덱스 접근이 빠름.</li>
<li><strong>연결리스트 기반 스택</strong>: 크기 제한이 없고, 필요에 따라 메모리가 할당되어 유연함</li>
</ul>
<hr>
<h2 id="4-스택의-활용-사례">4. 스택의 활용 사례</h2>
<p>스택은 다양한 분야에서 핵심적인 역할을 합니다.</p>
<ul>
<li><p><strong>함수 호출 관리(Recursion/Call Stack)</strong><br>함수가 호출될 때마다 현재 상태를 스택에 저장, 함수 종료 시 스택에서 꺼내 복귀</p>
</li>
<li><p><strong>수식 계산 및 괄호 검사</strong><br>중위/후위/전위 표기법 변환, 괄호의 짝이 맞는지 검사할 때 활용</p>
</li>
<li><p><strong>브라우저 뒤로가기(Undo/Redo)</strong><br>사용자의 이전 작업 상태를 스택에 저장, 뒤로가기/앞으로가기 기능 구현</p>
</li>
<li><p><strong>문자열 역순 출력</strong><br>문자열을 한 글자씩 스택에 넣었다가 꺼내면 역순으로 출력 가능.</p>
</li>
<li><p><strong>운영체제의 프로세스 스케줄링</strong><br>프로세스의 상태 저장 및 복원에 활용</p>
</li>
</ul>
<hr>
<h2 id="5-스택의-장단점">5. 스택의 장단점</h2>
<h3 id="장점">장점</h3>
<ul>
<li>구현이 간단하고, 삽입/삭제 연산이 매우 빠름(O(1))</li>
<li>함수 호출, undo/redo 등 다양한 분야에서 필수적으로 사용</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>중간 요소에 직접 접근 불가(Top에서만 접근 가능)</li>
<li>배열 기반 스택은 크기 제한이 있을 수 있음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[연결리스트]]></title>
            <link>https://velog.io/@gang_shik/%EC%97%B0%EA%B2%B0%EB%A6%AC%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@gang_shik/%EC%97%B0%EA%B2%B0%EB%A6%AC%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Thu, 17 Apr 2025 07:20:14 GMT</pubDate>
            <description><![CDATA[<h1 id="연결리스트linked-list">연결리스트(Linked List)</h1>
<hr>
<h2 id="1-연결리스트란">1. 연결리스트란?</h2>
<p>연결리스트는 <strong>각 노드가 오직 한 방향(다음 노드)으로만 연결</strong>되어 있는 선형 자료구조<br>각 노드는 두 가지 정보를 가짐.</p>
<ul>
<li><strong>데이터(Data)</strong>: 저장하고자 하는 값</li>
<li><strong>포인터(Next)</strong>: 다음 노드를 가리키는 참조(주소)</li>
</ul>
<p>리스트의 시작은 <strong>헤드(Head)</strong> 노드로, 끝은 <strong>null(혹은 None)</strong>로 표시함.</p>
<h3 id="구조-예시">구조 예시</h3>
<pre><code>[Head] -&gt; [Node1] -&gt; [Node2] -&gt; [Node3] -&gt; null</code></pre><hr>
<h2 id="2-연결리스트의-장점과-단점">2. 연결리스트의 장점과 단점</h2>
<h3 id="장점">장점</h3>
<ul>
<li><strong>동적 크기</strong>: 필요에 따라 노드를 추가/삭제할 수 있어 메모리 효율적</li>
<li><strong>빠른 삽입/삭제</strong>: 특정 위치(특히 맨 앞)에서의 삽입/삭제가 빠름 (O(1))</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li><strong>느린 탐색</strong>: 임의 접근이 불가능해, 원하는 위치까지 순차적으로 접근해야 함 (O(n))</li>
<li><strong>추가 메모리 사용</strong>: 데이터 외에 포인터를 위한 메모리 필요</li>
</ul>
<hr>
<h2 id="3-연결리스트의-활용-예시">3. 연결리스트의 활용 예시</h2>
<h3 id="1-스택stack과-큐queue-구현">1) 스택(Stack)과 큐(Queue) 구현</h3>
<ul>
<li><strong>스택</strong>: Head에 push/pop을 하면 LIFO 구조를 쉽게 구현 가능</li>
<li><strong>큐</strong>: Head와 Tail 포인터를 활용해 FIFO 구조 구현</li>
</ul>
<h3 id="2-동적-데이터-관리">2) 동적 데이터 관리</h3>
<ul>
<li><strong>실시간 데이터 추가/삭제가 빈번한 작업</strong><br>예: 작업 리스트, 대기열 등</li>
</ul>
<h3 id="3-해시-테이블의-체이닝">3) 해시 테이블의 체이닝</h3>
<ul>
<li>해시 충돌 시, 같은 해시값을 가진 데이터를 단일 연결리스트로 관리</li>
</ul>
<hr>
<h2 id="4-연결리스트의-한계와-개선-방향">4. 연결리스트의 한계와 개선 방향</h2>
<ul>
<li><strong>역방향 탐색 불가</strong>: 한 방향으로만 이동 가능, 이전 노드로 돌아갈 수 없음</li>
<li><strong>임의 접근 비효율</strong>: 인덱스 기반 접근이 느림</li>
</ul>
<p>이런 한계를 극복하기 위해 <strong>이중 연결리스트(Doubly Linked List)</strong>, <strong>원형 연결리스트(Circular Linked List)</strong> 등이 고안됨.</p>
<hr>
<h2 id="구현">구현</h2>
<pre><code class="language-javascript">// Node() : data와 point를 가지고 있는 객체
function Node(data) {
  this.data = data;
  this.next = null;
}

// LinkedList(): head와 length를 가지고 있는 객체
function LinkedList() {
  this.head = null;
  this.length = 0;
}

// size() : 연결 리스트 내 노드 개수 확인
LinkedList.prototype.size = function () {
  return this.length;
}

// isEmpty() : 객체 내 노드 존재 여부 파악
LinkedList.prototype.isEmpty = function () {
  return this.length === 0;
}

// printNode() : 노드 출력
LinkedList.prototype.printNode = function () {
  for (let node = this.head; node != null; node = node.next) {
    process.stdout.write(`${node.data} -&gt; `);
  }
  console.log(&quot;null&quot;);
}

// append() : 연결 리스트 가장 끝에 노드 추가
LinkedList.prototype.append = function (value) {
  let node = new Node(value),
    current = this.head;

  if (this.head === null) {
    this.head = node;
  } else {
    while (current.next != null) {
      current = current.next;
    }
    current.next = node;
  }

  this.length++;
};

// insert() : position 위치에 노드 추가
LinkedList.prototype.insert = function (value, position = 0) {
  if (position &lt; 0 || position &gt; this.length) {
    return false;
  }

  let node = new Node(value),
    current = this.head,
    index = 0,
    prev;

  if (position == 0) {
    node.next = current;
    this.head = node;
  } else {
    while (index++ &lt; position) {
      prev = current;
      current = current.next;
    }

    node.next = current;
    prev.next = node;
  }

  this.length++;

  return true;
};

// remove() : value 데이터를 찾아 노드 삭제
LinkedList.prototype.remove = function (value) {
  let current = this.head,
    prev = current;

  while (current.data != value &amp;&amp; current.next != null) {
    prev = current;
    current = current.next;
  }

  if (current.data != value) {
    return null;
  }

  if (current === this.head) {
    this.head = current.next;
  } else {
    prev.next = current.next;
  }

  this.length--;

  return current.data;
};

// removeAt() : position 위치 노드 삭제
LinkedList.prototype.removeAt = function (position = 0) {
  if (position &lt; 0 || position &gt;= this.length) {
    return null;
  }

  let current = this.head,
    index = 0,
    prev;

  if (position == 0) {
    this.head = current.next;
  } else {
    while (index++ &lt; position) {
      prev = current;
      current = current.next;
    }

    prev.next = current.next;
  }

  this.length--;

  return current.data;
}

// indexOf() : value 값을 갖는 노드 위치 반환
LinkedList.prototype.indexOf = function (value) {
  let current = this.head,
    index = 0;

  while (current != null) {
    if (current.data === value) {
      return index;
    }

    index++;
    current = current.next;
  }

  return -1;
}

// remove2() : indexOf + removeAt = remove
LinkedList.prototype.remove2 = function (value) {
  let index = this.indexOf(value);
  return this.removeAt(index);
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 기본(시간 복잡도, 경우의 수, 순열, 조합, 점화식)]]></title>
            <link>https://velog.io/@gang_shik/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B8%B0%EB%B3%B8%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84-%EA%B2%BD%EC%9A%B0%EC%9D%98-%EC%88%98-%EC%88%9C%EC%97%B4-%EC%A1%B0%ED%95%A9-%EC%A0%90%ED%99%94%EC%8B%9D</link>
            <guid>https://velog.io/@gang_shik/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B8%B0%EB%B3%B8%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84-%EA%B2%BD%EC%9A%B0%EC%9D%98-%EC%88%98-%EC%88%9C%EC%97%B4-%EC%A1%B0%ED%95%A9-%EC%A0%90%ED%99%94%EC%8B%9D</guid>
            <pubDate>Wed, 16 Apr 2025 07:20:20 GMT</pubDate>
            <description><![CDATA[<h3 id="알고리즘-복잡도">알고리즘 복잡도</h3>
<ul>
<li><p>알고리즘 성능 평가 지표</p>
<ul>
<li>정확성</li>
<li>작업성</li>
<li>메모리 사용량</li>
<li>최적성</li>
<li>효율성<ul>
<li>시간 복잡도</li>
<li>공간 복잡도</li>
</ul>
</li>
</ul>
</li>
<li><p>시간 복잡도</p>
<ul>
<li>입력 크기의 값에 대해 단위 연산을 몇 번 수행하는지 계산하여, 알고리즘의 수행시간을 평가하는 방법</li>
<li>3가지 점근적 표현법<ul>
<li>빅오 : 최악의 상황을 고려하여 성능 측정 결과 표현</li>
<li>세타 : 평균적인 경우에서의 성능 측정 결과 표현</li>
<li>오메가 : 최선의 상황일 때의 성능 측정 결과 표현</li>
</ul>
</li>
<li>빅오 복잡도 차트를 보면 log일 때 퍼포먼스가 좋음</li>
</ul>
</li>
<li><p>빅오 표기법의 경우 for문 하나의 경우 n회로 따지고 수행 하나는 1회로 침</p>
</li>
<li><p>이 때 1+1+1 = 3이어도 O(1)임 n이 있다면 1+n+1 = n+2인데 O(n)임</p>
</li>
<li><p><a href="https://www.bigocheatsheet.com/">참고자료</a></p>
</li>
</ul>
<h3 id="경우의-수">경우의 수</h3>
<ul>
<li>어떤 사건 혹은 일이 일어날 수 있는 경우의 가짓수를 수로 표현</li>
<li>일상 생활에서의 경우의 수<ul>
<li>주사위 : 던지는 결과, 1 ~ 6 사이의 숫자이므로 경우의 수는 6</li>
<li>윷 : 던지는 결과, 도,개,걸,윷,모 이므로 경우의 수는 5</li>
<li>가위바위보 : 게임 결과, 가위, 바위, 보 중에 하나를 낼 수 있으므로 경우의 수는 3</li>
<li>동전 : 던지는 결과, 앞면 혹은 뒷면이므로 경우의 수는 2</li>
</ul>
</li>
<li>완전탐색으로 경우의 수를 푸는 알고리즘<ul>
<li>순열 : 서로 다른 n개의 원소 중에서 r를 중복 없이 골라 순서에 상관 있게 나열하는 경우의 수 nPr</li>
<li>조합 : 서로 다른 n개의 원소 중에서 r를 중복 없이 골라 순서에 상관 없이 나열하는 경우의 수 nCr</li>
<li>중복 순열 : 서로 다른 n개의 원소 중에서 r개를 중복 있게 골라 순서에 상관 있게 나열하는 경우의 수 nHr<pre><code class="language-javascript">// 순열 예제
let input = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;];
let count = 0;
</code></pre>
</li>
</ul>
</li>
</ul>
<p>function permutation(arr) {
  for (let i = 0; i &lt; arr.length; i++) {
    for (let j = 0; j &lt; arr.length; j++) {
      if (i == j) continue;
      for (let k = 0; k &lt; arr.length; k++) {
        if (i == k) continue;
        if (j == k) continue;</p>
<pre><code>    console.log(arr[i], arr[j], arr[k]);
    count++;
  }
}</code></pre><p>  }
}</p>
<p>permutation(input);</p>
<pre><code>```javascript
// 순열 예제(재귀 활용)
let input = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;];
let count = 0;

function permutation(arr, s, r) {
  if (s == r) {
    count++;
    console.log(arr.join(&quot; &quot;));
    return;
  }

  for (let i = s; i &lt; arr.length; i++) {
    [arr[s], arr[i]] = [arr[i], arr[s]];
    permutation(arr, s + 1, r);
    [arr[s], arr[i]] = [arr[i], arr[s]];
  }
}

permutation(input, 0, 2);</code></pre><pre><code class="language-javascript">// 조합 예제
let input = [1, 2, 3, 4];
let count = 0;

function combination(arr) {
  for (let i = 0; i &lt; arr.length; i++) {
    for (let j = i + 1; j &lt; arr.length; j++) {
      count++;
      console.log(arr[i], arr[j]);
    }
  }
}

combination(input);</code></pre>
<pre><code class="language-javascript">// 조합 예제(재귀 활용)
let input = [1, 2, 3, 4];
let output = [];
let count = 0;

function combination(arr, data, s, idx, r) {
  if (s == r) {
    count++;
    console.log(data);
    return;
  }

  for (let i = idx; arr.length - i &gt;= r - s; i++) {
    data[s] = arr[i];
    combination(arr, data, s + 1, i + 1, r);
  }
}

combination(input, output, 0, 0, 2);</code></pre>
<h3 id="점화식">점화식</h3>
<ul>
<li>점화식(재귀식)이란 수열에서 이웃하는 두개의 항 사이에 성립하는 관계를 나타낸 관계식</li>
<li>대표적인 점화식<ul>
<li>등차수열 : F(n) = F(n - 1) + a</li>
<li>등비수열 : F(n) = F(n - 1) * a</li>
<li>팩토리얼 : F(n) = F(n - 1) * n</li>
<li>피보나치 수열 : F(n) = F(n - 1) + F(n - 2)</li>
</ul>
</li>
<li>등차수열 예제<pre><code class="language-javascript">let result;
</code></pre>
</li>
</ul>
<p>function forloop(s, t, number) {
  let acc = 0;</p>
<p>  for (let i = 1; i &lt;= number; i++) {
    if (i == 1)
      acc += s;
    else
      acc += t;</p>
<pre><code>console.log(i, acc);</code></pre><p>  }</p>
<p>  return acc;
}</p>
<p>result = forloop(3, 2, 5);
console.log(result);</p>
<pre><code>```javascript
let result;

function recursive(s, t, number) {
  if (number == 1) {
    return s;
  }

  return recursive(s, t, number - 1) + t;
}

result = recursive(3, 2, 5);
console.log(result);</code></pre><ul>
<li>등비수열 예제<pre><code class="language-javascript">let result;
</code></pre>
</li>
</ul>
<p>function forloop(s, t, number) {
  let acc = 0;</p>
<p>  for (let i = 1; i &lt;= number; i++) {
    if (i == 1)
      acc *= s;
    else
      acc *= t;</p>
<pre><code>console.log(i, acc);</code></pre><p>  }</p>
<p>  return acc;
}</p>
<p>result = forloop(3, 2, 5);
console.log(result);</p>
<pre><code>```javascript
let result;

function recursive(s, t, number) {
  if (number == 1) {
    return s;
  }

  return recursive(s, t, number - 1) * t;
}

result = recursive(3, 2, 5);
console.log(result);</code></pre><ul>
<li>팩토리얼 예제<pre><code class="language-javascript">let result;
</code></pre>
</li>
</ul>
<p>function recursive(number) {
  if (number == 1) {
    return number;
  }</p>
<p>  return recursive(number - 1) * number;
}</p>
<p>result = recursive(5);
console.log(result);</p>
<pre><code>- 피보나치 수열 예제
```javascript
let result;

function recursive(number) {
  if (number == 1 || number == 0) {
    return number;
  }

  return recursive(number - 1) + recursive(number - 2);
}

result = recursive(5);
console.log(result);</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[이벤트 위임 & 버블링]]></title>
            <link>https://velog.io/@gang_shik/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%9C%84%EC%9E%84-%EB%B2%84%EB%B8%94%EB%A7%81</link>
            <guid>https://velog.io/@gang_shik/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%9C%84%EC%9E%84-%EB%B2%84%EB%B8%94%EB%A7%81</guid>
            <pubDate>Thu, 27 Mar 2025 06:48:56 GMT</pubDate>
            <description><![CDATA[<h3 id="이벤트-위임과-이벤트-버블링-정리"><strong>이벤트 위임과 이벤트 버블링 정리</strong></h3>
<hr>
<h3 id="1-이벤트-위임-event-delegation"><strong>1. 이벤트 위임 (Event Delegation)</strong></h3>
<h4 id="개념"><strong>개념</strong></h4>
<ul>
<li>이벤트 위임은 부모 요소에 단일 이벤트 리스너를 추가하여 자식 요소의 이벤트를 처리하는 방식.</li>
<li>이벤트 버블링을 활용하여 자식 요소에서 발생한 이벤트가 부모 요소로 전파되도록 함.</li>
</ul>
<h4 id="장점"><strong>장점</strong></h4>
<ol>
<li><strong>성능 최적화</strong>:<ul>
<li>많은 자식 요소에 각각 이벤트 리스너를 추가할 필요 없이 부모에 단일 리스너만 추가하면 됨.</li>
</ul>
</li>
<li><strong>동적 요소 지원</strong>:<ul>
<li>DOM에 새로 추가된 자식 요소도 부모의 이벤트 리스너로 처리할 수 있음.</li>
</ul>
</li>
</ol>
<h4 id="단점"><strong>단점</strong></h4>
<ol>
<li><strong>추가 조건 필요</strong>:<ul>
<li>이벤트가 버블링되므로, 특정 자식 요소만 처리하려면 조건문(<code>if (event.target.tagName === &#39;I&#39;)</code>)을 추가해야 함.</li>
</ul>
</li>
<li><strong>복잡성 증가</strong>:<ul>
<li>단순한 구조에서는 개별 리스너를 사용하는 것이 더 직관적일 수 있음.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="2-이벤트-버블링-event-bubbling"><strong>2. 이벤트 버블링 (Event Bubbling)</strong></h3>
<h4 id="개념-1"><strong>개념</strong></h4>
<ul>
<li>DOM 이벤트 모델에서, 이벤트가 발생하면 가장 안쪽의 요소(타겟)에서 시작하여 부모 요소로 전파됨.</li>
<li>예를 들어, 자식 요소에서 <code>click</code> 이벤트가 발생하면 해당 이벤트는 부모, 조부모 등 상위 요소로 전달됨.</li>
</ul>
<h4 id="이벤트-캡처와의-차이"><strong>이벤트 캡처와의 차이</strong></h4>
<ul>
<li><strong>캡처(Capturing)</strong>: 이벤트가 최상위 요소에서 시작하여 타겟 요소로 전파됨.</li>
<li><strong>버블링(Bubbling)</strong>: 타겟 요소에서 시작하여 최상위 요소로 전파됨.</li>
</ul>
<hr>
<h3 id="3-mouseenter-vs-mouseover"><strong>3. <code>mouseenter</code> vs <code>mouseover</code></strong></h3>
<table>
<thead>
<tr>
<th>특성</th>
<th><code>mouseenter</code></th>
<th><code>mouseover</code></th>
</tr>
</thead>
<tbody><tr>
<td><strong>버블링</strong></td>
<td>버블링되지 않음</td>
<td>버블링됨</td>
</tr>
<tr>
<td><strong>트리거 조건</strong></td>
<td>자신만 트리거</td>
<td>자신과 자식 모두 트리거</td>
</tr>
<tr>
<td><strong>사용 사례</strong></td>
<td>자식 요소 무시</td>
<td>자식 포함 처리</td>
</tr>
</tbody></table>
<ul>
<li><code>mouseenter</code>: 마우스가 해당 요소에 진입할 때만 트리거되며, 자식 요소에는 영향을 받지 않음.</li>
<li><code>mouseover</code>: 마우스가 해당 요소 또는 그 자식 요소에 진입할 때마다 트리거됨.</li>
</ul>
<h4 id="장단점-비교"><strong>장단점 비교</strong></h4>
<ul>
<li><strong><code>mouseenter</code></strong>:<ul>
<li>버블링되지 않으므로 불필요한 이벤트 트리거를 방지할 수 있음.</li>
<li>자식 요소가 없는 구조에서 효율적임.</li>
</ul>
</li>
<li><strong><code>mouseover</code></strong>:<ul>
<li>버블링되므로 부모 컨테이너에서 자식 요소의 이벤트를 감지할 수 있음.</li>
<li>이벤트 위임 방식에 적합함.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="4-mouseleave-vs-mouseout"><strong>4. <code>mouseleave</code> vs <code>mouseout</code></strong></h3>
<table>
<thead>
<tr>
<th>특성</th>
<th><code>mouseleave</code></th>
<th><code>mouseout</code></th>
</tr>
</thead>
<tbody><tr>
<td><strong>버블링</strong></td>
<td>버블링되지 않음</td>
<td>버블링됨</td>
</tr>
<tr>
<td><strong>트리거 조건</strong></td>
<td>자신만 트리거</td>
<td>자신과 자식 모두 트리거</td>
</tr>
<tr>
<td><strong>사용 사례</strong></td>
<td>자식 요소 무시</td>
<td>자식 포함 처리</td>
</tr>
</tbody></table>
<ul>
<li><code>mouseleave</code>: 마우스가 해당 요소 전체에서 벗어날 때만 트리거되며, 자식 요소에는 영향을 받지 않음.</li>
<li><code>mouseout</code>: 마우스가 해당 요소 또는 그 자식 요소에서 벗어날 때마다 트리거됨.</li>
</ul>
<h4 id="장단점-비교-1"><strong>장단점 비교</strong></h4>
<ul>
<li><strong><code>mouseleave</code></strong>:<ul>
<li>버블링되지 않으므로 불필요한 이벤트 트리거를 방지할 수 있음.</li>
<li>자식 요소가 없는 구조에서 효율적.</li>
</ul>
</li>
<li><strong><code>mouseout</code></strong>:<ul>
<li>버블링되므로 부모 컨테이너에서 자식 요소의 이벤트를 감지할 수 있음.</li>
<li>이벤트 위임 방식에 적합함.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="코드-비교-및-분석"><strong>코드 비교 및 분석</strong></h3>
<h4 id="원래-코드-개별-리스너--mouseenter-mouseleave">원래 코드 (개별 리스너 + <code>mouseenter</code>, <code>mouseleave</code>)</h4>
<pre><code class="language-javascript">stars.forEach((star) =&gt; {
  star.addEventListener(&#39;mouseenter&#39;, handleMouseEnter);
  star.addEventListener(&#39;mouseleave&#39;, handleMouseLeave);
  star.addEventListener(&#39;click&#39;, handleClick);
});</code></pre>
<ul>
<li>각 별(<code>i</code>) 태그에 개별적으로 리스너를 추가.</li>
<li>이 방식은 구조가 단순하고 코드 가독성이 높음.</li>
<li>별점 개수가 많아질 경우 성능 저하 가능성이 있음.</li>
</ul>
<hr>
<h4 id="중간-코드-이벤트-위임--mouseover-mouseout">중간 코드 (이벤트 위임 + <code>mouseover</code>, <code>mouseout</code>)</h4>
<pre><code class="language-javascript">container.addEventListener(&#39;mouseover&#39;, (event) =&gt; {
  if (event.target.tagName === &#39;I&#39;) {
    const ratingValue = parseInt(event.target.dataset.ratingValue, 10);
    stars.forEach((star, index) =&gt; {
      star.classList.toggle(&#39;hovered&#39;, index  {
  stars.forEach((star) =&gt; star.classList.remove(&#39;hovered&#39;));
});

container.addEventListener(&#39;click&#39;, (event) =&gt; {
  if (event.target.tagName === &#39;I&#39;) {
    const ratingValue = parseInt(event.target.dataset.ratingValue, 10);
    stars.forEach((star, index) =&gt; {
      star.classList.toggle(&#39;selected&#39;, index &lt; ratingValue);
    });

    const ratingChangeEvent = new CustomEvent(&#39;rating-change&#39;, {
      detail: ratingValue,
    });
    container.dispatchEvent(ratingChangeEvent);
  }
});</code></pre>
<ul>
<li>부모 컨테이너(<code>container</code>)에 단일 리스너를 추가하여 모든 별의 이벤트를 처리함.</li>
<li>동적으로 추가된 별점도 자동으로 처리 가능.</li>
<li>성능 최적화 측면에서 유리함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[beforeunload 이벤트를 활용한 상태 저장]]></title>
            <link>https://velog.io/@gang_shik/beforeunload-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%83%81%ED%83%9C-%EC%A0%80%EC%9E%A5</link>
            <guid>https://velog.io/@gang_shik/beforeunload-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%83%81%ED%83%9C-%EC%A0%80%EC%9E%A5</guid>
            <pubDate>Thu, 27 Mar 2025 06:03:39 GMT</pubDate>
            <description><![CDATA[<h2 id="beforeunload-이벤트를-활용한-상태-저장"><strong>beforeunload 이벤트를 활용한 상태 저장</strong></h2>
<h3 id="1-beforeunload-이벤트란"><strong>1. beforeunload 이벤트란?</strong></h3>
<ul>
<li><code>beforeunload</code>는 사용자가 페이지를 떠나거나 새로고침하려고 할 때 발생하는 브라우저 이벤트.</li>
<li>이 이벤트를 활용하면 페이지를 벗어나는 시점에 특정 작업(예: 상태 저장)을 수행할 수 있음.</li>
<li>주로 다음과 같은 경우에 사용됨:<ul>
<li>세션 상태 저장</li>
<li>사용자 데이터를 서버에 전송</li>
<li>사용자에게 경고 메시지 표시 (예: &quot;페이지를 떠나시겠습니까?&quot;)</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-현재-코드에서의-활용"><strong>2. 현재 코드에서의 활용</strong></h3>
<p>현재 코드에서는 <code>beforeunload</code> 이벤트를 통해 사이드 내비게이션의 활성화 상태를 저장하고 있습니다. 이 과정은 다음과 같은 흐름으로 이루어짐:</p>
<h4 id="1-상태-확인"><strong>(1) 상태 확인</strong></h4>
<ul>
<li><code>nav</code> 요소의 <code>.active</code> 클래스가 존재하는지 확인하여 사이드 내비게이션이 열려 있는지 여부를 판단함</li>
</ul>
<h4 id="2-로컬-스토리지에-상태-저장"><strong>(2) 로컬 스토리지에 상태 저장</strong></h4>
<ul>
<li><code>localStorage.setItem(&quot;navActive&quot;, isCurrentlyActive)</code>를 사용하여 사이드 내비게이션의 상태를 저장함.</li>
<li>키(<code>navActive</code>)와 값(<code>true</code> 또는 <code>false</code>)을 통해 상태를 기록함.</li>
</ul>
<h4 id="3-beforeunload-이벤트-등록"><strong>(3) beforeunload 이벤트 등록</strong></h4>
<ul>
<li><code>window.addEventListener(&quot;beforeunload&quot;, ...)</code>를 통해 페이지가 떠나는 시점에 위의 상태 확인 및 저장 작업을 실행함.</li>
</ul>
<hr>
<h3 id="3-코드-흐름-설명"><strong>3. 코드 흐름 설명</strong></h3>
<pre><code class="language-javascript">window.addEventListener(&quot;beforeunload&quot;, () =&gt; {
  const isCurrentlyActive = nav.classList.contains(&quot;active&quot;);
  localStorage.setItem(&quot;navActive&quot;, isCurrentlyActive);
});</code></pre>
<h4 id="코드-실행-흐름"><strong>코드 실행 흐름</strong>:</h4>
<ol>
<li>사용자가 페이지를 떠나거나 새로고침하려고 할 때 <code>beforeunload</code> 이벤트가 발생함.</li>
<li>현재 내비게이션의 활성화 상태(<code>nav.classList.contains(&quot;active&quot;)</code>)를 확인.</li>
<li>로컬 스토리지에 해당 상태(<code>true</code> 또는 <code>false</code>)를 저장함.</li>
</ol>
<hr>
<h3 id="4-beforeunload와-unmount의-차이점"><strong>4. beforeunload와 unmount의 차이점</strong></h3>
<p>피드백에서 언급된 &quot;상태 저장은 unmount 시점에서 한 번만 하면 될 것 같다&quot;는 내용은 다음과 같은 차이를 고려해야 함:</p>
<h4 id="beforeunload"><strong>beforeunload</strong>:</h4>
<ul>
<li>브라우저 이벤트로, 페이지가 떠나는 시점에 실행됨.</li>
<li>클라이언트 측에서만 동작하며, 서버와 연동되지 않음.</li>
</ul>
<h4 id="unmount"><strong>Unmount</strong>:</h4>
<ul>
<li>React와 같은 프레임워크에서 컴포넌트가 DOM에서 제거될 때 발생하는 라이프사이클 메서드입니다 (<code>componentWillUnmount()</code>).</li>
<li>특정 컴포넌트 단위로 실행되며, 더 세부적인 제어가 가능.</li>
</ul>
<hr>
<h3 id="5-beforeunload-사용-시-고려사항"><strong>5. beforeunload 사용 시 고려사항</strong></h3>
<ol>
<li><p><strong>다른 도메인 간 데이터 공유</strong>:</p>
<ul>
<li><code>localStorage</code>는 동일한 도메인에서만 접근 가능하므로 다른 도메인에서는 사용할 수 없음.</li>
<li>크로스 도메인 데이터 공유가 필요하다면 쿠키 또는 서버 측 API와 연동해야함.</li>
</ul>
</li>
<li><p><strong>보안 문제</strong>:</p>
<ul>
<li>민감한 데이터를 저장할 경우 XSS 공격에 취약할 수 있음.</li>
<li>민감한 정보는 반드시 서버 측에서 관리.</li>
</ul>
</li>
<li><p><strong>사용 사례 적합성</strong>:</p>
<ul>
<li>현재 요구사항에서는 간단히 사이드 내비게이션 상태만 저장하므로 <code>localStorage</code>와 <code>beforeunload</code> 조합이 적합함.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="6-결론"><strong>6. 결론</strong></h3>
<p>현재 구현된 코드에서 <code>beforeunload</code>는 사용자가 페이지를 떠날 때 사이드 내비게이션의 활성화 상태를 로컬 스토리지에 저장하는 데 적합합니다. 다만, 다른 도메인 간 데이터 공유가 필요하거나 보안이 중요한 경우에는 쿠키 또는 서버 측 API를 사용하는 것이 더 적합할 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[localStorage vs SessionStorage vs Cookies]]></title>
            <link>https://velog.io/@gang_shik/localStorage-vs-SessionStorage-vs-Cookies</link>
            <guid>https://velog.io/@gang_shik/localStorage-vs-SessionStorage-vs-Cookies</guid>
            <pubDate>Thu, 27 Mar 2025 06:02:41 GMT</pubDate>
            <description><![CDATA[<h2 id="localstorage-vs-sessionstorage-vs-cookies">localStorage vs sessionStorage vs Cookies</h2>
<p><code>localStorage</code>, <code>sessionStorage</code>, 그리고 <code>Cookies</code>는 모두 브라우저에서 데이터를 저장하는 데 사용되지만, 각각의 특성과 사용 사례가 다름</p>
<hr>
<h2 id="비교표-localstorage-vs-sessionstorage-vs-cookies"><strong>비교표: localStorage vs sessionStorage vs Cookies</strong></h2>
<table>
<thead>
<tr>
<th><strong>특징</strong></th>
<th><strong>localStorage</strong></th>
<th><strong>sessionStorage</strong></th>
<th><strong>Cookies</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>저장 용량</strong></td>
<td>약 5-10MB (브라우저마다 다름)</td>
<td>약 5-10MB (브라우저마다 다름)</td>
<td>약 4KB (도메인당 제한)</td>
</tr>
<tr>
<td><strong>수명</strong></td>
<td>명시적으로 삭제하지 않는 한 영구적으로 저장</td>
<td>브라우저 탭이나 창을 닫으면 삭제됨</td>
<td>설정된 만료 시간까지 유지 가능. 만료 시간이 없으면 브라우저 종료 시 삭제됨</td>
</tr>
<tr>
<td><strong>데이터 전송</strong></td>
<td>HTTP 요청과 함께 전송되지 않음</td>
<td>HTTP 요청과 함께 전송되지 않음</td>
<td>모든 HTTP 요청에 포함되어 서버로 전송됨</td>
</tr>
<tr>
<td><strong>접근성</strong></td>
<td>클라이언트 측에서만 접근 가능</td>
<td>클라이언트 측에서만 접근 가능</td>
<td>클라이언트와 서버 모두에서 접근 가능</td>
</tr>
<tr>
<td><strong>보안</strong></td>
<td>XSS(크로스 사이트 스크립팅)에 취약. 민감한 데이터 저장에 부적합</td>
<td>XSS에 취약. 민감한 데이터 저장에 부적합</td>
<td>HttpOnly, Secure 플래그를 설정하면 보안 강화 가능. 하지만 CSRF(크로스 사이트 요청 위조)에 취약</td>
</tr>
<tr>
<td><strong>스코프</strong></td>
<td>동일한 도메인 및 프로토콜 내에서만 접근 가능</td>
<td>동일한 도메인 및 프로토콜 내에서만 접근 가능</td>
<td>도메인 전체와 하위 도메인에서도 접근 가능</td>
</tr>
<tr>
<td><strong>사용 사례</strong></td>
<td>사용자 설정, 애플리케이션 상태, SPA(Single Page Application)의 데이터</td>
<td>일회성 데이터, 현재 세션 동안만 필요한 데이터</td>
<td>인증 토큰, 세션 관리, 크로스 도메인/서브도메인 데이터 공유</td>
</tr>
</tbody></table>
<hr>
<h2 id="상황별-사용-사례"><strong>상황별 사용 사례</strong></h2>
<h3 id="1-localstorage">1. <strong>localStorage</strong></h3>
<ul>
<li><strong>특징</strong>: 영구적으로 데이터를 저장하며, 브라우저를 닫아도 데이터가 유지.</li>
<li><strong>사용 사례</strong>:<ul>
<li>사용자 환경설정 저장 (예: 다크 모드, 언어 설정)</li>
<li>SPA(Single Page Application)에서 상태 관리</li>
<li>페이지 간 데이터 공유 (동일 도메인 내)</li>
</ul>
</li>
</ul>
<h3 id="2-sessionstorage">2. <strong>sessionStorage</strong></h3>
<ul>
<li><strong>특징</strong>: 현재 브라우저 탭이나 창이 열려 있는 동안만 데이터를 유지.</li>
<li><strong>사용 사례</strong>:<ul>
<li>일시적인 폼 데이터 저장</li>
<li>탭 간 분리된 상태 관리</li>
<li>페이지 이동 중 임시 데이터 저장</li>
</ul>
</li>
</ul>
<h3 id="3-cookies">3. <strong>Cookies</strong></h3>
<ul>
<li><strong>특징</strong>: 서버와 클라이언트 간 데이터를 주고받을 수 있으며, 만료 시간 설정이 가능.</li>
<li><strong>사용 사례</strong>:<ul>
<li>세션 관리 및 인증 토큰 저장</li>
<li>크로스 도메인/서브도메인 데이터 공유</li>
<li>자동 로그인 구현</li>
</ul>
</li>
</ul>
<hr>
<h2 id="보안-고려사항"><strong>보안 고려사항</strong></h2>
<h3 id="localstorage와-sessionstorage">localStorage와 sessionStorage</h3>
<ul>
<li>XSS 공격에 취약하여 민감한 데이터를 저장하는 데 적합하지 않음.</li>
<li>데이터를 암호화하거나 민감한 정보는 서버 측으로 이동하는 것이 좋음.</li>
</ul>
<h3 id="cookies">Cookies</h3>
<ul>
<li>HttpOnly 및 Secure 플래그를 사용하면 보안을 강화할 수 있음</li>
<li>CSRF 공격 방지를 위해 SameSite 플래그를 설정하는 것이 권장됨.</li>
</ul>
<hr>
<h2 id="결론-및-선택-기준"><strong>결론 및 선택 기준</strong></h2>
<ol>
<li>민감한 데이터를 저장해야 한다면 Cookies를 사용하는 것이 가장 적합함</li>
<li>사용자 환경설정이나 애플리케이션 상태처럼 클라이언트 측에서만 필요한 데이터를 저장하려면 localStorage가 적합함</li>
<li>일회성 데이터나 세션 동안만 필요한 데이터를 저장하려면 sessionStorage를 선택</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[DOMContentLoaded와 onload의 차이]]></title>
            <link>https://velog.io/@gang_shik/DOMContentLoaded%EC%99%80-onload%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@gang_shik/DOMContentLoaded%EC%99%80-onload%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Thu, 27 Mar 2025 06:01:50 GMT</pubDate>
            <description><![CDATA[<h2 id="domcontentloaded와-onload의-차이">DOMContentLoaded와 onload의 차이</h2>
<p><code>DOMContentLoaded</code>와 <code>window.onload</code>의 차이는 주로 <strong>이벤트 발생 시점</strong>과 <strong>사용 사례</strong>에 따라 나뉨.</p>
<hr>
<h2 id="차이점-요약"><strong>차이점 요약</strong></h2>
<table>
<thead>
<tr>
<th><strong>특징</strong></th>
<th><strong>DOMContentLoaded</strong></th>
<th><strong>window.onload</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>발생 시점</strong></td>
<td>DOM 트리가 완전히 로드되고 파싱된 직후 발생 (스타일시트, 이미지 등 외부 리소스는 로드되지 않아도 됨)</td>
<td>페이지의 모든 리소스(이미지, 스타일시트, 서브프레임 등)가 완전히 로드된 후 발생</td>
</tr>
<tr>
<td><strong>속도</strong></td>
<td>더 빠르게 실행됨</td>
<td>DOMContentLoaded보다 느리게 실행됨</td>
</tr>
<tr>
<td><strong>사용 사례</strong></td>
<td>DOM 조작이나 초기화 작업이 필요할 때 사용</td>
<td>외부 리소스가 모두 로드된 후 작업이 필요할 때 사용</td>
</tr>
<tr>
<td><strong>코드 예시</strong></td>
<td><code>document.addEventListener(&#39;DOMContentLoaded&#39;, () =&gt; { ... });``````window.onload = () =&gt; { ... };</code></td>
<td></td>
</tr>
<tr>
<td><strong>브라우저 지원 및 제한</strong></td>
<td>대부분의 브라우저에서 지원</td>
<td>브라우저마다 약간의 차이가 있지만 거의 모든 환경에서 지원</td>
</tr>
</tbody></table>
<hr>
<h2 id="세부-설명"><strong>세부 설명</strong></h2>
<h3 id="1-domcontentloaded">1. <strong>DOMContentLoaded</strong></h3>
<ul>
<li>이 이벤트는 브라우저가 HTML 문서를 완전히 로드하고 DOM 트리를 생성한 직후 발생</li>
<li>스타일시트, 이미지, 서브프레임과 같은 외부 리소스는 로드되지 않아도 이벤트가 발생</li>
<li>주요 사용 사례:<ul>
<li>DOM 요소를 조작하거나 초기화 작업을 수행해야 할 때.</li>
<li>외부 리소스 로드 여부와 상관없이 빠르게 동작해야 하는 스크립트를 실행할 때.</li>
</ul>
</li>
<li>예시:<pre><code class="language-javascript">document.addEventListener(&#39;DOMContentLoaded&#39;, () =&gt; {
  console.log(&#39;DOM is fully loaded and parsed&#39;);
  // DOM 조작 코드
});</code></pre>
</li>
</ul>
<h3 id="2-windowonload">2. <strong>window.onload</strong></h3>
<ul>
<li>이 이벤트는 페이지의 모든 리소스(이미지, 스타일시트, 서브프레임 등)가 완전히 로드된 후에 발생</li>
<li>DOMContentLoaded보다 느리게 실행되며, 모든 외부 자원이 필요할 때 유용합니다.</li>
<li>주요 사용 사례:<ul>
<li>이미지 크기 계산이나 레이아웃 관련 작업처럼 외부 리소스가 모두 준비된 후에 실행해야 할 작업.</li>
<li>페이지의 모든 콘텐츠가 로드된 상태에서 특정 작업을 수행해야 할 때.</li>
</ul>
</li>
<li>예시:<pre><code class="language-javascript">window.onload = () =&gt; {
  console.log(&#39;Page is fully loaded&#39;);
  // 외부 리소스를 포함한 작업
};</code></pre>
</li>
</ul>
<hr>
<h2 id="적합한-사용-시점"><strong>적합한 사용 시점</strong></h2>
<h3 id="언제-domcontentloaded를-사용할까">언제 <code>DOMContentLoaded</code>를 사용할까?</h3>
<ul>
<li>DOM 요소를 즉시 조작하거나 초기화해야 하는 경우.</li>
<li>외부 리소스(이미지, CSS 등)가 필요하지 않은 작업.</li>
</ul>
<h3 id="언제-windowonload를-사용할까">언제 <code>window.onload</code>를 사용할까?</h3>
<ul>
<li>이미지나 스타일시트와 같은 외부 리소스가 모두 준비된 후에 작업해야 하는 경우.</li>
<li>예를 들어, 이미지 슬라이더나 레이아웃 계산 작업.</li>
</ul>
<hr>
<h2 id="결론"><strong>결론</strong></h2>
<p>대부분의 경우에는 <code>DOMContentLoaded</code>를 사용하는 것이 더 적합함. 이는 DOM이 준비되는 즉시 JavaScript를 실행할 수 있어 사용자 경험을 개선하고 페이지 로드를 빠르게 느끼게 하기 때문임. 그러나 모든 외부 리소스를 기다려야 하는 특정 상황에서는 <code>window.onload</code>를 사용하는 것이 적합함. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS 레이아웃 최적화]]></title>
            <link>https://velog.io/@gang_shik/CSS-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@gang_shik/CSS-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Sun, 23 Mar 2025 02:58:04 GMT</pubDate>
            <description><![CDATA[<h1 id="css-레이아웃-최적화-가변적이고-단순한-단위-사용">CSS 레이아웃 최적화: 가변적이고 단순한 단위 사용</h1>
<p>웹 개발에서 레이아웃을 구성할 때, 복잡한 계산식보다는 가변적이고 단순한 단위를 사용하는 것이 중요합니다. 이는 브라우저의 계산 부담을 줄이고, 팀원들 간의 코드 이해도를 높이는 데 도움이 됩니다. 이번 글에서는 박스 모델을 활용해 복잡한 계산식을 피하는 방법과, 가변적이고 단순한 단위를 사용하는 전략을 소개합니다.</p>
<hr>
<h3 id="1-박스-모델-이해"><strong>1. 박스 모델 이해</strong></h3>
<h4 id="박스-모델의-구성"><strong>박스 모델의 구성</strong></h4>
<ul>
<li><strong>콘텐츠 영역</strong>: 실제 콘텐츠가 포함된 영역.</li>
<li><strong>패딩</strong>: 콘텐츠와 테두리 사이의 공간.</li>
<li><strong>테두리</strong>: 콘텐츠와 패딩을 둘러싼 선.</li>
<li><strong>마진</strong>: 박스와 다른 요소 사이의 공간.</li>
</ul>
<h4 id="박스-모델의-중요성"><strong>박스 모델의 중요성</strong></h4>
<ul>
<li><strong>레이아웃 제어</strong>: 박스 모델을 이해하면 복잡한 계산식 없이도 레이아웃을 쉽게 제어할 수 있습니다.</li>
<li><strong>가변적 레이아웃</strong>: <code>min-width</code>, <code>max-width</code>와 같은 속성을 사용해 가변적인 레이아웃을 구현할 수 있습니다.</li>
</ul>
<hr>
<h3 id="2-가변적이고-단순한-단위-사용"><strong>2. 가변적이고 단순한 단위 사용</strong></h3>
<h4 id="min-width와-max-width"><strong><code>min-width</code>와 <code>max-width</code></strong></h4>
<ul>
<li><strong>적합한 상황</strong>: 레이아웃이 특정 너비 이하로 줄어들지 않도록 하거나, 최대 너비를 설정할 때 유용합니다.</li>
<li><strong>예제 코드</strong><pre><code class="language-css">.container {
  max-width: 1020px;
  width: 100%;
}</code></pre>
</li>
</ul>
<h4 id="vw와--단위"><strong><code>vw</code>와 <code>%</code> 단위</strong></h4>
<ul>
<li><strong>적합한 상황</strong>: 반응형 디자인에서 브라우저 창의 너비에 따라 레이아웃을 조정해야 할 때 유용합니다.</li>
<li><strong>예제 코드</strong><pre><code class="language-css">.responsive-card {
  width: 30vw; /* 브라우저 창의 너비에 따라 조정 */
}</code></pre>
</li>
</ul>
<h4 id="rem-단위"><strong><code>rem</code> 단위</strong></h4>
<ul>
<li><p><strong>적합한 상황</strong>: 폰트 크기와 같은 상대적인 크기를 조정해야 할 때 유용합니다. 미디어 쿼리와 함께 사용하면 반응형 디자인에 유리합니다.</p>
</li>
<li><p><strong>예제 코드</strong></p>
<pre><code class="language-css">:root {
  font-size: 16px; /* 기본 폰트 크기 설정 */
}

.card {
  width: 20rem; /* 폰트 크기에 상대적인 너비 */
}</code></pre>
</li>
</ul>
<hr>
<h3 id="3-float-레이아웃-최적화"><strong>3. Float 레이아웃 최적화</strong></h3>
<h4 id="float의-문제점"><strong>Float의 문제점</strong></h4>
<ul>
<li><strong>복잡한 계산</strong>: Float를 사용할 때 직접 여백과 아이템 크기를 계산해야 하는 경우가 많습니다.</li>
<li><strong>부모 요소 처리</strong>: Float 요소의 부모 요소에 <code>overflow: hidden</code>이나 <code>clearfix</code> 클래스를 사용해 Float를 해제해야 합니다.</li>
</ul>
<h4 id="최적화-전략"><strong>최적화 전략</strong></h4>
<ul>
<li><strong>박스 모델 활용</strong>: 박스 모델을 잘 이해하고, <code>min-width</code>, <code>max-width</code>와 같은 속성을 사용해 가변적인 레이아웃을 구현합니다.</li>
<li><strong>가상 요소 사용</strong>: <code>clearfix</code>와 같은 클래스를 사용해 Float를 해제하는 방법을 활용합니다.</li>
</ul>
<h4 id="예제-코드"><strong>예제 코드</strong></h4>
<pre><code class="language-css">.clearfix::after {
  content: &quot;&quot;;
  display: block;
  clear: both;
}

.float-item {
  float: left;
  width: calc(100% - 300px); /* 복잡한 계산식 대신 박스 모델 활용 */
}</code></pre>
<hr>
<h3 id="4-grid-레이아웃과의-비교"><strong>4. Grid 레이아웃과의 비교</strong></h3>
<h4 id="grid의-장점"><strong>Grid의 장점</strong></h4>
<ul>
<li><strong>2차원 배열</strong>: Grid는 행과 열로 아이템을 배치하는 데 유리합니다.</li>
<li><strong>복잡한 레이아웃 지원</strong>: Grid는 복잡한 레이아웃을 쉽게 구현할 수 있습니다.</li>
</ul>
<h4 id="grid의-단점"><strong>Grid의 단점</strong></h4>
<ul>
<li><strong>복잡한 계산</strong>: Grid도 특정 상황에서 복잡한 계산식이 필요할 수 있습니다.</li>
<li><strong>예제 코드</strong><pre><code class="language-css">.grid-container {
  grid-template-columns: minmax(200px, 1fr) 2fr;
}</code></pre>
</li>
</ul>
<hr>
<h3 id="5-결론"><strong>5. 결론</strong></h3>
<p>CSS 레이아웃을 구성할 때, 복잡한 계산식보다는 가변적이고 단순한 단위를 사용하는 것이 중요합니다. 박스 모델을 잘 이해하고, <code>min-width</code>, <code>max-width</code>와 같은 속성을 활용하면 레이아웃을 쉽게 제어할 수 있습니다. Float나 Grid를 사용할 때도, 박스 모델과 가상 요소를 활용해 복잡한 계산식을 피하는 것이 좋습니다. 이러한 전략을 통해 더 나은 웹 페이지를 구현할 수 있습니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS 단위 선택]]></title>
            <link>https://velog.io/@gang_shik/CSS-%EB%8B%A8%EC%9C%84-%EC%84%A0%ED%83%9D</link>
            <guid>https://velog.io/@gang_shik/CSS-%EB%8B%A8%EC%9C%84-%EC%84%A0%ED%83%9D</guid>
            <pubDate>Sun, 23 Mar 2025 02:57:41 GMT</pubDate>
            <description><![CDATA[<h1 id="css-단위-선택-px-vs-rem---접근성과-반응형-디자인을-위한-선택">CSS 단위 선택: <code>px</code> vs <code>rem</code> - 접근성과 반응형 디자인을 위한 선택</h1>
<p>웹 개발에서 CSS 단위 선택은 디자인의 반응성과 접근성을 결정하는 중요한 요소입니다. <code>px</code>와 <code>rem</code>은 각각 고유한 특성과 장단점을 가지고 있으며, 프로젝트의 요구 사항에 맞게 적절히 선택해야 합니다. 이번 글에서는 <code>px</code>와 <code>rem</code>의 차이점과 사용 사례를 살펴보고, 이를 선택하는 기준을 정리해 보겠습니다.</p>
<hr>
<h3 id="1-px-단위"><strong>1. <code>px</code> 단위</strong></h3>
<h4 id="특징"><strong>특징</strong></h4>
<ul>
<li><strong>고정된 크기</strong>: <code>px</code>는 절대적인 단위로, 브라우저의 해상도에 따라 고정된 크기를 제공합니다.</li>
<li><strong>적합한 상황</strong>: 고정된 레이아웃이 필요한 경우나, 브라우저의 가변성에 관계없이 일정한 크기를 유지해야 할 때 유용합니다.</li>
<li><strong>예시</strong>: 이미지나 아이콘의 크기, 테두리 너비 등 고정된 크기가 필요한 요소에 적합합니다.</li>
</ul>
<h4 id="문제점"><strong>문제점</strong></h4>
<ul>
<li><strong>반응형 디자인 제한</strong>: <code>px</code>는 사용자의 브라우저 설정에 따라 크기를 조정할 수 없으므로, 반응형 디자인에서 제한적입니다.</li>
<li><strong>접근성 문제</strong>: 시각 장애가 있는 사용자가 브라우저에서 폰트 크기를 조정할 때, <code>px</code>로 설정된 요소는 크기가 변하지 않아 접근성이 저해될 수 있습니다.</li>
</ul>
<hr>
<h3 id="2-rem-단위"><strong>2. <code>rem</code> 단위</strong></h3>
<h4 id="특징-1"><strong>특징</strong></h4>
<ul>
<li><strong>상대적인 크기</strong>: <code>rem</code>은 루트 요소의 폰트 크기에 상대적인 단위로, 반응형 디자인에 유리합니다.</li>
<li><strong>적합한 상황</strong>: 반응형 디자인에서 전체 레이아웃의 크기를 쉽게 조정해야 할 때 유용합니다.</li>
<li><strong>예시</strong>: 폰트 크기, 패딩, 마진 등 레이아웃의 크기를 유연하게 조정해야 하는 요소에 적합합니다.</li>
</ul>
<h4 id="장점"><strong>장점</strong></h4>
<ul>
<li><strong>반응성</strong>: 사용자가 브라우저에서 폰트 크기를 조정할 수 있어, 레이아웃이 자연스럽게 확대/축소됩니다.</li>
<li><strong>접근성 향상</strong>: 시각 장애가 있는 사용자가 브라우저에서 폰트 크기를 조정할 때도 레이아웃이 잘 유지됩니다.</li>
</ul>
<hr>
<h3 id="3-선택-기준"><strong>3. 선택 기준</strong></h3>
<h4 id="목적성"><strong>목적성</strong></h4>
<ul>
<li>각 단위의 특성과 프로젝트의 요구 사항에 맞춰 선택합니다.</li>
<li><strong>고정된 크기 필요</strong>: <code>px</code>를 사용.</li>
<li><strong>반응형 디자인 필요</strong>: <code>rem</code>을 사용.</li>
</ul>
<h4 id="성능"><strong>성능</strong></h4>
<ul>
<li><code>px</code>는 고정된 크기를 제공하지만, 반응형 디자인에서는 <code>rem</code>이 더 유리합니다.</li>
<li><code>rem</code>은 루트 요소의 폰트 크기에 따라 크기가 조정되므로, 사용자 환경에 맞게 레이아웃이 확대/축소됩니다</li>
</ul>
<hr>
<h3 id="4-예외-및-특이-케이스"><strong>4. 예외 및 특이 케이스</strong></h3>
<h4 id="px-사용-사례"><strong><code>px</code> 사용 사례</strong></h4>
<ul>
<li><strong>고정된 요소</strong>: 이미지, 아이콘, 테두리 너비 등 고정된 크기가 필요한 요소에 <code>px</code>를 사용합니다.</li>
<li><strong>레거시 브라우저 지원</strong>: 일부 오래된 브라우저에서 <code>rem</code>이 지원되지 않을 수 있으므로, <code>px</code>를 사용해야 할 수도 있습니다.</li>
</ul>
<h4 id="rem-사용-사례"><strong><code>rem</code> 사용 사례</strong></h4>
<ul>
<li><strong>반응형 폰트 크기 조정</strong>: 사용자가 브라우저에서 폰트 크기를 조정할 때 레이아웃이 자연스럽게 확대/축소되도록 <code>rem</code>을 사용합니다.</li>
<li><strong>접근성 향상</strong>: 시각 장애가 있는 사용자가 브라우저에서 폰트 크기를 조정할 때도 레이아웃이 잘 유지되도록 <code>rem</code>을 사용합니다.</li>
</ul>
<hr>
<h3 id="5-결론"><strong>5. 결론</strong></h3>
<p>CSS 단위 선택은 프로젝트의 목적과 요구 사항에 따라 다르게 접근해야 합니다. <code>px</code>는 고정된 크기를 제공하며, <code>rem</code>은 반응형 디자인과 접근성을 향상시킵니다. 각 단위의 특성을 이해하고 적절히 활용하면 더 나은 웹 페이지를 구현할 수 있습니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS 레이아웃 선택]]></title>
            <link>https://velog.io/@gang_shik/CSS-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%EC%84%A0%ED%83%9D</link>
            <guid>https://velog.io/@gang_shik/CSS-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%EC%84%A0%ED%83%9D</guid>
            <pubDate>Sun, 23 Mar 2025 02:34:50 GMT</pubDate>
            <description><![CDATA[<h1 id="css-레이아웃-선택의-기준-flexbox-grid-float">CSS 레이아웃 선택의 기준: Flexbox, Grid, Float</h1>
<p>웹 개발에서 레이아웃을 구성하는 방법은 여러 가지가 있으며, 각 방법은 고유한 특성과 장단점을 가지고 있습니다. 이번 글에서는 Flexbox, Grid, Float의 사용 사례와 예외적인 경우를 살펴보고, 이를 선택하는 기준을 정리해 보겠습니다.</p>
<hr>
<h3 id="1-flexbox"><strong>1. Flexbox</strong></h3>
<h4 id="특징"><strong>특징</strong></h4>
<ul>
<li><strong>1차원 배열</strong>: 주로 수평 또는 수직으로 아이템을 배치하는 데 적합합니다.</li>
<li><strong>유연한 크기 조정</strong>: 아이템의 크기를 유연하게 조정할 수 있어 반응형 디자인에 유리합니다.</li>
<li><strong>간단한 구현</strong>: 비교적 간단한 구현이 가능하며, 브라우저에서도 계산이 단순합니다.</li>
</ul>
<h4 id="예시"><strong>예시</strong></h4>
<ul>
<li><strong>네비게이션 바</strong>: 메뉴 아이템을 수평으로 배치할 때 유용합니다.</li>
<li><strong>리스트 아이템</strong>: 여러 아이템을 수평 또는 수직으로 나열할 때 적합합니다.</li>
</ul>
<h4 id="예제-코드"><strong>예제 코드</strong></h4>
<pre><code class="language-css">.flex-container {
  display: flex;
  justify-content: space-between; /* 아이템을 수평으로 배치 */
  align-items: center; /* 아이템을 수직으로 중앙 정렬 */
}</code></pre>
<hr>
<h3 id="2-grid"><strong>2. Grid</strong></h3>
<h4 id="특징-1"><strong>특징</strong></h4>
<ul>
<li><strong>2차원 배열</strong>: 행과 열로 아이템을 배치하는 데 적합합니다.</li>
<li><strong>복잡한 레이아웃 지원</strong>: 복잡한 레이아웃을 쉽게 구현할 수 있으며, 모바일에서도 순서를 자유롭게 변경할 수 있습니다.</li>
<li><strong>다소 복잡한 구현</strong>: 1차원보다 복잡한 계산이 필요할 수 있습니다.</li>
</ul>
<h4 id="예시-1"><strong>예시</strong></h4>
<ul>
<li><strong>갤러리</strong>: 이미지나 아이템을 격자 형태로 배치할 때 유용합니다.</li>
<li><strong>테이블 형태의 레이아웃</strong>: 데이터를 테이블 형태로 표현할 때 적합합니다.</li>
</ul>
<h4 id="예제-코드-1"><strong>예제 코드</strong></h4>
<pre><code class="language-css">.grid-container {
  display: grid;
  grid-template-columns: repeat(3, 1fr); /* 3열 격자 */
  grid-template-rows: repeat(2, 1fr); /* 2행 격자 */
  gap: 10px; /* 격자 간격 */
}</code></pre>
<hr>
<h3 id="3-float"><strong>3. Float</strong></h3>
<h4 id="특징-2"><strong>특징</strong></h4>
<ul>
<li><strong>레거시 프로젝트</strong>: 오래된 웹사이트나 특정 상황에서 여전히 유용할 수 있습니다.</li>
<li><strong>단순한 구현</strong>: 비교적 간단한 구현이 가능하지만, 최근에는 Flexbox나 Grid로 대체되는 경우가 많습니다.</li>
</ul>
<h4 id="예시-2"><strong>예시</strong></h4>
<ul>
<li><strong>네이버 포털</strong>: Float를 사용한 레이아웃이 여전히 존재하는 레거시 프로젝트.</li>
<li><strong>특정 상황</strong>: 특정한 레이아웃 요구 사항이 있을 때 Float가 유용할 수 있습니다.</li>
</ul>
<h4 id="예제-코드-2"><strong>예제 코드</strong></h4>
<pre><code class="language-css">.float-item {
  float: left; /* 아이템을 왼쪽으로 띄움 */
  width: 50%; /* 아이템의 너비 설정 */
}</code></pre>
<hr>
<h3 id="4-선택-기준"><strong>4. 선택 기준</strong></h3>
<h4 id="목적성"><strong>목적성</strong></h4>
<ul>
<li>각 레이아웃 방법은 고유한 쓰임새가 있으며, 프로젝트의 요구 사항에 맞춰 선택해야 합니다.</li>
<li><strong>1차원 배열</strong>: Flexbox가 적합.</li>
<li><strong>2차원 배열</strong>: Grid가 적합.</li>
<li><strong>레거시 프로젝트</strong>: Float가 필요할 수 있음.</li>
</ul>
<h4 id="성능"><strong>성능</strong></h4>
<ul>
<li>Flexbox와 Grid의 성능 차이는 미미하지만, 복잡한 레이아웃에서는 Grid가 더 복잡할 수 있습니다.</li>
</ul>
<h4 id="팀-내부-규칙"><strong>팀 내부 규칙</strong></h4>
<ul>
<li>팀 내부에서 일관된 스타일을 유지하기 위해 특정 레이아웃 방법만 사용하는 규칙을 정할 수 있지만, 상황에 맞게 유연하게 활용하는 것이 더 효율적일 수 있습니다.</li>
</ul>
<hr>
<h3 id="5-예외-및-특이-케이스"><strong>5. 예외 및 특이 케이스</strong></h3>
<h4 id="2차원-배열에서-flexbox-사용"><strong>2차원 배열에서 Flexbox 사용</strong></h4>
<ul>
<li><strong>Flexbox의 2D 레이아웃</strong>: Flexbox도 2D 레이아웃을 구현할 수 있으며, 특히 아이템의 크기가 유연하게 변할 때 유용합니다. 예를 들어, Flexbox의 <code>flex-wrap</code> 속성을 사용해 아이템을 여러 줄로 나열할 수 있습니다</li>
<li><strong>Grid보다 유리한 경우</strong>: Flexbox는 아이템의 크기를 유연하게 조정할 수 있어, Grid보다 더 유연한 레이아웃이 필요한 경우에 유리할 수 있습니다</li>
</ul>
<h4 id="1차원-배열에서-grid-사용"><strong>1차원 배열에서 Grid 사용</strong></h4>
<ul>
<li><strong>Grid의 1D 레이아웃</strong>: Grid는 1차원 배열에서도 유용할 수 있으며, 특히 아이템의 크기를 정확히 제어해야 할 때 유리합니다. 예를 들어, Grid는 열의 너비를 <code>1fr</code>로 설정하여 동일한 너비로 유지할 수 있습니다</li>
<li><strong>Flexbox보다 유리한 경우</strong>: Grid는 아이템의 크기를 정확히 제어할 수 있어, Flexbox보다 더 정밀한 레이아웃이 필요한 경우에 유리할 수 있습니다</li>
</ul>
<hr>
<h3 id="6-결론"><strong>6. 결론</strong></h3>
<p>CSS 레이아웃 방법은 프로젝트의 요구 사항에 맞게 선택해야 합니다. Flexbox는 1차원 배열에, Grid는 2차원 배열에 적합하며, Float는 레거시 프로젝트나 특정 상황에서 유용할 수 있습니다. 각 레이아웃 방법의 특성을 이해하고, 상황에 맞게 적절히 활용하는 것이 중요합니다. 예외적으로 Flexbox도 2D 레이아웃에 사용될 수 있으며, Grid도 1D 레이아웃에 유리할 수 있습니다. 이러한 유연성을 이해하고 활용하면 더 나은 웹 페이지를 구현할 수 있습니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML / CSS 실습 회고록 - 3]]></title>
            <link>https://velog.io/@gang_shik/HTML-CSS-%EC%8B%A4%EC%8A%B5-%ED%9A%8C%EA%B3%A0%EB%A1%9D-3</link>
            <guid>https://velog.io/@gang_shik/HTML-CSS-%EC%8B%A4%EC%8A%B5-%ED%9A%8C%EA%B3%A0%EB%A1%9D-3</guid>
            <pubDate>Sun, 23 Mar 2025 02:31:02 GMT</pubDate>
            <description><![CDATA[<h2 id="htmlcss-인터랙티브-요소--박스-모델-정복-실전-피드백-총정리">HTML/CSS 인터랙티브 요소 &amp; 박스 모델 정복: 실전 피드백 총정리</h2>
<hr>
<h3 id="1-호버포커스-상호작용의-올바른-처리"><strong>1. 호버/포커스 상호작용의 올바른 처리</strong></h3>
<h4 id="문제점"><strong>문제점</strong></h4>
<p>초기 코드에서는 개별 요소에 호버/포커스 효과를 적용해 전체적인 상호작용이 부자연스러웠습니다.<br>예: 카드와 아이콘이 별도로 반응해 UX 일관성 저하.</p>
<h4 id="수정-전-코드"><strong>수정 전 코드</strong></h4>
<pre><code class="language-css">/* 카드와 아이콘을 각각 처리 → 일관성 없는 동작 */
.card-content:hover { border-color: blue; }
.arrow-icon-container:hover { background-color: blue; }</code></pre>
<h4 id="수정-후-코드"><strong>수정 후 코드</strong></h4>
<pre><code class="language-css">.card-content {
  transition: border-color 0.3s ease-in; 
}

/* 카드 전체에 호버/포커스 시 하위 요소 제어 */
.card-content:hover .purchase-icon-container,
.card-content:focus-within .purchase-icon-container {
  background-color: var(--color-blue-500);
}

.card-content:hover .purchase-icon-container::before {
  content: &quot;구매하기&quot;; /* 가상 요소로 텍스트 추가 */
}</code></pre>
<h4 id="핵심-원칙"><strong>핵심 원칙</strong></h4>
<ol>
<li><strong>부모 요소 중심 제어</strong>: <code>:focus-within</code>을 사용해 자식 요소의 포커스도 캐치.</li>
<li><strong>상태 동기화</strong>: 카드 호버 시 모든 하위 요소가 동시에 반응하도록 처리.</li>
<li><strong>트랜지션 최적화</strong>: <code>transition</code> 속성은 최종 대상에 직접 적용.</li>
</ol>
<hr>
<h3 id="2-박스-모델-정렬-flexbox-활용법"><strong>2. 박스 모델 정렬: Flexbox 활용법</strong></h3>
<h4 id="문제점-1"><strong>문제점</strong></h4>
<p>이미지가 카드 내에서 수직/수평 중앙 정렬되지 않아 시안과 달랐습니다.</p>
<h4 id="수정-전"><strong>수정 전</strong></h4>
<pre><code class="language-css">.product-card-image {
  padding-top: 10px; /* 임시방편 조정 */
}</code></pre>
<h4 id="수정-후"><strong>수정 후</strong></h4>
<pre><code class="language-css">.card-content {
  display: flex;
  justify-content: center; /* 수평 중앙 */
  align-items: center;     /* 수직 중앙 */
  height: 310px;           /* 고정 높이 필수 */
}</code></pre>
<h4 id="왜-flexbox인가"><strong>왜 Flexbox인가?</strong></h4>
<ul>
<li><strong>반응형 대응 용이</strong>: <code>margin</code>이나 <code>padding</code>으로 위치 조정 시 다양한 해상도에서 깨질 수 있음.</li>
<li><strong>직관적인 정렬</strong>: 1줄의 CSS로 복잡한 계산 없이 중앙 정렬 가능.</li>
</ul>
<hr>
<h3 id="3-aria-속성의-올바른-사용"><strong>3. ARIA 속성의 올바른 사용</strong></h3>
<h4 id="잘못된-사례"><strong>잘못된 사례</strong></h4>
<pre><code class="language-html">&lt;span class=&quot;btn&quot; aria-label=&quot;구매하기&quot;&gt;...&lt;/span&gt;</code></pre>
<p>→ <code>span</code>은 클릭 이벤트를 기본 지원하지 않음. <code>role=&quot;button&quot;</code> 추가 필요.</p>
<h4 id="개선-코드"><strong>개선 코드</strong></h4>
<pre><code class="language-html">&lt;button class=&quot;purchase-btn&quot; aria-label=&quot;상품 구매하기&quot;&gt;
  &lt;img src=&quot;icon.svg&quot; alt=&quot;&quot; aria-hidden=&quot;true&quot;&gt;
&lt;/button&gt;</code></pre>
<h4 id="aria-사용-가이드"><strong>ARIA 사용 가이드</strong></h4>
<table>
<thead>
<tr>
<th>속성</th>
<th>사용 목적</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>aria-label</code></td>
<td>요소의 목적 설명</td>
<td><code>×</code></td>
</tr>
<tr>
<td><code>aria-hidden</code></td>
<td>불필요한 중복 콘텐츠 숨김</td>
<td><code>▲</code></td>
</tr>
<tr>
<td><code>role</code></td>
<td>요소의 역할 재정의</td>
<td><code>...</code></td>
</tr>
</tbody></table>
<hr>
<h3 id="4-디자인-시안-정확-구현-전략"><strong>4. 디자인 시안 정확 구현 전략</strong></h3>
<h4 id="실제-사례-비교"><strong>실제 사례 비교</strong></h4>
<p><strong>구현 절차</strong></p>
<ol>
<li><strong>픽셀 단위 측정</strong>: Figma에서 <code>padding</code>, <code>margin</code> 값 정확히 확인.</li>
<li><strong>상대 단위 사용</strong>: <code>px</code> 대신 <code>rem</code>으로 반응형 대응.</li>
<li><strong>변수 활용</strong>: <pre><code class="language-css">:root {
  --button-padding: 8px 16px;
  --icon-size: 40px;
}</code></pre>
</li>
</ol>
<hr>
<h3 id="5-종합-평가표"><strong>5. 종합 평가표</strong></h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>초기 구현</th>
<th>개선 후</th>
<th>중요도</th>
</tr>
</thead>
<tbody><tr>
<td><strong>시맨틱 태그</strong></td>
<td><code>div</code> 남발</td>
<td><code>ul</code>/<code>li</code> 사용</td>
<td>★★★★★</td>
</tr>
<tr>
<td><strong>접근성</strong></td>
<td><code>alt=&quot;로고 이미지&quot;</code></td>
<td><code>alt=&quot;쿠팡&quot;</code></td>
<td>★★★★☆</td>
</tr>
<tr>
<td><strong>유지보수성</strong></td>
<td><code>nth-child</code> 복잡 구조</td>
<td>클래스 기반 스타일링</td>
<td>★★★★☆</td>
</tr>
<tr>
<td><strong>디자인 정확도</strong></td>
<td>수동 위치 조정</td>
<td>Flexbox 중앙 정렬</td>
<td>★★★☆☆</td>
</tr>
</tbody></table>
<hr>
<h3 id="6-개발자-체크리스트"><strong>6. 개발자 체크리스트</strong></h3>
<p>다음 작업 전에 반드시 확인하세요:</p>
<ol>
<li><input disabled="" type="checkbox"> W3C Validator로 HTML 검증</li>
<li><input disabled="" type="checkbox"> 스크린 리더로 음성 출력 테스트</li>
<li><input disabled="" type="checkbox"> Figma 시안과 픽셀 단위 비교</li>
<li><input disabled="" type="checkbox"> <code>nth-child</code> 대신 명시적 클래스 사용 여부 확인</li>
<li><input disabled="" type="checkbox"> 모든 인터랙티브 요소 키보드 탐색 테스트</li>
</ol>
<blockquote>
<p>&quot;디자인을 코드로 옮기는 것은 단순 변환이 아닌 <strong>재해석</strong>입니다.<br>태그 선택에서부터 스타일링까지, 모든 단계에서 &#39;왜?&#39;를 묻는 습관이 전문가와 주니어를 구분합니다.&quot;<br>– 프론트엔드 개발자 마틴 파울러</p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML / CSS 실습 회고 - 2]]></title>
            <link>https://velog.io/@gang_shik/HTML-CSS-%EC%8B%A4%EC%8A%B5-%ED%9A%8C%EA%B3%A0-2</link>
            <guid>https://velog.io/@gang_shik/HTML-CSS-%EC%8B%A4%EC%8A%B5-%ED%9A%8C%EA%B3%A0-2</guid>
            <pubDate>Sun, 23 Mar 2025 02:26:20 GMT</pubDate>
            <description><![CDATA[<h2 id="htmlcss에서-시맨틱-태그-alt-속성-그리고-클래스-활용의-중요성">HTML/CSS에서 시맨틱 태그, <code>alt</code> 속성, 그리고 클래스 활용의 중요성</h2>
<p>웹 개발에서 시맨틱 태그와 적절한 속성 사용은 웹 접근성을 높이고 유지보수를 용이하게 만듭니다. 이번 글에서는 <code>h1</code> 태그와 <code>alt</code> 속성의 의미 있는 사용, 그리고 <code>nth-child</code> 대신 클래스를 활용한 스타일링의 중요성을 이론과 예제를 통해 정리해보겠습니다.</p>
<hr>
<h3 id="1-시맨틱-태그와-헤딩-구조"><strong>1. 시맨틱 태그와 헤딩 구조</strong></h3>
<h4 id="문제점"><strong>문제점</strong></h4>
<p>초기 코드에서는 다음과 같은 문제가 있었습니다:</p>
<ol>
<li><strong>헤딩 구조의 단절</strong>: <code>h2</code> 이후 바로 <code>h6</code>을 사용하여 시맨틱 구조가 깨짐.</li>
<li><strong>스타일을 위한 헤딩 태그 남용</strong>: 헤딩 태그는 문서의 계층 구조를 표현하기 위한 용도인데, 단순히 스타일링 목적으로 사용됨.</li>
</ol>
<h4 id="수정-과정"><strong>수정 과정</strong></h4>
<p><strong>수정 전 코드</strong>:</p>
<pre><code class="language-html">&lt;header class=&quot;header&quot;&gt;
  &lt;h1 class=&quot;logo&quot;&gt;
    &lt;a href=&quot;https://www.coupang.com/&quot;&gt;
      &lt;img src=&quot;./assets/logo/logo.png&quot; alt=&quot;쿠팡 로고 이미지&quot; /&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
&lt;/header&gt;
&lt;section class=&quot;section&quot;&gt;
  &lt;div class=&quot;today-discovery-headline&quot;&gt;
    &lt;h2 class=&quot;title-medium-24&quot;&gt;오늘의 발견&lt;/h2&gt;
    &lt;h6 class=&quot;title-medium-16 subtitle&quot;&gt;
      &lt;span role=&quot;separator&quot; aria-orientation=&quot;vertical&quot;&gt;|&lt;/span&gt;
      오늘 쿠팡이 엄선한 가장 핫한 제품
    &lt;/h6&gt;
  &lt;/div&gt;
&lt;/section&gt;</code></pre>
<p><strong>수정 후 코드</strong>:</p>
<pre><code class="language-html">&lt;header class=&quot;header&quot;&gt;
  &lt;h1 class=&quot;logo&quot;&gt;
    &lt;a href=&quot;https://www.coupang.com/&quot;&gt;
      &lt;img src=&quot;./assets/logo/logo.png&quot; alt=&quot;쿠팡&quot; /&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
&lt;/header&gt;
&lt;section class=&quot;section&quot;&gt;
  &lt;div class=&quot;today-discovery-headline&quot;&gt;
    &lt;h2 class=&quot;title-medium-24&quot;&gt;오늘의 발견&lt;/h2&gt;
    &lt;h3 class=&quot;title-medium-16 subtitle&quot;&gt;
      &lt;span role=&quot;separator&quot; aria-orientation=&quot;vertical&quot;&gt;|&lt;/span&gt;
      오늘 쿠팡이 엄선한 가장 핫한 제품
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/section&gt;</code></pre>
<h4 id="개선-사항"><strong>개선 사항</strong></h4>
<ol>
<li><strong>헤딩 구조의 연속성 유지</strong>: <code>h2</code> 다음에 <code>h3</code>를 사용하여 문서 계층 구조를 명확히 표현.</li>
<li><strong>스타일링은 CSS로 처리</strong>: 헤딩 태그를 스타일링 목적으로 사용하는 대신, CSS 클래스(<code>title-medium-16</code>)를 활용.</li>
<li><strong><code>alt</code> 속성의 의미 부여</strong>: 로고 이미지의 <code>alt</code> 값을 &quot;쿠팡 로고 이미지&quot;에서 &quot;쿠팡&quot;으로 변경하여 간결하면서도 명확하게 표현.</li>
</ol>
<h4 id="왜-중요한가"><strong>왜 중요한가?</strong></h4>
<ul>
<li><strong>시맨틱 구조 유지</strong>: 검색 엔진과 스크린 리더가 문서를 올바르게 해석할 수 있도록 돕습니다.</li>
<li><strong>접근성 향상</strong>: 스크린 리더 사용자는 <code>alt</code> 텍스트를 통해 이미지를 이해합니다.</li>
<li><strong>유지보수 용이성</strong>: 스타일링은 CSS로 분리하여 HTML 구조와 디자인을 독립적으로 관리할 수 있습니다.</li>
</ul>
<hr>
<h3 id="2-nth-child-대신-클래스-활용"><strong>2. <code>nth-child</code> 대신 클래스 활용</strong></h3>
<h4 id="문제점-1"><strong>문제점</strong></h4>
<p>초기 코드에서는 <code>nth-child</code> 선택자를 사용해 카드 레이아웃을 스타일링했습니다. 그러나 이는 다음과 같은 문제를 야기합니다:</p>
<ol>
<li>요소 순서가 변경되면 스타일이 깨질 위험이 있음.</li>
<li>코드 가독성이 떨어짐(어떤 요소가 어떤 스타일을 가지는지 명확하지 않음).</li>
</ol>
<h4 id="수정-과정-1"><strong>수정 과정</strong></h4>
<p><strong>수정 전 코드</strong>:</p>
<pre><code class="language-html">&lt;div class=&quot;card-link-column&quot;&gt;
  &lt;a href=&quot;/&quot;&gt;
    &lt;div class=&quot;card-content&quot; tabindex=&quot;0&quot;&gt;
      &lt;img src=&quot;./assets/discovery/honeytea.jpg&quot; alt=&quot;오뚜기 따뜻한 차 향기&quot; class=&quot;product-card-image&quot; /&gt;
      &lt;div class=&quot;arrow-icon-container&quot; tabindex=&quot;0&quot;&gt;
        &lt;img src=&quot;./assets/icon/angle-right-square.svg&quot; alt=&quot;화살표 아이콘&quot; /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;</code></pre>
<pre><code class="language-css">.card-link-column:nth-child(-n + 2) {
  width: 50%;
  float: left;
  padding: 10px;
}

.card-link-column:nth-child(n + 3):nth-child(-n + 6) {
  width: 25%;
  float: left;
  padding: 10px;
}</code></pre>
<p><strong>수정 후 코드</strong>:</p>
<pre><code class="language-html">&lt;ul class=&quot;card-link&quot;&gt;
  &lt;li class=&quot;card-link-item big&quot;&gt;
    &lt;a href=&quot;/&quot; class=&quot;card-content&quot;&gt;
      &lt;img src=&quot;./assets/discovery/honeytea.jpg&quot; alt=&quot;오뚜기 따뜻한 차 향기&quot; class=&quot;product-card-image&quot; /&gt;
      &lt;span class=&quot;purchase-icon-container&quot; aria-label=&quot;구매하기&quot;&gt;
        &lt;img src=&quot;./assets/icon/angle-right-square.svg&quot; alt=&quot;구매하기 아이콘&quot; /&gt;
      &lt;/span&gt;
    &lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;</code></pre>
<pre><code class="language-css">.card-link {
  list-style: none;
  padding-left: 0;
}

.card-link-item {
  float: left;
  padding: 10px;
}

.card-link-item.big {
  width: 50%;
}

.card-link-item.small {
  width: 25%;
}</code></pre>
<h4 id="개선-사항-1"><strong>개선 사항</strong></h4>
<ol>
<li><strong>클래스 기반 스타일링</strong>: 각 카드에 명확한 클래스(<code>big</code>, <code>small</code>)를 부여하여 역할과 크기를 정의.</li>
<li><strong>가독성 향상</strong>: 클래스 이름만 보고도 요소의 크기와 레이아웃을 쉽게 이해할 수 있음.</li>
<li><strong>변경에 유연함</strong>: HTML 요소 순서가 바뀌더라도 스타일이 깨지지 않음.</li>
</ol>
<hr>
<h3 id="3-요약-및-결론"><strong>3. 요약 및 결론</strong></h3>
<h4 id="시맨틱-태그와-접근성"><strong>시맨틱 태그와 접근성</strong></h4>
<ul>
<li>헤딩 태그는 문서 계층 구조를 표현하는 데 사용하며, 디자인 목적으로 남용하지 않아야 합니다.</li>
<li><code>alt</code> 속성은 이미지의 의미를 간결하고 명확하게 전달해야 합니다.</li>
</ul>
<h4 id="클래스-기반-스타일링"><strong>클래스 기반 스타일링</strong></h4>
<ul>
<li><code>nth-child</code> 선택자는 특정 상황에서 유용하지만, 요소 순서 변경에 취약하므로 클래스 기반 접근법이 더 안전하고 유지보수에 용이합니다.</li>
</ul>
<h4 id="최종-메시지"><strong>최종 메시지</strong></h4>
<p>HTML과 CSS는 단순히 화면에 보이는 요소를 만드는 도구가 아니라, 사용자 경험(UX)과 접근성을 고려해야 하는 중요한 기술입니다. 시맨틱 태그와 적절한 속성 사용, 그리고 명확한 클래스 네이밍은 더 나은 웹 개발로 가는 첫걸음임. 위 내용을 바탕으로 프로젝트나 과제에서 코드를 작성할 때 항상 &quot;구조적 의미&quot;, &quot;접근성&quot;, &quot;유지보수성&quot;을 염두하자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML & CSS 실습 회고]]></title>
            <link>https://velog.io/@gang_shik/HTML-CSS-%EC%8B%A4%EC%8A%B5-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@gang_shik/HTML-CSS-%EC%8B%A4%EC%8A%B5-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 23 Mar 2025 02:21:51 GMT</pubDate>
            <description><![CDATA[<h2 id="htmlcss에서-tabindex와-시맨틱-태그-활용-이론과-실전-예시">HTML/CSS에서 <code>tabindex</code>와 시맨틱 태그 활용: 이론과 실전 예시</h2>
<h3 id="1-시맨틱-태그와-웹-접근성"><strong>1. 시맨틱 태그와 웹 접근성</strong></h3>
<p>웹 개발에서 시맨틱 태그를 사용하는 것은 구조적 의미를 명확히 하고, 웹 접근성을 향상시키는 데 중요한 역할을 합니다. 시맨틱 태그는 콘텐츠의 목적을 명확히 하여 검색 엔진 최적화(SEO)와 접근성 도구(스크린 리더 등)의 효율성을 높입니다.</p>
<p>예를 들어, <code>ul</code>과 <code>li</code>는 목록 데이터를 표현할 때 사용되며, <code>div</code>는 구조적 의미가 없는 컨테이너로 사용됩니다. 과도한 <code>div</code> 태그 남발은 코드 가독성을 낮추고 접근성을 저해할 수 있습니다.</p>
<h3 id="2-tabindex-속성의-정의와-사용법"><strong>2. <code>tabindex</code> 속성의 정의와 사용법</strong></h3>
<p><code>tabindex</code>는 HTML 요소의 키보드 포커스 탐색 순서를 제어하는 속성입니다. 기본적으로 대화형 요소(예: <code>a</code>, <code>button</code>, <code>input</code>)는 <code>tabindex=&quot;0&quot;</code>을 가지며, 키보드로 탐색할 수 있습니다.</p>
<p>계속 놓친 부분이</p>
<ol>
<li>a 태그 속성에 자식요소로 tabindex 가질 수 있는 인터랙티브한 요소를 삽입한 것(tabindex 사용 포함)</li>
<li>img 태그로 변경 후에도 span 태그가 아닌 button 태그로 또 자식 요소로 삽입해서 오류가 뜬 것</li>
</ol>
<h4 id="tabindex-값의-의미"><strong><code>tabindex</code> 값의 의미</strong></h4>
<ul>
<li><strong><code>tabindex=&quot;0&quot;</code></strong>: 요소를 기본 탐색 순서에 포함.</li>
<li><strong>양의 정수(<code>tabindex=&quot;1&quot;</code> 이상)</strong>: 탐색 순서를 명시적으로 지정하지만, 접근성을 저해할 수 있어 권장하지 않음[3].</li>
<li><strong><code>tabindex=&quot;-1&quot;</code></strong>: 요소를 키보드 포커스에서 제외하지만 프로그래밍적으로 포커스 가능[5].</li>
</ul>
<h4 id="사용-사례"><strong>사용 사례</strong></h4>
<ul>
<li>비대화형 콘텐츠에 키보드 이벤트를 추가하려면 <code>tabindex=&quot;0&quot;</code>을 설정합니다.</li>
<li>특정 요소를 탐색 순서에서 제외하려면 <code>tabindex=&quot;-1&quot;</code>을 사용합니다.</li>
</ul>
<h3 id="3-실전-예제-카드-컴포넌트-개선"><strong>3. 실전 예제: 카드 컴포넌트 개선</strong></h3>
<h4 id="문제점"><strong>문제점</strong></h4>
<p>초기 코드에서 다음과 같은 문제가 있었습니다:</p>
<ol>
<li>과도한 `` 태그 사용으로 시맨틱 구조가 부족.</li>
<li><code>tabindex</code> 속성 남용으로 키보드 탐색이 비정상적으로 작동.</li>
<li><code>button</code> 태그를 <code>a</code> 태그 내부에 삽입하여 HTML 구조 오류 발생.</li>
</ol>
<h4 id="수정-과정"><strong>수정 과정</strong></h4>
<p><strong>수정 전 코드</strong>:</p>
<pre><code class="language-html">&lt;div class=&quot;card-link&quot;&gt;
  &lt;div class=&quot;card-link-column&quot;&gt;
    &lt;a href=&quot;/&quot;&gt;
      &lt;div class=&quot;card-content&quot; tabindex=&quot;0&quot;&gt;
        &lt;img src=&quot;./assets/image.jpg&quot; alt=&quot;상품 이미지&quot; class=&quot;product-card-image&quot; /&gt;
        &lt;div class=&quot;arrow-icon-container&quot; tabindex=&quot;0&quot;&gt;
          &lt;img src=&quot;./assets/icon.svg&quot; alt=&quot;아이콘&quot; /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;</code></pre>
<p><strong>최종 수정 코드</strong>:</p>
<pre><code class="language-html">&lt;ul class=&quot;card-link&quot;&gt;
  &lt;li class=&quot;card-link-item&quot;&gt;
    &lt;a href=&quot;/&quot; class=&quot;card-content&quot;&gt;
      &lt;img src=&quot;./assets/image.jpg&quot; alt=&quot;상품 이미지&quot; class=&quot;product-card-image&quot; /&gt;
      &lt;span class=&quot;purchase-icon-container&quot; aria-label=&quot;구매하기&quot;&gt;
        &lt;img src=&quot;./assets/icon.svg&quot; alt=&quot;구매하기 아이콘&quot; /&gt;
      &lt;/span&gt;
    &lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;</code></pre>
<h4 id="개선-사항"><strong>개선 사항</strong></h4>
<ol>
<li><strong>시맨틱 태그 활용</strong>: <code>ul</code>과 <code>li</code>를 사용해 목록 구조를 명확히 함.</li>
<li><strong>HTML 구조 오류 제거</strong>: <code>button</code> 대신 <code>span</code>을 사용하여 구조적 문제 해결.</li>
<li><strong>웹 접근성 향상</strong>: 키보드 탐색은 기본적인 HTML 마크업 순서에 따라 자연스럽게 이루어지도록 설계.</li>
</ol>
<h3 id="4-tabindex-활용-시-주의사항"><strong>4. <code>tabindex</code> 활용 시 주의사항</strong></h3>
<ul>
<li><strong>남용 금지</strong>: <code>tabindex</code> 값을 0보다 큰 값으로 설정하면 접근성 도구의 탐색 흐름을 방해할 수 있습니다[3].</li>
<li><strong>자연스러운 탐색 보장</strong>: 마크업 순서를 논리적으로 구성하여 추가적인 <code>tabindex</code> 설정 없이도 키보드 탐색이 원활하도록 작성합니다.</li>
<li><strong>비대화형 콘텐츠에 이벤트 추가</strong>: 필요할 경우만 <code>tabindex</code>를 사용해 키보드 이벤트를 활성화합니다.</li>
</ul>
<h3 id="5-요약"><strong>5. 요약</strong></h3>
<p>웹 개발에서는 디자인 요구사항과 접근성 사이의 균형을 유지해야 합니다. 시맨틱 태그와 적절한 <code>tabindex</code> 속성 사용은 사용자 경험과 웹 접근성을 동시에 향상시킬 수 있습니다. 위 예제는 이러한 원칙을 실천하며, 올바른 HTML 구조와 속성 활용의 중요성을 보여줍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 예외처리]]></title>
            <link>https://velog.io/@gang_shik/JS-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@gang_shik/JS-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Wed, 19 Mar 2025 03:36:54 GMT</pubDate>
            <description><![CDATA[<h3 id="예외처리란">예외처리란?</h3>
<ul>
<li>예외를 처리하는 방법, 특정 문 실행시 에러가 발생한다면 나머지 코드를 계속 실행하는 것은 의미가 없음</li>
<li>그래서 이러한 예외를 처리해서 우회하는 방법이나 다양하게 처리를 하는 것, 사용자에게 예외 사항을 알려주는 경우도 포함</li>
</ul>
<h3 id="try-catch">try-catch</h3>
<ul>
<li>기본적으로 에러를 다룰 때 try-catch 구문으로 다룸</li>
<li>try 구문에서 발생한 에러는 catch에서 error 객체로 들어가 catch 구문이 실행이 됨</li>
<li>이 error 객체에 다양한 에러 관련 값들이 있기에 이를 잘 처리해 에러 처리를 할 수 있음<pre><code class="language-js">try {
X();
} catch (e) {
// try에서 에러가 발생할 경우 실행되는 구문
console.error(e);
console.error(&#39;에러 발생&#39;);
} finally {
// 어떠한 상황에서도 실행되는 구문
}</code></pre>
</li>
</ul>
<h3 id="에러---던지기">에러 - 던지기</h3>
<ul>
<li><p>사용자 &amp; 개발자를 위해서 일부러 에러를 던져서 알려줄 수 있음</p>
</li>
<li><p>아래와 같이 에러를 던져서 처리할 수 있음</p>
</li>
<li><p>사이트가 다운되거나 알 수 없는 에러 등 사용자에게 안내해서 잘 처리할 수 있음</p>
<pre><code class="language-js">function login(id, pw) {
if (id === &#39;zero&#39; &amp;&amp; pw === 0000) {
  return true;
}

// 위 조건을 만족 못해도 문법적 에러가 없기에 직접 아래와 같이 throw를 통해 에러를 발생시켜줘야함
throw new Error(&#39;로그인 실패&#39;)
}
</code></pre>
</li>
</ul>
<p>try {
  login(&#39;one&#39;, 111)
} catch (error) {
  console.error(error);
  console.error(&#39;에러 발생&#39;);
  window.alert(error); // 사용자에게 안내하는 것도 추가
} finally {
  console.log(&#39;로그인 시도 시간 : &#39; + new Date());
}</p>
<pre><code>
### 스택 - 추적
- 에러는 스택 형태로 쌓여서 기록이 됨, `e.stack` 을 통해서 에러의 스택을 확인할 수 있음
- 어디서부터 어디가 문제가 있는지 그 기록을 확인가능함, 어디서 잘못되고 처리해야하는지 스택 형태로 추적해서 확인가능함
```js
function x() {
  c();
  v();
  b();
  x();
  x();
  x();
}

try {
  x();
} catch (e) {
  console.error(e.stack);
}</code></pre><ul>
<li>로그를 보게 되면 스택 기록임을 알면 됨(에러에 대해서)</li>
</ul>
<h3 id="커스텀---에러">커스텀 - 에러</h3>
<ul>
<li><p>에러 객체는 메시지, 스택 꽤 여러가지 다룰 수 있는게 많음</p>
</li>
<li><p>좀 더 구체적인 에러처리를 위해서 에러 객체를 확장해서 직접 커스텀한 에러를 만들 수 있음</p>
</li>
<li><p>그리고 에러 객체를 구분해서 좀 더 디테일하게 처리할 수도 있음</p>
<pre><code class="language-js">class LoginError extends Error {
constructor(message) {
  super(message);

  this.name = &#39;Login Error&#39;;
}
}
</code></pre>
</li>
</ul>
<p>// LoginError도 확장가능
class SomeError extends LoginError {</p>
<p>}</p>
<p>function login(id, pw) {
  if (id !== &#39;a&#39;) {
    throw new LoginError(&#39;아이디 불일치&#39;)
  }</p>
<p>  if (id === &#39;a&#39; &amp;&amp; password === &#39;a&#39;) {
    return true;
  }</p>
<p>  throw new Error(&#39;로그인 실패&#39;);
}</p>
<p>try {
  login(&#39;ac&#39;, &#39;a&#39;);
} catch (e) {
  console.error(e);
  if (e instanceof LoginError) {
    console.error(&#39;로그인 에러가 발생했습니다&#39;);
  } else {
    console.error(&#39;에러가 발생했습니다.&#39;);
  }
} finally {
  console.log(&#39;로그인 시도 시간 : &#39; + new Date());
}</p>
<p>```</p>
<ul>
<li>커스텀 에러 객체를 기존의 에러 객체에서 확장해서 만들 수 있음</li>
<li>더 견고하게 에러 처리를 하다보면 직접 만들어서 처리할 수 있음, 또한 커스텀 에러를 더 확장해서 만들 수도 있음 </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 모듈]]></title>
            <link>https://velog.io/@gang_shik/JS-%EB%AA%A8%EB%93%88</link>
            <guid>https://velog.io/@gang_shik/JS-%EB%AA%A8%EB%93%88</guid>
            <pubDate>Wed, 19 Mar 2025 03:36:08 GMT</pubDate>
            <description><![CDATA[<h3 id="모듈이란">모듈이란?</h3>
<ul>
<li>모듈은 독립적인 특성을 가진 기능 단위의 부품</li>
<li>프로그램의 기능을 독립적인 부품으로 분리한 것, 동시에 여러 다른 모듈과 함께 조합하여 재사용될 수 있음</li>
<li>여기서 중요한 것은 내가 만든 코드가 어떻게 재활용 할 수 있는가도 중요함</li>
<li>예전에는 즉시 실행함수, 클로저등으로 모듈로 응용해서 썼지만, 요즘에는 이러한 모듈을 지원을 해 줌</li>
</ul>
<h3 id="commonjs">CommonJS</h3>
<ul>
<li><p>JS 커뮤니티에서 만든 모듈 시스템임, NodeJS 모듈도 CommonJS로 이루어져있음(모듈 사용이 용이함)</p>
</li>
<li><p>아래와 같이 별도의 파일로 저장된 것에 대해서 모듈을 써서 할 수 있음</p>
<pre><code class="language-js">// CommonJS (Export)
function Person(name, age, location) {
this.name = name;
this.age = age;
this.location = location;

this.getName = function () {
  return this.name + &#39;입니다&#39;;
};
}
</code></pre>
</li>
</ul>
<p>module.exports = Person;</p>
<pre><code>```js
// CommonJS (Import)
// require를 통해서 모듈을 불러와서 사용함
const Person = require(&#39;./02-CommonJS-person&#39;);

const me = new Person(&#39;kevin&#39;, 10, &#39;Korea&#39;);
const you = new Person(&#39;hart&#39;, 20, &#39;USA&#39;);

console.log(me.getName());
console.log(you.getName());</code></pre><ul>
<li>다양한 파일을 가져오고 다양한 함수, 객체등을 불러와서 처리할 수 있음, 이를 통해서 굳이 복잡하게 파일을 구성하지 않고 파일을 여러개 나누고 분리해서 재활용을 할 수 있음</li>
<li>파일 단위로 나눠서 편하게 할 수 있음</li>
</ul>
<h3 id="amd">amd</h3>
<ul>
<li><p>Asynchronous Module Definition의 약자, 모듈을 선언하면서 의존하고 있는 모듈을 함께 명시, 비동기적으로 의존 모듈을 불러옴</p>
</li>
<li><p>왜냐하면 CommonJS는 동기적으로 쓰기 때문임</p>
</li>
<li><p>아래와 같이 별도의 정의 문법을 바탕으로 사용할 수 있음</p>
<pre><code class="language-js">define([&#39;module&#39;], function(module) {
return function() {

}
})</code></pre>
</li>
<li><p>CommonJS는 서버에서 쓰면서 동기적으로 동작하고 amd의 경우 브라우저에서도 동작할 수 있고 비동기적인 특성을 가짐</p>
</li>
<li><p>amd를 주로 쓰지 않지만 특성을 알아두면 좋음</p>
</li>
</ul>
<h3 id="umd">umd</h3>
<ul>
<li>Universal Module Definition으로 AMD와 CommonJS 두 방식 모두 지원하고 클라이언트, 서버 어디에서나 작동함</li>
<li>아래와 같이 적용되서 쓸 수 있음<pre><code class="language-js">(function (root, factory) {
if (typeof exports === &#39;object&#39; &amp;&amp; module.exports) {
  // CommonJS
  module.exports = factory(require(&#39;module&#39;))
} else if (typeof define === &#39;function&#39; &amp;&amp; define.amd) {
  // AMD
  define([&#39;module&#39;], function (module) { })
} else {
  // 전역 공간
  root.global = factory(root.module)
}
}(this, function (module) {
// 실제 모듈
}))</code></pre>
</li>
</ul>
<h3 id="es-module">es-module</h3>
<ul>
<li>es-module은 최신 브라우저에서만 동작함, 아래와 같이 간단하게 키워드만을 통해서 쓸 수 있음<pre><code class="language-js">export const a = &#39;a&#39;;
</code></pre>
</li>
</ul>
<p>export function hello() {
  return &#39;hello&#39;;
}</p>
<p>export { a, hello };</p>
<pre><code>```js
import { a, hello } from &#39;./05-es-module-export.js&#39;;
// as 키워드로 이름을 바꿀 수도 있음
// import * as i from &#39;./05-es-module-export.js&#39;;

console.log(a);
console.log(hello());</code></pre><ul>
<li>기본 내보내기와 기본 가져오기는 아래와 같이 할 수 있음, 해당 파일에서 내보내는 것이 기본이라 중괄호 없이 쓴 것<pre><code class="language-js">function hello() {
return &#39;hello&#39;;
}
</code></pre>
</li>
</ul>
<p>export default hello;</p>
<pre><code>```js
import h from &#39;./05-es-module-default-export&#39;;

console.log(h());</code></pre><ul>
<li><p>브라우저에서도 아래와 같이 script 타입을 모듈로 지정해서 쓸 수 있음</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
  &lt;title&gt;Zero base&lt;/title&gt;
  &lt;meta charset=&quot;utf-8&quot; /&gt;
  &lt;script type=&quot;module&quot; src=&quot;./05-es-module-export.js&quot;&gt;&lt;/script&gt;
  &lt;script type=&quot;module&quot; src=&quot;./05-es-module-default-export.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;&lt;/body&gt;
&lt;/html&gt;</code></pre>
<pre><code class="language-js">const a = &#39;a&#39;;
</code></pre>
</li>
</ul>
<p>function hello() {
  return &#39;hello&#39;;
}</p>
<p>export { a, hello };</p>
<pre><code>- 이 때 위의 경우 모듈을 순서대로 가져오는데 `async` 키워드를 붙이면 예측할 수 없음(비동기로 불러오니까)
```html
    &lt;script async type=&quot;module&quot; src=&quot;./05-es-module-export.js&quot;&gt;&lt;/script&gt;
    &lt;script async type=&quot;module&quot; src=&quot;./05-es-module-default-export.js&quot;&gt;&lt;/script&gt;</code></pre><ul>
<li><p>아래와 같이 되는 것을 확인가능함</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
  &lt;title&gt;Zero base&lt;/title&gt;
  &lt;meta charset=&quot;utf-8&quot; /&gt;
  &lt;script type=&quot;module&quot; src=&quot;./05-es-module-export.js&quot;&gt;&lt;/script&gt;
  &lt;script type=&quot;module&quot;&gt;
    import { a, hello } from &#39;./05-es-module-export.js&#39;;

    console.log(a);
    console.log(hello());
  &lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;&lt;/body&gt;
&lt;/html&gt;</code></pre>
</li>
<li><p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Modules">참고자료</a></p>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>