클린코드 - 03. 함수

 

어떤 프로그램이든 가장 기본적인 단위가 함수이다.

우리는 어떤 함수를 읽었을때 프로그램 내부를 직관적으로 파악할 수 있을까?

 

작게 만들어라

  • 블록과 들여쓰기
    • 중첩 구조가 생길 만큼 함수가 커지면 안된다. (if else문 등 주의하여 쓰자)
    • 블록 안에서 호출하는 함수 이름을 적절히 짓는다면 코드를 이해하기 쉽다.
  • 한가지만 해라
    • 예시: 1. 페이지가 테스트 페이지인지 판단 => 2. 설정 페이지와 해제 페이지를 넣는다 => 3. 페이지를 HTML로 렌더링한다.
      • 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한가지 작업을 한다고 할 수 있다.
      • 더이상 줄이기가 불가능 하며 if, else를 따로 뺀다고 해도 다른 표현일 뿐 추상화 수준이 바뀌지 않는다.
  • 함수 당 추상화 수준은 하나로!
    • 함수가 확실히 '한가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야한다.
    • 수준이 섞여있을 경우 근본 개념인지 세부사항인지 구분하기 어려워져 추후에 사람들이 함수에 세부사항을 점점 더 추가하게 된다.
  • 위에서 아래로 코드 읽기 (내려가기 규칙)
    • 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
    • 즉 위에서 아래로 읽으면 함수 추상화 수준이 한 단계씩 낮아진다.

 

Switch문

  • 다형성을 이용한다.
  • 상속관계를 숨긴 후 절대로 다른 코드에 노출하지 않는다.

 

서술적인 이름을 사용하라

  • 길고 서술적인 이름이 짧고 이해하기 어려운 이름보다 좋다. (일관성있게!)

 

함수 인수

  • 이상적인 인수 개수는 0개 (무항)
    • 4개 이상은 특별한 이유가 필요하다. (이유가 있어도 사용해서는 안된다..)
  • 플래그 인수
    • 함수 안에 여러가지 일을 처리하는 것을 의미한다.
    • 함수로 부울 값을 넘기는 것은 좋지않다.
      • render(boolean isSuite) => renderForSuite(), renderForSingleTest() 로 함수를 나누는 것이 좋다.
  • 이항, 삼항 함수
    • 인수가 많은 함수는 적은 함수보다 이해하기 어렵다.
    • 단항에 비해 위험이 따른다.
      • 순서, 무시로 야기되는 문제가 생길 수도 있다.
  • 동사와 키워드
    • 함수 이름에 인수의 순서, 의도를 넣는것도 좋다.
      • assertEqual(expected, actual) => assertExpectedEqualsActual(expected, actual) 이렇게 할 경우 인수의 순서를 기억할 필요가 없다.

 

부수 효과를 일으키지 마라

  • checkPass-word일 경우 암호 확인 작업만 하라
    • 세션 초기화하는 로직이 있을 경우 문제가 생길 수 있으므로 따로 initializeSession으로 함수를 분리하는 것이 좋다.

 

명령과 조회를 분리하라

  • 수행하거나 답하거나... 분리하라!

 

오류 코드 보다 예외 케이스를 사용하라

  • 명령 함수 에서 오류코드를 반환하는 방식은 명령, 조회 분리 규칙을 미묘하게 위반한다
if (deletePage(page) = E_OK) {
    if (registry.deleteReference(page.name) = E_OK) {
      if (configKeys.deleteKey(page.name.makeKey()) = E_OK){ 
          logger.log('page deleted');
      } else {
          logger.log('configKey not deleted');
      }
    } else {
    	logger.log('deleteReference from registry failed');
    }
} else {
	logger.log('delete failed'); 
    return E_ERR0R;
}

// 수정
try {
  deletePage(page);
  registry.deleteRefe rence(page.name); 
  configKeys.deleteKey(page.name.makeKey());
}catch (Exception e) { 
	logger.log(e.getMessage());
}

 

Try/Catch 블록 뽑아내기

  • 정상 동작과 오류 처리 동작을 뒤섞는다. 그러므로 별도의 함수로 뽑아내는 것이 좋다.
