Javascript Pattern 함수

이 글은 JavaScript Patterns Build Better Applications with Coding and Design Patterns 책을 학습 후 중요 부분을 요약한 것입니다.

Background

  • 자바스크립트의 함수는 일급 객체다.
  • 함수는 유효범위를 갖는다.
  • 함수는 다음과 같은 특징을 갖는 객체다.
    • 프로그램 실행(런타임) 중에 동적으로 생성한다
    • 변수에 할당할 수 있고, 다른 변수에 참조를 복사할 수 있고, 확장가능하고, 삭제할 수 있다.
    • 다른 함수의 인자로 전달할 수 있고, 다른 함수의 반환 값이 될 수 있다.
    • 프로퍼티와 메서드를 가질 수 있다.
  • 자바스크립트에서 함수는 하나의 객체
  • 자바스크립트는 중괄호 {} 의 유효범위가 함수 내부를 제외하곤 없다.

용어

  • 기명 함수 (named function expression)
    var add = function add (a, b) {
    return a + b;
    };
    
  • 무기명 함수 (unnamed function expression) & 함수 표현식 (function expression)
    var add = function (a, b) {
    return a + b;
    };
    
  • 함수 선언문 (function declaration)
    function foo() {
    // body
    }
    

콜백 패턴

  • 함수는 객체이기 때문에 아래처럼 함수를 다른 함수에 콜백 형태로 전달할 수 있다.
    function writeCode (callback) {
    callback();
    }
    
    function introduceBugs () {
    // body...
    }
    writeCode(introduceBugs);
    // ! introduceBugs() 를 넘기게 되면 함수가 호출이 되므로 함수의 참조값만 넘긴다.
    
  • 대부분의 클라이언트 브라우저 프로그래밍은 event-driven 방식 : Don't call us, we'll call you

  • 아래는 타임아웃에 관한 안티패턴이다.

    var thePlotThickens = function () {
    console.log('500ms later...');
    }
    setTimeout(thePlotThickens(), 500); // 안티패턴
    // setTimeout(thePlotThickens, 500) 처럼 함수 포인터만 넘겨줘야 한다.
    

함수 반환하기

  • 함수 반환의 간단한 예
    var setup = function () {
    alert(1);
    return function() {
      alert(2);
    };
    };
    
    var my = setup(); // alert(1)
    my();             // alert(2)
    
  • 함수 반환에서 사용되는 클로저의 예
    var setup = function() {
    var count = 0;
    return function () {
      return (count += 1);
    };
    };
    
    var next = setup();
    next(); // 1
    next(); // 2
    next(); // 3
    

자기 자신을 정의하는 함수

  • 새로운 함수로 자기 자신을 덮어쓰는 경우 : 어플리케이션 성능에 많은 도움이 된다
    // Lazy Function Definition : 사용 시점 전까지 함수를 정의하지 않고 있다가, 호출된 이후에는 더 적게 일한다.
    var scareMe = function () {
    alert("Boo!");
    scareMe = function () {
      alert("Double boo!");
    };
    };
    
    scareMe();  // Boo!
    scareMe();  // Double Boo!
    
  • scareMe() 함수를 일급객체로 사용하는 예
    scareMe.property = "properly";
    var prank = scareMe;v
    var spooky = {
    boo: scareMe;
    };
    
    prank();  // Boo!
    prank();  // Boo!
    console.log(prank.property); // "properly"
    
    spooky.boo(); // Boo!
    spooky.boo(); // Boo!
    console.log(spooky.boo.property); // "properly"
    
    scareMe();  // Double Boo!
    scareMe();  // Double Boo!
    console.log(scareMe.property);  // undefined
    

