[ Architecture, Technology ,Web ] SSO(Single Sign On) 그리고 SAML에 대해

이미지
이번 프로젝트 내부에서 어쩌다보니  유저 인증 관련 업무를 담당하게 되었고, 해야하는 업무는 내부에 사용했던 적이 없던  새로운 개발 플랫폼에서  SSO의 프로토콜 중  SAML을 이용해 앱의 인증을 구현해야만 했다. SSO를 생각해본적 조차 없는 상황에 이를 새로운 개발 플랫폼에 도입해야 했기 때문에 많은 시행착오를 겪었으나 구현에 성공하였으며 덕분에 SSO에 대한 전반적인 지식을 쌓을 수 있었다. 이번에는 그러한 과정에서 나온 지식들과 경험을  공유하고자 한다. SSO에 대한 정의 먼저 사전적 정의 부터 살펴보자. 다만, 기술적인 용어다보니 자주 사용하는 옥스포드 사전에 정의를 찾을 수 없기 때문에  검색으로 찾을 수 있는 정의를 몇 가지 살펴보고 교차 검증을 해보자. 첫 번째 정의를 살펴보자. Single sign-on (SSO) is an identification method that enables users to log in to multiple applications and websites with one set of credentials.  SSO는 웹사이트에서 한 번의 인증(one set of credentials)으로 복수의 어플리케이션에 로그인 할 수 있는 인증(identification) 방법(method) 이다. 두 번째는 위키피디아의 정의이다. Single sign-on (SSO) is an authentication scheme that allows a user to log in with a single ID to any of several related, yet independent, software systems. SSO는 독립적이지만 연관되어있는 몇몇 소프트웨어에 대해 하나의 ID로 로그인을 할 수 있도록 하는 인증 구조(scheme) 세부 설명에 조금 차이가 있어 보이지만 전체적인 틀은 매우 비슷해 보인다.  몇 가지 포인트가 되는 단어를 추출해 이를 연결해보자면 아래와 같은 의미를 산출 할 수 있다. 독립적이지만 연관되어 있

[ React ] React에 대해 : 서론, 소개 (Class Based Component, Hooks)


비교적 최근에 새로운 프로젝트에 참가하게 되었다.

이 프로젝트는 나의 모회사에서 사용하고 있는 
자사 시스템의 리뉴얼로 기존 시스템은 perl로 작성되어 있기 때문에

리뉴얼 하면서 프론트 엔드 쪽과 
백 엔드 쪽 모두 바꾸고 하고 싶어했다.

프론트 엔드 쪽은 React였다.

물론 나는 이전 부터 React에 대해 
조금씩 공부하고 있었으며 운이 좋게도 프로젝트에 참가할 수 있게 되었다.

그러한 상황에서 
나는 React에 대해 제대로 공부할 필요가 있었고
또한 이를 정리해볼 필요가 있었다.

따라서 이를 정리하면서 React에서 
깊은 인상을 받은 부분을 소개하면서 공유해보려고 한다.

서론


아마 대부분의 개발자가 느끼고 있듯이(적어도 내가)
React와 같은 자바스크립트 라이브러리가 등장하기 전까지만 해도
기존 프론트 엔드의 프로그래밍은 방식은 너무나도 지저분해 보였다.

왜냐하면, 기존 프로그래밍 형식과 다르게 
HTML 태그에 덕지덕지 붙여 있는 JavaSciprt는
가독성이 떨어지고 이는 생산성에 영향을 주기 때문이다.

이러한 상황 속에서 ReactJS와 같은
자바스크립트 라이브러리가 등장 했는데,
React외에도 구글의 AngluarJS, Vue.JS등이 있다.

Ruby가 모든 것을 객체화하려 했다면
이런 자바스크립트 라이브러리 중에서 
React는 모든 것을 컴포넌트화 한듯 보였다.

모든 항목에 대해 컴포넌트화 할 수 있으며,
이는 마치 객체지향 패러다임의 캡슐화 한 것과 유사하다.

이러한 자바스크립트 라이브러리들 가운데 
React에서는 두 가지 방식으로 프로그래밍하기를 권장하는듯 보였다.

첫 번째, Class Based Component를 이용한 컴포넌트화 그리고
두 번째, Hooks를 이용한 컴포넌트화 이다.