public void delete(Page page) {
  try {
  	deletePageAndAllReferences (page);
  } catch (Exception e) { 
    logError(e);
    private void deletePageAndAHReferences(Page page) 

    throws Exception { 
      deletePage(page);
      registry.deleteReference(page.name); 
      configKeys.deleteKey(page.name.makeKey());
    }
    private void logError(Exception e) { 
      logger.log(e.getNlessage());
	}
}
  • 오류 처리도 한가지 작업이다.
  • Error.java 의존성 자석
    • enum 열거형 타입을 사용할 경우 의존성이 생겨서 문제가 될 수 있다. (import 해서 사용할 떄 변경이 생기면 매번 재 컴파일 해야하는 문제가 생길 수 있다.)
  • 반복하지 마라
    • 중복되는 코드를 없앨경우 가독성이 높아진다.
    • 객체 지향 프로그래밍은 코드를 부모 클래스로 몰아 중복을 없앤다.
  • 구조적 프로그래밍
    • 모든 블록에 입구와 출구는 하나만 존재해야한다
      • return 문이 하나여야한다. 하지만 함수가 작다면 사용해도 괜찮다.
      • goto문은 피해야한다.
  • 함수를 어떻게 짜죠?
    • 먼저 생각한대로 기록한 후 읽기 좋게 다듬는다. 초안은 대개 서투르고 어수선하므로 원하는 대로 읽힐때까지 다듬고 정리한다.

 

 

내 생각....

역시 책은 내가 사용하는 언어로 된 것을 읽는 것이 가장 베스트인 것같다.

모르는 부분들 혹은 공감하지 못하는 부분들이 존재하는 것을 서서히 느끼고 있다.

하지만 큰 틀은 모두 동일하니 완독하는 것이 목표다.

다른 작업들도 같이 하고 있어 언제 완독할지는 모르겠다.

아 그리고 지금까지 읽은 부분으로 주변 사람들과 가끔 대화를 했는데, 

대부분은 맞는 말이지만 아니라고 생각하는 부분들이 존재하기때문에 융통성있게 이점을 배워가면 될 듯하다.

클린코드 - 02. 의미있는 이름

의도를 분명히 밝혀라

변수(혹은 함수나 클래스)

  • 존재이유?
  • 수행기능?
  • 사용방법?

이 모든 것을 답하기 위해 주석이 필요하다면 의도를 분명히 드러내지 못했다는 뜻.

 

그릇된 정보를 피하라

그릇된 단서는 코드의 의미를 흐린다

  • 나름대로 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용해도 안된다
    • 실제 컨테이너가 List가 아닐 경우 List로 명명하면 그릇된 정보를 제공하므로 이렇게 명명하지 않는다
    • 실제 List여도 컨테이너 유형의 이름에 넣지 않는 것이 바람직하다
  • 유사한 개념은 유사한 표기법을 사용한다
    • 이름만 보고 정보를 추측하기 때문에 일관성이 떨어지는 표기법은 그릇된 정보다
    • 연관성이 없는 것에 대해 비슷한 단어를 사용했을 경우 연관이 있다고 생각하는 오류를 범하기 때문에 흡사한 이름을 사용하지 않는다.
  • I과 O는 숫자와 유사하기 때문에 그릇된 정보가 될 수 있다

 

의미있게 구분하라

단순 컴파일러나 인터프리터를 통과하는 네이밍은 문제를 일으킨다.

  • 연속적인 숫자를 덧붙이지 않는다
    • a1, a2, ....aN
    • 의도적인 이름과 정반대이며 아무런 정보를 제공하지 못하는 이름이다
    • 저자의 의도가 드러나지 않는다
  • 불용어를 추가한 이름은 아무런 정보도 제공하지 못한다
    • Info, Data는 a, an, the와 마찬가지로 의미가 불분명한 불용어이다
      • 접두어를 사용하지 말라는 소리가 아니다
      • Name, NameString에서 Name은 String일 확률이 대부분 이므로 불용어다
      • customerInfo - customer, accountData - account, TheMessage - message와 구분이 안되기 때문에 차이를 알 수 있도록 한다 

 

발음하기 쉬운 이름을 사용하라

발음하기 어려운 이름은 토론하기도 어렵다

  • 긴 단어를 합쳐서 줄임말로 쓸 경우 의사소통에 문제가 생길 수도 있다

 

검색하기 쉬운 이름을 사용하라

문자 하나를 사용하는 이름(짧은)보다 길더라도 검색해서 찾아내기 쉬운 이름이 좋다

  • e로만 검색할 경우 무수히 많은 검색 결과가 나온다
  • 버그가 있을 경우 찾기 쉬운 이름이 짧은 이름보다 때론 좋다

 

인코딩을 피하라

  • 헝가리식 표기법
    • 에디터 등의 발전으로 변수 이름에 타입을 인코딩할 필요가 없다
    • 타입을 바꾸기가 어려워지고 읽기도 힘들다
    • 독자를 오도할 가능성도 커진다
  • 멤버 변수 접두어
    • 클래스와 함수는 접두어가 필요없을 정도로 작아야한다
  • 인터페이스 클래스와 구현 클래스

 

자신의 기억력을 자랑하지 마라

  • 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야한다면 바람직하지않다
    • 일반적인 변수 이름이 아니었기에 생기는 문제
  • 문자 하나만 사용하는 변수 이름은 문제가 있다
    • 루프에서 범위가 작고 다른 이름과 충돌하지 않을 때는 괜찮다
      • 루프에서 반복 횟수 변수는 전통적으로 한 글자를 사용하기 때문에...
    • a와 b를 사용한다고 가정했을때 c를 선택해 사용한다면 최악이다

 

클래스 이름

  • 명사나 명사구가 적합하며 동사는 사용하지 않는다.
  • Manager, Processor, Data, Info 등과 같은 단어는 피한다

 

메서드 이름

  • 동사나 동사구가 적합
  • Javabean 표준에 따라 값 앞에 get, set, is를 붙인다

 

기발한 이름은 피하라

  • 의도를 분명하고 솔직하게 표현해야한다

 

한 개념에 한 단어를 사용하라

  • 추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다
    • fetch, retrieve, get | controller, manager, driver으로 제각각 쓰면 혼란스럽다
  • 이름이 다를 경우 당연히 클래스, 타입도 다르다 생각한다

 

말장난을 하지마라

  • 한 단어를 두 가지 목적으로 사용하지 마라
    • add가 하나의 값에 더한다는 의미라면 집합에 값을 추가하는 경우에는 add보다 insert나 append라는 이름을 사용한다

 

해법 영역에서 가져온 이름을 사용하라

기술 개념에는 기술 이름이 가장 적합한 선택이다

  • 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 괜찮다

 

문제 영역에서 가져온 이름을 사용하라

'프로그래머 용어'에 없다면 문제 영역에서 이름을 가져와도 괜찮다

 

의미있는 맥락을 추가하라

스스로 의미가 분명한 이름이 없지 않다

  • 접두어를 붙인다
    • firstName -> addrFirstName이라는 접두어를 통해 주소의 일부라는 사실을 분명히한다

 

불필요한 맥락을 없애라

애플리케이션 네임을 모든 클래스의 이름의 시작에 넣는 것은 바람직하지 않다

 


 

 

문헌

클린코드 - 로버트 C. 마틴

클린코드 - 01. 깨끗한 코드

원초적 난제

기한에 맞추려면 나쁜코드를 양산할 수 밖에 없다고 느낄 것이다.

하지만 코드를 최대한 깨끗하게 유지하는 습관이 기한 맞추는 유일한 방법일지 모른다.

 

깨끗한 코드라는 예술?

"깨끗한 코드가 무엇일까?"

"어떻게 작성할까?"

'깨끗한 코드'가 무엇인지 조차 모른다면 노력해봤자 소용없다.
하지만 이 말이 '깨끗한 코드'와 '나쁜 코드'를 구분할 줄 알아야만 '깨끗한 코드'를 작성할 수 있다는 의미는 아니다.
‘코드 감각’이 있다면 개선할 방안이 떠오를 것이다.

다만 '코드 감각'은 누군가는 타고날수도 있고 누군가는 경험을 통해 얻을 수도 있다. 

 

깨끗한 코드란?

비야네 스트롭스트룹

"우아하고 효율적인 코드를 좋아한다."

우아하고 효율적인 코드를 좋아한다. 효율적인 코드는 단순 속도만을 의미하지 않고 CPU의 자원또한 포함할 것인다.

함수가 너무 많은 일을 할 경우 의도가 뒤섞이고 목적이 흐려진다. 한가지를 제대로해야하며 논리가 간단해야 버그가 숨어들지 못한다. 또한 서로에 대한 의존성을 줄여야 유지보수가 쉬워진다.

많이 놓치는 오류처리, 메모리누수, 경쟁상태, 일관성 없는 명명법 등도 신경써야하며 오류는 전략에 의거해 처리한다. 

성능을 최적으로 유지해야만 원칙없는 최적화를 하지않게된다. 원칙없는 최적화는 코드를 망칠 확률이 있다.

 

그래디 부치

"깨끗한 코드는 단순하고 직접적이다. 
가독성을 강조하고 잘 쓰여진 문장처럼 읽혀야한다."

깨끗한 코드는 단순하고 직접적이다. 

가독성을 강조하여 잘 쓰여진 문장처럼 읽혀야하며 설계자의 의도를 숨기지 않고 명쾌한(사실에 기반하여) 추상화와 단순한 제어문을 사용한다.

*명쾌한: 힘차고 단호하고 사실적인

 

데이브 토마스

"테스트 없는 코드는 깨끗한 코드가 아니다."

작성자가 아닌 사람이 읽기는 물론 고치기도 쉬울만큼 가독성 있는 코드가 깨끗한 코드이다.

읽기 쉬운 것과 고치기 쉬운 것은 엄연히 다르다.

테스트 주도 개발(TDD)이 오늘날 가장 근본적인 원칙 중 하나가 된 것을 보아라, 테스트 없는 코드는 깨끗한 코드가 아니다.

- 의미 있는 이름을 붙이고 목적을 달성하는 방법을 하나만 제공하라.

- 의존성은 최소로하고 각 의존성을 명확히 정의한다.

- API는 명확하며 최소로 줄인다. 최소는 큰 코드보다 작은 코드에 가치를 둔다는 의미로 코드는 작을 수록 좋다.

- 언어마다 모든 정보를 코드만으로 표현할 수 없기에 코드는 문학적(인간이 읽기 좋게)으로 표현해야한다


마이클 페더스

"깨끗한 코드란 언제나 누군가 주의 깊게 짰다는 느낌을 주며
고치려봐도 손 댈 곳이 없다."


론 제프리스

"중복이 없으며 간단한 코드, 단순한 코드 규칙으로 시작하고 끝낸다."

초반부터 간단한 추상화를 고려하며 시스템 내 모든 설계 아이디어를 표현한다.

의미 있는 이름으로 명명하며 객체나 메서드가 여러 기능을 수행한다면 여러개로 나누고 클래스, 메서드, 함수 등을 최대한의 크기로 줄인다.

모든 테스트를 통과해야한다.

 

워드 커닝햄

"코드가 그 문제를 풀기위한 언어처럼 보인다면 아름다운 코드라 불러도된다."

코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드다.

 

우리들 생각

"깨끗한 변수 이름, 함수, 클래스"

 

 

결론

책을 읽는다고 뛰어난 프로그래머가 된다는 보장은 없다.

그저 뛰어난 프로그래머가 생각하는 방식과 그들이 사용하는 기술, 기교, 도구(경험적 교훈, 체계, 절차, 기법 등)를 소개하는 것을 보고 난 이후는 본인들이 하기 나름이다.

 


“연습해, 연습!”

 


문헌

클린코드 - 로버트 C. 마틴

"프론트엔드 전반" 포스팅을보고 공부하면서 요약해보자!

 

 

 

 

CSR(Client Side Rendering)과 SSR(Server Side Rendering)

SPA (Single Page Application)

  • 하나의 HTML 파일을 기반

  • 자바스크립트를 이용해 동적으로 화면의 컨텐츠를 바꾸는 방식

 

MPA (Multiple Page Application)

  • 사용자가 페이지를 요청할 때마다 웹 서버가 요청한 UI와 필요한 데이터를 HTML로 파싱해서 보여주는 방식

  • 전통적인 방식

    • SPA - CSR

    • MPA - SSR

 

CSR

출처 :  https://medium.com/@adamzerner/client-side-rendering-vs-server-side-rendering-a32d2cf3bfcc

 

SSR

출처 :  https://medium.com/@adamzerner/client-side-rendering-vs-server-side-rendering-a32d2cf3bfcc

 

 


 

브라우저의 렌더링 과정

원리

렌더링엔진이 HTML, CSS, Javasript 렌더링 시에 CRP(Critical Rendering PAth) 프로세스로 진행

 

과정

HTML 파싱, DOM(Document Object Model) 트리 구축

*HTML중간에 Javascript가 있을 경우 HTML 파싱 중단

.CSS 파싱,

CSSSOM(CSS Object Model) 트리 구축

Javscript 실행

 

DOM + CSSSOM = Render 트리 구축 * display:none 과 같은 요소들은 렌더트리로 구축되지 않는다.

viweport 기반으로 Render 트리의 각 노드가 가지는 정확한 윛, 크기 계산

브라우저에 그려줌

 

자바스크립트 엔진이 코드를 실행하는 과정

 

출처 : https://mathiasbynens.be/notes/shapes-ics

  1. 소스 파싱 *AST(Abstract Syntax Tree)로 변환

  2. 인터프리터(Interpreter)는 AST기반으로 바이트 코드(Bytecode)생성

  3. 인터프리터가 바이트코드를 실행할때 자주사용되는 함수, 타입정보등의 프로파일링 데이터(Profiling Data)와 같이 최적화 컴파일러(Optimizing Compiler)에게 보낸다.

  4. 최적화 컴파일러가 프로파일링데이터 기반으로 최적화된 코드를 생성한다.

    • 프로파일링데이터가 잘못되었다면 최적화해제 후 다시 바이트 코드를 실행해서 이전 동작을 반복한다.(3번)

AST란?

더보기

추상구문 트리(Abstract Syntax Tree), 구문 트리(Syntax Tree)라고 한다

프로그래밍 언어로 작성된 소스코드의 추상 구문 구조의 트리이다.

이 트리의 각 노드는 소스 코드에서 발생되는 구조를 나타낸다.

추상적이기 때문에 실제 구문에 나타나는 모든 세세한 정보를 나타내지 않는다.

출처  https://ko.wikipedia.org/wiki/%EC%B6%94%EC%83%81_%EA%B5%AC%EB%AC%B8_%ED%8A%B8%EB%A6%AC

 

인터프리터(Interpreter)란?

더보기

고급언어로 작성된 코드를 실행하는 것에는 두가지 방법이 있다.

1. 인터프리터

2. 컴파일

 

프로그래밍 언어의 소스코드를 바로 실행하는 컴퓨터 프로그램 또는 환경으로 인터프리터에 고급언어를 통과시켜 고급 명령어들을 중간 형태로 번역한 다음 실행할 수 있다.

원시 코드를 기계어로 번역하는 컴파일러와 대비된다.

아래 과정 중 한가지 기능을 가진 프로그램이다.

- 소스 코드 직접 실행

- 소스 코드를 효율적인 다른 중간 코드로 변환, 변환한 것을 바로 실행

- 인터프리터 시스템의 일부인 컴파일러가 만든(미리 컴파일된) 저장 코드의 실행을 호출

 

출처 : https://ko.wikipedia.org/wiki/%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0

 

바이트코드(Bytecode)란?

더보기

특정 하드웨어가 아닌 가상 컴퓨터에서 돌아가는 실행프로그램을 위한 이진 표현법

출처 : https://ko.wikipedia.org/wiki/%EB%B0%94%EC%9D%B4%ED%8A%B8%EC%BD%94%EB%93%9C

 

프로파일링 데이터(Profiling Data)란?

더보기

데이터 현황정보를 통계적으로 분석하는 것...

(??????????? 이런의미가 맞나)

출처 : http://www.dbguide.net/knowledge.db?cmd=specialist_view&boardUid=184480&boardConfigUid=114&boardStep=0&categoryUid=

 

최적화 컴파일러(Optimizing Compoiler)란?

더보기

컴파일러에서 출력되는 실행프로그램의 효율성을 최적화하는 과정

출처 : https://ko.wikipedia.org/wiki/%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC_%EC%B5%9C%EC%A0%81%ED%99%94

 

 

 

BOM(Browser Object Model) 과 DOM(Document Object Model)

BOM

  • 브라우저 창, 프레임을 프로그래밍적으로 제어할 수 있게해주는 객체 모델

 

DOM

  • 웹 페이지를 프로그래밍적으로 제어할 수 있게 해주는 객체 모델

    • 텍스트나 주석도 포함

 

 

 

 

모듈 번들러와 트랜스파일러

모듈번들러

  • 모듈단위로 파일을 엮어서 개발하는 방식

 

트랜스파일러

  • 특정언어로 작성된 코드를 비슷한 다른 언어로 변환시키는 것

  • ES6이상의 기능을 하위 브라우저가 지원하지 않기 때문에 ES5로 변환할때, 트랜스파일러가 이 작업을 수행한다.

    • React, Vue, Typescript 등도 마찬가지

  • 보통 모듈번들러에 트랜스파일러를 추가 사용하는 방식을 사용한다.

 

 

 

CI와 CD

CI (Continuous Integration, 지속적 통합)

  • 빌드와 테스트를 자동화해서 공유 저장소에 병합시키는 프로세스

 

CD (Continuous Delivery/Deploy, 지속적 전달/배포)

  • CI 빌드, 테스트가 정상적으로 수행되었을때 빌드에 대한 프로세스를 뜻한다.

  • CI/CD의 대표적인 서비스로 Jenkins, Travis CI, Circle CI 등이 있다.

    • 지속적 전달 : 수동으로 배포

    • 지속적 배포 : 자동으로 배포

 

출처 :  https://aws.amazon.com/ko/devops/continuous-integration/

 

 

 

 

CSS와 JS 애니메이션의 차이점

웹 페이지에 애니메이션 효과를 부여할 때

  • CSS : transition / animation 

  • JS : setInterval() / requestAnimationFrame()

 

CSS 애니메이션

  • 간단한 애니메이션 처리시 사용된다.

    • 지속적으로 하나의 객체의 style 속성의 x, y 좌표값의 이동을 변경하는 예제를 진행할 때 JS 애니메이션을 사용한다면 setInterval을 통해 작업해야하며

  • 브라우저의 렌더링 과정에서 reflow(layout)단계를 발생 시켜 부자연스러운 느낌을 받을 수 있다. 이때 CSS 애니메이션으로 구현하는 것이 더 좋다고 할 수 있다.
    • JS는 메인쓰레드에서 작업하지만 CSS는 별도의 컴포지터 쓰레드에서 그려지기 때문에 효율적이다.

 

메인 스레드(Main Thread)

더보기

JS 실행, DOM 엘리먼트 렌더링 담당, 주어진 작업이 끝나기전 다른작업이 모두 블락

 

컴포지터 스레드(Compositor Thread)

더보기

GPU를 이용해 렌더링된 엘리먼트를 화면어 그리는 역할, 특정부분이 블락되면 해당 영역을 빈 부분으로 대체한 후 다른영역을 진행

 

JS 애니메이션

CSS로 처리하기 복잡하고 무거운 작업에 효율적이고 세밀한 작업 시 사용된다.

바닐라 JS로 구현 시 비효율적인 부분이 존재하며 60fps를 보장할 수 없다. (사용자가 느끼는 자연스러운 프레임?)

이 때문에 RAF(RequestAnimationFrame) API가 등장했다.

구현방식은 동일하며 60fps를 보장할 수 있다.

이 외에도 외부 라이브러리나 Web Animations API(지원브라우저가 적다)를 통해 성능좋은 애니메이션을 구현할 수 있다.

* 하드웨어 가속이 모바일에서 성능저하를 발생할 수 있는데, GPU를 통한 하드웨어 가속을 제어할 수 있다. 이는 CSS 특정 속성으로 인한 가속을 막아준다.

 


추가 공부 Keyword

렌더링 : CSR, SSR,  SPA, MAP, Parser

BOM, DOM

requestAnimationFrame

Web Animation API

 

 

프론트엔드 전반취준생이 반드시 알아야 할 프론트엔드 지식들-프론트엔드 전반

https://github.com/baeharam/Must-Know-About-Frontend

+ Recent posts