[JS] 모듈화에 대해

star_delight.yeji·2023년 7월 15일
0

개발 일지

목록 보기
13/15
post-thumbnail

정해진 기간 안에 프로젝트를 마무리 하기 위해 작업을 하다보니 반복되는 코드, 불필요한 코드 등 개선할 부분이 있었다. 그래서 프로젝트 리팩토링을 진행했다. 프로젝트 기간에 진행한 파트를 중심으로 작업을 진행했다. 상품 등록/수정 페이지, 모달창 리팩토링 진행 과정

상품 업로드와 수정 페이지

처음 작업 당시 상품을 업로드하고 수정하는 기능을 분리해서 작성했다. 리팩토링을 하기 위해 코드를 다시 분석하는데 반복되는 코드가 많아 파일을 합치기로 결정했다. 파일을 합칠 경우 동일한 코드이지만 다른 변수값, 상품이 있을 때와 없을 때의 상태에 따른 작업 등 여러 고려사항이 있어 차례대로 수정을 진행했다.

1 .변수값 변경

변수를 선언하는 것부터 개선했다. 리팩토링 전에는 같은 요소를 선택했지만 변수 값을 다르게 선언했다.

produce_add.js
const submitButton = document.querySelector('.btn-save');
const inpsProduct = document.querySelectorAll('#upload-product input');

produce_modification.js
const editButton = document.querySelector('.btn-save');
const editInpsProduct = document.querySelectorAll('#upload-product input');

이와 같은 문제가 있는 코드의 변수를 변경해주는 작업을 진행했다.

const submitButton = document.querySelector(".btn-save"),
     inputFields = document.querySelectorAll("#upload-product input")

2 .동일한 코드 찾기

변수명은 다르지만 선택자가 동일하고 코드가 같은 것을 찾아주었다.

// 유효성 검사
async function validateProduct(target) {
    // 상품명 validation
    if (target.id === 'product-name') {
        if (!target.validity.tooShort && !target.validity.tooLong) {
            document.querySelector(`.warning-msg-productname`).style.display = 'none';
            produceName.style.borderBottom = '1px solid #dbdbdb';
            validItemName = true;
        } else {
            document.querySelector('.warning-msg-productname').textContent = '*2~15자 이내여야 합니다.'
            document.querySelector(`.warning-msg-productname`).style.display = 'block'
            produceName.style.borderBottom = '1px solid red';
            validItemName = false;
        }
    }
  ...
}

상품 가격 유효성 검사에서 productPrice, editProductPrice동일한 선택자이기 때문에 변수명을 통일시켜 준 다음 코드를 동일하게 작성했다. 처음 코드를 작성할 때는 이벤트에 들어있는 코드가 길어 함수로 분리해서 작성을 해주었다.

function validateProduct(target) {
    // 상품명 validation
    if (target === productName) {
        isValidProductName = validateProductName(productName, warningMsgProductName);
    }
  ...
}
  
// 상품명 validation 함수
function validateProductName(productName, warningMsgProductName) {
    const warningText = "*2~15자 이내여야 합니다.";
    if (productName.value.length >= 2 && productName.value.length <= 15) {
        validateTrueField(productName, warningMsgProductName);
        return true;
    } else {
        validateFalseField(productName, warningMsgProductName, warningText);
        return false;
    }
}

3 .상태 설정 - 상품이 있고 없고

두 파일의 큰 차이점 중 하나는 상품의 유무에 따른 저장 버튼이 활성화이다. product_modification.js에서는 상품의 정보가 있기 때문에 바로 저장 버튼이 활성화될 수 있도록 설정해두었다.

const editButton = document.querySelector('.btn-save');
editButton.disabled = false

하지만 코드를 합치면서 productID를 변수로 저장하고 값이 있는 경우에 버튼이 바로 활성화될 수 있도록 코드를 수정해주었다.
const productID = pageUrl.searchParams.get('productId');

