
원클릭 회원가입 승인 구현(telegram bot, AWS s3, Flutter, Nest.js)Node.js2023. 7. 21. 20:14
Table of Contents
서론
학교 애플리케이션을 개발 중, 학생증 사본을 통해 학생 인증을 하는 기능을 구현하려고 합니다.
사용 기술:
- nest.js 9.4.1
- node-telegram-bot-api 0.61.0
- aws s3, ec2
결과물:

실제 운영되는 프로덕션입니다.
1. telegram 봇 발급
이번에 구현 방식을 조사해보며 telegram을 선택하게 된 이유는 아래와 같다.
- p2p 방식으로 높은 기밀성
- 빠르게 개발할 수 있는 개발자 친화적 플랫폼
1. BotFather 검색 후 추가

2. /start 명령어를 입력하여 채팅을 시작해주세요.

3. /newbot 명령어를 입력하여 새로운 봇을 만들어주세요.

4. 봇 이름을 입력해주세요.

네이밍 규칙은 마지막이 bot으로 끝나면 됩니다. (대소문자 무관) 이름이 이미 존재하는 경우, 다른 이름을 입력해주세요.
5. 봇 생성 완료

아래와 같이 API 봇과 API 키가 생성되었습니다.
6. 생성된 봇 추가 후 채팅 걸기.

/start 명령어를 보낸 후 채팅을 걸어주세요.
7. chat_id 가져오기
https://api.telegram.org/bot${bot key}/getUpdates 로 접속해주세요.

chat_id를 메모해주세요. 유저의pk라고 생각하시면 됩니다.
여기까지 bot api key와 유저의 chat id를 받아야합니다.
2. Nest.js 코드 예제
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Inject, Injectable } from '@nestjs/common'; | |
import { User } from '@domain/user/user.entity'; | |
import { UserService } from '@app/user/user.service'; | |
import { MailerService } from '@nestjs-modules/mailer'; | |
import { UserAuthProvider } from '@domain/user/user-auth-provider.entity'; | |
import { Repository } from 'typeorm'; | |
import { PhotoClient } from '@infrastructure/utils/photo.client'; | |
import { UserVerificationRequestCommand } from '@app/auth/authentication/dto/register-request'; | |
import { ConfigService } from '@nestjs/config'; | |
// eslint-disable-next-line @typescript-eslint/no-var-requires | |
const TelegramBot = require('node-telegram-bot-api'); | |
@Injectable() | |
export class AuthenticationService { | |
private readonly bot: any; | |
private readonly chatId: string; | |
constructor( | |
private readonly userService: UserService, | |
private readonly mailerService: MailerService, | |
private readonly universityService: UniversityService, | |
private readonly configService: ConfigService, | |
@Inject('AuthPhotoClient') | |
private readonly authPhotoClient: PhotoClient, | |
private readonly userAuthProviderRepository: Repository<UserAuthProvider>, | |
) { | |
this.bot = new TelegramBot(process.env.TELEGRAM_BOT_KEY, { polling: true }); | |
this.bot.on('callback_query', (msg) => { | |
this.answerVerification(msg); | |
}); | |
this.bot.on('polling_error', console.log); | |
this.chatId = this.configService.get('TELEGRAM_CHAT_ID', '6279443618'); | |
} | |
// 이미지 업로드 및 회원가입 요청 | |
async uploadRegisterImage( | |
userVerificationRequestCommand: UserVerificationRequestCommand, | |
photo: Buffer, | |
user: User, | |
) { | |
// 이미지 업로드 및 리사이징 | |
const resizedPhoto = await this.authPhotoClient.resizePhoto(photo); | |
const photoUrl = await this.authPhotoClient.uploadPhoto(resizedPhoto); | |
// 텔레그램으로 허용 여부 물어보기 | |
await this.requestTelegramVerification( | |
userVerificationRequestCommand, | |
user.id, | |
photoUrl, | |
); | |
return; | |
} | |
// 응답 요청 | |
private answerVerification(callbackData: any) { | |
const data: string = callbackData.data; | |
const [userId, studentId, majorId, action] = data.split(':'); | |
switch (action) { | |
case 'accept': | |
const isVerification = this.userService.verifyUser(userId, { | |
studentId, | |
majorId: parseInt(majorId), | |
}); | |
if (isVerification) { | |
return this.bot.sendMessage(this.chatId, '인증이 완료되었습니다.'); | |
} | |
return this.bot.sendMessage(this.chatId, '인증에 실패했습니다.'); | |
case 'reject': | |
return this.bot.sendMessage(this.chatId, '인증이 거절되었습니다.'); | |
default: | |
return this.bot.sendMessage(this.chatId, '응답에 실패했습니다.'); | |
} | |
} | |
// 수락 요청 | |
private async requestTelegramVerification( | |
userVerificationRequestCommand: UserVerificationRequestCommand, | |
userId: string, | |
photoUrl: string, | |
) { | |
const { name, schoolId, majorId } = userVerificationRequestCommand; | |
const majorName = | |
await this.universityService.getUniversityMajorNameByMajorId(majorId); | |
this.bot.sendPhoto( | |
this.chatId, | |
`https://duscltkrckrf7.cloudfront.net/${photoUrl}`, | |
{ | |
caption: `인제생 회원가입 인증 요청입니다\n사용자명: ${name}\n학번: ${schoolId}\n전공 명: ${majorName}`, | |
reply_markup: { | |
inline_keyboard: [ | |
[ | |
{ | |
text: '수락', | |
callback_data: `${userId}:${schoolId}:${majorId}:accept`, | |
}, | |
], | |
[ | |
{ | |
text: '거절', | |
callback_data: `${userId}:${schoolId}:${majorId}:reject`, | |
}, | |
], | |
], | |
}, | |
}, | |
); | |
} | |
} |
3. AWS 설정
s3 설정
Bucket policy
{
"Version": "2008-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::ijs-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::567894824337:distribution/EVE7H18KXODL9"
}
}
}
]
}
IAM 설정
Role의 Trusted entities:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::ijs-bucket/*",
"arn:aws:s3:::ijs-bucket"
]
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "s3:ListAllMyBuckets",
"Resource": "*"
}
]
}
EC2 설정
EC2 > Instances > {이미지를 업로드 하는 인스턴스 선택} > Actions > Security > Modify IAM Role > 위 IAM 등록
CloudFront 설정
cloudFront는 너무 길어서 영상 첨부합니다.
결과물

공식 문서 및 Github
- github https://github.com/puleugo/IJS/blob/main/src/app/auth/authentication/authentication.service.ts
- node telegram bot api docs https://www.npmjs.com/package/node-telegram-bot-api
'Node.js' 카테고리의 다른 글
PostgreSQL Check란? Check를 사용하지 말아야하는 이유 (0) | 2024.04.11 |
---|---|
[NestJS] eslint를 작성해보자. (0) | 2023.08.23 |
[NestJS] TypeORM을 통한 트랜잭션 관리 (0) | 2023.04.30 |
[Jest] it vs test (0) | 2023.03.04 |
[NestJS] Forever를 사용하여 영구적인 실행을 해봅시다. (0) | 2023.01.18 |
@임채성 :: 푸르고 개발 블로그
글 내용 중 잘못되거나 이해되지 않는 부분은 댓글을 달아주세요! 감사합니다! 문의: puleugo@gmail.com