<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>nbh.dev</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 03 Nov 2025 00:05:28 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>nbh.dev</title>
            <url>https://velog.velcdn.com/images/d_ham8_8/profile/2a6017cb-73fa-48ee-8481-38aae110f026/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. nbh.dev. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/d_ham8_8" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Spring Boot는 어떻게 스스로 동작하는가 (WAS, Bean 등록)]]></title>
            <link>https://velog.io/@d_ham8_8/Spring-Boot%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94%EA%B0%80-WAS-Bean-%EB%93%B1%EB%A1%9D</link>
            <guid>https://velog.io/@d_ham8_8/Spring-Boot%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%8A%A4%EC%8A%A4%EB%A1%9C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94%EA%B0%80-WAS-Bean-%EB%93%B1%EB%A1%9D</guid>
            <pubDate>Mon, 03 Nov 2025 00:05:28 GMT</pubDate>
            <description><![CDATA[<h1 id="spring-boot는-어떻게-스스로-동작하는가-was-bean-등록">Spring Boot는 어떻게 스스로 동작하는가 (WAS, Bean 등록)</h1>
<blockquote>
<p>Spring Boot 애플리케이션은 <code>main</code> 메서드를 실행하기만 하면, 별도의 서버 설치 없이도 웹 서비스가 즉시 동작한다.</p>
</blockquote>
<p>이 편리함 속에는 웹을 구동하는 핵심 구성 요소와 Spring의 객체 관리 메커니즘이 숨어있다. 이 &#39;마법&#39;처럼 보이는 현상을 이해하기 위해서는, 먼저 웹 서버와 WAS의 차이를 알아야 하고, Spring이 내부에서 객체(Bean)를 어떻게 찾아내고 관리하는지 파악해야 한다.</p>
<hr>
<h2 id="1-웹-서버web-server-vs-wasweb-application-server">1. 웹 서버(Web Server) vs WAS(Web Application Server)</h2>
<p>웹 서비스를 구성할 때, 이 두 용어는 자주 혼용되지만 명확한 역할 차이가 있다.</p>
<h3 id="a-웹-서버-web-server-정적-콘텐츠-담당">A. 웹 서버 (Web Server): 정적 콘텐츠 담당</h3>
<p>웹 서버는 HTTP 요청을 받아 <strong>정적(Static) 콘텐츠</strong>를 제공하는 서버다. 정적 콘텐츠란, 요청에 따라 변하지 않는 파일(HTML, CSS, JavaScript, 이미지)을 의미한다.</p>
<ul>
<li><strong>주요 기능</strong>: HTTP 프로토콜을 통해 클라이언트의 요청을 받고, 이미 준비된 정적 파일을 응답으로 전송한다.</li>
<li><strong>대표 예시</strong>: Nginx, Apache HTTP Server</li>
</ul>
<p>만약 웹 서버가 처리할 수 없는 동적인 요청이 들어오면, 웹 서버는 해당 요청을 WAS로 전달하는 중개자 역할도 수행한다.</p>
<h3 id="b-was-web-application-server-동적-콘텐츠-담당">B. WAS (Web Application Server): 동적 콘텐츠 담당</h3>
<p>WAS는 웹 서버의 기능에 더해, <strong>애플리케A션 로직을 실행</strong>하여 <strong>동적(Dynamic) 콘텐츠</strong>를 생성하고 제공하는 서버다.</p>
<ul>
<li><strong>주요 기능</strong>: 클라이언트의 요청에 따라 Java 코드(비즈니스 로직)를 실행하고, 데이터베이스를 조회하며, 그 결과를 바탕으로 새로운 HTML 페이지나 JSON 데이터를 생성하여 응답한다.</li>
<li><strong>핵심 구성</strong>: 웹 서버 기능 + <strong>웹 컨테이너(Web Container)</strong>.<ul>
<li>Java 진영에서는 &#39;서블릿 컨테이너&#39;(Servlet Container)가 이 역할을 하며, 서블릿(Servlet)의 생명주기를 관리하고 동적 요청을 처리한다.</li>
</ul>
</li>
<li><strong>대표 예시</strong>: Apache Tomcat, JBoss, WebLogic</li>
</ul>
<p>즉, WAS는 정적 콘텐츠 처리(웹 서버)와 동적 콘텐츠 처리(웹 컨테이너)를 모두 수행할 수 있는, 더 포괄적인 개념의 서버다.</p>
<h3 id="spring-boot의-내장-톰캣embedded-tomcat">Spring Boot의 내장 톰캣(Embedded Tomcat)</h3>
<p>그렇다면 Spring Boot를 실행할 때 별도로 설치하지 않았는데도 동작하는 &#39;톰캣(Tomcat)&#39;은 무엇일까?</p>
<ul>
<li><strong>톰캣은 전통적인 WAS(정확히는 서블릿 컨테이너)다.</strong></li>
<li>Spring Boot는 이 톰캣을 <strong>내장(Embedded)</strong>하고 있다. 즉, Spring Boot 애플리케이션을 실행하면, 애플리케이션 코드 내에서 톰캣 서버가 함께 구동된다.</li>
<li>따라서 Spring Boot는 <code>main</code> 메서드 실행 시, <strong>그 자체로 하나의 완전한 WAS</strong>가 되어 서비스 요청을 처리할 수 있다.</li>
</ul>
<hr>
<h2 id="2-spring은-객체bean를-어떻게-관리하는가">2. Spring은 객체(Bean)를 어떻게 관리하는가?</h2>
<p>Spring의 핵심은 &#39;제어의 역전(IoC)&#39;이며, Spring IoC 컨테이너가 애플리케이션의 객체(Bean)를 생성하고 관리한다. 컨테이너가 객체를 관리하기 위해서는, 먼저 &quot;어떤 객체들을 관리해야 하는지&quot; 알려주는 &#39;Bean 등록&#39; 과정이 필요하다.</p>
<p>Spring Boot에서는 주로 다음 두 가지 방법을 사용한다.</p>
<h3 id="a-컴포넌트-스캔-component-scanning">A. 컴포넌트 스캔 (Component Scanning)</h3>
<p>가장 보편적이고 자동화된 방법이다.</p>
<ul>
<li><strong>사용 방법</strong>: 클래스에 <code>@Component</code> 어노테이션을 붙인다.<ul>
<li>Spring은 <code>@Component</code>를 확장한 <code>@Service</code> (비즈니스 로직), <code>@Repository</code> (데이터 접근), <code>@Controller</code>, <code>@RestController</code> (웹) 등의 어노테이션도 모두 인식한다.</li>
</ul>
</li>
<li><strong>동작 방식</strong>: Spring Boot가 실행될 때, <code>@SpringBootApplication</code> 어노테이션이 붙은 메인 클래스의 패키지를 기준으로 하위 패키지를 모두 스캔(Scan)한다. 이 과정에서 해당 어노테이션이 붙은 클래스들을 찾아 자동으로 IoC 컨테이너에 Bean으로 등록한다.</li>
<li><strong>장점</strong>:<ul>
<li>매우 간편하다. 어노테이션 하나만 붙이면 자동으로 등록된다.</li>
<li>&quot;관심사에 따라 어노테이션을 분리&quot;하여 코드의 역할을 명확하게 드러낼 수 있다.</li>
</ul>
</li>
<li><strong>단점</strong>:<ul>
<li>내가 직접 코드를 수정할 수 없는 외부 라이브러리의 클래스는 이 방법으로 등록할 수 없다.</li>
<li>등록하려는 Bean의 초기화에 복잡한 로직이 필요할 경우 구현이 제한된다.</li>
</ul>
</li>
</ul>
<h3 id="b-java-설정-파일-java-configuration">B. Java 설정 파일 (Java Configuration)</h3>
<p>컴포넌트 스캔으로 처리하기 어려운 Bean들을 수동으로 등록하는, 명시적이고 유연한 방법이다.</p>
<ul>
<li><strong>사용 방법</strong>: <code>config</code> 패키지 등에 <code>@Configuration</code> 어노테이션이 붙은 설정 클래스를 만든다. 그 후, Bean으로 등록하고자 하는 객체를 반환하는 메서드를 만들고 <code>@Bean</code> 어노테이션을 붙인다.</li>
<li><strong>동작 방식</strong>: Spring 컨테이너는 <code>@Configuration</code> 클래스를 먼저 인식하고, 해당 클래스 내부의 <code>@Bean</code> 어노테이션이 붙은 메서드들을 실행한다. 그리고 그 메서드가 반환하는 객체를 IoC 컨테이너에 Bean으로 등록한다.</li>
</ul>
<pre><code class="language-java">// 예: 외부 라이브러리의 ObjectMapper를 Bean으로 등록하는 경우
@Configuration
public class AppConfig {

    @Bean
    public ObjectMapper objectMapper() {
        // 이 메서드 안에서 복잡한 초기화 로직 수행 가능
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        return mapper;
    }
}</code></pre>
<ul>
<li>장점:<ul>
<li>외부 라이브러리의 클래스도 Bean으로 등록할 수 있다.</li>
<li>메서드 내에서 객체 생성 및 초기화 로직을 자유롭게 구현할 수 있어 유연성이 높다.</li>
<li>Bean 등록 과정을 개발자가 명시적으로 제어할 수 있다.</li>
</ul>
</li>
<li>단점:<ul>
<li>등록할 Bean이 많아지면 설정 코드가 길어지고 번거롭다.</li>
<li>모든 클래스를 이 방식으로 등록하면 컴포넌트 스캔의 편리함을 잃게 된다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="결론">결론</h2>
<p>Spring Boot 애플리케이션의 실행은 내장된 WAS(톰캣)를 구동시켜 그 자체로 웹 서버이자 애플리케이션 서버로 동작함을 의미한다. 이렇게 구동된 서버 내부에서 Spring 컨테이너는 @Component 스캔과 @Configuration 설정 등을 바탕으로 필요한 Bean(객체)들을 미리 준비하고, 개발자가 정의한 비즈니스 로직을 수행할 준비를 마친다.</p>
<p>이 두 가지 핵심 원리를 이해하는 것은 Spring Boot의 자동화된 편리함 이면의 동작 방식을 파악하고, 문제 발생 시 더 깊이 있는 트러블슈팅을 가능하게 하는 기반이 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring의 탄생과 제어의 역전 (Framework vs Library)]]></title>
            <link>https://velog.io/@d_ham8_8/Spring%EC%9D%98-%ED%83%84%EC%83%9D%EA%B3%BC-%EC%A0%9C%EC%96%B4%EC%9D%98-%EC%97%AD%EC%A0%84-Framework-vs-Library</link>
            <guid>https://velog.io/@d_ham8_8/Spring%EC%9D%98-%ED%83%84%EC%83%9D%EA%B3%BC-%EC%A0%9C%EC%96%B4%EC%9D%98-%EC%97%AD%EC%A0%84-Framework-vs-Library</guid>
            <pubDate>Sun, 26 Oct 2025 23:56:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&quot;왜 Spring을 사용해야 할까?&quot;</p>
</blockquote>
<p>Java로 웹 애플리케이션을 개발할 때, Spring Framework는 거의 표준처럼 사용된다. 하지만 이 강력한 도구가 처음부터 당연하게 존재했던 것은 아니다. Spring이 등장하기 이전의 엔터프라이즈 개발 환경은 지금과 매우 달랐으며, 심각한 문제들을 안고 있었다.</p>
<p>Spring의 탄생 배경과 이로 인해 정립된 &#39;프레임워크&#39;와 &#39;라이브러리&#39;의 근본적인 차이점을 이해하는 것은 Java 개발자에게 필수적인 기초 역량이다.</p>
<hr>
<h2 id="1-spring-framework는-왜-탄생했는가">1. Spring Framework는 왜 탄생했는가?</h2>
<p>Spring의 등장은 <strong>EJB(Enterprise JavaBeans)</strong>라는 기술이 주도하던 2000년대 초반의 Java 엔터프라이즈 환경에 대한 반작용에서 시작되었다. 당시 EJB는 트랜잭션, 보안, 분산 처리 등 기업용 애플리케이션에 필요한 복잡한 기능들을 제공했지만, 개발자들에게 감당하기 힘든 문제점들을 안겨주었다.</p>
<h3 id="a-ejb-시대의-문제점">A. EJB 시대의 문제점</h3>
<ol>
<li><strong>극심한 복잡성</strong>: EJB를 사용하기 위해서는 수많은 XML 설정 파일과 상속받아야 하는 인터페이스, 원격 호출을 위한 별도의 객체 등, 실제 비즈니스 로직과 관계없는 상용구(Boilerplate) 코드가 너무 많이 필요했다.</li>
<li><strong>무거운 환경 (Heavyweight)</strong>: EJB는 EJB 컨테이너를 포함하는 무거운 &#39;애플리케이션 서버&#39;(WebLogic, WebSphere 등)에서만 동작했다. 이는 애플리케이션을 한 번 실행하고 테스트하는 데 수 분이 걸릴 정도로 개발 속도를 저하시켰다.</li>
<li><strong>강한 결합과 침투성 (Intrusive)</strong>: 비즈니스 로직을 담고 있는 순수한 Java 객체가 EJB 관련 API에 직접 의존해야 했다. 이로 인해 코드는 특정 기술(EJB)에 강하게 종속되었고, 다른 환경에서 재사용하거나 테스트하기가 불가능에 가까웠다.</li>
<li><strong>테스트의 어려움</strong>: EJB 컨테이너 없이는 코드가 동작하지 않았기 때문에, 간단한 비즈니스 로직을 테스트하는 것조차 서버를 구동해야 하는 복잡한 &#39;통합 테스트&#39;가 되어버렸다.</li>
</ol>
<h3 id="b-spring이-해결하고자-한-문제">B. Spring이 해결하고자 한 문제</h3>
<p>2002년, 로드 존슨(Rod Johnson)은 이러한 EJB의 문제점들을 비판하며 더 나은 대안을 제시했다. 이것이 Spring Framework의 시작이었다.</p>
<p>Spring이 해결하고자 한 핵심 문제는 <strong>&quot;개발자가 비즈니스 로직이라는 본질에만 집중할 수 있게 하자&quot;</strong>는 것이었다.</p>
<ol>
<li><strong>POJO (Plain Old Java Object) 프로그래밍</strong>: Spring은 EJB와 달리 특정 기술에 종속되지 않는 &#39;평범한 Java 객체&#39;(POJO)를 사용하여 비즈니스 로직을 구현하도록 했다. 이는 코드가 특정 환경이나 기술로부터 자유로워짐을 의미했다.</li>
<li><strong>의존성 주입 (DI)과 제어의 역전 (IoC)</strong>: 객체의 생성과 생명 주기 관리를 개발자가 직접 하는 것이 아니라, Spring 컨테이너가 대신 처리하도록 했다. (IoC) 이를 통해 객체 간의 의존성을 외부에서 주입(DI)받게 되면서 결합도가 극도로 낮아졌다.</li>
<li><strong>AOP (관점 지향 프로그래밍)</strong>: 트랜잭션, 로깅, 보안처럼 여러 비즈니스 로직에서 공통으로 필요한 부가 기능(공통 관심사)들을 실제 비즈니스 코드에서 분리해냈다. 이로써 비즈니스 로드는 순수하게 자신의 논리에만 집중할 수 있게 되었다.</li>
<li><strong>경량 컨테이너</strong>: 무거운 애플리케이션 서버가 아닌, Tomcat 같은 가벼운 &#39;서블릿 컨테이너&#39;에서도 애플리케이션이 충분히 동작할 수 있게 만들었다.</li>
</ol>
<p>결국 Spring은 EJB의 복잡성을 걷어내고, <strong>낮은 결합도(Decoupling)</strong>와 <strong>높은 테스트 용이성(Testability)</strong>을 가진 유연한 애플리케이션을 만들 수 있는 환경을 제공했다.</p>
<hr>
<h2 id="2-framework-vs-library-제어-흐름의-차이">2. Framework vs Library: 제어 흐름의 차이</h2>
<p>Spring을 &#39;프레임워크&#39;라고 부르는 이유는 &#39;라이브러리&#39;와 근본적인 차이점이 있기 때문이다. 이 차이를 구분하는 핵심 기준은 <strong>&quot;제어 흐름의 주체가 누구인가?&quot;</strong>이다.</p>
<h3 id="a-library-라이브러리">A. Library (라이브러리)</h3>
<p>라이브러리는 <strong>개발자가 제어 흐름의 주체</strong>가 된다.
마치 &#39;도구 상자&#39;와 같다. 개발자는 필요할 때마다 도구(라이브러리)를 가져와서 사용하고, 언제 사용할지, 어떻게 사용할지를 직접 결정한다.</p>
<ul>
<li><strong>사용 방식</strong>: 내 코드(애플리케이션)가 라이브러리의 코드를 <strong>호출(Call)</strong>한다.</li>
<li><strong>예시 (Java의 <code>ArrayList</code>)</strong>:</li>
</ul>
<pre><code class="language-java">// 개발자가 직접 &#39;ArrayList&#39; 라이브러리를 생성하고 호출한다.
// 제어의 흐름은 개발자의 코드에 있다.
List&lt;String&gt; myList = new ArrayList&lt;&gt;();

