잡다한 프로그래밍 :: 잡다한 프로그래밍
반응형

지인의 추천으로 오랜만에 책을 읽게 되었다.

책에서는 경제적 자유에 관하여 이야기 하고 있는데, 넓게 보면 좀 더 나은 사람이 될 수 있는 방법이라고 생각할 수 있을 것 같다. (이 글에서는 더 나아가는 방법, 돈, 목표 등 으로 생각하고 글을 적었다)

책에서는 사람을 순리자, 역행자 두 부류로 구분 지어 이야기 하고 있다.
현재 그대로, 혹은 타고난 그대로 평범하게 살아가는 사람을 순리자로 현재를 더 나아가기 위해 노력하고 운명을 거스르는 사람을 역행자로 구분하고 있다.


책 제목 그대로 역행자가 되기 위해서는 어떤것이 중요한지 7단계에 거쳐서 설명하고 있다.
1. 자의식 해체
2. 정체성 만들기
3. 유전자 오작동 극복
4. 뇌 자동화
5. 역행자의 지식
6. 경제적 자유를 얻는 구체적 루트
7. 역행자의 쳇바퀴


가장 주의 깊게 봤던 항목은 1, 3, 5 챕터이다.
이 글을 읽고 나머지 챕터가 궁금하다면, 책을 한번 읽어보는 것을 추천한다.


먼저 자의식 해체 이다. 말 그대로 자의식을 해체해야만 경제적 자유(성공)를 누릴 수 있다.
이 말이 무엇일까? 사람들은 본능적으로 자기를 방어 하기 위한 행동을 취한다.
예를들어 주식투자를 하다가 큰 손해를 봤다고 가정했을 때 대부분의 사람들은 다음과 같이 행동한다.
- 곧 상승하겠지 기다려보자.
- 이래서 주식은 하면 안돼 등...


합리적인 방법으로는 어떤 거래가 잘못되었는지, 어떤 실수를 범했는지 관련 지식을 공부할 수 있어야 한다.


내가 생각했을 때 이 챕터에서 하고자 하는 말을 정리하자면 다음과 같다.

어떠한 일(실패에서 오는 자기 합리화, 잘난 사람에게서 오는 질투심 등)에서 느껴지는 불편함(묘한 이질감 등)을 정확하게 인지하고 이를 해결해 나갈 방법(관련 지식을 공부해서 실패하지 않도록, 더 나은 사람에게서 배울점을 찾을 수 있도록)을 찾는것이 자의식 해체이다.


말로만 들었을 때는 참 쉽지만. 나 역시 떠올려보면 자의식 과잉이 많았던 것 같다.


다음은 3챕터이다.
간단하게 정리하면 유전자 오작동 극복 = 인간은 도전을 꺼린다. (그렇게 진화해왔다)
이러한 오작동을 극복하는 것 3챕터의 주요 내용이다.

대부분의 사람들은 새로운 것에 도전하는 것에 핑계를 대지 않는가?
(이건 전문가만 할 수 있어, 이미 레드오션이야 등등)
해당 챕터에서는 일단 실행하기를 강조하고 있다. 왜냐하면 어차피 95% 사람들은 시도조차 하지않고 있으니까. 시도하는 순간부터 역행자가 될 가능성이 있는것이다.

마지막으로 5챕터는 내용이 많지만 그 중 한가지만 공유하자면 대부분의 사람들이 상위 1%센트의 재능을 타고나긴 어렵다. 그래서 노력이 가능한 상위 20%센트의 능력 여러개를 조합하여 0.1%의 무언가를 만들어 낼 수 있다는 내용이다. 책 타이탄의 도구처럼
나처럼 상위 1%가 아닌 사람도 0.1%가 될 수 있다니 참 매력적이라 생각했다.
3챕터가 결국 5챕터와도 연관이 있는 내용임이 증명되는 부분이라 생각했다.

오랜만에 노력하고자 하는 마음을 불태우는 책을 읽고 정리하는 시간을 가져본다.

반응형

'일상 > 독서' 카테고리의 다른 글