Component Class를 이용한 컴포넌트화는 React팀에서 제안하는 방식이고,
Hooks는 나중에 확장된 방식이다.

먼저 Class Based Component 부터 살펴보자.

소개: Class Based Component를 이용한 컴포넌트화


지금이야 대부분 Hooks를 이용해서 React를 구현하지만
초기 React 개발팀에서 제시한 방식은 Class Based Component이다.

react에서 제공해주는 Class를 이용하여 컴포넌트를 구성하면
생명주기(Lifecycle) 함수에 접근할 수 있는데 

자주 사용하는 몇 가지 함수를 소개하려고 한다.

추가적인 함수나 자세한 내용은 아래의 [1]공식 도큐먼트를 참고하길 바란다.

①constructor

Java의 Constructor(생성자)와 동일한 역할을 한다.
Java에서 오브젝트가 생성될 때, 생성자가 단 한번 실행되듯이

React에서는 컴포넌트가 마운트 되기 전에 단 한번 실행 된다.
따라서 java와 동일하게 생성자에는 보통 초기값을 설정하는 코드들이 들어가게 된다.

아래는 Constructor의 예를 보여준다.

import React from "react";

class Welcome extends React.Component {

    constructor(props) {
        super(props);

        this.state = { counter: 0 };
        this.hendleStateInfoClick = this.hendleStateInfoClick.bind(this);
        this.hendleCounterClick = this.hendleCounterClick.bind(this);
      }

    hendleStateInfoClick(){
      console.log(this.state);
    }
    hendleCounterClick(){
      //this.state.counter = this.state.counter + 1;
      this.setState({
        counter: this.state.counter+1});
    }

    render() {
      return (
        <div>
          <div>
            <h1>Hello, {this.state.counter}</h1>
            <button onClick={this.hendleStateInfoClick}>Counter State</button>
          </div>
          <div>
            <button onClick={this.hendleCounterClick}>Count+1</button>
          </div>
        </div>
      );
     
    }
  }

export default Welcome;

여기서 확인해 볼만한 부분은 
이 생성자 안에는 super()가 들어간다는 점인데 
super라는 메소드는 this 키워드에 접근하기 위해 사용한다.

이는 클래스 기반 컴포넌트의 경우 
JavaScript의 ES6 구문을 이용하여 확장하기 때문이다.

즉 이 super 함수를 생성자 내부에 작성하지 않는다면
state에 접근할 수가 없기 때문이다.

위의 코드의 예로 들면 

    constructor(props) {
        //super(props);

        this.state = { counter: 0 };
        this.hendleStateInfoClick = this.hendleStateInfoClick.bind(this);
        this.hendleCounterClick = this.hendleCounterClick.bind(this);
      }

위와 같이 super 부분을 주석처리 한다면
constructor내부에 super 메소드를 지정하지 않았기 때문에
this.state와 onClick에 접근할 수 없기 때문에 에러를 발생시킬 것이다.

이 경우 결과를 확인해 본다면 아래와 같은 결과가 나타난다.

위에서 언급한대로 this에 접근하기 위해서는 
super를 반드시 호출해야 된다는 메시지가 나타난다.

②componentDidMount()

constructor가 마운트 전이라면 
이 함수는 마운트 후에 한번 실행 된다.

즉, 리렌더링 된다하더라도 이 메소드는 다시 실행되지 않는다.

따라서 이 함수의 적절한 사용법은 
초기 화면에서 사용해야 하는 데이터들을 
API등을 통해 받아올 때 사용하는 것이다.

예컨데 영화 리스트를 보여주는 화면이라면
처음 렌더링때 보여줄 데이터들을 가져오는 코드(get, fetch 등)를 
이 함수 안에 작성하면 된다.

예를 살펴보자.

import React from "react";

class Welcome extends React.Component {

  constructor(props) {
    console.log("call constructor() method");
    super(props);

    this.state = { counter: 0 };
    this.hendleStateInfoClick = this.hendleStateInfoClick.bind(this);
    this.hendleCounterClick = this.hendleCounterClick.bind(this);
    console.log(this.state.counter);
  }

  hendleStateInfoClick() {
    console.log(this.state);
  }
  hendleCounterClick() {
    //this.state.counter = this.state.counter + 1;
    this.setState({
      counter: this.state.counter + 1
    });
  }

