<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>junyCoding</title>
    <link>https://codingjuny.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 4 Jul 2026 17:30:09 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>o늘do</managingEditor>
    <item>
      <title>Spring Boot,  Custom Annotation 만들기</title>
      <link>https://codingjuny.tistory.com/63</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스프링에서 개발을 하다 보면 요청, 응답 dto 값에 validation을 적용하는 경우가 많다.&lt;br /&gt;예를 들어 Request 요청이 다음과 같고 내부 필드에는 title 이 존재할 때 title의 null 여부와 size에 대해서 유효성 검사를 추가하고 싶은 경우가 존재한다. title 은 null 이 아니었으면 좋겠고 길이는 1 ~ 255 자 사이였으면 좋겠다.&lt;br /&gt;&lt;b&gt;다행히도 스프링은 기본적으로 다양한 유효성을 검사해주는 어노테이션을 제공한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class Request {
    // title 은 null 이 아니여야하고 길이는 1 ~ 255 자 사이여야한다. 그렇지 않으면 validation error 를 발생시키겠다. 
    @NotNull()
    @Size(min = 1, max = 255)
    private String title;

}&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;b&gt;만약에 위 Request 필드에 추가로 emails라는 데이터 필드를 추가해야 하는 경우가 생겼고 데이터의 조건은 아래와 같다고 생각해보자&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;1. emails 는 email 형식의 데이터가 들어오는데 email 이 , 로 구분되어 들어오는 형태여야한다. 
2. , 로 구분된 이메일은 유효한 이메일 형식이여야한다.
ex) &quot;yhy7132@naver.com,dgd@google.com,junjun@naver.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 필드에 대해서 올바르게 email 이 요청으로 들어오는지를 validation 해주는 어노테이션은 없다(적어도 내가 찾기에는)&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;위와 같은 경우에는 일단 요청을 받고 비즈니스 로직에서 처리를 해줄 수 도 있으나 anotation을 이용하면 비즈니스 로직과 분리도 가능하고 조건에 변경이 생겼을 때 해당 어노테이션 부분만 수정하면 되기 때문에 매우 깔끔하게 코드를 작성할 수 있다.&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; 그럼 위에서 말한 emails 필드에 조건을 검사하는 커스텀한 어노테이션을 만들어 보자. 첫 번째는 어노테이션을 정의하는 인터페이스 클래스이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;monkey&quot;&gt;&lt;code&gt;@Target(ElementType.FIELD)
@Constraint(validatedBy = IsAllEmailValidator.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface IsAllEmail {
    String message() default &quot;recvAddrs are required and email format&quot;;
    Class&amp;lt;?&amp;gt;[] groups() default {};
    Class&amp;lt;? extends Payload&amp;gt;[] payload() default {};
}&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;@Target(ElementType.FIELD): 적용하려는 타입은 Request 객체의 emails 필드(변수)이기때문에 target을 field로 설정한다.&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;@Constraint(validatedBy = IsAllEmailValidator.class): 실제로 해당 어노테이션으로 필드를 어떻게 검증할 건지에 대한 대한 클래스를 선언한다. IsAllEmailValidator이고 밑에서 설명한다.&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;@Retention(RetentionPolicy.RUNTIME): 어플리케이션 실행 시점 동안만 해당 어노테이션이 유효하다는 설정으로 RUNTIME을 지정해준다.&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;String message() default &quot;recvAddrs are required and email format&quot;: 기본적으로 해당 어노테이션으로 필드를 검증하고 통과하지 못했을 때 표시해주는 default 메시지이다.&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;그다음은 @Constraint(validatedBy = IsAllEmailValidator.class)에서 검증 로직이 들어갈 클래스인 IsAllEmailValidator를 생성한다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class IsAllEmailValidator implements ConstraintValidator&amp;lt;IsAllEmail,String&amp;gt; {

    public static final String EMAIL_REGEX = &quot;[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-.]?[0-9a-zA-Z])*\\.[a-zA-Z]{2,3}$&quot;;
    public static final Integer MAX_EMAIL_LENGTH = 255;

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {

        if(value == null || value == &quot;&quot;)
            return false;

        List&amp;lt;String&amp;gt; emails = Arrays.asList(value.split(&quot;;&quot;));
        if(emails.isEmpty()) return false;

        return emails.stream()
                .allMatch((email) -&amp;gt; Pattern.matches(EMAIL_REGEX, email) &amp;amp;&amp;amp; email.length() &amp;lt;= MAX_EMAIL_LENGTH);

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IsAllEmailValidator 클래스는 ConstraintValidator 클래스를 상속받게 되고 제네릭의 첫 번째 인자로는 어노테이션, 두 번째 인자로는 string을 지정해준다. string 을 지정하는 이유는 적용하려는 emails 필드가 string 타입이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;isValid 함수를 Override 해서 검증 로직을 구현한다. 첫 번째 필드인 value 가 해당 어노테이션을 추가한 필드 값이 들어오게 된다. 위에 예시에서는 emails 필드가 된다. 내부 로직은 value 생략한다 대충 해당 어노테이션이 적용된 string 필드는 , 로 구문 된 문자열이고 , 로 구문된 문자열은 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;&lt;b&gt;적용&lt;/b&gt;&lt;br /&gt;위에서 생성한 IsAllEmail 어노테이션을 적용하고자 하는 필드에 붙여주기만 하면 된다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;public class Request {
    // title 은 null 이 아니여야하고 길이는 1 ~ 255 자 사이여야한다. 그렇지 않으면 validation error 를 발생시키겠다. 
    @NotNull()
    @Size(min = 1, max = 255)
    private String title;

    @IsAllEmail() 
    private String recvAddrs;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>스프링</category>
      <category>스프링</category>
      <category>어노테이션</category>
      <category>커스텀</category>
      <author>o늘do</author>
      <guid isPermaLink="true">https://codingjuny.tistory.com/63</guid>
      <comments>https://codingjuny.tistory.com/63#entry63comment</comments>
      <pubDate>Tue, 25 Jan 2022 20:54:51 +0900</pubDate>
    </item>
    <item>
      <title>스프링 IOC, Bean</title>
      <link>https://codingjuny.tistory.com/62</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;IOC 란 ?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 보자&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;@Controller
public class OrderController {

    private OrderService orderService = new OrderService();

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 OrderController 라는 클래스에서 OrderService 객체를 new 키워드로 생성해서 사용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 경우는 직접 OrderService 라는 의존성을 관리 하고 있는 상태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 보자.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Controller
public class OrderController {

    private OrderService orderService;

        public Controller(OrderService orderService){
            this.orderService = orderService;
        }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 OrderController 클래스가 OrderService를 사용하지만 집접 객체를 생성하지 않고 누군가가 Controller 밖에서 생성자를 통해서 받아오는 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;더이상 OrderController 가 OrderService 라는 의존성을 생성하지 않고 OrderController의 제어권이 아니다. 그렇기 때문에 제어권이 역전 되었다고 하는 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이렇게 의존성을 주입하는것을 DI 라고 하는데 DI 또한 일종의 IOC 이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;IOC Container 란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;일단 여기서 Container 란 무엇일까?&lt;/b&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;&lt;b&gt;즉 IOC Container 는 객체에 대한 생성 및 생명주기를 관리한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IOC Container 는 대표적으로 두가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Application Context&lt;/li&gt;
&lt;li&gt;BeanFactory&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;914&quot; data-origin-height=&quot;75&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckBkdk/btq8KQ2pNug/keVou6Oa7ajyvAG0VzQzG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckBkdk/btq8KQ2pNug/keVou6Oa7ajyvAG0VzQzG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckBkdk/btq8KQ2pNug/keVou6Oa7ajyvAG0VzQzG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckBkdk%2Fbtq8KQ2pNug%2FkeVou6Oa7ajyvAG0VzQzG1%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;914&quot; height=&quot;75&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;75&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BeanFactory 를 사용해도 되지만 아래를 보면 Application Context 는 BeanFactory 를 상속받고 있기 때문에 더 다양한 기능을 가지고 있어서 IOC 컨테이너는 Application Context 를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Bean?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 IOC Container 는 객체를 관리한다고 했는데 여기서 이 객체를 Bean 이라고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Bean은 Application Context 가 관리하는 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Bean 으로 등록하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;ol 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;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방법들이 있고 Bean 으로 등록하게 되면 Ioc 컨테이너가 의존성을 주입해주게 된다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Controller
public class OrderController {

    private OrderService orderService;

        public Controller(OrderService orderService){
            this.orderService = orderService;
        }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드도 @Controller 어노테이션으로 빈으로 등록하게 되면 IOC Container 가 OrderService(**역시 빈으로 등록되어있음**) 객체를 찾아서 의존성을 주입하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;OrderService 가 역시 빈으로 등록되어있다&lt;/b&gt; 라는 말은 보편적으로 IOC Container에 빈으로 등록된 객체들 끼리만 의존성을 주입받을 수 있다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 IOC Container 에 있는 Bean들을 가져오는 방법을 제공하지만 IOC Container 밖에 있는 객체에는 의존성 주입이 가능하지만 지양하는 편이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 직접 applicationContext 이 관리하는 빈을 확인 할수 있다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Controller
public class OrderController {

    private OrderService orderService;
        private ApplicationContext applicationContext

        public Controller(OrderService orderService, ApplicationContext applicationContext){
            this.orderService = orderService;
        }

    @GetMapping(&quot;/bean&quot;) 
    @ResponseBody 
    public String bean() { 
    return &quot;bean : &quot; + applicationContext.getBean(OrderService.class)  
     + &quot;&quot; + &quot;orderService : &quot; + this.orderService; 
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;applicationContext.getBean(OrderService.class) 와 this.orderService 를 비교해보면&lt;br /&gt;같은 해시코드를 가지게된다. 즉 같은 객체라는 말이다. 이러한 형태를 Singleton Scope의 객체라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring은 Bean을 생성할 때 기본적으로 모든 &lt;b&gt;Bean을 Singleton으로 생성하여 관리하게된다.&lt;/b&gt;&lt;br /&gt;SIngleton Bean은 Spring 컨테이너에서 한 번 생성되며, 컨테이너가 사라질 때 같이 제거되며,객체 하나를 Application 전반에서 계속 재사용하게 된다.&lt;/p&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;&lt;b&gt;그렇다면 단점, 문제점은?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brxer1/btq8Kblr74l/GojfjokoqGe22y1miKb2kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brxer1/btq8Kblr74l/GojfjokoqGe22y1miKb2kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brxer1/btq8Kblr74l/GojfjokoqGe22y1miKb2kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbrxer1%2Fbtq8Kblr74l%2FGojfjokoqGe22y1miKb2kk%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;604&quot; height=&quot;445&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;445&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;다음과 같은 코드가 있다고 하자&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;package com.example.productprj.study.java;

import java.util.stream.IntStream;

class Singleton {

        // count 공유자원, 전역변수
    private int count; 

    private static Singleton singleton = new Singleton(); // static 초기화시 바로 할당

    private Singleton() {
    }

    public static Singleton getInstance() {
        return singleton;
    }

    public int add() {
        return ++count;
    }

}

public class Test {

    public static void main(String[] args) {

        Singleton singleton = Singleton.getInstance();

        IntStream.range(1, 15)
                .forEach(n -&amp;gt; new Thread(() -&amp;gt; {
                            try {
                                Thread.sleep(1000);
                                System.out.println(singleton.add());
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }).start()
                );

    }

}
## 결과
1
1
2
4
3
5
9
8
7
6
10
13
12
11&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 1 ~ 14 가 나오길 예상했지만 1 이 두번 중복되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 count 변수는 공유자원이다. 좀더 자세히 말하면 각 스레드들은 stack 영역은 각자 스레드 마다 고유하지만 힙 영역(전역변수가 저장되는)은 공유되기 때문에 위와 같이 데이터 조작에 문제가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결론은 스프링빈은 싱글톤으로 관리되기 때문에 멀티 스레드 환경에서 Thread-safe 하지 못하다.&lt;/b&gt; 내가 코드를 짤때 보통의 경우에 스프링빈에서 불변객체를 사용해서 이러한 동시성 문제가 발생하지 않은 것 뿐이였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 스프링 빈사이에서 데이터를 조작할때 메소드의 파라미터(지역변수)를 사용해서 Thread-safe 한것 뿐이였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 코드를 짤때 스프링 빈에서 공유변수를 사용하게 되면 synchronized 키워드나 다른 방법을 사용해서 Thread-safe 한 코드를 짜도록 해야겠다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bean&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 중에서 Spring IoC Container가 관리하는 객체를 Bean 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스프링의 Bean 등록은 ComponentScan, EnableAutoConfiguration 두단계로 이루어져 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Boot Project 에서 빈을 찾아 등록하는 과정(ComponentScan)&lt;/b&gt;&lt;/p&gt;
&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;245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpAt5r/btq8GnIb6Ax/Ra5YrPgU4ExWE2K6IaAd31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpAt5r/btq8GnIb6Ax/Ra5YrPgU4ExWE2K6IaAd31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpAt5r/btq8GnIb6Ax/Ra5YrPgU4ExWE2K6IaAd31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpAt5r%2Fbtq8GnIb6Ax%2FRa5YrPgU4ExWE2K6IaAd31%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;245&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;245&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 사진과 같이 @SpringBootApplication 어노 테이션 내부에는 @ComponentScan이라는 Annotation 이 적용되어 있다. @ComponentScan 어노테이션의 설정값에 따라 ComponentScan 이 동작하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 SpringBootApplication 과 같은 레벨에(같은 패키지 레벨 혹은 하위) 있는 클래스 들중에서 @ComponentScan 으로 Component 어노테이션이 붙어있는 클래스들을 찾아 IOC Container 가 관리하는 빈으로 등록하게 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 @Repository, @Service @Configuration, @Controller 가 붙은 클래스들도 빈으로 등록되는 이유는 위 해당 어노테이션 내부에 @Component 이 붙어있기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ngCsm/btq8KYlPRiq/IByrXd2W3BnyVZjjozE6c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ngCsm/btq8KYlPRiq/IByrXd2W3BnyVZjjozE6c1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ngCsm/btq8KYlPRiq/IByrXd2W3BnyVZjjozE6c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FngCsm%2Fbtq8KYlPRiq%2FIByrXd2W3BnyVZjjozE6c1%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;1526&quot; height=&quot;672&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 빈등록은 @ComponentScan 이후에 @EnableAutoConfiguration 으로 등록되는 과정도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 spring.factories 라는 파일에 EnabledConfiguration 이라는 이름으로 여러 클래스들이 선언되어 있는데 @EnableAutoConfiguration 과정에서 빈으로 등록되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;++ &lt;b&gt;@ComponentScan 어노테이션의 설정값에 따라 ComponentScan 이 동작하게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제를 보면 &lt;b&gt;@ConditionalOnMissingBean&lt;/b&gt; 어노테이션이 붙어 있다. 이 어노테이션의 역활은 SpringApplicationAdminMXBeanRegistrar 빈으로 등록되어 있지 않을 경우에만&lt;br /&gt;빈으로 등록하라는 뜻이다.(예를 들어 이름이 같은 경우가 발생할 수 있으니까)&lt;br /&gt;이외에도 많은 설정값이 있다 필요할때 찾아보자&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;  @Bean
    @ConditionalOnMissingBean
    public SpringApplicationAdminMXBeanRegistrar springApplicationAdminRegistrar(
            ObjectProvider&amp;lt;MBeanExporter&amp;gt; mbeanExporters, Environment environment) throws MalformedObjectNameException {
        String jmxName = environment.getProperty(JMX_NAME_PROPERTY, DEFAULT_JMX_NAME);
        if (mbeanExporters != null) { // Make sure to not register that MBean twice
            for (MBeanExporter mbeanExporter : mbeanExporters) {
                mbeanExporter.addExcludedBean(jmxName);
            }
        }
        return new SpringApplicationAdminMXBeanRegistrar(jmxName);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 개발을 하다보면 모든 클래스를 빈으로 등록하지 않아도 되는 경우가 있다.&lt;br /&gt;예를 들어 배치를 돌릴때 특정 잡을 수행하는 클래스만 빈으로 등록하고 해당 잡을 수행하지 않는 클래스들은 불필요하게 빈으로 등록하지 않아도 되는 경우가 있을 것 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**이럴 때사용할 수 있는 어노테이션이 @ConditionalOnExpression 이다.**&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 보자&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;package com.example.productprj.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnExpression(value = &quot;'${spring.application.name}' == 'hakjun'&quot;)
public class Hakjun {

    public String name(){
        return &quot;hakjun&quot;;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hakjun 이라는 클래스를 만들었다. @Configuration 을 통해서 컴포넌트 스캔의 대상이 되도록 했지만(일반 적인 경우에는 빈으로 등록) @ConditionalOnExpression 을 value 값을 주어 프로그램 인자가 &lt;a href=&quot;http://spring.application.name&quot;&gt;spring.application.name&lt;/a&gt;=hakjun 일경우에만 빈으로 등록되게 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 프로그램 인자값에 다음과 아래와 같이 설정을 하고 어플리케이션을 구동을 하면 아래 ApplicationRunner 코드의 run 함수가 정상적으로 작동할 것이고 아래와 값이 결과값이 나올것이다.&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;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czWHhK/btq8HUSu7Q6/iwDWaYu9PMMs2d36xM9gyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czWHhK/btq8HUSu7Q6/iwDWaYu9PMMs2d36xM9gyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czWHhK/btq8HUSu7Q6/iwDWaYu9PMMs2d36xM9gyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczWHhK%2Fbtq8HUSu7Q6%2FiwDWaYu9PMMs2d36xM9gyK%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;171&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;171&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 class=&quot;asciidoc&quot;&gt;&lt;code&gt;hakjun
==============
spring boot run!
==============&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 프로그램 인자값에 올바른 값을 넣어주지 않는 다면 Hakjun 클래스는 컴포넌트 스캔단계에서 빈으로 등록되지않고 ApplicationRunner 에서 Hakjun 은 빈이아니기 때문에 의존성 주입을 받지 못해 다음과 같은 에러와 함께 어플리션이 종료되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과)&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Consider defining a bean of type 'com.example.productprj.config.Hakjun' in your configuration.&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;package com.example.productprj.config;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class AppRunner implements ApplicationRunner {

    private final Hakjun hakjun;

    public AppRunner(Hakjun hakjun) {
        this.hakjun = hakjun;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(hakjun.name());
        System.out.println(&quot;==============&quot;);
        System.out.println(&quot;Spring Boot Runner!&quot;);
        System.out.println(&quot;==============&quot;);
    }
}
&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;&lt;a href=&quot;http://wonwoo.ml/index.php/post/20&quot;&gt;http://wonwoo.ml/index.php/post/20&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DI (Dependency Injection)&lt;/h3&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;&lt;b&gt;생성자로 주입하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Repository
public class ItemRepository {
    private final EntityManager em; 

        @Autowired // 최근 버전부터는 하나의 생성자에 대해서는
                                // 생략이 가능하다.(EntityManager 가 빈으로 등록되어 있어야한다.)
    public ItemRepository(EntityManager em){
        this.em = em;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;final 로 선언하여 런타임에 의존성을 주입받는 객체가 변하지 않음을 보장할 수 있고&lt;br /&gt;final 로 선언한 생성자 주입방식은 null 이 올수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**필드로 바로 주입받는 방법**&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;@Repository
public class ItemRepository {

    @Autowired
    private EntityManager em;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;setter 를 사용하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Repository
public class ItemRepository {
    private EntityManager em;

    @Autowired
    public void setEntityManager(EntityManager em){
        this.em = em;
    }

}&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;생성자로 의존성을 주입하는 것이 좋은이유는 필수적으로 사용해야하는 참조값 없이는 해당 클래스를 만들수 없도록 강제할 수 있다. 좀더 안정성있는 프로그래밍이 가능해진다. ****&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서에 안정성은 객체가 생성되는 시점에 빈을 주입하기 때문에 의존성이 주입되지 않아 발생 할 수 있는 NullPointerException 을 방지하게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이와 같은 경우에는 순환 참조가 발생할 수 있다. 이때는 필드 의존성 주입이나, setter 의존성 주입을 사용하도록 해결이 가능하다. (필드 의존성 주입도 충분이 순환 참조가 발생할 수 있다..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어찌됐든 순환 참조가 발생하지 않도록 설계하는 것이 좋다. (이부분은 좀더 알아보자.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사실 DI 를 주입 받는 순서, 시점이 다르다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드로 주입받는 경우와, setter 로 의존성 주입을 받는 경우에는 런타임에 의존성 주입을 하기 때문에 의존성 주입을 하지 않아도 객체가 생성될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 생성자로 주입하는 경우에는 객체가 생성되는 시점에 빈을 주입하기 때문에 의존성이 주입되지 않아 발생할 수 있는 NullPointerException 을 방지한다.&lt;/p&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;테스트하려는 클래스가 필드 주입을 받을 때 외부에서 빈을 주입해 줄 수 없기 때문에 해당 필드는 null 값이 된다. 따라서 스프링 빈 및 모든 설정을 가져오고 실행해야 테스트가 가능하다.&lt;br /&gt;하지만 생성자로 의존성을 주입하게 되면 테스트 코드 자체에서 필요한 의존관계를 만들어서 테스트가 가능하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 개발시에는 lombok 을 활용하는 좋은 방법도 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@RequiredArgsConstructor 을 통해 간단하게 생성자 주입 방식을 사용할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Repository
@RequiredArgsConstructor
public class ItemRepository {
    private final EntityManager em;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>스프링</category>
      <category>스프링#IOC#Bean</category>
      <author>o늘do</author>
      <guid isPermaLink="true">https://codingjuny.tistory.com/62</guid>
      <comments>https://codingjuny.tistory.com/62#entry62comment</comments>
      <pubDate>Mon, 17 Jan 2022 21:30:25 +0900</pubDate>
    </item>
    <item>
      <title>실행컨텍스트</title>
      <link>https://codingjuny.tistory.com/61</link>
      <description>&lt;h2&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; font-size: 16px; letter-spacing: 0px;&quot;&gt;&amp;nbsp;자바스크립트의 호이 스팅, 클로저, this에 대한 이해를 하기 위해서는 실행 콘텍스트에 대한 이해가 매우 중요하다. 그럼 실행 콘텍스트란 무엇일까?&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실행 컨텍스트?&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;실행 가능한 코드가 실행되기 위해 필요한 환경이다.&lt;/b&gt; 여기서 실행 가능한 코드는 전역 코드나 함수 내 코드를 말한다. 결국 코드를 실행하기 위해 JS 엔진은 코드 실행에 필요한 변수나, 함수, 변수의 유효 범위들을 알고 있어야 하기 때문에 실행 컨텍스트가 필요하게 되는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그럼 실행 컨텍스트는 어떻게 이루어져 있을까? &lt;b&gt;실행컨텍스트는 3가지 객체로 이루어져 있다. 하나씩&lt;/b&gt; 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;첫 번째는 변수 객체이다. 변수 객체는 JS 실행에 필요한 변수, 함수 선언, 함수에 인자로 전달되는 매개변수의 인수를 담는 객체이다. 이때 전역 컨텍스트의 경우에는 전역 객체를 가리키고 함수컨텍스트의 경우 활성 객체를 가리키게 된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;두 번째는 스코프 체인이다. 일종의 리스트로서 해당컨텍스트가 참조할 수 있는 변수 등의 정보를 갖고 전역 객체 또는 활성 객체이다. 클로저가 발생하는 이유를 설명할 수 있는 중요한 실행 컨텍스트의 구성 요소이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;마지막은 this이다. this는 일반적으로 메서드를 호출한 객체가 저장되어있는 속성이지만. 함수를 호출하는 방식에 의해서 결정되기도 한다. 나중에 더 자세히 알아보기로 하자.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;지금까지 실행 컨텍스트가 먼지? 어떻게 이루어져 있는지 간단하게 알아보았다. &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;그렇다면 실제로 코드로 확인하기 전에 몇까지 실행 컨텍스트의 특징들을 알아보자.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;첫 번째는 실행컨텍스트는 실행컨텍스트 스택에 차례대로 쌓이게 된다. 항상 전역 &lt;b&gt;컨&lt;/b&gt;텍스트가 먼저 쌓이게 되고 이후에 함수가 호출될 때마가 새로운 함수컨텍스트가 스택에 쌓이게 된다. 그리고 호출된 함수가 완료되면 컨텍스트에서 사라지게 된다(LIFO 구조)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;두 번째는 실행컨텍스트에 쌓이게 되면 3가지 작업이 순서대로 일어난다. 이미 위에서 설명한 것이다. 아래의 3가지 작업은 실행컨텍스트가 쌓일 때마다! 일어나는 것이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스코프 체인의 생성 및 초기화가 일어난다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;변수 객체화가 실행된다. 쉽게 말하면 해당 컨텍스트에 속하는 변수, 함수들이 변수 객체 내에 선언되는 것이다.(이는 또 호이 스팅과 관련이 있다. 참 실행컨텍스트가 중요하다는 생각이 든다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;this value를 결정한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;세 번째는 실행컨텍스트가 스택에 쌓이고 위에서 두 번째 단계에서 말한 3가지 작업들이 실행한다고 실제로 코드가 실행되는 것이 아니다.&lt;/b&gt; 코드가 올바르게 규칙에 맞게 실행될 수 있도록 실행환경을 세팅하는 과정 정도로 생각하면 좋을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이제 드디어 코드를 보자!.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;var x = 'xxx';

function foo () {
  var y = 'yyy';

  function bar () {
    var z = 'zzz';
    console.log(x + y + z);
  }
  bar();
}
foo();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;코드를 작성한 이후에 초기 작업은 항상 공통적으로 일어나게 된다. 컨트롤이 실행 컨텍스트에 진입하기 &lt;b&gt;전에 유일한 전역 객체가 생성된다.&lt;/b&gt; &lt;b&gt;전역 객체가 생성된 이후에 전역 코드로 들어서게 되면 전역 컨텍스트가 생성되고 실행 콘텍스트가 스택에 쌓이게 된다.&lt;/b&gt;(아까 말했듯이 변수 객체에서 전역 컨택스트의 변수 객체는 전역 객체가 되고 함수 콘텍스트의 변수 객체는 활성화 객체가 된다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/7gjPFbN.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자 이제 실행 컨택스트가 스택에 쌓였다. 무슨 일이 일어나게 될까? 바로 위에서 말한 세 가지 작업이 실행되게 되는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;첫 번째로 스코프 체인의 생성과 초기화가 이루어진다.&lt;/b&gt; 전역 콘텍스트의 스코프 체인의 경우에는 전역 객체를 가리키게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/BxvGXQF.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;두 번째로는 변수 객체화가 실행이 된다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자세히 말하자면 실행 콘텍스트의 변수 객체가 전역 객체를 가리키고 전역 객체에 인수, 함수, 변수들을 등록하게 된다. 이때 항상 순서는 함수의 매개변수, 함수, 변수 순으로 설정되는데 이는 함수 호이 스팅과 변수 호이 스팅이 일어나게 된다. 이는 나중에 자세히 알아보자. 자 이제 다시 코드로 돌아와서 확인해 보자&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;var x = 'xxx';

function foo () {
  ....
}
foo();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;전역에 선언된 변수, 함수인 x, foo()가 있다. 아까 말했듯이 전역 객체(변수 객체)에 함수가 먼저 선언이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/KDU97ke.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위와 같이 생성된 함수 객체는 scopes라는 속성을 가지게 되는데 이는 현재 실행 콘텍스트의 스코프 체인을 가리키게 된다. 나중에 콘텍스트에 존재하지 않는 변수가 있을 경우 이스 코프 체인을 통해서 해당 변수나 함수를 찾아나 갈 수 있는 것이다.(이는 클로저와 관련이 있다 나중에 알아보기로 하자.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/H9UsjaO.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이제 함수 선언이 끝났기 때문에 변수 선언이 이루어지게 된다. 코드가 실행되어 메모리에 실제 값이 &lt;b&gt;할당되지는 변수 객체화 단계에서는 않고 변수 x를 선언하고 초기화하는 작업만 진행되게 된다.&lt;/b&gt; 실제로 코드가 실행되어질 때 변수 x에 실제 값이 할당되게 되는 것이다. &lt;b&gt;중요한 점은 스코프 체인은 함수가 선언된 위치 기준으로 설정되는 것이다. 나중에 문제를 통해서 확인하자.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/E73WArV.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;세 번째로 this 객체가 설정된다.&lt;/b&gt; this 객체는 함수가 호출되는 패턴에 따라 달라지게 되는데 이는 나중에 자세히 알아보자. 참고로 전역 콘텍스트의 변수 객체, 스코프 체인, this는 모두 전역 객체를 가리키게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이제 전역 코드 공간에 들어와서 실제로 코드가 실행되기 위한 준비는 끝났다(&lt;b&gt;하지만 함수가 호출이 되어 새로운 함수 실행 콘텍스트가 쌓이게 되면 위 과정이 반복된다.&lt;/b&gt;).&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;var x = 'xxx'; =&amp;gt; 변수 x 에 값 등록...!

function foo () {
  var y = 'yyy';

  function bar () {
    var z = 'zzz';
    console.log(x + y + z);
  }
  bar();
}
foo(); =&amp;gt; 호출!! 함수실행 컨텍스트 생성 스택에 쌓자!!!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자 이제 코드가 실행되고 아까 전역 콘텍스트의 변수 객체인 전역 객체에서 선언되고 초기화만 되었던 변수 x에 실제 값이 할당되게 된다. 아래와 같이 말이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/cyD4ohM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다음 코드의 흐름을 보면 함수 foo()를 실행하게 된다. 이때 foo 함수에 대한 실행 콘텍스트가 생성이 되고 스택에 쌓이게 된다. 바로 위에서 말한 스코프 체인 초기화, 변수 객체화, this 설정이 일어나게 된다. 즉 js 엔진은 실제 코드 실행 이전에 함수 내 코드를 수행하기 위한 환경을 세팅하게 된다. 이후에 상세한 과정은 생략하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/XEKtCjM.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;함수 호출 이후에 3가지 작업이 일어나게 되고 다음과 같은 모습이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이제 함수 foo의 실행 콘텍스트 생성 작업이 끝났으니 함수 내 코드를 실행하게 된다. 변수 y를 할당하게 되고 bar()를 만난다. 그럼 다시 함수 bar를 실행하기 위한 실행 콘텍스트가 생성된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;var x = 'xxx'; 

function foo () {
  var y = 'yyy';

  function bar () {
    var z = 'zzz';
    console.log(x + y + z);
  }
  bar();
}
foo(); &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/SjjCKeE.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위와 같이 함수 bar 대한 실행 콘텍스트가 생성이 된 이후에 코드가 실행이 된다. console.log(x + y + z); 가 실행이 되는데 현재 함수 bar에 콘텍스트에는 x라는 변수가 없다. 그렇기 때문에 스코프 체인을 통해서 상위 변수 객체(함수면 활성 객체, 전역일 때는 전역 객체) 탐색을 시작한다. 그림과 같이 bar의 스코프 체인은 자신의 함수의 활성화 객체 =&amp;gt; 상위 함수의 활성화 객체(foo) =&amp;gt; 전역 객체 순으로 연결되어있고 이과정에서 자신에 스코프에는 존재하지 않던 x와 y를 찾아서 접근할 수 있게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;var x = 'xxx'; 

function foo () {
  var y = 'yyy';

  function bar () {
    var z = 'zzz';
    console.log(x + y + z);  
  }
  bar();
}
foo(); 

=&amp;gt; xxxyyyzzz 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그 결과 console.log(x + y + z); 의 결괏값이 출력되고 함수 bar의 호출이 끝났으니 실행 콘텍스트 스택에서 bar의 실행 콘텍스트가 사라지게 되고 foo 도 마찬가지로 사라지게 된다.(스택이니까 LIFO 구조)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;문제!&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;var color = 'black'
function bar (text) {
  console.log(text + color)
}
function foo () {
  var color = 'white'
  bar('color is ')
}
foo() &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위와 같은 코드의 출력 결과는 어떻게 될까?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;먼저 전역 객체와 전역 콘텍스트가 스택에 쌓이고 스코프 체인 초기화 , 변수 객체화, this value 설정 이 일어난다.(새로운 콘텍스트 생성 시마다 반복)&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;{ // 전역 컨텍스트
  '변수 객체': {
    arguments: null,
    variable: [ 'color', 'bar(함수)', 'foo(함수)' ]
  },
  scopeChain: [ '전역 변수 객체' ],
  this: window
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이후 foo 함수가 호출되면서 새로운 함수 콘텍스트가 생성이 되고 스택에 쌓이게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;{ // foo 함수 컨텍스트
  '변수 객체': {
    arguments: null,
    variable: [ 'color' ]
  },
  scopeChain: [ 'foo 변수 객체', '전역 변수 객체'],
  this: window
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이후 bar 함수가 호출되면서 새로운 콘텍스트가 생성이 되고 스택에 쌓이게 된다. 이때 헷갈릴 수 있는 것이 bar 함수는 foo 함수 내부에서 호출이 되었지만 bar 함수의 스코프 체인을 확인해보면 foo 함수는 없다. 이유는 스코프 체인은 함수의 실행이 아닌 선언 당시에 스코프 체인의 값이 결정되기 때문이다. bar 함수는 전역에 선언되었기 때문에 스코프 체인은 자신의 활성화 객체와, 전역 객체를 가리키게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;{ // bar 함수 컨텍스트
  '변수 객체': {
    arguments: [{ text: 'color is' }],
    variable: null
  },
  scopeChain: [ 'bar 변수 객체', '전역 변수 객체'],
  this: window
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이제 bar 함수의 내부가 실행이 되는데 위에서 확인한 거와 같이 bar의 변수 객체에서 arguments(함수 인자) text 값을 찾을 수 있다. 이후 color 값을 찾기 위해 자신의 콘텍스트의 변수 객체를 확인하지만 없기 때문에 다음 스코프 체인인 전역 변수 객체를 확인하게 된다. 전역 변수 객체에는 color 이 존재하기 때문에 참조를 하여 최종 결괏값인 color is black 이 출력되게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;openscad&quot;&gt;&lt;code&gt;function bar (text) {
  console.log(text + color) =&amp;gt; color is black
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;정리!&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;실행 콘텍스트는 실행 가능한 코드가 실행되기 위해 필요한 환경이다. 실행 컨텍스 생성은 코드를 실행하기 전에 필요한 환경을 세팅하기 위해 필요한 작업&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;함수가 호출될 때마다 실행 콘텍스트가 스택에 쌓이게 되고 새로운 콘텍스트가 생성된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;실행 콘텍스트는 변수 객체, 스코프 체인, this value로 이루어져 있고. 실행 콘텍스트가 생성되어 스택에 쌓일 때마다 스코프 체인 초기화 =&amp;gt; 변수 객체화 =&amp;gt; this value 설정 순으로 일어난다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;스코프 체인은 클로저, 변수 객체화의 변수, 함수의 선언과 초기화는 호이 스팅과 관련이 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참조&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://poiemaweb.com/js-execution-context&quot;&gt;https://poiemaweb.com/js-execution-context&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.zerocho.com/category/JavaScript/post/5741d96d094da4986bc950a0&quot;&gt;https://www.zerocho.com/category/JavaScript/post/5741d96d094da4986bc950a0&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://jinminkim-50502.medium.com/execution-context-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-4d6d082c83ff&quot;&gt;https://jinminkim-50502.medium.com/execution-context-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-4d6d082c83ff&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>JS</category>
      <author>o늘do</author>
      <guid isPermaLink="true">https://codingjuny.tistory.com/61</guid>
      <comments>https://codingjuny.tistory.com/61#entry61comment</comments>
      <pubDate>Fri, 15 Jan 2021 21:20:15 +0900</pubDate>
    </item>
    <item>
      <title>리엑트 Virtual DOM</title>
      <link>https://codingjuny.tistory.com/60</link>
      <description>&lt;h3&gt;Virtual DOM&lt;/h3&gt;
&lt;p&gt;Virtual DOM 은 실제 DOM과 비슷한 React의 객체 트리입니다.&lt;br /&gt;실제 DOM을 제어하지 않고 Virtual DOM을 통해 변경된 부분만&lt;br /&gt;DOM에 반영하는 작업을 하는 것이죠.&lt;/p&gt;
&lt;h3&gt;Virtual DOM의 생성&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;ReactDOM.render(
      &amp;lt;App/&amp;gt;,
    document.getElementById('root')
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JSX 문법을 사용한 ReactDOM.render을 호출하면 Virtual DOM 이 만들어집니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt; ReactDOM.render(
    React.createElement(App)
  , document.getElementById('root'));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JSX 문법을 변환하면 다음과 같은 JS 코드가 만들어지고 이때 &lt;b&gt;render 함수는 React에서 사용하는 타입의 컴포넌트를 생성합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/uMolhmW.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;첫 번째는 &lt;b&gt;DOM 이 아닌 컴포넌트를&lt;br /&gt;생성할 때 사용되는 ReactCompositeComponent 객체&lt;/b&gt;와 &lt;b&gt;DOM을 만들 때 생성하는 ReactDOMComponent 객체&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p&gt;render 함수가 생성한 컴포넌트를 React 컴포넌트에 마운트 하기 위해 &lt;b&gt;ReactReconciler.mountComponent() 메서드를 호출하는데 이 메서드에서&lt;br /&gt;ReactCompositeComponent, ReactDOMComponent 객체의 mountComponent() 메서드를 호출하여 작업이 시작됩니다&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/fykCcoB.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;위 과정을 통해 가상 돔이 생성되게 된다. 이후에 배치처리등 작업이 수행되지만 오늘은 가상돔이 어떻게 생기는지만 알아보았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Reconciliation 작업?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Reconciliation은 Virtual DOM과 DOM을 비교하여 DOM을 갱신하는 작업입니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;첫 번째 방법은 setState()를 호출하여 해당 컴포넌트를 변경 대상 컴포넌트에 등록하는 방법이 있고&lt;/b&gt;&lt;br /&gt;&lt;b&gt;두 번째 방법은 Redux처럼 스토어가 변할 때 다시 최상위 컴포넌트의 render 함수를 호출하여 컴포넌트를 변경 대상으로 등록하는 방법이 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;이 둘의 차이점은 비교를 시작하는 컴포넌트가 다르다는 점입니다. &lt;b&gt;setState는 해당 컴포넌트를 기준으로 갱신하고 render 함수를 호출하는 방식은 최상위 컴포넌트부터 비교를 시작합니다.&lt;/b&gt;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;Virtual DOM의 알고리즘&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/3dDmTyo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Virtual DOM의 DOM Tree는 In-Memory에 있으며, Virtual DOM은 DOM fragment를 관리하는 과정을 자동화하고 추상화합니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Smart Diffing Algorithm은 렌더링 되어 사용되고 있는 실제 regular DOM Tree와 React에서 쓰는 in-momery 안에 있는 Virtual DOM Tree를 비교하는 알고리즘&lt;/b&gt;입니다. 최소한의 연산으로 수정 작업이 요구되는 실제 돔 객체를 찾아서 수정 및 변경 작업을 하게 되죠. &lt;b&gt;smart diffing algorithm은 Virtaul DOM Tree와 regular DOM Tree를 맵핑되어있고 어떤 DOM Node가 다른지 찾게 됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;조화 비교 알고리즘은 virtual DOM과 Real DOM의 차이를 비교하여 수정해야 하는 부분만 &lt;b&gt;수정하며 연산이 끝나면 수정이 완성된 DOM Tree를 브라우저 레이아웃 엔진에 건네주어 한 번의 reflow와 repaint과정이 되도록 합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Virtual DOM은 실제로 렌더링 되지 않기 때문에 연산 비용이 적고 최종적인 변화를 실제 DOM에 딱 한번 전달&lt;/b&gt;하기 때문에 연산 횟수를 줄이게 되는 것입니다.&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;React에서 key의 존재 유무에 따른 업데이트 방식&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;    const array = ['a', 'b', 'c', 'd']&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;   array.map(item =&amp;gt; &amp;lt;div&amp;gt;{item}&amp;lt;/div&amp;gt;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같은 배열이 있고 화면에 렌더링 했다고 가정해보자 key 가 있을 때와 없을 때의 차이가 어떻게 될까?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;key가 없다면?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/jJlcgvo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;위 그림과 a, b, c 가 각각 변경되고 마지막 d는 제거되면서 비효율적으로 업데이트된다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;key가 있다면?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/GlURpyu.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;나머지 값들은 그대로 두고 a 만 제거하게 됩니다. 즉 고유한 키가 있다면 수정되지 않는 기존의 값은 그대로 두고 원하는 곳에 내용을 삽입하고 수정하게 됩니다.&lt;/p&gt;
&lt;p&gt;배열을 렌더링 할 때에는 고유한 key 값이 있는 것이 중요합니다. 또한 중복되는 key는 렌더링 시 오류가 나타나고 업데이트가 제대로 이루어지지 않기 때문에 주의해야합니다.&lt;/p&gt;</description>
      <category>FrontEnd</category>
      <author>o늘do</author>
      <guid isPermaLink="true">https://codingjuny.tistory.com/60</guid>
      <comments>https://codingjuny.tistory.com/60#entry60comment</comments>
      <pubDate>Thu, 31 Dec 2020 15:46:47 +0900</pubDate>
    </item>
    <item>
      <title>호이스팅</title>
      <link>https://codingjuny.tistory.com/59</link>
      <description>&lt;h2&gt;호이스팅?&lt;/h2&gt;
&lt;p&gt;오늘은 호이스팅이 무엇이고 어떻게 일어나는지를 알아보려고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;호이스팅은 함수나 변수가 코드가 실행되기 전에 메모리에 저장되는 것입니다&lt;/b&gt;. 실제로 코드가 우리가 작성한 코드 최상단으로 끌어 올려지는 것이 아닙니다! 끌어 올려진 것처럼 보이는 것이죠.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;JS 엔진의 동작 방식 때문에 호이 스팅이 일어나게 되는데요. &lt;b&gt;JS 엔진이 우리가 작성한 스크립트를 확인하고 첫 번째로 하는 일은 우리의 코드에 있는 데이터에 대한 메모리를 세팅하는 일입니다.&lt;/b&gt; 이때 실제로 실행하는 것이 아니라 실행을 위한 준비일 뿐이죠.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;함수 같은 경우는 전체 함수에 대한 참조값이 저장됩니다. var 같은 경우는 실제 변수에 저장된 값이 아닌 undefined로 메모리에 초기화가 되고 let, const 같은 경우는 uninitialized 상태로 메모리에 저장되게 됩니다. 아래와 같은 코드는 어떤 결과를 가지게 될까요?&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;fc();

console.log(a);
console.log(b);
console.log(c);

function fc(){
    console.log('function');
}

const a = 'hakjun';
let b = 'boost';
var c = 'camp';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/AUZE2XF.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;먼저 JS 엔진은 코드를 실행하기 전에 스크립트를 확인하고 메모리에 코드 내에 데이터를 세팅하기 시작합니다. 위 그림처럼 fc 함수에 대한 참조값을 저장하게 되고 변수 a는 var 이기 때문에 undefined , b, c 같은 const, let 이기 때문에 uninitalized로 저장되게 되죠.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/plkj9Qw.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;이후 실제 실행이 되게 되면 fc() 같은 경우는 이미 메모리가 함수의 참조값을 알고 있기 때문에 함수 내부가 실행되게 됩니다. var 같은 경우는 실제 값이 아닌 저장하지 않고 undefiend 가 출력 되게 됩니다. 하지만 let과 const는 uninitalized 초기화되지 않은 값이기 때문에 referenceError 가 뜨게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;a();

const a = function (){
    console.log('is right?')
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;함수의 호이스팅은 함수의 선언식일때만 작동하게되는데 다음과 같이 함수 표현식으로 저장하게되면 호이스팅시 메모리에 a 라는 값에 uninitalized 가 저장되게되고(var 로 선언했다면 undefined) 실제 실행시 a 는 함수가 아니기 때문에 TypeError: a is not a function 오류가 뜨게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;호이스팅은 함수나 변수가 코드가 실행되기 전에 메모리에 코드내의 데이터가 저장되는 것이고 실제로 코드 자체가 최상단으로 끌어 올려지는 것이 아니다.&lt;/p&gt;
&lt;p&gt;함수는 전체 함수에 대한 참조값이 저장되고 var는 undefined, let, const는 uninitalized 가 저장된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.to/lydiahallie/javascript-visualized-hoisting-478h&quot;&gt;https://dev.to/lydiahallie/javascript-visualized-hoisting-478h&lt;/a&gt;&lt;/p&gt;</description>
      <category>JS</category>
      <author>o늘do</author>
      <guid isPermaLink="true">https://codingjuny.tistory.com/59</guid>
      <comments>https://codingjuny.tistory.com/59#entry59comment</comments>
      <pubDate>Mon, 28 Dec 2020 22:43:31 +0900</pubDate>
    </item>
    <item>
      <title>Node JS 동작원리</title>
      <link>https://codingjuny.tistory.com/58</link>
      <description>&lt;h2&gt;NodeJS 동작원리&lt;/h2&gt;
&lt;p&gt;NodeJS의 동작 원리를 알기 위해서는 javascript의 동작원리를 먼저 알아야 합니다.&lt;/p&gt;
&lt;p&gt;javascript 는 싱글 스레드로 동작하는 언어입니다. 싱글 스레드이기 때문에 한 번에 하나의 작업만 처리할 수 있게 되죠.&lt;/p&gt;
&lt;p&gt;그렇다면 자바스크립트는 어떻게 동작 할까요?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/kSSTZ1r.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;위의 그림처럼 Javascript의 런타임은 메모리 힙과 콜 스택으로 구성됩니다 메모리 힙은&lt;b&gt; 메모리 할당을 담당&lt;/b&gt;하는 곳이고 &lt;b&gt;콜 스택은 코드가 호출되면서 스택으로 쌓이는 곳&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;콜 스택❓&lt;/p&gt;
&lt;p&gt;하나의 메인 스레드에서 호출되는 함수들은 콜 스택에 쌓이게 됩니다. 이 함수 들은 LIFO(나중에 쌓이는 것이 먼저 처리되는) 방식으로 실행되죠. &lt;b&gt;Javascript는 싱글 스레드 기반이기 때문에 하나의 메인 스레드와 하나의 콜 스택을 가지고 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Javascript 런타임은 자체적으로 비동기를 지원할까❓&lt;/p&gt;
&lt;p&gt;J&lt;b&gt;avascript 런타임 자체에서는 비동기 API를 지원하지 않습니다.&lt;/b&gt; 동시성을 보장하는 &lt;b&gt;비동기, 논블로킹 작업들을 Javascript 엔진을 구동하는 런타임 환경에서 담당합니&lt;/b&gt;다. 여기서 런타임 환경은 &lt;b&gt;NodeJS, 브라우저&lt;/b&gt;를 말하게 되죠.&lt;/p&gt;
&lt;p&gt;Javascript의 엔진은 단지 코드에 대한 실행환경입니다. 각 이벤트를 스케쥴링하는 것은 그것을 둘러싸고 있는 환경이며 비동기 작업들도 이러한 환경에서 지원하죠&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/eTwA908.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;위에 그림이 바로 Javascript의 엔진과 외부 런타임 환경들이 합쳐진 모습니다. 그럼 각 역할을 알아봅시다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;이벤트 루프&lt;/b&gt;❓&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;이벤트 루트는 이벤트 발생 시 호출되는 콜백 함수들을 관리하여 테스크 큐에 전달하고 테스크 큐에 담겨있는 콜백 함수들을 콜 스택에 넘겨줍니다. &lt;b&gt;이때 이벤트 루프는 콜 스택을 확인하고 콜 스택이 비어있는 경우에만 테스크 큐에 콜백 함수를 넘겨주게 됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;테스트 큐&lt;/b&gt;❓&lt;br /&gt;web api에서 비동기 작업들이 실행된 후 호출되는 콜백 함수들이 기다리는 공간입니다. 이벤트 루프가 정해준 순서로 기다리게 되고 FIFO으로 처리되죠. 사실 테스트 큐는 하나의 큐로 이루어 있지 않습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;web api&lt;/b&gt;❓&lt;br /&gt;브라우저에서 지원하는 api로 Dom 이벤트 Ajax, setTimeout 등의 비동기 작업들을 수행할 수 있도록 api를 지원합니다. 물론 Nodejs의 이벤트 루프와는 차이가 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;런타임 환경에서 어떻게 비동기 코드가 실행될까&lt;/b&gt;❓&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1tHLYwB.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;JS에서 Web api가 지원하는 setTimeout 같은 코드가 실행될 때 순서를 알아봅시다.&lt;/p&gt;
&lt;p&gt;setTimeout 코드가 호출 스택에 쌓이게 됩니다. 이후 Javascript 엔진은 비동기 작업을 Web api에게 위임하게 됩니다. Web api는 해당 비동기 작업을 수행하고 콜백 함수를 이벤트 루프를 통해 태스크 큐에 넘겨주게 됩니다. 이벤트루프는 콜 스택에 쌓여있는 함수가 없을때 태스크 큐에서 대기하고 있던 콜백함수를 콜스택으로 넘겨주게 되죠. 이후 콜스택에 쌓인 콜백 함수가 실행되고 콜 스택에서 제거됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;논블로킹 I/O&lt;/b&gt;❓&lt;br /&gt;만약 http 요청을 동기로 수행한다면 해당 함수가 콜 스택에 쌓인 채로 머물고 JS 엔진은 해당 작업이 끝날 때까지 어떤 작업도 수행할 수 없게 되죠. 바로 동기 작업이 다른 코드들을 블로킹하게 되는 것입니다. 하지만 &lt;b&gt;Javascript 는 비동기 작업들을 Web api에게 넘겨줄 수 있기 때문에 해당 작업이 완료될 때까지 다른 코드들을 실행할 수 있게 됩니다. 이것이 바로 논블로킹이죠&lt;/b&gt;.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;    console.log('실행시작');
setTimeout(() =&amp;gt; console.log('0초니까 바로 출력될까요?'), 0);
console.log('실행끝');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다음과 같은 코드는 실행 시작 =&amp;gt; 실행 끝 =&amp;gt; 0초니까 바로 출력될까요? 순서로 실행되게 됩니다. 시간이 중요한 게 아니라 setTimeOut 함수가 Web api 가 지원하는 비동기 함수라는 점이 중요하게 됩니다. setTimeOut는 바로 콜 스택에 쌓여 처리되는 것이 아니라 Web api 에서 비동기로 처리된 후 콜백함수가 테스크 큐에 전달되게 되는 것이죠. 그렇기 때문에 시간이 0초여도 콜스택에 바로 쌓이는 다른 함수들보다 늦게 호출되게 됩니다.&lt;/p&gt;
&lt;p&gt;또한 setTimeOut의 딜레이 시간을 5초도 두면 정확이 5초 뒤에 콜백 함수가 실행될까요? 아닙니다. 이벤트 큐는 콜 스택이 비어있을 경우 콜스택으로 태스크 큐의 콜백함수를 전달하기 때문에 콜스택이 많이 쌓여있으면 실행되는 시간이 지연되게 되죠.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Node JS란&lt;/b&gt;❓&lt;br /&gt;Javascript를 브라우저 밖에서도 실행시킬 수 있는 Javascript런타임입니다. &lt;b&gt;Javascript을 실행시킬 수 있는 환경이라는 뜻이죠. &lt;/b&gt;Node JS는 싱글 스레드입니다. 먼저 싱글 스레드와 멀티 스레드에 대한 개념을 알고 있어야 합니다. 싱글 스레드는 프로세스 내에서 하나의 스레드가 하나의 요청만을 수행합니다. 한 번에 여러 요청을 수행할 수 없는 것이죠 그래서 싱글 스레드는 블로킹 모델이라고 합니다. 반면 멀티스레드는 스레드 풀에서 실행의 요청만큼 스레드를 매칭 하여 작업을 수행합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Node JS는 싱글 스레드 논블로킹 모델입니다&lt;/b&gt;. &lt;b&gt;싱글 스레드 지만 비동기 I/O 작업을 통해 요청들을 서로 블로킹하지 않습니다.&lt;/b&gt; &lt;b&gt;즉 동시에 많은 요청들을 비동기로 수행함으로써 싱글 스레드 일지라고 논블로킹이 가능합니다.&lt;/b&gt; 또한 NodeJS는 클러스터링을 통해 프로세스를 포크하여 멀티스레드 처럼 사용할 수 있습니다. 이는 트래픽에 따라서 프로세스를 포크 할 수 있으므로 서버의 확장성 면에서 용이하죠&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;NodeJS 는 완전한 싱글 스레드❓&lt;/b&gt;&lt;br /&gt;&lt;b&gt;NodeJS는 싱글 스레드이지만 완전한 싱글 스레드를 기반으로 동작하지는 않습니다.&lt;/b&gt; 무슨 말일까요? 일부 블로킹 작업들은 &lt;b&gt;libuv의 스레드 풀에서 수행되기 때문이죠.&lt;/b&gt; 이를 이해하기 위해서 몇 가지 알아야 하는 사항이 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;이벤트 기반&lt;/b&gt;&lt;br /&gt;이벤트 기반이란 이벤트가 발생할 때 미리 지정해둔 작업을 처리하는 방식입니다. NodeJS는 이벤트 리스너에 등록해둔 콜백 함수를 실행하는 방식으로 동작합니다. 즉 이벤트 루프가 이를 가능하게 해 줍니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Node JS 내부 구조&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/4juLnES.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;NodeJS를 크게 나눠보면 내장 라이브러리와 V8 엔진 그리고 libuv로 구성됩니다. &lt;b&gt;NodeJS의 특성인 이벤트 기반, 논 블로킹 I/O 모델들은 모두 libuv 라이브러리에서 구현됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;NodeJS에서 작성되는 거의 모든 코드들은 콜백 함수로 이루어져 있죠. &lt;b&gt;콜백 함수들은 libuv 내에 위치한 이벤트 루프에서 관리 및 처리됩니다.&lt;/b&gt; 이벤트 루프는 여러 개의 페이즈 들을 갖고 있으며 해당 페이즈들은 각자만에 큐를 가지고 있습니다. &lt;b&gt;이벤트 루프는 라운드 로빈 방식으로 노드 프로세스가 종료될 때까지 여러 페이지들을 계속 순회합니다.&lt;/b&gt; &lt;b&gt;페이즈들은 각각의 큐들을 관리하고 각각의 큐는 FIFO 순서로 콜백 함수들을 처리&lt;/b&gt;하게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;논블로킹 I/O&lt;/b&gt;&lt;br /&gt;NodeJS에서 논블로킹 I/O 모델은 Input Output 이 관련된 작업(데이터베이스 CRUD, 파일 시스템) 등의 블로킹 작업들은 백그라운드에서 수행하고 이를 비동기 콜백 함수로 이벤트 루프에 전달하는 것을 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/0L1iKw7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;I/O 작업들은 OS 커널 혹은 libuv 내의 스레드 풀에서 담당하게 됩니다.&lt;/b&gt; libuv는 OS 커널에서 어떤 비동기 작업들을 지원해주는지 알고 있기 때문에 작업 종류에 따라서 커널 혹은 스레드 풀로 분기합니다. &lt;b&gt;libuv의 스레드풀은 커널이 지원안하는 작업들을 수행하게 됩니다.&lt;/b&gt; 또한 libuv 의 스레드 풀은 멀티 스레드로 이루어져 있습니다. 예를 들어 파일 시스템은 libuv에서 처리되게 되고 스레드 풀도 마찬가지로 작업을 마친 후 이벤트 루프에 콜백 함수를 전달하게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;libuv는 어떻게 동작할까&lt;/b&gt;❓&lt;br /&gt;NodeJS는 기본적으로 libuv 위에서 동작하여 node 인스턴스가 생성될 때 libuv 에는 스레드 풀이(기본값은 4개의 스레드)가 생성됩니다. libuv는 OS 커널이 어떤 비동기 작업들을 지원해주는지 알고 있기 때문에 libuv의 스레드는 커널이 지원하지 않는 작업들을 수행하게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;이벤트 루프의 내부 동작 과정&lt;/b&gt;❓&lt;/p&gt;
&lt;p&gt;이벤트 루프는 6 phase 들로 구성되어있습니다. 각 phase 들을 FIFO 큐를 가지고 있고 이벤트 루프가 해당 phase를 라운드 로빈 방식으로 순환하면서 실행되게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1ghSNyT.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;timers&lt;/b&gt;&lt;br /&gt;setTimeOut(), setIntever() 같은 timer 함수들이 처리가 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;I/O callbacks&lt;/b&gt;&lt;br /&gt;클로즈 콜백, 타이머로 스케쥴링된 콜백, setImemediate를 제외한 거의 모든 콜백들이 큐에 놓이게 됩니다.(http, api 호출, db 조회 등)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;poll&lt;/b&gt;&lt;br /&gt;poll 큐에 있는 이벤트 콜백들을 처리합니다. 이때 poll에 쌓인 콜백 함수들을 실행하게 되는데 더 이상 실행할 콜백 함수가 없을때에는 규칙에 따라 다음 단계로 넘거가거나 대기하게 됩니다. 일단 check 단계를 검사하여 setImemediate 가 있는지 확인하고 setImemediate 가 있으면 check 단계로 넘어가게 됩니다. 만약 setImemediate 가 없다면 timer 단계에 실행할 timer 함수가 있는지 확인하게 됩니다. timer 단계로 넘어갈 수있을때까지 대기하고 도중에 poll 큐에 콜백함수가 들어온다면 즉시 실행합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;check&lt;/b&gt;&lt;br /&gt;setImediate() 콜백이 호출되고 실행됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;close callbacks&lt;/b&gt;&lt;br /&gt;. on('close') 같은 이벤트에 따른 콜백 함수를 실행합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음과 같은 코드들은 어떻게 동작할까 ❓&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;setTimeout(() =&amp;gt; {
    console.log('timeout');
}, 0);

setImmediate(() =&amp;gt; {
    console.log('immediate');
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드를 실행하면 setTimeout()는 timer 영어에 들어가고 setimmediate()는 check 영역에 들어가게 됩니다. 이벤트 루프가 돌고 있는 시점에 따라 다르게 되는데 timer 단계라면 setTimeout 이 먼저 실행되고 그렇지 않다면 setImmediate 가 먼저 실행되게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;const fs = require('fs');

fs.readFile(__filename, () =&amp;gt; {
  setTimeout(() =&amp;gt; {
    console.log('timeout');
  }, 0);
  setImmediate(() =&amp;gt; {
    console.log('immediate');
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드를 실행하면 결과는 항상 아래와 같게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;immediate
timeout&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;fs.readFile 는 블로킹 작업이고 OS 가 지원하지 않는 비동기 작업 이므로 libuv의 스레드 풀에서 담당하게 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;스레드가 작업을 완료한 뒤 I/O callback 영역에 큐에 콜백을 등록합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이벤트 루프가 I/O callback 영역을 실행할 때 콜백을 poll 영역에 큐에 등록하게 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이제 poll 영역을 실행하게 되면서 I/O callback에서 받은 콜백 내부 로직을 실행하게 됩니다. setTimeout 같은 경우는 timer 등록되게 되고 setImmediate 같은 경우는 check 영역에 들어가게 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이벤트 루프가 큐를 비우고 다음 실행 영역인 check를 확인하게 됩니다. 위에서 말했듯이 check 영역에 실행할 setImmediate 이있기때문에 immediate을 출력하게 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;timers에 등록되었던 setTimeout의 콜백을 poll 큐에 등록하게됩되고 이벤트 루프가 poll 영역을 실행하고 timeout 을 출력하게 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;노드 프로세스가 반환되고 종료됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;b&gt;결론&lt;/b&gt;&lt;br /&gt;JavaScript NodeJs는 싱글 스레드이다. 비동기다. 정도만 알고 있었는데 이번 기회에 NodeJs 동작원리와 내부구조를 자세하게 알 수 있었던 거 같다. 동작원리를 활용하여 조금 더 효율적인 코드를 작성할 수 있도록 앞으로 노력해야겠다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejs.org/ko/docs/guides/blocking-vs-non-blocking/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;nodejs.org/ko/docs/guides/blocking-vs-non-blocking/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejs.org/ko/docs/guides/event-loop-timers-and-nexttick/&quot;&gt;https://nodejs.org/ko/docs/guides/event-loop-timers-and-nexttick/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sjh836.tistory.com/149&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;sjh836.tistory.com/149&lt;/a&gt;&lt;/p&gt;</description>
      <category>JS</category>
      <category>NODEJS#비동기#이벤트루프</category>
      <author>o늘do</author>
      <guid isPermaLink="true">https://codingjuny.tistory.com/58</guid>
      <comments>https://codingjuny.tistory.com/58#entry58comment</comments>
      <pubDate>Mon, 28 Dec 2020 13:37:20 +0900</pubDate>
    </item>
    <item>
      <title>백준_8983_사냥꾼</title>
      <link>https://codingjuny.tistory.com/57</link>
      <description>&lt;h2&gt;풀이&lt;/h2&gt;
&lt;p&gt;입력값이 순서대로 정렬되어있지 않으므로 입력을 받고 정렬을 해줍니다.&lt;br /&gt;사대 같은 경우에는 오름차순으로 동물 같은 경우에는 x 축이 작은 순으로 정렬해줍니다. 입력값이 크기 때문에 순차적으로 검사하기 위함이다. 만약 정렬하지 않고 모든 경우에 수를 검사한다면 시간 초과가 빵...!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/JkYkLHt.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;저는 동물을 기준으로 순회하면서 각동물이 사대에 범위에 포함되어 잡힐 수 있는지 판단했습니다.&lt;/p&gt;
&lt;p&gt;문제를 그림으로 표현하면 다음과 같고 &lt;b&gt;대략 3가지 경우를&lt;/b&gt; 생각해줘야합니다.&lt;br /&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;첫 번째는 사대의 범위를 Y 축 기준으로 넘는 경우입니다.&lt;/b&gt; 이때는 어느 사대도 저위에 동물을 잡을 수 없기 때문에 다음 동물을 검사합니다.&lt;br /&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;두 번째는 범위에 포함되는 경우입니다.&lt;/b&gt; 범위에 포함되는 경우에는 현재 확인 중인 사대를 유지하면서 다음 동물로 넘어갑니다.&lt;br /&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;세 번째는 범위에 포함되지 않았을 때입니다.&lt;/b&gt; 이경우는 또 두 가지 경우로 나눠지게 됩니다. 범위에 포함되지 않았는데 &lt;b&gt;만약에 동물의 X 축이 현재 확인 중인 사대에 x 축보다 작은 경우에는 절대로 다음 사대에 포함될 수 없습니다&lt;/b&gt; 그렇기&lt;/p&gt;
&lt;p&gt;때문에 얼른 다음 동물을 검사하러 넘어갑시다. &lt;b&gt;만약에 반대로 동물의 X 축이 현재 확인 중인 사대에 X 보다 크다면 다음 사대에 포함될 가능성이 존재하기 때문에 다음 사대를 검사해 줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;물론 추가적으로 확인해줘야 하는 사항도 존재합니다. 만약 현재 검사하고 있는 동물이 마지막 사대의 범위를 넘어간다면 더 이상 로직을 수행하지 않고 종료하면 됩니다. 동물을 X 축 기준으로 정렬했기 때문에 그다음 동물들은 절대로 잡히지 않기 때문이죠&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음은 전체 코드입니다. 입력 부분을 제외하면 생각보다 로직이 간단합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public class 사냥꾼 {

    static int M, N, L; // 사대의수, 동물의수, 사정거리
    static int gunLocation[];
    static int animalLocation[][];
    static int catchedAnimalCount = 0;
    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), &quot; &quot;);
        M = Integer.parseInt(st.nextToken());
        N = Integer.parseInt(st.nextToken());
        L = Integer.parseInt(st.nextToken());



        gunLocation = new int[M];
        animalLocation = new int[N][2];
        st = new StringTokenizer(br.readLine(), &quot; &quot;);
        for(int i = 0; i &amp;lt; M; i++) {
            gunLocation[i] = Integer.parseInt(st.nextToken());
        }

        Arrays.sort(gunLocation);

        for(int i = 0; i &amp;lt; N; i++) {
            st = new StringTokenizer(br.readLine(), &quot; &quot;);
            animalLocation[i][1] = Integer.parseInt(st.nextToken());
            animalLocation[i][0] = Integer.parseInt(st.nextToken());
        }

        Arrays.sort(animalLocation, (a, b)-&amp;gt;{
            if(a[1] == b[1]) {
                return a[0] - b[0];
            }
            return a[1] - b[1];
        });



        int gunIndex= 0;

        for(int i = 0; i &amp;lt; animalLocation.length; i++) {
            if(animalLocation[i][0] &amp;gt; L ) continue;
            if(animalLocation[i][1] &amp;gt; gunLocation[M - 1] + L) break;

            for(int j = gunIndex; j &amp;lt; M; j++) {
                int distance = Math.abs(gunLocation[j] - animalLocation[i][1]) + animalLocation[i][0];

                if(distance &amp;lt;= L) {
                    gunIndex = j;
                    catchedAnimalCount++;
                    break;
                }
                if(animalLocation[i][1] &amp;lt; gunLocation[j]) break;
            }


        }

        System.out.println(catchedAnimalCount);

    }
}

&lt;/code&gt;&lt;/pre&gt;</description>
      <category>백준</category>
      <author>o늘do</author>
      <guid isPermaLink="true">https://codingjuny.tistory.com/57</guid>
      <comments>https://codingjuny.tistory.com/57#entry57comment</comments>
      <pubDate>Fri, 25 Dec 2020 03:26:03 +0900</pubDate>
    </item>
    <item>
      <title>Apollo Client 캐시 관리 기법</title>
      <link>https://codingjuny.tistory.com/56</link>
      <description>&lt;h3&gt;Apollo Client의 캐시 관리 기법&lt;/h3&gt;
&lt;p&gt;아폴로 클라이언트는 GrahpQL 쿼리요청 결과를 로컬에 저장할 수 있는 캐시 기능을 제공한다. &lt;b&gt;클라이언트에서 서버로 Graphql 쿼리를 요청하면 그 결과를 클라이언트 측 메모리에 저장하게 되는 것이다.&lt;/b&gt; 이를 잘 활용하면 클라이언트 측에서 동일한 쿼리를 요청할 때 재사용할 수 있기 떄문에 불필요한 요청을 줄일 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;기본적으로 브라우저 캐시는 GET 요청만 캐싱한다. 하지만 GrahpQL의 경우는 모든 요청을 POST로 하므로 브라우저 캐시를 사용할 수 없다. 이를 해결해 주는 좋은 해결책이 Apollo Client이다.&lt;/b&gt; 이는 POST 요청을 하는 GraphQL 요청에도 캐시를 사용할 수 있게 해 준다. 그렇다면 캐시 과정은 어떻게 일어날까?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/0UuDdQV.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;요청을 보내면서 먼저 Apollo 캐시를 확인한다 만약 로컬에 저장된 데이터가 존재한다면 그 데이터를 반환받는다. 만약의 데이터가 존재하지 않는다면 서버에 요청을 보낸 후 응답받은 데이터로 캐시 데이터를 업데이트한 후 데이터를 반환받는다.&lt;/p&gt;
&lt;p&gt;하지만 항상 위와 같은 방식으로만 캐싱이 진행되는 것은 아니다. 여러 가지 캐시 옵션을 제공한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;cache-first&lt;/b&gt;&lt;br /&gt;항상 캐시를 먼저 확인한다. 캐시에 데이터가 없다면 network 요청을 한다&lt;/li&gt;
&lt;li&gt;&lt;b&gt;network-only&lt;/b&gt;&lt;br /&gt;캐시를 확인하지 않고 항상 network 요청을 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cache-only&lt;/b&gt;&lt;br /&gt;캐시만 확인한다. 캐시에 데이터가 없다면 에러를 반환한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;cache-and-network&lt;/b&gt;&lt;br /&gt;먼저 캐시를 확인하여 데이터를 반환한다. 이후 network 요청을 하여 새로운 데이터를 받아와 캐시를 업데이트하고 업데이트된 캐싱 데이터를 다시 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;상황에 맞게 적절히 캐시 정책을 적용할 필요가 있다고 생각한다. 최근 프로젝트에서 채팅방 기능이 있었는데 사용자가 채팅방에서 나간 후 다시 들어왔을 때 사용자가 나가 있는 사이에 수신된 채팅 데이터가 보이지 않는 문제가 발생했다. &lt;b&gt;원인은 apollo client의 캐시 정책이 디폴트 값인 cache-first로 설정되어있었기 때문이다&lt;/b&gt;. 즉 사용자가 채팅방 재접속했을 때 최근까지 쌓인 채팅 데이터가 아닌 이미 캐시 되어있는 이전 채팅 데이터를 불러왔기 때문이다. &lt;b&gt;이를 해결하기 위해서 캐시 정책을 network-only 로 수정해 주었다.&lt;/b&gt; 실시간 통신이 위주가 되는 프로젝트로 데이터가 수시로 변했기 때문에 캐시 정책을 모든 query 요청에 전역적으로 설정 해주었다. 물론 필요한 요청에만 캐시정책을 선택적으로 적용할 수 있다. 다음과 같은 방법이 존재한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;캐시 설정 방법 전역으로 설정.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;const client = new ApolloClient({
...
defaultOptions : {
  watchQuery: {
      fetchPolicy: 'network-only',
      errorPolicy: 'ignore',
},
  query: {
      fetchPolicy: 'network-only',
      errorPolicy: 'all',
},
};,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;특정 요청에만 설정.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt; const { loading, error, data } = useQuery(GET_DOGS, {
fetchPolicy: &quot;network-only&quot;
});&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;지금까지 apollo 캐시 정책에 대하여 간단하게 알아보았다. 다음 포스팅에서는 apollo cache를 활용한 local state 관리에 대하여 이어서 알아볼 예정이다.&lt;/p&gt;</description>
      <category>FrontEnd</category>
      <author>o늘do</author>
      <guid isPermaLink="true">https://codingjuny.tistory.com/56</guid>
      <comments>https://codingjuny.tistory.com/56#entry56comment</comments>
      <pubDate>Wed, 23 Dec 2020 02:25:12 +0900</pubDate>
    </item>
    <item>
      <title>NOSQL vs SQL</title>
      <link>https://codingjuny.tistory.com/55</link>
      <description>&lt;h3&gt; MongoDB를 사용한 이유&lt;/h3&gt;
&lt;p&gt;우리 프로젝트에서 NOSQL에 해당하는 MongoDB 를 사용했다. 실시간 통신이 많아서 빠른 Data 조회 속도가 필요했기 때문이다. 또한 MongoDB 는 위치기반의 데이터 관련 쿼리를 지원하기 때문에 위치기반 애플리케이션인 우리 앱에 적합하고 판단했다. 그렇다면 SQL과 NOSQL의 특징과 차이점을 무엇일까?&lt;/p&gt;
&lt;h3&gt;⁉️ SQL?&lt;/h3&gt;
&lt;p&gt;구조화된 쿼리 언어 (Structured Query Language) 로서 특정 유형의 데이터 베이스와 상호 작용하는 데 사용되는 쿼리 언어입니다.&lt;/p&gt;
&lt;p&gt;SQL의 주요 특징은 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;정해진 스키마(구조)에 따라서 데이터 베이스를 테이블에 저장&lt;/li&gt;
&lt;li&gt;데이터는 관계를 통해서 연결되고 여러 개의 테이블에 분산되어 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;첫 번째는 엄격한 스키마&lt;/b&gt;인데 데이터를 테이블에 레코드 형태로 저장하고 테이블은 명확하게 정의된 구조가 존재합니다. &lt;b&gt;관계형 데이터 베이스에서는 스키마를 준수하지 않는 레코드를 추가할 수 없습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;⁉️스키마란? 스키마는 데이터 베이스의 구조와 제약조건에 관해 전반적인 명세를 기술한 것입니다. 바로 개체의 특성을 나타내는 속성과, 속성의 집합으로 이루어진 개체, 개체 사이에 존재하는 관계, 이들이 유지해야 하는 제약조건을 기술한 것이죠.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;두 번째는 관계입니다.&lt;/b&gt; 관계를 기반으로 데이터를 여러 개의 테이블에 나누어서 데이터들의 중복을 피할 수 있습니다. 즉 하나의 테이블에서 중복 없이 하나의 데이터만 관리하기 때문에 테이블에서 부정확한 데이터를 다룰 위험이 없어지는 거죠.&lt;/p&gt;
&lt;h3&gt;⁉️ NoSQL?&lt;/h3&gt;
&lt;p&gt;NoSQL 은 비 관계형 데이터 베이스로 SQL 과는 반대되는 접근 방식을 따른다고 생각하시면 됩니다.&lt;/p&gt;
&lt;p&gt;monggose 같은 ORM을 사용하면 스키마나 관계를 사용할 수도 있지만 기본적으로 NoSQL의 주요 특징은 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;스키마가 존재하지 않습니다&lt;/li&gt;
&lt;li&gt;관계가 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SQL 가 정해진 스키마에 따라 데이터를 추가해야 한다면 NoSQL 은 다른 구조의 데이터를 같은 컬렉션에 추가할 수 있습니다. 여기서 컬렌션은 SQL 의 테이블이라고 생각하시면됩니다. 마찬가지로 SQL 의 레코드(튜플) 은 NoSQL 에서 Document 라고 생각하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;스키마가 없기 때문에 유연하게 데이터를 조정하고 새로운 필드가 추가된 데이터를 추가&lt;/b&gt; 할 수 있습니다. 또한 관&lt;b&gt;련된 데이터를 동일한 컬렉션에 넣기 때문에 관계형 데이터 베이스처럼 조인할 필요가 없습니다.&lt;/b&gt; 실제로 NoSQL 에는 조인이라는 개념이 존재하지도 않습니다.&lt;/p&gt;
&lt;p&gt;다만 &lt;b&gt;데이터가 중복될 수 있어 불안정한 측면&lt;/b&gt;이 있기 때문에 특정 데이터를 같이 사용하는 모든 컬렉션에서 똑같은 데이터 업데이트를 수행되도록 해야 하죠. 그렇기 &lt;b&gt;때문에 자주 변경되지 않는 데이터가 NoSQL에 적합&lt;/b&gt;합니다.&lt;/p&gt;
&lt;h3&gt;⁉️&lt;b&gt;수직적 확장과 수평적 확장&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;수직 확장이란?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;데이터 베이스 서버의 성능을 향상하는&lt;/b&gt; 것입니다. CPU, RAM을 업그레이드하는 것이죠. 거대한 헐크 혼자서 일을 한다고 생각하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;수평 확장이란?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;더 많은 &lt;b&gt;서버를 추가하여 데이터 베이스가 전체적으로 분산&lt;/b&gt;되는 것을 의미합니다. 다수의 작은 미니언이 일을 한다고 생각하시면 됩니다.&lt;/p&gt;
&lt;p&gt;SQL 은 데이터가 스키마, 관계 기반으로 저장되기 때문에 수직적 확장만을 지원합니다. &lt;b&gt;수평적 확장은 NoSQL에서 가능합니다.&lt;/b&gt; &lt;b&gt;NoSQL 데이터 베이스는 샤딩을 기본적으로 지원하므로 여러 서버에서 데이터 베이스를 쉽게 분리할 수 있기 때문에 수평 확장에 유리&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p&gt;⁉️&lt;b&gt;샤딩?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;관계형 데이터베이스에서 대량의 데이터를 처리하기 위해서 데이터를 파티셔닝 하는 기술인데요. 샤딩은 DBMS 레벨에서 데이터를 나누는 것이 아니고 &lt;b&gt;데이터베이스 자체를 분할하는 방식&lt;/b&gt;입니다. 간단하게 예를 들면, 현재 운영 중인 서비스가 해외로도 진출을 하게 되어서 더 큰 데이터 베이스가 필요하게 되면 이때 한국 유저의 정보는 A 데이터 베이스를 이용하고 유럽 쪽의 데이터베이스는 B 데이터 베이스에 저장하는 것이죠.&lt;/p&gt;
&lt;h3&gt;⁉️속도&lt;/h3&gt;
&lt;p&gt;NOSQL 은 SQL과 다르게 관련된 데이터를 같이 저장하므로 조인이 필요 없고 ACID를 보장하는데 시간을 투자하지 않으므로 속도면에서 빠르게 됩니다.&lt;/p&gt;
&lt;h3&gt;✏️요약&lt;/h3&gt;
&lt;p&gt;SQL의 장점은 명확하게 정의된 스키마가 존재하기 때문에 데이터 무결정을 보장하고 관계가 존재하기 때문에 각데이터를 중복없이 저장하게됩니다. 반면에 SQL 의 단점은 구조화된 스키마의 필요성으로 상대적으로 덜 유연하고 잦은 JOIN은 데이테 베이스의 성능에 영향을 줍니다. 또한 수평적 확장이 어렵고 수직확장만 가능하기 때문에 더 이상 성능 향상을 할 수 없는 한계에 직면하게 됩니다.&lt;/p&gt;
&lt;p&gt;NOSQL 은 스키마가 없기 때문에 새로운 데이터, 필드를 추가하는데 자유롭습니다. 데이터는 애플리케이션이 필요로 하는 형식으로 저장되기 때문에 데이터를 조회 속도가 빠르고 수직/수평 확장 둘 다 가능합니다. 반면에 NOSQL에 단점은 데이터가 자유롭게 추가가 가능하기 때문에 데이터 업데이트 시 중복되어 저장되어 있는 데이터를 똑같이 업데이트해줘야 합니다.&lt;/p&gt;
&lt;h3&gt; 결론&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;사용자와 데이터에게 명확한 스키마가&lt;/b&gt; 중요하고 &lt;b&gt;데이터&lt;/b&gt; &lt;b&gt;베이스 구조가 변경될 여지가 없는 경우,&lt;/b&gt; &lt;b&gt;관계를 맺고 있는 데이터가 자주 변경되는 경우&lt;/b&gt;에는 SQL을 사용하는 것이 유리하다고 생각이 됩니다.&lt;/p&gt;
&lt;p&gt;반면에 &lt;b&gt;데이터 구조가 변경, 확장&lt;/b&gt;에 가능성이 존재하고 &lt;b&gt;데이터의 수정보다는 읽기 작업이 많이 일어나고&lt;/b&gt; &lt;b&gt;막대한 양의 데이터를 다뤄야 해 수평 확장이 필요한 경우에는 NOSQL을&lt;/b&gt; 사용하는 것이 유리하다고 생각이 됩니다.&lt;/p&gt;</description>
      <category>FrontEnd</category>
      <author>o늘do</author>
      <guid isPermaLink="true">https://codingjuny.tistory.com/55</guid>
      <comments>https://codingjuny.tistory.com/55#entry55comment</comments>
      <pubDate>Sat, 19 Dec 2020 18:12:15 +0900</pubDate>
    </item>
    <item>
      <title>웹팩 설정하기</title>
      <link>https://codingjuny.tistory.com/54</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.notion.so/93636d3e1ad1481faa261cbfc25d8be4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.notion.so/93636d3e1ad1481faa261cbfc25d8be4&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1607925241713&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;개발환경설정&quot; data-og-description=&quot;웹팩 ✍️&quot; data-og-host=&quot;www.notion.so&quot; data-og-source-url=&quot;https://www.notion.so/93636d3e1ad1481faa261cbfc25d8be4&quot; data-og-url=&quot;https://www.notion.so/93636d3e1ad1481faa261cbfc25d8be4&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.notion.so/93636d3e1ad1481faa261cbfc25d8be4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.notion.so/93636d3e1ad1481faa261cbfc25d8be4&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;&gt;개발환경설정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;웹팩 ✍️&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.notion.so&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>부스트캠프</category>
      <author>o늘do</author>
      <guid isPermaLink="true">https://codingjuny.tistory.com/54</guid>
      <comments>https://codingjuny.tistory.com/54#entry54comment</comments>
      <pubDate>Mon, 14 Dec 2020 14:54:10 +0900</pubDate>
    </item>
  </channel>
</rss>