📌 필요한 기능

학교 선택 -> 해당 학교 이메일 뒷부분(예를 들어 @korea.ac.kr) 확인 -> 일치한다면 입력한 이메일로 메일 전송 -> 인증

이런 로직으로 학교 인증을 구현하였다.

firebase를 사용하면 이메일 인증도 쉽게 구현할 수 있다.

 

 📌 이메일 전송 코드

// 로그인 및 메일 전송
const sentEmail = await signInWithEmailAndPassword(firebaseAuth, email, password)
  .then(() => {
    sendEmailVerification(firebaseAuth.currentUser);
    return { err: false };
  })
  .catch((e) => {
    console.log(e);
    return { err: true, error: e };
  });

if (sentEmail.err) {
  return res
    .status(statusCode.INTERNAL_SERVER_ERROR)
    .send(util.fail(statusCode.INTERNAL_SERVER_ERROR, 
    		responseMessage.SEND_VERIFICATION_EMAIL_FAIL));
}

 

 📌 인증 여부 확인 코드

const userFirebase = await signInWithEmailAndPassword(firebaseAuth, email, password)
  .then((user) => user)
  .catch((e) => {
    console.log(e);
    return { err: true, error: e };
  });
  
  ...
  
const {
  user: { uid: firebaseId, emailVerified: isEmailVerified },
} = userFirebase;
// const firebaseId = userFirebase.user.uid;
// const isEmailVerified = userFirebase.user.emailVerified; 와 동일

if(!isEmailVerfied) {
	// 이메일 인증이 되지 않은 유저에 대한 처리
}

인증 여부는 로그인 정보의 emailVerifed를 가지고 처리해주었다.

 

 📌 이메일 전송에서 어려웠던 점

serverless 환경이 아니라 서버측에서 이메일 인증을 구현하였기 때문에 해당 유저가 로그인이 되어있는지 알 수 없다.

따라서 로그인을 시키고 이메일을 전송해야 한다.

 

signInWithEmailAndPassword로 로그인을 시키고 그 이후 sendEmailVerification을 사용하여 인증 메일을 보낸다.

 📌 들어가며

저번 포스팅에서는 node.js와 Express를 이용한 간단한 서버 구축을 해보았습니다.

이번 글에서는 Express 폴더, 파일에 대해 좀 더 자세하게 다뤄보려고 합니다.

 

⬇️ 저번 포스팅 보러가기!

https://ju-hyeon.tistory.com/28

 

[Server] nodejs, express 서버 구축

 📌 들어가며 최근에 서버 프로젝트를 몇 개 시작하면서 서버 구축을 처음부터 다뤄보고자 글을 작성하려고 합니다. 이 글에서는 간단한 express 프로젝트 생성하는 법을 다루고, 다음 글에서는

ju-hyeon.tistory.com

 

 📌 Express 구조

​가장 상단의 practice (express 명령어로 만든 프로젝트의 이름) 를 눌러보면

5개의 폴더와 app.js, package.json을 볼 수 있습니다.

 

위에서 부터 하나씩 뜯어보도록 하겠습니다.

 

1. bin 폴더

bin폴더에는 오른 쪽 이미지와 같은 www파일이 있습니다.

이 파일은 서버를 실행하는 스크립트이며 프로젝트에 할당되는 포트 번호를 변경할 수 있습니다.

 

🔴 포트번호 주의 (겹치지 않도록 주의해야 합니다.)

1 ~ 1023: well-known port

1024 ~ 49151: registered port

49152 ~ 65535: dynamic port

 

well-known port란?

국제인터넷주소관리기구인 ICANN에서 TCP, UDP에 할당한 포트 번호입니다.

해당 포트 번호들은 특정 애플리케이션을 사용하기 위해 할당된 포트 번호입니다.

예를 들어, 20번과 21번 포트는 TCP, 22번은 SSH 등이 있습니다.

각 포트는 강제가 아니라 권고 사항이라 할당된 포트 번호를 그대로 사용하지 않고 다른 포트 번호를 사용하기도 합니다.

 

registered port란?

특정 용도로 사용하기 위한 포트 번호입니다.

등록된 포트라는 것은 말 그대로 기업에서 등록해서 사용이 가능한 포트입니다.

예를 들면, 8080번 포트는 HTTP 포트를 대체할 수 있는 포트 또는 MS에서 제공하는 SQL을 사용하기 위한 1433 포트 등이 있습니다.

