ํฐ์คํ ๋ฆฌ ๋ทฐ
์ธ์ฆ๋ฒํธ ํ์ํ ๋ถ๋ถ์ ํด๋น ๊ธฐ๋ฅ ๊ฐ์ ธ๋ค ์ธ ๊ฒ
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=
'๐' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
EC2 - ๋๋ฉ์ธ ์ฐ๊ฒฐ / ์ฌ์ดํธ ์ฃผ์์์ ํฌํธ ๋ฒํธ ์์ ๊ธฐ (:8080) (0) | 2023.11.06 |
---|---|
AWS RDS / ORACLE โ MYSQL ๋ง์ด๊ทธ๋ ์ด์ ๊ธฐ๋ก (0) | 2023.07.31 |
ํด๋ํฐ ์ธ์ฆ๋ฒํธ ํ๋ฆ ๊ตฌ์ (0) | 2023.05.12 |
AWS EC2๋ก ์๋ฒ ๊ตฌ์ถํ๊ธฐ (0) | 2023.05.07 |
SPRING ์นด์นด์ค ๋ก๊ทธ์ธ (0) | 2023.05.06 |