웹의 기본 컨셉은 Aysnc 비동기 방식의 호출을 합니다.
비동기 방식은 일반적인 함수의 실행 결과를 기다리지 않고 다음 코드를 차례대로 실행합니다.
그렇기 때문에 여러개의 함수를 순차적으로 코딩을 하더라도, 각 함수의 수행 시간이 다를수 있어서, 순서 보장이 안되어서 에러를 야기 할수 있습니다.
예를들어, 고객이름을 변경하는 프로그램을 만들고 , 내부적으로 고객정보 조회 , 고객명 변경의 2개 함수를 만들었다고 가정합시다. 고객이름은 반드시 존재하는 고객정보를 조회하여 기존 수정할 고객정보와 이름을 조회하고 그 다음 조회된 고객정보의 고객명을 바꾸는것이 일반적이 패턴이고, 그렇게 개발했다고 합시다.
1) seachCustomer( customerId )
2) modifyCustomer( customerName )
위와 같은 순서를 생각하고 코딩 할 것입니다. 이때 고객정보(이름)을 변경하기 위해서 customerId가 필수인데, 해당 프로그램이 실행되고 DB 데이터 조회해서 수정 대상 고객정보를 가져오기 전에 고객정보(이름)을 수정하려고 시도한다면 에러가 바로 발생할 것이다.
콜백 ( Callback ) 패턴 이란?
여기서, 우리는 웹의 비동기 방식이지만, 순서를 보장 해야하며 ( 고객정보조회 > 고객명변경 )이를 위해 Callback 패턴을 통해서 함수의 순서를 보장하는 방법을 알아봅니다.
샘플코드
console.log('Test Aysnc Call')
const a = (callback) => {
setTimeout(()=> {
console.log('1')
callback()
}, 1000)
}
const b = (callback) => {
setTimeout(()=>{
console.log('2')
callback()
}, 1000)
}
const c = () => {
setTimeout(()=> {
console.log('3')
}, 1000)
}
a(() => {
b(() => {
c()
})
})
위 프로그램은 a(), b(), c() 순서를 각각 1초 기다렸다가 실행합니다.
결과는 아래와 같습니다.
프로그램 설명
const a = (callback) => { // callback 을 매개변수로하여 함수를 전달 받음
callback ( ) // 전달받은 함수를 실행
}
a( ( ) => { // a( ) 함수 실행
b ( ) // b( ) 함수 실행
}
a( )함수 정의 할때, callback 매개변수를 통해서 함수를 전달닫고 , 전달 받은 함수( callback )를 이후에 실행 합니다.
이로써, a ( ) 함수 실행 후 b ( ) 함수를 실행하는 순서를 보장합니다.
Callback 패턴을 사용하면, 위 프로그램 소스처럼 순서를 제어가 많아질 수록 "콜백 지옥 ( 함수를 계속 들여 쓰면서 복잡 해짐 )을 마주하게 됩니다.
코드가 점점 들여쓰기가 되고, 가독성이 떨어지는 문제가 있습니다.
해당 문제를 조금 더 가독성을 높이는 방법은 Promise 클래서를 통해서 완화 할 수 가 있습니다.
프로미스 ( Promise )란?
앞서 언급한, '콜백 지옥'을 해결하고 보다 이해하기 쉬운 비동기 호출방식에서 함수 순서를 보장하기 위해서 Promise 클래스를 활용해 봅니다.
Promise 는 함수 실행의 결과 (성공, 실패)를 분리해서 수행합니다. 그래서 상태 (State)를 가지고 있고, 우리는 상태를 가지고 순서를 보장하여 개발 할수 있습니다.
샘플코드
// promise
console.log('Test Aysnc Call - Promise ')
const a = () => {
return new Promise ((resolve)=> { // Promise 객체를 반환하고 resolve를 인자로 받는다.
setTimeout(()=> {
console.log('1')
resolve() // console.log(1) 실행이 끝나면 resolve()를 호출
}, 1000)
})
}
const b = () => {
return new Promise ((resolve)=> { // Promise 객체를 반환하고 resolve를 인자로 받는다.
setTimeout(()=> {
console.log('2')
resolve() // console.log(2) 실행이 끝나면 resolve()를 호출
}, 1000)
})
}
const c = () => {
return new Promise ((resolve)=> { // Promise 객체를 반환하고 resolve를 인자로 받는다.
setTimeout(()=> {
console.log('3')
resolve() // console.log(3) 실행이 끝나면 resolve()를 호출
}, 1000)
})
}
const d = () => {
return new Promise ((resolve)=> { // Promise 객체를 반환하고 resolve를 인자로 받는다.
setTimeout(()=> {
console.log('4') // console.log(4) 실행
}, 1000)
})
}
// Promise 객체를 반환하는 함수를 호출하고 then()을 이용하여 chain으로 연결
/*
a().then(() => { // a() 함수를 호출하고 resolve()가 호출되면 then()을 호출
return b() // a().then()이후 Promise 객체를 반환
}).then(()=>{ // b().then()이후 chain으로 c()를 호출
return c() // c()를 호출하고 Promise 객체를 반환
}).then(()=>{ // c().then()이후 chain으로 d()를 호출
return d() // d()를 호출
})
*/
// 코드 간결화
// a().then(b).then(c).then(d)
a()
.then(b) // a() 함수를 호출하고 Promise 객체 반환하므로 then()로 b()를 호출
.then(c) // b() 함수를 호출하고 Promise 객체 반환하므로 then()로 c()를 호출
.then(d) // c() 함수를 호출하고 Promise 객체 반환하므로 then()로 d()를 호출
프로그램 설명
기존 Callback 패턴을 Promise 로 묶어주고, callback 부분인 () => { } 부분이 return 인경우 간소화 까지 한 프로그램 볼수 있습니다.
resolve는 함수를 받는 데이터로 콜백 영역인 ' ( ) => b ( )' 부분이 결국 Proimse (resolve)의 resolve 매개변수로 들어가는 구조입니다. 앞서 Callback 보다 복잡하지 않고 훨씬 간결하게 코드를 만들 수 있습니다.
'Language' 카테고리의 다른 글
[JS] 에러 핸들링 (Error Handling) - callback vs Promise vs async/await (0) | 2024.08.03 |
---|---|
[JS] async / await 으로 비동기를 동기처럼 써보자 (0) | 2024.08.03 |
[Python] 해쉬 문제 - 달리기 경주 (Programmers) (0) | 2023.08.10 |
[Python] 사진 자동 정리 프로그램 개발 (EXIF) (0) | 2022.12.18 |
[Python]이미지 메타정보(EXIF)로 촬영일시 찾기 (0) | 2022.12.18 |