ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

์ธ์ฆ๋ฒˆํ˜ธ ํ•„์š”ํ•œ ๋ถ€๋ถ„์— ํ•ด๋‹น ๊ธฐ๋Šฅ ๊ฐ€์ ธ๋‹ค ์“ธ ๊ฒƒ



VIEW

 

 

์ธ์ฆ๋ฒˆํ˜ธ ๊ด€๋ จ html ์ฝ”๋“œ

(๋‚˜๋จธ์ง€ html ํƒœ๊ทธ๋Š” ์ œ์™ธ)

<table>
    <tr>
        <td class="td_width">
            <input class="form-control" type="tel" id="phoneNumber" name="phone" placeholder="ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ (- ์ œ์™ธ)" aria-label="default input example" required/>
        </td>
        <td>
            <button type="button" id="sendConfirmNum" class="btn btn-primary btn-default" style="background-color: #4373E6;">์ธ์ฆ๋ฒˆํ˜ธ</button>
        </td>
    </tr>
    <tr>
        <td class="td_width">
            <input class="form-control" type="text" id="code" name="code" placeholder="์ธ์ฆ๋ฒˆํ˜ธ" aria-label="default input example" required/>
        </td>
        <td>
            <button type="button" id="checkConfirmNum" class="btn btn-primary btn-default" style="background-color: #4373E6;">์ธ์ฆํ™•์ธ</button>
        </td>
    </tr>
    <tr>
        <td>
            <label id="checkCodeResult">โ˜… ์ธ์ฆ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.</label>
        </td>
        <td></td>
    </tr>
</table>

 

 

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ

1) '์ธ์ฆ๋ฒˆํ˜ธ' ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ธ์ฆ๋ฒˆํ˜ธ ๋ฐ›๊ธฐ

// โ— โ— โ— '์ธ์ฆ๋ฒˆํ˜ธ' ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ธ์ฆ๋ฒˆํ˜ธ ๋ฐ›์•„์˜ค๊ธฐ
    let num = "";
    const sendConfirmNum = document.getElementById('sendConfirmNum');
    sendConfirmNum.addEventListener('click', ()=>{
        $.ajax({
              type: "POST",
              dataType: 'json',
              url: "sms/send",
              async: false,
              data: {
                "phone": $("input[name=phone]").val() // ์ž…๋ ฅ๋œ ํœด๋Œ€ํฐ๋ฒˆํ˜ธ ๊ฐ€์ ธ์˜ค๊ธฐ
              },
              success: function(data) {
                num = data.smsConfirmNum; // ๋ฐ›์•„์˜จ ์ธ์ฆ๋ฒˆํ˜ธ๋ฅผ ์ €์žฅ 
                console.log('data : ' + data);
                console.log('num : ' + num);
               $('#smsModal').modal('show');
              },
              error: function(error) {
                console.log("์ธ์ฆ๋ฒˆํ˜ธ ๋ฐ›์•„์˜ค๊ธฐ ์‹คํŒจ...");
                console.log(error);
              }
        });
        $('#phoneNumber').attr('disabled', true); // ์ˆ˜์ • ๋ถˆ๊ฐ€
    });

โ‰ซ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด ๋ฐ›์•„์˜จ data๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ๊ทธ ์•ˆ์˜ smsConfirmNum์„ num์— ๋‹ด์Œ

โ‰ซ ๋˜ํ•œ ์ธ์ฆ๋ฒˆํ˜ธ ์ „์†ก ์‹œ, #phoneNumber๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜๋„๋ก ์„ค์ •

 

2) '์ธ์ฆํ™•์ธ' ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์ธ์ฆ๋ฒˆํ˜ธ์™€์˜ ์ผ์น˜ ์—ฌ๋ถ€ ํ™•์ธ

// โ— โ— โ— '์ธ์ฆํ™•์ธ' ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์ž…๋ ฅํ•œ ์ธ์ฆ๋ฒˆํ˜ธ์™€์˜ ์ผ์น˜ ์—ฌ๋ถ€ ํ™•์ธ
    const checkConfirmNum = document.getElementById('checkConfirmNum'); // ์ธ์ฆํ™•์ธ ๋ฒ„ํŠผ
    const checkCodeResult = document.getElementById('checkCodeResult');
    const code = document.getElementById('code');

    checkConfirmNum.addEventListener('click', ()=>{
        if(code.value.trim() == ''){
            checkCodeResult.innerText = 'ํœด๋Œ€ํฐ ์ธ์ฆ์„ ์ง„ํ–‰ํ•ด์ฃผ์„ธ์š”.';
            checkCodeResult.style.color = '#DC6089';
        } else{
            if(code.value.trim() == num){
                checkCodeResult.innerText = '๐Ÿ€์ธ์ฆ๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.';
                checkCodeResult.style.color = '#4373E6';
                flagPhone = true
                if(flagId == true){
                    $('#signUp').attr('disabled', false);
                }
            } else {
                checkCodeResult.innerText = '์ธ์ฆ๋ฒˆํ˜ธ๊ฐ€ ๋ถˆ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.';
                checkCodeResult.style.color = '#DC6089';
                $('#signUp').attr('disabled', true);
            }
        }
    });

