<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>밤낮개발</title>
    <link>https://itistori.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 27 May 2026 16:21:52 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Nickolodeon</managingEditor>
    <image>
      <title>밤낮개발</title>
      <url>https://tistory1.daumcdn.net/tistory/5463321/attach/7cc181fc293045b3ba4d7c336fcd231c</url>
      <link>https://itistori.tistory.com</link>
    </image>
    <item>
      <title>Kafka 메시지 브로커 활용기 - 1</title>
      <link>https://itistori.tistory.com/87</link>
      <description>&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;개요  &lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;A 사에서 진행하는 x 프로젝트에서는 기존에 A 사에 구축된 시스템과 연동을 통해 데이터를 받아오는 로직이 존재했었다.&amp;nbsp;새롭게 A 사에서 수주한 y 프로젝트는 x 프로젝트가 받아오는 데이터가 필요했으나, x 프로젝트와 같이 A 사 시스템과의 연동은 보안 문제로 허용되지 않는 상황이었다. 결국 x 프로젝트에서 A 사와 연동해 가져온 데이터를 y 프로젝트 쪽 DB로 넘겨주는 방안이 필요했고, 이를 위해 메시지 브로커인 Kafka를 사용하게 되었다.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;내용  &lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;앞에서 설명했듯이, 나는 데이터를 주고받는 중계자의 역할로서 Kafka를 활용하게 되었다. API 가 아닌 외부 브로커인 Kafka를 사용했던 이유는 데이터 송수신 간 아래와 같은 장점이 있기 때문이었다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka를 사용하는 애플리케이션 인스턴스가 오류로 인해 중지되어도, 인덱스를 이용해 마지막까지 읽어 들인 데이터부터 다시 수신할 수 있다.&lt;/li&gt;
&lt;li&gt;Polling 방식으로 데이터를 읽어 들이므로, 실시간으로 데이터의 변동 사항을 반영할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같은 이유로, DB 쿼리에의 의존성 및 서버에의 부하를 감소시키는 방향으로 진행이 가능했다.&lt;br /&gt;&lt;br /&gt;구축 방식을 살펴보기 이전에, 먼저 Kafka의 기본 개념들을 살펴보도록 하자.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비동기 메시지 처리&lt;/li&gt;
&lt;li&gt;Topic에 대해 Sub / Pub으로 메시지 주고받음&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;구성&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Zookeeper
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka 내의 메시지 큐 메타데이터 관리용&lt;/li&gt;
&lt;li&gt;KRaft를 지원하는 버전 (KIP-500, 2019.08.01부터) 은 자체 클러스터 내에서 메타데이터를 관리하므로 Zookeeper 를 사용하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Kafka Cluster&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka Server (Broker)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 개 구동 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Partition
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Log (각 Partition 도 여러 Log로 나누어진다)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Key&lt;/li&gt;
&lt;li&gt;Value&lt;/li&gt;
&lt;li&gt;Timestamp&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;Facts&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Producer는 순차적으로 Topic에 맞게 (Queue) 메시지를 Partition에 쌓는다. 이때, Partition 이 여러 개인 경우 Round-Robin 방식으로 쌓는다.&lt;/li&gt;
&lt;li&gt;Comsumer Group과 Topic 은 1 : 1 관계이다. (단방향)&lt;/li&gt;
&lt;li&gt;Topic과 Consumer Group 은 1 : N 관계이다. (단방향)&lt;/li&gt;
&lt;li&gt;Consumer 인스턴스 : offset을 Partition과 공유한다.&lt;/li&gt;
&lt;li&gt;즉 Consumer Group 내에서 Consumer 인스턴스는 한 번에 하나의 Partition 만 접근할 수 있다. (Partition : Consumer Instance = 1 : 1 (단방향))&lt;/li&gt;
&lt;li&gt;Partition의 개수가 많을수록 병렬 처리의 이점이 있으나 단점도 존재한다. 바로 Partition 은 한 번 개수를 늘리면 줄이는 것이 불가능하다는 점이다.&lt;/li&gt;
&lt;li&gt;Partition 중 Leader와 Follower를 설정할 수 있으며, Follower는 Leader를 복제하며 Leader와 다른 브로커에 존재한다. Leader 가 다운되었을 때를 대비해서 In-Sync Replica를 만드는 것이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;정해야 할 것들&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Topic&lt;/li&gt;
&lt;li&gt;Partition 개수&lt;/li&gt;
&lt;li&gt;Consumer 개수&lt;/li&gt;
&lt;li&gt;보존 기간&lt;/li&gt;
&lt;li&gt;메시지 보존 기간은 log.retention.hours의 값에 따라 정할 수 있다. (기본값 = 7일)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 개의 Consumer를 고려한 설계 덕분에 소비가 효율적으로 이뤄진다.&lt;/li&gt;
&lt;li&gt;매번 찾아서 메시지 pull 하는 게 아니라 offset을 활용, 이전에 consumer 가 공유해 준 정보를 통해 움직인다. (캐싱 같이?)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 유무에 관련 없이 정기적으로 polling 한다.&lt;/li&gt;
&lt;li&gt;그러나 데이터가 도착할 때까지 대기하는 long poll 방식을 쓸 수 있게 하는 parameter 가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지 카프카에 대해서 간략하게 알아보았다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이제 2부에서는 카프카를 실제로 어떻게 스프링에서 적용할 수 있는지 알아보겠다.&lt;/p&gt;</description>
      <category>Project</category>
      <category>DBA</category>
      <category>kafka</category>
      <category>MQ</category>
      <author>Nickolodeon</author>
      <guid isPermaLink="true">https://itistori.tistory.com/87</guid>
      <comments>https://itistori.tistory.com/87#entry87comment</comments>
      <pubDate>Tue, 3 Dec 2024 09:04:39 +0900</pubDate>
    </item>
    <item>
      <title>FCM (Firebase Cloud Messaging) 사용기 - 1</title>
      <link>https://itistori.tistory.com/86</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;FCM 이란?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Firebase Cloud Messaging 의 줄임말로, 메시징 기능을 클라우드 서버에서 제공해주는 구글의 서비스이다.&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;개요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;프로젝트를 하는 도중에 FCM 을 사용해 볼 수 있는 기회가 생겼다. 용도는 특정 알림 기능을 모바일 단말에 푸시로 전달하는 것이었다. 기존에 구현되어 있는 기능이었으나, 최근에 FCM Legacy API 가 만료됨에 따라 V1 으로 마이그레이션하는 작업을 하게 되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;FCM 동작 플로우&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로젝트 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;먼저 FCM 은 위에서 언급한대로 클라우드 서버에서 기능을 제공하기 때문에, 어떤 프로젝트 하위에서 푸시를 주고받을 건지 정해야 한다. 그래서 프로젝트를 생성하면 아래와 같은 페이지로 관리가 가능하다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;fcm.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;953&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWEqsu/btsJg6iNWm2/6Ff5wxsGVtCFIRE02hU7Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWEqsu/btsJg6iNWm2/6Ff5wxsGVtCFIRE02hU7Q0/img.png&quot; data-alt=&quot;이름들은 가렸지만, 클라우드 메시징을 관리하는 페이지임을 확인할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWEqsu/btsJg6iNWm2/6Ff5wxsGVtCFIRE02hU7Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWEqsu%2FbtsJg6iNWm2%2F6Ff5wxsGVtCFIRE02hU7Q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;953&quot; data-filename=&quot;fcm.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;953&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이름들은 가렸지만, 클라우드 메시징을 관리하는 페이지임을 확인할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;관리자 페이지는 프로젝트별로 생성이 되고, 프로젝트 정보는 아래 항목들을 포함한다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 이름&lt;/li&gt;
