<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yong-lee.log</title>
        <link>https://velog.io/</link>
        <description>오늘은 어떤 새로운 것이 나를 즐겁게 할까?</description>
        <lastBuildDate>Sun, 08 Mar 2026 08:30:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yong-lee.log</title>
            <url>https://velog.velcdn.com/images/yong-lee/profile/a2a57418-b37e-4c65-97d4-fe1c67e6ff16/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yong-lee.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yong-lee" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[재무제표 2강 복습 문제(액티브)]]></title>
            <link>https://velog.io/@yong-lee/%EC%9E%AC%EB%AC%B4%EC%A0%9C%ED%91%9C-2%EA%B0%95-%EB%B3%B5%EC%8A%B5-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@yong-lee/%EC%9E%AC%EB%AC%B4%EC%A0%9C%ED%91%9C-2%EA%B0%95-%EB%B3%B5%EC%8A%B5-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Sun, 08 Mar 2026 08:30:22 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>다음 중 개발비를 무형자산으로 인식하기 위한 요건이 아닌 것은 무엇인가? <strong>4</strong>
① 기술적 실현 가능성
② 판매 또는 사용 의도
③ 미래 경제적 이익의 존재
④ 시장점유율 상승 가능성</p>
</li>
<li><p>연구비는 비용으로 처리하지만 개발비는 자산으로 인식할 수 있는 이유를 설명하시오.</p>
</li>
</ol>
<p>**
가장 큰 이유는 이 지출이 미래에 경제적 가치가 있냐에 달려 있음.
연구에서는 아직 미래에 경제적으로 이익을 줄 수 있는지 불확실하지만, 그것이 만약 여러 연구 끝에 미래적 가치가 어느 정도 입증되면 무형자산으로 인식.
**</p>
<ol start="3">
<li>어떤 기업의 무형자산이 3년간 크게 증가했지만 매출은 거의 증가하지 않았다. 이 상황에서 투자자가 의심해볼 수 있는 회계적 또는 경영적 시나리오를 최소 3가지 설명하시오.</li>
</ol>
<ul>
<li>기업측에서 아직 계상하기에는 무리가 있는 개발비를 계상하게 되어 무형자산(개발비)의 미래적 가치가 있는지 의심 필요.</li>
<li>무형자산(개발비)이 실제로는 이익으로 가져올 수 있는 가치가 있는지 의심 필요.</li>
<li>무형자산의 가치는 여전히 존재하지만, 그 외적으로 회사의 또 다른 악재가 발생하여 매출 증가가 안되는 경우.</li>
</ul>
<ol start="4">
<li><p>다음 중 영업권에 대한 설명으로 옳은 것은 무엇인가? <strong>1</strong>
① 내부적으로 형성된 브랜드 가치는 영업권으로 계상된다
② 영업권은 일정 기간 동안 감가상각한다
③ 기업 인수 시 발생한 경영권 프리미엄은 영업권으로 인식된다
④ 영업권은 매년 반드시 상각해야 한다</p>
</li>
<li><p>기업 인수 시 영업권이 발생하는 구조를 간단한 예시를 통해 설명하시오.</p>
</li>
</ol>
<p>**
기업 인수 시에 기업이 소유하고 있는 물건(기계, 시설 장비, 건물)들의 가치 확인.
기업의 영업권 가치의 따로 추가적으로 비용도 확인. 이 영업권에는 공급망, 입지, 브랜드 값 등이 해당.
**</p>
<ol start="6">
<li>기술 변화가 빠른 산업에서 영업권 규모가 큰 기업이 가지는 투자 리스크를 설명하고, 투자자가 추가로 확인해야 할 재무 지표나 사업 요소를 제시하시오.</li>
</ol>
<p>**
  영업권에는 감가상각 하지는 않는다.
  하지만, 그 영업권에 해당하는 것의 상품자체의 가치가 할 시 장부 가치를 줄여야 한다.
  2강에서 했던 얘기처럼 HBM3 기술이 새로운 기술로 인해 가치가 떨어지면 이 영업권도 급락한다.</p>
<p>  이 예시처럼 HBM3의 필요성이 떨어지면서 매출이 줄어들 확률이 높다. 그것을 잘 확인 필요하다.
**</p>
<ol start="7">
<li><p>다음 중 자사주 매입의 효과로 보기 어려운 것은 무엇인가? <strong>4</strong>
① 유통주식수 감소
② EPS(주당 순이익) 상승 가능성
③ ROE 상승 가능성
④ 회사의 총자산 증가</p>
</li>
<li><p>자사주 매입이 주주가치 제고 정책으로 평가되는 이유를 설명하시오.</p>
</li>
</ol>
<ul>
<li>시장에 풀리는 기업 주식 수가 줄어들면서 기존에 들고 있던 주식들의 가치가 향상</li>
<li>경영진이 자사에 진심이라는 진성을 보여줌.</li>
</ul>
<ol start="9">
<li>어떤 기업이 대규모 차입을 통해 자사주 매입을 진행했다. 이 정책이 단기 주가, 장기 재무 안정성 에 어떤 영향을 줄 수 있는지 설명하시오.</li>
</ol>
<p>**
 한국에서는 주로 경영권 방어 등의 문제로 단기적으로는 주가가 많이 오를 것으로 보인다.
 하지만 빚을 내서 자사주를 구매했기 때문에 추후 이자와 빚을 갚기위해 현금이 적어질 것으로 보임.
**</p>
<ol start="10">
<li><p>다음 중 인적분할의 특징으로 가장 적절한 것은 무엇인가? <strong>2</strong>
① 분할된 회사의 지분을 모회사가 100% 보유한다
② 기존 주주들이 분할된 두 회사의 주식을 동일하게 보유한다
③ 분할된 회사는 반드시 상장해야 한다
④ 분할 시 부채는 모두 소멸한다</p>
</li>
<li><p>물적분할과 인적분할의 차이를 설명하시오.</p>
</li>
</ol>
<ul>
<li>물적분할은 기존 회사의 자회사로 분할되는 것.</li>
<li>인적분할은 정말 완전히 다른 회사로 분리하는 것.</li>
</ul>
<ol start="12">
<li>기업분할 이후 특정 사업부의 가치가 시장에서는 오히려 저평가될 수 있는 이유를 설명하고, 투자자가 이 상황을 어떻게 활용할 수 있는지 서술하시오.</li>
</ol>
<p>**
인적분할이라고 가정했을 때, 어떤 사업에 대하여 분리된 회사 쪽으로 넘어갔다고 가정해보자.
그 사업의 대한 미래적 가치가 높았는데 분리되면서 기존의 회사는 미래적 먹거리가 사라지면서 저평가 될 수 있다.
**</p>
<ol start="13">
<li><p>다음 중 부채 사용이 ROE 상승으로 이어질 수 있는 이유로 가장 적절한 것은 무엇인가? <strong>2</strong>
① 부채는 항상 비용이 발생하지 않는다
② 부채를 활용하면 자기자본 대비 투자 규모가 커질 수 있다
③ 부채는 기업가치를 자동으로 상승시킨다
④ 부채는 세금이 면제된다</p>
</li>
<li><p>매입채무가 기업에게 유리한 부채로 평가되는 이유를 설명하시오.</p>
</li>
</ol>
<p>**
이자 없이 돈을 끌어다 쓰는 것이기 때문에 더 많은 투자를 할 수 있음
**
15. 다음 두 기업의 재무구조를 비교하시오.
A기업
자산 100 / 부채 0 / 자본 100</p>
<p>B기업
자산 200 / 부채 100 / 자본 100</p>
<p>두 기업의 ROA가 동일하게 26%일 때 ROE는 어떻게 달라지는가?
그리고 투자자는 어떤 추가 리스크를 고려해야 하는지 설명하시오.</p>
<p>**
ROA(총자산이익률)이 같다는 것은 B기업은 A기업보다 2배의 순이익이였다는 뜻.
그러므로 순이익/자본이 ROE이니 2배의 차이가 있음 알 수 있음.</p>
<p>여기서 B기업은 더 많은 돈을 벌었기 때문에 좋을 수 있지만, 여러 사항들을 고려할 필요가 있다.
부채에서는 이자를 내야하는데 그것의 대한 고려가 필요하며, 순이익이 하락하면 더 많은 투자를 했기 때문에 리스크가 더 크다 볼 수 있다.
**</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[재무제표 1장]]></title>
            <link>https://velog.io/@yong-lee/%EC%9E%AC%EB%AC%B4%EC%A0%9C%ED%91%9C-1%EC%9E%A5</link>
            <guid>https://velog.io/@yong-lee/%EC%9E%AC%EB%AC%B4%EC%A0%9C%ED%91%9C-1%EC%9E%A5</guid>
            <pubDate>Tue, 24 Feb 2026 16:40:07 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-1-재무제표-구성">Chapter 1. 재무제표 구성</h1>
