매직값 반환금지
매직값은 버그를 유발할 수 있다.
아래 예제를 보면 나이값이 없는 경우도 있는데, -1으로 리턴하고
평균을 계산시에 계산에 오류가 발생할 수 있다
class User {
#age;
constructor(age) {
this.#age = age;
}
// 매직값인 -1을 리턴한다
getAge() {
if (!this.#age) return -1;
return this.#age;
}
}
const user1 = new User(31);
const user2 = new User(30);
// 나이값이 없을수도 있다
const user3 = new User();
const userList = [user1, user2, user3];
function getAvgAge() {
sum = 0;
for (user of userList) {
sum += user.getAge();
}
return sum / userList.length;
}
// 평균값에 -1도 포함하여 계산한다
console.log(getAvgAge());
널,옵셔널 또는 오류를 반환하라
호출하는 쪽에서는 코드의 세부사항에 대해서 모를 수도 있다. 그러므로 null을 반환시킴으로써,
오류를 알리는것이다
class User {
#age;
constructor(age) {
this.#age = age;
}
getAge() {
// 빈값인 경우 Error를 리턴함으로서, 호출하는쪽에서 인지하도록 한다
if (!this.#age) {
throw new Error("age is empty");
}
return this.#age;
}
}
const user1 = new User(31);
const user2 = new User(30);
const user3 = new User();
const userList = [user1, user2, user3];
function getAvgAge() {
sum = 0;
for (user of userList) {
// 빈 값일 경우 오류를 일으킨다
sum += user.getAge();
}
return sum / userList.length;
}
console.log(getAvgAge());
예상치 못한 부수효과는 피해라
💡함수 호출이 함수 외부에 초래한 상태 변화
함수 반환 값 이외에 다른효과 있다면 그것은 부수효과
- 사용자에게 출력 표시
- 파일이나 데이터베이스에 무언가를 저장
- 다른시스템을 호출하여 네트워크 트래픽 발생
- 캐시 업데이트 혹은 무효화
의도적인 부수효과
💡displayErrorMessage 는 오류를 표시하는 것이며, canvas에 에러 표시를 하는데, 이것은 의도적인 부수효과이다
const Color = {
RED: "red",
};
class Canvas {
drawText(message, color) {}
}
class UserDisPlay {
/** @type {Canvas} */
#canvas;
/**
* @param {Canvas} canvas
*/
constructor(canvas) {
this.#canvas = canvas;
}
displayErrorMessage (message) {
// 부수효과로 캔버스가 업데이트 된다
this.#canvas.drawText(message, Color.RED);
}
}
예기치 않은 부수 효과는 문제가된다
💡getPixel은 함수명만 보면 pixel을 가져오는 함수이다.
다만 내용을 보면 canvas를 다시 그리는 로직이 있어서, 이것은 상세내용을 보기전까지는 부수효과가 발생한다
class UserDisPlay {
/** @type {Canvas} */
#canvas;
/**
* @param {Canvas} canvas
*/
constructor(canvas) {
this.#canvas = canvas;
}
displayErrorMessage(message) {
// 부수효과로 캔버스가 업데이트 된다
this.#canvas.drawText(message, ColorConstants.RED);
}
getPixel(x, y) {
// 다시 그리기 효과로 부수효과가 발생한다
this.#canvas.redraw();
//로직 수행
}
}
부수효과는 많은 비용이든다
💡
this.#canvas.redraw(); 같은 경우 잠재적으로 많은 비용이 들 수 있다
가령 captureScreenshot 에서 화면에서 getPixel를 호출한다면 문제가 될 수 있다
class UserDisPlay {
/** @type {Canvas} */
#canvas;
/**
* @param {Canvas} canvas
*/
constructor(canvas) {
this.#canvas = canvas;
}
displayErrorMessage(message) {
this.#canvas.drawText(message, ColorConstants.RED);
}
getPixel(x, y) {
this.#canvas.redraw();
//로직 수행
}
// 스크린샷을 호출하여 반복문 만큼 redraw를 하게 된다.
captureScreenshot() {
const image = new Images(100, 100);
for (let x = 0; x < image.getWidth(); x++) {
for (let y = 0; y < image.getHeight(); y++) {
// getPixel에서 부수효과 발생하여 느려질수 있음
image.setPixel(x, y, this.getPixel(x, y));
}
}
}
}
입력 매개변수를 수정하는것에 주의하라
💡신규 이용자에게 무료 평가판을 제공하는 기능이 존재한다고 가정한다
/**
* @typedef {Object} User
* @property {number} id
* @property {string} name
*/
/**
* @typedef {Object} Invoice
* @property {number} id
* @property {number} amount
*/
/**
* @typedef {Object} OrderBatch
* @property {() => Map<User, Invoice>} getUserInvoices
* @property {() => Set<User>} getFreeTrialUsers
*/
const user1 = {
id: 1,
name: "철수",
};
const user2 = {
id: 2,
name: "영희",
};
const user3 = {
id: 3,
name: "민수",
};
/** @type {OrderBatch} */
const orderBatch = {
getUserInvoices() {
return new Map([
[
user1,
{
id: 101,
amount: 10000,
},
],
[
user2,
{
id: 102,
amount: 20000,
},
],
[
user3,
{
id: 103,
amount: 30000,
},
],
]);
},
getFreeTrialUsers() {
return new Set([user1, user2, user3]);
},
};
/**
* @param {Map<User,Invoice>} userInvoices
* @param {Set<User>} userWithFreeTrial
*/
function getBillableInvoices(userInvoices, userWithFreeTrial) {
// 무료 이용자는 다 제거한다
for (const user of userWithFreeTrial) {
userInvoices.delete(user);
}
return userInvoices.values();
}
/**
*
* @param {OrderBatch} orderBatch
*/
function processOrders(orderBatch) {
const userInvoices = orderBatch.getUserInvoices();
const usersWithFreeTrial = orderBatch.getFreeTrialUsers();
// 무료 이용자는 다 제거한다
sendInvoices(getBillableInvoices(userInvoices, usersWithFreeTrial));
// * userInvoices에서 무료이용자는 제거되므로, 서비스 이용이 불가능하다 *
enableOrderedServices(userInvoices);
}
function sendInvoices(invoices) {
// 송장을 발송하는 로직을 수행한다
}
/**
* @param {Map<User,Invoice>} userInvoices
*/
function enableOrderedServices(userInvoices) {
// 서비스사용을 활성화 하는 로직을 수행한다
}
processOrders(orderBatch);
만약 무료 이용자에게도 서비스를 사용하고 싶다고 기능을 제공한다고 하면
getBillableInvoices에서 무료이용자를 매개변수로 받고 매개변수 원본을 수정하여, enableOrderedServices에서 서비스가 활성화 되지 않는 버그가 발생하게 된다
변경하기 전에 복사하라
/**
* @param {Map<User,Invoice>} userInvoices
* @param {Set<User>} userWithFreeTrial
*/
function getBillableInvoices(userInvoices, userWithFreeTrial) {
return [...userInvoices]
.filter((user) => !userWithFreeTrial.has(user))
.map(([, invoice]) => invoice);
}
위 코드는 이전과 달리 원본을 훼손하지 않고 복사후 필터걸어서 리턴한다
미래를 위한 열거형 처리
💡열거형은 사용 가능한 값들을 미리 정해놓은 타입/구조이다
다른 사람의 코드 결과를 사용해야 할때가 있다.
const PredictedOutCome = Object.freeze({
COMPANY_WILL_GO_BUST: "COMPANY_WILL_GO_BUST",
COMPANY_WILL_MAKE_A_PROFIT: "COMPANY_WILL_MAKE_A_PROFIT",
});
/**
* @typedef {typeof PredictedOutCome[keyof typeof PredictedOutCome]} PredictedOutComeType
*/
/**
*
* @param {PredictedOutComeType} prediction
*/
function isOutcomeSafe(prediction) {
if (prediction === PredictedOutCome.COMPANY_WILL_GO_BUST) {
return true;
}
return false;
}
위와 같이 사용하는 경우 isOutcomeSafe 에서 PredictedOutCome이 새로운 값이 추가가 된다면 false를 리턴하고, 만약 isOutcomeSafe의 코드가 멀리 떨어져있다면, 예상치 못한 오류가 발생할 수 있다
Switch 이용하기
const PredictedOutCome = Object.freeze({
COMPANY_WILL_GO_BUST: "COMPANY_WILL_GO_BUST",
COMPANY_WILL_MAKE_A_PROFIT: "COMPANY_WILL_MAKE_A_PROFIT",
});
/**
* @typedef {typeof PredictedOutCome[keyof typeof PredictedOutCome]} PredictedOutComeType
*/
/**
*
* @param {PredictedOutComeType} prediction
*/
function isOutcomeSafe(prediction) {
switch (prediction) {
case PredictedOutCome.COMPANY_WILL_GO_BUST:
return false;
case PredictedOutCome.COMPANY_WILL_MAKE_A_PROFIT:
return true;
}
throw new Error("Unhandled prediction : " + prediction);
}
isOutcomeSafe("WORLD_WILD_END");
에러를 일으킴으로써, 예상치 못한값에 대해 처리한다
'Javascript' 카테고리의 다른 글
| 🐶 좋은코드 나쁜코드 4장 오류 (0) | 2026.08.05 |
|---|---|
| Promise.all / Promise.allSettled (0) | 2026.05.26 |
| Web-Client DIP(Dependency inversion principle) (0) | 2026.05.15 |
| 😌 DIP (0) | 2026.05.11 |
| Queue으로 작업실행 (0) | 2025.05.19 |
댓글