  componentDidMount() {
    // Runs after the first render() lifecycle
    console.log("call componentDidMount() method");
    this.setState({
      counter: 5,
    },()=>{console.log(this.state.counter)});
  }

  render() {
    return (
      <div>
        <div>
          <h1>Hello, {this.state.counter}</h1>
          <button onClick={this.hendleStateInfoClick}>Counter State</button>
        </div>
        <div>
          <button onClick={this.hendleCounterClick}>Count+1</button>
        </div>
      </div>
    );

  }
}

export default Welcome;

constructor 예의 코드에서 조금 추가했다.

여기서 잠깐 흐름을 예측해보자.

위에서 설명했듯이 마운트 되기 전에 
constructor가 실행되고, 그 다음에 componentDidMount가 실행될 것이다.

따라서 초기 화면 콘솔 창에는 아래와 같은 결과가 나타날 것이다.

call constructor() method
0
call componentDidMount() method
5

실제로도 확인 해보자.


추가적으로 Count+1버튼을 3번 누르고
마지막에 현재 counter 카운트의 값을 확인해보자.
이 때 주목해야될 점은 
리렌더링 되었을 때, componentDidMount()가 호출되는지 살펴봐야한다.

위의 설명에 따르면 
componentDidMount()는 단 한번 호출되기 때문에 
아래와 같은 결과가 나올 것이다.

call constructor() method
0
call componentDidMount() method
8

실제로 값을 확인해보자.


우리의 예상과 같은 결과 값이 나타나는 것을 확인 할 수 있다.


④componentDidUpdate()

이 함수는 컴포넌트가 업데이트 되었을 때 호출 되며
componentDidMount()가 마운트 될 때 단 한번 호출되고 
랜더링 되더라도 실행되지 않는다면
componentDidUpdate는 마운트 될 때는 호출되지 않고
랜더링 될 때 마다 호출 된다.

예를 살펴보자.

  componentDidUpdate(){
    console.log("call componentDidUpdate() method");
    this.setState({
      updateCounter: this.state.updateCounter+1,
    },()=>{console.log("updateCounter:",this.state.updateCounter)});
  }

설명만 살펴보면 큰 문제는 없어 보인다.

하지만, 이 코드를 실행하면
치명적인 오류가 발생하는데,


바로 위와 같이 무한 루프가 발생하며 일정 이상 반복되면 
리액트에서 에러를 반환하고 강제 종료 시킨다.

무엇이 문제일까?

이에 대한 공식 도큐먼트를 살펴보면 아래와 같은 설명을 한다.

[2]이러한 생명 주기들은 프로퍼티(props)가 이전과 다른지 아닌지와 관계없이 
부모 컴포넌트가 다시 랜더링될 때 마다 호출됩니다.

따라서 생명 주기 중 하나를 사용하여
무조건 재정의 하는 것은 항상 안전하지는 않습니다.

그럴 경우 state 갱신의 손실을 야기 합니다. 

즉, 우리의 예상과 다르게 
우리의 코드 이외에도 부모 컴포넌트가 어떻게든 바뀐다면
componentDidUpdate가 실행되기 때문에 
이 생명 주기 안에 값을 변경시키는 setState를 사용하는 
이 방식은 애초에 적절한 사용 방법이 아님을 알 수 있다.

다만, 아래와 같이 조건을 걸어준다면 
우리의 예상대로 작동하는 것은 확인 할 수 있다.
    
왜냐하면 componentDidUpdate는 
첫번째 인수로 업데이트 전의 프로퍼티(props)
두번째 인수로 업데이트 전의 state를 가져올 수 있기 때문이다.

따라서 여기서 이용할 것은 두 번째 인수를 이용한다면 
조건을 걸어 줄 수 있게 된다.

  componentDidUpdate(prevProps, prevState){
    console.log("call componentDidUpdate() method");
    if(this.state.counter !== prevState.counter){
      this.setState({
        updateCounter: this.state.updateCounter+1,
      },()=>{console.log("updateCounter:",this.state.updateCounter)});
    }
  }

위의 코드를 통해
counter가 값이 변할 때, 해당 코드를 실행하게 될 것이며
무한 루프에 빠지지 않게 될 것이다.

이에 대한 자세한 설명은 
공식 도큐먼트에서 이를 언급하고 있기 때문에
이 링크[3]를 참고하길 바란다. 

