JS를 활용하는 front 소스에서 사용자 입력을 통해서 함수를 더 진행시킬지 중단할지 정해야하는 경우가 많이 생깁니다.
모든 Modal 창을 통해서 callback 처리를 각각 함수에서 받아서 처리하면 문제가 생길 일이 없지만, 복잡한 비지니스 로직해서
하나의 함수에서 연속적으로 사용자 입력을 받아할 경우가 생길 수 있습니다.
최근 실무에서 위와 같은 문제가 생긴적이 있는데 이를 해결하기 위해서 사용한 간략한 기법을 정리해 봅니다.
예시코드
let success = '' //resolve를 담을 변수
let fail = '' //reject를 담을 변수
const returnPromise = () => {
console.log("returnPromise call")
return new Promise((resolve, reject) => {
success = resolve
fail = reject
})
}
const promiseControllTest = async () => {
console.log("test start")
//유저 입력 confirm을 비동기로 호출
//업무에서는 Modal창을 통해서 사용자 입력을 받는다.
setTimeout(() => {
if(confirm("확인을 눌러주세요.")) success()
else fail()
}, 0)
console.log("유저 입력까지 returnPromise 대기")
let resultP = await returnPromise()
.then(() => "success 되었음.")
.catch(() => "reject 되었음.")
//resultP 값에 따라서 함수를 더 진행할지 결정할 수 있음
console.log("resultP = ", resultP)
console.log("test end")
}
위 소스의 promiseControllTest 함수는 await된 returnPromise의 resolve가 사용자 입력인 confirm이 resolve()를 해줄 때까지 pending 되었다가 이후 결과 값 resultP를 통해서 함수를 더 진행시킬지 종료 시킬지(reuturn) 결정할 수 있습니다.
실무에서 React 혹은 Vue를 사용할 경우, 새로운 컴포넌트에서 success / fail 변수를 할당하고 Modal 창을 비동기적으로 호출하여 동작을 컨트롤할 수 있습니다.
회사에서 운영하고 있는 앱의 보안 키패드를 사용하는 서비스에서 잘못된 입력 값으로 인한 오류의 트러블 슈팅을 진행했습니다.
서버에서 input 값 검증을 통해서 업무 오류를 뱉어내 잘못된 정보가 업데이트 되지는 않았었지만, 화면 소스(front end)에서 예외처리 미흡으로 잘못된 input 값을 서버(back end) 측으로 보내고 있음을 확인했습니다.
😮 문제 상황
보안 키패드의 사용하는 이벤트의 value를 처리하는 부분에서 numberic의 검증이 되지 않아서 숫자형 값만 들어와야하는 부분에 실제 string이 들어오는 경우가 발생하였습니다.
🛠️ 조치방법
키패드 이벤트를 통해서 들어오는 value 값을 화면에서 isNaN를 사용해 한번 더 예외처리 하였습니다.
기존 로직의 경우, value가 string으로 들어오는데 해당 값이 '1'...'9'(1~9) 로 들어온다는 상정하고 개발되어 있었기에 숫자형의 value가 들어온다면, 이를 예외처리하기 위해서 JS Number class의 isNaN 메서드를 사용하여 예외처리 하였습니다.
if (Number.isNaN(v) === false) {
// 숫자형 v 가 들어왔음으로 처리
}
isNaN는 숫자형 데이터(numberic)가 아닐 경우(NaN), true를 return 합니다.
여기서 숫자형은 실제 Number 타입의 데이터와 숫자형식의 char('0' ~ '9')를 모두 포함 합니다.
▶ isNaN 예제
let n1 = '1'
let n2 = Number(n1) // 1
let r1 = Number.isNaN(n1)
let r2 = Number.isNaN(n2)
console.log('n1', n1)
console.log('n2', n2)
console.log('r1', r1)
console.log('r2', r2)
▶ 결과
> "n1" "1"
> "n2" 1
> "r1" false
> "r2" false
isNaN는 static으로 해당 메서드만 바로 사용해도 되지만, 명확하기 Number.isNaN() 로 사용하는 것이 권장 됩니다.
자바스크립트에서 객체를 생성하는 방법은 크게 위 두 가지 방식인데 아래 방식의 class를 만드는 것은 SE6 이후에 나온 문법이라고 한다.
그래서인지 대체로 function을 사용하는 위 방법으로 작성된 코드들이 대부분인 듯하다.
🤔 자바스크립트 prototype을 이용한 상속
자바스크립트에서 객체를 생성하면 자동적으로 prototype이라는 property가 생성된다.
이 property안에 새로운 객체를 생성하면 그것이 '부모' 객체가 되면서 상속이 일어난다고 할 수 있다.
function Papa(){
this.age = 50
}
function Son() {
this.name = 'son'
}
var sam = new Son()
sam.prototype = new Papa() // Son의 다형성 객체인 sam은 Papa객체를 부모 객체로 상속 받는다.
console.log(sam.name) // >>> son
console.log(sam.age) // >>> 50
실제로 sam이라는 객체는 Son 객체이기 때문에 age라는 속성은 없지만, prototype이라는 속성 안에 Papa를 생성하여 상속받았기에 Papa가 가지고 있는 age라는 속성을 가지게 된다.
handler 내의 WebSocketSession에서 httpSession에서 설정한 attribute를 "getAttrubutes().get(저장한 애트리뷰트이름)을 통해서 가져올 수 있었다.
▶ DB를 사용하지 않고 기존 메시지 저장하기 -> Session Storage 사용하기
<script>
// save chat history
function setChatHistory(name){
var value =[];
document.querySelectorAll('.message-li').forEach(item => {
var time = item.querySelector('.message-data > .message-data-time').textContent;
var message = item.querySelector('.message').textContent;
var senderId;
var type = item.querySelector('.message').classList[1];
if(type == 'my-message'){
senderId = myId;
} else {
senderId = name;
}
data = {
"time":time,
"message":message,
"senderId":senderId
}
value.push(data);
})
sessionStorage.setItem(name, JSON.stringify(value));
};
// insert pre chat history
function getChatHistory(name){
var data = JSON.parse(sessionStorage.getItem(name));
if(data != null) {
data.forEach(item => {
var time = item.time;
var message = item.message;
var senderId = item.senderId;
insertMessage(senderId, time, message);
})
}
};
</script>
상대방의 id를 session stroage의 key로 상대방과의 대화 내용은 Json String으로 만들어 value로 넣어줬다.
sessionStorage를 사용한 이유?
내가 필요한 부분은 실시간 라이브 채팅이지만, 1:1 채팅이였기에 채팅 상대를 바꾸는 과정이 있기에 채팅 히스토리를 저장할 공간이 필요했다.
sessionStorage는 localStorage나 DB 저장과는 다르게 session이 만료되면 사라지는 정보이다. 해당 웹 페이지는 익명으로 채팅에 참여하는 것이고 중요한 정보를 저장할 필요가 없기에 세션이 종료시 사라지는 휘발성 저장 공간이 필요했기에 사용하기 적절해 사용하였다.
<script>
// counter라는 함수 정의
function counter() {
// content 변수 지정
// getElementById(id입력)
// -> 특정 id를 가진 DOM(textarea, span 과 같은 특정 객체를 의미함)을 가져옴
// .value를 붙이면 그 안의 내용을 가져옴
var content = document.getElementById('wordarea').value;
//count 라는 id를 가진 객체 내용 (0/200) 을 가져온 후 현재 글자수 넣어주기
document.getElementById('count').innerHTML = '(' + content.length + ' / 200)';
// 글자수 200초과 시 200자리까지만 표시
if (content.length > 200){
document.getElementById('wordcount').value = content.substring(0, 200);
}
}
counter() //counter 함수 실행
</script>