<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yuna.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 25 Jul 2025 19:42:18 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yuna.log</title>
            <url>https://velog.velcdn.com/images/yuna-c/profile/134220af-c890-45da-8d7e-60c6f15a19f9/image.PNG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yuna.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yuna-c" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[기타] AI로 뉴스레터 100% 자동화_뉴스발행 2]]></title>
            <link>https://velog.io/@yuna-c/%EA%B8%B0%ED%83%80-AI%EB%A1%9C-%EB%89%B4%EC%8A%A4%EB%A0%88%ED%84%B0-100-%EC%9E%90%EB%8F%99%ED%99%94%EA%B8%B0%EC%82%AC%EC%88%98%EC%A7%91-2</link>
            <guid>https://velog.io/@yuna-c/%EA%B8%B0%ED%83%80-AI%EB%A1%9C-%EB%89%B4%EC%8A%A4%EB%A0%88%ED%84%B0-100-%EC%9E%90%EB%8F%99%ED%99%94%EA%B8%B0%EC%82%AC%EC%88%98%EC%A7%91-2</guid>
            <pubDate>Fri, 25 Jul 2025 19:42:18 GMT</pubDate>
            <description><![CDATA[<h1 id="🌍챗gpt--make로-뉴스레터-자동화">🌍챗GPT + Make로 뉴스레터 자동화</h1>
<h2 id="📌-구글-시트-불러오기">📌 구글 시트 불러오기</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/d8f75118-1ad2-4180-9da6-c51024ddf40a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/fc84623e-b6dc-43ee-866e-d4955bd7f3fd/image.png" alt="">
필터를 걸어 5점 이상 뉴스만 가져오기</p>
<h2 id="📌-text-aggregatortool">📌 Text aggregator(Tool)</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/bff874d7-06f3-465f-884f-db693dfd180d/image.png" alt=""></p>
<p>텍스트를 하나로 합칠 때 쓰는 Tool</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/5fb7c9cc-90ec-4a10-8e94-f63db4b4acd9/image.png" alt="">
불러올 값 + {{newline}}</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/86f1b43e-738c-4148-beab-6664b93b15db/image.png" alt="">
6점 이상의 기사들을 모아 하나로 합쳐줌</p>
<h2 id="📌-markdown">📌 MarkDown</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/c4b75e48-45cc-4747-9ad6-2b7b5214f116/image.png" alt="">
마크다운을 HTML로 바꿈
으로 처리하려 하였으나 </p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/e5c67787-26fe-461b-891e-e2d11697e323/image.png" alt="">
html 문서 양식을 적용하지 않고 태그만 받아오는 문제가 발생 됬다</p>
<p><code>###</code> 세개는 <code>H3</code>이다</p>
<h2 id="📌-make-a-request-html-변환">📌 Make a request HTML 변환</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/653d9d9e-933f-4266-b4cc-07960b244a91/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/11ae93b9-a30c-4c7d-a827-e8ff3690b7e7/image.png" alt=""></p>
<p>엑셀 파일의 메일 요약을 <a href="https://app.kit.com/">Kit</a> 의 Broadcasts로 데이터를 삽입하기 위해 
<a href="https://developers.kit.com/api-reference/broadcasts/create-a-broadcast">Kit developers</a> 사이트의 Post 부분을 참고하여 작성하였는데 
처음 실수는 </p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/e75ac674-0459-4fae-9017-6f09f36c3a97/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/6173aee8-f806-4ec1-86b3-638ffafb1162/image.png" alt="">
형식을 html 방식으로 변경하여주는 지티피를 이어 준다</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/27e6120c-50aa-4934-8b90-8d9c956f4f00/image.png" alt="">
JSON Object로 파싱을 yes 클릭해준 후 저정</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/a29127b5-5b53-4226-8383-d0d6dc7905f8/image.png" alt=""></p>
<pre><code class="language-JSON">{
  &quot;email_template_id&quot;: null,
  &quot;email_address&quot;:null,
  &quot;content&quot;: &quot;{{17.result.html}}&quot;,
  &quot;description&quot;: &quot;AI 뉴스레터 초안&quot;,
  &quot;public&quot;: true,
  &quot;published_at&quot;: &quot;2025-07-26T06:43:55-05:00&quot;,
  &quot;send_at&quot;: null,
  &quot;thumbnail_alt&quot;: null,
  &quot;thumbnail_url&quot;: null,
  &quot;preview_text&quot;: &quot;안녕하세요. AI 뉴스레터 입니다.&quot;,
  &quot;subject&quot;: &quot;AI 뉴스레터에 오신 것을 환영합니다.&quot;
}
</code></pre>
<p>뉴스레터 초안을 작성 시킨 후 저장하면 </p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/fddbb684-0760-416c-a568-e2dedf4df964/image.png" alt=""></p>
<h2 id="📌-결과">📌 결과</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/b543f89c-eca6-4407-bb14-2a7cc4d4cf3e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/1426229e-98cd-40d0-ba14-d48b9443eb32/image.png" alt=""></p>
<p>초안이 작성된 것이 보인다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기타] AI로 뉴스레터 100% 자동화_기사수집 1]]></title>
            <link>https://velog.io/@yuna-c/%EA%B8%B0%ED%83%80-AI%EB%A1%9C-%EB%89%B4%EC%8A%A4%EB%A0%88%ED%84%B0-100-%EC%9E%90%EB%8F%99%ED%99%94</link>
            <guid>https://velog.io/@yuna-c/%EA%B8%B0%ED%83%80-AI%EB%A1%9C-%EB%89%B4%EC%8A%A4%EB%A0%88%ED%84%B0-100-%EC%9E%90%EB%8F%99%ED%99%94</guid>
            <pubDate>Fri, 25 Jul 2025 16:05:30 GMT</pubDate>
            <description><![CDATA[<h1 id="🌍챗gpt--make로-뉴스레터-자동화">🌍챗GPT + Make로 뉴스레터 자동화</h1>
<h2 id="📌-해외-ai-뉴스레터-탐방">📌 해외 AI 뉴스레터 탐방</h2>
<blockquote>
</blockquote>
<p><a href="https://openai.com/ko-KR/news/">OpenAI Blog</a> : ChatGPT, GPT 모델 관련 공식 뉴스
<a href="marktechpost.com">MarkTechPost</a> : AI 미디어 플랫폼으로, 최신 AI/ML/DL 관련 뉴스·튜토리얼·리서치 요약 콘텐츠
<a href="https://research.google/blog/">Google Research Blog</a> : Google이 자사 연구 결과, AI/ML 기술 진보, 연구 논문 발표 등을 공유
<a href="https://news.mit.edu/topic/artificial-intelligence2">MIT News - Artificial intelligence RSS Feed</a> : MIT에서 운영하는 기술 전문 매체</p>
<h2 id="📌-rss-란">📌 RSS 란?</h2>
<ul>
<li><p><strong>RSS(Really Simple Syndication)</strong>
웹사이트(뉴스, 블로그 등)의 새 글을 자동으로 받아볼 수 있게 해주는 기술</p>
</li>
<li><p><strong>RSS 피드 (Feed)</strong>
새로운 글/콘텐츠 목록이 정리돼 있는 데이터</p>
</li>
<li><p><strong>RSS 리더</strong>
여러 피드를 모아서 보여주는 앱 또는 사이트 (ex. Feedly)</p>
</li>
<li><p><strong>확장자</strong>
보통 .xml 형식 URL (예: <a href="https://example.com/rss.xml">https://example.com/rss.xml</a>)</p>
</li>
</ul>
<p>아래의 HTML을 기반으로 GPT로 요약하거나, 슬랙/뉴스레터로 보내는 자동화</p>
<pre><code class="language-Html">&lt;rss version=&quot;2.0&quot;&gt;
  &lt;channel&gt;
    &lt;title&gt;My AI News&lt;/title&gt;
    &lt;item&gt;
      &lt;title&gt;OpenAI releases GPT-5&lt;/title&gt;
      &lt;link&gt;https://openai.com/blog/gpt-5&lt;/link&gt;
      &lt;pubDate&gt;Thu, 25 Jul 2025 10:00:00 GMT&lt;/pubDate&gt;
      &lt;description&gt;GPT-5 is here and it&#39;s multimodal...&lt;/description&gt;
    &lt;/item&gt;
  &lt;/channel&gt;
&lt;/rss&gt;</code></pre>
<h2 id="📌-rss-feed-유무-체크">📌 RSS Feed 유무 체크</h2>
<p>예시 사이트
<img src="https://velog.velcdn.com/images/yuna-c/post/2b10890b-37e8-42a1-b5b5-ceaa6fc48ccb/image.png" alt=""></p>
<p><strong>RSS TOP 100</strong>
<a href="https://rss.feedspot.com/ai_rss_feeds/">https://rss.feedspot.com/ai_rss_feeds/</a></p>
<blockquote>
<ul>
<li>openai : <a href="https://openai.com/news/rss.xml">https://openai.com/news/rss.xml</a></li>
<li>MarkTechPost : <a href="https://www.marktechpost.com/feed/">https://www.marktechpost.com/feed/</a></li>
<li>Google Research Blog : <a href="https://research.google/blog/rss/">https://research.google/blog/rss/</a></li>
<li>MIT News : <a href="https://news.mit.edu/rss/topic/artificial-intelligence2">https://news.mit.edu/rss/topic/artificial-intelligence2</a></li>
</ul>
</blockquote>
<p><strong>그외 자료</strong></p>
<table>
<thead>
<tr>
<th>이름</th>
<th>특징</th>
<th>RSS</th>
</tr>
</thead>
<tbody><tr>
<td><strong>MarkTechPost</strong></td>
<td>AI 논문 요약, 최신 툴 소개 많음. 연구 + 실무 사이 포지션.</td>
<td>✅ <a href="https://www.marktechpost.com/feed">RSS</a></td>
</tr>
<tr>
<td><strong>Google Research Blog</strong></td>
<td>구글의 AI 연구 결과 공식 발표. Gemini, PaLM 등 다 여기서 나옴.</td>
<td>✅ <a href="https://research.google/blog/rss">RSS</a></td>
</tr>
<tr>
<td><strong>OpenAI Blog</strong></td>
<td>ChatGPT, GPT 모델 관련 공식 뉴스</td>
<td>✅ <a href="https://openai.com/feed.xml">RSS</a></td>
</tr>
<tr>
<td><strong>Meta AI Blog</strong></td>
<td>LLaMA, SeamlessM4T 등 Meta의 AI 연구 소개</td>
<td>✅ 없음 (스크래핑 필요)</td>
</tr>
<tr>
<td><strong>Hugging Face Blog</strong></td>
<td>오픈소스 LLM, Transformers, datasets 등 소식</td>
<td>✅ 없음 (하지만 GitHub RSS 활용 가능)</td>
</tr>
<tr>
<td><strong>The Decoder</strong></td>
<td>AI 업계 뉴스 + 논문 리뷰 + 기업 분석까지 한눈에</td>
<td>✅ <a href="https://the-decoder.com/feed/">RSS</a></td>
</tr>
<tr>
<td><strong>TechCrunch AI</strong></td>
<td>스타트업, AI 비즈니스 트렌드 위주 (OpenAI 투자, xAI 등)</td>
<td>✅ <a href="https://techcrunch.com/tag/artificial-intelligence/feed/">RSS</a></td>
</tr>
<tr>
<td><strong>VentureBeat AI</strong></td>
<td>B2B 중심 AI 뉴스 + 분석</td>
<td>✅ <a href="https://venturebeat.com/category/ai/feed/">RSS</a></td>
</tr>
<tr>
<td><strong>Synced Review</strong></td>
<td>최신 논문 + 산업 적용 사례</td>
<td>❌ RSS 없음 (웹 크롤링 필요)</td>
</tr>
<tr>
<td><strong>MIT Technology Review – AI</strong></td>
<td>기술·사회적 영향까지 폭넓게 다룸</td>
<td>✅ <a href="https://www.technologyreview.com/feed/">RSS</a> ← 전체 피드지만 AI 카테고리 많음</td>
</tr>
</tbody></table>
<h2 id="📌-workflow">📌 Workflow</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/05380510-6919-49a5-acf8-05f9bdc3dbb5/image.png" alt=""></p>
<h2 id="📌-rss-설정">📌 RSS 설정</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/765963de-3c2a-4ddf-bd53-653b0469fac4/image.png" alt="">
add Day에서 지금으로 부터 7일 전 뉴스를 10개 씩 받아오고  </p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/79b311eb-6960-44ef-8cf7-d1c4d322c06c/image.png" alt="">
Run Onec를 눌러 뉴스 받아와 지는지 OUTPUT 부분을 확인 한 후, </p>
<h2 id="📌-구글-시트-저장-raw-data">📌 구글 시트 저장 (Raw Data)</h2>
<p><a href="https://workspace.google.com/intl/ko/products/sheets/">구글 엑셀 시트</a></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/351fa82f-319a-41ca-a937-607d1b47897f/image.png" alt="">
미리 만들어놓은 구글 시트에 연결할 이름을 지정하고 로그인을 한다 </p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/a3836624-ac73-4af3-8b1d-e9896a2be0e4/image.png" alt="">
<img src="https://velog.velcdn.com/images/yuna-c/post/ba9d42d7-54b6-4bfd-a552-20ad45c156d2/image.png" alt=""></p>
<p>그 후 내 시트를 가져와 시트에 정의해 둔 Title, URL등을 받아와 RSS의 title, URL과 매핑 한다</p>
<h2 id="📌-basic-triger-생성">📌 Basic Triger 생성</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/ad9be179-c700-4b30-9743-708d75f90a40/image.png" alt="">
Tools의 Basic Triger에 아무 내용이나 적고 실행 돌려본다</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/1eea6ac4-b8f8-4fc7-a6cb-0024ab2fd7f7/image.png" alt=""></p>
<p>단방향이라 세 자료를 취합하는게 어려워 Basic Triger 생성 하는 것
(꼼수_각자 받아와 지는지 확인을 위한)</p>
<h2 id="📌-데이터-확인">📌 데이터 확인</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/6c648487-478d-4068-bcae-f46b947bff36/image.png" alt="">
트리거에 이어 RUN을 하면 하나씩 받아와 지는 걸 볼 수 있다</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/ce3433b2-b392-4bc8-9559-eabacf658d93/image.png" alt="">
URL을 가지고오기 위해 HTTP get file 선택</p>
<h2 id="📌-text-parser">📌 Text parser</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/0a162ac2-0e76-4bb2-a4c7-6bb6c5d2f317/image.png" alt="">
Text Parser Html to text로 덱스트 추출</p>
<h2 id="📌-open-ai-keyid">📌 Open ai key/id</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/17009849-df2a-4a4d-908d-2efab58ebf20/image.png" alt="">
OpenAI (ChatGPT, Whisper, DALL-E) 선택 후 로그인 하여 key와 id를 가져와 붙여 넣어준다</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/c346e5f0-5330-4f73-993f-f1b823b16715/image.png" alt="">
뉴스 값을 세팅해주고 요약본의 대본도 적어준다</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/c3313a11-1f49-46e6-8cfb-cfde86cc4184/image.png" alt=""></p>
<p>응답 받는 포맷을 JSON으로</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/5bc6b9c4-0205-4646-a9a9-22e54e5f9cbd/image.png" alt=""></p>
<h2 id="📌-ai-기사-필터링">📌 AI 기사 필터링</h2>
<p>ai기사인지 아닌지 판별하는 openAI를 붙여서 중간에 트루인지 아닌지 필터링을 건다</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/2f44ae29-2d7b-46a3-bb93-26918f2df4ab/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/e06f1c82-aaea-45f9-bedd-4b98d98faf82/image.png" alt="">
AI 기사 판단 OpenAi</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/feb0df55-0724-4013-a173-356e1c4696c4/image.png" alt="">
AI 판단 필터</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/43fc253b-38c2-4b2b-9b32-cc6bc27a3d54/image.png" alt="">
AI 판단 후 시트 저장 </p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/c999c330-9c38-4ef7-974b-5baf42dbe38a/image.png" alt="">
true/false 판가름 된 시트</p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/a5930493-574d-4b8f-8c60-f1f2101fdecd/image.png" alt="">
Number(스코어), boolean(참/거짓), String(문자열) 은 모두 parse JSON Response를 yes
로 설정해야 하며, 값 처럼 
Number(스코어), boolean(참/거짓)은 &quot;&quot;를 생략해야 한다.</p>
<p><strong>데이터를 넣을때 마다 구글 시트로 돌려 확인하면 result 뒤의 변수들이 자동 생성됨</strong></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/3de0bab3-676e-43c8-ac50-6ac3399d2e37/image.png" alt="">
기사 점수 넣기</p>
<h2 id="📌-뉴스-수집-완성">📌 뉴스 수집 완성</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/70d69891-1028-4b38-8651-966e5f0a96a0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/ba155c91-37e0-47cc-84b3-b7e349dc77bb/image.png" alt=""></p>
<p> <a href="https://www.youtube.com/watch?v=eLKdo-UiLPM&amp;t=72s">출처 : AI로 뉴스레터 100% 자동화하는 방법 (챗GPT + Make)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 기술면접_CS 2]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91CS-2</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91CS-2</guid>
            <pubDate>Wed, 23 Jul 2025 09:17:47 GMT</pubDate>
            <description><![CDATA[<h1 id="✨프로세스와-스레드의-차이">✨프로세스와 스레드의 차이</h1>
<h3 id="✅-프로세스와-스레드-정의">✅ 프로세스와 스레드 정의</h3>
<p><strong>프로세스(Process)</strong></p>
<blockquote>
<p>운영체제로부터 자원을 할당받는 독립적인 실행 단위</p>
</blockquote>
<p><strong>스레드(Thread)</strong></p>
<blockquote>
<p>하나의 프로세스 안에서 실제로 작업을 수행하는 실행 흐름</p>
</blockquote>
<h3 id="✅-자원-구조-차이">✅ 자원 구조 차이</h3>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/09f40b0a-b229-420d-8e8e-007a6776506e/image.png" alt=""></p>
<ul>
<li>하나의 프로세스 안에는 여러 스레드가 있을 수 있음</li>
<li>스레드들은 Code / Data / Heap 영역을 공유,
단, Stack 영역은 각자 따로 가짐</li>
</ul>
<h3 id="✅-핵심-요약">✅ 핵심 요약</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>프로세스</th>
<th>스레드</th>
</tr>
</thead>
<tbody><tr>
<td>실행 단위</td>
<td>독립적인 실행 단위</td>
<td>프로세스 내부의 실행 흐름 단위</td>
</tr>
<tr>
<td>메모리</td>
<td>독립적 (자원 별도 할당)</td>
<td>프로세스 자원 공유</td>
</tr>
<tr>
<td>안정성</td>
<td>하나 죽어도 다른 영향 X</td>
<td>하나 죽으면 프로세스 전체 영향 가능</td>
</tr>
<tr>
<td>생성 비용</td>
<td>높음</td>
<td>낮음</td>
</tr>
</tbody></table>
<h3 id="🧠-언제-써먹냐">🧠 언제 써먹냐?</h3>
<blockquote>
<p><strong>멀티 프로세싱</strong> : 독립된 작업 → 안정성 우선
<strong>멀티 스레딩</strong> : 빠른 처리, 자원 공유 → 성능 우선</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 기술면접_CS 1]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91CS-1</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91CS-1</guid>
            <pubDate>Wed, 23 Jul 2025 06:46:21 GMT</pubDate>
            <description><![CDATA[<h1 id="✨객체-지향-프로그래밍">✨객체 지향 프로그래밍</h1>
<h3 id="✅-객체지향의-정의">✅ <strong>객체지향의 정의</strong></h3>
<blockquote>
<p>객체지향프로그래밍(OOP, Object Oriented Programming)은 컴퓨터 프로그래밍 패러다임 중 하나로, 데이터를 추상화시켜 상태(attribute)와 행위(Method)를 가진 객체로 만들고 그 객체들 간의 상호작용을 통해 로직을 구성하는 프로그래밍 방법</p>
</blockquote>
<h3 id="✅-객체지향의-장단점">✅ <strong>객체지향의 장단점</strong></h3>
<ul>
<li>장점 : 코드 재사용 및 유지보수 용이, 대형 프로젝트에 적합</li>
<li>단점 : 처리속도 느림, 설계 시 많은 시간이 필요, 객체가 많을 시 용량이 커짐</li>
</ul>
<h3 id="✅-객체지향의-특성">✅ <strong>객체지향의 특성</strong></h3>
<ol>
<li>캡슐화 : 변수와 함수를 하나의 단위로 묶는 것, 정보 은닉</li>
<li>상속화 : 이미 정의된 상위 클래스의 모든 속성과 연산을 하위 클래스가 물려받는 것</li>
<li>추상화 : 객체들의 공통적인 특징을 도출하는 것</li>
<li>다형성 : 하나의 변수 또는 함수가 상황에 따라 다른 의미로 해석될 수 있는 것</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 기술면접_JS (3) ]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%95%A4%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%913-JS</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%95%A4%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%913-JS</guid>
            <pubDate>Wed, 23 Jul 2025 06:26:20 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yuna-c/post/42af79c7-45f3-4512-bf84-a38e9e87fc11/image.PNG" alt=""></p>
<h1 id="js">JS</h1>
<p><strong>11. 자바스크립트에서 동기와 비동기의 차이에 대해 설명해주세요.</strong></p>
<p>비동기 프로그래밍에 대해 이해하고 있는지 확인하는 질문.
추가로 둘을 비교한 예시</p>
<blockquote>
<p>자바스크립트에서 동기는 작업을 순서대로 실행하며, 한 작업이 끝나야 다음 작업을 실행하는 것을 뜻합니다. 반면에 비동기 처리는 한 작업이 완료되기까지 기다리지 않고 바로 다음 작업을 실행할 수 있어 더 효율적으로 리소스를 활용할 수 있습니다. 비동기 프로그래밍은 주로 네트워크 요청, 파일 IO와 같이 시간이 얼마나 걸릴지 알 수 없는 작업을 처리할 때 필요합니다. (+나만의 예시 추가: 식당에서 사장님이 그릇을 치우려고 손님이 음식을 다 먹을 때까지 옆에서 기다리기만 해서는 안 되겠죠?)</p>
</blockquote>
<p><strong>12. 이벤트 루프와 실행 컨텍스트에 대해서 설명해주세요.</strong></p>
<p>자바스크립트의 기본 원리와 함께 기술 면접 대표 질문에 대해 준비해왔는지 확인하는 질문.
그림으로 설명하는 게 더 좋은 내용이라 제스처를 넣어 설명해도 좋습니다. 스스로 대답할 때도 그림을 설명하듯이 상상해서 해주세요.</p>
<blockquote>
<p>우선 이벤트 루프는 그 이름답게 이벤트를 순서대로 돌아가며 처리하는 역할을 합니다. 마치 줄을 서있는 것처럼 이벤트 루프는 큐에 담긴 이벤트를 하나하나 호출 스택으로 옮깁니다. 물론 실행 중인 컨텍스트가 없어 호출 스택이 비어있을 때만 말입니다.
        그리고 실행 컨텍스트는 이러한 이벤트가 실행되는 환경을 뜻합니다. 앞서 말한 호출 스택에 이러한 실행 컨텍스트를 쌓아두고 작업이 종료되면 스택에서 빠져나오게 됩니다.
        이러한 이벤트 루프와 실행 컨텍스트는 싱글 스레드 환경인 자바스크립트에서 비동기 작업을 효과적으로 처리하기 위해 필수적인 메커니즘입니다.</p>
</blockquote>
<p><strong>13. 클로저에 대해 설명해주세요.</strong></p>
<p>어려운 개념인 클로저에 대한 지식을 가지고 있는지 확인하는 질문</p>
<blockquote>
<p>자바스크립트에서는 작업이 종료되어 호출 스택에서 빠져나오게 되면 참조가 사라져 가비지 컬렉터에 의해 메모리에서 사라지게 됩니다. 그런데 호출 스택에서 빠져나왔지만 특정 행동에 의해서 참조가 사라지지 않았다면 어떻게 될까요? 이 객체는 아무도 접근할 수는 없지만 메모리에 존재는 하는, 일종의 유령이 되는 겁니다. 이러한 특징을 활용하여 데이터 은닉화 및 캡슐화를 구현할 수 있습니다. 다만 남용할 경우 메모리의 누수를 피할 수 없다는 단점도 있습니다.</p>
</blockquote>
<p><strong>14. html에서 자바스크립트 코드를 로딩하는 <code>script</code> 태그는 보통 <code>body</code> 태그의 최하단에 위치하는데요. 그 이유가 무엇일까요?</strong></p>
<p>브라우저의 로딩 방식과 DOM 트리 구축 등 렌더링 과정에 대한 지식이 있는지 확인하는 질문
<code>module</code>과 <code>defer</code> 속성에 대해서도 언급</p>
<blockquote>
<p>그 첫 번째 이유는 페이지의 로딩 속도 최적화 때문입니다. 브라우저는 html을 읽어내려가다 <code>script</code> 태그를 만나게 되면 해당 자바스크립트 코드를 읽고 실행하기 시작합니다. 만약 <code>script</code> 태그가 상단에 있다면 DOM 트리 구축 시기가 늦어져 페이지의 로딩이 느려질 수 있습니다.
        여기에 이어 두 번째 이유는 DOM 접근시에 오류가 생길 수 있기 때문입니다. 앞서 자바스크립트가 먼저 읽히고 실행될 수도 있다고 말씀드렸죠? 그런데 만약 자바스크립트 코드에 <code>document.querySelector</code>처럼 html 태그를 가져오는 코드가 있다면 어떻게 될까요? 아직 DOM 트리가 구축되지 않았으므로 원하는 태그를 가져오지 못하고 <code>undefined</code>를 반환하게 됩니다. 이러한 이유로 <code>script</code> 태그를 body의 최하단에 넣곤 합니다.
        물론 다른 방법도 있는데요. 바로 <code>script</code> 태그에 <code>defer</code>나 <code>module</code>을 작성하는 것입니다. 그러면 자동으로 <code>script</code> 태그를 지연 로딩할 수 있습니다.</p>
</blockquote>
<p><strong>15. 여러 비동기 함수를 동시에 병렬로 호출하기 위해서는 어떻게 해야 할까요?</strong></p>
<p>Promise에 대한 이해도를 확인하는 질문
<code>Promise.all()</code>을 언급</p>
<blockquote>
<p>보통 async/await를 사용해 데이터를 불러오게 되면 순차적으로 데이터를 불러오기 마련입니다. 이때 <code>Promise.all()</code> 함수를 사용한다면 함수의 인자로 넘겨준 비동기 함수를 병렬로 실행할 수 있습니다. 예를 들어 3초의 실행 시간을 가진 비동기 함수 3개가 있다고 한다면, 기본적으로 순차적으로 호출할 시에 모든 함수가 끝나기까지 3 * 3 = 9초가 걸리게 됩니다. 하지만 이걸 병렬로 호출하게 된다면 3개의 요청을 동시에 보내게 되어 3초만 걸리게 됩니다.</p>
</blockquote>
<p><strong>16. Promise를 사용해 함수의 타임아웃 기능을 구현하려면 어떤 메서드를 사용해야 할까요?</strong></p>
<p>Promise에 대한 이해도를 확인하는 질문
<code>Promise.race()</code>를 언급</p>
<blockquote>
<p><code>Promise.race()</code>를 사용하면 됩니다. 이 함수의 기능은, 인자에 비동기 함수를 넣어주게 되면 넣어준 함수들 중 가장 빠르게 resolve되는 함수의 결과만을 가져오게 됩니다. 그러면 여기에 내가 사용할 비동기 함수와, <code>setTimeout</code>을 함께 넣어주게 된다면 비동기 함수와 <code>setTimeout</code> 둘 중 먼저 끝나는 쪽을 반환해주게 됩니다. 만약 <code>setTimeout</code>을 3초로 정해둔다면 3초 안에 비동기 함수가 resolve 되지 않을 시 타임 아웃 판단을 내리도록 구현할 수도 있습니다.</p>
</blockquote>
<p><strong>17. 자바스크립트의 기본 문법인 변수, 상수, 데이터 타입, 연산자, 함수 등을 이해하고 적용할 수 있습니다.</strong></p>
<p>지원자가 자바스크립트의 기본적인 문법과 개념을 이해하고 있는지 확인하기 위함.
지원자가 변수 선언 및 할당, 데이터 타입 식별, 다양한 연산자 사용, 함수 작성 및 호출에 대해 얼마나 잘 알고 있는지 평가.</p>
<p>변수 선언 시 var, let, const의 차이를 명확히 설명하세요.
자바스크립트의 기본 데이터 타입(숫자, 문자열, 불리언, null, undefined, 객체)에 대해 언급하세요.
기본적인 산술, 논리, 비교, 삼항 연산자를 사용하는 예제를 준비하세요.
함수 선언 방식(함수 선언식, 함수 표현식, 화살표 함수)에 대해 설명하세요.</p>
<blockquote>
<ul>
<li>변수 선언: let과 const는 블록 스코프를 가지며, var는 함수 스코프를 가집니다. const는 재할당이 불가능합니다.</li>
</ul>
</blockquote>
<ul>
<li>데이터 타입: 자바스크립트에는 숫자, 문자열, 불리언, null, undefined, 객체 타입이 있습니다.<ul>
<li>산술, 논리, 비교, 삼항 연산자의 사용법을 잘 이해하고 있습니다.</li>
<li>함수 선언 방식으로는 함수 선언식, 함수 표현식, 화살표 함수가 있습니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 피자 나눠 먹기 (2)]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%94%BC%EC%9E%90-%EB%82%98%EB%88%A0-%EB%A8%B9%EA%B8%B02</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%94%BC%EC%9E%90-%EB%82%98%EB%88%A0-%EB%A8%B9%EA%B8%B02</guid>
            <pubDate>Wed, 23 Jul 2025 05:29:13 GMT</pubDate>
            <description><![CDATA[<h2 id="✨-피자-나눠-먹기-2">✨ 피자 나눠 먹기 (2)</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/2306ffad-9649-4b4b-a582-11a8b1340c31/image.png" alt=""></p>
<h2 id="나의-풀이">나의 풀이</h2>
<h3 id="✅-문제-요약">✅ 문제 요약</h3>
<p><code>n</code>명이 남김 없이 같은 조각의 피자를 먹는 최소의 판 수 구하기 </p>
<pre><code class="language-javascript">function solution (n) {
    for(let i = 1; i &lt;= 100; i++){
        if((6 * i) % n === 0){
            return i
        }
    }
}</code></pre>
<p><code>i</code>는 피자의 판 수 (각 판은 6조각 → 총 6 * i 조각)
<code>(6 * i) % n === 0</code>인 순간이 각자 <strong>공평하게 나눠먹을 수 있는 최소 판 수</strong></p>
<h3 id="✅-시간복잡도">✅ 시간복잡도</h3>
<p>최대 100까지 순회 =&gt; 최악의 경우 O(100) = O(1) : 상수시간
루프는 고정 범위 내에 돌아가므로 효율적</p>
<p>👉 총 시간복잡도: O(1) : 상수 시간</p>
<h3 id="✅-공간복잡도">✅ 공간복잡도</h3>
<p>변수 <code>i</code> 하나만 사용 → <code>O(1)</code></p>
<p>👉 총 공간복잡도: <code>O(1)</code></p>
<h3 id="✅-개선-포인트-메모리-최적화">✅ 개선 포인트 (메모리 최적화)</h3>
<ul>
<li>실제론 100까지 돌릴 필요 없이, LCM(6, n) / 6 으로 바로 구할 수 있음</li>
<li>최소공배수(Lowest Common Multiple) 개념 사용하면 더 간결하게 가능</li>
</ul>
<pre><code class="language-javascript">function solution(n) {
  function 최대공약수(a, b) {
    if (b === 0) {
      return a;
    } else {
      return 최대공약수(b, a % b); // 나머지
    }
  }

  function 최소공배수(a, b) {
    return (a * b) / 최대공약수(a, b);
  }

  return 최소공배수(6, n) / 6;
}</code></pre>
<pre><code class="language-javascript">function solution(n) {
  const 최대공약수 = (a, b) =&gt; b ? 최대공약수(b, a % b) : a;
  const 최소공배수 = (a, b) =&gt; (a* b) /최대공약수(a, b);
  return 최소공배수(6, n) / 6;
}</code></pre>
<p>📌 해석 : <code>6조각</code>짜리 피자를 <code>n명</code>이 <strong>공평하게 나눌 수 있는 최소 공배수</strong>를 구한뒤 
그걸 <code>6으로 나눈 값</code>이 최소 판 수</p>
<h3 id="✅-결론">✅ 결론</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>기본 반복문 풀이</th>
<th>수학 최적화 풀이</th>
</tr>
</thead>
<tbody><tr>
<td>시간복잡도</td>
<td>O(1) (최대 100번 루프)</td>
<td>O(log n) (gcd 사용)</td>
</tr>
<tr>
<td>공간복잡도</td>
<td>O(1)</td>
<td>O(1)</td>
</tr>
<tr>
<td>가독성</td>
<td>간단</td>
<td>수학 지식이 있으면 더 간단</td>
</tr>
<tr>
<td>확장성</td>
<td>낮음</td>
<td>높음 (6조각 외 다른 문제 확장 쉬움)</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 기술면접_JS(2)]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%95%A4%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%912-JS</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%95%A4%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%912-JS</guid>
            <pubDate>Mon, 21 Jul 2025 20:06:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yuna-c/post/42af79c7-45f3-4512-bf84-a38e9e87fc11/image.PNG" alt=""></p>
<h1 id="js">JS</h1>
<p><strong>1. 변수를 선언할 때 쓰는 var, let, const의 차이에 대해 알려주세요.</strong></p>
<p> 자바스크립트 기본 지식, 스코프에 대한 이해도, 추가로 호이스팅에 대한 지식을 가지고 있는지 확인하는 질문.</p>
<blockquote>
<p>각각 순서대로 설명드리겠습니다.
<code>var</code>의 경우에는 함수 스코프를 가지며, 초기화 전에 접근하면 호이스팅 덕분에 <code>undefined</code>를 반환합니다.
es6에서 추가된 <code>const</code>와 <code>let</code>의 경우에는 블록 스코프를 가지며 호이스팅은 일어나지만 초기화 전에 접근 시 오류가 발생합니다.
이 중 <code>const</code>와 <code>let</code>은 초기화 이후 재할당이 가능하냐의 의미에서 다시 갈라집니다. <code>const</code>는 상수라는 뜻의 <code>constant</code>의 약자로 초기화 이후 재할당이 불가능합니다. 따라서 코드의 예측 가능성을 높여주는 장점이 있다고 할 수 있습니다.
현재에는 블록 스코프를 가졌다는 점과 유지 보수가 용이하다는 점 때문에 <code>const/let</code>만을 사용하는 편입니다.</p>
</blockquote>
<ul>
<li>var: 예전 방식. 함수 안에서만 유효함. 블록 {}을 무시하고 밖에서도 보임. 선언 전에 써도 undefined로 나옴. (호이스팅 됨)</li>
<li>let: 요즘 방식. 블록 안에서만 유효함. 선언 전에 쓰면 에러.</li>
<li>const: let이랑 비슷하지만, 한 번 값 주면 못 바꿈.</li>
</ul>
<blockquote>
<p>💡 지금은 var 거의 안 쓰고, let이나 const만 씀. 바꿀 일이 없으면 const, 바뀔 수 있으면 let.</p>
</blockquote>
<p><strong>2. 쓰로틀링과 디바운싱의 개념과 사용하는 이유 및 대표적인 사용처에 대해서 설명해주세요.</strong></p>
<p>성능 최적화 및 각 개념을 어디에 적용하면 좋을지 확인하는 질문.
마지막에 정리하는 말을 넣어주셔도 좋습니다. 사용한 경험에 대해서도 언급하면 좋습니다.</p>
<blockquote>
<p>두 개념 모두 특정 함수의 실행 빈도를 조절하는 기술이지만 서로의 차이점 때문에 사용처가 다릅니다.
먼저 쓰로틀링의 경우에는 일정 간격마다 함수를 실행하도록 횟수를 제한하는 기술입니다. 예를 들어 스크롤 이벤트처럼 순식간에 여러 번 실행되는 이벤트는 성능 저하를 일으키기 쉬운데요. 여기에 쓰로틀링을 적용해주면 스크롤할 때마다가 아닌 설정한 간격마다 함수를 실행하기에 불필요한 호출을 줄여 성능을 향상시킬 수 있습니다.
그리고 디바운싱은 연속된 함수의 호출이 들어올 경우 무시하고 있다가 제일 마지막에 호출된 함수만을 실행하도록 하는 기술입니다. 대표적인 사용처는 검색어 자동 완성입니다. 유저의 인풋 중간 결과는 중요하지 않기에 전부 스킵해버리고, 마지막 결과 값에 대해서만 자동 완성 결과를 보여주면 불필요한 검색 요청을 줄여 성능을 향상시킬 수 있습니다.
다시 말해 지속된 이벤트의 호출에서 중간 호출도 중요하면 쓰로틀링을, 마지막 호출만이 중요하면 디바운싱을 사용하는 것이 좋습니다.</p>
</blockquote>
<p>둘 다: 함수를 너무 자주 실행하는 걸 막는 기술.</p>
<ul>
<li>쓰로틀링: 일정 시간마다 한 번만 실행.
⏱️ 예: 스크롤 이벤트 (스크롤할 때마다 무조건 실행 ❌)</li>
<li>디바운싱: 마지막 동작만 실행.
⌨️ 예: 검색창 타이핑 → 다 치고 나서만 자동완성 실행.</li>
</ul>
<blockquote>
<p>🔁 계속 일어나는 이벤트면 → 쓰로틀링,
🕒 마지막 입력만 중요하면 → 디바운싱</p>
</blockquote>
<p><strong>3. 자바스크립트의 호이스팅에 대해 설명해주세요.</strong></p>
<p>호이스팅에 대해 이해하고 있고, 어떤 오류가 호이스팅 때문인가 구분할 수 있는지 확인하는 질문.</p>
<blockquote>
<p>호이스팅의 영어 뜻은 바로 끌어올리다입니다. 그렇기에 자바스크립트의 호이스팅도 끌어올린다는 의미인데요. 바로 코드 실행 전에 변수 선언과 함수 선언을 스코프의 최상단으로 끌어올리는 것을 뜻합니다. 원래 C언어 같은 프로그래밍 언어를 사용했을 때는 변수나 함수가 무조건 상단에 선언이 되어 있어야 했습니다. 그래야 아래 줄에서 참조가 가능하니깐요. 하지만 자바스크립트는 실제로 선언된 위치와 상관 없이 최상단으로 선언부가 끌어올려지기에 선언 위치 앞에서도 호출이 가능해집니다.
다만 이게 함수, <code>var</code>, <code>const/let</code>의 경우에 따라 달라지는데요. 함수의 경우에는 어디서든 호출이 가능하고 제대로 작동도 합니다. 하지만 <code>var</code>의 경우에는 선언 전에 호출한다면 <code>undefined</code>를 반환하게 됩니다. 그러나 이게 오히려 오류를 안 내서 불편한 나머지 <code>const/let</code>은 선언 전에 호출하면 에러를 내보내게 되었습니다. 물론 에러는 내보내지만 호이스팅이 되지 않은 건 아니란 점에 주의해야 합니다.</p>
</blockquote>
<ul>
<li>코드 실행 전에, 선언된 것들을 위로 끌어올림.</li>
<li>var는 선언만 끌어올려서 → 값은 undefined.</li>
<li>let, const도 끌어올리긴 하는데 → 선언 전에 쓰면 에러.</li>
<li>함수는 전체가 끌어올려져서 → 어디서든 호출 가능.</li>
</ul>
<blockquote>
<p>호출이 위에 있는데 선언이 아래인데도 작동하는 건 이 때문</p>
</blockquote>
<p><strong>4. 이벤트 버블링과 캡처링에 대해 설명해주세요.</strong></p>
<p>버블링과 캡처링으로 이벤트를 제어할 수 있는지 확인하는 질문
이름에서 오는 뜻을 활용하세요.</p>
<blockquote>
<p>이벤트 버블링과 캡처링은 모두 이벤트가 전파되는 방식을 뜻합니다. 다만 서로 그 방향이 다를 뿐입니다.
        버블링부터 말씀드리자면, 버블링은 거품이지 않습니까? 마치 거품이 수면으로 떠올라가듯이 이벤트가 전파되는 겁니다. 그래서 하위 요소에서 상위 요소로 이벤트가 전파되는 것을 버블링이라고 합니다.
        반대로 캡처링은 포착한다고 생각하시면 됩니다. 마치 카메라가 내가 보는 시야의 일부분을 포착해서 담듯이 이벤트가 전파되는 겁니다. 그래서 이번엔 상위 요소에서 하위 요소로 이벤트가 전파되는 것을 캡처링이라고 합니다. 이러한 특성을 잘 활용한다면 이벤트 위임을 구현할 수 있습니다.</p>
</blockquote>
<p>이벤트가 일어날 때, 어떻게 전파되느냐의 차이.</p>
<ul>
<li>버블링: 아래(자식) → 위(부모)로 올라감.
버블처럼 위로.</li>
<li>캡처링: 위(부모) → 아래(자식)로 내려감.
📸 먼저 포착하고 내려옴.<blockquote>
<p>대부분 브라우저는 버블링 기본. 필요할 때 캡처링 사용 가능.</p>
</blockquote>
</li>
</ul>
<p><strong>5. 이벤트 위임에 대해 설명해주세요.</strong></p>
<p>이벤트 버블링과 캡처링에 대해 잘 이해하고 있는지 재확인 및 위임을 이용해 최적화를 할 수 있는지 확인하기 위한 질문
최적화 얘기를 하면 좋습니다.</p>
<blockquote>
<p>이벤트 위임은 하위 요소의 이벤트를 상위 요소로 위임하는 걸 뜻합니다. 하위 요소가 많을 경우, 이러한 모든 요소들에 이벤트를 등록하고 관리하는 건 힘든 일이 될 수 있습니다. 그래서 버블링과 캡처링을 활용해 상위 요소에 이벤트를 위임하고, 이벤트 함수 내에서 하위 요소를 판단하여 이벤트를 처리하면 성능을 최적화할 수 있는 장점을 가진 게 바로 이벤트 위임입니다.</p>
</blockquote>
<ul>
<li>많은 자식에게 각각 이벤트 달지 말고,
부모 하나에만 달고 버블링으로 처리하는 방법.</li>
<li>성능 좋아지고 유지 보수 쉬워짐.</li>
</ul>
<blockquote>
<p>예: ul에서 각 li 클릭 처리할 때 → ul에 이벤트 하나만 걸기</p>
</blockquote>
<p><strong>6. 자바스크립트의 <code>this</code>가 어떤 값을 가지는지 각 상황을 가정하여 설명해주세요.</strong></p>
<p>각 상황에 따라 <code>this</code>의 변화를 이해하고 있는지 확인하기 위한 질문
암기 질문과 다름 없습니다. 외워오세요.</p>
<blockquote>
<p>모범 답안: <code>this</code> 는 호출되는 상황마다 서로 다른 값을 참조하고 있어 혼동하기 쉬운 값입니다. 우선 일반 함수를 호출할 때는 전역 객체인 <code>window</code>를 뜻합니다. 그리고 객체의 메서드를 호출할 때에는 해당 객체를 의미합니다. 이어서 콜백 함수는 전달하는 곳은 상관이 없고, 함수가 실제로 호출되는 상황에 따라 달라집니다. 만약 호출하는 상황이 일반 함수라면 전역 객체를 가리키겠죠? 마지막으로 화살표 함수는 생성된 함수의 스코프에 따라 달라집니다. 예를 들어 객체의 메서드에서 화살표 함수를 만들어서 <code>this</code>를 호출했다면 생성된 함수의 스코프는 객체 스코프이므로 여기서 <code>this</code>는 객체를 뜻하게 됩니다. 이러한 <code>this</code>의 특징 때문에 <code>call</code>, <code>apply</code>, <code>bind</code> 같은 걸로 <code>this</code>를 고정시켜주기도 하지만, 저는 저포함 동료 분들의 혼선을 피하기 위해 <code>this</code>의 사용을 좀 지양하는 편입니다.</p>
</blockquote>
<ul>
<li>상황에 따라 달라짐 💡 this = 호출한 놈</li>
</ul>
<table>
<thead>
<tr>
<th>상황</th>
<th>this가 가리키는 것</th>
</tr>
</thead>
<tbody><tr>
<td>일반 함수</td>
<td><code>window</code> (엄격모드에선 undefined)</td>
</tr>
<tr>
<td>객체의 메서드</td>
<td>그 <strong>객체</strong></td>
</tr>
<tr>
<td>이벤트 핸들러</td>
<td><strong>이벤트를 건 요소</strong></td>
</tr>
<tr>
<td>화살표 함수</td>
<td><strong>바깥 this</strong> 그대로 씀</td>
</tr>
<tr>
<td><code>bind</code>, <code>call</code>, <code>apply</code></td>
<td>강제로 바꾼 <strong>그 값</strong></td>
</tr>
</tbody></table>
<blockquote>
<p>⚠️ 그래서 화살표 함수는 this로 혼동 없어서 자주 씀</p>
</blockquote>
<p><strong>7. 자바스크립트의 프로토타입의 역할을 설명해주세요.</strong></p>
<p>ES6 이전에 많이 쓰이던 프로토타입에 대해서도 인지하고 있는지 확인하는 질문.
설마 이런 거까지 알겠어? </p>
<blockquote>
<p>모범 답안: 자바스크립트의 프로토타입은 객체가 가진 기본 속성과 메서드를 정의하는 객체입니다. 프로토타입에 정의된 이러한 속성과 메서드가 각 타입을 타입답게 만들게 됩니다. 예를 들어 숫자에 <code>toString()</code>메서드가 있는 것처럼 말입니다. 옛날에 클래스가 나오기 전에는 이 프로토타입을 통해 직접 메서드를 만들어주고는 했습니다.</p>
</blockquote>
<ul>
<li>객체들이 공통으로 공유하는 기능(메서드) 저장소.</li>
<li>예: 모든 배열에는 map, filter 같은 함수 있음 → Array.prototype에 있음.</li>
<li>직접 확장도 가능:<pre><code class="language-Javascript">Array.prototype.sayHi = () =&gt; console.log(&#39;hi&#39;);</code></pre>
</li>
</ul>
<blockquote>
<p>클래스 나오기 전엔 이걸로 직접 상속 만들고 했음</p>
</blockquote>
<p><strong>8. 자바스크립트의 객체는 참조에 의해 복사됩니다. 이로 인해 생길 수 있는 오류와, 해당 오류를 피하는 방법을 설명해주세요.</strong></p>
<p>참조에 의한 접근과 값에 의한 접근을 구분할 수 있는지 확인하는 질문입니다.
객체를 참조가 아닌 값으로서 복사하는 방법도 소개하면 좋습니다.</p>
<blockquote>
<p>네, 여기서 참조에 의한 복사가 일어나는 이유는 값 자체를 복사해 주는 게 아니라 값이 들어있는 메모리 주소를 복사해서 주기 때문입니다. 그렇기에 객체를 복사하게 되면 서로 같은 메모리 주소를 가리키게 되어 한쪽이 값을 바꾸면 다른 쪽이 보여주는 값도 바뀌게 되는 겁니다. 이러한 특징 때문에 여러 오류가 생기곤 합니다. 그래서 깊은 복사를 통해 객체 또한 값으로서 복사하도록 하는 방법들이 있는데 대표적으로는 <code>JSON.stringify()</code>를 통해 객체를 문자열로 바꾸어서 복사하는 방법이 있고, 비교적 최근에 나온 <code>structuredClone()</code>을 사용하는 방법이 있습니다. 개인적으로는 후자가 더 짧고 쉽기 때문에 더 애용하는 편입니다.</p>
</blockquote>
<ul>
<li><code>{}</code> 같은 객체를 복사하면, 값이 아닌 주소가 복사됨.</li>
<li>그래서 하나를 바꾸면 다른 것도 바뀜.</li>
<li>막으려면 깊은 복사 필요:<ul>
<li>JSON.parse(JSON.stringify(obj))</li>
<li>structuredClone(obj) (최신 방법)</li>
</ul>
</li>
</ul>
<blockquote>
<p>❌ 얕은 복사 (...obj)는 중첩 객체까지는 안 복사됨</p>
</blockquote>
<p><strong>9. 자바스크립트의 nullish 값은 무엇이 있을까요?</strong></p>
<p>nullish로 평가되는 데이터 타입과 값의 종류를 이해하고 있는지 확인
nullish 병합 연산자를 언급하면 좋습니다.</p>
<blockquote>
<p>네 자바스크립트에서 nullish는 말 그대로 <code>null</code>과 같은 값을 뜻합니다. 이에 대한 예시로는 대표적으로 <code>null</code>과 <code>undefined</code>가 있습니다. 여기서 알고 넘어가면 좋은 게 있는데, 바로 nullish 병합 연산자입니다. 기존에는 <code>||</code> 를 이용해서 병합을 구현했는데요. 이렇게 되면 0이나 &#39;&#39;처럼 빈 문자열 등이 <code>false</code>로 평가되어 제대로 동작하지 않을 가능성이 생기곤 합니다. 그때 바로 물음표 2개를 붙인 <code>??</code>로 대체해준다면 nullish한 값에 대해서만 병합을 실행하게 됩니다.</p>
</blockquote>
<ul>
<li><code>null</code>과 <code>undefined</code></li>
<li>그래서 <code>??</code> 연산자에서:</li>
</ul>
<pre><code class="language-javascript">null ?? &#39;대체값&#39; // 👉 &#39;대체값&#39;
0 ?? &#39;대체값&#39; // 👉 0 (nullish가 아니니까!)
기존 ||는 0이나 빈 문자열도 false로 처리해서 문제가 있었음</code></pre>
<blockquote>
<p>기존 <code>||</code>는 0이나 빈 문자열도 false로 처리해서 문제가 있었음</p>
</blockquote>
<p><strong>10. <code>==</code> 과 <code>===</code>의 차이점은 무엇일까요?</strong></p>
<p>자바스크립트의 기본 문법을 확인하는 질문입니다.
 nullish와 연계하여 대답하면 좋습니다.</p>
<blockquote>
<p>비교할 때 판단이 다릅니다. 간단히 요약하자면 <code>==</code>는 타입은 다르지만 값이 같을 때, 예를 들어 문자열 1과 숫자 1을 비교하면 같다고 뜰 겁니다. 그래서 숫자 0과 nullish 값을 이 연산자를 이용해 비교하면 true가 떠서 같다고 나올 겁니다. 그리고 <code>===</code>는 좀 더 엄격하게 타입과 값을 모두 비교합니다. 그래서 문자열 1과 숫자 1을 비교하면 다르다고 뜰 것입니다. 저는 보통 엄격한 비교를 위해 <code>===</code>를 더 선호해 사용하는 편입니다.</p>
</blockquote>
<ul>
<li>==: 값만 비교. 타입 자동 변환함.<ul>
<li>&#39;1&#39; == 1 👉 true</li>
<li>0 == &#39;&#39; 👉 true</li>
</ul>
</li>
<li>===: 값 + 타입 완전 일치해야 true</li>
</ul>
<blockquote>
<p>안전하게 쓰려면 무조건 === 쓰는 게 낫다</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 짝수 홀수 개수]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A7%9D%EC%88%98-%ED%99%80%EC%88%98-%EA%B0%9C%EC%88%98-gl0d9jza</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A7%9D%EC%88%98-%ED%99%80%EC%88%98-%EA%B0%9C%EC%88%98-gl0d9jza</guid>
            <pubDate>Mon, 21 Jul 2025 16:35:21 GMT</pubDate>
            <description><![CDATA[<h2 id="✨-짝수-홀수-개수">✨ 짝수 홀수 개수</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/71877980-c02d-45f6-b98d-d5895c4b4458/image.png" alt=""></p>
<h2 id="나의-풀이">나의 풀이</h2>
<h3 id="✅-문제-요약">✅ 문제 요약</h3>
<p><code>num_list</code>에서 짝수의 개수와 홀수의 개수를 <code>[짝수, 홀수]</code> 배열 형태로 반환하는 함수</p>
<pre><code class="language-javascript">function solution(num_list) {
  let answer = [];
  let newArr = [];
  for (let i = 0; i &lt; num_list.length; i++) {
    if (num_list[i] % 2 === 0) newArr.push(i);
  }
  return answer = [newArr.length, num_list.length - newArr.length];
}</code></pre>
<p>(for + push) : 의도는 짝수의 개수를 세는 것이나 newArr.push(i)로 짝수의 값이 아닌 인덱스를 저장하고 있어서 개선이 필요함
짝수의 개수가 카운트라면 push() 자체가 불필요하므로 더 간단한 방식으로 개선 해야함    </p>
<h3 id="✅-시간복잡도">✅ 시간복잡도</h3>
<p>for 루프는 num_list 를 한 번 순회 -&gt; O(n) 
newArr.push()는 배열 끝에 요소를 추가 -&gt; 평균 O(1)
newArr.length, num_list.length -&gt; O(1)</p>
<p>👉 총 시간복잡도: O(n) : 상수 시간</p>
<h3 id="✅-공간복잡도">✅ 공간복잡도</h3>
<p>newArr에 짝수 인덱스만 저장  -&gt; 최악의 경우 모든 요소가 짝수라면 O(n) 공간 사용</p>
<p>👉 총 공간복잡도: O(n)</p>
<h3 id="✅-개선-포인트-메모리-최적화">✅ 개선 포인트 (메모리 최적화)</h3>
<pre><code class="language-javascript">javascript
function solution(num_list) {
  let even = 0;
  for(let i = 0; i &lt; num_list.length; i++){
      if(num_list[i] % 2 === 0) even++
  }
  return [even, num_list.length - even]</code></pre>
<ul>
<li><strong>불필요한 배열(newArr) 제거</strong></li>
<li>메모리 O(1)로 최적화됨</li>
<li>가독성 향상</li>
</ul>
<h3 id="✅-최적-코드-시간공간-복잡도">✅ 최적 코드 시간/공간 복잡도</h3>
<ul>
<li>시간복잡도: O(n)</li>
<li>공간복잡도: O(1)</li>
</ul>
<h3 id="✅-고차함수-활용foreach--reduce">✅ 고차함수 활용(forEach / reduce)</h3>
<pre><code class="language-javascript">javascript
function solution(num_list) {
  let even = 0;
  num_list.forEach((n)=&gt; n % 2 ===0 &amp;&amp; even++)
  return [even, num_list.length - even]
}</code></pre>
<p>🔍 해석</p>
<blockquote>
<ul>
<li>even 짝수 카운터의 초기값을 0으로 표새하여 변수를 담을 그릇을 만든 다음,</li>
<li>forEach로 num_list 배열을 하나씩 순회하며 n이 짝수일 경우 even++</li>
<li>even = 짝수 개수이니, 전체 개수에서 뺴면 홀수 개수 = num_list.length -even</li>
<li>[짝수, 홀수] 형태로 변환</li>
</ul>
</blockquote>
<pre><code class="language-javascript">javascript
function solution(num_list) {
  let even = 0;
  num_list.reduce(([even, odd], cur)=&gt; cur % 2 === 0 ? [even +1, odd] : [even, odd + 1],[0, 0])
  return [even, odd];
}</code></pre>
<p>🔍 해석</p>
<blockquote>
<ul>
<li>reduce() 는 누적값 [evne, odd]를 유지하면서 cur값을 하나씩 검사</li>
<li>cur이 짝수 -&gt; [even + 1, odd] </li>
<li>cur이 홀수 -&gt; [even, odd + 1] </li>
<li>초기 값은 [0, 0] (짝수 0개, 홀수 0개)</li>
</ul>
</blockquote>
<h3 id="✅-결론">✅ 결론</h3>
<p>둘 다 시간복잡도 O(n) / 공간복잡도 O(1)로 효율은 같음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] UPSKILL : Javascript 코딩테스트 131개 예제 & CS지식으로 끝내기(1)]]></title>
            <link>https://velog.io/@yuna-c/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-UPSKILL-Javascript-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-131%EA%B0%9C-%EC%98%88%EC%A0%9C-CS%EC%A7%80%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EB%81%9D%EB%82%B4%EA%B8%B01</link>
            <guid>https://velog.io/@yuna-c/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-UPSKILL-Javascript-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-131%EA%B0%9C-%EC%98%88%EC%A0%9C-CS%EC%A7%80%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EB%81%9D%EB%82%B4%EA%B8%B01</guid>
            <pubDate>Mon, 21 Jul 2025 10:58:05 GMT</pubDate>
            <description><![CDATA[<h1 id="✅코딩-테스트">✅코딩 테스트</h1>
<ol>
<li>프로그래밍 언어 선택</li>
<li>유형별로 10개 이상 풀어보기</li>
<li>대표 알고리즘 유형 : 정렬, DFS/BFS, 구현, 완전 탐색, 탐욕 알고리즘</li>
<li>기업 기출 문제 풀기</li>
</ol>
<h1 id="✅시간-복잡도">✅시간 복잡도</h1>
<ul>
<li>크기 형태의 의미, 수학적, 수치</li>
<li>알고리즘의 성능을 나타내는 척도(얼마나 빠르게 수행되는가)</li>
<li><strong>시간 복잡도</strong> : 특정한 크기의 입력에 대해 알고리즘 수행 시간 분석(낮아야 빠름)<pre><code>             조금더 값이 크게 증가하는 정도, 수치적으로 값이 얼마나 큰가</code></pre></li>
<li>복잡도가 낮을수록 우수한 코드</li>
</ul>
<h1 id="✅시간-복잡도-표기법">✅시간 복잡도 표기법</h1>
<h2 id="빅오-표기법-big-o-notation-나타낸다">빅오 표기법 (Big O Notation= 나타낸다)</h2>
<ul>
<li><strong>가장 빠르게 증가하는 항만 고려하는 표기법</strong></li>
<li>함수의 상한(가장 작은 수) 나타냄</li>
<li>3N³+ 5N² +1,000,000</li>
<li>² = 차수</li>
<li>N³ = 100 * 100 * 100 = 1000000</li>
<li>N이 급격하게 커지는 것에 비해 다른 항들에 대해선 영향이 작다</li>
<li>Big-O : 차수가 가장 큰 항에서 계수를 제외하여 O(N³)으로 표기</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/13ed0769-0bc1-4fda-8ea9-0e148b53a4f4/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>시간 복잡도</th>
<th>의미 (한글/영문)</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>O(1)</code></td>
<td><strong>상수 시간 (constant time)</strong></td>
<td>입력 크기와 무관하게 항상 같은 시간. 가장 빠름. ex) 배열 인덱스로 접근</td>
</tr>
<tr>
<td><code>O(log N)</code></td>
<td><strong>로그 시간 (log time)</strong></td>
<td>입력이 커져도 천천히 증가. 이진 탐색 등에서 등장</td>
</tr>
<tr>
<td><code>O(N)</code></td>
<td>선형 시간 (linear time)</td>
<td>입력 크기만큼 실행 시간 증가. ex) 배열 전체 순회</td>
</tr>
<tr>
<td><code>O(N log N)</code></td>
<td>로그 선형 시간 (log-linear time)</td>
<td>병합 정렬(Merge Sort)이나 퀵 정렬 평균 시간</td>
</tr>
<tr>
<td><code>O(N²)</code></td>
<td>이차 시간 (quadratic time)</td>
<td>중첩 루프. ex) 버블 정렬, 브루트포스 2중 반복</td>
</tr>
<tr>
<td><code>O(N³)</code></td>
<td>삼차 시간 (cubic time)</td>
<td>3중 루프. ex) 플로이드-워셜 알고리즘 등</td>
</tr>
<tr>
<td><code>O(2^N)</code></td>
<td>지수 시간 (exponential time)</td>
<td>입력이 조금만 커져도 실행 시간이 폭증. 피해야 함. ex) 모든 부분 집합 찾기</td>
</tr>
</tbody></table>
<h3 id="🌍상수-시간-복잡도">🌍상수 시간 복잡도</h3>
<p>사칙연산</p>
<h3 id="🌍로그-시간-복잡도">🌍로그 시간 복잡도</h3>
<p>절반씩 쪼갤때(이분 탐색), 상수 시간에 가까울 정도로 빠름</p>
<blockquote>
<p>이분 탐색 (Binary Search)
정렬된 리스트나 배열에서 특정 값의 위치를 찾는 알고리즘</p>
</blockquote>
<h3 id="🌍선형-시간-복잡도">🌍선형 시간 복잡도</h3>
<p>배열 하나씩 확인해서 어떤 원소가 존재하는지 찾을 때 사용(병합, 힙정렬...기반 알고리즘)</p>
<h4 id="병합-정렬-merge-sort">병합 정렬 (Merge Sort)</h4>
<blockquote>
<p><em>분할 정복 (Divide and Conquer)</em>
병합 정렬은 배열을 절반으로 계속 나누어 더 이상 나눌 수 없을 때까지 반복
그런 다음, 정렬된 부분 배열들을 병합하여 최종적으로 정렬된 배열</p>
</blockquote>
<blockquote>
<p><em>재귀적 호출</em>
각 부분 배열을 정렬하기 위해 재귀적으로 병합 정렬을 호출</p>
</blockquote>
<blockquote>
<p><em>병합 과정</em>
정렬된 두 부분 배열을 병합할 때, 각 배열의 첫 번째 요소를 비교하여 작은 값을 결과 배열에 추가, 이 과정을 반복하여 최종적으로 정렬된 배열을 얻음</p>
</blockquote>
<h4 id="힙-정렬-heap-sort">힙 정렬 (Heap Sort)</h4>
<blockquote>
<p><em>힙 자료구조</em>
힙 정렬은 힙 자료구조를 기반함, 힙은 완전 이진 트리의 일종으로, 부모 노드가 자식 노드보다 항상 크거나 작은 특성을 갖음 (최대 힙 또는 최소 힙).</p>
</blockquote>
<blockquote>
<p><em>최대 힙 구성</em>
먼저, 정렬되지 않은 배열을 최대 힙으로 구성 
이 과정에서 부모 노드가 자식 노드보다 항상 크도록 힙 속성을 유지</p>
</blockquote>
<blockquote>
<p><em>정렬 과정</em>
최대 힙의 루트 노드(가장 큰 값)를 힙의 마지막 요소와 교환하고, 힙 크기를 줄여서 다시 힙 속성을 유지. 이 과정을 반복하여 정렬된 배열을 얻음 </p>
</blockquote>
<h3 id="🌍이차-시간-복잡도">🌍이차 시간 복잡도</h3>
<p>동적 계획법, 넥센 문제 등등</p>
<blockquote>
<p><em>동적 계획법</em>
큰 문제를 여러 개의 작은 부분 문제로 나누어 해결하고, 각 부분 문제의 해를 저장하여 중복 계산을 피하는 알고리즘 설계 기법</p>
</blockquote>
<blockquote>
<p><em>동적 계획법의 활용</em>
최단 경로 문제: 그래프에서 두 점 사이의 최단 거리를 구하는 문제에 사용
예를 들어, 구글 지도에서 길 찾기 기능와 같은 서비스에서 활용
배낭 문제(Knapsack Problem): 제한된 용량의 배낭에 최대한 많은 가치를 가진 물건들을 넣는 문제에 사용</p>
</blockquote>
<blockquote>
<p><em>문자열 유사도 계산</em>
두 문자열의 유사도를 계산하는 문제에 사용 </p>
</blockquote>
<h3 id="🌍삼차-시간-복잡도">🌍삼차 시간 복잡도</h3>
<p>알고리즘의 실행 시간이 입력 크기 (n)의 세제곱에 비례하여 증가하는 것</p>
<blockquote>
<p>_플로이드 워셜 _
모든 지점에서 다른 모든 지점까지의 최단 경로를 모두 구해야 하는 경우를 구하는 알고리즘</p>
</blockquote>
<h1 id="📌결론">📌결론</h1>
<p>알고리즘이 얼마나 빠르게 동작하는지 평가하기 위해 사용</p>
<h1 id="🎯-예시">🎯 예시</h1>
<pre><code class="language-javascript">let array = [1,2,3,4,5] // 5개의 데이터(N = 5)
let summary = 0; // 합계를 지정할 변수

// 모든 데이터를 하나씩 확인하며 합계를 계산
for(let i = 0; i &lt; array.length; i++){
  summary += array[i];
}

// 결과를 출력
console.log(summary)</code></pre>
<blockquote>
<p>수행시간은 데이터 개수 N에 비례할 것임을 예측 가능
<code>O(N) : 선형 시간 복잡도</code> </p>
</blockquote>
<pre><code class="language-javascript">let array = [3,5,1,2,4] // 5개의 데이터(N = 5)
let summary = 0; // 합계를 지정할 변수

// 모든 데이터를 하나씩 확인하며 합계를 계산(5의 n제곱)
for(let i = 0; i &lt; array.length; i++){ // i가 한번 바뀔때마다
  for(let j= 0; j &lt; array.length; j++){ // j가 다섯번씩 변경됨
    let temp = array[i] * array[j]
    console.log(temp)
}

// 결과를 출력 
console.log(summary)</code></pre>
<blockquote>
<p>모든 2중문이 시간 복잡도가 <code>O(N²)</code>은 아니니 소스코드가 내부적으로 호출하는 함수 고려
<code>O(N²): 이차 시간 복잡도</code> </p>
</blockquote>
<h1 id="💪알고리즘-설계-tip">💪알고리즘 설계 TIP</h1>
<ul>
<li>일반적인 CPU 기반 개인 컴퓨터</li>
<li>JS기준 1억번의 연산은 1~5초</li>
<li>O(N³) 설계의 경우 , N이 5,000이 넘는다면 걸리는 시간은? </li>
<li>코딩 테스트 시간 제한 : 1~5초</li>
<li>명시 X : 5초</li>
</ul>
<h1 id="💪요구사항에-따른-적절한-알고리즘-설계">💪요구사항에 따른 적절한 알고리즘 설계</h1>
<ul>
<li>시간제한(수행 시간 요구사항)</li>
<li>시간 제한이 1초인 문제를 만났을 때, 일반적인 기준은 다음과 같다.</li>
<li>𝑁의 범위가 500인 경우 : 시간 복잡도가 𝑂(N³) 설계</li>
<li>𝑁의 범위가 2,000인 경우 : 시간 복잡도가 𝑂(N³) 설계</li>
<li>N의 범위가 2,000인 경우: 시간 복잡도가 𝑂(N²) 설계</li>
<li>𝑁의 범위가 100,000인 경우: 시간 복잡도가 O(N log N) 설계</li>
<li>𝑁의 범위가 10,000,000인 경우: 시간 복잡도가 O(N) 설계</li>
</ul>
<p>범위를 미리 파악하고 역으로 어느 정도로 동작하는지 이해해서 푸는 것</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트엔드 기술면접_CSS(1)]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%95%A4%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91-css</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%95%A4%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91-css</guid>
            <pubDate>Mon, 21 Jul 2025 07:55:34 GMT</pubDate>
            <description><![CDATA[<h1 id="css">CSS</h1>
<p><strong>1. <code>display: none</code> 과 <code>visibility: hidden</code>의 차이점은 무엇인가요?</strong></p>
<p>기본적인 CSS 지식과 브라우저 렌더링 과정을 알고 있는지 확인하는 질문</p>
<blockquote>
<p>두 속성 다 화면에서 요소를 보이지 않게 하는 것, 레이아웃에 영향을 미치는지에 따라 차이가 있습니다. 화면에 보이지 않지만 원래 있었던 공간을 차지하는지 아닌지 입니다.
<code>display: none</code>은 말 그대로 디스플레이에서 없애는 것이기 때문에 기존 공간을 다른 요소로 채울 수 있습니다. 다시 말해 공간을 차지하지 않습니다. 반면에
<code>visibility: hidden</code>은 요소를 그저 투명하게만 만들어주는 것이기에 기존 요소의 공간을 차지합니다.</p>
</blockquote>
<p><strong>2. <code>styled-components</code>를 써보셨나요? 사용하면서 느낀 장단점에 대해 알려주세요.</strong></p>
<p>styled-components에 대한 경험이 있는지 확인하는 질문</p>
<blockquote>
<p><code>styled-components</code>를 사용하며 가장 크게 느꼈던 장점은 컴포넌트이기에 props를 내려줘 스타일의 동적이 변화에 사용할 수 있다는 점, 컴포넌트이기에 재사용이 수월하다는 점
다만 단점으로는 네이밍이 어렵고, 스타일이 들어간 컴포넌트에도 모두 이름을 지어주는 건 너무 시간이 드는 작업. 또한 미미하지만 성능상 단점도 있습니다. 그래도 본질이 js이기 때문에 css로 변환하는 데에 걸리는 시간이 좀 더 길어지게 되는 특징도 있습니다. 최근에 유행하는 Next.js의 서버 컴포넌트에서도 지원이 적다는 단점도 있습니다.저는 장점 보다는 단점이 더 크다고 생각하여 styled-components 대신 tailwind CSS를 더 선호하는 편입니다.</p>
</blockquote>
<p><strong>3. CSS의 단위 중 <code>px</code>, <code>em</code>, <code>rem</code> 의 차이점에 대해 설명해주세요.</strong></p>
<p>CSS의 기본적인 단위에 대해 알고 있는지 확인하는 질문</p>
<blockquote>
<p><code>px</code>는 폰트를 고정값으로 표기하기 때문에 적응형에서 많이 사용하지만 현재는 유저 디바이스 환경에 따라 크기 변화가 있을 수 있기 때문에 <code>em</code>이나 <code>rem</code>을 주로 쓰는 편입니다.
<code>em</code>은 상대적인 단위로서 현재 속해있는 요소의 폰트 크기를 기준으로 계산합니다. 현재 속해 있는 요소의 폰트 크기가 크다면 <code>em</code>값도 커지게 됩니다. <code>px</code>과 반대로 반응형 디자인에 좋습니다. 그러나 현재 속해 있는 요소의 기본 크기가 어느 정도인지에따라 바뀌기 때문에 조심해야 합니다.
그리고 rem은 em하고 모두 같지만 하나 다른 점은 바로 기준이 되는 폰트 크기입니다. rem의 r은 root를 말하는 만큼 최상단 루트의 기본 크기를 바탕으로 적용이 됩니다. 그래서 루트에 정의된 폰트 크기를 바꾸지 않는 한 항상 같은 값을 지니고 있어 저는 이 속성을 가장 많이 씁니다.</p>
</blockquote>
<p><strong>4. reset.css 또는 normal.css가 필요한 이유를 설명할 수 있습니다.</strong></p>
<p>지원자가 reset.css와 normalize.css의 필요성과 역할을 이해하고 있는지 평가.</p>
<blockquote>
<p>reset.css는 브라우저 기본 스타일을 모두 제거하여 모든 요소가 동일한 기본 스타일을 가지도록 합니다.
normalize.css는 브라우저 간의 스타일 차이를 최소화하여 일관된 스타일을 유지합니다.
reset.css는 모든 스타일을 제거하므로 스타일링을 처음부터 정의해야 하고, normalize.css는 기본 스타일을 유지하면서 브라우저 차이를 최소화합니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 개인 : Supabase Storage에 이미지 업로드하고 public URL 반환하는 함수 만들기]]></title>
            <link>https://velog.io/@yuna-c/TIL-%EA%B0%9C%EC%9D%B8-Supabase-Storage%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B3%A0-public-URL-%EB%B0%98%ED%99%98%ED%95%98%EB%8A%94-%ED%95%A8%EC%88%98-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@yuna-c/TIL-%EA%B0%9C%EC%9D%B8-Supabase-Storage%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B3%A0-public-URL-%EB%B0%98%ED%99%98%ED%95%98%EB%8A%94-%ED%95%A8%EC%88%98-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 21 Jul 2025 06:48:55 GMT</pubDate>
            <description><![CDATA[<h1 id="public-url">public URL</h1>
<h3 id="✅-public-url이란">✅ public URL이란?</h3>
<blockquote>
<p>누구나 접근할 수 있는 &quot;인터넷 주소&quot;
즉, 로그인하지 않아도 이미지나 파일에 접근할 수 있는 공개 링크</p>
</blockquote>
<h3 id="🔍-예시">🔍 예시</h3>
<pre><code class="language-javascript">https://abc.supabase.co/storage/v1/object/public/images/mycat.jpg</code></pre>
<blockquote>
<p>이런 주소가 생성되면, 이 링크를 다른 사람한테 줘도, 그 사람은 바로 이미지를 볼 수 있음</p>
</blockquote>
<h3 id="📦-supabase에서-말하는-public-url은">📦 Supabase에서 말하는 public URL은?</h3>
<blockquote>
<p>Supabase의 Storage는 파일을 저장하는 &quot;클라우드 폴더&quot;
거기서 getPublicUrl()이라는 함수를 쓰면 →
해당 파일에 접근 가능한 공개 주소 (public URL) 을 만들어줌</p>
</blockquote>
<p>🧁 요약하자면</p>
<blockquote>
<p>public URL = &quot;누구나 볼 수 있는 파일 링크&quot;
로그인 필요 없음
블로그, 프로필 이미지, 썸네일 등에 자주 사용됨</p>
</blockquote>
<h1 id="public-url-반환-유틸">public URL 반환 유틸</h1>
<pre><code class="language-javascript">
/**
 * Supabase Storage에 이미지를 업로드하고 public URL을 반환
 * @param {File} file - 업로드할 이미지 파일 (input[type=&quot;file&quot;]의 파일 객체)
 * @param {string} bucket - 저장할 버킷 이름 (기본값: &#39;images&#39;)
 * @param {string} folder - 저장할 폴더 경로 (기본값: &#39;public&#39;)
 * @returns {string|null} 업로드된 이미지의 public URL 또는 null
 */

export const getImageURL = async (file, bucket = &#39;images&#39;, folder = &#39;public&#39;) =&gt; {
  let imageUrl = null;
  if (!file) return null;

  try {
    const ext = file.name.split(&#39;.&#39;).pop();
    const filename = `${Date.now()}-${crypto.randomUUID()}.${ext}`;
    const filepath = `${folder}/${filename}`;

    const { error: uploadError } = await supabase.storage.from(bucket).upload(filepath, file);

    if (uploadError) {
      console.error(`[이미지 업로드 실패], ${uploadError.message}`);
      return null;
    }

    const { data, error: urlError } = supabase.storage.from(bucket).getPublicUrl(filepath);
    if (urlError) {
      console.error(`[이미지 URL 가져오기 실패], ${urlError.message}`);
      return null;
    }

    imageUrl = data.publicUrl;
  } catch (e) {
    console.error(&#39;[이미지 업로드 오류]&#39;, e.message);
    return null;
  }

  return imageUrl;
};
</code></pre>
<ul>
<li>file.name에서 확장자 추출 후 UUID 기반으로 파일명 생성</li>
<li>업로드 후 에러 체크</li>
<li>getPublicUrl()로 공개 URL 받아서 반환</li>
<li>실패 시 null 반환</li>
</ul>
<blockquote>
<p><strong>Supabase</strong>의 <strong>Storage</strong> 기능을 이용해, 사용자가 업로드한 이미지 파일을 저장하고
해당 이미지의 공개 <strong>URL(publicUrl)</strong> 을 반환하는 유틸 함수를 만들었다.</p>
</blockquote>
<blockquote>
<p>파일명은 중복을 방지하기 위해 <strong>Date.now()</strong> 와 <strong>crypto.randomUUID()</strong> 를 조합해 고유하게 생성했고,
지정한 경로에 이미지를 업로드한 후 <strong>getPublicUrl()</strong> 을 통해 public URL을 받아오는 흐름이다.</p>
</blockquote>
<blockquote>
<p>업로드나 URL 생성 중 에러가 발생할 경우에는 <strong>null</strong>을 반환하도록 예외 처리를 했다.
성공적으로 업로드되면 <strong>data.publicUrl</strong>을 <strong>imageUrl</strong>로 받아 최종적으로 반환한다.</p>
</blockquote>
<h3 id="사용법">사용법</h3>
<pre><code class="language-javascript">const handleUpload = async (e) =&gt; {
  const file = e.target.files[0];
  const url = await getImageURL(file);
  if (url) {
    console.log(&#39;업로드 성공:&#39;, url);
  }
};


const { _data, error: profileError } = await supabase.from(&#39;profiles&#39;).upsert({
  id: user.id,
  nick_name: formData.nick_name,
  website_url: formData.website_url,
  updated_at: new Date().toISOString()
});</code></pre>
<blockquote>
<p>upsert나 insert로 데이터를 덮어 씌우거나 삽입한다 
테이블 컬럼 이름은 되도록 소문자 + 스네이크 케이스(nick_name)로 일관되게 쓰는 것이 좋고 프론트 단에서는 카멜케이스로 구분하는게 좋다</p>
</blockquote>
<h3 id="주의점">주의점</h3>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/1338c1ab-bbd5-431a-8a87-895db97f3d2b/image.png" alt=""></p>
<blockquote>
<p>수퍼베이스 스토리지는 공개로 해 놓아야 하며</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/a2dcd115-5e5b-4edb-9d7a-e9e9ddfe1143/image.png" alt=""></p>
<blockquote>
<p>정책은 select, insert, update, delete 전부 만들어 주어야 한다</p>
</blockquote>
<h1 id="🔐-signed-url">🔐 Signed URL</h1>
<blockquote>
<p>시간 제한이 있는 URL을 만들어서 정해진 시간 동안만 해당 파일에 접근할 수 있도록 하는 방식으로, 기업 환경에서는 대부분 public URL보다 보안에 강한 signed URL(= 인증된 URL)을 쓴다</p>
</blockquote>
<pre><code class="language-javascript">const { data } = await supabase.storage
  .from(&#39;private&#39;)
  .createSignedUrl(&#39;report.pdf&#39;, 60); // 60초 유효
</code></pre>
<blockquote>
<p>이렇게 하면, 60초 동안만 열리는 링크가 생성돼. 그 후엔 자동으로 접근 불가!</p>
</blockquote>
<h3 id="✅-기업에서-signedurl을-쓰는-이유">✅ 기업에서 signedUrl을 쓰는 이유</h3>
<ul>
<li>유저별 접근 권한을 제어하고 싶을 때</li>
<li>유료 콘텐츠나 민감한 자료를 보호해야 할 때</li>
<li>외부에 링크 유출되는 상황을 방지하고 싶을 때</li>
</ul>
<h3 id="✅-실제예시">✅ 실제예시</h3>
<table>
<thead>
<tr>
<th>상황</th>
<th>쓰는 방식</th>
</tr>
</thead>
<tbody><tr>
<td>쇼핑몰 상품 썸네일</td>
<td><code>public URL</code> (누구나 봐야 하니까)</td>
</tr>
<tr>
<td>직원용 월급명세서 PDF</td>
<td><code>signed URL</code> (로그인한 직원만 열람 가능)</td>
</tr>
<tr>
<td>온라인 강의 영상</td>
<td><code>signed URL</code> (유료 사용자만 제한 시간 동안 재생 가능)</td>
</tr>
</tbody></table>
<h3 id="✅-public-url-vs-private-url-signed-url-차이">✅ public URL vs private URL (signed URL) 차이</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>Public URL</th>
<th>Signed URL (Private URL)</th>
</tr>
</thead>
<tbody><tr>
<td>🔓 접근</td>
<td>누구나 접근 가능</td>
<td>토큰/시간 제한된 인증된 사용자만 가능</td>
</tr>
<tr>
<td>🕒 만료 시간</td>
<td>없음 (영구적으로 유효)</td>
<td>있음 (예: 1분, 5분 후 만료)</td>
</tr>
<tr>
<td>🔐 보안</td>
<td>없음 → 이미지 링크 퍼지면 누구나 열람 가능</td>
<td>있음 → 링크 유출돼도 일정 시간 후 무효</td>
</tr>
<tr>
<td>🔧 사용 예</td>
<td>썸네일, 프로필 이미지, 블로그 게시물 등</td>
<td>민감 정보, 유료 콘텐츠, 내부 자료, 비공개 파일 등</td>
</tr>
<tr>
<td>📦 Supabase 함수</td>
<td><code>getPublicUrl()</code></td>
<td><code>createSignedUrl()</code></td>
</tr>
</tbody></table>
<h1 id="✅-결론">✅ 결론</h1>
<ul>
<li>공개 이미지: getPublicUrl()로 public URL 쓰면 편하고 빠름</li>
<li>보안이 필요한 파일: createSignedUrl()로 signed URL 써야 함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 개인 : Tab기능 useHooks로 변경]]></title>
            <link>https://velog.io/@yuna-c/TIL-%EA%B0%9C%EC%9D%B8-Tab%EA%B8%B0%EB%8A%A5-useHooks%EB%A1%9C-%EB%B3%80%EA%B2%BD</link>
            <guid>https://velog.io/@yuna-c/TIL-%EA%B0%9C%EC%9D%B8-Tab%EA%B8%B0%EB%8A%A5-useHooks%EB%A1%9C-%EB%B3%80%EA%B2%BD</guid>
            <pubDate>Mon, 21 Jul 2025 06:32:46 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yuna-c/post/3d733460-a8c2-424a-a6aa-f3aaad9e5271/image.png" alt=""></p>
<h1 id="🔖-리액트-간단-탭-기능-구현--커스텀-훅-분리">🔖 리액트 간단 탭 기능 구현 &amp; 커스텀 훅 분리</h1>
<blockquote>
<p>마이페이지 UI를 구성하면서, 탭 기능을 간단하게 구현해봤다.
처음에는 useState를 컴포넌트 내부에 직접 선언해서 관리했지만, 재사용성과 가독성을 높이기 위해
공통 로직을 커스텀 훅 useTabs로 분리했다.</p>
</blockquote>
<h3 id="📌-1-기본-구조-usestate-기반">📌 1. 기본 구조 (useState 기반)</h3>
<pre><code class="language-javascript">const tabs = [&#39;내 정보&#39;, &#39;내 포스트&#39;, &#39;좋아요&#39;];
const [activeTab, setActiveTab] = useState(0);
</code></pre>
<ul>
<li>tabs 배열에 탭 라벨을 정의</li>
<li>useState(0)으로 첫 번째 탭을 기본값으로 설정</li>
<li>activeTab에 따라 조건부 렌더링 처리</li>
</ul>
<pre><code class="language-javascript">&lt;div className=&quot;flex w-auto border-b md:w-80&quot;&gt;
  {tabs.map((tab, index) =&gt; {
    return (
      &lt;button
        key={index}
        onClick={() =&gt; setActiveTab(index)}
        className={`flex-1 p-2 text-center ${
          activeTab === index ? &#39;border-b-2 border-gray-500 font-semibold&#39; : &#39;text-gray-500&#39;
        }`}
      &gt;
        {tab}
      &lt;/button&gt;
    );
  })}
&lt;/div&gt;

&lt;div className=&quot;flex flex-col items-center justify-center w-full p-4&quot;&gt;
  {activeTab === 0 &amp;&amp; &lt;MyInfo /&gt;}
  {activeTab === 1 &amp;&amp; &lt;MyPost /&gt;}
  {activeTab === 2 &amp;&amp; &lt;MyStatus /&gt;}
&lt;/div&gt;
</code></pre>
<ul>
<li>삼항 연산자를 이용해 버튼 스타일도 조건 분기</li>
</ul>
<blockquote>
</blockquote>
<p>tabs 배열에 탭 이름을 정의하고, useState(0)으로 기본 탭을 설정했다.
이후 activeTab의 값에 따라 각 컴포넌트를 조건부 렌더링하는 방식으로 구성했다. 이런 방식은 단순하지만 직관적이고 유지보수도 편함.
<strong>activeTab === index ?</strong> 삼항 연산자를 통한 클래스 또한 분기하여 selected 상태일 때를 표시</p>
<h3 id="🔄-2-커스텀-훅으로-리팩터링-usetabsjs">🔄 2. 커스텀 훅으로 리팩터링 (useTabs.js)</h3>
<pre><code class="language-javascript">import { useState } from &#39;react&#39;;

export const useTabs = (initialIndex = 0) =&gt; {
  const [index, setIndex] = useState(initialIndex);
  const changeTab = (i) =&gt; setIndex(i);

  return { index, changeTab };
};
</code></pre>
<ul>
<li>initialIndex를 기본값으로 받음 → 기본 탭 지정 가능</li>
<li>객체로 { index, changeTab } 반환 → 구조분해 할당 편리함</li>
<li>추후 다른 컴포넌트에서도 탭 상태를 동일한 방식으로 쉽게 재사용 가능</li>
</ul>
<blockquote>
<p>hooks 폴더에 useTabs.js로 추출하여 재사용성과 가독성을 높인 훅
초기값을 initailIndex = 0 으로 설정해 두어 호출시 원하는 탭 번호로 인덱스를 바꿀 예정 
객체(Object)로 리턴하여 <strong>{index: 현재 선택된 탭 번호 (state), changeTab: 탭을 바꾸는 함수 (함수)}</strong> 로구조분해 할당을 하기 위한 전처리</p>
</blockquote>
<h3 id="💻-3-훅-적용된-최종-사용-코드">💻 3. 훅 적용된 최종 사용 코드</h3>
<pre><code class="language-javascript">import { useTabs } from &#39;../hooks/useTabs&#39;;

const tabs = [&#39;내 정보&#39;, &#39;내 포스트&#39;, &#39;좋아요&#39;];
const { index, changeTab } = useTabs();

{tabs.map((tab, i) =&gt; (
  &lt;button
    key={i}
    onClick={() =&gt; changeTab(i)}
    className={`flex-1 p-2 text-center ${index === i ? &#39;border-b-2 border-gray-500 font-semibold&#39; : &#39;text-gray-500&#39;}`}
  &gt;
    {tab}
  &lt;/button&gt;
))}

{index === 0 &amp;&amp; &lt;MyInfo /&gt;}
{index === 1 &amp;&amp; &lt;MyPost /&gt;}
{index === 2 &amp;&amp; &lt;MyStatus /&gt;}</code></pre>
<blockquote>
<p>요약
useState로 탭 기능을 만들고, 커스텀 훅(useTabs)으로 분리하면 재사용성과 코드 가독성이 향상된다. 탭 상태 관리가 필요한 컴포넌트에서 간단히 적용 가능!</p>
</blockquote>
<h1 id="✍️-결론">✍️ 결론</h1>
<blockquote>
<p>이처럼 간단한 탭 UI라도 커스텀 훅으로 분리하면 코드의 재사용성과 유지보수가 훨씬 쉬워진다. 추후 더 복잡한 탭 구조로 확장할 때도 기본 뼈대가 단단해지는 느낌이다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[네이밍 조심 또 조심]]></title>
            <link>https://velog.io/@yuna-c/%EB%84%A4%EC%9D%B4%EB%B0%8D-%EC%A1%B0%EC%8B%AC-%EB%98%90-%EC%A1%B0%EC%8B%AC</link>
            <guid>https://velog.io/@yuna-c/%EB%84%A4%EC%9D%B4%EB%B0%8D-%EC%A1%B0%EC%8B%AC-%EB%98%90-%EC%A1%B0%EC%8B%AC</guid>
            <pubDate>Sun, 20 Jul 2025 23:45:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yuna-c/post/6918bede-6d57-4bfd-a53d-98cc17598ea0/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Storybook 도입기]]></title>
            <link>https://velog.io/@yuna-c/Storybook-%EB%8F%84%EC%9E%85%EA%B8%B0</link>
            <guid>https://velog.io/@yuna-c/Storybook-%EB%8F%84%EC%9E%85%EA%B8%B0</guid>
            <pubDate>Mon, 18 Nov 2024 00:31:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yuna-c/post/62fbfb85-7e81-4c53-a438-2d447c767488/image.gif" alt=""></p>
<h2 id="프로젝트에서-스토리북-도입기">프로젝트에서 스토리북 도입기</h2>
<p>마지막 프로젝트에서 결국 공통 UI컴포넌트를 위해 storybook을 사용하였습니다.
Storybook은 컴포넌트를 애플리케이션과 분리하여 독립적으로 개발할 수 있도록 도와주고, 프로젝트를 실행하지 않아도 Storybook 내에서 컴포넌트 상태를 다양한 시나리오로 테스트 가능한 UI전용 도구입니다. </p>
<p>처음에 러닝커브에도 불구하고 마지막 프로젝트에 storybook을 도입하며 저는 컴포넌트의 공용화의 시점을 어디로 해야 하는지와, 도입 배경, 장점에 대해 서술하였습니다.  </p>
<h3 id="도입-배경">도입 배경</h3>
<ol>
<li>복잡한 UI 컴포넌트 구조
SnapRoad의 주요 기능은 다음과 같이 복잡한 UI 컴포넌트를 필요로 했습니다. </li>
</ol>
<ul>
<li>이미지 업로드 및 다양한 필요에 의한 사용처</li>
<li>여러 상태를 가진 이미지 슬라이더</li>
<li>지도와 연동된 주소 검색 필드</li>
<li>다양한 입력 폼과 상태 처리(해시태그, 날짜 선택 등)</li>
</ul>
<p>각 컴포넌트는 다양한 상태(로딩, 에러, 성공 등)를 처리해야 했고, 이러한 상태를 일일이 구현하고 확인하기 어려웠습니다. 따라서 UI를 독립적으로 개발 및 테스트할 수 있는 환경이 필요했습니다.</p>
<ol start="2">
<li><p>생산성 문제
컴포넌트를 개발할 때마다 앱을 실행하고 특정 페이지까지 이동해야 하는 번거로움이 있었습니다. 이를 해결하기 위해 스토리북을 통해 컴포넌트를 격리된 환경에서 개발하고 테스트할 필요성을 느꼈습니다.</p>
</li>
<li><p>유지보수 및 협업
컴포넌트가 많아지면서 재사용성과 문서화가 중요해졌습니다. 컴포넌트는 프로젝트 전체에서 자주 재사용되기 때문에, 각 컴포넌트의 상태를 시각적으로 확인하고 명확히 문서화하는 작업이 필수적이었습니다.</p>
</li>
</ol>
<h3 id="도입-과정">도입 과정</h3>
<ol>
<li>스토리북 설치 및 설정
먼저 Next.js 프로젝트에 스토리북을 설치했습니다.</li>
</ol>
<pre><code class="language-Javascript">npx storybook init</code></pre>
<p>스토리북 초기 설정 과정에서 TypeScript와 Tailwind CSS 환경을 통합하여 해당 프로젝트의 스타일을 그대로 적용했습니다. 추가적으로 Next.js의 Image 컴포넌트를 처리하기 위해 storybook-addon-next를 설정했습니다.</p>
<ol start="2">
<li>주요 컴포넌트 작성
스토리북 도입 초기에는 가장 자주 사용하는 공통 컴포넌트부터 스토리를 작성하기 시작했습니다:</li>
</ol>
<ul>
<li>버튼(Button)</li>
<li>입력 필드(Input)</li>
<li>바텀 시트(BottomSheet)</li>
<li>공통 모달(CommonModal)</li>
<li>로딩 컴포넌트(Spinner)</li>
<li>탭(Tab)</li>
</ul>
<p>컴포넌트별로 다양한 상태를 시뮬레이션:</p>
<p>버튼의 disabled, loading, full 상태
입력 필드의 에러 메시지, 기본값 상태
공통 모달과 바텀시트의 상태 관리, 및 기본 컬러 차트</p>
<ol start="3">
<li>문서화 및 상태 관리
스토리북을 단순히 UI 테스트뿐만 아니라 문서화 도구로 활용하기 위해 Controls를 사용하여 Props를 동적으로 조작할 수 있도록 설정했습니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/0385bf47-4975-4c89-bdd3-383337c6a70f/image.png" alt="">
안티패턴인 SVG in js를 활용한 공통 컴포넌트의 활용, 필요한 클래스를 직접 작성 할 수 있는 </p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/a5c2555a-7ff4-46b1-a056-e402ca000ff8/image.png" alt="">
shadCn의 tweenMerge이용으로 컴포넌트를 다양한 스타일로 확장 시키게 사용하기 위해 노력하였습니다.</p>
<h3 id="도입-후-변화">도입 후 변화</h3>
<ol>
<li><p>컴포넌트 독립성 강화
스토리북을 통해 컴포넌트를 페이지와 분리하여 개발했기 때문에, 컴포넌트 하나만으로도 모든 동작을 확인할 수 있었습니다. 특히, 상태 관리(store) 의존성을 제거하여 컴포넌트를 더욱 독립적으로 만들 수 있었습니다.</p>
</li>
<li><p>디버깅 속도 향상
스토리북을 통해 각 컴포넌트를 빠르게 테스트하고, 문제를 해결할 수 있었습니다. </p>
</li>
</ol>
<p>예를 들어:
주소 입력 필드의 에러 메시지 렌더링 문제 해결
버튼의 상태 관리, 및 공통 컴포넌트를 활용환 프로젝트 획일화</p>
<ol start="3">
<li><p>문서화로 협업 효율성 향상
스토리북이 컴포넌트 사용법과 Props를 명확히 보여줬기 때문에, 프로젝트를 확장하거나 유지보수할 때 기억에 의존하지 않고 빠르게 이해할 수 있었습니다. 가이드 파일을 적용하여 팀원에게 스토리북에 있는 컴포넌트 사용법을 제공 하였고, 팀원들도 함께 사용하며 개선 할 점들을 서로 피드백 받아 함께 제작하였습니다.</p>
</li>
<li><p>디자인 시스템 구축 가능성
버튼, 입력 필드, 모달 등 공통 컴포넌트를 스토리북으로 관리하면서, 디자인 시스템 구축 가능성을 염두에 두게 되었습니다.</p>
</li>
</ol>
<h3 id="한계점-및-개선점">한계점 및 개선점</h3>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/45aec39d-69da-47ec-9c70-3dd57a0145c8/image.png" alt=""></p>
<ol>
<li><p>초기 설정 시간
스토리북을 Next.js와 통합하고 프로젝트 스타일에 맞추는 데 시간이 소요되었습니다. 특히 Tailwind CSS와 Next.js의 여러 컴포넌트를 처리하는 과정이 까다로웠습니다.
특히 윈도우와 맥북의 시스템 설정이 다른 부분이 있어 이를 처리하며 꽤 많은 초기 시간이 소요되었습니다.</p>
</li>
<li><p>페이지 기반 테스트 한계
스토리북은 컴포넌트 단위 테스트에 적합하지만, 페이지 단위의 상호작용 테스트에는 Cypress(현대 웹 애플리케이션을 테스트하기 위한 엔드투엔드 테스트(E2E) 프레임워크)와 같은 도구가 더 적합했습니다.</p>
</li>
</ol>
<h3 id="스토리북의-장점">스토리북의 장점</h3>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/f330c3f3-daa2-4894-9a5a-f004d8843dbd/image.png" alt=""></p>
<ol>
<li><p>컴포넌트 중심 개발
스토리북은 컴포넌트를 독립적으로 개발하고 테스트할 수 있는 환경을 제공합니다. 이는 특히 컴포넌트 기반 구조를 사용하는 Next.js와 같은 프레임워크에서 유용합니다. 프로젝트에서는 컴포넌트가 복잡하고 재사용 가능한 경우가 많기 때문에, 각각의 컴포넌트를 독립적으로 개발하고 테스트하기에 적합합니다.</p>
</li>
<li><p>UI 테스트 및 검증
SnapRoad처럼 UI 중심의 프로젝트에서는 디자인과 기능이 사용자의 경험에 큰 영향을 미칩니다. 스토리북을 통해 컴포넌트의 상태(예: 로딩, 성공, 오류)를 시각적으로 검토하고, 버그를 조기에 발견할 수 있습니다.</p>
</li>
<li><p>협업 향상
공통 프로젝트이기 때문에, 협업을 염두에 두면서, 디자이너나 팀원들에게 컴포넌트 상태를 스토리북으로 공유하여 피드백을 받을 수 있습니다. 스토리북은 문서 역할도 하기 때문에 컴포넌트의 사용법과 디자인 의도를 명확히 전달할 수 있습니다.</p>
</li>
<li><p>디자인 시스템 구축
프로젝트에서 사용하는 버튼, 폼, 모달 등과 같은 공통 UI 컴포넌트를 스토리북에 추가하면 디자인 시스템을 구축하는 데 도움을 줍니다. 마지막 프로젝트에서도 버튼, 이미지 슬라이더, 업로드 모달 등을 관리하고 테스트하기에 스토리북이 적합합니다.</p>
</li>
<li><p>컴포넌트 테스트 자동화
스토리북은 Visual Regression Testing(시각적 회귀 테스트) 도구와 함께 사용될 수 있습니다. 이는 UI 변경 사항이 의도치 않게 영향을 미치지 않도록 방지합니다.</p>
</li>
<li><p>생산성 향상
스토리북 환경에서 독립적으로 컴포넌트를 개발하면 개발 환경에서 직접 코드를 실행하지 않아도 됩니다. 마지막 프로젝트 처럼 복잡한 폼과 이미지 업로드 기능을 개발할 때, 각각의 상태를 바로 확인할 수 있어 시간과 노력을 줄일 수 있습니다.</p>
</li>
</ol>
<h3 id="향후-계획">향후 계획</h3>
<p>프로젝트에서 스토리북을 활용하여 더 나은 UI 개발 환경을 만들기 위해,</p>
<p>Visual Regression Testing 도입 예정 (UI 변경 시 자동 확인)
디자인 시스템 구축으로 컴포넌트 재사용성 극대화
더 복잡한 폼과 상태 관리 컴포넌트의 시뮬레이션 강화</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 개인정보 수집 유효기간]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4-%EC%88%98%EC%A7%91-%EC%9C%A0%ED%9A%A8%EA%B8%B0%EA%B0%84</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4-%EC%88%98%EC%A7%91-%EC%9C%A0%ED%9A%A8%EA%B8%B0%EA%B0%84</guid>
            <pubDate>Wed, 23 Oct 2024 00:26:05 GMT</pubDate>
            <description><![CDATA[<h2 id="✨-개인정보-수집-유효기간">✨ 개인정보 수집 유효기간</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/4922250c-911f-4e10-8eb6-f4cb42eb9f5f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/6f013e5a-a0f9-4704-9830-757c7c0f6e32/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/e038bcb9-2483-42ff-803e-c392c663863a/image.png" alt=""></p>
<h2 id="나의-풀이">나의 풀이</h2>
<pre><code class="language-javascript">function solution(today, terms, privacies) {
    // 날짜를 일 단위로 변환하는 함수 
    const toDays = (date) =&gt; {
        const [year, month, day] = date.split(&#39;.&#39;).map(Number);
        return year * 12 * 28 + month * 28 + day;
    }

    // today를 일 단위로 변환
    const todayDays = toDays(today);

    // 약관 종류에 따른 유효기간을 저장할 딕셔너리
    const termDict = {};
    terms.forEach((term)=&gt;{
        const [key, value] = term.split(&#39; &#39;);
        termDict[key] = Number(value);
    })

    // 파기해야 할 개인정보 번호를 저장할 배열
    let result = [];

    privacies.forEach((privacy, index) =&gt; {
        const [date, termType] = privacy.split(&#39; &#39;);
        // 수집된 날짜에 약관 유효기간을 더한 값
        const expireDays = toDays(date) + termDict[termType] * 28;
        // 유효기간이 지났으면 결과에 추가    
        if(expireDays &lt;= todayDays) {
            result.push(index + 1)
        }
    })
    return result;
}</code></pre>
<ol>
<li><p>toDays 함수: 날짜를 연도, 월, 일로 나누고 이를 일 단위로 변환하는 함수이다. 연도는 12개월, 한 달은 28일로 계산한다. 예를 들어, 1년은 12 * 28일로 처리된다. 이 함수는 2022.05.19 같은 날짜를 단순히 일수로 바꿔서 계산할 수 있게 해준다.</p>
</li>
<li><p>todayDays: 주어진 오늘 날짜를 일 단위로 변환한다. 이 값을 기준으로 개인정보의 유효기간이 지났는지 비교하게 된다.</p>
</li>
<li><p>termDict 객체: 약관 종류와 그에 해당하는 유효기간을 저장하는 객체이다. 예를 들어, A 6이 주어졌다면 A라는 약관의 유효기간은 6개월이 된다. 이를 termDict에 저장하여 나중에 약관 종류에 맞는 유효기간을 가져올 수 있게 한다.</p>
</li>
<li><p>result 배열: 파기해야 할 개인정보의 번호를 저장할 배열이다. 최종적으로 이 배열을 반환하게 된다.</p>
</li>
<li><p>privacies.forEach 반복문: 각 개인정보에 대해 수집 일자와 약관 종류를 분리하여 처리한다. 수집 일자를 toDays 함수를 통해 일수로 변환하고, 약관에 맞는 유효기간을 termDict에서 찾아서 이를 더한 후, 오늘 날짜와 비교하여 유효기간이 지났다면 해당 개인정보의 번호를 result에 추가한다.</p>
</li>
<li><p>expireDays: 수집된 날짜에 약관 유효기간을 더한 값을 의미한다. 이 값이 오늘 날짜보다 작거나 같다면 유효기간이 지난 것으로 판단하고 해당 개인정보 번호를 결과에 추가한다.</p>
</li>
<li><p>주어진 오늘 날짜와 각 개인정보의 수집 일자를 비교하여 유효기간이 지난 개인정보를 찾아내고 그 번호들을 오름차순으로 반환한다.</p>
</li>
</ol>
<h2 id="다른사람의-풀이">다른사람의 풀이</h2>
<pre><code class="language-javascript">// for문
function solution(today, terms, privacies) {
    let result = [];
    let obj1 = {};
    for(let e of terms){
        const [type, month] = e.split(&quot; &quot;);
        obj1[type] = month;
    }
    for(let i = 0; i &lt; privacies.length; i++){
        const[start, type] = privacies[i].split(&quot; &quot;);
        let now = new Date(start);
        let date = new Date(today);
        now.setMonth(now.getMonth() + Number(obj1[type]));
        if(date &gt;= now) result.push(i+1);
    }
    return result;
}</code></pre>
<p>result 배열: 파기해야 할 개인정보 번호를 저장할 배열이다. 최종적으로 이 배열을 반환한다.</p>
<p>obj1 객체: 약관 종류와 그에 해당하는 유효기간(개월 수)을 저장하는 객체이다. 약관 정보 배열인 terms에서 약관 종류(type)와 유효기간(month)을 분리하여 obj1에 저장한다. 예를 들어, terms가 [&quot;A 6&quot;, &quot;B 12&quot;]라면, obj1 객체는 { A: 6, B: 12 } 형태로 저장된다.</p>
<p>첫 번째 for 루프: terms 배열을 순회하면서 각 약관 정보를 객체 obj1에 저장한다. e.split(&quot; &quot;)을 사용해 약관 종류(type)와 유효기간(month)를 분리하고, 이를 객체의 키-값 쌍으로 저장한다.</p>
<p>두 번째 for 루프: privacies 배열을 순회하면서 각 개인정보 수집 일자와 약관 종류를 처리한다.</p>
<p>privacies[i].split(&quot; &quot;)를 사용해 개인정보 수집 일자(start)와 약관 종류(type)를 분리한다.</p>
<p>new Date(start)는 개인정보가 수집된 날짜를 Date 객체로 변환하고, new Date(today)는 오늘 날짜를 Date 객체로 변환한다.</p>
<p>now.setMonth(now.getMonth() + Number(obj1[type]))는 개인정보 수집 일자에 해당 약관의 유효기간(개월 수)을 더한다. 즉, 수집된 날짜에 약관에 따른 유효기간을 더한 새로운 날짜(now)를 계산한다.</p>
<p>날짜 비교: if (date &gt;= now)는 오늘 날짜(date)가 개인정보의 유효기간이 지난 날짜(now)보다 크거나 같은지 확인하는 조건이다. 만약 오늘 날짜가 유효기간을 지난 경우라면, 해당 개인정보는 파기 대상이 된다.</p>
<p>결과 배열에 추가: 유효기간이 지났다면 result 배열에 해당 개인정보 번호(i + 1)를 추가한다. 번호는 1부터 시작하기 때문에 인덱스 i에 1을 더해 추가한다.</p>
<p>최종 반환: 유효기간이 지난 개인정보 번호들이 담긴 result 배열을 반환한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 옹알이 (2)]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%98%B9%EC%95%8C%EC%9D%B4-2</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%98%B9%EC%95%8C%EC%9D%B4-2</guid>
            <pubDate>Tue, 22 Oct 2024 22:13:28 GMT</pubDate>
            <description><![CDATA[<h2 id="✨-옹알이-2">✨ 옹알이 (2)</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/0c54bd2d-e6f1-4570-820e-b3e68bc6fdab/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/594d703c-77bf-420e-bc02-7d2ec694ea6c/image.png" alt=""></p>
<h2 id="나의-풀이">나의 풀이</h2>
<pre><code class="language-javascript">function solution(babbling) {
    const canSay = [&quot;aya&quot;, &quot;ye&quot;, &quot;woo&quot;, &quot;ma&quot;];
    let answer = 0;

    //발음 가능한 단어 
    for(let i = 0; i &lt; babbling.length; i++){
        let babble = babbling[i];

        // 발음 가능 한 단어들 반복 시 (yeye) 문자열 유효 X 종료
        for(let j = 0; j &lt; canSay.length; j++){
            if(babble.includes(canSay[j].repeat(2))){
                break;
            }
            // 반복이 아닌 경우 발음 가능한 단어 문자열 제거
            babble = babble.split(canSay[j]).join(&quot; &quot;);
        }
        // 남은 부분이 빈 문자열일 때 +1
        if(babble.split(&quot; &quot;).join(&quot;&quot;).length === 0){
            answer += 1;
        }
    }

    return answer;
}</code></pre>
<h2 id="다른사람의-풀이">다른사람의 풀이</h2>
<pre><code class="language-javascript">function solution(babbling) {
    const babblables = [&quot;aya&quot;, &quot;ye&quot;, &quot;woo&quot;, &quot;ma&quot;];

    return babbling.reduce((possible, babbl, index) =&gt; {
        for (let i = 0; i &lt; babblables.length; i += 1) {
            if (babbl.includes(babblables[i].repeat(2))) return possible;
        }

        for (let i = 0; i &lt; babblables.length; i += 1) {
            babbl = babbl.split(babblables[i]).join(&#39; &#39;).trim();
        }

        if (babbl) return possible;

        return possible += 1;
    }, 0)
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] pr/issue template ]]></title>
            <link>https://velog.io/@yuna-c/TIL-prissue-template</link>
            <guid>https://velog.io/@yuna-c/TIL-prissue-template</guid>
            <pubDate>Mon, 21 Oct 2024 16:29:43 GMT</pubDate>
            <description><![CDATA[<pre><code>---
name: Bug Report Template
about: 버그 리포트 이슈 템플릿
title: &#39;BUG:&#39;
labels: bug
assignees: &#39;&#39;

---

## 어떤 버그인가요?

&gt; 어떤 버그인지 간결하게 설명해주세요

## 어떤 상황에서 발생한 버그인가요?

&gt; (가능하면) Given-When-Then 형식으로 서술해주세요

## 예상 결과

&gt; 예상했던 정상적인 결과가 어떤 것이었는지 설명해주세요

## 참고할만한 자료(선택)

_____________________________________________________

---
name: Feature Request Template
about: 기능 추가 이슈 템플릿
title: &#39;SETTING:&#39;
labels: &#39;&#39;
assignees: &#39;&#39;

---

## 어떤 기능인가요?

&gt; 추가하려는 기능에 대해 간결하게 설명해주세요

## 작업 상세 내용

- [ ] TODO
- [ ] TODO
- [ ] TODO

## 참고할만한 자료(선택)

_________________________________________________________
```bash
FEATURE
&quot;FEAT:&quot; - 기능 추가나 개선 사항을 나타낼 때
예: FEAT: 새로운 로그인 기능 추가

ENHANCEMENT
&quot;ENHAN:&quot; - 기존 기능의 개선 사항을 나타낼 때
예: ENHAN: 성능 최적화

&quot;FIX:&quot; - 버그 수정일 경우
예: FIX: 로그인 시 발생하는 오류 수정

&quot;SETUP:&quot; - 프로젝트 환경 설정과 관련된 경우
예: SETUP: ESLint 설정 추가

&quot;REFACTOR:&quot; - 리팩토링과 관련된 경우
예: REFACTOR: 코드 구조 개선

&quot;DOCS:&quot; - 문서 관련 변경 사항일 경우
예: DOCS: README 업데이트</code></pre><h2 id="🛂-연관된-커밋">🛂 연관된 커밋</h2>
<blockquote>
<p>ex) #7</p>
</blockquote>
<h2 id="️⃣연관된-이슈">#️⃣연관된 이슈</h2>
<blockquote>
<p>ex) #이슈번호, #이슈번호</p>
</blockquote>
<h2 id="📝작업-내용">📝작업 내용</h2>
<blockquote>
<p>이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)</p>
</blockquote>
<h3 id="스크린샷-선택">스크린샷 (선택)</h3>
<h2 id="💬리뷰-요구사항선택">💬리뷰 요구사항(선택)</h2>
<blockquote>
<p>리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요</p>
<p>ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?</p>
</blockquote>
<p>```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 바탕화면 정리]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%B0%94%ED%83%95%ED%99%94%EB%A9%B4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%B0%94%ED%83%95%ED%99%94%EB%A9%B4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 14 Oct 2024 00:38:18 GMT</pubDate>
            <description><![CDATA[<h2 id="✨-바탕화면-정리">✨ 바탕화면 정리</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/09b4a26d-d82c-443a-a4b3-61be370ad122/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/69dde814-4789-48d0-ad9e-4d43f1ee4035/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/36d6991f-a748-464c-8b06-702122ae9196/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/1d978980-9e71-4d4d-8592-22a6d41bfcd0/image.png" alt=""></p>
<h2 id="나의-풀이">나의 풀이</h2>
<pre><code class="language-javascript">function solution(wallpaper) {
    // 위치 변수
    let lux = wallpaper.length; // 맨위
    let luy = wallpaper[0].length; // 맨왼쪽
    let rdx = 0; // 맨아래
    let rdy = 0; // 맨오른

    // 최소 범위 파일 위치
    for(let i = 0; i &lt; wallpaper.length; i++){
        for(let j = 0; j &lt; wallpaper[i].length; j++){
            if(wallpaper[i][j] === &#39;#&#39;){
                lux = Math.min(lux, i);
                luy = Math.min(luy, j);
                rdx = Math.max(rdx, i);
                rdy = Math.max(rdy, j);
            }
        }
    }
    // 맨 아래 파일 포함 끝점
    return [lux, luy, rdx + 1, rdy + 1];
}</code></pre>
<h2 id="다른사람의-풀이">다른사람의 풀이</h2>
<pre><code class="language-javascript">function solution(wallpaper) {
    let left = [];
    let top = [];
    let right = []
    let bottom = [];
    wallpaper.forEach((v,i) =&gt; {
        [...v].forEach((val,ind) =&gt; {
            if(val === &quot;#&quot;) {
                left.push(i)
                top.push(ind)
                right.push(i + 1)
                bottom.push(ind + 1)
            }
        })
    })
    return [Math.min(...left), Math.min(...top), Math.max(...right), Math.max(...bottom)]
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 성격 유형 검사하기]]></title>
            <link>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%84%B1%EA%B2%A9-%EC%9C%A0%ED%98%95-%EA%B2%80%EC%82%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yuna-c/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%84%B1%EA%B2%A9-%EC%9C%A0%ED%98%95-%EA%B2%80%EC%82%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 13 Oct 2024 11:05:10 GMT</pubDate>
            <description><![CDATA[<h2 id="✨-성격-유형-검사하기">✨ 성격 유형 검사하기</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/5399fd11-dccc-42dd-a0fb-da17fd68abe2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/124290f5-4d69-46de-9120-ab8e8aeee347/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/e8058ea2-3d79-416a-95d3-4d74b555c9ff/image.png" alt=""></p>
<h2 id="나의-풀이">나의 풀이</h2>
<pre><code class="language-javascript">function solution(survey, choices) {
     const 스코어들 = {
        R: 0, T: 0,
        C: 0, F: 0,
        J: 0, M: 0,
        A: 0, N: 0,
    }

    // 테스트지 점수
    choices.forEach((choice, idx) =&gt; {
        const [비동의, 동의] = survey[idx]
        // 비동의 점수 더하기
        if(choice &lt; 4) 스코어들[비동의] += (4 - choice);
        // 동의 점수 더하기
        if(choice &gt; 4) 스코어들[동의] += (choice -4)
    });

    const result = [
        스코어들.R &gt;= 스코어들.T ? &#39;R&#39;:&#39;T&#39;,
        스코어들.C &gt;= 스코어들.F ? &#39;C&#39;:&#39;F&#39;,
        스코어들.J &gt;= 스코어들.M ? &#39;J&#39;:&#39;M&#39;,
        스코어들.A &gt;= 스코어들.N ? &#39;A&#39;:&#39;N&#39;
    ].join(&#39;&#39;)

    return result;
}</code></pre>
<p>mbti페이지를 만들 때 사용 했던 로직과 비슷하게 풀이 함 </p>
<h2 id="다른사람의-풀이">다른사람의 풀이</h2>
<pre><code class="language-javascript">function solution(survey, choices) {
    const MBTI = {};
    const types = [&quot;RT&quot;,&quot;CF&quot;,&quot;JM&quot;,&quot;AN&quot;];

    types.forEach((type) =&gt;
        type.split(&#39;&#39;).forEach((char) =&gt; MBTI[char] = 0)
    )

    choices.forEach((choice, index) =&gt; {
        const [disagree, agree] = survey[index];

        MBTI[choice &gt; 4 ? agree : disagree] += Math.abs(choice - 4);
    });

    return types.map(([a, b]) =&gt; MBTI[b] &gt; MBTI[a] ? b : a).join(&quot;&quot;);
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] Warning: Extra attributes from the server: class,style at html at RootLayout (Server)]]></title>
            <link>https://velog.io/@yuna-c/TIL-Warning-Extra-attributes-from-the-server-classstyle-at-html-at-RootLayout-Server</link>
            <guid>https://velog.io/@yuna-c/TIL-Warning-Extra-attributes-from-the-server-classstyle-at-html-at-RootLayout-Server</guid>
            <pubDate>Sun, 13 Oct 2024 11:00:40 GMT</pubDate>
            <description><![CDATA[<h2 id="extra-attributes-from-the-server-classstyle-at-html-at-rootlayout">Extra attributes from the server: class,style at html at RootLayout</h2>
<p><img src="https://velog.velcdn.com/images/yuna-c/post/5c3024d0-0736-4544-8a90-84ce81e284ea/image.png" alt=""></p>
<pre><code class="language-javascript">app-index.tsx:25 Warning: Extra attributes from the server: class,style
at html
at RootLayout (Server)
at RedirectErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/redirect-boundary.js:74:9)
at RedirectBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/redirect-boundary.js:82:11)
at NotFoundErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/not-found-boundary.js:76:9)
at NotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/not-found-boundary.js:84:11)</code></pre>
<p>next-theme 설치 후 provider 전역 적용 후 난 에러 
전체 파일을 다 뒤져서 검사 하던 중, root layout.tsx 파일의 파일에 </p>
<pre><code class="language-html"> &lt;html lang=&quot;en&quot; suppressHydrationWarning&gt;
      &lt;body className={`${spaceGrotesk.variable} ${geistSans.variable} ${geistMono.variable} antialiased`}&gt;
        &lt;ThemeProvider attribute=&quot;class&quot; defaultTheme=&quot;dark&quot; enableSystem disableTransitionOnChange&gt;
          {children}
        &lt;/ThemeProvider&gt;
      &lt;/body&gt;
    &lt;/html&gt;</code></pre>
<p>suppressHydrationWarning 적용 
shadcn의 가이드를 제대로 읽어 보지 않아 생긴 에러 </p>
<p>참고 : <a href="https://ui.shadcn.com/docs/dark-mode/next">https://ui.shadcn.com/docs/dark-mode/next</a></p>
]]></description>
        </item>
    </channel>
</rss>