Firebase Web 채팅앱 만들기 - 4. Authentication을 이용한 유저 가입 및 로그인 구현하기

1. Authentication 콘솔 설정

  • Firebase Authentication을 사용하여 가입 및 로그인 처리를 진행합니다.
  • Authentication 을 사용하기 위하여 Firebase Console화면으로 진입한 후 좌측 Authentication 메뉴에 들어가고, 그 다음 로그인 방법 탭으로 들어갑니다.
  • 로그인 방법 탭 항목 중 이번 예제에서는 ‘이메일/비밀번호’, ‘Google’, ‘Facebook’ 항목을 이용합니다.

4-1

1.1 이메일과 Google 설정

  • ‘이메일/비밀번호’ 라인을 클릭해봅니다.
  • 사용설정 스위치버튼을 눌러 사용설정을 합니다.
  • ‘Google’도 마찬가지로 사용설정을 합니다.

4-2

1.2 Facebook 설정

  • Facebook 설정은 Facebook 로그인과 Firebase Authentication을 연결지어 주기 위하여 사용 설정을 하기 전에 Facebook 개발자 사이트에서 설정이 필요합니다.
  • 아래의 링크를 방문해주세요.

4-3

  • 만약에 Facebook계정이 없다면 Facebook 계정이 필요합니다.
  • 해당 사이트 로그인 후 우측 상단에 내 앱 > 새 앱 추가를 누르고 간단하게 명칭을 지어주고 이메일을 입력한 뒤 ‘앱 ID 만들기’ 버튼을 눌러줍니다.

4-4

  • Facebook 개발자 콘솔 화면이 나옵니다. 여기서 Facebook 로그인을 마우스를 올려보면, 설정버튼이 있는데 설정 버튼을 눌러 줍니다.

4-5

  • 설정을 누르면 좌측화면에 ‘Facebook 로그인’ 메뉴가 생성이 됩니다. 그 아래에 설정으로 들어가 주세요.

4-6

  • 설정으로 들어가면 ‘유효한 OAuth 리다이렉션 URI’ 라는 항목이 있습니다.
  • 이 때 Firebase에서 알려주는 URI 입력이 필요한데 Firebase console 화면에서 Authentication > 로그인 방법 > Facebook을 클릭하면 뜨는 팝업화면 아래에 있는 정보를 복사하여 ‘유효한 OAuth 리다이렉션 URI’ 항목에 입력하고, 아래쪽 변경 내용 저장 버튼을 눌러 값을 저장해 줍니다.

4-7

  • Facebook 앱은 처음 생성하게 되면 개발 모드 상태입니다.
  • 이는 로컬호스트에서 테스트 때만 정상 작동하므로 배포시 정상 작동하게 하기 위해서 앱을 공개해야합니다.
  • 좌측 메뉴 앱 검수 메뉴에서 앱을 공개해주세요.

4-8

  • Facebook 개발자 콘솔 화면에서의 설정은 완료되었습니다.
  • 대시 보드에 있는 ‘앱 ID’와 ‘앱 시크릿 코드’ 정보를 Firebase console 화면에 입력이 필요합니다.

4-9

  • 다시 Firebase 콘솔로 진입하여 Facebook 개발자 콘솔에서 획득한 ‘앱 ID’와 ‘앱 시크릿 코드’ 를 입력하고 사용 설정을 켜줍니다.

4-10

  • 나중에 로그인 코드가 완성이 되고 로그인을 하게 되면 아래와 같은 로그인 화면이 팝업으로 뜨게 됩니다.

4-11

2. 구글 계정으로 가입 및 로그인 작성

  • 프로젝트 소스에서 public/index.html 파일을 열고 소스 아래쪽 script 태그 안에서 아래의 코드를 작성하고, 콘솔 또는 명령프롬프트 창에서 firebase serve명령어를 입력하여 결과화면을 보기위해 http://localhost:5000 으로 들어갑니다.