myList.add(&quot;Data1&quot;); // 필요할 때 호출
myList.add(&quot;Data2&quot;); // 필요할 때 호출

// 라이브러리는 호출되었을 때만 동작한다.
System.out.println(myList.get(0));</code></pre>
<h3 id="b-framework-프레임워크">B. Framework (프레임워크)</h3>
<p>프레임워크는 프레임워크가 제어 흐름의 주체가 된다. &#39;잘 지어진 뼈대&#39;나 &#39;설계도&#39;와 같다. 프레임워크는 전체적인 구조와 동작 흐름을 이미 가지고 있으며, 개발자는 그 뼈대 속의 비어 있는 공간에 자신의 코드를 작성해 끼워 넣는다.</p>
<p>사용 방식: 프레임워크의 코드가 내 코드(개발자가 작성한 코드)를 <strong>호출(Call)</strong>한다.</p>
<p>이를 <strong>제어의 역전 (IoC, Inversion of Control)</strong>이라고 부른다. &quot;Don&#39;t call us, we&#39;ll call you.&quot; (우리를 부르지 마세요, 우리가 당신을 부를 겁니다.)라는 할리우드 원칙으로도 유명하다.</p>
<p>예시 (Spring Framework의 @RestController):</p>
<pre><code class="language-java">
// 개발자는 &#39;MyController&#39; 클래스와 &#39;hello()&#39; 메서드를 정의할 뿐,
// 이 코드를 언제, 어떻게 실행할지 결정하지 않는다.
@RestController
public class MyController {

