정의

npm은 환경 설정을 CLI, 환경 변수, npmrc 파일을 이용하여 가져옵니다. 

npm config 명령어를 이용하면 npmrc의 기본 유저, 글로벌 설정을 변경할 수 있습니다.

 

파일

npmrc 파일은 사용처별로 4가지 가 있을 수 있습니다.

  • 프로젝트 설정 (/path/to/my/product/.npmrc)
  • 유저별 설정 (~/.npmrc)
  • 글로벌 설정 ($PREFIX/etc/npmrc)
  • npm 내장 설정 (/path/to/npm/npmrc)

각 사용처별 모든 파일들은 key=value 형식 포맷입니다.

${}를 이용하여 환경 변수를 replace 하여 사용할 수 있습니다.

prefix = ${HOME}/.npm-packages​

주의 할 점은 각 파일들은 우선순위에 따라 덮어쓰여질 수 있습니다.  

예를 들어서, 유저별 설정은 글로벌 설정보다 우선순위가 높으므로 동일한 키가 있으면 덮어씁니다.

key[] = "first value"
key[] = "second value"

주석

.npmrc 파일 내에 주석을 사용할 수 있습니다. https://github.com/npm/ini 스펙을 따르며,

; 또는 # 를 라인 맨 앞에 명시해주면 됩니다.

 

프로젝트별 설정 

프로젝트 파일 최상단(node_modules, package.json 과 같은 path)에 .npmrc 파일로 프로젝트별 설정을 할 수 있습니다.

이 설정은 npm 을 실행하고 있는 프로젝트 루트에만 영향을 줄 수 있고, 이미 배포한 모듈의 경우에는 적용되게 할 수 없습니다.
( @scope로 배포한 모듈을 받는 클라이언트의 프로젝트에  강제로 npmrc 파일 설정이 적용되도록 못한다는 뜻) 

npm install -g 명령어의 경우 해당 설정이 적용되지 않으니 주의해야 합니다.

 

유저별 설정

각 사용자 기기 $HOME/.npmrc 경로에 파일을 생성하여 적용할 수 있습니다.

 

글로벌 설정

$PREFIX/etc/npmrc 경로에 파일을 생성하여 적용할 수 있습니다. 우선순위가 낮기 때문에 다른 설정에 의해 덮여쓰여질 수 있습니다.

 

내장 설정

path/to/npm/

업데이트가 되어도 수정되지 않는 내장 설정입니다. ./configure 로 설정 할 수 있습니다.

배포할 때, 기본 설정을 overwrite하기 위해 표준 방법으로 사용하기도 합니다.

 

PS

사내 npm registry에 등록된 라이브러리를 가져와서 사용하고 있었는데, 

빌드 과정에서 사내 npm registry를 찾는것이 아닌, npm 공용 registry 주소를 찾는 이슈가 있었다. 
.npmrc내에는 아래와 같이 명시했었다. 

test-viewer:registry=https://registry.{사내도메인}/api/npm/npm-{사내}/

해당 라이브러리를 퍼블리시 할 때, 별도의 scope를 주지 않고 registry만 명시해서 배포되어서, 

발생한 문제였고, scope (@smart) 를 명시해준 뒤 해결 되었다.

@smart:registry=https://registry.{사내도메인}/api/npm/npm-{사내}/

Overview

HTML, DOM을 템플릿, 템플릿 함수를 생성합니다. 이로써 scope와 template을 연결 시킬 수 있습니다.
컴파일은 DOM tree를 탐색하고 Dom Element를 directive에 연결합니다.

Comprehensive Directive API

directive를 정의하는데는 여러가지 방법이 있습니다.
directive의 정의 객체를 반환하는 방법도 있고, postLink 함수만을 반환하는 방법도 있습니다.

Best Practice : 정의 객체를 리턴하는 것을 추천합니다.

var myModule = angular.module(...);

