<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yoonmin_tech</title>
        <link>https://velog.io/</link>
        <description>'같이의 가치를'</description>
        <lastBuildDate>Mon, 08 Dec 2025 11:35:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yoonmin_tech</title>
            <url>https://velog.velcdn.com/images/yoon_min/profile/ff409229-09bd-4440-a751-f4a581445218/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yoonmin_tech. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yoon_min" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Flutter + Riverpod + Firebase: 로그아웃 후 재로그인 시 "permission-denied" 오류 해결하기]]></title>
            <link>https://velog.io/@yoon_min/Flutter-Riverpod-Firebase-%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83-%ED%9B%84-%EC%9E%AC%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%8B%9C-permission-denied-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yoon_min/Flutter-Riverpod-Firebase-%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83-%ED%9B%84-%EC%9E%AC%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%8B%9C-permission-denied-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 08 Dec 2025 11:35:38 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoon_min/post/c4ff1230-56f5-4e73-816b-8b686f541f61/image.gif" alt=""></p>
<h2 id="🚨-문제-상황">🚨 문제 상황</h2>
<p>Flutter 앱에서 Firebase Authentication과 Firestore를 사용하고, 상태 관리로 Riverpod을 사용할 때 다음과 같은 문제가 발생했습니다.</p>
<blockquote>
<p><strong>로그아웃 후 동일한 계정으로 재로그인하면 &quot;permission-denied&quot; 오류가 발생하고, 앱을 완전히 종료했다가 다시 시작해야만 정상 작동함</strong></p>
</blockquote>
<pre><code>[cloud_firestore/permission-denied] Missing or insufficient permissions.</code></pre><p><img src="https://firebasestorage.googleapis.com/..." alt="에러 화면"></p>
<hr>
<h2 id="🔍-원인-분석">🔍 원인 분석</h2>
<h3 id="1-riverpod-provider의-캐싱-문제">1. Riverpod Provider의 캐싱 문제</h3>
<p>Riverpod의 <code>StreamProvider</code>는 한 번 생성되면 <strong>내부적으로 Stream을 캐싱</strong>합니다.
로그아웃 시 Firebase Auth는 로그아웃 처리가 되지만, Provider가 구독하고 있던 <strong>이전 사용자의 Firestore Stream이 여전히 활성화</strong>되어 있습니다.</p>
<pre><code class="language-dart">// ❌ 문제가 있는 코드
final childrenStreamProvider = StreamProvider&lt;List&lt;ChildData&gt;&gt;((ref) {
  final firestoreService = ref.watch(firestoreServiceProvider);
  return firestoreService.getChildren(); // 이전 사용자의 Stream이 캐싱됨
});</code></pre>
<h3 id="2-타이밍-이슈">2. 타이밍 이슈</h3>
<p>로그인 → 홈 화면 이동 과정에서:</p>
<ol>
<li>Firebase Auth가 새 사용자로 로그인됨</li>
<li>하지만 Provider는 아직 이전 상태를 유지</li>
<li>Firestore는 새 사용자 토큰으로 접근하려 하지만, 쿼리는 이전 사용자 기준</li>
<li><strong>권한 오류 발생!</strong></li>
</ol>
<hr>
<h2 id="✅-해결-방법">✅ 해결 방법</h2>
<h3 id="핵심-아이디어-provider가-auth-상태-변화에-자동-반응하도록-만들기">핵심 아이디어: Provider가 Auth 상태 변화에 자동 반응하도록 만들기</h3>
<p><code>authStateChangesProvider</code>를 <strong>watch</strong>하면, 로그인/로그아웃 시 Provider가 자동으로 <strong>재생성</strong>됩니다.</p>
<h3 id="step-1-auth-state-provider-확인">Step 1: Auth State Provider 확인</h3>
<p>먼저 <code>authStateChangesProvider</code>가 정의되어 있는지 확인합니다.</p>
<pre><code class="language-dart">// lib/core/providers/auth_providers.dart

import &#39;package:firebase_auth/firebase_auth.dart&#39;;
import &#39;package:flutter_riverpod/flutter_riverpod.dart&#39;;

/// Firebase Auth 상태 변화를 감지하는 StreamProvider
final authStateChangesProvider = StreamProvider&lt;User?&gt;((ref) {
  return FirebaseAuth.instance.authStateChanges();
});</code></pre>
<h3 id="step-2-firestore-streamprovider가-auth-상태를-watch하도록-수정">Step 2: Firestore StreamProvider가 Auth 상태를 Watch하도록 수정</h3>
<pre><code class="language-dart">// lib/core/providers/children_provider.dart

import &#39;package:flutter_riverpod/flutter_riverpod.dart&#39;;
import &#39;auth_providers.dart&#39;; // authStateChangesProvider import

/// 아동 목록을 Firebase에서 실시간으로 가져오는 Stream Provider
/// ✅ auth 상태가 변경되면 자동으로 다시 구독함
final childrenStreamProvider = StreamProvider&lt;List&lt;ChildData&gt;&gt;((ref) {
  // 🔑 핵심: auth 상태를 watch하여 로그인/로그아웃 시 자동으로 provider 재생성
  final authState = ref.watch(authStateChangesProvider);

  return authState.when(
    data: (user) {
      if (user == null) {
        // 로그인하지 않은 경우 빈 리스트 반환
        return Stream.value(&lt;ChildData&gt;[]);
      }
      // 로그인한 경우 Firestore에서 데이터 가져오기
      final firestoreService = ref.watch(firestoreServiceProvider);
      return firestoreService.getChildren().map((childrenData) {
        return childrenData
            .map(ChildDataConverter.fromFirestore)
            .where((child) =&gt; !child.isDeleted)
            .toList();
      });
    },
    loading: () =&gt; Stream.value(&lt;ChildData&gt;[]),
    error: (_, __) =&gt; Stream.value(&lt;ChildData&gt;[]),
  );
});</code></pre>
<h3 id="step-3-ui에서-streamprovider-직접-사용">Step 3: UI에서 StreamProvider 직접 사용</h3>
<p><code>StateNotifierProvider</code>를 거치지 않고 <code>StreamProvider</code>를 직접 watch하면 더 반응적으로 동작합니다.</p>
<pre><code class="language-dart">// lib/views/home/pages/child_list_page.dart

@override
Widget build(BuildContext context) {
  // ✅ childrenStreamProvider를 직접 사용하여 auth 상태 변화에 자동 반응
  final childrenAsync = ref.watch(childrenStreamProvider);

  return childrenAsync.when(
    loading: () =&gt; const Center(child: CircularProgressIndicator()),
    error: (error, stack) =&gt; Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Icon(Icons.error_outline, size: 48, color: Colors.red),
          const SizedBox(height: 16),
          Text(&#39;데이터 로드 실패: $error&#39;),
          const SizedBox(height: 16),
          ElevatedButton(
            onPressed: () =&gt; ref.invalidate(childrenStreamProvider),
            child: const Text(&#39;다시 시도&#39;),
          ),
        ],
      ),
    ),
    data: (children) =&gt; _buildContent(context, children),
  );
}</code></pre>
<h3 id="step-4-선택-로그아웃-시-provider-명시적-무효화">Step 4: (선택) 로그아웃 시 Provider 명시적 무효화</h3>
<p>추가적인 안전장치로 로그아웃 시 Provider를 명시적으로 무효화할 수 있습니다.</p>
<pre><code class="language-dart">// lib/views/settings/settings_screen.dart

Future&lt;void&gt; _handleLogout(BuildContext context, WidgetRef ref) async {
  // ✅ 로그아웃 전에 Provider 무효화 (순서 중요!)
  ref.invalidate(firestoreServiceProvider);
  ref.invalidate(childrenStreamProvider);

  // 이제 로그아웃
  final authController = ref.read(authControllerProvider.notifier);
  await authController.signOut();

  if (context.mounted) {
    context.go(&#39;/login&#39;);
  }
}</code></pre>
<hr>
<h2 id="🎯-핵심-포인트-정리">🎯 핵심 포인트 정리</h2>
<table>
<thead>
<tr>
<th>문제</th>
<th>해결책</th>
</tr>
</thead>
<tbody><tr>
<td>Provider가 이전 사용자 Stream 캐싱</td>
<td><code>authStateChangesProvider</code>를 watch하여 자동 재생성</td>
</tr>
<tr>
<td>StateNotifier의 초기화 타이밍 이슈</td>
<td><code>StreamProvider</code> 직접 사용</td>
</tr>
<tr>
<td>로그아웃 후 잔여 상태</td>
<td>로그아웃 <strong>전에</strong> <code>ref.invalidate()</code> 호출</td>
</tr>
</tbody></table>
<hr>
<h2 id="📊-before-vs-after">📊 Before vs After</h2>
<h3 id="❌-before-문제-있는-구조">❌ Before (문제 있는 구조)</h3>
<pre><code>로그아웃 → 로그인 → 홈 화면
              ↓
    Provider: 이전 사용자 Stream 유지
              ↓
    Firestore: &quot;permission-denied&quot; 💥</code></pre><h3 id="✅-after-해결된-구조">✅ After (해결된 구조)</h3>
<pre><code>로그아웃 → (Provider invalidate) → 로그인 → 홈 화면
                                      ↓
                          authStateChangesProvider 변화 감지
                                      ↓
                          childrenStreamProvider 자동 재생성
                                      ↓
                          새 사용자 기준 Firestore 쿼리 ✅</code></pre><hr>
<h2 id="🔑-결론">🔑 결론</h2>
<p>Riverpod + Firebase 조합에서 <strong>인증 상태 변화에 반응하는 Provider</strong>를 만드는 것이 핵심입니다.</p>
<ol>
<li><strong><code>authStateChangesProvider</code>를 watch</strong>하면 로그인/로그아웃 시 자동으로 Provider가 재생성됩니다.</li>
<li><strong><code>StreamProvider</code>를 직접 사용</strong>하면 더 반응적으로 동작합니다.</li>
<li><strong>로그아웃 시 <code>ref.invalidate()</code></strong>를 로그아웃 <strong>전에</strong> 호출하면 더 안전합니다.</li>
</ol>
<p>이 패턴을 적용하면 앱을 재시작하지 않아도 재로그인 시 정상적으로 데이터를 불러올 수 있습니다! 🎉</p>
<hr>
<h2 id="📚-참고-자료">📚 참고 자료</h2>
<ul>
<li><a href="https://riverpod.dev/docs/concepts/providers">Riverpod 공식 문서 - Provider Lifecycle</a></li>
<li><a href="https://firebase.google.com/docs/auth/flutter/manage-users#get_the_currently_signed-in_user">Firebase Auth - Auth State Changes</a></li>
<li><a href="https://riverpod.dev/docs/concepts/combining_providers">Flutter Riverpod - Combining Providers</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[husky를 이용한 Git hook 사용하기🪝]]></title>
            <link>https://velog.io/@yoon_min/husky%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-Git-hook-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yoon_min/husky%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-Git-hook-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 04 Nov 2025 14:46:24 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoon_min/post/2edf6503-39d2-4e33-b49e-d497334e405f/image.gif" alt=""></p>
<p>안녕하세요! 오늘은 Git 워크플로우의 품질과 보안을 한 단계 끌어올릴 수 있는 강력한 도구, <strong>husky</strong>에 대해 이야기해보려고 합니다. 코드 리뷰에서 반복적으로 지적되는 문제들을 사전에 방지하고, 팀의 코딩 규칙을 자동으로 강제할 수 있다면 얼마나 좋을까요? husky가 바로 그 해답입니다.</p>
<h2 id="husky란-무엇인가">Husky란 무엇인가?</h2>
<p>Husky는 Git hook을 쉽게 관리하고 공유할 수 있게 해주는 npm 패키지입니다. Git hook이란 Git의 특정 이벤트(커밋, 푸시 등)가 발생했을 때 자동으로 실행되는 스크립트를 의미합니다. 예를 들어 코드를 커밋하기 직전에 자동으로 린트 검사를 실행하거나, 푸시하기 전에 테스트를 돌려볼 수 있습니다.</p>
<p>기본적으로 Git hook은 <code>.git/hooks</code> 디렉토리에 저장되는데, 이 디렉토리는 Git 저장소에 포함되지 않습니다. 즉, 각 개발자가 로컬 환경에서 개별적으로 설정해야 한다는 뜻이죠. 여기서 husky의 진가가 드러납니다. husky는 Git의 <code>core.hooksPath</code> 설정을 활용하여 hook 설정을 프로젝트에 포함시켜 모든 팀원이 동일한 규칙을 적용받을 수 있게 만들어줍니다.</p>
<p>최신 버전인 husky v9는 설정이 더욱 간소화되었고, 성능도 크게 개선되었습니다. 단 2KB의 매우 가벼운 크기로 zero-dependency로 동작하며, 약 1ms 만에 실행됩니다. 이전 버전과 비교해 불필요한 래퍼 코드가 제거되어 설치와 사용이 훨씬 직관적입니다.</p>
<h2 id="왜-husky를-사용해야-할까">왜 Husky를 사용해야 할까?</h2>
<p>실제 개발 현장에서 husky가 필요한 이유를 구체적으로 살펴보겠습니다.</p>
<p>첫째, <strong>보안 문제를 사전에 차단</strong>할 수 있습니다. 개발하다 보면 실수로 API 키, 비밀번호, 인증 토큰 등 민감한 정보를 커밋할 위험이 있습니다. husky를 통해 pre-commit 단계에서 이러한 민감 정보를 자동으로 감지하고 차단할 수 있습니다. 한 번 원격 저장소에 푸시된 비밀 정보는 Git 히스토리에 영구적으로 남기 때문에, 이를 사전에 방지하는 것이 매우 중요합니다.</p>
<p>둘째, <strong>코드 품질을 일관되게 유지</strong>할 수 있습니다. ESLint, Prettier 같은 도구를 수동으로 실행하는 것은 번거롭고, 깜빡하기 쉽습니다. husky를 사용하면 커밋할 때마다 자동으로 린트 검사와 포맷팅이 실행되어, 코드베이스의 일관성을 자연스럽게 유지할 수 있습니다.</p>
<p>셋째, <strong>CI/CD 파이프라인의 부담을 줄여줍니다</strong>. 로컬에서 기본적인 검증을 마치고 푸시하면, 원격 CI에서 실패하는 경우가 줄어듭니다. 이는 빌드 시간과 리소스를 절약하고, 개발 속도를 높이는 데 기여합니다.</p>
<p>넷째, <strong>커밋 메시지의 품질을 강제</strong>할 수 있습니다. Conventional Commits 같은 규칙을 적용하여 커밋 히스토리를 읽기 쉽게 만들고, 자동화된 체인지로그 생성이나 시맨틱 버저닝을 가능하게 합니다.</p>
<h2 id="husky-세팅하기">Husky 세팅하기</h2>
<p>최신 husky v9 버전을 기준으로 설정하는 방법을 단계별로 살펴보겠습니다. 이 과정은 프로젝트 관리자가 최초 1회만 수행하면 됩니다.</p>
<h3 id="1단계-husky-설치">1단계: Husky 설치</h3>
<p>먼저 프로젝트에 husky를 개발 의존성으로 설치합니다.</p>
<pre><code class="language-bash">npm install --save-dev husky</code></pre>
<p>프로젝트가 이미 Git 저장소로 초기화되어 있어야 합니다. 만약 Git이 초기화되지 않았다면 <code>git init</code>을 먼저 실행하세요.</p>
<h3 id="2단계-husky-초기화">2단계: Husky 초기화</h3>
<p>husky를 초기화하면 필요한 디렉토리 구조와 기본 설정이 자동으로 생성됩니다.</p>
<pre><code class="language-bash">npx husky init</code></pre>
<p>이 명령어는 다음 작업들을 수행합니다:</p>
<ul>
<li><code>.husky</code> 디렉토리를 생성합니다</li>
<li>기본 <code>pre-commit</code> hook 예제 파일을 생성합니다</li>
<li><code>package.json</code>의 <code>scripts</code>에 <code>&quot;prepare&quot;: &quot;husky&quot;</code> 스크립트를 추가합니다</li>
<li>Git의 <code>core.hooksPath</code>를 <code>.husky</code>로 설정합니다</li>
</ul>
<p><code>prepare</code> 스크립트는 다른 개발자가 <code>npm install</code>을 실행할 때 자동으로 husky를 설정하도록 보장합니다. 이것이 팀원 간 설정 공유의 핵심입니다.</p>
<h3 id="3단계-실용적인-hook-설정">3단계: 실용적인 Hook 설정</h3>
<p>이제 실제로 유용한 hook들을 설정해보겠습니다. 보안과 코드 품질을 모두 고려한 실무 중심의 설정을 구성하겠습니다.</p>
<p><strong>Pre-commit Hook 설정</strong></p>
<p><code>npx husky init</code>을 실행하면 <code>.husky/pre-commit</code> 파일이 생성됩니다. v9에서는 더 이상 shebang이나 husky.sh를 참조할 필요가 없습니다. 파일을 다음과 같이 수정하세요:</p>
<pre><code class="language-bash"># 린트 검사 (v9에서는 npx 없이 직접 실행 가능 - 약 0.2초 더 빠름)
npm run lint

# 타입 체크 (TypeScript 프로젝트인 경우)
npm run type-check

# 변경된 파일만 검사하려면 lint-staged 사용
npx lint-staged</code></pre>
<p><strong>중요</strong>: Husky v9에서는 <code>#!/usr/bin/env sh</code>나 <code>. &quot;$(dirname -- &quot;$0&quot;)/_/husky.sh&quot;</code> 같은 코드가 deprecated되었습니다. 이런 코드는 제거해야 합니다. 파일에 실행할 명령어만 직접 작성하면 됩니다.</p>
<p><strong>Commit-msg Hook 설정</strong></p>
<p>커밋 메시지의 형식을 검증하는 hook을 추가하려면 직접 파일을 생성하거나 echo 명령어를 사용합니다. v9에서는 <code>npx husky add</code> 명령어가 제거되었으므로 다음 방법을 사용하세요:</p>
<pre><code class="language-bash"># 방법 1: echo 명령어 사용 (권장)
echo &quot;npx --no -- commitlint --edit \$1&quot; &gt; .husky/commit-msg

# 방법 2: 직접 파일 생성
# .husky/commit-msg 파일을 생성하고 다음 내용을 입력</code></pre>
<p><code>.husky/commit-msg</code> 파일 내용:</p>
<pre><code class="language-bash">npx --no -- commitlint --edit $1</code></pre>
<p>이를 위해 commitlint도 설치해야 합니다:</p>
<pre><code class="language-bash">npm install --save-dev @commitlint/cli @commitlint/config-conventional</code></pre>
<p>프로젝트 루트에 <code>commitlint.config.js</code> 파일을 생성합니다:</p>
<pre><code class="language-javascript">// commitlint.config.js
export default {
  extends: [&#39;@commitlint/config-conventional&#39;],
  rules: {
    // 커스텀 규칙 추가 가능
    &#39;type-enum&#39;: [
      2,
      &#39;always&#39;,
      [
        &#39;feat&#39;,     // 새로운 기능
        &#39;fix&#39;,      // 버그 수정
        &#39;docs&#39;,     // 문서 변경
        &#39;style&#39;,    // 코드 포맷팅
        &#39;refactor&#39;, // 리팩토링
        &#39;perf&#39;,     // 성능 개선
        &#39;test&#39;,     // 테스트 추가/수정
        &#39;chore&#39;,    // 빌드 프로세스, 도구 변경
        &#39;security&#39;  // 보안 관련 변경
      ]
    ],
    &#39;subject-case&#39;: [2, &#39;never&#39;, [&#39;upper-case&#39;]]
  }
};</code></pre>
<p><strong>Pre-push Hook 설정</strong></p>
<p>푸시하기 전에 테스트를 실행하는 것도 좋은 습관입니다:</p>
<pre><code class="language-bash">echo &quot;npm test&quot; &gt; .husky/pre-push</code></pre>
<h3 id="4단계-lint-staged-설정">4단계: lint-staged 설정</h3>
<p>변경된 파일만 검사하여 속도를 높이려면 lint-staged를 사용합니다:</p>
<pre><code class="language-bash">npm install --save-dev lint-staged</code></pre>
<p><code>package.json</code>에 설정을 추가합니다:</p>
<pre><code class="language-json">{
  &quot;lint-staged&quot;: {
    &quot;*.{js,jsx,ts,tsx}&quot;: [
      &quot;eslint --fix&quot;,
      &quot;prettier --write&quot;
    ],
    &quot;*.{json,md,yml,yaml}&quot;: [
      &quot;prettier --write&quot;
    ]
  }
}</code></pre>
<h3 id="5단계-보안-강화-설정">5단계: 보안 강화 설정</h3>
<p>민감 정보 유출 방지를 위해 추가적인 검사를 <code>.husky/pre-commit</code>에 추가합니다:</p>
<pre><code class="language-bash"># 변경된 파일만 검사 (lint-staged 사용)
npx lint-staged

# 환경 변수 파일이 커밋되지 않도록 검사
if git diff --cached --name-only | grep -qE &quot;^\.env$|^\.env\.&quot;; then
  echo &quot;❌ Error: .env files should not be committed!&quot;
  echo &quot;Please remove .env files from staging area.&quot;
  exit 1
fi

# 민감한 정보 패턴 검사 (선택적)
if git diff --cached | grep -qE &quot;(password|secret|api[_-]?key|private[_-]?key|token)\s*[:=]&quot;; then
  echo &quot;⚠️  Warning: Possible sensitive information detected!&quot;
  echo &quot;Please review your changes carefully.&quot;
  exit 1
fi</code></pre>
<p><strong>추가 보안 도구 (선택사항):</strong></p>
<p>더 강력한 보안 검사를 원한다면 gitleaks를 사용할 수 있습니다:</p>
<pre><code class="language-bash">npm install --save-dev @gitleaks/gitleaks</code></pre>
<p><code>.husky/pre-commit</code>에 추가:</p>
<pre><code class="language-bash">npx gitleaks protect --staged --verbose</code></pre>
<h2 id="관리자-입장에서의-사용-방법">관리자 입장에서의 사용 방법</h2>
<p>프로젝트 관리자로서 husky를 도입하고 관리할 때는 몇 가지 중요한 원칙을 따라야 합니다.</p>
<p><strong>명확한 문서화</strong></p>
<p>팀원들이 husky의 목적과 동작 방식을 이해할 수 있도록 <code>README.md</code>에 명확히 문서화하세요. 특히 hook이 실패했을 때 어떻게 대처해야 하는지, 정당한 이유로 hook을 우회해야 할 때는 어떻게 하는지 설명해야 합니다.</p>
<pre><code class="language-markdown">## Git Hooks (Husky v9)

이 프로젝트는 코드 품질과 보안을 위해 Git hook을 사용합니다.