registered port도 강제가 아닌 권고사항이라 할당된 포트 번호를 그대로 사용하지 않고 다른 포트 번호를 사용하기도 합니다.

 

dynamic port란?

특별히 용도가 지정되지 않았으며, 어느 프로그램에서나 사용이 가능한 포트 번호입니다.

 

포트 설명 출처: https://m.blog.naver.com/kyeong477/221558180792

 

 

2. public 폴더

public 폴더에는 각종 리소스들이 포함되어 있습니다.

이미지, JS파일, stylesheets 파일을 볼 수 있네요!

 

이 폴더는 서버에서 건드릴 일이 없습니다!

 

 

3. routes 폴더

route 폴더에는 페이지 라우팅과 관련된 파일들이 들어있습니다.

주소 별로 라우터들을 모아둔 것이며 URL 별로 실행되는 실제 서버 로직입니다.

 

index.js를 시작으로 관리해주면 됩니다.

이 라우터에 대해서는 제가 주로 사용하던 폴더링 구조에서 확인하시면 이해하기 쉬울 겁니다!

 

 

4. views 폴더

views 폴더에는 jade, ejs 등 템플릿 파일들을 모아 둡니다.

웹 서버 사용 시 해당 폴더의 파일들을 사용해서 렌더링 하는 것입니다.

 

이 폴더도 public 폴더와 같이 서버에서 건드릴 일이 없습니다!

 

 

5. app.js

app.js 파일은 프로젝트의 중심이자 핵심적인 서버 역할을 합니다.

이 파일에서는 미들웨어 관리가 이루어지며 라우팅의 시작점이 됩니다.

 

 

6. package.json

package.json 파일은 저번 포스팅에서 설명한 것과 같이 프로젝트에 사용된 모듈들이 나와있습니다.

 

npm install 명령어를 이용해 해당 파일에 있는 모듈들을 모두 설치할 수 있습니다.

또한 npm <모듈명>을 하게 된다면 package.json 파일에 추가됩니다.

 

 

 📌 마치며

Express처럼 자동으로 생성되거나 타인의 프로젝트 내용을 이해하기 위해서는 폴더, 파일을 뜯어보는 것이 정말 중요합니다!

저도 동아리에서 처음 서버를 공부할 때는 파트장님이 만들어주신 보일러 플레이트를 사용하여 api 코드를 짜는 것에 집중을 했었습니다.

최근에 진행한 서버 프로젝트에서는 정말 모든 파일을 뜯어본 것 같습니다.

시간이 좀 걸려도 이렇게 공부했던 것이 서버 로직 이해 뿐만 아니라 코드 짜는 것에도 도움이 되고, 오류가 나도 어디서 오류가 났고 어떤 것을 수정해야 하는지 알기 쉬웠던 것 같습니다.

api를 짜는 것도 중요하지만 파일을 처음부터 하나하나 뜯어보는 것을 추천드립니다!!!! 파이팅 👏

 

다음 포스팅에서는 firebase functions를 이용한 서버 초기 셋팅 방법, 자주 사용한 폴더링 구조를 공유하도록 하겠습니다.

 

 

 

참고: SOPT 29th 서버 파트 2차 세미나 자료

 📌 들어가며

최근에 서버 프로젝트를 몇 개 시작하면서 서버 구축을 처음부터 다뤄보고자 글을 작성하려고 합니다.

이 글에서는 간단한 express 프로젝트 생성하는 법을 다루고, 다음 글에서는 Express 구조 뜯어보기,

그 이후에는 firebase functions를 이용한 서버 프로젝트 및 자주 사용했던 폴더링 구조를 공유하도록 하겠습니다. 😃

 

 📌 사전 준비

1. Nodejs 설치

 

잠시, Nodejs란?

Node.js는 Chrome V8 Javascript 엔진으로 빌드된 Javascript 런타임 입니다

 

쉽게 말해서, javascript로 작성한 코드로 서버를 실행시키기 위한 도구다~ 라고 알고 계시면 될 것 같습니다.

 

https://nodejs.org/en/

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

해당 링크로 들어가서 LTS 버전 다운로드 합니다.

 

설치 후 터미널에서

$ node -v
$ npm -v

두 가지의 명령어가 실행된다면, 정상적으로 설치 된 것!

 

2. VS Code 설치