firebase serve
        /**
           *  FirebaseChat ES5 클래스
           */
          function FirebaseChat(){
              this.init();
              this.initEvent();
          }

          /**
           * 초기 필드 변수 할당
           */
          FirebaseChat.prototype.init = function(){
              this.auth = firebase.auth();
              this.liGoogleBtn = document.getElementById('liGoogleBtn');
          }

          /**
           * 초기 이벤트 바인딩
           */
          FirebaseChat.prototype.initEvent = function(){
              this.liGoogleBtn.addEventListener('click', this.onGoogleBtnClick.bind(this));
              this.auth.onAuthStateChanged(this.onAuthChange.bind(this));
          }

          /**
           * Google 로그인 버튼 클릭
           */
          FirebaseChat.prototype.onGoogleBtnClick = function(){
              var googleProvider = new firebase.auth.GoogleAuthProvider();
              this.auth.signInWithPopup(googleProvider).then(function(result) {
                  console.log('로그인 성공')
              }).catch(function(error) {
                  alert('로그인에 실패하였습니다');
                  console.error('구글 로그인 과정 에러',error);
              });
          }

          /**
           * 인증 정보가 변화 되었을 시에 변화
           */
          FirebaseChat.prototype.onAuthChange = function(user){
              if (user) {
                  console.log('user로그인 : ',JSON.stringify(user));
              } else {
                  console.log('로그아웃')
              }
          }
  • FirebaseChat 클래스의 생성자 안에 init메소드와 initEvent 메소드가 있습니다.
  • init 메소드는 클래스가 인스턴스로 생성이 될때 할당될 변수들을 선언해두는 곳이고, initEvent는 초기에 바인딩할 이벤트들을 모아두었습니다.

  • 화면에서 ‘구글 계정으로 가입 및 로그인’ 버튼을 누르면 onGoogleBtnClick 메소드가 실행됩니다.
  • Firebase Authentication라이브러리가 제공하는 팝업을 띄우며 로그인하는 signInWithPopup 메소드가 실행이 되며 구글 로그인 화면이 나옵니다.

  • 브라우저 개발자 도구를 통해 잠시 Console 로그를 살펴보겠습니다.
  • 화면을 띄우면서 로그인을 아직 하지 않은 상태이므로 onAuthChange 메소드에 user값이 null이 들어와서 Console에 ‘로그아웃’이 찍힙니다.
  • ‘구글 계정으로 가입 및 로그인’ 버튼을 눌러 로그인을 하게되면 먼저 signInWithPopup 프로미스 결과 값으로 then에서 ‘로그인 성공’ 이 먼저 찍히고, 그 다음 onAuthChange 함수에서 user 값이 들어오며 Console 화면에서 user 로 들어오는 정보가 찍히게 됩니다.
  • JSON 형태로 들어오므로 stringfy함수로 해당 데이터를 print 하였습니다.
  • user정보에서 uid 는 프로젝트에 가입한 고유한 id 값이며, displayName 은 provider(구글, 페이스북 등등) 에서 제공받은 사용자 이름이며, photoURL은 provider에서 제공 받은 이미지이며, email 은 가입한 email정보 입니다. emailVerified는 사용자 확인 여부 입니다.
  • emailVerified 값은 구글 또는 페이스북 같은 provider를 통해 가입한 유저는 true 로 들어오며, 이메일 가입 유저의 경우는 가입 직후는 false로 들어오며, Firebase 에서 전송된 이메일 주소 인증 메일을 인증하게 되면 true 값이 들어오게 됩니다.

  • 그 이외에도 많은 다양한 정보들이 user정보에 포함되어 들어오는 것을 알 수 있습니다.

4-12

  • 이제 브라우저를 닫고 다시 한번 http://localhost:5000 으로 들어갑니다.
  • 다시한번 브라우저 개발자 도구의 콘솔 창을 확인해봅니다.