myModule.directive('directiveName', function factory(injectables) {
  var directiveDefinitionObject = {
    priority: 0,
    template: '<div></div>', // or // function(tElement, tAttrs) { ... },
    // or
    // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
    transclude: false,
    restrict: 'A',
    templateNamespace: 'html',
    scope: false,
    controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
    controllerAs: 'stringIdentifier',
    bindToController: false,
    require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
    multiElement: false,
    compile: function compile(tElement, tAttrs, transclude) {
      return {
         pre: function preLink(scope, iElement, iAttrs, controller) { ... },
         post: function postLink(scope, iElement, iAttrs, controller) { ... }
      }
      // or
      // return function postLink( ... ) { ... }
    },
    // or
    // link: {
    //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
    //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
    // }
    // or
    // link: function postLink( ... ) { ... }
  };
  return directiveDefinitionObject;
});

directive를 정의할 때, 옵션을 명시해주지 않으면 기본 값을 사용하게 됩니다.
따라서 위의 directive 코드는 사실 아래와 같습니다.

var myModule = angular.module(...);

myModule.directive('directiveName', function factory(injectables) {
  var directiveDefinitionObject = {
    link: function postLink(scope, iElement, iAttrs) { ... }
  };
  return directiveDefinitionObject;
  // or
  // return function postLink(scope, iElement, iAttrs) { ... }
});

Life Cycle Hook

directive 컨트롤러는 angularJs 자체에서 정해진 directive 생명 주기에 따라 메서드를 제공합니다.

  • $onInit()
    • 모든 컨트롤러, Element의 Data Binding이 완료 된 후, pre, post link 함수가 실행되기 전에 정의된 컨트롤러 마다 실행됩니다.
  • $onChanges(changesObj)
    • (<)단방향 바인딩, (@)interpolation 바인딩이 갱신될 때마다 호출됩니다.
    • 파라미터인 changesObj 는 연결된 프로퍼티의 해시 값을 저장하고 있습니다. { currenctValue, previouseValue, isFirstChange()}
    • $scope 외부 프로퍼티의 값을 변경하는 일을 감지하는데 사용 할 수 있습니다. 바인딩이 초기화 된 후부터 호출된다는 것을 주의해야 합니다.
  • $doCheck()
    • digest loop 마다 실행됩니다.
    • 값의 변경을 감지할 수 있도록 합니다.
    • deep equality, 날짜 객체 검사, $onChange로 감지 불가능한 값의 변경을 감지 할 수 있도록 합니다. 비교를 위해서 이 함수를 이용할 때, 이전 값을 저장하고 있어야 합니다.
  • $onDestroy
    • scope가 제거될 때 호출됩니다.
    • 가지고 있는 외부 리소스를 해제할 때 사용할 수 있습니다.
    • $onDestroy 이벤트는 $scope.broadcast 가 수행되는 순서(top->down)대로 호출된다는 것을 기억하십시오. (부모가 먼저 호출된다.)
  • $postLink
    • directive 컨트롤러 자신과, 자식들이 link되고 난 후에 호출됩니다.
    • DOM event, DOM 조작을 수행할 수 있습니다.
    • 자식 element의 templateUrl 은 각각 비동기로 컴파일, 링크 과정을 거치기 때문에 접근할 수 없는 것을 명심하십시오.

Comparison with life-cycle hooks in the new Angular

새로운 Angular 역시 생명주기 훅을 사용하긴 하지만 다음과 같은 다른점이 있습니다.

  • AngularJS 훅에는 $ 기호가 붙지만, Angular에서는 ngOnInit 처럼 ng가 붙습니다.
  • AngularJS 훅은 컨트롤러 프로토타입으로 정의될 수 있고, 생성자 내부에서 정의될 수 있지만, Angular에서는 Component 클래스의 생성자 내부에서만 정의할 수 있습니다.
  • Angular에서 값의 변경을 감지하는 동작에 변경사항이 생겨서, Angular를 사용하게 되면 $doCheck 가 훨씬 더 적게 수행될 것입니다.
  • AngularJs에서는 $doCheck 가 어플리케이션 전체에 broadCast 되지만, Angular에서는 값의 변경이 Component를 벗어나는 것을 막았습니다. 비활성화는 enableProdModel() 로 가능합니다.

Life-cycle hook examples

예제1

예제2

Directive Definition Object