โ‰ซ ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ๊ฐ€ ๋น„์–ด์žˆ๊ฑฐ๋‚˜, ์ผ์น˜ํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋น„๊ตํ•˜๊ธฐ ์œ„ํ•ด ์กฐ๊ฑด๋ฌธ์œผ๋กœ ๋‚˜๋ˆ”

โ‰ซ ํ”Œ๋ž˜๊ทธ ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ flagPhone ๊ณผ flagId(์•„์ด๋”” ์ค‘๋ณตํ™•์ธ)์ด true์ธ ๊ฒฝ์šฐ์—๋งŒ

ใ€€ํšŒ์›๊ฐ€์ž… ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™”๋  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •

 

+) ํ”Œ๋ž˜๊ทธ ํ•จ์ˆ˜ ๋‘ ๊ฐ€์ง€๋ฅผ false๋กœ ๋ฏธ๋ฆฌ ์„ ์–ธํ•ด ๋‘ 

window.onload = () =>{
    let flagId = false;
    let flagPhone = false;
}

 

3) ํšŒ์›๊ฐ€์ž… ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋ฐœ์ƒํ•  ๊ฒƒ๋“ค

// ํšŒ์›๊ฐ€์ž… ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ,
// 1. ํฐ ๋ฒˆํ˜ธ ๋„˜๊ธธ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ, 2. ๋ชจ๋‹ฌ ๋ณด์—ฌ์ฃผ๊ธฐ
    const signUpBtn = document.getElementById('signUp');
    signUpBtn.addEventListener('click', ()=>{
        $('#phoneNumber').attr('disabled', false);
        $('#signUpModal').modal('show');
    });

โ‰ซ #phoneNumber ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ๋ฅผ ๋ฐ›๋Š” input ํƒœ๊ทธ,

ใ€€์†์„ฑ์ด 'disabled' ์ผ ๊ฒฝ์šฐ ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ ์ „์†ก์ด ๋˜์ง€ ์•Š์Œ.

ใ€€๋”ฐ๋ผ์„œ ํšŒ์›๊ฐ€์ž… ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์ด๋ฒคํŠธ ๋ฐœ์ƒํ•˜๊ฒŒ ํ•˜์—ฌ disabled๋ฅผ false๋กœ ๋ณ€๊ฒฝ + ๋ชจ๋‹ฌ ๋ฐœ์ƒ

 

 


MODEL

 

 

Message ๊ฐ์ฒด

// import ์ƒ๋žต

@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@Builder
public class Message {
	private String to;	// ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฐ’ = ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ
}

 

 

SmsRequest ๊ฐ์ฒด

// import ์ƒ๋žต

@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@Builder
public class SmsRequest {	// ๋„ค์ด๋ฒ„์— ์š”์ฒญ ๋ณด๋‚ด๋Š” ์• 
	private String type;
	private String contentType;
	private String countryCode;
	private String from;
	private String content;
	private List<Message> messages;
	
}

 

 

SmsResponse ๊ฐ์ฒด

// import ์ƒ๋žต

@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@Builder
public class SmsResponse {		// ๋„ค์ด๋ฒ„๋กœ๋ถ€ํ„ฐ ์‘๋‹ต๋ฐ›๋Š” ์• 
	private String requestId;
	private LocalDateTime requestTime;
	private String statusCode;			// ์š”์ฒญ ์„ฑ๊ณตํ•˜๋ฉด HTTP ์ƒํƒœ์ฝ”๋“œ๊ฐ€ 200!
	private String statusName;
	private String smsConfirmNum;
}

 

 


CONTROLLER

 

 

SmsController

(์•„๋‹ˆ ๋ฌด์Šจ import๊ฐ€ ์ด๋ ‡๊ฒŒ ๋งŽ์ง€?)

package semi.project.bookmaroo.member.controller;

// import ์ƒ๋žต

@Controller
@RequiredArgsConstructor
public class SmsController {
	
	private final SmsService smsService;
	
