<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>squee_z_e.log</title>
        <link>https://velog.io/</link>
        <description>멋있는 사람이 되는 게 꿈입니다</description>
        <lastBuildDate>Thu, 01 Oct 2026 02:00:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>squee_z_e.log</title>
            <url>https://velog.velcdn.com/images/squee_z_e/profile/6f2a4f45-70ce-4328-8a53-2e9d94c8670c/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. squee_z_e.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/squee_z_e" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[JS) 언어별 특성]]></title>
            <link>https://velog.io/@squee_z_e/JS-20261001-2</link>
            <guid>https://velog.io/@squee_z_e/JS-20261001-2</guid>
            <pubDate>Thu, 01 Oct 2026 02:00:25 GMT</pubDate>
            <description><![CDATA[<h1 id="javascript-·-java-·-python-·-c-핵심-특성-비교">JavaScript · Java · Python · C++ 핵심 특성 비교</h1>
<p>프로그래밍 언어는 문법만 다른 것이 아니라 <strong>타입을 다루는 방식, 객체 모델, 메모리 관리 방식, 함수 호출 방식, 동시성 모델</strong>에서 큰 차이가 있다.
JavaScript, Java, Python, C++을 같은 기준으로 비교하면 각 언어가 왜 특정 분야와 프레임워크에서 많이 사용되는지도 함께 이해할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/12753ffa-e654-4c30-9722-0264aeea0759/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/a0800955-ea67-49bf-a9ce-206571bcfb16/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/52cbbfe7-6399-43c7-8953-e00f65475cd6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/767da9cf-3079-4c0f-b27f-f085619b5ab2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/0e4ceb04-505e-4ebb-8961-0d5770ef05c5/image.png" alt=""></p>
<h1 id="1-전체-특징-한눈에-보기">1. 전체 특징 한눈에 보기</h1>
<table>
<thead>
<tr>
<th>구분</th>
<th>JavaScript</th>
<th>Java</th>
<th>Python</th>
<th>C++</th>
</tr>
</thead>
<tbody><tr>
<td>타입</td>
<td>동적 타입</td>
<td>정적 타입</td>
<td>동적 타입</td>
<td>정적 타입</td>
</tr>
<tr>
<td>객체 모델</td>
<td>Prototype 기반</td>
<td>Class 기반</td>
<td>Class 기반·동적</td>
<td>Class 기반</td>
</tr>
<tr>
<td>메모리 관리</td>
<td>GC</td>
<td>GC</td>
<td>Reference Counting + GC</td>
<td>RAII / 직접 관리</td>
</tr>
<tr>
<td>인자 전달</td>
<td>Call by Value</td>
<td>Call by Value</td>
<td>Object Sharing</td>
<td>Value / Reference 선택</td>
</tr>
<tr>
<td>실행 환경</td>
<td>Browser / Node.js</td>
<td>JVM</td>
<td>CPython</td>
<td>Native</td>
</tr>
<tr>
<td>실행 방식</td>
<td>JIT 중심</td>
<td>Bytecode + JIT</td>
<td>Bytecode + VM</td>
<td>AOT Compile</td>
</tr>
<tr>
<td>동시성 특징</td>
<td>Event Loop 중심</td>
<td>Multi Thread</td>
<td>GIL 영향</td>
<td>Multi Thread</td>
</tr>
<tr>
<td>함수</td>
<td>일급 객체</td>
<td>Lambda / Functional Interface</td>
<td>일급 객체</td>
<td>Lambda / 함수 포인터</td>
</tr>
<tr>
<td>대표 환경</td>
<td>React / Node.js</td>
<td>Spring</td>
<td>Django / FastAPI</td>
<td>System / Game / Embedded</td>
</tr>
</tbody></table>
<hr>
<h1 id="2-실행-방식과-runtime">2. 실행 방식과 Runtime</h1>
<p>언어마다 작성한 코드가 CPU에서 실행되기까지의 과정이 다르다.</p>
<pre><code class="language-text">C++

Source
  ↓ Compiler
Machine Code
  ↓
CPU</code></pre>
<pre><code class="language-text">Java

Source
  ↓ javac
Bytecode
  ↓
JVM
  ↓ JIT
Machine Code</code></pre>
<pre><code class="language-text">Python

Source
  ↓
Bytecode
  ↓
Python VM</code></pre>
<pre><code class="language-text">JavaScript

Source
  ↓
JS Engine
  ↓
Bytecode / JIT
  ↓
Machine Code</code></pre>
<h3 id="javascript">JavaScript</h3>
<p>브라우저나 Node.js 같은 Runtime에서 실행된다. V8 같은 JavaScript Engine이 코드를 해석하고 필요에 따라 JIT 컴파일을 이용해 최적화한다.</p>
<p>브라우저에서는 JavaScript Engine뿐 아니라 DOM, Timer, <code>fetch</code> 같은 Web API도 함께 사용한다.</p>
<h3 id="java">Java</h3>
<p>소스 코드를 먼저 Bytecode로 컴파일하고 JVM이 이를 실행한다.</p>
<p>JVM은 실행 중 자주 사용되는 코드를 JIT 컴파일하여 Native Code로 최적화한다. JVM이 메모리 관리, Garbage Collection, Thread 관리도 담당한다.</p>
<h3 id="python">Python</h3>
<p>일반적인 CPython 구현에서는 소스 코드가 Bytecode로 변환되고 Python Virtual Machine이 이를 실행한다.</p>
<p>흔히 인터프리터 언어라고 부르지만 내부적으로 Bytecode 단계가 존재한다.</p>
<h3 id="c">C++</h3>
<p>실행 전에 Native Machine Code로 컴파일된다.</p>
<p>별도의 JVM이나 Python VM 없이 OS와 CPU에서 직접 실행되기 때문에 성능과 제어력이 높다.</p>
<h3 id="핵심">핵심</h3>
<pre><code class="language-text">Compile Time
→ 프로그램 실행 전 검사·변환 단계

Runtime
→ 프로그램이 실제 실행되는 시점과 환경

AOT
→ 실행 전에 Machine Code 생성

JIT
→ 실행 중 Machine Code로 컴파일·최적화</code></pre>
<hr>
<h1 id="3-타입-시스템">3. 타입 시스템</h1>
<p>타입 시스템에서 가장 먼저 볼 것은 <strong>타입이 언제 결정되고 검사되는가</strong>이다.</p>
<h2 id="정적-타입-java--c">정적 타입: Java / C++</h2>
<p>변수를 선언할 때 타입이 정해지고 컴파일 단계에서 타입을 검사한다.</p>
<pre><code class="language-java">int age = 20;
String name = &quot;Kim&quot;;</code></pre>
<pre><code class="language-cpp">int age = 20;
std::string name = &quot;Kim&quot;;</code></pre>
<p>잘못된 타입을 사용하면 실행 전에 많은 오류를 발견할 수 있다.</p>
<pre><code class="language-java">int age = &quot;20&quot;; // Compile Error</code></pre>
<h2 id="동적-타입-javascript--python">동적 타입: JavaScript / Python</h2>
<p>변수에 타입을 고정하지 않고 실행 중 값에 따라 타입이 결정된다.</p>
<pre><code class="language-js">let value = 10;
value = &quot;hello&quot;;
value = true;</code></pre>
<pre><code class="language-python">value = 10
value = &quot;hello&quot;</code></pre>
<p>Python에서는 Type Hint를 사용할 수 있다.</p>
<pre><code class="language-python">def add(a: int, b: int) -&gt; int:
    return a + b</code></pre>
<p>하지만 기본적으로 Runtime에서 강제되는 것은 아니다.</p>
<h3 id="javascript의-특징">JavaScript의 특징</h3>
<p>암묵적 타입 변환이 존재한다.</p>
<pre><code class="language-js">1 == &quot;1&quot;   // true
1 === &quot;1&quot;  // false</code></pre>
<p>따라서 일반적으로 <code>===</code> 사용을 권장한다.</p>
<h3 id="핵심-1">핵심</h3>
<pre><code class="language-text">Java / C++
→ Compile Time에 타입 검사

JavaScript / Python
→ Runtime에 타입이 결정되는 동적 타입</code></pre>
<hr>
<h1 id="4-값--객체--참조--pointer">4. 값 / 객체 / 참조 / Pointer</h1>
<p>객체를 이해할 때 가장 중요한 것은 <strong>변수에 실제 값이 들어가는지, 객체를 가리키는 정보가 들어가는지</strong> 구분하는 것이다.</p>
<h2 id="javascript-1">JavaScript</h2>
<p>Primitive와 Object를 구분한다.</p>
<pre><code class="language-js">const user = {
    name: &quot;Kim&quot;
};

const a = user;
const b = a;

b.name = &quot;Lee&quot;;

console.log(a.name); // Lee</code></pre>
<p><code>a</code>, <code>b</code>가 같은 객체를 가리키고 있기 때문에 한쪽에서 객체를 수정하면 다른 쪽에서도 보인다.</p>
<p>객체 비교 역시 참조 기준이다.</p>
<pre><code class="language-js">{} === {} // false</code></pre>
<h2 id="java-1">Java</h2>
<p>Primitive Type과 Reference Type을 구분한다.</p>
<pre><code class="language-java">int age = 20;
User user = new User();</code></pre>
<p>개념적으로:</p>
<pre><code class="language-text">Stack                     Heap

age = 20

user ───────────────────→ User 객체
                           name
                           age</code></pre>
<p>객체 변수에는 객체 전체가 아니라 객체를 가리키는 Reference가 저장된다.</p>
<h2 id="python-1">Python</h2>
<p>Python에서는 거의 모든 것이 객체다.</p>
<pre><code class="language-python">a = [1, 2]
b = a

b.append(3)

print(a)  # [1, 2, 3]</code></pre>
<p>변수는 객체 자체라기보다 <strong>객체를 가리키는 이름</strong>처럼 이해하면 편하다.</p>
<h2 id="c-1">C++</h2>
<p>값, Pointer, Reference를 명시적으로 구분한다.</p>
<pre><code class="language-cpp">int a = 10;

int* p = &amp;a;   // Pointer
int&amp; r = a;    // Reference</code></pre>
<ul>
<li>Pointer → 메모리 주소 저장</li>
<li>Reference → 기존 객체의 별칭</li>
<li>Value → 값 자체</li>
</ul>
<p>C++은 이 차이를 개발자가 직접 다룬다는 점이 다른 언어와 크게 다르다.</p>
<hr>
<h1 id="5-함수-호출과-인자-전달">5. 함수 호출과 인자 전달</h1>
<h2 id="java-2">Java</h2>
<p>Java는 <strong>항상 Call by Value</strong>다.</p>
<pre><code class="language-java">void change(User user) {
    user.name = &quot;Lee&quot;;
}</code></pre>
<p>객체를 전달할 때도 객체 자체가 아니라 <strong>Reference 값의 복사본</strong>이 전달된다.</p>
<p>따라서 객체 내부 변경은 반영된다.</p>
<pre><code class="language-java">void change(User user) {
    user.name = &quot;Lee&quot;;
}</code></pre>
<p>하지만 매개변수를 다른 객체로 바꾸는 것은 외부 변수에 영향을 주지 않는다.</p>
<pre><code class="language-java">void change(User user) {
    user = new User();
}</code></pre>
<h2 id="javascript-2">JavaScript</h2>
<p>JavaScript도 Call by Value다.</p>
<p>객체를 전달하면 객체를 가리키는 참조값이 복사된다.</p>
<pre><code class="language-js">function change(obj) {
    obj.name = &quot;Lee&quot;;
}

const user = { name: &quot;Kim&quot; };

change(user);

console.log(user.name); // Lee</code></pre>
<h2 id="python-2">Python</h2>
<p>Python은 일반적으로 <strong>Call by Sharing(Object Sharing)</strong>이라고 설명한다.</p>
<pre><code class="language-python">def change(arr):
    arr.append(3)

a = [1, 2]
change(a)

print(a)  # [1, 2, 3]</code></pre>
<p>Mutable 객체 내부를 수정하면 외부에서도 변경이 보인다.</p>
<p>하지만 재바인딩은 외부에 영향을 주지 않는다.</p>
<pre><code class="language-python">def change(arr):
    arr = []

a = [1, 2]
change(a)

print(a)  # [1, 2]</code></pre>
<h2 id="c-2">C++</h2>
<p>C++은 값 전달과 참조 전달을 직접 선택한다.</p>
<pre><code class="language-cpp">void change(int x) {
    x = 10;
}</code></pre>
<p>값 전달.</p>
<pre><code class="language-cpp">void change(int&amp; x) {
    x = 10;
}</code></pre>
<p>참조 전달.</p>
<p>큰 객체는 복사 비용을 줄이기 위해 <code>const reference</code>를 많이 사용한다.</p>
<pre><code class="language-cpp">void print(const User&amp; user) {
}</code></pre>
<hr>
<h1 id="6-객체-모델">6. 객체 모델</h1>
<h2 id="java-3">Java</h2>
<p>전형적인 Class 기반 객체지향 언어다.</p>
<pre><code class="language-text">Class
  ↓
Object</code></pre>
<pre><code class="language-java">class User {
    String name;

    void hello() {
        System.out.println(name);
    }
}</code></pre>
<p>주요 개념:</p>
<pre><code class="language-text">Encapsulation
Inheritance
Polymorphism
Abstraction
Interface</code></pre>
<h2 id="c-3">C++</h2>
<p>C++ 역시 Class 기반 객체지향 언어다.</p>
<pre><code class="language-cpp">class Animal {
public:
    virtual void sound() {}
};

class Dog : public Animal {
public:
    void sound() override {}
};</code></pre>
<p>생성자와 소멸자, <code>virtual</code> 함수, 객체 생명주기가 중요한 특징이다.</p>
<h2 id="python-3">Python</h2>
<p>Python도 Class 기반이지만 훨씬 동적이다.</p>
<pre><code class="language-python">class User:
    def __init__(self, name):
        self.name = name

user = User(&quot;Kim&quot;)
user.age = 20</code></pre>
<p>Runtime에 객체 속성을 추가하는 것도 가능하다.</p>
<p>함수와 클래스 자체도 객체이기 때문에 Decorator 같은 기능도 자연스럽게 사용할 수 있다.</p>
<h2 id="javascript-3">JavaScript</h2>
<p>JavaScript는 본질적으로 Prototype 기반이다.</p>
<pre><code class="language-text">Object
  ↓
Prototype
  ↓
Prototype</code></pre>
<p>예를 들어:</p>
<pre><code class="language-js">const arr = [1, 2, 3];

arr.map(x =&gt; x * 2);</code></pre>
<p><code>map()</code>을 찾을 때 Prototype Chain을 따라 <code>Array.prototype</code>까지 탐색한다.</p>
<p>JavaScript의 <code>class</code>는 Prototype 기반 객체 모델을 편리하게 사용하는 문법이다.</p>
<hr>
<h1 id="7-mutable--immutable--equality">7. Mutable / Immutable / Equality</h1>
<h2 id="mutable--immutable">Mutable / Immutable</h2>
<p>Mutable은 객체 내부 상태를 변경할 수 있다는 의미이고, Immutable은 생성된 값을 변경할 수 없다는 의미다.</p>
<h3 id="javascript-4">JavaScript</h3>
<p>객체와 배열은 기본적으로 Mutable이다.</p>
<pre><code class="language-js">const user = {
    name: &quot;Kim&quot;
};

user.name = &quot;Lee&quot;; // 가능</code></pre>
<p><code>const</code>는 객체가 Immutable이라는 의미가 아니다.</p>
<pre><code class="language-js">user = {}; // Error</code></pre>
<p>단지 변수 재할당을 막는다.</p>
<h3 id="java-4">Java</h3>
<p>일반 객체는 Mutable할 수 있지만 <code>String</code>은 Immutable이다.</p>
<pre><code class="language-java">String s = &quot;hello&quot;;

s = s + &quot; world&quot;;</code></pre>
<p>기존 문자열이 바뀐 것이 아니라 새로운 String 객체가 만들어진다.</p>
<h3 id="python-4">Python</h3>
<p>Mutable:</p>
<pre><code class="language-text">list
dict
set</code></pre>
<p>Immutable:</p>
<pre><code class="language-text">int
float
str
tuple</code></pre>
<h3 id="c-4">C++</h3>
<p><code>const</code>를 통해 변경 가능성을 적극적으로 제어한다.</p>
<pre><code class="language-cpp">const int x = 10;

void print(const User&amp; user) {
}</code></pre>
<h2 id="equality--identity">Equality / Identity</h2>
<p>값이 같은 것과 같은 객체인 것은 다른 개념이다.</p>
<h3 id="javascript-5">JavaScript</h3>
<pre><code class="language-js">{} === {} // false</code></pre>
<p>객체는 Reference를 비교한다.</p>
<h3 id="java-5">Java</h3>
<pre><code class="language-java">a == b       // Reference 비교
a.equals(b) // 값 비교</code></pre>
<h3 id="python-5">Python</h3>
<pre><code class="language-python">a == b // 값 비교
a is b // 동일 객체 비교</code></pre>
<h3 id="c-5">C++</h3>
<p><code>operator==</code>를 오버로딩하여 값 비교 의미를 직접 정의할 수 있다.</p>
<pre><code class="language-cpp">bool operator==(const User&amp; other) const {
    ...
}</code></pre>
<hr>
<h1 id="8-메모리-관리와-객체-생명주기">8. 메모리 관리와 객체 생명주기</h1>
<h2 id="javascript-6">JavaScript</h2>
<p>Garbage Collector가 사용하지 않는 객체를 정리한다.</p>
<pre><code class="language-text">객체 생성
↓
Reference 존재
↓
Reference가 모두 사라짐
↓
GC 대상</code></pre>
<p>하지만 Closure, Event Listener, Timer 등이 객체를 계속 참조하고 있으면 불필요한 객체가 유지될 수 있다.</p>
<h2 id="java-6">Java</h2>
<p>JVM의 Garbage Collector가 Heap 객체를 관리한다.</p>
<p>GC Root에서 더 이상 도달할 수 없는 객체가 수거 대상이 된다.</p>
<p>GC가 있다고 해서 메모리 누수가 불가능한 것은 아니다.</p>
<pre><code class="language-text">불필요한 Reference 유지
→ 객체가 계속 Reachable
→ GC가 제거하지 못함</code></pre>
<h2 id="python-6">Python</h2>
<p>주로 Reference Counting 방식으로 관리한다.</p>
<pre><code class="language-text">Object Reference Count
3 → 2 → 1 → 0
             ↓
           제거</code></pre>
<p>순환 참조는 별도의 Garbage Collector가 처리한다.</p>
<h2 id="c-6">C++</h2>
<p>기본적인 Garbage Collector가 없다.</p>
<p>대신 <strong>RAII(Resource Acquisition Is Initialization)</strong>가 핵심이다.</p>
<pre><code class="language-cpp">{
    std::lock_guard&lt;std::mutex&gt; lock(mutex);

    // 작업
}</code></pre>
<p>Scope가 끝나면서 객체가 소멸되고 Lock도 자동 해제된다.</p>
<p>Smart Pointer도 사용한다.</p>
<pre><code class="language-cpp">std::unique_ptr&lt;User&gt;
std::shared_ptr&lt;User&gt;
std::weak_ptr&lt;User&gt;</code></pre>
<hr>
<h1 id="9-함수--scope--closure">9. 함수 / Scope / Closure</h1>
<h2 id="javascript-7">JavaScript</h2>
<p>함수가 <strong>일급 객체</strong>다.</p>
<pre><code class="language-js">const add = (a, b) =&gt; a + b;

function run(fn) {
    return fn(1, 2);
}</code></pre>
<p>함수를 변수에 저장하고, 인자로 전달하고, 반환할 수 있다.</p>
<p>Closure도 핵심이다.</p>
<pre><code class="language-js">function outer() {
    let count = 0;

    return () =&gt; ++count;
}

const counter = outer();

counter(); // 1
counter(); // 2</code></pre>
<p>JavaScript는 Lexical Scope를 사용한다.</p>
<p>또한 일반 함수와 화살표 함수의 <code>this</code> 동작이 다르다.</p>
<h2 id="python-7">Python</h2>
<p>Python에서도 함수는 일급 객체다.</p>
<pre><code class="language-python">def add(a, b):
    return a + b

fn = add
fn(1, 2)</code></pre>
<p>Decorator와 Closure도 자주 사용한다.</p>
<pre><code class="language-python">def decorator(fn):
    def wrapper():
        print(&quot;before&quot;)
        fn()

    return wrapper</code></pre>
<p>Python의 Scope는 LEGB 규칙을 따른다.</p>
<pre><code class="language-text">Local
Enclosing
Global
Built-in</code></pre>
<h2 id="java-7">Java</h2>
<p>기본적으로 객체와 Method 중심이다.</p>
<p>Lambda와 Functional Interface를 통해 함수형 스타일을 사용할 수 있다.</p>
<pre><code class="language-java">list.stream()
    .filter(x -&gt; x &gt; 10)
    .map(x -&gt; x * 2)
    .toList();</code></pre>
<h2 id="c-7">C++</h2>
<p>Lambda와 함수 포인터를 사용할 수 있다.</p>
<pre><code class="language-cpp">auto add = [](int a, int b) {
    return a + b;
};</code></pre>
<p>외부 변수 Capture도 가능하다.</p>
<pre><code class="language-cpp">[=] // 값 Capture

[&amp;] // Reference Capture</code></pre>
<hr>
<h1 id="10-thread--동시성--비동기">10. Thread / 동시성 / 비동기</h1>
<p>먼저 세 개념을 구분해야 한다.</p>
<pre><code class="language-text">Concurrency
→ 여러 작업을 함께 진행시키는 구조

Parallelism
→ 실제 여러 작업을 동시에 실행

Asynchronous
→ 작업 완료를 기다리지 않고 다른 작업 수행</code></pre>
<h2 id="javascript-8">JavaScript</h2>
<p>기본 JavaScript 실행은 하나의 Main Thread에서 이루어진다.</p>
<pre><code class="language-text">Call Stack
    ↓
Event Loop
    ↓
Microtask Queue / Task Queue</code></pre>
<p>Timer, Network, DOM Event 등은 Browser 또는 Node.js Runtime이 처리한다.</p>
<pre><code class="language-js">console.log(&quot;A&quot;);

setTimeout(() =&gt; {
    console.log(&quot;B&quot;);
}, 0);

console.log(&quot;C&quot;);</code></pre>
<p>결과:</p>
<pre><code class="language-text">A
C
B</code></pre>
<p>Promise와 <code>async/await</code>도 Event Loop와 연결된다.</p>
<pre><code class="language-js">async function getUsers() {
    const response = await fetch(&quot;/api/users&quot;);
    return response.json();
}</code></pre>
<pre><code class="language-text">Single Thread ≠ 동시성 불가능
Async ≠ Multi Thread</code></pre>
<h2 id="java-8">Java</h2>
<p>멀티스레드를 적극적으로 활용한다.</p>
<pre><code class="language-text">Request A → Thread 1
Request B → Thread 2
Request C → Thread 3</code></pre>
<p>여러 Thread가 같은 객체를 공유하기 때문에 Thread Safety가 중요하다.</p>
<p>대표적으로:</p>
<pre><code class="language-text">synchronized
volatile
Atomic
ExecutorService
CompletableFuture
Virtual Thread</code></pre>
<p>등을 사용한다.</p>
<h2 id="python-8">Python</h2>
<p>CPython에는 <strong>GIL(Global Interpreter Lock)</strong>이 있다.</p>
<p>여러 OS Thread를 만들 수 있지만 한 시점에는 하나의 Thread만 Python Bytecode를 실행한다.</p>
<p>따라서 일반적으로:</p>
<pre><code class="language-text">CPU Bound
→ multiprocessing

I/O Bound
→ threading / asyncio</code></pre>
<p>방식을 사용한다.</p>
<p><code>asyncio</code>는 Coroutine과 Event Loop를 이용한다.</p>
<h2 id="c-8">C++</h2>
<p>OS Thread를 직접 사용할 수 있다.</p>
<pre><code class="language-cpp">std::thread
std::mutex
std::atomic
std::condition_variable</code></pre>
<p>그만큼 다음 문제를 직접 고려해야 한다.</p>
<pre><code class="language-text">Race Condition
Data Race
Deadlock</code></pre>
<hr>
<h1 id="11-프레임워크와-언어-특성">11. 프레임워크와 언어 특성</h1>
<p>언어의 특성은 실제 프레임워크 설계에도 그대로 드러난다.</p>
<h2 id="javascript--react--nodejs">JavaScript + React / Node.js</h2>
<p>React에서 특히 중요한 개념:</p>
<pre><code class="language-text">Function
Closure
Reference Equality
Immutability
Event Loop
Async</code></pre>
<p>상태 변경과 렌더링을 제대로 이해하려면 객체 참조와 Closure를 이해해야 한다.</p>
<p>Node.js에서는 Event Loop와 비동기 I/O가 핵심이다.</p>
<hr>
<h2 id="java--spring">Java + Spring</h2>
<p>Spring에서 중요한 Java 특성:</p>
<pre><code class="language-text">Class / Object
Reflection
Annotation
Proxy
GC
Multi Thread
Thread Safety</code></pre>
<p>Spring Bean은 기본적으로 Singleton이므로 여러 Thread가 동일 Bean을 동시에 사용할 수 있다.</p>
<p>따라서 Mutable 상태를 Bean 필드에 함부로 저장하면 Thread Safety 문제가 발생할 수 있다.</p>
<hr>
<h2 id="python--fastapi--django">Python + FastAPI / Django</h2>
<p>FastAPI:</p>
<pre><code class="language-text">Type Hint
Decorator
async / await
Coroutine</code></pre>
<p>Python의 동적인 특성과 비동기 기능을 적극적으로 활용한다.</p>
<p>Django 역시 Class, Decorator, ORM 등 Python의 객체 모델을 많이 활용한다.</p>
<hr>
<h2 id="c-9">C++</h2>
<p>C++은 특정 하나의 웹 프레임워크보다 다음 분야에서 많이 사용된다.</p>
<pre><code class="language-text">System Programming
Game Engine
Embedded
High Performance Application</code></pre>
<p>이 환경에서는 특히:</p>
<pre><code class="language-text">Pointer / Reference
RAII
Object Lifetime
Memory Safety
Thread</code></pre>
<p>에 대한 이해가 중요하다.</p>
<hr>
<h1 id="12-핵심-비교">12. 핵심 비교</h1>
<pre><code class="language-text">JavaScript
→ 동적 타입
→ Prototype 기반 객체 모델
→ Garbage Collector
→ 함수 / Closure 중심
→ Event Loop 기반 동시성

Java
→ 정적 타입
→ Class 기반 객체지향
→ JVM + Garbage Collector
→ Reference를 Call by Value로 전달
→ Multi Thread와 Thread Safety 중요

Python
→ 동적 타입
→ 거의 모든 것이 객체
→ Reference Counting + GC
→ 함수가 일급 객체
→ GIL + asyncio / multiprocessing

C++
→ 정적 타입
→ Class + Pointer + Reference
→ Native 실행
→ RAII 기반 자원 관리
→ Memory / Thread를 개발자가 직접 제어</code></pre>
<p>결국 네 언어를 비교할 때는 다음 질문을 중심으로 보면 된다.</p>
<pre><code class="language-text">1. 코드는 어떤 Runtime에서 어떻게 실행되는가?
2. 타입은 언제 결정되고 검사되는가?
3. 변수는 값과 객체를 어떻게 저장하는가?
4. 함수에 인자를 넘기면 무엇이 전달되는가?
5. 객체 모델은 Class인가 Prototype인가?
6. 객체는 Mutable인가 Immutable인가?
7. 메모리와 객체 수명은 누가 관리하는가?
8. 함수와 Scope는 어떤 특징을 가지는가?
9. 여러 작업을 Thread / Event Loop / Async 중 어떻게 처리하는가?
10. 이러한 특성이 프레임워크에서 어떻게 활용되는가?</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS) JS 기초]]></title>
            <link>https://velog.io/@squee_z_e/JS-20261001-1</link>
            <guid>https://velog.io/@squee_z_e/JS-20261001-1</guid>
            <pubDate>Thu, 01 Oct 2026 01:15:27 GMT</pubDate>
            <description><![CDATA[<h1 id="part-1-javascript-기본-문법">Part 1. JavaScript 기본 문법</h1>
<h2 id="1-변수-선언-let-const-var">1. 변수 선언: <code>let</code>, <code>const</code>, <code>var</code></h2>
<p>JavaScript에서는 주로 <code>let</code>, <code>const</code>를 사용한다.</p>
<pre><code class="language-js">let age = 20;
const name = &quot;Kim&quot;;</code></pre>
<ul>
<li><code>let</code>: 값 변경 가능</li>
<li><code>const</code>: 재할당 불가능</li>
<li><code>var</code>: 과거 문법. 함수 스코프와 호이스팅 문제 때문에 일반적으로 사용하지 않는다.</li>
</ul>
<pre><code class="language-js">let a = 10;
a = 20; // 가능

const b = 10;
b = 20; // Error</code></pre>
<p>기본적으로 <strong><code>const</code>를 사용하고, 값이 바뀌어야 할 때만 <code>let</code>을 사용하는 방식</strong>이 일반적이다.</p>
<h2 id="2-javascript의-주요-자료형">2. JavaScript의 주요 자료형</h2>
<p>JavaScript의 기본 자료형은 다음과 같다.</p>
<pre><code class="language-js">let num = 10;              // number
let str = &quot;hello&quot;;         // string
let bool = true;           // boolean
let empty = null;          // null
let value;                 // undefined
let big = 123n;            // bigint
let id = Symbol(&quot;id&quot;);     // symbol</code></pre>
<p>Java와 다르게 정수와 실수를 별도의 타입으로 구분하지 않고 대부분 <code>number</code>로 처리한다.</p>
<pre><code class="language-js">typeof 10;    // &quot;number&quot;
typeof 3.14;  // &quot;number&quot;</code></pre>
<h2 id="3-동적-타입-언어">3. 동적 타입 언어</h2>
<p>JavaScript는 <strong>동적 타입 언어</strong>이다.</p>
<p>변수를 선언할 때 타입을 지정하지 않고, 실행 중에 다른 타입의 값을 넣을 수도 있다.</p>
<pre><code class="language-js">let value = 10;

value = &quot;hello&quot;;
value = true;</code></pre>
<p>Java에서는 변수의 타입이 고정되지만, JavaScript와 Python은 변수에 저장되는 값에 따라 타입이 결정된다.</p>
<h2 id="4-문자열">4. 문자열</h2>
<p>문자열은 <code>&#39;</code>, <code>&quot;</code>, 백틱 <code>`</code>을 사용할 수 있다.</p>
<pre><code class="language-js">const name = &quot;Kim&quot;;
const age = 20;</code></pre>
<p>백틱을 사용하면 문자열 안에 변수를 쉽게 넣을 수 있다.</p>
<pre><code class="language-js">const message = `이름은 ${name}, 나이는 ${age}살입니다.`;</code></pre>
<p>이를 <strong>Template Literal</strong>이라고 한다.</p>
<h2 id="5-배열">5. 배열</h2>
<p>JavaScript 배열은 서로 다른 타입의 값을 함께 저장할 수 있다.</p>
<pre><code class="language-js">const arr = [1, &quot;hello&quot;, true];</code></pre>
<p>주요 사용법:</p>
<pre><code class="language-js">arr.push(10);
arr.pop();

arr[0];
arr.length;</code></pre>
<p>실제 웹 개발에서는 배열 자체보다 <code>map</code>, <code>filter</code>, <code>find</code> 같은 배열 메서드를 매우 자주 사용한다.</p>
<h2 id="6-객체">6. 객체</h2>
<p>JavaScript에서 객체는 <code>key-value</code> 형태의 데이터를 저장한다.</p>
<pre><code class="language-js">const user = {
    name: &quot;Kim&quot;,
    age: 20
};</code></pre>
<p>값 접근:</p>
<pre><code class="language-js">user.name;
user[&quot;age&quot;];</code></pre>
<p>값 변경:</p>
<pre><code class="language-js">user.age = 21;</code></pre>
<p>Java의 객체보다 Python의 <code>dict</code>와 비슷하게 느껴질 수 있지만, JavaScript에서는 객체가 언어 전체에서 매우 중요한 역할을 한다.</p>
<h2 id="7-함수">7. 함수</h2>
<p>일반 함수:</p>
<pre><code class="language-js">function add(a, b) {
    return a + b;
}</code></pre>
<p>함수 표현식:</p>
<pre><code class="language-js">const add = function(a, b) {
    return a + b;
};</code></pre>
<p>화살표 함수:</p>
<pre><code class="language-js">const add = (a, b) =&gt; {
    return a + b;
};</code></pre>
<p>한 줄이면 더 줄일 수 있다.</p>
<pre><code class="language-js">const add = (a, b) =&gt; a + b;</code></pre>
<p>웹 개발, 특히 React에서는 화살표 함수를 매우 자주 사용한다.</p>
<h2 id="8-조건문과-반복문">8. 조건문과 반복문</h2>
<p>Java와 거의 비슷하다.</p>
<pre><code class="language-js">if (age &gt;= 20) {
    console.log(&quot;성인&quot;);
} else {
    console.log(&quot;미성년자&quot;);
}</code></pre>
<p>반복문:</p>
<pre><code class="language-js">for (let i = 0; i &lt; 5; i++) {
    console.log(i);
}</code></pre>
<p>배열 순회에는 다음 방식도 자주 사용한다.</p>
<pre><code class="language-js">for (const value of arr) {
    console.log(value);
}</code></pre>
<h2 id="9-구조-분해-할당">9. 구조 분해 할당</h2>
<p>배열이나 객체에서 값을 쉽게 꺼낼 수 있는 문법이다.</p>
<pre><code class="language-js">const user = {
    name: &quot;Kim&quot;,
    age: 20
};

const { name, age } = user;</code></pre>
<p>배열에서도 사용할 수 있다.</p>
<pre><code class="language-js">const arr = [10, 20];

const [a, b] = arr;</code></pre>
<p>React에서 매우 많이 등장한다.</p>
<pre><code class="language-js">const [count, setCount] = useState(0);</code></pre>
<h2 id="10-spread-문법-">10. Spread 문법 <code>...</code></h2>
<p>배열이나 객체의 값을 펼칠 때 사용한다.</p>
<pre><code class="language-js">const a = [1, 2];
const b = [...a, 3, 4];</code></pre>
<p>객체:</p>
<pre><code class="language-js">const user = {
    name: &quot;Kim&quot;,
    age: 20
};

const newUser = {
    ...user,
    age: 21
};</code></pre>
<p>React에서 상태를 변경할 때 특히 자주 사용한다.</p>
<h2 id="핵심-정리">핵심 정리</h2>
<pre><code class="language-text">let   → 재할당 가능
const → 재할당 불가능
var   → 가급적 사용하지 않음

JavaScript는 동적 타입 언어

배열 → []
객체 → {}

함수는 값처럼 변수에 저장 가능

() =&gt; {} → 화살표 함수

const { a, b } = obj
→ 구조 분해 할당

...arr / ...obj
→ Spread 문법</code></pre>
<h1 id="part-2-javascript-타입-시스템">Part 2. JavaScript 타입 시스템</h1>
<h2 id="1-null-vs-undefined">1. <code>null</code> vs <code>undefined</code></h2>
<p>둘 다 “값이 없음”을 나타내지만 의미가 다르다.</p>
<pre><code class="language-js">let a;
console.log(a); // undefined

let b = null;
console.log(b); // null</code></pre>
<ul>
<li><code>undefined</code>: 값이 아직 할당되지 않음</li>
<li><code>null</code>: 개발자가 의도적으로 “값 없음”을 넣음</li>
</ul>
<p>실무에서는 API 응답, 초기 상태값, optional 값 처리할 때 자주 만난다.</p>
<h2 id="2--vs-">2. <code>==</code> vs <code>===</code></h2>
<pre><code class="language-js">1 == &quot;1&quot;   // true
1 === &quot;1&quot;  // false</code></pre>
<p><code>==</code>는 비교 전에 타입을 자동 변환한다.</p>
<p><code>===</code>는 <strong>타입과 값이 모두 같은지 비교</strong>한다.</p>
<p>그래서 실무에서는 거의 항상:</p>
<pre><code class="language-js">if (a === b) {
}</code></pre>
<p>처럼 <code>===</code>를 사용한다.</p>
<h2 id="3-타입-강제-변환type-coercion">3. 타입 강제 변환(Type Coercion)</h2>
<p>JavaScript는 상황에 따라 타입을 자동으로 바꾼다.</p>
<pre><code class="language-js">&quot;5&quot; + 1   // &quot;51&quot;
&quot;5&quot; - 1   // 4</code></pre>
<p><code>+</code>는 문자열 연결로 동작할 수 있지만, <code>-</code>는 숫자 연산만 가능해서 숫자로 변환된다.</p>
<p>이런 암묵적 변환 때문에 JS에서 예상하지 못한 결과가 생길 수 있다.</p>
<h2 id="4-truthy--falsy">4. Truthy / Falsy</h2>
<p>JS에서는 boolean이 아니어도 조건문에서 참/거짓으로 판단된다.</p>
<p>Falsy 값:</p>
<pre><code class="language-text">false
0
&quot;&quot;
null
undefined
NaN</code></pre>
<p>이외 대부분의 값은 Truthy다.</p>
<pre><code class="language-js">if (&quot;hello&quot;) {
    console.log(&quot;실행됨&quot;);
}</code></pre>
<p>특히 주의할 것:</p>
<pre><code class="language-js">[]   // truthy
{}   // truthy</code></pre>
<p>빈 배열과 빈 객체도 <code>true</code> 취급된다.</p>
<h2 id="5-nan">5. <code>NaN</code></h2>
<p><code>NaN</code>은 Not a Number라는 뜻이다.</p>
<pre><code class="language-js">Number(&quot;hello&quot;); // NaN</code></pre>
<p>특이하게:</p>
<pre><code class="language-js">NaN === NaN // false</code></pre>
<p>그래서 확인할 때는:</p>
<pre><code class="language-js">Number.isNaN(value);</code></pre>
<p>을 사용한다.</p>
<h2 id="6-typeof">6. <code>typeof</code></h2>
<p>타입 확인:</p>
<pre><code class="language-js">typeof 10          // &quot;number&quot;
typeof &quot;hello&quot;     // &quot;string&quot;
typeof true        // &quot;boolean&quot;
typeof undefined   // &quot;undefined&quot;
typeof {}          // &quot;object&quot;</code></pre>
<p>그런데 유명한 예외가 있다.</p>
<pre><code class="language-js">typeof null // &quot;object&quot;</code></pre>
<p>이건 JS 초기 설계에서 생긴 오래된 버그성 동작이다.</p>
<h2 id="핵심-정리-1">핵심 정리</h2>
<pre><code class="language-text">undefined
→ 값이 아직 없음

null
→ 의도적으로 값 없음

== 
→ 타입 변환 후 비교

===
→ 타입 + 값 비교
→ 실무에서 기본 사용

Truthy / Falsy
→ 조건문에서 값 자체가 true/false처럼 평가됨

[]와 {}는 truthy

NaN
→ 숫자로 변환할 수 없는 값

typeof null === &quot;object&quot;
→ JS의 오래된 특이사항</code></pre>
<h1 id="part-3-scope와-실행-구조">Part 3. Scope와 실행 구조</h1>
<h2 id="1-scope란">1. Scope란?</h2>
<p>Scope는 <strong>변수에 접근할 수 있는 범위</strong>다.</p>
<pre><code class="language-js">const a = 10;

function test() {
    const b = 20;
    console.log(a); // 가능
    console.log(b); // 가능
}

console.log(b); // 불가능</code></pre>
<p>JS에는 크게 다음 범위가 있다.</p>
<pre><code class="language-text">Global Scope
Function Scope
Block Scope</code></pre>
<h2 id="2-block-scope">2. Block Scope</h2>
<p><code>let</code>, <code>const</code>는 <code>{}</code> 단위로 범위가 나뉜다.</p>
<pre><code class="language-js">if (true) {
    const a = 10;
}

console.log(a); // Error</code></pre>
<p>반면 <code>var</code>는 block scope가 아니라 <strong>function scope</strong>를 따른다.</p>
<pre><code class="language-js">if (true) {
    var a = 10;
}

console.log(a); // 10</code></pre>
<p>이것도 <code>var</code>를 잘 쓰지 않는 이유 중 하나다.</p>
<h2 id="3-scope-chain">3. Scope Chain</h2>
<p>현재 범위에 변수가 없으면 바깥 범위로 올라가며 찾는다.</p>
<pre><code class="language-js">const a = 10;

function outer() {
    const b = 20;

    function inner() {
        console.log(a);
        console.log(b);
    }

    inner();
}</code></pre>
<p><code>inner()</code>는 자신의 scope → <code>outer</code> → global 순서로 변수를 찾는다.</p>
<p>이 구조를 <strong>Scope Chain</strong>이라고 한다.</p>
<h2 id="4-lexical-scope">4. Lexical Scope</h2>
<p>JS는 <strong>함수가 어디에서 호출됐는지가 아니라, 어디에서 선언됐는지</strong>를 기준으로 바깥 scope를 결정한다.</p>
<pre><code class="language-js">const x = 10;

function test() {
    console.log(x);
}

function run() {
    const x = 20;
    test();
}

run(); // 10</code></pre>
<p><code>test()</code>가 <code>run()</code> 안에서 호출됐지만, 선언된 위치가 global이므로 <code>x = 10</code>을 본다.</p>
<p>이걸 <strong>Lexical Scope</strong>라고 한다.</p>
<h2 id="5-execution-context">5. Execution Context</h2>
<p>JS는 코드를 실행할 때 <strong>Execution Context(실행 컨텍스트)</strong>를 만든다.</p>
<p>쉽게 말하면:</p>
<blockquote>
<p>현재 실행 중인 코드의 변수, 함수, scope 정보를 관리하는 실행 환경</p>
</blockquote>
<p>함수가 호출될 때마다 새로운 실행 컨텍스트가 생성된다.</p>
<pre><code class="language-js">function a() {
    b();
}

function b() {
    console.log(&quot;hello&quot;);
}

a();</code></pre>
<p>개념적으로:</p>
<pre><code class="language-text">Global Execution Context
        ↓
a() Execution Context
        ↓
b() Execution Context</code></pre>
<p>가 만들어진다.</p>
<h2 id="6-call-stack">6. Call Stack</h2>
<p>Execution Context는 <strong>Call Stack</strong>에 쌓인다.</p>
<pre><code class="language-js">function first() {
    second();
}

function second() {
    console.log(&quot;hello&quot;);
}

first();</code></pre>
<p>실행 흐름:</p>
<pre><code class="language-text">Global
↓
first()
↓
second()
↓
second 종료
↓
first 종료</code></pre>
<p>Stack이기 때문에 <strong>LIFO(Last In First Out)</strong> 구조다.</p>
<p>이 Call Stack 개념은 나중에 <strong>싱글 스레드와 Event Loop</strong>를 이해할 때 핵심이 된다.</p>
<h2 id="핵심-정리-2">핵심 정리</h2>
<pre><code class="language-text">Scope
→ 변수에 접근할 수 있는 범위

let / const
→ block scope

var
→ function scope

Scope Chain
→ 변수를 현재 scope부터 바깥으로 찾아감

Lexical Scope
→ 함수가 선언된 위치 기준으로 scope 결정

Execution Context
→ 현재 실행 중인 코드의 환경 정보

Call Stack
→ 실행 컨텍스트가 쌓이는 구조
→ LIFO</code></pre>
<h1 id="part-4-함수와-closure-this">Part 4. 함수와 Closure, this</h1>
<h2 id="1-js에서-함수는-값이다">1. JS에서 함수는 값이다</h2>
<p>JavaScript에서 함수는 변수에 저장하거나, 다른 함수에 전달하거나, 반환할 수 있다.</p>
<pre><code class="language-js">const add = (a, b) =&gt; a + b;</code></pre>
<p>즉 함수도 하나의 값처럼 다룬다.</p>
<h2 id="2-callback-함수">2. Callback 함수</h2>
<p>다른 함수에 인자로 전달되는 함수를 callback이라고 한다.</p>
<pre><code class="language-js">function run(callback) {
    callback();
}

run(() =&gt; console.log(&quot;hello&quot;));</code></pre>
<p>웹 개발에서 이벤트, 비동기 처리, 배열 메서드 등에 매우 자주 사용된다.</p>
<pre><code class="language-js">arr.map(x =&gt; x * 2);</code></pre>
<h2 id="3-고차-함수">3. 고차 함수</h2>
<p>함수를 인자로 받거나 함수를 반환하는 함수를 <strong>고차 함수(Higher-Order Function)</strong>라고 한다.</p>
<p>대표적으로:</p>
<pre><code class="language-text">map()
filter()
reduce()</code></pre>
<p>같은 배열 메서드가 있다.</p>
<h2 id="4-closure">4. Closure</h2>
<p>Closure는 <strong>함수가 자신이 선언된 외부 Scope의 변수를 기억하는 특성</strong>이다.</p>
<pre><code class="language-js">function outer() {
    let count = 0;

    return () =&gt; {
        count++;
        return count;
    };
}

const counter = outer();

counter(); // 1
counter(); // 2</code></pre>
<p><code>outer()</code> 실행은 끝났지만 내부 함수가 <code>count</code>를 계속 기억한다.</p>
<p>즉:</p>
<pre><code class="language-text">함수 + 함수가 선언될 당시의 외부 환경
= Closure</code></pre>
<p>React의 state, event handler, hook을 이해할 때 중요하다.</p>
<h2 id="5-this">5. <code>this</code></h2>
<p><code>this</code>는 <strong>현재 함수가 어떤 객체와 연결되어 실행되는지</strong>에 따라 값이 달라질 수 있다.</p>
<pre><code class="language-js">const user = {
    name: &quot;Kim&quot;,

    hello() {
        console.log(this.name);
    }
};

user.hello(); // Kim</code></pre>
<p>여기서 <code>this</code>는 <code>user</code>를 가리킨다.</p>
<h2 id="6-화살표-함수의-this">6. 화살표 함수의 <code>this</code></h2>
<p>화살표 함수는 자신의 <code>this</code>를 새로 만들지 않는다.</p>
<p>바깥 Scope의 <code>this</code>를 그대로 사용한다.</p>
<pre><code class="language-js">const user = {
    name: &quot;Kim&quot;,

    hello: () =&gt; {
        console.log(this.name);
    }
};</code></pre>
<p>이 경우 <code>this</code>가 <code>user</code>를 가리킨다고 기대하면 안 된다.</p>
<p>그래서 객체 메서드에서는 일반 함수 문법을 사용하는 경우가 많다.</p>
<h2 id="핵심-정리-3">핵심 정리</h2>
<pre><code class="language-text">JS에서 함수는 값처럼 사용 가능

Callback
→ 다른 함수에 전달되는 함수

고차 함수
→ 함수를 인자로 받거나 반환하는 함수

Closure
→ 함수가 외부 Scope의 변수를 기억하는 특성

this
→ 함수가 어떻게 호출되었는지에 따라 결정

화살표 함수
→ 자신의 this를 만들지 않음
→ 바깥 this 사용</code></pre>
<h1 id="part-5-객체--reference--prototype--class">Part 5. 객체 / Reference / Prototype / Class</h1>
<h2 id="1-객체는-참조-타입">1. 객체는 참조 타입</h2>
<p>JS에서 객체와 배열은 값을 직접 복사하는 게 아니라 <strong>참조값</strong>을 다룬다.</p>
<pre><code class="language-js">const a = { name: &quot;Kim&quot; };
const b = a;

b.name = &quot;Lee&quot;;

console.log(a.name); // Lee</code></pre>
<p><code>a</code>, <code>b</code>가 같은 객체를 가리키기 때문이다.</p>
<h2 id="2-얕은-복사">2. 얕은 복사</h2>
<p>Spread를 사용하면 새 객체를 만들 수 있다.</p>
<pre><code class="language-js">const a = { name: &quot;Kim&quot;, age: 20 };
const b = { ...a };</code></pre>
<p>하지만 중첩 객체는 여전히 참조가 공유될 수 있다.</p>
<pre><code class="language-js">const a = {
    user: { name: &quot;Kim&quot; }
};

const b = { ...a };

b.user.name = &quot;Lee&quot;;

console.log(a.user.name); // Lee</code></pre>
<p>즉 <code>...</code>은 기본적으로 <strong>얕은 복사(Shallow Copy)</strong>다.</p>
<h2 id="3-객체-비교">3. 객체 비교</h2>
<p>객체는 내용이 아니라 참조값을 비교한다.</p>
<pre><code class="language-js">{} === {} // false</code></pre>
<p>반면:</p>
<pre><code class="language-js">const a = {};
const b = a;

a === b // true</code></pre>
<p>같은 객체를 가리킬 때만 <code>true</code>다.</p>
<h2 id="4-prototype">4. Prototype</h2>
<p>JavaScript 객체는 다른 객체를 기반으로 기능을 상속받을 수 있다.</p>
<p>이 연결 구조가 <strong>Prototype</strong>이다.</p>
<p>예를 들어 배열에서:</p>
<pre><code class="language-js">const arr = [1, 2, 3];

arr.map(...)
arr.filter(...)</code></pre>
<p><code>map</code>, <code>filter</code>를 우리가 직접 만든 적이 없는데 사용할 수 있는 이유는 배열이 <code>Array.prototype</code>의 메서드를 상속받기 때문이다.</p>
<p>개념적으로:</p>
<pre><code class="language-text">arr
 ↓
Array.prototype
 ↓
Object.prototype</code></pre>
<p>이렇게 prototype chain을 따라 메서드를 찾는다.</p>
<h2 id="5-prototype-chain">5. Prototype Chain</h2>
<p>객체에서 어떤 속성이나 메서드를 찾을 때:</p>
<pre><code class="language-text">현재 객체에서 찾음
↓
없으면 prototype에서 찾음
↓
또 없으면 상위 prototype에서 찾음</code></pre>
<p>이 구조를 <strong>Prototype Chain</strong>이라고 한다.</p>
<h2 id="6-class">6. Class</h2>
<p>JS에서도 Java처럼 <code>class</code>를 사용할 수 있다.</p>
<pre><code class="language-js">class User {
    constructor(name) {
        this.name = name;
    }

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

const user = new User(&quot;Kim&quot;);</code></pre>
<p>하지만 JS의 <code>class</code>는 본질적으로 <strong>prototype 기반 구조를 더 익숙하게 표현한 문법</strong>이다.</p>
<h2 id="7-상속">7. 상속</h2>
<pre><code class="language-js">class Animal {
    move() {
        console.log(&quot;move&quot;);
    }
}

class Dog extends Animal {
    bark() {
        console.log(&quot;bark&quot;);
    }
}</code></pre>
<p>Java와 비슷하게 보이지만 내부적으로는 prototype chain을 사용한다.</p>
<h2 id="핵심-정리-4">핵심 정리</h2>
<pre><code class="language-text">객체 / 배열
→ 참조 타입

const b = a
→ 같은 객체 참조

{ ...a }
→ 새 객체 생성
→ 하지만 얕은 복사

{} === {}
→ false
→ 객체는 참조값 비교

Prototype
→ 다른 객체의 기능을 상속받는 구조

Prototype Chain
→ 속성/메서드를 상위 prototype으로 찾아감

class
→ prototype 기반 객체 시스템을 감싼 문법</code></pre>
<h1 id="part-6-싱글-스레드--비동기--event-loop">Part 6. 싱글 스레드 / 비동기 / Event Loop</h1>
<h2 id="1-javascript는-싱글-스레드">1. JavaScript는 싱글 스레드</h2>
<p>JavaScript는 기본적으로 <strong>한 번에 하나의 작업만 실행</strong>한다.</p>
<pre><code class="language-text">작업 A 실행
→ 작업 A 종료
→ 작업 B 실행</code></pre>
<p>즉 JS 코드를 실행하는 <strong>Call Stack은 하나</strong>다.</p>
<h2 id="2-그럼-비동기는-어떻게-처리하나">2. 그럼 비동기는 어떻게 처리하나?</h2>
<p>브라우저 환경에서는 오래 걸리는 작업을 브라우저가 대신 처리한다.</p>
<p>대표적으로:</p>
<pre><code class="language-js">setTimeout(...)
fetch(...)</code></pre>
<p>개념적으로:</p>
<pre><code class="language-text">JavaScript
→ Call Stack

Browser
→ Timer
→ Network
→ DOM Event</code></pre>
<p>JS 자체는 한 번에 하나만 실행하지만, 외부 작업은 브라우저가 처리할 수 있다.</p>
<h2 id="3-event-loop">3. Event Loop</h2>
<p>비동기 작업이 끝나면 callback이 Queue에 들어간다.</p>
<p>Event Loop는 계속 확인한다.</p>
<pre><code class="language-text">Call Stack이 비었나?
↓
비었으면 Queue의 작업 실행</code></pre>
<p>이 구조 덕분에 싱글 스레드에서도 비동기 처리가 가능하다.</p>
<h2 id="4-실행-순서-예시">4. 실행 순서 예시</h2>
<pre><code class="language-js">console.log(&quot;A&quot;);

setTimeout(() =&gt; {
    console.log(&quot;B&quot;);
}, 0);

console.log(&quot;C&quot;);</code></pre>
<p>결과:</p>
<pre><code class="language-text">A
C
B</code></pre>
<p><code>setTimeout(..., 0)</code>이라고 해도 바로 실행되는 것이 아니다.</p>
<p>callback은 Queue에서 기다리고, 현재 동기 코드가 모두 끝난 뒤 실행된다.</p>
<h2 id="5-동기-vs-비동기">5. 동기 vs 비동기</h2>
<p>동기:</p>
<pre><code class="language-js">const a = 1;
const b = 2;
console.log(a + b);</code></pre>
<p>앞 작업이 끝나야 다음 작업이 실행된다.</p>
<p>비동기:</p>
<pre><code class="language-js">fetch(&quot;/api/users&quot;);
console.log(&quot;next&quot;);</code></pre>
<p>네트워크 응답을 기다리는 동안 다음 코드가 계속 실행될 수 있다.</p>
<h2 id="6-callback-방식">6. Callback 방식</h2>
<p>초기 JS에서는 비동기 처리를 callback으로 많이 했다.</p>
<pre><code class="language-js">setTimeout(() =&gt; {
    console.log(&quot;done&quot;);
}, 1000);</code></pre>
<p>하지만 여러 비동기 작업이 중첩되면 코드가 복잡해진다.</p>
<pre><code class="language-js">a(() =&gt; {
    b(() =&gt; {
        c(() =&gt; {
        });
    });
});</code></pre>
<p>이런 형태를 흔히 <strong>Callback Hell</strong>이라고 한다.</p>
<h2 id="7-promise">7. Promise</h2>
<p>Promise는 비동기 작업의 결과를 표현하는 객체다.</p>
<p>상태는 3개다.</p>
<pre><code class="language-text">pending
→ 진행 중

fulfilled
→ 성공

rejected
→ 실패</code></pre>
<p>사용 예:</p>
<pre><code class="language-js">fetch(&quot;/api/users&quot;)
    .then(response =&gt; response.json())
    .then(data =&gt; console.log(data))
    .catch(error =&gt; console.error(error));</code></pre>
<h2 id="8-async--await">8. async / await</h2>
<p>Promise를 더 읽기 쉽게 사용하는 문법이다.</p>
<pre><code class="language-js">async function getUsers() {
    const response = await fetch(&quot;/api/users&quot;);
    const data = await response.json();

    return data;
}</code></pre>
<p><code>await</code>는 Promise가 처리될 때까지 해당 <code>async</code> 함수의 다음 실행을 잠시 멈춘다.</p>
<p>중요한 점은 <strong>JS 전체 스레드를 멈추는 것은 아니다.</strong></p>
<h2 id="9-microtask-queue">9. Microtask Queue</h2>
<p>비동기 Queue가 하나만 있는 것은 아니다.</p>
<p>대표적으로:</p>
<pre><code class="language-text">Microtask Queue
→ Promise
→ async/await 후속 작업

Task Queue
→ setTimeout
→ setInterval
→ 일부 이벤트</code></pre>
<p>일반적으로 현재 코드가 끝난 후:</p>
<pre><code class="language-text">Call Stack
↓
Microtask Queue
↓
Task Queue</code></pre>
<p>순서로 처리된다.</p>
<h2 id="10-실행-순서-문제">10. 실행 순서 문제</h2>
<pre><code class="language-js">console.log(&quot;A&quot;);

setTimeout(() =&gt; console.log(&quot;B&quot;), 0);

Promise.resolve()
    .then(() =&gt; console.log(&quot;C&quot;));

console.log(&quot;D&quot;);</code></pre>
<p>결과:</p>
<pre><code class="language-text">A
D
C
B</code></pre>
<p>이유:</p>
<pre><code class="language-text">동기 코드
→ A, D

Microtask
→ C

Task
→ B</code></pre>
<h2 id="핵심-정리-5">핵심 정리</h2>
<pre><code class="language-text">JavaScript
→ 싱글 스레드
→ Call Stack 하나

비동기 작업
→ 브라우저 Web API가 처리

작업 완료
→ Queue로 이동

Event Loop
→ Call Stack이 비면 Queue 작업 실행

Promise
→ 비동기 작업의 상태와 결과 표현

async / await
→ Promise를 읽기 쉽게 사용하는 문법

실행 우선순위
→ 동기 코드
→ Microtask
→ Task Queue</code></pre>
<h1 id="part-7-브라우저--dom--event">Part 7. 브라우저 / DOM / Event</h1>
<h2 id="1-브라우저에서-js의-역할">1. 브라우저에서 JS의 역할</h2>
<p>브라우저는 HTML, CSS, JavaScript를 함께 처리한다.</p>
<pre><code class="language-text">HTML → 구조
CSS → 스타일
JavaScript → 동작</code></pre>
<p>JavaScript는 브라우저가 제공하는 API를 통해 화면과 상호작용한다.</p>
<h2 id="2-dom">2. DOM</h2>
<p>DOM(Document Object Model)은 HTML 문서를 <strong>JavaScript가 다룰 수 있는 객체 구조로 표현한 것</strong>이다.</p>
<pre><code class="language-html">&lt;button id=&quot;btn&quot;&gt;클릭&lt;/button&gt;</code></pre>
<pre><code class="language-js">const btn = document.querySelector(&quot;#btn&quot;);</code></pre>
<p>여기서 <code>document</code>는 현재 HTML 문서를 나타내는 객체다.</p>
<h2 id="3-dom-조작">3. DOM 조작</h2>
<p>요소 찾기:</p>
<pre><code class="language-js">const title = document.querySelector(&quot;.title&quot;);</code></pre>
<p>내용 변경:</p>
<pre><code class="language-js">title.textContent = &quot;Hello&quot;;</code></pre>
<p>스타일 변경:</p>
<pre><code class="language-js">title.style.fontSize = &quot;20px&quot;;</code></pre>
<p>요소 생성:</p>
<pre><code class="language-js">const div = document.createElement(&quot;div&quot;);
div.textContent = &quot;new&quot;;</code></pre>
<h2 id="4-window와-document">4. <code>window</code>와 <code>document</code></h2>
<p>브라우저에서 가장 상위 객체는 보통 <code>window</code>다.</p>
<pre><code class="language-js">window.alert(&quot;hello&quot;);
window.location;</code></pre>
<p><code>document</code>도 <code>window</code> 아래에 있다.</p>
<p>개념적으로:</p>
<pre><code class="language-text">window
 └─ document
     └─ HTML DOM</code></pre>
<h2 id="5-event">5. Event</h2>
<p>사용자의 행동이나 브라우저의 상태 변화를 Event라고 한다.</p>
<p>대표적인 이벤트:</p>
<pre><code class="language-text">click
input
change
submit
keydown
load
scroll</code></pre>
<p>이벤트 등록:</p>
<pre><code class="language-js">btn.addEventListener(&quot;click&quot;, () =&gt; {
    console.log(&quot;clicked&quot;);
});</code></pre>
<h2 id="6-event-객체">6. Event 객체</h2>
<p>이벤트 핸들러에는 이벤트 정보가 전달된다.</p>
<pre><code class="language-js">btn.addEventListener(&quot;click&quot;, (event) =&gt; {
    console.log(event.target);
});</code></pre>
<p><code>event.target</code>은 실제 이벤트가 발생한 요소를 의미한다.</p>
<h2 id="7-event-bubbling">7. Event Bubbling</h2>
<p>이벤트는 기본적으로 <strong>자식 요소에서 부모 요소 방향으로 전파</strong>된다.</p>
<pre><code class="language-html">&lt;div id=&quot;parent&quot;&gt;
    &lt;button id=&quot;child&quot;&gt;Click&lt;/button&gt;
&lt;/div&gt;</code></pre>
<p>버튼 클릭 시:</p>
<pre><code class="language-text">button
→ div
→ body
→ document</code></pre>
<p>방향으로 이벤트가 올라간다.</p>
<p>이를 <strong>Event Bubbling</strong>이라고 한다.</p>
<h2 id="8-event-capturing">8. Event Capturing</h2>
<p>반대로 부모에서 자식 방향으로 내려가는 단계도 있다.</p>
<pre><code class="language-text">document
→ body
→ div
→ button</code></pre>
<p>이를 <strong>Capturing</strong>이라고 한다.</p>
<p>전체 흐름은:</p>
<pre><code class="language-text">Capturing
↓
Target
↓
Bubbling</code></pre>
<p>이다.</p>
<h2 id="9-preventdefault">9. <code>preventDefault()</code></h2>
<p>브라우저의 기본 동작을 막는다.</p>
<p>예를 들어 form은 submit 시 기본적으로 페이지를 이동하거나 새로고침할 수 있다.</p>
<pre><code class="language-js">form.addEventListener(&quot;submit&quot;, (event) =&gt; {
    event.preventDefault();
});</code></pre>
<p>SPA나 React에서도 자주 접한다.</p>
<h2 id="10-stoppropagation">10. <code>stopPropagation()</code></h2>
<p>이벤트가 부모로 전파되는 것을 막는다.</p>
<pre><code class="language-js">button.addEventListener(&quot;click&quot;, (event) =&gt; {
    event.stopPropagation();
});</code></pre>
<p>즉:</p>
<pre><code class="language-text">preventDefault()
→ 브라우저 기본 동작 차단

stopPropagation()
→ 이벤트 전파 차단</code></pre>
<h2 id="핵심-정리-6">핵심 정리</h2>
<pre><code class="language-text">DOM
→ HTML을 JS에서 다룰 수 있게 객체 구조로 표현

document
→ 현재 HTML 문서

window
→ 브라우저의 최상위 객체

Event
→ 클릭, 입력, 제출 등 사용자/브라우저 동작

Event Bubbling
→ 자식 → 부모

Event Capturing
→ 부모 → 자식

preventDefault()
→ 기본 동작 차단

stopPropagation()
→ 이벤트 전파 차단</code></pre>
<h1 id="part-8-fetch--http--cors--인증">Part 8. fetch / HTTP / CORS / 인증</h1>
<h2 id="1-fetch">1. <code>fetch</code></h2>
<p>브라우저에서 HTTP 요청을 보낼 때 사용하는 기본 API다.</p>
<pre><code class="language-js">const response = await fetch(&quot;/api/users&quot;);
const data = await response.json();</code></pre>
<p>흐름은:</p>
<pre><code class="language-text">브라우저
→ HTTP 요청
→ 서버
→ HTTP 응답
→ JSON 변환</code></pre>
<h2 id="2-http-요청-기본-구조">2. HTTP 요청 기본 구조</h2>
<p>주로 쓰는 메서드:</p>
<pre><code class="language-text">GET    → 조회
POST   → 생성
PUT    → 전체 수정
PATCH  → 일부 수정
DELETE → 삭제</code></pre>
<p>예:</p>
<pre><code class="language-js">await fetch(&quot;/api/users&quot;, {
    method: &quot;POST&quot;,
    headers: {
        &quot;Content-Type&quot;: &quot;application/json&quot;
    },
    body: JSON.stringify({
        name: &quot;Kim&quot;
    })
});</code></pre>
<h2 id="3-json">3. JSON</h2>
<p>프론트와 백엔드가 데이터를 주고받을 때 매우 자주 사용한다.</p>
<p>JS 객체:</p>
<pre><code class="language-js">const user = {
    name: &quot;Kim&quot;,
    age: 20
};</code></pre>
<p>JSON 문자열로 변환:</p>
<pre><code class="language-js">JSON.stringify(user);</code></pre>
<p>JSON 문자열을 객체로 변환:</p>
<pre><code class="language-js">JSON.parse(json);</code></pre>
<h2 id="4-fetch에서-주의할-점">4. <code>fetch</code>에서 주의할 점</h2>
<p><code>fetch</code>는 404, 500 응답이 와도 Promise 자체는 보통 <code>reject</code>되지 않는다.</p>
<pre><code class="language-js">const response = await fetch(&quot;/api/users&quot;);

if (!response.ok) {
    throw new Error(&quot;요청 실패&quot;);
}</code></pre>
<p>즉:</p>
<pre><code class="language-text">네트워크 자체 실패
→ reject

HTTP 404 / 500
→ 응답은 정상적으로 도착
→ response.ok 확인 필요</code></pre>
<h2 id="5-same-origin-policy">5. Same-Origin Policy</h2>
<p>브라우저는 보안을 위해 다른 출처의 리소스 접근을 제한한다.</p>
<p>Origin은 보통 다음 3개로 결정된다.</p>
<pre><code class="language-text">protocol + host + port</code></pre>
<p>예:</p>
<pre><code class="language-text">https://example.com:443</code></pre>
<p>이 중 하나라도 다르면 다른 Origin으로 본다.</p>
<h2 id="6-cors">6. CORS</h2>
<p>CORS는 서버가 브라우저에게:</p>
<blockquote>
<p>&quot;이 Origin에서 오는 요청은 허용해도 된다&quot;</p>
</blockquote>
<p>라고 알려주는 방식이다.</p>
<p>예를 들어:</p>
<pre><code class="language-text">Frontend
http://localhost:3000

Backend
http://localhost:8080</code></pre>
<p>포트가 다르므로 서로 다른 Origin이다.</p>
<p>서버가 적절한 CORS 헤더를 주지 않으면 브라우저가 요청을 차단할 수 있다.</p>
<p>중요한 점:</p>
<blockquote>
<p>CORS는 브라우저의 보안 정책이다.</p>
</blockquote>
<p>서버끼리 통신할 때는 일반적으로 CORS 문제가 발생하지 않는다.</p>
<h2 id="7-cookie">7. Cookie</h2>
<p>브라우저에 저장되는 작은 데이터다.</p>
<p>서버가:</p>
<pre><code class="language-text">Set-Cookie</code></pre>
<p>헤더를 보내면 브라우저가 저장하고 이후 요청에 자동으로 포함할 수 있다.</p>
<p>주로:</p>
<pre><code class="language-text">로그인 상태
세션 ID
사용자 설정</code></pre>
<p>등에 사용한다.</p>
<h2 id="8-session">8. Session</h2>
<p>Session 방식은 로그인 정보를 주로 서버에서 관리한다.</p>
<pre><code class="language-text">로그인
↓
서버가 Session 생성
↓
Session ID 발급
↓
브라우저 Cookie에 저장
↓
이후 요청마다 Session ID 전달</code></pre>
<p>서버는 Session ID를 보고 사용자를 식별한다.</p>
<h2 id="9-jwt">9. JWT</h2>
<p>JWT 방식에서는 사용자 정보를 포함한 토큰을 클라이언트에 전달한다.</p>
<pre><code class="language-text">로그인
↓
서버가 JWT 발급
↓
클라이언트가 저장
↓
요청마다 JWT 전달</code></pre>
<p>주로:</p>
<pre><code class="language-text">Authorization: Bearer &lt;token&gt;</code></pre>
<p>형태로 전달한다.</p>
<h2 id="10-session-vs-jwt">10. Session vs JWT</h2>
<p>핵심 차이는 상태를 어디서 관리하느냐다.</p>
<pre><code class="language-text">Session
→ 서버가 로그인 상태 관리

JWT
→ 토큰 자체에 인증 정보 포함</code></pre>
<p>단, JWT라고 해서 무조건 더 좋거나 더 최신 방식인 것은 아니다.</p>
<p>서비스 구조와 보안 요구사항에 따라 선택한다.</p>
<h2 id="핵심-정리-7">핵심 정리</h2>
<pre><code class="language-text">fetch
→ 브라우저 HTTP 요청 API

JSON.stringify()
→ JS 객체 → JSON 문자열

JSON.parse()
→ JSON 문자열 → JS 객체

response.ok
→ HTTP 요청 성공 여부 확인

Same-Origin Policy
→ 브라우저가 다른 출처 접근 제한

CORS
→ 서버가 다른 Origin의 요청을 허용하는 방식

Cookie
→ 브라우저에 저장되는 데이터

Session
→ 서버 중심 인증 상태 관리

JWT
→ 토큰 기반 인증</code></pre>
<h1 id="part-9-nodejs--npm--packagejson--module--vite">Part 9. Node.js / npm / package.json / Module / Vite</h1>
<h2 id="1-nodejs">1. Node.js</h2>
<p>Node.js는 <strong>브라우저 밖에서도 JavaScript를 실행할 수 있게 해주는 런타임 환경</strong>이다.</p>
<pre><code class="language-text">JavaScript = 언어

Browser
→ JavaScript + Browser API

Node.js
→ JavaScript + Node API</code></pre>
<p>즉 Node.js가 있으면 서버, CLI, 빌드 도구 등을 JS로 만들 수 있다.</p>
<h2 id="2-npm">2. npm</h2>
<p>npm은 <strong>Node.js 패키지 관리자</strong>다.</p>
<pre><code class="language-bash">npm install axios</code></pre>
<p>외부 라이브러리를 설치하고 관리할 수 있다.</p>
<p>대표 명령:</p>
<pre><code class="language-text">npm install
→ 의존성 설치

npm install 패키지명
→ 패키지 추가

npm run dev
→ package.json에 정의된 dev 명령 실행</code></pre>
<h2 id="3-packagejson">3. <code>package.json</code></h2>
<p>프로젝트 정보와 의존성을 관리하는 파일이다.</p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;my-app&quot;,
  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;vite&quot;
  },
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;^19.0.0&quot;
  }
}</code></pre>
<p>주요 역할:</p>
<pre><code class="language-text">프로젝트 정보
의존성 목록
실행 명령어
빌드 설정</code></pre>
<h2 id="4-node_modules">4. <code>node_modules</code></h2>
<p>npm으로 설치한 라이브러리들이 들어가는 폴더다.</p>
<pre><code class="language-text">node_modules/</code></pre>
<p>보통 용량이 크기 때문에 Git에는 올리지 않는다.</p>
<p>대신 <code>package.json</code>과 lock 파일을 공유하고:</p>
<pre><code class="language-bash">npm install</code></pre>
<p>로 다시 설치한다.</p>
<h2 id="5-module">5. Module</h2>
<p>코드를 여러 파일로 나누고 필요한 것만 가져오는 방식이다.</p>
<pre><code class="language-js">export function add(a, b) {
    return a + b;
}</code></pre>
<p>다른 파일:</p>
<pre><code class="language-js">import { add } from &quot;./math.js&quot;;</code></pre>
<p>이런 방식을 <strong>ES Module(ESM)</strong>이라고 한다.</p>
<h2 id="6-commonjs">6. CommonJS</h2>
<p>Node.js에서는 과거에 CommonJS 방식도 많이 사용했다.</p>
<pre><code class="language-js">const express = require(&quot;express&quot;);</code></pre>
<p>내보내기:</p>
<pre><code class="language-js">module.exports = something;</code></pre>
<p>현재는 ES Module 방식도 많이 사용한다.</p>
<pre><code class="language-js">import express from &quot;express&quot;;</code></pre>
<h2 id="7-bundler">7. Bundler</h2>
<p>웹 프로젝트는 파일이 많아지기 때문에 여러 파일과 의존성을 정리해 브라우저가 사용할 수 있게 묶는 과정이 필요할 수 있다.</p>
<p>이 역할을 하는 도구가 <strong>Bundler</strong>다.</p>
<p>대표적으로:</p>
<pre><code class="language-text">Webpack
Rollup
esbuild</code></pre>
<h2 id="8-vite">8. Vite</h2>
<p>Vite는 현대 프론트엔드 개발에서 많이 사용하는 <strong>개발 서버 + 빌드 도구</strong>다.</p>
<pre><code class="language-bash">npm create vite@latest
npm run dev</code></pre>
<p>주요 역할:</p>
<pre><code class="language-text">개발 서버 실행
빠른 코드 변경 반영
모듈 처리
프로덕션 빌드</code></pre>
<p>React 프로젝트 만들 때 자주 보게 된다.</p>
<h2 id="9-개발-환경-전체-흐름">9. 개발 환경 전체 흐름</h2>
<p>대략 이렇게 연결된다.</p>
<pre><code class="language-text">JavaScript 코드
↓
npm으로 라이브러리 관리
↓
package.json에 의존성 기록
↓
Vite로 개발 서버 실행
↓
빌드
↓
브라우저에서 실행</code></pre>
<h2 id="핵심-정리-8">핵심 정리</h2>
<pre><code class="language-text">Node.js
→ 브라우저 밖에서 JS 실행

npm
→ JS 패키지 관리자

package.json
→ 프로젝트 설정 + 의존성 + 실행 명령

node_modules
→ 설치된 패키지 저장

ES Module
→ import / export

CommonJS
→ require / module.exports

Bundler
→ 여러 파일과 의존성을 웹 실행 형태로 묶음

Vite
→ 개발 서버 + 빌드 도구</code></pre>
<h1 id="part-10-react를-이해하기-위한-javascript-핵심">Part 10. React를 이해하기 위한 JavaScript 핵심</h1>
<p>React는 새로운 언어가 아니라 <strong>JavaScript를 기반으로 UI를 만드는 라이브러리</strong>다. 그래서 아래 JS 개념을 잘 알면 React 코드가 훨씬 쉽게 읽힌다.</p>
<h2 id="1-구조-분해-할당">1. 구조 분해 할당</h2>
<p>React에서 매우 자주 등장한다.</p>
<pre><code class="language-js">const user = {
  name: &quot;Kim&quot;,
  age: 20
};

const { name, age } = user;</code></pre>
<p>배열 구조 분해:</p>
<pre><code class="language-js">const [a, b] = [10, 20];</code></pre>
<p>React의 <code>useState</code>도 같은 문법이다.</p>
<pre><code class="language-js">const [count, setCount] = useState(0);</code></pre>
<h2 id="2-spread와-불변성">2. Spread와 불변성</h2>
<p>React에서는 기존 객체를 직접 수정하기보다 <strong>새 객체를 만드는 방식</strong>을 많이 사용한다.</p>
<pre><code class="language-js">const user = {
  name: &quot;Kim&quot;,
  age: 20
};

const newUser = {
  ...user,
  age: 21
};</code></pre>
<p>배열도 동일하다.</p>
<pre><code class="language-js">const newArr = [...arr, 4];</code></pre>
<p>핵심은:</p>
<pre><code class="language-text">기존 값 직접 변경 X
→ 새로운 객체/배열 생성</code></pre>
<p>이걸 <strong>불변성(immutability)</strong>이라고 한다.</p>
<h2 id="3-map">3. <code>map</code></h2>
<p>배열을 다른 배열로 변환한다.</p>
<pre><code class="language-js">const nums = [1, 2, 3];

const doubled = nums.map(n =&gt; n * 2);</code></pre>
<p>React에서는 리스트 렌더링에 매우 자주 사용한다.</p>
<pre><code class="language-js">users.map(user =&gt; (
  &lt;div&gt;{user.name}&lt;/div&gt;
));</code></pre>
<h2 id="4-filter">4. <code>filter</code></h2>
<p>조건에 맞는 값만 남긴다.</p>
<pre><code class="language-js">const nums = [1, 2, 3, 4];

const even = nums.filter(n =&gt; n % 2 === 0);</code></pre>
<p>삭제 처리에도 자주 사용한다.</p>
<pre><code class="language-js">const newUsers = users.filter(user =&gt; user.id !== id);</code></pre>
<h2 id="5-find">5. <code>find</code></h2>
<p>조건에 맞는 첫 번째 값을 찾는다.</p>
<pre><code class="language-js">const user = users.find(user =&gt; user.id === 3);</code></pre>
<p>API 응답이나 상태에서 특정 데이터를 찾을 때 자주 사용한다.</p>
<h2 id="6-조건부-렌더링에-쓰이는-js-문법">6. 조건부 렌더링에 쓰이는 JS 문법</h2>
<p>삼항 연산자:</p>
<pre><code class="language-js">const message = login ? &quot;로그인됨&quot; : &quot;로그인 필요&quot;;</code></pre>
<p>React:</p>
<pre><code class="language-js">{login ? &lt;Main /&gt; : &lt;Login /&gt;}</code></pre>
<p>AND 연산자도 자주 사용한다.</p>
<pre><code class="language-js">{login &amp;&amp; &lt;Profile /&gt;}</code></pre>
<p><code>login</code>이 truthy일 때만 <code>&lt;Profile /&gt;</code>이 렌더링된다.</p>
<h2 id="7-optional-chaining">7. Optional Chaining</h2>
<p>값이 없을 수도 있을 때 안전하게 접근한다.</p>
<pre><code class="language-js">user.profile.name</code></pre>
<p><code>profile</code>이 <code>undefined</code>면 에러가 발생한다.</p>
<p>그래서:</p>
<pre><code class="language-js">user?.profile?.name</code></pre>
<p>처럼 사용할 수 있다.</p>
<p>API 데이터를 다룰 때 특히 자주 등장한다.</p>
<h2 id="8-nullish-coalescing">8. Nullish Coalescing</h2>
<p><code>null</code> 또는 <code>undefined</code>일 때 기본값을 사용한다.</p>
<pre><code class="language-js">const name = user.name ?? &quot;Unknown&quot;;</code></pre>
<p><code>||</code>와 차이가 있다.</p>
<pre><code class="language-js">0 || 100   // 100
0 ?? 100   // 0</code></pre>
<p><code>??</code>는 <code>null</code>, <code>undefined</code>만 비어 있는 값으로 판단한다.</p>
<h2 id="9-callback과-이벤트">9. Callback과 이벤트</h2>
<p>React 이벤트도 결국 함수를 전달하는 구조다.</p>
<pre><code class="language-js">function handleClick() {
  console.log(&quot;click&quot;);
}

&lt;button onClick={handleClick}&gt;
  Click
&lt;/button&gt;</code></pre>
<p>즉:</p>
<pre><code class="language-text">onClick
→ callback 함수 전달</code></pre>
<p>이다.</p>
<h2 id="10-closure와-react">10. Closure와 React</h2>
<p>React의 함수 컴포넌트와 Hook에서는 closure가 중요하다.</p>
<pre><code class="language-js">function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    console.log(count);
  }</code></pre>
