Project/[Team]외국인 관광객을 위한 카드 플랫폼

[Spring Boot/React] CoolSMS API 사용해 문자 인증 구현

dbfl9911 2024. 8. 24. 19:49

- 백엔드 코드 (Spring Boot)

 

1. buil.gradle에 아래 코드 추가

implementation 'net.nurigo:sdk:4.2.7'

 

 

2. application.yaml에 아래 코드 추가

coolsms:
  api:
    key: 쿨SMS에서 받은 API키
    secret: 쿨SMS에서 받은 SECRET키
    number: 핸드폰 번호

 

 

3. Controller 코드 작성

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.nurigo.sdk.NurigoApp;
import net.nurigo.sdk.message.model.Message;
import net.nurigo.sdk.message.request.SingleMessageSendingRequest;
import net.nurigo.sdk.message.response.SingleMessageSentResponse;
import net.nurigo.sdk.message.service.DefaultMessageService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import jakarta.annotation.PostConstruct;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

@RestController
public class SMSController {

	private DefaultMessageService messageService;
	private Map<String, String> verificationCodes = new HashMap<>(); // 전화번호별 인증번호 저장

	@Value("${coolsms.api.key}")
	private String apiKey;

	@Value("${coolsms.api.secret}")
	private String apiSecret;

	@Value("${coolsms.api.number}")
	private String fromNumber;

	@PostConstruct
	public void init() {
		this.messageService = NurigoApp.INSTANCE.initialize(apiKey, apiSecret, "https://api.coolsms.co.kr");
	}

	/**
	 * 단일 메시지 발송 예제
	 */
	@PostMapping("/send-one")
	public SingleMessageSentResponse sendOne(@RequestBody SMSRequest smsRequest) {
		String phoneNumber = smsRequest.getPhoneNumber();
		String verificationCode = generateVerificationCode();
		verificationCodes.put(phoneNumber, verificationCode); // 전화번호와 인증번호 매핑

		Message message = new Message();
		message.setFrom(fromNumber);
		message.setTo(phoneNumber);
		message.setText("인증번호는 " + verificationCode + "입니다.");

		SingleMessageSentResponse response = this.messageService.sendOne(new SingleMessageSendingRequest(message));
		System.out.println(response);

		return response;
	}

	/**
	 * 인증번호 확인 예제
	 */
	@PostMapping("/verify-code")
	public boolean verifyCode(@RequestBody VerificationRequest verificationRequest) {
		String sentCode = verificationCodes.get(verificationRequest.getPhoneNumber());
		return verificationRequest.getVerificationCode().equals(sentCode);
	}

	private String generateVerificationCode() {
		Random random = new Random();
		int code = 100000 + random.nextInt(900000); // 100000 ~ 999999 사이의 랜덤 6자리 숫자 생성
		return String.valueOf(code);
	}
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class SMSRequest {
	private String phoneNumber;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class VerificationRequest {
	private String phoneNumber;
	private String verificationCode;
}

 


 

- 프론트 코드 (React)

import React, { useState } from 'react';
import { Input, Button } from '@mui/material';
import axios from 'axios';

function Card2() {
    // 상태 관리: 휴대전화 번호, 인증번호, 인증번호 발송 여부, 인증 성공 여부
    const [phoneNumber, setPhoneNumber] = useState(''); // 입력된 휴대전화 번호
    const [verificationCode, setVerificationCode] = useState(''); // 입력된 인증번호
    const [isVerificationSent, setIsVerificationSent] = useState(false); // 인증번호 발송 여부
    const [isVerificationSuccessful, setIsVerificationSuccessful] = useState(null); // 인증 성공 여부

    // 인증번호 요청 함수
    const handleVerificationRequest = () => {
        // 서버에 인증번호 발송 요청을 보냄
        axios.post('/send-one', { phoneNumber })
            .then(response => {
                console.log('인증번호 발송 성공:', response.data);
                setIsVerificationSent(true); // 인증번호 발송 성공 시 입력 필드를 표시하기 위해 상태를 true로 설정
            })
            .catch(error => {
                console.error('인증번호 발송 실패:', error);
            });
    };

    // 인증번호 입력 값 업데이트 함수
    const handleVerificationCodeChange = (event) => {
        setVerificationCode(event.target.value); // 입력된 인증번호를 상태에 저장
    };

    // 인증번호 검증 요청 함수
    const handleVerificationSubmit = () => {
        // 서버에 입력된 인증번호 검증 요청을 보냄
        axios.post('/verify-code', { phoneNumber, verificationCode })
            .then(response => {
                setIsVerificationSuccessful(response.data); // 서버의 응답에 따라 인증 성공 여부를 업데이트
                if (response.data) {
                    alert('인증 성공!'); // 인증 성공 시 알림 표시
                } else {
                    alert('인증 실패! 다시 시도해주세요.'); // 인증 실패 시 알림 표시
                }
            })
            .catch(error => {
                console.error('인증 실패:', error);
            });
    };

    return (
        <div>
            {/* 휴대전화 번호 입력 및 인증번호 요청 버튼 */}
            <div style={{ marginBottom: '30px' }}>
                <div style={{ justifyContent: 'flex-start', display: 'flex' }}>휴대전화 번호</div>
                <Input
                    id="phone"
                    placeholder="휴대전화 번호를 입력하세요"
                    value={phoneNumber}
                    onChange={(e) => setPhoneNumber(e.target.value)} // 입력된 전화번호를 상태에 저장
                    style={{ width: '80%' }}
                />
                <Button
                    variant="contained"
                    color="primary"
                    onClick={handleVerificationRequest} // 인증번호 요청 버튼 클릭 시 호출
                >
                    인증
                </Button>
                <br />
            </div>

            {/* 인증번호 입력 필드 및 확인 버튼, 인증번호 발송 후에만 표시됨 */}
            {isVerificationSent && (
                <div style={{ marginBottom: '30px' }}>
                    <div style={{ justifyContent: 'flex-start', display: 'flex' }}>인증번호 입력</div>
                    <Input
                        id="verification-code"
                        placeholder="인증번호를 입력하세요"
                        value={verificationCode}
                        onChange={handleVerificationCodeChange} // 입력된 인증번호를 상태에 저장
                        style={{ width: '80%' }}
                    />
                    <Button
                        variant="contained"
                        color="primary"
                        onClick={handleVerificationSubmit} // 인증번호 확인 버튼 클릭 시 호출
                    >
                        확인
                    </Button>
                </div>
            )}
        </div>
    );
}

export default Card2;