즉시 실행 함수

  • 즉시 실행함수는 선언됨과 동시에 실행된다. 자기 실행함수 라고도 한다.
  • 즉시 실행함수의 패턴은 다음과 같다.
    1. 함수를 함수 표현식으로 선언한다 (var a = function(){}; 타입으로는 동작하지 않는다.)
    2. 함수 마지막에 괄호쌍을 추가한다.
    3. 전체 함수를 괄호로 감싼다.
  • 즉시 실행함수는 코드 안의 모든 코드를 지역 유효범위로 감싸고, 어떤 변수도 전역 유효범위로 새어나가지 않게 한다.
    (function () {
    var days = ["Sun", "Mon"];
        today = new Date();
        msg = "Today is" + days[today.getDay()];
    
    alert(msg);
    }());
    
    // days, today, msg 변수들의 범위는 모두 전역이 아니다.
    
  • 즉시 실행함수의 매개변수는 다음과 같이 전달한다.
    (function (who, when) {
    
    console.log("I met " + who + " on " + when);
    
    }("Joe Black", new Date()));
    
  • 일반적으로 즉시 실행함수에는 매개변수를 많이 전달하지 않는게 코드 가독성에 도웅이 된다.

즉시 실행함수의 반환 값

  • 다른 함수와 마찬가지로 즉시 실행함수도 값을 반환할 수 있고, 변수에 할당할 수 있다.

    var result = (function() {
    return 2 + 2;
    }());
    
    var getResult = (function () {
    var res = 2 + 2;
    return function () {
      return res;
    };
    }());
    
  • 장점과 사용방법 : 선언된 모든 변수는 스스로를 호출하는 함수의 지역변수가 되기 때문에, 임시 변수가 전역 공간을 어지럽힐까 걱정하지 않아아도 된다.

즉시 객체 초기화

  • 아래 패턴은 객체가 생성된 즉시 객체를 초기화 한다.

    ({
    maxWidth: 600.
    maxHeight: 400,
    
    gimmeMax : function () {
      return this.maxWidth + "x" + this.maxHeight;
    },
    init : function () {
      console.log(this.gimmeMax());
    }
    }).init();
    
  • 위 패턴의 장점은 초기화 하는 동안, 전역 네임스페이스를 보호할 수 있다.

  • 위 패턴의 단점은 자바스크립트 compression 시에 즉시 실행 함수 패턴에 비해 효과적으로 압축하지 못할 수 있다. 왜냐하면 비공개 프로퍼티와 이름이 더 짧게 변경되지 않는 것이 compression 도구 관점에서는 안전하기 때문.

초기화 시점의 분기

  • 초기화 시점의 분기(로드타임 분기)는 각각 브라우저의 기능을 확인하는 최적화 패턴이다.
  • 아래 코드처럼 각 브라우저의 기능 지원범위를 확인할 수 있다.

    var utils = {
    addListener: function (el, type, fn) {
      if (typeof window.addEventListener === 'function') {
        el.addEventListener(type, fn, false);
      } else if (typeof document.attachEvent === 'function') {
        el.attachEvent('on' + type, fn);
      } else {
        el['on' + type] = fn;
      }
    },
    removeListener: function (el, type, fn) {
      // ...
    }
    };
    
  • 브라우저의 기능은 독립적으로 변하기 때문에, 상기 코드로 초기화 시점 분기를 사용해 기능 지원여부를 판단한다.

설정 객체 패턴

  • 설정 객체 패턴은 좀 더 깨끗한 API 를 제공하는 방법. 라이브러리나 reuse 컴포넌트를 만들 때 유용하다.
  • addPerson() 이라는 함수를 만들 때, 인자수가 많아지면 다음과 같이 addPerson("First Name", "Last Name", ...) 함수 선언이 길어지므로 아래와 같은 패턴을 사용한다.

    addPerson(conf);
    var conf = {
    userName : "batman",
    first: "Bruce",
    last: "Wayne"
    };
    
  • 위 패턴의 장점은
    1. 매개변수와 순서를 기억할 필요가 없다.
    2. 선택적인 매개변수를 안전하게 생략이 가능하다.
    3. 매개변수 추가 제거가 편하다.
  • 위 패턴의 단점은
    1. 매개변수의 이름을 기억해야한다.
    2. 프로퍼티의 이름은 압축되지 않는다.

커리 (Curry)