[독서] - 원띵  (0) 2023.12.06
[독서] - 메이크 타임  (1) 2023.11.04
[독서] 창업가의 특성  (0) 2023.08.17
[독서] 클루지  (0) 2023.06.27
[독서] - 포커스 리딩  (0) 2023.06.20
반응형

1. Transaction에서 rollback이 되지않는 경우는?

- checked exception은 트랜잭션에서 롤백되지않는다. (명시적인 예외처리가 필요한 것, try catch해야하는것 들)

- unchecked exception은 롤백대상 (예외처리를 하지 않아도 IDE에서 에러 뱉지 않음, Runtime Exception...)

 

2. checked exception 경우에도 rollback을 하고싶으면?

만약 B에서 에러가 발생했을때 A를 rollback시키고 싶다면? 이럴때 transaction을 이용한다

하지만 B의 exception이 checked exception이라면?

@Transactional
public void test() {
    A();
    B();
}

만약 아래처럼 try catch를 이용하면 에러를 catch에서 처리했기 때문에, rollback이 발생하지 않는다.

@Transactional
public void test() {
    A();
    try {
    	B(); // checked exception을 발생시키는 부분
    } catch {
    
    }
}

 

- @Transcational 어노테이션에 rollbackFor라는 옵션을 이용한다

해당 에러는 throw하고, rollbackFor옵션을 사용하면 checked exception도 확인할 수 있게 된다.

@Transactional(rollbackFor = {Exception.class})
public void test() {
    A();
    B(); // checked exception을 발생시키는 부분에서는 error throw
}

 

- checked 예외를 unchecked 예외로 변경하여 throw한다

public void B() {
    try {
    
    }catch(Exception e) {
    	throw new RuntimeException("예외");
    }
}


@Transactional
public void test() {
    A();
    B(); // unchecked exception으로 바꿈
}

 

3. 아래와 같은 경우에는 롤백이 일어날까 일어나지 않을까?

	// ATest 클래스
    @Transactional
    public void A() {
        try {
            test.B();
        } catch (RuntimeException e) {
            System.out.println("예외 처리");
        }
    }
    
    
    // BTest클래스
    @Transactional
    public void B() {
        //로직
        mapper.save();
        throw new RuntimeException("예외"); // unchecked exception
    }

 

해당 코드는 다음과 같이 동작한다.

1. A클래스의 트랜잭션이 실행된다.

2. B메소드가 실행되면서 1번의 트랜잭션에 참여한다 (기본 propagation 속성이 PROPAGATION_REQUIRED)

3. save를 실행하는 부분의 처리가 끝나고 트랜잭션의 완료처리 (completion)을 진행함

4. checked Exception이 일어나면서 트랜잭션이 완료처리 됨

5. checked Exception때문에 해당 트랜잭션을 롤백 규칙을 적용(기본 규칙적용), 해당 메소드에서 바로 롤백하지않고 rollback mark를 함 (해당 마크는 전역으로 관리함)

6. A의 로직 수행 > 1번에서 생성된 트랜잭션의 완료처리가 진행, 이때 rollback mark를 확인해서 값이 true라면 rollback진행

따라서 이경우에는 롤백되어버린다

 

해당 propagation 옵션을 변경할경우 해당 rollback mark 문제를 해결할 수 있다

 

PROPAGATION_REQUIRES_NEW 사용시 매번 새로운 트랜잭션 생성

- A와 B가 각각 트랜잭션을 생성하고, 매 번 commit하므로 서로의 rollback에 영향을 미치지 않음

하지만 오버헤드 발생할 수 있음 (매번 새로운 커넥션 생성)

반응형
반응형

mybatis Cursor란?

- 공식문서에 다음과 같이 설명하고 있다

A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.

(Cursor는 Iterator를 사용하여 Lazy하게 데이터를 가져오고 이는 List와 동일한 결과를 제공한다)

 

간단 정리 : 대량 데이터(대량 ROW)를 가져올때 사용하는 방법

- 약 1천만건의 데이터를 List에 담아서 어떠한 처리를 한다고 가정했을때 기존 방법은 OOM 발생 > CURSOR사용하면 해결 가능

 

