<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>mi__xx2.log</title>
        <link>https://velog.io/</link>
        <description>기록을 좋아하는 프론트엔드 개발자입니다.</description>
        <lastBuildDate>Tue, 03 Sep 2024 18:25:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>mi__xx2.log</title>
            <url>https://velog.velcdn.com/images/mi__xx2/profile/937e5548-bb15-4dee-ab13-e118157f2df5/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. mi__xx2.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/mi__xx2" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[오피스너] 기술 스택 설명 및 3일차 학습 방향]]></title>
            <link>https://velog.io/@mi__xx2/%EC%98%A4%ED%94%BC%EC%8A%A4%EB%84%88-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%83%9D-%EC%84%A4%EB%AA%85-%EB%B0%8F-3%EC%9D%BC%EC%B0%A8-%ED%95%99%EC%8A%B5-%EB%B0%A9%ED%96%A5</link>
            <guid>https://velog.io/@mi__xx2/%EC%98%A4%ED%94%BC%EC%8A%A4%EB%84%88-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%83%9D-%EC%84%A4%EB%AA%85-%EB%B0%8F-3%EC%9D%BC%EC%B0%A8-%ED%95%99%EC%8A%B5-%EB%B0%A9%ED%96%A5</guid>
            <pubDate>Tue, 03 Sep 2024 18:25:55 GMT</pubDate>
            <description><![CDATA[<p>팀원들의 프로젝트 이해도를 향상시키기 위해, 오피스너 웹 버전의 기술스택에 대해 각각 간단히 설명을 해보고 학습 방향을 정해보겠습니다.</p>
<h2 id="core-nextjs-app-router">[Core] Next.js (App Router)</h2>
<hr>
<blockquote>
<p>Next.js를 사용하는 주된 이유는 SSR을 구현하기 위함 입니다. 오피스너 웹 버전에서는 건물명을 검색했을 때 사용자들이 유입될 수 있어야 합니다. 이를 위해서는 SEO 최적화가 필요하므로 Next.js 사용이 보다 유리합니다.</p>
</blockquote>
<ul>
<li>React 기반의 프레임워크로, 서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG)을 쉽게 처리할 수 있어서 검색 엔진 최적화(SEO)에 유리합니다.</li>
<li>초기 페이지 로딩에 대해서는 SSR, SSG 방식으로 동작시킬 수가 있고, 이후의 요청에 대해서는 기본적으로 CSR 방식으로 동작하기 때문에 SSR 방식의 장점인 SEO에 유리하면서도 CSR의 장점을 활용할 수 있습니다.
<br><br></li>
</ul>
<h2 id="styling-tailwindcss">[Styling] TailwindCSS</h2>
<hr>
<blockquote>
<p>TailwindCSS는 스타일링을 쉽게 하기 위한 CSS 프레임워크입니다.</p>
</blockquote>
<ul>
<li>유틸리티 기반 CSS 프레임워크로, 제공되는 CSS 클래스들을 이용해서 빠르게 웹 페이지의 스타일을 지정할 수 있습니다.</li>
<li>쉽게 말하자면, 디자인의 기본 재료들을 제공하는 도구라고 할 수 있습니다.</li>
<li>페이지의 레이아웃이나 텍스트, 색상, 여백 등을 세밀하게 조정해야할 때 사용합니다. 예를 들어, 버튼의 크기, 색깔, 배경색 등을 조정할 수 있습니다.</li>
<li>다만 클래스를 남용하면 코드가 복잡해 보일 수 있어서, 일관된 구조로 작성하는 것이 좋습니다.
<br><br></li>
</ul>
<h2 id="styling-radix-ui">[Styling] Radix UI</h2>
<hr>
<blockquote>
<p>Radix UI는 Headless UI 라이브러리로, 쉽게 말하자면 디자인 없이 기능만 제공하는 UI 라이브러리입니다. 사용자가 원하는 스타일로 쉽게 커스터마이징 할 수 있습니다.</p>
</blockquote>
<ul>
<li>Radix UI 공식 문서를 살펴보면, <strong>&quot;Core building blocks for your design system”이</strong>라는 문구를 확인할 수 있습니다. 해석해보면, “나만의 디자인 시스템을 만들기 위한 핵심 빌딩 블록”이라는 의미겠네요.</li>
<li>Radix UI는 기본적인 UI 구성 요소를 제공하면서도 스타일링과 디자인은 제공하지 않습니다. 즉, UI 요소의 구조와 동작은 제공하지만, 스타일링은 개발자가 직접 지정할 수 있습니다.</li>
<li>따라서 반응형에 의해 디자인이 달라지거나, 기능 변경이나 추가가 많이 발생한다면 Headless가 유지보수에 더 유리하겠네요.</li>
<li>웹 접근성을 보장하는 UI 라이브러리라서, 디자인 시스템을 별도로 사용하면서도 접근성을 보장할 수 있다는 장점이 있습니다.<br>

</li>
</ul>
<h3 style="color:SteelBlue">왜 Headless UI 라이브러리가 필요할까?</h3>

<ul>
<li>Material UI나 Bootstrap과 같은 UI 라이브러리들은 각자만의 스타일이 입혀져 있기 때문에 회사에서 설계한 디자인과 다른 경우들이 많습니다. 오피스너의 경우에도 오피스너만의 디자인이 설계되어 있죠.</li>
<li>이러한 UI 라이브러리의 테마를 커스터마이징해서 문제를 해결할 수 있지만, 완벽하게 원하는 디자인으로 만드는 작업이 다소 번거로울 수 있습니다.</li>
<li>그렇다고 개발자가 처음부터 다 만들기엔 웹 접근성을 준수하며 개발하기가 어렵습니다.</li>
<li>그래서 이런 니즈들로 인해, 접근성을 보장하면서 기능을 제공하는 Headless UI 라이브러리가 등장하게 됐습니다.
<br><br></li>
</ul>
<h2 id="styling-shadcnui">[Styling] shadcn/ui</h2>
<hr>
<blockquote>
<p>기능(Radix UI)과 디자인(TailwindCSS)이 잘 결합된 코드 모음(컴포넌트)이라고 생각하면 됩니다.</p>
</blockquote>
<ul>
<li>다시 한번 말하자면, Radix UI에 TailwindCSS의 스타일링을 곁들여, 가공된 컴포넌트로 제공하는 UI 모음집(<strong>Component Collection</strong>)입니다.</li>
<li>라이브러리가 아니기 때문에, npm 패키지 형태가 아닌, 컴포넌트를 복사 붙여넣기 하는 제공 방식을 택하고 있습니다. (패키지 매니저 사용이 아닌 npx 명령어 사용)</li>
<li>왜 복붙 방식을 하느냐면, 커스터마이징 자유도를 높이기 위해서라고 생각하면 될 것 같습니다. 또한 의존성을 주입시키지 않음으로써 불필요한 번들 크기 증가를 막을 수 있습니다.
<br><br></li>
</ul>
<h2 id="state-management-react-query">[State Management] React Query</h2>
<hr>
<blockquote>
<p><strong>서버 상태 관리</strong>에 사용되는 라이브러리로, 비동기 데이터(fetching)를 쉽게 관리 할 수 있도록 도와줍니다. 즉, 서버에서 가져오는 데이터를 효율적으로 관리하기 위해 사용합니다.</p>
</blockquote>
<ul>
<li>서버에서 데이터를 가져오고(API 요청 결과), 캐싱하고, 재요청(동기화)하는 작업들을 쉽게 처리할 수 있습니다.</li>
<li>데이터를 불필요하게 너무 자주 요청하지 않도록 설정하고, 캐싱이 필요한 시점에만 적절히 캐싱을 사용해 성능을 최적화하는 것이 중요합니다.
<br><br></li>
</ul>
<h2 id="state-management-zustand">[State Management] Zustand</h2>
<hr>
<blockquote>
<p><strong>클라이언트 상태 관리</strong>를 위해 사용되는 가벼운 전역 상태 관리 라이브러리입니다.</p>
</blockquote>
<ul>
<li>테마(라이트/블랙 모드)나 로그인 상태, 사용자 정보와 같은 데이터를 여러 컴포넌트에서 공유해야 할 때 사용합니다.</li>
<li>상태 관리가 너무 복잡해지지 않도록 하는 것이 중요하고, 컴포넌트가 필요 이상으로 상태를 구독하지 않도록 신경써야합니다.</li>
</ul>
<p><br><br></p>
<h2 id="🔥-학습-방향">🔥 학습 방향</h2>
<hr>
<ol>
<li>next-auth를 사용하여 로그인 기능 구현을 할 예정이므로 이에 대한 학습이 필요할 것 같습니다.</li>
<li>로그인/회원가입 기능 구현 시 유효성 검사를 효율적으로 하기 위해 React Hook Form 도입을 고려해봐야 할 것 같습니다. React Hook Form과 Zod를 함께 사용하면 폼 제출 시 유효성 검사를 효율적으로 수행할 수 있을 것 같습니다.</li>
<li>React Query의 경우 캐싱 기능을 제공하는데, 무분별한 캐싱은 되려 성능 저하를 발생시키므로 React Query를 학습하며 캐싱 처리에 대해 생각해보면 좋을 것 같습니다.</li>
<li>Zustand를 사용할 때, 클라이언트 상태 관리가 필요한 정보가 뭘지 생각해보며 개발하면 좋을 것 같습니다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[VSCode에서 추천하는 Extensions]]></title>
            <link>https://velog.io/@mi__xx2/VSCode%EC%97%90%EC%84%9C-%EC%B6%94%EC%B2%9C%ED%95%98%EB%8A%94-Extensions</link>
            <guid>https://velog.io/@mi__xx2/VSCode%EC%97%90%EC%84%9C-%EC%B6%94%EC%B2%9C%ED%95%98%EB%8A%94-Extensions</guid>
            <pubDate>Thu, 18 Jul 2024 08:26:37 GMT</pubDate>
            <description><![CDATA[<p>이번 포스트에서는 VSCode에서 유용하게 사용할 수 있는 몇 가지 extensions를 소개하겠습니다.</p>
<h2 id="1-live-server">1. Live Server</h2>
<p>웹 개발을 하면서 HTML, CSS, JS 파일을 수정할 때마다 브라우저를 새로고침해야 하는 번거로움이 있습니다. Live Server는 이를 해결하기 위한 확장 기능으로, 파일을 저장할 때마다 자동으로 브라우저를 새로고침해줍니다.</p>
<h2 id="2-prettier">2. Prettier</h2>
<p>코드 포맷팅은 일관된 코드 스타일을 유지하고 가독성을 높이기 위해 중요합니다. Prettier는 다양한 프로그래밍 언어에 대해 일관된 코드 포맷팅을 제공하는 확장 기능입니다.</p>
<h2 id="3-eslint">3. ESLint</h2>
<p>ESLint는 자바스크립트 코드에서 일반적인 오류를 검사하고, 코드 스타일 가이드를 준수하는지 검사하는 확장 기능입니다. 코드의 품질을 높이고 유지 보수성을 높이기 위해 ESLint를 사용하는 것이 좋습니다.</p>
<h2 id="4-gitlens">4. GitLens</h2>
<p>GitLens는 Git 저장소의 커밋 기록을 시각화하고, 각 커밋에 대한 정보를 제공하는 확장 기능입니다. 코드 변경 내역을 빠르게 확인할 수 있어, 협업하면서 코드를 관리할 때 유용합니다.</p>
<h2 id="5-material-icon-theme">5. Material Icon Theme</h2>
<p>VSCode는 파일의 확장자에 따라 아이콘을 제공합니다. 하지만 이 아이콘은 파일의 종류를 구분하기에는 충분하지 않을 때가 있습니다. Material Icon Theme은 다양한 아이콘을 제공하여 파일의 종류를 더 쉽게 구분할 수 있게 해줍니다.</p>
<h2 id="6-bracket-pair-colorizer">6. Bracket Pair Colorizer</h2>
<p>소스 코드에서 괄호의 쌍이 맞아야 하는 경우가 많습니다. 이때 Bracket Pair Colorizer는 괄호의 쌍을 쉽게 확인할 수 있도록 쌍을 색상으로 강조해주는 확장 기능입니다.</p>
<h2 id="7-remote-development">7. Remote Development</h2>
<p>Remote Development는 VSCode를 사용하여 원격 서버나 도커 컨테이너, WSL 등의 환경에서 개발할 수 있게 해주는 확장 기능입니다. 로컬에서 개발하지 않아도 되므로 환경 설정 등의 번거로움을 줄여주며, 원격 개발 환경에서도 VSCode의 모든 기능을 사용할 수 있습니다.</p>
<h2 id="8-better-comments">8. Better Comments</h2>
<p>주석은 코드의 이해를 돕기 위해 작성되지만, 일반적인 주석은 그저 글씨로 보여지기 때문에 가독성이 떨어지는 경우가 많습니다. Better Comments는 주석의 가독성을 높이기 위한 다양한 스타일을 제공하는 확장 기능입니다.</p>
<h2 id="9-auto-close-tag">9. Auto Close Tag</h2>
<p>HTML이나 XML 같은 마크업 언어에서 태그를 작성할 때, 시작 태그와 종료 태그를 일일히 작성하는 것은 번거로운 작업입니다. Auto Close Tag는 시작 태그를 작성할 때 자동으로 종료 태그를 추가해주는 확장 기능입니다.</p>
<h2 id="10-code-runner">10. Code Runner</h2>
<p>특정 파일에서 코드를 실행하고 결과를 확인하는 것은 프로그래밍 공부를 할 때 유용합니다. Code Runner는 VSCode에서 다양한 언어로 작성된 코드를 바로 실행할 수 있도록 지원하는 확장 기능입니다.</p>
<h2 id="11-live-share">11. Live Share</h2>
<p>Live Share는 원격에서 실시간으로 코드를 공유하고, 페어 프로그래밍을 할 수 있도록 지원하는 확장 기능입니다. 같은 코드를 함께 작성하면서 피드백을 주고받아 개발 생산성을 높일 수 있습니다.</p>
<h2 id="12-code-spell-checker">12. Code Spell Checker</h2>
<p>코드를 작성할 때 철자 오류가 발생할 수 있습니다. Code Spell Checker는 영어 철자 검사를 지원하는 확장 기능으로, 코드를 작성하는 도중 오타를 찾아주고 수정할 수 있습니다.</p>
<h2 id="13-intellisense-for-css-scss-less">13. IntelliSense for CSS, SCSS, Less</h2>
<p>CSS, SCSS, Less 같은 스타일 시트 언어에서 클래스, ID, 속성 등을 자동 완성해주는 IntelliSense 기능을 제공하는 확장 기능입니다.</p>
<h2 id="14-rest-client">14. REST Client</h2>
<p>REST API를 테스트하거나 디버깅할 때 유용한 REST Client는 VSCode에서 HTTP 요청을 보내고 응답을 받을 수 있는 확장 기능입니다.</p>
<h2 id="15-indent-rainbow">15. Indent Rainbow</h2>
<p>들여쓰기를 시각적으로 확인할 수 있도록 색상을 입혀주는 Indent Rainbow는 코드 가독성을 높이는데 도움을 주는 확장 기능입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프론트엔드 기술 선정과 그 이유] 상태 관리 라이브러리]]></title>
            <link>https://velog.io/@mi__xx2/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B8%B0%EC%88%A0-%EC%84%A0%EC%A0%95%EA%B3%BC-%EA%B7%B8-%EC%9D%B4%EC%9C%A0-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</link>
            <guid>https://velog.io/@mi__xx2/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B8%B0%EC%88%A0-%EC%84%A0%EC%A0%95%EA%B3%BC-%EA%B7%B8-%EC%9D%B4%EC%9C%A0-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</guid>
            <pubDate>Fri, 02 Feb 2024 08:31:10 GMT</pubDate>
            <description><![CDATA[<h2 id="react-query-vs-redux">React Query vs Redux</h2>
<blockquote>
<p>💡 <strong>React Query</strong></p>
</blockquote>
<ul>
<li>서버 데이터 관리하기에 적합하다.</li>
<li>데이터 Fetching, 캐싱, 동기화, 서버 데이터 업데이트 등을 쉽게 만들 수 있다.</li>
<li>Redux에 비해 보일러 플레이트가 적다.</li>
<li><code>캐싱 처리</code>가 간단해진다.</li>
<li>다양한 옵션을 사용 가능하다. (Redux에서는 직접 로직을 작성해야 한다.)</li>
<li><code>onSuccess/onError</code> 핸들링 간편하게 처리 가능</li>
<li>React Query로 서버 데이터를 관리한다면, 이외 클라이언트 데이터를 관리하기 위한 <code>상태 관리 라이브러리</code>를 추가 사용 필요<br/>

</li>
</ul>
<blockquote>
<p>💡 <strong>Redux</strong></p>
</blockquote>
<ul>
<li>클라이언트 데이터 관리하기에 적합하다.</li>
<li>서버 상태를 관리하기 위해서는 비동기 처리를 위한 추가 라이브러리 필요</li>
<li>서버 데이터를 관리하기 위한 로직이 과도하게 커지고, 보일러 플레이트가 비대해진다.<br/>

</li>
</ul>
<p>서버 데이터 관리로 <strong>React Query</strong>를 사용하고 클라이언트 측에서 전역 데이터 관리 필요
<br/></p>
<blockquote>
<p>💡 클라이언트 전역 데이터 관리 : <strong>Recoil vs 다른 상태관리 방법</strong></p>
</blockquote>
<ul>
<li><p>React <code>useState</code></p>
<ul>
<li>상태를 공유해 주는 부모 컴포넌트에서 prop drilling을 하는 방법은 불편하다.</li>
<li>하나의 공통 prop이 변경될 때마다 re-rendering 해야 하므로 많은 양의 데이터는 처리할 수 없다.</li>
</ul>
</li>
<li><p>React <code>useContext</code></p>
<ul>
<li>각 상태는 Context Provider가 필요한데, 상태가 1000개라면 1000개의 Provider를 선언해야 한다. (Wrapper 지옥…) → Recoil 은 Atom을 동적으로 생성할 수 있다.</li>
<li>상태가 변경될 때마다 캐싱 없이 매번 re-rendering 한다.</li>
</ul>
</li>
<li><p>Redux</p>
<ul>
<li>전역 상태를 수정하기 위해서는 반드시 액션을 선언해서 수행해야 하므로 데이터의 흐름을 좀 더 쉽게 예측 가능하다.</li>
<li>좋은 개발자 도구가 있어서 문제가 생긴 경우 빠르게 어느 시점에 무슨 컴포넌트가 어떤 데이터를 변경하는지 확인할 수 있다.</li>
<li>RTK(Redux Tool Kit)를 사용하면 Reducer를 통한 데이터 업데이트시 RTK가 Immer를 사용하여 불변성을 지켜준다.</li>
<li>초기 구축, 유지 보수 과정에서 boilerplate 코드를 많이 작성해야 한다.</li>
<li>React concurrent renderer를 지원하지 않는다.</li>
</ul>
</li>
<li><p>Recoil이 해결하는 문제: 손쉬운 전역 상태관리</p>
<ul>
<li>Recoil의 상태를 구독, 업데이트하는 API는 간단하고 안정적이다.</li>
<li>Recoil에서 하나의 전역 상태는 Atom이라고 부른다.</li>
<li>Atom은 어느 하나의 컴포넌트에 묶여있지 않고, 모든 컴포넌트에서 접근 가능한 하나의 그래프라고 이해하면 쉽다.</li>
</ul>
</li>
<li><p>Recoil의 손쉬운 전역상태 사용 방법</p>
<ul>
<li>아래의 아주 간단한 코드만으로 Recoil을 React에 적용하고 상태를 구독, 변경하는 것이 가능하다.</li>
<li>이 단순함과 안정성이 Recoil의 최대 장점</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">// app.js: Recoil Root Store 선언
function App() {
  return (
    &lt;RecoilRoot&gt;
      &lt;Child /&gt;
    &lt;/RecoilRoot&gt;
  );
}

// store.js: Recoil Atom 선언
const globalState = atom({
  key: &#39;globalState&#39;,
  default: &#39;blah&#39;,
});

// Child.jsx: 자식 컴포넌트에서 atom 구독, 변경.
function Child() {
  const [globalState, setGlobalState] = useRecoilState(globalState);
  const onChange = (event) =&gt; {
    setGlobalState(event.target.value);
  };
  return (
    &lt;div&gt;
      &lt;input value={text} onChange={onChange} /&gt;
      Output: {text}
    &lt;/div&gt;
  );
}</code></pre>
<br/>

<blockquote>
<p>✅ 결론 : React Query  + Recoil</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프론트엔드 기술 선정과 그 이유] JavaScript vs TypeScript]]></title>
            <link>https://velog.io/@mi__xx2/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B8%B0%EC%88%A0-%EC%84%A0%EC%A0%95%EA%B3%BC-%EA%B7%B8-%EC%9D%B4%EC%9C%A0-JavaScript-vs-TypeScript</link>
            <guid>https://velog.io/@mi__xx2/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B8%B0%EC%88%A0-%EC%84%A0%EC%A0%95%EA%B3%BC-%EA%B7%B8-%EC%9D%B4%EC%9C%A0-JavaScript-vs-TypeScript</guid>
            <pubDate>Fri, 02 Feb 2024 08:25:04 GMT</pubDate>
            <description><![CDATA[<p>Javascript와 Typescript의 가장 핵심적인 차이는 type이 있느냐 없느냐이다.</p>
<pre><code class="language-jsx">// JS
let a = 1;
let b = 2;

let c = a + b;</code></pre>
<pre><code class="language-tsx">// TS
let a: number = 1;
let b: number = 2;

let c: number;
c = a + b;</code></pre>
<br/>

<blockquote>
<p>💡 <strong>TS 장점</strong> - type이 있음으로써 좋아지는 점?</p>
</blockquote>
<ol>
<li>런타임 에러 vs 컴파일 에러</li>
</ol>
<pre><code class="language-jsx">// JS
const plus = (a, b) =&gt; {
    let c = a + b; // 1.runtime err 발생
    return c;
};

let a = 1;
let b = &#39;aa&#39;;

console.log(&#39;a + b = &#39; + plus(a, b));</code></pre>
<pre><code class="language-tsx">// TS
const plus = (a: number, b: number): number =&gt; {
    let c: number = a + b;
    return c;
};

let a: number = 1;
let b: number = &#39;aa&#39;; // 2. compile err 발생

console.log(&#39;a + b = &#39; + plus(a, b));</code></pre>
<blockquote>
<p>type을 명시함으로써 컴파일 단에서 미리 선언될 수 없는 변수를 차단</p>
</blockquote>
<br/>

<ol start="2">
<li>IDE와의 결합성</li>
</ol>
<ul>
<li>TS는 JS보다 IDE와의 결합성이 훨씬 좋다.</li>
<li>TS는 ctrl + click으로 웬만한 class와 fuction을 쉽게 역추적 할 수 있다.</li>
<li>물론 JS도 IDE에서 해당 기능을 제공하지만 쉽게 역추적 되지 않는 경우가 많다.</li>
<li>그럴땐 find를 통해서 찾아야만 한다. 클릭 한번, 타자 한번이 쌓여서 시간을 잡아먹는다.</li>
</ul>
<ol start="3">
<li>가독성</li>
</ol>
<ul>
<li>TS는 자료형을 명시함으로써 어떤형의 데이터가 들어가고 어떤 형의 data가 return 되는지 쉽게 알 수 있다.</li>
<li>JS의 경우에는 따로 설명이 되어 있지 않거나 네이밍이 이상할 경우, 직접 들어가서 코드를 읽는 수 밖에 없다.</li>
<li>또한 JS의 input이나 return 값이 object일 경우는 코드를 읽기가 더 힘들어진다.</li>
<li>하지만 TS는 interface로 type을 새로 선언함으로써 어떤 데이터가 들어가고 어떤 데이터가 return 되는지 좀더 쉽게 파악이 가능하다. 즉, 가독성이 좋아진다.</li>
</ul>
<br/>

<blockquote>
<p>💡 <strong>JS 장점</strong> - 하지만 JS가 좋을 수도 있잖아?</p>
</blockquote>
<ol>
<li>유연성 &amp; 생산성</li>
</ol>
<pre><code class="language-jsx">// JS
let a = &#39;string&#39;;
let b = 2;
a = 1;

let c = a + b;
console.log(&#39;a + b = &#39;, c);</code></pre>
<pre><code class="language-tsx">// TS
let a: string = &#39;string&#39;;
let b: number = 2;
a = 1;

let c: number = a + b;
console.log(&#39;a + b = &#39;, c);</code></pre>
<ul>
<li>위 코드를 보면 알 수 있듯이 JS는 a라는 변수를 자료형을 바꿔가면 사용하는게 가능하다. TS는 불가능하다.</li>
<li>또한 number나 string이 기본 자료형이 아닌 object같이 복합 자료형일 경우엔 TS는 선언해줘야 할 게 정말 많다.</li>
<li>그래서 코드 작성을 유연하게 하지 못할 때가 정말 많다.</li>
<li>JS라면 3줄이면 될 것을 TS는 10줄 또는 그 이상을 작성해야 할 때도 있다.<br/>

</li>
</ul>
<table>
<thead>
<tr>
<th></th>
<th>JavaScript</th>
<th>TypeScript</th>
</tr>
</thead>
<tbody><tr>
<td>장점</td>
<td>코드 작성이 유연하다.<br/>빠른 코딩이 가능하다.</td>
<td>컴파일 시 에러를 잡을수 있다.<br/>IDE와 결합성이 좋다.<br/>코드 가독성이 좋다.</td>
</tr>
<tr>
<td>단점</td>
<td>프로그램이 알수 없는 에러로 죽곤 한다.<br/>코드 가독성이 비교적 떨어진다.<br/>IDE와 결합성이 비교적 떨어진다.</td>
<td>코드 작성에 제약이 많다.<br/>코드 작성에 시간이 많이 걸린다.<br/>여러가지 설정이 좀 많다.</td>
</tr>
</tbody></table>
<br/>
<br/>

<blockquote>
<p>✅ <strong>결론</strong> - TypeScript를 쓰자</p>
</blockquote>
<ul>
<li>컴파일로 많은 에러를 사전에 예방할 수 있고 가독성이 좋다.</li>
<li>협업 시에 서로 빠르게 코드를 이해할 수 있다.</li>
<li>JS의 장점인 <strong>유연성</strong>과 <strong>생산성</strong>은 TS의 any 자료형을 사용함으로써 어느정도 해결이 가능하다.</li>
<li>any는 어떤 자료형이든 허용하기 때문에 경우에 따라 유연하게 사용할 수도 있고 제약적으로 사용할 수도 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 1654. 랜선 자르기]]></title>
            <link>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-1654.-%EB%9E%9C%EC%84%A0-%EC%9E%90%EB%A5%B4%EA%B8%B0</link>
            <guid>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-1654.-%EB%9E%9C%EC%84%A0-%EC%9E%90%EB%A5%B4%EA%B8%B0</guid>
            <pubDate>Fri, 05 Jan 2024 05:38:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/1654">1654번: 랜선 자르기</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>이 문제를 주어진 랜선들을 같은 길이로 잘라 <code>n</code> 개 이상으로 만들 수 있는 길이의 최댓값을 구하는 문제다.</li>
<li>이 길이를 구하기 위해 길이의 범위를 설정하자.</li>
<li>자르는 길이는 자연수이고 랜선들의 최댓값보다 커질 수 없으므로 범위는 <code>(1, max(lanList))</code> 이다.</li>
<li>탐색 범위의 중간값인 <code>mid</code> 를 기준으로 하여 범위를 반씩 좁혀나가며 탐색을 진행한다.</li>
<li><code>mid</code> 로 랜선들을 자른 개수의 총합이 <code>n</code> 이상이면 <code>low</code>의 위치를 <code>mid</code> 의 오른쪽으로 보내주고, 자른 길이 중 최댓값을 저장한다.</li>
<li>자른 개수의 총합이 <code>n</code> 미만이면 <code>high</code> 의 위치를 <code>mid</code> 의 왼쪽으로 보내준다.</li>
<li>이 과정을 <code>high</code>가 <code>low</code> 보다 작아지기 전까지 반복해주면 구하는 길이의 최댓값을 구할 수 있다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드84-ms">파이썬 코드(84 ms)</h3>
<pre><code class="language-python"># 백준 입력 시간 줄이기
import sys
input = sys.stdin.readline

# 풀이 코드
k, n = map(int, input().split())
lanList = [int(input()) for _ in range(k)]
maxLen = -sys.maxsize - 1
low, high = 1, max(lanList)

while low &lt;= high:
    mid = (low + high) // 2

    cutCnt = 0
    for lan in lanList:
        cutCnt += lan // mid

    if n &lt;= cutCnt:
        maxLen = max(maxLen, mid)
        low = mid + 1
    else:
        high = mid - 1

print(maxLen)</code></pre>
<br>

<h3 id="자바스크립트-코드184-ms">자바스크립트 코드(184 ms)</h3>
<pre><code class="language-jsx">const fs = require(&#39;fs&#39;);
const filePath = process.platform === &#39;linux&#39; ? &#39;/dev/stdin&#39; : __dirname + &#39;/input.txt&#39;;
const [L, ...datas] = fs.readFileSync(filePath).toString().trim().split(&#39;\n&#39;);

const solution = ([k, n], lanList) =&gt; {
  let [low, high] = [1, Math.max(...lanList)];
  let maxLen = Number.MIN_SAFE_INTEGER;

  while (low &lt;= high) {
    let mid = ~~((low + high) / 2);
    let cutCnt = lanList.reduce((sum, lan) =&gt; sum + ~~(lan / mid), 0);

    if (n &lt;= cutCnt) {
      maxLen = Math.max(maxLen, mid);
      low = mid + 1;
    } else {
      high = mid - 1;
    }
  }
  console.log(maxLen);
};

solution(L.split(&#39; &#39;).map(Number), datas.map(Number));</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 2805. 나무 자르기]]></title>
            <link>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-2805.-%EB%82%98%EB%AC%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0</link>
            <guid>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-2805.-%EB%82%98%EB%AC%B4-%EC%9E%90%EB%A5%B4%EA%B8%B0</guid>
            <pubDate>Fri, 05 Jan 2024 05:37:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/2805">2805번: 나무 자르기</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>주어진 나무들을 자르고 남은 나무들의 길이 합이 최소한 <code>m</code> 이상이 되도록 하는 자르는 길이의 최댓값을 찾는 문제다.</li>
<li>이분 탐색을 이용하면 길이를 찾는 시간 복잡도를 줄일 수 있다.</li>
<li>구하는 것은 나무를 자를 길이이므로 탐색을 진행할 범위는 <code>(0, max(trees))</code> 이다.</li>
<li>범위의 중간값을 <code>mid</code> 라 하고 <code>mid</code> 보다 큰 나무가 존재하면 자르고 남은 길이를 합해준다.</li>
<li>이 길이의 합이 <code>m</code> 과 같다면 구하는 최댓값이 현재의 <code>mid</code> 값이므로 저장해주고 반복문을 종료한다.</li>
<li>크다면 나무를 자를 수는 있지만 최댓값인지 모르므로 현재까지 저장된 값과 현재의 <code>mid</code> 중 더 큰 값을 저장하고 범위의 시작 위치를 <code>mid</code> 의 오른쪽으로 옮겨준다.</li>
<li>작다면 범위의 끝을 <code>mid</code> 의 왼쪽으로 옮겨준다.</li>
<li>이 과정이 완료되면 문제의 조건을 만족하는 최댓값을 구할 수 있다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드480-ms">파이썬 코드(480 ms)</h3>
<pre><code class="language-python">n, m = map(int, input().split())
trees = list(map(int, input().split()))
low, high = 0, max(trees)
maxLen = 0

while low &lt;= high:
    mid = (low + high) // 2

    cutLen = 0
    for tLen in trees:
        if mid &lt; tLen:
            cutLen += tLen - mid

    if m == cutLen:
        maxLen = mid
        break
    elif m &lt; cutLen:
        maxLen = max(mid, maxLen)
        low = mid + 1
    else:
        high = mid - 1

print(maxLen)</code></pre>
<br>

<h3 id="자바스크립트-코드1028-ms">자바스크립트 코드(1028 ms)</h3>
<pre><code class="language-jsx">const fs = require(&#39;fs&#39;);
const filePath = process.platform === &#39;linux&#39; ? &#39;/dev/stdin&#39; : __dirname + &#39;/input.txt&#39;;
const [L, datas] = fs.readFileSync(filePath).toString().trim().split(&#39;\n&#39;);

const solution = ([n, m], trees) =&gt; {
  let [low, high] = [0, Math.max(...trees)];
  let maxLen = Number.MIN_SAFE_INTEGER;

  while (low &lt;= high) {
    let mid = ~~((low + high) / 2);
    let cutLen = trees.reduce((sum, tLen) =&gt; (mid &lt; tLen ? sum + tLen - mid : sum), 0);

    if (m === cutLen) {
      maxLen = mid;
      break;
    } else if (m &lt; cutLen) {
      maxLen = Math.max(mid, maxLen);
      low = mid + 1;
    } else {
      high = mid - 1;
    }
  }
  console.log(maxLen);
};

solution(L.split(&#39; &#39;).map(Number), datas.split(&#39; &#39;).map(Number));</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 2512. 예산]]></title>
            <link>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-2512.-%EC%98%88%EC%82%B0</link>
            <guid>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-2512.-%EC%98%88%EC%82%B0</guid>
            <pubDate>Fri, 05 Jan 2024 05:36:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/2512">2512번: 예산</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>전체 국가 예산의 한도 내에서 줄 수 있는 예산의 상한액을 구하는 문제.</li>
<li>이분 탐색을 하기 전에 예외의 경우를 살펴보자.</li>
<li>총 예산의 한도 값보다 예산 요청 리스트의 최솟값에 n을 곱한 값이 크거나 같을 때는, 이분 탐색을 진행할 필요 없이 상한액은 한도를 n으로 나눈 몫이다. <code>ex) 예산 요청: 2, 2, 3 and 한도: 5 → 상한액: 1</code></li>
<li>이러한 경우를 제외하고서는 상한액은 예산 요청 리스트의 최솟값과 최댓값 사이에 있을 것이다.</li>
<li>효율적으로 찾기 위해 이분 탐색을 진행하며 상한액의 최댓값을 찾아보자.</li>
<li>범위의 중간값을 상한액으로 지정했을 때 요청 예산이 더 크다면 예산을 상한액만큼 지급하고, 같거나 작다면 요청 예산만큼 지급한다.</li>
<li>이 값들을 더한 예산의 총합과 예산의 한도를 비교하여 이분 탐색을 진행하면 된다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드64-ms">파이썬 코드(64 ms)</h3>
<pre><code class="language-python">n, budgets, limit = int(input()), list(map(int, input().split())), int(input())
low, high = min(budgets), max(budgets)
maxBudget = 0

if limit &lt;= low * n:
    maxBudget = limit // n
else:
    while low &lt;= high:
        mid = (low + high) // 2

        budSum = 0
        for bud in budgets:
            budSum += mid if mid &lt; bud else bud

        if budSum &lt;= limit:
            maxBudget = max(maxBudget, mid)
            low = mid + 1
        else:
            high = mid - 1

print(maxBudget)</code></pre>
<br>

<h3 id="자바스크립트-코드176-ms">자바스크립트 코드(176 ms)</h3>
<pre><code class="language-jsx">const fs = require(&#39;fs&#39;);
const filePath = process.platform === &#39;linux&#39; ? &#39;/dev/stdin&#39; : __dirname + &#39;/input.txt&#39;;
const [N, datas, M] = fs.readFileSync(filePath).toString().trim().split(&#39;\n&#39;);

const solution = (n, budgets, limit) =&gt; {
  let [low, high] = [Math.min(...budgets), Math.max(...budgets)];
  let maxBudget = Number.MIN_SAFE_INTEGER;

  if (limit &lt;= low * n) {
    maxBudget = Math.floor(limit / n);
  } else {
    while (low &lt;= high) {
      let mid = Math.floor((low + high) / 2);
      let budSum = budgets.reduce((sum, bud) =&gt; (mid &lt; bud ? sum + mid : sum + bud), 0);

      if (budSum &lt;= limit) {
        maxBudget = Math.max(maxBudget, mid);
        low = mid + 1;
      } else {
        high = mid - 1;
      }
    }
  }
  console.log(maxBudget);
};

solution(Number(N), datas.split(&#39; &#39;).map(Number), Number(M));</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LeetCode] 240. Search a 2D Matrix II]]></title>
            <link>https://velog.io/@mi__xx2/LeetCode-240.-Search-a-2D-Matrix-II</link>
            <guid>https://velog.io/@mi__xx2/LeetCode-240.-Search-a-2D-Matrix-II</guid>
            <pubDate>Fri, 05 Jan 2024 05:35:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://leetcode.com/problems/search-a-2d-matrix-ii/description/">Search a 2D Matrix II - LeetCode</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>이진 탐색(이분 탐색)을 통해 매 행마다 target과 같은 값이 있는지 확인하고 존재한다면 True, 존재하지 않으면 False를 반환하면 되는 문제.</li>
<li>브루트포스를 이용해서 해결할 수도 있지만 오름차순으로 정렬되어 있는 행렬이기 때문에 빠른 처리를 위해서는 이분 탐색을 고려해보아야 한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/mi__xx2/post/89a5a3ce-f23b-4530-9bb7-cc1b885c6dbf/image.gif" alt=""></p>
<ul>
<li>주어진 행렬은 행, 열 모두 오름차순으로 정렬되어 있으므로 0번째 행부터 순서대로 target의 존재를 확인하면 된다.</li>
<li>현재 행의 범위 안에 타겟이 속한다면 타겟이 존재할 수도 있으므로 이분 탐색을 진행하면 된다.</li>
<li>low, high 의 초기값을 각각 0(첫번째 인덱스), 열의 길이로 선언해준다.</li>
<li>low가 high보다 커지지 않을 때까지 반복문을 돌며, low와 high의 중앙값인 mid를 통해 타겟의 존재 여부를 확인한다.</li>
<li><code>matrix[i][mid]</code>이 타겟과 같다면 True를 반환해준다.</li>
<li>타겟보다 작다면 탐색 범위를 (mid + 1, high)로, 크다면 탐색 범위를 (low, mid)로 좁혀준다.</li>
<li>이 과정을 통해 모든 행을 확인해보았을 때도 같은 값을 찾지 못했다면 False를 반환한다.</li>
<li>행의 개수를 M 열의 개수를 N 이라고 했을 때, 이분 탐색을 이용한 이 문제의 총 시간 복잡도는 O(M log N) 을 갖는다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>이진 탐색에서는 없음.</li>
<li>이진 탐색 전에 먼저 떠오른 풀이가 있어 제출 해보았는데, 파이썬에선 통과가 되었고 JS에서는 시간초과가 났다 🤯</li>
<li>혹시 JS에서 배열에 같은 원소가 있는지 확인하는 <code>includes</code> 함수에서 오래 걸리나? 해서</li>
<li>시간 복잡도가 O(N)인 <code>includes</code> 함수에서 O(1)의 시간 복잡도를 갖는 <code>Set</code>의 <code>has</code> 함수로 변경하여 확인해보았을 때도 여전히 시간 초과가 났다.</li>
<li>파이썬의 <code>in</code> 연산자도 list에서 O(N)의 시간복잡도를 갖는데, 왜 파이썬은 되고 JS는 안 되는거죠? (JS 이지매 당함)</li>
<li>심지어 시간 복잡도가 O(logN)인 이진 탐색 풀이보다도 조금 빠른 결과물이 나왔다?!</li>
<li>왜 이런 차이가 나는 것인지는 조금 더 찾아봐야 될 듯.
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드142-ms">파이썬 코드(142 ms)</h3>
<pre><code class="language-python"># 이진 탐색 풀이 (시간: 142 ms)
class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -&gt; bool:
        row, col = len(matrix), len(matrix[0])

        for i in range(row):
            if matrix[i][0] &lt;= target and matrix[i][-1] &gt;= target:
                low, high = 0, col

                while (low &lt; high):
                    mid = (low + high) // 2

                    if matrix[i][mid] == target:
                        return True
                    elif matrix[i][mid] &lt; target:
                        low = mid + 1
                    else:
                        high = mid

        return False</code></pre>
<pre><code class="language-python"># 행렬의 매 행마다 원하는 값이 있는지 확인하는 풀이 (시간: 137 ms)
class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -&gt; bool:
        for row in matrix:
            if target in row:
                return True
        return False</code></pre>
<br>

<h3 id="자바스크립트-코드862-ms">자바스크립트 코드(862 ms)</h3>
<pre><code class="language-jsx">const searchMatrix = (matrix, target) =&gt; {
    let [row, col] = [matrix.length, matrix[0].length];

    for (let i = 0; i &lt; row; i++) {
        let [low, high] = [0, col];

        while (low &lt; high) {
            let mid = ~~((low + high) / 2);

            if (matrix[i][mid] === target) {
                return true;
            } else if (matrix[i][mid] &lt; target) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
    }
    return false;
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LeetCode] 167. Two Sum II - Input Array Is Sorted]]></title>
            <link>https://velog.io/@mi__xx2/LeetCode-167.-Two-Sum-II-Input-Array-Is-Sorted</link>
            <guid>https://velog.io/@mi__xx2/LeetCode-167.-Two-Sum-II-Input-Array-Is-Sorted</guid>
            <pubDate>Fri, 05 Jan 2024 05:32:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/">Two Sum II - Input Array Is Sorted - LeetCode</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>투포인터를 이용하여 탐색을 해보았다.</li>
</ul>
<table>
<thead>
<tr>
<th align="center"></th>
<th>이분 탐색</th>
<th align="center">투포인터</th>
</tr>
</thead>
<tbody><tr>
<td align="center">시간복잡도</td>
<td>O(log n)</td>
<td align="center">O(n)</td>
</tr>
<tr>
<td align="center">방식</td>
<td>mid를 지정하여 탐색 범위를 절반으로 탐색</td>
<td align="center">문제에서 주어진 조건을 맞추는 방향</td>
</tr>
<tr>
<td align="center">양끝단 혹은 좌에서 우로 탐색</td>
<td></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">조건</td>
<td>데이터 정렬 sort 필요</td>
<td align="center">필요 x, 있으면 좋음</td>
</tr>
<tr>
<td align="center">- 이중 루프 대신 투포인터를 이용하면 시간 복잡도를 O(n^2) → O(n)으로 줄일 수 있다.</td>
<td></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">- 시작 인덱스를 <code>start = 0</code>, 끝 인덱스를 배열의 마지막 인덱스인 <code>end = len(numbers) - 1</code> 로 설정하자.</td>
<td></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">- 반복문을 돌며 <code>numbers[start] + numbers[end]</code> 와 <code>target</code> 을 비교하여 두 값을 더한 값이 타겟보다 크면 end의 위치를 왼쪽으로 보내주고, 작다면 start의 위치를 오른쪽으로 보내주며 탐색한다.</td>
<td></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">- 두 값이 서로 같아질 때 원하는 인덱스 값을 찾은 것이므로 탐색을 종료한다.</td>
<td></td>
<td align="center"></td>
</tr>
<tr>
<td align="center"><br><br></td>
<td></td>
<td align="center"></td>
</tr>
<tr>
<td align="center"># 놓쳤던 부분 😅</td>
<td></td>
<td align="center"></td>
</tr>
</tbody></table>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드96-ms">파이썬 코드(96 ms)</h3>
<pre><code class="language-python">class Solution:
    def twoSum(self, numbers: List[int], target: int) -&gt; List[int]:
        start, end = 0, len(numbers) - 1
        while numbers[start] + numbers[end] != target:
            if numbers[start] + numbers[end] &gt; target:
                end -= 1
            else:
                start += 1
        return [start + 1, end + 1]</code></pre>
<br>

<h3 id="자바스크립트-코드63-ms">자바스크립트 코드(63 ms)</h3>
<pre><code class="language-jsx">const twoSum = (numbers, target) =&gt; {
    let [start, end] = [0, numbers.length - 1];

    while (numbers[start] + numbers[end] !== target) {
        if (numbers[start] + numbers[end] &gt; target) {
            end--;
        } else {
            start++;
        }
    }
    return [start + 1, end + 1];  
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 5939. Race Results]]></title>
            <link>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-5939.-Race-Results</link>
            <guid>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-5939.-Race-Results</guid>
            <pubDate>Fri, 05 Jan 2024 05:30:33 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/5939">5939번: Race Results</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li><code>heapq</code> 모듈을 이용하여 힙을 만들되, 원소로 <code>(시간, 분, 초)</code> 를 튜플 형태로 저장한다.</li>
<li>힙에서 저장된 값을 뽑아내면 원하는 조건에 맞는 순서대로 값을 출력할 수 있다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드56-ms">파이썬 코드(56 ms)</h3>
<pre><code class="language-python">import sys
import heapq
input = sys.stdin.readline
N = int(input())
heap = []

for _ in range(N):
    hours, minutes, seconds = map(int, input().split())
    heapq.heappush(heap, (hours, minutes, seconds))

for _ in range(N):
    print(&#39; &#39;.join(map(str, heapq.heappop(heap))))</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 14534. String Permutation]]></title>
            <link>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-14534.-String-Permutation</link>
            <guid>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-14534.-String-Permutation</guid>
            <pubDate>Fri, 05 Jan 2024 05:29:41 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/14534">14534번: String Permutation</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li><code>itertools.permutations</code> 모듈을 이용하여 입력 받은 문자열로 만들 수 있는 순열을 생성한다.</li>
<li>순열로 만들어진 모든 값들을 조인하여 문자열로 변환해주고 출력한다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드40-ms">파이썬 코드(40 ms)</h3>
<pre><code class="language-python">from itertools import permutations

for i in range(1, int(input()) + 1):
    pList = list(permutations(list(input())))
    print(f&#39;Case # {i}:&#39;)
    for val in pList:
        print(&#39;&#39;.join(val))</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LeetCode] 179. Largest Number]]></title>
            <link>https://velog.io/@mi__xx2/LeetCode-179.-Largest-Number</link>
            <guid>https://velog.io/@mi__xx2/LeetCode-179.-Largest-Number</guid>
            <pubDate>Fri, 05 Jan 2024 05:28:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://leetcode.com/problems/largest-number/description/">Largest Number - LeetCode</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>전형적인 정렬 문제이나 하나의 키 값으로 정렬하는 문제가 아니었다. 어떤 키 값으로 정렬을 할지가 중요한 문제.</li>
<li>여기서 키 값은 비교할 두 원소를 문자열로 변환하여 더한 값이었다.</li>
<li>따라서 nums의 원소인 숫자들을 문자열로 변환하고 <code>sorted</code> 함수를 통해 이 리스트를 정렬하자.</li>
<li>key 를 <code>functools.cmp_to_key(compare)</code> 로 설정하고 정렬의 기준을 정해줄 <code>compare</code> 함수를 선언한다.</li>
<li><code>compare</code> 함수는  <code>a + b &gt; b + a</code>  가 True 라면 -1, False라면 1을 반환하는 함수이다.</li>
<li>1이 반환되면 두 원소의 위치가 서로 바뀌기 때문에 주어진 조건에 맞게 정렬이 된다.</li>
<li>리턴 값을 str(int())로 감싸주는 이유는 입력값이 [0, 0] 일 때 00 이 아닌 0을 반환하기 위한 처리이다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>효율적으로 작성하는 코드를 찾아보다가 <code>functools</code> 모듈의 <code>cmp_to_key</code> 함수를 알게 되었다.</li>
<li><code>cmp_to_key</code> 를 활용하면 자신만의 조건에 따라 sorting 가능하게 할 수 있다.</li>
</ul>
<blockquote>
<p><code>functools.cmp_to_key(func)</code> 는 sorted()와 같은 정렬 함수의 key 매개변수에 함수(func)를 전달할 때 사용하는 함수이다. 단, func() 함수는 두 개의 인수를 입력하여 그 둘을 비교하고 기준에 따라 음수, 0, 양수를 반환하는 비교 함수이어야 한다.</p>
</blockquote>
<ul>
<li>양수를 반환하면 두 입력의 자리를 바꾼다는 의미이고, 음수를 반환하면 위치를 바꾸지 않는다는 의미이다.</li>
<li>속도도 빠른 편인 것 같아서 정렬 문제를 풀 때 알아두면 유용하게 활용할 수 있을 듯.
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드37-ms">파이썬 코드(37 ms)</h3>
<pre><code class="language-python">from functools import cmp_to_key

class Solution:

    def compare(self, a, b):
        return -1 if a + b &gt; b + a else 1

    def largestNumber(self, nums: List[int]) -&gt; str:
        nums = sorted(list(map(str, nums)), key = cmp_to_key(self.compare))
        return str(int(&quot;&quot;.join(nums)))</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LeetCode] 147. Insertion Sort List]]></title>
            <link>https://velog.io/@mi__xx2/LeetCode-147.-Insertion-Sort-List</link>
            <guid>https://velog.io/@mi__xx2/LeetCode-147.-Insertion-Sort-List</guid>
            <pubDate>Fri, 05 Jan 2024 05:27:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://leetcode.com/problems/insertion-sort-list/description/">Insertion Sort List - LeetCode</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<blockquote>
<p>💡 <strong>삽입 정렬 (insertion sort)</strong></p>
<p>자료 배열의 모든 요소를 앞에서부터 차례대로 이미 정렬된 배열 부분과 비교하여, 자신의 위치를 찾아 삽입함으로써 정렬을 완성하는 알고리즘이다.</p>
</blockquote>
<ul>
<li>입력으로 주어진 연결 리스트를 오름차순으로 정렬하는 문제</li>
<li>정렬된 연결 리스트를 저장할 빈 연결 리스트를 선언한다. 이를 <code>dummy</code> 라 하자.</li>
<li>입력으로 주어진 연결 리스트를 순회하면서, 각 노드마다 <code>dummy</code>에 삽입할 위치를 찾는다.</li>
<li>순회가 끝나면, <code>dummy</code> 는 정렬된 연결 리스트가 된다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>처음에 반쪽짜리 정렬을 하게 되어서 찾아보니 빈 연결 리스트를 선언하여 추가해줘야 하는 문제였다. 🥹
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드64-ms">파이썬 코드(64 ms)</h3>
<pre><code class="language-python">class Solution:
    def insertionSortList(self, head: ListNode) -&gt; ListNode:
        dummy = curr = ListNode()
        while head:
            while curr.next and curr.next.val &lt; head.val:
                curr = curr.next
            curr.next, head.next, head = head, curr.next, head.next
            if head and curr.val &gt; head.val:
                curr = dummy
        return dummy.next</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 11279. 최대 힙]]></title>
            <link>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-11279.-%EC%B5%9C%EB%8C%80-%ED%9E%99</link>
            <guid>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-11279.-%EC%B5%9C%EB%8C%80-%ED%9E%99</guid>
            <pubDate>Fri, 05 Jan 2024 05:24:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/11279">11279번: 최대 힙</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<blockquote>
<p>파이썬의 <code>heapq</code> 모듈은 최소 힙으로 구현되어 있기 때문에 최대 힙 구현을 위해서는 트릭이 필요하다.</p>
<p>힙에 원소를 추가할 때 <code>(-num, num)</code> 의 튜플 형대로 넣어주면 튜플의 첫 번째 원소를 우선순위로 힙을 구성하게 된다.</p>
<p>이때 원소 값의 부호를 바꿨기 때문에, 최소 힙으로 구현되었던 <code>heapq</code> 모듈을 최대 힙 구현에 활용할 수 있다.</p>
</blockquote>
<ul>
<li>입력 값으로 자연수가 들어왔을 때마다 이 방식으로 <code>heapq</code> 를 최대 힙으로 구현한다.</li>
<li>0이 입력되었을 때, 현재 힙이 빈 상태라면 0, 아니라면 힙에 존재하는 최댓값을 출력해준다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드152-ms">파이썬 코드(152 ms)</h3>
<pre><code class="language-python">import sys
import heapq
input = sys.stdin.readline
heap = []

for _ in range(int(input())):
    num = int(input())
    if not num:
        print(heapq.heappop(heap)[1] if len(heap) else 0)
    else:
        heapq.heappush(heap, (-num, num))</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 1927. 최소 힙]]></title>
            <link>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-1927.-%EC%B5%9C%EC%86%8C-%ED%9E%99</link>
            <guid>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-1927.-%EC%B5%9C%EC%86%8C-%ED%9E%99</guid>
            <pubDate>Fri, 05 Jan 2024 05:22:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/1927">1927번: 최소 힙</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>힙을 사용해서 리스트에 입력으로 들어오는 자연수를 저장하고 0이 들어왔을 때 힙의 최솟값을 출력하면 된다.</li>
<li>리스트가 빈 상태라면 0을 출력한다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>없음</li>
<li>맞는데 자꾸 시간초과래서 찾아보니까 백준 시간 초과 방지로 인해 <code>input = sys.stdin.readline</code> 이 부분을 추가해줘야 했음 ㅋㅋ 🤬
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드128-ms">파이썬 코드(128 ms)</h3>
<pre><code class="language-python">import heapq
import sys

input = sys.stdin.readline
heap = []

for _ in range(int(input())):
    num = int(input())
    if not num:
        print(heapq.heappop(heap) if len(heap) else 0)
    else:
        heapq.heappush(heap, num)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LeetCode] 215. Kth Largest Element in an Array]]></title>
            <link>https://velog.io/@mi__xx2/LeetCode-215.-Kth-Largest-Element-in-an-Array</link>
            <guid>https://velog.io/@mi__xx2/LeetCode-215.-Kth-Largest-Element-in-an-Array</guid>
            <pubDate>Fri, 05 Jan 2024 05:22:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://leetcode.com/problems/kth-largest-element-in-an-array/description/">Kth Largest Element in an Array - LeetCode</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>힙에서 최댓값부터 뽑아내기 위해 <code>nums</code> 의 인자들을 음수로 변환한 값을 가진 리스트를 선언한다.</li>
<li><code>heapify()</code>를 통해 값이 들어 있는 리스트를 힙으로 변환한다.</li>
<li>k번째로 큰 값을 출력해주면 된다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드485-ms">파이썬 코드(485 ms)</h3>
<pre><code class="language-python">import heapq

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -&gt; int:
        heap = [-n for n in nums]
        heapq.heapify(heap)

        for _ in range(k):
            findKth = -1 * heapq.heappop(heap)

        return findKth</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LeetCode] 1337. The K Weakest Rows in a Matrix]]></title>
            <link>https://velog.io/@mi__xx2/LeetCode-1337.-The-K-Weakest-Rows-in-a-Matrix</link>
            <guid>https://velog.io/@mi__xx2/LeetCode-1337.-The-K-Weakest-Rows-in-a-Matrix</guid>
            <pubDate>Fri, 05 Jan 2024 05:21:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://leetcode.com/problems/the-k-weakest-rows-in-a-matrix/description/">The K Weakest Rows in a Matrix - LeetCode</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>이 문제는 각 행의 전사 수를 기준으로 가장 약한 행부터 강한 행까지 <code>k</code> 개를 찾아 반환하는 문제이다.</li>
<li><code>heapq</code> 에 값을 튜플 형태로 저장하면 정렬의 우선순위를 정할 수 있다.<ul>
<li>전사의 수가 더 적은 행이 약하다.</li>
<li>전사의 수가 같다면 행렬에서 인덱스 값이 더 낮은 행이 약하다.</li>
</ul>
</li>
<li>즉 값으로 <code>(전사의 수, 인덱스)</code> 를 저장해준다면 힙에는 전사의 수가 더 낮은 순으로 저장되고, 전사의 수가 같다면 인덱스가 낮은 순으로 저장된다.</li>
<li>전사는 1로 나타내므로 각 행마다 <code>collections.Counter</code> 함수를 사용하여 1의 개수를 추출하고, 힙에 이 값과 행의 인덱스 값을 저장한다.</li>
<li><code>heapq.heappop(힙)</code> 을 이용하여 약한 행의 정보를 <code>k</code> 번 꺼내어 행의 인덱스 값을 리스트에 담아주고 이 리스트를 최종 출력하면 된다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드97-ms">파이썬 코드(97 ms)</h3>
<pre><code class="language-python">from collections import Counter
import heapq

class Solution:
    def kWeakestRows(self, mat: List[List[int]], k: int) -&gt; List[int]:
        heap, answer = [], []
        for idx, row in enumerate(mat):
            heapq.heappush(heap, (Counter(row)[1], idx))

        while k:
            answer.append(heapq.heappop(heap)[1])
            k -= 1
        return answer</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LeetCode] 1464. Maximum Product of Two Elements in an Array]]></title>
            <link>https://velog.io/@mi__xx2/LeetCode-1464.-Maximum-Product-of-Two-Elements-in-an-Array</link>
            <guid>https://velog.io/@mi__xx2/LeetCode-1464.-Maximum-Product-of-Two-Elements-in-an-Array</guid>
            <pubDate>Fri, 05 Jan 2024 05:19:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://leetcode.com/problems/maximum-product-of-two-elements-in-an-array/description/">Maximum Product of Two Elements in an Array - LeetCode</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>리스트 <code>nums</code> 를 최대 힙으로 구현하고 최댓값을 차례로 두개를 꺼낸다.</li>