4-13

  • 로그인 버튼을 누르지 않았음에도 불구하고 onAuthChange 메소드에서 user값이 찍히는 것을 볼 수 있습니다.
  • Authentication 에서는 인증상태 지속 유형이 3가지로 나뉘는데 그 중에 기본 값이 local 상태 입니다.
  • local 상태는 브라우저가 닫혀도 로그인 값을 브라우저내에 저장하고 있습니다.
  • 명시적으로 로그아웃하거나 브라우저 캐시를 지워야지만 로그인이 제거 됩니다.
  • 상태 값의 3가지 내용은 아래와 같습니다.
    • firebase.auth.Auth.Persistence.LOCAL(값:local) : 브라우저 창이 닫히거나 React Native에서 활동이 폐기된 경우에도 상태가 유지됨을 나타냅니다. 이 상태를 삭제하려면 명시적으로 로그아웃해야 합니다. Firebase 인증 웹 세션은 단일 호스트 출처이며 단일 도메인의 경우에만 유지된다는 점에 유의하세요.
    • firebase.auth.Auth.Persistence.SESSION(값:session) : 상태가 현재 세션이나 탭에서만 유지되며 사용자가 인증된 탭이나 창이 닫히면 삭제됨을 나타냅니다. 웹 앱에만 적용됩니다.
    • firebase.auth.Auth.Persistence.NONE(값:none) : 상태가 메모리에만 저장되며 창이나 활동이 새로고침되면 삭제됨을 나타냅니다.
  • 앱을 만들때 상태값의 적절한 선택이 필요합니다.
  • 이번 예제에서는 브라우저를 닫으면 로그아웃 되는 상태로 만들고자합니다. 그렇게 하기 위해서는 onGoogleBtnClick 메소드에 변화를 주겠습니다.
  • onGoogleBtnClick 메소드 안에 Authentication 메소드 setPersistence 메소드를 사용하여 지속성을 설정합니다.
  • 브라우저가 닫힐 때 로그아웃을 위해 firebase.auth.Auth.Persistence.SESSION 값을 줍니다.
  • signInWithPopup 메소드를 FirebaseChat클래스에 추가하였습니다. 다른 Provider로그인에서 공통으로 사용하기 위함입니다.
        /**
           * Google 로그인 버튼 클릭
           */
          FirebaseChat.prototype.onGoogleBtnClick = function(){
              var googleProvider = new firebase.auth.GoogleAuthProvider();
              this.auth.setPersistence(firebase.auth.Auth.Persistence.SESSION)
                  .then(this.signInWithPopup.bind(this, googleProvider))
                  .catch(function(error) {
                      console.error('인증 상태 설정 중 에러 발생' , error);
                  });
          }

          /**
           * 지속성 설정 후 sign-in 팝업창
           */
          FirebaseChat.prototype.signInWithPopup = function(provider) {
              var cbSignIn = function(result){
                  console.log('로그인 성공')
              }

              return  this.auth.signInWithPopup(provider).then(cbSignIn.bind(this)).catch(function(error) {
                  alert('로그인에 실패하였습니다');
                  console.error('로그인 에러',error);
              });
          }

3. 페이스북 계정으로 가입 및 로그인 작성

  • 페이스북 로그인 기능을 구현하기 위해 아래의 코드를 입력합니다.
  • FirebaseChat클래스의 init 메소드에 페이스북 계정으로 가입 및 로그인’ 버튼의 엘리먼트를 할당할 변수를 추가합니다.
  • initEvent 메소드에는 페이스북 버튼의 클릭 이벤트리스너를 추가합니다.
  • 페이스북 버튼의 클릭리스너의 콜백 메소드인 onFacebookBtnClick 메소드를 추가합니다.
  • onFacebookBtnClick 메소드는 onGoogleBtnClick 기능은 동일합니다. 다만 할당 받는 Provider만 다릅니다.
        /**
           * 초기 필드 변수 할당
           */
          FirebaseChat.prototype.init = function(){
              //...생략
              this.liFacebookBtn = document.getElementById('liFacebookBtn');
          }

          /**
           * 초기 이벤트 바인딩
           */
          FirebaseChat.prototype.initEvent = function(){
              //...생략
              this.liFacebookBtn.addEventListener('click', this.onFacebookBtnClick.bind(this));
          }

          /**
           * 페이스북 로그인 버튼 클릭
           */
          FirebaseChat.prototype.onFacebookBtnClick = function(){
              var facebookProvider = new firebase.auth.FacebookAuthProvider();
              this.auth.setPersistence(firebase.auth.Auth.Persistence.SESSION)
                  .then(this.signInWithPopup.bind(this, facebookProvider))
                  .catch(function(error) {
                      console.error('인증 상태 설정 중 에러 발생' , error);
                  });
          }