https://code.visualstudio.com/

 

Visual Studio Code - Code Editing. Redefined

Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications.  Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows.

code.visualstudio.com

해당 홈페이지에서 본인의 OS에 맞게 다운로드 합니다.

 

 

3. Express 설치

 

Express란...?

Node를 위한 빠르고 간결한 서버 프레임워크로
HTTP 요청에 대해 라우팅 및 미들웨어 기능을 제공한다.

$ sudo npm install express-g

* sudo 명령어는 max, linux 환경에서만 사용하면 됩니다 !!

 

-g 옵션을 주어 전역 설치를 하기 때문에 어느 경로에서 설치를 하든 상관 없습니다.

 

 

 📌 프로젝트 생성

1. VSCode 실행

2. Terminal을 사용해서 프로젝트를 만들 위치로 이동

VSCode를 실행했다면 mac 기준 ^(control) + ` 로 터미널 창을 열어줍니다.

만약, 열리지 않는다면 혹시 현재 언어가 영어가 맞는지 확인해주세요!

(영어 `가 한글로 하면 로 표시될 겁니다!)

 

터미널에서 아래의 명령어를 입력해줍니다.

$ npm install -g express-generator

-g는 --global 옵션의 줄임말로 전역 설치 옵션이며 express-generator는 express 프로젝트 생성기입니다.

 

설치가 된 것을 폴더에서 보고싶다! 하시는 분은

/usr/local/lib/node_modules

폴더로 이동하면 전역으로 설치한 모듈들이 보일 겁니다.

 

이제 터미널에 하단의 명령어를 입력해줍니다.

$ express practice

 

프로젝트 생성 완료!

 

 📌 Express 프로젝트 실행

프로젝트 최상위 폴더 위치로 이동합니다.

쉽게, 프로젝트 생성 시 적었던 express <프로젝트명> 에서 프로젝트명으로 이동해주면 됩니다.

 

해당 위치에서 dependency 설치를 위해 아래의 명령어를 입력합니다.

$ npm install

설치를 했다면 node_modules라는 폴더가 생겼을 겁니다.

해당 폴더는 dependency를 모두 담고 있는 폴더입니다.

 

npm install 명령어는 package.json에 있는 모듈들을 인스톨 해줍니다.

여기에 있는 dependencies를 인스톨해주는 것입니다!

 

이제 express 시작을 위해 아래 명령어를 입력해줍니다.

$ npm start

이 상태에서 브라우저에 접속해줍니다.

 

프로젝트 실행 완료!

 

다시 VSCode로 돌아와서 터미널을 보면

GET 요청이 실행되었던 것을 확인할 수 있네요!

 

 📌 마치며

이번 글에서는 간단히 Express 프로젝트 생성 & 실행을 다뤄보았습니다.

다음 글에서는 Express 프로젝트 생성 시 나오는 폴더 & 파일에 대해 더 자세히 알아보겠습니다!

 

참고: SOPT 29th 서버 2차 세미나 자료

 📌 들어가며

https://ju-hyeon.tistory.com/22

 

[Server] firebase FCM을 이용한 pushAlarm 서버 구현

 📌 들어가며 지금은 SOPT 29기 장기 해커톤인 앱잼이 진행 중이다. 이번에는 학과 후기, 전공 정보 공유를 위한 커뮤니티 앱인 '나도선배'의 서버 개발자로 참여하게 되었다. 기획자분들이 만들

ju-hyeon.tistory.com

🔺푸시 알림 구현

 

 

 📌 푸시 알림 소리 설정의 핵심

아래는 푸시 알림 구현 포스트에서 보여준 코드의 일부이다.

 

해당 메시지 내용에 보면 따로 sound를 설정해주는 코드가 없다.

// 메세지 내용
const message = {
	notification: {
	title: notificationTitle,
	body: notificationContent,
	},
	token: token,
};

message 부분에 안드로이드 소리 설정을 위한 android 부분과 iOS 소리 설정을 위한 apns 부분을 나눠서 코드를 작성한다.

(apns는 Apple push notification service의 약어이다.)

 

처음에는 message 밑에 있는 notification에 그냥 sound 설정을 해봤는데 아무 반응도 없길래 열심히 구글링했다.

