리팩토링-목적에 맞게 클래스 구분

김철준·2024년 12월 19일
0

리팩토링

목록 보기
3/9

프로젝트에서 퀴즈 도메인 관련 비즈니스 로직을 응집도있게 관리하기 위해 다음과 같이 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라는 클래스를 주입받아 내부적으로 메서드들이 사용하고 있습니다.

목적에 부합하지 못한 class

하지만 위 클래스가 범용적이지 못하다고 생각이 들었습니다.
왜냐하면 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를 주입받아야합니다.

목적에 맞도록 class 구분

그래서 위와 같이 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관련 메서드들만 사용하는 클래스로 사용하면 해당 클래스를 보았을 때, 목적성과 책임을 분명하게 알 수 있죠.

profile
FE DEVELOPER

0개의 댓글