&lt;li&gt;프로젝트 ID&lt;/li&gt;
&lt;li&gt;프로젝트 번호&lt;/li&gt;
&lt;li&gt;Web API 키&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;private key 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 좌측 네비게이션 바에 있는 &lt;span style=&quot;color: #0593d3;&quot;&gt;Project Overview&lt;/span&gt; 항목 오른쪽의 톱니바퀴를 누르면, &lt;span style=&quot;color: #0593d3;&quot;&gt;Project settings&lt;/span&gt; 메뉴를 확인할 수 있다. &lt;span style=&quot;color: #0593d3;&quot;&gt;Service accounts&lt;/span&gt; 탭에 &lt;span style=&quot;color: #1a5490;&quot;&gt;Generate new private key&lt;/span&gt; 버튼을 누르면 FCM 을 사용하기 위한 인증에 필요한 key 를 생성할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단말 토큰 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;private key 가 푸시 자체를 보내기 위한 인증에 필요하다면, 각 단말이 FCM 을 사용하는지 여부는 단말 토큰을 통해 인증하게 된다. 이 토큰은 각 단말을 분별하는 데에 사용된다. 최초에 앱을 설치할 때 생성되며, 나의 경우 RDB 에 저장해두고 관리하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring 에서 FCM 활용하기&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;의존성 라이브러리 추가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 모든 기능과 마찬가지로, 빌드 시에 관련 라이브러리에 대한 의존성을 갖고 있어야 Spring 어플리케이션 내에서 FCM 을 사용할 수 있다. 아래는 Maven 으로 빌드된 프로젝트에서 의존성을 추가하는 방법이다:&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.google.firebase&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;firebase-admin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;9.3.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;firebase-admin 은 SDK 형태로 만들어져서 코드로 구현 시 FCM 의 기능들을 편리하게 사용할 수 있게 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 firebase-admin 라이브러리를 활용한 코드의 예시이다:&lt;/p&gt;
&lt;pre class=&quot;crystal&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;package framework.config;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import javax.annotation.PostConstruct;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @deprecated Firebase Admin SDK 방식은 아직 적용되지 않았으므로
 * 우선은 {@link api.presence.bo.service.PushNotificationServiceImpl} 활욯.
 */
@Deprecated
@Configuration
public class FirebaseConfig {

