<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev_jaeha.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 20 Jul 2025 21:55:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. dev_jaeha.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jaeha_l" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[코드 처리하기]]></title>
            <link>https://velog.io/@jaeha_l/%EC%BD%94%EB%93%9C-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jaeha_l/%EC%BD%94%EB%93%9C-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 20 Jul 2025 21:55:55 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>문자열 code가 주어졌을 때, code를 앞에서부터 읽으면서 특정 규칙에 따라 새로운 문자열 answer를 만들어야 합니다.</p>
<h3 id="📚-규칙-설명">📚 규칙 설명</h3>
<p>mode는 0 또는 1의 값을 가지며, 초기값은 0입니다.</p>
<p>code를 왼쪽부터 차례대로 읽으면서 각 인덱스 i에 대해 다음 조건을 수행합니다.</p>
<h3 id="✅-mode가-0일-때">✅ mode가 0일 때</h3>
<p>code[i] == &quot;1&quot;이면 → mode를 1로 전환</p>
<p>&quot;1&quot;이 아니고 i가 짝수이면 → answer에 code[i] 추가</p>
<h3 id="✅-mode가-1일-때">✅ mode가 1일 때</h3>
<p>code[i] == &quot;1&quot;이면 → mode를 0으로 전환</p>
<p>&quot;1&quot;이 아니고 i가 홀수이면 → answer에 code[i] 추가</p>
<h3 id="❗추가-조건">❗추가 조건</h3>
<p>최종 문자열 answer가 빈 문자열이면 &quot;EMPTY&quot;를 반환합니다.</p>
<h4 id="🖥️-입력--출력-예시">🖥️ 입력 / 출력 예시</h4>
<table>
<thead>
<tr>
<th>code</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;abc1abc1abc&quot;</td>
<td>&quot;acbac&quot;</td>
</tr>
<tr>
<td>&quot;11111&quot;</td>
<td>&quot;EMPTY&quot;</td>
</tr>
</tbody></table>
<pre><code class="language-python">def solution(code):
    answer = &#39;&#39;
    mode = 0

    for i in range(len(code)):
        if code[i] == &quot;1&quot;:
            mode = 1 - mode  # mode 전환 (0 ↔ 1)
        else:
            if mode == 0 and i % 2 == 0:
                answer += code[i]
            elif mode == 1 and i % 2 == 1:
                answer += code[i]

    return answer if answer else &quot;EMPTY&quot;
