• 주의 1. 이 글은 async / await와 아무런 관련이 없습니다.
  • 주의 2. 이 글에는 멍충함이 묻어있을 수 있습니다.
  • 주의 3. 테스트 방법이 잘못됐을 수 있습니다.
  • 주의 4. 위의 이유로 예고없이 삭제될 수 있습니다.
  • 주의 5. 수정할 사항이 있다면 댓글에 남겨주세요. 검증 후 본문을 업데이트 하겠습니다.

 

의심

큰 프로젝트를 앞두고 PoC를 진행하다 기본적인 부분을 돌아보는 시간을 갖게 되었습니다. 어플리케이션에서 전반적으로 사용하는 기능을 개발해야하는데 평소에 알고있다 생각했던 로딩과 실행에 관한 기본적인 것들을 확인하던 중, 제가 알고있던 사실을, 제가 검증한 적이 없다는 것을 깨닫고 스펙을 확인한 후 나름의 환경을 구성하여 테스트를 진행했습니다.

script의 async / defer 요소

제가 검증하려던 부분은 스크립트의 로딩과 실행이었습니다. 모듈, 의존되는 기능 실행, DOM 핸들링에 관하여 테스트를 실행하다 보니 자연스럽게 async와 defer를 테스트하게 되었습니다. 우선 script의 async와 defer는 무엇인지, 어떤 특징과 차이를 가지는지 알아보겠습니다.

 

출처 / MDN

 

async | HTML5

For classic scripts, if the async attribute is present, then the classic script will be fetched in parallel to parsing and evaluated as soon as it is available.
For module scripts, if the async attribute is present then the scripts and all their dependencies will be executed in the defer queue, therefore they will get fetched in parallel to parsing and evaluated as soon as they are available.
This attribute allows the elimination of parser-blocking JavaScript where the browser would have to load and evaluate scripts before continuing to parse. defer has a similar effect in this case.
This is a boolean attribute: the presence of a boolean attribute on an element represents the true value, and the absence of the attribute represents the false value.
See Browser compatibility for notes on browser support. See also Async scripts for asm.js.

 

defer

This Boolean attribute is set to indicate to a browser that the script is meant to be executed after the document has been parsed, but before firing DOMContentLoaded.
Scripts with the defer attribute will prevent the DOMContentLoaded event from firing until the script has loaded and finished evaluating.
This attribute must not be used if the src attribute is absent (i.e. for inline scripts), in this case it would have no effect.
The defer attribute has no effect on module scripts — they defer by default.
Scripts with the defer attribute will execute in the order in which they appear in the document.
This attribute allows the elimination of parser-blocking JavaScript where the browser would have to load and evaluate scripts before continuing to parse. async has a similar effect in this case.

(*한글 번역이 매끄럽지 않아 영문으로 가져왔습니다.)

 

MDN과 '노란책'으로 통용되는 '프론트엔드 개발자를 위한 자바스크립트 프로그래밍'에서 async와 defer에 관하여 언급한 내용을 요약하면 async는 HTML5에 등장, 즉시 다운로드 후 사용이 가능한 시점에 바로 실행, defer는 HTML 4.01에서 소개, 즉시 다운로드하지만 문서가 준비되었을 때 실행한다 입니다.

 

출처 / async vs defer attributes

 

 

 

위의 설명과 이미지를 보면 스크립트의 async와 defer 다운로드와 실행 순서는 async는 다운로드 된 이후, 다큐먼트가 준비된 것과는 별개로 실행 가능한 시점에 바로 실행이 될것이고 defer는 다운로드 후 문서의 콘텐트가 모두 완료된 후, DOMContentLoaded 전에 실행되리라 짐작할 수 있습니다. 그런데 꼭 그렇지만은 않았습니다.

환경 구축

예제파일

 

위의 링크에서 예제파일을 다운로드 및 설치 후 npm run start를 입력하시면 간단한 테스트베드에서 실제로 동작하는 모습을 콘솔창을 통해 확인할 수 있습니다. index.html을 확인하면 head의 script 구문들과 body 안에서 inline으로 작동하는 스크립트 확인이 가능하며, head의 script 작성 순서는 다음과 같습니다.

 

  • index.js / global function definition
  • async.js / async option
  • defer.js / defer option
  • default / no options

 

우리가 앞서 살펴본 내용으로 추론하자면,

1) index.js는 다큐먼트 구문 분석을 멈추고 실행

2) async.js는 다운로드 즉시 실행되므로 async.js에서 정의한 색상(red)의 div는 페이지에서 확인이 불가