<li>이 두 값을 각각 1씩 빼서 곱한 값을 반환해주면 된다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드50-ms">파이썬 코드(50 ms)</h3>
<pre><code class="language-python">import heapq

class Solution:
    def maxProduct(self, nums: List[int]) -&gt; int:
        maxHeap = []
        for n in nums:
            heapq.heappush(maxHeap, (-n, n))
        i = heapq.heappop(maxHeap)[1]
        j = heapq.heappop(maxHeap)[1]
        return (i - 1) * (j - 1)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 1068. 트리]]></title>
            <link>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-1068.-%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@mi__xx2/%EB%B0%B1%EC%A4%80-1068.-%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Fri, 05 Jan 2024 05:18:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.acmicpc.net/problem/1068">1068번: 트리</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<ul>
<li>삭제해야 하는 부모 트리의 인덱스와 입력된 트리 리스트를 입력 값으로 받는 DFS 함수를 선언한다.</li>
<li>전달 받은 인덱스의 리스트 값을 삭제한다는 의미로 해당 값을 <code>None</code> 으로 바꾼다.</li>
<li>리스트 전체를 탐색하며, 방금 삭제한 인덱스를 부모 노드로 가지는 노드를 찾아 dfs 함수를 재귀 호출한다.</li>
<li>재귀가 끝나면 삭제된 노드들은 전부 <code>None</code> 으로 갱신되어 있으므로, 값이 <code>None</code> 이 아니면서, 다른 노드의 부모 노드도 아닌 원소를 찾을 때마다 <code>cnt</code>를 1씩 늘린다.
<br><br><h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
</li>
</ul>
<hr>
<ul>
<li>DFS / BFS 를 사용할 때에 가장 중요한 것은 <strong>탐색의 기준</strong>을 잡는 것이라고 생각한다.</li>
<li>이번에는 바로 떠오르지 않아서 다른 분의 답안을 참고 했는데, <strong>삭제해야 하는 인덱스</strong>를 기준으로 하고 탐색을 돌리면 생각보다 단순한 문제였다.</li>
<li>다양한 문제들을 접하며 문제를 해결할 아이디어를 떠올리는 능력을 더 길러야겠다.
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드40-ms">파이썬 코드(40 ms)</h3>
<pre><code class="language-python">n, parentList, k = int(input()), list(map(int, input().split())), int(input())
cnt = 0

