<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Kani Kim Dev</title>
    <link>https://kimkani.tistory.com/</link>
    <description>Welcome to Kani Kim's Tistory</description>
    <language>ko</language>
    <pubDate>Fri, 19 Jun 2026 19:02:23 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Kani Kim</managingEditor>
    <image>
      <title>Kani Kim Dev</title>
      <url>https://tistory1.daumcdn.net/tistory/5870982/attach/0c8b599fc27c4e16be328a163602c710</url>
      <link>https://kimkani.tistory.com</link>
    </image>
    <item>
      <title>Python(파이썬) 이용자의 눈에서 본 Java(자바)의 메모리 구조 비교 및 정리, GC, GIL, 메모리, JVM구조, 정적 타입, 동적 타입 등등</title>
      <link>https://kimkani.tistory.com/72</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;무제 5.001.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuYD7m/dJMb99TRcg5/67kKT65ut4vwbgH5TfEeF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuYD7m/dJMb99TRcg5/67kKT65ut4vwbgH5TfEeF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuYD7m/dJMb99TRcg5/67kKT65ut4vwbgH5TfEeF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuYD7m%2FdJMb99TRcg5%2F67kKT65ut4vwbgH5TfEeF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-filename=&quot;무제 5.001.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 서론&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파이썬을 주로 사용하던 입장에서 자바를 언젠가는 꼭 공부해야겠다는 생각을 하고는 했다. 물론 나의 게으름과 귀찮음으로 인해 그다지 큰 진도를 나가지 못해 아쉬움을 느끼던 와중, 이번에는 꼭 자바를 공부해야겠다는 결심을 했다. 하지만 어떻게 공부해야 할지, 그리고 무엇을 공부해야 할지 모르겠던 와중 한 번 내가 주로 사용하던 파이썬을 자바와 주로 비교하면서 한 번 공부를 해 나가야겠다는 생각을 했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;오늘은 자바의 메모리 구조(JVM)와 그것이 어떻게 작동하는지를 파이썬의 메모리 구조와 그 작동방식과 비교하면서 진행해 나가보려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 자바의 메모리 구조&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;968&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pBVPD/dJMcaf7AOXA/PdxxqOaGbEYWNADFH5rfe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pBVPD/dJMcaf7AOXA/PdxxqOaGbEYWNADFH5rfe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pBVPD/dJMcaf7AOXA/PdxxqOaGbEYWNADFH5rfe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpBVPD%2FdJMcaf7AOXA%2FPdxxqOaGbEYWNADFH5rfe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1448&quot; height=&quot;968&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;968&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-1. JVM의 Class Loader&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바의 경우 Java 소스 코드가 읽힌 뒤 이를 Java 컴파일러가 Java 바이트 코드로 변환한다. 그리고 이 바이트 코드를 JVM에서 읽어 들인 뒤 실행하게 된다. 이 때 자바 컴파일러에 의해 바이트 코드 형태가 된 파일은. class확장자를 가진 클래스 파일이 된다. 그리고 위에서 나오는 JVM의 Class Loader는 이러한 바이트 코드를 동적으로 로드(Loading)하고 링크(Linking)한 뒤 초기화(Initialization)한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;로딩(Loading)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;바이트 코드(*.class)를 메서드 영역에 저장한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;각각의 바이트 코드는 JVM에 의해 메서드 영역에 다음과 같은 정보를 저장한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;로드한 클래스를 비롯한 부모 클래스의 정보&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스 파일과 함께 Class, Interface, Enum과의 관련 여부 및 변수나 메서드 등의 정보&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스 로딩 시점 - 클래스 인스턴스 생성, 정적 변수 사용(final 키워드가 아닌 경우), 정적 메서드 호출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스 로딩이 되지 않는 경우 - 클래스에 접근하지 않는 경우, 클래스의 정적 변수 사용(final 키워드의 경우)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;링크(Linking)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;검증 : 읽은 클래스가 자바의 언어 명세와 함께 JVM 명세에 명시된 대로 구성되어 있는지 검사&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;준비 : 클래스의 필요한 메모리를 할당, 클래스에 정의된 필드, 메소드와 인터페이스를 나타내는 데이터 구조 준비&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;분석 : Symbolic 메모리 참조를 메소드 영역에 있는 실제 참조로 교체&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;초기화(Initialization)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;static 필드들이 설정된 값으로, 클래스 변수들을 적절한 값으로 초기화.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;초기화 시점 - 클래스의 인스턴스 생성, 정적 변수 사용(final 키워드가 아닌 경우), 정적 메서드 호출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;진행 순서 - 1. 정적 블록 / 2. 정적 변수 / 3. 생성자&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 때 클래스의 로딩은 한 번만 수행되고, 그때 한 번만 초기화를 수행한다. 이는 여러 스레드가 생성된 상황에서 스레드가 동시에 같은 코드 영역에 접근하거나 데이터를 공유할 때에도 올바른 실행 결과를 보장하는 스레드 세이프(thread-safe) 속성을 의미하게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 다른 글들을 참고하면서 헷갈렸던 것이 초기화 단계에서는 thread-safe 하다고 하는데 이 thread-safe를 이용해서 싱글톤 패턴을 thread-safe 하게 한다는 것이었다. 살짝 이해가 되지 않았다. JVM에서 초기화 단계 후에 안전하게 보장하는데 왜 싱글톤 패턴으로 다시 안전하게 만드는 것일까?라는 의문이 들었다. 이런 의문을 해결하기 위해 이것저것 찾아보다 보니 다음과 같이 결론이 나왔다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스 초기화의 thread-safe는 JVM이 클래스 초기화 절차 전체에 락을 걸어서 static 초기화를 한 번만 수행하게 보장하지만 일반 인스턴스의 경우 메서드 안의 if-check, new, assignment가 일반 코드이므로 개발자가 동기화하지 않으면 경쟁 상태 발생한다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778385009818&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {

        return INSTANCE;

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드는 private static final Singleton INSTANCE = new Singleton();가 핵심인데, 이 코드는 클래스 초기화 단계에서 실행된다. 만약 이 상태에서 다음과 같은 코드를 실행한다고 보자. 여러 스레드에서 Singleton.getInstance()를 호출하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778385238672&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Thread t1 = new Thread(() -&amp;gt; Singleton.getInstance());
Thread t2 = new Thread(() -&amp;gt; Singleton.getInstance());&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;내부적으로는 Thread-1과 Thread-2가 클래스 초기화를 시도하지만 JVM은 클래스 초기화를 동시에 수행하지 못하게 막는다. 만약 Thread-1이 INSTANCE = new Singletone()을 실행해 초기화를 한다면 Thread-2는 이미 초기화된 INSTANCE를 사용하게 된다. 이때 thread-safe 하다는 의미는 private static final Singleton INSTANCE = new Singleton(), 이 초기화 코드는 클래스당 한 번만 실행된다는 것이며 동시에 여러 스레드에서 클래스 초기화를 시작할 수 없고, 다른 스레드가 초기화 완료 전인 객체에 접근할 수 없다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778385539819&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class LazySingleton {

    private static LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {

        if (instance == null) {

            instance = new LazySingleton();

        }

        return instance;

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 위의 코드를 보자, instance = new LazySingleton();가 객체를 생성하는데 이 때 이 코드가 사용되는 곳이 일반적인 ㅜ메서드 실행 중에 수행된다. 즉 클래스 초기화 단계에서 실행되지 않는다. 클래스 초기화 상태에서는 private static LazySingleton instance;의 코드가 초기화되지만, instance에는 null로 초기화된다. 즉 이 경우는 JVM에서 보장하는 스레드 세이프(thread-safe)가 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-2. JVM의 Execution Engine&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 Class Loader에 의해 메모리에 적재된 바이트 코드를 기계어로 변경해 명령어 단위로 실행하는 역할을 한다. 이 때 파이썬처럼 명령어를 하나하나 실행하는 Interpreter(인터프리터) 방식이 있고 JIT(Just-In-Time) 컴파일러를 사용하는 방식이 있다. 인터프리터처럼 한 줄 한 줄씩 읽으면서 바이트 코드를 실행하다가, 컴파일 임계치(Compile Threshold)를 넘으면 JIT 컴파일러가 메서드가 자주 사용되는지 체크하는 방식으로 컴파일 임계치를 사용한다. 이 임계치를 초과하면 JIT 컴파일이 트리거 되어서 기계어를 캐싱하고 인터프리터가 매번 기계어로 컴파일하는 것이 아닌 미리 캐싱된 기계어를 사용하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-3. JVM의 Garbage Collector&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Garbage Collector의 경우 참조되지 않은 객체들을 탐색 후 삭제 - 메모리에서 할당을 해제 - 하며 이렇게 삭제된 객체의 메모리를 반환한다. 특히 이는 JVM의 Runtime Data Area의 Heap Area하고 큰 연관이 있어서 나중에 Runtime Data Area의 Heap 부분하고 같이 설명하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-4. JVM의 Runtime Data Area&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLOBbr/dJMcafzNvE5/JTk1Kx5qK14QlEndQVZTa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLOBbr/dJMcafzNvE5/JTk1Kx5qK14QlEndQVZTa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLOBbr/dJMcafzNvE5/JTk1Kx5qK14QlEndQVZTa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLOBbr%2FdJMcafzNvE5%2FJTk1Kx5qK14QlEndQVZTa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1446&quot; height=&quot;724&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JVM의 Runtime Data Area는 실제로 클래스 파일이 적재되는 곳이다. JVM이 운영체제로부터 자바를 실행하기 위한 데이터와 명령어를 저장하기 위해 할당받는 메모리 공간이다. 위의 그림에서는 조금 복잡하게 그려져 있지만, 크게 각 Thread 별로 할당된 PC Register, Stack, Native Method Stack 영역과 스레드와 별개로 모든 스레드가 공유하는 Method Area, Heap으로 이루어져 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-4-1. PC Register&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;PC Register의 경우 간단히 말해 각 스레드마다 현재 수행 중인 JVM 명령어의 주소를 저장하고 관리하여 추적을 할 수 있게 해주는 공간이다. 운영체제의 PC Register와 유사한 역할을 하지만 CPU와는 별개로 JVM에서 관리를 수행한다. 만약 실행했던 메서드가 native - native 메서드로 System.currentTimeMillis();같이 Java 바이트코드가 아닌 JVM 또는 OS 레벨에서 C/C++ 코드 등으로 구현되어 있으면, 이를 undefined로 기록한다. JVM명세서에도 native 메서드 실행 중인 경우 PC Register의 값은 정의되지 않는다고 설명한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-4-2. Stack&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스택영역은 지역 변수, 매개 변수, 메서드의 리턴 값, 연산에 사용되는 임시 데이터 등등이 저장되는 곳으로 스택에 저장되는 데이터들은 프레임 구조로 저장된다. 이 역시도 각각의 스레드 별로 생성되며 메서드 호출 시 생성되었다가 메서드 종료시 소멸된다. 이때 이 Stack영역은 위의 PC Register에서 설명한 Java 바이트코드로 이루어진 메서드가 쌓인다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-4-3. Native Method Stack&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 native 메서드를 사용한 경우에는 native 메서드를 실행하기 위한 영역이 별도로 필요하고 이는 곧 Native Method Stack에 저장된다. 예를들어 다음과 같은 코드가 있다고 해보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778403023373&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {

    public static void main(String[] args) {

        long now = System.currentTimeMillis();

        System.out.println(now);

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드에서 main(String[] args)은 Java 메서드이다. 따라서 이것이 아래에서 설명할 Stack에 쌓이고 stack frame이 만들어진다. 그리고 PC Register는 main(String[] args)의 바이트코드 위치를 가리킨다. 그런데 System.currentTimeMillis()를 호출하면 내부적으로 native 메서드로 연결된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java Virtual Machine Stack&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;└─ main() frame&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&amp;nbsp; └─ currentTimeMillis() 호출 지점에서 대기&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Native Method Stack &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;└─currentTiumeMillis() native frame&lt;/span&gt;&lt;/blockquote&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-4-4. Method Area&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메서드 영역은 클래스 수준의 정보를 저장하는 영역으로 이곳에는 클래스의 메서드 정보, 정적 변수와 같은 데이터가 저장된다. Constant Pool에는 문자 상수, 타입, 필드,객체 참조 정보가 저장된다. 참고로 찾아보니 이 메서드 영역은 JVM의 벤더마다 다르게 구현되어 있고 그중 Oracle인 경우에는 JVM JDK 7까지는 메서드 영역은 Permanent Generation(PermGen)이라고 불렸지만, 이 PermGen의 경우 JDK 8부터는 Metatspace로 완전히 대체되었다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-4-5. Heap&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1331&quot; data-origin-height=&quot;1009&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c19pto/dJMcai4hume/rk2u7xmKe6qUjMFBpquvLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c19pto/dJMcai4hume/rk2u7xmKe6qUjMFBpquvLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c19pto/dJMcai4hume/rk2u7xmKe6qUjMFBpquvLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc19pto%2FdJMcai4hume%2Frk2u7xmKe6qUjMFBpquvLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1331&quot; height=&quot;1009&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1331&quot; data-origin-height=&quot;1009&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위에서 봤다 싶이, Garbage Collector&lt;span style=&quot;text-align: start;&quot;&gt;의 경우&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;heap과&lt;/span&gt; 관련이 깊은 부분이라 여기서 heap의 구조와 함께 같이 GC를 같이 설명해 나가려고 한다. 이러한 JVM의 heap영역은 참조 데이터가 저장되는 공간으로 GC의 대상이 되는 공간이다. heap의 경우 처음 설계될 때부터 2가지 전제 - Weak Generational Htpothesis - 로 설계되었다. 먼저 대부분의 객체는 금방 접근 불가능한 - 즉 어떠한 참조도 받지 않는 낙동강 오리알 신세 - 상태가 된다. 두 번째로 오래된 객체에서 새로운 객체로의 참조는 아주 적게 존재한다. 따라서 이러한 설계 기조를 바탕으로 객체의 생존 기간에 따라 물리적으로 heap영역을 나누고 Young과 Old, 두 가지로 설계했다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Young Geneartion&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;새로이 생성된 객체가 할당되는 영역이며 대부분의 객체가 금방 접근 불가능한 상태가 되어서 Young Generation에서 생성되었다가 사자린다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 이 Young Generation의 경우 Eden, Survivor 0, Survivor 1이라는 3가지 영역으로 나뉜다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Eden - new를 통해 새로 생성된 객체가 위치하며, 여기서 GC로부터 살아남은 객체들은 Survivor 영역으로 간다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Survivor 영역 - 최소 1번 이상 GC의 활동에서 살아남은 객체가 존재하는 영역으로 특별히 Survivor 0, 1에서 둘 중 하나는 무조건 비어 있어야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 영역에서 이루어지는 Garbage Collection을 Minor GC라고 부른다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Old Generation&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 Young Generation에서 살아남은, 즉 참조 상태미여 접근 가능한 상태를 유지한 객체가 복사되는 영역이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Young Generation보다 크게 할당되고 영역의 크기가 큰 대신 GC는 덜 발생한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 영역에서 수행되는 Garbage Collection을 Major GC 혹은 Full GC라고 부른다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;번외) Permanent&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Permanent의 경우에는 직역하면 영구적인 세대인데, 생성된 객체들의 주소값이 저장된 공간이다. Class Loader에 의해 로드되는 클래스나 메서드 등에 대한 메타 데이터가 저장되는 영역이다. Java 7 까지는 heap 영역에 존재했지만, Java 8 부터는 Native Method Stack에 편입된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-4-5-1. Minor GC 과정&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1273&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BkVuv/dJMb99M368H/gwvdET185Cm4RUcMwCnNI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BkVuv/dJMb99M368H/gwvdET185Cm4RUcMwCnNI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BkVuv/dJMb99M368H/gwvdET185Cm4RUcMwCnNI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBkVuv%2FdJMb99M368H%2FgwvdET185Cm4RUcMwCnNI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1273&quot; height=&quot;1076&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1273&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Minor GC의 경우 새로이 생성된 객체의 경우 전부 Eden으로 명명된 공간으로 할당된다. 만약 Eden공간이 전부 꽉 찼다면 Minor GC가 이루어진다. 만약 이 경우 참조되고 있는 객체가 있거나 죽지 않은 객체가 있다면 Survivor 0나 Survivor 1으로 이동하게 된다. 위의 사진에서는 먼저 S0공간으로 움직이게 된다. 그 이후 S0 이외의 영역의 객체들을 삭제한다. 그 이후 Eden과 S0에서 기준치 이상으로 객체가 메모리에 할당되어 있으면 Eden과 S0에서 다시 Minor GC를 실행하여 이를 전부 Survivor 1으로 옮긴다. 이때 살아남은 모든 객체들은 age값이 1씩 증가하는데 이는 Survivor 영역에서 객체가 살아남은 횟수를 의미한다. 이 age값을 기준으로 Old영역으로 옮길지 말지 결정한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이때 중요한 것이 S0나 S1 중 하나는 무조건 비어있어야 한다. 이에 대한 이유로 디스크 조각 모음을 생각하면 편하다. 데이터 - 여기서는 메모리에 할당된 객체 - 가 흩어져서 저장되는 파편화가 이루어지면 GC가 행해질 때 데이터를 읽는 속도가 느려지게 된다. 따라서 모든 객체 데이터를 한 곳에 모음으로써 디스크 조각 모음을 통해 순차 읽기 및 쓰기 속도를 올리는 것처럼 더 빠르게 GC를 수행할 수 있게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-4-5-1. Major GC 과정&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Old영역에 특정 허용치 이상의 할당된 메모리를 사용중이면 자연스럽게 일어난다. 이때 Old 영역의 모든 객체들을 검사하여 참조되지 않는 객체들을 한꺼번에 삭제하는 Major GC를 수행한다. 하지만 이 Old Generation은 Young Generation보다 상대적으로 큰 공간을 가지고 있기 때문에 많은 시간이 걸리게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 GC의 경우에는 가장 큰 문제인 Stop-The-World(STW)문제가 생기게 된다. GC를 실행하기 위해 JVM이 프로그램을 멈추는 현상으로 GC가 작동하는 사이에 GC와 관련된 스레드를 제외한 모든 스레드를 멈추게 하여 서비스 이용에 차질이 생길 수 있다. Major GC의 경우에는 Minor GC보다 오랜 시간이 걸리기 때문에 이를 염두하고 개발을 해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 GC의 경우 여러 알고리즘이 있는데, Serial GC, Parallel GC, Parallel Old GC, CMS GC, G1 GC, Shenandoah GC, ZGC 등이 있다. 이러한 알고리즘의 경우는 나중에 파이썬과 비교하면서 어떤지 살펴보려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 파이썬의 메모리 구조&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바에서는 class를 기준으로 정보가 저장되고 class내부에 있는 메서드나 변수등이 저장되지만, 파이썬은 다르다. 파이썬은 모든 것이 객체다 - In Python, Everything is an Object. Java에서 Class와 변수들이 다르게 다루어지지만 파이썬에서는 class도 a = 1이라는 코드의 변수 a와 똑같이 취급된다. &lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;귀도 반 로섬(Guido van Rossum)은 The Python Lanugage Reference(2012)에서 다음과 같이 - &quot;파이썬에서의 객체는 데이터의 추상화이다&quot;라고 말한 적이 있다. 이를 생각해 보면, 파이썬에서 모든 데이터는 객체나 객체들 관의 관계로서 표현될 수 있다는 것이다. 이는 불리안(boolean), 정수(integer), 실수(float), 문자열(string), 그 외 등등 심지어 함수(function)까지도, 모든 것이 객체로서 실행된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;1005&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQAgF/dJMcajozja0/8huLh0IDqA7bOzofPbAazk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQAgF/dJMcajozja0/8huLh0IDqA7bOzofPbAazk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQAgF/dJMcajozja0/8huLh0IDqA7bOzofPbAazk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQAgF%2FdJMcajozja0%2F8huLh0IDqA7bOzofPbAazk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1444&quot; height=&quot;1005&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;1005&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파이썬도 Java와 같이 실행하는 - 컴파일하고 인터프리터를 사용하는 - 방식은 비슷하다. Python 소스코드가 있으면 이를 컴파일러가 컴파일한다. 이때 컴파일하는 파일의 경우 import가 된 모듈만 컴파일한다. 이 후에 이 컴파일된 바이트 코드를 Python Virtual Machine이 이와 연결된 Libaray Module을 연결시켜 실행시킨다. 이 때 Java와 다르게 파이썬은 CPython이라는 C언어로 만들어진 구현체를 가지고 있다. 쉽게 말해 Java에서 컴파일러와 JVM, Execution Engine 같은 것이 전부 포함되어 있는 프로그램이 있고 이를 Java언어가 아닌 C언어로 구현한 것이라고 보면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3-1. CPython에서의 메모리 할당&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1778413398503&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = 10
y = 10
z = &quot;Korea&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드가 있다고 해보자. 앞에서 말했다시피 파이썬에서는 모든 것이 객체이다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이때 파이썬에서 불변 데이터와 그렇지 않은 데이터가 중요해진다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 불변 데이터에는 숫자(integer, float), 문자열(string), 튜플(tuple)이다. 그 반대에는 리스트(list), 사전(dictionary), 그리고 셋(집합 혹은 set)이 있다. 간단하게 불변데이터에 대해 비유를 하자면, 마치 볼 수만 있는 혹은 참조만 할 수 있는 데이터라 보면 된다. 수정은 불가능한 데이터이다. 이를 파이썬의 객체 개념까지 추가하면, 객체를 참조 및 불러오기, 할당만 가능하며 이 데이터 객체 자체는 수정이 불가능하다. value는 수정 불가능하다. 여기에 수정이 가능한 개념이 추가되면 변화가 가능한 데이터가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇다면 이 불변 데이터에 대한 취급이 중요한 이유는 무엇일까?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일단 불변 데이터 - 불변 객체 - 의 경우는 변화 가능한 데이터보다 접근이 쉽다. 하지만 이 불변 데이터의 표면상의 수정은 Python에서 결국 또 다른 하나의 객체를 생성하기에 새로운 메모리 공간에 무조건 할당해야 한다. 하지만 그렇지 않은 데이터는 쉽게 수정이 가능하며 또 다른 복사본을 만들 필요가 없다. 그렇다면 이러한 메모리 차지를 하는 불변 데이터는 좋지 않은 것일까? 다르게 생각하면 디버깅에 좀 더 유리할 수 있는 면이 있다. 값이 바뀌지 않기에 객체의 상태를 쉽게 추적할 수 있게 해 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드를 다시 보면 메모리 상에서 다음과 같이 표시할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;865&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjlGn4/dJMcahqQVj4/b4W5HK2QWrwRBruw9eCSDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjlGn4/dJMcahqQVj4/b4W5HK2QWrwRBruw9eCSDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjlGn4/dJMcahqQVj4/b4W5HK2QWrwRBruw9eCSDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjlGn4%2FdJMcahqQVj4%2Fb4W5HK2QWrwRBruw9eCSDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1460&quot; height=&quot;865&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;865&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 위의 코드에서 x += 1을 하면 어떻게 될까? 기존 값을 업데이트하지 않고 현재 메모리 상에 11이 없으니 새로이 11 객체를 만들어 준다. 왜냐하면 앞에서 언급했던 것처럼 immutable 즉 수정이 불가능한 객체이기 때문에 새로 만들어줄 수밖에 없다. 만약 계속해서 1을 더해줘서 100까지 만들면 어떻게 될까? 이를 C언어의 느낌으로 보면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778414160508&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = 10 # malloc(x)

while x &amp;lt;= 100:
	
    # malloc(x+1)
    # free(x)
    x = x + 1&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 이를 C언어로 표현하면 malloc(x+1)을 통해 새로이 값을 할당하고 기존에 있던 값은 free(x)를 통해 메모리에서 해제한다. 정말로 CPython에서 위의 작업을 계속해서 반복하지는 않는다. 이는 파이썬의 메모리 관리하고도 관련이 깊다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;3-2. CPython에서의 메모리 관리&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1009&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wYN3O/dJMcadPrivw/oOPHjYroZ3Pxq6awaNRwLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wYN3O/dJMcadPrivw/oOPHjYroZ3Pxq6awaNRwLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wYN3O/dJMcadPrivw/oOPHjYroZ3Pxq6awaNRwLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwYN3O%2FdJMcadPrivw%2FoOPHjYroZ3Pxq6awaNRwLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1448&quot; height=&quot;1009&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1009&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 C언어로 표현한 코드처럼 빈번한 메모리의 동적할당과 해제를 방지하기 위해 CPython은 메모리 할당자의 계층을 정의하였다. 위에서부터 아래로 내려가보면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특정 객체 할당자(Object-specific Allocators) : 특정한 데이터 타입을 위한 특정한 메모리 할당자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Python의 객체 할당자(Object Allocator) : 512바이트 이하의 객체를 위한 할당자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Raw 메모리 할당자(Raw Memory Allocator) : 512바이트보다 큰 객체를 위한 할당자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일반적인 목적의 할당자(General Purpose Allocator) : CPython의 malloc 메서드&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일반화하기는 어렵지만, 위에서 밑으로 갈수록 권한이 강해지고 운영체제에 가깝게 작동하며 메모리를 다루는 용량도 커진다고 보면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특정 객체 할당자(Object-specific Allocators)의 경우 그림에 파이썬 타입이 있는 것처럼 간단한 데이터 타입을 가진 객체들마다 각각 메모리 관리 정책을 가져간다. int객체와 float객체가 다른 방식으로 실행된다고 보면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;Python의 객체 할당자(Object Allocator)의 경우 512바이트 이하의 작은 객체를 할당할 때 사용되며 만약 이보다 큰 경우에는 Raw 메모리 할당자(Raw Memory Allocator)에다가 바로 요청한다. 이 객체 할당자는 pymalloc이라고도 불린다. 만약 작은 객체가 메모리를 요구하면 그 객체만을 위해 단순히 메모리를 할당하는 것이 아닌 객체 할당자(Object Allocator)는 운영체제에 큰 블록을 요청한다. 이 큰 블록을 나중에 작은 객체들을 위해 할당된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 방식을 통해 앞에서 봤던 malloc을 여러 번 호출하지 않아도 되게 된다. 이 큰 블록을 Arena라고 하며 이 Arena의 경우 256KB의 사이즈를 가지고 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;특정 객체 할당자(Object-specific Allocators)와 Python의 객체 할당자(Object Allocator)의 경우&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;Raw 메모리 할당자(Raw Memory Allocator)가&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;파이썬 프로세스에 이미 할당한 메모리 위에서 작동한다. 즉 운영체제에다가 직접적으로 메모리를 요구하지 않고 앞에서 보았던 Private Heap위에서 작동한다. 만약 이 둘이 메모리를 더 필요로 한다면 Raw 메모리 할당자(Raw Memory Allocator)에 요청한 뒤 이 할당자가&amp;nbsp;&lt;/span&gt;일반적인 목적의 할당자(General Purpose Allocator)하고 소통하며 메모리를 할당해 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Raw 메모리 할당자(Raw Memory Allocator)는 일반적인 목적의 할당자(General Purpose Allocator)의 추상화 계층으로 만약 파이썬 프로세스가 메모리를 필요로 하면 이 할당자가 일반적인 목적의 할당자(General Purpose Allocator)와 상호작용하여 필요한 메모리를 공급해 준다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;1037&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PPdpc/dJMcaarIXlg/20iqEpkzsn78lunVu4YuRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PPdpc/dJMcaarIXlg/20iqEpkzsn78lunVu4YuRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PPdpc/dJMcaarIXlg/20iqEpkzsn78lunVu4YuRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPPdpc%2FdJMcaarIXlg%2F20iqEpkzsn78lunVu4YuRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1442&quot; height=&quot;1037&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;1037&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위에서 말한 Arena를 효율적으로 사용하기 위해 CPython은 이 Arena를 Pool로 나누어서 사용한다. 그리고 이 Pool이 다시 나누어져서 Block이 된다. Block은 가장 작은 단위로 객체에 할당될 수 있으며 객체 하나당 하나의 블록에 할당될 수 있다. 블록의 사이즈는 8의 배수만큼 커지며 최종적으로 512바이트 까지 커질 수 있다. 각 블록의 사이즈는 Size Class라고 불리고 8의 n승의 사이즈인 경우 n-1의 클래스 인덱스를 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 이런 Block을 가지고 있는 Pool은 하나의 Size Class로 이루어져 있는 Block으로 되어있다. Pool의 크기는 가상 메모리 페이지와 사이즈가 같다. 이 Pool은 다음과 같은 상태를 가질 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Used : 할당에 사용할 수 있는 block을 가졌다면 used상태이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Full : Pool에 있는 모든 Block이 사용되었다면 full상태이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Empty : 모든 Block이 사용가능하다면 empty상태이며 이 경우 블록에 대한&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;Size Class가 따로 설정되어 있지 않으며 어느 &lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;Size Class의 Block을 사용할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1778418866672&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       &quot;&quot;        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드는 Pool을 정의한 CPython의 C언어 구조체이다. 여기서 szidx가 Size Class이며 arenaindex는 이 Pool이 어느 Arena에 속해있는지 알려주며 nextpool과 prevpool은 각각 Pool들이 연결된 Linked List라는 것을 알게 해 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아무리 Pool자체가 비어 있는 상태 - 즉 전부 free인 상태 여도, CPython은 이를 운영체제에 반환하지 않고 파이썬 프로세스는 이를 계속해서 새로운 객체에 할당하는 데 사용한다. 메모리를 운영체제에 반환하는 경우는 Arena 레벨에서 이루어진다. 그래서 CPython은 정말로 필요할 때만 Arena를 생성한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3-3. CPython의 Generational Garbage Collection&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;앞의 사진에서 봤다시피 Heap에 있는 객체의 Reference Count(참조 횟수)가 가장 중요한 포인트가 된다. 하지만 이런 참조의 경우 자기 자신을 참조하거나 Circular Reference같이 서로가 서로를 참조하여 참조 횟수를 영원히 0으로 만들지 못하는 경쟁상태로 만들기도 한다. 이를 해결하기 위해 Generational Garbage Collection이 사용된다. 객체와 이러한 순환참조의 경우 시간이 드는 작업이지만 이 GC는 실시간으로 이를 스캔하지 않는다. 주기적으로 트리거를 받아 실행된다. Java와 마찬가지로 트리거가 된 경우 모든 작업이 멈추며 STW(Stop-The-World) 상태가 되어 GC가 작동한다. 따라서 CPython은 이런 작동을 자주 하지 않게 하기 위해 세대별 GC를 사용하며 이 세대는 0, 1, 2의 세 가지로 나뉜다. 새로 생성된 객체는 0으로 분류되며 GC의 과정에서 살아남을수록 상위 단계로 가게 된다. 자바의 Young Generation, Old Generation과 비슷하게 세대가 올라갈수록 GC를 실행하는 비중이 낮아진다. 이 세대에 분류된 모든 객체들의 상위 루트를 찾아가면서 만약 루트가 없는 경우면 순환참조라 파악하고 할당을 해제하는 방식으로 작동한다. 그리고 각각의 세대는 임계치(threashold)가 존재하며 이는 기본 값을 사용하거나 상황에 맞게 사용할 수 있다. 임계치를 확인하는 코드는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778421881873&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static PyObject *
gc_get_threshold_impl(PyObject *module)
/*[clinic end generated code: output=7902bc9f41ecbbd8 input=286d79918034d6e6]*/
{
    GCState *gcstate = get_gc_state();
#ifndef Py_GIL_DISABLED
    return Py_BuildValue(&quot;(iii)&quot;,
                         gcstate-&amp;gt;generations[0].threshold,
                         gcstate-&amp;gt;generations[1].threshold,
                         gcstate-&amp;gt;generations[2].threshold);
#else
    return Py_BuildValue(&quot;(iii)&quot;,
                         gcstate-&amp;gt;young.threshold,
                         gcstate-&amp;gt;old[0].threshold,
                         gcstate-&amp;gt;old[1].threshold);
#endif
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드의 Py_GIL_DISABLED가 정의되어 있지 않다면, 즉 일반적인 Python GIL이 적용되어 있는 경우이다. 파이썬 3.13.0부터 도입되었을 것이며 CPython에서 &lt;span style=&quot;text-align: start;&quot;&gt;GIL을 해제하여 빌드한 경우에 생기는 것이다. 즉 일반적인 경우에는 세대가 0, 1, 2로 구분되어 사용된다. 하지만 신기한 점은 만약 이 구현체가 GIL을 해제하여 빌드한 경우이다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778422200747&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef struct _gc_runtime_state GCState;

struct _gc_runtime_state {
    int enabled;
    int debug;
#ifndef Py_GIL_DISABLED
    struct gc_generation generations[NUM_GENERATIONS];
#else
    struct gc_generation young;
    struct gc_generation old[2];
#endif
    struct gc_generation permanent_generation;
    struct gc_stats *generation_stats;
    int collecting;
    _PyInterpreterFrame *frame;
    PyObject *garbage;
    PyObject *callbacks;
    Py_ssize_t heap_size;
    Py_ssize_t long_lived_total;
    Py_ssize_t long_lived_pending;

#ifdef Py_GIL_DISABLED
    int freeze_active;
#else
    PyGC_Head *generation0;
#endif
};&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드는 임계치를 가져오는 get_gc_state();코드에서 리턴값으로 가지는 구조체이다. 특이한 점은 위의 세대별 분류가 기존에는 0,1,2였다면 이번에는 young과 두 단계의 old가 추가된 것이다. 이는 CPython의 GIL을 선택적으로 해제하자고 제안한 &quot;PEP 703 &amp;ndash; Making the Global Interpreter Lock Optional in CPython&quot;에서 나타나는 부분이다. 이 부분이 나온 PEP 703의 일부분을 번역하면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 PEP는&amp;nbsp;CPython이 GIL 없이 빌드될 때, 세대별 순환 가비지 컬렉터를&amp;nbsp;비세대형 가비지 컬렉터로 전환하는 것을 제안한다. 이는 하나의 세대, 즉 &amp;ldquo;old generation&amp;rdquo;만 존재하는 것과 같다. 이 변경이 제안된 이유는 두 가지다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;순환 가비지 컬렉션은 young generation만 대상으로 하더라도, 프로그램 내 다른 스레드들을 일시 중지해야 한다. 저자는 young generation의 빈번한 컬렉션이 멀티스레드 프로그램에서 효율적인 확장을 저해할 수 있다고 우려한다. 이것이 old generation보다 young generation에서 문제가 되는 이유는, young generation의 컬렉션은 고정된 할당 횟수 이후에 수행되는 반면, older generation의 컬렉션은 힙에 존재하는 live object 수에 비례해 스케줄링되기 때문이다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또한 GIL이 없는 환경에서는 각 세대에 속한 객체들을 효율적으로 추적하기가 어렵다. 예를 들어, 현재 CPython은 각 세대의 객체들을 linked list로 관리한다. CPython이 이 설계를 유지한다면, 그 linked list들은 thread-safe 하게 만들어져야 한다. 그런데 이를 효율적으로 수행하는 방법은 명확하지 않다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;세대별 가비지 컬렉션은 다른 여러 언어 런타임에서는 효과적으로 사용된다. 예를 들어 Java HotSpot의 여러 가비지 컬렉터 구현은 여러 세대를 사용한다. 이러한 런타임에서 young generation은 자주 처리량 측면의 이점을 제공한다. young generation에 속한 객체 중 상당수가 보통 &amp;ldquo;죽은&amp;rdquo; 상태이기 때문에, GC는 수행한 작업량에 비해 많은 양의 메모리를 회수할 수 있다. 예를 들어, 여러 Java 벤치마크에서는 &amp;ldquo;young&amp;rdquo; 객체의 90% 이상이 일반적으로 컬렉션 된다는 결과를 보여준다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 현상은 흔히 &amp;ldquo;weak generational hypothesis&amp;rdquo;라고 불린다. 즉, 대부분의 객체는 생성된 지 얼마 되지 않아 죽는다는 관찰이다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 CPython에서는 참조 카운팅을 사용하기 때문에 이 패턴이 반대로 나타난다. 대부분의 객체가 여전히 일찍 죽기는 하지만, 이들은 참조 카운트가 0이 되는 시점에 즉시 수거된다. 가비지 컬렉션 사이클까지 살아남은 객체들은 대체로 계속 살아 있을 가능성이 높다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 차이 때문에, 세대별 컬렉션은 다른 여러 언어 런타임에서만큼 CPython에서는 효과적이지 않다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, 기존에는 generation을 0,1,2로 구분한 것이 그저 Old만 존재하는 것처럼 보이며 이는 young과 old로 구분하는 비 세대형 GC를 제안한 것이다. 그 이유로는 Stop-The-World로 인한 멀티스레딩 상태에서의 영향을 줄이기 위한 것이다. 아무튼 갑작스럽게 다른 이야기로 새어나간 것 같다. 정리하자면 파이썬에서는 GC를 할 때 세대별로 일반적인 경우에는 0,1,2세대같이 구분하여 메모리의 해제를 수행한다는 것을 알 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. 파이썬과 자바와의 차이점&lt;/span&gt;&lt;/h2&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4-1. Java의 JVM과 Python의 CPython의 큰 차이&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java는. java를 컴파일한 뒤 이를. class의 바이트코드로 만들고 JVM이 이를 실행한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java source &amp;rarr; javac &amp;rarr;. class bytecode &amp;rarr; JVM 실행&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Python도 소스코드를 컴파일한 뒤 여기서 생성한 바이트코드를 Python Virtual Machine이 실행한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Python source &amp;rarr; compile &amp;rarr; code object /. pyc bytecode &amp;rarr; Python VM 실행&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉 둘 다 바이트코드를 실행하는 가상 머신이 있다. 다만 Python은 JVM처럼 이를 저장하는 Runtime Data Area와 같은 구조가 없다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4-2. JVM Runtime Data Area와 CPython 구조 비교&lt;/span&gt;&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;CPython&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;JVM에서의 역할&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Java JVM의 요소&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Object(객체) - function, variable 등등&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스 메타데이터, 메서드 정보, constant pool&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Method Area&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Heap, Private Heap&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;객체 인스턴스 저장&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Heap&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Call Stack, Value Stack&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메서드 호출 프레임, 지역 변수 등등&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java Stack&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Stack frame의 instruction pointer&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 실행중인 바이트 코드의 위치&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;PC Regster&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;C stack, C extension call stack&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;native 메서드(외부 API) 호출용 스택&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Native Method Stack&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특히 Java의 Method Area가 된다면 어떻게 되는지 코드로 비교해 보자면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778424420488&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class User:

    count = 0

    def hello(self):

        print(&quot;hi&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드는 파이썬에서 호출하면 print(User)를 하면 &amp;lt;class '__main__. User'&amp;gt;와 같이 출력되고 이는 User 클래스 자체가 하나의 객체라는 것을 알 수 있다. 이를 Java에 대응한 코드로 비슷하게 바꿔 보면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778424489484&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class User {

    static int count = 0;

    void hello() {

        System.out.println(&quot;hi&quot;);

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;User 클래스 메타데이터, 필드 정보, 메서드 정보, static 변수, constant pool과 같은 정보가 Method Area에서 관리된다. 파이썬에서는 Java의 Method Area처럼 별도 영역에 클래스 메타데이터를 저장한다기보다는, Python에서는&amp;nbsp;클래스 자체가 heap 위의 객체이고, 그 내부 dictionary에 속성과 메서드가 들어간다. 이는 곧 모든 것이 객체다라는 파이썬의 설계철학에서 나오는 차이라고도 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4-3.&amp;nbsp;Java 객체 필드와 Python 인스턴스 변수 비교&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1778424725129&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class User {

    String name;

}

User u = new User();
u.name = &quot;Kim&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1778424742421&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class User:

    pass

u = User()
u.name = &quot;Kim&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 두 코드를 비교해 보자. 첫 번째가 자바코드이고 두 번째가 파이썬 코드이다. 파이썬에서는 인스턴스의 변수도 객체의 __dict__에 저장된다. 이는 동적 타입을 가지고 있는 파이썬의 설계로 인한 것으로 Java는 클래스 설계 시 필드가 정해지지만 파이썬은 런타임에 인스턴스 속성을 추가할 수 있다. 즉 이를 정리하자면 Java는 클래스 메타데이터, static 필드, constant pool 등을 Method Area 모델로 설명할 수 있지만, Python은 클래스, 함수, 모듈, 코드도 모두 객체로 취급하고 이로 인해 대부분 heap 위의 객체와 namespace dictionary로 관리한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4-4. Garbage Collection의 차이&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java JVM은 객체 생존 여부를 tracing GC가 판단하고 reachability graph를 따라가며 살아 있는 객체를 찾고, 나머지를 수거한다. 반면에 CPython은 기본적으로 reference counting이다. 참조 횟수가 0이 되면 즉시 해제되고 순환 참조는 별도의 cyclic GC가 보조적으로 처리한다. Java는 도달 가능한지를 여부로 판단하고 Python은 참조 횟수가 몇 번인지 그리고 순환 참조인지 여부로 결정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그러면 이런 생각이 든다. Java는 순환참조가 발생하지 않는가? 하지만 가장 큰 차이점인 파이썬은 참조 횟수로 판별하는 반면, Java는 GC Root, 즉 현재 실행 중인 스레드의 stack local variable, static field, JVM 내부에서 참조 중인 객체 등과 같이 판별되는 기준이 다르다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778425137472&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Node {

    Node other;

}

Node a = new Node();
Node b = new Node();

a.other = b;
b.other = a;

a = null;
b = null;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드를 보면 객체끼리는 서로 참조하고 있다. 하지만 순환 참조 여부와 무관하게 GC Root에서 도달 불가능하면 수거 대상이 되므로 순환 참조가 발생하든 말든 아무 문제가 되지 않는 것이다. 이 점에서 Java GC는 CPython의 단순 reference counting보다 순환 구조에 강하다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4-4.&amp;nbsp;Garbage Collection으로 인한 메모리 누수 차이&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1778425489358&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static List&amp;lt;Object&amp;gt; cache = new ArrayList&amp;lt;&amp;gt;();

void add(Object obj) {

    cache.add(obj);

}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 위의 코드에서 객체를 계속 cache변수에 넣고서 제거하지 않으면 static field는 List를 참조하고 이는 Object를 참조하게 되어 GC Root인 static field에서 시작되어 계속해서 참조 - reachable 하기에 수거 대상이 되지 않는다. 즉 계속해서 메모리에 남게 된다. 파이썬도 이와 비슷하다, 특히 전역 객체에서 이러한 문제가 많이 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778425639052&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cache = []

def add(obj):

    cache.append(obj)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드에서 cache는 전역변수로 계속해서 살아있기 때문에 이 전역 변수 리스트가 계속 객체를 참조하면 참조 횟수가 0이 되지 않아 메모리 누수가 발생한다. 즉 큰 차이는 Java의 경우 GC Root에 접근되는지 Python은 참조 횟수가 0이 아닌지에 대한 것이라고 보면 될 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4-5.&amp;nbsp;Garbage Collection 실행 시의 STW(Stop-The-World)의 경우&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java는 앞에서 말한 다양한 알고리즘(G1, ZGC, Shenandoah, Parallel GC, Serial GC)을 통해 최대한 스레드가 멈추는 시간을 줄이려고 한다. CPython의 경우 참조 횟수가 0이 되는 순간 스레드에서 즉시 해제한다. 하지만 순환 참조를 탐지하는 경우에는 일시적인 작업 중단이 발생할 수 있다. 그리고 가장 큰 문제는 GIL이다. 즉 Java는 멀티 스레드를 통해 병렬 실행을 하고 이를 위해 GC도 동시성과 병렬성을 위한 설계로 접근하지만, CPython은 GIL로 인해 Python 바이트코드는 한 번에 한 스레드 중심으로 실행된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결국 파이썬과 Java는 놓인 상황자체가 다르다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;정리하자면 Java JVM은 GC Root에서 도달 가능한 객체를 살리고, 도달 불가능한 객체를 tracing GC가 수거한다. 이때 순환 참조는 문제가 되지 않는다. 실제 해제 시점은 GC 정책에 따라 비결정적이다. 반면 CPython 객체마다 reference count를 기준으로 한다. 이 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;count가 0이 되면 즉시 해제한다. &lt;/span&gt;순환 참조는 참조 횟수만으로 못 잡기 때문에 cyclic GC가 별도로 처리한다고 보면 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 타입에서는 어떨까? 정적 타입과 동적 타입&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JVM / Java는 정적 타입 언어를 실행하기 위한 VM이며 변수, 필드 및 메서드 시그니처의 타입이 컴파일 시점에 결정되며 런타임에는 그 타입 정보를 바탕으로 검증, 최적화 및 동적 디스패치를 수행한다. 반면 CPython / Python은 동적 타입 언어를 실행하기 위한 VM이며 변수 이름에는 타입이 없고, 객체에 타입이 있고 이를 위해 연산 시점마다 실제 객체의 타입을 확인해서 동작을 결정한다. 한마디로 Java는 &amp;ldquo;변수의 타입&amp;rdquo;이 중요하고, Python은 &amp;ldquo;객체의 타입&amp;rdquo;이 중요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot;Java source &amp;rarr; javac 타입 검사 &amp;rarr; bytecode 생성 &amp;rarr; JVM 실행&quot;과 같은 실행 순서에서 컴파일 시점에서 변수에 입력한&amp;nbsp;타입으로 결정된다. 이는 객체도 똑같다. 하지만 파이썬은 변수에 타입이 없다. 객체와 이를 담는 변수는 따로 구별되어 다루어지기 때문이다. 이를 단적으로 보여주는 코드가 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778426482421&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Object x = &quot;hello&quot;;
System.out.println(x.length()); // 컴파일 에러

Object x = &quot;hello&quot;;
System.out.println(((String) x).length());&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드에서 파이썬이라면 당연하게 객체 중심으로 이루어져서 length라는 메서드가 호출이 가능하겠지만, Java 컴파일러는&amp;nbsp;x를&amp;nbsp;Object&amp;nbsp;타입으로 본다. 즉 Object에는&amp;nbsp;length()가 없고 이에 따라 컴파일 에러가 난다. Java에서 이를 해결하기 위해 캐스팅이 필요하다. 즉 정적 타입은 Object이지만 실제 런타임 객체 타입은 String이라고 알려주는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Java는 타입 오류를 컴파일 시점에 많이 잡고 Python 타입 오류를 실행 시점에 만나게 된다. 즉 대응할 수 있는 시점 자체가 다르다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 강타입과 약타입의 차이는 JIT Compile방식에서도 나타난다. 물론 파이썬도 이를 지원하기는 하나, 따로 설정을 해줘야 하니, 근본적으로는 CPython의 경우에는 없다고 보는 편이 낫다. Java의 경우 정적 타입 덕분에 JVM JIT가 최적화하기 좋다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778426790808&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int add(int a, int b) {

    return a + b;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JIT는 코드를 CPU의 정수 덧셈으로 강하게 최적화할 수 있다. 물론 Java도 다형성이 있으므로 런타임 프로파일링을 사용하지만, 파이썬의 동적 타입으로 인한 매 연산마다 많은 확인이 필요한 것과는 다르다. 파이썬의 단순한 코드 &quot;c = a + b&quot;의 경우 런타임에서 &quot;a 객체 로드, b 객체 로드, a의 타입 확인, + 연산 슬롯 찾기, b와 호환되는지 확인, 결과 객체 생성, refcount 조정&quot;과 같은 작업을 거치게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <category>python</category>
      <category>개발자</category>
      <category>공부</category>
      <category>자바</category>
      <category>컴퓨터</category>
      <category>코드</category>
      <category>코딩</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/72</guid>
      <comments>https://kimkani.tistory.com/72#entry72comment</comments>
      <pubDate>Mon, 11 May 2026 00:35:23 +0900</pubDate>
    </item>
    <item>
      <title>[토이프로젝트 배포기] JudgeYourPrompt의 백엔드 편 : Python(파이썬), FastAPI, DDD, Dependency Injector, SQLAlchemy, Alembic, DI, IoC  그외 등등</title>
      <link>https://kimkani.tistory.com/71</link>
      <description>&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;목차&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 토이 프로젝트 고민하기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 스택 정하기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. FastAPI 사용기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. DDD 사용하기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 추후 개발 개선 방향&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;JudgeMyPrompt_logo_circle_bubble-check_fixed_1024_transparent.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kEKSx/dJMcaibgu3Z/Gt4oCN8TcwkIgMa1oapmuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kEKSx/dJMcaibgu3Z/Gt4oCN8TcwkIgMa1oapmuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kEKSx/dJMcaibgu3Z/Gt4oCN8TcwkIgMa1oapmuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkEKSx%2FdJMcaibgu3Z%2FGt4oCN8TcwkIgMa1oapmuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;447&quot; height=&quot;447&quot; data-filename=&quot;JudgeMyPrompt_logo_circle_bubble-check_fixed_1024_transparent.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 토이 프로젝트 고민하기 : LLM이 범람하는 시대와 나의 번아웃&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1020.png&quot; data-origin-width=&quot;2240&quot; data-origin-height=&quot;1260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXKXia/dJMb99L7uxc/T0tKQQAEem6E1KaeKb8cLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXKXia/dJMb99L7uxc/T0tKQQAEem6E1KaeKb8cLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXKXia/dJMb99L7uxc/T0tKQQAEem6E1KaeKb8cLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXKXia%2FdJMb99L7uxc%2FT0tKQQAEem6E1KaeKb8cLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2240&quot; height=&quot;1260&quot; data-filename=&quot;1020.png&quot; data-origin-width=&quot;2240&quot; data-origin-height=&quot;1260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LLM이 범람하는 시대다. ChatGPT부터 Gemini, Claude, 그리고 Grok 등등. 다양한 AI LLM 서비스들이 범람하는 시대다. 누구는 OpenAI의 ChatGPT를 가장 좋다고 생각하고, 누군가는 Gemini의 상요성을 좋게 평가하고, 혹자는 Claude를 좋아한다. 이렇게 다양한 AI 서비스에 대한 취향이 넘쳐나는 시대에 각자의 취향에 맞는 서비스에 입력하는 LLM 프롬프트도 다양하다. 그리고 그 프롬프트의 결과도 시시각각 다르다. 나도 다양한 모델을 사용하며 입맛에 맞게 프롬프트를 입력하고 그 프롬프트를 사용하고는 했다. 그러다가 든 생각이 있었다. 이러한 프롬프트나 결과들을 누군가가 보면 어떤 생각을 할까?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;처음에는 단순한 생각에서 출발했다. 내가 지금 이 모델에 맞게 제대로 된 성능을 뽑아내는 최적의 프롬프트를 작성하고 있는 것일까? 혹은 내가 쓴 프롬프트 의외로 괜찮은 거 같은데 다른 사람들은 어떻게 생각할까? 라는 궁금증이었다. 여기서 더 나아가서 든 생각은 그렇다면 이런 프롬프트를 모아두거나 혹은 프롬프트를 잘 쓰는 방법을 이야기하는 사이트가 있을까? 하는 생각이었다. 물론 여러 사이트에서 프롬프트를 어떻게 사용할지에 대해서는 이야기하고 있었다. 국내만 봐도 나무위키 같은 대중 정보 취합 사이트에서도 간단하게 적혀 있을 정도고 특정 주제에 대해서는 Medium같은 블로그 사이트에서 아예 전문적으로 글을 작성하는 사람도 있다. 해외만 봐도 레딧에서 활발하게 토론하는 스레드가 열릴 정도이다. 하지만 이것을 한 곳에 모아둔 사이트 혹은 전문적인 사이트는 내가 조사한 바로는 없었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 토이프로젝트겸 한 번 만들어 보는 것도 좋지 않을까 하는 생각을 1년 정도 생각해 두었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 그 1~2년의 시간이 지난 올해 1월 예전부터 이 생각을 구현하기 위해 조금씩 틀을 잡아오긴 했지만 일에 치어서 시간 부족으로 크게 디벨롭하지 못하고 생각과 작은 코드 덩어리로 남겨두고 있었다. 그러다가 7월 말 퇴사후 8월 부터 쉬기 시작한 뒤 대략 5~6개월 정도 쉬고 난 뒤 회복이 된 날, 나는 무료함을 느꼈다. 사실 퇴사 직후 개발을 아예 손을 뗄 정도로 회사에서 일을 하다가 번아웃을 느꼈다. 그 결과 개발이 오히려 미워지는 순간이 오게 되었다. 그 시절에는 개발의 영어 한 글자 꼴도 보기 싫었다. - 물론 회사 동료들은 좋았다. 하지만 나는 못버틸 것 같다는 생각이 개발로 못 버티겠다는 생각으로도 이어진 것 같다 - 번아웃은 결국 내 정신을 갉아 먹었고 회사에서 한 이 개발이 나를 망친 것 같은 생각이 들었기 때문이다. 하지만, 이 시간이 지나가고 개발이 조금씩 내 손에서 다시 욕망의 꿈틀림으로 몸부림치고 있는 순간이 다가왔다. 그 때는 손가락에 점점 코드의 맛이 사라지고 있을 때 즈음이었다. 그러다가 생각해 둔 내 토이 프로젝트 주제와 작은 코드 덩어리가 생각났다. 이걸 계기로 다시 개발을 시작해 보면 어떨까 하는 생각이 들었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇게 주제를 다시 고민했다. 큰 틀은 벗어나지 않았다. 하지만 큰 틀만으로는 부족했다. 글을 쓸 때도 그렇지만, 아무리 아이디어가 있어도 재료가 될 목차는 필요했다. 그래야 그 틀을 벗어나지 않는 결과물이 나온다고 생각하기 때문이다. 그래서 살짝 목표를 상세하게 잡았다. &quot;프롬프트를 모아볼 수 있는 사이트&quot;가 큰 틀이었다면 여기서 조금 상세하게 디벨롭하는 것이다. 그래서 고민했다. 뭐 그렇게 치열하게 고민하지는 않았고 적당한 열기가 들 정도로 고민했다. 너무 상세하게 고민하면 오히려 제품 구상만 하다가 그 열기에 취해서 결국 코드를 짜지 못하고 제품으로 빚어내지 못하는 경우가 많았다는 것을 느꼈기 때문이다. - 이 부분은 직전 회사에서 많이 배운 부분이다. 아직도 감사하게 생각한다. 어찌보면 내가 단순 개발자가 아닌 프로덕트 개발자가 되는 계기가 된 것 같다 - 아무튼 그렇게 고민하다가 큰 틀에서 벗어나지 않는 적당히 상세한 틀을 정했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프롬프트와 프롬프트의 결과를 공유할 수 있어야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프롬프트와 그 결과에 대한 의견을 공유할 수 있는 댓글 시스템이 필요하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;회원이 가입하고 직접 글을 써야한다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자신이 어떤 글을 썼는지 혹은 댓글을 달았는지 알 수 있어야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 이 프로덕트는 결심한 뒤 1~2개월 안에 나와야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 5가지로 큰 틀을 잡았다. 처음에는 프롬프트를 단순하게 모아두는 사이트로 생각했다가, 이걸로는 깃허브 Awesome 시리즈 README.md와 뭐가 다를 것인가 라는 생각이 들어서 커뮤니티로 살짝 방향을 디벨롭했다. 그렇게 큰 틀과 그에 맞는 몇몇의 세세한 방향성 그리고 스스로에게 둔 기간까지 정해졌으니 이제 개발 스택을 정할 차례가 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 스택 정하기 : 백엔드는 FastAPI로&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1315&quot; data-origin-height=&quot;740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUgls3/dJMb99L7UdA/qvSBp7k9hCOY4CT34Ke3k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUgls3/dJMb99L7UdA/qvSBp7k9hCOY4CT34Ke3k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUgls3/dJMb99L7UdA/qvSBp7k9hCOY4CT34Ke3k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUgls3%2FdJMb99L7UdA%2FqvSBp7k9hCOY4CT34Ke3k1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1315&quot; height=&quot;740&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1315&quot; data-origin-height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이전 회사에서는 주로 파이썬을 이용한 백엔드 툴을 사용했다. Flask(이하 플라스크)와 Django(이하 장고)가 메인이었다. 하지만 장고에는 손이 가지 않았다. 일단 장고를 너무 자주 사용해서 질리기도 한 부분이 있고 장고의 경우 흔히 말하는 풀스택 프레임워크다. 즉, 다양한 기능을 제공해 주지만, 그만큼 그 기능을 내가 만들 서비스에서 다 쓸 것인가?라는 생각이 들었다. 물론 장고와 django-rest-framework 혹은 django-ninja를 통해 간편하게 RestAPI를 설정할 수 있고, 데이터베이스 설계도 따로 라이브러리를 설치할 필요 없이 모델 폴더를 설정한 뒤 그 안에다가 모델을 코드로 적어서 &quot;make migrations&quot;를 CLI에 입력하면 금방 코드로 만들어 주고 그거를 &quot;migrate&quot;를 통해 간단하게 디비에다가 반영할 수도 있다. 이렇게 하면 손 쉽게 내가 하려는 프로젝트를 1달이라는 시간 내에 빨리 끝낼 수 있다는 생각도 했다. 진지하게 고민하다가 결국 장고는 쓰지 않기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일단 &quot;필요가 없다&quot;, 이게 가장 컸다. 너무 많은 것을 제공했다. 나는 장고의 불필요하게 많은 것을 이 프로젝트에 투입하고 싶지 않았다. User 기능부터가 그랬다. 물론 백오피스 기능이나 프론트 기능도 바로 구축이 가능하겠지만 - 추후에 쓸 글에서 언급하겠지만 - 리액트를 쓸 것이고, 백 오피스도 있어서 좋겠지만 지금 당장은 백오피스 등등 장고의 다양한 기능들을 사용하다 보면 오히려 이것을 세세하게 조정하거나 세팅하는 과정에서 오히려 시간을 더 뺏길 것 같았다. 두번 째로 &quot;하고 싶지 않았다&quot;, 너무 장고를 자주 - 뭐 그래봤자 2~3년 밖에 실무에서 사용하지 않았지만, 그것도 나에게는 긴 시간이다 - 사용했다고 생각했다. 내가 재밌게 하고 스스로를 디벨롭 하자는 생각으로 하는 프로젝트인데, 이걸 굳이 &amp;nbsp;장고를 쓰면서 고통받고 싶지 않았다. 마치 &quot;끔찍한 시간을 보내고 싶어?&quot;를 반복하고 싶지 않았다. 그래서 장고는 일단 기각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇다면 여러 선택지가 남아있다. 일단 대표적으로 Java/Kotlin과 Spring같은 국민적인 스택이다. 사실 나에게 가장 부족한 부분이 이 스택이다. 이 설명을 하자면, 내가 지금까지 실무에서 써 온 스택은 기업에서 사용하는 '일반적인' 스택과는 많이 달랐다. 그래서 이 부분을 장고 다음으로 진짜 진지하게 고민했다. 내가 부족한 부분이 맞고, 이것을 계기로 어느정도 자바 혹은 코틀린과 함께 스프링 - 흔히들 말하는 자프링/코프링 스택 - 을 잘하면 체득할 수 있는 기회라고 생각했기 때문이다. 하지만 허들이 있다. 일단 스프링 프레임워크다. 언어는 문제가 되지 않는다. 언어는 거기서 거기다라고 생각한다 - 물론 이 말을 듣고서 굉장히 극대노할 사람도 있겠지만, 내 의견에서는 그냥 큰 틀에서 비슷하다는 의미다, 깊게 파고들면 다 다른 것은 맞다, 한중일 모두 동아시아인이어도 한 국가를 깊게 파고들면 다 다른 것이 맞는데 말이다 - 문제는 스프링이었다. 내가 이 스프링을 이용해서 프레임워크를 배워 프로젝트와 프로덕트를 디벨롭하고 제품으로 그럴듯하게 보일 수 있을까?라는 생각이었다. 큰 고민을 했다. 하지만 앞에서 세운 5가지 기준 중 마지막이 이 생각을 고이 접게 만들었다. 바로 &quot;1~2달 내로 끝내는 것&quot;, 이 부분을 고려하니 이걸로는 힘들다는 생각을 했다. 이 프로젝트는 내 재미와 속도감 있는 프로덕트 빌딩을 위한 것이라고 생각했다. 그래서 자프링/코프링 스택은 생각을 접었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;남은 것은 두 가지 생각이었다. Python에서 그대로 갈 것인지 아니면 아예 리액트와 맞춰서 JavaScript/TypeScript의 Nest.js 혹은 Express.js + GraphQL스택을 해볼 것인가?였다. 그러다가 생각이 난 것이 FastAPI다. 예전부터 애정을 가지고 있었는데, 어쩌다가 까먹은 나를 원망했다. 아니 이런 스택을 까먹다니! 내가 생각한 기간에 맞출 정도로 어느정도 익숙하기도 하고 큰 기능을 필요로 하지도 않는 내 프로젝트에 가볍게 - 근데 DDD나 Dependency Injector를 적용하면서 그런거 같지도 않다 - 사용할 수 있지 않을까?라는 생각을 하고 결국은 FastAPI로 결정햇다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. FastAPI 사용기 : FastAPI를 위한 라이브러리 정하기&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;logo-teal.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buJtQj/dJMcafr3w9D/7qQK45d9SCQO92bbvIEE00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buJtQj/dJMcafr3w9D/7qQK45d9SCQO92bbvIEE00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buJtQj/dJMcafr3w9D/7qQK45d9SCQO92bbvIEE00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuJtQj%2FdJMcafr3w9D%2F7qQK45d9SCQO92bbvIEE00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;369&quot; data-filename=&quot;logo-teal.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇게 백엔드 스택을 정하고 난 뒤 세팅을 시작했다. 간단하게 pip을 사용하고 pylint를 사용할 수 있었지만, 무언가 최신 혹은 최근에 나온 파이썬에서 핫한 스택들을 사용하고 싶었다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 패키지 관리자의 경우에는 pip을 처음에는 사용하다가 그 다음에는 poetry로 넘어갔고 그 다음에는 poetry를 사용했다. 그러다가 uv의 존재를 알게 되었다. Rust로 작성되어서 굉장히 빠르고 설치나 관리도 편하다는 것이었다. 그래서 uv로 - 근데 지금 생각하면 그렇게 많은 패키지를 관리할 필요도 없었고, 오히려 poetry만으로도 충분하지 않았나 하는 오버 엔지니어링이 생각나기도 한다 - 넘어갔다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;린터의 경우에는 처음에는 pylint를 사용하다가, 타입 추론 및 정합을 위해 mypy를 추가했다. 워낙 FastAPI의 경우에 타입 힌팅(type hinting)과 type annotation(타입 어노테이션)을 잘 활용해서 궁합이 좋다고 생각했다. 실제로도 mypy와의 궁합은 좋았고, 내가 놓친 부분을 잡아 주기도 하는 등 여러 도움을 받았다. 그리고 린터를 black으로 넘어갔는데, 그때 다시 위의 uv처럼 ruff를 찾게 된다. uv의 경우 지금 생각하면 오버 엔지니어링이 아닌가 하는 생각이 들지만, ruff의 경우에는 린팅 속도가 굉장히 빠르다고 하고 실제로도 사용하면서 빠른 사용성을 보여줘서 오버 엔지니어링은 아니라고 생각한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1*wF2CvFviu6zK7d-5c9_UOA.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dABzGd/dJMcacvmInd/W8rD6EkNB7zezz0T4ZZ8d0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dABzGd/dJMcacvmInd/W8rD6EkNB7zezz0T4ZZ8d0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dABzGd/dJMcacvmInd/W8rD6EkNB7zezz0T4ZZ8d0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdABzGd%2FdJMcacvmInd%2FW8rD6EkNB7zezz0T4ZZ8d0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;480&quot; data-filename=&quot;1*wF2CvFviu6zK7d-5c9_UOA.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇게 자잘한 부분을 세팅한 뒤 백엔드인 FastAPI를 위한 ORM툴 과 디비 스키마 툴을 생각했다. DB설계의 경우에는 그냥 간단하게 SQLAlchemy를 써서 SQL문으로 하나하나 설정한 뒤 반영할 수 있기도 했다. 하지만 장고에서 들인 그 편한 손 맛을 잊지 못해서 alembic을 사용하게 되었다. 사실 트레이드 오프가 있는 듯 하다. alembic을 사용해서 편하게 데이터베이스 마이그레이션을 파이썬 코드로 관리하고 이를 형상화할 수 있겠지만, 그만큼 세세한 조정이 힘들다는 점도 있다. 물론 내 프로젝트를 생각하면 데이터베이스에서 그렇게 미세 조정이 필요할까 라는 생각을 했고, 추가적으로 나중에 필요해지면 SQLAlchemy에서 지원하는 SQL문 입력을 이용해서 디비 스키마를 설계하는 것이 나을 것이라고 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇게 FastAPI를 위해 필요한 라이브러리들을 간단하게? 정한 뒤 설치하고 본격적인 개발을 시작했다. 일단 FastAPI의 장점이라고 한다면 파이썬의 Type Hinting 기능을 100% - 까지는 모르겠으나 그래도 90%는 진짜 잘 활용하고 있다고 생각한다 - 활용한 파이썬에서 지키기 힘든 타입 문제와 FastAPI에서 사용하는 Pydantic의 데이터 정합성 맞추기다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;물론 개발을 진행하면서 문제에 직면하는 것은 당연지사다. 바로 Django라면 당연히 @atomic같은 데코레이터를 활용해 데이터베이스 트랜잭션 락 관리가 없다는 것이다. 물론 그냥 넘어갈 수도 있겠지만, 일단 이 부분 만큼은 놓치기 싫었다. 아집이라고 해야할지 아니면 내 스스로 레벨업을 위한 것인지는 모르겠지만, 이 부분은 놓고 가고 싶지 않았다. 그래서 인터넷을 뒤져서 이 트랜잭션을 관리할 데코레이터를 직접 만든 사례나 SQLAlchemy를 활용한 사람들, 특히 비동기 asyncio를 활용한 디비 트랜잭션을 사람들은 어떻게 관리하나 찾아봤다. 그리고 이 들의 사례를 조합해서 다음 섹션에서 설명할 DDD, Dependency Injector를 활용한 방안을 고려해서 Transactional이라는 데코레이터를 만들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771593350559&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Transactional:

    def __init__(self, is_readonly: bool):
        self.is_readonly = is_readonly

    def __call__(self, func: Callable[..., Any]) -&amp;gt; Callable[..., Any]:
    
        @wraps(func)
        async def _wrapper(*args, **kwargs):
            async with session_factory() as session:
                try:
                    result = await func(*args, **kwargs, session=session)
                    if not self.is_readonly:
                        await session.commit()
                    return result
                except Exception as e:
                    await session.rollback()
                    raise e
                finally:
                    await session.close()

        return _wrapper
        

...

AsyncScopedSession = async_scoped_session(
    session_factory=_async_session_factory, scopefunc=get_session_context
)

@asynccontextmanager
async def session_factory() -&amp;gt; AsyncGenerator[AsyncSession]:

    _session = AsyncScopedSession
    try:
        yield _session()
    finally:
        await _session.remove()&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드가 DDD에서 Repository단에서 직접 디비에 접근해서 트랜잭션을 수행할 때 걸 데코레이터 코드이다. 위의 코드는 session_factory()를 통해 async_scoped_session으로 생성된 AsyncSession을 받아 디비 관련 업무를 수행한다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일단 이 코드를 간단하게 설명하기 전에 SQLAlchemy의 Session에 대해서 이야기 해야 한다. &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; text-align: left;&quot;&gt;SQLAlchmey에서 Session&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; text-align: left;&quot;&gt;은 데이터베이스의 트랜잭션을 설정하고 관리합니다. 그리고 이 트랜잭션은 위의 코드에서 보이듯이 commit()이나 rollback()이 있기 전까지 유효하다. 데이터베이스의 원칙인 ACID를 잘 지키는 역할이라고 보면 될 것이다. 근데 동기 버전인 scoped_session의 경우 스레드당 하나의 세션만을 유지한다. 하지만&lt;/span&gt; asyncio의 경우 하나의 스레드에서 코루틴 들이 Event Loop를 돌면서 여러 context에 접근하는데 이때 동일한 Session에 접근하게 되면 thread-safe를 지키지 못하게 된다. 즉 &lt;span style=&quot;text-align: left;&quot;&gt;의도치 않게 세션에 접근하면서 세션들이 의도치않게 서로 공유되거나 이미 롤백 된 세션을 다른 곳에서 가져가서 다시 사용할 수 있다. 그래서 &lt;span style=&quot;text-align: left;&quot;&gt;SQLAlchemy에서는&amp;nbsp;&lt;/span&gt;async_scoped_session&lt;span style=&quot;text-align: left;&quot;&gt;클래스를 지원하고 나는 그것을 사용했다. async_scoped_session을 보면 scopefunc에 get_session_context가 들어간 것이 보일텐데, 따로 get_session_context, set_session_context, remove_session_context라는 함수를 만든 뒤에 (Async)Session을 안전하게 관리할 태그를 달아주는 것이라고 보면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;아무튼 이렇게 해서 FastAPI에서 안전하게 데이터베이스 트랜잭션을 관리할 기본 작업도 끝냈고, 이제는 어떤 식으로 백엔드 구조를 가져갈 지 고민할 차례였고, 예전부터 희망이자 염원이었던 - 장고에서는 거의 사용하기 힘들었던 - DDD, 속칭 Domain Driven Design을 사용해보기로 결심했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. DDD 사용하기 : Dependency Injector&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;logo.svg&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;941&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YdgTN/dJMcabpGZTY/pziz1tYKUsTqWz2ORyKiVk/tfile.svg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YdgTN/dJMcabpGZTY/pziz1tYKUsTqWz2ORyKiVk/tfile.svg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YdgTN/dJMcabpGZTY/pziz1tYKUsTqWz2ORyKiVk/tfile.svg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYdgTN%2FdJMcabpGZTY%2Fpziz1tYKUsTqWz2ORyKiVk%2Ftfile.svg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;529&quot; data-filename=&quot;logo.svg&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;941&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일단 DDD를 사용하기로 결정했지만 대부분의 자료는 스프링에 맞춰져 있었고, 실제로 파이썬에서는 DDD를 사용하기 어렵겠다는 생각을 하기도 했다. 하지만 Dependency Injector라는 라이브러리를 알게 되고 이렇게 하면 - 완벽하게 DDD라는 것이 있는 것이 아니지만, 그래도 흉내라도 내거나 - 사용할 수 있겠다고 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바와 스프링에서는 DI를 Spring Container가 관리해준다고 들었다. 그리고 이를 IoC, 즉 Inversion of Control, 제어의 역전이라고 부른다고 한다. 하지만 파이썬에서는 순수하게는 힘들고 이 Dependency Injector를 사용하면 가능하다고 해서 자료를 찾아서 적용해 보았다. 일단, Dependency Injector 는 크게 provider, container, wiring이 있다. provider는 객체를 생성해주고 관리해주며 container:는 이 provider의 집합이고 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;wiring은 container가 의존성 주입을 할 수 있도록 연결을 해준다. 근데 이 wiring을 사용하기 위해서는 3가지 조건이 필요하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;1) @inject 데코레이터를 통해 객체를 주입할 곳을 명시해야 한다.&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px; color: #000000;&quot;&gt;2) container.wire() 메소드를 통해 container 구현체를 모듈과 패키지에 연결해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px; color: #000000;&quot;&gt;3) provide 함수를 통해 연결 고리를 만들어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px; color: #000000;&quot;&gt;그리고 이를 코드로 적용한 부분은 먼저 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1771594800721&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Container(DeclarativeContainer):
    
    wiring_config = WiringConfiguration(packages=[&quot;server&quot;])

    user_repository = Singleton(UserSQLRepository)
    
    user_repository_adaptor = Factory(
        UserRepositoryAdaptor, user_repository=user_repository
    )
    
    user_service = Factory(UserService, repository=user_repository_adaptor)
    
    ...
    
    user_dashboard_service = Factory(
        UserDashboardService,
        thread_repository=thread_repository_adaptor,
        like_history_repository=like_history_repository_adaptor,
        comment_repository=comment_repository_adaptor,
        comment_like_history_repository=comment_like_history_repository_adaptor,
    )&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;먼저 Container에 DeclarativeContainer클래스를 상속받아 이 Container를 통해 객체를 주입하고 관리할 것을 설정했다. 그 다음에 WiringConfiguration을 통해 어느 패키지를 연결할 것인지를 설정했다. 나는 server라는 폴더에서 작업하고 있어서 server를 명시해 줬다. 그 다음은 싱글톤 패턴을 사용했다. 이 &lt;/span&gt;싱글톤 패턴은 클래스는 오직 인스턴스를 하나만 생성하고, 이 인스턴스에 전역적으로 접근 가능하도록 하는 원칙이라고 한다. 그래서 위의 코드에서 보이는 UserSQLRepository처럼 &quot;~~SQLRepository&quot;라는 직접 디비와의 연산을 처리하는 레포지토리를 만들고 이를 일반 Repository에다가 주입하는 식으로 설정했다. 그 다음 Factory를 통해 &lt;span style=&quot;text-align: start;&quot;&gt;객체 생성 과정에서 발생할 수 있는 모든 복잡한 로직은 모두 캡슐화한 뒤, 생성된 객체는 비즈니스 로직에만 집중하도록 이를 &quot;~~RepositoryAdaptor&quot;와 같이 &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;팩토리에서 생성 로직을 통해 생성된 객체가 레포지토리를 통해 저장되게 했다. 위의 코드에서 처럼 user_dashboard_service같이 다양한 레포지토리가 필요하면, 원래라면 이 로직들을 하나의 서비스에서 다 처리했겠지만, 위의 코드처럼 다르고도 다양한 repository_adaptor를 생성해준다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;그리고 이렇게 설정한 Container를 실제 코드에 주입하면 다음과 같은 코드로 예시를 들 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771595332280&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@user_router.post(&quot;/login&quot;, response_model=LoginResponse)
@inject
async def login(
    request: LoginRequest,
    usecase: UserUseCase = Depends(Provide[Container.user_service]),
):
    return await usecase.login(email=request.email, password=request.password)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드처럼 @inject 데코레이터를 통해 주입할 대상을 명시해 주고, Provide를 통해 Container에서 설정한 Factory를 주입해 준다. 이렇게 하여 대부분의 DDD를 나는 나름대로 구성했다고 생각한다. 이렇게 직접 DDD를 맛보면서 느낀 점은 일단 역할이 상당히 정확하게 분리되어서 각각의 폴더 혹은 파일의 역할이 명확해 진다는 것을 느꼈다. 그러면서 이를 통해 테스트 코드를 작성한다면 분명 역할과 책임이 분리되어 있어 테스트를 하기 유용할 것이라고 생각했다. 하지만 단점도 느꼈는데, 굉장히 파일과 폴더의 구조가 방대해 진다는 것이었다. 굉장히 관리가 힘들어진다는 것을 느꼈고 오히려 이게 개발에 있어서 유지보수는 좋을 수 있지만 속도감이나 프로젝트의 빌드 속도에 영향을 끼치는 것이 아닐까 하는 생각이 들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;뭐 일단은 단점보다는 장점이 나에게는 더 크게 다가왔다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 추후 개발 개선 방향&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일단 이렇게 백엔드 서버 개발을 하고 나니 느낀 점은 오랜만에 재밌으면서 몰입하는 경험을 했다는 것이다. 하지만 아쉬운 점은 이 DDD 부분이었다. 이 DDD 아키텍처를 충분히 활용할 테스트 코드 작성이나 DTO, VO등의 작성을 조금 소홀히 해서 아쉽다는 생각을 했다. 아마 여기서 기능을 더 디벨롭하고 시간이 남으면 테스트 코드를 더 작성하면서 코드 퀄리티나 프로덕트의 완성도를 높일 수 있지 않을까 생각한다. 더 나아가 내가 놓친 에러 부분을 더 쉽게 디버그할 수 있지 않을까 생각하기도 한다. 아무튼 테스트 코드를 나중에 작성해 봐야 겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일단 이렇게 백엔드 부분을 작성해 봤다. 다음 글에서는 프론트엔드 부분을 작성해 보겠다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Product</category>
      <category>FastAPI</category>
      <category>python</category>
      <category>개발</category>
      <category>개발자</category>
      <category>공부</category>
      <category>백엔드</category>
      <category>서버</category>
      <category>컴퓨터</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/71</guid>
      <comments>https://kimkani.tistory.com/71#entry71comment</comments>
      <pubDate>Fri, 20 Feb 2026 22:57:04 +0900</pubDate>
    </item>
    <item>
      <title>[파이썬 기본 다지기] 파이썬에서의 Descriptor(디스크립터)의 역할 및 Non-Data Descriptor(비 데이터 디스크립터), Data Descriptor(데이터 디스크립터)와 Django(장고)에서의 model Field에서 살펴보기</title>
      <link>https://kimkani.tistory.com/70</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;R1280x0.jpeg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EXTit/dJMcafeoQTL/ICTzGFBvk11RjuzfZXPxg0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EXTit/dJMcafeoQTL/ICTzGFBvk11RjuzfZXPxg0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EXTit/dJMcafeoQTL/ICTzGFBvk11RjuzfZXPxg0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEXTit%2FdJMcafeoQTL%2FICTzGFBvk11RjuzfZXPxg0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;807&quot; data-filename=&quot;R1280x0.jpeg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 디스크립터란?&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파이썬 공식 문서에서는 다음과 같이 디스크립터에서 간략하게 설명하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;A&amp;nbsp;descriptor&amp;nbsp;is what we call any object that defines&amp;nbsp;__get__(),&amp;nbsp;__set__(), or&amp;nbsp;__delete__().&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Optionally, descriptors can have a&amp;nbsp;__set_name__()&amp;nbsp;method. This is only used in cases where a descriptor needs to know either the class where it was created or the name of class variable it was assigned to. (This method, if present, is called even if the class is not a descriptor.)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;디스크립터는 __get__(), __set__(), 혹은 __delete__()를 정의한 아무 객체를 지칭한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추가적으로 디스크립터는 __set_name__()를 가질 수 있다. 이는 이 디스크립터가 생성된 클래스나 클래스의 변수에 할당된 디스크립터에서 구별할 필요가 있을 때 사용되어진다. (이 메서드는 클래스가 디스크립터가 아니여도 호출될 수 있다.)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;https://docs.python.org/ko/3.13/howto/descriptor.html&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉 가장 기본적인 Magic Method(매직 메소드 혹은 Special Method/스페셜 메소드)인 &lt;span style=&quot;text-align: left;&quot;&gt;__get__(), __set__(), 혹은 __delete__()이 정의된 클래스의 경우 디스크립터라고 볼 수 있다. 디스크립터의 활용은 다른 클래스의 변수로 사용될 때만 작동하고, 클래스 인스턴스에 넣으면 작동하지 않는다. 그렇다면 이런 &lt;span style=&quot;text-align: left;&quot;&gt;__get__(), __set__(), 혹은 __delete__()에는 어떤 파라메터가 들어갈까?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769838736400&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyDescriptor:
	
    def __get__(self, instance, owner):
        print(&quot;Get a value of attribute&quot;)
    
    def __set__(self, instance, value):
    	print(&quot;Set a value of attribute&quot;)
    
    def __delete__(self, instance):
    	print(&quot;Delete a value of attribute&quot;)
        
class MyClass:
	attribute = MyDescriptor()
    
&amp;gt;&amp;gt;&amp;gt; my_class = MyClass()
&amp;gt;&amp;gt;&amp;gt; my_class.attribute
&amp;gt;&amp;gt;&amp;gt; MyClass.attribute&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;__get__(self, instance, owner)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 self는 디스크립터 인스턴스 자체, instance는 디스크립터를 호출한 객체, owner는 디스크립터가 속해 있는 클래스를 의미한다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드에서 self는 MyDescriptor가 되며, instance와 owner는 &quot;my_class.attribute&quot;로 호출했을 경우 instance my_class가 되며 owner의 경우 MyClass이며, &quot;MyClass.attribute&quot;로 호출했을 경우 None이 되고 owner는 역시 MyClass가 된다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;__set__(self, instance, value)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 self는 디스크립터 인스턴스 자체, instance는 디스크립터를 호출한 객체, value는 할당된 값을 의미한다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;__delete__(self, instance)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 self는 디스크립터 인스턴스 자체, instance는 디스크립터를 호출한 객체가 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;간단하게 말해서 위의 매직 메소드가 들어가 있는 Class라면 디스크립터가 될 수 있다고 할 수 있다. 하지만 이 디스크립터도 어떤 매직 메소드가 들어가는지에 따라 두가지의 종류로 나뉜다. 바로 &quot;비 데이터 디스크립터&quot;와 &quot;데이터 디스크립터&quot;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. Non-Data Descriptor(비 데이터 디스크립터)와 Data Descriptor(데이터 디스크립터)&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비 데이터 디스크립터 : __set__()과 __delete__()가 정의되어 있지 않고 __get__()만 정의 되어있는 경우. 객체를 통한 디스크립터 접근과 클래스를 통한 디스크립터 접근을 모두 지원한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터 디스크립터 : &lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;__set__()이나 __delete__() 혹은 둘 다 정의되어 있는 경우. 객체를 통한 디스크립터 접근만 된다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;간단하게 정의가 가능하다. 즉 __set__()이나 __delete__() 중 하나라도 정의되어 있으면 데이터 디스크립터이며 그렇지 않으면 비 데이터 디스크립터라고 볼 수 있다. 근데 이 데이터 디스크립터와 비 데이터 디스크립터의 분류로 인해 호출에도 우선순위가 생긴다. 만약 데이터 디스크립터, 인스턴스 변수, 비 데이터 디스크립터가 모두 있는 상황이라면 &quot;데이터 스크립터 &amp;gt; 인스턴스 변수 &amp;gt; 비 데이터 디스크립터&quot;순으로 우선순위가 나뉜다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;2-1. 데이터 디스크립터 - __set__()의 경우&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769839860968&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyDescriptor:
	
    def __get__(self, instance, owner):
        print(&quot;Get a value of attribute&quot;)
        return 10
    
    def __set__(self, instance, value):
    	print(&quot;Set a value of attribute&quot;)
    
class MyClass:
	
    attribute = MyDescriptor()
    
    def __init__(self):
    	self.x = 20

&amp;gt;&amp;gt;&amp;gt; MyClass.attribute
&amp;gt;&amp;gt;&amp;gt; MyClass.attribute = 30
&amp;gt;&amp;gt;&amp;gt; MyClass.attribute&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드를 한 번 봐보겠다. 그냥 단순하게 생각해서 MyClass.attribute를 처음 호출 했을 때는 __get__()으로 가서 &quot;Get a value of attribute&quot;를 출력한 뒤 10을 반환할 것이고, 30을 할당하면 아마 __set__()으로 가서 할당과 동시에 &quot;Set a value of agttribute&quot;가 출력될 것이라고 예상할 수 있다. 하지만 결과는 다르다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqc64Q/dJMcaiB8Hbh/JhKaBFjZia8mV3H20Ku3FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqc64Q/dJMcaiB8Hbh/JhKaBFjZia8mV3H20Ku3FK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqc64Q/dJMcaiB8Hbh/JhKaBFjZia8mV3H20Ku3FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqc64Q%2FdJMcaiB8Hbh%2FJhKaBFjZia8mV3H20Ku3FK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1178&quot; height=&quot;720&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결과는 처음에는 예상대로 나왔지만 30을 할당하니, attribute가 30으로 치환되어 버린다. 이는 위에서 설명한 데이터 디스크립터의 특징 때문이다. 즉 데이터 디스크립터는 객체(Instance)로만 접근이 가능한데 위의 코드는 class로 접근했으니 __set__()이 호출되지 않고 attribute에 있던 MyDescriptor()가 30으로 치환되어 버린 것이다. 그렇다면 객체 형태로 접근하면 어떻게 될까?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAnplh/dJMcacBXEWd/aAhIK7NP2taWvooYNiMWn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAnplh/dJMcacBXEWd/aAhIK7NP2taWvooYNiMWn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAnplh/dJMcacBXEWd/aAhIK7NP2taWvooYNiMWn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAnplh%2FdJMcacBXEWd%2FaAhIK7NP2taWvooYNiMWn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;426&quot; height=&quot;208&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;정상적으로 &quot;Set a value of attribute&quot;가 출력되는 것을 알 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 디스크립터의 실행 예시&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769840990840&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyDescriptor:
	
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, owner):
        return self.value
    
    def __set__(self, instance, value):
        self.value = value
    
class MyClass:
    
    attribute = MyDescriptor(20)
        
&amp;gt;&amp;gt;&amp;gt; my_class = MyClass()
&amp;gt;&amp;gt;&amp;gt; my_class.attribute
&amp;gt;&amp;gt;&amp;gt; my_class.attribute = 30
&amp;gt;&amp;gt;&amp;gt; my_class.attribute&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드를 실행하면 어떻게 나올지 이제 알 것이다. 아마 예상대로라면 &quot;20, 30&quot;이 차례차례로 출력될 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZ2KKO/dJMcagK8D0j/QzHY0CuQSbG2aBJQyNFwk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZ2KKO/dJMcagK8D0j/QzHY0CuQSbG2aBJQyNFwk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZ2KKO/dJMcagK8D0j/QzHY0CuQSbG2aBJQyNFwk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZ2KKO%2FdJMcagK8D0j%2FQzHY0CuQSbG2aBJQyNFwk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;484&quot; height=&quot;214&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예상대로 20과 30이 제대로 출력되었다. 그러면 여기서 드는 생각이 있다. 만약 __set__()이 없는 상황에서 할당을 하려고 하면 어떻게 될 것인가 하는 의문이다. 데이터 디스크립터의 경우 가장 우선순위가 높기 때문에 __set__()이나 __get__()을 할 때 가장 먼저 고려되어 속성에 접근하여 값을 가져오고 수정할 수 있다. 하지만 우선순위가 가장 낮은 비 데이터 디스크립터의 경우라면 어떨까?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769841913406&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyDescriptor:
	
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, owner):
        return self.value
    
class MyClass:
    
    attribute = MyDescriptor(20)
        
&amp;gt;&amp;gt;&amp;gt; my_class = MyClass()
&amp;gt;&amp;gt;&amp;gt; my_class.attribute&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 경우처럼 단순하게 값을 가져온다면 20이 나올 것이다. 하지만 다음과 같은 경우는 어떨까?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769842006456&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; my_class.attribute
&amp;gt;&amp;gt;&amp;gt; my_class.attribute = 30
&amp;gt;&amp;gt;&amp;gt; my_class.attribute&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉 값을 수정하는 경우에는 어떻게 나올까? 그대로 30으로 수정될까? 아니면 다른 사이드 이펙트가 있을까?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wutKq/dJMcahpJNgS/ZkINhsMGrIQVRaYvevjBE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wutKq/dJMcahpJNgS/ZkINhsMGrIQVRaYvevjBE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wutKq/dJMcahpJNgS/ZkINhsMGrIQVRaYvevjBE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwutKq%2FdJMcahpJNgS%2FZkINhsMGrIQVRaYvevjBE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;920&quot; height=&quot;446&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기존 my_class에는 아무 attribute(속성)도 들어있지 않지만, &quot;my_class.attribute = 30&quot;을 해주자, {'attribute': 30}이라는 콘솔 로그처럼 새로운 속성이 생긴 것을 알 수 있다. 만약 이 상태에서 수정 전에는 어떤 값을 가져오고 값을 넣어주는 수정 후에는 어떤 값을 가져오게 될까?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xy3Qb/dJMcac9J7Sk/82n71JVu7cXO00r47N5rl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xy3Qb/dJMcac9J7Sk/82n71JVu7cXO00r47N5rl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xy3Qb/dJMcac9J7Sk/82n71JVu7cXO00r47N5rl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxy3Qb%2FdJMcac9J7Sk%2F82n71JVu7cXO00r47N5rl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;318&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 경우처럼 처음에는 __get__()을 통해 20을 가져오지만, 그 후에는 30을 가져온다. 왜냐하면 위에서 언급한 대로 현재 디스크립터는 비 데이터 디스크립터이고 &quot;인스턴스 변수 &amp;gt; 비 데이터 디스크립터&quot;순으로 우선순위가 있기 때문에 인스턴스에 속성(변수)가 생긴 뒤로는 인스턴스 변수가 우선순위가 더 높아 변수에 있는 값인 30을 가져오는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. Django(장고) ORM, 모델에서 Field(필드)를 지정하는 경우&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이런 패턴을 장고를 자주 사용해 보신 분이라면 비슷한 패턴이라고 경험해 봤을 것이다. 위의 코드 패턴이 장고에서 모델을 정의할 때 사용하는 패턴과 유사하기 때문이다. 실제 장고 코드를 보면 어떤 말인지 감이 올 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769842636475&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.db import models

class Book(models.Model):

    title = models.CharField(max_length=255)
    published_date = models.DateField(null=True, blank=True)

    def __str__(self):
        return self.title&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 눈치 빠르신 분이라면 알아 챘겠지만, &quot;models.CharField(), models.DateField()&quot;이 둘은 디스크립터이며, 실제로도 그렇게 작동한다. 물론 위에서 설명한 부분처럼 __get__()이나 __set__()이 정확하게 구현되어 있는 것은 아니다. 실제로는 Field 클래스가 CharField class나 DateField등에 디스크립터를 심어주는 역할을 한다. 실제로 이 역할을 하는 함수가 Field 클래스에 있는 contribute_to_class 메소드이다. 이 구현을 살짝 봐보면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769843346009&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def contribute_to_class(self, cls, name, private_only=False):

	&quot;&quot;&quot;
    Register the field with the model class it belongs to.

    If private_only is True, create a separate instance of this field
    for every subclass of cls, even if cls is not an abstract model.
    &quot;&quot;&quot;
        
    self.set_attributes_from_name(name)
    self.model = cls
    cls._meta.add_field(self, private=private_only)
        
    if self.column:
        setattr(cls, self.attname, self.descriptor_class(self))
            
    if self.choices is not None:
        # Don't override a get_FOO_display() method defined explicitly on
        # this class, but don't check methods derived from inheritance, to
        # allow overriding inherited choices. For more complex inheritance
        # structures users should override contribute_to_class().
            
        if &quot;get_%s_display&quot; % self.name not in cls.__dict__:
            setattr(
                cls,
                &quot;get_%s_display&quot; % self.name,
                partialmethod(cls._get_FIELD_display, field=self),
            )&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 메소드가 모델 정의 시점에 호출되어서 Field가 모델 클래스에 자신을 등록하고 실제 작동할 디스크립터를 심어준다. 여기서 위의 파라메터 중 cls는 지금 생성되고 있는 모델 클래스로 위의 코드 예시에서는 Book이 되며 name은 변수 명으로 지정한 필드 명으로 위의 코드에서는 title, published_date가 된다. 위의 코드에서 가장 중요한 부분이 바로 디스크립터를 주입하는 부분인 &quot;cls._meta.add_field(self, private=private_only)&quot;부분과 &quot;setattr(cls, self.attname, self.descriptor_class(self))&quot; 부분이다. add_field를 통해 메타 데이터에 필드 객체를 등록하고 setattr(cls, self.attname, self.descriptor_class(self))를 통해 기존 Field객체를 descriptor_class(self) 즉 디스크립터로 교체한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;※ 참고로 이 descriptor_class는 DeferredAttribute 구현체를 받는데, 이 DeferredAttribute 클래스의 경우 장고에서 자주 언급되는 LazyLoading을 구현하는 디스크립터이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉 이런 식으로 장고는 디스크립터를 활용하여 장고의 데이터베이스 모델 정의를 활용하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 번외 __set_name__&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;디스크립터를 만들 때 디스크립터가 처리하려 하는 attribute의 이름을 알아야 하는데, 이를 간편하게 해주는 것이 __set_name__ 매직 메소드이다. &lt;span style=&quot;text-align: start;&quot;&gt;__set_name__&amp;nbsp; 메소드는 파라메터로 디스크립터를 소유한 클래스와 이름을 받으며 이를 지정해 두면 필요한 이름을 지정할 수 있다. 코드로 보면 다음과 같다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1769844072437&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyDescriptor:
	
    def __get__(self, instance, owner):
        print(&quot;Get a value of attribute&quot;)
        return 10
    
    def __set__(self, instance, value):
    	print(&quot;Set a value of attribute&quot;)
    
    def __set_name__(self, owner, name):
    	self.name = name
    
class MyClass:
	
    attribute = MyDescriptor()
    
    def __init__(self):
    	self.x = 20&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Python/ETC</category>
      <category>python</category>
      <category>공부</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>컴퓨터공학</category>
      <category>코딩</category>
      <category>코딩공부</category>
      <category>파이썬</category>
      <category>파이썬공부</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/70</guid>
      <comments>https://kimkani.tistory.com/70#entry70comment</comments>
      <pubDate>Sat, 31 Jan 2026 16:22:52 +0900</pubDate>
    </item>
    <item>
      <title>[파이썬 기본 다지기] Python의 Event Loop(이벤트 루프)의 작동 방식과 원리, 비동기 프로그래밍, 동기/비동기, 블로킹/논블로킹</title>
      <link>https://kimkani.tistory.com/69</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;R1280x0.jpeg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgL9e7/dJMcabXjCrP/fOzef4X7kNdNPWBSRqSB8k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgL9e7/dJMcabXjCrP/fOzef4X7kNdNPWBSRqSB8k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgL9e7/dJMcabXjCrP/fOzef4X7kNdNPWBSRqSB8k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgL9e7%2FdJMcabXjCrP%2FfOzef4X7kNdNPWBSRqSB8k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;807&quot; data-filename=&quot;R1280x0.jpeg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1줄 요약&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파이썬의 Event Loop(이벤트 루프)는 asyncio의 핵심이며, 비동기 작업과 콜백, 네트워크 I/O 연산 및 자식 프로세스 등을 실행한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 파이썬 그리고 동기/비동기, 블로킹/논블로킹&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;먼저 파이썬에서 asyncio를 사용하기 전 동기/비동기(sync/async)와 블로킹/논블로킹(blocking/non-blocking)에 대해서 설명하고 가는 것이 이 글에 대한 이해를 상당히 도울 수 있다고 생각한다. 그렇다면 동기와 비동기 그리고 블로킹과 논블로킹의 차이를 집고 넘어가는 것이 중요하다. 일단 큰 틀에서 &quot;동기/비동기&quot;와 &quot;블로킹/논블로킹&quot;의 차이를 짚고 넘어가려고 한다. 먼저 이 동기/비동기와 블로킹/논블로킹은 주체(Caller)가 호출한 함수(Callee)라는 것을 생각해야 한다. 즉 A가 동기적으로 B를 호출한다거나, A가 논블로킹으로 B를 호출한다거나 하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동기/비동기 - 요청한 작업의 결과의 반환 여부에 따라 순차적으로 수행할지 아닌지에 대한 관점&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;블로킹/논블로킹 - 작업의 주도권(제어권)이 누구에게 있는지 여부에 따라 다른 작업을 수행할 수 있는지에 대한 관점&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동기와 비동기는 작업의 결과가 돌아오면 그 다음 차례차례 순차적으로 할지 아니면 결과의 반환 여부에 상관 없이 병렬적으로 이를 실행하는지에 대한 것이고, 블로킹과 논블로킹은 다른 요청의 작업을 처리하기 위해 현재 작업이 대기 혹은 차단되는 것인지에 대한 여부 - 사실 더 자세하게 설명하면, 제어권을 호출자가 가지고 있는지의 여부 - 라고 생각하면 편하다. 즉 실행의 결과에 대한 것은 동기/비동기에 대한 내용이며, 실행의 주도권에 대한 것은 블로킹/논블로킹에 대한 내용이다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예시를 들어 설명해보자면, A가 B를 호출한 상태에서 B의 리턴값이 필요하면 동기, 필요하지 않고 그냥 할거면 비동기다. A가 B를 블로킹으로 호출한 경우 B가 제어권을 가지고 있으니 - 쉽게 예를 들면 옆 개발자에게 본체는 남아있는데 키보드를 넘겨준 경우 - A는 아무것도 안하고 있고, B를 논블로킹으로 호출했으면 제어권은 A가 가지고, B가 뭘 하든 - 마치 옆에 동료 개발자에게 무언가 부탁하고 자기 할 업무를 하는 것이다 - 자기 할 일을 하는 것이다. 간단하게 이들의 조합을 도표로 표현한 그림을 첨부하려고 한다. &quot;동기-블로킹, 동기-논블로킹, 비동기-블로킹, 비동기-논블로킹&quot;, 이렇게 조합이 가능하기 때문에 4가지 그림이 나온다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-27 18.37.00.png&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;1160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ca6sql/dJMcacIHNhU/ROXTYBKj7juFeiKWHvqKRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ca6sql/dJMcacIHNhU/ROXTYBKj7juFeiKWHvqKRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ca6sql/dJMcacIHNhU/ROXTYBKj7juFeiKWHvqKRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fca6sql%2FdJMcacIHNhU%2FROXTYBKj7juFeiKWHvqKRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1408&quot; height=&quot;1160&quot; data-filename=&quot;스크린샷 2026-01-27 18.37.00.png&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;1160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서는 자세하게 설명하지 않겠지만, 간단하게 4개를 설명해 보자면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동기-블로킹 : Task A가 작업을 진행하다가 Task B를 호출하면 Task B의 작업이 진행되는 동안 자신은 작업을 멈추고(블로킹/Blocking), 제어권을 돌려받을 때 까지 아무것도 하지 않는다. 그리고 제어권을 넘겨 받으면 - 그림 상의 결과값 반환(동기/sync) - 다시 Task A는 작업을 수행한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동기-논블로킹 : Task A가 작업을 진행하다가 Task B를 호출하면 Task B의 작업이 진행되어도 Task A는 자기 작업을 하지만(논블로킹/Non-Blocking) Task B의 결과가 필요하기 때문에(동기/sync) 계속해서 A는 B에게 결과를 확인한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비동기-블로킹 : Task A 가 작업을 진행하다가 Task B를 호출하면 Call Back함수를 넘기고(비동기/Async) 동시에 제어권도 넘겨 Task B의 작업이 진행되는 동안 자신은 작업을 멈추고(블로킹/Blocking) 아무것도 하지 않는다. 그리고 B가 작업이 끝나면 제어권과 같이 보낸 콜백 함수를 실행한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비동기-논블로킹 : Task A가 작업을 진행하다가 Task B를 호출하면 Task B의 작업이 진행되어도 Task A는 자기 작업을 하고(논블로킹/Non-Blocking) 제어권과 함께 콜백함수를 보냈고 결과 값도 신경쓰지 않기에(비동기/Async) B는 작업이 끝나면 A가 넘겨 준 콜백 함수를 실행한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아무튼 이정도로 설명할 수 있을 것 같다. 뭔가 중구난방이 된 것 같지만, 아무튼 추후에 설명할 파이썬의 Event Loop(이벤트 루프)와 다음 글에서 설명할 Coroutine(코루틴)에서 설명할 때 상당히 도움이 될 것 같아 이렇게 내용을 추가했다. 이제 본격적으로 이벤트 루프에 대해 이야기해 보겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 파이썬에서 Asyncio의 등장&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Python에는 GIL(Global Interpretor Lock)이 존재한다. 간략하게 설명하면 여러 스레드가 돌아가도 한 시점에 하나의 스레드가 Python 바이트 코드를 실행하고 있으면 다른 스레드는 실행하지 못하는 것이다. 이는 파이썬의 객체가 생성되고 소멸하는 시점을 관리하는 Reference Count(참조 횟수)와 관련이 있는데, 멀티 스레드 상태에서 GIL이 없는 경우에 여러 스레드가 하나의 객체에 접근하게 되면 Race Condition(경쟁 상태)에 돌입하여 참조 횟수, 카운트가 한 번만 올라야 하는데 추가로 오르거나 사용하고 있는 스레드의 참조 횟수가 다른 스레드의 접근으로 0이 되어버리는 경우가 생길 수 있다. 이를 막으려면 모든 객체에 Lock을 걸어야 하지만 이러면 비용이 너무 비싸진다. 추가적으로 서로 다른 락을 기다리다가 Dead Lock(데드락)에 빠져버릴 수도 있다. 그래서 Guido van Rossum(귀도 반 로섬)은 단순히 인터프리터 자체에 락 하나를 전체적으로 걸어버려서 한 번에 하나의 스레드만 Python 코드를 실행하게 하자는 아주 단순하고 강력한 해결책을 선택했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;물론 Asycnio등장 이전에 I/O 작업에서는 GIL이 적용되어 있는 스레드에서도 유용하다. 왜냐하면 스레드 A가 파일 입출력을 기다리고 있따면 이 A는 응답을 기다리는 동안 GIL을 해제하고 다른 스레드가 이를 활요할 수 있기 때문이다. 물론 이런 멀티 스레드가 수백 수천개가 되면 Context Switching(문맥 전환)비용 이라던지, 동기화 문제 그 외에도 다양한 공유 자원 관리 등의 복잡도가 늘기 때문에 개발 난이도가 올라가고 디버깅도 어려워진다. 이렇듯 가벼운 Cocurrency(동시성) 모델이 필요한데 멀티 스레드는 너무 무겁기에 이를 대체할 용도가 필요했다. Node.js같이 이벤트 루프 기반 비동기 처리를 위해 Python에도 Twisted나 Tornado같이 라이브러리가 있었지만 실질적으로 내장된 것은 없었다. 이런 성원에 힘입어 Python3.4부터 Asyncio가 도입되기 시작했고 지금에 이르렀다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Event Loop와 코루틴 그리고 코드&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파이썬 비동기 프로그래밍에서 다음 글에서 설명할 Coroutine(코루틴 - 일반적인 함수와 다르게 실행을 정지하거나 재개할 수 있는 특별한 객체)을 &amp;nbsp;실행시키는 방법에 크게 await, asyncio.run(), asyncio.create_task()로 구분할 수 있다. 현재 대부분의 상용 파이썬 버전에서는 asyncio.run()으로 사용하면 되지만, 이 asyncio.run()은 개략적으로 살펴보면 다음과 같이 구분할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;loop = asyncio.get_event_loop()&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;loop.run_until_complete(coroutine_A)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;loop.close()&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;asyncio.run()도 비슷하지만, asyncio.get_event_loop()를 통해 스레드에 존재하는 이벤트 루프를 가져오거나 없으면 생성하고, run_until_complete()를 통해 코루틴을 실행한 뒤, 코루틴이 완료되면 close()를 통해 이벤트 루프를 종료한다. 밑의 코드를 통해 자세하게 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-27 19.59.02.png&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;1284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3kWBa/dJMcahQLDI9/HFynHIDIdwRjPeZ6ZJEw2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3kWBa/dJMcahQLDI9/HFynHIDIdwRjPeZ6ZJEw2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3kWBa/dJMcahQLDI9/HFynHIDIdwRjPeZ6ZJEw2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3kWBa%2FdJMcahQLDI9%2FHFynHIDIdwRjPeZ6ZJEw2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1760&quot; height=&quot;1284&quot; data-filename=&quot;스크린샷 2026-01-27 19.59.02.png&quot; data-origin-width=&quot;1760&quot; data-origin-height=&quot;1284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3-1. loop = asyncio.get_event_loop()&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 스레드에 설정된 이벤트 루프를 가져오거나 새로 만들어서 가져온다. 이때 상단의 그림처럼 이벤트 루프는 무한 루프를 돌면서 Task(태스크)를 하나씩 실행시키는 Logic을 의미한다. 이때 태스크는 하나의 Coroutine에서 출발하는 실행흐름이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3-2. loop&lt;span style=&quot;text-align: left;&quot;&gt;.run_until_complete(coroutine_A)&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;인자로 받아온 코루틴을 이용해 Task 객체를 생성하고 이벤트 루프에 의해 Task의 실행이 즉시 예약된다. 이 때 코드 내에 asyncio.sleep또는 파일 입출력 혹은 네트워크와 같은 I/O와 관련된 코루틴을 await하는 코드를 마주친다. 만약 이 코루틴 B가 다른 역할 - 파일 입출력 같은 I/O 혹은 sleep이 아닌 경우 - 코루틴 B로 넘어가게 된다. 이를 코루틴 체인이 이루어진다고 보는데, 즉 B로 넘어가서 코루틴 B가 똑같은 상황을 맞이하면 다른 코루틴으로 연쇄(Chain)적으로 호출하는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;이런 코루틴 체인은 &lt;span style=&quot;text-align: start;&quot;&gt;이 때 코드 내에 asyncio.sleep또는 파일 입출력 혹은 네트워크와 같은 I/O와 관련된 코루틴을 await하는 코드를 마주치면 이제 일시 정지가 되고 이를 연쇄적으로 물고 있던 - 위의 그림 상에서는 A, B, C - 코루틴들이 완료된지 않은 Pending상태가 되며 제어권이 이벤트 루프로 돌아간다. 이 때 이벤트 루프는 실행 준비가 된 Task를 선택하거나 콜백 및 Future 객체를 완료처리하는 등의 일을 한다. 스레드 스케줄러 처럼 움직이는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;그리고 I/O가 끝나거나 sleep이 끝나 일시 정지가 해제 되고 코루틴이 재개되면 이벤트 루프가 해당 작업이 끝났음을 감지하고 중단된 지점부터 다시 실행을 재개한다. 위의 예시에서는 코루틴 C가 다시 실행되고 코루틴 C가 값을 반환하면 그 다음은 B, A 순으로 결과가 전달 &lt;span style=&quot;text-align: start;&quot;&gt;- 이 때 이를 감싸고 있떤 Task의 Future 객체(간단히 설명해 비동기 결과의 보관처)가 완료 상태가 된다 - &lt;/span&gt;되며 체인이 풀린다. &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;3-3. loop.close()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;언젠가 Task가 실행한 최초의 코루틴이 반환되며 최종적으로 실행될 코루틴이 완료되면 실행을 종료한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;4. 그렇다면 멀티 스레딩과 Asyncio, 뭐가 더 나을까?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;가장 큰 차이는 바로 멀티 스레드인가 단일 스레드인가의 차이이다. 멀티 스레딩은 여러개의 스레드를 사용하지만, Asyncio는 단일 스레드에서 구현된다. 이는 다르게 보면 여러 작업을 할 때 멀티 스레딩은 여러개의 스레드를 사용함에 따른 Context Switching(문맥 전환) 비용이 들지만, Asyncio는 그렇지 않다는 것이다. 또한 멀티 스레딩은 GIL로 인해 제약이 생기지만, Asyncio가 유리하다. 그렇다면 무조건 멀티 스레딩 보다 Asyncio가 더 좋은 것일까? 그렇지 않다. asyncio의 경우 코드가 복잡하고 Threading의 경우가 파이썬에서 더 고수준에서 간편하게 작업할 수 있다. 그래서 간단한 경우에는 스레딩을 사용하는 것이 생산성이 높을 수 있다. 여기에 더해 비동기/논블로킹이 주된 목적인 Asycnio이기에 만약 코드가 블로킹이 대부분이면 멀티스레딩이 더 편할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;5. 아니면 멀티 스레드와 같이 쓰자&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비동기 서버나 클라이언트 로직은 Asyncio 로 작성하면서, 내부에서 꼭 필요한 블로킹 코드나 무거운 CPU 작업은&amp;nbsp;&amp;nbsp;loop.run_in_executor(ThreadPoolExecutor)를 통해 스레드 풀에 넘기는 것이다. 이렇게 하면 이벤트 루프는 I/O 코루틴을 계속 돌리고, 무거운 일은 별도 워커 스레드/프로세스가 처리하게 된다. 결국 무조건 한 쪽만 써야한다는 정답은 없는 것이다. 둘 다 쓰면서 적절하게 섞어 쓰는 것이 좋은 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;다음 글에서는 이 글에서 설명된 Coroutine(코루틴)과 Task, Future 및 그 외의 객체들에 대해 이야기해 보겠다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Python/ETC</category>
      <category>Asyncio</category>
      <category>python</category>
      <category>공부</category>
      <category>자바스크립트</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>코드</category>
      <category>코딩</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/69</guid>
      <comments>https://kimkani.tistory.com/69#entry69comment</comments>
      <pubDate>Tue, 27 Jan 2026 20:33:22 +0900</pubDate>
    </item>
    <item>
      <title>CKAD 공부 노트 : YAML(YML) 문법 정리 및 사용법</title>
      <link>https://kimkani.tistory.com/68</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;234&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pWmq9/dJMcabwd3Nf/c5waAOP9hGJuTfucptFWmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pWmq9/dJMcabwd3Nf/c5waAOP9hGJuTfucptFWmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pWmq9/dJMcabwd3Nf/c5waAOP9hGJuTfucptFWmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpWmq9%2FdJMcabwd3Nf%2Fc5waAOP9hGJuTfucptFWmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;368&quot; data-origin-width=&quot;234&quot; data-origin-height=&quot;215&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3줄 요약&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML은 Sequence라는 파이썬의 리스트와 Mapping이라는 Python의 Dict와 비슷한 형식으로 파일을 구성한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML은 Indentation(들여쓰기)로 문단을 구분한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML의 주석은 &quot;#&quot;을 사용하며, 그 외에 문단 구분은 &quot;---&quot;을 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;0. 들어가며&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2023년에 CKA를 딴 뒤, 3년이 지나가는 지금 CKA의 유효기간이 만료를 향해 달려가고 있다. 그래서 생각난 김에 유효기간이 만료되기 전에 CKAD를 따보자 하는 생각으로 오랜 만에 UDEMY(유데미)에서 CKAD강의를 들으면서 YAML파일을 자주 만지게 되었다. YAML의 경우 계륵 같은 느낌이 강한 것 같다. 공부하기에는 &quot;이걸 굳이 해야하나&quot;하는 생각이 들고, 그렇다고 공부를 안하자니 막상 사용할 때 &quot;이거 문법이 살짝 틀려서 에러를 뱉네&quot;하는 상황이 많았다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 이 참에 YAML을 간단하게 정리하면서 위와 같은 애매한 상황에서 벗어나기 위한 정리 글을 작성하는 것도 도움이 될 것 같아서 한 번 이렇게 글을 작성하게 되었다. 이 글을 보시는 독자 분들께서 간단하게 정리하며 Recap을 할 수 있는 시간이 되었으면 하는 마음에서 한 번 오늘 주제인 YAML에 대해서 이야기 해보겠다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 소개&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Red Hat에서는 다음과 같이 YAML에 대해 이야기하고 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML is a human-readable data serialization language that is often used for writing configuration files. Depending on whom you ask, YAML stands for yet another markup language or YAML ain&amp;rsquo;t markup language (a recursive acronym), which emphasizes that YAML is for data, not documents.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML은 사람이 읽을 수 있는 데이터 직렬화 언어로 설정 파일을 작성할 때 자주 사용됩니다. 누군가 당신에게 YAML에 대해 질문을 할 때, 어느 부분을 강조하느냐에 따라 데이터를 강조하고 문서가 아니라고 한다면 YAML이 마크업 언어가 아니라고 할 수 있고 혹은 YAML은 또다른 마크업 언어가 될 수 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Red Hat 소개 페에지 : https://www.redhat.com/en/topics/automation/what-is-yaml&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML이 어떤 문장의 약자인지 혹은 어떤 뜻에서 유래했는지 궁금하신 분이 있을 텐데, &quot;YAML Ain't Markup Language(YAML은 마크업 언어가 아니다)&quot;의 약자이다. 즉 YAML자체가 이미 재귀적인 구조를 가지고 있는 언어유희이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML은 현재 1.2.2버전까지 나왔으며 2004년 YAML 1.0을 시작으로 &quot;Oren&amp;nbsp;Ben-Kiki&quot;, &quot;Clark&amp;nbsp;Evans&quot; 그리고 &quot;Brian&amp;nbsp;Ingerson&quot;을 통해 배포되었다. 당시 YAML을 만들 때 7가지 목표가 있었다. 이는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML documents are easily readable by humans. (YAML 문서는 사람이 쉽게 읽을 수 있어야 한다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML uses the native data structures of agile languages. (YAML은 애자일(Agile) 언어의 기본 자료 구조를 사용해야 한다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML data is portable between programming languages. (YAML의 데이터는 프로그래밍 언어와 서로 호환되어야 한다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML has a consistent model to support generic tools. (YAML은 일반적인 도구를 지원하기 위해 일관성을 가진 모델이다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML enables stream-based processing. (YAML은 스트림 기반 처리를 지원해야 한다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML is expressive and extensible. (YAML은 표현 가능하면서 확장 가능해야 한다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML is easy to implement and use. (YAML은 쉽게 실행하고 사용할 수 있어야 한다.)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아무튼 이러한 역사를 가진 YAML은 굉장히 직관적이면서도 처음 쓰는 사람들도 쉽게 접할 수 있다. 하지만 YAML만의 고유한 문법이 어느정도 존재하기 때문에 이를 짚고 넘어가야 한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. YAML 예제&lt;/span&gt;&lt;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#Comment: This is a supermarket list using YAML
#Note that - character represents the list
---
food: 
&amp;nbsp;&amp;nbsp;- vegetables: tomatoes #first list item
&amp;nbsp;&amp;nbsp;- fruits: #second list item
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;citrics: oranges 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tropical: bananas
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nuts: peanuts
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sweets: raisins&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위는 YAML의 대부분의 문법이 들어간 예제다. 여기에 YAML에 대한 문법이 다 들어가 있다고 볼 수 있다. 일단, YAML에서 기본적으로 지켜야 하는 법칙 혹은 룰같은 것이 있는데 다음과 같다. YAML은 들여쓰기(indentation)로 범위를 구분하며 각 항목은 한 줄에 하나씩 시작한다. Python(파이썬)을 생각하면 편하다. 파이썬의 들여쓰기처럼 YAML도 문단을 구분할 때 들여쓰기로 구분한다. 그렇다면 위의 예제에서 나온 요소들을 하나하나 살펴보겠다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-1. 주석과 구분선&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML에서는 주석을 &quot;#&quot;으로 사용한다. 그리고 YAML에서 &amp;ldquo;---&amp;rdquo;을 통해 문서의 문맥을 나눈다. 즉 위에서는 주석 부분과 실제 코드가 돌아가는 부분을 나눈 곳이 &quot;food: &quot; 윗 칸의&amp;nbsp;&amp;ldquo;---&amp;rdquo;이 되는 것이다. 만약 문단이 두개라면 어떻게 할까? 다음과 같이 써주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# Ranking of 1998 home runs
---
- Mark McGwire
- Sammy Sosa
- Ken Griffey

# Team ranking
---
- Chicago Cubs
- St Louis Cardinals&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2-2. 데이터의 종류&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML은 두가지 방식으로 데이터를 나눈다. 하나는 Sequence이며 하나는 Mapping이다. 쉽게 말해, 파이썬으로 비유하자면 List와 Dict가 될 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Sequence : 특정한 순서로 나열된 값들의 목록이다. Sequence는 대시와 공백(-&amp;nbsp;)으로 시작합니다. 시퀀스는 Python의 리스트(list)나 Bash 또는 Perl의 배열(array)로 생각하면 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Mapping : &amp;nbsp;키-값 쌍이다. 각 키는 반드시 고유해야 하며, 순서는 중요하지 않다. Python의 Dict나 Bash 스크립트에서의 변수 할당으로 생각하면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 Sequence, Mapping말고도 Scalar라는 것이 존재하는데, 단순하게 말해 Python에서 변수에 들어갈 값이라 생각하면 편한다. String, Integer, Date 등등이 있다. 그리고 Object도 있는데, 단순하게 생각해서 Key-Value가 갖춰진 파이썬의 Dict이라고 봐도 괜찮다. &amp;nbsp;아무튼 위의 Sequence와 Mapping을 조합하여 다양한 형식의 YAML 파일을 구성할 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. Mapping 블록&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;YAML 파일을 매핑으로 시작하면, YAML은 매핑들이 계속 나올 것처럼 기대한다. YAML에서 매핑은 끝나고 새로운 매핑 블록이 명시적으로 생성되기 전까지는 닫히지 않는다. 새 블록은 두 가지 경우에만 만들 수 있다. 첫째, 들여쓰기 수준을 더 깊게 늘리는 경우(이때 새 블록은 이전 블록 내부에 존재합니다). 둘째, 이전 매핑을 끝낸 뒤 이와 나란히 위치하는 새 매핑 블록을 시작하는 경우이다. 한 번 예시를 봐보자.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;---
Store: Bakery
&amp;nbsp;&amp;nbsp;- Sourdough loaf
&amp;nbsp;&amp;nbsp;- Bagels&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 단순한 Mapping으로만 이루어진 YAML파일이 있다. 위 파일의 문제는 Mapping 블록을 열었지만 닫지 않았다는 것이다. 위의 경우에서 Bakery로 Key-Value를 지정해주고 그 다음에 단순히 Sequence를 넣어주었다. 근데 이러면 YAML은 단순히 인식하기에 Mapping이 끝나지 않고 갑자기 튀어나온 Sequence에 당황하게 되는 것이다. 그렇다면 이 Mapping 블록을 닫고서 새로운 형식을 넣으려면 어떻게 해야할까? 단순하다. 새로운 Mapping 블록을 넣어야 한다. 이 때 Mapping의 Key-Value에서 Value는 Sequence가 될 수 있지만 무조건 Key가 존재해야 한다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;---
Store:
&amp;nbsp;&amp;nbsp;Bakery:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &amp;lsquo;Sourdough loaf&amp;rsquo;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &amp;lsquo;Bagels&amp;rsquo;
&amp;nbsp;&amp;nbsp;Cheesemonger:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &amp;lsquo;Blue cheese&amp;rsquo;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &amp;lsquo;Feta&amp;rsquo;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 YAML파일을 고치면 위의 코드처럼 된다. 이를 JSON으로 바꾸면 &quot;{&amp;ldquo;Store&amp;rdquo;: {&amp;ldquo;Bakery&amp;rdquo;: [&amp;ldquo;Sourdough loaf&amp;rdquo;, &amp;ldquo;Bagels&amp;rdquo;], &amp;ldquo;Cheesemonger&amp;rdquo;: [&amp;ldquo;Blue cheese&amp;rdquo;, &amp;ldquo;Feta&amp;rdquo;]}}&quot;와 같이 표현할 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. Sequence 블록&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;앞에서 말했듯이 Sequence는 파이썬의 List로 생각하면 편하다. 그렇다면 [&amp;ldquo;Flour&amp;rdquo;, &amp;ldquo;Water&amp;rdquo;, &amp;ldquo;Salt&amp;rdquo;]와 같은 List를 YAML로 표시하면 어떻게 될까? 간단하다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;---
- Flour
- Water
- Salt&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위와 같이 대쉬(-)를 사용한 뒤 한칸 뛰어주고 값을 적어주면 된다. 물론 List처럼 콤마(,)로 구분하는 것이 아닌 한 줄에 하나씩 대시(-)로 구분해 줘야 한다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. Mapping과 Sequence를 혼합해서 쓰기&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#Comment: This is a supermarket list using YAML
#Note that - character represents the list
---
food: 
&amp;nbsp;&amp;nbsp;- vegetables: tomatoes #first list item
&amp;nbsp;&amp;nbsp;- fruits: #second list item
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;citrics: oranges 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tropical: bananas
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nuts: peanuts
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sweets: raisins&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇다면 위의 예제를 다시 살펴보겠다. 위의 예제는 Mapping과 Sequence를 혼합해서 사용한 좋은 예시이다. 그렇다면 이를 파이썬으로 표현하면 어떻게 될까? 혹은 TypedDict처럼 Type Annotation을 해주면 어떻게 될까? 아마 다음과 같이 될 것이다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Dict[ Key: Value ], Dict[ Key : List[ Dict[ Key : Value ]]]&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 타입을 간단하게 표현해 줄 수 있을 것이다. 물론 역시나 주의해야할 점은 YAML에서 &amp;nbsp;Indentation을 통해 이 단락안에 포함되어 있다는 것을 명시해 줘야 한다는 것이다. 위의 YAML을 JSON으로 바꾸면 다음과 같을 것이다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;{&quot;food&quot;: [&quot;vegetables&quot;: &quot;tomatoes&quot;, &quot;fruits&quot; : [{&quot;citrics&quot;: &quot;oranges&quot;, &quot;tropical&quot;: &quot;bananas&quot;, &quot;nuts&quot;: &quot;peanuts&quot;, &quot;sweets&quot;: &quot;raisins&quot;}]]&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;크게 어려운 부분은 없다. - vegetables: tomatoes는 List의 첫번째 값으로 쓰이면서 Dict 형태라고 생각하면 되며, - fruits: 이후의 부분은 fruits를 Key값으로 가지고 4개의 Key-Value를 가진 Dict이 List에 하나 들어 있다고 생각하면 된다. 다른 예시를 한 번 봐보겠다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;list:
&amp;nbsp;&amp;nbsp;- color: red
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;direction: left
&amp;nbsp;&amp;nbsp;- color: blue
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;datetime: 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time: '11:14:11'
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;date: '2012-04-11'&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 필드를 파이썬으로 대입해서 생각해보면 list라는 변수가 있고 거기 안에는 {&quot;color&quot;: &quot;red&quot;, &quot;direction&quot;: &quot;left&quot;}, {&quot;color&quot;: &quot;blue&quot;, &quot;datetime&quot; : {&quot;time&quot;: &quot;11:14:11&quot;, &quot;date&quot;: &quot;2012-04-11&quot;}}이 들어있는 것이다. 최종적으로 보면 List에는 두 개의 요소(element)가 있으며 그 element는 중첩된 파이썬 Dict로 구성되어 있다고 생각하면 편하다. 그리고 이 값을 담는 변수는 list가 되는 것이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아무튼 이렇게 간단하게 Python 문법에 비유해서 YAML파일을 읽는 법과 이를 사용하는 방버을 알아보았다. 이 글이 많은 사람들에게 도움이 되기를 바란다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Infra/CKAD</category>
      <category>ckad</category>
      <category>CS</category>
      <category>IT</category>
      <category>python</category>
      <category>YAML</category>
      <category>백엔드</category>
      <category>인프라</category>
      <category>컴퓨터공학</category>
      <category>파이썬</category>
      <category>프론트엔드</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/68</guid>
      <comments>https://kimkani.tistory.com/68#entry68comment</comments>
      <pubDate>Sun, 25 Jan 2026 13:29:57 +0900</pubDate>
    </item>
    <item>
      <title>[Django] QuerySet, Manager, Active Record 패턴, django-model-utils의 InheritanceManager들 톺아보기 및 성능 개선기</title>
      <link>https://kimkani.tistory.com/67</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTZVM3/btsKcZPL4UJ/nSAgm7gj1jdmgiPJ9qaFmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTZVM3/btsKcZPL4UJ/nSAgm7gj1jdmgiPJ9qaFmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTZVM3/btsKcZPL4UJ/nSAgm7gj1jdmgiPJ9qaFmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTZVM3%2FbtsKcZPL4UJ%2FnSAgm7gj1jdmgiPJ9qaFmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;480&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장고의 ORM을 사용하다 보면 다양한 기능이 있지만, 가끔씩 답답해서 무언가 안타까운 경우도 있다. 개인적으로 이번에 업무를 진행하면서 우연히 알게 된 django-model-utils의 Manager를 알게되었고, 그와 동시에 이러한 내용을 정리해 두면 좋을 것 같아서 이번에 한 번 글을 쓰게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 장고 Models, Fields, Manager, QuerySet 그리고 Backend에 대해 알아보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;전체적인 개요 / General Overview&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wBT25/btsKc26KPkj/DTsdk3OtxrViVj6jr3XQbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wBT25/btsKc26KPkj/DTsdk3OtxrViVj6jr3XQbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wBT25/btsKc26KPkj/DTsdk3OtxrViVj6jr3XQbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwBT25%2FbtsKc26KPkj%2FDTsdk3OtxrViVj6jr3XQbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1670&quot; height=&quot;654&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. Active Record Pattern&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;361&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUhKbS/btsKb3rXWBa/A0VCUV0FQIYaFSvkPeivB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUhKbS/btsKb3rXWBa/A0VCUV0FQIYaFSvkPeivB0/img.png&quot; data-alt=&quot;https://docs.djangoproject.com/ko/5.0/misc/design-philosophies/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUhKbS/btsKb3rXWBa/A0VCUV0FQIYaFSvkPeivB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUhKbS%2FbtsKb3rXWBa%2FA0VCUV0FQIYaFSvkPeivB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;929&quot; height=&quot;361&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;361&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.djangoproject.com/ko/5.0/misc/design-philosophies/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Active Record란?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Software Architecture중의 한 가지 패턴&amp;rarr; RDBMS를 그대로 반영하는 패턴 (비즈니스 로직과 데이터의 영속성을 하나의 객체에 담아 일체감 있게 통합하는 것)&lt;/li&gt;
&lt;li&gt;즉, 하나의 객체, 혹은 데이터, 모델이 자신의 영속성까지 책임(혹은 인지)을 겪는 상황이라고 볼 수 있다.&lt;/li&gt;
&lt;li&gt;대표적으로 Ruby on Rails가 이에 해당하고, Django의 경우 공식 문서에서 Active Record의 철학을 따른다고만 하고 있다.&lt;/li&gt;
&lt;li&gt;프로그램 속 각각의 객체가 데이터베이스 테이블의 row에 해당한다는 것.&lt;/li&gt;
&lt;li&gt;그 객체의 속성들은 각각 column에 해당한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ORM(Object-Relational Mapping)이란?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램 속 각각의 객체를 데이터베이스에 매핑한 것.&lt;/li&gt;
&lt;li&gt;SQL과 데이터 베이스의 사소한 디자인에서 캡슐화(혹은 추상화)해서 데이터베이스의 데이터를 마치 객체처럼 사용하는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Active Record Pattern의 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간단하다. 객체 하나에 모든 CRUD를 달성할 수 있게 해준다.&lt;/li&gt;
&lt;li&gt;가볍게 쓰기 좋다. 즉, 단순하다. 이는 곧 속도로 이어지며, MVP 모델 혹은 급하게 무언가 만들어야 하는 경우에 빛을 발한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Active Record Pattern의&lt;span&gt;&amp;nbsp;&lt;/span&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하지만 하나의 클래스 혹은 객체가 모든 데이터베이스와 관련된 로직을 가지고 있다. 심지어 검증 로직 까지.&lt;/li&gt;
&lt;li&gt;특정 영속성 메커니즘과 강력하게 묶인다. 강하게 결합(tight coupling)된다는 단점이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 하나를 바꾸면 다른 곳에 영향이 가 다 바꿔야 한다는 소리이다.&lt;/li&gt;
&lt;li&gt;RPY(Repeat Your Self, 공통 로직 반복)를 요구하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Models과 Fields에 대해서&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Models의 경우, 위에서 말한 Active Record Pattern을 실행하는 대표적인 패턴. 이 때 객체가 데이터 베이스의 row를 감싼다. 이 때 객체는 데이터와 행동(CRUD)을 모두 들고 있으며 Model Manager를 통해 데이터베이스에 지속된다. ORM설계에서 모델은 Fields에 정의된 쿼리 결과를 파이썬 객체로 연결(map)시키는 메타데이터를 포함한다. QuerySet은 이 map을 이용해서 작업을 수행하고 데이터가 객체의 속성에 완벽히 매핑된 모델의 인스턴스를 반환한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 즉, 모델은 데이터베이스의 데이터를 어떻게 변환시킬 지에 대한 규칙을 가지고 있고, 그리고 그 규칙을 기반으로 변환시켜주는 것은 QuerySet이 수행한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 이 때, Fields는 그 자체로 의미가 없다. 단순히 어떤 타입에 대해 어떻게 규칙을 실행할지에 대해 명시를 해두는 곳이라 생각하면 편한다. (FK의 경우에는 어떻게 연결할지 등의 의미)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;2. Manager에 대해서&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyp8gJ/btsKdeTyilR/LuIwgwQFkfUAEJBmYdfmS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyp8gJ/btsKdeTyilR/LuIwgwQFkfUAEJBmYdfmS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyp8gJ/btsKdeTyilR/LuIwgwQFkfUAEJBmYdfmS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcyp8gJ%2FbtsKdeTyilR%2FLuIwgwQFkfUAEJBmYdfmS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;563&quot; height=&quot;211&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;211&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Django Docs&lt;/b&gt;에서 설명에 따르면 매니저는 장고 모델에 데이터베이스 쿼리 작업을 제공하는 인터페이스이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이는 Table Data Gateway처럼 작동한다.&lt;/li&gt;
&lt;li&gt;여기서 Table Data Gateway란, 객체가 마치 데이터베이스 테이블과 연결된 게이트웨이처럼 작동하는 것을 의미.&lt;/li&gt;
&lt;li&gt;즉, 실제 데이터를 가져오는 것과, 그 데이터를 사용하는 것을 분리하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;기본적으로 &amp;lsquo;objects&amp;rsquo;도 매니저이고, 이는 모델 클래스에 자동 추가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;* django.db.models.manager&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;기본적으로 4개의 클래스가 정의되어 있다. (BaseManager, Manager, ManagerDescriptor, EmptyManager)&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1729417421756&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BaseManager:
    # To retain order, track each time a Manager instance is created.
    creation_counter = 0

    # Set to True for the 'objects' managers that are automatically created.
    auto_created = False

    #: If set to True the manager will be serialized into migrations and will
    #: thus be available in e.g. RunPython operations.
    use_in_migrations = False
		...
		
--------

class Manager(BaseManager.from_queryset(QuerySet)):
    pass

--------

class ManagerDescriptor:
    def __init__(self, manager):
        self.manager = manager
    ...

--------

class EmptyManager(Manager):
    def __init__(self, model):
        super().__init__()
        self.model = model

    def get_queryset(self):
        return super().get_queryset().none()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;* BaseManager&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 BaseManager를 상속한 매니저에서 반환하는 쿼리셋에 자신이 원하는 쿼리 메서드를 추가할 수 있다.&lt;/li&gt;
&lt;li&gt;다음과 같은 코드를 작성할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1729417479966&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role=&quot;A&quot;)

    def editors(self):
        return self.filter(role=&quot;E&quot;)


class PersonManager(models.Manager):
    def get_queryset(self):
        return PersonQuerySet(self.model, using=self._db)

    def authors(self):
        return self.get_queryset().authors()

    def editors(self):
        return self.get_queryset().editors()


class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(
        max_length=1, choices=[(&quot;A&quot;, _(&quot;Author&quot;)), (&quot;E&quot;, _(&quot;Editor&quot;))]
    )
    people = PersonManager()


authors = Person.people.authors()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;* BaseManager&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디폴트 값으로, &amp;lsquo;objects&amp;rsquo;로 사용되는 클래스.&lt;/li&gt;
&lt;li&gt;BaseManager.from_queryset(QuerySet)의 반환 값을 상속받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1729417513582&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def from_queryset(cls, queryset_class, class_name=None):
	if class_name is None:
		class_name = &quot;%sFrom%s&quot; % (cls.__name__, queryset_class.__name__)
	return type(
		class_name,
        (cls,),
        {
			&quot;_queryset_class&quot;: queryset_class,
			**cls._get_queryset_methods(queryset_class),
		},
	)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. QuerySet에 대해서&lt;/b&gt;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; font-size: 16px; letter-spacing: 0px;&quot;&gt;Queryset은 파이썬 객체를 이용해 쿼리를 만들 수 있게 해준다.&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그리고 각기 다른 Backend를 사용하여 이러한 Query들을 Raw SQL 쿼리로 변경 시켜주는 작업을 한다.&lt;/li&gt;
&lt;li&gt;Lazy Load 패턴을 사용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Lazy Load를 간단하게 설명하면 실제 쿼리(SQL Query)는 서버 혹은 다양한 애플리케이션이 실제로 결과를 원할 때에 실행되는 것이다.&lt;/li&gt;
&lt;li&gt;즉, 데이터베이스로부터 가져온 데이터의 집합이라고 생각해도 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;rarr; &lt;b&gt;그렇다면 Manager와 QuerySet의 관계는 무엇인가?&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Manager는 우리가 만든&lt;span&gt;&amp;nbsp;&lt;/span&gt;Model&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 위한 데이터베이스의 실행방식을 정의한 인터페이스라고 보면 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이를 위해 장고는 기본적으로, 만약 커스텀 매니저를 정의하고 있지 않다면, objects를 속성을 가지고 있다.&lt;/li&gt;
&lt;li&gt;그리고 이 Manager는 같은 모델에서 여러개가 정의될 수도 있다.&lt;/li&gt;
&lt;li&gt;이 커스텀 매니저는 무조건 QuerySet에 국한되어 있지 않다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;코드 예시&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1729417647752&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from datetime import date, timedelta

from django.db import models
from django.db.models.functions import Concat


class PersonManager(models.Manager):
    def with_extra_fields(self):
        return self.annotate(
            full_name=Concat(&quot;first_name&quot;, models.Value(&quot; &quot;), &quot;last_name&quot;),
        )

    def experienced(self):
        return self.filter(
            join_date__lt=date.today() - timedelta(days=1000)
        )


class PersonAnalyticsManager(models.Manager):
    def number_of_unique_names(self):
        return self.aggregate(
            count=models.Count(&quot;last_name&quot;, distinct=True),
        )[&quot;count&quot;]


class Person(models.Model):
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)
    join_date = models.DateField(default=date.today)

    # 기본이 되는 매니저
    objects = PersonManager()
    # 추가적으로 정의한 매니저
    analytics = PersonAnalyticsManager()

    def __str__(self):
        return f&quot;{self.first_name} {self.last_name}&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;위의 코드를 실행하면 다음과 같이 된다.&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1729417670745&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from .models import Person

&amp;gt;&amp;gt;&amp;gt; Person.objects.with_extra_fields().order_by(&quot;full_name&quot;)
&amp;lt;QuerySet [&amp;lt;Person: Catherine Smith&amp;gt;, &amp;lt;Person: Joe Doe&amp;gt;, &amp;lt;Person: Omega Smith&amp;gt;]&amp;gt;

&amp;gt;&amp;gt;&amp;gt; Person.objects.experienced()
&amp;lt;PersonQuerySet [&amp;lt;Person: Joe Doe&amp;gt;]&amp;gt;

&amp;gt;&amp;gt;&amp;gt; Person.analytics.number_of_unique_names()
2&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;주의 할 점이 있다면, 매니저 끼리는 Chaning, 즉, 서로가 서로에게 무관한 존재이다.&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1729417697349&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Person.objects.experienced().with_extra_fields()
...
AttributeError: 'QuerySet' object has no attribute 'with_extra_fields'&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;만약 이러한 두 매니저를 같이 쓰고 싶다면 as_manager()를 쓸 때가 온 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1729417719736&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from datetime import date, timedelta

from django.db import models
from django.db.models.functions import Concat


class PersonQuerySet(models.QuerySet):  # 커스텀 쿼리셋으로 변경
    def with_extra_fields(self):
        return self.annotate(
            full_name=Concat(&quot;first_name&quot;, models.Value(&quot; &quot;), &quot;last_name&quot;),
        )

    def experienced(self):
        return self.filter(
            join_date__lt=date.today() - timedelta(days=1000)
        )

    def number_of_unique_names(self):
        return self.aggregate(
            count=models.Count(&quot;last_name&quot;, distinct=True),
        )[&quot;count&quot;]


class Person(models.Model):
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)
    join_date = models.DateField(default=date.today)
    # 매니저로 쓰인 쿼리셋
    objects = PersonQuerySet.as_manager()

    def __str__(self):
        return f&quot;{self.first_name} {self.last_name}&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1729417775933&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; Person.objects.experienced()
&amp;lt;PersonQuerySet [&amp;lt;Person: Joe Doe&amp;gt;]&amp;gt;

&amp;gt;&amp;gt;&amp;gt; Person.objects.experienced().with_extra_fields().get().full_name
'Joe Doe'

&amp;gt;&amp;gt;&amp;gt; Person.objects.experienced().number_of_unique_names()
1

&amp;gt;&amp;gt;&amp;gt; Person.objects.number_of_unique_names()
2&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. django-model-utils의 Manager중 InheritanceManager 알아보기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 기준으로 Django Model Utils에는 총 5개의 Model Manager가 존재한다. InheritanceManager, JoinQueryset, QueryManager, SoftDeletableManager 그리고 Mixins이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4-1. InheritanceManager&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 다음과 같은 Model을 설계했다고 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1729418082892&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    school_name = models.CharField(max_length=50)
    major_name = models.CharField(max_length=50)
    
class PhDStudent(Student):
    professor_name = models.CharField(max_length=50)
    paper_name = models.CharField(max_length=50)
	
class UndergraduateStudent(Student):
    pass&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 모델을 돌려 마이그레이션을 하면 정확히 3개의 테이블이 생길 것이다. 여기서 Student Table은 PhDStudent와 UndergraduateStudent가 상속하고 있으며, 이를 통해 Student Table은 포괄적인 테이블이 될 것이다. 만약 이를 django shell을 통해 적용해 보면 다음과 같은 행동을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729418231423&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; s = Student( )
&amp;gt;&amp;gt;&amp;gt; s.first_name = 'Kim'&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;gt;&amp;gt;&amp;gt; s.second_name = 'Kani'&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;gt;&amp;gt;&amp;gt; s.major_name = 'CS'
&amp;gt;&amp;gt;&amp;gt; s.school_name = 'SKKU'
&amp;gt;&amp;gt;&amp;gt; s.paper_name = &quot;Attention Is All You Need&quot; 
# 에러가 난다.Student에는 없고 PhDStudent에만 있기 때문이다.
&amp;gt;&amp;gt;&amp;gt; s.save()&amp;nbsp; &amp;nbsp;
# 위의 에러를 제외하면 정상적으로 잘 저장된다.

&amp;gt;&amp;gt;&amp;gt; p = PhDStudent()
&amp;gt;&amp;gt;&amp;gt; p.paper_name = &quot;Attention Is All You Need&quot; 
&amp;gt;&amp;gt;&amp;gt; p.school_name = 'SKKU'&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 부모의 필드까지 모두 가지고 있기 때문에 정상적으로 작동한다
(...생략...)
&amp;gt;&amp;gt;&amp;gt; p.save()&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제 DB에는 어떻게 저장되어 있을까? 위의 코드를 쉘에서 실행하면 Student에는 두개의 데이터가 그리고 PhDStudnet에는 하나의 데이터가 적재된다. 그렇다면 부모에서 자식 모델에게 접근이 될까? 당연히 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1729418655481&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; s = Student.objects.get(paper_name=&quot;Attention Is All You Need&quot;)
&amp;gt;&amp;gt;&amp;gt; s.phdstudnet # 이 때 s는 PhDStudent클래스라 보면 된다.
&amp;gt;&amp;gt;&amp;gt; s.phdstudnet.paper_name&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;근데 여기서 사소한 불편함이 있다. Student를 상속하는 테이블 및 클래스가 많아져서 일일이 하나하나 내가 원하는 Class인지 검증하면서 찾아야 하는 불편함이 있다. 즉 일일이 하나하나 클래스의 attribute를 찾으면서 검증해야한다. 이런 부분을 해결하기 위해 있는 것이 바로 django-model-utils의 InheritanceManager이다.&lt;/p&gt;
&lt;pre id=&quot;code_1729418791864&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from model_utils.managers import InheritanceManager

class Student(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    school_name = models.CharField(max_length=50)
    major_name = models.CharField(max_length=50)
    
    objects = InheritanceManager()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 objects에다 InheritacneManager()를 해주면 된다. 만약 이를 실제 코드에 적용시키면 다음과 같이 활용이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1729418992336&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;specific_students = Student.objects.filter(name='kim').select_subclasses()
for student in specific_students:
    # &quot;student&quot; 는 자동으로 PhDStudent, UndergraduateStudent, Student중 하나의 인스턴스로 바뀐다.
    
Student.objects.filter(name='kim').instance_of(PhDStudent, UndergraduateStudent)
#위의 경우처럼 특정 인스턴스를 지정할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 내가 겪은 상황&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;기존 코드의 문제점 및 상황은 다음과 같았다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;model_one.model_foreign_set.order_by(&quot;-created_at&quot;)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 통해 해당 모델(이 사례에서는 model_one)의 모든 model_foreign_set의 인스턴스 결과를 조회하는 것이 문제였다.&lt;/li&gt;
&lt;li&gt;그리고 해당 model_foreign을 전부 순회하면서 일일히 회사에서 커스터마이징한 타입 캐스팅을 일일이 순회하는 것이 성능 하락을 원인이 되었다는 것을 추정했다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;여기서 커스텀 타입 캐스팅 함수에 들어갈 때도 고정된 n값의 순회가 한 번 더 일어남&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대략 Big O(n*m)~O(n^2) (n &amp;ge; m) 정도의 속도가 나타날 수 있었다.&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;특히 instance를 확인할 때 사용하는 getattr의 경우 소모하는 비용이 큰 것을 확인을 했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 나의 경우 애초에 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;InheritanceManager를 사용하여 커스텀 타입 캐스팅을 사용하는 상황을 없애고자 했다. 생각보다 이 작업은 순조롭게 진행되어서 1초에서 최대 7초까지 걸리던 프로덕션 응답 속도를 40~60%까지 응답속도를 줄이는 것이 가능해 졌다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;참고 자료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@shiiyan/active-record-pattern-vs-repository-pattern-making-the-right-choice-f36d8deece94&quot;&gt;https://medium.com/@shiiyan/active-record-pattern-vs-repository-pattern-making-the-right-choice-f36d8deece94&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://softwareengineering.stackexchange.com/questions/70291/what-are-the-drawbacks-to-the-activerecord-pattern&quot;&gt;https://softwareengineering.stackexchange.com/questions/70291/what-are-the-drawbacks-to-the-activerecord-pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@benjaminchamwb/object-relational-mapping-orm-vs-active-record-navigating-database-interaction-patterns-fe5c45a2aaa3&quot;&gt;https://medium.com/@benjaminchamwb/object-relational-mapping-orm-vs-active-record-navigating-database-interaction-patterns-fe5c45a2aaa3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1404405/what-is-the-purpose-of-active-records&quot;&gt;https://stackoverflow.com/questions/1404405/what-is-the-purpose-of-active-records&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jairvercosa.medium.com/manger-vs-query-sets-in-django-e9af7ed744e0&quot;&gt;https://jairvercosa.medium.com/manger-vs-query-sets-in-django-e9af7ed744e0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/29798125/when-should-i-use-a-custom-manager-versus-a-custom-queryset-in-django&quot;&gt;https://stackoverflow.com/questions/29798125/when-should-i-use-a-custom-manager-versus-a-custom-queryset-in-django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/bunkerkids/django-model-manager-8b5d8b3b539b&quot;&gt;https://medium.com/bunkerkids/django-model-manager-8b5d8b3b539b&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fly.io/django-beats/organizing-database-queries-managers-vs-querysets/&quot;&gt;https://fly.io/django-beats/organizing-database-queries-managers-vs-querysets/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Python/Django</category>
      <category>CS</category>
      <category>Django</category>
      <category>sql</category>
      <category>백엔드</category>
      <category>서버</category>
      <category>성능</category>
      <category>장고</category>
      <category>컴퓨터공학</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/67</guid>
      <comments>https://kimkani.tistory.com/67#entry67comment</comments>
      <pubDate>Sun, 20 Oct 2024 19:17:30 +0900</pubDate>
    </item>
    <item>
      <title>[SQLAlchemy] - Asynchronous I/O (asyncio) : 비동기 I/O (asyncio) 공식 문서 번역 1편 : 개요, 동시성 태스크와 같이 사용하기</title>
      <link>https://kimkani.tistory.com/66</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AB5GT/btsITPHW9O7/kJLHi8RbRHRBmbRmrWDAZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AB5GT/btsITPHW9O7/kJLHi8RbRHRBmbRmrWDAZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AB5GT/btsITPHW9O7/kJLHi8RbRHRBmbRmrWDAZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAB5GT%2FbtsITPHW9O7%2FkJLHi8RbRHRBmbRmrWDAZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;900&quot; height=&quot;472&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;본 문서는 SQLAlchemy의 공식 문서를 번역한 것입니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;버전은 2.0.x 를 기준으로 합니다.&lt;/li&gt;
&lt;li&gt;매주 일요일마다 올릴 예정이며, 번역에 오류가 있을 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;figure id=&quot;og_1722745229775&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Asynchronous I/O (asyncio)
 &amp;mdash;
    SQLAlchemy 2.0 Documentation&quot; data-og-description=&quot;&amp;gt;&amp;gt;&amp;gt; from __future__ import annotations &amp;gt;&amp;gt;&amp;gt; import asyncio &amp;gt;&amp;gt;&amp;gt; import datetime &amp;gt;&amp;gt;&amp;gt; from typing import List &amp;gt;&amp;gt;&amp;gt; from sqlalchemy import ForeignKey &amp;gt;&amp;gt;&amp;gt; from sqlalchemy import func &amp;gt;&amp;gt;&amp;gt; from sqlalchemy import select &amp;gt;&amp;gt;&amp;gt; from sqlalchemy.ext.asyncio import AsyncAt&quot; data-og-host=&quot;docs.sqlalchemy.org&quot; data-og-source-url=&quot;https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html&quot; data-og-url=&quot;https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Asynchronous I/O (asyncio) &amp;mdash; SQLAlchemy 2.0 Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;gt;&amp;gt;&amp;gt; from __future__ import annotations &amp;gt;&amp;gt;&amp;gt; import asyncio &amp;gt;&amp;gt;&amp;gt; import datetime &amp;gt;&amp;gt;&amp;gt; from typing import List &amp;gt;&amp;gt;&amp;gt; from sqlalchemy import ForeignKey &amp;gt;&amp;gt;&amp;gt; from sqlalchemy import func &amp;gt;&amp;gt;&amp;gt; from sqlalchemy import select &amp;gt;&amp;gt;&amp;gt; from sqlalchemy.ext.asyncio import AsyncAt&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.sqlalchemy.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요 - Core&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심적인 사용을 위해&lt;b&gt; &lt;i&gt;create_async_engine()&lt;/i&gt;&lt;/b&gt; 함수는 기존의 Engine API의 async(비동기) 버전에 해당하는 &lt;b&gt;&lt;i&gt;AsyncEngine&lt;/i&gt;&lt;/b&gt; 인스턴스를 만들고 제공합니다. 이 &lt;b&gt;&lt;i&gt;AsyncEngine&lt;/i&gt;&lt;/b&gt;은 &lt;b&gt;&lt;i&gt;AsyncEngine.connect()&lt;/i&gt;&lt;/b&gt;와 &lt;b&gt;&lt;i&gt;AsyncEngine.begin()&lt;/i&gt;&lt;/b&gt; 메소드를 통해 &lt;b&gt;&lt;i&gt;AsyncConnection&lt;/i&gt;&lt;/b&gt;을 전달합니다. 이 두 메소드 모두 비동기 컨텍스트 관리자를 제공합니다. 이 &lt;b&gt;&lt;i&gt;AsyncConnection&lt;/i&gt;&lt;/b&gt;은 &lt;i&gt;&lt;b&gt;AsyncConnection.stream()&lt;/b&gt;&lt;/i&gt;을 활용해 스트리밍중인 서버 측의 &lt;i&gt;&lt;b&gt;AsyncResult&lt;/b&gt;&lt;/i&gt;를 전달하거나, &lt;i&gt;&lt;b&gt;AsyncConnection.execute()&lt;/b&gt;&lt;/i&gt; 메소드를 이용하여 저장되어(buffered) 있는&lt;i&gt;&lt;b&gt; Result&lt;/b&gt;&lt;/i&gt;를 전달하여 실행문을 호출할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722739915697&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import asyncio

from sqlalchemy import Column
from sqlalchemy import MetaData
from sqlalchemy import select
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy.ext.asyncio import create_async_engine

meta = MetaData()
t1 = Table(&quot;t1&quot;, meta, Column(&quot;name&quot;, String(50), primary_key=True))


async def async_main() -&amp;gt; None:
    engine = create_async_engine(&quot;sqlite+aiosqlite://&quot;, echo=True)
    async with engine.begin() as conn:
        await conn.run_sync(meta.drop_all)
        await conn.run_sync(meta.create_all)
        await conn.execute(
            t1.insert(), [{&quot;name&quot;: &quot;some name 1&quot;}, {&quot;name&quot;: &quot;some name 2&quot;}]
        )
    async with engine.connect() as conn:
    	# Result를 선택, 저장된 상태로 전달되는
        # 결과들
        result = await conn.execute(select(t1).where(t1.c.name == &quot;some name 1&quot;))
        print(result.fetchall())
    # 함수 범위에서 생성된 AsyncEngine을 위해, 닫고서
    # 풀된(pooled) 연결들을 청소한다
    await engine.dispose()


asyncio.run(async_main())&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722739938068&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BEGIN (implicit)
...
CREATE TABLE t1 (
    name VARCHAR(50) NOT NULL,
    PRIMARY KEY (name)
)
...
INSERT INTO t1 (name) VALUES (?)
[...] [('some name 1',), ('some name 2',)]
COMMIT
BEGIN (implicit)
SELECT t1.name
FROM t1
WHERE t1.name = ?
[...] ('some name 1',)
[('some name 1',)]
ROLLBACK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 사용된 &lt;i&gt;&lt;b&gt;AsyncConnection.run_sync()&lt;/b&gt;&lt;/i&gt; 메소드는 특별한 DDL(Data Definition Language, 데이터 정의 언어), awaitable(*await 키워드를 사용 가능한 객체)를 포함하지 않는 &lt;i&gt;&lt;b&gt;MetaData.create_all()&lt;/b&gt;&lt;/i&gt;같은 것들을 호출하는데 주로 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;TIP&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;위의 예제 코드에서&lt;b&gt;&lt;i&gt; async_main&lt;/i&gt;&lt;/b&gt; 함수를 사용한 것처럼, &lt;i&gt;&lt;b&gt;AsyncEngine&lt;/b&gt;&lt;/i&gt; 객체를 사용 후 범위에서 컨텍스트를 벗어나거나 Garbage Collect가 될 때는, &lt;i&gt;&lt;b&gt;AsyncEngine.dispose()&lt;/b&gt;&lt;/i&gt; 메소드를 await와 함께 사용하는 것을 권합니다. 이는 연결 풀(connection pool)내에서 열린 연결이 awaitable 컨텍스트 내에서 올바르게 폐기(disposse)되도록 보장합니다. 블로킹 IO를 사용할 때와는 다르게, SQLAlchemy은 &lt;i&gt;&lt;b&gt;__del__&lt;/b&gt;&lt;/i&gt;이나 &lt;i&gt;&lt;b&gt;약한 참조 파이널라이저(weakref finalizers)&lt;/b&gt;&lt;/i&gt; 메소드에서 await를 호출할 기회가 없기에, 적절하게 이런 연결들을 폐기할 수 없습니다. 엔진이 범위를 벗어날 때 명시적으로 폐기하지 않으면 가비지 컬렉션 중에 &lt;i&gt;&lt;b&gt;RuntimeError: Event loop is closed&lt;/b&gt;&lt;/i&gt;와 같은 경고가 표준 출력으로 발생할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;AsyncConnection&lt;/b&gt;&lt;/i&gt;은 또한 &lt;i&gt;&lt;b&gt;AsyncResult&lt;/b&gt;&lt;/i&gt; 객체를 반환하는 &lt;b&gt;&lt;i&gt;AsyncConnection.stream()&lt;/i&gt;&lt;/b&gt; 메소드를 통한 &quot;streaming&quot; API 특징을 가지고 있습니다. 이 결과는 서버 측의 커서(cursor)를 사용하고 비동기 반복자(async iterator)와 같은 async/await API를 제공합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1722740922319&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async with engine.connect() as conn:
    async_result = await conn.stream(select(t1))

    async for row in async_result:
        print(&quot;row: %s&quot; % (row,))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;개요 - ORM&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(SQLAlchemy) 2.0 스타일의 쿼리를 통해 AsyncSession 클래스는 완전한 ORM 기능을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 모드를 사용함에 있어 지연 로딩(lazy loading)이나 기타 만료된 속성 접근을 피하기 위한 특별한 주의가 필요합니다. 다음 섹션인 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;i&gt;&lt;b&gt;AsyncSession 사용 시 암시적 IO 방지(&lt;a style=&quot;color: #bb0000; text-align: start;&quot; href=&quot;https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#asyncio-orm-avoid-lazyloads&quot;&gt;&lt;span&gt;Preventing Implicit IO when Using AsyncSession&lt;/span&gt;&lt;/a&gt;)&lt;/b&gt;&lt;/i&gt;에서 이에 대해 자세히 설명합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Warning&lt;br /&gt;&lt;br /&gt;하나의 AsyncSession 인스턴스는 여러개의 동시성 태스크들에 사용에 있어서 안전하지 않습니다. &quot;동시성 태스크와 함께 AsyncSession 사용하기(Using AsyncSession with Concurrent Tasks)&quot; 섹션과&lt;br /&gt;&quot;세션은 스레드 안전한가? AsyncSession은 동시성 태스크들 사이에서 공유하기에 안전한가?(Is the Session thread-safe? Is AsyncSession safe to share in concurrent tasks?)&quot; 섹션을 미리 봐두세요.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722741430248&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from __future__ import annotations

import asyncio
import datetime
from typing import List

from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncAttrs
from sqlalchemy.ext.asyncio import async_sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.orm import selectinload


class Base(AsyncAttrs, DeclarativeBase):
    pass

class B(Base):
    __tablename__ = &quot;b&quot;
    id: Mapped[int] = mapped_column(primary_key=True)
    a_id: Mapped[int] = mapped_column(ForeignKey(&quot;a.id&quot;))
    data: Mapped[str]

class A(Base):
    __tablename__ = &quot;a&quot;
    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[str]
    create_date: Mapped[datetime.datetime] = mapped_column(server_default=func.now())
    bs: Mapped[List[B]] = relationship()

async def insert_objects(async_session: async_sessionmaker[AsyncSession]) -&amp;gt; None:
    async with async_session() as session:
        async with session.begin():
            session.add_all(
                [
                    A(bs=[B(data=&quot;b1&quot;), B(data=&quot;b2&quot;)], data=&quot;a1&quot;),
                    A(bs=[], data=&quot;a2&quot;),
                    A(bs=[B(data=&quot;b3&quot;), B(data=&quot;b4&quot;)], data=&quot;a3&quot;),
                ]
            )


async def select_and_update_objects(
    async_session: async_sessionmaker[AsyncSession],
) -&amp;gt; None:
    async with async_session() as session:
        stmt = select(A).order_by(A.id).options(selectinload(A.bs))
        result = await session.execute(stmt)
        for a in result.scalars():
            print(a, a.data)
            print(f&quot;created at: {a.create_date}&quot;)
            for b in a.bs:
                print(b, b.data)
        result = await session.execute(select(A).order_by(A.id).limit(1))
        a1 = result.scalars().one()
        a1.data = &quot;new data&quot;
        await session.commit()
        # 커밋한 다음에 속성에 접근, 이는
        # expire_on_commit=False로 인해 가능한 부분
        print(a1.data)
        # 대신, awaitable로서
        # AsyncAttrs가 어느 속성에나 접근 할 수 있도록 할 수 있다(2.0.13에서 새로 도입)
        for b1 in await a1.awaitable_attrs.bs:
            print(b1, b1.data)


async def async_main() -&amp;gt; None:
    engine = create_async_engine(&quot;sqlite+aiosqlite://&quot;, echo=True)
    # async_sessionmaker: AsyncSession 객체들을 만들어내기 위한 공장.
    # expire_on_commit - 트랙잭션 커밋 후에 객체를 만료시키지 않도록 한다
    async_session = async_sessionmaker(engine, expire_on_commit=False)
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    await insert_objects(async_session)
    await select_and_update_objects(async_session)
    # 함수 범위에서 생성된 AsyncEngine을 위해, 닫고서
    # 풀된(pooled) 연결들을 청소한다
    await engine.dispose()


asyncio.run(async_main())&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722741808993&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BEGIN (implicit)
...
CREATE TABLE a (
    id INTEGER NOT NULL,
    data VARCHAR NOT NULL,
    create_date DATETIME DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
    PRIMARY KEY (id)
)
...
CREATE TABLE b (
    id INTEGER NOT NULL,
    a_id INTEGER NOT NULL,
    data VARCHAR NOT NULL,
    PRIMARY KEY (id),
    FOREIGN KEY(a_id) REFERENCES a (id)
)
...
COMMIT
BEGIN (implicit)
INSERT INTO a (data) VALUES (?) RETURNING id, create_date
[...] ('a1',)
...
INSERT INTO b (a_id, data) VALUES (?, ?) RETURNING id
[...] (1, 'b2')
...
COMMIT
BEGIN (implicit)
SELECT a.id, a.data, a.create_date
FROM a ORDER BY a.id
[...] ()
SELECT b.a_id AS b_a_id, b.id AS b_id, b.data AS b_data
FROM b
WHERE b.a_id IN (?, ?, ?)
[...] (1, 2, 3)
&amp;lt;A object at ...&amp;gt; a1
created at: ...
&amp;lt;B object at ...&amp;gt; b1
&amp;lt;B object at ...&amp;gt; b2
&amp;lt;A object at ...&amp;gt; a2
created at: ...
&amp;lt;A object at ...&amp;gt; a3
created at: ...
&amp;lt;B object at ...&amp;gt; b3
&amp;lt;B object at ...&amp;gt; b4
SELECT a.id, a.data, a.create_date
FROM a ORDER BY a.id
LIMIT ? OFFSET ?
[...] (1, 0)
UPDATE a SET data=? WHERE a.id = ?
[...] ('new data', 1)
COMMIT
new data
&amp;lt;B object at ...&amp;gt; b1
&amp;lt;B object at ...&amp;gt; b2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;위의 예시에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;b&gt;AsyncSession&lt;/b&gt;&lt;/i&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;은 선택적으로 사용되는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;b&gt;async_sessionmaker&lt;/b&gt;&lt;/i&gt; 도움자(helper)&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;를 사용해 인스턴스화(instantiated)됩니다. 이 도움자는 고정된 매개변수 집합과 함께 새로운&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;b&gt;AsyncSession&lt;/b&gt;&lt;/i&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체를 생성하는 공장을 제공하며, 여기서는 특정 데이터베이스 URL에 대한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;b&gt;AsyncEngine&lt;/b&gt;&lt;/i&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;과 연결하는 것을 포함합니다. 이후 이 세션은 다른 메서드에 전달되어 Python 비동기 컨텍스트 관리자(예를들어,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;b&gt;async with:&lt;/b&gt;&lt;/i&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;실행문&lt;/span&gt;)에서 사용될 수 있습니다. 이 경우 블록이 끝날 때 자동으로 닫히며, 이는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;b&gt;AsyncSession.close()&lt;/b&gt;&lt;/i&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드를 호출하는 것과 동일합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;AsyncSession을 동시성 태스크와 같이 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;AsyncSession&lt;/b&gt;&lt;/i&gt;은 &lt;b&gt;가변적(mutable)&lt;/b&gt;이고 &lt;b&gt;상태를 유지(stateful)&lt;/b&gt;하는 객체입니다. 이는 하나의 상태를 유지하는 &lt;b&gt;데이터베이스의 진행 중인 트랜잭션&lt;/b&gt;을 표현합니다. asyncio와 함께 동시성 태스크를 사용하기 위해서는 (예를 들어 &lt;b&gt;&lt;i&gt;asyncio.gather()&lt;/i&gt;&lt;/b&gt;와 같은 API들) 각각의 태스크에다 분리된&lt;i&gt;&lt;b&gt; AsyncSession&lt;/b&gt;&lt;/i&gt;을 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&quot;세션은 스레드 안전한가? AsyncSession은 동시성 태스크들 사이에서 공유하기에 안전한가?(Is the Session thread-safe? Is AsyncSession safe to share in concurrent tasks?)&quot; 섹션을 통해 &lt;i&gt;&lt;b&gt;Session&lt;/b&gt;&lt;/i&gt;과&lt;b&gt;&lt;i&gt; AsyncSession&lt;/i&gt;&lt;/b&gt;, 이 둘이 어떻게 동시성 워크로드에 대해서 어떻게 사용되어야 하는지와 함께 일반적인 설명을 보세요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Python/ETC</category>
      <category>IT</category>
      <category>orm</category>
      <category>python</category>
      <category>sqlalchemy</category>
      <category>공부</category>
      <category>데이터베이스</category>
      <category>컴퓨터</category>
      <category>컴퓨터공학</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/66</guid>
      <comments>https://kimkani.tistory.com/66#entry66comment</comments>
      <pubDate>Sun, 4 Aug 2024 12:35:54 +0900</pubDate>
    </item>
    <item>
      <title>[책 리뷰] 모두의 네트워크 기초 - 10일만에 배우는 네트워크 (2024, 길벗)</title>
      <link>https://kimkani.tistory.com/65</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2024-05-15-10-43-19.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbgvD1/btsHqbFPacr/DQA1BBSgpnLO7firO7l2PK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbgvD1/btsHqbFPacr/DQA1BBSgpnLO7firO7l2PK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbgvD1/btsHqbFPacr/DQA1BBSgpnLO7firO7l2PK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbgvD1%2FbtsHqbFPacr%2FDQA1BBSgpnLO7firO7l2PK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;683&quot; data-filename=&quot;KakaoTalk_Photo_2024-05-15-10-43-19.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;책의 감상&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 네트워크를 접하는 사람에게 친절하게 다가가는 책이다. 10일만에 네트워크를 배우기는 어렵지만, 그럼에도 초보자에게 잘 다가간 책이라고 생각되어 진다. 목차는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;네트워크 첫걸음 (LESSON 1 ~ 4)&lt;/li&gt;
&lt;li&gt;네트워크 통신을 위한 약속 (LESSON 5 ~ 9)&lt;/li&gt;
&lt;li&gt;물리 계층, 데이터를 전기 신호로 변환하는 단계 (LESSON 10 ~ 12)&lt;/li&gt;
&lt;li&gt;데이터 링크 계층, MAC 주소로 통신하는 단계 (LESSON 13 ~ 17)&lt;/li&gt;
&lt;li&gt;네트워크 계층, 목적지를 찾는 단계 (LESSON 18 ~ 24)&lt;/li&gt;
&lt;li&gt;전송 계층, 오류 없이 데이터를 전달하는 단계 (LESSON 25 ~ 29)&lt;/li&gt;
&lt;li&gt;응용 계층, 애플리케이션에 접속하는 단계 (LESSON 30 ~ 35)&lt;/li&gt;
&lt;li&gt;예시를 통한 네트워크 흐름 이해하기 (LESSON 36 ~ 37)&lt;/li&gt;
&lt;li&gt;무선으로 통신하기 (LESSON 38 ~ 41)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧게 Lesson 1 하나만 소개해 보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1장 네트워크 첫걸음&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2024-05-15-16-49-51.jpeg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rJShe/btsHqlVO57I/GkjulutzSD4A5ZCjx4ZMH0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rJShe/btsHqlVO57I/GkjulutzSD4A5ZCjx4ZMH0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rJShe/btsHqlVO57I/GkjulutzSD4A5ZCjx4ZMH0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrJShe%2FbtsHqlVO57I%2FGkjulutzSD4A5ZCjx4ZMH0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;874&quot; height=&quot;656&quot; data-filename=&quot;KakaoTalk_Photo_2024-05-15-16-49-51.jpeg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크에 대해 찬찬히 설명해준다. 물론 컴퓨터 공학과나 그 외의 학과에서 &quot;네트워크 기초 혹은 이론&quot; 수업을 들은 학생이라면 알 수 있는 정보가 많기는 하다. 그래서 이 책의 경우 네트워크에 대한 지식이 전무한 사람이 들으면 좋을 것 같다는 생각이 들었다. 그치만 현업 개발자 혹은 그와 관련된 학과 출신이라도 리마인드 하며 보기 괜찮은 책이라는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다양한 그림이 잘 표시되어 있어 이해가 쉬운 책이라고도 생각했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 OSI 7 계층 이라던지, TCP/IP의 경우 그림과 함께 도식도 잘 되어 있어서 네트워크를 처음 배우는 사람들에게 있어 첫 스타트를 끊기 좋은 책이 아닐까 하는 생각도 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나같은 경우에도 처음 책을 읽으면서 예전에 배웠던 내용을 리마인드하게 되어 좋은 계기가 된 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ETC</category>
      <category>IT책</category>
      <category>OSI</category>
      <category>TCPIP</category>
      <category>네트워크</category>
      <category>리뷰</category>
      <category>책</category>
      <category>책리뷰</category>
      <category>컴퓨터</category>
      <category>컴퓨터공학과</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/65</guid>
      <comments>https://kimkani.tistory.com/65#entry65comment</comments>
      <pubDate>Wed, 15 May 2024 16:58:55 +0900</pubDate>
    </item>
    <item>
      <title>[번역] Django REST Framework(장고 레스트 프레임워크): 장점과 단점 그리고 이의 대체제</title>
      <link>https://kimkani.tistory.com/64</link>
      <description>&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce2Wmq/btsHjUYCZKY/etTYSsobfngf4KDbKX8gdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce2Wmq/btsHjUYCZKY/etTYSsobfngf4KDbKX8gdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce2Wmq/btsHjUYCZKY/etTYSsobfngf4KDbKX8gdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce2Wmq%2FbtsHjUYCZKY%2FetTYSsobfngf4KDbKX8gdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;265&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Django REST Framework: Pros and Cons&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;This article looks at the pros and cons of using Django REST Framework for building RESTful APIS with Django.&quot; data-og-host=&quot;testdriven.io&quot; data-og-source-url=&quot;https://testdriven.io/blog/drf-pros-cons/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gCd7a/hyV2tkfYjT/oL5VdBGcfjE2BnZbcHsLv0/img.png?width=2034&amp;amp;height=1010&amp;amp;face=0_0_2034_1010,https://scrap.kakaocdn.net/dn/pAlUd/hyV2qHQjqx/s9q80o4KOsxaalX3hHyfIk/img.png?width=2034&amp;amp;height=1010&amp;amp;face=0_0_2034_1010&quot; data-og-url=&quot;https://testdriven.io/blog/drf-pros-cons/&quot;&gt;&lt;a href=&quot;https://testdriven.io/blog/drf-pros-cons/&quot; target=&quot;_blank&quot; data-source-url=&quot;https://testdriven.io/blog/drf-pros-cons/&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gCd7a/hyV2tkfYjT/oL5VdBGcfjE2BnZbcHsLv0/img.png?width=2034&amp;amp;height=1010&amp;amp;face=0_0_2034_1010,https://scrap.kakaocdn.net/dn/pAlUd/hyV2qHQjqx/s9q80o4KOsxaalX3hHyfIk/img.png?width=2034&amp;amp;height=1010&amp;amp;face=0_0_2034_1010')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;Django REST Framework: Pros and Cons&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;This article looks at the pros and cons of using Django REST Framework for building RESTful APIS with Django.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;testdriven.io&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;Django REST Framework란 무엇인가?&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.django-rest-framework.org/&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;Django REST Framework&lt;/span&gt;&lt;/a&gt; (DRF)는 RESTFul API를 설계하기 위한 강력한 오픈 소스 프레임워크입니다. DRF의 견고함, 사용하기 쉬움 그리고 보편적 기능들로 인해 &lt;a href=&quot;https://www.djangoproject.com/&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;Django&lt;/span&gt;&lt;/a&gt;로 API를 설계할 때의 &lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;de facto(드 팍토)&lt;/span&gt;&lt;/span&gt; 표준이 되었습니다. DRF의 주된 기능은 다음을 포함합니다.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;라우팅(Routing)&lt;/li&gt;&lt;li&gt;요청/응답 처리&lt;/li&gt;&lt;li&gt;인증과 인가&lt;/li&gt;&lt;li&gt;직렬화와 데이터 검증&lt;/li&gt;&lt;li&gt;필터링(Filtering), 순서(Ordering) 그외 등등!&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 &lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.django-rest-framework.org/topics/browsable-api/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #1972d1;&quot;&gt;web browseable API(웹 브라우저로 볼 수 있는 API)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.django-rest-framework.org/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #1972d1;&quot;&gt;extensive documentation(추가적인 문서화)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;, 그리고 더 많은&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.django-rest-framework.org/community/third-party-packages/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #1972d1;&quot;&gt;third-party packages(서드파티 라이브러리)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;가 프레임워크와 함께 딸려 옵니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;더 많은 DRF의 기능을 보려면, Django REST Framework Basics(https://testdriven.io/blog/drf-basics/)를 참고하세요.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;장점과 단점&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용하기 쉬움&lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;Django REST Framework의 가장 큰 이점은 즉시 사용가능하다는 점입니다. 당신은 단순하게 pip install커맨드를 실행하고, installed apps에다가 패키지를 추가하면 이걸로 당신의 API를 설계하기 좋은 시작입니다. &lt;br&gt;&lt;br&gt;DRF는 API 설계 문제의 대부분에 해당하는 문제의 해결책을 제시해 줍니다. 새로운 것을 발명하기 보다, 세부 프로덕트 기능에 집중할 수 있도록 도와줍니다. 사실, 당신은 이미 탑재되어 있는 시리얼라이저(Serializer), 뷰 셋(View Set) 그리고 라우터(Router)를 사용하여 몇 줄 만으로 본격적인 API를 만들 수 있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;성숙되고 테스트를 거침&lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;Django REST Framework는 수 년 동안 있어왔습니다. DRF는 성숙되고 테스트를 많이 거친 프레임워크입니다. 수 년 동안, Mozilla, Red Hat 그리고 Heroku같은 유명한 회사를 포함해 많은 회사들이 채택했습니다. &lt;br&gt;&lt;br&gt;다른 DRF에 대한 장점으로는, 이것이 신중하게 계획되었다는 것입니다. 이는 모듈러 아키텍처를 기반으로 하고 있으며, 이를 통해 큰 혼란 없이 다른 컴포넌트들과 교환할 수 있게 한다는 겁니다. 예를 들면 허가 시스템, 인증 혹은 직렬화 레이어입니다. 이에 더해 이 프레임워크는 Django의 컨벤션과 동행합니다. 만약 당신이 Django에 익숙하다면 DRF의 소스코드를 따라가는 것도 쉬울 것입니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;보안&lt;/b&gt;&lt;br&gt;&lt;br&gt;Django REST Framework는 당신이 더 적은 에러를 만들고, 유지가능하며 그리고 더 안전한 API들을 설계할 수 있게 도와줍니다.&lt;br&gt;&lt;br&gt;DRF 라이브러리는 코딩 표준에 부합하며, 이를 똑같이 하도록 강제합니다. 이는 당신이 일반적인 API 설계 실수를 하는 것을 피하게 해주며 다른 개발자가 온보딩하는 것을 쉽게 도와줍니다. 추가적으로 DRF는, CSRF 보호, 데이터 검증, 그리고 요청 쓰로틀링 같은 수많이 내재된 보안 기능과 같이 딸려옵니다. &lt;br&gt;&lt;br&gt;&lt;b&gt;큰 에코시스템&lt;/b&gt;&lt;br&gt;&lt;br&gt;Django REST Framework는 Djnago의 유명한 대부분의 확장하고 잘 결합됩니다. 게다가, DRF는 써&lt;a href=&quot;https://www.django-rest-framework.org/community/third-party-packages/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;드파티 패키지&lt;/span&gt;&lt;/a&gt;로 된 거대한 에코시스템을 키워 왔습니다. 더 나아가, DRF는 거대한 개발자 커뮤니티를 뒤에 두고 있습니다. 지금 이 글을 쓰고 있는 순간에도 &lt;a href=&quot;https://github.com/encode/django-rest-framework&quot; target=&quot;_self&quot;&gt;&lt;span&gt;django-rest-framework&lt;/span&gt;&lt;/a&gt;는 1200명 이상의 기여자를 깃허브에서 가지고 있습니다. 만약 당신이 막혔다면, 도움을 구하기 쉬울 것입니다.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;br&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;마스터하기 어려움&lt;/b&gt;&lt;br&gt;&lt;br&gt;앞에서 소개한 대로 Django REST Framework는 상대적으로 시작하기 쉽습니다. 당신은 기본적은 API 뷰 클래스를 활용하여 실질적으로 무엇이든 이룰 수 있습니다. 하지만, DRF 프레임워크의 힘은 CBV(Class Based View)와 미리 만들어 놓은 직렬화, 뷰 셋, 그리고 라우팅 등과 같은 클래스에 있습니다.&lt;br&gt;&lt;br&gt;만약 당신이 DRF가 새로운 사람이면, 당신의 머리를 그것들로 채우는 데 시간이 걸릴 수 있습니다. 하지만 한번 그것을 파악하면, 당신의 코드베이스의 반을 그걸로 자를 수 있으며 그리고 마침내 더욱 “깨끗한” 웹 앱을 만들 수 있게 될 것입니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;성능 이슈들&lt;/b&gt;&lt;br&gt;&lt;br&gt;Django REST Framework는 추가된 오버헤드로 당신의 웹 앱의 성능을 늦출 수 있습니다. 이 패키지는 API를 쉽게 설계할 수 있게 디자인되어 있지 가능한 최대 성능을 이끌어 내도록 되어 있찌 않습니다. 이 직렬화 과정은 &lt;a href=&quot;https://marshmallow.readthedocs.io/en/stable/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;marshmallow&lt;/span&gt;&lt;/a&gt;하고 &lt;a href=&quot;https://serpy.readthedocs.io/en/latest/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;serpy&lt;/span&gt;&lt;/a&gt;(&lt;a href=&quot;https://voidfiles.github.io/python-serialization-benchmark/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;벤치마크&lt;/span&gt;&lt;/a&gt;를 보세요)와 같은 다른 직렬화 라이브러리와 비교하여 상대적으로 느립니다.&lt;br&gt;&lt;br&gt;심지어 DRF의 개발자 Tom Christie가 언급한 것처럼, 당신은 성능이 중요한 뷰에서는 이 직렬화를 쓰는 것을 피해야 합니다.(&lt;a href=&quot;https://www.dabapps.com/insights/api-performance-profiling-django-rest-framework/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;소스&lt;/span&gt;&lt;/a&gt;)&lt;br&gt;&lt;br&gt;다른 문제로는 관련된 필드를 직렬화할 때 생겨납니다. 기본적으로 DRF는 &lt;b&gt;select_related&lt;/b&gt;하고 &lt;b&gt;prefetch_related&lt;/b&gt;의 문법으로 쿼리셋을 자동적으로 최적화하여 시리얼라이저(serializer)에게 넘기지 않습니다. 이는 N+1문제로 쉽게 이어집니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;나쁜 위험 분리&lt;/b&gt;&lt;br&gt;&lt;br&gt;DRF에 있는 직렬화 레이어 너무 많은 작업을 수행합니다. 직렬화, 데이터 검증, 모델 생성과 같은 일을 맞고 있습니다. 제가 몇몇 발견한 코드베이스는 시리얼라이저에 비즈니스 로직을 포함하고 있습니다. 이는 개발자로 하여금 그들의 시리얼라이저와 모델에 대한 중복된 검증 코드를 작성하게하는 결과로 나타납니다. 게다가, 당신의 코드를 더욱 테스트하기 어렵게 하게 만듭니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;부족한 신 기능&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;현재 단계에서 이 프레임워크는 새로운 기능을 넣는 것을 우선시하고 있지 않습니다. 대부분의 커밋이 리팩토링과 버그 수정입니다. 이는 DRF로 하여금 몇몇 어플리케이션에서 조금은 뒤쳐지고 한정적으로 보이게 할 수 있습니다.&lt;br&gt;&lt;br&gt;제 의견에서 프레임워크에 부족한 기능은 다음과 같습니다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;좋은 인증 솔루션&lt;/li&gt;&lt;li&gt;비동기 지원 (이에대한 &lt;a href=&quot;https://github.com/encode/django-rest-framework/issues/8496&quot; target=&quot;_self&quot;&gt;&lt;span&gt;공개된 이슈&lt;/span&gt;&lt;/a&gt;가 있습니다.)&lt;/li&gt;&lt;li&gt;웹 소켓 지원&lt;/li&gt;&lt;li&gt;더 나은 중첩 직렬화&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;이러한 결함들은 서드 파티 패키지를 통해 다루어질 수 있습니다. 예를들면, 인증을 위한 &lt;a href=&quot;https://dj-rest-auth.readthedocs.io/en/latest/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;dj-rest-auth&lt;/span&gt;&lt;/a&gt;, 비동기를 지원을 위한 &lt;a href=&quot;https://github.com/em1208/adrf&quot; target=&quot;_self&quot;&gt;&lt;span&gt;adrf&lt;/span&gt;&lt;/a&gt; 그리고 웹 소켓을 위한 &lt;a href=&quot;https://channels.readthedocs.io/en/latest/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;django-channels&lt;/span&gt;&lt;/a&gt;가 있습니다. 하지만 이러한 것들 대부분이 아직은 미흡합니다.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;대체제&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;한번 당신이 다음 프로젝트를 시작할 때 고려하면 좋은 엄청난 대체제를 알아봅시다.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;Django Ninja&lt;br&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://django-ninja.dev/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Django Ninja&lt;/span&gt;&lt;/a&gt;는 상용에 사용하기 위해 준비되고 빠르게 API를 설계하기 위한 웹 프레임워크입니다. 이 프레임워크는 양식화된 API 구조를 넘어 스피드와 간결함을 우선순위로 둡니다. 이는, 훌륭한 비동기 능력을 주는 S&lt;a href=&quot;https://www.starlette.io/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;tarlette&lt;/span&gt;&lt;/a&gt;위에서 설계되었습니다. 이는 API를 위해 오픈 스탠다드를 준수하는 (전에는 Swagger로 잘 알려진) &lt;a href=&quot;https://swagger.io/specification/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;OpenAPI&lt;/span&gt;&lt;/a&gt;와 &lt;a href=&quot;https://json-schema.org/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;JSON Schema&lt;/span&gt;&lt;/a&gt;를 포함하고 있습니다. 이는 대화형 API 문서를 자동적으로 생성할 수 있게 합니다.&lt;br&gt;&lt;br&gt;Django Ninjia는 Django와 FastAPI사이의 혼합체입니다. 이는 두 세상의 장점 : Django의 어드민 사이트와 ORM 그리고 FastAPI의 타입 힌트인 Pydantic 모델의 장점을 줍니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;장점&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;성능&lt;/li&gt;&lt;li&gt;타입 힌트와 Pydantic 지원&lt;/li&gt;&lt;li&gt;자동화된 문서 생성&lt;/li&gt;&lt;li&gt;비동기 지원&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;&lt;b&gt;단점&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;기능이 풍부하지 않다&lt;/li&gt;&lt;li&gt;제한된 확장성&lt;/li&gt;&lt;li&gt;덜 유명&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Django Ninja에 더 알아보기 위해 Django와 Pydnatic(&lt;a href=&quot;https://testdriven.io/blog/django-and-pydantic/&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;https://testdriven.io/blog/django-and-pydantic/&lt;/span&gt;&lt;/a&gt;)아티클을 참조해 보세요.&lt;/blockquote&gt;&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;br&gt;Django Tastypie&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;&lt;a href=&quot;https://django-tastypie.readthedocs.io/en/latest/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;Django Tastypie&lt;/span&gt;&lt;/a&gt;는 API 설계를 위한 미니멀하지만 유연한 프레임워크입니다. 이 프레임워크는 직렬화, 인증, 캐싱, 필터링, 정렬, 그리고 기본적인 보안 기능을 제공합니다. &lt;br&gt;&lt;br&gt;이는 리소스의 밑바탕에서 이루어집니다. 당신이 당신만의 Django 모델을 만들고, Django Tastypie는 나머지를 처리합니다. 이는 자동으로 시리얼라이저를 생성하고 CRUD 작업을 관리합니다. 이는 JSON, XML 그리고 YAML 형식을 지원합니다.&lt;br&gt;&lt;br&gt;Django Tastypie는 CRUD 작업을 요구하는 대부분의 작은 프로젝트에 적합합니다. 이를 활용하여 당신은 작으면서도 괜찮은 API-설계 인터페이스를 Django에게 제공해야하는 것을 갖추면서 얻을 수 있습니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;장점&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;유연함&lt;/li&gt;&lt;li&gt;사용하기 쉬움&lt;/li&gt;&lt;li&gt;가벼움&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;&lt;b&gt;단점&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;부족한 기능&lt;/li&gt;&lt;li&gt;“복잡한” 앱에서는 부적절&lt;/li&gt;&lt;li&gt;아직 베타 상태 (완벽한 릴리즈는 아직)&lt;/li&gt;&lt;li&gt;덜 유명함&lt;/li&gt;&lt;/ul&gt;&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;br&gt;FastAPI, Flask, 그리고 다른 프레임워크&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;만약 Django REST Framework를 사용하기를 고려할 때, 먼저 스스로에게 Django가 필요한지 질문을 던지는 것이 중요합니다. 당신의 프로젝트가 Django에서 특정된 기능이 필요한지 말입니다. 혹은 당신이 Django가 편하기에 Django를 혼자 사용하고 있는지 말입니다.&lt;br&gt;&lt;br&gt;Django는 API를 설계하기 위한 최고의 프레임워크가 아닐수도 있습니다. Python기반의 몇몇 대체제가 더 적절할 수 있습니다. 그것들을 이용해서 더 좋은 성과를 얻고, 그것들은 당신으로 하여금 읽기 쉽고 덜 반복되는 코드 작성을 할 수 있게 할 수 있습니다.&lt;br&gt;&lt;br&gt;저는 당신이 다음 세가지를 체크했으면 합니다.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;FastAPI&lt;/li&gt;&lt;li&gt;Flask&lt;/li&gt;&lt;li&gt;Pyramid&lt;/li&gt;&lt;/ol&gt;&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;br&gt;결론&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;Django REST Framework는 필수는 아니지만 확실히 개발자의 생활을 더 쉽게 해줍니다. 코드 덩어리를 재사용할 수 있어 무언가 다시 할 필요가 없습니다. 또한 더 나은, 깨끗한, 그리고 에러가 적은 코드를 작성하도록 이끌어 줍니다.&lt;br&gt;&lt;br&gt;이 프레임워크의 가장 큰 단점은 성능 문제와 새로운 기능 부족입니다. Django REST Framework가 가장 빠른 프레임워크는 아니지만 광범위한 기능 때문에 사용하는 것이 합리적입니다. 대부분의 웹 개발 성능 향상은 적절한 캐싱과 데이터베이스 계층 최적화를 통해 달성할 수 있습니다.&lt;br&gt;&lt;br&gt;API가 가능한 한 높은 성능을 내기를 원한다면 FastAPI를 실험해볼 수 있습니다. Django와 FastAPI 사이의 모범적인 중간지점은 Django Ninja입니다. 마지막으로 간단한 API의 경우 Django Tastypie나 Flask를 사용할 수 있습니다.&lt;/p&gt;</description>
      <category>Python/Django</category>
      <category>공부</category>
      <category>소프트웨어</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>컴퓨터공학</category>
      <category>코딩</category>
      <category>파이썬</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/64</guid>
      <comments>https://kimkani.tistory.com/64#entry64comment</comments>
      <pubDate>Sat, 11 May 2024 17:09:06 +0900</pubDate>
    </item>
    <item>
      <title>Clean Architecture(클린 아키텍처) 공부 - 육각형 아키텍처, 엔티티, 유스케이스, 프레임워크, 드라이버, 관심사 분리(SoC), 험블 객체 패턴, 프레젠터, 뷰, 뷰모델, 아키텍처</title>
      <link>https://kimkani.tistory.com/63</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-20 오후 5.28.08.png&quot; data-origin-width=&quot;1248&quot; data-origin-height=&quot;1144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F61BY/btsGNzNK8pg/hsvukAavkQ7UzVkpRlTnzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F61BY/btsGNzNK8pg/hsvukAavkQ7UzVkpRlTnzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F61BY/btsGNzNK8pg/hsvukAavkQ7UzVkpRlTnzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF61BY%2FbtsGNzNK8pg%2FhsvukAavkQ7UzVkpRlTnzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;624&quot; height=&quot;572&quot; data-filename=&quot;스크린샷 2024-04-20 오후 5.28.08.png&quot; data-origin-width=&quot;1248&quot; data-origin-height=&quot;1144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 육각형 아키텍처(Hexagonal Architecture), DCI(Data, Context and Interaction) 그리고 BCE(Boundary-Control-Entity)를 소개하면서 이들 아키텍처가 시스템으로 하여금 다음과 같은 특징을 지니도록 만든다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관심사 분리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프레임워크 독립성 - 프레임워크의 존재 여부에 의존하지 않는다. 프레임워크를 도구로 사용하고, 이것이 지닌 제약사항으로 시스템을 욱여 넣도록 강제하지 않는다.&lt;/li&gt;
&lt;li&gt;테스트 용이성 - 외부 요소 없이도 테스트가 가능하다.&lt;/li&gt;
&lt;li&gt;UI 독립성 - 시스템의 일부분을 변경하지 않아도 UI를 쉽게 변경할 수 있다.&lt;/li&gt;
&lt;li&gt;DB 독립성 - 다른 데이터 베이스로 바꾸어도 지장이 없다.&lt;/li&gt;
&lt;li&gt;모든 외부 에이전시에 대한 독립성.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 관심사 분리에 대해 좀 더 찾아봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1713603123970&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Separation of Concerns (SoC) - GeeksforGeeks&quot; data-og-description=&quot;A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.&quot; data-og-host=&quot;www.geeksforgeeks.org&quot; data-og-source-url=&quot;https://www.geeksforgeeks.org/separation-of-concerns-soc/&quot; data-og-url=&quot;https://www.geeksforgeeks.org/separation-of-concerns-soc/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Gp8qE/hyVS6bBgtU/tms3MC9sZdjLWC914MRNt1/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/sssoo/hyVS6CEx7k/CPtC6caSNmFGAJTjxNkVbk/img.png?width=500&amp;amp;height=600&amp;amp;face=0_0_500_600&quot;&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/separation-of-concerns-soc/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.geeksforgeeks.org/separation-of-concerns-soc/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Gp8qE/hyVS6bBgtU/tms3MC9sZdjLWC914MRNt1/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/sssoo/hyVS6CEx7k/CPtC6caSNmFGAJTjxNkVbk/img.png?width=500&amp;amp;height=600&amp;amp;face=0_0_500_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Separation of Concerns (SoC) - GeeksforGeeks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.geeksforgeeks.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 내가 관심 가는 분야는 바로 응용 분야였다. 아키텍처, 설계, 코딩, 테스트, 디버깅, &amp;nbsp;유지 및 보수 쪽에 대해 자세하게 설명을 해놓아서 한번 가져와 보았다.&lt;/p&gt;
&lt;p style=&quot;color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;3:1-3:110&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;5:1-5:9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;아키텍처:&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;7:1-10:0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;7:1-7:55&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;계층 아키텍처: 프레젠테이션, 비즈니스 로직, 데이터 접근과 같은 계층으로 관심사를 분리.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;8:1-8:59&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마이크로서비스 아키텍처: 시스템을 독립적인 서비스로 분해하며, 각 서비스는 특정 관심사를 담당.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;9:1-10:0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;서비스 지향 아키텍처 (SOA): 시스템을 느슨하게 결합되고 상호 운영 가능한 서비스로 구성.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;11:1-11:7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;설계:&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;13:1-16:0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;13:1-13:54&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모듈화: 시스템을 더 작고 관리하기 쉬운 모듈로 분할하며, 각 모듈은 특정 관심사를 다룬다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;14:1-14:67&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;명확한 인터페이스: 컴포넌트 간의 명확한 인터페이스를 정의하여 구현 세부 사항을 캡슐화하고 느슨한 결합을 촉진.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;15:1-16:0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;도메인 주도 설계 (DDD): 핵심 도메인을 식별하고 모델링하며, 경계 컨텍스트(bounded context) 내에 도메인 로직을 캡슐화.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;17:1-17:7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;코딩:&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;19:1-22:0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;19:1-19:54&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;단일 책임 원칙 (SRP): 함수, 클래스 및 모듈이 단일 책임 또는 관심사를 갖도록 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;20:1-20:64&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;캡슐화: 관련 로직과 데이터를 함수, 클래스 또는 모듈 내에 캡슐화하면서 관련 없는 관심사는 별도로 유지.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;21:1-22:0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;명확한 명명: 함수, 변수 및 클래스의 용도와 책임을 전달하기 위해 명확하고 설명적인 이름을 사용.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;23:1-23:8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;테스트:&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;25:1-28:0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;25:1-25:41&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;단위 테스트: 각 관심사를 독립적으로 검증하는 집중된 단위 테스트 작성.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;26:1-26:57&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;통합 테스트: 모듈 또는 컴포넌트 간의 상호 작용을 테스트하여 예상대로 함께 작동하는지 확인.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;27:1-28:0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 격리: 테스트 내에서 관심사를 분리하여 문제를 식별하고 진단하는 작업을 용이하게 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;29:1-29:8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;디버깅:&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;31:1-34:0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;31:1-31:45&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;격리: 코드 내에서 관심사를 분리하여 버그 또는 문제의 근본 원인을 좁힌다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;32:1-32:51&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추적 및 로깅: 로깅 및 추적 관심사를 비즈니스 로직과 분리하여 디버깅 용이하게 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;33:1-34:0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;오류 처리: 핵심 기능과 별도로 오류 및 예외를 처리하여 오류 진단 및 해결 향상.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;35:1-35:15&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;유지 관리 및 발전:&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;37:1-39:40&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;37:1-37:51&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;변경 격리: 특정 관심사로 변경을 국한화하여 시스템 수정 및 유지 관리 용이하게 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;38:1-38:67&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;버전 관리 및 의존성 관리: 의존성과 버전을 관리하여 한 관심사의 변경이 다른 관심사에 영향을 미치지 않도록 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;39:1-39:40&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리팩토링: 관심사 분리 및 유지 관리 향상을 위해 코드 리팩토링 수행.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 SRP(Single Responsibility Principle)과의 차이점도 있는데 아주 명쾌하게 설명해 주었다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-20 오후 6.03.22.png&quot; data-origin-width=&quot;1864&quot; data-origin-height=&quot;818&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dY4ISe/btsGNZk3pbA/MCkcYKa5W42acthtYS1k9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dY4ISe/btsGNZk3pbA/MCkcYKa5W42acthtYS1k9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dY4ISe/btsGNZk3pbA/MCkcYKa5W42acthtYS1k9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdY4ISe%2FbtsGNZk3pbA%2FMCkcYKa5W42acthtYS1k9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1864&quot; height=&quot;818&quot; data-filename=&quot;스크린샷 2024-04-20 오후 6.03.22.png&quot; data-origin-width=&quot;1864&quot; data-origin-height=&quot;818&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;7:1-9:0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;7:1-7:107&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;SRP (Single Responsibility Principle)&lt;/b&gt;: 하나의 컴포넌트는 변경이 필요하다는 것을 결정하는 역할에 의해만 변경될 수 있는 한 가지 이유만 있어야 한다. 콘웨이 법칙에 따르면, &lt;span style=&quot;text-align: start;&quot;&gt; 컴포넌트는 해당 컴포넌트를 사용하는 역할에서만 변경 필요성이 결정됩니다. 따라서 책임을 &lt;/span&gt;&lt;b&gt;역할별로 수직적으로&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt; 분할해야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;8:1-9:0&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;SoC (Separation of Concerns)&lt;/b&gt;: 기능을 구현하려면 인프라, 애플리케이션 및 도메인 계층 로직의 조합에 의존해야 한다. SoC 원칙은 기능의 &quot;기술적&quot; 구현 세부 정보를 구분하도록 촉구합니다. 이를 위해 계층화된 아키텍처(수평)를 구현할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;의존성 규칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림에서 내부의 원 안으로 들어갈수록 고수준의 소프트웨어가 된다. 바깥쪽은 메커니즘 안쪽은 정책이다. 여기서 중요하게 설명하는 것이 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;소스 코드 의존성은 반드시 안쪽으로,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;고수준의 정책을 향해야 한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부의 원 : 외부 원에 선언된 어떤 것에 대해서도 언급해서는 안된다&lt;/li&gt;
&lt;li&gt;외부의 원 : 내부의 원에서 외부 원에 선언된 데이터 형식은 절대 사용해서는 안된다. 만약 그것이 프레임워크가 생성한 것은 더더욱 사용해서는 안 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Entity : 전사적인 핵심 업무 규칙을 캡슐화&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적으로 엔티티는 가장 일반적이며 고수준인 규칙을 캡슐화한다. 외부의 무언가가 변경되더라도 엔티티는 변경될 가능성이 지극히 낮다.&lt;/li&gt;
&lt;li&gt;단순하게 만약 투두 앱을 만든다고 하면 JavaScript코드로 구현하면 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1713604911588&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Todo {
  constructor(text) {
    this.text = text;
    this._isComplete = false;
  }

  markComplete() {
    this._isComplete = true;
  }

  getStatus() {
    return this._isComplete ? 'Complete' : 'Incomplete';
  }
}

module.exports = Todo;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;UseCase : 어플리케이션에 특화된 업무규칙을 포함&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유스케이스는 엔티티로 들어오고 나가는 데이터 흐름을 조정한다. 엔티티가 자신의 핵심 업무 규칙을 사용해서 유스케이스의 목적을 달성하도록 이끈다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Interface Adaptor : 데이터의 변환을 맡은 임무&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 유스케이스와 엔티티에게 가장 편리한 형식에서 DB같은 외부 에이전세에게 편리한 형식으로 변환.&lt;/li&gt;
&lt;li&gt;Presenter, View, Controller등등이 속한다.&lt;/li&gt;
&lt;li&gt;SQL기반 DB를 사용하면, SQL은 이 계층을 벗어나면 안된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 프레임워크나 DB는 모두 세부사항이 위치하는 곳이다. 이러한 것들을 모두 외부에 위치시켜서 피해를 최소화 해야 한다. 또한 원이 4개로만 이루어질 필요도 없다. &lt;b&gt;하지만 어떤 경우에도 의존성 규칙은 적용되어야 한다.&amp;nbsp;&lt;/b&gt;소스 코드 의존성은 항상 안 쪽을 향해야 한다. 안 쪽으로 향할 수록 점점 추상화 되고 캐슐화 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;경계 횡단하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-20 오후 6.08.09.png&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eaawY5/btsGNgHHhh9/5GnKVYk1wfY6k2BWg7b6VK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eaawY5/btsGNgHHhh9/5GnKVYk1wfY6k2BWg7b6VK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eaawY5/btsGNgHHhh9/5GnKVYk1wfY6k2BWg7b6VK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeaawY5%2FbtsGNgHHhh9%2F5GnKVYk1wfY6k2BWg7b6VK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;330&quot; height=&quot;301&quot; data-filename=&quot;스크린샷 2024-04-20 오후 6.08.09.png&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;632&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어 흐름이나, 소스 코드 의존성 모두 원 안쪽으로 방향성을 향하고 있는 것을 알 수 있다. 만약 방향이 반대가 되어야 하는 경우 대체로 의존성 역전 원칙을 사용하여 해결한다. 만약 유스케이스에서 프레젠터를 호출한다면? 직접 호출해버리면 의존성 규칙을 위배하기에, 인터페이스를 호출하도록 하고, 외부 원인 프레젠터가 그 인터페이스를 구현하도록 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 경계를 횡단하는 데이터는 다양한 모습을 띌 수 있다. DTO(Data Transfer Object), 구조체, 인자를 사용해서 데이터 전달 등등 다양하게 전달할 수 있다. 핵심은 이것이다. &lt;b&gt;격리되어 있는 간단한 데이터 구조가 경계를 가로질러 전달된다.&amp;nbsp;&lt;/b&gt;경계를 가로질러 데이터를 전달할 때, 항상 내부의 원에서 사용하기에 가장 편리한 형태를 가져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시나리오 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lp4Js/btsGML2v6Rw/gZ6LyMMDQbwjti3u49hETk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lp4Js/btsGML2v6Rw/gZ6LyMMDQbwjti3u49hETk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lp4Js/btsGML2v6Rw/gZ6LyMMDQbwjti3u49hETk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLp4Js%2FbtsGML2v6Rw%2FgZ6LyMMDQbwjti3u49hETk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;456&quot; height=&quot;328&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;259&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시나리오를 정리하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;WS(Web Server)는 컨트롤러에게 데이터를 모아 전달한다, 이를 평범한 자바 객체(단순하게 말해 인스턴스, 혹은 Object)로 묶고서 UseCaseInteractor로 전달.&lt;/li&gt;
&lt;li&gt;UseCaseInteractor는 이를 분석, Entity의 제어 및 이것이 사용할 데이터를 Data Access Interface에서 불러온다.&lt;/li&gt;
&lt;li&gt;Entity가 완성된다.&lt;/li&gt;
&lt;li&gt;이후 UseCaseInteractor는 OutputData를 구현한다. 이를 OutputBoundary를 통해 Presenter로 전달한다.&lt;/li&gt;
&lt;li&gt;Presenter는 OutputData를 화면에 출력할 수 있게 데이터를 가공한다.&lt;/li&gt;
&lt;li&gt;이후 ViewModel과 View를 통해 화면에 출력한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;험블 객체 패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 말해, 테스트하기 쉬운 행위와 그렇지 않은 것들(이를 험블객체로 분리한다)을 분리해서 정리하는 것이다. 여기서 View는 험블 객체이다. 즉 테스트하기 어렵다는 것이다. 왜냐하면 데이터를 GUI(Graphic User Interface)로 옮기기만 할 뿐이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프레젠터는 테스트하기 쉽다. 프레젠터는 Application, Server 혹은 그 외 요소로부터 데이터를 받아 화면에 표현할 수 잇는 포맷으로 만들기 때문이다. 즉, 복잡하거나 자주 변경되는 레이어를 가능한 험블(Humble)하게 유지하고 이 부분에 대한 단위 테스트 작성 노력을 최소화하는 것이 좋다. 단위 테스트 작성에 드는 노력 대비 효과가 적기 때문. 대신 비즈니스 로직 레이어에 집중하여 상세한 테스트를 진행하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터베이스 게이트웨이와 데이터 매퍼(ORM)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유스케이스 인터랙터와 데이터베이스 사이에는 데이터베이스 게이트웨이가 위치한다. 다형적 인터페이스로, CRUD와 관련된 모든 메서드를 포함한다. 이 때 유스케이스 계층은 SQL을 허용하지 않는다, 즉 필요한 메서드를 제공하는 게이트웨이 인터페이스를 호출한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구현체는 험블 객체다 - 너무 데이터와 가까워서 그러지 않을까 하는 생각이 든다 - 반면에 인터랙터는 업무 규칙을 캡슐화하기에, 테스트하기 쉬운데 게이트웨이 같은 것들은 Mock, Stub, Faker같은 것들로 대체가 가능하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 ORM(Prisma, SQLAlchemy, JPA 등등)은 어디에 속할까? 저자는 이러한 명칭부터 잘못 되었다고 본다. 결국 데이터 매퍼라고 불러야 한다고 하며, 그 이유에 대해 RDB에서 가져온 데이터를 데이터 구조에 맞게 담아주기 때문이라고 한다. 결국 Mapping을 해주는 것이라고 보는 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 ORM의 경우에는 실제로 데이터베이스 계층과 가깝고, 저자도 데이터베이스 계층이라고 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서비스 리스너&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서도 험블 객체 패턴을 발견할 수 있다. 결국 데이터가 횡당되면서 다양한 변화가 이루어지고 포맷이 변경되기도 한다. 즉, 데이터의 변화에 따라 테스트하기 어려울 수도 쉬울 수도 있다.&lt;/p&gt;</description>
      <category>Architecture</category>
      <category>공부</category>
      <category>소프트웨어</category>
      <category>아키텍처</category>
      <category>자바스크립트</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>컴퓨터공학</category>
      <category>코딩</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/63</guid>
      <comments>https://kimkani.tistory.com/63#entry63comment</comments>
      <pubDate>Sat, 20 Apr 2024 18:38:11 +0900</pubDate>
    </item>
    <item>
      <title>Clean Architecture(클린 아키텍처) 공부 - 컴포넌트 응집도(Component Cohesion), REP, CCP, CRP, 깃 플로우 전략, MSA</title>
      <link>https://kimkani.tistory.com/62</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGgQYy/btsGqLVz3cA/w8khQb3K43QhnAIzwKvXnk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGgQYy/btsGqLVz3cA/w8khQb3K43QhnAIzwKvXnk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGgQYy/btsGqLVz3cA/w8khQb3K43QhnAIzwKvXnk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGgQYy%2FbtsGqLVz3cA%2Fw8khQb3K43QhnAIzwKvXnk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1060&quot; height=&quot;700&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트 응집성, 영어로 풀어보자면 Component Conhesion이다. 여기에 컴포넌트 응집도와 관련된 세 가지 원칙을 이야기 한다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;REP(Reuse/Release Equivalence Principle) : 재사용/릴리스 등가 원칙&lt;/li&gt;
&lt;li&gt;CCP(Common Closure Principle) : 공통 폐쇄 원칙&lt;/li&gt;
&lt;li&gt;CRP(Common Reuse Principle) : 공통 재사용 원칙&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;REP - 재사용/릴리스 등가 원칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 모듈 관리 도구의 중요해짐을 설파한다. 사실 파이썬에서도 비슷하다. 이러한 모듈 관리 - 혹은 패키지 관리 도구들은 소프트웨어의 재사용을 어떻게 하면 잘 할지를 도와준다. 각각의 언어에 대해 사용하는 소프트웨어 모듈, 패키지 혹은 버전 관리 도구들은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java - maven, gradle&lt;/li&gt;
&lt;li&gt;Ruby - RVM, rbenv&lt;/li&gt;
&lt;li&gt;Closure - Leiningen&lt;/li&gt;
&lt;li&gt;Python - pip, pyenv, poetry&lt;/li&gt;
&lt;li&gt;JavaScript - yarn, npm, NVM&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이러한 모듈 매니저, 버전 매니저 들은 추적을 위한 기능이다. 즉, 프로그램을 버전 - 숫자 혹은 다양한 알파벳 - 으로 관리하여 절차를 만드는 것이다. 물론 이 절차에 대해 다양한 의견들이 있고 다양한 정책들이 있다. 내가 생각하기에 이러한 절차, 넘버링이 대표적으로 등장하는 곳이 깃허브이며 그에 대한 정책이 나타나는 것이 gitflow 전략이라고 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;795&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3mNsG/btsGpKpTCRn/A2IN5ujVrlkUBAsCWdRrrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3mNsG/btsGpKpTCRn/A2IN5ujVrlkUBAsCWdRrrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3mNsG/btsGpKpTCRn/A2IN5ujVrlkUBAsCWdRrrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3mNsG%2FbtsGpKpTCRn%2FA2IN5ujVrlkUBAsCWdRrrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;795&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;795&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃 플로우 전략이 현재 개발자에게 가장 잘 보여줄 수 있는 재사용/릴리스 등가 원칙이 아닐까 생각한다. 각각의 feautre, develop, release, hot fixes, 그리고 master(혹은 main)을 간략히 설명하면 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #222222; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;master(혹은 main): 제품 출시 버전 관리를 위한 브랜치&lt;/li&gt;
&lt;li&gt;develop: 다음 출시 버전을 위한 개발 전용 브랜치&lt;/li&gt;
&lt;li&gt;feature: 새로운 기능의 개발을 위한 브랜치&lt;/li&gt;
&lt;li&gt;release: 다음 출시를 준비하늩 브랜치&lt;/li&gt;
&lt;li&gt;hotfix: 이미 출시한 제품의 버그를 고치기 위한 브랜치&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 다양한 버전이 있지만, 나는 이 모두가 결국 이 책에서 제시하는 컴포넌트 응집도를 높이고 이에 대한 재사용과 릴리스를 더 좋게 하가 위해 사용하는 것이 아닐까해서 나온 전략이 아닐까 생각한다. - 물론 이 깃 플로우 전략은 웹 어플리케이션에는 어울리지 않는 다고 한다, 주된 소프트웨어 버전 관리가 필요한 iOS나 Android 어플리케이션 혹은 퍼블릭 패키지 같이 공개되어 있는 것에 어울린다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 이러한 gitflow 전략은 결국 번호 혹은 알파벳 같은 것을 부여하여 추적을 용이하게 하고, 릴리스에 태그를 달아 컴포넌트들이 서로 호환되는지 보증하기 위한 전략이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 REP에 대하여 저자는 &quot;컴포넌트를 구성하는 모든 모듈은 서로 공유하는 중요한 테마나 목적이 있어야 한다&quot;의 필요성을 제기한다. 하지만, 이러한 문구는 굉장히 애매하다. 저자는 너무 당연하다는 듯이 이야기 하지만, 실제 제품을 출시할 때 과연 어디까지를 같은 테마나 목적으로 보고 선을 그을 것인가는 굉장히 현실세계에서도 애매하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이러한 문제를 저자도 알고 있어서 굉장히 약점이 큰 원칙으로 이야기 하기도 한다. 내가 보기에는 굉장히 애매한 &quot;공통 테마란?&quot;이라는 질문에 쉽게 대답할 수 없어서 그런 것 같기도 하다. 이를 회사로 예로 들면, &quot;테마나 목적으로 묶인 구성원&quot;인 팀으로도 볼 수 있다. 하지만 이 구성원들이 지닌 생각은 전부 다르며 생각 - 프로그램에서는 릴리스 버전 - 을 Sync하기도 굉장히 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 REP에 대하여 저자가 하고 싶은 말은 CCP와 CRP를 통해 보완을 해서 해결하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;CCP- 공통 폐쇄 원칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;동일한 이유로 동일한 시점에 변경되는 클래스를 같은 컴포넌트로 묶어라.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서로 다른 시점에 다른 이유로 변경되는 클래스는 다른 컴포넌트로 분리하라.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 이 부분을 읽을 때 SRP와 굉장히 비슷하다 생각했는데, 역시나 SRP를 컴포넌트 관점에서 다시 쓴 것이라고 저자가 친절하게 설명해 주었다. 사실 이 부분을 읽으면서 든 생각은 - 백엔드 개발자로서 - MSA(Mciro Service Architecture)가 굉장히 좋아할만한 원칙이라는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpH2Cf/btsGpLvwOR7/LYjiayNrh996Q54InZ3Qmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpH2Cf/btsGpLvwOR7/LYjiayNrh996Q54InZ3Qmk/img.png&quot; data-alt=&quot;https://learn.microsoft.com/ko-kr/azure/architecture/guide/architecture-styles/microservices&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpH2Cf/btsGpLvwOR7/LYjiayNrh996Q54InZ3Qmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpH2Cf%2FbtsGpLvwOR7%2FLYjiayNrh996Q54InZ3Qmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;987&quot; height=&quot;399&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://learn.microsoft.com/ko-kr/azure/architecture/guide/architecture-styles/microservices&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSA가 도입 혹은 탄생한 이유는 많겠지만 모놀로식의 한계 극복에서 나온 것이라고 생각한다. 결국 &lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;모놀리식 애플리케이션의 경우 시간이 경과하면서 코드 종속성이 얽히는 경우가 많기에 MSA는 코드나 데이터 저장소를 공유 - 즉 의존성을 최소하 하면서 - 하지 않으므로 종속성을 최소화한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;CCP는 동일한 유형의 변경에 대해 닫혀 있는 클래스 - 혹은 기능 - 들을 하나의 컴포넌트로 묶는다. 그리고 이러한 CCP의 원칙으로 묶인 프로그램을 다시금 기능별로 합쳐서 각각의 서비스로 개별화 시킨다. 하지만 MSA같이 CCP를 통해 개별적으로 묶어서 &quot;관리&quot;한다는 것은 개발팀의 규모가 어느정도 있어야 유지될 수 있지 않을까 생각한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;극 개개의 서비스, 컴포넌트들은 아주 단순하게 관리할 수 있지만, 이 서비스, 컴포넌트들이 서로 영향을 주고 받는 전체 시스템 구조 상으로는 개인으로 관리하기에는 너무 복잡해지는 것이다. 사실 개발팀이 크다고 해도 각 서비스, 컴포넌트들에 대한 관리를 위해 커뮤니케이션이 아주 활발하게 이루어지는 이상적인 상황이 일어나야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;즉, 컴포넌트를 동일한 이유로 묶었다고 해도, 그 컴포넌트가 여러개 이면 오히려 관리의 어려움이 더 늘어나지 않을까 한다. &lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;CRP- 공통 재사용 원칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;컴포넌트 사용자들을 필요하지 않는 것에 의존하게 강요하지 마라.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;나는 이 CRP가 컴포넌트 자체에 집중한다고 생각한다. 즉 SOLID원칙에서 나온 ISP처럼, 필요없는 것은 과감하게 쳐내라 - 다른 말로 하자면, 필요 없는 것을 잘 파악해야 한다 - 라는 것처럼 들렸다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;결론&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8RUyT/btsGp0eV7Wz/RIgVKPkuhykCV75tpx15Nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8RUyT/btsGp0eV7Wz/RIgVKPkuhykCV75tpx15Nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8RUyT/btsGp0eV7Wz/RIgVKPkuhykCV75tpx15Nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8RUyT%2FbtsGp0eV7Wz%2FRIgVKPkuhykCV75tpx15Nk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;993&quot; height=&quot;720&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;결국 각각의 삼원칙은 상호보완적이지만, 무조건 다 선택할 수 있는 것이 아니라고 느껴졌다. 결국 자신의 상황에 맞춰서 어느 곳의 꼭지점이 약한 지를 먼저 파악하는 것이 중요하다고 생각한다. 만약 REP와 CRP를 우리 팀은 잘 한다고 해보자 이는 곧 너무 많은 컴포넌트의 수정이 이루어지지만, 각각의 모듈, 컴포넌트 혹은 클래스의 재 사용성이 높으며 릴리스 및 배포 주기도 적절하다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;클린 아키텍처를 읽으면서 드는 생각은 결국, 팀 혹은 회사에서 프로그램 혹은 코드가 이루어지는 개발 문화가 어떻게 이루어져 있는지 - 혹은 어떻게 이루어질 수 있는지 - 를 제시하고 이에 대한 판단 근거 혹은 기준을 제안해준 다는 점일 것 같다. 물론 이 문화다, 저 문화다를 딱 잘라서 말할 수 없다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;결국 코드도 프로그램도, 제작자 사용자 모두 인간이기에 모호하기 때문이다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Architecture</category>
      <category>CCP</category>
      <category>CRP</category>
      <category>REP</category>
      <category>개발자</category>
      <category>아키텍처</category>
      <category>코드</category>
      <category>코딩</category>
      <category>클린아키텍처</category>
      <category>프로그래밍</category>
      <category>프로그램</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/62</guid>
      <comments>https://kimkani.tistory.com/62#entry62comment</comments>
      <pubDate>Sun, 7 Apr 2024 10:21:49 +0900</pubDate>
    </item>
    <item>
      <title>Clean Architecture(클린 아키텍처) 공부 - OOP, SOLID, SRP(단일 책임 원칙), OCP(개방-폐쇄 원칙), LSP(리스코프 치환 원칙), ISP(인터페이스 분리 원칙), DIP(의존성 역전 원칙)</title>
      <link>https://kimkani.tistory.com/61</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTKMEV/btsGpL9YMB9/ecvBPiEnodwDykyiRqkHk1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTKMEV/btsGpL9YMB9/ecvBPiEnodwDykyiRqkHk1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTKMEV/btsGpL9YMB9/ecvBPiEnodwDykyiRqkHk1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTKMEV%2FbtsGpL9YMB9%2FecvBPiEnodwDykyiRqkHk1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;420&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. SRP(Single Responsibility Principle, 단일 책임 원칙)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 SOLID 원칙 중에서 그 의미가 가장 잘 전달되지 못한 원칙이라고 한다. 단 하나의 일만 해야한다는 원칙은 따로 있다고 하며 다음과 같이 설명한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단 하나의 일만 해야 한다는 원칙 : 함수는 반드시 하나의 일만 해야한다.&lt;/li&gt;
&lt;li&gt;커다란 함수를 작은 함수들로 리팩토링하는 저수준에서 사용.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 SRP에 대해 조금은 역사적인 흐름에 관련하여 간단하게 설명한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;역사적으로, 단일 모듈은 변경의 이유가 하나, 오직 하나 뿐이어야 한다.&lt;/li&gt;
&lt;li&gt;저자의 입맛으로, 하나의 모듈은 하나의, 오직 하나의 사용자 또는 이해관계자에 대해서만 책임져야 한다.&lt;/li&gt;
&lt;li&gt;최종 버전, 하나의 모듈은 하나의, 오직 하나의 액터에 대해서만 책임져야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 액터(Actor)와 응집된(cohesive)에 대해 언급이 된다. 이 둘에 대해 다음과 같이 적혀있다. &quot;단일 액터를 책임지는 코드를 함께 묶어 주는 힘이 바로 응집성이다&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 의미에서 스택 오버 플로우에 다양한 이야기가 오간 것을 알 수 있었다. 바로 SRP가 OOP에서 제 역할을 하냐는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1712394290367&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Does the Single Responsibility Principle work in OOP?&quot; data-og-description=&quot;I am struggling to understand how the Single Responsibility Principle can me made to work with OOP. If we are to follow the principle to a tee, then are we not left with many classes, many of whi...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/46541197/does-the-single-responsibility-principle-work-in-oop&quot; data-og-url=&quot;https://stackoverflow.com/questions/46541197/does-the-single-responsibility-principle-work-in-oop&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eKNxG/hyVMXd3i7Q/iGxFMkOXqFhvMWXx5K34YK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/46541197/does-the-single-responsibility-principle-work-in-oop&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/46541197/does-the-single-responsibility-principle-work-in-oop&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eKNxG/hyVMXd3i7Q/iGxFMkOXqFhvMWXx5K34YK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Does the Single Responsibility Principle work in OOP?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I am struggling to understand how the Single Responsibility Principle can me made to work with OOP. If we are to follow the principle to a tee, then are we not left with many classes, many of whi...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그에 대한 답변은 생각할 거리가 많아서 가져와 번역해 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;나는 SRP를 다음과 같이 설명하고 싶어: '너가 작성하는 모든 코드 - 모든 모듈, 클래스, 인터페이스 혹은 메소드는 하나의 일을 가지고 있어. 그것이 모든 일이 되고 그리고 오직 하나의 일이 되어야해.' 모듈과 같은 큰 코드 혹은 메소드 같은 작은 코드, 그리고 그 사이에 있는 클래스들을 작성할 때 이러한 것들은 모두 작은 것들로 이루어져 있으니깐 말이야.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일과 책임은 다양한 크기로 다가오고 계층적으로 분해될 수 있어. &amp;nbsp;예를 들어 경찰의 일은 '보호와 봉사'지만 이러한 일들은 '거리 순찰', '버모지 해결'같은 것들로 분해될 수 있고, 이러한 것들은 다른 유닛으로 다뤄질 수 있지. (중략)&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대해 질문자가 혼란스러운 부분이라고 말한다. 즉 정확하게 책임을 나누는 방법이나, 이것이 맞는 방법이냐고 느낄 수 있는 방법이 있는지 물어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대한 답변은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;span style=&quot;color: #000000;&quot;&gt;일 분할의 트레이드 오프: &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일을 더 작은 단위로 나누면 큰 일을 이해하기 쉬워지지만, 전체 작업의 수가 늘어나 시스템이 복잡해질 수 있어. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;각 작업의 솔루션을 편하게 머릿속에 떠올릴 수 있는 지점이 있지. 그 이상으로 나누면 변경 사항이 더 많은 곳에 영향을 미쳐 오히려 관리가 어려워져. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 절충점은 사람마다 약간씩 달라.&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, 어느정도로 새세하게 분리해야 하는 가는 결국 개발자의 몫이라는 것이다. 그리고 이러한 SRP에 대해 조금은 고통스러워 하는 글도 발견해서 가져와 보려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1712394755351&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;I don't love the single responsibility principle&quot; data-og-description=&quot;Did you ever happen to disagree with a colleague on the single responsibility principle and its application? Let's try to understand why that could be the case.&quot; data-og-host=&quot;www.sklivvz.com&quot; data-og-source-url=&quot;https://sklivvz.com/posts/i-dont-love-the-single-responsibility-principle&quot; data-og-url=&quot;https://www.sklivvz.com/posts/i-dont-love-the-single-responsibility-principle&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fkxkT/hyVMUhkuc3/CKCpuZuRkrghImuiVxNAG0/img.jpg?width=740&amp;amp;height=372&amp;amp;face=0_0_740_372,https://scrap.kakaocdn.net/dn/noj3j/hyVMWe9VTZ/nPjSFhokLH5xMycspW0Is0/img.jpg?width=740&amp;amp;height=372&amp;amp;face=0_0_740_372,https://scrap.kakaocdn.net/dn/m2BjO/hyVMQF0xKO/GgSl1CZ0Q6SNeYmdt1TXn0/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://sklivvz.com/posts/i-dont-love-the-single-responsibility-principle&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sklivvz.com/posts/i-dont-love-the-single-responsibility-principle&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fkxkT/hyVMUhkuc3/CKCpuZuRkrghImuiVxNAG0/img.jpg?width=740&amp;amp;height=372&amp;amp;face=0_0_740_372,https://scrap.kakaocdn.net/dn/noj3j/hyVMWe9VTZ/nPjSFhokLH5xMycspW0Is0/img.jpg?width=740&amp;amp;height=372&amp;amp;face=0_0_740_372,https://scrap.kakaocdn.net/dn/m2BjO/hyVMQF0xKO/GgSl1CZ0Q6SNeYmdt1TXn0/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;I don't love the single responsibility principle&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Did you ever happen to disagree with a colleague on the single responsibility principle and its application? Let's try to understand why that could be the case.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.sklivvz.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;글쓴이는 다음과 같은 이유를 들며 SRP에 대한 단점을 서술한다.&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;불명확 :&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버그 수정, 성능 향상, 리팩토링 등이 &quot;변경 이유&quot;에 명확하게 해당되는지 여부가 불분명하다는 점을 정확하게 지적한다. 이는 실제로 원칙을 적용하기 어렵게 만든다고 한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #0c0d0e; text-align: left;&quot;&gt;&quot;이유&quot; 또는 &quot;변경&quot;이 무엇을 구성하는지에 대한 설명이 없다. 버그 수정이 변경인지를 지적한다. 그렇다면 정말 버그가 변경의 타당한 이유인지도 지적한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #0c0d0e; text-align: left;&quot;&gt;하지만 책에서는 버그 수정을 변경으로 간주하지 않을 것 같다고 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #0c0d0e; text-align: left;&quot;&gt;&lt;span style=&quot;color: #0c0d0e; text-align: left;&quot;&gt;코드를 작성할 때는 현재의 실제 요구사항만이 중요하며, 미래는 거의 관련이 없으므로 미래 요구사항을 기반으로 설계하라고 요구하는 것은 어색한 부분이 있다고 한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #0c0d0e; text-align: left;&quot;&gt;&lt;span style=&quot;color: #0c0d0e; text-align: left;&quot;&gt;&lt;span style=&quot;color: #0c0d0e; text-align: left;&quot;&gt;클래스는 새로운 요구사항이 있을 때만 변경 이유를 가질 수 있다. 그러나 그때는 SRP를 위반하면 발생할 것으로 예상되는 나쁜 일들이 이미 일어다고 치면, 오직 사건이 일어난 후에 적용되는 원칙의 장점은 무엇인지를 반문한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모호함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우리가 오직 하나의 변경 이유만 식별한다고 해도, 이 원칙에는 좋은 이유나 나쁜 이유에 대한 개념이 없다.&lt;/li&gt;
&lt;li&gt;이 모든 경우가 이 원칙에 의해 허용되는 것처럼 보이거나, 또는 금지되는 것처럼 보입니다. 분명히 정의가 누락되어 있으며, 우리 모두가 무엇이 유효한 책임인지 동의하지 않으면 이 원칙은 작동하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;임의성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 SRP 원칙 자체가 임의적이다. 무엇이 &quot;한 개의 변경 이유&quot;를 항상 &quot;두 개의 변경 이유&quot;보다 더 낫게 만들까?&lt;/li&gt;
&lt;li&gt;숫자 '1'은 훌륭해 보이지만, 저는 단순한 것을 강력히 옹호하는 사람이며 때로는 변경 이유가 더 많은 클래스가 가장 단순한 것일 수 있다.&lt;/li&gt;
&lt;li&gt;우리가 너무 많은 다양한 작업을 수행하려는 거대 클래스를 만들어서는 안 된다는 것에는 동의합니다만, 왜 클래스가 한 가지 변경 이유만 가져야 하는가?&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;불균형
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;균형이 없다. 내가 보는 모든 예는 단일 메서드 클래스를 수백만 개 만드는 쪽으로 치우쳐 있다. 두 개의 클래스를 하나로 병합하는 방법에 대해서는 언급이 없습니다.&lt;/li&gt;
&lt;li&gt;거대 클래스를 만들지 말아야 한다는 전제에는 전적으로 동의하지만, 이 원칙은 개념을 설명하거나 명백히 문제가 있는 경우를 식별하는 데 전혀 도움이 되지 않는다.&lt;/li&gt;
&lt;li&gt;마찬가지로 활동이 거의 없는 빈약한 마이크로 클래스도 코드베이스를 구성하는 매우 복잡한 방법이다. &quot;이유&quot;에 대한 정의가 매우 좁다면 클래스가 여러 &quot;책임&quot;과 여러 변경 이유를 처리하는 것이 더 나을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2. OCP(Open-Close Principle, 개방-폐쇄 원칙)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;소프트웨어 개체 아티팩트는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 SRP와 DIP와 연결되어 있기도 하다. 즉, SRP의 &quot;서로 다른 목적으로 변경되는 요소를 적절하게 분리&quot;하는 것과, DIP의 &quot;이들 요소 사이의 의존성을 체계화&quot;하는 것이 합쳐진 것이다. 즉 책임의 분리와 소스코드 의존성도 확실히 조직화하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 파이썬 코드로 살펴보겠다.(출처 : &lt;a href=&quot;https://www.linkedin.com/pulse/solid-principles-python-open-closed-principle-mahdi-jafari/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.linkedin.com/pulse/solid-principles-python-open-closed-principle-mahdi-jafari/&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1712395644156&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from math import pi

class Shape:
    def __init__(self, shape_type, **kwargs):
        self.shape_type = shape_type
        if self.shape_type == &quot;rectangle&quot;:
            self.width = kwargs[&quot;width&quot;]
            self.height = kwargs[&quot;height&quot;]
        elif self.shape_type == &quot;circle&quot;:
            self.radius = kwargs[&quot;radius&quot;]

    def calculate_area(self):
        if self.shape_type == &quot;rectangle&quot;:
            return self.width * self.height
        elif self.shape_type == &quot;circle&quot;:
            return pi * self.radius**2i&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드의 큰 문제는 바로 &quot;새로운 형태 타입&quot;이 추가되면 어떻게 해야 하는가 이다. 즉 코드의 수정 범위가 __init__과 함께 calculate_area까지 뻗치게 된다. 이를 OCP의 원칙을 준수하는 형태로 고치면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1712395760672&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from abc import ABC, abstractmetho
from math import pi

class Shape(ABC):
    def __init__(self, shape_type):
        self.shape_type = shape_type

    @abstractmethod
    def calculate_area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        super().__init__(&quot;circle&quot;)
        self.radius = radius

    def calculate_area(self):
        return pi * self.radius**2

class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__(&quot;rectangle&quot;)
        self.width = width
        self.height = height

    def calculate_area(self):
        return self.width * self.height

class Square(Shape):
    def __init__(self, side):
        super().__init__(&quot;square&quot;)
        self.side = side

    def calculate_area(self):
        return self.side**2d&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Class Shape은 변경은 하지 않지만, 확장에는 유연하게 대처할 수 있으며 다른 이 추상 클래스 코드를 상속해서 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3. LSP(Liskov Substitution Principle, 리스코프 치환 원칙)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책의 내용이 너무 모호하고 설명이 불친절 해서 괜찮은 내용의 아티클을 가져와 봤다.&lt;/p&gt;
&lt;figure id=&quot;og_1712396098192&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Demystifying the Liskov Substitution Principle: A Guide for Developers&quot; data-og-description=&quot;What Liskov Substitution Principle (LSP) is? The Liskov Substitution Principle (LSP) is...&quot; data-og-host=&quot;dev.to&quot; data-og-source-url=&quot;https://dev.to/tkarropoulos/demystifying-the-liskov-substitution-principle-a-guide-for-developers-3gmm&quot; data-og-url=&quot;https://dev.to/tkarropoulos/demystifying-the-liskov-substitution-principle-a-guide-for-developers-3gmm&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/sREcU/hyVMREVaMw/rcQIvBJLyNR3gjSp4hPvU0/img.png?width=1000&amp;amp;height=500&amp;amp;face=93_389_128_428&quot;&gt;&lt;a href=&quot;https://dev.to/tkarropoulos/demystifying-the-liskov-substitution-principle-a-guide-for-developers-3gmm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dev.to/tkarropoulos/demystifying-the-liskov-substitution-principle-a-guide-for-developers-3gmm&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/sREcU/hyVMREVaMw/rcQIvBJLyNR3gjSp4hPvU0/img.png?width=1000&amp;amp;height=500&amp;amp;face=93_389_128_428');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Demystifying the Liskov Substitution Principle: A Guide for Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What Liskov Substitution Principle (LSP) is? The Liskov Substitution Principle (LSP) is...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dev.to&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1712395877794&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Liskov&amp;rsquo;s Substitution Principle (LSP)&quot; data-og-description=&quot;&amp;ldquo;Objects of super classes must be replaceable with the objects of its sub-classes without affecting the correctness.&amp;rdquo;&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@shashikantrbl123/liskovs-substitution-principle-lsp-ca9e218a7c54&quot; data-og-url=&quot;https://medium.com/@shashikantrbl123/liskovs-substitution-principle-lsp-ca9e218a7c54&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zSXAI/hyVJXmuCUU/OInEI9yg9CUWjWFWbLvUCk/img.jpg?width=1358&amp;amp;height=907&amp;amp;face=0_0_1358_907,https://scrap.kakaocdn.net/dn/cAvWvj/hyVJTK9SYl/ZVUlshDUL4puWKpFtFPrf0/img.jpg?width=1358&amp;amp;height=904&amp;amp;face=0_0_1358_904,https://scrap.kakaocdn.net/dn/SWy40/hyVJWOCTqQ/zzY1u0qiYi5YkadCCmz85k/img.jpg?width=1358&amp;amp;height=902&amp;amp;face=0_0_1358_902&quot;&gt;&lt;a href=&quot;https://medium.com/@shashikantrbl123/liskovs-substitution-principle-lsp-ca9e218a7c54&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@shashikantrbl123/liskovs-substitution-principle-lsp-ca9e218a7c54&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zSXAI/hyVJXmuCUU/OInEI9yg9CUWjWFWbLvUCk/img.jpg?width=1358&amp;amp;height=907&amp;amp;face=0_0_1358_907,https://scrap.kakaocdn.net/dn/cAvWvj/hyVJTK9SYl/ZVUlshDUL4puWKpFtFPrf0/img.jpg?width=1358&amp;amp;height=904&amp;amp;face=0_0_1358_904,https://scrap.kakaocdn.net/dn/SWy40/hyVJWOCTqQ/zzY1u0qiYi5YkadCCmz85k/img.jpg?width=1358&amp;amp;height=902&amp;amp;face=0_0_1358_902');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Liskov&amp;rsquo;s Substitution Principle (LSP)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Objects of super classes must be replaceable with the objects of its sub-classes without affecting the correctness.&amp;rdquo;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리스코프 치환 원칙은 상속의 행동에 초점을 맞추고 있다. 이는 한 클래스의 객체가 그 서브클래스의 객체로 대체될 수 있어야 한다고 명시하고 있다. 이를 통해 이 원칙은 하위 클래스가 상위 클래스에 충실하도록 강제한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하위 클래스는 상위 클래스 메서드의 행동을 증가시키거나 감소시키는 방식으로 수정할 수 있지만, 기본 행동에서 벗어날 수는 없다. 이런 방식으로 이 원칙은 부모 클래스를 자식 클래스로 대체함으로써 행동을 수정할 수 있는 힘을 제공할 뿐만 아니라, 그렇게 하는 경우에도 버그 없는 코드를 보장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;원칙 적용 방법&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LSP를 적용한다는 것은 상속을 그 본래의 범위 내에서 적용하는 것, 즉 자식 클래스가 부모 클래스의 모든 행동을 가져야 한다는 것을 의미한다. 상속 관계를 적용할 때 고려해야 할 몇 가지 사항은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&quot;is-a&quot; 관계&lt;/b&gt;: 클래스를 상속할 때, 자식 클래스가 부모 클래스와 &quot;is-a&quot; 관계를 보여야 한다. 만약 자식 클래스가 부모 클래스의 모든 메서드와 속성을 가질 수 없거나, 어떤 메서드나 속성의 기본 행동을 변경해야 한다면 상속 관계가 아닌 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;인터페이스&lt;/b&gt;: 때로는 클래스를 상속하는 것보다 인터페이스를 통해 그 메서드들을 사용하는 것이 현명할 수 있다. 이렇게 하면 불필요한 상속을 피할 수 있고 진정한 상속을 구현할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;다형성&lt;/b&gt;: 이 원칙을 사용하면 다형성을 달성할 수 있다. 즉, 자식 클래스들이 부모 클래스 메서드의 행동을 변경하지 않고도 같은 클래스의 다른 구현을 제공할 수 있고, 이 자식 클래스들의 객체들이 부모 클래스의 객체로 대체될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;재사용성과 모듈성: &lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LSP를&amp;nbsp;준수하면&amp;nbsp;파생&amp;nbsp;클래스를&amp;nbsp;기본&amp;nbsp;클래스&amp;nbsp;대신&amp;nbsp;원활하게&amp;nbsp;대체할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;이를&amp;nbsp;통해&amp;nbsp;코드&amp;nbsp;재사용성이&amp;nbsp;높아지며,&amp;nbsp;동일한&amp;nbsp;코드를&amp;nbsp;다양한&amp;nbsp;서브클래스에&amp;nbsp;적용할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;유지보수성과 유연성: &lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LSP 준수를 통해 코드베이스의 유지보수성과 유연성이 향상된다. LSP는 코드 중복을 줄이고, 일관되고 명확한 구조를 만들며, 기존 코드에 영향을 미치지 않으면서 파생 클래스에 대한 수정 및 추가를 가능하게 한다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #f5f4ef; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;4. ISP(Interface Segregation Principle, 인터페이스 분리 원칙)&lt;/h2&gt;
&lt;figure id=&quot;og_1712396266745&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;The difference between liskov substitution principle and interface segregation principle&quot; data-og-description=&quot;Is there any core difference between Liskov Substitution Principle (LSP) and Interface Segregation Principle (ISP)? Ultimately, both are vouching for designing the interface with common functionali...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/54480725/the-difference-between-liskov-substitution-principle-and-interface-segregation-p&quot; data-og-url=&quot;https://stackoverflow.com/questions/54480725/the-difference-between-liskov-substitution-principle-and-interface-segregation-p&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QBkdn/hyVMLEHyDW/GdI33kK5zcF4RSCoFTqODK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/54480725/the-difference-between-liskov-substitution-principle-and-interface-segregation-p&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/54480725/the-difference-between-liskov-substitution-principle-and-interface-segregation-p&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QBkdn/hyVMLEHyDW/GdI33kK5zcF4RSCoFTqODK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;The difference between liskov substitution principle and interface segregation principle&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Is there any core difference between Liskov Substitution Principle (LSP) and Interface Segregation Principle (ISP)? Ultimately, both are vouching for designing the interface with common functionali...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 ISP와 LSP의 차이에 대해서 많이 헷갈리긴 했는데 이에 대해 잘 정리해준 답변이 있어서 가져와 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;답변 1번&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSP: 하위 타입은 약속한 계약대로 준수해야 된다.&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;ISP: 호출자는 기반 타입의 인터페이스에서 필요 이상으로 의존하지 않아야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;만약 인터페이스 분리 원칙(ISP)을 적용한다면, 리시버의 전체 인터페이스가 아닌 일부분만을 사용하게 된다. 하지만 리스코프 치환 원칙(LSP)에 따르면, 리시버는 여전히 그 일부분 인터페이스를 준수해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;만약 ISP를 적용하지 않는다면, LSP를 위반할 유혹이 생길 수 있다. 왜냐하면 &quot;이 메서드는 중요하지 않으니 실제로 호출되지 않을 것&quot;이라고 생각할 수 있기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;답변 2번&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LSP(리스코프 치환 원칙): &lt;/b&gt;이 원칙은 모든 자식 클래스가 부모 클래스와 똑같은 동작을 가져야 한다고 요구한다. 예를 들어, Device 클래스가 있고 그 클래스에 callBaba() 함수가 있어서 아버지의 전화번호를 가져와 전화를 거는 기능을 한다고 합시다. 그러면 Device 클래스의 모든 하위 클래스에서 callBaba() 메서드가 똑같은 작업을 수행해야 한다. 만약 Device의 어떤 하위 클래스에서 callBaba() 메서드에 다른 동작이 포함되어 있다면, 이는 LSP를 위반하는 것을 의미한다.&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ISP&amp;nbsp;(인터페이스&amp;nbsp;분리&amp;nbsp;원칙):&lt;/b&gt; 서로 다른 책임에 대해서는 다른 인터페이스를 만들라고 요구한다. 다시 말해, 관련 없는 동작들을 하나의 인터페이스에 모아두지 말자. 이미 여러 책임을 가진 인터페이스가 있고, 구현자가 그 모든 것을 필요로 하지 않는다면 ISP를 위반하게 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;5. DIP(Dependency Inversion Principle, 의존성 역전 원칙)&lt;/h2&gt;
&lt;p style=&quot;color: #0c0d0e; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;DIP의 경우는 파이썬 코드로 한 번 보겠다. (출처 : &lt;a href=&quot;https://blog.nonstopio.com/dependency-inversion-principle-in-python-18bc0165e6f1&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.nonstopio.com/dependency-inversion-principle-in-python-18bc0165e6f1&lt;/a&gt;)&lt;/p&gt;
&lt;pre id=&quot;code_1712396756376&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Logger:
    def log(self, message):
        with open('log.txt', 'a') as f:
            f.write(message + '\n')

class Calculator:

    def __init__(self):
        self.logger = Logger()

    def add(self, x, y):
        result = x + y
        self.logger.log(f&quot;Added {x} and {y}, result = {result}&quot;)
        return result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시에서는 Calculator(계산기) 클래스가 로거 클래스에 의존하고 있다. 이는 DIP를 위배한다. 왜냐하면 계산기 클래스가 저 수준 모듈에 의존하고 있기 때문이다. 이를 고치면 다음과 같이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1712396847411&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from abc import ABC, abstractmethod

class LoggerInterface(ABC):
    @abstractmethod
    def log(self, message):
        pass

class Logger(LoggerInterface):
    def log(self, message):
        with open('log.txt', 'a') as f:
            f.write(message + '\n')

class Calculator:
    def __init__(self, logger: LoggerInterface):
        self.logger = logger

    def add(self, x, y):
        result = x + y
        self.logger.log(f&quot;Added {x} and {y}, result = {result}&quot;)
        return result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;log&lt;span style=&quot;text-align: start;&quot;&gt; 메서드를 정의하는 &lt;/span&gt;LoggerInterface&lt;span style=&quot;text-align: start;&quot;&gt;라는 추상 클래스를 만들었다. 또한 &lt;/span&gt;Calculator&lt;span style=&quot;text-align: start;&quot;&gt; 클래스를 수정하여 생성자에서 &lt;/span&gt;LoggerInterface&lt;span style=&quot;text-align: start;&quot;&gt; 객체를 의존성으로 받도록 설정했다. 이렇게 함으로써 &lt;/span&gt;Calculator&lt;span style=&quot;text-align: start;&quot;&gt; 클래스는 &lt;/span&gt;Logger&lt;span style=&quot;text-align: start;&quot;&gt; 클래스의 구체적인 구현체에 의존하는 것이 아니라 추상화에 의존하게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Architecture</category>
      <category>DIP</category>
      <category>ISP</category>
      <category>lsp</category>
      <category>OCP</category>
      <category>OOP</category>
      <category>SOLID</category>
      <category>SRP</category>
      <category>객체</category>
      <category>객체지향프로그래밍</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/61</guid>
      <comments>https://kimkani.tistory.com/61#entry61comment</comments>
      <pubDate>Sat, 6 Apr 2024 18:51:34 +0900</pubDate>
    </item>
    <item>
      <title>Clean Architecture(클린 아키텍처) 공부 - 설계, 아키텍처, 구조적, 객체 지향, 함수형 프로그래밍, 트랜잭션, OOP, 캡슐화, 추상화</title>
      <link>https://kimkani.tistory.com/60</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lk0TC/btsGcc7ZsHU/hltIHLZI5OkRZEFufnDqa1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lk0TC/btsGcc7ZsHU/hltIHLZI5OkRZEFufnDqa1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lk0TC/btsGcc7ZsHU/hltIHLZI5OkRZEFufnDqa1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLk0TC%2FbtsGcc7ZsHU%2FhltIHLZI5OkRZEFufnDqa1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;720&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설계와 아키텍처란?&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설계 : 저수준의 구조 또는 결정사항 등을 의미&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아키텍처 : 저수준의 세부사항과는 분리된 고수준의 무언가&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 부분은 애매했다. 무언가 설계와 아키텍처를 분리하면서도 책에서는 둘의 경계는 무관하다고 했다. 하지만 내 안의 의문은 계속해서 이 둘의 차이가 마음에 걸렸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1711716789911&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;What&amp;rsquo;s the difference between software architecture and design?&quot; data-og-description=&quot;Building software is a complicated process made up of many different parts. Among them are developing software architecture and design&amp;hellip;&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@concisesoftware/whats-the-difference-between-software-architecture-and-design-b705c2584631&quot; data-og-url=&quot;https://medium.com/@concisesoftware/whats-the-difference-between-software-architecture-and-design-b705c2584631&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bEo0AK/hyVGHXAmNg/Afcy4CHzlIf6dV2V9DPFok/img.jpg?width=1200&amp;amp;height=800&amp;amp;face=0_0_1200_800,https://scrap.kakaocdn.net/dn/fkaLM/hyVGMEzedL/yKxYcLNmUYzkw6MCmEfAp1/img.jpg?width=1358&amp;amp;height=906&amp;amp;face=0_0_1358_906,https://scrap.kakaocdn.net/dn/ry4Rb/hyVGRsnX0E/wseqf2rDJOI5U5SpIG9fMK/img.jpg?width=1358&amp;amp;height=906&amp;amp;face=0_0_1358_906&quot;&gt;&lt;a href=&quot;https://medium.com/@concisesoftware/whats-the-difference-between-software-architecture-and-design-b705c2584631&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@concisesoftware/whats-the-difference-between-software-architecture-and-design-b705c2584631&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bEo0AK/hyVGHXAmNg/Afcy4CHzlIf6dV2V9DPFok/img.jpg?width=1200&amp;amp;height=800&amp;amp;face=0_0_1200_800,https://scrap.kakaocdn.net/dn/fkaLM/hyVGMEzedL/yKxYcLNmUYzkw6MCmEfAp1/img.jpg?width=1358&amp;amp;height=906&amp;amp;face=0_0_1358_906,https://scrap.kakaocdn.net/dn/ry4Rb/hyVGRsnX0E/wseqf2rDJOI5U5SpIG9fMK/img.jpg?width=1358&amp;amp;height=906&amp;amp;face=0_0_1358_906');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;What&amp;rsquo;s the difference between software architecture and design?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Building software is a complicated process made up of many different parts. Among them are developing software architecture and design&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 글에서 어느정도 내가 고민하던 - 혹은 의문을 품었던 - 부분을 해결해 주었다. 정리하자면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소프트웨어 설계&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소프트웨어 설계는 시스템을 구성하는 서로다른 요소들에 탐구하는 계획이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설계 플랜은 개발 프로세스를 통해 가치있는 참고 자료로 여겨질 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설계 플랜은 &quot;요구사항 분석&quot;, &quot;위험 분석&quot; 그리고 &quot;도메인 분석&quot;을 필요로한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소프트웨어 설계는 요구사항을 실체로 바꾸는 개발자들을 도와준다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소프트웨어 아키텍처&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소프트웨어 시스템의 청사진. 각각의 컴포넌트들이 소통 및 협력하도록 설정하고 시스템의 복잡도를 다룰 수 있게 해준다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;소프트웨어 아키텍처는 비즈니스와 기술적 요소들이 만나는 지점, 구조화된 해결책이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 아키텍처의 목표는 어플리케이션의 구조에 영향을 미치는 요구사항을 명확하게 하는 것에 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;차이와 관계&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아키텍처는 시스템의 구조를 보여주면서 세부 구현을 숨겨주며, 시스템 구성 요소가 다른 요소와 상호작용하도록 도와준다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설계는 반대로 시스템의 실행에 집중하며, 고려가능한 세부사항을 탐구하기도 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개발 팀은 가끔씩 소프트웨어 개발보다 설계에 혹은 그 반대에 집중하기도 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결국 이 둘 사이에 선을 긋는 것은 개발자의 몫이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결국 이 둘 사의 관계는 상호 보완적이지만, 이 둘 사이의 적절한 템포 혹은 선을 긋는 것도 필요하다는 내용이다. 내게 있어서는 설계들이 모여 하나의 컴포넌트를 이루고 그 컴포넌트(구성 요소)들이 모여 하나의 아키텍처를 구성하는 것처럼 느껴졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로그래밍 패러다임&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 책에서는 구조적 프로그래밍, 객체 지향 프로그래밍, 그리고 함수형 프로그래밍을 차례차례로 소개한다. 나는 그 전에 가장 인상적인 구문이 있어서 소개하려 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;패러다임은 프로그래머에게 새로운 권한을 주는게 아니라 하면 안될것을 정의하고 할 수 있는것을 박탈한다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;즉 무엇을 해야 할지를 말하기 보다는 무엇을 해서는 안되는 지를 말해준다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 각 패러다임을 소개하자면.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구조적 프로그래밍 : 최초로 적용된 프로그래밍, 다익스트라가 발견, goto문의 해로움에 대해 논함. 제어 흐름의 직접적인 전환에 대해 규칙을 부과.&lt;/li&gt;
&lt;li&gt;객체지향 프로그래밍 : ALGOL의 함수 호출 스택 프레임을 힙으로 옮기면 지역변수가 오랫동안 유지되는 것에서 시작. 제어 흐름의 간접적인 전환에 대해 규칙을 부과&lt;/li&gt;
&lt;li&gt;함수형 프로그래밍 : 불변성, 할당문에 대해 규칙을 부과&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사실 이 말이 왜 나왔을까 고민하다가, 예전에 봤던 C언어의 유명한 구문이 생각났다. 정확히는 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;C99 Rationale&lt;/a&gt;에 나온 말이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-29 오후 10.11.14.png&quot; data-origin-width=&quot;2596&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bacYYj/btsGdCkvHs5/DMWky1Y60UAlQdggIMlOBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bacYYj/btsGdCkvHs5/DMWky1Y60UAlQdggIMlOBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bacYYj/btsGdCkvHs5/DMWky1Y60UAlQdggIMlOBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbacYYj%2FbtsGdCkvHs5%2FDMWky1Y60UAlQdggIMlOBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2596&quot; height=&quot;956&quot; data-filename=&quot;스크린샷 2024-03-29 오후 10.11.14.png&quot; data-origin-width=&quot;2596&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start; color: #000000;&quot;&gt;&lt;b&gt;C의 정신을 지키라.&lt;/b&gt; C89 위원회는 C의 전통적인 정신을 보존하는 것을 주요 목표로 삼았습니다. C의 정신에는 많은 측면이 있지만, 그 본질은 C 언어가 기반하고 있는 15가지 근본 원칙에 대한 커뮤니티의 정서입니다. C의 정신의 일부 측면들은 &quot;간결함&quot;, &quot;이식성&quot;, &quot;효율성&quot; 등의 문구로 요약될 수 있습니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래머를 신뢰하라.&lt;/li&gt;
&lt;li&gt;프로그래머가 필요한 일을 하는 것을 막지 말라.&lt;/li&gt;
&lt;li&gt;언어를 작고 단순하게 유지하라.&lt;/li&gt;
&lt;li&gt;실행에는 한가지 방법만 제공하라.&lt;/li&gt;
&lt;li&gt;이식성이 보장되지 않더라도 빠른 실행 속도를 지향하라.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 위의 첫번째 말이 제일 현 세대의 다양한 패러다임의 탄생에 기여를 했다고 생각한다. 즉 &quot;프로그래머를 신뢰&quot;하지 못해서 하나씩 권한을 빼앗은 것이 아닐까 하는 생각이 들었다. 이는 사실 패러다임뿐 만이 아니라 언어에도 영향을 주었다고 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 Python, JAVA, JavaScript, GO, Rust 등등의 탄생은 프로그래머를 신뢰하지 못해 - 사실 신뢰하지 못하기 보다는 휴먼 에러의 방지에 가까울 것 같다 - 일어난 현상이 아닐까 조심스레 추측해 본다. 결국은 더 안전한 프로덕트를 만들기 위한 방향으로 진화한 것이 아닐까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;객체 지향 프로그래밍(OOP, Object Oriented Programming)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;캡슐화와 추상화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동차를 생각해보자, 자동차가 달려가고 있다. 그 안에는 수많은 움직이는 부품들이 있고, 작게는 기어부터 시작해 톱니바퀴까지 움직인다. 우리는 이 자동차 내에 수많은 움직임이 다른 움직임을 만들어 내고, 상호작용하고 있다는 것을 알 수 있다. 그와 동시에, 차체를 보자, 차체는 크게보면 디자인이 있지만, 그 안에는 다양한 프레임과 함께 견골들이 존재한다. 우리는 자동차에 엔진이 있다는 것을 알고 배터리부터 다양한 부품이 있다는 것을 안다.&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단순하게 말해서, 캡슐화는 &quot;어떠한 정보 혹은 데이터의 은닉&quot;이다. 추상화는 &quot;어떠한 실행 혹은 행동의 은닉&quot;이다.&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;상속&lt;/h3&gt;
&lt;pre id=&quot;code_1711718585589&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Country:
    name = '국가명'
    population = '인구'
    capital = '수도'

    def show(self):
        print('국가 클래스의 메소드입니다.')


class Korea(Country):
    def __init__(self, name):
        self.name = name
        
    def show_name(self):
        print('국가 이름은 : ', self.name)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속은 책에서 너무 효과적으로 설명했다. &quot;어떤 변수와 함수를 하나의 유효 범위로 묶어 재정의&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다형성 및 의존성 역전 원칙&lt;/h3&gt;
&lt;pre id=&quot;code_1711718861355&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BaseUserRepository(ABC):
    def get_user_all(self, db: Session) -&amp;gt; List[User]:
        pass


class UserRepository(BaseUserRepository):
    async def get_user_all(self, db: Session) -&amp;gt; Optional[List[User]]:

        query = await db.execute(select(UserModel))
        result = query.scalars().all()

        if result:
            return result
        return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다형성은 결국 다양한 형태, 즉 하나의 객체가 다른 여러 가지 타입을 가질 수 있음을 의미한다. 왜 이런 다형성이 필요한 것일까? 결국 개발의 용이함, 유지 보수의 기회비용을 줄이기 위함이 아닐까 생각한다. &lt;span style=&quot;color: #000000;&quot;&gt;기존 코드 자체에 이미 고수준 - 위의 코드에서는 BaseUserRepository - 객체로 동작에 초점을 맞춰 구현했기 때문에, 저수준 - 위의 코드에서는 BaseUserRepository를 상속하는 UserRepository - 객체를 변경해주면 원래 코드 수정 없이 변경이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 이러한 다형성 원칙은 곧 의존성 역전 원칙으로 이어진다. &lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;의존성 역전을 위해 추상화(인터페이스)를 개발하고 외부 코드가 인터페이스에 의존적이도록 해야한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;함수형 프로그래밍(FP, Functional Programming)&lt;/h2&gt;
&lt;div style=&quot;color: #e6edf3; text-align: start;&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;불변성과 아키텍처&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;경쟁 조건, 교착, 동시성 문제는 가변 변수로 인해 발생&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 모든 변수를 불변으로 유지 불가능, 여기서 가변성의 분리가 등장&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div style=&quot;color: #e6edf3; text-align: start;&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가변성의 분리&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a id=&quot;user-content-가변성의-분리&quot; style=&quot;color: #000000;&quot; href=&quot;https://github.com/orgs/FrontendStudySeoul/dashboard#%EA%B0%80%EB%B3%80%EC%84%B1%EC%9D%98-%EB%B6%84%EB%A6%AC&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e6edf3; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;내부 서비스를 가변 컴포넌트와 불변 컴포넌트로 분리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;불변 컴포넌트는 변수의 상태를 변경할 수 있는 다른 컴포넌트와 통신&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가변 컴포넌트는 최소한으로 해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;color: #e6edf3; text-align: start;&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이벤트 소싱&lt;/span&gt;&lt;/h3&gt;
&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a id=&quot;user-content-이벤트-소싱&quot; style=&quot;color: #000000;&quot; href=&quot;https://github.com/orgs/FrontendStudySeoul/dashboard#%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%86%8C%EC%8B%B1&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e6edf3; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;저장 공간과 처리 능력만 충분하면 완전히 불변성을 만들 수 있다. 그리고 완전한 함수형이 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;저장과 처리공간에 대한 제약이 사라지면서, 실제 데이터를 변경하는 것이 아닌, 검색 및 삽입만 하는 방식이 등장&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이벤트 소싱은 상태가 아닌 트랜잭션을 저장하여 상태가 필요해지는 순간 초기 상태에서 모든 실행된 트랜잭션을 추적하여 현재 값을 파악&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;트랜잭션이란?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #e6edf3; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot;&gt;트랜잭션이란, 데이터베이스에서 이루어지는 연속되는 여러 실행 단위를 묶어둔 것이라고 생각하면 된다. 그리고 각각은 하나의 논리적인 작업의 유닛으로 제공된다. 예를들어 설명해보겠다. A라는 유저가 B라는 유저에게 500만원을 보낸다고 해보자. 그러면 다음과 같은 실행이 일어난다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #444444; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #444444; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;(데이터베이스 트랜잭션의 시작이라고 불리는) 유저 A에게서 B에게 500만원을 보낸다는 기록을 만든다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;유저 A에게서 잔고를 확인한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;유저 A의 잔고에서 500만원을 뺀다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;유저 B의 잔고를 읽는다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;유저 B의 잔고에다가 500만원을 추가한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 이러한 트랜잭션을 하나의 원자성(하나의 깨지지 않는) 유닛으로 처리하고 만약 시스템이 이러한 트랜잭션을 도중에 실패하면, 트랜잭션은 완료되지 않은 상태에서 기존의 상태로 돌아간다. 일반적으로 롤백(rollback)이라는 단어는 트랜잭션을 통해 만들어진 어느 변화든 미완료 상태에 놓인 프로세스를 되돌리는 것을 말한다. 커밋(commit)이라는 단어는 트랜잭션을 통해 영구적인 변화가 이루어진 경우를 언급한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE0K4J/btsGfiLM55H/PdH1NMDwZgkJkCojaQnzmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE0K4J/btsGfiLM55H/PdH1NMDwZgkJkCojaQnzmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE0K4J/btsGfiLM55H/PdH1NMDwZgkJkCojaQnzmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE0K4J%2FbtsGfiLM55H%2FPdH1NMDwZgkJkCojaQnzmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;418&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #444444; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;활성 상태(Active) - 트랜잭션의 첫번째 상태이다. 트랜잭션의 명령어(읽고 쓰는 작업)이 실행되는 동안 활성되어 있다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;일부 커밋된 상태(Partially Commited) - 이 상태로 변한 경우 데이터베이스는 아직 디스크에 데이터를 커밋하지 않은 상태이다. 이러한 상태의 경우 메모리 버퍼에 있는 경우이며, 이 버퍼에 있는 데이터가 아직 쓰이지 않은 경우이다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;커밋된 상태(Committed) - 이 상태의 경우 데이터베이스에 영구적으로 저장된 상태이다. 그렇기에, 이 상태 이전으로 롤백은 불가능하다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;실패(Failed) - 만약 일부 커밋된 상태라든지, 활성 상태에서 트랜잭션이 거부되거나 실패한 경우 실패 상태에 진입한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;종료(Terminated) - 마지막 상태로 데이터베이스 트랜잭션의 라이프 사이클의 최종 단계이다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Architecture</category>
      <category>OOP</category>
      <category>python</category>
      <category>객체지향</category>
      <category>공부</category>
      <category>데이터베이스</category>
      <category>설계</category>
      <category>아키텍처</category>
      <category>트랜잭션</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/60</guid>
      <comments>https://kimkani.tistory.com/60#entry60comment</comments>
      <pubDate>Fri, 29 Mar 2024 22:45:39 +0900</pubDate>
    </item>
    <item>
      <title>Domain Driven Design for FastAPI(파이썬 FastAPI를 위한 도메인 주도 디자인) - Layered Architecture(계층적 구조)</title>
      <link>https://kimkani.tistory.com/59</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1611&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/odYU1/btsF9H6wB7H/8fosJlvzysCVFmTtZE3hT0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/odYU1/btsF9H6wB7H/8fosJlvzysCVFmTtZE3hT0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/odYU1/btsF9H6wB7H/8fosJlvzysCVFmTtZE3hT0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FodYU1%2FbtsF9H6wB7H%2F8fosJlvzysCVFmTtZE3hT0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;617&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1611&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Domain Driven Design이나 Hexagonal Architecture를 공부하다 보면, 이 둘의 차이가 무엇인지 궁금해진다. 가끔씩 둘이 같은 느낌이 들면서도 차이가 있는 듯 한 헷갈림을 얻고 간다. 그래서 오늘은 이 내용을 공부하면서 느낀 것들을 정리해 보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Layered Architecture(계층적 구조)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-27 오후 9.10.55.png&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G1Tll/btsF7LCh4PK/0ywEpcCm5NfeNNn9KJIsf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G1Tll/btsF7LCh4PK/0ywEpcCm5NfeNNn9KJIsf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G1Tll/btsF7LCh4PK/0ywEpcCm5NfeNNn9KJIsf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG1Tll%2FbtsF7LCh4PK%2F0ywEpcCm5NfeNNn9KJIsf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;671&quot; data-filename=&quot;스크린샷 2024-03-27 오후 9.10.55.png&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계층적 구조는 응용 프로그램들을 - Application이지만, 이에 대한 정확한 기준은 없는 듯 하다 - 분리된 레이어로 각각 분리하는 것을 뜻한다. 각각의 레이어는 Presentation(표현 계층), Application(응용 계층), Domain Layer(도메인 계층) 그리고 Infrat Layer(인프라 계층)으로 분리하며 각각의 역할은 다음과 같다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1711542327058&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


app = FastAPI()


@app.post(&quot;/items/&quot;)
async def create_item(item: Item):
    return item&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Presentation Layer&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유저 인터페이스나 표현에 관한 로직과 관련된 패키지 혹은 모듈들을 포함한다&lt;/li&gt;
&lt;li&gt;대표적으로 컨트롤러라 뷰, 뷰모델 등이 될 것이다&lt;/li&gt;
&lt;li&gt;대표적으로 FastAPI에서는 Pydantic으로 정의한 Schema인 Request, Response Model등이 있을 것이다.&lt;/li&gt;
&lt;li&gt;위의 코드에서는 Item 클래스가 대표적인 예시일 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;pre id=&quot;code_1711542210591&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def create_user(self, password: str, email: str, name: str, db: Session):
    query_result = await self.user_repo.get_user_by_email(email=email, db=db)

    if query_result:
        return None
        
    hashed_password = self.password_service.get_password_hash(password=password)
    result = await self.user_repo.create_user(
        email=email,
        hashed_password=hashed_password,
        name=name,
        db=db,
    )

    return result&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Application Layer
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 부분은 트랜잭션 관리, DTO의 변환 그리고 도메인 객체의 생성과 접근이 잇다.&lt;/li&gt;
&lt;li&gt;위의 대부분 파라메터는 유저 혹은 API가 받은 Value Object이다. 왜냐하면 API에서는 Domain 객체를 모르기 때문이다.&lt;/li&gt;
&lt;li&gt;대표적으로 Service라고 칭하는 파일 이름들이 여기에 해당하는 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1711542624251&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import datetime
from pydantic import BaseModel, EmailStr
from dataclasses import dataclass

#FastAPI에서 이 정도를 Entity라고 생각하고 싶다.
class User(BaseModel):
    id: int
    name: str
    email: EmailStr
    password: str
    created_at: datetime.datetime
    updated_at: datetime.datetime

#파이썬에서 이 정도를 VO라고 생각하고 싶다.
@dataclass(frozen=True)
class User:
    name: str
    email: str
    password: str&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Domain Layer
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인 계층에는 2가지를 주되게 보면 된다고 생각한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Value Objects : 생명주기가 없는 데이터, 변하지 않는 데이터&lt;/li&gt;
&lt;li&gt;Entities : 생명주기가 있는 데이터, 이력이 생기는 데이터, 즉 데이터베이스와 1:1로 매핑되는 클래스&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;VO에 대한 설명부터 시작하겠다
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;VO의 경우 값이 같으면 같은 것으로 친다. 즉 동등성을 가진다.&lt;/li&gt;
&lt;li&gt;VO의 경우 변하지 않는다. 동시성을 가질 수 있게 해주며, 불변성을 띄는 것이다. 한 번 만들어지면 수정될 수 없다&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Entity에 대한 설명을 하겠다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Entity의 경우 DB와 관련이 크다. 즉 id - 혹은 primary key값 - 이 같아야 똑같다. VO와의 차이는 즉, 식별 가능성이 값을 통해서 나는지 아닌지의 차이일 것 같다.&lt;/li&gt;
&lt;li&gt;결국 DB와의 종속성 차이이다. 이 데이터가 직접적으로 DB에 영향을 미치는가의 여부라고 생각한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;pre id=&quot;code_1711543988350&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserRepository(BaseUserRepository):
    async def get_user_all(self, db: Session) -&amp;gt; Optional[List[User]]:

        query = await db.execute(select(UserModel))
        result = query.scalars().all()

        if result:
            return [self.ConvertToUser(res) for res in result]
        return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Infra Layer
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 데이터베이스와 가까운 계층이다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;관련된 데이터에 접근하는 어댑터와 교환이 이루어지는 곳이다.&lt;/li&gt;
&lt;li&gt;결론적으로 데이터베이스와 연동하는 구간이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-03-27 오후 9.58.01.png&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7wUWM/btsF8wkkqgQ/zwZKmJthEPX6KrmhvWYhak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7wUWM/btsF8wkkqgQ/zwZKmJthEPX6KrmhvWYhak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7wUWM/btsF8wkkqgQ/zwZKmJthEPX6KrmhvWYhak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7wUWM%2FbtsF8wkkqgQ%2FzwZKmJthEPX6KrmhvWYhak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;554&quot; height=&quot;577&quot; data-filename=&quot;스크린샷 2024-03-27 오후 9.58.01.png&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 정리해 보자면, 가장 밑에 데이터베이스 및 영속성 계층이 있으며, 그 위에 비즈니스 계층이 있고 그러한 비즈니스 계층에서 도메인 로직이 이루어진다. 가장 큰 단점이면서 장점이라고 생각하는 것이 바로 밑으로 내려가면서 종속성을 띄는 것이다. 위로 거슬러 올라가기 힘들다는 단점이자 장점이 있다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 단점부터 말해보려고 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;성능 부하&lt;/b&gt; : 계층 간의 통신은 데이터 전환이 필연적으로 이루어진다. 이는 결국 부하를 줄 수 있고 전체 시스템에 속도와 반응성에 영향을 줄 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;변경 어려움&lt;/b&gt; : 다만 이러한 계층 구조를 엄격하게 지키려고 하면, 한 계층의 변경 사항은 다른 계층에 필연적으로 영향을 준다. 유연성이 떨어질 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 및 로직의 중복&lt;/b&gt; : 계층의 관심사 - 즉 도메인의 분리 - 를 위해 데이터나 로직을 중복할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 장점이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #29261b; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;모듈성 및 관심사 분리:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;각 계층은 특정 기능 측면에 초점을 맞추므로 모듈성이 향상된다. 이러한 관심사 분리로 인해 시스템을 더 쉽게 이해, 유지 관리 및 업데이트할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재사용성: &lt;/b&gt;계층 간의 명확한 경계로 인해 코드 재사용이 용이해진다. 공통 기능은 레이어에서 한 번만 구현되어 다른 레이어에서 활용될 수 있으므로 개발 시간과 노력을 절약할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;확장성:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;각 레이어는 수요에 따라 독립적으로 확장될 수 있다. 예를 들어, 애플리케이션 트래픽이 증가하면 비즈니스 로직이나 데이터 레이어에 영향을 주지 않고 프레젠테이션 레이어만 확장할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유연성:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;레이어는 잘 정의된 인터페이스를 통해 상호 작용하므로, 시스템의 나머지 부분에 영향을 주지 않고 레이어를 수정하거나 교체할 수 있다. 이러한 유연성은 새로운 기술을 도입하거나 구성 요소를 업그레이드할 때 특히 유용하다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Architecture</category>
      <category>IT</category>
      <category>개발</category>
      <category>개발자</category>
      <category>공부</category>
      <category>도메인주도개발</category>
      <category>백엔드</category>
      <category>서버</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <category>프로그램</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/59</guid>
      <comments>https://kimkani.tistory.com/59#entry59comment</comments>
      <pubDate>Wed, 27 Mar 2024 22:05:12 +0900</pubDate>
    </item>
    <item>
      <title>[번역] 개발자를 위한 멘탈 케어 방법 6가지</title>
      <link>https://kimkani.tistory.com/58</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1710405319994&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;What are the best ways for software developers to maintain their mental health?&quot; data-og-description=&quot;Learn some tips to cope with the stress and pressure of coding without compromising your well-being. Boost your mood, confidence, and creativity as a software developer.&quot; data-og-host=&quot;www.linkedin.com&quot; data-og-source-url=&quot;https://www.linkedin.com/advice/1/what-best-ways-software-developers-maintain-gjege&quot; data-og-url=&quot;https://www.linkedin.com/advice/1/what-best-ways-software-developers-maintain-gjege&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bqFhIX/hyVxrImqsv/CWq7d0UtRTjP1CtJAlLKOK/img.png?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/XtSXn/hyVAMD6i1x/y38L5IVof8bbNBAZ9H1uJ1/img.png?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot;&gt;&lt;a href=&quot;https://www.linkedin.com/advice/1/what-best-ways-software-developers-maintain-gjege&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.linkedin.com/advice/1/what-best-ways-software-developers-maintain-gjege&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bqFhIX/hyVxrImqsv/CWq7d0UtRTjP1CtJAlLKOK/img.png?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/XtSXn/hyVAMD6i1x/y38L5IVof8bbNBAZ9H1uJ1/img.png?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;What are the best ways for software developers to maintain their mental health?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn some tips to cope with the stress and pressure of coding without compromising your well-being. Boost your mood, confidence, and creativity as a software developer.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.linkedin.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;본 글은 위의 글을 번역한 글입니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0895.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXASjn/btsFOe4EEkv/A43iUVN8So6aTgWbFBGnLk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXASjn/btsFOe4EEkv/A43iUVN8So6aTgWbFBGnLk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXASjn/btsFOe4EEkv/A43iUVN8So6aTgWbFBGnLk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXASjn%2FbtsFOe4EEkv%2FA43iUVN8So6aTgWbFBGnLk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-filename=&quot;IMG_0895.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;1. 현실적인 목표를 설정하자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 소프트웨어 개발자들이 겪는 스트레스의 일반적인 원인은 너무 많은 것을 너무 짧은 시간에 성취하려고 해서이다. 여러분이 개인 프로젝트, 프리랜서 작업 혹은 팀 태스크를 하든, 현실적이고 성취 가능한 목표를 여러분의 스킬, 리소스 그리고 기한에 맞춰서 설정해야 한다. 프로젝트를 작고 관리 가능한 덩어리로 쪼개고, 가장 중요한 기능에 순위를 둔 뒤 진행을 주기적으로 체크해야 한다. 이 방법으로 여러분은 압도되거나, 좌절하거나, 혹은 번 아웃을 일에서 느끼는 것을 피할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 개발자들의 의견
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;빡빡한 스케쥴 혹은 작업 사이에서 주기적인 휴식을 취해주세요.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;가끔씩, 개발자는 완벽주의라는 함정에 빠지길 마련입니다. 개발자는 모든 것을 완벽하게 하려고 하며 시간이 지남에 따라 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;기한이 오기 중간 전까지&lt;/span&gt; 첫번째 여러 PR을 엽니다. 이는 남은 작업들을 해내는데 엄청난 스트레스를 주며 많은 불안감을 줍니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;인생은 마라톤이야. 장기적으로 건강은 승진으로 얻는 이득보다 중요해요.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2. 휴식을 취하세요, 쉬러가세요&lt;/h2&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div data-tracking-prefix=&quot;article-ssr-frontend-x-article&quot; data-total-count=&quot;168&quot; data-test-id=&quot;article-segment&quot; data-thread-urn=&quot;urn:li:articleSegment:(urn:li:linkedInArticle:7117939159297572864,7117939160945963008)&quot; data-is-logged-in=&quot;true&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;content-urn_li_articleSegment__urn_li_linkedInArticle_7117939159297572864_7117939160945963008_&quot;&gt;
&lt;div style=&quot;color: #000000;&quot; data-test-id=&quot;publishing-text-block&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;또 다른 정신 건강을 위한 주요 요인은 규칙적으로 휴식을 취하는 것이다. 중단 없이 오랜 시간 동안 코딩하면 피로, 권태, 집중력 저하를 초래할 수 있다. 뇌와 신체에 휴식과 재충전의 시간을 주어야 한다. 근무 시간 중에 스트레칭, 산책, 명상 등 짧은 휴식 시간을 계획하세요. 또한 충분한 수면을 취하는 것이 중요하다. 수면 부족은 인지 기능, 기분, 건강에 악영향을 줄 수 있다. 그리고 일 외에 행복과 만족감을 주는 취미 활동, 여가 활동 또는 사회적 교류를 즐기는 것을 잊지 마세요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 개발자들의 의견
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;최고의 정신적 능력을 발휘하려면 휴식과 회복이 필수적입니다. 번아웃되기 전에 휴식을 취하세요. 45-60분마다 5-10분 정도의 휴식 시간을 가져 정신을 가다듬으세요. 산책을 가거나 간식을 먹거나 스트레칭을 하세요. 업무 중 휴식 시간에는 외출해 신선한 공기를 마시고 햇빛을 쬐며 정신적 여유를 가지세요. 5분만으로도 도움이 됩니다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;마라톤 코딩 세션을 피하세요. 계획된 휴식과 함께 집중적인 작업 단위로 일하면서 집중력을 유지하세요. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;뇌가 재충전될 수 있도록 하루 7-9시간의 양질의 수면을 우선시하세요. 수면 부족은 집중력을 저하시킵니다.&lt;/span&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 에러와 피드백에 대처하는 법을 배우세요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;소프트웨어 개발은 시행착오, 디버깅, 피드백이 수반되는 과정이다. 코드에 오류, 버그 또는 실패가 발생하거나 클라이언트, 사용자 또는 동료로부터 건설적이거나 부정적인 피드백을 받는 것은 불가피하다. 이러한 경험은 스트레스를 주고 사기를 저하시킬 수 있지만, 동시에 배우고 개선할 기회가 될 수 있다. 개인적으로 받아들이거나 포기하지 마세요. 대신 긍정적이고 회복력 있는 방식으로 대처할 줄 알아야 한다. 오류의 근본 원인을 분석하고 필요하다면 도움을 구하며, 피드백을 적용하여 코드 품질과 성능을 향상시키세요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 개발자들의 의견
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;정신 건강을 관리하는 것이 클린 코드를 작성하는 것만큼이나 중요하다는 것을 배웠습니다! 이를 재미있게 해낼 수 있는 방법은 &quot;실수에서 배운다&quot;는 마인드셋을 받아들이는 것입니다. 저도 처음에는 코드에 오류가 있거나 피드백을 받을 때 좌절감을 느꼈습니다. 하지만 시간이 지나면서 그런 순간이 바로 진정한 배움의 기회라는 것을 깨달았습니다. 이제 저는 각각의 오류를 개선하고 코드를 더 나은 상태로 만들 수 있는 기회로 생각합니다. 마치 도전적인 퍼즐을 푸는 것 같습니다!&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;진지한 취미 활동을 갖는 것이 중요합니다. 소프트웨어 개발은 불규칙적인 업무입니다 - 때로는 몰려오고 때로는 한가할 때가 있죠. 보람 있는 일이기는 하지만, 실패와 좌절감을 안겨줄 수도 있습니다. 열정을 가진 진지한 취미 활동은 이런 고통과 환희를 극복하는 데 도움이 될 수 있습니다. 취미 활동을 통해 편안함과 스트레스를 해소할 수 있어 업무의 기복을 평준화할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;계속해서 배우고 성장하세요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 개발은 지속적인 배움과 적용을 요구하는 다이나믹 하면서 성장하는 공간이다. 다른 영역에서의 지식과 스킬을 확장하는 것처럼 최신 트렌드, 기술 그리고 여러분의 도메인에 관한 최고의 연습에 뒤쳐지지 않을 필요가 있다. 새로운 것을 배우는 것은 지루함과 침체를 막는 것과 같이 여러분의 자신감, 동기 그리고 창의성을 상승시켜줄 수 있습니다. 온라인 강의, 책, 팟캐스트, 블로그 혹은 포럼등의 다양한 자원을 통해 배울 수 있습니다. 여러분의 아이디어, 인사이트 혹은 질문을 다른 개발자와 공유할 수 있는 커뮤니티, 이벤트 혹은 프로젝트에 참가할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 개발자들의 의견
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소트트웨어 개발과 기술은 너무 빨리 움직이는 나머지 부서에서 새로운 기술을 적용하고 실행하려는 순간 다른 곳에서 기술이 태어납니다. 때때로 이것이 과하거나 여러분이 너무 뒤쳐진다는 생각을 하게 만들기도 합니다. 하지만 이는 어쩔 수 없는 것이라고 이해할 필요가 있으며, 사실 여러분이 연구를 하거나 새로운 기술이 어떤 것을 가져올지 짧게 훑어보는 것만으로도 여러분은 잘하고 있는 것입니다!&lt;/li&gt;
&lt;li&gt;지속적인 학습 습관을 기르는 것은 소프트웨어 개발이라는 도전적인 분야에서 열정과 회복력을 가지고 성장하는 데 있어 핵심적입니다. 여기 소프트웨어 개발자가 계속 배우고 성장하여 정신건강을 유지하기 위한 추가 팁이 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨퍼런스, 세미나 또는 워크숍에 참석하여 새로운 개념을 접하고 전문가들과 교류합니다.&lt;/li&gt;
&lt;li&gt;새로운 언어나 프레임워크를 실험해볼 수 있는 부업이나 오픈소스에 기여합니다.&lt;/li&gt;
&lt;li&gt;귀중한 조언을 통해 학습과 경력 성장을 안내해줄 멘토를 찾습니다.&lt;/li&gt;
&lt;li&gt;다른 사람들을 가르치고 훈련시킵니다. 이는 자신의 지식을 강화하고 심화시키는 데 도움이 됩니다.&lt;/li&gt;
&lt;li&gt;기술 외에도 디자인, 비즈니스, 심리학 등을 탐구하여 시각을 넓힙니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. 도움 혹은 지원을 찾으세요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;소프트웨어 개발은 외롭고도 고립된 직업이다. 특히 원격이나 독립적으로 일할 때 더더욱 그렇다. 다른 사람들과 끊겨있다는 것을 때때로 느낄 것이며, 멘탈 건강에 영향을 주는 개인적인 혹은 직업적인 이슈와 싸우고 있을 수 있다. 혼자서 해결하려 하지 마라. 도움을 주고 받는 것을 여러분의 친구, 가족, 동료, 멘토 혹은 상담사에게서 찾을 수 있다. 그들은 여러분에게 감정적인, 사회적인 혹은 실질적인 도움, 혹은 관점, 조언 혹은 응원을 줄 수 있다. 또한 멘탈 건강 자원을 제공하는 테라피, 코칭 그리고 명상을 위한 온라인 플랫폼, 앱 혹은 서비스를 이용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 개발자들의 의견
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러분은 무엇이든 한번에 할 수 있는 사람이 아닙니다.&lt;/li&gt;
&lt;li&gt;누군가에게서, 아무리 그것이 생각하기에 해결하기에 작은 문제라도, 도움을 받는 것은 일반적입니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;우리의 뇌는 항상 동일하게 기능하지 않습니다. 때로는 매우 쉽게 해결할 수 있는 문제에 대해서도 뇌가 생각해내지 못할 때가 있습니다. 그런 경우에는 주저하지 말고 도움을 요청하는 것이 좋습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;6. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;고마움과 감사함을 배우세요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;소프트웨어 개발은 보람 있고 만족스러운 직업일 수 있지만, 작업과 삶의 긍정적인 측면을 잊거나 간과하게 만들 수도 있다. 문제, 도전 또는 실수에 너무 집중하여 성취, 기회 또는 기쁨을 소홀히 할 수 있다. 작업 및 환경에 대한 감사와 감상을 실천할 필요가 있다. 이를 위해 감사 일지를 작성하거나, 이정표를 축하하거나, 노력에 대한 보상을 하거나, 타인에게 감사를 표현할 수 있다. 이를 통해 기분, 자존감과 행복감을 높이고 스트레스와 부정적인 생각을 줄일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 개발자들의 의견
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;결&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;국 모든 직업에서 &lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;실제로 일이 어떻게 진행되는지 보게 될 것입니다. 이것이 당신의 초기 열망과 맞지 않을 수 있으며, 이에 대처하는 방식이 중요합니다. 왜냐하면 부정적인 마음가짐은 전염성이 있기 때문입니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;이&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt; 순간 한발짝 물러서서 &lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;관점을 유지하는 것이 중요합니다. 당신의 일상을 돌아보세요. 이것이 정말 당신에게 영향을 미치나요? 어려움과 좌절을 공개적으로 표현하는 것은 매우 가치 있지만, 그것이 팀 역학에 어떤 영향을 미치는지에 대해서도 주의를 기울여야 합니다. 균형을 잡아야 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;color: #29261b; text-align: start;&quot;&gt;7. 그 외 다른 개발자들의 의견&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #29261b; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경계 설정: 계획되지 않은 업무에 '아니오'라고 말하세요. 관리자와 협력하여 작업의 우선순위를 정하세요.&lt;/li&gt;
&lt;li&gt;휴가 사용: 개인 휴가는 생산성 향상 도구입니다. 활용하세요!&lt;/li&gt;
&lt;li&gt;개인 프로젝트: 개인 프로젝트가 업무량을 늘릴 수 있다고 생각될 수 있습니다. 즐겁게 작업할 수 있는 프로젝트를 선택하세요.&lt;/li&gt;
&lt;li&gt;아침 정복: 운동으로 아침을 시작하고, 출근 후 가장 중요한 업무부터 처리하세요. 이렇게 하면 성취감을 느낄 수 있어 하루를 잘 보낼 수 있습니다.&lt;/li&gt;
&lt;li&gt;사교: 사람들과 연결할 시간을 가지세요. 주말에 동료, 친구들과 외출하여 기분전환을 하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ETC</category>
      <category>개발</category>
      <category>개발자</category>
      <category>멘탈</category>
      <category>소프트웨어</category>
      <category>정신</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/58</guid>
      <comments>https://kimkani.tistory.com/58#entry58comment</comments>
      <pubDate>Fri, 15 Mar 2024 11:04:58 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트 스터디] 클로저(Closure) 그리고 작동 원리</title>
      <link>https://kimkani.tistory.com/57</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbxlEh/btsDh88oJqS/BzWDmP92v7DBAkkfXxNks1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbxlEh/btsDh88oJqS/BzWDmP92v7DBAkkfXxNks1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbxlEh/btsDh88oJqS/BzWDmP92v7DBAkkfXxNks1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbxlEh%2FbtsDh88oJqS%2FBzWDmP92v7DBAkkfXxNks1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;689&quot; height=&quot;689&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1704954724526&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function outer() {
  var a = 1;
  
  var inner = function () {
    return a++;
  }

  return inner;
}

var increment = outer();

console.log(increment());
console.log(increment());
console.log(increment());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저를 살펴보기 전에 위의 코드를 살펴보겠다. 단순한 변수 a의 값을 호출할 때 마다 inner의 함수가 scope체인 상의 변수 a를 가져와서 하나씩 늘린다. 밑의 결과를 보면은 납득이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LUjyY/btsDkBBRgO9/xkb77D84VypZCoyVr7RHMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LUjyY/btsDkBBRgO9/xkb77D84VypZCoyVr7RHMK/img.png&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;184&quot; data-is-animation=&quot;false&quot; style=&quot;width: 66.9107%; margin-right: 10px;&quot; data-widthpercent=&quot;67.7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LUjyY/btsDkBBRgO9/xkb77D84VypZCoyVr7RHMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLUjyY%2FbtsDkBBRgO9%2Fxkb77D84VypZCoyVr7RHMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;541&quot; height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JVHge/btsDitK8wHV/hSjm5VHGHCLfOAZUESLzak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JVHge/btsDitK8wHV/hSjm5VHGHCLfOAZUESLzak/img.png&quot; data-origin-width=&quot;383&quot; data-origin-height=&quot;273&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.9266%;&quot; data-widthpercent=&quot;32.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JVHge/btsDitK8wHV/hSjm5VHGHCLfOAZUESLzak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJVHge%2FbtsDitK8wHV%2FhSjm5VHGHCLfOAZUESLzak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;273&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console.dir(increment); 를 찍어보면 위의 사진처럼 Closure라고 되어있고 a = 4라고 되어있다. 이것이 바로 클로저이다. 클로저를 MDN에서 찾아보면 다음과 같이 뜬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;클로저는 주변 상태(어휘적 환경)에 대한 참조와 함께 묶인(포함된) 함수의 조합입니다. 즉, 클로저는 내부 함수에서 외부 함수의 범위에 대한 접근을 제공합니다. JavaScript에서 클로저는 함수 생성 시 함수가 생성될 때마다 생성됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 한 번 Environment관점에서 한 번 보겠다. 그러면 아래와 같이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KqbOH/btsDj1udpvI/5tBSmTBm8kbFMjKqWLLmHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KqbOH/btsDj1udpvI/5tBSmTBm8kbFMjKqWLLmHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KqbOH/btsDj1udpvI/5tBSmTBm8kbFMjKqWLLmHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKqbOH%2FbtsDj1udpvI%2F5tBSmTBm8kbFMjKqWLLmHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;833&quot; height=&quot;432&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 여기서 궁금한 점이 나온다. Execution Context상에서 사라지는 Outer 환경 정보에 a가 있을 텐데 어떻게 inner에서 이 정보를 참조해서 유지하고 있는 것일까? 이를 일단 코드 라인 별로 어떻게 되는지 살펴본 뒤, Execution Context가 콜 스택에 쌓이는 순으로 확인해 보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1704966130686&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function outer() {
  var a = 1;
  
  var inner = function () {
    return a++;
  }

  return inner;
}

var increment = outer();

console.log(increment());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;outer라는 변수와 increment 라는 변수를 전역에 생성하고 Global Execution Context에 모아둔다.&lt;/li&gt;
&lt;li&gt;outer에 함수를 할당하고, increment에는 outer()를 호출한 다음 그 리턴 값인 함수 자체인 inner를 increment변수에 할당한다. 이 때 outer라는 함수를 호출했으므로 새로운 Execution Context가 생성된다. 이 때 변수 a와 inner의 정보를 수집한다. 이때 inner의 선언 당시에 환경을 파악하여 스코프 체인을 만든다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;outer의 실행 컨텍스트가 삭제된다. 하지만 변수 a가 inner의 스코프 체인 상에 있으므로 GC의 대상이 되지 않는다. 그리고 increment인 inner함수가 실행된다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;이 때 inner함수의 a값을 찾아 스코프 체인 상을 탐색하고, 여기서 a = 1을 찾아 실행한다.&lt;/li&gt;
&lt;li&gt;그 후 a = 2가 되고, console.log(increment())를 실행한 후 Execution Context가 사라진다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xm9Yv/btsDkNh4xLW/8nRmkA4mtjidl8aKKRwy8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xm9Yv/btsDkNh4xLW/8nRmkA4mtjidl8aKKRwy8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xm9Yv/btsDkNh4xLW/8nRmkA4mtjidl8aKKRwy8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxm9Yv%2FbtsDkNh4xLW%2F8nRmkA4mtjidl8aKKRwy8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;837&quot; height=&quot;464&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이러한 Closure를 왜 사용할까? 여러 이유가 있지만, 크게 두가지로 정리가 가능할 것 같다. 개인적으로 &quot;폐쇄성&quot;이 키워드인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;폐쇄성을 이용한 정보/상태 보존&lt;/li&gt;
&lt;li&gt;폐쇄성을 이용해 정보의 접근을 제한한 캡슐화&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번의 경우 정보/상태 보존이라는 점이 크다. 위에서 말한 것처럼, 외부 함수의 정보가 그대로 유지되기 때문에, 정보가 지속적으로 보존된다는 것이다. 2번도 큰 특징이다. 한마디로 inner함수를 통해서만 접근이 가능하다. 즉 다른 함수 - 자바로 예를 들자면 - class에서 private역할을 해준 다는 것이다. 위의 특징을 이용해서 아래와 같이 특정 함수만을 이용해서 정보/상태 - 위의 코드에서는 변수 a - 에만 접근이 가능하도록 코드를 짤 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1704970840243&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var change = function numberChange() {
  var a = 1;

  return {
    plusOne: function () {
      return ++a;
    },
    minusOne: function () {
      return --a;
    },
    getA: function () {
      return a;
    }
  };
}

var example = change();

console.log(example.plusOne());
console.log(example.getA());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드 처럼 짜면, a의 정보를 은닉화 하면서, 접근도 제어할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 클로저를 알아봤는데, 많이 쓰이는 React에서는 과연 어떻게 쓰일까? 바로 Hooks에서 사용한다. 이 개념은 나중에 시간이 되면 알아보겠다.&lt;/p&gt;</description>
      <category>JavaScript</category>
      <category>clousre</category>
      <category>javascript</category>
      <category>개념</category>
      <category>공부</category>
      <category>자바스크립트</category>
      <category>컴공</category>
      <category>컴퓨터공학</category>
      <category>클로저</category>
      <category>프론트</category>
      <category>프론트엔드</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/57</guid>
      <comments>https://kimkani.tistory.com/57#entry57comment</comments>
      <pubDate>Thu, 11 Jan 2024 20:02:37 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트 스터디] Scope(스코프), Scope Chain(스코프 체인)</title>
      <link>https://kimkani.tistory.com/55</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NvksP/btsCMzypnWO/NeqrVzP3qNufFK814YQKeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NvksP/btsCMzypnWO/NeqrVzP3qNufFK814YQKeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NvksP/btsCMzypnWO/NeqrVzP3qNufFK814YQKeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNvksP%2FbtsCMzypnWO%2FNeqrVzP3qNufFK814YQKeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;580&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번 글에서는 Hoisting(호이스팅)에 대해 알아봤다. 이번 시간에는 스코프와, 스코프 체인 그리고 그를 도와주는 outerEnvironmentReferenece에 대해 알아 볼 것이다. 먼저 스코프에 대해 알아보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Scope(스코프)&lt;/h2&gt;
&lt;pre id=&quot;code_1703902588313&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;var a = &quot;Hello&quot;;

function outer() {
  console.log(a);
  
  function inner() {
    console.log(a);
  }
  var a = &quot;bye&quot;;
  inner();
}
outer();
console.log(a);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스코프에 대해 먼저 알아보겠다. 스코프, 말 그대로 해석하자면 범위라는 뜻이다. JavaScript에서는 식별자의 범위라고 할 수 있다. 변수가 어디까지 유효하고 그 유효한 범위에 대해 접근 가능한 지에 대한 것이다. 스코프의 경우 크게 global scope와 local scope로 나뉘고, local scope는 다시금 function scope와 block scope로 나뉜다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 스코프를 간단하게 그림을 통해 살펴보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ms1jB/btsCOE7bwzN/fCqsdLJSNb79CU6NmiOFr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ms1jB/btsCOE7bwzN/fCqsdLJSNb79CU6NmiOFr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ms1jB/btsCOE7bwzN/fCqsdLJSNb79CU6NmiOFr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fms1jB%2FbtsCOE7bwzN%2FfCqsdLJSNb79CU6NmiOFr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;518&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우처럼 Function(Local) Scope 내에서는 해당하는 두번째 a 변수와 inner함수만 호출이 outer 함수 내에서만 가능하다. 하지만, outer 함수와 outer 함수 바깥의 첫번째 a 변수는 어디서든 호출이 가능하다. 그치만 이상하다, inner함수의 경우 a가 정의되어 있지 않았는데도 외부의 변수 a를 참조할 수 있었다. 이는 바로 Scope Chain과 그를 도와주는 outer Environment Reference의 도움으로 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;outer Environment Reference의 경우 상위 환경에 대한 참조를 한다. 여기서 상위 환경이란 무엇일까? 바로 직전 컨텍스의 Lexical Environment이다. 이를 그림으로 한 번 살펴보겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FEPv2/btsCN5X6wHx/cf7jJ4kFkQ0arQuF3T4ZQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FEPv2/btsCN5X6wHx/cf7jJ4kFkQ0arQuF3T4ZQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FEPv2/btsCN5X6wHx/cf7jJ4kFkQ0arQuF3T4ZQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFEPv2%2FbtsCN5X6wHx%2Fcf7jJ4kFkQ0arQuF3T4ZQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;844&quot; height=&quot;454&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 Lexical Environment끼리 연결되어 있는 것이 바로 Scope Chain이다. 스코프 체인은 결국 유효 범위 스코프를 한번 탐색하고 해당 변수나 함수가 없을 경우 외부 환경을 탐색해 나가면서 하나하나 찾아나가는 것이다. 최종적으로는 global environment에 당도하게 된다. 이는 코드에 console.dir(inner)를 넣어서 더욱 자세하게 파악할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1703906832585&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;var a = &quot;Hello&quot;;

function outer() {
  console.log(a);
  
  function inner() {
    console.log(a);
    console.dir(inner);
  }
  var a = &quot;bye&quot;;
  inner();
}
outer();
console.log(a);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BTl3j/btsCOHbIXOr/qQ91W1htk1kg2E6zoUQXm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BTl3j/btsCOHbIXOr/qQ91W1htk1kg2E6zoUQXm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BTl3j/btsCOHbIXOr/qQ91W1htk1kg2E6zoUQXm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBTl3j%2FbtsCOHbIXOr%2FqQ91W1htk1kg2E6zoUQXm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;674&quot; height=&quot;426&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpfhZP/btsCReNAxnO/SrDeXFIQU7uJOk0rj5kDE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpfhZP/btsCReNAxnO/SrDeXFIQU7uJOk0rj5kDE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpfhZP/btsCReNAxnO/SrDeXFIQU7uJOk0rj5kDE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpfhZP%2FbtsCReNAxnO%2FSrDeXFIQU7uJOk0rj5kDE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;284&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의할 점은 콜 스택 상에 쌓여있는 상위 순서대로 참조하는 것은 아니라는 것이다. Lexical Environment에서 Environment Record는 함수의 호출이 아닌 선언 당시의 환경을 파악한다. 즉, 선언 당시에 스코프 체인이 만들어 진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;실행 컨텍스트 - 코드 실행 전 코드에 대한 환경 정보 수집&lt;br /&gt;콜스택에는 전역 실행 컨텍스트가 담기고 함수가 실행 혹은 호출될 때 마다 추가로 쌓인다.&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1703906248471&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var a = &quot;1&quot;;
first();

function first() {
    var b = &quot;2&quot;;
    second();

    function second() {
        var c = &quot;3&quot;;
        third();
    }
}

function third() {
    var d = &quot;4&quot;;
    console.log(a + b + c + d);
    console.dir(third)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보겠다. 콜 스택 상에는 다음과 같이 쌓일 것이다. 하지만 Scope Chain은 오른쪽과 같이 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIh5tA/btsCRih9wah/jOBZN8LPElQYpdwLVEMXyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIh5tA/btsCRih9wah/jOBZN8LPElQYpdwLVEMXyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIh5tA/btsCRih9wah/jOBZN8LPElQYpdwLVEMXyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIh5tA%2FbtsCRih9wah%2FjOBZN8LPElQYpdwLVEMXyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;848&quot; height=&quot;459&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유인 즉슨, 이미 third()와 first()는 전역 함수로서 당시 선언된 전역 변수 상태의 정보만 담고 있어서 second와 first내의 변수 정보들에 대해서는 알 턱이 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 closure(클로저)에 대해 더 자세히 알아보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript</category>
      <category>javascript</category>
      <category>변수</category>
      <category>자바스크립트</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>컴퓨터공학</category>
      <category>코딩</category>
      <category>프로그래밍</category>
      <category>프론트엔드</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/55</guid>
      <comments>https://kimkani.tistory.com/55#entry55comment</comments>
      <pubDate>Sat, 30 Dec 2023 12:29:25 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트 스터디] 자바스크립트에서의 호이스팅(Hoisting)</title>
      <link>https://kimkani.tistory.com/54</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZSXBI/btsCyxtctFz/xv8Ye7ibI7Mlgj4hS5HbC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZSXBI/btsCyxtctFz/xv8Ye7ibI7Mlgj4hS5HbC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZSXBI/btsCyxtctFz/xv8Ye7ibI7Mlgj4hS5HbC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZSXBI%2FbtsCyxtctFz%2Fxv8Ye7ibI7Mlgj4hS5HbC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;507&quot; height=&quot;507&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번 글에서 Execution Context(실행 컨텍스트)의 environmentRecord가 미리 식별자의 정보를 수집한다고 언급했을 것이다. 이러한 environmentRecord에 대해 좀 더 자세하게 들여다 보겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFMHEs/btsCwgMXruN/Mn8G3PaW7xN11NtDV4ywb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFMHEs/btsCwgMXruN/Mn8G3PaW7xN11NtDV4ywb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFMHEs/btsCwgMXruN/Mn8G3PaW7xN11NtDV4ywb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFMHEs%2FbtsCwgMXruN%2FMn8G3PaW7xN11NtDV4ywb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;417&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;417&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;environmentRecord는 해당 Lexical Environment에 있는 범위 내의 식별자를 정의한다. 매번 자바스크립트 코드가 평가될때 마다, 함수 선언, 블록 스코프 혹은 Catch와 Try구문을 발견할 때 마다 새로운 식별자가 바인딩 되어 environmentRecord에 기록된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 environmentRecord에는 3개의 하위 클래스로 이루어져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Declarative Environment Record(선언적 환경 레코드)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var, let, const, class, module, import, function 등의 해당 스코프 내의 식별자들의 바인딩 - 여기서 바인딩이란 식별자와 그 값을 묶는 다는 것 - 을 관리한다. 선언적 환경 레코드는 두가지로 다시 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나는 Function Environment Record(함수 환경 레코드)이다. 말 그대로 함수랑 관계되는 것으로, 함수의 상위 스코프를 결정하며 - 이 때 함수를 호출한 위치가 아닌 함수를 어디에서 정의했는지에 따라 달라진다. 또한 이때 this가 바인딩 되며, 이 this는 함수의 호출 방식에 따라 결정된다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지 하나는 Module Environment Record(모듈 환경 레코드)이다. 모듈의 외부 스코프를 나타낼 때 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Object Environment Record(객체 환경 레코드)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;with문에 대해 실행 컨텍스트를 만들 때, 여기다 저장한다. 현재의 스코프의 변수들을 대상으로, Object Environment Record는 객체와 관련된 프로퍼티나 메소드의 접근을 제공한다. 추가적으로 Object Environment Record는 두 가지 필드를 가진다. [[BindingObject]], 환경 레코드에 바인딩된 객체, 그리고 [[IsWithEnvironment]]로 with문에 의해 생성됐는지를 나타내는 값이다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Global Environment Record(전역 환경 레코드)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌트인 객체들, 전역 객체의 프로퍼티 그리고 전역 스코프에서의 선언에 관한 바인딩을 관리한다. 모든 자바스크립트 코드들이 공유하는 최상위 스코프를 나타낸다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제부터 호이스팅(Hoisting)을 본격적으로 알아보자. 하나 알아둘 것은 호이스팅은 그럴듯 하게 보이는 현상이라는 것이다. 실제로 변수가 위로 끌려 올라가지는 것이 아니다. 그렇게 보이는 듯한 현상이다. 다음 예제를 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1703749818691&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var a = &quot;Hello&quot;;

function outer() {
  console.log(a);
  
  function inner() {
    console.log(a);
  }
  var a = &quot;bye&quot;;
  inner();
}
outer();
console.log(a);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 실행하면 어떻게 나올까? 밑의 결과가 나온다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;281&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUlJC1/btsCMpIBiN1/CYsQpcfSj3xXIkQWXfFD6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUlJC1/btsCMpIBiN1/CYsQpcfSj3xXIkQWXfFD6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUlJC1/btsCMpIBiN1/CYsQpcfSj3xXIkQWXfFD6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUlJC1%2FbtsCMpIBiN1%2FCYsQpcfSj3xXIkQWXfFD6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;281&quot; height=&quot;322&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;281&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 이렇게 나올까? 한번 하나씩 살펴보겠다. 일단 하나 알아둘 것이 있다. 바로 environmetRecord가 식별자를 수집하지만, 이는 함수 혹은 변수가 선언될 당시의 정보를 가져온다. 즉 호출 - 실제로 코드에서 실행하려고 실행문을 쓸 때 - 가 아닌, 선언 했을 때의 정보이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 순서를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;먼저 코드 전역에 걸쳐서 정보를 수집한다. 이때 코드 전역에 있는 함수 혹은 변수는 a와 outer()이다. 이때 a의 값은 할당되지 않는다.&lt;/li&gt;
&lt;li&gt;그렇게 정보 수집이 끝났으면 var a = &quot;hello&quot;가 담긴다. 그 다음에는 outer()가 실행된다.&lt;/li&gt;
&lt;li&gt;outer 함수가 실행되면서 outer 컨텍스트가 생성된다. 이때 outer 내부에 - environment Record 에 쌓인다 - inner() 함수와 a가 쌓인다.&lt;/li&gt;
&lt;li&gt;그다음 a를 찾는다. 이때 a가 hoisting에 의해 environment Record에 미리 기록되어 있으므로 undefined를 출력한다.&lt;/li&gt;
&lt;li&gt;그 후 a에 &quot;bye&quot;가 할당된다.&lt;/li&gt;
&lt;li&gt;그 다음 inner()가 실행된다. 이 때 a를 함수 내부에서 찾을 수 없음으로 다음 시간에 언급할 스코프체인 - 간단하게 말해 콜스택 상에서 상위 컨텍스트를 찾는 체인같은 역할을 한다 - 을 따라 변수를 찾는다. outer안에 있는 a를 찾았다. inner()가 호출되기 전에 이미 &quot;bye&quot;가 할당되어 있기 때문에 &quot;bye&quot;를 출력한다.&lt;/li&gt;
&lt;li&gt;그리고 outer()가 끝난 뒤에 console.log(a)를 실행한다. 이 때&amp;nbsp; &quot;bye&quot;가 아닌 &quot;Hello&quot;가 출력되는 이유는 스코프 범위 상에서 outer()안과 밖의 a는 서로 다른 변수이기 때문이다. 즉 서로 다른 환경이다. 그렇기에 a에는 &quot;Hello&quot;가 할당되어서 &quot;Hello&quot;가 출력된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번에서 만약 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;변수가 재선언 되지 않은 상태에서 할당만 이루어졌다면 어찌 되었을까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;282&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk9kAg/btsCJkgWuAh/GrBkcuSsM3KCYEDK7kL5kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk9kAg/btsCJkgWuAh/GrBkcuSsM3KCYEDK7kL5kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk9kAg/btsCJkgWuAh/GrBkcuSsM3KCYEDK7kL5kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk9kAg%2FbtsCJkgWuAh%2FGrBkcuSsM3KCYEDK7kL5kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;282&quot; height=&quot;317&quot; data-origin-width=&quot;282&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 상태처럼 &quot;Hello&quot;와 &quot;bye&quot;, &quot;bye&quot;가 출력된다. 왜 그런 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;먼저 코드 전역에 걸쳐서 정보를 수집한다. 이때 코드 전역에 있는 함수 혹은 변수는 a와 outer()이다. 이때 a의 값은 할당되지 않는다.&lt;/li&gt;
&lt;li&gt;그렇게 정보 수집이 끝났으면 var a = &quot;hello&quot;가 담긴다. 그 다음에는 outer()가 실행된다.&lt;/li&gt;
&lt;li&gt;outer 함수가 실행되면서 outer 컨텍스트가 생성된다. 이때 outer 내부에 - environment Record 에 쌓인다 - inner() 함수만 쌓인다. 왜냐하면 변수가 선언되어 있지 않은 환경이기 때문이다.&lt;/li&gt;
&lt;li&gt;그다음 a를 찾는다. 이때 a가 내부에 없으므로 스코프 체인을 따라서 상위 스코프 변수를 찾아간다. 이 때 outer() 바깥에 있는 a를 찾는다. 이미 &quot;Hello&quot;가 할당되어 있으므로 &quot;Hello&quot;를 출력한다.&lt;/li&gt;
&lt;li&gt;그다음 a에다가 &quot;bye&quot;를 할당한다.&lt;/li&gt;
&lt;li&gt;그 다음 inner()가 실행된다. 이 때 a를 함수 내부에서 찾을 수 없음으로 다음 시간에 언급할 스코프체인 - 간단하게 말해 콜스택 상에서 상위 컨텍스트를 찾는 체인같은 역할을 한다 - 을 따라 변수를 찾는다. outer밖에 있는 a를 찾았다. inner()가 호출되면서 이미 &quot;bye&quot;를 할당했기 때문에 &quot;bye&quot;를 출력한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;그리고 console.log(a)에서도 &quot;bye&quot;를 출력하는데, 이는 outer()가 호출되면서 스코프 체인으로 상위 범위를 찾아 a를 가져와서 &quot;bye&quot;를 할당했기 때문이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇듯 호이스팅은 변수의 선언과 그 당시 함수가 어디에 어떻게 선언되어 있는지에 따라 크게 갈린다.&amp;nbsp; 이번 글에서는 environment Record를 공부하면서 호이스팅에 대해 알아봤다. 그렇다면 나머지 한가지인 outerEnvironmentReference는 무엇일까? 그것은 Closure하고도 관계가 있기에 다음 시간에 알아보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript</category>
      <category>javascript</category>
      <category>공부</category>
      <category>자바스크립트</category>
      <category>컴공</category>
      <category>코딩</category>
      <category>프로그래밍</category>
      <category>프론트엔드</category>
      <category>호이스팅</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/54</guid>
      <comments>https://kimkani.tistory.com/54#entry54comment</comments>
      <pubDate>Thu, 28 Dec 2023 17:18:38 +0900</pubDate>
    </item>
    <item>
      <title>[도커 알아가기] Hypervisor(하이퍼바이저)와 Virtual Machine</title>
      <link>https://kimkani.tistory.com/53</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhI4kB/btsCzIhlzJB/tAFSEDRvdj82GH51RZgltK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhI4kB/btsCzIhlzJB/tAFSEDRvdj82GH51RZgltK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhI4kB/btsCzIhlzJB/tAFSEDRvdj82GH51RZgltK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhI4kB%2FbtsCzIhlzJB%2FtAFSEDRvdj82GH51RZgltK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;416&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전부터 도커는 자주 사용했지만, 그 원리와 동작 방식을 자세히 알지는 못했다. 이번 기회에 도커가 어떻게 동작하는지 알아보면서, 도커 파일에서 내가 궁금했던 부분을 하나씩 알아가 보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Virtual Machine이란 무엇인가?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Rdf56/btsCDgdhw9U/fgTFMnzSxadiEDILkYcHE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Rdf56/btsCDgdhw9U/fgTFMnzSxadiEDILkYcHE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Rdf56/btsCDgdhw9U/fgTFMnzSxadiEDILkYcHE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRdf56%2FbtsCDgdhw9U%2FfgTFMnzSxadiEDILkYcHE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;887&quot; height=&quot;484&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가상화, Virtual Machine과 Hypervisor&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Virtual Machine(이하 VM)의 경우 Hyperviosr의 도움을 받는다. 그렇다면 도커와 비교하기 전에 VM에서 Hypervisor가 어떻게 동작하는지, 그리고 어떤 부분에 있어서 Hypervisor가 가지고 있는 능력이 도커와 비교되는지 알아보려고 한다. 먼저 VM의 뜻을 풀어보면 &quot;가상 기계&quot;이다. 무언가 진짜가 아닌 가상화를 통해 이루어지는 기계라는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상화란 결국 하드웨어의 자원을 논리적으로 나누어서 추상화 하는 것이다. 하나의 하드웨어를 마치 여러개의 하드웨어인 것처럼 가상화하거나, 여러 하드웨어를 마치 하나의 하드웨어로 묶어 하나인 것처럼 역할하게 하거나 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 생각해보면 이렇게 VM에는 다양한 OS가 깔릴 것이다. 이런 OS들은 서로 다른 커널을 가지고 있고, 같은 명령어라도 언어가 다르다. 이러한 명령어를 해석해서 하드웨어가 이해할 수 있게 지원하는 시스템이 바로 Hypervisor이다. 윈도우의 경우 Hyper-V, 그리고 리눅스의 경우에는 KVM/QEMU를 기본적으로 지원한다. 이러한 하이퍼바이저는Type1과 Type2로 구분지을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5XJN9/btsCyIWmrQS/JFkbDWOUrrZvGCj8eizPrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5XJN9/btsCyIWmrQS/JFkbDWOUrrZvGCj8eizPrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5XJN9/btsCyIWmrQS/JFkbDWOUrrZvGCj8eizPrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5XJN9%2FbtsCyIWmrQS%2FJFkbDWOUrrZvGCj8eizPrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;520&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Type 1의 경우에는 물리적인 하드웨어 바로 위에 하이퍼바이저가 위치한 방식 - 베어 메탈 하이퍼바이저라고도 부른다 - 이다. 이런 하이퍼바이저의 경우 Host OS가 없이 바로 하드웨어에 직접 설치되는 구조이다. 이러한 Type 1의 경우 운영체제에 별도로 리소스를 할당하지 않기에 부하가 적고 리소스 관리가 유연하지만, 자체적인 관리 기능이 없기에 콘솔의 필요성이 대두된다. 그와는 다르게 Type 2의 경우에는 Host OS - 우리가 흔히 말하는 윈도우나, 우분투 등등 - 에서 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 나중에 가상화랑 하이퍼바이저를 더 자세하게 다룰 때 이야기하고 일단은 넘어가도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ETC</category>
      <category>IT</category>
      <category>vm</category>
      <category>가상머신</category>
      <category>가상화</category>
      <category>공부</category>
      <category>도커</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>하이퍼바이저</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/53</guid>
      <comments>https://kimkani.tistory.com/53#entry53comment</comments>
      <pubDate>Mon, 25 Dec 2023 16:07:48 +0900</pubDate>
    </item>
    <item>
      <title>Django의 N+1 문제, Eager Loading, prefetch_related, select_related 등</title>
      <link>https://kimkani.tistory.com/52</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFQu0c/btsCzJzrP5Z/QaK7w4WWEQX66diEoIz8x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFQu0c/btsCzJzrP5Z/QaK7w4WWEQX66diEoIz8x1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFQu0c/btsCzJzrP5Z/QaK7w4WWEQX66diEoIz8x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFQu0c%2FbtsCzJzrP5Z%2FQaK7w4WWEQX66diEoIz8x1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;480&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본적인 모델 구조 예시&lt;/h2&gt;
&lt;pre id=&quot;code_1703239617061&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.db import models


class Customer(models.Model):
    user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
    phone = models.CharField(max_length=20, null=True)
    address = models.CharField(max_length=100, null=True)
    city = models.CharField(max_length=50, null=True)
    state = models.CharField(max_length=50, null=True)
    zipcode = models.CharField(max_length=10, null=True)
    country = models.CharField(max_length=50, null=True)

    class Meta:
        app_label = &quot;shopping&quot;
        
class Order(models.Model):
    customer = models.ForeignKey(Customer, null=True, on_delete=models.SET_NULL)
    products = models.ManyToManyField(Product, related_name=&quot;orders&quot;)
    order_created = models.DateTimeField(auto_now_add=True)

    class Meta:
        app_label = &quot;shopping&quot;
        
class Product(models.Model):
    name = models.CharField(max_length=100, null=True)
    price = models.FloatField(null=True)

    class Meta:
        app_label = &quot;shopping&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style=&quot;text-align: center; caret-color: transparent; letter-spacing: 0px;&quot; src=&quot;https://blog.kakaocdn.net/dn/04lS1/btsCxGwTHlT/Grp61VV7AQ67888482qTyK/img.png&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;626&quot; data-is-animation=&quot;false&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같은 코드가 있다고 하자. 위의 코드는 Django의 Model로 쇼핑에서 고객, 제품 그리고 주문을 각기 표시한 것이다. 이를 ERD로 표시하면 다음과 같이 된다. 먼저 User와 Customer는 1:1관계이다. 그리고 Order에는 ForeignKey로 Customer를 참조한다. 즉 1:N관계이며 Customer &amp;lt;- Order의 관계가 된다. 그리고 Order에서 ManyToMany모델을 사용해서 Order와 Product는 M:N관계이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 모델에서 ORM을 사용할 경우 N+1 문제가 쉽게 발생할 수 있다. 이는 Django를 비롯한 ORM들의 Lazy Loading에서 비롯되는 문제이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;QuerySet에 대해서 언급하자면, &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;QuerySet은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;전달받은 모델의 객체 목록이다. DB&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;로부터 데이터를 읽고 필터를 걸거나 정렬 등을 할 수 있다. 리스트와 구조는 같지만 파이썬 기본 자료구조가 아니기에 사용을 위해서는 변환을 해줘야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Lazy Loading vs Eager Loading&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lazy Loading이란 ORM이 DB에 요청을 하여 데이터를 가져올 때 ORM을 실행할 때 마다 가져오는 것이 아닌, 실제 데이터를 불러와야할 때 SQL등의 Query문을 날려서 가져오는 것이다. 즉, ORM을 실행할 때 보다 늦게 로딩이 된다고 이해하면 편할 것이다. 하지만, 실제로 어떻게 되는지는 잘 모른다. 밑의 그림을 보면 이해가 편할 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSKDEi/btsCwLltAxi/TeTVlLtK4XpOn0rGQ7l5N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSKDEi/btsCwLltAxi/TeTVlLtK4XpOn0rGQ7l5N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSKDEi/btsCwLltAxi/TeTVlLtK4XpOn0rGQ7l5N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSKDEi%2FbtsCwLltAxi%2FTeTVlLtK4XpOn0rGQ7l5N0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;670&quot; height=&quot;330&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위의 그림에서 django-extensions의 --print-sql을 사용하여 실제로 쿼리를 날릴 때를 보여준다. 보면 Customer.objects.all()을 했을 때에는 아무것도 가져오지 않는다. 하지만 customer[0]을 해서 실제로 데이터를 조회하려고 할 때 쿼리문을 날리는 것을 알 수 있다. 이는 Django의 QuerySet이 평가되는 과정 중 하나이며, Django Docs에서는 QuerySet이 실제로 Database에 쿼리를 바로 보내는 것이 아닌 몇가지 평가될 때의 조건 하에서 보낸다고 한다. - 좀 더 간단히 말하면 QuerySet이 실행되어 실제로 Query문을 보내는 조건일 듯 하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1703242608935&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Django&quot; data-og-description=&quot;The web framework for perfectionists with deadlines.&quot; data-og-host=&quot;docs.djangoproject.com&quot; data-og-source-url=&quot;https://docs.djangoproject.com/en/4.2/ref/models/querysets/#when-querysets-are-evaluated&quot; data-og-url=&quot;https://docs.djangoproject.com/en/4.2/ref/models/querysets/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oF048/hyUPNLGRUq/sWCAZlUcERD8HN1gMCjFiK/img.png?width=1200&amp;amp;height=546&amp;amp;face=0_0_1200_546&quot;&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/4.2/ref/models/querysets/#when-querysets-are-evaluated&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.djangoproject.com/en/4.2/ref/models/querysets/#when-querysets-are-evaluated&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oF048/hyUPNLGRUq/sWCAZlUcERD8HN1gMCjFiK/img.png?width=1200&amp;amp;height=546&amp;amp;face=0_0_1200_546');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Django&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The web framework for perfectionists with deadlines.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.djangoproject.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기에는 다양한 조건이 있는데 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순회(Iteration)할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1703242874178&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;customer = Customer.objects.all()
for cus in customer:
	print(cust.id)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;슬라이싱(Slicing)할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1703242900327&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;customer = Customer.objects.all()
customer[0]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pickling과 Caching&lt;/li&gt;
&lt;li&gt;repr(), len(), list()를 호출할 때&lt;/li&gt;
&lt;li&gt;bool()처럼 if문 등으로 쿼리셋의 진위여부 파악할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;N+1 문제란?&lt;/h3&gt;
&lt;pre id=&quot;code_1703242991421&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;customer = Customer.objects.all()
for cus in customer:
    print(cus.user.id)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같은 코드가 있다고 다시 생각해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;1688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MFTPq/btsCy6oh5Hg/yYHksnKUtSKned20TcsdvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MFTPq/btsCy6oh5Hg/yYHksnKUtSKned20TcsdvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MFTPq/btsCy6oh5Hg/yYHksnKUtSKned20TcsdvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMFTPq%2FbtsCy6oh5Hg%2FyYHksnKUtSKned20TcsdvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;754&quot; height=&quot;1688&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;1688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위의 실제 쿼리처럼, customer = Customer.objects.all()을 한 뒤 customer를 순회하면서 1:1관계에 있는 customer.user.id를 가져오려고 하면, Customer의 모든 정보는 SQL문을 한 번만 날리면 되지만 다시 user의 수만큼 query문을 날려야 한다. 즉 1+N에서 N이 추가적으로 발생한다는 점이다. 이럴 때 사용하는 것이 바로 select_related()이다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;select_related()는 JOIN을 통해 한번에 데이터를 가져오는 Eger Loading(즉시 로딩)방식이다. 즉, 기존에 쿼리문을 여러번 날리던 것을 한번에 뭉쳐서 날리는 것이다. 보통 1:1관계나, ForiegnKey를 가지고 있는 쪽에서 참조하는 1:N관계에서 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1703243986239&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;customer = Customer.objects.select_related(&quot;user&quot;)
for cus in customer:
    print(cus.user.id)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;839&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCkJ4x/btsCxIVUm4J/AqaOfgoE8RlkrXvaoNIDik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCkJ4x/btsCxIVUm4J/AqaOfgoE8RlkrXvaoNIDik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCkJ4x/btsCxIVUm4J/AqaOfgoE8RlkrXvaoNIDik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCkJ4x%2FbtsCxIVUm4J%2FAqaOfgoE8RlkrXvaoNIDik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;839&quot; height=&quot;678&quot; data-origin-width=&quot;839&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위의 코드를 개선하면 단 한번의 Query문을 날리는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 다른 관계인 ManyToMany같은 곳에서는 어떻게 사용할까, 일단 ManyToMany의 관계인 Product와 Order를 쿼리해보겠다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1703245045231&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;products = Product.objects.all()
for product in products:
	print(product.orders.all())&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;1093&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nye2H/btsCzrFSx2l/sPun97iLCU0r2RaEIxPvJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nye2H/btsCzrFSx2l/sPun97iLCU0r2RaEIxPvJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nye2H/btsCzrFSx2l/sPun97iLCU0r2RaEIxPvJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnye2H%2FbtsCzrFSx2l%2FsPun97iLCU0r2RaEIxPvJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;1093&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;1093&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 Query문을 잘 살펴보면 처음에 Product를 찾고 각기 Product에 대해 Inner Join을 하는 것을 알 수 있다. 즉 1+N에서 N번의 쿼리가 더 발생한다. 이를 prefetch_related로 개선해 보겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1703247336584&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;products = Product.objects.all().prefetch_related(&quot;orders&quot;)
for product in products:
	print(product.orders.all())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5qla5/btsCzQFpoEf/c6mGMrjb97SyfclpHkh6i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5qla5/btsCzQFpoEf/c6mGMrjb97SyfclpHkh6i0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5qla5/btsCzQFpoEf/c6mGMrjb97SyfclpHkh6i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5qla5%2FbtsCzQFpoEf%2Fc6mGMrjb97SyfclpHkh6i0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;785&quot; height=&quot;485&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;485&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보면, 단 두번의 쿼리로 질의를 끝내는 것을 알 수 있다. 여기서 알아야할 것은 select_related가 Inner Join으로 한번에 끝낸다면, prefetch_related는 메인 Query에서 이미 모든 관계가 있는 모델의 QuerySet Instance를 만들어낸다. 그래서 두번째 실행문인 product.orders.all()을 할 때 이미 관계가 cache에 저장해둔 상태이기에, product의 수만큼 order가 수행되더라도 db에 접근하지 않고 cache에서 찾아쓰게 된다.&lt;/p&gt;</description>
      <category>Python/Django</category>
      <category>Django</category>
      <category>Prefetch</category>
      <category>python</category>
      <category>sql</category>
      <category>데이터베이스</category>
      <category>디비</category>
      <category>백엔드</category>
      <category>서버</category>
      <category>장고</category>
      <category>파이썬</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/52</guid>
      <comments>https://kimkani.tistory.com/52#entry52comment</comments>
      <pubDate>Fri, 22 Dec 2023 21:30:34 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트 스터디] 자바스크립트의 실행 환경(Execution Context), Lexical Environment</title>
      <link>https://kimkani.tistory.com/51</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oeQkg/btsCwjt5DcO/qNlaof5tCzZ7JmrYoeBEIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oeQkg/btsCwjt5DcO/qNlaof5tCzZ7JmrYoeBEIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oeQkg/btsCwjt5DcO/qNlaof5tCzZ7JmrYoeBEIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoeQkg%2FbtsCwjt5DcO%2FqNlaof5tCzZ7JmrYoeBEIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;486&quot; height=&quot;486&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;ECMA-262 Edition5을 기준으로 설명합니다.&lt;/blockquote&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;변수가 선언되고 할당되고, 초기화되기 까지&lt;/h2&gt;
&lt;pre id=&quot;code_1703217255889&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var num = 7;&lt;/code&gt;&lt;/pre&gt;
&lt;figure id=&quot;og_1703217551746&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자바스크립트 스터디] 자바스크립트의 데이터 타입, 참조형&quot; data-og-description=&quot;[자바스크립트 스터디] 자바스크립트의 데이터 타입, 원시형 최근에 코어 자바스크립트를 통해 아는 친구의 대학 후배들과 스터디를 진행하고 있다. 그에 대한 내용을 정리할 겸, 이제부터 하나&quot; data-og-host=&quot;kimkani.tistory.com&quot; data-og-source-url=&quot;https://kimkani.tistory.com/41&quot; data-og-url=&quot;https://kimkani.tistory.com/41&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bcx6kR/hyUPGMrTA9/WR3N9FURfCkMkKGAqqpO51/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bDRvND/hyUPAZLXCD/slDz1sSKxNkBRD8CwIK3cK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/eArsU/hyUPynjLGM/1f0l5guop7SxE9d6k5gsJK/img.png?width=1268&amp;amp;height=634&amp;amp;face=0_0_1268_634&quot;&gt;&lt;a href=&quot;https://kimkani.tistory.com/41&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kimkani.tistory.com/41&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bcx6kR/hyUPGMrTA9/WR3N9FURfCkMkKGAqqpO51/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bDRvND/hyUPAZLXCD/slDz1sSKxNkBRD8CwIK3cK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/eArsU/hyUPynjLGM/1f0l5guop7SxE9d6k5gsJK/img.png?width=1268&amp;amp;height=634&amp;amp;face=0_0_1268_634');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[자바스크립트 스터디] 자바스크립트의 데이터 타입, 참조형&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[자바스크립트 스터디] 자바스크립트의 데이터 타입, 원시형 최근에 코어 자바스크립트를 통해 아는 친구의 대학 후배들과 스터디를 진행하고 있다. 그에 대한 내용을 정리할 겸, 이제부터 하나&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kimkani.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 아주 기본적인 자바스크립트에서의 변수 선언, 초기화 그리고 할당 단계가 함축되어 있는 코드이다. 데이터의 타입에 따라 어떻게 메모리에 할당되는지는 다르다. 그에 관해서는 이전 글에서 설명했다. 이번 글에서는 &quot;그렇다면 이러한 변수들은 자바스크립트 실행 시 어떻게 활용될까?&quot;의 관점에서 써보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Execution Context(실행 컨텍스트) and Execution Stack(실행 스택)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨텍스트와 실행 스택에 대해 먼접 짚고 넘어가야 할 것 같다. 먼저 실행 컨텍스트이다. 컨텍스트를 잘 생각해보면 무언가 &quot;환경 - Nature가 아닌 Environment의 느낌&quot;에 관한 단어라는 것을 짐작할 수 있을 것이다. 다음과 같은 코드가 있다고 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1703218043366&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var num = 6;

function double(n) {
  var num = n * 2;
  samplePrint();
  return num;
}

function samplePrint() {
	console.log(num);
}

console.log(double(2));
samplePrint();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 먼저 이 파일을 스캔한 다음, 필요한 정보를 모아서, 위의 코드의 실행에 필요한 &quot;정보&quot;를 수집할 것이다. 이것이 &quot;실행 컨텍스트&quot;이다. 그 후 파서를 통해 코드를 분해하고, 변수와 함수를 메모리에 할당하고 소스코드가 생성되고 실행될 것이다. 즉 코드를 실행하기 위한 미리 정보를 모아둔 &quot;게임의 스타터 팩&quot; 같은 느낌이라고 보면 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이 게임 스타터 팩 - 실행 컨텍스트는 어떻게 구성되어 있을까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edMrfq/btsCtnxvpWi/CBXPKvP9ifb01gM6QYB7mK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edMrfq/btsCtnxvpWi/CBXPKvP9ifb01gM6QYB7mK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edMrfq/btsCtnxvpWi/CBXPKvP9ifb01gM6QYB7mK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedMrfq%2FbtsCtnxvpWi%2FCBXPKvP9ifb01gM6QYB7mK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;417&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;417&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타터 팩 - 실행 컨텍스트는 크게 VariableEnvironment, LexicalEnvironment 그리고 ThisBinding으로 구성되어 있다. 각기를 예를 들어서 설명하자면 게임 초기 캐릭터 생성시의 유저 정보를 VariableEnvironment가 가지고 있는다. 즉, 실행 컨텍스트가 처음 만들어질 때의 환경 정보를 미리 사진처럼 찍어서 보관한다고 보면 된다. 그 이후에 LexicalEnvironment가 이 초기 사진 - 정보 -를 가지고 복사해서 LexicalEnvironment에 저장한 다음 이를 주로 사용하게 된다. ThisBinding은 this가 바라봐야 할 곳을 가리키게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이러한 실행 컨텍스트들을 구성한다고 하면 정작 어떻게 쓰이는 것일까? 바로 Call Stack이라는 곳에 쌓아놨다가 하나하나 실행하는 것이다. 근데 이런 Call Stack에 담기는&amp;nbsp;실행 컨텍스트에는 3가지 타입이 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpdPGt/btsCv2MWM5t/Kkja4k43rO03l7yNZl4qyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpdPGt/btsCv2MWM5t/Kkja4k43rO03l7yNZl4qyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpdPGt/btsCv2MWM5t/Kkja4k43rO03l7yNZl4qyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpdPGt%2FbtsCv2MWM5t%2FKkja4k43rO03l7yNZl4qyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;431&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Global Execution Context(GEC) :&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GEC의 경우 자바스크립트 코드가 처음 브라우저 등에 로드될 때 만들어지는 실행 컨텍스트이다. 함수안에 없거나, 전역 변수 등등이 이 GEC에 위치된다. 만약 아무 코드가 없는 경우에는 어찌 될까? Browser의 경우에는 window object를 그리고 Node의 경우에는 global object를 실행하고 this의 값을 실행 환경에 따라 window나 global로 할당한다. 위의 코드 같은 경우 다음과 같이 GEC에 초기 정보가 담긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;419&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTs56n/btsCqvXIbtz/t7M5Y795AkDtKSeZKQKhNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTs56n/btsCqvXIbtz/t7M5Y795AkDtKSeZKQKhNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTs56n/btsCqvXIbtz/t7M5Y795AkDtKSeZKQKhNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTs56n%2FbtsCqvXIbtz%2Ft7M5Y795AkDtKSeZKQKhNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;582&quot; height=&quot;419&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;419&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Functional Execution Context(FEC)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 실행 될 때 마다 만들어지는 컨텍스트이다. 전역 실행 컨텍스트(GEC)가 단 한번만 정의되는 것에 비하면 함수가 실행될 때 마다 정의되서 Call Stack(그림상 왼쪽 박스)에 담긴다. 위의 코드 상에서는 double과 samplePrint가 실행 - 호출 -되었기에 스택의 LIFO(Last In First Out)에 의해 samplePrint의 FEC가 정의되며 가장 나중에 호출되었기에 가장 먼저 실행된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eval&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 간략하게 나마 JavaScript의 실행 컨텍스트에 대해 알아봤다. 하지만 아직 우리가 알아야 할 부분이 몇 개 더 있다. 바로 environmentRecord와&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; outer&lt;/span&gt;EnvironmentRecord이다. 먼저 하나 알아두어야 할 것이 있다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;environmentRecord는 식별자의 정보를 수집해둔다. 근데 이때는 실행 컨텍스트가 환경을 만들어 두었을 뿐 코드 자체는 실행되기 전의 상태이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 다음 시간에 알아볼 호이스팅이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript</category>
      <category>javascript</category>
      <category>js</category>
      <category>리액트</category>
      <category>웹</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <category>프론트</category>
      <category>프론트엔드</category>
      <category>호이스팅</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/51</guid>
      <comments>https://kimkani.tistory.com/51#entry51comment</comments>
      <pubDate>Fri, 22 Dec 2023 15:11:08 +0900</pubDate>
    </item>
    <item>
      <title>[프로세스 이코노미] 사람이 프로세스에 공감하는 과정 - 일본판 공부 노트 PM 전환기</title>
      <link>https://kimkani.tistory.com/50</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1t1UU/btsASbTFIsW/B7Y7fRiM4xqdxhTdk1fT21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1t1UU/btsASbTFIsW/B7Y7fRiM4xqdxhTdk1fT21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1t1UU/btsASbTFIsW/B7Y7fRiM4xqdxhTdk1fT21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1t1UU%2FbtsASbTFIsW%2FB7Y7fRiM4xqdxhTdk1fT21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;582&quot; height=&quot;582&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;人がプロセスに共感するメカニズム&lt;br /&gt;사람이 프로세스에 공감하는 매커니즘&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1700973090019&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;What is public narrative?&quot; data-og-description=&quot; &quot; data-og-host=&quot;narrativearts.org&quot; data-og-source-url=&quot;https://narrativearts.org/article/public-narrative/&quot; data-og-url=&quot;https://narrativearts.org/article/public-narrative/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ewP7s2/hyUFenDedY/5YJZqSWFb7xG3rT9HKBOyk/img.jpg?width=450&amp;amp;height=333&amp;amp;face=206_62_278_134,https://scrap.kakaocdn.net/dn/bWrHoe/hyUFaFxkne/QsCfWh8u2xnMVgGmgFEbr0/img.jpg?width=450&amp;amp;height=333&amp;amp;face=206_62_278_134,https://scrap.kakaocdn.net/dn/bK9Ejq/hyUChM5znm/HK1sacsDw7z9fCRkuA5yEK/img.jpg?width=450&amp;amp;height=333&amp;amp;face=206_62_278_134&quot;&gt;&lt;a href=&quot;https://narrativearts.org/article/public-narrative/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://narrativearts.org/article/public-narrative/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ewP7s2/hyUFenDedY/5YJZqSWFb7xG3rT9HKBOyk/img.jpg?width=450&amp;amp;height=333&amp;amp;face=206_62_278_134,https://scrap.kakaocdn.net/dn/bWrHoe/hyUFaFxkne/QsCfWh8u2xnMVgGmgFEbr0/img.jpg?width=450&amp;amp;height=333&amp;amp;face=206_62_278_134,https://scrap.kakaocdn.net/dn/bK9Ejq/hyUChM5znm/HK1sacsDw7z9fCRkuA5yEK/img.jpg?width=450&amp;amp;height=333&amp;amp;face=206_62_278_134');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;What is public narrative?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;narrativearts.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 일단 오바마 대통령의 선거 당시, 2008년의 이야기를 가져온다. 당시 캐치 프레이즈는 &quot;Yes We Can&quot;과 &quot;Change&quot;였다. 오바마를 당선에 이끈 것은 선거전 참모를 역임한 &lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;Marshall Ganz(마샬 간츠)였다. 당시 그는 Public Narrative(퍼블릭 내러티브)와 Community Organizing(커뮤니티 오거나이징)이라는 수법을 선거전과 연설에 집어 넣는다. 이는 &quot;Self Us Now&quot;이론으로도 말한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;오바마는 &quot;나는 이런 인생을 살아왔다&quot;와 &quot;조그마한 이야기&quot;를 주장하는 부분부터 이야기 시작한다. 이는 3가지로 나뉜다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Story of Self : 내가 여기에 있는 이유를 이야기한다&lt;/li&gt;
&lt;li&gt;Story of Us : 우리가 여기에 있는 이유를 대중에게 던진다&lt;/li&gt;
&lt;li&gt;Story of Now : 지금 행동해야하는 이유를 주장한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;대통령 후보의 탄생 과정이라는 &quot;타인의 이야기&quot;를 잘 풀어나간다. 여기서, &quot;Self, Us, Now&quot;이론에서 인생이란 프로세스를 공유하는 사이에 자신 속에 있는 스토리가 다른 타인의 스토리와 점점 겹쳐져 간다. 이를 풀어서 이야기하자면, &quot;나와 너 사이에는 공통점이 있다. 그 공통점을 계기로 연대하며 모두와 같이 무언가를 일으키자&quot;라는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;자신의 프로세스(살아온 궤적)를 열어나가며 공유하는 것으로, 개개인의 열광이 집단의 열광으로 넓어져 갔다. 하지만, 한 명의 리더 아웃풋에 의해 큰 사회 변혁이 갑자기 일어나는 것은 아니다. 여기서 오바마 대통령의 수법은 완전히 사람이 프로세스에 공감하는 매커니즘을 파악하고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1700974086926&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;堀江貴文 - Wikipedia&quot; data-og-description=&quot;出典: フリー百科事典『ウィキペディア（Wikipedia）』 堀江 貴文（ほりえ たかふみ、1972年〈昭和47年〉10月29日 - ）は、日本の実業家・著作家（書籍・動画）・政治活動家・投資家[2]・タレン&quot; data-og-host=&quot;ja.wikipedia.org&quot; data-og-source-url=&quot;https://ja.wikipedia.org/wiki/%E5%A0%80%E6%B1%9F%E8%B2%B4%E6%96%87&quot; data-og-url=&quot;https://ja.wikipedia.org/wiki/%E5%A0%80%E6%B1%9F%E8%B2%B4%E6%96%87&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bnPVSP/hyUCeCPGXv/1flNZ893iYn3a5MsI0g190/img.jpg?width=1200&amp;amp;height=1600&amp;amp;face=457_385_782_740,https://scrap.kakaocdn.net/dn/0mGRM/hyUChffziw/4BGGw3tQPoav4lUAMF49zk/img.jpg?width=800&amp;amp;height=1067&amp;amp;face=308_271_506_487,https://scrap.kakaocdn.net/dn/4F3r4/hyUB9nZpV0/Tat1kycuMUPfAevWvkYKsk/img.jpg?width=640&amp;amp;height=853&amp;amp;face=261_219_411_382&quot;&gt;&lt;a href=&quot;https://ja.wikipedia.org/wiki/%E5%A0%80%E6%B1%9F%E8%B2%B4%E6%96%87&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ja.wikipedia.org/wiki/%E5%A0%80%E6%B1%9F%E8%B2%B4%E6%96%87&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bnPVSP/hyUCeCPGXv/1flNZ893iYn3a5MsI0g190/img.jpg?width=1200&amp;amp;height=1600&amp;amp;face=457_385_782_740,https://scrap.kakaocdn.net/dn/0mGRM/hyUChffziw/4BGGw3tQPoav4lUAMF49zk/img.jpg?width=800&amp;amp;height=1067&amp;amp;face=308_271_506_487,https://scrap.kakaocdn.net/dn/4F3r4/hyUB9nZpV0/Tat1kycuMUPfAevWvkYKsk/img.jpg?width=640&amp;amp;height=853&amp;amp;face=261_219_411_382');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;堀江貴文 - Wikipedia&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;出典: フリー百科事典『ウィキペディア（Wikipedia）』 堀江 貴文（ほりえ たかふみ、1972年〈昭和47年〉10月29日 - ）は、日本の実業家・著作家（書籍・動画）・政治活動家・投資家[2]・タレン&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ja.wikipedia.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;堀江貴文(호리에 타카후미)는 &quot;Me와 We와 Now&quot;라는 구성으로 분석했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Me : 자신의 이야기를 하여 거리를 줄인다&lt;/li&gt;
&lt;li&gt;We : 공통점을 발견하여 연대감을 만든다&lt;/li&gt;
&lt;li&gt;Now : 자신이 하고싶은 것을 설명한다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;System 1, System 2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 추가적으로 노벨 경제학상 수상자, Daniel &lt;span style=&quot;background-color: #ffffff; color: #373a3c; text-align: start;&quot;&gt;Kahneman(다니엘 카너먼)로 넘어간다. 저자는 다니엘 카너먼의 인간 생각 분류 모형 System 1과 System 2를 이야기한다. System 1은 Narrative Heart(저자는 &quot;감정적인 뇌&quot;로 번역했다)와 System 2는 Strategy Head(저자는 &quot;논리적인 뇌&quot;로 번역했다)를 이야기한다. 그러면서 인간이 행동을 일으킬 때는 사실 논리적인 System2가 아닌, 직감적이고 감정적인 System1에 따른다고 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #373a3c; text-align: start;&quot;&gt;즉, 두근거림을 공유하고 설렘이 움직이는 감정적인 뇌에 접근하는 것이 효과적이라고 한다. 그리고 이러한 감정적인 뇌에 찌릿하고 오는 것은 Logic이 아닌, Story이며 Narrative라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #373a3c; text-align: start;&quot;&gt;Signature Story&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 David A. Aaker(데이비드 아커)의 이야기를 하며 시작한다. &quot;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Creating Signature Stories : Strategic Messaging that Persuade, Energizes, and Inspires&quot;라는 책에서 브랜드에게 있어 중요한 것은 시그니처 스토리라고 이야기한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;기업이나 서비스를 상징하는 눈에 띄는 스토리를 철저하게 내세우면 브랜드는 고객 마음 속 깊은 곳 까지, 깊이 찌르게 된다고 한다. 이는 창업자 스토리만이 아니다, 직원이라면 거래처나 고객쪽과의 이야기가 더 리얼할 것이다. 즉, &quot;브랜드가 가진 고집 혹은 철학&quot;에 일치하는 스토리인가 아닌가 하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;이는 창작된 이야기가 아닌, 리얼한 이야기를 보여주고 잘 가꾸면 되는 것이다. 전달 방식에 있어서, &quot;전달되어 간다&quot;보다는 &quot;전달되어 온다&quot;라는 같이 걸어가고 싶은 스토리나 내러티브를 언어화하는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1701086067178&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;佐藤尚之 - Wikipedia&quot; data-og-description=&quot;出典: フリー百科事典『ウィキペディア（Wikipedia）』 佐藤 尚之（さとう なおゆき、1961年6月1日 - ）は、日本・東京都出身のコミュニケーション・ディレクター。 株式会社電通でコピーライ&quot; data-og-host=&quot;ja.wikipedia.org&quot; data-og-source-url=&quot;https://ja.wikipedia.org/wiki/%E4%BD%90%E8%97%A4%E5%B0%9A%E4%B9%8B&quot; data-og-url=&quot;https://ja.wikipedia.org/wiki/%E4%BD%90%E8%97%A4%E5%B0%9A%E4%B9%8B&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ja.wikipedia.org/wiki/%E4%BD%90%E8%97%A4%E5%B0%9A%E4%B9%8B&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ja.wikipedia.org/wiki/%E4%BD%90%E8%97%A4%E5%B0%9A%E4%B9%8B&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;佐藤尚之 - Wikipedia&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;出典: フリー百科事典『ウィキペディア（Wikipedia）』 佐藤 尚之（さとう なおゆき、1961年6月1日 - ）は、日本・東京都出身のコミュニケーション・ディレクター。 株式会社電通でコピーライ&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ja.wikipedia.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;firstHeading&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;佐藤尚之(사토 나오유키)는 3가지 업그레이드로 표현했다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공감 =&amp;gt; 열광&lt;/li&gt;
&lt;li&gt;애착 =&amp;gt; 유일무이&lt;/li&gt;
&lt;li&gt;신뢰 =&amp;gt; 응원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 것이 쌓이면 &quot;Community Takes All&quot;이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&quot;사람을 위한다&quot;는 마음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 프로세스 이코노미를 움직이는 엔진은 &quot;이타심&quot;이라고 한다. 자신의 사리사욕으로는 공감이 생겨나지 않으며, 인간의 뇌에는 &quot;누군가를 위해 행동하고 싶다&quot;란 이타적 정신과 행동 양식이 새겨져 있다고 한다. 물욕이나 권력욕이 채워지고 소속욕구나 승인 욕구가 채워졌다고 해도, 사람은 사실 만족하지 못한다고 한다. 그러면서 최종적으로 &quot;사람을 위해 무언가 하고 싶다&quot;라는 &quot;궁극적 욕망&quot;에 이른다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그와 동시에, 아메리카 캘리포니아 대학의 연구를 소개하는데, 감사의 종류에는 두가지가 있다는 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;은혜적 감사 (Doing의 감사) - 누군가에게 도움을 받거나, 무언가를 받는 등의 소위 Doing에 의한 감사.&lt;/li&gt;
&lt;li&gt;보편적 감사 (Being의 감사) - 감사의 기분을 언제나 느끼고 있는 마음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;하이네켄의 최고 CM&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=dKggA9k8DKw&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/VEf2A/hyUFaso8dP/T7yHaHUFjF7GrMrRnWaf11/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=246_144_472_390&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;Heineken   Worlds Apart    OpenYourWorld 1&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/dKggA9k8DKw&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 하이네켄의 광고를 마지막으로 예시로써 들며 2장을 끝낸다. 프로세스를 공유하면서, 인간은 아주 다른 정책이나 사상을 가진 타자에게서 친근함을 기억하며 &quot;이런 사람도 내 동료야&quot;라고 느끼게 된다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SNS사회에서는 어딜 가든 상대를 논파하지 않으면 기분이 풀리지 않는 세상이라고 한다. 그러면서 논리와 논리가 부딪힌다고 한다. 그리고 그런 상대의 주장이나 사상은 쉽게 바뀌지 않는다고 한다. 세계는 복잡하며 어딜가든 정의가 있는 편이다. 그럼에도 상대방을 강제적으로 굴복시킨다면 결국 싸움이나 전쟁이 될 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이네켄의 CM도 그러한 맥락에서 프로세스로 이어지는 절실함이 전달되는 아주 좋은 교재라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2장이 끝났습니다. 3장으로 넘어가겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFePi9/btsA3zT0n41/stpXbQZCrPof4efuM47cPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFePi9/btsA3zT0n41/stpXbQZCrPof4efuM47cPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFePi9/btsA3zT0n41/stpXbQZCrPof4efuM47cPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFePi9%2FbtsA3zT0n41%2FstpXbQZCrPof4efuM47cPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;863&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Product</category>
      <category>pm</category>
      <category>경영</category>
      <category>공부</category>
      <category>독서</category>
      <category>일본</category>
      <category>일본어</category>
      <category>책</category>
      <category>프로세스이코노미</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/50</guid>
      <comments>https://kimkani.tistory.com/50#entry50comment</comments>
      <pubDate>Tue, 28 Nov 2023 16:59:31 +0900</pubDate>
    </item>
    <item>
      <title>[프로세스 이코노미] 마케팅 관점에서의 프로세스 - 일본판 공부 노트 PM전환기</title>
      <link>https://kimkani.tistory.com/49</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duVoYM/btsAJwKeEpE/LDh6M4djsry9KTCMS49XGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duVoYM/btsAJwKeEpE/LDh6M4djsry9KTCMS49XGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duVoYM/btsAJwKeEpE/LDh6M4djsry9KTCMS49XGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduVoYM%2FbtsAJwKeEpE%2FLDh6M4djsry9KTCMS49XGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;579&quot; height=&quot;579&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;なぜアウトプットよりもプロセスに価値が生まれるのか？&lt;br /&gt;왜, 아웃풋보다도 프로세스에서 가치가 생기는 것인가?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필립 코틀러(Philp Kotler)의 마케팅 4.0의 예시를 들며 마케팅 관점에서 프로세스 이코노미를 풀어나간다. - 사실 현 시점에서는 5.0까지 나왔다. - 그러면서 간략하게 1.0부터 4.0까지의 이야기를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;마케팅 1.0 : 제품 중심 마케팅 - 기능을 통한 가치 전달&lt;/li&gt;
&lt;li&gt;마케팅 2.0 : 고객 지향 마케팅 - 차이(&lt;span style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-lang=&quot;hj&quot; data-type=&quot;arken&quot; data-hook=&quot;tip&quot;&gt;差&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-lang=&quot;hj&quot; data-type=&quot;arken&quot; data-hook=&quot;tip&quot;&gt;異&lt;/span&gt;)를 통한 전달&lt;/li&gt;
&lt;li&gt;마케팅 3.0 : 가치 주도 마케팅 - 참가 가치를 통한 전달&lt;/li&gt;
&lt;li&gt;마케팅 4.0 : 경험 가치 지향 마케팅 - 공동 가치 창조를 통한 전달&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 1.0에서는 유저는 일단 필요한 제품이 있으면 그것 만으로도 기뻐한다. 일본의 고도 경제 성장기에는 &quot;3중신기&quot;라는 절묘한 캐치프레이즈를 붙여서, &quot;냉장고, 세탁기, 그리고 흑백 티비&quot;를 왕창 팔았다. 즉, &quot;그 상품이 있다면 어느정도 생활이 풍부해질까?&quot;라는 상품 중심의 마케팅이 이루어졌다. 하지만, 대량 생산에 의해 상품이 틈새까지 파고 들면서, 이 마케팅 1.0을 하는 것만으로는 물건이 팔리지 않게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 경제적으로 유복해진 사람들이 &quot;저 사람이 원하는 것과 내가 원하는 것이 다르다&quot;는 것을 느끼면서, 예를 들면, 아이스 아메리카노를 많이 마시는 사람은 얼음이 나오는 냉장고를 더 원할 것이고, 미세먼지에 예민한 사람은 그에 특화된 공기 청정기를 원할 것이다. 즉, 주제별로 각각 세밀하게 고객을 타겟팅해야 물건이 팔리는 시대가 당도한 것이다. 여기서 마케팅 2.0이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 점점 고객만족도의 형태가 변화한다. &quot;그저 막 쓰기 좋은 것으로는 부족하다. 회사가 추구하는 미션이나 태도가 중요해&quot;라는 메이커의 자세를 엄하게 주시하고 있다. 미국에서 인종 차별이 사회 문제로 대두되었을 때, 차별이나 편견에 반대하는 기업이 메시지를 즉시 내놓는다. 거기에 쿨함을 느낀 유저가 상품을 사서 기업 활동을 소비하는 것으로 응원한다. 이는 곧, 사회적인 메시지를 내기 시작하는 마케팅 3.0으로 바꿔야 하는 신호이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀러는 더 나아가 마케팅 4.0을 제창한다. 제품이나 서비스가 가진 &quot;기능 가치&quot;는 이미 빛을 잃었고, &quot;감정 가치&quot;나 &quot;참가 가치&quot;가 반대 편에서 빛을 발하고 있는 것이다. 즉, 자기 자신이 가치 창조에 참가하고 싶다고 생각하기 시작한 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 마케팅 1.0, 2,0, 3.0 그리고 4.0을 정리하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot; rowspan=&quot;2&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;제품 중심 마케팅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;고객 지향 마케팅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;가치 주도 마케팅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;경험 가치 지향 마케팅&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;마케팅 1.0&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;마케팅 2.0&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;마케팅 3.0&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;마케팅 4.0&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;목적&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;상품 판매 및 보급&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;고객 만족&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;가치있는 체험&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;고객의 자기 실현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;기술적 배경&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;대량 생산 기술&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;정보 통신 기술&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;소셜 미디어&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;빅 데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;고객 니즈&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;소유 욕구&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;성장 욕구&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;참가 욕구&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;창조 욕구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;기업 행동&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;제품 개발 4Ps&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;소비자 조사 제품 차별화 STP&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;브랜드 커뮤니티&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;커스터머 저니 AIDA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;제공 가치&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;기능적 가치&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;차이적 가치&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;참가 가치&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;공동 창조 가치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;b&gt;고객과의 소통&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;광고 및 판매 촉진&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;홈페이지, 메일&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;참가형 SNS&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;공동 창조형 SNS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개인적인 생각&lt;br /&gt;&lt;br /&gt;참가형 SNS와 공동 창조형 SNS에 집중할 필요가 있을 것 같다. 참가형 SNS는 일대일 혹은 일대다 소통이라면 공동 창조형 SNS는 다대다 소통이라고 생각한다. 대표적인 참가형 SNS는 인스타그램, 트위터가 있을 것이다. 창조형 SNS에는 디스코드가 대표적일 것이다. 즉 1인이 운영하느냐, 아니면 다수가 참여해서 커뮤니티를 이루느냐의 차이일 것 같다.&lt;br /&gt;&lt;br /&gt;최근에는 이런 공동 창조형 SNS에 기존의 많은 참가형 SNS들이 힘을 주는 것 같다. 대표적으로 X(구 트위터)에 커뮤니티기능이라 든지, 스페이스 기능이 도입된 것과, 인스타그램 공지 커뮤니티 등이 생긴 것이다. 즉, 일대다, 혹은 일대일 소통으로는 한계를 느끼고 있다는 반증이 아닐까 생각한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마케팅 관점에서 저자는 프로세스 이코노미를 풀어나간 후, 기술 관점에서 프로세스 이코노미를 풀어나간다. 일단, 저자는 테크놀로지의 발전으로 인한 아웃풋에 아주 가까이 접근 가능하며, 유저는 아웃풋이 아닌 프로세스자체에 돈을 내게 되었다고 다시 한 번 말하며, &quot;6D&quot;를 이야기한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6D는 피터 디아만디스와 스티븐 코틀러가 지은 &quot;The Future is Faster than You Think(한국어 제 : 컨버전스 2030)&quot;에서 제시한 내용이다. 여기서 AI의 진화에 의해 인간의 지능을 돌파하는 특이점이 도래한다고 하며, 그 격동의 시대에 제품은 6D가 될 것이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 6가지 D에 대해서 이야기해 보자면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Digitized(디지털화)&lt;/li&gt;
&lt;li&gt;Deceptive(현혹적)&lt;/li&gt;
&lt;li&gt;Disruptive(파괴적)&lt;/li&gt;
&lt;li&gt;Demonetized(비 수익화 - 화폐가 가치를 잃음)&lt;/li&gt;
&lt;li&gt;Dematerialized(비 물질화)&lt;/li&gt;
&lt;li&gt;Democratized(민주화)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 디지털화이다. 아마 많은 사람 및 기업들이 피부에 가장 느끼는 분야일 것이다. iPad와 iPhone의 등장으로 콘텐츠 사업은 가장 디지털화에 접어들었다. 특히 아주 예전에는 종이나 책으로 밖에 없던 것이었다. 기존에는 영화관에 가서 영화를 보거나, 대여점에 가서 DVD를 빌렸다면, 이제는 넷플릭스나 디즈니 플러스 등으로 대체되었다. 그 외에도 여러 정보들까지 - 인간의 DNA 까지 - 디지털화 되었다. 그 외에도 현금 없는 사회 등이 도래하기도 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개인적인 생각&lt;br /&gt;&lt;br /&gt;다양한 AI, 특히 ChatGPT와 DALL・E의 등장으로 인한 정보 탐색의 시간이 줄어들고, 다양한 정보들을 손쉽게 찾을 수 있게 되었다. 하지만, 이런 인공지능 - 딥러닝을 필두로 한 - 제품들은 데이터의 소유권, 즉 데이터의 민주화와 민감성에 있어서 조금은 주의가 필요하지 않을까 생각한다.&lt;br /&gt;&lt;br /&gt;20년도에 들어서, 다양한 인공지능 모델들이 등장하지만, 그 이면에는 데이터가 가장 중요하다는 것이 드러났다. 생각해보면, 이런 데이터는 지금까지 사람들이 그다지 중요하게 생각하지 않았다. 좀 더 집중적으로 생각하자면, 데이터의 소유권에 대해 크게 관심을 갖지 않았다. 물론 이는 돈이 될 것이라고 생각하지 못했기 때문이다. 결국 미래에는 데이터에 대한 러다이트 운동이 일어나지 않을까 하는 생각도 든다. 즉, 데이터를 단순 인공지능 학습을 위해 더이상 생산하지 않는 활동이 이루어질지도 모른다는 생각이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 저자는 6D와 특이점을 중심으로 미래에 대한 프로세스 이코노미를 엮으며 이야기한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1장이 끝났습니다. 이제 2장으로 넘어가겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xIEJw/btsAQeJzpmE/eI5Ecjf6z3cqVTQstmQA20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xIEJw/btsAQeJzpmE/eI5Ecjf6z3cqVTQstmQA20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xIEJw/btsAQeJzpmE/eI5Ecjf6z3cqVTQstmQA20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxIEJw%2FbtsAQeJzpmE%2FeI5Ecjf6z3cqVTQstmQA20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;716&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Product</category>
      <category>pm</category>
      <category>경제</category>
      <category>공부</category>
      <category>과정</category>
      <category>미래</category>
      <category>원서</category>
      <category>일본어</category>
      <category>일서</category>
      <category>프로덕트</category>
      <category>프로세스이코노미</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/49</guid>
      <comments>https://kimkani.tistory.com/49#entry49comment</comments>
      <pubDate>Fri, 24 Nov 2023 19:05:36 +0900</pubDate>
    </item>
    <item>
      <title>[프로세스 이코노미] 페르소나를 위한 소비 - 일본판 공부 노트 PM전환기</title>
      <link>https://kimkani.tistory.com/48</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1687&quot; data-origin-height=&quot;1687&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4uw72/btsAzVhORG4/5CRmGkMir8GfB5kjEdNTm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4uw72/btsAzVhORG4/5CRmGkMir8GfB5kjEdNTm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4uw72/btsAzVhORG4/5CRmGkMir8GfB5kjEdNTm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4uw72%2FbtsAzVhORG4%2F5CRmGkMir8GfB5kjEdNTm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;529&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1687&quot; data-origin-height=&quot;1687&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;所属欲求を満たすための消費活動&lt;br /&gt;소속 욕구를 채우기 위한 소비 활동&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 지금의 소비자는 물질적인 것보다 내면적인 것을 중시하며, &quot;도움이 된다&quot;보다는 &quot;의미가 있음&quot;에 가치를 느낀다고 한다. 이는 곧 자신의 정체성(아이덴티티)를 지지해주는, 자신의 소속욕구까지 채워주는 것을 브랜드에 요구하기 시작했다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래라면 사람들은 필요에 의해 자신이 살고 있는 현실 장소의 지역 커뮤니티에 소속되어 있다. 하지만 점점 근처 사람들과 교류가 어려워지고, 도시 중심부는 많은 사람들이 밀집해있지만, 언제든지 모일 수 있는 리얼한 장소가 어디든 찾기가 어려워졌다. 그리고 정체성을 채워주었던 가족, 이웃, 회사는 더이상 제 역할을 못하며, 기존 관념이었던 &quot;어딘가 그룹에 소속하고 싶다&quot;는 소속욕구를 채우는 것을 소비활동에도 요구하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 정체성을 지지하기 위한 소비 활동에는 현대의 시대상도 한 몫했다고 저자는 보고 있다. 일본의 한 방송을 예로들며 저자는 설명한다. 한 예능 방송에서는 가격차가 나는 두 제품 A와 B를 보거나, 듣거나, 만지거나 하는 등 하여 어느쪽이 더 좋은지 선택한다. 여기서 A를 다수가 골랐는데, 자신은 B를 고른 경우 자신의 &quot;선택이 잘못되었나?&quot; 생각할 수 있다. 즉, 많은 동료들이 같이 걷는 길을 걸으면 안심감을 주는 것과 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리들은 보통 살아가면서 비슷한 사람들과의 커뮤니티에 속하게 된다. 하지만, 그러한 장이 사라지면서 불안감이 특색이 되어버린다. 또한 사람은 자신이 혼자서 선택할 경우 &quot;이 선택이 옳은가&quot;라는 불안감에 휩싸이게 된다. 또한 이는 &quot;무엇을 위해 살아가는가&quot;, &quot;무엇을 위해 일하는가&quot;같은 막연한 불안이나 고민에 휩싸이게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 시대 배경도 자신의 정체성이 되는 브랜드를 요구하는 이유중 하나가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개인적인 생각&lt;br /&gt;&lt;br /&gt;나의 선택이 옳은가?에 대해 자신의 선택을 옳게끔 만들어주는 브랜드 요구는 굉장히 논리적인 추론이라고 본다. 이는 SNS와 숏폼 영상 발달의 영향도 큰 몫을 하는 것 같다. 특히 한동안 오마카세, 명품 등의 인증 문화 발달로 인해 자신의 행복을 - 여기서 보면 정체성의 자랑도 아닐까 생각한다 - 자랑하는 것을 브랜드를 통해 인증하는 것을 도래시키지 않았는가 하는 생각도 든다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면서 책에서는 종교에 대한 이야기를 꺼낸다. 그러면서 과거에도 미래에 대한 불안이 있었으며, 종교의 &quot;거대한 서사&quot;가 그 불안감 해소를 담당했다 한다. 대표적으로 진언종의 창시자 홍법대사 구카이(弘法大師 空海)의 &quot;同行二人(동행이)&quot;를 예로 든다. 즉 순례자 곁에는 언제나 홍법대사가 항상 있다는 것으로 혼자가 아니라는 것을 의미한다. 이는 안심감을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 현대에 와서 무교인 사람이 많아지며 &quot;거대한 서사&quot;로서의 종교가 제대로 기능하지 못한다. 현대에 와서 이 역할을 브랜드라고 불리는 기업들이 떠맡고 있다고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;オタク化・오타쿠화&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 정체성을 둘 장소가 기업이나, 인플루언서같이 인터넷 상의 커뮤니티로 이동하는 것은 세계적인 추세이다라고 말하며, 이를 일본의 오타쿠화처럼 되어간다고 이야기한다. 그러면서 트위터 상의 계정 이야기를 하면서 일본의 오타쿠 처럼 90년대 후반에 탄생한 누군가와 이어져있는 것이 당연한 Z세대들이라고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFRGYH/btsAJ120NQD/2KkkbTkKCTxsxdGRD93sTK/tfile.svg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFRGYH/btsAJ120NQD/2KkkbTkKCTxsxdGRD93sTK/tfile.svg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFRGYH/btsAJ120NQD/2KkkbTkKCTxsxdGRD93sTK/tfile.svg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFRGYH%2FbtsAJ120NQD%2F2KkkbTkKCTxsxdGRD93sTK%2Ftfile.svg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;449&quot; height=&quot;385&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면서 일본의 세대들을 언급하며 각각의 세대들이 어떻게 인터넷을 접했는지 이야기한다. &quot;단카이 주니어(일본의 제 2차 베이비 붐 세대로 71~74년생)&quot;운 사회인이 되어 회사에 들어가 메일 주소를 가진것으로 인터넷을 접했다고 한다. 그 후 밀레니엄 세대(M세대)는 사회에 진출하기 전 대학에 들어가며 인터넷에 연결되었고, 그 때부터 다양한 재밌는 것들은 인터넷에 있음을 배우기도 했으며, 인터넷에 연결되지 않으면 혼자 남겨질 것이라는 불안감인, FOMO(Fear Of Missing Out) 현상이 나타난다고 한다. 그리고 마지막으로 Z세대의 경우 중학생 시절부터 &quot;중2병&quot;이라는 시기를 인터넷과 거치면서 &quot;인터넷 상에서 자신이 어떻게 보이는가?&quot;가 가치 기준이 되었다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이러한 Z세대는 인터넷에 연결이 안되어 있는 상황이 없기 때문에, 인터넷에 연결된 적이 없음을 경험 혹은 기억하지 못하는 세대다. 이를 JOMO(Joy Of Missing Out)이라고 하며, 오히려 남겨지는 것을 즐기는 세대이다. 더 어린 세대는 모르는 사람과 연결되는 것을 꺼리지 않는다고 한다. 더 어린 세대 - 여기서는 알파세대 라고 부른다 - 는 게임을 통해, 판단력을 기르는 리더십까지 기른다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개인적인 생각&lt;br /&gt;&lt;br /&gt;다만 이는 숏폼 영상의 등장으로 모든 것이 바뀌었다고 생각한다. 대부분의 세대가 두가지로 나뉘었다고 생각한다. &quot;숏폼을 이용하는 세대&quot;와 &quot;숏폼을 향유하는 세대&quot;이다. 즉, 숏폼을 통해 &quot;숏폼을 향유하는 세대&quot;를 &quot;이용하는 세대&quot;로 나뉜다고 본다. 숏폼의 등장으로 오히려 판단력이라든지, 순간적인 쾌락등이 늘어났으며, 정보의 량과 질은 역전이 되어간다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;이 기회를 찰나의 순간에 잘 잡으면 오히려 숏폼을 이용해서 비즈니스 모델을 꾸리며, 마케팅 비용도 절감시키고, 돈을 창출할 수 있는 기회를 만들 수 있지 않을까 하는 생각은 한다. 예를 들면, 숏폼을 통해 짧은 시간동안 여러 동영상을 노출시켜 마케팅을 할 수 있을 것이라는 생각이 든다.&lt;br /&gt;&lt;br /&gt;이는, 숏폼이 마케팅 적으로는 훌륭한 수단으로 자리잡을 수 있겠다라는 개인적인 생각이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uJn6y/btsAJDBnGxT/dZWnVlUt6ywTC7SSONKIqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uJn6y/btsAJDBnGxT/dZWnVlUt6ywTC7SSONKIqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uJn6y/btsAJDBnGxT/dZWnVlUt6ywTC7SSONKIqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuJn6y%2FbtsAJDBnGxT%2FdZWnVlUt6ywTC7SSONKIqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;624&quot; height=&quot;832&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Product</category>
      <category>pm</category>
      <category>PO</category>
      <category>UI</category>
      <category>UX</category>
      <category>경영</category>
      <category>경제</category>
      <category>마케팅</category>
      <category>제품</category>
      <category>프로덕트</category>
      <category>프로세스이코노미</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/48</guid>
      <comments>https://kimkani.tistory.com/48#entry48comment</comments>
      <pubDate>Tue, 21 Nov 2023 20:11:54 +0900</pubDate>
    </item>
    <item>
      <title>[프로세스 이코노미] 슈퍼 퀄리티 혹은 슈퍼 커뮤니티 - 일어판 공부 노트 PM전환기</title>
      <link>https://kimkani.tistory.com/47</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;981&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YrSPL/btsAxfOWaS2/TibRVPlDK2iYQRl1hg5xrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YrSPL/btsAxfOWaS2/TibRVPlDK2iYQRl1hg5xrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YrSPL/btsAxfOWaS2/TibRVPlDK2iYQRl1hg5xrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYrSPL%2FbtsAxfOWaS2%2FTibRVPlDK2iYQRl1hg5xrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;534&quot; height=&quot;534&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;981&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;なぜアウトプット（完成形）と同じようにプロセス（過程）にも価値が出るのか。&lt;br /&gt;왜 아웃풋(완성형)과 똑같이 프로세스(과정)에서도 가치가 나는 것일까.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 자신이 정의한 세대를 언급한다. 이름하여 &quot;마르지 않는 세대(乾けない世代)&quot;, 저자는 30대 이하로 말하며 태어났을 때 부터 가정에 대부분의 전자기기 - 티비, 휴대폰, 등등 - 가, 물질적으로 배고픔을 경험해본 적이 없는 세대라고 말한다. 그 이전 세대는 &quot;말라 있는 세대(乾いている世代)&quot;라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;nbsp;개인적인 생각&lt;br /&gt;&lt;br /&gt;일본에서는 사토리 세대, 유토리 세대 등으로 불리긴 하지만, 이 책이 출간된 21년도를 기준으로 보아 대충 한국에서 언급하는 MZ세대를 칭하는 것 같다. 다만 이러한 &quot;물질적인 부족함이 없음&quot;이라는 말에 조금은 위험성이 있을 것 같다. 결국 물질적인 부족성이 없다는 것은 내가 현재 가지고 있는지 없는지 여부가 아닌, 사회에 구매할 수 있는 물건의 여부일 듯 하다. 양극화의 심화가 계속되며 물질적인 풍족함 여부는 결국 양극적으로 나타나기 \때문이다.&lt;br /&gt;&lt;br /&gt;다만 저번에 읽었던 것처럼, 정보의 접근이라 든지, 정보의 습득 가능성은 더 높아진 것이 맞을 것 같다. 즉, 나는 마르지 않는 세대 - 한국의 대충 말하는 MZ세대 - 에게 있어 이 세대는 물질적인 풍족함보다, 매체의 풍족함이 갖추어진 세대가 아닐까 생각한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 마틴 셀리그먼(Martin Seligman)의 5가지 행복의 조건, PREMA(Positive emotion, Engagement, Relationship, Meaning, Accomplishment)를 든다. 일역과 한역을 대충 섞어서 해보자면, &quot;긍정적 감정, 몰두, 긍정적 관계, 의미, 성취 혹은 달성&quot;이다. 그러면서 말라있는 세대와 마르지 않는 세대에게 중요시되었고 중요시되는 조건들을 이야기한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 말라있는 세대는 &quot;성취&quot;와 &quot;긍정적 감정 - 책에서는 단순히 쾌락으로 표현했다&quot;으로 대표된다고 한다. &quot;높은 보수나 출세라는 달성&quot;과 &quot;미식과 물욕이라는 쾌락&quot;이라는 두가지를 포인트로 들어 &quot;성공한 사람&quot;이라 불리기 위해 한계단 더 높은 생활을 손에 넣는 세대라고 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마르지 않는 세대는 &quot;풍족한 사회&quot;에서 자라났기 때문에, 달성이나 긍정적 감정(쾌락)을 채운다는 것에 방점을 두지 않고, 또한 그에 대한 &quot;배고픔, 부족함(飢え)&quot;도 없다고 한다. 그리고 정신적인 요소가 강하다고 평가한다. 즉, 나머지 3요소인 &quot;몰두, 긍정적 관계, 의미&quot;에 대해 좀 더 방점을 둔다고 한다. 그리고 이러한 중요 가치관은 물질적이 것이 아닌, 내면적인 것을 중시하게 되며, 필연적인 소비에서 회사의 비전이나, 회사의 주인, 아웃풋을 만드는 사람들이 어떻게 살아가는지에 공감한다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이를 통해 프로세스를 공유하는 것 자체에 가치를 느끼기 시작했다고 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개인적인 생각&lt;br /&gt;&lt;br /&gt;다만 이는 너무 성급한 끼워 맞추기가 아닐까 생각한다. 명품 소비에 대한 열기가 지금은 꺾였지만, 코로나 시기만 해도 굉장히 열기가 뜨거웠고, 그 외에도 다양한 매체 - 인스타그램, 유튜브를 위시로 한 브이로그 같은 생활 밀착형 SNS - 에서 자신의 정신적 만족도 아니고 관계를 통한 만족도 아니며, 더 나아가 좋은 의미를 가지는 소비를 통한 것도 부여되지 않았다. 그저 수단으로 사용하여 오히려 물질적 부족함을 커버하는 느낌의 유행이 돌기도 하였다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;즉, 내가 생각하는 바는, 행복의 3요소를 보여주는 척 하면서, 저자가 말한 &quot;말라있는 세대&quot;의 &quot;성취, (쾌락에 가까운) 긍정적 감정&quot;을 반복하는 것과 다름 없다. 물론 차이점이라면, 그때는 부족함을 인정하며 부족함을 채우기 위한 과정이었다면, 지금은 반대로 부족함을 부인하며 부족함을 채우는 것이 아닌가 하는 생각이 든다.&lt;br /&gt;&lt;br /&gt;기업들은 이러한 면을 유행이라는 것으로 파고들어 마케팅을 했다. 과시욕이란 유행을 포인트로 잡아 돈을 벌지 않았을까 하는 생각도 든다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1700297000153&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;book&quot; data-og-title=&quot;ニュータイプの時代 新時代を生き抜く24の思考・行動様式&quot; data-og-description=&quot;ニュータイプの時代 新時代を生き抜く24の思考・行動様式&quot; data-og-host=&quot;www.amazon.co.jp&quot; data-og-source-url=&quot;https://www.amazon.co.jp/%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%BF%E3%82%A4%E3%83%97%E3%81%AE%E6%99%82%E4%BB%A3-%E6%96%B0%E6%99%82%E4%BB%A3%E3%82%92%E7%94%9F%E3%81%8D%E6%8A%9C%E3%81%8F24%E3%81%AE%E6%80%9D%E8%80%83%E3%83%BB%E8%A1%8C%E5%8B%95%E6%A7%98%E5%BC%8F-%E5%B1%B1%E5%8F%A3-%E5%91%A8/dp/447810834X&quot; data-og-url=&quot;https://www.amazon.co.jp/dp/447810834X/ref=tsm_1_fb_lk&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/N3gx8/hyUylBtNuC/D5Dq7dvvZaQze1nuC2KOLK/img.png?width=600&amp;amp;height=315&amp;amp;face=354_195_377_220,https://scrap.kakaocdn.net/dn/34k8b/hyUyAZH3UU/0sFzixUiolAGiPnpKTpBIK/img.png?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/b7C99b/hyUytM2qxp/jmpKVN7aKGt9R4mvjQmOk0/img.jpg?width=308&amp;amp;height=445&amp;amp;face=235_301_283_353&quot;&gt;&lt;a href=&quot;https://www.amazon.co.jp/%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%BF%E3%82%A4%E3%83%97%E3%81%AE%E6%99%82%E4%BB%A3-%E6%96%B0%E6%99%82%E4%BB%A3%E3%82%92%E7%94%9F%E3%81%8D%E6%8A%9C%E3%81%8F24%E3%81%AE%E6%80%9D%E8%80%83%E3%83%BB%E8%A1%8C%E5%8B%95%E6%A7%98%E5%BC%8F-%E5%B1%B1%E5%8F%A3-%E5%91%A8/dp/447810834X&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.amazon.co.jp/%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%BF%E3%82%A4%E3%83%97%E3%81%AE%E6%99%82%E4%BB%A3-%E6%96%B0%E6%99%82%E4%BB%A3%E3%82%92%E7%94%9F%E3%81%8D%E6%8A%9C%E3%81%8F24%E3%81%AE%E6%80%9D%E8%80%83%E3%83%BB%E8%A1%8C%E5%8B%95%E6%A7%98%E5%BC%8F-%E5%B1%B1%E5%8F%A3-%E5%91%A8/dp/447810834X&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/N3gx8/hyUylBtNuC/D5Dq7dvvZaQze1nuC2KOLK/img.png?width=600&amp;amp;height=315&amp;amp;face=354_195_377_220,https://scrap.kakaocdn.net/dn/34k8b/hyUyAZH3UU/0sFzixUiolAGiPnpKTpBIK/img.png?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/b7C99b/hyUytM2qxp/jmpKVN7aKGt9R4mvjQmOk0/img.jpg?width=308&amp;amp;height=445&amp;amp;face=235_301_283_353');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;ニュータイプの時代 新時代を生き抜く24の思考・行動様式&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ニュータイプの時代 新時代を生き抜く24の思考・行動様式&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.amazon.co.jp&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;야마구치 슈가, 자신의 책 &quot;뉴타입 시대, 신시대를 살아남는 24가지 사고, 행동양식&quot;에 대해 언급하며 책에서 &quot;도움이 되다&quot;보다는 &quot;의미가 있다&quot;에 가치가 있다고 지적한 것을 언급한다. 즉, 그저 생활 필수품 처럼 도움이 되는 상품이 아닌, 나 다운 인생을 살기 위해 특별한 의미를 주는 것이 가치가 높다는 것으로 본다. 그러며 인용문을 드는데, 간략하게 요약하자면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTiEwG/btsAuPKAypH/JA3y0kvf0foLv5C86K8bt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTiEwG/btsAuPKAypH/JA3y0kvf0foLv5C86K8bt1/img.png&quot; data-alt=&quot;도요타, 닛산 부분을 한국입장에서 현대, 기아로 바꿨습니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTiEwG/btsAuPKAypH/JA3y0kvf0foLv5C86K8bt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTiEwG%2FbtsAuPKAypH%2FJA3y0kvf0foLv5C86K8bt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;320&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;도요타, 닛산 부분을 한국입장에서 현대, 기아로 바꿨습니다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;nbsp;다양한 상품이 진열되어 있는 편의점, 편의점의 진열장에는 한 품종에 한두 제품 밖에 안놓여져 있다. 하지만, 한 품종에 수십 제품이 놓여져 있는 것이 있다. 바로 담배이다. &quot;도움은 되지 않지만, 의미가 있는&quot;물건이다. 예를 들어, 세븐스타같은 담배를 애연하는 사람은 세븐스타만 핀다. 다른 담배도 있지만, 세븐스타가 유일무이하며 대체불가능한 존재이기 때문이다. 자동차 업계도 비슷하다. 위의 그림처럼, 기능성이 우수하면 하나로 - 차 한 대 -로 충분하다. 하지만 스토리가 있는 차, 페라리나 람보르기니같은 것들은 기술력 하나만으로 충분하지 않다. 하지만 다양성이 충족되면 그 가치는 굉장히 높아진다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 예시를 들면서, 이러한 &quot;도움이 된다&quot;에 방점을 찍은 상품이나 서비스는 승자 독식, 즉 의자가 한 개일 수 밖에 없는 극단적인 경쟁양상을 거쳐야 하며, 중간을 가는 경우에는 살아남을 수 없다고 한다. 그리고 다음 장에서 굉장히 재밌는 제목으로 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;글로벌&amp;amp;하이 퀄리티인가 아니면 로컬&amp;amp;로우 퀄리티인가&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;グローバル・ハイクオリティかローカル・ロークオリティか&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말은 일본의 유명 기업 팀랩의 대표이사, 猪子寿之(이노코 토시유키)가 한 말이라고 한다. 그러면서 GQ Japan 2014년 7월호를 인용해온다. 개인적으로 가장 현시점을 꿰뚫는 말을 하나 가져와보려고 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이후에 도시는 세계에서 경쟁력을 가진 슈퍼 하이퀄리티에 몸을 담은 층이거나, 강한 커뮤니티를 가진 층으로 분단되지 않을까 생각합니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 논리 전개 과정을 정리해 보겠다. 일단, 인터넷의 보급이 한몫했다. 인터넷 보급으로 인해 각 가정에 세계의 제품이 들어와 있으며, 그 제품들은 국산이든 외산이든 퀄리티만 좋으면 사용하게 된다. 그리고 이러한 퀄리티 높은 제품들은 글로벌이 시장이다. 즉, 로컬로 승부하는 제품보다 돈이 쌓이는 속도가 다르다. 그리고 이러한 돈은 다시금 퀄리티를 높이는데 들어간다. 즉, 슈퍼 하이퀄리티가 된다. 그와 반대로 로컬에서는 로우 퀄리티의 제품이 그나마 통하며 그런 제품이 그나마 로컬에서 돈을 모은다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한편으로 인터넷으로 대규모 커뮤니티 형성의 용이함을 이야기한다. 이러한 커뮤니티내에서 아는 지인, 친구 등을 통해서&amp;nbsp; 구하게 된다. 그리고 이런 커뮤니티는 콘텐츠와 서비스가 따라온다. 따라와야 한다. 그래야 커뮤니티의 가치가 다시금 올라간다. 그리고 이러한 콘텐츠와 서비스는 아웃풋이기 보다는 프로세스 자체에 있다. 즉 무언가 만들면서 소통하고, 누군가 만든 것을 구경한다든지, 등의 과정을 밟으며 - 커뮤니케이션이 일어나며 - 가치를 일으킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하이퀄리티 제품과 로우퀄리티 제품은, 점점 글로벌 하이퀄리티 커뮤니티와 그 반대로 극단화된다. 즉 세계 속에서 경쟁하는 제품은 계속해서 퀄리티를 높여가며 수 많은 경쟁자를 제거해야되고, 로컬 커뮤니티는 그럴필요가 없기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개인적인 생각&lt;br /&gt;&lt;br /&gt;이 부분은 현재 다양한 애플리케이션들이 보여주는 행태와 많이 닮았다. 대표적으로 당근마켓을 보자. 현재는 당근이라는 좀더 추상화된 개념으로 바뀌었다. 하지만, 당근이 추구하는 가치는 대부분 로컬 커뮤니티이다. 물론 그렇다고 책에서 말한 것처럼 하이퀄리티가 아닌 것은 아니다. - 일본에도 진출하고 있는 것을 보아, 글로벌 속 로컬 커뮤니티를 꿈꾸는 것 같다. 아무튼 이러한 당근의 행보는 로컬 커뮤니티 내에서, 다양한 시도를 하여 아주 깊숙이 침투하겠다는 야망? 포부를 가지고 있다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;카카오를 보자, 카카오는 로컬&amp;amp;하이퀄리티로 볼 수 있다. 다만, 카카오의 경우 이 경우의 아예 중간에 껴있는 이도저도 못하는 케이스이다. 최근 터진 사건들만 봐도 대충 분위기는 알 수 있다. 쿠팡의 경우는 어떨까? 로우 퀄리티, 로컬적이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;여기서 로우퀄리티와 하이퀄리티를 생각해보자면, 어느정도 기술력의 고도화보다는, 어느정도 비용으로 만족도를 높이는가 인 것 같다. 즉 만족도를 위한 합리적인 비용이다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUjiWZ/btsAxUDTX1z/IKu60nGKNkHMNkkia9zHZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUjiWZ/btsAxUDTX1z/IKu60nGKNkHMNkkia9zHZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUjiWZ/btsAxUDTX1z/IKu60nGKNkHMNkkia9zHZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUjiWZ%2FbtsAxUDTX1z%2FIKu60nGKNkHMNkkia9zHZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Product</category>
      <category>경영</category>
      <category>공부</category>
      <category>공부인증</category>
      <category>독서</category>
      <category>일본</category>
      <category>일본어</category>
      <category>책</category>
      <category>프로덕트</category>
      <category>프로세스이코노미</category>
      <category>필사</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/47</guid>
      <comments>https://kimkani.tistory.com/47#entry47comment</comments>
      <pubDate>Sat, 18 Nov 2023 20:09:11 +0900</pubDate>
    </item>
    <item>
      <title>[프로세스 이코노미] 처음 들어가며 - 일어판 공부 노트 PM전환기</title>
      <link>https://kimkani.tistory.com/46</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JYySQ/btsAvs14PGl/NqY9wLWDmJN1orXJyuiRzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JYySQ/btsAvs14PGl/NqY9wLWDmJN1orXJyuiRzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JYySQ/btsAvs14PGl/NqY9wLWDmJN1orXJyuiRzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJYySQ%2FbtsAvs14PGl%2FNqY9wLWDmJN1orXJyuiRzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;463&quot; height=&quot;463&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;今、「良いもの」をつくるだけはモノが売れない時代になりました。&lt;br /&gt;지금, &quot;좋은 물건&quot;을 만드는 것만으로는 물건이 팔릴 수 없는 시대가 되었습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;이 말의 요지는 많은 것을 품고 있지만, 가장 큰 것은 바로 인터넷의 발달로 인한 정보의 카피&amp;amp;페이스트가 쉬워져 기능 면으로나 성능 면으로 비슷해진 제품들이 나오기 시작했다는 것이다. 대표적으로 성능면과 기능면에서 큰차이가 나타나지 않기 시작한 20년대 들어서부터 나온 스마트폰의 경쟁 구도 양상이 그럴 것이다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;삼성의 갤럭시와 애플의 아이폰 대결구도가 그렇다. 즉, 대중들이 받아들이는 성능상 - 엑시노스와 GOS사태를 제외하고 - 큰 차이점은 없다. 하지만 애플의 아이폰을 10~30대는 아주 선호한다. 이 책에서도 도시바를 사든 샤프를 사든 소비자들은 큰 차이를 못느끼며, 이제는 신제품 발표에도 두근거림이 거의 없다고 한다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;'갤럭시男 거른다'부터 '갤레기'까지&amp;hellip;아이폰 열광하는 20대&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;20대 소비자 사이에서 애플 아이폰 선호도가 날로 심화되고 있다. 연령별 소비자 가운데 국내에서 아이폰 사용도가 더 높은 것도 20대가 유일하고, 최근 출시된 아이폰15 시...&quot; data-og-host=&quot;zdnet.co.kr&quot; data-og-source-url=&quot;https://zdnet.co.kr/view/?no=20231019113052&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b7feYr/hyUymGOIn3/aTVwIjsQcAhkP45YtgBi1k/img.jpg?width=640&amp;amp;height=428&amp;amp;face=96_91_221_216,https://scrap.kakaocdn.net/dn/3wGzE/hyUysNN7Dj/H5vMBusGGubtrVo8dmPw9k/img.jpg?width=640&amp;amp;height=428&amp;amp;face=96_91_221_216,https://scrap.kakaocdn.net/dn/rdEj2/hyUyv4PsDQ/FBM4JbUQ6yOcNzP0s31Az0/img.jpg?width=640&amp;amp;height=360&amp;amp;face=0_0_640_360&quot; data-og-url=&quot;https://zdnet.co.kr/view/?no=20231019113052&quot;&gt;&lt;a href=&quot;https://zdnet.co.kr/view/?no=20231019113052&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://zdnet.co.kr/view/?no=20231019113052&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b7feYr/hyUymGOIn3/aTVwIjsQcAhkP45YtgBi1k/img.jpg?width=640&amp;amp;height=428&amp;amp;face=96_91_221_216,https://scrap.kakaocdn.net/dn/3wGzE/hyUysNN7Dj/H5vMBusGGubtrVo8dmPw9k/img.jpg?width=640&amp;amp;height=428&amp;amp;face=96_91_221_216,https://scrap.kakaocdn.net/dn/rdEj2/hyUyv4PsDQ/FBM4JbUQ6yOcNzP0s31Az0/img.jpg?width=640&amp;amp;height=360&amp;amp;face=0_0_640_360');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;'갤럭시男 거른다'부터 '갤레기'까지&amp;hellip;아이폰 열광하는 20대&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;20대 소비자 사이에서 애플 아이폰 선호도가 날로 심화되고 있다. 연령별 소비자 가운데 국내에서 아이폰 사용도가 더 높은 것도 20대가 유일하고, 최근 출시된 아이폰15 시...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;zdnet.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;이는 &quot;완성형&quot;으로는 차이를 벌리기가 힘들어진 것을 의미한다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개인적인 생각&lt;br /&gt;&lt;br /&gt;완성형으로 차이를 벌리기 어렵다기 보다는, 이미 완성형이 된 프로덕트 만을 바라보는 관점에서는 프로덕트 자체는 모두가 완성형이기에, 더이상 그 점만을 중심으로 파고들기는 힘들다는 것을 의미하는 것 같다. 삼성이 50~100배 줌을 내놓았어도 - 물론 어느정도 불안정한 면이 있었지만 - 애플은 그러한 50배줌에 미치지 않는데도 충분히 다른 점으로 경쟁을 하고 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;이는 사람도 제품도 모두에게 해당되는 점이다. 즉, 이미 완성형을 요구하는 것은 기본이고 거기서 플러스 알파가 필요하다. 책은 이러한 사람과 제품에 파묻힌 시대, 새로운 돈버는 방식으로 &quot;프로세스(과정)&quot; 그 자체를 파는 &quot;프로세스 이코노미&quot;를 제안한다. 그 이유로 &quot;프로세스는 카피할 수 없기 때문&quot;이라고 소개한다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;대표적으로 회사들에서 어떠한 소재를 사용했다고 이야기를 하지않고, 이 소재를 사용하면서 어떻게 이 과정을 거쳤고, 어떤 어려운 점이 있었으며, 그것을 어떻게 극복했는지를 첨부하는 것과 비슷한 것 같다. 아니면 이렇게 하지 않아도 되지만, 이렇게 한 고집 - 흔히 말하는 장인 정신 - 을 소개하는 이유가 그런 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VgvmP/btsAuLnhTET/m7FqZIuadWMYNDgzYd1hmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VgvmP/btsAuLnhTET/m7FqZIuadWMYNDgzYd1hmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VgvmP/btsAuLnhTET/m7FqZIuadWMYNDgzYd1hmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVgvmP%2FbtsAuLnhTET%2Fm7FqZIuadWMYNDgzYd1hmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;539&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 단어는 &quot;00:00 Studio(포 제로 스튜디오)&quot;의 &quot;けんすう(켄수)&quot;께서 어휘화한 단어라고 한다. 다만, 00:00 Studio의 경우는 4월 28일을 기점으로 지금 문을 닫은 상태이다. 이 부분도 추가적으로 생각해볼 거리가 있을 것 같다. 아무튼, 켄수의 note를 참고하면서 이 글은 설명을 좀더 추가한다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;아래의 원문을 통해서도 볼 수 있다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;「プロセス・エコノミー」が来そうな予感です｜けんすう&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;こんにちは！アルというマンガサービスを作っている、けんすうと申します。最近だと、作業中をライブ配信する「00:00 Studio(ふぉーぜろすたじお)」というサービスも作っています。 最近、&quot; data-og-host=&quot;kensuu.com&quot; data-og-source-url=&quot;https://kensuu.com/n/nf4270e069c20?gs=176c768cd12d&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fWTgk/hyUyygcdqF/o1AMYHKs6A0tkhcsI8fmr1/img.jpg?width=1280&amp;amp;height=635&amp;amp;face=258_242_374_368,https://scrap.kakaocdn.net/dn/DtoKc/hyUyr9dngK/DdtI93mIATN0HneN924jYK/img.png?width=800&amp;amp;height=476&amp;amp;face=0_0_800_476,https://scrap.kakaocdn.net/dn/cQM8Of/hyUyyAvR0Z/YaRgVvfy9zsdlFY0vDE8c0/img.jpg?width=800&amp;amp;height=397&amp;amp;face=0_0_800_397&quot; data-og-url=&quot;https://kensuu.com/n/nf4270e069c20&quot;&gt;&lt;a href=&quot;https://kensuu.com/n/nf4270e069c20&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kensuu.com/n/nf4270e069c20?gs=176c768cd12d&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fWTgk/hyUyygcdqF/o1AMYHKs6A0tkhcsI8fmr1/img.jpg?width=1280&amp;amp;height=635&amp;amp;face=258_242_374_368,https://scrap.kakaocdn.net/dn/DtoKc/hyUyr9dngK/DdtI93mIATN0HneN924jYK/img.png?width=800&amp;amp;height=476&amp;amp;face=0_0_800_476,https://scrap.kakaocdn.net/dn/cQM8Of/hyUyyAvR0Z/YaRgVvfy9zsdlFY0vDE8c0/img.jpg?width=800&amp;amp;height=397&amp;amp;face=0_0_800_397');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;「プロセス・エコノミー」が来そうな予感です｜けんすう&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;こんにちは！アルというマンガサービスを作っている、けんすうと申します。最近だと、作業中をライブ配信する「00:00 Studio(ふぉーぜろすたじお)」というサービスも作っています。 最近、&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kensuu.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;반대 개념을 생각해 보겠습니다. 이를 잠시 &quot;아웃풋 이코노미&quot;라고 하겠습니다. 아웃풋 이코노미는 &quot;프로세스에서는 과금하지 않고, 아웃풋으로 과금한다&quot;라는 것입니다. 예를들면,&lt;br /&gt;&lt;br /&gt;- 음악을 만들고 있는 곳에서는 돈을 벌지 않고, 만든 음악을 판다.&lt;br /&gt;- 영화를 만들고 있는 곳에서는 돈을 벌지 않고, 만든 영화를 판다.&lt;br /&gt;- 요리를 만들고 있는 곳에서는 돈을 벌지 않고, 만든 요리를 판다.&lt;br /&gt;&lt;br /&gt;등입니다. 파는 법은, 고객이 직접 과금하는 방식이 있는가 하면, 티비같이 광고 모델로 하는 두가지 방식등이 있겠습니다만, 두 방식 모두 아웃풋으로 돈을 벌고 있는 점은 같습니다. 이렇듯, 아웃풋 이코노미에서는, 평범한 사람이 생각하는, 아주 일반적인 상품 매매 방식입니다.&lt;br /&gt;&lt;br /&gt;그렇다면, 아웃풋 이코노미에서는 무엇이 중요한 것일까요? 그것은 제품의 품질...&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;아웃풋 이코노미 쪽을 요약하자면,&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&quot;아웃풋을 파는 방법은, 예전에는 먹혔을지 몰라도, 이런 아웃풋 이코노미 속에서는 제품들의 상향평준화가 이루어졌고, 차이가 크게 나지 않게 되었다. 이는 정보를 얻는 방식이 쉬워졌기도 한 탓이다. 또한 입소문이 트위터, 인스타그램, 유튜브 등을 중심으로 빠르게 퍼진 것도 한 몫 했다. 이러한 이유로 대부분의 분야에서 퀄리티가 높아졌다. 이는 곧, '어떤 제품을 사든 프로덕트 퀄리티가 높아서 무언가를 고를 때 고집을 부릴 필요가 없어졌다'라는 소비자의 반응으로 이어졌다.&quot;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;정도이다. 이는 현실에서도 쉽게 발견할 수 있다. 대표적으로 크라우드 펀딩이며, 이러한 크라우드 펀딩 게임들 - 오모리, 발더스 게이트, 언더테일 등등 - 이 처음에 데모로 시작해, 만들어 가는 과정을 공개하고, 그와 동시에 유튜브 등지에서 - 요즘은 쇼츠나 릴스 같은 숏폼 동영상 플랫폼으로 인해 더더욱 빠르게 확산된다 - 그러한 정보를 공유하고 점점 퍼지게 되어 응원하게 된다. 즉 프로세스 자체를 응원하게 되는 것이다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;저자는 더 극적으로 말해, &quot;적어도 프로세스 이코노미와 완벽하게 연이 없고, 아웃풋 이코노미만으로 생활을 한다고 말하는 사람은 없을 것입니다.&quot;라고 말한다. 물론 저자도 이런 프로세스 이코노미가 조금은 다르고 이질적이라는 것을 인정하면서, 다양한 예시를 들며 프로세스 자체에 과금을 하는 상황이 더 늘어날 것이라고 한다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;개인적인 생각&lt;br /&gt;&lt;br /&gt;사실 이러한 방식, 프로세스를 공개하면서 점점 키워나가는 - 흔히 말하는 판키우기 방식은 - 한국의 아이돌 서바이벌이나, 쇼미더머니를 통해 많이 느낄 수 있다. 다만, 이러한 프로세스 방식은 새로운 - 즉 지루하지 않은 - 원판이 있어야 한다고 생각한다. 대표적으로 쇼미더머니는 점점 알던 래퍼들이 나옴으로써, 지루함이 늘어갔다는 평을 받기도 했다. 이러한 프로세스 이코노미 방식은 신선함이 처음에는 무기가 되어야 할 듯 하다. 물론 여기서 신섬함과 자극성은 구분해야될 것 같다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;다만 이 방식은 커뮤니티의 부재, 팬과의 소통 부재가 있었기에, 어찌보면 완벽한 프로세스 이코노미가 아니지 않았을까 하는 생각도 든다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;프로세스에 가치를 싣기 위해서는, 만드는 사람이 스토리를 넣는 다던가 Why를 강조해야 된다고 - 흔히 말하는 철학의 대두 - 가 중요하다고 한다. 그리고 제작자 혼자로는 힘들기에, 유저를 팬으로서 만들어 2차창작 - Second Creater - 로 흡수해서 열량을 높여가는 것이 중요하다고 한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;대표적으로 밈적인 요소가 이에 해다될 것 같다. Aiobahn의 Internet Yamero와 Internet Overdose의 한 장면을 여러 유튜버들이 따라하며 - 그리거나 혹은 춤을 추거나 - 밈적 요소가 되어 엄청나게 퍼져나간 경우가 그에 해당할 것 같다. 그 외에도, 이세계 아이돌의 비챤의 &quot;나랑 노라줘&quot;가 목소리 만으로도 다양한 아트를 생성시켜 인지도를 높여준 것도 이에 해당될 것이다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;물론 이 책에서는 Clubhouse앱을 소개하는데, 시기가 시기인지라, 그 때 당시에는 엄청 핫한 어플이었지만 지금은 그렇지 않다는 점에서 되돌아보기 좋은 포인트인 것 같기도 하다. 이런 클럽하우스에 투자한 당시 투자자중 한 명이 Community Takes All라고도 말한 것을 책에서는 예시를 들고 있다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그와 동시에, 새로운 서비스를 만드는 기업가에게 있어, 이러한 프로세스 이코노미를 이해하는 것은 필수라고 강조하는데, 왜냐하면 프로덕트를 완성시켜 아웃풋 이코노미를 만들어 수익화하려고 해도 힘을 다써버리기 때문이라고 한다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그러면서 이 책은 프로세스 이코노미를 어떻게 풀어나갈지에 대해 이야기하며 첫 시작을 끝낸다.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽고서 조금씩 정리하며 올려보겠습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGU2u4/btsAzCvv0TY/6nFUZEtNmohSDuIZqUdK70/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGU2u4/btsAzCvv0TY/6nFUZEtNmohSDuIZqUdK70/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGU2u4/btsAzCvv0TY/6nFUZEtNmohSDuIZqUdK70/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGU2u4%2FbtsAzCvv0TY%2F6nFUZEtNmohSDuIZqUdK70%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;오늘의 공부 및 정리 인증&lt;/p&gt;</description>
      <category>Product</category>
      <category>결과</category>
      <category>공부</category>
      <category>과정</category>
      <category>기업</category>
      <category>수익</category>
      <category>수익화</category>
      <category>창업</category>
      <category>책</category>
      <category>프로덕트</category>
      <category>프로세스이코노미</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/46</guid>
      <comments>https://kimkani.tistory.com/46#entry46comment</comments>
      <pubDate>Fri, 17 Nov 2023 19:35:08 +0900</pubDate>
    </item>
    <item>
      <title>[번역] 파이썬을 통해 보는 웹 인증 및 인가 방법 1편 - HTTP, JWT, 세션(Session), 쿠키(Cookie) 등</title>
      <link>https://kimkani.tistory.com/45</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UoqNb/btszojrh3Az/wsDlJqMFEUvsX80iWp1OK1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UoqNb/btszojrh3Az/wsDlJqMFEUvsX80iWp1OK1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UoqNb/btszojrh3Az/wsDlJqMFEUvsX80iWp1OK1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUoqNb%2Fbtszojrh3Az%2FwsDlJqMFEUvsX80iWp1OK1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;807&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1698640038456&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Web Authentication Methods Compared&quot; data-og-description=&quot;This article looks at the most commonly used web authentication methods.&quot; data-og-host=&quot;testdriven.io&quot; data-og-source-url=&quot;https://testdriven.io/blog/web-authentication-methods/&quot; data-og-url=&quot;https://testdriven.io/blog/web-authentication-methods/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iOrG4/hyUkkqaIDJ/8g1nwNrUrvO5qtCSmv9hZk/img.png?width=2034&amp;amp;height=1016&amp;amp;face=0_0_2034_1016,https://scrap.kakaocdn.net/dn/qtQaS/hyUnVWsTJb/UiLHstXQsWOKTGL84yncA1/img.png?width=2034&amp;amp;height=1016&amp;amp;face=0_0_2034_1016&quot;&gt;&lt;a href=&quot;https://testdriven.io/blog/web-authentication-methods/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://testdriven.io/blog/web-authentication-methods/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iOrG4/hyUkkqaIDJ/8g1nwNrUrvO5qtCSmv9hZk/img.png?width=2034&amp;amp;height=1016&amp;amp;face=0_0_2034_1016,https://scrap.kakaocdn.net/dn/qtQaS/hyUnVWsTJb/UiLHstXQsWOKTGL84yncA1/img.png?width=2034&amp;amp;height=1016&amp;amp;face=0_0_2034_1016');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Web Authentication Methods Compared&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This article looks at the most commonly used web authentication methods.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;testdriven.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 웹 인증을 위해 가장 보편적으로 사용되는 방법들을 파이썬 웹 개발자 관점에서 볼 것이다.&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;authentication-vs-authorization&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;Authentication(인증) vs Authorization(인가)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Authentication(인증)은 유저나 디바이스가 제한된 시스템에 접근하려 할 때 신원을 증명하는 프로세스면, 인가는 주어진 시스템 내에서 특정한 태스크를 허락받았는지에 대한 증명 프로세스라고 보면 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 말하자면,&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인증 : 넌 누구인가?&lt;/li&gt;
&lt;li&gt;인가 : 넌 뭘 할 수 있는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증이 먼저이며 그 후에 인가가 온다. 그 의미인 즉슨,&amp;nbsp; 유저는 무조건 그들의 인증 레벨에 따라 부여된 리소스 접근 권한을 부여받기 전에 유효해야 한다는 것이다. 가장 일반적인 인증 방식은 username(아이디)와 password(비밀번호)다. 한번 인증되면 관리자나, 모더레이터 등과 같은 다른 역할이 부여되며 이는 곧 시스템에 대한 특별 권한을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 알아봤으니, 유저를 인증하기 위한 각기 다른 방법들을 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HTTP Basic Authentication&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP프로토콜에 내장되어 있는 Basic Authentication(기본 인증 방식)은 가장 기본적인 인증 형태이며, 이를 통해, 로그인시의 중요 정보를 각각의 리퀘스트를 보낼 때 마다 리퀘스트 헤더에 담는다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698640659681&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=&quot; your-website.com&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이디와 패스워드는 암호화되지 않는다. 대신, 아이디와 패스워드는 &quot;:&quot; 사이에 연결되어서 &quot;username:password&quot;형태를 띈 하나의 문자열로 보내진다. 이 문자열이 base64를 이용해서 암호화 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1698640741836&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import base64
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; auth = &quot;username:password&quot;
&amp;gt;&amp;gt;&amp;gt; auth_bytes = auth.encode('ascii') # convert to bytes
&amp;gt;&amp;gt;&amp;gt; auth_bytes
b'username:password'
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; encoded = base64.b64encode(auth_bytes) # base64 encode
&amp;gt;&amp;gt;&amp;gt; encoded
b'dXNlcm5hbWU6cGFzc3dvcmQ='
&amp;gt;&amp;gt;&amp;gt; base64.b64decode(encoded) # base64 decode
b'username:password'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 상태지속성을 띄지 않기에 Client(클라이언트)는 중요 정보를 각각의 모든 리퀘스트마다 공급해줘야 한다. 이는 영구적인 세션을 요구하지 않는 간단한 인증 흐름을 위한 API 호출에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flow(흐름)&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;비인증 유저가 제한된 리소스를 요청한다.&lt;/li&gt;
&lt;li&gt;HTTP_401_Unauthorized 코드가 WWW-Authenticate: Basic이라는 키 밸류를 가진 헤더와 함께 돌아온다.&lt;/li&gt;
&lt;li&gt;이 &lt;b&gt;WWW-Authenticate: Basic&lt;/b&gt;이 브라우저로 하여금 아이디와 패스워드 입력칸을 즉각적으로 보여주게 만든다.&lt;/li&gt;
&lt;li&gt;정보를 입력하고 나면, 이 정보들이 각각의 리퀘스트마다 &lt;b&gt;Authorization: Basic dcdvcmQ=&lt;/b&gt;와 같은 형식으로 전송된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;507&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EGaQB/btszvbscFxv/J1BXukD8QIEuPZZxkCD911/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EGaQB/btszvbscFxv/J1BXukD8QIEuPZZxkCD911/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EGaQB/btszvbscFxv/J1BXukD8QIEuPZZxkCD911/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEGaQB%2FbtszvbscFxv%2FJ1BXukD8QIEuPZZxkCD911%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;507&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;507&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;많은 연산이 이루어지지 않기 때문에, 인증 자체는 더 빠르게 할 수 있다.&lt;/li&gt;
&lt;li&gt;구현 및 실행 자체가 쉽다.&lt;/li&gt;
&lt;li&gt;모든 주된 브라우저에서 지원된다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Base64는 암호화와 다르다. 데이터의 표현 방식 중 하나일 뿐이다. base64로 암호화된 문자열은 일반적인 텍스트로 보내지기 때문에 쉽게 복호화가 가능하다. 이런 빈약한 보안 특징은 다양한 방식의 공격을 불러일으킨다. 이러한 이유로 HTTPS/SSL이 거의 필수적이다.&lt;/li&gt;
&lt;li&gt;중요 정보를 매 리퀘스트마다 보내야 한다.&lt;/li&gt;
&lt;li&gt;유저는 오직 잘못된 로그인 정보를 입력해야만 로그아웃이 가능하다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;대표적인 파이썬 프레임워크 패키지&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #114d8d;&quot; href=&quot;https://flask-httpauth.readthedocs.io/&quot;&gt;Flask-HTTPAuth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/hirokiky/django-basicauth/&quot;&gt;django-basicauth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://fastapi.tiangolo.com/advanced/security/http-basic-auth/&quot;&gt;FastAPI: HTTP Basic Auth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플라스크를 사용한다면 Flask-HTTP 패키지를 통해 Basic HTTP Authentication은 쉽게 구현가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1698641529123&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from flask import Flask
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import generate_password_hash, check_password_hash

app = Flask(__name__)
auth = HTTPBasicAuth()

users = {
    &quot;username&quot;: generate_password_hash(&quot;password&quot;),
}


@auth.verify_password
def verify_password(username, password):
    if username in users and check_password_hash(users.get(&quot;username&quot;), password):
        return username


@app.route(&quot;/&quot;)
@auth.login_required
def index():
    return f&quot;You have successfully logged in, {auth.current_user()}&quot;


if __name__ == &quot;__main__&quot;:
    app.run()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그 외 자료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://tools.ietf.org/html/rfc7617&quot;&gt;IETF: The 'Basic' HTTP Authentication Scheme&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://blog.miguelgrinberg.com/post/restful-authentication-with-flask&quot;&gt;RESTful Authentication with Flask&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://www.django-rest-framework.org/api-guide/authentication/#basicauthentication&quot;&gt;DRF Basic Authentication Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://gist.github.com/nilsdebruin/8b36cd98c9949a1a87e3a582f70146f1&quot;&gt;FastAPI Basic Authentication Example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTP Digest Authentication&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;HTTP Digest Authentication(또는 Digest Access Authentication)은 HTTP Basic Auth보다 더 안전한 형태이다. 가장 주된 차이점은 비밀번호가 평문이 아닌 MD5방식으로 해시(hash)되기에 Basic Auth보다 더 안전하다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Flow(흐름)&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;비인증 유저가 제한된 리소스를 요청한다.&lt;/li&gt;
&lt;li&gt;Nonce(논스)라는 무작위 값을 서버가 생성하고 HTTP_401_Unauthorized 상태 코드와 함께 이 논스를 WWW-Authenticate헤더 키값과 함께 값으로서 Digest를 포함해 &lt;b&gt;WWW-Authenticate: Digest nonce=&quot;44f0437004157342f50f9d39s01dfjgc&quot;&lt;/b&gt;처럼 논스를&amp;nbsp; 같이 보낸다.&lt;/li&gt;
&lt;li&gt;WWW-Authenticate: Digest가&amp;nbsp;브라우저로 하여금 아이디와 패스워드 입력칸을 즉각적으로 보여주게 만든다.&lt;/li&gt;
&lt;li&gt;유저가 로그인 정보를 입력하면, 패스워드는 해시 처리되며 논스와 함께 매 리퀘스트마다 헤더에 담겨져서 다음과 같이 보내진다. &lt;b&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;Authorization: Digest username=&quot;username&quot;, nonce=&quot;16e30069e45a7f47b4e2606aeeb7ab62&quot;, response=&quot;89549b93e13d438cd0946c6d93321c52&quot;&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;아이디와 함께 비밀번호를 얻은 서버는 논스와 함께 비밀번호를 해시 처리하며, 그 값이 같은지 검증한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;507&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/urRKd/btszkBT3Cuc/44rFJJBe9X4LQnOXZQe061/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/urRKd/btszkBT3Cuc/44rFJJBe9X4LQnOXZQe061/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/urRKd/btszkBT3Cuc/44rFJJBe9X4LQnOXZQe061/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FurRKd%2FbtszkBT3Cuc%2F44rFJJBe9X4LQnOXZQe061%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;507&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;507&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비밀번호를 평문으로 보내지 않기 때문에 Basic Auth보다는 안전하다.&lt;/li&gt;
&lt;li&gt;구현 및 실행 자체가 쉽다.&lt;/li&gt;
&lt;li&gt;모든 주된 브라우저에서 지원된다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Basic Auth와 비교해서 bcrypt - 해시 방법 중 하나 - 를 사용할 수 없기에 덜 안전할 수도 있다.&lt;/li&gt;
&lt;li&gt;중요 정보를 매 리퀘스트마다 보내야 한다.&lt;/li&gt;
&lt;li&gt;유저는 오직 잘못된 로그인 정보를 입력해야만 로그아웃이 가능하다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;man-in-the-middle(중간자 공격)에 취약하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;대표적인 파이썬 프레임워크 패키지&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-httpauth.readthedocs.io/&quot;&gt;Flask-HTTPAuth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;코드 예시&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;플라스크를 사용한다면 Flask-HTTP 패키지를 통해 Digest HTTP Authentication또 쉽게 구현가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1698642319458&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from flask import Flask
from flask_httpauth import HTTPDigestAuth

app = Flask(__name__)
app.config[&quot;SECRET_KEY&quot;] = &quot;change me&quot;
auth = HTTPDigestAuth()

users = {
    &quot;username&quot;: &quot;password&quot;
}


@auth.get_password
def get_user(username):
    if username in users:
        return users.get(username)


@app.route(&quot;/&quot;)
@auth.login_required
def index():
    return f&quot;You have successfully logged in, {auth.current_user()}&quot;


if __name__ == &quot;__main__&quot;:
    app.run()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그 외 자료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://tools.ietf.org/html/rfc7616&quot;&gt;IETF: HTTP Digest Access Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://requests.readthedocs.io/en/latest/user/authentication/#digest-authentication&quot;&gt;Digest Authentication from the Requests library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 id=&quot;session-based-auth&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;Session-based Auth&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Session-based auth(세션 기반 인증) 혹은 세션 쿠키 인증, 쿠키 기반 인증은 유저의 상태가 서버에 저장된다. 매 요청마다 유저가 아이디와 비밀번호를 제공할 필요가 없다. 대신, 로그인 후, 서버가 로그인 정보를 검증하고 브라우저에게 세션 아이디(session ID)를 보내준다. 만약 (정보가) 유효하다면 서버는 세션을 생성하며 세션 창고에 저장하며 브라우저에게 세션 아이디를 리턴한다. 브라우저는 쿠키처럼 세션 아이디를 저장하고 이는 서버에서 매번 요청할 때 마다 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 기반 인증은 상태가 지속적이다. 서버에 클라이언트가 매번 요청하기에, 서버는 이와 관련된 유저의 세션 아이디를 보내주기 때문, 무조건 메모리에 세션을 위치시켜야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Flow(흐름)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;1015&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pJ1qL/btsznC5zILd/QfYcphuap2gUp8m8c7vZ5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pJ1qL/btsznC5zILd/QfYcphuap2gUp8m8c7vZ5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pJ1qL/btsznC5zILd/QfYcphuap2gUp8m8c7vZ5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpJ1qL%2FbtsznC5zILd%2FQfYcphuap2gUp8m8c7vZ5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;510&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;1015&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그인 정보가 매번 요구되지 않기에, 로그인 유지를 위한 처리가 빠르다.&lt;/li&gt;
&lt;li&gt;향상된 유저 경험.&lt;/li&gt;
&lt;li&gt;상대적으로 쉬운 구현과 실행. 장고같은 많은 파이썬 프레임워크에 이러한 특징들이 내장되어 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지속성을 가지기에 서버는 각각의 세션을 서버 쪽에다가 유지해야한다. 유저의 세션 정보를 저장하는 창고는 다양한 서비스의 인증을 위해 공유되어야 한다. 이러한 이유로 REST한 서비스와는 잘 안맞는데, 이는 REST자체가 비 지속적인 프로토콜이기 때문이다.&lt;/li&gt;
&lt;li&gt;인증을 필요로 하지 않아도, 매 요청마다 쿠키가 담겨서 보내진다.&lt;/li&gt;
&lt;li&gt;CSRF공격에 약하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;대표적인 파이썬 프레임워크 패키지&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-login.readthedocs.io/&quot;&gt;Flask-Login&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-httpauth.readthedocs.io/&quot;&gt;Flask-HTTPAuth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/auth/&quot;&gt;User authentication in Django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/MushroomMaula/fastapi_login&quot;&gt;FastAPI-Login&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/frankie567/fastapi-users&quot;&gt;FastAPI-Users&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;코드 예시&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;플라스크를 사용한다면 Flask-Login은 세션 기반 인증을 위한 최고의 패키지다. 패키지를 통해 로그인, 로그아웃 그리고 일전 기간동안 유저 정보 기억등이 가능하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698643347284&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from flask import Flask, request
from flask_login import (
    LoginManager,
    UserMixin,
    current_user,
    login_required,
    login_user,
)
from werkzeug.security import generate_password_hash, check_password_hash


app = Flask(__name__)
app.config.update(
    SECRET_KEY=&quot;change_this_key&quot;,
)

login_manager = LoginManager()
login_manager.init_app(app)


users = {
    &quot;username&quot;: generate_password_hash(&quot;password&quot;),
}


class User(UserMixin):
    ...


@login_manager.user_loader
def user_loader(username: str):
    if username in users:
        user_model = User()
        user_model.id = username
        return user_model
    return None


@app.route(&quot;/login&quot;, methods=[&quot;POST&quot;])
def login_page():
    data = request.get_json()
    username = data.get(&quot;username&quot;)
    password = data.get(&quot;password&quot;)

    if username in users:
        if check_password_hash(users.get(username), password):
            user_model = User()
            user_model.id = username
            login_user(user_model)
        else:
            return &quot;Wrong credentials&quot;
    return &quot;logged in&quot;


@app.route(&quot;/&quot;)
@login_required
def protected():
    return f&quot;Current user: {current_user.id}&quot;


if __name__ == &quot;__main__&quot;:
    app.run()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그 외 자료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://tools.ietf.org/id/draft-broyer-http-cookie-auth-00.html&quot;&gt;IETF: Cookie-based HTTP Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login&quot;&gt;How To Add Authentication to Your App with Flask-Login&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://testdriven.io/blog/flask-spa-auth/&quot;&gt;Session-based Auth with Flask for Single Page Apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://testdriven.io/blog/csrf-flask/&quot;&gt;CSRF Protection in Flask&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://learndjango.com/tutorials/django-login-and-logout-tutorial&quot;&gt;Django Login and Logout Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://testdriven.io/blog/django-spa-auth/&quot;&gt;Django Session-based Auth for Single Page Apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://fastapi-users.github.io/fastapi-users/10.3/configuration/authentication/transports/cookie/&quot;&gt;FastAPI-Users: Cookie Auth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 id=&quot;token-based-authentication&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;Token-Based Authentication&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 쿠키 대신 토큰을 사용하여 유저를 인증한다. 유요한 로그인 정보로 유저는 인증을 하고 서버는 서명된 토큰을 전달한다. 이 토큰은 계속되는 요청에 사용될 수 있다. 가장 널리 사용되는 토큰은 JSON Web Token(JWT)이며, 이는 세 개의 파트로 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;헤더(Header): 토큰 타입과 사용된 해시 알고리즘을 담고 있다.&lt;/li&gt;
&lt;li&gt;페이로드(Payload): 토큰에서 사용할 상태의 주제에 대한 클레임을 담고 있다.&lt;/li&gt;
&lt;li&gt;서명(Signature): 토큰이 보내지는 동안 메시지가 바뀌지 않았다는 것을 증명할 때 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세가지는 base64로 암호화되어 &quot;.&quot;사이사이 연결되고 해시된다. (base64로)암호화된 것이기에 누구나 복호화해서 메시지를 읽을 수 있지만, 인증된 유저만 유효하게 서명된 토큰을 제공할 수 있다. 개인키를 통해 서명된 토큰은 서명(Signature)을 통해 인증된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;JSON Web Token(JWT)이 작고, URL에 대해 안전하다는 의미는 (페이로드의) 클레임들이 두개의 축에서 전달될 수 있다는 것을 뜻한다. JWT의 클레임은 JSON Web Signature(JWS) 구조의 페이로드(내용)나 JSON Web Encryption(JWE) 구조의 평문으로 사용되는 JSON 객체로 인코딩 되며, 이는 클레임이 전자 서명이 되거나 Message Authentication Code(MAC)을 통해 통합적으로 보호되고 혹은 암호화되는 것을 뜻한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;IETF 출처&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰은 서버 쪽에 저장될 필요성이 없다. 서명을 이용해 증명되면 된다. 최근에는 RESTful API와 Single Page Applications(SPAs)의 도입 성장세로 토큰 방식의 채택이 증가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;흐름&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1869&quot; data-origin-height=&quot;1645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PzGzN/btszltahTO5/E0FRYrrGdQsrp9mU0hB7LK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PzGzN/btszltahTO5/E0FRYrrGdQsrp9mU0hB7LK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PzGzN/btszltahTO5/E0FRYrrGdQsrp9mU0hB7LK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPzGzN%2FbtszltahTO5%2FE0FRYrrGdQsrp9mU0hB7LK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;602&quot; data-origin-width=&quot;1869&quot; data-origin-height=&quot;1645&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;pros_3&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;Pros&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비지속성을 띈다. 토큰은 사인을 통해 증명되기 때문에 서버는 토큰을 저장할 필요가 없다. 이는 데이터베이스 참조를 필요로 하지 않기 때문에 요청을 더 빨리 처리할 수 있게 된다.&lt;/li&gt;
&lt;li&gt;다양한 서비스들이 인증을 요구하기에, MSA(Micro Service Architecture)에 적합하다. 그저, 각각의 엔드포인트들이 어떻게 토큰과 토큰 비밀 정보를 다룰지를 설정해주면 끝이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;cons_3&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;Cons&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토큰이 클라이언트 측에 어떻게 저장되는지에 따라, XSS(local storage에 저장하는 경우)공격이나 CSRF(쿠키로 저장하는 경우)공격으로 이어질 수 있따.&lt;/li&gt;
&lt;li&gt;토큰들은 삭제될 수 없다. 오직 만료될 뿐이다. 이는 즉, 토큰이 유출되면 만료될 때 까지 공격자가 오남용할 수 있다는 것이다. 그렇기에 15분 정도로 아주 짧게 만료기간을 설정하는 것이 좋다.&lt;/li&gt;
&lt;li&gt;토큰이 만료될 때마다 자동으로 발급해주는 Refresh token(리프레시 토큰)을 준비할 필요가 있다.&lt;/li&gt;
&lt;li&gt;토큰을 삭제하는 방법은 토큰에 대한 블랙리스트를 데이터베이스에 저장하는 것이다. 하지만 이는 곧 MSA같은 구조에 추가적인 부하를 줄 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;대표적인 파이썬 프레임워크 패키지&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/vimalloc/flask-jwt-extended&quot;&gt;Flask-JWT-Extended&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-httpauth.readthedocs.io/&quot;&gt;Flask-HTTPAuth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/SimpleJWT/django-rest-framework-simplejwt&quot;&gt;Simple JWT for Django REST Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/IndominusByte/fastapi-jwt-auth&quot;&gt;FastAPI JWT Auth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 예시&lt;/h3&gt;
&lt;pre id=&quot;code_1698644747517&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from flask import Flask, request, jsonify
from flask_jwt_extended import (
    JWTManager,
    jwt_required,
    create_access_token,
    get_jwt_identity,
)
from werkzeug.security import check_password_hash, generate_password_hash

app = Flask(__name__)
app.config.update(
    JWT_SECRET_KEY=&quot;please_change_this&quot;,
)

jwt = JWTManager(app)

users = {
    &quot;username&quot;: generate_password_hash(&quot;password&quot;),
}


@app.route(&quot;/login&quot;, methods=[&quot;POST&quot;])
def login_page():
    username = request.json.get(&quot;username&quot;)
    password = request.json.get(&quot;password&quot;)

    if username in users:
        if check_password_hash(users.get(username), password):
            access_token = create_access_token(identity=username)
            return jsonify(access_token=access_token), 200

    return &quot;Wrong credentials&quot;, 400


@app.route(&quot;/&quot;)
@jwt_required
def protected():
    return jsonify(logged_in_as=get_jwt_identity()), 200


if __name__ == &quot;__main__&quot;:
    app.run()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그 외 자료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://jwt.io/introduction&quot;&gt;Introduction to JSON Web Tokens&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://tools.ietf.org/html/rfc7519&quot;&gt;IETF: JSON Web Token (JWT)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://simpleisbetterthancomplex.com/tutorial/2018/12/19/how-to-use-jwt-authentication-with-django-rest-framework.html&quot;&gt;How to Use JWT Authentication with Django REST Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://testdriven.io/blog/fastapi-jwt-auth/&quot;&gt;Securing FastAPI with JWT Token-based Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://blog.asayer.io/jwt-authentication-best-practices&quot;&gt;JWT Authentication Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Python/ETC</category>
      <category>Django</category>
      <category>FastAPI</category>
      <category>flask</category>
      <category>HTTP</category>
      <category>인증</category>
      <category>장고</category>
      <category>컴공</category>
      <category>컴퓨터공학</category>
      <category>파이썬</category>
      <category>플라스크</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/45</guid>
      <comments>https://kimkani.tistory.com/45#entry45comment</comments>
      <pubDate>Mon, 30 Oct 2023 14:47:30 +0900</pubDate>
    </item>
    <item>
      <title>[파이썬 기본 다지기] Python에서의 메모리 관리 1편</title>
      <link>https://kimkani.tistory.com/44</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oZZ5u/btsvkr7ckus/O9sJcZXLxnOl98VEvwd8UK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oZZ5u/btsvkr7ckus/O9sJcZXLxnOl98VEvwd8UK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oZZ5u/btsvkr7ckus/O9sJcZXLxnOl98VEvwd8UK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoZZ5u%2Fbtsvkr7ckus%2FO9sJcZXLxnOl98VEvwd8UK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;807&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;*이 글에서는 표준 구현체인 CPython을 기준으로 진행합니다&lt;/h4&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;In Python, Everything is an Object&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 모든 것이 객체다. 이 말을 들어본 적이 많을 것이다. 파이썬에서는 클래스(class), 함수(function), 그리고 정수(integer), 실수(float) 등등 모든 것이 객체이다. 각각의 파이썬 객체는 값(Value), Type(타입), Reference Count(참조 카운트)를 포함하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 만약에 다음과 같은 코드를 작성했다고 쳐보자.&lt;/p&gt;
&lt;pre id=&quot;code_1695555411736&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = 10&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;535&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T40Oc/btsvkOWUDBg/FiFBvrUZ8troA3Zg6FG5j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T40Oc/btsvkOWUDBg/FiFBvrUZ8troA3Zg6FG5j0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T40Oc/btsvkOWUDBg/FiFBvrUZ8troA3Zg6FG5j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT40Oc%2FbtsvkOWUDBg%2FFiFBvrUZ8troA3Zg6FG5j0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;515&quot; height=&quot;298&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;535&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위의 코드가 실행되면, CPython은 타입이 integer인 객체를 만들고 이 객체를 메모리에 할당한다. 이때 value에는 10dl Reference Count에는 1이 들어가는데, 이는 나중에 설명하겠지만, a = 10에서 변수 a에 10이 할당되었기 때문에 10을 참조하는 카운트가 1이 늘은 것이라고 보면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그래서, 모든 것이 객체다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;귀도 반 로섬(Guido van Rossum)은 The Python Lanugage Reference(2012)에서 다음과 같이 - &quot;파이썬에서의 객체는 데이터의 추상화이다&quot;라고 말한 적이 있다. 이를 생각해보면, 파이썬에서 모든 데이터는 객체나 객체들 관의 관계로서 표현될 수 있다는 것이다. 이는 불리안(boolean), 정수(integer), 실수(float), 문자열(string), 그 외 등등 심지어 함수(function)까지도, 모든 것이 객체로서 실행된다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;불변 데이터와 그렇지 않은 데이터&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python에서는 어떤 데이터는 변하고 어떤 데이터는 변하지 않는다. 불변 데이터에는 숫자(integer, float), 문자열(string), 튜플(tuple)이다. 그 반대에는 리스트(list), 사전(dictionary), 그리고 셋(집합 혹은 set)이 있다. 간단하게 불변데이터에 대해 비유를 하자면, 마치 볼 수만 있는 혹은 참조만 할 수 있는 데이터라 보면 된다. 수정은 불가능한 데이터이다. 이를 파이썬의 객체 개념까지 추가하면, 객체를 참조 및 불러오기, 할당만 가능하며 이 데이터 객체 자체는 수정이 불가능하다. value는 수정 불가능하다. 여기에 수정이 가능한 개념이 추가되면 변화가 가능한 데이터가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이 불변 데이터에 대한 취급이 중요한 이유는 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 불변 데이터 - 불변 객체 - 의 경우는 변화 가능한 데이터보다 접근이 쉽다. 하지만 이 불변 데이터의 표면상의 수정은 Python에서 결국 또 다른 하나의 객체를 생성하기에 새로운 메모리 공간에 무조건 할당해야 한다. 하지만 그렇지 않은 데이터는 쉽게 수정이 가능하며 또다른 복사본을 만들 필요가 없다. 그렇다면 이러한 메모리 차지를 하는 불변 데이터는 좋지 않은 것일까? 다르게 생각하면 디버깅에 좀 더 유리할 수 있는 면이 있다. 값이 바뀌지 않기에 객체의 상태를 쉽게 추적할 수 있게 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 함수에 인자로 넘길 때도 차이가 나타난다. 불변 데이터의 경우는 Call by Value로서 넘어가게 된다. 하지만 list와 같은 데이터는 Call by Reference로 넘어가게 된다. 이는 실제 데이터의 id값을 확인해 보면 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695616120651&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = 10
b = [0,0,0,0]

def immutable(a):
	i = a + 10
    print(id(i))
    
def mutable(b):
	b[0] = 10
    i = b
    print(id(i))
    
    
if __name__ == &quot;__main__&quot;:
	print(id(a))
    print(immutable(a))
    print(id(b))
    print(mutable(b))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코드를 짰다고 가정해보자. 편의상 밑에는 코드를 간단하게 Python IDLE에서 실행해 본 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;248&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEtzVk/btsvHiB7KaA/diPVigUpokpNl1abNNCnck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEtzVk/btsvHiB7KaA/diPVigUpokpNl1abNNCnck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEtzVk/btsvHiB7KaA/diPVigUpokpNl1abNNCnck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEtzVk%2FbtsvHiB7KaA%2FdiPVigUpokpNl1abNNCnck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;248&quot; height=&quot;101&quot; data-origin-width=&quot;248&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;246&quot; data-origin-height=&quot;97&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B4TzW/btsvnb43HME/ub5wid7g7ScwqEasLDCJjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B4TzW/btsvnb43HME/ub5wid7g7ScwqEasLDCJjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B4TzW/btsvnb43HME/ub5wid7g7ScwqEasLDCJjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB4TzW%2Fbtsvnb43HME%2Fub5wid7g7ScwqEasLDCJjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;246&quot; height=&quot;97&quot; data-origin-width=&quot;246&quot; data-origin-height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불변 데이터를 보낸 함수에서는 값이 10이 증가하고 이는 새로운 객체 생성으로 CPython에서는 이루어지기 때문에 둘의 id값이 다른 것을 알 수 있다. 하지만 단순 수정만 한 mutable함수의 경우 Call by Reference로 보내졌기에, 즉 주소값이 보내졌기에, 같은 주소값을 가지고 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CPython에서의 메모리 할당&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에는 객체와 데이터 구조를 저장하는 Private Heap이 존재한다. 파이썬 메모리 매니저는 파이썬에서 프로그램이 실행될 때 다양한 프로그램들의 메모리 할당과 비할당의 책임을 가지고 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Heap Space v.s. Stack Space&lt;/h3&gt;
&lt;pre id=&quot;code_1695554019552&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = 10
y = korea&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEXA1b/btsvjhrL6H7/DNPdERHJ4BQkxTo7WHN0ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEXA1b/btsvjhrL6H7/DNPdERHJ4BQkxTo7WHN0ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEXA1b/btsvjhrL6H7/DNPdERHJ4BQkxTo7WHN0ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEXA1b%2FbtsvjhrL6H7%2FDNPdERHJ4BQkxTo7WHN0ck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;727&quot; height=&quot;421&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 Heap Space에는 파이썬의 객체와 데이터 구조를 저장한다. 반면에, Stack Space에는 Heap Space에 있는 객체에 대한 참조체를 가지고 있다. 만약 앞에서 a = 10인 코드를 작성하고 b = a인 코드를 작성했다면 어떻게 될까? 다음과 같은 코드를 한 번 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1695606920126&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = 10
b = a&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;617&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ekJY2u/btsvoWT5leU/pufVtZPCGW7YGcutTIeKy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ekJY2u/btsvoWT5leU/pufVtZPCGW7YGcutTIeKy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ekJY2u/btsvoWT5leU/pufVtZPCGW7YGcutTIeKy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FekJY2u%2FbtsvoWT5leU%2FpufVtZPCGW7YGcutTIeKy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;625&quot; height=&quot;320&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;617&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 Reference Count가 2가 된다. 이유는 간단하다. b = a를 했고, 이에 b에도 a의 값인 10이 할당되었다. 여기서, b의 경우 10을 바라보기에 a와 b 둘 다 10을 바라보게 되고 이는 곧 두개의 참조자가 10이라는 객체에 생긴 것이다. 따라서 Reference Count는 2가 된다. 만약 이 다음에 a += 10을 하게 된다면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695607167553&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = 10
b = a
a += 10&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1087&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b39bX8/btsvkalClfm/koUVEJMwIZ6OrQTeJmVCW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b39bX8/btsvkalClfm/koUVEJMwIZ6OrQTeJmVCW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b39bX8/btsvkalClfm/koUVEJMwIZ6OrQTeJmVCW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb39bX8%2FbtsvkalClfm%2FkoUVEJMwIZ6OrQTeJmVCW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;558&quot; height=&quot;368&quot; data-origin-width=&quot;1087&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 생각해야할 것은 &quot;Everything is an object&quot; 이다. a += 10을 하면 새로운 값인 20이 생성되고 이 값 마저도 새로운 객체이다. 즉, CPython에서 위에서 언급했다시피 새로운 20이라는 객체를 만든다. 다시 생각해야할 부분이 생긴다. 바로 a와 b가 가르키는 곳이 어디인가 이다. a는 이제 새로운 값인 20을 얻었으므로 20을 참조한다. 그렇다면 b는? 기존에 10을 계속 참조한다. 하지만 여기서 차이가 난다. 바로 a와 b가 동시에 참조하던 20, 즉 두개의 참조 카운트가 이제는 하나로 줄어드는 것이다.&lt;/p&gt;</description>
      <category>Python/ETC</category>
      <category>OS</category>
      <category>python</category>
      <category>메모리</category>
      <category>운영체제</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>컴퓨터구조</category>
      <category>코딩</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/44</guid>
      <comments>https://kimkani.tistory.com/44#entry44comment</comments>
      <pubDate>Mon, 25 Sep 2023 13:33:45 +0900</pubDate>
    </item>
    <item>
      <title>RDBMS와 NoSQL - ACID, Transaction 등등</title>
      <link>https://kimkani.tistory.com/43</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEqRar/btsrjr6eXyz/JGSKKZRg3QWWLVe02XenY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEqRar/btsrjr6eXyz/JGSKKZRg3QWWLVe02XenY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEqRar/btsrjr6eXyz/JGSKKZRg3QWWLVe02XenY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEqRar%2Fbtsrjr6eXyz%2FJGSKKZRg3QWWLVe02XenY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;258&quot; height=&quot;258&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. RDBMS란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDBMS라는 이름만 보고는 쉽게 유추할 수 없겠지만, 약어로서 작용하는 하나의 시스템이다. 이것을 풀어보자면, Relational Data Base Management System이다. 즉, 관계형 데이터 베이스 관리 시스템이라는 직역이 가능하다. 여기서 가장 큰 중요성이 있는데 바로 Relational(관계형)이다. RDBMS는 프로그래머로서 하여금 관계형 디비 - MySQL, PostgreSQL, MariaDB 등등 - 와 상호작용(생성, 업데이트, 그리고 관리 등등)을 할 수 있게금 해주는 여러 프로그램이나 능력들을 모아둔 것이라고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1. RDBMS와 DBMS의 차이&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 데이터베이스 저장소(DB)는 쿼리를 날려 다른 어플리케이션도 사용할 수 있게하는 데이터들의 집합이라고 할 수 있다. DBMS는 데이터베이스 플랫폼 위에서 관리, 개발등을 지원하는 데이터베이스 관리 시스템이다. 이중에서 RDBMS는 열(row)기반 구조로서 관계되어 있는 데이터 요소들을 연결해준다. 이때 RDBMS는 데이터의 보안, 정확성, 무결 그리고 일관성을 관리하는 기능들을 포함하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에도 다음과 같은 차이가 있다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;RDBMS&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;DBMS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;접근 가능한 유저의 수&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;여러명의 유저가 접근 가능&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;한명의 유저밖에 한번에 접근 불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;하드웨어 및 소프트웨어 요구 사항&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;높음&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;데이터의 수&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;다양한 크기의 데이터 관리 가능&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;적은 데이터만 관리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;데이터베이스 구조&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;테이블 기반 관계형 구조&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;계층적 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;ACID여부&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;사용&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;미사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;분산형 디비&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;지원&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;미지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;데이터 정규화&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;가능&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: left;&quot;&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-2. RDBMS에서의 ACID란?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1-2-1. 트랜잭션이란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACID를 먼저 언급 하기 전에, Transaction(이하 트랜잭션)에 대해 언급하고 넘어가야 된다. 트랜잭션이란, 데이터베이스에서 이루어지는 연속되는 여러 실행 단위를 묶어둔 것이라고 생각하면 된다. 그리고 각각은 하나의 논리적인 작업의 유닛으로 제공된다. 예를들어 설명해보겠다. A라는 유저가 B라는 유저에게 500만원을 보낸다고 해보자. 그러면 다음과 같은 실행이 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;(데이터베이스 트랜잭션의 시작이라고 불리는) 유저 A에게서 B에게 500만원을 보낸다는 기록을 만든다.&lt;/li&gt;
&lt;li&gt;유저 A에게서 잔고를 확인한다.&lt;/li&gt;
&lt;li&gt;유저 A의 잔고에서 500만원을 뺀다.&lt;/li&gt;
&lt;li&gt;유저 B의 잔고를 읽는다.&lt;/li&gt;
&lt;li&gt;유저 B의 잔고에다가 500만원을 추가한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이러한 트랜잭션을 하나의 원자성(하나의 깨지지 않는) 유닛으로 처리하고 만약 시스템이 이러한 트랜잭션을 도중에 실패하면, 트랜잭션은 완료되지 않은 상태에서 기존의 상태로 돌아간다. 일반적으로 롤백(rollback)이라는 단어는 트랜잭션을 통해 만들어진 어느 변화든 미완료 상태에 놓인 프로세스를 되돌리는 것을 말한다. 커밋(commit)이라는 단어는 트랜잭션을 통해 영구적인 변화가 이루어진 경우를 언급한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNutJA/btsrkPMTbg4/CbIXB1cP0qBNEiWcekm7V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNutJA/btsrkPMTbg4/CbIXB1cP0qBNEiWcekm7V1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNutJA/btsrkPMTbg4/CbIXB1cP0qBNEiWcekm7V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNutJA%2FbtsrkPMTbg4%2FCbIXB1cP0qBNEiWcekm7V1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;418&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 트랜잭션에는 여러 상태가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;활성 상태(Active) - 트랜잭션의 첫번째 상태이다. 트랜잭션의 명령어(읽고 쓰는 작업)이 실행되는 동안 활성되어 있다.&lt;/li&gt;
&lt;li&gt;일부 커밋된 상태(Partially Commited) - 이 상태로 변한 경우 데이터베이스는 아직 디스크에 데이터를 커밋하지 않은 상태이다. 이러한 상태의 경우 메모리 버퍼에 있는 경우이며, 이 버퍼에 있는 데이터가 아직 쓰이지 않은 경우이다.&lt;/li&gt;
&lt;li&gt;커밋된 상태(Committed) - 이 상태의 경우 데이터베이스에 영구적으로 저장된 상태이다. 그렇기에, 이 상태 이전으로 롤백은 불가능하다.&lt;/li&gt;
&lt;li&gt;실패(Failed) - 만약 일부 커밋된 상태라든지, 활성 상태에서 트랜잭션이 거부되거나 실패한 경우 실패 상태에 진입한다.&lt;/li&gt;
&lt;li&gt;종료(Terminated) - 마지막 상태로 데이터베이스 트랜잭션의 라이프 사이클의 최종 단계이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1-2-2. ACID 특성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Atomicity(원자성) - 원자성을 생각하면 편하다. 즉 모 아니면 도인 상태이다. 트랜잭션이 커밋되면, 트랜잭션은 기존 상태로 롤백되거나, 아니면 완전히 성공하거나 하는 상태만을 가지는 것이다. 즉, 부분적으로 실행되거나 중단되지 앟ㄴ는 것을 보장해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Consistency(일관성) - 트랜잭션을 사용하는 주된 이유중 하나가 바로 트랜잭션이 성공하든 실패하든 바로 데이터 무결성을 유지하는 것이다. 트랜잭션은 데이터베이스 엔진에 의해 인가된 데이터에게만 영향을 줄 수 있으며, 데이터에 대한 일관성을 제공해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Isolation(독립성) - 만약 여러 다른 트랜잭션이 동시에 실행될 경우, 각기 다른 트랜잭션은 독립적으로 다른 트랜잭션에 영향을 주지 않고 실행되어야 한다. 이는 다른 관점에서 다른 트랜잭션이 다른 트랜잭션의 실행 작업도중을 볼 수 없다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Durability(지속성) - 성공적인 트랜잭션은 영원히 커밋되어야 한다. 모든 트랜잭션은 로그로 남으며 에러 발생 전으로 되돌릴 수 없다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1-3. RDB와 DB의 차이&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDB의 경우 관계형 디비(Relational Database)의 줄임말이며, DB는 어느 타입의 데이터베이스든 언급하는 주체가 똑같다. RDB의 경우 데이터를 한개 이상의 테이블 - 각각의 기록을 파악할 수 있는 유니크한 키값이 있는 - 특정한 데이터베이스를 언급한다. RDB의 가장 큰 특징은 바로 데이터의 구성 및 접근 방식이다. RDB는 Structured Query Language(SQL)을 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. NoSQL과 RDBMS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL은 위의 RDBMS의 대표적인 특징 중 하나인 데이터 간의 관계를 정의하지 않는다. 즉 여기서 RDBMS의 특징인 JOIN연산은 NoSQL에서 불가능하다. 하지만, 이러한 큰 장점을 가지고 있는 RDBMS과 달리 NoSQL에는 기존의 정형화된 데이터 뿐만 아니라, 음성, 비디오 등등, 이러한 비정형화된 데이터도 저장하고 다뤄야 하는 필요성이 제기되기에 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL은 수평 확장성과 고 가용성을 가지고 있는 특징 덕분에 현재 각광받고 있기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDBMS의 한계는 스키마 형태의 장점이 반대로 다가오는 과정이다. 기존 테이블에 정의된 데이터 타입이 아니면 컬럼을 자유롭게 추가하거나 변경해야되는데, 이것이 불가능하기에, 새로운 테이블을 만들거나 해야한다. 그 외에도 앞에서 말한 수평 확장성이 거의 불가능하다는 점이 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2-1. NoSQL의 종류&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Key-Value 데이터베이스 - 키와 밸류 쌍으로 데이터가 저장되며, 이때 Key는 값에 접근하기 위한 용도로 사용되고, 이때 Key는 어떤 형태의 데이터라도 담을 수 있다. 대표적으로 Redis가 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;성능 향상을 위해 데이터 캐싱을 할 때&lt;/li&gt;
&lt;li&gt;웹앱의 일시적인 속성 추적 및 모바일 앱용 사용자 데이터 정보와 구성정보 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Document Database - 테이블의 스키마가 유동적이며, 레코드마다 각각 다른 스키마를 가질 수 있다. 대표적으로 MongoDB가 있다. 여기서 단점이 나오는데, 각 문서(Documnet)별로 다른 필드를 가질 수 있기에 칼럼과 필드에 대한 관리를 확실히 해야한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 속성이 있는 데이터 관리 및 다양한 유형의 메타데이터 추적&lt;/li&gt;
&lt;li&gt;비정규화된 중첩 구조의 데이터를 사용하는 앱&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Column Family Database&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Graph Database&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ETC</category>
      <category>Database</category>
      <category>DB</category>
      <category>NOSQL</category>
      <category>RDBMS</category>
      <category>데이터베이스</category>
      <category>디비</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/43</guid>
      <comments>https://kimkani.tistory.com/43#entry43comment</comments>
      <pubDate>Thu, 17 Aug 2023 20:10:23 +0900</pubDate>
    </item>
    <item>
      <title>Domain Driven Design for FastAPI(파이썬 FastAPI를 위한 도메인 주도 디자인) - 캡슐화와 추상화</title>
      <link>https://kimkani.tistory.com/42</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1982&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0O1A1/btsnnTX5xHw/Ni3wKJYwolFIgSUhi39UBk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0O1A1/btsnnTX5xHw/Ni3wKJYwolFIgSUhi39UBk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0O1A1/btsnnTX5xHw/Ni3wKJYwolFIgSUhi39UBk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0O1A1%2FbtsnnTX5xHw%2FNi3wKJYwolFIgSUhi39UBk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;585&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1982&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본격적으로 도메인 주도 개발을 공부하면서 하나하나 정리하면서 내가 생각하고 고민한 흔적을 남겨보려고 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;캡슐화와 추상화의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동차를 생각해보자, 자동차가 달려가고 있다. 우리는 자동차가 바퀴가 굴러가면서 공기저항을 이겨내고 달려가는 것을 알 수 있다. 하지만, 그 안에는 수많은 움직이는 부품들이 있고, 작게는 기어부터 시작해 톱니바퀴까지 움직인다. 우리는 이 자동차 내에 수많은 움직임이 다른 움직임을 만들어 내고, 상호작용하고 있다는 것을 알 수 있다. 그와 동시에, 차체를 보자, 차체는 크게보면 디자인이 있지만, 그 안에는 다양한 프레임과 함께 견골들이 존재한다. 우리는 자동차에 엔진이 있다는 것을 알고 배터리부터 다양한 부품이 있다는 것을 안다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 이러한 설명이 너무 어렵게 다가왔을 수도 있겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 말해서, 캡슐화는 &quot;어떠한 정보 혹은 데이터의 은닉&quot;이다. 추상화는 &quot;어떠한 실행 혹은 행동의 은닉&quot;이다. 이것을 프로그래밍적 관점에서 살펴보겠다. 다양한 언어에서 정보 혹은 행동을 은닉하는 방법이 있겠지만, 나는, 이 글에서는 파이썬을 중점적으로 다룰 것이기에, 파이썬으로 이야기하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 더하기를 하는 계산기를 생각해보자, 우리에게 필요한 것은 두개의 피연산자 - 정보 - 와 그것을 구현해줄 더하기 기능 - 하나의 행동 - 이다.&lt;/p&gt;
&lt;pre id=&quot;code_1689137311410&quot; class=&quot;ruby&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class operation:
	def __init__(self):
    	self.result = 0
	
    def set(self, data):
		self.result = data
    
    def add(self, num):
    	return self.result += num&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보면 캡슐화와 추상화를 한번에 파악하기 어려울 것이다. 하지만, 실제 코드 구현을 하고서 아래와 같이 실행해 보면 그 맛을 느낄 수 있을 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1689137604759&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;operator = operation()
operator.set(3) # result는 3이 된다.
operator.add(4) # 이때 더하기가 실행되며 출력된다
&amp;gt;&amp;gt;&amp;gt; 7&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위의 실행코드만 던져주고 파악해본다면 어떤 느낌이 올까? 일단 set(3)를 해서 무언가 세팅한다는 것을 알 수 있다. 그리고 add를 통해 무언가 더하기를 한다는 것을 알 수 있다. 여기서 우리는 추상화와 캡슐화를 모두 감지했다. 자동차의 부품 - 정보(result)가 은닉되어 있다는 것 - 의 자세한 부분까지 모르지만 자동차가 있다는 것을 알 수 있으며, 자동차의 주행 - add의 실행을 통한 더하기 연산자 실행 - 이 실제로 이루어지고 있다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;추상화와 캡슐화의 중요성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머가 코드를 짜면 그걸로 끝나지 않는다. 코드의 양이 많아질 수록 그에 따른 시간과 노력이 들어가며 이에 비례해서 추후에 유지보수에도 시간을 쏟아야 할 것이다. 추상화와 캡슐화의 중요성은 여기서 드러난다. 추상화는 중요한 행동에 집중하도록 도와준다면, 캡슐화는 필요한 정보만을 필터링하도록 도와준다고 생각된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;추상화와 캡슐화에 대한 오해&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #232629; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추상화와 캡슐화가 무조건적으로 옳은 것은 아니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상화와 캡슐화에 너무 집착하지는 말자, 너무 과도하면 오히려 개발할 시간을 뺏기게 될 것이다. 다만 유념할 것은 느슨한 결합을 유지하자는 생각이다. 파이썬의 경우에는 후에도 이야기하겠지만, 언어차원에서 변수의 접근제한을 두는 경우가 없기에 완벽한 추상화와 캡슐화는 불가능하다는 생각이다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #232629; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추상화와 캡슐화는 실수를 줄이는 것이 아니라, 유지보수성을 높이는 것이다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실수를 줄인다고 생각하기 보다는 유지보수성을 높인다고 생각하자. 즉 미래에 투자하는 것이다. 아무리 추상화와 캡슐화를 잘해도 그 객체나 도메인 엔티티 자체 설계에 문제가 있을 수 있다. 다만 그것을 다시금 고칠 수 있는 시간과 비용을 줄이는 것이지 처음부터 실수를 줄이는 것은 아니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파이썬에서의 추상화와 캡슐화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상화와 캡슐화는 Domain Driven Design(앞으로 DDD로 줄여 부르겠다)을 비롯해, Object Oriented Programming(OOP)에서도 중요하게 받아들여지는 개념이다. 캡슐화를 통해 데이터의 접근을 제어하고 추상화를 통해 행동(함수 혹은 메서드)의 접근을 제한한다. 그리고 객체의 데이터의 변경 - DDD에서는 어떠한 도메인에서의 데이터 변경 - 이 객체의 함수 혹은 메서드를 통해서만 이루어지는 것을 추구한다. 그리고 이러한 변수의 데이터는 Private 변수로 불린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;하지만 C++, Java등 과는 달리 파이썬에서는 추상화와 캡슐화가 조금 다르게 받아들여진다고 생각한다.&lt;span&gt; 물론 파이썬은 OOP를 추구하는 언어지만, 철학이 조금은 다르다. 파이썬은 언어 자체에서 Private, Protected, Public같이 변수에 접근 제한을 두지않고 &quot;_&quot; 혹은 &quot;__&quot;를 붙임으로써 관습화된 - 즉 프로그래머 사이의 약속을 통해 - 제한을 둘 뿐이다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #ffffff; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;A class is an example of encapsulation as it encapsulates all the data that is member functions, variables, etc. The goal of information hiding is to ensure that an object&amp;rsquo;s state is always valid by controlling access to attributes that are hidden from the outside world.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Architecture</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/42</guid>
      <comments>https://kimkani.tistory.com/42#entry42comment</comments>
      <pubDate>Wed, 12 Jul 2023 14:28:38 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트 스터디] 자바스크립트의 데이터 타입, 참조형</title>
      <link>https://kimkani.tistory.com/41</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNiQvI/btsjTuP84dN/7xCBODU8W39qDEH4NuLLVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNiQvI/btsjTuP84dN/7xCBODU8W39qDEH4NuLLVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNiQvI/btsjTuP84dN/7xCBODU8W39qDEH4NuLLVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNiQvI%2FbtsjTuP84dN%2F7xCBODU8W39qDEH4NuLLVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;493&quot; height=&quot;493&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1686756412073&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자바스크립트 스터디] 자바스크립트의 데이터 타입, 원시형&quot; data-og-description=&quot;최근에 코어 자바스크립트를 통해 아는 친구의 대학 후배들과 스터디를 진행하고 있다. 그에 대한 내용을 정리할 겸, 이제부터 하나하나 찬찬히 참조해보려고 한다. 1. 자바스크립트의 데이터 &quot; data-og-host=&quot;kimkani.tistory.com&quot; data-og-source-url=&quot;https://kimkani.tistory.com/37&quot; data-og-url=&quot;https://kimkani.tistory.com/37&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/J4eXn/hySYKjDRDI/uJ1X2MdmL8tbTX1W69uapk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bwtSDf/hySYL3VU1U/jsXC27lfiyRXHQsO7BgSi1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bzntAW/hySYMV4z2y/EeyDtWb27jLmnPzlAifVO0/img.png?width=1268&amp;amp;height=634&amp;amp;face=0_0_1268_634&quot;&gt;&lt;a href=&quot;https://kimkani.tistory.com/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kimkani.tistory.com/37&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/J4eXn/hySYKjDRDI/uJ1X2MdmL8tbTX1W69uapk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bwtSDf/hySYL3VU1U/jsXC27lfiyRXHQsO7BgSi1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bzntAW/hySYMV4z2y/EeyDtWb27jLmnPzlAifVO0/img.png?width=1268&amp;amp;height=634&amp;amp;face=0_0_1268_634');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[자바스크립트 스터디] 자바스크립트의 데이터 타입, 원시형&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;최근에 코어 자바스크립트를 통해 아는 친구의 대학 후배들과 스터디를 진행하고 있다. 그에 대한 내용을 정리할 겸, 이제부터 하나하나 찬찬히 참조해보려고 한다. 1. 자바스크립트의 데이터&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kimkani.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 스택과 힙 메모리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 당신이 변수를 선언하면, 자바스크립트 엔진은 이 변수들을 위해 두개의 메모리 지역 - 힙과 스택 - 에 변수들을 할당한다. 앞 글에서 설명했듯이 스택에는 미리 사이즈를 알 수 있는 데이터만이 들어가며, 그 외에 런타임시 변경될 수 있는 데이터는 힙에 들어간다. 이러한 데이터는 바로 Object(객체) 혹은 function(함수)이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-30 01.16.47.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7ZGSp/btsjWq0TOLX/46zlPh8KHT0kUlNZdp8RtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7ZGSp/btsjWq0TOLX/46zlPh8KHT0kUlNZdp8RtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7ZGSp/btsjWq0TOLX/46zlPh8KHT0kUlNZdp8RtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7ZGSp%2FbtsjWq0TOLX%2F46zlPh8KHT0kUlNZdp8RtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1268&quot; height=&quot;634&quot; data-filename=&quot;스크린샷 2023-04-30 01.16.47.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우를 예시로 hobbies랑 person이 어떻게 되는지 한번 알아보겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1002&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1003&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1004&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;이름 : hobbies&lt;br /&gt;값 : @5002&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 100%; height: 36px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;5002&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;5003&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;5004&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;@7003~@?&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;28&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;&quot;MAX&quot;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;객체 영역(heap)&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;7003&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;7004&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;7005&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;이름: age&lt;br /&gt;값: @5003&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;이름: name&lt;br /&gt;값: @5004&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 우리가 var age = 28과 var name = &quot;MAX&quot;를 할당한다면 다음과 같은 경우가 일어난다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #444444; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;변수 영역에서 빈 공간 (@1002)을 확보한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;확보한 공간의 식별자를 hobbies로 정한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;이때, 이 hobbies의 경우 여러개의 프로퍼티로 이루어져 있기 때문에 별도의 데이터 영역에서 주소를 @7003 ~ @? 으로 지정한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;@7003 및 @7004에 age와 name이라는 프로퍼티 이름을 지정한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;데이터 영역에서 숫자 28과 이름 &quot;MAX&quot;를 검색하고 없으므로 임의로 @5003과 @5004에 저장한다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 데이터 영역은 불변값이다. 다만 변수에는 얼마든지 다른 값을 넣을 수 있다. 이 부분에서 흔히 참조형 데이터는 가변이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 여기서 중첩으로 객체를 만든다면 어떻게 될까?&lt;/p&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686758426648&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var hobbies = {
	age: 28,
    hobby: [&quot;baseball&quot;, &quot;soccer&quot;, &quot;home&quot;]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 100%; height: 54px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;1002&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;1003&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;1004&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 36px;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px;&quot;&gt;이름 : hobbies&lt;br /&gt;값 : @5002&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 99.8828%; height: 71px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 15.9302%;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 12.5737%;&quot; colspan=&quot;2&quot;&gt;5002&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.5733%;&quot;&gt;5003&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 11.9728%;&quot;&gt;5004&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.6997%;&quot;&gt;5005&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 13.3714%;&quot;&gt;5006&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.0754%;&quot;&gt;5007&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.0754%;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 15.9302%;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 12.5737%;&quot; colspan=&quot;2&quot;&gt;@7003~@?&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16.5733%;&quot;&gt;28&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 11.9728%;&quot;&gt;baseball&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 15.6997%;&quot;&gt;@8003~@?&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 13.3714%;&quot;&gt;soccer&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.0754%;&quot;&gt;home&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 9.0754%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;객체 영역(heap)&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;7003&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;7004&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;7005&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;이름: age&lt;br /&gt;값: @5003&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;이름: hobby&lt;br /&gt;값: @5005&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;객체 영역(heap)&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 100%; height: 54px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 18px; width: 16.744186%;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px; width: 26.511628%;&quot;&gt;8003&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px; width: 21.162791%;&quot;&gt;8004&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 10.465116%;&quot;&gt;8005&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px; width: 8.837209%;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 36px; width: 16.744186%;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px; width: 26.511628%;&quot;&gt;이름: 0&lt;br /&gt;값: @5004&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px; width: 21.162791%;&quot;&gt;이름: 1&lt;br /&gt;값: @5006&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 10.465116%;&quot;&gt;이름: 2&lt;br /&gt;값: @5007&lt;/td&gt;
&lt;td style=&quot;width: 8.837209%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 컴퓨터에서 우선 변수 영역의 빈 공간을 탐색한다. 그리고 위의 예시에서는 @1002에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이때 hobbies의 경우는 여러개의 변수와 값들을 모아놓은 객체이다. 따라서 hobby와 age를 저장하기 위한 별도의 변수 영역을 마련한다. 그 공간은 @7003 ~ @?이 된다. 그 이후에 그 영역의 주소를 @5002에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. @7003에는 age를 @7004에는 hobby를 선언한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 데이터 영역에서 숫자 28을 검색하고 없으므로 임의의 주소인 @5003에 저장한다. 이는 다시 @7003에 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 이때 hobby의 경우 여러개의 변수와 값들을 모아놓은 객체이다. 이때 이 객체는 배열이므로 이 배열 들의 원소를 저장할 별도의 변수 영역을 마련한다. 이 때 이 변수 영역은 @8003 ~ @?가 되고 이를 @5005에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 그 후 각 값을 찾고서 없으면 새로운 영역에 저장한 다음 각각의 @8003~에 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 여기서 hobbies.hobby = 28로 할당하면 어찌 될까?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 100%; height: 54px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;1002&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;1003&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;1004&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 36px;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px;&quot;&gt;이름 : hobbies&lt;br /&gt;값 : @5002&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot; colspan=&quot;2&quot;&gt;5002&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;5003&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;5004&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;5005&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;5006&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;5007&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot; colspan=&quot;2&quot;&gt;@7003~@?&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;28&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;baseball&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;@8003~@?&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;soccer&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;home&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #444444; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;객체 영역(heap)&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;7003&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;7004&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;7005&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;이름: age&lt;br /&gt;값: @5003&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;이름: hobby&lt;br /&gt;값: @5003&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;객체 영역(heap)&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #444444; text-align: start; border-collapse: collapse; width: 100%; height: 54px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 18px; width: 16.744186%;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px; width: 26.511628%;&quot;&gt;&lt;b&gt;8003&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px; width: 21.162791%;&quot;&gt;&lt;b&gt;8004&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 10.465116%;&quot;&gt;&lt;b&gt;8005&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 18px; width: 8.837209%;&quot;&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 36px; width: 16.744186%;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px; width: 26.511628%;&quot;&gt;&lt;b&gt;이름: 0&lt;/b&gt;&lt;br /&gt;&lt;b&gt;값: @5004&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 36px; width: 21.162791%;&quot;&gt;&lt;b&gt;이름: 1&lt;/b&gt;&lt;br /&gt;&lt;b&gt;값: @5006&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 10.465116%;&quot;&gt;&lt;b&gt;이름: 2&lt;/b&gt;&lt;br /&gt;&lt;b&gt;값: @5007&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.837209%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 @7004에 @5003의 값이 할당되어 있다. 이전까지는 참조하던 값들이 있었지만, 그 이후에는 참조하던 값들이 사라진다. 자신의 주소를 참조하는 변수의 개수를 참조 카운트라고 한다. 그리고 이러한 참조 카운트가 없어진 것이 위의 표에서 볼드체 된 곳들이다. 참조 카운트가 0인 메모리 주소는 Garbage Collector, GC의 수거 대상이 되며, 런타임 호나경에 따라 특정 시점에 수거 - 즉 메모리에서 비할당 - 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 참조형의 복사에 대해서 알아보겠다.&lt;/p&gt;</description>
      <category>JavaScript</category>
      <category>javascript</category>
      <category>자바스크립트</category>
      <category>컴퓨터</category>
      <category>코딩</category>
      <category>프로그래머</category>
      <category>프로그래밍</category>
      <category>프로그램</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/41</guid>
      <comments>https://kimkani.tistory.com/41#entry41comment</comments>
      <pubDate>Thu, 15 Jun 2023 01:44:54 +0900</pubDate>
    </item>
    <item>
      <title>[클라우드] CKA 합격 및 후기, 혹은 팁</title>
      <link>https://kimkani.tistory.com/40</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;293&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfyea0/btsiQ6HqITI/31dTL91463NKEK8ZApGGB1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfyea0/btsiQ6HqITI/31dTL91463NKEK8ZApGGB1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfyea0/btsiQ6HqITI/31dTL91463NKEK8ZApGGB1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcfyea0%2FbtsiQ6HqITI%2F31dTL91463NKEK8ZApGGB1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;293&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;293&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;1. 회사로부터 주어진 첫 미션&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;회사에서 나에게 미션을 주었다. 바로 CKA - 풀어 쓰자면 Certified Kubernetes Administrator -를 따라는 특명이었다. 이미 다른 팀원 몇명도 가지고 있었고 나도 필요성을 느끼긴 했다. 그래서 그렇게 3월 초부터 CKA를 공부하기 시작했다. 다행히 회사에서는 다양한 영상 소스 - Udemy 등등을 비롯한 수강 사이트 - 등이 무료였기 때문에, 마음껏 들을 수 있었다. 특히 CKA강의로 유명한 Mumshad님의 강의도 있었기에, 충분한 학습 환경이 무료로 - 무료라고 해야될지는 모르겠다 - 형성되어 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 그렇게 나는 3월 초부터 책 몇권을 읽으면서 CKA시험 준비를 시작했다.&lt;br /&gt;&lt;br /&gt;* 참고로 나는 어느정도 vim과 리눅스 혹은 그 계열인 우분투 환경에서 커맨드 라인을 다룰줄 아는 상황에서 공부한 환경을 이야기 하려고 한다.&lt;br /&gt;&lt;br /&gt;** 쿠버네티스 시험은 전적으로 vim과 커맨드 라인을 얼마나 잘 사용하느냐의 시간 싸움이라고 생각한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;2. CKA를 공부하기 위해 사용한 책&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9EJ9v/btsiM96FdpR/4gE2Z23kKKkjHnbKuto2Ak/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9EJ9v/btsiM96FdpR/4gE2Z23kKKkjHnbKuto2Ak/img.jpg&quot; data-origin-width=&quot;488&quot; data-origin-height=&quot;628&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;49.28&quot; style=&quot;width: 48.7101%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9EJ9v/btsiM96FdpR/4gE2Z23kKKkjHnbKuto2Ak/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9EJ9v%2FbtsiM96FdpR%2F4gE2Z23kKKkjHnbKuto2Ak%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;488&quot; height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvZy4C/btsiNvBJlDq/c1tL1fKbJJbHVKGedQYIkk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvZy4C/btsiNvBJlDq/c1tL1fKbJJbHVKGedQYIkk/img.jpg&quot; data-origin-width=&quot;495&quot; data-origin-height=&quot;619&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.1272%;&quot; data-widthpercent=&quot;50.72&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvZy4C/btsiNvBJlDq/c1tL1fKbJJbHVKGedQYIkk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvZy4C%2FbtsiNvBJlDq%2Fc1tL1fKbJJbHVKGedQYIkk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;495&quot; height=&quot;619&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;사실 책같은 경우는 시험을 보신 분이라면 알겠지만, 크게 도움되지 않았다. 물론 나는 이미 yaml이나 도커에 익숙했기 때문에 리프레시를 위해 다시 한번 더 본 경우도 있다. 하지만 개인적으로 책으로 하는 공부는 쿠버네티스를&amp;nbsp;&amp;nbsp;- CKA 시험을 - 다루기 위한 전초전이라고 생각해 주면 좋겠다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;나같은 다음 같은 경우의 책들을 가볍게 훑어 보기만 했다. 이전에 모르는 부분이 있거나 잘 이해가 안되는 경우가 있다면 보는 정도였다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;나는 위의 두 책을 이용했다. 물론 전체적인 컨셉을 잡는 용도로만 사용했다. 보통 &quot;컨테이너 인프라 환경을 구축을 위한 쿠버네티스/도커&quot;, &quot;쿠버네티스 인 액션&quot;이 두 책은 참고용으로만 썼다. 딱히 추천하지는 않고, 그냥 이 책이 그냥 괜찮아보여서 샀을 뿐 절대적인 것은 아니다. 그리고 CKA를 위한 책도 간간히 보이는데 개인적으로는 추천하지 않는다. CKA라는 시험은 결국 지식 테스트라기 보다는 실전에서 어떻게 잘 사용하는지에 대한 테스트 성격이 강하다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;내가 추천하는 방법은 바로 Mumshad님의 CKA강의이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;3. CKA - Udemy강의&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.udemy.com/course/learn-kubernetes/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.udemy.com/course/learn-kubernetes/&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AbvQu/btsiPONZdhb/FmKLGueuEpa3ocq6PIFzY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AbvQu/btsiPONZdhb/FmKLGueuEpa3ocq6PIFzY0/img.png&quot; data-origin-width=&quot;2292&quot; data-origin-height=&quot;1152&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.6583%; margin-right: 10px;&quot; data-widthpercent=&quot;51.25&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AbvQu/btsiPONZdhb/FmKLGueuEpa3ocq6PIFzY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAbvQu%2FbtsiPONZdhb%2FFmKLGueuEpa3ocq6PIFzY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2292&quot; height=&quot;1152&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kFJQb/btsiO53wgvc/c38xKqIdZkepxRipIACbG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kFJQb/btsiO53wgvc/c38xKqIdZkepxRipIACbG0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2282&quot; data-origin-height=&quot;1206&quot; data-filename=&quot;스크린샷 2023-06-05 21.46.08.png&quot; data-widthpercent=&quot;48.75&quot; style=&quot;width: 48.1789%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kFJQb/btsiO53wgvc/c38xKqIdZkepxRipIACbG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkFJQb%2FbtsiO53wgvc%2Fc38xKqIdZkepxRipIACbG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2282&quot; height=&quot;1206&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 위의 두 강의를 추천한다. 먼저 첫번째 강의는 &quot;Kubernetes for the Absolute Beginners - Hands-on&quot;이라는 강의로, 전체적인 쿠버네티스 개념, 그리고 쿠버네티스에 대한 입문같은 느낌으로 다가갈 수 있다. 친절하게 설명해주기 때문에 쿠버네티스가 뭔지는 잘 몰라도 가볍게 처음에 다가갈 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 강의는 &quot;Certified Kubernetes Administrator (CKA) with Practice Tests&quot;라는 강의로 이 강의가 바로 진면목을 보여준다. 전체적인 량은 많지만, 그만큼 알찬 구성과 함께 CKA에 필수적인 요소들을 가르쳐 준다. 특히 이 강의에서 제공하는 CKA를 위한 샌드박스 플랫폼 - 라고 말하지만, 사실 다양한 데브옵스 및 쿠버네티스 등등 활용할 수 있는 요소들이 많다 - Kodekloud를 같이 제공해줘서 그곳에서 쿠버네티스 명령어 및 테스트를 여러번 돌리면서 공부할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. CKA 시험 결제 및 할인 팁&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-05 21.59.45.png&quot; data-origin-width=&quot;2606&quot; data-origin-height=&quot;878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tFwxV/btsiJyZ1ijW/ZooQadKOVHQIiOSS6ncSK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tFwxV/btsiJyZ1ijW/ZooQadKOVHQIiOSS6ncSK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tFwxV/btsiJyZ1ijW/ZooQadKOVHQIiOSS6ncSK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtFwxV%2FbtsiJyZ1ijW%2FZooQadKOVHQIiOSS6ncSK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2606&quot; height=&quot;878&quot; data-filename=&quot;스크린샷 2023-06-05 21.59.45.png&quot; data-origin-width=&quot;2606&quot; data-origin-height=&quot;878&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CKA의 경우 아주 살인적인 가격을 자랑한다. 395달러, 현재 환율로 환산하면, 대략 50~55만원 사이의 사악한 가격을 자랑한다. 하지만 이런 사악한 가격을 알기에, CKA주관사인 Linux Foundation도 할인쿠폰을 달 초에 자주 뿌린다. 물론 사이버 먼데이라고 하는 것도 있지만, 그 경우 기간이 연말로 한정되어 있기 때문에 개인적으로 공식 사이트에서 월초마다 뿌리는 30~40프로 정도의 할인 쿠폰을 겟 하는 것도 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XYCvc/btsiNvBNQXN/LxZaG7AgMk2pzkQkp0TNF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XYCvc/btsiNvBNQXN/LxZaG7AgMk2pzkQkp0TNF0/img.png&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;501&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.1353%; margin-right: 10px;&quot; data-widthpercent=&quot;46.68&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XYCvc/btsiNvBNQXN/LxZaG7AgMk2pzkQkp0TNF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXYCvc%2FbtsiNvBNQXN%2FLxZaG7AgMk2pzkQkp0TNF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;901&quot; height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUgwWG/btsiPiIqeJa/0NkaHuth9hsAALQUkppmd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUgwWG/btsiPiIqeJa/0NkaHuth9hsAALQUkppmd0/img.png&quot; data-origin-width=&quot;2720&quot; data-origin-height=&quot;1324&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.702%;&quot; data-widthpercent=&quot;53.32&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUgwWG/btsiPiIqeJa/0NkaHuth9hsAALQUkppmd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUgwWG%2FbtsiPiIqeJa%2F0NkaHuth9hsAALQUkppmd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2720&quot; height=&quot;1324&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 위의 사이트에서 최대 50% - 물론 이 경우는 번들 팩이고 대부분의 경우는 - 40%의 할인 쿠폰을 뿌린다. 특히 월 초마다 대략 25일까지 사용할 수 있는 쿠폰을 뿌린다. 위의 사진처럼 검색하면 Bundle &amp;amp; Save에 유효한 할인코드를 뿌린다. 나의 경우 40%로 할인을 받아 사용했다. 아직 글을 쓰는 현 시점에서 6월 프로모션은 안보이지만, 매일 들어가서 확인해 보는 것도 추천한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2312&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XBZhD/btsiO5JdHLX/U5u8tWkqKcUr5KrkwTn3tk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XBZhD/btsiO5JdHLX/U5u8tWkqKcUr5KrkwTn3tk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XBZhD/btsiO5JdHLX/U5u8tWkqKcUr5KrkwTn3tk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXBZhD%2FbtsiO5JdHLX%2FU5u8tWkqKcUr5KrkwTn3tk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2312&quot; height=&quot;546&quot; data-origin-width=&quot;2312&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 나같은 경우에는 237달러의 가격에 샀다. 대충 300달러 미만으로 결제하는 것이 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. CKA 시험 실전 팁&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGN9Sb/btsiM8Ufy9Z/kcCeC6e5LRKUBhC6nOYL71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGN9Sb/btsiM8Ufy9Z/kcCeC6e5LRKUBhC6nOYL71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGN9Sb/btsiM8Ufy9Z/kcCeC6e5LRKUBhC6nOYL71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGN9Sb%2FbtsiM8Ufy9Z%2FkcCeC6e5LRKUBhC6nOYL71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;350&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 환경에서 모든 것이 이루어진다고 보면 된다. 참고로 나는 맥을 사용했는데, 맥의 경우 Command + C,V가 기본 카피, 페이스트인데, 먹히지 않는다. 다른 방법으로 있던 것 같은데, 나는 개인적으로 마우스로 복사 붙여넣기를 하는 것이 더 마음에 편하다고 생각한다. 시험 문제는 17문제로 이루어져 있으며, 4%, 7% 그리고 고득점 문제가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2시간 동안 시험이 이루어지며, 대략 시험 시간 30분 전부터 억섹스 가능하다. 시험 환경은 시험관 마다 다르지만, 꼼꼼하게 체크한다. 보통 웹캠으로 사방팔방을 다 비춰야 하며, 시험 환경이 이루어지는 책상 밑에도 비춰야 한다. 그 외에 핸드폰도 보여주고 멀리 놓았다는 것도 인증해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;killer.sh라는 시험 풀이 사이트가 있는데, 개인적으로 다 풀어보기 보다는 한번 &quot;이런 환경에서 시험을 본다&quot;는 식으로 접근하는 것이 좋다. killer.sh는 굉장히 문제가 극악 난이도이며 이정도 난이도에서는 문제가 출제되지 않았기 때문이다. 오히려 Mumshad님의 kodekloud에서 Mock Exam 1,2,3그리고 Lightening Lab을 여러번 푸는 것을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 문제가 있겠지만, Pod생성, Multi Container Pod생성, Deployment 스케일 업, Kubelet, Kubectl그리고 Kubeadm을 통한 업그레이드 등의 문제가 있다. 특히 Kubeadm을 통한 업그레이드 문제는 전체적으로 시간을 많이 잡아먹기에 나중에 푸는 것을 추천한다. 그 외에도 여러 문제가 있는데, 다음 링크를 참고해서 보는 것을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1685971131074&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Certified Kubernetes Administrator(CKA) 자격증 합격 후기 및 유용한 팁 (2022.02, v1.23)&quot; data-og-description=&quot;CKA는 쿠버네티스 클러스터 관리 능력을 검증하는 대표적인 국제 자격증 중 하나다. 엔지니어 경험이 없는 초심자로서 이 CKA 자격증 시험에 도전하여 합격한 과정과 후기를 공유한다. 같은 시험&quot; data-og-host=&quot;seongjin.me&quot; data-og-source-url=&quot;https://seongjin.me/how-to-prepare-cka-exam/&quot; data-og-url=&quot;https://seongjin.me/how-to-prepare-cka-exam/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bGlkgD/hySTWikSk1/xKKP8X5kEUVJAxKy1dpOq1/img.png?width=2000&amp;amp;height=1040&amp;amp;face=0_0_2000_1040,https://scrap.kakaocdn.net/dn/cOZBT7/hySTPXN6VT/ssCOdFFLiW45PkjkF4znCk/img.png?width=2000&amp;amp;height=1040&amp;amp;face=0_0_2000_1040,https://scrap.kakaocdn.net/dn/dNfdvH/hySR3wClWd/47l88VpvrVIn5ncAgycLX1/img.png?width=2752&amp;amp;height=1282&amp;amp;face=0_0_2752_1282&quot;&gt;&lt;a href=&quot;https://seongjin.me/how-to-prepare-cka-exam/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://seongjin.me/how-to-prepare-cka-exam/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bGlkgD/hySTWikSk1/xKKP8X5kEUVJAxKy1dpOq1/img.png?width=2000&amp;amp;height=1040&amp;amp;face=0_0_2000_1040,https://scrap.kakaocdn.net/dn/cOZBT7/hySTPXN6VT/ssCOdFFLiW45PkjkF4znCk/img.png?width=2000&amp;amp;height=1040&amp;amp;face=0_0_2000_1040,https://scrap.kakaocdn.net/dn/dNfdvH/hySR3wClWd/47l88VpvrVIn5ncAgycLX1/img.png?width=2752&amp;amp;height=1282&amp;amp;face=0_0_2752_1282');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Certified Kubernetes Administrator(CKA) 자격증 합격 후기 및 유용한 팁 (2022.02, v1.23)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;CKA는 쿠버네티스 클러스터 관리 능력을 검증하는 대표적인 국제 자격증 중 하나다. 엔지니어 경험이 없는 초심자로서 이 CKA 자격증 시험에 도전하여 합격한 과정과 후기를 공유한다. 같은 시험&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;seongjin.me&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1685971518387&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;CKA 대비 공부한 문제들&quot; data-og-description=&quot;다음은, CKA 공부를 하며 정리한 내용으로, 시험을 보시는 분들에게 도움이 되길 바랍니다. 1. 시험 문제 풀기 전 반드시 해야할 것 1) Cheat Sheet 확인해 자동완성 활성화 https://kubernetes.io/docs/reference&quot; data-og-host=&quot;ls-altr.tistory.com&quot; data-og-source-url=&quot;https://ls-altr.tistory.com/82&quot; data-og-url=&quot;https://ls-altr.tistory.com/82&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nAt0p/hySRZ13hZV/xwRrUWlWcCH6PxNL9eKEe1/img.png?width=800&amp;amp;height=467&amp;amp;face=0_0_800_467,https://scrap.kakaocdn.net/dn/bakQQ3/hySR1lf4CM/nEk8JbWDO5LCRno3CF0kKK/img.png?width=800&amp;amp;height=467&amp;amp;face=0_0_800_467,https://scrap.kakaocdn.net/dn/bcCnGQ/hySRVZFEOa/xLZML6M5lAvqoBaW6Ivpnk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://ls-altr.tistory.com/82&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ls-altr.tistory.com/82&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nAt0p/hySRZ13hZV/xwRrUWlWcCH6PxNL9eKEe1/img.png?width=800&amp;amp;height=467&amp;amp;face=0_0_800_467,https://scrap.kakaocdn.net/dn/bakQQ3/hySR1lf4CM/nEk8JbWDO5LCRno3CF0kKK/img.png?width=800&amp;amp;height=467&amp;amp;face=0_0_800_467,https://scrap.kakaocdn.net/dn/bcCnGQ/hySRVZFEOa/xLZML6M5lAvqoBaW6Ivpnk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CKA 대비 공부한 문제들&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;다음은, CKA 공부를 하며 정리한 내용으로, 시험을 보시는 분들에게 도움이 되길 바랍니다. 1. 시험 문제 풀기 전 반드시 해야할 것 1) Cheat Sheet 확인해 자동완성 활성화 https://kubernetes.io/docs/reference&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ls-altr.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1685971536102&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[쿠버네티스] CKA 기출 자료_1 [RBAC, Node]  | 데인트리 라이브러리&quot; data-og-description=&quot;이 페이지에서는 CKA 문제를 풀기위한 여러 쿠버네티스 기술을 다룬다. *해당 포스트는 컨테이너의 개념과 쿠버네티스의 리소스에 대한 사전 지식이 필요합니다. CKA 시험은 직접 쿠버네티스 플&quot; data-og-host=&quot;daintree.tistory.com&quot; data-og-source-url=&quot;https://daintree.tistory.com/13&quot; data-og-url=&quot;https://daintree.tistory.com/13&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bmLT2F/hySRWEf8DM/JKZgMW3zREFGk84hyZss60/img.png?width=800&amp;amp;height=264&amp;amp;face=0_0_800_264,https://scrap.kakaocdn.net/dn/mfCK7/hySRY3aaxR/EwKFCr7BYhnqmsynPvcAwK/img.png?width=800&amp;amp;height=264&amp;amp;face=0_0_800_264,https://scrap.kakaocdn.net/dn/MkMSp/hySR2R0tJi/Suq7IWa0dTFk7OkYfyopc0/img.png?width=1387&amp;amp;height=458&amp;amp;face=0_0_1387_458&quot;&gt;&lt;a href=&quot;https://daintree.tistory.com/13&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://daintree.tistory.com/13&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bmLT2F/hySRWEf8DM/JKZgMW3zREFGk84hyZss60/img.png?width=800&amp;amp;height=264&amp;amp;face=0_0_800_264,https://scrap.kakaocdn.net/dn/mfCK7/hySRY3aaxR/EwKFCr7BYhnqmsynPvcAwK/img.png?width=800&amp;amp;height=264&amp;amp;face=0_0_800_264,https://scrap.kakaocdn.net/dn/MkMSp/hySR2R0tJi/Suq7IWa0dTFk7OkYfyopc0/img.png?width=1387&amp;amp;height=458&amp;amp;face=0_0_1387_458');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[쿠버네티스] CKA 기출 자료_1 [RBAC, Node] | 데인트리 라이브러리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 페이지에서는 CKA 문제를 풀기위한 여러 쿠버네티스 기술을 다룬다. *해당 포스트는 컨테이너의 개념과 쿠버네티스의 리소스에 대한 사전 지식이 필요합니다. CKA 시험은 직접 쿠버네티스 플&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;daintree.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;- 그 외&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_kwanhee-kim-500bd2b4-b6c4-4db2-ba45-f0361f875189-certificate1024_1.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mhHxB/btsiQ6U3gsN/Y5XTBDbA3TAUvDA0Lkg850/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mhHxB/btsiQ6U3gsN/Y5XTBDbA3TAUvDA0Lkg850/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mhHxB/btsiQ6U3gsN/Y5XTBDbA3TAUvDA0Lkg850/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmhHxB%2FbtsiQ6U3gsN%2FY5XTBDbA3TAUvDA0Lkg850%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;794&quot; data-filename=&quot;edited_kwanhee-kim-500bd2b4-b6c4-4db2-ba45-f0361f875189-certificate1024_1.jpeg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험의 경우 66점 이상 맞아야 통과이며, 나같은 경우 첫 시험에서는 65점을 맞아 아쉬운 불합격을 맞았고 그 이후에 81점을 맞아 무난하게 통과했다. 시험을 회고해 보자면, 상당히 난해하지는 않지만, 도전적인 시험이다. 무조건 암기식 보다는 이 상황에서 공식 독스를 보며 어떻게 응용해서 풀어야 하는지를 보는 아주 실전적인 시험이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 분들의 도전을 응원하며 이만 글을 마친다. 다들 화이팅.&lt;/p&gt;</description>
      <category>ETC</category>
      <category>CKA</category>
      <category>IT</category>
      <category>kubernetes</category>
      <category>네트워크</category>
      <category>리눅스</category>
      <category>자격증</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>쿠버네티스</category>
      <category>클라우드</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/40</guid>
      <comments>https://kimkani.tistory.com/40#entry40comment</comments>
      <pubDate>Mon, 5 Jun 2023 22:29:55 +0900</pubDate>
    </item>
    <item>
      <title>[번역] 파이썬에서의 타입 체킹, mypy를 중심으로</title>
      <link>https://kimkani.tistory.com/39</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;R1280x0-2.jpeg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MA8QI/btsd4eYeXj8/KbJQCtrYCb60KAbvr4AK20/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MA8QI/btsd4eYeXj8/KbJQCtrYCb60KAbvr4AK20/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MA8QI/btsd4eYeXj8/KbJQCtrYCb60KAbvr4AK20/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMA8QI%2Fbtsd4eYeXj8%2FKbJQCtrYCb60KAbvr4AK20%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;807&quot; data-filename=&quot;R1280x0-2.jpeg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1683351031706&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Python Type Checking&quot; data-og-description=&quot;This article looks at what type hints are and how they can benefit you. We'll also dive into how you can use Python's type system for type checking.&quot; data-og-host=&quot;testdriven.io&quot; data-og-source-url=&quot;https://testdriven.io/blog/python-type-checking/&quot; data-og-url=&quot;https://testdriven.io/blog/python-type-checking/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/UZbm1/hySwFuEruV/3WuBsEjyVvRSzLmLX9MXw1/img.png?width=2028&amp;amp;height=1004&amp;amp;face=0_0_2028_1004,https://scrap.kakaocdn.net/dn/ebglWe/hySvxdVkYU/hEqfVmKIb7LJY5IehEjELk/img.png?width=2028&amp;amp;height=1004&amp;amp;face=0_0_2028_1004&quot;&gt;&lt;a href=&quot;https://testdriven.io/blog/python-type-checking/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://testdriven.io/blog/python-type-checking/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/UZbm1/hySwFuEruV/3WuBsEjyVvRSzLmLX9MXw1/img.png?width=2028&amp;amp;height=1004&amp;amp;face=0_0_2028_1004,https://scrap.kakaocdn.net/dn/ebglWe/hySvxdVkYU/hEqfVmKIb7LJY5IehEjELk/img.png?width=2028&amp;amp;height=1004&amp;amp;face=0_0_2028_1004');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Python Type Checking&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This article looks at what type hints are and how they can benefit you. We'll also dive into how you can use Python's type system for type checking.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;testdriven.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;타입 체킹이란 무엇일까? 왜 필요할까? 정적 타입하고 런타입 타입체킹하고 차이는 무엇일까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬은 강력한 동적 프로그래밍 언어이다. 동적으로 코드를 타이핑하면, 타이핑한 것은 동적으로 추론되며 자바같은 정적 타입 프로그래밍 언어와 달리, 변수 값을 직접적으로 타입 정의 없이 세팅할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683351300027&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name = &quot;Michael&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1683351318407&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String name = &quot;Michael&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t8e0K/btsd0o1QuNq/rtMnFfy0MSVy6tiQyEHr41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t8e0K/btsd0o1QuNq/rtMnFfy0MSVy6tiQyEHr41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t8e0K/btsd0o1QuNq/rtMnFfy0MSVy6tiQyEHr41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft8e0K%2Fbtsd0o1QuNq%2FrtMnFfy0MSVy6tiQyEHr41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;416&quot; height=&quot;372&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;강하면서 동적이라는 말은 타입들이 런타임 시에 추론되지만, 타입들 끼리 섞을 수 없다는 말이다. 예를 들어 a = 1 + &quot;0&quot;은 파이썬에서 에러를 일으킬 것이다. 반대로, 자바스크립트는 약하면서 동적이란 말은 런타임 시에 추론되면서 타입들 끼리 섞을 수 있다는 말이다. 예를 들어, a = 1 + &quot;0&quot;을 하면 a는 10이 될 것이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 타이핑이 유연성을 가지는 반면 - 언제나 희망 사항은 아니지만, 나중에 가서 동적 언어에 정적 타입 추론을 적용할 노력을 할 필요가 생기기도 한다. 이 글에서는, 어던 타입 힌트가 어떻게 당신에게 도움을 줄지 볼 것이다. 또한 mypy를 통해 정적 타입 체킹을 파이썬의 타입 시스템을 사용하는지 파고볼 것이며, 그리고 pydantic, marshmallow 그리고 typeguard를 통해 런타임 타입 체킹을 사용할 수 있는지 알아볼 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;tools&quot; style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;툴 - Tools&lt;/h2&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;몇몇 툴들이 타입 힌트를 이용하여 정적과 런타임 타입 체킹을 할 수 있게 도와준다.&lt;/p&gt;
&lt;h3 id=&quot;static-typing&quot; style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;Static typing&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/python/mypy&quot;&gt;mypy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://pyre-check.org/&quot;&gt;pyre&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/Microsoft/pyright&quot;&gt;Pyright&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/google/pytype&quot;&gt;pytype&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/quora/pyanalyze&quot;&gt;pyanalyze&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;runtime-type-checking-data-validation&quot; style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;Runtime type checking / data validation&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://marshmallow.readthedocs.io/en/stable/&quot;&gt;marshmallow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/samuelcolvin/pydantic&quot;&gt;pydantic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/agronholm/typeguard&quot;&gt;typeguard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/seandstewart/typical/&quot;&gt;typical&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/Stewori/pytypes&quot;&gt;pytypes&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;project-specific&quot; style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;Project specific&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://pypi.org/project/pydantic-django/&quot;&gt;pydantic-django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://pypi.org/project/django-stubs/&quot;&gt;django-stubs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/typeddjango&quot;&gt;typeddjango&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://pypi.org/project/Flask-Pydantic/&quot;&gt;flask-pydantic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-marshmallow.readthedocs.io/en/latest/&quot;&gt;flask-marshmallow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://fastapi.tiangolo.com/&quot;&gt;fastapi&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(pydantic is built in -- yay!)&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote style=&quot;color: #6f6f6f; text-align: left;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/typeddjango/awesome-python-typing&quot;&gt;Awesome Python Typing&lt;/a&gt;을 확인해보자&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;타입 힌트 - Type Hints&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 3.5에서부터 타입 힌트가 추가되었다. 이러한 타입 힌트는 개발자로 하여금 파이썬 코드에서 변수, 함수 파라미터, 그리고 함수의 리턴값의 기대되는 타입들을 명시해준다. 물론 이러한 타입들은 파이썬 인터프리터로 하여금 강제되는 것은 아니지만 - 다시금 말하지만, 파이썬은 동적 타입 언어이다 - 몇몇 이점을 제시해준다. 먼저 타입 힌트를 통한 강력한 것은 코드를 통해 하려는 것과 어떻게 사용하는지에 대한 의도를 더 좋게 표현할 수 있다는 것이다. 더 좋은 이해는 적은 버그의 결과로 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 나날의 기온 평균을 계산하는 함수를 작성했다고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1683352070748&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def daily_average(temperatures):
    return sum(temperatures) / len(temperatures)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기온 리스트를 제대로 넣어주는 한, 함수는 의도한대로 작동하며, 기대된 결과도 리턴해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1683352177106&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;average_temperature = daily_average([22.8, 19.6, 25.9])
print(average_temperature)  # =&amp;gt; 22.76666666666667&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 키값이 측정 값의 타임스탬프이고 밸류는 측정 값인 딕셔너리를 넣으면 어떻게 될까?&lt;/p&gt;
&lt;pre id=&quot;code_1683352259427&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;average_temperature = daily_average({1599125906: 22.8, 1599125706: 19.6, 1599126006: 25.9})
print(average_temperature)  # =&amp;gt; 1599125872.6666667&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필연적으로 이 함수는 &quot;키값의 합 / 키값의 수&quot;를 리턴하며 이는 아주 잘못된 결과이다. 아무래도 함수가 에러를 발생시키지 않으니, 특히 유저에게 제공되는 이 온도는 곧 발견되지 못한다. 이러한 혼동을 피하기 위해 파라미터랑 리턴 값에 타입 힌트를 명시할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1683352410649&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def daily_average(temperatures: list[float]) -&amp;gt; float:
    return sum(temperatures) / len(temperatures)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 함수 정의는 우리에게&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;temperatures 소수 리스트로 이루어져 있어야 한다 : temperatures: list[float]&lt;/li&gt;
&lt;li&gt;함수는 소수를 리턴해야 한다 : -&amp;gt; float&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1683352482279&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(daily_average.__annotations__)
# {'temperatures': list[float], 'return': &amp;lt;class 'float'&amp;gt;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 힌트는 정적 타입 체킹 툴을 가능하게 해준다. 코드 에디터나 IDE는 이를 잘 사용하고 있으며, 타입 힌트에 따라 특정 함수나 메소드를 사용할 때 경고해주며, 강력한 자동완성을 제공해준다. 그치만, 타입 힌트는 말 그대로 &quot;힌트&quot;이다. 다른 말로, 이는 정적 타입 언어에서처럼 타입 정의가 강제가 아니다. 이것이 의미하는 것은, 비록 꽤나 유연하지만 의도를 더욱 명확하게 표현해서 코드 퀄리티를 향상시켜준다는 것이다.&amp;nbsp; 이를 통해 아주 많은 툴에게서 이득을 더 얻을 수 있따.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;type-annotations-vs-type-hints&quot; style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;Type Annotations vs Type Hints&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 어노테이션은 그저 함수 인풋, 아웃풋 그리고 변수들을 표시해주는 문법에 불과하다.&lt;/p&gt;
&lt;pre id=&quot;code_1683353032391&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def sum_xy(x: 'an integer', y: 'another integer') -&amp;gt; int:
    return x + y


print(sum_xy.__annotations__)
# {'x': 'an integer', 'y': 'another integer', 'return': &amp;lt;class 'int'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 힌트는 더 유용하게 쓸 수 있게 annotation위에 속해 있다. 힌트와 어노테이션은 가끔씩 상호호환적으로 사용되지만, 둘은 다르다.&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;pythons-typing-module&quot; style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;Python의 타이핑 모듈&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가끔씩 이러한 코드를 보고서 의문을 품은 적이 있을 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1683353193339&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import List


def daily_average(temperatures: List[float]) -&amp;gt; float:
    return sum(temperatures) / len(temperatures)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴 타입을 정의하기 위해 원래 있던 float를 사용했지만, List는 typing모듈에서 임포트 되었다. 파이썬 3.9 이전에서는 파이썬 인터프리터는 인자에 대한 타입 힌팅을 위해 빌트인 타입들을 지원하지 않았다. 예를 들어 list를 타입힌트처럼 다음과 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1683353341549&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def daily_average(temperatures: list) -&amp;gt; float:
    return sum(temperatures) / len(temperatures)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 타이핑 모듈 없이 기대되는 리스트의 원소(list[float]) 타입들을 정의하는 것은 불가능했다. 이는 딕셔너리나 다른 연속체 그리고 복잡한 타입들에서도 똑같이 적용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1683353446678&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import Tuple, Dict


def generate_map(points: Tuple[float, float]) -&amp;gt; Dict[str, int]:
    return map(points)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 다르게 타이핑 모듈은 새로운 타입(&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://docs.python.org/3.7/library/typing.html#newtype&quot;&gt;new types&lt;/a&gt;)&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;,&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://docs.python.org/3.7/library/typing.html#type-aliases&quot;&gt;type aliases&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://docs.python.org/3.7/library/typing.html#typing.Any&quot;&gt;type Any&lt;/a&gt; 그리고 여러 다른 타입들을 정의할 수 있게해준다. 예를들어 다양한 타입들을 포함시키고 싶다면&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://docs.python.org/3.7/library/typing.html#typing.Union&quot;&gt;Union&lt;/a&gt;을 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1683353550536&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import Union


def sum_ab(a: Union[int, float], b: Union[int, float]) -&amp;gt; Union[int, float]:
    return a + b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 3.9부터는 다음과 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1683353574340&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def sort_names(names: list[str]) -&amp;gt; list[str]:
    return sorted(names)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 id=&quot;static-type-checking-with-mypy&quot; style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;mypy를 통한 정적 타입 체킹&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mypy는 컴파일 타임에서 타입 체킹을 할 수 있는 툴이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;pip install mypy&lt;br /&gt;python -m mypy my_module.py&lt;/blockquote&gt;
&lt;pre id=&quot;code_1683353658380&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def daily_average(temperatures):
    return sum(temperatures) / len(temperatures)


average_temperature = daily_average(
    {1599125906: 22.8, 1599125706: 19.6, 1599126006: 25.9}
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시를 mypy를 통해 돌리면 아무 에러도 안뜬다. 왜냐하면 함수가 어떠한 타입 힌트도 사용하지 않았기 때문이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Success: no issues found in 1 source file&lt;/blockquote&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f5f5f5; color: #212529; text-align: left;&quot;&gt;&lt;code&gt;def daily_average(temperatures: list[float]) -&amp;gt; float:
    return sum(temperatures) / len(temperatures)


average_temperature = daily_average(
    {1599125906: 22.8, 1599125706: 19.6, 1599126006: 25.9}
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 python -m mypy my_module.py를 통해 돌리면 다음과 같은 에러를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f5f5f5; color: #212529; text-align: left;&quot;&gt;&lt;code&gt;my_module.py:6: error: Argument 1 to &quot;daily_average&quot; has incompatible
type &quot;Dict[int, float]&quot;; expected &quot;List[float]&quot;  [arg-type]

Found 1 error in 1 file (checked 1 source file)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mypy는 함수가 잘못 호출되었다는 것을 인식하고 있다. mypy는 파일 이름, 라인 넘버 그리고 에러에 대한 설명을 리포트한다. mypy와 함께 타입 힌트를 사용하면 함수, 메서드 그리고 클래스의 잘못된 사용을 줄일 수 잇다. 이는 빠른 피드백 루프를 형성시킬 수 있다. 모든 테스트를 돌릴 필요가 없어지며, 혹은 전체 어플리케이션을 배포할 때도 말이다. 당신은 즉시 에러를 통보받을 수 있다. 또한 mypy를 CI(Continuous Integration)에 포함시켜서 당신의 코드가 머지되고 배포되기 전에 타이핑을 체크하는 것도 좋은 아이디어이다. 이에 대해서는 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://testdriven.io/blog/python-code-quality/&quot;&gt;Python Code Quality&lt;/a&gt;&lt;span&gt; 글을 읽어보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;비록 코드 퀄리티 측면에서는 큰 향상일 수 있지만, 정적 타입 체킹은 당신의 프로그램이 돌고 있을 때 런타임 상에서 강제하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;mypy는 파이썬 정식 라이브러리와 파이썬의 빌트인들, 그리고 서드 파티 패키지을 위한 외부 타입 어노테이션을 포함한 typeshed와 같이 딸려온다. 기본적으로 mypy는 파이썬 프로그램을 런타임 오버헤드 없이 체크한다. 비록 타입을 체크하지만, 덕 타이핑이 일어날 수 있따. 따라서 CPython의 확장팩으로 사용할 수 없다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Python/ETC</category>
      <category>python</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>코딩</category>
      <category>타입</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <category>프로그래밍언어</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/39</guid>
      <comments>https://kimkani.tistory.com/39#entry39comment</comments>
      <pubDate>Sat, 6 May 2023 15:25:06 +0900</pubDate>
    </item>
    <item>
      <title>[번역] 장고(Django)와 플라스크(Flask) : 2023년 어떤 것을 선택해야 하는가? 2편</title>
      <link>https://kimkani.tistory.com/38</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;R1280x0.jpeg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uXpWd/btsdW9w5p9n/3DqssddPaTfiqtA26TVsI1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uXpWd/btsdW9w5p9n/3DqssddPaTfiqtA26TVsI1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uXpWd/btsdW9w5p9n/3DqssddPaTfiqtA26TVsI1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuXpWd%2FbtsdW9w5p9n%2F3DqssddPaTfiqtA26TVsI1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;600&quot; data-filename=&quot;R1280x0.jpeg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Forms&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Form은 대부분의 웹 앱의 또다른 중요한 파트이며, 장고는 패키지화되서 온다. 인풋 핸들링과 함께, CSRF(Cross-Site Request Forgery), XSS(Cross-Site Scripting), 그리고 SQL Injection같은 다양한 보안 위협들을 다루는 서버 사이드 검증을 가지고 있다. 이것들은 ModelForms이라는 Data Model들을 통해 생성되며 어드민 패널과 잘 융합된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플라스크는 기본적으로는 제공하지 않지만,&lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-wtf.readthedocs.io/&quot;&gt;Flask-WTF&lt;/a&gt;같은 강력한 확장팩이 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://wtforms.readthedocs.io/&quot;&gt;WTForms&lt;/a&gt;와 같이 플라스크에 합쳐질 수 있다. &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://wtforms-alchemy.readthedocs.io/en/latest/&quot;&gt;WTForms-Alchemy&lt;/a&gt;는 자동적으로 SQLAlchemy 모델에 기반하여 자동적으로 form을 만드는데 사용되며, 장고의 ModelFrom처럼 From과 ORM사이의 갭을 메워줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;재사용 가능한 컴포넌트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 구조에 따라 - 당신의 앱이 더더욱 복잡해질 수록, 두 프레임워크 둘 다 이러한 프로젝트를 분해하여 기능적으로 비슷한 파일을 같이 묶을 수 있다. 이에따라, 예를들어 유저 관련된 기능들을 라우트, 뷰, 폼, 템플릿 그리고 스태틱 애셋등을 포함하여 모두 같이 묶을 수 있다. 장고는 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/ref/applications/&quot;&gt;app&lt;/a&gt;이라는 컨셉이라면, 플라스크는 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/blueprints/#blueprints&quot;&gt;blueprints&lt;/a&gt;를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장고 app은 플라스크의 blueprint보다 더 복잡하지만, 한번 셋업되면 다시 사용되기 쉽게 되어 있다. 게다가 urls.py, models.py 그리고 views.py 컨벤션 - 일관된 프로젝트 구조 - 에 따라 새로운 개발자를 장고 프로젝트에 상대적으로 쉽게 추가할 수 있다. 그 와중에 Blueprint는 간단하고 쉽게 세팅하고 돌릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;템플릿과 정적 파일&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿 엔진은 백엔드에서 HTML페이지에 정보를 동적으로 넣을 수 있게 해준다. 기본적으로 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/templating/&quot;&gt;Flask는 Jinja2를 사용하고&lt;/a&gt;&lt;span&gt; &lt;/span&gt;Django는 장고만의 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/ref/templates/&quot;&gt;templating engine&lt;/a&gt;을 가지고 있다. 문법적인 구조나 특징적으로 보면 상대적으로 비슷하다. &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/templates/#django.template.backends.jinja2.Jinja2&quot;&gt;Jinja2를 Django와 같이&lt;/a&gt; 사용 가능하다. 두 프레임워크 모두 정적 파일 핸들링 지원도 잘 된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/quickstart/#static-files&quot;&gt;Flask&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/4.1/howto/static-files/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Django&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Django는 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/ref/contrib/staticfiles/#collectstatic&quot;&gt;collecting&lt;/a&gt;이라는 편리한 관리 커맨드를 통해 모든 정적 파일을 관리하고, 프로덕션 배포를 위해 집적된 위치에 위치시킬 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;비동기 뷰&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장고는 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/async/&quot;&gt;비동기 핸들러(asynchronous handlers)&lt;/a&gt;를&lt;span&gt; 장고 3.1부터 지원한다. 뷰는 async 키워드를 통해 비동기적으로 만들어질 수 있다. 미들웨어에도 또한 비동기 지원이 가능하다. 만약 비동기 뷰에다가 동기적 콜을 하고 싶다면,&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/async/#asgiref.sync.sync_to_async&quot;&gt;sync_to_async&lt;/a&gt;&lt;span&gt; 함수/데코레이터(&lt;/span&gt;function/decorator)를 사용할 수 있다. 이런 기능은 장고에서 ORM이나 cache 레이어 같이 비동기를 지원하지 않는 장고의 다른 파츠와도 상호작용이 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;비동기 웹 서버는 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/daphne/&quot;&gt;Daphne&lt;/a&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/hypercorn/&quot;&gt;Hypercorn&lt;/a&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/uvicorn/&quot;&gt;Uvicorn&lt;/a&gt; 를 포함하여 - 이것뿐만은 아니지만 - 장고와 사용되면 아주 강력한 비동기 뷰에 대해 힘을 낼 수 있다. &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://palletsprojects.com/blog/flask-2-0-released/&quot;&gt;Flask 2.0&lt;/a&gt;은&lt;span&gt; 빌트인 된 비동기(&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/en/2.2.x/async-await/&quot;&gt;asynchronous&lt;/a&gt;)&lt;span&gt;&amp;nbsp;&lt;/span&gt;routes/views, error handlers, request functions, 그리고 teardown callbacks을 지원한다!&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;더 많은 장고와 플라스크의 비동기 뷰에 대해서는, 각각 &lt;a href=&quot;https://testdriven.io/blog/django-async-views/&quot;&gt;Async Views in Django&lt;/a&gt;와&amp;nbsp;&lt;a href=&quot;https://testdriven.io/blog/flask-async/&quot;&gt;Async in Flask 2.0&lt;/a&gt; 아티클을 참조해보자.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스팅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 프레임워크 모두 테스트를 위해 미리 지원되고 있다. 유닛 테스트를 위해서 둘 다 파이썬의 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.python.org/3/library/unittest.html&quot;&gt;unittest&lt;/a&gt;&lt;span&gt; 프레임워크를 지원한다&lt;/span&gt;. 각각의 프레임워크는 테스트 클라이언트를 지원해서 리퀘스트를 보내고, 관찰하고 그리고 리스폰스를 검증하는 것이 가능하다.&lt;span&gt; &lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/testing/&quot;&gt;Testing Flask Applications&lt;/a&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/testing/&quot;&gt;Testing in Django&lt;/a&gt;를 더 많은 정보가 필요하면 보기를 바란다. 확장팩의 경우, 유닛테스트 프레임워크가 어떻게 작동하는지 좋아한다면 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://pythonhosted.org/Flask-Testing&quot;&gt;Flask-Testing&lt;/a&gt;를 체크해보길 바란다. 다른 한편으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/pytest-dev/pytest-flask&quot;&gt;pytest-flask&lt;/a&gt;&lt;span&gt; 확장팩은&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.pytest.org/&quot;&gt;pytest&lt;/a&gt;를 플라스크를 위해 지원한다. 장고의 경우&lt;span&gt; &lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://pytest-django.readthedocs.io/&quot;&gt;pytest-django&lt;/a&gt;를 체크해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그 외 특징&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장고에는 있지만 플라스크에는 없는 언급하지 못한 특징들이 있다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 87px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 19px;&quot;&gt;장고&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 19px;&quot;&gt;플라스크 확장팩/리소스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/ref/contrib/syndication/&quot;&gt;Atom and RSS feeds&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://techmonger.github.io/25/flask-atom-feed/&quot;&gt;Atom RSS Feed Generator with Python and Flask&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/cache/&quot;&gt;Caching framework&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://flask-caching.readthedocs.io/&quot;&gt;Flask-Caching&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/ref/django-admin/&quot;&gt;Bootstrapping tool&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://flask-appbuilder.readthedocs.io/&quot;&gt;Flask-AppBuilder&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://flask.palletsprojects.com/en/2.2.x/cli/&quot;&gt;CLI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/ref/contrib/sitemaps/&quot;&gt;Sitemaps&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://github.com/inveniosoftware/flask-sitemap&quot;&gt;Flask-Sitemap&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;보안&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급한대로, 장고는 CSRF, XSS와 SQL injection과 같은 일반적인 공격에 대해 빌트인 방어체계를 구축하고 있다. 이러한 보안 수단들은 당신의 코드 내의 취약점에 대한 방어를 도와준다. 장고 개발 팀은 또한 알려진 보안 취약점을 사전 예방으로 - &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/dev/internals/security/&quot;&gt;proactively discloses&lt;/a&gt; - 발표하고 재빠른 픽스를 수행한다.&lt;span&gt; 그와 반대로&amp;nbsp;&lt;/span&gt;플라스크는 더 적은 코드 베이스를 가지고 있기에, 더 적은 면적에 공격에 노출되어 있다. 하지만, 당신이 손수 만든 앱 코드가 넓어질 수록 보안 취약점을 손수 관리하고 고칠 필요가 있어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 있어서, 당신 앱은 코드의 싱크홀의 크기만큼 안전할 것있다. 플라스크의 경우 서드 파티 확장팩에 더욱 의존적이기에, 앱은 최근에 안전한 확장팩일수록 더욱더 안전해질 것이다. 이러한 특징은 당신의 팀으로 하여금 서드 파티 확장팩 혹은 라이브러리를 평가하고 모니터링을 통해 보안을 유지하도록 압박을 준다. 이러한 서드파티를 최신으로 유지하는 것은 가장 중요하면서 가장 힘든 일인데, 이는 각 서드파티마다 각자의 개발팀, 문서 그리고 릴리즈 사이클이 잇기 때문이다. 몇몇 케이스 에서는 소수의 개발자만이 특정한 확장팩을 관리하고 있을지도 모른다. 어떠한 확장팩을 평가할 떄 깃허브 이슈들을 리뷰하여 치명적인 이슈에 얼마나 관리자들이 대응하고 있는지 살펴볼 필요가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 장고는 태초부터 플라스크보다 더 안전하다는 것을 의미하는 것은 아니다 - 그저 안전성과 유지보수성에서 좀 더 쉬울 뿐이다.&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;리소스&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/security/&quot;&gt;Flask Security Considerations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/security/&quot;&gt;Security in Django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://smirnov-am.github.io/securing-flask-web-applications/&quot;&gt;Securing Flask web applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://web.archive.org/web/20221002220405/https://hashedin.com/blog/protect-your-django-web-application-from-security-threats/&quot;&gt;Protect Your Django Web Application From Security Threats&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Python/ETC</category>
      <category>Django</category>
      <category>flask</category>
      <category>python</category>
      <category>백엔드</category>
      <category>장고</category>
      <category>컴퓨터</category>
      <category>코딩</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <category>플라스크</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/38</guid>
      <comments>https://kimkani.tistory.com/38#entry38comment</comments>
      <pubDate>Thu, 4 May 2023 18:18:08 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트 스터디] 자바스크립트의 데이터 타입, 원시형</title>
      <link>https://kimkani.tistory.com/37</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qmUoj/btsddjULPHp/AlUQvLigTTUgEHZVgwtiz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qmUoj/btsddjULPHp/AlUQvLigTTUgEHZVgwtiz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qmUoj/btsddjULPHp/AlUQvLigTTUgEHZVgwtiz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqmUoj%2FbtsddjULPHp%2FAlUQvLigTTUgEHZVgwtiz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;493&quot; height=&quot;493&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 코어 자바스크립트를 통해 아는 친구의 대학 후배들과 스터디를 진행하고 있다. 그에 대한 내용을 정리할 겸, 이제부터 하나하나 찬찬히 참조해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 자바스크립트의 데이터 타입&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FPcpQ/btsdeHNRQXV/8wSHSbFtQftAIGgNKdSOb1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FPcpQ/btsdeHNRQXV/8wSHSbFtQftAIGgNKdSOb1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FPcpQ/btsdeHNRQXV/8wSHSbFtQftAIGgNKdSOb1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFPcpQ%2FbtsdeHNRQXV%2F8wSHSbFtQftAIGgNKdSOb1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;987&quot; height=&quot;363&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 표를 보다싶이, 자바스크립트에는 다양한 데이터 타입이 존재하는데, 이를 표로 정리하여 하나하나 예시를 들어보려고 한다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 198px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;데이터 타입&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;해설&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;예시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;String&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;문자열 데이터를 표시&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;&quot;hello&quot;, &quot;hello world!&quot; 등등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;Number&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;정수나 소수를 표시&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;3, 3.234, 3e-2 등등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;Boolean&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;참 혹은 거짓을 표시&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;true, false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 36px;&quot;&gt;undefined&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 36px;&quot;&gt;변수는 선언되어 있으나 &lt;br /&gt;값이 들어있지 않은 상태&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 36px;&quot;&gt;let a;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;null&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;명시적으로 값이 없음을 표시&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;let a = null;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 36px;&quot;&gt;Symbol&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 36px;&quot;&gt;인스턴스가 유니크하거나 &lt;br /&gt;변경 불가능한 것을 표시&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 36px;&quot;&gt;let value = Symbol(&quot;Hello&quot;);&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;Object&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;키-값 쌍의 데이터 모음&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center; height: 18px;&quot;&gt;let student = {};&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 타입은 기본적으로 프로그램 상에서 어떤 데이터가 저장되거나 변조될 수 있는지를 나타내는 것이다. 이러한 데이터 타입들은 크게 2개의 카테고리로 나눌 수 있는데, Primitive(원시형), Reference(참조형) 데이터 타입으로 나눌 수 있다. Number, String, Boolean, Undefined, Null의 경우는 원시형 데이터 타입이며, Object, Array, Function, Symbol 등은 참조형 데이터 타입이라고 할 수 있다. 원시형 타입은 하나의 값만 가질 수 있는 반면에, 참조형 데이터는 여러 데이터 타입의 모음 혹은 엔티티를 가질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 자바스크립트 원시형 혹은 참조형&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘의 차이는 여러가지가 있겠지만, 가장 큰 차이는 바로 메모리에서 어떻게 관리되는지에 따라 구분된다. 자바스크립트의 경우 두 경우의 메모리를 알고 있따. 바로 Stack과 Heap이다. 스택은 말 그대로 순차적으로 위에서 아래로 쌓이는 박스를 생각하면 된다. 메모리 접근 자체가 쉰다. 여기서 주의점은 오직 미리 사이즈를 알고 있는 타입만이 스택에 갈 수 있다. 힙의 경우 미리 사이즈나 구조를 정할 수 없는 아이템들이 들어가게 된다. Object나, Array의 경우 런타임 환경에서 변할 수 있기 때문에 힙으로 들어가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-30 01.16.47.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNQv1h/btsdjnVDXgG/oJ5frqfzml2jPI1MOwUcU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNQv1h/btsdjnVDXgG/oJ5frqfzml2jPI1MOwUcU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNQv1h/btsdjnVDXgG/oJ5frqfzml2jPI1MOwUcU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNQv1h%2FbtsdjnVDXgG%2FoJ5frqfzml2jPI1MOwUcU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1268&quot; height=&quot;634&quot; data-filename=&quot;스크린샷 2023-04-30 01.16.47.png&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우를 예시로 들어보겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1002&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1003&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1004&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;이름: age&lt;br /&gt;값: @5003&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;이름: name&lt;br /&gt;값: @5004&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5002&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5003&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5004&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;28&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&quot;MAX&quot;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 우리가 var age = 28과 var name = &quot;MAX&quot;를 할당한다면 다음과 같은 경우가 일어난다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;변수 영역에서 빈 공간 (@1002, @1003)을 확보한다.&lt;/li&gt;
&lt;li&gt;확보한 공간의 식별자를 age 및 name으로 정한다.&lt;/li&gt;
&lt;li&gt;데이터 영역의 빈 공간 (@5003, @5004)에 각각 값을 저장한다.&lt;/li&gt;
&lt;li&gt;변수 영역에서 age, name이라는 식별자를 검색한다. 이때 이 주소는 @1002, @1003이 된다.&lt;/li&gt;
&lt;li&gt;앞서 저장한 문자열의 주소(@5003, @5004)를 @1002, @1003의 공간에 대입한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 여기서 age의 값을 30으로 바꾸면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1002&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1003&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1004&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;이름: age&lt;br /&gt;값: &lt;s&gt;@5003&lt;/s&gt; -&amp;gt; @5005&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;이름: name&lt;br /&gt;값: @5004&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터&lt;span&gt;&amp;nbsp;&lt;/span&gt;영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5002&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5003&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5004&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;28&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&quot;MAX&quot;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;변수 영역에서 age라는 식별자를 검색한다. 이때 이 주소는 @1002이 된다.&lt;/li&gt;
&lt;li&gt;데이터 영역에서 값이 30인 데이터 주소를 찾는다.&lt;/li&gt;
&lt;li&gt;이 때 값이 30인 데이터 영역이 없으므로 주소 @5005에 새로운 값을 할당한다.&lt;/li&gt;
&lt;li&gt;저장한 데이터의 주소(@5005)를 @1002의 공간에 대입한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이 때 의문이 들 수 있다. 왜 변수 영역에 직접 대입하지 않고 저장하는 것일까?이다. 이는 데이터 변환을 자유롭게 할 수 있게 함과, 메모리를 더욱 효율적으로 관리하기 위함이다.&lt;span&gt; 만약 더 나아가서 age2라는 식별자를 만들고 거기다 30을 넣으면 어떻게 될까?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;b&gt;변수영역&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1002&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1003&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1004&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;이름: age&lt;br /&gt;값:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;@5003&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;-&amp;gt; @5005&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;이름: name&lt;br /&gt;값: @5004&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;이름: age2&lt;br /&gt;값: @5005&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터&lt;span&gt;&amp;nbsp;&lt;/span&gt;영역&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;주소&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5002&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5003&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5004&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;5005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;데이터&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;28&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&quot;MAX&quot;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말한대로 효율적인 메모리 관리를 위해 똑같은 주소를 사용하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 참조형에 대해서 이야기해보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript</category>
      <category>javascript</category>
      <category>js</category>
      <category>공부</category>
      <category>스터디</category>
      <category>언어</category>
      <category>자바스크립트</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>코딩</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/37</guid>
      <comments>https://kimkani.tistory.com/37#entry37comment</comments>
      <pubDate>Sun, 30 Apr 2023 01:42:35 +0900</pubDate>
    </item>
    <item>
      <title>[공지] 파이썬 웹 프레임워크 정리 페이지 프로젝트</title>
      <link>https://kimkani.tistory.com/36</link>
      <description>&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;GitHub - KaniKim/PythonFrameworkGuide: 이 레포지토리는 파이썬 웹 프레임워크들을 정리한 것으로 각각의 &quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;이 레포지토리는 파이썬 웹 프레임워크들을 정리한 것으로 각각의 프레임워크에 맞는 가이드, 도큐 등등을 소개합니다. - GitHub - KaniKim/PythonFrameworkGuide: 이 레포지토리는 파이썬 웹 프레임워크들&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/KaniKim/PythonFrameworkGuide&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/p5mRt/hySpHM3AkZ/towzSkmlH6ikfJobvRk8f1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot; data-og-url=&quot;https://github.com/KaniKim/PythonFrameworkGuide&quot;&gt;&lt;a href=&quot;https://github.com/KaniKim/PythonFrameworkGuide&quot; target=&quot;_blank&quot; data-source-url=&quot;https://github.com/KaniKim/PythonFrameworkGuide&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/p5mRt/hySpHM3AkZ/towzSkmlH6ikfJobvRk8f1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;GitHub - KaniKim/PythonFrameworkGuide: 이 레포지토리는 파이썬 웹 프레임워크들을 정리한 것으로 각각의 &lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;이 레포지토리는 파이썬 웹 프레임워크들을 정리한 것으로 각각의 프레임워크에 맞는 가이드, 도큐 등등을 소개합니다. - GitHub - KaniKim/PythonFrameworkGuide: 이 레포지토리는 파이썬 웹 프레임워크들&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: center;&quot;&gt;&lt;br&gt;오랜만에 파이썬 웹 프레임워크 및 그 외 확장팩들 버전 및 추가사항을 업데이트했네요&lt;br&gt;&lt;br&gt;아마 이제부터 조금 더 본격적으로 하려고 합니다.&lt;br&gt;&lt;br&gt;혹여 관심있으신 분들은 참여 부탁드립니다&lt;br&gt;&lt;br&gt;&lt;/p&gt;</description>
      <category>Python/ETC</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/36</guid>
      <comments>https://kimkani.tistory.com/36#entry36comment</comments>
      <pubDate>Wed, 26 Apr 2023 18:58:56 +0900</pubDate>
    </item>
    <item>
      <title>[번역] 장고(Django)와 플라스크(Flask) : 2023년 어떤 것을 선택해야 하는가? 1편</title>
      <link>https://kimkani.tistory.com/35</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbQVsC/btsbbrfSdTQ/QkgcBNcBPKewnU4GEIjNc0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbQVsC/btsbbrfSdTQ/QkgcBNcBPKewnU4GEIjNc0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbQVsC/btsbbrfSdTQ/QkgcBNcBPKewnU4GEIjNc0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbQVsC%2FbtsbbrfSdTQ%2FQkgcBNcBPKewnU4GEIjNc0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;600&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1681887363830&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Django vs. Flask in 2023: Which Framework to Choose&quot; data-og-description=&quot;In this article, we'll look at the best use cases for Django and Flask along with what makes them unique, from an educational and development standpoint.&quot; data-og-host=&quot;testdriven.io&quot; data-og-source-url=&quot;https://testdriven.io/blog/django-vs-flask/&quot; data-og-url=&quot;https://testdriven.io/blog/django-vs-flask/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kySAX/hySk0UiW98/Xji023AfTfJANEPWYrP7U0/img.png?width=2030&amp;amp;height=1010&amp;amp;face=0_0_2030_1010,https://scrap.kakaocdn.net/dn/b8zCl8/hySkUmb3Zk/ghbUDXniJtZmC9KvTBL2gK/img.png?width=2030&amp;amp;height=1010&amp;amp;face=0_0_2030_1010&quot;&gt;&lt;a href=&quot;https://testdriven.io/blog/django-vs-flask/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://testdriven.io/blog/django-vs-flask/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kySAX/hySk0UiW98/Xji023AfTfJANEPWYrP7U0/img.png?width=2030&amp;amp;height=1010&amp;amp;face=0_0_2030_1010,https://scrap.kakaocdn.net/dn/b8zCl8/hySkUmb3Zk/ghbUDXniJtZmC9KvTBL2gK/img.png?width=2030&amp;amp;height=1010&amp;amp;face=0_0_2030_1010');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Django vs. Flask in 2023: Which Framework to Choose&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In this article, we'll look at the best use cases for Django and Flask along with what makes them unique, from an educational and development standpoint.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;testdriven.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://lp.jetbrains.com/python-developers-survey-2021/#FrameworksLibraries&quot;&gt;2021 JetBrains Python Developers Survey&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;에 따르면 Django와 Flask 모두 인기있는 파이썬 웹 프레임워크이다. 장고가 전통적으로 아주 인기있는 파이썬 웹 프레임워크라면, 플라스크는 탑을 노리기 위해 장고 몇여년 동안 따라갔다. 그다지 놀랍지 않은 트렌드로, 더 가벼운 프레임워크, 마이크로서비스 그리고 서버리스 플랫폼을 지향했던 지난 몇년간의 웹 개발 산업 트렌드이다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;혹은 산업계에서 더 적게 나타나고 젯브레인 유저들에게서 더 많이 나타나는 것일지도 모른다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장고와 플라스크 둘다 거대한 커뮤니티, 널리 알려지고 지원받고, 앱 개발에 있어 생산적인 접근을 제공해주며 둘 다 앱을 만듬에 있어, 코어 설계보다는 당신의 시간과 에너지를 아주 특별한 부분에 신경쓰도록 한다. 끝으로 두 프레임워크 모두 웹앱을 만드는 곳에 쓴다. 이 둘의 차이점은 이 목적을 어떻게 이루는지에 달려있다. 장고가 자동차라고 하면 플라스크는 자전거라고 볼 수 있듯이 말이다. 둘다 - 자동차와 자전거 둘다 - A지점에서 B지점까지 데려다 주지면, 둘의 접근은 다르다. 각각 최적의 선택지로서 쓰이는 상황이 있다. 이는 장고와 플라스크 둘 모두에 적용된다. 이 글에서는 장고와 플라스크를 특별하게 만들어주는 - 교육적인 측면과 개발적인 측면에서 - 최적의 상황을 살펴볼 것이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;철학 - Philosophy&lt;/h2&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;장고와 플라스크 모두 자유롭게 사용 가능하며, 오픈소스, 그리고 웹앱을 만들기위해 디자인된 파이썬 기반의 웹 프레임워크이다. 플라스크와 비교하면 장고는 포용력 높은 - 마치 내장형 배터리 처럼 툴, 패턴 특징 그리고 기능성을 가지고 있는 - 따로 특별한 설정이 필요없는 즉시 사용가능한 자동차이다. 유지보수 측면에서, 장고는 일반적으로 더 길고 까다로우면서 엄격한 릴리즈 사이클을 가지고 있다. 그래서, 장고가 적으면서 반짝이는 새로운 기능들을 가지고 올 때 아주 강력한 하위 호환성을 제공하려고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://testdriven.io/blog/what-is-werkzeug/&quot;&gt;Werkzeug&lt;/a&gt;을 기반으로 한 플라스크는 기반 작업을 다룬다. 즉시 실행 가능하기에, URL라우팅, 리퀘스트와 에러 처리, 틀 잡기, 쿠키와 함께 유닛 테스트, 디버거 그리고 개발 서버 지원을 해준다. 대부분의 웹앱이 좀더 많은 기능들을 요구하기에 - 조금 예를 들자면, ORM, 인증 및 인가 등이 있다 - 당신의 앱을 만들기 위해 결정을 할 필요가 있다. 당신이 써드파티 확장자나 스스로 코드를 커스터마이징하는 등 어떻게 활용하는지에 따라, 플라스크는 당신 곁을 떠날 수도 있다. 장고보다 더 유연하다는 것이다. 공격 취약에 대한 타격 면적이 장고에 비해 적고, 리뷰할 코드의 양도 적으며(&lt;a style=&quot;color: #1972d1;&quot; href=&quot;http://www.grokcode.com/864/snakefooding-python-code-for-complexity-visualization/&quot;&gt;less code to review&lt;/a&gt;),&lt;span&gt; 개조를 원한다면 보닛을 열고 소스코드를 직접 보자.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;개인적으로 &lt;a href=&quot;https://github.com/pallets/flask/tree/main/src/flask&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;플라스크의 소스 코드&lt;/a&gt;를 읽고 리뷰하기를 권장한다. 깔끔하며 깨끗하고 간결하다. 이는 아주 잘 설계된 파이썬 코드이다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;특징 - Features&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터베이스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장고는 바로 사용할 수 있는, 여러 관계형 데이터베이스(&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/ref/databases/&quot;&gt;databases&lt;/a&gt;)&amp;nbsp;- SQLite, PostgreSQL, MySQL, MariaDB, 그리고 Oracle - 을 지원하는 심플하고도 강력한 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/db/models/&quot;&gt;ORM&lt;/a&gt;을 포함하고 있다. 데이터베이스 마이그레이션(&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/migrations/&quot;&gt;migrations&lt;/a&gt;)을 관리 및 생성에 대한 지원을 제공하는 ORM이다. 데이터 모델에 기반한 form, view 그리고 template을 쉽게 만들 수 있고, 이는 곧 전형적인 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://en.wikipedia.org/wiki/Create,_read,_update_and_delete&quot;&gt;CRUD&lt;/a&gt; 웹앱에 딱 맞을 것이다. 물론 단점(&lt;a style=&quot;color: #1972d1;&quot; href=&quot;http://charlesleifer.com/blog/shortcomings-in-the-django-orm-and-a-look-at-peewee-a-lightweight-alternative/&quot;&gt;shortcomings&lt;/a&gt;)도 있지만, 주된 웹 앱에 있어 충분하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플라스크는 데이터가 어떻게 저장될지에 대해 어떤 가정도 하지 않지만, 여러 라이브러리 그리고 확장팩들이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;사용처&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;라이브러리&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;확장팩&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;ORM&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;SQLAlchemy&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;Flask-SQLAlcehmy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;SQLAlchemy를 위한 마이그레이션 툴&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;Alembic&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;Flask-Alembic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;ORM, 마이그레이션&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;Peewee&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;Flask-Peewee&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;ORM&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;PonyORM&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;Flask-Pony&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;ODM(Object Document Mapper)&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;PyMongo&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;Flask-PyMongo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;ODM&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;MongoEngine&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; text-align: center;&quot;&gt;Flask-MongoEngine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면, 관계형 데이터베이스를 사용한다면 장고가 더 시작하기 편할것이다. 내장된 ORM과 마이그레이션 관리 툴이 있기 때문이다. 하지만 NoSQL을 사용하거나 SQLAlchemy와 같은 툴을 사용한다면 장고는 당신과 매 단계마다 싸우려 들것이다. 여기에 더해 Django Admin, Model Forms, 그리고 DRF 모델 직렬화의 이점을 사용하기 어려울 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플라스크는 당신 곁에서 한발짝 떨어져 어플리케이션에 딱 맞는 ORM(혹은 ODM)을 고르거나 뽑을 자유를 준다. 비록 자유에는 대가가 따른다. 스스로 관리해야 되기에 이는 곧, 높은 러닝커브 그리고 당신을 기다리는 더 많은 에러들을 가져다 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;스스로 자신만의 무언가를 할수록 당신이 만들 실수는 많아진다, 특히 스케일이 커질수록&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인증 - Auth&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;대부분의 웹 앱들이 인증(누구인지?)과 인가(어떤 행동을 허락받았는지?)를 요구하기에, 장고는 &lt;a style=&quot;color: #1972d1; text-align: left;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/ref/contrib/auth/&quot;&gt;User model&lt;/a&gt;을 통해 세션과, 계정 관리를 기능적으로 바로 쓸 수 있게 지원한다. 플라스크는 쿠키 기반 세션 지원을 제공하지만 계정 관리, 인증, 인가를 위한 확장팩을 찾게 될 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;사용처&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;확장팩&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;계정 관리, 인증&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;Flask-Login&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;인증&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;Flask-Principal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;계정 관리, 인증, 인가&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;Flask-Security&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관리자 - Admin&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;장고는 &lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/ref/contrib/admin/&quot;&gt;admin panel&lt;/a&gt;이 딸려오는데, 당신이 만든 모델 기반의 데이터를 관리하기 위한 유저 인터페이스를 제공하는 웹 애플리케이션이다. 장고가 빛이나는 부분이기도 하다. 이를 통해 CRUD를 어떠한 추가 코드 작성 없이 당신의 모델에 대해 수행할 수 있게 한다. 그에 반해, 플라스크는 어떠한 것도 당신에게 던져주지 않지만, &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://github.com/flask-admin/flask-admin&quot;&gt;Flask-Admin&lt;/a&gt;&lt;span&gt; 확장팩이 같은 기능 혹은 더 많은 기능(&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-admin.readthedocs.io/en/latest/advanced/#migrating-from-django&quot;&gt;all of the same functionality and a lot more&lt;/a&gt;)을 부여한다:&lt;/p&gt;
&lt;blockquote style=&quot;color: #6f6f6f; text-align: left;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장고는 기능적으로 많은 일을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플라스크의 철학은 살짝 다르다 - 명백한 것이 함축한 것보다 낫다. 만약 무언가가 초기세팅이 되어야 하면, 이는 개발자의 손에 의해 이루어져야 한다는 것이다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flask-Admin은 이러한 관습을 따른다. 당신은 - 즉 개발자는 - Flask-Admin에게 무엇을 보여주고 어떻게 할지에 대한 권한이 달&lt;br /&gt;&lt;br /&gt;때로는 이러한 것은 보일러플레이트 코드 작성을 요구하기도 하지만, 미래에 비용이 감소되며, 특히 당신이 커스텀 로직을 짤 때 더더욱 도움이 될 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Flask-Admin는 &lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;SQLAlchemy, Peewee, MongoEngine와 같은 &lt;/span&gt;몇몇 데이터베이스 백엔드(&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-admin.readthedocs.io/en/latest/advanced/#using-different-database-backends&quot;&gt;number of database backends&lt;/a&gt;)를 지원한다. 당신만의 백엔드를 추가할 수 도 있다(&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-admin.readthedocs.io/en/latest/adding_a_new_model_backend/&quot;&gt;add your own backends&lt;/a&gt;). 또한 유명한 플라스크 인증 확장팩하고도 같이 혹은 따로 쓸수도 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-login.readthedocs.io/&quot;&gt;Flask-Login&lt;/a&gt; 그리고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://pythonhosted.org/Flask-Principal/&quot;&gt;Flask-Principal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask-security-too.readthedocs.io/&quot;&gt;Flask-Security&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;라우팅과 뷰 - Routing and Views&lt;/h2&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;두 프레임워크 모두 뷰에 URL을 매핑할 수 있도록 하며, CBV(Class-Based Views)나 FBV(Function-Based View)를 지원한다.&lt;/p&gt;
&lt;h3 style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;장고&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;만약 리퀘스트가 URL패턴과 매치되면, HTTP 요구사항을 품고 있는 리퀘스트 객체는 뷰로 패스되며, 뷰가 실행되게 된다. 리퀘스트 객체에 접근하고 싶을 때 마다, 이를 통해 명시적으로 넘겨줘야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;URL과 뷰는 각기 다른 파일에 있다 -&lt;span&gt; 각각 &lt;/span&gt;urls.py&lt;span&gt; 그리고 &lt;/span&gt;views.py이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/http/urls/&quot;&gt;Routing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/http/views/&quot;&gt;Views&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/class-based-views/&quot;&gt;Class-based views&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/ref/request-response/&quot;&gt;Request context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://docs.djangoproject.com/en/4.1/topics/http/urls/#how-django-processes-a-request&quot;&gt;How Django processes a request&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;플라스크&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #212529; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;플라스크에서 코어로 &lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://werkzeug.palletsprojects.com/&quot;&gt;Werkzeug&lt;/a&gt;를 사용하며, 이는 URL 라우팅과 리퀘스트/리스폰스 처리를 제공해준다. 플라스크에서 리퀘스트 객체는 전역적이기 때문에 - 당신이 import하는 한 - 더 쉽게 접근 가능하다. URL은 일반적으로 뷰를 통해 - &amp;nbsp;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/patterns/viewdecorators/&quot;&gt;decorator&lt;/a&gt;와 함께 - 정의되지만, 장고의 패턴처럼 중앙화된 지역에다 분리(&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/patterns/lazyloading/#converting-to-centralized-url-map&quot;&gt;separated out&lt;/a&gt;)될수도 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #212529; text-align: left;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/api/#url-route-registrations&quot;&gt;Routing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/views/#views&quot;&gt;Views&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/api/#class-based-views&quot;&gt;Class-based views&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://flask.palletsprojects.com/reqcontext/&quot;&gt;Request context&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote style=&quot;color: #6f6f6f; text-align: left;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 장고와 플라스크 둘다 어떻게 요청을 처리하는지 눈치 챘는가? 일반적으로 플라스크가 명시적으로 움직이려 하면, 장고는 그 반대라고 볼 수 있다. 장고는 노골적으로 전달을 주위로 넘기게끔 강요한다면, 플라스크의 리퀘스트는 마법처럼(&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://testdriven.io/blog/flask-contexts/&quot;&gt;magically&lt;/a&gt;)사용할 수 있다. 이는 플라스크가 어렵게 느껴지는 부분중 하나이다, 특히&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #1972d1;&quot; href=&quot;https://expressjs.com/&quot;&gt;Express.js&lt;/a&gt;처럼 새로이 접근하는 사람들에게 말이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Python/ETC</category>
      <category>Django</category>
      <category>flask</category>
      <category>백엔드</category>
      <category>번역</category>
      <category>영어</category>
      <category>웹</category>
      <category>장고</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <category>플라스크</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/35</guid>
      <comments>https://kimkani.tistory.com/35#entry35comment</comments>
      <pubDate>Thu, 20 Apr 2023 09:39:11 +0900</pubDate>
    </item>
    <item>
      <title>[파이썬 기본 다지기] Python에서 변수를 Print할 때 쓰는 방법들</title>
      <link>https://kimkani.tistory.com/34</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0*fwA0wmFohfxNxOOE.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn7j7M/btsalaLEheB/cCX0o8l69GzORHOldreKd0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn7j7M/btsalaLEheB/cCX0o8l69GzORHOldreKd0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn7j7M/btsalaLEheB/cCX0o8l69GzORHOldreKd0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn7j7M%2FbtsalaLEheB%2FcCX0o8l69GzORHOldreKd0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;807&quot; data-filename=&quot;0*fwA0wmFohfxNxOOE.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 다음과 같은 변수가 있다고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1681486939186&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = 1
b = 3
c = &quot;hello&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 변수들이 있을 때 각각을 표현하는 방식은 파이썬에 여러가지가 있겠지만, 먼저 가장 기본적인 것부터 소개를 시작하며 어떻게 Print하는지를 알아보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;C언어의 printf 스타일로 프린트하는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 경우와 같이 사용이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1681487382522&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(&quot;%d, %s, %f&quot; % (a, c, a/b))
print(&quot;%(decimal)d, %(string)s, %(float)f&quot; % {&quot;decimal&quot;: a, &quot;string&quot;: c, &quot;float&quot;: a/b})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 줄의 경우부터 살펴 보자면, %의 경우는 식별자의 시작을 알리는 마크라고 볼 수 있다. 이 때 두번째 줄 처럼 네이밍을 지정해줘서, 어느 변수가 어느 자리에 들어갈지 식별해줄 수 있다. 이 때 무조건 d, s, 그리고 s처럼 타입은 명시해야 한다. 여기서 궁금하신 분들이 계실것이다. 과연 어떤 문자를 써야 올바른 타입이 명시되는 것인지 말이다. 다음 표에 정리해 보겠다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 336px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;타입표시&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;뜻&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;부연 설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;d&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;정수&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;i&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;정수&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;o&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;8진수&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;만약 0o와 같이 표시를 하려면, 8진수 식별자인 &quot;0o&quot;가 앞에 들어가야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;u&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;구식 표현 - &quot;d&quot;와 동일하다&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;x&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;16진수 표현 - 소문자&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 36px;&quot; rowspan=&quot;2&quot;&gt;만약 0x나 0X를 표시하기 워한다면 16진수 식별자 앞에 위의 표시가 들어가야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;16진수 표현 - 대문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;e&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;지수 표현 - 소문자&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 36px;&quot; rowspan=&quot;4&quot;&gt;자세하게 표현되는 숫자의 기본 값은 정수 이후 6자리이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;E&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;지수 표현 - 대문자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;f&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;유리수 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;F&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;유리수 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;g&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;유리수 표현, 소문자로 표현, 만약 지수가 -4를 기준으로 정수형 혹은 지수형으로 표현된다&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;G&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;유리수 표현, 대문자로 표현, 만약 지수가 -4를 기준으로 정수형 혹은 지수형으로 표현된다&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;c&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;한자리 문자 - 정수나 한자리 문자열을 받는다&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;r&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;문자열 - repr()로 변환 된 모든 것&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;s&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;문자열 - str()로 변환 된 모든 것&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;a&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;문자열 - 아스키 코드로 변환된 모든 것&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇듯 다양한 문자열을 표현할 수 있는 방법이 있다. 여기서 궁금점이 있는 사람들이 있을 것이다. 과연 정수자리에 유리수를 넣으면 어떻게 표현이 될까? 정답은 자동으로 정수로 변환되서 표현된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-15 01.06.20.png&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rVyoN/btsahLfeDA3/v7cE1oGkENaA6rRKAtUAU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rVyoN/btsahLfeDA3/v7cE1oGkENaA6rRKAtUAU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rVyoN/btsahLfeDA3/v7cE1oGkENaA6rRKAtUAU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrVyoN%2FbtsahLfeDA3%2Fv7cE1oGkENaA6rRKAtUAU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;246&quot; data-filename=&quot;스크린샷 2023-04-15 01.06.20.png&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 보면 알다시피 a/b를 %d에 넣었을 경우, 즉 1/3을 넣었을 경우 0으로 즉 정수로 내림 되는 것을 알 수 있으며, %f 자리에 a를 넣었을 경우 소수점 6자리가 표시되는 것을 알 수 있다. 이러한 특징은 파이썬이 동적 타입 언어라서 가능한 것이다. 만약 여기서 소수점을 6자리가 아닌, 10자리 혹은 3자리만 표현하고 싶다면, %0.3f로 넣거나, %0.10f로 넣으면 가능하다. 그렇다면 0.10에서 왼쪽의 0은 무슨 부분일까? 바로 정수 부분의 표시이다. %10.10f을 표시한다면, 정수 자리에 어떻게든 10자리를 표시하겠다는 의미이며 소수점 자리도 10자리를 표시하겠다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt; format함수의 사용&lt;/h3&gt;
&lt;pre id=&quot;code_1681488888059&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(&quot;A = {}, C = {}&quot;.format(a, c))
print(&quot;A = {a_name}, a/b = {a_b}&quot;.format(a_name=a, a_b=a/b))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알다시피 파이썬의 경우 동적 타입 언어이기에 런타임시에 타입이 결정된다. 위의 경우를 살펴보면 먼저 자신이 넣고싶은 자리에 {}를 포함시키고 .format()자리에다가 순서대로 하나씩 넣는 것이다. 이를 통해 전보다 더 편하게 변수를 넣을 수 있게 되었다. 하지만, 이런 경우에도 변수가 많아지면 복잡해진다. 그래서 3.6이후부터 나온 새로운 형식인 f-string방식을 사용하는 것을 나는 강력하게 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 여기서도 유리수의 소수점을 보다 정확하게 표현하고 싶다면 다음과 같이 하면 된다. print(&quot;{0:.10f}&quot;.format(a/b)), 하나하나 씩 해석해보자면 0:.10f에서 0은 첫번째 변수를 뜻하며 .10f은 소수점 10자리를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;f-string의 사용&lt;/h3&gt;
&lt;pre id=&quot;code_1681489148134&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(f&quot;A = {a}, B = {b}, C = {c}, A/B = {a/b}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우를 사용하면 바로 변수의 타입을 굳이 정할 필요 없이 바로 변수를 넣어서 프린트 할 수 있다. 자주 애용하도록 하자.&lt;/p&gt;</description>
      <category>Python/ETC</category>
      <category>개발</category>
      <category>개발자</category>
      <category>공부</category>
      <category>변수</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/34</guid>
      <comments>https://kimkani.tistory.com/34#entry34comment</comments>
      <pubDate>Sat, 15 Apr 2023 01:23:48 +0900</pubDate>
    </item>
    <item>
      <title>[파이썬 기본 다지기] 파이썬에서 print(&amp;quot;Hello, world&amp;quot;)는 어떻게 나오는 걸까? 파이썬의 실행 방식 간략하게 알아보기</title>
      <link>https://kimkani.tistory.com/33</link>
      <description>&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=3KvJMzxP0TE&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bEjRPh/hySa34Fpfl/FonS6R5gWA4jxREBHhFXJ1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/3KvJMzxP0TE&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 파이썬이란?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-1. 파이썬은 크게 두가지 의미에서 사용된다&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래밍 언어 Python의 언어 사양 - 문법이나 작동방식등을 모아둔 것&lt;/li&gt;
&lt;li&gt;언어의 사양에 따른 처리방식의 대표격 CPython
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;귀도 반 로섬이 개발한 본체 CPython의 처리계통을 말한다.&lt;/li&gt;
&lt;li&gt;이번 글에서는 CPython을 기준으로 운영체제는 리눅스(도커)를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1-2. 그 외의 파이썬 인터프리터 예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PyPy : 파이썬으로 실행되는 파이썬&lt;/li&gt;
&lt;li&gt;Jython : 자바로 실행되는 파이썬&lt;/li&gt;
&lt;li&gt;Pyodide : WebAssembly를 대상으로 하는 파이썬&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OQfWG/btr8hvekG01/ODRQO82ZRauH2YSEj5n8kk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OQfWG/btr8hvekG01/ODRQO82ZRauH2YSEj5n8kk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OQfWG/btr8hvekG01/ODRQO82ZRauH2YSEj5n8kk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOQfWG%2Fbtr8hvekG01%2FODRQO82ZRauH2YSEj5n8kk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;848&quot; height=&quot;477&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 프로그래밍 언어의 실행방식&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1. 인터프리터 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소스 코드를 한줄 씩 읽어가며 순차적으로 해석하며 움직이는 실행방식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 순차 해석을 실행하는 프로그램을 인터프리터라고 부른다&lt;/li&gt;
&lt;li&gt;스크립트 형식의 언어에 많이 사용되며, Python, Ruby, JavaScript 등이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-2. 컴파일 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소스코드를 사전에 기계어로 변환 - 컴파일 - 하고나서 실행하는 방식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C, C++, Go, Rust등이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위의 방식들은 어디까지나 간단하게 범주를 나눈 것으로, 실제로는 입력 형식이나 결과물에 따라 다양한 중간 표현식도 자주 사용.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 파이썬이 실행될 때에 대한 간략한 개요&lt;/h2&gt;
&lt;pre id=&quot;code_1680746560900&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def hello():
	#This is a comment line
    print(&quot;Hello, world&quot;)
    
if __name__ == &quot;__main__&quot;:
	hello()​&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-1. 프로그래밍 언어와 소스 코드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램의 소스코드는 다른 문자열&lt;/li&gt;
&lt;li&gt;문자열에 의미를 가지게 하는 것을 언어라고 부른다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 &quot;1+2&quot;는 수식인지, 한국어인지, 파이썬의 코드인지 아니면 자바스크립트의 코드인지 모른다.&lt;/li&gt;
&lt;li&gt;언어에 따라 같은 문자열이라도 해석이 달라진다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;언어 중에서도 특히 컴퓨터에게 해석을 부탁하는 것이 프로그래밍 언어&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3-2. 프로그래밍 언어와 추상구문트리&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ftgsg/btr8iZe9lGm/hAEP0Z2hAjkOuzFct7Ksk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ftgsg/btr8iZe9lGm/hAEP0Z2hAjkOuzFct7Ksk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ftgsg/btr8iZe9lGm/hAEP0Z2hAjkOuzFct7Ksk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFtgsg%2Fbtr8iZe9lGm%2FhAEP0Z2hAjkOuzFct7Ksk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;526&quot; height=&quot;337&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래밍 언어는 소스코드의 문자열을 해체해서 컴퓨터가 해석하기 쉬운 형식으로 교환하는 것이 필수적이라고 볼 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 교환을 하는 과정을 Parse(파스)라고 부르며, 이것을 실행하는 것을 Parser(파서)라고 부른다&lt;/li&gt;
&lt;li&gt;컴퓨터가 해석하기 쉬운 형태 = Abstract Syntax Tree, AST&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;프로그래밍언어는 인간이 AST를 구축하기 쉽게 하기위해 존재한다.&lt;/li&gt;
&lt;li&gt;이때 파이썬의 파서는 파이썬 3.9 이전과 이후로 나뉜다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파이썬 3.9부터는 PEG - Parsing Expression Grammer, PEP617 - 파서를 사용한다.&lt;/li&gt;
&lt;li&gt;그 이전에는 LL(1)파서가 이용되었고, 이 파서는 Python 3.10에서 삭제된다.&lt;/li&gt;
&lt;li&gt;이 글에서는 3.11.0을 사용한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;주된 흐름은 &lt;u&gt;&lt;b&gt;&quot;구문분석 -&amp;gt; 토큰화 -&amp;gt; 추상화 -&amp;gt; AST -&amp;gt; 컴파일 -&amp;gt; 바이트 코드 -&amp;gt; 실행&quot;&lt;/b&gt;&lt;/u&gt;으로 이루어진다.&lt;/li&gt;
&lt;li&gt;이때 토큰화를 할 때, Tokenize Module이 그 역할을 해준다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;표준 라이브러리인 tokenize모듈은 파이썬 소스코드를 토큰으로 분해해서 출력한다.&lt;/li&gt;
&lt;li&gt;토큰은 의미를 가지는 최소 단위&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EAdcp/btr8neCng9v/JtHPrfK9HIMUGhe08qdZU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EAdcp/btr8neCng9v/JtHPrfK9HIMUGhe08qdZU0/img.png&quot; width=&quot;286px;&quot; height=&quot;181px;&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;348&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;51.85&quot; style=&quot;width: 51.246689%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EAdcp/btr8neCng9v/JtHPrfK9HIMUGhe08qdZU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEAdcp%2Fbtr8neCng9v%2FJtHPrfK9HIMUGhe08qdZU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qrTzX/btr8f060nVx/HI6KYKJLIokkdKozbvRKKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qrTzX/btr8f060nVx/HI6KYKJLIokkdKozbvRKKk/img.png&quot; width=&quot;477px;&quot; height=&quot;326px;&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;930&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;48.15&quot; style=&quot;width: 47.590521%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qrTzX/btr8f060nVx/HI6KYKJLIokkdKozbvRKKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqrTzX%2Fbtr8f060nVx%2FHI6KYKJLIokkdKozbvRKKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;930&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그 후 추상화를 할 때, AST Module이 그 역할을 해준다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;표준 라이브러리인 AST모듈은 파이썬 소스코드를 추상화해서 출력한다.&lt;/li&gt;
&lt;li&gt;AST는 프로그램으로서 의미를 가지는 정보를 구조체로 표현한 것으로, 이 때 주석은 포함되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EAdcp/btr8neCng9v/JtHPrfK9HIMUGhe08qdZU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EAdcp/btr8neCng9v/JtHPrfK9HIMUGhe08qdZU0/img.png&quot; style=&quot;width: 51.246689%; margin-right: 10px;&quot; width=&quot;286px;&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;348&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;51.85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EAdcp/btr8neCng9v/JtHPrfK9HIMUGhe08qdZU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEAdcp%2Fbtr8neCng9v%2FJtHPrfK9HIMUGhe08qdZU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgQodb/btr8ia85raL/XbOMKzkkfjbKlJX9mibYk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgQodb/btr8ia85raL/XbOMKzkkfjbKlJX9mibYk1/img.png&quot; width=&quot;477px;&quot; height=&quot;326px;&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;930&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;48.15&quot; style=&quot;width: 47.590521%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgQodb/btr8ia85raL/XbOMKzkkfjbKlJX9mibYk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgQodb%2Fbtr8ia85raL%2FXbOMKzkkfjbKlJX9mibYk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;930&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그 후 바이크 코드로 변환할 때, dis Module이 그 역할을 해준다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파이썬 바이트 코드를 역 어셈블한(합친) 결과를 출력&lt;/li&gt;
&lt;li&gt;파이썬 바이트 코드는 Python VM(가상머신)이 해석한 기계어&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l1ndt/btr8iZGhYKD/hbSgxTlxNWIvvfIkZAVU81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l1ndt/btr8iZGhYKD/hbSgxTlxNWIvvfIkZAVU81/img.png&quot; width=&quot;286px;&quot; height=&quot;181px;&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;348&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;51.85&quot; style=&quot;width: 51.246689%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l1ndt/btr8iZGhYKD/hbSgxTlxNWIvvfIkZAVU81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl1ndt%2Fbtr8iZGhYKD%2FhbSgxTlxNWIvvfIkZAVU81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F0nlm/btr8hnHVa5m/U8MywshoitTL3v0qlY8qok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F0nlm/btr8hnHVa5m/U8MywshoitTL3v0qlY8qok/img.png&quot; width=&quot;477px;&quot; height=&quot;326px;&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;930&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;48.15&quot; style=&quot;width: 47.590521%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F0nlm/btr8hnHVa5m/U8MywshoitTL3v0qlY8qok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF0nlm%2Fbtr8hnHVa5m%2FU8MywshoitTL3v0qlY8qok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;930&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면 다음과 같다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;&quot;구문분석 -&amp;gt; 토큰화(tokenize Module)-&amp;gt; &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;&amp;nbsp;추상화 -&amp;gt; AST(AST Module) -&amp;gt; &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;&amp;nbsp;컴파일 -&amp;gt; 바이트 코드(dis Module) -&amp;gt; 실행&quot;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 파이썬 가상머신에 의한 바이트코드 실행&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yIBB2/btr8aw6eME0/irutzneJA6Z0Z0kwKyN7iK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yIBB2/btr8aw6eME0/irutzneJA6Z0Z0kwKyN7iK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yIBB2/btr8aw6eME0/irutzneJA6Z0Z0kwKyN7iK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyIBB2%2Fbtr8aw6eME0%2FirutzneJA6Z0Z0kwKyN7iK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2048&quot; height=&quot;1382&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파이썬 가상머신은 파이썬의 바이트 코드를 순차 실행하는 가상 머신이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 컴퓨터에서는 CPU가 메모리상의 기계어를 순차실행하고 있다.&lt;/li&gt;
&lt;li&gt;실제 코드는 Python/ceval.c에 기술되어 있는 거대한 루프이다.&lt;/li&gt;
&lt;li&gt;기억 영역에 스택 구조를 사용하는 스택 머신이라고 불리는 계산 모델이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Python/ETC</category>
      <category>python</category>
      <category>개발자</category>
      <category>코드</category>
      <category>코딩</category>
      <category>파이썬</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/33</guid>
      <comments>https://kimkani.tistory.com/33#entry33comment</comments>
      <pubDate>Thu, 6 Apr 2023 11:23:17 +0900</pubDate>
    </item>
    <item>
      <title>[번역] 코드에 주석을 다는 완벽한 연습</title>
      <link>https://kimkani.tistory.com/31</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;comment.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0c11N/btrXFtSJcgv/KOgWAE1mEhtcUk7EMuKwW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0c11N/btrXFtSJcgv/KOgWAE1mEhtcUk7EMuKwW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0c11N/btrXFtSJcgv/KOgWAE1mEhtcUk7EMuKwW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0c11N%2FbtrXFtSJcgv%2FKOgWAE1mEhtcUk7EMuKwW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; data-filename=&quot;comment.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;책이나 정적 분석 도구처럼 많은 리소스가 프로그래머들을 하여금 좋은 코드를 적게 도와주지만, 더 좋은 주석을 다는 방법을 가르쳐 주는 것은 적다. 프로그램에서 주석의 양을 확인하는 것은 쉽지만, 퀄리티를 확인하는 것은 어려우며, 주석의 양과 퀄리티가 반드시 연관성이 있는 것은 아니다. 안좋은 주석은 아예 없는 것보다 못하다. 이 글에서는 좋은 코드를 작성하도록 도와줄 가이드를 제시한다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1675161706340&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Best practices for writing code comments&quot; data-og-description=&quot;While there are many resources to help programmers write better code&amp;mdash;such as books and static analyzers&amp;mdash;there are few for writing better comments. While it's easy to measure the quantity of comments in a program, it's hard to measure the quality, and t&quot; data-og-host=&quot;stackoverflow.blog&quot; data-og-source-url=&quot;https://stackoverflow.blog/2021/12/23/best-practices-for-writing-code-comments/&quot; data-og-url=&quot;https://stackoverflow.blog/2021/12/23/best-practices-for-writing-code-comments/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bcl3xg/hyRsl5MpAE/EKTH4Cw5LsmeH5f6ncMkX1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ZTToO/hyRskeHwaS/IfB2PACIPAoXO6E4HXEM5k/img.png?width=800&amp;amp;height=1133&amp;amp;face=0_0_800_1133,https://scrap.kakaocdn.net/dn/5YS2S/hyRsmcvxcY/njvaYM8Hzkz8WCvQQW1kL0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://stackoverflow.blog/2021/12/23/best-practices-for-writing-code-comments/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.blog/2021/12/23/best-practices-for-writing-code-comments/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bcl3xg/hyRsl5MpAE/EKTH4Cw5LsmeH5f6ncMkX1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ZTToO/hyRskeHwaS/IfB2PACIPAoXO6E4HXEM5k/img.png?width=800&amp;amp;height=1133&amp;amp;face=0_0_800_1133,https://scrap.kakaocdn.net/dn/5YS2S/hyRsmcvxcY/njvaYM8Hzkz8WCvQQW1kL0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Best practices for writing code comments&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;While there are many resources to help programmers write better code&amp;mdash;such as books and static analyzers&amp;mdash;there are few for writing better comments. While it's easy to measure the quantity of comments in a program, it's hard to measure the quality, and t&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.blog&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;유명한 MIT 교수 Hal Abelson은 &quot;프로그램은 사람들이 읽을 수 있도록 작성되어야 하고 기계가 실행될 수 있도록 부수적으로만 작성되어야 한다&quot;고 말했다. 그가 의도적으로 코드 실행의 중요성을 과소평가했을 수도 있지만, 그는 프로그램에 매우 다른 두 명의 독자가 있다는 것에 주목한다. 컴파일러와 인터프리터는 주석을 무시하고 구문적으로 정확한 모든 프로그램을 똑같이 이해하기 쉽다는 것을 발견한다. 인간 독자는 매우 다르다. 우리는 몇몇 프로그램이 다른 프로그램보다 이해하기 어렵다는 것을 찾고서, 그것들을 이해하는 데 도움이 되는 주석을 기대한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd;&quot;&gt;안좋은 주석은 아예 없는 것보다 못하다는 것처럼, Peter Vogel이 쓴 것이 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;주석을 쓰고 유지하는 것은 비싸다.&lt;/li&gt;
&lt;li&gt;당신의 컴파일러는 너의 주석을 체크하지 않기에, 그 주석이 올바른지 확인할 방법이 없다.&lt;/li&gt;
&lt;li&gt;반면에, 당신은 &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;컴퓨터가 당신의 코드가 말하는 것과 정확히 같은 일을 하고 있다는 것을 보장한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;이러한 점이 전부 맞기는 하지만, 주석을 아예 안쓰거나 극단적으로 가는 경우에는 실수를 할 수 있다. 여기서 당신이 조금은 행복하게 주석을 달 수 있는 룰을 소개하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;주석은 코드를 중복해서 작성하면 안된다.&lt;/li&gt;
&lt;li&gt;좋은 주석은 명확하지 않은 코드를 변명하지 않는다.&lt;/li&gt;
&lt;li&gt;만약 당신이 명확한 주석을 쓸 수 없다면, 코드에 문제가 있다는 것이다.&lt;/li&gt;
&lt;li&gt;주석은 혼란을 해결해야지 야기하면 안된다.&lt;/li&gt;
&lt;li&gt;비 관용적인 코드를 주석에서 설명해야 한다.&lt;/li&gt;
&lt;li&gt;복사한 코드의 원본 링크를 첨부해야 한다.&lt;/li&gt;
&lt;li&gt;가장 도움이 될만한 외부 참조 링크를 포함시켜야 한다.&lt;/li&gt;
&lt;li&gt;버그를 고쳤을 경우 주석을 달아라.&lt;/li&gt;
&lt;li&gt;아직 완성되지 못한 구현에 대해 주석을 달아라.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;남은 아티클은 각각의 룰에 대해 이야기 하고 예시를 보여주고, 실제 적용되면 어떻게 되는지를 보여주려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rule 1. 주석은 코드를 중복해서 작성하면 안된다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 주니어 프로그래머들이 입문을 할 때 너무 많은 주석을 달도록 훈련되어 왔다. 내가 본 상위권 컴퓨터 공학 수업들은 주석을 달 때 중괄호를 닫는 곳마다 주석을 달았다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1675161793847&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (x &amp;gt; 3) {
   &amp;hellip;
} // if&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 또한 강사가 학생에게 각 코드 라인마다 주석을 달도록 요구했다고 들은 적이 있다. 물론 이것이 아주 초보에 합리적인 정책이 될 수 있지만, 이러한 주석은 자전거의 보조바퀴 같은 것이며 아이가 크면 없애야 하는 것과 같다. 어떤 정보도 없는 주석은 다음과 같은 이유로 부정적인 결과를 초래한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추가적인 시각적 혼란&lt;/li&gt;
&lt;li&gt;읽고 쓰는데 드는 시간&lt;/li&gt;
&lt;li&gt;구식이 될 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아주 멋진 나쁜 예는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1675162119499&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;i = i + 1;         // Add one to i&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무 정보도 주지 않으면서 유지하는데만 비용이 든다. 레딧에서는 이러한 주석을 매 라인마다 다는 것에 대해 조롱하기도 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1675162177307&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// create a for loop // &amp;lt;-- comment
for // start for loop
(   // round bracket
    // newline
int // type for declaration
i    // name for declaration
=   // assignment operator for declaration
0   // start value for i&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rule 2: 좋은 주석은 명확하지 않은 코드를 변명하지 않는다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 잘못된 주석의 사용법은 코드에 있어야할 정보를 주석에다가 반영하는 것이다. 아주 간단한 예로 누군가가 한글자로 변수명을 짓고 여기다가 그 변수의 목적을 주석으로 추가하는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1675162288314&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static Node getBestChildNode(Node node) {
    Node n; // best child node candidate
    for (Node node: node.getChildren()) {
        // update n if the current state is better
        if (n == null || utility(node) &amp;gt; utility(n)) {
            n = node;
        }
    }
    return n;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석의 필요성은 더 좋은 변수 작명법으로 제거될 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675162321078&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private static Node getBestChildNode(Node node) {
    Node bestNode;
    for (Node currentNode: node.getChildren()) {
        if (bestNode == null || utility(currentNode) &amp;gt; utility(bestNode)) {
            bestNode = currentNode;
        }
    }
    return bestNode;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kernighan과 Plauger가 쓴 &quot;The Elements of Programming Style&quot;에서처럼 말이다. &quot;나쁜 코드를 주석으로 설명하지 마라, 다시 써라&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rule 3: 만약 당신이 명확한 주석을 쓸 수 없다면, 코드에 문제가 있다는 것이다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유닉스 소스코드에 있는 아주 악명높은 주석은 &quot;&lt;a href=&quot;https://web.archive.org/web/20070220094221/http://cm.bell-labs.com/cm/cs/who/dmr/odd.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;너가 이걸 이해했다고 기대하지 않는다&lt;/a&gt;&quot;, 아주 복잡한 context-switching코드가 등장하기 전에 나타나 있는 주석이다. Dennis Ritchie는 후에 이를 두고 &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&quot;이것은 뻔뻔한 도전이라기보다는 '시험에 나오지 않을 것'이라는 정신으로 의도된 것&quot;이라고 설명했다. 불행히도, &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;그와 공동 저자인 Ken Thompson은 자신들도 그것을 이해하지 못했고 나중에 다시 써야 했던 것으로 밝혀졌다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;이는 Kernighan의 법칙을 고려하게 한다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;디버깅은 코드를 처음 작성할 때 보다 두배는 더 힘들다. 그래서, 만약 당신이 코드를 기깔나게 작성하면, 정의상으로 당신은 기깔나게 디버그 할 정도가 못됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;독자들에게, 자신의 코드에서 벗어나라고 경고하는 것은 자동차의 비상등을 켜는 것과 같다. 즉, 당신이 알고 있는 것을 하고 있다는 것을 인정하는 것은 불법적인 일이다. 대신, 더 나은 것은, 코드를 설명할 수 있을 정도로 충분히 잘 이해하고 있는 것으로 다시 쓰는 것이며, 그것이 간단하다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rule 4: 주석은 혼란을 해결해야지 야기하면 안된다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나쁜 주석에 대해 이야기할 때 Steven Levy의 Hackers: Heores of the Computer Revolution의 스토리를 이야기하는 것 만큼 완벽한 것이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;[Peter Samson]은 주어진 시간에 자신이 무엇을 하고 있었는지 설명하는 그의 소스 코드에 주석을 추가하는 것을 거부하는 것에 대해 특히 의문을 품었다. Samson이 작성한 잘 배포된 프로그램은 수백 개의 어셈블리어 명령어를 위해 진행되었으며, 1750이라는 숫자가 포함된 명령어 옆에 단 한 개의 코멘트만 있었다. 그 논평은 RIPJSB였고, 사람들은 1750년이 바흐가 죽은 해라는 것과 Samson이 Rest in Peace Johann Sebastian Bach의 약어를 썼다는 것을 알게 될 때까지 그 의미에 대해 아주 깊게 생각했다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;나는 다른 사람만큼 좋은 해킹에 대해 감사한 마음을 가지고 있지만, 이러한 것이 모범적인 것은 아니다. 주석이 의문을 해소하는 대신 혼란을 야기할 경우 주석을 삭제하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;Rule 5: 비 관용적인 코드를 주석에서 설명해야 한다&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;App Inventor의 다음 코드와 같이 다른 사용자가 불필요하거나 중복된다고 생각할 수 있는 코드에 주석을 다는 것이 좋다(모든 긍정적인 예의 출처):&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675163180798&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final Object value = (new JSONTokener(jsonString)).nextValue();
// Note that JSONTokener.nextValue() may return
// a value equals() to null.
if (value == null || value.equals(null)) {
    return null;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;주석이 없다면, 누군가는 코드를 &quot;단순화&quot;하거나 그것을 신비롭지만 필수적인 주문으로 볼 수 있다. 코드가 필요한 이유를 적어 미래의 독자들의 시간과 걱정을 덜어준다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;코드에 설명이 필요한지 판단을 내려야 하기도 한다. 코틀린을 배울 때, 나는 안드로이드 튜토리얼에서 코드를 접하기도 했다:&lt;/span&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675163248531&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (b == true)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 곧바로 의문에 들었는데, 위의 코드가 아래와 같이 대체될 수 있지 않을까 여서 였다.&lt;/p&gt;
&lt;pre id=&quot;code_1675163279723&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (b)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마치 자바에서 하던것 처럼 말이다. 그 후 조금의 조사를 통해, &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;나는 null이 될 수 있는 Boolean 변수가 추악한 null 검사를 피하기 위해 true와 명시적으로 비교된다는 것을 배웠다:&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675163350493&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (b != null &amp;amp;&amp;amp; b)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&quot;targetEditArea&quot;&gt;
&lt;div id=&quot;txtTarget&quot;&gt;&lt;span&gt;초보자를 위한 튜토리얼을 작성하지 않는 한 일반적인 관용구에 대한 주석을 포함하지 않는 것이 좋다.&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rule 6: 복사한 코드의 원본 링크를 첨부해야 한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;대부분의 프로그래머 처럼 온라인에서 찾은 코드를 사용하는 경우도 있습니다. 소스에 대한 참조를 포함하면 향후 독자들이 다음과 같은 전체 맥락 얻을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤, 문제가 해결되고 있었는가&lt;/li&gt;
&lt;li&gt;누가, 코드를 제공했는지&lt;/li&gt;
&lt;li&gt;왜, 이 솔루션이 권장되지&lt;/li&gt;
&lt;li&gt;어떤, 것에 대해 생각했는지&lt;/li&gt;
&lt;li&gt;과연, 그것이 효과가 있는지 없는지&lt;/li&gt;
&lt;li&gt;어떻게, 그것이 개선될 수 있는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 주석을 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1675163604576&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/** Converts a Drawable to Bitmap. via https://stackoverflow.com/a/46018816/2219998. */&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링크를 따라가면 다음과 같이 나타난다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 작성자는 Tom&amp;aacute;&amp;scaron; Proch&amp;aacute;zka이며, Stack Overflow에서 상위 3%에 든다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;댓글 작성자는 이미 레포에 통합된 최적화를 제공했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;다른 댓글 작성자는 edge 케이스를 피하는 방법을 제안했따.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;이 의견과 대조해 보자 (죄인을 보호하기 위해 약간 변경됨):&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675163749942&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Magical formula taken from a stackoverflow post, reputedly related to
// human vision perception.
return (int) (0.3 * red + 0.59 * green + 0.11 * blue);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span&gt;이 &lt;/span&gt;코드를 이해하려는 사람은 공식을 검색해야 한다. URL에 붙여넣는 것이 나중에 참조를 찾는 것보다 훨씬 빠르다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;일부 프로그래머들은 그들이 직접 코드를 작성하지 않았다는 것을 표시하는 것을 꺼릴 수 있지만, 코드를 재사용하는 것은 시간을 절약하고 더 많은 안구의 이점을 제공하는 현명한 조치가 될 수 있다. 물론 이해하지 못하는 코드는 절대 붙여넣어서는 안 된다. &lt;/span&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;사람들은 스택 오버플로 질문과 답변에서 많은 코드를 복사하고, 그 코드는 귀속을 요구하는 크리에이티브 커먼즈 라이선스에 속한다. 참조 주석은 그러한 요구사항을 충족한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;마찬가지로, 도움이 된 튜토리얼을 주석으로 달면 좋으며 이를 통해 작성자 덕분에 다시 찾을 수 있음을 감사하게 된다:&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675163810603&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Many thanks to Chris Veness at http://www.movable-type.co.uk/scripts/latlong.html
// for a great reference and examples.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rule 7: 가장 도움이 될만한 외부 참조 링크를 포함시켜야 한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 모든 참조가 스택 오버플로우인 것은 아니다. 다음을 고려해보자:&lt;/p&gt;
&lt;pre id=&quot;code_1675163898229&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// http://tools.ietf.org/html/rfc4180 suggests that CSV lines
// should be terminated by CRLF, hence the \r\n.
csvStringBuilder.append(&quot;\r\n&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;표준 및 기타 문서에 대한 링크는 코드가 해결하고 있는 문제를 독자가 이해하는 데 도움이 될 수 있다. 이 정보는 설계 문서의 어딘가에 있을 수 있지만, 잘 배치된 주석은 독자들에게 가장 필요한 시간과 장소에 대한 지침을 제공한다. 이 경우 링크를 따라가면 RFC 4180이 RFC 7111에 의해 업데이트되었음을 알 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd;&quot;&gt;Rule 8: &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;버그를 고쳤을 경우 주석을 달아라.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석은 처음에 코드를 작성 할 때 뿐만 아니라, 수정할 때 특히 버그를 고쳤을 때 달아야 한다. 다음과 같은 주석을 생각해보자:&lt;/p&gt;
&lt;pre id=&quot;code_1675163997565&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; // NOTE: At least in Firefox 2, if the user drags outside of the browser window,
  // mouse-move (and even mouse-down) events will not be received until
  // the user drags back inside the window. A workaround for this issue
  // exists in the implementation for onMouseLeave().
  @Override
  public void onMouseMove(Widget sender, int x, int y) { .. }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;주석은 독자가 현재 코드와 참조된 방법으로 코드를 이해하는 데 도움이 될 뿐만 아니라 코드가 여전히 필요한지 여부와 테스트 방법을 결정하는 데 도움이 된다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;또한 issue tracker*깃허브를 참조하는 데 도움이 될 수 있다:&lt;/span&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675164087458&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Use the name as the title if the properties did not include one (issue #1425)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&quot;git blame&quot;은 줄이 추가되거나 수정된 커밋을 찾는 데 사용될 수 있지만 커밋 메시지는 짧은 경향이 있으며 가장 중요한 변경 사항(예: fixing issue #1425)은 가장 최근 커밋의 일부가 아닐 수 있다(&lt;/span&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;예: 한 파일에서 다른 파일로 메소드 이동).&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rule 9: 아직 완성되지 못한 구현에 대해 주석을 달아라.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;코드가 이미 알고 있는 한계가 있음에도 불구하고 코드를 체크해야 하는 경우가 있다. 코드의 알려진 결함을 공유하지 않는 것이 유혹적일 수 있지만, TODO 코멘트와 같이 다음과 같이 명시적으로 만드는 것이 좋다:&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675164169311&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// TODO(hal): We are making the decimal separator be a period, 
// regardless of the locale of the phone. We need to think about 
// how to allow comma as decimal separator, which will require 
// updating number parsing and other places that transform numbers 
// to strings, such as FormatAsDecimal&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;이러한 의견에 표준 형식을 사용하면 기술 부채를 측정하고 해결하는 데 도움이 된다. Tracker에 문제를 추가하고 주석에 문제를 참조하는 것이 좋습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;결론&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;나는 위의 예들이 주석이 나쁜 코드를 변명하거나 고치는 것이 아니라 다른 유형의 정보를 제공함으로써 좋은 코드를 보완한다는 것을 보여주었기를 바란다. 스택 오버플로우의 공동 설립자 Jeff Atwood가 쓴 것처럼, &quot;코드는 방법을 알려주고, 주석은 이유를 말해준다.&quot; &lt;/span&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;이 규칙들을 따르는 것은 여러분과 여러분의 팀원들의 시간과 좌절을 덜어줄 것입니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;그렇긴 하지만, 나는 이 규칙들이 완전하지 않다고 확신하며 (다른 곳에서) 주석에 제안된 추가 사항을 보기를 기대한다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>ETC</category>
      <category>개발</category>
      <category>개발자</category>
      <category>디벨로퍼</category>
      <category>언어</category>
      <category>주석</category>
      <category>주석처리</category>
      <category>코드</category>
      <category>코딩</category>
      <category>프로그래밍</category>
      <category>프로그램</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/31</guid>
      <comments>https://kimkani.tistory.com/31#entry31comment</comments>
      <pubDate>Tue, 31 Jan 2023 20:25:17 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI 알아보기 - 클래스로서 의존, Dependency Injection, Dependency Injector</title>
      <link>https://kimkani.tistory.com/29</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kybsa/btrVzZGki2O/tPKmSPs8yxKek1Dd7uNyt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kybsa/btrVzZGki2O/tPKmSPs8yxKek1Dd7uNyt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kybsa/btrVzZGki2O/tPKmSPs8yxKek1Dd7uNyt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fkybsa%2FbtrVzZGki2O%2FtPKmSPs8yxKek1Dd7uNyt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;675&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;What is Dependency Injector?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dependency Injector는 파이썬의 Dependency Injection(이하, DI)을 위한 프레임워크다. 먼저 소개글을 보겠다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Originally dependency injection pattern got popular in languages with static typing like Java. Dependency injection is a principle that helps to achieve an inversion of control. A dependency injection framework can significantly improve the flexibility of a language with static typing. Implementation of a dependency injection framework for a language with static typing is not something that one can do quickly. It will be a quite complex thing to be done well. And will take time.&lt;br /&gt;&lt;br /&gt;기본적으로 의존성주입 패턴은 자바같은 정적 타 언어에서 유명해진 패턴입니다. 의존성주입은 Inversion of Control(IoC)를 성취하기 위한 원칙이다.&amp;nbsp; 의존성 주입 프레임워크는 정적 타입과 같은 언어에서 유연성에 상당한 상승을 기여한다. 정적 타입 언어를 위한 의존성주입 프레임워크 실행은 누군가 빨리 할 수 있는 것은 아니다. 이를 잘하기 위해서는 꽤나 복잡한 일이다. 그리고 시간도 걸린다.&lt;br /&gt;&lt;br /&gt;Python is an interpreted language with dynamic typing. There is an opinion that dependency injection doesn&amp;rsquo;t work for it as well as it does for Java. A lot of the flexibility is already built-in. Also, there is an opinion that a dependency injection framework is something that Python developer rarely needs. Python developers say that dependency injection can be implemented easily using language fundamentals.&lt;br /&gt;&lt;br /&gt;파이선은 동적 타이핑을 하는 인터프리터 언어이다. 자바에서 하는 만큼 의존성 주입이 잘 작동하지 않는 의견도 있다. 대부분의 유연성이 이미 빌트인 되어있기 때문이다. 또한 파이썬 개발자에게 의존성 주입 프레임워크는 거의 필요하지 않다는 의견도 있다. 파이썬 개발자들은 의존성 주입은 언어의 기본을 잘 사용하면 쉽게 할 수 있다고 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Dependency Injection에 대해서 설명하고 있는데, 이에 대해 괜찮은 이야기 인것 같아서 다시 한 번 이야기하고 넘어가려고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k6Boc/btrXmRlTEIM/KXExKN0CWLEfjGhmktOQwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k6Boc/btrXmRlTEIM/KXExKN0CWLEfjGhmktOQwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k6Boc/btrXmRlTEIM/KXExKN0CWLEfjGhmktOQwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk6Boc%2FbtrXmRlTEIM%2FKXExKN0CWLEfjGhmktOQwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;268&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Coupling(커플링)과 Cohesion(응집력)&lt;br /&gt;&lt;br /&gt;커플링과 응집력은 컴포넌트들이 얼마나 강하게 묶여 있는지에 대한 이야기이다.&lt;br /&gt;- High Coupling(높은 커플링) : 커플링이 높으면, 강력 접착제같은 것을 사용한 것과 같다. 다시 떼기가 힘들다.&lt;br /&gt;- High Cohension(높은 응집력) : 응집력이 높으면, 커플링과 반대로 분리하고 다시 붙이기 쉽다.&lt;br /&gt;&lt;br /&gt;결론&lt;br /&gt;&lt;br /&gt;- 유연성 : 컴포넌트들은 약하게 커플링된다. 당신은 쉽게 시스템의 기능을 컴포넌트를 다른 방식으로 엮어서 확장하거나 바꿀 수 있다.&amp;nbsp;&lt;br /&gt;- 시험 가능성 : Mock을 API나 데이터베이스같은 실제 오브젝트 대신에 주입 가능하기에, 테스트가 더 쉬워진다.&lt;br /&gt;- 명확함과 유지 보수성 : 의존성주입은 의존성을 드러나게 도와준다. 즉, 애매함을 명확하게 해준다. 그리고 PEP20 - 파이썬의 선에서 나온 것처럼, &quot;애매한 것보다 명확한게 낫다&quot;를 실현 가능하다. 컨테이너 속 컴포넌트와 의존성을 명확하게 정의 가능하다. 이는 곧 전체 청사진과 앱 구조를 제공한다. 쉽게 이해 가능하고 바꿀 수 있다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Classes as Dependencies&lt;/h3&gt;
&lt;pre id=&quot;code_1673231009641&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {&quot;q&quot;: q, &quot;skip&quot;: skip, &quot;limit&quot;: limit}


@app.get(&quot;/items/&quot;)
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


@app.get(&quot;/users/&quot;)
async def read_users(commons: dict = Depends(common_parameters)):
    return commons&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;무엇이 의존성을 만드는가?&lt;/h3&gt;
&lt;pre id=&quot;code_1674885410681&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def common_parameters(
    q: Union[str, None] = None, skip: int = 0, limit: int = 100
):&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보면 의존들이 함수로 정의된 것을 알 수 있다. 하지만 이것이 의존성을 선언하는 유일한 방법은 아니다. 중요한 요소는 바로 의존이 &quot;callable(호출 가능성)&quot;이 되어야 한다는 것이다. 그래서 만약 어떤 오브젝트 something을 호출한다고 하면 다음과 같을 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1674885545454&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;something()
something(some_argument, some_keyword_argument=&quot;foo&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 한번 이를 클래스로 만들어보자.&lt;/p&gt;
&lt;pre id=&quot;code_1674885578882&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Cat:
    def __init__(self, name: str):
        self.name = name


fluffy = Cat(name=&quot;Mr Fluffy&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상황에서는 fluffy는 Cat 클래스의 인스턴스이다. fluufy를 만드는 것을 통해 Cat을 부르게 된다. 그리고 FastAPI에서 파이썬 클래스를 의존성으로 사용 가능하다. FastAPI에서 실질적으로 체크하는 것은 &quot;호출 가능성(함수, 클래스 혹은 아무거나)&quot;이다. 만약 &quot;callable&quot;들을 FastAPI의 의존으로 넘긴다면, FastAPI는 파라미터를 callble을 위해 분석하고, &quot;Path 실행함수&quot;처럼 동일하게 처리한다.&lt;/p&gt;
&lt;pre id=&quot;code_1674885775431&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import Union

from fastapi import Depends, FastAPI

app = FastAPI()


fake_items_db = [{&quot;item_name&quot;: &quot;Foo&quot;}, {&quot;item_name&quot;: &quot;Bar&quot;}, {&quot;item_name&quot;: &quot;Baz&quot;}]


class CommonQueryParams:
    def __init__(self, q: Union[str, None] = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


@app.get(&quot;/items/&quot;)
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
    response = {}
    if commons.q:
        response.update({&quot;q&quot;: commons.q})
    items = fake_items_db[commons.skip : commons.skip + commons.limit]
    response.update({&quot;items&quot;: items})
    return response&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 자세하게 분석해 보겠다. 위의 코드에서 클래스의 __init__은 사실 함수에서 정의하면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1674885939829&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def __init__(self, q: Union[str, None] = None, skip: int = 0, limit: int = 100):
async def common_parameters(q: Union[str, None] = None, skip: int = 0, limit: int = 100):&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위으 파라미터들이 FastAPI가 의존성을 풀기위해 사용하는 것이다. 그리고 이를 사용하면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1674885998261&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;타입 힌팅 VS Depends&lt;/h4&gt;
&lt;pre id=&quot;code_1674886071809&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;commons: CommonQueryParams = Depends(CommonQueryParams)
... = Depends(CommonQueryParams)
commons: CommonQueryParams ...
commons = Depends(CommonQueryParams)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서는 CommonQueryParams를 힌팅과 파라미터로 동시에 사용했다. 하지만 Depends(CommonQueryParma)가 실질적으로 의존성을 푸는 인자이며 FastAPI가 실질적으로 호출하는 것이다. 따라서 타입힌팅을 쓸 필요 없이,&lt;/p&gt;
&lt;pre id=&quot;code_1674886191016&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;commons = Depends(CommonQueryParams)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드처럼 쓰면 된다. 그 외에도 타입힌팅을 사용해서&lt;/p&gt;
&lt;pre id=&quot;code_1674886223123&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;commons: CommonQueryParams = Depends()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와 같이 써도 상관없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 PathParameter에 대해서 알아보겠다.&lt;/p&gt;</description>
      <category>Python/FastAPI</category>
      <category>FastAPI</category>
      <category>개발</category>
      <category>개발자</category>
      <category>컴공</category>
      <category>컴퓨터</category>
      <category>컴퓨터공학</category>
      <category>코딩</category>
      <category>프로그래머</category>
      <category>프로그래밍</category>
      <category>프로그램</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/29</guid>
      <comments>https://kimkani.tistory.com/29#entry29comment</comments>
      <pubDate>Sat, 28 Jan 2023 15:11:40 +0900</pubDate>
    </item>
    <item>
      <title>[EuroPython] 어떻게 Python 3.11은 빨라질 수 있었을까? 2편 - dictionary</title>
      <link>https://kimkani.tistory.com/28</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.11.png&quot; data-origin-width=&quot;525&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MKzMN/btrXpcaEJUN/sZCgznOr05hgz5mdr3xz9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MKzMN/btrXpcaEJUN/sZCgznOr05hgz5mdr3xz9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MKzMN/btrXpcaEJUN/sZCgznOr05hgz5mdr3xz9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMKzMN%2FbtrXpcaEJUN%2FsZCgznOr05hgz5mdr3xz9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;525&quot; height=&quot;513&quot; data-filename=&quot;3.11.png&quot; data-origin-width=&quot;525&quot; data-origin-height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일반적인 파이썬 객체&lt;/h3&gt;
&lt;pre id=&quot;code_1674823936105&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class C:
	def __init__(self, a, b):
    	self.a = a
        self.b = b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위으 코드는 두개의 attribute를 가지고 있으며 a, b를 할당한다. 위의 코드가 메모리 상에서 어떻게 접근하고 메모리를 점유하는지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 모든 파이썬 Ojbect는 __dict__을 가지고 있다. 이 때 __dict__ 속성은 거의 직접적으로 사용되지 않는다. 파이썬 객체는 고정된 크기를 가지고 있지 않다. 그리고 __slots__이라는 속성을 가지고 있을 수 있다. 또는 List처럼 빌트인 타입들로부터 상속받을 수 있다. 이러한 것들은 읽기 쉽고 직관적인 코드를 제공해준다. 이는 곧 dict[&quot;attribute&quot;]처럼 할 필요 없이 dict.attribute로 가능케 한다. 하지만 모든 객체가 이러한 방식을 사용하는 것이 아니다. 아까 언급했듯이 __slots__라는 속성을 가지고 있을 수 있고, 그 외에도 다양한 타입을 가질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 곧 다시금 말하지만 크기가 가변적이라는 것이다. 이러한 특성으로 인해 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;일반적인 파이썬 객체의 실행은 느리다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zE5yE/btrXnuckHaI/wbB3PrmJHvoGA35Jgi2lsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zE5yE/btrXnuckHaI/wbB3PrmJHvoGA35Jgi2lsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zE5yE/btrXnuckHaI/wbB3PrmJHvoGA35Jgi2lsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzE5yE%2FbtrXnuckHaI%2FwbB3PrmJHvoGA35Jgi2lsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;522&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;위의 그림을 보면, 흰색 박스로 된 객체가 있다. 그 객체는 클래스의 향하는 포인터를 가지고 있다. 그리고 다시 이 클래스는 객체를 가리키는 포인터를 가지고 있다. 이는 가변적인 사이즈로 인해 발생하는 문제이다. 따라서 dict_offset을 통해 __dict__이 있는 곳을 찾는다. (초록색 박스), 여기서 이 클래스는 객체가 몇개가 있든 단 하나만 존재한다. 하지만 빨간색 박스는 다르다. 딕셔너리 속 값들은 해시와 키, 그리고 값들로 이루어져있다.&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 위의 경우를 self.a에 대입하면, 키 a에 저장되는 값들이 테이블로 이루어진다. 이를 개선한 것이 3.2까지의 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;435&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxCgyE/btrXn7AWrBo/60PUdfgVT3jelKTAXwc57k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxCgyE/btrXn7AWrBo/60PUdfgVT3jelKTAXwc57k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxCgyE/btrXn7AWrBo/60PUdfgVT3jelKTAXwc57k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxCgyE%2FbtrXn7AWrBo%2F60PUdfgVT3jelKTAXwc57k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;435&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;435&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.3 부터 3.10까지는 데이터 구조를 조금 변경해서 딕셔너리 키들(3.2까지는 테이블에 있던 것들을) 클래스의 인자로서 작동하도록 만들었다. 이를통해 중복되는 키값을 지울 수 있게 되었다. 이러한 것들은 전부 분리된 데이터 구조로 분리되어 class와 dictionary가 공유되도록 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pz0Pl/btrXn7VfNTV/fIRbHufP4NMWvonXLyih9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pz0Pl/btrXn7VfNTV/fIRbHufP4NMWvonXLyih9k/img.png&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;448&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.788%; margin-right: 10px;&quot; data-widthpercent=&quot;50.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pz0Pl/btrXn7VfNTV/fIRbHufP4NMWvonXLyih9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpz0Pl%2FbtrXn7VfNTV%2FfIRbHufP4NMWvonXLyih9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duRO2n/btrXpbQppE9/BkqUT17DHc039DG2NVeYK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duRO2n/btrXpbQppE9/BkqUT17DHc039DG2NVeYK0/img.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;452&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.0492%;&quot; data-widthpercent=&quot;49.63&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duRO2n/btrXpbQppE9/BkqUT17DHc039DG2NVeYK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduRO2n%2FbtrXpbQppE9%2FBkqUT17DHc039DG2NVeYK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;3.11에서는 한번 더 바뀌게 되는데, 포인터의 위치 변경이다. 기본적으로 딕셔너리의 포인터 __dict__이 고정된 offset 즉, object의 앞으로 옮겨지게 되었다. 이는 곧 dict_offset을 통해 참조할 필요가 없어짐을 보이는 것이며, 이를 통해 메모리 접근을 줄이는 결과를 보이기도 한다. 즉 메모리 크기를 줄인 것은 아니지만, 메모리 접근, 메모리 사이클에 대해서 어느정도 줄이게 되었음을 의미한다. 또한 딕셔너리(dictionary)를 줄여서 딕셔너리 키를 class를 통해 접근할 수 있게 한다. 모든 값들이 object에 넣어졌을 때 접근할 수 있도록 하기도 했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;즉 이를 요약하자면, 기존에 있던 dictionary의 메모리 크기는 줄이지 않았지만, 메모리 자체에 대한 접근을 줄여서 속도를 향상시킨 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 정리 글에서는 알고리즘 변경에 대해서 이야기 해보겠다.&lt;/p&gt;</description>
      <category>Python/ETC</category>
      <category>3.11</category>
      <category>python</category>
      <category>개발</category>
      <category>개발자</category>
      <category>코드</category>
      <category>코딩</category>
      <category>파이썬</category>
      <category>프로그래머</category>
      <category>프로그래밍</category>
      <category>프로그램</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/28</guid>
      <comments>https://kimkani.tistory.com/28#entry28comment</comments>
      <pubDate>Fri, 27 Jan 2023 22:24:27 +0900</pubDate>
    </item>
    <item>
      <title>[EuroPython] 어떻게 Python 3.11은 빨라질 수 있었을까? 1편</title>
      <link>https://kimkani.tistory.com/27</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;525&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NcnB3/btrW9lgipru/PVuymhEP8DCH6iG5eiyGjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NcnB3/btrW9lgipru/PVuymhEP8DCH6iG5eiyGjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NcnB3/btrW9lgipru/PVuymhEP8DCH6iG5eiyGjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNcnB3%2FbtrW9lgipru%2FPVuymhEP8DCH6iG5eiyGjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;525&quot; height=&quot;513&quot; data-origin-width=&quot;525&quot; data-origin-height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;Python, 아주 강력한 인터프리터 언어다. 아주 다양한 곳에 사용되고, Django, FastAPI, Flask를 비롯한 강력한 웹 프레임워크들도 있다. 하지만 한가지 걸리는 점이 있었으니, 바로 이전부터 제기되어왔던 속도 문제였다. 다른 언어와 비교해서 파이썬은 속도에 대해서 약점을 가지고 있다. 물론 태생적인 한계일수도 있다. 아래의 표를 통해 파이썬의 빠르기를 알 수 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u2A50/btrW6qh9V6U/XG5EZyxrt5UYq7kNvBpYAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u2A50/btrW6qh9V6U/XG5EZyxrt5UYq7kNvBpYAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u2A50/btrW6qh9V6U/XG5EZyxrt5UYq7kNvBpYAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu2A50%2FbtrW6qh9V6U%2FXG5EZyxrt5UYq7kNvBpYAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;432&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파이썬 3.11의 주된 변경점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 Faster CPython(더 빠른 CPython) 방법은 파이썬 특히 CPython의 실행 속도를 높이기 위해 만들어진 프로젝트다. 특히 이번 업데이트에서는 많은 병견점이 있는데 다음과 같은 변경점이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;적응형 전문 인터프리터 (PEP 659)&lt;/li&gt;
&lt;li&gt;연속적으로 할당된 실행 프레임들&lt;/li&gt;
&lt;li&gt;Zero cost try-except&lt;/li&gt;
&lt;li&gt;더욱 정형화된 오브젝트 레이아웃&lt;/li&gt;
&lt;li&gt;레이지하게 생성하는 객체 Dict&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메모리 접근은 비싸다&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li id=&quot;8935&quot; data-selectable-paragraph=&quot;&quot;&gt;Arithmetic operation: 1 cycle&lt;/li&gt;
&lt;li id=&quot;6d63&quot; data-selectable-paragraph=&quot;&quot;&gt;L1 cache latency: ~4 cycles&lt;/li&gt;
&lt;li id=&quot;e2a7&quot; data-selectable-paragraph=&quot;&quot;&gt;L2 cache latency: ~10 cycles&lt;/li&gt;
&lt;li id=&quot;b9fd&quot; data-selectable-paragraph=&quot;&quot;&gt;L3 cache latency: ~30 cycles&lt;/li&gt;
&lt;li id=&quot;d09a&quot; data-selectable-paragraph=&quot;&quot;&gt;RAM latency: ~200+ cycles&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5Ghz CPU에서 위와 같은 메모리 접근성을 보인다. 물리적인 접근은 L1, L2, L3 그리고 RAM으로 갈수록 소통이 비싸진다. 즉, 어느 프로그램이든 메모리 접근이 있으면 물리적인 한계로 인해 프로그램은 느려질 수 밖에 없다. 만약 다른 프로그램이 특정 메모리 주소를 접근하려고 할 때, 다른 프로그램이 그 메모리 주소를 점유하고 있다면 더욱더 느려질 수 밖에 없을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;파이썬의 내부 데이터 구조 실행방식&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;Linked List vs Array List&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rhIzH/btrW9lt5vFj/kmKbaixEFKwktBywFsAxD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rhIzH/btrW9lt5vFj/kmKbaixEFKwktBywFsAxD0/img.png&quot; data-alt=&quot;Link Lists vs Arrays (Image Source:&amp;amp;amp;nbsp; EuroPython Conference )&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rhIzH/btrW9lt5vFj/kmKbaixEFKwktBywFsAxD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrhIzH%2FbtrW9lt5vFj%2FkmKbaixEFKwktBywFsAxD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;459&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Link Lists vs Arrays (Image Source:&amp;amp;nbsp; EuroPython Conference )&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linked List와 Array를 잠시 보고 가겠다. 만약 우리가 2번째 요소를 읽겠다고 하면 Linked List의 경우는 Head에서 2번째 요소까지 4번 메모리 접근을 해야하는 악재가 따른다. 하지만 Array의 경우는 두번의 메모리 접근(즉, Head에서 Array로 그리고 Array에서 각 요소로 한번에 접근)할 수 있다. 메모리 접근에서는 이득을 보인다. 만약 메모리를 더 할당하려면 Linked List는 메모리를 더 할당해야할 것이다. 따라서 이러한 의존성과 메모리 접근을 최대한 피하기 위한 프로그래밍 언어 설계는 힘든 편이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 다른 이야기인 Frame 스택에 대해 이야기하고 넘어가야 할 것 같다. 이 프레임 스택은 파이썬 함수를 부를 때 사용되는 객체이다. 각각의 파이썬 함수를 부를 때 마다, Frame Object를 스택에 넣는다. 이때 이 프레임은 지역 변수, 임시 값을 위한 공간, 이전 프레임, 전역 변수 그외 등등을 위한 참조, 그리고 디버깅 정보를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파이썬 3.10 그이하 버전&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uSiKd/btrXhvvzO3o/5Du0imXAgahlKQfQmcQkG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uSiKd/btrXhvvzO3o/5Du0imXAgahlKQfQmcQkG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uSiKd/btrXhvvzO3o/5Du0imXAgahlKQfQmcQkG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuSiKd%2FbtrXhvvzO3o%2F5Du0imXAgahlKQfQmcQkG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;987&quot; height=&quot;714&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명했다시피 파이썬 프레임은 Linked List로 연결되어 있었고, 이는 Top에 있는 스택만 가져와서 부르기에는 편했다. 하지만 다른 스택을 부르기에는 추가적인 비용도 들고 새로운 공간 할당을 위해 공간을 비워둬야 하는 등 위에서 말한 메모리 접근 측면에서 굉장히 비용을 치루게 된다. 3.10이하 에서는 이러한 공간 할당을 위한 Caching이 없었고, 그리고 있었다 하더라도 그렇게 효과적이지 않았다. 실제로 3.10이하에서는 링크 스택을 위해 많은 메모리 청크가 스레드 마다 할당되어 있었고, 만약 새로운 프레임이 생성될 경우 기존 메모리를 참고해야하는 경우가 생긴다. 이는 곧 L1같은 빠른 메모리에서, L2, L3, 그리고 RAM같은 느린 메모리로 접근할 때 많은 대가를 치루게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파이썬 3.11&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DCBXK/btrXdNqMSbq/HvygzgXCepYJ0dlfKTxcK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DCBXK/btrXdNqMSbq/HvygzgXCepYJ0dlfKTxcK0/img.png&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;700&quot; data-is-animation=&quot;false&quot; style=&quot;width: 55.3414%; margin-right: 10px;&quot; data-widthpercent=&quot;55.99&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DCBXK/btrXdNqMSbq/HvygzgXCepYJ0dlfKTxcK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDCBXK%2FbtrXdNqMSbq%2FHvygzgXCepYJ0dlfKTxcK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1185&quot; height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSbX1Y/btrXjCOeffE/mKN4l748EDb2BdHbdknqD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSbX1Y/btrXjCOeffE/mKN4l748EDb2BdHbdknqD0/img.png&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;472&quot; data-is-animation=&quot;false&quot; width=&quot;824&quot; height=&quot;619&quot; style=&quot;width: 43.4958%;&quot; data-widthpercent=&quot;44.01&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSbX1Y/btrXjCOeffE/mKN4l748EDb2BdHbdknqD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSbX1Y%2FbtrXjCOeffE%2FmKN4l748EDb2BdHbdknqD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;628&quot; height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러한 사항을 파이썬 3.11에서는 아주 큰 메모리 할당을 통해 해결하고자 했다. 일단 미리 큰 메모리를 할당해놓는데, 얼마나 큰 Frame Stack을 얻게 될지 몰라서이다. 그리고 메모리에서 새로운 할당보다는, 재사용을 통해 메모리 활용성을 높이고자 했다. 그리고 이러한 Frame 객체를 재활용할 때, 파이썬 3.11에서 프레임 객체는 느리게(Lazily) 생성된다. 이는 곧 더 적은 메모리를 요구한다. 물론 이러한 Lazily 생성은 무조건 모든 케이스에 맞는 것이 아니지만, 이러한 경우는 적기에 3.10과 비교했을 때 굉장히 큰 이득을 얻게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 3.10 이하와 비교했을 때 3.11은 두가지 변경점을 가진다. 먼저 디버깅 정보가 느리게 생성된다. 왜냐하면 이는 기본적으로 Frame Stack의 직접적인 파트가 아니기 때문이다. 그리고 Exception 스택이 버려졌다. 또한 네임 스페이스 Dict이 키값을 가능할 때마다 공유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Zero Cost Exceptions&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림을 보다보면, Exception 스택이 없어진 것을 확인할 수 있다. 이는 메모리 절약을 위해 사용된다. 그 이유를 알기 위해 Exception이 어떻게 활용되는지를 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.10에서, try-except는 바이트코드에 명시적으로 구현되어 있다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li id=&quot;0dbc&quot; data-selectable-paragraph=&quot;&quot;&gt;Try는 내부 스택으로 자그마한 데이터를 넣고, 시스템이 예외 처리를 위해 어디로 가야할지, 그리고 얼마나 많은 실행 스택을 사출해야할지를 이야기해준다.&lt;/li&gt;
&lt;li id=&quot;7cc5&quot; data-selectable-paragraph=&quot;&quot;&gt;이는 160바이트를 매 프레임 객체마다 소모하며, 3.10 미만의 버전에서는 240바이트나 소모한다.&lt;/li&gt;
&lt;li data-selectable-paragraph=&quot;&quot;&gt;실제로 21개의 try, except를 Nested하게 짜면 21 x 160바이트로 메모리 이슈가 나타난다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;9bbb&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.11에서 정보는 테이블에 저장된다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li id=&quot;b155&quot; data-selectable-paragraph=&quot;&quot;&gt;예외가 발생하지 않는 한, 아무것도 실행되지 않는다&lt;/li&gt;
&lt;li id=&quot;a576&quot; data-selectable-paragraph=&quot;&quot;&gt;예외 발생시, 오프셋과 스택의 깊이는 테이블에서 조회된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;5847&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;꽤나 Zero, 하지만 완벽하지 않은 Zero&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li id=&quot;9415&quot; data-selectable-paragraph=&quot;&quot;&gt;코드 객체의 크기를 조금 늘리게 되었다.&lt;/li&gt;
&lt;li id=&quot;1db1&quot; data-selectable-paragraph=&quot;&quot;&gt;그리고 예외가 발생할 때 조금 느려졌다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;/p&gt;
&lt;figure id=&quot;og_1674696837172&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;How Python 3.11 is becoming faster&quot; data-og-description=&quot;Python is a great language but everyone already knows that. Now with Python 3.11, it&amp;rsquo;s making quite some noise in the Python circles. It&amp;hellip;&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/aiguys/how-python-3-11-is-becoming-faster-b2455c1bc555&quot; data-og-url=&quot;https://medium.com/aiguys/how-python-3-11-is-becoming-faster-b2455c1bc555&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxps1B/hyRpRvHUl2/XFSmmQmEWEZiLYoXzJ4oaK/img.jpg?width=960&amp;amp;height=684&amp;amp;face=0_0_960_684&quot;&gt;&lt;a href=&quot;https://medium.com/aiguys/how-python-3-11-is-becoming-faster-b2455c1bc555&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/aiguys/how-python-3-11-is-becoming-faster-b2455c1bc555&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxps1B/hyRpRvHUl2/XFSmmQmEWEZiLYoXzJ4oaK/img.jpg?width=960&amp;amp;height=684&amp;amp;face=0_0_960_684');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;How Python 3.11 is becoming faster&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Python is a great language but everyone already knows that. Now with Python 3.11, it&amp;rsquo;s making quite some noise in the Python circles. It&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1674765674320&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Running Faster than Ever Before&quot; data-og-description=&quot;How does Python 3.11 perform in comparison with Python 3.10 and Julia 1.7?&quot; data-og-host=&quot;towardsdatascience.com&quot; data-og-source-url=&quot;https://towardsdatascience.com/running-faster-than-ever-before-afd1d213a3ae&quot; data-og-url=&quot;https://towardsdatascience.com/running-faster-than-ever-before-afd1d213a3ae&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cL8vf8/hyRpG2ydgS/7bTNjfar7R6KR2AsgKfSD1/img.jpg?width=1200&amp;amp;height=789&amp;amp;face=0_0_1200_789&quot;&gt;&lt;a href=&quot;https://towardsdatascience.com/running-faster-than-ever-before-afd1d213a3ae&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://towardsdatascience.com/running-faster-than-ever-before-afd1d213a3ae&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cL8vf8/hyRpG2ydgS/7bTNjfar7R6KR2AsgKfSD1/img.jpg?width=1200&amp;amp;height=789&amp;amp;face=0_0_1200_789');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Running Faster than Ever Before&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How does Python 3.11 perform in comparison with Python 3.10 and Julia 1.7?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;towardsdatascience.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=xKk7IXm0XO0&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/tB62X/hyRoKZMpR5/RhvY4tCk9E42SiR4ltOSik/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=166_238_212_288&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/xKk7IXm0XO0&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Python/ETC</category>
      <category>3.11</category>
      <category>python</category>
      <category>개발</category>
      <category>개발자</category>
      <category>코드</category>
      <category>코딩</category>
      <category>파이썬</category>
      <category>파이썬3.10</category>
      <category>프로그래밍</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/27</guid>
      <comments>https://kimkani.tistory.com/27#entry27comment</comments>
      <pubDate>Fri, 27 Jan 2023 06:22:31 +0900</pubDate>
    </item>
    <item>
      <title>Django 모델 알아보기 - User 모델 및 커스터마이징</title>
      <link>https://kimkani.tistory.com/26</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;django_final-2048x768.png&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rN95Q/btrWXjWRyrk/kDo2FHJBtLx9njD8W5K2Fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rN95Q/btrWXjWRyrk/kDo2FHJBtLx9njD8W5K2Fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rN95Q/btrWXjWRyrk/kDo2FHJBtLx9njD8W5K2Fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrN95Q%2FbtrWXjWRyrk%2FkDo2FHJBtLx9njD8W5K2Fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2048&quot; height=&quot;768&quot; data-filename=&quot;django_final-2048x768.png&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Django에서 간단하게 User 모델 만들기&lt;/h3&gt;
&lt;pre id=&quot;code_1674707397446&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from django.contrib.auth.models import User
&amp;gt;&amp;gt;&amp;gt; user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')

# At this point, user is a User object that has already been saved
# to the database. You can continue to change its attributes
# if you want to change other fields.
&amp;gt;&amp;gt;&amp;gt; user.last_name = 'Lennon'
&amp;gt;&amp;gt;&amp;gt; user.save()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 기본적인 장고의 User모델은 django.contrib.auth.models의 User를 import하면 된다. 위의 코드에서 장고 ORM을 사용해서 create_user메소드를 사용해 간단하게 만들 수 있다. 위의 경우 user모델에 'john', 'lennon@thebeatles.com', 'johnpassword'를 넣어서 간단하게 User 모델을 만든 경우이다. 그러면 궁금한 분들이 계실 것이다, 바로 이 User모델은 기본적으로 어떤 필드 가지고 있을 것인가? 이다. 그것을 아래에서 차근차근 하나씩 설명해 보려고 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디폴트 User 모델&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*이 부분은 장고의 Docs를 참조하는 것을 추천합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;username&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필수, 150자 이하의 필드이다. 만약 150자 이상이 필요한 경우라면 커스텀 유저 모델을 사용하는 것이 좋다. 만약 MySQL을 utf8mb4과 같이 사용한다면, max_length=191을 최대로 설정해야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;first_name, last_name&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션, 150자 이하의 필드이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;email&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션, 이메일 필드이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;password&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필수, 비밀번호의 메타데이터, 해시이다. (장고는 해시되지 않은 raw 패스워드를 저장하지 않는다) 더 자세한 것은 &lt;a href=&quot;https://docs.djangoproject.com/en/4.1/topics/auth/passwords/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.djangoproject.com/en/4.1/topics/auth/passwords/&lt;/a&gt;를 참고하자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;groups, user_permissions&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각, Group하고 Permission을 위한 Many-to-many(다대다) 관계이다.&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;is_staff&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;admin사이트에 입장 가능한지 판별하는 Boolean 값.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;is_active&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django에서는 유저 데이터를 바로 삭제하기 보다는, is_active&amp;nbsp; 플래그를 False로 둬서 하는 것을 추천한다. 그 이유는 이 유저 데이터와 연결된 외래키가 있을 경우 외래키 관계가 부셔질수도 있기 때문이다. 만약 inactive(False)인 유저가 로그인하게 허락하고 싶으면, LoginView를 이용해서 AuthenticationForm을 커스터마이징하는 것을 추천한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;is_superuser&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Boolean값으로, 모든 권한을 가진 유저를 뜻한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;last_login, date_joined&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 마지막으로 로그인한 DateTime과 유저가 생성된 DateTime을 뜻한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;User모델 커스터마이징 하기 그 세가지 방법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;User모델과 일대일 관계를 가지는 모델 생성&lt;/li&gt;
&lt;li&gt;AbstractUser를 상속받는 모델을 생성&lt;/li&gt;
&lt;li&gt;AbstractBaseUser를 상속받는 모델을 생성&lt;/li&gt;
&lt;li&gt;Proxy 모델을 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;User모델과 일대일 관계를 가지는 모델 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 User모델과 관련된 정보를 저장하고 싶다면, 일대일 관계를 가지는 모델을 생성하면 된다. 이 일대일 모델은 프로필 모델이라고도 불리며, 유저의 인증과 관련없는 정보를 저장할 때 사용한다. 예를들면, 유저의 닉네임, 소속 등등일 것이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674708907102&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.contrib.auth.models import User

class Employee(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    department = models.CharField(max_length=100)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 User모델에 새로운 정보를 추가할 수 있게 된다. 하지만 기존 정보는 그대로 남아있으므로, User모델을 그대로 이용해야 하며, email로 로그인해야되는 것은 변함없다. 그리고 이러한 테이블이 나눠진 경우 웹앱의 퍼포먼스가 나빠진다는 단점이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 모델을 이용해 만약 ORM을 작성한다면 아래와 같이 작성이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1674709122402&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; u = User.objects.get(username='fsmith')
&amp;gt;&amp;gt;&amp;gt; freds_department = u.employee.department&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AbstractUser를 상속받는 모델을 생성&lt;/h4&gt;
&lt;pre id=&quot;code_1674709394460&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    nickname = models.CharField(max_length=50)
    phone = models.PhoneNumberField(unique = True, null = False, blank = False)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AbstractUser은 User모델에서 동작은 그대로 하지만, 필드만 재정의하고 싶을 때 사용한다. 즉, 기존 필드를 사용하면서 새로 정의한 필드를 추가할 때 사용한다. 이 때 새로운 User모델을 정의했기 때문에, settings.py에다가 AUTH_USER_MODEL = 'app_name.User'를 추가해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;AbstractBaseUser를 상속받는 모델을 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AbstractBaseUser같은 경우 로그인 방식의 변경, 원하는 필드들로 유저 모델의 구현 등등 완전한 커스터마이징을 하고 싶을 때 사용한다. 즉 코드를 재작성 하는 수준이라고 보면 좋을 것 같다. 필수 필드로서 password 하나의 필드만 설정되어 있다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1674709852764&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.db import models
from django.db.models import CharField, EmailField, BooleanField, UUIDField, DateTimeField
from django.contrib.auth.models import AbstractBaseUser

class User(AbstractBaseUser):
    id = UUIDField(primary_key=True, max_length=64, unique=True, null=False)
    name = CharField(max_length=255, unique=False)
    email = EmailField(unique=True)
    password = CharField(max_length=255, null=False, blank=False)
    is_active = BooleanField(default=True)
    is_staff = BooleanField(default=False)
    is_superuser = BooleanField(default=False)
    created_at = DateTimeField(auto_now_add=True, blank=True)
    updated_at = DateTimeField(auto_now_add=True, blank=True)
    login_at = DateTimeField(auto_now_add=True, blank=True, null=True)
    ethereum_address = CharField(max_length=255, null=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드처럼 아주 자유도가 높은 코드다. 실제로 AbstractBaseUser를 상속받으면, id, last_login, password만 상속받고, 나머지는 스스로 정의해줘야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Proxy 모델을 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 User 모델을 직접 상속하며, Meta클래스에서 proxy=True 속성을 추가하여 사용한다. 기존 DB 스키마에 영향을 주지 않으며, 프로젝트 중간에 무언가 추가할 필요가 있다면 사용하기 좋은 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674710007114&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.contrib.auth.models import User
from .managers import PersonManager

class Person(User):
    objects = PersonManager()

    class Meta:
        proxy = True

    def do_something(self):
        ...&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 장고의 User 모델에 대해 알아봤다. 다음에는 장고에서 사용되는 Permission에 대해 알아보려고 한다. 다음 글에서 보겠다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Python/Django</category>
      <category>Django</category>
      <category>python</category>
      <category>til</category>
      <category>개발</category>
      <category>개발자</category>
      <category>백엔드</category>
      <category>서버</category>
      <category>장고</category>
      <category>파이썬</category>
      <category>프레임워크</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/26</guid>
      <comments>https://kimkani.tistory.com/26#entry26comment</comments>
      <pubDate>Thu, 26 Jan 2023 14:14:23 +0900</pubDate>
    </item>
    <item>
      <title>[번역] 2022년 리뷰 &amp;amp; 2023년의 테마 : Layer 1 - BNB체인, Solana(솔라나)</title>
      <link>https://kimkani.tistory.com/25</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfgYxE/btrWtfGvfE5/dp0JHfYdKC9cNQAlYJjVok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfgYxE/btrWtfGvfE5/dp0JHfYdKC9cNQAlYJjVok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfgYxE/btrWtfGvfE5/dp0JHfYdKC9cNQAlYJjVok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfgYxE%2FbtrWtfGvfE5%2Fdp0JHfYdKC9cNQAlYJjVok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;650&quot; data-filename=&quot;다운로드.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BNB 체인&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DeFi에서의 좋은 퍼포먼스와 on-chain 메트릭스의 지속된 강함을 보여준 BNB체인은 비교적 강세인 해를 보냈다. 시가총액은 내려갔지만, 상대적으로 견고한 상태를 유지했다. 2022년 동안 BNB 체인이 ~55%하락할때, 이더리움(-67%), Solana(-93%), 그리고 아발란체(-88%)를 보여줬다.&lt;/li&gt;
&lt;li&gt;On-chain 메트릭스는 일일 거래가 Solana에 이어 두번째로 많은 300만으로 2022년을 마무리했다. 일일 활성 지갑주소는, 다른 한편으로 BNB체인이 연중 차트 1위를 차지하는 등 강세를 이어가고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhElec/btrWqpwVmoQ/OPkgX4vKsoFVbJXOPzShL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhElec/btrWqpwVmoQ/OPkgX4vKsoFVbJXOPzShL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhElec/btrWqpwVmoQ/OPkgX4vKsoFVbJXOPzShL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdhElec%2FbtrWqpwVmoQ%2FOPkgX4vKsoFVbJXOPzShL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1148&quot; height=&quot;600&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;DeFi 측면에서 BNB체인은 연중 지속적인 상승세를 이어왔으며, 지난 5월까지 비교적 견조한 모습을 보이다가 테라 생태계 붕괴에 따른 강한 상승세를 보였다. 사실,&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt; BNB체인은 지난 4월 6.3%까지 점유율이 하락했지만, 12월 초에는 TVL 점유율이 13%를 넘어 2배 이상 상승했다. PancakeSwap은 계속해서 TVL을 점유했으며, &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;매일 3억에서 5억 달러 이상을 정기적으로 살펴본다. PancakeSwap의 StableSwap의 소개와 Aptos와 Ethereum과의 병합은 주목할만한 2022 하이라이트였으며, 우리는 PancakeSwap팀이 올해 어떤 것을 할지 지켜보고 있다. 게다가, Wombat Exchagne(stablecoin swaps)의 시작과 이와 관련된 에코시스템의 성장 (예 : Wombex Finance)이 약속되어가고 있으며, 만약 BUSD 시장 지배율을 이어간다면 계속해서 사용이 늘 것이라고 본다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyXeu6/btrWop5hOrf/a4gRfnNoiKKuxyKBpqnAKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyXeu6/btrWop5hOrf/a4gRfnNoiKKuxyKBpqnAKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyXeu6/btrWop5hOrf/a4gRfnNoiKKuxyKBpqnAKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyXeu6%2FbtrWop5hOrf%2Fa4gRfnNoiKKuxyKBpqnAKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1114&quot; height=&quot;642&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보안 측면에서, BNB&amp;nbsp; 체인은 AvengerDAO를 만들며 이어가고 있다. &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Avenger DAO는 웹3의 선도적인 보안 회사와 블록체인의 얼리어답터들이 BNB Chain의 사용자들을 잠재적인 해킹, 사기 및 악의적인 행위자들로부터 보호하기 위해 고안된 독특한 커뮤니티 운영 보안 인프라 프로젝트에 함께 모인 협업이다. &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Avenger DAO는 2023년에도 우선 순위로 남아 있으며 dApp에 제공할 수 있는 서비스의 품질과 양을 개선하는 데 있어 추가적인 진전이 있을 것으로 기대한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;확장적 관점에서 BNB 체인은 네트워크에 구축하려는 dApp을 위한 일련의 솔루션을 보유하고 있다. BNB 사이드 체인은 2022년 초반에 배포되었으며, 이는 약간의 견인 능력을 보였으며, BNB 체인에서의 메이저 공지는 그들만의 zk Rollups인 zkBNB다. 이 zkBNB의 테스트넷은 11월에 출시되었으며, 메인넷 런칭은 2023년 1분기로 예상된다. 더 나아가,&amp;nbsp; BNB 체인은 BNB사이드 체인과 zkBNB를 위한 EVM과 동일한 확장 솔루션을 위해 일하고 있으며, 이러한 자세한 사항은 2023년에 보여질 것으로 기대된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;BNB 유동성 예치의 런칭과 BNB 체인 NFT의 OpenSea와의 통합은 주목할만한 2022년의 하이라이트였다. 더 나아가, BNB체인은 또한 Binance Accound Bounb(BAB) 토큰과 함께 BNB체인 사상 최초의 Soulbound Token의 출시를 알렸다. 이 토큰은 KYC 증명을 마친 바이낸스 유저들의 신원 보증을 대표하며, 다른 BNB 체인 dApp과 통합될 것이라고 예상한다. 이 글을 쓰는 지금, 620,000개 이상의 BAB토큰이 민팅되었다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Solana&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3sPgW/btrWu6iqYN4/5CLxWrlBuMAQSeH0QCDaGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3sPgW/btrWu6iqYN4/5CLxWrlBuMAQSeH0QCDaGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3sPgW/btrWu6iqYN4/5CLxWrlBuMAQSeH0QCDaGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3sPgW%2FbtrWu6iqYN4%2F5CLxWrlBuMAQSeH0QCDaGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1176&quot; height=&quot;706&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2022년은 Solana에게 있어 반쪽짜리가 계속된 해였다. 2022년은 2021년의 거래에 비해, 클래식 &quot;알트-L1&quot;들에게 있어 도전적인 해였으며, Solana또한 예외는 아니였다. &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Solana는 NFT 및 dApp 혁신을 통해 지속적으로 높은 수준의 일일 트랜잭션을 유지할 수 있었던 반면, 시스템 중단과 FTX 상황의 영향으로 인해 지속적인 문제가 발생했다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Ethereum에 비해 저렴한 가스 요금의 혜택을 받은 체인과 함께 NFT는 Solana에게 밝은 부분이었다. Magic Eden과 같은 마켓플레이스는 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이러한 노력을 주도하는 데 도움이 되었고 Solana NFT를 새로운 최고치로 끌어올리는 데 도움이 되었다. 위의 그림과 같이, 2022년 통틀어 Solana NFT는 Ethereum다음으로 두번째를 차지했으며, 전체적으로 전체 거래량 3위를 차지하는데 달했다. 다른 한편으로, &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;최근 대표적인 Solana NFT 컬렉션인 DeGods와 y00ts가 각각 이더리움과 폴리곤으로 브릿지하기 위한 움직임을 발표하는 것을 보았다. &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;만약 이것이 더 넓은 트렌드의 일부가 된다면, 이는 최근 몇 달 동안 빛나고 있는 Solana 내 하위 부문에 큰 역풍이 될 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;네트워크 관점에서 Solana는 성능 문제로 인해 계속해서 어려움을 겪고 있으며 2022년까지 여러 번의 운영 중단을 겪었다. 최근 FTX 상황은, FTX, 알라메다, Solana 에코시스템 사이의 매우 공개적인 지지와 관계를 고려할 때 L1에게도 좋지는 않다. 놀랄 필요도 없이, Solana와 에코시스템 프로젝트는 시가총액과 DeFi 점유율 지표 모두에서 상당한 출혈이 있었다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;흥미로운 것은 Solana Mobile과 함께 하드웨어 분야에 대한 Solana의 모험이다. Solana Mobile은 암호화 사용을 위해 맞춤 설계된 프리미엄 하드웨어 경험을 약속하며, 사용자는 암호화를 염두에 두고 빌트인으로 설계된 휴대폰에서 얻을 수 있는 안전과 보안을 중요하게 생각할 것이라고 믿는다. 하드웨어 디바이(the Saga Phone)는 1분기부터 출하될 것으로 예상된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;올해는 Firedancer의 추가적인 발전과 최종적인 구현이 기대된다. Jump Crypto에 의해 구축되는 Firedancer는 Solana 네트워크를 위한 두 번째, 완전히 독립적인 합의 노드 클라이언트 구현이 될 것이다. 단일 장애 지점(현재 Solana Labs 클라이언트 실행점)을 제거하여 분산성을 높이고 네트워크의 성능과 복원력을 향상시키는 동시에 자체 노드를 보다 저렴하게 실행할 수 있을 것으로 기대된다. 이 두 번째 클라이언트가 있다는 것은 본질적으로 버그가 Solana Labs 클라이언트를 다운시키더라도 체인은 다른 클라이언트에서 계속 실행된다는 것을 의미한다. 이것이 완성되면 Solana는 이더리움을 제외한 체인 중 유일하게 둘 이상의 독립적인 검증 클라이언트를 보유하게 된다. 이는 Solana 네트워크의 핵심 다음 단계가 될 것이며 연중 모니터링해야 할 중요한 이야기가 될 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Web3/BlockChain</category>
      <category>binance</category>
      <category>BnB</category>
      <category>Solana</category>
      <category>바이낸스</category>
      <category>블록체인</category>
      <category>비트코인</category>
      <category>솔라나</category>
      <category>이더리움</category>
      <category>체인</category>
      <category>코인</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/25</guid>
      <comments>https://kimkani.tistory.com/25#entry25comment</comments>
      <pubDate>Tue, 17 Jan 2023 13:12:18 +0900</pubDate>
    </item>
    <item>
      <title>[번역] 2022년 리뷰 &amp;amp; 2023년의 테마 : Layer 1 - 이더리움(Ethereum)</title>
      <link>https://kimkani.tistory.com/24</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRRQSb/btrWmnqYIxI/GWkwzpwG4jmFppmy3nIYr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRRQSb/btrWmnqYIxI/GWkwzpwG4jmFppmy3nIYr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRRQSb/btrWmnqYIxI/GWkwzpwG4jmFppmy3nIYr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRRQSb%2FbtrWmnqYIxI%2FGWkwzpwG4jmFppmy3nIYr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;650&quot; data-filename=&quot;다운로드.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Layer 1&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 기준, 암호화폐 세상에서 일어난 일을 모두 고려하면 Layer 1은 흥미진진하고 다사다난한 해를 보냈다고 말할 수 있을 것이다. 2022년동안 많은 사건이 Layer 1공간에서 일어났다. 9월의 이더리움의 Proof-of-Work(&quot;PoW, 작업 증명 방식&quot;)에서 Proof-of-Stake(&quot;PoS, 지분 증명 방식&quot;)으로의 전환부터, 5월 테라 에코시스템의 내파까지 말이다. Aptos의 그들의 메인넷부터 Sui가 곧 하기로 예정된 것 까지 새로운 Layer 1도 발표되었다. 주목할만한 현역 BNB 체인과 이를 주도하는 L2 솔루션, 폴리곤이 테라가 나가고 남은 공백을 시장 점유율을 얻었으며, 솔라나는 FTX 파산에 따라 영향을 크게 받은 Layer 1이 된, 도전적인 한해 였다.&amp;nbsp; 2022년은&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt; 논쟁의 여지가 있는 물질적인&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;, 가장 중요한 암호화폐의 서브 섹터인 &lt;/span&gt;사건들로 가득했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5wxXG/btrWppIP9Ts/sJZPaJGKmLMRkkWaUKrPNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5wxXG/btrWppIP9Ts/sJZPaJGKmLMRkkWaUKrPNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5wxXG/btrWppIP9Ts/sJZPaJGKmLMRkkWaUKrPNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5wxXG%2FbtrWppIP9Ts%2FsJZPaJGKmLMRkkWaUKrPNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;911&quot; height=&quot;595&quot; data-origin-width=&quot;911&quot; data-origin-height=&quot;595&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시장과의 연관성에 대한 우리의 견해를 이야기하기 전에, 위의 그림은 현재 L1 / L2의 풍경의 추상화된 개요를 보여준다. &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;근본적으로 이더리움 기본 계층은 체인 혼잡으로 인해 대량 채택에 적합하지 않으며 이에 따른 높은 거래 비용(가스 요금)이 여전히 존재한다. 아직, L1 / L2 공간에서의 개발은 확장성 문제에 대한 최적의 솔루션을 다루고 있다. 각기 다른 체인들은 각기 다른 의견으로 핵심 이슈에 대해 씨름하며 다양한 솔루션에 작업하고, 몇몇은 이더리움과 나란히 가거나, 그렇지 않은 것도 있다. &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;매우 넓게 말해서, 우리는 네 개의 다른 섹션을 나누려고 한다. &lt;/span&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;이더리움 가상 머신(&quot;EVM&quot;) 체인, 비 EVM 체인, 롤업(L2) 및 Layer 0/크로스 체인 프로토콜.&lt;/span&gt; 더 나아가, 이 섹션에서 우리는 모든 열거된 프로젝트, 그리고 더, 디테일하게 다루고 2022년 그들의 성과와 우리가 기대하는 것에 대해 인사이트를 제공하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzJ4eu/btrWdGTpiBc/4Ip1eEiDbNDy3ebFejXvzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzJ4eu/btrWdGTpiBc/4Ip1eEiDbNDy3ebFejXvzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzJ4eu/btrWdGTpiBc/4Ip1eEiDbNDy3ebFejXvzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzJ4eu%2FbtrWdGTpiBc%2F4Ip1eEiDbNDy3ebFejXvzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;504&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시가총액 vs 온-체인 시장&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 시가총액 복수의 이유로 인해 낮아졌으며, 이는 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;전반적으로 악화되는 거시경제 상황이 핵심적이다. 하지만 우리는 명백하게 분명히 할 것이 시가총액이 &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;일일 거래 및 활성 주소 측면에서 중요한 온체인 메트릭과 반드시 상관관계가 있는 것은 아니라는 것이다. 보다싶이, BNB체인과 솔라나가 뛰어나게 잘하고 있는 것을 여기서 볼 수 있으며, 그에 반해 이더리움은 큰 시가총액에도 불구하고, 눈에 띄게 더 낮은 일일 활성화를 보여준다. &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;BNB 체인은 하루 평균 100만 건 이상을 꾸준히 달성하며 일일 활성 주소 기준으로 오랫동안 1위를 유지해왔다. &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;이와 유사하게 솔라나는 지난 한 해 동안 일일 거래 수치에서 상위권에 들거나 그 근처에 있었다. 우리는 이런 특징을 각 체인마다 아래 섹션에서 더 알아보고, 자세히 볼려고 한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;Ethereum&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2022년은 이더리움의 역사에서 아주 큰 순간을 본 해가 될 것이며, 전체 암호화폐 산업에서 아주 주목할만한 순간중 하나가 될 것이다. 9월 15일, PoW 합의 메커니즘에서 PoS로 전환된 &lt;a href=&quot;https://academy.binance.com/en/articles/the-merge-ethereum-upgrade-all-you-need-to-know&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;The Merge&lt;/a&gt;가 드디어 이루어졌기 때문이다. 그 과정에서 &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;블록체인이 전력을 집약적으로 채굴하는 것이 아닌 검증자와 예치자로 확보된다는 점에서 이더리움의 에너지 소비량은 99.99% 감소했다. 좀더 넓은 방면에서, 이더리움의 PoS로의 마이그레이션은 0.2%의 전세계 에너지 소비를 줄였다고 한다. 전체적으로 The Merge는 &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;광범위한 dApp 생태계, NFT와 관련된 두려움, 불확실성, 의심(&quot;FUD, &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Fear &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Uncertainty and &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Doubt&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&quot;)의 핵심 원천을 제거하는 데 도움이 되었다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eox6QU/btrWopPRPZU/j93emczNfmvW2R81eOaxg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eox6QU/btrWopPRPZU/j93emczNfmvW2R81eOaxg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eox6QU/btrWopPRPZU/j93emczNfmvW2R81eOaxg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feox6QU%2FbtrWopPRPZU%2Fj93emczNfmvW2R81eOaxg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;417&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;The Merge가 &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;끼친 가장 중요한 영향 중 하나는 일일 이더 발행의 측면이다. 이런 전환으로 인해, 활동에 참여하게끔 하는 유저가 더이상 비싼 채굴 보상을 지급할 필요가 없어졌다. &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;실제로 이로 인해 이더 발행은 3.58%/년에서 0.005%/년으로 감소했으며, 이는 시장에 의해, 아직 완전히 가격을 책정하지 않은 공급 증가율의 상당한 감소다. 사실, 소각 메커니즘인 EIP-1559의 실행과의 조합과 함께, &lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;이더는 11월의 대부분을 디플레이션 자산으로 보냈고 현재 그 수준에 매우 근접해 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OdtA9/btrWd2CceVB/bKNyzpkOYsNGf0dK7IKaR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OdtA9/btrWd2CceVB/bKNyzpkOYsNGf0dK7IKaR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OdtA9/btrWd2CceVB/bKNyzpkOYsNGf0dK7IKaR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOdtA9%2FbtrWd2CceVB%2FbKNyzpkOYsNGf0dK7IKaR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;395&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;The Merge 이전에는 존재하지 않던 실질적인 예치 수익률은 합병 이후 5~7% 사이에서 등락을 거듭하고 있다. 이더리움의 지분율은 토큰의 지분율뿐만 아니라 네트워크 상의 활동 수준에 따라 달라진다. 당신은 더 넓은 이더리움 생태계를 위한 &quot;위험에서 자유로운 비율&quot;을 설정하는 것으로 예치 수익을 생각할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wWHxb/btrWku5hY1P/zZc7fEgK3rayDlDp3G3aF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wWHxb/btrWku5hY1P/zZc7fEgK3rayDlDp3G3aF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wWHxb/btrWku5hY1P/zZc7fEgK3rayDlDp3G3aF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwWHxb%2FbtrWku5hY1P%2FzZc7fEgK3rayDlDp3G3aF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;621&quot; height=&quot;371&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이더리움 여정의 다음 단계는 사용자들에게 지분이 있는 이더를 철회할 수 있는 기능을 제공하는 상하이 업그레이드를 포함할 것이다. 그 후, EIP-4844가 다음 주요 목표가 될 것이며, 완전한 Sharding으로 가는 주요 단계인 Proto-Danksharding을 가능케 할 것이다. 이더리움 내의 우선순위 속, The Merge와 Layer 2로 이동하는 것이 확장성 격차를 메우는 데 도움이 되었지만 Sharding이 가져올 것으로 예상되는 L1과 L2 용량의 증가는 여전히 높은 기대를 받고 있으며 2023년까지 이 분야의 개발이 면밀히 추적될 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/27R6y/btrWggzPisp/zxcaBRyunTXQLoXPLQDJ5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/27R6y/btrWggzPisp/zxcaBRyunTXQLoXPLQDJ5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/27R6y/btrWggzPisp/zxcaBRyunTXQLoXPLQDJ5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F27R6y%2FbtrWggzPisp%2FzxcaBRyunTXQLoXPLQDJ5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;840&quot; height=&quot;714&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Web3/BlockChain</category>
      <category>ethereum</category>
      <category>경제</category>
      <category>바이낸스</category>
      <category>번역</category>
      <category>블록체인</category>
      <category>영어</category>
      <category>이더</category>
      <category>이더리움</category>
      <category>코인</category>
      <category>토큰</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/24</guid>
      <comments>https://kimkani.tistory.com/24#entry24comment</comments>
      <pubDate>Mon, 16 Jan 2023 12:53:50 +0900</pubDate>
    </item>
    <item>
      <title>Events that tiangolo(Sebasti&amp;aacute;n Ram&amp;iacute;rez) who is creator of FastAPI went through, and My Opinion</title>
      <link>https://kimkani.tistory.com/23</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JVIn1/btrWeeB2FEt/8kDegkyoXV7DoUeBk1kwtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JVIn1/btrWeeB2FEt/8kDegkyoXV7DoUeBk1kwtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JVIn1/btrWeeB2FEt/8kDegkyoXV7DoUeBk1kwtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJVIn1%2FbtrWeeB2FEt%2F8kDegkyoXV7DoUeBk1kwtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;675&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;This post is based on Tweet of tiangolo&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;While I do a programming with surfing a twitter, I saw some tweets of tiangolo, who is creater of FastAPI, that he went through it &lt;span style=&quot;color: #000000;&quot;&gt;When he created the framework, 1.5 years ago. &lt;/span&gt;It was a funny contents about a job description. &lt;span style=&quot;color: #000000;&quot;&gt; It was funny that it has been only for 1.5 years that the framework has been developed but the job required 4+ years of experience. &lt;/span&gt;Even the creater couldn't apply to the job.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;693&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KqiH6/btrWjlUtfKv/ahfVbGzZ5nOkLn8UAYOyj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KqiH6/btrWjlUtfKv/ahfVbGzZ5nOkLn8UAYOyj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KqiH6/btrWjlUtfKv/ahfVbGzZ5nOkLn8UAYOyj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKqiH6%2FbtrWjlUtfKv%2FahfVbGzZ5nOkLn8UAYOyj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;445&quot; height=&quot;302&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;693&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;After 2.5 years, when the FastAPI is launched about exactly 4 years, he updated this tweet again, and it gives me many impressions and thinkings. For me, I'm a just junior developer who just met a FastAPI just 1 year ago, It's very impressive for a who want to join a developer. Frankly, I thought &quot;years of experience = skill level&quot;, and it is really general thoughts to me. After saw this post, however, I want to ask some question that &quot;Years of Experience is really absolute scale for developer?&quot;, and other job.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;525&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blAdfy/btrWd0Ydy2J/NnE2yRXhhwgw6jrpHVZwQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blAdfy/btrWd0Ydy2J/NnE2yRXhhwgw6jrpHVZwQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blAdfy/btrWd0Ydy2J/NnE2yRXhhwgw6jrpHVZwQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblAdfy%2FbtrWd0Ydy2J%2FNnE2yRXhhwgw6jrpHVZwQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;413&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;525&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We need to think about this question, &quot;is the years of experience same as skill level?&quot; Well, for HR Team, Year of Expereienc can be a very powerful tools and scales to estimate developer. But, is this still valid to these days? tiangolo throw a question to this by &quot;For many modern jobs, years of experience is not a good estimator for skill level&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;663&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcNbrw/btrWfQt4EWH/JDWfL7IPD8oJeT0SJNvYH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcNbrw/btrWfQt4EWH/JDWfL7IPD8oJeT0SJNvYH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcNbrw/btrWfQt4EWH/JDWfL7IPD8oJeT0SJNvYH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcNbrw%2FbtrWfQt4EWH%2FJDWfL7IPD8oJeT0SJNvYH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;642&quot; height=&quot;581&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;663&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I also agree to his answer to these questions, that &quot;how to choose applicants&quot;, we need to think a actual test or actual skills to figure out their REAL POWER. University Diplomes, it's also not a good choice, are valid for some Jobs, but is it still effective to check a applicants' skill or ability? And he argue that companies need to check &quot;Ability to Learn&quot;, and I absolutely agree to this. But, it's maybe hard to estimate, because it's not a number, it's a potential. But, I think this part is really considerd to HR Team.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And he also recommend to applicant and people who loves job. &quot;Apply Anyway&quot;, I truly agree to this, because if nothing is showed, then how to check own's potential? Also applicants should need to show their potential by stuff. It can be blog posting, meetups and conference talks. We, applicants, need to be more bold and need to be more detail to own's skill and ability.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In my opinion, this tweet penetrates all the companies and the applicants. Don't be a number, but we need to be ourself potential.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thank you for reading.&amp;nbsp;&lt;/p&gt;</description>
      <category>Python/FastAPI</category>
      <category>ENGLISH</category>
      <category>FastAPI</category>
      <category>tiangolo</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/23</guid>
      <comments>https://kimkani.tistory.com/23#entry23comment</comments>
      <pubDate>Sun, 15 Jan 2023 21:30:56 +0900</pubDate>
    </item>
    <item>
      <title>[번역] 2022년 리뷰 &amp;amp; 2023년의 테마 : 키 포인트</title>
      <link>https://kimkani.tistory.com/22</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;747&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDi1DN/btrWllT2DHR/05Y1X2l1qVNWyekkwjaPu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDi1DN/btrWllT2DHR/05Y1X2l1qVNWyekkwjaPu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDi1DN/btrWllT2DHR/05Y1X2l1qVNWyekkwjaPu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDi1DN%2FbtrWllT2DHR%2F05Y1X2l1qVNWyekkwjaPu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1470&quot; height=&quot;747&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;747&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주된 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체적으로 2022년은 경종의 역할과 함께 2021년 최고점과 나란히 놓여졌다. 그렇기는 하지만, 변덕스러움은 시장의 사이클에 있어 핵심적인 부분이다. 이것은 암호화폐 산업에서 딱히 새로운 것은 아니다, 그리고 우리는 조심스럽게 낙관적인 전망을 2023년에 바라본다.&lt;/li&gt;
&lt;li&gt;L1(Layer 1)에게는 다사다난한 해였다.이더리움은 Proof-of-Stake(지분 증명 방식)으로 전환했으며, BNB체인과 폴리곤은 테라의 붕괴에서 남은 공간, 시장 파이를 차지했으며, Aptos나 Sui같은 새로운 L1은 시장으로의 첫 침략을 만들어냈다.&lt;/li&gt;
&lt;li&gt;L2(Layer 2)에게 있어서는 아주 강력한 유입이 있던 히트친 2022년이었다. TVL(Total Value Locked, 예치된 자금)은 2022년동안 (이더리움으로 환산해서) 119%나 올랐다. 와중에 USD환산 TVL은 28%정도 하락했으며, 이는 전체 DeFi시장에서 ~76%정도의 축소가 일어난 것에 비하면 굉장히 낮은 수치이며, 주장컨대 암호화폐 자산의 가격을 떨군 가장 큰 요인이라고 본다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;DeFi 부분은 외부 요인에 의한, 계속된 시장에서의 사건으로 TVL부분에서 급락을 맛봤다. TVL은 약 400억 달러에 이르렀거나, 혹은 2022년 시작의 25%에 가까워졌다. 더욱더 많은 유저들이 안전자산으로 회귀하느라 바쁘다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;NFT는 219억 달러 판매를 기록했으며, 저년대비 10.6%의 상승을 달성했다. 이는 2022년 중 첫 반년 정도의 주된 기여에서 나왔다. 하지만 NFT 시장은 상대적으로 조용한 나머지 반년을, 전체적으로 부정적인 감정에 기반한 시선으로 바라보았으며, 거래자들은 이어지는 시장의 사건들로 인해 시장을 탈출했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;몇몇 블록체인 게임 프로젝트는 계속해서 성장했지만, 천천한 성장의 신호가 있다. X-to-Earn(무언가 하며 돈을 버는) 게임 뒤편에 위치한 선전은 유저 성장이 둔화됨에 따라 사라지고, 스폿라이트는 게임 플레이의 퀄리티로 옮겨졌으며, 이는 X-to-Earn 게임들의 토크노믹스도 똑같았다. 메타버스를 향한 관심도 점차 시들해졌다.&lt;/li&gt;
&lt;li&gt;2022년 수많은 시장에서 일어난 사건은 참여정책분석에 불을 지폈다. 규제가 요구한 투명성을 들고오는 동안, 정보에 근거한 결정은 필수적이 되었다.&lt;/li&gt;
&lt;li&gt;2022년은 기록을 갱신한 VC 투자 및 모금 활동으로 기록될 것이다. 모금이 수많은 자금 - 이제는 효율적 사용을 찾고 있는 - 을 일으킨 초기 투자비용이 높은 년도였다.2022년 2분기 이후 거래흐름은 일반적으로 줄어들었다.&lt;/li&gt;
&lt;li&gt;마지막으로 이 리포트에서 Cyprto의 다른 섹터 속에서 2023년의 키 테마를 요약하려고 한다. 우리가 말하려고 하는 것을, 이 글에서 봐줬으면 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;도입부&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년이 수많은 좋지 않는 사건들이 일어났던 해로 기억되는 동안, 누군가는 이것들이 초창기에 놓여있는 산업의 증상발현이며 이 산업의 초창기 이슈들이라고 주장할 수 있다. 좀더 자세히는, 기록적인 인플레이션에 휩싸인 - 계속되는 글로벌화의 붕괴로 이어진 지정학적인 긴장 - 거시 경제적 기후와 초창기에 놓여 있는 산업와의 싸움이라고 말이다. 전체적으로 2022년은 경종의 역할과 함께 2021년 최고점과 나란히 놓여졌다. 2022년의 시도들은 암호화폐의 미래와 암호화폐의 탈 중앙화, 보안 그리고 투명성의 이상(理想)을 확인하는 능력에 있어 많은 회의론을 가져왔지만, 우리는 이미 이것이 암호화폐 산업에서 새로운 것이 아니라는 것을 알고 있으며, 우리는 조심스럽게나마 낙관적인 전망을 2023년에 보이려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 회복이나 발전의 조짐을 찾는 것이 2022년도, 암호화폐 시장 자본총액이 64% 하락하며, 1월 1일 2조 2천억 달러에서 12월 31일 7860억 달러로 내려간 년도에는 힘들수도 있지만, 축하할 만한 많은 것들이 있다. Ethereum은 Proof-of-Work에서 Proof-of-Stake로 전환했으며, 이는 블록체인 역사에서 기록할만한 순간일 것이다. 그와중에 Terra가 지고, BNB 체인이 떠오르며 계속해서 DeFi를 향해 큰 발걸음을 내딛었다. Polygon은 지속적인 비지니스 개발 감각을 입증했으며, 비즈니스에서 유명한 기업들을 암호화폐를 철저하게 조사하기 위해 채용했다. (Reddit, Starbucks, 그리고 Meta 등등). Layer2 네트워크들은 상승을 유지했으며,&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;L2-22&quot;를 가지고 있지 않았을 수도 있다. &lt;/span&gt;많은 사람들이 염원하던 L2는 새로운 이정표를 세웠고, 이더리움 Layer 1를 여러방면에서 넘어섰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;스테이블코인 시가총액도 사상 최고치를 경신하며 많은 코인들의 상승세가 이어졌으며, &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;암호화폐를 받아들이는 것이 시도하는 것보다 낫다는 것을 현명하게 이해하면서, 물에 발을 담궈보며 시험삼아 도전하기 시작했다. &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;사용 사례와 볼륨 모두에서 상당한 발전을 이룬&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt; DeFi는 파생 프로토콜과 유동성 공급자와의 싸움에서 주목할 만한 승리를 거두며 어려운 한 해를 보냈다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;대부분의 프로필 사진(&quot;PFP&quot;) NFT 컬렉션의 바닥 가격이 크게 떨어졌지만 여전히 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;PFP가 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;트위터와 인스타그램과 같은 web2 소셜 채널과 같은&lt;/span&gt; 메이저에 도입된 2022년 web3 채택을 향한 핵심적인 역할을 했다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;정책은 대서양을 마주보는 미국과 유럽 모두에서 주목할 만한 움직임을 보였으며, &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;EU의 가상 자산 시장 규제안이 유럽에서 헤드라인을 차지하는 반면, 미국은 계속해서 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;증권거래위원회와 CFTC를 오갔다. 다른 곳을 보면, 전세계의 많은 나라들이 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;점점 더 암호화 친화적인 규제와 함께 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;암호화폐 세계로 향하는 경쟁과 함께 암호화폐 세계로 향하는 사람들을 계속 환영했다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이 보고서에서, 우리는 위에서 이갸기한 것보다 더 많은 2022년 암호화폐의 이야기, 서사, 시점을 탐구하려고 한다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;또한 우리는 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;앞으로의 한 해와 모든 지속적인 암호화 혁신과 이것이 가져올 수 있는 비들(BUIDL)에 대해 생각할 때, &lt;/span&gt;바이낸스 리서치가 기대하는 맛과 향을 제공하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web3/BlockChain</category>
      <category>NFT</category>
      <category>가상자산</category>
      <category>가상화폐</category>
      <category>바이낸스</category>
      <category>비트코인</category>
      <category>암호</category>
      <category>암호화폐</category>
      <category>이더리움</category>
      <category>폴리곤</category>
      <category>화폐</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/22</guid>
      <comments>https://kimkani.tistory.com/22#entry22comment</comments>
      <pubDate>Sun, 15 Jan 2023 17:11:56 +0900</pubDate>
    </item>
    <item>
      <title>[번역] &amp;quot;Web3&amp;quot;에서 우리들의 생활은 어찌 바뀔까? 현재 남은 과제와 이후의 전망</title>
      <link>https://kimkani.tistory.com/21</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;본 기사는 아래의 Soft Bank 뉴스 기사를 번역한 것입니다.&lt;/p&gt;
&lt;figure id=&quot;og_1673759417992&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;「Web3」で私たちの生活はどう変わる？ 現状の課題と今後の展望 - ITをもっと身近に。ソフトバ&quot; data-og-description=&quot;特定の企業に依存しない次世代インターネット「Web3（ウェブスリー）」ですが、Web3の活用が普及することで、私たちの生活にどのような変化をもたらすのでしょうか。 NFTやメタバースなど&quot; data-og-host=&quot;www.softbank.jp&quot; data-og-source-url=&quot;https://www.softbank.jp/sbnews/entry/20221102_03&quot; data-og-url=&quot;https://www.softbank.jp/sbnews/entry/20221102_03&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/GsXfw/hyRhQkB2zH/pNHcXi2wyRz8QZlPI2vfkk/img.jpg?width=830&amp;amp;height=552&amp;amp;face=0_0_830_552,https://scrap.kakaocdn.net/dn/c5RuCu/hyRhGCjnji/PRIsC9ZkRZEaxUZOpUuIEK/img.jpg?width=830&amp;amp;height=552&amp;amp;face=0_0_830_552,https://scrap.kakaocdn.net/dn/chJXPY/hyRhOmL550/ODZb5MdEDLjYQXz9H8ak4K/img.jpg?width=830&amp;amp;height=553&amp;amp;face=0_0_830_553&quot;&gt;&lt;a href=&quot;https://www.softbank.jp/sbnews/entry/20221102_03&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.softbank.jp/sbnews/entry/20221102_03&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/GsXfw/hyRhQkB2zH/pNHcXi2wyRz8QZlPI2vfkk/img.jpg?width=830&amp;amp;height=552&amp;amp;face=0_0_830_552,https://scrap.kakaocdn.net/dn/c5RuCu/hyRhGCjnji/PRIsC9ZkRZEaxUZOpUuIEK/img.jpg?width=830&amp;amp;height=552&amp;amp;face=0_0_830_552,https://scrap.kakaocdn.net/dn/chJXPY/hyRhOmL550/ODZb5MdEDLjYQXz9H8ak4K/img.jpg?width=830&amp;amp;height=553&amp;amp;face=0_0_830_553');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;「Web3」で私たちの生活はどう変わる？ 現状の課題と今後の展望 - ITをもっと身近に。ソフトバ&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;特定の企業に依存しない次世代インターネット「Web3（ウェブスリー）」ですが、Web3の活用が普及することで、私たちの生活にどのような変化をもたらすのでしょうか。 NFTやメタバースなど&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.softbank.jp&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정한 기업에 의존하지 않는 차세대 인터넷 &quot;Web3(웹3)&quot;이지만,&amp;nbsp; Web3의 활용이 보급되는 것에서, 우리들의 생활에 어떤 변화를 가져올 것인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NFT나 메타버스 등, Web3관련 서비스는 주목을 계속 받고 있지만, 생활과의 관계성은 조금은 이미지로 떠오르기 힘들다는 분도 많다고 보지 않을까 생각합니다. 이번에는 경제산업성 &quot; Web3.0 시대에서 일어나는 크리에이터 이코노미 창출에 따른 연구회&quot;나 총무성 &quot;Web3시대를 바라보는 메타버스 등의 이용과 활용에 관한 연구회&quot;의 위원이기도 한 변호사 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;増田雅史(마스다 마사후미)씨에게 Web3의 생활에서의 영향이나 현재의 과제에 대해 이야기를 여쭈어 보려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Web3에 의해 우리들의 생활은 어떻게 바뀔까?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kB0So/btrWcL1vRYE/qQ7sec5803WNcfjw0U7Qh0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kB0So/btrWcL1vRYE/qQ7sec5803WNcfjw0U7Qh0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kB0So/btrWcL1vRYE/qQ7sec5803WNcfjw0U7Qh0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkB0So%2FbtrWcL1vRYE%2FqQ7sec5803WNcfjw0U7Qh0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;474&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web3는 블록체인 기술을 이용한 분산형 인터넷을 말합니다. 특정한 관리자가 없는 비 중앙 집권적 컨트롤이 가능하며, 기업의 서버에 개인정보를 기록할 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Web2.0은 Web3에 옮겨간다?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1NupJ/btrWlmk5IxR/yitW2WhyTkETtkUvaT7Enk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1NupJ/btrWlmk5IxR/yitW2WhyTkETtkUvaT7Enk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1NupJ/btrWlmk5IxR/yitW2WhyTkETtkUvaT7Enk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1NupJ%2FbtrWlmk5IxR%2FyitW2WhyTkETtkUvaT7Enk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;553&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷이 보급된 1990년대 텍스트가 주체면서 커뮤니케이션이 한 방향으로 가는 &quot;Web1.0&quot;이었지만, 2000년대는 인터넷 통신속도가 고속화되면서, SNS나 영상 생중계 서비스의 보급에 의해 양방향 커뮤니케이션이 가능한 &quot;Web2.0&quot;으로 변화했습니다. Web2.0은 GAFAM&lt;span style=&quot;color: #000000;&quot;&gt;(Google, Apple, Facebook, Amazon,Microsoft)&lt;/span&gt;에 개인정보나 활동이력, 유저의 성향이란 많은 데이터가 모여있습니다. 그곳에서 개인정보의 유출이나 프라이버시 침해라는 리스크에서 벗어나 특정 기업에 의존하지 않는 Web3이란 개념이 태어났습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;増田雅史(마스다 마사후미) : Web3는 Web2.0에 대응하는 문제 의식에서 태어난 개념입니다. 다만, Web2.0에서 Web3으로 한번에 움직이는 것은 아니라고 봅니다. 왜냐하면, 현재는 Web3에 일반 계층에 도달할 서비스가 없기 떄문입니다. 또한 Web2.0같은 서비스에도 다양한 좋은 측면이 있기에, 아마도 Web2.0과 Web3는 이제부터 공존해갈 것이라고 생각합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Web3가 후에 급속도로 퍼질 &quot;계기&quot;가 되는 것은?&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSvkdX/btrWg1IFeEG/eXMBk59CPkcI4Jwq2VeGi1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSvkdX/btrWg1IFeEG/eXMBk59CPkcI4Jwq2VeGi1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSvkdX/btrWg1IFeEG/eXMBk59CPkcI4Jwq2VeGi1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSvkdX%2FbtrWg1IFeEG%2FeXMBk59CPkcI4Jwq2VeGi1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;467&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;467&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web3는 국가에서도 추진하고 있고, 메타버스(가상공간)나 NFT를 활용한 Web3 서비스의 이용 확대에 힘을 넣고 있습니다. 마스다씨도 후에 Web3의 이용이 넓어질 가능성이 크다고 말하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;増田雅史(마스다 마사후미)&lt;span&gt; : 인터넷이나 스마트폰이 이 세상에 등장한 초기에는, 스스로 받아들이는 경우는 적었다고 생각합니다. 하지만, 지금은 인터넷이 없는 생활은 생각할 수 없고, 스마트폰도 보다 넓은 세대에서 쓰이고 있습니다. 새로운 인터넷으로서 일반인도 이용하기 쉬운 UX, UI를 갖춘 킬러 프로덕트가 출현한다면 확 보급되어 흐름이 바뀔것이라고 생각합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span&gt;Web3가 급속도로 보급될 계기로서, 마스다씨는 아주 유력시하는 것이 젊은 층에게 인기있는 &quot;게임&quot;이라고...&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span&gt;Web3가 맨 먼저 퍼진다고 하면, 유력한 것은 게임이라고 생각하빈다. 최근에 게임은 젊은 세대가 많은 시간을 할애하는 분야입니다. 예를 들어, 메타버스등의 가상공간을 활용한 게임에서는 기존의 SNS와는 다른 형태로 자신의 거처가 되는 커뮤니티를 발견하는 것이 가능하며, 아바타에게 옷을 입혀서 자신을 표현한다던지, 타자와 커뮤니케이션 하면서 돈을 쓰는 사람도 늘어나고 있는 분야입니다. 게임은 Web3가 최초로 펼쳐질 입구 중 하나로 생각합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span&gt;Web3의 활용이 기대되는 분야&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvoZaX/btrWd2hdJyH/ORDZa7wKOxK9kFcOeazeZ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvoZaX/btrWd2hdJyH/ORDZa7wKOxK9kFcOeazeZ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvoZaX/btrWd2hdJyH/ORDZa7wKOxK9kFcOeazeZ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvoZaX%2FbtrWd2hdJyH%2FORDZa7wKOxK9kFcOeazeZ1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;438&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span&gt;Web3에는 블록체인 기술이 채용되어 디지털 상의 데이터를 변경할 수 없는 구조로 되어있습니다. 그 특성을 살려서 이미 이하의 분야를 시작으로 하는 다양한 분야에서 Web3가 활용되고 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임 시장&lt;/li&gt;
&lt;li&gt;음악시장&lt;/li&gt;
&lt;li&gt;소매, EC시장&lt;/li&gt;
&lt;li&gt;애니메이션, 만화 시장&lt;/li&gt;
&lt;li&gt;의류&lt;/li&gt;
&lt;li&gt;부동산 업계&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Web3를 가볍게 체험하기 위한 수단은?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web3는 다양한 분야에서 활용되고 있습니다만, 먼저 가볍게 이용해보기 위한 분은 아래와 같은 것을 시험해봐도 좋겠지요?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암호화폐나 NFT를 구매해 본다&lt;/li&gt;
&lt;li&gt;메타버스(가상공간)을 체험해 본다&lt;/li&gt;
&lt;li&gt;게임&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;増田雅史(마스다 마사후미)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&lt;span&gt; 메타버스는 Zoom이나 팀즈같이 일에서 쓸 수 밖에 없는 상황이 일어난 경우, 한번에 보급될 가능성이 높다고 생각합니다. 또한, 특정한, 좋아하는 콘텐츠가 메타버스 공간을 활용했던 게임이 된다던지, 원래부터 좋아했던 게임의 새로운 버전이 온라인을 통해 메타버스 공간에 제공된다면 그러한 콘텐츠 팔로워가 자연스럽게 유입하고 Web3를 체험하는 기회도 늘 것이겠지요.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;Web3의 현재 과제&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGL5UK/btrWgg60KVo/6MEeRTcVI0IQDIVFGdTUsk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGL5UK/btrWgg60KVo/6MEeRTcVI0IQDIVFGdTUsk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGL5UK/btrWgg60KVo/6MEeRTcVI0IQDIVFGdTUsk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGL5UK%2FbtrWgg60KVo%2F6MEeRTcVI0IQDIVFGdTUsk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;530&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 분야에서 활용이 기대되는 Web3이지만, 물론 과제도 있으며, 밑에 적은 것들이 제기되고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;법 정비가 정리되지 않았다&lt;/li&gt;
&lt;li&gt;수수료(가스비)가 나온다 (블록체인 이용에는 가스비가 발생)&lt;/li&gt;
&lt;li&gt;이용을 위한 허들이 높다 (블록체인에 관한 지식이 얕은 사람이라면 쓰기 어렵다)&lt;/li&gt;
&lt;li&gt;쓰기 어려운 UI가 많다&lt;/li&gt;
&lt;li&gt;문제에 대해서는 전부 자기 책임&lt;/li&gt;
&lt;li&gt;일반인 대상의 서비스가 적다&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;増田雅史(마스다 마사후미)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&lt;span&gt;&lt;span&gt; 일반인이 Web3에 접촉하기 위해서는 두가지 허들이 있습니다. 한가지는 UI, 이용 환경이 정비되어 있는가 하는 문제이며, 또 한가지는 킬러 프로덕트의 존재, 즉 수요가 환기되는가 하는 점입니다. Web3는 비 중앙 집권으로 자신의 정보나 재산은 자신이 관리합니다. Web3가 편리하다던지, 쓰기 쉽다던지, 아주 유용하다고 판단하는 경우 사용하시는 분도 있을거라고 생각합니다만, 그렇지 않은 경우 종래의 Web2.0 서비스 그 자체로 좋지,라는 형태가 됩니다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;span&gt;Web3에서는 원칙적으로 기업이 개인정보를 관리하는 경우는 없지만, 그만큼 유저 자신에게 모든 관리 필요성이 요구됩니다. 블록체인을 활용하고 있는 것을 의식하지 않는 듯한 편리한 UI를 준비한 서비스가 보급되면 UX도 향상될 것이며, 자연스레 이용자가 늘 것이라고 생각하지만, 현시점에서는 일정한 응용력이 없으면 이용을 시작하는 것도 힘들다고 생각합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;서서히 퍼져가는 차세대 인터넷 &quot;Web3&quot;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2022년 현재는 Web2.0이 주류이지만, NFT나 메타버스의 인지도가 올라가고 Web3같은 블록체인 기술의 활용에 주목이 모이고 있습니다. 게임이나, 예술분야, 패션 업계등 Web3를 도입하는 기업은 늘고 있으며, 일상생활에서 Web3 서비스를 이용하는 장면은 이후에도 넓어져 갈 것이라고 예상됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web3</category>
      <category>NFT</category>
      <category>web</category>
      <category>Web2.0</category>
      <category>web3</category>
      <category>기술</category>
      <category>메타버스</category>
      <category>블록체인</category>
      <category>웹</category>
      <category>웹3</category>
      <category>프로그램</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/21</guid>
      <comments>https://kimkani.tistory.com/21#entry21comment</comments>
      <pubDate>Sun, 15 Jan 2023 15:17:18 +0900</pubDate>
    </item>
    <item>
      <title>[번역] 엘든링에서 배운 프로그래밍 학습에 필요한 7가지</title>
      <link>https://kimkani.tistory.com/19</link>
      <description>&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqBlJW/btrVTUcm0Sg/m6TefsRHEE1IfJ8YAQj5Fk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqBlJW/btrVTUcm0Sg/m6TefsRHEE1IfJ8YAQj5Fk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqBlJW/btrVTUcm0Sg/m6TefsRHEE1IfJ8YAQj5Fk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqBlJW%2FbtrVTUcm0Sg%2Fm6TefsRHEE1IfJ8YAQj5Fk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 글은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://qiita.com/karamage/items/d0b323ac0143a9f51ebb#comment-3684cf80803cd111fbc9&quot;&gt;https://qiita.com/karamage/items/d0b323ac0143a9f51ebb#comment-3684cf80803cd111fbc9&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;의 카라마게님의 글을 번역한 것입니다. 허락을 받고 번역했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엘든링의 요소가 많이 들어가 있으며, 참고정도로 봐주시면 좋을듯 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;인도의-시작&quot;&gt;인도의 시작&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아아...휴일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빛바랜 자는 엘든링에 부지런히 힘쓸 시간이지. 밤, 엘든링을 하고서, 할 맘을 잃었어...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 앞에, 프로그래밍 학습 공략과 이어지는 중요한 아이템이 7가 있다고. 굉장한 무언가라고 생각하겠지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 글을 봐주시게나!&lt;/p&gt;
&lt;h1 id=&quot;1-강한-적은-패스하고-나아가자&quot;&gt;1. 강한 적은 패스하고 나아가자&lt;/h1&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;793&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qjegR/btrVOyVUA0c/ux0S7NiMOtFRZekg0spwXK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qjegR/btrVOyVUA0c/ux0S7NiMOtFRZekg0spwXK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qjegR/btrVOyVUA0c/ux0S7NiMOtFRZekg0spwXK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqjegR%2FbtrVOyVUA0c%2Fux0S7NiMOtFRZekg0spwXK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;793&quot; height=&quot;800&quot; data-origin-width=&quot;793&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래밍 공부를 하고있으면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;초반에 트리가드같은 강적이 나타나는 경우&lt;/b&gt;가 있지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;패스하고 지나가는 것&lt;/b&gt;이 중요합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초반부터 강적과 싸우면 너덜너덜해지며 할 마음이 꺽이게 됩니다.&lt;/li&gt;
&lt;li&gt;#include &amp;lt;stdio.h&amp;gt;는, 이런 것이구나하고 &quot;주문&quot;처럼 패스합시다.&lt;/li&gt;
&lt;li&gt;var body: some View나 fn longest&amp;lt;`a&amp;gt; 등등, 익숙하지 않은 표기를 봐도 일단은 신경쓰지 말고, 앞으로 전진합시다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;힘 스탯 어느정도 오르고, 레벨이 올라가면 다시 싸워봅시다.&lt;/li&gt;
&lt;li&gt;멀기트처럼 &quot;&lt;b&gt;일반 진행 루트에서 막아서는 강적&lt;/b&gt;&quot; (C언어의 포인터나 Swift의 프로토콜, Rust의 소유권 등등)의 경우,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;일단은 후퇴하고 주변을 탐색&lt;/b&gt;하면서 자신의 레벨을 올리고 힘을 비축합시다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h1 id=&quot;2-자신의-스킬트리-타입을-정하자&quot;&gt;2. 자신의 스킬트리 타입을 정하자&lt;/h1&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciNeAd/btrVTl145GN/NRhGG92t7ViF3GGfFRbZFK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciNeAd/btrVTl145GN/NRhGG92t7ViF3GGfFRbZFK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciNeAd/btrVTl145GN/NRhGG92t7ViF3GGfFRbZFK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciNeAd%2FbtrVTl145GN%2FNRhGG92t7ViF3GGfFRbZFK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빠른 시일내에,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;자신이 목적으로 하는 엔지니어(개발자)는 어떤 타입인지&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정합시다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;근접형으로 대검을 붕붕 휘두르는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;근육돼지(프론트엔드)&lt;/b&gt;인지&lt;/li&gt;
&lt;li&gt;후방에서 강력한 마법으로 적을 유린하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;마도사(백엔드)&lt;/b&gt;인지&lt;/li&gt;
&lt;li&gt;마력으로 무기에 인챈트를 거는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;마법검사(인프라)&lt;/b&gt;를 목표로 하는지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모든 스킬에 균일하게 스택을 찍는 (풀스택)&lt;/b&gt;은 보기좋은 거지가 될 가능성이 높습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결정되었으면 자신의 스킬트리에 따라 해당 분야의 주변을 공략합시다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그 분야를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;공략하면 얻을 수 있는 것과 자신의 특성이 맞는지가 요구&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;li&gt;마도사인데 대검을 손에 넣으면 돼지목에 진주목걸이를 건거나 마찬가지입니다. (콜렉팅이 목적이면 그거 나름대로도 괜찮습니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도중에 스킬트리를 바꾸는 것은 가능하지만, 스탯을 다시 찍는 것은 불가능해 보입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스탯을 다시 찍는 것은 힘들기에 게임이 진행되면 진행될수록 스킬트리를 변경하는 것은 어렵습니다.&lt;/li&gt;
&lt;li&gt;정신적인 의미로 다시 태어나서, 물방울 유생을 손에 넣는다면 (집착을 버리면) 가능할지도 모릅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h1 id=&quot;3-일단-지도를-손에-넣는-것을-우선시하자&quot;&gt;3. 일단 지도를 손에 넣는 것을 우선시하자&lt;/h1&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;699&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UiCb9/btrVUxm75Z1/RyCvKV38k7j4SG0LRXAcRK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UiCb9/btrVUxm75Z1/RyCvKV38k7j4SG0LRXAcRK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UiCb9/btrVUxm75Z1/RyCvKV38k7j4SG0LRXAcRK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUiCb9%2FbtrVUxm75Z1%2FRyCvKV38k7j4SG0LRXAcRK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;699&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;699&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프로그래밍 학습은 오픈월드입니다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게임이 시작되면, 무지막지하게 넓으 평야에 던져집니다.&lt;/li&gt;
&lt;li&gt;자신의 호기심이나 목적에 따라 탐색할 장소를 정합시다.&lt;/li&gt;
&lt;li&gt;어디를 향해 전진하든 괜찮으며, 자신에게 맞지 않는 길이라고 생각되면, 다른 길을 선택하는 것도 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;배우고 싶은 목적지가 결정되었으면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;먼저 공식 도큐먼트나 입문서를 손에 넣어&lt;/b&gt;서, 대충 눈을 통해 그 지방의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;주변 전체 느낌을 파악&lt;/b&gt;합시다.&lt;/li&gt;
&lt;li&gt;축복의 인도(도큐먼트나 책의 목차)에 따라,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;일단 정규 루트로 전진&lt;/b&gt;해보는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;적이 강하다고 느껴진다면, 조금은 돌아가서 주변 탐색을 한다든지, 완전 다른 분야에 탐색하러 놀러가본다하면, 생각지 못한 보물을 발견할 수 있을지도 모릅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h1 id=&quot;4-1대-1-상황을-만들자&quot;&gt;4. 1대 1 상황을 만들자&lt;/h1&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHJFIh/btrVUq2RPcb/gQzDlMvRiFbcwyZsvZZwJK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHJFIh/btrVUq2RPcb/gQzDlMvRiFbcwyZsvZZwJK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHJFIh/btrVUq2RPcb/gQzDlMvRiFbcwyZsvZZwJK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHJFIh%2FbtrVUq2RPcb%2FgQzDlMvRiFbcwyZsvZZwJK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;800&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;복수의 적(모르는 내용)에 둘러쌓인다면, 일단 이길 수는 없습니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;록온 기능을 써서 하나의 적에 집중합시다.&lt;/li&gt;
&lt;li&gt;적(모르는 내용)은 하나하나 제대로 이해해서,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;개별 격파&lt;/b&gt;를 목적으로 합시다.&lt;/li&gt;
&lt;li&gt;근접으로 이길 수 없다면 떨어진 장소에서 원거리 공격을 시험해봅시다, 적의 움직임 패턴을 자주 보며 시행착오를 겪읍시다.&lt;/li&gt;
&lt;li&gt;먼저 떠나간 이들이 남긴 메시지나 영상을 참고하며 하는 법을 따라하고, 공략의 실마리를 손에 쥡시다.&lt;br /&gt;-개별 공격이 어려운 경우, 영혼 재나 흰색 사인을 사용하여,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;누군가에게 도움을 요청합시다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h1 id=&quot;5-실전에서-죽어가면서-몸에-익히자&quot;&gt;5. 실전에서 죽어가면서 몸에 익히자&lt;/h1&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;695&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z94ff/btrVSsG8hlC/LPgOIueqfrzNNMo7fYIwqK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z94ff/btrVSsG8hlC/LPgOIueqfrzNNMo7fYIwqK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z94ff/btrVSsG8hlC/LPgOIueqfrzNNMo7fYIwqK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz94ff%2FbtrVSsG8hlC%2FLPgOIueqfrzNNMo7fYIwqK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;695&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;695&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어찌되었든 손을 움직이면서, 코드를 쓰고 프로그램을 움직여 봅시다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로그래밍 학습은, 초보자용 함정이 만연&lt;/b&gt;해 있습니다.&lt;/li&gt;
&lt;li&gt;역으로 말하면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;과거에 그 패턴을 만났던 적이 있는지 없는지에 따라 승패가 결정&lt;/b&gt;된다고 볼 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;몇번이고 코드를 쓰며, 적의 공격(에러) 패턴을 몸에 녹여내봅시다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;코드를 쓰고 에러가 발생하면 대박인 겁니다.&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;그 에러가 당신을 강하게 해줍니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h1 id=&quot;6-장비-강화를-하는-편이-효과적&quot;&gt;6. 장비 강화를 하는 편이 효과적&lt;/h1&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b63Fsp/btrVT4Z4nph/c6e8i2p9ZBsdqtpb8FBpZ0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b63Fsp/btrVT4Z4nph/c6e8i2p9ZBsdqtpb8FBpZ0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b63Fsp/btrVT4Z4nph/c6e8i2p9ZBsdqtpb8FBpZ0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb63Fsp%2FbtrVT4Z4nph%2Fc6e8i2p9ZBsdqtpb8FBpZ0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;628&quot; height=&quot;800&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;자신의 능력치를 올리기보다도 무기나 탈리스만, 영혼 재 등의 장비를 강화하는 것이 화력을 올리는데에 효과적인 경우&lt;/b&gt;가 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴퓨터의 스펙 업&lt;/li&gt;
&lt;li&gt;넓은 책상, 모니터, 앉기 좋은 의자, 타건하기 좋은 키보드를 준비한다든지,&lt;/li&gt;
&lt;li&gt;에디터나 환경설정을 다시 본다든지&lt;/li&gt;
&lt;li&gt;최신 프레임워크나 라이브러리 툴을 도입한다든지&lt;/li&gt;
&lt;li&gt;AWS의 강력한 인스턴스에 돈다발을 붓는다든지&lt;/li&gt;
&lt;li&gt;전문서를 사서 지식을 주입한다든지&lt;/li&gt;
&lt;li&gt;스터디나 커뮤니티에 참여해 최신 기술 트렌드나 인맥을 넓힌다든지&lt;/li&gt;
&lt;li&gt;잘나가는 회사에 이직해서 거대한 룬의 능력치 부스터를 손에 넣는다든지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;장비 강화로 전투가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;한번에 쉬워지는 것은 아닙니다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;다만, 무기성능에 너무 기대는 것도 금물, 그 무기가 업데이트로 너프먹을 경우 사서 고생하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h1 id=&quot;7-룬을-잃는-것을-겁먹지-말자&quot;&gt;7. 룬을 잃는 것을 겁먹지 말자&lt;/h1&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kG6Bz/btrVP988tRJ/MXUz0mM7SuPTKVPGwYpKEK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kG6Bz/btrVP988tRJ/MXUz0mM7SuPTKVPGwYpKEK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kG6Bz/btrVP988tRJ/MXUz0mM7SuPTKVPGwYpKEK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkG6Bz%2FbtrVP988tRJ%2FMXUz0mM7SuPTKVPGwYpKEK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;616&quot; height=&quot;800&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현실에서는 정말로 죽지않는 한, 경험치를 잃지 않습니다.&lt;/li&gt;
&lt;li&gt;무서워하지 말고 싸우는 것이 이득입니다.&lt;/li&gt;
&lt;li&gt;다만, 싸움에서 계속 지고 있다면, 뇌사 상태로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;경험치를 쌓아도 영원히 발목을 붙잡힐 수 있습니다&lt;/b&gt;.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;왜 에러가 일어나지?&quot;&lt;/li&gt;
&lt;li&gt;&quot;어느 코드가 좋지 않지?&quot;&lt;/li&gt;
&lt;li&gt;&quot;장비나 빌드 특성이 맞지 않나?&quot;&lt;/li&gt;
&lt;li&gt;&quot;멘탈이 좋지 않아. 감정에 패배한 것인가?&quot;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;등을 생각하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;실패를 다시 만드는 것&lt;/b&gt;이 중요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;싸움에 져서 별꼴을 다보여도 괜찮지 않을까요?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드를 쓰는 것은 실패의 연속입니다.&lt;/li&gt;
&lt;li&gt;하나하나 실패하는 것을 무서워하면 무엇도 작성할 수 없게 됩니다.&lt;/li&gt;
&lt;li&gt;코드를 작성함에 있어 중요한 것은 &quot;정답&quot;을 발견하는 것이 아닙니다. (절대적인 정답은 없으니까요)&lt;/li&gt;
&lt;li&gt;시행착오를 반복하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;실패경험으로부터 빠르게 배울 수 있는 유연성과 끈기&lt;/b&gt;를 단련하는 것이 중요합니다.&lt;/li&gt;
&lt;li&gt;저는 도전하는 분들을 존경합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h1 id=&quot;엘데의-왕이-되어라&quot;&gt;엘데의 왕이 되어라&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막까지 읽어주셔서, 아마도 감사합니다.&lt;br /&gt;이 앞에, 엘데의 세계에는, 점프가 유효해.&lt;br /&gt;프롬 만세!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>ETC</category>
      <category>개발</category>
      <category>개발자</category>
      <category>개발태도</category>
      <category>번역</category>
      <category>엘든링</category>
      <category>일본어</category>
      <author>Kani Kim</author>
      <guid isPermaLink="true">https://kimkani.tistory.com/19</guid>
      <comments>https://kimkani.tistory.com/19#entry19comment</comments>
      <pubDate>Tue, 10 Jan 2023 15:08:51 +0900</pubDate>
    </item>
  </channel>
</rss>