3) defer.js는 다큐먼트가 준비된 후 실행되므로 마지막에 실행

4) default.js는 문서 작업을 멈춘 후 바로 실행될 것이므로 async.js와 동일하게 페이지에서 확인이 불가

 

할 것으로 예상했습니다.

검증

파이어폭스, 사파리, 크롬으로 해당 테스트를 진행했습니다. (IE는 여건상 할 수 없었습니다.) 아래는 테스트를 진행한 결과의 스크린샷 입니다.

 

 

예상했던 대로 테스트 결과, 브라우저 별로 작동하는 방식이 달랐습니다. 하지만 너무나 다릅니다. index.js를 제외한 스크립트의 실행 순서는 다음과 같았습니다.

 

파이어폭스
ASYNC -> DEFAULT -> INLINE / head -> INLINE / body -> INLINE / after header -> INLINE / after section -> INLINE / after footer -> DEFER -> DOMContentLoaded -> load

 

사파리

DEFAULT -> INLINE / head -> INLINE / body –> INLINE / after header -> INLINE / after section –> INLINE / after footer -> DEFER -> DOMContentLoaded –> ASYNC -> load

 

크롬

DEFAULT -> INLINE / head -> INLINE / body -> INLINE / after header -> INLINE / after section -> INLINE / after footer -> ASYNC -> DEFER -> DOMContentLoaded -> load

 

추가적으로 모든 브라우저에서 계속 동일한 결과를 주지는 않았습니다. 가끔은 실행 순서가 바뀌고 캐시를 삭제하지 않은 페이지 재진입은 크롬에서 꽤 높은 확률로 async가 defer 보다 뒤에서 실행되었습니다. 이것은 단어를 어떻게 이해했느냐와 관련된 문제일 수도 있겠지만 너무나 당연시 여기던 저의 '상식'이 잘못되었음을 깨닫게 되었습니다. 여기서 확실히 다짐한 점은, '절대 스크립트 로딩과 실행을 option 값으로만(async, defer) 판단하고 사용하지 말자'입니다.

결론

'알고있는 내용이 알고있던 것과 다를 수 있음을 기억하자'

 

파이어폭스 조차 페이지 리로드를 계속해서 하다보면 '빨간 박스'가 페이지에 노출될 때가 많습니다. 물론 스크립트 파일을 내려받고 실행하는 것이 동일한 기능을 한다해도 늘 같은 속도를 보장하지는 않겠지만 이를 의식하지 않고 암기한 내용을 사용하면 큰 낭패를 볼 가능성이 보입니다. 해당 문제에 관한 내용은 추후 포스팅을 통해서 그 이유를 추적하고자 합니다. (물론, 언제할지는 미정입니다만...)

 

무엇이든 신뢰하지 말자는게 아니라 확실히 알고 사용해야 한다에 초점을 두고 알고있던 기능도 한번쯤은 이렇게 테스트 해보는 계기가 되었으면 합니다.

! 시니어는 구간을 의미하지도 기술을 대변하지도 않는다. 기간과 실력 모두에서 멀어질 수 없지만 또 절대적이지 않다는 의미다.

 

@ 어딘가에 해결하지 못한 문제가 있다. 그 문제에 관하여 누군가는 시간이 부족하다고, 아니면 또 다른이는 우리 중 해결할 사람이 없다고 말한다.

 

# 정말 그럴 수 있다. 내가, 혹은 우리가 가진 자산과 환경이 그렇지 않을 수 있다. 급격하게 팽창하는 조직은 더 그렇다.

 

$ 그러면 포기해야 하는가?! 아니다, 그렇지 않다. 누군가를 다독이고 또 어떠한 솔루션을 함께 찾아주고 미리 경험했던 것, 혹은 알고 있던것을 전파하고 공유할 수 있다.

 

% 미리 경험한다는 것은 단순히 경력이 길어서가 아니다. 연차가 쌓인다고 우리가 세상을 위해, 또는 무언가를 이루려고 했던 모든 행위의 폭이 더 넓어지는 것은 아니다. 누군가 생각만으로 해결책을 찾을 때 다른 누군가는 코드스니펫을 만들어 직접 해결을 시도한다.

 

^ 흔히 말하는 '레거시'를 대하는 태도는 시니어와 주니어를 보다 거시적으로 구분해준다. 전임자가 작성한 코드를 보면서 그 이유를 되새기고 그럴 수 밖에 없었던 치열함을 인정하는 사람과 알수없는 코드를 욕하기만 하는 사람.

 