물론 억지로 사용할 수 있기는 하지만 성능를 야기할 수 있다. 

도큐먼트에서는 이 방법을 권장하지 않으며,
그들이 항상 언급하는 대로 생명주기는 최적화에만 이용하기를 권장한다.

이제 예측을 해보자. 

아마 componentDidUpdate가 어쨋든 한번 실행될 가능성이 크다.

componentDidMount()에서 counter의 값이 변하기 때문에 
한번 실행 될 것이고,
변했기 때문에 componentDidMount에서 한번 실행됨으로써 
updateCounter의 값이 1이 될 것이다.

import React from "react";

class Welcome extends React.Component {

  constructor(props) {
    console.log("call constructor() method");
    super(props);

    this.state = {
      counter: 0,
      updateCounter: 0,
    };
    this.hendleStateInfoClick = this.hendleStateInfoClick.bind(this);
    this.hendleCounterClick = this.hendleCounterClick.bind(this);
   
    console.log("counter:",this.state.counter);
    console.log("updateCounter:",this.state.updateCounter);
  }

  hendleStateInfoClick() {
    console.log(this.state);
  }
  hendleCounterClick() {
    //this.state.counter = this.state.counter + 1;
    this.setState({
      counter: this.state.counter + 1
    });
  }

  componentDidMount() {
    // Runs after the first render() lifecycle
    console.log("call componentDidMount() method");
    this.setState({
      counter: 5,
    },()=>{
      console.log("counter:",this.state.counter);
      console.log("updateCounter:",this.state.updateCounter);
    });
  }

  componentDidUpdate(prevProps, prevState){
    console.log("call componentDidUpdate() method");
    if(this.state.counter !== prevState.counter){
      this.setState({
        updateCounter: this.state.updateCounter+1,
      },()=>{
        console.log("counter:",this.state.counter);
        console.log("updateCounter:",this.state.updateCounter);
      });
    }

  }

  render() {
    return (
      <div>
        <div>
          <h1>Hello, {this.state.counter}</h1>
          <button onClick={this.hendleStateInfoClick}>Counter State</button>
        </div>
        <div>
          <button onClick={this.hendleCounterClick}>Count+1</button>
        </div>
        <div>
          <span>componentDidUpdate Count:{this.state.updateCounter}</span>
        </div>
      </div>
    );

  }
}

export default Welcome;

전체적인 코드는 아래와 같으며 나는 아래와 같이 예측해보았다.

call constructor() method
counter:0
updateCounter:0
call componentDidMount() method
counter:5
updateCounter:0
call componentDidUpdate() method
counter:5
updateCounter:1

먼저 마운트 되기 전에 constructor가 실행되고 
각 counter와 dupateCounter는 0으로 초기화 될 것이다.

그리고 마운트가 된다면 componentDidMount가 실행될 것이고
내부 로직으로 인해 counter가 5로 갱신 된다.

이때 componentDidUpdate는 실행되지 않는데
그 이유는 위에 설명과 같다면 마운트 될때는 실행되지 않기 때문이다.

이 후 값의 변화가 일어 났기 때문에 componentDidUpdate가 실행되고
updateCounter는 1이 된다.

그리고 counter의 값을 변화 시키는 버튼이 클릭될 때 마다
counter의 값은 1씩 증가하며,
이 때 componentDidUpdate가 실행되어 
updateCounter 값도 1씩 증가할 것이다.

실제 결과를 보자.

첫 번째, 빨간색 박스가 처음 화면에 진입했을 때 이고,
두 번째 부터 counter+1 버튼을 눌렀을 때의 콘솔 로그이다.

예측과 큰 차이는 없지만 
componentDidUpdate가 한번 씩 더 실행되는데 

값이 변하면서 부모의 컴포넌트도 한번 씩 더 실행되는 듯 싶다.

그 시점에는 이미 값이 업데이트 되어 있기 때문에
값은 변하지 않고 
call componentDidUpdate() method라는 텍스트만 출력되는 것을 알 수 있다.

다시 한번 말하지만 
이는 React 개발팀에서 권장하지 않는 방식이며,
생명주기는 최적화가 필요할 때만 이용하기를 권장한다.

실제 도큐먼트의 예는 onChange를 통한 방식을 예로 들고 있다.

⑤componentWillUnmount()