함수 적용

  • 순수한 함수형 프로그래밍에서 함수는 호출된다고 표현하기 보다 적용된다고 표현한다.
  • 자바스크립트에서도 Function.prototype.apply() 를 이용하여 함수를 적용할 수 있다.
    var sayHi = function (who) {
    return "Hello" + (who ? ", "+ who : "") + "!";
    };
    
    sayHi();        // "Hello"
    sayHi("World"); // "Hello, World"
    sayHi.apply(null, ["hello"]); // "Hello, hello!"
    
  • apply 의 첫 번째 매개변수가 null 이면 this 는 전역 객체를 가리킨다.
    var alien = {
    sayHi: function(who) {
      return "Hello" + (who ? ", " + who : "") + "!";
    }
    };
    
    alien.sayHi("world"); // "Hello, world!"
    sayHi.apply(alien, ["humans"]); // "Hello, humans!"
    
  • 이 코드에서 sayHi() 내부의 this 는 alien 을 가리킨다. 앞선 예제의 this 는 전역 객체를 가리킨다.

  • call() 메서드 역시 apply() 메서드와 비슷하며, 함수의 매개변수가 하나일 때는 굳이 배열을 만들지 않고 아래와 같이 call() 을 이용한다.

    sayHi.apply(alien, ["humans"]); // "Hello, humans!"
    sayHi.call(alien, "humans");    // "Hello, humans!"
    
Advertisements

Legacy 웹 사이트에 Progressive Web App 적용하기 (Part I)

Why did I start to work on this project?

  • 배경 : 팀내에서 고객과의 질의응답 채널로 포탈 사이트를 운영하고 있다.
  • 현상 : 포탈 사이트가 개발된지 약 2년이 된 것 같은데 (소스를 보니 2014년에 만들어진 것으로 추정), 시대를 역행하는 Non Responsive Design 과 고객 Q&A에 Notification 기능이 없어 수시로 사이트 접속하여 게시글과 댓글을 모니터링 해야하는 불편함이 있었다.
  • 계획 : Mobile 에서도 간단한 게시글 확인 및 알람 기능을 추가하여 사용성을 늘리기로 결정했고, Progressive Web 의 몇가지 기능들을 넣어보기로 결정했다.

What is Progressive Web App?

  • 구글에서 최근에 밀어붙이는 웹앱으로 Responsive Design, App Like, Re-engagable, Installable, Safe, … 등의 특징과 함께 빠른 로딩과 높은 사용자 경험을 제공하는 것이 특징이다.
  • Mobile 에서만 지원되었던 Push 알림, Mobile Icon, 앱 설치 배너 등을 웹 어플리케이션에서 지원하기 때문에, 일반 웹 개발로 Mobile Application 의 Appy 한 느낌을 낼 수 있다.
  • 자세한 내용은 Google Web Fundamental 참고

Non Responsive to Responsive Design

  • 기존에 적용된 UI Framework 은 반응형이 지원되지 않는 Bootstrap 이다. 무슨 이유에서인지 모르겠으나 반응형 지원을 하지 않고, 철저히 PC 기준으로만 사이트를 제작하였다. 아마도, 모바일에서의 화면 Layout 까지 고려하기에는 시간이 빠듯했거나, 요구사항 자체가 Mobile 에서도 볼 수 있게 해달라는 요청이 없었기 때문인 것 같다.
  • Boostrap 외에도 jstree, jquery-ui, jquery-remodal, jquery-multifile, NHN Smart Editor 등의 외부 라이브러리들을 가져다가 화면을 구성해놓았다.

레거시 사이트 UI

1365 * 600 해상도 PC 화면

pc_login
PC 로그인 페이지
  • 그냥 로그인 기능만 되면 문제 없어보이는 UI…

 

pc_main
PC 게시판 목록
  • 전체적으로 균형은 잘 잡혀 있어 보인다.

 

pc_list
PC 트리 선택 후 게시판 이동
  • 왼쪽 트리를 펼쳐 게시판 목록을 펼친다.

 

pc_viewpost
PC 게시글 조회
  • 게시판 질의 응답 기능에만……. 충실한 화면 Layout 과 UI

iPhone 6 브라우저 화면

1
Mobile 로그인 페이지
  • 로그인 페이지는 그럭저럭 봐줄만 하다.

 

