React VS Angular

라코마코·2021년 5월 15일
6

Angular

목록 보기
1/6
post-thumbnail

이 글은 앵귤러를 공부하면서 리액트와 다른 점을 비교하고 정리하는 목적으로 쓴 글입니다.

앵귤러와 리액트의 다른점!

1. 컴포넌트 구조

React

function Welcome(props){
  const clickEvent = (e) => {
    console.log('HelloWorld');
  }
  // someCode ...
  return <h1 onClick={clickEvent}>Hello World!</h1>
}

리액트 컴포넌트는 함수와 JSX로 이루어져있습니다.

컴포넌트 함수에는 JSX 외에도 비즈니스 코드를 컴포넌트 내에서 작성하는것이 가능합니다.

Angular

<h1 (click)="clickEvent()">Hello World!</h1>
@Component({
  selector:'app-hello-world',
  templateUrl:'./hello-world.html'
})
export class HelloWorldComponent {
  clickEvent(){
    console.log('HelloWorld');
  }
}

Angular의 컴포넌트는 View 파일인 HTML,CSS와 비즈니스 코드를 작성하는 Component 클래스로 분리하여 작성합니다.

Component 클래스는 @Component 데코레이터를 통해서 생성하는것이 가능합니다.


나의 생각

제 생각에는 Angular의 컴포넌트 구조가 조금 더 합리적이라고 생각합니다.

일단 Angular의 경우 HTML과 Component 클래스를 분리 함으로써 HTML 표준을 지키면서 코딩할 수 있습니다.