### 설정된 Hook들

- **pre-commit**: 린트, 포맷팅, 타입 체크 실행 및 민감 정보 검사
- **commit-msg**: 커밋 메시지 형식 검증 (Conventional Commits)
- **pre-push**: 전체 테스트 스위트 실행

### Hook 우회하기 (긴급한 경우만)

정말 긴급한 상황에서만 다음 방법을 사용하세요:

```bash
# 단일 커밋에만 적용
git commit --no-verify -m &quot;hotfix: critical security patch&quot;

# 또는 환경 변수 사용
HUSKY=0 git commit -m &quot;hotfix: critical security patch&quot;</code></pre>
<p>단, 이는 정말 필요한 경우에만 사용하고, 나중에 반드시 해당 커밋을 정리하세요.</p>
<pre><code>
**점진적 도입**

처음부터 너무 엄격한 규칙을 적용하면 팀원들의 반발을 살 수 있습니다. 먼저 경고만 표시하도록 설정하고, 팀이 익숙해지면 점진적으로 강제하는 방식을 권장합니다.

```bash
# 처음에는 경고만 표시
npm run lint || echo &quot;⚠️  Lint warnings detected, but allowing commit for now&quot;

# 익숙해지면 강제로 변경
npm run lint</code></pre><p><strong>CI/CD와의 통합</strong></p>
<p>husky는 로컬 검증이지만, CI/CD 파이프라인에서도 동일한 검사를 수행해야 합니다. husky를 우회하고 푸시한 코드도 잡아낼 수 있도록 이중 안전장치를 마련하세요.</p>
<pre><code class="language-yaml"># .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    env:
      HUSKY: 0  # CI에서는 husky 비활성화
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: &#39;20&#39;
      - run: npm ci
      - run: npm run lint
      - run: npm run type-check
      - run: npm test
      - run: npx gitleaks detect --no-git -v  # 보안 검사</code></pre>
<p><strong>모니터링과 개선</strong></p>
<p>정기적으로 hook 실행 로그를 검토하고, 팀원들의 피드백을 받아 규칙을 조정하세요. 너무 많은 false positive가 발생하면 개발 속도가 저하되고, 팀원들이 hook을 우회하려는 유혹을 느낄 수 있습니다.</p>
<p><strong>버전 관리 전략</strong></p>
<p><code>package.json</code>에서 husky의 버전을 고정하고, 업데이트할 때는 팀 전체에 공지하세요:</p>
<pre><code class="language-json">{
  &quot;devDependencies&quot;: {
    &quot;husky&quot;: &quot;~9.1.0&quot;  // 마이너 버전만 자동 업데이트
  }
}</code></pre>
<p><strong>Monorepo 환경 설정</strong></p>
<p>Monorepo 구조에서는 루트가 아닌 하위 디렉토리에 husky를 설정해야 할 수 있습니다:</p>
<pre><code class="language-json">// 하위 프로젝트의 package.json
{
  &quot;scripts&quot;: {
    &quot;prepare&quot;: &quot;cd .. &amp;&amp; husky frontend/.husky&quot;
  }
}</code></pre>
<h2 id="협업자-입장에서의-사용-방법">협업자 입장에서의 사용 방법</h2>
<p>이제 일반 개발자가 husky가 설정된 프로젝트에서 작업할 때 알아야 할 내용을 살펴보겠습니다.</p>
<p><strong>최초 설정</strong></p>
<p>프로젝트를 클론한 후 의존성을 설치하면 husky가 자동으로 설정됩니다:</p>
<pre><code class="language-bash">git clone &lt;repository-url&gt;
cd &lt;project-directory&gt;
npm install  # prepare 스크립트가 자동으로 husky를 설정합니다</code></pre>
<p>만약 husky가 제대로 설정되지 않았다면 수동으로 실행할 수 있습니다:</p>
<pre><code class="language-bash">npm run prepare</code></pre>
<p>설정이 제대로 되었는지 확인하려면:</p>
<pre><code class="language-bash"># Git hook 경로 확인
git config core.hooksPath
# 결과: .husky

# Hook 파일 확인
ls -la .husky/</code></pre>
<p><strong>일상적인 워크플로우</strong></p>
<p>husky가 설정되면 평소처럼 Git을 사용하면 됩니다. 다만 커밋이나 푸시할 때 자동으로 검사가 실행된다는 점을 인지하세요:</p>
<pre><code class="language-bash"># 파일 수정 후
git add .
git commit -m &quot;feat: add user authentication&quot;
# 이 시점에서 pre-commit hook이 실행되어 린트, 포맷팅 등을 검사합니다
# commit-msg hook이 커밋 메시지 형식을 검증합니다

git push
# 이 시점에서 pre-push hook이 실행되어 테스트를 실행합니다</code></pre>
<p><strong>Hook 실패 시 대응</strong></p>
<p>Hook이 실패하면 커밋이나 푸시가 차단됩니다. 이때 당황하지 말고 다음 단계를 따르세요:</p>
<ol>
<li><p><strong>에러 메시지를 주의 깊게 읽으세요.</strong> 무엇이 문제인지 명확히 알려줍니다.</p>
</li>
<li><p><strong>문제를 수정하세요.</strong> 린트 에러라면 코드를 수정하고, 커밋 메시지 에러라면 메시지를 수정합니다:</p>
</li>
</ol>
<pre><code class="language-bash"># 린트 에러 수정 후 다시 시도
git add .
git commit -m &quot;feat: add user authentication&quot;

# 커밋 메시지가 형식에 맞지 않으면
git commit --amend -m &quot;feat: add user authentication module&quot;</code></pre>
<ol start="3">
<li><strong>자동 수정을 활용하세요.</strong> 많은 린트 도구는 자동 수정 기능을 제공합니다:</li>
</ol>
<pre><code class="language-bash">npm run lint -- --fix
# 또는
npx eslint . --fix</code></pre>
<p><strong>긴급 상황 대처</strong></p>
<p>정말 긴급한 상황에서 hook을 우회해야 할 때가 있을 수 있습니다. 하지만 이는 최후의 수단이어야 합니다:</p>
<pre><code class="language-bash"># Hook을 우회하고 커밋 (권장하지 않음)
git commit --no-verify -m &quot;hotfix: critical security patch&quot;

# 또는 환경 변수 사용 (일시적)
HUSKY=0 git commit -m &quot;hotfix: critical security patch&quot;

# 장기간 비활성화 (rebase/merge 중)
# ~/.config/husky/init.sh 파일에 다음 추가:
# export HUSKY=0</code></pre>
<p>우회한 경우 반드시 나중에 해당 커밋을 정리하고, 팀에 알려야 합니다.</p>
<p><strong>로컬 개발 환경 최적화</strong></p>
<p>Hook 실행 시간이 너무 길면 개발 경험이 저하됩니다. 다음 방법으로 최적화할 수 있습니다:</p>
<pre><code class="language-bash"># ESLint 캐시 활용
npm run lint -- --cache

# 변경된 파일만 검사 (lint-staged 사용)
# 이미 설정되어 있다면 자동으로 적용됩니다

# 특정 환경에서만 hook 실행
# ~/.config/husky/init.sh 파일에 조건부 로직 추가 가능</code></pre>
<p><strong>커밋 메시지 작성 팁</strong></p>
<p>Conventional Commits 형식을 따르면 hook이 통과하기 쉽습니다:</p>
<pre><code>&lt;타입&gt;(&lt;범위&gt;): &lt;제목&gt;

&lt;본문&gt;

&lt;푸터&gt;</code></pre><p>예시:</p>
<pre><code>feat(auth): add JWT token validation

- Implement token expiration check
- Add refresh token logic
- Update authentication middleware

Closes #123</code></pre><p>타입 종류:</p>
<ul>
<li><code>feat</code>: 새로운 기능</li>
<li><code>fix</code>: 버그 수정</li>
<li><code>docs</code>: 문서 변경</li>
<li><code>style</code>: 코드 스타일 변경 (포맷팅, 세미콜론 등)</li>
<li><code>refactor</code>: 리팩토링</li>
<li><code>perf</code>: 성능 개선</li>
<li><code>test</code>: 테스트 추가/수정</li>
<li><code>chore</code>: 빌드, 설정 파일 변경</li>
<li><code>security</code>: 보안 관련 변경</li>
</ul>
<p><strong>문제 해결</strong></p>
<p>Hook이 예상대로 동작하지 않으면:</p>
<pre><code class="language-bash"># Husky 설정 확인
ls -la .husky
git config core.hooksPath

# Hook 파일 권한 확인 (실행 권한 필요 없음 - v9부터)
# Windows에서 생성한 hook도 chmod 불필요

# Husky 재설정
rm -rf .husky
npm run prepare

