<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>seo-garden.log</title>
        <link>https://velog.io/</link>
        <description>개인적인 궁금증을 바탕으로 글을 작성합니다! 공부한 내용에 대한 추가적인 내용은 github notion 확인해주시면 감사하겠습니다!</description>
        <lastBuildDate>Fri, 09 Jan 2026 07:04:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. seo-garden.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/seo-garden" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Singleton VS Dependency Injection]]></title>
            <link>https://velog.io/@seo-garden/Singleton-VS-Dependency-Injection</link>
            <guid>https://velog.io/@seo-garden/Singleton-VS-Dependency-Injection</guid>
            <pubDate>Fri, 09 Jan 2026 07:04:42 GMT</pubDate>
            <description><![CDATA[<p><a href="https://enterprisecraftsmanship.com/posts/singleton-vs-dependency-injection/">Singleton vs Dependency Injection</a></p>
<p>해당 글에서 보다보면 막히는 부분이 있다.</p>
<p>The heuristic to determine whether you need to introduce a singleton is simple. If a dependency cross-cuts most of your classes and/or several layers in your application, extract it using the Singleton pattern. Otherwise, use the standard Dependency Injection technique. The ultimate goal is to make sure that you don’t use DI for ambient dependencies.</p>
<p>싱글턴을 도입해야 하는지 판단하는 휴리스틱은 간단합니다. 만약 의존성이 대부분의 클래스와/또는 애플리케이션의 여러 레이어를 가로지른다면, 싱글턴 패턴을 사용하여 이를 추출하세요. 그렇지 않다면, 표준 의존성 주입 기법을 사용하세요. 궁극적인 목표는 주변 의존성에 대해 DI를 사용하지 않도록 하는 것입니다.</p>
<p>“여러 레이어를 가로지른다” 는 무슨뜻인가?</p>
<p>예를 들어, NetworkService 는 ViewModel이나 Service 레이어에서만 필요하다. 굳이 View가 알 필요가 없다. 하지만 로그나 날짜 포맷팅, 분석과 같은 기능들은 어떨까? </p>
<ul>
<li>View: 버튼 눌렀을 때 로그를 남김</li>
<li>ViewModel: 데이터를 변환할 때 로그를 남김</li>
<li>Repository: DB 에러 났을 때 로그를 남김</li>
</ul>
<p>이처럼 모든 층에서 공통적으로 필요해서 이것이 레이어를 가로지르는 의존성이다. 만약 로그 같은 기능을 모든 곳에 DI로 넣어주려 한다면, Constructor Pollution 이 발생한다. 만약 사용하지 않는 View가 있다고 한들, ViewModel에게 넘겨주기 위해 갖고 있어야 한다. </p>
<h3 id="요약">요약</h3>
<p>적절하게 싱글턴과 DI를 판단하고 사용해야 한다. </p>
<p>싱글턴을 써도 되는 경우</p>
<ul>
<li>로그</li>
<li>환경 설정</li>
<li>분석</li>
</ul>
<p>싱글턴을 쓰면 안되는 경우</p>
<ul>
<li>APIClient, DataBaseManager : <strong><em>이건 싱글턴으로 만들면 테스트가 불가능해지고,</em></strong> 앱의 동작을 제어하기가 힘들어진다.</li>
</ul>
<p>다음은 DIContainer 를 주제로 정리해서 들고오겠습니다 !</p>
<p>읽어주셔서 감사합니다! 오타 및 잘못된 내용은 언제든 댓글 달아주시면 최대한 빠르게 수정하겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CoreData에 대한 사실과 오해]]></title>
            <link>https://velog.io/@seo-garden/Core-Data</link>
            <guid>https://velog.io/@seo-garden/Core-Data</guid>
            <pubDate>Sat, 30 Aug 2025 16:11:15 GMT</pubDate>
            <description><![CDATA[<h3 id="백엔드가-있는데-굳이-코어데이터를-사용할-필요가-있나-오히려-앱-용량만-늘어나고-안좋은거-아닌가">백엔드가 있는데, 굳이 코어데이터를 사용할 필요가 있나? 오히려 앱 용량만 늘어나고 안좋은거 아닌가?</h3>
<p>라고 저의 건방진 생각이었습니다.. 이번 CoreData를 공부하면서 꼭 넣어야 하는 내용들을 정리할 생각입니다!!</p>
<p>코어데이터는 애플이 코코아 개발 환경을 통해 제공하는 <em><strong>인메모리 방식의 데이터 관리 프레임워크</strong></em> 입니다.
ORM 매핑 프레임워크가 맞는 표현입니다. </p>
<blockquote>
<p><strong>인메모리 방식 ?</strong> 
데이터를 하드디스크가 아닌 컴퓨터의 주기억장치인 RAM에 저장하고 처리하는 기술을 의미합니다. 이 방식은 데이터에 접근하는 속도를 획기적으로 높여 실시간 분석 및 빠른 의사결정을 가능하게 하며, 캐싱, 실시간 데이터 처리, 사용자 세션 관리 등 다양한 분야에서 활용됩니다. </p>
</blockquote>
<p>그래서 코어 데이터에서의 데이터를 다루는 모든 작업은 메모리를 기반으로 동작합니다. 코어 데이터를 통해 읽고 쓰는 모든 데이터는 원칙적으로 메모리에 로드된 다음에 처리되기 때문에, 대량의 읽기와 쓰기 작업이 발생하더라도 성능에 크게 영향을 끼치지 않습니다. 대부분의 작업이 영구 저장소에서 직접 처리되고, 효율성을 위해 읽기 목적의 데이터 일부만 메모리에 올려 놓고 사용하는 데이터베이스와는 구분되는 효율적 특성입니다.</p>
<p>그리고 내부적으로 파일이나 SQLite 같은 영구 저장소에 보조적으로 데이터를 저장할 수 있기 때문에, 앱이 종료되도 데이터는 삭제되지 않습니다!</p>
<p>여기까지만 보면 그럼 <em><strong>코어 데이터가 데이터베이스 아닌가?</strong></em> 라고 생각할 수 있지만, 이는 잘못된 생각입니다! </p>
<hr>
<p>데이터를 영구적으로 보존할 수 있고, 데이터베이스의 데이터 구조를 거의 그대로 사용하고, 검색이나 정렬같은 핵심 쿼리 기능을 모두 지원하기 때문에, 데이터베이스로 오해하는 경우가 많은 것 같습니다. </p>
<p>코어 데이터는 인메모리 방식으로 동작하는 프레임워크 입니다. 사용하려는 모든 데이터는 우선 메모리에 로딩되는 과정을 거친 다음에야 비로소 사용할 수 있고, 데이터를 읽거나 쓰고 수정하며 삭제하는 모든 작업은 메모리에 로딩된 데이터를 대상으로 이루어집니다. 이 방식의 장점으로는 빠른 처리 속도와 성능의 향상을 꼽을 수 있습니다. 
온디스크처럼 매번 디스크에 직접 작성하거나 읽어오지 않아도 되기 때문에 상대적으로 I/O가 적게 발생하며, 또한 비즈니스 로직 수행 과정에서 발생하는 데이터의 변경 내역을 모두 메모리 수준에서 처리한 후 최종 결과만 영구 저장소에 반영하면 되기 때문에 여러번 반복해서 읽거나 쓰더라도 성능에 문제는 거의 생기지 않습니다. (물론 인메모리 방식이라 할지라도 코드를 작성하는 방식에 따라 성능 저하는 생길 수 있겠죠?)</p>
<hr>
<h3 id="코어-데이터의-한계">코어 데이터의 한계</h3>
<p>크게 불편함을 느끼기는 어렵지만, 데이터베이스에 비해 코어 데이터가 마냥 좋은 것만은 아닙니다. </p>
<h4 id="데이터를-메모리에-로딩하는-과정-없이는-작업이-불가능합니다">데이터를 메모리에 로딩하는 과정 없이는 작업이 불가능합니다.</h4>
<p>데이터베이스에서 일반적으로 데이터를 삭제하거나 업데이트할 때는 특정 조건을 명시한 SQL문이 사용됩니다. 이에 따라 해당 조건에 부합하는 최소한의 데이터만 메모리에 로드한 다음에 필요한 작업을 처리하는데, 이 같은 온 디스크 방식의 특성은 전체 데이터 양이 많더라도 효율적으로 업데이트할 수 있다는 것입니다. 
하지만 코어 데이터는 인메모리 방식을 기반으로 동작하는 프레임워크 입니다. 메모리에 로드된 객체에 대해서만 수정이 가능하기 때문에, 먼저 객체를 메모리에 로딩해둬야 합니다. 데이터를 삭제하는 과정 역시 마찬가지입니다. 
영구 저장소로 부터 데이터를 잃어 객체로 만들고 이를 메모리에 로딩해 놓은 다음, 이를 삭제하고 다시 컨텍스트를 저장소에 커밋하는 과정을 거쳐야 합니다. 삭제도 마찬가지입니다. 이 같은 과정을 반복되면 메모리 사용량이 늘어나고 이는 성능하락으로 이어질 수 있습니다. </p>
<h4 id="데이터-로직을-다루는-데에-한계가-있습니다">데이터 로직을 다루는 데에 한계가 있습니다.</h4>
<p>관계형 데이터베이스에서 데이터의 저장과 관련해 제공하는 기능 중에서 코어 데이터가 지원하지 못하는 것이 있습니다. 동일 테이블에 중복된 값의 입력을 방지하는 Unique 키 입니다. 동일 테이블에 중복된 값의 입력을 방지하는 Unique 키는 주로 주민등록번호 같은 칼럼에 사용되는데, 똑같은 값이 재입력되는 것을 방지하는데 도움을 줍니다. 하지만 Unique키에 해당하는 기능이 제공되지 않기 때문에 중복 값의 입력을 방지하려면 비즈니스 로직 부분에서 처리를 해줘야 합니다. </p>
<h4 id="멀티-쓰레드-멀티-유저를-지원하지-않습니다">멀티 쓰레드, 멀티 유저를 지원하지 않습니다.</h4>
<p>코어 데이터는 원칙적으로 싱글 쓰레드만 지원합니다. 한 번에 하나의 작업만 처리할 수 있습니다. <em>*<em>하지만 데이터베이스들은 일반적으로 멀티 쓰레드를 지원할 뿐만 아니라 멀티 유저까지 지원합니다. *</em></em>
코어 데이터가 싱글 쓰레드만 지원하는 것은 단일 작업에서 처리 성능을 향상시키기 위함입니다. 멀티 쓰레딩 방식으로 동작할 때는 한쪽에서 작업하고 있는 동안 해당 영역을 다른 쓰레드가 침범하지 못하도록 락을 걸어야 하는데, 이 락은 데이터베이스 성능 저하의 원인이 되기도 합니다. 
반대로 말하면 락을 걸지 않음으로 훨씬 빠르게 데이터를 처리할 수 있습니다. </p>
<h4 id="다중-플랫폼-환경에서-어울리지-않는다-부족한-내용-피드백-받았습니다">다중 플랫폼 환경에서 어울리지 않는다. (부족한 내용 피드백 받았습니다!)</h4>
<p>iOS 앱만 서비스하는 경우 해당되지 않는 케이스지만, 만약 iOS, Android, Web 모두 지원하는 상황이라면, 다른 써드파티 라이브러리를 사용하는 방법이 더 나을 것 같습니다. 데이터 모델과 동작을 통일할 수 있습니다. (기존엔 Realm을 왜써야 되는지 몰랐는데, 이번 케이스를 통해 조금이나마 접근하게 됐습니다.)</p>
<hr>
<p>그래서 코어 데이터에는 어떤걸 저장해야 되는걸까? 네트워크가 유실되더라도 보여줘야 하는 데이터? 서버에는 보내지 않아도 되는 로컬 데이터? 등등 서비스의 성격에 따라 다르겠지만 이를 유연하게 잘 선택하는 것이 중요할 것 같습니다! </p>
<p>여기까지 코어 데이터에 대해 공부하면서 개인적으로 기록하고 싶은 내용들을 위주로 작성했습니다! 읽어주셔서 감사합니다! 오타 및 잘못된 내용은 언제든 댓글 달아주시면 최대한 빠르게 수정하겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[구조체와 클래스 선택하기]]></title>
            <link>https://velog.io/@seo-garden/%EA%B5%AC%EC%A1%B0%EC%B2%B4%EC%99%80-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%84%A0%ED%83%9D%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@seo-garden/%EA%B5%AC%EC%A1%B0%EC%B2%B4%EC%99%80-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%84%A0%ED%83%9D%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 06 Aug 2025 08:34:08 GMT</pubDate>
            <description><![CDATA[<h1 id="클래스와-구조체의-기본적인-동작에-대해서는-다루지-않습니다">클래스와 구조체의 기본적인 동작에 대해서는 다루지 않습니다.</h1>
<h2 id="해당-내용은-애플-공식문서를-참고해서-정리한-내용입니다">해당 내용은 애플 공식문서를 참고해서 정리한 내용입니다.</h2>
<p>구조체와 클래스는 데이터 저장 및 앱에서의 행동 모델링을 위한 좋은 선택이지만, 둘의 유사성 때문에 하나를 선택하는 것이 어려울 수 있습니다.</p>
<p>다음 권장 사항을 고려해 앱에 새로운 데이터 유형을 추가할 때 어떤 옵션이 의미가 있는지 선택하는데 도움을 줍니다.</p>
<ul>
<li>기본적으로 구조체를 사용하세요.</li>
<li>Objective-C 상호 운용성이 필요할 때 클래스를 사용하세요.</li>
<li>데이터의 정체성을 제어해야할 때 클래스를 사용하세요.</li>
<li>구현을 공유해 행동을 채택하기 위해 프로토콜과 함께 구조체를 사용하세요.</li>
</ul>
<h3 id="기본적으로-구조-선택하기">기본적으로 구조 선택하기</h3>
<p>일반적인 종류의 데이터를 나타내기 위해 구조체를 사용합니다. Swift의 구조체는 다른 언어의 클래스에만 한정된 많은 기능을 포함합니다. 저장 속성, 계산 속성 및 메서드를 포함할 수 있습니다. 게다가, Swift 구조체는 기본 구현을 통해 동작을 얻기 위해 프로토콜을 채택할 수 있습니다. Swift 표준 라이브러리와 Foundation은 숫자, 문자열, 배열 및 딕셔너리와 같이 자주 사용하는 유형에 구조체를 사용합니다.</p>
<p>구조체를 사용하면 애플리케이션의 전체 상태를 고려하지 않고도 코드의 일부에 대해 더 쉽게 추론할 수 있습니다. 구조체는 값 타입이기 때문에 클래스와는 달리 구조체에 대한 지역 변경 사항은 애플리케이션의 나머지 부분에서는 볼 수 없으며, 변경 사항을 애플리케이션의 흐름의 일부로 의도적으로 전달하지 않는 한 보이지 않습니다. 결과적으로 코드의 한 섹션을 보고 그 섹션의 인스턴스에 대한 변경 사항이 간접적으로 관련된 함수 호출로부터 보이지 않게 이루어지는 것이 아니라 명시적으로 이루어질 것이라는 확신을 가질 수 있습니다. </p>
<h3 id="objective-c-상속-운용성이-필요할-때-클래스-사용">Objective-C 상속 운용성이 필요할 때 클래스 사용</h3>
<p>데이터를 처리해야 하는 Objective-C API를 사용하거나 데이터 모델을 Objective-C 프레임워크에서 정의된 기존 클래스 계층에 맞춰야 하는 경우, 데이터를 모델링하기 위해 클래스 및 클래스 상속을 사용해야 할 수 있다. 예를 들어, 많은 Objective-C 프레임워크는 서브클래싱할 것으로 예상되는 클래스를 노출합니다. </p>
<h3 id="정체성을-제어해야-할-때-클래스를-사용하세요">정체성을 제어해야 할 때 클래스를 사용하세요.</h3>
<p>Swift 클래스는 참조 타입이기 때문에 내장된 정체성 개념을 가지고 있습니다. 이는 두 개의 서로 다른 클래스 인스턴스가 각 저장 속성에 대해 동일한 값을 가질 때, 여전히 정체성 연산자(===) 의해 다르게 간주된다는 것을 의미합니다. 또한 앱 전반에 걸쳐 클래스 인스턴스를 공유할 때, 해당 인스턴스에 대한 변경 사항이 해당 인스턴스에 대한 참조를 가진 코드의 모든 부분에 표시된다는 것을 의미합니다. 인스턴스에 이러한 종류의 정체성을 필요할 때 클래스를 사용합니다. 일반적인 사용 사례로는 <strong><em>file handles, network connections, shared hardware, CBCentralManager</em></strong>와 같은 것이 있습니다. </p>
<blockquote>
<p>여러 클론 프로젝트를 진행했고, 바이브 코딩을 진행했을 때 네트워크 작업을 수행하는 NetworkManager 혹은 NetworkService 전부 class로 작성이 되어 있었습니다. <em><strong>URLSession 자체가 class로 구현되어 있기 때문에, class를 사용하는 것에 대한 답변이 될 수 있을 것 같습니다.</strong></em> </p>
</blockquote>
<p>예를 들어, 로컬 데이터베이스 연결을 나타내는 유형이 있는 경우, 해당 데이터베이스에 대한 액세스를 관리하는 코드는 앱에서 보는 데이터베이스의 상태에 대한 완전한 제어가 필요합니다. 이 경우 클래스 사용이 적절하지만, 앱의 어떤 부분이 공유 데이터베이스 객체에 접근할 수 있는지를 제한하는 것이 중요합니다. </p>
<p><strong>Important</strong></p>
<p>정체성을 신중하게 다뤄야합니다. 앱 전반에 걸쳐 클래스 인스턴스를 광범위하게 공유하면 논리 오류가 발생할 가능성이 높아집니다. 많이 공유되는 인스턴스를 변경할 때 그 결과를 예상하지 못할 수 있으므로, 그러한 코드를 올바르게 작성하는 데 더 많은 작업이 필요합니다. </p>
<h3 id="정체성을-제어하지-않을-때-구조체를-사용하세요">정체성을 제어하지 않을 때 구조체를 사용하세요</h3>
<p>제어하지 않는 정체성을 가진 엔티티에 대한 정보를 포함한 데이터를 모델링할 때는 구조체를 사용하세요. 원격 데이터베이스에 상담하는 앱에서는 예를 들어, 인스턴스의 정체성이 외부 엔티티에 의해 완전히 소유되고 식별자로 전달될 수 있습니다. 앱의 모델의 일관성이 서버에 저장된다면, 식별자로 구조체로 레코드를 모델링할 수 있습니다. 아래 예시에는 jsonResponse 서버에서 인코딩된 PenPalRecord 인스턴스를 포함하고 있습니다. </p>
<pre><code class="language-swift">struct PenPalRecord {
    let myID: Int
    var myNickname: String
    var recommendedPenPalID: Int
}


var myRecord = try JSONDecoder().decode(PenPalRecord.self, from: jsonResponse)</code></pre>
<p>모델 유형인 PenPalRecord에 대한 로컬 변경 사항은 유용합니다. 예를 들어, 앱은 사용자 피드백에 따라 여러 다른 펜팔을 추천할 수 있습니다. PenPalRecord 구조는 기본 데이터베이스 레코드의 아이텐티티를 제어하지 않기 때문에, 로컬 PenPalRecord 인스턴스에 대해 수행된 변경 사항이 데이터베이스의 값을 우연히 변경할 위험이 없습니다. 앱의 다른 부분이 myNickname을 변경하고 서버에 변경 요청을 제출하면, 가장 최근에 거부된 PenPal 추천이 변경으로 인해 실수로 선택되지 않을 것입니다. myID 속성이 상수로 선언되었기 때문에 로컬에서 변경될 수 없습니다. 결과적으로 데이터베이스에 대한 요청은 실수로 잘못된 레코드를 변경하지 않을 것입니다. </p>
<blockquote>
<p><em><strong>Model을 정의할 때 구조체를 왜 사용해야 하는지에 대한 답변이 될 수 있을 것 같습니다.</strong></em> </p>
</blockquote>
<h3 id="구조체와-프로토콜을-사용해-상속을-모델링하고-행동을-공유하세요">구조체와 프로토콜을 사용해 상속을 모델링하고 행동을 공유하세요</h3>
<p>구조체와 클래스는 모두 일종의 상속을 지원합니다. 구조체와 프로토콜은 프로토콜만 채택할 수 있고, 클래스에서는 상속받을 수 없습니다. <strong>그러나 클래스 상속으로 구축할 수 있는 상속 계층의 종류는 프로토콜 상속과 구조체를 사용해 모델링할 수 있습니다. 만약 처음부터 상속 관계를 구축하고 있다면, 프로토콜 상속을 선호하세요.</strong> 프로토콜은 클래스와 구조체 및 열거형이 상속에 참여할 수 있도록 허용하는 반면, 클래스 상속은 다른 클래스와만 호환됩니다. 데이터 모델링 방법을 선택할 때는 먼저 프로토콜 상속을 사용해 데이터 유형의 계층을 구축한 다음, 해당 프로토콜을 구조체에 채택해 보세요.</p>
<p><a href="https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes#Use-Structures-and-Protocols-to-Model-Inheritance-and-Share-Behavior">Choosing Between Structures and Classes</a></p>
<p>읽어주셔서 감사합니다! 오타 및 잘못된 내용은 언제든 댓글 달아주시면 최대한 빠르게 수정하겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 백준 11000 강의실 배정 풀이 (swift)]]></title>
            <link>https://velog.io/@seo-garden/BOJ-%EB%B0%B1%EC%A4%80-11000-%EA%B0%95%EC%9D%98%EC%8B%A4-%EB%B0%B0%EC%A0%95-%ED%92%80%EC%9D%B4-swift</link>
            <guid>https://velog.io/@seo-garden/BOJ-%EB%B0%B1%EC%A4%80-11000-%EA%B0%95%EC%9D%98%EC%8B%A4-%EB%B0%B0%EC%A0%95-%ED%92%80%EC%9D%B4-swift</guid>
            <pubDate>Wed, 30 Oct 2024 00:55:20 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/11000">강의실 배정</a></p>
<p>풀이(투 포인터)</p>
<p>해당 문제에 우선순위 큐와 자료구조? 사실 우선순위 큐를 구현해서까지 풀어야 하나? 라는 생각을 가장 먼저 했습니다. (직접 구현할 줄 몰라서 그런것도 있고)</p>
<p>그래서 저는 우선순위 큐를 사용하지 않고 강의가 겹치는지 여부만 확인하고 각 회의의 시작과 종료 시간을 분리한 후 시작과 종료를 순차적으로 처리하는 방식으로 풀이를 했습니다.</p>
<pre><code class="language-swift">let N = Int(readLine()!)!

var startTimes = [Int]()
var endTimes = [Int]()

for _ in 0..&lt;N {
    let input = readLine()!.split(separator: &quot; &quot;).compactMap { Int($0) }
    startTimes.append(input[0])
    endTimes.append(input[1])
}

// 시작 시간과 종료 시간을 각각 정렬
startTimes.sort()
endTimes.sort()

var startPointer = 0
var endPointer = 0
var currentRooms = 0
var maxRooms = 0

while startPointer &lt; N {
    if startTimes[startPointer] &lt; endTimes[endPointer] {
        // 새로운 강의실이 필요하므로 현재 강의실 수 증가
        currentRooms += 1
        maxRooms = max(maxRooms, currentRooms)
        startPointer += 1
    } else {
        // 기존 강의실이 비게 되므로 강의실 수 감소
        currentRooms -= 1
        endPointer += 1
    }
}

print(maxRooms)</code></pre>
<p>위 풀이의 경우 startTimes 배열과 endTimes 배열을 각각 오름차순으로 정렬합니다.
startPointer와 endPointer를 각각 startTimes와 endTimes의 시작 위치에 놓고, 시작 시간이 종료 시간보다 빠르면 강의실을 추가하고 startPointer를 증가시킵니다.
반대로 종료 시간이 더 빠르면 강의실 하나가 비게 되므로 endPointer를 증가시키고 강의실 개수를 조정합니다.
이 방식을 사용하면 전체 탐색에 O(N) 시간이 소요되므로 효율적입니다.</p>
<p>아래의 풀이는 같이 스터디하는 동생의 풀이방법을 swift로 변경해봤습니다.</p>
<pre><code class="language-swift">let N = Int(readLine()!)!

var startTimes = Array(repeating: 0, count: N)
var endTimes = Array(repeating: 0, count: N)

for i in 0..&lt;N {
    let input = readLine()!.split(separator: &quot; &quot;).compactMap { Int($0) }
    startTimes[i] = input[0]
    endTimes[i] = input[1]
}

// 시작 시간과 종료 시간을 각각 정렬
startTimes.sort()
endTimes.sort()

var roomCount = 0
var endIndex = 0

for i in 0..&lt;N {
    if (startTimes[i] &gt;= endTimes[endIndex]) {
        endIndex += 1
    }
    else {
        roomCount += 1
    }
}
print(roomCount)</code></pre>
<p>이것 역시 시작 시간과 종료 시간을 쪼개서 정렬시킨 뒤 시간 시간 순으로 순회하면서 강의실을 배정하는 방법입니다. 아래는 6번 정도의 트라이 흔적입니다. 48ms 걸리는 코드는 Rhyno 님의 FileIO Class 를 사용한 코드 입니다. 입력만 다르게 받을 뿐 코드는 똑같습니다. 
<img src="https://velog.velcdn.com/images/seo-garden/post/983eb106-4aed-48d1-8452-da952b1c513d/image.png" alt=""></p>
<p>읽어주셔서 감사합니다! 오타 및 잘못된 내용은 언제든 댓글 달아주시면 최대한 빠르게 수정하겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 백준 11497 통나무 건너뛰기 풀이 (swift)]]></title>
            <link>https://velog.io/@seo-garden/BOJ-%EB%B0%B1%EC%A4%80-11497-%ED%86%B5%EB%82%98%EB%AC%B4-%EA%B1%B4%EB%84%88%EB%9B%B0%EA%B8%B0-%ED%92%80%EC%9D%B4-swift</link>
            <guid>https://velog.io/@seo-garden/BOJ-%EB%B0%B1%EC%A4%80-11497-%ED%86%B5%EB%82%98%EB%AC%B4-%EA%B1%B4%EB%84%88%EB%9B%B0%EA%B8%B0-%ED%92%80%EC%9D%B4-swift</guid>
            <pubDate>Sat, 19 Oct 2024 10:35:06 GMT</pubDate>
            <description><![CDATA[<p>[통나무 건너뛰기] (<a href="https://www.acmicpc.net/problem/11497">https://www.acmicpc.net/problem/11497</a>)</p>
<h3 id="풀이그리디">풀이(그리디)</h3>
<p>기존엔 left, right 로 구현해서 가장 높이 차가 최소인 경우를 찾아서 구현을 했는데, 더 좋은 방법이 있어서 가져왔습니다 😄</p>
<p>다른 분들의 아이디어에서 가장 중요한 건, 인접한 두 칸의 통나무는 비교하지 않는 것입니다. 이미 다른 더 큰 차이와 비교했을 때 최댓값에 영향을 주지 않기 때문에 문제에서 요구하는 가장 큰 차이를 구할 때 n 과 n-1 의 통나무는 무시하는 것입니다. </p>
<p>통나무를 오름차순으로 정렬했을 때, 1번 통나무는 가장 작은 값이고, N번 통나무는 가장 큰 값인데, 이런 방식으로 배치하게 되면 결국 인접한 통나무 사이의 가장 큰 차이가 나올 가능성이 있는 것은 두 통나무가 두 칸씩 건너뛰어져 있는 위치입니다. </p>
<p>n-1과 n은 배치에서 바로 옆에 있는 통나무들입니다. 하지만, 이 통나무들의 차이는 n-2와 n의 차이에 비해 항상 작을 가능성이 높습니다.</p>
<p>그럼 2칸보다 3칸이나 4칸으로 비교하는게 더 빠를 수 있지 않냐 라고 생각하실 수도 있는데, 비교하는 논리가 직관적이지 않습니다. 좌우에서 고르게 퍼져나가며 점진적으로 증가하기 때문에, 2칸씩 뛰어넘어 배치하는 것이 중요합니다. left, right 보다 연산이 절반 가량 줄어듭니다.</p>
<pre><code class="language-swift">import Foundation

final class FileIO {
    private let buffer:[UInt8]
    private var index: Int = 0

    init(fileHandle: FileHandle = FileHandle.standardInput) {

        buffer = Array(try! fileHandle.readToEnd()!)+[UInt8(0)] // 인덱스 범위 넘어가는 것 방지
    }

    @inline(__always) private func read() -&gt; UInt8 {
        defer { index += 1 }

        return buffer[index]
    }

    @inline(__always) func readInt() -&gt; Int {
        var sum = 0
        var now = read()
        var isPositive = true

        while now == 10
                || now == 32 { now = read() } // 공백과 줄바꿈 무시
        if now == 45 { isPositive.toggle(); now = read() } // 음수 처리
        while now &gt;= 48, now &lt;= 57 {
            sum = sum * 10 + Int(now-48)
            now = read()
        }

        return sum * (isPositive ? 1:-1)
    }

    @inline(__always) func readString() -&gt; String {
        var now = read()

        while now == 10 || now == 32 { now = read() } // 공백과 줄바꿈 무시

        let beginIndex = index-1

        while now != 10,
              now != 32,
              now != 0 { now = read() }

        return String(bytes: Array(buffer[beginIndex..&lt;(index-1)]), encoding: .ascii)!
    }

    @inline(__always) func readByteSequenceWithoutSpaceAndLineFeed() -&gt; [UInt8] {
        var now = read()

        while now == 10 || now == 32 { now = read() } // 공백과 줄바꿈 무시

        let beginIndex = index-1

        while now != 10,
              now != 32,
              now != 0 { now = read() }

        return Array(buffer[beginIndex..&lt;(index-1)])
    }
}
//Thanks to Rhyno
//위의 FileIO클래스는 Swift 에서 입출력 시간을 줄여주는 코드입니다.
let fileIO = FileIO()
let T = fileIO.readInt()

for _ in 0..&lt;T {
    let N = fileIO.readInt()
    var L = [Int](repeating: 0, count: N)
    for i in 0..&lt;N {
        L[i] = fileIO.readInt()
    }
    L.sort()
    var height = 0
    for i in 2..&lt;N {
        height = max(height, L[i] - L[i - 2]) // 2칸 간격의 높이차 계산
    }
    print(height)
}</code></pre>
<p>읽어주셔서 감사합니다! 오타 및 잘못된 내용은 언제든 댓글 달아주시면 최대한 빠르게 수정하겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 백준 15486 퇴사 2 풀이 (swift)]]></title>
            <link>https://velog.io/@seo-garden/BOJ-%EB%B0%B1%EC%A4%80-15486-%ED%87%B4%EC%82%AC-2-%ED%92%80%EC%9D%B4-swift</link>
            <guid>https://velog.io/@seo-garden/BOJ-%EB%B0%B1%EC%A4%80-15486-%ED%87%B4%EC%82%AC-2-%ED%92%80%EC%9D%B4-swift</guid>
            <pubDate>Sun, 13 Oct 2024 07:24:39 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/15486">퇴사 2</a></p>
<h3 id="풀이-dp">풀이 (DP)</h3>
<p>dp배열을 N+1 칸 만큼의 배열로 초기화를 합니다. 아래와 같이 예제 입력이 들어왔을 때
7
3 10
5 20
1 10
1 20
2 15
4 40
2 200</p>
<p>날짜 1 (now = 0):
상담 일수 T = 3, 이익 P = 10 1일차 상담을 하면 3일 후인 4일차에 이익을 얻게 됨.
dp[4] = max(dp[4], dp[1] + 10) -&gt; dp[4] = 10
그리고 현재까지 이익은 유지해야 하므로 dp[1] = max(dp[1], dp[0]) -&gt; dp[1] = 0
dp 상태: [0, 0, 0, 0, 10, 0, 0, 0]</p>
<p>날짜 2 (now = 1):
상담 일수 T = 5, 이익 P = 20 2일차 상담을 하면 7일 후에 이익을 얻게 됨.
dp[7] = max(dp[7], dp[2] + 20) -&gt; dp[7] = 20
현재까지 이익 유지: dp[2] = max(dp[2], dp[1]) -&gt; dp[2] = 0
dp 상태: [0, 0, 0, 0, 10, 0, 0, 20]</p>
<p>날짜 3 (now = 2):
상담 일수 T = 1, 이익 P = 10 3일차 상담을 하면 4일 후에 이익을 얻게 됨.
dp[4] = max(dp[4], dp[3] + 10) -&gt; dp[4] = max(10, 0 + 10) -&gt; dp[4] = 10 (변화 없음)
현재까지 이익 유지: dp[3] = max(dp[3], dp[2]) -&gt; dp[3] = 0
dp 상태: [0, 0, 0, 0, 10, 0, 0, 20]</p>
<p>날짜 4 (now = 3):
상담 일수 T = 1, 이익 P = 20 4일차 상담을 하면 5일 후에 이익을 얻게 됨.
dp[5] = max(dp[5], dp[4] + 20) -&gt; dp[5] = max(0, 10 + 20) -&gt; dp[5] = 30
현재까지 이익 유지: dp[4] = max(dp[4], dp[3]) -&gt; dp[4] = 10
dp 상태: [0, 0, 0, 0, 10, 30, 0, 20]</p>
<p>날짜 5 (now = 4): 
상담 일수 T = 2, 이익 P = 15 5일차 상담을 하면 7일 후에 이익을 얻게 됨.
dp[7] = max(dp[7], dp[5] + 15) -&gt; dp[7] = max(20, 30 + 15) -&gt; dp[7] = 45
현재까지 이익 유지: dp[5] = max(dp[5], dp[4]) -&gt; dp[5] = 30
dp 상태: [0, 0, 0, 0, 10, 30, 0, 45]</p>
<p>날짜 6 (now = 5):
상담 일수 T = 4, 이익 P = 40
6일차 상담은 10일차에 완료되므로 퇴사 후가 되어 상담을 할 수 없음.
현재까지 이익 유지: dp[6] = max(dp[6], dp[5]) -&gt; dp[6] = 30
dp 상태: [0, 0, 0, 0, 10, 30, 30, 45]
날짜 7 (now = 6):</p>
<p>상담 일수 T = 2, 이익 P = 200
7일차 상담도 퇴사일 이후에 완료되므로 상담을 할 수 없음.
현재까지 이익 유지: dp[7] = max(dp[7], dp[6]) -&gt; dp[7] = 45
dp 상태: [0, 0, 0, 0, 10, 30, 30, 45]</p>
<p>퇴사일을 넘기지 않는 선에서 최대의 이익을 얻는 방법으로 퇴사일까지 얻을 수 있는 최대 이익은 45</p>
<pre><code class="language-swift">import Foundation

let N = Int(readLine()!)!
var dp = Array(repeating: 0, count: N+1)

for now in 0..&lt;N {
    let input = readLine()!.split(separator: &quot; &quot;).map { Int($0)! }
    let T = input[0]
    let P = input[1]

    dp[now + 1] = max(dp[now + 1], dp[now])        // 현재까지 얻은 최대 이익을 그 다음 날로 넘겨줌 (현재까지 계산한 최대 이익 유지)

    if now + T &lt; N + 1 {        // 현재 상담을 진행할 수 있을 때만 (상담을 끝내는 날짜가 퇴사 날짜를 넘지 않을 경우)
        dp[now + T] = max(dp[now + T], dp[now] + P)
        print(dp[now + T])
    }
}
print(dp[N])</code></pre>
<p>읽어주셔서 감사합니다! 오타 및 잘못된 내용은 언제든 댓글 달아주시면 최대한 빠르게 수정하겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 백준 1940 주몽 풀이 (swift)]]></title>
            <link>https://velog.io/@seo-garden/BOJ-%EB%B0%B1%EC%A4%80-1940-%EC%A3%BC%EB%AA%BD-%ED%92%80%EC%9D%B4-swift</link>
            <guid>https://velog.io/@seo-garden/BOJ-%EB%B0%B1%EC%A4%80-1940-%EC%A3%BC%EB%AA%BD-%ED%92%80%EC%9D%B4-swift</guid>
            <pubDate>Thu, 03 Oct 2024 15:00:01 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1940">주몽</a></p>
