<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>0-x-14.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 30 Mar 2026 10:53:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>0-x-14.log</title>
            <url>https://velog.velcdn.com/images/0-x-14/profile/61fa6406-2e1e-445c-80a8-64ede4c50b3f/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 0-x-14.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/0-x-14" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Spring Boot] 소셜 로그인 구조를 범용으로 리팩토링하기]]></title>
            <link>https://velog.io/@0-x-14/Spring-Boot-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%EC%A1%B0%EB%A5%BC-%EB%B2%94%EC%9A%A9%EC%9C%BC%EB%A1%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@0-x-14/Spring-Boot-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%EC%A1%B0%EB%A5%BC-%EB%B2%94%EC%9A%A9%EC%9C%BC%EB%A1%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 30 Mar 2026 10:53:03 GMT</pubDate>
            <description><![CDATA[<p>CooKeep 개발을 진행하면서, 카카오 로그인 이후에 구글 로그인을 추가해야 하는 상황이 생겼다. </br></br></p>
<p>기존에 카카오 로그인은 카카오 OAuth 인증을 담당하는 KakaoOAuthProvider가 OAuthProvider 인터페이스를 구현하는 구조였는데, 구글 로그인 방식을 추가하는 과정에서 구글을 독립적으로 구현할지, 아니면 범용 구조로 리팩토링할지 고민했다. </br></br></p>
<p>인터페이스를 활용하지 않고 구글 로그인을 독립적으로 구현한다면, 인터페이스 리팩토링 없이 GoogleOAuthProvider만 새로 구현하면 된다는 장점이 있었다.</br></br></p>
<pre><code>KakaoOAuthProvider implements OAuthProvider
GoogleOAuthProvider  // 인터페이스 구현 안 함</code></pre><p>와 같은 구조가 되는데, 해당 방식은 기존의 카카오 코드를 건드리지 않아 위험이 적고, 빠른 구현이 가능하다. </br></br></br></p>
<p>하지만, OAuthProvider라는 인터페이스가 이미 존재하는 상황에서 구글만 인터페이스를 구현하지 않으면 <strong>구조의 일관성이 깨진다고 판단했다</strong>. 둘 다 소셜 로그인이지만 AuthService에서도 카카오는 인터페이스 타입으로, 구글은 구체 클래스 타입으로 주입을 받는 어색한 상황이 생길 것이다. </br></br></br></p>
<p>또, 범용 구조로 리팩토링 하는 건 시간이 조금 더 걸릴지라도, <strong>유지보수와 확장 측면에서 장점이 확실하였다.</strong></p>
<p><strong>유지보수</strong> - 각 Provider는 자신의 API 응답만 파싱하고, AuthService는 공통 DTO만 다루면 된다. 만약 카카오가 API 응답 구조를 변경하더라도 KakaoOAuthProvider 내부만 수정하면 되고, AuthService는 건드릴 필요가 없다. 즉, 변경의 영향 범위가 최소화된다.</p>
<p><strong>확장</strong> - 또, 추후 새로운 소셜 로그인 방식이 추가된다면 OAuthProvider 인터페이스를 구현한 새 Provider 클래스만 만들면 되게 된다. AuthService의 기존 코드를 수정할 필요 없이, 새 Provider를 주입받아 사용할 수 있게 된다. 즉, 기존 코드를 건드리지 않고 기능을 추가할 수 있는 구조가 된다.</br></br></p>
<p>객체지향 설계원칙 중 <strong>OCP(개방-폐쇄 원칙)을 고려하였을 때도</strong> 범용 구조로 리팩토링을 진행하는 것이 좋을 것 같아, 범용 구조로 리팩토링을 진행하게 되었다.</br></br></br></br></br></p>
<h3 id="oauthuserinfodto-추가">OAuthUserInfoDTO 추가</h3>
<p>범용 구조로 리팩토링을 진행하다보니, 한 가지 고민이 생겼다. 소셜 서비스로부터 사용자 정보를 전달 받을 때, 카카오와 구글에서 전달해주는 응답 구조가 달랐기 때문이다.</br></br></p>
<p>Kakao는 다음과 같이 <strong>중첩 구조</strong>로 응답을 주고,</p>
<pre><code class="language-json">{
  &quot;id&quot;: 123456,
  &quot;kakao_account&quot;: {
    &quot;email&quot;: &quot;test@kakao.com&quot;
  }
}</code></pre>
<p>Google에서는 다음과 같은 <strong>플랫 구조</strong>로 응답을 준다.</p>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;123456&quot;,
  &quot;email&quot;: &quot;test@gmail.com&quot;
}</code></pre>
<p></br></br></br>어떻게 할지 고민을 하다 _<strong>Provider에서 API 응답을 자신에게 맞는 형태로 파싱한 뒤, 공통 DTO로 변환해주는 방식</strong>_으로 처리하였다.</p>
<pre><code class="language-java">카카오 응답 → KakaoUserInfoResponseDTO (중첩구조)
                    ↓ 변환
              OAuthUserInfoDTO(id, email)  ← 공통

구글 응답  → GoogleUserInfoResponseDTO (flat구조)
                    ↓ 변환
              OAuthUserInfoDTO(id, email)  ← 공통</code></pre>
<p>KakaoUserInfoResponseDTO와 GoogleUserInfoResponseDTO는 각각 카카오, 구글의 API 응답 파싱용이고, OAuthUserInfoDTO는 유저의 정보를 AuthService에 전달해주는 공통 DTO이다. </br></br></br></p>
<p>현재 서비스에서 <strong>소셜 사용자의 소셜ID와 이메일 정보를 필요로 했기 때문에</strong>, 최종 코드는 다음과 같이 작성하였다.</br></br></p>
<p>KakaoUserInfoResponseDTO (기존 코드 유지)</p>
<pre><code class="language-java">package com.cookeep.cookeep.domain.user.dto;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

// DTO에서 요구로 하는 필드만 받아오도록 함
@JsonIgnoreProperties(ignoreUnknown = true)
public record KakaoUserInfoResponseDTO(
    @JsonProperty(&quot;id&quot;)
    Long id,

    @JsonProperty(&quot;kakao_account&quot;)
    KakaoAccount kakaoAccount
) {
    @JsonIgnoreProperties(ignoreUnknown = true)
    public record KakaoAccount(
        @JsonProperty(&quot;email&quot;)
        String email
    ) {}
}
</code></pre>
</br>
GoogleUserInfoResponseDTO (추가, 구글 API 응답 파싱용)

<pre><code class="language-java">package com.cookeep.cookeep.domain.user.dto;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public record GoogleUserInfoResponseDTO(
    @JsonProperty(&quot;id&quot;) String id,
    @JsonProperty(&quot;email&quot;) String email
) {
}
</code></pre>
</br>
OAuthUserInfoDTO (추가, AuthService에 전달하는 공통 DTO)

<pre><code class="language-java">package com.cookeep.cookeep.domain.user.dto;

// 카카오, 구글 모두 공통적으로 소셜ID, 이메일 정보 필요
public record OAuthUserInfoDTO(
    String id,
    String email
) {
}
</code></pre>
<p>OAuthUserInfoDTO는 JSON을 직접 파싱하는 게 아니라 각 Provider에서 Java 코드로 직접 생성되므로 Jackson 어노테이션이 필요 없다. 또한 카카오는 id가 숫자(Long)이지만 구글은 문자열(String)로 내려온다. 서로 다른 타입(Long, String)을 일관되게 처리하기 위해 <strong>String으로 변환</strong>하였다.</br></br></br></br></p>
<h3 id="oauthprovider-범용-구조로-리팩토링">OAuthProvider 범용 구조로 리팩토링</h3>
<p>다음으로는, OAuthProvider를 범용 구조로 리팩토링해주었다.</br></br></p>
<p>기존 코드 (Kakao 단독)</p>
<pre><code class="language-java">package com.cookeep.cookeep.domain.user.application;

import com.cookeep.cookeep.domain.user.dto.KakaoUserInfoResponseDTO;
import com.cookeep.cookeep.domain.user.entity.Provider;

public interface OAuthProvider {
    Provider provider();
    String getKakaoAccessToken(String code, String redirectUri);
    KakaoUserInfoResponseDTO getKakaoUserInfo(String accessToken);
}</code></pre>
<p></br>수정된 코드 (리팩토링 후)</p>
<pre><code class="language-java">package com.cookeep.cookeep.domain.user.application;

import com.cookeep.cookeep.domain.user.dto.OAuthUserInfoDTO;
import com.cookeep.cookeep.domain.user.entity.Provider;

public interface OAuthProvider {
    Provider provider();
    String getAccessToken(String code, String redirectUri);
    OAuthUserInfoDTO getUserInfo(String accessToken);
}</code></pre>
<p></br></br>또, 이에 맞춰 KakaoOAuthProvider의 코드도 수정해줬다.</p>
<p>OAuthProvider에서 변경된 메서드명에 맞춰 메서드명과 반환 타입을 수정할 뿐만 아니라, 받아온 유저의 정보값을 OAuthUserInfoDTO에 맞게 변환하는 로직 또한 추가해주었다.</p>
<pre><code class="language-java">    // 소셜 액세스 토큰을 사용해 유저의 정보값을 받아옴
    public OAuthUserInfoDTO getUserInfo(String accessToken) {
        KakaoUserInfoResponseDTO kakaoUserInfo = WebClient.create(KakaoUserInfoURL)
            .get()
            .header(HttpHeaders.AUTHORIZATION, &quot;Bearer &quot; + accessToken) // access token 인가
            .header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString())
            .retrieve()
            .bodyToMono(KakaoUserInfoResponseDTO.class)
            .block();

        return new OAuthUserInfoDTO(
            String.valueOf(kakaoUserInfo.id()),
            kakaoUserInfo.kakaoAccount().email()
        );
    }</code></pre>
<p></br></br></br></br>또, GoogleOAuthProvider도 구현하였다. KakaoOAuthProvider와 거의 유사하지만 다른 점이 있다면, 구글 id는 이미 String이기 때문에 getUserInfo에서 String.valueOf()로의 변환이 불필요하다는 점이다. 이메일 구조 또한 중첩이 아닌 플랫 구조이기 때문에 googleUserInfo.email()과 같이 간단하게 값을 받아왔다.
</br></br></br></br>
이후에는 AuthService와 AuthController 내에 구글 로그인 메서드를 작성해주었다.</p>
<p>구현 이후 테스트를 진행한 결과, 구글 로그인 또한 정상적으로 회원가입이 처리되는 것을 확인했다.</br></br></br></br></p>
<h3 id="authserivce-내-메서드-통합">AuthSerivce 내 메서드 통합</h3>
<p>이번에는 AuthService 내에 <strong>중복되는 부분을 하나의 메서드로 통합</strong>해보려 한다.</br></br></p>
<p>먼저, createKakaoUser와 createGoogleUser를 createSocialUser로 통합하였다. 이 부분은 크게 변동사항이 없어 넘어가도록 하겠다.</br></br></p>
<p>kakaoLogin과 googleLogin도 코드 자체는 거의 동일하다.</p>
<p>다음은 카카오로그인의 코드이다.</p>
<pre><code class="language-java">// 카카오 로그인
@Transactional
public SocialLoginResponseDTO kakaoLogin(String code, String redirectUri) {
    String kakaoAccessToken = kakaoOAuthProvider.getAccessToken(code, redirectUri);
    OAuthUserInfoDTO userInfo = kakaoOAuthProvider.getUserInfo(kakaoAccessToken);

    String kakaoId = userInfo.id();

    // provider = KAKAO, providerUserId인 값을 통해 이미 가입된 회원인지 식별
    Optional&lt;UserAuth&gt; existingUserAuth = userAuthRepository.findByProviderAndProviderUserId(KAKAO, kakaoId);

    String email = userInfo.email();

    // 신규 유저일 경우 User, UserAuth값을 새롭게 생성함
    UserAuth userAuth = existingUserAuth
       .orElseGet(() -&gt; {
          User user = createSocialUser(email);

          return userAuthRepository.save(
             UserAuth.builder()
                .user(user)
                .provider(KAKAO)
                .providerUserId(kakaoId)
                .build());
       });

    User user = userAuth.getUser();

    // 액세스 토큰, 리프레쉬 토큰 발급
    TokenPair tokenPair = issueTokensAndUpsertSession(user);

    UserStatus userStatus = user.getUserStatus();
    NextStep nextStep = null;
    Boolean marketingConsent = user.getMarketingConsent();

    // 최초 회원가입한 소셜 로그인 유저일 경우
    if (user.getUserStatus() == UserStatus.CREATED) {
       // 최초 회원가입인 경우 TERMS 페이지로 이동,
       // 회원가입 이후 약관 동의까지 마친 경우 ONBOARDING 페이지로 이동
       nextStep = (marketingConsent == null)
          ? NextStep.TERMS
          : NextStep.ONBOARDING;
    }

    return new SocialLoginResponseDTO(
       user.getUserId(), tokenPair.accessToken(), tokenPair.refreshToken(),
       userStatus, nextStep
    );
}</code></pre>
<p></br></br>kakaoOAuthProvider와 googleOAuthProvider만 다를 뿐, 나머지 로직은 완전히 같았기 때문에 <strong>하나의 socialLogin() 메서드로 통합</strong>하기로 했다.</br></br></p>
<p>하나의 메서드로 통합하기 위해서, OAuthProvider 인터페이스를 활용하였다.</br></p>
<p>Spring은 특정 인터페이스를 구현한 Bean이 여러 개 있을 때, List&lt;인터페이스&gt;를 주입 받으면 해당 구현체들을 모두 자동으로 담아준다. @Service가 붙은 클래스는 Spring이 자동으로 Bean으로 등록하게 되므로, KakaoOAuthProvider와 GoogleOAuthProvider도 모두 Bean으로 등록되어 있을 것이다.
따라서, List<OAuthProvider>를 주입 받으면KakaoOAuthProvider와 GoogleOAuthProvider가 자동으로 리스트에 포함되기 때문에 이와 같은 구현이 가능하다.</br></br></p>
<pre><code class="language-java">private final List&lt;OAuthProvider&gt; oAuthProviders;