2
Mobile 게시판 목록
  • 모바일에서 보는 게시판 페이지는 거의 재앙에 가깝다.

 

3
페이지 확대해서 트리 선택
  • 페이지를 이동하려면 왼쪽 트리를 클릭해야 하는데, 페이지 확대를 해서 저 작은 리스트를 클릭해야 페이지 이동이 된다… 참 난감하다.

 

4
Mobile 에서 게시글 조회
  • 게시글 조회 시 게시글과 답변 모두 확대 하지 않고는 보기가 거의 불가능하다.

 

Re-designing UI & UX with Critical User Journeys

  • 지난 6월 Google Campus 에서 진행된 UI & UX 워크샵에서 배운 “Critical User Journeys” 기법을 이번 사이트에 적용 해보았다.
  • Critical User Journeys : 사용자의 입장에서 해당 서비스를 사용할 때 모든 동작에 대해 UI 와 Layout 을 고려하고 이를 기반으로 UI Design 을 가다듬는 것.
  • UI 개선을 위해 가장 첫 번째로 해야할 질문들 (The very first thing to do is ask these questions)
  1. Who is using your website? 누가 사이트를 사용하는가?
  2. When are they using their website? 사용자들은 사이트를 언제 사용하는가?
  3. Why are they using the website? 사용자들은 왜 사이트를 사용하는가?
  4. Where are they using the website? 사용자들이 사이트를 어디에서 사용하는가?
  • 사이트를 사용하는 유저 입장에서, 로그인 절차부터 게시판 및 기타 기능들을 사용할 때 까지 위의 질문들을 염두에 두고 다음과 같이 개편하였다.
  1. Mobile First Design
  2. 버튼에 아이콘을 추가하여 직관화
  3. 글쓰기 / 글읽기 / 답글달기 할 때 보여지는 기능들의 우선순위를 정하여 UI 컴포넌트 재배치
  4. 게시글 정보를 중요도 순으로 다시 배치하고 간결하게 표현
  5. 댓글에 자신의 사진을 표시하여 책임감 및 흥미 부여 (화장실에 담당자 사진 걸어놓는 것과 동일한 목적)
  6. 전체적인 Tone & Manner 를 맞추기 위해 기존 jsTree 라이브러리 제거 후 Collapsible 과 Collection 으로 트리 구현

개선된 사이트 UI

15인치 노트북 PC에 최적화된 화면

new_pc_login
PC 로그인 페이지
  • MaterializeCSS 의 기본 Login Template 을 적용했다.

 

new_pc_list
PC 게시글 목록
  • UI 의 전체적인 느낌을 통일하기 위해 왼쪽 jstree 라이브러리를 걷어내고, collipsible & collection 조합으로 왼쪽에 트리를 구현하였다.

 

new_pc_viewpost
PC 게시글 조회
  • 글 수정 / 삭제 / 목록 등의 버튼에 아이콘을 이용하여 직관적인 표시를 하였고, 답글과 댓글 영역을 명확히 분리하였다. 댓글 리스트는 우선순위 기준으로 정보를 재배치 및 계정에 프로필 사진을 추가하여, 댓글 구분이 쉽도록 하였다.

 

new_pc_delete
게시물 삭제시 권한 확인 팝
  • 기존 화면의 불필요한 영역을 차지하는 ID & PW 를, 게시글을 삭제할 때 확인하도록 변경했다.

 

iPhone 6 브라우저 화면

new_mobile_login
Mobile 로그인 페이지
  • PC Login 화면에서 크기만 작아진다.

 

new_mobile_main
Mobile 메인 페이지
  • 로그인 후 메인화면, 모바일에서는 폴더 트리를 Global Navigation 으로 뺐다.

 

new_mobile_list
Mobile 게시글 목록 페이지
  • 게시글 목록은 다음과 같이 표시된다. 테이블 cell 의 더 세부적인 css 스타일링으로 가독성을 높이는 작업이 더 필요한 것 같다.

 

new_mobile_tree
Mobile 네비게이션 바
  • Global Navigation Bar 를 이용하여 페이지 간 이동을 한다.

 