4. 일반 이메일로 가입

  • 구글과 페이스북 로그인의 경우 유저 정보를 로그인 인증을 해주는 업체에서 제공을 받지만, 일반 이메일은 가입 폼을 따로 작성하여 가입처리를 해주어야 합니다.
  • 화면은 미리 구성 해놓은 것을 사용합니다.
  • ‘이메일으로 가입’ 버튼을 누르면 현재 보이는 로그인 화면은 숨기고, 숨겨져있는 가입 영역인 dvJoin 아이디를 가진 div 영역을 보이게 합니다.
  • 아래의 코드를 추가해주세요.
        /**
           * 초기 필드 변수 할당
           */
          FirebaseChat.prototype.init = function(){
              //...생략
              this.liEmailJoin = document.getElementById('liEmailJoin');
              this.dvLogin  = document.getElementById('dvLogin');
              this.dvJoin = document.getElementById('dvJoin');
          }

          /**
           * 초기 이벤트 바인딩
           */
          FirebaseChat.prototype.initEvent = function(){
              //...생략
              this.liEmailJoin.addEventListener('click', this.displayEmailJoin.bind(this));
          }

          /**
           * 이메일 가입 폼 표시
           */
          FirebaseChat.prototype.displayEmailJoin = function(){
              this.dvLogin.style.display='none';
              this.dvJoin.style.display='block';
          }

4-14

  • ‘이메일으로 가입’ 버튼을 누르면 위 화면이 나옵니다.
  • 이제 기입되는 정보를 바탕으로 가입처리를 하겠습니다.
  • 가입처리 전에 유효성 검증도 필요할 것입니다. 아래 코드를 입력하십시오.

  • ‘이메일으로 가입’ 버튼을 누르게되면 진행되는 메소드 들입니다.
  • 버튼을 누르면 createEmailUser 메소드가 실행이 되고, 메소드 안에서 validateJoinForm 메소드를 통해 유효성 검증을 합니다.
  • validateJoinForm 메소드안에서 이메일 형식과 비밀번호와 비밀번호 확인 데이터가 같은지 검증을 하게 됩니다.
  • createEmailUser 하단을 보면 다른 로그인 처리하는 것과 같이 지속성 설정이 되어 있고, 그 후 콜백으로 createUserWithEmailAndPassword메소드를 실행하여 가입처리를 진행하도록 되어 있습니다.
  • createUserWithEmailAndPassword메소드를 실행되면 프로미스를 반환받고 프로미스가 reject되게 되면 .catch 메소드에서 콜백을 수행하게 됩니다.
  • catch메소드의 콜백에는 4종류의 에러코드를 반환하는데 이에 따른 분기 처리 코드가 들어가 있습니다.

          /**
           * 초기 필드 변수 할당
           */
          FirebaseChat.prototype.init = function(){
              //...생략
              this.liEmailJoinSubmit = document.getElementById('liEmailJoinSubmit');
          }

          /**
           * 초기 이벤트 바인딩
           */
          FirebaseChat.prototype.initEvent = function(){
              //...생략
              this.liEmailJoinSubmit.addEventListener('click', this.createEmailUser.bind(this));
          }

          /**
           * 이메일로 가입 처리
           */
          FirebaseChat.prototype.createEmailUser = function(){
              var userName = document.getElementById('joinUserName').value.trim();
              var email = document.getElementById('joinUserEmail').value.trim();
              var password = document.getElementById('joinPassword').value.trim();
              var rePassword =document.getElementById('joinRePassword').value.trim();
              // 유효성 검증
              if(this.validateJoinForm(email, password, rePassword)){
                  var cbCreateUserWithEmail = function(user){
                      console.log('이메일 가입 성공 : ', JSON.stringify(user));
                  }
                  var cbAfterPersistence = function(){
                      return this.auth.createUserWithEmailAndPassword(email, password)
                          .then(cbCreateUserWithEmail.bind(this))
                          .catch(function(error) {
                              console.error('이메일 가입시 에러 : ', error);
                              switch(error.code){
                                  case "auth/email-already-in-use":
                                      alert('이미 사용중인 이메일 입니다.');
                                      break;
                                  case "auth/invalid-email":
                                      alert('유효하지 않은 메일입니다')
                                      break;
                                  case "auth/operation-not-allowed":
                                      alert('이메일 가입이 중지되었습니다.')
                                      break;
                                  case "auth/weak-password":
                                      alert("비밀번호를 6자리 이상 필요합니다");
                                      break;

                              }
                          });
                  }

                  this.auth.setPersistence(firebase.auth.Auth.Persistence.SESSION)
                      .then(cbAfterPersistence.bind(this))
                      .catch(function(error) {
                          console.error('인증 상태 설정 중 에러 발생' , error);
                      });


              }
          }

          /**
           *  이메일 가입 폼에서 유효성 체크
           */
          FirebaseChat.prototype.validateJoinForm = function(email, password, rePassword){
              //이메일 유효성 체크
              if(!FirebaseChat.emailCheck(email)){
                  alert("이메일 형식에 맞지 않습니다");
                  return false;
              }
              //패스워드 동일 여부 체크
              if(password !== rePassword){
                  alert('패스워드가 동일하지 않습니다');
                  return false
              }

              return true;
          }

          /**
           * 이메일 형식 체크
           */
          FirebaseChat.emailCheck = function(mail){
              if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(mail)) {
                  return true;
              }
              return false;
          }
  • 화면을 띄우고 이메일 가입을 수행하여, console 로그 정보를 잠시 살펴보겠습니다.
  • 이메일 가입 성공 로그가 찍히며 user 정보를 반환합니다. 그리고 가입 동시에 로그인 처리가되며 onAuthChange에도 user정보가 찍히는 것을 볼 수 있습니다.