1. cursor를 사용할때 동작방식의 차이

 

- 기존 방식의 동작 방법

1. DAO Mapper 인터페이스 선언을 바탕으로, Mybatis가 동적으로 생성한 코드로 DB작업을 준비

2. DAO Mapper를 통해 DB작업이 진행되면 알맞은 드라이버 (ex: JDBC)나 풀을 통하여 작업을 수행

3. 2.의 작업이 완료될때까지 코드 블로킹 (통상 service 코드 블로킹)

4. DB작업이 끝나면 spring 으로 돌아옴 (서비스로)

- Cursor를 이용했을 때 동작 방법

1. DAO Mapper 인터페이스 선언을 바탕으로, Mybatis가 동적으로 생성한 코드로 DB작업을 준비

2. DAO Mapper를 통해 DB작업이 진행되면 알맞은 드라이버 (ex: JDBC)나 풀을 통하여 작업을 수행

3. 트랜잭션을 시작, cursor의 경우 2의 작업이 cursor로 iteration을 반복할 수 있는 상태가 되면 모든 데이터가 받아지지 않더라도 DAO interface를 통해 cursor를 반환한다. (반환한 cursor를 서비스에서 처리 후 > 다시 반복)
4. DB 커넥션이 유지되는 동안 필요한 작업을 수행. cursor가 데이터셋의 끝에 도달할때까지 반복 가능

5. 트랜잭션 종료 (커넥션 종료)

 

 

3번에 따라 JVM 메모리에 한번에 모든 결과를 올려둘 필요가 없으므로, 충분한 시간만 주어진다면 조회 데이터 수가 많더라도 OOM 없이 데이터를 모두 읽어서 처리할 수 있다.

 

2. 사용 방법

기존 코드를 다음과 같이 개선하여 사용할 수 있다.

 

- 기존 Mapper 코드

List<TestDto> selectTest();

- 개선 Mapper 코드

Cursor<TestDto> selectTest();

 

- 기존 서비스 코드

@Service
@RequiredArgsConstructor
public class testService {
    @Autowired
    private TestMapper testMapper;

    public void test() {
    	List<TestDto> list = testMapper.selectTest();
        ...
        ....
    }
}

 

- 개선 서비스 코드

@Service
@RequiredArgsConstructor
public class testService {
    @Autowired
    private TestMapper testMapper;

	@Transactional
    public void test() {
    	try( Cursor<TestDto> list = testMapper.selectTest()) {
        	for (TestDto dto : list) {
            //....
            //..
            
            }
        } catch (Exception e) {
        	// ..
        }
    }
}

왜 이렇게 개선되어야 할까? 이는 앞서 말한 동작방식을 보면 알 수 있다.

Cursor 는 한줄씩 데이터를 처리할 수 있게 해준다고 앞에서 언급했는데, 이는 즉 데이터 처리가 끝나면 다음 줄을 읽어와야하는것을 의미한다.

따라서 전체 데이터를 모두 순회 할때까지 DB 연결이 유지되어야 한다는 걸 의미한다.

 

정리해보면

1. 해당 서비스 메소드에 @Transactional 을 달아 트랜잭션 상태를 유지시킨다

2. Service 메소드를 벗어나기전 Cursor를 써야하는 작업을 모두 마쳐야한다.

 

3. cursor와 fetchsize의 관계

네트워크 통신보다 메모리에 있는 내용을 처리하는 속도가 훨씬 빠름 따라서 얼만큼 처리할 데이터를 메모리에 올려놓는지 적절히 조율이 필요함

  • 통신 빈도를 줄인다 - 통신 한번에 받아올 데이터의 양이 늘어난다(캐시를 많이 해야하므로 JVM 메모리를 많이 먹는다)
  • 통신 빈도를 늘린다 - 통신 한번에 받아올 데이터의 양이 줄어든다(캐시를 적게 해도 되므로 JVM 메모리를 적게 먹는다)

 

4. 내가 겪었던 상황

 

테이블 A, 테이블 B의 데이터를 JOIN해서 select 해오고 있었음

