<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>코린이_성장일기.log</title>
        <link>https://velog.io/</link>
        <description>곽숭아_놀이터</description>
        <lastBuildDate>Sat, 21 Mar 2026 05:51:43 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>코린이_성장일기.log</title>
            <url>https://velog.velcdn.com/images/wjdtor_2/profile/260e4c8f-d20d-4bc3-880a-d8e56dcdf8b8/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 코린이_성장일기.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/wjdtor_2" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[🌤️ 스트레스 예측 AI & 웹앱 "마음날씨" 제작기 (대회 3위)]]></title>
            <link>https://velog.io/@wjdtor_2/%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%8A%A4-%EC%98%88%EC%B8%A1-AI-%EC%9B%B9%EC%95%B1-%EB%A7%88%EC%9D%8C%EB%82%A0%EC%94%A8-%EC%A0%9C%EC%9E%91%EA%B8%B0-%EB%8C%80%ED%9A%8C-3%EC%9C%84</link>
            <guid>https://velog.io/@wjdtor_2/%EC%8A%A4%ED%8A%B8%EB%A0%88%EC%8A%A4-%EC%98%88%EC%B8%A1-AI-%EC%9B%B9%EC%95%B1-%EB%A7%88%EC%9D%8C%EB%82%A0%EC%94%A8-%EC%A0%9C%EC%9E%91%EA%B8%B0-%EB%8C%80%ED%9A%8C-3%EC%9C%84</guid>
            <pubDate>Sat, 21 Mar 2026 05:51:43 GMT</pubDate>
            <description><![CDATA[<p>이번 부트캠프에서 해커톤을 진행함.
스트레스 수치 예측AI 모델링 이 주제였음.</p>
<p><strong>일정 :</strong> 3/13(월) ~ 3/20(금)
<strong>발표 일정:</strong> 3/20(금) </p>
<p>데이터 분석부터 모델링, 
그리고 이를 활용한 <strong>FastAPI + JS 웹 서비스</strong>까지 
구축한 전 과정을 정리하려고한다.</p>
<p><strong>성과 :</strong> 리더보드 MAE 0.140.26(최종 3등)</p>
<hr>
<h2 id="📌-프로젝트-개요">📌 프로젝트 개요</h2>
<p>건강 지표를 입력받아 <strong>0~1 사이의 스트레스 점수</strong>를 예측하는 회귀 모델 + 웹앱 제작.
7조 개발바닥 (조장 곽지은, 조원 정현희)</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td>데이터</td>
<td>train 3,000행 / test 3,000행 / 17개 컬럼</td>
</tr>
<tr>
<td>평가 지표</td>
<td>MAE (낮을수록 좋음)</td>
</tr>
<tr>
<td>최종 결과</td>
<td><strong>MAE 0.14026 · 리더보드 3등</strong></td>
</tr>
<tr>
<td>백엔드</td>
<td>FastAPI + AutoGluon</td>
</tr>
<tr>
<td>프론트</td>
<td>Vanilla JS (마음날씨 웹앱)</td>
</tr>
</tbody></table>
<hr>
<h2 id="1상식의-실패--가설이-먼저-틀렸다">1.상식의 실패 — 가설이 먼저 틀렸다</h2>
<p>컬럼 이름을 보고 가설을 먼저 세웠다.</p>
<table>
<thead>
<tr>
<th>가설</th>
<th>예상</th>
<th>실제 데이터</th>
<th>결과</th>
</tr>
</thead>
<tbody><tr>
<td>수면 장애 → 스트레스 높음</td>
<td>✅</td>
<td>sleep difficulty 0.488 / oversleeping 0.453</td>
<td>✅ 부분 일치 (과수면은 오히려 낮음)</td>
</tr>
<tr>
<td>흡연 → 스트레스 높음</td>
<td>✅</td>
<td>current-smoker 0.495 / ex-smoker 0.471</td>
<td>✅ 일치</td>
</tr>
<tr>
<td>기저질환 → 스트레스 높음</td>
<td>✅</td>
<td>질환 있음 0.495 / 결측 0.465</td>
<td>✅ 일치 + 결측이 별도 의미 발견</td>
</tr>
<tr>
<td>근무시간 → 스트레스 높음</td>
<td>✅</td>
<td>상관계수 0.18 (수치형 중 최고) but 결측 34%</td>
<td>⚠️ 결측 때문에 최종 제거</td>
</tr>
<tr>
<td>활동량 많음 → 스트레스 낮음</td>
<td>✅</td>
<td>세 그룹 간 차이 0.014에 불과</td>
<td>❌ 불일치</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>핵심 인사이트</strong>: 수면·흡연처럼 직관적인 변수가 생각보다 상관이 낮았고(p=0.082),
오히려 혈압·병력 같은 <strong>생체 복합 신호</strong>가 더 유의미했다. (p&lt;0.05)
→ 생활습관 변수 노이즈 배제, 생체 지표 중심으로 피벗했다.</p>
</blockquote>
<hr>
<h2 id="2주요-컬럼--처리-결정">2.주요 컬럼 &amp; 처리 결정</h2>
<p>결측률이 높은 컬럼이 핵심 판단 포인트였다.</p>
<table>
<thead>
<tr>
<th>컬럼</th>
<th>결측률</th>
<th>처리 결정</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td><code>mean_working</code></td>
<td>34.4%</td>
<td><strong>제거</strong></td>
<td>상관 0.18로 높지만 결측이 너무 많음</td>
</tr>
<tr>
<td><code>medical_history</code></td>
<td>43.0%</td>
<td><strong>&#39;None&#39; 문자열 유지</strong></td>
<td>결측 그룹과 질환 없음 그룹이 다른 의미</td>
</tr>
<tr>
<td><code>family_medical_history</code></td>
<td>49.5%</td>
<td><strong>&#39;None&#39; 문자열 유지</strong></td>
<td>동일</td>
</tr>
<tr>
<td><code>edu_level</code></td>
<td>20.2%</td>
<td><strong>제거</strong></td>
<td>성능 기여 미미</td>
</tr>
<tr>
<td><code>gender</code></td>
<td>0%</td>
<td><strong>제거</strong></td>
<td>F 0.486 / M 0.478 — 차이 없음</td>
</tr>
<tr>
<td><code>bone_density</code></td>
<td>0%</td>
<td><strong>제거</strong></td>
<td>상관계수 -0.023, 노이즈</td>
</tr>
<tr>
<td><code>height</code>, <code>weight</code></td>
<td>0%</td>
<td><strong>제거 후 BMI로 대체</strong></td>
<td>파생변수로 통합</td>
</tr>
<tr>
<td><code>systolic/diastolic_bp</code></td>
<td>0%</td>
<td><strong>제거 후 MAP·맥압으로 대체</strong></td>
<td>파생변수로 통합</td>
</tr>
</tbody></table>
<hr>
<h2 id="3피처-엔지니어링">3.피처 엔지니어링</h2>
<h3 id="파생변수-3종">파생변수 3종</h3>
<pre><code class="language-python">def engineering_refined(df):
    df = df.copy()

    # BMI: 키·몸무게를 하나의 체질량 지표로
    df[&#39;bmi&#39;] = df[&#39;weight&#39;] / (df[&#39;height&#39;] / 100) ** 2

    # 맥압: 심혈관 긴장 지표
    df[&#39;pulse_pressure&#39;] = (
        df[&#39;systolic_blood_pressure&#39;] - df[&#39;diastolic_blood_pressure&#39;]
    )

    # 평균동맥압(MAP): 실제 장기에 전달되는 혈압
    df[&#39;MAP&#39;] = (
        df[&#39;systolic_blood_pressure&#39;] + 2 * df[&#39;diastolic_blood_pressure&#39;]
    ) / 3

    # 범주형 결측 → &#39;None&#39; 문자열 (정보로 활용)
    fill_cols = [
        &#39;medical_history&#39;, &#39;family_medical_history&#39;,
        &#39;smoke_status&#39;, &#39;activity&#39;, &#39;sleep_pattern&#39;
    ]
    for col in fill_cols:
        if col in df.columns:
            df[col] = df[col].fillna(&#39;None&#39;).astype(str)

    return df</code></pre>
<blockquote>
<p>혈압 수치를 날 것으로 넣는 것보다, 두 값의 관계를 표현하는 MAP·맥압이 더 유의미한 신호를 줬다.
<code>medical_history</code>는 결측(0.465)과 질환 있음(0.495)이 다른 그룹이므로 절대 드랍하면 안 된다.</p>
</blockquote>
<hr>
<h2 id="4모델-실험-여정">4.모델 실험 여정</h2>
<h3 id="전략-1--svr--robustscaler-초기-실험">전략 1 — SVR + RobustScaler (초기 실험)</h3>
<p>데이터에 노이즈가 심하다는 판단 하에 SVR의 마진(Margin) 특성을 활용했다.</p>
<p>파이프라인: <code>Raw Data → RobustScaler → 5-Fold CV → Seed 42/121/2026 평균 → MAE 0.14</code></p>
<ul>
<li><strong>RobustScaler</strong>: 이상치가 많은 혈압·콜레스테롤 등에 중앙값 기반 스케일링 적용</li>
<li><strong>Seed 앙상블</strong>: 3개 난수로 모델을 반복 학습 후 평균 — 변동성 통제</li>
</ul>
<p>SVR의 커널 트릭(Kernel Trick)으로 선형 분석으로는 잡을 수 없는 <strong>변수 간 비선형 폭발 지점</strong>을 포착했다.</p>
<hr>
<h3 id="전략-2--autogluon-v3-최종-제출-mae-014026">전략 2 — AutoGluon v3 (최종 제출, MAE 0.14026)</h3>
<pre><code class="language-python">predictor = TabularPredictor(
    label=&#39;stress_score&#39;,
    eval_metric=&#39;mae&#39;,
    path=&#39;../models/ag_strategy_v3/&#39;,
    problem_type=&#39;regression&#39;,
)

predictor.fit(
    train_data=train,
    presets=&#39;best_quality&#39;,   # LightGBM·XGBoost·CatBoost·NN 앙상블
    num_bag_folds=10,         # 10-fold 배깅
    num_stack_levels=2,       # 2단 스태킹
    time_limit=3600 * 1.5,
    excluded_model_types=[&#39;KNN&#39;],
    refit_full=True,
    set_best_to_refit_full=True,
)</code></pre>
<p>SVR보다 AutoGluon 앙상블이 더 안정적인 점수를 냈고, 이를 최종 제출했다.</p>
<hr>
<h3 id="전략-3--autogluon-v5--pseudo-labeling-추가-실험">전략 3 — AutoGluon v5 + Pseudo Labeling (추가 실험)</h3>
<p>v3 결과물을 활용해 테스트 데이터에 의사 라벨을 붙여 학습 데이터를 늘리는 시도를 했다.</p>
<pre><code class="language-python"># v3 예측 결과를 pseudo label로 활용
test_pseudo = test.copy()
test_pseudo[&#39;stress_score&#39;] = v3_result[&#39;stress_score&#39;]

# 원본 100% + Pseudo 40%만 샘플링 (과적합 방지)
test_pseudo_sampled = test_pseudo.sample(frac=0.4, random_state=42)
combined_train = pd.concat([train, test_pseudo_sampled]).reset_index(drop=True)

predictor.fit(
    train_data=combined_train,
    presets=&#39;high_quality&#39;,   # best → high로 학습 시간 40% 단축
    num_bag_folds=8,
    num_stack_levels=1,       # 과적합 방지
    time_limit=3600 * 1,
    hyperparameters={
        &#39;CAT&#39;: {}, &#39;XGB&#39;: {}, 
        &#39;GBM&#39;: {&#39;num_boost_round&#39;: 500},
        &#39;NN_TORCH&#39;: {}
    },
    excluded_model_types=[&#39;KNN&#39;, &#39;RF&#39;, &#39;XT&#39;],
)</code></pre>
<blockquote>
<p>Pseudo Labeling은 v3 예측 정확도가 충분히 높을 때 효과적이었다.
테스트 전체가 아닌 <strong>40%만 샘플링</strong>해 원본 데이터 비중을 높여 과적합을 방지했다.</p>
</blockquote>
<hr>
<h3 id="전략별-비교">전략별 비교</h3>
<table>
<thead>
<tr>
<th>전략</th>
<th>모델</th>
<th>특징</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>v3 SVR</td>
<td>SVR + RobustScaler</td>
<td>이상치 강건, Seed 앙상블</td>
<td>PPT 발표 전략</td>
</tr>
<tr>
<td>v3 AutoGluon</td>
<td>AutoGluon best_quality</td>
<td>10-fold + 2단 스태킹</td>
<td><strong>최종 제출 · MAE 0.14026</strong></td>
</tr>
<tr>
<td>v5 AutoGluon</td>
<td>AutoGluon + Pseudo Label</td>
<td>학습 데이터 1.4배 확장</td>
<td>추가 실험</td>
</tr>
</tbody></table>
<hr>
<h2 id="5eda-시각화">5.EDA 시각화</h2>
<p>분석 과정에서 3가지 시각화를 작성했다.</p>
<pre><code class="language-python"># v3 피처 상관관계 히트맵
corr_cols = [&#39;stress_score&#39;, &#39;age&#39;, &#39;bmi&#39;, &#39;pulse_pressure&#39;, &#39;MAP&#39;]
sns.heatmap(df[corr_cols].corr(), annot=True, cmap=&#39;Purples&#39;, fmt=&quot;.2f&quot;)

# 수면 패턴별 스트레스 분포 (Violin Plot)
sns.violinplot(data=df, x=&#39;sleep_pattern&#39;, y=&#39;stress_score&#39;, palette=&#39;Purples&#39;)

# MAP vs 스트레스 회귀선
sns.regplot(data=df, x=&#39;MAP&#39;, y=&#39;stress_score&#39;,
            scatter_kws={&#39;alpha&#39;: 0.2, &#39;color&#39;: &#39;#6a0dad&#39;},
            line_kws={&#39;color&#39;: &#39;#b19cd9&#39;})</code></pre>
<hr>
<h2 id="6백엔드--fastapi">6.백엔드 — FastAPI</h2>
<pre><code class="language-python"># 서버 시작 시 모델 1회 로드
predictor = TabularPredictor.load(str(MODEL_DIR))

@app.post(&quot;/predict&quot;)
def predict(req: PredictRequest):
    input_df = pd.DataFrame([req.dict()])
    input_df = engineering_refined(input_df)
    input_df = input_df.drop(columns=existing_drop_cols)

    score = float(predictor.predict(input_df).iloc[0])
    score = max(0.0, min(score, 1.0))

    return {
        &quot;stress_score&quot;: round(score, 2),
        &quot;stress_level&quot;: &quot;낮음&quot; if score &lt; 0.34 else &quot;보통&quot; if score &lt; 0.67 else &quot;높음&quot;
    }</code></pre>
<pre><code class="language-bash">uvicorn main:app --reload --port 8000</code></pre>
<p><strong>포인트</strong>: 모델을 요청마다 로드하면 AutoGluon 특성상 매우 느리다. 서버 시작 시 1회만 로드해야 했다.</p>
<hr>
<h2 id="7프론트엔드--마음날씨-웹앱">7.프론트엔드 — 마음날씨 웹앱</h2>
<h3 id="구조">구조</h3>
<pre><code>page-intro → page-input(16단계) → page-result → page-reason → page-remedy</code></pre><h3 id="api-호출">API 호출</h3>
<pre><code class="language-javascript">const response = await fetch(&quot;http://localhost:8000/predict&quot;, {
  method: &quot;POST&quot;,
  headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
  body: JSON.stringify({
    age: answers.age,
    sleep_pattern: answers.sleepPattern,
    smoke_status: answers.smokeStatus,
    // ... 13개 필드
  }),
});
const { stress_score, stress_level } = await response.json();</code></pre>
<h3 id="스트레스-유형-분류-로컬-스코어링">스트레스 유형 분류 (로컬 스코어링)</h3>
<p>ML 점수는 결과 표시용, 원인 분석·유형 분류·추천 카드는 <strong>로컬 스코어링 함수</strong>로 별도 계산.</p>
<pre><code class="language-javascript">const typeSc = {
  sleep:     sleepScore * 0.55 + fatigueScore * 0.45,
  overload:  burdenScore * 0.45 + focusScore * 0.3 + moodScore * 0.25,
  tension:   hrScore * 0.35 + bpScore * 0.35 + cafScore * 0.15 + burdenScore * 0.15,
  lifestyle: bmiScore * 0.35 + actScore * 0.35 + smokeScore * 0.3,
};
// → 4종 중 최댓값 유형 1개 자동 선택</code></pre>
<table>
<thead>
<tr>
<th>유형</th>
<th>주요 신호</th>
</tr>
</thead>
<tbody><tr>
<td>😴 수면 리듬형</td>
<td>수면 패턴, 피로감</td>
</tr>
<tr>
<td>🌩️ 과부하형</td>
<td>부담감, 집중도, 기분</td>
</tr>
<tr>
<td>😰 긴장형</td>
<td>심박수, 혈압, 카페인</td>
</tr>
<tr>
<td>🌿 생활습관형</td>
<td>BMI, 활동량, 흡연</td>
</tr>
</tbody></table>
<hr>
<h2 id="8디렉토리-구조">8.디렉토리 구조</h2>
<pre><code>Stress-Score-Prediction/
├── backend/
│   ├── src/
│   │   ├── main.py          # FastAPI 서버
│   │   ├── train.py         # AutoGluon v3 학습 (최종 제출)
│   │   ├── train2.py        # AutoGluon v5 + Pseudo Labeling
│   │   ├── preprocess.py    # SVR 전략 전처리기
│   │   ├── predict.py       # v3 제출용 예측
│   │   └── predict2.py      # v4/v5 예측
│   ├── data/
│   │   ├── train.csv
│   │   └── test.csv
│   └── models/
│       ├── ag_strategy_v3/  # 최종 제출 모델
│       └── ag_strategy_v5/  # 실험 모델
└── frontend/
    ├── index.html
    └── src/
        ├── script.js
        └── style.css</code></pre><hr>
<h3 id="웹앱-ppt일부">웹앱 ppt일부</h3>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/209518b4-c95d-4efe-a4ce-9fdf091fc8ee/image.png" alt="">
<img src="https://velog.velcdn.com/images/wjdtor_2/post/e6f56e5d-41ba-4040-9173-b8df6bfbb97a/image.png" alt="">
<img src="https://velog.velcdn.com/images/wjdtor_2/post/aa9f941c-f0c0-4ef0-b0e4-1be6f3232fc2/image.png" alt=""></p>
<hr>
<h3 id="가설이-틀린-게-오히려-인사이트였다">가설이 틀린 게 오히려 인사이트였다</h3>
<p>수면·흡연이 주원인일 거라는 상식이 데이터에서 낮은 상관관계(p=0.082)로 나타났다. 이 실패가 &quot;생활습관 변수 배제 → 생체 지표 중심으로 피벗&quot;이라는 핵심 전략을 만들어줬다.</p>
<h3 id="결측치를-정보로-쓴-것이-주효했다">결측치를 정보로 쓴 것이 주효했다</h3>
<p><code>medical_history</code> 결측(0.465) vs 질환 그룹(0.495) — 이 차이를 살리기 위해 <code>NaN → &#39;None&#39;</code> 처리로 결측 자체를 하나의 카테고리로 학습시켰다.</p>
<h3 id="모델보다-피처가-먼저다">모델보다 피처가 먼저다</h3>
<p>SVR이든 AutoGluon이든, 성능 차이를 만든 건 결국 <strong>어떤 변수를 어떻게 만들었냐</strong>였다.</p>
<hr>
<blockquote>
<p>복잡하고 노이즈 가득한 생체 데이터를 분석하고, MAE 0.14026으로 3등을 기록하고, 이를 실제 웹앱으로 연결까지 해본 경험이 가장 값졌다 🌤️</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Day07 - LocalStorage로 Todo 데이터 저장]]></title>
            <link>https://velog.io/@wjdtor_2/React-Day07-LocalStorage%EB%A1%9C-Todo-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5</link>
            <guid>https://velog.io/@wjdtor_2/React-Day07-LocalStorage%EB%A1%9C-Todo-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5</guid>
            <pubDate>Tue, 10 Mar 2026 09:25:24 GMT</pubDate>
            <description><![CDATA[<h3 id="오늘-학습-내용">오늘 학습 내용</h3>
<p>Todo 데이터를 브라우저 LocalStorage에 저장하도록 수정했다.</p>
<p>이제 새로고침을 해도 Todo 데이터가 사라지지 않는다.</p>
<hr>
<h3 id="localstorage란">LocalStorage란?</h3>
<p>브라우저에 데이터를 저장할 수 있는 웹 스토리지 기능이다.</p>
<p>특징:</p>
<pre><code>브라우저에 데이터 저장
새로고침해도 유지됨
문자열 형태로 저장됨</code></pre><hr>
<h3 id="todo-저장-방식">Todo 저장 방식</h3>
<p>Todo 상태가 변경될 때마다
LocalStorage에 데이터를 저장하도록 구현했다.</p>
<h4 id="todo-추가-시-저장">Todo 추가 시 저장</h4>
<pre><code class="language-javascript">localStorage.setItem(&quot;todos&quot;, JSON.stringify(newTodos));</code></pre>
<h4 id="todo-삭제-시-저장">Todo 삭제 시 저장</h4>
<pre><code class="language-javascript">localStorage.setItem(&quot;todos&quot;, JSON.stringify(newTodos));</code></pre>
<h4 id="todo-완료-상태-변경-시-저장">Todo 완료 상태 변경 시 저장</h4>
<pre><code class="language-javascript">localStorage.setItem(&quot;todos&quot;, JSON.stringify(newTodos));</code></pre>
<hr>
<h3 id="localstorage에서-데이터-불러오기">LocalStorage에서 데이터 불러오기</h3>
<p>앱이 시작될 때 저장된 Todo를 불러온다.</p>
<pre><code class="language-javascript">const savedTodos = JSON.parse(localStorage.getItem(&quot;todos&quot;)) || [];</code></pre>
<p>설명:</p>
<pre><code class="language-javascript">localStorage → 문자열 저장
JSON.parse → 문자열을 배열/객체로 변환</code></pre>
<hr>
<h3 id="todo-전체-삭제">Todo 전체 삭제</h3>
<pre><code class="language-javascript">clearTodos: () =&gt; {
  localStorage.removeItem(&quot;todos&quot;);
  set({ todos: [] });
}</code></pre>
<p>LocalStorage에 저장된 데이터도 함께 삭제 된다.</p>
<hr>
<h3 id="결과물">결과물</h3>
<img src ="https://velog.velcdn.com/images/wjdtor_2/post/a9ec9ffc-32d9-4302-9902-bc84628eecdb/image.png" width="300"  hight="400" />


<hr>
<h3 id="오늘-배운-개념">오늘 배운 개념</h3>
<h4 id="jsonstringift">JSON.stringift()</h4>
<p>객체 / 배열 -&gt; 문자열 변환</p>
<h4 id="jsonparse">JSON.parse()</h4>
<p>문자열 -&gt; 객체 / 배열 변환</p>
<p>LocalStorage는 <strong>문자열만 저장 가능하기 때문에</strong>
<strong>JSON 변환 과정이 필요</strong>하다.</p>
<hr>
<h4 id="느낀점">느낀점</h4>
<p>처음에는 Todo가 새로고침하면 사라졌지만
LocalStorage를 이용해 데이터가 유지되는 것을 보니
작은 기능이지만 앱이 실제 서비스처럼 느껴졌다.</p>
<p>상태 관리와 데이터 저장 방식에 대해
조금 더 이해하게 된 하루였다.</p>
<hr>
<h2 id="📎-project-link">📎 Project Link</h2>
<p>🐙 GitHub Repository
👉 <a href="https://github.com/wjdtor2-design/Frontend-portfolio">https://github.com/wjdtor2-design/Frontend-portfolio</a>
📝 Velog Study Log</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Day06 - Zustand Todo Filter 구현하기]]></title>
            <link>https://velog.io/@wjdtor_2/React-Day06-Zustand-Todo-Filter-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@wjdtor_2/React-Day06-Zustand-Todo-Filter-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 09 Mar 2026 09:57:54 GMT</pubDate>
            <description><![CDATA[<h3 id="🔥오늘-목표🔥">🔥오늘 목표🔥</h3>
<p>단순한 Todo 리스트를 넘어, 실무에서 필수적인 상태 제어 기능을 구현해봄.</p>
<ul>
<li><strong>상태별 필터링</strong>: 전체,완료,미완료 버튼을 누르면 목록이 실시간으로 바뀐다.</li>
<li><strong>실시간 통계</strong>: 현재 총 개수, 완료 개수, 남은 개수를 계산하여 보여준다.</li>
<li><strong>전역 상태 관리</strong>: Zustand를 사용해 모든 데이터를 한 곳(store)에서 관리하고, 여러 컴포넌트가 공유하도록 설계했다.</li>
</ul>
<hr>
<h4 id="내가-마주친-벽--왜-이렇게-어렵게-해야하나">내가 마주친 벽 : &quot;왜 이렇게 어렵게 해야하나?&quot;</h4>
<p>오늘 공부하면서 가장 큰 의문과 답답함이 밀려왔던 포인트는 두가지</p>
<h5 id="막혔던-포인트-1-왜-appjsx에-다-안적고-파일을-쪼갤까">막혔던 포인트 1: &quot;왜 App.jsx에 다 안적고 파일을 쪼갤까?&quot;</h5>
<ul>
<li><strong>고민</strong>: 한 파일에 다적으면 한눈에 보이고 편한데, 왜 굳이 폴더를 만들어서 일을 어렵게 만드는지 궁금했음.</li>
<li><strong>해결</strong>: &#39;<strong>관심사 분리</strong>&#39;라는 개념을 배움. App은 전체 배치만 하고, 일꾼(components)들에게 일을 시키는 구조여야 나중에 고장이 났을 때 범인을 찾기 쉽다는걸 깨달음.(공장과 단톡방 비유로 이해 완!)</li>
</ul>
<h5 id="막혔던-포인트-2-필터-버튼을-눌러도-화면이-안변한다">막혔던 포인트 2: &quot;필터 버튼을 눌러도 화면이 안변한다!&quot;</h5>
<ul>
<li><strong>실수</strong>: 수첩(store)의 필터 값은 바꾸고 있었지만, 화면을 그리는 TodoList.jsx에서는 여전히 원본목록(todos)만 보고 있었음.</li>
<li><strong>해결</strong>: 데이터는 바뀌고 있었지만, 화면을 그리는 도구가 엉뚱한 곳을 보고 있었다는걸 알게됨.</li>
</ul>
<hr>
<h3 id="🛠️코드--이유">🛠️코드 / 이유</h3>
<p>1) <strong>전역 상태 확장 (TodoStore.js)</strong>
가장 먼저 <strong>공유 수첩(Store)</strong>에 새로운 기준을 추가함.</p>
<pre><code class="language-javascript">const useTodoStore = create((set) =&gt; ({
  todos: [], 
  filter: &quot;all&quot;, // 1. 기준값 추가 (전체, 미완료, 완료)

  // ... (생략)

  setFilter: (filter) =&gt; set({ filter }), // 2. 기준값을 바꾸는 리모컨 추가
}));
</code></pre>
<ul>
<li><strong>이유</strong>: 지금 화면에 &quot;무엇을 보여줄지&quot; 결정하는 기준이 필요하기 때문</li>
<li><strong>작동</strong>: 사용자가 버튼을 누르면 단톡방에 &quot;지금부터 미완료만 보자!&quot;라고 공지를 띄우는 것과 같다.</li>
</ul>
<p>2) <strong>필터 버튼 컴포넌트 (TodoFilter.jsx)</strong>
사용자의 명령을 받는 <strong>스위치</strong>를 만들었음.</p>
<pre><code class="language-javascript">function TodoFilter() {
  const { filter, setFilter } = useTodoStore(); // 수첩에서 리모컨 가져오기

  return (
    &lt;div&gt;
      &lt;button onClick={() =&gt; setFilter(&quot;all&quot;)}&gt;전체&lt;/button&gt;
      &lt;button onClick={() =&gt; setFilter(&quot;active&quot;)}&gt;미완료&lt;/button&gt;
      &lt;button onClick={() =&gt; setFilter(&quot;completed&quot;)}&gt;완료&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<ul>
<li><strong>이유</strong>: 버튼을 클릭이라는 역할만 따로 떼어내어 관리하기 위함임.(<strong>관심사 분리</strong>)</li>
</ul>
<p>3) <strong>필터링 로직 구현(TodoList.jsx)</strong>
오늘의 <strong>가장 중요한 하이라이트</strong></p>
<pre><code class="language-javascript">const { todos, filter } = useTodoStore(); // 수첩에서 데이터 가져오기

// 중요! 원본은 건드리지 않고 &#39;보여줄 목록&#39;만 새로 만듭니다 (파생 상태)
const filteredTodos = todos.filter((todo) =&gt; {
  if (filter === &quot;completed&quot;) return todo.completed; // 완료만 통과!
  if (filter === &quot;active&quot;) return !todo.completed;  // 미완료만 통과!
  return true; // 전체보기
});

// 화면에는 거른 목록(filteredTodos)을 뿌려줍니다.
{filteredTodos.map((todo, index) =&gt; ( ... ))}
</code></pre>
<ul>
<li><strong>이유</strong>: 원본 창고(todos)를 뒤섞지 않고, 출력할 때만 <strong>실시간 장바구니(filteredTodos)</strong>를 만들어 화면을 갈아 끼우기 위해서임.</li>
</ul>
<hr>
<h3 id="깨달은-핵심-개념">깨달은 핵심 개념</h3>
<p>이번 프로젝트를 통해 머릿속에 확실히 박힌 3단계 흐름이다.</p>
<ol>
<li><strong>입력(App)</strong>: useState로 잠깐 들고 있다가 단톡방에 전달.</li>
<li><strong>보관(Store)</strong>: Zustand라는 큰 수첩에 저장하고 공지사항 업데이트.</li>
<li><strong>출력(Components)</strong>: 수첩을 보고 있던 일꾼들이 자기 스타일대로 요리해서 화면에 출력.</li>
</ol>
<hr>
<h3 id="결과물">결과물</h3>
<img src="https://velog.velcdn.com/images/wjdtor_2/post/c515d40c-323a-4e3e-a2cb-b1797cb818d1/image.png" width="250" hight="350" />
<img src="https://velog.velcdn.com/images/wjdtor_2/post/711b0198-fbce-4a3f-9104-02f7673cae3e/image.png" width="250" hight="350" />
<img src="https://velog.velcdn.com/images/wjdtor_2/post/a3756c67-3077-4e24-9fbe-5cf780cfac1f/image.png" width="250" hight="350" />

<hr>
<h2 id="📎-project-link">📎 Project Link</h2>
<p>🐙 GitHub Repository
👉 <a href="https://github.com/wjdtor2-design/Frontend-portfolio">https://github.com/wjdtor2-design/Frontend-portfolio</a>
📝 Velog Study Log</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Day05 - Todo 통계 기능 만들기 | 파생 상태(Derived State) 이해하기]]></title>
            <link>https://velog.io/@wjdtor_2/React-Day05-Todo-%ED%86%B5%EA%B3%84-%EA%B8%B0%EB%8A%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0-%ED%8C%8C%EC%83%9D-%EC%83%81%ED%83%9CDerived-State-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@wjdtor_2/React-Day05-Todo-%ED%86%B5%EA%B3%84-%EA%B8%B0%EB%8A%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0-%ED%8C%8C%EC%83%9D-%EC%83%81%ED%83%9CDerived-State-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 05 Mar 2026 11:33:06 GMT</pubDate>
            <description><![CDATA[<h3 id="🔥오늘-목표🔥">🔥오늘 목표🔥</h3>
<ul>
<li>통계 기능(Todo Stats) 추가</li>
<li>전체 Todo 개수</li>
<li>완료된 Todo 개수</li>
<li>남은 Todo 개수</li>
</ul>
<p>예시</p>
<pre><code class="language-makdefile">전체: 3
완료: 1
남은: 2</code></pre>
<p>이 기능을 만들면서 <strong>배열 데이터를 계산하는 방법과 파생 상태(Derived State)</strong> 개념을 함께 이해하게됨</p>
<hr>
<h3 id="todostats-컴포넌트-만들기">TodoStats 컴포넌트 만들기</h3>
<p>Todo 통계를 관리하는 컴포넌트를 따로 분리함.</p>
<pre><code>components/TodoStats.jsx</code></pre><p>Zustand store에서 todos 상태를 가져온다.</p>
<pre><code class="language-javascript">import useTodoStore from &quot;../store/todoStore&quot;;

function TodoStats() {
  const todos = useTodoStore((state) =&gt; state.todos);

  const total = todos.length;
  const completed = todos.filter((todo) =&gt; todo.completed).length;
  const remaining = total - completed;

  return (
    &lt;div&gt;
      &lt;p&gt;전체: {total}&lt;/p&gt;
      &lt;p&gt;완료: {completed}&lt;/p&gt;
      &lt;p&gt;남음: {remaining}&lt;/p&gt;
    &lt;/div&gt;
  );
}

export default TodoStats;</code></pre>
<p>여기서 중요한 부분은 <strong>filter()를 이용한 계산.</strong></p>
<hr>
<h3 id="filter로-완료된-todo-계산하기">filter()로 완료된 Todo 계산하기</h3>
<p>배열에서 특정 조건에 맞는 데이터를 찾을 때
filter()를 사용한다</p>
<pre><code class="language-javascript">const completed = todos.filter((todo) =&gt; todo.completed).length;</code></pre>
<p>설명</p>
<p>1) todos 배열을 순회하면서
2) completed가 true인 todo만 남기고
3) 그 개수를 length로 계산한다.</p>
<p>예시데이터</p>
<pre><code class="language-javascript">[
 { text: &quot;공부&quot;, completed: true },
 { text: &quot;운동&quot;, completed: false },
 { text: &quot;코딩&quot;, completed: false }
]</code></pre>
<p>filter결과</p>
<pre><code class="language-arduino">[{ text: &quot;공부&quot;, completed: true }]</code></pre>
<p>따라서</p>
<pre><code class="language-ini">completed = 1</code></pre>
<p>이 된다.</p>
<hr>
<h3 id="남은-todo-계산하기">남은 Todo 계산하기</h3>
<p>남은 Todo는 간단하게 계산할 수 있다.</p>
<pre><code class="language-javascript">const remaining = total - completed;</code></pre>
<p>즉</p>
<pre><code>남은 할 일 = 전체 - 완료</code></pre><p>이렇게 계산한다.</p>
<hr>
<h3 id="derived-state-파생-상태">Derived State (파생 상태)</h3>
<p>이번 기능에서 중요한 개념이 <strong>파생 상태(Derived State)</strong>이다.</p>
<p><strong>파생 상태란</strong></p>
<p>👉 기존 상태를 기반으로 계산해서 만들어지는 값</p>
<p>예시</p>
<table>
<thead>
<tr>
<th>상태</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>todos</td>
<td>실제 상태</td>
</tr>
<tr>
<td>total</td>
<td>todos.length</td>
</tr>
<tr>
<td>completed</td>
<td>filter 계산</td>
</tr>
<tr>
<td>remaining</td>
<td>total - completed</td>
</tr>
</tbody></table>
<p>즉</p>
<pre><code class="language-nginx">todos -&gt; 실제 데이터
통계값 -&gt; 계산된 데이터</code></pre>
<p>이런 구조를 <strong>파생 상태(Derived State)</strong> 라고 한다.</p>
<hr>
<h3 id="todostats-컴포넌트-연결">TodoStats 컴포넌트 연결</h3>
<p>App.jsx에서 TodoStats 컴포넌트를 추가해 화면에 표시했다.</p>
<pre><code class="language-javascript">import TodoStats from &quot;./components/TodoStats&quot;;

function App() {
  return (
    &lt;div&gt;
      &lt;h1&gt;Todo App&lt;/h1&gt;

      &lt;TodoList /&gt;

      &lt;TodoStats /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>이제 Todo를 추가하면 통계가 자동으로 업데이트된다.</p>
<hr>
<h3 id="결과물">결과물</h3>
<img src= "https://velog.velcdn.com/images/wjdtor_2/post/8f63de25-5cf9-4b11-81a3-6416737095ad/image.png" width="300" />

<hr>
<h3 id="github-pull-request-workflow">GitHub Pull Request Workflow</h3>
<p>오늘은 기능 구현뿐 아니라 GitHub 협업 방식도 함께 연습함.</p>
<p>진행과정</p>
<pre><code>1. feature/day05 브랜치 생성
2. Todo Stats 기능 개발
3. GitHub에 Push
4. Pull Request 생성
5. Merge
6. 브랜치 삭제
</code></pre><p>이 과정은 실제 개발에서 사용하는 <strong>Git Workflow</strong>와 동일.</p>
<hr>
<h4 id="📌-오늘-배운점">📌 오늘 배운점</h4>
<p>단순히 기능을 추가하는 것뿐 아니라
React에서 중요한 개념들을 함께 배울 수 있었음.</p>
<ul>
<li>배열 데이터 계산(filter)</li>
<li>파생 상태(Derived State)</li>
<li>컴포넌트 역할 분리</li>
<li>Zustand 전역 상태 활용</li>
<li>GitHub Pull Request Workflow</li>
</ul>
<hr>
<h2 id="📎-project-link">📎 Project Link</h2>
<p>🐙 GitHub Repository
👉 <a href="https://github.com/wjdtor2-design/Frontend-portfolio">https://github.com/wjdtor2-design/Frontend-portfolio</a>
📝 Velog Study Log</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Day04 - Zustand로 전역 상태 관리하기 - Props Drilling에서 벗어나기]]></title>
            <link>https://velog.io/@wjdtor_2/React-Day04-Zustand%EB%A1%9C-%EC%A0%84%EC%97%AD-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-Props-Drilling%EC%97%90%EC%84%9C-%EB%B2%97%EC%96%B4%EB%82%98%EA%B8%B0</link>
            <guid>https://velog.io/@wjdtor_2/React-Day04-Zustand%EB%A1%9C-%EC%A0%84%EC%97%AD-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-Props-Drilling%EC%97%90%EC%84%9C-%EB%B2%97%EC%96%B4%EB%82%98%EA%B8%B0</guid>
            <pubDate>Wed, 04 Mar 2026 10:18:17 GMT</pubDate>
            <description><![CDATA[<h3 id="🔥오늘-목표🔥">🔥오늘 목표🔥</h3>
<ul>
<li>Zustand로 Todo 전역 상태 관리하기</li>
<li>props drilling 문제 이해하기</li>
<li>컴포넌트 분리 연습하기</li>
<li>상태를 어디에 둬야 하는지 기준 세우기</li>
</ul>
<hr>
<h3 id="day03-복습">Day03 복습</h3>
<p>Day03에서는 useState를 사용해 App에서 todos를 관리함.</p>
<pre><code class="language-js">const [todos, setTodos] = useState([]);</code></pre>
<p>그리고 TodoList로 props를 전달함.</p>
<pre><code class="language-nginx">App
 └ TodoList</code></pre>
<p>처음에는 단순했지만, 이런 생각이 들었음.</p>
<p>👉 컴포넌트가 더 깊어지면 props가 계속 내려가야 하는 거 아닌가?</p>
<p>이게 바로 <strong>Props Drilling</strong> 문제.</p>
<hr>
<h3 id="props-drilling이란">Props Drilling이란?</h3>
<p>Props는 부모가 자식에게 전달하는 데이터다.</p>
<pre><code class="language-js">&lt;Child name=&quot;Julie&quot; /&gt;</code></pre>
<p>하지만 구조가 이렇게 깊어지면?</p>
<pre><code class="language-markdown">App
 └ Parent
     └ Child
         └ GrandChild</code></pre>
<p>GrandChild가 데이터를 필요로 하면
중간 단계 컴포넌트도 모두 props를 전달해야 한다.</p>
<p>-&gt; 코드 복잡
-&gt; 유지보수 어려움
-&gt; 구조가 지저분해짐</p>
<p>이문제를 해결하기 위해 전역 상태 관리가 등장함.</p>
<hr>
<h3 id="전역global상태란">전역(Global)상태란?</h3>
<p><strong>전역상태</strong>란
<strong>👉 앱 어디서든 공통으로 사용할 수 있는 데이터</strong></p>
<p>를 의미함.</p>
<p>비유하자면:</p>
<ul>
<li>Props -&gt; 부모가 직접 주는 용돈</li>
<li>전역 상태 -&gt; 거실에 두고 누구나 꺼내 쓰는 간식 바구니</li>
</ul>
<p>전역 상태가 되면
부모 -&gt; 자식 전달 과정이 필요 없다.</p>
<p>각 컴포넌트가 직접 가져다 쓰면 된다.</p>
<hr>
<h3 id="zustand-적용하기">Zustand 적용하기</h3>
<p><strong>Zustand</strong>는 <span style="color:indianred">전역 상태 관리 라이브러리</span></p>
<p>store를 만들어 상태를 중앙에서 관리한다.</p>
<pre><code class="language-js">const useTodoStore = create((set) =&gt; ({
  todos: [],
  addTodo: (text) =&gt;
      set((state) =&gt; ({
      todos: [...state.todos, { text, completed: false }],
    })),
}));</code></pre>
<p>그리고 각 컴포넌트에서 이렇게 사용한다.</p>
<pre><code class="language-js">const { todos, deleteTodo, toggleTodo } = useTodoStore();</code></pre>
<p>중요한 점은
<strong>👉 props를 전혀 사용하지 않았다는 것.</strong></p>
<hr>
<h3 id="컴포넌트-분리-후-구조">컴포넌트 분리 후 구조</h3>
<pre><code class="language-markdown">
        store
        /    \
     App   TodoList</code></pre>
<p>App과 TodoList는 부모-자식 관계이지만
상태를 서로 주고받지 않는다.</p>
<p><strong>둘 다 store</strong>를 <strong>직접 구독</strong>한다.</p>
<p>이부분이 오늘 가장 크게 이해된 지점이었다.</p>
<hr>
<h3 id="input은-왜-store에-넣지-않았을까">input은 왜 store에 넣지 않았을까?</h3>
<p>처음에는 이런 고민을 했다.</p>
<p><strong>👉 input 값도 전역 상태로 관리해야 하나?</strong></p>
<p>하지만 정리해보니 기준이 생겼다.</p>
<table>
<thead>
<tr>
<th>상태 종류</th>
<th>어디에 둬야 할까?</th>
</tr>
</thead>
<tbody><tr>
<td>여러 컴포넌트가 공유</td>
<td>전역 상태</td>
</tr>
<tr>
<td>한 컴포넌트에서만 사용</td>
<td>useState</td>
</tr>
</tbody></table>
<p>input 값은 App에서만 사용되는 임시 데이터다.
따라서 전역 상태로 만들 필요가 없다.</p>
<p><strong>store</strong>는 &quot;<strong>공용 창고</strong>&quot;일 뿐,
모든 상태를 넣는 곳은 아니다.</p>
<hr>
<h3 id="오늘의-가장-큰-깨달음">오늘의 가장 큰 깨달음</h3>
<p>Day04는 기능이 복잡한 날은 아니었다.</p>
<p>하지만</p>
<ul>
<li>상태를 어디에 둬야 하는지</li>
<li>왜 전역 상태가 필요한지</li>
<li>props와의 차이는 무엇인지</li>
</ul>
<p>를 고민한 하루였다.</p>
<p>이제는 단순히 코드를 작성하는 것이 아니라</p>
<p><strong>👉 &quot; 왜 이렇게 설계하는가 ? &quot;</strong></p>
<p>를 생각하게 되었다.</p>
<hr>
<h3 id="🔥day04정리">🔥Day04정리</h3>
<p>✔ 전역 상태 개념 이해
✔ Props Drilling 문제 이해
✔ Zustand 구조 이해
✔ 컴포넌트 분리 경험
✔ 상태 설계 기준 세우기</p>
<p>기능은 단순했지만
설계 관점이 바뀐 날이었다.</p>
<hr>
<h2 id="📎-project-link">📎 Project Link</h2>
<p>🐙 GitHub Repository
👉 <a href="https://github.com/wjdtor2-design/Frontend-portfolio">https://github.com/wjdtor2-design/Frontend-portfolio</a>
📝 Velog Study Log</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Day03 - Todo 만들며 깨달은 상태 관리의 핵심]]></title>
            <link>https://velog.io/@wjdtor_2/React-Day03-Todo-%EB%A7%8C%EB%93%A4%EB%A9%B0-%EA%B9%A8%EB%8B%AC%EC%9D%80-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%EC%9D%98-%ED%95%B5%EC%8B%AC</link>
            <guid>https://velog.io/@wjdtor_2/React-Day03-Todo-%EB%A7%8C%EB%93%A4%EB%A9%B0-%EA%B9%A8%EB%8B%AC%EC%9D%80-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%EC%9D%98-%ED%95%B5%EC%8B%AC</guid>
            <pubDate>Tue, 03 Mar 2026 07:57:24 GMT</pubDate>
            <description><![CDATA[<h2 id="🔥오늘-목표🔥">🔥오늘 목표🔥</h2>
<ul>
<li>Todo 추가</li>
<li>삭제 기능 구현</li>
<li>완료 체크 기능 추가</li>
<li>Enter로 제출 처리</li>
<li>React에서 왜 &quot;직접 수정하면 안되는지&quot; 이해하기</li>
</ul>
<hr>
<h3 id="처음-막혔던-부분---todo가-뭐지">처음 막혔던 부분 - &quot;todo가 뭐지?&quot;</h3>
<p>처음엔 todo의 개념 자체가 헷갈렸음.</p>
<ul>
<li>input에 입력하는 건 문자열</li>
<li>여러 개의 할 일을 관리하려면 배열이 필요</li>
</ul>
<p>그래서 구조를 이렇게 잡음.</p>
<pre><code class="language-js">const [todos,setTodos] = useState([]);</code></pre>
<p>그리고 단순 문자열이 아니라 객체로 저장하도록 변경함.</p>
<pre><code class="language-js">{
    text: &quot;공부하기&quot;
    compleated: false
}</code></pre>
<p>👉 이유 : 완료 상태까지 관리해야 하기 때문.</p>
<hr>
<h3 id="push를-쓰면-왜-안될까">push를 쓰면 왜 안될까?</h3>
<p>처음에 이렇게 생각했다.</p>
<pre><code class="language-js">todos.push(newTodo);
setTodos(todos);</code></pre>
<p>하지만 <strong>React</strong>는 <span style="color:indianred">기존 배열을 직접 수정하는 걸 좋아하지 않는다.</span></p>
<h4 id="🔥-핵심-개념--불변성immutability">🔥 핵심 개념 : 불변성(Immutability)</h4>
<p>React는 상태가 바뀌었는지 확인할 때
값을 깊게 비교하지 않고 참조값(주소)을 본다.</p>
<p>push는 기존 배열을 수정하기 때문에
주소가 그대로다.</p>
<p>그래서</p>
<pre><code class="language-js">setTodos([...todos, newTodo]);</code></pre>
<p>👉 기존 배열을 복사해서
👉 새로운 배열을 만들어 넣어준다.</p>
<hr>
<h3 id="map으로-특정-요소만-수정하기">map으로 특정 요소만 수정하기</h3>
<p>완료 체크 기능을 구현하면서 또 한 번 막혔다.</p>
<pre><code class="language-js">const handleToggle = (index) =&gt; {
  setTodos(
    todos.map((todo, i) =&gt;
      i === index
        ? { ...todo, completed: !todo.completed }
        : todo
    )
  );
};</code></pre>
<p>여기서 이해한 것 :</p>
<ul>
<li>map은 새 배열을 만든다</li>
<li>클릭한 index만 수정하고</li>
<li>나머지는 그대로 반환해야 한다 (: todo)</li>
</ul>
<h4 id="왜-todo를-쓰는가">왜 ...todo를 쓰는가?</h4>
<p>기존 객체를 복사한 뒤
completed 값만 덮어쓰기 위해서.</p>
<pre><code class="language-js">{ ...todo, completed: !todo.completed }</code></pre>
<p>👉 기존 내용 유지 + 특정 속성만 변경</p>
<hr>
<h3 id="filter로-삭제-구현">filter로 삭제 구현</h3>
<pre><code class="language-js">setTods(todos.filter((_, i) =&gt; i !== index));</code></pre>
<p>filter는
👉 특정 조건을 만족하는 것만 남겨서
👉 새 배열로 반환한다.</p>
<p>그래서 삭제할 index만 제외하면 된다.</p>
<hr>
<h3 id="enter-키-중복-문제">Enter 키 중복 문제</h3>
<p>처음엔 onKeyDown과 버튼 클릭이 동시에 실행되어
중복 추가 문제가 발생했다.</p>
<p>해결 방법 :</p>
<pre><code class="language-js">&lt;form onSubmit={handleAdd}&gt;</code></pre>
<p>그리고</p>
<pre><code class="language-js">e.preventDefault();</code></pre>
<p>👉 Enter는 원래 form 제출 동작이기 때문에
form + onSubmit 구조가 가장 안정적이란걸 알게됨.</p>
<p>이걸 통해 이벤트 흐름도 이해하게 되었음.</p>
<hr>
<h3 id="오늘-깨달은-핵심-정리">오늘 깨달은 핵심 정리</h3>
<p>✔ React는 직접 수정하면 안된다
✔ 항상 &quot;새 배열/새 객체&quot;를 만들어야한다
✔ spread 문법은 복사
✔ map은 수정
✔ filter는 삭제
✔ form + onSubmit이 Enter 처리의 정석</p>
<hr>
<h3 id="결과물">결과물</h3>
<p aligh="center">
    <img src = "https://velog.velcdn.com/images/wjdtor_2/post/e6554d1e-2cfd-425b-aee2-909f00cdc3d0/image.png" width="300">
</p>


<hr>
<h2 id="📎-project-link">📎 Project Link</h2>
<p>🐙 GitHub Repository
👉 <a href="https://github.com/wjdtor2-design/Frontend-portfolio">https://github.com/wjdtor2-design/Frontend-portfolio</a>
📝 Velog Study Log</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Day02 -Counter 기능 확장과 상태 기반 UI 제어 이해하기]]></title>
            <link>https://velog.io/@wjdtor_2/React-Day02-Counter-%EA%B8%B0%EB%8A%A5-%ED%99%95%EC%9E%A5%EA%B3%BC-%EC%83%81%ED%83%9C-%EA%B8%B0%EB%B0%98-UI-%EC%A0%9C%EC%96%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@wjdtor_2/React-Day02-Counter-%EA%B8%B0%EB%8A%A5-%ED%99%95%EC%9E%A5%EA%B3%BC-%EC%83%81%ED%83%9C-%EA%B8%B0%EB%B0%98-UI-%EC%A0%9C%EC%96%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 27 Feb 2026 09:29:05 GMT</pubDate>
            <description><![CDATA[<h3 id="day-2-목표">Day 2 목표</h3>
<ul>
<li>기존 Counter 기능 확장</li>
<li>Reset 버튼 추가</li>
<li>조건에 따른 버튼 비활성화 구현</li>
<li>렌더링 흐름 복습</li>
</ul>
<hr>
<h3 id="오늘-추가한-기능">오늘 추가한 기능</h3>
<p><strong>✏️ Reset 버튼 추가</strong></p>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/4d7dea24-b342-4954-8df8-afe0d58ecb83/image.png" alt=""></p>
<pre><code class="language-jsx">&lt;button onClick={() =&gt; set Count(0)}&gt;
  reset
&lt;/button&gt;</code></pre>
<ul>
<li>state를 0으로 초기화</li>
<li>setCount 호출 -&gt; 재렌더링 발생</li>
</ul>
<hr>
<p><strong>✏️ count 10이상이면 증가 버튼 비활성화</strong></p>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/14506db6-9de6-4b55-b333-a0f1c6c70e89/image.png" alt=""></p>
<pre><code class="language-jsx">&lt;button
  onClick{() =&gt; setCount(prev =&gt; prev +1)}
  disabled = {count &gt;= 10}
&gt;
  증가버튼
&lt;/button&gt;</code></pre>
<p><strong>📌 핵심 개념</strong> :</p>
<ul>
<li>disabled는 HTMl 기본 속성</li>
<li>true면 클릭 불가</li>
<li>count 값에 따라 동적으로 변경됨</li>
</ul>
<hr>
<h3 id="✏️-이해한-핵심-흐름">✏️ 이해한 핵심 흐름</h3>
<p>count가 9 -&gt; 10이 될 때:</p>
<ol>
<li>setCount 실행</li>
<li>state 값 변경</li>
<li>App 함수 재실행 (렌더링)</li>
<li>JSX 재계산</li>
<li>disabled 값 true로 변경</li>
<li>브라우저 UI 업데이트</li>
</ol>
<hr>
<h3 id="✏️-깨달은-점">✏️ 깨달은 점</h3>
<ul>
<li>렌더링은 화면 새로고침이 아니다.</li>
<li>컴포넌트 함수가 다시 실행되는 것이다.</li>
<li>state가 바뀌면 return안의 JSX는 다시 계산된다.</li>
<li>조건부 렌더링은 화면뿐 아니라 속성에도 적용된다.</li>
</ul>
<hr>
<h4 id="day-01---day-02-성장차이">Day 01 -&gt; Day 02 성장차이</h4>
<p>Day 01 : 단순 카운터 구현
Day 02 : 상태 기반 UI 제어 및 조건부 속성 이해</p>
<hr>
<h2 id="📎-project-link">📎 Project Link</h2>
<p>🐙 GitHub Repository<br>👉 <a href="https://github.com/wjdtor2-design/Frontend-portfolio">https://github.com/wjdtor2-design/Frontend-portfolio</a>
📝 Velog Study Log</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] useState와 useEffect 제대로 이해하기]]></title>
            <link>https://velog.io/@wjdtor_2/React-useState%EC%99%80-useEffect-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@wjdtor_2/React-useState%EC%99%80-useEffect-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 26 Feb 2026 11:45:52 GMT</pubDate>
            <description><![CDATA[<h2 id="day-1-목표">Day 1 목표</h2>
<ul>
<li>useState 이해하기</li>
<li>렌더링 개념 이해하기</li>
<li>조건부 렌더링 구현하기</li>
<li>Git 브랜치 -&gt; PR -&gt; Merge까지 해보기</li>
</ul>
<hr>
<h2 id="오늘-구현한-기능">오늘 구현한 기능</h2>
<h4 id="📌-증가--감소-버튼-구현">📌 증가 / 감소 버튼 구현</h4>
<ul>
<li>버튼 클릭시 count 증가</li>
<li>버튼 클릭시 count 감소</li>
</ul>
<h4 id="📌-조건부-렌더링-추가">📌 조건부 렌더링 추가</h4>
<ul>
<li>count가 0이상이면 😊표시</li>
<li>음수이면 ⚠️ 음수입니다 출력</li>
<li>5 이상이면 &quot;5이상입니다&quot; 출력</li>
</ul>
<p>📸 캡쳐
<img src=https://velog.velcdn.com/images/wjdtor_2/post/267ebaa5-7603-49c2-8af5-ef9fd1007110/image.png width="400"/><img src = https://velog.velcdn.com/images/wjdtor_2/post/4feb9ea3-defa-4db9-a42c-0aa62c653659/image.png width="400"/></p>
<hr>
<h4 id="구현코드📝">구현코드📝</h4>
<pre><code class="language-jsx">import { useEffect, useState } from &quot;react&quot;;

function App () {
  const [count, setCount] = useState(0);

  useEffect(() =&gt; {
    console.log(&quot;렌더링 발생!&quot;);
  }, [count]);

  return(
    &lt;div&gt;
      &lt;h1&gt;증가, 감소 버튼&lt;/h1&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;
        증가버튼
      &lt;/button&gt;
      &lt;button onClick={() =&gt; setCount(count - 1)}&gt;
        감소버튼
      &lt;/button&gt;
      {count &gt;= 0 ? &lt;p&gt;😊&lt;/p&gt; : &lt;p&gt;⚠️ 음수입니다&lt;/p&gt;}
      {count &gt;= 5 &amp;&amp; &lt;p&gt;5 이상입니다&lt;/p&gt;}
      &lt;p&gt;현재 값 : {count}&lt;/p&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<hr>
<h3 id="핵심-개념-정리">핵심 개념 정리</h3>
<p><strong>👉 useState란</strong>
React에서 값을 저장하는 훅(Hook).
값이 바뀌면 컴포넌트가 다시 실행된다.</p>
<hr>
<p><strong>👉 렌더링이란</strong>
컴포넌트 함수가 다시 실행되는 것.</p>
<p>새로고침이 아니라,
React가 화면을 다시 계산해서 업데이트하는 과정.</p>
<hr>
<p><strong>👉 setState가 호출되면 일어나는 일</strong></p>
<ol>
<li>state 값 변경</li>
<li>컴포넌트 재실행</li>
<li>return 부분 다시 계산</li>
<li>화면 업데이트</li>
</ol>
<hr>
<p><strong>👉 useEffect란</strong>
렌더링이 끝난 뒤 실행되는 코드.</p>
<p>dependency 배열에 따라 실행 시점이 달라진다.</p>
<ul>
<li>[] -&gt; 처음 한 번</li>
<li>[count] -&gt; count 변경 시</li>
<li>생략 -&gt; 렌더링 마다</li>
</ul>
<hr>
<h4 id="git-정리🗃️">Git 정리🗃️</h4>
<p>오늘 처음으로 :</p>
<ul>
<li>feature 브랜치 생성</li>
<li>PR 생성</li>
<li>Merge</li>
<li>Branch 삭제</li>
</ul>
<p>👉 협업 기본 흐름을 경험했다.</p>
<p>이건 그냥 코드보다 더 중요한 경험임!</p>
<hr>
<h2 id="📎-project-link">📎 Project Link</h2>
<p>🐙 GitHub Repository<br>👉 <a href="https://github.com/wjdtor2-design/Frontend-portfolio">https://github.com/wjdtor2-design/Frontend-portfolio</a>
📝 Velog Study Log  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 서비스 기초와 구조 정리2]]></title>
            <link>https://velog.io/@wjdtor_2/%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B8%B0%EC%B4%88%EC%99%80-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC2</link>
            <guid>https://velog.io/@wjdtor_2/%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B8%B0%EC%B4%88%EC%99%80-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC2</guid>
            <pubDate>Fri, 06 Feb 2026 08:56:12 GMT</pubDate>
            <description><![CDATA[<hr>
<h3 id="네트워크-기초">네트워크 기초</h3>
<p>컴퓨터들이 서로 소통하기 위해서는 주소와 규칙이 필요함.</p>
<ul>
<li><strong>IP(Internet Protocol)</strong> : 인터넷에 연결된 기기의 고유 식별 주소.(비유:건물주소)</li>
<li><strong>Port(포트)</strong> : 한 컴퓨터 내에서 실행되는 특정 프로그램의 번호.(비유:건물 안의 방번호)<ul>
<li>HTTP는 기본적으로 80번,HTTPS는 443번 포트를 사용함  </li>
</ul>
</li>
<li><strong>DNS(Domain Name System)</strong> : naver.com 같은 문자를 IP 주소로 변환해주는 &#39;인터넷 전화번호부&#39;</li>
</ul>
<hr>
<h3 id="프론트엔드front-end--사용자의-접점">프론트엔드(Front-end) : 사용자의 접점</h3>
<p>사용자가 HTTP 요청을 직접 만들지 않아도 버튼 클릭이나 입력만으로 서비스가 동작하게 해주는 프로그램</p>
<ul>
<li><strong>역할</strong> : 사용자가 보는 UI구현, 사용자 입력 처리 및 HTTP요청 변환, 서버 응답을 화면에 반영</li>
<li><strong>주요 기술 스택</strong></li>
</ul>
<pre><code>- HTML : 화면의 구조 정의
- CSS : 스타일과 레이아웃 담당
- JavaScript : 화면의 동작과 로직 구현
- 브라우저 이해 : 사용자의 디바이스,브라우저 환경에서의 동작 제어
- HTTP / REST API : 서버와의 통신
- React /Vue /Svelte : UI와 상태 관리를 구조화</code></pre><ul>
<li>프론트엔드 vs 클라이언트 : 프론트엔드는 주로 브라우저에서 실행되는 웹 UI를 의미하며, 클라이언트는 웹을 포함해 모바일 앱, 데스크톱 앱 등 서버와 통신하는 모든 프로그램을 포괄하는 개념임.</li>
</ul>
<hr>
<h3 id="백엔드back-end--서비스의-핵심-로직">백엔드(Back-end) : 서비스의 핵심 로직</h3>
<p>클라이언트의 요청을 해석하고, 비즈니스 로직을 수행하며 데이터베이스를 관리함.</p>
<ul>
<li><p>** 역할** : 요청 검증, 비밀번호 함호화 등 보안 처리, 데이터 조회·저장·수정, API 설계 및 제공.</p>
</li>
<li><p><strong>회원가입 예시</strong> : 
  사용자가 UI에 정보를 입력하면 -&gt; 프론트엔드가 HTTP 요청을 전송 -&gt; 백엔드가 값을 검증하고 비밀번호 암호화 후 DB에 저장 -&gt; 성공/실패 응답 반환</p>
</li>
<li><p><strong>주요 기술 스택</strong></p>
</li>
</ul>
<pre><code>- 프로그래밍 언어(Python,,Java,Node.js) : 비즈니스 로직 구현
- 웹 프레임워크(FastAPI,Spring) : 요청,응답 처리 및 구조화
- HTTP /REST API : 클라이언트와의 통신 규약
- 데이터베이스 / SQL : 데이터 저장,조회,관리
- 운영체제 / 클라우드 /인프라 : 서비스 배포 및 확장
- 로그,모니터링 : 장애 분석과 안정성 확보</code></pre><ul>
<li>API의 책임 : REST API의 설계와 일관성은 주로 백엔드가 책임지며, 프론트엔드는 정의된 스펙(계약)에 맞춰 요청을 보낸다.</li>
</ul>
<hr>
<h3 id="데이터와-저장소-database">데이터와 저장소 (Database)</h3>
<p>단순한 값인 &#39;데이터&#39;가 맥락에 맞게 정리되면 &#39;정보가&#39;가 되고, 이를 활용할 때 비로소 &#39;지식&#39;이 됨.
즉 , 서비스에서 발생하는 모든 정보를 체계적으로 보관하는 곳임.</p>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/eb3a1c04-ad1a-4d79-a270-0aed5d119fb7/image.png" alt=""></p>
<ul>
<li><p><strong>RDB vs RDBMS</strong> :</p>
<ul>
<li><strong>RDB(Relational Database)</strong> : 표(Table) 형태로 구성된 관계형 데이터 모델 그 자체임</li>
<li><strong>RDBMS(Management System)</strong> : RDB를 관리하는 소프트웨어임.(예 :MySQL,PostgreSQL,Oracle)</li>
</ul>
</li>
<li><p><strong>데이터 모델링의 핵심</strong> :</p>
<ul>
<li><strong>Primary Key(PK)</strong> : 각 행을 구별하는 고유 신분증임</li>
<li><strong>Foreign Key(FK)</strong> : 다른 테이블의 PK를 참조하여 테이블 간의 관계(Realtionship)를 맺어줌</li>
<li>이를 통해 데이터 중복을 막고 데이터의 무결성을 유지함</li>
</ul>
</li>
</ul>
<hr>
<h3 id="정리">정리</h3>
<ol>
<li>사용자가 <strong>프론트엔드</strong>에서 행동을 하면,</li>
<li>네트워크(IP/Port)를 타고 <span style="color:indianred">HTTP 요청</span>이 <span style="color:indianred">백엔드</span>로 전달됨</li>
<li>백엔드는 <strong>데이터베이스(RDBMS)</strong>에서 필요한 정보를 꺼내 가공한 뒤,</li>
<li>적절한 <span style="color:indianred">응답 코드</span>와 함께 결과를 다시 사용자에게 돌려줌</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 서비스 기초와 구조 정리]]></title>
            <link>https://velog.io/@wjdtor_2/%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B8%B0%EC%B4%88%EC%99%80-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@wjdtor_2/%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B8%B0%EC%B4%88%EC%99%80-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 05 Feb 2026 09:49:33 GMT</pubDate>
            <description><![CDATA[<hr>
<h3 id="웹-서비스web-service개요">웹 서비스(Web Service)개요</h3>
<ul>
<li><strong>정의</strong> : 웹(Web) 표준 규약(URL, HTTP, HTTPS)을 통해 인터넷상에서 자원과 정보를 제공하는 시스템</li>
<li><strong>특징</strong> : 플랫폼과 기기에 독립적이며, 브라우저나 설치형 클라이언트(앱)을 통해 여러 사용자가 동시에 접근할 수 있음.</li>
</ul>
<p>-** 주요 구성 요소**:</p>
<pre><code>- 사용자(User) : 서비스를 이용하는 사람
- 클라이언트(Client) : 사용자의 행동을 받아 서버에 요청을 보내는 프로그램
- 서버(Server) : 클라이언트의 요청을 처리하고 결과를 반환하는 프로그램
- API : 프로그램 간 기능을 사용하기 위한 약속된 인터페이스</code></pre><hr>
<h3 id="웹의-동작-원리--http-프로토콜">웹의 동작 원리 : HTTP 프로토콜</h3>
<ul>
<li><strong>기본 흐름 : 클라이언트의 요청(Request) -&gt; 처리(Server) -&gt; 응답(Response)</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/993a6927-916d-42d8-829a-5c8ca28e3f53/image.png" alt=""></p>
<ul>
<li><strong>HTTP 메시지 구성</strong> :</li>
</ul>
<pre><code>- 시작 줄 : 메서드(요청 시), 상태 코드(응답 시) 등을 포함함
- 헤더(Header) : 데이터의 형식, 길이, 인증 정보 등 메타데이터가 담김
- 본문(Body) : 실제 전달하려는 데이터가 들어감(선택사항)</code></pre><ul>
<li><strong>HTTP 상태 코드</strong> :</li>
</ul>
<pre><code>- 2xx 성공 : 
    200 OK : 정상 처리,
    201 Created : 리소스 생성 성공,
    204 No Content : 성공했지만 응답 바디없음/ 보여줄게없다
- 3xx 리다이렉션 : 
    301 Moved Permanently : 영구 이동
- 4xx 클라이언트 오류 : 
    400 Bad Request: 요청 형식 오류
    401 Unauthorized: 인증 필요
    403 Forbidden: 권한 없음
    404 Not Found: 리소스 없음
    409 Conflict: 상태 충돌
    422 Unprocessable Entity: 유효성 검증 실패
- 5xx서버 오류 : 
    500 Internal Server Error: 서버 내부 오류
    502 Bad Gateway: 게이트웨이 오류
    503 Service Unavailable: 서비스 불가</code></pre><hr>
<h3 id="rest-api-설게와-데이터-전달">REST API 설게와 데이터 전달</h3>
<ul>
<li><p><strong>REST(Representational State Transfer)</strong> : 자원(Rescource)을 중심으로 HTTP 메서드를 사용하여 통신하는 API 설계 방식</p>
</li>
<li><p><strong>주요 HTTP 메서드(CRUD)</strong> :</p>
</li>
</ul>
<pre><code>- GET : 자원 조회
- POST : 자원 생성
- PUT/PATCH : 자원 전체/일부 수정
- DELETE : 자원 삭제</code></pre><ul>
<li><strong>데이터 전달 방식</strong> :<ul>
<li>Query Parameter: URL 뒤에 ?key=value 형태로 붙여 조회 조건이나 필터를 표현함</li>
<li>Request Body: 요청 메시지 내부에 실제 생성/수정에 필요한 데이터를 담아 보낸다</li>
</ul>
</li>
</ul>
<p><strong>CRUD와 관계</strong></p>
<ul>
<li><p>Create → POST</p>
</li>
<li><p>Read → GET</p>
</li>
<li><p>Update → PUT / PATCH</p>
</li>
<li><p>Delete → DELETE</p>
<p>CRUD는 자원에 대한 대표적인 요청의 개념적 분류이고,
HTTP Method는 이를 실제 HTTP요청으로 표현하는 수단</p>
</li>
</ul>
<hr>
<h3 id="보안과-https">보안과 HTTPS</h3>
<ul>
<li>HTTPS : HTTPS에 TLS(Transport Layer Security)암호화를 더해 통신 내용을 보호하는 보안 프로토콜</li>
<li>암호화 방식 <ul>
<li>대칭키 : 하나의 키로 암,복호화하며 속도가 빠름</li>
<li>공개키 : 공개키로 암호화하고 비밀키로만 복호화하여 키 전달이 안전함</li>
</ul>
</li>
</ul>
<p>_*복호화 : 암호화된 데이터를 원래의 평문(읽을 수 있는 데이터)으로 되돌리는 과정을 말함.쉽게말해, 금고 주인이 열쇠를 사용해 자물쇠를 열고 편지를 꺼내 읽는것.
_</p>
<hr>
<h3 id="http-요청시-주의사항">HTTP 요청시 주의사항</h3>
<ol>
<li>같은 자원이라도 메서드에 따라 전혀 다른 의미의 요청이 된다.<ul>
<li>GET/users  -&gt; 사용자 목록 조회</li>
<li>POST/users -&gt; 사용자 생성</li>
</ul>
</li>
<li>행위는 URL이 아니라 메서드로 표현한다.<ul>
<li>새로운 사용자 추가<ul>
<li>❌ /createUser</li>
<li>⭕ POST /users</li>
</ul>
</li>
<li>주문 삭제<ul>
<li>❌ /deleteOrder</li>
<li>⭕ DELETE /orders/123</li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h3 id="데이터베이스database-기초">데이터베이스(Database) 기초</h3>
<ul>
<li>필요성 : 대량의 데이터를 정리된 구조로 안전하게 저장하고, 여러 사람이 동시에 빠르게 검색,관리하기 위해 사용함</li>
<li>RDBMS(관계형 데이터베이스) : MySQL, PostgreSQL등과 같이 정형 데이터를 테이블 간의 관계(Key)를 통해 관리하는 시스템임</li>
<li>핵심 용어 <ul>
<li>Table : 데이터가 저장되는 표</li>
<li>Primary Key(PK) : 각 행을 유일하게 식별하는 대표 ID</li>
<li>Foreign Key(FK) : 다른 테이블의 PK를 참조하여 데이터 간 관계를 나타내는 키</li>
<li>무결성(Intergrity) : 데이터가 손상되지 않고 정확성과 완전성을 유지하는 상태</li>
</ul>
</li>
</ul>
<hr>
<h3 id="https-동작-방식">HTTPS 동작 방식</h3>
<p> 대칭키 암호화와 공개키 암호화를 함께 사용</p>
<ol>
<li>브라우저 -&gt; 서버 접속 요청</li>
<li>서버 -&gt; 인증서(공개키 포함)전달</li>
<li>브라우저 -&gt; 인증서 신뢰 여부 확인(CA)</li>
<li>브라우저 -&gt; 대칭키를 공개키로 암호화해서 전달</li>
<li>서버 -&gt; 비밀키로 복호화하여 대칭키 획득</li>
<li>이후 통신 -&gt; 대칭키로 빠르게 암호화</li>
</ol>
<p>쉽게 말해,</p>
<blockquote>
</blockquote>
<ol>
<li>client가 -&gt; 브라우제에게 안녕? 얘기좀하자</li>
<li>서버 : 그래 신분증 확인. 근데 나 진짜인지 가짜인지 몰라. ca님 이거 진짜에요?</li>
<li>ca 진품 확인</li>
<li>오 맞네 그럼 우리끼리만 아는 암호로 만들자</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[ResNet과 잔차 학습(Residual Learning)]]></title>
            <link>https://velog.io/@wjdtor_2/ResNet%EA%B3%BC-%EC%9E%94%EC%B0%A8-%ED%95%99%EC%8A%B5Residual-Learning</link>
            <guid>https://velog.io/@wjdtor_2/ResNet%EA%B3%BC-%EC%9E%94%EC%B0%A8-%ED%95%99%EC%8A%B5Residual-Learning</guid>
            <pubDate>Fri, 23 Jan 2026 13:31:47 GMT</pubDate>
            <description><![CDATA[<hr>
<h3 id="1딥러닝-학습의-본질--무한-오답노트">1.딥러닝 학습의 본질 : 무한 오답노트</h3>
<p>딥러닝은 결국 미분을 이용해 정답을 찾아가는 과정이다.</p>
<ul>
<li><strong>순전파(Forward)</strong> : 입력 데이터를 넣고 현재 가중치($W$)로 계산해 답을 내는 단계 (문제풀기)</li>
<li><strong>손실 계산(Loss)</strong> : 내 답과 실제 정답의 차이를 확인. 오차</li>
<li><strong>역전파(Backward)</strong> : 뒤에서부터 거꾸로 올라가며, 미분을 통해 각 가중치가 오차에 얼마나 기여했는지 &#39;책임&#39;을 묻는 단계 (범인 찾기)</li>
<li><strong>최적화(Optimizer)</strong> : 찾은 범인(가중치)들을 수정함. 이 과정을 반복(Epoch)할수록 오차가 줄어든다.</li>
</ul>
<hr>
<h3 id="2-resnet-residual-network">2. ResNet (REsidual Network)</h3>
<p><strong>분류(Classification) 분야</strong>에 <strong>혁신을 일으킨 모델</strong></p>
<ul>
<li>&#39;<strong>깊게 쌓는 것이 무조건 좋은가?</strong>&#39;란 질문에 대답한 모델</li>
</ul>
<p>이론적으로는 층이 깊어질수록 더 복잡한 특징을 배울 수 있어야 함.
하지만 ResNet 이전에는 두 가지 큰 벽에 부딪혔다.</p>
<ol>
<li><strong>기울기 소실(Vanishing Grdient)</strong> : 역전파 시 미분값을 계속 곱하며 앞으로 오는데, 층이 너무 깊으면 값이 0에 가까워져 앞쪽 층이 학습을 멈춰버림.</li>
<li><strong>망의 퇴화(Degradation)</strong> : 층이 깊어질수록 오히려 효율이 떨어져서, 얕은 모델보다 성능이 안 나오는 현상.</li>
</ol>
<p>이 문제들을 해결한 것이 바로 <strong>ResNet</strong>의 <strong>Skip Connection(Shortcut)</strong>임.</p>
<h4 id="✏️-skip-connection-하이패스-길을-뚫어주다">✏️ Skip Connection: &quot;하이패스&quot; 길을 뚫어주다</h4>
<p>다른 자료에서 말하는 &#39;지름길&#39;의 핵심은 불필요한 걸 버리는게 아니라, 최소한의 정보 전달 경로를 확보하는 것임.</p>
<p>기존 방식이 $F(x)$를 통째로 새로 학습해야 했다면, ResNet은 &#39;기존 입력($x$)은 그대로 두고, 거기서 추가로 개선할 부분($F(x)$)만 찾아내라&#39; 고 구조를 바꾼 것임.</p>
<ul>
<li><strong>Identity Mapping</strong> : 입력을 출력에 직접 더해주는 방식.</li>
<li>네트워크는 이제 &#39;차이(Residual)&#39;만 학습하면 됨. 덕분에 레이어가 아무리 많아져도 최소한 이전 레이어만큼의 성능이 보장되며, 1000층 이상의 깊은 망도 안정적으로 학습할 수 있게 되엇다.</li>
</ul>
<ul>
<li><strong>용도</strong> : 이미지 분류(Classification) 및 백본 네트워크(Backbone).</li>
<li><strong>특징</strong> : Skip Connection을 통한 레이어의 심층화</li>
<li><strong>의의</strong> : 현대 딥러닝 아키텍처의 표준(standard)이 됨.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/f276ca22-3890-469e-a33b-fa764651f379/image.jpeg" alt=""></p>
<hr>
<h3 id="💡-질문">💡 질문</h3>
<p><strong>1. 딥러닝이란 자체가 빅데이터를 다루는 건데 ResNet을 안쓰면 안정적이지가 못해지나?</strong></p>
<ul>
<li>ResNet의 &#39;잔차학습(Residual Learnin)&#39;개념이 없었다면 현대 초거대 Ai(GPT등)도 존재하기 어려웠다고함.</li>
<li>문제가 2개가 있는데 이를 해결한게 ResNet.(문제는 2.깊은 망의 역설과 ResNet의 등장)</li>
</ul>
<p><strong>2. 차이만 학습한다는게 앙상블에 있는 부스팅이랑 뭐가 다르지? 부스팅도 이전 모델들의 실수를 다음 모델이 보완해서 학습하는거는 비슷한 학습법같은데?</strong></p>
<ul>
<li><p>실수(잔차)를 보완하며 학습한다 즉, 이전 단계가 해결하지 못한 나머지(잔차)에 집중한다는 건 같지만, 구현방식이나 최종목적에서 큰 차이가 남.</p>
</li>
<li><p>부스팅 : 
독립된 개별 모델들의 집합/순차적(A끝난 뒤 B), 약한모델을 모아 강하게 만들기</p>
</li>
<li><p>ResNet(Residual Learning)
하나의 모델 내의 여러 레이어/동시적(전체 층한꺼번에)/깊은망의 학습 안정성 확보를 위해.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/002e3e86-7844-4e6b-a417-9823f6e87daa/image.png" alt=""></p>
<hr>
<h3 id="3-왜-분류classification에-깊은-레이어가-필요할까">3. 왜 분류(Classification)에 깊은 레이어가 필요할까?</h3>
<p>우리가 보는 이미지가 컴퓨터 입장에서는 &#39;숫자의 바다&#39;일 뿐이라서, 그 안에서 추상적인 개념(개,고양이)를 뽑아내려면 엄청나게 복잡한 단계가 필요하기 때문에</p>
<ol>
<li><p><strong>선 -&gt; 면 -&gt; 형체 -&gt; 개념의 단계 (계층적 학습)</strong>
 컴퓨터는 이미지를 볼때 픽셀의 밝기 값만 본다. 이 숫자들로부터 &#39;강아지&#39;라는 정답을 맞히기 위해서는 단계별 학습이 필요함.</p>
<ul>
<li><strong>초반 레이어(얕은 층)</strong> : 아주 단순한 점, 선, 대각선 같은 특징을 찾는다.</li>
<li><strong>중반 레이어(중간 층)</strong>: 선들이 모여 만든 동그라미, 삼각형, 질감(털모양, 가죽 모양)등을 이해한다.</li>
<li><strong>후반 레이어(깊은 층)</strong> : 이것들을 조합해 &#39;귀의 모양&#39;, &#39;눈의 위치&#39;, &#39;코의 생김새&#39;같은 구체적인 부위를 인식함.</li>
<li><strong>마지막 레이어(마지막 층)</strong> : 이 모든 부위가 모였으니 이것은 골든리트리버다!라고 결론을 내림.</li>
</ul>
</li>
</ol>
<ul>
<li>층이 깊을 수록 더 복잡하고 고차원적인 특징(예:강아지 품종차이 등)을 더 잘 구별해낼수 있음.</li>
</ul>
<ol start="2">
<li><strong>다양한 환경에 대한 불응 (불변성, Inveriance)</strong>
분류 모델은 똑같은 강아지라도 밝은 곳에 있을 때, 어두울때, 옆모습일때, 거꾸로 매달려 있을때 동일한 강아지로 인식해야함. ResNet처럼 층이 깊으면 이런 복잡한 변화들을 층마다 나누어 처리하며 훨씬 견고한 분류성능을 낸다.</li>
</ol>
<hr>
<h3 id="요약">요약</h3>
<p>ResNet은 단순히 층을 깊게 쌓은 모델이 아니라, &#39;잔차 학습&#39;이라는 개념으로 딥러닝의 고속도로를 뚫어준 모델임. 이 구조 덕분에 현대의 GPT 같은 초거대 AI도 탄생할 수 있음. 현대 딥러닝 아키텍처의 진정한 표준(Standard)이라 불릴만하다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Object Detection_성능지표]]></title>
            <link>https://velog.io/@wjdtor_2/Object-Detection%EC%84%B1%EB%8A%A5%EC%A7%80%ED%91%9C</link>
            <guid>https://velog.io/@wjdtor_2/Object-Detection%EC%84%B1%EB%8A%A5%EC%A7%80%ED%91%9C</guid>
            <pubDate>Mon, 19 Jan 2026 08:03:50 GMT</pubDate>
            <description><![CDATA[<p>머신러닝 / 딥러닝 할 때 성능지표를 보는게 중요함!
그래서 이번 happy_face 하면서 성능지표 나온거에 대해
왜 이게 중요하며 어떻게 나온지 한번더 정리하기로함!</p>
<hr>
<h3 id="성능지표">성능지표</h3>
<p>딥러닝 모델은 단순히 &quot;공부를 했다&quot;는 것만으로는 부족함.
얼마나 정확하게(Precision), 하나도 놓치지 않고(Recall), 균형있게(F1-score)
찾아내느냐가 실전 사용의 핵심이기 때문임. 
그래서 runs파일 -&gt; 라벨안에 있는 지표들은 모델의 &#39;성적표&#39;와 같다.</p>
<hr>
<h3 id="resultspng">Results.png</h3>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/b61ce882-f5fd-40c5-aced-7459959289d9/image.png" alt=""></p>
<p>가장 먼저 봐야할 전체 흐름도!</p>
<ul>
<li>loss(손실) : 왼쪽의 train/box_loss, cls_loss 등 그래프가 아래로 같이 떨어지고 있음. 이 모델이 정답과 예측의 차이를 줄여나가며 잘 학습했다는 뜻임.</li>
<li>Metrics(성능) : 오른쪽의 mAP50그래프가 급격히 상승해 1.0(100%)에 도달했음. 이건 모델이 학습 데이터에 대해서는 완벽하게 잘맞히고 있다는걸 의미함.</li>
</ul>
<hr>
<h3 id="precision-recallpr-곡선">Precision-Recall(PR) 곡선</h3>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/293bbf2b-0b0d-4a72-9c2b-77f21e39aca9/image.png" alt=""></p>
<ul>
<li>의미 : 정밀도(Precision)와 재현율(Recall)사이의 관계임.</li>
<li>보는 방법 : 선이 오른쪽 상단 구석(1,1)에 붙어 있을수록 좋음. 현재 그래프는 면적이 0.0995로, 거의 완벽한 사각형을 그림. 매우 우수한 성능이다라는걸 알수있음.</li>
</ul>
<hr>
<h3 id="f1-confidence-곡선">F1-Confidence 곡선</h3>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/f70d80cd-749e-4023-bc6a-192c1ed6dfae/image.png" alt=""></p>
<ul>
<li>의미 : &quot;몇 %이상의 확신이 들 때 박스를 쳐야 가장 효율적일까?&quot;를 알려줌.</li>
<li>보는 방법 : F1-score는 정밀도와 재현율의 평균값임. 그래프를 보면 Confidence 0.0874정도에서 F1-score가 0.99로 최대치가 됨. 즉, 모델이 &quot;이건 87% 확률로 happy_face야&quot;라고 판단할 때 가장 믿을 만하다는 뜻임.</li>
</ul>
<hr>
<h3 id="혼돈-행렬-confusion_matrix">혼돈 행렬 (Confusion_matrix)</h3>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/7d3ba09d-4e18-4e57-8653-61195412ad87/image.png" alt=""></p>
<ul>
<li>의미 : 모델이 헷갈려 하는 클래스가 있는지 보여줌</li>
<li>보는 방법 : 대각선 방향(happy_face vs happy_face)에만 숫자가 있고 나머지는 깨끗함. 즉, 배경을 얼굴로 착각하거나 얼굴을 배경으로 놓치는 실수가 전혀 없다는 뜻임.</li>
</ul>
<hr>
<h3 id="주의할-점-insight">주의할 점 (Insight)</h3>
<p>현재 데이터가 20장으로 매우적은데 지표가 1.0(100%)에 가깝게 나오고 있음.이 두가지 가능성있다고생각함!</p>
<ol>
<li>성공적인 학습 : 대상(happy_face)의 특징이 매우 뚜렷해서 적은 데이터로도 완벽히 학습됨.</li>
<li>과적합(Overfitting)위험 : 학습한게 20장의 사진은 잘 찾았지만, 새로운 장소나 다른 조명에서 찍은 사진을 보여주면 아예 못찾을 확률이 크지않을까</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[딥러닝_기초5일차]]></title>
            <link>https://velog.io/@wjdtor_2/%EB%94%A5%EB%9F%AC%EB%8B%9D%EA%B8%B0%EC%B4%885%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@wjdtor_2/%EB%94%A5%EB%9F%AC%EB%8B%9D%EA%B8%B0%EC%B4%885%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Sat, 17 Jan 2026 08:30:11 GMT</pubDate>
            <description><![CDATA[<p>딥러닝 기초 5일차 정리
전이학습과 AI 개발자의 길!</p>
<hr>
<h3 id="1전이학습transfer-learning">1.전이학습(Transfer Learning)</h3>
<p>모든 모델을 처음부터(From Scratch) 학습시키는 것은 비효율적임. 대규모 데이터로 이미 학습된 모델의 &#39;지식&#39;을 빌려오는 것이 핵심임.</p>
<ul>
<li><p>*<em>Pretrained Model(사전 학습 모델) *</em>: Google이나 Meta같은 곳에서 100만 장 이상의 이미지(ImageNet 등)로 미리 학습시켜둔 모델임.</p>
</li>
<li><p><strong>Transfer Learning의 원리</strong></p>
<ul>
<li>*<em>A(거인) *</em>: 빅데이터와 고사양 GPU로 이미 세상의 특징을 다 배워둔 모델.</li>
<li>*<em>B(나) *</em>: 적은 데이터와 저사양 GPU만 있어도 A의 모델을 가져와 내 문제에 맞춰 &#39;추가 학습&#39;만 하면 됨.</li>
</ul>
</li>
<li><p>** 핵심 용어**</p>
<ul>
<li><strong>Open Weights</strong> : 소스코드처럼 딥러닝 모델의 파라미터(지식)가 공개된 것</li>
<li><strong>Fine-tuning(미세 조정)</strong> : 내 데이터에 맞제 가중치를 미세하게 업데이트하는 과정.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2파인튜닝fine-tuning전략">2.파인튜닝(Fine-tuning)전략</h3>
<p>노션의 &#39;Fine-tuning Strategies&#39;섹션에 있는 내용을 바탕으로 한 실무 전략임.</p>
<ol>
<li><strong>Freeze(동결)</strong> : 기존 모델의 앞부분(특징 추출기)은 이미 똑똑하므로 학습되지 않게 고정함.</li>
<li><strong>Classifier / Head 교체</strong> : 기존 모델의 마지막 출력층을 내 문제(예: 강아지/고양이 2진 분류)에 맞는 개수로 갈아 끼운다.</li>
<li><strong>Train</strong> : 내가 가진 소량의 데이터로 교체된 Head 부분 위주로 학습을 진행함.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/cdcb799e-c5e8-49b1-8965-eeebe8ee6872/image.webp" alt=""></p>
<hr>
<h3 id="3실무에서의-ai-성능-향상-팁">3.실무에서의 AI 성능 향상 팁</h3>
<ul>
<li><strong>데이터 정규화 (Normalization)</strong> : 특정 데이터의 수치가 너무 크면 모델이편향되므로, 반드시 0~1사이 혹은 표준정규분포로 맞춰야 함.</li>
<li><strong>Batch Normalization</strong> : 각 레이어 사이에서 데이터 분포를 조정하여 학습 속도를 높이고 안정화함.</li>
<li><strong>통계적 검증 (t-test)</strong> : 내 모델이 이전보다 좋아졌을 때, 그것이 우연인지 아니면 정말 실력이 좋아진 것인지 통계적으로 증명하는 능력이 중요함!!</li>
</ul>
<hr>
<h3 id="💡-5일간의-과정을-마치며">💡 5일간의 과정을 마치며</h3>
<p>5일간 딥러닝의 본질부터 실전 기법까지 쉼 없이 달려왔고 달려가는중 ! 단순히 코드를 복붙하는 개발자가 아니라, 모델의 구조를 이해하고 결과물을 엄격하게 평가할 수 있는 AI 엔지니어로 성장해보자!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[딥러닝_기초4일차]]></title>
            <link>https://velog.io/@wjdtor_2/%EB%94%A5%EB%9F%AC%EB%8B%9D%EA%B8%B0%EC%B4%884%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@wjdtor_2/%EB%94%A5%EB%9F%AC%EB%8B%9D%EA%B8%B0%EC%B4%884%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Sat, 17 Jan 2026 08:00:30 GMT</pubDate>
            <description><![CDATA[<p>딥러닝 기초 4일차 정리!
데이터 맞춤형 구조 (CNN &amp; RNN),
모든 데이터를 MLP로만 해결할수 있을까? 딥러닝의 진정한 강점은 데이터의 특성에 맞춰
행렬곱의 구조를 커스터마이즈 할수 있다는 점!</p>
<hr>
<h3 id="1이미지의-공간-정보를-읽는-cnnconvolutional-neural-network">1.이미지의 공간 정보를 읽는 CNN(Convolutional Neural Network)</h3>
<p>이미지는 인접한 픽셀 간의 관계가 중요함. 이를 위해 탄생한 것이 합성곱(Convolution) 연산임.</p>
<ul>
<li><p><strong>핵심 개념</strong></p>
<ul>
<li><strong>Convolution(합성곱)</strong> : 필터(커널)가 이미지를 스캔하며 특징을 추출함. 과거에는 사람이 필터를 설계했지만, CNN은 필터 속 숫자 자체를 학습함.</li>
<li><strong>Padding &amp; Stride</strong> : 이미지 크기를 유지하거나(Padding), 필터가 이동하는 간격(Stride)을 조절하여 특징 추출의 밀도를 결정함.</li>
<li><strong>Pooling</strong> : 중요한 정보만 남기고 데이터 사이즈를 줄여 계산 효율을 높인다.(예 : Max Pooling)</li>
</ul>
</li>
<li><p><strong>장점</strong></p>
<ul>
<li>이미지의 지역적 특징을 잘 잡아낸다.</li>
<li>슬라이딩 윈도우 방식이지만, 같은 필터로 한꺼번에 연산이 가능해** 병렬 연산(GPU)**에 매우 유리함.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/cda264b1-26d0-4a9d-a970-7615e8ab1866/image.webp" alt=""></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/3f07a64e-5565-42ed-b93e-563ec178ce44/image.webp" alt=""></p>
<hr>
<h3 id="2흐름을-기억하는-rnnrecurrent-neural-network">2.흐름을 기억하는 RNN(Recurrent Neural Network)</h3>
<p>주식 가격, 날씨, 텍스트처럼 순서(Sequence)가 중요한 데이터에 사용됨.</p>
<ul>
<li><p><strong>핵심 구조</strong> : 이전 시점(t-1)의 연산 결과가 다음 시점(t)의 입력으로 들어가는 순환 구조임.</p>
</li>
<li><p><strong>한계점</strong></p>
<ul>
<li><strong>Long-term Dependency</strong> : 문장이 길어지면 앞부분의 정보를 잊어버리는 &#39;기억력의 한계&#39;가 있음</li>
<li><strong>병렬 연산 불가</strong> : 이전 연산이 끝나야 다음 연산을 할 수 있어 속도가 느림.</li>
</ul>
</li>
<li><p><strong>진화된 RNN</strong></p>
<ul>
<li>*<em>LSTM / GRU *</em>: &#39;Gate&#39;라는 구조를 도입하여 어떤 정보를 기억하고 잊을지 스스로 결정하게 만들어 RNN의 단점을 보완함.</li>
<li>실무에서는 보통 양방향 <strong>다층(Bi-LSTM)구조</strong>가 국룰로 통함.</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/11294a74-0116-494e-8968-1f2ef6536bd5/image.webp" alt=""></p>
<hr>
<h3 id="3cnn-vs-rnn-vs-transformer-연산-효율성-비교">3.CNN vs RNN vs Transformer (연산 효율성 비교)</h3>
<p>수업에서 강조된 시간/공간 복잡도와 병렬 연산 가능 여부임.</p>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/68e901e5-3e25-4ee3-a440-dd5e71aae93f/image.png" alt=""></p>
<hr>
<h3 id="4실무-데이터-shape-이해하기">4.실무 데이터 Shape 이해하기</h3>
<p>RNN류를 다룰 때 가장 헷갈리는 데이터 형태를 꼭 기억해야함!!</p>
<ul>
<li><strong>Input Shape</strong> : (배치 사이즈, 시계열 길이, 특징 차원)</li>
<li><strong>예시</strong> : 환자 1명의 5일간 맥박/체온/호흡 데이터 -&gt; (1, 5, 3)</li>
</ul>
<hr>
<h3 id="💡요약">💡요약</h3>
<ol>
<li>이미지는 CNN으로 공간적 특징(필터 학습)을 잡는다.</li>
<li>시계열은 <strong>RNN(LSTM)</strong>으로 흐름을 잡지만, 병렬 연산이 안 된다는 단점이 있다.</li>
<li>현재는 RNN의 단점을 극복하고 병렬 연산이 가능한 Transformer 구조가 대세로 자리 잡았다.</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[딥러닝_기초3일차]]></title>
            <link>https://velog.io/@wjdtor_2/%EB%94%A5%EB%9F%AC%EB%8B%9D%EA%B8%B0%EC%B4%883%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@wjdtor_2/%EB%94%A5%EB%9F%AC%EB%8B%9D%EA%B8%B0%EC%B4%883%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Sat, 17 Jan 2026 07:18:49 GMT</pubDate>
            <description><![CDATA[<p>딥러닝 기초 3일차 정리
모델의 실전 근육키우기!! (일반화 &amp; 정규화)</p>
<hr>
<h3 id="1딥러닝의-영원한-숙제--과적합overfitting">1.딥러닝의 영원한 숙제 : 과적합(Overfitting)</h3>
<ul>
<li><strong>과적합</strong> : 모델이 학습 데이터의 패턴뿐만 아니라 노이즈(불필요한 정보)까지 통째로 외워버린 상태.</li>
<li>*<em>현상 *</em> : 학습 데이터에 대한 Loss는 계속 떨어지는데, 검증(Validation) 데이터에 대한 Loss는 오히려 올라감.</li>
<li><strong>목표</strong> : 훈련 오차와 데이터 오차 사이의 간극을 줄여 &#39;일반화(Generalization)&#39;를 달성하는 것.</li>
</ul>
<hr>
<h3 id="2역전파backpropagation와-옵티마이저-심화">2.역전파(Backpropagation)와 옵티마이저 심화</h3>
<p>어떻게 가중치를 효율적으로 업데이트할 것인가에 대한 고민임.</p>
<ul>
<li><strong>역전파</strong> : 출력층의 오차를 입력층 방향으로 거슬러 올라가며 전파하는 과정임. 이때 Chain Rule(연쇄법칙)이 핵심적인 역할을 함.</li>
<li><strong>옵티마이저(Optimizer)의 진화</strong><ul>
<li><strong>SGD</strong> : 가장 기본적인 경사하강법. 단순하지만 일반화 성능이 의외로 좋을 때가 있음.</li>
<li><strong>Momentum</strong> : 관성을 추가하여 경사면을 따라 더 빠르게 내려가게 돕는다.</li>
<li><strong>Adm</strong> : 현재 가낭 많이 쓰는 국룰 옵티마이저. 방향(Momentum)과 보폭(RMSProp)을 동시에 고려하여 똑똑하게 최적화함.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="3과적합을-막는-정규화regularizatin-기법">3.과적합을 막는 정규화(Regularizatin) 기법</h3>
<p>모델이 특정 데이터에만 매몰되지 않도록 &#39;제약&#39;을 거는 방법들임.</p>
<ol>
<li><strong>Weight Decay(L2 Regularization)</strong> : 가중치($W$) 값이 너무 커지지 않게 패널티를 줌. 모델이 특정 피처에 과도하게 의존하는 것을 방지함.</li>
<li><strong>Dropout(드롭아웃)</strong> : 학습 시 무작위로 일부 노드르 &#39;꺼버림&#39;.이를 통해 모델이 특정 경로에 의존하지 않고 더 강건하(Robust)특징을 배우게 함.</li>
<li><strong>Batch Normalization(배치 정규화)</strong> : 각 레이어를 통과할 때마다 데이터의 분포를 일정하게 맞춰줌. 학습 속도가 빨라지고 초기화에 덜 민감해지는 효과가 있음.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/54e248a1-afa4-4497-a2d2-d0cde05af4a4/image.webp" alt=""></p>
<hr>
<h3 id="4실무-필수-테크닉--조기-종료early-stopping">4.실무 필수 테크닉 : 조기 종료(Early Stopping)</h3>
<p>수능 공부도 너무 오래 하면 오히려 역효과가 나듯, 딥러닝도 적절할 때 멈춰야 함.</p>
<ul>
<li><strong>원리</strong> : 매 에폭(Epoch)마다 검증 데이터의 손실(Val Loss)을 체크함.</li>
<li><strong>판단 기준</strong> : Val Loss가 일정 횟수(patience) 이상 개선되지 않으면 학습을 강제로 중단하고, 가장 성능이 좋았던 시점의 모델을 저장함.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/b27920dc-d0a0-486e-9662-d2880fc712f9/image.webp" alt=""></p>
<hr>
<h3 id="💡-요약">💡 요약</h3>
<ol>
<li>모델의 목표는 학습 데이터가 아닌 새로운 데이터를 잘 맞히는 것이다.</li>
<li>Adam 옵티마이저와 Dropout은 실무에서 가장 먼저 고려되는 옵션이다.</li>
<li>Early Stopping을 통해 과적합이 발생하기 직전의 가장 좋은 모델을 확보해야 한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[딥러닝_기초2일차]]></title>
            <link>https://velog.io/@wjdtor_2/%EB%94%A5%EB%9F%AC%EB%8B%9D%EA%B8%B0%EC%B4%882%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@wjdtor_2/%EB%94%A5%EB%9F%AC%EB%8B%9D%EA%B8%B0%EC%B4%882%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Sat, 17 Jan 2026 07:02:48 GMT</pubDate>
            <description><![CDATA[<p>딥러닝 기초 2일차 정리</p>
<hr>
<h3 id="1ml복습--결정트리의-진화-앙상블ensemble">1.ML복습 : 결정트리의 진화, 앙상블(Ensemble)</h3>
<p>데이터가 정형(표)형태이고 양이 아주 많지 않을 때는 여전히 강력한 도구임.</p>
<ul>
<li><strong>배깅(Bagging)</strong> : 병렬 구조. 여러 트리를 랜덤하게 만들어 투표함(예:Random Forest)</li>
<li><strong>부스팅(Boosting)</strong> : 직렬 구조. 이전 트리가 틀린 문제를 다음 트리가 집중 학슴함. 가중 평균을 통해 합쳐짐(예 : XGBoost, LightGBM)<ul>
<li><strong>Tip</strong> : LightGBM이 XGBoost보다 속도가 훨씬 빨라 실무에서 선호된다함.</li>
</ul>
</li>
<li><strong>결론</strong> : 표 형태의 데이터 + 단순 분류 문제라면 Random Forest나 LightGBM이 가성비 최고임.</li>
</ul>
<hr>
<h3 id="2왜-딥러닝인가-선형-vs-비선형">2.왜 딥러닝인가 (선형 vs 비선형)</h3>
<ul>
<li><strong>선형적 관계</strong> : 운동 시간-칼로리 소모처럼 정비례하거나 반비례하는 단순한 관계.</li>
<li><strong>비선형적 관계</strong> : 공부 시간-성적(계단식), 가격-만족도(지수적)처럼 복잡한 관계.</li>
<li><strong>딥러닝의 존재 이유</strong> : 현실 세계의 데이터는 대부분 비선형적임. 딥러닝은 활성화 함수를 통해 이 비선형성을 완벽하게 모사함.</li>
</ul>
<hr>
<h3 id="3mlpmulti-layer-perceptron의-설계-원리">3.MLP(Multi-Layer Perceptron)의 설계 원리</h3>
<p>퍼셉트론을 층층이 쌓은 구조.</p>
<ul>
<li><strong>지식 저장소(Parameter)</strong> : 가중치($W$)와 편향($b$)의 총합임. 이 숫자들의 모델의&#39;지능&#39;을 결정하며 GPU메모리를 차지하는 주범임.</li>
<li><strong>너비 vs 깊이</strong><ul>
<li>노드(너비)를 늘리는 것보다 레이어(깊이)를 쌓는 것이 모델의 표현력을 높이는 데 훨씬 효율적임.</li>
</ul>
</li>
<li><strong>활성화 함수의 배치</strong> : 선형 연산($Wx+b$) 뒤에 반드시 비선형(ReLU 등)을 붙여야 층을 쌓는 의미가 생긴다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/dbc38601-36cc-435f-9c53-39bd8e73b711/image.webp" alt=""></p>
<hr>
<h3 id="4모델-평가의-딜레마-성능-지표-제대로-알기">4.모델 평가의 딜레마: 성능 지표 제대로 알기</h3>
<p>단순히 &quot;정확도(Accuracy)가 높다&quot;고 좋은 모델이 아님.</p>
<ul>
<li><p><strong>임계치(Threshold)</strong> : 0.5라는 기준은 절대적이지 않음. 데이터 분포에 따라 조절해야 함.</p>
</li>
<li><p><strong>주요 지표 4총사</strong></p>
<ol>
<li><strong>정확도(Accuracy)</strong> : 데이터 불균형(예:의귀질환 0.1%)시 무의미함. 전부 음성이라 해도 99.9%가 나오기 때문임.</li>
<li><strong>민감도(Recall/Sensitivity)</strong> : &quot;실제 암 환자 중 암이라고 맞춘 비율&quot;. 놓치면 안 되는 문제에서 중요!</li>
<li><strong>특이도(Specificity)</strong> : &quot;정상인 중 정상이라고 맞춘 비율&quot;</li>
<li><strong>정밀도(Precision)</strong> : &quot;암이라고 예측한 사람 중 실제 환자 비율&quot;.</li>
</ol>
</li>
<li><p><strong>ACROC</strong> : 임계치를 변화시키며 민감도와 특이도의 트레이드오프 관계를 한눈에 평가하는 지표임. 1에 가까울수록 좋은 모델임.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/2ca82305-f887-4c8b-899f-4a1f1979eaac/image.webp" alt=""></p>
<hr>
<h3 id="💡요약">💡요약</h3>
<ol>
<li>정형 데이터에는 앙상블(LGBM 등) 모델이 효율적이다.</li>
<li>딥러닝 모델의 성능은 파라미터의 양과 층의 깊이에 비례한다.</li>
<li>모델 평가는 정확도뿐만 아니라 Recall, Precision, AUROC를 종합적으로 봐야 한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[딥러닝기초_정리]]></title>
            <link>https://velog.io/@wjdtor_2/%EB%94%A5%EB%9F%AC%EB%8B%9D%EA%B8%B0%EC%B4%88%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@wjdtor_2/%EB%94%A5%EB%9F%AC%EB%8B%9D%EA%B8%B0%EC%B4%88%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sat, 17 Jan 2026 06:44:35 GMT</pubDate>
            <description><![CDATA[<p>이번주 딥러닝 기초에 대해서 배웠다.
내용들을 정리하면서 한번더 공부하기!</p>
<hr>
<h3 id="1-머신러닝-vs-딥러닝">1. 머신러닝 vs 딥러닝</h3>
<p>모든 문제에 딥러닝이 정답은 아님. 데이터와 문제의 복잡도에 따라 선택해야 한다.</p>
<ul>
<li><strong>머신러닝(ML)</strong> : 데이터가 숫자로 가볍게 표현되고 양이 적을 때 사용한다.(예 : 선형회귀, 결정트리, XGBoost 등)</li>
<li><strong>딥러닝(DL)</strong> : 이미지, 자연어처럼 숫자로 바로 표현하기 어렵고 데이터 양이 방대할 때 필수적이다. 즉, 인공신경망을 활용해 복잡한 비선형 문제를 해결하는 과정이다.</li>
</ul>
<hr>
<h3 id="2-딥러닝의-핵심-단위--퍼셉트로perceptron">2. 딥러닝의 핵심 단위 : 퍼셉트로(Perceptron)</h3>
<p>딥러닝은 아주 단순한 &#39;퍼셉트론&#39;을 수없이 많이 쌓아 올린 것이다.</p>
<ul>
<li><strong>구조</strong> : 입력값($x$)에 가중치($w$)를 곱하고 편향($b$)을 더한 뒤, 활성화 함수를 통과시킨다.</li>
<li><strong>활성화 함수(Activation Function)</strong><ul>
<li>신경망에 비선형성을 부여하는 마법의 도구임. 이게 없다면 층을 아무리 쌓아도 단층 모델과 다를 바가 없음.</li>
<li><strong>sigmoid</strong> : 결과를 0~1사이로 수렴하게 만든다.</li>
<li><strong>ReLU</strong> : 음수 0, 양수는 그대로 통과시키는 가장 대중적인 함수임.</li>
<li><strong>Softmax</strong> : 마지막 출력층에서 여러 선택지 중 확률이 가장 높은 것을 고를 때 사용함.</li>
</ul>
</li>
</ul>
<p> <img src="https://velog.velcdn.com/images/wjdtor_2/post/41ccfd40-7e50-471f-98b5-0ce294724681/image.webp" alt=""></p>
<hr>
<h3 id="3학습의-매커니즘--순전파와-역전파">3.학습의 매커니즘 : 순전파와 역전파</h3>
<p>학습은 결국 손실함수를 0으로 만드는 과정임.</p>
<ol>
<li><strong>순전파(Forward Pass)</strong> : 데이터가 입려긍에서 출력층으로 흐르며 예측값을 내놓는 과정임.</li>
<li>*<em>손실 함수(Loss Function) *</em>: &quot;내 예측이 얼마나 틀렸나?&quot;를 수치화함.(회귀:MSE, 분류:CrossEntropy 등)</li>
<li><strong>역전파(Backward Pass)</strong> : 미분(Gradient)을 통해 출력층에서 거꾸로 올라가며 가중치를 수정함.</li>
<li><strong>경사하강법(Gradient Descent)</strong> : 손실 함수의 기울기를 따라 낮은 곳(정답)으로 한 발자국씩 이동하는 최정화 기법임.</li>
</ol>
<hr>
<h3 id="4실전-준비--pytorch-tensor-조작하기">4.실전 준비 : PyTorch Tensor 조작하기</h3>
<p>딥러닝의 모든 연산은 행렬곱이며, 이를 처리하는 단위가 바로 Tensor임.</p>
<ul>
<li><p><strong>Tensor</strong> : GPU 연산이 가능한 다차원 행렬임</p>
</li>
<li><p><strong>필수 체크 사항</strong></p>
<ul>
<li><strong>shape</strong> : 데이터의 차원을 확인하는 습관!</li>
<li>*<em>device *</em>: 현재 데이터가 CPU가 있는지 GPU(CUDA)에 있는지 확인해야 연산 에러가 안난다.</li>
</ul>
</li>
<li><p><strong>핵심 조작</strong></p>
<ul>
<li><strong>view / reshape</strong> : 데이터의 전체 개수는 유지하면서 모양만 바꾼다.(가장 많이 씀!)</li>
<li><strong>transpose / permute</strong> : 차원의 순서를 바꿀 때 사용함.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/e29708db-6f5d-4dad-8965-07b10ba2b65c/image.webp" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[YOLOv8 + Label Studio로 객체감지 모델만들기]]></title>
            <link>https://velog.io/@wjdtor_2/YOLOv8-Label-Studio%EB%A1%9C-%EA%B0%9D%EC%B2%B4%EA%B0%90%EC%A7%80-%EB%AA%A8%EB%8D%B8%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@wjdtor_2/YOLOv8-Label-Studio%EB%A1%9C-%EA%B0%9D%EC%B2%B4%EA%B0%90%EC%A7%80-%EB%AA%A8%EB%8D%B8%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sun, 11 Jan 2026 11:24:08 GMT</pubDate>
            <description><![CDATA[<p>최근 코딩 공부를 하다보니 <span style="color:indianred">CV(Computer Vision)</span>에 관심이 생겼다.
그래서 오늘 해본건 !
<strong>YOLOv8</strong>을 사용해 <strong>행복한 얼굴(happy_face)</strong>을 <strong>객체 감지(Object Detection)방식</strong>으로 <strong>학습</strong>했다.
물론 기본개념 어떤 분류들이있는지 먼저 공부했다.</p>
<hr>
<h3 id="객체-분류object-classification">객체 분류(Object Classification)</h3>
<p>: 이미지 전체가 어떤 클래스인지 분류하는 작업</p>
<ul>
<li>예 : 이 사진은 happy_face 인가? not_happy인가?</li>
<li>결과 : 하나의 라벨만 출력</li>
</ul>
<p>📌 특징</p>
<ul>
<li>위치 정보 X</li>
<li>가장 기본적인 CV 태스크</li>
</ul>
<hr>
<h3 id="객체-식별object-recognition">객체 식별(Object Recognition)</h3>
<p>: 이미지 속에 어떤 객체가 &quot;존재하는지&quot;를 인식하는 작업, object detection의 개념일부</p>
<ul>
<li>예 : 사진 안에 사람이 있는가? 얼굴이 있는가?</li>
<li>결과 : 객체 종류 인식(위치는 없음)</li>
</ul>
<p>📌 분류와 차이</p>
<ul>
<li>분류는 &quot;전체 이미지&quot;</li>
<li>식별은 &quot;이미지 안에 무엇이 있나&quot;</li>
</ul>
<hr>
<h3 id="객체-탐지--위치-추정object-detection--object-localization">객체 탐지 &amp; 위치 추정(Object Detection &amp; Object Localization)</h3>
<p>: 객체의 종류 + 위치(Bounding Box)를 함께 찾는 작업</p>
<ul>
<li>예 : 웃는 얼굴이 어디에 있는지</li>
<li>결과 : 클래스 + 박스 좌표</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/fd1bf9e3-c307-4bbe-b00c-13ac1f8f1b92/image.png" alt=""></p>
<p>📌 YOLO가 바로 이 작업
📌 이번 프로젝트에서 사용한 방식</p>
<hr>
<h3 id="객체-분할object-segmentationinstance-segmentation">객체 분할(Object Segmentation/Instance Segmentation)</h3>
<p>: 이미지의 모든 픽셀을 클래스 단위로 분류</p>
<ul>
<li>예 : 얼굴/배경/머리카락 영역을 픽셀 단위로 구분</li>
<li>결과 : 같은 클래스는 하나의 영역으로 처리</li>
</ul>
<p>📌 특징</p>
<ul>
<li>픽셀 단위</li>
<li>같은 객체 여러 개여도 하나로 취급</li>
</ul>
<hr>
<h3 id="인스턴스-분할instance-segmentation">인스턴스 분할(Instance Segmentation)</h3>
<p>: 객체 하나하나를 픽셀 단위로 분리</p>
<ul>
<li><p>예 : 얼굴이 3개 있으면 각 얼굴을 개별 마스크로 분리</p>
</li>
<li><p>결과 : 클래스 + 개별 객체 마스크</p>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/9606b4bf-9343-4c18-abf4-2c8c85319358/image.png" alt=""></p>
</li>
</ul>
<p>📌 Semantic Segmentation보다 한 단계 더 정밀
📌 Mask R-CNN, YOLOv8-seg 등 사용</p>
<hr>
<h3 id="자세-추정pose-estimation">자세 추정(Pose Estimation)</h3>
<p>: 사람 또는 객체의 관절·키포인트를 추정하는 작업 ,졸라맨같은거</p>
<ul>
<li>예 : 눈, 코, 입, 어깨, 팔꿈치 위치 추정</li>
<li>결과 : 스켈레톤 형태의 포인트</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/542971f1-e95c-43ac-be67-a9d93e06856e/image.png" alt=""></p>
<p>📌 운동 분석, 헬스케어, 모션 캡처에 활용</p>
<hr>
<h3 id="동영상-분석video-analysis">동영상 분석(Video Analysis)</h3>
<p>: 연속된 프레임을 분석해 시간적 변화까지 고려</p>
<ul>
<li>예 : 영상에서 사람 추적, 행동 변화 분석</li>
<li>결과 : 프레임 간 객체 추적/이벤트 탐지</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/136b01f3-aa15-4d10-a796-8d0a206d5f02/image.png" alt=""></p>
<p> 📌 이미지 + 시간 개념 추가</p>
<hr>
<h3 id="행동-인식activity-recognition">행동 인식(Activity Recognition)</h3>
<p>: 사람의 동작이나 행동을 인식하는 작업</p>
<ul>
<li>예 : 걷기,뛰기,웃기,넘어지기</li>
<li>결과 : 행동 클래스</li>
</ul>
<p>📌 보안, 헬스케어, 스포츠 분석에 활용</p>
<hr>
<h3 id="이미지-해상도-개선resolution-enhancing">이미지 해상도 개선(Resolution Enhancing)</h3>
<p>: 저해상도 이미지를 고해상도로 복원하는 작업</p>
<ul>
<li><p>예 : 흐릿한 얼굴을 선명하게 복원</p>
</li>
<li><p>결과 : 픽셀 품질 향상된 이미지</p>
<p>📌 ESRGAN, Real-ESRGAN 등 사용</p>
</li>
</ul>
<hr>
<h3 id="📌프로젝트-목표">📌프로젝트 목표</h3>
<ul>
<li><strong>Task</strong> : 얼굴 이미지에서 행복한 얼굴 감지</li>
<li><strong>Model</strong> : YOLOv8(Ultralytics)</li>
<li><strong>Labeling Tool</strong> : Label Studio</li>
<li><strong>환경</strong> : macOS + 가상환경</li>
<li><strong>데이터 수</strong> : 이미지 20장 (실습/파이프라인 검증용)</li>
</ul>
<hr>
<h3 id="✏️yolo-object-detection-라벨-형식-이해">✏️YOLO Object Detection 라벨 형식 이해</h3>
<p><strong>YOLO</strong>는 이미지마다 <strong>txt 라벨 파일 1개</strong>를 사용한다.</p>
<p>YOLO 라벨 형식</p>
<pre><code class="language-txt">class_id x_center y_ceter width height
</code></pre>
<ul>
<li>좌표는 0~1 사이로 정규화</li>
<li>class_id는 0부터 시작</li>
<li>이번 프로젝트에서는 :</li>
</ul>
<pre><code class="language-text">0 = happy_face</code></pre>
<hr>
<h3 id="✏️label-studio로-라벨링-환경-구성">✏️Label Studio로 라벨링 환경 구성</h3>
<p><a href="https://labelstud.io/label-studio-oss/?utm_adgroup=Label-Studio&amp;utm_source=google&amp;utm_medium=paidsearch&amp;utm_campaign=Branded-Label-Studio-97F&amp;utm_term=label%20studio&amp;hsa_acc=5127568629&amp;hsa_cam=20302203335&amp;hsa_grp=168126174141&amp;hsa_ad=723355426945&amp;hsa_src=g&amp;hsa_tgt=aud-2016956938303:kwd-301606282680&amp;hsa_kw=label%20studio&amp;hsa_mt=b&amp;hsa_net=adwords&amp;hsa_ver=3&amp;gad_source=1&amp;gad_campaignid=20302203335&amp;gbraid=0AAAAAo5UffLQ3DheTDY12ou2VSQ9ZTkQ9&amp;gclid=Cj0KCQiAsY3LBhCwARIsAF6O6XhqXIUmRaMRmWGqAXYTfKAKE48cMG6iUdwKmZB_BqQ6hzWvGTVEPswaAupyEALw_wcB">라벨스튜디오 바로가기</a></p>
<p><strong>프로젝트 생성</strong></p>
<ul>
<li>Label Studio 실행</li>
<li>새 프로젝트 생성</li>
</ul>
<p><strong>라벨링 인터페이스(XML)설정</strong>
Object Detection (Bounding Box)용 XML을 직접 설정했다.</p>
<pre><code class="language-xml">&lt;View&gt;
  &lt;Image name=&quot;image&quot; value=&quot;$image&quot;/&gt;
  &lt;RectangleLabels name=&quot;label&quot; toName=&quot;image&quot;&gt;
    &lt;Label value=&quot;happy_face&quot;/&gt;
  &lt;/RectangleLabels&gt;
&lt;/View&gt;</code></pre>
<p>이 설정이 없으면 박스 그리는 도구 자체가 안 뜬다</p>
<hr>
<h3 id="✏️이미지-import--라벨링">✏️이미지 Import &amp; 라벨링</h3>
<ul>
<li>이미지 20장 업로드</li>
<li>Label 버틍 클릭 -&gt; 라벨링 화면 진입</li>
<li>사각형(Rectangle) 도구로 얼굴 전체에 박스</li>
<li>웃는 얼굴이 없는 이미지는 박스 없이 저장</li>
</ul>
<blockquote>
<p>박스가 없는 이미지도 정상적인 데이터
(negative sample 역할)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/d68aeb1b-6e40-464d-9a66-79cc0ad87853/image.png" alt=""></p>
<hr>
<h3 id="✏️라벨링-결과json확인">✏️라벨링 결과(JSON확인)</h3>
<p>Label Studio 내부 annotation 결과 예시 :</p>
<pre><code class="language-json">&quot;result&quot;: [
  {
    &quot;type&quot;: &quot;rectanglelabels&quot;,
    &quot;value&quot;: {
      &quot;x&quot;: 15.36,
      &quot;y&quot;: 38.23,
      &quot;width&quot;: 83.23,
      &quot;height&quot;: 46.77,
      &quot;rectanglelabels&quot;: [&quot;happy_face&quot;]
    }
  }
]</code></pre>
<ul>
<li>박스 있음</li>
<li>라벨 있음</li>
<li>YOLO 변환 가능</li>
</ul>
<hr>
<h3 id="✏️yolo-형식으로-export">✏️YOLO 형식으로 Export</h3>
<p>Label Studio Export 옵션 중에서</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/14073dd6-c579-4c1e-9c60-d703905b9df8/image.png" alt=""></p>
<p>선택함.</p>
<ul>
<li>이미지 + 라벨(txt) 함께 다운로드</li>
<li>YOLOv8 학습에 바로 사용 가능</li>
</ul>
<hr>
<h3 id="✏️yolov8-학습용-폴더-구조">✏️YOLOv8 학습용 폴더 구조</h3>
<pre><code class="language-test">yolo_dataset/
├── images/
│   ├── train/
│   └── val/
├── labels/
│   ├── train/
│   └── val/
└── data.yaml

</code></pre>
<ul>
<li>images/val, labels/val 폴더는 비어있어도 <strong>반드시 존재</strong>해야함.</li>
</ul>
<hr>
<h3 id="✏️datayaml-작성중요">✏️data.yaml 작성(중요)</h3>
<pre><code class="language-yaml">path: /Users/admin/yolo_dataset

train: images/train
val: images/val

names:
  0: happy_face</code></pre>
<p>❗ macOS에서는 경로 앞에 &#39;/&#39; 필수</p>
<hr>
<h3 id="✏️yolov8-학습-실행">✏️YOLOv8 학습 실행</h3>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/f82255c2-7557-4d78-b68b-4a8a9283e727/image.png" alt=""></p>
<hr>
<h4 id="❗❗중간에-겪은-주요-에러--해결❗❗">❗❗중간에 겪은 주요 에러 &amp; 해결❗❗</h4>
<ol>
<li>data.yaml 경로 에러</li>
</ol>
<pre><code class="language-text">Dataset &#39;Users/admin/...&#39; does not exist</code></pre>
<p>-&gt;원인 : 절대경로 &#39;/&#39;누락</p>
<hr>
<ol start="2">
<li>images/val not found 에러</li>
</ol>
<pre><code class="language-text">images not found, missing path images/val</code></pre>
<ul>
<li>원인 :<ul>
<li>val 폴더 없음</li>
<li>data.yaml의 path와 실제 폴더 불일치</li>
</ul>
</li>
<li>해결 :</li>
</ul>
<pre><code class="language-bash">mkdir -p images/val labels/val</code></pre>
<hr>
<h3 id="✏️결과-확인">✏️결과 확인</h3>
<p>학습 완료 후 :</p>
<ul>
<li>loss 정상 감소</li>
<li>예측 시 웃는 얼굴에만 박스 생성</li>
<li>runs 폴더 생성됨</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/8ec18f5e-fa7a-4044-a83e-f8ebfb4b375c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/1d79f768-4e53-49c7-aced-a07e5fc3e655/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[흡연 예측 프로젝트 _개인]]></title>
            <link>https://velog.io/@wjdtor_2/%ED%9D%A1%EC%97%B0-%EC%98%88%EC%B8%A1-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%9C%EC%9D%B8</link>
            <guid>https://velog.io/@wjdtor_2/%ED%9D%A1%EC%97%B0-%EC%98%88%EC%B8%A1-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%9C%EC%9D%B8</guid>
            <pubDate>Sun, 04 Jan 2026 13:25:30 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/355b3546-4ebf-4a35-a35e-d910aad215b3/image.jpeg" alt="">
<img src="https://velog.velcdn.com/images/wjdtor_2/post/9822eeec-fd5f-4c6e-9917-9942b7d9aad4/image.pdf" alt="">
오늘은 부트캠프에서 밤낮으로 머리 싸매고 고민했던 &#39;건강검진 데이터 기반 흡연 여부 예측프로그램&#39;을 포스팅하려고한다. 예전 미니프로젝트로 팀원들과 한번 해보기했지만, 사실 처음에는 &quot;데이터로 담배 피우는지 안 피우는지 맞힐수 있다고?&quot; 하며 반신반의했는데, 하면 할수록 데이터의 세계는 신기한 그자체!!
원래 1/2일 부터였는데 스케줄이 변경되어서 12/31~1/4까지 미니 프로젝트를 하기로 했다.
방금 막 ppt를 만들고 제출 후에 정리도 할겸 벨로그작성!</p>
<h3 id="1-이번-프로젝트의-목표는">1. 이번 프로젝트의 목표는?</h3>
<p>내 목표는 단순했다. 단순히 정답만 잘 맞히는 AI가 아니라, &#39;왜 이사람이 흡연자라고 생각해?&#39; 라는 질문에 당당하게 답할 수 있는 똑똑한 프로그램을 만들어 보는것!</p>
<hr>
<h3 id="2-데이터랑-친해지기eda">2. 데이터랑 친해지기(EDA)</h3>
<p>우선 데이터부터 꼼꼼히 뜯어봄. 혈압,콜레스테롤,간 수치까지 데이터가 정말 방대했닿...ㅎ</p>
<ul>
<li>의외의 발견 : 분석해 보니 &#39;헤모글로빈&#39;수치랑 &#39;간 수치&#39;가 흡연 여부랑 연관이 깊어보였음. 역시우리 몸은 거짓말을 안함..</li>
<li>불균형의 늪 : 비흡연자가 훨씬 많아서, 단순히 정확도만 높이다간 큰일 나겠다싶었음. 그래서 이번엔 F1-socre 로 확인하기로 함.</li>
</ul>
<hr>
<h3 id="3-전처리하기">3. 전처리하기</h3>
<ul>
<li>파생 변수 만들기<pre><code class="language-python">df[&#39;콜레스테롤&#39;] = df[&#39;콜레스테롤&#39;].clip(upper=400)
df[&#39;중성 지방&#39;] = df[&#39;중성 지방&#39;].clip(upper=500)
df[&#39;공복 혈당&#39;] = df[&#39;공복 혈당&#39;].clip(upper=300)</code></pre>
</li>
<li>이상치 처리 : 삭제 대신 / 클리핑 사용.<ul>
<li>극단값 제거 대신 정보 손실 최소화</li>
<li>실제 의료 데이터 특성고려</li>
</ul>
</li>
</ul>
<hr>
<h3 id="4-데이터-분리-스케일링">4. 데이터 분리 ,스케일링</h3>
<ul>
<li>Stratify 적용</li>
</ul>
<pre><code class="language-python">train_test_split(..., stratify=y)</code></pre>
<p>-&gt; 클래스 불균형 유지</p>
<ul>
<li>RobustScaler 사용<ul>
<li>이상치에 강함</li>
<li>트리 모델 + 앙상블에 안정적</li>
</ul>
</li>
</ul>
<hr>
<h3 id="5-모델링--앙상블-튜닝">5. 모델링 : 앙상블, 튜닝</h3>
<p>하나의 모델만 쓰기엔 성능을 또 다다를수있어서 3모델을 써봄.</p>
<ul>
<li>XGBoost, LightGBM, CatBoost 3모델을 Soft Voting으로 묶음.</li>
<li>예측이 탄탄하고 안정적인느낌.</li>
<li>XGBoost + Optuna<pre><code class="language-python">study.optimize(objective, n_trials=20)</code></pre>
</li>
<li>F1-socre 기준 최적화</li>
<li>learnin_rate 낮게 -&gt; 과적합 방지</li>
</ul>
<p><em>*혼자서도 충분히 강한기준 모델확보하기</em></p>
<ul>
<li>앙상블(Soft Voting)<pre><code class="language-python">VotingClassifier(
estimators=[XGB, LGBM, CAT],
voting=&#39;soft&#39;,
weights=[1, 1, 2]
)</code></pre>
</li>
<li>서로 다른 트리 계열 모델의 장점 결합</li>
<li>CatBoost 가중치 ↑<ul>
<li>범주/비선형 패턴에 강함</li>
</ul>
</li>
</ul>
<p><em>* 예측이 안정적이고 흔들림 적음</em></p>
<hr>
<h3 id="6-임계값🔥🔥0378-">6. 임계값,🔥🔥0.378 !!!</h3>
<p>제일 공을 들인 작업이기도함. 바로 임계값(Threshold)찾기 .
보통 기준 0.5지만, 0.001 단위로 조정해가면서 테스트할 결과 0.378이라는 포인트 찾음 리더보드는 0.744 나옴 . 근데 아직 만족못해서 벨로그 작성후에도 계속 코드 만져볼 예정임.</p>
<p><em>* 나의 프로젝트 하이라이트 뽀인또</em></p>
<hr>
<h3 id="7-모델-평가-및-시각화">7. 모델 평가 및 시각화</h3>
<p>혼동 행렬 과 모델별 성능 비교를함.
혼동행렬은 어떤 유형에서 실수하는지 분석하고 FN/FP확인함
모델별 성능 비교는 단일 모델 vs 앙상블로함. 앙상블이 가장 균형잡힌 F1, Recall을 줌</p>
<h3 id="8shap">8.SHAP</h3>
<pre><code class="language-python">shap.TreeExplainer(best_xgb)</code></pre>
<p>결과만 내놓기보다는 SHAP 분석을 통해 모델들을 들여다봄. 확인해 보니 헤모글로빈과 신장이 예측에 엄청난 영향을주고있었음. 의학적으로도 일리가 있는 결과라 모델에 대한 신뢰도가 올라감.</p>
<hr>
<h4 id="프로젝트-마무리">프로젝트 마무리</h4>
<p>처음에는 코딩하고 점수 올리기 바빳는데, 이번 프로젝트하면서 단순 알고리즘만 돌리는게 아니라 이유 있는 결과를 찾아가는 방식으로 조금 바뀐것같음. 해석 가능한 예측모델로 만들어가는듯함. EDA → 전처리 → 앙상블 → Threshold → SHAP 단계를 거쳐보면서 전체 파이프라인 경험을 해봄. 이 미니 프로젝트 외에 이제 팀 프로젝트가 남았지만 , 그 또한 개인으로한 프로젝트를 바탕으로 조금 더 발전 해보려고함! 마지막 완성한 ppt 자료도 기록해놔야지 ㅎㅎ !</p>
<p>📎 발표자료(PDF)</p>
<ul>
<li><a href="!%5B%5D(https://velog.velcdn.com/images/wjdtor_2/post/665a34d8-890a-418b-9e94-e72b5f368319/image.pdf)">흡연데이터 PPT 자료</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[갓생일기_10일]]></title>
            <link>https://velog.io/@wjdtor_2/%EA%B0%93%EC%83%9D%EC%9D%BC%EA%B8%B010%EC%9D%BC</link>
            <guid>https://velog.io/@wjdtor_2/%EA%B0%93%EC%83%9D%EC%9D%BC%EA%B8%B010%EC%9D%BC</guid>
            <pubDate>Fri, 02 Jan 2026 11:28:42 GMT</pubDate>
            <description><![CDATA[<p>갓생살기 위한 10일차 일기!
오늘도 어김없이 어제에 이어서 흡연데이터를 가지고,
개인 프로젝트를 하는중이다. 사실 하루종일 여러개의 모델과
값들고 파생변수도 여러번 바꿔봐도 아직은 원하는 값은 안나오는것같다.
그래서 데이터파일들을 한번 더 뜯어봤는데
테스트에 이상치가 있어서 결과값이 이상하게 나온다.
어쩔 때는 과적합이
나오는것같고 , 어쩔때는 정확도나 이런게 낮아지거나
하는 게 보였다.</p>
<p><img src="https://velog.velcdn.com/images/wjdtor_2/post/ab5cde06-1aa2-453d-8619-363c733ff15b/image.png" alt=""></p>
<p>그리고 앙상블로 LightGBM,XGBoost,CatBoost 를 써봣는데
데이터자체가 범주형이아닌데 캣부스트 수치가 높게나와서
그것또한 의문을 품고 어디가 잘못되었는지 찾고있다.</p>
<p>이 프로젝트를 진행하면서 그래도 조금 더 용어라던가,모델들을
어떤식으로 쓰여야하는지 이유가 무엇때문인지를 공부할수있게되는것같아서
머리는 아프긴해도 좋은것같다!</p>
<p>사실 오늘이 갓생일기는 마지막날이다
벗뜨, 나는 이 습관의 계기로 날마다 공부한걸 
최대한 하루에하나 못해도 일주일에하나씩은 올릴 예정이다.!
그래야 조금이라도 공부한 내용도 정리할수있으니까!
힘내보쟈아아아!!!!</p>
]]></description>
        </item>
    </channel>
</rss>