4-15

  • 이메일 가입 성공 후 로그를 살펴보면 displayName에 Null이 들어가있습니다.
  • 안타깝게도 이메일 가입을 위한 Firebase에서 제공하는 createUserWithEmailAndPassword의 패스워드의 파라미터는 이메일과 패스워드 정보만 받기 때문입니다.
  • 이메일로 가입 성공 후 실행되는 then메소드 안에 콜백 함수 user정보를 통해 프로필을 업데이트 시킬 수 있습니다.
  • createEmailUser 메소드를 수정해보겠습니다.
        /**
           * 이메일로 가입 처리
           */
          FirebaseChat.prototype.createEmailUser = function(){
                  //...생략
                  var cbCreateUserWithEmail = function(user){
                      console.log('이메일 가입 성공 : ', JSON.stringify(user));
                      //프로필 업데이트 - 이메일 가입시 유저이름 파라미터를 보내지 않으므로 가입 성공 후 처리
                      user.updateProfile({
                          displayName: this.userName,
                      }).then(function() {
                          console.log('userName 업데이트 성공')
                      }).catch(function(error) {
                          console.error('userName 업데이트 실패 : ', error );
                      });
                  }
                  //...생략       
             }
  • 다시한번 가입 테스트를 수행하기 위해 브라우저를 다시 실행하여 가입을 수행해봅니다.
  • 만약에 수행할 다른 이메일이 없으시다면 Firebase console로 들어 가셔서 Authentication화면으로 들어가면 아래와 같이 유저를 관리할 수 있는 컨텍스트 메뉴가 있습니다.
  • 유저를 제거 하시고 가입테스트 하시면 되겠습니다.

4-16

  • 다시 가입 직후에는 아직 update된 displayName이 보이지 않지만, 새로고침을 해보면 아래와 같이 displayName에 이름이 들어간 것을 확인할 수 있습니다.

4-17

  • displayName은 갱신이 되었지만, 아직 유효한 이메일인지 여부를 알려주는 값은 여전히 false로 남아 있습니다.
  • 이메일 가입성공 시 이메일 주소 인증 메일을 보낼 수 있습니다.
  • 다시한번 createEmailUser 메소드를 수정하겠습니다.

  • sendEmailVerification 메소드로 가입 성공 후 유효한 이메일인지 확인하는 확인 메일을 전송 합니다.
  • Firebase 기본값은 영어 메일로 되어 있습니다.
  • useDeviceLanguage메소드를 앞서 실행해줌으로써 디바이스 또는 브라우저의 언어로 설정되어져 메일이 전송이 됩니다.
  • 메일이 구글 사정에 따라 전송되는데 시간이 걸릴 수 있습니다
        /**
           * 이메일로 가입 처리
           */
          FirebaseChat.prototype.createEmailUser = function(){
                  //...생략
                  var cbCreateUserWithEmail = function(user){
                      //...생략
                        
                      //인증 메일 발송
                      this.auth.useDeviceLanguage(); // 이메일 기기언어로 세팅
                      user.sendEmailVerification().then(function() {
                          console.log('인증메일 발송 성공')
                      }).catch(function(error) {
                          console.error('인증메일 발송 에러', error);
                      });
                  }
                 //...생략
          }