	@PostMapping("/sms/send")
	@ResponseBody		// ๋ฐ์ดํ„ฐ ๊ฐ์ฒด์— "phone"์ด๋ž€ ์ด๋ฆ„์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ํœด๋Œ€ํฐ๋ฒˆํ˜ธ๊ฐ€ ๋‹ด๊น€
	public String sendSms(String phone , Model model) throws JsonProcessingException, RestClientException, URISyntaxException, InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
//		System.out.println("ใ…Žใ…Ž");
		SmsResponse response;
		ObjectMapper objectMapper = new ObjectMapper();
		String responseJson = "";
		try {
	        
			Message message = new Message(phone);	// Message ๊ฐ์ฒด ์ƒ์„ฑํ•ด์„œ ์ž…๋ ฅ๋ฐ›์€ phone ๊ฐ’ 'to' ์†์„ฑ์— ๋„ฃ์–ด์คŒ
			response = smsService.sendSms(message);
//			model.addAttribute("response", response);						// json ํƒ€์ž…์˜ String ์œผ๋กœ ๋ณ€ํ™˜
			responseJson = objectMapper.registerModule(new JavaTimeModule()).writeValueAsString(response);
//			System.out.println(responseJson);						// pom.xml์— 'Jackson Datatype : JSR310' ์„ค์ •
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("responseJson : " + responseJson);
		return responseJson;
	}
}

๏ผ@ResponseBody ์–ด๋…ธํ…Œ์ด์…˜ : ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์˜ ๋ฐ˜ํ™˜ ๊ฐ’์„ HTTP ์‘๋‹ต์˜ body๋กœ ์‚ฌ์šฉํ•˜๋„๋ก ์ง€์ •

๏ผObjectMapper ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ : ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋Š” ๊ฐ์ฒด๋ฅผ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ or JSON ๋ฌธ์ž์—ด์„ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ ์‹œ ์‚ฌ์šฉ

๏ผresponseJson = objectMapper.registerModule(new JavaTimeModule()).writeValueAsString(response);

ใ€€: response ๊ฐ์ฒด๋ฅผ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ responseJson ๋ณ€์ˆ˜์— ํ• ๋‹น

ใ€€์ด๋•Œ JavaTimeModule์„ ๋“ฑ๋กํ•˜์—ฌ ๋‚ ์งœ์™€ ์‹œ๊ฐ„์„ JSON ๋ฌธ์ž์—ด๋กœ ์ง๋ ฌํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •

 

 


SERVICE

@Service
public class SmsServiceImpl implements SmsService {
	
	// @Value : ์„ค์ • ํŒŒ์ผ(properties) ๊ฐ’ ๊ฐ€์ ธ์˜ค๋Š” ์—ญํ• 
	@Value("${naver-cloud-sms.accessKey}")
	private String accessKey;	// ํ‚ค๋ฅผ ํ†ตํ•ด ๊ฐ€์ ธ์™€ ๋ณ€์ˆ˜์— ๋‹ด์Œ
	
	@Value("${naver-cloud-sms.secretKey}")
	private String secretKey;
	
	@Value("${naver-cloud-sms.serviceId}")
	private String serviceId;
 
	@Value("${naver-cloud-sms.senderPhone}")
	private String phone;
	
	public String makeSignature(Long time) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
		String space = " ";
        String newLine = "\n";
        String method = "POST";
        String url = "/sms/v2/services/"+ this.serviceId+"/messages";
        String timestamp = time.toString();
        String accessKey = this.accessKey;
        String secretKey = this.secretKey;
 
        String message = new StringBuilder()	// Builder
                .append(method)
                .append(space)
                .append(url)
                .append(newLine)
                .append(timestamp)
                .append(newLine)
                .append(accessKey)
                .toString();
 
        SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(signingKey);
 
        byte[] rawHmac = mac.doFinal(message.getBytes("UTF-8"));
        String encodeBase64String = Base64.encodeBase64String(rawHmac);
 
        return encodeBase64String;
	}

 

 