또한 컴포넌트도 빌드시 WebComponent가 나오는데 WebComponent는 웹 표준중 하나입니다. (https://developer.mozilla.org/ko/docs/Web/Web_Components)

이렇게 View와 비즈니스 코드를 분리 시키면 코드의 결합도도 낮아지고 재활용성도 높아지게 됩니다.

2. 서비스 코드를 분리하도록 강제함.

Angular

Angular의 서비스 클래스

@Injectable({
  provideIn:'root'
})
export class HelloService {
  constructor(private http:HttpClient){}
  putData(){
    //... putData
  }
}

서비스 코드를 사용하는 컴포넌트 클래스

@Component({
  ...
})
export class HelloComponent {
  constructor(private put:HelloService){}

  click(){
    put.putData();
  }
}

React

서비스 코드를 분리하도록 강제하지 않음.


Angular를 공부하면서 가장 좋았던 부분은 서비스 코드를 강제로 분리하도록 하는 부분이 마음에 들었습니다.

많은 React 프로젝트에서는 View와 Service영역을 분리하지 않고 컴포넌트에서 송신부터 데이터 가공까지 수행하는 코드를 본적이 많았습니다.

이렇게 되면 View와 Service의 결합도가 높아져 수정시 난감한 부분이 많이 생기기도 하고 재활용성도 낮아지게 됩니다.

React에서는 그런 부분을 피하고자 Container-Presenter 패턴을 사용하곤 합니다.

하지만 강제하지는 않기 때문에 편하다는 이유로 지키지 않고 개발하곤 합니다.

Angular에서는 Service에 필요한 코드는 모두 Service 객체에서 생성하도록 강제합니다.

이렇게 생성된 Service 객체는 Dependency injection을 통해서 각 컴포넌트가 사용할 수 있게 되고, View와 Service의 구분이 명확해져 더 유연한 구조를 가질 수 있습니다.

3. Dependency Injection System

Angular

@Component({
  ...
})
export class HelloComponent {
  // 이렇게 지정된 부분은 Angular에 의해서 DI (Dependency Injection) 되어져 내려옵니다.
  constructor(private put:HelloService){}

  click(){
    put.putData();
  }
}

React

없습니다.


Angular의 꽃인 DI입니다.

DI 객체로 Module에 등록되면 앵귤러는 객체 생성시 constructor에 필요한 DI 객체가 있는지 찾고 있다면 이를 주입하여 객체를 생성합니다.

DI를 하게 되면 코드의 결합도를 낮출 수 있고, 테스트 코드를 작성할때도 여러 이점을 누릴 수 있습니다.

하지만 React에서는 이러한 시스템이 존재하지 않습니다.

대신 React 진영에서는 이러한 부분을 HOC,HOF 으로 해결합니다.

// HOC
function Hello(Component,createProps){
  // 미리 작성한 비즈니스 코드
  const useAlways = ()=> {}
  return (props)=> <Component {...createProps} {...props} useAlways={useAlways}/>
}

HOF는 인자로 함수를 받아 함수를 반환하는 함수이고
HOC는 컴포넌트를 받아 컴포넌트를 반환하는 함수입니다.

사실 컴포넌트는 함수이기 때문에 HOC = HOF로 바라보는것도 가능합니다.

HOC의 이 점은 컴포넌트를 받은 후 해당 컴포넌트의 props로 특정 로직을 처리하는 값을 전달해 줌으로써

코드의 재활용성을 높힐 수 있다는 장점을 누릴 수 있습니다.

저는 Component를 인자로써 주입을 하는것 이기 때문에 이런 HOC를 사용한 방법이 DI와 비슷하다고 생각하여 비교 대상으로 놓았습니다.

4. re-rendering

React

import {useState} from 'react';

function Hello(props){
  const [number,setNumber] = useState(1);
  const click = () => {
    setNumber(number+1); // state를 변경시 re-rendering 된다.
  }
  ...
}

React의 경우 state와 props가 변경되면 컴포넌트가 re-rendering됩니다.

Angular

@Component({
  ...
})
class HelloComponent{
  private let number = 1;
  onClick(){ // 클릭시 re-rendering됩니다.
    number+=1;
 }
}

Angular는 클릭과 같은 이벤트가 작동되면 re-rendering이 호출됩니다.


Angular에 대한 부가 설명

React의 경우 state,와 props가 변경될 경우 re-rendering 되지만

Angular의 경우 어느 시점에 어떻게 re-rendering이 호출되는 걸까요?

그것을 알기 위해서는 Angular의 Zone.js 에 대해서 알아야 합니다.

이 Zone.js는 비동기 이벤트에 대해서 추적하고 가로채는 일을 수행하는 패키지입니다.

이 Zone.js는 주로 아래와 같은 이벤트들을 몽키 패칭 하여 이벤트 발동 후 re-rendering (Change Detection) 을 일으킵니다.

  • 브라우저 이벤트 (click,scroll,keyup ...)
  • setInterval, setTimeoout
  • xmlHttpRequest

정리

React의 경우 state,props가 호출될 경우 re-rendering 합니다.

Angular의 경우 비동기 이벤트 (click,setTimeout,setInterval,xmlHttpRequest) 가 호출될 경우 re-rendering (Change detection) 을 일으킵니다.

5. re-rendering 방지

React

const data = [1,2,3,4,5,6,7];
function Parent(){
  return <>
      {data.map(e=><Child key={e}/>)}
    </>
}

React에서는 자식 컴포넌트에 key를 추가함으로써 key가 변경될 경우에만 re-rendering을 하도록 지정하는것이 가능하다.

Angular

@Component({
  ...,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class HelloComponent {

}

Angular에서는 re-rendering을 방지하기 위해서는 OnPush 전략을 지정해야한다.


Angular 부가설명

Change Detection (CD)

Angular에서 CD가 발생하면 위 그림처럼 최상위 루트 컴포넌트부터 가장 아랫단의 컴포넌트까지 CD를 체크하게된다.

모든 컴포넌트에 대해서 CD를 검사하기 때문에 이를 막기 위해서는 OnPush 전략을 지정하여 막는다.

OnPush 전략을 지정할 경우에는 위 그림과 같이 OnPush 된 컴포넌트에 대해서는 CD를 수행하지 않는다.

OnPush 전략에선 4가지 변경 사항이 일어나는 상황에서만 CD가 발생한다.

  • @Input 레퍼런스가 변경 되었을때
  • 현재 컴포넌트나 그 자식 컴포넌트에서 이벤트가 발생했을때
  • CD를 수동으로 일으켰을때
  • template에서 async pipe로 연결된 Observable이 값을 emit 했을때

이 OnPush의 경우 Default 모드와는 다르게 아래의 이벤트에서는 CD를 일으키지 않기 때문에 조심해야한다.

  • setTimeout,setInterval
  • Promise.resolve().then()
  • Observable Subscription

꽤 복잡하기 때문에 Angular에서는 수동으로 CD를 일으키는 ChangeDetectorRef 객체도 제공한다.

이 객체는 4가지 메소드를 제공합니다.

  • markForCheck 메소드
    현재 컴포넌트에서 모든 조상 컴포넌트에 CD Check를 마킹합니다. CD Check가 마킹되면 다음 CD가 일어났을때 CD가 수행됩니다.
    이 메소드는 CD를 호출하지 않습니다.
    따라서 조상 컴포넌트에서 CD가 발생하여야 markForCheck가 호출된 객체에서도 CD가 발생합니다.

  • detectChanges
    현재 컴포넌트와 자식 컴포넌트에 대해서 CD를 수행합니다.

  • checkNoChanges
    detectChanges처럼 CD를 수행하지만 변경 사항이 없다면 에러를 리턴합니다.

  • detach
    현재 컴포넌트와 자식 컴포넌트를 CD Tree에서 분리시킵니다.

  • reattach
    분리되어진 CD 트리를 다시 부착시킵니다.


Angular의 경우 알아야 하는 내부 로직과 메소드가 추가되기 때문에 React보다 어려운 감이 없지않아 있습니다.

하지만 개발자가 직접 CD를 할 수 있게끔 선택권을 줌으로써 더 유여한 개발을 할 수 있게 선택권을 제공합니다.

정리

컴포넌트를 구조화 하는 방법, re-rendering 로직, DI 시스템 등등 React와 Angular는 많은 면에서 다릅니다.

React,Angular 둘 모두 나름의 장점과 매력이 있다는 것을 공부하면서 많이 느꼈습니다.

개인적으로 대규모 프로젝트의 경우 DI 기능을 제공하는 Angular가 더 적합하다고 생각합니다. (React가 못하다는것은 아닙니다! )

하지만 다양한 기능을 제공해주는 만큼 알아야 하는 것도 있어 React 보다 아주 쪼끔 러닝커브가 가파르다고 생각합니다. ( React도 충분히 어려워요! )

이상 React vs Angular 글이였습니다

profile
여러가지 화면을 만드는걸 좋아합니다. https://codepen.io/raiden2

3개의 댓글

comment-user-thumbnail
2021년 5월 15일

이렇게 보니까 장단점이 확연히 보이네요! 정리 감사합니다~ㅎㅎ

답글 달기
comment-user-thumbnail
2021년 5월 21일

좋은 글 감사합니다ㅎㅎ

답글 달기
comment-user-thumbnail
2021년 12월 21일

Angular도 기본적으로 props와, 컴포넌트 내부의 state들이 변경될 경우 re-rendering되는건 기본으로 봐야할 것 같습니다

답글 달기