    @GetMapping(&quot;/hello&quot;)
    public String hello() {
        // 이 메서드는 개발자가 직접 호출하지 않는다.
        return &quot;Hello, Spring!&quot;;
    }
}
// 웹 브라우저에서 &quot;/hello&quot; 요청이 들어오면,
// Spring Framework(의 DispatcherServlet)가 이 &#39;hello()&#39; 메서드를
// 대신 찾아서 실행(호출)하고 응답을 처리한다.
</code></pre>
<h3 id="결론">결론</h3>
<p>Spring Framework는 EJB의 복잡성을 해결하기 위해 &#39;제어의 역전(IoC)&#39;이라는 개념을 도입하여 개발자가 비즈니스 로직에만 집중할 수 있도록 만들었다. 라이브러리가 개발자의 코드에 의해 호출되는 수동적인 &#39;도구&#39;라면, 프레임워크는 개발자의 코드를 호출하는 능동적인 &#39;뼈대&#39;다.</p>
<p>이러한 도구들의 철학적 차이를 이해하는 것은, 우리가 특정 기술을 &#39;왜&#39; 그리고 &#39;어떻게&#39; 사용해야 하는지에 대한 명확한 기준을 제시해 준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] HashSet의 효율성 ]]></title>
            <link>https://velog.io/@d_ham8_8/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-HashSet%EC%9D%98-%ED%9A%A8%EC%9C%A8%EC%84%B1</link>
            <guid>https://velog.io/@d_ham8_8/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-HashSet%EC%9D%98-%ED%9A%A8%EC%9C%A8%EC%84%B1</guid>
            <pubDate>Sun, 19 Oct 2025 15:02:09 GMT</pubDate>
            <description><![CDATA[<h1 id="코드의-속도를-결정하는-내부-원리-hashset-시간-복잡도">코드의 속도를 결정하는 내부 원리 (HashSet, 시간 복잡도)</h1>
<p>프로그래밍을 하다 보면 &#39;어떻게&#39; 구현하는가만큼 &#39;왜&#39; 그렇게 동작하는지 이해하는 것이 중요하다는 것을 깨닫게 된다. 특히 대용량 데이터를 다룰 때, 자료구조의 내부 동작 방식과 알고리즘의 효율성에 대한 이해는 코드의 성능을 극적으로 좌우한다.</p>
<p>데이터의 중복을 효율적으로 제거하는 <code>HashSet</code>과 알고리즘의 성능을 평가하는 척도인 시간 복잡도 <code>O(n)</code>, <code>O(log n)</code>는 그 중요성을 잘 보여주는 대표적인 예시다.</p>
<hr>
<h2 id="1-hashset은-어떻게-중복을-허용하지-않는가">1. HashSet은 어떻게 중복을 허용하지 않는가?</h2>
<p>List와 달리 Set은 데이터의 중복을 허용하지 않는다. 그중에서도 <code>HashSet</code>은 중복 여부를 매우 빠르게 판단하는데, 그 비결은 <strong><code>hashCode()</code></strong>와 <strong><code>equals()</code></strong> 메서드를 활용하는 내부 메커니즘에 있다.</p>
<h3 id="a-내부-동작-방식---hashcode와-equals">A. 내부 동작 방식 - <code>hashCode()</code>와 <code>equals()</code></h3>
<p><code>HashSet</code>은 내부적으로 데이터를 <code>HashMap</code>의 키(key)로 저장하여 관리한다. 새로운 데이터를 추가하는 과정은 다음과 같다.</p>
<ol>
<li><p><strong><code>hashCode()</code>로 저장 위치 계산</strong>: 객체를 추가하면, 먼저 해당 객체의 <code>hashCode()</code> 메서드를 호출하여 고유한 정수 값(해시 코드)을 얻는다. 이 해시 코드를 통해 데이터가 저장될 내부 배열의 위치(bucket)를 즉시 계산한다. 이는 모든 데이터와 비교하지 않고 저장될 위치를 한 번에 찾아가는 과정이다.</p>
</li>
<li><p><strong><code>equals()</code>로 최종 중복 확인</strong>: 만약 <code>hashCode()</code>로 계산한 위치에 이미 다른 데이터가 존재한다면(해시 충돌), 그때 비로소 <code>equals()</code> 메서드를 호출한다. <code>equals()</code>는 두 객체의 내용이 실제로 동일한지 비교하여 최종적으로 중복 여부를 판단한다. 여기서 <code>true</code>가 반환되면, <code>HashSet</code>은 이미 동일한 데이터가 존재한다고 판단하고 새 데이터의 추가를 거부한다.</p>
</li>
</ol>
<pre><code class="language-java">Set&lt;String&gt; fruitSet = new HashSet&lt;&gt;();

fruitSet.add(&quot;Apple&quot;); // 1. &quot;Apple&quot;.hashCode() -&gt; 위치 계산 후 저장
fruitSet.add(&quot;Banana&quot;); // 2. &quot;Banana&quot;.hashCode() -&gt; 위치 계산 후 저장
fruitSet.add(&quot;Apple&quot;); // 3. &quot;Apple&quot;.hashCode() -&gt; 기존 &quot;Apple&quot;과 같은 위치 발견
                       // 4. &quot;Apple&quot;.equals(기존 &quot;Apple&quot;) -&gt; true 반환, 추가하지 않음</code></pre>
<p>이처럼 HashSet은 모든 데이터를 순차적으로 비교하는 대신, hashCode()를 통해 비교 대상을 최소화하고, 불가피한 경우에만 equals()로 정밀하게 비교한다. 이 방식 덕분에 데이터의 양이 아무리 많아져도 평균적으로 O(1), 즉 거의 일정한 속도로 데이터의 존재 여부를 확인할 수 있다.</p>
<p><strong>언제 사용할까?</strong></p>
<ul>
<li>데이터의 중복을 제거해야 할 때</li>
<li>데이터의 저장 순서가 중요하지 않을 때</li>
<li>특정 데이터의 존재 여부를 빠르게 확인해야 할 때</li>
</ul>
<hr>
<h1 id="2-효율성의-척도-on-vs-olog-n">2. 효율성의 척도: O(n) vs O(log n)</h1>
<p>시간 복잡도는 데이터 크기(n)가 증가할 때 연산 횟수가 어떻게 변하는지를 나타내는 지표다. O(n)과 O(log n)은 가장 기본적인 개념이지만, 성능에서 극명한 차이를 보인다.</p>
<h2 id="a-on--선형-탐색-linear-time">A. O(n)- 선형 탐색 (Linear Time)</h2>
<p>데이터의 크기(n)와 연산 횟수가 정비례하는 방식이다.</p>
<ul>
<li>예를 들어, 책장에서 원하는 책을 찾기 위해 첫 번째 칸부터 마지막 칸까지 하나씩 순서대로 확인하는 것과 같다. 운이 나쁘면 모든 책장을 다 확인해야 한다.</li>
<li>성능 같은 경우 데이터가 10배 늘어나면, 연산 횟수도 10배로 늘어난다. 직관적이지만 비효율적일 수 있다.</li>
</ul>
<h2 id="b-olog-n-이진-탐색-logarithmic-time">B. O(log n): 이진 탐색 (Logarithmic Time)</h2>
<p>한 번의 연산마다 탐색 범위가 절반으로 줄어드는 방식이다. 이 방식은 데이터가 정렬되어 있다는 조건이 필수적이다.</p>
<ul>
<li>전화번호부에서 특정 이름을 찾을 때, 앞에서부터 찾는 것이 아니라 가운데를 펼쳐서 찾으려는 이름이 앞부분에 있는지 뒷부분에 있는지 확인한다. 그리고 해당 부분에서 다시 가운데를 펼치는 과정을 반복한다. 매번 찾아야 할 범위가 절반씩 줄어든다.</li>
<li>데이터가 100만 개에서 200만 개로 2배 늘어나도, 필요한 연산은 단 1회만 추가된다. 이처럼 데이터가 폭발적으로 증가해도 연산 횟수는 매우 더디게 증가한다.</li>
</ul>
<p><strong>데이터 1백만 개일 때의 성능 차이</strong>
데이터의 크기(n)가 1,000,000개라고 가정했을 때, 최악의 경우 필요한 연산 횟수는 다음과 같다.</p>
<ul>
<li>O(n) - 약 1,000,000번의 연산이 필요하다.</li>
<li>O(log n) - 단 약 20번의 연산이면 충분하다. (2^20 ≈ 1,048,576)</li>
</ul>
<p>1백만 번과 20번. 실행 횟수가 압도적으로 많이 차이난다. 효율적인 알고리즘의 중요성을 명확히 알 수 있다.</p>
<hr>
<h3 id="결론">결론</h3>
<p>단순히 코드를 &#39;작동&#39;하게 만드는 것을 넘어, 그 코드가 &#39;어떻게&#39; 그리고 &#39;얼마나 효율적으로&#39; 작동하는지 이해하는 것은 개발자의 중요한 역량이다. 자료구조의 내부 동작 원리를 파악하고 시간 복잡도를 분석하는 습관은, 결국 더 빠르고 안정적인 소프트웨어를 만드는 단단한 기초가 될 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 좋은 코드란 무엇일까? (SRP/OCP, map/flatMap)]]></title>
            <link>https://velog.io/@d_ham8_8/JAVA-%EC%A2%8B%EC%9D%80-%EC%BD%94%EB%93%9C%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C-SRPOCP-mapflatMap</link>
            <guid>https://velog.io/@d_ham8_8/JAVA-%EC%A2%8B%EC%9D%80-%EC%BD%94%EB%93%9C%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C-SRPOCP-mapflatMap</guid>
            <pubDate>Sun, 12 Oct 2025 13:28:42 GMT</pubDate>
            <description><![CDATA[<p>&quot;일단 돌아가게만 만들자!&quot;고 생각하며 작성한 코드는, 몇 달 뒤 돌아보면 나조차도 이해하기 힘든 거대한 스파게티 덩어리가 되어 있었다. 새로운 기능을 하나 추가하려면 수많은 파일을 수정해야 했고, 예상치 못한 곳에서 버그가 발생했다.</p>
<h3 id="수정">&quot;수정&quot;</h3>
<p>이라는 단어가 두려워지는 순간이었다.
이러한 문제를 해결하기 위해 객체지향 설계 원칙과 효율적인 데이터 처리 방법을 학습했다. 그 과정에서 마주한 핵심 개념이 바로 <strong>SOLID 원칙</strong>과 <strong>Stream API</strong>였다.</p>
<h2 id="1-좋은-설계의-첫걸음---solid-원칙">1. 좋은 설계의 첫걸음 - SOLID 원칙</h2>
<p>복잡한 코드 속에서 길을 잃지 않게 도와주는 5개의 원칙, SOLID. 그중 가장 기본이 되는 두 가지를 먼저 정리해 보았다.</p>
<h3 id="a-단일-책임-원칙-srp-single-responsibility-principle---하나의-책임만">A. 단일 책임 원칙 (SRP: Single Responsibility Principle) - 하나의 책임만!</h3>
<p><strong>SRP</strong>는 하나의 클래스는 단 하나의 책임만 가져야 한다는 원칙이다. 만약 <code>Employee</code> 클래스가 직원의 정보를 관리하면서, 동시에 데이터베이스에 저장하고, 급여를 계산하는 책임까지 모두 가지고 있다면 어떨까?</p>
<pre><code class="language-java">// SRP 위반 예시
public class Employee {
    private String name;
    private long salary;

    // 책임 1: 정보 관리
    public String getName() { /* ... */ }
    public long getSalary() { /* ... */ }

    // 책임 2: 데이터베이스 처리
    public void saveToDatabase() { /* ... */ }

    // 책임 3: 급여 계산
    public long calculatePay() { /* ... */ }
}</code></pre>
<p>이 경우, 급여 계산 방식이 바뀌거나 데이터베이스 종류가 바뀌어도 <code>Employee</code> 클래스는 계속 수정되어야 했다. 너무 많은 책임을 가지고 있었기 때문이다.</p>
<p><strong>언제 적용할까?</strong></p>
<ul>
<li>클래스의 역할이 비대해지고 복잡해질 때</li>
<li>어떤 변경이 필요할 때, 수정해야 할 이유가 하나 이상으로 보일 때</li>
<li>코드의 재사용성과 테스트 용이성을 높이고 싶을 때</li>
</ul>
<p>책임을 분리하면 코드가 명확해지고, 변경의 영향 범위가 줄어든다.</p>
<h3 id="b-개방-폐쇄-원칙-ocp-open-closed-principle---확장은-쉽게-변경은-어렵게">B. 개방-폐쇄 원칙 (OCP: Open-Closed Principle) - 확장은 쉽게, 변경은 어렵게!</h3>
<p><strong>OCP</strong>는 소프트웨어의 구성요소(클래스, 모듈 등)는 <strong>확장에는 열려</strong> 있어야 하고, <strong>변경에는 닫혀</strong> 있어야 한다는 원칙이다. 새로운 기능이 추가되더라도 기존 코드는 수정되지 않아야 한다는 의미다.</p>
<p>예를 들어, 결제 유형에 따라 다른 로직을 처리해야 할 때 <code>if-else</code>로 분기하는 코드가 있었다.</p>
<pre><code class="language-java">// OCP 위반 예시
public class PaymentProcessor {
    public void process(Payment payment) {
        if (payment.getType().equals(&quot;CREDIT_CARD&quot;)) {
            // 신용카드 결제 로직
        } else if (payment.getType().equals(&quot;BANK_TRANSFER&quot;)) {
            // 계좌이체 결제 로직
        }
        // 새로운 결제 수단이 추가될 때마다 이 코드를 &#39;변경&#39;해야 한다!
    }
}</code></pre>
<p>이 코드는 새로운 결제 수단이 추가될 때마다 <code>PaymentProcessor</code> 클래스를 직접 수정해야 했다. 변경에 닫혀있지 않은 구조였다.</p>
<p><strong>언제 적용할까?</strong></p>
<ul>
<li>새로운 기능이나 정책이 자주 추가될 것으로 예상될 때</li>
<li>핵심 로직의 변경 없이 기능을 유연하게 확장하고 싶을 때</li>
<li>다형성을 활용하여 코드의 유연성을 높이고자 할 때</li>
</ul>
<p>OCP를 지키면 기존 코드를 건드리지 않고도 새로운 기능을 안전하게 추가할 수 있다.</p>
<h2 id="2-데이터를-자유자재로---map-vs-flatmap">2. 데이터를 자유자재로 - map vs flatMap</h2>
<p>복잡한 데이터를 다룰 때 <code>for</code>문과 <code>if</code>문을 중첩해서 사용하면 코드의 가독성이 떨어지기 쉬웠다. Java 8의 Stream API, 특히 <code>map</code>과 <code>flatMap</code>은 데이터를 우아하게 처리하는 강력한 도구였다.</p>
<h3 id="a-map---11로-변환하기">A. map - 1:1로 변환하기</h3>
<p><strong>map</strong>은 스트림의 각 요소를 받아서 다른 요소로 변환하는, 가장 직관적인 연산이다. 입력 요소 하나당 출력 요소 하나가 나오는 1:1 매핑이다.</p>
<pre><code class="language-java">List&lt;String&gt; words = Arrays.asList(&quot;apple&quot;, &quot;banana&quot;, &quot;cherry&quot;);

// 각 단어를 대문자로 변환
List&lt;String&gt; upperCaseWords = words.stream()
                                   .map(String::toUpperCase)
                                   .collect(Collectors.toList());
// 결과: [&quot;APPLE&quot;, &quot;BANANA&quot;, &quot;CHERRY&quot;]</code></pre>
<p><strong>언제 사용할까?</strong></p>
<ul>
<li>스트림의 각 요소를 다른 값이나 객체로 일대일 매핑하고 싶을 때</li>
<li>특정 필드만 추출하거나, 데이터의 형태를 바꾸고 싶을 때</li>
</ul>
<h3 id="b-flatmap---포장지를-벗겨-하나로-합치기">B. flatMap - 포장지를 벗겨 하나로 합치기</h3>
<p><strong>flatMap</strong>은 각 요소를 변환하여 <strong>새로운 스트림</strong>을 만들고, 이 모든 스트림들을 <strong>하나의 평평한 스트림</strong>으로 이어붙이는 연산이다. 중첩된 구조를 단일 계층으로 평탄화할 때 유용하다.</p>
<pre><code class="language-java">// 문장 배열을 단어 목록으로 만들기
String[] sentences = {&quot;hello world&quot;, &quot;java stream&quot;};

// map을 사용하면 List&lt;String[]&gt; 형태의 중첩 구조가 나온다.
List&lt;String[]&gt; wordsArray = Arrays.stream(sentences)
                                  .map(s -&gt; s.split(&quot; &quot;))
                                  .collect(Collectors.toList());
// 결과: [[&quot;hello&quot;, &quot;world&quot;], [&quot;java&quot;, &quot;stream&quot;]]

// flatMap을 사용하면 모든 단어가 포함된 단일 리스트가 나온다.
List&lt;String&gt; uniqueWords = Arrays.stream(sentences)
                                   .flatMap(s -&gt; Arrays.stream(s.split(&quot; &quot;)))
                                   .collect(Collectors.toList());
// 결과: [&quot;hello&quot;, &quot;world&quot;, &quot;java&quot;, &quot;stream&quot;]</code></pre>
<p><strong>언제 사용할까?</strong></p>
<ul>
<li>하나의 입력 요소에서 여러 개의 출력 요소가 나올 수 있을 때</li>
<li>중첩된 리스트나 배열 구조를 단일 리스트로 평탄화하고 싶을 때</li>
</ul>
<h2 id="정리하며">정리하며</h2>
<p>처음부터 완벽한 설계를 하기는 어렵다. 하지만 코드를 작성할 때 이런 원칙들을 떠올리는 것만으로도 코드의 질은 달라질 수 있다.</p>
<ol>
<li><strong>SRP</strong>: &quot;이 클래스가 너무 많은 일을 하고 있진 않나?&quot;</li>
<li><strong>OCP</strong>: &quot;기능을 추가할 때 기존 코드를 바꾸지 않을 방법은 없을까?&quot;</li>
<li><strong>map/flatMap</strong>: &quot;이 <code>for</code>문을 더 간결하게 바꿀 순 없을까?&quot;</li>
</ol>
<p>처음에는 복잡해 보이지만, 각 개념의 목적을 이해하고 상황에 맞게 사용하다 보면 자연스럽게 익숙해진다. 
새로운 배열의 키보드를 익힐 때처럼, 몸에 익을 때까지 계속 적용해가며 체화할 것이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[git] 헷갈리는 명령어 (rebase/merge, fetch/pull)]]></title>
            <link>https://velog.io/@d_ham8_8/git-%ED%97%B7%EA%B0%88%EB%A6%AC%EB%8A%94-%EB%AA%85%EB%A0%B9%EC%96%B4-rebasemerge-fetchpull</link>
            <guid>https://velog.io/@d_ham8_8/git-%ED%97%B7%EA%B0%88%EB%A6%AC%EB%8A%94-%EB%AA%85%EB%A0%B9%EC%96%B4-rebasemerge-fetchpull</guid>
            <pubDate>Sat, 27 Sep 2025 11:49:39 GMT</pubDate>
            <description><![CDATA[<p>협업을 시작하고 나서야 깨달았다. </p>
<blockquote>
<p>git은 단순한 &#39;코드 백업 도구&#39;가 아니구나! </p>
</blockquote>
<p>여러 명이 동시에 같은 프로젝트를 수정하다 보면, 내가 알던 단순한 명령어로는 해결할 수 없는 복잡한 상황들이 계속 발생한다. 
특히 다른 팀원이 먼저 push한 파일을 나 또한 push하려고 하면 </p>
<h3 id="rejected">&quot;rejected&quot;</h3>
<p>라는 무서운 메시지가 뜨고, 원격 저장소의 변경사항을 어떻게 처리해야 할지 막막해진다. 
이때 필요한 것이 바로 <strong>merge, rebase, fetch, pull</strong> 같은 명령어들이다.</p>
<h2 id="1-브랜치-통합의-두-가지-방법---merge-vs-rebase">1. 브랜치 통합의 두 가지 방법 - merge vs rebase</h2>
<p>팀 프로젝트에서 각자의 브랜치에서 작업을 마치고 main 브랜치에 합치려고 할 때, 두 가지 선택지가 있다.</p>
<h3 id="a-git-merge---두-히스토리를-그대로-보존하기">A. git merge - 두 히스토리를 그대로 보존하기</h3>
<p><strong>merge</strong>는 두 브랜치의 작업 내용을 하나로 합치면서, 각 브랜치의 커밋 히스토리를 모두 그대로 유지한다. 
마치 두 개의 강물이 합쳐져서 하나의 큰 강이 되는 것과 같다.</p>
<pre><code class="language-bash">git checkout main
git merge feature-branch</code></pre>
<p>이렇게 하면 새로운 merge commit이 생성되면서 두 브랜치의 작업이 하나로 합쳐진다. 커밋 히스토리를 보면 브랜치가 언제 갈라졌고 언제 합쳐졌는지 명확하게 볼 수 있다.</p>
<p><strong>언제 사용할까?</strong></p>
<ul>
<li>팀 프로젝트에서 feature 브랜치를 main에 통합할 때</li>
<li>브랜치 작업의 맥락과 히스토리를 보존하고 싶을 때</li>
<li>이미 공개된(push된) 브랜치를 다룰 때</li>
</ul>
<p>히스토리가 모두 남아있고, 안전하다. 대신 커밋 히스토리가 복잡하다!</p>
<h3 id="b-git-rebase---깔끔한-일직선-히스토리-만들기">B. Git rebase - 깔끔한 일직선 히스토리 만들기</h3>
<p><strong>rebase</strong>는 한 브랜치의 커밋들을 다른 브랜치의 끝에 차례대로 재배치한다. 마치 퍼즐 조각을 다시 정렬하는 것처럼, 커밋들이 시간순으로 일직선상에 배치된다.</p>
<pre><code class="language-bash">git checkout feature-branch
git rebase main</code></pre>
<p>이렇게 하면 feature-branch의 커밋들이 main의 최신 커밋 뒤에 순서대로 붙게 된다. 결과적으로 히스토리가 매우 깔끔하고 직관적으로 보인다.</p>
<p><strong>언제 사용할까?</strong></p>
<ul>
<li>로컬에서 작업 중인 브랜치를 최신 main과 동기화할 때</li>
<li>커밋 히스토리를 깔끔하게 정리하고 싶을 때</li>
<li>아직 공개하지 않은(push하지 않은) 개인 브랜치에서</li>
</ul>
<p><strong>주의사항!!!</strong> 
이미 push한 브랜치에서 rebase를 사용하면 다른 팀원들에게 문제가 될 수 있다....</p>
<p>-&gt; 팀원분이 프로젝트에선 사용하지 않는게 좋다는 의견을 주셨다. 
로컬 작업 브랜치에서만 사용 추천!</p>
<h2 id="2-원격-저장소-동기화---fetch-vs-pull">2. 원격 저장소 동기화 - fetch vs pull</h2>
<p>push가 rejected되는 가장 대표적인 원인은, <strong>내가 push하려 한 파일을 다른 사람이 이미 수정해 올렸을 때</strong>이다. 이를 피하려면 원격 저장소의 파일을 적절하게 가져와 반영하는데 필요한데, 이때 그 변경사항을 어떻게 가져올지 결정해야 한다.</p>
<h3 id="a-git-fetch---일단-가져와서-확인부터">A. git fetch - 일단 가져와서 확인부터</h3>
<p><strong>fetch</strong>는 원격 저장소의 최신 정보를 가져오되, 내 로컬 브랜치에는 바로 적용하지 않는 명령어다. </p>
<pre><code class="language-bash">git fetch origin
git log origin/main  # 원격의 변경사항 확인
git diff main origin/main  # 차이점 비교</code></pre>
<p>이렇게 하면 원격에서 무엇이 바뀌었는지 미리 확인한 후, 필요하다면 merge나 rebase로 직접 통합할 수 있다.</p>
<p><strong>언제 사용할까?</strong></p>
<ul>
<li>원격의 변경사항을 먼저 검토하고 싶을 때</li>
<li>충돌이 예상되어 신중하게 접근하고 싶을 때</li>
<li>여러 브랜치의 상태를 확인하고 싶을 때</li>
</ul>
<h3 id="b-git-pull---가져와서-바로-합치기">B. git pull - 가져와서 바로 합치기</h3>
<p><strong>pull</strong>은 fetch와 merge(또는 rebase)를 한 번에 수행한다. 원격의 변경사항을 가져와서 현재 브랜치에 !!바로!! 적용한다.</p>
<pre><code class="language-bash">git pull origin main  # fetch + merge
git pull --rebase origin main  # fetch + rebase</code></pre>
<p>빠르고 간편하지만, 예상치 못한 충돌이나 문제가 발생할 수 있다.</p>
<p><strong>언제 사용할까?</strong></p>
<ul>
<li>원격의 변경사항을 즉시 반영해야 할 때</li>
<li>팀원들의 작업을 신뢰할 수 있고 충돌 가능성이 낮을 때</li>
<li>단순한 업데이트가 필요할 때</li>
</ul>
<h2 id="실전에서는-이렇게">실전에서는 이렇게</h2>
<p>처음 협업을 시작할 때는 이런 흐름으로 작업하는 것을 추천한다.</p>
<ol>
<li><strong>작업 시작 전</strong>: <code>git fetch</code>로 원격 상태 확인</li>
<li><strong>개인 브랜치 작업 중</strong>: 주기적으로 <code>git rebase main</code>으로 최신 상태 유지</li>
<li><strong>작업 완료 후</strong>: <code>git merge</code>로 안전하게 main에 통합</li>
</ol>
<p>혹은, 팀 내에서 합의 후 한가지 방법을 정해서 사용하는게 좋다. </p>
<p>git을 잘 활용하면 혼자 작업할 때보다 오히려 더 체계적이고 안전하게 코드를 관리할 수 있다. 처음에는 복잡해 보이지만, 각 명령어의 목적을 이해하고 상황에 맞게 사용하다 보면 자연스럽게 익숙해진다. 힘내자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NN] NN으로 XOR 문제 풀기]]></title>
            <link>https://velog.io/@d_ham8_8/NN-NN%EC%9C%BC%EB%A1%9C-XOR-%EB%AC%B8%EC%A0%9C-%ED%92%80%EA%B8%B0</link>
            <guid>https://velog.io/@d_ham8_8/NN-NN%EC%9C%BC%EB%A1%9C-XOR-%EB%AC%B8%EC%A0%9C-%ED%92%80%EA%B8%B0</guid>
            <pubDate>Fri, 22 Nov 2024 08:32:21 GMT</pubDate>
            <description><![CDATA[<h1 id="모델-구조">모델 구조</h1>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/a7a24e4c-6370-4c69-90de-359d65f9da3f/image.png" alt="">
입력층은 x1, x2, b로 이루어져 있다.
은닉층은 2~5개의 유닛과 b로 이루어져 있다.
출력층은 한 개의 유닛으로 이루어져 있다.</p>
<p>활성 함수는 시그모이드 함수,
손실 함수는 MSE를 사용한다.</p>
<blockquote>
<h2 id="class-network">class Network</h2>
<p>은닉층의 유닛 개수와 학습률을 조정하여 모델을 만드는 클래스
------------------------------------------
def activate(x, d=False)
x에 대한 시그모이드 함수 계산 (d가 True인 경우 역전파의 값을 계산함)
def forward_pass(x)
입력값에 대해 순전파를 한 번 하고 입력층, 은닉층, 출력층의 값을 반환함
def fit(X, T, iter=5000)
입력값 X와 타겟 값T에 대해 iter 횟수만큼 순전파와 역전파를 반복하여 학습을 진행함
def test(X, T)
X와 T에 대한 모델의 예측값을 출력함 </p>
</blockquote>
<h1 id="반복-횟수에-따른-학습-비교">반복 횟수에 따른 학습 비교</h1>
<p>은닉층 유닛 2개, 학습률 0.1 고정.
<img src="https://velog.velcdn.com/images/d_ham8_8/post/97a16cc5-6f03-4505-b7d0-36fc755197d4/image.png" alt="">
<img src="https://velog.velcdn.com/images/d_ham8_8/post/b531c157-3d1e-4575-947f-215ed32d728e/image.png" alt="">
반복 횟수가 많아질 수록 충분한 학습이 이루어짐을 알 수 있었다.</p>
<h1 id="학습률에-따른-학습-비교">학습률에 따른 학습 비교</h1>
<ul>
<li>은닉층 유닛 2개, 반복 10000회 고정
<img src="https://velog.velcdn.com/images/d_ham8_8/post/6fa854ed-24e7-40e1-90d6-7724f936c301/image.png" alt="">
<img src="https://velog.velcdn.com/images/d_ham8_8/post/cef5c84e-cf89-4737-bb3a-47b695f96fe4/image.png" alt="">
각 모델의 예측값과 loss값을 바탕으로 학습률이 적절히 설정되어야 학습이 제대로 이루어짐을 알 수 있었다. 특히 학습률 50의 경우 값이 수렴하지 못했다.</li>
</ul>
<hr>
<ul>
<li>은닉층 유닛 5개, 반복 10000회 고정
<img src="https://velog.velcdn.com/images/d_ham8_8/post/128bf1fe-edfb-4147-92fb-d4e20a7dfd07/image.png" alt="">
<img src="https://velog.velcdn.com/images/d_ham8_8/post/7a74246f-18e2-4ea3-9d90-3021b621302a/image.png" alt="">
은닉층 유닛이 5개인 모델의 경우 학습률이 0.5인 경우 0.2일 때보다 빠르게 수렴했으나 학습률이 너무 큰 경우 발산하였다.</li>
</ul>
<h1 id="은닉층-유닛-수에-따른-학습-비교">은닉층 유닛 수에 따른 학습 비교</h1>
<p>*학습률 0.2, 반복 10000회 고정
<img src="https://velog.velcdn.com/images/d_ham8_8/post/4a29c86c-e227-405c-8ef1-20b68c00b0a0/image.png" alt="">
<img src="https://velog.velcdn.com/images/d_ham8_8/post/becb53c0-bec4-4b66-a171-e070a1576cfa/image.png" alt="">
유닛 개수가 많아질 수록 loss값이 줄어드는 반복 횟수의 임계값이 적어짐을 알 수 있었다. 따라서 유닛 개수가 많다면 학습이 더욱 빠르게 이루어진다고 추측할 수 있다.</p>
<hr>
<h1 id="결론">결론</h1>
<blockquote>
<p>신경망 모델에서 적절한 하이퍼파라미터를 설정하는 것은 중요하다.</p>
</blockquote>
<ul>
<li>반복 횟수가 충분하지 못한 경우, 학습이 제대로 이루어지지 않을 수 있다.</li>
<li>학습률이 너무 높은 경우, 모델이 수렴하지 못하고 발산할 수 있다.</li>
<li>은닉층의 유닛의 개수를 늘리면 모델의 성능 향상(더 빠른 수렴)을 기대할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[파이썬] 스택, 큐, 힙]]></title>
            <link>https://velog.io/@d_ham8_8/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%8A%A4%ED%83%9D-%ED%81%90-%ED%9E%99</link>
            <guid>https://velog.io/@d_ham8_8/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%8A%A4%ED%83%9D-%ED%81%90-%ED%9E%99</guid>
            <pubDate>Fri, 22 Nov 2024 08:02:34 GMT</pubDate>
            <description><![CDATA[<h2 id="스택">스택</h2>
<p>후입선출, LIFO(Last in First Out)형식의 자료 구조. </p>
<p>파이썬에서는 리스트로 사용해도 될 것 같다.</p>
<p>넣을 때는 <code>stack.append(num)</code>을, 뺄 때는 <code>.pop()</code>을 사용한다.</p>
<h2 id="큐">큐</h2>
<p>선입선출, FIFO(First in First Out)형식의 자료구조.</p>
<p>파이썬에선 collections 모듈의 deque를 이용한다.</p>
<p>생성 <code>queue = deque()</code></p>
<p>요소 넣기 <code>queue.append(요소)</code></p>
<p>요소 빼기 <code>(queue.popleft())</code></p>
<h2 id="힙">힙</h2>
<p>최소힙
최소힙은 완전이진트리로 구현된 자료구조이며, 부모 노드 값 &gt; 자식값으로 저장한다. 그렇게 하면 root(최상위)노드는 입력된 값들 중 가장 작은 값이 저장된다.</p>
<h3 id="최대힙">최대힙</h3>
<p>최대힙은 완전 이진트리로 구현된 자료구조이며, 부모 노드 값 &lt; 자식값으로 저장한다. 그러면 root노드는 입력된 값들 중 가장 큰 값이 저장되게 된다.</p>
<p>파이썬에서의 힙의 사용은 heapq 모듈을 사용한다.</p>
<p>데이터 추가 <code>heapq.heappush(힙, 넣을 배열)</code></p>
<p>데이터 삭제 <code>heapq.heappop(힙)</code> - 루트 노드를 삭제한다</p>
<p>리스트를 힙으로 변환 heapq.heapfy(list) - $$O(N)$$</p>
<p>heapq는 최소힙만 지원하므로 최대힙으로 구현하려면 요소에 -1을 곱한 값을 넣으면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[파이썬] 정렬, 그리디, 이진탐색]]></title>
            <link>https://velog.io/@d_ham8_8/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%A0%95%EB%A0%AC-%EA%B7%B8%EB%A6%AC%EB%94%94-%EC%9D%B4%EC%A7%84%ED%83%90%EC%83%89</link>
            <guid>https://velog.io/@d_ham8_8/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%A0%95%EB%A0%AC-%EA%B7%B8%EB%A6%AC%EB%94%94-%EC%9D%B4%EC%A7%84%ED%83%90%EC%83%89</guid>
            <pubDate>Fri, 22 Nov 2024 07:55:03 GMT</pubDate>
            <description><![CDATA[<h2 id="정렬">정렬</h2>
<p>파이썬에서 정렬은 sort함수를 사용한다.</p>
<blockquote>
<p> arr.sort() vs sorted(arr)</p>
</blockquote>
<p>sort()는 배열의 내부함수로, arr 리스트 자체를 정렬하며 None을 return한다.</p>
<p>sorted()는 배열 자체는 바꾸지 않고 정렬된 배열을 반환하며, 순회 가능한(iterable) 객체는 모두 가능하다.</p>
<blockquote>
<p>두 개 모두 key와 reverse라는 인자를 갖는다.</p>
</blockquote>
<p>key는 어느 것을 기준으로 정렬할지, reverse는 오름차순/내림차순 중 어느 것으로 정렬할지를 정한다. reverse인자의 기본값은 False(오름차순으로 정렬)이고, 인자 값으로 True를 주어 내림차순으로 정렬시킬 수 있다. </p>
<p>key값을 잘 사용하면 원하는 값을 기준으로 정렬을 시킬 수 있다. </p>
<p>lambda함수를 응용하여 사용하는데, 예를 들어 arr = [[1, 4], [5, 7], [6, 6], [2, 1], [7, 5], [3, 2], [4, 3]] 일 때,</p>
<p>arr을 [x, y]의 첫번째 값을 기준으로 오름차순으로 정렬시키고 싶다면</p>
<blockquote>
<p>key = lambda v : v[0]</p>
</blockquote>
<p>두번째 값을 기준으로 내림차순으로 정렬하고 싶다면</p>
<blockquote>
<p>key = lambda v : -v[1]</p>
</blockquote>
<p>과 같이 작성하면 된다.</p>
<p>더해서, x값으로 내림차순 정렬하되 값이 같은 경우 y의 오름차순으로 정리하고 싶다면,</p>
<blockquote>
<p>key = lambda v : (-v[0], v[1]) 을 쓰면 된다.</p>
</blockquote>
<h2 id="그리디">그리디</h2>
<p>그리디는 당장 눈 앞에 보이는 최고의 상황만 고르는 방식이다. 탐욕법이라고도 한다.</p>
<p>그리디를 사용하면 훨씬 빠르게 해결 할 수 있는 상황이 종종 있지만, 그리디를 써도 항상 문제 조건에 맞게 풀이가 가능하다는 증명을 하기는 쉽지 않다. 따라서 그리디를 사용해도 문제를 풀 수 없는 반례를 찾아내는 게 더 좋다. </p>
<h2 id="이진탐색">이진탐색</h2>
<p>투포인터이다. r과 l이라는 두 포인터 변수를 두고, $$l + r // 2 인 mid$$값이 내가 찾는 값과 비교했을때 큰지, 작은지, 일치하는지에 따라서 범위를 절반씩 줄여나간다. 따라서 시간 복잡도는 
$$O(\log n)$$이다.</p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/f13c1fd0-c2ff-4706-b2fe-a20e6e695226/image.png" alt=""></p>
<p>위와 같은 배열에서 18이라는 값을 target으로 이진탐색하는 과정을 살펴보자.</p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/7c5a27db-e074-45c3-91b7-d6b3770c44f7/image.png" alt=""></p>
<p>우선 배열의 가장 첫번째 인덱스를 l, 가장 마지막 인덱스를 r로 설정한다.</p>
<p>l과 r의 값을 더해 2로 나눈 몫을 mid라고 정한다.</p>
<p>arr[mid] == target라면 탐색이 성공했으므로 종료된다.</p>
<p>arr[mid]의 값이 target보다 크다면 r을 mid의 왼쪽으로, 작다면 l을 오른쪽으로 옮긴다.</p>
<p>여기서는 18이 arr[mid]보다 작아 r을 mid - 1로 옮긴다.</p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/f36d3149-3bda-4fef-b270-e0a7099b1c4d/image.png" alt=""></p>
<p>이후 위의 과정을 반복한다. 지금은 arr[mid]의 값이 18보다 작으므로 l을 mid + 1로 옮긴다.</p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/9390471c-6d74-4620-abed-9fcb400d5783/image.png" alt=""></p>
<p>이제 arr[mid] == target 이므로 탐색을 종료한다.
<img src="https://velog.velcdn.com/images/d_ham8_8/post/09ac766e-c07b-49cb-9583-3fbd82b0feab/image.png" alt=""></p>
<p>코드를 작성하면 다음과 같이 짜게 된다.</p>
<pre><code>def solution(nums, target): # 탐색 값의 인덱스를 반환
    answer = -1 # 탐색 실패시 -1을 반환
    left, right = 0, len(nums)
    while left &lt;= right:
        mid = (left + right) // 2
        if target &lt; nums[mid]:
            right = mid - 1
        elif nums[mid] &lt; target:
            left = mid + 1
        else:
            answer = mid
            break
    return answer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[객체의 행동 방식]]></title>
            <link>https://velog.io/@d_ham8_8/%EA%B0%9D%EC%B2%B4%EC%9D%98-%ED%96%89%EB%8F%99-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@d_ham8_8/%EA%B0%9D%EC%B2%B4%EC%9D%98-%ED%96%89%EB%8F%99-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Thu, 24 Oct 2024 13:45:18 GMT</pubDate>
            <description><![CDATA[<h1 id="아는-것과-하는-것">아는 것과 하는 것</h1>
<p>클래스는 객체에 대한 설계도이다. 클래스는 아는 것(인스턴스 변수)와 하는 것(메서드)를 가지고 있다. </p>
<p>메서드에는 매개변수를 넘길 수 있다. 호출하는 쪽에서 넘기는 것은 인자. 메서드에서 받는 것은 매개변수이다. 메서드에서 매개변수를 받도록 선언했다면 그 메서드를 호출할 때 반드시 무언가를 전달해야한다. </p>
<p>메서드에서 특정 값을 리턴할 수도 있다. 사실은 자바는 리턴값의 사용 여부에 전혀 신경을 쓰지 않지만, 리턴 해주기로 했다면 반드시 뭔가 돌려주는 것이 좋다 …</p>
<p>메서드에 두개 이상의 인자를 전달할 수도 있다. 하지만 반환값은 리스트나 배열 등을 활용하지 않는 이상 하나만 돌려줄 수 있다.</p>
<p>자바가 값을 주고받을 때, 원본이 아닌 복사본으로 전달된다. 값으로 전달한다는 것은 복사본을 전달하는 것이다. 중요해서 두번씀</p>
<p>x라는 변수에 7을 담아서 매개변수 z에 대한 인자로 넘긴다면 넘어가는 것은 7의 복사본이라서 함수 내에서는 원본 변수인 x를 조작할 수 없다. (비교대조 : C++의 레퍼런스 변수)</p>
<p>함수의 대표적인 활용으로는 게터(접근자)와 세터(변경자)가 있다. </p>
<h1 id="캡슐화">캡슐화</h1>
<p>캡슈화는 인스턴스 변수는 private로, 게터와 세터는 public으로 열어서 점 연산자(.)로 원본 변수에 접근할 수 없도록 한다. 캡슐화를 하지 않으면 어떤 개발자가 코드를 사용하면서 접근하면 안되는 레퍼런스 변수에 점 연산자로 접근해서 맞지 않는 값으로 바꿀 수 있다 … 인스턴스 변수가 그냥 노출된 경우에는 이런 경우에 아무 것도 할 수 없지만 변수를 변경할때 반드시 세터를 거치게 하면 값의 유효성을 검증한 다음에 넣는 등 조치를 취할 수 있다. (키에 음수가 안들어가도록 한다던지)</p>
<h2 id="인스턴스-변수">인스턴스 변수</h2>
<p>인스턴스 변수를 선언할 때에는 이름과 타입을 지정해야하고, 동시에 변수를 초기화할 수 있다. 만약 레퍼런스변수가 초기화되지  않는다면, 자바가 기본값을 넣어준다.</p>
<table>
<thead>
<tr>
<th>타입</th>
<th>기본값</th>
</tr>
</thead>
<tbody><tr>
<td>정수</td>
<td>0</td>
</tr>
<tr>
<td>부동소수점 수</td>
<td>0.0</td>
</tr>
<tr>
<td>불리언</td>
<td>false</td>
</tr>
<tr>
<td>레퍼런스</td>
<td>null</td>
</tr>
</tbody></table>
<h3 id="인스턴스-변수와-로컬-변수의-차이점">인스턴스 변수와 로컬 변수의 차이점</h3>
<ol>
<li>인스턴스 변수는 클래스 내에서 선언된다.</li>
<li>로컬 변수는 메서드 내에서 선언된다.</li>
<li>인스턴스 변수는 초기화하지 않아도 자바에서 기본값을 넣어주지만, 로컬 변수는 사용하기 전에 초기화해야한다.</li>
</ol>
<ul>
<li>메서드 매개변수는 메세드 인자 목록 내에서 선언되는 로컬 변수이다. 하지만 함수가 호출되려면 반드시 올바른 인자를 넘겨줘야 하므로 메서드 매개변수는 항상 초기화된다.</li>
</ul>
<h1 id="원시-변수와-레퍼런스-변수-비교">원시 변수와 레퍼런스 변수 비교</h1>
<h2 id="객체의-동치">객체의 동치</h2>
<h3 id="연산자">==연산자</h3>
<p>==연산자는 <strong>그 변수 값의 비트 패턴을 비교하는 연산자</strong>이므로 <strong>원시 타입 두 개</strong>를 비교하거나 레퍼런스가 같은 객체를 참조하고 있는지 (<strong>힙에 들어있는 똑같은 객체를 참조하는지</strong>) 확인할 때 쓴다.</p>
<p>서로 다른 두 객체가 같은지 확인할 때는 equals() 메서드를 사용한다. 객체의 동치(equality)의 기준은 객체의 타입에 따라 달라질 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 그래서 다형성이 대체 뭐예요?]]></title>
            <link>https://velog.io/@d_ham8_8/JAVA-%EA%B7%B8%EB%9E%98%EC%84%9C-%EB%8B%A4%ED%98%95%EC%84%B1%EC%9D%B4-%EB%8C%80%EC%B2%B4-%EB%AD%90%EC%98%88%EC%9A%94</link>
            <guid>https://velog.io/@d_ham8_8/JAVA-%EA%B7%B8%EB%9E%98%EC%84%9C-%EB%8B%A4%ED%98%95%EC%84%B1%EC%9D%B4-%EB%8C%80%EC%B2%B4-%EB%AD%90%EC%98%88%EC%9A%94</guid>
            <pubDate>Tue, 24 Sep 2024 23:59:35 GMT</pubDate>
            <description><![CDATA[<h1 id="그래서-다형성이-대체-뭔가요">그래서 다형성이 대체 뭔가요?</h1>
<p>헤드 퍼스트 자바를 읽으면서 계속 마주치는 단어, &#39;다형성&#39;. 무슨 느낌인지는 알겠지만 정확히 뭔지, 왜 필요한지 명확하게 설명하기는 어려웠다. 이번 기회에 다형성의 개념부터 구현 방법까지 정리해보았다.</p>
<h2 id="다형성이란-무엇인가">다형성이란 무엇인가</h2>
<p><strong>다형성(Polymorphism)</strong>은 &#39;여러 형태&#39;를 의미한다. 객체지향 프로그래밍에서는 하나의 객체가 여러 타입으로 사용될 수 있는 특성을 말한다. </p>
<p>예를 들어, <code>Dog</code> 클래스가 <code>Animal</code> 클래스를 상속받는다면, <code>Dog</code> 객체는 <code>Dog</code> 타입으로도, <code>Animal</code> 타입으로도 사용할 수 있다.</p>
<pre><code class="language-java">Animal myAnimal = new Dog();  // Dog 객체를 Animal 타입으로 참조</code></pre>
<p>이때 실제 객체는 <code>Dog</code>이지만, <code>Animal</code> 타입의 참조변수로 접근할 수 있다. 이것이 바로 다형성이다.</p>
<h2 id="왜-다형성이-필요한가">왜 다형성이 필요한가?</h2>
<p>다형성이 없다면 어떻게 될까? 동물원 관리 시스템을 만든다고 생각해보자.</p>
<pre><code class="language-java">// 다형성을 사용하지 않을 때
public void feedAnimals(Dog dog, Cat cat, Lion lion) {
    dog.eat();
    cat.eat();
    lion.eat();
}</code></pre>
<p>새로운 동물이 추가될 때마다 메서드를 수정해야 한다. 하지만 다형성을 사용하면:</p>
<pre><code class="language-java">// 다형성을 사용할 때
public void feedAnimals(Animal[] animals) {
    for(Animal animal : animals) {
        animal.eat();  // 실제로는 각각의 구체적인 eat() 메서드가 호출됨
    }
}</code></pre>
<p>코드가 훨씬 유연해지고, 새로운 동물을 추가해도 기존 코드를 수정할 필요가 없다.</p>
<h2 id="추상-클래스로-다형성-구현하기">추상 클래스로 다형성 구현하기</h2>
<h3 id="추상-클래스는-왜-필요한가">추상 클래스는 왜 필요한가?</h3>
<p>때로는 클래스의 인스턴스를 직접 만들면 안 되는 경우가 있다. <code>Animal</code> 클래스를 생각해보자. &quot;동물&quot;이라는 추상적 개념은 존재하지만, 구체적으로 어떤 동물인지 모르는 상태에서는 의미가 없다.</p>
<pre><code class="language-java">public abstract class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    // 구체적인 메서드
    public void sleep() {
        System.out.println(name + &quot;이 잠을 잡니다.&quot;);
    }

    // 추상 메서드 - 반드시 하위 클래스에서 구현해야 함
    public abstract void eat();
    public abstract void makeSound();
}</code></pre>
<h3 id="추상-메서드의-역할">추상 메서드의 역할</h3>
<p>추상 메서드는 하위 클래스에서 반드시 구현해야 하는 &quot;계약&quot;과 같다. 이를 통해 모든 동물이 <code>eat()</code>과 <code>makeSound()</code> 메서드를 가지도록 강제할 수 있다.</p>
<pre><code class="language-java">public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    @Override
    public void eat() {
        System.out.println(name + &quot;이 사료를 먹습니다.&quot;);
    }

    @Override
    public void makeSound() {
        System.out.println(&quot;멍멍!&quot;);
    }
}

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void eat() {
        System.out.println(name + &quot;이 생선을 먹습니다.&quot;);
    }

    @Override
    public void makeSound() {
        System.out.println(&quot;야옹!&quot;);
    }
}</code></pre>
<h2 id="모든-클래스의-조상-object">모든 클래스의 조상, Object</h2>
<p>자바에서 모든 클래스는 <code>Object</code> 클래스를 상속받는다. 명시적으로 다른 클래스를 상속하지 않으면 자동으로 <code>Object</code>를 상속한다.</p>
<pre><code class="language-java">public class MyClass {  // 자동으로 extends Object
    // ...
}</code></pre>
<h3 id="object의-주요-메서드들">Object의 주요 메서드들</h3>
<ul>
<li><code>equals(Object obj)</code>: 객체 비교</li>
<li><code>hashCode()</code>: 해시테이블에서 사용하는 해시코드</li>
<li><code>toString()</code>: 객체의 문자열 표현</li>
<li><code>getClass()</code>: 객체의 실제 클래스 타입</li>
</ul>
<p>이 덕분에 모든 객체를 <code>Object</code> 타입으로 다룰 수 있다.</p>
<pre><code class="language-java">Object[] objects = {new Dog(&quot;멍이&quot;), new Cat(&quot;야옹이&quot;), &quot;Hello&quot;};
for(Object obj : objects) {
    System.out.println(obj.toString());  // 모든 객체가 toString() 메서드를 가짐
}</code></pre>
<h3 id="다운캐스팅과-instanceof">다운캐스팅과 instanceof</h3>
<p><code>Object</code> 타입으로 캐스팅된 객체를 원래 타입으로 사용하려면 다운캐스팅이 필요하다.</p>
<pre><code class="language-java">Animal myAnimal = new Dog(&quot;멍이&quot;);
if(myAnimal instanceof Dog) {
    Dog myDog = (Dog) myAnimal;  // 안전한 다운캐스팅
    // 이제 Dog의 고유 메서드 사용 가능
}</code></pre>
<h2 id="인터페이스로-다형성-확장하기">인터페이스로 다형성 확장하기</h2>
<p>자바는 다중 상속을 지원하지 않는다. 하지만 인터페이스를 통해 다중 구현이 가능하다.</p>
<pre><code class="language-java">public interface Flyable {
    void fly();
}

public interface Swimmable {
    void swim();
}

public class Duck extends Animal implements Flyable, Swimmable {
    public Duck(String name) {
        super(name);
    }

    @Override
    public void eat() {
        System.out.println(name + &quot;이 물풀을 먹습니다.&quot;);
    }

    @Override
    public void makeSound() {
        System.out.println(&quot;꽥꽥!&quot;);
    }

    @Override
    public void fly() {
        System.out.println(name + &quot;이 날아갑니다.&quot;);
    }

    @Override
    public void swim() {
        System.out.println(name + &quot;이 헤엄칩니다.&quot;);
    }
}</code></pre>
<p>이제 <code>Duck</code> 객체는 <code>Animal</code>, <code>Flyable</code>, <code>Swimmable</code> 타입으로 모두 사용할 수 있다.</p>
<pre><code class="language-java">Duck duck = new Duck(&quot;도널드&quot;);
Animal animal = duck;
Flyable flyer = duck;
Swimmable swimmer = duck;</code></pre>
<h2 id="언제-무엇을-사용해야-할까">언제 무엇을 사용해야 할까?</h2>
<h3 id="클래스-vs-추상-클래스-vs-인터페이스">클래스 vs 추상 클래스 vs 인터페이스</h3>
<p><strong>일반 클래스</strong>를 사용하는 경우:</p>
<ul>
<li>완전한 구현이 가능한 구체적인 개념</li>
<li>인스턴스를 직접 생성해야 하는 경우</li>
</ul>
<p><strong>추상 클래스</strong>를 사용하는 경우:</p>
<ul>
<li>하위 클래스들이 공통으로 사용할 구현 코드가 있는 경우</li>
<li>인스턴스 생성을 막고 싶은 경우</li>
<li>&quot;is-a&quot; 관계가 명확한 경우</li>
</ul>
<p><strong>인터페이스</strong>를 사용하는 경우:</p>
<ul>
<li>구현 코드 없이 행동만 정의하고 싶은 경우</li>
<li>다중 구현이 필요한 경우</li>
<li>&quot;can-do&quot; 관계를 표현하고 싶은 경우</li>
</ul>
<h2 id="마무리">마무리</h2>
<p>다형성은 객체지향 프로그래밍의 핵심 개념이다. 같은 타입으로 다양한 객체를 다룰 수 있게 해주어 코드의 유연성과 재사용성을 크게 향상시킨다. 추상 클래스와 인터페이스는 이런 다형성을 구현하는 강력한 도구들이며, 상황에 맞게 적절히 선택해서 사용하는 것이 중요하다.</p>
<p>다형성을 제대로 이해하고 활용하면, 변화에 유연하게 대응할 수 있는 견고한 코드를 작성할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[인텔리제이] Live Templates로 반복되는 코드 쉽게 작성하기]]></title>
            <link>https://velog.io/@d_ham8_8/%EC%9D%B8%ED%85%94%EB%A6%AC%EC%A0%9C%EC%9D%B4-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%9E%91%EC%84%B1-%ED%95%9C%EB%B0%A9%EC%97%90</link>
            <guid>https://velog.io/@d_ham8_8/%EC%9D%B8%ED%85%94%EB%A6%AC%EC%A0%9C%EC%9D%B4-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%9E%91%EC%84%B1-%ED%95%9C%EB%B0%A9%EC%97%90</guid>
            <pubDate>Tue, 03 Sep 2024 06:10:17 GMT</pubDate>
            <description><![CDATA[<p>테스트 메서드 적어야겠다!</p>
<pre><code class="language-java">@Test
void 테스트메서드1() {
    // given

    //when

    //then
}

@Test
void 테스트메서드2() {
    // given

    //when

    //then
}

...(타닥타닥)</code></pre>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/70fff53a-d978-44ce-aa60-e291c8b4257f/image.png" alt=""></p>
<p>귀찮음 타파를 위해 템플릿을 이용해서 한 번에 작성할 수 있게 만들어보자!</p>
<h1 id="intellij-live-templates">IntelliJ Live Templates</h1>
<p>인텔리제이에는 키워드를 통해 미리 만들어둔 템플릿을 쉽게 작성할 수 있는 기능이 있다.
<img src="https://velog.velcdn.com/images/d_ham8_8/post/85ff82e2-b133-4ee8-82cc-9d3abc953492/image.png" alt=""></p>
<p>자주 쓰는 키워드로는</p>
<blockquote>
<ul>
<li>sout : System.out.Println();</li>
</ul>
</blockquote>
<ul>
<li>soutv : System.out.Println(&quot;변수_이름 = &quot; + 변수);</li>
<li>soutm : System.out.Println(&quot;작성중인 메서드 이름(패키지 경로 포함)&quot;)</li>
<li>iter : 순회가능한 자료형이 있는 경우 for-each문으로 순회할 수 있게 만들어준다. </li>
<li>psvm : public static void main의 약자로 메인 메서드를 한방에 만들어줌!! </li>
</ul>
<p>가 있다.</p>
<p>이 템플릿들은 직접 작성할 수 있다!
그래서 이번에는 인프런 강의를 수강하며 자주 쓰게 되는 given-when-then 테스트 메서드를 &quot;gwt&quot;키워드로 등록해볼것이다.</p>
<h1 id="live-templates에-자주-쓰는-코드-등록하기">Live Templates에 자주 쓰는 코드 등록하기</h1>
<h2 id="어디서-할-수-있나요">어디서 할 수 있나요?</h2>
<blockquote>
<p>File - Settings - Editor - Live Templates
혹은 Settings에서 Live Templates 검색!</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/5487f664-ad04-4d8e-a570-d79f73082799/image.png" alt=""></p>
<h2 id="어떻게-하나요">어떻게 하나요?</h2>
<h3 id="1-새-템플릿-그룹-선택-혹은-만들기">1. 새 템플릿 그룹 선택 혹은 만들기</h3>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/23587f77-62ee-44ee-81f5-2e0750327b8a/image.png" alt="">
새로 작성하는 템플릿을 넣을 그룹을 선택한다. 
Java 코드로 작성할 거니까 Java 폴더에 넣어줘도 되는데 나는 관리하기 쉽게 직접 만든 템플릿은 Custom 폴더에 저장해서 관리하고 있다.</p>
<h3 id="2-새-템플릿-만들기">2. 새 템플릿 만들기</h3>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/ae34425d-93a7-4c23-a723-95621c81c6db/image.png" alt="">
지정한 폴더에 템플릿을 만들어준다.</p>
<h3 id="3-템플릿-작성하기">3. 템플릿 작성하기</h3>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/f85f81bc-4adf-4a96-953c-095f2d769ec8/image.png" alt="">
Abbrevition(약어)에 내가 사용할 키워드를 적고, Description에 설명을 적는다.
그리고 아래에 파란색 Change를 누르면 내가 어떤 Context에서 사용할지 선택할 수 있는데, 나는 Java에서 사용하기 위해 적어서 Java를 선택했다.</p>
<p>그리고 OK 누르면 끝!</p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/994209eb-98e8-4e6e-8e2d-ac7041949b30/image.png" alt=""></p>
<p>근데 별로 한 게 없어보인다.
딱히 코드 작성하는 수고를 던 것 같지도 않고
뭔가 한 것 같지도 않다.</p>
<h1 id="더-나아가기---변수-사용하기">더 나아가기 - 변수 사용하기</h1>
<h2 id="메서드-자체를-작성하는-템플릿-만들기">메서드 자체를 작성하는 템플릿 만들기</h2>
<p>이번 메서드 자체를 만들어주는 템플릿을 작성해 보자. 키워드는 soutm처럼 Method의 m을 붙여 gwtm을 사용한다.
이 템플릿은 아래와 같이 애노테이션부터 함수까지 모두 포함한다.</p>
<pre><code class="language-java">@Test
void 테스트메서드1() {
    // given

    //when

    //then
}</code></pre>
<p>이 템플릿을 호출(?)하고 나면, &#39;테스트메서드1&#39;부분에 텍스트 커서를 이동해 바로 메서드 이름을 작성할 수 있게 만들 것이다.</p>
<h3 id="작성하기-전에---다른-템플릿-살펴보기---soutv">작성하기 전에 - 다른 템플릿 살펴보기 ::  soutv</h3>
<p>근데 그걸 어떻게 하는 걸까?? 우선 지금까지 자주 사용한 템플릿을 살펴보면서 파악해 보자.</p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/1297ba49-2244-418d-b6fc-4aa802e96443/image.png" alt="">
soutv는 메서드에서 사용할 수 있는 변수를 이름과 값을 함께 출력하는 코드를 적는 템플릿이다.
$EXPR_COPY$에는 변수명이, $EXPR$에는 변수가 들어간다. (결론적으로 적히는 글자는 같다.)
Edit Variables를 살펴보면, </p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/6ca4e190-b384-4e08-a0ad-4af837e542f2/image.png" alt="">
이렇게 $ 사이에 감싸진 변수들이 정의되어 있고
<img src="https://velog.velcdn.com/images/d_ham8_8/post/6d90fde6-42d7-442d-8573-cd372c1c4ddf/image.png" alt="">
Expression을 눌러보면 엄청 다양한 표현식들이 있다. 이걸 이용해서 변숫값을 Context에 따라 불러와 주는 것 같다.</p>
<p>각각의 설명은 아래 링크에서 볼 수 있다.
<a href="https://www.jetbrains.com/help/idea/template-variables.html#predefined_functions">Live template variables | IntelliJ</a></p>
<h2 id="이제-한번-만들어보자">이제 한번 만들어보자</h2>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/35b9c42b-e09a-4c3d-993d-251a94e37e06/image.png" alt="">
이렇게 작성했다.
METHOD_NAME은 빈 채로 놔두고 싶어서 딱히 건드리지 않았다.
$END$는 템플릿이 작성된 뒤 커서의 위치이다.</p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/9446d733-2ea5-4c46-b45b-c933f5a4f361/image.png" alt=""></p>
<p>apply, ok를 누르고 나가면 정상적으로 사용이 가능한 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/e798e5ae-9700-4aaf-b139-726afb23ed09/image.png" alt="">
↑ gwtm을 쓰고 엔터를 누르면 나오는 모습
<img src="https://velog.velcdn.com/images/d_ham8_8/post/016ab22c-4e51-4f91-81b8-5ad1bdccf3e7/image.png" alt="">
↑ 메서드명을 적고 엔터를 누른 뒤의 모습</p>
<p>이제는 귀찮은 기본 테스트 메서드 작성을 쉽게 할 수 있게 됐다.
<strong>끝!!!!!!!!!!!!!!!!!!!!!!!!</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 모의고사]]></title>
            <link>https://velog.io/@d_ham8_8/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC</link>
            <guid>https://velog.io/@d_ham8_8/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC</guid>
            <pubDate>Sat, 13 Jul 2024 14:34:43 GMT</pubDate>
            <description><![CDATA[<p><code>문제 이름</code> 모의고사
<code>문제 출처</code> <a href="https://school.programmers.co.kr/learn/courses/30/lessons/42840">https://school.programmers.co.kr/learn/courses/30/lessons/42840</a></p>
<h3 id="문제">문제</h3>
<p>수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다.</p>
<p>1번 수포자가 찍는 방식: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...
2번 수포자가 찍는 방식: 2, 1, 2, 3, 2, 4, 2, 5, 2, 1, 2, 3, 2, 4, 2, 5, ...
3번 수포자가 찍는 방식: 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, ...</p>
<p>1번 문제부터 마지막 문제까지의 정답이 순서대로 들은 배열 answers가 주어졌을 때, 가장 많은 문제를 맞힌 사람이 누구인지 배열에 담아 return 하도록 solution 함수를 작성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<ul>
<li>시험은 최대 10,000 문제로 구성되어있습니다.</li>
<li>문제의 정답은 1, 2, 3, 4, 5중 하나입니다.</li>
<li>가장 높은 점수를 받은 사람이 여럿일 경우, return하는 값을 오름차순 정렬해주세요.</li>
</ul>
<blockquote>
<h3 id="내-풀이">내 풀이</h3>
</blockquote>
<pre><code class="language-java">import java.util.ArrayList;
import java.util.Arrays;

class Solution {
    public int[] solution(int[] answers) {
        ArrayList&lt;Integer&gt; result = new ArrayList&lt;&gt;();

        int[] first = new int[]{1, 2, 3, 4, 5};
        int[] second = new int[]{2, 1, 2, 3, 2, 4, 2, 5};
        int[] third = new int[]{3, 3, 1, 1, 2, 2, 4, 4, 5, 5};

        int[] scores = new int[]{0, 0, 0};

        for (int i = 0; i &lt; answers.length; i++) {
            if (first[i % first.length] == answers[i]) {
                scores[0] += 1;
            }
            if (second[i % second.length] == answers[i]) {
                scores[1] += 1;
            }
            if (third[i % third.length] == answers[i]) {
                scores[2] += 1;
            }
        }

        int maxScore = Arrays.stream(scores).max().getAsInt();

        for (int i = 0; i &lt; 3; i++) {
            if (scores[i] == maxScore) {
                result.add(i + 1);
            }
        }


        return result.stream().mapToInt(Integer::intValue).toArray();
    }
}</code></pre>
<h3 id="코드-설명">코드 설명</h3>
<p>각 수포자가 정답을 찍는 패턴을 배열로 저장해, 최고 점수를 구한다.
이후 점수 목록을 순회하며 최고 점수와 같다면 ArrayList에 저장한다.
마지막으로 ArrayList를 배열로 바꿔서 반환하였다.</p>
<p>문제에서는 동점자가 있다면 오름차순으로 정렬해 반환하라고 되어있는데, for문에서 1, 2, 3번 수포자를 오름차순으로 순회해 저장하고 있기 때문에 따로 재정렬은 하지 않았다.</p>
<h4 id="memo">MEMO</h4>
<p>최고 점수를 맞은 사람의 인덱스를 어떻게 가져와야할지 몰라서 답안을 조금 보고 작성했다.
stream을 이용한 코드가 많아서 최고값의 인덱스도 그렇게 팬시하게 가져올 수 있을 줄 알았는데 그런건 없었다 ㅎㅎ</p>
<hr>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code class="language-java">import java.util.ArrayList;
import java.util.Arrays;

class Solution{
    public int[] solution(int[] answers) {

        int[][] pattern = new int[][]{{1, 2, 3, 4, 5},
                {2, 1, 2, 3, 2, 4, 2, 5},
                {3, 3, 1, 1, 2, 2, 4, 4, 5, 5}};

        int[] scores = new int[3];

        for (int i = 0; i &lt; answers.length; i++) {
            for(int j = 0; j &lt; pattern.length; j++){
                if(answers[i] == pattern[j][i % pattern[j].length]){
                    scores[j]++;
                }
            }
        }

        int maxScore = Arrays.stream(scores).max().getAsInt();

        ArrayList&lt;Integer&gt; answer = new ArrayList&lt;&gt;();

        for (int i = 0; i &lt; 3; i++) {
            if (scores[i] == maxScore) {
                answer.add(i + 1);
            }
        }


        return answer.stream().mapToInt(Integer::intValue).toArray();
    }
}
</code></pre>
<p><code>출처</code> : 책 &#39;코딩테스트 합격자되기&#39;</p>
<p>내 코드와의 대표적인 차이점은 찍기 패턴을 이중 리스트로 선언하고, for문에서 이를 이용한 것인것 같다.</p>
<p>만약 수포자의 수가 많았다면 나도 비슷한 코드를 썼겠지만, 3명밖에 없기 때문에 직접 써주는 게 가독성과 실수를 줄일 수 있을 것이라 판단했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 두 개 뽑아서 더하기]]></title>
            <link>https://velog.io/@d_ham8_8/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%91%90-%EA%B0%9C-%EB%BD%91%EC%95%84%EC%84%9C-%EB%8D%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@d_ham8_8/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%91%90-%EA%B0%9C-%EB%BD%91%EC%95%84%EC%84%9C-%EB%8D%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 13 Jul 2024 13:49:14 GMT</pubDate>
            <description><![CDATA[<p><code>문제 이름</code> 두 개 뽑아서 더하기
<code>문제 출처</code> <a href="https://school.programmers.co.kr/learn/courses/30/lessons/68644">https://school.programmers.co.kr/learn/courses/30/lessons/68644</a></p>
<h3 id="문제">문제</h3>
<p>정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 두 개의 수를 뽑아 더해서 만들 수 있는 모든 수를 배열에 오름차순으로 담아 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<ul>
<li>numbers의 길이는 2 이상 100 이하입니다.</li>
<li>numbers의 모든 수는 0 이상 100 이하입니다.</li>
</ul>
<blockquote>
<h3 id="내-풀이">내 풀이</h3>
</blockquote>
<pre><code class="language-java">import java.util.ArrayList;
import java.util.Arrays;

class Solution {
    public int[] solution(int[] numbers) {
        ArrayList&lt;Integer&gt; result = new ArrayList&lt;&gt;();

        for(int i = 0; i &lt; numbers.length - 1; i++){
            for (int j = i + 1; j &lt; numbers.length; j++){
                result.add(numbers[i] + numbers[j]);
            }
        }

        int[] re = result.stream().mapToInt(Integer::intValue).distinct().toArray();
        Arrays.sort(re);
        return re;
    }
}</code></pre>
<h3 id="코드-설명">코드 설명</h3>
<p>문제가 요구하는 계산값을 저장하기 위한 ArrayList를 만들어 모든 경우의 수를 더해 넣었다.
이후 이 ArrayList를 스트림을 통해 배열로 바꾸어 반환하였다.</p>
<h4 id="memo">MEMO</h4>
<ul>
<li>mapToInt() : 스트림의 값을 Integer에서 int로 바꿔주고 있다.</li>
<li>distinct() : 중복 값이 들어가지 않기 위함</li>
<li>Arrays.sort() : Arrays.sort()와 Collection.sort()를 구분하여 사용하도록 하자.</li>
</ul>
<hr>
<h3 id="풀이-코드">풀이 코드</h3>
<pre><code class="language-java">import java.util.HashSet
class Solution {
    public int[] solution(int[] numbers) {
        HashSet&lt;Integer&gt; result = new HashSet&lt;&gt;();

        for(int i = 0; i &lt; numbers.length - 1; i++){
            for (int j = i + 1; j &lt; numbers.length; j++){
                result.add(numbers[i] + numbers[j]);
            }
        }

        return set.stream().sorted().mapToInt(Integer::intValue).toArray();

    }
}
</code></pre>
<p><code>출처</code> : 책 &#39;코딩테스트 합격자되기&#39;</p>
<p>이 코드는 내가 작성한 코드와 다르게 두 수를 더한 값을 ArrayList가 아닌 HashSet에 저장하고 있다.
HashSet는 중복된 값을 저장하지 않기 때문에, 따로 중복을 제거하는 코드가 없다.</p>
<p>stream().sorted()를 몰랐어서 배열을 int[]로 만든 뒤 Arrays.sort()를 이용하여 정렬하고 반환했는데, 코드가 좀 지저분하다는 생각이 들었다.</p>
<p>스트림을 더 잘 쓰기 위해 공부해야겠다는 생각이 든다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UE5] 언리얼 엔진 로깅]]></title>
            <link>https://velog.io/@d_ham8_8/UE5-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%97%94%EC%A7%84-%EB%A1%9C%EA%B9%85</link>
            <guid>https://velog.io/@d_ham8_8/UE5-%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%97%94%EC%A7%84-%EB%A1%9C%EA%B9%85</guid>
            <pubDate>Sat, 13 Jul 2024 05:30:05 GMT</pubDate>
            <description><![CDATA[<p>언리얼 엔진 로깅과 관련된 메모.</p>
<h2 id="들어가기-전에---라이더-ulog-템플릿-만들기">들어가기 전에 - 라이더 ulog 템플릿 만들기</h2>
<p>나는 언리얼 개발을 하는 데에 VScode가 아니라 라이더를 사용한다. 별 이유는 없고 그냥 제트브레인사 IDE는 익숙하지만, VScode는 잘 못 다루기 때문이다.
그리고 라이더에는 ulog 자동완성 단축어가 없어 직접 템플릿을 만들어주었다.</p>
<p>Setting &gt; Editor &gt; Live Templates &gt; Other Languages
나중에 다른 템플릿을 추가할 수도 있기 때문에, custom 폴더를 만들어 거기에 넣어주었다.</p>
<pre><code class="language-cpp">UE_LOG(LogTemp, Display, TEXT(&quot;$TEXT$&quot;));</code></pre>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/e69cb855-461d-471e-8bd9-3e8325dda3eb/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/20d8fb34-3edc-4b08-9fc6-7d8ea583bbd2/image.png" alt="">
아름답다.</p>
<blockquote>
<h1 id="output-log-로깅">Output log 로깅</h1>
</blockquote>
<p>로그 형태
<code>UE_LOG(LogClass, Log, TEXT(&quot;This is a testing statement. %s&quot;), *TestHUDString);</code></p>
<h3 id="로그-클래스">로그 클래스</h3>
<p>DEFINE_LOG_CATEGORY 매크로에서 제공되는 카테고리 이름들을 넣으면 된다.
종류는 CoreGlobals.h에 들어가면 있음! </p>
<h3 id="로그-유형">로그 유형</h3>
<p>이게 어떤 로그 유형인지 쓰는 곳.
LogVerbosity.h에 들어가면 어떤 것이 있는지, 각각은 언제 쓰는 것인지 주석이 나와 있다.</p>
<p>주요 로그 유형 종류
-Fatal
    출력 범위: 콘솔, 로그 파일
    콘솔과 로그에 출력하고 강제종료된다.</p>
<ul>
<li>Error
  출력 범위 : 콘솔, 로그 파일
  Commandlets(한글로 뭐지?!)와 에디터가 수집하여 보여준다.</li>
<li>Warning
  출력 범위 : 콘솔, 로그 파일
  Commandlets와 에디터가 수집해서 보여준다. Warning은 에러로 다루어질 수 있다.</li>
<li>Display
  출력 범위 : 콘솔, 로그 파일
  콘솔과 로그에 출력한다.</li>
<li>Log
  출력 범위 : 로그 파일
  콘솔에는 출력하지 않고 로그에만 출력한다.
외에는 Verbose, VeryVerbose가 있다.</li>
</ul>
<p>3번째 인자에는 TEXT(＂문자열＂)의 형태로 넣어준다.
TEXT() 매크로를 사용하지 않으면 지원되는 글자가 크게 제한된다.</p>
<p>4번째 인자에는 TEXT에 넣은 포메팅 문자에 들어갈 변수들을 순서대로 넣어주면 된다.
 <br>
 <br></p>
<blockquote>
<h4 id="참고하면-좋은-글-다양한-로그-출력-예시">참고하면 좋은 글 (다양한 로그 출력 예시)</h4>
<p><a href="https://hackcatml.tistory.com/151">https://hackcatml.tistory.com/151</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Entity에 여러 테이블의 정보를 갖는 dataclass 만들기]]></title>
            <link>https://velog.io/@d_ham8_8/Entity%EC%97%90-%EC%97%AC%EB%9F%AC-%ED%85%8C%EC%9D%B4%EB%B8%94%EC%9D%98-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EA%B0%96%EB%8A%94-dataclass-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@d_ham8_8/Entity%EC%97%90-%EC%97%AC%EB%9F%AC-%ED%85%8C%EC%9D%B4%EB%B8%94%EC%9D%98-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EA%B0%96%EB%8A%94-dataclass-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Tue, 28 Nov 2023 12:41:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>기록용으로 작성하는 것이므로 참고만 해주세요.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/568f83a9-aeee-4e61-8f2a-dd1323fc0ca4/image.png" alt=""></p>
<h1 id="연결이-필요하다">연결이 필요하다!</h1>
<p>다소 나중의 이야기이긴 하지만, 코드를 작성하다보니 AlbumDao 또는 SongDao 둘 중 하나만을 불러오는 것으로는 문제가 있었다.
예를 들어서, 곡의 정보를 출력할 때, 거의 항상 곡 이름, 아티스트 이름, 앨범 이름을 가져와야 했는데, 곡 이름은 SongDao에, 앨범 이름과 아티스트 이름은 AlbumDao에 들어있어서 두 Dao를 모두 불러와야 했다.
그렇다고 테이블 구조나 Entity를 수정하기에는</p>
<ul>
<li>아티스트 이름과 앨범 이름을 Song 테이블에도 넣는다 → 데이터 중복</li>
<li>모르겠다! 그냥 전부 한 테이블에 합쳐버려 → 관계형 DB를 쓰는 의미가 없음</li>
</ul>
<p>이라는 문제가!</p>
<p>아무튼 Song 테이블과 Album 테이블을 함께 불러와야했다.</p>
<blockquote>
<p>해결 방법은?
<strong>객체간의 관계를 설정한다!!!</strong></p>
</blockquote>
<p>@Embedded와 @Relation 어노테이션을 사용해 SongWithAlbum 이라는 Song 테이블과 ALbum 테이블을 모두 불러오는 data class를 만들었다.</p>
<pre><code class="language-kotlin">data class SongWithAlbum(
    @Embedded
    val song:SongEntity,
    @Relation(
        parentColumn = &quot;albumName&quot;,
        entityColumn = &quot;name&quot;
    )
    val album: AlbumEntity
)</code></pre>
<p>이 코드에서 중요한 부분은 @Relation 어노테이션인데, 기존에 Entity에서 외래키로 설정해준 관계를 여기에 넣어주면 외래키를 이용해서 연결되는 데이터를 가져올 수 있다.
Song이 SongWithAlbum객체라고 할때,
Song.<del>~ 로는 SongDao의 함수들을 사용할 수 있고,
Song.Album.</del> 로는 Song과 연결된 앨범에 대하여 AlbumDao의 함수들을 사용 할 수 있다.</p>
<blockquote>
<p>🙋 왜 Song으로 Album에 접근하나요?
Album에서도 Song에 대한 접근을 설정해 AlbumWithSongs라는 Entity를 만들어야 하는게 아닌가요?</p>
</blockquote>
<p>나도 처음에는 모두 매칭이 되도록 각각의 Entity에서 다른 Entity로 접근할 수 있는 관계Entity들을 만들었다.
하지만 Song을 출력할 때 AlbumEntity의 정보를 불러올 일은 많았지만, (ex. 노래를 불러온다면 앨범 정보와 아티스트도 함께 출력함) Album을 출력할 때 SongEntity의 정보를 불러올 일은 거의 없었다.
그래서 일단 지금 작성되어 있는 프로젝트 코드에는 Album으로 Album의 노래들의 접근할 수 있도록 AlbumWithSongs라는 Entity를 정의해놓고 사용하게 되면 Dao를 구현하려고 했는데, 결국 아직은 필요성을 느끼지 못해서 Dao는 구현되어있지 않다.</p>
<p>그러고 나서 생각한건데, 
결국엔 자주 사용하는 방식으로만 관계를 설정하는게 낫지 않나 싶었다.
Album을 통해서 Song에 접근해야 하는 이유가 있는게 아니라면...
그리고 웬만해서는 SongWithAlbumDao에 정의한 함수들로 접근할 수 있기 때문에
코드에 혼동이 생기지 않도록, 가독성을 고려하여...</p>
<p>라는 생각을 했다</p>
<p>사실 이 글을 작성하는 지금 시점으로, 간단하게 노래와 앨범 테이블만 만들어서 읽고 출력했던 것과는 다르게 데이터베이스 기말과제를 하면서 데이터베이스 추가, 수정, 삭제랑 다른 추가적인 테이블 구현까지 할 생각이라...
다음 글들은 기말과제가 끝나고 작성할 것 같다.
DB를 다루는 범위가 넓어져서 지금 작성한 글들과는 또 다른 코드를 작성할 수도 있을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 생성하고 Entity 작성하기]]></title>
            <link>https://velog.io/@d_ham8_8/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%83%9D%EC%84%B1%ED%95%98%EA%B3%A0-Entity-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@d_ham8_8/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%83%9D%EC%84%B1%ED%95%98%EA%B3%A0-Entity-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 28 Nov 2023 10:21:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>개인적으로 기록하기 위한 용도의 포스팅으로,