new_mobile_viewpost
Mobile 게시글 조회
  • Mobile 에서 유용하게 사용할만한 보기 & 댓글 기능에 주안점을 두고, 가독성을 높이는데 주력했다.

 

Back-End Service Analysis

기술 및 기법

  • 기존의 시스템은 Spring FW 3.4 로 구성이 되어 있었고, 다음과 같은 기술 및 기법 들을 쓰고 있었다.
  • Spring Security : 권한 관리를 위한 스프링 기술스택
  • Controller Annotation in bean.xml : base.package 에 지정된 패키지 안에 해당되는 모든 @Controller 에 대해 처리해준다.
  • ControllerAdvice Annotation in bean.xml : 위와 동일한 성격으로, 여기서는 Controller 에서 발생하는 에러 케이스 들에 대한 Exception 처리를 지정해주었다.
  • Multipart Resolver in beans.xml : 파일 또는 이미지를 Client 에서 Stream 방식으로 서버로 보낼 때, Stream 형식으로 받아 처리해줄 수 있는 Resolver
  • JDBC Template in applicationContext.xml : 스프링에서 DB 연결시 사용하는 전형적인 스프링 JDBC 연결방법
  • DataSource Transcation Manager in applicationContext.xml : 스프링에서 제공하는 다양한 Transaction Manager중의 하나
  • DBCP DataSource in applicationContext.xml : DB와 어플리케이션을 효율적으로 연결하는 커넥션 풀을 제공. 일정 커넥션을 유지하다가 필요하면 사용하고 반납하여 재사용하는 형태

문제점

  • 기존 레거시 시스템의 가장 큰 문제점은 특정 주기 간격으로, 서비스가 올라가 있는 Tomcat 을 주당 2회 정도 계속 재시작을 해줘야 했다.
  • 그 이유를 진단하기 위해 아래 쿼리를 넣었고
show status like `%connect%`;
  • 진단 결과는 다음과 같았다.

“페이지 마다 수행되는 쿼리에 대해서 커넥션이 닫히지 않고, 계속 커넥션 수가 누적된다.”

Back-End Service Refactoring

안티패턴 1

  • 위와 같은 문제점을 해결하기 위해 구현된 Spring 로직을 분석한 결과, 다음과 같은 안티패턴을 발견했다.
@Controller
public class BoardController extends ExceptionControllerAdvice {

private BoardService getBoardService() {
ApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");
return context.getBean("boardService", BoardService.class);
}

// ...
}
  • 위의 안티패턴은 해당 컨트롤러가 수행이 될 때 마다, ApplicationConext 를 매번 생성하여 메모리 낭비 및 DB 커넥션 수를 불필요하게 늘리게 된다.
  • 해결책 : ApplicationContext는 WAS (Web Application Server) 에 Spring Container가 올라가는 최초에만 생성되고, 이후에는 생성된 Context 를 공유하는 형식으로 바꿨다.
@Controller
public class BoardController extends ExceptionControllerAdvice {

@Autowired
private BoardService boardService; // Application Context 에서 생성된 BoardService 를 참조한다.

// ...
}

안티패턴 2

  • Controller.java 에서 해당 URL에 관한 로직을 처리 후 URL redirect 을 다음과 같이 한 안티패턴이 발견되었다.
@RequestMapping(value = "replyBoard", method = RequestMapping.POST)
public String replyBoard(
HttpSession session,
@RequestParam("folder") String folder,
@RequestParam("subCategory") String subCategory, ... ) throws UserException {
ModelAndView mv = new ModelAndView;
mv.addObject("folder",folder);
mv.addObject("subCategory",subCategory);
// ...

return "redirect:/viewPost?folder="+folder+"&subCategory="+subCategory;
}
  • ModelAndView 를 사용하여 redirect 를 할 경우에는 ModelAndView 에 추가된 Object 변수들이 redirect 시에 자동으로 붙지 않는다. 따라서, 이를 아래와 같이 Model model 을 이용하면,
@RequestMapping(value = "replyBoard", method = RequestMapping.POST)
public String replyBoard(
HttpSession session,
@RequestParam("folder") String folder,
@RequestParam("subCategory") String subCategory, Model model ) throws UserException {
model.addObject("folder",folder);
model.addObject("subCategory",subCategory);
// ...

return "redirect:/viewPost";
}

