📌 들어가며

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를 유용하게 쓰고 있는데, 푸시 알림도 파이어 베이스에서 제공해주고 있어서 간편하게 사용할 수 있다!

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

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

 📌 들어가며

EC2를 이용해 서버 배포를 한 후, 장시간 입력이 없으면 서버가 닫히게 된다.

이를 방지하기 위해 프로세스 매니저 즉, PM2를 사용한다.

 

✔ 실습은 vscode 터미널에서 진행하려고한다. 이를 ec2 터미널에서 동일하게 진행하면 된다!

 

 

 📌 PM2 설치 및 사용법

1. 해당 디렉토리에서 PM2를 전역으로 설치한다.

$ cd <해당 디렉토리>
$ yarn global add pm2

✔ yarn 대신 npm을 사용할 시 (-g는 전역 설치 옵션)

$ npm install pm2 -g

2. yarn build를 통해 typescript코드를 javascript로 변환 (실행이 제대로 되는지 확인하기 위함)

$ yarn build

서버가 잘 실행된다면 성공!

 

3. dist 폴더 확인

해당 코드에 dist 폴더가 생긴다.

만약 dist 폴더가 생기지 않았다면 tsconfig.json로 들어가서 "outDir"가 "./dist"로 되어있는지 확인한다.

{
  "compilerOptions": {
    "target": "es6",
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true,
    "moduleResolution": "node",
    "module": "commonjs",
    "pretty": true,
    "sourceMap": true,
    "outDir": "./dist", //여기!!
    "allowJs": true,
    "noEmit": false,
    "esModuleInterop": true,
    "typeRoots": ["./src/types/express.d.ts", "./node_modules/@types"]
  },
  "include": ["./src/**/*"],
  "exclude": ["node_modules", "tests"]
}

✔ 해커톤 때 build 오류가 난 적이 있었는데 mongoose 관련 에러였다.

몽구스 버전이 업데이트 되면서 다른 패키지와 충돌이 났던 것이다.

이럴 경우 pakage.json에서 "mongoose": "^5.12.10" 버전으로 바꾸면 된다!

 

4. PM2 실행

yarn build해서 실행시켰던 서버는 ^C로 중지하고 해당 터미널에서 pm2를 실행한다.

$ pm2 start dist

 

 

 📌 PM2 기본 명령어 및 유용한 옵션

  • pm2 start <js파일>: pm2를 실행시킨다. (현재 실습에서는 ts에서 js로 바꾼 파일이 dist)
  • --watch 옵션: 자동 리로드해준다. (pm2를 실행시킨 후 코드 수정했을 시 변경사항을 자동으로 리로드 해준다.)
  • pm2 kill: pm2 종료시킨다.
  • pm2 list: 실행 중인 서버 상태를 보여준다.
  • pm2 stop <js파일>: 해당 서버를 중지시킨다.
  • pm2 restart <js파일>: 해당 서버를 재시작한다.
  • pm2 monit: pm2 실행 상황을 한 번에 보여준다.
  • pm2 logs: 현재 실행 중인 pm2 log 확인한다. (오류 확인 가능)

 

✔ 참고: 28th SOPT SERVER 7차 세미나 자료

 📌 들어가며

이전 글에서 putty 터미널에 접속하는 방법을 다뤘다.
이번엔 그 이후 터미널에서의 초기 환경 세팅 및 깃 클론, 서버 배포 방법에 대해 다뤄보려고 한다.

 

 📌 PuTTY 터미널 접속

무사히 이 창이 띄워졌다면 이후 세팅하는 과정이 필요하다.

 

1. 해당 명령어를 순서대로 입력

$ sudo apt-get install curl $ curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - $ sudo apt-get install -y nodejs $ sudo apt-get install build-essential

2. nvm 설치

$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash $ export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" $ nvm install --lts $ npm install -g yarn

 

이제 배포할 깃헙 코드 clone 주소를 복사한다.

 

다시 PuTTY 터미널로 와서 cd 명령어로 원하는 파일에 들어간 후 클론 해준다.

$ git clone https://github.com/~~~

 

이후
1. cd 명령어로 클론 한 파일에 들어간다.
2. vi명령어로 .env 파일을 만들어준다. (배포할 코드의. env 긁어오면 된다.)
2-1. .env 파일에서 코드 작성 후 :wq명령어로 저장하고 나온다.
3. yarn으로 패키지들 설치한다.
4. 실행!

$ cd <파일명> $ vi .env $ yarn $ yarn run dev

 

 

 📌 PuTTY에서 서버 실행한 후

서버를 실행했다면 인스턴스의 IP주소를 클라이언트에 전달해주면 된다.

혹시 포스트맨에서 확인하려면