def dfs(num, list):
    list[num] = None
    for i in range(len(list)):
        if num == list[i]:
            dfs(i, list)

dfs(k, parentList)

for i in range(len(parentList)):
    if parentList[i] != None and i not in parentList:
        cnt += 1
print(cnt)</code></pre>
<br>

<h3 id="자바스크립트-코드132-ms">자바스크립트 코드(132 ms)</h3>
<pre><code class="language-jsx">const fs = require(&#39;fs&#39;);
const filePath = process.platform === &#39;linux&#39; ? &#39;/dev/stdin&#39; : __dirname + &#39;/input.txt&#39;;
const [L, data, K] = fs.readFileSync(filePath).toString().trim().split(&#39;\n&#39;);

const dfs = (cutNum, list) =&gt; {
  list[cutNum] = null;

  for (let i = 0; i &lt; list.length; i++) {
    if (cutNum === list[i]) {
      dfs(i, list);
    }
  }
};

const solution = (parentList, k) =&gt; {
  let cnt = 0;
  dfs(k, parentList);

  for (let i = 0; i &lt; parentList.length; i++) {
    if (parentList[i] != null &amp;&amp; !parentList.includes(i)) {
      cnt++;
    }
  }
  console.log(cnt);
};

solution(data.split(&#39; &#39;).map(Number), Number(K));</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LeetCode] 110. Balanced Binary Tree]]></title>
            <link>https://velog.io/@mi__xx2/LeetCode-110.-Balanced-Binary-Tree</link>
            <guid>https://velog.io/@mi__xx2/LeetCode-110.-Balanced-Binary-Tree</guid>
            <pubDate>Fri, 05 Jan 2024 05:16:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://leetcode.com/problems/balanced-binary-tree/description/">Balanced Binary Tree - LeetCode</a></p>
</blockquote>
<h1 id="문제-접근-🤔">문제 접근 🤔</h1>
<hr>
<blockquote>
<p>A <strong>height-balanced</strong> binary tree is a binary tree in which the depth of the two subtrees of every node never differs by more than one.</p>
</blockquote>
<p>이진 트리에서 <strong>높이 균형</strong>이란, 각 서브 트리의 높이 차이가 1 이하인 상태를 말한다. 즉, 트리의 각 서브 트리에 대해 왼쪽 서브 트리와 오른쪽 서브 트리의 높이 차이가 1 이하여야 한다.</p>
<ul>
<li>주어진 이진 트리가 높이 균형이 맞는지 확인하는 문제</li>
<li>DFS를 통해 트리의 가장 하단부터 트리를 타고 올라가면서 각 노드의 높이를 반환하자.</li>
<li>노드의 왼쪽 노드와 오른쪽 노드의 높이 차이가 1보다 크다면 높이 균형이 맞지 않는 이진 트리이므로 False를 저장하고 모든 노드의 높이 차이가 1 이하라면 True를 저장하여 이를 최종 반환해주면 된다.</li>
</ul>
<br>

<h1 id="놓쳤던-부분-😅">놓쳤던 부분 😅</h1>
<hr>
<ul>
<li>없음
<br><br><h1 id="코드-😁">코드 😁</h1>
</li>
</ul>
<hr>
<h3 id="파이썬-코드56-ms">파이썬 코드(56 ms)</h3>
<pre><code class="language-python">class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -&gt; bool:
        self.isHeightBalanced = True

        def dfs(node, height = 0):
            if not node:
                return height
            height += 1
            left, right = dfs(node.left, height), dfs(node.right, height)
            if abs(left - right) &gt; 1:
                self.isHeightBalanced = False
            return max(left, right)

        dfs(root)
        return self.isHeightBalanced</code></pre>
<br>

<h3 id="자바스크립트-코드76-ms">자바스크립트 코드(76 ms)</h3>
<pre><code class="language-jsx">const isBalanced = (root) =&gt; {
  let isHeightBalanced = true;

  const dfs = (node, height = 0) =&gt; {
    if (!node) {
      return height;
    }
    height++;
    let [left, right] = [dfs(node.left, height), dfs(node.right, height)];
    if (Math.abs(left - right) &gt; 1) {
      isHeightBalanced = false;
    }
    return Math.max(left, right);
  };
  dfs(root);
  return isHeightBalanced;
};</code></pre>
]]></description>
        </item>
    </channel>
</rss>