본문 바로가기

부트캠프 일지

부트캠프 86일차 후기

이제 프로젝트 기간도 얼마 안 남았다. 최종 프로젝트 종료와 함께 Intellij 유료판 유효기간도 지나니까 최대한 빠르게 프로젝트를 마칠려고 한다.

 

이번은 유효값을 바로 프론트에 바로 보여주려는 기능을 구현하려 했다. 하지만, 이전 프로젝트의 코드를 보니 코드가 좀 지저분하고, 뼈대는 비슷한데 반복되고, 확장성면에서도 좋아보이진 않았다.

 

isAvailableNickname(nickname).then(
    result =>{
        let info = ""
        if(result == 0)
        {
            nicknameChecked = true
        }
        else
        {
            nicknameChecked = false
            if(result < 0) info = "Nickname Must be longer than 3 and less than 20 and only contain alphabet and Number"
            else info = "This Nickname is Already in used"
        }

        document.getElementById("nicknameInfo").innerHTML = info
    }
).catch(e => alert("Error while Checking Nickname"))


isAvailableID(mid).then(
            result =>{
                let info = ""
                if(result == 0) idChecked = true
                else
                {
                    idChecked = false
                    if(result < 0) info = "ID Must be longer than 3 and less than 20 and only contain alphabet and Number"
                    else info ="This ID is Already in used"
                }

                document.getElementById("idInfo").innerHTML = info
            }
        ).catch(e => alert("Error while Checking ID"))

 

result를 Enum으로 바꾼다하여도, 어차피 비교하는 것은 거기서거기일 것이고, 다른 곳에도 적용을 쉽게 하고, 좀 더 확장성 있는 방법이 없지 않을까 고민을 하였다. 그래서 나온 아이디어가 커맨드 패턴이다.

 

뼈대는 이렇다. list를 저장하는 클래스를 만들고, 해당 list에는 Checker 인터페이스를 구현한 클래스를 저장한다. Checker를 구현하려면 check란 핵심 함수를 오버라이딩해야하는데, 이 check가 유효한 값인지 확인하는 메소드이다. 다시말하자면, Checker가 하나의 조건이고, list는 특정 값이 만족해야하는 모든 조건이다. checkValueAvailable은 특정 값이 조건들을 만족하는 지 순회하며 만족하지 못하면 해당 조건을 만족하지 않는 메시지와 함께, 결과를 반환한다.

 

class AvailableCheckList
{
    checkList = [];
    errorMessageForm;

    constructor(errorMessageForm) {
        this.errorMessageForm = errorMessageForm
    }
    checkValueAvailable(checkValue)
    {
        this.errorMessageForm.innerHTML = ''
        for(const checker of this.checkList)
        {
            if(!checker.check(checkValue))
            {
                this.errorMessageForm.innerHTML = checker.errorMessage
                return false
            }
        }
        return true
    }
    addChecker(checker)
    {
        this.checkList.push(checker)
    }
}

class ValueChecker
{
    errorMessage = ""
    constructor(errorMessage) {
        this.errorMessage = errorMessage
    }
    check(checkValue)
    {

    }
}
class DuplicateChecker extends ValueChecker
{
    checkFunction = async (str) => {}
    constructor(checkFunction) {
        super("이미 존재하는 값입니다.");
        this.checkFunction = checkFunction
    }
    check(checkValue) {
        super.check(checkValue);
        let result = this.checkFunction(checkValue)
        (async () => {
            await new Promise((resolve, reject) => {
                this.checkFunction(checkValue)
                    .then(data => {
                        console.log("Modified!")
                        result = data
                        resolve()
                    }
                ).catch(err => {
                    console.log("Error....: ", err.code)
                })
            })
                .catch(err=> {
                    console.log("Erroooor...: ", err.code)
                })
            console.log("I'm Returning...")
        })()
        console.log("Welcome Return!")
        return result
    }
}