IP주소가 13.209.18.160라면 http://13.209.18.160:5000/~~로 확인하면 된다. 여기서 5000은 포트 번호이다. 만약 :5000을 안 붙이면 기본 포트가 80이기 때문에 서버가 없다고 떠서 포스트맨에 찍히지 않는다.

 

 

 📌 서버 실행 후 서버가 자꾸 닫힌다면

EC2는 장시간 입력이 없으면 자동으로 서버가 닫히게 된다. 이를 방지하기 위해서 PM2를 사용하는데 이 건 다음 글에서,,

다뤄야겠다.

 

 

✔ 참고: 28th SOPT SERVER 5차 세미나 자료

 📌 들어가며

클라이언트와의 협업을 위해 EC2를 이용해 서버 배포하는 법을 배웠다.

현재 사용 중인 windows 기준으로 서버 배포 방법을 정리해보려고 한다.

 

서버 인스턴스에 접속하는 데 SSH 통신을 사용한다. 시작 전에 SSH 클라이언트인 PuTTY를 먼저 다운받아야 한다.

 

▼ PuTTY 설치

https://www.putty.org/

 

Download PuTTY - a free SSH and telnet client for Windows

Is Bitvise affiliated with PuTTY? Bitvise is not affiliated with PuTTY. We develop our SSH Server for Windows, which is compatible with PuTTY. Many PuTTY users are therefore our users as well. From time to time, they need to find the PuTTY download link. W

www.putty.org

 

 

 📌 EC2 생성을 위한 개념

  • AMI(Amazon Machine Image) : EC2 인스턴스의 기반이 되는 이미지
  • 보안 그룹(Security Group) : 보안을 위해 IP와 포트 번호를 이용해 정의해두는 서버 접속 규칙
  • 키 페어(Key Pair) : 서버에 접속하기 위한 열쇠

 

 

 📌 EC2 인스턴스 생성

1. AWS 로그인 및 AWS 콘솔에 접속

https://ap-northeast-2.console.aws.amazon.com/console/home?region=ap-northeast-2 

 

https://ap-northeast-2.console.aws.amazon.com/console/home?region=ap-northeast-2

 

ap-northeast-2.console.aws.amazon.com

2. 서울 리전(ap-northeast-2)을 선택한다.

아시아 태평양 (서울) 선택

 

3. ec2 검색 후 ec2(클라우드의 가상 서버)에 들어간다.

ec2 검색 후 밑에 있는 ec2 클릭

4.  왼쪽의 인스턴스 클릭 후 오른쪽의 인스턴스 시작(주황색 버튼) 클릭

✔ 프리티어는 만약 인스턴스 두 개를 켜 놓을 경우 돈 나갈 수 있기 때문에 조심..! 꼭 다른 인스턴스는 종료하고 새로 시작해야 한다. 

왼쪽의 인스턴스 클릭 후 오른쪽 상단의 인스턴스 시작(주황색 버튼) 클릭

 

5. AMI 선택

ubuntu 검색 후 "프리티어 사용 가능"인

Ubuntu Server 20.04 LTS (HVM), SSD Volume Type - ami-04876f29fd3a5e8ba (64비트 x86) 또는

Ubuntu Server 18.04 LTS (HVM), SSD Volume Type - ami-0ba5cd124d7a79612 (64비트 x86)을 선택한다.

 

✔ 프리티어 제외하고는 결제가 될 수 있으니 조심!

20.04와 18.04는 버전만 달라서 아무거나 선택해도 상관없다.

AMI 선택

 

6. 인스턴스 선택

t2.micro가 프리티어 사용 가능하다.

인스턴스 선택

 

7. EC2 인스턴스 세부 정보 구성

일단 기본값 사용 => 다음 : 스토리지 추가 버튼 클릭

인스턴스 세부 정보 구성

 

7. EC2에서 사용할 저장 장치 선택(스토리지 추가)

일단 기본값 사용 => 다음 : 태그 추가 버튼 클릭

스토리지 추가

 

8. 태그 지정

태그는 운영 환경에서 수백 개의 인스턴스가 실행될 수 있으므로 성격에 맞게 분류할 때 유용하게 사용된다.

별칭을 정해준다고 생각하면 편리합니다!

 

임의로 Key : Sopt, Value : exercise - instance 넣어주고 다음 : 보안 그룹 구성 버튼 클릭

✔ Key와 Value 별칭은 마음대로 지정해주면 된다.

태그 추가

 

9. 인스턴스에 대한 접근 제어(보안 그룹 구성)

보통 본인 IP 주소만 지정하는 것이 보안에 좋고 정말 필요한 IP 주소와 포트 번호에 대해서만 접속을 허용하는 것이 중요하다.

보안 그룹 구성