// 메세지 내용
const message = {
	notification: {
    	title: notificationTitle,
        body: notificationContent,
    },
    android: {
        notification: {
        sound: "default",
        },
    },
    apns: {
        payload: {
        	aps: {
            	sound: "default",
        	},
        },
    },
    token: token, // device token
};

이렇게 하면 소리 모드일 때는 정상적으로 소리 알람이, 진동이나 무음 모드일 경우에는 경우에 맞게 진동이 가거나 무음이 된다!

 

 

 📌 마치며

엄청 간단하지만 구글링해도 잘 안나오길래 모든 경우의 수를 두고 코드를 작성해봤지만...

아무리 푸시 알림을 보내도 울리지 않는 띠링 소리.. 저렇게 안드랑 아요를 나눠서 설정해야 하는 걸 생각도 못했었다..

분명 저 코드를 알려주신 한 줄기의 빛 같은 감사한 글이 있었는데 어디있는지 못찾겠습니다....... 서버 개발자시겠죠? 작성자분 감사합니다...

 

이 글을 보는 또 다른 서버 개발자분들께 도움이 되었길,, 파이팅 💪

 📌 Firebase를 이용한 푸시알림 flow

이번 프로젝트에서는 firebase를 유용하게 쓰고 있는데, 푸시 알림도 파이어 베이스에서 제공해주고 있어서 간편하게 사용할 수 있다!

출처:&amp;amp;nbsp;https://noonestaysthesame.tistory.com/m/17

백엔드는 fcm 서버에 푸시 알림을 전송하고 프론트엔드는 fcm 서버에 있는 메시지를 해당 디바이스에 알림을 띄워주는 것이다.

 

 📌 Firebase로 메시지 보내기 위한 준비 과정


1, Firebase project 생성 후, 터미널에서 Firebase Admin SDK 설치

$ npm install firebase-admin --save


2. SDK 초기화를 위한 서버 키 다운로드
프로젝트 콘솔로 이동 -> 서비스 계정 -> 키 생성

하단의 새 비공개 키 생성 클릭!


3. SDK 초기화 코드 작성
위의 사진에 보이는 코드를 복사해서 붙여넣기 한다.

var admin = require("firebase-admin");

var serviceAccount = require("path/to/serviceAccountKey.json");

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount)
});

중간에 "path/to/serviceAccountKey.json"은 해당 키 경로를 써주면 되는데,
나는 프로젝트 폴더 안에 키를 넣어서 관리했기 때문에 아래와 같이 적어주었다.
(꼭 .gitignore에 키를 써주어야 합니다!! 깃헙에 올라가면 안 돼요!!!!!!!)

let serviceAccount = require(./다운받은serviceAccountKey값.json);


4. 실제 message 보내기

// 메세지 내용
const message = {
  notification: {
    title: notificationTitle,
    body: notificationContent,
  },
  token: token,
};

// 메세지 전송
admin
  .messaging()
  .send(message)
  .then(function (response) {
    console.log("푸시알림 전송 성공", response);
  })
  .catch(function (error) {
    console.log("푸시알림 전송 실패");
  });
};

👆 이 코드가 실제로 fcm 서버로 메시지를 보내는 코드이다. (unicast, 디바이스 토큰 한 개 필요)
디바이스 토큰은 프론트엔트 개발자 분들께 요청해서 푸시 알림이 잘 가는지 테스트했다.
그리고 프로젝트 상에서는 유저가 로그인할 때 db에 그 유저의 디바이스 토큰을 저장하는 코드를 추가했다.
어떤 유저에게 푸시 알림을 보내야 하는지 확인하면서 해당 유저의 디바이스 토큰을 가져와 token 값으로 넣어주었다!


나도선배 프로젝트에서는 api에 이 코드를 넣으면 너무 길어져서 lib안에 메시지를 보내는 코드를 모듈화 해 놓고, 필요한 api에서 호출하는 식으로 코드 리팩토링 중이다! 을 진행했다. (서버 선배들 최고 👍🏻)

밑에 코드를 보면 sendUnicast와 sendMulticast가 있는데

  • sendUnicast -> send(message) : 하나의 device token에게 보냄
  • sendMulticast -> sendMulticast(message) : 여러 개의 device token들에게 한 번에 보냄

이렇게 나눠서 볼 수 있다.
왜 multicast를 썼는지는 밑에서 설명하도록 한다.

const admin = require("firebase-admin");

