[Way to PM] 사이드 프로젝트 1. 구글 TM 머신 웹페이지에 커스텀하기 1장

2024. 2. 12. 12:41[Way to PM] 사이드 프로젝트

0. 레퍼런스

 

왼쪽에서부터 웹사이트의 상-하단부

 

1. 우선 순위 설정

1) Chat GPT 이용해서 python으로 이미지 크롤러 만들기 

2) 클로링한 이미지를 이용해 Google Teachable Machine으로 ML모델 만들기

*2번은 너무 쉬워서 생략

3) HTML과 ML 연동해 간단한 축구 유니폼 추천 사이트 구축

*(CSS로 기본적인 정리)

**참고영상(https://www.youtube.com/watch?v=OI3fZJHQF8Y&list=PLU9-uwewPMe2-vtJAgWB6SNhHcTjJDgEO&index=3)

2. WHY - 해당 과제를 통해 얻고자 하는 목표

1) Chat GPT라는 기술을 이용해 python에 대한 지식이 없어도 크롤링 시스템 구축하여 신기술을 직접 이용해보고 적용하는 경험 쌓기

2) 이미지 데이터 수집해서 ML모델 만들어보는 경험 쌓기

3) 구축한 ML모델을 이용할 수 있는 웹사이트 구축

 

3. HOW - HTML, JS, CSS

0) 기본적인 과정은 유튜브에 다 나와있으나, 구체적으로 나와있지 않은 ML의 결과값에 따라 퍼센트바의 형태로 추가하는 부분과 ML모델 동작 중 로딩메시지가 보이지 않았던 문제를 해결한 과정에 대해 기술했음.


*ML결과값에 따라 퍼센트바 만들기 전체코드

document.getElementById("progress-container").innerHTML = "";
    prediction.forEach((p) => {
        const classNameWithoutSpaces = p.className.replace(/\s/g, "-").toLowerCase();

        const labelAndProgress = document.createElement("div");
        labelAndProgress.classList.add("label-and-progress", classNameWithoutSpaces);

        const labelText = document.createElement("div");
        labelText.classList.add("label-text", classNameWithoutSpaces);
        labelText.innerText = p.className;

        const progressBackground = document.createElement("div");
        progressBackground.classList.add("progress-background", classNameWithoutSpaces);

        const progressBar = document.createElement("div");
        progressBar.classList.add("progress-bar", classNameWithoutSpaces);
        progressBar.style.width = `${p.probability.toFixed(2) * 100}%`;
        progressBar.innerText = `${(p.probability.toFixed(2) * 100).toFixed(0)}%`;

        labelAndProgress.appendChild(labelText);
        progressBackground.appendChild(progressBar);
        labelAndProgress.appendChild(progressBackground);

        document.getElementById("progress-container").appendChild(labelAndProgress);
    });
    $('#loading-indicator').hide(); // Hide loading indicator after prediction
}

1. 퍼센트 바를 포함할 부모요소(div#progress-container)를 생성하고 그 안에 퍼센트바가 생성될 공간을 innerHTML을 통해 생성

document.getElementById("progress-container").innerHTML = "";

-> 초기화를 통해

  1. 코드의 일관성: 스크립트가 여러 번 실행될 경우를 대비하여, 매 실행 시마다 progress-container의 상태를 명확한 초기 상태로 설정함으로써 코드의 예측 가능성과 일관성을 높일 수 있습니다. 이는 특히 동적인 웹 애플리케이션에서 중요하며, 사용자의 다양한 상호작용에 대응하기 위해 필요합니다.
  2. 재사용성 향상: 같은 페이지 내에서 다른 데이터나 상황에 따라 progress-container 내용을 업데이트해야 할 경우가 있습니다. 초기화 과정을 통해 컨테이너를 빈 상태로 만들고 새로운 내용을 추가함으로써, 이전 상태에 관계없이 동일한 방식으로 요소를 추가할 수 있게 됩니다.
  3. 예상치 못한 오류 방지: 페이지 로딩 과정에서 progress-container에 미리 어떤 내용이 채워져 있을 수도 있습니다(예를 들어, 서버 사이드 렌더링을 통해). 초기화를 명시적으로 수행함으로써, 이러한 초기 상태에 의존하지 않고 스크립트가 예상대로 동작하도록 보장할 수 있습니다.
  4. 유지보수 용이: 나중에 웹 페이지나 애플리케이션의 로직이 변경되어 progress-container에 초기에 어떤 내용이 채워지게 될 경우에도, 초기화 로직이 있으면 해당 부분을 쉽게 조정할 수 있습니다. 이는 코드의 유지보수성을 높여줍니다.

다음과 같은 이점을 가질 수 있다. 구현하는 것 자체엔 문제를 끼치지 않을 수 있지만 초기화가 권장된다.

 

2. 내가 생성하고자 하는 요소를 document.creatElement()메서드로 생성하고 각각의 결과값에 맞는 CSS를 적용하기 위해 classList.add를 통해 클래스 명을 명시해준다.

*classNameWithoutSpaces는 클래스명에 공백이 들어가면 오류가 발생하기 때문에 이를 없애줌.

prediction.forEach((p) => {
    const classNameWithoutSpaces = p.className.replace(/\s/g, "-").toLowerCase();

    const labelAndProgress = document.createElement("div");
    labelAndProgress.classList.add("label-and-progress", classNameWithoutSpaces);

    const labelText = document.createElement("div");
    labelText.classList.add("label-text", classNameWithoutSpaces);
    labelText.innerText = p.className;

    const progressBackground = document.createElement("div");
    progressBackground.classList.add("progress-background", classNameWithoutSpaces);

 

3. 퍼센트바의 길이를 결과값 퍼센트에 맞게 style.width에 리터럴템플릿으로 변환값을 할당한다. 여기서 toFixedO는 인자로 들어가는 값까지 반올림해주는 함수이다.(예로 0.4567이면 0.46으로 반환)

 innerText값에 다시 toFixed(0)을 해준 이유는 정밀하지 않은 문자열 연산으로 0.3400000000004같은 오류가 반환될 때가 있는데 이 값을 다시 소수점 0의 자리에서 반올림해 없애기 위함이다.

const progressBar = document.createElement("div");
progressBar.classList.add("progress-bar", classNameWithoutSpaces);
progressBar.style.width = `${p.probability.toFixed(2) * 100}%`;
progressBar.innerText = `${(p.probability.toFixed(2) * 100).toFixed(0)}%`;

 

4. append 함수를 통해 progress-container의 자식요소로 생성된 요소를 추가

labelAndProgress.appendChild(labelText);
    progressBackground.appendChild(progressBar);
    labelAndProgress.appendChild(progressBackground);

    document.getElementById("progress-container").appendChild(labelAndProgress);
});

 


문제해결 과정1. 

1) 문제 상황 발생:

-ML모델이 예측을 수행하는 과정에서 유저에게 보여줄 로딩메시지를 구현했지만 보이지 않는 문제 발견

 

2) 문제 원인 탐색: 

-Chat GPT로 오류 원인을 알아봤지만 해결되지 않아 코드 분석 진행

-비동기 작업으로 인해 이미지가 로드되고 바로 predict가 실행되면서 코드 실행의 순서가

1. readURL내에서 loading-indicator 호출 2. 바로 predict실행되면서 loading-indicator 호출 3. readURL내 loading-indicator 숨김

 즉, 이미지를 로드하는 순간 로딩메시지가 생기고 이미지 로드가 끝난 후 로딩메시지가 사라진 후에, 다시 모델 예측이 시작되면 로딩 메시지가 생기가 다시 모델 예측이 끝나면 로딩바가 숨겨지길 바랬지만, 이미지가 로드되고 완료되는 찰나의 순간에 readUTL함수 내에서 로딩바가 호출되고 사라지는 과정 사이에 predict함수가 시작되면서 로딩바가 호출이 겹치고 hide 메서드가 실행되어 쭉 사라진 상태로 있었던 것.

 

3) 문제 해결

function readURL(input) {
    if (input.files && input.files[0]) {
        var reader = new FileReader();
        $('#loading-indicator').show();
        reader.onload = function (e) {
            $('#face-image').attr('src', e.target.result);
            $('.image-upload-wrap').hide();
            $('.file-upload-content').show();
            // $('#loading-indicator').hide();
        };
        reader.readAsDataURL(input.files[0]);
    } else {
        removeUpload();
    }
}

readUTL의 로딩바 hide를 주석처리하여 해결했다. 여기서 readURL이나 Predict 둘 중 하나의 show메서드를 주석처리해도 로딩메시지는 그대로 구현된다.

 

4. WHAT - 실제 구축하고 배포한 사이트

https://sportsstyle.site/

 

스포츠 코디

Add description here

sportsstyle.site

5. 배운점 및 자기평가

배운점

1) JS를 통해 클래스를 조작하고 CSS를 유연하게 적용하는 방법에 대해 배움

2) 비동기 과정의 순서에 대해 파악해보고자 몇가지 가설을 세우고 해당 코드를 주석처리해보면서 비동기 작업에 대한 이해도를 높임

3) Chat GPT는 복잡한 코드내의 오류를 해결해주지 못하는 경우도 있으며, 근본적으로 코드의 원리를 이해하고 GPT는 이르 보조할 수단으로 활용하는게 바람직하다. GPT로 문제해결을 하려고 대략 1~2시간 동안 코드를 계속 바꿔가며 오류를 수정하라고 해봤지만 결국 주석처리 하나로 해결되었음(로딩메시지 안보이는 문제)

 

자기평가

1. GPT를 포함해 다른 도구들은 무언가를 구현하는데 편의를 줄 뿐 현상에 대해 이해도가 없으면 문제해결에 엄청난 시간이 걸린다.

2. GPT는 참고용으로 쓰고 습득한 정보를 바탕으로 현상을 이해하는 해상도를 높일 것