[express] twilio를 사용해 SMS 인증 구현하기

찐찐·2022년 3월 30일
0

Twilio를 사용해 문자 보내기

전화번호 발급 받기

  1. Twilio 가입 후 이메일과 내 전화번호 인증
  2. console로 넘어갈 수 있게 되면 전화번호를 발급 받을 수 있다.
  3. 발급이 완료되면 console에서 확인할 수 있다.
    • 한국 번호로 발급 받고 싶었는데 안돼서 찾아보니 25$ 주고 사야 된다고.. 그래서 그냥 공짜로 주는 듯한 미국 번호를 쓰기로 했다.

문자 보내기

문자 보내기는 아주 간단하다. Twilio에서 제공해주는 테스트 코드만 따라해도 바로 보내기가 가능하다.

const Twilio = require('./admin').Twilio;
const twilio = require('twilio')(Twilio.account_sid, Twilio.auth_token);

await twilio.messages.create({
        body: 'blah blah', // 문자로 보내고 싶은 내용
        from: '-', // 위에서 발급 받은 전화번호
        to: '-' // 받을 전화번호
    }, function (err, message) {
        if (err) console.log(err);
        else console.log(message.sid);
    });

이렇게 작성하고 실행만 하면 문자가 잘 보내진다.
넣고 싶은 적절한 라우터에 넣어주면 된다. 나는 문자 인증용 라우터를 따로 만들었다.


SMS 인증 구현하기

이제 문자 보내기는 완성 됐으니 SMS 인증 코드를 작성해보자!

인증 코드 저장 방법

인증 코드를 저장해야 사용자가 맞게 입력했는지, 아닌지를 확인할 수 있을텐데 어디에 저장해야 할지 고민이 많았다.

저장을 위한 선택지

  1. 세션에 저장
  2. DB에 저장(새 테이블 만들어서)
  3. Redis 사용
  4. cache에 저장

살펴본 바로 이렇게 4가지가 주로 사용되는 것 같았다.
나는 이 글을 보고 DB에 저장하기로 결정했다. (DB가 더 낫지 않냐는 추천도 받았고!)

DB 구조

  • 전화번호
  • 인증 코드
  • 만료 시간

이렇게 세 가지를 저장하는 DB 테이블을 하나 만들어 사용하기로 했다.

인증 코드 보내기 구현

const Twilio = require('./admin').Twilio;
const twilio = require('twilio')(Twilio.account_sid, Twilio.auth_token);

app.post('/phone_check', async (req, res) => {
    const body = req.body;
    const phone_number = body.phone_number;
    let code = '';
  	// 랜덤으로 4자리 인증 코드를 만든다.
    for (let i = 0; i < 4; i++) code += Math.floor(Math.random() * 10);

    try {
        const [result] = await db.execute(`INSERT INTO sms_validation(phone_number, validation_code, expire)
                                           VALUES (?, ?, NOW() + INTERVAL 3 MINUTE) ON DUPLICATE KEY
                UPDATE validation_code = ?, expire = NOW() + INTERVAL 3 MINUTE`,
            [phone_number, code, code]);
    } catch (e) {
        console.log(e);
    }

await twilio.messages.create({
        body: `${code}`, // 문자로 보내고 싶은 내용
        from: '-', // 위에서 발급 받은 전화번호
        to: '-' // 받을 전화번호
    }, function (err, message) {
        if (err) console.log(err);
        else console.log(message.sid);
    });
})

이미 존재한다면 UPDATE, 존재하지 않는다면 CREATE

고민했던 부분은 사용자가 문자 인증을 여러 번 시도할 수 있기 때문에 DB에 계속 업데이트를 해야 됐는데, 이미 존재한다면 UPDATE를 존재하지 않는다면 CREATE를 하는 MySQL 구문이 있나? 싶었다. 찾아보니 있더라!

db.execute(`INSERT INTO sms_validation(phone_number, validation_code, expire)
                                           VALUES (?, ?, NOW() + INTERVAL 3 MINUTE) ON DUPLICATE KEY
                UPDATE validation_code = ?, expire = NOW() + INTERVAL 3 MINUTE`,
            [phone_number, code, code]);

이렇게 ON DUPLICATE KEY를 사용해주면 가능했다.
참고로 나는 인증 제한 시간을 3분으로 뒀기 때문에 3 MINUTE를 추가했지만 원하는 만큼 자유롭게 바꿔서 사용하면 되겠다.

인증 코드 검증하기

app.post('/phone_check/verify', async (req, res) => {
    const body = req.body;
    const code = body.verify_code;
    const phone_number = body.phone_number;
    let phone_valid = false;

    try {
        const [result, field] = await db.execute(`SELECT *
                                                  FROM sms_validation
                                                  WHERE phone_number = ?`, [phone_number]);
        
      	// JS의 Date 객체로 변환
      	const expire_time = new Date(result[0].expire);
        const now = Date.now();
      	
      	// 인증코드와 유효시간 모두 검사
        if (code === result[0].validation_code && expire_time > now) {
            phone_valid = true;
        }

        res.send({phone_valid: phone_valid});
    } catch (e) {
        console.log(e);
    }
})

이렇게 해주면 코드의 완성이다. 테스트로 3분 후에 입력해봤더니 인증이 되지 않게끔 잘 구현됐다.

profile
백엔드 개발자 지망생

0개의 댓글