<p><code>handleClick</code>은 선언될 당시의 <code>count</code> 값을 참조한다.</p>
<p>그래서 React에서 흔히 말하는:</p>
<pre><code class="language-text">stale closure</code></pre>
<p>문제도 JS의 closure 특성과 관련되어 있다.</p>
<h2 id="11-비동기와-react">11. 비동기와 React</h2>
<p>API 호출:</p>
<pre><code class="language-js">async function getUsers() {
  const response = await fetch(&quot;/api/users&quot;);
  const data = await response.json();

  return data;
}</code></pre>
<p>React에서는 이런 비동기 작업을 상태와 연결한다.</p>
<p>개념적으로:</p>
<pre><code class="language-text">API 요청
↓
응답
↓
state 변경
↓
컴포넌트 다시 렌더링</code></pre>
<p>React를 이해하려면 <code>Promise</code>, <code>async/await</code>가 중요한 이유다.</p>
<h2 id="핵심-정리-9">핵심 정리</h2>
<pre><code class="language-text">React에서 특히 중요한 JS

구조 분해 할당
→ const { name } = user

Spread
→ ...obj, ...arr

불변성
→ 기존 값 수정 대신 새 값 생성

map
→ 배열 변환 / 리스트 렌더링

filter
→ 조건에 맞는 값만 유지

