<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bom_0320.log</title>
        <link>https://velog.io/</link>
        <description>FE 개발 꿈틀이</description>
        <lastBuildDate>Mon, 10 Nov 2025 11:11:45 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bom_0320.log</title>
            <url>https://velog.velcdn.com/images/bom_0320/profile/65f8fd9f-a573-40c7-9fd8-f4d0f71fe786/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bom_0320.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bom_0320" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[전국 SW 연합 해커톤_대상 수상]]></title>
            <link>https://velog.io/@bom_0320/%EC%A0%84%EA%B5%AD-SW-%EC%97%B0%ED%95%A9-%ED%95%B4%EC%BB%A4%ED%86%A4%EB%8C%80%EC%83%81-%EC%88%98%EC%83%81</link>
            <guid>https://velog.io/@bom_0320/%EC%A0%84%EA%B5%AD-SW-%EC%97%B0%ED%95%A9-%ED%95%B4%EC%BB%A4%ED%86%A4%EB%8C%80%EC%83%81-%EC%88%98%EC%83%81</guid>
            <pubDate>Mon, 10 Nov 2025 11:11:45 GMT</pubDate>
            <description><![CDATA[<p>2025년 11월 5일부터 7일까지 2박 3일간 경주 사한셀렉트호텔에서 열린 ‘전국 SW 마이스터고 연합 해커톤’에 참가하였습니다. 본 행사는 과학기술정보통신부가 주최하고 정보통신기획평가원(IITP)과 전국 4개 SW마이스터고(대덕·대구·광주·부산)가 공동으로 주관하는 규모 있는 전국 단위 행사로, 매년 비슷한 시기에 열려 각 학교 대표 학생들이 한 자리에 모여 개발을 하는 자리인데요.
<img src="https://velog.velcdn.com/images/bom_0320/post/794fe250-4cce-416b-9b2a-ae4aaeacb44a/image.png" alt=""></p>
<p>올해 해커톤의 대주제는 “AI·SW, Connecting Value : AI·SW로 가치를 연결하라” 였는데요. 저는 광주소프트웨어 마이스터고를 대표에 참가하였습니다.  마찬가지로 다른 학교에서 선발된 학생들과 함께 4인 1팀으로 구성되어 협업을 진행하였습니다. </p>
<h2 id="기획-및-문제-정의">기획 및 문제 정의</h2>
<p>저희 팀은 이번 해커톤에서 ‘지역사회·문화’ 문화 분야를 선택하였는데요. </p>
<p>특히 다문화 가정 학부모님들이 학교와의 의사소통에서 겪는 언어적 장벽에 주목하였습니다.  현재 몇몇 학교에서는 가정통신문과 같은 주요 공지사항을 문자나 QR 코드 형태로 제공하고는 있으나, 문체가 공문서체, 한자어 중심의 문어체로 구성되어 있어 이해가 어렵다는 문제가 있었습니다. 또한 자동 번역 기능을 활용하더라도 <strong>의역·오역 문제</strong>가 자주 발생하고, 검수 절차가 없이 긴급 안내 시 오류가 발생할 가능성이 높다는 점도 확인하였습니다.
<img src="https://velog.velcdn.com/images/bom_0320/post/24bba71b-1bf1-410b-834a-d1e6563d7094/image.png" alt=""></p>
<p>기존 번역 앱(예: 알번역, 파파고 등)은 단순히 문장 구조나 의미를 단순히 치환하는 수준에 그쳐, 다문화 가정의 학부모가 한국어를 익히거나 지역 사회에 진출할 기회를 제공하지 못하며, 문맥을 이해하는 데에도 한계가 있었습니다.</p>
<p>그 결과, 다문화 학부모들은 학교생활 참여에 제약을 받고, 나아가 지역 사회나 복지기관과의 연결이 단절되는 문제를 겪고 있었습니다.</p>
<h2 id="솔루션">솔루션</h2>
<p>저희 팀은 이러한 한계를 극복하기 위해 ‘단순 번역이 아닌, 이해 가능한 언어로의 변환’이라는 새로운 방향을 제시하였습니다. </p>
<p>즉, 학부모가 단순히 문서를 읽는 데 그치지 않고, <strong>AI를 통해 문장을 이해하고, 직접 표현을 연습할 수 있는 상호작용형 플랫폼</strong>을 구축하였습니다. </p>
<p>이를 위해  <strong>국립국어원의 1단계 어휘 데이터</strong>를 기반으로, 공문서체로 작성된 문장을 초급 수준의 일상 언어로 변환하는 기능을 구현하였으며, 이를 통해 학부모가 문서를 쉽게 이해하면서도 동시에 한국어를 자연스럽게 학습할 수 있도록 유도하였습니다. </p>
<p>결과적으로 <strong>‘학습 → 이해 → 참여’로 이어지는 언어 습득의 선순환 구조</strong>를 실현하였습니다.</p>
<p><img src="https://velog.velcdn.com/images/bom_0320/post/9acd6af6-631f-48ec-ad52-514baaf59dc4/image.png" alt="">
저희 서비스명 ClipCat은 ‘Clip(요약)’과 ‘Cat(도움을 주는 고양이)’ 의 합성어로, 단순히 번역 도구가 아닌 외국인 학부모의 언어 습득을 돕는 지능형 조력자를 의미합니다.</p>
<p>ClipCat은 기존 번역 중심 서비스와 달리, 이해되지 않는 문장을 드래그하면 AI 가 쉬운 한국어와 모국어 번역을 함께 제공하고, 이를 통해 학부모가 자신의 언어로 이해하며 동시에 한국어 표현을 학습할 수 있는 환경을 제공합니다. </p>
<p>물론, “그렇다면 그냥 모든 문장을 모국어로 번역해서 보면 되는 것 아니냐?” 라는 의문이 생길 수 있습니다. 하지만 저희 팀은 ‘즉각적인 편의’보다는 ‘지속적인 학습’을 원하는 학부모님들에게 초점을 맞추었습니다. 따라서 ClipCat은 단순히 번역 제공에 그치지 않고, 사용자가 한국어를 직접 읽고 이해해 나가며 자연스럽게 언어 습득 경험을 쌓을 수 있도록 설계하였습니다. </p>
<p>또한 저희는 실질적인 수요와 접근성을 고려하여 통계청 2022년 기준 다문화 가정 내 상위 5개의 언어(베트남어, 러시아어, 우즈베키스탄어, 캄보디아어, 몽골어)를 우선적으로 지원하였습니다. 이를 통해 서비스의 초기 효용성과 실제 사용성을 높이고 언어권별 사용자에게 실질적인 도움을 줄 수 있도록 설계하였습니다. </p>
<p>또한, 일부 학교에서 운영 중인 <strong>QR 번역 서비스는</strong> 학교 재량과 번역 인력의 한계로 인해 일관된 품질을 제공하지 못하는 문제가 있었습니다. 이에 ClipCatsms AI가 직접 문서를 <strong>분석·요약·번역</strong>하는 방식을 도입하여, 교사나 외부 번역가의 개입 없이도 정확한 결과를 즉시 제공함으로써 학교와 학부모 간의 정보 불균형을 최소화하였습니다. </p>
<h2 id="개발">개발</h2>
<p>이렇게 아이디어 구체화 이후, 본격적인 개발을 시작했습니다. </p>
<p>저는 프론트엔드 개발을 담당하였는데요.  Next.js와 Axios를 중심으로 백엔드와의 통신 구조를 구현하고,UI 구성과 상호작용 로직을 Figma 디자인을 기반으로 직접 퍼블리싱하였습니다. 특히 챗봇 인터페이스와 업로드 화면의 주요 기능을 설계하며 사용자가 직관적으로 이용할 수 있도록 컴포넌트를 구조화하였습니다.
<img src="https://velog.velcdn.com/images/bom_0320/post/98787725-4c67-481e-ad25-5c720f0e3794/image.png" alt="">
또한 웹페이지가 정적으로 보이지 않도록, ClipCat 캐릭터에 눈을 깜빡이는 애니메이션을 추가하여 자칫 단조로울 수 있는 화면에 생동감과 몰입감을 더하였습니다.  이 작은 움직임이 사용자에게 친근한 인상을 주어, AI 조력자라는 서비스의 정체성을 시각적으로 강화하는 효과를 주었다고 생각합니다. 특히 이러한 세심한 시각적 요소 덕분에,  “세부적인 완성도와 감각적인 연출이 돋보였다”는 평가를 받으며 디자인적 완성도 측면에서 높은 평가를 받을 수 있었습니다.</p>
<h2 id="협업-및-멘토-피드백">협업 및 멘토 피드백</h2>
<p>해커톤은 한정된 시간 속에서 기획, 개발, 디자인이 동시에 이뤄지기에 팀원간의 빠른 의사소통과 역할 분담이 무엇보다 중요했습니다. 저희 팀은 서로 다른 학교에서 처음 만났지만, 각자의 강점을 바탕으로 자연스럽게 협업 체계를 구축할 수 있었습니다.</p>
<p>개발이 예상보다 빠르게 진행되어 2일 차 오후에는 대부분의 핵심 기능이 완성되었고, 이후에는 멘토님들의 피드백을 바탕으로 프로젝트의 완성도와 방향성을 다듬는 데 집중하였습니다. 멘토님들이 각 팀을 돌아다니며 직접 조언을 해주셨는데, 저희 팀장이 직접 서비스를 소개하고 설명하는 동안, 저는 옆에서 멘토님들의 피드백을 빠짐없이 기록하며 즉시 반영할 부분을 정리했던 기억이 있습니다.
<img src="https://velog.velcdn.com/images/bom_0320/post/2c91a7e5-8fae-48a0-b271-b0f3c5d374b9/image.png" alt=""></p>
<p>모두가 공감할 수 있듯이, 그때는 단순히 좋은 조언이네라고 듣지만,  막상 실제로 적용해보다보면 잊거나 놓치기 쉬운 내용이 많습니다. 때문에 모든 피드백을 노션에 <strong>정리해 팀 전체가 바로 참고할 수 있도록 시각화</strong>했습니다.</p>
<p>이러한 태도 덕분에 저희 팀은 수정 속도가 빠르고 방향성 있는 개선이 가능했습니다. 특히 “기존 번역 앱과의 차별점을 명확히 하라”, “단순 번역이 아닌 AI 기반의 학습적 접근을 강조하라” 등의 멘토 피드백을 적극 반영하여 서비스의 핵심 가치를 더욱 구체화할 수 있었습니다.</p>
<p>이러한 과정은 단순히 기술적 보완을 넘어, 서비스의 철학과 차별점을 명확히 정립하는 전환점이 되었다고 생각하는데요. 짧은 시간이었지만, 서로의 전문성을 존중하고 멘토님의 피드백을 빠르게 실험하며 발전시킨 점이 결국 수상으로 이어질 수 있었던 저희 팀의 가장 큰 강점이었다고 생각합니다.</p>
<h2 id="결과-및-수상_1등">결과 및 수상_1등</h2>
<p>해커톤 마지막 날, 2박 3일간의 개발 결과물을 발표하는 시간이였습니다. 저희 팀 ClipCat은 단순한 번역을 넘어 “이해 가능한 언어로의 변환”과 “학습으로의 확장”이라는 새로운 접근 방식을 통해 사회적 가치와 기술적 완성도를 동시에 실현했다는 평가를 받았습니다. 그 결과, 심사위원들로부터 높은 호평을 얻을 수 있었는데요.</p>
<p>심사는 네이버클라우드, 니어네트웍스 등 5개 기업 관계자와 정보통신기획평가원(IITP) 심사위원단이 함께 진행하였으며, 대상 2팀, 최우수상 1팀, 우수상 5팀이 선정되었습니다.
<img src="https://velog.velcdn.com/images/bom_0320/post/b9cac143-ea3d-4705-a179-56d6983fe2ba/image.png" alt=""></p>
<p>중에서도 저희 팀 <strong>ClipCat은 과학기술정보통신부 장관상(대상)</strong> 을 수상하였습니다. 🎉</p>
<h2 id="마치며">마치며..</h2>
<p>이번 해커톤은 저에게 정말 의미있는 경험이였는데요. 이전에 1학년때는 운좋게 좋은 팀을 만나 수상을 하거나, 디자인 감각 덕분에 수상을 할수 있었던 경험이 있습니다. 하지만 이러한 경험들 속에서 언제나 마음 한켠에는 “진짜 내 실력으로 인정받고 싶다”는 갈증이 있었습니다. 그래서인지 그동안의 수상들은 기쁘면서도 어딘가 공허하게 느껴졌던 것 같습니다.</p>
<p>하지만 이번에는 달랐습니다. 기획 단계부터 직접 참여하고, 코드 한 줄 한 줄을 제 손으로 짜면서 결과물을 완성했기에 그 어느 때보다 큰 보람을 느꼈습니다. 짧은 시간 안에 오류를 해결하고, 구조를 다듬으며 완성도를 높여가는 과정 속에서 “내가 정말 개발자로 성장하고 있구나”라는 확신이 들었습니다. </p>
<p>무엇보다 이번 우승은 나 혼자만의 결과가 아닌, 각자의 강점을 살리며 함께 성장한 팀원들 덕분에 이룬 성과였다고 생각합니다. 서로 처음 만난 사이였지만, 기획, 디자인,개발 어느 한쪽도 소홀하지 않고 같은 목표를 향해 달려갔기에 이러한 결과를 얻을 수 있었다고 생각합니다. </p>
<p>이 경험을 통해 진짜 성장은 결과가 아니라 과정 속의 변화에서 비롯된다는 걸 배웠습니다. 이번 해커톤에선 그동안 제가 만들어왔던 모든 개발과 기획 방향을 다시 돌아보게 되는 계기가 되었으니깐요.☺️ </p>
<p>앞으로 이 갈증을 성장의 원동력으로 삼아, 더 깊이 있는 개발 역량을 쌓고, 함께 일하고 싶은 믿은직한 프론트엔드 개발자로 성장해 나가고 싶습니다. </p>
<p>긴 글 읽어주셔서 감사합니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[postcss에서 @tailwindcss/postcss를 사용하는 이유 (Tailwind v4 기준)]]></title>
            <link>https://velog.io/@bom_0320/postcss.config.mjs%EC%97%90%EC%84%9C-tailwindcsspostcss%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-Tailwind-v4-%EA%B8%B0%EC%A4%80</link>
            <guid>https://velog.io/@bom_0320/postcss.config.mjs%EC%97%90%EC%84%9C-tailwindcsspostcss%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-Tailwind-v4-%EA%B8%B0%EC%A4%80</guid>
            <pubDate>Thu, 12 Jun 2025 09:26:35 GMT</pubDate>
            <description><![CDATA[<h2 id="issue">Issue</h2>
<p><code>npx create-next-app</code>로 프로젝트를 만들면서 Tailwind CSS를 선택했더니, <code>postcss.config.mjs</code>가 자동으로 생성되었고, 그 안에는 다음과 같은 설정이 있었음</p>
<pre><code class="language-js">const config = {
  plugins: [&quot;@tailwindcss/postcss&quot;],
};

export default config;</code></pre>
<p>그런데 기존에 공식 문서나 블로그에서 봤던 예제들은 대부분 이랬다:</p>
<pre><code class="language-js">const config = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

export default config;</code></pre>
<p>내가 봤던 예제와, 자동으로 생성된 설정의 코드가 달라 혼란이 생겼고, 
챗 gpt 도 처음엔 @tailwindcss/postcss 방식이 잘못됐다라는 식으로 설명을 해서 더 헷갈리기 시작했다.</p>
<p>하지만 CNA에서 자동으로 생성되는 설정이 문제 있는 방식이라면 이미 이슈가 터졌을 테고,
공식에서 사용하는 템플릿이 틀릴 리가 없지 않은가?</p>
<p>그래서 더 챗gpt에 의존하지 않고 구글링을 더 해보기로 했다.</p>
<h2 id="최신-tailwind-v4-및-nextjs의-변화">최신 Tailwind v4 및 Next.js의 변화</h2>
<h3 id="tailwind-css-v41-이상-공식-문서에서-명시한-방식">Tailwind CSS v4.1 이상 공식 문서에서 명시한 방식:</h3>
<pre><code class="language-js">export default {
    plugins: {
        &quot;tailwindcss/postcss&quot;: {}.
    }
}
</code></pre>
<p>Tailwind CSs v4부터는 새로운 공식 PostCSS 플러그인인 <code>@tailwindcss/postcss</code>를 사용하도록 설정 방식이 바뀌었다.</p>
<ul>
<li>기존(v3): <code>tailwindcss</code>,<code>autoprefixer</code>를 직접 설정</li>
<li>변경(v4): <code>@tailwindcss/postcss</code> 하나로 통합 (내부적으로 처리)</li>
</ul>
<blockquote>
<p>이 플러그인은 Tailwind 설정, 변환, 최적화 과정을 모두 담당한다.</p>
</blockquote>
<h2 id="nextjs의-최신-템플릿">Next.js의 최신 템플릿</h2>
<p>Next.js v15.2 이상에서 Tailwind를 포함해 생성한 프로젝트는 다음 설정을 자동으로 생성한다.</p>
<pre><code class="language-bash">npx create-next-app my-app --tailwind</code></pre>
<ul>
<li><code>postcss.config.mjs</code> 파일 생성</li>
<li>내부에는 <code>&quot;@tailwindcss/postcss&quot;</code> 플러그인이 문자열로 등록됨</li>
<li>이는 Tailwind v4.1 기준의 공식 설정이다</li>
</ul>
<p>하지만 Tailwind CSS 공식 문서에는 여전히 다음과 같은 예제가 많이 등장한다:</p>
<pre><code class="language-js">plugins: {
  tailwindcss: {},
  autoprefixer: {},
}</code></pre>
<p>하지만 이건 <strong>Tailwind CSS v3 이하</strong> 의 설정 방식이며, v4부터는 더 이상 권장되지 않는다.</p>
<h2 id="요약-비교">요약 비교</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>Tailwind v3 이하</th>
<th>Tailwind v4.0</th>
<th>Tailwind v4.1 이상</th>
</tr>
</thead>
<tbody><tr>
<td>PostCSS 설정 방식</td>
<td><code>tailwindcss: {}</code></td>
<td>가능하지만 권장 안 함</td>
<td>✅ <code>@tailwindcss/postcss</code></td>
</tr>
<tr>
<td>설정 방식</td>
<td>직접 명시</td>
<td>중간 과도기</td>
<td>공식 통합 방식</td>
</tr>
<tr>
<td>Next.js 템플릿</td>
<td>수동 설정</td>
<td>일부 혼재</td>
<td>자동으로 <code>@tailwindcss/postcss</code> 생성됨</td>
</tr>
</tbody></table>
<h2 id="내가-얻은-인사이트">내가 얻은 인사이트</h2>
<ul>
<li>Tailwind CSS는 <strong>v4.1부터 설정 구조가 확실히 바뀌었고</strong>, 이에 따라 문서도 공식적으로 업데이트되었다.</li>
<li>공식 문서만이 절대적인 기준이다. 오래된 블로그, GPT 답변, 커뮤니티 포스트는 참고하되 반드시 <strong>버전 기준 확인이 필수</strong>다.</li>
<li><code>create-next-app</code>으로 Tailwind를 선택한 경우, <code>@tailwindcss/postcss</code>가 자동으로 설정되는 건 <strong>정확하고 공식적인 동작</strong>이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[모바일 vs 데스크탑에서의 Navigation 구현 방식 정리]]></title>
            <link>https://velog.io/@bom_0320/%EB%AA%A8%EB%B0%94%EC%9D%BC-vs-%EB%8D%B0%EC%8A%A4%ED%81%AC%ED%83%91%EC%97%90%EC%84%9C%EC%9D%98-Navigation-%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@bom_0320/%EB%AA%A8%EB%B0%94%EC%9D%BC-vs-%EB%8D%B0%EC%8A%A4%ED%81%AC%ED%83%91%EC%97%90%EC%84%9C%EC%9D%98-Navigation-%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 11 Jun 2025 08:18:17 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bom_0320/post/812d431d-2183-4979-a96d-05719868f470/image.png" alt=""></p>
<h2 id="개요">개요</h2>
<p>React + Tailwind 기반의 <code>main-nav.tsx</code> 컴포넌트에서, 모바일과 데스크탑은 서로 다른 방식으로 Navigation UI 를 구현하고 있다. 
그리고 그 차이와 이유를 명확이 이해하고자 다음과 같은 포스터를 작성하였다.</p>
<h2 id="💻-데스크탑-nav-구현-방식">💻 데스크탑 nav 구현 방식</h2>
<h3 id="사용-구조">사용 구조</h3>
<pre><code class="language-tsx">{items?.length ? (
  &lt;nav className=&quot;hidden gap-6 md:flex&quot;&gt;
    {items.map((item) =&gt; (
      &lt;Link href={item.href}&gt;...&lt;/Link&gt;
    ))}
  &lt;/nav&gt;
) : null}</code></pre>
<h3 id="해석">해석</h3>
<ul>
<li><code>md:flex</code> 는 데스크탑(medium ≥ 768px) 이상일 때만 메뉴 보이게 함</li>
<li><code>hidden</code> : 기본은 숨김 (모바일에서는 안 보이도록)</li>
<li>메뉴 항목은 <code>&lt;Link&gt;</code> 컴포넌트로 직접 나열</li>
<li>별도 state 관리 없이 항상 보이는 수평 메뉴</li>
</ul>
<hr>
<h2 id="📱-모바일-nav-구현-방식">📱 모바일 nav 구현 방식</h2>
<h3 id="사용-구조-1">사용 구조</h3>
<pre><code class="language-tsx">&lt;button className=&quot;md:hidden&quot; onClick={...}&gt;
  {showMobileMenu ? &lt;Icons.close /&gt; : &lt;Icons.logo /&gt;}
  &lt;span&gt;Menu&lt;/span&gt;
&lt;/button&gt;

{showMobileMenu &amp;&amp; items &amp;&amp; (
  &lt;MobileNav items={items}&gt;{children}&lt;/MobileNav&gt;
)}</code></pre>
<h3 id="해석-1">해석</h3>
<ul>
<li><code>md: hidden</code> : 데스크탑에서는 버튼 안 보이고, 모바일에서만 보임</li>
<li>햄버거 버튼 클릭 시 <code>showMobileMenu</code> 상태를 토글</li>
<li><code>showMobileMenu === true</code> 일 때만 <code>&lt;MobileNav&gt;</code> 렌더링</li>
<li><code>&lt;MobileNav /&gt;</code> 는 전체 화면을 덮는 오버레이 메뉴</li>
</ul>
<h2 id="📊-비교-요약">📊 비교 요약</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>💻 데스크탑</th>
<th>📱 모바일</th>
</tr>
</thead>
<tbody><tr>
<td>렌더링 방식</td>
<td><code>&lt;nav&gt;</code> + <code>&lt;Link&gt;</code></td>
<td><code>&lt;button&gt;</code> + <code>&lt;MobileNav&gt;</code></td>
</tr>
<tr>
<td>상태 관리</td>
<td>❌ 없음 (항상 보임)</td>
<td>✅ 있음 (<code>useState</code>)</td>
</tr>
<tr>
<td>표시 조건</td>
<td><code>md:flex</code> 이상</td>
<td><code>md:hidden</code> 이하</td>
</tr>
<tr>
<td>UI 형태</td>
<td>수평 메뉴바</td>
<td>오버레이(전체화면) 메뉴</td>
</tr>
<tr>
<td>사용자 조작</td>
<td>별도 조작 없음</td>
<td>햄버거 버튼 클릭 필요</td>
</tr>
</tbody></table>
<hr>
<h2 id="🎯-결론">🎯 결론</h2>
<blockquote>
<p>반응형 디자인에서 Navigation은 단순히 숨기고 보이는 걸 넘어서,</p>
<p><strong>기기 환경에 맞는 UX 구조 자체를 바꿔야 한다.</strong></p>
<p>데스크탑은 공간이 넓으므로 메뉴를 펼쳐두고,</p>
<p>모바일은 공간이 좁기 때문에 <strong>토글 기반의 오버레이 메뉴</strong>로 구현하는 것이 표준 패턴이다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL - Tailwind의 flex, hidden, md:flex 이해 & display의 본질]]></title>
            <link>https://velog.io/@bom_0320/TIL-Tailwind%EC%9D%98-flex-hidden-mdflex-%EC%9D%B4%ED%95%B4-display%EC%9D%98-%EB%B3%B8%EC%A7%88</link>
            <guid>https://velog.io/@bom_0320/TIL-Tailwind%EC%9D%98-flex-hidden-mdflex-%EC%9D%B4%ED%95%B4-display%EC%9D%98-%EB%B3%B8%EC%A7%88</guid>
            <pubDate>Wed, 11 Jun 2025 07:28:11 GMT</pubDate>
            <description><![CDATA[<h2 id="오늘-헷갈렸던-부분">오늘 헷갈렸던 부분</h2>
<p>처음엔 <code>flex</code> 를 단순히 가로 정렬하는 속성이라고 이해했었고, <code>md: flex</code>는 그저 반응형 정렬용이라고 생각음
그래서 nav 메뉴를 보여주기 위해 굳이 <code>md: flex</code>가 필요한 이유가 잘 이해되지 않았음</p>
<h2 id="핵심-정리">핵심 정리</h2>
<h3 id="1-display가-본질">1. <code>display</code>가 본질</h3>
<ul>
<li>HTML 요소가 <strong>보이냐 / 안 보이냐</strong>, <strong>어떻게 배치되느냐</strong>를 결정하는 핵심 속성이 <code>display</code></li>
<li><code>display: none</code> → 요소 자체가 화면에서 사라짐 (보이지 않음 + 공간도 차지하지 않음)</li>
<li><code>display: flex</code> → 요소가 보이고, 자식 요소들이 flexbox 방식으로 배치됨</li>
</ul>
<h3 id="2-flex는-정렬-방법이지-보이게-만드는-속성은-아님">2. <code>flex</code>는 &quot;정렬 방법&quot;이지, &quot;보이게 만드는 속성&quot;은 아님</h3>
<ul>
<li><code>flex</code>는 실제로는 <code>display: flex</code>를 의미하고,</li>
<li>자식 요소들을 <strong>가로 or 세로 방향</strong>으로 정렬하는 도구일 뿐임</li>
<li>단, 기본값이 <code>flex-direction: row</code>라서 보통 가로 정렬로 보임 → 그래서 흔히들 &quot;flex = 가로 정렬&quot;로 착각함</li>
</ul>
<h3 id="3-tailwind에서의-hidden-mdflex는-결국-display-제어">3. Tailwind에서의 <code>hidden</code>, <code>md:flex</code>는 결국 <code>display</code> 제어</h3>
<table>
<thead>
<tr>
<th>Tailwind 클래스</th>
<th>실제 CSS 효과</th>
</tr>
</thead>
<tbody><tr>
<td><code>hidden</code></td>
<td><code>display: none</code></td>
</tr>
<tr>
<td><code>flex</code></td>
<td><code>display: flex</code></td>
</tr>
<tr>
<td><code>md:flex</code></td>
<td>768px 이상에서 <code>display: flex</code></td>
</tr>
<tr>
<td><code>md:hidden</code></td>
<td>768px 이상에서 <code>display: none</code></td>
</tr>
</tbody></table>
<p>→ 즉, Tailwind는 <code>display</code> 속성을 <strong>조건부로 적용하는 래퍼일 뿐</strong>이다.</p>
<hr>
<h3 id="실전-적용-nav-메뉴-예시">실전 적용: <code>nav</code> 메뉴 예시</h3>
<pre><code class="language-tsx">&lt;nav className=&quot;hidden md:flex&quot;&gt;...&lt;/nav&gt;</code></pre>
<ul>
<li><p>모바일에서는 <code>display: none</code> → 메뉴가 숨겨짐</p>
</li>
<li><p>데스크탑에서는 <code>display: flex</code> → 메뉴가 보이고, 자식들이 가로로 정렬됨</p>
</li>
<li><p>메뉴의 <strong>보임 여부 자체가 flex 때문에 결정된 것처럼 보이지만</strong>,</p>
<p>  실제 핵심은 <code>display: none → display: flex</code>라는 <strong>display 상태의 전환</strong>이었다.</p>
</li>
</ul>
<hr>
<h3 id="오늘의-교훈">오늘의 교훈</h3>
<blockquote>
<p>Tailwind의 hidden, flex, md:flex 등은 전부 결국 display 속성을 조작하는 방식임</p>
<p>요소가 보이느냐 안 보이느냐는 <code>display</code>가 결정하며, <code>flex</code>는 정렬 도구일 뿐임</p>
<p>반응형 조건부 렌더링도 <code>display</code>의 on/off 개념으로 이해해보자</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL - Tailwind CSS v4에서 npx tailwindcss init -p 명령어가 작동하지 않는 이유와 해결 과정]]></title>
            <link>https://velog.io/@bom_0320/TIL-Tailwind-CSS-v4%EC%97%90%EC%84%9C-npx-tailwindcss-init-p-%EB%AA%85%EB%A0%B9%EC%96%B4%EA%B0%80-%EC%9E%91%EB%8F%99%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%9C%A0%EC%99%80-%ED%95%B4%EA%B2%B0-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@bom_0320/TIL-Tailwind-CSS-v4%EC%97%90%EC%84%9C-npx-tailwindcss-init-p-%EB%AA%85%EB%A0%B9%EC%96%B4%EA%B0%80-%EC%9E%91%EB%8F%99%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%9C%A0%EC%99%80-%ED%95%B4%EA%B2%B0-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Mon, 09 Jun 2025 10:58:26 GMT</pubDate>
            <description><![CDATA[<h3 id="🐛-문제-상황">🐛 문제 상황</h3>
<p>Tailwind를 CLI로 설치할 때 다음 명령어를 입력했다:</p>
<pre><code class="language-bash">
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
</code></pre>
<p>그런데 <code>npx tailwindcss init -p</code> 명령어에서 다음과 같은 에러가 발생함:</p>
<pre><code>
npm ERR! could not determine executable to run
</code></pre><p>또한, <code>node_modules/.bin/tailwindcss</code>가 존재하지 않음.</p>
<hr>
<h3 id="📌-원인-분석">📌 원인 분석</h3>
<p>이 문제는 <strong>Tailwind CSS v4부터 공식적으로 CLI 기반 init 명령어를 제거했기 때문</strong>이다.</p>
<h3 id="🚨-tailwind-css-v3-vs-v4-차이">🚨 Tailwind CSS v3 vs v4 차이</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>Tailwind v3</th>
<th>Tailwind v4</th>
</tr>
</thead>
<tbody><tr>
<td><code>npx tailwindcss init</code></td>
<td>✅ 지원됨 (자동으로 설정 파일 생성)</td>
<td>❌ 제거됨 (수동 작성 필요)</td>
</tr>
<tr>
<td>설정 파일 생성</td>
<td>CLI로 자동 생성</td>
<td>사용자가 직접 파일 생성</td>
</tr>
<tr>
<td><code>autoprefixer</code> 필요</td>
<td>필요함</td>
<td>❌ 내장된 Lightning CSS가 처리함</td>
</tr>
<tr>
<td>content 설정 방식</td>
<td>직접 명시해야 함 (<code>tailwind.config.js</code>)</td>
<td>자동 감지 또는 명시 (선택 사항)</td>
</tr>
</tbody></table>
<hr>
<h3 id="🔧-해결-방법-tailwind-css-v4-기준">🔧 해결 방법 (Tailwind CSS v4 기준)</h3>
<h3 id="1-필요한-패키지-수동-설치">1. 필요한 패키지 수동 설치</h3>
<pre><code class="language-bash">
npm install -D tailwindcss postcss autoprefixer
</code></pre>
<blockquote>
<p>참고: 사실 v4에서는 autoprefixer, postcss는 없어도 동작은 가능. Vite처럼 특정 빌드 툴을 쓰는 경우엔 더 단순화됨.</p>
</blockquote>
<h3 id="2-설정-파일을-직접-작성">2. 설정 파일을 <strong>직접 작성</strong></h3>
<ul>
<li><code>tailwind.config.js</code> 예시:</li>
</ul>
<pre><code class="language-jsx">
/** @type {import(&#39;tailwindcss&#39;).Config} */
module.exports = {
  content: [
    &quot;./src/**/*.{html,js,ts,jsx,tsx}&quot;,
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
</code></pre>
<ul>
<li><code>postcss.config.js</code> 예시:</li>
</ul>
<pre><code class="language-jsx">
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}
</code></pre>
<h3 id="3-글로벌-css-파일-설정-globalscss-등">3. 글로벌 CSS 파일 설정 (<code>globals.css</code> 등)</h3>
<pre><code class="language-css">
@tailwind base;
@tailwind components;
@tailwind utilities;
</code></pre>
<hr>
<h3 id="💡-결론-및-배운-점">💡 결론 및 배운 점</h3>
<ul>
<li>Tailwind v4부터는 더 이상 <code>npx tailwindcss init</code> 명령어를 사용할 수 없다.</li>
<li>대신 <strong>설정 파일을 직접 작성하는 방식으로 변경</strong>되었으며, 이는 불편해 보이지만 더 유연한 커스터마이징을 위한 구조이다.</li>
<li>문서와 커뮤니티 블로그에서 보던 예전 설치법을 그대로 따라하면 위와 같은 오류가 생기므로, <strong>버전에 맞는 공식 문서를 참고하는 습관이 중요하다.</strong></li>
</ul>
<h3 id="💬-참고-링크">💬 참고 링크</h3>
<p><a href="https://tailwindcss.com/docs/installation/framework-guides/nextjs">https://tailwindcss.com/docs/installation/framework-guides/nextjs</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: localStorage와 JSON.stringify vs. JSON.parse ]]></title>
            <link>https://velog.io/@bom_0320/TIL-localStorage%EC%99%80-JSON.stringify-vs.-JSON.parse</link>
            <guid>https://velog.io/@bom_0320/TIL-localStorage%EC%99%80-JSON.stringify-vs.-JSON.parse</guid>
            <pubDate>Tue, 27 May 2025 02:22:41 GMT</pubDate>
            <description><![CDATA[<p>브라우저 저장소인 localStorage의 개념과 JSON 변환 처리의 필요성 이해하기 위해서 다음과 같은 포스터를 작성했다.</p>
<h2 id="localstorage-란">localStorage 란?</h2>
<p><strong>localStorage</strong> 는 <strong>브라우저가 제공하는 클라이언트 측 저장소</strong>이다.</p>
<p>HTML5에서 도입된 <strong>Web Storage AP</strong>I 중 하나이며, key-value 형태의 데이터를 <strong>브라우저에 영구적으로 저장</strong>할 수 있다.</p>
<h3 id="💡-주요-특징">💡 주요 특징</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>지속성</td>
<td>브라우저를 꺼도, PC를 재부팅해도 데이터가 남아있음</td>
</tr>
<tr>
<td>저장 용량</td>
<td>보통 도메인당 약 5MB ~ 10MB 저장 가능 (쿠키보다 훨씬 큼)</td>
</tr>
<tr>
<td>접근 범위</td>
<td>같은 도메인에서만 접근 가능 (보안 보호)</td>
</tr>
<tr>
<td>저장 형식</td>
<td>반드시 <strong>문자열(string)</strong>만 저장 가능</td>
</tr>
<tr>
<td>사용 예시</td>
<td>자동 로그인 유지, 글 임시 저장, 테마 설정 등</td>
</tr>
</tbody></table>
<hr>
<h3 id="사용-예시">사용 예시</h3>
<pre><code class="language-tsx">// 저장
localStorage.setItem(&#39;nickname&#39;, &#39;김봄&#39;);

// 불러오기
const name = localStorage.getItem(&#39;nickname&#39;);

// 삭제
localStorage.removeItem(&#39;nickname&#39;)l

// 전체 삭제
localStorage.clear();</code></pre>
<h2 id="❓-그런데-객체배열은-왜-안-되는가">❓ 그런데 객체/배열은 왜 안 되는가?</h2>
<p>localStorage는 문자열만 저장 가능하므로, </p>
<p><strong>자바스크립트의 객체나 배열을 바로 저장할 수 없다.</strong></p>
<p>이럴 때 사용하는 것이 바로:</p>
<ul>
<li><code>JSON.stringify()</code>  : 객체 → 문자열</li>
<li><code>JSON.parse()</code>: 문자열 → 객체</li>
</ul>
<hr>
<h2 id="🔄-jsonstringify--jsonparse-흐름">🔄 JSON.stringify / JSON.parse 흐름</h2>
<h3 id="🔷-저장-시-객체-→-문자열">🔷 저장 시 (객체 → 문자열)</h3>
<pre><code class="language-tsx">const post = { title: &quot;안녕&quot;, content: &quot;첫 글이얌&quot; };

localStorage.setItem(&#39;posts&#39;, JSON.stringify([post]));
</code></pre>
<ul>
<li><code>[post]</code> 는 자바스크립트 배열</li>
<li><code>JSON.stringify()</code> 로 변환 시, 아래처럼 JSON 문자열로 바뀜:</li>
</ul>
<pre><code class="language-json">&quot;[{\&quot;title\&quot;:\&quot;안녕\&quot;,\&quot;content\&quot;:\&quot;첫 글이야\&quot;}]&quot;
</code></pre>
<hr>
<h3 id="🔶-꺼낼-때-문자열-→-객체">🔶 꺼낼 때 (문자열 → 객체)</h3>
<pre><code class="language-tsx">const saved = localStorage.getItem(&#39;posts&#39;);

if (saved) {
  const parsed = JSON.parse(saved);
  console.log(parsed[0].title);  // &quot;안녕&quot;
}</code></pre>
<ul>
<li><code>JSON.parse()</code> 는 문자열을 다시 자바스크립트 객체 배열로 복원</li>
<li>이후 배열처럼 다루기 가능</li>
</ul>
<hr>
<h2 id="📦-전체-흐름-정리">📦 전체 흐름 정리</h2>
<pre><code class="language-jsx">[ JS 객체/배열 ]
    ↓ JSON.stringify
&quot;문자열&quot; (localStorage에 저장됨)
    ↑ JSON.parse
[ JS 객체/배열 복원 ]
</code></pre>
<hr>
<h2 id="🔥-핵심-요약">🔥 핵심 요약</h2>
<table>
<thead>
<tr>
<th>질문</th>
<th>답변</th>
</tr>
</thead>
<tbody><tr>
<td>localStorage에 왜 stringify가 필요해?</td>
<td>문자열만 저장 가능하기 때문</td>
</tr>
<tr>
<td>왜 다시 parse 해야 해?</td>
<td>문자열로는 <code>.title</code> 등 프로퍼티 접근이 안 되기 때문</td>
</tr>
<tr>
<td>변환 형태는 어떻게 생김?</td>
<td>객체가 <code>&quot;{...}&quot;</code> 형태의 JSON 문자열로 바뀜</td>
</tr>
<tr>
<td>언제 써야 해?</td>
<td>localStorage, sessionStorage, 서버 통신 시 필수</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-실전에서-적용한-코드-예시-react-게시판-프로젝트">✅ 실전에서 적용한 코드 예시 (React 게시판 프로젝트)</h2>
<pre><code class="language-tsx">
// 저장
useEffect(() =&gt; {
  localStorage.setItem(&#39;posts&#39;, JSON.stringify(posts));
}, [posts]);

// 불러오기
useEffect(() =&gt; {
  const saved = localStorage.getItem(&#39;posts&#39;);
  if (saved) {
    setPosts(JSON.parse(saved));
  }
}, []);
</code></pre>
<blockquote>
<p>posts 상태가 바뀌면 자동으로 localStorage에 저장</p>
<p>페이지가 새로고침되면 localStorage에서 다시 불러와 복구됨</p>
</blockquote>
<hr>
<h2 id="💬-느낀점">💬 느낀점</h2>
<p>이번 학습을 통해 단순히 데이터를 저장하는 것만이 아니라,</p>
<p><strong>자바스크립트 객체와 문자열 간의 변환 로직이 왜 필요한지</strong>,</p>
<p>그리고 그 과정에서 어떤 데이터 형상이 오가는지 구체적으로 이해하게 되었다.</p>
<p>프론트엔드 개발에서 localStorage를 활용할 때는 무조건 <code>JSON.stringify</code> / <code>JSON.parse</code>가 함께 사용된다는 점을 명확히 인지하게 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Drag & Drop에서 style을 다루는 방식 차이 (Draggable vs Droppable)]]></title>
            <link>https://velog.io/@bom_0320/React-Drag-Drop%EC%97%90%EC%84%9C-style%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EC%8B%9D-%EC%B0%A8%EC%9D%B4-Draggable-vs-Droppable</link>
            <guid>https://velog.io/@bom_0320/React-Drag-Drop%EC%97%90%EC%84%9C-style%EC%9D%84-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EC%8B%9D-%EC%B0%A8%EC%9D%B4-Draggable-vs-Droppable</guid>
            <pubDate>Mon, 26 May 2025 05:15:29 GMT</pubDate>
            <description><![CDATA[<h2 id="🤔-문제-상황">🤔 문제 상황</h2>
<p>드래그 앤 드롭 리스트를 만들다가 궁금한 점이 생김:</p>
<ul>
<li><code>ul</code> 태그 (<code>Droppable</code>)에는 그냥 <code>style={{ padding: 20 }}</code> 이렇게 간단하게 스타일을 줘도 되는데,</li>
<li><code>li</code> 태그 (<code>Draggable</code>)에는 <code>...provided.draggableProps.style</code>처럼 <strong>복사해서 스타일을 덧붙여야</strong> 작동함</li>
</ul>
<p>왜 그런 차이가 생기는 걸까?</p>
<hr>
<h2 id="✅-핵심-개념-요약">✅ 핵심 개념 요약</h2>
<h3 id="🔹-draggable에는-라이브러리-내부에서-계산한-style이-있다">🔹 Draggable에는 라이브러리 내부에서 계산한 style이 있다!</h3>
<pre><code class="language-tsx">style={{
  ...provided.draggableProps.style,
  padding: &#39;8px&#39;,
}}
</code></pre>
<ul>
<li><code>provided.draggableProps.style</code>에는 드래그 중 <strong>위치 이동, 애니메이션, 트랜지션 등</strong>을 위한 내부 계산된 스타일이 담겨 있음</li>
<li>이 스타일을 유지하지 않으면 드래그 기능이 깨짐!</li>
<li>그래서 <strong>기존 스타일을 복사(...)하고, 내가 원하는 스타일을 덧붙여야 함</strong></li>
</ul>
<hr>
<h3 id="🔹-droppable에는-별도로-제공되는-style이-없다">🔹 Droppable에는 별도로 제공되는 style이 없다</h3>
<pre><code class="language-tsx">&lt;ul style={{ padding: 20 }}&gt;
</code></pre>
<ul>
<li><code>provided.droppableProps</code>는 드롭 이벤트를 위한 속성만 제공 (<code>data-*</code>, role 등)</li>
<li>별도로 계산된 style은 없음 → 내가 자유롭게 스타일 지정해도 기능에 영향 없음</li>
</ul>
<hr>
<h3 id="🔹-jsx에서-style-중괄호가-두-번-쓰이는-이유">🔹 JSX에서 <code>style={{}}</code> 중괄호가 두 번 쓰이는 이유</h3>
<ul>
<li>JSX에서는 JavaScript 표현식은 <code>{}</code>로 감싸야 하고,</li>
<li><code>style</code>은 JS 객체니까 <code>{{ ... }}</code> 형태가 됨</li>
</ul>
<p>📌 예:</p>
<pre><code class="language-tsx">&lt;div style={{ color: &#39;red&#39;, margin: &#39;10px&#39; }} /&gt;
</code></pre>
<hr>
<h2 id="📊-정리-표">📊 정리 표</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>Draggable</code>의 style</td>
<td>드래그 동작을 위해 내부 style 계산 포함됨 → 반드시 복사해서 사용</td>
<td><code>...provided.draggableProps.style</code></td>
</tr>
<tr>
<td><code>Droppable</code>의 style</td>
<td>라이브러리가 style을 계산해서 넘기지 않음 → 내가 직접 지정</td>
<td><code>style={{ padding: 20 }}</code></td>
</tr>
<tr>
<td><code>{{}}</code> 문법</td>
<td>JSX + JS 객체 표현식 조합</td>
<td><code>style={{ color: &#39;red&#39; }}</code></td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-한-줄-요약">✅ 한 줄 요약</h2>
<blockquote>
<p>Draggable은 라이브러리 내부 style을 유지해야 하므로 ...style로 복사해서 써야 하고,Droppable은 자유롭게 스타일을 지정해도 된다.
JSX의 {{}}는 JS 객체를 넘기기 위한 문법상 구조다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[@hello-pangea/dnd - provided 객체 정리 (Droppable vs Draggable)]]></title>
            <link>https://velog.io/@bom_0320/hello-pangeadnd-provided-%EA%B0%9D%EC%B2%B4-%EC%A0%95%EB%A6%AC-Droppable-vs-Draggable</link>
            <guid>https://velog.io/@bom_0320/hello-pangeadnd-provided-%EA%B0%9D%EC%B2%B4-%EC%A0%95%EB%A6%AC-Droppable-vs-Draggable</guid>
            <pubDate>Tue, 20 May 2025 09:03:54 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-provided란">✅ <code>provided</code>란?</h2>
<blockquote>
<p>@hello-pangea/dnd 라이브러리에서</p>
<p><code>Droppable</code>, <code>Draggable</code> 컴포넌트가 드래그 기능을 동작시키기 위해</p>
<p>자식 함수(children)에 넘겨주는 <strong>필수 도구 세트 객체</strong></p>
</blockquote>
<hr>
<h2 id="🧭-왜-존재하는가">🧭 왜 존재하는가?</h2>
<p>React에선 DOM 요소를 직접 조작하지 않기 때문에,</p>
<p>라이브러리가 드래그 기능을 위해 꼭 필요한 값들 (<code>ref</code>, props 등)을</p>
<p>우리가 <strong>직접 붙일 수 있도록 제공해주는 방식</strong>이 바로 <code>provided</code>.</p>
<hr>
<h2 id="✅-droppable의-provided">✅ Droppable의 <code>provided</code></h2>
<pre><code class="language-tsx">&lt;Droppable droppableId=&quot;myList&quot;&gt;
  {(provided) =&gt; (
    &lt;ul
      ref={provided.innerRef}
      {...provided.droppableProps}
    &gt;
      {items.map(...)}
      {provided.placeholder}
    &lt;/ul&gt;
  )}
&lt;/Droppable&gt;
</code></pre>
<h3 id="🔹-droppableprovided-구조">🔹 DroppableProvided 구조</h3>
<table>
<thead>
<tr>
<th>속성</th>
<th>설명</th>
<th>필수 여부</th>
</tr>
</thead>
<tbody><tr>
<td><code>innerRef</code></td>
<td>드롭 가능한 DOM 요소에 ref 연결</td>
<td>✅ 필수</td>
</tr>
<tr>
<td><code>droppableProps</code></td>
<td>드롭 기능을 위한 속성(data-*)들</td>
<td>✅ 필수</td>
</tr>
<tr>
<td><code>placeholder</code></td>
<td>드래그 중 비는 공간을 채우는 가짜 요소</td>
<td>✅ 필수</td>
</tr>
</tbody></table>
<h3 id="🔸-중요-포인트">🔸 중요 포인트</h3>
<ul>
<li><p><code>provided.placeholder</code>는 반드시 <code>&lt;ul&gt;</code> 안에 있어야 한다.</p>
<p>  → <code>&lt;ul&gt;</code> 바깥에 두면 React가 HTML 구조 에러로 판단함!</p>
</li>
</ul>
<hr>
<h2 id="✅-draggable의-provided">✅ Draggable의 <code>provided</code></h2>
<pre><code class="language-tsx">![](https://velog.velcdn.com/images/bom_0320/post/82e0c9f6-2702-4b21-9816-c970916259fc/image.png)
&lt;Draggable draggableId={item} index={index}&gt;
  {(provided) =&gt; (
    &lt;li
      ref={provided.innerRef}
      {...provided.draggableProps}
      {...provided.dragHandleProps}
      style={{
        ...provided.draggableProps.style,
        ...
      }}
    &gt;
      {item}
    &lt;/li&gt;
  )}
&lt;/Draggable&gt;
</code></pre>
<h3 id="🔹-draggableprovided-구조">🔹 DraggableProvided 구조</h3>
<table>
<thead>
<tr>
<th>속성</th>
<th>설명</th>
<th>필수 여부</th>
</tr>
</thead>
<tbody><tr>
<td><code>innerRef</code></td>
<td>드래그 가능한 요소에 ref 연결</td>
<td>✅ 필수</td>
</tr>
<tr>
<td><code>draggableProps</code></td>
<td>위치 계산, 드래그 이동 등 기본 props</td>
<td>✅ 필수</td>
</tr>
<tr>
<td><code>dragHandleProps</code></td>
<td>사용자가 마우스로 잡는 “핸들” 역할</td>
<td>✅ 필수</td>
</tr>
</tbody></table>
<hr>
<h2 id="🧠-droppable-vs-draggable-비교">🧠 Droppable vs Draggable 비교</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>Droppable</th>
<th>Draggable</th>
</tr>
</thead>
<tbody><tr>
<td>역할</td>
<td>드롭 가능한 <strong>영역</strong></td>
<td>드래그 가능한 <strong>요소</strong></td>
</tr>
<tr>
<td>내부 속성</td>
<td><code>innerRef</code>, <code>droppableProps</code>, <code>placeholder</code></td>
<td><code>innerRef</code>, <code>draggableProps</code>, <code>dragHandleProps</code></td>
</tr>
<tr>
<td>ref 연결 대상</td>
<td><code>&lt;ul&gt;</code>, <code>&lt;div&gt;</code> 등 드롭 영역</td>
<td><code>&lt;li&gt;</code>, 카드, 요소 등 드래그 대상</td>
</tr>
<tr>
<td>공통점</td>
<td>모두 <strong>React에서 DOM을 직접 연결해야 하기 때문에 ref와 props가 필요</strong></td>
<td></td>
</tr>
</tbody></table>
<hr>
<h2 id="💡-헷갈렸던-점--배운-것">💡 헷갈렸던 점 &amp; 배운 것</h2>
<table>
<thead>
<tr>
<th>질문</th>
<th>정리된 개념</th>
</tr>
</thead>
<tbody><tr>
<td><code>provided</code>는 Droppable이랑 Draggable이 공통으로 쓰는 거야?</td>
<td>이름은 같지만 <strong>완전히 다른 타입과 역할을 가짐</strong></td>
</tr>
<tr>
<td><code>ref</code>, <code>props</code>를 왜 붙여야 해?</td>
<td>드래그 위치 계산과 이벤트 처리를 위해 <strong>실제 DOM에 연결해야 하기 때문</strong></td>
</tr>
<tr>
<td><code>provided.placeholder</code>는 꼭 <code>&lt;ul&gt;</code> 안에 있어야 해?</td>
<td>✅ 그래야 HTML 구조가 맞고 드래그 시 레이아웃도 깨지지 않음</td>
</tr>
<tr>
<td>JSX 안에서 왜 <code>{(provided) =&gt; (...)}</code>처럼 함수로 써야 해?</td>
<td>Droppable이 값을 넘겨주기 위해 <strong>children을 함수로 받기 때문 (함수형 children)</strong></td>
</tr>
</tbody></table>
<hr>
<h2 id="🎯-한-줄-요약">🎯 한 줄 요약</h2>
<blockquote>
<p>provided는 드래그 기능을 DOM에 정확히 연결하기 위해 라이브러리가 넘겨주는 “도구 세트”이고,
이걸 제대로 붙여야만 드래그 앤 드롭이 정상 작동한다.
Droppable과 Draggable은 각각 전혀 다른 provided 구조를 가진다!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[ref 가 뭐길래 DND에 꼭 필요할까?]]></title>
            <link>https://velog.io/@bom_0320/ref-%EA%B0%80-%EB%AD%90%EA%B8%B8%EB%9E%98-DND%EC%97%90-%EA%BC%AD-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@bom_0320/ref-%EA%B0%80-%EB%AD%90%EA%B8%B8%EB%9E%98-DND%EC%97%90-%EA%BC%AD-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Tue, 20 May 2025 08:07:02 GMT</pubDate>
            <description><![CDATA[<h2 id="ref란">ref란?</h2>
<blockquote>
<p>ref는 React가 HTML 요소(실제 DOM)를 직접 찾을 수 있게 해주는 도구다.</p>
</blockquote>
<hr>
<h2 id="🧭-정리">🧭 정리</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>평소 코드</td>
<td>JSX → 가상 DOM → 실제 DOM으로 자동 반영됨</td>
</tr>
<tr>
<td>ref 사용 시</td>
<td>React가 아닌 <strong>개발자가 직접 실제 DOM을 조작</strong>함</td>
</tr>
<tr>
<td>언제 ref를 씀?</td>
<td>드래그, 포커스, 스크롤, 사이즈 측정 등 <strong>가상 DOM으론 할 수 없는 작업</strong>을 할 때 사용</td>
</tr>
</tbody></table>
<hr>
<h3 id="⭐️-ref는-왜-필요한가">⭐️ ref는 왜 필요한가?</h3>
<blockquote>
<p>평소엔 React가 가상 DOM을 관리하지만,</p>
<p>정말 필요한 경우, <strong>내가 직접 진짜 DOM을 만질 수 있게 해주는 “예외 통로”</strong>가 바로 <code>ref</code>다.</p>
</blockquote>
<hr>
<h2 id="💡-실제-코드-예시">💡 실제 코드 예시</h2>
<pre><code class="language-tsx">import { useRef, useEffect } from &#39;react&#39;;

function MyComponent() {
  const myBoxRef = useRef(null);

  useEffect(() =&gt; {
    console.log(myBoxRef.current); // 실제 DOM 요소가 출력됨
    myBoxRef.current.style.backgroundColor = &#39;lightblue&#39;;
  }, []);

  return (
    &lt;div ref={myBoxRef}&gt;
      Hello
    &lt;/div&gt;
  );
}</code></pre>
<h3 id="✅-위-코드에서-벌어지는-일">✅ 위 코드에서 벌어지는 일</h3>
<ul>
<li><code>myBoxRef</code>는 <code>div</code>를 가리키는 <strong>ref 객체</strong></li>
<li><code>myBoxRef.current</code>는 실제 HTML <code>&lt;div&gt;</code> 요소</li>
<li>여기에 직접 스타일을 주거나 포커스를 줄 수 있음</li>
</ul>
<hr>
<h2 id="🧠-ref를-쓰는-대표적인-상황">🧠 ref를 쓰는 대표적인 상황</h2>
<table>
<thead>
<tr>
<th>상황</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>드래그 앤 드롭</td>
<td>요소의 위치와 크기를 직접 측정해야 할 때</td>
</tr>
<tr>
<td>포커스 이동</td>
<td>input 창에 자동으로 포커스 줄 때</td>
</tr>
<tr>
<td>스크롤 조작</td>
<td>특정 위치로 스크롤 시킬 때</td>
</tr>
<tr>
<td>애니메이션 적용</td>
<td>DOM에 직접 스타일 조작할 때</td>
</tr>
</tbody></table>
<hr>
<h2 id="🧲-drag-and-drop에서-왜-꼭-ref가-필요할까">🧲 Drag and Drop에서 왜 꼭 ref가 필요할까?</h2>
<p>드래그 앤 드롭은 요소의 위치를 직접 추적해야 해.</p>
<ul>
<li>예: &quot;지금 이 박스 위에 마우스를 놓고 있어?&quot;</li>
<li>이걸 알려면 실제 DOM의 <strong>위치 정보</strong>를 가져와야 해</li>
</ul>
<pre><code class="language-tsx">const rect = element.getBoundingClientRect();</code></pre>
<p>이 함수는 <strong>실제 HTML 요소</strong>의 화면 위치(px 단위)를 알려줌 → <strong>가상 DOM으로는 절대 불가능</strong></p>
<p>그래서 드래그 라이브러리들은 <code>&lt;li&gt;</code>, <code>&lt;ul&gt;</code> 같은 DOM 요소를 ref로 직접 가리켜서,</p>
<p>브라우저에게 위치 정보를 묻고 → 거기에 드롭해도 되는지 판단함.</p>
<hr>
<h2 id="❗️그렇다면-가상-dom만-쓰면-어떤-문제가-생길까">❗️그렇다면 가상 DOM만 쓰면 어떤 문제가 생길까?</h2>
<table>
<thead>
<tr>
<th>문제</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>드롭 위치 계산 불가</td>
<td>마우스가 어디에 있는지 알 수 없어</td>
</tr>
<tr>
<td>충돌 감지 불가</td>
<td>두 요소가 겹쳤는지 모름</td>
</tr>
<tr>
<td>화면 변화 감지 불가</td>
<td>스크롤, 창 크기 변화 등 반응할 수 없음</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-결론">✅ 결론</h2>
<blockquote>
<p>가상 DOM은 <strong>구조(설계도)</strong>만 알고,</p>
<p><strong>위치, 크기, 충돌</strong> 같은 <strong>화면 위의 정보</strong>는 모른다.</p>
<p>그래서 드래그 앤 드롭처럼 화면에서 <strong>정확한 위치를 계산해야 하는 기능</strong>은</p>
<p>반드시 <strong>실제 DOM을 ref로 직접 조작</strong>해야 한다.</p>
</blockquote>
<hr>
<h2 id="📦-핵심-요약">📦 핵심 요약</h2>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>ref</code></td>
<td>실제 DOM을 React에서 가리키는 포인터</td>
</tr>
<tr>
<td><code>ref.current</code></td>
<td>진짜 HTML 요소를 가리킴</td>
</tr>
<tr>
<td><code>useRef()</code></td>
<td>ref를 만드는 Hook</td>
</tr>
<tr>
<td>드래그에서 역할</td>
<td>요소 위치를 계산하려면 반드시 필요함</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-정말-중요한-한-줄-요약">✅ 정말 중요한 한 줄 요약</h2>
<blockquote>
<p>ref는 &quot;React야, 이 DOM 여기 있어!&quot; 하고 알려주는 표시자야.</p>
<p>드래그 앤 드롭처럼 실제 위치, 크기 등을 계산해야 할 땐</p>
<p><strong>가상 DOM으론 부족하니까 → ref로 실제 DOM을 직접 가리켜야 해.</strong>
<img src="https://velog.velcdn.com/images/bom_0320/post/a3e46848-55c0-4c0b-a614-902348874f54/image.png" alt=""></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Zustand에 대하여]]></title>
            <link>https://velog.io/@bom_0320/Zustand%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@bom_0320/Zustand%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Wed, 14 May 2025 05:29:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bom_0320/post/e47d91bc-65a2-4196-a0fa-479bd0479804/image.png" alt="">
Zustand는 독일어로 ‘상태’ 라는 뜻으로 React 생태계에서 사용하는 <strong>상태 관리 라이브러리</strong>이다. </p>
<p>현재 Redux가 압도적으로 많이 사용되고 있지만 문법이 더러운편이라 학습에 시간이 필요하다고 한다. Post Redux로서 사용자가 빠르게 늘고 있는 Zustand에 대해 알아보도록 하자</p>
<hr>
<h3 id="사용하는-이유">사용하는 이유</h3>
<p>기본적으로 React의 <strong>데이터 흐름</strong>은 State &amp; Props를 사용해 <strong>단방향으로 이루져야 한다</strong>는 원칙이 있다. </p>
<p>이러한 데이터 흐름은 단순하고 예측 가능하며 컴포넌트 간의 관계를 명확히 정의하기 때문에 유지보수성을 향상시킨다. </p>
<blockquote>
<p><strong>하지만..</strong>
데이터를 전달하는 과정에서 <strong>거쳐야 하는 컴포넌트가 너무 많은 상황</strong>이면 어떨까?</p>
</blockquote>
<h3 id="⚠️props-drilling-발생">⚠️Props Drilling 발생!!!!!</h3>
<pre><code class="language-tsx">1. 최상위 부모 컴포넌트에서 상태나 데이터를 가지고 있다.
2. 중간에 위치한 하위 컴포넌트가 해당 데이터에 접근할 필요가 있다.
3.  하지만 중간에 있는 다른 컴포넌트들은 그 데이터를 사용하지 않음에도 불구하고, 
   React 구조상 자식에게 전달하려면 무조건 props로 받아야 하므로, 
   그냥 “전달만 하는” 역할을 하게 된다. 
4. 결과적으로 데이터는 여러 컴포넌트를 통과하여 목적지에 도달한다. 
5. 이 과정을 “props drilling”이라고 함</code></pre>
<p>코드로 보자면..</p>
<pre><code class="language-tsx">// 👑 App (최상위)
function App() {
  const user = { name: &quot;봄김&quot; };
  return &lt;Parent user={user} /&gt;;
}

// 👩 Parent (데이터 안 씀) → 그냥 전달
function Parent({ user }: { user: { name: string } }) {
  return &lt;Child user={user} /&gt;;
}

// 👧 Child (데이터 안 씀) → 또 전달
function Child({ user }: { user: { name: string } }) {
  return &lt;TargetComponent user={user} /&gt;;
}

// 🎯 TargetComponent (여기서만 사용)
function TargetComponent({ user }: { user: { name: string } }) {
  return &lt;div&gt;{user.name}님 환영합니다!&lt;/div&gt;;
}
</code></pre>
<p><code>Parent</code>, <code>Child</code>는 <strong>user라는 데이터가 필요 없음에도 불구하고, 하위 컴포넌트에 전달하기 위헤 props를 계속 받아서 넘겨야함</strong></p>
<p>이게 바로 props drillling의 문제의 핵심이다.</p>
<hr>
<p><strong>😵 문제점 요약</strong></p>
<ul>
<li>중간 컴포넌트가 필요 없는 데이터를 계속 전달해야 함</li>
<li>컴포넌트 구조가 복잡해질수록 코드 유지보수가 어려워짐</li>
<li>구조가 바뀔 경우 관련된 컴포넌트 모두를 수정해야 함</li>
<li>불필요한 렌더링 유발 가능성 존재</li>
</ul>
<hr>
<h3 id="그래서-zustand">그래서 Zustand!</h3>
<blockquote>
<p>👉 <strong>Zustand는 이런 props drilling을 막기 위한 &quot;전역 상태 관리 라이브러리&quot;!</strong></p>
</blockquote>
<p>즉, 필요한 컴포넌트는 Zustand stord에서 직접 가져와서 사용하면 된다.</p>
<p>중간 단계 컴포넌트를 거칠 필요가 없는것!!!!!</p>
<hr>
<h3 id="✨-zustand로-바꾼다면">✨ Zustand로 바꾼다면?</h3>
<h3 id="📦-상태-저장소-정의">📦 상태 저장소 정의</h3>
<pre><code class="language-tsx">// store/userStore.ts
import { create } from &#39;zustand&#39;

export const useUserStore = create(() =&gt; ({
  user: { name: &#39;봄김&#39; },
}));</code></pre>
<h3 id="🧾-컴포넌트에서-바로-사용">🧾 컴포넌트에서 바로 사용</h3>
<pre><code class="language-tsx">function UserInfo() {
  const user = useUserStore((state) =&gt; state.user);
  return &lt;div&gt;{user.name}님 환영합니다!&lt;/div&gt;;
}</code></pre>
<p>➡️ 이제는 중간에 있는 <code>Parent</code>, <code>Child</code> 컴포넌트가 <strong>전혀 신경 쓸 필요 없음!</strong></p>
<hr>
<h2 id="📌-마무리-요약">📌 마무리 요약</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>🔁 Props Drilling</td>
<td>필요 없는 컴포넌트들이 props를 전달만 하는 상황</td>
</tr>
<tr>
<td>🧠 Zustand</td>
<td>중간 단계를 건너뛰고, 필요한 곳에서 바로 상태를 가져올 수 있게 해줌</td>
</tr>
<tr>
<td>💡 효과</td>
<td>코드 간결, 구조 깔끔, 유지보수 쉬움</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[About_entities]]></title>
            <link>https://velog.io/@bom_0320/Aboutentities</link>
            <guid>https://velog.io/@bom_0320/Aboutentities</guid>
            <pubDate>Wed, 14 May 2025 00:58:00 GMT</pubDate>
            <description><![CDATA[<p>FSD 구조에서 <code>entities</code> 레이어는 <strong>비즈니스 개념 중심(User, Post 등)</strong> 의 코스를 담당한다. 
여기서 말하는 &quot;비즈니스 개념&quot;이란, 단순히 기능이 아니라 <strong>&quot;데이터 중심이냐 Vs. 기능 중심이냐&quot;</strong> 로 나눠서 이해해야 한다.</p>
<hr>
<h2 id="entities-폴더에는-데이터-중심-개념이-들어간다"><code>entities</code> 폴더에는 데이터 중심 개념이 들어간다.</h2>
<ul>
<li><code>User</code>, <code>Post</code>, <code>Comment</code>, <code>Product</code>, <code>Team</code>, <code>Ranking</code> 등</li>
<li>이들은 앱 내에서 <strong>&quot;핵심 객체(주체)&quot;</strong> 로 존재하며,</li>
<li><strong>관련된 모델, API, 상태</strong> 등을 포함한다.</li>
</ul>
<h2 id="features-폴더에는-기능-중심-개념이-들어간다"><code>features</code> 폴더에는 기능 중심 개념이 들어간다.</h2>
<ul>
<li>예: <code>login</code>, <code>signup</code>, <code>writeComment</code>, <code>likePost</code> 등</li>
<li>사용자의 <strong>행위(시나이로)</strong> 를 담당하며,</li>
<li>보통 <strong>UI와 로직</strong> 이 함께 들어있다.</li>
</ul>
<hr>
<h2 id="그렇다면-데이터-중심이란">그렇다면 &quot;데이터 중심&quot;이란?</h2>
<blockquote>
<p>현실 세계에 존재하는 개체를 앱 안에서 고유한 데이터 구조로 모델링한 것</p>
</blockquote>
<ul>
<li>앱에서 어떤 대상을 표현하거나 저장하려면 데이터를 기반으로 구조화해야 한다.</li>
<li>이런 구조는 다양한 화면인 기능에서 <strong>재사용 가능</strong>하다.</li>
</ul>
<hr>
<h3 id="🔍-예시로-이해하기">🔍 예시로 이해하기</h3>
<h3 id="🔷-user-→-데이터-중심-개념-✅-entities에-들어감">🔷 <code>User</code> → 데이터 중심 개념 (✅ <code>entities</code>에 들어감)</h3>
<pre><code class="language-tsx">{
  id: 1,
  name: &quot;봄김&quot;,
  email: &quot;bom@dev.com&quot;
}
</code></pre>
<ul>
<li>사용자라는 <strong>대상을 표현하는 객체</strong></li>
<li>여러 화면에서 보여지고, 상태로도 저장됨 → <strong>데이터 중심</strong></li>
</ul>
<hr>
<h3 id="🔷-signin-→-기능-중심-개념-✅-features에-들어감">🔷 <code>signin</code> → 기능 중심 개념 (✅ <code>features</code>에 들어감)</h3>
<ul>
<li><p>로그인을 한다는 건 <strong>행위</strong>이지, 어떤 <strong>대상을 표현</strong>하는 건 아님</p>
</li>
<li><p><code>POST /login</code> 요청을 보낼 뿐, 이 자체는 <strong>데이터로 저장되지 않음</strong></p>
<p>  → 따라서 <strong>기능 중심 개념</strong></p>
</li>
</ul>
<hr>
<h2 id="✅-개념-비교-정리">✅ 개념 비교 정리</h2>
<table>
<thead>
<tr>
<th>개념</th>
<th>중심</th>
<th>예시</th>
<th>소속</th>
</tr>
</thead>
<tbody><tr>
<td><code>User</code>, <code>Post</code>, <code>Product</code></td>
<td>✅ 데이터 중심</td>
<td>고유한 데이터가 있고 다양한 화면에 사용됨</td>
<td><code>entities/</code></td>
</tr>
<tr>
<td><code>Login</code>, <code>Signup</code>, <code>Like</code>, <code>WriteComment</code></td>
<td>❌ 기능 중심</td>
<td>유저의 특정 행위, 시나리오 중심</td>
<td><code>features/</code></td>
</tr>
</tbody></table>
<hr>
<h2 id="💡-더-쉽게-기억하는-법">💡 더 쉽게 기억하는 법</h2>
<table>
<thead>
<tr>
<th>질문</th>
<th>Yes라면 <code>entities</code>, No라면 <code>features</code></th>
</tr>
</thead>
<tbody><tr>
<td>이건 &quot;무언가&quot;를 설명하는 데이터인가?</td>
<td>✔️ → <code>User</code>, <code>Post</code></td>
</tr>
<tr>
<td>이건 &quot;무언가를 하는 행위&quot;인가?</td>
<td>✔️ → <code>signin</code>, <code>commentWrite</code></td>
</tr>
</tbody></table>
<hr>
<h2 id="🔄-기억용-요약-문장">🔄 기억용 요약 문장</h2>
<blockquote>
<p>&quot;데이터 중심이라는 건, 화면에 보여지고 저장되는 정보 자체를 중심으로 생각하는 거야.User는 어떤 대상(사람)이지만, signin은 행동이잖아?&quot;</p>
</blockquote>
<hr>
<h2 id="📌-추가로-알아두면-좋은-점">📌 추가로 알아두면 좋은 점</h2>
<h3 id="❓-entities-폴더에-signin-같은-기능이-들어가도-될까">❓ <code>entities</code> 폴더에 <code>signin</code> 같은 기능이 들어가도 될까?</h3>
<ul>
<li><p>FSD 원칙에 따르면,</p>
<ul>
<li><strong>데이터 중심 개념</strong>은 <code>entities/</code>,</li>
<li><strong>행동 중심 기능</strong>은 <code>features/</code>에 넣는 것이 가장 이상적이다.</li>
</ul>
</li>
<li><p>하지만 실제 프로젝트에서는 <strong>규모나 팀 스타일에 따라</strong></p>
<p>  <code>features/</code>를 따로 만들지 않고, <strong>작은 기능들을 <code>entities/</code> 안에 넣는 경우</strong>도 많다.</p>
</li>
<li><p>즉, <code>entities/signin</code>처럼 구성된 경우도 <strong>잘못된 건 아니며</strong>,</p>
<p>  <strong>작은 기능을 &quot;하나의 도메인처럼&quot; 다룬 유연한 구조</strong>라고 이해하면 된다.</p>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[🧼 Prettier란? 그리고 설정 방법]]></title>
            <link>https://velog.io/@bom_0320/Prettier%EB%9E%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@bom_0320/Prettier%EB%9E%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 13 May 2025 00:49:02 GMT</pubDate>
            <description><![CDATA[<h2 id="prettier란">Prettier란?</h2>
<blockquote>
<p><strong>코드 포맷터</strong>
내가 작성한 코드를 일정한 규칙에 맞게 자동으로 정리해주는 도구이다.</p>
</blockquote>
<h3 id="예시">예시</h3>
<pre><code class="language-arduino">// 포맷팅 전
function hello ( ){console.log(&quot;hi&quot;)}

// 포맷팅 후 (Prettier 적용 시)
function hello() {
  console.log(&quot;hi&quot;);
}</code></pre>
<hr>
<h2 id="🛠️-prettier의-장점">🛠️ Prettier의 장점</h2>
<ul>
<li>팀원 간 <strong>코드 스타일 통일</strong></li>
<li>저장할 때마다 자동 정리 → <strong>시간 절약</strong></li>
<li>리뷰 때 <strong>불필요한 스타일 논쟁 줄어듦</strong></li>
</ul>
<hr>
<h2 id="⚙️-prettier-설정-방법-2가지">⚙️ Prettier 설정 방법 (2가지)</h2>
<h3 id="①-prettierrc-파일로-설정">① <code>.prettierrc</code> 파일로 설정</h3>
<blockquote>
<p>설정 전용 파일을 루트 디렉토리에 만들어서 사용</p>
</blockquote>
<p>파일명 예시: <code>.prettierrc</code>, <code>.prettierrc.json</code>, <code>.prettierrc.yaml</code> 등</p>
<pre><code class="language-json">{
  &quot;semi&quot;: true,
  &quot;singleQuote&quot;: true,
  &quot;printWidth&quot;: 100
}</code></pre>
<p>→ 이 파일이 있으면 Prettier는 여기 설정을 따름</p>
<hr>
<h3 id="②-packagejson-안에-prettier-항목으로-설정">② <code>package.json</code> 안에 <code>&quot;prettier&quot;</code> 항목으로 설정</h3>
<blockquote>
<p>따로 파일 안 만들고, 프로젝트 설정 파일 안에서 설정</p>
</blockquote>
<pre><code class="language-json">
{
  &quot;name&quot;: &quot;my-project&quot;,
  ...
  &quot;prettier&quot;: {
    &quot;semi&quot;: true,
    &quot;singleQuote&quot;: true}
}
</code></pre>
<p>→ 이 경우에도 동일하게 작동함</p>
<hr>
<h2 id="✨-결론-요약">✨ 결론 요약</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>.prettierrc</code> 파일 있음</td>
<td>Prettier는 이걸 <strong>우선 사용</strong></td>
</tr>
<tr>
<td><code>package.json</code> 설정</td>
<td>가능하지만 <code>.prettierrc</code>보다 <strong>우선순위 낮음</strong></td>
</tr>
<tr>
<td>둘 다 있음</td>
<td>헷갈릴 수 있으니 <strong>하나만 쓰는 걸 권장</strong></td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[create-next-app 질문 선택 여부 확인 정리]]></title>
            <link>https://velog.io/@bom_0320/create-next-app-%EC%A7%88%EB%AC%B8-%EC%84%A0%ED%83%9D-%EC%97%AC%EB%B6%80-%ED%99%95%EC%9D%B8-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@bom_0320/create-next-app-%EC%A7%88%EB%AC%B8-%EC%84%A0%ED%83%9D-%EC%97%AC%EB%B6%80-%ED%99%95%EC%9D%B8-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 13 May 2025 00:27:28 GMT</pubDate>
            <description><![CDATA[<table>
<thead>
<tr>
<th>질문</th>
<th>확인 방법</th>
<th>확인 기준</th>
</tr>
</thead>
<tbody><tr>
<td>✔ <strong>Would you like to use TypeScript?</strong></td>
<td>프로젝트에 <code>tsconfig.json</code>이 있는지 확인</td>
<td>있으면 TypeScript 사용 (<code>.ts</code>, <code>.tsx</code> 파일도 같이 사용됨)</td>
</tr>
<tr>
<td>✔ <strong>Would you like to use ESLint?</strong></td>
<td><code>package.json</code>의 <code>devDependencies</code>에 <code>eslint</code>가 있는지 확인</td>
<td>있으면 ESLint 사용 중</td>
</tr>
<tr>
<td>✔ <strong>Would you like to use Tailwind CSS?</strong></td>
<td><code>package.json</code>에 <code>tailwindcss</code>, <code>postcss</code>, <code>autoprefixer</code>가 있는지 확인</td>
<td>이 3개가 있으면 Tailwind 사용 중</td>
</tr>
<tr>
<td>✔ <strong>Would you like your code inside a <code>src/</code> directory?</strong></td>
<td>프로젝트 루트에 <code>src/</code> 폴더가 있는지 확인</td>
<td>있으면 <code>src/</code> 구조 선택함</td>
</tr>
<tr>
<td>✔ <strong>Would you like to use App Router?</strong></td>
<td><code>src/</code> 또는 루트에 <code>app/</code> 폴더가 있는지 확인</td>
<td>있으면 App Router 사용 (없고 <code>pages/</code>만 있으면 전통 방식)</td>
</tr>
<tr>
<td>✔ <strong>Would you like to use Turbopack for <code>next dev</code>?</strong></td>
<td><code>npm run dev</code> 실행 시 터미널 로그 확인</td>
<td><code>✔ Using Turbopack</code>이 뜨면 사용, 아니면 Webpack 사용 중</td>
</tr>
<tr>
<td>✔ <strong>Would you like to customize the import alias?</strong></td>
<td><code>tsconfig.json</code>(또는 <code>jsconfig.json</code>) 확인</td>
<td><code>&quot;paths&quot;</code> 항목에 <code>&quot;@/*&quot;: [&quot;src/*&quot;]</code> 있으면 alias 설정한 것</td>
</tr>
<tr>
<td>✔ <strong>What import alias would you like configured?</strong></td>
<td>위 <code>&quot;paths&quot;</code> 설정을 보면 어떤 alias인지 알 수 있음</td>
<td>기본은 <code>&quot;@/*&quot;</code>이지만 직접 수정했을 수도 있음</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL – Next.js 프로젝트 생성 방식에 따른 차이점과 깨달음]]></title>
            <link>https://velog.io/@bom_0320/TIL-Next.js-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%94%B0%EB%A5%B8-%EC%B0%A8%EC%9D%B4%EC%A0%90%EA%B3%BC-%EA%B9%A8%EB%8B%AC%EC%9D%8C</link>
            <guid>https://velog.io/@bom_0320/TIL-Next.js-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%94%B0%EB%A5%B8-%EC%B0%A8%EC%9D%B4%EC%A0%90%EA%B3%BC-%EA%B9%A8%EB%8B%AC%EC%9D%8C</guid>
            <pubDate>Mon, 12 May 2025 23:59:37 GMT</pubDate>
            <description><![CDATA[<h3 id="날짜-20250513">날짜: 2025.05.13</h3>
<h3 id="📌-주제-nextjs-프로젝트-생성--수동-설치-vs-create-next-app-자동-설정">📌 주제: Next.js 프로젝트 생성 – 수동 설치 vs create-next-app 자동 설정</h3>
<hr>
<h2 id="✅-1-내가-처음-사용한-방식--수동-설치">✅ 1. 내가 처음 사용한 방식 – 수동 설치</h2>
<p>처음에는 강의에서 알려준 대로 아래처럼 수동으로 Next.js 프로젝트를 구성했다:</p>
<pre><code class="language-bash">mkdir my-next-app
cd my-next-app
npm init -y
npm install react react-dom next
mkdir app
touch app/page.tsx
</code></pre>
<p>그리고 <code>package.json</code>을 아래처럼 수정했다:</p>
<pre><code class="language-json">&quot;scripts&quot;: {
  &quot;dev&quot;: &quot;next dev&quot;,
  &quot;build&quot;: &quot;next build&quot;,
  &quot;start&quot;: &quot;next start&quot;
}
</code></pre>
<p>이 방식은 모든 구조와 설정을 직접 만져보면서 학습할 수 있다는 장점이 있었다.</p>
<p>하지만 실제 협업 프로젝트에 들어가면서 여러 <strong>치명적인 단점</strong>을 겪었다.</p>
<hr>
<h2 id="🧨-2-겪은-문제들">🧨 2. 겪은 문제들</h2>
<ul>
<li><p><code>.eslintrc.json</code>은 있는데 <code>eslint</code>가 설치되어 있지 않음</p>
<p>  → 설정만 있고 작동은 안 되는 상태</p>
</li>
<li><p><code>.prettierrc</code> 파일도 수동으로 만들었지만 관련 패키지가 없었음</p>
</li>
<li><p>TypeScript는 설치했지만 <code>tsconfig.json</code> 설정은 직접 만들어야 했음</p>
</li>
<li><p>팀원들마다 환경이 달라서 코드 스타일이 달라지고 충돌 발생</p>
</li>
<li><p>Tailwind CSS도 수동 설치해야 해서 공식 문서를 계속 참고해야 했음</p>
</li>
</ul>
<hr>
<h2 id="✅-3-협업에서는-create-next-app이-훨씬-낫다는-걸-깨달음">✅ 3. 협업에서는 create-next-app이 훨씬 낫다는 걸 깨달음</h2>
<p>나중에 <code>npx create-next-app@latest</code>를 써보면서 다음과 같은 장점을 느꼈다:</p>
<pre><code class="language-bash">npx create-next-app@latest .
</code></pre>
<blockquote>
<p>. 을 붙이면 현재 폴더에 세팅됨</p>
</blockquote>
<h3 id="생성-중-질문-예시--추천-답변">생성 중 질문 예시 &amp; 추천 답변:</h3>
<table>
<thead>
<tr>
<th>질문</th>
<th>추천 답변</th>
</tr>
</thead>
<tbody><tr>
<td>TypeScript 사용</td>
<td>Yes</td>
</tr>
<tr>
<td>ESLint 사용</td>
<td>Yes</td>
</tr>
<tr>
<td>Tailwind CSS 사용</td>
<td>Yes or No (원하는 대로)</td>
</tr>
<tr>
<td>src 폴더 사용할래?</td>
<td>Yes</td>
</tr>
<tr>
<td>App Router 사용할래?</td>
<td>Yes</td>
</tr>
<tr>
<td>Turbopack 쓸래?</td>
<td>No</td>
</tr>
<tr>
<td>import alias 설정할래?</td>
<td>Yes (<code>@/*</code>)</td>
</tr>
</tbody></table>
<p>이렇게만 선택하면 모든 설정이 자동으로 되어, 실수 없이 바로 개발에 들어갈 수 있다.</p>
<hr>
<h2 id="💡-4-도구들에-대한-이해">💡 4. 도구들에 대한 이해</h2>
<ul>
<li><strong>ESLint</strong>: 코드 문법 검사기. 실수를 방지하고 일관된 코드 스타일을 유지해준다.</li>
<li><strong>Prettier</strong>: 코드 포맷터. 줄 정리, 들여쓰기, 세미콜론 자동 정리 등.</li>
<li><strong>Tailwind CSS</strong>: CSS를 유틸리티 클래스 기반으로 빠르게 작성할 수 있게 해주는 프레임워크.</li>
<li><strong>TypeScript</strong>: 자바스크립트에 타입을 추가해 주는 언어. 코드 안정성과 자동완성 기능 향상.</li>
</ul>
<hr>
<h2 id="🌱-5-깨달은-점">🌱 5. 깨달은 점</h2>
<ul>
<li>수동 설치는 배우는 데는 좋지만, 협업이나 실무에서는 <strong>자동 설정(create-next-app)</strong>이 훨씬 안정적이다.</li>
<li><strong>필수 설정들을 빠뜨리면 작동이 안 되거나 버그가 생길 수 있다.</strong></li>
<li>create-next-app은 단순히 편한 게 아니라, <strong>&quot;실무 기준의 안정적인 개발 환경을 만들어주는 도구&quot;</strong>라는 걸 알게 됐다.</li>
<li>앞으로는 자동 설치로 시작한 후, 필요한 설정만 커스터마이징하는 방식으로 효율적으로 개발할 계획이다.</li>
</ul>
<hr>
<h2 id="📁-6-내가-만든-폴더-구조-예시">📁 6. 내가 만든 폴더 구조 예시</h2>
<pre><code class="language-bash">src/
├── entities/   # 핵심 도메인 모델
├── shared/     # 공통 유틸, 타입, 훅 등
├── views/      # 페이지 뷰
├── widgets/    # 재사용 가능한 UI 조각
</code></pre>
<hr>
<h2 id="🏁-마무리">🏁 마무리</h2>
<p>이 경험을 통해, 단순히 코드를 작성하는 것이 아니라 <strong>개발 환경을 어떻게 구성하느냐도 실력</strong>이라는 걸 깊이 깨달았다.</p>
<p>수동 설치도 분명 도움이 되었고, 그 덕분에 자동 설치의 강력함도 제대로 느낄 수 있었다.</p>
<hr>
<h2 id="📎-관련-학습-기록">📎 관련 학습 기록</h2>
<ul>
<li><p>처음에는 아래와 같이 수동으로 Next.js 프로젝트를 설정하는 방법을 학습했었다.</p>
<p>  👉 <a href="https://velog.io/@bom_0320/Next.js-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EA%B3%BC%EC%A0%95-%EC%A0%95%EB%A6%AC">TIL: Next.js 수동 프로젝트 생성법</a></p>
</li>
<li><p>이번에 협업 경험을 통해 자동 생성 방식(create-next-app)의 강력함과 효율성을 깨닫게 되었고,</p>
<p>  앞으로는 이 두 가지를 모두 이해하고 상황에 맞게 선택할 수 있게 되었다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: TypeScript에서의 세 가지 버전 충돌 문제 + CRA의 한계]]></title>
            <link>https://velog.io/@bom_0320/TIL-TypeScript%EC%97%90%EC%84%9C%EC%9D%98-%EC%84%B8-%EA%B0%80%EC%A7%80-%EB%B2%84%EC%A0%84-%EC%B6%A9%EB%8F%8C-%EB%AC%B8%EC%A0%9C-CRA%EC%9D%98-%ED%95%9C%EA%B3%84</link>
            <guid>https://velog.io/@bom_0320/TIL-TypeScript%EC%97%90%EC%84%9C%EC%9D%98-%EC%84%B8-%EA%B0%80%EC%A7%80-%EB%B2%84%EC%A0%84-%EC%B6%A9%EB%8F%8C-%EB%AC%B8%EC%A0%9C-CRA%EC%9D%98-%ED%95%9C%EA%B3%84</guid>
            <pubDate>Mon, 12 May 2025 08:30:41 GMT</pubDate>
            <description><![CDATA[<h3 id="오늘의-이슈">오늘의 이슈</h3>
<p><code>npm install --save-dev @types/react-query</code> 명령어 실행 시, 아래와 같은 에러가 발생함:</p>
<pre><code class="language-kotlin">npm error ERESOLVE could not resolve
...
Conflicting peer dependency: typescript@5.8.3
peerOptional typescript@&quot;^3.2.1 || ^4&quot; from react-scripts@5.0.1
</code></pre>
<hr>
<h2 id="원인-분석-세-가지-버전-충돌-중-하나">원인 분석: 세 가지 버전 충돌 중 하나</h2>
<p>TypeScript 프로젝트에서는 다음 세 가지 버전이 서로 영향을 주고 받는다:</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>설명</th>
<th>지금 내 상황과의 연관성</th>
</tr>
</thead>
<tbody><tr>
<td>① <strong>라이브러리 버전</strong></td>
<td>예: <code>react-scripts@5.0.1</code>프로젝트 내부의 빌드 및 실행 도구</td>
<td>CRA(Create React App)로 프로젝트를 시작했기 때문에 <code>react-scripts</code>가 자동 설치됨. 해당 버전은 TypeScript 5를 지원하지 않음</td>
</tr>
<tr>
<td>② <strong>타입 선언 버전 (@types)</strong></td>
<td>예: <code>@types/react-query</code>타입스크립트에서 사용할 수 있도록 타입 정보만 제공하는 라이브러리</td>
<td>설치하려 했지만 TypeScript와 <code>react-scripts</code>의 충돌로 인해 설치 실패</td>
</tr>
<tr>
<td>③ <strong>TypeScript 버전</strong></td>
<td>예: <code>typescript@5.8.3</code>내가 설치한 최신 TS 버전</td>
<td>최신 버전이지만 <code>react-scripts@5.0.1</code>에서 요구하는 버전보다 높아서 충돌 발생</td>
</tr>
</tbody></table>
<hr>
<h2 id="🔥-에러-발생-구조-요약">🔥 에러 발생 구조 요약</h2>
<pre><code class="language-graphql">타입스크립트 버전이 너무 높음
→ react-scripts가 이걸 감당하지 못함
→ @types/react-query 같은 타입 선언 설치도 실패</code></pre>
<hr>
<h2 id="cra가-왜-이-문제를-만들었을까">CRA가 왜 이 문제를 만들었을까?</h2>
<h3 id="cra의-핵심-빌드-툴--react-scripts">CRA의 핵심 빌드 툴 = <code>react-scripts</code></h3>
<ul>
<li><p>CRA는 내부적으로 <code>react-scripts</code>라는 패키지를 사용해</p>
<p>  빌드, 테스트, 타입 검사 등을 자동으로 해준다.</p>
</li>
<li><p>그런데 현재 사용하는 <code>react-scripts@5.0.1</code>은</p>
<p>  <strong>TypeScript 5버전을 아직 지원하지 않는다.</strong></p>
</li>
<li><p><code>react-scripts</code>가 허용하는 TypeScript 버전 범위는 다음과 같다:</p>
<pre><code>  ^3.2.1 || ^4 (즉, TS 3 또는 TS 4.x까지만 가능)</code></pre></li>
</ul>
<hr>
<h2 id="😵💫-그래서-무슨-일이-벌어졌냐면">😵‍💫 그래서 무슨 일이 벌어졌냐면?</h2>
<p>너는 최신 버전인 <code>TypeScript 5.8</code>을 설치했는데,</p>
<p>CRA 내부의 <code>react-scripts</code>는 이렇게 말하는 거야:</p>
<pre><code class="language-arduino">react-scripts: &quot;나 TS 4까지만 쓸 수 있어!&quot;
TypeScript 5.8: &quot;난 최신 버전이야!&quot;</code></pre>
<p>→ <strong>둘이 버전이 안 맞아서 싸움 발생 → npm install 실패</strong></p>
<hr>
<h2 id="✅-해결-방법">✅ 해결 방법</h2>
<h3 id="🔧-방법-1-typescript-버전을-낮춰서-cra에-맞춘다-가장-쉬움">🔧 방법 1: TypeScript 버전을 낮춰서 CRA에 맞춘다 (가장 쉬움)</h3>
<pre><code class="language-bash">npm uninstall typescript
npm install --save-dev typescript@4.9.5
npm install --save-dev @types/react-query</code></pre>
<ul>
<li>이렇게 하면 <code>react-scripts@5.0.1</code>과 TypeScript 버전이 일치하므로 충돌 없이 설치 가능!</li>
</ul>
<hr>
<h3 id="🔧-방법-2-cra를-탈출하고-vite나-nextjs-같은-현대적인-도구로-전환한다">🔧 방법 2: CRA를 탈출하고 Vite나 Next.js 같은 현대적인 도구로 전환한다</h3>
<ul>
<li><strong>CRA는 2024년 이후로 유지보수가 사실상 중단</strong>된 상태이다.</li>
<li>많은 개발자들이 더 빠르고 가벼운 빌드 도구인 <code>Vite</code>, <code>Next.js</code>, <code>Parcel</code> 등으로 넘어가는 중이다.</li>
<li>최신 TypeScript 기능을 무리 없이 사용하고 싶다면 장기적으로는 전환을 고려하는 것이 좋다.</li>
</ul>
<hr>
<h2 id="느낀-점-및-교훈">느낀 점 및 교훈</h2>
<ul>
<li>TypeScript 프로젝트에서는 <strong>세 가지 버전의 호환성</strong>을 꼭 확인해야 한다:<ol>
<li>라이브러리 버전 (ex. <code>react-scripts</code>)</li>
<li>타입 선언 버전 (ex. <code>@types/react-query</code>)</li>
<li>타입스크립트 자체 버전 (ex. <code>typescript@5.8</code>)</li>
</ol>
</li>
<li>하나라도 어긋나면 “설치가 안 되거나”, “엉뚱한 곳에서 에러가 터지는” 문제가 생긴다.</li>
<li><strong>CRA는 더 이상 최신 스택을 따라가지 못하고 있으며</strong>, 새로운 프로젝트에서는 대체 도구(Vite, Next.js 등)를 사용하는 것이 더 좋다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴포넌트 Props의 타입 지정과 기본값 설정 (TypeScript + React)]]></title>
            <link>https://velog.io/@bom_0320/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-Props%EC%9D%98-%ED%83%80%EC%9E%85-%EC%A7%80%EC%A0%95%EA%B3%BC-%EA%B8%B0%EB%B3%B8%EA%B0%92-%EC%84%A4%EC%A0%95-TypeScript-React</link>
            <guid>https://velog.io/@bom_0320/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-Props%EC%9D%98-%ED%83%80%EC%9E%85-%EC%A7%80%EC%A0%95%EA%B3%BC-%EA%B8%B0%EB%B3%B8%EA%B0%92-%EC%84%A4%EC%A0%95-TypeScript-React</guid>
            <pubDate>Wed, 07 May 2025 12:06:25 GMT</pubDate>
            <description><![CDATA[<h3 id="📌-1-props-타입-지정-방법">📌 1. Props 타입 지정 방법</h3>
<p>컴포넌트에 전달되는 props의 타입은 <code>interface</code>를 사용해 지정한다.</p>
<pre><code class="language-tsx">
interface TextProps {
  text: string;
  active?: boolean; // optional (선택적 props)
}
</code></pre>
<ul>
<li><code>text</code> → <strong>필수 props</strong></li>
<li><code>active?</code> → <strong>선택적 props</strong></li>
</ul>
<hr>
<h3 id="📌-2-선택적-props-vs-필수적-props">📌 2. 선택적 props vs 필수적 props</h3>
<ul>
<li><code>:</code>만 쓰면 필수</li>
<li><code>?:</code>를 쓰면 선택적</li>
</ul>
<pre><code class="language-tsx">
interface ExampleProps {
  requiredProp: string;       // 필수
  optionalProp?: number;      // 선택
}
</code></pre>
<hr>
<h3 id="📌-3-props에-기본값-설정">📌 3. props에 기본값 설정</h3>
<p>기본값은 <strong>함수 파라미터에 직접 설정</strong>한다:</p>
<pre><code class="language-tsx">
function Dummy({ text, active = false }: TextProps) {
  return &lt;h1&gt;{text}&lt;/h1&gt;;
}
</code></pre>
<p>이렇게 하면 <code>active</code>를 전달하지 않아도 자동으로 <code>false</code>가 된다.</p>
<hr>
<h3 id="📌-4-boolean-props는-true일-때-생략된-형태로-작성-가능">📌 4. Boolean props는 <code>true</code>일 때 생략된 형태로 작성 가능</h3>
<pre><code class="language-tsx">
&lt;Dummy text=&quot;hello&quot; active /&gt;
</code></pre>
<ul>
<li>위 코드는 <code>active={true}</code>와 완전히 동일하다.</li>
<li>이는 boolean 타입 props의 특징으로, <code>true</code>일 경우 속성 이름만 작성해도 된다.</li>
</ul>
<hr>
<h3 id="✅-실습-예시-요약">✅ 실습 예시 요약</h3>
<pre><code class="language-tsx">
interface TextProps {
  text: string;
  active?: boolean;
}

function Dummy({ text, active = false }: TextProps) {
  return &lt;h1&gt;{text}&lt;/h1&gt;;
}

// 사용 예시
&lt;Dummy text=&quot;hello&quot; active /&gt;           // ✅ active는 true
&lt;Dummy text=&quot;world&quot; /&gt;                 // ✅ active는 false (기본값)
</code></pre>
<hr>
<h3 id="💡-마무리-요약">💡 마무리 요약</h3>
<table>
<thead>
<tr>
<th>목적</th>
<th>문법</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>props 타입 지정</td>
<td><code>interface</code></td>
<td>컴포넌트에 전달되는 값의 타입 명시</td>
</tr>
<tr>
<td>선택적 props</td>
<td><code>propName?: Type</code></td>
<td>전달하지 않아도 되는 props</td>
</tr>
<tr>
<td>기본값 설정</td>
<td><code>= defaultValue</code></td>
<td>함수 파라미터에서 기본값 설정</td>
</tr>
<tr>
<td>Boolean props true 표현</td>
<td><code>&lt;Component prop /&gt;</code></td>
<td><code>prop={true}</code>와 동일</td>
</tr>
</tbody></table>
<hr>
<p>필요한 경우 <code>defaultProps</code> 방식도 있지만, 함수형 컴포넌트에선 <strong>파라미터 기본값 방식</strong>을 더 권장한다고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2. animation 속성 하나씩 완전 정복하기]]></title>
            <link>https://velog.io/@bom_0320/2.-animation-%EC%86%8D%EC%84%B1-%ED%95%98%EB%82%98%EC%94%A9-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bom_0320/2.-animation-%EC%86%8D%EC%84%B1-%ED%95%98%EB%82%98%EC%94%A9-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 24 Apr 2025 10:46:34 GMT</pubDate>
            <description><![CDATA[<h2 id="🧱-animation-속성-전체-구조">🧱 animation 속성 전체 구조</h2>
<pre><code class="language-css">
animation:
  name               → 어떤 애니메이션 쓸 건지
  duration           → 몇 초 동안 할 건지
  timing-function    → 속도가 어떻게 변할 건지
  delay              → 언제 시작할 건지
  iteration-count    → 몇 번 반복할 건지
  direction          → 방향은 어떻게 될 건지
  fill-mode          → 끝나고도 스타일 유지할 건지
  play-state         → 재생할지 멈출지
</code></pre>
<hr>
<h2 id="🔍-step-1-animation-name">🔍 Step 1: <code>animation-name</code></h2>
<h3 id="✅-개념">✅ 개념</h3>
<blockquote>
<p>@keyframes로 만든 애니메이션의 이름을 지정</p>
</blockquote>
<h3 id="🧠-예제">🧠 예제</h3>
<pre><code class="language-css">@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.box {
  animation-name: fadeIn;
}
</code></pre>
<hr>
<h2 id="🔍-step-2-animation-duration">🔍 Step 2: <code>animation-duration</code></h2>
<h3 id="✅-개념-1">✅ 개념</h3>
<blockquote>
<p>애니메이션이 한 번 완전히 실행되는 데 걸리는 시간</p>
</blockquote>
<h3 id="💡-단위">💡 단위</h3>
<ul>
<li><code>s</code> → 초 (<code>2s</code> = 2초)</li>
<li><code>ms</code> → 밀리초 (<code>500ms</code> = 0.5초)</li>
</ul>
<h3 id="🧠-예제-1">🧠 예제</h3>
<pre><code class="language-css">
.box {
  animation-name: fadeIn;
  animation-duration: 1.5s;
}
</code></pre>
<hr>
<h2 id="🔍-step-3-animation-timing-function">🔍 Step 3: <code>animation-timing-function</code></h2>
<h3 id="✅-개념-2">✅ 개념</h3>
<blockquote>
<p>애니메이션의 속도 곡선 (느려졌다 빨라졌다 같은)</p>
</blockquote>
<h3 id="💡-대표-값">💡 대표 값</h3>
<table>
<thead>
<tr>
<th>값</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>linear</code></td>
<td>일정한 속도</td>
</tr>
<tr>
<td><code>ease</code></td>
<td>느리게 시작 → 빠름 → 느리게 끝</td>
</tr>
<tr>
<td><code>ease-in</code></td>
<td>느리게 시작</td>
</tr>
<tr>
<td><code>ease-out</code></td>
<td>느리게 끝남</td>
</tr>
<tr>
<td><code>ease-in-out</code></td>
<td>양쪽 다 부드럽게</td>
</tr>
</tbody></table>
<h3 id="🧠-예제-2">🧠 예제</h3>
<pre><code class="language-css">
.box {
  animation-name: fadeIn;
  animation-duration: 2s;
  animation-timing-function: ease-in-out;
}
</code></pre>
<hr>
<h2 id="🔍-step-4-animation-delay">🔍 Step 4: <code>animation-delay</code></h2>
<h3 id="✅-개념-3">✅ 개념</h3>
<blockquote>
<p>애니메이션이 얼마 후에 시작될지 지정</p>
</blockquote>
<h3 id="🧠-예제-3">🧠 예제</h3>
<pre><code class="language-css">
.box {
  animation-name: fadeIn;
  animation-duration: 1s;
  animation-delay: 2s; /* 2초 뒤에 시작 */
}
</code></pre>
<hr>
<h2 id="🔍-step-5-animation-iteration-count">🔍 Step 5: <code>animation-iteration-count</code></h2>
<h3 id="✅-개념-4">✅ 개념</h3>
<blockquote>
<p>애니메이션을 몇 번 반복할지 정함</p>
</blockquote>
<h3 id="💡-값">💡 값</h3>
<ul>
<li>숫자 (<code>1</code>, <code>2</code>, <code>3</code> 등)</li>
<li><code>infinite</code> → 무한 반복</li>
</ul>
<h3 id="🧠-예제-4">🧠 예제</h3>
<pre><code class="language-css">
.box {
  animation-name: fadeIn;
  animation-duration: 2s;
  animation-iteration-count: infinite;
}
</code></pre>
<hr>
<h2 id="🔍-step-6-animation-direction">🔍 Step 6: <code>animation-direction</code></h2>
<h3 id="✅-개념-5">✅ 개념</h3>
<blockquote>
<p>애니메이션 반복 시 방향을 바꿀지 말지</p>
</blockquote>
<table>
<thead>
<tr>
<th>값</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>normal</code></td>
<td>항상 처음부터 끝까지</td>
</tr>
<tr>
<td><code>reverse</code></td>
<td>항상 끝부터 시작으로</td>
</tr>
<tr>
<td><code>alternate</code></td>
<td>왔다갔다 (정방향 → 역방향)</td>
</tr>
<tr>
<td><code>alternate-reverse</code></td>
<td>반대로 왔다갔다 (역방향 → 정방향)</td>
</tr>
</tbody></table>
<h3 id="🧠-예제-5">🧠 예제</h3>
<pre><code class="language-css">
.box {
  animation: fadeIn 2s ease-in-out infinite alternate;
}
</code></pre>
<hr>
<h2 id="🔍-step-7-animation-fill-mode">🔍 Step 7: <code>animation-fill-mode</code></h2>
<h3 id="✅-개념-6">✅ 개념</h3>
<blockquote>
<p>애니메이션 시작 전/끝난 후의 스타일을 유지할지</p>
</blockquote>
<table>
<thead>
<tr>
<th>값</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>none</code></td>
<td>애니메이션 외 상태 유지 (기본값)</td>
</tr>
<tr>
<td><code>forwards</code></td>
<td>끝난 후 마지막 상태 유지</td>
</tr>
<tr>
<td><code>backwards</code></td>
<td>시작 전 첫 상태 미리 적용</td>
</tr>
<tr>
<td><code>both</code></td>
<td>둘 다 적용</td>
</tr>
</tbody></table>
<h3 id="🧠-예제-6">🧠 예제</h3>
<pre><code class="language-css">
.box {
  animation: fadeIn 2s ease forwards;
}
</code></pre>
<p>→ 이걸 쓰지 않으면, opacity는 1이 되었다가 다시 0으로 돌아가 😅</p>
<hr>
<h2 id="🔍-step-8-animation-play-state">🔍 Step 8: <code>animation-play-state</code></h2>
<h3 id="✅-개념-7">✅ 개념</h3>
<blockquote>
<p>애니메이션을 재생할지, 일시정지할지</p>
</blockquote>
<table>
<thead>
<tr>
<th>값</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>running</code></td>
<td>실행 중</td>
</tr>
<tr>
<td><code>paused</code></td>
<td>멈춘 상태 (hover 시 일시정지 등 구현 가능)</td>
</tr>
</tbody></table>
<h3 id="🧠-예제-7">🧠 예제</h3>
<pre><code class="language-css">
.box {
  animation-play-state: paused;
}
.box:hover {
  animation-play-state: running;
}
</code></pre>
<hr>
<h2 id="✅-요약-표-정리용">✅ 요약 표 (정리용)</h2>
<table>
<thead>
<tr>
<th>속성</th>
<th>설명</th>
<th>자주 쓰는 값</th>
</tr>
</thead>
<tbody><tr>
<td>animation-name</td>
<td>애니메이션 이름</td>
<td>myAnimation</td>
</tr>
<tr>
<td>animation-duration</td>
<td>실행 시간</td>
<td><code>2s</code>, <code>500ms</code></td>
</tr>
<tr>
<td>animation-timing-function</td>
<td>속도 곡선</td>
<td><code>linear</code>, <code>ease-in-out</code></td>
</tr>
<tr>
<td>animation-delay</td>
<td>시작 지연</td>
<td><code>0s</code>, <code>1s</code></td>
</tr>
<tr>
<td>animation-iteration-count</td>
<td>반복 횟수</td>
<td><code>1</code>, <code>infinite</code></td>
</tr>
<tr>
<td>animation-direction</td>
<td>방향</td>
<td><code>normal</code>, <code>alternate</code></td>
</tr>
<tr>
<td>animation-fill-mode</td>
<td>끝난 후 상태 유지</td>
<td><code>none</code>, <code>forwards</code></td>
</tr>
<tr>
<td>animation-play-state</td>
<td>재생 상태</td>
<td><code>running</code>, <code>paused</code></td>
</tr>
</tbody></table>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[1. CSS 애니메이션의 본질]]></title>
            <link>https://velog.io/@bom_0320/CSS-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98%EC%9D%98-%EB%B3%B8%EC%A7%88</link>
            <guid>https://velog.io/@bom_0320/CSS-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98%EC%9D%98-%EB%B3%B8%EC%A7%88</guid>
            <pubDate>Thu, 24 Apr 2025 10:45:41 GMT</pubDate>
            <description><![CDATA[<h2 id="1-css-애니메이션이란">1. CSS 애니메이션이란?</h2>
<p><strong>CSS 애니메이션(Animation)</strong> 이란, </p>
<blockquote>
<p><strong>&quot;시간에 따라 요소의 스타일을 바꾸는 것&quot;</strong> 이다.</p>
</blockquote>
<p>즉, 요소를 <strong>움직이게 하거나 크기를 바꾸고, 색깔을 바꾸는 등의 동작을 자동으로 부드럽게 만들어주는 도구</strong>이다.</p>
<h2 id="2-왜-animation이-필요할까">2. 왜 animation이 필요할까?</h2>
<table>
<thead>
<tr>
<th>JavaScript로도 가능하잖아?</th>
<th>그런데 왜 CSS 애니메이션을 쓸까?</th>
</tr>
</thead>
<tbody><tr>
<td>맞아. JavaScript로도 가능해.</td>
<td>하지만 CSS는 <strong>간단하고 가볍게</strong> 쓸 수 있어.</td>
</tr>
<tr>
<td>JavaScript는 복잡한 코드 필요</td>
<td>CSS는 <code>@keyframes</code> + <code>animation</code>만 쓰면 돼</td>
</tr>
<tr>
<td>성능도 중요함</td>
<td>CSS 애니메이션은 GPU 가속이 더 잘 돼서 성능이 좋아</td>
</tr>
</tbody></table>
<p>👉 <strong>스타일 변경 중심</strong>이면 CSS 애니메이션!</p>
<p>👉 <strong>사용자 상호작용/복잡한 로직</strong>이 필요하면 JavaScript!</p>
<h2 id="3-css-애니메이션이-작동하는-원리">3. CSS 애니메이션이 작동하는 원리</h2>
<h3 id="1-애니메이션의-단계keyframes를-정의함">1. 애니메이션의 단계(keyframes)를 정의함</h3>
<pre><code class="language-css">@keyframes myAnimation {
    from { opacity: 0 }
    to { opacity: 1 }
}</code></pre>
<h3 id="2-애니메이션을-요소에-적용함">2. 애니메이션을 요소에 적용함</h3>
<pre><code class="language-css">.box {
    animation-name: myAnimation;
    animation-duration: 2s;
}</code></pre>
<h3 id="3-브라우저가-이걸-해석해서-시간에-따라-스타일을-자동으로-변화시킴">3. 브라우저가 이걸 해석해서 시간에 따라 스타일을 자동으로 변화시킴</h3>
<hr>
<h2 id="4-css-애니메이션-vs-트랜지션">4. CSS 애니메이션 vs 트랜지션</h2>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th>animation</th>
<th>transition</th>
</tr>
</thead>
<tbody><tr>
<td>설정 방식</td>
<td><code>@keyframes</code>로 여러 단계 설정</td>
<td>시작과 끝 스타일만 지정</td>
</tr>
<tr>
<td>반복 가능 여부</td>
<td><code>infinite</code>, <code>alternate</code> 등 가능</td>
<td>반복은 안 됨</td>
</tr>
<tr>
<td>복잡한 동작 가능</td>
<td>O (예: 흔들기, 회전 등)</td>
<td>제한적</td>
</tr>
<tr>
<td>예시</td>
<td>로딩 스피너, 입장 효과 등</td>
<td>버튼 호버 효과 등</td>
</tr>
</tbody></table>
<h2 id="5-시각-예제-opacity-애니메이션">5. 시각 예제: opacity 애니메이션</h2>
<pre><code class="language-html">&lt;style&gt;
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.box {
  width: 100px;
  height: 100px;
  background: pink;
  animation: fadeIn 2s ease-in-out forwards;
}
&lt;/style&gt;

&lt;div class=&quot;box&quot;&gt;&lt;/div&gt;
</code></pre>
<p><strong>👉 결과: 박스가 천천히 나타나는 느낌</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: 카드형 반응형 이미지 레이아웃 패턴 정리]]></title>
            <link>https://velog.io/@bom_0320/TIL-%EC%B9%B4%EB%93%9C%ED%98%95-%EB%B0%98%EC%9D%91%ED%98%95-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%8C%A8%ED%84%B4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@bom_0320/TIL-%EC%B9%B4%EB%93%9C%ED%98%95-%EB%B0%98%EC%9D%91%ED%98%95-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%ED%8C%A8%ED%84%B4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 21 Apr 2025 08:51:30 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-개요">✅ 개요</h2>
<p>강의에서 사용된 이미지 레이아웃 스타일이 매우 유연하고 깔끔했는데,</p>
<p>그 방식이 단순히 <code>width</code>, <code>height</code> 설정이 아니라 <code>max-width</code>, <code>min-height</code>, <code>grid</code>, <code>transition</code>, <code>opacity</code> 등을 활용한 고급 반응형 CSS 패턴이라는 것을 이해했다.</p>
<hr>
<h2 id="✅-패턴-예시-코드">✅ 패턴 예시 코드</h2>
<pre><code class="language-css">.movie {
  display: grid;
  grid-template-rows: 1fr auto;
  gap: 25px;
}

.movie img {
  max-width: 100%;
  min-height: 100%;
  object-fit: cover;
  border-radius: 20px;
  transition: opacity 0.2s ease-in-out;
  opacity: 0.7;
}

.movie img:hover {
  opacity: 1;
}

.movie a {
  text-align: center;
}
</code></pre>
<h2 id="🧠-각-속성의-의미-정리">🧠 각 속성의 의미 정리</h2>
<h3 id="🔹-movie-부모-요소">🔹 <code>.movie</code> (부모 요소)</h3>
<table>
<thead>
<tr>
<th>속성</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>display: grid</code></td>
<td>이미지와 텍스트(제목)를 위/아래로 나누기 위한 2행 그리드</td>
</tr>
<tr>
<td><code>grid-template-rows: 1fr auto</code></td>
<td>이미지가 대부분을 차지하고, 텍스트는 콘텐츠 높이만큼만</td>
</tr>
<tr>
<td><code>gap: 25px</code></td>
<td>이미지와 텍스트 사이 간격</td>
</tr>
</tbody></table>
<hr>
<h3 id="🔹-movie-img-이미지-요소">🔹 <code>.movie img</code> (이미지 요소)</h3>
<table>
<thead>
<tr>
<th>속성</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>max-width: 100%</code></td>
<td>부모 너비보다 커지지 않도록 제한 (반응형 핵심)</td>
</tr>
<tr>
<td><code>min-height: 100%</code></td>
<td>그리드 row 높이를 꽉 채움 (일정한 카드 비율 유지)</td>
</tr>
<tr>
<td><code>object-fit: cover</code></td>
<td>비율 유지하며 꽉 채움 (잘라도 중심을 유지)</td>
</tr>
<tr>
<td><code>border-radius: 20px</code></td>
<td>카드 스타일 연출용 둥근 테두리</td>
</tr>
<tr>
<td><code>opacity</code> + <code>transition</code></td>
<td>마우스 hover 시 부드러운 이미지 강조 효과 제공</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-핵심-특징">✅ 핵심 특징</h2>
<ul>
<li><strong>카드형 디자인</strong>에 적합한 레이아웃</li>
<li><strong>이미지 비율 유지</strong>, <strong>반응형 대응</strong>, <strong>미려한 시각 효과</strong></li>
<li>다양한 화면 크기에서도 <strong>레이아웃이 안정적</strong></li>
</ul>
<hr>
<h2 id="💡-실무-팁">💡 실무 팁</h2>
<ul>
<li><code>object-fit: cover</code> + <code>max-width: 100%</code> 조합은 <strong>이미지를 꽉 채우면서도 예쁘게 보여주는 필수 패턴</strong></li>
<li><code>min-height</code>는 <strong>카드 내부 요소들의 높이 균형을 맞추는 데 효과적</strong></li>
<li>이미지가 다양한 크기일 때도 레이아웃이 깨지지 않도록 설계됨</li>
</ul>
<hr>
<h2 id="💬-기억할-문장">💬 기억할 문장</h2>
<blockquote>
<p>반응형 이미지를 자연스럽게 보이게 하려면, max-width로 제어하고, object-fit으로 모양을 다듬자.</p>
</blockquote>
<hr>
<p>이 패턴은 <strong>카드, 갤러리, 영화 목록, 상품 리스트, 블로그 썸네일</strong> 등 실무에서 자주 쓰이는 고급 CSS 기법이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: width, height, max-width, min-height의 차이점 완벽 정리]]></title>
            <link>https://velog.io/@bom_0320/TIL-width-height-max-width-min-height%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EC%99%84%EB%B2%BD-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@bom_0320/TIL-width-height-max-width-min-height%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EC%99%84%EB%B2%BD-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 21 Apr 2025 08:47:57 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-개요">✅ 개요</h2>
<p>CSS에서 요소의 크기를 설정할 수 있는 다양한 속성들이 있다.</p>
<p>특히 <code>width</code>, <code>height</code> 와 <code>max-width</code>, <code>min-height</code> 는 이름이 비슷하지만 동작 방식이 매우 다르다.</p>
<p>이번에 그 차이를 실제 코드 예제와 함께 명확히 이해했다.</p>
<hr>
<h2 id="✅-각-속성의-의미">✅ 각 속성의 의미</h2>
<table>
<thead>
<tr>
<th>속성</th>
<th>의미</th>
<th>고정 여부</th>
<th>반응형 대응</th>
</tr>
</thead>
<tbody><tr>
<td><code>width</code></td>
<td>정확한 너비 설정</td>
<td>✅ 고정</td>
<td>❌</td>
</tr>
<tr>
<td><code>height</code></td>
<td>정확한 높이 설정</td>
<td>✅ 고정</td>
<td>❌</td>
</tr>
<tr>
<td><code>max-width</code></td>
<td>최대 너비 제한</td>
<td>❌ 제한만 함</td>
<td>✅</td>
</tr>
<tr>
<td><code>max-height</code></td>
<td>최대 높이 제한</td>
<td>❌ 제한만 함</td>
<td>✅</td>
</tr>
<tr>
<td><code>min-width</code></td>
<td>최소 너비 보장</td>
<td>❌ 제한만 함</td>
<td>✅</td>
</tr>
<tr>
<td><code>min-height</code></td>
<td>최소 높이 보장</td>
<td>❌ 제한만 함</td>
<td>✅</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-예제-비교">✅ 예제 비교</h2>
<h3 id="🎯-width-vs-max-width">🎯 <code>width</code> vs <code>max-width</code></h3>
<h3 id="1-width-300px">1. <code>width: 300px</code></h3>
<pre><code class="language-css">img {
  width: 300px;
}
</code></pre>
<ul>
<li>이미지 너비는 <strong>항상 300px</strong></li>
<li>부모가 더 작아도 넘쳐버릴 수 있음 → <strong>깨짐 가능성</strong></li>
</ul>
<h3 id="2-max-width-100">2. <code>max-width: 100%</code></h3>
<pre><code class="language-css">
img {
  max-width: 100%;
}
</code></pre>
<ul>
<li>이미지가 부모보다 <strong>커지지 않도록 제한</strong></li>
<li>부모가 작으면 <strong>그에 맞게 자동으로 줄어듦</strong></li>
</ul>
<hr>
<h2 id="✅-실전-코드에서의-차이">✅ 실전 코드에서의 차이</h2>
<h3 id="❌-이렇게-하면-강제로-꽉-채움">❌ 이렇게 하면 강제로 꽉 채움</h3>
<pre><code class="language-css">
.movie img {
  width: 100%;
  height: 100%;
}
</code></pre>
<ul>
<li>부모 요소 크기만큼 <strong>무조건 꽉 채움</strong></li>
<li>이미지 비율 깨질 위험 있음</li>
<li>높이도 강제로 맞추기 때문에, <strong>넘치거나 늘어지는 경우</strong> 생길 수 있음</li>
</ul>
<hr>
<h3 id="✅-이렇게-하면-유연하게-반응">✅ 이렇게 하면 유연하게 반응</h3>
<pre><code class="language-css">
.movie img {
  max-width: 100%;
  min-height: 100%;
}
</code></pre>
<ul>
<li><code>max-width</code>: <strong>부모보다 커지지 않게 제한</strong></li>
<li><code>min-height</code>: <strong>최소한 부모 높이만큼은 차지하도록 보장</strong></li>
<li>이미지의 <strong>비율도 유지되며</strong>, <strong>부드러운 반응형 UI 구성 가능</strong></li>
</ul>
<hr>
<h2 id="💬-기억-문장">💬 기억 문장</h2>
<blockquote>
<p>width는 명령, max-width는 제한, min-width는 최소한의 배려.</p>
</blockquote>
<hr>
<h2 id="🧠-언제-어떤-걸-쓰면-좋을까">🧠 언제 어떤 걸 쓰면 좋을까?</h2>
<table>
<thead>
<tr>
<th>목적</th>
<th>추천 속성</th>
</tr>
</thead>
<tbody><tr>
<td>정확한 고정 크기 필요</td>
<td><code>width</code>, <code>height</code></td>
</tr>
<tr>
<td>부모보다 커지면 안 됨</td>
<td><code>max-width</code>, <code>max-height</code></td>
</tr>
<tr>
<td>너무 작아지면 안 됨</td>
<td><code>min-width</code>, <code>min-height</code></td>
</tr>
<tr>
<td>이미지/카드 등 반응형 UI</td>
<td><code>max-width: 100%</code>, <code>height: auto</code></td>
</tr>
</tbody></table>
<hr>
<p>이번 정리를 통해 이미지, 카드, 버튼 등 다양한 요소들의 <strong>유연한 크기 제어</strong> 방법을 명확하게 이해할 수 있었다.</p>
]]></description>
        </item>
    </channel>
</rss>