</code></pre>
<h3 id="🔍-동작-예시-분석">🔍 동작 예시 분석</h3>
<pre><code>입력: &quot;abc1abc1abc&quot;
mode 변화 및 answer 누적 과정:</code></pre><table>
<thead>
<tr>
<th>i</th>
<th>code[i]</th>
<th>mode</th>
<th>조건 통과 여부</th>
<th>answer</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>a</td>
<td>0</td>
<td>✅ 짝수</td>
<td>a</td>
</tr>
<tr>
<td>1</td>
<td>b</td>
<td>0</td>
<td>❌ 홀수</td>
<td>a</td>
</tr>
<tr>
<td>2</td>
<td>c</td>
<td>0</td>
<td>✅ 짝수</td>
<td>ac</td>
</tr>
<tr>
<td>3</td>
<td>1</td>
<td>1</td>
<td>mode 전환</td>
<td>ac</td>
</tr>
<tr>
<td>4</td>
<td>a</td>
<td>1</td>
<td>❌ 짝수</td>
<td>ac</td>
</tr>
<tr>
<td>5</td>
<td>b</td>
<td>1</td>
<td>✅ 홀수</td>
<td>acb</td>
</tr>
<tr>
<td>6</td>
<td>c</td>
<td>1</td>
<td>❌ 짝수</td>
<td>acb</td>
</tr>
<tr>
<td>7</td>
<td>1</td>
<td>0</td>
<td>mode 전환</td>
<td>acb</td>
</tr>
<tr>
<td>8</td>
<td>a</td>
<td>0</td>
<td>✅ 짝수</td>
<td>acba</td>
</tr>
<tr>
<td>9</td>
<td>b</td>
<td>0</td>
<td>❌ 홀수</td>
<td>acba</td>
</tr>
<tr>
<td>10</td>
<td>c</td>
<td>0</td>
<td>✅ 짝수</td>
<td>acbac</td>
</tr>
</tbody></table>
<blockquote>
<p>최종 결과: &quot;acbac&quot;</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[문자열 겹쳐쓰기]]></title>
            <link>https://velog.io/@jaeha_l/%EB%AC%B8%EC%9E%90%EC%97%B4-%EA%B2%B9%EC%B3%90%EC%93%B0%EA%B8%B0</link>
            <guid>https://velog.io/@jaeha_l/%EB%AC%B8%EC%9E%90%EC%97%B4-%EA%B2%B9%EC%B3%90%EC%93%B0%EA%B8%B0</guid>
            <pubDate>Mon, 14 Jul 2025 21:26:46 GMT</pubDate>
            <description><![CDATA[<h3 id="📌-문제-설명">📌 문제 설명</h3>
<blockquote>
<p>문자열 my_string, overwrite_string, s가 주어집니다.</p>
</blockquote>
<blockquote>
<p>my_string의 인덱스 s부터 overwrite_string의 길이만큼을 overwrite_string으로 덮어쓴 문자열을 반환해야 합니다.</p>
</blockquote>
<h3 id="✅-제한-사항">✅ 제한 사항</h3>
<blockquote>
<p>my_string과 overwrite_string은 숫자와 알파벳으로 이루어짐
1 ≤ len(overwrite_string) ≤ len(my_string) ≤ 1000
0 ≤ s ≤ len(my_string) - len(overwrite_string)</p>
</blockquote>
<p>my_string[:s]는 파이썬 문자열 슬라이싱 문법 중 하나로, 문자열에서 처음부터 s-1번째 인덱스까지의 부분 문자열을 가져오는 표현입니다.</p>
<p>my_string[:s]
의미: 인덱스 0부터 s-1까지 잘라낸 부분 문자열</p>
<p>포함 범위: 시작 인덱스 0은 포함, 끝 인덱스 s는 포함하지 않음</p>
<p>형식: 문자열[시작:끝] → 시작 생략하면 0부터</p>
<p>📌 예시</p>
<pre><code class="language-python">my_string = &quot;HelloWorld&quot;
s = 5
print(my_string[:s])
출력:
Hello</code></pre>
<p>설명:</p>
<p>인덱스 0부터 4까지: 
&#39;H&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;</p>
<p>인덱스 5인 &#39;W&#39;는 포함되지 않음</p>
<h3 id="📌-관련-문법-요약">📌 관련 문법 요약</h3>
<blockquote>
<p>s[:n]    0부터 n-1까지
s[n:]    n부터 끝까지
s[a:b]    a부터 b-1까지
s[-1]    마지막 문자
s[::-1]    문자열 뒤집기</p>
</blockquote>
<h3 id="solution">Solution</h3>
<pre><code class="language-python">my_string[:s] + overwrite_string + my_string[s + len(overwrite_string):]</code></pre>
<p>my_string[:s]: 덮어쓰기 전까지의 앞부분</p>
<p>overwrite_string: 덮어쓸 문자열</p>
<p>my_string[s + len(overwrite_string):]: 덮어쓴 이후 뒷부분</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[멀리뛰기 - 피보나치 수열]]></title>
            <link>https://velog.io/@jaeha_l/%EB%A9%80%EB%A6%AC%EB%9B%B0%EA%B8%B0-%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98-%EC%88%98%EC%97%B4</link>
            <guid>https://velog.io/@jaeha_l/%EB%A9%80%EB%A6%AC%EB%9B%B0%EA%B8%B0-%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98-%EC%88%98%EC%97%B4</guid>
            <pubDate>Thu, 28 Mar 2024 13:49:59 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12914">https://school.programmers.co.kr/learn/courses/30/lessons/12914</a></p>
<h3 id="description">Description</h3>
<p>효진이는 멀리 뛰기를 연습하고 있습니다. 효진이는 한번에 1칸, 또는 2칸을 뛸 수 있습니다. 칸이 총 4개 있을 때, 효진이는
(1칸, 1칸, 1칸, 1칸)
(1칸, 2칸, 1칸)
(1칸, 1칸, 2칸)
(2칸, 1칸, 1칸)
(2칸, 2칸)
의 5가지 방법으로 맨 끝 칸에 도달할 수 있습니다. 멀리뛰기에 사용될 칸의 수 n이 주어질 때, 효진이가 끝에 도달하는 방법이 몇 가지인지 알아내, 여기에 1234567를 나눈 나머지를 리턴하는 함수, solution을 완성하세요. 예를 들어 4가 입력된다면, 5를 return하면 됩니다.</p>
<h3 id="제한-사항">제한 사항</h3>
<p>n은 1 이상, 2000 이하인 정수입니다.
입출력 예
n    result
4    5
3    3
입출력 예 설명
입출력 예 #1
위에서 설명한 내용과 같습니다.</p>
<p>입출력 예 #2
(2칸, 1칸)
(1칸, 2칸)
(1칸, 1칸, 1칸)
총 3가지 방법으로 멀리 뛸 수 있습니다.</p>
<pre><code class="language-java">class Solution {
    public int solution(int n) {

        // n이 2 이하인 경우, 그대로 n을 반환
        if (n &lt;= 2)
            return n;

        int[] a = new int[n + 1];

        a[1] = 1;
        a[2] = 2;


        for (int i = 3; i &lt;= n; i++) {
            a[i] = (a[i - 1] + a[i - 2]) % 1234567;
        }

        // n번째 위치의 경우의 수 % 1234567
        return a[n];
    }
}


// 한번에 1칸 or 2칸 뛸 수 있다.
// 멀리뛰기에 사용될 칸의 수 n
// answer = 뛸 수 있는 총 경우의 수 % 1234567
// 1 : [1] = 1
// 2 : [2] = 2
// 3 : [1,1,1],[1,2],[2,1] = 3
// 4 : [1,1,1,1],[1,1,2],[1,2,1],[2,1,1], [2,2] = 5
// 5 : [1,1,1,1,1],[1,1,1,2],[1,1,2,1],[1,2,1,1][2,1,1,1],[1,2,2],[2,1,2],[2,2,1] = 8

// 앞의 두 케이스의 경우의 수들의 합이 현재 케이스 경우의 수

// 점화식 : a[i] = a[i-1] + a[i-2]

// a[1] = 1 (1칸을 뛸 때의 경우의 수)
// a[2] = 2 (2칸을 뛸 때의 경우의 수)
// a[3] = a[2] + a[1] = 2 + 1 = 3 (3칸을 뛸 때의 경우의 수)
// a[4] = a[3] + a[2] = 3 + 2 = 5 (4칸을 뛸 때의 경우의 수)
// a[5] = a[4] + a[3] = 5 + 3 = 8 (5칸을 뛸 때의 경우의 수)
</code></pre>
<p>피보나치 수열에 대해서 알고 있었다면 더 빨리 풀 수 있었을 것 같다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[동기, 비동기 (Promise, async/await)]]></title>
            <link>https://velog.io/@jaeha_l/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-Promise-asyncawait</link>
            <guid>https://velog.io/@jaeha_l/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-Promise-asyncawait</guid>
            <pubDate>Mon, 05 Feb 2024 09:26:44 GMT</pubDate>
            <description><![CDATA[<h1 id=""></h1>
<h2 id="1-동기-vs-비동기"><strong>1. 동기 vs. 비동기</strong></h2>
<h3 id="동기-synchronous"><strong>동기 (Synchronous)</strong></h3>
<ul>
<li><strong>직렬적으로 작동:</strong> 작업이 순차적으로 진행되는 방식.</li>
<li>요청을 보내고 그 결과를 기다렸다가 다음 작업을 수행함.</li>
<li>실행 순서를 보장하며, 순서대로 작업이 완료될 때까지 다음 작업이 대기.</li>
</ul>
<h3 id="비동기-asynchronous"><strong>비동기 (Asynchronous)</strong></h3>
<ul>
<li><strong>병렬적으로 작동:</strong> 여러 작업이 동시에 진행되는 방식.</li>
<li>요청을 보내고 결과를 기다리지 않고 다음 작업을 수행함.</li>
<li>작업이 완료되면 특정 이벤트(콜백 함수, 프라미스 등)를 통해 결과를 처리.</li>
<li>비동기 작업은 순서를 보장하지 않을 수 있음.</li>
</ul>
<h3 id="차이-비교"><strong>차이 비교</strong></h3>
<ul>
<li>동기: 작업 순서를 중요시하며, 순서대로 실행되어야 하는 경우에 사용.</li>
<li>비동기: 시간이 오래 걸리는 작업이나 외부 요청을 기다리는 동안 다른 작업을 수행하고 싶을 때 사용.</li>
</ul>
<h2 id="2-promise란"><strong>2. Promise란?</strong></h2>
<ul>
<li><strong>Promise</strong>는 비동기 작업의 결과를 나타내는 객체로, 성공 또는 실패와 같은 상태를 가지고 있음.</li>
<li>주로 콜백 지옥(callback hell)을 피하고 코드를 더 깔끔하게 작성하기 위해 사용됨.</li>
</ul>
<h3 id="promise의-상태state"><strong>Promise의 상태(State)</strong></h3>
<ol>
<li><strong>Pending (대기):</strong> 초기 상태. 작업이 성공하거나 실패할 때까지 대기.</li>
<li><strong>Fulfilled (이행):</strong> 작업이 성공적으로 완료됨.</li>
<li><strong>Rejected (거부):</strong> 작업이 실패함.</li>
</ol>
<h3 id="promise-예시"><strong>Promise 예시</strong></h3>
<pre><code class="language-jsx">javascriptCopy code
const fetchData = new Promise((resolve, reject) =&gt; {
  // 비동기 작업 수행
  setTimeout(() =&gt; {
    const success = Math.random() &gt; 0.5;
    if (success) {
      resolve(&#39;Data fetched successfully!&#39;);
    } else {
      reject(new Error(&#39;Failed to fetch data.&#39;));
    }
  }, 1000);
});

// 사용
fetchData
  .then(data =&gt; console.log(data))
  .catch(error =&gt; console.error(error.message));
</code></pre>
<h2 id="3-async--await"><strong>3. Async / Await</strong></h2>
<h3 id="async-함수"><strong>async 함수</strong></h3>
<ul>
<li><strong>async</strong> 키워드를 함수 앞에 붙이면 해당 함수는 항상 프로미스를 반환.</li>
<li>비동기 작업이 완료될 때까지 기다리며, 결과는 이행된 프로미스로 반환됨.</li>
</ul>
<h3 id="await-키워드"><strong>await 키워드</strong></h3>
<ul>
<li><strong>await</strong>는 async 함수 내에서만 사용 가능.</li>
<li>Promise가 처리될 때까지 기다림. 결과를 반환하고 작업을 일시 중단.</li>
</ul>
<h3 id="async--await-예시"><strong>Async / Await 예시</strong></h3>
<pre><code class="language-jsx">javascriptCopy code
async function fetchData() {
  try {
    // 비동기 작업 수행
    const response = await fetch(&#39;https://api.example.com/data&#39;);
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error.message);
  }
}

// 사용
fetchData();
</code></pre>
<h3 id="비교-promise-vs-async--await"><strong>비교: Promise vs. Async / Await</strong></h3>
<pre><code class="language-jsx">javascriptCopy code
// Promise
fetchData()
  .then(data =&gt; console.log(data))
  .catch(error =&gt; console.error(error.message));

// Async / Await
async function fetchData() {
  try {
    const data = await fetchDataPromise();
    console.log(data);
  } catch (error) {
    console.error(error.message);
  }
}
</code></pre>
<p>Async / Await은 코드를 더 읽기 쉽게 만들어주며, 에러 처리도 일반적인 try...catch 구문을 사용해 보다 명확하게 할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 객체지향 정리]]></title>
            <link>https://velog.io/@jaeha_l/JavaScript-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@jaeha_l/JavaScript-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 19 Dec 2023 18:17:09 GMT</pubDate>
            <description><![CDATA[<h2 id="1-객체와-프로퍼티">1. 객체와 프로퍼티:</h2>
<p>자바스크립트에서 객체는 키와 값의 쌍으로 이루어진 프로퍼티들의 집합입니다. 객체의 프로퍼티는 다양한 데이터 타입의 값을 가질 수 있으며, 함수도 포함될 수 있습니다.</p>
<pre><code class="language-js">const person = {
  name: &#39;hyun&#39;,
  age: 7,
  sayHi: function() {
    console.log(`Hi ${this.name}`);
  }
};</code></pre>
<p>이 예제에서 person 객체는 name과 age라는 프로퍼티를 가지고 있습니다. sayHi는 함수로, 이를 메서드라고 부릅니다.</p>
<h2 id="2-객체-생성-방법">2. 객체 생성 방법:</h2>
<p>객체 리터럴:</p>
<pre><code class="language-js">const obj = {};</code></pre>
<p>Object 생성자 함수:</p>
<pre><code class="language-js">const person = new Object();
person.name = &#39;hyun&#39;;
person.age = 7;
person.sayHi = function() {
  console.log(`Hi ${this.name}`);
};</code></pre>
<p>생성자 함수:</p>
<pre><code class="language-js">function Person(name, age) {
  this.name = name;
  this.age = age;
  this.sayHi = function() {
    console.log(`Hi ${this.name}`);
  };
}

const person1 = new Person(&#39;hyun&#39;, 7);</code></pre>
<p>ES6 클래스:</p>
<pre><code class="language-js">class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHi() {
    console.log(`Hi ${this.name}`);
  }
}

const person1 = new Person(&#39;hyun&#39;, 7);</code></pre>
<h2 id="3-pass-by-value와-pass-by-reference">3. pass-by-value와 pass-by-reference:</h2>
<p>원시 타입(pass-by-value):</p>
<pre><code class="language-js">let a = 5;
let b = a; // 값 복사
b = 10;
console.log(a); // 5 (a는 변경되지 않음)</code></pre>
<p>참조 타입(pass-by-reference):</p>
<pre><code class="language-js">const obj1 = { name: &#39;hyun&#39; };
const obj2 = obj1; // 주소 복사
obj2.name = &#39;yoon&#39;;
console.log(obj1.name); // &#39;yoon&#39; (obj1도 변경됨)</code></pre>
<h2 id="4-es6-클래스">4. ES6 클래스:</h2>
<p>ES6 클래스는 생성자 함수와 프로토타입을 조합한 구조를 보다 간결하게 표현하는 방법입니다.</p>
<pre><code class="language-js">class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHi() {
    console.log(`Hi ${this.name}`);
  }
}

const person1 = new Person(&#39;hyun&#39;, 7);</code></pre>
<p>클래스는 생성자(constructor) 메서드를 포함하고, 다른 메서드들은 클래스 몸체에 정의됩니다. new 키워드를 사용하여 클래스의 인스턴스를 생성할 수 있습니다.</p>
<h2 id="5-클래스와-프로토타입">5. 클래스와 프로토타입:</h2>
<p>클래스를 사용하면 프로토타입 기반의 상속을 더 간단하게 표현할 수 있습니다. 클래스의 메서드는 자동으로 프로토타입에 추가되므로, 인스턴스 간에 메서드를 공유할 수 있습니다.</p>
<pre><code class="language-js">class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a sound`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  bark() {
    console.log(&#39;Woof!&#39;);
  }
}

const dog = new Dog(&#39;Buddy&#39;, &#39;Golden Retriever&#39;);
dog.speak(); // &quot;Buddy makes a sound&quot;
dog.bark();  // &quot;Woof!&quot;</code></pre>
<p>extends 키워드를 사용하여 클래스를 상속할 수 있고, super 키워드를 사용하여 부모 클래스의 생성자를 호출할 수 있습니다.</p>
<p>이렇게 클래스를 사용하면 코드가 더 간결해지고 객체지향 프로그래밍의 개념을 더 직관적으로 표현할 수 있습니다. 클래스를 사용하면 생성자 함수와 프로토타입을 사용하는 것보다 코드가 읽기 쉬워지며, 코드의 재사용성도 높아집니다.</p>
<p>ref : <a href="https://velog.io/@duboo/JavaScript-%EA%B0%9D%EC%B2%B4-Object-%EC%B6%94%EA%B0%80-%EC%A0%95%EB%A6%AC-ES6-%ED%81%B4%EB%9E%98%EC%8A%A4">https://velog.io/@duboo/JavaScript-%EA%B0%9D%EC%B2%B4-Object-%EC%B6%94%EA%B0%80-%EC%A0%95%EB%A6%AC-ES6-%ED%81%B4%EB%9E%98%EC%8A%A4</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-wnifqx7t</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-wnifqx7t</guid>
            <pubDate>Fri, 08 Dec 2023 09:56:01 GMT</pubDate>
            <description><![CDATA[<h2 id="oauth-open-authorization">OAuth (Open Authorization):</h2>
<p>OAuth는 웹 및 애플리케이션에서 안전하게 리소스에 접근하기 위한 개방형 표준 인증 프로토콜입니다. OAuth는 주로 다른 서비스나 애플리케이션에 사용자의 데이터를 제공하거나 사용할 수 있는 권한을 부여하기 위해 사용됩니다. 이로써 사용자는 자신의 계정 정보를 직접 노출하지 않고도 안전하게 다른 서비스를 이용할 수 있게 됩니다.</p>
<p>*<em>핵심 개념: *</em></p>
<ul>
<li><p>리소스 소유자 (Resource Owner): 사용자로서, 자신의 데이터 또는 리소스에 대한 액세스 권한을 부여하는 주체입니다.</p>
</li>
<li><p>클라이언트 (Client): 리소스 소유자의 데이터에 접근하려는 애플리케이션이나 서비스입니다.</p>
</li>
<li><p>인가 서버 (Authorization Server): 클라이언트에게 권한을 부여하고 액세스 토큰을 발급하는 서버입니다.</p>
</li>
<li><p>리소스 서버 (Resource Server): 클라이언트가 접근하려는 실제 데이터 또는 리소스가 있는 서버입니다.</p>
</li>
<li><p>액세스 토큰 (Access Token): 클라이언트가 리소스에 접근하기 위한 권한을 나타내는 토큰입니다.</p>
</li>
</ul>
<p><strong>동작 과정:</strong></p>
<ol>
<li><p>사용자 인증 및 권한 부여: 사용자는 클라이언트에게 권한을 부여하고, 클라이언트는 이를 인증 서버에 등록합니다.</p>
</li>
<li><p>액세스 토큰 요청: 클라이언트는 권한 부여된 사용자를 대신하여 액세스 토큰을 인증 서버에 요청합니다.</p>
</li>
<li><p>액세스 토큰 발급: 인증 서버는 클라이언트에게 액세스 토큰을 발급하고 필요한 경우 리프레시 토큰도 제공합니다.</p>
</li>
<li><p>리소스 접근: 클라이언트는 액세스 토큰을 사용하여 리소스 서버에 접근하고, 권한이 있을 경우 사용자의 데이터를 가져오거나 업데이트합니다.</p>
</li>
</ol>
<p>OAuth는 보안성과 사용자 편의성을 동시에 고려하여 다양한 웹 및 애플리케이션에서 널리 사용되고 있습니다.</p>
<hr>
<h2 id="http-상태-코드">HTTP 상태 코드</h2>
<p>HTTP 상태 코드는 클라이언트가 요청한 HTTP 요청의 결과를 나타냅니다. 각 상태 코드는 세 자리 숫자로 표현되며, 각각의 범주에는 특정한 의미가 있습니다. 주요 상태 코드 범주는 다음과 같습니다:</p>
<pre><code>1xx (Informational): 요청이 받아들여졌거나 프로세스 진행 중임을 나타냅니다.

2xx (Success): 요청이 성공적으로 처리되었음을 나타냅니다.

3xx (Redirection): 추가 조치가 필요함을 나타냅니다. 클라이언트는 추가 동작을 수행해야 합니다.

4xx (Client Error): 클라이언트의 잘못된 요청을 나타냅니다.

5xx (Server Error): 서버가 유효한 요청을 수행하지 못했음을 나타냅니다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-7eu49r0z</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-7eu49r0z</guid>
            <pubDate>Mon, 04 Dec 2023 12:35:36 GMT</pubDate>
            <description><![CDATA[<h3 id="세션-기반-인증session-based-authentication"><strong>세션 기반 인증(Session-Based Authentication):</strong></h3>
<ol>
<li><strong>동작 방식:</strong><ul>
<li>사용자가 로그인하면 서버는 세션을 생성하고 세션 식별자를 사용자 브라우저에게 제공합니다.</li>
<li>이후 모든 요청에서 브라우저는 세션 식별자를 서버에 제공하여 사용자를 식별합니다.</li>
</ul>
</li>
<li><strong>보안성:</strong><ul>
<li>쿠키를 통해 세션 식별자를 저장하므로 쿠키의 안전성이 중요합니다.</li>
<li>쿠키 변조 등에 대비하기 위해 보안 설정이 필요합니다.</li>
</ul>
</li>
<li><strong>서버 부하:</strong><ul>
<li>서버는 사용자의 상태를 유지해야 하므로 메모리를 사용하게 되어 서버 부하가 발생할 수 있습니다.</li>
</ul>
</li>
</ol>
<h3 id="토큰-기반-인증token-based-authentication"><strong>토큰 기반 인증(Token-Based Authentication):</strong></h3>
<ol>
<li><strong>동작 방식:</strong><ul>
<li>사용자가 로그인하면 서버는 토큰을 생성하고 이를 사용자에게 제공합니다.</li>
<li>사용자는 토큰을 갖고 API 요청을 할 때마다 토큰을 서버에 제공하여 인증을 수행합니다.</li>
</ul>
</li>
<li><strong>보안성:</strong><ul>
<li>토큰은 서명되어 있어 변조가 어렵습니다.</li>
<li>HTTPS를 통해 통신할 경우 보안성이 확보됩니다.</li>
</ul>
</li>
<li><strong>서버 부하:</strong><ul>
<li>서버는 상태를 유지하지 않아도 되므로 확장성이 높아집니다.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="jwt-json-web-token"><strong>JWT (JSON Web Token):</strong></h3>
<ul>
<li><strong>토큰 형식:</strong> JSON 형식의 토큰으로, 정보를 안전하게 전송하기 위한 간결하고 자가 수용적인 표준입니다.</li>
<li><strong>구성 요소:</strong><ul>
<li><strong>헤더(Header):</strong> 토큰 유형 및 서명 알고리즘 등의 메타데이터를 포함합니다.</li>
<li><strong>페이로드(Payload):</strong> 클레임(사용자, 권한 등) 정보를 포함합니다.</li>
<li><strong>서명(Signature):</strong> 토큰의 유효성을 확인하기 위한 서명이 포함됩니다.</li>
</ul>
</li>
</ul>
<h3 id="access-token과-refresh-token"><strong>Access Token과 Refresh Token:</strong></h3>
<ul>
<li><strong>Access Token:</strong><ul>
<li>사용자의 인증 및 권한 부여에 사용되는 토큰.</li>
<li>짧은 유효 기간을 가지며, 서버에 자주 새로운 토큰을 발급 받아야 합니다.</li>
</ul>
</li>
<li><strong>Refresh Token:</strong><ul>
<li>장기적으로 사용되는 토큰으로, Access Token을 갱신하는 데 사용됩니다.</li>
<li>더 높은 보안 수준을 필요로 하며, Access Token이 만료되면 Refresh Token을 사용하여 새로운 Access Token을 얻습니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-vzdkxoph</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-vzdkxoph</guid>
            <pubDate>Mon, 27 Nov 2023 08:15:50 GMT</pubDate>
            <description><![CDATA[<h3 id="http-메서드-get과-post"><strong>HTTP 메서드: GET과 POST</strong></h3>
<h3 id="get-메서드">GET 메서드:</h3>
<ul>
<li><strong>개념:</strong> GET은 웹 서버로부터 정보를 요청할 때 사용되는 HTTP 메서드입니다.</li>
<li><strong>데이터 전송:</strong> 주로 URL을 통해 데이터를 전송하며, 쿼리 파라미터를 이용하여 데이터를 전송합니다.</li>
<li><strong>데이터 보안:</strong> URL에 데이터가 노출되므로 보안에 취약하며, 브라우저 히스토리에 기록되므로 민감한 정보에는 적합하지 않습니다.</li>
<li><strong>캐싱 가능:</strong> 브라우저에서 결과를 캐싱할 수 있어, 동일한 요청에 대한 응답을 저장하고 재사용할 수 있습니다.</li>
</ul>
<h3 id="post-메서드">POST 메서드:</h3>
<ul>
<li><strong>개념:</strong> POST는 웹 서버로 데이터를 제출하고자 할 때 사용되는 HTTP 메서드입니다.</li>
<li><strong>데이터 전송:</strong> 주로 HTTP 요청 본문을 통해 데이터를 전송하며, 데이터는 URL에 노출되지 않습니다.</li>
<li><strong>데이터 보안:</strong> POST는 데이터가 요청 본문에 포함되어 있기 때문에 GET보다는 보안적으로 우수합니다.</li>
<li><strong>캐싱 불가능:</strong> 보통 POST 요청은 결과를 캐싱하지 않습니다. 각각의 요청은 서버에서 새로운 데이터를 가져오게 됩니다.</li>
</ul>
<h3 id="데이터-흐름"><strong>데이터 흐름:</strong></h3>
<ol>
<li><strong>GET 메서드의 데이터 흐름:</strong><ul>
<li>클라이언트가 웹 서버에 GET 요청을 보냄.</li>
<li>데이터는 URL의 쿼리 파라미터로 전달됨.</li>
<li>서버는 해당 요청을 처리하고 응답을 클라이언트에게 반환함.</li>
</ul>
</li>
<li><strong>POST 메서드의 데이터 흐름:</strong><ul>
<li>클라이언트가 웹 서버에 POST 요청을 보냄.</li>
<li>데이터는 HTTP 요청 본문에 담겨서 전송됨.</li>
<li>서버는 해당 요청을 처리하고 응답을 클라이언트에게 반환함.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="osi-7계층"><strong>OSI 7계층:</strong></h3>
<p>OSI (Open Systems Interconnection) 모델은 네트워크 프로토콜이 상호 작용하는 방식을 설명하는 7개의 계층으로 이루어진 모델입니다.</p>
<ol>
<li><strong>물리 계층 (Physical Layer):</strong><ul>
<li>전기 신호를 전송하고 기계적 특성을 정의함. (케이블, 허브 등)</li>
</ul>
</li>
<li><strong>데이터 링크 계층 (Data Link Layer):</strong><ul>
<li>프레임 간의 오류 검출 및 흐름 제어를 제공하며, 물리 주소 (MAC 주소)를 사용하여 네트워크 장비를 식별함. (이더넷 등)</li>
</ul>
</li>
<li><strong>네트워크 계층 (Network Layer):</strong><ul>
<li>IP 주소를 사용하여 패킷을 전달하고 라우팅을 수행함. (라우터)</li>
</ul>
</li>
<li><strong>전송 계층 (Transport Layer):</strong><ul>
<li>데이터의 전송을 담당하며, 오류 복구 및 흐름 제어를 수행함. (TCP, UDP)</li>
</ul>
</li>
<li><strong>세션 계층 (Session Layer):</strong><ul>
<li>데이터 교환을 관리하고, 동기화를 제공함. (세션 설정, 유지 및 종료)</li>
</ul>
</li>
<li><strong>표현 계층 (Presentation Layer):</strong><ul>
<li>데이터의 형식을 변환하고, 암호화 및 압축을 수행함. (데이터 형식 변환, 암호화)</li>
</ul>
</li>
<li><strong>응용 계층 (Application Layer):</strong><ul>
<li>최종 사용자에게 서비스를 제공하며, 응용 프로그램 간의 통신을 지원함. (HTTP, SMTP, FTP 등)</li>
</ul>
</li>
</ol>
<p>이 모델은 각 계층이 서로 독립적이고 특정 기능을 수행하며, 계층 간의 통신이 규격화되어 있는 방식으로 네트워크 프로토콜을 설명합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Find the Index of the First Occurrence in a String]]></title>
            <link>https://velog.io/@jaeha_l/Find-the-Index-of-the-First-Occurrence-in-a-String</link>
            <guid>https://velog.io/@jaeha_l/Find-the-Index-of-the-First-Occurrence-in-a-String</guid>
            <pubDate>Sun, 26 Nov 2023 11:16:13 GMT</pubDate>
            <description><![CDATA[<h2 id="problem">Problem</h2>
<p>Given two strings needle and haystack, return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.</p>
<p>Example 1:</p>
<pre><code>Input: haystack = &quot;sadbutsad&quot;, needle = &quot;sad&quot;
Output: 0
Explanation: &quot;sad&quot; occurs at index 0 and 6.
The first occurrence is at index 0, so we return 0.</code></pre><p>Example 2:</p>
<pre><code>Input: haystack = &quot;leetcode&quot;, needle = &quot;leeto&quot;
Output: -1
Explanation: &quot;leeto&quot; did not occur in &quot;leetcode&quot;, so we return -1.</code></pre><p>Constraints:</p>
<pre><code>1 &lt;= haystack.length, needle.length &lt;= 104
haystack and needle consist of only lowercase English characters.</code></pre><h2 id="code">Code</h2>
<pre><code class="language-java">class Solution {
    public int strStr(String haystack, String needle) {
        if (needle.isEmpty()) {
            return 0;
        }

        int n = haystack.length();
        int m = needle.length();

        for (int i = 0; i &lt;= n - m; i++) {
            int j;
            for (j = 0; j &lt; m; j++) {
                if (haystack.charAt(i + j) != needle.charAt(j)) {
                    break;
                }
            }
            if (j == m) {
                return i;
            }
        }

        return -1;
    }
}</code></pre>
<p>이 함수는 간단한 두 개의 중첩된 루프를 사용하여 문자열을 검색한다. 바깥쪽 루프는 haystack을 처음부터 끝까지 이동하며, 안쪽 루프는 needle의 각 문자를 haystack에서 대응하는 위치에 있는 문자와 비교한다. 일치하지 않는 경우 내부 루프를 중단하고 계속 진행한다. 내부 루프가 정상적으로 종료되면, needle이 haystack에서 발견된 것으로 판단하고 해당 위치를 반환한다. 만약 전체 검색에서 일치하는 부분을 찾지 못하면 -1을 반환한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-65l2s6w8</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-65l2s6w8</guid>
            <pubDate>Fri, 27 Oct 2023 09:33:35 GMT</pubDate>
            <description><![CDATA[<h2 id="대용량-트래픽-발생-시-대응-방법"><strong>대용량 트래픽 발생 시 대응 방법</strong></h2>
<ol>
<li><strong>스케일 아웃</strong>: 서버의 수를 늘려서 트래픽을 여러 서버에 분산시키는 것입니다. 클라우드 환경에서는 자동으로 확장되도록 설정할 수 있습니다.</li>
<li><strong>로드 밸런서 사용</strong>: 로드 밸런서를 사용하여 들어오는 트래픽을 여러 서버에 균등하게 분배합니다.</li>
<li><strong>캐싱</strong>: 데이터베이스 요청, 계산 결과 등 자주 사용되는 데이터나 처리 결과를 캐시에 저장하여 빠르게 접근하도록 합니다. Redis나 Memcached 같은 인메모리 데이터 스토어를 사용할 수 있습니다.</li>
<li><strong>CDN 사용</strong>: 정적 리소스(이미지, 스크립트, 스타일시트 등)를 CDN(Content Delivery Network)에 저장하여 사용자에게 더 빠르게 전달될 수 있도록 합니다.</li>
<li><strong>데이터베이스 최적화</strong>: 데이터베이스 쿼리를 최적화하거나, 인덱싱, 분할(sharding) 등의 방법을 사용하여 데이터베이스 성능을 향상시킵니다.</li>
<li><strong>백엔드 최적화</strong>: 비동기 처리, 효율적인 알고리즘 사용, 필요한 작업만 수행하도록 최적화합니다.</li>
<li><strong>모니터링</strong>: 시스템의 상태를 지속적으로 모니터링하여 문제가 발생하면 즉시 대응할 수 있도록 합니다.</li>
</ol>
<hr>
<h2 id="orm-사용시-복잡한-쿼리-대응-방법"><strong>ORM 사용시 복잡한 쿼리 대응 방법</strong></h2>
<ol>
<li><strong>원시 SQL 사용</strong>: ORM의 기능으로 해결하기 어려운 복잡한 쿼리의 경우, 직접 SQL을 작성하여 사용할 수 있습니다.</li>
<li><strong>쿼리 최적화</strong>: ORM에서 제공하는 쿼리 로깅 기능을 사용하여 생성되는 SQL 쿼리를 확인하고, 필요에 따라 최적화합니다.</li>
<li><strong>Lazy Loading &amp; Eager Loading</strong>: ORM의 연관 관계 페치 전략을 적절히 사용하여 성능을 최적화합니다. 필요한 경우 Eager Loading을, 그렇지 않은 경우 Lazy Loading을 사용합니다.</li>
<li><strong>캐싱</strong>: ORM의 2차 캐시 기능을 사용하여 자주 접근되는 데이터를 캐시에 저장하고, 데이터베이스 요청을 최소화합니다.</li>
<li><strong>배치 처리</strong>: ORM의 배치 처리 기능을 사용하여 한 번의 데이터베이스 연결로 여러 개의 쿼리를 한꺼번에 실행합니다.</li>
<li><strong>모델/스키마 리팩토링</strong>: 필요한 경우 데이터베이스 스키마나 모델의 구조를 변경하여 쿼리의 복잡성을 줄일 수 있습니다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-kfp5twm4</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-kfp5twm4</guid>
            <pubDate>Thu, 26 Oct 2023 11:16:02 GMT</pubDate>
            <description><![CDATA[<h2 id="di-와-ioc">DI 와 IOC</h2>
<h3 id="di-dependency-injection">DI (Dependency Injection)</h3>
<ul>
<li><strong>정의</strong>: DI는 의존성 주입이라고도 하며, 객체가 필요로 하는 의존성을 외부에서 주입받는 패턴입니다. 이를 통해 객체 간의 결합도를 낮추고 코드의 재사용성과 테스트 용이성을 향상시킬 수 있습니다.</li>
<li><strong>목적</strong>: 직접적인 의존성 생성 없이 객체의 의존성을 관리하며, 유연한 코드 변경을 가능하게 합니다.</li>
<li><strong>방식</strong>: Constructor Injection, Setter Injection, Method Injection 등 여러 방식이 있습니다.</li>
</ul>
<h3 id="ioc-inversion-of-control">IoC (Inversion of Control)</h3>
<ul>
<li><strong>정의</strong>: IoC는 프로그램의 제어 흐름을 역전시키는 개념입니다. 전통적인 프로그래밍에서는 주 프로그램이 서브 루틴을 호출하지만, IoC에서는 프레임워크나 서비스가 주 프로그램의 흐름을 제어합니다.</li>
<li><strong>목적</strong>: 모듈 간의 결합도를 낮추고 확장성 및 유연성을 향상시키기 위함입니다.</li>
<li><strong>예시</strong>: 스프링 프레임워크의 IoC 컨테이너가 대표적인 예입니다.</li>
</ul>
<hr>
<h2 id="객체-지향-프로그래밍-object-oriented-programming-oop">객체 지향 프로그래밍 (Object-Oriented Programming, OOP)</h2>
<ul>
<li><strong>정의</strong>: 객체 지향 프로그래밍은 데이터와 함수를 객체라는 단위로 묶어서 프로그래밍하는 방식입니다.</li>
<li><strong>기본 원칙</strong>:<ol>
<li><strong>캡슐화 (Encapsulation)</strong>: 데이터와 이 데이터를 처리하는 함수를 하나의 단위(객체)로 묶는 것.</li>
<li><strong>상속 (Inheritance)</strong>: 이미 정의된 클래스의 특성을 다른 클래스가 물려받는 것.</li>
<li><strong>다형성 (Polymorphism)</strong>: 같은 이름의 메서드가 다른 동작을 하는 것.</li>
<li><strong>추상화 (Abstraction)</strong>: 복잡한 시스템을 간단한 개념으로 나타내는 것.</li>
</ol>
</li>
<li><strong>활용 방법</strong>:<ol>
<li><strong>재사용성</strong>: 이미 정의된 클래스나 객체를 다른 프로젝트나 부분에서 재사용할 수 있습니다.</li>
<li><strong>유지 보수</strong>: 객체 지향 프로그래밍은 코드의 모듈화를 통해 유지 보수가 용이합니다.</li>
<li><strong>확장성</strong>: 기존의 코드를 변경하지 않고 기능을 추가하거나 확장할 수 있습니다.</li>
</ol>
</li>
</ul>
<p>간단히 말하면, DI와 IoC는 객체 지향적 설계 원칙을 잘 활용하여 소프트웨어의 유연성, 확장성 및 재사용성을 향상시키는 데 중요한 역할을 합니다. 객체 지향 프로그래밍은 데이터와 기능을 객체라는 단위로 묶어서 소프트웨어를 더욱 모듈화하고 조직화하는 방식입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-y5aonjaz</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-y5aonjaz</guid>
            <pubDate>Thu, 26 Oct 2023 11:12:41 GMT</pubDate>
            <description><![CDATA[<h2 id="tcp-와-udp">TCP 와 UDP</h2>
<h3 id="tcp-transmission-control-protocol"><strong>TCP (Transmission Control Protocol):</strong></h3>
<ol>
<li><strong>연결 지향적</strong>: 통신을 시작하기 전에 연결 설정 과정을 거칩니다.</li>
<li><strong>신뢰성</strong>: 패킷 전송의 확인과 오류 복구 기능을 제공합니다. 손실된 데이터는 재전송됩니다.</li>
<li><strong>흐름 제어</strong>: 데이터의 전송 속도를 제어하여 네트워크의 과부하를 방지합니다.</li>
<li><strong>순서 보장</strong>: 데이터는 전송된 순서대로 도착합니다.</li>
<li><strong>상대적으로 느림</strong>: 위의 특징들 때문에 UDP보다 상대적으로 느릴 수 있습니다.</li>
</ol>
<h3 id="udp-user-datagram-protocol"><strong>UDP (User Datagram Protocol):</strong></h3>
<ol>
<li><strong>연결이 없음</strong>: 통신을 시작하기 전에 별도의 연결 설정 과정 없이 데이터를 바로 전송합니다.</li>
<li><strong>신뢰성 없음</strong>: 패킷의 손실이나 순서 변경 없이 전송되는 것을 보장하지 않습니다.</li>
<li><strong>흐름 제어 없음</strong>: 데이터는 가능한 빠르게 전송됩니다.</li>
<li><strong>빠른 전송</strong>: TCP보다 빠르게 데이터를 전송할 수 있습니다.</li>
<li><strong>상태 정보 없음</strong>: 연결의 상태나 세션 정보를 유지하지 않습니다.</li>
</ol>
<hr>
<h2 id="http와-https의-차이">HTTP와 HTTPS의 차이</h2>
<ol>
<li><strong>보안</strong>:<ul>
<li><strong>HTTP</strong>: 데이터는 암호화되지 않고 평문 형태로 전송됩니다. 이로 인해 중간자 공격(man-in-the-middle attack)에 취약합니다.</li>
<li><strong>HTTPS</strong>: SSL/TLS를 사용하여 데이터를 암호화하여 전송합니다. 이로 인해 데이터의 기밀성 및 무결성이 보장됩니다.</li>
</ul>
</li>
<li><strong>포트 번호</strong>:<ul>
<li><strong>HTTP</strong>: 기본 포트는 80입니다.</li>
<li><strong>HTTPS</strong>: 기본 포트는 443입니다.</li>
</ul>
</li>
<li><strong>성능</strong>:<ul>
<li><strong>HTTP</strong>: 암호화 과정이 없기 때문에 상대적으로 빠릅니다.</li>
<li><strong>HTTPS</strong>: SSL/TLS 암호화 및 인증 과정 때문에 초기 연결 설정에 약간의 지연이 발생할 수 있습니다. 하지만 최근의 최적화 기술로 인해 차이는 점점 줄어들고 있습니다.</li>
</ul>
</li>
<li><strong>인증</strong>:<ul>
<li><strong>HTTPS</strong>는 SSL/TLS 인증서를 사용하여 웹사이트의 신뢰성을 검증합니다. 인증서는 인증 기관(Certificate Authority, CA)에 의해 발급됩니다.</li>
</ul>
</li>
</ol>
<p>간단히 말하면, HTTP는 암호화되지 않은 연결에서 작동하는 반면, HTTPS는 보안 연결에서 작동하며, 데이터의 기밀성, 무결성, 및 인증을 제공합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-77fnjczn</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-77fnjczn</guid>
            <pubDate>Thu, 26 Oct 2023 11:07:47 GMT</pubDate>
            <description><![CDATA[<h2 id="브라우저의-작동-방식">브라우저의 작동 방식</h2>
<ol>
<li><strong>주소 입력</strong>: 사용자가 브라우저의 주소창에 URL을 입력합니다.</li>
<li><strong>DNS 조회</strong>: 주어진 도메인 이름이 IP 주소로 변환됩니다. 만약 브라우저 캐시에 해당 도메인에 대한 IP가 저장되어 있다면 DNS 조회를 건너뛸 수 있습니다.</li>
<li><strong>TCP 연결</strong>: 브라우저는 서버의 IP 주소로 TCP 연결을 설정합니다.</li>
<li><strong>HTTP 요청</strong>: 브라우저는 요청된 리소스에 대한 HTTP 요청을 서버에 전송합니다.</li>
<li><strong>서버 응답</strong>: 서버는 요청된 리소스와 함께 HTTP 응답을 반환합니다.</li>
<li><strong>렌더링</strong>: 브라우저는 HTML 응답을 파싱하고 렌더링 과정을 시작합니다.<ul>
<li><strong>DOM 생성</strong>: HTML 문서를 파싱하여 Document Object Model (DOM) 트리를 생성합니다.</li>
<li><strong>CSSOM 생성</strong>: 외부 CSS 파일 및 인라인 스타일을 파싱하여 CSS Object Model (CSSOM) 트리를 생성합니다.</li>
<li><strong>렌더 트리 생성</strong>: DOM 및 CSSOM을 결합하여 렌더 트리를 생성합니다.</li>
<li><strong>레이아웃</strong>: 각 요소의 크기와 위치를 계산합니다.</li>
<li><strong>페인트</strong>: 렌더링된 요소를 화면에 그립니다.</li>
</ul>
</li>
<li><strong>자바스크립트 실행</strong>: 외부 JavaScript 파일과 인라인 스크립트가 실행됩니다. JavaScript는 DOM 및 CSSOM을 수정할 수 있으므로 렌더링 과정에 영향을 미칠 수 있습니다.</li>
<li><strong>리소스 로딩</strong>: 브라우저는 이미지, CSS 파일, JavaScript 파일 등의 추가 리소스를 요청하고 로드합니다.</li>
</ol>
<hr>
<h2 id="쿠키와-세션">쿠키와 세션</h2>
<p><strong>쿠키 (Cookie):</strong></p>
<ul>
<li>쿠키는 클라이언트(브라우저)에 저장되는 작은 텍스트 파일입니다.</li>
<li>이름, 값, 만료 날짜, 경로, 도메인 등의 정보를 포함할 수 있습니다.</li>
<li>사용자가 웹 사이트를 방문할 때마다 서버에 전송되어 사용자 식별, 세션 트래킹, 정보 저장 등의 용도로 사용됩니다.</li>
</ul>
<p><strong>세션 (Session):</strong></p>
<ul>
<li>세션은 서버 측에서 유지되는 사용자 정보 저장소입니다.</li>
<li>사용자 별로 고유한 세션 ID가 생성되고, 이 ID는 쿠키를 통해 클라이언트에 저장됩니다.</li>
<li>서버는 이 세션 ID를 사용하여 사용자의 세션 데이터에 액세스합니다.</li>
</ul>
<p><strong>쿠키와 세션의 차이점:</strong></p>
<ol>
<li><strong>저장 위치</strong>: 쿠키는 클라이언트에, 세션은 서버에 저장됩니다.</li>
<li><strong>보안</strong>: 쿠키는 클라이언트에 저장되기 때문에 상대적으로 취약할 수 있습니다. 반면, 세션은 서버에 저장되므로 더 안전합니다.</li>
<li><strong>생명주기</strong>: 쿠키는 만료 날짜를 설정할 수 있습니다. 만료 날짜가 지나면 삭제됩니다. 세션은 사용자가 브라우저를 종료할 때 일반적으로 종료됩니다 (하지만 세션 타임아웃 설정으로 변경할 수 있습니다).</li>
<li><strong>데이터 크기</strong>: 쿠키는 데이터 크기에 제한이 있습니다 (일반적으로 4KB). 세션은 서버의 메모리나 디스크에 저장되므로 더 큰 데이터를 저장할 수 있습니다.</li>
</ol>
<p>간단히 말하면, 쿠키는 브라우저에 저장되는 작은 데이터 조각이며, 세션은 서버에 저장되는 사용자 정보입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-hs8zaor5</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-hs8zaor5</guid>
            <pubDate>Thu, 26 Oct 2023 11:02:40 GMT</pubDate>
            <description><![CDATA[<h2 id="http-메서드">HTTP 메서드</h2>
<ol>
<li><strong>GET</strong>: 서버에서 리소스를 검색하기 위해 사용됩니다. GET 요청은 데이터를 검색하는 데만 사용되어야 하며, 서버의 데이터를 변경하거나 작업의 부작용을 발생시켜서는 안됩니다.</li>
<li><strong>POST</strong>: 새로운 리소스를 생성하거나 서버에 데이터를 제출하기 위해 사용됩니다.</li>
<li><strong>PUT</strong>: 리소스를 갱신하거나 대체하기 위해 사용됩니다. 지정된 URI에 리소스가 없으면 새 리소스가 생성될 수 있습니다.</li>
<li><strong>DELETE</strong>: 특정 리소스를 삭제하기 위해 사용됩니다.</li>
<li><strong>PATCH</strong>: 리소스의 부분적인 수정을 위해 사용됩니다.</li>
<li><strong>HEAD</strong>: GET과 유사하지만, 리소스의 본문(body)을 반환하지 않습니다. 리소스의 메타데이터만 요청됩니다.</li>
<li><strong>OPTIONS</strong>: 대상 리소스에 대한 통신 옵션을 설명하는 데 사용됩니다.</li>
</ol>
<hr>
<h2 id="corscross-origin-resource-sharing">CORS(Cross Origin Resource Sharing)</h2>
<p>CORS는 추가적인 HTTP 헤더를 사용하여 한 출처에서 실행되는 웹 페이지가 다른 출처의 리소스에 접근할 수 있게 허용하는 메커니즘입니다.</p>
<p>웹 페이지의 스크립트는 동일 출처 정책(Same-Origin Policy)으로 인해 다른 출처의 리소스에 접근할 수 없습니다. CORS는 이러한 제한을 안전하게 완화하기 위한 방법을 제공합니다.</p>
<p>서버는 <strong><code>Access-Control-Allow-Origin</code></strong>과 같은 CORS 관련 헤더를 포함하여 응답하면, 웹 브라우저는 이 헤더를 확인하여 해당 리소스에 대한 접근을 허용할지 여부를 결정합니다.</p>
<p>예를 들어, 서버가 <strong><code>Access-Control-Allow-Origin: *</code></strong> 헤더를 포함하여 응답하면, 모든 출처에서 해당 서버의 리소스에 접근이 허용됩니다. 반면, <strong><code>Access-Control-Allow-Origin: https://example.com</code></strong>과 같이 특정 도메인을 지정하면, 오직 <strong><code>https://example.com</code></strong> 도메인에서만 리소스에 접근이 가능합니다.</p>
<p>CORS는 웹 애플리케이션의 보안을 강화하기 위한 중요한 메커니즘입니다. 잘못 구성된 CORS 설정은 보안 취약점을 초래할 수 있으므로 주의가 필요합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-dofwb6ea</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-dofwb6ea</guid>
            <pubDate>Thu, 26 Oct 2023 10:57:19 GMT</pubDate>
            <description><![CDATA[<h2 id="rdbms의-정규화">RDBMS의 정규화</h2>
<p>정규화는 데이터의 중복을 최소화하고, 데이터의 무결성을 보장하기 위해 RDBMS에서 데이터를 구조화하는 과정입니다. 정규화의 목표는 각 정보 항목이 한 번만 나타나도록 테이블을 구성하는 것입니다.</p>
<p>정규화의 주요 단계는 다음과 같습니다:</p>
<ol>
<li><strong>제1정규형 (1NF)</strong>: 각 컬럼의 값이 원자적(분할 불가능)으로, 각 레코드가 유일한 값을 가지도록 합니다.</li>
<li><strong>제2정규형 (2NF)</strong>: 1NF를 만족하면서 부분적 함수 종속성이 없도록 합니다. 즉, 테이블의 모든 속성이 기본 키에 완전히 함수적으로 종속되어야 합니다.</li>
<li><strong>제3정규형 (3NF)</strong>: 2NF를 만족하면서 이행적 함수 종속성이 없도록 합니다.</li>
<li>그 이후에도 BCNF, 4NF, 5NF 등의 정규형이 있지만, 일반적인 데이터베이스 설계에서는 3NF까지의 정규화를 주로 고려합니다.</li>
</ol>
<hr>
<h2 id="primary-key-foreign-key">Primary Key, Foreign Key</h2>
<h3 id="primary-key-기본-키"><strong>Primary Key (기본 키):</strong></h3>
<p>Primary Key는 테이블의 각 레코드를 고유하게 식별하는 하나 이상의 속성(또는 필드)의 조합입니다. 기본 키의 특징은 다음과 같습니다:</p>
<ol>
<li><strong>유일성</strong>: 기본 키로 선택된 속성의 값은 테이블 내에서 유일해야 합니다.</li>
<li><strong>불변성</strong>: 기본 키의 값은 시간이 지나도 변경될 수 없습니다.</li>
<li><strong>NOT NULL</strong>: 기본 키는 null 값을 허용하지 않습니다.</li>
</ol>
<h3 id="foreign-key-외래-키"><strong>Foreign Key (외래 키):</strong></h3>
<p>Foreign Key는 한 테이블의 속성(또는 속성의 집합)이 다른 테이블의 Primary Key를 참조하는 것입니다. 외래 키의 주요 목적은 테이블 간의 관계를 정의하고 데이터의 무결성을 보장하는 것입니다. 외래 키의 특징은 다음과 같습니다:</p>
<ol>
<li><strong>참조 무결성</strong>: 외래 키의 값은 참조하는 테이블의 기본 키에 존재하는 값이어야 하거나 null이어야 합니다.</li>
<li>외래 키를 사용하여 테이블 간의 관계 (일대일, 일대다, 다대다)를 표현할 수 있습니다.</li>
</ol>
<p>예를 들어, &#39;주문&#39; 테이블과 &#39;고객&#39; 테이블이 있을 때, &#39;주문&#39; 테이블의 &#39;고객ID&#39;는 &#39;고객&#39; 테이블의 &#39;고객ID&#39;를 참조하는 외래 키가 될 수 있습니다. 이를 통해 주문이 특정 고객에게 속하는 것을 나타낼 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-nsxewet8</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-nsxewet8</guid>
            <pubDate>Thu, 26 Oct 2023 10:52:50 GMT</pubDate>
            <description><![CDATA[<h2 id="nosql과-rdbms">NoSQL과 RDBMS</h2>
<h3 id="nosql-not-only-sql">NoSQL (Not Only SQL):</h3>
<p><strong>특징</strong>:</p>
<ol>
<li><strong>스키마 없음</strong>: 데이터는 키-값 쌍, JSON, 그래프 등 다양한 방식으로 저장될 수 있습니다.</li>
<li><strong>수평적 확장</strong>: 일반적으로 분산 시스템에서 운영되어 빠른 읽기/쓰기 성능을 제공합니다.</li>
<li><strong>유연한 데이터 모델</strong>: 빠르게 변하는 데이터 요구사항에 적응하기 쉽습니다.</li>
</ol>
<p><strong>장점</strong>:</p>
<ul>
<li><strong>대량의 데이터 처리</strong>: 대량의 읽기/쓰기 요구 사항에 적합합니다.</li>
<li><strong>유연성</strong>: 스키마 변경 없이 데이터 구조를 쉽게 변경할 수 있습니다.</li>
<li><strong>수평적 확장</strong>: 추가 서버를 통해 쉽게 확장 가능합니다.</li>
</ul>
<p><strong>단점</strong>:</p>
<ul>
<li><strong>일관성</strong>: 대부분의 NoSQL 데이터베이스는 ACID 트랜잭션의 완전한 일관성을 포기합니다.</li>
<li><strong>표준 부재</strong>: 다양한 NoSQL 데이터베이스 중 표준화된 쿼리 언어나 인터페이스가 없습니다.</li>
</ul>
<h3 id="rdbms-relational-database-management-system">RDBMS (Relational Database Management System):</h3>
<p><strong>특징</strong>:</p>
<ol>
<li><strong>스키마 기반</strong>: 데이터는 잘 정의된 스키마에 따라 테이블에 저장됩니다.</li>
<li><strong>ACID 트랜잭션</strong>: 모든 트랜잭션은 원자성, 일관성, 고립성, 지속성을 보장합니다.</li>
<li><strong>SQL 쿼리</strong>: 데이터 조회와 관리를 위해 표준화된 SQL 언어를 사용합니다.</li>
</ol>
<p><strong>장점</strong>:</p>
<ul>
<li><strong>데이터 무결성</strong>: 스키마와 ACID 트랜잭션으로 데이터 무결성이 보장됩니다.</li>
<li><strong>표준화된 쿼리</strong>: SQL은 잘 알려진 표준 쿼리 언어로 다양한 응용 프로그램에서 지원됩니다.</li>
<li><strong>복잡한 쿼리</strong>: 조인, 서브쿼리 등의 복잡한 쿼리를 지원합니다.</li>
</ul>
<p><strong>단점</strong>:</p>
<ul>
<li><strong>확장성</strong>: 대규모 트래픽과 데이터에 대한 수직적 확장이 어려울 수 있습니다.</li>
<li><strong>유연성</strong>: 스키마 변경이 어렵고, 데이터 구조의 변경에 대응하기 힘듭니다.</li>
</ul>
<hr>
<h2 id="mvc-model-view-controller-패턴"><strong>MVC (Model-View-Controller) 패턴:</strong></h2>
<p>MVC는 소프트웨어 설계 패턴으로, 응용 프로그램을 세 가지 주요 구성 요소로 분리하여 관리합니다.</p>
<ol>
<li><strong>Model (모델)</strong>: 애플리케이션의 비즈니스 로직과 데이터를 처리합니다. 데이터베이스와의 상호 작용 및 데이터 처리를 담당합니다.</li>
<li><strong>View (뷰)</strong>: 사용자에게 보여지는 인터페이스 요소입니다. 사용자가 보고 상호 작용하는 UI 부분을 나타냅니다.</li>
<li><strong>Controller (컨트롤러)</strong>: 사용자의 입력을 받아 모델과 뷰에 전달합니다. 사용자의 요청을 해석하고 적절한 응답을 생성하는 데 중점을 둡니다.</li>
</ol>
<p><strong>장점</strong>:</p>
<ul>
<li><strong>분리의 원칙</strong>: UI와 비즈니스 로직이 분리되어 있어 개발과 유지 보수가 용이합니다.</li>
<li><strong>재사용성</strong>: 모델, 뷰, 컨트롤러는 독립적으로 재사용이 가능합니다.</li>
<li><strong>확장성</strong>: 각 구성 요소를 독립적으로 확장할 수 있습니다.</li>
</ul>
<p><strong>단점</strong>:</p>
<ul>
<li><strong>복잡성</strong>: 초기 설계에서 복잡성이 증가할 수 있습니다.</li>
<li><strong>학습 곡선</strong>: 패턴을 처음 접하면 이해하는 데 시간이 걸릴 수 있습니다.</li>
</ul>
<p>MVC 패턴은 특히 웹 응용 프로그램과 같은 사용자 인터페이스 중심의 응용 프로그램을 개발할 때 유용합니다. 이 패턴을 사용하면 로직, UI, 사용자 입력 처리를 분리하여 관리할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CS TIL]]></title>
            <link>https://velog.io/@jaeha_l/CS-TIL-26iaq7ob</link>
            <guid>https://velog.io/@jaeha_l/CS-TIL-26iaq7ob</guid>
            <pubDate>Thu, 26 Oct 2023 10:47:03 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-bean-container-생성부터-스프링-종료까지의-사이클">Spring bean container 생성부터 스프링 종료까지의 사이클</h2>
<h3 id="postconstruct-predestroy-어노테이션의-역할">@PostConstruct, @PreDestroy 어노테이션의 역할</h3>
<p>스프링 IoC컨테이너가 생성되면 Component-Scan으로 Bean을 등록합니다. IoC컨테이너에서 의존성을 주입합니다. 스프링은 Bean에게 콜백 메서드를 통해 초기화 시점을 알려주며 스프링 컨테이너가 종료되기 직전에도 소멸 콜백 메서드를 통해 소멸 시점을 알려주고 스프링이 종료됩니다. 스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 관리하는데 그 중 최신 스프링에서 가장 권장하는 방법이 @PostConstruct, @PreDestroy 어노테이션입니다. 위 어노테이션만 붙이면 자동으로 초기화 콜백, 소멸전 콜백이 되기 때문에 매우 편리하게 사용이 가능합니다. 스프링이 아닌 다른 컨테이너에서도 동작한다는 장점이 있지만 외부 라이브러리에는 적용하지 못합니다 @PostConstruct 의존관계 주입이 완료되면 호출됩니다 (초기화 콜백) @PreDestroy 빈이 소멸되기 직전에 호출됩니다 (소멸전 콜백)</p>
<hr>
<h2 id="aop-interceptor-filter-의-차이점-request가-들어올-때-거치는-순서-각-역할들의-장점">AOP, Interceptor, Filter 의 차이점, Request가 들어올 때 거치는 순서, 각 역할들의 장점</h2>
<p>Request가 들어올때 거치는 순서 : Filter -&gt; Interceptor -&gt; AOP</p>
<p>첫 번째로 Filter는 DispatcherServlet 이전에 동작하는데 Request와 Response를 정제해주는 역할을 합니다. 이후 서블렛이 동작하고 컨트롤러에 도달하기 전 Interceptor가 동작하여 스프링 컨텍스트 내부에서 컨트롤러에 관한 요청과 응답에 대해 처리합니다. AOP는 OOP를 보완하기 위해 나타난 개념인데 OOP에서 중복을 줄일 수 없는 코드를 모듈화 해서 분리하는 개념입니다. Interceptor, Filter와 달리 메소드의 전후지점에서 다양하게 사용이 가능하고, 어노테이션 파라미터 등 다양한 방법으로 대상을 지정할 수 있기에 로깅, 에러처리와 같이 비즈니스 로직에서 세밀한 조정이 필요할 때 주로 사용합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[EC2에서 Redis 서버를 구동하는 방법]]></title>
            <link>https://velog.io/@jaeha_l/EC2%EC%97%90%EC%84%9C-Redis-%EC%84%9C%EB%B2%84%EB%A5%BC-%EA%B5%AC%EB%8F%99%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@jaeha_l/EC2%EC%97%90%EC%84%9C-Redis-%EC%84%9C%EB%B2%84%EB%A5%BC-%EA%B5%AC%EB%8F%99%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sun, 22 Oct 2023 15:56:46 GMT</pubDate>
            <description><![CDATA[<h2 id="1-ec2-인스턴스-생성">1. EC2 인스턴스 생성:</h2>
<ul>
<li>AWS Management Console에 로그인 후 EC2 서비스로 이동한다.</li>
<li>&quot;인스턴스 시작&quot;을 클릭한다.</li>
<li>원하는 AMI를 선택한다. 예를 들면, Ubuntu Server LTS 등. -&gt; Ubuntu 로 했다.</li>
<li>원하는 인스턴스 유형을 선택하고 &quot;다음&quot;을 계속 클릭한다.</li>
<li>보안 그룹에서 TCP 포트 6379를 허용하여 Redis 클라이언트가 연결할 수 있도록 한다.</li>
<li>인스턴스를 시작한다.</li>
</ul>
<h2 id="2-탄력적-ip-설정">2. 탄력적 IP 설정:</h2>
<p>인스턴스를 재시작해도 IP 주소가 바뀌지 않는다.</p>
<ul>
<li>EC2 대시보드 왼쪽 메뉴에서 &quot;탄력적 IP&quot;를 선택한다.</li>
<li>&quot;새 주소 할당&quot;을 클릭하여 새 IP 주소를 생성한다.</li>
<li>생성된 IP 주소를 선택하고 &quot;작업&quot; -&gt; &quot;주소 연결&quot;을 선택하여 EC2 인스턴스와 연결한다.</li>
</ul>
<h2 id="3-ubuntu에-redis-설치">3. Ubuntu에 Redis 설치:</h2>
<ul>
<li>EC2 인스턴스에 SSH로 연결한다.<pre><code class="language-bash">ssh -i `{pem key}` ubuntu@`{EC2 인스턴스의 IP or DNS}`</code></pre>
</li>
<li>Redis를 설치한다.</li>
</ul>
<pre><code class="language-bash">sudo apt-get update
sudo apt-get install redis-server</code></pre>
<ul>
<li>Redis 설정을 변경한다:</li>
</ul>
<pre><code class="language-bash">sudo vim /etc/redis/redis.conf</code></pre>
<ul>
<li><p>bind 설정을 0.0.0.0으로 변경한다. -&gt; 외부에서 모든 접속 허용</p>
<pre><code class="language-bash"># 기존의 bind 설정을 찾아서 아래와 같이 수정한다.
bind 0.0.0.0</code></pre>
</li>
<li><p>timezone을 Asia/Seoul로 설정한다.</p>
<pre><code class="language-bash">기존 미국 시간대를 한국 시간으로 변경 (KST)
</code></pre>
</li>
</ul>
<p>sudo rm /etc/localtime
sudo ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime</p>
<pre><code>- maxmemory 설정은 기본값을 사용한다.
프리티어 1GB 

- maxmemory-policy를 allkeys-lru로 설정하여 오래된 키부터 삭제되도록 한다.
```bash
# redis.conf 파일 내에서 아래와 같이 설정을 추가하거나 수정한다.
maxmemory-policy allkeys-lru</code></pre><ul>
<li><p>필요한 경우 스왑 메모리 설정을 조절한다.</p>
</li>
<li><p>requirepass 로 Redis 비밀번호를 설정한다.</p>
</li>
</ul>
<pre><code class="language-bash"># redis.conf 파일 내에서 아래와 같이 설정을 추가하거나 수정헌다.
requirepass `{PASSWORD}`</code></pre>
<h2 id="4-redis-서버-시작">4. Redis 서버 시작:</h2>
<pre><code class="language-bash">sudo service redis-server restart</code></pre>
<h2 id="5-인스턴스-삭제-및-탄력적-ip-해제">5. 인스턴스 삭제 및 탄력적 IP 해제:</h2>
<ul>
<li><p>EC2 인스턴스를 삭제하려면 EC2 대시보드에서 해당 인스턴스를 선택하고 &quot;작업&quot; -&gt; &quot;인스턴스 상태&quot; -&gt; &quot;종료&quot;를 선택한다.</p>
</li>
<li><p>탄력적 IP를 해제하려면 EC2 대시보드에서 &quot;탄력적 IP&quot;를 선택하고 사용하지 않는 IP 주소를 선택한 다음 &quot;작업&quot; -&gt; &quot;주소 해제&quot;를 선택한다.</p>
</li>
</ul>
<hr>
<h2 id="redis-원격-접속">Redis 원격 접속</h2>
<pre><code class="language-bash">redis-cli -h `{EC2 인스턴스의 IP or DNS}` -p 6379 -a `{redis.conf에 설정한 비밀번호}`</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Week 11 WIL]]></title>
            <link>https://velog.io/@jaeha_l/Week-11-WIL</link>
            <guid>https://velog.io/@jaeha_l/Week-11-WIL</guid>
            <pubDate>Sun, 22 Oct 2023 15:21:41 GMT</pubDate>
            <description><![CDATA[<p>Redis 배포 서버 오류 해결
남은 3주 열심히 해서 정진하자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Soft Delete / Hard Delete]]></title>
            <link>https://velog.io/@jaeha_l/Soft-Delete-Hard-Delete</link>
            <guid>https://velog.io/@jaeha_l/Soft-Delete-Hard-Delete</guid>
            <pubDate>Wed, 18 Oct 2023 17:30:25 GMT</pubDate>
            <description><![CDATA[<p>웹소켓을 활용하여 실시간 채팅 기능을 구현하려는 계획 중에, 채팅 메시지의 효율적인 관리와 저장 전략의 중요성을 크게 느꼈다. 이러한 고민 속에서, 어떠한 데이터베이스를 선택할지는 매우 핵심적인 고려사항이다. 소프트 딜리트와 하드 딜리트의 전략에 대해 찾아보며 그 장단점과 각각의 특징에 대해 세밀하게 알아보고 있다. 또한, 다양한 데이터베이스 유형 별로 어떻게 소프트 딜리트를 효과적으로 구현할 수 있을지에 대한 검토를 진행 중이다. 이러한 탐색과 연구를 통해 웹소켓 기반의 실시간 채팅 서비스에서 데이터 관리의 복잡성과 그 중요성을 절감하게 되었다.</p>
<hr>
<h2 id="1-soft-delete논리-삭제와-hard-delete물리-삭제">1. Soft delete(논리 삭제)와 Hard delete(물리 삭제)</h2>
<h3 id="soft-delete논리-삭제">Soft delete(논리 삭제):</h3>
<p>실제 데이터를 데이터베이스에서 삭제하지 않고, 삭제되었다는 상태만을 표시하는 방법이다. 일반적으로 데이터의 속성 중 하나를 사용하여 데이터가 삭제되었는지 여부를 나타내며, 삭제 플래그 또는 삭제 시간 등의 칼럼을 사용한다.</p>
<h3 id="hard-delete물리-삭제">Hard delete(물리 삭제):</h3>
<p>실제로 데이터를 데이터베이스에서 완전히 삭제하는 것이다.</p>
<hr>
<h2 id="2-soft-delete논리-삭제를-사용하는-이유-및-장점">2. Soft delete(논리 삭제)를 사용하는 이유 및 장점</h2>
<ol>
<li><p><code>데이터 복구의 용이성</code>: 데이터를 실수로 삭제한 경우, 논리 삭제된 데이터는 쉽게 복구할 수 있다.</p>
</li>
<li><p><code>데이터의 무결성 유지</code>: 데이터베이스의 관계 무결성을 유지하면서도 데이터를 안전하게 &#39;삭제&#39;할 수 있다.</p>
</li>
<li><p><code>변경 이력 추적</code>: 데이터의 변경 이력을 추적하고 싶을 때 논리 삭제는 매우 유용하다다. 삭제된 데이터에 대한 이력도 남겨져 있기 때문에, 언제 어떤 데이터가 삭제되었는지 추적이 가능하다.</p>
</li>
</ol>
<hr>
<h2 id="3-soft-delete의-주된-단점은-무엇인가">3. Soft delete의 주된 단점은 무엇인가?</h2>
<ol>
<li><p><code>데이터베이스 용량 증가</code>: 삭제되지 않은 데이터는 계속 데이터베이스에 저장되기 때문에, 물리적인 저장 공간이 점차 증가한다.</p>
</li>
<li><p><code>쿼리 복잡성</code>: 데이터를 조회할 때마다 삭제되지 않은 데이터만을 조회하기 위한 추가적인 조건이 필요하다.</p>
</li>
<li><p><code>성능 저하 가능성</code>: 특히 대량의 데이터가 논리적으로 삭제된 경우, 해당 데이터를 매번 건너뛰어야 하므로 성능에 영향을 줄 수 있다.</p>
</li>
</ol>
<hr>
<h2 id="4-그럼-언제-논리-삭제를-사용하고-물리삭제를-사용해야-할까">4. 그럼 언제 논리 삭제를 사용하고 물리삭제를 사용해야 할까?</h2>
<ul>
<li>논리 삭제 사용 시기:<ul>
<li>데이터 복구의 필요성이 높을 때</li>
<li>데이터의 변경 이력을 추적하고 싶을 때</li>
<li>참조 무결성 문제를 피하고 싶을 때</li>
</ul>
</li>
<li>물리 삭제 사용 시기:<ul>
<li>데이터베이스의 저장 공간을 효율적으로 활용하고 싶을 때</li>
<li>성능 최적화가 필요한 경우</li>
</ul>
</li>
</ul>
<hr>
<h2 id="5-어떤-데이터베이스-선택해야-할까">5. 어떤 데이터베이스 선택해야 할까?</h2>
<p><code>관계형 데이터베이스(RDBMS)</code>: 관계형 데이터베이스는 트랜잭션과 데이터 무결성이 중요한 경우에 유리하다. RDBMS에서는 Foreign Key 제약 조건 등으로 인해 물리 삭제가 복잡할 수 있으므로 Soft delete가 주로 사용된다.</p>
<p><code>NoSQL 데이터베이스</code>: NoSQL 데이터베이스는 확장성과 높은 처리량을 중요시하는 경우에 유리하다. 이러한 데이터베이스는 스키마가 유연하므로 물리 삭제와 논리 삭제를 선택하는 기준은 주로 성능과 데이터 복구 요구사항에 기반한다.</p>
<p><code>시계열 데이터베이스</code>: 이러한 데이터베이스는 시간 순서로 데이터를 저장하는 것이 특징이다. 논리 삭제는 시간의 연속성을 깨뜨릴 수 있으므로, 해당 데이터베이스에서는 물리 삭제가 더 일반적일 수 있다.</p>
<hr>
<h2 id="6-spring에서-soft-delete-사용-시-많이-사용하는-방법들">6. Spring에서 Soft delete 사용 시 많이 사용하는 방법들</h2>
<ul>
<li><code>@SQLDelete와 @Where 어노테이션 사용</code>: Hibernate에서 제공하는 이 어노테이션들을 사용하여 논리 삭제를 구현할 수 있다.</li>
<li><code>JPA Entity Listener 사용</code>: @PreRemove 어노테이션과 같은 JPA Entity Lifecycle 이벤트를 활용하여 논리 삭제를 처리한다.</li>
<li><code>QueryDSL과 같은 쿼리 라이브러리 활용</code>: 특정 조건(예: isDeleted = false)을 기본 조건으로 쿼리를 작성할 수 있게 도와준다.</li>
</ul>
<h3 id="sqldelete와-where-사용-예제">@SQLDelete와 @Where 사용 예제</h3>
<p>가정: deleted라는 boolean 필드를 가진 Book 엔터티가 있다고 가정했다.</p>
<pre><code class="language-java">@Entity
@Table(name = &quot;books&quot;)
@SQLDelete(sql = &quot;UPDATE books SET deleted = true WHERE id = ?&quot;)
@Where(clause = &quot;deleted = false&quot;)
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    private boolean deleted;

    // getters, setters, etc.
}

</code></pre>
<p><code>@SQLDelete</code>: 해당 엔터티에 대한 삭제 작업이 발생할 때 실행될 SQL 문을 지정한다. 여기서는 deleted를 true로 설정하는 것으로 논리 삭제를 수행하도록 지정했다.</p>
<p><code>@Where</code>: 해당 엔터티를 조회할 때 적용되는 조건을 지정한다. 여기서는 삭제되지 않은(deleted = false) 데이터만을 기본적으로 조회하도록 설정했다.</p>
<p>이렇게 설정하면, Book 엔터티에 대한 삭제 작업은 실제로 데이터를 삭제하지 않고 deleted 필드를 true로 설정하는 논리 삭제로 수행된다. 또한, Book 엔터티를 조회할 때는 기본적으로 삭제되지 않은 데이터만을 조회하게 된다.</p>
<hr>
<h2 id="정리">정리</h2>
<blockquote>
<p>데이터의 중요성, 복구 요구사항, 성능, 저장 공간 및 관리 복잡성 등의 여러 측면을 고려하여 애플리케이션에 가장 적합한 삭제 전략을 선택해야 한다. Soft delete는 데이터의 안전성 및 추적 가능성을 제공하는 반면, Hard delete는 물리적 저장 공간 및 성능 최적화에 이점이 있을 수 있다. 데이터베이스의 선택도 삭제 전략의 선택에 큰 영향을 미칠 수 있다. 따라서 애플리케이션의 요구 사항과 데이터베이스의 특성을 꼼꼼히 검토하여 가장 적합한 전략을 선정해야 한다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>