find
→ 특정 값 검색

?. 
→ 안전한 속성 접근

??
→ null/undefined 기본값 처리

Callback
→ 이벤트 처리

Closure
→ Hook과 상태 이해에 중요

async/await
→ API 통신에 필수</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 12/03 데이터 저장은 어떡하지]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251203</link>
            <guid>https://velog.io/@squee_z_e/TIL-251203</guid>
            <pubDate>Wed, 03 Dec 2025 03:11:54 GMT</pubDate>
            <description><![CDATA[<h3 id="로컬-db-vs-디스크-저장">로컬 DB vs 디스크 저장</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th><strong>로컬 DB (Drift / Hive / Isar)</strong></th>
<th><strong>디스크 저장(JSON 등 파일)</strong></th>
</tr>
</thead>
<tbody><tr>
<td>구조</td>
<td>테이블/컬렉션(구조적)</td>
<td>파일 단위(비정형 가능)</td>
</tr>
<tr>
<td>장점</td>
<td>검색/정렬/필터링 강함</td>
<td>포맷 자유, 레포트·로그에 적합</td>
</tr>
<tr>
<td>단점</td>
<td>스키마·마이그레이션 필요</td>
<td>검색·부분 업데이트 비효율적</td>
</tr>
<tr>
<td>사용 목적</td>
<td>자주 조회/수정되는 데이터</td>
<td>변경 적고 “기록을 남기는” 데이터</td>
</tr>
<tr>
<td>실제 사례</td>
<td>채팅, 앱 캐시, 사용자 정보</td>
<td>로그, JSON 백업, PDF</td>
</tr>
</tbody></table>
<ul>
<li>DB = 데이터를 자주 읽고 관리하기 위한 것</li>
<li>디스크 저장 = 파일을 보존하거나 내보내기 위한 것</li>
</ul>
<h3 id="어떻게-저장할거임">어떻게 저장할거임?</h3>
<table>
<thead>
<tr>
<th>저장 방식</th>
<th>특징</th>
<th>장점</th>
<th>단점</th>
<th>주요 사용처</th>
</tr>
</thead>
<tbody><tr>
<td><strong>메모리 상태(Cubit/Provider 등)</strong></td>
<td>앱 실행 중에만 유지</td>
<td>가장 빠른 접근, 구조 단순</td>
<td>앱 종료 시 소멸</td>
<td>UI 임시 상태, 선택값 등</td>
</tr>
<tr>
<td><strong>SharedPreferences</strong></td>
<td>Key–Value 설정 저장</td>
<td>간단한 사용성, 가벼운 플래그 저장에 적합</td>
<td>복잡 구조 저장 어려움, 암호화 없음</td>
<td>다크모드, 온보딩 여부, 간단 설정</td>
</tr>
<tr>
<td><strong>flutter_secure_storage</strong></td>
<td>OS 보안 저장소 사용</td>
<td>암호화, 민감 정보 저장 적합</td>
<td>느린 I/O, 대량 저장 부적합</td>
<td>액세스 토큰, 인증 정보</td>
</tr>
<tr>
<td><strong>파일 저장(dart:io)</strong></td>
<td>JSON/텍스트/바이너리 파일 저장</td>
<td>자유도 높음, 로그/백업 저장 적합</td>
<td>직렬화 직접 처리, 동시성 고려 필요</td>
<td>로그, 레포트, 백업 데이터</td>
</tr>
<tr>
<td><strong>SQLite (sqflite)</strong></td>
<td>관계형 데이터베이스</td>
<td>복잡 쿼리/조인 가능, 검증된 안정성</td>
<td>SQL 직접 작성, 마이그레이션 부담</td>
<td>오프라인 DB, 검색/정렬이 많은 데이터</td>
</tr>
<tr>
<td><strong>Drift</strong></td>
<td>SQLite 기반 타입 안전 ORM</td>
<td>타입 안전 쿼리, 구조적 DB 설계 용이</td>
<td>러닝커브, 코드 생성 필요</td>
<td>로컬 데이터 구조가 복잡한 앱, 오프라인 동기화</td>
</tr>
<tr>
<td><strong>Hive</strong></td>
<td>키–값 기반 NoSQL</td>
<td>매우 빠른 속도, 모델 직렬화 지원</td>
<td>복잡 쿼리/조인 불가</td>
<td>캐시, 간단한 도메인 저장, 스냅샷</td>
</tr>
<tr>
<td><strong>Isar</strong></td>
<td>고성능 로컬 NoSQL DB</td>
<td>빠른 성능, 인덱스/필터 지원</td>
<td>마이그레이션 학습 필요</td>
<td>대량 로컬 데이터, 검색/필터 기능</td>
</tr>
</tbody></table>
<ul>
<li>머리에 넣어두자
앱 재시작 필요 없음 (일시 상태) → 메모리 상태
간단한 설정값 → SharedPreferences
민감 정보 → flutter_secure_storage
레포트·로그·백업 → 디스크 저장(JSON/PDF)
구조적 데이터(검색·정렬·통계)→ Drift or SQLite
캐시·간단 저장 → Hive or Isar</li>
<li>실제로는 혼용해야지
반복적으로 조회·필터링해야 하는 구조적 데이터는 Drift/Hive
레포트/기록성 데이터는 파일 저장(JSON 또는 이진 파일)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 12/02 추상화 니가 뭔데]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251202</link>
            <guid>https://velog.io/@squee_z_e/TIL-251202</guid>
            <pubDate>Wed, 03 Dec 2025 02:14:22 GMT</pubDate>
            <description><![CDATA[<h2 id="진행-상황">진행 상황</h2>
<hr>
<h3 id="1-모델을-freezed-기반으로-통일">1. 모델을 freezed 기반으로 통일</h3>
<h3 id="핵심-포인트">핵심 포인트</h3>
<ul>
<li>Freezed는 <strong>불변성(immutability)</strong> 확보</li>
<li><code>copyWith</code>, equality, toString 자동 생성</li>
<li>JSON 직렬화는 <code>json_serializable</code>로 자동 처리</li>
<li>enum 직렬화는 <code>JsonConverter</code>로 관리</li>
</ul>
<h4 id="개선-이유">개선 이유</h4>
<ul>
<li>모델 필드 변경 시 JSON 변환 코드를 직접 수정해야 하는 문제 해소</li>
<li>타입 안정성 확보</li>
<li>전체 모델 구조를 일관적으로 유지하기 용이</li>
</ul>
<h4 id="freezed-도입-효과">Freezed 도입 효과</h4>
<ul>
<li>생성자 + 직렬화 코드 자동화</li>
<li>state 관리 안정성 상승</li>
<li>유지보수 비용 감소</li>
</ul>
<hr>
<h3 id="2-json-직렬화--역직렬화-흐름-정리">2. JSON 직렬화 / 역직렬화 흐름 정리</h3>
<h4 id="역직렬화json-→-객체">역직렬화(JSON → 객체)</h4>
<ul>
<li>Dio가 JSON 문자열 → Map 변환</li>
<li>Retrofit이 응답 타입을 보고 <code>fromJson</code> 호출</li>
<li>json_serializable이 생성한 <code>_$ModelFromJson</code> 실행</li>
<li>Freezed 내부의 구현체로 모델 생성</li>
<li>최종적으로 Dart 객체로 전달</li>
</ul>
<h4 id="직렬화객체-→-json">직렬화(객체 → JSON)</h4>
<ul>
<li>Dio가 객체에 <code>toJson()</code> 존재 여부 확인</li>
<li>Freezed + json_serializable이 생성한 <code>_$ModelToJson</code> 실행</li>
<li>Map → JSON 문자열 변환 후 API 요청</li>
</ul>
<h4 id="핵심-요약">핵심 요약</h4>
<ul>
<li>Retrofit은 타입 기반으로 자동 직렬화</li>
<li>Freezed는 모델 구조를 일관적으로 유지</li>
<li>json_serializable은 실제 변환 로직 담당</li>
</ul>
<hr>
<h3 id="3-repository--datasource-추상화">3. Repository / DataSource 추상화</h3>
<h4 id="문제점">문제점</h4>
<ul>
<li>DataSource가 단일 구현체로 고정</li>
<li>테스트용 Mock과 실제 API 교체가 어려움</li>
<li>Repository가 구현체에 직접 결합됨</li>
</ul>
<h4 id="개선-방향">개선 방향</h4>
<ul>
<li><code>abstract DataSource</code> 정의</li>
<li>API 구현체, Mock 구현체를 별도 분리</li>
<li>Repository는 인터페이스에만 의존</li>
<li>main.dart에서 의존성 주입으로 구현체 교체</li>
</ul>
<h4 id="구조-요약">구조 요약</h4>
<pre><code>Cubit → Repository(인터페이스) → DataSource(인터페이스) → 구현체(API 또는 Mock)</code></pre><h4 id="장점">장점</h4>
<ul>
<li>테스트 쉬움</li>
<li>교체 유연성 확보</li>
<li>의존성 역전(DIP) 적용</li>
<li>단일 책임(SRP) 유지</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li>파일 증가</li>
<li>작은 프로젝트에서 과한 구조 가능</li>
</ul>
<hr>
<h3 id="4-전체-아키텍처-레이어-맵">4. 전체 아키텍처 레이어 맵</h3>
<pre><code>UI
  ↓
Cubit (State 관리)
  ↓
Repository (비즈니스 로직)
  ↓
DataSource (API, Mock)
  ↓
Model (Freezed)</code></pre><h4 id="핵심-원칙">핵심 원칙</h4>
<ul>
<li>상태는 불변성 기반</li>
<li>비즈니스 로직과 데이터 접근 분리</li>
<li>인터페이스 기반 의존</li>
<li>자동 코드 생성 활용</li>
</ul>
<hr>
<h2 id="진행-예정">진행 예정</h2>
<hr>
<h3 id="1-앱이-종료돼도-데이터가-유지되게">1. 앱이 종료돼도 데이터가 유지되게</h3>
<ul>
<li><strong>디스크 저장 방식</strong>이나 <strong>클라이언트 DB(Hive/Drift)</strong> 사용 고려</li>
<li>클라이언트 DB를 적극적으로 활용하는 앱 사례가 많으며,
우리도 앱 고도화 단계에서 <strong>변동성이 낮은 데이터(예: 리포트 등)를 로컬에 캐싱</strong>해 전달하는 방식으로 활용 가능</li>
</ul>
<blockquote>
<p>웹페이지 리뉴얼은 참여 안 할수도..?</p>
</blockquote>
<blockquote>
<p>나중에 reactiveX, 추상화, 디자인 패턴, flutter에서의 상태 관리와 타이밍 이슈 처리 전략 같은 부분을 좀 더 개념적이고 기술적인 부분 위주로 학습해서 포스팅 해야겠다. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 12/01 오래도 걸렸다]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251201</link>
            <guid>https://velog.io/@squee_z_e/TIL-251201</guid>
            <pubDate>Mon, 01 Dec 2025 08:18:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>11/28<del>주말 조금</del>12/01 진행 내용</p>
</blockquote>
<h2 id="진행-상황">진행 상황</h2>
<h3 id="1-상태-관리-구조-개선-sealed-class-→-freezed-전환">1. 상태 관리 구조 개선: Sealed Class → Freezed 전환</h3>
<p>상태 관리를 기존의 수동 sealed class 방식에서 Freezed 기반 구조로 정리함.
특히 화면 성격별로 <strong>플래그 기반 단일 State</strong>와 <strong>유니온 타입 State</strong>를 구분하여 적용함.</p>
<h4 id="mainscreen-플래그-기반-단일-state로-정리">MainScreen: 플래그 기반 단일 State로 정리</h4>
<p>메인 화면은 동시에 여러 데이터를 들고 있고, 부분 업데이트가 잦음.
따라서 여러 클래스로 나누기보다 하나의 State 안에 플래그로 묶는 방식이 적합하다고 판단함.</p>
<pre><code class="language-dart">@freezed
class MainScreenState with _$MainScreenState {
  const factory MainScreenState({
    @Default(UserRegistrationData()) UserRegistrationData userData,
    @Default(&lt;Pet&gt;[]) List&lt;Pet&gt; pets,
    @Default(false) bool isLoading,
    String? error,
  }) = _MainScreenState;
}</code></pre>
<p>이 구조는 다음과 같은 장점을 가짐:</p>
<ul>
<li><code>copyWith()</code>로 필요한 부분만 갱신하기 쉬움</li>
<li>UI는 <code>if (state.isLoading)</code> 같은 단순 조건으로 분기함</li>
<li>등록 정보 + 펫 정보처럼 <strong>서로 다른 리소스를 동시에 관리하는 화면</strong>에 적합함</li>
</ul>
<h4 id="userregistration--petregistration-유니온-타입-유지">UserRegistration / PetRegistration: 유니온 타입 유지</h4>
<p>등록 플로우는 단계가 명확한 구조라 유니온 타입이 더 자연스러움.
각 단계가 “명시적으로 표현되는” 것이 중요하다고 판단해 Freezed의 union type을 그대로 유지함.</p>
<pre><code class="language-dart">@freezed
class PetRegistrationState with _$PetRegistrationState {
  const factory PetRegistrationState.initial(Pet pet) = PetRegistrationInitial;
  const factory PetRegistrationState.loaded(Pet pet) = PetRegistrationLoaded;
  const factory PetRegistrationState.loading() = PetRegistrationLoading;
  const factory PetRegistrationState.success(Pet pet) = PetRegistrationSuccess;
  const factory PetRegistrationState.failure(String message) = PetRegistrationFailure;
}</code></pre>
<p>유니온 타입의 장점은 다음과 같음:</p>
<ul>
<li><code>when</code>, <code>map</code>으로 <strong>모든 상태를 반드시 처리</strong>하게 되어 실수 방지</li>
<li>각 상태별로 필요한 데이터만 들고 있게 설계 가능함</li>
<li>단계가 명확한 “등록/입력 흐름”에 최적화되어 있음</li>
</ul>
<p><strong>정리:</strong></p>
<ul>
<li><strong>메인 화면 → 플래그 기반 단일 State</strong></li>
<li><strong>등록 플로우 → Freezed 유니온 타입</strong>
두 방식을 상황에 맞게 병행함.</li>
</ul>
<hr>
<h3 id="2-reactivex-패턴-적용-behaviorsubject--switchmap">2. ReactiveX 패턴 적용: BehaviorSubject + switchMap</h3>
<p>변경 사항 자동 반영, 요청 버전 관리, 타이밍 문제 해결을 위해 Repository 레이어에 ReactiveX 패턴을 도입함.</p>
<h4 id="behaviorsubject를-repository에-추가함">BehaviorSubject를 Repository에 추가함</h4>
<p>각 Repository에서 <code>BehaviorSubject&lt;String?&gt;</code>를 사용해 “어떤 유저의 데이터가 변경됐는지”를 스트림 형태로 발행함.</p>
<pre><code class="language-dart">final _userUpdateSubject = BehaviorSubject&lt;String?&gt;.seeded(null);

Stream&lt;String?&gt; get userUpdates =&gt; _userUpdateSubject.stream;

Future&lt;void&gt; registerUser(String userId, UserRegistrationData data) async {
  await dataSource.registerUser(userId, data);
  _userUpdateSubject.add(userId);
}</code></pre>
<p>BehaviorSubject의 핵심 특징은 다음과 같음:</p>
<ul>
<li>“마지막 emit 값”을 기억함</li>
<li>새로운 구독자가 붙으면 그 값을 즉시 emit함</li>
<li>Cubit이 늦게 구독해도 최신 데이터를 놓치지 않음</li>
</ul>
<h4 id="mainscreencubit에서-switchmap으로-요청-버전-관리함">MainScreenCubit에서 switchMap으로 요청 버전 관리함</h4>
<p>MainScreenCubit은 userUpdates 스트림을 구독하며,
<strong>switchMap을 이용해 이전 API 요청을 취소하고 마지막 요청만 처리하는 방식</strong>을 적용함.</p>
<pre><code class="language-dart">_userUpdateSubscription = userRegistrationRepository.userUpdates
    .where((userId) =&gt; userId != null &amp;&amp; _currentUserId == userId)
    .switchMap(
      (userId) =&gt; Stream.fromFuture(
        userRegistrationRepository.getUserDataByUserId(userId!),
      ),
    )
    .listen(
      (userData) {
        emit(state.copyWith(
          userData: userData ?? state.userData,
          error: null,
        ));
      },
    );</code></pre>
<p>이 방식의 장점은 다음과 같음:</p>
<ul>
<li>연속으로 빠르게 업데이트가 들어와도 <strong>마지막 이벤트만 유효</strong></li>
<li>오래 걸린 응답이 늦게 도착해 UI를 덮어쓰는 문제 방지</li>
<li>BehaviorSubject와 조합하면 “초기 로딩 + 자동 새로고침”이 자연스럽게 연결됨</li>
</ul>
<p>요약하면,
<strong>BehaviorSubject는 최신 이벤트 보장, switchMap은 요청 버전 관리</strong>
이 두 가지가 결합해 타이밍 이슈 전반이 해결됨.</p>
<hr>
<h3 id="3-fastapi-로컬-서버-구축">3. FastAPI 로컬 서버 구축</h3>
<p>FastAPI로 직접 로컬 서버를 구축해 실제 API 구조와 더 유사한 환경을 구성함.</p>
<h4 id="인메모리-db-구조-설계">인메모리 DB 구조 설계</h4>
<ul>
<li>Python 딕셔너리로 users, pets 저장</li>
<li><code>user_id_counter</code>, <code>pet_id_counter</code>로 ID 자동 증가</li>
<li>login ID와 내부 시스템 ID를 별도로 관리</li>
</ul>
<p>이 구조로 Flutter 측의 <code>loginUserId</code>와 서버 내부 primary key를 명확히 분리할 수 있었음.</p>
<h4 id="환경-구성">환경 구성</h4>
<p>FastAPI에서 CORS 설정 후 Flutter에서 바로 호출하도록 구성했고,
Flutter 앱에서는 환경 변수를 다음과 같이 주입하는 방식으로 관리함.</p>
<pre><code class="language-bash">flutter run --dart-define=API_BASE_URL=http://192.168.0.2:3000</code></pre>
<p>환경별 서버 주소를 유연하게 바꿀 수 있는 구조로 개선함.</p>
<hr>
<h3 id="4-retrofit--dio로-flutter-api-클라이언트-정리">4. Retrofit + Dio로 Flutter API 클라이언트 정리</h3>
<p>FastAPI와 통신하는 Flutter 클라이언트를 Retrofit + Dio 기반으로 재구성함.</p>
<ul>
<li><code>@RestApi()</code>, <code>@GET</code>, <code>@POST</code>로 인터페이스만 작성</li>
<li>JSON 직렬화와 실제 HTTP 요청은 자동 생성됨</li>
<li><code>build_runner</code>로 <code>*.g.dart</code>, <code>*.freezed.dart</code> 파일 생성</li>
</ul>
<p>일관된 API 모델과 상태 모델을 유지할 수 있고, 404 응답을 에러가 아닌 “데이터 없음”으로 처리하는 식의 흐름 제어가 용이함.</p>
<pre><code class="language-dart">if (e is DioException &amp;&amp; e.response?.statusCode == 404) {
  return null;
}</code></pre>
<p>등록 정보가 없을 때는 비정상 상황이 아니므로 이렇게 처리함.</p>
<hr>
<h3 id="5-로그인-및-초기-로딩-처리-개선">5. 로그인 및 초기 로딩 처리 개선</h3>
<h4 id="listenwhen으로-불필요한-listener-호출-차단">listenWhen으로 불필요한 listener 호출 차단</h4>
<pre><code class="language-dart">listenWhen: (previous, current) {
  return previous.user?.id != current.user?.id;
},</code></pre>
<ul>
<li>user.id가 실제로 바뀔 때만 listener가 실행되도록 구성함</li>
<li>로딩 플래그 변화나 사소한 state 변경에는 반응하지 않도록 최적화함</li>
</ul>
<h4 id="초기-진입-시-데이터가-비어-보이는-문제-해결">초기 진입 시 데이터가 비어 보이는 문제 해결</h4>
<p>BlocListener는 첫 빌드 때 동작하지 않기 때문에,
필요한 경우 <code>addPostFrameCallback</code>으로 초기 <code>loadData</code>를 강제 실행하도록 구성함.</p>
<pre><code class="language-dart">if (!state.isLoading &amp;&amp;
    state.userData.nickname == null &amp;&amp;
    state.pets.isEmpty &amp;&amp;
    loginState.loginUserId != null) {
  WidgetsBinding.instance.addPostFrameCallback((_) {
    context.read&lt;MainScreenCubit&gt;().loadData(loginState.loginUserId!);
  });
}</code></pre>
<p>BehaviorSubject의 캐싱과 결합되면서
“초기 진입 / 로그인 변경 / 등록 완료 후 복귀 등 모든 타이밍에서 최신 상태 유지”가 가능해짐.</p>
<hr>
<blockquote>
<p>여기서부터 팀장님 피드백</p>
</blockquote>
<h2 id="진행-예정">진행 예정</h2>
<h3 id="1-repositorydatasource-추상화">1. Repository–DataSource 추상화</h3>
<p>현재 DataSource가 단일 구현체라 Repository가 구체 클래스에 직접 결합된 구조.
테스트용 목업과 실제 API를 동일한 흐름으로 교체하려면 DataSource를 <strong>인터페이스로 추상화</strong>하는 구조가 필요.</p>
<p><strong>개선 방향 요약</strong></p>
<ul>
<li><code>abstract UserRegistrationDataSource</code> 정의</li>
<li>Mock / API 구현체 분리</li>
<li>Repository는 인터페이스만 의존</li>
<li>저장 방식 변경 시에도 앱 흐름 유지</li>
</ul>
<hr>
<h3 id="2-freezed-state를-flag-기반으로-통일">2. Freezed State를 Flag 기반으로 통일</h3>
<p>MainScreen은 flag 기반 단일 state인데,
UserRegistration / PetRegistration은 union type 구성.</p>
<p>등록 플로우는 단계가 많지 않고 전이가 단순하므로,
MainScreen과 동일하게 <strong>단일 state + flag 기반</strong>이 더 적합한 구조.</p>
<p><strong>예시 형태</strong></p>
<pre><code class="language-dart">@freezed
class PetRegistrationState with _$PetRegistrationState {
  const factory PetRegistrationState({
    @Default(Pet.empty()) Pet pet,
    @Default(false) bool isLoading,
    String? error,
  }) = _PetRegistrationState;
}</code></pre>
<p><strong>핵심 개념</strong>
Freezed는 <strong>상태 변경 지점을 제한</strong>하는 목적.
state는 불변이며, 모든 변경은 <code>copyWith</code> 기반으로만 생성.
상태 추적, 디버깅, 변경 히스토리 파악이 쉬운 구조.</p>
<hr>
<h3 id="3-모델userregistrationdata--pet을-freezed로-전환">3. 모델(UserRegistrationData / Pet)을 Freezed로 전환</h3>
<p>현재 모델은</p>
<ul>
<li>직접 작성한 <code>fromJson</code> / <code>toJson</code></li>
<li><code>Equatable</code> 기반 비교
형태로 구성.</li>
</ul>
<p>필드 추가·변경 시 JSON 변환 코드도 매번 직접 수정해야 하는 구조라 유지보수 비용 증가.</p>
<p><strong>개선 방향</strong>
모델을 Freezed + json_serializable 기반으로 재구성.</p>
<p><strong>기대 효과 요약</strong></p>
<ul>
<li>JSON 직렬화 자동화</li>
<li>copyWith, equality, 불변성 자동 생성</li>
<li>타입 구조 일관성 확보</li>
<li>코드량 감소</li>
</ul>
<hr>
<blockquote>
<p>이번 리팩까지 하고나면 플러터 학습 마무리하고 앱 실제 구조 살펴볼 예정</p>
</blockquote>
<blockquote>
<p>서비스 리뉴얼 참여(약 1주)해서 프레임워크 안쓰고 순수 웹 3대장으로 UI 고치고 배포하는 과정에서 병목 등의 문제 해결..?</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 11/27 확실히 말끔해져 가는 코드]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251127</link>
            <guid>https://velog.io/@squee_z_e/TIL-251127</guid>
            <pubDate>Thu, 27 Nov 2025 10:11:51 GMT</pubDate>
            <description><![CDATA[<h3 id="느끼는-건데">느끼는 건데..</h3>
<p>제발 java랑 javascript 공부 좀 하자.. C python 해서 뭐할래
객체지향 프로그래밍도 더 깊게 공부해야 할 것 같다.</p>
<p>일에 바로 투입되지는 못하고.. 공부만 하고 있는 게 좀 죄송했다.
모두가 엄청 바쁘게 일 하는 상황에서 나만 1인분을 못하고 있었기 때문이다.
&#39;로그인-앱 등록-펫 등록&#39; 이 짧은 흐름 하나를 구현해놓고
피드백을 받아가며 여러 방식으로 고쳐나갔는데 뭘 하고 있는 건지 티도 안났다.</p>
<p><strong>근데? 오늘 처음 구현했던 코드를 보고 지금 코드를 보니 
실무 바로 들어가서 AI 써재꼈으면 진짜 큰일났겠다 싶었다!</strong></p>
<p>일단 하루 30분 할애해서 코드 보면서 피드백 해주시고..
어떤 식으로 생각해야 하는지, 어떤 패키지들을 적용해볼 수 있는지 하나하나 짚어주시는 게
사실 자원을 엄청 쓰고 있다는 걸 알고 있어서 공부를 열심히 하려곤 했다.
근데 베이스가 너무 부족해서 힘들고 학습이 되긴 하는 건지 몰랐는데
<strong>좋은 코드가 어떤 건지 보는 눈과 짜임새 있게 코드를 구성하는 사고력이 생겨가는 것 같다.</strong></p>
<p>물론 지금도 코드 쓰는 걸 버벅이고 생각의 흐름이 매끄럽지 못해 AI에게 많이 배우고 있지만..
진짜 뭣도 모르면서 AI에게 거의 모든 걸 맡겼던 내가 얼마나 잘못된 방식을 선택했는지 깨닫는다.</p>
<p>주니어 개발자 내 자신 화이팅...</p>
<hr>
<h3 id="오늘-한-내용">오늘 한 내용</h3>
<h4 id="1-레이어-구조-정리">1. 레이어 구조 정리</h4>
<ul>
<li>전체 구조를 <strong>UI → Cubit → Repository → DataSource</strong> 로 명확히 분리</li>
<li>UI에서 Repository를 직접 호출하던 부분 제거</li>
<li>Cubit이 비즈니스 로직과 데이터 접근을 전담하도록 구조 개선
→ 상태관리·의존성·테스트 용이성이 크게 향상됨</li>
</ul>
<h4 id="2-상태-클래스-개선">2. 상태 클래스 개선</h4>
<ul>
<li>모든 상태 클래스를 <code>sealed class + final class</code>로 재구성</li>
<li><code>switch</code> 표현식 사용 가능</li>
<li>모든 상태 분기를 <strong>컴파일 타임에 강제</strong>할 수 있어 안정성 확보</li>
</ul>
<h4 id="3-gorouter-실험">3. GoRouter 실험</h4>
<ul>
<li><strong>pageBuilder 방식</strong>: 부모 페이지(Page)가 실제로 생성되는 구조</li>
<li><strong>redirect 방식</strong>: 부모 페이지 없이 자식 경로로 곧바로 이동</li>
<li>AI는 redirect 방식이 URL 구조·뒤로가기 스택에 유리하다는데 뭐가 더 좋은지는..</li>
</ul>
<h4 id="4-mainscreen-구조-정비">4. MainScreen 구조 정비</h4>
<ul>
<li>메인 화면을 <code>StatefulWidget → StatelessWidget</code> 으로 변경</li>
<li>상태·데이터 로드를 담당하는 <strong>MainScreenCubit 신설</strong></li>
<li>로그인 상태 변화에 따라 Cubit이 사용자·펫 데이터를 자동 재로딩
→ UI는 “상태만 읽는 구조”로 단순화됨</li>
</ul>
<h4 id="5-자동-새로고침-로직-정비">5. 자동 새로고침 로직 정비</h4>
<p>1) 수동 refresh 방식 사용해봄</p>
<ul>
<li>등록 완료 후 UI 쪽에서 <code>refreshUser / refreshPets</code> 직접 호출</li>
<li>print 디버깅으로 호출 시점·rebuild 문제 확인</li>
</ul>
<p>2) Stream 기반 자동 동기화 사용해봄</p>
<ul>
<li>Repository에 <code>StreamController.broadcast()</code> 추가</li>
<li>데이터 변경 시 <code>stream.add(userId)</code>로 이벤트 발행</li>
<li>MainScreenCubit이 stream을 <code>listen()</code>하여
같은 <code>userId</code>일 때 자동으로 <code>refreshUser / refreshPets</code> 실행</li>
<li>구독은 <code>StreamSubscription</code>으로 관리, Cubit close에서 취소
→ 수동 refresh 불필요, 화면 자동 동기화 완성</li>
</ul>
<h4 id="6-사용자-정보-수정-플로우">6. 사용자 정보 수정 플로우</h4>
<ul>
<li>메인 화면에 유저 정보 수정 버튼 추가</li>
<li>기존 등록 플로우(닉네임 등 입력 화면)를 <strong>수정 모드</strong>로 재사용</li>
<li>기존 데이터가 TextField에 자동 세팅</li>
<li>수정 완료 시 stream 이벤트 → 메인 자동 새로고침</li>
</ul>
<h4 id="7-디버깅-및-개념-정리">7. 디버깅 및 개념 정리</h4>
<ul>
<li>Repository stream과 흐름 이해</li>
<li>Repository를 전역 Provider로 두는 이유:<ul>
<li>의존성 주입</li>
<li>Stream 공유</li>
<li>테스트 및 Mock 용이</li>
</ul>
</li>
</ul>
<hr>
<h3 id="피드백-받은-내용-한-번에-정리">피드백 받은 내용 한 번에 정리</h3>
<h4 id="1-로딩실패-상태-처리">1. <strong>로딩/실패 상태 처리</strong></h4>
<ul>
<li>새 데이터가 도착했을 때 Cubit이 로딩 중이거나 실패 상태라면 기존 데이터를 어떻게 유지/대체할지 타이밍을 고민해야 함</li>
</ul>
<h4 id="2-reactivexsubject-개념">2. <strong>ReactiveX/Subject 개념</strong></h4>
<ul>
<li>기본 Stream 외에 <code>BehaviorSubject</code> 같은 RX 개념도 존재. 마지막 emit 값을 캐싱해 두고 새 구독자가 즉시 현재 상태를 알 수 있어 UI 초기화에 유리</li>
</ul>
<h4 id="3-sealed-class-대안">3. <strong>sealed class 대안</strong></h4>
<ul>
<li>sealed class를 쓰지 않고 <code>MainState.loading</code>, <code>MainState.success</code>처럼 상태를 한 객체 안에 묶는 패턴도 있음  </li>
<li>더 immutable하게 만들고 싶다면 <code>freezed</code> 같은 코드 생성 패키지를 검토</li>
</ul>
<h4 id="4-실제-api-콜-테스트">4. <strong>실제 API 콜 테스트</strong></h4>
<ul>
<li>실제 REST API를 호출하는 플로우도 한번 테스트해 보자</li>
<li>간단한 서버를 띄우거나, 테스트용 HTTP 서비스를 이용해 보고, Retrofit 같은 REST 클라이언트도 경험해 보라고 권장</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 11/26 디버깅을 제대로 하자]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251126</link>
            <guid>https://velog.io/@squee_z_e/TIL-251126</guid>
            <pubDate>Wed, 26 Nov 2025 11:23:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>한 것 