잘못된 정보가 들어있을 수 있습니다.
참고용으로만 봐주세요.</p>
</blockquote>
<h1 id="데이터베이스-작성하기">데이터베이스 작성하기</h1>
<h2 id="요구사항-확인하기">요구사항 확인하기</h2>
<p>내가 데이터베이스를 통해 어플에서 구현하고 싶은 것은
Album fragment에서 곡의 정보를 통해 곡이 속한 앨범의 곡 목록을 불러오는 것이었다.
말이 좀 꼬였는데 
A 앨범에 속한 a라는 노래를 클릭하면, Album fragment가 나오면서 UI의 Recycler View의 목록에 A 앨범이 가지고 있는 모든 노래를 출력하고 싶었다.</p>
<p>그런 시스템을 만들기 위해서 데이터베이스에는</p>
<ol>
<li>앨범</li>
<li>곡 
두 테이블이 필요하고,
앨범과 곡이 외래 키로 1:n으로 매칭되어야했다.</li>
</ol>
<p>각 테이블은 다음과 같은 정보를 가졌다.</p>
<ol>
<li>앨범</li>
</ol>
<ul>
<li>앨범 이름</li>
<li>아티스트 이름</li>
<li>앨범 커버 이미지</li>
</ul>
<ol start="2">
<li>곡</li>
</ol>
<ul>
<li>ID (기본 키)</li>
<li>앨범 이름 (외래키)</li>
<li>트랙 넘버</li>
<li>곡 이름</li>
</ul>
<p>여기서 ID는 기본 키로 설정하기 위해서 넣었는데, 나중에 &#39;앨범 이름과 곡 이름으로 각 요소가 구분이 된다면 굳이 ID라는 기본 키를 가지고 있을 필요는 없다&#39;라는 조언을 받았다.</p>
<p>들어있는 데이터의 예시는 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/84ad8d3a-cbdc-4e19-b681-d09cc36537a9/image.png" alt="">
<img src="https://velog.velcdn.com/images/d_ham8_8/post/55f55a42-e64d-4aaf-9386-8477758db67f/image.png" alt=""></p>
<h1 id="roomentitykt-작성하기">RoomEntity.kt 작성하기</h1>
<p>데이터베이스를 Room으로 어플과 연동하기 위해선 우선 만든 데이터베이스와 같은 Entity를 가진 data class를 만들어야한다.
그리고 @Entity, @PrimaryKey, @ColumInfo 등등 알맞은 어노테이션을 써주어야한다.</p>
<p>전체 코드는 아래와 같고,</p>
<pre><code class="language-kotlin">@Entity(
    tableName = &quot;Song&quot;,
    foreignKeys = [
        ForeignKey(
            entity = AlbumEntity::class,
            parentColumns = arrayOf(&quot;name&quot;),
            childColumns = arrayOf(&quot;albumName&quot;)
        )
    ]
)
data class SongEntity(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo
    var ID: Long = 0,

    @ColumnInfo
    var trackId: Long? = 0,

    @ColumnInfo
    var name: String? = &quot;&quot;,

    @ColumnInfo
    @ForeignKey(
        entity = AlbumEntity::class,
        parentColumns = [&quot;name&quot;],
        childColumns = [&quot;albumName&quot;]
    )
    var albumName: String? = &quot;&quot;



)