다음과 같이 적고 소스를 위치 무관으로 변경한 후, 검토 및 시작 버튼 클릭

보안 그룹 이름, 설명은 알아보기 쉽게만 작성하면 된다.

나중에 HTTP, HTTPS 유형도 추가해야 한다. -> 밑에서 다룸!

✔ 0.0.0.0/0 은 모두에게 접근을 허용하는 것을 의미한다.

 

이후 인스턴스 시작 검토 단계에서 시작하기 버튼을 클릭한다.

인스턴스 시작 검토

 

10. 키 페어 생성

새 키 페어 생성을 선택하고 생성할 키 페어의 이름을 넣는다.

✔ 키 페어 이름은 본인의 자유!

키 페어 다운로드

키 페어 다운로드 버튼 클릭 후 키 페어를 저장한다.

저장받은 키 페어는 절대로 잃어버리거나 유출하면 안 된다.

꼭 다운로드 후 인스턴스 시작 버튼을 누른다.

다운 받은 키 페어

 

 

 📌 PuTTY 접속

서버 인스턴스에 접속하는 데 SSH 통신을 사용한다.

EC2 인스턴스 키 페어와 SSH 클라이언트인 PuTTY를 이용해 SSH 접속을 해보려고 한다.

 

✔ 키 변환 과정

PuTTY에서는 AWS에서 내려받은 키 페어 파일을 바로 사용할 수 없다.

별도의 키 변환 과정이 필요하기 때문에 PuTTY Key Generator를 사용한다.

윈도우 검색 창에서 puttygen을 검색한 후 puttygen.exe파일을 실행한다.

puttygen.exe 실행

 

Load를 선택한다.

Load 선택

 

파일 종류를 [All Files]로 변경한 후 다운 받았던 키 페어 pem 파일을 선택한다. 

All Files로 변경
pem 파일 선택
확인

 

확인 누르고 Save Private Key를 선택한다.

예 선택

 

나는 pem 파일명과 동일하게 저장했다.

ppk 저장

 

 

 📌 PuTTY 실행

1. puttygen.exe를 종료한 후 PuTTY를 실행한다.

Host Name에는 ubuntu@<EC2 인스턴스 퍼블릭 도메인 or 퍼블릭 IP 주소>를 입력한다. (입력 시에는 <> 제외하고 입력한다.)EC2 인스턴스 도메인이나 IP 주소는 AWS 인스턴스에서 찾을 수 있다.AWS -> 인스턴스 -> 해당 인스턴스 클릭

Connection type은 SSH를 선택하고 Port는 SSH 포트인 22를 입력한다.

 

2. Private Key 지정

왼쪽의 Category 중 Connection => SSH => Auth 클릭

Private key file for authentication 항목에 Browse 후 puttygen.exe에서 저장했던 ppk(gem파일 아님!) 파일을 불러온다.

 

3. Start

Open을 누르면 다음과 같은 창이 뜨는데 여기서 accept를 눌러준다.

이런 창이 뜨면 성공!

 

 

 📌 PuTTY Fatal Error

추가 이전 키는 삭제해야 한다.

레지스트리 편집기에서 HKEY_CURRENT_USER => Software => SimonTatham => PuTTY => SshHostKeys에서

기본값을 제외한 내용을 삭제 해야 한다.

기본 값 제외 내용 삭제 후

 

 

 📌 HTTP, HTTPS로 접근

현재 SSH 포트만 허용해 놓았기 때문에 HTTP, HTTPS의 기본 포트인 80, 443 포트로 접속이 불가하다.

보안 그룹에서 HTTP와 HTTPS를 추가해주는 작업이 필요하다.

 

보안 그룹 생성 버튼 클릭 후 세부 정보 작성 및 인바인드 규칙을 지정해준다.

여기서도 일단 위치 무관(0.0.0.0/0)으로 설정해준다.

✔ 세부 정보는 사용자 마음대로, 인바인드 규칙만 따르면 됨!

 

인바인드 규칙이 잘 생성되었다!

 

이제 아까 생성한 인스턴스에 보안 그룹을 연결해준다.

 

보안 그룹 추가 후 저장해준다.

 

 

 📌 EC2 이야기와 그 이후..

aws ec2 인스턴스 생성은 자칫하면 비용이 나갈 수 있어서 항상 조심해야 한다..

또 나도 모르게 어디선가 실행 중일지도 모르니 aws 결제 대시보드에서 주기적으로 확인해주는 게 좋다!

 

EC2 다음에 putty 접속 후 찐 서버 배포 과정이 필요한데 다음 글에서,, 정리해야겠다,,

 

 

 

✔ 참고: 28th SOPT SERVER 5차 세미나 자료

 

+ Recent posts