A에 약 100만건, B에 1000만건의 데이터가 있다고 가정

 

JOIN하는 순간부터 너무 많은 시간이 걸림

반응형
반응형

JWT 사용시 csrf.disable()를 한다.

Cross-Site Request Forgery

이용자가 의도하지 않은 요청을 통한 공격을 의미한다.

즉, CSRF 공격이란, 인터넷 사용자(희생자)가 자신의 의지와는 무관하게 공격자가 의도한 행위(등록, 수정, 삭제 등)를 특정 웹사이트에 요청하도록 만드는 공격이다.

 

- session, cookie를 이용하는 경우, 서버에서 사용자가 올바른지 검증하게 된다. 서버에서 검증을 진행하다보니 의도하지않은 공격이 있는지 확인하는 절차가 필요하지만,

JWT의 경우 토큰값에 정보들이 들어있어 서버에서 별다른 검증이 필요하지않으므로 csrf를 disable해도 무방하다.

 

- spring security에서도 권장함

반응형
반응형

대용량 트래픽 처리 시스템이란?

- 하나의 서버, 데이터베이스로 감당하기 힘든 부하를 처리하는것 > 다수의 서버와 데이터베이스를 마치 하나인것처럼 동작하게한다 (이안에는 여러 마이크로서비스들 포함)

 

대용량 트래픽 처리를 위한 특징 세가지

1. 고가용성

- 언제든 서비스를 이용할 수 있어야함

 

2. 확장성

- 시스템이 비대해짐에 따라 증가하는 데이터와 트래픽에 대응할 수 있어야한다.

 

3. 관측 가능성

- 문제가 생겼을 때 빠르게 인지할 수 있어야하고 문제의 범위를 최소화 할 수 있어야함

 앞서 DB의 병목현상에 대해 알아봤는데, 점진적으로 대용량 트래픽 처리 시스템을 발전시켜보자

1. 기본 구성

2. 사용자의 증가로인해 서버의 응답속도가 느려짐 > 서버의 스케일 아웃

해당 서버의 부하를 분산하는건 로드밸런서(nginx...)의 역할 (RR알고리즘....등)

 

3. 서버를 충분히 늘렸음에도 DB응답속도가 느려짐

> 쿼리튜닝(인덱스 등), 로컬캐시 (각 서버의 메모리에 캐싱), 글로벌캐시 (redis)

글로벌 캐시 예시

캐시를 이용할경우, 주기, 만료정책 등을 고려해야함

 

> DB 다중화 (스케일 아웃) 아키텍처 참고 https://blog.naver.com/takane7/221440417322

 

웹 어플리케이션 시스템 아키텍쳐의 변화

초기 아키텍쳐에서부터 시작해 클러스터링 아키텍쳐로의 확장을 고려하기까지의 스토리를 다뤄보고자 합니...

blog.naver.com

4. 이메일, 알림과 같은 대외기관 서버와의 연동이 많이 필요 해짐, 대외기관의 응답이 느려짐

> 클라이언트는 대외기관의 응답을 기다리느라, 요청을 기다리게 됨

비동기 큐 = kafka rabbitmq...(스레드풀을 이용한 비동큐도 가능)

비동기 큐에 요청을 쌓으므로서, 대외기관과의 트랜잭션을 클라이언트 요청에서 제외 시킬 수 있음 (내 서버에 데이터에만 의존하게 됨)

비동기 큐를 사용하면, 대외기관의 적정 TPS의 맞게 요청량을 조절할 수 있음

반응형
반응형

웹 아키텍쳐에서 DB는 병목 지점이다. (대용량 처리시 아키텍쳐를 어떤식으로 구성해야하는지 고민해보자)

 

1. 스케일업(scale-up), 스케일 아웃 (scale-out)이란?

 

  • 하나의 서버의 사양을 보다 높은 사양으로 업그레이드하는 것을 말한다.(더 좋은 성능으로 더많은 요청을 처리)

스케일업

  • 서버의 대수를 늘려 부하를 분산시켜 더많은 요청을 처리

스케일 아웃

 

  스케일업 스케일 아웃