@Entity(tableName = &quot;Album&quot;)
data class AlbumEntity(
    @PrimaryKey(autoGenerate = false)
    @ColumnInfo
    var name: String = &quot;&quot;,

    @ColumnInfo
    var artist: String = &quot;&quot;,

    @ColumnInfo
    var coverImage: Bitmap? = null,
)

data class AlbumWithSongs(
    @Embedded
    val album: AlbumEntity,
    @Relation(
        parentColumn = &quot;name&quot;,
        entityColumn = &quot;albumName&quot;
    )
    val songLists: List&lt;SongEntity&gt;
)

data class SongWithAlbum(
    @Embedded
    val song:SongEntity,
    @Relation(
        parentColumn = &quot;albumName&quot;,
        entityColumn = &quot;name&quot;
    )
    val album: AlbumEntity
)</code></pre>
<p>이 중 눈여겨 봐야 할 부분은</p>
<ol>
<li>필드 속성 맞추는 법</li>
</ol>
<ul>
<li>타입 맞추기
TEXT : String
INTEGER : Long
BLOB : Bitmap</li>
<li>속성 맞추기
NN(Non Null): 변수명 뒤에 ?를 붙여 nullable로 만들면 false, 아니라면 true
PK(Primary Key): @PrimaryKey라는 어노테이션을 넣는다</li>
</ul>
<p>2.변수 이름과 필드 이름 맞춰주기
변수 이름과 필드 이름을 동일하게 설정했지만, 두 개를 다르게 설정할 수도 있다. 각 필드에 매칭되는 변수를 선언하기 위해 한 줄 위에 @ColumnInfo라는 어노테이션을 달아줘야하는데, 
@ColumnInfo(name=&quot;필드 이름&quot;)
과 같이 작성해주면 된다.</p>
<p>3 각 테이블관의 관계 설정하기
외래키를 설정하는 방법이다. @Entity 어노테이션에서 괄호()를 열고 다음과 같이적어주면 된다.</p>
<pre><code class="language-kotlin">@Entity(
    tableName = &quot;테이블 이름&quot;,
    foreignKeys = [
        ForeignKey(
            entity = 관계를 맺는 엔티티 클래스 이름::class,
            parentColumns = arrayOf(&quot;부모 필드&quot;),
            childColumns = arrayOf(&quot;자식 필드&quot;)
        )
    ]
)</code></pre>
<p>그리고 외래키 컬럼 변수 위에 @ColumnInfo와 함께 @ForeignKey 어노테이션에 다음과 같이써주면 된다.</p>
<pre><code class="language-kotlin">@ColumnInfo
@ForeignKey(
    entity = 관계를 맺는 엔티티 클래스 이름::class,
    parentColumns = [&quot;부모 필드&quot;],
    childColumns = [&quot;자식 필드&quot;]
)
var albumName: String? = &quot;&quot;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[외부에서 작성한 SQLite .db파일을 안드로이드에서 Room으로 불러오기 (1)]]></title>
            <link>https://velog.io/@d_ham8_8/%EC%99%B8%EB%B6%80%EC%97%90%EC%84%9C-%EC%9E%91%EC%84%B1%ED%95%9C-SQLite-.db%ED%8C%8C%EC%9D%BC%EC%9D%84-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%EC%97%90%EC%84%9C-Room%EC%9C%BC%EB%A1%9C-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0-1</link>
            <guid>https://velog.io/@d_ham8_8/%EC%99%B8%EB%B6%80%EC%97%90%EC%84%9C-%EC%9E%91%EC%84%B1%ED%95%9C-SQLite-.db%ED%8C%8C%EC%9D%BC%EC%9D%84-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%EC%97%90%EC%84%9C-Room%EC%9C%BC%EB%A1%9C-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0-1</guid>
            <pubDate>Tue, 07 Nov 2023 13:14:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/e971c3bc-4e41-4cce-b52a-799188e65af0/image.png" alt=""></p>
