<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev_hogun.log</title>
        <link>https://velog.io/</link>
        <description>문과 였던 것...</description>
        <lastBuildDate>Mon, 18 Aug 2025 09:25:27 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev_hogun.log</title>
            <url>https://velog.velcdn.com/images/dev_hogun/profile/6984f21b-a454-4105-9d98-bf5e35d06ff5/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev_hogun.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev_hogun" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Java] Java의 Thread를 이해해보자. (이해 못함)]]></title>
            <link>https://velog.io/@dev_hogun/Java-Java%EC%9D%98-Thread%EB%A5%BC-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90.-%EC%9D%B4%ED%95%B4-%EB%AA%BB%ED%95%A8</link>
            <guid>https://velog.io/@dev_hogun/Java-Java%EC%9D%98-Thread%EB%A5%BC-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90.-%EC%9D%B4%ED%95%B4-%EB%AA%BB%ED%95%A8</guid>
            <pubDate>Mon, 18 Aug 2025 09:25:27 GMT</pubDate>
            <description><![CDATA[<h2 id="스레드에-대한-자바의-정의">스레드에 대한 자바의 정의</h2>
<blockquote>
<p>프로그램을 실행하는 스레드 단위. JVM은 한 애플리케이션에서 여러 Excution을 동시에 이루어지도록 한다. 
(A thread is a thread of execution in a program. The Java virtual machine allows an application to have multiple threads of execution running concurrently.)</p>
</blockquote>
<ul>
<li>스레드는 간접적으로 동시성을 구현하기 위한 도구</li>
<li>스레드는 <code>run</code> 을 통해 실행되며 작업을 정상적으로 마쳤을 때 (더 이상 실행시킬 코드가 남아있지 않을 때), 예외가 발생했을 때, <code>join</code> (스레드가 완료될 때까지 기다리는 메서드) 명령어를 쓸 때 등의 상황에서 중단될 수 있다. </li>
<li>Thread는 구분자(identifier)와 이름(name)을 갖는다. <ul>
<li>id는 직접 지정이 불가능</li>
<li>이름은 직접 지정이 가능하며 하지 않을 경우 자동 생성
예시 코드<pre><code class="language-java">Thread t1 = new Thread();  
System.out.println(&quot;thread id : &quot; + t1.getId());  
System.out.println(&quot;thread name : &quot; + t1.getName());  
</code></pre>
</li>
</ul>
</li>
</ul>
<p>Thread t2 = new Thread();<br>System.out.println(&quot;thread id : &quot; + t2.getId());<br>System.out.println(&quot;thread name : &quot; + t2.getName());</p>
<pre><code>실행 결과
```bash
thread id : 20
thread name : Thread-0
thread id : 21
thread name : Thread-1</code></pre><h2 id="os상의-thread와의-차이">OS상의 Thread와의 차이</h2>
<h3 id="platform-thread">Platform Thread</h3>
<ul>
<li>OS상의 스레드와 1:1로 매핑되는 스레드</li>
<li>OS Scheduler가 관리<h3 id="virtual-thread">Virtual Thread</h3>
</li>
<li>OS상의 스레드와 N:M으로 매핑되는 Java에서 만든 가상의 스레드</li>
<li>JVM의 Scheduler가 관리<h3 id="platform-vs-virtual">Platform vs Virtual</h3>
</li>
<li>Default는 Platform Thread</li>
<li>Virtual Thread는 JVM 21 부터 사용 가능 (19부터 쓸 수 있었다고는 하는데...)</li>
<li>I/O 대기(HTTP/DB/File)가 대부분이면 가상스레드를 사용 (생산 및 유지 비용이 적음)</li>
<li>CPU 계산, OS의 스레드 기능 등이 대부분이면 플랫폼 스레드를 사용<h2 id="thread-에서의-작업">Thread 에서의 작업</h2>
</li>
<li>Thread에서의 작업은 Runnable과 Callable로 구현된다. <ul>
<li>Runnable과 Callable은 모두 인터페이스며 이를 통해 간접적으로 코드베이스를 전달한다. <ul>
<li>Runnable은 반환값이 존재하지 않는 실행을 대변한다. </li>
<li>Callable은 결과를 반환하거나 그렇게 할 수 없을 경우 예외를 던진다. </li>
</ul>
</li>
<li>start를 하게 되면 Thread에서 Runnable / Callable의 run 메서드를 실행해준다. </li>
</ul>
</li>
<li>Thread의 Context는 ThreadLocal 변수에 보관된다. (라고 하네요)<h2 id="concurrency">Concurrency</h2>
Thread는 하나의 애플리케이션 내 여러 작업을 비동기적으로 실행하기 위해 존재한다.따라서 동시성을 제어하는 것이 중요한 이슈 중 하나이다. <h3 id="수명-lifecycle">수명 (LifeCycle)</h3>
</li>
<li>NEW : 객체가 생성은 됨, 스레드가 시작된 것은 아님</li>
<li>RUNNABLE : 실행 (스케줄링 대기 중일 수도 있음)<ul>
<li>start 메서드로 실행</li>
</ul>
</li>
<li>BLOCKED / WAITING / TIMED WAITING : 대기중인 상태<ul>
<li>syncronized, wait, sleep, join ...</li>
</ul>
</li>
<li>TERMINATED : 스레드가 종료됨 (run에 있는 코드베이스를 모두 실행)<h3 id="동기화">동기화</h3>
</li>
<li>volite(스레드에 의존성이 낮은 변수), synchronized(간단한 상호배제 기능 포함), Lock/Condition, 원자 클래스(Atomic), 그 밖의 동시성을 제어하는 도구들 (BlockingQueue, Semaphore) 등을 활용할 수 있다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] POJO에서 로깅 날먹하는법 - 어노테이션을 활용한 로깅 시스템 구현]]></title>
            <link>https://velog.io/@dev_hogun/Java-POJO%EC%97%90%EC%84%9C-%EB%A1%9C%EA%B9%85-%EB%82%A0%EB%A8%B9%ED%95%98%EB%8A%94%EB%B2%95-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%A1%9C%EA%B9%85-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@dev_hogun/Java-POJO%EC%97%90%EC%84%9C-%EB%A1%9C%EA%B9%85-%EB%82%A0%EB%A8%B9%ED%95%98%EB%8A%94%EB%B2%95-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%A1%9C%EA%B9%85-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Mon, 28 Jul 2025 00:45:22 GMT</pubDate>
            <description><![CDATA[<h2 id="어노테이션이란">어노테이션이란</h2>
<p>코드에 메타데이터를 추가하는 방법
어노테이션은 메타데이터만을 갖고있으나 리플렉션 API와 함께 사용하면 높은 활용도를 가진다. 
Reflection API : 동적으로 클래스를 다루는 API, 통상적으로는 개발 중 클래스를 개발자가 직접 다룬 후 런타임에서는 실행만 하게 되지만 reflection API를 활용할 경우 런타임 시점에 만들어둔 클래스의 정보를 가져와 동적으로 활용할 수 있다. </p>
<h3 id="사용할-api">사용할 API</h3>
<p>ClassLoader : 클래스를 메모리에 올리는 장치
Proxy : 직역하자면 대리인이라는 의미, <code>newProxyInstance</code> 메서드를 통해 ClassLoader, interface, InvocationHandler를 받아 프록시 객체를 생성
isAnnotationPresent : 어노테이션을 확인하는 함수</p>
<h2 id="로깅-시스템-전략">로깅 시스템 전략</h2>
<ol>
<li>실행시킬 서비스를 인터페이스와 구현체로 분리</li>
<li>main에서 이들을 갖고 프록시 객체를 생성. </li>
<li>함수 실행 시 그 흐름을 가로챔</li>
<li><code>LogIO</code> 어노테이션이 있다면 파라미터와 실행 결과를 로깅함</li>
<li><code>LogExecutionTime</code> 어노테이션이 있다면 실행 시간을 로깅함</li>
</ol>
<h2 id="구현">구현</h2>
<h3 id="어노테이션-생성">어노테이션 생성</h3>
<p>LogIO</p>
<pre><code class="language-java">@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogIO {}</code></pre>
<p>LogExecutionTime</p>
<pre><code class="language-java">@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {}</code></pre>
<h3 id="프록시-및-핸들러-구현">프록시 및 핸들러 구현</h3>
<p>핸들러 구현</p>
<pre><code class="language-java">class LoggingHandler implements InvocationHandler {
    private final Object target;

    LoggingHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Method realMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());

        if (realMethod.isAnnotationPresent(LogIO.class)) {

        }

        long start = System.nanoTime();
        Object result = realMethod.invoke(target, args);
        long end = System.nanoTime();

        if (realMethod.isAnnotationPresent(LogIO.class)) {
            System.out.print(&quot;[LOG] 함수 호출: &quot; + realMethod.getName() + &quot;(&quot;);
            System.out.print(String.join(&quot;, &quot;, Arrays.stream(args).map(Object::toString).toArray(String[]::new)));
            System.out.println(&quot;) -&gt; &quot; + result);
        }

        if (realMethod.isAnnotationPresent(LogExecutionTime.class)) {
            System.out.printf(&quot;[LOG] %s 실행 시간: %.3fms\n&quot;, method.getName(), (end - start) / 1_000_000.0);
        }

        return result;
    }
}</code></pre>
<p>InvocationHandler를 implement 해서 함수의 흐름을 가로챔
4, 5번 로직을 처리함</p>
<h3 id="프록시-객체-생성">프록시 객체 생성</h3>
<pre><code class="language-java">public class LoggingProxy {
    public static &lt;T&gt; T of(Class&lt;T&gt; targetClass) {
        try {
            T real = targetClass.getDeclaredConstructor().newInstance();

            return (T) Proxy.newProxyInstance(
                    targetClass.getClassLoader(),
                    targetClass.getInterfaces(),
                    new LoggingHandler(real)
            );
        } catch (Exception e) {
            throw new RuntimeException(&quot;프록시 생성 실패&quot;, e);
        }
    }
}</code></pre>
<ul>
<li>InvocationHandler를 확장한 LoggingHandler를 넘겨줘서 프록시를 생성</li>
</ul>
<h2 id="적용">적용</h2>
<h3 id="서비스-인터페이스-생성">서비스 인터페이스 생성</h3>
<pre><code class="language-java">public interface MyService {
    int calculate(int input1, int input2);
}</code></pre>
<h3 id="서비스-인터페이스-구현">서비스 인터페이스 구현</h3>
<pre><code class="language-java">public class MyServiceImpl implements MyService {
    @LogIO
    @LogExecutionTime
    public int calculate(int input1, int input2) {
        return input1 + input2;
    }
}</code></pre>
<ul>
<li>이 때 어노테이션을 붙여서 로깅</li>
</ul>
<h3 id="주입">주입</h3>
<pre><code>MyService service = LoggingProxy.of(MyServiceImpl.class);
service.calculate(3, 5);</code></pre><p>이 코드에서 구현하려고하는 클래스의 프록시가 생성되어 service에 할당됨. 
service가 함수를 호출하면 확장해둔 <code>InvocationHandler</code>와 오버라이딩해둔 <code>invoke</code> 메서드를 통해 그 흐름을 가로챔. 
어노테이션이 있다면 로깅처리</p>
<h2 id="결과-예시">결과 예시</h2>
<pre><code class="language-bash">[LOG] 함수 호출: calculate(3, 5) -&gt; 8
[LOG] calculate 실행 시간: 3.596ms</code></pre>
<p>결과적으로 최상위 클래스에 프록시를 생성해두면 어노테이션만 달아도 자동 로깅</p>
<blockquote>
<p>완전 럭키비키잖아?</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 상속과 다형성]]></title>
            <link>https://velog.io/@dev_hogun/Java-%EC%83%81%EC%86%8D%EA%B3%BC-%EB%8B%A4%ED%98%95%EC%84%B1</link>
            <guid>https://velog.io/@dev_hogun/Java-%EC%83%81%EC%86%8D%EA%B3%BC-%EB%8B%A4%ED%98%95%EC%84%B1</guid>
            <pubDate>Sun, 13 Jul 2025 13:17:25 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-상황">문제 상황</h3>
<p>강의를 담아 가격을 계산하는 로직에서 각 유형별로 특별한 로직(프로모션)을 부여하고자 한다. </p>
<pre><code class="language-java">public enum LectureType {
    DevOps(&quot;DevOps&quot;), DBMS(&quot;DBMS&quot;), Lang(&quot;Lang&quot;), FW(&quot;F/W&quot;), CS(&quot;CS&quot;);
    ...
}</code></pre>
<p>다음과 같이 유형은 Enum으로 구현된 상태였고, 각 로직은 모두 &quot;할인을 적용한다&quot;는 측면에서 같은 역할을 가졌으나 개별적인 로직이 필요했다. </p>
<table>
<thead>
<tr>
<th>유형</th>
<th>프로모션</th>
</tr>
</thead>
<tbody><tr>
<td>DevOps</td>
<td>모든 강의 10% 할인</td>
</tr>
<tr>
<td>DBMS</td>
<td>강의 마다 5,000원 할인</td>
</tr>
<tr>
<td>Lang</td>
<td>2개 이상 구매 시 가장 가격이 저렴한 강의 하나 무료</td>
</tr>
<tr>
<td>F/W</td>
<td>구매 금액이 90,000원을 넘을 경우 30,000원 할인</td>
</tr>
<tr>
<td>CS</td>
<td>강의 3과목 이상 구매 시 강의 30% 할인</td>
</tr>
</tbody></table>
<h3 id="srp와-다형성">SRP와 다형성</h3>
<p>SRP(Single Responsibility Principle)는 하나의 객체는 하나의 책임만을 가져야 한다는 의미이다. 이들은 모두 &quot;강의&quot;라는 틀로 묶일 뿐더러  프로모션을 적용하는 상황에서는 열거를 표현하는 Enum에게 그 책임을 지우는 것은 과도할 것이다. 하지만 만약 이들을 관련짓지 않고 다른 클래스로 두기에는 높은 연관성을 지닌다. 이와 관한 문제를 해결하기 위해 다형성의 개념을 적용해 볼 수 있다. 다형성은 하나의 형식에 다양한 구현을 대입할 수 있음을 의미하며 상속 등을 통해 구현이 가능하다. 이 경우에도 프로모션을 적용해 계산한다는 행동의 형식은 같지만 실제적인 구현은 상이한 형태를 지닌다. 이에 상속을 통해 해당 문제를 해결했다. </p>
<h3 id="해결-방법">해결 방법</h3>
<p>Lecture에 계산하는 로직을 두고 클래스를 상속받아 각 유형의 강의를 구현했다. </p>
<pre><code class="language-java">public class Lecture {
    int id;
    String name;
    LectureType type;
    int cost;
    ...
    public int calculateCost(List&lt;Lecture&gt; lectures) {
        return lectures.stream()
                .map(Lecture::getCost)
                .reduce(0, Integer::sum);
    }
    ...
}</code></pre>
<pre><code class="language-java">public class CSLecture extends Lecture {
    public CSLecture() {
        super();
    }

    public CSLecture(int id, String name, LectureType type, int cost) {
        super(id, name, type, cost);
    }

    @Override
    public int calculateCost(List&lt;Lecture&gt; lectures) {
        int price = lectures.stream()
                .map(Lecture::getCost)
                .reduce(0, Integer::sum);
        if (lectures.size() &gt;= 3) {
            return (int) (price * 0.7);
        }
        return price;
    }
}</code></pre>
<pre><code class="language-java">package mission.application.domain.lecture;

import java.util.Comparator;
import java.util.List;
import mission.application.domain.Lecture;
import mission.application.domain.enums.LectureType;

public class LangLecture extends Lecture {
    public LangLecture() {
        super();
    }

    public LangLecture(int id, String name, LectureType type, int cost) {
        super(id, name, type, cost);
    }

    @Override
    public int calculateCost(List&lt;Lecture&gt; lectures) {
        int price = lectures.stream()
                .map(Lecture::getCost)
                .reduce(0, Integer::sum);

        if (lectures.size() &gt;= 2) {
            price -= lectures.stream()
                    .min(Comparator.comparing(Lecture::getCost))
                    .get()
                    .getCost();
        }

        return price;
    }
}</code></pre>
<p>...
사실 Enum을 지우지는 않았다. 일단 구현해두고 리팩터링하면서 지우려고 했는데 아직도 리팩터링을 못했다...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Interface와 단위테스트]]></title>
            <link>https://velog.io/@dev_hogun/Java-Interface%EC%99%80-%EB%8B%A8%EC%9C%84%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@dev_hogun/Java-Interface%EC%99%80-%EB%8B%A8%EC%9C%84%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Mon, 19 May 2025 14:42:15 GMT</pubDate>
            <description><![CDATA[<h2 id="interface의-정의">Interface의 정의</h2>
<blockquote>
<p>추상화를 달성하기 위한 또다른 방식 (Another way to achieve abstraction)
속성 없이 관련된 메서드를 묶는데 쓰이는 완전한 추상 클래스 (Completely “abstract class”that is used to group related methods with empty bodies)</p>
</blockquote>
<p>출처 : <a href="https://www.w3schools.com/java/java_interface.asp">https://www.w3schools.com/java/java_interface.asp</a></p>
<p>즉, 인터페이스는 추상화를 목적으로 하며, 클래스의 메서드만을 명세한다. </p>
<h2 id="interface를-통한-추상화가-갖는-이점">Interface를 통한 추상화가 갖는 이점</h2>
<p>의존성 분리 - 인터페이스를 선언해 사용함으로써 인터페이스를 확장한 다른 클래스에 대한 의존성을 분리할 수 있다. </p>
<p>ex. 참가 자동차 이름과 라운드 수를 터미널을 통해서 입력받는 상황을 가정해볼 수 있다. 만약 입력을 터미널이 아닌 다른 방법으로 바꾼다면 (파일 읽기, 웹 요청 …) 이미 코드베이스는 터미널과의 의존관계를 갖기 때문에 코드베이스를 보며 다시 짜야하지만 인터페이스를 도입하면 다음과 같이 분리할 수 있다. </p>
<p>Interface</p>
<pre><code class="language-java">public interface GetGamePropertyUseCase {
    String getParticipantName();
    int getTurnCount();
}</code></pre>
<p>구현체</p>
<pre><code class="language-java">public class InputTerminal implements GetGamePropertyUseCase {
    public String getParticipantName() {
        return Console.readLine();
    }
    public int getTurnCount() {
        return Integer.parseInt(Console.readLine());
    }
}</code></pre>
<p>이렇게 구현되어있는 상황에서 만약 입력을 다른 방법으로 받고 싶다면 구현체만 갈아끼우면 된다. </p>
<h2 id="interface와-추상클래스가-갖는-차이">Interface와 추상클래스가 갖는 차이</h2>
<p>인터페이스를 추상화의 방법론으로 본다면 추상클래스 역시 그 대안이 될 수 있다. 추상클래스를 간단히 표현하자면 “미완성된 클래스” 정도로 요약할 수 있다. 클래스와 유사하게 필드와 메서드를 둘 수 있을 뿐더러 접근지정자까지 설정할 수 있다. 다른 점은 선언부만을 정의하는 추상 메서드를 둘 수 있기에 구현체와는 구분되며 인터페이스와 유사하게 구현된 코드가 없으면 무용지물이 된다. </p>
<p>이에 연역해서 둘의 구분적인 특징을 생각해볼 수 있다. 먼저 인터페이스는 일반적으로 메서드의 선언만 존재한다. 메서드는 클래스의 행동을 정의하는 것이기에 이는 순수하게 행동을 정의하는 역할을 갖게 된다. 반대로 추상클래스의 경우 추상 메서드를 갖기도 하지만 구현부가 포함되어 있다는 점을 고려했을 때 클래스의 정체성을 갖지만 일부 추상성을 허용한 꼴이며 순수하게 행동을 정의하는 인터페이스와는 구분된다. </p>
<h2 id="interface-사용-예시---다중-상속">Interface 사용 예시 - 다중 상속</h2>
<p>인터페이스는 행동을 정의한다는 점에서 클래스의 미구현체 보다는 클래스가 가져야할 행동을 선언한 것에 가깝다고 볼 수 있다. 비슷한 이유로 자바에서 클래스는 다중 상속이 불가능하지만 인터페이스는 가능하다. 이에 대한 일례로 <code>ArrayList</code> 컬렉션을 들 수 있다. ArrayList의 선언부는 다음과 같다. </p>
<pre><code class="language-java">public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt;
        implements List&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable</code></pre>
<p>ArrayList는 AbstractList라는 추상 클래스를 상속받아 구현된 구현체이며, 그 과정에서 List, RandomAccess, Cloneable, Serializable 등의 인터페이스를 상속받는다. 이를 통해 ArrayList는 리스트처럼 행동할 수 있으며, 랜덤액세스, 복제, 직렬화 등의 행동을 수행할 수 있음을 예상할 수 있다. 하지만 구현된 것이 아니기에 해당 행동들을 ArrayList에 맞게 구현할 수 있다. 이는 Interface를 통해 유연한 확장이 가능해진 사례로 볼 수 있을 것이다. </p>
<h2 id="interface-사용-예시---의존성-분리를-통한-단위-테스트-작성">Interface 사용 예시 - 의존성 분리를 통한 단위 테스트 작성</h2>
<p>클래스 사이에 의존성을 갖는다는 것은 두 클래스가 서로 영향을 미칠 수 있다는 의미이며 단위 테스트는 프로그램을 작은 단위로 떼어내 테스트하는 것을 말한다. 의존성은 클래스가 작아질 수 없게 만든다. A와 B가 의존성을 가진다면 A와 B는 더이상 떼어낼 수 없는 관계가 되기 때문이다. 반대로 단위테스트는 작아야만 의미를 가진다. 작은 단위에서 예상대로 실행되는지를 확인하는 것이 단위테스트이기 때문이다. 그렇기에 클래스의 관계가 유연해야 단위테스트가 용이해진다.</p>
<p>가령 위에서는 인터페이스를 통해 터미널과 입력 로직의 의존성을 분리했다. 만약 의존성이 분리되지 않은 상황에서 단위테스트의 상황에서 입력을 한 상황은 가정할 수 없다. 매 테스트마다 직접 입력할 수 없기 때문이다. 하지만 다음과 같이 입력 로직을 구현한다면 테스트 상황에서도 마치 입력이 발생했다고 가정한 채로 테스트를 진행할 수 있다. </p>
<pre><code class="language-java">public class InputMock implements GetGamePropertyUseCase {
        private final String name;
        private final int round;
        public InputMock(String name, int round) {
                this.name = name;
                this.round = round;
        }
    public String getParticipantName() {
        return name;
    }
    public int getTurnCount() {
        return round;
    }
}</code></pre>
<pre><code class="language-java">//이름이 Moongua이고 3라운드를 진행한다고 가정하고 테스트를 진행할 때
GetGamePropertyUseCase input = new InputMock(&quot;Moongua&quot;, 3);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[56회 SQLD 합격 회고]]></title>
            <link>https://velog.io/@dev_hogun/%EC%A0%9C56%ED%9A%8C-SQLD-%ED%95%A9%EA%B2%A9-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dev_hogun/%EC%A0%9C56%ED%9A%8C-SQLD-%ED%95%A9%EA%B2%A9-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 02 Apr 2025 07:23:13 GMT</pubDate>
            <description><![CDATA[<h3 id="동기">동기</h3>
<p>올해 초, 사회복무요원으로 복무를 시작하며 전역하기 전에 자격증은 따두겠다는 막연한 생각으로 SQLD를 신년 계획에 넣어두었다. 그리고 막무가내로 친구들을 꼬득였다. </p>
<img src="https://velog.velcdn.com/images/dev_hogun/post/e2301c07-f41b-47e9-9f08-f9378cc16072/image.png" style="width: 100%; max-width: 300px; height: auto;" />

<p>그리고 <del>친구들의 급발진으로</del> 아주 쉽게 스터디는 성사됐다. </p>
<div style="display: flex; justify-content: center; gap: 16px; flex-wrap: wrap;">
  <div style="flex: 1 1 300px; max-width: 300px;">
    <img
      src="https://velog.velcdn.com/images/dev_hogun/post/374273ff-3f09-4291-91aa-b91ce9e4bdd2/image.png"
      style="width: 100%; height: auto; display: block;"
    />
  </div>
  <div style="flex: 1 1 300px; max-width: 300px;">
    <img
      src="https://velog.velcdn.com/images/dev_hogun/post/0157ea00-6cc9-4deb-99e1-df35f45e70f7/image.png"
      style="width: 100%; height: auto; display: block;"
    />
  </div>
</div>

<p>물론 스터디에서 공부를 하지는 않았다. 그냥 같이 자격증 딸 사람을 모았을 뿐. 하지만 재밌었죠? 그 덕에 자격증을 딸 수밖에 없었다.  </p>
<h3 id="timeline">Timeline</h3>
<ul>
<li><p>시작 전 상태
SQLD를 막무가내로 써본 경험이 있다. 학부 과정에서 DB를 배운 적도 없었지만 플젝 때문에 알지도 못한채로 써본게 지식의 전부였다. SQLD를 목표로 둔 이유도 SQL을 좀 더 체계적으로 정리하고 싶어서 였다. </p>
</li>
<li><p>2/1 - 책을 샀다. 
이후 친절한 SQL 튜닝이라는 책을 사서 한 100p 좀 넘게 읽었다. 내용은 되게 좋다고 생각했지만 <del>내 능지로</del> 흡수하기 힘든 내용이었다.</p>
<img src="https://velog.velcdn.com/images/dev_hogun/post/2160fc6d-d294-465b-b076-d09a53542e45/image.png" style="width: 100%; max-width: 300px; height: auto;"/>


</li>
</ul>
<ul>
<li><p>2/3 시험을 접수했다. 
SQLD는 시험장이 거의 광역시밖에 없어서 시골에서 복무중인 입장에서는 당황스러웠다. 어쩔 수 없이 대구로 신청했다. </p>
<img src="https://velog.velcdn.com/images/dev_hogun/post/2d48382e-a5a5-4bac-a4a4-6bd2aa1e394c/image.png" style="width: 100%; max-width: 300px; height: auto;" />
</li>
<li><p>2/10 드디어 SQLD 공부를 시작했다. 
<a href="https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-SQLD-1%EA%B3%BC%EB%AA%A9-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EB%A7%81%EC%9D%98-%EC%9D%B4%ED%95%B4">SQLD 1과목</a>과 <a href="https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-SQLD-2%EA%B3%BC%EB%AA%A9-SQL-%EA%B8%B0%EB%B3%B8-%EB%B0%8F-%ED%99%9C%EC%9A%A9">SQLD 2과목</a>을 정리했다. 하지만 10일부터 공부했다기에는 2과목을 24일에 정리했다. 사실상 이때부터 공부한거나 다름없다. </p>
</li>
<li><p>2/26 노랭이 푼걸 오답정리했다. 
<a href="https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-%EB%85%B8%EB%9E%AD%EC%9D%B4-%EC%98%A4%EB%8B%B5%EB%85%B8%ED%8A%B8-%EA%B3%BC%EB%AA%A9-1-%EC%A0%9C-1%EC%9E%A5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EB%A7%81%EC%9D%98-%EC%9D%B4%ED%95%B4">1과목 1장</a>, <a href="https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-%EB%85%B8%EB%9E%AD%EC%9D%B4-%EC%98%A4%EB%8B%B5%EB%85%B8%ED%8A%B8-%EA%B3%BC%EB%AA%A9-1-%EC%A0%9C-2%EC%9E%A5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EA%B3%BC-SQL">1과목 2장</a>, <a href="https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-%EB%85%B8%EB%9E%AD%EC%9D%B4-%EC%98%A4%EB%8B%B5%EB%85%B8%ED%8A%B8-%EA%B3%BC%EB%AA%A9-2-%EC%A0%9C-1%EC%9E%A5-SQL-%EA%B8%B0%EB%B3%B8">2과목 1장</a>까지 정리했다. 2-1은 3월 6일에 정리했다. 굳이 오답 정리를 한 이유는 문서화하고 공유하고 싶은 욕심도 있었지만 혹시나 SQLP를 칠 때 도움이 되지 않을까 하는 생각에서였다. 2과목 2, 3장도 풀었지만 시험 전에 정리까지 하지는 못했다. </p>
</li>
<li><p>3/8 시험을 쳤다. 
버스 시간 이슈로 6시쯤에 일어나서 출발했다. </p>
<img src="https://velog.velcdn.com/images/dev_hogun/post/bb11d743-9124-4c8c-ba3f-c95a8a1903d2/image.png" style="width: 100%; max-width: 800px; height: auto;"/>
<div style="display: flex; justify-content: center; gap: 16px; flex-wrap: wrap;">
<div style="flex: 1 1 300px; max-width: 300px;">
  <img
    src="https://velog.velcdn.com/images/dev_hogun/post/ddebf288-abba-4cad-bf1b-675db4a77f8d/image.png"
    style="width: 100%; height: auto; display: block;"
  />
</div>
<div style="flex: 1 1 300px; max-width: 300px;">
  <img
    src="https://velog.velcdn.com/images/dev_hogun/post/8f38d480-6fe2-4c9f-9a73-7e78b5928070/image.png"
    style="width: 100%; height: auto; display: block;"
  />
</div>
</div>
고등학생이 된 기분을 만끽하며 들어가니 9시가 채 되지 않았다. 혹시 몰라서 그간 작성한 벨로그를 출력해갔지만 ~~카톡하면서 키득거리고 있느라~~ 별 의미는 없었다. 
10시에 시험은 시작됐다. 시험 내용은 생각보다 어렵다고 느꼈다. 문제를 만만하게 생각하고 천천히 풀기 시작했어서 그런지 시간도 채워서 풀었다. 다 풀고 대충 계산을 해 보니까 푼 문제는 34문제, 적당히 찍어서 푼 문제는 15문제, 감도 못잡은 문제 1문제 정도였다. 밖에서는 이번 시험이 너무 쉽게 나왔다고 떠드는 사람이 많았는데 나는 푼 문제가 맞을거라는 확신이 없으니 시험이 끝나고 나서 5월에 다시 봐야하나 걱정하고 있었다. </li>
<li><p>3/28 성적 사전 공개
안정적으로 합격 점수가 나왔다. 같이 준비한 3명도 합격이라고 한다. 
<img src="https://velog.velcdn.com/images/dev_hogun/post/a71baf9e-80c3-430c-b3eb-14f1e0dcb4a0/image.png" alt=""></p>
</li>
</ul>
<h3 id="결론">결론</h3>
<ol>
<li>준비하는데 2주면 충분하다. </li>
<li>노랭이처럼 복잡한 SQL문은 출제되지 않는다. </li>
<li>개념을 모르면 유튜브에 무료로 제공하는 인강만으로 충분하다. </li>
<li>개념을 알고 있다면 노랭이만 풀어도 충분하다. <ul>
<li>노랭이를 한 번 풀어보면 적어도 개념은 60점 맞을 만큼 익혀지기에 개인적인 생각으로는 2회독까지 필요없다고 생각한다. </li>
<li>SQL문과 친하지 않다면 <a href="https://leetcode.com/studyplan/top-sql-50/">리트코드의 문제</a>를 풀면 된다.  </li>
</ul>
</li>
</ol>
<p>+ 7만원(응시료 + 노랭이) 정도 비용이 발생한다.</p>
<blockquote>
<h4 id="공부를-하고-나서-드는-생각은">공부를 하고 나서 드는 생각은...</h4>
<p>자격증 취득이라는 목적만을 갖고 공부를 한다면 배울게 별로 없는 자격증이라고 생각한다. 하지만 SQL과 DB에 대한 기초적인 내용을 담고 있고 이를 찬찬히 배워볼 수 있기에 배움에 초점을 맞춰 공부한다면 배울 부분은 분명히 있다고 생각한다. 적어도 지식도, 기준도 없이 DB를 다루던 나에게는 관련 내용을 정리해볼 수 있었던 기회가 되었다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/36e8d750-b33c-407a-b7c3-b5b539e18648/image.png" alt=""></p>
<h3 id="사족蛇足">사족(蛇足)</h3>
<p>올해 초에 한 활동 중 유일하게 눈에 보이는 성과인 것 같다. 모든 일이 그렇겠지만 배운 점이 있는 만큼 반대 급부로 아쉬운 점도 있었다. 가장 아쉬웠던 점은 내가 무엇을 아는지 모르는지, 아니면 잘못 아는지를 분명히 모른다는 점이었다. </p>
<p>자바를 공부하며 &quot;힙 오염&quot;이라는 개념을 접하게 되었다. 대충 자바에서 인스턴스를 저장하는 힙 영역에 제네릭을 사용하며 잘못된 타입에 저장되는 문제이다. 내가 의문이었던 오염이라고 하면 &quot;다른 것으로 물들었다&quot;는 뜻이라고 하는데 바뀐게 단 한 인스턴스일지도 모르는데 왜 &quot;오염&quot;이라는 이름을 붙이는가였다. 곰곰히 생각해본 결과 내가 내린 결론은 정작 잘못 저장된게 단 하나의 인스턴스일지라도 그것 때문에 &quot;힙에서 안전하게 데이터를 다룰 수 있다&quot;는 믿음이 깨졌기 때문에 잘못 저장되지 않은 인스턴스일지언정 믿고 꺼낼 수 없는 데이터가 된다는, 즉 이를테면 하나의 데이터로 말미암아 힙 영역 전체에 데이터의 불안정성이 확산되었기에 이를 오염이라고 칭한다는 결론을 내렸다. </p>
<p>그런데 이런 개념이라면 우리가 공부를 할 때도 &quot;오염&quot;이라는 표현을 적용해볼 수 있지 않을까 싶다. 문제를 풀 때 내가 아는 내용이라면 아는 지식을 기반으로 쉽게 문제를 풀 수 있다. 모르는 내용이라면 문제를 풀 수는 없겠지만 적어도 내가 풀 수 없다는 사실은 단정할 수 있다. 하지만 내가 알고있는 내용이 정확하지 않을 수 있다면, 즉 내 지식이 오염되었다면 문제를 풀었는지, 불 수 없는지조차 판단할 수 없다. 결론적으로는 모르는 것과 다르지 않은, 아니면 오히려 모르는 것만 못한 상황을 마주하게 된다. </p>
<p>힙 오염의 해결법은 raw type을 함부로 사용하지 않고 타입을 분명히 명시하는 것이다. 타입을 분명히 알기에 이것이 타입 안정성을 가지고 있을지 걱정할 이유가 없다. 위의 적용에 맞춰 해석해보면 지식을 습득할 때 대충 넘겨짚으며, 맞는 대로 공부할게 아니라 기본적이고 기초적인 내용부터, 차근차근 확실하게 공부해 나가야 지식의 &quot;오염&quot;을 피할 수 있을 것이다. 그래서 사람들이 개념의 중요성을 그렇게 강조하나보다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQLD 학습 정리] 노랭이 오답노트 과목 2, 제 1장 - SQL 기본
]]></title>
            <link>https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-%EB%85%B8%EB%9E%AD%EC%9D%B4-%EC%98%A4%EB%8B%B5%EB%85%B8%ED%8A%B8-%EA%B3%BC%EB%AA%A9-2-%EC%A0%9C-1%EC%9E%A5-SQL-%EA%B8%B0%EB%B3%B8</link>
            <guid>https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-%EB%85%B8%EB%9E%AD%EC%9D%B4-%EC%98%A4%EB%8B%B5%EB%85%B8%ED%8A%B8-%EA%B3%BC%EB%AA%A9-2-%EC%A0%9C-1%EC%9E%A5-SQL-%EA%B8%B0%EB%B3%B8</guid>
            <pubDate>Thu, 06 Mar 2025 03:52:35 GMT</pubDate>
            <description><![CDATA[<h3 id="6-아래-내용">6. 아래 내용<img src="https://velog.velcdn.com/images/dev_hogun/post/57847415-1931-44d9-b7e9-a4edefdb0a46/image.png" alt=""></h3>
<p>의 범주에 해당하는 SQL 명령어로 가장 적절하지 <u>않은</u> 것은?</p>
<pre><code>테이블의 구조를 생성, 변경, 삭제하는 등 데이터 구조를 정의하는 데 사용되는 명령어이다. </code></pre><p>1️⃣ CREATE
2️⃣ GRANT
3️⃣ ALTER
4️⃣ DROP</p>
<p>📌 개념
SQL 문장 종류</p>
<ul>
<li>DML(데이터 조작어) : 값을 조작하는 명령어<ul>
<li>ex) <code>SELECT</code>, <code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code></li>
</ul>
</li>
<li>DDL(데이터 정의어) : 구조를 정의하는 명령어<ul>
<li>ex) <code>CREATE</code>, <code>ALTER</code>, <code>DROP</code>, <code>RENAME</code></li>
</ul>
</li>
<li>DCL(데이터 제어어 : 접근 및 권한을 관리하는 명령어<ul>
<li>ex) <code>GRANT</code>, <code>REVOKE</code></li>
</ul>
</li>
<li>TCL(트랜잭션 제어어) : 트랜잭션을 관리(제어)하는 명령어<ul>
<li>ex) <code>COMMIT</code>, <code>ROLLBACK</code></li>
</ul>
</li>
</ul>
<p>✅ 해설
2️⃣ (정답) <code>GRANT</code>는 DCL에 해당한다. </p>
<hr>
<h3 id="9-아래-sql의-수행-결과로-가장-적절한-것은">9. 아래 SQL의 수행 결과로 가장 적절한 것은?</h3>
<pre><code class="language-SQL">[SQL]
SELECT SUM(COL2) + SUM(COL3) FROM TAB_A;
SELECT SUM(COL2) + SUM(COL3) FROM TAB_A WHERE COL1 &gt; 0;
SELECT SUM(COL2) + SUM(COL3) FROM TAB_A WHERE COL1 IS NOT NULL;
SELECT SUM(COL2) + SUM(COL3) FROM TAB_A WHERE COL1 IS NULL;

[TAB_A]
------------------
COL1   COL2   COL3
30     NULL   20
NULL   50     10
0      10     NULL
------------------</code></pre>
<p>1️⃣ 60, NULL, 30, 60
2️⃣ 60, 20, 30, 60
3️⃣ 90, NULL, 30, 60
4️⃣ 90, 20, 30, 60</p>
<p>📌 개념</p>
<ul>
<li>NULL은 일반적으로 연산에서 제외된다. </li>
<li>SUM에서 연산 대상 값이 없다면 (모두 NULL이라면) NULL을 반환한다. </li>
</ul>
<p>✅ 해설
첫 번째 줄</p>
<ul>
<li>SUM(COL2) = 60, SUM(COL3) = 30, SUM(COL2) + SUM(COL3) = 90
두 번째 줄</li>
<li><code>COL1 &gt; 1</code>의 경우) 0은 조건을 충족하지 않음, NULL은 연산에서 제외</li>
<li>SUM(COL2) = NULL, SUM(COL3) = 20, SUM(COL2) + SUM(COL3) = NULL
세 번째 줄</li>
<li><code>COL1 IS NOT NULL</code>의 경우) NULL을 제외한 두 행 조건 충족</li>
<li>SUM(COL2) = 10, SUM(COL3) = 20, SUM(COL2) + SUM(COL3) = 30
네 번째 줄</li>
<li>COL1 IS NULL의 경우) COL1이 NULL인 두번째 열만 조건 충족</li>
<li>SUM(COL2) = 50, SUM(COL3) = 10, SUM(COL2) + SUM(COL3) = 60</li>
</ul>
<p>3️⃣ 90, NULL, 30, 60</p>
<hr>
<h3 id="11-아래에-대한-설명으로-가장-적절한-것은">11. 아래에 대한 설명으로 가장 적절한 것은?</h3>
<pre><code class="language-SQL">CREATE TABLE 서비스
(
    서비스번호 VARCHAR2(10) PRIMARY KEY,
    서비스명 VARCHAR2(100) NULL,
    개시일자 DATE NOT NULL
);

[SQL]
ㄱ. SELECT * FROM 서비스 WHERE 서비스번호 = 1;
ㄴ. INSERT INTO 서비스 VALUES (&#39;999&#39;, &#39;&#39;, &#39;2015-11-11&#39;);
ㄷ. SELECT * FROM 서비스 WHERE 서비스명 = &#39;&#39;;
ㄹ. SELECT * FROM 서비스 WHERE 서비스명 IS NULL;</code></pre>
<p>1️⃣ 서비스번호 칼럼의 레코드 중 &#39;001&#39;과 같은 숫자 형식으로 된 레코드가 하나라도 입력되어 있다면 ㄱ은 오류없이 실행된다. 
2️⃣ 오라클에서 ㄴ과 같이 데이터를 입력하였을 때, 서비스명 칼럼에 공백문자 데이터가 입력된다. 
3️⃣ 오라클에서 ㄴ과 같이 데이터를 입력하고 ㄷ과 같이 조회하였을 때, 데이터는 조회된다. 
4️⃣ SQL Server에서 ㄴ과 같이 데이터를 입력하고 ㄹ과 같이 조회하였을 때, 데이터는 조회되지 않는다. </p>
<p>📌 개념
Oracle의 VARCHAR2에서는 <code>&#39;&#39;</code>(Empty String)이 NULL로 변환됨
&lt;-&gt; SQL Server에서는 <code>&#39;&#39;</code>(Empty String)이 NULL을 의미하지는 않음
Oracle에서 VARCHAR2를 숫자와 비교할 때 자동으로 숫자로 변환</p>
<ul>
<li><code>서비스번호 = 1</code>와 <code>TO_NUMBER(서비스번호) = 1</code>는 현재 맥락에서 같은 의미</li>
</ul>
<p>✅ 해설
1️⃣ &quot;하나라도 입력되어 있다면&quot; -&gt; 모든 값이 숫자로 변환가능한 형태여야 함
2️⃣ Empty String이 아닌 NULL이 입력됨
3️⃣ NULL은 비교 연산 결과가 NULL임 -&gt; 연산에서 제외
4️⃣ (정답) SQL Server에서 Empty String과 NULL은 다른 의미를 가짐</p>
<hr>
<h3 id="14-group-by-절과-having-절에-대한-설명으로-가장-적절한-것은">14. GROUP BY 절과 HAVING 절에 대한 설명으로 가장 적절한 것은?</h3>
<p>1️⃣ 집계 함수의 통계 정보는 NULL 값을 가진 행을 포함하여 수행한다. 
2️⃣ GROUP BY 절에서는 SELECT 절과 같이 ALIAS 명을 사용할 수 있다. 
3️⃣ 집계 함수는 WHERE 절에도 올 수 있다. 
4️⃣ HAVING 절은 일반적으로 GROUP BY 절 뒤에 위치한다. </p>
<p>📌 개념</p>
<p>✅ 해설
1️⃣ NULL을 제외하고 수행
2️⃣ GROUP BY 절은 ALIAS를 갖지 않음 (GROUP BY가 SELECT보다 먼저 실행됨, HAVING도 마찬가지)
3️⃣ 집계 함수는 WHERE 절 이후에 올 수 있음</p>
<ul>
<li>WHERE은 개별 행을 필터링하는 기능, 집계 함수는 WHERE을 통해 필터링된 데이터로 집계 (그래서 HAVING을 사용 - 그룹을 필터링)
4️⃣ (정답)</li>
</ul>
<hr>
<h3 id="17-아래의-가와-나가-동일한-결과를-출력한다고-할-때-빈칸-ㄱ-에-들어갈-내용으로-가장-적절한-것은-단-스칼라-서브쿼리는-제외함">17. 아래의 (가)와 (나)가 동일한 결과를 출력한다고 할 때, 빈칸 ㄱ 에 들어갈 내용으로 가장 적절한 것은? (단, 스칼라 서브쿼리는 제외함)</h3>
<pre><code class="language-SQL">(가)
SELECT LOC,
    CASE WHEN LOC = &#39;NEW YORK&#39; THEN &#39;EAST&#39;
    ELSE &#39;ETC&#39;
    END as AREA
(나)
SELECT LOC,
    ( ㄱ ) as AREA
FROM DEPT;</code></pre>
<p>1️⃣ <code>CASE WHEN LOC IS &#39;NEW YORK&#39; THEN &#39;EAST&#39; ELSE &#39;ETC&#39; END</code>
2️⃣ <code>CASE LOC WHEN &#39;NEW YORK&#39; THEN &#39;EAST&#39; ELSE &#39;ECT&#39; END</code>
3️⃣ <code>CASE LOC WHEN &#39;NEW YORK&#39; THEN &#39;EAST&#39; DEFAULT &#39;ECT&#39; END</code>
4️⃣ <code>DECODE (LOC, &#39;EAST&#39;, &#39;NEW YORK&#39;, &#39;ECT&#39;)</code></p>
<p>📌 개념
CASE</p>
<pre><code class="language-SQL">CASE
    WHEN 조건1 THEN 결과1
    WHEN 조건2 THEN 결과2
    ELSE 기본값
END</code></pre>
<p>DECODE</p>
<pre><code class="language-SQL">DECODE(컬럼, 조건1, 결과1, 조건2, 결과2, 기본값)</code></pre>
<p>✅ 해설
1️⃣ <code>IS</code>라는 연산자는 없음 (<code>IS NULL, IS TRUE</code> 등 제외)
2️⃣ LOC 컬럼을 기준으로, 값이 &#39;NEW YORK&#39;이라면 &#39;EAST&#39;로, 그 외는 &#39;ECT&#39;
3️⃣ <code>DEFAULT</code>가 아니라 <code>ELSE</code>가 맞는 표현
4️⃣ 이 구문은 LOC이 &#39;EAST&#39;일 때, &#39;NEW YORK&#39;으로 바꾸라는 의미</p>
<hr>
<h3 id="21-실행-결과가-다른-하나는">21. 실행 결과가 다른 하나는?</h3>
<p>1️⃣ </p>
<pre><code class="language-SQL">SELECT DNAME, LOC, DEPTNO
    FROM DEPT
    ORDER BY DNAME, LOC, 3 DESC;</code></pre>
<p>2️⃣ </p>
<pre><code class="language-SQL">SELECT DNAME, LOC AREA, DEPTNO
    FROM DEPT
    ORDER BY DNAME, AREA, DEPTNO DESC;</code></pre>
<p>3️⃣ </p>
<pre><code class="language-SQL">SELECT DNAME, LOC AREA, DEPTNO
    FROM DEPT
    ORDER BY 1, AREA, 3 DESC;</code></pre>
<p>4️⃣ </p>
<pre><code class="language-SQL">SELECT DNAME DEPT, LOC AREA, DEPTNO
    FROM DEPT
    ORDER BY DEPT DESC, LOC, 3 DESC;</code></pre>
<p>📌 개념
ALIAS</p>
<ul>
<li>AS를 통해 별칭 부여 가능 ``</li>
<li>특정 DB에서는 생략 가능
ORDER BY에서 숫자는 해당 인덱스에 있는 항목을 의미함</li>
</ul>
<p>✅ 해설
1️⃣ DNAME - ASC, LOC - ASC, DEPTNO - DESC
2️⃣ DNAME - ASC, LOC - ASC, DEPTNO - DESC
3️⃣ DNAME - ASC, LOC - ASC, DEPTNO - DESC
4️⃣ (정답) DNAME - DESC, LOC - ASC, DEPTNO - DESC</p>
<hr>
<h3 id="24-sql의-실행-결과로-가장-적절하지-u않은u-것은">24. SQL의 실행 결과로 가장 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ ROUND(4.875, 2) = 4.88
2️⃣ LENGTH(&#39;KOREAN&#39;) = 6
3️⃣ DATE_FORMAT(&#39;2022-11-02&#39;, &#39;%Y-%m-%d&#39;) = 2022-11-02
4️⃣ SUBSTR(&#39;Gangneung Wonju&#39;, 8, 4) = &#39;g Wo&#39;</p>
<p>✅ 해설
1️⃣ ROUND(N, M)은 M째 자리까지 반올림 -&gt; ROUND(4.875, 2) = 4.88
4️⃣ SUBSTR에서 인덱스는 1부터 시작 -&gt; SUBSTR(&#39;Gangneung Wonju&#39;, 8, 4) = &#39;ng W&#39;</p>
<hr>
<h3 id="29-오류가-발생하는-sql은">29. 오류가 발생하는 SQL은?</h3>
<p>1️⃣ </p>
<pre><code class="language-SQL">SELECT 지역, SUM(매출금액) AS 매출금액
    FROM 지역별매출
    GROUP BY 지역
    ORDER BY 매출금액 DESC;</code></pre>
<p>2️⃣ </p>
<pre><code class="language-SQL">SELECT 지역, 매출금액
    FROM 지역별매출
    ORDER BY 년 ASC;</code></pre>
<p>3️⃣ </p>
<pre><code class="language-SQL">SELECT 지역, SUM(매출금액) AS 매출금액
    FROM 지역별매출
    GROUP BY 지역
    ORDER BY 년 DESC;</code></pre>
<p>4️⃣ </p>
<pre><code class="language-SQL">SELECT 지역, SUM(매출금액) AS 매출금액
    FROM 지역별매출
    GROUP BY 지역
    HAVING SUM(매출금액) &gt; 1000
    ORDER BY COUNT(*) ASC;</code></pre>
<p>📌 개념
SQL 구문의 실행순서</p>
<ul>
<li>FROM + JOIN -&gt; ON -&gt; WHERE -&gt; GROUP BY -&gt; HAVING -&gt; SELECT -&gt; ORDER BY</li>
</ul>
<p>✅ 해설
3️⃣ ORDER BY는 SELECT 이후에 실행</p>
<ul>
<li>SELECT는 필터링 기능, 즉 SELECT를 거친 후에는 &#39;지역&#39;과 &#39;매출금액&#39; 속성만 남음</li>
<li>SELECT에는 &#39;년&#39;이 존재하지 않음</li>
</ul>
<p>-&gt; 정렬 불가</p>
<hr>
<h3 id="37-순수-관계-연산자로-가장-적절하지-u않은u-것은">37. 순수 관계 연산자로 가장 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ SELECT
2️⃣ UPDATE
3️⃣ JOIN
4️⃣ DIVIDE</p>
<p>📌 개념
순수 관계 연산자</p>
<ul>
<li>릴레이션 간의 연산을 정의하는 이론적 개념</li>
<li>SELECT, JOIN, UPDATE ...
cf. DIVIDE는 SQL 표준 연산이 아님</li>
</ul>
<p>✅ 해설
4️⃣ (정답)</p>
<hr>
<h3 id="38-아래를-참고할-때-가장-적절한-sql은">38. 아래를 참고할 때 가장 적절한 SQL은?</h3>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/3afe05c5-0e8e-4b38-8e5d-69f50d01ffde/image.jpg" alt=""></p>
<p>[설명]
우리는 매일 배치작업을 통하여 고객에게 추천할 컨텐츠를 생성하고 고객에게 추천서비스를 제공한다. 
추천컨텐츠 에너티에서 언제 추천을 해야 하는지를 정의하는 추천대상일자가 있어 해당 일ㄹ자에만 컨텐츠를 추천해야 한다. 또한 고객이 컨텐츠를 추천받았을 때 선호하는 컨텐츠가 아닌 경우에는 고객이 비선호컨텐츠로 분류하여 더 이상 추천 받기를 원하지 않는다. 그러므로 우리는 비선호 컨텐츠 엔터티에 등록된 데이터에 대해서는 추천을 수행하지 않아야 한다. 
1️⃣ </p>
<pre><code class="language-SQL">SELECT C.컨텐츠ID, C.컨텐츠명
FROM 고객 A INNER JOIN 추천컨텐츠 B 
    ON (A.고객ID = B.고객ID) INNER JOIN 컨텐츠 C 
    ON (B.컨텐츠ID = C.컨텐츠ID)
WHERE A.고객ID = #custId#
    AND B.추천대상일자 = TO_CHAR(SYSDATE, &#39;YYYY.MM.DD&#39;)
    AND NOT EXISTS (
        SELECT X.컨텐츠ID
        FROM 비선호컨텐츠 X
        WHERE X.고객ID = B.고객ID);</code></pre>
<p>2️⃣ </p>
<pre><code class="language-SQL">SELECT C.컨텐츠ID, C.컨텐츠명
FROM 고객 A INNER JOIN 추천컨텐츠 B 
    ON (A.고객ID = #custId# AND A.고객ID = B.고객ID) INNER JOIN 컨텐츠 C 
    ON (B.컨텐츠ID = C.컨텐츠ID) RIGHT OUTER JOIN 비선호컨텐츠 D 
    ON (B.컨텐츠ID = D.컨텐츠ID AND B.컨텐츠ID = D.컨텐츠ID)
WHERE B.추천대상일자 = TO_CHAR(SYSDATE, &#39;YYYY.MM.DD&#39;)
    AND B.컨텐츠ID IS NOT NULL;</code></pre>
<p>3️⃣ </p>
<pre><code class="language-SQL">SELECT C.컨텐츠ID, C.컨텐츠명
FROM 고객 A INNER JOIN 추천컨텐츠 B 
    ON (A.고객ID = B.고객ID) INNER JOIN 컨텐츠 C 
    ON (B.컨텐츠ID = C.컨텐츠ID) LEFT OUTER JOIN 비선호컨텐츠 D 
    ON (B.컨텐츠ID = D.컨텐츠ID AND B.컨텐츠ID = D.컨텐츠ID)
WHERE A.고객ID = #custId#
    AND B.추천날짜 = TO_CHAR(SYSDATE, &#39;YYYY.MM.DD&#39;)
    AND D.컨텐츠ID IS NOT NULL;</code></pre>
<p>4️⃣ </p>
<pre><code class="language-SQL">SELECT C.컨텐츠ID, C.컨텐츠명
FROM 고객 A INNER JOIN 추천컨텐츠 B 
    ON (A.고객ID = #custId# AND A.고객ID = B.고객ID) INNER JOIN 컨텐츠 C 
    ON (B.컨텐츠ID = C.컨텐츠ID)
WHERE B.추천날짜 = TO_CHAR(SYSDATE, &#39;YYYY.MM.DD&#39;)
    AND NOT EXISTS (
        SELECT X.컨텐츠ID
        FROM 비선호컨텐츠 X
        WHERE X.고객ID = B.고객ID
        AND X.컨텐츠ID = B.컨텐츠ID
    );</code></pre>
<p>📌 개념
<code>AND NOT EXIST</code> : 특정 조건을 만족하는 서브쿼리가 존재하지 않을 때 TRUE를 반환</p>
<p>✅ 해설
1️⃣ <code>NOT EXIST</code>절에서 고객 ID 일치만 확인 -&gt; 비선호 컨텐츠가 있으면 모든 컨텐츠가 추천에서 배제
2️⃣ <code>RIGHT OUTER JOIN 비선호컨텐츠 D</code> &lt;- 비선호 컨텐츠 조회됨
3️⃣ LEFT OUTER JOIN + <code>D.컨텐츠ID IS NOT NULL</code> → 비선호 컨텐츠 조회됨
4️⃣ (정답) WHERE절에 비선호컨텐츠에 존재하지 않는 것을 쿼리</p>
<hr>
<h3 id="39아래에-대한-설명으로-가장-적절한-것은">39.아래에 대한 설명으로 가장 적절한 것은?</h3>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/eb5ec39c-3487-4f52-b359-d667a4887b4f/image.jpg" alt=""></p>
<p>1️⃣ 제품, 생산제품, 생산라인 엔터티를 INNER JOIN하기 위해서 생산제품 엔터티는 WHERE 절에 최소 3번 나타나야 한다. 
2️⃣ 제품과 생산라인 엔터티를 JOIN시 적절한 조인 조건이 없으므로 카티시안 곱(Cartesian Product)이 발생한다. 
3️⃣ 제품과 생산라인 엔터티에는 생산제품과 대응되지 않는 레코드는 없다. 
4️⃣ 특정 생산라인번호에서 생산되는 제품의 제품명을 알기 위해서는 제품, 생산제품, 생산라인까지 3개 엔터티의 INNER JOIN이 필요하다. </p>
<p>📌 개념</p>
<p>✅ 해설
1️⃣ 2번
2️⃣ (정답)
3️⃣ 대응되지 않는 레코드가 있을 수 있다. (0~N : 1로 대응됨)
4️⃣ 제품과 생산라인을 INNER JOIN 하면 됨 - 라인번호가 FK로 생산제품에 있으니까.</p>
<hr>
<h3 id="41-아래를-참고할-때-시간대별사용량-테이블을-기반으로-고객별-사용금액을-출력하는-sql로-가장-적절한-것은">41. 아래를 참고할 때 시간대별사용량 테이블을 기반으로 고객별 사용금액을 출력하는 SQL로 가장 적절한 것은?</h3>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/2cecd4d9-cc8b-40fd-9b51-a30432f7d56c/image.jpg" alt=""></p>
<p>1️⃣ </p>
<pre><code class="language-SQL">SELECT A.고객ID, A.고객명, SUM(B.사용량 * C.단가) AS 사용금액
FROM 고객 A INNER JOIN 시간대별사용량 B 
    ON (A.고객ID = B.고객ID) INNER JOIN 시간대구간 C 
    ON (B.사용시간대 &lt;= C.시작시간대 AND B.사용시간대 &gt;= C.종료시간대)
GROUP BY A.고객ID, A.고객명
ORDER BY A.고객ID, A.고객명;</code></pre>
<p>2️⃣ </p>
<pre><code class="language-SQL">SELECT A.고객ID, A.고객명, SUM(B.사용량 * C.단가) AS 사용금액
FROM 고객 A INNER JOIN 시간대별사용량 B INNER JOIN 시간대구간 C
    ON (A.고객ID = B.고객ID AND B.사용시간대 BETWEEN C.시작시간대 AND C.종료시간대)
GROUP BY A.고객ID, A.고객명
ORDER BY A.고객ID, A.고객명;</code></pre>
<p>3️⃣ </p>
<pre><code class="language-SQL">SELECT A.고객ID, A.고객명, SUM(B.사용량 * C.단가) AS 사용금액
FROM 고객 A 
INNER JOIN 시간대별사용량 B 
    ON (A.고객ID = B.고객ID) 
INNER JOIN 시간대구간 C 
    ON B.사용시간대 BETWEEN C.시작시간대 AND C.종료시간대
GROUP BY A.고객ID, A.고객명
ORDER BY A.고객ID, A.고객명;</code></pre>
<p>4️⃣ </p>
<pre><code class="language-SQL">SELECT A.고객ID, A.고객명, SUM(B.사용량 * C.단가) AS 사용금액
FROM 고객 A INNER JOIN 시간대별사용량 B 
    ON (A.고객ID = B.고객ID) BETWEEN JOIN 시간대구간 C
GROUP BY A.고객ID, A.고객명
ORDER BY A.고객ID, A.고객명;</code></pre>
<p>📌 개념</p>
<p>✅ 해설
1️⃣ BETWEEN으로 해야함 (화살표를 써도 가능하지만 방향이 맞지 않음)
2️⃣ 각 JOIN마다 ON절이 필요함 (A INNER JOIN B INNER JOIN C ON... 가 아니라, 적어도 갯수는 맞아야)
3️⃣ (정답)
4️⃣ BETWEEN JOIN이라는 구문은 없음</p>
<hr>
<h3 id="48-아래-sql의-실행-결과로-가장-적절한-것은">48. 아래 SQL의 실행 결과로 가장 적절한 것은?</h3>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/9fe7b0bc-08dd-455d-82d7-99d0280e9625/image.jpg" alt=""></p>
<p>📌 개념
LEFT OUTER JOIN이고 ON절은 C1 값이 같고 오른쪽 테이블의 C2가 1~3이어야 한다는 조건이다. 
TAB1을 순회하며...</p>
<ul>
<li>왼쪽 테이블 레코드 -(사유)&gt; 오른쪽 테이블 레코드</li>
<li>A, 1 -(A가 없음) -&gt; NULL, NULL</li>
<li>B, 2 --&gt; B, 2</li>
<li>C, 3 --&gt; C, 3</li>
<li>D, 4 -(C1 = D인 레코드의 C2가 3보다 큼)-&gt; NULL, NULL</li>
<li>E, 5 -(E가 없음)-&gt; NULL, NULL
✅ 해설
2️⃣ (정답)
C1, C2, C1, C2
A, 1, NULL, NULL
B, 2, B, 2
C, 3, C, 3
D, 4, NULL, NULL
E, 5, NULL, NULL</li>
</ul>
<hr>
<h3 id="49-아래의-오라클-sql을-동일한-결과를-출력하는-ansi-표준-구문으로-변경하고자-할-때-가장-적절한-sql은">49. 아래의 오라클 SQL을 동일한 결과를 출력하는 ANSI 표준 구문으로 변경하고자 할 때 가장 적절한 SQL은?</h3>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/3d7f314d-233a-4fd4-acbf-6c1d0ac107d9/image.jpg" alt=""></p>
<p>1️⃣ </p>
<pre><code class="language-SQL">SELECT A.게시판ID, A.게시판명, COUNT(B.게시글ID) AS CNT
FROM 게시판 A LEFT OUTER JOIN 게시글 B
ON (A.게시판ID = B.게시판ID AND B.삭제여부 = &#39;N&#39;)
WHERE A.사용여부 = &#39;Y&#39;
GROUP BY A.게시판ID, A.게시판명
ORDER BY A.게시판ID;</code></pre>
<p>2️⃣ </p>
<pre><code class="language-SQL">SELECT A.게시판ID, A.게시판명, COUNT(B.게시글ID) AS CNT
FROM 게시판 A LEFT OUTER JOIN 게시글 B
ON (A.게시판ID = B.게시판ID AND A.사용여부 = &#39;Y&#39;)
WHERE B.삭제여부 = &#39;N&#39;
GROUP BY A.게시판ID, A.게시판명
ORDER BY A.게시판ID;</code></pre>
<p>3️⃣ </p>
<pre><code class="language-SQL">SELECT A.게시판ID, A.게시판명, COUNT(B.게시글ID) AS CNT
FROM 게시판 A LEFT OUTER JOIN 게시글 B
ON (A.게시판ID = B.게시판ID)
WHERE A.사용여부 = &#39;Y&#39;
AND B.삭제여부 = &#39;N&#39;
GROUP BY A.게시판ID, A.게시판명
ORDER BY A.게시판ID;</code></pre>
<p>4️⃣ </p>
<pre><code class="language-SQL">SELECT A.게시판ID, A.게시판명, COUNT(B.게시글ID) AS CNT
FROM 게시판 A RIGHT OUTER JOIN 게시글 B
ON (A.게시판ID = B.게시판ID AND A.사용여부 = &#39;Y&#39; AND B.삭제여부 = &#39;N&#39;)
GROUP BY A.게시판ID, A.게시판명
ORDER BY A.게시판ID;</code></pre>
<p>📌 개념
ORACLE -&gt; ANSI 표준
A = B(+) -&gt; RIGHT OUTER JOIN
A(+) = B -&gt; LEFT OUTER JOIN</p>
<p>✅ 해설
보기의 SQL</p>
<pre><code class="language-SQL">SELECT A.게시판ID, A.게시판명, COUNT(B.게시글ID) AS CNT
FROM 게시판 A, JOIN 게시글 B -&gt; ANSI : FROM 게시판 A LEFT OUTER JOIN 게시글 B
WHERE A.게시판ID = B.게시판ID(+) -&gt; ANSI : ON (A.게시판ID = B.게시판ID
AND B.삭제여부(+) = &#39;N&#39; -&gt; ANSI : AND B.삭제여부 = &#39;N&#39;)
AND A.사용여부 = &#39;Y&#39; -&gt; ANSI : WHERE A.사용여부 = &#39;Y&#39;
GROUP BY A.게시판ID, A.게시판명
ORDER BY A.게시판ID;</code></pre>
<p>1️⃣ (정답) ANSI 표준으로 바꾸면 1번과 동일함</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQLD 학습 정리] 노랭이 오답노트 과목 1, 제 2장 - 데이터 모델과 SQL]]></title>
            <link>https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-%EB%85%B8%EB%9E%AD%EC%9D%B4-%EC%98%A4%EB%8B%B5%EB%85%B8%ED%8A%B8-%EA%B3%BC%EB%AA%A9-1-%EC%A0%9C-2%EC%9E%A5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EA%B3%BC-SQL</link>
            <guid>https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-%EB%85%B8%EB%9E%AD%EC%9D%B4-%EC%98%A4%EB%8B%B5%EB%85%B8%ED%8A%B8-%EA%B3%BC%EB%AA%A9-1-%EC%A0%9C-2%EC%9E%A5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EA%B3%BC-SQL</guid>
            <pubDate>Thu, 27 Feb 2025 01:57:39 GMT</pubDate>
            <description><![CDATA[<h3 id="39-아래-논리-데이터-모델을-3차-정규화까지-수행했을-때-도출되는-엔터티-수로-가장-적절한-것은">39. 아래 논리 데이터 모델을 3차 정규화까지 수행했을 때 도출되는 엔터티 수로 가장 적절한 것은?</h3>
<p>(단, 하나의 대출자에 대해 하나의 대출번호로 여러개의 도서 대출/반납을 관리한다고 가정하고, 엔터티 통합은 고려하지 않음)
<img src="https://velog.velcdn.com/images/dev_hogun/post/c8486c4d-8ea9-4b0a-8a89-62aef42ed05c/image.jpg" alt="">
1️⃣ 5
2️⃣ 6
3️⃣ 7
4️⃣ 8</p>
<p>📌 개념
1NF : 원자성 만족
2NF : 완전함수종속성 만족
3NF : 이행적종속성 제거</p>
<p>✅ 해설
1NF 수행</p>
<ul>
<li><code>LAB실이용신청일</code>, <code>시작일</code>, <code>이용승인교수번호</code>, <code>교수명</code> )-&gt; 여러 속성값을 가짐 -&gt; 엔터티 분리</li>
<li>테이블 : 학생, LAB실이용신청, 도서 대출
2NF 수행</li>
<li>PK가 각각 하나로 구성되어있기에 이미 완전함수종속성을 만족함</li>
<li>테이블 : 학생, LAB실이용신청, 도서 대출
3NF 수행</li>
<li><code>LAB실이용신청</code>에서 <code>교수명</code>은 <code>이용승인교수번호</code>에 종속 -&gt; <code>교수</code> 엔터티 분리</li>
<li><code>학생</code>에서 <code>학과명</code>은 <code>학과번호</code>에 종속 -&gt; <code>학과</code> 엔터티 분리</li>
<li><code>대출도서명</code>, <code>출판사명</code>, <code>출판년월</code>, <code>대표저자명</code>, <code>ISBN</code>은 <code>대출도서번호</code>에 종속 -&gt; <code>대출도서</code> 엔터티 분리</li>
<li><code>대출도서</code> 엔터티에서 PK는 <code>대출번호</code>와 <code>도서번호</code>로 구성됨<ul>
<li><code>도서명</code>, <code>출판사명</code>, <code>출판년월</code>, <code>대표저자명</code>, <code>ISBN</code>은 <code>도서번호</code>에 종속 -&gt; 부분함수종속성을 가짐</li>
<li>완전함수종속성을 만족시키기 위해(2NF 과정) <code>대출도서</code>와 <code>도서</code>로 엔터티 분리</li>
</ul>
</li>
<li>결과적으로 <code>학과</code>, <code>학생</code>, <code>교수</code>, <code>LAB실이용신청</code>, <code>도서대출</code>, <code>대출도서</code>, <code>도서</code> 엔터티로 분리
3️⃣ (정답) 7개 (학과, 학생, 교수, LAB실이용신청, 도서대출, 대출도서, 도서)</li>
</ul>
<hr>
<h3 id="41-데이터-모델링의-정규화에-대한-설명으로-가장-적절하지-u않은u-것은">41. 데이터 모델링의 정규화에 대한 설명으로 가장 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ 정규화는 개념 데이터 모델의 일관성을 확보하고 중복을 제거하여 속성들이 가장 적절한 엔터티에 배치되도록 한다. 
2️⃣ 제1정규형은 모든 인스턴스가 반드시 하나의 값을 가져야 함을 의미한다. 
3️⃣ 제3정규형을 만족하는 엔터티의 일반속성은 주식별자 전체에 종속적이다. 
4️⃣ 반정규화는 성능을 위해 데이터 중복을 허용하는 것이지만 성능의 향상을 항상 보장하는 것은 아니다. </p>
<p>📌 개념
데이터 모델링의 3단계</p>
<ul>
<li>개념적 모델링 : 개념적 설계 -&gt; ERD 작성</li>
<li>논리적 모델링 : 세부속성, 식별자, 관계 설계 -&gt; 정규화 수행</li>
<li>물리적 모델링 : 실제 데이터베이스 구축 -&gt; 반정규화를 수행하기도 함</li>
</ul>
<p>✅ 해설
정규화는 <code>개념 데이터 모델</code>링 과정이 아닌 논리 모델링 과정에 속함. 
1️⃣ (정답) 논리 모델링 과정에 대한 설명
2️⃣ 1차정규형 - 원자성 만족
3️⃣ 3차정규형 - 이행적 종속성 제거 = 일반속성에 종속되는 다른 일반속성이 없음
4️⃣ 반정규화 - 중복 허용, 조인 감소 -&gt; 조회 성능 향상 / 유연성 감소</p>
<hr>
<h3 id="43-아래에서-설명하는-정규형으로-가장-적절한-것은">43. 아래에서 설명하는 정규형으로 가장 적절한 것은?</h3>
<pre><code>엔터티의 일반속성은 주식별자 전체에 종속적이어야 한다. </code></pre><p>1️⃣ 제1정규형
2️⃣ 제2정규형
3️⃣ 제3정규형
4️⃣ 보이스-코드 정규형</p>
<p>📌 개념
1NF : 원자성 만족
2NF : 완전함수종속성 만족
3NF : 이행적종속성 제거</p>
<p>✅ 해설
2️⃣ (정답) 식별자에 일반속성이 종속됨 = 완전함수종속성 만족</p>
<hr>
<h3 id="49-null-값에-대한-설명으로-가장-적절한-것은">49. NULL 값에 대한 설명으로 가장 적절한 것은?</h3>
<p>1️⃣ NULL 값에 어떤 숫자를 더해도 결과는 항상 NULL이다. 
2️⃣ NULL 값과 어떤 숫자의 크기를 비교해도 결과는 항상 NULL이다. 
3️⃣ &quot;NULL = NULL&quot; 연산의 결과는 TRUE이다. 
4️⃣ 집계 함수를 계산할 때 NULL 값은 0으로 처리된다. </p>
<p>📌 개념</p>
<ul>
<li>NULL을 포함한 연산 결과는 (NULL을 핸들링하는 함수를 제외하면) 항상 NULL이다. </li>
<li>그룹 함수는 NULL을 제외하고 연산을 수행한다. </li>
</ul>
<p>✅ 해설
1️⃣ (정답) NULL 핸들링 함수를 제외한 NULL과의 연산 결과는 항상 NULL이다. 
2️⃣ 예외가 존재한다. (<code>ORDER BY</code>에서 NULL값과 비교가 이루어짐, <code>DISTINCT FROM</code> 마찬가지)
3️⃣ NULL 핸들링 함수를 제외한 NULL과의 연산 결과는 항상 NULL이다. 
4️⃣ 그룹함수는 NULL을 0으로 처리하는게 아니라 제외하고 연산한다. </p>
<hr>
<h3 id="50-본질식별자와-인조식별자에-대한-설명으로-가장-적절하지-u않은u-것은">50. 본질식별자와 인조식별자에 대한 설명으로 가장 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ 인조식별자는 대체로 본질식별자가 복잡한 구성을 가질 때 만들어진다. 
2️⃣ 인조식별자를 사용하면 중복 데이터를 막기 어려워진다. 
3️⃣ 인조식별자를 사용하면 본질식별자를 사용할 때와 비교하여 추가적인 인덱스가 필요하다. 
4️⃣ 인조식별자는 개발 편의성을 높여주기 때문에 되도록 사용하는 것이 바람직하다. </p>
<p>📌 개념
식별자의 대체 여부에 따른 분류</p>
<ul>
<li>본질 식별자 : 업무 프로세스에서 차용</li>
<li>인조 식별자 : 인위적으로 만듦</li>
</ul>
<p>✅ 해설
2️⃣ 실제 데이터를 구분하는 정보가 없다는 측면에서 맞는 설명
4️⃣ (정답) <code>되도록 사용하는 것이</code> -&gt; 단점()도 존재하기에 필요시 사용해야 함</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQLD 학습 정리] 노랭이 오답노트 과목 1, 제 1장 - 데이터 모델링의 이해]]></title>
            <link>https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-%EB%85%B8%EB%9E%AD%EC%9D%B4-%EC%98%A4%EB%8B%B5%EB%85%B8%ED%8A%B8-%EA%B3%BC%EB%AA%A9-1-%EC%A0%9C-1%EC%9E%A5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EB%A7%81%EC%9D%98-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-%EB%85%B8%EB%9E%AD%EC%9D%B4-%EC%98%A4%EB%8B%B5%EB%85%B8%ED%8A%B8-%EA%B3%BC%EB%AA%A9-1-%EC%A0%9C-1%EC%9E%A5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EB%A7%81%EC%9D%98-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Wed, 26 Feb 2025 06:39:35 GMT</pubDate>
            <description><![CDATA[<h3 id="2-데이터-모델링에-대한-설명으로-가장-적절하지-않은-것은">2. 데이터 모델링에 대한 설명으로 가장 적절하지 않은 것은?</h3>
<p>1️⃣ 업무 정보를 구성하는 기초가 되는 정보들을 일정한 표기법으로 표현한다. 
2️⃣ 분석된 모델로 데이터베이스를 생성하여 개발 및 데이터관리에 사용한다. 
3️⃣ 데이터베이스를 구축하는 목적으로 데이터 모델링을 수행하며 업무에 대한 설명은 별도의 표기법을 이용한다. 
4️⃣ 데이터 모델링 자체로서 업무의 흐름을 설명하고 분석하는 부분에 의미를 가지고 있다. </p>
<p>📌 개념
데이터 모델링의 목적 : 1. 업무 내용 분석, 2. 데이터베이스 구축 및 관리</p>
<p>✅ 해설
1️⃣ <code>업무 정보를</code> <code>표현</code> -&gt; 업무 내용 분석
2️⃣ <code>데이터베이스를 생성하여 개발 및 데이터관리</code> -&gt; 데이터베이스 구축 및 관리
3️⃣ (오답) <code>데이터베이스를 구축하는 목적</code> 뿐은 아님 (업무 분석 등), 따라서 <code>별도의 표기법</code>을 이용해야 하는 것은 아님
4️⃣<code>업무의 흐름을 설명하고 분석</code> -&gt; 업무 내용 분석</p>
<hr>
<h3 id="3-데이터-모델링을-할-때-유의해야-할-사항으로-적절하지-u않은u-것은">3. 데이터 모델링을 할 때 유의해야 할 사항으로 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ 여러 장소의 데이터베이스에 같은 정보를 저장하지 않도록 하여 중복성을 최소화한다. 
2️⃣ 데이터의 정의를 데이터의 사용 프로세스와 분리하여 유연성을 높인다. 
3️⃣ 사용자가 처리하는 프로세스나 장표 등에 따라 매핑이 될 수 있도록 프로그램과 테이블 간의 연계성을 높인다. 
4️⃣ 데이터 간의 상호 연관관계를 명확하게 정의하여 일관성 있게 데이터가 유지되도록 한다. </p>
<p>📌 개념
데이터 모델링의 유의사항 : 중복, 비유연성, 비일관성</p>
<ul>
<li>중복 : 데이터 중복 저장</li>
<li>비유연성 : 데이터와 프로세스가 분리되지 않음 -&gt; 유지보수성이 떨어짐</li>
<li>비일관성 : 데이터의 모순 발생 (데이터들이 갖는 관계에서 발생하는 모순)</li>
</ul>
<p>✅ 해설
1️⃣ <code>중복성을 최소화</code> -&gt; 중복 방지
2️⃣ <code>데이터의 정의</code>와 <code>사용 프로세스</code>의 분리 -&gt; 비유연성 방지
3️⃣ (오답) <code>프로그램과 테이블 간의 연계성을 높인다</code> -&gt; 비유연성 발생
4️⃣ <code>연관관계를 명확하게 정의</code>, <code>일관성 있게 데이터가 유지</code> -&gt; 비일관성 방지</p>
<hr>
<h3 id="6-아래에서-설명하는-스키마-구조로-가장-적절한-것은">6. 아래에서 설명하는 스키마 구조로 가장 적절한 것은?</h3>
<pre><code>- 모든 사용자 관점을 통합한 조직 전체 관점의 통합적 표현
- 모든 응용시스템들이나 사용자들이 필요로 하는 데이터를 통합시킨 조직
  전체의 DB를 기술한 것으로 DB에 저장되는 데이터와 그들 간의 관계를 표현하는 스키마</code></pre><p>1️⃣ 외부 스키마
2️⃣ 개념 스키마
3️⃣ 내부 스키마
4️⃣ 논리 스키마</p>
<p>📌 개념
스키마의 3단계 구조</p>
<ul>
<li>외부 스키마 : 사용자 관점, 업무적 관점서 필요한 데이터 명세</li>
<li>개념 스키마 : 전체적인 논리 구조를 정의 (개체, 속성, 관계, 타입 ...)</li>
<li>내부 스키마 : 물리적인 저장 방식을 정의 (저장 구조, 컬럼, 인덱스 ...)</li>
</ul>
<p>cf. 논리 스키마는 스키마의 3단계 구조에 포함되지 않음!</p>
<p>✅ 해설
2️⃣ (정답) 개념 스키마(통합 관점에서의 스키마 구조) &lt;- <code>모든 &quot;사용자 관점&quot;을 &quot;통합&quot;한 조직 &quot;전체 관점&quot;</code>, <code>DB에 저장되는 데이터와 그들 간의 관계</code></p>
<hr>
<h3 id="10-엔터티의-특징으로-가장-적절하지-u않은u-것은">10. 엔터티의 특징으로 가장 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ 속성이 없는 엔터티는 있을  수 없다. 
2️⃣ 엔터티는 다른 엔터티와 관계가 있어야 한다. 단, 통계성 엔터티나, 코드성 엔터티의 경우 관계를 생략할 수 있다. 
3️⃣ 객체 지향의 디자인 패턴에는 싱글턴패턴이 있어 하나의 인스턴스를 가지는 클래스가 존재한다. 이와 유사하게 엔터티는 한 개의 인스턴스를 가지는 것만으로도 충분한 의미를 부여할 수 있다. 
4️⃣ 데이터로서 존재하지만 업무에서 필요로 하지 않으면 해당 업무의 엔터티로 성립될 수 없다. </p>
<p>📌 개념
엔터티의 특징</p>
<ol>
<li>해당 업무에서 필요하고 관리하고자 하는 정보이어야 한다. </li>
<li>유일한 식별자에 의해 식별이 가능해야 한다. </li>
<li>영속적으로 존재하는 (2개 이상의) 인스턴스 집합이어야 한다. </li>
<li>엔터티는 업무 프로세스에 의해 이용되어야 한다. </li>
<li>엔터티는 반드시 속성이 있어야 한다. </li>
<li>엔터티는 다른 엔터티와 최소 한 개 이상의 관계가 있어야 한다. </li>
</ol>
<p>✅ 해설
1️⃣ 엔터티의 특징 5번에 부합
2️⃣ 엔터티의 특징 6번에 부합
3️⃣ (정답) 엔터티의 특징 3번에 위배
4️⃣ 엔터티의 특징 1번에 부합</p>
<hr>
<h3 id="12-발생-시점에-따라-구분할-수-있는-엔터티의-유형으로-적절하지-u않은u-것은">12. 발생 시점에 따라 구분할 수 있는 엔터티의 유형으로 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ 관계 엔터티 (Relation Entity)
2️⃣ 행위 엔터티 (Active Entity)
3️⃣ 중심 엔터티 (Main Entity)
4️⃣ 기본 엔터티 (Fundamental Entity)</p>
<p>📌 개념
엔터티의 발생 시점에 따른 분류</p>
<ul>
<li>기본 엔터티 : 원래 존재하는 정보, 독자적으로 생성</li>
<li>중심 엔터티 : 기본 엔터티로부터 발생, 업무에서 중심적인 역할</li>
<li>행위 엔터티 : 중심 엔터티의 확장</li>
</ul>
<p>✅ 해설
1️⃣ (정답) 관계 엔터티는 발생 시점에 따른 구분이 아님
    cf. 관계 엔터티 : N:M 관계를 해소하기 위한 엔터티 (M:N -&gt; M : 1 + 1 : M, 여기서 1을 담당하는 것)</p>
<hr>
<h3 id="15-속성에-대한-설명으로-가장-적절하지-u않은u-것은">15. 속성에 대한 설명으로 가장 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ 엔터티에 대한 자세하고 구체적인 정보를 나타낸다. 
2️⃣ 하나의 엔터티는 두 개 이상의 속성을 갖는다. 
3️⃣ 하나의 인스턴스에서 각각의 속성은 하나 이상의 속성값을 가질 수 있다. 
4️⃣ 속성도 집합이다. </p>
<p>📌 개념</p>
<p>✅ 해설
3️⃣ (정답) <code>하나의</code>, <code>인스턴스</code>에서, 하나의 인스턴스에서는 하나의 속성값만 가짐
cf. 4️⃣ 속성도 집합의 성질을 띔 (도메인)
    ex) SqldStudy DB &gt;&gt; 스터디원 테이블 &gt;&gt; 성별 속성이 있다면 도메인은 { &quot;남&quot;, &quot;여&quot; }로 표현 가능</p>
<hr>
<h3 id="16-아래에서-수행한-정규화-작업으로-가장-적절한-것은-단-이후에-필요한-정규화-작업은-계속-수행될-예정">16. 아래에서 수행한 정규화 작업으로 가장 적절한 것은? (단, 이후에 필요한 정규화 작업은 계속 수행될 예정)</h3>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/7654e6f8-6503-44fb-8be6-0aedf1c7a5c0/image.png" alt="">
1️⃣ 1차 정규화
2️⃣ 2차 정규화
3️⃣ 3차 정규화
4️⃣ 4차 정규화</p>
<p>📌 개념
정규화</p>
<ul>
<li>1NF : 원자성 (원자값을 갖도록 = 여러 값을 갖지 않도록)</li>
<li>2NF : 완전 함수 종속성 (&quot;기본키의 전체&quot;에 종속되도록 &lt;-&gt; 부분 함수 종속성)</li>
<li>3NF : 이행적 종속성 제거 ((A-&gt;B, B-&gt;C)라면 (A-&gt;B)와 (B-&gt;C로 분리))</li>
</ul>
<p>cf. 4NF &amp; 5NF : 다치종속 &amp; 조인종속 제거 )-&gt; 라고 하네요~</p>
<p>✅ 해설
2️⃣ 2차 정규화</p>
<ul>
<li>PK는 <code>주문 번호</code>와 <code>주문상품코드</code></li>
<li>그러나 <code>주문상품</code> 칼럼은 <code>주문상품코드</code>에 종속될 수 있음</li>
<li>따라서 <code>주문상품</code> 테이블에서 <code>상품</code>을 분리</li>
</ul>
<p>=&gt; 테이블의 모든 속성은 기본키 전체에 종속됨</p>
<hr>
<h3 id="20-데이터-모델링의-관계에-대한-설명으로-가장-적절하지-u않은u-것은">20. 데이터 모델링의 관계에 대한 설명으로 가장 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ 관계는 존재에 의한 관계와 행위에 의한 관계로 구분될 수 있으나 ERD에서는 관계를 연결할 때, 존재와 행위를 구분하지 않고 단일화된 표기법을 사용한다. 
2️⃣ UML(Unified Modeling Language)에는 클래스다이어그램의 관계 중 연관관계(Association)와 의존관계(Dependency)가 있고 이것은 실선과 점선으로 다르게 표현된다. 
3️⃣ 연관관계는 항상 이용하는 관계로 존재적 관계에 해당하고, 의존관계는 상대방 클래스의 행위에 의해 관계가 형성되는 행위적 관계에 해당한다. 
4️⃣ 연관관계는 오퍼레이션에서 파라미터 등으로 이용할 수 있고, 의존관계는 소스코드에서 멤버변수로 선언하여 사용할 수 있다. </p>
<p>📌 개념
연관관계</p>
<ul>
<li>항상 존재하는 관계</li>
<li>객체가 지속적으로 참조됨 -&gt; 멤버 변수로 선언됨</li>
<li>UML에서 실선으로 표현됨
의존관계</li>
<li>일시적으로 의존하는 관계</li>
<li>객체가 지속적으로 유지되지 않음 -&gt; 메서드 내에서 일시적으로 사용</li>
<li>UML에서 점선으로 표현됨</li>
</ul>
<p>✅ 해설
1️⃣ ERD에서는 관계를 구분하지 않음 (UML에서는 나눔)
2️⃣ 맞음
3️⃣ 존재 관계 = 엔터티간의 상태 = 지속적으로 유지 =&gt; 연관관계
    행위관계 = 엔터티의 행위 = 일시적으로 발생 =&gt; 의존관계
4️⃣ (정답) <code>연관관계</code> &lt;- <code>멤버 변수</code>, <code>상관관계</code> &lt;- <code>파라미터 등</code>이 맞음 (반대되는 설명)</p>
<hr>
<h3 id="21-데이터-모델링의-관계에-대한-설명으로-가장-적절하지-u않은u-것은">21. 데이터 모델링의 관계에 대한 설명으로 가장 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ 관계는 존재적 관계와 행위에 의한 관계로 나누어볼 수 있다. 
2️⃣ 관계의 표기법은 관계명, 관계차수, 식별성의 3가지 개념을 사용한다. 
3️⃣ 부서와 사원 엔터티 간의 &#39;소속&#39; 관계는 존재적 관계의 사례이다. 
4️⃣ 주문과 배송 엔터티 간의 &#39;배송근거&#39; 관계는 행위적 관계에 해당한다. </p>
<p>📌 개념
관계의 분류</p>
<ul>
<li>존재적 관계 : 한 존재가 다른 존재 여부에 영향을 주는 관계</li>
<li>행위적 관계 : 특정한 행위에 의해 형성되는 관계 (행위가 없으면 관계가 형성되지 않을 수 있음)</li>
</ul>
<p>✅ 해설
1️⃣ 관계의 분류 : 존재적 관계, 행위적 관계
2️⃣ (정답) 관계의 표기법에서는 <code>관계명</code>, <code>관계차수</code>, <code>선택성</code> 개념을 사용
3️⃣ 사원은 부서에 소속됨 = 부서가 없으면 사원도 없음 -&gt; 존재적 관계
4️⃣ 주문이 배송을 결정 = 주문이라는 행위에 의해 배송이 발생 -&gt; 행위적 관계</p>
<hr>
<h3 id="25-두-개의-엔터티-사이에서-관계를-도출할-때-확인해야-할-사항을-모두-고른-것은">25. 두 개의 엔터티 사이에서 관계를 도출할 때 확인해야 할 사항을 모두 고른 것은?</h3>
<pre><code>(가) 두 개의 엔터티 사이에 관심 있는 연관규칙이 존재하는가?
(나) 두 개의 엔터티 사이에 정보의 조합이 발생되는가?
(다) 업무기술서, 장표에 관계연결에 대한 규칙이 서술되어 있는가?
(라) 업무기술서, 장표에 관계연결을 가능하게 하는 동사(Verb)가 있는가?</code></pre><p>1️⃣ (가), (나), (다)
2️⃣ (가), (나), (라)
3️⃣ (가), (다), (라)
4️⃣ (가), (나), (다), (라)</p>
<p>📌 개념
관계 도출 시 고려해야할 요소</p>
<ul>
<li>두 엔터티 사이에 의미 있는 관계가 존재하는가</li>
<li>관계가 설정되었을 때 의미있는 정보 조합이 발생하는가</li>
<li>업무 상에서 명확한 관계 규칙이 존재하는가</li>
<li>관계의 연관성을 표현하는 동사로 표현해야 함</li>
<li>관계의 방향과 차수 (1:1, 1:N, N:M)를 결정해야 함</li>
<li>관계의 식별성, 선택성을 규정해야 함</li>
</ul>
<p>✅ 해설
4️⃣ (정답) 모두 옳은 설명</p>
<hr>
<h3 id="27-아래에서-사원-엔터티의-특성에-해당하지-u않는u-것은">27. 아래에서 사원 엔터티의 특성에 해당하지 <u>않는</u> 것은?</h3>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/fda50537-7382-4932-a3fb-6aa21cb2915c/image.jpg" alt="">
1️⃣ 주식별자
2️⃣ 단일식별자
3️⃣ 내부식별자
4️⃣ 인조식별자</p>
<p>📌 개념
식별자의 분류</p>
<ul>
<li>대표성 여부에 따른 분류 : 주식별자(대표성 有) vs 보조 식별자(대표성 無) / 둘 모두 유일성, 최소성 만족</li>
<li>생성 여부에 따른 분류 : 내부 식별자(참조 없이 생성) vs 외부 식별자 (관계에 의해 생성)</li>
<li>속성 수에 따른 분류 : 단일 식별자(하나의 속성으로 구성) vs 복합 식별자 (2개 이상의 속성으로 구성)</li>
<li>대체 여부에 따른 분류 : 본질 식별자(업무 프로세스에서 차용) vs 인조 식별자(인위적으로 만듦)
✅ 해설
사원의 식별자는 사번
1️⃣ 사번은 대표성을 가짐
2️⃣ 사번 하나로 구성
3️⃣ 스스로 생성됨 (참조에 의해 생성되지 않음)
4️⃣ (정답) 사원은 업무 프로세스에 존재함, 따라서 인조 식별자가 아닌 본질 식별자에 해당함</li>
</ul>
<hr>
<h3 id="30-데이터-모델링에서-비식별자-관계로-연결하는-경우로-가장-적절하지-u않은u-것은">30. 데이터 모델링에서 비식별자 관계로 연결하는 경우로 가장 적절하지 <u>않은</u> 것은?</h3>
<p>1️⃣ 엔터티와 엔터티가 1:M 관계의 부모와 자식관계에서 데이터가 부모 없이 자식쪽 엔터티의 인스턴스가 먼저 생성될 수 있을 경우 비식별자 관계로 연결해야 한다. 
2️⃣ 부모 엔터티의 인스턴스가 자식 엔터티의 인스턴스보다 먼저 소멸하는 경우 비식별자 관계로 연결해야 한다. 
3️⃣ SQL 문의 조인 관계를 최소화하는 경우 비식별자 관계로 연결해야 한다. 
4️⃣ 자식 엔터티의 식별자가 부모 엔터티의 주식별자를 상속받아 생성하는 것보다 별도의 주식별자를 생성하는 것이 더 유리하다고 판단되는 경우 비식별자 관계로 연결해야 한다. </p>
<p>📌 개념
식별관계 : 하나의 엔터티의 기본키를 다른 기본키로 공유하는 경우
비식별관계 : 강한 개체의 기본키를 다른 엔터티의 일반 속성으로 관계를 가짐
식별관계 vs 비식별관계</p>
<ul>
<li>목적 : 강한 연결관계를 표현 vs 약한 연결관계를 표현</li>
<li>자식 주식별자 영향 : 자식 주식별자의 구성에 포함 vs 자식 일반 속성에 포함</li>
<li>표기법 : 실선 표현 vs 점선 표현</li>
<li>연결 고려사항<ul>
<li>식별관계<ul>
<li>반드시 부모 엔터티에 종속</li>
<li>자식 주식별자 구성에 부모 주식별자 포함 필요</li>
<li>상속받은 주식별자 속성을 타 엔터티에 이전 필요</li>
</ul>
</li>
<li>비식별관계<ul>
<li>약한 종속관계</li>
<li>자식 주식별자 구성을 독립적으로 구성</li>
<li>상속받은 주식별자 속성을 타 엔터티에 차단 필요</li>
<li>부모의 관계참여가 선택관계</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>✅ 해설
3️⃣ (정답) 조인 관계를 최소화하려면 식별관계로 연결해야 함</p>
<hr>
<h3 id="33-아래-빈칸-ㄱ-ㄴ-ㄷ에-해당하는-것은">33. 아래 빈칸 ㄱ, ㄴ, ㄷ에 해당하는 것은?</h3>
<pre><code>업무분석을 통해 바로 정의한 속성을 ( ㄱ ), 원래 업무상 존재하지는 않지만 설계를 하면서 도출해 내는 속성을 ( ㄴ ), 다른 속성으로부터 계산이나 변형이 되어 생성되는 속성을 ( ㄷ )이라고 한다. </code></pre><p>1️⃣ ( ㄱ ) : 기본 속성, ( ㄴ ) : 설계 속성, ( ㄷ ) : 파생 속성
2️⃣ ( ㄱ ) : 기본 속성, ( ㄴ ) : 일반 속성, ( ㄷ ) : 설계 속성
3️⃣ ( ㄱ ) : 일반 속성, ( ㄴ ) : 기본 속성, ( ㄷ ) : 파생 속성
4️⃣ ( ㄱ ) : 일반 속성, ( ㄴ ) : 설계 속성, ( ㄷ ) : 파생 속성</p>
<p>📌 개념
속성의 특성에 따른 분류</p>
<ul>
<li>기본 속성 : 업무로 부터 추출된 모든 속성 (사실상 가장 많음)  </li>
<li>설계 속성 : 업무를 규칙화할 때 새로 만들어지는 속성 (ex. 상품코드, 지점코드)  </li>
<li>파생 속성 : 기본/설계 속성에서 파생되는 속성 (일반적으로 계산된 값, ex. 합계, 평균, 이자 ...)
cf. 
엔터티 구성방식에 따른 분류</li>
<li>PK(Primary Key, 기본키, 주식별자) : 인스턴스를 식별할 수 있는 속성  </li>
<li>FK(Foreign Key, 외래키): 다른 엔터티와의 관계에서 포함된 속성  </li>
<li>일반 속성 : 그 외
분해 여부에 따른 속성</li>
<li>단일 속성 : 하나의 의미로 구성 (ex. 회원ID, 이름)  </li>
<li>복합 속성 : 여러개의 의미로 구성 (ex. 주소 - 시, 구, 동 등으로 분해 가능)  </li>
<li>다중값 속성 : 여러개의 값을 가질 수 있음 (ex. 구매한 상품 &lt;- 하나의 고객이 여러개를 가지겠지)  </li>
</ul>
<p>✅ 해설
1️⃣ (정답) ㄱ : <code>업무분석을 통해 바로 정의한 속성</code> &lt;- 업무로부터 추출
    ㄴ : <code>설계를 하면서 도출해 내는 속성</code> &lt;- 설계 속성의 개념
    ㄷ : <code>다른 속성</code>(기본/설계 속성) <code>으로부터</code>, <code>계산이나 변형이 되어 생성되는 속성</code> &lt;- 파생 속성의 개념</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQLD 학습 정리] SQLD 2과목 - SQL 기본 및 활용]]></title>
            <link>https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-SQLD-2%EA%B3%BC%EB%AA%A9-SQL-%EA%B8%B0%EB%B3%B8-%EB%B0%8F-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-SQLD-2%EA%B3%BC%EB%AA%A9-SQL-%EA%B8%B0%EB%B3%B8-%EB%B0%8F-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Mon, 24 Feb 2025 03:56:54 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.youtube.com/watch?v=jx_zBkaVqjc">유튜브 강의 정리본</a> 입니다. </p>
<h1 id="2과목-1단원-관계형-데이터베이스-개요">2과목 1단원, 관계형 데이터베이스 개요</h1>
<p>관계형 데이터베이스 : 하나의 테이블에 있었던걸 분리시킨 후 이들간의 관계를 만든다</p>
<h2 id="관계형-데이터베이스의-구성요소">관계형 데이터베이스의 구성요소</h2>
<p>계정 : 데이터의 접근 제한을 위해 여러 업무별/시스템별 계정이 존대
테이블 : DBMS의 DB 내 데이터가 저장되는 형식
스키마 : 테이블의 구성, 정보 등의 구조를 정의</p>
<h3 id="테이블">테이블</h3>
<ul>
<li>행(row)과 열(Column)을 갖는 2차원 주소</li>
<li>데이터를 입력하여 저장하는 최소 단위<h4 id="특징">특징</h4>
</li>
<li>하나의 테이블은 반드시 하나의 계정의 소유여야 함 (여러명이 조회하는건 상관 없음)<ul>
<li>테이블간 관계는 일대일(1:1) / 일대다(1:N) / 다대다(N:N)의 관계를 가짐</li>
</ul>
</li>
<li>테이블명은 중복될 수 없지만, 소유자가 다른 경우 같은 이름으로 생성 가능</li>
<li>행 단위로 데이터가 입력 및 삭제, 값 단위로 데이터가 수정<h2 id="sql">SQL</h2>
</li>
<li>관계형 데이터베이스에서 데이터 조회 및 조작, DBMS 관리 기능을 명령하는 언어</li>
<li>DDL(정의어), DML(조작어), DCL(제어어)로 구분</li>
<li>SQL 문법은 대/소문자를 구분하지 않음 (하지만 현업에서는 표준이 있음)<h3 id="특징-1">특징</h3>
</li>
<li>데이터의 분류, 정렬, 탐색 속도가 빠름</li>
<li>신뢰성이 높음, 무결성 보장</li>
<li>스키마를 수정하는게 어려움</li>
<li>DB의 부하를 분석하기 어려움 (복잡한 시스템이다 보니)<h4 id="데이터의-무결성integrity">데이터의 무결성(Integrity)</h4>
의미 : 정확성 및 일관성을 가짐, 데이터의 결손과 부정합이 없음
종류</li>
<li>개체 무결성 : 기본 키는 NULL이나 중복값을 가질 수 없다. </li>
<li>참조 무결성 : 외래키 값은 NULL이거나 참조 테이블의 기본키 값과 동일해야 한다. <ul>
<li>기본키가 아닌 다른 값은 참조할 수 없다. </li>
</ul>
</li>
<li>도메인 무결성 : 주어진 속성 값이 정의된 도메인 범위 안에 있어야 한다. </li>
<li>NULL 무결성 : 특성 속성(ex. Nullable이 FALSE인 속성)에 대해 NULL을 허용하지 않음</li>
<li>고유 무결성 : 특정 속성(ex. Unique가 TRUE인 속성)에 대해 중복되는 값을 허용하지 않음</li>
<li>키 무결성 : 릴레이션(관계)에는 적어도 하나의 키(Join 키)가 존재해야 함<h2 id="sql의-종류">SQL의 종류</h2>
</li>
<li>DDL (Data Definition Language): 구조 자체를 변경하는 명령어<ul>
<li>Types: CREATE, ALTER, DROP, TRUNCATE</li>
<li>되돌릴 수 없음 (Auto Commit - 자동 확정)<ul>
<li>TRUNCATE가 데이터를 다 날리는건데 DDL인 이유? Auto Commit. </li>
</ul>
</li>
</ul>
</li>
<li>DML (Data Manipulation Language): 데이터를 조작하는 명령어<ul>
<li>Types: INSERT, DELETE, UPDATE, MERGE</li>
</ul>
</li>
<li>DCL (Data Control Language): GRANT, REVOKE(권한 회수)</li>
<li>TCL (Transaction Control Language): 트랜잭션을 제어하는 명령어<ul>
<li>Types: COMMIT. REVOKE</li>
</ul>
</li>
<li>DQL (Data Query Language): 쿼리어<ul>
<li>Types: SELECT<h1 id="2과목-2단원-select">2과목 2단원, SELECT</h1>
<h2 id="select-문-구조">SELECT 문 구조</h2>
<pre><code class="language-SQL">SELECT * | 컬럼명 | 표현식 -- 조회하고 싶은 대상
FROM 테이블명 또는 뷰명 -- 데이터의 출처
WHERE 조회 조건 -- 행을 걸러내는 조건
GROUP BY 그룹필터링컬럼 -- 그룹 연산이 필요할 때
HAVING 그룹핑 필터링 조건 -- GROUP BY를 수행한 결과에 조건을 추가하고 싶을 때
ORDER BY 정렬컬럼명 -- 마지막으로 정렬</code></pre>
</li>
</ul>
</li>
<li>6개의 절로 구성, 각 절의 순서대로 작성해야 함</li>
<li>파싱(문법적 해석) 순서<ul>
<li>FROM &gt; WHERE &gt; GROUP BY &gt; HAVING &gt; SELECT &gt; ORDER BY</li>
<li>데이터가 있어야 하니까 FROM 먼저, 그 다음 WHERE, GROUP으로 묶고, 그 후에 결과에 넣을 속성 정하고, 마지막으로 정렬<h2 id="select-절">SELECT 절</h2>
</li>
</ul>
</li>
<li>조회할 컬럼명/연산결과를 정의</li>
<li>표시할 대상에 Alias(별칭, 출력할 다른 이름) 지정 가능 (대소문자 구분 X)<ul>
<li>SELECT 절 이후에 파싱하는 ORDER BY에서만 사용 가능</li>
<li>한글 사용 가능, 예약어(avg, count, decode, SELECT, FROM ...)는 사용 불가</li>
</ul>
</li>
<li>다음의 경우 별칭에 반드시 쌍따옴표로 전달<ul>
<li><ol>
<li>공백을 포함하는 경우</li>
</ol>
</li>
<li>언더바 (<code>_</code>)를 제외한특수문자를 포함하는 경우</li>
<li>별칭의 대소문자 그대로 전달할 경우</li>
<li>AS는 생략 가능<h2 id="from-절">FROM 절</h2>
</li>
</ul>
</li>
<li>가장 먼저 실행되는 절, 데이터의 출처를 밝히는 역할</li>
<li>테이블명 대신 뷰명을 전달해도 됨<ul>
<li>뷰 : 데이터를 조회할 수 있지만, 물리적으로 저장되어있지 않는 객체</li>
</ul>
</li>
<li>테이블 여러개 전달 가능, 조인 조건 없이 테이블명만 나열 시 카타시안 곱 발생</li>
<li>별칭을 사용할 수 있음, ORACLE에서는 AS 사용 불가, SQL Server는 사용/생략 가능</li>
<li>ORACLE에서는 의미상 필요하지 않더라도 생략할 수 없음<ul>
<li>필요없는 경우 DUAL이라는 Dummy Table을 선언해야 함</li>
<li>SQL Server는 가능 (오늘 날짜 조회 등)</li>
<li>ORACLE 23c 버전부터는 생략 가능 (하지만, 자격증에서 물어보지는 않을 듯)
  ex) <code>SELECT 24 * 123 FROM DUAL;</code></li>
</ul>
</li>
<li>테이블 별칭을 선언한 이후에는 테이블명이 아닌 별칭으로 사용해야 함. <h1 id="2과목-3단원-함수">2과목 3단원, 함수</h1>
</li>
<li>input value에 대해 out value를 출력해주는 객체</li>
<li>from절을 제외한 모든 절에서 사용 가능<h2 id="함수의-기능">함수의 기능</h2>
</li>
<li>계산을 수행, 데이터의 항목을 수정<h2 id="입력값의-수에-따른-함수의-종류">입력값의 수에 따른 함수의 종류</h2>
단일행 함수 : input과 output의 관계가 1:1
복수행 함수 (그룹함수 / 집계함수) : 여러 건의 데이터를 입력받아 하나의 요약값을 반환<h2 id="입-출력값의-타입에-따른-함수-분류">입 출력값의 타입에 따른 함수 분류</h2>
<h3 id="문자형-함수">문자형 함수</h3>
문자열 결함, 추출, 삭제, 치환 ...
단일행 함수 형태
output도 대부분 문자값 (cf. LENGTH, INSTR)</li>
<li>LOWER(대상) / UPPER(대상) : 대소문자 치환 (~= Inicap &lt;- Camel 식으로 변환)</li>
<li>SUBSTR(대상, m, n) : m 위치에서 n개의 문자열 추출<ul>
<li>ex) SUBSTR(&#39;ABCDE&#39;, 2, 3) =&gt; BCD</li>
</ul>
</li>
<li>INSTR(대상, 찾을문자열, m, n) : 찾은 문자열 반환 (m번째부터, n번째 발견된 문자열)<ul>
<li>INSTR(&#39;A#B#C#&#39;, &#39;#&#39;, 3, 2) =&gt; 6</li>
<li>m의 값이 음수가 되면 스캔 방향도 바뀜</li>
</ul>
</li>
<li>LTRIM(대상, 삭제문자열) / RTRIM(대상, 삭제문자열) : 특정 문자열을 왼쪽에서 삭제<ul>
<li>~= TRIM(대상) : 양쪽에서 삭제 (ORACLE에서는 공백만 가능)<ul>
<li>LPAD(대상, n, 문자열) / RPAD(대상, n, 문자열) : 대상의 왼쪽 / 오른쪽에 문자열을 삽입해서 총 n만큼의 길이를 반환함</li>
<li>ex) RPAD(&#39;ABC&#39;, 5, &#39;#&#39;) =&gt; ABC##</li>
</ul>
</li>
</ul>
</li>
<li>CONCAT(대상1, 대상2) : 문자열 결함</li>
<li>LENGTH(대상) : 문자열 길이 반환<ul>
<li>LENGTHB(대상) : 문자열의 바이트 수 반환</li>
</ul>
</li>
<li>REPLACE(대상, 찾을문자열, 바꿀문자열) : 문자열 치환<ul>
<li>REPLCAE(&#39;ABBA&#39;, &#39;AB&#39;, &#39;ab&#39;) =&gt; abBA</li>
</ul>
</li>
<li>TRANSLATE(대상, 찾을문자열, 바꿀문자열) : 글자(char)를 1대1로 치환<ul>
<li>바꿀문자열을 생략할 수 없음, 빈 문자열 전달시 전체가 Null을 반환</li>
<li>TRANSLATE(&#39;ABBA&#39;, &#39;AB&#39;, &#39;ab&#39;) =&gt; abba
cf. ORACLE vs SQL Server</li>
</ul>
</li>
<li>SUBSTR vs SUBSTRING</li>
<li>LENGTH vs LEN</li>
<li>INSTR vs CHARINDEX<h3 id="숫자형-함수">숫자형 함수</h3>
</li>
<li>ABS(숫자) : 절댓값</li>
<li>ROUND(숫자, 자리수) : 반올림</li>
<li>TRUNC(숫자, 자리수) : 버림</li>
<li>SIGN(숫자) : 양수면 1, 음수면 -1, 0이면 0 반환</li>
<li><strong>FLOOR(숫자)</strong> : 작거나 같은 최대 정수 = 내림</li>
<li><strong>CEIL(숫자)</strong> : 크거나 같은 최대 정수 = 올림</li>
<li>MOD(숫자1, 숫자2) : 숫자1 % 숫자2</li>
<li>POWER(m, n) : m의 n 제곱</li>
<li>SQRT(숫자) : 루트값<h3 id="날짜형-함수">날짜형 함수</h3>
DBMS마다 다르기에 출제율이 낮을 것이라 예상됨</li>
<li>SYSDATE : 현재 날짜와 시간 리턴</li>
<li>CURRENT_DATE : 현재 날짜</li>
<li>CURRENT_TIMESTAMP : 현재 타임스탬프</li>
<li>ADD_MONTHS(날짜, n) : 날짜에서 n개월 후</li>
<li>MONTH_BETWEEN(날짜1, 날짜2) : 날짜1과 날짜2 사이의 개월수</li>
<li>LAST_DAY(날짜) : 주어진 월의 마지막 날짜 리턴</li>
<li>NEXT_DAY(날짜, n) : 오는 요일의 날짜를 리턴<ul>
<li>n은 숫자지만 1 : 일요일, 2 : 월요일 , ... , 7 : 토요일</li>
</ul>
</li>
<li>ROUND(날짜, 자리수) : 날자 반올림<ul>
<li>ex) ROUND(Date(&quot;2025/02.20 14:31:00&quot;), &#39;MONTH&#39;) =&gt; 2025-03-01 0:00 &lt;- 맞나...?</li>
<li>기준은 무조건 15, 16부터 올림</li>
</ul>
</li>
<li>TRUNC(날짜, 자리수) : 날짜 버림
월과 관련된 함수가 많은 이유 : 날짜에 + - 숫자를 하면 일 수가 바뀜, 그렇기에 월과 관련된 기능들을 두는 것
cf. ORACLE vs SQL Server</li>
<li>SYSDATE vs GETDATE</li>
<li>ADD_MONTHS vs DATEADD</li>
<li>MONTH_BETWEEN vs DATEDIFF<h3 id="변환-함수">변환 함수</h3>
</li>
<li>TO_NUMBER(문자)</li>
<li>TO_CHAR(대상, 포멧) : 대상은 숫자 / 날짜, 결과는 문자<ul>
<li>ex) TO_CHAR(9000, &#39;9,999&#39;) =&gt; 9,000, TO_CHAR(9000, &#39;09999&#39;) =&gt; 09000<ul>
<li><code>9,999</code> : 천 단위 구분자 찍어줌 / <code>0~</code> : 부족한 자리수를 0으로 채움</li>
</ul>
</li>
</ul>
</li>
<li>TO_DATE(문자, 포멧) : 포멧 형식에 맞게 읽어 날짜로 리턴<ul>
<li>ex) TODATE(&#39;2024/01/01&#39;, &#39;YYYY/MM/DD&#39;)</li>
</ul>
</li>
<li>FORMAT(날짜, 포멧) : 날짜의 포멧 변경<ul>
<li>SQL Server의 함수, FORMAT(GETDATE(), &#39;YYYY&#39;) =&gt; 2025</li>
</ul>
</li>
<li>CAST(대상 AS 데이터 타입) : 타입 캐스팅<ul>
<li>CAST(&#39;100&#39; AS int)<h3 id="그룹-함수">그룹 함수</h3>
</li>
</ul>
</li>
<li>COUNT, SUM, AVG, MIN, MAX, VARIANCE(분산), STDDEV(표준편차)<ul>
<li>NULL은 무시하고 연산
ORACLE vs SQL Server</li>
</ul>
</li>
<li>VARIANCE vs VAR</li>
<li>STDDEV vs STDEV<h3 id="일반-함수"><strong>일반 함수</strong></h3>
</li>
<li>DECODE(대상, 값1, 리턴1, 값2, 리턴2 ... 그 외 리턴) : 값과 리턴을 매핑 &lt;- switch네<ul>
<li>DECODE(DEPTNO, 10, A, B) &lt;- DEPTNO의 값이 10이면 A, 아니면(디폴트) B</li>
<li>디폴트가 없을 경우 Null 반환</li>
</ul>
</li>
<li>MVL(대상, 치환값) : Null 치환</li>
<li>MVL2(대상, 치환값1, 치환값2) : Null일때와 Null이 아닐 때 모두 치환<ul>
<li>Null이 아니면 치환값 1, Null이면 치환값 2</li>
</ul>
</li>
<li>NVL2(COMM, COMM*1.1, 0) =&gt; COMM1*1 or 0</li>
<li>COALESCE(대상1, 대상2, ... , 그 외 리턴) : 널이 아닌 값을 리턴하는 함수<ul>
<li>대상 1이 Null이 아니면 1을 반환, Null이면 대상2로 ...</li>
</ul>
</li>
<li>ISNULL(대상, 치환값) : SQL Server의 치환 함수</li>
<li>NULLIF(대상1, 대상2) : 두 값이 같으면 Null, 다르면 대상 1 반환</li>
<li>CASE문 : 조건별 치환 및 연산 수행 (대소 비교 가능)<pre><code class="language-SQL">SELECT SCORE, GRADE
  CASE WHEN SCORE &lt; 80 THEN &#39;C&#39;,
       WHEN SCORE &lt; 90 THEN &#39;B&#39;,
                       ELSE &#39;A&#39;
      END AS GRADE
FROM EMP;</code></pre>
SCORE 테이블에서 80보다 작으면 C를, 90보다 작으면 B를, 보다 크면 A를 GRADE 속성값으로
만약 동등 비교고 대상이 중복되면 SCORE을 제외해도 됨<pre><code class="language-SQL">SELECT AREA_CODE, AREA_NAME
  CASE AREA_CODE WHEN 031 THEN &#39;Gyeonggi&#39;,
                 WHEN 054 THEN &#39;Gyeongbuk&#39;,
                 WHEN 051 THEN &#39;Busan&#39;,
                          ELSE &#39;DK&#39;
      END AS AREA_NAME
FROM EMP;</code></pre>
<h1 id="2과목-4단원-where-절">2과목 4단원, WHERE 절</h1>
<h2 id="개념">개념</h2>
</li>
<li>조건에 맞는 데이터만 조회하고싶을 경우, 행을 선택</li>
<li>여러 조건을 동시에 전달할 수 있음 (AND와 OR로 조건 연결)</li>
<li>Null 조회 시 <code>IS NULL</code> / <code>IS NOT NULL</code> 연산자를 사용해야 함<h2 id="연산자">연산자</h2>
</li>
<li><code>=</code>, <code>!=</code>,<code>&lt;&gt;</code> , <code>&gt;</code>, <code>&gt;=</code>, <code>&lt;</code>, <code>&lt;=</code></li>
<li><code>BETWEEN a AND b</code></li>
<li><code>IN(a, b, c)</code></li>
<li><code>LIKE</code> : 패턴을 가진 조건 검색 (정규표현식과 유사해 보임)</li>
<li><code>IS NULL</code> / <code>IS NOT NULL</code></li>
<li><code>A AND B</code>, <code>A OR B</code>, <code>NOT A</code><h3 id="주의-사항">주의 사항</h3>
문자나 날짜 상수 표현 시 반드시 홑따옴표 사용<h3 id="예시">예시</h3>
<pre><code class="language-SQL">SELECT EMPNO, ENAME, SAL
  FROM EMP
  WHERE NOT ENAME = &#39;SMITH&#39; -- ENAME != SMITH가 더 낫겠지만...
  AND SAL &gt;= 1500;
</code></pre>
</li>
</ul>
<p>-- &quot;%&quot;: 모든, &quot;_&quot;: 한 자리
SELECT * FROM EMP
    WHERE ENAME LIKE &#39;S%&#39;; -- 이름이 S로 시작하는 사원</p>
<p>SELECT * FROM EMP
    WHERE ENAME LIKE &#39;S_M%&#39;; -- S로 시작하고 세 번째 문자가 M인 사원</p>
<pre><code># 2과목 5단원, GROUP BY 절과 HAVING 절
## GROUP BY 개념
- 특정 조건에 따라 그룹으로 분리하여 계산할 때 쓰임
- 나열할 때는 `,`를 사용
- 제외할 대상이 있다면 미리 WHERE 절에서 제외하는게 효율적
    - WHERE -&gt; GROUP BY 순서로 실행됨
- GROUP BY로 얻은 값을 WHERE로 필터링할 수 없음 (WHERE이 먼저기에)
    - 따라서 GROUP BY 뒤에 HAVING절로 필터링함
```SQL
SELECT JOB, AVG(SAL) AS AVG_SAL -- 직업별 평균 구하기
    FROM EMP
    GROUP BY JOB;

SELECT DEPTNO, AVG(SAL) AS AVG_SAL -- 평균 급여가 3000 이상인 부서만
    FROM EMP
    GROUP BY DEPTNO
    HAVING AVG(SAL) &gt;= 3000;</code></pre><h1 id="2과목-6단원-order-by-절">2과목 6단원, ORDER BY 절</h1>
<h2 id="개념-1">개념</h2>
<ul>
<li><p>정렬할 때 사용하는 절</p>
</li>
<li><p>SELECT 문의 마지막에 배치되며 마지막에 수행됨</p>
<ul>
<li>SELECT절 뒤에있는 유일한 절이기에 유일하게 Column 별칭을 사용할 수 있음</li>
</ul>
</li>
<li><p>1차 정렬, 2차 정렬 가능</p>
<ul>
<li>ASC와 DESC가 있음, 디폴트는 ASC (생략 가능)</li>
<li>2차 정렬 : 첫 번째 조건의 값이 같은 경우 두 번째 조건으로 정렬</li>
</ul>
</li>
<li><p>숫자도 들어갈 수 있음 (숫자는 SELECT에 열거되어있는 순서의 대상을 의미)</p>
<h3 id="2차-정렬-예시">2차 정렬 예시</h3>
<pre><code class="language-SQL">SELECT EMPNO, ENAME, JOB, SAL, COMM -- JOB 순으로 정렬되지만 같을경우 COMM 순으로
  FROM EMP
  ORDER BY JOB ASC, COMM DESC;</code></pre>
<h3 id="null의-정렬">NULL의 정렬</h3>
</li>
<li><p>ORACLE 디폴트는 맨 마지막, SQL Server는 맨 먼저 정렬</p>
<ul>
<li>ORACLE의 경우 <code>NULL LAST</code> / <code>NULL FIRST</code>로 제어 가능<h1 id="2과목-7단원-조인">2과목 7단원, 조인</h1>
</li>
</ul>
</li>
<li><p>여러 테이블을 사용하거나 참조할 경우, 관계 규칙을 연결해서 함께 조회</p>
</li>
<li><p>FROM에 조회할 테이블을 (ORACLE 기준 : <code>,</code>로 이어) 나열 (ANSI 기준 : OUTER JOIN시 순서 상관 있음) &lt;- ORACLE &lt;-&gt; ANSI 변환 문제 기출</p>
</li>
<li><p>N개의 Table이 Join 되어야 하면 최소 N-1개의 Join 조건이 필요, 부족하면 불필요한 데이터의 조합이 출력될 수 있음 (카타시안 곱)</p>
</li>
<li><p>사원 테이블</p>
<h2 id="종류">종류</h2>
<h3 id="조건-형태">조건 형태</h3>
</li>
<li><p>EQUI JOIN : 동등 조건 (가장 많은 JOIN 형태)</p>
<pre><code class="language-SQL">SELECT T1.C, T2,C
  FROM T1, T2
  WHERE T1.C = T2.C</code></pre>
</li>
<li><p>NOT EQUI JOIN : 동등 조건이 아닐 경우 (크다/작다, IN ... )</p>
<pre><code class="language-SQL">SELECT E.NAME, E.SAL, S.GRADE
  FROM EMP E, SAL.GRADE S
  WHERE E.SAL BETWEEN S.LLOSAL AND S.HISAL;</code></pre>
<h3 id="조인-결과">조인 결과</h3>
</li>
<li><p>INNER JOIN : 조건에 성립하는 데이터만 출력</p>
</li>
<li><p>OUTER JOIN : 성립하지 않는 데이터도 출력</p>
<ul>
<li>기준 테이블의 위치에 따라 LEFT/RIGHT/FULL로 나뉨 &lt;- ANSI 표준에서 테이블 순서가 중요한 이유</li>
</ul>
</li>
<li><p>NATURAL JOIN : 두 테이블에 같은 이름으로 자연 연결</p>
</li>
<li><p>CROSS JOIN : 조건 생략 시 발생 가능한 모든 행을 출력 (카타시안 곱 발생)</p>
</li>
<li><p>SELF JOIN : 하나의 테이블을 두 번 이상 참조하여 연결하는 조인</p>
<ul>
<li>앞에서 다뤘었음<h1 id="2과목-8단원-표준-조인">2과목 8단원, 표준 조인</h1>
<h2 id="정의">정의</h2>
</li>
</ul>
</li>
<li><p>ANSI 표준으로 작성되는 INNER JOIN, CROSS JOIN, NATURAL JOIN, OUTER JOIN</p>
<ul>
<li>ORACLE에서는 이러한 조인 방식을 쓰지 않음<h2 id="inner-join">INNER JOIN</h2>
</li>
</ul>
</li>
<li><p>INNER 생략 가능</p>
</li>
<li><p>반드시 조건절을 USING / ON 으로 작성해야 함</p>
<h3 id="on-절">ON 절</h3>
<ul>
<li>조건을 명시 (ORACLE과 달리 WHERE과 ON이 명확히 구분됨)</li>
</ul>
</li>
<li><p>괄호 생략 가능</p>
</li>
<li><p>이름이 같은 컬럼이 있다면 출처를 밝혀야 함</p>
<pre><code class="language-SQL">SELECT TABLE1.COMUMN1, TABLE2.COLUMN2
  FROM TABLE1 INNER JOIN TABLE2
  ON (TABLE1.JOIN_COLUMN1 = TABLE2.JOIN_COLUMN2);</code></pre>
<h3 id="using-절">USING 절</h3>
</li>
<li><p>조인할 컬럼명이 같을 경우</p>
</li>
<li><p>괄호 필수</p>
</li>
<li><p>테이블 출처는 불필요</p>
<pre><code class="language-SQL">SELECT EMP.ENAME, DEPT.DNAME
  FROM EMP JOIN DEPT
  USING(DEPTNO);</code></pre>
<h3 id="natural-join">NATURAL JOIN</h3>
</li>
<li><p>조건을 명시하지 않아도 JOIN 됨</p>
</li>
<li><p>두 테이블 간의 동일한 이름을 갖는 <code>모든</code> 컬럼들에 대해 EQUI JOIN</p>
<ul>
<li>USING, ON, WHERE로 조건 정의 불가</li>
<li>NULL은 연결되지 않음<pre><code class="language-SQL">SELECT T1.ENAME, T2.DNAME
FROM EMP NATURAL JOIN DEPT;</code></pre>
<h3 id="cross-join">CROSS JOIN</h3>
</li>
</ul>
</li>
<li><p>JOIN 조건이 없을 경우, 생성 가능한 모든 데이터들의 조합 (카타시안곱)</p>
<pre><code class="language-SQL">SELECT T1.C1, T2.C2
  FROM T1 CROSS JOIN T2;</code></pre>
<h3 id="outer-join">OUTER JOIN</h3>
</li>
<li><p>조인조건이 성립되지 않아도 출력을 원할 때, 무엇을 기준으로 JOIN할 것인지에 따라. </p>
</li>
<li><p>OUTER 생략 가능 (<code>LEFT OUTER JOIN</code> -&gt; <code>LEFT JOIN</code>)</p>
<h4 id="left-outer-join">LEFT OUTER JOIN</h4>
</li>
<li><p>왼쪽 테이블의 데이터는 생략되지 않도록 함. (JOIN 조건이 성립하지 않아도, NULL로 채움 =&gt; 무조건 있음)
```SQL</p>
</li>
<li><ul>
<li>ORACLE 표준
SELECT *
 FROM STUDENT S, PROFESSOR P
 WHERE S.PROFNO = P.RROFNO(+) -- (+)가 LEFT OUTER JOIN을 의미
 AND S.GRADE IN (1, 4);<pre><code></code></pre></li>
</ul>
</li>
</ul>
<pre><code class="language-SQL">-- ANSI 표준
SELECT S.STUDNO, S.NAME AS 학생명, S.GRADE, S.PROFNO, 
        P.PROFNO, P.NAME AS 교수명
    FROM STUDENT S LEFT OUTER JOIN PROFESSOR P -- 테이블 순서가 중요하겠죠? 이거 기준으로 왼쪽 / 오른쪽이 될테니
    ON S.PROFNO = P.PROFNO
    WHERE S.GRADE IN (1,4);</code></pre>
<h4 id="right-outer-join">RIGHT OUTER JOIN</h4>
<ul>
<li>마찬가지<h4 id="full">FULL</h4>
</li>
<li>양 테이블 모두 생략되는 컬럼은 없음</li>
<li>ORACLE은 이 결과를 지원하지 않음<ul>
<li>다만 LEFT OUTER JOIN 결과와 RIGHT OUTER JOIN 결과의 UNION으로 구현할 수 있음
```SQL</li>
</ul>
</li>
<li><ul>
<li>ORACLE 표준
SELECT *
 FROM STUDENT S, PROFESSOR P
 WHERE S.PROFNO = P.PROFNO(+)
 UNION
SELECT *
 FROM STUDENT S, PROFESSOR P
 WHERE S.PROFNO(+) = P.PROFNO;
<code></code>SQL</li>
</ul>
</li>
<li><ul>
<li>ANSI 표준
SELECT S.STUDNO, S.NAME AS 학생명, S.GRADE, S.PROFNO, <pre><code> P.PROFNO, P.NAME AS 교수명</code></pre> FROM STUDENT S FULL OUTER JOIN PROFESSOR P
 ON S.PROFNO = P.PROFNO;
```</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 기본형의 래퍼클래스 - 정수를 Null로 두는 방법]]></title>
            <link>https://velog.io/@dev_hogun/Java-%EA%B8%B0%EB%B3%B8%ED%98%95%EC%9D%98-%EB%9E%98%ED%8D%BC%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%A0%95%EC%88%98%EB%A5%BC-Null%EB%A1%9C-%EB%91%90%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@dev_hogun/Java-%EA%B8%B0%EB%B3%B8%ED%98%95%EC%9D%98-%EB%9E%98%ED%8D%BC%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%A0%95%EC%88%98%EB%A5%BC-Null%EB%A1%9C-%EB%91%90%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Fri, 21 Feb 2025 05:24:10 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-상황">문제 상황</h3>
<p>빌더 패턴을 구현하고 있었다. 보통은 필수 매개변수를 빌더의 생성자에 두지만, 나는 빌터 패턴의 점진적 생성(?)을 차용하고 싶었기에 마지막에, 빌더를 통해 원래 클래스를 생성할 때 검증을 하고 싶었다. </p>
<pre><code class="language-java">public static class LottoRepositoryBuilder {
    private List&lt;Lotto&gt; purchasedLotto;
    private List&lt;Integer&gt; winningNumbers;
    private int bonusNumber; &lt;- int로 선언

    public LottoRepositoryBuilder setPurchasedLotto(List&lt;Lotto&gt; purchasedLotto) {
        (설정 로직)
    }

    public LottoRepositoryBuilder setWinningNumber(String line) {
        (설정 로직)
    }

    private LottoRepositoryBuilder setWinningNumbers(List&lt;Integer&gt; winningNumbers) {
        (설정 로직)
    }

    public LottoRepositoryBuilder setBonusNumber(int bonusNumber) {
        (설정 로직)
    }

    public LottoRepository build() {
        if (purchasedLotto == null
                || winningNumbers == null 
                || bonusNumber == null &lt;- int는 null이 존재하지 않음
        ) {
            throw new IllegalStateException(ExceptionConstants.INTERNAL_SERVER_ERROR.getMessage());
        }
        return new LottoRepository(this);
    }
}</code></pre>
<p>null로 생성되었는지를 검증하던 중 하나의 문제에 봉착했다. 기본형(primitive type)은 null이 존재하지 않는다. </p>
<h3 id="자바의-변수와-null">자바의 변수와 null</h3>
<p>자바에서 변수는 크게 기본형(primitive type)과 참조형(reference type), 두 종류로 나눌 수 있다. 기본형에는 int, float, char, boolean 등이 있고, 기본형을 제외한 모든 클래스는 참조형이다. 참조형 클래스는 Object 클래스를 상속받으며 우리가 직접 선언하는 클래스들 역시 참조형이다. 기본형과 참조형의 차이는 저장 방식에 차이가 있다. 일반적으로 클래스의 인스턴스나 메서드 내 데이터는 스택에 저장된다. 기본형은 스택에 직접적인 값 자체를 갖지만 참조형은 값에 대한 참조를 가진다. 그리고 그 값은 가비지컬렉터(Garbage Collector)가 관리하는 Heap 영역에 저장된다. 간단히 도식화하면 다음과 같이 표현된다. 
<img src="https://velog.velcdn.com/images/dev_hogun/post/fed14d02-9ccd-468c-8143-a8e35076d632/image.png" alt="">
기본형인 a는 Stack에 값 자체를 갖고, 참조형인 b 값에 대한 참조를 갖는다. c 역시 참조형이지만 참조값을 갖지 않는다. 그리고 이 상태를 null이라고 표현할 수 있다. 즉 null은 참조형 변수에 참조가 없는 경우를 일반적으로 표현한다. </p>
<h3 id="기본형의-래퍼클래스">기본형의 래퍼클래스</h3>
<p>다시 돌아와서 기본형을 변수로 쓴 이상 이를 null로 둘 수는 없다. 하지만 모든 기본형은 이미 자바에서 기본형에 대한 래퍼클래스를 만들어뒀다. </p>
<pre><code class="language-java">public final class Integer extends Number {
    ...
    /**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    private final int value; // 1081 line에 있음
    ...
}</code></pre>
<p>래퍼클래스는 &quot;감싸다&quot;라는 뜻 처럼 기본형을 객체로 감싸는 클래스이다. 
int의 래퍼클래스인 Integer은 래퍼클래스인 만큼 int에 없는 다양한 기능을 구현해뒀다. parseInt, compare, min, max 등 int 자체에는 없는 다양한 기능을 제공한다. 이 뿐 아니라 int를 감싸는 클래스인 만큼 코드를 짤 때 마치 int를 쓰는 것 처럼 Integer을 쓸 수 있게 만들어 준다. 결과적으로 이 Integer을 쓴다면 int의 기능을 가져가면서 null로도 둘 수 있게 된다. </p>
<h3 id="해결-방법">해결 방법</h3>
<p>나는 문제 상황에서 int로 선언한 부분을 Integer로 선언하여 해결했다. </p>
<pre><code class="language-java">private int bonusNumber; -&gt; private Integer bonusNumber;</code></pre>
<p>결론적으로 빌더 패턴에서 정수형 필드를 Integer로 두어 값을 채워야지만 클래스를 만들 수 있도록 했다. </p>
<h3 id="사족蛇足">사족(蛇足)</h3>
<p>null으로 둘 수 없는 기본형을 래퍼클래스를 이용하여 null로 둘 수 있게 했으나, null을 활용하여 문제를 해결하는 것이 좋은 습관은 아닌 것 같다. 
null이라는 개념을 만든 사람인 Tony Hoare은 null을 만든 것을 자신의 실수라고 말했다. null로 두는 것이 구현하기는 쉬웠지만 너무 많은 오류, 취약점 등을 만들어냈기 때문이라고 한다. 
빌더 패턴은 기본적으로 빌더를 생성할 때 필수 인자를 두어 필수 인자에 대한 별다른 검증 과정 없이 객체를 생성할 수 있다. 여기에 null을 두어 검증하는 식으로 구현하게되면 필연적으로 null 상태를 통제하는데 많은 힘을 쏟게되며 실수를 하게 되면 <code>NullPointerException</code>를 마주하게 될 것이다. </p>
<h3 id="참고-자료">참고 자료</h3>
<p><a href="https://velog.io/@wkdwoo/Primitive-type%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85-vs.-Reference-type%EC%B0%B8%EC%A1%B0%ED%83%80%EC%9E%85">Primitive type(원시타입) vs. Reference type(참조타입)</a>
  <a href="https://www.upwork.com/resources/what-is-null-in-java">Null In Java: Understanding the Basics</a>
  <a href="https://maximilianocontieri.com/null-the-billion-dollar-mistake">Null: The Billion Dollar Mistake</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Java/JS 언어 스터디, 2주차 회고]]></title>
            <link>https://velog.io/@dev_hogun/Java-JavaJS-%EC%96%B8%EC%96%B4-%EC%8A%A4%ED%84%B0%EB%94%94-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-2ysueaih</link>
            <guid>https://velog.io/@dev_hogun/Java-JavaJS-%EC%96%B8%EC%96%B4-%EC%8A%A4%ED%84%B0%EB%94%94-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-2ysueaih</guid>
            <pubDate>Fri, 21 Feb 2025 02:34:46 GMT</pubDate>
            <description><![CDATA[<p>언어 스터디 2주차를 마치며...</p>
<hr>
<h2 id="summary">Summary</h2>
<p>미션 기간: 2/8(토) - 2/14(금)
코드리뷰 기간: 2/8(일) - 2/11(화)
참여 인원: 13명 (스터디원 11명 + 진행자 2명)
평균 스터디 시간 : 2시간 10분</p>
<hr>
<h2 id="나의-미션-구현">나의 미션 구현</h2>
<blockquote>
<p><a href="https://github.com/Java-JavaScript-Language-Stuty/java-racingcar-7/pull/4">제출한 미션 파일</a></p>
</blockquote>
<p>2주차 때는 의존성 분리를 통한 테스트에 주안점을 뒀다. </p>
<h3 id="인터페이스를-통한-의존성-분리">인터페이스를 통한 의존성 분리</h3>
<pre><code class="language-java">package racingcar.view;

import racingcar.utils.MessageConstants;
import racingcar.view.provider.InputProvider;

public class InputView {
    private final InputProvider inputProvider;

    public InputView(InputProvider inputProvider) {
        this.inputProvider = inputProvider;
    }

    public String getCarName() {
        (입력 로직)
    }

    public int getTrialNumber() {
        (입력 로직)
}</code></pre>
<p>이 코드는 뷰, 그중에서도 입력을 담당하는 InputView이지만, Console과 직접적인 의존성을 갖지 않도록 설계했다. InputProvider라는 인터페이스를 두고, 이에 대한 각각의 구현체를 주입받아 동작하는 식이다. </p>
<pre><code class="language-java">public interface InputProvider {
    String readLine() throws IOException;
}</code></pre>
<p>InputProvider라는 인터페이스에 뷰에서 쓸 readLine 메서드를 뒀다. </p>
<pre><code class="language-java">public class WoowaInputProvider implements InputProvider {
    @Override
    public String readLine() {
        return Console.readLine();
    }
}</code></pre>
<p>프로그램을 동작시킬 때는 Console을 통해 입출력을 진행한다. </p>
<pre><code class="language-java">public class MockInputProvider implements InputProvider {
    private final String[] inputs;
    private int index = 0;

    public MockInputProvider(String... inputs) {
        this.inputs = inputs;
    }

    @Override
    public String readLine() throws IOException {
        if (index &lt; inputs.length) {
            return inputs[index++];
        }
        throw new IOException();
    }
}</code></pre>
<p>그리고 테스트를 할 때는 입력을 모방한 가짜 입력을 만들어 입력 기능을 테스트했다. 생성할 때 입력값을 넣어두고 readLine 메서드를 호출하면 하나씩 꺼내주는 방식이다. 테스트 코드는 다음과 같다. </p>
<pre><code class="language-java">@BeforeEach
void setUp() {
    inputView = new InputView(new MockInputProvider(&quot;pobi,woni,jun&quot;, &quot;5&quot;));
    outputView = new OutputView();
}

@Test
@DisplayName(&quot;차 이름 테스트&quot;)
void carNameInputTest() {
    String carNames = inputView.getCarName();
    assertEquals(&quot;pobi,woni,jun&quot;, carNames);
}</code></pre>
<p>즉, 시작 전 모의 입력값을 정해두고 로직을 테스트하는 것이다. 
물론 단순한 입력 테스트에 인터페이스를 쓰는게 코드의 복잡도를 증대시킬 수는 있겠지만, 솔직히 말하면 그러한 필요성을 따지기 전에 한 번 해보고 싶어서 해봤다. </p>
<h3 id="메서드를-다른-메서드의-인자로-주입">메서드를 다른 메서드의 인자로 주입</h3>
<p>유사하게 메서드의 파라미터에 다른 메서드를 주입받아 쓰도록해서 의존성을 분리하기도 했다. </p>
<pre><code class="language-java">public List&lt;CarMovementDto&gt; playTurn(Supplier&lt;Boolean&gt; movementFunction) {
    race.advance(movementFunction);
    return race.getResult();
}

(Race 클래스에 있는advance 메서드의 내부 로직)
public void advance(Supplier&lt;Boolean&gt; movementFunction) {
    cars.forEach((car) -&gt; {
        if (movementFunction.get()) {
            car.move();
        }
    });
    turn++;
}</code></pre>
<p>CarControlService 메서드에서 자동차를 이동시키는 로직이다. 자동차는 이동 행동에는 2가지 경우의 결과가 있다. <code>이동하거나</code>, <code>이동하지 않거나</code>. 이는 랜덤하게 정해지지만, 랜덤 로직 자체는 자동차의 이동과 분리되는 관심사라고 바라봤다. 이에 <code>Supplier&lt;Boolean&gt;</code>을 통해 진리값을 반환하는 함수를 가져왔다. 그리고 이것 역시 원하는 함수를 집어넣을 수 있기에 우연한 결과값에 의존되지 않은 채로, Mocking을 활용하여 효율적인 테스트를 구현할 수 있다. </p>
<hr>
<h2 id="스터디에-대한-회고">스터디에 대한 회고</h2>
<h3 id="like-좋았던-점">Like (좋았던 점)</h3>
<ul>
<li>스터디원들의 생각은 잘 모르겠지만, 코드에서 배울게 많은 것 같다. <ul>
<li>이전 스터디에서 여러 코드를 봐서 이번에도 그 정도일 것이라 생각했는데, 각자의 생각이나 개성이 이전 스터디보다 더 강한 것 같다. </li>
</ul>
</li>
<li>스터디가 여전히 잘 지속된다. <h3 id="learned-배운-점">Learned (배운 점)</h3>
</li>
<li>대부분의 스터디원들이 MVC 패턴을 도입하려고 노력했다. </li>
<li>새로운 접근을 많이 볼 수 있는 것 같다. <ul>
<li>옵저버 패턴 도입, 인터페이스 사용 등등... 다양한 접근을 살펴볼 수 있어서 좋았다. <h3 id="laked-부족한-점">Laked (부족한 점)</h3>
</li>
</ul>
</li>
<li>스터디 관리의 자동화 필요성을 느낀다. <ul>
<li>10명이 넘는 사람들의 스터디를 관리(라기에도 뭐하지만)하는 것이 생각보다 어렵다. </li>
<li>(내 성격이 이유일 수도 있겠지만) 계속 스터디에 대해 독촉하는 것이 힘들다. </li>
<li>주먹구구식으로 확인하는 만큼 즉각적인 피드백이나 효율적인 관리가 부진했다. 이는 스터디원의 입장에서도 부정적인 경험을 준다. <ul>
<li>코드리뷰봇을 도입하자는 의견도 있었지만,,, 이제 미션 하나 남은 시점에서 그것까지 테스트하기는 힘들었다. </li>
</ul>
</li>
</ul>
</li>
</ul>
<p>-&gt; 이 부분에 대해서는 이번 스터디에서 개선하기는 어려울 것 같다. 만약 다음 스터디를 할 기회가 있다면, 혹은 주변에 스터디를 할 사람이 있다면 스터디 전에 시스템을 구축해둘 필요가 있다. </p>
<ul>
<li>학습 정리글이 기간 내 잘 안 올라왔다... 기타 일정이 밀리고있다. </li>
<li>일정이 계속 밀린다...</li>
<li>코드리뷰를 못 받은 사람이 있었다.....<h3 id="loged-for-희망-사항">Loged for (희망 사항)</h3>
</li>
<li>계속해서 코드가 발전했으면 좋겠다. <ul>
<li>1주차 코드와 2주차 코드가 눈에띄게 차이가 나는 만큼 3주차의 코드도 기대가 된다. </li>
</ul>
</li>
<li>단위테스트... 는 여전히 아쉬운 것 같다. </li>
<li>미션 구현은 한 주 남았으니 완주... 더 이상의 이탈자는 발생해서는 안된다...</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Java/JS 언어 스터디, 1주차 회고]]></title>
            <link>https://velog.io/@dev_hogun/Java-JavaJS-%EC%96%B8%EC%96%B4-%EC%8A%A4%ED%84%B0%EB%94%94-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dev_hogun/Java-JavaJS-%EC%96%B8%EC%96%B4-%EC%8A%A4%ED%84%B0%EB%94%94-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Thu, 13 Feb 2025 13:45:51 GMT</pubDate>
            <description><![CDATA[<p>언어 스터디 1주차 분량을 마쳤다. </p>
<hr>
<h2 id="summary">Summary</h2>
<p>미션 기간: 2/1(토) - 2/7(금)
코드리뷰 기간: 2/8(일) - 2/11(화)
참여 인원: 16명 (스터디원 14명 + 진행자 2명)
평균 스터디 시간 : 2시간 22분</p>
<hr>
<h2 id="나의-미션-구현">나의 미션 구현</h2>
<p>미션은 수식을 입력하면 계산해서 값을 반환하는 것이었다. 
계산은 덧셈밖에 없었으며 &#39;,&#39;, &#39;:&#39; 등이 연산자로 쓰인다. </p>
<pre><code class="language-bash">&gt;&gt; 1,2:3
결과 : 6</code></pre>
<blockquote>
<p><a href="https://github.com/woowacourse-precourse/java-calculator-7/pull/407">우테코할 때 제출한 미션 PR</a>
<a href="https://github.com/Java-JavaScript-Language-Stuty/java-calculator-7/pull/4">스터디에서 한 미션 PR</a></p>
</blockquote>
<p>첫 번째 코드는 자바로 거의 처음 코딩해보는 것이었기에 언어에 대해 미숙했다. 또 기존에 짜왔던 코드는 백준 문제풀이 같이 한 파일로 짜거나 뭣도 모르고 프레임워크를 쓰던 코드들밖에 없었기에 책임, 의존성과 같은 개념이 생소했다. 하지만 수식을 연산자와 피연산자가 이어져있는 형태로 표현한 것은 좀 참신했다고 생각한다. </p>
<p>두 번째 코드는 깔끔하고 담백한 코드를 짜려고 노력해서 나온 코드이다. MVC 패턴을 적용하고, </p>
<p>우테코가 10월쯤이었나, 그렇다면 4달정도 지난 시점에서 같은 문제를 풀어보니 내가 한 노력들이 의미없지는 않은 것 같다. </p>
<pre><code class="language-java">static public int sequentialAdd(Operators operators, Expression expression) {
    int sum = 0;
    boolean addFlag = true;
    LinkedList&lt;Object&gt; tokens = expression.getExpression();

    for (Object token : tokens) {
        if (token instanceof Integer &amp;&amp; addFlag) {
            addFlag = false;
            sum = addExact(sum, (int) token);
        } else if (token instanceof Character &amp;&amp; !addFlag &amp;&amp; operators.contains((char) token)) {
            addFlag = true;
        } else {
            throw new IllegalArgumentException(Message.CALCULATION_EXCEPTION_MESSAGE);
        }
    }
    if (addFlag) {
        throw new IllegalArgumentException(Message.CALCULATION_EXCEPTION_MESSAGE);
    }
    return sum;
}
</code></pre>
<p>다음은 기존 코드가 가진 문제를 총망라하는 하나의 함수이다. </p>
<h4 id="1-mvc에서-각-계층이-갖는-역할">1. MVC에서 각 계층이 갖는 역할</h4>
<p>우테코할 땐 MVC에 대한 이해가 없는 채로 시작했다. 다른 사람들이 하길래 왜 하는지도 모르고 도입해봤다. 모델은 대상에 대한 비지니스 로직을 포함해야하지만, 기존의 코드는 자료를 저장하는 구조로만 역할했다. 이에 Service 계층에서 모델의 비지니스 로직까지 흡수했다. </p>
<h4 id="2-api-사용-미숙">2. API 사용 미숙</h4>
<p>제네릭에 대한 이해가 낮아 충분히 제네릭을 쓸 수 있는 상황들에도 쓰지 않았다. Stream API를 잘 쓰지 않고 굳이 for문을 돌리는 것도 비효율적이었다. 사실 수식의 구성을 Object에 냅다 담아버리는 것도 처음이었기에 저지를 수 있는 실수였다. </p>
<h4 id="3-책임-과중">3. 책임 과중</h4>
<p>Service 계층에 책임이 과중된 것 외에도, 하나의 함수에서 여러 책임을 가져서 코드가 깊어지고 길어졌다. 위 코드에서도 변환, 검증, 연산의 책임이 뭉쳐있다. </p>
<h4 id="4-의존성">4. 의존성</h4>
<p>의존성에 대한 개념을 충분히 이해하지 못해서 DI(의존성 주입)을 하지 못했을 뿐더러 무엇을 정적 함수, 변수로 둬야하는지에 대한 이해도 부족했다. </p>
<hr>
<p>이번에 스터디에서 코드를 짤 때는 이와 같은 부분들을 최대한 보완해서 하려고 노력했다. 하지만 받은 리뷰가 별로 없어 지금의 코드에 무슨 문제가 있는지 충분히 파악하지는 못했다...</p>
<hr>
<h2 id="스터디에-대한-회고">스터디에 대한 회고</h2>
<p>4Ls 방법 (?)으로 회고를 진행해 보려 한다. </p>
<h3 id="like-좋았던-점">Like (좋았던 점)</h3>
<ul>
<li>이탈자 없이, 순탄히 첫 주차가 흘러갔다. </li>
<li>거의 모두가 매일 2시간씩 스터디했으며 모두가 미션 제출시각을 지켰다. </li>
<li>많은 스터디원들이 시간을 잘 준수했다. </li>
</ul>
<h3 id="learned-배운-점">Learned (배운 점)</h3>
<ul>
<li>MVC 패턴, API 등에 대한 연습을 했다. <ul>
<li>몇몇 스터디원이 MVC 패턴을 적용하기 위해 노력했다. </li>
<li>거의 모든 스터디원이 API를 적극적으로 활용하기위해 노력했다. </li>
</ul>
</li>
</ul>
<h3 id="laked-부족한-점">Laked (부족한 점)</h3>
<ul>
<li>방법과 절차를 찬찬히 설명할 필요가 있다. <ul>
<li>스터디원들이 코드 리뷰의 데드라인을 못 맞춘 줄 알았는데 리뷰를 게시하지 않아서(Finish Review를 누르지 않아서) 안 보였던 것이었다. </li>
</ul>
</li>
</ul>
<p>-&gt; 신경써서 일찍일찍 노티와 안내를 해야겠다. </p>
<ul>
<li>채점용 테스트코드가 부적절하다. <ul>
<li>백엔드에게만 너무 가혹하다. (JS에서 그렇게 문제가 없을줄은 몰랐죠...)</li>
<li>코드를 잘 짠 사람이 더 많이 통과하는 것 같지 않다. </li>
</ul>
</li>
</ul>
<p>-&gt; 채점용 테스트에 대해 스터디에서 얘기해볼 필요가 있다... 테스트코드를 굵직한 예외만 하고 동점일 경우 다른 기준을 통해 경품(?)을 주는게 어떨지 생각중이다.</p>
<ul>
<li>리뷰가 날카롭지 않았다...<ul>
<li>생각보다 비판적인 리뷰가 많지 않았다. 리뷰의 갯수 역시 충분하지 않았다. </li>
</ul>
</li>
<li>벌칙 수행이 제대로 되지 않는다...ㅠ</li>
<li>포멧이 종종 맞지 않는다. <ul>
<li>미션 파일의 입출력 포멧이 맞지 않거나 스터디에서 하기로한 양식에 맞지 않는 경우가 종종 있다</li>
</ul>
</li>
</ul>
<h3 id="loged-for-희망-사항">Loged for (희망 사항)</h3>
<ul>
<li>의존성에 대해 공부하면 좋을 것 같다. </li>
<li>단위테스트에 대해서도 공부할 필요가 있다. </li>
<li>스터디에 대한 열정이 지속되었으면 좋겠다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQLD 학습 정리] SQLD 1과목 - 데이터 모델링의 이해]]></title>
            <link>https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-SQLD-1%EA%B3%BC%EB%AA%A9-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EB%A7%81%EC%9D%98-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@dev_hogun/SQLD-%ED%95%99%EC%8A%B5-%EC%A0%95%EB%A6%AC-SQLD-1%EA%B3%BC%EB%AA%A9-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EB%A7%81%EC%9D%98-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Mon, 10 Feb 2025 12:58:13 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.youtube.com/watch?v=xmkaOqbTQRE">유튜브 강의 정리본</a> 입니다</p>
<h1 id="1과목-1단원-데이터-모델의-이해">1과목 1단원, 데이터 모델의 이해</h1>
<p>기초 용어</p>
<ul>
<li>DB : 데이터의 집합</li>
<li>DBMS : 데이터를 보다 잘 관리하기 위해 만든 시스템<ul>
<li>ex) Oracle, MSSQL ...<h2 id="1-데이터-모델링">1. 데이터 모델링</h2>
정의 : <u>현실의 데이터 요구사항을 구조화/단순화시키는 과정</u> =&gt; 관계를 정의
특징 : 추상화(일정한 형식으로), 단순화(단순하고 쉽게), 명확화(불분명함을 제거하여)<h3 id="1-1-용어">1-1. 용어</h3>
Table(Entity) : 엑셀의 표와 유사한 개념
논리적 - Entity, Attribute, Instance
물리적 - Table, Column, Row<h3 id="1-2-데이터-모델링의-세-관점">1-2. 데이터 모델링의 세 관점</h3>
<code>데이터 관점</code>: 업무간, 업무와 데이터간의 관계를 모델링
<code>프로세스 관점</code>: 업무의 프로세스 상에서 무엇이 필요한지, 무엇을 해야하는지를 모델링
<code>데이터와 프로세스 관점</code>: 업무 프로세스에 따라 각 데이터가 받는 영향을 모델링<h3 id="1-3-데이터-모델링의-3가지-요소">1-3. 데이터 모델링의 3가지 요소</h3>
대상(Entity): 업무가 관리하고자 하는 대상
속성(Attribute): 대상이 갖는 속성
관계(Relationship): 대상과 대상 사이의 관계<h3 id="1-4-데이터-모델링의-3단계">1-4. 데이터 모델링의 3단계</h3>
<h3 id="1단계--개념적-모델링">1단계 : 개념적 모델링</h3>
</li>
</ul>
</li>
<li>필요한 데이터를 개념적으로 설계</li>
<li>업무 분석 후 핵심 엔터티를 추출, 이들 간의 관계를 표현하기 위해 ERD를 작성</li>
<li>특징 : 업무 중심적, 포괄적<h3 id="2단계--논리적-모델링">2단계 : 논리적 모델링</h3>
</li>
<li>개념적 모델링을 토대로 세부속성, 식별자, 관계 등을 표현</li>
<li>데이터 정규화(데이터를 어떻게 분리할지) 수행</li>
<li>재사용성이 높음<h3 id="3단계--물리적-모델링">3단계 : 물리적 모델링</h3>
</li>
<li>설계한 것을 바탕으로 물리적으로 (<strong>실제 데이터 베이스를</strong>) 만드는 과정</li>
<li><strong>성능</strong>, 저장구조, 보안성, 권한 등을 규정
아래로 갈수록 구체화, 위로갈수록 추상화<h3 id="cf-유의점">cf. 유의점</h3>
<code>중복</code>(중복 데이터 지양), <code>비유연성</code>(유지보수가 쉽게), <code>비일관성</code>(타 데이터와의 연관성 고려) )-&gt; 이것들을 피해야 함<h2 id="2-스키마의-3단계-구조">2. 스키마의 3단계 구조</h2>
개념</li>
<li>데이터베이스의 구조와 제약 조건에 관한 전반적인 명세를 기술한 메타데이터의 집합
  =&gt; 모든 객체들에 대한 정보<h3 id="1단계--외부-스키마">1단계 : 외부 스키마</h3>
사용자가 보는 관점에서, 업무적인 관점에서 필요한 데이터를 정의 (사용자가 접근하는 대상)<h3 id="2단계--개념-스키마">2단계 : 개념 스키마</h3>
데이터베이스의 전체 논리적 구조를 정의
전체 데이터베이스의 개체, 속성, 관계, 데이터 타입 등을 정의<h3 id="3단계--내부-스키마">3단계 : 내부 스키마</h3>
물리적으로 어떻게 저장되는지를 정의
데이터의 저장 구조, 컬럼, 인덱스 등을 정의<h3 id="2-1-3단계-스키마의-독립성">2-1. 3단계 스키마의 독립성</h3>
독립성 : 한쪽이 바뀌어도 다른쪽(응용 프로그램)에 영향을 주지 않는 특성</li>
<li>논리적 독립성 : 논리적 데이터구조가 바뀌어도(개념 스키마가 달라져도) 응용 프로그램에 영향을 주지 않는 특성</li>
<li>물리적 독립성 : 물리적 구조가 변경되어도(내부 스키마가 달라져도) 응용 프로그램에 영향을 주지 않는 특성<h3 id="2-2-데이터-모델의-표기법-erd-entity-relationship-diagram">2-2. 데이터 모델의 표기법 (ERD: Entity Relationship Diagram)</h3>
논리적 설계 단계에서 만들어지는 엔터티와 이들의 관계를 시각적으로 표현한 다이어그램<h4 id="2-3-erd-작성-절차-6단계">2-3. ERD 작성 절차 (6단계)</h4>
</li>
</ul>
<ol>
<li>엔터티를 도출한 후 그린다</li>
<li>엔터티 배치</li>
<li>엔터티 간의 관계를 설정</li>
<li>관계명을 설정</li>
<li>관계의 참여도 기술</li>
<li>관계의 필수 여부를 확인<h1 id="1과목-2단원-엔터티">1과목 2단원, 엔터티</h1>
엔터티 : 독립적으로 식별 가능한 객체나 사물 =&gt; 대상</li>
</ol>
<ul>
<li>인스턴스(행)와 속성(열)으로 구성
ex)<pre><code>엔터티(Entity) : Student
속성(Attribute) : id, name, department
인스턴스(Instance)
  - id : 20233022
  - name : 박호건
  - department : 글로벌미디어학부</code></pre><h2 id="특징">특징</h2>
</li>
<li>식별자가 요구됨 (식별자 ~= 변수명)<ul>
<li>유일성이 보장되어야 함</li>
</ul>
</li>
<li>업무에서 관리하고자하는 정보</li>
<li>인스턴스들의 집합<ul>
<li>인스턴스가 하나 있다면 DB를 쓸 이유가 없음</li>
</ul>
</li>
<li>엔터티는 속성을 가짐<ul>
<li>2개 이상의 속성을 가짐</li>
</ul>
</li>
<li>업무 프로세스에 의해 이용<ul>
<li>안쓰이면 지워야</li>
</ul>
</li>
<li>다른 엔터티와 최소 1개 이상의 관계 성립<ul>
<li>관계가 없다면 도출 과정에 문제가 있는 것<h2 id="분류">분류</h2>
<h3 id="유형과-무형에-따른-분류">유형과 무형에 따른 분류</h3>
</li>
</ul>
</li>
<li>유형엔터티 : 물리적 형태가 있음 (ex. 사원, 물품 ...)</li>
<li>개념엔터티 : 물리적인 형태가 없음 (ex. 조직, 보험상품 ...)</li>
<li>사건엔터티 : 업무를 수행함에 따라 발생하는 엔터티 (ex. 주문, 청구 ...)<h3 id="발생-시점에-따른-분류">발생 시점에 따른 분류</h3>
기본 엔터티</li>
<li>원래 존재하는 정보</li>
<li>독립적으로 생성 (다른 엔터티의 관계로 생성되는게 아님)</li>
<li>자신의 고유한 주식별자를 가짐 (주식별자를 상속받지 않음)</li>
<li>ex) 사원, 부서, 고객, 상품 ...</li>
</ul>
<p>중심 엔터티</p>
<ul>
<li>기본엔터티로부터 발생되고 그 업무에서 중심적인 역할</li>
<li>ex) 계약, 사고, 청구, 주문, 매출 ...</li>
</ul>
<p>행위 엔터티 &lt;- 중심 엔터티의 확장</p>
<ul>
<li>2개 이상의 부모엔터티로부터 발생</li>
<li>자주 내용이 바뀌거나 데이터 양이 증가</li>
<li>ex) 주문(&lt;-고객,상품), 사원변경이력<h3 id="엔터티의-명명">엔터티의 명명</h3>
현업에서 사용하는 용어
가능하면 약자 사용을 자제
단수 명사 사용
유일성을 가져야 함
생성 의미대로 이름 부여<h3 id="엔터티--인스턴스-표기법">엔터티 &amp; 인스턴스 표기법</h3>
엔터티 : 사각형
관계 : 선
속성</li>
<li>IE 표기법 : 엔터티 이름 밑에 주식별자, 그 밑에 일반 속성 나열</li>
<li>Barker 표기법 : 엔터티 이름 밑에 속성 표기<ul>
<li>주식별자는 앞에 <code>#</code>을 붙임</li>
<li>그 외 nullable 등 역시 앞에 뭘 붙여서 표기<h1 id="1과목-3단원-속성">1과목 3단원, 속성</h1>
<h3 id="속성의-개념">속성의 개념</h3>
업무적으로 필요로하는 고유한 성질, 특징을 의미
더 이상 분리되지 않는 최소의 데이터 단위
ex) 학생 - 신상정보 - 이름, 학번, 학과 &lt;- 신상정보가 이름, 학번, 학과로 분리될 수 있음</li>
<li><blockquote>
<p>신상정보는 속성이 아니고 이름, 학번, 학과는 속성에 해당함</p>
</blockquote>
<h3 id="엔터티-인스턴스-속성의-관계">엔터티, 인스턴스, 속성의 관계</h3>
한 개의 엔터티는 2개 이상의 인스턴스 집합이어야 한다
한 개의 엔터티는 2개 이상의 속성을 갖는다
한 개의 속성은 1개의 속성값을 갖는다<h3 id="특징-1">특징</h3>
업무에서 필요해야함
주식별자에 함수적 종속성을 가져야 함</li>
</ul>
</li>
<li>ex) 학생의 주식별자 : 학번, 학번에 의해 결정되는 요인이어야 함. = 학번에 함수적 종속성을 띈다. <ul>
<li>학번이 다르면 이름이 다름 -&gt; 이름은 학번에 함수적 종속성을 띔
원자성 : 인스턴스가 해당 속성에 대해 단일하고 명확한 값을 가지는 것</li>
</ul>
</li>
<li>각 속성은 하나의 값을 가짐<h4 id="함수적-종속성">함수적 종속성</h4>
</li>
<li>어떤 속성의 값에 의해 다른 속성도 유일하게 결정되는 것 =&gt; 함수 종속 관계
완전 함수적 종속</li>
<li>PK를 구성하는 컬럼이 2개 이상일 경우, PK 값 모두에 의한 종속관계를 나타낼 때</li>
<li>ex) 쇼핑을 한다고 가정해보자. 주문번호와 상품번호가 있음 -&gt; 수량은 2개 모두를 봐야 알 수 있겠지
부분 함수적 종속</li>
<li>기본키 일부에 대해 종속될 때</li>
<li>ex) 인강을 본다고 가정해보자. 학생ID와 과목이 PK겠지만, 강사 이름은 오직 과목에만 종속됨<h3 id="분류-1">분류</h3>
<h4 id="특성에-따른-속성">특성에 따른 속성</h4>
기본 속성 : 업무로 부터 추출된 모든 속성 (사실상 가장 많음)
설계 속성 : 업무를 규칙화할 때 새로 만들어지는 속성 (ex. 상품코드, 지점코드)
파생 속성 : 기본/설계 속성에서 파생되는 속성 (일반적으로 계산된 값, ex. 합계, 평균, 이자 ...)<h4 id="엔터티-구성방식에-따른-분류">엔터티 구성방식에 따른 분류</h4>
PK(Primary Key, 기본키, 주식별자) : 인스턴스를 식별할 수 있는 속성
FK(Foreign Key, 외래키): 다른 엔터티와의 관계에서 포함된 속성
일반 속성 : 그 외<h4 id="분해-여부에-따른-속성">분해 여부에 따른 속성</h4>
단일 속성 : 하나의 의미로 구성 (ex. 회원ID, 이름)
복합 속성 : 여러개의 의미로 구성 (ex. 주소 - 시, 구, 동 등으로 분해 가능)
다중값 속성 : 여러개의 값을 가질 수 있음 (ex. 구매한 상품 &lt;- 하나의 고객이 여러개를 가지겠지)
속성의 명명규칙</li>
<li>업무에서 사용되어야</li>
<li>서술식으로는 사용되지 않음</li>
<li>약어 가급적 제한</li>
<li>유일한 명칭
도메인(Domain)</li>
<li>속성이 가질 수 있는 값의 범위</li>
<li>속성에 대한 데이터 타입, 크기, 제약사항 지정</li>
<li>ex) 중학교 - 학년 - <code>1, 2, 3</code><h1 id="1과목-4단원-관계">1과목 4단원, 관계</h1>
관계 : 엔터티간의 연관성</li>
<li>인스턴스간의 연관성을 파악하여 정의해야 함<h2 id="관계의-종류">관계의 종류</h2>
존재적 관계 : 하나의 엔터티가 다른 엔터티의 존재에 영향을 줌</li>
<li>ex) 사원 - 부서, 부서가 사라지면 사원은...
행위적 관계 : 엔터티간의 행위를 의미</li>
<li>ex) 고객 - 주문, 주문이 사라진다고 고객이 사라지는건 아님
cf. ERD에서는 존재적 관계와 행위적 관계를 구분하지 않음<h2 id="관계의-구성">관계의 구성</h2>
</li>
</ul>
<ol>
<li>관계명 (ex. 수강 이력)</li>
<li>차수 (Cardinality, 1:1, 1:多 ...)</li>
<li>선택성 (Optionality, 필수 여부)<h3 id="관계의-차수">관계의 차수</h3>
관계가 어떤식으로 연결되는지</li>
</ol>
<ul>
<li>1 대 1 관계<ul>
<li>완전 1 대 1 관계</li>
<li>선택적 1 대 1 관계</li>
</ul>
</li>
<li>1대 N 관계<ul>
<li>ex) 하나의 개체에 여러 사원이 종속</li>
</ul>
</li>
<li>N 대 N 관계<ul>
<li>카테시안 곱이 발생함<ul>
<li>카테시안 곱(Cartesian Product) : join의 WHERE 조건절이 잘못 기술되거나 없을 경우, 두 테이블의 행의 개수의 곱 만큼의 결과값이 산출됨 &lt;- 조합폭발 같은 느낌인듯</li>
</ul>
</li>
<li>관계 엔터티를 하나 더 둬서 1대 N 관계로 해소하는 것이 모범적<ul>
<li>ex) 학생 - 강의는 多 : 多 관계 =&gt; 학생 - 구매이력 (1 : 多) + 구매 이력 - 강의 (1 : 多)<h3 id="관계의-페어링">관계의 페어링</h3>
엔터티 안에 인스턴스가 개별적으로 관계를 가지는 것
관계 : 학생 - 강의</li>
</ul>
</li>
</ul>
</li>
<li>학생 테이블의과 강의 테이블의 카디널리티가 각각 N과 M이라면 차수는 N:M</li>
<li><blockquote>
<p>학생 A가 강의 B를 1월에 수강했고 성적은 A+을 받았다와 같은 페어링이 형성</p>
</blockquote>
<h1 id="1과목-5단원-식별자">1과목 5단원, 식별자</h1>
<h2 id="식별자의-개념">식별자의 개념</h2>
</li>
<li>엔터티를 대표할 수 있는 속성</li>
<li>유일한 식별자가 존재해야 함</li>
<li><code>식별자</code>는 논리 모델링에서 사용하는 용어, 물리 모델링에서는 <code>키(key)</code>라고 표현<h2 id="주식별자-특징">주식별자 특징</h2>
</li>
<li>유일성, 최소성(최소한의 속성), 불변성, 존재성(값이 존재해야 함 = Null을 허용하지 않음)<h2 id="식별자-분류">식별자 분류</h2>
<h3 id="대표성-여부에-따른-분류">대표성 여부에 따른 분류</h3>
</li>
<li>주식별자<ul>
<li>대표성을 가짐</li>
<li>유일성, 최소성 만족</li>
<li><blockquote>
<p>참조관계 연결</p>
</blockquote>
</li>
</ul>
</li>
<li>보조식별자<ul>
<li>대표성을 갖지 않음</li>
<li>유일성과 최소성 만족</li>
<li><blockquote>
<p>참조관계 연결 불가
주민번호가 학생의 식별자가 될 수 있겠지만, 보통은 학번을 쓰겠죠? -&gt; 주식별자 : 학번, 보조식별자 : 주민번호</p>
</blockquote>
<h3 id="생성-여부에-따른-분류">생성 여부에 따른 분류</h3>
</li>
</ul>
</li>
<li>내부식별자 : 타 엔터티 참조 없이 스스로 생성</li>
<li>외부식별자 : 타 엔텉티와 갖는 관계로 만들어지는 식별자 (외래키)<h3 id="속성-수에-따른-분류">속성 수에 따른 분류</h3>
</li>
<li>단일식별자 : 하나의 속성</li>
<li>복합식별자 : 2개 이상<h3 id="대체-여부에-따른-분류">대체 여부에 따른 분류</h3>
</li>
<li>본질식별자 (원조식별자) : 비지니스 프로세스에서 만들어지는 식별자</li>
<li>인조식별자 : 인위적으로 만들어지는 식별자, ex) AUTOINCREMENT, Hash, UUID<h2 id="식별자-표기법">식별자 표기법</h2>
<img src="https://velog.velcdn.com/images/dev_hogun/post/9301315e-9cdb-4a61-be05-ebc74eb48531/image.png" alt="">
<img src="https://velog.velcdn.com/images/dev_hogun/post/a2f54f2f-ee36-4fc0-9428-0bb091af4ae6/image.png" alt=""></li>
</ul>
<h2 id="주식별자-도출-기준">주식별자 도출 기준</h2>
<p>업무에서 자주 이용되는 속성을 주식별자로 지정
가급적 명칭이나 내역같은 이름은 피함
코드를 부여하여 주식별자로 사용
속성의 수를 최소한으로 구성 (7~8개 이상의 주식별자 구성은 인조식별자를 만드는게 나음)</p>
<h2 id="관계간-엔터티-구분">관계간 엔터티 구분</h2>
<h3 id="강한-개체">강한 개체</h3>
<ul>
<li>보다 독립적으로 존재할 수 있는 엔터티<ul>
<li>ex) 고객과 계좌가 있다면 고객은 강한 개체<h3 id="약한-개체">약한 개체</h3>
</li>
</ul>
</li>
<li>독립적으로 존재할 수 없는 엔터티<ul>
<li>ex) 고객과 계좌가 있다면 계좌는 약한 개체<h3 id="식별-관계와-비식별-관계">식별 관계와 비식별 관계</h3>
식별관계 : 하나의 엔터티의 기본키를 다른 기본키로 공유</li>
</ul>
</li>
<li>ERD서 실선으로 표시
비식별관계 : 강한 개체의 기본 키를 다른 엔터티의 일반 속성으로 관계를 가짐</li>
<li>ERD서 점선으로 표시<pre><code>  - ex) : 사원서의 부서번호는 비식별관계</code></pre><h2 id="key의-종류">Key의 종류</h2>
<h3 id="기본키primary-key">기본키(Primary Key)</h3>
</li>
<li>엔터티를 대표할 수 있는 키<h3 id="후보키-candidate-key">후보키 (Candidate Key)</h3>
</li>
<li>유일성과 최소성을 만족하는 키</li>
<li>후보키 중 하나가 기본키가 되고 나머지는 대체키가 됨<h3 id="슈퍼키super-key">슈퍼키(Super Key)</h3>
</li>
<li>유일성은 만족하지만 최소성을 만족하지 않는 키<ul>
<li>(학번 + 이름)이 있다면 슈퍼키 (학번만 있어도 되니까)<h3 id="대체키alternate-key">대체키(Alternate Key)</h3>
</li>
</ul>
</li>
<li>여러 후보키 중 기본키가 아닌 키<h3 id="외래키foreign-key">외래키(Foreign Key)</h3>
</li>
<li>다른 테이블의 기본키를 참조하는 키<h1 id="1과목-6단원-정규화">1과목 6단원, 정규화</h1>
<h2 id="개념">개념</h2>
</li>
<li>최소한의 데이터만을 하나의 엔터티에 넣는식으로 데이터를 분해하는 과정</li>
<li>중복 제거, 데이터 모델의 독립성 확보 -&gt; 이상현상을 줄이기 위함<ul>
<li>이상현상 : 정규화를 하지 않아 발생하는 현상<ul>
<li>삽입 이상 : 정의하지 않아도 될 속성까지 반드시 입력해야하는 상황</li>
<li>갱신 이상 : 중복되는 데이터 중 일부만 수정되어 모순이 발생하는 상황</li>
<li>삭제 이상 : 어떤 정보를 삭제할 때, 의도하지 않은 다른 정보까지 삭제되어버리는 현상</li>
</ul>
</li>
</ul>
</li>
<li>논리 데이터 모델링 수행 시점에서 고려됨</li>
<li>5정규화까지 있지만 실질적으로는 3 정규화까지만 수행<h2 id="정규화의-단계">정규화의 단계</h2>
<h3 id="제-1-정규화">제 1 정규화</h3>
테이블의 컬럼이 원자성을 갖도록 테이블을 분해
<img src="https://velog.velcdn.com/images/dev_hogun/post/f46825ca-c23b-4671-8e2a-fd1fe37e7792/image.png" alt=""><h3 id="제-2-정규화">제 2 정규화</h3>
제 1 정규화를 진행한 테이블에 대해 완전 함수 종속을 만들도록 테이블을 분해
완전 함수 종속 : 기본 키를 구성하는 컬럼의 값이 다른 컬럼을 결정짓는 상태 =&gt; PK와 나머지가 1:1 대응되어야
<img src="https://velog.velcdn.com/images/dev_hogun/post/29005c59-f3fe-46dc-834b-59bb1724b83c/image.png" alt="">
위에 있는 테이블의 PK는 학생 번호와 강의명</li>
<li>하지만 학생 번호에 의해 강의실이 결정되는 것이 아님, 강의명과 성적의 관계도 마찬가지</li>
<li>따라서 기본 키인 <code>학생번호</code>와 <code>강의명</code>에 따라 나머지 컬럼이 결정되도록 분해
=&gt; 제 2 정규화<h3 id="제-3-정규화">제 3 정규화</h3>
제 2 정규화를 진행한 테이블에 대해 이행적 종속을 없에도록 테이블을 분리</li>
<li>이행적 종속 : A -&gt; B, B -&gt; C 관계가 성립할 때, A -&gt; C가 성립되는 것</li>
<li>즉, 제 3 정규화는 (A,B)와 (B,C)로 테이블을 분리하는 과정
<img src="https://velog.velcdn.com/images/dev_hogun/post/f96de58d-2b56-4713-8904-5afac5746ddb/image.png" alt=""></li>
<li>고객 번호 -&gt; 상품명, 상품명 -&gt; 가격
=&gt; (고객 번호, 상품명), (상품명, 가격)으로 분리<h3 id="cf-bcnf-정규화-제-4-정규화-제-5-정규화">cf. BCNF 정규화, 제 4 정규화, 제 5 정규화</h3>
BCNF(Boyce-Codd Normal Form) 정규화</li>
<li>모든 결정자가 후보키가 되도록 테이블을 분해하는 것 (결정자가 후보키가 아닌 다른 칼럼에 종속되면 안됨)
제 4 정규화</li>
<li>여러 컬럼들이 하나의 컬럼을 종속시키는 경우 분해하여 다중값 종속성을 제거
제 5 정규화</li>
<li>조인에 의해서 종속성이 발생되는 경우 분해<h3 id="반정규화역정규화-de-normalization">반정규화(역정규화, De-Normalization)</h3>
</li>
<li><strong>정규화된 데이터 모델을 중복, 통합, 분리하는 데이터 모델링 기법</strong></li>
<li>DB의 성능 향상을 위해 데이터 중복을 허용하고 조인을 줄이는 데이터베이스 성능 향상 방법</li>
<li>조회(SELECT) 속도는 향상됨 / 데이터 모델의 유연성은 낮아짐</li>
<li>비정규화(처음부터 정규화를 수행하지 않는 것)를 하는 것은 아님</li>
<li>(어차피 계속 JOIN해서 쓸거면 합쳐두는게 낫잖아)
수행 케이스</li>
<li>속도가 느려지는 경우</li>
<li>다량의 범위를 자주 처리해야 하는 경우</li>
<li>특정 범위의 데이터만 자주 처리하는 경우</li>
<li>요약/집계 정보가 자주 요구되는 경우<h3 id="내가-시도하는-정규화">내가 시도하는 정규화</h3>
UNF(비정규형)
<img src="https://velog.velcdn.com/images/dev_hogun/post/62cdd423-2d6d-42fe-ad57-4678de3a2c5c/image.png" alt="">
1NF(제 1 정규형)
<img src="https://velog.velcdn.com/images/dev_hogun/post/6688059d-7ff4-44f7-9bb4-84bd012f7a48/image.png" alt="">
2NF(제 2 정규형)
<img src="https://velog.velcdn.com/images/dev_hogun/post/fd7d178d-4748-4198-8203-1e930d22c91c/image.png" alt="">
3NF(제 3 정규형)
<img src="https://velog.velcdn.com/images/dev_hogun/post/ac7c55f3-c4ca-4b76-b381-5df9eb8b2445/image.png" alt=""><h1 id="1과목-7단원-관계와-조인의-이해">1과목 7단원, 관계와 조인의 이해</h1>
관계 : 엔터티간의 연관성 ~= 페어링(인스턴스간의 연관성)
관계를 맺는다 = 부모의 식별자를 자식에게 상속하고, 상속된 속성을 매핑키(조인키)로 활용
<img src="https://velog.velcdn.com/images/dev_hogun/post/43620cda-1300-4b36-a750-ecf7f233b5a9/image.png" alt=""><h2 id="관계의-분류">관계의 분류</h2>
존재 관계 : 엔터티간의 상태 (ex. 사원과 부서 관계)
행위 관계 : 엔터티 간의 행위 (ex. 고객과 주문 관계)<h2 id="조인이-갖는-의미">조인이 갖는 의미</h2>
정규화를 통해 분리된 데이터를 동시에 처리해야할 때, 관계가 있는 테이블을 참조하기 위해 연결하는 과정
<img src="https://velog.velcdn.com/images/dev_hogun/post/54ed3365-ac09-4d3b-957a-cbe8cfb14d52/image.png" alt="">
100111의 관리점이 어디 있는지를 찾으려면</li>
<li>계좌 번호의 관리점 코드를 확인, 관리점 테이블에서 관리점 코드에 해당하는 관리점을 찾음<pre><code class="language-SQL">SELECT A.계좌번호, B.관리점 -- 4. 출력하고자 하는 정보를 작성
FROM 계좌 A, 관리점 B -- 1. 조인을 수행할 두 테이블 명시, 계좌의 별칭(ALIAS)은 A, 관리점은 B
WHERE A.관리점코드 = B.관리점코드 -- 2. 연결고리 작성 Oracle 표준
AND A.계좌번호 = &#39;100111&#39; -- 3. 일반 조건도 작성</code></pre>
<h3 id="계층형-데이터-모델">계층형 데이터 모델</h3>
SELF-JOIN, 즉 자기 자신끼리 관계가 발생
<img src="https://velog.velcdn.com/images/dev_hogun/post/e92ed540-4673-4d91-90e3-50c9c2804784/image.png" alt=""></li>
<li>사원번호 <code>7360</code>을 가진 SMITH씨의 상위관리자는 사원번호 <code>7902</code>를 가진 Ford씨.</li>
<li>하지만 FORD씨 역시 상위관리자를 가짐
그러다 보니 SELF-JOIN이 발생할 수밖에 없다. <pre><code class="language-SQL">SELECT E1.ENAME AS 사원 이름,
  E2.ENAME AS 매니저 이름
FROM EMP E1, EMP E2 -- 1. 테이블 명이 같기에 Table Alias가 필수적
WHERE E1.MGR = E2.EMPNO -- 2. E1은 사원의 정보를, E2는 매니저의 정보를 의미</code></pre>
<img src="https://velog.velcdn.com/images/dev_hogun/post/8ce095f6-eaa1-42d6-a2a0-c861920822b8/image.png" alt=""><h3 id="상호베타적-관계">상호베타적 관계</h3>
두 테이블 중 하나만 가능한 관계
<img src="https://velog.velcdn.com/images/dev_hogun/post/81a424c8-09ea-477b-9b5a-4b0224698502/image.png" alt=""><h1 id="1과목-8단원-모델이-표현하는-트랜잭션의-이해">1과목, 8단원, 모델이 표현하는 트랜잭션의 이해</h1>
<h2 id="개념-1">개념</h2>
하나의 연속적인 업무 단위
트랜잭션에 의한 관계는 필수적인 관계 형태를 가짐
하나의 트랜잭션에는 여러 SELECT, INSERT, DELETE, UPDATE 등이 포함될 수 있음
ex) A 고객이 B 고객에게 100만원을 이체하려 한다면...</li>
<li>A 고객의 잔액이 100만원 이상인지 확인</li>
<li>이상이면, A 고객 잔액을 -100 UPDATE</li>
<li>B 고객 잔액에 +100 UPDATE
=&gt; 이 과정은 동시에 수행되어야 함 = 모두 성공하거나 모두 취소되어야 함 = 필수적인 관계를 가짐
=&gt; 절대 독립적으로 발생하면 안됨, 부분 커밋 불가 -&gt; 동시 COMMIT / 동시 ROLLBACK<h3 id="erd서의-선택적-관계와-필수적-관계">ERD서의 선택적 관계와 필수적 관계</h3>
IE 표기법 : 원을 이용 (선택적 관계서만 원을 그림)
바커 표기법 : 실선과 점선을 이용 (필수적 관계는 실선, 선택적 관계는 점선)<h1 id="1과목-9단원-null-속성의-이해">1과목 9단원, Null 속성의 이해</h1>
DBMS에서 아직 정해지지 않은 값을 의미 (0이나 빈 문자와는 다른 개념)
모델 설계시 컬럼별로 Null을 허용할지를 결정<h2 id="null의-특성">Null의 특성</h2>
</li>
<li>Null을 포함한 연산 결과는 항상 NULL (연산하고 싶으면 Null을 치환 후 연산해야 함)<ul>
<li><img src="https://velog.velcdn.com/images/dev_hogun/post/2ca1e0f5-8391-4dd1-a57f-1fbd4825bf9b/image.png" alt=""></li>
<li>NVL : Null 치환 함수, NVL(COMM, 0) = COMM 값이 Null이면 0으로 치환해서 계산해라 </li>
</ul>
</li>
<li>그룹 함수는 NULL을 제외하고 연산을 수행한다. <ul>
<li>SUM, AVG, MIN, MAX 등의 그룹 함수는 항상 NULL을 무시하고 연산</li>
<li>COUNT는 NULL이 아닌 값의 수만 세지만 COUNT(*)은 모든 행의 수를 리턴<h3 id="null의-erd-표기법">NULL의 ERD 표기법</h3>
IE 표기법에서는 NULL 허용 여부를 알 수 없음 (표기되지 않음)
바커 표기법에서는 속성 앞에 동그라미가 NULL 허용 속성을 의미함<h1 id="1과목-10단원-식별자-구분대체-여부에-따른">1과목 10단원, 식별자 구분(대체 여부에 따른)</h1>
본질 식별자 : 업무에 의해 만들어지는(업무에 필요한) 식별자
인조 식별자 : 업무에 필요하지는 않지만 관리의 편의성 등을 이유로 인위적으로 만들어지는 식별자
<img src="https://velog.velcdn.com/images/dev_hogun/post/a33c8085-826d-4b1b-b03b-e56bfaad757b/image.png" alt=""></li>
</ul>
</li>
</ul>
<ol start="4">
<li>PK : 주문번호 + 상품번호<ul>
<li>본질 식별자가 됨</li>
<li>하나의 주문번호로 같은 상품의 주문 결과를 저장할 수 없음</li>
</ul>
</li>
<li>PK : 주문번호 + 주문순번<ul>
<li>하나의 주문에 여러 상품에 대한 주문 결과 저장 가능</li>
<li><img src="https://velog.velcdn.com/images/dev_hogun/post/e0c3bd4b-a3e7-4359-b07a-d220b0296e58/image.png" alt=""></li>
<li>동일한 상품을 주문할 때마다 주문 횟수를 파악해야 함</li>
</ul>
</li>
<li>PK : 주문상세번호(인조식별자 생성)<ul>
<li><img src="https://velog.velcdn.com/images/dev_hogun/post/ae46a1a8-1b3b-47db-b8da-d823fda6a3c5/image.png" alt=""></li>
<li>중복 데이터 발생 가능성(1, 2번 주문 처럼 중복이 될 수 있으니)</li>
<li>불필요한 인덱스 생성 -&gt; DML 성능 저하
cf. INDEX SPLIT : DML(INSERT/UPDATE/DELETE) 사용 시 인덱스도 모두 바꿔줘야하기에 인덱스 증가는 DML에 치명적</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 예외의 계층구조 - 엉뚱한 예외가 잡힐 때]]></title>
            <link>https://velog.io/@dev_hogun/Java-%EC%98%88%EC%99%B8%EC%9D%98-%EA%B3%84%EC%B8%B5%EA%B5%AC%EC%A1%B0-%EC%97%89%EB%9A%B1%ED%95%9C-%EC%98%88%EC%99%B8%EA%B0%80-%EC%9E%A1%ED%9E%90-%EB%95%8C</link>
            <guid>https://velog.io/@dev_hogun/Java-%EC%98%88%EC%99%B8%EC%9D%98-%EA%B3%84%EC%B8%B5%EA%B5%AC%EC%A1%B0-%EC%97%89%EB%9A%B1%ED%95%9C-%EC%98%88%EC%99%B8%EA%B0%80-%EC%9E%A1%ED%9E%90-%EB%95%8C</guid>
            <pubDate>Wed, 05 Feb 2025 15:44:32 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-상황">문제 상황</h3>
<p>JUnit으로 테스트코드를 짜던 중 다른 예외를 잡는 문제가 발생했다. 문제가 된 코드는 다음과 같다. </p>
<pre><code class="language-java">@Test
void Level3_emptyValueTest() {
    assertSimpleTest(() -&gt; {
        try {
            run(테스트케이스 입력부);
            assertThat(output()).contains(결과);
        } catch (IllegalArgumentException e) {
            assertThat(e).isInstanceOf(IllegalArgumentException.class);
        }
    });
}</code></pre>
<p>코드 내용은 <code>테스트케이스 입력부</code>를 넣으면 <code>결과</code>가 나오거나 <code>IllegalArgumentException</code>을 던지는지 확인하는 것이다. 일반적으로 잘 실행되었으나 오버플로우가 발생했을 때, <code>NumberFormatException</code>을 예외로 던졌는데 테스트가 성공했다. 문제 상황을 더 가시적으로 보기 위해 테스트를 해봤다. </p>
<pre><code class="language-java">public class Application {
    public static void main(String[] args) {
        exceptionTest();
    }

    static void exceptionTest() {
        try {
            throwNumberFormatException();
        } catch (IllegalArgumentException exception) {
            System.out.println(&quot;IllegalArgumentException&quot;);
        } catch (Exception exception) {
            System.out.println(&quot;Else&quot;);
        }
    }

    static void throwNumberFormatException() {
        throw new NumberFormatException();
    }
}</code></pre>
<p>실행 결과는 <code>IllegalArgumentException</code> 이었다. 결론부터 말하자면 <code>NumberFormatException</code>이 <code>IllegalArgumentException</code>를 상속해 구현되었기 때문에, 다형성을 지니는 자바의 클래스 특성상 하위 타입이 상위 타입으로 치환될 수 있기에 상위 타입을 잡아내는 코드에 걸린 것이었다. </p>
<h3 id="자바의-예외">자바의 예외</h3>
<p>자바는 객체지향 언어인 만큼 예외까지도 객체지향적으로 설계되었다. 예외 역시 class로 구현되어있다. Exception 파일을 뜯어보면 다음과 같이 구현되어있는 것을 확인할 수 있다. </p>
<pre><code class="language-java">public class Exception extends Throwable {
...</code></pre>
<p>Exception이 상속받고있는 Throwable 역시 class로 구현되어있다. </p>
<pre><code class="language-java">public class Throwable implements Serializable {
...</code></pre>
<p>그리고 대망의 <code>NumberFormatException</code>은 <code>IllegalArguementException</code>을 상속받아 구현되어있었다. </p>
<pre><code class="language-java">public class NumberFormatException extends IllegalArgumentException {
...</code></pre>
<p>그리고 위의 Exception 클래스를 제외한 자바의 모든 예외들은 다른 예외를 상속받아 구현되어있다. 다음은 예외의 계층구조를 보여주는 간단한 도식이다. 
<img src="https://velog.velcdn.com/images/dev_hogun/post/1182f732-172b-4909-bffa-02a4bda0079c/image.png" alt="">
즉 자바에서 예외는 계층구조를 가지고 있었다. </p>
<h3 id="문제의-원인">문제의 원인</h3>
<p>API에서 말하기를 <code>IllegalArguementException</code>은 메서드에 부적절한 인수가 전달되었다는 것을 드러내기 위해 발생시키는 예외이고, <code>NumberFormatException</code>은 문자열을 숫자로 바꾸려고 시도했으나 인수인 문자열이 숫자 포멧이 아니라는 것을 말하기 위해 발생시키는 예외이다. 즉 변환 메서드의 인수인 문자열을 메서드에 넣었을 때 발생시키는 예외가 <code>NumberFormatException</code>이기에 이는 부적절한 인수를 발생할 때 던지는 <code>IllegalArguementException</code>를 상속한 것으로 생각된다. 
결과적으로 <code>NumberFormatException</code>은 <code>IllegalArguementException</code>를 상속받았기에 후자를 걸러낼 때 함께 걸러진다는 것이다. </p>
<h3 id="해결-방법">해결 방법</h3>
<p>거창한 해결방법을 찾지는 못했다. <code>Exception</code>은 <code>Throwable</code>을 상속하고, 이것 역시 class를 통해 구현되기에 class의 공통 조상인 <code>Object</code>의 메서드를 통해 해결하고자 했다. </p>
<pre><code class="language-java">@Test
void Level3_emptyValueTest() {
    assertSimpleTest(() -&gt; {
        try {
            run(테스트케이스 입력부);
            assertThat(output()).contains(결과);
        } catch (IllegalArgumentException e) {
            assertThat(e.getClass()).isEqualTo(IllegalArgumentException.class);
        }
    });
}</code></pre>
<p>그렇다. 그냥 getClass()로 직접 비교했다. 결과적으로는 <code>NumberFormatException</code> 예외가 발생하면 테스트에 통과하지 못하고 <code>IllegalArgumentException</code> 만 테스트를 통과할 수 있게 되었다. 
<img src="https://velog.velcdn.com/images/dev_hogun/post/ef7e0bde-7b97-43b2-858b-aa8a4f842e09/image.png" alt=""></p>
<p>이 글의 주제에 대한 결론은 &quot;엉뚱한 예외가 잡힐 때&quot;라는 전제가 잘못되었다는 것이다. 다른 예외가 잡히긴 했으나 이는 적절하게 잡아낸 것이었다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 개발자가 되고싶은, 2024연말결산]]></title>
            <link>https://velog.io/@dev_hogun/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%90%98%EA%B3%A0%EC%8B%B6%EC%9D%80-2024%EC%97%B0%EB%A7%90%EA%B2%B0%EC%82%B0</link>
            <guid>https://velog.io/@dev_hogun/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EB%90%98%EA%B3%A0%EC%8B%B6%EC%9D%80-2024%EC%97%B0%EB%A7%90%EA%B2%B0%EC%82%B0</guid>
            <pubDate>Sat, 28 Dec 2024 03:25:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>대학교의 절반을 지나보내며 한 해를 기억하고자 하는 생각에, 또 나도 글을 쓰고싶다는 욕심에 한 해를 회고한다. 지난 1년을 되돌아 보면 풀리는 일은 별로 없었지만 참 애썼다는 생각이 든다. 프로그래밍을 처음 시작해서 아무것도 모르던 1학년을 지나 이제는 나름 개발자 지망생으로서 깊어지고 있다. </p>
</blockquote>
<hr>
<h2 id="학교-수업">학교 수업</h2>
<blockquote>
<h4 id="1학기-22학점">1학기 (22학점)</h4>
</blockquote>
<ul>
<li>교양 : <code>[글로벌소통과언어]CTE for IT</code>, <code>섬김의리더십</code></li>
<li>전공 : <code>디지털미디어원리</code>, <code>UX/UI디자인</code>, <code>웹프로그래밍및실습</code>, <code>데이터사이언스</code></li>
<li>타전공(정보사회학과) : <code>정보사회학</code>, <code>사회통계1</code><h4 id="2학기-17학점">2학기 (17학점)</h4>
</li>
<li>교양 : <code>[글로벌시민의식]글로벌시민과국제기구</code></li>
<li>전공 : <code>컴퓨터그래픽스개론</code>, <code>자료구조및실습</code></li>
<li>타전공(컴퓨터학부) : <code>컴퓨터구조</code></li>
<li>타전공(소프트웨어학부) : <code>알고리즘</code>, <code>데이터통신과네트워크</code></li>
</ul>
<p>혼종도 이런 혼종이 없을 것이다. 1학기 때 22학점은 정말 어떻게 들었는지... 지금 저렇게 들으라면 못 들을 것 같다. 와중에 전공 종류도 4가지(주전공, 복전1, 복전2, 타전공)를 들었다. 학점은 빠짐없이, 계속 떨어지고 있지만 4점대는 겨우 방어했다. 이만하면 된거 아닐까... 수업을 망나니처럼 듣는다는 말은 수업을 대충듣는 사람보다 나에게 더 어울리지 않을까... 다시 봐도 기괴하지만 후회는 없다. </p>
<p>가장 만족스러운 수업은 사회통계1이다. 진도 범위는 크게 t-test(중간)와 선형 회귀분석(기말) 정도로 어떻게 보면 되게 짧다. 하지만 개념과 맥락 등을 세세하고 반복적으로 설명해주셔서 배울것이 많았다. 그 다음은 디지털미디어원리와 알고리즘이다. 이 두 과목은 과목 내용을 전반적으로 설명해주셔서 다른 과목을 공부할 때도 도움이 많이 되었다. </p>
<hr>
<h2 id="github">Github</h2>
<blockquote>
<p><a href="https://github.com/moongua404">나의 깃허브 주소</a></p>
</blockquote>
<table>
<thead>
<tr>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/9696185a-9470-4bdc-8a55-6fce1270022e/image.png" alt="이미지"></th>
<th>올해 1학기 개강 즈음에 깃허브 계정을 만들었다. 나는 2년전까지만 해도 뼛속까지 문과라고 믿고왔던 사람이기에 대학에 와서도 우스갯소리로 문과라고 호소하고 다녔고 그게 재밌어서 깃허브 이름도 moongua라고 대충 지었다. 깃허브 계정을 만든 것도 1학기 때 있었던 &quot;웹프로그래밍 및 실습&quot; 수업 때문이었다.</th>
</tr>
</thead>
</table>
<p>3월 2일, 처음으로 깃허브에 커밋을 해본 이후로 한 해동안 거의 500개의 contributions를 했다. 3월에는 잔디가 듬성듬성 있는데 12월로 갈수록 조밀해진다. 
<img src="https://velog.velcdn.com/images/dev_hogun/post/60b0c340-d059-446f-83fd-128ad517b9c1/image.png" alt="깃허브 커밋 분포 이미지"></p>
<hr>
<h2 id="첫-번째-프로젝트-맛있다">첫 번째 프로젝트, 맛있다!</h2>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/dcc6b1bd-5836-4e7d-89da-fa5554f34064/image.png" alt=""></p>
<p>내 첫 웹프로그래밍 경험이자 첫 백엔드 경험이다. 대충 운동 도와주는 웹앱인데 처음 백엔드를 해봐서 정말 엉망이었다. 처음이지만 NestJS라는 프레임워크를 사용했고 얼렁뚱땅 만들었다. 그래도 열심히 개발해서 백엔드를 구현했는데,,, 연결하는 시간이 너무 촉박해서 백엔드 API는 별로 안쓰고 그냥 가짜로 구현했던 경험이... 정말 웹에 대한 이해가 너무 낮아서 힘들었다. </p>
<hr>
<h2 id="두-번째-프로젝트-씨즌넷-웹-리뉴얼">두 번째 프로젝트, 씨즌넷 웹 리뉴얼</h2>
<p>학교 방송국을 하고 있는데,,, 방송국의 웹사이트가 너무 구려서 리뉴얼하고싶다는 욕심에 일을 벌였다. 나는 백엔드와 프로젝트 진행을 맡았고 API는 다 만들었는데 프론트가 만들어지지 않아 좌초되었다. 솔직히 내가 너무 부족했다. 협업에 대한 지식이 너무 모자랐을 뿐 아니라 이해도 역시 낮았다. 프로젝트 중단 결정이 나자 너무 속상했는데 이제와서 돌아보니 미안한 마음도 적지 않게 든다. 망한 프로젝트니까,,, 더 이상의 흔적을 남기지 않는걸로...</p>
<hr>
<h2 id="세-번째-프로젝트-project-re-웹-리뉴얼">세 번째 프로젝트, Project-Re 웹 리뉴얼</h2>
<blockquote>
<p><a href="https://d1ppoxo07mliqy.cloudfront.net/">Project-Re 웹사이트</a>
<a href="https://www.figma.com/deck/I7jsjI5da1HmabLMMjaGOl/Project%3Are-web-renewal?node-id=2-25&amp;node-type=canvas&amp;viewport=184%2C433%2C0.03&amp;t=Zqtf0eqbBPPiOIU1-1&amp;scaling=min-zoom&amp;content-scaling=fixed&amp;page-id=0%3A1">프로젝트 PPT</a></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/e830db21-4ae2-4041-87c9-7da8bff55f3d/image.png" alt=""></p>
<p>프로젝트는 얼렁뚱땅 시작됐다. 자원순환 프로젝트인데 UI가 별로여서 바꾸고 싶었고, 운영하는 교수님께 말씀드리니 일이 벌어졌다는 것이다.</p>
<table>
<thead>
<tr>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/731350da-1a76-4ebd-a070-0a4f789a597b/image.png" alt="이미지1"></th>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/56f23aaf-08e0-40af-aed5-cee9284fe7b5/image.png" alt="이미지2"></th>
</tr>
</thead>
</table>
<p>핵심 기능까지는 그렇게 어려운 일이 아니었다. 하지만 세 가지 변수탓에 쉽지만은 않은 프로젝트였다. </p>
<blockquote>
<ol>
<li>클라이언트와 개발팀이 그린 그림이 서로 달랐다. </li>
<li>관리자 페이지를 만들어야 했다. </li>
<li>모두 웹개발 초보였다. </li>
</ol>
</blockquote>
<p>다행히도 처음으로 실제 배포에 성공한 프로젝트이가 되었다. 학교 축제인 대동제에서 내가 만든 웹사이트가 쓰이는 모습을 보고 정말 감격스러웠다. 1등 경품도 뽑았는데 내 경품의 행방은...</p>
<table>
<thead>
<tr>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/9249ae55-dd69-4aa2-b688-2fc29db4b259/image.png" alt="이미지1"></th>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/128d3895-ad06-4e76-9787-83c30cde8dbc/image.png" alt="이미지2"></th>
</tr>
</thead>
</table>
<hr>
<h2 id="학부-비공식-소모임-올라운더">학부 비공식 소모임, 올라운더</h2>
<p>플젝을 하며 그렇게 데여도 일을 벌일 사람은 또 일을 벌인다. Project-Re를 하던 친구들 중 몇명과 소모임을 만들자고 한 것이다. 웹개발을 해보며 부족함을 너무 많이 느꼈고, 우리 학부에도 프로젝트를 해볼 경험을 만들어보고 싶다는 것이 이유였다. 물론 계획한 소모임의 활동들 역시 거의다 처참하게 실패하고 이제 남은건 스터디 하나 뿐이다. 그래도 내 생각에 스터디는 나름 잘 진행되었다. 매주 모여서 공부를 하고 이를 공유하는...</p>
<hr>
<h2 id="정보-처리-기능사">정보 처리 기능사</h2>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/fcf4fca3-f44d-4000-8658-ff020730e7a6/image.png" alt=""></p>
<p>동기들 중 정처기를 준비한다는 친한 동기 두 명이 있어서 방학때부터 같이 준비했다. 필기는 문제집을 사서 절반 이상 풀 정도로 꽤나 열심히 준비했다. 준비하던 중 개강하고 시간이 없어 미루다 시험 전에 기출 몇개 풀었고, 80점으로 싱겁게 붙었다. 실기 역시 문제집을 샀으나 쓸 곳 없는 자격증을 따기 위해 공부할 시간은 없었다. 손도 안대고 시험 전에 기출 1개 풀어보고 75점으로 붙었다. 내 생각에 정보처리 &quot;기능사&quot; 자격증은 전공자라면 시험치기 전에 기출 몇개만 돌리고 적당히 신에게 기도하면 충분히 합격할 것 같다. </p>
<hr>
<h2 id="우아한-테크-코스">우아한 테크 코스</h2>
<table>
<thead>
<tr>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/d147ad56-389e-4d62-8810-49246798098a/image.jpg" style="width: 900px; height: auto;"></th>
<th>내가 처음 우테코를 알게 된 것은 9월 29일이었다. 학교에 붙어있는 홍보물을 보며 &quot;우와 이게 뭐지&quot;하고 사진을 찍었다. 그리고 나는 병역을 산업체로 복무를 하고싶었기에 부트캠프를 갔다가 산업체를 갈 수 있지 않을까라는 생각으로 우테코를 알아봤다. 찾아보면 찾아볼수록 너무 가고싶어졌다. 나 살길 찾기도 바빠진 대학생활에 &quot;몰입&quot;, &quot;함께 학습하고 성장하는 경험&quot;과 같은 말들이, 말들의 총체인 포비님의 철학이 매력적었다. 지원 마감까지 2주조차 안남은 촉박한 시기였으나 늦게나마 준비를 시작했다.</th>
</tr>
</thead>
</table>
<h3 id="자기소개서">자기소개서</h3>
<p>29일부터 마감 전날인 10일까지 자기소개서를 작성했다. 내용은 개략적으로 다음과 같다. </p>
<blockquote>
<p> 1. 프로그래밍 교육 이력 (200자 이내)</p>
</blockquote>
<p>&quot;숭실대학교 IT대학 글로벌미디어학부 재학, 컴퓨터학부 복수 전공&quot;
1번에 대해 더 쓸 것은 없었다. </p>
<blockquote>
<ol start="2">
<li>프로그래머가 되려는 이유와 지원 동기 (2000자 이내)</li>
</ol>
</blockquote>
<p>서론을 제외하고 크게 세 단락으로 나눴다. </p>
<ol start="0">
<li><p>서론에서는 내가 재미와 감동을 주는 것을 좋아하는 사람이라고 소개하는 정도였다. </p>
</li>
<li><p>첫 번째 단락은 내가 웹 프로그래밍에 관심을 갖게 된 동기를 설명했다. 콘텐츠의 매개로서 웹이 적합해보였다는 논지로 글을 썼다. </p>
</li>
<li><p>두 번째 단락에서는 위에 설명한 Project-Re에 대한 경험을 소개했다. &quot;가치 전달의 수단&quot;으로써 웹을 개발해본 경험이었다는 논지로 첫 번째 단락과 이었다. </p>
</li>
<li><p>마지막 단락에서는 지원 동기에 방점을 찍었다. 학과에서 개발의 깊이를 더하기 위해 노력했지만 한계가 있었고, 이에 우테코에서 개발 문화를 경험하고 더 나아가 학부에 기여하고 싶다는 논지였다. </p>
</li>
</ol>
<p>총 1984자 작성했다. </p>
<blockquote>
<ol start="3">
<li>오랜 시간 몰입했던 경험 그리고 도전 (2000자 이내)</li>
</ol>
</blockquote>
<p>이 문항 역시 서론과 세 단락으로 구성했다. </p>
<ol start="0">
<li><p>먼저 내가 생각하는 몰입을 &quot;깊게 파고드는 태도, 그 과정에서 마주하게 되는 문제를 찾고 이를 해결해 나가는 습관&quot;이라 정의하며 시작했다. </p>
</li>
<li><p>첫 번째 단락에서는 영상 만들때의 몰입 경험을 소개했다. 나는 중학교 3학년때부터 영상을 만들었고 그 때부터 퀄리티 높은 영상을 만들기 위해 노력했던 것들, 그 순간마다 마주했던 어려움들, 이에 내가 한 노력들을 설명했다. </p>
</li>
<li><p>두 번째 단락에서는 프로그래밍 경험을 언급했다. 위에 있는 &quot;첫 번째 프로젝트, 맛있다!&quot;와 &quot;두 번째 프로젝트, 씨즌넷 웹 리뉴얼&quot;의 경험을 바탕으로 &quot;Refresh Token Rotation&quot;이라는 기법을 도입한 경험을 맥락 중심으로 설명했다. </p>
</li>
<li><p>세 번째 단락에서는 &quot;두 번째 프로젝트, 씨즌넷 웹 리뉴얼&quot;에서 개발 문화의 필요성을 느낀 경험을, 추가로 학부에 좀 더 나은 개발 문화를 만들기 위한 노력으로 &quot;학부 비공식 소모임, 올라운더&quot;를 만든 경험을 소개했다. </p>
</li>
</ol>
<p>총 2000자 작성했다. </p>
<blockquote>
<ol start="4">
<li>프리코스 목표 설정 (1000자 이내)</li>
</ol>
</blockquote>
<p>이 문항은 서론과 결론을 한 문장씩 쓰고 본문을 두 문단으로 구성했다. </p>
<ol>
<li><p>첫 번째 단락에서는 &quot;고민&quot;을 목표로 제시했다. 코드에 대해 고민하며 쓰는 것을 목표로 잡겠는 내용이다. </p>
</li>
<li><p>두 번째 단락에서는 &quot;공유&quot;를 목표로 제시했다. 아는 내용을 문서화하며 지식을 체계화시킬 것이며 이를 공유하며 지식을 공유하는 문화에 기여하겠다는 내용이다. </p>
</li>
</ol>
<p>총 1000자 작성했다. </p>
<h3 id="프리코스">프리코스</h3>
<p>프리코스 문제를 처음 마주했을 때, 솔직히 좀 얕잡아봤다. <code>3,4,5</code>이런 문자열을 입력받으면 3과 4와 5를 더해서 12를 출력하면 되는 문제였다. 구현의 로직은 어렵지 않은게 사실이지만 문제는 사용자의 사용성을 고려하며, 개발 컨벤션에 맞춰 만들어야한다는 것이었다. 2주차 미션까지는 사실상 이러한 우테코의 미션에 적응하는 시간이었다. 3주차 미션 로또부터 생각할 것들이 슬슬 많아졌다. 지켜야 할 개발 컨벤션도 점점 추가되었고, 로직도 단순하지 않았다. 4주차 미션은 그 내용까지 꽤나 복잡했다. 정말 시간이 없어서 서울로 오던 고속버스에서도 코딩을 했다. </p>
<p>1주차 프리코스 미션을 끝내고 코드리뷰를 겨우 하나 받았다. 받는 것도 너무 힘들고 혼자서 하기 벅찬 부분들이 있어... 이렇게 하면 안되겠다 싶어 스터디에 들어갔다. 이런 스터디를 처음 해보는 것이기도 하고 성격이 원체 내향적인지라 어려웠지만 너무 좋았다. 스터디를 하며 하루에 적어도 2시간은 공부할 시간이 생겼고, 배운 내용을 공유할 수 있었으며 코드 리뷰도 받을 수 있었다. 마지막에는 최종코테 준비도 함께 했다. 스터디원으로서 부족한 점이 많았고 걸림돌이 된 순간이 있었음에도 함께 스터디를 해준 스터디원분들을 만난 것도, 그런 스터디를 만들고 이끌어준 스터디장이 계셨던 것도 올 해 있었던 몇 안되는 천운이었던 것 같다. </p>
<p>프리코스마다 있었던 회고글은 대충 다음과 같았다. </p>
<blockquote>
<p>1주차(1448자) : 자바에 대해 배웠다 MVC도 배웠다. 커뮤니티 사람과도 소통하고싶다. 
2주차(2913자) : 세부목표의 달성 여부, 사례를 중심으로 설명
3주차(1925자) : 시험때문에 목표로 세웠던 것들이 많이 어그러졌다. 그래도 노력하고 있다, 마지막 주에는 최선을 다할 것이다. 
4주차(3877자) : 세부 목표의 달성 여부, 사례를 중심으로 + 한 달 전과의 비교</p>
</blockquote>
<p>회고록도 정말 열심히 쓰긴 한 것 같다. 프리코스의 테스트케이스는 모두 통과했고, 다행히도 최종코테를 볼 수 있었다. </p>
<h3 id="최종-코딩테스트">최종 코딩테스트</h3>
<p>최종 코테는 스터디에서 함께 준비한 것을 제외하면 거의 준비하지 않았다. 최종 코테 이틀 전에 시험이 있기도 했고 공익을 신청해버렸기에, 이미 반 포기한 상태였다. 이 어그로만 아니었으면,,, 아마도... 음음</p>
<table>
<thead>
<tr>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/2c2e2544-2ba9-43de-87b4-260be4b816c9/image.png" style="height: 200px; margin-right: 15px;"></th>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/ae2f5d0e-db93-4bf2-b3ef-8bf2f94e0c19/image.png" style="height: 300px;"></th>
</tr>
</thead>
</table>
<p>보러가는 길에 카톡으로 친구들과 요란하게 떠들면서 갔다. </p>
<table>
<thead>
<tr>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/36bb4783-8ea4-446b-8e6b-d9ebe25dc23a/image.png" alt="이미지1"> <img src="https://velog.velcdn.com/images/dev_hogun/post/76b05b33-dd1b-4fe6-bc85-93f1c7a7377f/image.png" alt="이미지2"></th>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/516ef920-068a-4d35-a156-21cd436111f4/image.png" alt="이미지3"></th>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/cf18ffe2-bdea-496f-a368-a55b4f166530/image.png" alt="이미지4"> <img src="https://velog.velcdn.com/images/dev_hogun/post/68cc4383-0e1d-4648-80b6-86259292d823/image.png" alt="이미지5"></th>
</tr>
</thead>
</table>
<p>시험을 받고서 살짝 멘붕이 왔다. 생각보다 구현해야할게 많았다. 하지만 그냥 DB를 만지는 것과 문제가 비슷하길래 여기에 착안점을 갖고 구상과 구현을 했다. 역시나 구현 자체에서 시간을 많이 끌었고, 구현을 하는 도중에도 잘 안풀려서 돌아가지도 않는 쓰레기를 탄생시켰다. 내가 시험을 버리고 우테코를 준비하면 결과가 달랐을까... 시험을 끝내고 멘탈이 정말 탈탈 털렸다. </p>
<table>
<thead>
<tr>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/cb0b0948-efc7-42ac-8dd6-af67c4bdeb91/image.png" alt="이미지1"></th>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/dc4eba38-1109-4916-906a-bb105ca8fa41/image.png" alt="이미지2"> <img src="https://velog.velcdn.com/images/dev_hogun/post/a451217f-cd83-4939-aabe-4801cedfb886/image.png" alt="이미지3"></th>
<th align="center"><img src="https://velog.velcdn.com/images/dev_hogun/post/de62dd1e-7f91-41f1-9209-aa84df2511dd/image.png" alt="이미지4"></th>
</tr>
</thead>
</table>
<p>난 당연히 안될 줄 알았다. 되는게 이상한 상황이었다. 근데</p>
<p><img src="https://velog.velcdn.com/images/dev_hogun/post/74505c9d-0f3b-4ab8-9cc0-93016239acec/image.png" alt=""></p>
<p>3시 7분 쯤 네이버 메일 앱에 알람이 왔다. 근데 미리보기에 &#39;합격&#39;이란 글자를 본 것 같은데,, 에이 아니겠지 하고 들어갔는데... 왜? 정말 왜라는 생각밖에 안들었다. 받고 나서 정말 멘붕이 왔다. 왜냐하면 1월 6일에 훈련소를 가야하기 때문이다. </p>
<hr>
<h2 id="또-다시-새로운-삶을">또 다시 새로운 삶을...</h2>
<h3 id="스터디">스터디</h3>
<p>우테코를 하면서 많은 것들을 배울 수 있었다. 그래서 내 친구들도, 학과 사람들도 이를 경험해봤으면 좋겠다는 생각이 들었다. 그래서 또 다시 일을 벌였다. 어차피 가도 공익으로 가니까, 매일 저녁에는 시간이 있을테니까 우테코 프리코스로 스터디를 하자. </p>
<img src="https://velog.velcdn.com/images/dev_hogun/post/9c13986f-8367-49e5-ac41-f4caa1f029bf/image.png" width="400">



<p>그리고 벌써 8명의 스터디원이 생겼다. </p>
<h3 id="공익-vs-우테코">공익 vs 우테코</h3>
<blockquote>
<p>저녁은 오늘 하루 열심히 땀흘리고 일한 사람들의 밥그릇에만 담겨라. 
모든 오늘은 최후다.</p>
</blockquote>
<p>지난 1년간 마음에 새겼던 글귀이다. 한 해를 되돌아보니 지난 1년은 우테코를 붙기 위한 빌드업이 아니었나 싶다. 하지만 아마 지금 시점에서 군대를 미룰 수 있는 기적과 같은 방법이 있지 않는 한 우테코를 포기하게 되지 않을까... 생각한다. 붙어놓고나니 공익을 신청하지 말걸 후회가 된다. 그래도 별 수 없으니 우테코가 성장의 기회를 줬다고 생각하고 앞으로 나아가는 수밖에...</p>
<p>돌아보니 아무것도 하지 않았던 것 같은 한해에 이것저것 참 많은 것들을 했던 것 같다. 내년에는 올해처럼 무모하진 않되 올해만큼 열정적으로 살기 위해 노력해야겠다....</p>
]]></description>
        </item>
    </channel>
</rss>