// URL 결과 : `viewPost?folder="folder"&subCategory="subCategory"`
  • 위처럼 Model 을 이용하여 View에 데이터를 넘겨주면, redirect 시에 자동으로 변수가 붙게되는 이점이 있다.

Progressive Web App Native Feature 적용은 Part 2 에서 이어집니다…

(번역) 훌륭한 프론트엔드 개발자가 되는 법 by Google Engineer

※ 이글은 훌륭한 프론트엔드 개발자가 되는 법에 대해서 Google Engineer PHILIP의 개인적인 의견을 기록한 글 중 주요부분을 발췌하여 번역한 것입니다.

[이 글의 목적]

In this article I’m going to talk about the mindset of a front-end engineer, and hopefully give a more lasting answer to the question: how do you become great?

어떻게 최고가 되는지 그리고 프론트엔드 개발자로서의 가져야할 마음가짐에 대해서 이 글을 적습니다.

[단순히 문제를 풀지말고, 어떤것이 일어나고 있는지를 보라]

I get that there are times when you need something that works, and you need it now. But if you never take the time to understand the root of your problem, you’ll find yourself in the same situation over and over again.

진짜 문제의 근원이 무엇인지 생각을 햅지 않는다면 그 상황을 계속해서 반복적으로 직면하게 될 것이다.

Taking the time to figure out why your hack works may seem costly now, but I promise it’ll save you time in the future. Having a fuller understanding of the systems you’re working within will mean less guess-and-check work going forward.

시간을 내어 왜 당신의 일이 많은 비용을 소모하는 지 생각해보세요, 이게 미래에 당신에게 더 많은 시간을 절약하게 해줄 것입니다. 시스템에 대한 완벽한 이해는 앞으로 갈수록 더 적은 점검과 예측을 하도록 도와줄 것 입니다.

[브라우저가 미래에 변하는 것을 예측하는 법을 배우라]

One of the main differences between front and back-end code is back-end code generally runs in an environment that’s under your control. The front end, by contrast, is completely outside of your control. The platform or device your users have could completely change at any moment, and your code needs to be able to handle that gracefully.

백엔드와 프론트 엔지니어의 차이점은 바로 프론트엔드 영역이 엔지니어가 제어할 수 없는 범위를 포함하고 있다는 것입니다. 웹서비스를 사용하는 사용자의 플랫폼과 디바이스에 따라서, 완전히 다른 환경이 펼쳐질 수 가 있는 것이죠. 따라서, 당신의 코드가 이러한 환경을 자연스럽게 제어할 수 있어야 합니다.

I remember reading through the source code of a popular JavaScript framework back in 2011 and seeing the following line (changed for simplicity):

var isIE6 = !isIE7 && !isIE8 && !isIE9;

In this case IE6 was the catchall for IE versions, presumably to handle versions of IE older than 6. But at soon as IE10 came out, large portions of our application completely broke.

여기 이 코드를 보면, 전혀 IE10을 고려하지 않은 코드여서 IE10가 나오자마자 바로 엄청난 양의 어플리케이션들이 다 망가지게 되었습니다.

[스펙을 읽어라]

In addition, so-called “great” front-end engineers are often the people on the forefront of change, adopting new technologies before they’re mainstream and even contributing to the development of those technologies

소위 말하는 훌륭한 프론트엔드 개발자들은 변화에 앞장서서 새로 등장한 기술들이 주요 기술이 되기전에 발견하여, 그 기술의 발달에 기여하는 사람들입니다.

[다른사람의 소스를 보아라]

Reading other people’s code, for fun, is probably not your idea of a fun Saturday night, but it’s without a doubt one of the best ways to become a better developer.

토요일 저녁에 다른사람의 코드를 재미로 읽는 것은 정말 재밌지 않은 일일지 모르지만, 그렇게 주말을 보냄으로써, 당신은 보다 나은 프론트엔드 개발자가 될 수 있습니다.

[당신보다 똑똑한 사람들과 일을 하라]