<h3 id="풀이-투포인터">풀이 (투포인터)</h3>
<p>3번째 입력한 값을 입력받을 때 오름차순으로 받고, inputArr[left] 와 inputArr[right]의 합이 M과 동일하면 count 를 증가시키면서 left += 1, right -= 1 을 해주고 아래의 if 문은 실행하지 않습니다.
만약 leftIndex + rightIndex &lt; M 보다 작다면 left += 1, 그게 아닌 경우는 right -=1 을 해주면서 값을 찾으면 됩니다. </p>
<pre><code class="language-swift">let N = Int(readLine()!)!
let M = Int(readLine()!)!
let inputArr = readLine()!.split(separator: &quot; &quot;).map { Int($0)! }.sorted()

var left = 0
var right = inputArr.count - 1          //배열의 마지막 값은 index - 1
var count = 0

while left &lt; right {
    if inputArr[left] + inputArr[right] == M {
        count += 1
        left += 1
        right -= 1

        continue
    }

    if inputArr[left] + inputArr[right] &lt; M {
        left += 1
    } else { right -= 1 }
}

print(count)</code></pre>
<h3 id="후기">후기</h3>
<p>투 포인터를 처음 공부하실 때 풀기 좋은 문제인 것 같습니다.</p>
<p>읽어주셔서 감사합니다! 오타 및 잘못된 내용은 언제든 댓글 달아주시면 최대한 빠르게 수정하겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 백준 12871 무한 문자열 풀이 (swift)]]></title>
            <link>https://velog.io/@seo-garden/BOJ-%EB%B0%B1%EC%A4%80-12871-%EB%AC%B4%ED%95%9C-%EB%AC%B8%EC%9E%90%EC%97%B4-%ED%92%80%EC%9D%B4-swift</link>
            <guid>https://velog.io/@seo-garden/BOJ-%EB%B0%B1%EC%A4%80-12871-%EB%AC%B4%ED%95%9C-%EB%AC%B8%EC%9E%90%EC%97%B4-%ED%92%80%EC%9D%B4-swift</guid>
            <pubDate>Thu, 03 Oct 2024 14:47:55 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/12871">무한 문자열</a></p>
<h3 id="풀이--구현">*<em>풀이 *</em> 구현</h3>
<p>다른 분들은 최소공배수를 많이 이용해서 푸셨는데, 다른 방법인 것 같아서 들고왔습니다 !!
두 문자열이 같은지 비교하고, 같으면 1을 출력하고 다르면 0을 출력하는건데
result1 에 input2, result2 에 input1 을 추가하고 비교하면 끝입니다.</p>
<pre><code class="language-swift">import Foundation
let input1 = Array(readLine()!)
let input2 = Array(readLine()!)
var result1 = input1
var result2 = input2
for i in input2 {
    result1.append(i)
}
for i in input1 {
    result2.append(i)
}
if result1 == result2 {
    print(&quot;1&quot;)
} else {
    print(&quot;0&quot;)
}</code></pre>
<p>읽어주셔서 감사합니다! 오타 및 잘못된 내용은 언제든 댓글 달아주시면 최대한 빠르게 수정하겠습니다!</p>
]]></description>
        </item>
    </channel>
</rss>