4-18

  • 만약 전송되는 메일의 포맷을 변경하고자한다면 Firebase console 으로 진입하여 Authentication메뉴에 템플릿 탭을 확인해주세요.
  • 아래와 같이 이메일 주소 인증 메일 뿐만 아니라 비밀번호 재설정 메일, 이메일 주소 변경 메일 등을 설정할 수 있고, 기본 발송 언어 또한 설정할 수 있습니다.

4-19

%FIRST_NAME%
수신자의 이름
%APP_NAME%
앱의 이름. 설정 페이지에서 공개용 이름 필드를 수정하여 이 값을 설정할 수 있습니다.
%LINK%
계정 관리 작업을 완료하기 위해 수신자가 방문해야 하는 URL. 작업 링크 URL 맞춤설정을 참조하세요.
%EMAIL%
수신자의 이메일 주소
%NEW_EMAIL%
수신자의 기본 주소로 설정할 새 이메일 주소. 이메일 주소 변경 템플릿에만 사용됩니다.

5. 일반 이메일로 로그인

  • 이제 가입한 일반 이메일로 로그인하는 부분의 코드를 작성해보겠습니다.
  • ‘이메일으로 로그인’ 버튼에 이벤트를 바인딩하고 로그인을 실행하겠습니다. 다음 코드를 추가해주세요.

  • ‘이메일으로 로그인’을 클릭 시 수행할 메소드로 onEmailBtnClick을 작성하였습니다.
  • onEmailBtnClick를 살펴보면, 이메일 유효성 검증과 패스워드입력여부를 판단 한 후 다른 provider로그인과 마찬가지로 지속성 설정을 SESSION으로 설정한 이후 로그인 처리를 하였습니다.
  • 이메일 로그인 메소드인 signInWithEmailAndPassword메소드의 catch부분에서 에러코드 별로 분기 처리가 되어 있습니다.
          /**
           * 초기 필드 변수 할당
           */
          FirebaseChat.prototype.init = function(){
              //...생략
              this.liGoogleBtn = document.getElementById('liGoogleBtn');
              this.liEmailBtn = document.getElementById('liEmailBtn');
          }

          /**
           * 초기 이벤트 바인딩
           */
          FirebaseChat.prototype.initEvent = function(){
              //...생략
              this.liEmailBtn.addEventListener('click', this.onEmailBtnClick.bind(this));
          }

          /***
           *  이메일 로그인 버튼 클릭
           */
          FirebaseChat.prototype.onEmailBtnClick = function(){
              var email = document.getElementById('userName').value.trim();
              var password = document.getElementById('password').value.trim();
              if(FirebaseChat.emailCheck(email) && password.length > 0){ // 유효성 체크
                  var cbSignInEmail = function() {
                      return  this.auth.signInWithEmailAndPassword(email, password)
                          .then(function(){
                              console.log('이메일 로그인 성공');
                          })
                          .catch(function(error) {
                              console.error('이메일 로그인 과정 에러', error);
                              switch(error.code){
                                  case "auth/invalid-email":
                                      alert('유효하지 않은 메일입니다');
                                      break;
                                  case "auth/user-disabled":
                                      alert('사용이 정지된 유저 입니다.')
                                      break;
                                  case "auth/user-not-found":
                                      alert('사용자를 찾을 수 없습니다.')
                                      break;
                                  case "auth/wrong-password":
                                      alert("잘못된 패스워드 입니다.");
                                      break;

                              }
                          });
                  }
                  this.auth.setPersistence(firebase.auth.Auth.Persistence.SESSION)
                      .then(cbSignInEmail.bind(this));
              }
          }