private OAuthProvider getProvider(Provider provider) {
    return oAuthProviders.stream()
        .filter(p -&gt; p.provider() == provider)
        .findFirst()
        .orElseThrow(() -&gt; new RuntimeException(&quot;지원하지 않는 소셜 로그인입니다.&quot;));
}</code></pre>
<p></br></br>
만약 카카오 로그인이 요청되어 getProvider(Provider.KAKAO)가 호출될 경우,  provider() 메서드가 KAKAO를 반환하는 KakaoOAuthProvider가 반환된다. *<em>이처럼 Provider별로 나누어져야 하는 처리는 해당 코드에서 처리되고, AuthService는 소셜 로그인 종류와 상관없이 동일한 방식으로 처리할 수 있게 된다. *</em> </br></br></br></br></p>
<p>기존의 kakaoLogin 메서드에서 다음과 같았던 부분을</p>
<pre><code class="language-java">    String kakaoAccessToken = kakaoOAuthProvider.getAccessToken(code, redirectUri);
    OAuthUserInfoDTO userInfo = kakaoOAuthProvider.getUserInfo(kakaoAccessToken);
</code></pre>
<p>다음과 같이 변경하였다.</p>
<pre><code class="language-java">        // provider 타입에 따라 KakaoOAuthProvider 또는 GoogleOAuthProvider 반환
        OAuthProvider oAuthProvider = getProvider(provider);
        String accessToken = oAuthProvider.getAccessToken(code, redirectUri);
        OAuthUserInfoDTO userInfo = oAuthProvider.getUserInfo(accessToken);</code></pre>
<p></br></br></br></br></p>
<h3 id="이메일-일치-시-계정-통합">이메일 일치 시 계정 통합</h3>
<p>마지막으로, 현재 이메일은 중복을 허용하지 않고 있다.</p>
<p>기획에 따라, 이메일은 같지만 provider가 다른 경우 회원가입 과정에서 <strong>계정이 자동 통합</strong>되도록 하였다.</br></br></p>
<pre><code class="language-java">        // 신규 유저일 경우 User, UserAuth값을 새롭게 생성함
        UserAuth userAuth = existingUserAuth
            .orElseGet(() -&gt; {
                // 동일한 이메일로 가입된 User가 존재하는지 확인
                // 존재하지 않을 경우 새로운 유저 생성
                User user = userRepository.findByEmail(email)
                    .orElseGet(() -&gt; createSocialUser(email));

                // 기존 유저든 신규 유저든 UserAuth가 추가됨
                return userAuthRepository.save(
                    UserAuth.builder()
                        .user(user)
                        .provider(provider)
                        .providerUserId(socialId)
                        .build());
            });</code></pre>
<p></br>로컬 회원가입은 기획 변경으로 수정할 부분이 남아있으므로, 해당 부분을 수정하면서 관련 코드를 추가할 예정이다.</br></br></br></br></p>
<h3 id="마무리">마무리</h3>
<p>이와 같은 과정을 통해 소셜 로그인을 범용 구조로 리팩토링하는 작업을 마무리했다.</p>
<p>처음에는 구글 로그인 코드를 추가하는 방식이 더 빠를 것 같아 고민하였지만, 범용으로 리팩토링을 마치고 나니 코드가 훨씬 깔끔해졌다. </p>
<p>이후 새로운 소셜 로그인이 추가되더라도 OAuthProvider를 구현한 Provider 클래스만 추가하면 되므로, 손쉽게 확장할 수 있는 구조가 되었다. </p>
<p>이번 리팩토링을 통해 단순한 기능 구현을 넘어, 확장성과 유지보수성을 고려한 구조 설계의 중요성을 체감할 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQLD] 2-3. 관리 구문]]></title>
            <link>https://velog.io/@0-x-14/SQLD-2-3.-%EA%B4%80%EB%A6%AC-%EA%B5%AC%EB%AC%B8</link>
            <guid>https://velog.io/@0-x-14/SQLD-2-3.-%EA%B4%80%EB%A6%AC-%EA%B5%AC%EB%AC%B8</guid>
            <pubDate>Thu, 06 Mar 2025 12:24:26 GMT</pubDate>
            <description><![CDATA[<p></br></br></p>
<table>
<thead>
<tr>
<th>유형</th>
<th>종류</th>
</tr>
</thead>
<tbody><tr>
<td><strong>DML</strong></td>
<td><strong>INSERT, UPDATE, DELETE, MERGE</strong></td>
</tr>
<tr>
<td><strong>TCL</strong></td>
<td><strong>COMMIT, ROLLBACK, SAVEPOINT</strong></td>
</tr>
<tr>
<td><strong>DDL</strong></td>
<td><strong>CREATE, ALTER, DROP, RENAME, TRUNCATE</strong></td>
</tr>
<tr>
<td><strong>DCL</strong></td>
<td><strong>CREATE USER, DROP USER, GRANT, REVOKE</strong></td>
</tr>
</tbody></table>
<p></br></br></br></p>
<h2 id="dml">DML</h2>
</br>

<blockquote>
<ul>
<li><strong>DML</strong> (Data Manipulation Language)</li>
</ul>
</blockquote>
<ul>
<li>INSERT, UPDATE, DELETE, MERGE</li>
</ul>
<p></br></br></p>
<h3 id="insert">INSERT</h3>
<blockquote>
<p>테이블에 데이터를 <strong>입력</strong>하는 명령어</p>
</blockquote>
</br>

<ul>
<li><strong>INSERT INTO</strong> 테이블명 (컬럼명1, 컬럼명2, ...) <strong>VALUES</strong> (데이터1, 데이터2, ...);<ul>
<li>명시되지 않은 컬럼에는 NULL값이 입력됨</li>
<li><em><strong>PK, NOT NULL 조건 주의!!!</strong></em></li>
</ul>
</li>
</ul>
</br>

<ul>
<li><strong>INSERT INTO</strong> 테이블명 <strong>VALUES</strong> (전체 컬럼에 입력될 <strong>데이터 리스트</strong>);<ul>
<li><em>데이터 유형이 맞지 않거나 누락된 데이터 있을 경우 에러 발생</em></li>
</ul>
</li>
</ul>
</br>

<ul>
<li>DEFALUT값이 있는 컬럼이어도 다른 데이터 INSERT시 데이터 정상 삽입됨</li>
</ul>
</br>

<ul>
<li>VALUES 누락 주의!</li>
</ul>
<p></br></br></p>
<h3 id="update">UPDATE</h3>
<blockquote>
<p>이미 저장된 데이터를 <strong>수정</strong>하는 명령어</p>
</blockquote>
</br>

<ul>
<li><strong>UPDATE</strong> 테이블명 <strong>SET</strong> 컬럼명 = 새로운 데이터 (<strong>WHERE</strong> 수정할 데이터에 대한 조건);<ul>
<li>ex) UPDATE 입사 SET 구분 = &#39;경력&#39; WHERE 입사자사번 = &#39;220101&#39;; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 입사자사번이 220101인 사원을 찾아 구분을 경력으로 수정함</li>
</ul>
</li>
</ul>
</br>

<ul>
<li>여러 컬럼 수정시 SET절에 <strong>, (콤마)로 이어서</strong> 명시<ul>
<li>SET 컬럼명1 = 데이터1, 컬럼명2 = 데이터2, ...</li>
</ul>
</li>
</ul>
</br>

<ul>
<li><em>WHERE 절이 없으면 테이블의 모든 Row가 변경되니 주의</em></li>
</ul>
<p></br></br></p>
<h3 id="delete">DELETE</h3>
<blockquote>
<p>이미 저장된 데이터를 <strong>삭제</strong>하는 명령어</p>
</blockquote>
</br>

<ul>
<li><strong>DELETE FROM</strong> 테이블명 (<strong>WHERE</strong> 삭제할 데이터에 대한 조건);</li>
</ul>
</br>

<ul>
<li><em>WHERE 절이 없으면 테이블의 모든 Row가 삭제되니 주의</em></li>
</ul>
</br>

<h4 id="where-절-없는-delete-vs-truncate">WHERE 절 없는 DELETE vs TRUNCATE?</h4>
<ul>
<li>둘 다 테이블 전체 데이터 삭제 가능</li>
<li>삭제하고자 하는 마음이 확고하다면 TRUNCATE를 쓰는게 시스템 부하 측면에서 유리</li>
</ul>
</br>

<ul>
<li>DELETE<ul>
<li>COMMIT 전에 ROLLBACK 가능</li>
</ul>
</li>
<li>TRUNCATE<ul>
<li>별도의 로그를 쌓지 않아 ROLLBACK 불가능</li>
</ul>
</li>
</ul>
<p></br></br></p>
<blockquote>
<ul>
<li><strong>INSERT, UPDATE, DELETE</strong></li>
</ul>
</blockquote>
<ul>
<li>별도의 커밋 명령어를 실행해야 데이터 반영</li>
<li>ROLLBACK 가능</li>
</ul>
<p></br></br></p>
<h3 id="merge">MERGE</h3>
<blockquote>
<p>테이블에 <strong>새로운 데이터를 입력</strong>하거나 이미 저장되어 있는 데이터에 대한 <strong>변경 작업을 한 번에</strong> 할 수 있게 해주는 명령어</p>
</blockquote>
</br>

<blockquote>
<p>MERGE
&nbsp;&nbsp;&nbsp;&nbsp; INTO 타겟 테이블명
&nbsp;&nbsp;&nbsp;&nbsp; USING 비교 테이블명
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; ON 조건
&nbsp;&nbsp;&nbsp;&nbsp; WHEN MATCHED THEN
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; UPDATE
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; SET 컬럼명 = 새로운 데이터 [, 컬럼명 = 새로운 데이터 ...]
&nbsp;&nbsp;&nbsp;&nbsp; WHEN NOT MATCHED THEN
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; INSERT (컬럼명1, 컬럼명2, ...)]
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; VALUES (데이터1, 데이터2, ...);</p>
</blockquote>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="tcl">TCL</h2>
</br>

<blockquote>
<ul>
<li><strong>TCL</strong> (Transaction Control Language)</li>
</ul>
</blockquote>
<ul>
<li>COMMIT, ROLLBACK, SAVEPOINT</li>
</ul>
</br>

<ul>
<li>트랜잭션 : 쪼개질 수 없는 업무처리의 단위</li>
</ul>
<p></br></br></p>
<h3 id="트랜잭션의-특징">트랜잭션의 특징</h3>
</br>

<ul>
<li><strong>원자성</strong><ul>
<li>한 트랜잭션 내의 동작들은 모두 성공하거나 모두 실패해야 함</li>
</ul>
</li>
<li><strong>일관성</strong><ul>
<li>완료 후에도 DB가 가진 데이터에 일관성이 있어야 함</li>
</ul>
</li>
<li><strong>고립성</strong><ul>
<li>고립되어 수행되어야 함</li>
</ul>
</li>
<li><strong>지속성</strong><ul>
<li>성공적으로 수행시 변경된 데이터가 영구적으로 저장되어야 함</li>
</ul>
</li>
</ul>
<p></br></br></p>
<h3 id="commit">COMMIT</h3>
</br>

<blockquote>
<p>변경된 내용을 <strong>확정, 반영</strong>하는 명령어</p>
</blockquote>
</br>

<ul>
<li>UPDATE한 뒤 오랜 시간 COMMIT이나 ROLLBACK하지 않으면 Lock에 걸려서 다른 사용자가 변경할 수 없는 상황이 발생할 수 있으니 주의해야 함</li>
</ul>
<p></br></br></p>
<h3 id="rollback">ROLLBACK</h3>
</br>

<blockquote>
<p>변경된 내용을 <strong>취소</strong>하는 명령어</p>
</blockquote>
<p></br></br></p>
<h3 id="savepoint">SAVEPOINT</h3>
</br>

<blockquote>
<p>해당 지점까지 저장하는 명령어</p>
</blockquote>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="ddl">DDL</h2>
</br>

<blockquote>
<ul>
<li><strong>DDL</strong> (Data Definition Language)</li>
</ul>
</blockquote>
<ul>
<li>CREATE, ALTER, DROP, RENAME, TRUNCATE</li>
</ul>
<p></br></br></p>
<table>
<thead>
<tr>
<th>유형</th>
<th>데이터 타입</th>
</tr>
</thead>
<tbody><tr>
<td>문자</td>
<td>CHAR, VARCHAR, CLOB</td>
</tr>
<tr>
<td>숫자</td>
<td>NUMBER</td>
</tr>
<tr>
<td>날짜</td>
<td>DATE</td>
</tr>
</tbody></table>
<p></br></br></p>
<h3 id="create">CREATE</h3>
</br>

<ul>
<li>테이블을 <strong>생성</strong>하기 위한 명령어</li>
</ul>
</br>


<blockquote>
<p><strong>CREATE TABLE</strong> 테이블명 (
&nbsp;&nbsp;&nbsp;&nbsp;  컬럼명1 &nbsp; 데이터 타입 (DEFAULT / NULL 여부),
&nbsp;&nbsp;&nbsp;&nbsp; ...
);</p>
</blockquote>
</br>

<ul>
<li>NULL 여부 명시하지 않으면 기본은 NULL 가능</li>
</ul>
<p></br></br></p>
<ul>
<li>테이블 생성시 지켜야 할 <strong>규칙</strong> (지키지 않으면 에러 발생)<ul>
<li>테이블명은 고유해야 함</li>
<li>한 테이블 내에서 컬럼명은 고유해야 함</li>
<li>컬럼명 뒤에 <strong>데이터 유형과 크기가 명시</strong>되어야 함</li>
<li>컬럼에 대한 정의는 <strong>괄호( ) 안에 기술</strong></li>
<li>각 컬럼들은 <strong>, (콤마)</strong>로 구분됨</li>
<li>테이블명과 컬럼명은 <strong>숫자로 시작할 수 없음</strong></li>
<li>마지막은 ; (세미콜론)으로 끝남</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>테이블 생성시 권장 항목 (에러 발생 X)<ul>
<li>테이블명은 정체성을 나타내도록</li>
<li>컬럼명 정의시 다른 테이블과 통일성있게</li>
</ul>
</li>
</ul>
<p></br></br></p>
<h3 id="alter">ALTER</h3>
</br>