class LengthChecker extends ValueChecker
{
    minLength = 0
    maxLength = 100
    constructor(minLength, maxLength) {
        super(`길이는 ${minLength} 이상 ${maxLength} 이하여야 합니다.`);
        this.minLength = minLength
        this.maxLength = maxLength
    }
    check(checkValue){
        super.check(checkValue);
        return checkValue.length >= this.minLength && checkValue.length <= this.maxLength
    }
}

class RegexChecker extends ValueChecker
{
    regex =  /^[a-zA-Z0-9|s]*$/;
    constructor(regex, errorMessage) {
        super(errorMessage ? errorMessage : "값은 영어 혹은 숫자로 이루어져야합니다.");
        if(regex) this.regex = regex
    }
    check(checkValue) {
        super.check(checkValue);
        if(checkValue.match(this.regex))
            return checkValue.match(this.regex).length > 0
        return false
    }
}

 

나는 이것을 프론트엔드에 구현을 하기로 한다. 왜냐하면, 바로바로 프론트엔드에서 결과를 확인하기 위해서, 서버와의 통신 딜레이를 줄이고 싶었다. 결과부터 말하자면 결국 실패하였다. 이유는 커맨드 패턴의 뼈대 때문이다.

 

DuplicateChecker 클래스는 axios를 통해 비동기적으로 서버와 통신해서 특정 값이 중복되는 지 확인하기 위해 설계한 클래스였다. 하지만, check는 async가 아니다. 그렇기 때문에 for문을 통해 list를 순회할 때, DuplicateChecker 클래스의 check 메소드는 서버와 통신도 하기 전에 빠져나가 버린다. 즉, 이 클래스 하나만이 비동기적으로 작동해서 문제가 발생했다.

 

이 문제를 해결하기 위해서 여러 고민을 하였다. 다른 클래스의 check를 async로 바꾸어볼까도 고민했지만, 아무리 그래도 너무 억지인 것 같아서 체념했고, 구글링을 통해 비동기함수가 동기함수에서 멈추는 방법을 연구도 해봤고, while(true)도 시도해서 무한루프에도 빠져봤다.

 

하지만 어느쪽도 실패로 끝나고, 결국 최종수단을 택했는데, 서버로 이 클래스를 옮기는 것이다. 어찌보면 간단하지만, 단순히 통신 시간을 줄이고 싶다는 내 소망 하나때문에 이런 시간낭비가 되었으니 어쩔 도리가 없었다. 사실 내가 참여하고 있는 부트캠프가 백엔드 부트캠프라서, 생각을 해보면 이쪽이 더 맞을 것 같다.

 

그렇게 JavaScript의 클래스를 Kotlin으로 옮기고 실행을 하였다. 

private val nicknameAvailableCheckList = AvailableCheckList(
    listOf(
        DuplicateChecker(memberRepository::existsByNickname),
        LengthChecker(3, 20),
        RegexChecker()
        )
)
private val usernameAvailableCheckList = AvailableCheckList(
    listOf(
        DuplicateChecker(memberRepository::existsByUsername),
        LengthChecker(3, 20),
        RegexChecker()
    )
)

 

isAvailableNickname(nickname).then(
    result => {
        nicknameChecked = result.result
        document.getElementById("nicknameInfo").innerHTML = result.errorMessage
    }
)

isAvailableUsername(username).then(
            result => {
                usernameChecked = result.result
                document.getElementById("usernameInfo").innerHTML = result.errorMessage
            }
        )

이제 깔끔하고 확장성 있게 값의 유효값을 즉각적으로 확인할 수 있다.

'부트캠프 일지' 카테고리의 다른 글

부트캠프 88일차 후기  (0) 2024.04.03
부트캠프 87일차 후기  (0) 2024.04.02
부트캠프 85일차 후기  (0) 2024.03.29
부트캠프 84일차 후기  (0) 2024.03.28
부트캠프 83일차 후기  (1) 2024.03.27