이직한 회사에서 (심지어 Angular2도 아닌) AngularJS를 사용한 legacy를 분석해야하는 상황이 있어서, 정리 겸 간단하게 살펴본 기록을 남긴다.
들어가기에 앞서
알아둘 것
AngularJS는 2022년 1월을 마지막으로 더 이상 개발을 하지 않는다. 이후 부터는 TypeScript 기반의 Angular로 병합하여 진행하고 있다.
당시 상황
AngularJS는 Front-End(당시에는 Client-side)에서 근대적인 구조를 자리잡게 한 효시 격이라고 할 수 있다.
MVW(Model-View-Whatever)라고 하는 구호를 바탕으로, 일일이 데이터를 ajax로 가지고 와서 JQuery Selector로 해당하는 element를 찾아서 변경해야했던 과거에는 혁신이었다.
Service와 Controller에 있는 값을 변경하면 똑똑한 AngularJS가 저절로 값을 변경해준다.
물론 element를 등록해주면 event 기반으로 해당 함수를 실행시켜주는 backbone.js라거나,
Front-End(client-side)와 Back-End(server-side)를 동기화 시켜서 실시간으로 값을 바꿔주는 meteor.js와 같은 시도도 있었지만,
“server와는 별개로 client에서 정보를 가지고 있고, 그 값을 바꾸면 자동으로 element를 바꿔서 보여준다”는, SPA(Single Page Application) 개념을 가능케 했다. (물론 AngularJS는 정확히 말하면 SPA는 아니다. 필요에 따라 html과 js 파일을 load한다)
다만.
1. 특유의 제한적인 방식
2. 학습 하기 위해 많은 시간과 노력이 필요한 점
3. 이후 등장한 JSX가 더 널리 퍼져서 vDom을 이용한 render 방식이 대세로 자리잡으면서,
react와 vue.js에게 점유를 빼앗겨 자리를 넘겨주게 되었다.
하지만 AngularJS가 끝난 현 시점에서, 아직 Angular.io 에서 그 명맥을 이어가고 있다.
AngularJS 환경 구성
AngularJS는 html에서 직접 script를 불러와서 전역적으로 closure를 활용하여 angular 객체를 만들고,
module과 service를 관리한다. 일종의 angularJS 전용으로 namespace를 사용하는 것과 같다고 보면 된다.
이는 AngularJS가 나온 당시에는 ES5에서 제공해는 Import/export도 없었으며 당연히 bundler가 없었기 때문이다.
webpack, roll-up과 같은 bundler를 사용하여 깔끔하게 변수와 객체를 관리하는 현재 상황과는 많이 다르다.
(물론 bundler를 잘 구성해서 html과 js로 packaging하는 것이 불가능하지는 않다. 그러나 당시에는 없던 기술이다.)
따라서 html에 다음과 같이 angular.js를 script tag로 불러오는 것 부터 인지해야한다.
<!DOCTYPE html>
<!-- myApp이라고 하는 angular 객체를 binding -->
<html ng-app="myApp">
<head></head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.2/angular.min.js"></script>
<script>
// 사용 예시
import module1 from 'module1'
angular
.module('myApp', [module1, 'module2name', ...,])
.config('$someProvider',($someProvider)=>{ ... })
.config('$someProvider1',($someProvider)=>{ ... })
.run(($someProvider, $someProvider1)=>{ ... })
.directive('directiveName', ['$dependency1', '$dependency2'], ($dependency1, $dependency2)=>{
return {
restrict: "E",
link(scope, element, attributes){
...
}
}
})
.config('$someProvider2', ($someProvider2)=>{ ... })
</script>
</body>
<html>
이 파일들을 서버에서 정적으로 제공해야한다. minify나 uglify 같은 방법을 통해서 압축하거나 난독화 하는 경우, 위 ng-app에 할당하는 AngularJS module 이름(myApp)을 변경하면 안 된다.
기초 개념
AngularJS에서 사용하는 개념을 크게 나누어보면 다음과 같다.
- Service : AngularJS에서 저장하고 있는 정보다. Service에 객체나 배열, 변수, 함수 등을 저장하여 controller나 directive, template에서 사용할 수 있다. 아래는 Service에 등록하기 위한 여러 가지 방법이다.
- Provider : Service에 특정 객체나 값을 저장하는 함수를 반환하는 함수
- Factory : Provider를 생성하여 반환하는 함수. 주로 특정 dependency 값에서 provider를 생성할 때 필요하다.
- Controller : Service에서 정보를 가져와서 가공하여 Directive나 Template에 데이터를 연결한다.
- Filter : view에서 특정한 값 뒤에 filter를 사용하여 원하는 데이터를 걸러낸다. 데이터나 표현식 뒤에 |를 붙여서 사용한다. 그 뒤에 :를 붙여서 추가적인 질의를 위한 데이터를 넘겨줄 수 있다.
- Directive : 사용자가 정의한 template이다. react에서 component와 같이, view에서 directive를 작성하여 runtime에서 render한 template를 생성한다.
- Template : ng-로 시작하는 예약어와 driective, html로 구성되어있다. 데이터를 보여주는 역할을 한다.
- ng-if, ng-repeat, ng-model, ng-controller 등 다양한 예약어로 angularJS 객체가 가지고 있는 Service나 controller에서 데이터를 binding해서 그릴 수 있다.
- Module : angularJS에서 독립적인 app 또는 package라고 볼 수 있다. Javascript에서 사용하는 module과는 다르다. module은 service, provider, directive, controller, constant 등을 포함할 수 있으며, 이를 export하여 다른 module에서 사용할 수 있다.
<script>
// filter 예시
const nthExtractor = () => (arr, n) => arr.filter((_, index)=> index % n === 0)
angular.module('example').filter('nthExtractor', nthExtractor)
</script>
<div ng-model="core">
<!-- 아래 코드의 결과는 [0, 2, 4, 6] -->
{{ [0, 1, 2, 3, 4, 5, 6] | nthExtractor : 2 }}
</div>
<div>
<!-- 아래 코드의 결과는 [0, 3, 6] -->
{{ [0, 1, 2, 3, 4, 5, 6] | nthExtractor : 3 }}
</div>
특수 변수
AngularJS에서는 특별한 변수를 제공한다.
- $rootScope : 최상위 범위 변수이다. 모든 변수를 탐색한다.
- $scope : controller나 module이 가지고 있는 변수는 $scope에 등록한다. 만약 해당 controller나 module 범위에 찾는 변수가 없으면 부모 범위를 확인하고, 이를 반복하여 $rootScope까지 확인한다.
- $inject
- $http
- $location
Bootstrap
AngularJS는 bootstrap이라고 하는 과정을 거친다. html에서 angular.js(혹은 angular.min.js)를 import 하면 자동으로 angular 객체를 설정하여 global 변수로 등록한다.
아래는 bootstrap을 위해 angular 객체를 설정하는 예시 코드이다. 이어서 이것을 나누어 살펴볼 것이다.
// 사용 예시
import module1 from 'module1'
angular
.module('myApp', [module1, 'module2name', 'provider1', ... ])
.factory('factoryName', (dependency1, dependency2) => (someVaraible) => { /* Do Something */})
.factory('factoryName2', (dependency) => dependecy1.someFunc('some value'))
.config('$someProvider',($someProvider)=>{ ... })
.config('$someProvider1',($someProvider)=>{ ... })
.run(($someProvider, $someProvider1)=>{ ... })
.directive('directiveName', ['$dependency1', '$dependency2', ($dependency1, $dependency2)=>{
return {
restrict: "E",
replace: false,
transclude: false,
template: '<div>hello world!</div>'
link(scope, element, attributes){
...
}
}
}])
.config('$someProvider2', ($someProvider2)=>{ ... })
angular.modules()
module 이름(myApp)과 종속성을 정의한다. 이후 이 module 안에서 사용할 config과 factory, directive, filter와 같은 함수를 builder pattern으로 정의한다.
angular.config()
Service에서 사용할 provider를 정의한다. provider는 service에 바로 등록하는 것이 아닌, 생성자와 다른
angular.factory()
Service에서 사용할 provider를 생성하는 factory를 설정한다.
angular.directive()
view에서 치환할 custom tag를 등록한다.
angular.run()
AngularJS에서 angular 객체를 설정 완료하고 실행 시킬 때 인자로 받은 함수를 실행시킨다.
마치며
이직한 회사에서는 열심히 React를 전면적으로 활용한 차세대 Front-End를 개발하고 있지만, 아직은 진행 중이기에 legacy에 해당하는 AngularJS 부분을 이해하기 위해서 살펴보았다.
이후에는 다음과 같은 글을 쓸 계획이다.
- AngularJS 기초 부터 만들어보기 : 예제 코드를 통해 예시로 알아보기
- AngularJS Provider 자세히 알아보기
- AngularJS와 Redux+React를 통합하여 Component를 가져오고 state를 공유하여 사용하는 방법
- AngularJS와 Tailwind CSS를 통합하여 사용하는 방법
- AngularJS를 webpack으로 bundle
- AngularJS에서 많이 사용하는 module 소개 및 사용 방법 안내
'Front-End' 카테고리의 다른 글
[React Native] Version Upgrade 후기 (0) | 2024.03.27 |
---|---|
이직 회사 적응 안내서 -2- Front-End 분석 (0) | 2024.03.26 |