<ul>
<li>테이블 구조를 <strong>불가피하게</strong> 변경할 경우</li>
</ul>
<p></br></br></p>
<h4 id="add-column">ADD COLUMN</h4>
<blockquote>
<p><strong>ALTER TABLE</strong> 테이블명 <strong>ADD</strong> 컬럼명 데이터유형;</p>
</blockquote>
<ul>
<li>새로운 컬럼 추가</li>
<li>추가된 컬럼은 항상 맨 뒤, 위치 지정 불가</li>
<li>✅ 사용할 땐 ADD COLUMN 아니고 ADD임!!!</li>
</ul>
<p></br></br></p>
<h4 id="drop-column">DROP COLUMN</h4>
<blockquote>
<p><strong>ALTER TABLE</strong> 테이블명 <strong>DROP COLUMN</strong> 컬럼명;</p>
</blockquote>
<ul>
<li>컬럼 삭제</li>
<li>한 번 삭제한 컬럼은 복구 불가</li>
</ul>
<p></br></br></p>
<h4 id="modify-column">MODIFY COLUMN</h4>
<blockquote>
<p><strong>ALTER TABLE</strong> 테이블명 <strong>MODIFY</strong> (컬럼명1 데이터 유형 [DEFALUT 값][NOT NULL], 컬럼명2 데이터 유형 ...);</p>
</blockquote>
<ul>
<li>기존 컬럼 변경</li>
</ul>
<p></br></br></p>
<h4 id="rename-column">RENAME COLUMN</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQLD] 2-2. SQL 활용]]></title>
            <link>https://velog.io/@0-x-14/SQLD-2-2.-SQL-%ED%99%9C%EC%9A%A9-8kp8oyfi</link>
            <guid>https://velog.io/@0-x-14/SQLD-2-2.-SQL-%ED%99%9C%EC%9A%A9-8kp8oyfi</guid>
            <pubDate>Thu, 06 Mar 2025 07:32:49 GMT</pubDate>
            <description><![CDATA[<p></br></br></br></p>
<h2 id="서브쿼리-subquery">서브쿼리 (Subquery)</h2>
<p></br></br></p>
<p><img src="https://velog.velcdn.com/images/0-x-14/post/a2e3c47b-6bc0-467d-8cf7-0ce59c375498/image.jpg" alt=""></p>
</br>

<h3 id="스칼라-서브쿼리">스칼라 서브쿼리</h3>
</br>

<ul>
<li>주로 <strong>SELECT 절</strong>에 위치</li>
<li><strong>컬럼이 올 수 있는 대부분 위치에 사용 가능</strong><ul>
<li><em>FROM 절은 불가능 (테이블명이 오는 위치라)</em></li>
</ul>
</li>
</ul>
</br>

<ul>
<li><strong>반드시 하나의 값만 반환</strong>해야 함<ul>
<li>그렇지 않은 경우 에러 발생</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/0-x-14/post/e988a103-1746-4864-b57b-a0ec056285b6/image.png" alt=""></p>
<p></br></br></p>
<h3 id="인라인-뷰">인라인 뷰</h3>
</br>

<ul>
<li><strong>FROM 절</strong> 등 <strong>테이블명이 올 수 있는 위치</strong>에 사용 가능</li>
</ul>
<p><img src="https://velog.velcdn.com/images/0-x-14/post/11a72124-e7dc-4d92-89a8-32d307eff72c/image.png" alt=""></p>
<p></br></br></p>
<h3 id="중첩-서브쿼리">중첩 서브쿼리</h3>
<ul>
<li><strong>WHERE 절, HAVING 절</strong>에 사용 가능</li>
</ul>
</br>

<h4 id="메인쿼리와의-관계에-따라">메인쿼리와의 관계에 따라</h4>
<ul>
<li><strong>비연관 서브쿼리</strong><ul>
<li>메인쿼리와 관계를 맺고 있지 X</li>
<li>즉, 서브쿼리 내에 메인쿼리 컬럼 X</li>
</ul>
</li>
<li><strong>연관 서브쿼리</strong><ul>
<li>메인쿼리와 관계 O</li>
</ul>
</li>
</ul>
</br>

<h4 id="반환하는-데이터-형태에-따라">반환하는 데이터 형태에 따라</h4>
<table>
<thead>
<tr>
<th>유형</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>단일 행(Single Row) 서브쿼리</strong></td>
<td>- 서브쿼리가 <strong>1건 이하</strong>의 데이터를 반환 <br> - 단일 행 비교 연산자와 함께 사용 <br> 예) =, &lt;, &gt;, &lt;=, &gt;=, &lt;&gt;</td>
</tr>
<tr>
<td><strong>다중 행(Multi Row) 서브쿼리</strong></td>
<td>- 서브쿼리가 <strong>여러 건</strong>의 데이터를 반환 <br> - 다중 행 비교 연산자와 함께 사용 <br> 예) IN, ALL, ANY, SOME, EXISTS</td>
</tr>
<tr>
<td><strong>다중 컬럼(Multi Column) 서브쿼리</strong></td>
<td>- 서브쿼리가 여러 컬럼의 데이터를 반환</td>
</tr>
</tbody></table>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="뷰">뷰</h2>
</br>

<ul>
<li>뷰의 특징<ul>
<li>보안성</li>
<li>독립성</li>
<li>편리성</li>
</ul>
</li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="집합-연산자">집합 연산자</h2>
</br>

<table>
<thead>
<tr>
<th>유형</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>UNION ALL</strong></td>
<td>합집합, 중복 O</td>
</tr>
<tr>
<td><strong>UNION</strong></td>
<td>합집합, 중복 X (중복 제거되고 한 줄로 출력됨)</td>
</tr>
<tr>
<td><strong>INTERSECT</strong></td>
<td>교집합, 중복 X</td>
</tr>
<tr>
<td><strong>MINUS/EXCEPT</strong></td>
<td>차집합, 중복 X</td>
</tr>
</tbody></table>
<p></br></br></p>
<ul>
<li>UNION은 UNION ALL보다 성능상 불리함<ul>
<li>중복된 행 제거하는 과정 거치므로 </li>
</ul>
</li>
</ul>
</br>

<ul>
<li>INTERSECT에서 같은 컬럼에 서로 다른 ALIAS를 부여했을 경우, 헤더값은 첫 번째 쿼리를 따라감</li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="그룹-함수">그룹 함수</h2>
</br>

<h3 id="rollup">ROLLUP</h3>
</br>

<ul>
<li>소그룹 간의 소계 및 총계를 계산하는 함수</li>
<li><strong>총합계</strong>!!!!</li>
</ul>
</br>

<table>
<thead>
<tr>
<th>유형</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>ROLLUP(A)</strong></td>
<td>- A로 그룹핑 <br> - 총합계</td>
</tr>
<tr>
<td><strong>ROLLUP(A,B)</strong></td>
<td>- A,B로 그룹핑 <br> - A로 그룹핑 <br> - 총합계</td>
</tr>
<tr>
<td><strong>ROLLUP(A,B,C)</strong></td>
<td>- A,B,C로 그룹핑 <br> - A,B로 그룹핑 <br> - A로 그룹핑 <br> - 총합계</td>
</tr>
</tbody></table>
</br>

<ul>
<li>ROLLUP(A,B) != ROLLUP(B,A)</li>
</ul>
</br>

<ul>
<li>ROLLUP((A,B),C)<ul>
<li>중간 NULL이 마지막 한 칸</li>
</ul>
</li>
<li>ROLLUP(A,(B,C))<ul>
<li>중간 NULL이 뒤에 두 칸</li>
</ul>
</li>
</ul>
<p></br></br></p>
<h3 id="cube">CUBE</h3>
</br>

<ul>
<li>조합할 수 있는 <strong>모든 소그룹에 대해</strong> 소계 및 총계 계산<ul>
<li>번외) GROUP BY는 일방향으로 그룹핑</li>
</ul>
</li>
</ul>
</br>

<ul>
<li>ROLLUP(A,B)<ul>
<li>ROLLUP은 중간 부분에서 A에 대한 총합계만 구함</li>
</ul>
</li>
<li>CUBE(A,B)<ul>
<li>CUBE는 ROLLUP과 달리 중간부분에서 B에 대한 총합계도 구함</li>
</ul>
</li>
</ul>
<p></br></br></p>
<table>
<thead>
<tr>
<th>CUBE 연산</th>
<th>그룹화 방식</th>
</tr>
</thead>
<tbody><tr>
<td><strong>CUBE (A)</strong></td>
<td>- A로 그룹핑 <br> - 총합계</td>
</tr>
<tr>
<td><strong>CUBE (A, B)</strong></td>
<td>- A, B로 그룹핑 <br> - A로 그룹핑 <br> - B로 그룹핑 <br> - 총합계</td>
</tr>
<tr>
<td><strong>CUBE (A, B, C)</strong></td>
<td>- A, B, C로 그룹핑 <br> - A, B로 그룹핑 <br> - A, C로 그룹핑 <br> - B, C로 그룹핑 <br> - A로 그룹핑 <br> - B로 그룹핑 <br> - C로 그룹핑 <br> - 총합계</td>
</tr>
</tbody></table>
<p></br></br></p>
<h3 id="grouping-sets">GROUPING SETS</h3>
</br>

<ul>
<li>특정 항목에 대해</li>
<li><strong>마지막에 ( )가 없다면 총합계 계산 X</strong></li>
<li>ROLLUP, CUBE 사용 가능 (사용시 총합계 계산 O)</li>
</ul>
<p></br></br></p>
<table>
<thead>
<tr>
<th>GROUPING SETS 연산</th>
<th>그룹화 방식</th>
</tr>
</thead>
<tbody><tr>
<td><strong>GROUPING SETS (A, B)</strong></td>
<td>- A로 그룹핑 <br> - B로 그룹핑 <br> - <strong>총합계X</strong></td>
</tr>
<tr>
<td><strong>GROUPING SETS (A, B, ( ))</strong></td>
<td>- A로 그룹핑 <br> - B로 그룹핑 <br> - 총합계</td>
</tr>
<tr>
<td><strong>GROUPING SETS (A, ROLLUP(B))</strong></td>
<td>- A로 그룹핑 <br> - B로 그룹핑 <br> - 총합계</td>
</tr>
<tr>
<td><strong>GROUPING SETS (A, ROLLUP(B, C))</strong></td>
<td>- A로 그룹핑 <br> - B, C로 그룹핑 <br> - B로 그룹핑 <br> - 총합계</td>
</tr>
<tr>
<td><strong>GROUPING SETS (A, B, ROLLUP(C))</strong></td>
<td>- A로 그룹핑 <br> - B로 그룹핑 <br> - C로 그룹핑 <br> - 총합계</td>
</tr>
</tbody></table>
<p><br><br></p>
<ul>
<li>ROLLUP<ul>
<li>인수 순서에 따라 결과 달라짐</li>
</ul>
</li>
<li>CUBE, GROUPING SETS<ul>
<li>인수 순서 영향 X</li>
</ul>
</li>
</ul>
<p></br></br></p>
<h3 id="grouping">GROUPING</h3>
</br>