if(productID){
    submitButton.disabled = false;
    getProduct(url, token, productID);
}

4 .비슷하지만 상태에 따라 다른 코드 찾기

product_add.js와 product_modification.js의 코드에서 saveProduct 함수를 살펴보면 3번에서 설명한 product의 유무에 따라 코드의 일부가 다르다.

produce_add.js
async function saveProduct(url, token){
  	const reqPath = '/product'
    ...
    const res = await fetch(url + reqPath, {
        method: "POST",
        headers: {
            "Authorization": `Bearer ${token}`,
            "Content-Type": "application/json",
        },
        body: JSON.stringify(data)
    });

    const json = await res.json();
    console.log(json);
}

produce_modify.js
async function saveProduct(url, token, productID){
  	const reqPath = '/product/'
    ...
    try{
        const currentImageSrc = document.querySelector('.product-img').style.backgroundImage;
        let updatedData;
        let checkURL = `url('${imageURL}')`
        if (currentImageSrc !== checkURL) {
            updatedData = { ...data };
            updatedData.product.itemImage = currentImageSrc.slice(5, -2);
        }

      	const res = await fetch(url + reqPath + productID, {
            method: "PUT",
            headers: {
                "Authorization": `Bearer ${token}`,
                "Content-Type": "application/json",
            },
            body: JSON.stringify(updatedData || data)
        });
        const json = await res.json();
        return json;
    }
    catch (err) {
        console.error(err);
        location.href='./404.html'
    }
}

3번에서 설정한 productID의 유무에 따라 상태를 설정할 수 있도록 코드를 수정했다.

const METHOD = productID ? 'PUT' : 'POST'
...
async function saveProduct(url, token, METHOD) {
    ...
    try {
        const currentImageSrc = imageInput.style.backgroundImage;
        let updatedData = { ...data };
        let checkURL = `url('${imageURL}')`;
        const reqPath = (METHOD === "POST") ? url + "/product" : url + `/product/${productID}`
        if (currentImageSrc !== checkURL) {
            updatedData.product.itemImage = currentImageSrc.slice(5, -2);
        }

        const res = await fetch(reqPath, {
            method: METHOD,
            headers: {
                Authorization: `Bearer ${token}`,
                "Content-Type": "application/json",
            },
            body: JSON.stringify(updatedData),
        });
        return res.json();
    } catch (err) {
        console.error(err);
        location.href = "./404.html";
        // 저장 실패 시 에러 처리
    }
}

productID가 있는 경우 METHOD는 PUT이 되고 false인 경우에는 POST가 되도록 설정을 해주었다. reqPath에서 METHOD가 POST인 경우 productID가 없는 경우이므로 상품이 등록되지 않았다. 따라서 주소를 기본주소인 url에 +/product가 되도록 설정을 해주었다. PUT인 경우에는 상품이 있기 때문에 url+/product+ 등록된 상품 ID를 불러오도록 주소를 설정해주었다.


느낀점

프로젝트는 정해진 기간이 있다보니 기간 안에 작품을 완성하기 위해 코드를 작성하는 방향에만 신경을 쓰고 작업했다. 자세히 보면 겹치는 코드 혹은 상태만 다를 뿐 동일한 형태의 코드인데 그걸 생각하지 못하고 오직 코드를 작성하는 것에만 집중하고 작업을 했다는 것을 알게 되었다. 리팩토링 기간을 가지면서 내가 왜 이 코드를 사용하였는지와 같은 '왜?'라는 질문을 계속 던지면서 코드를 수정했다. 그러면서 반복되는 코드를 줄이고 개선하다보니 가독성이 더 나아지고 재사용성이 가능한 코드로 만들 수 있게 되었다. 프로젝트를 기간 안에 작성하는 것도 중요하지만 내가 코드를 어떤 방식으로 사용할 것인지 파악하고 작성하는 것도 중요하다는 것을 느끼게 된 시간이었다.

0개의 댓글