추상화
- 구체적인 것을 감추고, 보고 싶어하는 전체적인 특성을 드러내는 것
- 생각의 단위를 묶어서 그룹으로 만들 수 있으며, 그것이 무엇을 의미하는지를 알 수 있도록 해줌
그래서 정말로, 소프트웨어 개발에서의 "추상화"가 뭔데?
이해하기 어려우니까 "함수"라는 익숙한 개념을 통해 알아보자.
함수 그 자체도 기본적인 추상화 방법이라고 한다.
👉🏻 함수의 이름을 통해 구체적으로 하는 일을 추상화하여 나타냄
예시
printf()
👉🏻 파일에 대한 출력을 담당한다는 의미를 가짐
👉🏻 구체적인 출력에 관련된 내용을 추상적으로 대표해서 표현결론
printf()
함수가 실제로 출력하기 위해서 하는 일은 모르지만, 어떠한 역할을 하는지 알고 사용한다.
그것만으로도 구현하는데 충분한 정보가 되기 때문!
예를 들면, 함수는 그 이름보다 한 단계 낮은 수준에서 내부의 코드들을 구성하고 있고,
그 내부로 들어가면 그것 역시도 다른 개념을 추상화하고 있는 것이다.
🌟 가장 구체적인 핵심 내용이 나오기 전까지 한 단계 더 구체적으로 변화하며 추상화가 진행되는 것!
함수는 작게 만드는 것이 핵심이며, 함수가 하는 일도 하나여야 한다.
함수의 이름을 통해 그 역할을 표현하기 때문에, 이름만 보아도 함수의 역할을 명확하게 파악할 수 있어야 한다.
함수가 커지는 이유는 추상화를 게을리 한 탓이며, 막연한 성능에 대한 기대 때문이다.
추상화는 다양하게 적용되는 개념인 듯 하다.
이 다양한 상황들이 원하는 목적은 동일하다.
👉🏻 지식을 대표적으로 표현함으로서 전체의, 하나의 덩어리로 이해하기 위함!
제대로 정보를 묶지 못해서 구체적인 것에 대한 의존이 시스템 전체에 번져있는 경우 👉🏻 추상화가 제대로 되지 않은 것!
이렇게 정리를 하다보니, 왜 함수명, 변수명을 정하는 것이 중요하다고 하는지 더욱 와닿는 것 같다
(그냥 농담인줄 알았는데!)
그리고, "유지보수하기 용이한 코드"를 강조하는 것도 추상화의 일부라고 느껴진다. 맞는지는 모르겠지만.
현재 기업협업을 통해 진행중인 프로젝트에서 가장 중요한(?) 부분, Cron 잡
!
실시간으로 데이터를 저장하기 위해 Cron
을 사용한다.
하지만, 지금 우리가 작성한 코드는 거의 하드코딩이라고 해도 무색할 정도로 반복되는 코드가 많다.
예를들어, Cron
을 사용하기 위한 전체적인 플로우는 다음과 같다고 하자.
(node로 배우고 nest로 기업협업 진행중임
👉🏻 추상화 예제는 node.js, 하드코딩 예제는 NestJS임. 감안하고 봐야한다)
@Cron('*/15 * * * *')
cron_15min() {
Promise.all([
this.cronJob(
key : 'a',
params : {
SELECT_DT: moment().tz("Asia/Seoul").format('YYYYMMDD'),
USER_ID: "abc000"
}
),
this.cronJob(
key : 'b',
params : {
SELECT_DT: moment().tz("Asia/Seoul").format('YYYYMMDD'),
USER_ID: "abc001"
}
),
this.cronJob(
key : 'c',
params : {
SELECT_DT: moment().tz("Asia/Seoul").format('YYYYMMDD'),
USER_ID: "abc002"
}
)
])
.then(([result1, result2, result3]) => {
console.log(result1, result2, result3);
})
.catch(([err1, err2, err3]) => {
console.log(err1, err2, err3);
});
}
async cronJob(key: string, params?: Object) {
let isSuccess = true;
try {
const dataList = await this.getData(key, params);
for (const data of dataList) {
const CODE = data;
let keyParams = {
CODE
};
await this.dataService.upsertData(
data,
keyParams,
);
}
} catch (err) {
isSuccess = false;
} finally {
return isSuccess;
}
}
Promise.all()
을 사용하면 promise
동작들을 병렬로 처리할 수 있다.
(Cron 잡 병렬처리하세요
라는 말은 Promise.all()
을 사용하라는 말과 같은 것!)
아무튼, 지금 눈에 거슬리는 것들을 찾아보자.
Promise
내부에 적혀진cronJob
메서드를 호출하기 위한 인자의 구조가 반복된다.this.cronJob
메서드 자체도 계속해서 반복되고 있다.
심지어 우리의 코드는 저 cron_15min
이라는 함수가 이름만 변경되어서
cron잡
을 돌아야 하는 시간별로 엄청 많이 반복되고 있다 😱 ..!!
엄청나게.. 많은 코드들이 하드코딩 되어있는 셈..!
자. 이렇게 하드코딩 된 소스를 보니 리팩토링이 간절해보인다.
모듈화 하고싶다면? 코드를 줄이고 싶다면? 리팩토링? 👉🏻 추상화 GO!
반복되는 코드(혹은 구조)가 무엇인지, 불필요한 코드가 무엇인지를 파악한 뒤 진행해야 한다.
전체적인 flow를 생각해보자.
cron잡
을 실행하기 위한,cron_15min()
을 선언한다.- 기존에
this.cronJob()
을 여러번 반복하던 것을map()
을 사용해서 줄여본다.cronJob()
을 실행하기 위해 필요했던, 반복되는 인자들과 그 구조를 배열을 사용해서 줄여본다.cronJob()
을 호출한다
// 4. 진짜 cronJob 실행!
const cronJob = ({ key, params, time }) =>
new Promise((resolve) => {
resolve({ key, params })
})
// 3. 반복되는 구조의 추상화 + 인자들의 배열화
const executeCronJob = ({ key, params }) => cronJob({ key, params, time: 'moment' })
const data = [
{ key: 'a', params: { name: 'hey' } },
{ key: 'b', params: {} },
{ key: 'c', params: { age: 3 } },
]
// 1. 선언
// 2. map 사용 -> 이전의 this.cronjob()의 반복과 같다.
const cron_15min = async () => {
const result = await Promise.all(data.map((el) => executeCronJob(el)))
console.log(result)
}
// 4. 호출
cron_15min()
참고로 cron_15min()
함수 실행 결과는 요로케 된다. 👇🏻