public SmsResponse sendSms(Message message) throws JsonProcessingException, RestClientException, URISyntaxException, InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
		String smsConfirm = createSmsKey(); // ์ธ์ฆ๋ฒˆํ˜ธ ๋งŒ๋“ค๊ธฐ (์•„๋ž˜ ๋ฉ”์†Œ๋“œ ์žˆ์Œ)
		Long time = System.currentTimeMillis();
		
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
		headers.set("x-ncp-apigw-timestamp", time.toString());				// header
		headers.set("x-ncp-iam-access-key", accessKey);						// ๋„ค์ด๋ฒ„ ํด๋ผ์šฐ๋“œ๊ฐ€ ์›ํ•˜๋Š” ์ •๋ณด ๋„ฃ์–ด์ฃผ๋Š” ๊ณณ
		headers.set("x-ncp-apigw-signature-v2", makeSignature(time));
		
		List<Message> messages = new ArrayList<>();
		messages.add(message);	// 1๊ฐœ์—ฌ๋„ list์— ๋‹ด์•„์„œ ๋ณด๋ƒ„ (์ •ํ•ด์ง„ ๊ทœ์น™)
		
		SmsRequest request = SmsRequest.builder() // ๋„ค์ด๋ฒ„์—๊ฒŒ ์š”์ฒญ ๋ณด๋‚ด๊ธฐ
				.type("SMS")
				.contentType("COMM")
				.countryCode("82")					// ๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹					
				.from(phone) // ๋ฐœ์‹ ์ž
				.content("[์ฑ…๋งˆ๋ฃจ]\n์ธ์ฆ๋ฒˆํ˜ธ ใ€ˆ" + smsConfirm + "ใ€‰๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”(^^)โ™ช")
				.messages(messages) // ์ˆ˜์‹  ๋ฒˆํ˜ธ ๋ฆฌ์ŠคํŠธ (ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” ํ•˜๋‚˜๋งŒ ์ž…๋ ฅ ๋ฐ›์Œ. ์œ„ 88๋ฒˆ์งธ ์ค„)
				.build();	// ๊ฐ์ฒด ์ƒ์„ฑ
		
	
		ObjectMapper objectMapper = new ObjectMapper(); // ๊ฐ์ฒด์— ๋Œ€ํ•œ ๋งคํ•‘์„ ๋„์™€์คŒ → ObjectMapper
		String body = objectMapper.writeValueAsString(request);	// ๋ฉ”์†Œ๋“œ๋กœ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๋งŒ๋“ฆ  	// body
								// โ”” ์ด ๋ฉ”์†Œ๋“œ๊ฐ€ ๊ฐ์ฒด๋ฅผ JSON ํ˜•ํƒœ๋กœ ๋ฐ”๊ฟˆ (์ด๋ฏธ ์ •์˜๋œ ๋ฉ”์†Œ๋“œ)
		HttpEntity<String> httpBody = new HttpEntity<>(body, headers);
		// โ”” HTTP ํŒจํ‚ท์„ ์ƒ์„ฑํ•ด ์คŒ.
		// HTTP ์•ˆ์—๋Š” ํฌ๊ฒŒ header์™€ body๋กœ ๋‚˜๋‰จ
		
		SmsResponse response = null;
		try {
			// โ”Œ ํ†ต์‹  ๋„์™€์ฃผ๋Š” ์•  (์„œ๋ฒ„ → ์„œ๋ฒ„ ๋กœ ์š”์ฒญ ๋ณด๋‚ผ ๋•Œ ์ž์ฃผ ์“ฐ์ž„)
			RestTemplate restTemplate = new RestTemplate();
		    restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());								
//		    System.out.println(serviceId);																										// ์‘๋‹ต ํด๋ž˜์Šค ์ง€์ •
		    response = restTemplate.postForObject(new URI("https://sens.apigw.ntruss.com/sms/v2/services/" + serviceId + "/messages"), httpBody, SmsResponse.class);
		    response.setSmsConfirmNum(smsConfirm);	// โ”” postForObject : ์‘๋‹ต ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด๋กœ ๋ฐ›๋Š” ๋ฉ”์†Œ๋“œ
	    											// SmsResponse.class : ๊ฐ์ฒด ๋ฐ›์„ ํƒ€์ž…์„ ์ •ํ•ด์ฃผ๋Š” ์ธ์ž(๊ฐ์ฒด ๋งŒ๋“ค ํด๋ž˜์Šค๋ฅผ ์ง€์ •ํ•œ ๊ฒƒ → SmsResponse)
		} catch (Exception e) {
			e.printStackTrace();
		}
		return response;	
	}

 

 

// ์ธ์ฆ๋ฒˆํ˜ธ ๋งŒ๋“ค๊ธฐ
	public static String createSmsKey() {
		StringBuffer key = new StringBuffer();
		Random rnd = new Random();
		
		for(int i = 0; i < 5; i++) {
			key.append(rnd.nextInt(10));
		}
		
		return key.toString();
	}

 

 


APPLICATION.PROPERTIES

naver-cloud-sms.accessKey=
naver-cloud-sms.secretKey=
naver-cloud-sms.serviceId=
naver-cloud-sms.senderPhone=

 

๋Œ“๊ธ€
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2025/04   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
๊ธ€ ๋ณด๊ด€ํ•จ