go router 사용 라우트 관리, cubit provider 주입 시점 조절,  repo provider 사용</li>
</ul>
</blockquote>
<ul>
<li>못 한 것
provider 내부 동작 살펴보기, sealed class 사용</li>
<li>과제는 아니었지만 이상하게 만들어서 수정이 시급한 것
레이어 분리 제대로(UI가 레포 호출 바로 못하게 - cubit이 처리)</li>
</ul>
<p>어찌저찌했는데 머리에 다 들어온 건 아닌 듯</p>
<hr>
<blockquote>
<p>여기서부터 팀장님 피드백</p>
</blockquote>
<hr>
<h3 id="cubit-분리-전략">Cubit 분리 전략</h3>
<h4 id="cubit을-따로-만들지-않는-경우">Cubit을 따로 만들지 않는 경우</h4>
<p>등록 플로우 Cubit과 전역 리스트 Cubit을 분리하지 않고 하나로 운용할 때 두 방식 중 선택</p>
<h4 id="방식-1-파라미터--리프레시-호출">방식 1: 파라미터 + 리프레시 호출</h4>
<ul>
<li>업데이트가 필요한 화면으로 파라미터 전달</li>
<li>변경 시 리프레시 신호를 보내서 해당 화면이 데이터 다시 로드</li>
<li>장점: 구조 단순</li>
<li>단점: 수동 리프레시 필요, 여러 화면 동기화 어렵다</li>
<li>사용 시기: 단순 플로우</li>
</ul>
<h4 id="방식-2-repository-스트림-구독">방식 2: Repository 스트림 구독</h4>
<ul>
<li>Repository에 스트림을 두고 데이터 변경 시 이벤트 발행</li>
<li>Cubit 등이 스트림을 구독해 자동으로 상태 갱신</li>
<li>장점: 자동 동기화, 화면 여러 개여도 일관성 유지</li>
<li>단점: 구조 복잡</li>
<li>사용 시기: 데이터 공유가 많고 흐름이 복잡할 때</li>
</ul>
<p><strong>선택 기준</strong></p>
<ul>
<li>단순: 방식 1</li>
<li>복잡·다중 화면: 방식 2</li>
</ul>
<hr>
<h3 id="디버깅-습관">디버깅 습관</h3>
<h4 id="의식의-흐름을-제대로">의식의 흐름을 제대로</h4>
<ol>
<li>갱신이 필요한 위치 확인</li>
<li>갱신을 호출하는 코드 위치 확인</li>
<li>호출은 되었는지, 수신은 되었는지, 수행은 되었는지 로그로 확인</li>
</ol>
<p>이런게 자연스럽게 이뤄져야 함</p>
<h4 id="로그-활용-잘하기">로그 활용 잘하기</h4>
<p>에러 떴으면 뭐라 떴는 지 좀 잘 보기</p>
<hr>
<h3 id="go_router-메서드-요약">go_router 메서드 요약</h3>
<h4 id="contextgopath"><code>context.go(path)</code></h4>
<ul>
<li>스택 전체 대체</li>
<li>뒤로가기 불가</li>
<li>로그인 성공 후 메인 이동 등</li>
</ul>
<h4 id="contextpushpath"><code>context.push(path)</code></h4>
<ul>
<li>스택에 화면 추가</li>
<li>뒤로가기 가능</li>
<li>pop 결과값(Future) 받을 수 있음</li>
</ul>
<h4 id="contextpopresult"><code>context.pop([result])</code></h4>
<ul>
<li>현재 화면 제거</li>
<li>push로 온 화면에서만 가능</li>
<li>이전 화면으로 결과 전달 가능</li>
</ul>
<h4 id="contextreplacepath"><code>context.replace(path)</code></h4>
<ul>
<li>현재 화면만 교체</li>
<li>아래 스택 유지</li>
<li>뒤로가기 가능</li>
</ul>
<h4 id="contextcanpop"><code>context.canPop()</code></h4>
<ul>
<li>pop 가능 여부 확인</li>
</ul>
<h4 id="contextgonamedname-"><code>context.goNamed(name, …)</code></h4>
<ul>
<li>라우트 이름 기반 go</li>
<li>동작은 go와 동일</li>
</ul>
<h4 id="contextpushnamedname-"><code>context.pushNamed(name, …)</code></h4>
<ul>
<li>라우트 이름 기반 push</li>
<li>동작은 push와 동일</li>
</ul>
<h4 id="contextreplacenamedname-"><code>context.replaceNamed(name, …)</code></h4>
<ul>
<li>라우트 이름 기반 replace</li>
<li>동작은 replace와 동일</li>
</ul>
<hr>
<h3 id="ui-→-repository-직접-호출-금지">UI → Repository 직접 호출 금지</h3>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/f1aaf2ec-a8d4-45a5-befe-7eba2d6db90b/image.png" alt=""></p>
<p>레이어는 제대로 짜놓고 왜 ui가 cubit 부르는거랑 repo    부르는 걸 섞어놨니..</p>
<h4 id="잘못된-구조-ui-→-repository-직접-호출">잘못된 구조 (UI → Repository 직접 호출)</h4>
<pre><code class="language-dart">onPressed: () async {
  await context.read&lt;PetRepository&gt;().registerPet(userId, pet);
}</code></pre>
<h4 id="올바른-구조-ui-→-cubit-→-repository">올바른 구조 (UI → Cubit → Repository)</h4>
<pre><code class="language-dart">onPressed: () {
  context.read&lt;PetListCubit&gt;().addPet(userId, pet);
}