The problem with being both self-taught and also working for yourself is you generally don’t get the benefit of learning from people smarter than you. You don’t have anyone to bounce ideas off of or review your code.

혼자 배우고 일하는 것은 당신보다 똑똑한 사람들과 일을 했을 때의 이점을 전혀 얻지 못할 것입니다. 왜냐면, 당신의 코드를 리뷰해주고, 아이디어를 굴려줄 사람이 없기 때문이죠.

If you do end up working for yourself at some point in your career, make a point of becoming (or staying) involved in open source. Actively contributing to open-source projects gives you many of the same benefits of working on a team, sometimes even more.

그래서 혹시나 당신의 커리에어서 혼자 일할 시기가 온다면, 오픈소스에 기여해보세요. 팀으로 일하는 것보다 훨씬 많은 혜택을 가져다 줄 것입니다.

[바퀴를 재창조하라]

Reinventing the wheel is bad for business, but it’s great for learning. You may be tempted to grab that typeahead widget or event delegation library from npm, but imagine how much more you’d learn by trying to build those things yourself.

기존의 것을 재창조하는게 사업의 관점에서는 나쁠지 모르겠으나, 배우기에는 매우 좋은 기회입니다. 3rd Party Library 같은 것을 혼자 만들어보면 정말 많은 것을 배울 수 있거든요.

But in this article I’m talking about how to go from good to great. Most of the people I consider great in this industry are the creators or maintainers of very popular libraries that I use all the time.

제가 생각하는 이 분야의 최고인 사람들은 자주 사용되는 유명한 라이브러리의 제작자이거나 유지하는 분들입니다.

You could probably have a successful career without ever building your own JavaScript library, but you’ll probably also never work close enough to the metal to really get your hands dirty

당신이 아마 자바스크립트 라이브러리를 제작하지 않고도 훌륭한 커리어를 가질 수 있지만, 절대 훌륭한 프론트엔드 개발자가 되기 위한 필요한 경험들은 해보지 못할 것입니다.

[너가 무엇을 배웠는지 적어라]

Last but certainly not least, you should write about what you learn. There are so many good reasons to do this, but perhaps the best reason is it forces you to understand the topic better. If you can’t explain how something works, there’s a decent chance you don’t really understand it yourself. And oftentimes you don’t realize you don’t understand it until you try writing it down.

마지막으로, 배운 것을 꼭 기록하세요. 수많은 이유가 있겠지만서도, 가장 중요한 이유는 당신이 그 토픽을 이해하는데 많은 도움을 준다는 것이니다. 아마 직접 써보기 전까지는 당신이 그것을 제대로 이해하고 있지 않다는 것을 깨닫지 못할거에요.

from http://philipwalton.com/about/

PageSpeed Insights 에서의 모바일 분석

What is PageSpeed Insights?

  • 구글에서 제안하는 튜닝 가이드를 포함한 크롬 플러그인
  • 구글에서 진행한 연구결과에서 페이지 로딩시 일초 이상 지연이 발생하면 사용자에게 poor experience 를 초래한다고 나와있다.
  • PageSpeed Insights 는 사용자가 페이지를 접속했을 때 모바일 네트워크에서도 일초 내로 로딩될 수 있도록 자세한 가이드를 제공한다.
  • 여기서 말하는 1초 이내의 페이지 렌더링은 사용자가 웹 어플리케이션을 조작할 수 있도록 기본적인 ATF (Above The Fold) 콘텐츠를 로딩하는 것을 의미한다.
  • 첫 페이지만 일단 렌더링이 되서 사용자가 조작을 할 수 있으면, 나머지 페이지는 점차적으로 로딩이 되는 형식이다.

Adapting to high latency mobile networks

  • 모바일 웹을 접속하는 사용자들은 대부분 2G, 3G, 4G 등 다양한 네트워크를 이용한다.
  • 전 세계적으로 3G를 대부분 사용하고 있고, 4G는 아직 성장하고 있는 중이다.
  • 네트워크 지연시간은 아래와 같은 범위를 갖는다.
  • 3G 네트워크 : 200 ~ 300ms 의 왕복시간
  • 4G 네트워크 : 50 ~ 100ms 의 왕복시간
  • 여기서 브라우저와 서버와의 일반적인 통신 절차를 보면 아래와 같다. !