컴포넌트가 해제(언마운트) 직전에 호출되는 부분이다.
타이머와 같이 필요 없는 사용하지 않지만 
해제 하면 성능에 큰 문제가 될 수 있는 것들을 처리할 수 있을 것 이다.

즉, 리액트 컴포넌트 클래스를 이용한다면,
클래스 내부에서 제공하는 함수들을 이용해 
컴포넌트 호출 전/후, 업데이트되었을 전/후, 컴포넌트가 해제 전/후 등
전반적인 생명 주기에 따른 처리를 할 수 있다.

이를 확인하기 위해 App파일 내부 코드를 조금 추가하였다.

class Hello extends React.Component {

  componentWillUnmount() {
    alert('call componentWillUnmount() method');
  }

  render() {
    return <h1>Hello World!</h1>;
  }
}

class App extends React.Component {

  state = { display: true };
  delete = () => {
    this.setState({ display: false });
  };

  render() {
    let comp;
    if (this.state.display) {
      comp = <Hello />;
    }

    return (
      <div>
        <div>
        {comp}
        <button onClick={this.delete}>Hello Component unMount</button>
        </div>

        <Welcome />
      </div>
    );
  }

}

export default App;

새로운 컴포넌트 클래스 Hello와 
버튼을 눌렀을 시 componentWillUnmount가 호출되게 끔 코드를 추가하였다.

추가적인 코드 설명을 하자면
우선 버튼을 누르게 되면 아래의 코드가 실행되는데
  delete = () => {
    this.setState({ display: false });
  };

setState로 값을 변경하면서 
값이 업데이트 되었기 때문에 랜더링이 발생하여
render()안의 코드들이 실행 된다.