유지보수 및 관리 쉬움 여러 노드를 관리해야함 (분산 필요)
확장성 제약있음 (서버의 성능을 높이는데 한계) 자유로움
장애복구 서버가 1대, 다운타임 존재 장애에 탄력적인 대응 가능

 

※ 스케일 아웃시 같은 입력에대해서는 항상 같은 결과를 반환해야함

- 따라서 다음과 같은 구조를 띄게 된다.

데이터 베이스의 스케일 아웃은 어려운가?

- 스케일 아웃에는 상태가 없어야 하는데, DB에는 데이터의 상태가 존재함 따라서 서버보다 더많은 비용이 발생 (스케일 아웃이 가능한 방법이 존재함)

※ 참고 https://willseungh0.tistory.com/131

 

따라서 서버 아키텍쳐는 상태관리는 DB에 위임 서버는 상태 관리를 하지않는 형태로 발전

 

DB는 왜 병목지점?

  • 앞서 말한 스케일 아웃이 어렵기 때문에 병목지점이 될 수 있음
  • DB는 디스크에서 데이터를 가져오기 때문에 속도가 상대적으로 느림 (병목지점이 될 수 있음)
  • 서버와 DB사이에 네트워크 구간이 존재 (네트워크 상황에 따라 요청속도가 느려질 수 있음)
반응형
반응형

1. 엘라스틱 서치 쿼리에대해 간단하게 정리하고자 한다.

 

기본적으로 아래와 같은 형태를 띄고 있다.

- query: 기존 DB의 where 조건문에 해당 (query, filter)

- size: 0으로 설정할경우 결과는 보지않고 집계결과만 보겠다는 의미

- aggs: 집계 부분 기존 쿼리의 Group by에 해당

GET _search
{
  "query":{
  },
  "size":0,
  "aggs": {
  }
}

 

집계 정리

1. SELECT count(*) FROM test group by userNo

- 유저 번호로 그룹핑한 결과를 보고 싶을때

{
  "size":0,
  "aggs": {
    "user_no (집계 대상 이름)" : {
      "terms": {
        "field": "user.no"
      }
    }
  }
}

결과

  "aggregations" : {
    "aa" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : 0,
          "doc_count" : 66
        },
        {
          "key" : 107,
          "doc_count" : 14
        }
        ...
        이하 생략

 

2. userNo 값으로 집계한 결과를 대상으로 특정 url로 집계하고 싶을때

{
  "size":0,
  "aggs": {
    "aa" : {
      "terms": {
        "field": "user.no"
      },
      "aggs": {
        "url_name": {
          "terms": {
            "field": "url.keyword"
          }
        }
      }
    }
  }
}