multiElement

  • 기본 값은 false 이다.
  • true 일 경우, Html 컴파일러에서 DOM 노드 중에서 directive-name-start directive-name-end 를 찾고, 함께 그룹화 한다.
  • ngClick 과 같이 반드시 엘리먼트에 event가 필요한 경우나 , ngInclude 와 같이 자식 element를 관리하는 directive 에서는 사용하지 않는것이 좋다.

priority

  • 1개의 Dom Element에 여러개의 directive가 매칭되어 있을 때 사용한다.
  • 이 값을 이용해서 directive가 컴파일 되는 순서를 정한다.
  • 숫자가 높은 순서가 높은 우선순위를 가진다.
  • pre-link function은 높은 우선순위부터 실행되지만, post-link는 반대로 실행된다.
  • 기본값은 0

terminal

  • true 일경우 directive 컴파일 우선순위의 가장 마지막에 넣는다.

scope

  • false, true 값중 1개로 설정할 수 있다.
    • false (default) : directive에 scope가 생성되지 않고, parent scope를 사용한다.
    • true : 새로운 자식 scope가, 부모 scope를 상속한채로 생성된다.
    • {...} (an object hash) : directive template에 새로운 isolated scope 가 생성된다. 부모 scope를 상속받지 않는다. template, templateUrl 이 없이는 자식 element에 isolated scope가 생성되지 않음을 주의하자. isolated scope object에서 상위 parent scope에 매칭되는 값을 정의할 수 있다.
    • @ or @attr : local scope 프로퍼티를 Dom attribute에 매핑시킨다. 결과는 Dom attribute 타입이 string이기 때문에 항상 string이다. 만약, attribute name이 따로 정의되어 있지 않으면 local name과 같다고 가정한다. 예를 들어서 <my-compnent my-attr="hello {{name}}"> 이 있다고 가정하면, isoated scope에서는 scope : { localName: '@myAttr'} 로 매핑되고, 실제 hello {{name}} 을 parent scope에서 읽게 된다.
    • = or =attr : 양방향 데이터 바인딩을 설정할 때 사용한다. parent scope에서 값이 계산된다. <my-component my-attr="parentModel"> 로 정의했을 때, isolated scope scope : { localModel: '=myAttr'} 로 매핑되고, parent scope의 parentModel 값에 따르게 된다. localModel, parentModel 값이 각각 변경되면 서로에게 영향을 미치게 된다. binding expression이 non-assignable일 때, $compile:nonassign이 발생한다. 기본적으로 $watch 메소드로 값의 변화를 추적하게 되며, 값이 array 일경우에 한해서 angular.equals 를 사용한다. 얕은 비교를 사용하게 할수도 있는데, 이때는 =* or =*attr 을 사용하면 된다.
    • < or <attr: 단방향 바인딩에 사용된다. parent context에서 계산된다. 같은 예제로 <my-component my-attr="parentModel"> 은 isolated scope scope : { localModel: '<myAttr'} 에 매핑된다. parent scope에서 parent Model이 변경됐을 떄, local Model 값이 변경된다. 역시나 얕은 비교를 위해서 <* , <*attr 로 정의할 수 있다.
        1. 같은 Model을 가르키고 있다. 값을 clone해서 갖고 있는것이 아니기 때문에, 주의 해야 한다.
        1. parent Model을 $watch 하고 있어서, isolated scope에서 바인딩된 객체를 바꾸었을 경우, event가 발생하지 않을 수 있으니 주의해야한다.
    • & or &attr: parent context의 expression을 실행하는 방법을 제공한다. <my-component my-attr="count = count + value"> 일 경우 isolated scope scope: { localFn : '&myAttr'} 로 매핑된다. localFn은 "count = count + value" expression을 래핑하고 있다.

4가지 종류 모두 ? 을 붙일 수 있다. optional , non-optional 을 의미한다.

app.directive('testDir', function() {
  return {
    scope: {
      notoptional: '=',
      optional: '=?',
    },
    bindToController: true,
    controller: function() {
      this.$onInit = function() {
        console.log(this.hasOwnProperty('notoptional')) // true
        console.log(this.hasOwnProperty('optional')) // false
      }
    }
  }
})