server_browser_communication

  • 위 표를 보면 네트워크 오버헤드에 고정적으로 600ms 시간이 쓰이는데, 내역은 아래와 같다.
  1. hostname (예. google.com) 을 IP 주소로 매칭하는 DNS 작업
  2. TCP handshake 수행을 위한 네트워크 왕복
  3. HTTP 요청을 보내기 위한 네트워크 왕복

Delivering the sub one second rendering experience

  • 위에서 설명한 네트워크 지연시간을 제외하고는 페이지 렌더링 처리를 위해 약 400ms가 남는다. 해당 시간안에 아래와 같은 내용을 처리해야한다.
  • 서버에서 응답을 준다
  • 클라이언트 사이트 코드가 실행 되어야 한다.
  • 브라우저가 콘텐츠의 레이아웃을 조정하고, 렌더링을 해야한다.
  • 이제 각 항목에 대해서 자세히 들여다보자.

(1) Server must render the response (< 200 ms)

  • 서버의 응답시간은 서버가 초기 HTML 파일을 클라이언트에 전달하는데 걸리는 시간이다.
  • Optimization 을 위한 시간이 많지 않기 떄문에 최대한 200 ms 이내가 걸리도록 한다.

(2) Number of redirects should be minimized

  • HTTP redirect 요청이 늘어날 때마다 최소 1개에서 2개의 네트워크 왕복이 늘어난다. (추가적인 DNS 이용이 필요할 시 2개)
  • 따라서, redirect 요청을 최소화 하거나 혹은 아예 없도록 구현한다. (HTML에서 "m dot" 하지 않도록 주의)

(3) Number of roundtrips to first render should be minimized

  • TCP 통신 특성
    • 서버는 첫 네트워크 왕복에서 TCP 패킷을 10개까지 보낼 수 있다. (~14KB)
    • 서버가 보낸 요청을 클라이언트가 인식하기까지 약간의 대기 시간이 발생한다.
  • 이러한 TCP 특성 떄문에 페이지의 첫 렌더링을 위한 콘텐츠의 양을 최대한 줄여 네트워크 왕복 수를 줄여야한다.
  • 브라우저가 한 번의 네트워크 왕복만 하고 바로 페이지를 그릴 수 있게끔 ATF는 14KB 이하로 맞추는 것이 좋다.
  • TCP 표준이 최근에 업데이트가 되어 10개까지 패킷을 보낼 수 있기 때문에, 서버의 구성정보를 최신 버전으로 맞추어 3~4 개가 아닌 10개가 되도록 한다.

(4) Avoid external blocking JavaScript and CSS in above-the-fold content

  • 브라우저는 페이지를 사용자에게 보여주기 전에 파싱을 해야한다.
  • 파싱을 하는 동안 non-async 스크립트나 외장 스크립트를 마주치면 진행하던 파싱을 멈추고 해당 리소스를 다운로드 해야한다.
  • 이렇게 매번 수행할 때마다 네트워크 왕복이 매번 증가하기 때문에 페이지 렌더링이 늦어진다.
  • 결론적으로 Javascript 와 CSS 는 인라인으로 포함하는 게 좋다.
  • 첫 페이지 렌더링에 필요한 리소스만 먼저 로딩하고 나머지 기능은 추가로 로딩하도록 구현해야한다.

(5) Reserve time for browser layout and rendering (200 ms)

  • HTML과 CSS 파싱과 Javascript 실행은 모두 시간을 요하는 작업이다.
  • 모바일 기기의 속도와 페이지의 복잡도에 따라서 이 작업이 수백초가 걸릴 수 있다.
  • 브라우저 오버헤드는 200ms 이하가 되도록 설정한다.

(6) Optimize JavaScript execution and rendering time

  • 복잡하고 비효율적인 코드는 수십 ~ 수백 초가 걸린다.
  • 브라우저에 내장된 개발자도구를 이용하여 코드를 profile 하고 최적화 한다. Chrome Developer Tool Course