    @Value(&quot;#{config['firebase.admin.sdk.key.name']}&quot;)
    private String FIREBASE_KEY_NAME;

    @PostConstruct
    public void initFirebase() throws IOException {
        Resource resource = new ClassPathResource(&quot;/&quot; + FIREBASE_KEY_NAME);
        FileInputStream stream = new FileInputStream(resource.getFile());;
        try {
            GoogleCredentials googleCredentials = GoogleCredentials.fromStream(stream);
            FirebaseOptions options = new FirebaseOptions.Builder()
                    .setCredentials(googleCredentials)
                    .build();
            FirebaseApp.initializeApp(options);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            stream.close();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이, 단순히 클래스들을 import 하여 연관된 메서드들을 호출하는 것만으로도 FCM 사용이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 이렇게 세팅된 FCM 을 통해 구체적으로 어떻게 푸시 메시지를 주고 받을 수 있는지 알아보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <category>cloud</category>
      <category>FCM</category>
      <category>Firebase</category>
      <category>Google</category>
      <category>Mobile</category>
      <author>Nickolodeon</author>
      <guid isPermaLink="true">https://itistori.tistory.com/86</guid>
      <comments>https://itistori.tistory.com/86#entry86comment</comments>
      <pubDate>Tue, 1 Oct 2024 00:52:22 +0900</pubDate>
    </item>
    <item>
      <title>레디스 활용기 - 2</title>
      <link>https://itistori.tistory.com/85</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;새로운 프로세스 구현하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 &lt;a title=&quot;레디스 활용기 - 1&quot; href=&quot;https://itistori.tistory.com/83&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;레디스&amp;nbsp;활용기&amp;nbsp;-&amp;nbsp;1&lt;/a&gt;&amp;nbsp;는 어떻게 레디스를 활용하기로 결정하게 되었는지에 대한 내용이었다면, 이번에는 그 구체적인 구현 방식을 소개해 보고자 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;레디스의 특징&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현을 하기 위해 학습해야 했던 레디스 (Redis) 의 몇몇 특징은 다음과 같다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;레디스는 key-value 형태로 데이터를 저장한다.&lt;/li&gt;
&lt;li&gt;value 는 hkey-value 형태 또는 그냥 문자열이다.&lt;/li&gt;
&lt;li&gt;key 가 문자열인 데에 반해, hkey 는 해시 값으로 인식된다.&lt;/li&gt;
&lt;li&gt;여러 명령어를 통해 key / hkey / value 를 다룰 수 있다. 몇 가지 자주 사용되는 명령어들은 아래와 같다:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;get {key 이름} : key-value(문자열) 인 경우에 {key 이름} 에 해당하는 value 를 반환한다.&lt;/li&gt;
&lt;li&gt;hget {key 이름} {field(hkey) 이름} : key-value(해시) 인 경우에 {key 이름} 과 {field(hkey) 이름} 에 해당하는 value 를 반환한다.&lt;/li&gt;
&lt;li&gt;keys {key 이름} : 이름에 {key 이름} 을 포함하는 key 들을 모두 반환한다. {key 이름} 이 asterisk (*) 인 경우에는 모든 key 들을 반환한다.&lt;/li&gt;
&lt;li&gt;hgetall {key 이름} : {key 이름} key 에 row 로 등록된 field(hkey) 들과 valule 들을 모두 반환한다.&lt;/li&gt;
&lt;li&gt;set {key 이름} {value 이름} : {key 이름} key 에 {value 이름} value 를 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;레디스가 적합했던 이유&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;레디스 사용기 - 1&quot; href=&quot;https://itistori.tistory.com/83&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;레디스 활용기 - 1&lt;/a&gt; 에서도 확인했던 것처럼, 현재 &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;방문자 출입기록&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;테이블에&lt;/span&gt; 들어가야 할 데이터는 &lt;span style=&quot;background-color: #f8fbfb; color: #333333; text-align: start;&quot;&gt;로그 테이블&lt;/span&gt;에서 재조합이 필요했다. 이는 소스 상에서 &lt;span style=&quot;background-color: #f8fbfb; color: #333333; text-align: start;&quot;&gt;로그 테이블&lt;/span&gt; 엔티티의 도메인 클래스 객체 A를 우선 조회한 후, A가 갖고 있는 데이터로&amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;방문자 출입기록&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;테이블 엔티티의 도메인 클래스 객체 B를 생성하여 테이블에 추가함으로써 가능했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;이러한 방식은 불필요하게 많고 복잡한 RDB 조회가 발생하고, &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;주기적으로 도는 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;스케줄러에 대한 의존성이 크다는 점에서 프로그램 성능을 떨어트린다. 레디스 (Redis) 를 사용하면 빠른 읽기/쓰기가 가능하므로,&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;IoT 기기에서 수신된 데이터로 위 설명에서의 B 객체를 생성한 후,&lt;/li&gt;
&lt;li&gt;실시간으로 레디스에 쓸 수 있게 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;변경된 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;스프링에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;RedisTemplate 를 사용하면 쉽게 레디스를 다룰 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구현 동안에는 Redis Desktop Manager 를 사용하여 로컬 레디스의 상태를 확인하며 진행할 수 있었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://itistori.tistory.com/84&quot;&gt;Redis Desktop Manager 설치 방법&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레디스가 활용될 프로세스는 아래와 같다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;IoT 기기에서 데이터가 수신될 때마다, 아래 중 한 가지를 수행한다:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;하루를 기준으로 방문자 출입기록 관련 데이터 수신이 최초인 경우, 레디스에 새로운 key 를 생성한다.&lt;/li&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;이미 하루 중에 방문자 출입기록 관련 데이터 수신을 했던 경우, 생성되어 있을 key 의 value 를 수정한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;이 때 이미 하루 중에 데이터 수신이 되었는지의 여부는 &lt;u&gt;TTL 이 당일이 끝나는 시간까지로 설정되어 있는 다른 key&lt;/u&gt; 를 조회하여 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;하루가 끝나는 자정에 방문자 출입기록 관련 데이터 value 를 추출하여 RDB 에 저장한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li data-ke-style=&quot;style3&quot;&gt;스케줄러로 구현한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방문자 출입기록 관련 key - value 쌍에서 value 에 해당하는 데이터는 다음과 같다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 방문자 타입 별 방문 횟수
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;방문자 타입은 소스 상 로직에서 판단한 후 프로세스 1-2 에서 타입에 맞는 데이터 값 + 1 을 해주었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;전체 방문자 방문 횟수 총계
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;프로세스 1-2 에서 value 수정 시 총계에 해당하는 데이터는 단순 + 1 을 해주었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배운 점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;레디스에서 hkey 로 등록된 데이터는 key-hkey-value 형태로 이뤄져 있으며, hkey-value 가 비어 있을 시 TTL 설정이 불가능하다. 그래서 구현 당시에 hkey-value 를 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;임의로&lt;span&gt; 설정해준 뒤에, TTL 을 설정하고, 이후 다른 hkey-value 쌍이 추가로 저장된 이후에 임의 hkey-value 쌍을 삭제하는 방식으로 진행해야 했다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;짧은 시간 안에 많은 데이터를 다루어야 하는 상황에선, 레디스가 매우 유용하다. 그러나 소스 상 로직에서도 분명 데이터 누락을 잘 확인해주어야 한다. 구현 당시에는 방문자 10명 정도가 약 1~2초 이내에 방문하는 상황에서는 1명이나 2명에 대한 카운트가 누락되는 경우가 있었다. 그래서 for 문 내에서 방문자 데이터를 레디스에 쓰는 로직이 정상적으로 수행되기 전까지 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;다른 로직이 수행되지 못하게&lt;span&gt; 반복하도록 구현하여 해결해야 했다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDB 만 쓰다가 처음으로 NoSQL 기반의 데이터베이스를 사용했는데, 확실히 단순한 질의 방식이나 빠른 읽기/쓰기가 쉬운 사용성을 보장한다는 점에서 큰 강점을 가졌다는 것을 느낄 수 있는 경험이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <category>IOT</category>
      <category>Redis</category>
      <category>spring</category>
      <category>실시간데이터</category>
      <author>Nickolodeon</author>
      <guid isPermaLink="true">https://itistori.tistory.com/85</guid>
      <comments>https://itistori.tistory.com/85#entry85comment</comments>
      <pubDate>Mon, 8 Jul 2024 22:50:14 +0900</pubDate>
    </item>
    <item>
      <title>레디스 활용기 - 1</title>
      <link>https://itistori.tistory.com/83</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;  개요&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 사물 또는 사람에게 부착된 IoT 기기로부터 BLE 신호 형태로 수신되어 정제된 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;데이터를 RDB 에 쓰고, 다시 읽어서 상태 (위치 등) 를 모니터링 할 수 있는 소프트웨어 개발 및 운영에 참여하고 있는 중이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사업은 병원들을 위주로 전개하고 있으며, 각 클라이언트마다 서로 다른 요구사항들을 갖고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 3년 전 이 모니터링 소프트웨어를 설치하여 지금까지 개발 및 유지보수를 해주고 있는 Y 병원에서 한 달 전쯤 이슈제기를 해왔다:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;방문자 출입기록과 로그 데이터가 일치하지가 않습니다!&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방문자 출입기록은 말 그대로 병원에 각기 다른 목적으로 방문하는 다양한 사람들의 현황을 IoT 기기 (Y 병원에서는 스마트폰 앱) 를 이용하여 파악하고 정리한 기록이다. 회사에서 개발 중인 모니터링 소프트웨어에는 IoT 기기로부터 신호를 받을 때 이미 이 IoT 기기를 사용하는 사람의 프로필을 알 수 있다. 그리고 수신된 정보를 잘 정리해서 기록해놓는다. 모든 사람에 대해 출입 유형을 분석하고 내용을 재구성해서 DB 에 써넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, 프로세스에 무엇인가 문제가 있었다는 이야기였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;  기존 프로세스의 문제점&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;기존 프로세스는 아래와 같았다:&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;1385&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crNurM/btsHGzrVAw1/lNqrnWYJjGhlUwa1j9BAF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crNurM/btsHGzrVAw1/lNqrnWYJjGhlUwa1j9BAF1/img.png&quot; data-alt=&quot;먼저 로그 테이블에 넣고, 하루에 두 번씩 방문자 출입기록 테이블로 옮긴다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crNurM/btsHGzrVAw1/lNqrnWYJjGhlUwa1j9BAF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrNurM%2FbtsHGzrVAw1%2FlNqrnWYJjGhlUwa1j9BAF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1385&quot; height=&quot;333&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;1385&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;먼저 로그 테이블에 넣고, 하루에 두 번씩 방문자 출입기록 테이블로 옮긴다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;위 그림에서 하루 두 번씩 스케줄러가 돌면서 로그 테이블에 있는 데이터를 재조합해 방문자 출입기록 테이블에 넣는 형태였다. 데이터를 재조합하는 쿼리는 꽤 복잡했는데, 테이블들의 구조가 아래와 같은 차이와 공통점이 있었기 때문이었다:&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 99.6476%; height: 131px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style5&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 14.8341%; height: 16px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 7.25132%; height: 16px;&quot;&gt;PK&lt;/td&gt;
&lt;td style=&quot;width: 14.1599%; height: 16px;&quot;&gt;각 컬럼이 나타내는 것&lt;/td&gt;
&lt;td style=&quot;width: 16.9955%; height: 16px;&quot;&gt;유니크 키&lt;/td&gt;
&lt;td style=&quot;width: 18.2867%; height: 16px;&quot;&gt;특징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 14.8341%; height: 20px;&quot;&gt;로그 테이블&lt;/td&gt;
&lt;td style=&quot;width: 7.25132%; height: 20px;&quot;&gt;로그 번호&lt;/td&gt;
&lt;td style=&quot;width: 14.1599%; height: 20px;&quot;&gt;로그의 다양한 정보&lt;/td&gt;
&lt;td style=&quot;width: 16.9955%; height: 20px;&quot;&gt;로그 등록 날짜 / 시간&lt;/td&gt;
&lt;td style=&quot;width: 18.2867%; height: 20px;&quot;&gt;각 row 가 단일 정보를 담음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 14.8341%; height: 40px;&quot;&gt;방문자 출입기록 테이블&lt;/td&gt;
&lt;td style=&quot;width: 7.25132%; height: 40px;&quot;&gt;로그 번호&lt;/td&gt;
&lt;td style=&quot;width: 14.1599%; height: 40px;&quot;&gt;방문자 종류 별 카운트&lt;/td&gt;
&lt;td style=&quot;width: 16.9955%; height: 40px;&quot;&gt;통계, 로그 등록 날짜 / 시간&lt;/td&gt;
&lt;td style=&quot;width: 18.2867%; height: 40px;&quot;&gt;각 row 가 여러 정보의 통계임&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 로그 테이블에서는 데이터의 통계를 내서 방문자 출입기록 테이블로 insert 해야 하는 상황이었고, 이슈가 발생한 시점에는 1억 6천만 건의 데이터가 저장되어 있었다. 통계 쿼리 수행에는&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #10101b; text-align: left;&quot;&gt;383.437&lt;/span&gt; 초 = 약 6분이 걸리고 있었는데, 이는 다른 스케줄러가 돌기 시작할 때까지의 간격 이상의 시간이 쿼리에 소요되고 있었다는 의미였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  변경된 프로세스&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 쿼리를 튜닝하고자 했는데, 이미 인덱스도 걸려 있었고 통계를 내기 위해 반드시 수행되어야 하는 로직으로만 구성되어 있는 것을 확인하고서 다른 방법을 고민해보게 되었다. 먼저 떠오른 방법은 간단했다. 바로 IoT 기기에서 신호가 들어오면 로그 테이블에 쌓은 뒤 방문자 출입기록 테이블에 넣지 않고, 바로 방문자 출입기록 테이블에 기록하도록 하는 것이었다. 이 때 몇 가지 걸리는 것이 있었다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫째, 신호 수신 시 곧바로 어떤 종류의 방문자인지 알 수 있는가?&lt;/li&gt;
&lt;li&gt;둘째, 몇 초 단위로 신호를 받는데, 다른 로직에 영향 없이 수신된 데이터를 RDB 에 쓰는 로직을 추가할 수 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 동료 분들과 이 두가지 문제에 대해 상의했고, 내린 결론은 아래와 같았다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫째, 신호 수신에는 대상의 카테고리 정보 등 신호에 대한 정보들이 포함된다. 여기서 대상의 카테고리 정보가 방문자 종류와 같은 데이터이다.&lt;/li&gt;
&lt;li&gt;둘째, 수초 단위로 RDB 에 쓰는 로직은 부하가 심할 수 있으므로, 쓰기/읽기가 빠른 NoSQL 기반 데이터 저장소인 레디스를 활용하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 결론으로 도출된 해결 방법들을 구체적으로 어떻게 구현했는지 소개하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <author>Nickolodeon</author>
      <guid isPermaLink="true">https://itistori.tistory.com/83</guid>
      <comments>https://itistori.tistory.com/83#entry83comment</comments>
      <pubDate>Tue, 28 May 2024 22:30:20 +0900</pubDate>
    </item>
    <item>
      <title>@@sql_mode 변경 방법</title>
      <link>https://itistori.tistory.com/82</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;...which&amp;nbsp;is&amp;nbsp;not&amp;nbsp;functionally&amp;nbsp;dependent&amp;nbsp;on&amp;nbsp;columns&amp;nbsp;in&amp;nbsp;GROUP&amp;nbsp;BY&amp;nbsp;clause;&amp;nbsp;this&amp;nbsp;is&amp;nbsp;incompatible&amp;nbsp;with&amp;nbsp;sql_mode=only_full_group_by&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 에러가 발생해서, 확인해보니 full_group_by 옵션이 켜져 있어 group by 절에 없는 컬럼은 어느 부분에 표시해야 할 지 정하지 못해서 발생하는 문제라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;full_group_by 옵션을 끄기 위해, 구글링을 해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 아래 쿼리로 full_group_by 옵션이 설정되어 있는지 확인했다:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;쿼리: select @@sql_mode;&lt;br /&gt;결과 (@@sql_mode 컬럼의 값): &lt;br /&gt;ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후, 이를 끄기 위해 windows 내의 mysql 관련 config 설정 파일인 my.ini 를 찾아봤는데, 도저히 찾을 수가 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 위치에 있다고 하는데...&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;C:\Program Files\MySQL\MySQL Server 8.0&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무리 봐도 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 방법을 찾아보려고 같은 팀 동료분께 문의드렸는데, chatGPT 로 금방 찾아주셨다. 아래 쿼리로 가능하다고 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 @@sql_mode 를 ONLY_FULL_GROUP_BY 를 제외한 나머지로 재설정하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 실패...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고보니 잘못된 위치에서 my.ini 파일을 찾고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program Files 가 아니라 ProgramData 였다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 위치에서 my.ini 을 발견하였다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;C:\ProgramData\MySQL\MySQL Server 8.0&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my.ini 내에 다음 한 줄을 추가해주었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 windows &amp;gt; 시스템 &amp;gt; 서비스에서 MySQL 서비스를 찾아 다시 시작 해주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHVh02/btsHzuZKNKg/yKmHqv64xkDJV4rAEfCR01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHVh02/btsHzuZKNKg/yKmHqv64xkDJV4rAEfCR01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHVh02/btsHzuZKNKg/yKmHqv64xkDJV4rAEfCR01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHVh02%2FbtsHzuZKNKg%2FyKmHqv64xkDJV4rAEfCR01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;822&quot; height=&quot;682&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 해결 완료.&lt;/p&gt;</description>
      <category>Project</category>
      <author>Nickolodeon</author>
      <guid isPermaLink="true">https://itistori.tistory.com/82</guid>
      <comments>https://itistori.tistory.com/82#entry82comment</comments>
      <pubDate>Thu, 23 May 2024 18:41:22 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Controller 메서드 파라미터에 어노테이션이 안 붙어있을 때</title>
      <link>https://itistori.tistory.com/79</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 String, Long 타입은 @RequestParam 을 붙였을 때와 같이,&lt;br /&gt;이외의 클래스 객체는 @ModelAttribute 를 붙였을 때와 같이 동작합니다.&lt;/p&gt;</description>
      <category>Web Framework</category>
      <author>Nickolodeon</author>
      <guid isPermaLink="true">https://itistori.tistory.com/79</guid>
      <comments>https://itistori.tistory.com/79#entry79comment</comments>
      <pubDate>Fri, 10 Nov 2023 11:17:41 +0900</pubDate>
    </item>
    <item>
      <title>[기술면접] General Interview Questions</title>
      <link>https://itistori.tistory.com/78</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 컴포넌트와 모듈의 차이&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;① 컴포넌트: &lt;span style=&quot;font-size: 1em; letter-spacing: 0px;&quot;&gt;런타임 개체 단위&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; letter-spacing: 0px;&quot;&gt;② 모듈: &lt;/span&gt;&lt;span style=&quot;font-size: 1em; letter-spacing: 0px;&quot;&gt;실질적으로 구현이 된 단위&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;※ 서버 / 클라이언트 구조에서 서버 1개 &amp;middot; 클라이언트 100개라면?&lt;br /&gt;&lt;span style=&quot;color: #333333; letter-spacing: 0px;&quot;&gt;&amp;rarr; 모듈: 서버 1 + 클라이언트 1 = 2개 &lt;/span&gt;&lt;span style=&quot;color: #333333; letter-spacing: 0px;&quot;&gt;&amp;rarr; 컴포넌트: 서버 1 + 클라이언트 100 = 101개&lt;/span&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 자바란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&quot;Write once, Run anywhere&quot; 라는 특성의 객체지향 언어&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 자바의 구동원리&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;① 컴파일러가 코드를 검증하고 JVM이 읽는 바이트 코드 (.class) 로 변환한다.&lt;br /&gt;② JVM 으로 바이트 코드를 가져온다.&lt;br /&gt;③ 바이트 코드가 올바른지 검증한다.&lt;br /&gt;④ 인터프리트한다.&lt;br /&gt;⑤ 바이트 코드를 바이너리 코드로 변경하고 JVM 의 클래스 영역에 저장한다.&lt;br /&gt;⑥ 런타임 과정에서 클래스들의 static 변수를 자동으로 초기화한다.&lt;br /&gt;⑦ static 블록을 수행한다.&lt;br /&gt;⑧ 인스턴스 블록을 수행한다.&lt;br /&gt;⑨ 생성자를 호출한다.&lt;br /&gt;⑩ main 함수를 호출한다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. JVM 의 특징&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;▷ 스택 기반의 가상 머신이다.&lt;br /&gt;▷ GC (Garbage Collector) 가 동작한다.&lt;br /&gt;▷ 플랫폼에 독립적으로 작동한다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 객체 지향과 절차 지향의 차이점&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;◎ 절차 지향 프로그래밍 ▶ 루틴, 서브루틴, 함수 (프로시저) 등을 이용해 프로그래밍하는 것&lt;br /&gt;◎ 객체 지향 프로그래밍 ▶ 프로그램을 데이터 \ 처리방법 으로 나누지 않고 객체 내에 데이터와 처리방법 (동작) 을 모두 담아 객체 간의 상호작용으로 서술하는 것 ▶캡슐화, 상속, 다형성, 추상화와 같은 특징을 가짐&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 객체 지향 언어의 특징&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;◈ 캡슐화:객체의 속성과 행위를 하나로 묶고 실제 구현 내용의 일부는 외부에 감추는 것&lt;br /&gt;◈ 상속: 새 클래스가 기존 클래스의 자료 및 연산을 사용할 수 있는게 하는 것&lt;br /&gt;◈ 다형성: 하나의 요소에 여러 개념을 넣은 것 (오버라이딩, 오버로딩)&lt;br /&gt;◈ 추상화: 공통되는 속성이나 기능을 묶어서 이름을 붙이는 것&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 상속과 구현의 차이점과 특징 및 장단점&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;상속은 is-a, 구현은 can-do&lt;br /&gt;장점 ☞ 코드의 중복을 줄일 수 있다; 확장성이 증가한다; 하나의 인터페이스와 여러 구현체로 다형성을 구현 가능하다.&lt;br /&gt;단점 ☞ 다중상속 시 복잡도가 증가한다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. 오버라이딩과 오버로딩&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;오버라이딩? 상속하는 부모 클래스의 함수를 재정의하는 것, 함수 이름 / 매개변수 / 반환형이 동일하다.&lt;br /&gt;오버로딩? 함수 이름 외의 다른 요소들을 재정의한다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. 기본형 변수와 참조형 변수&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;기본형 ≫ Stack 영역에 값이 저장되는 변수&lt;br /&gt;e.g. byte, short, int, long, float, double, boolean, char&lt;br /&gt;참조형 ≫ Heap 영역에 저장되고, Stack 에 저장되는 주소값에 의해 참조되는 변수&lt;br /&gt;e.g. Integer, Long, Double, Character, String&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10. 스택 오버플로우의 원인&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;스택 포인터가 스택의 경계를 넘는 것이 원인&lt;br /&gt;왜? 상당히 깊은 / 무한 루프 (예) 탈출 조건이 없는 재귀 함수)&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;11. 메모리 누수란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;프로세스가 메모리 할당을 한 후 해제하지 않으면 발생한다.&lt;br /&gt;프로세스를 장기간 동작시키는 경우에 메모리 누수의 위험이 있다.&lt;br /&gt;막기 위해서는 프로세스가 끝날 때 메모리가 할당된 변수를 제거하면 된다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;12. static 이란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;절차 지향과 객체 지향으로 나누어 생각할 수 있다.&lt;br /&gt;㉮ 절차 지향: 지역변수 &amp;middot; 전역변수; 함수 내에서만 사용가능 (지역 변수) 함수 종료 시까지 메모리에서 해제되지 않음 (전역 변수)&lt;br /&gt;㉯ 객체 지향: 클래스 멤버; 클래스 내에 선언이 되면 클래스 관련 모든 인스턴스가 사용 가능한 객체이다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;13. 싱글턴 패턴이란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;인스턴스를 한 번만 생성하고 계속 재사용하는 패턴&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;14. 전략 패턴?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;스트래티지 패턴(Strategy pattern)이라고도 하며, 교환 가능한 행동을 캡슐화하고 위임을 해 행동을 다르게 하는 패턴&lt;br /&gt;e.g. '운송 전략' 이라는 인터페이스를 생성하고 서로 다른 전략을 클래스로 구현한 후, 버스나 기차와 같은 운송 수단이 전략을 매개 변수로 변경할 수 있도록 한다. 버스나 기차의 내부 구현 방식은 한 메서드의 매개 변수로 주어지는 전략에 따라 행동을 변경하는 방식으로 되어 있다.&lt;br /&gt;&lt;span style=&quot;background-color: #313338; color: #dbdee1; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;15. 익명 클래스와 익명 객체&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;익명 클래스 &amp;rarr; 인스턴스를 생성함과 동시에 클래스를 선언한다; 일회성, 유일의 객체&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;16. 스레드란? 스레드가 사용되는 때&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;프로세스 내에서 실제로 작업을 수행하는 주체&lt;br /&gt;하나 이상의 프로세스가 있을 때 멀티 스레드&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;17. 여러 번 Join 하기 vs 역정규화&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;데이터가 500만개만 되도 join 을 여러 번 하는 것은 시간이 많이 걸린다. 정규화되지 않은 DB 라도 foreign key 를 이용해 데이터를 바로 참조할 수 있도록 설계하는 것을 선호하기도 한다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;18. 쿼리 실행 계획&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;explain 키워드를 사용한다.&lt;br /&gt;id, type, key 등의 컬럼의 값을 확인하여 쿼리가 어떻게 실행될지 판단할 수 있다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;19. 서브 쿼리 순서&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;서브 쿼리 &amp;rarr; 메인 쿼리&lt;br /&gt;장점: 쿼리의 각 부분을 명확히 구분할 수 있다; 복잡한 JOIN 을 대체할 수 있다; 복잡한 JOIN에 비해 가독성이 뛰어나다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;20. lazy loading&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;당장에 필요하지 않은 리소스를 추후에 로딩하는 것&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;21. Spring을 사용함에 있어서 가장 핵심적인 이유는?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Spring 의 IOC, 즉 제어의 역전은, 개발자가 관리하던 객체의 생성부터 소멸까지의 생명주기를 스프링 빈을 통해 프레임워크가 관리한다는 의미이다. Spring 은 개발 외의 구성을 신경 쓰지 않고 오롯이 코드 작성에 집중할 수 있게 해준다.&lt;br /&gt;▷ 스프링은 POJO 기반의 프레임워크이므로, 객체 지향적 설계를 하는 데에 용이하며, 개발자의 생산성을 높여준다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;22. Java 컴파일 과정을 설명하시요&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Java 는 컴파일러에 의해 바이트 코드 (.class) 로 변환된다. 바이트 코드를 JVM 이 읽고 바이너리 코드로 변환한 후 클래스 영역에 저장한다. execution engine 이 바이너리 코드를 읽고 실행시킨다.&lt;br /&gt;▷ 1. 컴파일러가 코드를 검증한 후 바이트 코드 (.class) 로 변환한다.&lt;br /&gt;▷ 2. &amp;nbsp;컴파일러가 바이트 코드를 검증한다.&lt;br /&gt;▷ 3. 인터프리트&lt;br /&gt;▷ 4. 바이트 코드를 바이너리 코드로 변환하고 JVM 의 클래스 영역에 저장한다.&lt;br /&gt;▷ 5. 런타임 과정에서 static 변수들을 초기화한다.&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;23. Array와 Linked List의 차이점을 설명하시고 시간복잡도 순으로 설명하시요&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Array는 값을 단순히 나열한 자료 구조로 삽입, 조회, 삭제 모두 O(n) 의 시간 복잡도를 가진다.&lt;br /&gt;LinkedList는 노드로 구성되어 각 노드가 앞뒤 노드의 정보를 알고 있는 노드끼리 연결된 형태의 자료구조이다. 그래서 단순히 삽입과 삭제에 앞뒤 노드가 참조하는 모양을 바꾸면 되기 때문에 O(1)이 걸리고 최악의 경우 O(n)이 걸린다. 조회도 O(1) 이 걸리지만 최악의 경우 O(n) 이 걸린다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;24. TCP&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;/UDP의 통신 방식 차이점&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;TCP 는 연결 지향적인 프로토콜로, 서버와 클라이언트 간 통신을 위한 연결을 맺을 때 3-way handshake, 끊을 때 4-way handshake 를 사용해 안전하게 연결한다. 보안성이 높지만 속도가 느리다는 단점이 있다.&lt;br /&gt;UDP 는 서버의 상태에 상관 없이 계속해서 연결 요청을 보낸다. 그래서 연결이 보장되지는 않지만 통신의 속도는 빠르다는 장점이 있다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;25. HTTP/HTTPS의 정의와 차이점&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;HTTP 는 서버 클라이언트 간 네트워크 통신 프로토콜이다. 80 포트를 사용한다.&lt;br /&gt;HTTPS 는 HTTP 에 데이터 암호화를 추가한 프로토콜이다. 443 포트를 사용한다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;26. DB의 ACID 요소 설명&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;A: Atomicity, 원자성으로 트랜잭션은 오직 완전히 성공하거나 완전히 실패하기만 한다는 특성이다.&lt;br /&gt;C: Consistency, 일관성으로 트랜잭션 전후로 데이터의 모순이 존재하지 않는다는 특성이다.&lt;br /&gt;I: Isolation, 독립성으로 트랜잭션은 한번에 하나만 존재할 수 있다는 특성이다.&lt;br /&gt;D: Durability, 지속성으로 모든 트랜잭션은 (rollback 포함) 영원히 남는다는 특성이다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;27. DB의 Transaction이란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;트랜잭션은 DB 에 수행하는 작업의 단위이다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;28. 조인이란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;2 개 이상의 테이블 레코드들을 하나로 엮어서 하나의 열로 보고 싶을 때 사용하는 쿼리문이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;29. SQL vs NoSQL&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;SQL은 정형화된, 스키마가 있는 중복이 없고 관계가 있는 데이터베이스 &amp;rarr; 수직적 확장만 가능&lt;br /&gt;NoSQL은 비정형화된, 스키마가 없고 중복이 있으며 관계도 없는 데이터베이스&amp;nbsp;&amp;rarr; 수평적, 수직적 확장 모두 가능&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;30. 이상 (Anomaly) 의 종류&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. 삽입 이상&lt;br /&gt;2. 갱신 이상&lt;br /&gt;3. 삭제 이상&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;31. 쿠키와 세션&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Http 의 Stateless, Connectionless 한 특징을 보완할 수 있다.&lt;br /&gt;쿠키는 클라이언트에 저장된다. 쿠키 만료 시간을 설정하면 브라우저가 종료되어도 상태를 유지할 수 있다. 서버에 저장되지 않기 때문에 보안에 취약하지만 속도가 빠르다.&lt;br /&gt;세션은 서버에 저장된다. 브라우저가 종료되면 세션은 만료된다. 서버에 저장되어 보안성이 높지만 서버의 자원을 활용하기 때문에 서버에 부하를 줄 수 있다.&lt;/blockquote&gt;</description>
      <category>CS</category>
      <author>Nickolodeon</author>
      <guid isPermaLink="true">https://itistori.tistory.com/78</guid>
      <comments>https://itistori.tistory.com/78#entry78comment</comments>
      <pubDate>Wed, 11 Oct 2023 23:38:09 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Interview Questions for Beginner</title>
      <link>https://itistori.tistory.com/77</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;스프링 프레임워크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 IOC (제어의 역전), DI (의존성 주입) 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스프링 빈&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스프링 빈의 특징&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 빈은 Singleton 으로 생성해 메모리나 성능 최적화에 유리하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IOC (제어의 역전)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 제어권을 다른 대상에게 위임한 것으로, 개발자 (또는 애플리케이션 코드) 가 아닌 프레임워크가 객체의 생성부터 소멸까지 관리하게 되는 것을 말한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DI (의존성 주입)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 클래스와 다른 클래스 사이의 의존 관계를 컨테이너가 빈을 가지고 자동으로 연결해주는 것을 말한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AOP (관점 지향 프로그래밍)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프록시 패턴 기반의 구현체를 사용한다. 중복 제거, 객체 간 복잡도 증가에 대한 해결책이 될 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;POJO&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마틴 파울러는 EJB 에서 돌아서서 POJO 로 돌아와야 한다고 이야기했다. 비즈니스 로직과 특정환경을 분리시켜 객체지향적이고 단순한 코드를 짤 수 있게 해준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DAO 와 DTO&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAO 는 Data Access Object 로, DB 에 접근할 목적으로 사용되는 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO 는 Data Transfer Object 로, 계층 간 데이터 교환을 위해 사용되는 객체이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Filter, Interceptor&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Filter 는 WAS 단에서 Spring 과 무관한 자원을 처리하고, Interceptor 는 Spring 내에서 controller 실행 전 후에 자원을 가로채서 조작한다.&lt;/p&gt;</description>
      <category>CS</category>
      <author>Nickolodeon</author>
      <guid isPermaLink="true">https://itistori.tistory.com/77</guid>
      <comments>https://itistori.tistory.com/77#entry77comment</comments>
      <pubDate>Fri, 22 Sep 2023 07:34:03 +0900</pubDate>
    </item>
    <item>
      <title>[OOP &amp;amp; Java &amp;amp; etc.] Interview Question for Beginner</title>
      <link>https://itistori.tistory.com/76</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot;&gt;객체 지향 프로그래밍&lt;/h1&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;캡슐화&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;항상 요구 사항은 추가되거나 변경될 수 있다&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1. 객체의 의인화&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현실 세계와는 다르게 가상 세계에서의 객체는 능동적이다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2. 클래스 설계&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 객체가 형태를 갖추면 비슷한 객체들끼리 타입으로&amp;nbsp; 묶어 클래스를 추상화한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 외부와 내부를 구분 지어야 한다. 외부와의 협력은 public 인터페이스를 통해서만 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 객체가 능동적이기 위해서는 외부로부터의 간섭을 최소화해야 한다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3. 객체의 특징&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 캡슐화 - 객체의 세부적인 상태는 감추고 메시지는 오직 public 인터페이스로만 주고받는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 단일 책임 - 모든 객체는 맡은 일을 외부의 도움 없이 할 수 있어야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 응집도 - 밀접하게 연관되어 있는 일만 수행하고 관련이 없는 일은 다른 객체에게 위임해야 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;훌륭한 객체는 외부와 내부의 구분이 잘 이루어져있다. 외부에서 알지 못하는 내부의 상태를 구현이라고 하고&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;외부에서 접근 가능한 부분을 public 인터페이스라고 한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;함수형 프로그래밍&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;함수를 일급 객체로 간주한다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;MVC 아키텍처&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 모델 (Model) : 컨트롤러가 호출할 때 요청에 맞는 역할을 수행하는 영역이다. 비즈니스 로직을 수행하는 부분이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;- 뷰 (View) : 사용자에게 보여지는 화면을 만드는 역할을 한다. 사용자와의 상호작용을 위한 인터페이스가 있는 영역이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 컨트롤러 (Controller) : 일종의 조정자 또는 중재자로서, 클라이언트로부터 온 요청을 수행하는 모델 컴포넌트 (서비스 레이어) 를 호춣한다. 수행이 완료된 이후에는 뷰 (View) 로 결과를 반환한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5UGJC/btsuQlnlN0Y/cIc5DH78ScE0aRjzCmdGnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5UGJC/btsuQlnlN0Y/cIc5DH78ScE0aRjzCmdGnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5UGJC/btsuQlnlN0Y/cIc5DH78ScE0aRjzCmdGnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5UGJC%2FbtsuQlnlN0Y%2FcIc5DH78ScE0aRjzCmdGnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;847&quot; height=&quot;363&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Git flow vs GitHub flow vs GitLab flow&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GitLab flow 의 장점: 단순하고 릴리즈와 머지 등에 발생할 수 있는 오버헤드를 줄여준다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;Integer 과 int&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;Wrapper Class 와 Primitive 타입&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;차이점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제너릭이나 Collection 에서는 Wrapper class 만 사용할 수 있다. null 이 필요하거나 비교가 필요할 때 Wrapper class 를 사용하면 유용하다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;== 와 equals() 의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;== 는 메모리 주소를 비교하는 것이고, equals() 는 내용을 비교하는 것이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;Call by Value, Call by Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack 영역에 있는 변수가 수정되지 않는 것은 값만 가져오는 것이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 객체를 참조하고 있는 변수를 가져온 경우에는 같은 객체를 참조하기 때문에 객체도 변한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 Call by Reference 이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;Garbage Collection&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHdq1r/btsu2eH9SoN/Zn3nbKFnmvAUJI20te6iKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHdq1r/btsu2eH9SoN/Zn3nbKFnmvAUJI20te6iKk/img.png&quot; data-alt=&quot;Garbage Collector 의 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHdq1r/btsu2eH9SoN/Zn3nbKFnmvAUJI20te6iKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHdq1r%2Fbtsu2eH9SoN%2FZn3nbKFnmvAUJI20te6iKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;741&quot; height=&quot;217&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Garbage Collector 의 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;869&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bURx25/btsvarGHId3/ZHulNLpd4DQ1IMESHuAmyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bURx25/btsvarGHId3/ZHulNLpd4DQ1IMESHuAmyK/img.png&quot; data-alt=&quot;Reachability 와 Unreachability&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bURx25/btsvarGHId3/ZHulNLpd4DQ1IMESHuAmyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbURx25%2FbtsvarGHId3%2FZHulNLpd4DQ1IMESHuAmyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;585&quot; height=&quot;397&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;869&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Reachability 와 Unreachability&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stop the world 가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;young generation 과 old generation 이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;eden 과 survivor 영역이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;minor GC 와 full (major) GC 가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;mark and sweep 으로 이루어진다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;reachable 과 unreachable 을 분리한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;JVM&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Interpreter vs JIT compiler&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 줄씩 명령어 단위로 해석해서 실행하는 인터프리터와, 모두 native code 로 변환해 캐시에 저장하여 한 번에 실행하는 컴파일러가 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Stack / Heap / Method Area / PC Register&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stack: 스레드 하나당 가지고 있고, 변수들을 저장하는 스택과 Native 코드를 저장하는 스택이 있다.&lt;/li&gt;
&lt;li&gt;Heap: 동적으로 할당되는 객체들을 저장하며, GC 가 활동하는 영역이다.&lt;/li&gt;
&lt;li&gt;Method Area: 상수를 가지고 있는 Runtime constant pool 을 가지고 있으며, 모든 스레드가 공유하는 메모리 영역이다.&lt;/li&gt;
&lt;li&gt;PC Register: 실행되고 있는 명령어의 주소값을 가지고 있다. 스레드마다 하나씩 부여된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;String&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;string constant pool 이 존재하여 equals 메서드가 가능하게 만든다. intern() 이 실행되어서 String 을 리터럴로 생성할 때 constant pool 에 있으면 주소값을 반환하기 때문에 리터럴로 선언되면 == 가 true 를 반환하는 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인터페이스 vs 추상 클래스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터페이스: 상수와 추상 메서드 외에는 가질 수 없다.&lt;/li&gt;
&lt;li&gt;추상 클래스: 추상 메서드 하나만 가지고 있어도 추상 클래스이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;</description>
      <category>CS</category>
      <author>Nickolodeon</author>
      <guid isPermaLink="true">https://itistori.tistory.com/76</guid>
      <comments>https://itistori.tistory.com/76#entry76comment</comments>
      <pubDate>Thu, 21 Sep 2023 19:26:41 +0900</pubDate>
    </item>
    <item>
      <title>[자료 구조] Interview Question for Beginner</title>
      <link>https://itistori.tistory.com/75</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Array&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 조회: 논리적 저장 순서 = 물리적 저장 순서 (random access O(1) 가능)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 삭제, 삽입: 접근 O(1) + shift O(n) = O(n)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Linked List&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 조회: 논리적 저장 순서 != 물리적 저장 순서 O(n)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 삭제, 삽입: O(n)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Stack&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- LIFO or FILO&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Queue&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Heap 구현하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Stack 2개로 Queue 구현하기&lt;br /&gt;하나는 입력만 받고, 다른 하나는 pop 만 하게 한 후, pop 하는 쪽이 비면 입력 받는 stack 에 원소가 있다면 pop 시켜서 가져오게끔 구현하면 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Tree&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 포화 이진 트리: 모든 노드가 2개의 자식 노드를 갖는 트리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 완전 이진 트리: 위에서 아래로, 왼쪽에서 오른쪽으로 차곡차곡 채워져 있는 트리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 정 이진 트리: full binary tree, 모든 노드가 0개 또는 2개의 자식 노드를 갖는 트리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이진탐색트리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 구현 시 고려할 점이 많고 까다롭다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Heap&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시간복잡도&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 101.045%; height: 148px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 13.0536%; height: 20px;&quot;&gt;&lt;i&gt;종류&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.8341%; height: 20px;&quot;&gt;&lt;i&gt;ArrayList&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 9.31805%; height: 20px;&quot;&gt;&lt;i&gt;LinkedList&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.53671%; height: 20px;&quot;&gt;&lt;i&gt;HashSet&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.08033%; height: 20px;&quot;&gt;&lt;i&gt;TreeSet&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 9.18986%; height: 20px;&quot;&gt;&lt;i&gt;HashMap&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.72936%; height: 20px;&quot;&gt;&lt;i&gt;TreeMap&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.1325%; height: 20px;&quot;&gt;&lt;i&gt;PriorityQueue&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 17.081%; height: 20px;&quot;&gt;&lt;i&gt;ConcurrentHashMap&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 13.0536%; height: 16px;&quot;&gt;&lt;i&gt;add&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.8341%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 9.31805%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 8.53671%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 8.08033%; height: 16px;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 9.18986%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.72936%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 12.1325%; height: 16px;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 17.081%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 13.0536%; height: 16px;&quot;&gt;&lt;i&gt;remove&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.8341%; height: 16px;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 9.31805%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 8.53671%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.08033%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 9.18986%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.72936%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 12.1325%; height: 16px;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 17.081%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 13.0536%; height: 16px;&quot;&gt;&lt;i&gt;get&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.8341%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 9.31805%; height: 16px;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 8.53671%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.08033%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 9.18986%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 8.72936%; height: 16px;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 12.1325%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 17.081%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 13.0536%; height: 16px;&quot;&gt;&lt;i&gt;contains&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.8341%; height: 16px;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 9.31805%; height: 16px;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 8.53671%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 8.08033%; height: 16px;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 9.18986%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.72936%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 12.1325%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 17.081%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 13.0536%; height: 16px;&quot;&gt;&lt;i&gt;containsKey&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.8341%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 9.31805%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.53671%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.08033%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 9.18986%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 8.72936%; height: 16px;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 12.1325%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 17.081%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 13.0536%; height: 16px;&quot;&gt;&lt;i&gt;iterator.remove&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.8341%; height: 16px;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;width: 9.31805%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 8.53671%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.08033%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 9.18986%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.72936%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 12.1325%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 17.081%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 13.0536%; height: 16px;&quot;&gt;&lt;i&gt;next&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.8341%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 9.31805%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.53671%; height: 16px;&quot;&gt;O(h/n)&lt;/td&gt;
&lt;td style=&quot;width: 8.08033%; height: 16px;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 9.18986%; height: 16px;&quot;&gt;O(h/n)&lt;/td&gt;
&lt;td style=&quot;width: 8.72936%; height: 16px;&quot;&gt;O(logn)&lt;/td&gt;
&lt;td style=&quot;width: 12.1325%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 17.081%; height: 16px;&quot;&gt;O(h/n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 13.0536%; height: 16px;&quot;&gt;&lt;i&gt;size&lt;/i&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.8341%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 9.31805%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.53671%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.08033%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 9.18986%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 8.72936%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 12.1325%; height: 16px;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 17.081%; height: 16px;&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HashSet 과 HashMap&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HashSet: 중복을 허용하지 않는다. 순서가 없다. equals() 와 hashcode() 메서드를 사용해 중복 여부를 체크한다.&lt;/li&gt;
&lt;li&gt;HashMap: key 는 중복이 없지만, value 는 중복이 허용된다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS</category>
      <author>Nickolodeon</author>
      <guid isPermaLink="true">https://itistori.tistory.com/75</guid>
      <comments>https://itistori.tistory.com/75#entry75comment</comments>
      <pubDate>Thu, 21 Sep 2023 19:24:06 +0900</pubDate>
    </item>
  </channel>
</rss>