프로젝트에서 퀴즈 도메인 관련 비즈니스 로직을 응집도있게 관리하기 위해 다음과 같이 class로 관리하였습니다.
import {QuizNavigator} from "@/app/(page)/quiz/_helper/QuizNavigator";
import {QuizStorageManager} from "@/app/(page)/quiz/_helper/QuizStoreManager";
import {ArrayUtils} from "@/app/_utils/class/ArrayUtils";
// 퀴즈 로직 관련 클래스
export class QuizHelper {
constructor(
private storageManager: QuizStorageManager,
private navigator: QuizNavigator,
) {}
// 푼 문제 저장, 기존에 푼 문제가 있다면 추가,없다면 새로 저장
saveSolvedQuiz(currentQuiz: string) {
const solvedQuizList = this.storageManager.getSolvedQuiz();
const updatedList = ArrayUtils.removeDuplicate([...solvedQuizList, currentQuiz]);
this.storageManager.saveSolvedQuiz(updatedList);
}
// 안 푼 문제 중 랜덤으로 하나 반환
getRandomOneFromUnsolvedQuiz() {
const unsolvedQuiz = this.getUnsolvedQuiz();
return ArrayUtils.pickRandomOne<string>(unsolvedQuiz);
}
// 모든 퀴즈를 푼 경우, 퀴즈 완료 페이지로 이동
redirectToCompletionPageIfAllSolved(){
if(this.isAllQuizSolved()) {
this.navigator.navigate("/quiz/completed");
}
}
// 안 푼 문제 조회
getUnsolvedQuiz(): string[] {
const quizUrlList = this.storageManager.getQuizUrlList();
const solvedQuiz = this.storageManager.getSolvedQuiz();
return ArrayUtils.getDifference<string>(quizUrlList, solvedQuiz);
}
// 모든 퀴즈가 풀렸는지 확인
isAllQuizSolved(): boolean {
const quizUrlList = this.storageManager.getQuizUrlList();
const solvedQuiz = this.storageManager.getSolvedQuiz();
const isNotEmptyQuizUrlList = quizUrlList.length > 0;
const isNotEmptySolvedQuiz = solvedQuiz.length > 0;
return isNotEmptyQuizUrlList
&&isNotEmptySolvedQuiz
&&ArrayUtils.isEqualLength<string>(quizUrlList, solvedQuiz);
}
}
위 클래스는 QuizStorageManager
라는 클래스를 주입받아 내부적으로 메서드들이 사용하고 있습니다.
하지만 위 클래스가 범용적이지 못하다고 생각이 들었습니다.
왜냐하면 QuizStorageManager
에 종속적이기때문이죠.
현재는 어쩌다보니 내부 메서드들이 모두 QuizStorageManager
에 연관되어있습니다. 때문에 처음에 위처럼 설계를 한 것인데요.
추후에 퀴즈 관련 다른 로직을 위 메서드에 추가한다고 했을 때, 만약 QuizStorageManager
와 연관이 없는 메서드가 추가되었다고 해보겠습니다.
import {QuizNavigator} from "@/app/(page)/quiz/_helper/QuizNavigator";
import {QuizStorageManager} from "@/app/(page)/quiz/_helper/QuizStoreManager";
import {ArrayUtils} from "@/app/_utils/class/ArrayUtils";
// 퀴즈 로직 관련 클래스
export class QuizHelper {
constructor(
private storageManager: QuizStorageManager,
private navigator: QuizNavigator,
) {}
// 푼 문제 저장, 기존에 푼 문제가 있다면 추가,없다면 새로 저장
saveSolvedQuiz(currentQuiz: string) {
const solvedQuizList = this.storageManager.getSolvedQuiz();
const updatedList = ArrayUtils.removeDuplicate([...solvedQuizList, currentQuiz]);
this.storageManager.saveSolvedQuiz(updatedList);
}
// 안 푼 문제 중 랜덤으로 하나 반환
getRandomOneFromUnsolvedQuiz() {
const unsolvedQuiz = this.getUnsolvedQuiz();
return ArrayUtils.pickRandomOne<string>(unsolvedQuiz);
}
...
// 오늘의 퀴즈
getTodayQuiz(){
}
QuizStorageManager
와 연관이 없음에도 불구하고 위 getTodayQuiz
메서드를 사용하기 위해서는 QuizStorageManager
를 주입받아야합니다.
그래서 위와 같이 QuizStorageManager
와 연관있는 위 클래스와 QuizStorageManager
와 연관없는 클래스를 구분하기로 하였습니다.
다음처럼 기존에 QuizHelper
라고 명명되어있던 클래스를 QuizStorageHelper
로 class명만 바꾸어주었습니다.
import {QuizStorage} from "@/app/(page)/quiz/_helper/QuizStorage";
import {ArrayUtils} from "@/app/_utils/class/ArrayUtils";
// 퀴즈 로직 관련 클래스
export class QuizStorageHelper {
constructor(
private storageManager: QuizStorage,
) {}
// 푼 문제 저장, 기존에 푼 문제가 있다면 추가,없다면 새로 저장
saveSolvedQuiz(currentQuiz: string) {
const solvedQuizList = this.storageManager.getSolvedQuiz();
const updatedList = ArrayUtils.removeDuplicate([...solvedQuizList, currentQuiz]);
this.storageManager.saveSolvedQuiz(updatedList);
}
// 안 푼 문제 중 랜덤으로 하나 반환
getRandomOneFromUnsolvedQuiz() {
const unsolvedQuiz = this.getUnsolvedQuiz();
return ArrayUtils.pickRandomOne<string>(unsolvedQuiz);
}
// 모든 퀴즈를 푼 경우, 퀴즈 완료 페이지로 이동
redirectToCompletionPageIfAllSolved(navigate: (url: string) => void){
if(this.isAllQuizSolved()) {
navigate("/quiz/completed");
}
}
// 안 푼 문제 조회
getUnsolvedQuiz(): string[] {
const quizUrlList = this.storageManager.getQuizUrlList();
const solvedQuiz = this.storageManager.getSolvedQuiz();
return ArrayUtils.getDifference<string>(quizUrlList, solvedQuiz);
}
// 모든 퀴즈가 풀렸는지 확인
isAllQuizSolved(): boolean {
const quizUrlList = this.storageManager.getQuizUrlList();
const solvedQuiz = this.storageManager.getSolvedQuiz();
const isNotEmptyQuizUrlList = quizUrlList.length > 0;
const isNotEmptySolvedQuiz = solvedQuiz.length > 0;
return isNotEmptyQuizUrlList
&&isNotEmptySolvedQuiz
&&ArrayUtils.isEqualLength<string>(quizUrlList, solvedQuiz);
}
}
위 클래스는 QuizStorage
관련 메서드들만 사용하는 클래스로 사용하면 해당 클래스를 보았을 때, 목적성과 책임을 분명하게 알 수 있죠.