6. 로그인 후 화면 이동

  • 로그인 후 화면 이동 처리는 각 개별 로그인 메소드에서 처리해도 되나, 한 곳에서 처리하기 위해 인증상태 변화를 감지하는 onAuthChange 메소드를 통해 처리하도록 하겠습니다.
  • onAuthChange 메소드를 아래와 같이 수정하고, 로그인일 때와 로그아웃일때 동작을 정의하는 메소드를 추가해주세요.

  • onAuthChange 메소드가 수정되었으며, setLogin과 setLogOut메소드가 추가되었습니다.
  • onAuthChange를 통해 인증변화 감지에서 user 정보가 넘어오면 로그인 화면을 숨기고, 채팅앱의 본화면으로 전환하는 코드가 들어가있습니다.
  • 반대로 user 정보가 넘어오지 않으면 채팅화면을 숨기고, 로그인 화면으로 돌아가도록 코드가 작성되어져 있습니다.
/**
           * 초기 필드 변수 할당
           */
          FirebaseChat.prototype.init = function(){
              //...생략
              this.dvAuth = document.getElementById('dvAuth');
          }

          /**
           * 인증 정보가 변화 되었을 시에 변화
           */
          FirebaseChat.prototype.onAuthChange = function(user){
              if (user) {
                  console.log('user로그인 : ',JSON.stringify(user));
                  this.setLogin();
              } else {
                  console.log('로그아웃');
                  this.setLogOut();
              }
          }

          /**
           *  로그인 후 세팅
           */
          FirebaseChat.prototype.setLogin = function(){
              this.database = firebase.database(); // 인증 받은 후 초기화 하여야 합니다. 필드 변수에 서 할당하면 인증 받기전에 할당이라 에러발생
              this.database.goOnline(); // 데이터베이스를 명시적으로 온라인
              this.dvAuth.style.display = 'none';
          }

          /**
           * 로그아웃 되어 onAuthStateChange 에 감지 된 후 동작
           */
          FirebaseChat.prototype.setLogOut = function(){
              this.dvAuth.style.display = 'block';
              this.dvJoin.style.display='none';
              this.dvLogin.style.display = 'block';
          }
  • setLogin메소드를 보면 firebase의 database 객체를 필드 변수에 할당하는 코드가 들어가 있습니다.
  • Firebase Realtime Database의 경우에 기본 값으로 Authentication을 통해 인증 받은 사용자만 읽기와 쓰기 권한을 가지도록 설정되어있습니다.
  • database.rules.json 파일을 열어봅니다.

4-20

  • database 객체를 FirebaseChat클래스가 생성이 될 때 할당이 된다면, 즉 Authentication인증 받기 전에 database를 할당 받고, 읽거나 쓰기 작업을 시작하면 권한 오류가 발생합니다.

4-21

  • setLogin메소드에서는 goOnline메소드가 logOut 메소드에서는 goOffline메소드가 있습니다.
  • goOnline메소드는 연결을 재설정하고 동기화 하며, goOffline메소드는 연결된 데이터베이스 모두 연결을 해제합니다.
  • Firebase Realtime Database의 경우 연결 커넥션 수와 자원을 아끼기 위해 로그아웃이 되면 명시적으로 연결을 해제하였습니다.
  • 뒤쪽에서 설명될 접속 유저를 판별하기위해서 로그아웃 직전에 명시적으로 데이터를 끊을 필요가 있습니다.

7. 로그아웃 기능

  • 로그인기능을 통해 화면에 진입을 할수 있게 되면 아래쪽에 3개의 탭이 있습니다.
  • 그 중 톱니바퀴 모양의 3번째 설정 탭을 클릭해보면 로그아웃 기능을 위한 버튼이 준비되어 있습니다.
  • 여기에 이벤트를 바인딩 하도록 하겠습니다.

4-22

  • 아래 코드를 입력해주세요.
          /**
           * 초기 필드 변수 할당
           */
          FirebaseChat.prototype.init = function(){
              //...생략
              this.liLogOut = document.getElementById('liLogOut');
          }

          /**
           * 초기 이벤트 바인딩
           */
          FirebaseChat.prototype.initEvent = function(){
              //...생략
              this.liLogOut.addEventListener('click', this.logOut.bind(this));
          }

          /**
           * 로그아웃
           */
          FirebaseChat.prototype.logOut = function(){
              if(confirm('로그아웃 하시겠습니까?')){
                  if(this.database){
                      this.database.goOffline(); // 데이터 베이스를 명시적으로 오프라인
                  }
                  this.auth.signOut();
              }
          }