class PetListCubit extends Cubit&lt;PetListState&gt; {
  Future&lt;void&gt; addPet(String userId, Pet pet) async {
    await repository.registerPet(userId, pet);
    emit(PetListLoaded([...pets, pet]));
  }
}</code></pre>
<h4 id="이유">이유</h4>
<ul>
<li>비즈니스 로직은 Cubit에 집중</li>
<li>UI는 “상태와 이벤트”만 알고 Repository는 Cubit만 알도록 분리</li>
<li>테스트 용이성</li>
<li>상태 흐름 일관성 유지</li>
</ul>
<p>핵심: <strong>UI는 Cubit만 호출, Cubit이 Repository를 호출하는 구조</strong></p>
<hr>
<h3 id="goroute--redirect--builder--pagebuilder-정리">GoRoute : redirect / builder / pageBuilder 정리</h3>
<h4 id="redirect">redirect</h4>
<ul>
<li>라우팅 직전에 실행되어 <strong>경로만 변경하는 함수</strong></li>
<li>UI나 페이지를 만들지 않고 단순히 이동만 처리</li>
<li>인증 체크, 초기 플로우 분기 같은 상황에서 사용</li>
<li>부모 페이지 없이 바로 다른 경로로 이동하는 구조</li>
</ul>
<h4 id="builder">builder</h4>
<ul>
<li>child 라우트를 <strong>부모 위젯으로 감싸는 방식</strong></li>
<li>반환값이 Widget이기 때문에 Navigator 스택에는 올라가지 않음</li>
<li>공통 Scaffold, AppBar, BlocProvider 같은 “레이아웃 래핑”에 사용</li>
<li>화면 구조는 부모 위젯이 존재하지만 “페이지 단위”는 아님</li>
</ul>
<h4 id="pagebuilder">pageBuilder</h4>
<ul>
<li><strong>Navigator가 사용하는 실제 Page(MaterialPage 등)</strong>를 생성</li>
<li>부모 Page가 스택에 올라가고, 그 안에서 child가 보여지는 구조</li>
<li>뒤로가기 히스토리에도 부모 페이지가 포함됨</li>
<li>페이지 단위 공통 레이아웃이 필요할 때 사용</li>
</ul>
<h4 id="요약">요약</h4>
<ul>
<li><strong>redirect</strong>: 경로만 변경, UI 없음</li>
<li><strong>builder</strong>: 부모 위젯 생성, Page는 아님</li>
<li><strong>pageBuilder</strong>: 부모 Page 생성, 스택에 올라감</li>
</ul>
<hr>
<blockquote>
<ul>
<li>레이어 제대로 고치고 디버깅 제대로 해서 내가 못고쳤던 문제 해결해보기</li>
</ul>
</blockquote>
<ul>
<li>go_route에서 리다이렉트말고 페이지 빌더 사용해보기</li>
<li>스트림 구독 vs 리프레시 주기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 11/25 cubit으로 상태 관리를 해봤다]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251125</link>
            <guid>https://velog.io/@squee_z_e/TIL-251125</guid>
            <pubDate>Tue, 25 Nov 2025 10:23:39 GMT</pubDate>
            <description><![CDATA[<h3 id="cubit이란">Cubit이란</h3>
<ul>
<li>Bloc 라이브러리에서 제공하는 <strong>단순 상태관리 클래스</strong></li>
<li>메서드를 통해 상태 변경 → <code>emit()</code> 호출</li>
<li>UI는 Cubit의 상태 스트림을 구독해 화면을 갱신함</li>
</ul>
<hr>
<h3 id="cubit-기본-구조">Cubit 기본 구조</h3>
<ul>
<li><strong>State</strong>: 불변 객체, 여러 상태 클래스로 표현</li>
<li><strong>Cubit 클래스</strong>: 초기 상태 설정 → 메서드에서 <code>emit()</code> 호출</li>
<li>비동기/동기 모두 가능</li>
</ul>
<hr>
<h3 id="ui와-cubit-연결">UI와 Cubit 연결</h3>
<ul>
<li><strong>UI → Cubit</strong>: <code>context.read&lt;Cubit&gt;().method()</code></li>
<li><strong>Cubit → UI</strong>: 상태 스트림 전달</li>
<li><strong>BlocBuilder</strong>: 상태 기반으로 UI 빌드</li>
<li><strong>BlocListener</strong>: 네비게이션·팝업 등 부수 효과 처리</li>
<li><strong>BlocConsumer</strong>: builder + listener 동시 사용</li>
</ul>
<hr>
<h3 id="read--watch--select">read / watch / select</h3>
<ul>
<li><strong>read()</strong>: Cubit 인스턴스 가져오기 (액션 호출용), 리빌드 없음</li>
<li><strong>watch()</strong>: Cubit 상태 전체 구독, 리빌드 발생</li>
<li><strong>select()</strong>: 특정 필드만 구독, 리빌드 최적화</li>
</ul>
<hr>
<h3 id="cubit-동작-원리">Cubit 동작 원리</h3>
<ul>
<li><strong>단방향 흐름</strong>: <code>UI → Cubit → State → UI</code></li>
<li><strong>emit()</strong>: 새 상태를 스트림으로 내보냄</li>
<li><strong>상태는 반드시 새로운 객체</strong>로 생성될 것</li>
</ul>
<hr>
<h3 id="cubit-vs-bloc">Cubit vs Bloc</h3>
<ul>
<li><p><strong>공통</strong>: 단방향 데이터 흐름, 상태 기반 UI, 테스트 용이</p>
</li>
<li><p><strong>차이점</strong></p>
<ul>
<li>Cubit: 메서드 → emit, 코드량 적음, 단순 로직에 적합</li>
<li>Bloc: Event → Handler → emit, 복잡한 로직·플로우에 적합</li>
</ul>
</li>
</ul>
<hr>
<h3 id="언제-어떤-걸-쓰는가">언제 어떤 걸 쓰는가</h3>
<ul>
<li><strong>Cubit</strong>: 단일 화면, API 호출 + 로딩/성공 구조 등 대부분의 케이스</li>
<li><strong>Bloc</strong>: 상태 흐름 복잡, 다단계 이벤트, 상태머신이 필요한 기능</li>
</ul>
<hr>
<blockquote>
<p>여기부터는 팀장님 피드백 받은 내용들</p>
</blockquote>
<hr>
<h3 id="contextread--watch--select">context.read / watch / select</h3>
<ul>
<li><p>flutter_bloc이 BuildContext에 추가한 확장 함수</p>
</li>
<li><p><strong>read<T>()</strong> <code>context.read&lt;T&gt;()</code> </p>
<ul>
<li>Cubit/Bloc 인스턴스를 가져오기만 함</li>
<li>상태 변화와 무관, 리빌드 없음</li>
<li>버튼 클릭처럼 Cubit 메서드 실행할 때 사용</li>
</ul>
</li>
<li><p><strong>watch<T>()</strong> <code>context.watch&lt;T&gt;()</code></p>
<ul>
<li>Cubit 상태를 구독해 상태가 바뀔 때마다 위젯 리빌드</li>
<li>작은 위젯은 <code>context.watch&lt;Cubit&gt;().state</code>로 간단하게 사용</li>
</ul>
</li>
<li><p><strong>select()</strong> <code>context.select&lt;T, R&gt;(selector)</code></p>
<ul>
<li>상태의 특정 필드만 구독</li>
<li>selector 값이 바뀌지 않는 한 리빌드 없음 → 퍼포먼스 최적화</li>
</ul>
</li>
</ul>
<hr>
<h3 id="blocbuilder-vs-watch">BlocBuilder vs watch</h3>
<ul>
<li><strong>BlocBuilder</strong>: 어떤 Cubit을 구독하는지 명시적, 리빌드 범위 제어에 유리</li>
<li><strong>watch</strong>: 간단하게 상태를 읽고 싶을 때 편함</li>
<li>둘 다 Cubit 상태에 따라 UI를 다시 그림</li>
</ul>
<hr>
<h3 id="repository를-provider로-주입하자">Repository를 Provider로 주입하자</h3>
<ul>
<li><p>Repository를 Cubit 내부에서 직접 생성하는 대신
상위 위젯에서 Repository 인스턴스를 Provider로 만들어 하위에서 <code>context.read&lt;Repository&gt;()</code>로 가져와 사용</p>
</li>
<li><p>장점</p>
<ul>
<li>재사용성 증가</li>
<li>mock 교체 쉬워 테스트 편함</li>
<li>여러 Cubit이 동일한 Repository 공유 가능</li>
<li>의존성 주입 구조가 명확해짐</li>
</ul>
</li>
</ul>
<hr>
<h3 id="abstract-class-대신-sealed-class">abstract class 대신 sealed class</h3>
<ul>
<li>Dart 3에서 제공하는 sealed class는<ul>
<li>같은 파일 내부에서만 하위 타입 생성 가능</li>
<li>switch/case 문에서 누락된 상태가 있으면 컴파일러가 잡아줌</li>
<li>default 없이도 모든 상태를 안전하게 처리 가능</li>
</ul>
</li>
<li>Bloc/Cubit의 상태(State) 선언에 가장 안전한 방식</li>
</ul>
<hr>
<h3 id="repository를-직접-new-하지-말고-contextread로-주입하는-이유">Repository를 직접 new 하지 말고 <code>context.read()</code>로 주입하는 이유</h3>
<ul>
<li>Repository를 Cubit 안에서 직접 <code>new</code> 하면 Cubit이 특정 구현체에 묶여 결합도가 높아짐</li>
<li>테스트에서 Mock Repository로 교체하기 어렵고, 화면·플로우마다 다른 Repository를 쓰는 것도 불가능해짐</li>
<li>Provider는 위젯 트리 상단에서 Repository 인스턴스를 한 번만 만들어 두고, 하위에서는 <code>context.read&lt;Repository&gt;()</code>로 받아서 사용</li>
<li>Cubit은 생성 방식이나 구현체를 몰라도 되고, “필요한 의존성을 주입받아 쓰는” 구조가 됨</li>
<li>의존성 관리가 깔끔해지고 교체·모킹·재사용·라이프사이클 제어가 쉬워짐</li>
<li>결론적으로 Repository는 Provider가 만들고, Cubit은 read로 받아 쓰는 구조가 정석임</li>
</ul>
<hr>
<h3 id="go_router와-provider-주입-시점-조절">go_router와 Provider 주입 시점 조절</h3>
<ul>
<li>go_router는 라우트 단위로 화면을 구성하는 패키지</li>
<li>특정 플로우(회원가입, 결제 등)에서만 필요한 Cubit은 해당 라우트 내부에서 Provider로 생성</li>
<li>장점<ul>
<li>플로우 종료 시 Cubit이 자동 dispose</li>
<li>메모리 관리 깔끔</li>
<li>라우트 단위로 상태 수명 주기를 통제할 수 있음</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 11/24 provider를 이용해서 상태 관리를 해봤다]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251124</link>
            <guid>https://velog.io/@squee_z_e/TIL-251124</guid>
            <pubDate>Mon, 24 Nov 2025 09:50:40 GMT</pubDate>
            <description><![CDATA[<h3 id="flutter-상태관리-정리">Flutter 상태관리 정리</h3>
<p>Flutter에서는 화면을 구성하는 모든 요소가 위젯이며, 위젯들은 부모-자식 관계로 이루어진 트리 구조를 형성한다. 위젯은 불변이기 때문에, UI가 변경될 때는 기존 위젯을 수정하는 대신 새로운 위젯을 다시 그려주는 방식으로 동작한다.</p>
<hr>
<h3 id="statelesswidget과-statefulwidget">StatelessWidget과 StatefulWidget</h3>
<h4 id="공통점">공통점</h4>
<p>두 위젯 모두 <code>build(BuildContext)</code> 메서드 안에서 “이 위젯이 어떤 UI인지”를 선언한다.</p>
<h4 id="statelesswidget">StatelessWidget</h4>
<ul>
<li>내부에 변경되는 상태가 없다.</li>
<li><code>Text</code>, <code>Icon</code>, <code>Padding</code> 같은 정적인 UI에 사용된다.</li>
<li>동일한 입력이 주어지면 항상 동일한 결과를 반환하며, 부모 위젯이 재빌드될 때만 다시 그려진다.</li>
</ul>
<h4 id="statefulwidget">StatefulWidget</h4>
<ul>
<li>변경 가능한 상태를 가진다.</li>
<li><code>StatefulWidget</code>과 실제 상태를 보관하는 <code>State</code> 객체가 쌍으로 동작한다.</li>
<li><code>setState()</code>를 호출하면 해당 위젯만 다시 빌드되어 동적 UI를 만들 수 있다.</li>
<li>사용자 입력, 값 변화, 애니메이션 등 시간이 지나며 변하는 UI에 사용한다.</li>
</ul>
<hr>
<h3 id="상태state의-개념">상태(State)의 개념</h3>
<p>상태는 UI를 그리기 위해 필요한 데이터나 리소스를 의미한다. Flutter에서는 상태를 크게 두 가지로 나눈다.</p>
<h4 id="ephemeral-state-지역-상태">Ephemeral State (지역 상태)</h4>
<ul>
<li>한 위젯 내부에서만 잠시 필요한 상태</li>
<li>예: 체크박스 선택 여부, 특정 화면의 입력값</li>
<li><code>State + setState()</code>로 관리한다.</li>
</ul>
<h4 id="app-state-전역공유-상태">App State (전역/공유 상태)</h4>
<ul>
<li>여러 화면 또는 여러 위젯에서 함께 사용하는 상태</li>
<li>예: 로그인 정보, 장바구니, 앱 설정 값</li>
<li>InheritedWidget, Provider 등 상태관리 도구를 사용해 관리한다.</li>
</ul>
<hr>
<h3 id="주요-상태관리-방식-요약">주요 상태관리 방식 요약</h3>
<h4 id="statefulwidget--setstate">StatefulWidget + setState</h4>
<ul>
<li>간단한 로컬 상태에 적합</li>
<li>추가 패키지 없이 사용할 수 있고 이해하기 쉽다</li>
<li>하지만 공유 상태가 많아질수록 구조가 복잡해진다</li>
</ul>
<h4 id="inheritedwidget-계열-inheritednotifier--inheritedmodel">InheritedWidget 계열 (InheritedNotifier / InheritedModel)</h4>
<ul>
<li>트리 상단에서 하위 위젯들에게 상태를 전달하는 Flutter 기본 메커니즘</li>
<li>직접 활용하면 코드가 복잡해 주로 기반 개념으로만 사용된다</li>
</ul>
<h4 id="changenotifier--provider">ChangeNotifier + Provider</h4>
<ul>
<li>Flutter 공식이 초보자에게 추천하는 방식</li>
<li>ChangeNotifier 안에 상태를 보관하고 <code>notifyListeners()</code>로 변경을 알린다</li>
<li>Provider는 이 변경을 구독하고 필요한 위젯만 다시 빌드한다</li>
<li>전역 상태를 깔끔하게 관리할 수 있고, MultiProvider로 여러 상태를 함께 관리할 수 있다</li>
<li>상태가 커지면 ChangeNotifier가 비대해지는 단점이 있다</li>
</ul>
<h4 id="기타-상태-관리-패키지-bloc-riverpod-등">기타 상태 관리 패키지 (Bloc, Riverpod 등)</h4>
<ul>
<li>팀 규모나 프로젝트 복잡도에 따라 선택</li>
<li>구조적이고 테스트하기 쉬운 상태관리 패턴 제공</li>
<li>보일러플레이트가 줄거나, 더 명확한 아키텍처를 만들 수 있다</li>
<li>러닝 커브가 존재하고 패키지 의존성이 추가된다</li>
</ul>
<hr>
<h3 id="flutter-상태관리와-provider-그리고-repository-아키텍처-정리">Flutter 상태관리와 Provider, 그리고 Repository 아키텍처 정리</h3>
<p>Flutter에서 화면은 모두 위젯으로 이루어져 있으며, UI는 위젯 트리 형태로 그려진다. 위젯은 불변이기 때문에 상태가 변경되면 기존 위젯을 수정하는 것이 아니라 새로운 위젯을 다시 생성하는 방식으로 갱신된다. 이로 인해 상태가 필요 없는 UI는 <code>StatelessWidget</code>, 상태가 필요한 UI는 <code>StatefulWidget</code>으로 나뉘게 된다.</p>
<p><code>StatefulWidget</code>은 특정 위젯 내부에서만 유효한 “지역 상태(ephemeral state)”를 관리하기에는 충분하지만, 앱의 여러 화면에서 공유해야 하는 전역 상태를 관리하기에는 한계가 있다. setState는 오직 자신의 위젯 내부만 업데이트하기 때문에, 상태를 다른 화면에 전달하려면 각 단계마다 파라미터로 넘겨야 하고, 구조가 빠르게 복잡해진다.</p>
<p>이 문제를 해결하기 위해 Flutter에서 널리 사용하는 방식이 Provider다. Provider는 <code>ChangeNotifier</code> 기반으로 상태를 객체 하나에 모아두고, <code>notifyListeners()</code>를 호출했을 때 해당 객체를 구독하는 Consumer만 다시 빌드된다. 이 덕분에 부모 위젯이 데이터를 일일이 전달하지 않아도 되고, 화면 어디에서든 동일한 상태를 쉽게 접근하고 변경할 수 있다.</p>
<hr>
<blockquote>
<p>나는 provider로 전역적으로 상태관리가 가능하도록 해서 학습을 하고 있었는데, 
팀장님께서 실무에서 쓰는 상황들을 더 설명해주셨다.
아래 내용은 설명해주신걸 기억하기 위해 정리해둔 글이다.</p>
</blockquote>
<hr>
<h3 id="provider의-범위scope와-라이프사이클">Provider의 범위(Scope)와 라이프사이클</h3>
<p>Provider는 흔히 “전역 상태관리 도구”로 오해되지만, 실제로는 <strong>상태를 원하는 범위의 서브트리에만 주입할 수 있는 도구</strong>다.
즉, Provider는 반드시 루트에만 두라는 규칙이 없다.</p>
<p>예를 들어 앱 구조가 다음과 같다고 하자.</p>
<pre><code>MultiProvider
  └── App
        ├── Route A
        └── Route B</code></pre><p>Provider는 다음과 같은 범위 중 어디든 주입할 수 있다.</p>
<ul>
<li>App 위 (전역)</li>
<li>App 아래</li>
<li>Route A 트리 내부</li>
<li>Route B의 하위 위젯에서만 사용</li>
</ul>
<p>Provider는 <strong>자신이 주입된 위치에 따라 lifespan(생명주기)이 결정</strong>된다.
Provider가 트리에 붙어 있는 동안만 살아 있고, 해당 트리가 사라지면 Provider도 자동으로 dispose된다.</p>
<p>이 특징 때문에 실무에서는 다음과 같이 나누는 패턴이 일반적이다.</p>
<ul>
<li><p><strong>앱 전체에서 필요한 상태</strong></p>
<ul>
<li>예: 로그인한 User 정보, 현재 재생 중인 음악, 설정 값</li>
<li>→ 루트(MultiProvider) 또는 최상단에 주입</li>
</ul>
</li>
<li><p><strong>특정 플로우 안에서만 필요한 상태</strong></p>
<ul>
<li>예: 회원가입 단계별 입력값, 특정 페이지에서만 쓰는 임시 필터 값</li>
<li>→ 해당 Route 또는 해당 화면 트리에만 Provider 주입</li>
</ul>
</li>
</ul>
<p>이렇게 하면 “전역에 올릴 필요 없는 상태”를 전역에 올려서 메모리를 낭비하거나 잔존 데이터를 남기는 문제를 피할 수 있다.</p>
<hr>
<h3 id="provider-shadowing-가장-가까운-provider를-읽는다">Provider Shadowing: 가장 가까운 Provider를 읽는다</h3>
<p>같은 타입의 Provider가 여러 레벨에 있을 수도 있다.
이 경우 <code>Consumer&lt;T&gt;</code> 또는 <code>context.watch&lt;T&gt;()</code>는 <strong>트리 아래에서 위로 올라가며 가장 가까운 Provider</strong>를 읽는다.</p>
<p>즉,</p>
<ul>
<li>Consumer는 “이 값이 어디서 왔는지” 모른다.</li>
<li>단지 “내 트리에서 가장 가까운 Provider<T>”를 구독할 뿐이다.</li>
</ul>
<p>이 특징을 이용하면 다음과 같은 패턴도 가능하다.</p>
<ul>
<li>플레이어 상태는 전역 Provider에 두고</li>
<li>추천 페이지에서만 필요한 추천 알고리즘 상태는 추천 페이지 트리에 Provider를 두고</li>
<li>페이지를 벗어나면 추천 상태만 자연스럽게 dispose됨</li>
<li>하지만 전역 Provider에 있는 플레이어 상태는 유지됨</li>
</ul>
<p>실무에서 화면별 상태와 전역 상태를 자연스럽게 분리하는 데 매우 유용한 방식이다.</p>
<hr>
<h3 id="flutter에서도-repository-레이어가-필요한-이유">Flutter에서도 Repository 레이어가 필요한 이유</h3>
<p>상태관리와 별개로, Flutter의 아키텍처에서도 자바/Spring과 동일한 방식으로 Repository 패턴을 사용할 수 있다.</p>
<p>Repository의 목적은 명확하다.
** “UI나 비즈니스 로직이 데이터가 어디서, 어떻게 오는지 모르게 한다.&quot;**</p>
<p>Dart에는 interface 키워드는 없지만, <code>abstract class</code>를 통해 인터페이스와 동일한 역할을 만들 수 있다.
Cubit/Bloc은 Repository의 인터페이스만 의존하고, API/캐시/Mock 구현체는 뒤에서 교체 가능하다.</p>
<hr>
<h3 id="레이어-구조">레이어 구조</h3>
<p>여러 디자인 패턴들이 있지만 다 비슷비슷하다. 중요한건 인터페이스를 잘 만드는 것이다.
내가 참여할 파트의 구조는 이런 식으로 되어있다.</p>
<pre><code>Screen → Cubit/Bloc → Repository(인터페이스) → 구현체(API/캐시/Local)</code></pre><ul>
<li><strong>Screen</strong>
UI 렌더링, 사용자 입력을 Cubit/Bloc에게 전달</li>
<li><strong>Cubit/Bloc</strong>
화면 상태/비즈니스 로직 담당. Repository 인터페이스만 호출</li>
<li><strong>Repository (abstract class)</strong>
앱 관점의 “데이터 가져오기 API” 정의</li>
<li><strong>구현체(Impl)</strong>
실제 HTTP 요청, 로컬 DB, 캐시 등 상세한 데이터 접근 로직</li>
</ul>
<p>이 구조가 가지는 가장 큰 장점은 <strong>UI가 데이터 출처를 전혀 모른다는 점</strong>이다.
UI 입장에서는 단지 <code>getUser()</code>를 호출하는 것뿐이며,
그 뒤에서 실제 요청이 어떤 방식으로 이루어지는지는 Repository가 책임진다.</p>
<hr>
<h3 id="인터페이스를-잘-만드는-것이-핵심">인터페이스를 잘 만드는 것이 핵심</h3>
<p>Repository 패턴의 가치는 단순히 “코드 나누기”가 아니라,
<strong>UI가 비즈니스 의미만 이해하고 구현 세부 사항을 모르게 만드는 데 있다.</strong></p>
<p>Bad 예시:</p>
<pre><code class="language-dart">Future&lt;Response&gt; get(String url);</code></pre>
<p>Good 예시:</p>
<pre><code class="language-dart">abstract class MusicRepository {
  Future&lt;List&lt;Music&gt;&gt; getRecommended();
}</code></pre>
<p>이렇게 Repository 이름과 메서드가 “도메인 용어”를 표현해야
UI와 Cubit/Bloc이 데이터 구조 변경에 영향을 받지 않는다.</p>
<hr>
<h3 id="왜-repository-패턴을-써야-하냐">왜 Repository 패턴을 써야 하냐?</h3>
<ul>
<li>API 변경에도 UI 로직 수정 없음</li>
<li>테스트 시 Mock Repository로 손쉽게 대체 가능</li>
<li>로컬 캐시 + API + DB 같은 복합 데이터 소스 구성에 적합</li>
<li>화면 로직(Cubit/Bloc)이 깔끔해지고 유지보수성 증가</li>
<li>실무 앱 대부분이 이 방식으로 성장함</li>
</ul>
<p>즉, Flutter에서도 Repository 레이어는
데이터 로직을 독립시키고 UI·상태 로직을 깨끗하게 유지하는 핵심 아키텍처다.</p>
<blockquote>
<p>이제 cubit을 이용한 걸로 다시 한번 로그인-회원가입-ㅁㅁ 등록 흐름을 만들어볼거다.
Event → State 식으로 하면 복잡해지는 부분이? 있기 때문에 cubit만을 사용할 것 같다.
지금 일단 알려주신 단축키들 잘 활용해보자.
<code>cmd + .</code> : 전구기능 실행 / <code>option + shift + F</code> : 줄맞춤 / <code>Snippet + tap</code> : 자동 완성</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 11/20 flutter 도전기]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251120</link>
            <guid>https://velog.io/@squee_z_e/TIL-251120</guid>
            <pubDate>Thu, 20 Nov 2025 01:59:22 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>실무 배정을 앱 쪽으로 받게 되면서 flutter를 공부하게 됐다. 학습을 좀 깊게 해야할 듯</p>
</blockquote>
<p>개발환경 세팅은 <a href="https://www.youtube.com/watch?v=fBz3dlQ-EDM&amp;list=PLQt_pzi-LLfokCMjavyUqpAi_NdxVbgrq&amp;index=5">코딩 셰프의 mac 세팅 영상</a>을 따라하다가, <a href="https://docs.flutter.dev/get-started/quick?_gl=1*4amn84*_up*MQ..*_ga*ODg3OTM0MDkxLjE3NjM1MTM0ODY.*_ga_04YGWK0175*czE3NjM1MTM0ODUkbzEkZzAkdDE3NjM1MTM0ODUkajYwJGwwJGgw">공식 문서</a>보고 그냥 좀 더 쉬운 방식으로 했다.
막히는 건 GPT로 해결.. 경로 중간에 <strong>한글이 포함되지 않도록</strong> 미리 주의합시다</p>
<p><strong><a href="https://product.kyobobook.co.kr/detail/S000200473539">코드팩토리의 플러터 프로그래밍</a></strong> 책을 기반으로 개념을 학습하고 있다.</p>
<hr>
<h3 id="dart-언어의-장점">Dart 언어의 장점</h3>
<ul>
<li><strong>일관된 개발 경험</strong>: 모바일·웹·데스크톱을 하나의 언어로 개발</li>
<li><strong>빠른 개발 사이클</strong>: Hot Reload 지원</li>
<li><strong>타입 안정성</strong>: Null-safety 기반</li>
<li><strong>성능 최적화 용이</strong>: VM + AOT 조합으로 런타임 성능 확보</li>
<li><strong>UI 지향 언어 디자인</strong>: 비동기 처리·상태 관리에 자연스러운 문법</li>
</ul>
<h3 id="dart의-컴파일-방식jit--aot">Dart의 컴파일 방식(JIT / AOT)</h3>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/06755178-4112-4eaf-a74f-50ae24bb197f/image.png" alt=""></p>
<h4 id="jit-just-in-time">JIT (Just-In-Time)</h4>
<ul>
<li><strong>개발 단계용</strong></li>
<li>코드를 실행 중에 바로 컴파일, Hot Reload 가능, 초기 실행은 느리지만 반복 개발 속도가 빠름</li>
</ul>
<h4 id="aot-ahead-of-time">AOT (Ahead-Of-Time)</h4>
<ul>
<li><strong>배포용</strong></li>
<li>실행 전에 네이티브 코드로 미리 컴파일, 앱 시작 속도 빠르고 실행 성능 높음, 최종 빌드 시 용량은 커질 수 있음</li>
</ul>
<hr>
<h3 id="dart-기본-문법">Dart 기본 문법</h3>
<ul>
<li><p><strong>변수 선언</strong></p>
<ul>
<li><code>var</code>: 타입 추론 후 고정</li>
<li><code>dynamic</code>: 어떤 타입도 허용, 런타임에 타입 결정</li>
<li><code>final</code>: 런타임에 값 확정</li>
<li><code>const</code>: 컴파일 타임 상수, 불변 + 컴파일 최적화</li>
</ul>
</li>
<li><p><strong>기본 타입</strong></p>
<ul>
<li><code>String</code>, <code>int</code>, <code>double</code>, <code>bool</code></li>
</ul>
</li>
<li><p><strong>컬렉션 타입</strong></p>
<ul>
<li><code>List</code>: 순서 기반, 인덱스로 접근</li>
<li><code>Map</code>: key–value 구조</li>
<li><code>Set</code>: 중복 없음</li>
<li><code>enum</code>: 값 집합 정이</li>
</ul>
</li>
<li><p><strong>제어문</strong></p>
<ul>
<li><code>if / else</code>, <code>switch</code></li>
<li><code>for</code>, <code>for-in</code>, <code>while</code>, <code>do-while</code></li>
</ul>
</li>
<li><p><strong>함수 문법</strong></p>
<ul>
<li>반환값 + 매개변수</li>
<li>익명 함수 &amp; 람다 <code>(args) =&gt; expr</code></li>
</ul>
</li>
<li><p><strong>typedef</strong></p>
<ul>
<li>함수 타입에 별칭 부여</li>
</ul>
</li>
<li><p><strong>try / catch / finally</strong></p>
<ul>
<li><code>try</code>: 실행</li>
<li><code>catch(e)</code>: 예외 처리</li>
<li><code>on SomeException</code>: 특정 예외 처리</li>
<li><code>finally</code>: 무조건 실행</li>
</ul>
</li>
<li><p><strong>비동기 문법</strong></p>
<ul>
<li><code>async</code>, <code>await</code> 사용</li>
<li><code>Future</code>: 1회성 비동기 결과</li>
<li><code>Stream</code>: 지속적으로 여러 값을 방출</li>
<li><img src="https://velog.velcdn.com/images/squee_z_e/post/ee499bd5-1d40-4b82-8d17-242757cb0a40/image.png" alt=""></li>
</ul>
</li>
</ul>
<hr>
<h3 id="객체지향-개념-정리">객체지향 개념 정리</h3>
<h4 id="클래스-class">클래스 (Class)</h4>
<ul>
<li>데이터(필드)와 동작(메서드)을 하나로 묶는 기본 단위</li>
<li>실제 앱 구조는 거의 전부 “클래스 기반”으로 설계됨</li>
<li>Flutter 위젯도 모두 클래스 → Widget 이해 = Class 이해</li>
</ul>
<h4 id="인스턴스-instance--멤버-member">인스턴스 (Instance) &amp; 멤버 (Member)</h4>
<ul>
<li>클래스가 설계도라면 인스턴스는 그 설계도로 만든 <strong>실제 객체</strong> (붕어빵틀과 붕어빵)</li>
<li>인스턴스화는 클래스를 실제 객체로 생성하는 과정</li>
<li>생성된 인스턴스는 각각 독립된 상태를 가짐. 인스턴스 멤버(필드/메서드)가 객체마다 독립적으로 존재</li>
<li>클래스를 인스턴스화 하면 클래스의 인스턴스를 변수로 저장 가능</li>
</ul>
<h4 id="생성자-constructor">생성자 (Constructor)</h4>
<ul>
<li>객체 생성 시 초기값을 정의</li>
<li>기본 생성자 + named constructor 모두 지원</li>
<li>생성자 오버로딩 대신 named constructor 방식 사용</li>
</ul>
<h4 id="게터-getter--세터-setter">게터 (Getter) / 세터 (Setter)</h4>
<ul>
<li>게터는 객체의 내부 값을 읽기 위한 접근자</li>
<li>세터는 객체의 내부 값을 변경하기 위한 설정자</li>
<li>최근 변수의 값을 불변성(인스턴스화 후 변경할 수 없는) 특성으로 사용해서 세터는 잘 안씀</li>
</ul>
<h4 id="상속-extends">상속 (extends)</h4>
<ul>
<li>부모 클래스의 기능을 그대로 물려받음</li>
<li>하나의 클래스만 상속 가능(단일 상속)</li>
<li>재사용성과 일관성을 유지할 때 유용</li>
<li>Flutter에서 공통 UI/로직 구조를 만들 때 자주 등장</li>
</ul>
<h4 id="인터페이스-implements">인터페이스 (implements)</h4>
<ul>
<li>클래스가 반드시 가져야 할 기능 “목록”</li>
<li>Dart에서는 모든 클래스가 자동으로 인터페이스 역할 가능</li>
<li><strong>implements는 반드시 모든 메서드를 재정의해야 함</strong></li>
<li>명확한 규약/프로토콜을 만들 때 효과적</li>
</ul>
<h4 id="추상-클래스-abstract">추상 클래스 (abstract)</h4>
<ul>
<li>인스턴스를 만들 수 없음</li>
<li>공통 동작의 뼈대를 정의하는 용도</li>
<li>“기능의 템플릿” 역할</li>
<li>UI · 데이터 모델 계층에 자주 사용됨</li>
</ul>
<h4 id="오버라이드-override">오버라이드 (override)</h4>
<ul>
<li>상속받은 메서드/필드를 자식 클래스에서 재정의</li>
<li>클래스 간 동작을 상황에 맞게 다르게 표현</li>
<li><code>@override</code> 애노테이션을 사용해 의도를 명확히 표시함</li>
</ul>
<h4 id="믹스인-mixin">믹스인 (Mixin)</h4>
<ul>
<li>상속과 달리 “기능만” 주입하는 방식</li>
<li>여러 클래스에 공통 기능을 공유하고 싶을 때 사용</li>
<li>다중 상속이 없는 Dart에서 <strong>재사용성을 강화하는 핵심 기능</strong></li>
</ul>
<h4 id="제네릭-generics">제네릭 (Generics)</h4>
<ul>
<li>타입을 외부에서 주입해 재사용성과 안정성을 높임</li>
<li>클래스/함수/컬렉션 모두 제네릭을 사용 가능</li>
<li>&lt;&gt; 안에 타입을 명시 <code>List&lt;int&gt;</code>, <code>Map&lt;String, dynamic&gt;</code></li>
</ul>
<h4 id="스태틱-static">스태틱 (Static)</h4>
<ul>
<li>클래스에 직접 귀속되는 값/메서드</li>
<li>인스턴스마다 복사되지 않고 모두가 공유</li>
<li>상태 보관용으로 남용은 위험 → 설정/상수에 적합</li>
</ul>
<h4 id="캐스케이드-연산자-cascade-operator">캐스케이드 연산자 (Cascade Operator)</h4>
<ul>
<li>하나의 인스턴스에 대해 연속적으로 필드·메서드를 호출하는 문법</li>
<li>인스턴스 초기화나 설정 코드를 간결하게 작성할 때 유용</li>
</ul>
<hr>
<h3 id="플러터-구조와-통신">플러터 구조와 통신</h3>
<div style="display:flex; gap:8px;">
  <img src="https://velog.velcdn.com/images/squee_z_e/post/a83513cb-279b-4ffa-98d4-a246aad51d96/image.png" style="width:50%;" />
  <img src="https://velog.velcdn.com/images/squee_z_e/post/39401cca-7736-424f-9bdf-a998c12f2f6c/image.png" style="width:50%;" />
</div>

<h4 id="플러터-구조framework-→-engine-→-embedder">플러터 구조(Framework → Engine → Embedder)</h4>
<ul>
<li><strong>Framework(Dart)</strong>: Material/Cupertino, Widgets, Rendering 등 UI 계층. 개발자가 직접 사용하는 부분</li>
<li><strong>Engine(C/C++)</strong>: 렌더링, 텍스트 레이아웃, Dart VM, 이벤트 처리 등 핵심 런타임</li>
<li><strong>Embedder(Platform-specific)</strong>: iOS/Android/웹 등 플랫폼에 맞게 화면, 스레드, 플러그인, 이벤트 루프를 설정</li>
</ul>
<blockquote>
<p>개발자가 Dart로 작성한 UI 코드는 Engine에서 처리되어 플랫폼 Embedder 위에서 실행되는 구조</p>
</blockquote>
<h4 id="플러터-vs-리액트-네이티브-플랫폼-통신-구조">플러터 vs 리액트 네이티브 플랫폼 통신 구조</h4>
<ul>
<li><strong>Flutter</strong>: Dart 코드가 직접 Engine을 통해 <strong>위젯을 렌더링</strong>
플랫폼 기능은 <strong>메서드 채널</strong>로 호출해 Native 서비스와 통신
→ 브릿지 비용이 적고 렌더링을 엔진이 직접 수행하므로 성능 이점이 있음</li>
<li><strong>React Native</strong>: JavaScript ↔ Bridge ↔ Native 위젯 구조
→ 브릿지를 거쳐 OEM UI를 렌더링하므로 통신 비용이 크고 Flutter보다 오버헤드가 큼</li>
</ul>
<blockquote>
<p><strong>Flutter는 브릿지 없이 자체 엔진으로 UI를 그림</strong>, RN은 <strong>네이티브 UI를 호출하는 중간 브릿지가 필요</strong></p>
</blockquote>
<hr>
<h3 id="플러터에서의-위젯-widget">플러터에서의 위젯 (Widget)</h3>
<h4 id="모든-것이-위젯이다"><strong>모든 것이 위젯이다</strong></h4>
<div style="display:flex; gap:8px;">
  <img src="https://velog.velcdn.com/images/squee_z_e/post/6f881365-51f7-47ad-9ae0-2067674d8318/image.png" style="width:40%;" />
  <img src="https://velog.velcdn.com/images/squee_z_e/post/6727c8cc-3f1e-4829-a98a-51847f8d1f9a/image.png" style="width:25%;" />
  <img src="https://velog.velcdn.com/images/squee_z_e/post/db38c515-d6ff-4cd9-ba7d-d34d56783ea2/image.png" style="width:30%;" />
</div>

<h4 id="위젯-widget">위젯 (Widget)</h4>
<ul>
<li>플러터에서 화면을 구성하는 모든 요소의 최소 단위</li>
<li>텍스트, 버튼, 아이콘 같은 눈에 보이는 UI뿐만 아니라
레이아웃, 패딩, 정렬, 애니메이션 같은 보이지 않는 요소도 모두 위젯</li>
<li>플러터는 위젯 트리(widget tree) 구조로 화면을 구성
→ 부모 위젯 안에 자식 위젯이 포함되며, 전체 화면이 트리처럼 표현</li>
</ul>
<h4 id="child와-children의-차이">child와 children의 차이</h4>
<ul>
<li><table>
<thead>
<tr>
<th>구분</th>
<th>child</th>
<th>children</th>
</tr>
</thead>
<tbody><tr>
<td>수용 가능한 자식 수</td>
<td>1개</td>
<td>여러 개</td>
</tr>
<tr>
<td>타입</td>
<td>Widget</td>
<td>List<Widget></td>
</tr>
<tr>
<td>사용 목적</td>
<td>꾸미기, 감싸기, 위치 조정</td>
<td>레이아웃 배치, 리스트 구조</td>
</tr>
<tr>
<td>사용 가능 예</td>
<td>Container, SizedBox</td>
<td>Row, Column, ListView</td>
</tr>
</tbody></table>
</li>
</ul>
<hr>
<h3 id="콜백-함수-웹뷰-위젯-statelesswidget-vs-statefulwidget-setstate">콜백 함수, 웹뷰 위젯, StatelessWidget vs StatefulWidget, setState</h3>
<h4 id="콜백-함수">콜백 함수</h4>
<ul>
<li>특정 시점에 실행되도록 <strong>함수를 인자로 전달하는 구조</strong></li>
<li>버튼 이벤트, 제스처, WebView 이벤트 등에서 사용</li>
<li>UI가 이벤트 기반으로 동작하기 때문에 필수 개념</li>
</ul>
<h4 id="webview-위젯">WebView 위젯</h4>
<ul>
<li>앱 내부에서 <strong>웹 페이지를 렌더링</strong>하는 위젯</li>
<li>결제 화면, 약관, 외부 링크 구현에 사용</li>
<li>URL 로딩, 뒤로가기, JavaScript 통신 지원</li>
</ul>
<h4 id="statelesswidget">StatelessWidget</h4>
<ul>
<li><strong>내부 상태가 없는 위젯</strong></li>
<li>부모가 전달한 값(생성자 매개변수)이 변경될 때만 build가 다시 실행됨</li>
<li>단순 UI 요소에 적합</li>
</ul>
<h4 id="statefulwidget">StatefulWidget</h4>
<ul>
<li><strong>상태(State)를 가지고 UI를 갱신하는 위젯</strong></li>
<li>실제 상태는 State 객체에 저장됨</li>
<li>상태 변화에 따라 build가 재실행됨</li>
<li>입력값 변화, 애니메이션, 화면 갱신에 사용</li>
<li>(사진 참고) 상태 변경이 없는 생명 주기 / StatefulWidget 생성자의 매개변수가 변경됐을 때의 생명 주기 / State 자체적으로 build()를 재실행할 때 생명주기</li>
</ul>
<div style="display:flex; gap:8px;">
  <img src="https://velog.velcdn.com/images/squee_z_e/post/420d37be-49ce-4bd2-9214-7052307b27ed/image.png" style="width:30%;" />
  <img src="https://velog.velcdn.com/images/squee_z_e/post/22e01d9e-3492-4e95-b495-cb46a0353f75/image.png" style="width:30%;" />
  <img src="https://velog.velcdn.com/images/squee_z_e/post/7ff09d2b-e15e-414c-ad7d-d7e51c91f8aa/image.png" style="width:30%;" />
</div>


<h4 id="setstate-함수">setState 함수</h4>
<ul>
<li>StatefulWidget에서 <strong>UI를 다시 그릴 때 사용하는 함수</strong></li>
<li>setState 내부에서 상태 값을 변경하면 해당 위젯만 다시 build됨</li>
<li>전체 앱이 다시 그려지는 것이 아니라 필요한 위젯 트리만 갱신됨</li>
</ul>
<hr>
<h3 id="디자인-패턴-비교-mvc--mvp--mvvm--mvi">디자인 패턴 비교 (MVC / MVP / MVVM / MVI)</h3>
<p>  <img src="https://velog.velcdn.com/images/squee_z_e/post/c345a496-9a38-4bb0-99e0-79fc55f09812/image.png" alt=""></p>
<h4 id="아키텍처-패턴-비교표">아키텍처 패턴 비교표</h4>
<table>
<thead>
<tr>
<th>패턴</th>
<th>구조</th>
<th>데이터 흐름</th>
<th>View와 로직의 분리</th>
<th>장점</th>
<th>단점</th>
<th>Flutter와의 궁합</th>
</tr>
</thead>
<tbody><tr>
<td><strong>MVC</strong></td>
<td>Model – View – Controller</td>
<td>양방향</td>
<td>View와 Controller 결합도가 높음</td>
<td>단순, 이해 쉬움</td>
<td>앱 규모 커지면 Controller 비대</td>
<td>보통 안 맞음 (위젯 트리와 안 맞음)</td>
</tr>
<tr>
<td><strong>MVP</strong></td>
<td>Model – View – Presenter</td>
<td>양방향</td>
<td>Presenter가 View를 직접 업데이트</td>
<td>테스트 용이</td>
<td>View–Presenter 의존성 여전</td>
<td>Flutter와는 잘 안 맞음</td>
</tr>
<tr>
<td><strong>MVVM</strong></td>
<td>Model – View – ViewModel</td>
<td>단방향(or 단방향 데이터 바인딩)</td>
<td>ViewModel이 상태 제공, View는 구독</td>
<td>UI–로직 분리 명확, 유지보수 쉬움</td>
<td>바인딩 구조 이해 필요</td>
<td><strong>Flutter와 가장 잘 맞음</strong></td>
</tr>
<tr>
<td><strong>MVI</strong></td>
<td>Model – View – Intent</td>
<td>단방향 (Action → State)</td>
<td>완전한 단방향(state machine)</td>
<td>예측 가능, 상태 추적 쉬움</td>
<td>코드 양 많고 복잡</td>
<td><strong>Bloc, Redux 계열이 여기에 가까움</strong></td>
</tr>
</tbody></table>
<h3 id="왜-이런-디자인-패턴들이-중요한가">왜 이런 디자인 패턴들이 중요한가?</h3>
<h4 id="1-화면이-커질수록-코드가-복잡해지기-때문">1) 화면이 커질수록 코드가 복잡해지기 때문</h4>
<ul>
<li>버튼 하나 눌러도 다양한 로직이 필요한데, 이를 한 파일에서 처리하면 금방 “스파게티 코드”가 됨<ul>
<li>UI 업데이트 / 데이터 갱신 / API 요청 / 라우팅 처리</li>
</ul>
</li>
</ul>
<h4 id="2-ui와-비즈니스-로직을-분리하면-유지보수성이-올라감">2) UI와 비즈니스 로직을 분리하면 유지보수성이 올라감</h4>
<ul>
<li>UI 변경 시 로직 영향 없음</li>
<li>로직 변경 시 UI 영향 없음</li>
<li>테스트가 쉬워짐</li>
</ul>
<h4 id="3-플러터는-선언형-ui이기-때문에-상태state-설계가-핵심">3) 플러터는 선언형 UI이기 때문에 “상태(State)” 설계가 핵심</h4>
<ul>
<li>UI를 직접 수정하는 것이 아니라 <strong>상태를 바꾸면 UI가 자동 업데이트되는 구조</strong>
→ 즉, “상태 중심 아키텍처&quot;가 중요</li>
<li>그래서 MVC처럼 View와 Controller가 얽힌 구조는 잘 안 맞음
→ MVVM 또는 MVI 계열이 적합</li>
</ul>
<h3 id="플러터는-어떤-아키텍처-패턴과-잘-맞는가---mvvm-또는-mvi-계열">플러터는 어떤 아키텍처 패턴과 잘 맞는가? -&gt; MVVM 또는 MVI 계열</h3>
<ul>
<li>선언형 UI 방식(Build를 계속 다시 그리는 구조)</li>
<li>상태 중심(State-driven) 아키텍처</li>
<li>Provider, Riverpod, Bloc, Cubit 등 생태계 도구들이 모두 MVVM/MVI 구조를 지원</li>
</ul>
<blockquote>
<p>“MVC나 MVP처럼 View가 직접 로직을 호출하는 구조”보다
“ViewModel/Bloc 같은 상태 제공자를 구독해 UI를 만든다” 자연스러움</p>
</blockquote>
<ul>
<li>참고 : <a href="https://dev.to/arslanyousaf12/flutter-architecture-patterns-a-developers-journey-through-mvc-mvp-mvvm-and-mvi-bej">플러터 아키텍쳐 패턴</a> - 플러터가 MVVM과 잘 맞는이유</li>
</ul>
<h3 id="플러터에서-자주-사용하는-상태관리-도구들">플러터에서 자주 사용하는 상태관리 도구들</h3>
<table>
<thead>
<tr>
<th>상태관리 도구</th>
<th>철학</th>
<th>어떤 패턴에 가까움</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td><strong>setState</strong></td>
<td>직접 상태 변경</td>
<td>패턴 없음</td>
<td>소규모 화면에 적합</td>
</tr>
<tr>
<td><strong>Provider</strong></td>
<td>View ↔ ViewModel</td>
<td>MVVM</td>
<td>가장 기본적인 MVVM 구현 도구</td>
</tr>
<tr>
<td><strong>Riverpod</strong></td>
<td>상태 + DI 컨테이너</td>
<td>MVVM</td>
<td>Provider의 발전형, 강력함</td>
</tr>
<tr>
<td><strong>Bloc / Cubit</strong></td>
<td>Event → State</td>
<td>MVI</td>
<td>기업·대규모 프로젝트에서 선호</td>
</tr>
<tr>
<td><strong>Redux</strong></td>
<td>글로벌 상태 관리</td>
<td>MVI</td>
<td>단방향 구조, 규모 크면 좋음</td>
</tr>
<tr>
<td><strong>GetX</strong></td>
<td>반응형</td>
<td>MVVM</td>
<td>빠르지만 안정성 논란</td>
</tr>
</tbody></table>
<h3 id="만약-팀에서-bloc을-사용한다면">만약 팀에서 Bloc을 사용한다면?</h3>
<blockquote>
<p><strong>MVI(Model–View–Intent) 기반 단방향 상태관리 아키텍처를 사용한다는 의미</strong>
  View → Event → Bloc → 새로운 State → View rebuild</p>
