[TIL #59] 최종프로젝트 #8 검색기능, 실시간 결과변경

안떽왕·2023년 6월 16일
0

Today I Learned

목록 보기
61/76

오늘은 백엔드 db에 있는 데이터를 검색하는 기능을 만들었습니다. 단순히 사용자에게 검색어를 입력받고 버튼을 누를때마다 검색이 되게 하는방식도 방법이지만 저는 이번 프로젝트의 작업물이 좀 더 살아있는 느낌을 주기 위해 검색엔진의 검색처럼 타이핑을 하는 순간마다 그에 맞는 검색 결과들이 아래에 배치되길 바랬습니다.

HTML

해당 기능을 담을 html입니다.

<div class="col-xxs-12 col-xs-12 mt">
    <div class="input-field">
	    <label for="spot">목적지:</label>
		<input type="text" class="form-control" id="route-spot" placeholder="올레길" />
		<div id="spot-results"></div>
	</div>
</div>

input태그에는 사용자가 검색어를 입력하는 공간이고 그 밑 div가 검색결과를 넣어줄 박스가 되겠습니다. 현재는 기능에 충실한 상태라 css를 입혀주지 못했으나 기능구현이 마무리되면 css를 입혀주겠습니다..

JS

이제 js를 보겠습니다.

전체 코드

async function searchSpot() {
    const input = document.getElementById('route-spot');
    const query = input.value.trim(); //앞뒤 공백제거

    // 비어있는 경우 검색 결과 영역 숨기기
    if (query === '') {
        document.getElementById('spot-results').innerHTML = '';
        return;
    }

    try {
        //백엔드에 데이터 요청
        const response = await fetch(`${proxy}/spots/?search=${query}`);
        const data = await response.json();

        // id로 태그 찾은다음 할당
        const resultsContainer = document.getElementById('spot-results');
        resultsContainer.innerHTML = '';

        // 데이터가 하나라도 있는 경우
        if (data.results.length > 0) {
            // for문을 이용 하나씩 관광지를 불러옴
            for (const spot of data.results) {
                const resultElement = document.createElement('div');// div 생성
                resultElement.textContent = spot.title; // 관광지명 채우기
                resultElement.classList.add('search-result');   // class 삽입
                resultElement.addEventListener('click', () => {
                    input.value = spot.title; // 클릭된 검색 결과를 입력창에 채우기
                    resultsContainer.innerHTML = ''; // 드롭다운 영역 숨기기
                });
                resultsContainer.appendChild(resultElement);
            }
            resultsContainer.style.display = 'block'; // 드롭다운 영역 보이기
        } else {
            //데이터가 없는 경우 결과없다고 공지
            resultsContainer.style.display = 'block';
            resultsContainer.innerHTML = `<p>검색된 결과가 없습니다.<p>`
        }
    } catch (error) {
        console.error(error);
    }
}

//관광지 입력 필드에 입력이 변경되면 검색 함수 호출
document.getElementById('route-spot').addEventListener('input', searchSpot);

// 클릭 이벤트가 발생한 곳 이외의 영역을 클릭하면 드롭다운 숨기기
document.addEventListener('click', (event) => {
    const resultsContainer = document.getElementById('spot-results');
    if (!event.target.closest('.input-field')) {
        resultsContainer.style.display = 'none';
    }
});

//관광지 입력 필드에 입력이 변경되면 검색 함수 호출
document.getElementById('route-spot').addEventListener('input', searchSpot);

// 클릭 이벤트가 발생한 곳 이외의 영역을 클릭하면 드롭다운 숨기기
document.addEventListener('click', (event) => {
    const resultsContainer = document.getElementById('spot-results');
    if (!event.target.closest('.input-field')) {
        resultsContainer.style.display = 'none';
    }
});

const createButton = document.getElementById("route-button");
createButton.addEventListener('click', handleCreateRoute);

설명

하나씩 설명드리며 나아가겠습니다.

1

const input = document.getElementById('route-spot');
const query = input.value.trim(); //앞뒤 공백제거

route-spot이라는 id를 가진 html태그를 찾아 input이라는 변수에 할당해줍니다.
query변수는 input변수에 입력된 값을 가져오는데 trim()함수를 이용해 앞뒤로 공백이 있는경우 제거 해줍니다.

2

if (query === '') {
    document.getElementById('spot-results').innerHTML = '';
    return;
}

이 구문은 없어도 기능이 정상적으로 작동합니다.
하지만 사용자 입력값이 없을 때 값이 없다고 나오거나 비어있는 결과창이 나오는 것 보다는 결과창을 안보이게 하는 것이 훨씬 깔끔해 보일 수 있습니다.

맨 처음 페이지에 진입했을 때는 결과창이 보이지 않지만 사용자가 기입한 후 모두 지웠을 때 마지막 글자의 결과창이 계속 남아있는게 보기 싫어 넣게된 기능입니다.

3

// const proxy = "http://127.0.0.1:8000"
const response = await fetch(`${proxy}/spots/?search=${query}`);
const data = await response.json();

백엔드 db에 있는 자료를 요청합니다. 해당 주소는 백엔드에 만들어진 주소에 따라 달라질 수 있습니다. 데이터를 받게되면 data에 json화 해서 넣어줍니다.

4

const resultsContainer = document.getElementById('spot-results');
resultsContainer.innerHTML = '';

결과를 저장할 html태그를 찾아 할당합니다.

5

if (data.results.length > 0) {
    // for문을 이용 하나씩 관광지를 불러옴
    for (const spot of data.results) {
        const resultElement = document.createElement('div');// div 생성
        resultElement.textContent = spot.title; // 관광지명 채우기
        resultElement.classList.add('search-result');   // class 삽입
        resultElement.addEventListener('click', () => {
            input.value = spot.title; // 클릭된 검색 결과를 입력창에 채우기
            resultsContainer.innerHTML = ''; // 드롭다운 영역 숨기기
                });
        resultsContainer.appendChild(resultElement);
            }
    resultsContainer.style.display = 'block'; // 드롭다운 영역 보이기
} else {
    //데이터가 없는 경우 결과없다고 공지
    resultsContainer.style.display = 'block';
    resultsContainer.innerHTML = `<p>검색된 결과가 없습니다.<p>`
}

data.results에 제가 원하는 데이터가 담겨져 있는데 해당 데이터 안에 값이 있느냐 아니냐로 조건문을 만들었습니다.

만약 데이터가 잘 넘어왔다면 for문을 이용해 원하는 데이터를 하나씩 가져옵니다.
resultElement에 생성한 div를 할당해주고 resultElement에 가져온 데이터의 title을 적고 search-result라는 클래스를 삽입해줍니다. search-result클래스는 사실 css를 아직 만들지않아서 있으나 마나하지만 앞으로 css도 만들어 줄거라서 미리 넣어줬습니다!

이후 resultElement가 클릭되었을 때 지금은 해당 결과물의 title을 맨 위에 선언되어 있던 사용자의 입력창에 기입해주게 만들었지만 추후 다른 기능을 추가해 더 쓰임새 있게 만들 예정입니다. 정보가 입력되면 결과창을 숨깁니다.

마지막으로 해당 div를 resultsContainer 검색결과를 담고있는 div에 추가해주며 반복 1회가 끝나고 resultsContainer를 보일 수 있게 style을 수정해줍니다.

그리고 데이터가 없을 경우에는 검색결과가 없다고 공지하기 위해 resultsContainer의 style을 수정해 보일 수 있게 만들고 해당 div에 결과가 없다는 공지문구를 적어줍니다.

6

document.getElementById('route-spot').addEventListener('input', searchSpot);

사실 이 방법이 맞는지는 알지 못하겠으나 제 수준에서 생각했을때 타이핑을 할 때마다 검색 결과가 바뀌기 위해선 타이핑을 할때 마다 해당 문구로 요청을 보내야겠다는 생각이였습니다.

그래서 route-spot이라는 html태그를 찾고 이 태그안에 input이 일어날 때마다 지금까지 소개한 함수인 searchSpot함수를 실행시키게 만들었습니다.

이렇게 무자비하게 요청을 보내도 되나 싶으면서도 현재로서 제가 생각한 기능을 표현하기 위해선 이 방법이 최선이었다고 생각하고 있습니다!

7

document.addEventListener('click', (event) => {
    const resultsContainer = document.getElementById('spot-results');
    if (!event.target.closest('.input-field')) {
        resultsContainer.style.display = 'none';
    }
});

해당 기능은 검색기능을 사용하다가 페이지의 다른 부분을 클릭했을 시에 검색결과창을 숨겨주는 기능입니다.

input-field라는 클래스를 가지고있는 태그를 클릭한게 아니라면 검색결과창 태그의 style을 none으로 바꿔 보이지않게 만들었습니다.

후기

최근들어 자신감을 찾게해줄 동기가 부족했던 것 같은데 오늘 이 기능을 구현하면서 팀원들에게 어떻게했냐고 질문도 받고 코드리뷰도 진행하고 자신감을 꽤 되찾는 하루였던 것 같습니다. 다른 날도 오늘처럼 색다른 기능을 구현하며 나아갈 수 있는 사람이 되고 싶습니다.

profile
이제 막 개발 배우는 코린이

0개의 댓글