<ul>
<li><strong>소계(합계)된 열은 1, 아니면 0</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQLD] 2-1. SQL 기본]]></title>
            <link>https://velog.io/@0-x-14/SQLD-2-1.-SQL-%EA%B8%B0%EB%B3%B8</link>
            <guid>https://velog.io/@0-x-14/SQLD-2-1.-SQL-%EA%B8%B0%EB%B3%B8</guid>
            <pubDate>Mon, 03 Mar 2025 14:54:27 GMT</pubDate>
            <description><![CDATA[<p></br></br></br></p>
<h2 id="관계형-데이터베이스">관계형 데이터베이스</h2>
<p></br></br></p>
<blockquote>
<ul>
<li>관계형 데이터베이스</li>
</ul>
</blockquote>
<ul>
<li>모든 데이터를 2차원 테이블 형태로 표현한 뒤 관계 정의</li>
</ul>
</br>

<blockquote>
<ul>
<li>TABLE</li>
</ul>
</blockquote>
<ul>
<li><strong>인스턴스 = Row</strong>, <strong>속성 = Column</strong></li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="select문">SELECT문</h2>
<p></br></br></p>
<blockquote>
<ul>
<li>SELECT문</li>
</ul>
</blockquote>
<ul>
<li>저장되어 있는 데이터를 <strong>조회</strong></li>
</ul>
<p></br></br></p>
<ul>
<li>SELECT ***** FROM 테이블;<ul>
<li>전체 컬럼이 조회됨</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li><strong>Alias</strong> (별칭)<ul>
<li>TABLE AS T, TABLE T와 같이 사용 가능</li>
<li>Alias를 설정했는데 Alias가 아닌 테이블명 사용시 에러 발생</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>ALIAS 별도로 지정 X시 컬럼명 <strong>대문자로 출력</strong><ul>
<li>ex) SELECT hire_date → 컬럼명 HIRE_DATE로 출력</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>산술연산자<ul>
<li>NULL 포함시 결과값도 NULL
<img src="https://velog.velcdn.com/images/0-x-14/post/2f8b4a20-d03f-4a47-9fc9-be32d92e3682/image.jpg" alt=""></li>
</ul>
</li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="함수">함수</h2>
</br>

<h3 id="문자-함수">문자 함수</h3>
</br>

<h4 id="chr아스키코드"><strong>CHR(아스키코드)</strong></h4>
<ul>
<li>아스키코드 매핑되는 문자로</li>
<li>CHR(65) → A</li>
</ul>
<p></br></br></p>
<h4 id="lower문자열"><strong>LOWER(문자열)</strong></h4>
<ul>
<li>소문자로 변환</li>
<li>LOWER(&#39;JENNIE&#39;) → jennie</li>
</ul>
<p></br></br></p>
<h4 id="upper문자열"><strong>UPPER(문자열)</strong></h4>
<ul>
<li>대문자로 변환</li>
<li>UPPER(&#39;jennie&#39;) → JENNIE</li>
</ul>
<p></br></br></p>
<h4 id="ltrim문자열특정문자-는-옵션"><strong>LTRIM(문자열[,특정문자])</strong> *[]는 옵션</h4>
<ul>
<li><strong>맨왼쪽</strong>에서 특정문자 제거</li>
<li>특정 문자 <strong>지정 X시 공백 제거</strong></li>
<li>맨왼쪽에서 지정한 문자들이 연속해서 존재하는 동안만 제거됨 (= 그뒤에 있는 건 제거 X)</li>
<li>LTRIM(&#39;AAABBABC&#39;, &#39;A&#39;) → BBABC (연속해서 있는 A 모두 제거됨)</li>
<li>LTRIM(&#39;DATABASE&#39;, &#39;DE&#39;) → ATABASE (D만이라도 제거됨)</li>
</ul>
<p></br></br></p>
<h4 id="rtrim문자열특정문자-는-옵션"><strong>RTRIM(문자열[,특정문자])</strong> *[]는 옵션</h4>
<ul>
<li><strong>맨오른쪽</strong>에서 특정문자 제거</li>
<li>특정 문자 지정 X시 공백 제거</li>
<li>맨오른쪽에서 지정한 문자들이 연속해서 존재하는 동안만 제거됨 (= 그앞에 있는 건 제거 X)</li>
<li>RTRIM(&#39;ABCABCC&#39;, &#39;C&#39;) → ABCAB (연속해서 있는 C 모두 제거됨)</li>
<li>RTRIM(&#39;SQL&#39;, &#39;LE&#39;) → SQ (L만이라도 제거됨)</li>
</ul>
<p></br></br></p>
<h4 id="trim위치-특정-문자-from-문자열-는-옵션"><strong>TRIM([위치] [특정 문자] [FROM] 문자열)</strong> *[]는 옵션</h4>
<ul>
<li>문자 <strong>한 글자만</strong> 지정 가능!!</li>
<li><strong>옵션이 없을 경우</strong><ul>
<li><strong>양쪽</strong>(맨왼쪽, 맨오른쪽) <strong>공백 제거</strong></li>
<li>TRIM(&#39;  JENNIE &#39;) → JENNIE</li>
</ul>
</li>
<li><strong>LEADING</strong><ul>
<li><strong>맨왼쪽</strong>에서만 제거</li>
<li>TRIM(LEDAING &#39;블&#39; FROM &#39;블랙핑크&#39;) → 랙핑크</li>
</ul>
</li>
<li><strong>TRAILING</strong><ul>
<li><strong>맨오른쪽</strong>에서만 제거</li>
<li>TRIM(TRAILING &#39;크&#39; FROM &#39;블랙핑크&#39;) → 블랙핑</li>
</ul>
</li>
<li><strong>BOTH</strong><ul>
<li><strong>양쪽</strong>(맨왼쪽, 맨오른쪽)에서 제거</li>
<li>TRIM(BOTH &#39;A&#39; FROM &#39;AAABACAAA&#39;) → BAC</li>
</ul>
</li>
</ul>
<p></br></br></p>
<h4 id="substr문자열-시작점-길이-는-옵션">SUBSTR(문자열, 시작점 [,길이] *[]는 옵션</h4>
<ul>
<li><strong>원하는 부분만 잘라서 반환</strong></li>
<li>길이 지정 X시 끝까지</li>
<li>문자열 맨앞 0 아니고 1부터 시작임!!</li>
<li>공백도 계산하기! 주의!</li>
</ul>
<p></br></br></p>
<h4 id="length문자열">LENGTH(문자열)</h4>
<ul>
<li>문자열 길이 반환</li>
</ul>
<p></br></br></p>
<h4 id="replace문자열-변경-전-문자열-변경-후-문자열-는-옵션">REPLACE(문자열, 변경 전 문자열 [,변경 후 문자열]) *[]는 옵션</h4>
<ul>
<li><strong>바꿔주는 함수</strong></li>
<li>변경 후 문자열 <strong>명시 X시</strong> 문자열에서 변경 전 <strong>문자열을 제거함</strong></li>
</ul>
<p></br></br></p>
<h4 id="lpad문자열-길이-문자">LPAD(문자열, 길이, 문자)</h4>
<ul>
<li>문자열이 길이가 될 때까지 <strong>왼쪽을 특정 문자로 채움</strong></li>
<li>LPAD(&#39;JENNIE&#39;, 10, &#39;V&#39;) → VVVVJENNIE</li>
</ul>
<p></br></br></p>
<h4 id="concat문자열1-문자열2">CONCAT(문자열1, 문자열2)</h4>
<ul>
<li>문자열1과 문자열2를 <strong>연결</strong>해줌</li>
</ul>
<p></br></br></p>
<h3 id="숫자-함수">숫자 함수</h3>
</br>

<h4 id="abs수">ABS(수)</h4>
<ul>
<li><strong>절댓값 반환</strong></li>
</ul>
<p></br></br></p>
<h4 id="sign수">SIGN(수)</h4>
<ul>
<li><strong>부호 반환</strong></li>
<li>양수 1, 음수 -1, 0이면 0</li>
</ul>
<p></br></br></p>
<h4 id="round수자릿수-는-옵션">ROUND(수[,자릿수]) *[]는 옵션</h4>
<ul>
<li>지정된 소수점 자릿수까지 <strong>반올림</strong>하여 반환</li>
<li>자릿수 명시 X시 <strong>기본값 0</strong></li>
<li>ROUND(163.76, 1) → 163.8</li>
<li>ROUND(163.76) → 164</li>
<li>ROUND(163.76, -1) → 160</li>
</ul>
<p></br></br></p>
<h4 id="ceil수">CEIL(수)</h4>
<ul>
<li><strong>소수점 이하의 수 올림</strong>해서 반환</li>
<li>CEIL(72.86) → 73</li>
<li>CEIL(7.1) → 8</li>
</ul>
</br>

<ul>
<li><em>ROUND(수)는 반올림, CEIL(수)는 올림</em></li>
</ul>
<p></br></br></p>
<h4 id="floor수">FLOOR(수)</h4>
<ul>
<li><strong>소수점 이하의 수 버림</strong></li>
<li>FLOOR(22.6) → 22</li>
</ul>
<p></br></br></p>
<h4 id="mod수1-수2">MOD(수1, 수2)</h4>
<ul>
<li>수1 % 수2 반환 (=<strong>나머지값 반환</strong>)</li>
<li><strong>수2가 0일 경우 1 반환</strong></li>
</ul>
<p></br></br></p>
<h3 id="날짜-함수">날짜 함수</h3>
</br>

<h4 id="sysdate">SYSDATE</h4>
<ul>
<li>현재의 연, 월, 일, 시, 분, 초 반환</li>
<li>SYSDATE → 2025-03-04 02:00:58</li>
</ul>
<p></br></br></p>
<h4 id="extract특정-단위-from-날짜-데이터">EXTRACT(특정 단위 FROM 날짜 데이터)</h4>
<ul>
<li>날짜 데이터에서 특정 단위(YEAR, MONTH, DAY, HOUR, MINUTE, SECOND)만을 출력</li>
<li>EXTRACT(YEAR FROM SYSDATE) → 2025</li>
</ul>
<p></br></br></p>
<h4 id="add_months날짜-데이터-특정-개월-수">ADD_MONTHS(날짜 데이터, 특정 개월 수)</h4>
<ul>
<li>날짜 데이터에서 특정 개월 수를 더한 날짜 반환</li>
<li>일자가 존재하지 않을 경우 해당 월의 마지막 일자 반환</li>
<li>ADD_MONTHS(DATE &#39;2025-01-31&#39;, 1) → 2025-02-28</li>
</ul>
<p></br></br></p>
<h3 id="변환-함수">변환 함수</h3>
</br>

<h4 id="명시적-형변환과-암시적-형변환">명시적 형변환과 암시적 형변환</h4>
<p>명시적 형변환 - 변환 함수 사용
암시적 형변환 - DB가 내부적으로 알아서
</br></br></p>
<h4 id="명시적-형변환에-쓰이는-함수">명시적 형변환에 쓰이는 함수</h4>
</br>

<ul>
<li>TO_NUMBER(문자열)<ul>
<li>문자열 → 숫자형으로 변환</li>
<li>TO_NUMBER(&#39;1234&#39;) → 1234</li>
<li>TO_NUMBER(&#39;abc&#39;) → 에러 발생</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>TO_CHAR(수 or 날짜[, 포맷]) *[]는 옵션<ul>
<li>수 or 날짜 → (포맷 형식의) 문자형</li>
<li>TO_CHAR(1234) → &#39;1234&#39;</li>
<li>TO_CHAR(SYSDATE, &#39;YYYY-MM-DD HH24:MI:SS&#39;) → &#39;2025-03-04 20:47:38&#39;</li>
</ul>
</li>
</ul>
<p></br></br></p>
<h3 id="null-관련-함수">NULL 관련 함수</h3>
</br>

<h4 id="nvl인수1-인수2">NVL(인수1, 인수2)</h4>
<ul>
<li><strong>인수1이 NULL일 경우 인수2 반환</strong></li>
<li>인수1이 <strong>NULL이 아닐 경우 인수1 반환</strong></li>
</ul>
<p></br></br></p>
<h4 id="nullif인수1-인수2">NULLIF(인수1, 인수2)</h4>
<ul>
<li>인수1과 인수2가 <strong>같으면 NULL 반환</strong></li>
<li><strong>같지 않으면 인수1 반환</strong></li>
</ul>
<p></br></br></p>
<h4 id="coalesce인수1-인수2-인수3-">COALESCE(인수1, 인수2, 인수3, ...)</h4>
<ul>
<li>NULL이 아닌 최초의 인수 반환</li>
</ul>
<p></br></br></p>
<h4 id="nvl2인수1-인수2-인수3">NVL2(인수1, 인수2, 인수3)</h4>
<ul>
<li><strong>인수1이 NULL이 아닌 경우 인수2 반환</strong></li>
<li><strong>NULL인 경우 인수3 반환</strong></li>
<li>NVL에서 한 칸 밀린 버전이라고 생각하기...</li>
</ul>
<p></br></br></p>
<h3 id="case">CASE</h3>
</br>

<ul>
<li><strong>별도의 ELSE가 없을 경우 NULL값이 DEFAULT값이 됨</strong></li>
<li>Oracle의 <strong>DECODE</strong> 함수도 같은 기능<ul>
<li>DECODE또 DEFAULT값이 별도로 없을 경우 NULL값이 DEFAULT값이 됨</li>
</ul>
</li>
</ul>
<blockquote>
<p>CASE WHEN SUBWAY_LINE = &#39;1&#39; THEN &#39;BLUE&#39;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WHEN SUBWAY_LINE = &#39;2&#39; THEN &#39;GREEN&#39;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WHEN SUBWAY_LINE = &#39;3&#39; THEN &#39;ORANGE&#39;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ELSE &#39;GRAY&#39;]
END</p>
</blockquote>
<blockquote>
<p>CASE SUBWAY_LINE
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WHEN &#39;1&#39; THEN &#39;BLUE&#39;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WHEN &#39;2&#39; THEN &#39;GREEN&#39;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WHEN &#39;3&#39; THEN &#39;ORANGE&#39;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ELSE &#39;GRAY&#39;]
END</p>
</blockquote>
<blockquote>
<p>DECODE (SUBWAY_LINE, &#39;1&#39;, &#39;BLUE&#39;, &#39;2&#39;, &#39;GREEN&#39;, &#39;3&#39;, &#39;ORANGE&#39;[, &#39;GRAY&#39;])</p>
</blockquote>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="where-절">WHERE 절</h2>
</br>

<h3 id="비교-연산자">비교 연산자</h3>
</br>

<table>
<thead>
<tr>
<th>연산자</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>=</td>
<td>같음</td>
</tr>
<tr>
<td>&lt;</td>
<td>작음</td>
</tr>
<tr>
<td>&lt;=</td>
<td>작거나 같음</td>
</tr>
<tr>
<td>&gt;</td>
<td>큼</td>
</tr>
<tr>
<td>&gt;=</td>
<td>크거나 같음</td>
</tr>
</tbody></table>
</br>

<ul>
<li><p>조건식 컬럼명 <em>우측에 위치해도 정상적으로 동작 O</em></p>
</li>
<li><p><em>SQL에서는 같음을 의미하는 연산자가 ==가 아니라 =이다.</em></p>
</li>
</ul>
</br>



<p></br></br></p>
<h3 id="부정-비교-연산자">부정 비교 연산자</h3>
</br>

<table>
<thead>
<tr>
<th>연산자</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>!=</td>
<td>같지 않음</td>
</tr>
<tr>
<td><strong>^=</strong></td>
<td><strong>같지 않음</strong></td>
</tr>
<tr>
<td><strong>&lt;&gt;</strong></td>
<td><strong>같지 않음</strong></td>
</tr>
<tr>
<td>not 컬럼명 =</td>
<td>같지 않음</td>
</tr>
<tr>
<td>not 컬럼명 &gt;</td>
<td>크지 않음</td>
</tr>
</tbody></table>
<p></br></br></p>
<h3 id="sql-연산자">SQL 연산자</h3>
</br>

<table>
<thead>
<tr>
<th>연산자</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>BETWEEN A AND B</td>
<td>A와 B 사이 (A,B 범위에 포함)</td>
</tr>
<tr>
<td>LIKE &#39;비교 문자열&#39;</td>
<td>% : 문자열, _ : 문자, %,_ 포함된 문자 검색시 ESCAPE 지정</td>
</tr>
<tr>
<td><strong>IN (LIST)</strong></td>
<td><strong>LIST 중 하나와 일치</strong> ex) where col in (1, 3, 5)</td>
</tr>
<tr>
<td>IS NULL</td>
<td>NULL값</td>
</tr>
</tbody></table>
</br>

<ul>
<li>LIKE <strong>앞뒤로</strong> &#39;%&#39;가 붙으면 <strong>어디에든</strong> 해당 문자열이 들어간 행을 출력함</li>
</ul>
<p></br></br></p>
<h3 id="부정-sql-연산자">부정 SQL 연산자</h3>
</br>

<table>
<thead>
<tr>
<th>연산자</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>NOT BETWEEN A AND B</td>
<td>A와 B 사이가 아님 (A,B 제외되는 범위에 포함)</td>
</tr>
<tr>
<td>NOT IN (LIST)</td>
<td>LIST 중 일치하는 것이 없음</td>
</tr>
<tr>
<td>IS NOT NULL</td>
<td>NULL값 아님</td>
</tr>
</tbody></table>
<p></br></br></p>
<h3 id="논리-연산자">논리 연산자</h3>
</br>

<blockquote>
<ul>
<li>논리연산자 <strong>우선순위</strong></li>
</ul>
</blockquote>
<ul>
<li><strong>( ) → NOT → AND → OR</strong></li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="group-by-having-절">GROUP BY, HAVING 절</h2>
</br>

<h3 id="group-by">GROUP BY</h3>
<blockquote>
<p>GROUP BY - 데이터를 <strong>그룹별로 묶을 수 있게</strong> 해줌</p>
</blockquote>
<p></br></br></p>
<h3 id="집계-함수">집계 함수</h3>
</br>

<blockquote>
<p>집계함수는 <strong>NULL 제외</strong>하고 연산!!</p>
</blockquote>
</br>

<ul>
<li>COUNT(컬럼)<ul>
<li>COUNT(DISTINCT 컬럼) - 중복 제거 후 Count</li>
</ul>
</li>
<li>SUM(컬럼)</li>
<li>AVG(컬럼)<ul>
<li>ex) NULL, 25, 65일 경우 (25+65)/2로 계산</li>
</ul>
</li>
<li>MIN(컬럼)</li>
<li>MAX(컬럼)</li>
</ul>
<p></br></br></p>
<h3 id="having">HAVING</h3>
</br>

<ul>
<li>GROUP BY절 사용시 WHERE 절처럼 사용하는 조건절</li>
<li>데이터를 그룹핑한 후 <strong>특정 그룹 골라낼 때 사용</strong></li>
<li><em>테이블 전체가 한 개의 그룹이 되는 경우</em> GROUP BY 없이 HAVING만 단독으로 사용 가능</li>
</ul>
</br>

<ul>
<li><strong>SELECT문 논리적 수행 순서</strong>
<img src="https://velog.velcdn.com/images/0-x-14/post/f55e62f1-6b8c-4baf-a8aa-f357be9b9ce8/image.jpg" alt=""></li>
</ul>
</br>

<ul>
<li><em>HAVING 절은 SELECT 절보다 먼저 수행되므로 SELECT 절에서 기술된 ALIAS <strong>사용 불가</strong></em></li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="order-by-절">ORDER BY 절</h2>
</br>

<h3 id="order-by">ORDER BY</h3>
<ul>
<li>SELECT한 데이터 정렬</li>
</ul>
</br>

<ul>
<li>조건 여러개 가능. 여러개일 경우 앞에서부터</li>
</ul>
</br>

<ul>
<li>ASC 오름차순, DESC 내림차순<ul>
<li>옵션 생략시 ASC가 기본값</li>
</ul>
</li>
</ul>
</br>

<ul>
<li>Oracle은 NULL을 최댓값으로 취급함<ul>
<li>문제에서 Oracle이라고 명시하지 않았을 경우 DBMS마다 다르므로 주의</li>
</ul>
</li>
</ul>
</br>

<ul>
<li>컬럼명 말고 숫자로도 가능<ul>
<li>ex) ORDER BY 1 → 첫 번째 컬럼 기준으로 오름차순 정렬</li>
</ul>
</li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="join">JOIN</h2>
</br>


<h3 id="join-1">JOIN</h3>
<ul>
<li>테이블 간에 PK, FK의 연관 관계가 없어도 JOIN 가능</li>
</ul>
<p></br></br></p>
<h3 id="equi-join">EQUI JOIN</h3>
<ul>
<li>EQUAL JOIN (=)</li>
</ul>
<p></br></br></p>
<h3 id="non-equi-join">Non EQUI JOIN</h3>
<ul>
<li>Non EQUI JOIN (BETWEEN, &gt;, &gt;=, &lt;, &lt;=)</li>
</ul>
<p></br></br></p>
<h3 id="3개-이상-table-join">3개 이상 TABLE JOIN</h3>
<ul>
<li>EQUI JOIN과 Non EQUI JOIN은 하나의 쿼리에서 <em>같이 사용할 수 <strong>있다</strong>.</em></li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="standard-join">STANDARD JOIN</h2>
</br>


<h3 id="inner-join">INNER JOIN</h3>
<ul>
<li>JOIN 조건에 충족하는 데이터만 출력</li>
<li>JOIN 조건을 ON을 사용해서 작성</li>
</ul>
<p></br></br></p>
<h3 id="outer-join">OUTER JOIN</h3>
<ul>
<li><strong>반대편</strong> 테이블의 옆에 <strong>(+)</strong> 기호 붙여서 작성<ul>
<li>한쪽에만 사용해야 함</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/0-x-14/post/70c93422-96c2-491d-bcd3-6ba415fabb34/image.jpg" alt=""></p>
<ul>
<li>반대편 테이블에 JOIN되는 데이터가 없는 Row들을 반대편 테이블 컬럼의 값이 NULL로 출력됨</li>
</ul>
<p></br></br></p>
<h3 id="full-outer-join">FULL OUTER JOIN</h3>
<ul>
<li>모두 출력됨</li>
</ul>
<p></br></br></p>
<h3 id="natural-join">NATURAL JOIN</h3>
<ul>
<li><em>같은 이름을 가진 컬럼들이 <strong>모두 동일한 데이터를 가지고 있을 경우</strong></em> JOIN됨</li>
<li>A <strong>NATURAL JOIN</strong> B</li>
<li>NATURAL JOIN에는 <strong>ON 절 사용 불가</strong>, <strong>ALIAS 사용 불가</strong> 사용시 에러 발생</li>
</ul>
</br>

<ul>
<li><strong>JOIN</strong> <del>~</del> <strong>USING</strong> (특정 컬럼들)<ul>
<li>이 형태도 NATURAL JOIN임</li>
<li>특정 컬럼들이 동일한 데이터를 가지고 있을 경우</li>
<li>단, USING 사용시 <strong>ALIAS 사용 불가</strong></li>
</ul>
</li>
</ul>
<p></br></br></p>
<h3 id="cross-join">CROSS JOIN</h3>
<ul>
<li>조합할 수 있는 모든 경우 출력</li>
<li>Cartesian Product</li>
</ul>
<p><img src="https://velog.velcdn.com/images/0-x-14/post/c1fb0528-de2d-406d-8a1c-b8fcfb3d63b8/image.jpg" alt=""></p>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="출처">출처</h2>
<ul>
<li>유선배 SQLD 과외노트</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQLD] 1-2. 데이터 모델과 SQL]]></title>
            <link>https://velog.io/@0-x-14/SQLD-1-2.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EA%B3%BC-SQL</link>
            <guid>https://velog.io/@0-x-14/SQLD-1-2.-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AA%A8%EB%8D%B8%EA%B3%BC-SQL</guid>
            <pubDate>Mon, 03 Mar 2025 11:39:14 GMT</pubDate>
            <description><![CDATA[<p></br></br></br></p>
<h2 id="정규화-normalization">정규화 (Normalization)</h2>
<p></br></br></p>
<blockquote>
<ul>
<li><strong>정규화</strong></li>
</ul>
</blockquote>
<ul>
<li>엔터티를 <strong>작은 단위로 분리</strong>하는 과정</li>
<li>입력, 수정, 삭제 성능 일반적으로 향상됨</li>
<li>데이터의 <strong>무결성을 보장하기 위해</strong> 수행</li>
</ul>
<p></br></br></br></p>
<ul>
<li>제1정규형<ul>
<li>모든 속성은 반드시 <strong>하나의 값만</strong> 가져야 함
</br></br></li>
</ul>
</li>
<li>제2정규형<ul>
<li>모든 일반속성이 반드시 모든 주식별자에 종속되어야 함</li>
<li>제2정규화 - 주식별자가 <strong>복합식별자인 경우 부분 종속을 없애야 함</strong></li>
<li>제2정규화 - 현재 테이블의 주제와 관련없는 컬럼을 다른 테이블로 뺌
</br></br></li>
</ul>
</li>
<li>제3정규형<ul>
<li>주식별자가 아닌 모든 속성 간에는 서로 종속될 수 없음</li>
<li>제3정규화 - 일반속성에만 종속된 속성은 다른 테이블로 뺌</li>
<li>제3정규형을 만족하는 엔티티의 일반 속성은 주식별자 전체에 종속적임</li>
</ul>
</li>
</ul>
</br>

<p>✅ 어떤 릴레이션 R이 제2정규형이고, 기본키에 속하지 않은 속성 모두가 기본키에 이행적 함수종속이 아닐 때 제3정규형에 속한다.</p>
</br>

<p>✅ 정규화는 <strong>논리</strong> 데이터 모델의 일관성을 확보하고 중복을 제거하여 속성들이 가장 적절한 인스턴스에 배치되도록 한다.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  → 개념 데이터 모델 아님!!!! 논리 데이터 모델!!!!!!!!</p>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="반정규화-de-normalization">반정규화 (De-Normalization)</h2>
<p></br></br></p>
<blockquote>
<ul>
<li>반정규화</li>
</ul>
</blockquote>
<ul>
<li><strong><em>조회 성능 향상을 위해</em> 데이터 중복 허용 / 데이터 그룹핑</strong></li>
<li>입력, 수정, 삭제 성능은 저하될수도</li>
<li><em>정규화가 끝난 후에</em></li>
</ul>
<p></br></br></p>
<ul>
<li>테이블 <strong>병합</strong><ul>
<li><strong>1:1</strong> / <strong>1:M</strong> / 슈퍼 서브 타입</li>
<li>JOIN이 많을 경우 고려</li>
<li>1:M 테이블 병합 - 1쪽 엔티티 속성 개수가 <em>적은 경우에만</em></li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>테이블 <strong>분할</strong><ul>
<li>테이블 <strong>수직</strong> 분할<ul>
<li><em>일부 속성을</em> 별도의 엔터티로. 1:1 관계 성립</li>
<li>자주 사용 X거나 대부분의 속성값이 NULL일 경우 고려
<img src="https://velog.velcdn.com/images/0-x-14/post/e263308a-408f-45eb-ae7c-f88ed4f0147a/image.jpg" alt=""></li>
</ul>
</li>
<li>테이블 <strong>수평</strong> 분할<ul>
<li><em>인스턴스를 특정 기준으로</em> 분할</li>
<li><strong>파티셔닝</strong>
  <img src="https://velog.velcdn.com/images/0-x-14/post/883f9f5f-3cff-4646-8d1f-77a856f402fd/image.jpg" alt=""></li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>테이블 <strong>추가</strong><ul>
<li><strong>중복</strong> 테이블 추가<ul>
<li>필요할 경우 데이터 중복 감안</li>
</ul>
</li>
<li><strong>통계</strong> 테이블 추가<ul>
<li>통계치 미리 계산하여 저장</li>
</ul>
</li>
<li><strong>이력</strong> 테이블 추가<ul>
<li>ex) 상품 가격 이력 테이블</li>
</ul>
</li>
<li><strong>부분</strong> 테이블 추가<ul>
<li>특정 범위 데이터를 자주 처리할 경우</li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>컬럼 반정규화<ul>
<li>중복 컬럼 추가</li>
<li>파생 컬럼 추가</li>
<li>이력 테이블 컬럼 추가</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>관계 반정규화<ul>
<li>중복 관계 추가</li>
</ul>
</li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="트랜잭션-transaction">트랜잭션 (Transaction)</h2>
<p></br></br></p>
<blockquote>
<ul>
<li><strong>트랜잭션</strong></li>
</ul>
</blockquote>
<ul>
<li>데이터를 조작하기 위한 하나의 논리적인 작업 단위</li>
</ul>
<p></br></br></p>
<ul>
<li>트랜잭션의 특징<ul>
<li><strong>원자성</strong><ul>
<li>전부 처리되거나 아예 하나도 처리되지 않아야 함</li>
</ul>
</li>
<li><strong>일관성</strong><ul>
<li>일관된 상태여야 함. 모순X</li>
</ul>
</li>
<li><strong>고립성</strong><ul>
<li>실행 중엔 다른 트랜잭션 접근 불가</li>
</ul>
</li>
<li><strong>지속성</strong><ul>
<li>성공적으로 실행 완료시 영속적으로 저장됨</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>✅ 순차적으로 수행되는 작업 A와 B가 원자성을 지닐 경우 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A까지만 수행하고 오류 발생했다면 A를 undo해야 함</p>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="null">NULL</h2>
<p></br></br></p>
<blockquote>
<ul>
<li><strong>NULL</strong></li>
</ul>
</blockquote>
<ul>
<li>존재하지 않음. 값이 없음. 모르는 값</li>
</ul>
<p></br></br></p>
<ul>
<li>가로 연산<ul>
<li>NULL 포함시 결과값도  NULL</li>
</ul>
</li>
</ul>
</br>

<ul>
<li>세로 연산<ul>
<li>NULL값 제외하고 연산</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>✅ NULL과의 <strong>비교 연산 결과는 항상 Unknown</strong> (IS NULL 제외)</li>
<li>SUM,COUNT, AVG 등 <strong>집계함수는 NULL 제외하고 연산</strong></li>
</ul>
<p></br></br></p>
<p>✅ WHERE COL IS NULL과 WHERE COL = NULL은 다름!!!
✅ NULL = NULL 연산의 결과는 FALSE 또는 Unknown</p>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="본질식별자-vs-인조식별자">본질식별자 vs 인조식별자</h2>
<p></br></br></p>
<ul>
<li><strong>본질식별자</strong><ul>
<li>가공 X 원래의 식별자</li>
<li><em>원조식별자</em></li>
</ul>
</li>
</ul>
</br>

<ul>
<li><strong>인조식별자</strong><ul>
<li>PK 속성이 두 개 이상인 경우 하나로 묶어서 사용하는 식별자</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>복합식별자인 경우 간결하게 대체하기 위해 인조식별자 생성 가능</li>
</ul>
<p>✅ 인조식별자 사용시 중복 데이터 막기 어려워짐</p>
<p></br></br></p>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="출처">출처</h2>
<ul>
<li>유선배 SQLD 과외노트</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQLD] 1-1. 데이터 모델링의 이해]]></title>
            <link>https://velog.io/@0-x-14/SQLD-1-1.-%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/@0-x-14/SQLD-1-1.-%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>Sun, 02 Mar 2025 12:54:53 GMT</pubDate>
            <description><![CDATA[<p></br></br></br></p>
<h2 id="데이터-모델의-이해">데이터 모델의 이해</h2>
<p></br></br></p>
<blockquote>
<p><strong>모델링</strong>이란 <em>현실 세계를 단순화하여 표현하는 기법</em> 이다.</p>
</blockquote>
<p></br></br></p>
<ul>
<li>모델링의 <strong>특징</strong><ul>
<li><strong>추상화</strong><ul>
<li>현실 세계를 <strong>일정한 형식</strong>에 맞추어 표현</li>
</ul>
</li>
<li><strong>단순화</strong><ul>
<li>복잡한 현실을 <strong>제한된</strong> 언어나 표기법으로 이해하기 쉽게 함</li>
</ul>
</li>
<li><strong>명확화</strong><ul>
<li>애매모호함을 배제하고 누구나 이해가 가능하도록 <strong>정확</strong>하게 현상을 기술</li>
</ul>
</li>
</ul>
</li>
</ul>
</br>


<table>
<thead>
<tr>
<th>구분</th>
<th>추상화</th>
<th>단순화</th>
</tr>
</thead>
<tbody><tr>
<td>목적</td>
<td>일반화</td>
<td>간소화</td>
</tr>
<tr>
<td>방법</td>
<td>불필요한 세부 사항 제거</td>
<td>생략 or 단순한 방식으로 표현</td>
</tr>
<tr>
<td>비교</td>
<td>무엇이 중요한가</td>
<td>어떻게 쉽게 표현할 것인가</td>
</tr>
</tbody></table>
</br>

<p>✅ 모델링 시스템 구현만을 위한다? → X
&nbsp;&nbsp;&nbsp;&nbsp; 시스템 구현 포함 업무 분석 및 업무 형상화 목적</p>
<p></br></br></p>
<ul>
<li>모델링의 <strong>관점</strong><ul>
<li><strong>데이터</strong> 관점</li>
<li><strong>프로세스</strong> 관점</li>
<li>데이터와 프로세스의 <strong>상관</strong> 관점<ul>
<li>데이터-프로세스의 <strong>관계</strong>를 위주로 모델링</li>
<li>프로세스의 흐름에 따라 데이터가 <em>어떤 영향을 받는지</em></li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>모델링시 <strong>유의사항</strong><ul>
<li><strong>중복</strong></li>
<li><strong>비유연성</strong><ul>
<li>유연성 - <strong>유지보수</strong>를 위해</li>
</ul>
</li>
<li><strong>비일관성</strong><ul>
<li>다른 데이터 연관성 고려하지 않고 일부 데이터만 변경시 발생</li>
<li>데이터 간 <strong>연관관계 명확하게</strong> 정의해야 함</li>
</ul>
</li>
</ul>
</li>
</ul>
</br>

<p>✅ 유의사항 문제 나오면 중복, 비유연성, 비일관성 먼저 찾기</p>
<p></br></br></p>
<ul>
<li>모델링 <strong>단계</strong><ul>
<li><strong>개념적</strong> 데이터 모델링<ul>
<li>첫 번째 단계, 전산적</li>
</ul>
</li>
<li><strong>논리적</strong> 데이터 모델링<ul>
<li>두 번째 단계, 재사용성 높음</li>
</ul>
</li>
<li><strong>물리적</strong> 데이터 모델링<ul>
<li>세 번째 단계, 실제 구현 위해</li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li><strong>3단계 스키마 구조</strong><ul>
<li><strong>외부</strong> 스키마<ul>
<li><strong>여러 사용자</strong> 관점</li>
</ul>
</li>
<li><strong>개념</strong> 스키마<ul>
<li><strong>통합</strong>된 관점. 모든 사용자 관점 통합</li>
</ul>
</li>
<li><strong>내부</strong> 스키마<ul>
<li><strong>물리적</strong>인 관점. 실질적</li>
</ul>
</li>
</ul>
</li>
</ul>
</br>

<p>✅ &lt;헷갈리지 않게 주의&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 여러 사용자 → 외부 스키마
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 모든 사용자 관점 통합 → 개념 스키마</p>
<p></br></br></p>
<ul>
<li>3단계 스키마 구조가 보장하는 <strong>독립성</strong><ul>
<li><strong>논리적</strong> 독립성<ul>
<li><em>개념</em> 스키마가 변경되어도 <em>외부</em> 스키마는 영향 X</li>
</ul>
</li>
<li><strong>물리적</strong> 독립성<ul>
<li><em>내부</em> 스키마가 변경되어도 <em>외부, 개념</em> 스키마는 영향 X</li>
<li>✅ 데이터베이스의 파일 구조 변화가 논리스키마에 영향을 주지 않음</li>
<li>✅ 데이터베이스의 색인 구조 변화가 응용 프로그램에 영향을 주지 않음</li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>ERD<ul>
<li>IE/Crow&#39;s Foot : 가장 많이 사용</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>IE/Crow&#39;s Foot 표기법
<img src="https://velog.velcdn.com/images/0-x-14/post/4d1a9a64-f681-4985-838c-5055c3205b3f/image.png" alt=""></li>
</ul>
<p></br></br></p>
<p><strong>✅ ERD 작성 순서</strong></p>
<p>&nbsp;엔터티 도출 및 그림 
&nbsp;&nbsp; → 배치 
&nbsp;&nbsp; → 관계 설정 
&nbsp;&nbsp; → 관계명 기입 
&nbsp;&nbsp; → 참여도 기입 
&nbsp;&nbsp; → 필수/선택 여부 기입</p>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="엔터티-entity">엔터티 (Entity)</h2>
<p></br></br></p>
<blockquote>
<p>엔터티 - Table
인스턴스 - Row
속성 - Column</p>
</blockquote>
<p></br></br></p>
<p><img src="https://velog.velcdn.com/images/0-x-14/post/c457121e-9ae2-4627-87d4-10587f4b55c7/image.png" alt=""></p>
<p></br></br></p>
<ul>
<li>엔터티 <strong>특징</strong><ul>
<li>업무에서 쓰이는 정보여야 함</li>
<li><strong>유니크함</strong>을 보장할 수 있는 <strong>식별자</strong>가 있어야 함 (중복X, 모호X)</li>
<li><strong>2개 이상의 인스턴스</strong>를 가지고 있어야 함</li>
<li>반드시 <strong>속성</strong>을 가지고 있어야 함</li>
<li>다른 엔터티와 1개 이상의 <strong>관계</strong>를 가지고 있어야 함</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>엔터티 분류 - 유형 vs 무형<ul>
<li><strong>유형</strong> 엔터티<ul>
<li><em>물리적</em> 형태 O</li>
</ul>
</li>
<li><strong>개념</strong> 엔터티<ul>
<li>물리적 형태 X, <em>개념적</em></li>
</ul>
</li>
<li><strong>사건</strong> 엔터티<ul>
<li><em>행위를 통해</em> 발생</li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>엔터티 분류 - <strong>발생시점</strong><ul>
<li><strong>기본</strong> 엔터티<ul>
<li><em>원래 존재</em>. 독립적. 자식 엔터티 가질 수 O</li>
</ul>
</li>
<li><strong>중심</strong> 엔터티<ul>
<li>기본 엔터티로부터 <em>파생</em>, 행위 엔터티 생성</li>
</ul>
</li>
<li><strong>행위</strong> 엔터티<ul>
<li>2개 이상의 엔터티로부터 파생</li>
<li>ex) 주문 내역, 이벤트 응모 이력</li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></br></p>
<p>✅ <strong>엔터티명</strong> 정할 때 <strong>규칙</strong> </p>
<p>&nbsp;&nbsp; - 실제 쓰이는 용어 사용
&nbsp;&nbsp;&nbsp; - 영문 대문자 표기 / 한글 약어 X
&nbsp;&nbsp;&nbsp; - 단수 명사
&nbsp;&nbsp;&nbsp; - 띄어쓰기 X 
&nbsp;&nbsp;&nbsp; - 의미상 중복 X (주문, 결제는 중복 가능)
&nbsp;&nbsp;&nbsp; - 명확하게 표현</p>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="속성-attribute">속성 (Attribute)</h2>
<p></br></br></p>
<blockquote>
<p><strong>속성</strong> - <strong>더이상 쪼개지지 X</strong>. <strong>최소의 데이터 단위.</strong> 필요해야 함</p>
</blockquote>
<p></br></br></p>
<ul>
<li><strong>특성</strong>에 따른 분류<ul>
<li><strong>기본</strong> 속성<ul>
<li>바로 정의 가능</li>
</ul>
</li>
<li><strong>설계</strong> 속성<ul>
<li><strong>설계 과정에서</strong> 만들어짐</li>
<li>ex) <em>고유번호</em></li>
</ul>
</li>
<li><strong>파생</strong> 속성<ul>
<li>다른 속성으로부터 <strong>파생된</strong> 속성</li>
<li>ex) <em>계산된 값</em></li>
</ul>
</li>
</ul>
</li>
</ul>
</br>

<p>✅ 설계 vs 파생 헷갈리지 않게 주의
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 계산된 값은 파생된 속성이므로 파생속성!!!</p>
<p></br></br></p>
<ul>
<li><strong>구성 방식</strong>에 따른 분류<ul>
<li><strong>PK</strong> 속성<ul>
<li><em>유니크함</em> 부여</li>
</ul>
</li>
<li><strong>FK</strong> 속성<ul>
<li>다른 엔터티와 관계 맺게 해주는 <em>매개체</em> 역할</li>
</ul>
</li>
<li><strong>일반</strong> 속성<ul>
<li>PK, FK 제외한 <em>나머지</em></li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li><strong>도메인</strong> ✅<ul>
<li>속성이 가질 수 있는 <strong>속성값의 범위</strong></li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li><strong>용어 사전</strong><ul>
<li><em>속성 이름 정확, 직관적으로 부여하기 위해</em></li>
<li>용어 혼란 없애기 위해</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li><strong>시스템 카탈로그</strong><ul>
<li><em>시스템 관련 데이터 담고있는 DB</em></li>
<li>SQL 이용하여 조회 가능</li>
</ul>
</li>
</ul>
<p></br></br></p>
<p>✅ <strong>속성 명칭</strong> - 다른 테이블이더라도 속성명 다르게!!! 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>전체 데이터 모델에서 유일성 확보하도록</strong></p>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="관계-relationship">관계 (Relationship)</h2>
<p></br></br></p>
<ul>
<li>존재 관계<ul>
<li>존재 자체로 연관성이 있는 관계</li>
<li>ex) 엄마-아기, 직원-부서, 학생-학과</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>행위 관계<ul>
<li>특정한 행위를 함으로써 연관성이 생기는 관계</li>
<li>ex) 회원-주문, 학생-출석부</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>표기법<ul>
<li><strong>관계명</strong> (Membership)<ul>
<li>관계의 이름</li>
<li>명확한 문장, 현재형</li>
<li>ex) 포함한다, 소속된다 ex) 포함한다, 소속된다</li>
</ul>
</li>
<li><strong>관계차수</strong> (Cardinality)<ul>
<li>관계에 <em>참여하는 수</em>
<img src="https://velog.velcdn.com/images/0-x-14/post/f7836f89-5d3f-4e64-867e-84364140a9b4/image.jpg" alt=""></li>
</ul>
</li>
<li><strong>관계선택사양</strong> (Optionality)<ul>
<li><em>필수/선택 여부</em>
<img src="https://velog.velcdn.com/images/0-x-14/post/784fe5b6-a09b-449f-81af-d748320c068c/image.jpg" alt=""></li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>✅ 엔터티 간 관계 도출시 확인사항<ul>
<li>연관규칙 존재?</li>
<li>정보 조합 발생 O?</li>
<li>업무기술서, 장표에 관계연결에 대한 규칙 서술 O?</li>
<li>업무기술서, 장표에 관계연결을 가능하게 하는 동사(Verb) 존재 O?</li>
</ul>
</li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="식별자-identifiers">식별자 (Identifiers)</h2>
<p></br></br></p>
<blockquote>
<p><strong>식별자</strong> - 인스턴스를 구분 가능하게 해주는 대표격 속성</p>
</blockquote>
<p></br></br></p>
<ul>
<li><strong>주식별자</strong> (<strong>PK</strong>, Primary Key)<ul>
<li><strong>유일성</strong><ul>
<li>_유니크함 부여_하여 식별 가능하도록</li>
</ul>
</li>
<li><strong>최소성</strong><ul>
<li><em>최소 개수</em> 속성 (유일성 보장하는)</li>
</ul>
</li>
<li><strong>불변성</strong><ul>
<li>속성값 <em>변하지 X</em></li>
<li>✅ &#39;자주 변하지 않는 것이어야 한다&#39;라고 되어있어도 O</li>
</ul>
</li>
<li><strong>존재성</strong><ul>
<li>속성값 <em>NULL X</em></li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li>분류 - <em>대표성</em> 여부<ul>
<li><strong>주식별자</strong> (PK)</li>
<li><strong>보조식별자</strong></li>
</ul>
</li>
</ul>
</br>


<ul>
<li>분류 - <em>스스로 생성되었는지</em> 여부<ul>
<li><strong>내부식별자</strong><ul>
<li>엔터티 내부에서 스스로 생성됨</li>
</ul>
</li>
<li><strong>외부식별자</strong> (FK)</li>
</ul>
</li>
</ul>
</br>


<ul>
<li>분류 - <em>단일 속성</em> 여부<ul>
<li><strong>단일식별자</strong></li>
<li><strong>복합식별자</strong></li>
</ul>
</li>
</ul>
</br>

<ul>
<li>분류 - <em>대체</em> 여부<ul>
<li><strong>본질식별자</strong><ul>
<li>가공 X 원래의 식별자</li>
<li><em>원조식별자</em></li>
<li>✅ 엔터티 내 집합을 명확하게 설명할 수 있는 업무적으로 의미가 부여된 식별자</li>
</ul>
</li>
<li><strong>인조식별자</strong><ul>
<li>PK 속성이 두 개 이상인 경우 하나로 묶어서 사용하는 식별자</li>
</ul>
</li>
</ul>
</li>
</ul>
<p></br></br></p>
<ul>
<li><strong>식별자</strong> 관계<ul>
<li>부모 엔터티의 식별자가 자식 엔터티의 <em>PK</em></li>
<li><strong>실선</strong>으로 표시</li>
<li>부모 엔터티가 있어야 생성 가능</li>
<li>단일식별자/복합식별자에 따라 1:1/1:M 결정</li>
</ul>
</li>
<li><strong>비식별자</strong> 관계<ul>
<li>부모 엔터티의 식별자가 자식 엔터티의 <em>일반 속성</em></li>
<li><strong>점선</strong>으로 표시</li>
<li>부모 엔터티 없이 자식 엔터티 생성 가능</li>
<li>자식 엔터티 존재하는 상태에서 부모 엔터티 삭제 가능</li>
</ul>
</li>
</ul>
<p></br></br></br></br></p>
<hr>
<p></br></br></br></p>
<h2 id="출처">출처</h2>
<ul>
<li>유선배 SQLD 과외노트</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] 네이버 쇼핑 API 필터링]]></title>
            <link>https://velog.io/@0-x-14/Spring-%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%87%BC%ED%95%91-API-%ED%95%84%ED%84%B0%EB%A7%81</link>
            <guid>https://velog.io/@0-x-14/Spring-%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%87%BC%ED%95%91-API-%ED%95%84%ED%84%B0%EB%A7%81</guid>
            <pubDate>Tue, 19 Nov 2024 06:31:55 GMT</pubDate>
            <description><![CDATA[<p></br></br>
오늘은 ‘따끈’ 프로젝트를 진행하면서 겪었던 문제와 해결한 방법에 대해 정리해보려 한다.
</br></br></p>
<p>‘<strong>따끈</strong>’은 사용자가 카테고리별로 반려동물의 건강 상태에 대한 일지를 작성하고, 이를 기반으로 AI를 통해 건강 상태를 진단할 수 있는 <strong>반려동물 스킨케어 서비스</strong>이다.</br>
일지를 작성할 때는 귀/눈/털/발톱/이빨 5개 카테고리 중에서 선택할 수 있고, 이외에도 카테고리별 제품 랭킹, 커뮤니티, 투두 등의 기능이 있다.
</br></br></br></br></br></br>
이중 내가 개발한 AI진단 방법은 다음과 같다.</p>
<ol>
<li>반려동물의 정보와 선택한 일지 기록을 토대로 ChatGPT에게 반려동물의 건강 상태 진단과 관련 제품을 추천해달라고 한다. (GPT의 모델은 chat gpt 4o를 사용하였다.)</li>
<li>챗GPT가 추천한 제품을 네이버 쇼핑 API를 통해 네이버 쇼핑에 등록된 제품 정보를 불러와서 사용자에게 보여준다.
</br></br></br></br></li>
</ol>
<p>이 과정에서, <strong>챗GPT가 해외 제품을 추천해주는 문제가 발생</strong>하였다.
</br>
질문 내용을 구성할 때 <em>5개 제품을 추천</em> 해달라고 했는데, 5개를 모두 한국 제품으로 추천해줄 때도 있지만 해외 제품을 추천할 때도 있었다.</p>
<p>추천해준 제품이 해외에서만 판매될 경우, 네이버 쇼핑 API에서 검색되지 않아 2번의 과정에서 _개 사료 등 &#39;반려동물 스킨케어 서비스&#39;라는 <strong>따끈 앱의 취지와는 맞지 않는 제품</strong>들_이 불러와지는 일이 발생하였다.
</br></br></br></br>
이를 해결하기 위해 먼저 단순히 챗GPT에게 보내는 질문의 내용에 “한국의 제품을 추천해달라”고 추가해보았으나, 여전히 해외 제품만을 추천하는 문제가 발생하였다.
별도의 질문을 통해 확인해보니 챗GPT는 제품이 어느 나라에서 판매되는지는 알 수 없다고 하였다.
</br></br></br>
고민 끝에 네이버 쇼핑 API로 제품 정보를 업데이트할 때 <strong>필터링</strong>을 하는 방법으로 이를 해결하였다.
</br></br></br>
네이버 쇼핑 API의 제품 정보에는 다양한 정보들이 저장되어 있는데, 다음과 같이 <strong>카테고리</strong>도 저장되어 있다.</p>
<p><img src="https://velog.velcdn.com/images/0-x-14/post/16babb02-ca6a-4894-9be0-f2d13038766f/image.png" alt=""></p>
<p></br></br>관련도 순으로 검색 결과를 받아온 후 제품을 선택할 때, 다음과 같은 필터링을 추가하여 _지정한 카테고리 내의 제품_을 선택하도록 하였다.
</br></p>
<pre><code>public JSONObject getFilteredItems(JSONArray items) {

        for (int i = 0; i &lt; items.length(); i++) {

            // 반복문을 돌면서 조건을 충족하는 값이 나오면 바로 종료, 해당 item으로 저장함
            JSONObject itemJson = items.getJSONObject(i);

            String category3 = itemJson.getString(&quot;category3&quot;);
            String category4 = itemJson.getString(&quot;category4&quot;);

            List&lt;String&gt; validCategory3 = Arrays.asList(
                    &quot;미용/목욕&quot;, &quot;강아지 건강/관리용품&quot;, &quot;고양이 건강/관리용품&quot;
            );
            List&lt;String&gt; validCategory4 = Arrays.asList(
                    &quot;브러시/빗&quot;, &quot;에센스/향수/밤&quot;, &quot;샴푸/린스/비누&quot;, &quot;이발기&quot;, &quot;발톱/발 관리&quot;,
                    &quot;드라이기/드라이룸&quot;, &quot;미용가위&quot;, &quot;타월/가운&quot;, &quot;물티슈/크리너&quot;,
                    &quot;눈/귀 관리용품&quot;, &quot;구강청결제&quot;, &quot;칫솔&quot;, &quot;치약&quot;, &quot;구강티슈&quot;, &quot;구강관리용품&quot;
            );

            if (validCategory3.contains(category3) &amp;&amp; validCategory4.contains(category4)) {
                // 해당하는 카테고리값에 해당하는 제품이 나오면 즉시 반복문을 종료하고 해당 itemJson값을 리턴함
                return itemJson;
            }
        }

        return null;
    }</code></pre><p></br></br></br></br>
물론 챗GPT가 추천해준 제품과는 다른 제품을 가져올 수도 있다.
</br></p>
<p>네이버 쇼핑 API에서 <strong>관련도</strong> 순으로 제품을 검색하므로 완벽하게 일치하지 않더라도 <strong>관련 키워드들이 들어간 제품</strong>을 선택한다. 
PM과 논의한 결과, <em>키워드를 통해 진단 내용에 맞는 제품들을 선택하므로</em> 앱의 취지와도 적합하다는 판단이 들어 필터링으로 문제를 해결하였다.
</br></br>
관련 전체 코드는 아래 링크에서 확인할 수 있다.
<a href="https://github.com/ttakkeun/Backend_Spring/pull/120">https://github.com/ttakkeun/Backend_Spring/pull/120</a>
</br></br></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[OSI 7계층 모델, TCP/IP 4계층 모델]]></title>
            <link>https://velog.io/@0-x-14/OSI-7%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B8-TCPIP-4%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@0-x-14/OSI-7%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B8-TCPIP-4%EA%B3%84%EC%B8%B5-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Tue, 05 Nov 2024 09:26:34 GMT</pubDate>
            <description><![CDATA[<p></br></br>네트워크에 대해 공부해본 적이 있는 사람이라면 OSI 7계층과 TCP/IP 4계층에 대해 한번쯤 들어봤을 것이다. 이번 글에서는 해당 주제를 다뤄보려 한다.</br></br></br></br></br></br></p>
<h2 id="네트워크-통신-절차-예시">네트워크 통신 절차 (예시)</h2>
<p></br>먼저, 다음과 같은 예시를 통해 이해해보자.</br></br></p>
<p><strong>&lt;PC 크롬 브라우저에서 네이버 웹사이트를 접속하는 경우&gt;</strong></p>
<ol>
<li>크롬 브라우저에서 <a href="http://www.naver.com%EC%9D%84">www.naver.com을</a> 입력한다.</li>
<li>크롬 브라우저는 이를 네트워크에서 통신 가능한 &#39;패킷(packet)&#39; 형태로 만든다.</li>
<li>해당 패킷을 네트워크에 흘려 보낸다.</li>
<li>네트워크 중간에 있는 기기 &#39;라우터(router)&#39;들이 이 패킷을 읽고 네이버 서버로 전달한다.</li>
<li>네이버 서버는 이 패킷을 다시 풀어 웹서버가 읽을 수 있는 형태로 만든 뒤, 웹서버에 전달한다.</br></br></br></br></br></br></br><h2 id="프로토콜">프로토콜</h2>
</br><strong>프로토콜</strong>이란 쉽게 말해 <strong>통신을 위한 규약</strong>이다.
</br>예를 들어, A라는 사람이 생년월일 정보를 B에게 전달한다고 가정해보자.
A가 &quot;제 생년월일은 010203이에요&quot;라고 한다면,
B는 별다른 설명 없이도 앞의 01이 생년, 중간 두 자리 02이 월, 마지막 두 자리 03이 일이라는 것을 알고 있을 것이다.</li>
</ol>
<p></br>이처럼 문법, 해당 문법의 의미, 이것으로 어떤 액션을 취할 것인지 등의 <em>규칙</em> 을 통틀어 &#39;프로토콜&#39;이라고 한다.
</br>네트워크에서 계층이 존재할 경우, 하위 계층은 상위 계층에게 서비스를 제공하고 상위 계층은 이를 이용하게 된다.
그러기 위해서는 <em>계층 간의 인터페이스_가 잘 정의되어 있어야 하며, 이 _정의된 인터페이스를 프로토콜이라고 한다.</em></br></br></br></br></br></br></br></p>
<h2 id="osi-7계층-모델과-예시"><strong>OSI 7계층 모델과 예시</strong></h2>
<p></br>OSI 7계층이란 <strong>네트워크에서 통신이 일어나는 과정</strong>을 7단계로 나눈 것이다.
앞선 예시를 사진을 참고하며 다시 한 번 이해해보자.</p>
<p><img src="https://velog.velcdn.com/images/0-x-14/post/ef2bdcd1-0095-4677-88e0-2eefa5d01132/image.png" alt=""></p>
</br>

<ul>
<li><p>앞선 예시에서 _송신 호스트_는 _사용자_이며, _수신 호스트_는 _네이버 서버_가 된다.</p>
</li>
<li><p>송신 호스트가 응용 계층(브라우저)에서 데이터(URL)을 입력한다.</p>
</li>
<li><p>해당 데이터는 전송이 되기 전 표현 계층, 세션 계층, 전송 계층, 네트워크 계층, 데이터 링크 계층을 <em>차례대로 지나며</em>, 필요한 데이터를 기존 데이터에 추가한다.</p>
<ul>
<li>각 계층에는 순서가 존재하며, 데이터는 해당 순서에 따라 전달된다.</li>
<li>각 계층은 이전 계층으로부터 데이터를 전달받은 뒤, 자신의 계층에서 기존 데이터에 필요한 데이터들을 추가로 붙인다.</li>
</ul>
</li>
<li><p>물리 계층에서는 _완성된 데이터를 네트워크에 전송_한다.</p>
</li>
<li><p>서버는 전달받은 데이터를 물리 계층 -&gt; 응용 계층 순으로 전달한다.</p>
<ul>
<li>각 계층은 이전 계층으로부터 전달받은 데이터 중 <strong>필요한 데이터만 분리하여 해석</strong>한다.</li>
</ul>
</li>
<li><p>응용 계층에서 전달 받은 요청을 수신 호스트에 전달한 뒤, 수신 호스트가 해당 요청의 응답을 다시 데이터로 만든다.</br></br></br></br></br></br></br></p>
<h2 id="osi-7계층">OSI 7계층</h2>
</li>
</ul>
<p></br>이번에는 각 계층에 대해 조금 더 자세히 알아보자.</br>
</br></p>
<ul>
<li><p>7계층 - <strong>응용 계층</strong> (Application Layer)</p>
<ul>
<li><p>사용자와 <strong>상호작용</strong>을 하는 계층</p>
</li>
<li><p>응용 프로그램 및 사용자 인터페이스 제공</p>
</li>
<li><p>웹 서비스의 UI부분, 사용자의 입출력(I/O) 담당</p>
</li>
</ul>
</li>
<li><p>6계층 - <strong>표현 계층</strong> (Presentation Layer)</p>
<ul>
<li>응용-네트워크 계층 간 <strong>데이터를 적절히 표현</strong>하는 부분 담당</li>
<li>데이터 형식 변환, 데이터 압축 및 암호화, 문자 코드 변환 등</li>
</ul>
</li>
<li><p>5계층 - <strong>세션 계층</strong> (Session Layer)</p>
<ul>
<li><strong>통신 세션</strong>을 구성하는 계층</li>
<li>통신하는 사용자들을 동기화하고 오류복구 명령들을 일괄적으로 다</li>
<li>통신을 하기 위한 세션을 확립/유지/중</li>
<li>TCP/IP 세션을 만들고 없애는 책임을 짐</li>
</ul>
</li>
<li><p>4계층 - <strong>전송 계층</strong> (Transport Layer)</p>
<ul>
<li>IP에 의해 전달되는 패킷의 오류를 검사하고 재전송 요구 등의 제어를 담당하는 계층</li>
<li>즉 <strong>오류 검출 및 복구, 흐름 제어, 중복 검사 등을 담당</strong>하는 계층</li>
<li>종단간(end-to-end) 신뢰성 있는 통신 제공 -&gt; 상위 계층이 유효성, 효율성을 고려하지 않아도 되게 해줌</li>
</ul>
</li>
<li><p>3계층 - <strong>네트워크 계층</strong> (Network Layer)</p>
<ul>
<li><strong>경로를 선택</strong>하고 <strong>경로에 따라 패킷을 전달</strong>하는 역할</li>
<li>흔히 말하는 &#39;<strong>라우터</strong>&#39;가 해당 역할을 함</li>
<li>데이터를 목적지로 안전하고 빠르게 전달(라우팅)함</li>
<li>IP 주소를 사용하여 목적지 식별</li>
</ul>
</li>
<li><p>2계층 - <strong>데이터 링크 계층</strong> (Data Link Layer)</p>
<ul>
<li>데이터의 <strong>오류와 흐름을 관리</strong> -&gt; <strong>안전한 정보의 전달</strong>을 수행할 수 있도록 함</li>
<li><strong>MAC 주소</strong>를 가지고 통신 (MAC 주소는 변동 X)</li>
</ul>
</li>
<li><p>1계층 - <strong>물리 계층</strong> (Physical Layer)</p>
<ul>
<li><strong>물리적인 연결과 전송 매체</strong>를 다룸</li>
<li>데이터 비트를 신호로 변환하고 전송</li>
<li>해당 계층은 <strong>전달만 함</strong>. 데이터가 무엇인지, 에러가 있는지는 신경 X</br></br></br></br></br></br></br><h2 id="tcpip-4계층-모델">TCP/IP 4계층 모델</h2>
</br>_OSI 7계층 모델_은 네트워크 통신 표준화를 위한 _개념 모델_이다.</li>
</ul>
</li>
</ul>
<p></br>즉, 실제로 인터넷 통신이 OSI 7계층 모델처럼 동작하지는 않는다.
실제 인터넷 통신의 대부분은 <strong>TCP/IP 통신을 사용</strong>한다.</p>
<p></br>TCP/IP 통신은 TCP와 IP에 기반한 통신 방법으로, 이 통신에 특화된 모델을 TCP/IP 4계층 모델이라 한다.</p>
<p>아래 사진에서 알 수 있듯, <strong>TCP/IP 4계층</strong>은 OSI 7계층과 유사하지만 <strong>4계층으로 간소화</strong> 되어 있다.</br></br>
<img src="https://velog.velcdn.com/images/0-x-14/post/e67f1779-4f79-41f1-8b78-06f8db5c0ade/image.png" alt=""></p>
</br>

<ul>
<li><p>4계층 - <strong>응용 계층</strong> (Application Layer)</p>
<ul>
<li>OSI 7계층 모델의 <em>응용, 표현, 세션 계층</em> 기능을 담당</li>
</ul>
</li>
<li><p>3계층 - <strong>전송 계층</strong> (Transport Layer)</p>
<ul>
<li>OSI 7계층 모델의 _전송 계층_과 같음</li>
<li>프로세스 간의 신뢰성 있는 데이터 전송 담당</li>
</ul>
</li>
<li><p>2계층 - <strong>인터넷 계층</strong> (Internet Layer)</p>
<ul>
<li>OSI 7계층 모델의 _네트워크 계층_과 같음</li>
<li>컴퓨터 간 라우팅 담당</li>
</ul>
</li>
<li><p>1계층 - <strong>네트워크 인터페이스 계층</strong> (Network Interface Layer)</p>
<ul>
<li>OSI 7계층 모델의 <em>데이터 링크, 물리 계층</em> 기능 담당</li>
<li>네트워크 통신의 물리적인 부분</li>
</ul>
</li>
</ul>
<p></br></br></br></br></br></br></br>참고 링크</p>
<ul>
<li><a href="https://ariz1623.tistory.com/327">https://ariz1623.tistory.com/327</a></li>
<li><a href="https://shlee0882.tistory.com/110">https://shlee0882.tistory.com/110</a></li>
<li><a href="https://blog.naver.com/ssudol2/223212288601">https://blog.naver.com/ssudol2/223212288601</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Network] HTTP 상태코드 400 Bad Request vs 404 Not Found]]></title>
            <link>https://velog.io/@0-x-14/HTTP-%EC%97%90%EB%9F%AC%EC%BD%94%EB%93%9C-400-Bad-Request-vs-404-Not-Found</link>
            <guid>https://velog.io/@0-x-14/HTTP-%EC%97%90%EB%9F%AC%EC%BD%94%EB%93%9C-400-Bad-Request-vs-404-Not-Found</guid>
            <pubDate>Mon, 16 Sep 2024 13:52:07 GMT</pubDate>
            <description><![CDATA[<p>최근 진행하였던 프로젝트에서 팀원의 코드를 리뷰하던 중, “유효하지 않는 사용자”를 나는 404 에러로 처리한 것과 달리 팀원은 400 에러로 처리한 것을 발견하였다.
나는 존재하지 않는 사용자이므로 404 Not Found로 처리하였는데, 팀원의 코드를 보고 400 Bad Request에 대해 찾아보니 400도 적절한 것 같아서 혼란스러워졌다 🫨
이 글에서는 400 Bad Request와 404 Not Found에 대해 정리하고, 둘의 차이에 대해 다뤄보려 한다. </br> </br> </br> </br> </br></p>
<h2 id="400-bad-request">400 Bad Request</h2>
<ul>
<li><strong>클라이언트가 잘못된 요청을 해서</strong> 서버가 요청을 처리할 수 없음</li>
<li>요청 구문, 메세지 등의 오류</li>
<li>*<em>클라이언트는 요청 내용을 다시 검토하고, 보내야 함❗️ *</em></li>
<li>ex) 요청 파라미터가 잘못되거나, API 스펙이 맞지 않을 때</br> </br> </br> </br></li>
</ul>
<h2 id="404-not-found">404 Not Found</h2>
<ul>
<li><strong>요청 리소스를 찾을 수 없음</strong>. 즉, 요청한 리소스가 서버에 없음</li>
<li>또는 클라이언트가 권한이 부족한 리소스에 접근할 때 해당 리소스를 숨기고 싶을 때 </br> </br> </br> </br> </br></li>
</ul>
<h2 id="무슨-차이일까">무슨 차이일까?</h2>
<p>나는 “요청 리소스를 찾을 수 없다”는 말을 DB에서 자원을 찾을 수 없다는 말로 해석해서 404도 적절하다고 생각했는데, 조금 더 찾아보니 404 Not Found는 <strong>API URI를 조회하지 못했다</strong>는 의미가 더 강하다고 한다.</p>
<p>404 Not Found 코드는 해당 리소스가 존재하지 않음을 나타낸다. 이와 달리 400 Bad Request의 경우에는 리소스가 아니라 설정 값을 나타내는 것이다.</p>
<p>만약 게시글에 대한 400 Bad Request가 발생할 경우 클라이언트에게 “게시글을 먼저 작성해주세요”라는 메시지를 반환하여 오류가 발생한 원인을 더욱 명확하게 알릴 수 있다. </br> </br> </br> </br> </br></p>
<h2 id="정리">정리</h2>
<ul>
<li>400 Bad Request<ul>
<li>클라이언트가 전송한 데이터가 부적절하거나, 요청 자체가 유효하지 않은 경우</li>
<li><strong>클라이언트가 요청을 수정하고 다시 시도하도록 유도하면 됨</strong></li>
</ul>
</li>
<li>404 Not Found<ul>
<li>클라이언트가 잘못된 URI를 시도했거나, 해당 리소스가 삭제되었을 가능성이 있음</li>
<li><strong>오류가 발생한 원인을 확인해야 함</strong> </br> </br> </br> </br> </br></li>
</ul>
</li>
</ul>
<h2 id="결론">결론!</h2>
<blockquote>
<ul>
<li>즉, <strong>클라이언트의 행동을 유도해야 하는지</strong>에서 핵심적인 차이가 있다❗️❗️<ul>
<li>만약 <em>클라이언트의 행동을 교정</em>해서 올바른 요청으로 만들 수 있다면 400이 좋은 선택이다.</li>
</ul>
</li>
</ul>
</blockquote>
<p> </br> </br> </br> </br> </br> </br> </br> </br></p>
<p>참고 링크</p>
<ul>
<li><a href="https://hyeon9mak.github.io/400-bad-request-vs-404-not-found/">https://hyeon9mak.github.io/400-bad-request-vs-404-not-found/</a></li>
<li><a href="https://velog.io/@carrykim/HTTP-Stauts-4xx-%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B0%9C%EC%9D%B8%EC%A0%81%EC%9D%B8-%EC%9D%B4%ED%95%B4">https://velog.io/@carrykim/HTTP-Stauts-4xx-%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B0%9C%EC%9D%B8%EC%A0%81%EC%9D%B8-%EC%9D%B4%ED%95%B4</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 원시타입과 참조타입이란?]]></title>
            <link>https://velog.io/@0-x-14/Java-%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85%EA%B3%BC-%EC%B0%B8%EC%A1%B0%ED%83%80%EC%9E%85%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@0-x-14/Java-%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85%EA%B3%BC-%EC%B0%B8%EC%A1%B0%ED%83%80%EC%9E%85%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Sat, 14 Sep 2024 09:40:33 GMT</pubDate>
            <description><![CDATA[<p>자바를 공부하던 중, boolean은 null값을 허용하지 않는 것과 달리 Boolean은 null값이 허용된다는 사실을 알게 되었다.
boolean과 Boolean의 차이에 대해 찾아보니, boolean은 원시타입인 것과 달리 Boolean은 참조타입이라고 한다...! </br>
이 글에서는 원시타입과 참조타입에 대해 조금 더 정리해보려고 한다.</br></br></br></p>
<h2 id="원시타입과-참조타입이란br">원시타입과 참조타입이란?</br></h2>
<ul>
<li><strong>원시타입</strong> : 논리형, 문자형, 정수형, 실수형 등의 <strong>실제 데이터(값)을 저장</strong>하는 타입<ul>
<li>int, float, double, boolean 등</li>
</ul>
</li>
<li><strong>참조타입</strong> : 객체가 생성된 <strong>메모리의 주소를 저장</strong>하는 타입<ul>
<li>Integer, Float, Double, Boolean 등</br></br></br></li>
</ul>
</li>
</ul>
<h2 id="실제로-값이-저장되는-영역은br">실제로 값이 저장되는 영역은?</br></h2>
<ul>
<li><strong>원시타입</strong>의 객체는 JVM 메모리 구조 중 <strong>Stack 메모리 영역</strong>에 저장된다.</li>
<li><strong>참조타입</strong>의 실제 객체는 <strong>Heap 영역</strong>에 저장되며, 참조타입의 변수는 Stack 영역에 실제 객체 주소의 값을 저장하게 된다.</br></li>
<li>아래와 같은 두 변수가 있다고 가정해보자.</br><pre><code>int age = 25;
String name = &quot;커피&quot;;</code></pre></br>이 경우, age와 name의 값은 각각 다음과 같은 영역에 저장된다.</li>
</ul>
<table>
<thead>
<tr>
<th>스택 영역</th>
<th>힙 영역</th>
</tr>
</thead>
<tbody><tr>
<td>int age = 25</td>
<td></td>
</tr>
<tr>
<td>String name = 1111번지</td>
<td>1111번지 : &quot;커피&quot;</td>
</tr>
</tbody></table>
<p></br></br></br></p>
<h2 id="boxing과-unboxing이란br">Boxing과 Unboxing이란?</br></h2>
<ul>
<li>참조타입은 원시타입을 _박스화한 자료형(boxed primitive type)_이라고 할 수 있다.</li>
<li>그래서 _원시타입을 참조타입으로 변환하는 과정_을 <strong>박싱(Boxing)</strong>이라고 한다.</li>
<li>_참조타입을 원시타입으로 변환하는 과정_은 <strong>언박싱(Unboxing)</strong>이라고 한다.</li>
</ul>
</br>
자바 1.5부터 추가된 Auto Boxing / Unboxing 기능으로 인해 명시적으로 형변환을 하지 않아도 자바가 자동으로 박싱과 언박싱을 해준다.</br>
예를 들어 다음 코드처럼 사용 가능하다.</br>

<pre><code>public class BoxingUnBoxing {

    public static void main(String[] args) {

        // Boxing
        int i = 10;
        Integer integer = i; // 원시타입 i를 참조타입 integer에 대입

        System.out.println(i);
        System.out.println(integer);

        // UnBoxing
        Integer integer2 = 20;
        int i2 = integer2; // 참조타입 integer2를 원시타입 i2에 대입

        System.out.println(i2);
        System.out.println(integer2);
    }

}

결과
10
10
20
20</code></pre><p></br></br></br></p>
<h2 id="원시타입-vs-참조타입-비교brbr">원시타입 vs 참조타입 비교</br></br></h2>
<h3 id="null을-담을-수-있는가br">null을 담을 수 있는가?</br></h3>
<ul>
<li>원시타입 - <strong>불가능</strong></li>
<li>참조타입 - 가능</br><pre><code>int i = null; // 원시타입이므로 불가능
Integer integer = null; // 가능</code></pre></br></br></br></li>
</ul>
<h3 id="제네릭-타입에서-사용할-수-있는가br">제네릭 타입에서 사용할 수 있는가?</br></h3>
<ul>
<li>원시타입 - <strong>불가능</strong></li>
<li>참조타입 - 가능</br><pre><code>List&lt;int&gt; i2; // 원시타입이므로 불가능
List&lt;Integer&gt; integer2; // 가능</code></pre></br></br></br><h3 id="접근속도br">접근속도?</br></h3>
</li>
<li><strong>원시타입(빠름) &lt; 참조타입(느림)</strong></li>
<li>참조타입은 값에 접근할 때 Unboxing 과정을 거쳐야 하므로 원시타입에 비해 접근 속도가 느리다.</li>
<li>단, 예외적으로 엄청 큰 숫자를 복사해야 한다면 참조 타입이 좋을 수도 있다.
  아래 그래프에서 확인할 수 있듯, 원시타입인 long과 참조타입인 Long의 평균 실행시간은 다른 타입들에 비해 차이가 적다.
  <img src="https://velog.velcdn.com/images/0-x-14/post/f313bd82-23aa-4c6d-8920-8eed2d63ab20/image.png" alt="">
</br></br></br></li>
</ul>
<h3 id="차지하는-메모리의-양br">차지하는 메모리의 양?</br></h3>
<ul>
<li><strong>참조타입(큼) &gt; 원시타입(작음)</strong></li>
<li>참조타입은 객체로 감싸져있기 때문에 차지하는 메모리의 양이 원시타입에 비해 크다.</li>
</ul>
<table>
<thead>
<tr>
<th>원시타입이 사용하는 메모리</th>
<th align="right">참조타입이 사용하는 메모리</th>
</tr>
</thead>
<tbody><tr>
<td>boolean - 1bit</td>
<td align="right">Boolean - 128bits</td>
</tr>
<tr>
<td>byte - 8bits</td>
<td align="right">Byte - 128bits</td>
</tr>
<tr>
<td>short, char - 16bits</td>
<td align="right">Short, Character - 128bits</td>
</tr>
<tr>
<td>int, float - 32bits</td>
<td align="right">Integer, Float - 128bits</td>
</tr>
<tr>
<td>long, double - 64bits</td>
<td align="right">Long, Double - 196bits</td>
</tr>
</tbody></table>
<p></br></br></br></p>
<h2 id="결론br">결론!</br></h2>
<blockquote>
<p>원시타입은 성능과 메모리 면에서 장점이 있으므로 <strong>원시타입 사용을 먼저 고려해보면 좋을 것 같다.</strong>
다만 _null이나 제네릭 타입을 사용할 경우_에는 <strong>참조타입</strong>을 사용해야 한다.</p>
</blockquote>
<p></br></br></br></br></br></p>
<p>참고 링크</br></p>
<ul>
<li><a href="https://velog.io/@ljs0429777/%EC%9E%90%EB%B0%94%EC%9D%98-%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90">https://velog.io/@ljs0429777/자바의-원시타입을-이해해보자</a></li>
<li><a href="https://siyoon210.tistory.com/139">https://siyoon210.tistory.com/139</a></li>
</ul>
<p></br></br></br></p>
]]></description>
        </item>
    </channel>
</rss>