& 클린 코드는 '리얼 월드'에서, 또는 '프로덕션'에서 그렇게 쉽게 찾아볼 수 있는것이 아니다. 그렇다고 상상 속에만 있는 것은 아니다. 우리가 지향해야 하는 일은 클린 코드 그 자체가 아니라 상황을 인지하고 하나씩 하나씩, 점진적으로 약속을 늘려가고 그것을 공표하는 것, 클린 코드는 볼 수 없지만 좋은 코드베이스를 향하는 것을 멈추지 않는 일이다.

 

* 그렇다면 그러한 것을 누가 하는가?? 힘이 들어 자신을 믿지 못할 때 본인을 믿게하고 답이 보이지 않는 일을 그래도 찾을 수 있을거라며 먼저 앞장서는 일, 코드 분석이 안될 정도로 처참하게 파편화된 코드를 보며 정리할 수 있는 부분을 캐치하는 역할, 그게 바로 시니어다. 시니어는 경력으로 인해 자동적으로 획득되는게 아니다.

 

좋은 시니어는 여지껏 없었다는 말을 듣거나 접할 때마다 그렇다면 넌 좋은 주니어였는지 물어보고 싶었다. 물론 꼰대소리가 싫어 피했지만...

 

어차피 나하고는 상관없는 이야기, 난 좋은 시니어도, 좋았던 주니어도 아니었다. 그냥 모니터만 바라보며 욕받이만 했을 뿐...

리액트를 다루는 기술을 보면서 학습하던 중 중요한 부분들은 블로그에 정리 중에 있습니다. 아직 정리가 덜 끝나 간단한 부분 몇개만 공개를 하고 추후(언제 될지는 몰라요...) 포스팅 하도록 하겠습니다. 오늘은 어플리케이션 개발에 많은 혼란을 가져오는 부분 중 하나인 라이프 사이클에 대한 정리 부분을 공개합니다.

라이프 사이클

모든 리액트 컴포넌트에느 라이프사이클이 존재. 컴포넌트의 수명은 페이지에 렌더링되기 전인 '준비 과정'에서 시작하여 페이지에서 사라질 때 '끝'남.

메서드

  1. Will 접두사 - 어떤 작업을 작동하기 전에 실행되는 메서드
  2. Did 접두사 - 어떤 작업을 작동한 후 실행되는 메서드

컴포넌트의 라이프 사이클

1. 마운트 - 페이지에 컴포넌트가 나타남

  • 마운트 과정에서 호출하는 메서드

    1. constructor: 컴포넌트를 새롭게 만들 때 호출되는 클래스 생성자
    2. getDerivedStateFromProps: props의 값을 state에 넣을 때 사용하는 메서드
    3. render: UI를 렌더링하는 메서드
    4. componentDidMount: 컴포넌트가 웹 브라우저상에 나타난 후 호출하는 메서드

2. 업데이트 - 컴포넌트 정보를 업데이트 (리렌더링)

  • 업데이트가 일어나는 경우

    1. props가 바뀔 때
    2. state가 바뀔 때
    3. 부모 컴포넌트가 리렌더링될 때
    4. this.forceUpdate로 강제 렌더링을 트리거할 때
  • 업데이트가 일어나는 경우 호출하는 메서드

    1. 업데이트를 발생시키는 경우가 실행된 경우 (위의 원인)
    2. getDerivedStateFromProps: (마운트 과정 및 업데이트가 시작하기 전 호출) props의 값을 state에 넣을 때 사용하는 메서드
    3. shouldComponentUpdate: 컴포넌트 리렌더링을 결정하는 flag. true 반환 시 라이프 사이클의 다음 메서드 실행 / false 반환 시 작업중지. this.forceUpdate() 함수를 호출하면 이 과정을 생략하고 바로 render 함수 호출
    4. render: 컴포넌트 리렌더링
    5. getSnapshotBeforeUpdate: 업데이트 반영 전 snapshot을 가져오는 메서드
    6. componentDidUpdate: 컴포넌트의 업데이트 작업이 끝난 후 호출하는 메서드

3. 언마운트 - 페이지에서 컴포넌트가 사라짐

  • 언마운트 과정에서 호출하는 메서드

    1. componentWillUnmount: 컴포넌트가 브라우저에서 사라지기 전 호출하는 메서드

출처: 리액트를 다루는 기술

'Notes' 카테고리의 다른 글

[React / What is react.js??] 리액트  (0) 2020.07.14

+ Recent posts