일반적인 경우 1개의 Element에 1개의 directive를 매핑시키지만, 아닐경우 매핑 방법이 여러가지 있을 수 있다.

  • no scope + no scope: 모두 부모 scope를 사용한다.
  • child scope + no scope: 1개의 child scope를 공유한다.
  • child scope + child scope: 1개의 child scope를 공유한다.
  • isolated scope + no scope: isolated scope만 자신의 scope를 사용하고, 나머지는 parent를 사용한다.
  • isolated scope + child scope: 동작하지 않는다.
  • isolated scope + isolated scope: 동작하지 않는다.

bindToController

bind scope 프로퍼티를 바로 컨트롤러에 매핑할 때 사용한다.

controller

컨트롤러 생성자 함수이다. pre-link 함수가 실행되기 전에 수행되며, 다른 directive에서 접근할 수 있다.
주입 가능한 local 변수들을 가지고 있다.

  • $scope: 현재 scope
  • $element: 현재 element
  • $attrs: 현재 attrs
  • $transcluede: 자식 element compile과정에서 사용할 변수. transclude link function function([scope], cloneLinkingFn, futureParentElements, slotName)

require

다른 디렉티브의 컨트롤러에 접근할 때 사용한다. link 함수의 4번째 파라미터를 컨트롤러로 만들 수 있다. 가능한 데이터 타입 string, object, array

  • (no prefix) : required controller를 현재 element에 위치시킨다. 없으면 에러
  • ? : required controller를 현재 element에 위치시킨다. 없으면 null
  • ^ : required controller를 현재 element, parent에서 찾는다. 없으면 에러
  • ^^ : required controller를 parent에서 찾는다. 없으면 에러
  • ?^: required controller를 현재 element, parent에서 찾는다. 없으면 null
  • ?^^: required controller를 parent에서 찾는다. 없으면 null

'프론트엔드 > AngularJS 1.8' 카테고리의 다른 글

2. Expressions (번역)  (0) 2021.12.25
1. Data Binding (번역)  (0) 2021.12.25

AngularJS Expressions 와 Javascript Expressions 차이점

AngularJS Expressions은 Javascript와 비슷하긴 하지만 차이점이 있습니다.

  • Context : Javascript 에서 Expressions 는 global window 객체에서 연산되지만, Angular JS는 scope 라는 object에서 연산됩니다.
  • Forgiving : Javascript 에서는 undefined 프로퍼티를 연산하려고 했을 때, ReferenceError , TypeError가 발생하지만, AngularJS는 undefined를 null 취급합니다.
  • Filters : Expression 내에 data display 전 filters를 사용할 수 있습니다.
  • No Control Flow Statements : AngularJS Expression 에서는 conditonal, loops, exception 을 사용하지 않습니다.
  • No Function Declarations : AngularJS Expression 내에서 함수를 선언할 수 없습니다. (ng-init 도 예외는 아닙니다.)
  • No RegExp Creation With Literal Notation : AngularJS Expression 내에서 정규식을 사용할 수 없습니다. 대신 ng-pattern 을 사용합니다.
  • No Object Creation With New Operator : AngularJS 에서 new Operator를 사용할 수 없습니다.
  • No Bitwise, Comma, And Void Operators : AngularJS 에서 Bitwise , ',' , void operator를 사용할 수 없습니다.

Example

---

Context

AngularJS에서는 Javascript eval()을 사용하지 않습니다. 대신에 AngularJS 자체 $parse를 사용합니다.
AngularJS에서 자체적으로 window , document, location 에 접근할 수 없습니다. 이는 global 영역에 무분별하게 접근하여서 위험성이 큽니다.
대신 AngularJS 컨트롤러 내부에 있는 자체 $window ,$location 을 사용합니다.
context 자체에 접근하기 위해서 this 키워드도 사용 가능합니다.

---

Forgiving

Javascript 코드 내에서 a.b.ceval() 하게 되면, exception이 발생합니다.
하지만 AngularJS Expression 내부에서는, undefined가 반환됩니다.

{{a.b.c}}

No Control Flow Statements