결과

  "aggregations" : {
    "user_no" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : 0,  # 유저별 그룹핑
          "doc_count" : 664745,
          "url" : {
            "doc_count_error_upper_bound" : 94,
            "sum_other_doc_count" : 4659,
            "buckets" : [ # url별 그룹핑
              {
                "key" : "http://aaa.com",
                "doc_count" : 655
              },
              {
                "key" : "https://test.com/",
                "doc_count" : 469
              },
              {
              .. 이하생략

 

3. 일별, 사용자별로 그룹핑 한다면? (2번은 집계한 결과의 집계, 3번과차이가 있음)

{
  "size":0,
  "aggs": {
    "abnormal_status" : {
      "composite": {
        "sources": [
         # time을 일별로 묶어줌, calendar_interval = day, week, hour..
          { "date": { "date_histogram": { "field": "time", "calendar_interval": "day", "format": "yyyy-MM-dd"}}},
          { "name": { "terms": { "field": "userNo" } } }
        ]
      }
    }
  }
}

 

결과

 "aggregations" : {
    "abnormal_status" : {
      "after_key" : {
        "date" : "2022-07-14",
        "name" : 2
      },
      "buckets" : [
        {
          "key" : {
            "date" : "2022-01-03",
            "name" : 0
          },
          "doc_count" : 1
        },
        {
          "key" : {
            "date" : "2022-05-30",
            "name" : 0
          },
          "doc_count" : 1
        }
        ... 이하생략

 

4. count(distinct column) > 사용자별로 (그룹핑) 몇개의 ip를 가지고있는가 (count(distint))

{
  "size":0,
  "aggs": {
    "user" : {
      "terms": {
        "field": "userNo"
      },
      "aggs": {
        "ip": {
          "cardinality": {
            "field": "ip"
          }
        }
      }
    }
  }
}

결과

"aggregations" : {
    "multi_ip_top_n" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : 0,
          "doc_count" : 45,
          "ip_cardinality" : {
            "value" : 3
          }
        },
        {
          "key" : 2,
          "doc_count" : 9,
          "ip_cardinality" : {
            "value" : 2
          }
        },
        {
          "key" : 25,
          "doc_count" : 5,
          "ip_cardinality" : {
            "value" : 2
          }
        },
        .. 이하 생략

 

5. sum, avg, min, max > status

status를 사용할경우 앞의 4가지를 전부 구할 수 있다.

  "size":0,
  "aggs": {
    "user_no" : {
      "terms": {
        "field": "user.no"
      },
      "aggs": {
        "sum": {
          "sum": {
            "field": "queryCnt"
          }
        },
        "avg": {
          "avg": {
            "field": "queryCnt"
          }
        },
        "min": {
          "min": {
            "field": "queryCnt"
          }
        },
        "max": {
          "max": {
            "field": "queryCnt"
          }
        }
      }
    }
  }
}
{
  "query":{
    "bool":{
      "filter": [
        { "range": { "reqTime": { "gte": 20210623000000, "lte": 20220723235959 }}}
      ]
    }
  },
  "size":0,
  "aggs": {
    "user_no" : {
      "terms": {
        "field": "user.no"
      },
      "aggs": {
        "stats": {
          "stats": {
            "field": "queryCnt"
          }
        }
      }
    }
  }
}

 

집계시 기억해야 할 점

 

엘라스틱 서치에서 제공하는 기본 집계의 수는 10000건이다. 이 이상으로 설정할 수 있지만 권장하지 않는다하고, 따라서 이에 맞게 결과를 도출하려 해야한다.

 

그럼에도 불구하고 10000건이상, 혹은 전체를 불러오고 싶을경우?(ex 페이징처리가 필요하다)

 

composite집계를 이용하여 처리할 수 있다.

앞선 composite 집계 결과를 보면 다음과 같이 after_key를 포함하고있다. 집계시 해당 키값을 넣어주게 되면 해당 키 다음부터 조회할 수 있다.

이런 방법으로 전체를 조회하거나 원하는 페이지를 조회할 수 있다.

 "aggregations" : {
    "abnormal_status" : {
      "after_key" : {
        "date" : "2022-07-14",
        "name" : 2
      },
      "buckets" : [
        {
          "key" : {
            "date" : "2022-01-03",
            "name" : 0
          },
          "doc_count" : 1
        },
        {
          "key" : {
            "date" : "2022-05-30",
            "name" : 0
          },
          "doc_count" : 1
        }
        ... 이하생략

 

{
  "size":0,
  "aggs": {
    "abnormal_status" : {
      "composite": {
        "sources": [
         # time을 일별로 묶어줌, calendar_interval = day, week, hour..
          { "date": { "date_histogram": { "field": "time", "calendar_interval": "day", "format": "yyyy-MM-dd"}}},
          { "name": { "terms": { "field": "userNo" } } }
        ],
        "size": 10000,
        "after": #해당부분에 afterKey값을 넣어주면 그 위치부터 시작함
      }
    }
  }
}

 

※ 이외에 알아둘만한 집계 함수

percentiles > 해당 퍼센트에 해당하는 기준값이 얼마인지 알 수 있다.

    "percentiles_bucket" : {
      "values" : {
        "75.0" : 75,
        "80.0" : 80,
        "85.0" : 85,
        "90.0" : 90,
        "95.0" : 95,
        "99.0" : 99
      }
    }

script사용

아래와 같이 집계중 script를 사용할 수 있는 집계들이 있다.

아래 집계는 time 필드를 HH형태로 바꾸어 집계한 결과이다.

ex 20220805124333 > 12

  "aggs": {
    "hourly_privacy_count" : {
      "terms": {
        "script": {"source": "doc['time'].value.getHour()"}
      }
 }
반응형
반응형

자바스크립트의 호이스팅에 대해 알아보는 시간을 가진다.


1. 호이스팅 (Hoisting)

- 자바스크립트에서 선언하는 변수값은 모두 유효 범위의 최상단으로 선언된다 = 즉 나는 밑에서 선언했지만 제일 상단에서 선언한것처럼 동작한다. 라는 의미를 가진다.

  • 자바스크립트 Parser가 함수 실행 전 해당 함수를 한번 훑는과정을 거친다.
  • 함수 안에 존재하는 변수/함수 선언에 대한 정보를 내부적으로 끌어올려서 처리한다(코드상의 변화가 있지는 않음)

 호이스팅의 대상

  • var 변수 선언, 함수 선언문에서 호이스팅이 일어난다.
    • let, const 에서는 호이스팅이 일어나지 않음 (ES6에서 개선) ※ 호이스팅은 일어나나, 발생하지 않는것처럼 참조 에러가 발생함

변수는 어떤식으로 생성 될까?

1. 변수의 선언

  • 변수를 실행 컨텍스트의 변수 객체에 등록함.

2. 초기화 단계

  • 변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다.
  • 이때 변수는 undefined로 초기화 된다.

3. 할당 단계

  • 실제 undefined로 초기화된 변수에 값을 할당한다.

 

앞서 말한 var키워드는 선언, 초기화가 한번에 이루어진다. 즉 스코프에 변수를 등록하고 공간을 확보한 후 undefined로 초기화한다. 하지만 let키워드는 선언과 초기화가 분리되어 진행되므로, 초기화 이전에 접근하면 에러가 발생한다.

 

- 앞서 이론적인 부분을 코드적으로 이해해보도록 하자

변수 선언에서의 호이스팅 예시

console.log(val); //선언 + 초기화
val = 'text'; //선언 + 초기화 + 할당
var val = 'hi';

- 해당 코드를 js가 아닌 다른 언어로 생각했을경우 1 번째 line에서 에러가 발생해야한다 하지만 결과는 undefined값이 찍힌다. 이유는 앞선 선언 + 초기화가 되어있기 때문이다

console.log(val); // 호이스팅에 의해 끌어올려졌지만 초기화는 안된 상태
let val = 'hi'

let을 사용하게되면 선언만 이루어지고 초기화는 되지않아서 에러가 발생한다.

 

함수 선언에서의 호이스팅 예시

test(); // 함수 선언문 정상동작 호이스팅 O
test2(); // 함수 표현식 오류 발생 호이스팅 X 

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

var test2 = function() {
	console.log('test2');
}

- 함수 선언문같은 경우 호이스팅이 된다. 하지만 함수 표현식의 경우 호이스팅이 이루어지지 않는다.

함수 선언문의 경우 = 함수 전체가 최상단으로 호이스팅 된다. 하지만 함수 표현식의 경우 변수가 호이스팅 되기때문에 에러가 발생한다

 

클래스의 호이스팅

그렇다면 ES6에 추가된 클래스는 어떨까? (let, const가 변수 선언 전에 사용하면 에러가발생하듯 class의 경우도 오류가 발생할까?)

let test = new Test('hi'); // ReferenceError: Person is not defined
console.log(peter);

class Test {
  constructor(val) {
    this.val = val;
  }
}

생각한 대로 클래스는 ReferenceError를 내뱉는다. 함수 선언문의 경우 호이스팅 되어 이전에 사용할 수 있었지만. ES6에서 추가된 클래스의 경우는 호이스팅 되어도 초기화가 되지않기 때문에 (let, const 처럼) 에러를 내뱉는다.

 

- 마찬가지로 클래스 표현식 let val = class Test {....} 로 정의할 경우도 에러를 발생시킨다.

 

이상 간단한 호이스팅 정리였다.

반응형

+ Recent posts