여기서 주목할 점은 컴포넌트를 직접 호출하지 않고 
comp라는 변수를 생성해 그 안에 저장했다는 점이다.

    let comp;
    if (this.state.display) {
      comp = <Hello />;
    }

    return (
      <div>
        <div>
        {comp}
        <button onClick={this.delete}>Hello Component unMount</button>
        </div>

        <Welcome />
      </div>

즉, 클릭으로 인해 display는 false가 되며 
comp는 재정의되지만 Hello 컴포넌트는 저장되지 않기 때문에 
Hello 컴포넌트는 해제가 되며 componentDidUpdate가 실행되어
Console에 작성한 문자가 표시 된다.

버튼을 클릭하면 아래와 같은 알림창이 표시되고 
확인 버튼을 누르면
Hello World라는 문자가 사라지며, 
콘솔 창에는 예상대로 작성했던 문자가 표시된다.

이 메소드는 일반적으로 사용할 일은 없지만,
setTimeout과 같이 직접 해제해야만 하는 작업이 필요할 때 사용한다.

여기 까지 해서 클래서 기반 컴포넌트에서 
자주 사용되는 생명 주기 메소드에 대해 이야기 해보았다.

위에서 언급했다시피 해당 생명 주기 메소드는 
최적화를 할 때만 사용하기를 권장하고 있다.

따라서 이 부분들을 확실한 이해를 갖추고 있는 상황에서
적절히 활용한다면 성능 향상에 기대해 볼 수 있다.
물론 이는 쉽지 않은 작업이다.

다만, 배우는 입장에서 사이드 프로젝트를 하고 있다면
일단 사용해보고 어떤 결과를 낳는지 살펴보고,
어떻게 이 메소드를 사용하면 좋을지에 대해 
생각해보는 것도 나쁘지 않다고 생각한다.

소개: Hooks를 이용한 컴포넌트화


Hooks는 사실 처음 부터 React 개발팀에서 제시되던 방식은 아니다.

처음 제시되었던 것은 recompose라는 라이브러리 부터 출발했고,
이를 React팀이 수용하면서 확장되었다고 한다.

Hooks의 멋진 점은 함수형 프로그래밍에 가깝다는 것이다.

즉, 나와 같은 백엔드 방식에 익숙해 있는 사람들에게 
정확히는 일반적인 프로그래밍 언어에 익숙해있는 사람들에게
Hooks는 아주 적절한 개발 방식이라고 할 수 있다.

함수형 프로그래밍 패러다임을 이용해 컴포넌트를 정의하고,
이를 태그로 사용함으로써 실제 앱단의 코드는 매우 간결해진다.

또한 클래스 기반 컴포넌트와 다르게 
state를 정의하거나 render 함수 내에 이를 가져오는 등의 
처리가 필요하지 않아 코드도 굉장히 간결해진다.

즉, 클래스 기반 컴포넌트 말고 
Hooks를 사용하는 것을 택한다면,
보다 더 많은 코드를 압축할 수 있다.

소개 할 만한 Hooks는 정말 많지만
가장 많이 사용되며 React에서 기본적으로 제공해주는 
useState와 useEffect에 대해 이야기해보려 한다.

클래스 기반 컴포넌트와 동일하게 
간단한 코드를 살펴보며 이야기를 나누어보자.

먼저 useState에 대해서 살펴보자.

useState()를 입력으로 받게 된다면,
useState는 첫 번째 인덱스에는 변수, 
두 번째 인덱스에는 Modify Object 두 가지를 반환한다.

이 Modify Object 인수에 변수에 대한 처리가 이루어진다.

const [count, setCount] = useState(0);

count가 해당 값을 접근 할때의 변수이고
setCount가 값을 변경 할때 접근할 객체이다.
이를 Modify Object라고 한다.

useState안에 들어가는 인수는 초기화할 값을 나타낸다.

즉, 이 단 한줄로 
변수, 값을 변경할 함수, 초기화 까지 다 할 수 있게 된다.

다음으로는 useEffect이다.

useEffect는 React Component Class에서 
ComponentDidMount와 ComponentWillUnMount 
그리고 ComponentWillUpdate의 역할을 한다

즉, useEffect를 사용한다면 컴포넌트가 마운트 되거나 
내부 업데이트가 이루어질 때의 처리를 제어할 수 있다.

예컨데 count라는 변수가 있고, 
버튼을 눌렀을 때, 1씩 증가한다고 했을 때, 
count 변수를 의존성(dependency)으로 설정해 준다면, 
useEffect 내부에 정의되어있는 코드들이 실행된다.

설정하지 않는다면, 
컴포넌트가 마운트 될 때 단 한번 실행된다.

  useEffect(() => {
    setCount(3);
  }, []);

위의 예는 dependency를 설정하지 않았을 때의 예를 보여준다.

useState 설명의 예에서 
초기화를 0으로 설정 했는데,

위의 코드가 추가된 경우
먼저 마운트 되기 전에 0으로 설정되고, 
마운트 된 후 useEffect가 실행되기 때문에 3으로 변하게 된다.

콘솔로 확인해보자.


위에서 확인 가능하다시피
dependency를 설정하기 않았을 경우 
정확히 componentDidMount 역활을 하는 것을 볼 수 있다.

단 한번 실행되기 때문에
아무리 값을 변화 시켜도 useEffect 내부의 값은 실행하지 않는다.

dependency를 설정할 경우,
componentDidUpdate의 역할을 하게 되는데 예를 살펴보자.

  useEffect(() => {
    setCount(3);
  }, [count]);

그렇다면 여기에 count 변수를 
dependency로 설정 할 경우 어떻게 될까?

잠깐 추론해보자.
dependency를 설정하면 componentDidUpdate의 역할을 하게 된다.

그렇다는 것은 count의 값이 변할 때마다
useEffect의 내부 코드가 실행된다는 말이고

즉, 어떤 조작에 의해 count의 값이 변경된다면
setCount(3)이 실행되어,
값이 항상 3으로 초기화 될 것이다.

실제 결과를 보자.


값을 올리려고 해도
useEffect 내부의 코드 때문에 3으로 고정되는 것을 확인할 수 있다.

위에서 설명했다시피 
dependency를 따로 설정하지 않는다면,
컴포넌트가 마운트 될 경우 호출되는데

이를 이용해 아래와 같은 코드를 작성한다면
컴포넌트를 중복 호출하지 않게 할 수 있다.

const 구현한 컴포넌트 = () ={
console.log("구현한 컴포넌트");
}

예컨데 위와 같이 간단한 함수를 작성했다고 가정해보자.

useEffect(() => {
window.addEventListener("등록할 함수이름",구현한 함수이름);
return () =>{
window.removeEventListener("등록할 함수이름",구현한 함수이름);
}
},[count]);

이 경우 위와 같은 코드를 작성한다면 마운트 될때 
구현한 함수 이름 이라는 이벤트가 등록되고 
언마운트 될 때, 해당 이벤트가 제거 된다.

위의 코드는 setInterval와 같이 
해제가 꼭 필요한 컴포넌트의 경우 사용된다.

useEffect에 대한 좀 더 자세한 내용은
공식 도큐먼트를 확인하기 바린다.[4] 

결론적으로 Hooks를 이용한다면 

useState를 통해 
변수 설정, 값 변경 객체, 초기화를 
단 한 줄의 코드로 작성 할 수 있으며

useEffect를 통해 
ComponentDidMount와 ComponentWillUnMount 그리고 
ComponentWillUpdate까지 제어할 수 있기 때문에 
클래스 컴포넌트를 사용할 때 보다 코드가 매우 간결해진다.

물론 이외에도 React에서 공식적으로 지원하는 Hooks도 많으며,
다른 개발자들이 Hooks 라이브러리도 존재 한다.

React가 인기가 많은 이유는 바로 여기에 있지 않을까 싶다.

이 예제의 전체 적인 코드는 아래와 같다.

import { useEffect, useState } from "react";

const WelcomeReactHooks = () => {
  const [count, setCount] = useState(0);
  //increase counter
  const increase = () => {
    setCount(count => count + 1);
  };
  //decrease counter
  const decrease = () => {
    setCount(count => count - 1);
  };
  //reset counter
  const reset = () => {
    setCount(0)
  }

  useEffect(() => {
    setCount(3);
  }, []);

  // useEffect(() => {
  //   setCount(3);
  // }, [count]);

  console.log({ count });

  return (
    <div>
      <h1>Hello,{count}</h1>
      <div>
        <button onClick={increase}>+</button>
        <button onClick={decrease}>-</button>
        <button onClick={reset}>Reset</button>
      </div>
    </div>
  );
}
export default WelcomeReactHooks;

마치며


따라서 React를 이용한다면,
바닐라 자바스크립트를 사용할 때, 
몇 십 줄을 사용해야 구현할 수 있는 코드들의 수를 굉장히 압축시킬 수 있으며,

이를 통해 개발자에게 있어서 가장 중요한 
코스트가 줄어듬은 물론이고 
가독성이 좋아지기 때문에 유지/보수적인 면에서도 
좋은 효과를 볼 것으로 기대할 수 있다.

물론 클래스 기반 컴포넌트를 사용하던, 
Hooks를 사용하던 크게 상관 없지만
현재 많이 사용되는 것은 의심의 여지 없이 Hooks를 이용한 방식이다.

Hooks를 이용한다면, 코드를 더욱더 압축할 수 있다.

이번에 소개한 Hooks는 공식 도큐먼트에서 소개한 Hooks로 
다른 React 개발자들이 개발한 Hooks도 존재하며,
이 라이브러리를 가져와 개발중인 프로젝트에 사용할 수 있다.

어떤가?

내가 잠깐 소개했음에도 React를 사용할 이유는 충분하지 않은가?

물론 사실상 React에서 지켜야만하는 규칙들이 있기 때문에 
조금 낯설 수는 있지만,

자바스크립트에 대한 지식이 어느 정도 있다면
그렇게 많은 시간이 소요되지는 않을 것 이다. 

업무에 이용하지 않더라도
잠깐 사이드 프로젝트를 진행해 간단한 앱을 개발해보면
분명 React의 매력에 빠질 것이다.

실무에서 React를 사용하고 있기도 하고 
개인적으로도 React로 사이드 프로젝트를 하고 있기 때문에 
이 후 생각해볼 만한 것들은 글로 작성해 볼 예정이다.

따라서 앞으로 대부분의 포스팅은 
아마  React에 대한 이야기가 되지 않을까 싶다.


[2] : These lifecycles are called any time a parent component rerenders, regardless of whether the props are “different” from before. Because of this, it has always been unsafe to unconditionally override state using either of these lifecycles. Doing so will cause state updates to be lost.







이 블로그의 인기 게시물

[ Web ] 웹 애플리케이션 아키텍처 (Web Application Architecture)

[ Web ] 서버 사이드(Sever Side) ? 클라이언트 사이드(Client Side)? 1 [서론, 클라이언트 사이드(Client Side)]

[ Web ] 웹 애플리케이션 서버 아키텍처의 정의 및 유형 ( Define and Types of Web Application Server Architecture )