const sendUnicast = function (token, notificationTitle, notificationContent) {
  // 메세지 내용
  const message = {
    notification: {
      title: notificationTitle,
      body: notificationContent,
    },
    token: token,
  };

  // 메세지 전송
  admin
    .messaging()
    .send(message) // 하나의 device token에게 보냄
    .then(function (response) {
      console.log("푸시알림 전송 성공", response);
    })
    .catch(function (error) {
      console.log("푸시알림 전송 실패");
    });
};

const sendMulticast = function (tokens, notificationTitle, notificationContent) {
  // 댓글이 있을 때만 푸시알림 전송, 댓글이 없을 경우 tokens가 빈 배열이라서 오류남.

  if (tokens.length !== 0) {
    // 메세지 내용
    const message = {
      notification: {
        title: notificationTitle,
        body: notificationContent,
      },
      tokens: tokens,
    };

    // 메세지 전송
    admin
      .messaging()
      .sendMulticast(message) // 여러개의 device token에게 한 번에 보냄
      .then((response) => {
        console.log("푸시알림 전송 성공", response.successCount);
      })
      .catch(function (error) {
        console.log("푸시알림 전송 실패");
      });
  }
};

module.exports = {
  sendUnicast,
  sendMulticast,
};

‼️ 아, 중간에 오류를 한 번 만났는데 그 이유는.... multicast에 빈 배열이 들어간 경우엔 empty array로 에러를 반환하기 때문이었다...
그래서 tokens 배열 길이가 0인 경우는 보내지 않도록 예외 처리를 해주었다.

 📌 나도선배에 알람이 필요한 상황! - 왜 multicast가 필요해?

나도선배 앱에는 크게

  1. 나에게 온 1:1 질문글 알림(마이페이지에 1:1 질문글이 올라온 경우)
  2. 답글 알림(내가 쓴 글에 답글이 달린 경우)
  3. 새 답글 알림(내가 답글을 쓴 타인 글에 새 답글이 달린 경우)

이렇게 세 가지의 알림 종류가 있고,
세부적으로는 2, 3번은 질문글 / 정보글로 나누어져서 총 7개의 유형이 있다.
유형마다 알림 문구도 달랐고, 특히 게시글인 경우는 특정 유저 1명에게 보내는 unicast인지 다수에게 보내는 multicast인지 분기 처리를 해주어야 하는 부분이 좀 까다로웠다.


예를 들어, 내가 1번 게시물에 댓글을 단 경우

  • 1번 게시물을 쓴 유저(작성자)에게 "작성하신 질문글에 주현님이 답글을 남겼습니다." 또는 "작성하신 정보글에 주현님이 답글을 남겼습니다."라는 알림을 보낸다.
  • 1번 게시물에 댓글을 달았던 유저들에게 "답글을 작성하신 질문글에 주현님이 답글을 남겼습니다." 또는 "답글을 작성하신 정보글에 주현님이 답글을 남겼습니다."

이런 식으로 많은 case가 있다.

여기서 "유저들" 에게 보내야 했기 때문에 multicast를 사용했다.
만약, 그냥 send를 반복문을 사용해서 푸시 알림을 전송한다면 해당 게시글에 댓글을 단 유저마다 푸시알림을 받는 시간이 달라지기 때문이다.
(여러 명에게 한 번에 보낼 땐 multicast를 사용해야 합니다!)

 📌 마치며

사실 다른 팀원들에게 도움이 되고 싶어서 다른 API를 구현하면서 틈틈이 푸시 알림 공부를 하면서 코드를 짰었는데 디바이스 토큰이 없어서 직접 알람이 오는 것을 눈으로 확인 못하는 게 아쉬웠다.
비록 앱잼 기간이 얼마 안 남아서 알람 / 이메일 인증 이렇게 두 가지 기능을 나눠서 담당해서 나는 알람 코드에 직접적인 영향(?)을 끼치진 못했지만 혼자 공부하면서 재밌었다. 추가로 시간 설정해서 푸시 알림 보내는 기능도 공부해야겠다!
다음 프로젝트에 푸시 알림이 있다면 혼자 구현할 수... 있지 않을까..? 하하

+ 서버 팀원들과 코드 구현 관련 회의할 때 다들 푸시 알림이 해보고 싶다고 해서 각자 코드를 짜며 공부를 해보기로 했는데, 모두 열심히 해줘서 고마웠다. 다들 고생했어요 🔥

+ Recent posts