</blockquote>
<ul>
<li><p>전통적인 MVVM은 ViewModel이 상태를 가지고 있고, View는 상태만 읽음(양방향 바인딩도 가능)</p>
</li>
<li><p>Bloç을 쓴다면(MVI) <strong>MVVM보다 더 엄격한 단방향 흐름</strong>을 가짐
→ 코드가 늘어나는 대신, <strong>예측 가능성 + 테스트 용이성 + 유지보수성</strong>이 뛰어나 회사들이 선호함</p>
<ul>
<li>View는 Event만 Bloc에게 전달</li>
<li>Bloc은 Event + 현재 State로 새로운 State를 생성</li>
<li>View는 이 State를 구독</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 11/12 오늘은 적을 게 없어서 알고리즘 공부]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251112</link>
            <guid>https://velog.io/@squee_z_e/TIL-251112</guid>
            <pubDate>Wed, 12 Nov 2025 09:39:25 GMT</pubDate>
            <description><![CDATA[<p>오늘은 지난 개발 회고하고.. 발표하고..
생성형 AI로 프롬포트 작성해서 음악을 생성해봤다.
개발을 한 게 없어서 적을 게 없지만
지금은 할 일을 다 마친 상태로 야근 중이기 때문에 뭐라도 끄적거려본다
어차피 집가면 공부하고 다시 적어야 할 듯</p>
<hr>
<h3 id="백준-1644-소수의-연속합---python">백준 1644. 소수의 연속합 - python</h3>
<p><a href="https://www.acmicpc.net/problem/1644">문제 링크</a> </p>
<h4 id="문제-설명">문제 설명</h4>
<p>하나 이상의 연속된 소수의 합으로 나타낼 수 있는 자연수들이 있다. 몇 가지 자연수의 예를 들어 보면 다음과 같다.</p>

<ul>
    <li>3 : 3 (한 가지)</li>
    <li>41 : 2+3+5+7+11+13 = 11+13+17 = 41 (세 가지)</li>
    <li>53 : 5+7+11+13+17 = 53 (두 가지)</li>
</ul>

<p>하지만 연속된 소수의 합으로 나타낼 수 없는 자연수들도 있는데, 20이 그 예이다. 7+13을 계산하면 20이 되기는 하나 7과 13이 연속이 아니기에 적합한 표현이 아니다. 또한 한 소수는 반드시 한 번만 덧셈에 사용될 수 있기 때문에, 3+5+5+7과 같은 표현도 적합하지 않다.</p>

<p>자연수가 주어졌을 때, 이 자연수를 연속된 소수의 합으로 나타낼 수 있는 경우의 수를 구하는 프로그램을 작성하시오.</p>

<h4 id="입력">입력</h4>
 <p>첫째 줄에 자연수 N이 주어진다. (1 ≤ N ≤ 4,000,000)</p>

<h4 id="출력">출력</h4>
 <p>첫째 줄에 자연수 N을 연속된 소수의 합으로 나타낼 수 있는 경우의 수를 출력한다.</p>

<h4 id="내-풀이">내 풀이</h4>
<p>뭔가 굉장히 별로인 방법인걸 알지만 슬라이딩 윈도우 구현 방식이 기억이 안나 꾸역꾸역 풀었다.
당연히 시공간 자원 많이 씀..
범위 설정을 자꾸 틀려서 연습을 많이 해야할 것 같다.
에라토스 뭐시기는 소수 구할 때 써야한다는 건 기억나는데 방법이 기억 안나서 나무위키 잠깐 봤음</p>
<pre><code class="language-python"># 백준 1644번 소수의 연속합

N = int(input())
answer = 0

# 예외 처리: 2 미만이면 만들 수 있는 연속 소수 합 없음
if N &lt; 2:
    print(0)
    exit()

# 에라토스테네스의 체 (i*i부터 지우기)
temp_list = [i for i in range(N + 1)]
temp_list[0] = 0
temp_list[1] = 0

limit = int(N ** 0.5)
for i in range(2, limit + 1):
    if temp_list[i] != 0:
        start = i * i
        step = i
        for m in range(start, N + 1, step):
            temp_list[m] = 0

prime_list = [v for v in temp_list if v &gt; 0]

# 두 포인터로 연속합 세기 (오른쪽 늘리고, N 이상이면 왼쪽 줄이기)
left = 0
right = 0
temp_sum = 0

while True:
    if temp_sum &gt;= N:
        if temp_sum == N:
            answer += 1
        temp_sum -= prime_list[left]
        left += 1
    else:
        if right == len(prime_list):
            break
        temp_sum += prime_list[right]
        right += 1

print(answer)</code></pre>
<p>아래는 gpt와 함께 코드를 효율적으로 다듬은 거다.</p>
<pre><code class="language-python"># 백준 1644번 소수의 연속합

N = int(input())
answer = 0

# 예외 처리: 2 미만이면 만들 수 있는 연속 소수 합 없음
if N &lt; 2:
    print(0)
    exit()

# 에라토스테네스의 체 (i*i부터 지우기)
temp_list = [i for i in range(N + 1)]
temp_list[0] = 0
temp_list[1] = 0

limit = int(N ** 0.5)
for i in range(2, limit + 1):
    if temp_list[i] != 0:
        start = i * i
        step = i
        for m in range(start, N + 1, step):
            temp_list[m] = 0

prime_list = [v for v in temp_list if v &gt; 0]

# 두 포인터로 연속합 세기 (오른쪽 늘리고, N 이상이면 왼쪽 줄이기)

left = 0
right = 0
temp_sum = 0

while True:
    if temp_sum &gt;= N:
        if temp_sum == N:
            answer += 1
        temp_sum -= prime_list[left]
        left += 1
    else:
        if right == len(prime_list):
            break
        temp_sum += prime_list[right]
        right += 1

print(answer)</code></pre>
<p>사실 오늘 스터디에서 두 문제를 풀어야 했지만 한 문제는 포기</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 11/11 공식문서를 제발 잘봅시다]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251111</link>
            <guid>https://velog.io/@squee_z_e/TIL-251111</guid>
            <pubDate>Tue, 11 Nov 2025 09:53:21 GMT</pubDate>
            <description><![CDATA[<h3 id="supabase-edge-function--cron-사용하기">supabase edge function + cron 사용하기</h3>
<p><a href="https://supabase.com/docs/guides/functions/schedule-functions">https://supabase.com/docs/guides/functions/schedule-functions</a>
<a href="https://supabase.com/docs/guides/database/vault">https://supabase.com/docs/guides/database/vault</a></p>
<p>수퍼베이스에서 기본 제공하는 스케줄러를 사용해서 예약 30분 전 슬랙 알림을 보내는 기능을 구현했다.</p>
<p>service_role_key를 헤더에 하드코딩하는 미친짓을 해보기도 하고
anon, publishable 키들을 사용해 보기도 하고
cron secret이라는 임시키를 만들어서 헤더에도 넣어보고 바디에도 넣어보았지만
401 에러만 뱉고 로그를 찍지도 않는 상황이 반복됐다.</p>
<ul>
<li>vault로 DB에 비밀 키 저장해봐라. 
edge function에는 env에 키 넣어두고 비교하게 하면 헤더에 하드코딩할 필요가 없다.</li>
<li>대부분 서비스가 JWT 토큰 검증할 텐데 이것도 그런 거 아니냐?</li>
</ul>
<p>등의 조언을 들으면서 로그를 찍어보다가,
일단 edge function이 JWT 토큰 형식으로 Authorization 헤더 검사를 하는 것 같다는 결론에 도달했다.
공식 문서에서도 anon 키를 쓰라고 되어 있어서,
쓸데없이 하던 거 다 갖다 버리고 공식 문서대로 하되 anon 키를 vault에 저장해두고 불러 쓰는 식으로 바꾸니까 됐다.</p>
<pre><code class="language-sql">-- **실행한 sql문 정리**

-- vault에 anon key 넣기

SELECT vault.create_secret(
  &#39;eyJ...&#39;,  -- Dashboard의 실제 anon key (JWT 형식)
  &#39;publishable_key&#39;
);

-- pg_cron job 업데이트 (Authorization 헤더 포함)

SELECT cron.alter_job(
  (SELECT jobid FROM cron.job WHERE command LIKE &#39;%check-reminders%&#39; LIMIT 1),
  command := $$
  SELECT
    net.http_post(
      url := &#39;https://프로젝트.supabase.co/functions/v1/check-reminders&#39;,
      headers := jsonb_build_object(
        &#39;Content-Type&#39;, &#39;application/json&#39;,
        &#39;Authorization&#39;, &#39;Bearer &#39; || (
          SELECT decrypted_secret 
          FROM vault.decrypted_secrets 
          WHERE name = &#39;publishable_key&#39;
        )
      ),
      body := &#39;{}&#39;::jsonb
    ) AS request_id;
  $$
);


-- 업데이트된 job 확인 (Authorization과 Bearer 포함 여부 확인용)
SELECT command 
FROM cron.job 
WHERE command LIKE &#39;%check-reminders%&#39;;


-- 수동으로 함수 호출 테스트

SELECT
  net.http_post(
    url := &#39;https://프로젝트.supabase.co/functions/v1/check-reminders&#39;,
    headers := jsonb_build_object(
      &#39;Content-Type&#39;, &#39;application/json&#39;,
      &#39;Authorization&#39;, &#39;Bearer &#39; || (
        SELECT decrypted_secret 
        FROM vault.decrypted_secrets 
        WHERE name = &#39;publishable_key&#39;
      )
    ),
    body := &#39;{}&#39;::jsonb
  ) AS request_id;</code></pre>
<p>해결된 이유는... supabase key에 대한 <strong>깃허브 토론</strong>과 <strong>공식 문서</strong>에서 실마리를 찾을 수 있었다.
이런거 많이 봐야지 개발실력이 는다는 유튜브 영상을 봤는데, 
트러블 슈팅할 때는 무조건 이것들부터 보는 습관을 길러야 할 것 같다.. 
API key가 있는 페이지만 봐서는 절대 이해 불가</p>
<p><a href="https://supabase.com/docs/guides/api/api-keys">https://supabase.com/docs/guides/api/api-keys</a>
<a href="https://github.com/orgs/supabase/discussions/29260">https://github.com/orgs/supabase/discussions/29260</a></p>
<p>최근 Supabase는 보안 강화를 위해 <code>anon / service_role</code> 키 대신
<code>publishable / secret</code> 키 체계를 새로 도입했는데,
이 새 키들(욕 아님)은 JWT 형식이 아니다.
그래서 Authorization 헤더에 <code>Bearer sb_publishable_...</code> 형태로 넣으면
Edge Function이 “JWT가 아님”이라며 무조건 401을 반환한다.</p>
<p>반면에 기존의 anon 키는 여전히 JWT 기반이어서
기본적으로 JWT 검증이 걸려 있는 Edge Function에서도 정상적으로 인증이 통과된다.
즉, publishable 키로는 안 되고 anon 키로는 되는 이유가 바로 이 구조 차이 때문이다.</p>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/1967a5b0-0f58-4e4d-9c85-e62eb2f9e267/image.png" alt=""></p>
<p>edge function에 자동으로 들어가있는 anon key는 로그 찍어보면 publishable형식이다. 
그래서 DB에서 edge function 호출할 때 publishable 키를 써야 할 줄 알았지만?
요청 헤더에 JWT(= 레거시 anon 키)를 보내줘야 통과되는 구조라는 거임... 껄껄</p>
<p>기본 설정 그대로라면 Authorization 헤더 + JWT가 있어야 하는데, 
헤더 검증을 안하게 하려면 배포 시 --no-verify-jwt 옵션을 사용했거나, 
함수 설정에서JWT 검증 해 상태여야 한다고 한다. 
cli 가 익숙하지 않아서 edge funtion을 수동으로 배포한 사람은...
배포한 함수 페이지 -&gt; details -&gt; funciton configuration에서 끄면 되는 것 같다.
사이트가 많이 개편되어서인지 제대로 경로를 적어놓은 곳이 별로 없다.</p>
<p>AI는 anon key만 쓰거나 jwt 인증을 아예 꺼버리면 보안 쪽 문제가 있으니
cron_secret 같은 임시 키를 만들어서 요청 헤더나 body에 같이 넣고 Edge Function에서 직접 비교해서
외부 무단 접근 막으라는데 일단은 생략..</p>
<blockquote>
<p>추후 변경
결국 jwt 검증 아예 끄고 랜덤값 생성한거 DB엔 valut로, edge funtion엔 환경변수로 넣음
헤더에서의 검증에서는 이 값을 사용하는걸로 바꿨음</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL) 11/10 개념부터 잡고 코드 작업]]></title>
            <link>https://velog.io/@squee_z_e/TIL-251110</link>
            <guid>https://velog.io/@squee_z_e/TIL-251110</guid>
            <pubDate>Mon, 10 Nov 2025 01:26:12 GMT</pubDate>
            <description><![CDATA[<h3 id="supabase-사용-시-잡고-가야-할-개념">supabase 사용 시 잡고 가야 할 개념</h3>
<h4 id="1-db-함수--프로시저">1. <strong>DB 함수 / 프로시저</strong></h4>
<ul>
<li>Supabase(PostgreSQL)에서는 <strong>DB 함수가 곧 프로시저</strong>처럼 쓰임
데이터를 조회하거나 수정하는 로직을 DB 내부에 저장해두고 실행하는 코드 블록</li>
<li>실행 위치: 데이터베이스 내부</li>
<li>역할: 여러 SQL을 하나로 묶어 재사용·트랜잭션 처리에 사용</li>
<li>장점: 빠르고 일관성 있음</li>
<li>단점: DB 종속이고, 형상관리 어려움</li>
</ul>
<h4 id="2-트리거-trigger">2. <strong>트리거 (Trigger)</strong></h4>
<ul>
<li>특정 테이블에 <code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code>가 일어날 때
<strong>자동으로 DB 함수(프로시저)를 실행</strong>시키는 메커니즘</li>
<li>실행 위치: 데이터베이스 내부 (함수 호출자 역할</li>
<li>역할: 데이터 변경 감지 → 후속 처리 (예: 로그 기록, 알림 호출)</li>
<li>트리거 자체는 로직이 아니라 “언제 함수를 실행할지 정의한 연결 장치”</li>
</ul>
<h4 id="3-rpc-remote-procedure-call">3. RPC (Remote Procedure Call)</h4>
<ul>
<li>정의: 클라이언트가 Supabase에 정의된 DB 함수를 원격으로 호출</li>
<li>실행 위치: Supabase API 게이트웨이를 통해 전달되어 <strong>DB 내부 함수가 실행</strong></li>
<li>역할: REST API 없이도 데이터베이스 내부 로직(DB 함수)을 직접 호출할 수 있게 함</li>
<li>특징:<ul>
<li><code>supabase.rpc(&#39;function_name&#39;)</code> 형태로 호출</li>
<li>네트워크 요청처럼 보이지만, 실제 연산은 DB 내부에서 수행되어 <strong>지연이 적음</strong></li>
<li>DB 함수가 API처럼 동작하므로 <strong>백엔드 서버 없이도 비즈니스 로직 노출 가능</strong></li>
</ul>
</li>
</ul>
<h4 id="4-rls-row-level-security-와-policy">4. RLS (Row Level Security) 와 Policy</h4>
<ul>
<li>RLS는 데이터베이스의 행 단위 접근 제어 기능,
Policy는 그 RLS 위에서 구체적인 접근 조건을 정의하는 규칙</li>
<li>로그인된 사용자 정보(auth.uid())를 기준으로,
각 사용자가 자신에게 허용된 행만 조회·수정할 수 있도록 제한</li>
<li>실행 위치: DB 내부 (쿼리 실행 시점)</li>
<li>역할: “누가 어떤 조건에서 어떤 데이터에 접근 가능한가”를 정의하는 보안 레이어</li>
</ul>
<h4 id="5-edge-function">5. <strong>Edge Function</strong></h4>
<ul>
<li>DB 밖에서 동작하는 <strong>서버리스 함수</strong></li>
<li>실행 위치: <strong>Supabase의 Edge 네트워크 (Deno 기반 서버)</strong></li>
<li>역할: 외부 API 호출, Slack 알림, 비즈니스 로직 등
DB에 넣기 애매한 서버 역할을 담당</li>
<li>장점: 서버 따로 안 두고 로직 추가 가능</li>
<li>단점: 형상관리·테스트 제약이 있어서 팀 규모 커지면 일반 서버 코드로 대체하는 경우 많음</li>
</ul>
<blockquote>
<p><strong>DB 함수(=프로시저)</strong> 는 로직의 본체,
<strong>트리거</strong>는 그걸 자동으로 실행하는 장치,
<strong>RPC</strong>는 DB 함수를 외부에서 API처럼 호출하는 통로,
<strong>RLS</strong>는 접근을 제한하는 보안 규칙,
<strong>Edge Function</strong>은 DB 밖에서 돌아가는 확장 로직이다.</p>
</blockquote>
<hr>
<h3 id="supabase에-slack-bot-연동하기">supabase에 slack bot 연동하기</h3>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/237e3146-bfcd-4018-91a8-2dfd6baea206/image.png" alt="">
<a href="https://api.slack.com/apps/">https://api.slack.com/apps/</a>
create new app -&gt; from a manifest -&gt; select a workspace 해서 새 app 생성
app home -&gt; Your App’s Presence in Slack에서 이름 바꿀 수 있음
OAuth  &amp; Permissions -&gt; scope에서 chat:write 권한 추가, OAuth Tokens에서 install to 워크스페이스
이러면 Bot User OAuth Token 보일거임</p>
<p>supabase에서 bot token 사용해서 알림 보내는 트리거 함수를 edge function에 등록
settings -&gt; edge functions -&gt; secres에서 bot token 등록
(로컬에서 테스트할 땐 환경변수에 저장) </p>
<blockquote>
<p>후술하지만 다른 방식도 있습니다</p>
</blockquote>
<p>slack oicd 연동해뒀으면 Authentication에 Sign In / Providers 쪽에 slack oicd가 enabled되어 있을텐데... 봇을 쓴다고 해서 client id랑 client secret을 전부 바꿀 필요는 없는듯? 워크스페이스가 다를 때만 의미가 있는 것 같음</p>
<hr>
<h3 id="supabase에서-스케줄러-사용하기">supabase에서 스케줄러 사용하기</h3>
<h4 id="문제-상황">문제 상황</h4>
<ul>
<li>30분 전 알림은 주기적 실행이 필요</li>
<li>Docker 사내망 배포 환경</li>
<li>별도 스케줄러 인프라 구축 부담</li>
</ul>
<h4 id="해결-방법-비교">해결 방법 비교</h4>
<table>
<thead>
<tr>
<th>방식</th>
<th>장점</th>
<th>단점</th>
<th>선택 여부</th>
</tr>
</thead>
<tbody><tr>
<td>Next.js API + 외부 cron</td>
<td>코드베이스 통합</td>
<td>별도 서버/스케줄러 필요</td>
<td>❌</td>
</tr>
<tr>
<td>컨테이너 내부 cron</td>
<td>간단</td>
<td>Dockerfile 수정, 권장하지 않음</td>
<td>❌</td>
</tr>
<tr>
<td>Edge Function + pg_cron</td>
<td>인프라 불필요, Supabase가 실행</td>
<td>-</td>
<td>✅</td>
</tr>
</tbody></table>
<p>더 자세히 보면</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>컨테이너 cron</th>
<th>Next.js API + 외부 cron</th>
<th>pg_cron (현재)</th>
</tr>
</thead>
<tbody><tr>
<td>설정 복잡도</td>
<td>중간 (Dockerfile 수정)</td>
<td>중간 (API Route + 외부 서비스)</td>
<td>낮음 (SQL만)</td>
</tr>
<tr>
<td>안정성</td>
<td>낮음 (컨테이너 의존)</td>
<td>중간 (외부 서비스 의존)</td>
<td>높음 (DB 레벨)</td>
</tr>
<tr>
<td>스케일링</td>
<td>문제 있음 (중복 실행)</td>
<td>문제 있음 (외부 호출 중복 가능)</td>
<td>문제 없음</td>
</tr>
<tr>
<td>로그 관리</td>
<td>어려움</td>
<td>중간 (Next.js 로그)</td>
<td>쉬움 (DB 쿼리)</td>
</tr>
<tr>
<td>재시작 영향</td>
<td>있음</td>
<td>없음 (외부 호출)</td>
<td>없음</td>
</tr>
<tr>
<td>인프라 의존성</td>
<td>컨테이너</td>
<td>외부 cron 서비스</td>
<td>Supabase</td>
</tr>
<tr>
<td>비용</td>
<td>없음</td>
<td>무료/유료 서비스</td>
<td>없음</td>
</tr>
<tr>
<td>보안</td>
<td>내부</td>
<td>외부 HTTP 노출</td>
<td>내부</td>
</tr>
<tr>
<td>실행 이력 추적</td>
<td>어려움</td>
<td>외부 서비스에 의존</td>
<td>쉬움 (DB 쿼리)</td>
</tr>
</tbody></table>
<h4 id="edge-function--pg_cron-선택-이유">Edge Function + pg_cron 선택 이유</h4>
<ol>
<li>인프라 설정 불필요<ul>
<li>별도 서버/스케줄러 없음</li>
<li>pg_cron만 설정하면 자동 실행</li>
</ul>
</li>
<li>형상관리 가능<ul>
<li>Edge Function 코드를 Git에 포함</li>
<li>이전 Edge Function은 코드베이스에 없어 관리 어려움</li>
</ul>
</li>
<li>간단한 설정<ul>
<li>SQL 한 번 실행으로 완료</li>
<li>Dashboard에서 환경 변수만 설정</li>
</ul>
</li>
<li>자동 실행<ul>
<li>Supabase가 스케줄링 처리</li>
<li>모니터링/로그 확인 용이</li>
</ul>
</li>
</ol>
<hr>
<h3 id="db-function과-edge-funciton---요즘은-안쓴다">DB Function과 Edge Funciton -&gt; 요즘은 안쓴다?</h3>
<p>형상 관리가 어려워서 좀 복잡해지는 한이 있더라도 codebase로 옮기는 추세
slack 알림 연동 기능을 처음엔 edge function으로 작성했으나 코드로 마이그레이션 했음
gemini 코드 리뷰에서 원자성 보장을 위해 db function쓰는거 생각해보라 했지만
최대한 서버액션으로 처리하는걸로 결정</p>
<p>그러나... 주기적 알림을 추가 인프라를 만지지 않고 구현하려다보니
pg_cron을 사용하게 되면서 edge function을 다시 쓰게 되긴 했다</p>
<hr>
<h3 id="supabase-cli로-edge-function-배포하기">supabase cli로 edge function 배포하기</h3>
<p>직접 사이트에서 edge function을 deploy해왔었는데 구글링하다가 발견..
공식 문서를 잘 읽읍시다
<a href="https://supabase.com/docs/guides/functions/deploy">https://supabase.com/docs/guides/functions/deploy</a>
post 401 에러에 마주쳤는데 이건 jwt 인증 끄고 해결..?
<a href="https://github.com/orgs/supabase/discussions/33194">https://github.com/orgs/supabase/discussions/33194</a>
이건 좀 위험하다해서 cron secret을 헤더에 추가해서 검증...?
사실 이해가 좀 덜 되긴 했다.
sql문 지금까지 실행하면서 policy / db함수 / edge function 만든 거 다시봐야될 것 같다.</p>
<pre><code class="language-sql">SELECT cron.schedule(
  &#39;check-reminders&#39;,
  &#39;*/5 * * * *&#39;,  -- 매 5분마다
  $$
  SELECT
    net.http_post(
      url := &#39;https://프로젝트코드.supabase.co/functions/v1/함수이름&#39;,
      headers := jsonb_build_object(
        &#39;Content-Type&#39;, &#39;application/json&#39;,
        &#39;x-cron-secret&#39;, &#39;임의의 값&#39; 
      ),
      body := &#39;{}&#39;::jsonb
    ) AS request_id;
  $$
);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[최근의 근황]]></title>
            <link>https://velog.io/@squee_z_e/%EC%B5%9C%EA%B7%BC%EC%9D%98-%EA%B7%BC%ED%99%A9</link>
            <guid>https://velog.io/@squee_z_e/%EC%B5%9C%EA%B7%BC%EC%9D%98-%EA%B7%BC%ED%99%A9</guid>
            <pubDate>Sat, 01 Nov 2025 14:13:18 GMT</pubDate>
            <description><![CDATA[<p>정글 생활을 마치고 취업 준비 활동을 이어가던 중,
정글에서 지원해준 기회로 한 회사에 풀스택 개발자 인턴으로 합류하게 되었다.
이번주가 출근 첫 주였음!</p>
<p>지금 당장 해야할 일들이 많이 쌓여있지만
너무 집중이 안되니까 기록이라도 하나 남겨놓으려고 오랜만에 블로그에 방문했다.
사실 그냥 조잘조잘 떠들 공간이 필요했답니다</p>
<p>출근 첫날엔 전반적인 회사생활에 대한 온보딩 교육, 앞으로 진행할 프로젝트에 대한 온보딩 교육을 들었다.
배정받은 장비로 작업 환경을 세팅했는데, 처음으로 맥북을 사용하게 돼서 적응하는게 쉽지 않았다.
(요즘은 조금 적응했는데, 회사에서는 macOS 쓰고 집에서는 window 쓰니까 자꾸 키가 헷갈려서 킹받는다.
지금도 한영키 대신 caps lock, ctrl대신 fn 키 눌러대는 중)</p>
<p>업무 적응을 위해 일주일 간 토이 프로젝트를 진행하게 됐다.
회사에서 사용하고 있는 그룹웨어에서 좀 불편하게 되어 있는 기능이 하나 있는데, 
그걸 확실히 개선한 시스템을 만들어서 사내망에 배포할 예정이다.
하루만에 프로젝트 범위 잡고 ERD, flow chart 작성 후, 수~금 3일간 개발을 진행했다.
처음에는 간단해보였지만, 생각보다 쉽지 않다.
express, prisma, supabase, adminjs, react // potainer, nginx proxy manager, github action</p>
<p>이해가 안되는 게 많아도 어떻게든 기간안에 뭘 만드려고 바이브 코딩을 해댔는데,
회사에서 지원해준 cursor 팀 플랜에서 기본제공 20달러 + 청구형 50달러 사용을 달성했다.
금요일 퇴근할 때 쯤에 팀장님께서 언질주셔서 확인해보게 됐는데 <strong>ㄹㅇ 대충격 먹음</strong>
비싼 모델 막 쓴 게 문제가 된 것 같은데, 3일만에 70달러는 좀 아니잖아 징짜
cursor pro 결제해서 썼을때는 auto로 놓고 썼고 추가비용 들일도 없어서 너무 안일했다.
월급에서 깐다고 농을 하셨는데 눈치가 엄청 보였다. 
AI를 보조도구로만 사용해야하는 이유가 금액적인 부분에도 있는 듯
실제 프로젝트 투입되면 spring 사용할 것 같던데 더 걱정이다.</p>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/dd32e9fa-5d7e-4d41-9346-9af55b03b5af/image.png" alt=""></p>
<p>내 개발 실력을 키우는 것이 해결방안임이 자명함에도.... 
일단 인턴 생활 중이니 업무 투입됐을 때 도움이 되어야 한다는 생각과
생산성 측면에서 AI를 안쓰는 건 불가능하다는 생각때문에 claude code, cursor 유료 플랜들을 막 찾아봤다.
개인적으로 이용중이던 chatGPT, cursor의 20$ 플랜 결제 중단하고
<strong>조만간 claude code의 100$ 플랜을 결제하게 될 것 같다^.^</strong> 개비싸 진짜
IDE가 편하다고 머무르려고 하지 말고 더 성능좋고 가성비 좋은 걸 사용하자..
회사의 지원이 어렵다면, 많은 걸 경험하고 배우는 입장이니 혼자서라도 사서 여러 방면에 사용해보자...
프롬프트 효율적으로 작성하는 법이랑 비용 아끼는 법들도 있으니 많이 찾아보자....</p>
<p>회사는 위치나 업무환경이 너무 좋고, 사람들도 좋다.
데일리 스크럼 참여하면서 뭐라도 한마디 내뱉는 게 아직은 어색하지만 개발팀 일원이 된 것 같아 기분이 미묘하다.
일이 많을 것 같긴 하지만, 바로 주요 업무에 투입되면서 배우는 부분이 엄청 많을 것 같다.
다만 어리벙벙한 나 때문에 동기가 고통받을 것 같아 미리 죄송함</p>
<p>공부를 계속하면서도 느끼는건데 개발자들은 정말 대단하다
저도 업계에서 살아남고 싶어요 흑흑 열심히 해볼게요</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[pintOS 주간 회고]]></title>
            <link>https://velog.io/@squee_z_e/pintOS-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@squee_z_e/pintOS-%EC%A3%BC%EA%B0%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Thu, 12 Jun 2025 17:10:50 GMT</pubDate>
            <description><![CDATA[<p>길고 긴... 지옥의 주간이 끝났다.</p>
<ul>
<li>지금 쓰긴 귀찮아서 내일 수정 예정</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[week12~13. pintOS(3)]]></title>
            <link>https://velog.io/@squee_z_e/week1213.pintos3</link>
            <guid>https://velog.io/@squee_z_e/week1213.pintos3</guid>
            <pubDate>Thu, 12 Jun 2025 17:07:53 GMT</pubDate>
            <description><![CDATA[<p>Virtual Memory
Page Table
Translation Lookaside Buffer (TLB)
Page Fault
Lazy Loading
Page Replacement Policy
Anonymous page
Swap Disk
File-backed Page
Direct Memory Access</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[week10~11. pintOS(2)]]></title>
            <link>https://velog.io/@squee_z_e/week1011.-pintOS2</link>
            <guid>https://velog.io/@squee_z_e/week1011.-pintOS2</guid>
            <pubDate>Tue, 27 May 2025 15:15:38 GMT</pubDate>
            <description><![CDATA[<p>User mode vs Kernel mode
Register vs Memory
User Stack
System Call
File Descriptor
Cache
Atomic Operation
rax register
32 bit OS vs 64 bit OS
Interrupt
Segmentation Fault</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[week09. pintOS(1)]]></title>
            <link>https://velog.io/@squee_z_e/week09.-pintos1</link>
            <guid>https://velog.io/@squee_z_e/week09.-pintos1</guid>
            <pubDate>Thu, 15 May 2025 05:44:36 GMT</pubDate>
            <description><![CDATA[<p>Process, Thread</p>
<p>CPU Scheduling 알고리즘</p>
<pre><code>FCFS (First Come First Served)
SJF (Shortest Job First)
SRTF (Shortest Remaining Time First)
Round Robin
Multilevel Queue Scheduling</code></pre><p>Semaphore와 Mutex
Race Condition
Deadlock
Context Switching
Multi-Level Feedback Queue Scheduler (MLFQS)</p>
<p>Project 1</p>
<pre><code>Chapter 3. Machine-Level Representation of Programs
Chapter 12. Concurrent Programming</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[week08. 웹서버 만들기]]></title>
            <link>https://velog.io/@squee_z_e/week08.-%EC%9B%B9%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@squee_z_e/week08.-%EC%9B%B9%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 08 May 2025 14:00:43 GMT</pubDate>
            <description><![CDATA[<p>네트워크 계층 (OSI7 Layer, TCP/IP Layer)웹페이지</p>
<p>클라이언트-서버 모델 웹페이지</p>
<p>소켓(socket, bind, listen, accept, connect, close)웹페이지</p>
<p>파일 디스크립터 웹페이지</p>
<p>Datagram Socket vs Stream Socket웹페이지</p>
<p>CGI / WebServer / MIME Type웹페이지</p>
<p>HTTP (요청/응답, 헤더, 메소드, 상태코드, HEAD 메소드)웹페이지</p>
<p>Proxy</p>
<p>BSD소켓, IP, TCP, HTTP, file descriptor, DNS</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크의 이해(2) - HTTP, tiny web 
server 구현, proxy server 구현]]></title>
            <link>https://velog.io/@squee_z_e/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%9D%98-%EC%9D%B4%ED%95%B42-HTTP-Tiny-Web-server-%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@squee_z_e/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%9D%98-%EC%9D%B4%ED%95%B42-HTTP-Tiny-Web-server-%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Sat, 03 May 2025 08:51:38 GMT</pubDate>
            <description><![CDATA[<h2 id="웹-서버와-동적정적-콘텐츠-제공-방식">웹 서버와 동적/정적 콘텐츠 제공 방식</h2>
<p>웹 서버는 클라이언트(브라우저 등)와 HTTP 프로토콜을 사용해 통신한다. 클라이언트는 서버에 정적 콘텐츠 혹은 동적 콘텐츠를 요청하며, 요청 방식에 따라 서버의 응답 방식이 달라진다.</p>
<ul>
<li><strong>정적 콘텐츠(static content)</strong>: 서버는 클라이언트의 요청을 받으면 디스크에서 해당 파일을 읽어 클라이언트로 전송한다.</li>
<li><strong>동적 콘텐츠(dynamic content)</strong>: 서버는 요청을 처리하기 위해 <strong>자식 프로세스(child process)</strong>를 생성하고, 그 안에서 프로그램을 실행하여 출력 결과를 클라이언트에게 반환한다.</li>
</ul>
<p>이때 사용되는 것이 CGI (Common Gateway Interface) 표준이다. CGI는 다음과 같은 정보를 다룰 수 있는 규칙들을 정의한다.</p>
<ul>
<li>클라이언트가 서버에 전달하는 프로그램 인자</li>
<li>서버가 자식 프로세스에 전달하는 환경 정보</li>
<li>자식 프로세스가 클라이언트에 출력 결과를 전달하는 방법</li>
</ul>
<p>이러한 구조를 바탕으로, 정적 및 동적 콘텐츠를 모두 제공하는 웹 서버는 수백 줄의 C 코드만으로도 구현 가능하다. 이는 웹 통신의 본질이 클라이언트-서버 모델 + 소켓 통신 + 간단한 프로토콜 해석에 있음을 보여준다.</p>
<hr>
<h2 id="내가-궁금해서-찾아본-qna">내가 궁금해서 찾아본 QnA</h2>
<h3 id="1-브라우저가-웹사이트를-처음-열었을-때">[1] 브라우저가 웹사이트를 처음 열었을 때</h3>
<ul>
<li>사용자가 <code>https://www.google.com</code>에 접속하면 브라우저는 내부적으로 다음 순서를 따른다:<ul>
<li><code>getaddrinfo()</code>로 도메인을 IP 주소로 변환 (DNS 질의 포함)</li>
<li><code>socket()</code> 호출로 TCP 소켓 생성</li>
<li><code>connect()</code>를 통해 서버에 3-way handshake 요청</li>
<li>서버는 <code>listen()</code> 상태에서 대기 중이므로, <code>accept()</code>로 연결 수락 후 <code>connfd</code> 반환</li>
<li>이 연결을 통해 서버는 HTML과 이미지, JS 등의 리소스를 응답하고 브라우저는 이를 화면에 렌더링</li>
</ul>
</li>
</ul>
<h3 id="2-이후-같은-도메인에서-검색이나-이동이-발생했을-때">[2] 이후 같은 도메인에서 검색이나 이동이 발생했을 때</h3>
<ul>
<li><strong>HTTP/1.0</strong> 이라면 기본적으로 요청-응답 이후 TCP 연결을 끊는다 → 검색을 할 때마다 새로운 <code>socket()</code>과 <code>connect()</code> 호출</li>
<li><strong>HTTP/1.1 이상</strong> 이라면 기본적으로 Persistent Connection 유지:<ul>
<li>서버가 <code>Connection: keep-alive</code> 헤더를 응답에 포함하면,</li>
<li>브라우저는 기존 TCP 연결을 재사용하여 요청을 보냄 (같은 <code>connfd</code>에서 <code>rio_readlineb()</code> 등으로 다시 처리)</li>
<li>단, 서버가 연결을 닫거나 일정 시간이 지나면 새로운 연결을 다시 수립함</li>
</ul>
</li>
</ul>
<h3 id="3-다른-도메인예-navercom으로-이동했을-때">[3] 다른 도메인(예: naver.com)으로 이동했을 때</h3>
<ul>
<li>새로운 DNS 질의(<code>getaddrinfo(&quot;www.naver.com&quot;)</code>)를 통해 IP 주소 획득</li>
<li>별도의 <code>socket()</code> 및 <code>connect()</code>로 새로운 TCP 연결 생성</li>
<li>이 연결은 완전히 독립적인 소켓이며, 서버 측에서도 다른 프로세스 혹은 다른 <code>listenfd</code>에서 수락됨</li>
</ul>
<h3 id="4-브라우저의-뒤로-가기-버튼을-눌렀을-때">[4] 브라우저의 &quot;뒤로 가기&quot; 버튼을 눌렀을 때</h3>
<ul>
<li>대부분의 경우 <strong>캐시된 페이지를 재활용</strong> 하며, 네트워크 요청이 발생하지 않음</li>
<li>발생해도 다음과 같은 캐시 제어 정책에 따름:<ul>
<li><code>200 (from cache)</code>: 완전히 로컬에서 렌더링</li>
<li><code>304 Not Modified</code>: 서버에 조건부 요청 (<code>If-Modified-Since</code>, <code>If-None-Match</code> 등)을 보내고 변경 없음 판단 시 캐시 사용</li>
</ul>
</li>
<li>따라서 &quot;뒤로 가기&quot;는 소켓 재연결 없이도 동작하는 <strong>브라우저 레벨 히스토리 복원</strong> 에 해당</li>
</ul>
<h3 id="5-로그인-상태에서-새-탭을-열었을-때-로그인-유지-이유">[5] 로그인 상태에서 새 탭을 열었을 때 로그인 유지 이유</h3>
<ul>
<li>로그인 정보는 TCP 연결이 아닌 <strong>HTTP 쿠키</strong> 에 저장됨</li>
<li>쿠키는 브라우저의 <strong>도메인 단위 저장소</strong> 에 기록되며, 같은 브라우저 프로세스에서 새 탭을 열더라도 다음 요청에 자동 첨부됨</li>
<li>따라서 새로 열린 탭에서 새로운 TCP 연결이 생성되더라도 로그인 상태는 쿠키를 통해 유지됨</li>
</ul>
<h3 id="6-같은-url이라도-접근-경로링크-클릭-vs-직접-입력에-따른-차이">[6] 같은 URL이라도 접근 경로(링크 클릭 vs 직접 입력)에 따른 차이</h3>
<ul>
<li>HTTP 요청에 포함되는 <code>Referer</code> 헤더가 다름:<ul>
<li>링크 클릭 시: <code>Referer</code>는 이전 페이지의 URL</li>
<li>주소창 직접 입력 또는 즐겨찾기 클릭 시: <code>Referer</code>는 빈 문자열이거나 존재하지 않음</li>
</ul>
</li>
<li>서버는 이 값을 이용해 사용자 유입 경로, 광고 성과 측정, A/B 테스트 등을 수행할 수 있음</li>
</ul>
<h3 id="7-동일한-url-요청-시-브라우저가-캐시를-사용할지-서버에-다시-요청할지-결정하는-기준">[7] 동일한 URL 요청 시 브라우저가 캐시를 사용할지 서버에 다시 요청할지 결정하는 기준</h3>
<ul>
<li>서버 응답의 헤더에 따라 달라짐:<ul>
<li><code>Cache-Control: no-cache</code>: 무조건 서버 재확인</li>
<li><code>ETag</code>: 클라이언트는 <code>If-None-Match</code>로 조건부 요청 수행, 변경 없으면 <code>304</code> 응답</li>
<li><code>Expires</code>: 기한 전까지는 재요청하지 않음</li>
</ul>
</li>
<li>같은 URL이라도 HTTP 메서드(GET/POST), 헤더, 쿠키 상태, 캐시 제어 값에 따라 네트워크 요청 여부가 결정됨</li>
</ul>
<h3 id="8-브라우저에서-하나의-html-요청이-만들어내는-다수의-연결">[8] 브라우저에서 하나의 HTML 요청이 만들어내는 다수의 연결</h3>
<ul>
<li>브라우저는 HTML 본문 외에도 CSS, JS, 이미지 등 수십 개의 리소스를 병렬로 요청함</li>
<li>HTTP/1.0: 한 연결당 하나의 리소스 → 다수의 <code>socket()</code> 호출과 <code>connect()</code> 필요</li>
<li>HTTP/1.1: Persistent Connection + 파이프라이닝 가능 → 같은 연결 재사용</li>
<li>HTTP/2: 하나의 TCP 연결 내에서 <strong>멀티플렉싱</strong> 을 통해 동시에 여러 요청/응답 처리</li>
</ul>
<h3 id="9-keep-alive-연결은-언제-끊기는가">[9] keep-alive 연결은 언제 끊기는가?</h3>
<ul>
<li>HTTP/1.1에서 기본적으로 <code>Connection: keep-alive</code>지만 무한히 유지되진 않음</li>
<li>서버는 일정 시간(<code>timeout</code>) 동안 추가 요청이 없으면 연결을 닫음</li>
<li>클라이언트도 명시적으로 <code>Connection: close</code>를 보낼 수 있음</li>
<li><code>close</code> 후에는 새로운 요청 시 새로운 TCP 연결을 열게 됨</li>
</ul>
<h3 id="10-http와-tcp의-관계-그-사이에서-발생할-수-있는-문제들">[10] HTTP와 TCP의 관계, 그 사이에서 발생할 수 있는 문제들</h3>
<ul>
<li>HTTP는 TCP 위에서 동작하는 텍스트 기반 프로토콜</li>
<li>TCP는 바이트 스트림이므로, HTTP 메시지의 경계를 보장하지 않음<ul>
<li>예: 2개의 HTTP 요청이 하나의 TCP 세그먼트로 묶일 수도 있고 반대로 쪼개질 수도 있음</li>
</ul>
</li>
<li>따라서 웹 서버는 버퍼 기반으로 <code>\r\n</code> 또는 헤더 길이를 기준으로 정확히 메시지를 파싱해야 함</li>
</ul>
<h3 id="11-서버는-동시에-여러-연결을-어떻게-처리하나">[11] 서버는 동시에 여러 연결을 어떻게 처리하나?</h3>
<ul>
<li>전통적 방식: <code>accept()</code> 후 <code>fork()</code> 또는 <code>pthread_create()</code>로 각 연결을 분기 처리</li>
<li>현대적 방식:<ul>
<li><code>select()</code>, <code>poll()</code>, <code>epoll()</code> 등을 사용한 I/O Multiplexing</li>
<li>비동기/논블로킹 소켓</li>
<li>이벤트 기반 서버 (예: Node.js, Nginx)</li>
</ul>
</li>
<li>이 모든 방법은 클라이언트 수 증가에 대응하기 위한 확장성 확보 방식임</li>
</ul>
<h3 id="12-https의-등장으로-생기는-차이점">[12] HTTPS의 등장으로 생기는 차이점</h3>
<ul>
<li>HTTPS는 HTTP 메시지를 TLS를 통해 암호화하여 전송</li>
<li><code>socket()</code> 후 <code>connect()</code>까지는 같지만, 이후 TLS Handshake를 통해 인증서 교환 및 키 협상이 추가됨</li>
<li>이후부터는 애플리케이션 레벨에서는 평문처럼 <code>GET /</code>, <code>Host:</code> 등을 보내더라도 실제 전송은 모두 암호화됨</li>
<li><strong>HTTPS에서는 중간 프록시가 내용 파악 불가</strong> , 캐시 활용도 제한됨</li>
</ul>
<hr>
<h2 id="웹-서버csapp-115">웹 서버(CS:APP 11.5)</h2>
<h3 id="웹-기초">웹 기초</h3>
<p>웹은 <strong>HTTP(Hypertext Transfer Protocol)</strong>라는 텍스트 기반 애플리케이션 계층 프로토콜을 통해 클라이언트와 서버가 통신하는 구조다. 클라이언트인 브라우저는 서버와의 인터넷 연결을 열고 콘텐츠를 요청한 뒤, 서버가 응답을 보내고 연결을 종료하면 그 내용을 화면에 출력한다. 이 과정은 매우 단순한 요청-응답 패턴으로 이루어진다.</p>
<p>웹 서비스가 FTP와 같은 전통적인 파일 전송 서비스와 구별되는 가장 큰 특징은, 콘텐츠가 <strong>HTML(Hypertext Markup Language)</strong>이라는 언어로 작성된다는 점이다. HTML은 텍스트와 그래픽 요소들을 화면에 어떻게 표시할지를 지시하는 <strong>태그(tag)</strong>로 구성되며, 예를 들어 <code>&lt;b&gt;Make me bold!&lt;/b&gt;</code>는 해당 텍스트를 굵게 표시하라는 지시다.</p>
<p>하지만 HTML의 진정한 힘은 문서 안에 <strong>하이퍼링크(hyperlink)</strong>를 포함할 수 있다는 점에 있다. 하이퍼링크는 인터넷상의 다른 호스트에 있는 콘텐츠를 참조할 수 있게 해준다.  예컨데 <code>&lt;a href=&quot;http://www.cmu.edu/index.html&quot;&gt;Carnegie Mellon&lt;/a&gt;</code>이라는 태그는 &#39;Carnegie Mellon&#39;이라는 텍스트를 강조 표시하며, 사용자가 이를 클릭하면 브라우저는 CMU 서버에 저장된 index.html 파일을 요청하고, 응답을 받아 화면에 표시한다.</p>
<h3 id="웹-컨텐츠">웹 컨텐츠</h3>
<p>웹 서버와 클라이언트가 주고받는 웹 콘텐츠는 MIME 타입이 명시된 바이트 시퀀스로 구성된다. 서버는 이러한 콘텐츠를 두 가지 방식으로 제공할 수 있다. 첫째, 디스크에 저장된 파일을 그대로 반환하는 정적 콘텐츠(static content) 방식이 있다. 예를 들어 HTML, 이미지, CSS 파일 등이다. 둘째, 실행 가능한 프로그램을 실행한 결과(실행파일이 런타임에 만든 출력)를 반환하는 동적 콘텐츠(dynamic content) 방식이 있으며, 이는 CGI 방식 등으로 서버에서 프로그램을 실행하고 그 출력을 클라이언트에게 전달한다.</p>
<p>각 콘텐츠는 고유한 URL(Uniform Resource Locator)로 식별되며, 이는 서버 주소, 포트, 파일 경로, 그리고 필요시에는 프로그램 인자까지 포함한다. 예를 들어, <code>http://host:port/path/to/file</code> 같은 URL에서 클라이언트는 호스트와 포트를 통해 서버에 접속하고, 서버는 그 뒤의 경로를 해석해 파일을 찾는다. (<code>?</code> 로 파일 이름과 인자를 구분, <code>&amp;</code>로 인자 연결)</p>
<p>서버는 URL의 경로(접미사)를 분석하여 정적 혹은 동적 콘텐츠인지를 판별하는데, 이는 서버마다 다르며 보통 cgi-bin과 같은 특정 디렉토리에 따라 결정된다. 또한 /만 있는 요청은 index.html 같은 기본 파일로 확장되어 처리된다.</p>
<h3 id="http-트랜잭션">HTTP 트랜잭션</h3>
<p>HTTP 트랜잭션은 클라이언트와 서버 간의 요청(request)과 응답(response)을 텍스트 기반의 애플리케이션 계층 프로토콜인 HTTP를 통해 주고받는 과정을 말한다. 이 프로토콜은 단순하고 사람이 읽기 쉬운 형식으로, telnet 같은 도구를 통해 수동으로도 요청을 생성해 확인할 수 있다.</p>
<pre><code>// 정적 컨텐츠를 제공하는 HTTP 트랜잭션의 예

1  linux&gt; telnet www.aol.com 80       # 클라이언트가 Telnet 명령어로 AOL 웹 서버(포트 80)에 TCP 연결 시도
2  Trying 205.188.146.23...           # Telnet이 도메인명을 IP 주소(IPv4)로 변환하여 접속 시도 중
3  Connected to aol.com.              # TCP 연결 성공
4  Escape character is &#39;^]&#39;.          # Telnet 세션을 종료할 때 사용할 이스케이프 문자 안내
5  GET / HTTP/1.1                     # 요청 라인: 루트 페이지(/)를 요청하는 HTTP/1.1 GET 요청
6  Host: www.aol.com                  # 필수 요청 헤더: 요청한 호스트 이름 (HTTP/1.1에서 필수)
7                                     # 빈 줄: 헤더 종료를 나타내며, 본문이 없음을 의미 (GET 요청의 경우 일반적)
8  HTTP/1.0 200 OK                    # 응답 라인: HTTP/1.0 기반 응답이며, 상태 코드 200은 정상 처리 의미
9  MIME-Version: 1.0                  # MIME 버전 정보 (콘텐츠의 형식을 설명하기 위한 사양)
10 Date: Mon, 8 Jan 2010 4:59:42 GMT  # 응답이 생성된 시간 (표준 형식, GMT 시간대)
11 Server: Apache-Coyote/1.1          # 서버 소프트웨어 정보 (서버 식별용)
12 Content-Type: text/html            # 응답 본문의 MIME 타입 (HTML 문서임을 명시)
13 Content-Length: 42092              # 응답 본문의 바이트 수 (클라이언트는 이 길이만큼 읽으면 됨)
14                                    # 빈 줄: 응답 헤더 종료를 알림
15 &lt;html&gt;                             # 응답 본문 시작: HTML 페이지의 첫 줄
16 ...                                # 생략된 HTML 콘텐츠 (총 766줄)
17 &lt;/html&gt;                            # HTML 문서 종료
18 Connection closed by foreign host. # 서버가 연결을 종료함 (HTTP/1.0은 기본적으로 비지속 연결)
19 linux&gt;                             # Telnet 클라이언트도 종료됨, 셸로 복귀</code></pre><pre><code>// 동적 HTML 컨텐츠를 제공하는 HTTP 트랜잭션의 예

1  linux&gt; telnet kittyhawk.cmcl.cs.cmu.edu 8000      # 클라이언트: kittyhawk 서버의 8000번 포트로 TCP 연결 시도
2  Trying 128.2.194.242...                           # telnet: 해당 호스트의 IP 주소로 DNS 해석 후 접속 시도
3  Connected to kittyhawk.cmcl.cs.cmu.edu.           # telnet: 서버와의 TCP 연결이 성공적으로 이루어짐
4  Escape character is ’^]’.                         # telnet: 연결 중 명령어를 빠져나오는 특수 키 안내
5  GET /cgi-bin/adder?15000&amp;213 HTTP/1.0             # 클라이언트: CGI 프로그램 호출 요청 (파일명과 인자 포함한 GET 요청)
                                                        → URI: /cgi-bin/adder 프로그램을 15000, 213 인자와 함께 실행 요청
6                                                    # 클라이언트: 빈 줄을 통해 요청 헤더 종료 (필수, HTTP 규격상)
7  HTTP/1.0 200 OK                                   # 서버: 정상 처리 응답 (200 상태 코드)
8  Server: Tiny Web Server                           # 서버: 응답 헤더로 서버 종류 명시 (Tiny 서버 사용 중)
9  Content-length: 115                               # CGI 프로그램: 응답 본문의 총 바이트 수 (115바이트)
10 Content-type: text/html                           # CGI 프로그램: 응답 콘텐츠의 MIME 타입은 HTML
11                                                   # CGI 프로그램: 빈 줄로 응답 헤더 종료 표시
12 Welcome to add.com: THE Internet addition portal. # CGI 프로그램: HTML 본문 시작 – 첫 줄
13 &lt;p&gt;The answer is: 15000 + 213 = 15213             # CGI 프로그램: 실제 계산 결과가 포함된 HTML 문단
14 &lt;p&gt;Thanks for visiting!                           # CGI 프로그램: 마지막 안내 문구 포함 HTML 문단
15 Connection closed by foreign host.                # 서버: CGI 출력 완료 후 연결 종료
16 linux&gt;                                            # 클라이언트: telnet 종료됨, 프롬프트로 돌아감
</code></pre><p>HTTP 요청은 클라이언트가 서버에 특정 리소스를 요구하기 위해 보내는 메시지로, 요청 라인, 요청 헤더, 그리고 빈 줄로 구성된다. 요청 라인은 보통 <code>GET /index.html HTTP/1.1</code> 형태를 가지며, 여기서 GET은 요청 메서드, /index.html은 요청하려는 리소스의 경로(URI), HTTP/1.1은 사용하는 프로토콜 버전을 의미한다. 이어지는 요청 헤더들은 클라이언트의 브라우저 정보나 허용 가능한 콘텐츠 형식 등을 전달하며, 특히 Host 헤더는 HTTP/1.1에서 필수로 포함되어야 하고, 요청 대상 서버의 도메인 이름을 명시한다. 마지막 빈 줄은 헤더의 끝을 나타내며, 이후에 본문(body)이 따라올 수도 있다. POST 요청의 경우가 바로 이런 예로, 클라이언트가 서버에 데이터를 전송할 필요가 있을 때 사용된다. 예를 들어 로그인 정보나 폼 데이터를 서버로 보낼 때, POST 요청은 요청 본문에 그 데이터를 포함시켜 전송한다. 이때 Content-Type이나 Content-Length 같은 헤더가 함께 사용되어, 본문의 데이터 형식과 길이를 서버에 알린다. 모든 HTTP 요청의 각 줄은 \r\n으로 끝나야 하며, 이는 HTTP 명세에서 정한 필수 형식이다.(<code>\r</code> 커서를 줄의 맨 앞으로 이동시킴, <code>\n</code> 현재 위치에서 줄을 바꿈)</p>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/ede6c758-d777-4302-8082-c3e0f76adaac/image.png" alt=""></p>
<p><strong>HTTP 응답</strong>은 서버가 클라이언트의 요청에 대해 보내는 메시지로, 응답 라인, 응답 헤더, 빈 줄, 응답 본문으로 구성된다. 응답 라인은 예를 들어 HTTP/1.0 200 OK와 같은 형식으로, 서버가 응답하는 HTTP 버전, 상태 코드(예: 200은 성공), 상태 메시지를 포함한다. 응답 헤더는 Content-Type, Content-Length 등의 메타데이터를 담고 있으며, 이는 클라이언트가 응답 내용을 어떻게 해석할지 결정하는 데 도움을 준다. Content-Type은 MIME 타입을 명시하며, Content-Length는 본문의 크기를 바이트 단위로 제공한다. 빈 줄 이후에는 HTML 문서와 같은 실제 콘텐츠가 응답 본문으로 포함되며, 클라이언트는 이를 파싱해 화면에 출력한다. 이와 같은 구조 덕분에 브라우저는 텍스트 기반의 요청과 응답만으로도 웹 페이지를 정확히 렌더링할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/d8873f06-b12e-42ac-a734-6fcb6cf49072/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/squee_z_e/post/753c60a4-3029-4557-a2ac-410a14f6b65f/image.png" alt=""></p>
<h3 id="동적-컨텐츠의-처리">동적 컨텐츠의 처리</h3>
<p>웹 서버가 동적 콘텐츠를 제공하려면 단순히 파일을 읽어 반환하는 것과는 달리 여러 추가 작업이 필요하다. 클라이언트가 동적 콘텐츠를 요청할 때는, 실행할 프로그램과 인자를 함께 전달해야 하며, 서버는 이 요청을 해석해 프로그램을 실행하고 그 출력을 클라이언트에게 반환해야 한다. 이를 가능하게 해주는 표준이 바로 CGI (Common Gateway Interface) 다.</p>
<p>클라이언트는 GET 요청에서 인자를 URI의 일부로 전달한다. 예를 들어 <code>adder?15000&amp;213</code> 같은 형식에서 <code>?</code> 뒤가 인자이며, 여러 인자는 <code>&amp;</code>로 구분된다. 공백과 같은 특수문자는 <code>%20</code> 같은 문자열로 인코딩된다.</p>
<p>서버는 이 요청을 받아 fork()로 자식 프로세스를 만든 뒤, execve()로 CGI 프로그램을 실행한다. 이때 서버는 QUERY_STRING이라는 환경 변수를 설정하고, 여기에 <code>15000&amp;213</code>처럼 클라이언트가 넘긴 인자를 넣는다. CGI 프로그램은 실행 중에 <code>getenv(&quot;QUERY_STRING&quot;)</code>처럼 환경 변수에서 이 값을 읽을 수 있다.</p>
<p>CGI는 이 외에도 CGI 프로그램이 알아야 할 정보(요청 메서드, 서버 이름, 사용자 에이전트 등)를 위해 다양한 환경 변수를 정의한다. 예를 들어 REQUEST_METHOD, CONTENT_TYPE, SCRIPT_NAME 등이 있다.
<img src="https://velog.velcdn.com/images/squee_z_e/post/a6da47b2-1d24-47e5-b8bf-d09da442a38d/image.png" alt=""></p>
<p>CGI 프로그램의 출력은 표준 출력(stdout)을 통해 클라이언트로 직접 전달된다. 서버는 exec 전에 dup2()를 사용해 자식 프로세스의 stdout을 클라이언트 소켓 디스크립터로 리디렉션한다. 따라서 CGI 프로그램이 printf() 등을 호출하면, 그 출력은 그대로 웹 브라우저에 전달된다.</p>
<p>CGI 프로그램은 반드시 응답 헤더를 스스로 생성해야 한다. 서버는 콘텐츠의 타입이나 길이를 알 수 없기 때문에, CGI 프로그램이 Content-type과 Content-length 같은 HTTP 응답 헤더와 빈 줄을 직접 출력해야 한다. 그 뒤에 본문을 출력하는 구조다. 이는 정적 콘텐츠와 가장 큰 차이점 중 하나다.</p>
<hr>
<h2 id="tiny-web-server-구현하기">Tiny Web Server 구현하기</h2>
]]></description>
        </item>
    </channel>
</rss>