<hr>
<h2 id="q1-재무상태표-손익계산서-현금흐름표의-이해">Q1. 재무상태표, 손익계산서, 현금흐름표의 이해</h2>
<h3 id="재무상태표사진">재무상태표(=사진)</h3>
<ul>
<li>재무상태표는 특정 시기의 회사 재무 상태를 보는 것<ul>
<li>2026년 1월 1일 A회사 재무상태</li>
</ul>
</li>
<li>1년 전 사진에 찍힌 나와 방금 찍은 사진에 있는 나를 비교할 수 있듯이, 재무상태표도 과거와 현재를 비교가 가능하다.</li>
</ul>
<h3 id="손익계산서영화">손익계산서(=영화)</h3>
<ul>
<li>특정 기간동안의 손익을 볼 수 있음<ul>
<li>2025년에 이 회사는 얼마 벌고, 썼고, 남았을까?</li>
</ul>
</li>
<li>왜 영화와 비슷할까?<ul>
<li>손익계산서에는 일정 기간의 스토리를 보여준다.</li>
<li>매출이 발생하면서 회사에 돈이 생기지만, 중간중간 지출해야하는 것이 발생한고, 결과적으로 얼마를 얻거나 잃었는지 알 수 있다.</li>
</ul>
</li>
</ul>
<h3 id="현금흐름표통장">현금흐름표(=통장)</h3>
<ul>
<li>실제로 현금이 들어오고 나간 기록<ul>
<li>그냥 딱봐도 통장에 찍히는 내역과 같음.</li>
</ul>
</li>
<li>손익계산서와 비슷한 것 아닌가?<ul>
<li>현금흐름표는 실질적인 현금이 오간것을 보여줌</li>
<li>손익계산서는 감가상각비 혹은 외상 등도 포함되어 있기 때문에 실제로 가지고 있는 현금과 다를 수 있음</li>
</ul>
</li>
</ul>
<hr>
<h2 id="q2">Q2</h2>
<p>당기순이익이 크게 증가했는데 주가가 하락했다.
이 현상을 <code>재무제표</code> 관점에서 설명할 수 있는 가능한 원인을 제시하시오.</p>
<blockquote>
<p>당기순이익: 기업이 일정 기간 동안 벌어들인 총수익에서 모든 비용을 차감한 후 남은 최종 순이익</p>
</blockquote>
<p>재무제표에서는 크게 자산, 부채, 자본를 볼 수 있다.
그렇게 생각했을 때 자산과 자본이 증가했을 것으로 보인다.
하지만 주가가 하락했다면 부채도 크게 증가했을 가능성이 있다.</p>
<p>만약 부채에는 영향이 없다면 자산에 재고 자산이 크게 증가했을 가능성이 있을것으로 예상된다.
무리하게 재고(비유동자산)을 늘리면서 추후 쌓이면서 문제가 될 소지가 있을 수 있다고 판단했기 때문으로 본다.</p>
<hr>
<h2 id="q3">Q3</h2>
<p>손익계산서상 흑자인 기업이 파산하는 경우가 있다.
재무제표 3개를 연결해 그 이유를 설명하시오.</p>
<p>나의 답: 문제에서 말하는 것처럼 손익계산서를 통해 흑자가 나는 것을 보게 됩니다. 하지만 이상하게 현금흐름표에는 들어오지 않고 있습니다. 왜 그런지 재무상태표를 읽고 깨닫습니다. 유동자산(현금)으로 받지 못하고 있었던 것이죠. 정작 현금을 써야할 때 쓰지 못하는 상황이 된 것입니다.</p>
<p>이전 강의에서 들었던 것으로 기억하는데 유동자산으로 받지 못하고 비유동자산으로 받으면서 문제가 생겼던 것으로 알고 있다.</p>
<p>내가 생각한 상황 예시:A가게에서 100개 티셔츠를 팔았다. 그런데 손님 중 절반이 현금이 없다고 나중에 주겠다는 의미로 개인의 신분증만 주고 갔다. 그런데 내일 100만원을 지출해야하는 일이 생겼다. 하지만 손님들이 현금을 주지 않아서 못내게 되는 상황이다.</p>
<hr>
<h2 id="q4">Q4</h2>
<p>재무상태표에 현금이 늘었을 때,
그 현금이 <code>영업활동</code> <code>투자활동</code> <code>재무활동</code> 중 어디에서 왔는지 판단하는 방법을 설명하시오.</p>
<p>Q1에서 말했 듯 재무상태표는 사진과 같이 특정시기를 보여준다. 사진은 한정된 공간만 보여주듯 재무상태표도 디테일한 것을 보여줄 수 없을 것으로 보인다. 현금이 늘었지만 단지 지금 이 정도 현금이 있고, 이전 재무상태표를 비교하면서 늘어난 것을 확인할 수 있을 것으로 보인다.</p>
<p>그렇다면 손익계산서 혹은 현금흐름표 둘 중 하나에 답이 있을 것으로 보인다. </p>
<p>손익계산서는 발생기준으로 작성된다는 것을 알게되었다. 위에서 얘기한 외상, 감가상각 등 여러 가지로 현금이 들어오지 않아도 들어올것으로 예상한 것들을 적기 때문에 실질적으로 현금이 들어오지 않는 경우가 있다.</p>
<p>그렇기 때문에 현금흐름표를 참조하는게 맞다.
직접 확인할 시 영업/투자/재무로 구분하여 볼 수 있다.</p>
<hr>
<h2 id="q5">Q5</h2>
<p>당기순이익 -&gt; 이익잉여금 -&gt; 설비투자 -&gt; 미래매출
이 연결이 어떻게 작동하는지 서술 및 어디에서 실패할 수 있는지 예측하시오.</p>
<p>사이클 해석:</p>
<ul>
<li>회사에서 당기순이익이 발생하여 회사에 돈이 생겼다.
이 돈을 사용하여 이익을 만들만한 장비와 설비에 벌어들인 현금을 쓴다.
새로운 장비와 설비를 사용함으로써 더 많은 돈을 벌어들인다.</li>
</ul>
<p>실패 상황:</p>
<ul>
<li>당기순이익이 현금성 자산으로 들어오지 않았을 경우</li>
<li>이익잉여금을 설비가 아닌 다른 곳에 써야만 할 때</li>
<li>이익잉여금이 설비투자하기 위해 충분하지 않을 때</li>
<li>잘못된 방향으로 설비투자를하여 매출 늘리기 실패했을 때</li>
</ul>
<hr>
<hr>
<h1 id="chapter-2-재무상태표-기업의-현재를-읽는다">Chapter 2. 재무상태표: 기업의 현재를 읽는다.</h1>
<h2 id="q1">Q1</h2>
<p>자산이 증가했는데 자본은 그대로이고 부채만 증가했다.
이 기업의 상황을 해석하시오.</p>
<p>재무상태표: 자산 = 부채 + 자본
지금 이 짧은 글로는 이 기업의 어떠한 사유로 부채를 늘려 자산을 늘렸는지 알 수 없다. 다만 느낌상 누군가에게 돈을 빌려서라도 해야만 하는 것이 있다.</p>
<hr>
<h2 id="q2-1">Q2</h2>
<p>자기자본은 늘었는데 현금은 줄었다.
가능한 시나리오 3가지 제시하시오.</p>
<ol>
<li>기업이 돈을 이익을 얻어 자본이 늘었으나, 그 만큼 더 많은 재투자로 현금이 줄었다.</li>
<li>기존에 있던 유동성 자본으로 무언가 투자했고 그것이 비유동성 자본으로써 가치가 올랐다.</li>
<li>소유하고 있는 비유동성 자본의 가치가 상승했지만, 부채를 갚으면서 현금은 줄어들었다.</li>
</ol>
<hr>
<h2 id="q3-1">Q3</h2>
<p>재무상태표를 보고 <code>이 회사는 공격적으로 확장 중이다</code>라고 판단 할 수 있는 항목 변화를 설명하시오.</p>
<p>개인적인 생각으로는 부채를 늘려서 공격적으로 필요한 설비 투자, 재고 생산, 기술 개발 등에 사용하는 것이 공격적으로 확장한다고 할 수 있지 않을까?</p>
<hr>
<h2 id="q4-1">Q4</h2>
<p>총자산이 크게 증가했는데 ROA가 하락했다.
이 현상이 의미하는 바를 설명하시오.</p>
<blockquote>
<p>ROA(Return on Assets, 총자산이익률)
ROA = 당기순이익 / 총자산 x 100 or 당기순이익 / 평균총자산 x 100</p>
</blockquote>
<p>총자산이익률을 풀어서 말하면 자산대비 이익률이 얼마나 되느냐 인 것으로 보이는데 그렇다면 총자산이 증가했는데도 이익은 그대로이거나 떨어졌다고 판단할 수 있다.
그렇다면 자본쪽에서는 무언가 건드릴만한 것이 없음으로 부채에서 크게 증가했을 것으로 보인다.</p>
<hr>
<h2 id="q5-1">Q5</h2>
<p>신규 설비투자 발표 공시를 보고 재무상태표만으로 &quot;재무 리스크가 커질지 여부&quot;를 판단하는 순서를 제시하시오.</p>
<p>나의 생각</p>
<ol>
<li>우선적으로 신규 설비투자에 들어갈 것으로 예상되는 금액을 확인한 후 재무상태표에서 이 회사가 현금을 주기적으로 얼마를 꾸준히 갖고 있었는지 확인 필요.</li>
<li>설비투자 기간은? 기간 중간에 돈이 부족하여 실패하면 안 되기 때문에 유동적 자산과 비교할 필요 있어 보인다.</li>
</ol>
<h4 id="gpt-답변">GPT 답변</h4>
<p>① 설비투자 규모와 총자산 대비 비율 확인
이유: 투자 규모가 회사에 비해 너무 크면 리스크 증가</p>
<p>② 현금 및 유동자산 규모 확인
재무상태표 항목:</p>
<ul>
<li>현금 및 현금성자산</li>
<li>유동자산
이유: 자체 자금으로 투자 가능한지 판단</li>
</ul>
<p>③ 부채 규모 및 부채비율 확인
이유: 이미 빚이 많은 상태에서 추가 투자 → 위험 증가</p>
<p>④ 자기자본 규모 확인
이유: 자기자본이 크면 버틸여력 있음</p>
<hr>
<hr>
<h1 id="chapter-3-자기자본-재무-안정성-판단-기준">Chapter 3. 자기자본: 재무 안정성 판단 기준</h1>
<p>(자본의 질 -&gt; 레버리지 -&gt; ROE 구조 이해)</p>
<h2 id="q1-1">Q1</h2>
<p>이익잉여금이 꾸준히 증가하는 기업이 왜 복리 구조를 가진 기업이라고 말할 수 있는가?</p>
<blockquote>
<p>이익잉여금 = 당기순이익 − 배당 + 누적금액
기업이 영업 활동을 통해 벌어들인 순이익 중, 배당 등으로 사외 유출하지 않고 사내에 쌓아둔 누적 금액</p>
</blockquote>
<p>이익잉여금이 꾸준히 증가한다는 것은 기업이 돈을 벌어들이고 있다는 뜻이다.
그 돈을 엿 바꿔먹는게 아닌 더 많은 돈을 벌어들이기 위해 사용되고 더 많은 또 더 많은 이익이 생기면서 자산은 더 늘어난 상태에서 또 그 벌어들인 돈으로 투자하다보니 점점 많아지는 자산으로 더 많은 투자를 하면서 덩달아 이익도 늘어나니 복리 구조가 맞는 것 같다.</p>
<hr>
<h2 id="q2-2">Q2</h2>
<p>자기자본 비율이 낮지만 ROE가 높은 기업은 투자 매력이 있는가?
찬반 논리로 설명하시오.</p>
<blockquote>
<p>ROE(Return On Equity, 자기자본이익률) = 당기순이익 / 자기자본
기업이 주주의 돈(자기자본)을 활용해 1년간 얼마만큼의 순이익을 냈는지 보여주는 대표적인 수익성 지표</p>
</blockquote>
<p>여러가지 고려를 해야겠지만 찬성과 반대를 골라야한다면 위험성이 커보여서 반대에 쪽에 가고 싶다.
ROE에서 보이는 것은 당기순이익과 자기자본이다.
이 두가지만 보았을 때는 큰 문제가 없지만 여기에는 부채가 없다는 사실을 알게 되었다.
생각해보면 100만원을 투자해서 100만원의 이익을 얻는 것은 불가능하다.
그렇다면 여기에 플러스로 자본쪽이 아닌 부채까지 끌어들여서 만들어낸 이익일 것이다.
부채+자본으로 만들어낸 이익일테니 자본이 적다면 부채가 많을 수 밖에 없을 것이므로 리스크 커보인다.</p>
<hr>
<h2 id="q3-2">Q3</h2>
<p>유상증자가 반드시 악재는 아니다.
어떤 조건에서 긍정적일 수 있는 설명하시오.</p>
<blockquote>
<p>유상증자
회사가 새로운 주식을 발행하고, 그 대가로 투자자로부터 돈을 받는 것</p>
</blockquote>
<p>처음 유상증자를 봤을 때, 주식을 들고 있던 주주들은 싫어할 것이라 생각했다.
하지만 잘못 생각한 것이 주주들이 들고있는 주식의 값어치가 떨어지는 것이 아니라는 것을 알게되었다.
단순히 회사 지분율이 떨어지는 것이다.</p>
<p>대신 새로운 주주들로부터 돈을 얻게 되고 그것으로 회사는 더 커지고 이익을 위해 설비 투자에 나설 것이므로 긍정적이라고 볼 수 있겠다는 생각을했다.</p>
<hr>
<h2 id="q4-2">Q4</h2>
<p>자기자본이 과도하게 많을 때 발생할 수 있는 문제점을 설명하시오.</p>
<p>고려할만한 문제점은 기업이 미래 발전을 위해 충분히 투자를 하고 있는지 의심할 필요가 있을 것으로 보인다.</p>
<hr>
<h2 id="q5-2">Q5</h2>
<p>ROE가 20%인 기업이 10년간 이익을 전부 재투자한다고 가정할 때 기업가치는 어떻게 변할 수 있는지 논리적으로 설명하시오.</p>
<p>초기 자산이 1억이라고 가정했을 때 20%라면 1년이 지난 후 1.2억이 될 것이고, 2년이 지나면 1.44(1.2+0.24)억이 될 것이다. 복리로 이 회사는 자산이 증식할 것으로 보인다.</p>
<hr>
<hr>
<h1 id="chapter-4-유동비율-단기-생존의-조건">Chapter 4. 유동비율: 단기 생존의 조건</h1>
<p>(유동성 -&gt; 신용도 -&gt; 악순환 구조 인식)</p>
<h2 id="q1-2">Q1</h2>
<p>유동비율이 90%인 기업과 180%인 기업 중 어느 기업이 더 안전한지 단순 비교가 어려운 이유를 설명하시오.</p>
<blockquote>
<p>유동비율 = 유동자산 / 유동부채 x 100
유동자산: 1년 이내 현금화 가능한 자산(현금, 예금, 매출채권, 재고자산)
유동부채: 1년 이내 갚아야하는 부채(매입채무, 단기차입금, 미지급금)</p>
</blockquote>
<p>나의 답변: 유동비율이 낮을 시 빨리 갚아야할 돈이 많다는 뜻이고, 높다면 갚을 돈은 적고 금방 현금이 늘어날 확률이 높다. 허나 이것이 장기적인 모습이 아닌, 그 당시에 상황만 보여주는 것으로 보여 안전한 회사라고 하기는 어려울 것으로보임</p>
<p>AI 답변: 유동비율은 유동자산과 유동부채의 양적 관계만 나타낼 뿐, 유동자산의 구성 내용이나 현금화 가능성, 업종 특성, 자산 회전 속도 등을 반영하지 못한다. 따라서 유동비율이 높더라도 재고자산 비중이 높아 현금화가 어려울 경우 지급능력이 낮을 수 있으며, 반대로 유동비율이 낮더라도 현금 비중이 높고 자산 회전이 빠르면 지급능력이 양호할 수 있다. 그러므로 유동비율이 90%인 기업과 180%인 기업의 안전성을 단순 비교하기는 어렵다.</p>
<hr>
<h2 id="q2-3">Q2</h2>
<p>유동자산이 충분한데도 회사가 유상증자를 했다.
가능한 이유를 설명하시오.</p>
<p>유동자산이 많다고 지금 당장 돈이 있는 것이 아니기 때문에 공격으로 설비투자든 개발이든 뭐든 하기 위해 지금 당장 현금이 필요한 상황을 위해 유상증자를 할 수 있을 것으로 보인다.</p>
<hr>
<h2 id="q3-3">Q3</h2>
<p>유동부채가 급증했을 때 주가가 먼저 반응하는 이유를 설명하시오.</p>
<p>위에서 말했듯 유동부채와 유동비율로는 알 수 있는 것은 한정될 것으로 보인다.
하지만 유동부채가 급증한다는 것은 단기적으로 미래 투자할 수 있는 기회가 줄어든 다는 뜻으로 보인다. 그리고 피치못한 악재가 발생했을 때 대응이 어려워질 수 있다는 소리로 보인다.</p>
<hr>
<h2 id="q4-3">Q4</h2>
<p>유동비율이 낮은 기업이 추가 차입을 할 경우 어떤 재무적 악순환이 발생할 수 있는지 서술하시오.</p>
<blockquote>
<p>차입: 개인이나 기업이 은행, 타인 등 외부로부터 돈이나 물건을 빌리는 행위</p>
</blockquote>
<p>갚아야할 빚이나 늘어날 것이고 추가 차입을 해야할만큼 현금 자산이 부족하다는 소리로 들리니 신용도가 떨어질 것으로 보인다. 그리고 이자가 늘어나는 만큼 이전과 똑같이 기업이 돈을 벌어도 이익은 감소할 수 밖에 없음.</p>
<hr>
<h2 id="q5-3">Q5</h2>
<p>기업이 자산을 매각할 때 <code>유동성 확보</code>와 <code>구조조정</code>의 차이를 어떻게 구분할 것인지 설명하시오.</p>
<p>유동성 확보는 단기적으로 필요한 돈이 필요해져서 자산을 매각해서라도 현금을 얻기 위함.
구조조정은 큰 이익이 없거나 불필요하다 판단되는 것을 매각하여 그 자산의 대한 관리를 줄여 다른 자산관련된 일에 더 집중하는 방향으로 보인다.</p>
<hr>
<hr>
<h1 id="chapter-5-가중평균자본비용wacc">Chapter 5. 가중평균자본비용(WACC)</h1>
<blockquote>
<p>기업이 투자할 때 최소한 이 정도 이상의 수익률은 벌어야 한다는 기준선(요구수익률)
WACC=(자기자본/총자본×자기자본비용​)+(부채 / 총자본​×부채비용​×(1−법인세율))
총자본 = 자산</p>
</blockquote>
<p>(조달비용 -&gt; 투자판단 -&gt; 기업가치)</p>
<hr>
<h2 id="q1-3">Q1</h2>
<p>왜 부채는 세금 절감 효과가 있는가?
이것이 WACC에 어떤 영향을 주는가?</p>
<p>이자는 비용으로 포함되기 때문이다. 100만원을 벌어들였을 때 세율이 10%라면 세금이 10만원이다. 하지만 만약 30만원으로 이자를 내게 된다면 70만원에서 10%를 때가는 방식이여서 7만원만 세금으로 나간다.
그러므로 WACC도 줄어들지 않을까?</p>
<hr>
<h2 id="q2-4">Q2</h2>
<p>ROA &lt; WACC인 기업이 지속적으로 설비투자를 한다면 장기적으로 기업가치는 어떻게 변하는가?</p>
<p>지금 이 말은 돈도 못 벌었으면서 장비만 맞추는 적자행동으로 보인다.
이발소에서 한달동안 10만원 벌었는데 매달 수익보다 비싼 바리깡을 구매하는 것과 같다고 본다.
그러다보면 돈이 없어서 이발소가 망하듯이, 기업에 투자할 가치가 없어질 것으로 보인다.</p>
<hr>
<h2 id="q3-4">Q3</h2>
<p>베타가 높은 기업이 자기자본으로 자금을 조달할 경우 조달비용은 어떻게 변하는가?</p>
<blockquote>
<p>여기서 베타란?
베타는 기업의 주식이 시장 전체에 비해 얼마나 민감하게 움직이는지를 나타내는 위험 지표
(기업의 체계적 위험의 크기)</p>
</blockquote>
<blockquote>
<p>조달비용: 기업이 사업 운영이나 투자에 필요한 자금을 외부(부채)나 내부(자기자본)에서 확보할 때 발생하는 이자, 배당, 수수료 등의 비용</p>
</blockquote>
<p>베타가 높다는 것은 리스크가 높다는 것으로 보인다.
그러므로 자기 기업에 투자하는 것이 매력적으로 보이기 위해 조달비용은 증가할 수 밖에 없다.</p>
<hr>
<h2 id="q4-4">Q4</h2>
<p>부채비율을 높이면 ROE가 상승할 수 있다.
그 메커니즘을 설명하라.</p>
<p>ROE = 순이익 / 자기자본
그러므로 자기자본의 비율을 낮추고 부채비율을 올린다면 ROE가 증가할 수 있다.</p>
<hr>
<h2 id="q5-4">Q5</h2>
<p>A기업의 WACC가 8%인데, 예상 투자수익률이 10%라면 투자해야 하는가? 단, 경기 침체 가능성을 함께 고려하여 논리적으로 판단하시오.</p>
<p>어느정도 고려한 것이 맞다면 투자해도 괜찮다고 생각한다.
이유는 예상했던 것보다 아주 조금 더 많은 수익률이 있으며 예상할 수 있는 범위 내에서 A회사는 선방했다고 볼 수 있다.
하지만 경기 침체를 고려하라고 한다면 이번에 보여줬던 투자수익률만큼의 값이 나올꺼라는 보장이 없다. 그러므로 투자는 더 신중히 고려할 필요가 있을 것으로 보인다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[재무제표 단어 정리]]></title>
            <link>https://velog.io/@yong-lee/%EC%9E%AC%EB%AC%B4%EC%A0%9C%ED%91%9C-%EB%8B%A8%EC%96%B4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@yong-lee/%EC%9E%AC%EB%AC%B4%EC%A0%9C%ED%91%9C-%EB%8B%A8%EC%96%B4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 17 Feb 2026 14:56:15 GMT</pubDate>
            <description><![CDATA[<h1 id="자산assets">자산(Assets)</h1>
<h4 id="의미">의미:</h4>
<ul>
<li>소유하는 유형, 무형의 물품 및 권리</li>
<li>예시) 현금, 집, 예금, 자동차 등등<h4 id="공식">공식:</h4>
자산 = 채무 + 자본</li>
</ul>
<hr>
<h3 id="현금">현금</h3>
<ul>
<li>유동적으로 사용할 수 있는 통화</li>
</ul>
<h3 id="현금성자산cash-equivalents">현금성자산(Cash Equivalents)</h3>
<ul>
<li>즉시 현금으로 전환이 가능한 자산</li>
<li>예시) 은행 예금</li>
</ul>
<h3 id="단기금융자산short_term-financial-assets">단기금융자산(Short_term Financial Assets)</h3>
<ul>
<li>투자한 자금을 단기에 수취하게 되는 금융 자산</li>
<li>보통 1년 이내를 단기로 친다</li>
<li>예시) 정기적금, 양도성 예금증서, 환매조건 부채권</li>
<li>단기금융자산의 목적: 자금운용</li>
</ul>
<h3 id="매출채권accounts-receivable">매출채권(Accounts Receivable)</h3>
<ul>
<li>납품된 상품이나 소비된 서비스에 대해 회사에 지불해야 하는 금액을 나타내는 회계 용어</li>
<li>물건을 팔고 아직 받지 못한 돈으로, 기업의 현금 흐름과 직결되는 중요 자산</li>
<li>구성: 외상매출금(거래처와의 외상 거래) + 받을어음(약속어음 수취)</li>
<li>이것도 자산인가?</li>
</ul>
<h3 id="대손충당금allowance-for-doubtful-accounts">대손충당금(Allowance for Doubtful Accounts)</h3>
<ul>
<li>외상매출금 혹은 대출금 중 회수가 불가능하다 예상되어 미리 비용으로 쌓아두는 금액</li>
<li>대출해줬던 회사중 일부가 갑작스럽게 망하거나 문제가 생겨서 돈을 갚지 못 할 수 있다. 이것을 처음부터 못 받는 돈이라 생각하는 금액이 대손충당금.</li>
</ul>
<h3 id="재고자산">재고자산</h3>
<ul>
<li>창고에 있는 자산</li>
<li>유동자산에 속한다.<ul>
<li>이유: 판매 과정을 통해 1년 이내 또는 정상적인 영업 주기 내에 현금화될 것으로 예상되는 상품, 제품, 원재료, 반제품 등을 포함하기 때문</li>
</ul>
</li>
</ul>
<h4 id="유동-자산의-구성-유동자산--당좌자산현금-매출채권-등--재고자산">유동 자산의 구성: 유동자산 = 당좌자산(현금, 매출채권 등) + 재고자산</h4>
<h3 id="유형자산tangible-assets">유형자산(Tangible Assets)</h3>
<ul>
<li>기업의 영업활동과정에서 장기간에 걸쳐 사용되어 미래의 경제적 효익이 기대되는 유형의 자산</li>
<li>예시) 토지, 건물, 기계장치, 구축물, 선박, 차량운반구, 공구와 기구, 비품 등</li>
</ul>
<h3 id="감가상각-depreciation">감가상각 (Depreciation)</h3>
<ul>
<li>비용을 지출해서 구입한, 장기간 사용하는 자산의 비용화에 대한 개념</li>
<li>회사의 고정자산을 한 번에 비용처리하지 않고 세법에서 정한 기간 동안 나누어 비용처리를 하는 것을 말합니다</li>
<li>예시) 음식점에서 빠른 조리를 위해 500만 원 상당의 설비를 구매. -&gt; <ul>
<li>이때 감가상각: 설비를 구매한 해에 500만 원을 한 번에 비용으로 처리하는 것이 아닌 5년간 100만 원씩 나누어 비용으로 처리하는 것</li>
</ul>
</li>
<li>무형자산의 상각은 Amortization</li>
</ul>
<h3 id="무형자산intangible-asset">무형자산(Intangible Asset)</h3>
<ul>
<li>물리적 실체는 없으나 식별 가능하고 통제 가능하며 미래 경제적 효익을 가져오는 비화폐성 자산</li>
<li>예시) 특허권, 상표권, 소프트웨어, 영업권, 개발비 등이 대표적이며, 기업의 가치 창출에 필수적인 요소로 감가상각(또는 상각) 과정을 거쳐 가치가 비용화됨</li>
</ul>
<h3 id="관계기업투자-vs-종속기업투자">관계기업투자 vs 종속기업투자</h3>
<ul>
<li>결국 이 두가지 방법은 투자를 얼마나 했냐에 따라 갈린다. 20~50% 사이라면 관계 기업, 50%를 초과했다면 지배.</li>
<li>이 지분율을 피가 섞인 정도라고 생각하면 이해하기 쉬움<ul>
<li>관계기업: 삼촌같은 존재. 여러 조언이나 도움 등을 줄 수 있으나, 이 아이(기업)를 자신의 가치관에 따라 행동하게 만들 수 없음. </li>
<li>종속기업: 부모같은 존재. 부모가 생각했을 때 좋은 방향으로 이 아이(기업)가 무엇을 해야하고, 행동하게 할 수 있는 힘이 있음. </li>
</ul>
</li>
</ul>
<h3 id="장기금융자산">장기금융자산</h3>
<ul>
<li>장기금융자산은 만기가 1년 이후에 도래하는 정기예적금, 장기대여금, 장기 투자 목적의 주식·채권 등 비유동자산. </li>
<li>목적: 단기금융상품과 달리 현금화가 장기간 소요되는 금융상품으로, 재무제표상 투자자산 혹은 비유동자산으로 분류되어 안정적인 자산 운용에 활용</li>
</ul>
<h3 id="이연법인세자산">이연법인세자산</h3>
<ul>
<li>회계상 비용이 세법상 비용보다 커서 당기에 세금을 더 납부했으나, 미래에 세법상 비용으로 인정받아 세금이 줄어들 것으로 예상되는 금액.</li>
<li>질문사항: 그렇다면 미래에 세금이 줄어든다는 것은 어떻게 알고, 얼마나 줄지 어떻게 알까?</li>
</ul>
<h3 id="사용권자산">사용권자산</h3>
<ul>
<li>리스 계약을 통해 리스 이용자가 특정 자산을 일정 기간 동안 사용할 수 있는 권리를 나타내는 자산</li>
</ul>
<h4 id="리스란">리스란?</h4>
<ul>
<li>리스제공자가 특정 자산의 법적 소유권을 유지한 채 일정 기간동안 해당 자산의 사용권을 리스이용자에게 이전하고, 리스이용자는 해당 자산의 사용대가로 리스제공자에게 사용료를 지급하는 계약을 말함</li>
</ul>
<hr>
<h3 id="운전자본">운전자본</h3>
<ul>
<li>유동 자산과 유동 부채의 차액을 의미</li>
<li>사용도: 이는 기업이 일상적인 운영을 운영하는 데 사용할 수 있는 유동 리소스를 반영하며, 기업의 단기 재무 건전성을 나타내는 지표</li>
<li>비즈니스에 중요한 이유: 기업이 단기적인 재정적 의무와 운영상의 필요를 충족하고 예상치 못한 비용을 충당하며 성장 기회를 활용할 수 있는지를 보여주기 때문</li>
<li>예시) 운전자본이 충분한 식당<ul>
<li>직원과 공급업체에 제때 대금을 지급하고, 고객 경험을 개선하기 위해 직원 교육에 투자하고, 매출을 촉진하는 계절별 프로모션과 메뉴 확장에 자금을 지원하고, 예기치 않은 주방 수리 비용을 지불하여 운영을 원활하게 유지할 수 있음</li>
<li>운영 자본이 충분하지 않으면 임대료, 급여, 식재료 구매와 같은 필수 비용을 충당하지 못해 메뉴 제공이 줄어들고 서비스 속도가 느려질 수 있습니다. 고객 만족도는 매출과 함께 하락할 수 있음</li>
</ul>
</li>
</ul>
<h3 id="손운전자본nwc">손운전자본(NWC)</h3>
<ul>
<li>순운전자본 = 유동자산 - 유동부채</li>
</ul>
<h3 id="자산총계">자산총계</h3>
<ul>
<li>자산 = 부채 + 자본</li>
<li>자본은 자산에서 부채를 뺀 것이므로 헷갈리는 것 주의</li>
</ul>
<h3 id="capexcapital-expenditures-자본적-지출">capex(Capital Expenditures, 자본적 지출)</h3>
<ul>
<li>기업이 미래 수익 창출이나 사업 확장을 위해 건물, 기계, 설비, IT 인프라 등 장기적 고정자산을 취득/개량하는 데 드는 비용</li>
<li>주요 대상: 공장, 토지, 설비, 컴퓨터, 서버, 소프트웨어 등</li>
<li>목적: 생산성 향상, 신사업 확장, 설비 교체</li>
<li>capex가 중요한 이유:<ul>
<li>기업이 미래를 위하여 무언가 투자하고 있음을 알 수 있음</li>
<li>과거 투자 규모와 실제 이익 비교로 기업의 효율성 확인 가능</li>
<li>산업 특성: 제조업, 통신, 데이터 센터 관련 기업 등은 대규모 CAPEX가 필수적</li>
</ul>
</li>
</ul>
<hr>
<hr>
<h1 id="부채">부채</h1>
<hr>
<h3 id="매입채무">매입채무</h3>
<ul>
<li>기업이 공급업체에 빚진 돈으로, 회사의 재무상태표에 부채로 표시.</li>
<li>외상매입금 (순수 외상 거래) + 지급어음 (어음을 발행하여 지급)</li>
<li>기업이 주된 영업 활동(상품·원재료 구매 등) 과정에서 외상으로 물건을 산 후, 아직 대금을 지급하지 않은 부채를 의미합니다. 이는 재무상태표의 유동부채 항목으로, 외상매입금과 지급어음을 합하여 계산하며 보통 1년 이내에 상환.</li>
</ul>
<h3 id="단기차입금-vs-장기-차입금">단기차입금 vs 장기 차입금</h3>
<ul>
<li>단기차입금은 금전소비대차계약에 의하여 금융기관 등 외부로부터 빌린 채무로서 1년 이내에 갚아야 하는 차입금과 당좌차월을 말하는 것으로 보통 어음을 발행하거나 차용증서를 작성해 금전을 빌릴 때 발생하는 채무이다.</li>
<li>단기차입금은 유동 부채 항목으로 감소는 차변에, 증가는 대변에 기록한다.</li>
<li>나중에 다시 확인할 참고자료 : <a href="https://woori.iquest.co.kr/service/accountTitle.do?board_key=3&amp;board_ref=58">우리자금관리서비스</a></li>
<li>단기:<ul>
<li>만기: 결산일로부터 1년 이내에 도래하는 차입금</li>
<li>유동부채</li>
<li>단기 운영자금 조달용</li>
</ul>
</li>
<li>장기:<ul>
<li>만기: 결산일로부터 1년 이후 </li>
<li>비유동부채</li>
<li>설비투자 등 장기 자금 조달용</li>
</ul>
</li>
</ul>
<h3 id="유동성장기부채">유동성장기부채</h3>
<ul>
<li>원래 장기부채였으나 결산일로부터 1년 이내에 상환기일이 도래하여 유동부채로 재분류된 것.</li>
<li>예) 3년 만기 장기차입금 중 1년 이내 상환 예정인 부분</li>
<li>단기차입금과의 주요 차이점을 묻는다면, 단기는 처음부터 유동부채였고 유동성장기부채는 원래 장기였던 비유동이 유동으로 변환한 것이라고 하면 됨.</li>
</ul>
<h3 id="사채">사채</h3>
<ul>
<li>은행 등 제도권 금융기관이 아닌 개인이나 미등록 대부업체에서 빌리는 고금리 사설 채무</li>
</ul>
<h3 id="충당부채">충당부채</h3>
<ul>
<li>과거 사건으로 인해 발생한 현재의무가 존재하며 그 이행을 위해 경제적 자원의 유출 가능성이 높고 금액을 합리적으로 추정할 수 있을 때 인식되는 부채</li>
<li>충당부채 정의 방법:<ul>
<li>과거 사건(Past Event)으로 인해</li>
<li>기업이 회피할 수 없는 현재의무(Present Obligation)가 존재하고</li>
<li>그 의무 이행에 따른 자원 유출이 높은 가능성(probable)을 가지며</li>
<li>금액을 합리적으로 추정할 수 있는 경우이다.</li>
</ul>
</li>
<li>핵심: 충당부채는 “불확실하지만, 더 이상 무시할 수 없을 정도로 확정된 의무”라는 점</li>
<li>다시 확인할 자료: <a href="https://cookiedeal.io/blog/post/%EC%B6%A9%EB%8B%B9%EB%B6%80%EC%B1%84-provision">충당부채</a></li>
</ul>
<hr>
<h3 id="부채총계">부채총계</h3>
<ul>
<li>말 그대로 모든 부채의 합<ul>
<li>유동 부채(1년 이내에 상환) + 비유동부채(1년 이상 걸림)</li>
</ul>
</li>
</ul>
<h3 id="순차입금net-debt">순차입금(net debt)</h3>
<ul>
<li>총 차입금에서 회사가 보유한 현금과 예금을 차감한 것으로, 부채의 만기가 도래하였을 때 회사가 즉시 변제할 수 있는 만큼을 제외한 부채</li>
<li>공식: 이자부부채 - 현금 및 현금등가물</li>
<li><a href="https://m.blog.naver.com/themodellers/222606757685">참고자료</a></li>
</ul>
<h3 id="부채비율">부채비율</h3>
<ul>
<li>기업이 보유하고 있는 자본에 비하여 부채가 얼마나 있는지를 묻는 것</li>
<li>공식: 부채총계/자본총계*100</li>
<li>참고로 200%를 초과하면 재무구조가 불안정하다 할 수 있음</li>
</ul>
<h3 id="이자보상비율">이자보상비율</h3>
<ul>
<li>기업이 얼마나 이자비용을 상환할 수 있는지 측정하기 위함</li>
<li>실질적으로 자금을 대여해주는 이들이 사용하여 위험도를 평가할 때 사용</li>
<li>이자보상비율 = 영업이익/이자비용</li>
<li>높을수록 좋은가? 좋다. 그만큼 영업이익으로 이자비용을 줄 수 있다는 것을 입증할 수 있고, 낮다면 기업이 부담하는 부채 비용이 높을 수 있음.</li>
<li><a href="https://m.blog.naver.com/frame8717/222259976924">참고 자료</a></li>
</ul>
<hr>
<hr>
<h1 id="자본">자본</h1>
<ul>
<li>자본 = 자본금 + 자본잉여금 + 자본조정 + 이익잉여금 + 기타포괄손익누계액
<a href="https://zuzu.network/resource/guide/capital-and-investment/?tags=investment_par-value-and-capital/">참고자료</a></li>
</ul>
<hr>
<h2 id="주주와의-거래를-반영하는-항목">주주와의 거래를 반영하는 항목</h2>
<h3 id="자본금">자본금</h3>
<ul>
<li>기업이 주식을 발행해서 조달한 자금의 액면금 총액</li>
<li>1주당 액면가 * 발행한주식총수</li>
</ul>
<h3 id="자본잉여금">자본잉여금</h3>
<ul>
<li>증자나 감자 등 주주와의 거래에서 자본을 증가시키는 잉여금</li>
</ul>
<h3 id="자본조정">자본조정</h3>
<ul>
<li>기업이 주식 발행 시 액면 이하로 들어오거나, 자기주식 보유로 자본을 차감할 때 표시하는 부분</li>
</ul>
<h2 id="수익과-비용의-누적액을-반영하는-항목">수익과 비용의 누적액을 반영하는 항목</h2>
<h3 id="이익잉여금">이익잉여금</h3>
<ul>
<li>이익 누적액으로서 배당으로 지급할 수 있는 부분</li>
</ul>
<h3 id="기타포괄손익누계액">기타포괄손익누계액</h3>
<ul>
<li>자산 평가이익 등이 반영된 누적액으로 배당으로 지급하기는 어려운 부분</li>
</ul>
<hr>
<h3 id="자본총계">자본총계</h3>
<ul>
<li>자산 = 부채 + 자본 =&gt; 자본 = 자산 - 부채</li>
<li>기업의 순자산으로, 주주가 투자한 자본금과 기업이 쌓은 이익잉여금 등을 합친 금액 =&gt; 기업이 얼마 가지고 있음?</li>
</ul>
<h3 id="자기자본비율">자기자본비율</h3>
<ul>
<li>총자산 중 금융비용을 부담하지 않는 자기자본의 비중? 흠... 왜 자본이 아니라 자산이지?</li>
<li>자기자본/총자본*100</li>
</ul>
<h3 id="roe">ROE</h3>
<ul>
<li>Return On Equtiy 자기자본이익률</li>
<li>주주의 돈(자기자본)으로 1년간 벌어들인 순이익을 보여주는 지표.</li>
<li>당기순이익/자기자본*100</li>
<li>주의할점: 일시적 ROE 높아진 것일 수 있으므로 조심할 필요 있음</li>
<li><a href="https://blog.naver.com/blogfsc/223858712006">참고자료</a></li>
</ul>
<h3 id="per">PER</h3>
<ul>
<li>Price Eaning Ratio 주가수익률</li>
<li>수익 대비 주가 수준을 나타내는 지표</li>
<li>주가/주당순이익(EPS)</li>
<li>PER을 통해 이익에 비해 주가가 높은지 낮은지 알 수 있음(그렇다고 PER이 낮다고 무조건 주가가 상승 x)</li>
<li>주의할점: 성장성도 같이 고려해야함</li>
</ul>
<h3 id="pbr">PBR</h3>
<ul>
<li>Price Book-value Ratio 주가순자산비율</li>
<li>기업 순 자산 대비 주가 수준을 나타내는 지표</li>
<li>주가/주당순자산가치(BPS)</li>
<li>주의할 점: 업황, 부실여부 함께 체크</li>
</ul>
<h3 id="bps">BPS</h3>
<ul>
<li>Book-value Per Share 주당순자산가치</li>
<li>회사가 가진 총자산에서 빚을 뺀 자산 가치를 주식 수로 나눈 값.</li>
<li>계산법: 주당순자산 = 회사 순자산 / 발행된 주식 수</li>
</ul>
<h3 id="eps">eps</h3>
<ul>
<li>Earnings Per Share 주당 순이익</li>
<li>한 주당 회사가 벌어들인 순수익</li>
<li>주당순이익 = 당기순이익/가중평균유통보통주식 수</li>
</ul>
<h4 id="가중평균유통보통주식">가중평균유통보통주식</h4>
<ul>
<li>기업이 일정 회계기간 동안 유통한 보통주식 수를 시간으로 가중 평규한 수치</li>
<li>예시: 기초 10,000주 중 7월 1일에 2,000주를 유상증자(50% 시점)했다면, 가중평균주식수는 주가 됩니다.</li>
</ul>
<h3 id="주주환원배당-자사주">주주환원(배당 자사주)</h3>
<ul>
<li>기업이 벌어들인 수익의 일부를 주주(투자자)에게 돌려주는 것</li>
<li>배당도 이 주주환원 아래에 있다고 보면되며, 배당은 현금을 직접 지급하는 방식.</li>
<li>주주환원은 배당성향, 자사주 매입률 등을 포함한 종합적인 주주 가치 제고 정책을 의미</li>
</ul>
<h3 id="roa">ROA</h3>
<ul>
<li>Return On Assets 총자산 수익률</li>
<li>투자된 총자산 대비 이익 창출 능력을 나타내는 지표</li>
<li>순이익/평균 총 자산</li>
<li>ROE와의 차이: ROA는 총자산(빚 포함)을, ROE(자기자본이익률)는 주주 자본(자기 돈)만을 기준으로 한 수익률</li>
<li>ROI의 하위 유형.</li>
<li><a href="https://www.servicenow.com/kr/products/it-asset-management/what-is-return-on-assets.html">참고자료</a></li>
</ul>
<h3 id="roi">ROI</h3>
<ul>
<li>투자 수익률</li>
<li>매출액과 순익을 비교하는데 사용됨</li>
</ul>
<h3 id="psr">PSR</h3>
<ul>
<li>Price to Sales Ratio 주가매출비율</li>
<li>기업의 시가총액을 연간 총매출액으로 나눈 지표</li>
<li>시가총액/매출액 or 주가/주당매출액</li>
<li>이게 왜 필요하지? 매출 1원당 기업의 가치(주가)가 얼마로 평가받는지 나타내며, 주로 성장주나 적자 기업의 적정 주가를 판단할 때 활용</li>
</ul>
<hr>
<h1 id="단어장">단어장</h1>
<ul>
<li>수취: 돈, 물품, 문서 등을 받는 것</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jira 찍먹해보기]]></title>
            <link>https://velog.io/@yong-lee/Jira-%EC%B0%8D%EB%A8%B9%ED%95%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@yong-lee/Jira-%EC%B0%8D%EB%A8%B9%ED%95%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 31 Dec 2025 07:41:18 GMT</pubDate>
            <description><![CDATA[<h1 id="jira-완벽-정리-가이드">Jira 완벽 정리 가이드</h1>
<h2 id="핵심-요약">핵심 요약</h2>
<p><strong>Jira는 &quot;큰 목표 → 작은 작업&quot;으로 쪼개서 관리하는 프로젝트 도구입니다.</strong></p>
<ul>
<li><strong>구조</strong>: Space(Project) → Epic → Story/Task/Bug → Sub-task</li>
<li><strong>방법론</strong>: 신규 개발은 <strong>스크럼</strong>, 운영/유지보수는 <strong>칸반</strong></li>
<li><strong>핵심 역할</strong>: PO(무엇을 만들지), 스크럼 마스터(어떻게 잘 돌아가게 할지)</li>
</ul>
<hr>
<h2 id="1-jira-계층-구조">1. Jira 계층 구조</h2>
<pre><code>Epic (큰 단위: 도메인/화면 파트)
 └── Story, Task, Bug
      └── Sub-task</code></pre><table>
<thead>
<tr>
<th>이슈 타입</th>
<th>관점</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Story</strong></td>
<td>사용자</td>
<td>사용자에게 전달되는 가치/기능</td>
<td>&quot;사용자가 카카오 로그인을 할 수 있다&quot;</td>
</tr>
<tr>
<td><strong>Task</strong></td>
<td>팀 내부</td>
<td>기능 외 기술적 업무</td>
<td>DB 스키마 설계, API 명세서 작성, Redis 설정</td>
</tr>
<tr>
<td><strong>Bug</strong></td>
<td>결함</td>
<td>발견된 문제 (Epic 없이 생성 가능)</td>
<td>&quot;로그인 시 세션 만료 안 됨&quot;</td>
</tr>
<tr>
<td><strong>Sub-task</strong></td>
<td>세분화</td>
<td>상위 작업을 더 잘게 쪼갠 것</td>
<td>&quot;레몬을 짠다&quot;</td>
</tr>
</tbody></table>
<h3 id="story-vs-task-구분법">Story vs Task 구분법</h3>
<blockquote>
<p>식당 비유: <strong>&quot;스테이크를 먹을 수 있다&quot;</strong> = Story (고객 가치)<br><strong>&quot;고기 손질, 굽기, 플레이팅&quot;</strong> = Task (내부 작업)</p>
</blockquote>
<ul>
<li>Story 질문: *&quot;사용자가 뭘 할 수 있지?&quot;*</li>
<li>Task 질문: *&quot;우리가 뭘 해야 하지?&quot;*</li>
</ul>
<hr>
<h2 id="2-스크럼-vs-칸반">2. 스크럼 vs 칸반</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>스크럼</th>
<th>칸반</th>
</tr>
</thead>
<tbody><tr>
<td><strong>비유</strong></td>
<td>마라톤 페이스 조절</td>
<td>컨베이어 벨트</td>
</tr>
<tr>
<td><strong>시간 단위</strong></td>
<td>고정 스프린트 (보통 2주)</td>
<td>없음, 연속 흐름</td>
</tr>
<tr>
<td><strong>계획</strong></td>
<td>스프린트 전 확정</td>
<td>수시로 변경 가능</td>
</tr>
<tr>
<td><strong>변경</strong></td>
<td>스프린트 중 변경 지양</td>
<td>언제든 우선순위 변경</td>
</tr>
<tr>
<td><strong>회의</strong></td>
<td>플래닝 → 데일리 → 리뷰 → 회고</td>
<td>필요 시에만</td>
</tr>
<tr>
<td><strong>배포</strong></td>
<td>스프린트 종료 시</td>
<td>준비되면 바로</td>
</tr>
<tr>
<td><strong>측정</strong></td>
<td>Velocity, 번다운 차트</td>
<td>리드 타임, 사이클 타임</td>
</tr>
</tbody></table>
<h3 id="언제-뭘-쓰나">언제 뭘 쓰나?</h3>
<table>
<thead>
<tr>
<th>상황</th>
<th>추천</th>
</tr>
</thead>
<tbody><tr>
<td>신규 기능 개발, 명확한 목표, 출시 일정 고정</td>
<td><strong>스크럼</strong></td>
</tr>
<tr>
<td>운영/유지보수, CS 대응, 버그 수정, 요청이 수시로 들어옴</td>
<td><strong>칸반</strong></td>
</tr>
</tbody></table>
<p>💡 <strong>실무 팁</strong>: 신규 개발 → 스크럼, 서비스 운영 단계 → 칸반으로 전환하는 팀이 많음</p>
<hr>
<h2 id="3-스크럼-핵심-4가지">3. 스크럼 핵심 4가지</h2>
<ol>
<li><strong>고정된 스프린트 주기</strong>: 2주 안에 &quot;완료 가능한 것&quot;만 약속</li>
<li><strong>스프린트 이벤트</strong>: 플래닝 → 데일리 스크럼 → 리뷰 → 회고 (반복 개선)</li>
<li><strong>Story Points + Velocity</strong>: 팀이 얼마나 할 수 있는지 예측 가능하게</li>
<li><strong>보고서</strong>: 번다운, 속도 차트 = &quot;우리 잘하고 있나?&quot; 확인용 (목적 아님)</li>
</ol>
<hr>
<h2 id="4-핵심-역할">4. 핵심 역할</h2>
<h3 id="po-product-owner---무엇을-만들지">PO (Product Owner) - &quot;무엇을 만들지&quot;</h3>
<table>
<thead>
<tr>
<th>업무</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>백로그 관리</td>
<td>기능 정의, 우선순위 결정</td>
</tr>
<tr>
<td>요구사항 정의</td>
<td>Story 작성, 기획 의도 설명</td>
</tr>
<tr>
<td>완료 기준 판단</td>
<td>요구사항 충족 여부 확인</td>
</tr>
</tbody></table>
<h3 id="스크럼-마스터---어떻게-잘-돌아가게-할지">스크럼 마스터 - &quot;어떻게 잘 돌아가게 할지&quot;</h3>
<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>
</tbody></table>
<p>⚠️ 스크럼 마스터는 &quot;관리자&quot;가 아님 → 명령이 아니라 <strong>돕는</strong> 역할</p>
<h3 id="실무-현실">실무 현실</h3>
<table>
<thead>
<tr>
<th>상황</th>
<th>현실</th>
</tr>
</thead>
<tbody><tr>
<td>대기업</td>
<td>전담 스크럼 마스터, 전담 PO</td>
</tr>
<tr>
<td>스타트업</td>
<td>PM이 PO 겸임, 시니어 개발자가 스크럼 마스터 겸임</td>
</tr>
</tbody></table>
<hr>
<h2 id="5-story-points">5. Story Points</h2>
<h3 id="핵심-시간이-아니라-상대적-복잡도">핵심: 시간이 아니라 &quot;상대적 복잡도&quot;</h3>
<blockquote>
<p>같은 작업도 주니어는 3일, 시니어는 반나절 걸릴 수 있음.<br>하지만 <strong>해야 할 일의 복잡도</strong>는 동일 → 이걸 기준으로 산정</p>
</blockquote>
<h3 id="활용-목적">활용 목적</h3>
<table>
<thead>
<tr>
<th>목적</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>스프린트 계획</td>
<td>이번 스프린트에 몇 포인트까지 가능할까?</td>
</tr>
<tr>
<td>Velocity 측정</td>
<td>스프린트마다 완료 포인트 누적 → 평균 속도 파악</td>
</tr>
<tr>
<td>일정 예측</td>
<td>백로그 총 포인트 ÷ 평균 속도 = 예상 스프린트 수</td>
</tr>
</tbody></table>
<p>💡 대략적인 시간이 필요하면 <strong>Time Tracking</strong> 기능 활용</p>
<hr>
<h2 id="실전-예시-회원가입-기능-개선">실전 예시: 회원가입 기능 개선</h2>
<pre><code>[Epic] 회원가입 기능 개선
 │
 ├── [Story] 소셜 로그인 추가
 │    ├── [Sub-task] Google OAuth API 연동 (백엔드)
 │    ├── [Sub-task] 소셜 로그인 버튼 구현 (프론트)
 │    └── [Sub-task] QA 테스트
 │
 ├── [Task] 소셜 로그인 UI 디자인 (디자이너)
 ├── [Task] 테스트 케이스 문서 작성 (QA)
 └── [Bug] 카카오 로그인 오류</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Wheel Picker 스크롤 차단 해결 방법]]></title>
            <link>https://velog.io/@yong-lee/Wheel-Picker-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EC%B0%A8%EB%8B%A8-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@yong-lee/Wheel-Picker-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EC%B0%A8%EB%8B%A8-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 23 Dec 2025 04:38:38 GMT</pubDate>
            <description><![CDATA[<h2 id="📋-문제-상황">📋 문제 상황</h2>
<p>Wheel picker 컴포넌트에서 마우스 휠을 사용할 때, wheel picker 자체와 상위 요소(페이지) 두 개의 스크롤이 모두 움직이는 문제가 발생했습니다.</p>
<pre><code>상위 요소: min-h-screen bg-background flex flex-col overflow-y-auto
  └─ 하위 요소: relative overflow-hidden rounded-lg bg-background touch-none overscroll-none
      └─ 문제: 두 스크롤이 동시에 움직임 ❌</code></pre><h2 id="🔍-문제의-원인">🔍 문제의 원인</h2>
<h3 id="react의-onwheel-prop의-한계">React의 <code>onWheel</code> prop의 한계</h3>
<p>React의 <code>onWheel</code> prop은 내부적으로 브라우저가 <strong>passive listener</strong>로 등록할 수 있습니다.</p>
<pre><code class="language-tsx">// ❌ 작동하지 않았던 코드
&lt;div onWheel={handleIntegerWheel}&gt;
  {/* React의 onWheel은 passive listener로 등록될 수 있음 */}
&lt;/div&gt;

const handleIntegerWheel = async (e: React.WheelEvent) =&gt; {
  e.preventDefault()  // ❌ passive listener에서는 무시됨!
  e.stopPropagation()
  // ...
}</code></pre>
<h3 id="passive-listener의-특징">Passive Listener의 특징</h3>
<pre><code class="language-javascript">// 브라우저가 자동으로 passive로 등록할 수 있음
element.addEventListener(&#39;wheel&#39;, handler)
// 또는
element.addEventListener(&#39;wheel&#39;, handler, { passive: true })

// 특징:
// - preventDefault() 호출해도 무시됨 ❌
// - 성능 최적화 (스크롤이 블로킹되지 않음)
// - 스크롤 이벤트는 기본적으로 passive로 처리됨</code></pre>
<p><strong>왜 passive로 처리하나요?</strong></p>
<ul>
<li>브라우저는 성능 최적화를 위해 스크롤 이벤트를 passive로 처리합니다</li>
<li>스크롤이 블로킹되지 않아 사용자 경험이 부드럽습니다</li>
<li>하지만 우리는 스크롤을 차단해야 하므로 non-passive가 필요합니다</li>
</ul>
<h2 id="✅-해결-방법">✅ 해결 방법</h2>
<h3 id="핵심--passive-false--옵션으로-직접-이벤트-등록">핵심: <code>{ passive: false }</code> 옵션으로 직접 이벤트 등록</h3>
<p>React의 <code>onWheel</code> prop 대신, <code>useEffect</code>에서 <code>addEventListener</code>를 사용하여 <strong>non-passive listener</strong>로 직접 등록합니다.</p>
<pre><code class="language-tsx">// ✅ 해결된 코드
const handleIntegerWheel = useCallback(async (e: React.WheelEvent | { deltaY: number }) =&gt; {
  const delta = e.deltaY &gt; 0 ? 1 : -1
  const currentInteger = getIntegerPart(value)
  const newInteger = Math.max(integerMin, Math.min(integerMax, currentInteger + delta))

  if (newInteger !== currentInteger) {
    // ... 애니메이션 및 상태 업데이트
  }
}, [value, integerMin, integerMax, getIntegerPart, getDecimalPart, getIntegerY, integerControls, onChange])

// Non-passive wheel event listeners를 직접 등록
useEffect(() =&gt; {
  const integerContainer = integerContainerRef.current
  const decimalContainer = decimalContainerRef.current

  if (!integerContainer || !decimalContainer) return

  // Native wheel handler
  const handleIntegerWheelNative = (e: WheelEvent) =&gt; {
    e.preventDefault()      // ✅ 이제 확실히 작동!
    e.stopPropagation()     // ✅ 이벤트 버블링 차단
    handleIntegerWheel({ deltaY: e.deltaY } as React.WheelEvent)
  }

  const handleDecimalWheelNative = (e: WheelEvent) =&gt; {
    e.preventDefault()
    e.stopPropagation()
    handleDecimalWheel({ deltaY: e.deltaY } as React.WheelEvent)
  }

  // 핵심: { passive: false } 옵션
  integerContainer.addEventListener(&#39;wheel&#39;, handleIntegerWheelNative, { passive: false })
  decimalContainer.addEventListener(&#39;wheel&#39;, handleDecimalWheelNative, { passive: false })

  return () =&gt; {
    integerContainer.removeEventListener(&#39;wheel&#39;, handleIntegerWheelNative)
    decimalContainer.removeEventListener(&#39;wheel&#39;, handleDecimalWheelNative)
  }
}, [handleIntegerWheel, handleDecimalWheel])</code></pre>
<h3 id="non-passive-listener의-특징">Non-Passive Listener의 특징</h3>
<pre><code class="language-javascript">// 명시적으로 non-passive로 등록
element.addEventListener(&#39;wheel&#39;, handler, { passive: false })

// 특징:
// - preventDefault()가 정상 작동 ✅
// - 스크롤을 차단할 수 있음
// - 약간의 성능 오버헤드 (하지만 이 경우 필요함)</code></pre>
<h2 id="🎯-usecallback의-역할">🎯 useCallback의 역할</h2>
<p><code>useCallback</code>은 스크롤 차단과 직접적인 관련은 없지만, <code>useEffect</code>의 의존성 배열을 안정화하기 위해 필요합니다.</p>
<h3 id="usecallback-없이-사용하면-문제-발생">useCallback 없이 사용하면 (문제 발생)</h3>
<pre><code class="language-tsx">// ❌ 문제가 있는 코드
const handleIntegerWheel = async (e) =&gt; { 
  // ... 로직
}

useEffect(() =&gt; {
  // handleIntegerWheel이 의존성 배열에 있으면
  // 컴포넌트가 리렌더링될 때마다 새로운 함수가 생성되어
  // useEffect가 계속 실행됨 → 이벤트 리스너가 계속 추가/제거됨
  integerContainer.addEventListener(&#39;wheel&#39;, handler, { passive: false })
}, [handleIntegerWheel])  // handleIntegerWheel이 매번 새로 생성됨!</code></pre>
<p><strong>문제점:</strong></p>
<ul>
<li>컴포넌트가 리렌더링될 때마다 <code>handleIntegerWheel</code>이 새로운 함수로 생성됨</li>
<li><code>useEffect</code>가 계속 실행되어 이벤트 리스너가 반복적으로 추가/제거됨</li>
<li>메모리 누수 및 성능 저하 가능성</li>
</ul>
<h3 id="usecallback을-사용하면-해결">useCallback을 사용하면 (해결)</h3>
<pre><code class="language-tsx">// ✅ 올바른 코드
const handleIntegerWheel = useCallback(async (e) =&gt; {
  // ...
}, [value, integerMin, integerMax, ...])  // 의존성이 변경될 때만 새로 생성

useEffect(() =&gt; {
  // handleIntegerWheel이 의존성 배열에 있어도
  // 의존성이 실제로 변경될 때만 useEffect가 실행됨
  integerContainer.addEventListener(&#39;wheel&#39;, handler, { passive: false })
}, [handleIntegerWheel])  // 안정적인 참조</code></pre>
<p><strong>장점:</strong></p>
<ul>
<li>의존성이 실제로 변경될 때만 함수가 새로 생성됨</li>
<li><code>useEffect</code>가 불필요하게 재실행되지 않음</li>
<li>이벤트 리스너가 안정적으로 관리됨</li>
</ul>
<h2 id="🔄-전체-이벤트-흐름">🔄 전체 이벤트 흐름</h2>
<pre><code>1. 사용자가 wheel picker 위에서 마우스 휠을 돌림
   ↓
2. 브라우저가 wheel 이벤트 발생
   ↓
3. 우리가 등록한 non-passive listener가 먼저 실행
   ↓
4. e.preventDefault() 호출 → 상위 스크롤 차단 ✅
   ↓
5. e.stopPropagation() 호출 → 이벤트 버블링 차단 ✅
   ↓
6. handleIntegerWheel 실행 → wheel picker 값 변경
   ↓
7. 상위 요소로 이벤트가 전파되지 않음 → 상위 스크롤 안 움직임 ✅</code></pre><h2 id="📝-추가-적용-사항">📝 추가 적용 사항</h2>
<h3 id="css-overscroll-설정">CSS overscroll 설정</h3>
<p>상위 요소에도 <code>overscroll-contain</code> 클래스를 추가하여 추가 보호:</p>
<pre><code class="language-tsx">// features/onboarding/components/onboarding-flow.tsx
&lt;div className=&quot;min-h-screen bg-background flex flex-col overflow-y-auto overscroll-contain&quot;&gt;</code></pre>
<h3 id="터치-이벤트-처리">터치 이벤트 처리</h3>
<p>모바일 환경을 위한 터치 이벤트도 처리:</p>
<pre><code class="language-tsx">const handleTouchMove = useCallback((e: React.TouchEvent) =&gt; {
  // 상위 요소의 스크롤을 완전히 차단
  e.preventDefault()
  e.stopPropagation()
}, [])</code></pre>
<h2 id="📊-비교-passive-vs-non-passive">📊 비교: Passive vs Non-Passive</h2>
<table>
<thead>
<tr>
<th>특징</th>
<th>Passive Listener</th>
<th>Non-Passive Listener</th>
</tr>
</thead>
<tbody><tr>
<td><code>preventDefault()</code></td>
<td>❌ 무시됨</td>
<td>✅ 정상 작동</td>
</tr>
<tr>
<td>성능</td>
<td>⚡ 빠름 (블로킹 없음)</td>
<td>🐌 약간 느림 (블로킹 가능)</td>
</tr>
<tr>
<td>스크롤 차단</td>
<td>❌ 불가능</td>
<td>✅ 가능</td>
</tr>
<tr>
<td>기본 동작</td>
<td>브라우저가 자동 선택</td>
<td>명시적으로 지정 필요</td>
</tr>
<tr>
<td>사용 사례</td>
<td>일반적인 스크롤 이벤트</td>
<td>스크롤을 차단해야 하는 경우</td>
</tr>
</tbody></table>
<h2 id="🎓-핵심-요약">🎓 핵심 요약</h2>
<ol>
<li><strong>해결의 핵심</strong>: <code>{ passive: false }</code> 옵션으로 non-passive 이벤트 등록</li>
<li><strong>useCallback의 역할</strong>: <code>useEffect</code> 의존성 배열 안정화 (부수효과)</li>
<li><strong>결과</strong>: <code>preventDefault()</code>가 정상 작동하여 상위 스크롤 차단 성공</li>
</ol>
<h2 id="🔗-관련-파일">🔗 관련 파일</h2>
<ul>
<li><code>components/ui/decimal-wheel-picker.tsx</code> - Wheel picker 컴포넌트</li>
<li><code>features/onboarding/components/onboarding-flow.tsx</code> - 상위 레이아웃</li>
<li><code>app/globals.css</code> - 전역 CSS 설정</li>
</ul>
<h2 id="📚-참고-자료">📚 참고 자료</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault">MDN: Event.preventDefault()</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#passive">MDN: Passive Event Listeners</a></li>
<li><a href="https://react.dev/reference/react/useCallback">React: useCallback Hook</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 초기화 순서: @PostConstruct vs @EventListener]]></title>
            <link>https://velog.io/@yong-lee/Spring-Boot-%EC%B4%88%EA%B8%B0%ED%99%94-%EC%88%9C%EC%84%9C-PostConstruct-vs-EventListener</link>
            <guid>https://velog.io/@yong-lee/Spring-Boot-%EC%B4%88%EA%B8%B0%ED%99%94-%EC%88%9C%EC%84%9C-PostConstruct-vs-EventListener</guid>
            <pubDate>Thu, 18 Dec 2025 05:38:32 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-상황">문제 상황</h2>
<p><code>LanguageCache</code>에서 <code>@PostConstruct</code>를 사용하여 언어 데이터를 캐싱하려 했으나,
<code>languageRepository.findAll()</code>이 빈 결과를 반환하는 문제 발생.</p>
<pre><code class="language-java">@PostConstruct
public void init() {
    languageRepository.findAll().forEach(language -&gt; {
        languageCodeToIdMap.put(language.getLanguageCode(), language.getId());
    });
    log.info(&quot;Loaded {} languages into cache&quot;, languageCodeToIdMap.size());  // 0개 출력
}</code></pre>
<h2 id="원인-분석">원인 분석</h2>
<h3 id="spring-boot-초기화-순서">Spring Boot 초기화 순서</h3>
<pre><code>┌─────────────────────────────────────────────────────────────────┐
│  1. Spring Container 시작                                        │
├─────────────────────────────────────────────────────────────────┤
│  2. Bean 생성 및 의존성 주입 (DI)                                  │
├─────────────────────────────────────────────────────────────────┤
│  3. @PostConstruct 실행  ◀── 이 시점에 DB 데이터 없음!            │
├─────────────────────────────────────────────────────────────────┤
│  4. Hibernate DDL 실행 (테이블 생성)                              │
├─────────────────────────────────────────────────────────────────┤
│  5. SQL 스크립트 실행 (데이터 INSERT)                             │
│     └── defer-datasource-initialization: true 설정 필요          │
├─────────────────────────────────────────────────────────────────┤
│  6. ApplicationReadyEvent 발생  ◀── 이 시점에 DB 데이터 있음!     │
└─────────────────────────────────────────────────────────────────┘</code></pre><h3 id="관련-설정-application-localyml">관련 설정 (application-local.yml)</h3>
<pre><code class="language-yaml">jpa:
  hibernate:
    ddl-auto: create-drop
  defer-datasource-initialization: true  # DDL 이후에 SQL 스크립트 실행

sql:
  init:
    mode: always
    data-locations:
      - classpath:local-init/seed/01_languages.sql
      - classpath:local-init/seed/02_currencies.sql
      # ...</code></pre>
<h3 id="defer-datasource-initialization-true의-역할"><code>defer-datasource-initialization: true</code>의 역할</h3>
<table>
<thead>
<tr>
<th>설정</th>
<th>실행 순서</th>
</tr>
</thead>
<tbody><tr>
<td><code>false</code> (기본값)</td>
<td>SQL 스크립트 → Hibernate DDL (실패: 테이블 없음)</td>
</tr>
<tr>
<td><code>true</code></td>
<td>Hibernate DDL → SQL 스크립트 (성공: 테이블 있음)</td>
</tr>
</tbody></table>
<p><strong>주의:</strong> 이 설정과 관계없이 <code>@PostConstruct</code>는 DDL/SQL 스크립트보다 <strong>먼저</strong> 실행됨.</p>
<h2 id="spring-boot-이벤트-순서">Spring Boot 이벤트 순서</h2>
<pre><code>ApplicationStartingEvent
        ↓
ApplicationEnvironmentPreparedEvent
        ↓
ApplicationContextInitializedEvent
        ↓
ApplicationPreparedEvent
        ↓
ContextRefreshedEvent  ←  @PostConstruct 실행 시점
        ↓
ApplicationStartedEvent
        ↓
ApplicationReadyEvent  ←  @EventListener(ApplicationReadyEvent.class) 실행 시점
        ↓
(애플리케이션 실행 중...)</code></pre><h2 id="해결-방법">해결 방법</h2>
<h3 id="변경-전">변경 전</h3>
<pre><code class="language-java">import jakarta.annotation.PostConstruct;

@PostConstruct
public void init() {
    // DB 데이터 없음 - 실패
}</code></pre>
<h3 id="변경-후">변경 후</h3>
<pre><code class="language-java">import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;

@EventListener(ApplicationReadyEvent.class)
public void init() {
    // 모든 초기화 완료 후 실행 - 성공
}</code></pre>
<h2 id="초기화-방법-비교">초기화 방법 비교</h2>
<table>
<thead>
<tr>
<th>방법</th>
<th>실행 시점</th>
<th>SQL 스크립트 이후 실행?</th>
<th>사용 사례</th>
</tr>
</thead>
<tbody><tr>
<td><code>@PostConstruct</code></td>
<td>Bean 초기화 직후</td>
<td>❌</td>
<td>외부 의존성 없는 초기화</td>
</tr>
<tr>
<td><code>CommandLineRunner</code></td>
<td>애플리케이션 시작 후</td>
<td>✅</td>
<td>CLI 인자 처리</td>
</tr>
<tr>
<td><code>ApplicationRunner</code></td>
<td>애플리케이션 시작 후</td>
<td>✅</td>
<td>ApplicationArguments 필요 시</td>
</tr>
<tr>
<td><code>@EventListener(ApplicationReadyEvent.class)</code></td>
<td>완전히 준비된 후</td>
<td>✅</td>
<td>DB 데이터 의존 초기화</td>
</tr>
</tbody></table>
<h2 id="결론">결론</h2>
<ul>
<li><code>@PostConstruct</code>는 Bean 생성 직후 실행되므로 DB 초기 데이터에 의존하는 로직에는 부적합</li>
<li><code>defer-datasource-initialization: true</code> 설정은 DDL과 SQL 스크립트 순서만 조정할 뿐, <code>@PostConstruct</code> 실행 시점에는 영향 없음</li>
<li>DB 데이터에 의존하는 캐시 초기화는 <code>@EventListener(ApplicationReadyEvent.class)</code> 사용 권장</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 다국어 처리 리팩토링 기록]]></title>
            <link>https://velog.io/@yong-lee/Spring-Boot-%EB%8B%A4%EA%B5%AD%EC%96%B4-%EC%B2%98%EB%A6%AC-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@yong-lee/Spring-Boot-%EB%8B%A4%EA%B5%AD%EC%96%B4-%EC%B2%98%EB%A6%AC-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Mon, 15 Dec 2025 02:17:25 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>기존 커스텀 헤더(<code>Language-Code</code>)를 사용하던 다국어 처리 방식을 HTTP 표준 헤더(<code>Accept-Language</code>)로 변경하고, 언어 결정 로직을 최적화한 과정을 정리합니다.</p>
<hr>
<h2 id="1-커스텀-헤더-vs-표준-헤더">1. 커스텀 헤더 vs 표준 헤더</h2>
<h3 id="기존-방식">기존 방식</h3>
<pre><code class="language-java">private static final String LANGUAGE_CODE_HEADER = &quot;Language-Code&quot;;
String languageCode = request.getHeader(LANGUAGE_CODE_HEADER);</code></pre>
<h3 id="변경-이유">변경 이유</h3>
<ul>
<li><code>Accept-Language</code>는 HTTP 표준 헤더로, 브라우저가 자동으로 설정</li>
<li>클라이언트에서 별도 설정 없이도 사용자의 선호 언어 전달 가능</li>
<li>RESTful API 설계 원칙에 부합</li>
</ul>
<h3 id="변경-후">변경 후</h3>
<pre><code class="language-java">String acceptLanguage = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);</code></pre>
<h3 id="accept-language-파싱-고려사항">Accept-Language 파싱 고려사항</h3>
<p><code>Accept-Language</code> 헤더는 <code>en-US</code>, <code>ko-KR</code> 같은 locale 형식이나 <code>en</code>, <code>ko</code> 같은 단순 형식 모두 가능합니다.</p>
<p><strong>결정</strong>: Primary language code만 추출하여 사용 (예: <code>en-US</code> → <code>en</code>)</p>
<ul>
<li>DB에 저장된 <code>languageCode</code> 필드가 <code>en</code>, <code>ko</code> 형식이므로 일관성 유지</li>
<li>불필요한 복잡성 제거</li>
</ul>
<hr>
<h2 id="2-언어-fallback-체인-설계">2. 언어 Fallback 체인 설계</h2>
<h3 id="요구사항">요구사항</h3>
<ol>
<li><code>Accept-Language</code> 헤더가 있으면 해당 언어 사용</li>
<li>헤더가 없으면 로그인한 사용자의 언어 설정 사용</li>
<li>사용자 정보도 없으면 기본값(영어) 사용</li>
</ol>
<h3 id="fallback-체인">Fallback 체인</h3>
<pre><code>Accept-Language 헤더 → 사용자 언어 설정 → English (기본값)</code></pre><h3 id="유효하지-않은-언어-코드-처리">유효하지 않은 언어 코드 처리</h3>
<p><strong>고민</strong>: 헤더의 언어 코드가 DB에 없을 때 어떻게 처리할까?</p>
<p><strong>결정</strong>: 사용자 언어로 Fallback</p>
<ul>
<li>사용자가 의도적으로 설정한 언어가 있다면 그것을 존중</li>
<li>바로 영어로 가는 것보다 자연스러운 UX</li>
</ul>
<hr>
<h2 id="3-언어-캐싱-전략">3. 언어 캐싱 전략</h2>
<h3 id="문제점">문제점</h3>
<pre><code class="language-java">// 매 요청마다 DB 조회 발생
return languageRepository.findByLanguageCode(languageCode)
    .map(Language::getId)
    .orElse(null);</code></pre>
<h3 id="해결책-애플리케이션-시작-시-캐싱">해결책: 애플리케이션 시작 시 캐싱</h3>
<pre><code class="language-java">@Component
@RequiredArgsConstructor
public class LanguageCache {

    private final LanguageRepository languageRepository;
    private final Map&lt;String, Long&gt; languageCodeToIdMap = new ConcurrentHashMap&lt;&gt;();
    private Long defaultLanguageId;

    @PostConstruct
    public void init() {
        languageRepository.findAll().forEach(language -&gt; {
            languageCodeToIdMap.put(language.getLanguageCode(), language.getId());
            if (&quot;en&quot;.equals(language.getLanguageCode())) {
                defaultLanguageId = language.getId();
            }
        });
    }

    public Long getLanguageId(String languageCode) {
        return languageCodeToIdMap.get(languageCode);
    }

    public Long getDefaultLanguageId() {
        return defaultLanguageId;
    }
}</code></pre>
<h3 id="장점">장점</h3>
<ul>
<li>언어 정보는 거의 변하지 않는 정적 데이터</li>
<li>매 요청마다 DB 조회 불필요</li>
<li><code>Map</code> 조회로 O(1) 시간 복잡도</li>
</ul>
<hr>
<h2 id="4-언어-결정-로직-위치-선정">4. 언어 결정 로직 위치 선정</h2>
<h3 id="option-a-languageutil에서-처리-lazy-loading">Option A: LanguageUtil에서 처리 (Lazy Loading)</h3>
<pre><code class="language-text">Filter: Accept-Language → ThreadLocal (있으면)
LanguageUtil.getLanguageId(): ThreadLocal → User → English</code></pre>
<h3 id="option-b-languagefilter에서-처리-eager-loading">Option B: LanguageFilter에서 처리 (Eager Loading)</h3>
<pre><code class="language-text">Filter: Accept-Language → User → English → ThreadLocal
LanguageUtil.getLanguageId(): ThreadLocal만 반환</code></pre>
<h3 id="비교">비교</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>Option A</th>
<th>Option B</th>
</tr>
</thead>
<tbody><tr>
<td>로직 위치</td>
<td>분산 (Filter + Util)</td>
<td>집중 (Filter)</td>
</tr>
<tr>
<td>User 조회 시점</td>
<td>getLanguageId() 호출 시</td>
<td>요청 시작 시</td>
</tr>
<tr>
<td>LanguageUtil 복잡도</td>
<td>높음</td>
<td>낮음 (단순 holder)</td>
</tr>
<tr>
<td>테스트 용이성</td>
<td>어려움</td>
<td>쉬움</td>
</tr>
</tbody></table>
<h3 id="결정-option-b">결정: Option B</h3>
<p><strong>이유</strong>:</p>
<ol>
<li>단일 책임 원칙: 언어 결정은 Filter, 저장은 Util</li>
<li>명확한 흐름: Fallback 체인이 한 곳에서 명시적으로 보임</li>
<li>예측 가능성: 요청 시작 시 언어가 결정되어 일관성 보장</li>
<li>테스트 용이: Filter만 테스트하면 언어 결정 로직 검증 가능</li>
</ol>
<hr>
<h2 id="5-최종-구현">5. 최종 구현</h2>
<h3 id="languagefilterjava">LanguageFilter.java</h3>
<pre><code class="language-java">@Component
@RequiredArgsConstructor
public class LanguageFilter extends OncePerRequestFilter {

    private final LanguageCache languageCache;
    private final UserRepository userRepository;

    @Override
    protected void doFilterInternal(...) {
        try {
            LanguageUtil.clear();

            if (shouldApplyLanguageFilter(request)) {
                Long languageId = resolveLanguageId(request);
                LanguageUtil.setLanguageId(languageId);
            }

            filterChain.doFilter(request, response);
        } finally {
            LanguageUtil.clear();
        }
    }

    private Long resolveLanguageId(HttpServletRequest request) {
        // 1. Accept-Language 헤더
        Long languageId = resolveFromHeader(request);
        if (languageId != null) {
            return languageId;
        }

        // 2. 인증된 사용자의 언어
        languageId = resolveFromUser();
        if (languageId != null) {
            return languageId;
        }

        // 3. 기본값 (English)
        return languageCache.getDefaultLanguageId();
    }
}</code></pre>
<h3 id="languageutiljava">LanguageUtil.java</h3>
<pre><code class="language-java">public class LanguageUtil {

    private static final ThreadLocal&lt;Long&gt; languageIdHolder = new ThreadLocal&lt;&gt;();

    private LanguageUtil() {}

    public static void clear() {
        languageIdHolder.remove();
    }

    public static Long getLanguageId() {
        return languageIdHolder.get();
    }

    public static void setLanguageId(Long languageId) {
        languageIdHolder.set(languageId);
    }
}</code></pre>
<hr>
<h2 id="6-swagger-설정">6. Swagger 설정</h2>
<h3 id="문제">문제</h3>
<p><code>Accept-Language</code>는 표준 헤더지만 Swagger UI에 자동으로 표시되지 않습니다.</p>
<h3 id="해결">해결</h3>
<pre><code class="language-java">@Bean
public OperationCustomizer globalHeaderCustomizer() {
    return (operation, handlerMethod) -&gt; {
        if (shouldAddLanguageHeader(handlerMethod)) {
            operation.addParametersItem(
                new Parameter()
                    .in(&quot;header&quot;)
                    .name(HttpHeaders.ACCEPT_LANGUAGE)
                    .description(&quot;언어 코드 (예: en, ko)&quot;)
                    .required(false)
                    .example(&quot;ko&quot;)
            );
        }
        return operation;
    };
}</code></pre>
<hr>
<h2 id="7-주의사항">7. 주의사항</h2>
<h3 id="filter-순서">Filter 순서</h3>
<p><code>LanguageFilter</code>에서 사용자 언어를 조회하려면 JWT 인증 Filter가 먼저 실행되어야 합니다.</p>
<pre><code class="language-text">JwtAuthenticationFilter → LanguageFilter → Controller</code></pre>
<p><code>AuthUtil.getCurrentUserId()</code>는 <code>SecurityContextHolder</code>에서 인증 정보를 가져오므로, JWT Filter가 먼저 실행되어 인증 정보를 설정해야 합니다.</p>
<hr>
<h2 id="결론">결론</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody><tr>
<td>헤더</td>
<td>커스텀 (<code>Language-Code</code>)</td>
<td>표준 (<code>Accept-Language</code>)</td>
</tr>
<tr>
<td>DB 조회</td>
<td>매 요청마다</td>
<td>시작 시 1회 (캐싱)</td>
</tr>
<tr>
<td>Fallback</td>
<td>없음</td>
<td>Header → User → English</td>
</tr>
<tr>
<td>로직 위치</td>
<td>LanguageUtil (분산)</td>
<td>LanguageFilter (집중)</td>
</tr>
<tr>
<td>LanguageUtil</td>
<td>복잡한 로직 포함</td>
<td>단순 ThreadLocal holder</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 여행 일정 API 개발기 - QueryDSL, DTO 설계, Haversine 거리 계산]]></title>
            <link>https://velog.io/@yong-lee/Spring-Boot-%EC%97%AC%ED%96%89-%EC%9D%BC%EC%A0%95-API-%EA%B0%9C%EB%B0%9C%EA%B8%B0-QueryDSL-DTO-%EC%84%A4%EA%B3%84-Haversine-%EA%B1%B0%EB%A6%AC-%EA%B3%84%EC%82%B0</link>
            <guid>https://velog.io/@yong-lee/Spring-Boot-%EC%97%AC%ED%96%89-%EC%9D%BC%EC%A0%95-API-%EA%B0%9C%EB%B0%9C%EA%B8%B0-QueryDSL-DTO-%EC%84%A4%EA%B3%84-Haversine-%EA%B1%B0%EB%A6%AC-%EA%B3%84%EC%82%B0</guid>
            <pubDate>Wed, 10 Dec 2025 01:01:17 GMT</pubDate>
            <description><![CDATA[<p>⏺ </p>
<blockquote>
<h3 id="들어가며">들어가며</h3>
<p>  여행 플래닝 앱에서 스케줄 상세 조회 API를 구현하게 되었다. 단순히 &quot;데이터 조회해서 반환하면 되겠지&quot;라고 생각했는데, 생각보다 고민할 게 많았다.
  이 글에서는 구현 과정에서 마주친 고민들과 해결 과정을 공유한다.</p>
</blockquote>
<hr>
<h3 id="🤔-고민-1-응답에-owner를-어떻게-담을까">🤔 고민 1: 응답에 owner를 어떻게 담을까?</h3>
<h4 id="상황">상황:</h4>
<p>  스케줄에는 <strong>생성자(owner)</strong>와 <strong>초대된 참가자(participants)</strong>가 있다. 응답 설계 시 이들을 어떻게 구분할지 고민이 됐다.</p>
<h4 id="선택지">선택지:</h4>
<ol>
<li>owner_user 필드와 participants 배열을 분리</li>
<li>participants 배열에 owner를 포함하고, 첫 번째 요소로 구분</li>
</ol>
<h4 id="결정">결정:</h4>
<p>  라운지 공개용 API에서는 owner_user를 별도로 노출하고, 내 스케줄 조회 API에서는 participants 배열의 첫 번째로 owner를 넣기로 했다.</p>
<pre><code class="language-java">private List&lt;ParticipantDto&gt; getParticipants(User owner, List&lt;ScheduleMember&gt; members) {
      return Stream.concat(
          Stream.of(toParticipantDto(owner)),  // owner가 항상 첫 번째
          members.stream().map(member -&gt; toParticipantDto(member.getInvitedUser()))
      ).toList();
  }</code></pre>
<p>  💡 배운 점: API 용도에 따라 같은 데이터도 다르게 표현할 수 있다. 클라이언트 입장에서 어떤 형태가 사용하기 편한지 고민하자.</p>
<hr>
<h3 id="🤔-고민-2-중복되는-dto-어떻게-할까">🤔 고민 2: 중복되는 DTO, 어떻게 할까?</h3>
<h4 id="상황-1">상황:</h4>
<p>  ScheduleDetailResponse와 LoungeScheduleDetailResponse 안에 똑같은 구조의 TagDto, CityDto가 nested record로 있었다.</p>
<p>  // ScheduleDetailResponse 안에
  public record TagDto(Long id, String name) {}</p>
<p>  // LoungeScheduleDetailResponse 안에도
  public record TagDto(Long id, String name) {}  // 완전 똑같음!</p>
<p>  처음엔 &quot;각 Response에 종속된 DTO니까 분리된 게 맞지 않나?&quot;라고 생각했다.</p>
<h4 id="고민-과정">고민 과정:</h4>
<ul>
<li>현재 방식의 문제: 똑같은 코드가 두 군데에. 하나 수정하면 다른 것도 수정해야 함</li>
<li>&quot;나중에 달라질 수 있으니까 분리해두자&quot;: 근데 지금 똑같은데 미래를 위해 중복을 유지하는 게 맞나?</li>
<li>결론: 지금 똑같으면 하나로 합치고, 정말 달라질 때 그때 분리하자</li>
</ul>
<h4 id="결정-1">결정:</h4>
<p>  공통 DTO로 추출하고, from() 팩토리 메서드 패턴 적용:</p>
<pre><code class="language-java">public record TagDto(Long id, String name) {
      public static TagDto from(Long id, String name) {
          return new TagDto(id, name);
      }
  }</code></pre>
<pre><code class="language-java">  public record CityDto(Long cityId, String cityName,
                        String countryEmoji, String countryName) {
      public static CityDto from(Long cityId, String cityName,
                                 String countryEmoji, String countryName) {
          return new CityDto(cityId, cityName, countryEmoji, countryName);
      }
  }
</code></pre>
<p>  최종 구조:
  schedule/dto/
  ├── TagDto.java          ← 공통
  ├── CityDto.java         ← 공통
  ├── PlanDto.java         ← 공통
  ├── ScheduleDayDto.java  ← 공통
  ├── ScheduleDetailResponse.java
  │   └── ParticipantDto (nested)
  └── LoungeScheduleDetailResponse.java
      └── OwnerUserDto (nested)</p>
<p>  💡 배운 점: &quot;나중에 달라질 수 있으니까&quot;는 YAGNI(You Ain&#39;t Gonna Need It) 위반이다. 지금 중복이면 지금 제거하자.</p>
<hr>
<h3 id="🤔-고민-3-장소-간-거리를-어떻게-계산할까">🤔 고민 3: 장소 간 거리를 어떻게 계산할까?</h3>
<h4 id="상황-2">상황:</h4>
<p>  라운지 스케줄 상세 조회 시 각 장소(Plan) 사이의 거리를 보여주고 싶었다.</p>
<h4 id="선택지-1">선택지:</h4>
<ol>
<li>직선 거리 (Haversine 공식): 서버에서 바로 계산 가능</li>
<li>실제 이동 거리: Google Maps API 등 외부 서비스 필요</li>
</ol>
<h4 id="결정-2">결정:</h4>
<p>  일단 Haversine 공식으로 직선 거리 계산하기로 했다.</p>
<pre><code class="language-java">  public final class DistanceCalculator {
      private static final double EARTH_RADIUS_MILES = 3958.8;

      public static BigDecimal calculateDistanceMiles(
              BigDecimal lat1, BigDecimal lon1,
              BigDecimal lat2, BigDecimal lon2) {

          if (lat1 == null || lon1 == null || lat2 == null || lon2 == null) {
              return null;
          }

          double lat1Rad = Math.toRadians(lat1.doubleValue());
          double lat2Rad = Math.toRadians(lat2.doubleValue());
          double deltaLat = Math.toRadians(lat2.subtract(lat1).doubleValue());
          double deltaLon = Math.toRadians(lon2.subtract(lon1).doubleValue());

          double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2)
                   + Math.cos(lat1Rad) * Math.cos(lat2Rad)
                   * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);

          double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
          double distance = EARTH_RADIUS_MILES * c;

          return BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_UP);
      }
  }</code></pre>
<p>  // km일 때
  private static final double EARTH_RADIUS_KM = 6371.0;</p>
<p>  // mile일 때
  private static final double EARTH_RADIUS_MILES = 3958.8;</p>
<p>  💡 배운 점: 외부 API 연동 없이도 기본적인 거리 정보는 제공할 수 있다. 완벽하지 않아도 MVP로 충분할 수 있다.</p>
<hr>
<h3 id="😱-삽질-기록-numeric-overflow">😱 삽질 기록: numeric overflow</h3>
<h4 id="상황-3">상황:</h4>
<p>  테스트 데이터 INSERT 중 에러 발생:
    numeric field overflow - A field with precision 10, scale 8
  must round to an absolute value less than 10^2</p>
<h4 id="원인">원인:</h4>
<p>  경도(longitude) 127.0276...를 저장하려는데, precision=10, scale=8 설정으로는 정수부가 2자리밖에 안 됐다.</p>
<p>  precision 10, scale 8 → 전체 10자리 중 소수점 8자리 → 정수부 2자리
  127.xxx → 정수부 3자리 필요 → 💥 overflow!</p>
<h4 id="해결">해결:</h4>
<p>  // Before
  @Column(name = &quot;latitude&quot;, precision = 10, scale = 8)
  @Column(name = &quot;longitude&quot;, precision = 10, scale = 8)</p>
<p>  // After
  @Column(name = &quot;latitude&quot;, precision = 11, scale = 8)   // 정수부 3자리
  @Column(name = &quot;longitude&quot;, precision = 12, scale = 8)  // 정수부 4자리 (경도는 -180~180)</p>
<p>  💡 배운 점: DB 컬럼 설계할 때 실제 데이터 범위를 꼭 확인하자. 위도는 -9090, 경도는 -180180이다.</p>
<hr>
<h3 id="🔍-querydsl로-다국어-데이터-조회">🔍 QueryDSL로 다국어 데이터 조회</h3>
<p>  태그, 도시 정보가 i18n 테이블로 분리되어 있어서 JOIN이 필요했다.</p>
<pre><code class="language-java">  @Override
  public List&lt;TagDto&gt; findTagsByScheduleId(Long scheduleId, Long languageId) {
      return queryFactory
          .select(tag.id, tagI18n.name)
          .from(scheduleTag)
          .join(scheduleTag.tag, tag)
          .join(tagI18n).on(
              tagI18n.tag.eq(tag)
              .and(tagI18n.language.id.eq(languageId))  // 언어 필터
          )
          .where(scheduleTag.schedule.id.eq(scheduleId))
          .fetch()
          .stream()
          .map(tuple -&gt; TagDto.from(
              tuple.get(tag.id),
              tuple.get(tagI18n.name)
          ))
          .toList();
  }</code></pre>
<hr>
<p>  📝 마무리</p>
<p>  이번 구현에서 배운 것들:</p>
<ol>
<li><p>DTO 설계는 용도에 맞게 - 같은 데이터도 API 목적에 따라 다르게 표현</p>
</li>
<li><p>중복은 바로 제거 - &quot;나중에 달라질 수 있으니까&quot;는 핑계</p>
</li>
<li><p>MVP 먼저 - 완벽한 거리 계산보다 일단 동작하는 것부터</p>
</li>
<li><p>DB 설계 시 데이터 범위 확인 - precision/scale 실수하면 런타임 에러</p>
<p>다음엔 실제 이동 거리 계산을 위한 외부 API 연동을 고민해봐야겠다. 🚗</p>
</li>
</ol>
<hr>
<p>  참고 자료</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Haversine_formula">https://en.wikipedia.org/wiki/Haversine_formula</a></li>
<li><a href="http://querydsl.com/">http://querydsl.com/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[클로드 코드(노션 MCP)]]></title>
            <link>https://velog.io/@yong-lee/%ED%81%B4%EB%A1%9C%EB%93%9C-%EC%BD%94%EB%93%9C%EB%85%B8%EC%85%98-MCP</link>
            <guid>https://velog.io/@yong-lee/%ED%81%B4%EB%A1%9C%EB%93%9C-%EC%BD%94%EB%93%9C%EB%85%B8%EC%85%98-MCP</guid>
            <pubDate>Sun, 23 Nov 2025 08:36:10 GMT</pubDate>
            <description><![CDATA[<p>클로드 코드 문서: <a href="https://code.claude.com/docs/ko/mcp">https://code.claude.com/docs/ko/mcp</a>
notion-mcp-server 깃헙 설명 : <a href="https://github.com/makenotion/notion-mcp-server?tab=readme-ov-file#readme">https://github.com/makenotion/notion-mcp-server?tab=readme-ov-file#readme</a></p>
<h1 id="노션에서-먼저-해야할-것">노션에서 먼저 해야할 것</h1>
<h3 id="1-노션-api-페이지-이동-및-새-api-통합-생성">1. 노션 API 페이지 이동 및 새 API 통합 생성</h3>
<p><a href="https://www.notion.so/profile/integrations/">https://www.notion.so/profile/integrations/</a>
<img src="https://velog.velcdn.com/images/yong-lee/post/88b1776a-b574-4ff7-afee-1a4ee755173e/image.png" alt=""></p>
<h3 id="2-api-통합-이름-워크스페이스-선정">2. API 통합 이름, 워크스페이스 선정,</h3>
<p><img src="https://velog.velcdn.com/images/yong-lee/post/0f7a7e91-15b2-406f-827b-507f6d6ffbd2/image.png" alt=""></p>
<h3 id="3-페이지-사용-권한-관리">3. 페이지 사용 권한 관리</h3>
<p>새로운 API 통합이 성공적으로 생성하면 이런 페이지가 뜹니다.
그러면 이곳에서 &#39;사용 권한&#39;을 클릭합니다.
<img src="https://velog.velcdn.com/images/yong-lee/post/c1dc3e81-348f-44c5-893a-cbc51a94bb93/image.png" alt=""></p>
<p>그러면 거기서 편집 권한(파란 버튼)을 클릭하여 MCP로 쓸 페이지를 선택합니다.
<img src="https://velog.velcdn.com/images/yong-lee/post/a5dcf308-f5f2-4a85-8554-091db401f53d/image.png" alt=""></p>
<h3 id="4-잘-연결되어-있는지-확인">4. 잘 연결되어 있는지 확인</h3>
<p>선택했던 페이지에 직접 들어가 오른쪽 상단의 점점점 버튼을 누른 후 연결을 확인하시면 아까 생성했던 API 통합이 있는 것을 볼 수 있습니다.
없으면 거기서 추가!
<img src="https://velog.velcdn.com/images/yong-lee/post/d6fbbe50-f747-4e49-9a70-cb138a5eb8b9/image.png" alt=""></p>
<h3 id="5-클로드-코드의-configjson-파일-찾기">5. 클로드 코드의 config.json 파일 찾기</h3>
<p>현재 laptop으로 사용하는 클로드 코드에서 사용되는 config 파일을 찾을 필요가 있습니다.
<img src="https://velog.velcdn.com/images/yong-lee/post/444f57e2-58d9-4cda-9176-09007b0eda47/image.png" alt=""></p>
<h3 id="6-api-통합-키-추가하기">6. API 통합 키 추가하기</h3>
<p>들어가서 확인 할 시, env에 키에 저런 식으로 넣어주면 됩니다.
프라이빗 시크릿 키는 노션 API 통합 생성할던 곳에 들어가면 찾을 수 있습니다.
<img src="https://velog.velcdn.com/images/yong-lee/post/0207b44c-c481-4782-81ce-b0611b925742/image.png" alt=""></p>
<p>만약 claude code가 켜진 상태라면, 다시 재시작해주세요.</p>
<h3 id="7-mcp-연결-혹은-연결-확인">7. mcp 연결 혹은 연결 확인</h3>
<p><img src="blob:https://velog.io/c1728a1b-68a8-47e6-befb-7a03079caa60" alt="업로드중.."></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[클로드 코드(기본)]]></title>
            <link>https://velog.io/@yong-lee/%ED%81%B4%EB%A1%9C%EB%93%9C-%EC%BD%94%EB%93%9C</link>
            <guid>https://velog.io/@yong-lee/%ED%81%B4%EB%A1%9C%EB%93%9C-%EC%BD%94%EB%93%9C</guid>
            <pubDate>Sun, 23 Nov 2025 05:34:57 GMT</pubDate>
            <description><![CDATA[<p>클로드 코드 한국어 버전: <a href="https://code.claude.com/docs/ko/overview">https://code.claude.com/docs/ko/overview</a>
클로드 코드 영어 버전: <a href="https://code.claude.com/docs/en/overview">https://code.claude.com/docs/en/overview</a></p>
<h1 id="클로드-코드--클로드-desktopwith-mcp">클로드 코드 != 클로드 DESKTOP(with MCP)</h1>
<p>회사에서 클로드 맥스x5를 결제해줘서 이번 주에 처음 사용해봤습니다.
요즘 많이 사용한다는 MCP를 사용하여 claude와 intellij, figma, notion 등을 연동하여 사용했습니다.</p>
<p>인터넷에서 봤던 MCP 연동은 Claude Desktop을 사용하여 연동하는 것이였습니다.
인터넷의 떠다니는 블로그의 설명을 따라 Intellij와 연동할 수 있었습니다.
그리고 잘 사용하고 있다고 생각했습니다.</p>
<p>그런데 한 시니어 개발자님이 왜 그런 식으로 하고 있냐는 소리를 들었고 친절하게 Claude Code Document를 보여주면서 여러 기능들을 보여주셨습니다.</p>
<p>정말... 찐 개발자를 위한 기능들이 여기에 다 있었습니다.
그래서 현재 맡은 프로젝트에서 잘 사용해보기 위해 탐구해보기로 했습니다.</p>
<p>Claude Code 설치는 어려움 없을테니 많이 쓸거같은 기능들을 정리해보려고 합니다.</p>
<h1 id="사용-전-해야할-것intellij-기준">사용 전 해야할 것(Intellij 기준)</h1>
<ul>
<li>claude code 설치<ul>
<li>터미널에서 클로드를 사용하기 위해선 필수입니다.</li>
</ul>
</li>
<li>intellij에서 claude code plug in 설치<ul>
<li>원하는 코드를 선택할 수 있습니다.</li>
</ul>
</li>
</ul>
<h1 id="기본-명령어">기본 명령어</h1>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>claude</code></td>
<td>대화형 모드 시작</td>
<td><code>claude</code></td>
</tr>
<tr>
<td><code>claude -c</code></td>
<td>가장 최근 대화 계속</td>
<td><code>claude -c</code></td>
</tr>
<tr>
<td><code>claude -r</code></td>
<td>이전 대화 재개</td>
<td><code>claude -r</code></td>
</tr>
<tr>
<td><code>claude commit</code></td>
<td>Git 커밋 생성</td>
<td><code>claude commit</code></td>
</tr>
<tr>
<td><code>/clear</code></td>
<td>대화 기록 지우기</td>
<td><code>&gt; /clear</code></td>
</tr>
<tr>
<td><code>/help</code></td>
<td>사용 가능한 명령 표시</td>
<td><code>&gt; /help</code></td>
</tr>
<tr>
<td><code>exit</code> 또는 <code>Ctrl+C</code></td>
<td>Claude Code 종료</td>
<td><code>&gt; exit</code></td>
</tr>
</tbody></table>
<p>알면 유용한 것</p>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>?</td>
<td>?를 입력하여 사용 가능한 모든 키보드 바로가기 보기</td>
</tr>
<tr>
<td>tab</td>
<td>&#39;tab&#39; 버튼을 눌러 명령 자동 완성</td>
</tr>
<tr>
<td>↑</td>
<td>위 에로우 키를 눌러 이전 명령 기록 보기</td>
</tr>
<tr>
<td>/</td>
<td>/를 입력하여 모든 슬래시 명령 보기</td>
</tr>
</tbody></table>
<h1 id="클로드-코드-사용할-때-기억할-것">클로드 코드 사용할 때 기억할 것</h1>
<p>공식문서에서 초보자들을 위한 꿀팁을 알려주고 있습니다.</p>
<ul>
<li>Specifically!(최대한 상세하게)<ul>
<li>&quot;버그 고쳐&quot; x</li>
<li>&quot;게시글을 조회하면 연관된 댓글들이 두번씩 조회 되는 문제가 있어. N+1문제인것 같은데 이 문제를 고쳐줘.&quot;</li>
</ul>
</li>
<li>Step by Step!(단계별로)<ul>
<li>한번에 다 만들기보단 작업들을 나눠서 해결해야 한다.</li>
</ul>
</li>
<li>explore first(코드 적용하기 전, 코드 이해부터!)<ul>
<li>클로드에게 고치고 싶은 프로젝트의 대한 이해도를 높일 필요가 있습니다.<ul>
<li>예시) &#39;analyze the database schema&#39;</li>
</ul>
</li>
<li>DB 외에도 프로젝트와 관련이 있는 문서같은게 있다면 MCP 혹은 직접 데이터를 제공해주면 클로드는 더 효과적으로 코드를 만들어줄 겁니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Servlet, 그게 뭔데?]]></title>
            <link>https://velog.io/@yong-lee/Servlet-%EA%B7%B8%EA%B2%8C-%EB%AD%94%EB%8D%B0</link>
            <guid>https://velog.io/@yong-lee/Servlet-%EA%B7%B8%EA%B2%8C-%EB%AD%94%EB%8D%B0</guid>
            <pubDate>Wed, 15 Oct 2025 05:55:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&quot;서블릿을 아는 만큼 설명해주세요.&quot;라는 질문을 받았다.
그런데 막상 정확하게 무엇이다라고 설명하기 어려움을 겪게되어 정리하기로 하였다.</p>
</blockquote>
<h1 id="servlet이란">Servlet이란?</h1>
<p>서블릿은 클라이언트의 HTTP 요청을 받아서 동적인 웹 페이지나 데이터를 생성하고, 다시 클라이언트에게 응답을 보내는 자바 기반의 웹 프로그램입니다.</p>
<p>조금 더 풀어 설명하면, 웹 서버가 받은 요청을 자바 언어로 처리할 수 있도록 도와주며, 서블릿 컨테이너라는 관리자에 의해 생명주기가 관리되는 자바 클래스라고 할 수 있습니다.</p>
<p>솔직히 이렇게만 봐서는 이해하기 어렵다.
그러니 이것이 왜 필요한지 이야기해보자.</p>
<hr>
<h1 id="servlet이-필요한-이유">Servlet이 필요한 이유</h1>
<p>웹 서버가 처음 등장 시기에는 HTML과 같은 정적인 파일들만 제공했다.
사용자가 특정 URL을 요청하면 서버는 미리 저장된 HTML 파일을 그대로 보내주는 방식이다.
하지만 웹이 발전하면서 사용자마다 다른 정보를 보여주거나, 데이터베이스에서 정보를 가져와 보여주는 등 &#39;동적인&#39; 웹 페이지가 필요해진 것이다.</p>
<p>이런 문제를 해결하기 위해 자바 측에서는 서블릿이라는 기술을 개발했다.
서블릿은 다음과 같은 이유로 동적인 웹 페이지 생성에 필수적이다:</p>
<ul>
<li>효율적인 요청 처리: 서블릿은 <code>스레드</code>를 사용하여 처리하므로 메모리 사용량이 적고 처리 속도가 빠르다.</li>
<li>자바의 강점 활용: 자바 언어 강점이라 할 수 있는 객체지향, 다양한 라이브러리, 플랫폼 독립성 등을 웹 개발에서도 활용할 수 있게 해줬다.</li>
<li>데이터 공유 및 상태 관리: 서블릿은 여러 요청 간에 데이터를 공유하고 세션관리 등을 효과적으로 할 수 있는 기능을 제공한다.</li>
<li>쿠키 처리: 서블릿은 쿠키를 쉽게 처리할 수 있어 사용자 상태를 추적하는데 유용하다.</li>
<li>웹 서버와의 직접 통신: 서블릿은 웹 서버와 직접 통신할 수 있어 더 효율적인 웹 어플리케이션 개발이 가능하다.</li>
</ul>
<hr>
<h1 id="서블릿의-구조와-작동-방식">서블릿의 구조와 작동 방식</h1>
<p>일단 서블릿은 API(Application Programming Interface)라는 것을 꼭 기억해야한다.
우리가 흔히 기억하는 RestAPI와 같은 Web API가 아니다.
이 부분은 아래에서 구분하여 얘기를 해보는 것으로 할테니 서블릿은 API라는 것만 기억하자.</p>
<p>서블릿은 기본적으로 클라이언트의 HTTP 요청을 처리하고 동적인 웹 응답을 생성하는 자바 프로그램이다. 
이 과정은 주로 <code>서블릿 컨테이너</code>라는 관리자에 의해 이루어진다.</p>
<h3 id="서블릿-컨테이너의-역할">서블릿 컨테이너의 역할</h3>
<ul>
<li>서블릿 컨테이너(ex:톰캣)는 서블릿의 생명주기(생성, 초기화, 요청처리, 소멸)를 관리하는 주체다.</li>
<li>클라이언트로부터 HTTP 요청이 오면, 이 요청을 받아 처리할 적절한 서블릿을 찾아 실행한다.</li>
<li>요청을 서블릿이 처리할 수 있는 <code>HTTPServletRequest</code>객체로 변환하고, 응답을 보낼 <code>HttpServletResponse</code>객체를 생성하여 서블릿에게 전달한다.</li>
</ul>
<h3 id="서블릿의-생명주기lifecycle-메서드">서블릿의 생명주기(Lifecycle) 메서드:</h3>
<ul>
<li>init() 메서드: 서블릿이 서블릿 컨테이너에 의해 처음 로드될 때 한 번만 호출되어 초기화 작업을 수행합니다. 서블릿은 여러 클라이언트 요청을 처리할 수 있는 단일 인스턴스(싱글턴)로 메모리에 상주하며, init()은 이 인스턴스 생성 시 호출됩니다.</li>
<li>service() 메서드: 클라이언트로부터 요청이 올 때마다 서블릿 컨테이너가 호출하는 핵심 메서드입니다. 이 메서드는 요청(HttpServletRequest)과 응답(HttpServletResponse) 객체를 매개변수로 받아서 실제 비즈니스 로직을 수행합니다. (참고: HttpServlet을 상속받으면 service() 메서드 안에서 요청 방식(GET, POST 등)에 따라 자동으로 doGet(), doPost() 등을 호출해 줍니다.)</li>
<li>destroy() 메서드: 서블릿이 컨테이너에서 제거될 때 한 번만 호출되어 자원 해제 등의 마무리 작업을 수행합니다.</li>
<li></li>
</ul>
<h3 id="httpservletrequest-객체">HttpServletRequest 객체:</h3>
<ul>
<li>클라이언트로부터 전송된 HTTP 요청 정보를 담는 객체입니다.</li>
<li>요청 파라미터(Parameter), 헤더(Header), 쿠키(Cookie), 세션 정보(Session) 등을 이 객체를 통해 얻을 수 있습니다.</li>
</ul>
<h3 id="httpservletresponse-객체">HttpServletResponse 객체:</h3>
<ul>
<li>서블릿이 클라이언트에게 보낼 HTTP 응답 정보를 담는 객체입니다.</li>
<li>콘텐츠 타입(Content-Type), 상태 코드(Status Code), 응답 본문(Response Body) 등을 이 객체를 통해 설정하고 클라이언트로 전송합니다.</li>
</ul>
<hr>
<h1 id="서블릿-요청-처리-흐름">서블릿 요청 처리 흐름</h1>
<ol>
<li>클라이언트 요청: 웹 브라우저(클라이언트)가 URL을 통해 웹 서버에 HTTP 요청을 보냅니다.</li>
<li>웹 서버 전달: 웹 서버는 동적인 요청임을 인지하고 해당 요청을 서블릿 컨테이너(WAS)로 전달합니다.</li>
<li>서블릿 로드 및 init() 호출: 서블릿 컨테이너는 요청 URL에 매핑된 서블릿을 찾아 로드하고, 필요시 - init() 메서드를 호출하여 서블릿을 초기화합니다. (최초 요청 시 한 번만 발생)</li>
<li>service() 호출: 서블릿 컨테이너는 요청이 올 때마다 HttpServletRequest와 HttpServletResponse 객체를 생성하고, 서블릿의 service() (혹은 doGet(), doPost() 등) 메서드를 호출합니다. 이 과정에서 각 요청은 별도의 스레드에서 처리됩니다.</li>
<li>비즈니스 로직 수행: 서블릿은 HttpServletRequest에서 요청 데이터를 읽고, 비즈니스 로직을 수행하여 HttpServletResponse에 응답 데이터를 기록합니다.</li>
<li>응답 전송: 서블릿 컨테이너는 HttpServletResponse에 담긴 정보를 바탕으로 HTTP 응답 메시지를 생성하여 웹 서버를 통해 클라이언트에게 전송합니다.</li>
</ol>
<hr>
<h1 id="web-api-vs-서블릿-api">Web API vs 서블릿 API</h1>
<h2 id="web-api의-역할-외부-통신-규약-정의">Web API의 역할 (외부 통신 규약 정의):</h2>
<ul>
<li>Web API는 클라이언트와 서버가 어떻게 소통할지, 어떤 형식으로 데이터를 주고받을지에 대한 &#39;외부 규약&#39;을 정의합니다. (예: /users 경로로 GET 요청 시 JSON 형식의 사용자 목록 반환)</li>
<li>클라이언트는 이 Web API 규약에 따라 요청을 생성하여 서버로 보냅니다.</li>
</ul>
<h2 id="서블릿-api의-역할-규약-구현을-위한-자바-도구">서블릿 API의 역할 (규약 구현을 위한 자바 도구):</h2>
<ul>
<li>서버에 요청이 도달하면, 서블릿 컨테이너는 이 클라이언트의 HTTP 요청을 &#39;자바 프로그램&#39;이 이해하고 처리할 수 있는 형태로 변환해 줍니다. (예: HTTP 요청 메시지를 HttpServletRequest 객체로 변환)</li>
<li>이때, 개발자는 서블릿 API가 제공하는 메서드와 객체(HttpServletRequest, HttpServletResponse 등)를 사용하여 Web API 규약에 정의된 로직을 자바 코드로 구현합니다. 즉, Web API 규약을 &#39;수정&#39;하는 것이 아니라, Web API 규약을 자바 언어로 &#39;구현&#39;하고 처리하는 데 필요한 기반과 도구를 서블릿 API가 제공하는 것입니다.</li>
</ul>
<h2 id="응답-처리-및-web-api-규약-준수">응답 처리 및 Web API 규약 준수:</h2>
<ul>
<li>구현된 자바 로직이 처리된 후, 개발자는 서블릿 API를 사용하여 Web API 규약에 맞는 응답(예: JSON 형식의 데이터)을 HttpServletResponse 객체에 담습니다.</li>
<li>그러면 서블릿 컨테이너가 이 HttpServletResponse 객체에 담긴 정보를 다시 HTTP 응답 메시지 형태로 변환하여 클라이언트에게 전송합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[session vs token 기반 로그인에 대한 의문해소]]></title>
            <link>https://velog.io/@yong-lee/session-vs-token-%EA%B8%B0%EB%B0%98-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%98%EB%AC%B8%ED%95%B4%EC%86%8C</link>
            <guid>https://velog.io/@yong-lee/session-vs-token-%EA%B8%B0%EB%B0%98-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%98%EB%AC%B8%ED%95%B4%EC%86%8C</guid>
            <pubDate>Mon, 13 Oct 2025 08:11:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가 갑자기 session과 토큰이 어떻게 다른지 물어보았습니다.
실제로 구현도 해봤고 아는 내용이라고 생각했지만 상대방이 이해할 수 있게 설명하는 것은 다르다는 것을 느꼈습니다.</p>
</blockquote>
<h1 id="세션-기반-인증">세션 기반 인증</h1>
<h3 id="장점">장점:</h3>
<ul>
<li>즉각적인 무효화 가능: 서버에서 세션을 관리하기 때문에 특정 세션을 즉시 만료시키거나 무효화할 수 있어, 로그아웃 처리나 보안 사고 발생 시 대응이 용이합니다. </li>
<li>보안: 세션 ID만 클라이언트에 저장되고 실제 사용자 정보는 서버에 보관되므로, 클라이언트 측에서 민감한 정보가 노출될 위험이 적습니다.</li>
</ul>
<h3 id="단점">단점:</h3>
<ul>
<li>서버 자원 소모: 사용자 인증 정보(세션 데이터)를 서버에 저장해야 하므로, 사용자 수가 많아지면 서버의 저장 공간 및 메모리 자원을 많이 소모하게 됩니다. </li>
<li>확장성 문제: 여러 대의 서버를 사용할 경우, 사용자가 다른 서버로 요청을 보낼 때마다 세션 정보를 공유해야 하는 문제가 발생하며, 이는 로드 밸런싱 환경에서 복잡성을 증가시킵니다.</li>
<li>CORS(Cross-Origin Resource Sharing) 제약: 도메인이 다른 환경에서 쿠키를 주고받는 데 제약이 있어, MSA(마이크로서비스 아키텍처)나 모바일 환경에서는 구현이 복잡해질 수 있습니다.</li>
</ul>
<h3 id="세션-기반-인증의-단점-극복-방안">세션 기반 인증의 단점 극복 방안</h3>
<p>세션 기반 인증의 주요 단점은 확장성 부족과 서버 자원 소모였습니다. 이를 극복하기 위한 방법은 다음과 같습니다.</p>
<ul>
<li>분산 세션 관리 시스템 도입 (Redis, Memcached 등)<ul>
<li>문제점: 여러 대의 서버를 운영할 때, 각 서버가 자신의 로컬 메모리에 세션 정보를 저장하면, 사용자의 요청이 다른 서버로 라우팅될 경우 기존 세션을 찾을 수 없어 재로그인을 해야 하는 문제가 발생합니다.</li>
<li>해결책: 세션 정보를 모든 서버가 공유하는 별도의 저장소(예: Redis, Memcached, 데이터베이스)에 저장합니다. 이렇게 하면 어떤 서버로 요청이 오든 동일한 세션 정보에 접근할 수 있어, 서버 확장에 용이해집니다. </li>
</ul>
</li>
<li>Sticky Session (세션 고정)<ul>
<li>문제점: 위와 동일하게 여러 서버 환경에서 세션 일관성 문제가 발생합니다.</li>
<li>해결책: 로드 밸런서가 특정 사용자의 요청을 항상 동일한 서버로 보내도록 설정합니다. 이는 비교적 간단하게 세션 문제를 해결할 수 있지만, 특정 서버에 부하가 집중되거나 서버가 다운될 경우 다른 서버로 failover가 원활하지 않을 수 있어 완전한 해결책은 아닙니다. </li>
</ul>
</li>
<li>세션 타임아웃 및 주기적인 세션 삭제<ul>
<li>문제점: 사용하지 않는 세션이 서버 메모리에 계속 남아있으면 자원 소모가 심해집니다.</li>
<li>해결책: 일정 시간 이상 활동이 없는 세션은 자동으로 만료시키거나, 주기적으로 만료된 세션을 정리하여 서버 자원을 효율적으로 관리합니다.</li>
</ul>
</li>
</ul>
<hr>
<h1 id="토큰-기반-인증">토큰 기반 인증</h1>
<h3 id="장점-1">장점:</h3>
<ul>
<li>Stateless(무상태성): 서버가 클라이언트의 상태를 저장하지 않습니다. 토큰 자체가 사용자의 인증 정보를 포함하고 있어 서버의 부담을 줄이고 확장성을 향상시킵니다. </li>
<li>확장성: 서버의 상태를 유지할 필요가 없어 여러 대의 서버에 쉽게 분산할 수 있고, 로드 밸런싱에 유리합니다. 또한, 여러 서비스 간에 토큰을 공유하여 사용할 수 있어 마이크로서비스 환경에 적합합니다. </li>
<li>CORS에 유리: 토큰은 HTTP 헤더를 통해 전달되므로 도메인 제약 없이 요청을 보낼 수 있어, 다양한 클라이언트(웹, 모바일 앱)에서 유연하게 사용할 수 있습니다.</li>
<li>성능 향상: 사용자가 자격 증명을 보내는 횟수를 줄일 수 있습니다. </li>
</ul>
<h3 id="단점-1">단점:</h3>
<ul>
<li>즉각적인 무효화 어려움: 토큰은 한 번 발급되면 유효기간이 만료될 때까지 유효합니다. 따라서 강제 로그아웃이나 토큰 탈취 시 즉각적으로 토큰을 무효화하기 어렵습니다. (Redis 등을 활용한 블랙리스트 구현으로 해결 가능) </li>
<li>토큰 탈취 시 위험: 클라이언트 측에 토큰이 저장되므로, XSS(Cross-Site Scripting) 공격 등으로 토큰이 탈취될 경우 보안에 취약해질 수 있습니다.</li>
<li>토큰 크기: 토큰에 많은 정보를 담을수록 토큰의 크기가 커져 네트워크 부하를 증가시킬 수 있습니다.</li>
</ul>
<h3 id="토큰-기반-인증의-단점-극복-방안">토큰 기반 인증의 단점 극복 방안</h3>
<p>토큰 기반 인증의 주요 단점은 즉각적인 무효화 어려움과 토큰 탈취 시 보안 취약성이었습니다. 이를 극복하기 위한 방법은 다음과 같습니다.</p>
<ul>
<li>리프레시 토큰(Refresh Token) 도입<ul>
<li>문제점: 액세스 토큰은 무상태성 때문에 한번 발급되면 만료되기 전까지 강제로 무효화하기 어렵습니다. 만약 유효기간이 긴 액세스 토큰이 탈취되면 큰 보안 위협이 됩니다. </li>
<li>해결책: 유효기간이 짧은 <strong>액세스 토큰(Access Token)</strong>과 유효기간이 긴 <strong>리프레시 토큰(Refresh Token)</strong>을 함께 사용합니다.
액세스 토큰은 실제 리소스 접근에 사용되며, 탈취되어도 유효기간이 짧아 피해를 최소화합니다.
리프레시 토큰은 서버에 안전하게 저장(예: HTTP-only 쿠키)하고, 액세스 토큰이 만료되면 이를 사용하여 새로운 액세스 토큰을 발급받습니다.
서버에서는 리프레시 토큰을 관리하여 필요시 개별 리프레시 토큰을 즉시 만료시킬 수 있어, 토큰 강제 만료 및 로그아웃 기능을 구현할 수 있습니다. </li>
</ul>
</li>
<li>블랙리스트(Blacklist) 또는 화이트리스트(Whitelist) 관리<ul>
<li>문제점: 리프레시 토큰을 사용하더라도, 이미 발급된 액세스 토큰은 만료 시간까지 유효합니다.</li>
<li>해결책: 사용자가 로그아웃하거나 보안상의 이유로 토큰을 즉시 무효화해야 할 경우, 해당 토큰(혹은 토큰의 고유 ID)을 블랙리스트에 추가하여 서버에서 유효하지 않은 토큰으로 처리합니다. Redis와 같은 빠른 NoSQL 데이터베이스에 저장하여 검증하는 방식으로 구현할 수 있습니다.</li>
</ul>
</li>
<li>토큰 저장 위치와 전송 보안 강화<ul>
<li>문제점: 클라이언트 측에 저장되는 토큰(특히 액세스 토큰)은 XSS 공격 등으로 탈취될 위험이 있습니다.</li>
<li>해결책:<ul>
<li>HTTP-only 쿠키 사용: JavaScript로 쿠키에 접근하는 것을 막아 XSS 공격으로부터 토큰을 보호할 수 있습니다.</li>
<li>HTTPS 프로토콜 사용: 모든 통신을 암호화하여 중간자 공격(Man-in-the-Middle Attack)으로부터 토큰이 탈취되는 것을 방지합니다.</li>
<li>로컬 스토리지 대신 세션 스토리지 사용: 브라우저 종료 시 토큰이 자동으로 삭제되도록 하여 지속적인 위협 노출 시간을 줄일 수 있습니다.</li>
</ul>
</li>
</ul>
</li>
<li>토큰 페이로드 최소화<ul>
<li>문제점: 토큰에 많은 정보를 담으면 크기가 커져 네트워크 부하를 증가시킬 수 있습니다.</li>
<li>해결책: JWT 페이로드에는 필수적인 정보(예: 사용자 ID, 권한)만 담고, 추가적인 사용자 정보는 서버에서 데이터베이스 조회를 통해 가져오도록 합니다.</li>
</ul>
</li>
</ul>
<hr>
<h1 id="어떤-프로젝트에-어떤-인증이-더-적합한가요">어떤 프로젝트에 어떤 인증이 더 적합한가요?</h1>
<h3 id="세션-기반-인증-1">세션 기반 인증:</h3>
<ul>
<li>상태 유지가 중요한 경우: 전통적인 웹 애플리케이션처럼 서버에서 사용자 상태(예: 장바구니 정보, 로그인 상태)를 적극적으로 관리해야 하는 경우에 적합합니다.</li>
<li>소규모 서비스 또는 단일 서버 환경: 서버 확장 및 분산 환경의 복잡성을 크게 고려하지 않아도 되는 경우에 더 간단하게 구현할 수 있습니다.</li>
</ul>
<h3 id="토큰-기반-인증-1">토큰 기반 인증:</h3>
<ul>
<li>SPA 및 모바일 애플리케이션: 백엔드 API와 프론트엔드가 분리된 SPA나 모바일 앱 환경에서 클라이언트가 서버에 구애받지 않고 유연하게 인증을 처리할 수 있습니다.</li>
<li>마이크로서비스 아키텍처: 여러 개의 독립적인 서비스들이 서로 통신해야 하는 복잡한 분산 시스템 환경에서 서비스 간의 인증을 효율적으로 처리할 수 있습니다.</li>
<li>확장성이 중요한 대규모 서비스: 사용자가 급증하거나 트래픽이 많아 서버 확장이 자주 필요한 서비스에 적합합니다.</li>
</ul>
<hr>
<h1 id="어떤-방식을-더-많이-사용하고-그-이유는-무엇인가요">어떤 방식을 더 많이 사용하고, 그 이유는 무엇인가요?</h1>
<p>최근에는 토큰 기반 인증(특히 JWT) 방식이 더 많이 사용되는 추세입니다.</p>
<p>그 이유는 웹 서비스의 발전과 밀접하게 관련이 있습니다. 현대의 웹 애플리케이션은 SPA(Single Page Application), 모바일 앱, 그리고 다양한 마이크로서비스가 연동되는 복잡한 아키텍처를 가집니다. 이러한 환경에서 토큰 기반 인증은 다음과 같은 장점 때문에 선호됩니다.</p>
<ul>
<li>확장성: 서버의 상태를 저장할 필요가 없어 서버 확장이 용이하고, 여러 서버 간의 세션 동기화 문제를 해결할 수 있습니다.</li>
<li>유연성: 특정 도메인에 종속되지 않고, 모바일 앱이나 다른 서비스와의 연동에도 유연하게 대처할 수 있습니다.</li>
<li>분산 환경에 적합: 마이크로서비스 아키텍처에서 각 서비스가 독립적으로 인증을 처리하거나 토큰을 공유할 수 있어 효율적입니다. </li>
</ul>
<hr>
<h1 id="면접-인터뷰-예상-질문">면접 인터뷰 예상 질문</h1>
<ol>
<li>세션 기반 인증과 토큰 기반 인증(JWT)의 차이점을 설명해 주세요. </li>
<li>각각의 장단점은 무엇이며, 어떤 상황에서 더 적합하다고 생각하나요? </li>
<li>JWT(JSON Web Token)는 무엇이며, 어떻게 동작하는지 설명해 주세요. </li>
<li>JWT의 구조(헤더, 페이로드, 서명)에 대해 아는 대로 설명해 주세요. </li>
<li>토큰 기반 인증에서 로그아웃은 어떻게 구현해야 할까요?</li>
<li>토큰이 탈취되었을 때 발생할 수 있는 문제점과 해결 방안은 무엇이 있을까요?</li>
<li>액세스 토큰(Access Token)과 리프레시 토큰(Refresh Token)은 무엇이며, 왜 두 가지 토큰을 함께 사용하는 것이 좋은가요?</li>
<li>세션 하이재킹(Session Hijacking)과 같은 공격에 대해 설명하고, 세션 기반 인증에서는 어떻게 방어할 수 있을까요?</li>
<li>토큰 기반 인증 시 발생할 수 있는 보안 취약점(예: XSS)은 무엇이며, 이를 어떻게 방지할 수 있을까요? </li>
<li>본인이 개발한 프로젝트에서는 어떤 인증 방식을 사용했고, 그 방식을 선택한 이유는 무엇인가요?</li>
</ol>
<hr>
<h1 id="면접-인터뷰-답변">면접 인터뷰 답변</h1>
<ol>
<li>세션 기반 인증과 토큰 기반 인증(JWT)의 차이점을 설명해 주세요. </li>
</ol>
<ul>
<li>가장 큰 차이점은 유저의 상태를 서버가 직접 관리하느냐 아니냐의 차이라고 생각합니다. 토큰은 주로 JWT를 사용합니다. Header, Payload, Signature를 기반으로 하여 직렬화를 해주고 서버에 이 로그인 데이터를 저장하지 않습니다. 외부에서 이 토큰을 저장하고 있기 때문에 MSA같은 여러 서버를 사용할 때 혹은 Single Page Application할 때 유리합니다. 다만 외부에 이 토큰을 관리하고 토큰을 임의로 삭제가 불가능하기 때문에 토큰 탈취를 조심해야합니다.
그리고 session은 서버가 직접 로그인 데이터를 들고 있습니다.
그렇기 때문에 세션 만료나 로그아웃 기능을 만들 때 유리한 측면이 있습니다.
다만 서버가 직접 이 데이터를 가지고 있기 때문에 확장성면에서 떨어지는 문제가 있습니다.</li>
</ul>
<ol start="2">
<li>각각의 장단점은 무엇이며, 어떤 상황에서 더 적합하다고 생각하나요? </li>
</ol>
<ul>
<li>session 기반은 프론트 혹은 클라이언트 측에 세션 아이디만 주기 때문에 안정성 올라갑니다. 그리고 서버가 로그인 데이터를 직접 들고 있기 때문에 로그아웃 혹은 만료 기능을 넣기 쉽습니다.
하지만 서버가 직접 들고 있기 때문에 서버가 늘어났을 때 복잡해질 수 있어서 확장성이 떨어집니다.
토큰은 탈취 문제를 고려해야합니다.
서버 안이 아닌 외부에 이 토큰을 가지고 있기 때문에 이 토큰을 가지고 있다면 어디서든 쓸 수 있기 때문입니다.
하지만 확장성 높고 JWT같이 직렬화 역직렬화의 대한 최적화가 높기 때문에 성능상 유리할 수 있습니다.</li>
</ul>
<ol start="3">
<li>JWT(JSON Web Token)는 무엇이며, 어떻게 동작하는지 설명해 주세요. </li>
</ol>
<ul>
<li>JWT는 Header, Payload, Signature를 종합하여 직렬화한 키라고 볼 수 있습니다.</li>
</ul>
<ol start="4">
<li>JWT의 구조(헤더, 페이로드, 서명)에 대해 아는 대로 설명해 주세요. </li>
</ol>
<ul>
<li>Header에서 알고리즘을 고르고, payload에 유저를 식별할 수 있는 데이터를 집어넣고, Signature에 이 jwt의 비밀번호를 집어넣는 형식입니다.</li>
</ul>
<ol start="5">
<li>토큰 기반 인증에서 로그아웃은 어떻게 구현해야 할까요?</li>
</ol>
<ul>
<li>토큰의 경우에는 유효기간을 정해야하는데 이 기간이 지나기 전까지는 삭제되지 않습니다.
그렇기 때문에 저는 redis를 사용하여 블랙리스트를 만들었습니다.
서버 측에서 만약 이 블랙리스트에 해당된다면 더 이상 이 토큰으로 로그인 상태를 유지할 수 없게 만들었습니다.</li>
</ul>
<ol start="6">
<li>토큰이 탈취되었을 때 발생할 수 있는 문제점과 해결 방안은 무엇이 있을까요?</li>
</ol>
<ul>
<li>토큰이 탈취될 경우 어디서든 누구나 그 토큰을 사용하여 로그인할 수 있습니다.
이 문제를 해결하기 위해 토큰의 유효기간을 짧게 만듭니다.
토큰이 만료된다면 또 다시 로그인해야하는 번거로운 문제가 있기 때문에 accessToken과 refreshToken을 만들어 이 문제를 해결할 수 있습니다.</li>
</ul>
<ol start="8">
<li>세션 하이재킹(Session Hijacking)과 같은 공격에 대해 설명하고, 세션 기반 인증에서는 어떻게 방어할 수 있을까요?</li>
</ol>
<ul>
<li>세션 하이재킹(Session Hijacking)은 공격자가 합법적인 사용자의 세션을 가로채서 해당 사용자인 것처럼 웹 애플리케이션에 접근하는 공격 기법입니다.
방어 방법은 여러가지 있습니다.
https를 사용하여 네트워크상에 sessionID 노출을 차단합니다.
secure과 httpOnly 쿠키 설정을 하여 https에서만 쿠키를 사용할 수 있게 합니다.
세션에 유효기간을 만들어 탈취된 세션의 대한 사용시간이 제한할 수 있습니다.
(추가로 SameSite 쿠키 속성 사용: SameSite=Strict 또는 Lax 설정을 통해 크로스 사이트 요청에서 쿠키가 전송되지 않도록 제한합니다.)</li>
</ul>
<ol start="9">
<li><p>토큰 기반 인증 시 발생할 수 있는 보안 취약점은 무엇이며, 이를 어떻게 방지할 수 있을까요? 
취약점으로는 XSS와 CSRF 방법으로 토큰이 탈취될 위험이 있습니다.
XSS의 문제를 해결하기 위해 HttpOnly 플래그가 설정된 쿠키를 사용하여 JavaScript에서 토큰에 접근할 수 없도록 해야합니다.
만약 로컬이나 세션 스토리지에 저장한다면 이런 취약점에 대응하기 어렵기 때문에 쿠키에 저장하고 설정을 잘 해줘야합니다.</p>
</li>
<li><p>본인이 개발한 프로젝트에서는 어떤 인증 방식을 사용했고, 그 방식을 선택한 이유는 무엇인가요?
저는 주로 토큰 기반 방식을 선호하여 사용했습니다.
프로젝트를 하다보면 최소한으로 만들었지만 점점 거대해지는 경향이 있습니다.
MSA까지는 아니더라도 서버가 여러 개가 되는 상황이 많이 발생하면서 프로젝트 때마다 처음부터 확장성에 대한 고려를 하게 되었습니다.
그래서 토큰 방식이 익숙해졌습니다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향 프로그래밍]]></title>
            <link>https://velog.io/@yong-lee/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@yong-lee/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Tue, 30 Sep 2025 13:22:22 GMT</pubDate>
            <description><![CDATA[<h3 id="oop가-뭔지-설명해주세요">OOP가 뭔지 설명해주세요.</h3>
<ul>
<li><p>OOP는 오브젝트를 가지고 프로그램을 만드는 방법입니다.
현실 세계를 모방해서 사물을 객체화하여 정의하고, 이 객체들의 상태와 행동을 중심으로 프로그램을 구성합니다.
덕분에 코드 재사용성, 유지보수, 확장성이라는 장점을 가지게 됩니다.</p>
</li>
<li><p>핵심키워드:</p>
<ul>
<li>클래스: 객체를 만드는 템플릿(설계도)</li>
<li>객체: 클래스 설계도에 따라 실제로 만들어진 실체. 자기만의 고유한 속성과 행동을 가지고 있음</li>
<li>추상화: 복잡한 내용을 다 보여주지 않고, 핵심 기능만 제공</li>
<li>캡슐화: 객체 내부 구현을 숨기고 외부에서는 접근 불가능하게 막는 것</li>
<li>상속: 부모 클래스의 속성과 메서드를 자식 클래스가 물려받아 사용하는 것</li>
<li>다형성: 똑같은 기능이더라도 내부 동작을 다르게 하는 것</li>
<li>SOLID 원칙: 아래에서 자세히 설명</li>
</ul>
</li>
</ul>
<h3 id="class와-object에-대해-설명해주세요">Class와 Object에 대해 설명해주세요.</h3>
<ul>
<li>class는 어떤 완성체를 만들기 위한 설계도같은 존재입니다. 그리고 이 클래스로 찍어내서 만들어낸 것이 오브젝트라고 할 수 있습니다.</li>
<li>추가로 그 오브젝트가 메모리에 올라가서 실제로 사용 가능한 상태가 되면 그걸 인스턴스라고 합니다.</li>
</ul>
<h3 id="다형성-개념을-설명하고-프로젝트에-적용한-사례가-있다면-얘기하세요">다형성 개념을 설명하고, 프로젝트에 적용한 사례가 있다면 얘기하세요.</h3>
<ul>
<li>다형성은 똑같은 기능이더라도 내부 구조를 여러 방법으로 하는 것을 말합니다. 하나의 인터페이스나 부모 클래스를 통해서 여러 형태의 객체를 다룰 수 있게 해주는 기능입니다.</li>
<li>저는 헬스 첼린지라는 프로그램에서 로그인 기능을 만들 때 소셜로그인과 id비번으로 로그인하는 두가지 방법을 만들었습니다.
이때 로그인을 하기 위해 JWT 토큰을 받기위한 방법을 달리하기 위해 인터페이스를 만들었고 원하는 토큰만 응답받을 수 있게하였습니다.</li>
</ul>
<h3 id="캡슐화란-무엇인가요">캡슐화란 무엇인가요?</h3>
<ul>
<li>클래스의 속성과 메서드를 외부에서 접근을 막는 것을 캡슐화라고 합니다.
접근 단계로는 public private protected가 있으며,
private을 하게 된다면 해당 클래스 내부에서만 해당 데이터를 수정할 수 있습니다.</li>
<li>캡슐화를 하는 이유는 객체 외부에서 잘못된 데이터 사용을 막아서 데이터 보호할 수 있기 때문입니다.</li>
</ul>
<h3 id="상속의-개념을-설명하고-장단점을-얘기하세요">상속의 개념을 설명하고, 장단점을 얘기하세요.</h3>
<ul>
<li>개념은 부모클래스가 자식클래스에게 속성과 메서드를 물려주는 것을 말합니다.</li>
<li>주로 상속은 <code>IS-A</code> 관계일 때 많이 사용됩니다. (Car IS-A Vehicle)</li>
<li>장점으로는 코드 재사용성이 증가하여 반복된 코드를 줄일 수 있습니다. 자식 클래스 코드는 변경하지 않고 부모 클래스 하나만 수정하면 되기 때문에 유지보수성이 높습니다.</li>
<li>단점으로는 강한 결합도가 있습니다.
부모 클래스 하나에 결집되어 있기 때문에 유연성이 떨어집니다.
설계 자체가 까다로울 수 있습니다.</li>
</ul>
<h3 id="solid-원칙이란-무엇인가요">SOLID 원칙이란 무엇인가요?</h3>
<ul>
<li>클린코드를 위한 5가지 객체지향 설계 원칙을 말합니다.
이 5가지는 단일 책임, 개방 폐쇄, 리스코프 치환, 인터페이스 분리, 의존성 역전이라는 원칙입니다.</li>
</ul>
<h3 id="단일-책임-원칙을-설명하세요">단일 책임 원칙을 설명하세요.</h3>
<ul>
<li>SRP(Single Responsibility Principle)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[자료구조]]></title>
            <link>https://velog.io/@yong-lee/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@yong-lee/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Mon, 29 Sep 2025 06:52:21 GMT</pubDate>
            <description><![CDATA[<h3 id="자료구조data-structure가-무엇인지-설명해주세요">자료구조(data structure)가 무엇인지 설명해주세요?</h3>
<p>데이터 저장을 시작으로, 데이터를 어떤 형태로 조직하느냐에 따라 프로그램의 성능, 시간복잡도, 공간복잡도에 지대한 영향을 미치는 존재입니다.
자료구조의 예시로는 링크드 리스트, 어레이 등이 있습니다.</p>
<h3 id="시간-복잡도와-공간-복잡도를-말씀하셨는데-조금-더-구체적으로-설명해주세요">시간 복잡도와 공간 복잡도를 말씀하셨는데 조금 더 구체적으로 설명해주세요.</h3>
<ul>
<li>시간 복잡도는 알고리즘이 특정 작업을 완료하는 데 최악의 상황에 걸리는 시간의 양을 나타냅니다.</li>
<li>공간 복잡도는 작업을 완료하는 데 필요한 <code>메모리 공간의 양</code>을 나타냅니다.</li>
<li>이것이 중요한 이유는 개발자가 만든 프로그램이 어떤 환경에서 얼마나 빠르게 작동하고 얼마나 적은 자원을 사용하는지를 예측하고 평가하는 기준이 되기 때문입니다.</li>
</ul>
<h3 id="어레이과-링크드-리스트의-특징과-다른점-그리고-어떠한-상황에-쓰나요">어레이과 링크드 리스트의 특징과 다른점 그리고 어떠한 상황에 쓰나요?</h3>
<ul>
<li><p>어레이 같은 타입의 데이터를 메모리상에 연속적으로 할당하여 저장합니다.
인덱스를 통해 아주 빠른 시간(O(1)) 안에 접근할 수 있는 강점이 있습니다.
하지만, 한번 선언된 크기를 변경하는 것은 어렵고, 중간에 데이터를 삽입하거나 삭제할 때 해당 위치 뒤에 데이터를 모두 옮겨야합니다.
예시) [0,1,2,3,4] --2에 해당하는 인덱스 없애고 싶어--&gt; [0,1,3,4] == 2가 사라지면서 3의 인덱스는 3-&gt;2 변경, 4의 인덱스는 4-&gt;3 변경</p>
</li>
<li><p>링크드 리스트는 각 노드가 데이터와 다음 노드의 주소를 가지고는 비연속적인 메모리 공간에 저장됩니다.
어레이처럼 데이터가 차곡차곡 쌇여있는게 아닌, 중구난방으로 데이터가 위치합니다.
차곡차곡 쌓인것이 아니기 때문에 원하는 곳에 노드를 만들어 삽입 혹은 삭제가 간단합니다.
다음 노드의 주소만 바꿔주면 되기 때문입니다.
하지만, 특정 데이터를 찾기 위해서는 처음 노드부터 순차적으로(O(N)) 찾아야하기 때문에 느린 편입니다.</p>
</li>
<li><p>결론적으로, 크기가 고정적인 경우에는 배열, 삽입/삭제가 빈번하다면 링크드 리스트 사용하는게 좋습니다.
만약 크기가 고정이 아니지만 중간 삽입/삭제가 아니라면 어레이 리스트를 사용하는게 효율적입니다.</p>
</li>
</ul>
<h3 id="개발에서-특히-중요하다고-생각하는-자료구조가-있나요">개발에서 특히 중요하다고 생각하는 자료구조가 있나요?</h3>
<p>모든 자료구조가 중요하다고 생각하지만, 지금 머릿속에 떠오르는 것은 <code>해시 테이블</code>입니다.
키-벨류 형태로 데이터를 저장하고 빠르게 검색, 삽입, 삭제할 수 있어 인증 정보 관리, 캐싱 시스템 구현, API 요청 처리 등 다양한 곳에 활용됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[동시성]]></title>
            <link>https://velog.io/@yong-lee/%EB%8F%99%EC%8B%9C%EC%84%B1</link>
            <guid>https://velog.io/@yong-lee/%EB%8F%99%EC%8B%9C%EC%84%B1</guid>
            <pubDate>Wed, 24 Sep 2025 08:58:47 GMT</pubDate>
            <description><![CDATA[<h1 id="🎯-동시성-제어-어디까지-알고-계신가요-jvm--db-레벨-락-완벽-분석">🎯 동시성 제어, 어디까지 알고 계신가요? JVM &amp; DB 레벨 락 완벽 분석!</h1>
<p>동시성 문제 해결의 핵심 개념인 JVM 레벨 동시성 제어와 DB 레벨 동시성 제어에 대해 함께 알아보겠습니다. 이 두 가지를 정확히 이해하고 상황에 맞춰 활용하는 것이 안정적이고 효율적인 시스템을 구축하는 데 매우 중요합니다.</p>
<h1 id="1-동시성의-기본-이해">1. 동시성의 기본 이해</h1>
<ul>
<li><p>정의: 여러 스레드나 사용자가 공유 자원(데이터, 메모리 등)에 동시에 접근할 때 발생하는 문제와 이를 효율적으로 처리하는 방법입니다.</p>
</li>
<li><p>핵심 예시: 재고가 단 1개 남았을 때, 여러 사용자가 동시에 구매를 요청하더라도 단 한 명만이 성공적으로 구매할 수 있도록 처리하는 상황이 대표적인 동시성 문제 해결의 예시입니다.</p>
<h1 id="2-jvm-레벨-동시성-제어">2. JVM 레벨 동시성 제어</h1>
<p>이 부분은 자바 애플리케이션의 단일 JVM(메모리) 내부에서 여러 스레드가 공유하는 인메모리 데이터 (변수, 객체 등)의 일관성을 보장하기 위해 사용됩니다.</p>
</li>
<li><p>synchronized 키워드: 특정 코드 블록이나 메서드를 한 번에 한 스레드만 실행하도록 하는 상호 배제(Mutex) 메커니즘을 제공합니다. 이는 단순한 공유 자원 접근에 유용합니다. (예: count++와 같은 원자적 연산 보호)</p>
</li>
<li><p>wait() / notify() / notifyAll() 메서드: synchronized 블록 내에서 스레드 간에 특정 조건이 충족될 때까지 대기하거나 깨우는 통신 메커니즘입니다. 이는 스레드 간의 <strong>협업(Cooperation)</strong>이 필요할 때 주로 사용됩니다.</p>
<ul>
<li>synchronized만으로 충분한 경우 (increment() 예시)와 wait/notify가 필요한 경우 (생산자-소비자 예시)를 구분하여 살펴보았습니다.</li>
</ul>
</li>
</ul>
<p>예시: 생산자-소비자 패턴 (JVM 레벨 동시성 제어)
다음 코드는 wait()와 notifyAll()을 활용하여 생산자 스레드와 소비자 스레드가 공유 큐를 안전하게 사용하는 예시입니다. 큐가 가득 차면 생산자는 대기하고, 큐가 비면 소비자가 대기하며 서로에게 알림을 보냅니다.</p>
<pre><code class="language-java">import java.util.LinkedList;
import java.util.Queue;

public class Main {
    private static final int BUFFER_SIZE = 5;
    private final Queue&lt;Integer&gt; queue = new LinkedList&lt;&gt;();
    private final int maxSize;

    public Main(int maxSize) {
        this.maxSize = maxSize;
    }

    public synchronized void produce(int item) throws InterruptedException {
        // 큐가 가득 찼으면 대기
        while (queue.size() == maxSize) {
            System.out.println(&quot;큐가 가득 찼어요! 생산자 대기 중...&quot;);
            wait(); // 다른 스레드가 notify() 호출할 때까지 대기
        }

        // 아이템 추가
        queue.add(item);
        System.out.println(&quot;생산: &quot; + item + &quot; (큐 크기: &quot; + queue.size() + &quot;)&quot;);

        // 소비자에게 알림
        notifyAll();
    }

    public synchronized int consume() throws InterruptedException {
        // 큐가 비었으면 대기
        while (queue.isEmpty()) {
            System.out.println(&quot;큐가 비었어요! 소비자 대기 중...&quot;);
            wait(); // 다른 스레드가 notify() 호출할 때까지 대기
        }

        // 아이템 꺼내기
        int item = queue.poll();
        System.out.println(&quot;소비: &quot; + item + &quot; (큐 크기: &quot; + queue.size() + &quot;)&quot;);

        // 생산자에게 알림
        notifyAll();
        return item;
    }

    public static void main(String[] args) {
        Main example = new Main(BUFFER_SIZE);

        // 생산자 스레드
        Thread producer = new Thread(() -&gt; {
            try {
                for (int i = 0; i &lt; 20; i++) {
                    example.produce(i);
                    Thread.sleep(300); // 생산 속도 조절
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 소비자 스레드
        Thread consumer = new Thread(() -&gt; {
            try {
                for (int i = 0; i &lt; 20; i++) {
                    example.consume();
                    Thread.sleep(100); // 소비 속도 조절 (생산보다 느림)
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producer.start();
        consumer.start();
    }
}</code></pre>
<h1 id="3-db-레벨-동시성-제어">3. DB 레벨 동시성 제어</h1>
<p>이 영역은 <strong>DB(데이터베이스)</strong>에 저장된 영구 데이터에 여러 서버 인스턴스 또는 여러 애플리케이션의 트랜잭션들이 동시에 접근하여 변경할 때 데이터 무결성을 보장하기 위해 사용됩니다.</p>
<p>비관적 락 (Pessimistic Lock)</p>
<ul>
<li>개념: &quot;어차피 충돌이 발생할 것이니, 미리 잠가버리고 내가 먼저 처리하겠다!&quot;는 비관적인 관점에서 접근합니다.</li>
<li>동작: 데이터를 읽기 전에 미리 락을 걸어서 다른 트랜잭션의 접근(읽기/쓰기)을 명시적으로 차단합니다. 첫 번째 트랜잭션이 작업을 완료하고 락을 해제해야 다른 트랜잭션이 접근할 수 있습니다.</li>
<li>JPA: @Lock(LockModeType.PESSIMISTIC_WRITE) 어노테이션을 사용하며, DB의 SELECT FOR UPDATE 쿼리로 변환됩니다.</li>
<li>핵심: 이 락은 해당 쿼리로 선택된 특정 레코드(데이터)에만 걸립니다. (예: Stock 테이블의 id=1인 데이터에만!) 다른 id의 데이터나 다른 테이블의 데이터는 영향을 받지 않습니다. PESSIMISTIC_WRITE는 해당 레코드에 대한 모든 (읽기/쓰기) 접근을 락이 풀릴 때까지 대기시킵니다.</li>
<li>장점: 데이터 무결성 보장이 확실하며, 구현이 비교적 간단합니다.</li>
<li>단점: 높은 대기 시간, 전반적인 시스템 성능 저하, 데드락 발생 위험이 있습니다.</li>
</ul>
<p>낙관적 락 (Optimistic Lock)</p>
<ul>
<li>개념: &quot;충돌이 자주 발생하지 않을 것이니, 일단 다 같이 작업하고 문제가 생기면 그때 처리하자!&quot;는 낙관적인 관점에서 접근합니다.</li>
<li>동작: 락을 미리 걸지 않습니다. 데이터를 읽을 때 버전(또는 타임스탬프) 정보를 같이 가져옵니다. 이후 업데이트 시점에, 자신이 가져왔던 버전과 DB에 현재 저장된 데이터의 버전이 같은지 확인합니다. 버전이 다르면 (즉, 그 사이에 다른 트랜잭션이 데이터를 변경했다면) 충돌로 간주하고 OptimisticLockException을 발생시킵니다.</li>
<li>JPA: 엔티티에 @Version 필드를 추가하면 JPA가 자동으로 관리하며, 업데이트 시 WHERE 절에 버전 조건을 추가하여 검증합니다.</li>
<li>핵심: 버전 정보를 통해 &quot;내가 조회한 이후로 다른 사용자가 이 데이터를 건드렸는지&quot;를 확인하는 방식으로 충돌을 감지합니다.</li>
<li>장점: 높은 동시성 제공, 데드락 발생 없음, 불필요한 DB 자원 점유가 없어 효율적입니다.</li>
<li>단점: 충돌 시 예외가 발생하므로 재시도 로직 구현이 필요합니다 (Spring Retry, 수동 루프, AOP 등을 통해 처리 가능). 경쟁이 심할 때는 계속 재시도만 하다 비효율적이 될 수 있습니다.</li>
</ul>
<h1 id="4-jvm-vs-db-레벨-락-결론은">4. JVM vs. DB 레벨 락, 결론은?</h1>
<p>두 가지 동시성 제어 방식을 모두 이해하고 상황에 따라 적절히 결합해야 합니다.</p>
<ul>
<li>JVM 레벨 락: 주로 서버 메모리 내부의 공유 자원 보호에 사용됩니다.</li>
<li>DB 레벨 락: 주로 데이터베이스에 저장된 영구 데이터 보호에 사용됩니다.</li>
</ul>
<p>대부분의 웹 애플리케이션에서 동시성 문제의 핵심은 DB 데이터를 여러 트랜잭션이 동시에 조작할 때 발생하기 때문에, 낙관적/비관적 락이 더 중요하고 빈번하게 사용됩니다. JVM 내부의 인메모리 데이터를 보호할 필요가 있을 때만 synchronized나 Lock 등을 추가적으로 사용하는 것이 일반적입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향 프로그래밍, 추상화]]></title>
            <link>https://velog.io/@yong-lee/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%B6%94%EC%83%81%ED%99%94-%EB%8F%99%EC%8B%9C%EC%84%B1</link>
            <guid>https://velog.io/@yong-lee/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%B6%94%EC%83%81%ED%99%94-%EB%8F%99%EC%8B%9C%EC%84%B1</guid>
            <pubDate>Tue, 23 Sep 2025 06:47:08 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>개체지향 프로그래밍(OOP)
객체지향은 프로그램을 객체들의 모임으로 보는 패러다임이에요. 객체는 데이터(속성)와 기능(메서드)을 가진 단위입니다.</p>
</blockquote>
<h1 id="핵심-특징">핵심 특징</h1>
<ul>
<li>캡슐화: 데이터와 메서드를 하나로 묶고 외부에서의 접근을 제한</li>
<li>상속: 기존 클래스를 확장해 새로운 클래스 생성</li>
<li>다형성: 같은 인터페이스로 다양한 구현체 접근</li>
<li>추상화: 공통된 특성을 뽑아내 일반화</li>
</ul>
<hr>
<h1 id="예시-1">예시 1</h1>
<pre><code class="language-java">// 동물 추상 클래스

// 클래스 해석

// 1. 추상 클래스는 미완성 설계도이므로 `Animal myPet = Animal(&quot;초코&quot;)` x 이런 식으로 인스턴스화는 불가능하다.
// 2. 추상 클래스는 여러 클래스에서 공통으로 사용되는 메소드와 타입이 있다면 상위 클래스 형태로 사용된다.
abstract class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }
    // 3. 추상 메서드는 상속받은 클래스 쪽에서 오버라이드화하여서만 사용가능하다.(반드시 override하여 메서드가 상속된 클래스에 있어야한다! 없으면 컴파일러 에러 생김)
    public abstract void makeSound(); // 추상 메서드

    public void sleep() {
        System.out.println(name + &quot;이(가) 자고 있습니다.&quot;);
    }
}

// 상속을 통한 구체 클래스
class Dog extends Animal {
    // super는 상위 클래스의 오브젝트를 하위 클래스에서 끌어다 쓸 때 쓰는 겁니다.(메서드, 타입 등 다 끌어다 쓸 수 있음)
    public Dog(String name) {
        super(name);
    }

    //위에서 말한 오버라이드화한 메서드
    @Override
    public void makeSound() {
        System.out.println(name + &quot;: 멍멍!&quot;);
    }

    public void fetch() {
        System.out.println(name + &quot;이(가) 공을 가져옵니다.&quot;);
    }
}

// 실행 코드
public class Main {
    public static void main(String[] args) {
        Animal myPet = new Dog(&quot;초코&quot;);
        myPet.makeSound(); // 다형성
        myPet.sleep();
    }
}</code></pre>
<h2 id="예시-1을-본-후에-장점들을-생각해보자">예시 1을 본 후에 장점들을 생각해보자</h2>
<ul>
<li>각 클래스에 똑같은 코드를 적을 필요가 없어졌다.(추상 클래스에서 한 곳에만 적으면 끝)</li>
<li>캡슐화를 통해 외부에서 마음대로 데이터가 변하지 않게 되었다(생성자로만 이름을 받아오기 때문)</li>
<li>메서드를 추상화해서 각 클래스에 꼭 필요한 메서드를 만드는 것을 의무화 시켰다.</li>
</ul>
<hr>
<h1 id="추상화-방법에는-두-가지가-있다">추상화 방법에는 두 가지가 있다.</h1>
<blockquote>
<p>추상화는 복잡한 시스템에서 핵심적인 부분만 추출하여 간결하게 표현하는 것이에요.</p>
</blockquote>
<h2 id="추상화-방법">추상화 방법</h2>
<ul>
<li>추상 클래스: 미완성된 설계도로, 직접 인스턴스 생성 불가</li>
<li>인터페이스: 메서드 시그니처만 정의하고 구현은 각 클래스에 맡김</li>
</ul>
<h1 id="예시-2">예시 2</h1>
<pre><code class="language-java">// 인터페이스를 통한 추상화
// 앞서 얘기했듯 인터페이스에는 메서드가 있다고 구현만 해둔다.
// 이 인터페이스를 implement할 경우 그 클래스에서는 반드시 인터페이스에 있던 메서드를 모두 사용해야한다.
interface PaymentProcessor {
    boolean processPayment(double amount);
    void cancelPayment(String transactionId);
}

// 구현체 1
class CreditCardProcessor implements PaymentProcessor {
    @Override
    public boolean processPayment(double amount) {
        System.out.println(&quot;신용카드로 &quot; + amount + &quot;원 결제 처리&quot;);
        // 실제 결제 로직
        return true;
    }

    @Override
    public void cancelPayment(String transactionId) {
        System.out.println(&quot;거래 ID: &quot; + transactionId + &quot; 취소됨&quot;);
    }
}

// 구현체 2
class PayPalProcessor implements PaymentProcessor {
    @Override
    public boolean processPayment(double amount) {
        System.out.println(&quot;PayPal로 &quot; + amount + &quot;원 결제 처리&quot;);
        // 실제 결제 로직
        return true;
    }

    @Override
    public void cancelPayment(String transactionId) {
        System.out.println(&quot;PayPal 거래 ID: &quot; + transactionId + &quot; 취소됨&quot;);
    }
}</code></pre>
<h2 id="예시-2를-본-후-인터페이스의-장점은">예시 2를 본 후 인터페이스의 장점은?</h2>
<ul>
<li>표준화 및 구현 강제<ul>
<li>예시 1에 있던 추상 메서드와 같다고 보면 된다. 인터페이스에 있는 메서드를 반드시 사용함으로써 오류를 차단하고 프로세스 자체를 표준화할 수 있습니다.</li>
</ul>
</li>
<li>다양화<ul>
<li>프로세스 자체는 인터페이스로 메서드 이름이 같더라도 방법에 차이가 있을 수 있습니다. 인터페이스로 선언하고 원하는 방법에 따라 사용할 수 있습니다.</li>
</ul>
</li>
<li>느슨한 결합 (확장성 &amp; 유지보수성)<ul>
<li>인터페이스를 사용하면 코드를 짤 때 구현 클래스에 종속되지 않고 인터페이스에만 의존하게 됩니다. 이걸 <strong>느슨한 결합(Loose Coupling)</strong>이라고 합니다.</li>
<li>예를 들어, 결제 시스템을 만드는데 초반엔 CreditCardProcessor만 썼습니다. 근데 나중에 KakaoPayProcessor도 추가하고 싶으면, 그냥 새로운 클래스가 PaymentProcessor 인터페이스를 구현하게 만들면 끝! 기존 코드(PaymentProcessor 타입으로 결제하는 부분)는 하나도 안 바꿔도 됩니다.
이렇게 되면 나중에 기능을 바꾸거나 추가할 때 유지보수나 확장이 훨씬 쉬워져요!</li>
</ul>
</li>
<li>다중 상속의 대안<ul>
<li>extends와 다르게 implements의 특징은 다중 상속이 가능하다는겁니다.</li>
<li>class Monkey implements interfaceA, interfaceB...
이런식으로 여러 인터페이스를 쓸 수 있습니다. 이걸 통해 여러 인터페이스가 제공하는 기능들을 한 클래스에 부여할 수 있어서, 다중 상속의 장점을 어느 정도 가져가면서도 복잡성은 줄이는 효과가 있습니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[IaaS vs PaaS vs SaaS]]></title>
            <link>https://velog.io/@yong-lee/IaaS-vs-PaaS-vs-SaaS</link>
            <guid>https://velog.io/@yong-lee/IaaS-vs-PaaS-vs-SaaS</guid>
            <pubDate>Wed, 10 Sep 2025 06:50:46 GMT</pubDate>
            <description><![CDATA[<h1 id="iaas">IaaS</h1>
<ul>
<li>Infrastructure-as-a-Service</li>
<li>인프라형 클라우드 서비스</li>
</ul>
<h3 id="예시">예시)</h3>
<ul>
<li>AWS: EC2, S3, VPC</li>
<li>GCP: Compute Engine</li>
<li>Azure: Virtual Machines</li>
</ul>
<p>사용자가 관리 하는 것: <code>OS + 런타임 + 앱 + 데이터 → 사용자가 관리</code>
아무것도 없는 서버 하나를 달랑 줍니다.
그리고 그 서버 하나에 필요한 인프라를 직접 구축하여 사용합니다.</p>
<p>IaaS는 직접 구축해야하기 때문에 배포할 때 직접 세팅해야해서 느릴 수 밖에 없습니다. 
하지만 OS부터 인프라를 구축하기 때문에 기존 환경을 그대로 복사하여 서버를 이전하기 쉽다는 장점이 있습니다.(확장성 up)</p>
<h1 id="paas">PaaS</h1>
<ul>
<li>Platform-as-a-Service</li>
</ul>
<h3 id="예시-1">예시)</h3>
<ul>
<li>AWS: Elastic Beanstalk, Lambda(서버리스도 PaaS에 가까움)</li>
<li>GCP: App Engine</li>
<li>Azure: App Service</li>
<li>Heroku, Vercel, Render</li>
</ul>
<p>사용자가 관리 하는 것: <code>앱 + 데이터 → 사용자가 관리</code>
히로쿠같은 플랫폼을 예시로 들자면 알아서 플랫폼에서 서버하나를 얻습니다.
이때 원하는 인프라를 직접 구축하는 것이 아닌 원하는 인프라를 플랫폼에서 선택하여 구축합니다.</p>
<p>PaaS는 플랫폼에서 알아서 구축해주기 때문에 배포가 빠르지만, 플랫폼에 맞춰서 해야하기 때문에 자유도가 작을 수 밖에 없습니다.
플랫폼의 클라우드에서 정해준 OS를 사용하기 때문에 런타임만 사용 가능하므로 자유도가 작습니다.
그래서 서버를 이전하는 것이 쉽지 않습니다.(확장성 down)</p>
<h1 id="saas">SaaS</h1>
<ul>
<li>Software-as-a-Service</li>
<li>서비스형 클라우드 서비스</li>
</ul>
<h3 id="예시-2">예시)</h3>
<ul>
<li>Google Workspace (Gmail, Google Docs)</li>
<li>Microsoft 365 (Office Online)</li>
<li>Slack, Zoom, Notion, Figma</li>
<li>Netflix, Spotify (컨슈머 서비스도 SaaS에 포함)</li>
</ul>
<p>사용자가 관리 하는 것: x 없어용
모두 클라우드가 관리
완전한 서비스를 클라우드 서비스로부터 제공받아 사용합니다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th><strong>IaaS</strong></th>
<th><strong>PaaS</strong></th>
<th><strong>SaaS</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>제공 범위</strong></td>
<td>인프라(서버, 스토리지, 네트워크)</td>
<td>인프라 + OS + 런타임 + 개발도구</td>
<td>완성된 소프트웨어</td>
</tr>
<tr>
<td><strong>사용자 관리</strong></td>
<td>OS ~ 앱까지 직접 관리</td>
<td>앱 + 데이터만 관리</td>
<td>없음</td>
</tr>
<tr>
<td><strong>배포 속도</strong></td>
<td>느림 (모든 세팅 직접)</td>
<td>빠름 (코드만 배포)</td>
<td>즉시 사용</td>
</tr>
<tr>
<td><strong>유연성</strong></td>
<td>매우 높음</td>
<td>중간</td>
<td>매우 낮음</td>
</tr>
<tr>
<td><strong>예시 서비스</strong></td>
<td>AWS EC2, GCP Compute Engine</td>
<td>AWS Elastic Beanstalk, Heroku</td>
<td>Google Docs, Slack, Netflix</td>
</tr>
<tr>
<td><strong>비유</strong></td>
<td>땅만 빌려줌 → 건물 직접 지어야 함</td>
<td>완성된 아파트 제공</td>
<td>호텔 숙박</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[On-Premise vs Off-Premise]]></title>
            <link>https://velog.io/@yong-lee/On-Premise-vs-Off-Premise</link>
            <guid>https://velog.io/@yong-lee/On-Premise-vs-Off-Premise</guid>
            <pubDate>Wed, 10 Sep 2025 06:21:05 GMT</pubDate>
            <description><![CDATA[<h1 id="premise가-뭐지">Premise가 뭐지?</h1>
<p>premise라는 단어는 논리에서 &#39;전제&#39;라는 뜻으로 많이 쓰입니다.
하지만, IT맥락으로 보았을 땐, 물리적인 시설을 뜻합니다.</p>
<h1 id="on-premise란">On-premise란?</h1>
<p>on-premise는 물리적인 서버, 하드웨어, 소프트웨어를 직접 구축한 채 관리하는 방식입니다.
회사 자체에서 서버실이 따로 두고 시스템을 운영하는 형태를 말합니다.</p>
<h3 id="장점">장점:</h3>
<ul>
<li>데이터 보안 통제력이 높습니다.</li>
<li>자체적으로 커스터마이징이 자유롭습니다.</li>
</ul>
<h3 id="단점">단점:</h3>
<ul>
<li>물리적인 시스템 구축으로 인해 초기 비용이 큽니다.</li>
<li>유지보수에 인력과 비용이 많이 듭니다.</li>
</ul>
<h1 id="off-premise">Off-premise</h1>
<p>흔하게 사용되는 클라우드 서비스를 말한다고 볼 수 있습니다.
외부 업체의 인프라를 이용하는 방식이며 주로 AWS, Azure, GCP 등의 클라우드 서비스가 대표적입니다.</p>
<h3 id="장점-1">장점:</h3>
<ul>
<li>초기비용이 적습니다.</li>
<li>확장성이 좋습니다.</li>
<li>유지보수 부담이 적습니다.</li>
</ul>
<h3 id="단점-1">단점:</h3>
<ul>
<li>외부에 데이터가 있는 것이기 때문에 클라우드 서비스측에 보안 문제가 생기면 데이터가 위험할 수 있습니다.</li>
<li>커스터마이징이 제한적이며, 사용하고 있는 클라우드 서비스 측의 룰을 따라야합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[클라우드 vs 클라우드 이전]]></title>
            <link>https://velog.io/@yong-lee/%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C</link>
            <guid>https://velog.io/@yong-lee/%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C</guid>
            <pubDate>Tue, 09 Sep 2025 05:19:18 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Cloud 가상머신</p>
</blockquote>
<h1 id="클라우드가-생기기-전-전통적-배포방식">클라우드가 생기기 전.. 전통적 배포방식</h1>
<p>물리적인 컴퓨터(서버) 한 대에 하나의 OS를 깔고 여러 프로그램을 설치하는 방식</p>
<h3 id="특징">특징</h3>
<ol>
<li>서버 하나 =&gt; OS가 하나, 앱이 여러 개</li>
<li>하드웨어(CPU, RAM 등등) 위에 OS를 직접 설치</li>
<li>CPU, 메모리, 스토리지가 한 OS가 독점 사용</li>
<li>OS에 문제가 생기면 전체 서버에 영향이 생김</li>
</ol>
<h3 id="문제점">문제점:</h3>
<ul>
<li>어떤 프로그램을 설치했을 때 다른 앱에 영향을 미칩니다. -&gt; 독립적이지 않음</li>
</ul>
<h1 id="가상화-배포방식">가상화 배포방식</h1>
<p>가상머신을 기반으로 배포하는 방식</p>
<p>가상머신이란? 컴퓨터의 하드웨어를 소프트웨어적으로 구현한 것을 말합니다.</p>
<h3 id="특징-1">특징</h3>
<ol>
<li>서버 하나 =&gt; VM(virtual machine) 여러 개 실행<ul>
<li>어떻게? 서버 위에 Hypervisor라는 것이 올라간다.(VM 주머니)</li>
</ul>
</li>
<li>VM마다 독립적으로 OS를 가진다.</li>
<li>Hypervisor가 자원(CPU, 메모리, 스토리지)을 분할해 VM에게 할당</li>
<li>VM하나가 다운되어도 다른 VM의 OS에는 영향 x</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[API]]></title>
            <link>https://velog.io/@yong-lee/API</link>
            <guid>https://velog.io/@yong-lee/API</guid>
            <pubDate>Tue, 09 Sep 2025 01:54:25 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>API(Application Programming Interface)
둘 이상의 컴퓨터 프로그램이 서로 통신하는 방법이자 컴퓨터 사이에 있는 <code>중계 계층</code>을 의미합니다.</p>
</blockquote>
<h1 id="인터페이스란">인터페이스란?</h1>
<ul>
<li>서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 접점이나 경계면입니다.</li>
<li>특징으로는 내부서버가 어떻게 구현되어있는지는 서로 알 수 없는 상태로 통신 된다는 겁니다.</li>
</ul>
<p>쉽게 얘기하면 시스템끼리 정보를 주고 받는 장소라고 이해했습니다.
두 나라끼리 무역은 어떤 특정한 지역에서만 거래하는게 합법인 것과 비슷한 느낌이라고 생각합니다.</p>
<h1 id="api를-쓰는-이유">API를 쓰는 이유</h1>
<h2 id="1-보안적-요소">1. 보안적 요소</h2>
<ul>
<li>서버 내부적인 코드를 모두 보여주는 것은 위험합니다. 개발자가 모르는 취약점이 있을 수 있고, DB 설계 구조나 프로젝트의 코드를 숨기고 싶을 수 있기 때문입니다.</li>
</ul>
<h2 id="2-필요한-데이터-혹은-정보만-교환할-수-있습니다">2. 필요한 데이터 혹은 정보만 교환할 수 있습니다.</h2>
<ul>
<li>사실 사용자 입장에서는 서비스를 사용할 때 서버가 어떻게 구현되고 있는지 알 필요 없습니다.</li>
<li>원하는 정보와 데이터만 잘 받아오면 되기 때문입니다.</li>
</ul>
<h2 id="3-개발-프로세스-단순화">3. 개발 프로세스 단순화</h2>
<ul>
<li>Open API의 경우 앱 개발 프로세스를 단순화 시키고 시간과 비용을 절약할 수 있습니다.</li>
</ul>
<h2 id="4-매번-사용자가-앱을-업데이트-하는-일이-줄어든다">4. 매번 사용자가 앱을 업데이트 하는 일이 줄어든다.</h2>
<ul>
<li>내부 프로세스에 어떠한 수정되었을 때 유저는 앱을 업데이트 해줘야할까요?</li>
<li>그럴 필요가 없습니다. 이유는 API 중계 계층에서는 데이터를 똑같이 받아옵니다.</li>
<li>예시) A라는 나라와 B라는 나라가 있는데 Z지역에서 무역을 합니다. 이때 A는 원래 H라는 지역에서 물건을 가져왔는데 J라는 지역에서 물건을 가져오기로 했습니다. 이때 B는 그것을 꼭 알아야할까요? 몰라도 상관없습니다. 중요한 것은 약속했던 물건을 제대로 받냐 못받냐니까요. </li>
</ul>
<h1 id="api의-종류">API의 종류</h1>
<h2 id="private">private</h2>
<ul>
<li>주로 hashKey를 하드코딩하여 서버와 서버간의 통신합니다.</li>
<li>주로 비즈니스 파트너와 사용됩니다.</li>
<li>목적은 아무나 사용할 수 있는게 아닌 비밀스럽게 파트너와 hashKey를 공유해 통신한다는겁니다.</li>
</ul>
<h2 id="public">public</h2>
<ul>
<li>누구든 서버를 사용할 수 있게 한 겁니다.</li>
<li>누구든 쓸 수 있기 때문에 트래픽을 방지하는 시스템이 있습니다. (예: 하루 쵸어수의 제한, 계정당 몇 개 등등)</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>