<h1 id="room에서-외부-db파일-사용하기">Room에서 외부 DB파일 사용하기</h1>
<h2 id="들어가며">들어가며</h2>
<p>UMC, Universe MakeUs Challenge의 Android 파트에서 FLO 어플을 클론코딩하는 실습을 하고있다. 같은 가이드라인과 워크북으로 실습을 하고 있지만, 각자 공부를 하는 속도나 스타일에 따라서 어플을 구현하는 순서나 방법이 조금씩 다르다. 
나는 앨범과 곡의 정보를 보여주는 Fragment를 만들 때, 마침 전공 과목으로 데이터베이스를 듣고 있어서 그 지식을 활용하고 싶어서 DB Browser for SQLite로 데이터베이스를 작성하고 이를 불러와 Recycler View에 적용하고 싶었다. 그래서 그렇게 했다. </p>
<p>DB Browser로 데이터베이스를 작성할 때까지는 &quot;쉽네, 오래 안 걸리겠네^^&quot;라고 생각했다.</p>
<p><strong>그리고 그걸 room으로 recycler view에 적용하기 까지 2주(혹은 3주)를 헤맸다.</strong></p>
<p>Android 시스템과 데이터베이스의 이해도가 높지 않은 시점에서 적용을 하려니 참 고생을 많이 했다.</p>
<p>내가 이 작업을 다시 할 때 수월하게 할 수 있도록, 그리고 나와 같은 사람들이 같은 작업을 할 때 참고가 되었으면 해서 글을 작성한다.</p>
<h1 id="개요">개요</h1>
<h2 id="무엇을-만드나">무엇을 만드나</h2>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/4d9f6f75-8dff-4359-a7ff-a9db9e0481d9/image.png" alt=""></p>
<h6 id="span-stylecolor-lightgray-gg-및-warbear-등-오자키-유우키-노래-좋아하시는-분은-제게-연락-주세요--친구합시다span"><span style="color: lightgray"> GG 및 warbear 등 오자키 유우키 노래 좋아하시는 분은 제게 연락 주세요 ^^ 친구합시다^^</span></h6>
<p>FLO 어플을 보면서 이와 같은 작업을 하는 어플리케이션을 만들고 있다.
노래 정보가 나온 것을 누르면 Fragment를 실행해 해당 노래에 관한 정보와, 그 곡이 속해있는 
앨범 정보(앨범 이름, 수록 곡 리스트)를 보여주었다.</p>
<h2 id="구현하고자-하는-기능">구현하고자 하는 기능</h2>
<ol>
<li>HomeFragment의 &quot;오늘 발매 음악&quot;의 아이템을 RecyclerView로 구현, 각 item에 Click Listener를 달아, 곡을 클릭하면 HomeFragment와 곡의 정보를 보여주는 AlbumFragment를 전환</li>
<li>AlbumFragment의 상단에는 클릭한 곡의 정보가 나오고, 하단에는 Recycler View를 이용해 곡이 속한 앨범의 노래 목록을 보여준다.</li>
<li>지금은 어플 내에서 db를 수정하지 않기에, 삽입/수정/삭제 메서드는 구현하지 않고, 오직 불러오기만 하는 것이 목표!</li>
</ol>
<p><br><br><br>
...를 하는 과정을 시리즈로 작성하도록 하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DataBase] 관계 대수]]></title>
            <link>https://velog.io/@d_ham8_8/DataBase-%EA%B4%80%EA%B3%84-%EB%8C%80%EC%88%98</link>
            <guid>https://velog.io/@d_ham8_8/DataBase-%EA%B4%80%EA%B3%84-%EB%8C%80%EC%88%98</guid>
            <pubDate>Tue, 31 Oct 2023 13:42:43 GMT</pubDate>
            <description><![CDATA[<h1 id="관계대수란">관계대수란</h1>
<p>관계대수란, 내가 원하는 릴레이션을 <strong>릴레이션 간 연산을 통해 찾는 절차</strong>를 기술한 언어이다.
<strong>릴레이션</strong>과 <strong>연산자</strong>로 구성되어 있으며, 연산의 결과는 <strong>릴레이션</strong>이다.
관계대수식의 결과 릴레이션을 또 다른 관계 대수 연산자의 입력으로 사용할 수 있어서, 관계 대수 연산자들을 여러개 조합할 수 있다.</p>
<h2 id="관계-대수의-완전성">관계 대수의 완전성</h2>
<p>관계대수의 기본연산자인 셀렉트, 프로젝트, 합집합, 차집합, 카티션 프로덕트 만으로도 모든 질의를 표현할 수 있다. 어떤 DB 언어가 어떤 임의의 질의어를 가진다고 해도, 저 5가지 질의어를 모두 표현할 수 있다면 <strong>관계적으로 완전하다</strong>고 한다.</p>
<h2 id="관계-대수-연산자의-분류">관계 대수 연산자의 분류</h2>
<ul>
<li>단항 연산자 vs 다항 연산자</li>
<li>기본 연산자 vs 관계 연산자</li>
</ul>
<hr>
<h1 id="관계-대수-연산자">관계 대수 연산자</h1>
<h2 id="1-σ-셀렉트">1. σ 셀렉트</h2>
<p>릴레이션에서 원하는 <strong>투플(행)</strong>을 추출하기 위한 연산자이다. 
단항 연산자이며, 찾고자하는 투플의 조건을 명시하고, 그 조건에 만족하는 투플을 반환한다.</p>
<blockquote>
<h4 id="σ셀렉트-조건r">σ셀렉트 조건(R)</h4>
</blockquote>
<p>결과 릴레이션의 차수 = 입력 릴레이션의 차수
결과 릴레이션의 카디널리티 ≤ 입력 릴레이션의 카디널리티</p>
<p>셀렉트 조건은 릴레이션의 속성, 상수, 비교 연산자(=, &lt;, &gt;, ≤, ≥, &gt;), 논리 연산자(AND, OR)를 조합하여 사용한다.</p>
<p>예시</p>
<ul>
<li>등급이 Gold이면서 적립금이 2000 이상인 고객을 검색하라
  → $\boldσ _{등급=&#39;Gold&#39; \ AND \ 적립금 ≥ 2000} (고객)$</li>
</ul>
<hr>
<h2 id="2-π-프로젝트">2. π 프로젝트</h2>
<p>릴레이션에서 원하는 <strong>속성(열)</strong>을 추출하기 위한 연산자이다. 
단항 연산자이다.
셀렉트의 결과 릴레이션은 중복 투플이 없지만, 프로젝트 결과 릴레이션은 중복된 투플이 존재 가능하다. 다만, 최종 결과 릴레이션에선 중복된 투플은 제거된다. (key 속성이 없어질 수 있어서)</p>
<blockquote>
<h4 id="π속성리스트r">π속성리스트(R)</h4>
</blockquote>
<p>결과 릴레이션의 차수 ≤ 입력 릴레이션의 차수
결과 릴레이션의 카디널리티 = 입력 릴레이션의 카디널리티</p>
<p>프로젝트 조건도 셀렉트와 마찬가지로 릴레이션의 속성, 상수, 비교 연산자(=, &lt;, &gt;, ≤, ≥, &gt;), 논리 연산자(AND, OR)를 조합하여 사용한다.</p>
<p>예시</p>
<ul>
<li>고객의 고객 이름, 등급, 적립금을 검색하라
  → $\boldπ _{고객이름, 등급, 적립금} (고객)$</li>
</ul>
<hr>
<h4 id="셀렉트와-프로젝트를-모두-사용한-예시">셀렉트와 프로젝트를 모두 사용한 예시</h4>
<ul>
<li>등급이 &#39;gold&#39;인 고객의 이름과 나이를 검색하라.
$\bold{π} _{고객 이름, 나이} (\bold{σ} _{등급=&#39;gold&#39;}(고객))$</li>
</ul>
<hr>
<h2 id="3-집합-연산자">3. 집합 연산자</h2>
<p>릴레이션은 투플들의 집합이기 때문에 집합 연산을 릴레이션에 적용할 수 있다.</p>
<blockquote>
<p>🍀 종류 : 합집합, 교집합, 차집합</p>
</blockquote>
<p>이항연산자이다.</p>
<p><strong>집합 연산자의 입력으로 사용되는 2개의 릴레이션은 합병 가능 조건을 만족해야한다</strong></p>
<blockquote>
<p>🍀 합병 가능 조건</p>
</blockquote>
<ol>
<li>두 릴레이션의 속성 개수가 같고, 대응되는 속성의 도메인이 같다. (속성 이름은 달라도 됨)</li>
<li>두 릴레이션 $R1(A_1, A_2, \dots, A_n)$과 $R2(B_1, B_2, \dots, B_n)$이 합병 가능할 필요 충분 조건은,$n=m$이고, 모든 $1\leq i \leq n$에 대해 $domain(A_i)=domain(B_i)$</li>
</ol>
<p>합병 불가능한 예시</p>
<ul>
<li>고객(고객번호, 이름, 주소, 핸드폰)</li>
<li>주문(주문번호, 고객번호, 도서번호, 판매가격, 주문일자)</li>
</ul>
<p>고객 릴레이션과 주문 릴레이션은 속성 개수가 다르므로 합병이 불가능하다.</p>
<h3 id="∪-합집합">∪ 합집합</h3>
<p>두 릴레이션 R과 S의 합집합은 R 또는 S에 존재하는 모든 투플들로 이루어진 릴레이션이다.</p>
<blockquote>
<h4 id="-r∪s-">** R∪S **</h4>
<p>단, 결과 릴레이션에서 중복된 투플들은 제외된다.</p>
</blockquote>
<p>결과 릴레이션의 차수 = R과 S의 차수
결과 릴레이션의 카디널리티 ≤ (R의 카디널리티 + S의 카디널리티)</p>
<p>결과 릴레이션의 속성 이름들은 R의 속성들의 이름과 같거나 S의 속성들의 이름과 같음</p>
<h3 id="∩-교집합">∩ 교집합</h3>
<p>두 릴레이션 R과 S의 교집합은 R과 S에 모두 존재하는 투플들로 이루어진 릴레이션이다.</p>
<blockquote>
<h4 id="-r∩s-">** R∩S **</h4>
</blockquote>
<p>결과 릴레이션의 차수 = R과 S의 차수
결과 릴레이션의 카디널리티 ≤ R의 카디널리티
                AND 
결과 릴레이션의 카디널리티 ≤ S의 카디널리티</p>
<p>결과 릴레이션의 속성 이름들은 R의 속성들의 이름과 같거나 S의 속성들의 이름과 같음</p>
<h3 id="ㅡ-차집합">ㅡ 차집합</h3>
<p>두 릴레이션 R과 S의 차집합은 R에는 존재하지만 S에는 존재하지 않는 투플들로 이루어진 릴레이션이다.</p>
<blockquote>
<h4 id="-rㅡs-">** RㅡS **</h4>
</blockquote>
<p>결과 릴레이션의 차수 = R과 S의 차수
R - S의 카디널리티 ≤ R의 카디널리티</p>
<p>결과 릴레이션의 속성 이름들은 R의 속성들의 이름과 같거나 S의 속성들의 이름과 같음</p>
<h2 id="4-x-카티션-프로덕트">4. X 카티션 프로덕트</h2>
<p>두 릴레이션을 연결시켜 하나로 합칠 때 사용하며, R과 S의 릴레이션으로 만들 수 있는 모든 조합으로 이루어진 릴레이션이다.
릴레이션의 크기가 매우 클 수 있어 카티션 프로덕트 자체는 유용한 연산자가 아니다.
동일한 속성이 두 릴레이션에 포함되어있을 수 있기 때문에 속성 이름 앞에 릴레이션 이름을 붙인다.</p>
<blockquote>
<h4 id="r-×-s">R × S</h4>
</blockquote>
<p>결과 릴레이션의 차수 = R의 차수 + S의 차수
결과 릴레이션의 카디널리티 = R의 카디널리티 X S의 카디널리티</p>
<h2 id="5-조인">5. 조인</h2>
<p>릴레이션 하나로 원하는 데이터를 얻을 수 없어, 관계가 있는 여러 릴레이션을 함께 사용해야 하는 경우 사용한다.
두 릴레이션의 공통 속성을 기준으로 속성 값이 같은 투플들을 결합하며, 공통 속성의 값이 동일한 투플만을 반환한다.
조인을 수행하기 위해서는 <strong>두 릴레이션의 조인에 참여하는 속성이 서로 동일한 도메인으로 구성</strong>되어야한다.</p>
<h3 id="⋈-동등-조인">⋈ 동등 조인</h3>
<p>조인에 참여하는 두 릴레이션의 속성 값을 비교하여 그 값이 같은 투플만 반환한다.</p>
<blockquote>
<h4 id="rbowtie_rss">$R\bowtie_{r=s}S$</h4>
<p>기본 연산자로 풀어낸 식은
$R\bowtie_{r=s}S\equiv\sigma_{r=s}(R\times S)$</p>
</blockquote>
<h3 id="⋈n-자연-조인">⋈N 자연 조인</h3>
<p>동등 조인에서 조인에 참여한 속성이 두 번 나오지 않도록 두 번째 속성을 제거한 결과를 반환한다.</p>
<blockquote>
<h4 id="rbowtie_nrss">$R\bowtie_{N(r,s)}S$</h4>
<p>$R\bowtie_{(r,s)}S$
$R\bowtie_{N}S$
$R\bowtie S$ 와 같이 나타낸다.</p>
</blockquote>
<p>여러가지 조인 연산자들 중에서 가장 자주 사용된다.</p>
<h3 id="⟕-외부-조인">⟕ 외부 조인</h3>
<p>자연 조인시 조인에 실패한 투플들을 모두 보여주되, 값이 없는 대응 속성에는 NULL값을 채워서 반환한다.
모든 속성을 보여주는 기준 릴레이션의 위치에 따라 왼쪽 외부조인, 오른쪽 외부조인, 완전 외부조인으로 나뉜다. </p>
<h4 id="왼쪽-외부조인">왼쪽 외부조인</h4>
<p>릴레이션 R과 S의 왼쪽 외부조인은 R의 모든 투플들을 결과에 포함시키고, 만일 S에 관련된 투플이 없으면 S의 속성들은 NULL값으로 채운다.</p>
<h4 id="오른쪽-외부조인">오른쪽 외부조인</h4>
<p>릴레이션 R과 S의 오른쪽 외부조인은 S의 모든 투플들을 결과에 포함시키고, 만일 R에 관련된 투플이 없으면 R의 속성들은 NULL값으로 채운다.</p>
<h4 id="완전-외부조인">완전 외부조인</h4>
<p>릴레이션 R과 S의 완전 외부조인은 R과 S의 모든 투플들을 결과에 포함시키고, 관련된 투플이 없으면 그 속성들은 NULL값으로 채운다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Activity vs Fragment]]></title>
            <link>https://velog.io/@d_ham8_8/Android-Activity-vs-Fragment</link>
            <guid>https://velog.io/@d_ham8_8/Android-Activity-vs-Fragment</guid>
            <pubDate>Tue, 10 Oct 2023 14:00:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="🤷-activity랑-fragment는-도대체-뭐가-다른가요">🤷 {Activity랑 Fragment는 도대체 뭐가 다른가요?)</h3>
</blockquote>
<p>안드로이드 어플에서 화면을 구성하는 요소로 크게 <strong>Activity</strong>와 <strong>Fragment</strong>를 꼽을 수 있다. 동일한 폴더에 .xml 파일로 뷰를 구성하는 점이 동일해, 언제 어느 것을 사용해야하는지 헷갈리는 경우가 있다.</p>
<p>이번 글에서는 </p>
<ul>
<li>Activity와 Fragment란 무엇이고</li>
<li>어떤 관계가 있으며</li>
<li>어느 때 무엇을 사용해야 하는지 </li>
</ul>
<p>정리한다.</p>
<h1 id="activity란">Activity란</h1>
<blockquote>
<h4 id="사용자가-직접-보고-입력하는-화면">사용자가 직접 보고 입력하는 화면!</h4>
<p>공식문서
<a href="https://developer.android.com/guide/components/activities/intro-activities">https://developer.android.com/guide/components/activities/intro-activities</a></p>
</blockquote>
<p>Activity란 안드로이드 어플을 구성하는 중요한 요소로, 여러 프로그램들이 main()함수로 프로그램을 시작하는 것처럼, 안드로이드는 Activity 인스턴스를 호출하여 시스템을 시작한다.</p>
<p>Activity는 앱이 사용자에게 보여줄 UI를 그리는 것을 담당하기도 한다. 대체로 화면에 꽉 차게 생성되지만, 화면보다 작게 만들어 다른 창 위에 띄울 수도 있다. </p>
<h1 id="fragment란">Fragment란</h1>
<h4 id="액티비티를-나누는-조각">액티비티를 나누는 조각!</h4>
<p>공식 문서
<a href="https://developer.android.com/guide/fragments">https://developer.android.com/guide/fragments</a></p>
<p>하나의 어플을 다양한 화면 비율에 대응하게 만든다고 생각해보자. constraint Layout을 사용해 제약조건을 정의하는 것 만으로는 한계가 있다. 그렇다고 화면을 구성하는 모든 View를 비율에 맞춰 다시 디자인하고 작성하는 건 너무너무 끔찍하지 않은가?! (나는 그런데...) </p>
<h2 id="모듈화">모듈화!</h2>
<p><img src="https://velog.velcdn.com/images/d_ham8_8/post/53ffbc55-b11c-4c06-8174-274d80f11ec3/image.png" alt=""></p>
<p>이 때, UI를 쪼개고 모듈화해서, 각 비율에 알맞은 화면으로 구성할 수 있도록 하는 것이 Fragment이다.
위의 사진은 Fragment(초록색)를 이용해서 list를 탭 비율에서는 grid list로, 스마트폰 비율에서는 linear list로 보여주는 예시이다.</p>
<h3 id="😧-하지만-실제로는">😧 하지만 실제로는?!</h3>
<p>실제로는 이렇게 태블릿 환경에 대응할 때 보다는, </p>
<ul>
<li>탭이나 스와이프로 화면 전환을 할 때 미리 만들어놓은 Fragment를 전환하거나</li>
<li>한 화면에 여러개의 섹션을 모듈화하고 한번에 나타낼 때</li>
</ul>
<p>사용된다.</p>
]]></description>
        </item>
    </channel>
</rss>