# 디버그 모드로 실행
HUSKY=2 git commit -m &quot;test&quot;</code></pre>
<p><strong>v9 마이그레이션 체크리스트</strong></p>
<p>기존 프로젝트를 v9로 업그레이드하는 경우:</p>
<ol>
<li>모든 hook 파일에서 <code>#!/usr/bin/env sh</code> 제거</li>
<li>모든 hook 파일에서 <code>. &quot;$(dirname -- &quot;$0&quot;)/_/husky.sh&quot;</code> 제거  </li>
<li><code>~/.huskyrc</code> 파일이 있다면 <code>~/.config/husky/init.sh</code>로 이동</li>
<li>hook 파일에서 불필요한 <code>npx</code> 제거 (선택사항, 성능 개선)</li>
<li><code>.husky/_</code> 디렉토리는 자동 생성되며 <code>.gitignore</code>에 포함됨</li>
</ol>
<h2 id="마치며">마치며</h2>
<p>Husky v9는 이전 버전보다 훨씬 가볍고 빠르며, 사용법도 간단해졌습니다. 불필요한 래퍼 코드가 제거되어 hook 파일이 더 직관적이고 읽기 쉬워졌습니다. 단순한 도구를 넘어서 팀의 개발 문화를 개선하는 강력한 수단으로, 보안 문제를 사전에 차단하고, 코드 품질을 일관되게 유지하며, 불필요한 CI 실패를 줄여줍니다.</p>
<p>v9의 주요 개선사항을 요약하면:</p>
<ul>
<li>2KB의 초경량 패키지 (MIT 라이선스 파일이 가장 큰 파일입니다!)</li>
<li>약 1ms의 빠른 실행 속도</li>
<li>deprecated된 코드 제거로 더 깔끔한 hook 파일</li>
<li>Windows 권한 문제 해결</li>
<li>더 나은 디버깅 지원 (HUSKY=2)</li>
</ul>
<p>처음에는 추가적인 제약으로 느껴질 수 있지만, 익숙해지면 자연스러운 개발 워크플로우의 일부가 됩니다. 중요한 것은 husky를 도입하는 목적이 팀원을 통제하는 것이 아니라, 모두가 더 나은 코드를 작성하도록 돕는 것이라는 점입니다. 명확한 규칙, 적절한 피드백, 그리고 지속적인 개선을 통해 husky는 여러분의 프로젝트를 한 단계 성숙시켜줄 것입니다.</p>
<p>Happy coding! 🪝✨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS Lambda + FastAPI + GitHub Actions로 서버리스 API 자동 배포하기 🙌🏻]]></title>
            <link>https://velog.io/@yoon_min/AWS-Lambda-FastAPI-GitHub-Actions%EB%A1%9C-%EC%84%9C%EB%B2%84%EB%A6%AC%EC%8A%A4-API-%EC%9E%90%EB%8F%99-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yoon_min/AWS-Lambda-FastAPI-GitHub-Actions%EB%A1%9C-%EC%84%9C%EB%B2%84%EB%A6%AC%EC%8A%A4-API-%EC%9E%90%EB%8F%99-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 15 Oct 2025 16:52:44 GMT</pubDate>
            <description><![CDATA[<p>FastAPI로 만든 API를 AWS Lambda에 배포하고, GitHub에 코드를 푸시하면 자동으로 배포되는 파이프라인을 만들어보겠습니다.</p>
<h2 id="기술-스택-선택-이유">기술 스택 선택 이유</h2>
<ul>
<li><strong>FastAPI</strong>: 빠르고 현대적인 Python 웹 프레임워크, 자동 문서화 지원</li>
<li><strong>AWS Lambda</strong>: 서버 관리 불필요, 사용한 만큼만 과금</li>
<li><strong>SAM</strong>: 서버리스 앱을 간단하게 정의하고 배포</li>
<li><strong>GitHub Actions</strong>: 코드 저장소에서 바로 사용 가능한 CI/CD</li>
</ul>
<h2 id="전체-흐름">전체 흐름</h2>
<pre><code>GitHub Push → 자동 테스트 → 빌드 → AWS 배포 → 헬스체크</code></pre><h2 id="핵심-보안-oidc-인증">핵심 보안: OIDC 인증</h2>
<p>기존에는 AWS Access Key를 GitHub에 저장했지만, 이제는 <strong>OIDC 방식</strong>을 사용합니다.</p>
<p><strong>장점:</strong></p>
<ul>
<li>Access Key 저장 불필요</li>
<li>키 유출 걱정 없음</li>
<li>AWS 공식 권장 방식</li>
</ul>
<p><strong>설정 3단계:</strong></p>
<ol>
<li>AWS IAM에 GitHub을 Identity Provider로 등록</li>
<li>GitHub Actions용 IAM Role 생성 (특정 리포지토리만 접근 가능하도록)</li>
<li>최소 권한만 부여 (Lambda, API Gateway, CloudFormation 등)</li>
</ol>
<h2 id="프로젝트-구조">프로젝트 구조</h2>
<pre><code>project/
├── .github/workflows/deploy.yml    # GitHub Actions 설정
├── src/
│   ├── app.py                      # FastAPI 앱
│   └── requirements.txt
├── tests/test_app.py               # 테스트
├── template.yaml                   # SAM 인프라 정의
└── samconfig.toml                  # SAM 설정</code></pre><h2 id="fastapi-앱-작성-예시">FastAPI 앱 작성 (예시)</h2>
<pre><code class="language-python">from fastapi import FastAPI
from mangum import Mangum

app = FastAPI()

@app.get(&quot;/health&quot;)
async def health():
    return {&quot;status&quot;: &quot;healthy&quot;}

@app.post(&quot;/api/items&quot;)
async def create_item(item: dict):
    return {&quot;id&quot;: &quot;123&quot;, &quot;item&quot;: item}

# Lambda용 핸들러
handler = Mangum(app, lifespan=&quot;off&quot;)</code></pre>
<p><strong>주요 포인트:</strong></p>
<ul>
<li><code>Mangum</code>: FastAPI를 Lambda에서 실행하게 해주는 어댑터</li>
<li>환경 변수로 설정 관리 (API 키, CORS 등)</li>
<li>입력 검증과 에러 핸들링 필수</li>
</ul>
<h2 id="sam-template-인프라-정의">SAM Template (인프라 정의)</h2>
<pre><code class="language-yaml">Resources:
  FastAPIFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.12
      Architectures: [arm64]  # 비용 절감
      Handler: app.handler
      Environment:
        Variables:
          ENVIRONMENT: dev
      Events:
        ApiEvent:
          Type: HttpApi
          Properties:
            Path: /{proxy+}
            Method: ANY</code></pre>
<p><strong>핵심 설정:</strong></p>
<ul>
<li>Python 3.12 최신 버전</li>
<li>ARM64 아키텍처 (20% 저렴)</li>
<li>HTTP API 사용 (REST API보다 빠르고 저렴)</li>
<li>스로틀링으로 과도한 요청 방지</li>
</ul>
<h2 id="github-actions-워크플로우">GitHub Actions 워크플로우</h2>
<pre><code class="language-yaml">name: Deploy to AWS

on:
  push:
    branches: [main, develop]

permissions:
  id-token: write  # OIDC 토큰용
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # 1. 코드 체크아웃
      - uses: actions/checkout@v4

      # 2. Python 설정
      - uses: actions/setup-python@v5
        with:
          python-version: &#39;3.12&#39;

      # 3. 테스트
      - run: |
          pip install -r src/requirements.txt pytest
          pytest tests/

      # 4. AWS 인증 (OIDC)
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ap-northeast-2

      # 5. SAM 빌드 &amp; 배포
      - uses: aws-actions/setup-sam@v2
      - run: |
          sam build
          sam deploy --no-confirm-changeset

      # 6. 헬스체크
      - run: curl -f $API_ENDPOINT/health</code></pre>
<p><strong>워크플로우 흐름:</strong></p>
<ol>
<li>코드 품질 검사 (린팅, 보안 스캔)</li>
<li>테스트 실행</li>
<li>OIDC로 AWS 인증</li>
<li>SAM으로 빌드 &amp; 배포</li>
<li>API 정상 작동 확인</li>
</ol>
<h2 id="보안-체크리스트">보안 체크리스트</h2>
<p>✅ <strong>반드시 해야 할 것:</strong></p>
<ul>
<li>OIDC 인증 사용 (Access Key 금지)</li>
<li>최소 권한 IAM 정책</li>
<li>API 인증 추가 (API Key, JWT 등)</li>
<li>CORS 특정 도메인만 허용</li>
<li>의존성 보안 스캔 (Bandit, Safety)</li>
<li>민감 정보는 GitHub Secrets 사용</li>
<li>환경별 분리 (dev, prod)</li>
</ul>
<h2 id="로컬-테스트">로컬 테스트</h2>
<pre><code class="language-bash"># SAM 로컬 실행
sam build
sam local start-api

# 테스트
curl http://localhost:3000/health</code></pre>
<h2 id="배포-과정">배포 과정</h2>
<ol>
<li><strong>코드 푸시</strong>: <code>git push origin main</code></li>
<li><strong>자동 실행</strong>: GitHub Actions 트리거</li>
<li><strong>품질 검사</strong>: 린팅, 테스트, 보안 스캔</li>
<li><strong>빌드</strong>: SAM이 Lambda 패키지 생성</li>
<li><strong>배포</strong>: CloudFormation으로 AWS 리소스 생성</li>
<li><strong>검증</strong>: 헬스체크로 정상 작동 확인</li>
</ol>
<p>완료되면 API 엔드포인트 URL을 받고, 바로 사용 가능합니다.✨
<img src="https://velog.velcdn.com/images/yoon_min/post/686cf800-d7f7-4b25-b0a9-ab2c5ccf50e1/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GitHub와 GitHub Desktop 시작하기]]></title>
            <link>https://velog.io/@yoon_min/GitHub%EC%99%80-GitHub-Desktop-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yoon_min/GitHub%EC%99%80-GitHub-Desktop-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 30 Sep 2025 11:44:10 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoon_min/post/131b547b-4bea-408c-a34d-a64803a92c05/image.gif" alt=""></p>
<h2 id="1-github란">1. GitHub란?</h2>
<p>GitHub는 Git 기반의 코드 호스팅 플랫폼입니다. 현재 1억 5천만 명 이상이 사용하며 4억 2천만 개 이상의 프로젝트가 호스팅되고 있습니다.</p>
<h3 id="최신-주요-기능-2024-2025">최신 주요 기능 (2024-2025)</h3>
<p><strong>GitHub Copilot 멀티모델 지원</strong>
Claude 3.5 Sonnet, Gemini 1.5 Pro, OpenAI o1 등 여러 AI 모델을 선택해서 사용할 수 있습니다. 작업에 따라 최적의 모델을 선택할 수 있어 효율적입니다.</p>
<p><strong>자동 보안 수정</strong>
Copilot Autofix는 JavaScript, TypeScript, Java, Python에서 90% 이상의 보안 알림을 자동으로 수정합니다.</p>
<p><strong>Push Protection</strong>
비밀 정보(API 키, 비밀번호 등)가 레포지토리에 푸시되기 전에 자동으로 차단합니다.</p>
<hr>
<h2 id="2-github-desktop이란">2. GitHub Desktop이란?</h2>
<p>GitHub Desktop은 명령줄 대신 GUI로 Git을 쉽게 사용할 수 있게 해주는 무료 오픈소스 도구입니다.</p>
<h3 id="2025년-신규-기능">2025년 신규 기능</h3>
<p><strong>멀티도메인 지원</strong>
여러 GitHub 계정(회사, 개인 등)에 동시에 로그인할 수 있습니다.</p>
<p><strong>파일 필터링</strong>
변경된 파일이 많을 때 파일명으로 필터링하여 빠르게 찾을 수 있습니다.</p>
<hr>
<h2 id="3-github-desktop-설치">3. GitHub Desktop 설치</h2>
<h3 id="macos">macOS</h3>
<pre><code class="language-bash">brew install --cask github</code></pre>
<h3 id="windows">Windows</h3>
<pre><code class="language-bash">winget install github-desktop</code></pre>
<p>또는 <a href="https://desktop.github.com/">공식 웹사이트</a>에서 다운로드</p>
<hr>
<h2 id="4-기본-사용법">4. 기본 사용법</h2>
<h3 id="41-레포지토리-생성">4.1 레포지토리 생성</h3>
<pre><code>1. File &gt; New Repository
2. 이름과 경로 입력
3. .gitignore 선택 (중요!)
4. Create Repository</code></pre><p><strong>⚠️ 보안 팁: .gitignore에 꼭 포함할 것</strong></p>
<pre><code>.env
.env.local
*.key
*.pem
config/secrets.yml
node_modules/</code></pre><h3 id="42-레포지토리-복제">4.2 레포지토리 복제</h3>
<pre><code>1. File &gt; Clone Repository
2. URL 입력 또는 목록에서 선택
3. 저장할 폴더 선택
4. Clone</code></pre><h3 id="43-변경사항-커밋">4.3 변경사항 커밋</h3>
<pre><code>1. 파일 수정
2. GitHub Desktop에서 변경사항 확인
3. 커밋 메시지 작성
4. &quot;Commit to main&quot; 클릭
5. &quot;Push origin&quot; 클릭</code></pre><h3 id="44-브랜치-작업">4.4 브랜치 작업</h3>
<pre><code>1. Branch &gt; New Branch
2. 브랜치 이름 입력 (예: feature/login)
3. 작업 후 커밋
4. Branch &gt; Create Pull Request</code></pre><hr>
<h2 id="5-필수-보안-설정">5. 필수 보안 설정</h2>
<h3 id="51-2단계-인증-2fa-설정">5.1 2단계 인증 (2FA) 설정</h3>
<p>2FA는 계정 보안을 크게 향상시킵니다. 비밀번호만으로는 피싱이나 재사용으로 손상될 수 있습니다.</p>
<p><strong>권장 순서:</strong></p>
<ol>
<li><strong>하드웨어 보안 키</strong> (YubiKey 등) - 가장 안전</li>
<li><strong>Passkeys</strong> (Touch ID, Face ID)</li>
<li><strong>인증 앱</strong> (Google Authenticator, Authy)</li>
<li><strong>SMS</strong> (최후의 수단)</li>
</ol>
<p><strong>설정 방법:</strong></p>
<pre><code>GitHub.com &gt; Settings &gt; 
Password and authentication &gt; 
Two-factor authentication &gt; Enable</code></pre><h3 id="52-ssh-키-설정-https보다-안전">5.2 SSH 키 설정 (HTTPS보다 안전)</h3>
<pre><code class="language-bash"># 최신 알고리즘 사용
ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;

# 공개 키 복사
cat ~/.ssh/id_ed25519.pub

# GitHub에 등록: Settings &gt; SSH and GPG keys</code></pre>
<h3 id="53-환경-변수-사용">5.3 환경 변수 사용</h3>
<p><strong>절대 커밋하면 안 되는 것:</strong></p>
<ul>
<li>API 키</li>
<li>데이터베이스 비밀번호</li>
<li>JWT 시크릿</li>
<li>개인 키 파일</li>
</ul>
<p><strong>.env 파일 사용 (절대 커밋 금지!)</strong></p>
<pre><code class="language-bash"># .env
DATABASE_URL=postgresql://user:password@localhost/db
API_KEY=your-secret-key</code></pre>
<p><strong>코드에서 사용:</strong></p>
<pre><code class="language-javascript">// Node.js
require(&#39;dotenv&#39;).config();
const apiKey = process.env.API_KEY;</code></pre>
<pre><code class="language-python"># Python
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv(&#39;API_KEY&#39;)</code></pre>
<hr>
<h2 id="6-실무-활용-팁">6. 실무 활용 팁</h2>
<h3 id="61-브랜치-전략">6.1 브랜치 전략</h3>
<pre><code>main (배포용)
  └── develop (개발용)
      ├── feature/new-feature (새 기능)
      ├── bugfix/fix-login (버그 수정)
      └── hotfix/security-patch (긴급 수정)</code></pre><h3 id="62-커밋-메시지-작성법">6.2 커밋 메시지 작성법</h3>
<pre><code>✅ 좋은 예:
feat: 사용자 로그인 기능 추가
fix: 비밀번호 유효성 검사 버그 수정
docs: README에 설치 가이드 추가

❌ 나쁜 예:
수정
test
ㅇㅇ</code></pre><h3 id="63-pull-request-체크리스트">6.3 Pull Request 체크리스트</h3>
<pre><code class="language-markdown">## 작업 내용
- [ ] 새로운 기능 구현 완료
- [ ] 테스트 코드 작성
- [ ] 문서 업데이트

## 보안 체크
- [ ] 민감한 정보 제거 확인
- [ ] 입력 검증 추가
- [ ] 의존성 취약점 없음</code></pre>
<hr>
<h2 id="7-자주-묻는-질문">7. 자주 묻는 질문</h2>
<p><strong>Q: 실수로 비밀번호를 커밋했어요!</strong></p>
<pre><code class="language-bash"># 1. 즉시 해당 비밀번호 변경
# 2. GitHub Desktop에서 해당 커밋 되돌리기
# 3. 이미 푸시했다면 강제 푸시 (조심!)
git push --force</code></pre>
<p><strong>Q: 충돌이 발생했어요!</strong></p>
<pre><code>1. GitHub Desktop에서 충돌 파일 확인
2. &quot;Open in [Editor]&quot; 클릭
3. &lt;&lt;&lt;&lt; HEAD와 &gt;&gt;&gt;&gt; 사이 코드 수정
4. 저장 후 커밋</code></pre><p><strong>Q: 이전 버전으로 돌아가고 싶어요!</strong></p>
<pre><code>1. History 탭에서 원하는 커밋 찾기
2. 우클릭 &gt; Revert this commit</code></pre><hr>
<h2 id="8-추천-도구">8. 추천 도구</h2>
<p><strong>보안 스캔:</strong></p>
<ul>
<li>GitGuardian - 비밀 정보 탐지</li>
<li>Snyk - 의존성 취약점 검사</li>
</ul>
<p><strong>코드 품질:</strong></p>
<ul>
<li>ESLint - JavaScript 린팅</li>
<li>Prettier - 코드 포맷팅</li>
</ul>
<hr>
<h2 id="9-핵심-요약">9. 핵심 요약</h2>
<p>✅ <strong>GitHub Desktop으로 GUI에서 쉽게 Git 사용</strong>
✅ <strong>2FA는 필수! 하드웨어 키 권장</strong>
✅ <strong>환경 변수로 비밀 정보 관리</strong>
✅ <strong>.gitignore로 민감한 파일 제외</strong>
✅ <strong>브랜치 전략으로 체계적 개발</strong>
✅ <strong>Pull Request로 코드 리뷰</strong></p>
<p>보안은 귀찮은 것이 아니라 필수입니다. 처음부터 올바른 습관을 들이면 나중에 큰 문제를 예방할 수 있습니다! 🚀</p>
<hr>
<p><strong>참고 링크:</strong></p>
<ul>
<li><a href="https://desktop.github.com/">GitHub Desktop 다운로드</a></li>
<li><a href="https://docs.github.com">GitHub 공식 문서</a></li>
<li><a href="https://skills.github.com">GitHub Skills (무료 학습)</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[생성형 AI란?(LLM,GAN,VAE)]]></title>
            <link>https://velog.io/@yoon_min/%EC%83%9D%EC%84%B1%ED%98%95-AI%EB%9E%80LLMGANVAE</link>
            <guid>https://velog.io/@yoon_min/%EC%83%9D%EC%84%B1%ED%98%95-AI%EB%9E%80LLMGANVAE</guid>
            <pubDate>Thu, 03 Jul 2025 10:01:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>생성형 AI(Generative AI)</strong>는 기존 데이터를 학습해 새로운 데이터를 생성하는 인공지능 기술입니다. 
이 기술은 <strong>텍스트, 이미지, 음성, 영상 등</strong> 다양한 형태의 콘텐츠를 창의적으로 만들어낼 수 있습니다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/a6777d08-1322-4271-b714-52beca0d7443/image.jpeg" alt=""></p>
<h2 id="📍ai-발전-과정">📍<a href="https://news.skhynix.co.kr/all-around-ai-1/">AI 발전 과정</a></h2>
<p>SK 하이닉스 블로그(AI의 시작과 발전 과정, 미래 전망)
<img src="https://velog.velcdn.com/images/yoon_min/post/811008eb-8841-4ffa-84a9-ed51dca0805b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/56c0c345-31c0-460e-a4b5-ccb3ffd2ce24/image.png" alt=""></p>
<h2 id="📍-llm-large-language-model--대규모-언어-모델">📍 LLM (Large Language Model) — 대규모 언어 모델</h2>
<blockquote>
<p>LLM은 <strong>대량의 텍스트 데이터를 학습하여 자연어(사람들이 일상적으로 사용하는 언어)를 이해하고 생성하는 딥러닝 모델</strong>입니다.</p>
</blockquote>
<ul>
<li>대표 모델: GPT 시리즈, PaLM, Claude, LLaMA, Mistral 등</li>
<li>기반 구조: Transformer (Vaswani et al., 2017)</li>
</ul>
<h4 id="🔧--작동-원리">🔧  작동 원리</h4>
<ol>
<li>입력 텍스트를 받아들임</li>
<li>다음에 올 단어의 확률을 예측</li>
<li>문장/문단/코드/지식 등의 텍스트를 생성<blockquote>
<p>“오늘 날씨가” → LLM은 그 다음 단어로 “좋다”를 높은 확률로 생성할 수 있습니다.</p>
</blockquote>
</li>
</ol>
<h4 id="🌟-특징">🌟 특징</h4>
<ul>
<li>자연어 처리의 범용 엔진 (요약, 번역, 질의응답 등)</li>
<li>확률적 텍스트 생성기 (문장의 일관성과 문법 유지)</li>
<li>수십억~수조 개의 파라미터 보유 (GPT-3: 175B, GPT-4: 더 큼)</li>
</ul>
<h2 id="📍-gangenerative-adversarial-network--적대적-생성-신경망">📍 GAN(Generative Adversarial Network) — 적대적 생성 신경망</h2>
<blockquote>
<p>GAN은 <strong>두 개의 신경망(생성자 G, 판별자 D)이 서로 경쟁하며 실제 같은 데이터를 생성하는 모델</strong>입니다.</p>
</blockquote>
<p>•    처음 제안: Ian Goodfellow (2014)
•    대표 적용: 이미지 생성 (얼굴, 예술, 스타일 전환 등)</p>
<h4 id="🔧-작동-원리">🔧 작동 원리</h4>
<ol>
<li>생성자(Generator): 노이즈를 받아 가짜 데이터를 생성</li>
<li>판별자(Discriminator): 진짜인지 가짜인지 구분</li>
<li>두 모델이 경쟁하면서 점점 더 정교한 데이터를 생성</li>
</ol>
<blockquote>
<p>🎯 목표: 생성자는 판별자를 속이고, 판별자는 생성자를 간파하려고 함 → 적대적 학습</p>
</blockquote>
<h4 id="🌟-특징-1">🌟 특징</h4>
<p>•    현실적인 이미지/영상/오디오 생성에 강함
•    고해상도 이미지, Deepfake, 스타일 전환 등에 사용
•    훈련이 어려움 (불안정한 수렴, 모드 붕괴 등 이슈 존재)</p>
<h2 id="📍-vae">📍 VAE</h2>
<blockquote>
<p>Variational Autoencoder(VAE, 변분 오토인코더)는 단순한 함수 변화 추적보다는 <strong>확률 분포를 인코딩하고, 그 분포에서 샘플링해 재구성하는 확률 기반의 생성 모델</strong>입니다.</p>
</blockquote>
<h3 id="️핵심-개념">‼️핵심 개념</h3>
<p>“잠재 공간(latent space)(ex. 벡터 z)“의 분포를 배우는 인코더</p>
<ul>
<li>일반 오토인코더는 
입력 → 벡터 z → 복원을 진행</li>
<li>VAE는 
입력 → 정해진 분포(보통 정규분포)에 대한 매개변수(μ, σ)를 구해 → 이 분포에서 샘플링하여 z를 얻고 → 복원합니다.</li>
</ul>
<blockquote>
<p><strong>즉,단일 지점을 인코딩하는 것이 아니라,
“입력 데이터를 잘 설명하는 분포”를 추정해서 그 분포를 인코딩하는 것입니다.</strong></p>
</blockquote>
<p>이 과정이 “<a href="https://velog.io/@yoon_min/Variational-Inference-%EA%B0%9C%EB%85%90">변분 추정(Variational Inference)</a>“입니다. 그래서 이름이 Variational Autoencoder입니다.</p>
<h2 id="📍멀티-모달-ai">📍멀티 모달 AI</h2>
<h3 id="mmlmmulytimodal-large-language-model">MMLM(Mulytimodal Large Language Model)</h3>
<blockquote>
<p>멀티모달 AI는 텍스트, 이미지, 음성, 영상 등 <strong>다양한 형태의 데이터를 동시에 이해하고 생성할 수 있는 인공지능 모델</strong>입니다.</p>
</blockquote>
<p>크게 대표되는 예시로 제가 가장 많이 사용하는 멀티모달 AI로는 <strong>Chat GPT4o, Gemini, Claude</strong>가 있습니다. </p>
<p><del>전 이미 인공지능 없이는 살아가기 힘든 몸이 되었습니다...🥲</del></p>
<pre><code>•    “모달(modal)” = 데이터의 형태(양식)(예: 텍스트, 이미지, 소리, 센서 데이터 등)
•    “멀티모달” = 다양한 모달리티를 함께 처리</code></pre><h4 id="🔧--작동-원리-기본-구조">🔧  작동 원리 (기본 구조)</h4>
<p>멀티모달 모델은 여러 입력 양식을 하나의 공통된 표현(latent space)으로 인코딩하고, 그걸 바탕으로 다양한 출력 양식을 디코딩합니다.</p>
<p>예: 텍스트+이미지를 처리하는 구조</p>
<pre><code>[이미지] → Vision Encoder ─┐
                           │ → 공동 임베딩 → 디코더 → [텍스트 생성]
[텍스트] → Text Encoder ──┘</code></pre><blockquote>
<p>최신 모델들은 이 과정을 Transformer 기반의 통합 모델로 수행합니다.
특히 LLM 기반으로 멀티모달을 통합하는 게 주류입니다 (예: GPT-4o, Gemini, Claude).</p>
</blockquote>
<h4 id="🌟-멀티모달의-특징">🌟 멀티모달의 특징</h4>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/23b2cb58-b956-44e4-b7ca-ba7b1085439e/image.png" alt=""></p>
<hr>
<p>[참고 문헌]</p>
<ul>
<li><a href="https://news.skhynix.co.kr/all-around-ai-1/">https://news.skhynix.co.kr/all-around-ai-1/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Variational Inference 개념]]></title>
            <link>https://velog.io/@yoon_min/Variational-Inference-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@yoon_min/Variational-Inference-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Thu, 03 Jul 2025 09:45:26 GMT</pubDate>
            <description><![CDATA[<h2 id="📍-변분-추정variational-inference">📍 변분 추정(Variational Inference)</h2>
<blockquote>
<p>생성형 모델에서는 어떤 관측 데이터 x가 있다고 할 때,
그걸 만든 잠재 변수 z를 찾고 싶습니다.</p>
</blockquote>
<blockquote>
<p>우리가 하고 싶은 일
 Posterior(사후확률)인  p(z | x) 를 구하는 것, 하지만 대부분의 경우 이 확률은 계산이 불가능하거나 너무 복잡합니다.</p>
</blockquote>
<blockquote>
<p>p(z | x)를 직접 계산할 수 없으니,
대신 계산 가능한 <strong>근사 분포 q(z|x)</strong> 를 만들고 이걸 학습하자!</p>
</blockquote>
<blockquote>
<p>즉,q(z | x) ≈ p(z | x) 가 되도록
q를 최적화(함수의 형태를 조정) 하는 방법이 <strong>변분 추론(variational inference) 입니다.</strong></p>
</blockquote>
<p>우리는 <strong>q(z|x)라는 근사 분포를 설계</strong>하고,
그게 진짜 분포 <strong>p(z|x)와 가깝도록 학습</strong>시키고 싶습니다.</p>
<h3 id="1️⃣-사전확률">1️⃣ 사전확률</h3>
<p>사전 확률(pre-priori probability)은 특정 이벤트가 일어나기 전에, <strong>그 이벤트가 일어날 확률</strong>을 말합니다. 이러한 확률은 과거의 데이터, 경험, 또는 전문가의 지식을 바탕으로 추정됩니다.( ex. 전체 메일중 20%가 스팸라면, 사전확률은 0.2이다)</p>
<h3 id="2️⃣-사후-확률">2️⃣ 사후 확률</h3>
<p>사후 확률(posterior probability)은 새로운 정보나 증거가 주어진 후에, <strong>특정 이벤트가 일어날 확률을 업데이트한 것</strong>을 말합니다. 이 확률은 사전 확률에 새로운 증거를 통합하여 계산됩니다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/0d9d74e7-6c5c-40a6-8af9-e79cfed8b466/image.jpeg" alt=""></p>
<ul>
<li>P(A∣B)
P(A∣B)는 B가 주어졌을 때 A가 일어날 조건부 확률, 즉 사후 확률입니다. </li>
<li>P(B∣A)
P(B∣A)는 A가 주어졌을 때 B가 일어날 조건부 확률, </li>
<li>(A)
P(A)는 A의 사전 확률, 그리고 </li>
<li>P(B)
P(B)는 B의 확률입니다.</li>
</ul>
<hr>
<p>[참고문헌]</p>
<ul>
<li><a href="https://www.mindscale.kr/docs/probability/prior-and-posterior-probability">https://www.mindscale.kr/docs/probability/prior-and-posterior-probability</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI(Artificial Intelligence)란?]]></title>
            <link>https://velog.io/@yoon_min/%EC%83%9D%EC%84%B1%ED%98%95-AI%EB%9E%80</link>
            <guid>https://velog.io/@yoon_min/%EC%83%9D%EC%84%B1%ED%98%95-AI%EB%9E%80</guid>
            <pubDate>Thu, 03 Jul 2025 03:39:57 GMT</pubDate>
            <description><![CDATA[<h1 id="📋-ai-기본-원리-잡기-📋">📋 AI 기본 원리 잡기 📋</h1>
<blockquote>
<p>AI에 관한 개념 잡기 및 공부에 도움이되었으면 좋겠군요,,🥹</p>
</blockquote>
<h3 id="1️⃣-aiartificial-intelligence">1️⃣ AI(Artificial Intelligence)</h3>
<blockquote>
<p>Artificla(인공의) + Intelligence(지능)
사람의 지능을 모방하여 컴퓨터가 자동화를 대신해주는 기술</p>
</blockquote>
<h3 id="2️⃣-머신러닝machine-learning">2️⃣ 머신러닝(Machine Learning)</h3>
<blockquote>
<p>규칙을 따로 알려주는것이 아닌 컴퓨터가 데이터를 보고 스스로 패턴을 학습하는 기술</p>
</blockquote>
<h3 id="3️⃣-딥러닝depp-learning">3️⃣ 딥러닝(Depp Learning)</h3>
<blockquote>
<p>인공신경망 기반의 모델로, 비정형 데이터와 같은 복잡한 데이터에 대해 스스로 특성 추출 및 의사 결정까지 컴퓨터가 한번에 수행하는 기술</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/c855cc8b-f75a-4d59-92c4-4adb2026a97b/image.jpeg" alt=""></p>
<h3 id="🧐인공-신경망-비정형-데이터-무슨-말이지">🧐인공 신경망? 비정형 데이터? 무슨 말이지?</h3>
<h4 id="📍인공신경망ann">📍인공신경망(ANN)</h4>
<blockquote>
<p>Aificial Neural Network =&gt; ANN</p>
</blockquote>
<p>인간 뇌의 신경구조에서 영감을 받아 설치된 기계학습 모델입니다.
뇌의 뉴런처럼 동작하는 <strong>노드(뉴런)</strong> 들이 계층적으로 연결되어 정보를 전달하고, 입력 데이터를 처리하여 학습과 예측을 수행합니다.
인공신경망은 크게 <strong>입력층, 은닉층, 출력층</strong>으로 구성되며
각 노드는 입력 값에 가중치를 곱해 더한 뒤, 활성화 함수를 통해 출력을 결정합니다.
<img src="https://velog.velcdn.com/images/yoon_min/post/b7dcfdb8-f5e6-47c9-94ea-5da8cead2f4b/image.jpeg" alt="뉴런 인공신경망 비교"></p>
<h4 id="📍비정형-데이터">📍비정형 데이터</h4>
<blockquote>
<p>Unstructured Data</p>
</blockquote>
<p>미리 정의된 구조나 <strong><a href="https://jwprogramming.tistory.com/47">스키마</a></strong>가 없는 데이터를 의미합니다.
즉, 데이터베이스의 표 형태(행과 열)에 맞춰 저장되지 않고 <strong>자유로운 형식으로 존재하는 데이터</strong>입니다. 
대표적인 예로는 텍스트(문서, 이메일, 채팅), 이미지, 오디오, 비디오, 센서 데이터 등이 있습니다.</p>
<h2 id="🤖-strong-ai--weak-ai">🤖 <a href="https://www.ibm.com/kr-ko/think/topics/strong-ai">Strong AI / Weak AI</a></h2>
<h3 id="📍strong-ai">📍Strong AI</h3>
<ul>
<li>다양한 기능을 수행 </li>
<li>새로운 문제를 해결하는 방법을 스스로에게 가르침<h3 id="📍weak-ai">📍Weak AI</h3>
</li>
<li>한가지 유형의 작업을 수행</li>
<li>사용자가 입력한 데이터를 기반으로 같은 특정 작업을 수행하는데 집중</li>
</ul>
<h2 id="📋-ai의-학습">📋 AI의 학습</h2>
<p>AI의 학습 단계는 크게 *<em>지도학습 → 비지도 학습 → 강화 학습 *</em>순으로 이루어집니다.</p>
<ul>
<li>📍<a href="https://www.youtube.com/watch?v=hnc1DGz9UCU">지도학습</a> : 해당 DATA(정보)에 대한 정답을 미리 알려준 후(지도) 해당 내용을 학습하게함, 이후 해당 학습에 대한 결과를 물어봄</li>
<li>📍<a href="https://www.youtube.com/watch?v=xwe1cbZaFEg&amp;list=PLuHgQVnccGMDy5oF7G5WYxLF3NCYhB9H9&amp;index=21">비지도 학습</a> : 정답을 알려주지 않은 상태로 DATA(정보)만 전달해 특성을 파악하게함</li>
<li>📍<a href="https://www.youtube.com/watch?v=BPCAP7DHLYw&amp;list=PLuHgQVnccGMDy5oF7G5WYxLF3NCYhB9H9&amp;index=22">강화 학습</a> : 시행착오를 경험하며 경험 기반으로 학습하게함
<img src="https://velog.velcdn.com/images/yoon_min/post/aa66e4e7-5a2b-4a26-8794-d320a7a1455d/image.png" alt=""></li>
</ul>
<hr>
<p>참고 문헌</p>
<ul>
<li><a href="https://jwprogramming.tistory.com/47">https://jwprogramming.tistory.com/47</a></li>
<li><a href="https://www.ibm.com/kr-ko/think/topics/strong-ai">https://www.ibm.com/kr-ko/think/topics/strong-ai</a></li>
<li><a href="https://www.youtube.com/watch?v=LPqmPfhnR1o&amp;list=PLuHgQVnccGMDy5oF7G5WYxLF3NCYhB9H9">https://www.youtube.com/watch?v=LPqmPfhnR1o&amp;list=PLuHgQVnccGMDy5oF7G5WYxLF3NCYhB9H9</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP API와 JSON을 이해합시도🧐]]></title>
            <link>https://velog.io/@yoon_min/HTTP-API%EC%99%80-JSON%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%A9%EC%8B%9C%EB%8F%84</link>
            <guid>https://velog.io/@yoon_min/HTTP-API%EC%99%80-JSON%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%A9%EC%8B%9C%EB%8F%84</guid>
            <pubDate>Fri, 13 Dec 2024 15:37:40 GMT</pubDate>
            <description><![CDATA[<p>[사진 출처 | <a href="https://blog.treblle.com/60-api-terms-every-developer-must-know/%5D">https://blog.treblle.com/60-api-terms-every-developer-must-know/]</a></p>
<h1 id="✅-http-api">✅ HTTP API</h1>
<blockquote>
<ul>
<li>애플리케이션 간 데이터를 주고 받기 위한 인터페이스</li>
</ul>
</blockquote>
<ul>
<li>HTTP 프로토콜을 사용하여 데이터를 요청하고 응답함</li>
<li>REST API가 HTTP API의 대표적 형태로, JSON 형식을 주로 사용함</li>
</ul>
<h2 id="✔️-http-api-요청-구조">✔️ HTTP API 요청 구조</h2>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/e89b92bf-0c51-49d6-b21b-bb5c07886025/image.jpeg" alt=""></p>
<ul>
<li><strong>요청 메서드</strong>: 각각의 메서드(하단)는 특정 작업을 수행함<blockquote>
<ul>
<li>GET | POST | PUT | DELETE 등</li>
</ul>
</blockquote>
</li>
<li><strong>URL</strong>: 요청 보낼 리소스의 주소</li>
<li><strong>헤더(Header)</strong>: 요청과 응답에 대한 추가 정보 포함( ex. 인증토큰, 콘텐츠 타입 )</li>
<li><strong>본문(Body)</strong>: 필요한 경우 데이터를 전달하며 주로 JSON 형식을 사용한다.</li>
</ul>
<h1 id="✅-json">✅ JSON</h1>
<h3 id="javascript-object-notation---자바스크립트-객체-표기법">JAVASCRIPT Object Notation  | 자바스크립트 객체 표기법</h3>
<blockquote>
<ul>
<li>데이터를 주고받기 위해 경량화된 데이터 형식</li>
</ul>
</blockquote>
<ul>
<li>Key(키)-value(값) 쌍 구조로 데이터를 표현하며 사람이 일기 쉽고 기계가 처리하기 쉬움</li>
</ul>
<h2 id="✔️-json-구조">✔️ JSON 구조</h2>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/8fb51900-a5c3-4ecc-90ff-850e54b3c0b7/image.jpeg" alt=""></p>
<ul>
<li><strong>데이터 타입</strong>: 문자열, 숫자, 불리언, 객체, 배열, null 사용가능.</li>
<li><strong>배열(Array)</strong>: 대괄호[] 로 묶으며, 여러 데이터를 리스트 형식으로 저장.</li>
<li><strong>객체(Object)</strong>: 중괄호{}로 묶이며, 키-값 쌍으로 데이터를 저장.</li>
</ul>
<h1 id="✅-http-요청-메서드와-json-사용-예시">✅ HTTP 요청 메서드와 JSON 사용 예시</h1>
<pre><code class="language-python">url = &#39;https://api.example.com/users&#39;
data={&quot;name&quot;:&quot;yoonmin&quot;,&quot;age&quot;:500}
response = requests.post(url, json=data) #POST요청 예시
print(response.json()) #JSON 응답</code></pre>
<h3 id="✔️-get">✔️ GET</h3>
<ul>
<li>기능 :  서버에서 데이터 조회</li>
<li>본문(body) 없이 요청<h3 id="✔️-post">✔️ POST</h3>
</li>
<li>기능 :  서버에서 데이터 생성 요청</li>
<li>본문(body)에 JSON 데이터 포함하여 전송<h3 id="✔️-put">✔️ PUT</h3>
</li>
<li>기능 :  기존 데이터 업데이트</li>
<li>본문(body)에 JSON 데이터 포함하여 전송<h3 id="✔️-delete">✔️ DELETE</h3>
</li>
<li>기능 :  특정 데이터 삭제 요청</li>
<li>본문(body) 없이 요청 가능</li>
</ul>
<h2 id="✅-장점">✅ 장점</h2>
<ul>
<li>경량성
JSN은 가볍고 데이터 표현이 간단해 네트워크 전송에 효율적입니다</li>
<li>가독성
사람이 일고 쓰기 쉬우며, JSON 포맷을 그대로 데이터베이스와 연결 가능합니다.</li>
<li>언어독립성
대부분의 언어에서 쉽게 파싱이 가능해서 서로 다른 시스템 간의 데이터 교환에 유리합니다.</li>
</ul>
<hr>
<p>오늘은 간단하게 HTTP JSON에 관해 알아보았습니다😉</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Axios를 만나다✨]]></title>
            <link>https://velog.io/@yoon_min/Axios%EB%A5%BC-%EB%A7%8C%EB%82%98%EB%8B%A4</link>
            <guid>https://velog.io/@yoon_min/Axios%EB%A5%BC-%EB%A7%8C%EB%82%98%EB%8B%A4</guid>
            <pubDate>Fri, 13 Dec 2024 13:22:40 GMT</pubDate>
            <description><![CDATA[<h1 id="✅-axios란">✅ Axios란?</h1>
<blockquote>
<ul>
<li><strong>Axios는</strong> <strong>Node.js, 브라우저를 위한 <a href="https://velog.io/@yoon_min/Promise-%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EA%B0%9C%EB%85%90">Promise</a> 기반 <a href="https://docs.tosspayments.com/resources/glossary/http-protocol">HTTP 클라이언트</a></strong> 입니다.</li>
</ul>
</blockquote>
<ul>
<li>쉽게 말해서 <strong>백엔드랑 프론트엔드랑 통신</strong>을 <strong>쉽게하기 위해 Ajax</strong>와 더불어 사용합니다.</li>
<li>이미 자바스크립트에는 <strong>fetch api</strong>가 있지만, <strong>프레임워크에서 ajax를 구현할땐 axios</strong>를 쓰게됩니다.</li>
</ul>
<h1 id="✅-axios-특징">✅ Axios 특징</h1>
<ul>
<li>운영 환경에 따라 브라우저의 XMLHttpRequest 객체 생성 </li>
<li>Node.js를 위해 http 요청 생성</li>
<li>Promise(ES6) API 지원(async,await)</li>
<li>요청과 응답 데이터의 변형, 인터셉트</li>
<li>HTTP 요청 취소</li>
<li>HTTP 요청과 응답을 JSON 형태로 자동 변경</li>
<li>XSRF를 막기위한 클라이언트 사이드 지원</li>
</ul>
<h2 id="✔️axios-설치">✔️Axios 설치</h2>
<p>✨ <a href="https://www.npmjs.com/package/axios">npm axios 설치</a></p>
<pre><code class="language-javascript">npm install axios</code></pre>
<p>✨ 설치 확인 후 하단 코드를 사용하여 Axios를 불러 올 수 있습니다.</p>
<pre><code class="language-javascript">import axios from &#39;axios&#39;;</code></pre>
<h2 id="✔️-클라이언트html에서-axios-설치">✔️ 클라이언트(html)에서 axios 설치</h2>
<p>✨ jsDeliver / unpkg CDN 둘중 하나를 선택해 넣으면된다.</p>
<pre><code>&lt;!--jsDeliver--&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js&quot;&gt;&lt;/script&gt;

&lt;!--unpkg CDN--&gt;
&lt;script src=&quot;https://unpkg.com/axios/dist/axios.min.js&quot;&gt;&lt;/script&gt;</code></pre><h1 id="✅-axios-문법-구성">✅ Axios 문법 구성</h1>
<pre><code class="language-javascript">axios({
  url: &#39;https://test/api/cafe/list/today&#39;, // 통신할 웹문서
  method: &#39;get&#39;, // 통신할 방식
  data: { // 인자로 보낼 데이터
    foo: &#39;diary&#39;
  }
});</code></pre>
<h1 id="axios-사용-예제">Axios 사용 예제</h1>
<h3 id="✔️promise-then-사용">✔️Promise .then() 사용</h3>
<pre><code class="language-javascript">import axios from &#39;axios&#39;;

export const exampleAxios = () =&gt; {
  return axios.get(&#39;https://test.com/api/sample&#39;)
       //첫 번째 API 호출의 결과에서 userId를 추출.
    .then((response1) =&gt; {
      const data = response1.data;
      const userId = data.userId;

      return axios.get(`https://test2.com/api/v2/${userId}`);
    })
    //두 번째 API 호출의 결과를 처리하고 반환.
    .then((response2) =&gt; {
      console.log(&#39;response:&#39;, response2.data);
      return response2.data;
    })
    //error반환
    .catch((error) =&gt; {
      console.error(&#39;대시보드 통계 데이터 가져오기 실패:&#39;, error);
      return { user: [] };
    });
};
}</code></pre>
<h3 id="✔️-asyncawait-사용">✔️ async/await 사용</h3>
<pre><code class="language-javascript">import axios from &#39;axios&#39;;

export const exampleAxios = async () =&gt; {
  try {
    const response = await axios.get(&#39;https://test.com/api/sample&#39;);
    console.log(&#39;response:&#39;, response.data);
    return response.data;
  } catch (error) {
    console.error(&#39;대시보드 통계 데이터 가져오기 실패:&#39;, error);
    return { user: [] }; 
  }
};</code></pre>
<p>상하단 코드 모두 동일하게 동작하며, 비동기 처리로 진행하게됩니다.</p>
<h1 id="✅-axios-단축-메소드">✅ Axios 단축 메소드</h1>
<p>axios를 편리하게 사용하기 위해 모든 요청 메소드는 aliases가 제공됩니다.
위 처럼 객체 옵션을 이것저것 주면 가독성이 떨어지니, 함수형으로 재구성하여 나눠논 것으로 이해하면 됩니다.
axios의 Request method에는 대표적으로 다음과 같은 것들이 있습니다.</p>
<blockquote>
</blockquote>
<ul>
<li>GET : axios.get(url[, config])</li>
<li>POST : axios.post(url, data[, config])</li>
<li>PUT : axios.put(url, data[, config])</li>
<li>DELETE : axios.delete(url[, config])</li>
</ul>
<pre><code class="language-javascript">axios#request(config)
axios#get(url[, config])
axios#delete(url[, config])
axios#head(url[, config])
axios#options(url[, config])
axios#post(url[, data[, config]])
axios#put(url[, data[, config]])
axios#patch(url[, data[, config]])
axios#getUri([config])</code></pre>
<h1 id="✅axios-요청-예제">✅Axios 요청 예제</h1>
<h2 id="✔️-get">✔️ Get</h2>
<p>Get 메서드에는 2가지 상황은 크게 존재합니다.</p>
<blockquote>
<ol>
<li>단순 데이터(페이지 요청, 지정된 요청) 요청을 수행할 경우</li>
<li>파라미터 데이터를 포함시키는 경우 (사용자 번호에 따른 조회)</li>
</ol>
</blockquote>
<pre><code class="language-javascript">const axios = require(&#39;axios&#39;); // node.js쓸때 모듈 불러오기


// user에게 할당된 id 값과 함께 요청을 합니다.
axios.get(&#39;/user?ID=12345&#39;)
  .then(function (response) {
    console.log(response); // 성공했을 때 반환
  })
  .catch(function (error) {
    console.log(error); // 에러가 났을 때 반환
  })
  .finally(function () {
    // 항상 실행되는 함수
  });


// 위와는 같지만, 옵션을 주고자 할 때는 이렇게 요청을 합니다.
axios.get(&#39;/user&#39;, {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  })
  .finally(function () 
  });  </code></pre>
<h3 id="asyncawait-사용">async/await 사용</h3>
<pre><code class="language-javascript">// async/await 를 쓰고 싶다면 async 함수/메소드를 만듭니다. 
async function getUser() {
  try {
    const response = await axios.get(&#39;/user?ID=12345&#39;);
    console.log(response);
  } catch (error) {
    console.error(error);
  }
}</code></pre>
<h2 id="✔️-post">✔️ Post</h2>
<blockquote>
<p>post 메서드에는 일반적으로 데이터를 Message Body에 포함시켜 보냅니다.
위에서 봤던 get 메서드에서 params를 사용한 경우와 비슷하게 수행됩니다.</p>
</blockquote>
<pre><code class="language-javascript">axios.post(&quot;url&quot;, {
        firstName: &#39;Fred&#39;,
        lastName: &#39;Flintstone&#39;
    })
    .then(function (response) {
        // response  
    }).catch(function (error) {
        // 오류발생시 실행
    })</code></pre>
<h2 id="✔️-delete">✔️ Delete</h2>
<blockquote>
<p>delete 메서드에는 일반적으로 body가 비어있다.
REST 기반 API 프로그램에서 데이터베이스에 저장되어 있는 내용을 삭제하는 목적으로 사용합니다.</p>
</blockquote>
<pre><code class="language-javascript">axios.delete(&#39;/user?ID=12345&#39;)
  .then(function (response) {
    // handle success
    console.log(response);
  })
  .catch(function (error) {
    // handle error
    console.log(error);
  })</code></pre>
<p>하지만 query나 params가 많아져서 헤더에 많은 정보를 담을 수 없을 때는 
다음과 같이 두 번째 인자에 data를 추가해줄 수 있습니다.</p>
<pre><code class="language-javascript">axios.delete(&#39;/user?ID=12345&#39;,{
    data: {
      post_id: 1,
      comment_id: 13,
      username: &quot;foo&quot;
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  })</code></pre>
<h2 id="✔️-put">✔️ Put</h2>
<blockquote>
<p>REST 기반 API 프로그램에서 데이터베이스에 저장되어 있는 내용을 갱신(수정)하는 목적으로 사용됩니다.
put 메서드는 서버 내부적으로 get -&gt; post 과정을 거치기 때문에 post 메서드와 비슷한 형태입니다.</p>
</blockquote>
<pre><code class="language-javascript">axios.put(&quot;url&quot;, {
        username: &quot;&quot;,
        password: &quot;&quot;
    })
    .then(function (response) {
         // 성공시 결과 값
    }).catch(function (error) {
        // error발생시 실행
    })</code></pre>
<h3 id="출처">출처</h3>
<p><a href="https://inpa.tistory.com/entry/AXIOS-%F0%9F%93%9A-%EC%84%A4%EC%B9%98-%EC%82%AC%EC%9A%A9">Tstory blog | Inpa_dev</a>
<a href="https://axios-http.com/kr/docs/instance">Axios http blog</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🤯 안끝난 비동기 Async / Await 개념(with JS)]]></title>
            <link>https://velog.io/@yoon_min/%EC%95%88%EB%81%9D%EB%82%9C-%EC%9D%B4%EC%95%BC%EA%B8%B0-Async-Await-%EA%B0%9C%EB%85%90with-JS</link>
            <guid>https://velog.io/@yoon_min/%EC%95%88%EB%81%9D%EB%82%9C-%EC%9D%B4%EC%95%BC%EA%B8%B0-Async-Await-%EA%B0%9C%EB%85%90with-JS</guid>
            <pubDate>Fri, 13 Dec 2024 12:26:55 GMT</pubDate>
            <description><![CDATA[<p><em>제목이 이런 이유는 앞에 적은 Promise,동기,비동기 내용이 너무 길어져 
따로 async/await를 설명하고자,,,(이하 생략)</em></p>
<h1 id="✅-async--await--누구">✅ Async / Await ,,, 누구,,,?</h1>
<blockquote>
<p><strong>async와 await</strong>는 자바스크립트의 <strong>비동기 처리 패턴</strong> 중 가장 최근에 나온 문법입니다. 
async와 await은 기존의 비동기 처리 방식인 callback 함수, Promise의 단점을 보완하고 개발자가 읽기 좋은 코드를 작성할 수 있게 도와줍니다.</p>
</blockquote>
<blockquote>
<p>➕ <strong>Syntatic Sugar(문법적 설탕)</strong>라고도 불립니다.
Syntatic Sugar란 코드를 더 알기 쉽게 작성하고 이해할 수 있도록 도와주는 문법적인 표현방식입니다.</p>
</blockquote>
<h2 id="✅-async">✅ async</h2>
<blockquote>
<p><strong>async</strong>는 <strong>function 앞</strong>에 <strong>붙습니다</strong>.</p>
</blockquote>
<pre><code class="language-javascript">async function f() {
  return 1;
}</code></pre>
<p>function 앞에 async를 붙이면 <strong>해당 함수는 항상 프라미스를 반환</strong>합니다. 
프라미스가 아닌 값을 반환하더라도 resolved promise로 값을 감싸 resolved promise가 반환되도록 합니다.</p>
<blockquote>
<p>아래 예시의 함수를 호출하면 result가 1인 resolve promise가 반환됩니다.</p>
</blockquote>
<pre><code class="language-javascript">async function f() {
  return 1;
}
f().then(alert); // 1</code></pre>
<p>명시적으로 프라미스를 반환하는 것도 가능한데, 결과는 동일합니다.</p>
<pre><code class="language-javascript">async function f() {
  return Promise.resolve(1);
}
f().then(alert); // 1</code></pre>
<p><strong>async가 붙은 함수는 반드시 프라미스를 반환</strong>하고, <strong>프라미스가 아닌 것은 프라미스로 감싸 반환</strong>합니다.</p>
<p>또 다른 키워드인 <strong>await</strong>는 <strong>async 함수 안에서만 동작</strong>합니다.</p>
<h2 id="✅-await">✅ await</h2>
<blockquote>
<h4 id="자바스크립트는-await-키워드를-만나면-promise가-처리될-때까지-기다리고-결과는-그-이후-반환됩니다">자바스크립트는 await 키워드를 만나면 Promise가 처리될 때까지 기다리고 결과는 그 이후 반환됩니다.</h4>
<p><em>(await는 &#39;기다리다’라는 뜻을 가진 영단어입니다).</em></p>
</blockquote>
<pre><code class="language-javascript">// await는 async 함수 안에서만 동작합니다.
let value = await promise;</code></pre>
<p>Promise를 반환하는 함수 앞에 await를 붙이면, 해당 Promise의 상태가 바뀔 때까지 코드가 기다려요.
Promise가 성공 상태 또는 실패 상태로 바뀌기 전까지는 다음 연산을 시작하지 않는 것이죠.</p>
<h3 id="✔️-예시toss-payments">✔️ 예시(Toss payments)</h3>
<p>예시를 보면 더 쉬운데요. </p>
<blockquote>
<p>아래와 같이 결제를 요청하는 <strong>requestPayment() 메서드 앞</strong>에 <strong>await</strong>가 있습니다. 
결제 요청이 잘 완료되어 <strong>paymentData(Promise 객체)의 상태가 바뀐 이후</strong>에만 <strong>console.log(paymentData)가 실행</strong>됩니다.  즉, 비동기 작업을 동기적으로 바꾸는 것이죠.</p>
</blockquote>
<pre><code class="language-javascript">async function handleSubmit() {
  const paymentData = await paymentWidget.requestPayment({
    orderId: &quot;KOISABLdLiIzeM-VGU_8Z&quot;, // 주문번호(직접 만들어주세요)
    orderName: &quot;토스 티셔츠 외 2건&quot; // 주문명(직접 만들어주세요)
  });
  console.log(paymentData);
  return paymentData //Promise 객체의 상태가 바뀌어야 conole.log 정상 출력
}</code></pre>
<p>사실상 await는 then()과 같은 역할을 하는데, 콜백 함수를 등록할 필요가 없기 때문에 더 편리합니다. 
체이닝으로 인해 코드가 복잡해질 일도 없죠.</p>
<h3 id="✔️-에러-처리법">✔️ 에러 처리법</h3>
<p>근데 에러는 어떻게 처리할까요? </p>
<blockquote>
<p><strong>async/await</strong>로 <strong>코드를 동기적</strong>으로 바꾸는 것으로 생각할 수 있다고 했습니다.
그럼 이제 간단히 <strong>try/catch</strong>를 사용하면 됩니다. 
아래 코드에서는 requestPayment()에서 <strong>실패 상태</strong>의 <strong>Promise를 반환</strong>하면, 바로 <strong>catch</strong> 블록이 <strong>실행</strong>됩니다</p>
</blockquote>
<pre><code class="language-javascript">async function handleSubmit() {
    try {
        const paymentData = await paymentWidget.requestPayment({
        orderId: &quot;KOISABLdLiIzeM-VGU_8Z&quot;, // 주문 ID(직접 만들어주세요)
        orderName: &quot;토스 티셔츠 외 2건&quot; // 주문명
        });
        console.log(paymentData);
        // 성공 처리
        return paymentData;
    } catch (error) {
        // 에러 처리
        console.log(error.message);
    }
}</code></pre>
<p>이상 짧게 async await에 대해 알아보았습니다.
 토스 개발팀에서 운영하는 블로그에서 해당 내용을 직접 API연결후 사용하는 방법도 잘 설명 해뒀으니
 필요하신분들은 참조해주시면 좋을거같네요!</p>
<h3 id="출처">출처</h3>
<p><a href="https://docs.tosspayments.com/blog/async-await-example#%EA%B7%B8%EB%9E%98%EC%84%9C-ayncawait%EB%9E%80">토스 페이먼츠 블로그</a>
<a href="https://ko.javascript.info/async-await">JAVASCRIPT Info blog</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Promise 🤝🏻 | 동기, 비동기 개념]]></title>
            <link>https://velog.io/@yoon_min/Promise-%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@yoon_min/Promise-%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Thu, 12 Dec 2024 15:39:52 GMT</pubDate>
            <description><![CDATA[<h1 id="🤝🏻-promise란">🤝🏻 Promise란?</h1>
<blockquote>
<p>&#39;<strong>약속</strong>&#39;입니다. 미래에 일어날 일을 약속하는 것으로 
<strong>비동기 작업을 처리할 때 사용되는 객체입니다.</strong></p>
</blockquote>
<h2 id="✅-비동기-동기-어디서-많이-들어봤는데">✅ 비동기? 동기? 어디서 많이 들어봤는데?</h2>
<p>여럿 개발 과정에서 동기, 비동기라는 말을 많이 들어 보시지 않았나요?</p>
<blockquote>
<h3 id="✔️-동기">✔️ 동기</h3>
<p>동기는 <strong>직렬적</strong>으로 작동하는 방식입니다.</p>
</blockquote>
<h3 id="✔️-비동기">✔️ 비동기</h3>
<blockquote>
<p>비동기는 <strong>병렬적</strong>으로 작동하는 방식입니다.</p>
</blockquote>
<p>여기서 보이는 <strong>동기와 비동기의 차이점</strong>이 있습니다.
비동기는 특정 코드가 끝날때 까지 <strong>코드의 실행을 멈추지 않고</strong> 다음 코드를 먼저 실행하는 것이죠. </p>
<p>한번 사진을 통해 비교해볼까요?
<img src="https://velog.velcdn.com/images/yoon_min/post/bb05067d-b078-4783-b6c5-f2af81b1393c/image.png" alt="">[ 출처 | What every programmer should know about Synchronous vs. Asynchronous Code]</p>
<p>왼쪽 그래프는 <strong>&#39;비동기&#39;</strong>로 작동하는 방식입니다. 
한번에 여러 Task(빨강, 민트, 옐로 막대그래프)가 동시에 병렬적으로 실행되는 모습을 보입니다.</p>
<p>반면, 오른쪽 차트는 <strong>&#39;동기&#39;</strong>적으로 작동하는 방식입니다. 
하나의 Task가  끝날 때 까지 기다렸다가 다음 Task가 실행되는 모습을 볼 수 있죠. </p>
<p>색깔 순서대로 빨강이 실행되고 민트가 실행되고 노랑이 실행됩니다.
그렇기에 총 실행 시간으로 따지자면 &#39;동기&#39; 방식이 더 느리게 됩니다.</p>
<blockquote>
<p><a href="https://www.youtube.com/watch?v=1BAZf3PsjWA">NVIDIA GPU와 CPU차이</a>
해당 링크는 NVIDIA에서 CPU,GPU를 소개하는 영상입니다.
저는 해당 영상을 보면서 동기(CPU) 비동기(GPU)를 이해하는데 도움을 얻었습니다. 
필요하신분은 시청해주시면  한번 봐주세요 :) (매우 짧습니다 😉)</p>
</blockquote>
<p>자 그럼 다시 돌아가보죠</p>
<h3 id="✅-동기synchronous란-무엇일까">✅ &#39;동기(synchronous)&#39;란 무엇일까?</h3>
<p>동기는 <strong>직렬적</strong>으로 <strong>Task를 수행</strong>하는 방식입니다.</p>
<p>즉, 요청을 보낸 후 응답을 받아야지만 다음 동작이 이루어집니다. 
그렇다면 앞에 Task가 처리되어야 나머지 뒤에 Task들이 실행 할 수 있는 겁니다.
<img src="https://velog.velcdn.com/images/yoon_min/post/8eb7656d-628e-4cdf-90bd-2021cd17fee8/image.png" alt=""><a href="https://poiemaweb.com/es6-promise"> 출처 | poiemaweb</a></p>
<h3 id="✅-동기synchronous란-무엇일까">✅ &#39;동기(synchronous)&#39;란 무엇일까?</h3>
<p>비동기는 <strong>병렬적</strong>으로 <strong>Task를 수행</strong>하는 방식입니다.
<strong>Task가 종료되지 않은 상태</strong>라 하더라도 <strong>대기하지 않고</strong> 즉시 <strong>다음 Task를 실행</strong>한다. 
<img src="https://velog.velcdn.com/images/yoon_min/post/257825ad-4806-4c46-9667-da8dfaeb9e45/image.png" alt=""><a href="https://poiemaweb.com/es6-promise"> 출처 | poiemaweb</a></p>
<blockquote>
<ol>
<li>예를 들어 서버에서 <strong>데이터를 가져와서 화면에 표시</strong>하고자 할때 <strong>서버에 Task데이터를 요청</strong>하게됩니다.</li>
<li>이후 서버로부터 <strong>데이터가 응답될 때까지</strong> <strong>대기하지 않고(Non-Blocking)</strong> 즉시 <strong>다음 Task를 수행</strong>하게 됩니다. </li>
<li>이후 <strong>서버로부터 데이터가 응답</strong>되면 <strong>이벤트가 발생</strong>하고 <strong>이벤트 핸들러가 데이터를 가지고 수행할 Task를 계속해 수행</strong>합니다. </li>
</ol>
</blockquote>
<p>자바스크립트의 대부분의 DOM 이벤트와 Timer 함수(setTimeout, setInterval), Ajax 요청은 비동기식 처리 모델로 동작합니다.</p>
<h4 id="장점">장점</h4>
<p>자바스크립트에서 빈번하게 사용되는 비동기식 처리 모델은 요청을 병렬로 처리하여 다른 요청이 블로킹(blocking, 작업 중단)되지 않습니다.</p>
<h4 id="단점">단점</h4>
<p>하지만 비동기 처리를 위해 콜백 패턴을 사용하면 처리 순서를 보장하기 위해 여러 개의 콜백 함수가 네스팅(nesting, 중첩)되어 복잡도가 높아지는 콜백 헬(Callback Hell)이 발생하는 단점이 있습니다. 
콜백 헬은 가독성을 나쁘게 하며 실수를 유발하는 원인이 됩니다. 
하단 코드는 콜백 헬이 발생하는 전형적인 사례입니다.</p>
<pre><code class="language-javascript">step1(function(value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        step5(value4, function(value5) {
            // value5를 사용하는 처리
        });
      });
    });
  });
});</code></pre>
<h2 id="✅-promise">✅ Promise</h2>
<p>동기 비동기에 대한 지식을 채웠으니 해당 글의 중심인 Promise로 돌아와보자</p>
<blockquote>
<p>비동기 작업을 처리할 때 사용되는 객체(Promise 객체)</p>
</blockquote>
<h3 id="✨-특징">✨ 특징</h3>
<ul>
<li>Promise는 미래에 완료될 작업을 나타내는 객체입니다.</li>
<li>작업이 완료되면 결과를 반환(resolve)하거나, 작업이 실패하면 에러를 반환(reject)합니다.</li>
<li>주로 .then()과 .catch()구문을 사용해 성공 시와 실패 시의처리를 구분합니다.</li>
</ul>
<h3 id="✔️-예제-코드">✔️ 예제 코드</h3>
<p>코드를 보며 무슨 말인지 보도록하죠
하단 코드는 Promise객체를 이용해 hello를 입력시 
console에 정상적으로 성공, 실패 내용이 반환 되는지 확인하고자한 코드입니다.</p>
<pre><code class="language-javascript">//Promise는 객체이기 때문에 생성자(new)를 이용해 만들게됩니다.
const myPromise = new Promise((resolve, reject)=&gt;{
      //Promise 객체 생성시 인자로 call back 함수를 넣을 수 있습니다.
      //이러한 call back 함수 안에서 비동기 작업을 처리합니다.
   //어떤 작업을 진행하고 결과를 알려줘~ 하고 약속한 것 이죠
    //myPromise를 통해 약속의 객체를 받습니다.
    setTimeout(()=&gt;{
        const text = prompt(`&quot;hello&quot;를 입력하면 선물을 줄께`);
        if(text === &quot;hello&quot;){
            resolve( &#39;💻&#39;); //승인
        }else{
            reject(&#39;error message&#39;) //실패
        }
    }, 2000); //2초 후 결과값 반환
});

myPromise
    .then((result)=&gt;{
        console.log(&#39;result&#39;,result);
    })
    .catch(()=&gt;{
        console.log(&quot;err:&quot;, err);
    })
    .finally(()=&gt;{
        console.log(&quot;finally&quot;);
    })</code></pre>
<blockquote>
<pre><code> #### 코드를 자세히 살펴봅시다.</code></pre><p>Promise 객체 생성시(생성자 new) 인자로 call back 함수를 안에 넣을 수 있습니다.</p>
</blockquote>
<pre><code class="language-javascript">//myPromise를 통해 약속의 객체를 받습니다.
const myPromise = new Promise(()=&gt;{ 
      //해당 공간에는 callback(비동기) 함수를 집어 넣으면 됩니다.
    //그래서 상단 코드에서는 setTimeout(비동기)이 사용된것입니다.
    }</code></pre>
<p>이러한 call back 함수 안에서 비동기 작업을 처리하게되는겁니다.</p>
<p>즉 <strong>&quot;어떤 작업을 진행하고 결과를 알려줘~&quot;</strong> 하고 <strong>약속(Promise)</strong>한 것 이죠</p>
<blockquote>
<p>두번째 특징에서 </p>
</blockquote>
<ul>
<li>작업이 <strong>완료되면 결과를 반환(resolve)</strong></li>
<li>작업이 <strong>실패하면 에러를 반환(reject)</strong>
이렇게 소개드렸습니다.<pre><code class="language-javascript">// Promise 객체의 생성
const promise = new Promise((resolve, reject) =&gt; {// resolve, reject 사용시 선언
// 비동기 작업을 수행한다.
    setTimeout(()=&gt;{
      const text = prompt(`&quot;hello&quot;를 입력하면 선물을 줄께`);//prompt로 텍스트 전달해줘
      if(text === &quot;hello&quot;){
          resolve( &#39;💻&#39;); //승인시 출력
      }else{
          reject(&#39;error message&#39;) //실패시 출력
      }
  }, 2000); //2초 후 결과값 반환
}
});</code></pre>
해당 코드를 보면 setTimeout으로 비동기 작업을 실행하게되고
prompt에 원하는 값을 입력합니다. 
<img src="https://velog.velcdn.com/images/yoon_min/post/e60964a4-8d89-41dd-8852-d3e15156aa56/image.png" alt="">
hello를 입력시 💻정상적으로 출력이되는 모습을 볼 수 있습니다.
<img src="https://velog.velcdn.com/images/yoon_min/post/67689308-8fb9-40aa-9d6d-18ff078a193b/image.png" alt="">
그렇다면 다른 값을 넣어보겠습니다.<img src="https://velog.velcdn.com/images/yoon_min/post/dd18ead1-8416-4cf1-8356-c4a6b27b1c49/image.png" alt="">
이렇게 에러를 출력하는 모습을 볼 수 있습니다.
<img src="https://velog.velcdn.com/images/yoon_min/post/10471d90-044f-4047-846b-897bc655ce61/image.png" alt=""></li>
</ul>
<h3 id="✔️-비동기-처리-결과">✔️ 비동기 처리 결과</h3>
<p>이때 Promise는 비동기 처리가 성공(fulfilled)하였는지 또는 실패(rejected)하였는지 등의 상태(state) 정보를 가지게됩니다.</p>
<ul>
<li><strong>pending</strong>: 비동기 처리가 아직 수행되지 않은 상태</li>
<li><strong>fulfilled</strong>: 비동기 처리가 수행된 상태 (성공)</li>
<li><strong>rejected</strong>: 비동기 처리가 수행된 상태 (실패)</li>
<li><strong>settled</strong>: 비동기 처리가 수행된 상태 (성공 또는 실패)</li>
</ul>
<h3 id="✔️-promise-호출-과정">✔️ Promise 호출 과정</h3>
<blockquote>
<p>하단 부분은 비동기 함수가 정상적으로 작동했을때
.then() , .catch()이라는 메소드에 callback함수로 담아서 확인이 가능합니다.</p>
</blockquote>
<blockquote>
<h4 id="then">then()</h4>
<p>비동기 함수 내에서 Promise 객체를 생성하고 그 내부에서 비동기 처리를 구현합니다. 
이때 비동기 처리에 성공하면 resolve 메소드를 호출합니다.
이때 resolve 메소드의 인자로 비동기 처리 결과를 전달 하는데, 이 처리 결과는 Promise 객체의 후속 처리 메소드로 전달됩니다..</p>
</blockquote>
<blockquote>
<h4 id="catch">catch()</h4>
<p>만약 비동기 처리에 실패하면 reject 메소드를 호출합니다. 
이때 reject 메소드의 인자로 에러 메시지를 전달합니다. 
이 에러 메시지는 Promise 객체의 후속 처리 메소드로 전달되며 후속 처리 메소드에는 대표적으로 then(Promise 반환)과 catch(예외)가 있습니다.</p>
</blockquote>
<pre><code class="language-javascript">myPromise
    .then((result)=&gt;{
        console.log(&#39;result&#39;,result);
    })
    .catch(()=&gt;{
        console.log(&quot;err:&quot;, err);
    })
    .finally(()=&gt;{
        console.log(&quot;finally&quot;);
    })</code></pre>
<h3 id="✔️-promise-체이닝">✔️ Promise 체이닝</h3>
<p>비동기 함수의 처리 결과를 가지고 다른 비동기 함수를 호출해야 하는 경우가있습니다. 
이때 함수의 호출이 중첩(nesting)이 되어 복잡도가 높아지는 콜백 헬이 발생하게됩니다. </p>
<p>프로미스는 후속 처리 메소드인 then이나 catch로 메소드를 체이닝(chainning)하여 여러 개의 프로미스를 연결하여 사용할 수 있기 때문에 콜백 헬을 해결할 수있습니다.</p>
<p>따라서, then 메소드가 Promise 객체를 반환하도록 하면(then 메소드는 기본적으로 Promise를 반환한다.) 여러 개의 프로미스를 연결하여 사용할 수 있습니다.</p>
<h3 id="출처">출처</h3>
<p><a href="https://www.youtube.com/watch?v=iUGLyhbwYkU">짐코딩 유튜브</a>
<a href="https://velog.io/@khy226/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0%EB%9E%80-Promise-asyncawait-%EA%B0%9C%EB%85%90">velog_khy226</a>
<a href="https://poiemaweb.com/es6-promise">poiemaweb</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[How to use Postman? 🙌🏻(feat: express.js)]]></title>
            <link>https://velog.io/@yoon_min/How-to-use-Postman-feat-express.js</link>
            <guid>https://velog.io/@yoon_min/How-to-use-Postman-feat-express.js</guid>
            <pubDate>Wed, 11 Dec 2024 16:32:04 GMT</pubDate>
            <description><![CDATA[<h1 id="✅-postman-이란">✅ Postman 이란?</h1>
<p><em>해당 글은 설치, 접속 방법이 주가 아닌 postman 코드 연결, 요청 확인등을 위주로 작성하였습니다! :)</em></p>
<blockquote>
<p><strong>&quot;API의 개발을 빠르고 쉽게, 개발된 API를 테스트할 수 있고,팀원들간의 공유를 할 수 있게 해주는 플랫폼입니다.&quot;</strong> </p>
</blockquote>
<h3 id="✅-api-넌-누구🧐">✅ API 넌 누구?🧐</h3>
<p>API란 <strong>Application Programming Interface</strong>의 약자입니다.</p>
<ul>
<li><strong>Application</strong></li>
</ul>
<p>API의 맥락에서 <strong>애플리케이션이</strong>라는 단어는 <strong>고유한 기능을 가진 모든 소프트웨어</strong>를 나타냅니다. </p>
<ul>
<li><strong>Interface</strong></li>
</ul>
<p>인터페이스는 <strong>두 애플리케이션 간</strong>의 <strong>서비스 계약</strong>이라고 할 수 있습니다. </p>
<p>interface를 쉽게 설명해보면 풀어보면, 어떤 두 매개체가 상호작용하여 영향을 끼칠 수 있는 어떠한 요소들을 의미합니다.요소들의 예시로 사람과 사람, 기계와 기계, 사람과 기계의 관계등을 나타낼 수 있습니다.</p>
<p>예시로 우리가 컴퓨터의 글을 입력하고 싶을때, 키보드를 사용해 글을 입력하게 될 것 입니다.
이때 우리가 사용한 키보드가 사람과 컴퓨터 사이에 인터페이스가 되는 겁니다.</p>
<ul>
<li><strong>API</strong></li>
</ul>
<p>이 계약은 <strong>요청</strong>과 <strong>응답</strong>을 사용하여 <strong>두 애플리케이션이 데이터를 주고받는 방법이란뜻</strong>을 정의합니다. 
이때는 컴퓨터끼리 데이터를 주고받는것이기 때문에 어떠한 양식이 필요할것이고
API 문서에는 개발자가 이러한 요청과 응답을 구성하는 방법에 대한 정보가 들어 있습니다.</p>
<h4 id="✅-api-사용방법">✅ API 사용방법</h4>
<p>여럿 공식사이트에 들어가면 각 회사의 API가 존재하는 곳이 있습니다.
해당 API에는 key가 존재 할 것이고, 그에따른 사용 방법이 적혀있으니 참고하면 될 것이다.</p>
<h3 id="✅-api-예시를-통한-postman-사용법">✅ API 예시를 통한 Postman 사용법</h3>
<p>express.js를 통해 만든 간단한 API를 통해 Postman을 테스트 해보자
먼저 간단한 사용법을 순서대로 보여주면
<img src="https://velog.velcdn.com/images/yoon_min/post/8e399c51-4464-46f8-b8c1-2092dfd838d5/image.png" alt="">
사진에서 보이는 1번(new button)을 누른 후 2번(HTTP icon)을 선택해준다.
<img src="https://velog.velcdn.com/images/yoon_min/post/fec77875-69a6-4bf4-b91b-e86b8c34259c/image.png" alt="">
API에 작성한 요청(Get,Post등)을 적어주고 올바른 주소 값(localhost:3000)을 넣어준 후 API명령에 따라 작성해주면된다.
</p>
<h3 id="⚡️-실전-사용">⚡️ 실전 사용</h3>
<blockquote>
<p>시작 전 서버를 꼭 켜주세요</p>
</blockquote>
<h4 id="✔️-setting">✔️ Setting</h4>
<pre><code class="language-javascript">//해당 란은 설정 값을 보여주기위해 작성되었습니다.
const express = require(&#39;express&#39;);
const app = express();
const cors = require(&#39;cors&#39;);
app.use(cors()); // 쿠키 파싱 미들웨어
app.use(express.json()); // JSON 요청 본문 파싱
// sample DB
![](https://velog.velcdn.com/images/yoon_min/post/1609075f-4394-4f99-8139-29d50e5ae60f/image.png)

//Port | 포트는 예제로 3003을 임의로 잡음
const PORT = 3003;
app.listen(PORT, () =&gt; {
    console.log(`서버가 http://localhost:${PORT}에서 실행 중`);
});</code></pre>
<h4 id="✔️-get">✔️ Get</h4>
<p>먼저 간단한 <strong>get 요청</strong>을 postman으로 확인해보자</p>
<pre><code class="language-javascript">// 공개 라우트
app.get(&#39;/&#39;, (req, res) =&gt; {
    res.send(&#39;모든 사용자가 접근 가능&#39;);
});</code></pre>
<p>해당 코드를 작성 후 Postman에서 정상작동하는지 요청을 받아보면
<img src="https://velog.velcdn.com/images/yoon_min/post/5e227206-a3e5-4378-9864-d7606d1d3349/image.png" alt="">
이렇게 잘받아오는걸 확인 할 수 있다.
다른 예시도 해보자</p>
<h4 id="✔️-post">✔️ Post</h4>
<p>해당 코드는 간단한 로그인 API입니다.</p>
<p>코드 주석을 잘보며 따라가시길 추천드립니다!</p>
<pre><code class="language-javascript">// Login API
app.post(&#39;/login&#39;, (req, res) =&gt; { //login 경로 request, response요청
    const {username, password} = req.body; // 요청 body에 username, password가 있어야해
    if (sample_db.id === username &amp;&amp; sample_db.password === password) { //샘플 DB와 username과 password가 동일하다면
        const token = jwt.sign({ userId: sample_db.id, role: sample_db.role }, SECRET_KEY, { expiresIn: &#39;1h&#39; });//JWT 토큰을 1시간동안 생성해줘
        res.send({ token }); // response에 토큰을 넣어줘
    }
    else {
        res.status(401).send(&#39;Invalid username or password&#39;); //틀리면 401 상태와 해당 메시지를 전송해줘
    }
});</code></pre>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/213e50d2-79b2-4044-81c4-5163e6ec17e2/image.png" alt="">
자! 코드 주석을 잘 따라가며 봤으면 이제부터 Postman에서 요청해 확인해보도록 하겠습니다.</p>
<h4 id="순서대로-따라와주세요">순서대로 따라와주세요</h4>
<ul>
<li>왼쪽 상단 빨간 박스부터 Post요청이었으니 Post로 변경합니다.</li>
<li>/login으로 경로를 요청했으니 알맞게 변경합니다.</li>
<li>code에서 body에 username, password 요청했으니 Body 선택합니다.<pre><code class="language-javascript">const {username, password} = req.body; // 요청 body에 username, password가 있어야해</code></pre>
</li>
<li>Body 안에 raw  선택 후 sample_db에있던 데이터 값을 동일하게 넣어주고 send버튼을 누르면</li>
<li>성공적으로 전송(200 OK)가 완료되고</li>
<li>JWT 토큰도 정상적으로 발급된 모습을 확인 가능합니다.</li>
</ul>
<p>프로젝트 상단에서 문서화도 쉽게 가능합니다.
<img src="https://velog.velcdn.com/images/yoon_min/post/5e3fb31a-6cac-4ae7-afcc-6ba12c0757a5/image.png" alt="">
<img src="https://velog.velcdn.com/images/yoon_min/post/ffe7ec0c-8543-4dac-9ba4-5a1aa2716976/image.png" alt="">
문서에 들어가 Publish 진행시 테스트 진행된 API들이 정리되기에 사용자에게 링크만 전달하게되면
보다 쉬운 협업이 가능합니다. 이점이 Postman에 가장 큰 장점이라 생각합니다.
<img src="https://velog.velcdn.com/images/yoon_min/post/00d7b52a-495d-41fe-9ed4-f32d8d844c8d/image.png" alt="">
해당 사진은 문서화가된 예시 사진입니다.</p>
<h3 id="✨-postman-정리">✨ Postman 정리</h3>
<blockquote>
<p>HTTP 요청 보내기
: GET, POST, PUT, DELETE 등의 HTTP 요청을 보낼 수 있다.
요청 파라미터 설정
: HTTP 요청에 필요한 파라미터를 쉽게 설정할 수 있다.
응답 확인
: HTTP 요청에 대한 응답을 확인하고, JSON, XML 등의 다양한 형식으로 응답을 처리할 수 있다.
히스토리 관리
: 이전에 보낸 HTTP 요청과 응답을 저장하고, 다시 사용할 수 있다.
환경 변수 설정
: 서로 다른 환경에서 동일한 HTTP 요청을 보내야 하는 경우, 환경 변수를 설정해 간편하게 처리할 수 있다.
API 문서화
: API 요청과 응답을 문서화하고, 다른 개발자와 공유할 수 있다.
Postman은 무료로 사용할 수 있으며, 다양한 템플릿과 예제 코드를 제공해주어 사용자가 더욱 효과적으로 API를 테스트하고 개발할 수 있도록 도와준다.</p>
</blockquote>
<p><strong>출처</strong>
<a href="https://velog.io/@seungmini/%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC-Postman">velog | seunmini.log</a>
<a href="https://velog.io/@oka1313/HTTP%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-Postman">velog | oka1313.log</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[암호화, bcrypt를 만나다⚡️(feat.해시함수)]]></title>
            <link>https://velog.io/@yoon_min/%EC%95%94%ED%98%B8%ED%99%94-bcrypt%EB%A5%BC-%EB%A7%8C%EB%82%98%EB%8B%A4feat.%ED%95%B4%EC%8B%9C%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@yoon_min/%EC%95%94%ED%98%B8%ED%99%94-bcrypt%EB%A5%BC-%EB%A7%8C%EB%82%98%EB%8B%A4feat.%ED%95%B4%EC%8B%9C%ED%95%A8%EC%88%98</guid>
            <pubDate>Wed, 11 Dec 2024 07:41:38 GMT</pubDate>
            <description><![CDATA[<h2 id="너는-누구냐-bcrypt🧐">너는 누구냐, bcrypt🧐</h2>
<blockquote>
<p><strong>bcrypt란 암호화 라이브러리이다.</strong>
🚨우리는 먼저 사전 지식을 알아보고 가보도록하자🚨</p>
</blockquote>
<blockquote>
<p>✨사전 지식[해시, 해시함수, 복호화, 암호화]</p>
</blockquote>
<p>비밀 번호를 안전하게 해시(hash) 처리하여 데이터 베이스에 저장할때 사용된다.
해싱된 값은 복호화가 불가능하고, 비교를 통해 비밀번호를 검증해야한다.</p>
<h3 id="✅-해시-함수-해싱-복호화-너는-누구">✅ 해시 함수? 해싱? 복호화? 너는 누구?</h3>
<h4 id="해시-함수는-무엇인가요-토스페이먼츠-해싱-함수-이미지">해시 함수는 무엇인가요? <img src="https://velog.velcdn.com/images/yoon_min/post/0b653d1d-ca92-4af2-b630-b0f9c86c52b6/image.png" alt="토스페이먼츠 해싱 함수 이미지"></h4>
<p>[출처 | 토스페이먼츠 개발자 센터]</p>
<p><strong>📲 입력값</strong></p>
<p>해시 함수의 입력 값은 <strong>&#39;메시지&#39;</strong> 라고 부릅니다.
입력값으로 다양한 길이와 형태의 데이터가 사용됩니다. 
단일 문자, 음악파일, 메시지뿐만 아니라 복잡한 데이터 구조도 입력 가능합니다.</p>
<p>또한 해시 함수는 입력 데이터를 특정 알고리즘에 따라 고정된 길이의 해시값으로 변환하는데,
이 과정에서 데이터는 내부적으로 이진수로 처리가 처리됩니다.</p>
<pre><code class="language-javascript">//Hello ASCII code 변환 ASCII code 이진수로 변환
&quot;H&quot; -&gt; ASCII: 72 -&gt; Binary: 01001000
&quot;e&quot; -&gt; ASCII: 101 -&gt; Binary: 01100101
&quot;l&quot; -&gt; ASCII: 108 -&gt; Binary: 01101100
&quot;o&quot; -&gt; ASCII: 111 -&gt; Binary: 01101111</code></pre>
<pre><code class="language-javascript">//Hello 이진수 도출 값
01001000 01100101 01101100 01101100 01101111</code></pre>
<p><strong>🔐 해시 함수</strong></p>
<p>해시 함수는 입력값을 고정된 크기의 블록으로 나눈 다음에 수학적 연산을 적용해 최종 해시를 출력한다.
<img src="https://velog.velcdn.com/images/yoon_min/post/618b66b9-05e6-4bfa-896d-c427eb586ec0/image.png" alt="">[출처 | 업비트 투자자보호센터]</p>
<p>해시 함수는 <strong>주어진 입력에 대해 항상 동일한 해시를 생성</strong>하고</p>
<blockquote>
<p>이미지와 상단과 같은 해시함수 입력시, 같은 출력 값 도출</p>
</blockquote>
<p><strong>입력 데이터</strong>가 <strong>조금이라도 바뀌어도 출력값은 크게 달라집니다</strong>.</p>
<blockquote>
<p>이미지와 하단과 같이 해시함수 입력시 상단과 크게 다른 값 출력</p>
</blockquote>
<p>좋은 해시 함수는 효율적이며 다른 입력이 동일한 해시를 생성하는 것을 거의 불가능하게 합니다.  이런 특성 때문에 해시를 데이터 비교 및 무결성에 안전하게 사용할 수 있습니다.</p>
<p>대표적으로 사용되는 해시 함수는 다음과 같습니다.</p>
<ul>
<li><strong>암호화 해시 함수(Cryptographic Hash Functions)</strong> 
비밀번호 보관, 디지털 서명 등 보안 관련 용도로 사용되는 해시 함수입니다. 
<em>대표적으로는 SHA-256, SHA-3, MD5 함수가 있죠.</em></li>
<li><strong>비암호화 해시 함수(Non-cryptographic Hash Functions)</strong> 
데이터 인덱싱 또는 해시 테이블과 같이 보안보다 효율이 필요할 때 사용되는 해시 함수입니다. 
<em>대표적으로는 MurmurHash, CityHash 함수가 있죠.</em></li>
</ul>
<p><strong>📤 출력값</strong></p>
<p>해시 함수의 <strong>출력값</strong>은 <strong>&#39;해시&#39;</strong> 또는 <strong>&#39;다이제스트&#39;</strong>라고도 합니다.
<strong>입력값</strong>에 <strong>고유한 출력값이 생성</strong>되기 때문에 지문과 비슷하다고 생각할 수 있습니다. </p>
<p>출력값의 길이는 해시 함수에 따라 달라지는데 예를 들어 SHA-256 해시 함수는 <strong>항상 265비트의 출력값을 생성</strong>합니다.
<img src="https://velog.velcdn.com/images/yoon_min/post/bae8a4a3-afe4-4116-822b-d042776b652a/image.png" alt="">[출처 | 업비트 투자자보호센터]</p>
<h4 id="✅-해싱">✅ 해싱</h4>
<p><strong>해싱은 암호화와 달리</strong> <strong>출력값</strong>을 <strong>원본 입력값</strong>으로 <strong>복호화할 수 없습니다</strong>. 
출력값은 항상 고정된 길이의 문자열을 출력하기 때문에 입력 데이터가 손실되고, 출력값과 해싱 함수를 안다고 해도 대응하는 입력값을 찾는 것은 매우 어려운 일입니다.</p>
<blockquote>
<p>🧐 그럼 해싱은 언제 필요하죠?
✨ 해시 함수는 다양한 분야에서 중요한 역할을 하고 있어요.</p>
</blockquote>
<ul>
<li><p><strong>데이터 무결성</strong> 
해시 함수로 데이터가 조작되지 않았는지 검증할 수 있습니다. 
예를 들어, 파일을 다운로드하면 파일과 함께 해시가 제공됩니다. 
다운로드 파일을 해싱해서 나오는 출력값과 다운로드할 때 받은 해시와 비교하면 데이터 무결성을 확인할 수 있습니다.</p>
</li>
<li><p><strong>디지털 서명</strong> 
암호화 해시 함수로 메시지나 문서에 디지털 서명을 추가할 수 있습니다. 
디지털 서명은 메시지 전송자의 신원을 증명하고 내용이 변조되지 않았음을 보증합니다. 
전송자는 메시지의 해시를 암호화해서 디지털 서명을 생성하고, 수신자는 디지털 서명을 복호화한 다음에 수신한 메시지의 해시와 비교합니다.</p>
</li>
<li><p><strong>비밀번호 저장</strong> 
대부분의 서비스는 사용자의 실제 비밀번호를 서버에 저장하지 않습니다. 
대신 비밀번호의 해시를 저장합니다. 
서버가 해킹되어도 실제 비밀번호가 노출되지 않기 때문에 비밀번호가 안전하게 보호가 가능합니다. 
사용자가 비밀번호를 입력하면 비밀번호를 해싱하고, 서버에 있는 해시랑 비교하며 사용자의 신원을 확인합니다.</p>
</li>
<li><p><strong>해시 테이블</strong> 
테이블의 인덱스로 해시를 사용하는 자료구조입니다. 
검색이 빠르다는 장점이 있지만 충돌이 일어날 수 있다는 위험이 있습니다.</p>
</li>
</ul>
<h4 id="✅-복호화decryption">✅ 복호화(Decryption)</h4>
<p>복호화는 암호화의 기본 요소입니다.
<img src="https://velog.velcdn.com/images/yoon_min/post/1af8dd48-d338-414f-ab93-542d7920f668/image.png" alt="신시웨이 암호화 복호화">[출처 | 신시웨이]
암호화 알고리즘을 사용하여 평문을 암호문으로 변환합니다.</p>
<p>이후 암호화된 데이터를 원래의 원본 데이터로 되돌리는 과정으로 암호문과 암호키를 사용하여 수행됩니다. </p>
<p>이과정을 복호화라하며 올바른 암호키를 사용하여 복호화를 수행하여야 암호문이 원본 데이터로 해독 할 수 있습니다.</p>
<h2 id="이제-bcrypt를-알아보자-⚡️">이제 bcrypt를 알아보자 ⚡️</h2>
<h3 id="✅-bcrypt">✅ bcrypt</h3>
<p>• 암호화 라이브러리
• 비밀번호를 안전하게 해시(hash) 처리하여 데이터베이스에 저장할 때 사용
• 해싱된 값은 복호화 불가능하고, 비교를 통해 비밀번호를 검증</p>
<h4 id="✅-주요기능">✅ 주요기능</h4>
<p><strong>✔️ 비밀번호 해싱</strong></p>
<ul>
<li>비밀 번호를 안전하게 암호화함
해당 코드와 같이 해쉬 가능<pre><code class="language-javascript">// 내가 삽입한 password를 해쉬함 | 작업 계수 10 (2^10 = 1,024회 반복)
bcrypt.hash(password, 10, (err, hash) =&gt; {
    if (err) {
      console.error(&#39;해싱 에러:&#39;, err);
      return res.status(500).send(&#39;Server error occurred while hashing.&#39;);
    }
    console.log(&#39;해싱된 비밀번호:&#39;, hash);</code></pre>
<blockquote>
<ul>
<li>BCrypt는 &#39;작업 계수(work factor)&#39;라는 매개변수를 사용합니다.</li>
</ul>
</blockquote>
</li>
<li>이 작업 계수는 해시를 생성하는 데 필요한 계산 횟수를 결정합니다.</li>
<li>계수가 1 증가할 때마다 필요한 계산 시간은 2배로 증가합니다.</li>
</ul>
<pre><code class="language-javascript">// 작업 계수 10 (2^10 = 1,024회 반복)
bcrypt.hashpw(password, bcrypt.gensalt(10))

// 작업 계수 12 (2^12 = 4,096회 반복)
bcrypt.hashpw(password, bcrypt.gensalt(12))</code></pre>
<p><strong>✔️ 해시 비교</strong></p>
<ul>
<li>입력된 비밀번호와 저장된 해시를 비교
해당 코드와 같이 비교 가능<pre><code class="language-javascript">// password와 hashed password를 compare 함수를 통해 비교해 맞는지 비교 확인한다.
bcrypt.compare(password, hashedPassword, (err, result) =&gt; {
      if (err) {
        console.error(&#39;비교 에러:&#39;, err);
        return res.status(500).send(&#39;Server error occurred while comparing passwords.&#39;);
      }
      console.log(&#39;비밀번호 일치 여부:&#39;, result);</code></pre>
</li>
<li><em>✔️ 솔트(salt)*</em></li>
<li>동일한 비밀번호에 대해 서로 다른 해시 값을 생성하기 위해 랜덤 데이터를 추가한다. 솔트가 있기에 같은 비밀번호일지라도 다른 해시 값을 가지게된다.
<img src="https://velog.velcdn.com/images/yoon_min/post/773ad9f2-991f-403a-86a7-e966d0720df7/image.png" alt="">[출처 | velog_sangmin7648]</li>
</ul>
<h3 id="✅-bcrypt-해쉬-비밀번호-구조">✅ bcrypt 해쉬 비밀번호 구조</h3>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/20e53270-deda-43a1-8be3-28ed59708c02/image.png" alt="">[출처 | velog_sangmin7648]
<strong>✔️ Algorithm</strong>
• 알고리즘 식별자. &#39;$2a$$&#39;는bcrypt를 의미
<strong>✔️ Cost factor</strong>
• 키 스트레칭한 횟수. 2^n으로 위의 경우 2^10=1024
<strong>✔️ Salt</strong>
• 128비트 솔트, 22자 base64로 인코딩
<strong>✔️ Hash</strong>
• salting과 키 스트레칭 후 해시 값
<strong>✔️ bcrypt에서 검증</strong>
• 비밀번호가 동일한지 검증하기 위해 입력된 비밀번호에 salting, 키 스트레칭을 해서 저장된 bcrypt 문자열과 비교</p>
<h4 id="✅-장점">✅ 장점</h4>
<p><strong>✔️ 복호화 불가능</strong></p>
<ul>
<li>해시된 비밀번호는 복호화할 수 없음</li>
<li><em>✔️ 솔트(salt)로 안전성 강화*</em></li>
<li>동일한 비밀번호에 대해 다른 해시 값을 생성함</li>
<li><em>✔️ 간단한 사용*</em></li>
<li>비밀번호 저장 및 비교를 간편하게 처리 가능</li>
</ul>
<h4 id="✅-단점">✅ 단점</h4>
<p><strong>✔️ CPU사용 량</strong></p>
<ul>
<li>솔트 라운드 수가 높을수록 처리 시간이 길어진다.<ul>
<li>상단 코드에서 보았던 해당 부분에서 10을 나타낸다.<pre><code class="language-javascript">bcrypt.hash(password, 10, (err, hash) =&gt; {...</code></pre>
<h4 id="✅-주의사항">✅ 주의사항</h4>
</li>
</ul>
</li>
<li>비밀번호는 절대 평문으로 저장하면안된다.(반드시 해쉬 비밀번호로 저장)</li>
<li>솔트 라운드 값을 적절히 설정(10~12추천)</li>
<li>bcrypt는 비밀번호 해싱 전용이며, 데이터 암호화에는 사용하지 않음</li>
</ul>
<p>** 오늘은 bcryp에 대해 알아보았습니다. 감사합니다.**</p>
<h4 id="출처">출처</h4>
<p><a href="https://docs.tosspayments.com/resources/glossary/hashing">토스페이먼츠 개발자 센터</a>, <a href="https://developer.mozilla.org/ko/docs/Glossary/Decryption">MDN</a>, <a href="https://m.upbitcare.com/academy/education/blockchian/52">업비트 투자자보호센터</a></p>
<h4 id="메모-오류-해결-list">메모 오류 해결 list</h4>
<p><a href="https://helloinyong.tistory.com/130">[2019.06.04] 오늘의 TIL- 암호화 bcrypt.compare가 무조건 false로 나던 문제</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JWT(JSON Web Token), 누구신가요?🧐]]></title>
            <link>https://velog.io/@yoon_min/JWTJSON-Web-Token-%EB%88%84%EA%B5%AC%EC%8B%A0%EA%B0%80%EC%9A%94</link>
            <guid>https://velog.io/@yoon_min/JWTJSON-Web-Token-%EB%88%84%EA%B5%AC%EC%8B%A0%EA%B0%80%EC%9A%94</guid>
            <pubDate>Sun, 17 Nov 2024 17:13:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoon_min/post/d899bf16-789a-41cc-8330-55bc93d51033/image.png" alt="JWT Image"></p>
<h2 id="들어가며">들어가며</h2>
<p>개인 프로젝트 진행 간 백엔드, 프론트 통신 단계에서 JWT라는 단어를 접하게되었다.
인증/인가, 세션과 토큰이라는 단어와 같이 나오는 단어였고, 대부분 큰플젝이 아니면 JWT쓴다고한다.
일단 찾아보고 공부하다보니 인증,인가 /세션, 토큰, 쿠키에 관한 이야기도 다른 글에서 추가로 적어 보도록하겠다.</p>
<h2 id="너는-누구냐-jwt-🤯">너는 누구냐, <a href="https://jwt.io/">JWT</a> 🤯</h2>
<p>JWT(Json Web Token)은 Json 객체에 인증이 필요한 정보들을 담은 후 비밀키로 서명한 토큰이다.
공식적으로 인증(Authentication) &amp; 권한허가(Authorization) 방식으로 사용되는 <a href="https://datatracker.ietf.org/doc/html/rfc7519">인터넷 표준 인증 방식</a>이다.
그리고, 필요한 모든 정보를 한 객체에 담아서 전달하기 때문에 JWT 한 가지로 인증을 마칠 수 있습니다.
( 보안 강화를 위해 예시로 JWE와 같은 방어 기술들을 함께 사용 가능합니다 )
<br>
JWT는 웹 표준(HTTP)을 따르기 때문에 대부분의 언어를 지원하기에 큰 장점으로 볼 수 있습니다.
가장 많이 사용되는 &#39;로그인&#39; 과정 위주로 풀어 보겠습니다.</p>
<h2 id="jwt의-구조💻">JWT의 구조💻</h2>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/96375ce7-9e72-4130-ba0d-d0ff40e42e4c/image.jpeg" alt="">
JWT는 위와 같은 구조를 가지고 있습니다.
헤더, 내용, 서명이 &#39;.&#39;으로 구분하여 1개의 토큰을 이루고 완성된 토큰은 이와 같은 형태를 보입니다.</p>
<p>하단 토큰은 공식문서에서 예시이며, 인코더와 디코더를  볼 수 있으며
<strong><a href="https://jwt.io/">해당 링크</a></strong>를 통해 JWT 만들기 및 변하는 과정을 볼 수 있습니다.</p>
<h4 id="encoded"><strong>Encoded</strong></h4>
<pre><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</code></pre><h4 id="decoded"><strong>Decoded</strong></h4>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/432badd0-31d6-4873-84f5-61b493859217/image.png" alt=""></p>
<ul>
<li>Header</li>
<li>Payload</li>
<li>Signature</li>
</ul>
<h3 id="header">Header</h3>
<pre><code>{
  &quot;alg&quot;: &quot;HS256&quot;,
  &quot;typ&quot;: &quot;JWT&quot;
}</code></pre><p>헤더에서는 해시 알고리즘과 토큰의 타입을 정의 할 수 있습니다.</p>
<ul>
<li>alg : Signature에서 사용하는 알고리즘</li>
<li>typ : 토큰 타입</li>
</ul>
<p><strong>HS256</strong>은 signature에서 HMAC SHA256을 의미하며, 해시 알고리즘 중 하나입니다.
타입의 값이 JWT은 이 토큰을 JWT로 명시해줍니다.</p>
<p>_HS256에 대한 자세한 내용은 <a href="https://auth0.com/docs/get-started/applications/signing-algorithms">auth0 공식 문서</a>를 통해 확인 가능합니다
_</p>
<h3 id="payload">Payload</h3>
<pre><code>{
  &quot;sub&quot;: &quot;1234567890&quot;,
  &quot;name&quot;: &quot;John Doe&quot;,
  &quot;admin&quot;: true
}</code></pre><p><strong>내가 전달하고자하는 데이터</strong>를 포함할 수 있습니다.</p>
<p>데이터 각각의 <strong>Key</strong>를 <strong>claim</strong>이라고 부릅니다.</p>
<p><strong>claim</strong>은 사용자가 원하는 <strong>Key, Value</strong>를 구성할 수 있으며, 
<strong>registered, public, private</strong> 이렇게 3가지 종류가 있습니다.</p>
<ul>
<li><p><strong>registered claim</strong>: 
필수는 아니지만 권장되는 미리 정의된 클레임 세트로 일부는 다음과 같습니다. </p>
</li>
<li><p><em>iss(발급자), exp(만료 시간), sub(주체), aud(대상)*</em> 등.
JWT는 압축되도록 되어 있으므로 클레임 이름은 3자 길이에 불과합니다.</p>
</li>
<li><p><strong>public claim</strong>: 
JWT를 사용하는 사람이 원하는 대로 정의할 수 있습니다. 
그러나 충돌을 피하려면 <a href="https://www.iana.org/assignments/jwt/jwt.xhtml">IANA JSON 웹 토큰 레지스트리</a>에서 정의하거나 충돌 방지 네임스페이스가 포함된 URI로 정의해야 합니다.</p>
</li>
<li><p><strong>private claim</strong>: 
이는 사용에 동의한 당사자 간에 정보를 공유하기 위해 생성된 사용자 지정 클레임이며 등록 클레임이나 공개 클레임이 아닙니다.</p>
</li>
</ul>
<p>Payload는 <strong>Base64Url</strong>로 인코딩되어 <strong>JSON 웹 토큰의 두 번째 부분을 형성</strong>합니다.
서명된 토큰의 경우 이 정보는 변조로부터 보호되지만 누구나 읽을 수 있습니다. </p>
<p>⚡️ <strong>암호화되지 않은 경우</strong> JWT의 <strong>Payload</strong> 혹은 <strong>Header 요소</strong>에 <strong>비밀 정보는 넣으면 안된</strong>다.</p>
<h3 id="signature">Signature</h3>
<pre><code>HMACSHA256(
  base64UrlEncode(header) + &quot;.&quot; +
  base64UrlEncode(payload),
  secret)</code></pre><p>서명 파트는 Base64 인코딩된 <strong>Header &quot;.&quot; Payload</strong> 그리고 <strong>secret</strong>이 필요합니다.
<strong>secret</strong>은 유저가 지정하는 비밀 코드이며, 위와 같이 해싱하면 서명이 완성됩니다.</p>
<h2 id="jwt의-장단점">JWT의 장단점</h2>
<h4 id="📌-장점">📌 장점</h4>
<ol>
<li>Stateless (무상태):
• JWT는 서버에서 세션을 저장할 필요가 없기에, 애플리케이션이 무상태(Stateless)로 작동할 수 있습니다. </li>
<li>다양한 클레임 지원:
•    JWT는 사용자 정보, 권한, 만료 시간 등의 정보를 클레임으로 저장 가능해 유연하게 사용 가능합니다.</li>
<li>Self-contained (자체 포함):
•    JWT 자체에 필요한 정보(사용자 정보, 권한 등)가 포함되어 있어 추가적인 데이터베이스 조회가 필요 없는 경우도 있습니다.</li>
<li>확장성과 분산 시스템에 적합:
• Stateless 인증 방식이기 때문에 여러 서버가 JWT를 동일하게 검증 가능해, 분산 시스템에 유용합니다.</li>
<li>웹 표준 지원:
• JWT는 OIDC(OpenID Connect)와 OAuth 2.0 표준에서 인증 토큰으로 사용되며, 다양한 언어 및 프레임워크에서 지원됩니다.</li>
<li>네트워크 부하가 적다.
http헤더나 url 파라미터를 통해 간단하게 전송되기 때문에 네트워크 부하 현상을 적습니다.</li>
</ol>
<h4 id="📌-단점">📌 단점</h4>
<ol>
<li>보안 취약점:
•    JWT는 정보(클레임)를 디코딩할 수 있어, 민감한 정보는 저장해서는 안 됩니다. 또한 토큰이 탈취되면 무단 사용이 가능합니다.
•    토큰은 Base64 URL로 인코딩되어 누구나 디코딩할 수 있지만, 서명(Signature)은 검증을 위한 것이지 암호화 아니다.</li>
<li>토큰 크기 문제:
•    JWT는 사용자 정보와 서명을 포함해 일반적인 세션 ID보다 크기가 크기에 네트워크 트래픽이 증가할 수 있다.</li>
<li>갱신 및 만료 처리:
•    JWT는 Stateless 특성상 서버에서 토큰 강제 만료 어려워 토큰이 유효한 동안은 사용이 가능하다.
•    이를 해결하기 위해 Refresh Token과 함께 사용하거나, 토큰 블랙리스트를 도입해야 할 수 있다.</li>
<li>무결성 검증 실패 시 문제:
•    토큰의 서명이 올바르지 않으면 전체 토큰이 무효화된다,토큰에 포함된 데이터에 의존하는 경우 애플리케이션에서 예외 처리가 필요하다.</li>
<li>무거운 페이로드 문제:
•    많은 클레임(Claim)을 담으면 페이로드가 커지고, 이는 성능 저하로 이어질 수 있기에 페이로드에는 최소한의 정보만 담는것을 권장한다.</li>
</ol>
<h2 id="정리">정리</h2>
<p><strong>사용 목적</strong> : JWT 토큰은 사용자 인증과 로그인 유지를 위해 쓰인다
<strong>토큰의 구조</strong> : Header, Payload, Signature
Header: 서명 알고리즘 (공개키/개인키 or 비밀키)
Payload : 토큰 정보(대상, 발급시각, 만료시각)
Signature : (Header+Payload) 서명 (인증용)</p>
<hr>
<h3 id="참고-링크">참고 링크</h3>
<p><a href="https://jwt.io/introduction">https://jwt.io/introduction</a>
<a href="https://velog.io/@c65621/Session%EA%B3%BC-JWT">https://velog.io/@c65621/Session%EA%B3%BC-JWT</a>
<a href="https://velog.io/@vamos_eon/JWT%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80-1">https://velog.io/@vamos_eon/JWT%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80-1</a>
<a href="https://velog.io/@chuu1019/%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-JWTJson-Web-Token">https://velog.io/@chuu1019/%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-JWTJson-Web-Token</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[동기(Synchronous)& 비동기( Asynchronous) with javascript]]></title>
            <link>https://velog.io/@yoon_min/%EB%8F%99%EA%B8%B0Synchronous-%EB%B9%84%EB%8F%99%EA%B8%B0-Asynchronous-with-javascript</link>
            <guid>https://velog.io/@yoon_min/%EB%8F%99%EA%B8%B0Synchronous-%EB%B9%84%EB%8F%99%EA%B8%B0-Asynchronous-with-javascript</guid>
            <pubDate>Sun, 20 Oct 2024 06:43:15 GMT</pubDate>
            <description><![CDATA[<h3 id="동기synchronous와-비동기asynchronous-프로그래밍">동기(Synchronous)와 비동기(Asynchronous) 프로그래밍</h3>
<p>프로그래밍을 하다 보면 작업을 처리하는 방식에 따라 <strong>동기(Synchronous)</strong>와 <strong>비동기(Asynchronous)</strong>로 나뉘게 됩니다. 
동기와 비동기을 하단과 같이 알아보도록하겠습니다.</p>
<blockquote>
</blockquote>
<ul>
<li><em><strong>개념</strong></em></li>
<li><em><strong>차이점</strong></em></li>
<li><em><strong>자바스크립트 처리 과정, 관련된 코드를 작성해보겠습니다.</strong></em></li>
</ul>
<h2 id="동기synchronous-프로그래밍">동기(Synchronous) 프로그래밍</h2>
<p>동기 프로그래밍은 코드가 <strong>작성된 순서대로 작업이 처리</strong>되는 방식입니다. 
<strong>각 작업이 끝날 때까지 다음 작업이 대기합니다.</strong> 
** 즉, 하나의 작업이 완료되기 전까지는 다른 작업이 실행되지 않습니다.**
<img src="https://velog.velcdn.com/images/yoon_min/post/2f1426dc-0a0a-4264-9d4e-3ffc6b96de96/image.jpeg" alt="">
<a href="https://www.youtube.com/watch?v=U42qWURR6Gw">그림 출처: 기술노트 유튜브</a></p>
<p><em>프로그램이 순차적으로 진행될때 func()함수를 만나 해당 안에 작업을 모두 완료하고 나오는 모습을 도식화한 이미지이다.</em></p>
<blockquote>
<h4 id="동기의-특징">동기의 특징</h4>
</blockquote>
<ul>
<li>순차적 처리<ul>
<li>작업이 차례대로 처리되며, 이전 작업이 미완료시 다음 작업은 시작되지 안됌</li>
</ul>
</li>
<li>블로킹(Blocking)<ul>
<li>어떤 작업이 오래 걸리면 다른 작업들은 대기 상태 지속</li>
</ul>
</li>
</ul>
<blockquote>
<h4 id="동기-방식-예시code">동기 방식 예시(code)</h4>
</blockquote>
<pre><code>function syncTask() {
    console.log(&quot;첫 번째 작업 시작&quot;);
    for (let i = 0; i &lt; 1000000000; i++) {} // 시간 지연을 위한 작업
    console.log(&quot;첫 번째 작업 완료&quot;);

    console.log(&quot;두 번째 작업 시작&quot;);
    console.log(&quot;두 번째 작업 완료&quot;);
}

syncTask();</code></pre><blockquote>
<h4 id="장점">장점</h4>
</blockquote>
<ul>
<li>코드가 이해하기 쉽고 디버깅이 간편함.</li>
<li>작업이 순차적으로 처리되므로 결과를 예측하기 쉬움.</li>
</ul>
<blockquote>
<h4 id="단점">단점</h4>
</blockquote>
<ul>
<li>시간이 오래 걸리는 작업(예: 파일 읽기, 네트워크 요청)이 있을 경우 전체 애플리케이션이 느려질 수 있습니다.</li>
</ul>
<h2 id="비동기asynchronous-프로그래밍">비동기(Asynchronous) 프로그래밍</h2>
<p>비동기 프로그래밍은 작업이 <strong>병렬로 처리</strong>될 수 있도록 합니다. 
<strong>어떤 작업이 끝나기를 기다리지 않고, 다른 작업을 진행이 가능합니다.</strong> 
자바스크립트에서 비동기는 주로 콜백 함수, Promise, 그리고 async/await 기능을 사용하여 구현됩니다.
<img src="https://velog.velcdn.com/images/yoon_min/post/0e7d3ba6-5f2b-4e7b-812c-9bd5ec39576f/image.jpeg" alt="">
<a href="https://www.youtube.com/watch?v=U42qWURR6Gw">그림 출처: 기술노트 유튜브</a></p>
<p><em>프로그램이 순차적으로 진행될때 func()함수를 만나도 순차적으로 진행되며 callback 함수가 func()에게 확인 요청 전송시 func()함수를 확인 가능하다.</em></p>
<blockquote>
<h3 id="비동기-함수-정리">비동기 함수 정리</h3>
</blockquote>
<ol>
<li>콜백(callback)</li>
<li>Promise</li>
<li>async/await</li>
</ol>
<blockquote>
<h4 id="1-콜백callback-함수-방식의-비동기-처리">1. 콜백(callback) 함수 방식의 비동기 처리</h4>
</blockquote>
<p>콜백(callback) 함수는 작업이 완료된 후 호출되는 함수입니다. 
이는 비동기적으로 실행될 작업을 정의하는 데 유용합니다.</p>
<pre><code>function asyncTask(callback) {
    console.log(&quot;첫 번째 작업 시작&quot;);
    setTimeout(() =&gt; {
        console.log(&quot;첫 번째 작업 완료&quot;);
        callback();
    }, 2000);
}

function secondTask() {
    console.log(&quot;두 번째 작업 시작&quot;);
    consol![](https://velog.velcdn.com/images/yoon_min/post/4bbce093-1d6b-4ae7-b429-958ee92c96b2/image.jpeg)
e.log(&quot;두 번째 작업 완료&quot;);
}

asyncTask(secondTask);</code></pre><p>위 코드에서는 첫 번째 작업이 2초 후에 완료되고, 콜백 함수로 두 번째 작업이 실행됩니다.</p>
<blockquote>
<h4 id="2-promise-방식의-비동기-처리">2. Promise 방식의 비동기 처리</h4>
</blockquote>
<p>Promise는 비동기 작업을 처리하기 위한 또 다른 방법입니다. 
then 메서드를 사용하여 작업 완료 후의 처리를 정의합니다.</p>
<pre><code>function asyncTaskWithPromise() {
    return new Promise((resolve) =&gt; {
        console.log(&quot;첫 번째 작업 시작&quot;);
        setTimeout(() =&gt; {
            console.log(&quot;첫 번째 작업 완료&quot;);
            resolve();
        }, 2000);
    });
}

asyncTaskWithPromise().then(() =&gt; {
    console.log(&quot;두 번째 작업 시작&quot;);
    console.log(&quot;두 번째 작업 완료&quot;);
});</code></pre><blockquote>
<h4 id="3-async와-await방식의-비동기-처리">3. async와 await방식의 비동기 처리</h4>
</blockquote>
<p>async와 await는 Promise를 더 쉽게 사용할 수 있도록 도와주는 문법입니다. 
await 키워드는 비동기 작업이 완료될 때까지 기다렸다가 작업을 계속 진행합니다.</p>
<pre><code>async function asyncTaskWithAwait() {
    console.log(&quot;첫 번째 작업 시작&quot;);
    await new Promise(resolve =&gt; setTimeout(resolve, 2000));
    console.log(&quot;첫 번째 작업 완료&quot;);

    console.log(&quot;두 번째 작업 시작&quot;);
    console.log(&quot;두 번째 작업 완료&quot;);
}

asyncTaskWithAwait();</code></pre><p>위 코드에서는 await를 사용해 첫 번째 작업이 끝날 때까지 기다리고
그 후 두 번째 작업이 실행됩니다.</p>
<blockquote>
<h3 id="비동기의-특징">비동기의 특징</h3>
</blockquote>
<ul>
<li><strong>비순차적 처리</strong><ul>
<li>작업이 병렬로 처리될 수 있으며, 긴 작업이 끝날 때까지 다른 작업이 계속 진행될 수 있습니다.</li>
</ul>
</li>
<li><strong>논블로킹(Non-blocking)</strong><ul>
<li>하나의 작업이 실행되는 동안 다른 작업을 계속 실행할 수 있어 성능 향상이 가능합니다.</li>
</ul>
</li>
</ul>
<blockquote>
<h4 id="장점-1">장점</h4>
</blockquote>
<ul>
<li>네트워크 요청이나 파일 읽기 등 시간이 오래 걸리는 작업이 있을 때 유용합니다.</li>
<li>애플리케이션이 멈추지 않고 계속 작업을 처리할 수 있습니다.</li>
</ul>
<blockquote>
<h4 id="단점-1">단점</h4>
</blockquote>
<ul>
<li>코드가 복잡해지고 가독성이 떨어질 수 있습니다.</li>
<li>작업 순서를 보장하기 위해 추가적인 처리나 구조화가 필요합니다. <pre><code>  (예: 콜백 지옥, Promise 체이닝)</code></pre></li>
</ul>
<blockquote>
<h4 id="동기와-비동기-비교">동기와 비동기 비교</h4>
</blockquote>
<p><strong>동기(Synchronous)</strong></p>
<ul>
<li>작업이 순차적으로 처리됨</li>
<li>블로킹 방식</li>
<li>코드가 이해하기 쉬움    코드가 복잡해질 수 있음</li>
</ul>
<p><strong>비동기(Asynchronous)</strong></p>
<ul>
<li>작업이 병렬로 처리될 수 있음</li>
<li>논블로킹 방식</li>
<li>느린 작업이 전체를 느리게 만듦    성능 최적화에 유리<blockquote>
<h4 id="결론">결론</h4>
</blockquote>
</li>
</ul>
<p>동기와 비동기 프로그래밍은 각각의 장단점을 가지고 있으며, 상황에 맞게 적절히 사용하는 것이 중요합니다. 
자바스크립트에서는 기본적으로 비동기 처리를 많이 사용하지만, 그 과정에서 콜백 함수, Promise, async/await와 같은 다양한 기법을 활용할 수 있습니다. 
성능과 코드 가독성을 고려하면서 적절한 방법을 선택해 구현하는 것이 핵심입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[연결리스트(Linked List)]]></title>
            <link>https://velog.io/@yoon_min/%EC%97%B0%EA%B2%B0%EB%A6%AC%EC%8A%A4%ED%8A%B8Linked-List</link>
            <guid>https://velog.io/@yoon_min/%EC%97%B0%EA%B2%B0%EB%A6%AC%EC%8A%A4%ED%8A%B8Linked-List</guid>
            <pubDate>Thu, 17 Oct 2024 08:32:24 GMT</pubDate>
            <description><![CDATA[<p>학교 알고리즘, 자료구조 수업중 만나게된 연결리스트 네 정체가 궁금하다</p>
<h2 id="연결리스트linekd-list란">연결리스트(Linekd List)란?</h2>
<blockquote>
<p><strong>핵심</strong> | 연결리스트는 연속된 노드(Node)의 연결체이다.</p>
</blockquote>
<h3 id="그럼-노드란">그럼, 노드란?</h3>
<p>연결리스트에서 사용되는 하나의 덩어리로 <strong>데이터</strong>와 <strong>링크</strong>를 담고있는 구조이다.
그림으로 먼저 알아보자
<img src="https://velog.velcdn.com/images/yoon_min/post/d0238a94-a7ca-4f2d-bfe4-4782cc26ea53/image.jpeg" alt=""></p>
<p>여기서 왼쪽에 data, 오른쪽에 next라 적혀있는 부분이 있다.
각 역할을 보면</p>
<ul>
<li>data : Data, value(데이터 값이 들어감)</li>
<li>next : pointer(링크, 포인터 역할로써 다음 노드 주소 저장)</li>
</ul>
<p>코드로 보게된다면</p>
<pre><code>class Node{
    constructor(data){
        this.data=data;
        this.next=null;
       }
 }</code></pre><p>이렇게 구성이되고 next의 연결리스트의 기본 모델로 보게된다면 목적지는 null값이 출력된다.</p>
<blockquote>
<p>연결리스트의 종류</p>
</blockquote>
<ul>
<li>단순 연결리스트(Singly Linked List)</li>
<li>원형 연결리스트(Circular Linked List)</li>
<li>이중 연결리스트(Doubly Linked List)</li>
</ul>
<blockquote>
<p>필요 단어 | Head Node(노드의 시작점), Tail Node(노드의 마지막)</p>
</blockquote>
<p>각 리스트는 그림으로 보게되면 쉽게 비교된다</p>
<h3 id="단순-연결리스트">단순 연결리스트</h3>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/a14fb4b6-45c0-494b-b3e0-eaf36a888a94/image.jpeg" alt="">
<strong>단순연결리스트</strong>는 처음 노드부터(Head Node) next가  경로를 찾아 다음 노드로 향하게되고 마지막 노드(Tail Node)은 None 값을 출력하게된다.</p>
<pre><code># Node 클래스 정의
class Node:
    def __init__(self, data):
        self.data = data  # 노드의 값
        self.next = None  # 다음 노드에 대한 포인터

# LinkedList 클래스 정의
class LinkedList:
    def __init__(self):
        self.head = None  # 첫 번째 노드 (head)

    # 리스트의 끝에 노드 추가
    def append(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
        else:
            last = self.head
            while last.next:
                last = last.next
            last.next = new_node

    # 리스트 출력
    def display(self):
        current = self.head
        while current:
            print(current.data, end=&quot; -&gt; &quot;)
            current = current.next
        print(&quot;None&quot;)

# 연결 리스트 사용 예시
ll = LinkedList()
ll.append(10)
ll.append(20)
ll.append(30)
ll.display()  # 10 -&gt; 20 -&gt; 30 -&gt; None</code></pre><h3 id="원형-연결리스트">원형 연결리스트</h3>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/85d40233-c72d-4fcf-8eeb-8986e855ec89/image.jpeg" alt="">
<strong>원형연결리스트</strong>는 순서대로 next가 다음 노드를 찾아가게되고 마지막 노드(Tail Node)는 처음 노드(Head Nod)로 향하게된다.</p>
<pre><code># Node 클래스 정의
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

# CircularLinkedList 클래스 정의
class CircularLinkedList:
    def __init__(self):
        self.head = None

    # 리스트의 끝에 노드 추가
    def append(self, data):
        new_node = Node(data)
        if not self.head:
            self.head = new_node
            new_node.next = self.head  # 자기 자신을 가리키도록 함
        else:
            current = self.head
            while current.next != self.head:
                current = current.next
            current.next = new_node
            new_node.next = self.head  # 마지막 노드가 다시 첫 번째 노드를 가리킴

    # 리스트 출력
    def display(self):
        current = self.head
        if self.head is None:
            print(&quot;Empty list&quot;)
            return
        while True:
            print(current.data, end=&quot; -&gt; &quot;)
            current = current.next
            if current == self.head:
                break
        print(&quot;(Back to Head)&quot;)

# 원형 연결 리스트 사용 예시
cll = CircularLinkedList()
cll.append(10)
cll.append(20)
cll.append(30)
cll.display()  # 10 -&gt; 20 -&gt; 30 -&gt; (Back to Head)</code></pre><h3 id="이중연결리스트">이중연결리스트</h3>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/b84a270b-069b-46a2-9640-5df8681c7302/image.jpeg" alt="">
<strong>이중연결리스트</strong>는 처음 노드부터(Head Node) next가  경로를 찾아 간 후다음 노드로 향하게되고 마지막 노드(Tail Node)에서 거꾸로 돌아온다.</p>
<pre><code># Node 클래스 정의
class Node:
    def __init__(self, data):
        self.data = data  # 노드의 값
        self.next = None  # 다음 노드에 대한 포인터
        self.prev = None  # 이전 노드에 대한 포인터

# DoublyLinkedList 클래스 정의
class DoublyLinkedList:
    def __init__(self):
        self.head = None  # 첫 번째 노드 (head)

    # 리스트의 끝에 노드 추가 (Append)
    def append(self, data):
        new_node = Node(data)
        if self.head is None:  # 리스트가 비어있을 경우
            self.head = new_node
        else:
            current = self.head
            while current.next:  # 마지막 노드까지 이동
                current = current.next
            current.next = new_node  # 마지막 노드의 다음을 새로운 노드로 설정
            new_node.prev = current  # 새로운 노드의 이전을 현재 노드로 설정

    # 리스트의 앞에 노드 추가 (Prepend)
    def prepend(self, data):
        new_node = Node(data)
        if self.head is None:  # 리스트가 비어있을 경우
            self.head = new_node
        else:
            new_node.next = self.head  # 새로운 노드의 다음을 현재 첫 번째 노드로 설정
            self.head.prev = new_node  # 현재 첫 번째 노드의 이전을 새로운 노드로 설정
            self.head = new_node  # 새로운 노드를 첫 번째 노드로 설정

    # 리스트 출력 (앞에서부터)
    def display_forward(self):
        current = self.head
        while current:
            print(current.data, end=&quot; &lt;-&gt; &quot;)
            current = current.next
        print(&quot;None&quot;)

    # 리스트 출력 (뒤에서부터)
    def display_backward(self):
        current = self.head
        if current is None:
            return
        while current.next:  # 마지막 노드까지 이동
            current = current.next
        while current:  # 마지막 노드에서 첫 번째 노드까지 이동
            print(current.data, end=&quot; &lt;-&gt; &quot;)
            current = current.prev
        print(&quot;None&quot;)

# 이중 연결 리스트 사용 예시
dll = DoublyLinkedList()
dll.append(10)
dll.append(20)
dll.append(30)
dll.prepend(5)  # 리스트의 앞에 추가

print(&quot;Forward traversal:&quot;)
dll.display_forward()  # 5 &lt;-&gt; 10 &lt;-&gt; 20 &lt;-&gt; 30 &lt;-&gt; None

print(&quot;Backward traversal:&quot;)
dll.display_backward()  # 30 &lt;-&gt; 20 &lt;-&gt; 10 &lt;-&gt; 5 &lt;-&gt; None</code></pre><blockquote>
<p>그럼 배열과 연결리스트의 다른점은 뭘까?</p>
</blockquote>
<p>배열과 리스트의 차이점은 그럼으로 보면 더욱 쉽다.</p>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/80f29519-f64a-4dc7-8624-312aada859b8/image.jpeg" alt=""></p>
<p>array즉 배열은 하나의 메모리 안에 순서대로 연결되어있다면</p>
<p>linked list는 각노드가 다음 노드의 주소를 찾아가는 모습을 볼 수 있다.</p>
<blockquote>
<p>그렇다면 언제 배열과 연결리스트는 뭐가달라?</p>
</blockquote>
<p>두 가지의 큰차이점은 하단과 같다.
<strong>array</strong></p>
<ul>
<li>배열은 random access 가능</li>
<li>배열의 n번째 원소 방문시 O(1)시간으로 가능하다<pre><code>  ex) arr[3]</code></pre></li>
<li>원소 삽입&amp;삭제시 일반적으로 O(n)시간 소요</li>
<li><em>Linked List*</em></li>
<li>연결리스트는 random access 불가능</li>
<li>연결리스트의 n번째 원소 방문시 O(n)시간으로 가능
head노드부터 n번째 노드까지 순회하게된다.</li>
<li>배열보다 빨라질수있는 노드 삽입 삭제 가능</li>
</ul>
<blockquote>
<p>공부를 위해 기록해두는 글이다보니 틀린점이나 수정부분이 있으면 댓글 부탁드립니다!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[윤민 Photo Web Project(개인 사진전용 웹)📸]]></title>
            <link>https://velog.io/@yoon_min/%EC%9C%A4%EB%AF%BC-Photo-Web-Project%EA%B0%9C%EC%9D%B8-%EC%82%AC%EC%A7%84%EC%A0%84%EC%9A%A9-%EC%9B%B9</link>
            <guid>https://velog.io/@yoon_min/%EC%9C%A4%EB%AF%BC-Photo-Web-Project%EA%B0%9C%EC%9D%B8-%EC%82%AC%EC%A7%84%EC%A0%84%EC%9A%A9-%EC%9B%B9</guid>
            <pubDate>Mon, 22 Jul 2024 10:50:01 GMT</pubDate>
            <description><![CDATA[<h1 id="안녕하세요-윤민입니다">안녕하세요 윤민입니다.</h1>
<p>현재 저는 프론트엔드 개발자겸, 윤민필름으로 취미 사진가 활동을하고있습니다.</p>
<p>이 웹 프로젝트를 시작하게된 이유는 <strong>나만의 자율성</strong>을 확보하고자하는 마음이었습니다.</p>
<p>늘 좀 더 이쁘고 감성지게 꾸미고싶은 SNS 피드지만,
SNS의 특성상 한번 올리게되면 쉽게 피드수정이 불가능하다는 점과 정해진 규격으로 원하는 느낌을 전달하기가 어렵다는게 큰 단점이었습니다.</p>
<p>그래서 그동안 공부해왔던 지식들을 가지고 스스로 <strong>웹 사이트 자체를 제작</strong>하고자 합니다. 
해당 자료들을 기록해나가며 제 소중한 순간들을 하나씩 남겨 보고자합니다.</p>
<blockquote>
<p><strong>&#39;같이의 가치를&#39;</strong> 
해당 프로젝트에 대한 첨언은 환영입니다.✨
많은 시행착오들을 블로그에 적어 내려갈 예정이니 혹시 지나가다 관심이 있으면 한번씩 봐주시면 감사하겠습니다 :)</p>
</blockquote>
<blockquote>
<p>하단 링크를 통해 제 활동을 확인 할 수 있습니다.
📸 <a href="https://www.instagram.com/yoonmin_film/">@yoonmin_film SNS</a> 📸
<img src="https://velog.velcdn.com/images/yoon_min/post/ca9fba2d-0b61-47fe-8c5c-ad50304dcbb3/image.png" alt=""></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MIT 무료 강의 | CS 무료강의 추천] The Missing Semester of Your CS Education]]></title>
            <link>https://velog.io/@yoon_min/MIT-%EB%AC%B4%EB%A3%8C-%EA%B0%95%EC%9D%98-CS-%EB%AC%B4%EB%A3%8C%EA%B0%95%EC%9D%98-%EC%B6%94%EC%B2%9C-The-Missing-Semester-of-Your-CS-Education</link>
            <guid>https://velog.io/@yoon_min/MIT-%EB%AC%B4%EB%A3%8C-%EA%B0%95%EC%9D%98-CS-%EB%AC%B4%EB%A3%8C%EA%B0%95%EC%9D%98-%EC%B6%94%EC%B2%9C-The-Missing-Semester-of-Your-CS-Education</guid>
            <pubDate>Fri, 14 Jun 2024 06:39:40 GMT</pubDate>
            <description><![CDATA[<p>매사추세츠 공과대학교 (Massachusetts Institute of Technology)
해당 수업은 MIT 방학 기간에 진행했던 수업입니다.</p>
<p>현재 무료로 강의를 청강할 수 있으며 비전공자로 시작해 현재 컴공 CS 수업을 듣고있지만 
독학으로 시작한 비전공자는 언제나 스스로 공부하는 것이 가장 중요한 것을 알기에,,,🫠
해당 수업을 시작하게 되었습니다.</p>
<p>인터넷에서 많은 분들이 추천했던 강의이며 무려 MIT 수업이라니,,,🫨</p>
<p><a href="https://missing.csail.mit.edu/">The Missing Semester of Your CS Education</a> [ English ver.]</p>
<p><a href="https://missing-semester-kr.github.io/">The Missing Semester of Your CS Education</a> [ Korea ver.]
<img src="https://velog.velcdn.com/images/yoon_min/post/b2a0f42e-3a57-44a5-b894-b7f2d80acb63/image.png" alt=""></p>
<p>미국 대학은 보통 여름방학이 길고 겨울방학이 1달정도로 짧은데,
 MIT에서는 1월에 쉬는 약 한 달간의 시간을 IAP(Independent Activities Period) 라고 부릅니다. </p>
<p>CS분야 코스중 웹사이트 만들어보기, 포케 게임 머신 만들기 등 여러 활동이 있으며 해당 수업도 IAP 수업중 하나입니다. 
대충 한 달 안에 대부분 둘러볼 수 있는 코스입니다!</p>
<p>Shell, ,Git, Vim 등 CS 과목 수업에서 제대로 가르쳐주지 않아 상황에 맞춰 필요한 것만 공부하며 해결했던 분들에게 알맞은 수업일꺼 같습니다.</p>
<hr>
<p><a href="https://www.youtube.com/watch?v=piSLobJfZ3c">MIT 챌린지 관련 영상</a> | <a href="https://jesus-never-fail.tistory.com/46">출처 : ( 샤니의 공부로그 )</a>
✨추가로 MIT에서 진행하는 챌린지도 있다고합니다-! 
관심있는 분들으 확인해보셔도 좋을꺼 같아요🥰
The Missing Semester of Your CS Education 수업을 다 듣게되면  저도 샤니님의 글을 보고 한 단계씩 따라가며 공부해볼까합니다-!</p>
<p>이 글을 보시는 분들에게도 큰 도움이 되었으면 좋겠네🍀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[블록체인 Dapp(디앱)이란?]]></title>
            <link>https://velog.io/@yoon_min/%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-Dapp%EB%94%94%EC%95%B1%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@yoon_min/%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-Dapp%EB%94%94%EC%95%B1%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Fri, 12 Apr 2024 03:37:22 GMT</pubDate>
            <description><![CDATA[<h1 id="디앱dapp이란">디앱(Dapp)이란?</h1>
<p><strong>디앱(DApp, Decentralized Application)</strong>이란 <strong>탈중앙화 애플리케이션</strong>의 약자로 _<strong>탈중앙화된 블록체인 플랫폼을 기반으로 작동하는 앱</strong>_을 말합니다. 
예를 들어 안드로이드 같은 <strong>스마트폰 운영체제</strong>가 <strong>이더리움과 같은 &#39;플랫폼&#39;</strong>이라면 <strong>안드로이드용 스마트폰 앱</strong>이 <strong>디앱</strong>이라고 할 수 있습니다.</p>
<h4 id="출처googleplaystore-andriod"><img src="https://velog.velcdn.com/images/yoon_min/post/965dbf33-3ed8-42b6-a774-ca102fe4859f/image.png" alt="">[출처]googleplaystore, andriod</h4>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/295aac53-ce45-41fe-9765-519b829217b4/image.png" alt=""> </p>
<h4 id="출처ethereum-pixabay">[출처]ethereum, pixabay</h4>
<p>기존에는 ‘중앙화 애플리케이션’에서는 중앙 서버의 데이터베이스에 정보를 저장하였습니다. 또한 그 운용 역시 중앙 서버를 통해 이루어 졌습니다. </p>
<p>하지만 디앱이 나오게되면서 <strong>중앙 서버가 가졌던 신뢰와 권한</strong>을 <strong>블록체인 네트워크에 참여</strong>하는 <strong>수많은 컴퓨터가 나눠 가지게</strong>되면서 중<strong>앙 서버의 역할을 대체</strong>했습니다.</p>
<h2 id="디앱-출현-배경">디앱 출현 배경</h2>
<p>블록체인 기술의 발달에 따라 은행, 기업, 기관, 정부 등의 중앙 집중적인 제 3자의 보증 없이도 당사자끼리 직접 신뢰할 수 있게 해주는 블록체인 위에 스마트 컨트랙트를 추가하여 실행할 수 있게 해주는 DApp이라는 개념이 출현하였습니다.</p>
<h3 id="스마트-컨트랙트-도입-이후">스마트 컨트랙트 도입 이후</h3>
<p>디앱이란 개념은 본격적으로 사용되어 하단과 같이 발전했습니다.</p>
<ul>
<li>DAO(탈중앙화된 자율조직)</li>
<li>DeFi(탈중앙화된 금융)</li>
<li>NFT 마켓플레이스</li>
<li>P2E(Play-to-Earn)</li>
</ul>
<p>가상자산 시장에서 가장 많이 사용되는 디앱은 <strong>탈중앙화 거래소(DEX, Decentralized Exchange</strong>)입니다.
탈중앙화 거래소는 중간 매개자 없이 스마트 컨트랙트를 통해 유저와 유저가 직접 거래하게 됩니다. 
<em>(모든 신뢰와 권한은 수 많은 컴퓨터가 나눠가지게되어서 가능하게 되었습니다.)</em></p>
<h3 id="dapp-설계-및-구현-webfeatnodejs">Dapp 설계 및 구현 [WEB(feat.node.js)]</h3>
<p>김순곤님이 작성한 논문 DApp 개발을 위한 블록체인 2.0 이더리움 플랫폼 분석 연구 과정 일부를 설명을 위해 발췌하였다.</p>
<p>전체적인 DApp의 시스템은 Back-End 시스 템 사양으로 Solidity, Truffle FrameWork, API, 라이브러리 등은 계약을 작성하는 곳에서 필요하다.</p>
<p>제품 구매, 등록, SOT 환전, 암호 화 폐 상품운영 등 Back-end에서 모든 것을 구현 한 후에 JavaScript를 이용하여 UI와 연동하여 E-bay같은 시스템을 완성해 진행하였다.
<img src="https://velog.velcdn.com/images/yoon_min/post/893d6e54-dbdc-4abc-9e5d-5db888fdae78/image.png" alt=""> [출처] : <a href="https://scienceon.kisti.re.kr/srch/selectPORSrchArticle.do?cn=JAKO201810866006677">DApp 개발을 위한 블록체인 2.0 이더리움 플랫폼 분석 연구(김순곤) 일부 발췌</a></p>
<p>실제로 그림2와 같은 DApp 웹구조에서 Front-end에는 HTML폼이 있어서 사용자가 이 름, 가격, 이미지, 설명 등의 상품 정보를 직접 입력하고 저장 가능하다.</p>
<p>상품 정보와 이미지를 IPFS에 올리고 그 링크를 연결한다. 
또한 컨트랙 트를 호출하여 IPFS의 상품 링크를 블록체인에 저장한다. 
상품을 블록체인에 올리는데 성공하면 컨트랙트가 이벤트를 하나 생성하고 이 이벤트는</p>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/34b862d9-8a4a-473f-a624-9632f36049ad/image.png" alt=""> [출처] : <a href="https://scienceon.kisti.re.kr/srch/selectPORSrchArticle.do?cn=JAKO201810866006677">DApp 개발을 위한 블록체인 2.0 이더리움 플랫폼 분석 연구(김순곤) 일부 발췌</a></p>
<p>node.js 서버에서 이러한 이벤트를 모니터링 하고 컨트랙트가 이벤트를 생성하면 이벤트의 내 용을 읽어서 Mongo DB 데이터베이스에 상품 정보를 입력한다.</p>
<p>일반적인 제품의 유통 구성도 는 그림 3과 같이 되어 있으며 본 논문에서도 이를 기반으로 주문, 결제, 제품 생산들 모두에 블록을 이용하여 사용하는 방식으로 바꿔서 기존 유통업 어플과의 차이점을 두었다.</p>
<h2 id="장점">장점</h2>
<h4 id="1--보안성-및-투명성">1.  <strong>보안성 및 투명성</strong></h4>
<p>데이터를 중앙화 된 데이터베이스에 기존의 앱과 다르게 중요 데이터를 탈중앙화된 블록체인에 저장하여 데이터의 보안성과 투명성을 높일 수 있습니다. </p>
<h4 id="2-인센티브">2) <strong>인센티브</strong></h4>
<p>사용자의 참여 및 행동에 따라 스마트 계약을 통해 코인을 제공할 수 있습니다. 
(다양한 사람들이돈을 받고 계약 관리자 기능을 가능케한다. 지금의 블록체인과 같음)</p>
<h4 id="3-거버넌스-참여">3) <strong>거버넌스 참여</strong></h4>
<p>프로젝트의 방향성에 대한 제안 및 공동 의사 결정을 코인 보유자들이 투표로 결정할 수 있습니다.</p>
<h2 id="단점">단점</h2>
<h4 id="1-느린-속도">1) 느린 속도</h4>
<p>블록체인의 블록 생성 시간에 따라 속도가 달라지지만 일반적인 앱보다는 느립니다. 
또한 디앱의 사용량이 증가하여 블록체인의 처리량을 초과하면 데이터를 기록하기 위한 시간이 더욱 증가합니다.</p>
<h4 id="2-높은-비용">2) 높은 비용</h4>
<p>블록체인의 유지 비용은 일반적인 중앙화 데이터베이스보다 비쌉니다. 
따라서 블록체인에 데이터를 기록하는 데는 비용이 필요합니다. </p>
<h4 id="3-불편한-사용자-경험">3) 불편한 사용자 경험</h4>
<p>블록체인을 사용하는 디앱의 근본적 문제는 사용자 경험 문제로 귀결됩니다. 
사용자는 느린 속도로 인해 즉각적으로 디앱과 상호작용 할 수 없습니다. 
또한 높은 거래 비용은 사용자가 지불해야 합니다.</p>
<h2 id="최종-생각">최종 생각</h2>
<p>보안 부분에있어서 탈중앙화는 놀라운 변화라 생각합니다.
하지만 아직 일반 사용자들에게 있어서 아직 개선되야할 부분이 많이 존재합니다.</p>
<p>정말 좋은 보안 기술이지만, 그에 따르는 단점들을 최적화 및 해결이 되어야 대중화 가능한 기술이지 않을까 생각합니다.</p>
<p>인공지능 발전을 통해 점점 가속화되는 하드웨어 기술들이 블록체인 기술의 비용 및 속도에 대한 일부분을 해결해 줄수있지 않을까 생각합니다.</p>
<p>출처: <a href="https://upbitcare.com/academy/education/blockchain/239">업비트 투자자보호센터</a>,  <a href="https://scienceon.kisti.re.kr/srch/selectPORSrchArticle.do?cn=JAKO201810866006677">DApp 개발을 위한 블록체인 2.0 이더리움 플랫폼 분석 연구(김순곤)</a>, </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ERC-20 분석(Block chain)]]></title>
            <link>https://velog.io/@yoon_min/ERC-20-%EB%B6%84%EC%84%9DBlock-chain</link>
            <guid>https://velog.io/@yoon_min/ERC-20-%EB%B6%84%EC%84%9DBlock-chain</guid>
            <pubDate>Wed, 03 Apr 2024 15:39:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>블록체인 기술에 처음으로 들어오게되면 마주하게되는 ERC-20 ERC-20이란 뭘까?</p>
</blockquote>
<h1 id="erc-20란">ERC-20란?</h1>
<p>ERC 20(Ethereum Request for Comment 20)의 약자이다. 
즉, 이더리움 프로토콜 개발을 위한 표준을 제안하는 문서의 집합이다.</p>
<p>ERC-20 외에 ERC-223, 621, 827등 여러 다른 종류가 있다.
위에 적힌 이들은 대부분은 ERC-20을 기반으로 뻗어나간 형태이기에 ERC-20을 분석해 보도록 하겠다.</p>
<p><a href="http://wiki.hash.kr/index.php/ERC#ERC-20"><strong><em>그 외에 많은 ERC-000가 있으니 여기를 클릭해 확인해보자</em></strong></a></p>
<p><em><strong>ERC를 알기 위핸 먼저 Ethereum, EIPs를 알아야한다</strong></em></p>
<h2 id="이더리움ethereum이란">이더리움(Ethereum)이란?</h2>
<blockquote>
<p>이더리움은 자체 블록체인을 기반으로 탈중앙화된 애플리케이션들이 작동할 수 있도록 고안된 하나의 플랫폼 네트워크이다.</p>
</blockquote>
<p>디앱(Decentralized Application의 약자 플랫폼 코인 위에 작동되는 탈중앙화 분산 애플리케이션)은 이러한 이더리움 플랫폼 상에서 스마트 계약을 이용하여 쉽고 빠르게 토큰을 발행할 수 있다. </p>
<p>이더리움 블록체인에서는 이더(ETH)가 사용되고, 이더리움 블록체인 상의 디앱은 또 다른 다양한 분야에 적용될 수 있는 각각의 솔루션으로 그에 맞는 토큰을 발행하게된다.</p>
<p><em>이때 발행된 토큰은 독자적인 토큰인 듯 하지만 실제로는 이더리움 생태계에서 호환 및 사용 가능하다.</em></p>
<p><img src="https://velog.velcdn.com/images/yoon_min/post/1b74277b-7d06-4246-b2bc-32d837f76d89/image.png" alt="이더리움 로고"><em>[출처] 이더리움</em></p>
<h2 id="그렇다면-eips란">그렇다면 EIPs란?</h2>
<blockquote>
<p>EIPs(Ethereum Improvement Proposals)는 이더리움 개선 제안을 의미한다.
EIP는 이더리움 커뮤니티에 정보를 제공하거나 이더리움 프로세스 , 환경에 대한 새로운 시능을 설명하는 설계 문서이다.</p>
</blockquote>
<h2 id="좋다-그럼-다시-그럼-erc로-돌아가보자">좋다 그럼 다시 그럼 ERC로 돌아가보자.</h2>
<p>먼저 ERC-20에서 20은 리퀘스트 숫자이다.
ERC-20은 이더리움 블록체인 네트워크에서 정한 표준 토큰 스펙이다.</p>
<p>이더리움 블록체인을 사용하여 생성된 <strong>대체 가능한 토큰</strong>에 대한 기술 표준이고, ERC-721의 <strong>대체 불가능 토근(NFT)</strong>와는 다릅니다.</p>
<h3 id="erc-20의-역사">ERC-20의 역사</h3>
<p>ERC-20은 이더리움 블록체인의 스마트 계약 표준에 대한 필요성을 해결이 필요했다.
이에 개발자 Fabian Vogelsteller가 2015년에 Vogelsteller는 프로젝트의 Github 페이지를 통해 이더리움 의견 요청(ERC)를 제안서를 제출했다.
20번째 comment이므로 ERC-20이라는 명칭이 지정되었다.</p>
<h2 id="특징">특징</h2>
<h3 id="규칙으로는-하단과-같이-6가지-함수가있다">규칙으로는 하단과 같이 6가지 함수가있다.</h3>
<pre><code>totalSupply()
transfer(address _to, uint256 _value)
balanceOf(address _owner)
transferFrom(address _from, address _to, uint256 _value)
approve(address _spender, uint256 _value)
allowance(address _owner, address _spender)</code></pre><h4 id="자세히-하나씩-살펴보자">자세히 하나씩 살펴보자</h4>
<ul>
<li>totalSupply ()<ul>
<li>총 발행량<pre><code>totalSupply()</code></pre></li>
</ul>
</li>
<li>transfer() 함수<ul>
<li>송금</li>
<li>_to에게 _value만큼 토큰을 전송</li>
</ul>
</li>
</ul>
<pre><code>transfer(address _to, uint256 _value)</code></pre><ul>
<li>balanceOf () 함수<ul>
<li>잔액 </li>
<li>_owner가 소지하고 있는 토큰의 수량 확인<pre><code>balanceOf(address _owner)</code></pre></li>
</ul>
</li>
<li>transferFrom () 함수<ul>
<li>사용자는 송금 기능을 사용하여 측정 토큰을 사용자가 전송및 교환 가능</li>
<li>_from으로부터 _to에게 _value만큼 토큰 전달<pre><code>transferFrom(address _from, address _to, </code></pre></li>
</ul>
</li>
<li>approve () 함수<ul>
<li>승인 기능 위조 토큰을 만드는 것을 방지 </li>
<li>_spender에게 _value만큼 토큰의 권한을 부여</li>
<li>토큰의 총 발행 양을 확인해 트랜잭션을 허용하거나 거부하고, 최대 토큰 수를 유지 관리하고 어떤 지갑에 어떤 토큰이 있는지 추적한다.<pre><code>approve(address _spender, uint256 _value)</code></pre></li>
</ul>
</li>
<li>allowance ()<ul>
<li>허용 기능 누군가가 시스템을 속이고 본인이 소유한 것보다 더 많은 토큰을 보내고자 하는 상황일 때, 허용 기능을 통해사용자들은 자신들이 가진 것보다 더 많은 토큰을 보낼 수 없다.</li>
<li>_owner가 _spender에게 권한을 부여한 토큰 수량 확인</li>
<li>거래가 이루어지면 취소된다. </li>
<li>모든 트랜잭션은 트랜잭션이 실행되기 전에 유효한지 이중으로 확인된다.<pre><code>allowance(address _owner, address _spender)</code></pre><h4 id="선택적-규칙">선택적 규칙</h4>
<blockquote>
<p>선택적 규칙은 모든 사람의 편의를 위해 존재한다. 
이 옵션들은 토큰, 심볼 및 10진법 규칙이다. 
토큰의 이름은 사람들이 어떤 토큰을 다루고 있는지 알 수 있도록 도와주는 역할을 한다. </p>
</blockquote>
</li>
</ul>
</li>
</ul>
<p>예를들어 싸이클린의 ERC-20 토큰 이름은 CyClean이다. 
싸이클린의 심볼은 CCL이며, Dobitrade와 HitBTC에서 이러한 방식으로 표시되어 있다. 
마지막으로 10진법 규칙이 있다. 
이는 토큰을 몇 개의 10진수로 나눌 수 있는지를 알려준다. 
싸이클린 토큰은 18개의 소수점으로 나눌 수 있다. 
10진수 값이 0이면 토큰이 분리되지 않는다.</p>
<h2 id="코인">코인</h2>
<blockquote>
<p>이더리움 네트워크에서 이더는 자유롭게 거래된다. 
이더는 모든 종류의 거래에 사용되는 토큰이나 암호화폐로 간주한다. </p>
</blockquote>
<p>이더리움 네트워크에는 이더와 교환 가치에 기반해 사용되는 다른 토큰들도 존재한다. 
이더리움 블록체인에서 또 다른 토큰을 실행하려면 ERC-20이라고 불리는 스마트 계약에서 코드나 기술 표준을 설정해야 한다.</p>
<h2 id="문제점">문제점</h2>
<p>스마트 계약 결제 수단으로 ERC-20 토큰을 사용할 경우 토큰이 파괴되는 현상이 나타난다.</p>
<p>ERC-20 토큰은 다른 이더리움 주소 사이에서 이동할 수 있는데, 
그러나 이더리움 블록체인의 일반적인 에테르 거래는 근본적으로 다르다. </p>
<p>트랜잭션 양 및 추가 정보에 대한 트랜잭션 필드, 정보 필드의 필드를 포함하는 이더넷을 전송한다. 그러나 이때 토큰 트랜잭션에서 보낼 토큰 수에 대한 정보는 두 번째 필드에 있다.</p>
<p>따라서 양쪽의 당사자의 주소 간에 전송이 가능하지만 다른 스마트 계약 기능의 트리거로 토큰을 사용하면 작동하지 않는다. </p>
<p>사용자가 트랜잭션을 스마트 계약으로 보내고 스마트 계약이 인식되지 않으면 트랜잭션의 에테르가 영구적으로 손실된다.
이더로만 결제해야 하는 버그가 존재하게 되어, 현재까지 이 버그 때문에 약 3백만 달러어치의 토큰이 사라져버렸다고 한다.</p>
<p><a href="http://wiki.hash.kr/index.php/ERC-20">[출처]해시넷</a>, <a href="https://rainbow96bear.tistory.com/entry/Solidity-ERC20%EC%9D%B4%EB%9E%80-ERC20-%ED%86%A0%ED%81%B0-%EB%B0%9C%ED%96%89">무지개곰 티스토리 블로그</a>, <a href="https://www.investopedia.com/news/what-erc20-and-what-does-it-mean-ethereum/">Investopedia</a>, <a href="https://coinmarketcap.com/academy/glossary/ethereum-request-for-comment-erc">coinmarketcap</a></p>
]]></description>
        </item>
    </channel>
</rss>