3항 연산자(a ? b : c)를 제외한 flow control 구문을 AngularJS에서 사용할 수 없습니다. 이유는 AngularJS 핵심 철학에 따라 Controller에서는
어플리케이션 로직을 가질 수 없도록 하기 위함입니다.

No Function Declarations or RegExp creation with literal notation

Angular JS Expressions 내부에서 정규식을 사용할 수 없습니다. 이는 AngularJS가 템플릿 내부 model 변수를 변환하는 로직에 영향을 끼치는것을 막기 위해서 입니다. 대신 Expression Filter 사용을 고려 할 수 있습니다.


$event

ngClick , ngFocust Directive는 scope 안의 $event 객체를 사용합니다. 이 객체는 jQuery Event Object 입니다.

One-time Binding

:: 키워드는 One-time Expressions 입니다. 한 번 바인딩 되고 나면 그 후의 digest 과정에서 제외됩니다.
Angular에서 수행하는 Digest Loop 속도를 최적화하고 리소스 관리 차원에서 이점이 있습니다.

Value stabilization algorithm

One-time binding에서 expression 값은 digest cycle에서 값을 유지하고 있습니다.

  1. :: 로 시작하는 expression을 만났을떄 digest loop는 undefined가 아닌 한, 값을 V로 저장합니다.
  2. expression의 결과를 마킹하고, digest loop를 빠져나가면서 expression을 dirty-checking watching하는 스케줄에서 제외시킵니다.
  3. 평상시처럼 digest looprk 가 실행됩니다.
  4. undefined일 경우, dirty-checking watch 스케줄을 그대로 놔두고, step 1으로 돌아갑니다.

One-time binding이 구체적으로 어느 이점이 있는가 ?

1번 정해지면 변경되지 않는 부분들

<div name="attr: {{::color}}">text: {{::name | uppercase}}</div>

Bidirectional Binding을 사용하더라도, 파라미터가 변경되지 않습니다.

someModule.directive('someDirective', function() {
  return {
    scope: {
      name: '=',
      color: '@'
    },
    template: '{{name}}: {{color}}'
  };
});
<div some-directive name="::myName" color="My color is {{::myColor}}"></div>

'프론트엔드 > AngularJS 1.8' 카테고리의 다른 글

0. $compile (1)  (0) 2021.12.26
1. Data Binding (번역)  (0) 2021.12.25

개요

AngularJS 어플리케이션에서의 데이터 바인딩 이란, Model과 View 사이에서의 자동 동기화를 말합니다. 

Model 은 어플리케이션에서 유일하게 데이터를 가공하는 곳입니다. 

View는 Model의 변경에 따라 그 내용을 사용자에게 보여주는 역할을 합니다. 

Model에서의 변경은 View의 변경을 일으키고, View에서의 변경은 Model에서의 변경을 일으킵니다. 

일반적인 템플릿 엔진에서의 Data Binding 

일반적인 템플릿 엔진에서 데이터는 한 방향으로 흐릅니다. 

템플릿과 모델을 merge하는 과정을 거쳐서 view를 만들게 되면, 이 때 Model이 변경되어도 View에 반영되지 않습니다. 
사용자가 view에서 하는 action들 또한 Model에 영향을 미치지 않습니다. 
이 이유로 개발자가 직접 Model과 View사이를 주기적으로 동기화 하는 과정을 관리해야하는 번거로움이 있습니다.

 

AngularJS에서의 Data Binding

AngularJS에서 템플릿은 다르게 동작합니다. 템플릿은 ( 컴파일되지 않은 Html, Angular Directive등이 포함되어 ) 동시에 브라우저에 의해 컴파일 됩니다. 컴파일 과정의 결과물이 View입니다. Model에서의 변경사항은 즉시 View에 반영되고, 그 반대 역시 반영됩니다.

Model은 여전히 유일하게 Data를 가져오고 다루는 곳입니다. 위에서 일반적인 템플릿 엔진에서 개발자가 신경써야 했던, 데이터 동기화가 간단해 졌습니다.

'프론트엔드 > AngularJS 1.8' 카테고리의 다른 글

0. $compile (1)  (0) 2021.12.26
2. Expressions (번역)  (0) 2021.12.25

+ Recent posts